omnilane 0.15.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "omnilane",
3
+ "owner": {
4
+ "name": "Vincent Wang",
5
+ "url": "https://github.com/Seraphim0916/omnilane"
6
+ },
7
+ "metadata": {
8
+ "description": "One routing table, cross-vendor dispatch, foreman completion inbox.",
9
+ "version": "0.21.0"
10
+ },
11
+ "plugins": [
12
+ {
13
+ "name": "omnilane",
14
+ "source": "./",
15
+ "description": "Route each subtask to the model that fits, dispatch it to any vendor CLI, and have finished dispatches report back into the foreman's next prompt.",
16
+ "version": "0.21.0",
17
+ "category": "development",
18
+ "keywords": [
19
+ "routing",
20
+ "dispatch",
21
+ "multi-model",
22
+ "codex",
23
+ "orchestration"
24
+ ]
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "omnilane",
3
+ "version": "0.21.0",
4
+ "description": "One routing table, every harness: classify subtasks into lanes and auto-dispatch each lane to the best vendor CLI (Codex, Claude Code, Grok Build, Antigravity) with background jobs, depth guard, and serialized codex dispatch.",
5
+ "author": {
6
+ "name": "Seraphim0916"
7
+ },
8
+ "skills": "./skills/"
9
+ }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,59 @@ semantic version tags.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.21.0] - 2026-08-30
10
+
11
+ ### Added
12
+
13
+ - Explicit session-mode selection for background dispatches: `--live` requires a resident session, while `--single-shot` forces a one-shot job. `--live` fails fast for a vendor without live support and names the live-capable vendors; the chosen `session_mode` is recorded in `meta.json`.
14
+ - Gemini joins Claude in the live mailbox through the `agy` stream protocol, with the shared protocol implementation in `scripts/lib/live-protocol.sh`.
15
+ - `--idle-timeout N` automatically closes an untended live session, records `closed by idle cap after Ns` as its close reason, and writes `idle_timeout` to `meta.json`.
16
+ - `tests/test_live_mailbox.sh` is wired into `tests/run.sh`, expanding the suite to 94 checks.
17
+
18
+ ## [0.20.0] - 2026-08-30
19
+
20
+ ### Added
21
+
22
+ - `docs/roadmap.md` plans threaded dispatch (continue a job instead of
23
+ restarting it) and a live mailbox (message a job while it runs), with a
24
+ per-vendor session capability matrix. `docs/experiments/live-inbox-probe.sh`
25
+ is the probe backing the mailbox claim.
26
+
27
+ - Finished dispatches now report themselves to the foreman. Each completed job
28
+ writes a private record under `$OMNILANE_HOME/inbox/`, and the bundled Claude
29
+ Code `UserPromptSubmit` hook delivers matching records into the foreman's next
30
+ prompt, so a background dispatch no longer has to be polled. Records are
31
+ scoped by the dispatch `workdir` and, when available, its foreman session,
32
+ claimed at most once, and moved to `inbox/consumed/`. Worker-output tails are
33
+ framed as indented data and stripped of control characters and Unicode
34
+ `U+2028`/`U+2029` separators, so they cannot forge a header or issue
35
+ instructions. Set `OMNILANE_INBOX=0` to disable record creation.
36
+ - `.claude-plugin/marketplace.json` makes the repository installable as a Claude
37
+ Code plugin, which is the install path the completion notice needs:
38
+ `claude plugin marketplace add <repo>` then `claude plugin install
39
+ omnilane@omnilane`.
40
+ - The published package now ships `hooks/`, `skills/` and `.claude-plugin/`, so
41
+ an npm install carries the hook script instead of only the CLI.
42
+ - `install.sh` reports whether the completion notice is active and, when it is
43
+ not, prints the exact commands that enable it; the skill symlink alone does
44
+ not deliver notices.
45
+ - `doctor` answers whether the completion notice is live, reading the
46
+ `hooks/hooks.json` of the current checkout rather than the state recorded at
47
+ install time — checking out a branch without the hook silently disables the
48
+ notice while the plugin still reports itself as enabled.
49
+ - Claude background dispatches can now stay resident as a live mailbox. A live
50
+ job accepts `jobs.sh send`, `watch`, `tail`, `close`, and `retry`, and records
51
+ lifecycle events in `events.jsonl`. Other vendors explicitly fall back to
52
+ single-shot mode with a `stderr` and `mode-notice.txt` notice; `jobs.sh wait`
53
+ now prints `done exit=N` when the job finishes.
54
+ - Foreman completion records are now bound to the Claude session that launched
55
+ the dispatch. The `SessionStart` hook records the `session_id` against the
56
+ parent PID and its start time, making PID reuse safe; dispatch finds that
57
+ identity by walking its ancestors and stamps `foreman_session` into
58
+ `meta.json` and completion records. The inbox consumes a session match first
59
+ and falls back to `workdir` for legacy records, preventing two foremen in one
60
+ repository from taking each other's notices.
61
+
9
62
  ## [0.15.0] - 2026-08-28
10
63
 
11
64
  ### Added
@@ -616,7 +669,11 @@ work to the wrong model, and records the evidence behind the shipped defaults.
616
669
  - Initial shared routing table, cross-vendor dispatcher, runners, installer,
617
670
  and baseline lint fixes.
618
671
 
619
- [Unreleased]: https://github.com/Seraphim0916/omnilane/compare/v0.15.0...HEAD
672
+ [Unreleased]: https://github.com/Seraphim0916/omnilane/compare/v0.21.0...HEAD
673
+
674
+ [0.21.0]: https://github.com/Seraphim0916/omnilane/compare/v0.20.0...v0.21.0
675
+
676
+ [0.20.0]: https://github.com/Seraphim0916/omnilane/compare/v0.15.0...v0.20.0
620
677
 
621
678
  [0.15.0]: https://github.com/Seraphim0916/omnilane/compare/v0.14.0...v0.15.0
622
679
 
package/README.ja.md CHANGED
@@ -364,6 +364,24 @@ configure.sh set|get|unset|list|diff LANE [SPEC] # routing.local.yaml を非
364
364
  記録し、`jobs.sh status` が `dead` を報告。
365
365
  - **ペイロード上限** — 巨大なタスクテキストは自動で頭尾トランケート。
366
366
 
367
+ ## 📬 ライブメールボックス
368
+
369
+ ライブメールボックスは、1 回で完結するディスパッチとは別の、Claude と Gemini が利用できる常駐バックグラウンド実行です。フォアマンが `--background` で開始し、実行中にも追加の指示を送れます。終わったらフォアマンが `jobs.sh close ID` で閉じます。放置しても常駐し続けるわけではなく、アイドル上限または設定済みのジョブ全体タイムアウト(`--job-timeout`)に達すれば終了します。
370
+
371
+ ```bash
372
+ scripts/dispatch.sh --background --vendor claude hard-judgment "タイムアウトしたテストを確認する"
373
+ # dispatch が表示したジョブ ID を $ID として保存
374
+ scripts/jobs.sh send "$ID" "リトライ経路も確認してください。"
375
+ scripts/jobs.sh watch "$ID"
376
+ scripts/jobs.sh tail "$ID" --lines 20
377
+ scripts/jobs.sh close "$ID"
378
+ scripts/jobs.sh retry "$ID" --background
379
+ ```
380
+
381
+ `watch` は `$JOB_DIR/events.jsonl` を追跡し、`tail` は公開出力の `out.txt` を読みます。ライブメールボックスは Claude と Gemini に対応し、ほかのベンダーは通常の単発ディスパッチとして実行され、stderr と `$JOB_DIR/mode-notice.txt` に通知が残ります。`--live` は常駐セッションを必須にし、解決されたベンダーが非対応なら即時失敗します。`--single-shot` は Claude や Gemini でも単発実行を強制します。`--idle-timeout SECONDS` はアイドル上限を設定し、既定値は 900 秒、`0` で無効になります。
382
+
383
+ アイドル中は API 呼び出しも料金も発生しません。既定では、新しい受信メッセージまたは結果イベントが 900 秒間なければ worker が自動で終了し、ジョブ全体タイムアウトは外側の上限として残ります。やり取りが終わったら早めに `close` できます。終了済み、またはライブでないジョブへの `jobs.sh send` は明確なエラーで失敗します。送った後に追跡しない作業、ライブ対応していないベンダー、クリーンな状態からの再実行が必要な場合には使わず、新しいディスパッチ(または完了後の `retry`)を使ってください。
384
+
367
385
  ## ❓ FAQ
368
386
 
369
387
  <details>
@@ -516,6 +534,34 @@ scripts/dispatch.sh --dry-run hardest-coding "…" # 解決済みプラン、
516
534
 
517
535
  ## 📜 リリース履歴
518
536
 
537
+ ## v0.21.0 の新機能
538
+
539
+ - **セッションモードを明示的に選択。** `dispatch --live` で常駐セッションを必須にするか、`--single-shot` で単発ジョブを強制できます。ライブセッション非対応ベンダーでは `--live` が即時に失敗し、対応ベンダーを表示します。
540
+ - **Gemini がライブメールボックスに参加。** Gemini は `agy` ストリームプロトコルを介し、Claude と並んで常駐ライブジョブを実行できます。
541
+ - **ライブジョブのアイドル上限。** `--idle-timeout N` は放置されたライブセッションを自動で閉じ、終了理由とタイムアウトを `meta.json` に記録します。
542
+
543
+ ## v0.20.0 の新機能
544
+
545
+ - **フォアマン完了インボックス。** バックグラウンド dispatch の完了時に非公開の
546
+ 完了レコードを書き込み、同梱の Claude Code プラグインが一致するレコードを
547
+ フォアマンの次のプロンプトへ届けます。出力末尾はプロンプト注入対策済みで、
548
+ 制御文字と `U+2028`/`U+2029` を除去し、ワーカー出力をインデントしたデータ
549
+ として枠付けます。
550
+ - **インストール可能な Claude Code プラグイン。**
551
+ `.claude-plugin/marketplace.json` は自己参照 source を使い、公開 npm tarball
552
+ には `hooks/`、`skills/`、`.claude-plugin/` も含まれます。
553
+ - **Claude のライブメールボックス。** 常駐バックグラウンド Claude ジョブは
554
+ 実行中にメッセージを受け取り、`events.jsonl` を記録します。操作は
555
+ [📬 ライブメールボックス](#-ライブメールボックス) を参照してください。
556
+ 他ベンダーは `stderr` と `mode-notice.txt` の通知付きで明示的に単発実行へ
557
+ フォールバックし、`jobs.sh wait` は `done exit=N` で完了します。
558
+ - **フォアマンのセッション識別。** `SessionStart` hook は Claude `session_id` を
559
+ PID と開始時刻へ結び付け、PID 再利用にも安全です。dispatch は親プロセスを
560
+ たどって `foreman_session` を `meta.json` と完了レコードへ記録します。
561
+ インボックスはセッション一致を優先し、旧レコードでは `workdir` に
562
+ フォールバックするため、同一リポジトリ内の複数フォアマンが互いの通知を
563
+ 取得しません。
564
+
519
565
  ## v0.15.0 の新機能
520
566
 
521
567
  - **Codex の進捗証跡をストリーミング保存** — `codex exec --json` が JSONL イベントを
package/README.ko.md CHANGED
@@ -351,6 +351,24 @@ CLI 를 사용할 수 없음, `5` 1라운드 성공 투표자 부족, `6` 2라
351
351
  `jobs.sh status` 가 `dead` 를 보고.
352
352
  - **페이로드 상한** — 과대한 태스크 텍스트는 머리/꼬리만 남기고 자동 절단.
353
353
 
354
+ ## 📬 라이브 메일함
355
+
356
+ 라이브 메일함은 일회성 dispatch와 다른 Claude 및 Gemini용 상주 백그라운드 실행입니다. 포어맨이 `--background`로 열고, 실행 중에도 추가 지시를 보낼 수 있으며, 끝나면 `jobs.sh close ID`로 닫을 책임이 있습니다. 방치해도 영구히 남아 있지는 않습니다. 유휴 상한 또는 설정된 전체 작업 시간 제한(`--job-timeout`)에 도달하면 종료됩니다.
357
+
358
+ ```bash
359
+ scripts/dispatch.sh --background --vendor claude hard-judgment "시간 제한 테스트 실패를 확인해 주세요"
360
+ # dispatch가 출력한 작업 ID를 $ID로 저장
361
+ scripts/jobs.sh send "$ID" "재시도 경로도 확인해 주세요."
362
+ scripts/jobs.sh watch "$ID"
363
+ scripts/jobs.sh tail "$ID" --lines 20
364
+ scripts/jobs.sh close "$ID"
365
+ scripts/jobs.sh retry "$ID" --background
366
+ ```
367
+
368
+ `watch`는 `$JOB_DIR/events.jsonl`을 따라가고, `tail`은 공개 `out.txt`를 읽습니다. 라이브 메일함은 Claude와 Gemini를 지원하며, 다른 벤더는 일반 일회성 dispatch로 실행되고 stderr와 `$JOB_DIR/mode-notice.txt`에 알림이 남습니다. `--live`는 상주 세션을 필수로 하며 결정된 벤더가 지원하지 않으면 즉시 실패합니다. `--single-shot`은 Claude나 Gemini에서도 일회성 실행을 강제합니다. `--idle-timeout SECONDS`는 유휴 상한을 설정하며 기본값은 900초이고 `0`이면 비활성화됩니다.
369
+
370
+ 유휴 상태에서는 API 호출이나 비용이 발생하지 않습니다. 기본적으로 새 수신 메시지나 결과 이벤트가 900초 동안 없으면 worker가 자동으로 종료되며, 전체 작업 시간 제한은 바깥쪽 상한으로 유지됩니다. 대화가 끝나면 더 일찍 `close`할 수 있습니다. 끝났거나 라이브가 아닌 작업에 `jobs.sh send`를 실행하면 명확한 오류와 함께 실패합니다. 보낸 뒤 추적하지 않을 작업, 라이브 지원이 없는 벤더, 깨끗한 상태에서 다시 실행해야 하는 경우에는 쓰지 말고 새 dispatch 또는 완료 뒤 `retry`를 사용하세요.
371
+
354
372
  ## ❓ FAQ
355
373
 
356
374
  <details>
@@ -499,6 +517,32 @@ scripts/dispatch.sh --dry-run hardest-coding "…" # 완전히 해석된 계
499
517
 
500
518
  ## 📜 릴리스 기록
501
519
 
520
+ ## v0.21.0 새 기능
521
+
522
+ - **세션 모드를 명시적으로 선택.** `dispatch --live`로 상주 세션을 요구하거나 `--single-shot`으로 단발 작업을 강제할 수 있습니다. 라이브 세션을 지원하지 않는 벤더에서는 `--live`가 즉시 실패하고 지원 벤더를 표시합니다.
523
+ - **Gemini가 라이브 메일함에 합류.** Gemini는 `agy` 스트림 프로토콜을 통해 Claude와 함께 상주 라이브 작업을 실행할 수 있습니다.
524
+ - **라이브 작업의 유휴 제한.** `--idle-timeout N`은 방치된 라이브 세션을 자동으로 닫고 종료 사유와 제한 시간을 `meta.json`에 기록합니다.
525
+
526
+ ## v0.20.0 새 기능
527
+
528
+ - **Foreman 완료 수신함.** 백그라운드 디스패치가 끝나면 비공개 완료 레코드를
529
+ 쓰고, 함께 제공되는 Claude Code 플러그인이 일치하는 레코드를 Foreman의 다음
530
+ 프롬프트에 전달합니다. 출력 꼬리에는 프롬프트 주입 방어가 적용되어 제어 문자와
531
+ `U+2028`/`U+2029`를 제거하고 작업자 출력을 들여쓴 데이터로 감쌉니다.
532
+ - **설치 가능한 Claude Code 플러그인.** `.claude-plugin/marketplace.json`은
533
+ 자기 참조 source를 사용하며, 배포 npm tarball에는 `hooks/`, `skills/`,
534
+ `.claude-plugin/`도 포함됩니다.
535
+ - **Claude 라이브 메일함.** 상주 백그라운드 Claude 작업은 실행 중 메시지를
536
+ 받을 수 있고 `events.jsonl`을 기록합니다. 사용법은
537
+ [📬 라이브 메일함](#-라이브-메일함)을 참조하세요. 다른 벤더는 `stderr`와
538
+ `mode-notice.txt` 알림을 남긴 채 명시적으로 단발 실행으로 폴백하며,
539
+ `jobs.sh wait`는 `done exit=N`으로 끝납니다.
540
+ - **Foreman 세션 식별.** `SessionStart` hook은 Claude `session_id`를 PID와
541
+ 시작 시각에 연결하여 PID 재사용에도 안전합니다. 디스패치는 부모 프로세스를
542
+ 따라 `foreman_session`을 `meta.json`과 완료 레코드에 기록합니다. 수신함은
543
+ 세션 일치를 우선하고 레거시 레코드에서만 `workdir`로 폴백하므로, 한 repository의
544
+ 여러 Foreman이 서로의 알림을 가져가지 않습니다.
545
+
502
546
  ## v0.15.0 새 기능
503
547
 
504
548
  - **Codex 진행 증거의 스트리밍 보존** — `codex exec --json`이 JSONL 이벤트를
package/README.md CHANGED
@@ -207,7 +207,7 @@ Core routing does not need Python; only this UI requires Python 3.9 or newer.
207
207
 
208
208
  ### Foreman completion inbox
209
209
 
210
- Finished dispatches write a private completion record under `$OMNILANE_HOME/inbox/`. The bundled Claude Code `UserPromptSubmit` hook atomically delivers up to ten matching records on the foreman's next prompt, scoped by the dispatch `workdir`; claimed records move to `inbox/consumed/`. Set `OMNILANE_INBOX=0` on dispatch to disable record creation. The default is enabled.
210
+ Finished dispatches write a private completion record under `$OMNILANE_HOME/inbox/`. A Claude Code `SessionStart` hook binds each foreman's hook-provided session ID to its live process ancestry, and the bundled `UserPromptSubmit` hook atomically delivers up to ten records owned by that session on the foreman's next prompt. Older records without a session ID still use the original dispatch `workdir` scope; claimed records move to `inbox/consumed/`. Set `OMNILANE_INBOX=0` on a dispatch to disable record creation; the default is enabled.
211
211
 
212
212
  ## 📦 Install
213
213
 
@@ -228,6 +228,15 @@ each CLI's instruction file (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`,
228
228
  versions) so the main loop remembers to consult the table; non-interactive
229
229
  installs can pass `OMNILANE_HOOKS=all|none|claude,codex`.
230
230
 
231
+ The foreman completion notice requires the Claude Code plugin install; the
232
+ skill-only symlink path does not deliver completion notices. Install the plugin
233
+ from this checkout with:
234
+
235
+ ```bash
236
+ claude plugin marketplace add <path to this repo>
237
+ claude plugin install omnilane@omnilane
238
+ ```
239
+
231
240
  Use `./install.sh --check` for a read-only drift report. Add `--dry-run` to an
232
241
  install or `--uninstall` to preview every checkout-owned file action.
233
242
  Rollback the installer-owned links and marked reminders with
@@ -412,6 +421,24 @@ code passes through.
412
421
  - **Payload caps** — oversized task text is truncated head+tail before it can
413
422
  blow a worker's context.
414
423
 
424
+ ## 📬 Live mailbox
425
+
426
+ A live mailbox is a resident Claude or Gemini background dispatch, not a one-shot dispatch. The foreman opens it with `--background`, can send another instruction while it is still running, and is responsible for closing it with `jobs.sh close ID`. Leaving it unattended does not make it permanent: the idle cap and configured whole-job timeout (`--job-timeout`) can still end it.
427
+
428
+ ```bash
429
+ scripts/dispatch.sh --background --vendor claude hard-judgment "Review the timeout failure"
430
+ # save the printed job ID as $ID
431
+ scripts/jobs.sh send "$ID" "Also inspect the retry path."
432
+ scripts/jobs.sh watch "$ID"
433
+ scripts/jobs.sh tail "$ID" --lines 20
434
+ scripts/jobs.sh close "$ID"
435
+ scripts/jobs.sh retry "$ID" --background
436
+ ```
437
+
438
+ `watch` follows `$JOB_DIR/events.jsonl`; `tail` reads the public `out.txt`. Live mailbox support covers Claude and Gemini; other vendors run as normal one-shot dispatches, with a notice sent to stderr and stored in `$JOB_DIR/mode-notice.txt`. `--live` requires a resident session and fails fast when the resolved vendor is not capable. `--single-shot` forces one-shot execution even for Claude or Gemini. `--idle-timeout SECONDS` sets the inactivity cap (default 900; `0` disables it).
439
+
440
+ An idle mailbox makes no API calls and incurs no API spend. By default it closes after 900 seconds without a new inbox message or result event, while the whole-job timeout remains the outer cap. Close it sooner when its exchange is finished. `jobs.sh send` to a finished job or a job that is not live fails with a clear error. Do not use this for fire-and-forget work, vendors without live support, or a clean-slate rerun; start a fresh dispatch (or retry a completed job) instead.
441
+
415
442
  ## ❓ FAQ
416
443
 
417
444
  <details>
@@ -569,6 +596,34 @@ working notes, including per-benchmark caveats, live in
569
596
 
570
597
  ## 📜 Release history
571
598
 
599
+ ## What's new in v0.21.0
600
+
601
+ - **Explicit session mode.** Use `dispatch --live` to require a resident session or `--single-shot` to force a one-shot job. `--live` fails immediately for incompatible vendors and lists the live-capable choices.
602
+ - **Gemini joins the live mailbox.** Gemini can now run a resident live job through the `agy` stream protocol alongside Claude.
603
+ - **Idle cap for live jobs.** `--idle-timeout N` automatically closes an untended live session and preserves the close reason and timeout in `meta.json`.
604
+
605
+ ## What's new in v0.20.0
606
+
607
+ - **Foreman completion inbox.** Finished background dispatches write a private
608
+ completion record, and the bundled Claude Code plugin delivers matching
609
+ records into the foreman's next prompt. The output tail is injection-hardened:
610
+ control characters and `U+2028`/`U+2029` are removed, and worker output is
611
+ framed as indented data.
612
+ - **Installable Claude Code plugin.** `.claude-plugin/marketplace.json` uses a
613
+ self-referencing source, and the published npm tarball includes `hooks/`,
614
+ `skills/`, and `.claude-plugin/`.
615
+ - **Claude live mailbox.** Resident background Claude jobs can receive messages
616
+ while they run and record `events.jsonl`. For operations, see
617
+ [📬 Live mailbox](#-live-mailbox); other vendors explicitly fall back to
618
+ single-shot mode with a `stderr` and `mode-notice.txt` notice, and
619
+ `jobs.sh wait` ends with `done exit=N`.
620
+ - **Foreman session identity.** The `SessionStart` hook binds a Claude
621
+ `session_id` to a PID plus start time, making PID reuse safe. Dispatch walks
622
+ its ancestors to stamp `foreman_session` into `meta.json` and completion
623
+ records; the inbox prefers a session match and falls back to `workdir` for
624
+ legacy records, so two foremen in one repository do not take each other's
625
+ notices.
626
+
572
627
  ## What's new in v0.15.0
573
628
 
574
629
  - **Streaming Codex progress evidence** — `codex exec --json` now streams JSONL
package/README.zh-CN.md CHANGED
@@ -333,6 +333,24 @@ configure.sh set|get|unset|list|diff LANE [SPEC] # 非交互编辑/查看 rou
333
333
  而不是永远显示 `running`。
334
334
  - **任务载荷上限** — 过大的任务文本自动头尾截断,防止撑爆工作端上下文。
335
335
 
336
+ ## 📬 实时邮箱
337
+
338
+ 实时邮箱是 Claude 和 Gemini 可用的常驻后台派发,不是一次性派发。派发方以 `--background` 打开后,运行中仍可追加指令,并负责用 `jobs.sh close ID` 收尾。即使无人处理,它也不会永久存在:空闲上限或已配置的整个作业超时(`--job-timeout`)到期后都会终止它。
339
+
340
+ ```bash
341
+ scripts/dispatch.sh --background --vendor claude hard-judgment "检查超时测试失败的原因"
342
+ # 将 dispatch 显示的作业 ID 保存为 $ID
343
+ scripts/jobs.sh send "$ID" "再检查重试路径。"
344
+ scripts/jobs.sh watch "$ID"
345
+ scripts/jobs.sh tail "$ID" --lines 20
346
+ scripts/jobs.sh close "$ID"
347
+ scripts/jobs.sh retry "$ID" --background
348
+ ```
349
+
350
+ `watch` 会跟随 `$JOB_DIR/events.jsonl`;`tail` 读取公开的 `out.txt`。目前 Claude 和 Gemini 支持实时邮箱;其他供应商会执行普通的一次性派发,并在 stderr 和 `$JOB_DIR/mode-notice.txt` 留下提示。`--live` 明确要求常驻会话,解析出的供应商不支持时立即失败。`--single-shot` 即使遇到 Claude 或 Gemini 也强制一次性派发。`--idle-timeout SECONDS` 设置空闲上限,默认 900 秒,设为 `0` 可禁用。
351
+
352
+ 空闲时不会发出 API 调用,也不会产生 API 费用。默认若 900 秒内没有新邮箱消息或新结果事件,worker 会自动收尾;整个作业超时仍是外层上限。处理结束可提前执行 `close`。向已结束或不是实时邮箱的作业执行 `jobs.sh send` 会明确报错并失败。即发即忘的工作、没有实时支持的供应商,或需要从干净状态重新运行的情况都不适用;请新建一次派发,或在作业完成后使用 `retry`。
353
+
336
354
  ## ❓ 常见问题
337
355
 
338
356
  <details>
@@ -473,6 +491,29 @@ vendor 一律当成 `work`,而且它只能逐次明确指定,永远不是 lane
473
491
 
474
492
  ## 📜 版本历程
475
493
 
494
+ ## v0.21.0 新功能
495
+
496
+ - **显式选择会话模式。** 可使用 `dispatch --live` 要求常驻会话,或用 `--single-shot` 强制单次派发;对不支持实时会话的供应商,`--live` 会立即失败并列出可用供应商。
497
+ - **Gemini 加入实时邮箱。** Gemini 通过 `agy` 流式协议加入常驻实时任务,与 Claude 并列支持。
498
+ - **实时任务的空闲上限。** `--idle-timeout N` 会自动关闭无人处理的实时会话,并在关闭原因与 `meta.json` 中保留超时信息。
499
+
500
+ ## v0.20.0 新功能
501
+
502
+ - **Foreman 完成收件箱。** 后台派发完成后会写入私有完成记录,内置 Claude
503
+ Code 插件会在 Foreman 的下一次提示中递交匹配记录。输出尾段已做提示注入防护:
504
+ 移除控制字符及 `U+2028`/`U+2029`,并将工作器输出框为缩进数据。
505
+ - **可安装的 Claude Code 插件。** `.claude-plugin/marketplace.json` 使用自指
506
+ 来源,已发布的 npm tarball 也包含 `hooks/`、`skills/` 和
507
+ `.claude-plugin/`。
508
+ - **Claude 实时邮箱。** 常驻后台 Claude 作业可在运行时接收消息,并写入
509
+ `events.jsonl`。操作方式请见 [📬 实时邮箱](#-实时邮箱);其他供应商会明确
510
+ 降级为单次模式,并留下 `stderr` 与 `mode-notice.txt` 通知;`jobs.sh wait`
511
+ 最后会输出 `done exit=N`。
512
+ - **Foreman 会话身份。** `SessionStart` hook 会将 Claude `session_id` 绑定到
513
+ PID 与启动时间,避免 PID 重用误判。派发会沿父进程向上查找,将
514
+ `foreman_session` 写入 `meta.json` 与完成记录;收件箱优先按会话匹配,旧记录
515
+ 才回退至 `workdir`,避免同一 repository 的两个 Foreman 互取通知。
516
+
476
517
  ## v0.15.0 新功能
477
518
 
478
519
  - **流式保留 Codex 进度证据**:`codex exec --json` 会将 JSONL 事件逐条写入
package/README.zh-TW.md CHANGED
@@ -360,6 +360,24 @@ codex/claude/grok/gemini 自選 1-4 個評審。開了之後,同一個問題丟
360
360
  而不是永遠顯示 `running`。
361
361
  - **任務酬載上限** — 過大的任務文字自動頭尾截斷,防止撐爆工作端脈絡。
362
362
 
363
+ ## 📬 即時信箱
364
+
365
+ 即時信箱是 Claude 與 Gemini 可用的常駐背景派工,不是一次性派工。派工者以 `--background` 開啟後,執行中仍能補傳指示,並負責用 `jobs.sh close ID` 收尾。即使沒人處理,也不會永久存在:閒置上限或設定的整體工作逾時(`--job-timeout`)一到就會結束。
366
+
367
+ ```bash
368
+ scripts/dispatch.sh --background --vendor claude hard-judgment "檢查逾時測試失敗的原因"
369
+ # 將 dispatch 顯示的工作 ID 存成 $ID
370
+ scripts/jobs.sh send "$ID" "再確認重試路徑。"
371
+ scripts/jobs.sh watch "$ID"
372
+ scripts/jobs.sh tail "$ID" --lines 20
373
+ scripts/jobs.sh close "$ID"
374
+ scripts/jobs.sh retry "$ID" --background
375
+ ```
376
+
377
+ `watch` 追隨 `$JOB_DIR/events.jsonl`;`tail` 讀取公開的 `out.txt`。目前 Claude 與 Gemini 支援即時信箱;其他供應商會執行一般的一次性派工,stderr 與 `$JOB_DIR/mode-notice.txt` 都會留下提示。`--live` 會明確要求常駐工作階段,解析出的供應商不支援時立即失敗。`--single-shot` 即使遇到 Claude 或 Gemini 也會強制一次性派工。`--idle-timeout SECONDS` 設定閒置上限,預設 900 秒,設為 `0` 則停用。
378
+
379
+ 閒置時不會發出 API 呼叫,也不會增加 API 費用。預設若 900 秒內沒有新信箱訊息或新結果事件,工作程序會自動收尾;整體工作逾時仍是外層上限。處理完成可提早執行 `close`。對已結束或不是即時信箱的工作使用 `jobs.sh send`,會明確報錯並失敗。送出後不需追蹤的工作、沒有即時支援的供應商,或必須從乾淨狀態重跑的情況都不適用;請使用新的派工,或在工作完成後使用 `retry`。
380
+
363
381
  ## ❓ 常見問題
364
382
 
365
383
  <details>
@@ -497,6 +515,29 @@ vendor 一律當成 `work`,而且它只能逐次明確指定,永遠不是 lane
497
515
 
498
516
  ## 📜 版本歷程
499
517
 
518
+ ## v0.21.0 新功能
519
+
520
+ - **明確選擇工作階段模式。** 可用 `dispatch --live` 要求常駐工作階段,或以 `--single-shot` 強制單次派工;對不支援即時工作階段的供應商,`--live` 會立即失敗並列出可用供應商。
521
+ - **Gemini 加入即時信箱。** Gemini 透過 `agy` 串流協定加入常駐即時工作,與 Claude 並列支援。
522
+ - **即時工作的閒置上限。** `--idle-timeout N` 會自動關閉無人處理的即時工作階段,並在關閉原因與 `meta.json` 留下逾時資訊。
523
+
524
+ ## v0.20.0 新功能
525
+
526
+ - **Foreman 完成收件匣。** 背景派工完成後會寫入私有完成紀錄,內建的 Claude
527
+ Code 外掛會在 Foreman 的下一個提示中遞交相符紀錄。輸出尾段已做提示注入防護:
528
+ 移除控制字元與 `U+2028`/`U+2029`,並把工作者輸出框成縮排資料。
529
+ - **可安裝的 Claude Code 外掛。** `.claude-plugin/marketplace.json` 使用自指
530
+ 來源,已發布的 npm tarball 也會包含 `hooks/`、`skills/` 和
531
+ `.claude-plugin/`。
532
+ - **Claude 即時信箱。** 常駐背景 Claude 工作可在執行時接收訊息,並寫入
533
+ `events.jsonl`。操作方式請見 [📬 即時信箱](#-即時信箱);其他供應商會明確
534
+ 退化成單次模式,留下 `stderr` 與 `mode-notice.txt` 通知;`jobs.sh wait`
535
+ 最後會印出 `done exit=N`。
536
+ - **Foreman 工作階段身分。** `SessionStart` hook 會把 Claude `session_id` 綁定
537
+ 至 PID 與開始時間,避免 PID 重用誤判。派工會往上走訪父程序,將
538
+ `foreman_session` 寫入 `meta.json` 與完成紀錄;收件匣先比對工作階段,舊紀錄
539
+ 才回退到 `workdir`,避免同一 repository 的兩個 Foreman 互拿通知。
540
+
500
541
  ## v0.15.0 新功能
501
542
 
502
543
  - **串流保留 Codex 進度證據**:`codex exec --json` 會把 JSONL 事件逐筆寫入
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.15.0
1
+ 0.21.0
package/bin/omnilane CHANGED
@@ -30,7 +30,8 @@ case "$cmd" in
30
30
  ;;
31
31
  list) exec bash "$REPO/scripts/dispatch.sh" --list ;;
32
32
  route|dispatch) exec bash "$REPO/scripts/dispatch.sh" "$@" ;;
33
- jobs) exec bash "$REPO/scripts/jobs.sh" "$@" ;;
33
+ jobs) exec bash "$REPO/scripts/jobs.sh" "$@" ;;
34
+ goal) exec bash "$REPO/scripts/lib/goal-loop.sh" "$@" ;;
34
35
  mcp)
35
36
  command -v node >/dev/null 2>&1 || {
36
37
  echo "omnilane: mcp requires Node.js on PATH" >&2
@@ -75,6 +76,9 @@ omnilane — one routing table, every harness
75
76
  omnilane list effective routing table
76
77
  omnilane route [--vendor V] [flags] LANE "TASK"
77
78
  dispatch or consult a model
79
+ omnilane goal "TEXT" [--budget-jobs N] [--budget-seconds S] [--workdir DIR]
80
+ run bounded sequential goal loop
81
+ omnilane goal status GOAL_ID inspect goal budget and last action
78
82
  omnilane jobs [--json] COMMAND [ARGS] list, status, result, stats, recommend, wait, or audit
79
83
  omnilane mcp start the MCP stdio server
80
84
  omnilane completion bash|zsh|fish print a shell completion script
@@ -0,0 +1,33 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/record-foreman-session.sh",
10
+ "timeout": 5
11
+ },
12
+ {
13
+ "type": "command",
14
+ "command": "cat",
15
+ "args": ["${CLAUDE_PLUGIN_ROOT}/hooks/routing-instruction.md"],
16
+ "timeout": 5
17
+ }
18
+ ]
19
+ }
20
+ ],
21
+ "UserPromptSubmit": [
22
+ {
23
+ "hooks": [
24
+ {
25
+ "type": "command",
26
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/report-completions.sh",
27
+ "timeout": 5
28
+ }
29
+ ]
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # Fail-open SessionStart hook: bind Claude's session_id to its process identity.
3
+ set -uo pipefail
4
+
5
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd -P)" || exit 0
6
+ # shellcheck source=../scripts/lib/common.sh
7
+ source "$ROOT/scripts/lib/common.sh" >/dev/null 2>&1 || exit 0
8
+
9
+ record_foreman_session() {
10
+ local session_id parent_pid start_time sessions entry tmp old_umask rc=0
11
+ session_id="$(perl -MJSON::PP -0777 -e '
12
+ use strict;
13
+ use warnings;
14
+ my $payload = eval { decode_json(<STDIN>) };
15
+ exit 1 unless ref($payload) eq "HASH";
16
+ my $id = $payload->{session_id};
17
+ exit 1 if ref($id) || !defined($id) ||
18
+ $id !~ /\A[A-Za-z0-9._:-]{1,256}\z/;
19
+ print $id;
20
+ ' 2>/dev/null)" || return 0
21
+
22
+ parent_pid="$PPID"
23
+ [[ "$parent_pid" =~ ^[1-9][0-9]{0,9}$ ]] || return 0
24
+ start_time="$(process_start_time "$parent_pid")" || return 0
25
+ sessions="$OMNILANE_HOME/sessions"
26
+ prepare_private_store "$sessions" "session identity store" || return 0
27
+ entry="$sessions/$parent_pid.json"
28
+ tmp="$sessions/.$parent_pid.tmp.$$-$RANDOM"
29
+
30
+ old_umask="$(umask)"
31
+ umask 077
32
+ PID_VALUE="$parent_pid" START_VALUE="$start_time" SESSION_VALUE="$session_id" \
33
+ perl -MJSON::PP -e '
34
+ use strict;
35
+ use warnings;
36
+ my $entry = {
37
+ pid => 0 + $ENV{PID_VALUE},
38
+ start_time => $ENV{START_VALUE},
39
+ session_id => $ENV{SESSION_VALUE},
40
+ };
41
+ print JSON::PP->new->canonical->encode($entry), "\n";
42
+ ' > "$tmp" || rc=$?
43
+ if [[ "$rc" -eq 0 ]]; then
44
+ chmod 600 "$tmp" || rc=$?
45
+ fi
46
+ if [[ "$rc" -eq 0 ]]; then
47
+ mv "$tmp" "$entry" || rc=$?
48
+ fi
49
+ umask "$old_umask"
50
+ if [[ "$rc" -ne 0 ]]; then
51
+ rm "$tmp" 2>/dev/null || true
52
+ fi
53
+ return 0
54
+ }
55
+
56
+ record_foreman_session || true
57
+ exit 0