cueline 0.1.7 → 0.2.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.
Files changed (75) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +23 -0
  4. package/README.ja.md +67 -42
  5. package/README.ko.md +67 -42
  6. package/README.md +45 -70
  7. package/README.zh-CN.md +67 -42
  8. package/README.zh-TW.md +57 -40
  9. package/dist/src/api-contracts.d.ts +7 -0
  10. package/dist/src/api-controller-handoff.d.ts +5 -1
  11. package/dist/src/api-controller-handoff.js +148 -0
  12. package/dist/src/api-controller-handoff.js.map +1 -1
  13. package/dist/src/api.d.ts +9 -1
  14. package/dist/src/api.js +6 -2
  15. package/dist/src/api.js.map +1 -1
  16. package/dist/src/browser/browser-adapter.d.ts +26 -0
  17. package/dist/src/browser/codex-iab/bootstrap.d.ts +1 -0
  18. package/dist/src/browser/codex-iab/bootstrap.js +1 -0
  19. package/dist/src/browser/codex-iab/bootstrap.js.map +1 -1
  20. package/dist/src/browser/codex-iab/chatgpt-client.js +107 -19
  21. package/dist/src/browser/codex-iab/chatgpt-client.js.map +1 -1
  22. package/dist/src/cli/health-commands.js +74 -0
  23. package/dist/src/cli/health-commands.js.map +1 -1
  24. package/dist/src/cli/main.js +31 -10
  25. package/dist/src/cli/main.js.map +1 -1
  26. package/dist/src/cli/observation-commands.js +112 -1
  27. package/dist/src/cli/observation-commands.js.map +1 -1
  28. package/dist/src/cli/run-status-view.d.ts +8 -0
  29. package/dist/src/cli/run-status-view.js +9 -0
  30. package/dist/src/cli/run-status-view.js.map +1 -1
  31. package/dist/src/core/controller-loop.js +23 -1
  32. package/dist/src/core/controller-loop.js.map +1 -1
  33. package/dist/src/core/controller-turn.d.ts +7 -1
  34. package/dist/src/core/controller-turn.js +73 -1
  35. package/dist/src/core/controller-turn.js.map +1 -1
  36. package/dist/src/core/run-status.d.ts +9 -1
  37. package/dist/src/core/run-status.js +26 -0
  38. package/dist/src/core/run-status.js.map +1 -1
  39. package/dist/src/core/state-machine.d.ts +15 -0
  40. package/dist/src/core/state-machine.js +104 -0
  41. package/dist/src/core/state-machine.js.map +1 -1
  42. package/dist/src/observation/run-diff.d.ts +39 -0
  43. package/dist/src/observation/run-diff.js +67 -0
  44. package/dist/src/observation/run-diff.js.map +1 -0
  45. package/dist/src/observation/run-graph.d.ts +17 -0
  46. package/dist/src/observation/run-graph.js +77 -0
  47. package/dist/src/observation/run-graph.js.map +1 -0
  48. package/dist/src/observation/run-status-at.d.ts +41 -0
  49. package/dist/src/observation/run-status-at.js +65 -0
  50. package/dist/src/observation/run-status-at.js.map +1 -0
  51. package/dist/src/observation/run-timeline.js +2 -1
  52. package/dist/src/observation/run-timeline.js.map +1 -1
  53. package/dist/src/router/explain.d.ts +28 -0
  54. package/dist/src/router/explain.js +86 -0
  55. package/dist/src/router/explain.js.map +1 -0
  56. package/dist/src/router/resolver.d.ts +2 -1
  57. package/dist/src/router/resolver.js +3 -3
  58. package/dist/src/router/resolver.js.map +1 -1
  59. package/dist/src/version.d.ts +1 -1
  60. package/dist/src/version.js +1 -1
  61. package/docs/assets/README.md +3 -1
  62. package/docs/assets/cueline-architecture-en.svg +63 -0
  63. package/docs/assets/cueline-architecture-ja.svg +63 -0
  64. package/docs/assets/cueline-architecture-ko.svg +63 -0
  65. package/docs/assets/cueline-architecture-zh-CN.svg +63 -0
  66. package/docs/assets/cueline-architecture-zh-TW.svg +63 -0
  67. package/docs/assets/cueline-states-en.svg +50 -0
  68. package/docs/assets/cueline-states-ja.svg +48 -0
  69. package/docs/assets/cueline-states-ko.svg +48 -0
  70. package/docs/assets/cueline-states-zh-CN.svg +48 -0
  71. package/docs/assets/cueline-states-zh-TW.svg +48 -0
  72. package/docs/controller-protocol.md +6 -0
  73. package/docs/multi-model-routing.md +256 -0
  74. package/docs/state-and-recovery.md +30 -0
  75. package/package.json +1 -1
@@ -0,0 +1,48 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 424" width="1000" height="424" role="img" aria-label="CueLine の run 状態:ready、awaiting_controller、awaiting_caller、awaiting_caller_work、complete、blocked、cancelled と、それぞれの意味。">
2
+ <title>CueLine の run 状態</title>
3
+ <style>
4
+ text { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Hiragino Kaku Gothic ProN", "Yu Gothic", monospace }
5
+ .hd { fill: #0F1720; font-size: 17px; letter-spacing: .02em }
6
+ .sub { fill: #8B949E; font-size: 13.5px }
7
+ .rule { stroke: #E4E8EC; stroke-width: 1 }
8
+ .say { fill: #C8553D; font-size: 15px }
9
+ .obs { fill: #4A5561; font-size: 14px }
10
+ .foot { fill: #8B949E; font-size: 13.5px }
11
+ @media (prefers-color-scheme: dark) {
12
+ .hd { fill: #E6EDF3 }
13
+ .sub, .foot { fill: #7D8590 }
14
+ .rule { stroke: #21262D }
15
+ .say { fill: #E0674C }
16
+ .obs { fill: #ADBAC7 }
17
+ }
18
+ </style>
19
+
20
+ <text x="40" y="44" class="hd">run の状態</text>
21
+ <text x="40" y="64" class="sub">状態値は API のそのままの文字列</text>
22
+ <path d="M40 84H960" class="rule"/>
23
+
24
+ <text x="60" y="122" class="say">ready</text>
25
+ <text x="340" y="122" class="obs">作成済みで永続化済み。まだ何も送信していない</text>
26
+
27
+ <text x="60" y="156" class="say">awaiting_controller</text>
28
+ <text x="340" y="156" class="obs">永続的な送信を 1 回完了——観測のみ、再送は決してしない</text>
29
+
30
+ <text x="60" y="190" class="say">awaiting_caller</text>
31
+ <text x="340" y="190" class="obs">advise の引き渡し——調整のみ、副作用 claim なし</text>
32
+
33
+ <text x="60" y="224" class="say">awaiting_caller_work</text>
34
+ <text x="340" y="224" class="obs">work 保留——claim + start までは開始されない</text>
35
+
36
+ <text x="60" y="258" class="say">complete</text>
37
+ <text x="340" y="258" class="obs">最終成果物をそのまま返却。任意のアーカイブは 1 回だけ</text>
38
+
39
+ <text x="60" y="292" class="say">blocked</text>
40
+ <text x="340" y="292" class="obs">コントローラーが明確な理由つきで停止</text>
41
+
42
+ <text x="60" y="326" class="say">cancelled</text>
43
+ <text x="340" y="326" class="obs">永続的なキャンセル。開始済みの owner 不在の work は ambiguous に</text>
44
+
45
+ <path d="M40 352H960" class="rule"/>
46
+ <text x="40" y="378" class="foot">曖昧なものはすべて明示的な reconcile が必要——CueLine は再送も推測もしない。</text>
47
+ <text x="40" y="400" class="foot">ラウンド上限は既定 12。既定値以外の maxRounds は run 作成時に固定される。</text>
48
+ </svg>
@@ -0,0 +1,48 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 424" width="1000" height="424" role="img" aria-label="CueLine run 상태: ready, awaiting_controller, awaiting_caller, awaiting_caller_work, complete, blocked, cancelled — 각 상태의 의미.">
2
+ <title>CueLine run 상태</title>
3
+ <style>
4
+ text { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Apple SD Gothic Neo", "Malgun Gothic", monospace }
5
+ .hd { fill: #0F1720; font-size: 17px; letter-spacing: .02em }
6
+ .sub { fill: #8B949E; font-size: 13.5px }
7
+ .rule { stroke: #E4E8EC; stroke-width: 1 }
8
+ .say { fill: #C8553D; font-size: 15px }
9
+ .obs { fill: #4A5561; font-size: 14px }
10
+ .foot { fill: #8B949E; font-size: 13.5px }
11
+ @media (prefers-color-scheme: dark) {
12
+ .hd { fill: #E6EDF3 }
13
+ .sub, .foot { fill: #7D8590 }
14
+ .rule { stroke: #21262D }
15
+ .say { fill: #E0674C }
16
+ .obs { fill: #ADBAC7 }
17
+ }
18
+ </style>
19
+
20
+ <text x="40" y="44" class="hd">run 상태</text>
21
+ <text x="40" y="64" class="sub">상태 값은 API 문자열 그대로입니다</text>
22
+ <path d="M40 84H960" class="rule"/>
23
+
24
+ <text x="60" y="122" class="say">ready</text>
25
+ <text x="340" y="122" class="obs">생성되고 영속화됨; 아직 아무것도 보내지 않음</text>
26
+
27
+ <text x="60" y="156" class="say">awaiting_controller</text>
28
+ <text x="340" y="156" class="obs">지속 전송 한 번 완료 — 관측만 하고 절대 재전송하지 않음</text>
29
+
30
+ <text x="60" y="190" class="say">awaiting_caller</text>
31
+ <text x="340" y="190" class="obs">advise 인계 — 조정 전용, 부작용 claim 없음</text>
32
+
33
+ <text x="60" y="224" class="say">awaiting_caller_work</text>
34
+ <text x="340" y="224" class="obs">work 대기 — claim + start 전에는 시작되지 않음</text>
35
+
36
+ <text x="60" y="258" class="say">complete</text>
37
+ <text x="340" y="258" class="obs">최종 결과를 그대로 반환; 선택적 보관은 정확히 한 번</text>
38
+
39
+ <text x="60" y="292" class="say">blocked</text>
40
+ <text x="340" y="292" class="obs">컨트롤러가 명확한 이유와 함께 중단</text>
41
+
42
+ <text x="60" y="326" class="say">cancelled</text>
43
+ <text x="340" y="326" class="obs">지속 취소; 시작된 owner 없는 work는 ambiguous가 됨</text>
44
+
45
+ <path d="M40 352H960" class="rule"/>
46
+ <text x="40" y="378" class="foot">모호한 것은 모두 명시적 reconcile이 필요합니다 — CueLine은 재전송도 추측도 하지 않습니다.</text>
47
+ <text x="40" y="400" class="foot">라운드 상한은 기본 12; 기본값이 아닌 maxRounds는 run 생성 시 고정됩니다.</text>
48
+ </svg>
@@ -0,0 +1,48 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 424" width="1000" height="424" role="img" aria-label="CueLine 运行状态:ready、awaiting_controller、awaiting_caller、awaiting_caller_work、complete、blocked、cancelled,以及每个状态的含义。">
2
+ <title>CueLine 运行状态</title>
3
+ <style>
4
+ text { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", "PingFang SC", "Microsoft YaHei", monospace }
5
+ .hd { fill: #0F1720; font-size: 17px; letter-spacing: .02em }
6
+ .sub { fill: #8B949E; font-size: 13.5px }
7
+ .rule { stroke: #E4E8EC; stroke-width: 1 }
8
+ .say { fill: #C8553D; font-size: 15px }
9
+ .obs { fill: #4A5561; font-size: 14px }
10
+ .foot { fill: #8B949E; font-size: 13.5px }
11
+ @media (prefers-color-scheme: dark) {
12
+ .hd { fill: #E6EDF3 }
13
+ .sub, .foot { fill: #7D8590 }
14
+ .rule { stroke: #21262D }
15
+ .say { fill: #E0674C }
16
+ .obs { fill: #ADBAC7 }
17
+ }
18
+ </style>
19
+
20
+ <text x="40" y="44" class="hd">运行状态</text>
21
+ <text x="40" y="64" class="sub">状态值为 API 原文字符串</text>
22
+ <path d="M40 84H960" class="rule"/>
23
+
24
+ <text x="60" y="122" class="say">ready</text>
25
+ <text x="340" y="122" class="obs">已创建且持久化;尚未发送任何内容</text>
26
+
27
+ <text x="60" y="156" class="say">awaiting_controller</text>
28
+ <text x="340" y="156" class="obs">已完成一次持久发送——只观测、绝不重发</text>
29
+
30
+ <text x="60" y="190" class="say">awaiting_caller</text>
31
+ <text x="340" y="190" class="obs">advise 交接——仅协调,无副作用 claim</text>
32
+
33
+ <text x="60" y="224" class="say">awaiting_caller_work</text>
34
+ <text x="340" y="224" class="obs">work 待办——claim + start 之前绝不开始</text>
35
+
36
+ <text x="60" y="258" class="say">complete</text>
37
+ <text x="340" y="258" class="obs">最终交付逐字返回;可选归档,恰好一次</text>
38
+
39
+ <text x="60" y="292" class="say">blocked</text>
40
+ <text x="340" y="292" class="obs">控制器附明确理由停下</text>
41
+
42
+ <text x="60" y="326" class="say">cancelled</text>
43
+ <text x="340" y="326" class="obs">持久取消;已开始的无主工作变为 ambiguous</text>
44
+
45
+ <path d="M40 352H960" class="rule"/>
46
+ <text x="40" y="378" class="foot">任何模糊状态都需要显式 reconcile——CueLine 绝不重发、绝不靠猜。</text>
47
+ <text x="40" y="400" class="foot">轮次上限默认 12;非默认 maxRounds 在创建 run 时即固定。</text>
48
+ </svg>
@@ -0,0 +1,48 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 424" width="1000" height="424" role="img" aria-label="CueLine 執行狀態:ready、awaiting_controller、awaiting_caller、awaiting_caller_work、complete、blocked、cancelled,以及每個狀態的意義。">
2
+ <title>CueLine 執行狀態</title>
3
+ <style>
4
+ text { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", "PingFang TC", "Microsoft JhengHei", monospace }
5
+ .hd { fill: #0F1720; font-size: 17px; letter-spacing: .02em }
6
+ .sub { fill: #8B949E; font-size: 13.5px }
7
+ .rule { stroke: #E4E8EC; stroke-width: 1 }
8
+ .say { fill: #C8553D; font-size: 15px }
9
+ .obs { fill: #4A5561; font-size: 14px }
10
+ .foot { fill: #8B949E; font-size: 13.5px }
11
+ @media (prefers-color-scheme: dark) {
12
+ .hd { fill: #E6EDF3 }
13
+ .sub, .foot { fill: #7D8590 }
14
+ .rule { stroke: #21262D }
15
+ .say { fill: #E0674C }
16
+ .obs { fill: #ADBAC7 }
17
+ }
18
+ </style>
19
+
20
+ <text x="40" y="44" class="hd">執行狀態</text>
21
+ <text x="40" y="64" class="sub">狀態值為 API 原文字串</text>
22
+ <path d="M40 84H960" class="rule"/>
23
+
24
+ <text x="60" y="122" class="say">ready</text>
25
+ <text x="340" y="122" class="obs">已建立且持久化;尚未送出任何東西</text>
26
+
27
+ <text x="60" y="156" class="say">awaiting_controller</text>
28
+ <text x="340" y="156" class="obs">已完成一次持久送出——只觀測、絕不重送</text>
29
+
30
+ <text x="60" y="190" class="say">awaiting_caller</text>
31
+ <text x="340" y="190" class="obs">advise 交接——僅協調,無副作用 claim</text>
32
+
33
+ <text x="60" y="224" class="say">awaiting_caller_work</text>
34
+ <text x="340" y="224" class="obs">work 待辦——claim + start 之前絕不開始</text>
35
+
36
+ <text x="60" y="258" class="say">complete</text>
37
+ <text x="340" y="258" class="obs">最終交付逐字回傳;選用封存,恰好一次</text>
38
+
39
+ <text x="60" y="292" class="say">blocked</text>
40
+ <text x="340" y="292" class="obs">主控端附明確理由停下</text>
41
+
42
+ <text x="60" y="326" class="say">cancelled</text>
43
+ <text x="340" y="326" class="obs">持久取消;已開始的無主工作轉為 ambiguous</text>
44
+
45
+ <path d="M40 352H960" class="rule"/>
46
+ <text x="40" y="378" class="foot">任何含糊狀態都需要明確 reconcile——CueLine 絕不重送、絕不用猜的。</text>
47
+ <text x="40" y="400" class="foot">輪數上限預設 12;非預設 maxRounds 在建立 run 時即固定。</text>
48
+ </svg>
@@ -13,6 +13,12 @@ Every command must echo these values from the pending observation:
13
13
 
14
14
  A stale or mismatched value is rejected. CueLine parses only the **last complete** control envelope, which prevents an older example earlier in the assistant response from winning.
15
15
 
16
+ ### Not-sent retry identity
17
+
18
+ Operator-confirmed not-sent recovery abandons the original request identity and creates exactly one new request ID. The controller sees the new identity in the next observation and must echo it. CueLine verifies that the regenerated prompt matches the abandoned checkpoint hash after replacing only the request ID; a changed instruction, observation, or other prompt content is not a recovery retry.
19
+
20
+ The abandoned identity remains durable. If later browser evidence or a control envelope proves that the abandoned user message or response actually appeared, CueLine fails closed and requires manual review. It never accepts an old envelope as the new retry's result.
21
+
16
22
  ## Observation
17
23
 
18
24
  ```json
@@ -0,0 +1,256 @@
1
+ # Multi-model routing
2
+
3
+ ## What ships by default
4
+
5
+ CueLine ships one enabled lane, `default`, with one candidate, `codex-default`. That candidate runs the bundled `codex exec --ignore-user-config` process route when process execution is explicitly authorized.
6
+
7
+ Process execution is not the default. `startCueLineRun` and `runCueLine` default to `executor: "caller"`, which spawns no worker process at all. A controller dispatch is persisted and handed back to the current Codex for execution. Multi-model process routing is therefore an opt-in user configuration, not a CueLine code change.
8
+
9
+ ## What the controller can see
10
+
11
+ For a process run, CueLine builds a routing instruction before every controller turn. ChatGPT Pro sees only:
12
+
13
+ - enabled lane names;
14
+ - enabled, currently available candidate IDs inside each lane.
15
+
16
+ The controller never sees candidate `argv`, executable paths, wrapper contents, environment handling, or flags such as `--model`. It may select a lane and may name one advertised candidate with the optional `runner` field. CueLine validates that choice locally before any process starts.
17
+
18
+ Candidate IDs are therefore the controller's menu. Encode both the model and intended purpose in each ID so the choice remains meaningful without exposing implementation details:
19
+
20
+ - `claude-opus-4-8-advise`
21
+ - `gemini-3-1-pro-advise`
22
+ - `grok-4-5-advise-search`
23
+
24
+ Keep IDs stable after they are used in durable runs. Change the `argv` behind an ID only when the new command preserves the same intended contract; otherwise add a new candidate ID.
25
+
26
+ ## Known limitations and wrapper boundaries
27
+
28
+ The following constraints were verified against CueLine `0.1.7` and working local multi-model runners on 2026-07-16.
29
+
30
+ ### Candidates cannot define environment variables
31
+
32
+ The routing schema accepts `id`, `argv`, `task_input`, and `enabled`. It has no `env` field.
33
+
34
+ A worker CLI that requires environment setup must therefore use a wrapper script as `argv[0]`. For example, the Antigravity `agy` CLI needs `NO_BROWSER=1` for headless execution and should have API-key variables removed when the intended path is the existing CLI login. A wrapper can establish that environment before replacing itself with the real worker process.
35
+
36
+ Keep secrets out of the routing JSON and wrapper source. Wrappers should unset conflicting variable names or inherit an already authorized local login; they should not embed credential values.
37
+
38
+ ### Candidate argv cannot branch on `advise` versus `work`
39
+
40
+ CueLine expands fixed placeholders inside individual argv elements. `{mode}` expands to the literal `advise` or `work`. `{sandbox}` is the only mode-derived argument value: it expands to `read-only` for `advise` and `workspace-write` for `work`. Those values are designed for the Codex sandbox flag and are not a portable permission contract for other CLIs.
41
+
42
+ The routing format has no conditional expression that can select one argv sequence for `advise` and another for `work`. For non-Codex workers, prefer an advise-only lane:
43
+
44
+ - Claude: hardcode `--tools Read Glob Grep`.
45
+ - Antigravity: hardcode `--mode plan`.
46
+ - Grok: hardcode `--permission-mode plan`.
47
+
48
+ Keep mutating `work` jobs on the bundled `default` lane unless a separate worker has an independently verified work-mode permission contract.
49
+
50
+ ### Registration is not a sandbox
51
+
52
+ The runner contract states:
53
+
54
+ > Registration is an allow-list, not a sandbox: the registered program still has the OS permissions of the local CueLine process.
55
+
56
+ CueLine verifies that `argv[0]` came from the loaded routing configuration and launches it without a shell. That prevents the controller from supplying an arbitrary executable, but it does not reduce the executable's operating-system permissions.
57
+
58
+ A registered non-Codex worker therefore runs with the full OS permissions of the local CueLine process. Its read-only behavior depends entirely on the CLI flags, wrapper behavior, credentials, and working directory chosen by the user.
59
+
60
+ ## Worked four-lane configuration
61
+
62
+ This example keeps `work` on the bundled Codex route and adds three advise-only lanes:
63
+
64
+ - `taste-final`: Claude Opus for final prose and judgment;
65
+ - `long-context`: Gemini through an Antigravity wrapper;
66
+ - `live-search`: Grok through a live-search wrapper.
67
+
68
+ Create a private runner directory and replace `/absolute/path/to/cueline-runners` below with its real absolute path. Do not use `$HOME` or `~` inside JSON; CueLine passes argv directly and does not perform shell expansion.
69
+
70
+ ### Routing configuration
71
+
72
+ ```json
73
+ {
74
+ "$schema": "/absolute/path/to/cueline/config/routing.schema.json",
75
+ "version": 1,
76
+ "lanes": {
77
+ "default": {
78
+ "enabled": true,
79
+ "candidates": [
80
+ {
81
+ "id": "codex-default",
82
+ "argv": [
83
+ "codex",
84
+ "exec",
85
+ "--ignore-user-config",
86
+ "--skip-git-repo-check",
87
+ "--ephemeral",
88
+ "--color",
89
+ "never",
90
+ "-C",
91
+ "{workdir}",
92
+ "-s",
93
+ "{sandbox}",
94
+ "-"
95
+ ],
96
+ "task_input": "stdin"
97
+ }
98
+ ]
99
+ },
100
+ "taste-final": {
101
+ "enabled": true,
102
+ "candidates": [
103
+ {
104
+ "id": "claude-opus-4-8-advise",
105
+ "argv": [
106
+ "claude",
107
+ "--disable-slash-commands",
108
+ "--model",
109
+ "claude-opus-4-8",
110
+ "--output-format",
111
+ "text",
112
+ "--effort",
113
+ "high",
114
+ "--tools",
115
+ "Read",
116
+ "Glob",
117
+ "Grep",
118
+ "-p",
119
+ "{task}"
120
+ ],
121
+ "task_input": "argv"
122
+ }
123
+ ]
124
+ },
125
+ "long-context": {
126
+ "enabled": true,
127
+ "candidates": [
128
+ {
129
+ "id": "gemini-3-1-pro-advise",
130
+ "argv": [
131
+ "/absolute/path/to/cueline-runners/gemini-advise.sh",
132
+ "Gemini 3.1 Pro (High)"
133
+ ],
134
+ "task_input": "stdin"
135
+ }
136
+ ]
137
+ },
138
+ "live-search": {
139
+ "enabled": true,
140
+ "candidates": [
141
+ {
142
+ "id": "grok-4-5-advise-search",
143
+ "argv": [
144
+ "/absolute/path/to/cueline-runners/grok-advise.sh",
145
+ "grok-4.5"
146
+ ],
147
+ "task_input": "stdin"
148
+ }
149
+ ]
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ The `$schema` path is editor metadata; set it to the installed or checked-out `routing.schema.json` when schema-aware validation is desired. CueLine validates the routing object independently at load time.
156
+
157
+ ### Gemini wrapper
158
+
159
+ Save as `gemini-advise.sh` and make it executable:
160
+
161
+ ```bash
162
+ #!/usr/bin/env bash
163
+ # CueLine advise-only Gemini runner through Antigravity.
164
+ # argv: gemini-advise.sh "<agy model display string>"; task arrives on stdin.
165
+ set -euo pipefail
166
+
167
+ MODEL="${1:-Gemini 3.1 Pro (High)}"
168
+ TASK="$(cat)"
169
+
170
+ # Stay on the existing CLI login and avoid browser interaction.
171
+ exec env -u GEMINI_API_KEY -u GOOGLE_API_KEY -u GOOGLE_AI_API_KEY \
172
+ NO_BROWSER=1 \
173
+ agy --dangerously-skip-permissions --add-dir "$PWD" \
174
+ --mode plan --model "$MODEL" \
175
+ --print-timeout 570s \
176
+ --print "$TASK"
177
+ ```
178
+
179
+ `--dangerously-skip-permissions` disables Antigravity's interactive permission prompts; it does not make the process read-only. The enforced read-only intent in this wrapper comes from `--mode plan`. Verify that contract against the installed `agy` version before registering it.
180
+
181
+ ### Grok wrapper
182
+
183
+ Save as `grok-advise.sh` and make it executable:
184
+
185
+ ```bash
186
+ #!/usr/bin/env bash
187
+ # CueLine advise-only Grok runner for live web and X search.
188
+ # argv: grok-advise.sh "<model>"; task arrives on stdin.
189
+ set -euo pipefail
190
+
191
+ MODEL="${1:-grok-4.5}"
192
+
193
+ TMP="$(mktemp)"
194
+ trap '{ rm -- "$TMP"; } 2>/dev/null || true' EXIT
195
+ cat > "$TMP"
196
+
197
+ # Keep the subscription login path when an API-key variable would override it.
198
+ unset XAI_API_KEY 2>/dev/null || true
199
+
200
+ grok --cwd "$PWD" --model "$MODEL" \
201
+ --no-memory --no-subagents --no-plan --no-alt-screen \
202
+ --output-format plain --verbatim --permission-mode plan \
203
+ --prompt-file "$TMP"
204
+ ```
205
+
206
+ Make both wrappers executable:
207
+
208
+ ```bash
209
+ chmod 700 /absolute/path/to/cueline-runners/gemini-advise.sh
210
+ chmod 700 /absolute/path/to/cueline-runners/grok-advise.sh
211
+ ```
212
+
213
+ ### Verify the routing file
214
+
215
+ Point `CUELINE_CONFIG` at the completed file and inspect it through CueLine's real routing surface:
216
+
217
+ ```bash
218
+ CUELINE_CONFIG=/absolute/path/to/routing.multimodel.json \
219
+ cueline routing --json
220
+ ```
221
+
222
+ The command validates the file and checks whether each candidate's `argv[0]` is executable in the current environment. A valid file has this output shape:
223
+
224
+ ```json
225
+ {
226
+ "version": "0.1.7",
227
+ "config": {
228
+ "path": "/absolute/path/to/routing.multimodel.json",
229
+ "valid": true
230
+ },
231
+ "availableLanes": 4,
232
+ "lanes": [
233
+ {
234
+ "name": "default",
235
+ "enabled": true,
236
+ "status": "available",
237
+ "selectedRunnerId": "codex-default"
238
+ }
239
+ ],
240
+ "findings": []
241
+ }
242
+ ```
243
+
244
+ The real `lanes` array contains one entry per configured lane. `availableLanes` may be less than four, and individual lanes may report `status: "unavailable"`, when a CLI or wrapper is not executable on the current `PATH` or at the configured absolute path. That is an availability result, not a schema-validation failure.
245
+
246
+ Use the same `CUELINE_CONFIG` value when creating and continuing the process run. Process execution still requires both `executor: "process"` and `allowProcessExecution: true`.
247
+
248
+ ## What the controller sees
249
+
250
+ With all four candidates available, the per-round routing instruction includes a line shaped like:
251
+
252
+ ```text
253
+ Available routing lanes: default [codex-default]; taste-final [claude-opus-4-8-advise]; long-context [gemini-3-1-pro-advise]; live-search [grok-4-5-advise-search].
254
+ ```
255
+
256
+ That is the complete routing menu exposed to ChatGPT Pro. The wrapper paths, CLI arguments, model flags, environment cleanup, and permission flags remain local.
@@ -48,6 +48,8 @@ The controller turn is recorded before sending it through the browser. The built
48
48
 
49
49
  The absence of `controller_response_received` proves only that the local runtime did not record a response. It does **not** prove that ChatGPT did not answer. `run_failed` therefore retains the safe error message, stage, exact request ID, submission state, and known conversation URL. CueLine never treats a missing response event as permission to resend an ambiguous prompt.
50
50
 
51
+ Submission checkpoints also bind the run, round, request ID, prompt hash, exact conversation URL, Pro model evidence, baseline user and assistant message counts, composer state, click-attempt state, and bounded DOM evidence. If the click throws, CueLine records the exception class and message plus any readable post-click DOM evidence before classification.
52
+
51
53
  ## Snapshot
52
54
 
53
55
  `snapshot.json` is a disposable materialized view of the event stream. CueLine writes a temporary file in the same directory and renames it over the destination. A snapshot records its state protocol, run ID, and last applied physical sequence. Loads replay the authoritative event stream from event 1 instead of trusting a snapshot that a retired owner could have replaced.
@@ -77,6 +79,34 @@ The run event log still records the controller-visible job transitions. A status
77
79
  - `controller_archive_pending` / `settle_controller_archive` means the controller run is already durably `complete`, but an explicitly enabled post-completion archive has not reached terminal proof. Only missing/released ownership may settle it. `archived`, `ambiguous`, and `failed` are terminal archive outcomes; never retry an `ambiguous` attempt.
78
80
  - `continueAllowed: false` forbids `continueCueLineRun`. A caller-work state may separately authorize only the exact claim/start/continue API action named by `safeNextAction`; it never authorizes a browser resend.
79
81
 
82
+ ## Operator-confirmed not-sent recovery
83
+
84
+ `possibly_sent` remains non-retryable by default. If the operator directly inspects the exact ChatGPT conversation and confirms that the original user message is absent, a separate append-only recovery path can authorize one retry:
85
+
86
+ ```bash
87
+ cueline run reconcile RUN_ID \
88
+ --request-id REQUEST_ID \
89
+ --not-sent-confirmed \
90
+ --conversation-url https://chatgpt.com/c/EXACT_CONVERSATION
91
+ ```
92
+
93
+ The command does not drive the browser or send a message. It requires exactly one pending controller turn and validates the exact run, conversation, round, request ID, persisted prompt hash, and Pro model checkpoint. It also refuses recovery if a matching response or a command for the same or a newer controller round was accepted. Success appends `controller_turn_not_sent_confirmed` and `controller_turn_abandoned` with reason `operator_confirmed_not_sent`; old events remain unchanged.
94
+
95
+ The next continuation creates exactly one new request ID for the same round. The regenerated prompt must match the abandoned checkpoint hash after substituting only the new request ID for the abandoned request ID. Any other content change records `CONTROLLER_NOT_SENT_PROMPT_MISMATCH` and freezes the run for manual review. A crash after the retry request is written resumes the same deterministic retry identity. Repeating the confirmation command is idempotent and never appends another confirmation, abandonment, or send.
96
+
97
+ Status exposes `operatorConfirmation`, `abandonedRequestId`, `retryRequestId`, `promptHash`, and `resendBlockedReason`. Before the retry, `safeNextAction` is `retry`; after submission it is the normal `observe`. If later DOM or response evidence shows that the abandoned request actually appeared, CueLine records `CONTROLLER_NOT_SENT_CONFIRMATION_CONFLICT` or `CONTROLLER_NOT_SENT_RESPONSE_CONFLICT`, changes `safeNextAction` to `manual_review`, and refuses automatic resend or command acceptance.
98
+
99
+ Existing manual-send path is distinct:
100
+
101
+ ```bash
102
+ cueline run reconcile RUN_ID \
103
+ --request-id REQUEST_ID \
104
+ --manual-send-confirmed \
105
+ --conversation-url https://chatgpt.com/c/EXACT_CONVERSATION
106
+ ```
107
+
108
+ `--manual-send-confirmed` says the original request was sent and must be observed under the same identity. `--not-sent-confirmed` says the original request definitely was not sent and must be abandoned. The flags are mutually exclusive.
109
+
80
110
  ## Continue behavior
81
111
 
82
112
  Always run `cueline run status <run-id> --json` before continuation. `continueCueLineRun` loads the exact `runId`, replays state as needed, and resumes the same persisted run. The public runtime also reuses the stored ChatGPT conversation URL unless an explicit compatible adapter is supplied.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cueline",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "A ChatGPT web conversation directs; CueLine validates each command and hands durable advice or explicitly claimed work to the current Codex.",
5
5
  "keywords": [
6
6
  "chatgpt",