jeo-code 0.11.2 → 0.11.4
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 +51 -1
- package/README.ja.md +62 -33
- package/README.ko.md +65 -35
- package/README.md +61 -32
- package/README.zh.md +64 -35
- 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/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
|
|
@@ -312,12 +344,9 @@ bun run dev:doctor # report whether global `jeo` runs this source (link
|
|
|
312
344
|
## Publishing
|
|
313
345
|
|
|
314
346
|
|
|
315
|
-
CI publishes via `.github/workflows/npm-publish.yml` — triggered by a published GitHub release, or manually with `workflow_dispatch` (optional dry-run). The workflow
|
|
316
|
-
|
|
317
|
-
Required npm token permissions (repository secret `NPM_TOKEN`):
|
|
347
|
+
CI publishes via `.github/workflows/npm-publish.yml` — triggered by a published GitHub release, or manually with `workflow_dispatch` (optional dry-run). The workflow runs the browser hard gate, typecheck, the test suite (browser tool tests in their own process), `pack:check`, a packed-tarball boot smoke, then `npm publish --provenance`.
|
|
318
348
|
|
|
319
|
-
|
|
320
|
-
- "**bypass 2FA** for publishing" must be allowed — Automation tokens always bypass; granular tokens need the option enabled
|
|
349
|
+
Publishing uses **npm trusted publishing (OIDC)** — there is no `NPM_TOKEN` secret to rotate, leak, or exempt from 2FA (npm restricts bypass-2FA tokens from Aug 2026 and ends direct publishing with them in Jan 2027). One-time setup on npmjs.com: package `jeo-code` → Settings → Trusted Publisher → GitHub Actions, owner `akillness`, repository `jeo-code`, workflow `npm-publish.yml`, environment blank. The job needs `permissions.id-token: write` and npm ≥ 11.5.1 (the workflow upgrades npm itself).
|
|
321
350
|
|
|
322
351
|
## Acknowledgements
|
|
323
352
|
|
|
@@ -327,10 +356,10 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
|
|
|
327
356
|
|
|
328
357
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
329
358
|
- **[Unreleased]**
|
|
359
|
+
- **[0.11.4]** (2026-10-01) — The release pipeline could not actually release: 13 tests failed on Linux CI only, and the stored npm token no longer authenticated. Both are fixed at the root — the suite is order-independent, and publishing no longer depends on a secret that can expire.
|
|
360
|
+
- **[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
361
|
- **[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
362
|
- **[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
|
-
- **[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
363
|
|
|
335
364
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
336
365
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
## 亮点
|
|
40
40
|
|
|
41
|
-
- **多提供商、单一循环** — Anthropic / OpenAI(+Codex) / Gemini / Antigravity / Ollama / LM Studio,以及 20+ 个 OpenAI、Anthropic 兼容云服务(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,以及 20+ 个 OpenAI、Anthropic 兼容云服务(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`),每次模型选择都会持久化为新的默认值,而 prompt routing 只会自动选择真实可用的凭据路径: 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` 诚实报告(套件运行只是全局信号,绝不伪造逐条通过)。
|
|
@@ -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`** — 生成一个 GitHub Actions 工作流,在 schedule/issue/PR 触发器上以无头模式运行 jeo(`jeo "<prompt>" -p`),运行在 GitHub 自己的 runner 上 — 不需要笔记本电脑,并且不会给 jeo 自身增加任何新的攻击面(没有进程内调度器或 webhook 监听器)。`--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。每一条远程控制都由本地会话确认收到,绝不假定已执行。
|
|
53
|
+
- **无法绕过的门禁** — `approve` 会重新校验它正在批准的那份计划的精确摘要,`team` 会拒绝已审阅摘要发生变化的计划;`done` 调用会对照最新的验证证据重新检查(后来的失败检查会使先前的通过失效,三次纠正往返后硬性拒绝);`autopilot` ratchet 会把失败的回滚记录为 `rollback_failed` 并以非零状态停止,而不是声称该步骤已回退。
|
|
53
54
|
- **不阻塞回合的会话级异步执行** — 通过 `task` 工具真实的 `tasks` 数组扇出独立工作,不阻塞父回合。detached 子代理、后台 job 和逐行 monitor 可在后续回合中继续使用 `subagent`/`job`/`monitor` 的 `list`、`inspect`、`await`、`cancel`、`tail` 控制。内联 TUI 为每个 worker 保留独立的实时 activity 槽位,并在会话退出或 Ctrl-C 时清理全部 registry。
|
|
54
55
|
- **独立验证器,真正强制执行** — 计划再也无法跳过 architect/critic 步骤: `PlanSchema` 会拒绝任何以未验证变更结尾的计划(把验证器放在它该检查的变更之前也不算数),在 `ralplan` 起草阶段和 `team`/`approve` 执行阶段都强制生效。每一次 architect/critic 裁决也都必须展示真实证据 — 观测到的 `read`/`search`/`find`/`ast_grep`/`lsp` 调用为零,无论文本怎么声称,裁决都会被拦下。
|
|
55
56
|
- **安全边界自动模型回退** — 未分类的安全拒绝(可能是分类器误报,而非真实的内容策略命中)现在会切换到真正不同提供商的模型,而不是在同一个模型上无限退让 — 与现有的 rate-limit 快速回退是同一套模式。`Refusal (<category>)` 这种形态的确定性拒绝不受影响,仍然零回退硬失败。
|
|
@@ -85,27 +86,45 @@ jeo --tmux # 在独立 tmux 会话中运行
|
|
|
85
86
|
|
|
86
87
|
| 命令 | 说明 |
|
|
87
88
|
| --- | --- |
|
|
88
|
-
| `/model` · `/provider` | 选择模型/提供商;`/model` 在一个流程内显示默认/角色徽章、Ralph
|
|
89
|
+
| `/model` · `/provider` | 选择模型/提供商;`/model` 在一个流程内显示默认/角色徽章、Ralph 风格嵌套的"设为角色"thinking 选择与 OpenAI Codex 角色预设 |
|
|
89
90
|
| `/provider login <name>` · `/logout` | 在输入框内 OAuth 登录/登出 |
|
|
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]]` |
|
|
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 "任务"` 风格) |
|
|
96
97
|
| `/view` · `/diff` · `/find` · `/search` | 代码查看、git diff、文件/模式搜索 |
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
| `/computer [status\|on\|off]` | 切换本会话的 fail-closed 桌面自动化工具 |
|
|
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
|
-
| `/retry` · `/btw
|
|
103
|
+
| `/retry` · `/btw <q>` | 重试上次请求 · 不写入历史的旁路提问 |
|
|
102
104
|
| `/usage` · `/context` · `/compact` | Token 用量、上下文明细、手动压缩 |
|
|
103
105
|
| `/theme` · `/config` · `/help` | 主题、运行时配置、帮助 |
|
|
104
|
-
| `jeo autopilot status` | 显示分数方向、keep/revert 次数和下一步动作的 ratchet 状态字段 |
|
|
105
106
|
|
|
106
107
|
> [!CAUTION]
|
|
107
108
|
> **通过 `/model <name>` 手动指定模型后,路由会在本会话内保持锁定。** Prompt routing(`/route`)只在没有手动锁定模型时才会逐轮重新评估。用 `/model <name>` 选定具体模型后,该选择会被锁定 — 直到你运行 `/model auto`(彻底解除锁定),或运行 `/route on`(不会清除锁定,只是优先级更高 — 一旦运行 `/route off`,锁定会立刻恢复)之前,路由都不会再切换。未配置 `roles.*` 条目时,只有 `standard` 层级会确定性地退回到 `defaultModel`;`high`/`complex` 层级通常会先实时扫描已配置凭证中最强的可用模型再决定是否回退,所以即使未配置,也可能每轮落到不同的模型上。**例外:** 通过 Antigravity 或 Gemini OAuth 授权的会话会用同一个凭证重新导出 Anthropic/Google/OpenAI 的模型 —此时 `high`/`complex` 会按公司各选一个模型、以会话为单位稳定分布(不一定是最强的那个),因此在同一会话内保持固定,而不是逐轮变化。
|
|
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` 共用同一套 planner) |
|
|
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>` | 带分数 ratchet 的自主构建循环(`status` 显示方向、keep/revert 次数和下一步动作) · 跨计划的只追加账本 |
|
|
122
|
+
| `jeo routine init …` | 生成一个 GitHub Actions 工作流,按计划或仓库事件以无头模式运行 jeo |
|
|
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
|
+
可选功能: 一个共享守护进程监视本机上所有活跃的 `jeo` 会话,并在每次**子代理状态变化**(启动 → 完成/失败/取消)时推送消息,另附会话身份头、回合开始/结束摘要以及最终的回合文本 — 绝不会重复"仍在运行"之类的通知。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 …] # masked token, destination, allowlist, daemon state (stopped / stale / initializing / initialized / pairing)
|
|
211
|
+
jeo notify health [--provider …] # read-only: bot identity + destination access, nothing sent
|
|
212
|
+
jeo notify test [--provider …] # sends one real test message
|
|
213
|
+
jeo daemon start|stop|status|reload # the shared daemon has no provider selector
|
|
193
214
|
```
|
|
194
215
|
|
|
216
|
+
| | Telegram(默认) | Discord | Slack |
|
|
217
|
+
| --- | --- | --- | --- |
|
|
218
|
+
| 凭证 | 机器人 token(`JEO_TELEGRAM_BOT_TOKEN` 或 `--token-env`) | 机器人 token(`JEO_DISCORD_BOT_TOKEN` 或 `--token-env`)+ **Message Content** intent | 机器人 `xoxb` token(`JEO_SLACK_BOT_TOKEN` 或 `--token-env`)+ 应用 `xapp` token(`SLACK_APP_TOKEN` 或 `--app-token-env`),并启用 Socket Mode |
|
|
219
|
+
| 目标 | 通过挑战配对的私聊(在 120 秒内把显示的 `/start jeo_<code>` 发给机器人)或显式 `--chat-id`;群组需要 `--allowed-user-ids` | 显式 `--channel-id`(文字频道、DM、公告频道或已有线程) | 机器人已加入的显式 `--channel-id`(`C…`);工作区固定为 token 所属的团队 |
|
|
220
|
+
| Allowlist | 私聊可选,群组必需 | 必需(人类用户 ID;机器人会被忽略) | 必需(人类用户 ID) |
|
|
221
|
+
| 传输 | 直接调用 Bot API `getUpdates` 长轮询,每个机器人 token 只有一个轮询所有者 | 发送走 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
|
+
- **Setup** 会通过官方 API 验证机器人身份和目标访问权限(Telegram `getMe`,Discord 机器人 + 频道查询,Slack `auth.test`/`bots.info`/`conversations.info` 以及一个 Socket Mode URL),并把结果存入 `~/.jeo/config.json` 的 `notifications.<provider>` 下 — **明文**;请保持该文件私有。它不会发送消息,也不会打开 socket。
|
|
228
|
+
- **`initialized`**(`jeo daemon status`)表示守护进程已持有自己的锁并启动了各传输层;它并不证明 Telegram 轮询、Discord Gateway 握手或 Slack Socket Mode 连接已经成功。要验证这些请使用 `notify health` / `notify test`。
|
|
229
|
+
- **更改 token 或目标**会重置 Discord 和 Slack 的 allowlist(你必须再次传入 `--allowed-user-ids`)以及 Telegram 的每会话主题映射;用完全相同的凭证和目标重新运行 setup 则会保留它们。
|
|
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
|
|
@@ -302,12 +334,9 @@ bun run dev:doctor # 报告全局 `jeo` 是否运行的是本源码(lin
|
|
|
302
334
|
|
|
303
335
|
## 发布 (Publishing)
|
|
304
336
|
|
|
305
|
-
CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release 时自动触发,或手动 `workflow_dispatch`(可选 dry-run)
|
|
306
|
-
|
|
307
|
-
所需 npm 令牌权限(仓库 secret `NPM_TOKEN`):
|
|
337
|
+
CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release 时自动触发,或手动 `workflow_dispatch`(可选 dry-run)。工作流依次执行浏览器硬门禁、类型检查、测试(浏览器工具测试在独立进程中)、`pack:check`、打包 tarball 的启动冒烟,然后运行 `npm publish --provenance`。
|
|
308
338
|
|
|
309
|
-
-
|
|
310
|
-
- 必须允许"发布时 **bypass 2FA**" — Automation 令牌始终绕过,granular 令牌需启用该选项
|
|
339
|
+
发布使用 **npm trusted publishing(OIDC)** — 不存在需要轮换、可能泄露或需豁免 2FA 的 `NPM_TOKEN` secret(npm 自 2026 年 8 月起限制绕过 2FA 的令牌,并于 2027 年 1 月终止用其直接发布)。在 npmjs.com 上一次性设置: 包 `jeo-code` → Settings → Trusted Publisher → GitHub Actions,owner `akillness`,repository `jeo-code`,workflow `npm-publish.yml`,environment 留空。该 job 需要 `permissions.id-token: write` 和 npm ≥ 11.5.1(工作流会自行升级 npm)。
|
|
311
340
|
|
|
312
341
|
## 致谢 (Acknowledgements)
|
|
313
342
|
|
|
@@ -317,10 +346,10 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
317
346
|
|
|
318
347
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
319
348
|
- **[Unreleased]**
|
|
349
|
+
- **[0.11.4]** (2026-10-01) — The release pipeline could not actually release: 13 tests failed on Linux CI only, and the stored npm token no longer authenticated. Both are fixed at the root — the suite is order-independent, and publishing no longer depends on a secret that can expire.
|
|
350
|
+
- **[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.
|
|
320
351
|
- **[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.
|
|
321
352
|
- **[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.
|
|
322
|
-
- **[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.
|
|
323
|
-
- **[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.
|
|
324
353
|
|
|
325
354
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
326
355
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
|
@@ -104,11 +104,8 @@ export const ConfigSchema = z
|
|
|
104
104
|
onAsk: z.boolean().optional(),
|
|
105
105
|
})
|
|
106
106
|
.optional(),
|
|
107
|
-
/** Remote
|
|
108
|
-
*
|
|
109
|
-
* master toggle; a session only starts publishing its loopback endpoint
|
|
110
|
-
* when this is true, and `jeo notify-daemon-run` refuses to poll Telegram
|
|
111
|
-
* without a stored botToken + chatId. */
|
|
107
|
+
/** Remote session visibility/control. `enabled` is the master toggle;
|
|
108
|
+
* each configured transport is hosted by the single notification daemon. */
|
|
112
109
|
notifications: z
|
|
113
110
|
.object({
|
|
114
111
|
enabled: z.boolean().optional(),
|
|
@@ -123,18 +120,34 @@ export const ConfigSchema = z
|
|
|
123
120
|
.object({
|
|
124
121
|
botToken: z.string().optional(),
|
|
125
122
|
chatId: z.string().optional(),
|
|
123
|
+
allowedUserIds: z.array(z.string().regex(/^[1-9]\d*$/)).optional(),
|
|
126
124
|
/** Forum-topic thread id (message_thread_id) for supergroups with
|
|
127
125
|
* topics enabled — daemon pushes go into this topic. Ignored for a
|
|
128
126
|
* session-owned topic when `perSessionTopics` is true. */
|
|
129
127
|
topicId: z.number().optional(),
|
|
130
128
|
/** Auto-create and manage ONE forum topic per interactive session
|
|
131
129
|
* (gjc per-session-thread parity), instead of the single flat/global
|
|
132
|
-
* `topicId` above. Requires
|
|
133
|
-
*
|
|
130
|
+
* `topicId` above. Requires a private chat with Threaded Mode
|
|
131
|
+
* enabled (fail-closed — see `TopicRegistry`).
|
|
134
132
|
* Off by default; existing flat-topic/no-topic setups are unaffected. */
|
|
135
133
|
perSessionTopics: z.boolean().optional(),
|
|
136
134
|
})
|
|
137
135
|
.optional(),
|
|
136
|
+
discord: z
|
|
137
|
+
.object({
|
|
138
|
+
botToken: z.string().optional(),
|
|
139
|
+
channelId: z.string().regex(/^[1-9]\d{0,19}$/).optional(),
|
|
140
|
+
allowedUserIds: z.array(z.string().regex(/^[1-9]\d{0,19}$/)).optional(),
|
|
141
|
+
})
|
|
142
|
+
.optional(),
|
|
143
|
+
slack: z
|
|
144
|
+
.object({
|
|
145
|
+
botToken: z.string().min(1),
|
|
146
|
+
appToken: z.string().min(1),
|
|
147
|
+
channelId: z.string().regex(/^[CGD][A-Z0-9]+$/),
|
|
148
|
+
allowedUserIds: z.array(z.string().regex(/^[UW][A-Z0-9]+$/)).min(1),
|
|
149
|
+
})
|
|
150
|
+
.optional(),
|
|
138
151
|
})
|
|
139
152
|
.optional(),
|
|
140
153
|
|
|
@@ -236,12 +236,16 @@ export class DebugSession {
|
|
|
236
236
|
const decoder = new TextDecoder();
|
|
237
237
|
let buf = "";
|
|
238
238
|
const deadline = Date.now() + CONNECT_TIMEOUT_MS;
|
|
239
|
+
let pendingRead: ReturnType<typeof reader.read> | undefined;
|
|
239
240
|
try {
|
|
240
241
|
while (Date.now() < deadline) {
|
|
241
|
-
const
|
|
242
|
-
reader.read(),
|
|
243
|
-
new Promise<
|
|
242
|
+
const result = await Promise.race([
|
|
243
|
+
pendingRead ??= reader.read(),
|
|
244
|
+
new Promise<undefined>((r) => setTimeout(() => r(undefined), 200)),
|
|
244
245
|
]);
|
|
246
|
+
if (!result) continue;
|
|
247
|
+
pendingRead = undefined;
|
|
248
|
+
const { value, done } = result;
|
|
245
249
|
if (done) break;
|
|
246
250
|
if (value) {
|
|
247
251
|
const text = decoder.decode(value, { stream: true });
|