jeo-code 0.11.1 → 0.11.3

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/CHANGELOG.md CHANGED
@@ -9,6 +9,46 @@ The README mirrors the latest 5 entries — regenerate with `bun run changelog:s
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.11.3] - 2026-10-01
13
+ _Remote control grew from one Telegram bot into one shared daemon serving Telegram, Discord, and Slack — and the workflow gates that were meant to block unverified work (`approve`/`team` plan identity, `done` re-checks, the autopilot ratchet) now actually block it._
14
+
15
+ ### Added
16
+ - **Discord notification & remote control parity** — `jeo notify setup --provider discord` configures a Discord channel and allowed user IDs; same daemon serves both Telegram and Discord, pushing subagent state edges only. Inbound `/subagents`, `/steer`, `/cancel` commands work identically on Discord (allowed-user-ID-scoped). Requires bot `Message Content` intent and channel permissions (`View Channel`, `Send Messages`).
17
+ - **Telegram challenge-pairing and group allowlist** — `jeo notify setup --provider telegram` without explicit `--chat-id` displays a time-limited challenge code; send it to the bot within 120 seconds to pair. Groups require explicit `--allowed-user-ids` (comma-separated Telegram user IDs). Use `jeo notify test --provider telegram` for explicit outbound verification.
18
+ - **Daemon readiness distinction** — `jeo daemon status` and `jeo notify status` distinguish process initialization from platform connectivity. `initialized` does not prove a successful Telegram poll, Discord Gateway handshake, or Slack Socket Mode connection. `jeo notify health` checks bot identity and destination access; `jeo notify test` sends an actual message.
19
+ - **Plain-text credential storage** — Telegram, Discord, and Slack credentials are stored in `~/.jeo/config.json` under their `notifications` entries. Authentication uses the corresponding official provider APIs; tokens are not included in chat output or logs. Telegram uses direct Bot API polling, Discord uses REST + Gateway WebSocket, and Slack uses Web API + Socket Mode. Aside is a research reference, not a runtime dependency.
20
+ - **Bounded retries and orphan recovery** — provider-specific retry and backoff policies bound recovery; Slack does not blindly retry an uncertain POST. Telegram polling has separate error backoff. A stale daemon lock from a dead process can be reclaimed on `jeo daemon start`.
21
+ - **Direct remote controls with session ACK** — `/steer` and `/cancel` use typed controls authorized by the configured human allowlist and chat/channel/workspace boundary. Replies reflect the local session's correlated ACK; timeout, rejection, and shutdown do not count as successful execution. Allowlisted operators must be trusted with local agent capabilities.
22
+ - **Slack Socket Mode integration** — `jeo notify setup --provider slack --token-env JEO_SLACK_BOT_TOKEN --app-token-env SLACK_APP_TOKEN --channel-id <ID> --allowed-user-ids <HUMAN_ID>` validates bot identity (`auth.test` + `bots.info`), destination access (`conversations.info`, bot must be a member, workspace pinned to the token's team), and Socket Mode URL acquisition without opening a socket. The shared daemon posts notifications to the channel and routes replies in a session notification's thread back to that session (24 h); literal `/sessions`, `/subagents`, `/send`, `/steer`, `/cancel` work as `@bot` text or as registered Slack slash commands. Offline tests and scoped code, security, and TypeScript reviews passed. Live Slack connectivity remains unverified because bot/app credentials are not configured.
23
+ - **Live Antigravity model discovery honoured by the picker** (`src/ai/model-discovery.ts`, `src/commands/launch.ts`) — the server's `agentModelSorts` agent IDs are the source of truth: non-chat roles (`tab`/`image`/`audio`/`commit`/`mquery` lists) and `deprecatedModelIds` are excluded, `models/`-prefixed and provider-qualified rows normalize to the same id, and an empty or failed Antigravity list is reported as such instead of being papered over with the static catalog (same rule as OpenAI).
24
+ - **Plan identity gate** (`src/commands/approve.ts`, `src/commands/team.ts`) — `approve` requires the persisted consensus hash and re-validates schema, roles, and the current plan digest even on an already-approved plan; `team` refuses a plan without a reviewed hash, persists the hash in team state, and restarts execution when the path, slug, or digest differs from the state it resumes.
25
+ - **Completion gate re-checks** (`src/agent/engine.ts`, `src/agent/loop-guards.ts`) — every explicit or prose completion is evaluated against the latest verification evidence; a later failed check invalidates an earlier pass, and after `MAX_DONE_CORRECTIONS` (3) rejected attempts the turn stops with `done:false` and the reason instead of a fourth correction.
26
+ - **Autopilot ratchet failure semantics** (`src/autopilot.ts`, `ralplan`/`ultragoal` wrappers) — a failed evaluator can never KEEP; a failed `--on-revert` rollback is logged as `rollback_failed` with the error and halts with exit code 1 instead of recording a reverted step that did not happen; engine `ok:false` propagates as a non-zero exit from the CLI wrappers.
27
+ - **Telegram destination hygiene** (`src/commands/notify.ts`, `src/agent/notify/telegram-daemon.ts`) — changing the bot or chat no longer inherits the previous allowlist or per-session topic map; concurrent setup saves cannot interleave stale credentials; stopping the daemon settles every pending steer/cancel ACK; a SIGTERM during startup exits 0 instead of reporting a failed start.
28
+
29
+ ### Changed
30
+ - **Notification configuration unified** — one daemon manages Telegram, Discord, and Slack. `notify` operations accept `--provider telegram|discord|slack` (default: `telegram`); `jeo daemon start|stop|status|reload` operates on the shared daemon without a provider selector.
31
+ - **Setup output accuracy** — setup confirms bot identity and channel/chat access, but does NOT claim "live bot verification"; health/test commands are explicit.
32
+
33
+ ### Fixed
34
+ - **Delayed Node inspector startup detection** (`src/agent/debug-session.ts`) — `waitForInspectorUrl` now retains one pending stream read across 200ms timer ticks and clears it only after consuming a read result. A late inspector URL is no longer lost to an abandoned read. Verified with an actual Node inspector whose first stderr chunk is delayed 250ms.
35
+ - **Upstream PID-reuse tests stamped `Date.now()` on a minutes-old test process** (`test/notify-daemon-control.test.ts`) — the guard correctly read that as a start-time mismatch once the suite had run for more than 10 s, so two tests failed only in a full run. The fixtures now record the process's real start time; the guard is unchanged.
36
+
37
+ ### Verified
38
+ - `bun run typecheck` clean; isolated `bun test` (temp `HOME`/`JEO_CONFIG_DIR`, hooks off, 30 s timeout) — **3634 pass, 0 fail** across 329 files, 13,789 assertions.
39
+ - Telegram exercised live: real send, two successful `getUpdates` polls over a 70 s foreground monitor, clean SIGTERM shutdown, lock released. Antigravity live discovery returned the account's current eight ids and a smoke request on `gemini-3.6-flash-low` answered `OK`.
40
+
41
+ ## [0.11.2] - 2026-09-30
42
+ _OpenAI model pickers now follow the authenticated API and Codex catalogs, including subscription-only models, without hidden or stale entries._
43
+
44
+ ### Fixed
45
+ - Use Codex `visibility` for ChatGPT model discovery; retain visible subscription-only models even when `supported_in_api` is false. Keep modern GPT Codex models in API-key discovery and fetch both catalogs when both credentials are configured.
46
+ - Preserve the full OpenAI model list by default, de-duplicate picker rows, and keep empty or failed live results honest instead of fabricating availability from a static snapshot.
47
+ - Replace account-scoped OpenAI model observations on refresh, migrate old caches, and separate authentication sources and custom endpoints. Use stored account identity for opaque OAuth credentials and the proxy's own API key for custom endpoints.
48
+
49
+ ### Validation
50
+ - Regression coverage for visibility, dual credentials, complete lists, account headers, proxy scoping, cache replacement, and authoritative empty catalogs.
51
+
12
52
  ## [0.11.1] - 2026-08-25
13
53
  _Every non-interactive `jeo` run hung forever once Telegram notifications were configured — `echo "..." | jeo`, `jeo -p "..."` in CI, any scripted use. The work completed and the command returned; the process just never exited._
14
54
 
@@ -2417,7 +2457,12 @@ _Initial release._
2417
2457
  ### Added
2418
2458
  - Initial jeo-code agent and CLI.
2419
2459
 
2420
- [Unreleased]: https://github.com/akillness/jeo-code/compare/v0.9.0...HEAD
2460
+ [Unreleased]: https://github.com/akillness/jeo-code/compare/v0.11.3...HEAD
2461
+ [0.11.3]: https://github.com/akillness/jeo-code/releases/tag/v0.11.3
2462
+ [0.11.2]: https://github.com/akillness/jeo-code/releases/tag/v0.11.2
2463
+ [0.11.1]: https://github.com/akillness/jeo-code/releases/tag/v0.11.1
2464
+ [0.11.0]: https://github.com/akillness/jeo-code/releases/tag/v0.11.0
2465
+ [0.10.0]: https://github.com/akillness/jeo-code/releases/tag/v0.10.0
2421
2466
  [0.9.0]: https://github.com/akillness/jeo-code/releases/tag/v0.9.0
2422
2467
  [0.4.5]: https://github.com/akillness/jeo-code/releases/tag/v0.4.5
2423
2468
  [0.4.4]: https://github.com/akillness/jeo-code/releases/tag/v0.4.4
package/README.ja.md CHANGED
@@ -38,7 +38,7 @@
38
38
 
39
39
  ## ハイライト
40
40
 
41
- - **マルチプロバイダ・単一ループ** — Anthropic / OpenAI(+Codex) / Gemini / Antigravity / Ollama / LM Studio に加え、OpenAI・Anthropic 互換クラウド20以上(Groq、DeepSeek、Mistral、OpenRouter、xAI、Kimi、z.ai など)を均一な JSON ツールループで — **さらに自分で登録したエンドポイントも**: `jeo provider add --id my-proxy --base-url https://…`(または `--preset litellm|vllm|sglang|azure-openai|…`)で、LiteLLM プロキシ・自前ホストの vLLM・社内 Anthropic ゲートウェイが、組み込みの `openai` プロバイダを乗っ取ることなく独自のルーティング接頭辞・モデル一覧・認証情報を持てます。入力欄から OAuth ログイン(`/provider login`)、モデル選択は即座にデフォルトとして永続化。プロンプトルーティングは実際に使える認証済み経路だけを自動選択します: Gemini OAuth は provider-qualified な `antigravity/*` エージェントセット(Gemini 3.5 Flash 各ティア、Gemini 3.1 Pro、Claude Sonnet/Opus 4.6)へ向かい、`GEMINI_API_KEY` が必要な public `google/gemini-*` 行は選びません。
41
+ - **マルチプロバイダ・単一ループ** — Anthropic / OpenAI(+Codex) / Gemini / Antigravity / Ollama / LM Studio に加え、OpenAI・Anthropic 互換クラウド20以上(Groq、DeepSeek、Mistral、OpenRouter、xAI、Kimi、z.ai など)を均一な JSON ツールループで — **さらに自分で登録したエンドポイントも**: `jeo provider add --id my-proxy --base-url https://…`(または `--preset litellm|vllm|sglang|azure-openai|…`)で、LiteLLM プロキシ・自前ホストの vLLM・社内 Anthropic ゲートウェイが、組み込みの `openai` プロバイダを乗っ取ることなく独自のルーティング接頭辞・モデル一覧・認証情報を持てます。入力欄から OAuth ログイン(`/provider login`)、モデル選択は毎回新しいデフォルトとして永続化。プロンプトルーティングは実際に使える認証済み経路だけを自動選択します: Gemini OAuth は provider-qualified な `antigravity/*` エージェントセットへ、**アカウントが現在提供している構成のまま**向かい(ライブ検出 — 例: Gemini 3.6 Flash 各ティア、Gemini 3.1 Pro、Claude Sonnet/Opus 4.6; チャット以外や非推奨の ID は除外され、一覧が空でも静的カタログで代用しません)、`GEMINI_API_KEY` が必要な public `google/gemini-*` 行は決して選びません。設定済みルートが未準備のプロバイダを指している場合、jeo はデフォルトへフォールバックする前に同等ティアの認証済みモデルへ切り替えます。
42
42
  - **編集の完全性** — read 出力にコンテンツアンカー(`42ab|`)が付き、アンカー付き編集は現在のファイルと照合・行移動時は自動再マッピング・不一致時は最新内容と共に拒否されます。
43
43
  - **自己修正の検証ループ** — post-edit フック(tsc / eslint / テスト)を設定すると、エージェントが診断を*自ら読み*ループ内で修正します。フックが赤のままだと `done` はブロックされます。
44
44
  - **芝居なしの本物のゲート** — `ralplan` の合議はリポジトリを実際に読む critic サブエージェントで、`[OKAY]` 評決が永続化され `jeo approve` がそれを*要求*します。`ultragoal` は誠実に報告します(スイート1回実行はグローバル信号であり、基準ごとの合格を捏造しません)。
@@ -49,7 +49,8 @@
49
49
  - **蓄積されるスキル** — 行き詰まったターンは、その行き止まりをまさに同じスキルのプロジェクトレベルファイル(`.jeo/skills/<name>.md`、初回書き込み時にバンドルスキルからシード、決定論的なキーワードマッチ、LLM不使用)に書き込むようになりました。これにより次のセッションの `$<skill>` 呼び出しは、バンドルされたドキュメントが永遠に静的なままでいる代わりに、蓄積された「Known Failure Modes」/「Anti-Patterns」の知識を引き継ぎます。`jeo skills lesson <skill> <failure|anti-pattern> "<title>" "<detail>"` で手動記録、`jeo skills eval <skill>` は記録済みの各教訓がスキルの現行ガイダンスでまだカバーされているか、それとも陳腐化したかを実際の LLM 判定で確認します。
50
50
  - **低コスト層のグレーダールーティング** — `/goal` の検証器、`critic` サブエージェントロール、固定されていない `task` ファンアウトバッチは、採点・実行対象の作業と同じフルプライスモデルに黙って乗る代わりに、デフォルトで低コストのクレデンシャル済みモデルを使用します(`resolveVerifierModel`、ブラウザの `verify` アクションについてはビジョン対応能力でフィルタされ、テキストのみの低コストモデルが添付スクリーンショットを黙って取りこぼすことがありません)。
51
51
  - **`jeo routine init`** — スケジュール/issue/PR トリガーで jeo をヘッドレス実行する(`jeo "<prompt>" -p`) GitHub Actions ワークフローを、GitHub 自身のランナー上に生成します — ラップトップは不要で、jeo 自体の内部に新しい攻撃対象領域も一切増えません(インプロセスのスケジューラや Webhook リスナーはありません)。`--dry-run` でプレビュー、`--no-pr` でデフォルトの実行ごとの PR の代わりに直接コミット。
52
- - **リモートサブエージェント可視化(Telegram)** — ボットを一度ペアリング(`jeo notify setup`)すれば、`jeo daemon start` がサブエージェントの状態遷移(開始 → 完了/失敗/キャンセル)ごとにメッセージを送り、`/subagents`、`/steer <id> <subagentId> <msg>`、`/cancel <id> <subagentId>` を受け付けます。Telegram フォーラムトピック、インラインキーボード、画像添付を含む `gjc` 完全パリティを提供し、コマンドはペアリングされたチャットのみ許可されます。
52
+ - **リモート監視・制御(Telegram, Discord, Slack)** — ボットを一度ペアリング(`jeo notify setup --provider telegram|discord|slack`)すれば、共有の `jeo daemon start` 1 つが、稼働中のすべてのセッションにわたってサブエージェントの状態遷移(開始 → 完了/失敗/キャンセル)ごとにメッセージを送り、人間の許可リスト(allowlist)からの `/subagents`、`/steer`、`/cancel`(Discord/Slack ではさらに `/sessions` と `/send`)を受け付けます。Telegram はフォーラムトピック・インラインキーボード・画像添付に対応し、Discord は REST + Gateway WebSocket、Slack は Web API + Socket Mode を使います。すべてのリモート制御はローカルセッションが確認応答(acknowledge)するものであり、実行済みと決めつけることはありません。
53
+ - **ごまかしの効かないゲート** — `approve` は承認対象そのもののプランダイジェストを再検証し、`team` はレビュー済みダイジェストが変わったプランを拒否します。`done` 呼び出しは最新の検証証拠と照らして再チェックされ(後から失敗したチェックは先の合格を無効化し、修正の差し戻し 3 回の後は強い拒否)、`autopilot` ラチェットはロールバック失敗を `rollback_failed` として記録し、ステップを戻したと主張する代わりに非ゼロで停止します。
53
54
  - **ターンをブロックしないセッション単位の非同期実行** — `task` ツールの実際の `tasks` 配列で独立した作業をファンアウトし、親ターンを止めません。detached サブエージェント、バックグラウンド job、行単位 monitor は後続ターンからも `subagent`/`job`/`monitor` の `list`、`inspect`、`await`、`cancel`、`tail` で制御できます。インライン TUI は各ワーカーのライブ activity を専用スロットに表示し、セッション終了または Ctrl-C ですべてのレジストリを破棄します。
54
55
  - **独立した検証者、実際に強制** — プランはもう architect/critic ステップをスキップできません: `PlanSchema` は未検証の変更で終わるプランを拒否し(検証対象の変更より前に置かれた verifier も無効)、`ralplan` のドラフト時点と `team`/`approve` の実行時点の両方で適用されます。すべての architect/critic 評決も実際の証拠を示す必要があり、観測された `read`/`search`/`find`/`ast_grep`/`lsp` 呼び出しがゼロなら、テキストが何を主張していても評決はブロックされます。
55
56
  - **セーフティバウンダリの自動モデルフォールバック** — 未分類のセーフティ拒否(本物のコンテンツポリシー違反ではなく、分類器の誤検知の可能性)は、同じモデルで永遠に譲歩するのではなく、実際に別プロバイダのモデルへ切り替わるようになりました — 既存のレート制限時の高速フォールバックと同じパターンです。`Refusal (<category>)` 形式の確定的な拒否は影響を受けず、引き続きフォールバックなしでハードフェイルします。
@@ -90,22 +91,40 @@ jeo --tmux # 独立した tmux セッションで実行
90
91
  | `/provider add` · `list` · `remove` · `presets` | OpenAI/Anthropic 互換エンドポイントを正式なプロバイダとして登録(ゲートウェイプリセット15種) |
91
92
  | `/agents [role]` · `/subagent` | ロール別(executor/planner/architect/critic)モデル・thinking・ステップ設定 |
92
93
  | `/thinking [level]` | デフォルト推論予算(low…xhigh)の表示/設定 |
93
- | `/route [status\|on\|off\|why\|history [n]]` | セッション単位のプロンプトベース・モデルルーティングの切替 · 直近のルーティング判断の説明 · `history [n]` はこのセッションの直近 n 件(デフォルト 10 件)のルーティング判断を表示(設定済み資格情報 — OAuth または API キー — が実際に提供するモデルの中でのみ自動ルーティング) |
94
+ | `/route [status\|on\|off\|why\|history [n]]` | セッション単位のプロンプトベース・モデルルーティングの切替 · 直近のルーティング判断の説明 · `history [n]` はこのセッションの直近 n 件(デフォルト 10 件)のルーティング判断を表示(毎ターン、設定済み資格情報 — OAuth または API キー — が実際に提供するモデルの中からティアに適したモデルへ自動ルーティングし、設定済みルートが未準備なら同等ティアのモデルへ切り替え) |
94
95
  | `/fast [on\|off\|status]` | 現在のモデルが low 推論をサポートする場合に fast thinking モードを切替 |
95
96
  | `/skill` · `$<skill> [intent]` | ワークフロースキルの一覧/実行(`$team "task"` 形式) |
96
97
  | `/view` · `/diff` · `/find` · `/search` | コード表示、git diff、ファイル/パターン検索 |
97
- || `/new` · `/sessions` | 新しいセッションを開始、または保存済みセッションを一覧表示 |
98
- || `/resume [id|gajae:<session-id>[#<leaf>]] [--any-cwd]` | Jeo セッションを再開、または読み取り専用の正確なバージョンの GJC v5 ブランチを新しい Jeo セッションへインポート |
99
- || `/changelog [--full]` · `/jobs [list|tail|await|cancel]` | リリースノートを表示 · 現在のセッションのバックグラウンドジョブを確認・待機・キャンセル |
98
+ | `/computer [status\|on\|off]` | このセッションのフェイルクローズドなデスクトップ自動化ツールを切替 |
99
+ | `/new` · `/sessions` | 新しいセッションを開始、または保存済みセッションを一覧表示 |
100
+ | `/resume [id\|gajae:<session-id>[#<leaf>]] [--any-cwd]` | Jeo セッションを再開、または読み取り専用の正確なバージョンの GJC v5 ブランチを新しい Jeo セッションへインポート |
101
+ | `/changelog [--full]` · `/jobs [list\|tail\|await\|cancel]` | リリースノートを表示 · 現在のセッションのバックグラウンドジョブを確認・待機・キャンセル |
100
102
  | `/history [n\|all]` · `/export` | 作業アクティビティ履歴を読みやすくスクロールバックへ再出力・トランスクリプト出力 |
101
103
  | `/retry` · `/btw <q>` | 直前要求の再試行 · 履歴に残らないサイド質問 |
102
104
  | `/usage` · `/context` · `/compact` | トークン使用量、コンテキスト内訳、手動コンパクション |
103
105
  | `/theme` · `/config` · `/help` | テーマ、ランタイム設定、ヘルプ |
104
- | `jeo autopilot status` | スコア方向、keep/revert 数、次アクションを示す ratchet ステータスフィールド |
105
106
 
106
107
  > [!CAUTION]
107
108
  > **`/model <name>` で特定モデルを手動指定すると、そのセッション中ルーティングが固定されます。** プロンプトルーティング(`/route`)はモデルが手動固定されていない間のみ毎ターン再評価されます。`/model <name>` で特定モデルを選ぶとその選択が固定され、`/model auto`(ピンを完全に解除)を実行するか、`/route on`(ピンを消さずに優先度だけ上げる — `/route off` した瞬間ピンが復活)を実行するまでルーティングは切り替わりません。`roles.*` 未設定時に `defaultModel` へ確定フォールバックするのは `standard` ティアのみで、`high`/`complex` ティアは通常フォールバック前にライブでクレデンシャル済みの最強モデルを先に探索するため、未設定でも毎ターン別モデルに着地することがあります。**例外:** Antigravity または Gemini OAuth でクレデンシャルされたセッションは一つの資格情報で Anthropic/Google/OpenAI のモデルを再公開しており、その場合 `high`/`complex` は(必ずしも最強ではなく)会社ごとに1モデルへセッション単位で安定的に分散されるため、ターンごとに変わらずそのセッション中は固定されます。
108
109
 
110
+ ## CLI コマンド
111
+
112
+ `jeo --help` が正式な一覧です。よく使うものは次のとおり:
113
+
114
+ | コマンド | 用途 |
115
+ | --- | --- |
116
+ | `jeo [prompt] [--resume [id]] [--tmux] [--worktree <path>] [-p] [-q]` | 対話エージェント(デフォルト); `-p`/`--print` でヘッドレスのワンショット出力、`--worktree` で隔離された兄弟チェックアウト |
117
+ | `jeo setup` · `jeo auth login\|logout\|refresh\|status [provider]` · `jeo doctor` | プロバイダ、自動更新付き OAuth(PKCE)トークン、ライブ接続性 + 古いピンのチェック |
118
+ | `jeo provider list\|add\|remove\|presets\|test` | 名前付きカスタム OpenAI/Anthropic 互換プロバイダ(`/provider` と同じプランナー) |
119
+ | `jeo deep-interview` → `ralplan` → `approve` → `team` → `ultragoal` | Spec-first ワークフロー(後述); `jeo state <skill> read\|write\|clear\|handoff` でレシートを確認 |
120
+ | `jeo notify setup\|status\|health\|test` · `jeo daemon start\|stop\|status\|reload` | Telegram / Discord / Slack 通知と共有制御デーモン |
121
+ | `jeo autopilot <subcommand>` · `jeo ledger <subcommand>` | スコアラチェット付きの自律ビルドループ(`status` は方向、keep/revert 数、次アクションを表示) · プラン横断の追記専用台帳 |
122
+ | `jeo routine init …` | スケジュールまたはリポジトリイベントで jeo をヘッドレス実行する GitHub Actions ワークフローを生成 |
123
+ | `jeo skills list\|read\|sync\|lesson\|eval` | バンドル・ユーザー・プロジェクトのスキル; ドリフトチェック; 記録済みレッスン |
124
+ | `jeo mcp serve\|tools` · `jeo computer <action>` | 外部コントローラ向け MCP stdio サーバー · デスクトップ自動化アクション |
125
+ | `jeo session list\|attach\|rm` · `jeo export [id]` · `jeo chat "<msg>"` | tmux セッション管理 · トランスクリプト出力 · ツールなしのストリーミングチャット |
126
+ | `jeo update [--check]` · `jeo whats-new` · `jeo memory-migrate` | npm からの自己更新 · バンドルされたリリースノート · レガシー `MEMORY.md` → OKF バンドル |
127
+
109
128
  ## Spec-first ワークフロー
110
129
 
111
130
  要件 → プラン → 承認 → 実行 → 検証が `.jeo/state/` で繋がり、各ハンドオフに**ブロック可能な本物のゲート**があります:
@@ -182,32 +201,45 @@ jeo ultragoal
182
201
 
183
202
  `--worktree <name>` は隔離された兄弟 git worktree で jeo を実行するため(パスがあれば再利用、なければ basename ブランチで作成)、リスクのある作業やレビュー対象の作業がメインのチェックアウトに触れることはありません。`jeo mcp serve` は stdio を介して MCP 対応のあらゆるコントローラーに jeo のツールを公開します(`jeo mcp tools` で一覧表示)。`-q`/`--quiet` (または `JEO_QUIET=1`) を追加すると、起動バナー・ウェルカムアニメーション・リリースノート・再開ヒントが抑制され、jeo を別のエージェントと並べて実行したりボットから駆動したりできます。`-p`/`--print` は quiet を含みます。
184
203
 
185
- ## リモート監視・制御(Telegram)
204
+ ## リモート監視・制御(Telegram, Discord & Slack)
205
+
206
+ オプトイン: 共有デーモン 1 つがマシン上の稼働中の `jeo` セッションをすべて監視し、**サブエージェントの状態遷移**(開始 → 完了/失敗/キャンセル)ごとにメッセージを送ります。加えてセッション識別ヘッダー、ターンの開始/終了サマリー、確定したターンのテキストも送りますが、「まだ実行中」という繰り返しの ping は決して送りません。allowlist に登録された人間はチャットから一覧・送信・ステア・キャンセルができ、すべての制御はローカルセッションへ中継されて **確認応答済み(受理であって完了ではない)** または **未確認応答** として報告されます — デーモンがコマンドの実行完了を主張することはありません。
186
207
 
187
208
  ```bash
188
- jeo notify setup # BotFather ボットを一度ペアリング(getMe 検証 + chat-id ペアリング)
189
- jeo notify status # マスクされたトークン、ペアリング済み chat id、デーモン状態
190
- jeo daemon start # シングルトンのバックグラウンドデーモンを起動
191
- jeo daemon status # 実行中かどうかを確認
192
- jeo daemon stop # SIGTERM で停止
209
+ jeo notify setup [--provider telegram|discord|slack] [--token-env NAME] [--app-token-env NAME] [--chat-id ID | --channel-id ID] [--allowed-user-ids ID,ID,...]
210
+ jeo notify status [--provider …] # マスク済みトークン、宛先、allowlist、デーモン状態(stopped / stale / initializing / initialized / pairing)
211
+ jeo notify health [--provider …] # 読み取り専用: ボット ID + 宛先アクセス、何も送信しない
212
+ jeo notify test [--provider …] # 実際のテストメッセージを 1 件送信
213
+ jeo daemon start|stop|status|reload # 共有デーモンにプロバイダ選択はない
193
214
  ```
194
215
 
216
+ | | Telegram(デフォルト) | Discord | Slack |
217
+ | --- | --- | --- | --- |
218
+ | 認証情報 | ボットトークン(`JEO_TELEGRAM_BOT_TOKEN` または `--token-env`) | ボットトークン(`JEO_DISCORD_BOT_TOKEN` または `--token-env`)+ **Message Content** インテント | ボット `xoxb` トークン(`JEO_SLACK_BOT_TOKEN` または `--token-env`)+ アプリ `xapp` トークン(`SLACK_APP_TOKEN` または `--app-token-env`)、Socket Mode 有効化 |
219
+ | 宛先 | チャレンジペアリングによるプライベートチャット(表示された `/start jeo_<code>` を 120 秒以内にボットへ送信)または明示的な `--chat-id`; グループは `--allowed-user-ids` が必要 | 明示的な `--channel-id`(テキストチャネル、DM、アナウンス、または既存スレッド) | ボットが参加済みの明示的な `--channel-id`(`C…`); ワークスペースはトークンのチームに固定 |
220
+ | allowlist | プライベートチャットでは任意、グループでは必須 | 必須(人間のユーザー ID; ボットは無視) | 必須(人間のユーザー ID) |
221
+ | トランスポート | Bot API `getUpdates` の直接ロングポール、ボットトークンごとにポール所有者 1 つ | 送信は REST、受信は Gateway WebSocket | 送信は Web API、受信は Socket Mode WebSocket |
222
+ | 追加機能 | フォーラムトピック、インラインのキャンセルボタン、画像添付、任意のセッション単位トピック(`notifications.telegram.perSessionTopics`、Threaded Mode のプライベートチャットのみ) | セッション通知に返信するとそのセッションへテキスト送信(24 時間) | セッション通知のスレッド内で返信するとそのセッションへテキスト送信(24 時間); 登録済み Slack スラッシュコマンドと `@bot /command` のどちらも使用可 |
223
+ | コマンド | `/subagents` `/steer <session> <subagent> <msg>` `/cancel <session> <subagent>` `/help` | `/sessions` `/subagents` `/send <session> <text>` `/steer …` `/cancel …` `/help` | Discord と同じ |
224
+
225
+ 用語の意味 — これらは互いに置き換えられません:
226
+
227
+ - **セットアップ**は公式 API(Telegram `getMe`、Discord のボット + チャネル検索、Slack `auth.test`/`bots.info`/`conversations.info` と Socket Mode URL)でボット ID と宛先アクセスを検証し、結果を `~/.jeo/config.json` の `notifications.<provider>` 配下に **平文** で保存します — このファイルは非公開に保ってください。メッセージは送信せず、ソケットも開きません。
228
+ - **`initialized`**(`jeo daemon status`)はデーモンプロセスがロックを所有しトランスポートを起動したことを意味し、Telegram ポール、Discord Gateway ハンドシェイク、Slack Socket Mode 接続が成功した証明ではありません。それには `notify health` / `notify test` を使ってください。
229
+ - **トークンや宛先を変更する**と、Discord と Slack では allowlist が(`--allowed-user-ids` を再度渡す必要があります)、Telegram ではセッション単位トピックのマップがリセットされます。まったく同じ認証情報と宛先でセットアップを再実行した場合は保持されます。
230
+ - **リモートコマンドはリテラルなテキスト**(`/subagents` など)で、設定済みチャット/チャネルと allowlist に対して認可されます。Slack ではさらに同じ名前を登録済みスラッシュコマンドとしても受け付けます。allowlist に載ったオペレータは事実上ローカルエージェントの能力を握るため、この一覧は信頼境界として扱ってください。
231
+ - **再試行はプロバイダごとに上限あり**: Telegram は 429 の `retry_after` クールダウン(1 秒〜1 時間にクランプ)を尊重し、ポールのバックオフは別途管理します。Discord と Slack は 429 時にサーバーの `Retry-After` を使って最大 3 回まで再試行し、結果が不明な POST を再送することはありません(プラットフォーム側がすでに受理している可能性があるため)。
232
+ - **オーファン復旧**: デーモンがクラッシュするとロックは `stale` になり、次の `jeo daemon start` が回収します。`jeo daemon stop` は、ホストがプロセス起動時刻を報告できる場合は再利用された PID へのシグナル送信を拒否し、報告できない場合はその旨を伝えます。
233
+ - **これまでの検証状況**: Telegram はエンドツーエンドで検証済み(実送信、ポーリング、クリーンシャットダウン)。Discord と Slack は同じデーモンの背後に実装され、オフラインのトランスポート/配線テストとコード・セキュリティ・型レビューを経ていますが、実際のボット/アプリ認証情報を使ったライブ接続はまだ行っていません。
234
+
195
235
  ```
196
- ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
197
- │ interactive turn │◄──ws──►│ notify daemon │◄─poll──►│ Telegram bot │
198
- │ SubagentRegistry │ │ (singleton) │ │ (paired chat) │
199
- └─────────────────────┘ └─────────────────────┘ └─────────────────────┘
236
+ ┌─────────────────────┐ ┌──────────────────┐ ┌───────────────────────────┐
237
+ │ interactive turn │◄──ws──►│ notify daemon │◄──────►│ Telegram Bot API (poll) │
238
+ │ SubagentRegistry │ (one │ (singleton) │ │ Discord REST + Gateway WS │
239
+ │ session endpoint │ per │ │ │ Slack Web API + Socket WS │
240
+ └─────────────────────┘ session└──────────────────┘ └───────────────────────────┘
200
241
  ```
201
242
 
202
- オプトインかつ遅延バインド: `notifications.enabled` が設定され、かつ detached サブエージェント(`task {detached:true}`)が実際に実行されるまで何もバインドされません。デーモンは生存中のセッションディスカバリファイルをスキャンし、セッションごとにループバック WebSocket を接続、サブエージェントの状態 *遷移*(開始 → 完了/失敗/キャンセル)時にのみメッセージを送信します — 「実行中のまま」の繰り返し通知はありません。受信した Telegram コマンドはペアリング済みチャットのみ許可され、それ以外は黙って破棄されます。
203
-
204
- | コマンド | 効果 |
205
- | --- | --- |
206
- | `/subagents` | 接続中の全セッションの実行中/最近のサブエージェント一覧 |
207
- | `/steer <sessionId> <subagentId> <message>` | 実行中のサブエージェントへライブメッセージを送信 |
208
- | `/cancel <sessionId> <subagentId>` | 実行中のサブエージェントをキャンセル |
209
- | `/help` | コマンドリファレンスを表示 |
210
-
211
243
  ## ルーティン(GitHub Actions)
212
244
 
213
245
  ```bash
@@ -319,10 +351,10 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
319
351
 
320
352
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
321
353
  - **[Unreleased]**
354
+ - **[0.11.3]** (2026-10-01) — Remote control grew from one Telegram bot into one shared daemon serving Telegram, Discord, and Slack — and the workflow gates that were meant to block unverified work (`approve`/`team` plan identity, `done` re-checks, the autopilot ratchet) now actually block it.
355
+ - **[0.11.2]** (2026-09-30) — OpenAI model pickers now follow the authenticated API and Codex catalogs, including subscription-only models, without hidden or stale entries.
322
356
  - **[0.11.1]** (2026-08-25) — Every non-interactive `jeo` run hung forever once Telegram notifications were configured — `echo "..." | jeo`, `jeo -p "..."` in CI, any scripted use. The work completed and the command returned; the process just never exited.
323
357
  - **[0.11.0]** (2026-08-25) — One bad boundary check was corrupting agent context, subagent fan-out, and the Telegram daemon's kill safety at the same time — and none of the three looked related from the outside.
324
- - **[0.10.0]** (2026-08-25) — jeo could only be pointed at ONE user-supplied endpoint (`config.openaiBaseUrl`), and doing so rebound the built-in `openai` provider: it stole the `openai/` routing prefix, collided with real OpenAI model ids, and could not speak the Anthropic Messages protocol at all. There was no way to run a company LiteLLM proxy and a self-hosted vLLM box at the same time, or to reach either from a script.
325
- - **[0.9.17]** (2026-08-18) — Live model discovery already existed (`listProviderModels`/`discoverModels`), but only `jeo auth login openai` ever called it — logging into Anthropic or Antigravity left the account pinned to the maintained static catalog snapshot until the next unrelated live-discovery call happened to run.
326
358
 
327
359
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
328
360
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -38,7 +38,7 @@
38
38
 
39
39
  ## 하이라이트
40
40
 
41
- - **멀티 프로바이더, 단일 루프** — Anthropic / OpenAI(+Codex) / Gemini / Antigravity / Ollama / LM Studio, 그리고 OpenAI·Anthropic 호환 클라우드 20종 이상(Groq, DeepSeek, Mistral, OpenRouter, xAI, Kimi, z.ai 등)까지 균일한 JSON 도구 루프로 — **여기에 직접 등록한 엔드포인트까지**: `jeo provider add --id my-proxy --base-url https://…` (또는 `--preset litellm|vllm|sglang|azure-openai|…`)로 LiteLLM 프록시·자체 호스팅 vLLM·사내 Anthropic 게이트웨이가 내장 `openai` 프로바이더를 덮어쓰지 않고 자기만의 라우팅 접두사·모델 목록·자격증명을 갖습니다. 입력창에서 바로 OAuth 로그인(`/provider login`), 모델 선택은 즉시 기본값으로 영속됩니다. 프롬프트 라우팅은 실제 사용 가능한 인증 경로만 자동 선택합니다: Gemini OAuth는 provider-qualified `antigravity/*` 에이전트 세트(Gemini 3.5 Flash 등급, Gemini 3.1 Pro, Claude Sonnet/Opus 4.6)로만 가고, `GEMINI_API_KEY`가 필요한 public `google/gemini-*` 행은 고르지 않습니다.
41
+ - **멀티 프로바이더, 단일 루프** — Anthropic / OpenAI(+Codex) / Gemini / Antigravity / Ollama / LM Studio, 그리고 OpenAI·Anthropic 호환 클라우드 20종 이상(Groq, DeepSeek, Mistral, OpenRouter, xAI, Kimi, z.ai 등)까지 균일한 JSON 도구 루프 하나로 — **여기에 직접 등록한 엔드포인트까지**: `jeo provider add --id my-proxy --base-url https://…` (또는 `--preset litellm|vllm|sglang|azure-openai|…`)로 LiteLLM 프록시·자체 호스팅 vLLM·사내 Anthropic 게이트웨이가 내장 `openai` 프로바이더를 가로채지 않고 자기만의 라우팅 접두사·모델 목록·자격증명을 갖습니다. OAuth 로그인은 입력창에서 바로(`/provider login`), 모델 선택은 모두 새 기본값으로 영속되며, 프롬프트 라우팅은 실제 사용 가능한 자격증명 경로만 자동 선택합니다: Gemini OAuth는 provider-qualified `antigravity/*` 에이전트 세트를 **계정이 현재 실제로 제공하는 그대로** 사용하고(실시간 디스커버리 — 예: Gemini 3.6 Flash 등급, Gemini 3.1 Pro, Claude Sonnet/Opus 4.6; 비채팅·폐기된 id는 걸러내며, 목록이 비어 있어도 정적 카탈로그로 대체하지 않음), `GEMINI_API_KEY`가 필요한 public `google/gemini-*` 행은 절대 고르지 않습니다. 설정된 라우트가 준비되지 않은 프로바이더를 가리키면 jeo는 기본값으로 폴백하기 전에 동급 티어의 자격증명 있는 모델로 전환합니다.
42
42
  - **편집 무결성** — read 출력에 콘텐츠 앵커(`42ab|`)가 붙고, 앵커 편집은 현재 파일과 대조 검증·줄 이동 시 자동 재매핑·불일치 시 최신 내용과 함께 거부 — 파일을 오염시키지 않습니다.
43
43
  - **자기수정 검증 루프** — post-edit 훅(tsc / eslint / 테스트)을 설정하면 에이전트가 진단을 *직접 읽고* 루프 안에서 수정합니다. 훅이 빨간 상태면 `done`이 차단됩니다.
44
44
  - **연극 없는 진짜 게이트** — `ralplan` 합의는 실제 저장소를 읽는 critic 서브에이전트이며 `[OKAY]` 평결이 영속되고 `jeo approve`가 이를 *요구*합니다. `ultragoal`은 정직하게 보고합니다(스위트 1회 실행은 전역 신호일 뿐, 기준별 통과를 조작하지 않음).
@@ -49,7 +49,8 @@
49
49
  - **누적되는 스킬** — 정체된 턴은 이제 그 막다른 지점을 바로 그 스킬의 프로젝트 레벨 파일(`.jeo/skills/<name>.md`, 첫 작성 시 번들 스킬로 시드, 결정론적 키워드 매칭, LLM 미사용)에 기록하므로, 다음 세션의 `$<skill>` 호출은 번들 문서가 영원히 정적으로 남는 대신 누적된 "Known Failure Modes"/"Anti-Patterns" 지식을 함께 가져갑니다. 수동 항목은 `jeo skills lesson <skill> <failure|anti-pattern> "<title>" "<detail>"`로; `jeo skills eval <skill>`은 기록된 각 교훈이 스킬의 현재 가이던스로 여전히 커버되는지 아니면 낡았는지를 실제 LLM 판단으로 확인합니다.
50
50
  - **저비용 등급 채점 라우팅** — `/goal` 검증기, `critic` 서브에이전트 역할, 그리고 고정되지 않은 `task` 팬아웃 배치는 채점·실행 대상인 작업과 같은 풀프라이스 모델에 조용히 편승하는 대신 기본적으로 저비용 크레덴셜 모델을 사용합니다(`resolveVerifierModel`, 브라우저 `verify` 액션에 대해서는 비전 능력으로 필터링되어 텍스트 전용 저비용 모델이 첨부된 스크린샷을 조용히 누락시키지 않도록 합니다).
51
51
  - **`jeo routine init`** — 스케줄/이슈/PR 트리거에서 jeo를 헤드리스로 실행하는(`jeo "<prompt>" -p`) GitHub Actions 워크플로를 생성합니다, GitHub 자체 러너 위에서 — 노트북이 필요 없고, jeo 내부에 새로운 공격 표면도 전혀 추가되지 않습니다(인프로세스 스케줄러도 웹훅 리스너도 없음). `--dry-run`으로 미리보기, `--no-pr`로 기본값인 실행당 PR 대신 직접 커밋.
52
- - **원격 서브에이전트 가시성(Telegram)** — 봇을 한 번 페어링(`jeo notify setup`)하면, `jeo daemon start`가 서브에이전트 상태 전환(시작 → 완료/실패/취소)마다 메시지를 보내고 `/subagents`, `/steer <id> <subagentId> <msg>`, `/cancel <id> <subagentId>`를 되받습니다. 이제 Telegram 포럼 토픽, 인라인 키보드, 이미지 첨부 파일 지원 등 `gjc`와의 완전한 패리티를 제공하며, 명령은 페어링된 채팅에서만 허용됩니다.
52
+ - **원격 모니터링 & 제어(Telegram, Discord, Slack)** — 봇을 한 번 페어링(`jeo notify setup --provider telegram|discord|slack`)하면, 공유 `jeo daemon start` 하나가 모든 활성 세션에 걸쳐 서브에이전트 상태 전환(시작 → 완료/실패/취소)마다 메시지를 보내고, 사람 allowlist로부터 `/subagents`, `/steer`, `/cancel`(Discord/Slack에서는 `/sessions`, `/send` 추가)을 되받습니다. Telegram은 포럼 토픽·인라인 키보드·이미지 첨부를 더하고, Discord는 REST + Gateway WebSocket, Slack은 Web API + Socket Mode를 사용합니다. 모든 원격 제어는 로컬 세션이 확인 응답(acknowledge)하며, 실행된 것으로 가정하지 않습니다.
53
+ - **말로 넘길 수 없는 게이트** — `approve`는 승인 대상인 바로 그 플랜 다이제스트를 재검증하고, `team`은 리뷰된 다이제스트가 바뀐 플랜을 거부합니다. `done` 호출은 최신 검증 증거와 대조해 재확인되며(나중에 실패한 검사가 앞선 통과를 무효화하고, 교정 반송 3회 뒤에는 하드 거부), `autopilot` ratchet은 실패한 롤백을 `rollback_failed`로 기록하고 되돌린 스텝이라고 주장하는 대신 0이 아닌 코드로 중단합니다.
53
54
  - **턴을 막지 않는 세션 범위 비동기 실행** — `task` 도구의 실제 `tasks` 배열로 독립 작업을 팬아웃해 부모 턴을 막지 않습니다. detached 서브에이전트, 백그라운드 job, 줄 단위 monitor는 이후 턴에서도 `subagent`/`job`/`monitor`의 `list`, `inspect`, `await`, `cancel`, `tail`로 계속 제어할 수 있습니다. 인라인 TUI는 각 워커의 실시간 activity를 별도 슬롯에 표시하고 세션 종료 또는 Ctrl-C에서 모든 레지스트리를 정리합니다.
54
55
  - **실제로 강제되는 독립 검증자** — 플랜은 이제 architect/critic 단계를 건너뛸 수 없습니다: `PlanSchema`는 미검증 변이로 끝나는 모든 플랜을 거부하며(검증 대상 변이보다 앞에 배치된 검증자도 인정하지 않음), `ralplan` 드래프트 시점과 `team`/`approve` 실행 시점 양쪽 모두에 적용됩니다. 모든 architect/critic 평결도 실제 증거를 제시해야 하며, 관찰된 `read`/`search`/`find`/`ast_grep`/`lsp` 호출이 0건이면 텍스트가 무엇을 주장하든 평결이 차단됩니다.
55
56
  - **안전 경계 자동 모델 폴백** — 미분류 안전 거부(실제 콘텐츠 정책 위반이 아니라 분류기의 오탐일 가능성)가 발생하면 이제 같은 모델에서 영원히 물러서는 대신 실제로 다른 프로바이더의 모델로 전환합니다 — 기존 rate-limit 빠른 폴백과 동일한 방식입니다. `Refusal (<category>)` 형태의 결정론적 거부는 영향받지 않고 여전히 폴백 없이 하드 실패합니다.
@@ -86,27 +87,45 @@ jeo --tmux # 독립 tmux 세션에서 실행
86
87
 
87
88
  | 명령 | 설명 |
88
89
  | --- | --- |
89
- | `/model` · `/provider` | 모델/프로바이더 선택; `/model`에서 기본/역할 배지, Ralph식 하위 리스트 역할·thinking 선택, OpenAI Codex 역할 프리셋을 한 흐름으로 설정 |
90
+ | `/model` · `/provider` | 모델/프로바이더 선택; `/model`에서 기본/역할 배지, Ralph식 중첩 Set-as-role thinking 선택, OpenAI Codex 역할 프리셋을 한 흐름으로 설정 |
90
91
  | `/provider login <name>` · `/logout` | 입력창에서 OAuth 로그인/로그아웃 |
91
92
  | `/provider add` · `list` · `remove` · `presets` | OpenAI/Anthropic 호환 엔드포인트를 정식 프로바이더로 등록 (게이트웨이 프리셋 15종) |
92
93
  | `/agents [role]` · `/subagent` | 역할별(executor/planner/architect/critic) 모델·thinking·스텝 구성 |
93
94
  | `/thinking [level]` | 기본 추론 예산(low…xhigh) 조회/설정 |
94
- | `/route [status\|on\|off\|why\|history [n]]` | 세션별 프롬프트 기반 모델 라우팅 켜기/끄기 · 마지막 라우팅 결정 설명 · `history [n]`은 이번 세션의 최근 n개(기본 10개) 라우팅 결정 표시(설정된 자격증명 — OAuth 또는 API 키 — 이 실제로 서비스하는 모델 안에서만 자동 라우팅) |
95
+ | `/route [status\|on\|off\|why\|history [n]]` | 이 세션의 프롬프트 기반 모델 라우팅 켜기/끄기 · 마지막 라우팅 결정 설명 · `history [n]`은 이번 세션의 최근 n개(기본 10개) 라우팅 결정 표시(설정된 자격증명 — OAuth 또는 API 키 — 이 실제로 서비스하는 모델 안에서 매 턴 티어에 맞는 모델로 자동 라우팅하고, 설정된 라우트가 준비되지 않았으면 동급 티어 모델로 전환) |
95
96
  | `/fast [on\|off\|status]` | 현재 모델이 low 추론을 지원하면 fast thinking 모드를 켜고 끔 |
96
- | `/skill` · `$<skill> [intent]` | 워크플로 스킬 목록/실행(`$team "작업"` 스타일) |
97
+ | `/skill` · `$<skill> [intent]` | 워크플로 스킬 목록/실행(`$team "task"` 스타일) |
97
98
  | `/view` · `/diff` · `/find` · `/search` | 코드 보기, git diff, 파일/패턴 검색 |
98
- || `/new` · `/sessions` | 새 세션 시작 또는 저장된 세션 목록 표시 |
99
- || `/resume [id|gajae:<session-id>[#<leaf>]] [--any-cwd]` | Jeo 세션을 재개하거나 읽기 전용 정확한 버전의 GJC v5 브랜치를 새 Jeo 세션으로 가져오기 |
100
- || `/changelog [--full]` · `/jobs [list|tail|await|cancel]` | 릴리스 노트 표시 · 현재 세션의 백그라운드 작업 조회·대기·취소 |
99
+ | `/computer [status\|on\|off]` | 이 세션의 fail-closed 데스크톱 자동화 도구 켜기/끄기 |
100
+ | `/new` · `/sessions` | 새 세션 시작 또는 저장된 세션 목록 표시 |
101
+ | `/resume [id\|gajae:<session-id>[#<leaf>]] [--any-cwd]` | Jeo 세션을 재개하거나 읽기 전용 정확 버전 GJC v5 브랜치를 새 Jeo 세션으로 가져오기 |
102
+ | `/changelog [--full]` · `/jobs [list\|tail\|await\|cancel]` | 릴리스 노트 표시 · 이 세션의 백그라운드 job 조회·대기·취소 |
101
103
  | `/history [n\|all]` · `/export` | 작업 활동 히스토리를 읽기 좋게 스크롤백에 재출력 · 트랜스크립트 내보내기 |
102
- | `/retry` · `/btw <질문>` | 마지막 요청 재시도 · 히스토리에 안 남는 사이드 질문 |
104
+ | `/retry` · `/btw <q>` | 마지막 요청 재시도 · 히스토리에 안 남는 사이드 질문 |
103
105
  | `/usage` · `/context` · `/compact` | 토큰 사용량, 컨텍스트 내역, 수동 컴팩션 |
104
106
  | `/theme` · `/config` · `/help` | 테마, 런타임 설정, 도움말 |
105
- | `jeo autopilot status` | 점수 방향, keep/revert 횟수, 다음 액션을 보여주는 ratchet 상태 필드 |
106
107
 
107
108
  > [!CAUTION]
108
109
  > **`/model <name>`으로 특정 모델을 수동 지정하면 그 세션 동안 라우팅이 고정됩니다.** 프롬프트 라우팅(`/route`)은 모델이 수동으로 고정되지 않은 동안만 매 턴 재평가됩니다. `/model <name>`으로 특정 모델을 선택하면 그 선택이 그대로 고정되며, `/model auto`(핀을 완전히 해제)를 실행하거나 `/route on`(핀을 지우지 않고 그보다 우선순위를 높임 — `/route off` 하는 즉시 핀이 다시 살아남)을 실행하기 전까지는 라우팅이 다시 전환되지 않습니다. `roles.*` 항목 미설정 시 `defaultModel`로 확정 폴백되는 건 `standard` 티어뿐이며, `high`/`complex` 티어는 보통 폴백 전에 실시간으로 크레덴셜된 가장 강력한 모델을 먼저 탐색하므로, 설정이 없어도 매 턴 다른 모델로 갈 수 있습니다. **예외:** Antigravity 또는 Gemini OAuth로 크레덴셜된 세션은 하나의 크레덴셜로 Anthropic/Google/OpenAI 모델을 함께 재노출하는데, 이 경우 `high`/`complex`는 (항상 최강 모델이 아니라) 회사당 모델 1개로 세션-안정적으로 분산되어, 턴마다 바뀌지 않고 해당 세션 내내 고정됩니다.
109
110
 
111
+ ## CLI 명령
112
+
113
+ `jeo --help`가 공식 목록이며, 자주 쓰게 될 것들은 다음과 같습니다:
114
+
115
+ | 명령 | 용도 |
116
+ | --- | --- |
117
+ | `jeo [prompt] [--resume [id]] [--tmux] [--worktree <path>] [-p] [-q]` | 대화형 에이전트(기본); `-p`/`--print`는 헤드리스 1회성 출력, `--worktree`는 격리된 형제 체크아웃 |
118
+ | `jeo setup` · `jeo auth login\|logout\|refresh\|status [provider]` · `jeo doctor` | 프로바이더, 자동 갱신되는 OAuth(PKCE) 토큰, 실시간 연결 + stale 핀 점검 |
119
+ | `jeo provider list\|add\|remove\|presets\|test` | 이름 붙인 커스텀 OpenAI/Anthropic 호환 프로바이더(`/provider`와 같은 플래너) |
120
+ | `jeo deep-interview` → `ralplan` → `approve` → `team` → `ultragoal` | Spec-first 워크플로(아래 참조); `jeo state <skill> read\|write\|clear\|handoff`로 기록(receipt)을 확인 |
121
+ | `jeo notify setup\|status\|health\|test` · `jeo daemon start\|stop\|status\|reload` | Telegram / Discord / Slack 알림과 공유 제어 데몬 |
122
+ | `jeo autopilot <subcommand>` · `jeo ledger <subcommand>` | 점수 ratchet이 있는 자율 빌드 루프(`status`는 방향, keep/revert 횟수, 다음 액션 표시) · 플랜 간 append-only 원장 |
123
+ | `jeo routine init …` | 스케줄 또는 저장소 이벤트에서 jeo를 헤드리스로 실행하는 GitHub Actions 워크플로 생성 |
124
+ | `jeo skills list\|read\|sync\|lesson\|eval` | 번들·사용자·프로젝트 스킬; 드리프트 점검; 기록된 레슨 |
125
+ | `jeo mcp serve\|tools` · `jeo computer <action>` | 외부 컨트롤러용 MCP stdio 서버 · 데스크톱 자동화 액션 |
126
+ | `jeo session list\|attach\|rm` · `jeo export [id]` · `jeo chat "<msg>"` | tmux 세션 관리 · 트랜스크립트 내보내기 · 도구 없는 스트리밍 채팅 |
127
+ | `jeo update [--check]` · `jeo whats-new` · `jeo memory-migrate` | npm 자가 업데이트 · 번들 릴리스 노트 · 레거시 `MEMORY.md` → OKF 번들 |
128
+
110
129
  ## Spec-first 워크플로
111
130
 
112
131
  요구사항 → 플랜 → 승인 → 실행 → 검증이 `.jeo/state/`로 이어지며, 모든 핸드오프에 **차단 가능한 진짜 게이트**가 있습니다:
@@ -183,31 +202,45 @@ jeo ultragoal
183
202
 
184
203
  `--worktree <name>`는 격리된 형제 git worktree에서 jeo를 실행하므로(경로가 있으면 재사용, 없으면 basename 브랜치로 생성) 위험하거나 검토가 필요한 작업이 메인 체크아웃을 건드리지 않습니다. `jeo mcp serve`는 stdio를 통해 MCP를 지원하는 모든 컨트롤러에 jeo의 도구를 노출합니다(`jeo mcp tools`로 목록 확인). `-q`/`--quiet` (또는 `JEO_QUIET=1`)를 추가하면 시작 배너, 환영 애니메이션, 릴리스 노트, 재개 힌트가 억제되어 jeo를 다른 에이전트와 나란히 실행하거나 봇으로 구동할 수 있습니다. `-p`/`--print`는 quiet를 함의합니다.
185
204
 
186
- ## 원격 모니터링 & 제어 (Telegram)
205
+ ## 원격 모니터링 & 제어 (Telegram, Discord & Slack)
206
+
207
+ 옵트인: 공유 데몬 하나가 머신의 모든 활성 `jeo` 세션을 지켜보다가 **서브에이전트 상태 전환**(시작 → 완료/실패/취소)마다 메시지를 보내고, 세션 식별 헤더, 턴 시작/종료 요약, 확정된 턴 텍스트도 함께 전송합니다 — "여전히 실행 중" 반복 핑은 절대 보내지 않습니다. allowlist에 등록된 사람은 채팅에서 목록 조회·전송·조종·취소를 할 수 있고, 모든 제어는 로컬 세션으로 중계되어 **확인 응답됨(수락됨, 완료 아님)** 또는 **확인 응답 안 됨**으로 보고됩니다 — 데몬은 명령이 실행됐다고 절대 주장하지 않습니다.
187
208
 
188
209
  ```bash
189
- jeo notify setup # BotFather 봇 한 번 페어링 (getMe 검증 + chat-id 페어링)
190
- jeo notify status # 마스킹된 토큰, 페어링된 chat id, 데몬 상태
191
- jeo daemon start # 싱글턴 백그라운드 데몬 실행
192
- jeo daemon status # 실행 여부 확인
193
- jeo daemon stop # SIGTERM으로 종료
210
+ jeo notify setup [--provider telegram|discord|slack] [--token-env NAME] [--app-token-env NAME] [--chat-id ID | --channel-id ID] [--allowed-user-ids ID,ID,...]
211
+ jeo notify status [--provider …] # 마스킹된 토큰, 목적지, allowlist, 데몬 상태 (stopped / stale / initializing / initialized / pairing)
212
+ jeo notify health [--provider …] # 읽기 전용: 봇 신원 + 목적지 접근 검증, 아무것도 보내지 않음
213
+ jeo notify test [--provider …] # 실제 테스트 메시지 1건 전송
214
+ jeo daemon start|stop|status|reload # 공유 데몬에는 프로바이더 선택자가 없음
194
215
  ```
195
216
 
217
+ | | Telegram (기본) | Discord | Slack |
218
+ | --- | --- | --- | --- |
219
+ | 자격증명 | 봇 토큰(`JEO_TELEGRAM_BOT_TOKEN` 또는 `--token-env`) | 봇 토큰(`JEO_DISCORD_BOT_TOKEN` 또는 `--token-env`) + **Message Content** intent | 봇 `xoxb` 토큰(`JEO_SLACK_BOT_TOKEN` 또는 `--token-env`) + 앱 `xapp` 토큰(`SLACK_APP_TOKEN` 또는 `--app-token-env`), Socket Mode 활성화 |
220
+ | 목적지 | challenge 페어링으로 개인 채팅(표시된 `/start jeo_<code>`를 120초 내에 봇에 전송) 또는 명시적 `--chat-id`; 그룹은 `--allowed-user-ids` 필요 | 명시적 `--channel-id`(텍스트 채널, DM, 공지 채널, 또는 기존 스레드) | 봇이 참여한 명시적 `--channel-id`(`C…`); 워크스페이스는 토큰의 팀에 고정 |
221
+ | Allowlist | 개인 채팅은 선택, 그룹은 필수 | 필수(사람 사용자 ID; 봇은 무시) | 필수(사람 사용자 ID) |
222
+ | 전송 방식 | Bot API `getUpdates` 직접 long-poll, 봇 토큰당 poll owner 하나 | 송신은 REST, 수신은 Gateway WebSocket | 송신은 Web API, 수신은 Socket Mode WebSocket |
223
+ | 추가 기능 | 포럼 토픽, 인라인 취소 버튼, 이미지 첨부, 선택적 세션별 토픽(`notifications.telegram.perSessionTopics`, Threaded Mode가 켜진 개인 채팅만) | 세션 알림에 답글을 달면 그 세션에 텍스트 전송(24시간) | 세션 알림의 스레드에 답글을 달면 그 세션에 텍스트 전송(24시간); 등록된 Slack 슬래시 명령과 `@bot /command` 모두 동작 |
224
+ | 명령 | `/subagents` `/steer <session> <subagent> <msg>` `/cancel <session> <subagent>` `/help` | `/sessions` `/subagents` `/send <session> <text>` `/steer …` `/cancel …` `/help` | Discord와 동일 |
225
+
226
+ 이 단어들은 서로 바꿔 쓸 수 없으므로, 각각의 의미를 정확히 짚어 둡니다:
227
+
228
+ - **Setup**은 공식 API로 봇 신원과 목적지 접근을 검증하고(Telegram `getMe`, Discord 봇 + 채널 조회, Slack `auth.test`/`bots.info`/`conversations.info`와 Socket Mode URL) 결과를 `~/.jeo/config.json`의 `notifications.<provider>` 아래에 저장합니다 — **평문**이므로 그 파일은 비공개로 유지하세요. 메시지를 보내지도, 소켓을 열지도 않습니다.
229
+ - **`initialized`**(`jeo daemon status`)는 데몬 프로세스가 락을 소유하고 전송 계층을 시작했다는 뜻이지, Telegram poll·Discord Gateway 핸드셰이크·Slack Socket Mode 연결이 성공했다는 증거가 아닙니다. 그 확인에는 `notify health` / `notify test`를 사용하세요.
230
+ - **토큰이나 목적지를 바꾸면** Discord와 Slack은 allowlist가 초기화되고(`--allowed-user-ids`를 다시 넘겨야 함), Telegram은 세션별 토픽 맵이 초기화됩니다. 정확히 같은 자격증명과 목적지로 setup을 다시 실행하면 그대로 유지됩니다.
231
+ - **원격 명령은 리터럴 텍스트**(`/subagents`, …)이며 설정된 채팅/채널과 allowlist에만 인가됩니다; Slack은 추가로 같은 이름을 등록된 슬래시 명령으로도 받습니다. allowlist에 등록된 운영자는 사실상 로컬 에이전트의 능력을 그대로 쥐게 되므로, 그 목록을 신뢰 경계로 다루세요.
232
+ - **재시도는 프로바이더별로 상한이 있습니다**: Telegram은 429의 `retry_after` 쿨다운(1초–1시간으로 클램프)을 poll 백오프와 별도로 지키고, Discord와 Slack은 429에 대해 서버의 `Retry-After`를 따라 최대 3회만 재시도하며, 결과를 알 수 없는 POST는 절대 재전송하지 않습니다(플랫폼이 이미 받았을 수 있음).
233
+ - **고아 복구**: 데몬이 크래시하면 락이 `stale`이 되고, 다음 `jeo daemon start`가 회수합니다. `jeo daemon stop`은 호스트가 프로세스 시작 시각을 보고할 수 있으면 재활용된 PID에 시그널을 보내기를 거부하고, 보고할 수 없으면 그 사실을 알립니다.
234
+ - **지금까지 검증된 범위**: Telegram은 엔드투엔드(실제 전송, 폴링, 깔끔한 종료). Discord와 Slack은 같은 데몬 뒤에 오프라인 전송/배선 테스트와 코드·보안·타입 리뷰로 구현되어 있으며, 실제 봇/앱 자격증명으로 라이브 연결은 아직 실행하지 않았습니다.
235
+
196
236
  ```
197
- ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
198
- │ interactive turn │◄──ws──►│ notify daemon │◄─poll──►│ Telegram bot │
199
- │ SubagentRegistry │ │ (singleton) │ │ (paired chat) │
200
- └─────────────────────┘ └─────────────────────┘ └─────────────────────┘
237
+ ┌─────────────────────┐ ┌──────────────────┐ ┌───────────────────────────┐
238
+ │ interactive turn │◄──ws──►│ notify daemon │◄──────►│ Telegram Bot API (poll) │
239
+ │ SubagentRegistry │ (one │ (singleton) │ │ Discord REST + Gateway WS │
240
+ │ session endpoint │ per │ │ │ Slack Web API + Socket WS │
241
+ └─────────────────────┘ session└──────────────────┘ └───────────────────────────┘
201
242
  ```
202
243
 
203
- 옵트인이며 지연 바인딩됩니다: `notifications.enabled`가 설정되고 detached 서브에이전트(`task {detached:true}`)가 실제로 실행되어야만 동작합니다. 데몬은 살아있는 세션 디스커버리 파일을 스캔해 세션별로 루프백 WebSocket을 연결하고, 서브에이전트 상태 *전환* 시점(시작 → 완료/실패/취소)에만 메시지를 보냅니다 — "여전히 실행 중" 같은 반복 알림은 없습니다. 이제 Telegram 포럼 토픽, 인라인 키보드, 이미지 첨부 파일 지원 등 `gjc`와의 완전한 패리티를 제공합니다. 수신되는 Telegram 명령은 페어링된 채팅에서만 허용되며, 그 외는 조용히 무시됩니다.
204
-
205
- | 명령 | 동작 |
206
- | --- | --- |
207
- | `/subagents` | 연결된 모든 세션의 실행 중/최근 서브에이전트 목록 |
208
- | `/steer <sessionId> <subagentId> <message>` | 실행 중인 서브에이전트에 실시간 메시지 전송 |
209
- | `/cancel <sessionId> <subagentId>` | 실행 중인 서브에이전트 취소 |
210
- | `/help` | 명령 안내 표시 |
211
244
 
212
245
  ## 루틴 (GitHub Actions)
213
246
 
@@ -320,10 +353,10 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
320
353
 
321
354
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
322
355
  - **[Unreleased]**
356
+ - **[0.11.3]** (2026-10-01) — Remote control grew from one Telegram bot into one shared daemon serving Telegram, Discord, and Slack — and the workflow gates that were meant to block unverified work (`approve`/`team` plan identity, `done` re-checks, the autopilot ratchet) now actually block it.
357
+ - **[0.11.2]** (2026-09-30) — OpenAI model pickers now follow the authenticated API and Codex catalogs, including subscription-only models, without hidden or stale entries.
323
358
  - **[0.11.1]** (2026-08-25) — Every non-interactive `jeo` run hung forever once Telegram notifications were configured — `echo "..." | jeo`, `jeo -p "..."` in CI, any scripted use. The work completed and the command returned; the process just never exited.
324
359
  - **[0.11.0]** (2026-08-25) — One bad boundary check was corrupting agent context, subagent fan-out, and the Telegram daemon's kill safety at the same time — and none of the three looked related from the outside.
325
- - **[0.10.0]** (2026-08-25) — jeo could only be pointed at ONE user-supplied endpoint (`config.openaiBaseUrl`), and doing so rebound the built-in `openai` provider: it stole the `openai/` routing prefix, collided with real OpenAI model ids, and could not speak the Anthropic Messages protocol at all. There was no way to run a company LiteLLM proxy and a self-hosted vLLM box at the same time, or to reach either from a script.
326
- - **[0.9.17]** (2026-08-18) — Live model discovery already existed (`listProviderModels`/`discoverModels`), but only `jeo auth login openai` ever called it — logging into Anthropic or Antigravity left the account pinned to the maintained static catalog snapshot until the next unrelated live-discovery call happened to run.
327
360
 
328
361
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
329
362
  <!-- CHANGELOG:END -->