jeo-code 0.11.2 → 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 +35 -1
- package/README.ja.md +59 -27
- package/README.ko.md +62 -29
- package/README.md +58 -26
- package/README.zh.md +61 -29
- package/package.json +1 -1
- package/src/agent/config-schema.ts +20 -7
- package/src/agent/debug-session.ts +7 -3
- package/src/agent/engine.ts +46 -102
- package/src/agent/loop-guards.ts +11 -13
- package/src/agent/notify/daemon-control.ts +109 -24
- package/src/agent/notify/discord-api.ts +148 -0
- package/src/agent/notify/discord-daemon.ts +443 -0
- package/src/agent/notify/paths.ts +1 -1
- package/src/agent/notify/session-discovery.ts +16 -0
- package/src/agent/notify/session-endpoint.ts +43 -25
- package/src/agent/notify/slack-api.ts +177 -0
- package/src/agent/notify/slack-daemon.ts +361 -0
- package/src/agent/notify/telegram-daemon.ts +215 -83
- package/src/agent/state.ts +13 -1
- package/src/ai/model-discovery.ts +13 -21
- package/src/autopilot.ts +14 -5
- package/src/cli/runner.ts +4 -4
- package/src/commands/approve.ts +21 -16
- package/src/commands/daemon.ts +5 -3
- package/src/commands/launch/agents-slash.ts +8 -3
- package/src/commands/launch.ts +36 -44
- package/src/commands/notify.ts +215 -83
- package/src/commands/ralplan.ts +2 -1
- package/src/commands/team.ts +17 -12
- package/src/commands/ultragoal.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,35 @@ 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
|
+
|
|
12
41
|
## [0.11.2] - 2026-09-30
|
|
13
42
|
_OpenAI model pickers now follow the authenticated API and Codex catalogs, including subscription-only models, without hidden or stale entries._
|
|
14
43
|
|
|
@@ -2428,7 +2457,12 @@ _Initial release._
|
|
|
2428
2457
|
### Added
|
|
2429
2458
|
- Initial jeo-code agent and CLI.
|
|
2430
2459
|
|
|
2431
|
-
[Unreleased]: https://github.com/akillness/jeo-code/compare/v0.
|
|
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
|
|
2432
2466
|
[0.9.0]: https://github.com/akillness/jeo-code/releases/tag/v0.9.0
|
|
2433
2467
|
[0.4.5]: https://github.com/akillness/jeo-code/releases/tag/v0.4.5
|
|
2434
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`)
|
|
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
|
-
-
|
|
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 件)のルーティング判断を表示(
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
189
|
-
jeo notify status
|
|
190
|
-
jeo
|
|
191
|
-
jeo
|
|
192
|
-
jeo daemon stop
|
|
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──►│
|
|
198
|
-
│
|
|
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.
|
|
322
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.
|
|
323
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.
|
|
324
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.
|
|
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
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 도구
|
|
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
|
-
- **원격
|
|
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식
|
|
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]]` |
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
190
|
-
jeo notify status
|
|
191
|
-
jeo
|
|
192
|
-
jeo
|
|
193
|
-
jeo daemon stop
|
|
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──►│
|
|
199
|
-
│
|
|
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.
|
|
323
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.
|
|
324
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.
|
|
325
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.
|
|
326
|
-
- **[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.
|
|
327
360
|
|
|
328
361
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
329
362
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Run `jeo` inside a repository and it reads files, edits them, runs commands, and
|
|
|
42
42
|
|
|
43
43
|
- **Prompt routing (cost-aware, credential-aware)** — every turn can auto-route to a tier-appropriate model among only the providers your configured credentials actually serve (`/route [status|on|off|why|history]`), with a live equivalent-model fallback whenever a routed provider is rate-limited, unauthenticated, unreachable, or silently times out — see `/route why` for the last decision, `/route history` for the recent ones.
|
|
44
44
|
- **Computer use (desktop automation)** — a fail-closed `computer` tool (screenshot/click/type/scroll/drag/batch) gated by both a config flag and an independent kill-switch/heartbeat supervisor; toggle it for the current session with `/computer [status|on|off]` without touching `~/.jeo/config.json`.
|
|
45
|
-
- **Multi-provider, one loop** — Anthropic / OpenAI (+Codex) / Gemini / Antigravity / Ollama / LM Studio, plus 20+ OpenAI- and Anthropic-compatible clouds (Groq, DeepSeek, Mistral, OpenRouter, xAI, Kimi, z.ai, …), all behind one uniform JSON tool loop — **plus any endpoint you register yourself**: `jeo provider add --id my-proxy --base-url https://…` (or `--preset litellm|vllm|sglang|azure-openai|…`) gives a LiteLLM proxy, a self-hosted vLLM box or a corporate Anthropic gateway its own routing prefix, model list and credential, instead of hijacking the built-in `openai` provider. OAuth login happens from the input box (`/provider login`), every model pick persists as the new default, and prompt routing only auto-selects usable credentialed paths: Gemini OAuth goes through the provider-qualified `antigravity/*` agent set (Gemini 3.
|
|
45
|
+
- **Multi-provider, one loop** — Anthropic / OpenAI (+Codex) / Gemini / Antigravity / Ollama / LM Studio, plus 20+ OpenAI- and Anthropic-compatible clouds (Groq, DeepSeek, Mistral, OpenRouter, xAI, Kimi, z.ai, …), all behind one uniform JSON tool loop — **plus any endpoint you register yourself**: `jeo provider add --id my-proxy --base-url https://…` (or `--preset litellm|vllm|sglang|azure-openai|…`) gives a LiteLLM proxy, a self-hosted vLLM box or a corporate Anthropic gateway its own routing prefix, model list and credential, instead of hijacking the built-in `openai` provider. OAuth login happens from the input box (`/provider login`), every model pick persists as the new default, and prompt routing only auto-selects usable credentialed paths: Gemini OAuth goes through the provider-qualified `antigravity/*` agent set **as the account currently serves it** (live-discovered — e.g. Gemini 3.6 Flash tiers, Gemini 3.1 Pro, Claude Sonnet/Opus 4.6; non-chat and deprecated ids filtered out, no static-catalog stand-in when the list is empty), never public `google/gemini-*` rows that require `GEMINI_API_KEY`; if a configured route points at an unready provider, jeo switches to an equivalent credentialed tier model before falling back to the default.
|
|
46
46
|
|
|
47
47
|
- **Edit integrity** — read output carries content anchors (`42ab|`); anchored edits are verified against the current file, re-mapped when lines shifted, and rejected with fresh content instead of corrupting.
|
|
48
48
|
- **Self-correcting verification loop** — configure a post-edit hook (tsc / eslint / tests) and the agent *sees* the diagnostics and fixes them in-loop; a red hook blocks `done` until resolved.
|
|
@@ -54,7 +54,8 @@ Run `jeo` inside a repository and it reads files, edits them, runs commands, and
|
|
|
54
54
|
- **Skills that compound** — a stalled turn now writes the dead end into the SAME skill's project-level file (`.jeo/skills/<name>.md`, seeded from the bundled skill on first write, deterministic keyword match, no LLM), so the next session's `$<skill>` invocation carries accumulated "Known Failure Modes"/"Anti-Patterns" knowledge instead of the bundled doc staying static forever. `jeo skills lesson <skill> <failure|anti-pattern> "<title>" "<detail>"` for manual entries; `jeo skills eval <skill>` runs a real LLM judgment on whether each recorded lesson is still covered by the skill's current guidance or has gone stale.
|
|
55
55
|
- **Cheap-tier grader routing** — the `/goal` verifier, the `critic` subagent role, and unpinned `task` fan-out batches default to a cheap credentialed model instead of silently riding the same full-price model as the work they're grading/executing (`resolveVerifierModel`, vision-capability-filtered for the browser `verify` action so a text-only cheap model never silently drops an attached screenshot).
|
|
56
56
|
- **`jeo routine init`** — generates a GitHub Actions workflow that runs jeo headlessly (`jeo "<prompt>" -p`) on a schedule/issue/PR trigger, on GitHub's own runners — no laptop required, and zero new attack surface inside jeo itself (no in-process scheduler or webhook listener). `--dry-run` to preview, `--no-pr` for a direct commit instead of the default PR-per-run.
|
|
57
|
-
- **Remote
|
|
57
|
+
- **Remote monitoring & control (Telegram, Discord, Slack)** — pair a bot once (`jeo notify setup --provider telegram|discord|slack`), then one shared `jeo daemon start` pushes a message on every subagent state edge (started → done/failed/cancelled) across every live session and accepts `/subagents`, `/steer`, `/cancel` (plus `/sessions` and `/send` on Discord/Slack) back from a human allowlist. Telegram adds forum topics, inline keyboards, and image attachments; Discord uses REST + Gateway WebSocket; Slack uses Web API + Socket Mode. Every remote control is acknowledged by the local session, never assumed executed.
|
|
58
|
+
- **Gates that cannot be talked around** — `approve` re-validates the exact plan digest it is approving and `team` refuses a plan whose reviewed digest changed; a `done` call is re-checked against the latest verification evidence (a later failed check invalidates an earlier pass, three correction bounces then a hard rejection); the `autopilot` ratchet records a failed rollback as `rollback_failed` and halts non-zero instead of claiming a reverted step.
|
|
58
59
|
- **Session-scoped async execution** — fan out independent work through the `task` tool's real `tasks` array without blocking the parent turn; detached subagents, background jobs, and line monitors remain controllable from later turns with `subagent`/`job`/`monitor` actions (`list`, `inspect`, `await`, `cancel`, `tail`). The inline TUI keeps each worker's live activity in its own slot and tears down every registry on session exit or Ctrl-C.
|
|
59
60
|
- **Independent verifier, actually enforced** — a plan can no longer skip its architect/critic step: `PlanSchema` rejects any plan that ends with an unverified mutation (a verifier placed BEFORE the mutation it should check doesn't count either), at both `ralplan` draft time and `team`/`approve` execution time. Every architect/critic verdict must also show real evidence — zero observed `read`/`search`/`find`/`ast_grep`/`lsp` calls blocks the verdict regardless of what the text claims.
|
|
60
61
|
- **Safety-boundary automatic model fallback** — an uncategorized safety refusal (a possible classifier false positive, not a genuine content-policy hit) now switches to a genuinely different-provider model instead of backing off forever on the same one — mirrors the existing rate-limit fast-fallback. A `Refusal (<category>)`-shaped deterministic hit is untouched and still hard-fails with zero fallback.
|
|
@@ -100,18 +101,36 @@ Inside the `jeo` REPL (Tab autocompletes; `/` opens the palette).
|
|
|
100
101
|
| `/fast [on\|off\|status]` | Toggle fast thinking mode when the active model advertises low reasoning |
|
|
101
102
|
| `/skill` · `$<skill> [intent]` | List/run workflow skills (`$team "task"` style) |
|
|
102
103
|
| `/view` · `/diff` · `/find` · `/search` | Code view, git diff, file/pattern search |
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
| `/computer [status\|on\|off]` | Toggle the fail-closed desktop-automation tool for this session |
|
|
105
|
+
| `/new` · `/sessions` | Start a fresh session or list saved sessions |
|
|
106
|
+
| `/resume [id\|gajae:<session-id>[#<leaf>]] [--any-cwd]` | Resume a Jeo session or import a read-only exact-version GJC v5 branch into a fresh Jeo session |
|
|
107
|
+
| `/changelog [--full]` · `/jobs [list\|tail\|await\|cancel]` | Show release notes · inspect, await, or cancel this session's background jobs |
|
|
106
108
|
| `/history [n\|all]` · `/export` | Reprint readable worked activity history into scrollback · transcript export |
|
|
107
109
|
| `/retry` · `/btw <q>` | Retry last request · side question without touching history |
|
|
108
110
|
| `/usage` · `/context` · `/compact` | Token usage, context breakdown, manual compaction |
|
|
109
111
|
| `/theme` · `/config` · `/help` | Theme, runtime config, help |
|
|
110
|
-
| `jeo autopilot status` | Ratchet status field with score direction, keep/revert counts, and next action |
|
|
111
112
|
|
|
112
113
|
> [!CAUTION]
|
|
113
114
|
> **`/model <name>` locks routing for the rest of the session.** Prompt routing (`/route`) only re-evaluates per turn while no model is manually pinned. Picking a specific model via `/model <name>` freezes that choice — routing will *not* switch away from it again until you run `/model auto` (which clears the pin), or `/route on` (which *outranks* an active pin without clearing it — the pin reasserts itself the moment you run `/route off`). Missing a `roles.*` entry only guarantees a `defaultModel` fallback on the `standard` tier; the `high`/`complex` tiers otherwise scan for the strongest live-credentialed model, so they can still land on a different model each turn even when unconfigured. **Exception:** an Antigravity- or Gemini-OAuth-credentialed session re-exports Anthropic/Google/OpenAI models under one credential — `high`/`complex` there instead session-stably spread across one model per company (not necessarily the strongest), so the pick stays fixed for that session rather than varying turn to turn.
|
|
114
115
|
|
|
116
|
+
## CLI commands
|
|
117
|
+
|
|
118
|
+
`jeo --help` is the authoritative list; the ones you will reach for:
|
|
119
|
+
|
|
120
|
+
| Command | Purpose |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `jeo [prompt] [--resume [id]] [--tmux] [--worktree <path>] [-p] [-q]` | Interactive agent (default); `-p`/`--print` for headless one-shot output, `--worktree` for an isolated sibling checkout |
|
|
123
|
+
| `jeo setup` · `jeo auth login\|logout\|refresh\|status [provider]` · `jeo doctor` | Providers, OAuth (PKCE) tokens with auto-refresh, live connectivity + stale-pin check |
|
|
124
|
+
| `jeo provider list\|add\|remove\|presets\|test` | Named custom OpenAI/Anthropic-compatible providers (same planners as `/provider`) |
|
|
125
|
+
| `jeo deep-interview` → `ralplan` → `approve` → `team` → `ultragoal` | Spec-first workflow (see below); `jeo state <skill> read\|write\|clear\|handoff` inspects the receipts |
|
|
126
|
+
| `jeo notify setup\|status\|health\|test` · `jeo daemon start\|stop\|status\|reload` | Telegram / Discord / Slack notifications and the shared control daemon |
|
|
127
|
+
| `jeo autopilot <subcommand>` · `jeo ledger <subcommand>` | Autonomous build loop with a score ratchet (`status` shows direction, keep/revert counts, next action) · cross-plan append-only ledger |
|
|
128
|
+
| `jeo routine init …` | Generate a GitHub Actions workflow that runs jeo headlessly on a schedule or repo event |
|
|
129
|
+
| `jeo skills list\|read\|sync\|lesson\|eval` | Bundled, user, and project skills; drift check; recorded lessons |
|
|
130
|
+
| `jeo mcp serve\|tools` · `jeo computer <action>` | MCP stdio server for external controllers · desktop automation actions |
|
|
131
|
+
| `jeo session list\|attach\|rm` · `jeo export [id]` · `jeo chat "<msg>"` | tmux session management · transcript export · tool-less streaming chat |
|
|
132
|
+
| `jeo update [--check]` · `jeo whats-new` · `jeo memory-migrate` | Self-update from npm · bundled release notes · legacy `MEMORY.md` → OKF bundle |
|
|
133
|
+
|
|
115
134
|
## Spec-first workflow
|
|
116
135
|
|
|
117
136
|
Requirements → plan → approval → execution → verification, carried through `.jeo/state/` with **real, blocking gates** at every handoff:
|
|
@@ -189,32 +208,45 @@ Non-zero hook output is appended to the tool result the model reads (deduped per
|
|
|
189
208
|
|
|
190
209
|
`--worktree <name>` runs jeo in an isolated sibling git worktree (reused if the path exists, else created on a branch named after the basename) so risky or reviewable work never touches your main checkout. `jeo mcp serve` exposes jeo's tools to any MCP-capable controller over stdio (`jeo mcp tools` lists them). Add `-q`/`--quiet` (or `JEO_QUIET=1`) to suppress startup banners, the welcome animation, release notes, and resume hints so jeo runs cleanly beside another agent or is driven by a bot — `-p`/`--print` implies quiet.
|
|
191
210
|
|
|
192
|
-
## Remote monitoring & control (Telegram)
|
|
211
|
+
## Remote monitoring & control (Telegram, Discord & Slack)
|
|
212
|
+
|
|
213
|
+
Opt-in: one shared daemon watches every live `jeo` session on the machine and pushes a message on each **subagent state edge** (started → completed/failed/cancelled), plus the session identity header, turn start/finish summaries, and the finalized turn text — never repeated "still running" pings. Allowlisted humans can list, send, steer, and cancel from the chat; every control is relayed to the local session and reported as **acknowledged (accepted, not completed)** or **not acknowledged** — the daemon never claims a command ran.
|
|
193
214
|
|
|
194
215
|
```bash
|
|
195
|
-
jeo notify setup
|
|
196
|
-
jeo notify status
|
|
197
|
-
jeo
|
|
198
|
-
jeo
|
|
199
|
-
jeo daemon stop
|
|
216
|
+
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,...]
|
|
217
|
+
jeo notify status [--provider …] # masked token, destination, allowlist, daemon state (stopped / stale / initializing / initialized / pairing)
|
|
218
|
+
jeo notify health [--provider …] # read-only: bot identity + destination access, nothing sent
|
|
219
|
+
jeo notify test [--provider …] # sends one real test message
|
|
220
|
+
jeo daemon start|stop|status|reload # the shared daemon has no provider selector
|
|
200
221
|
```
|
|
201
222
|
|
|
223
|
+
| | Telegram (default) | Discord | Slack |
|
|
224
|
+
| --- | --- | --- | --- |
|
|
225
|
+
| Credentials | bot token (`JEO_TELEGRAM_BOT_TOKEN` or `--token-env`) | bot token (`JEO_DISCORD_BOT_TOKEN` or `--token-env`) + **Message Content** intent | bot `xoxb` token (`JEO_SLACK_BOT_TOKEN` or `--token-env`) + app `xapp` token (`SLACK_APP_TOKEN` or `--app-token-env`), Socket Mode enabled |
|
|
226
|
+
| Destination | private chat via challenge pairing (send the displayed `/start jeo_<code>` to the bot within 120 s) or explicit `--chat-id`; groups need `--allowed-user-ids` | explicit `--channel-id` (text channel, DM, announcement, or an existing thread) | explicit `--channel-id` (`C…`) the bot has joined; workspace is pinned to the token's team |
|
|
227
|
+
| Allowlist | optional for a private chat, required for groups | required (human user IDs; bots are ignored) | required (human user IDs) |
|
|
228
|
+
| Transport | direct Bot API `getUpdates` long-poll, one poll owner per bot token | REST for sends, Gateway WebSocket for inbound | Web API for sends, Socket Mode WebSocket for inbound |
|
|
229
|
+
| Extras | forum topics, inline cancel buttons, image attachments, optional per-session topics (`notifications.telegram.perSessionTopics`, private chat with Threaded Mode only) | reply to a session notification to send that session text (24 h) | reply in a session notification's thread to send that session text (24 h); registered Slack slash commands or `@bot /command` both work |
|
|
230
|
+
| Commands | `/subagents` `/steer <session> <subagent> <msg>` `/cancel <session> <subagent>` `/help` | `/sessions` `/subagents` `/send <session> <text>` `/steer …` `/cancel …` `/help` | same as Discord |
|
|
231
|
+
|
|
232
|
+
What the words mean, because they are not interchangeable:
|
|
233
|
+
|
|
234
|
+
- **Setup** validates bot identity and destination access with the official API (Telegram `getMe`, Discord bot + channel lookup, Slack `auth.test`/`bots.info`/`conversations.info` and a Socket Mode URL) and stores the result in `~/.jeo/config.json` under `notifications.<provider>` — **plaintext**; keep that file private. It does not send a message and does not open a socket.
|
|
235
|
+
- **`initialized`** (`jeo daemon status`) means the daemon process owns its lock and has started its transports; it is not proof of a successful Telegram poll, Discord Gateway handshake, or Slack Socket Mode connection. Use `notify health` / `notify test` for that.
|
|
236
|
+
- **Changing a token or destination** resets the allowlist for Discord and Slack (you must pass `--allowed-user-ids` again) and the per-session topic map for Telegram; re-running setup with the exact same credentials and destination keeps them.
|
|
237
|
+
- **Remote commands are literal text** (`/subagents`, …) authorized to the configured chat/channel and allowlist; Slack additionally accepts the same names as registered slash commands. Allowlisted operators effectively hold the local agent's capabilities — treat the list as a trust boundary.
|
|
238
|
+
- **Retries are bounded per provider**: Telegram honours a 429 `retry_after` cooldown (clamped 1 s–1 h) with separate poll backoff; Discord and Slack retry at most 3 times on 429 using the server's `Retry-After`, and never re-send a POST whose outcome is unknown (the platform may already have it).
|
|
239
|
+
- **Orphan recovery**: if a daemon crashes, its lock becomes `stale`; the next `jeo daemon start` reclaims it. `jeo daemon stop` refuses to signal a recycled PID when the host can report process start times, and says so when it cannot.
|
|
240
|
+
- **Verified so far**: Telegram end to end (real send, polling, clean shutdown). Discord and Slack are implemented behind the same daemon with offline transport/wiring tests and code, security, and type reviews; live connectivity has not been exercised against real bot/app credentials.
|
|
241
|
+
|
|
202
242
|
```
|
|
203
|
-
┌─────────────────────┐
|
|
204
|
-
│ interactive turn │◄──ws──►│
|
|
205
|
-
│
|
|
206
|
-
|
|
243
|
+
┌─────────────────────┐ ┌──────────────────┐ ┌───────────────────────────┐
|
|
244
|
+
│ interactive turn │◄──ws──►│ notify daemon │◄──────►│ Telegram Bot API (poll) │
|
|
245
|
+
│ SubagentRegistry │ (one │ (singleton) │ │ Discord REST + Gateway WS │
|
|
246
|
+
│ session endpoint │ per │ │ │ Slack Web API + Socket WS │
|
|
247
|
+
└─────────────────────┘ session└──────────────────┘ └───────────────────────────┘
|
|
207
248
|
```
|
|
208
249
|
|
|
209
|
-
Opt-in and lazy: nothing binds until `notifications.enabled` is set AND a detached subagent (`task {detached:true}`) actually runs. The daemon scans live session discovery files, connects a loopback WebSocket per session, and pushes a message only on a subagent state *edge* (started → completed/failed/cancelled) — never a repeated "still running" ping. Telegram Daemon now supports full `gjc` parity, including forum topics, inline keyboards, and image attachments. Inbound Telegram commands are authorized to the paired chat only; anything else is dropped silently.
|
|
210
|
-
|
|
211
|
-
| Command | Effect |
|
|
212
|
-
| --- | --- |
|
|
213
|
-
| `/subagents` | List running/recent subagents across every connected session |
|
|
214
|
-
| `/steer <sessionId> <subagentId> <message>` | Send a live message into a running subagent |
|
|
215
|
-
| `/cancel <sessionId> <subagentId>` | Cancel a running subagent |
|
|
216
|
-
| `/help` | Show the command reference |
|
|
217
|
-
|
|
218
250
|
## Routines (GitHub Actions)
|
|
219
251
|
|
|
220
252
|
```bash
|
|
@@ -327,10 +359,10 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
|
|
|
327
359
|
|
|
328
360
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
329
361
|
- **[Unreleased]**
|
|
362
|
+
- **[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.
|
|
330
363
|
- **[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.
|
|
331
364
|
- **[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.
|
|
332
365
|
- **[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.
|
|
333
|
-
- **[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.
|
|
334
366
|
|
|
335
367
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
336
368
|
<!-- CHANGELOG:END -->
|