cueline 0.1.6 → 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 (188) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +50 -0
  4. package/README.ja.md +77 -30
  5. package/README.ko.md +77 -30
  6. package/README.md +71 -29
  7. package/README.zh-CN.md +77 -30
  8. package/README.zh-TW.md +74 -29
  9. package/config/routing.schema.json +3 -0
  10. package/dist/src/api-caller-work.js +58 -14
  11. package/dist/src/api-caller-work.js.map +1 -1
  12. package/dist/src/api-contracts.d.ts +56 -0
  13. package/dist/src/api-controller-handoff.d.ts +5 -1
  14. package/dist/src/api-controller-handoff.js +178 -38
  15. package/dist/src/api-controller-handoff.js.map +1 -1
  16. package/dist/src/api-run-verification.d.ts +3 -0
  17. package/dist/src/api-run-verification.js +174 -0
  18. package/dist/src/api-run-verification.js.map +1 -0
  19. package/dist/src/api-runtime-lifecycle.d.ts +6 -1
  20. package/dist/src/api-runtime-lifecycle.js +97 -8
  21. package/dist/src/api-runtime-lifecycle.js.map +1 -1
  22. package/dist/src/api.d.ts +26 -4
  23. package/dist/src/api.js +111 -40
  24. package/dist/src/api.js.map +1 -1
  25. package/dist/src/browser/browser-adapter.d.ts +41 -0
  26. package/dist/src/browser/codex-iab/bootstrap.d.ts +21 -0
  27. package/dist/src/browser/codex-iab/bootstrap.js +174 -9
  28. package/dist/src/browser/codex-iab/bootstrap.js.map +1 -1
  29. package/dist/src/browser/codex-iab/chatgpt-client.d.ts +3 -0
  30. package/dist/src/browser/codex-iab/chatgpt-client.js +216 -70
  31. package/dist/src/browser/codex-iab/chatgpt-client.js.map +1 -1
  32. package/dist/src/browser/codex-iab/probe.d.ts +36 -0
  33. package/dist/src/browser/codex-iab/probe.js +217 -0
  34. package/dist/src/browser/codex-iab/probe.js.map +1 -0
  35. package/dist/src/browser/codex-iab/recovery-evidence.d.ts +1 -1
  36. package/dist/src/browser/codex-iab/recovery-evidence.js +7 -10
  37. package/dist/src/browser/codex-iab/recovery-evidence.js.map +1 -1
  38. package/dist/src/browser/codex-iab/selectors.d.ts +1 -0
  39. package/dist/src/browser/codex-iab/selectors.js +1 -0
  40. package/dist/src/browser/codex-iab/selectors.js.map +1 -1
  41. package/dist/src/browser/codex-iab/send-button.d.ts +5 -0
  42. package/dist/src/browser/codex-iab/send-button.js +62 -0
  43. package/dist/src/browser/codex-iab/send-button.js.map +1 -0
  44. package/dist/src/browser/codex-iab/submission-url.js +6 -15
  45. package/dist/src/browser/codex-iab/submission-url.js.map +1 -1
  46. package/dist/src/browser/codex-iab/tab-discovery.js +45 -12
  47. package/dist/src/browser/codex-iab/tab-discovery.js.map +1 -1
  48. package/dist/src/browser/codex-iab/timing-options.d.ts +1 -0
  49. package/dist/src/browser/codex-iab/timing-options.js +5 -0
  50. package/dist/src/browser/codex-iab/timing-options.js.map +1 -0
  51. package/dist/src/cli/health-commands.d.ts +2 -0
  52. package/dist/src/cli/health-commands.js +265 -0
  53. package/dist/src/cli/health-commands.js.map +1 -0
  54. package/dist/src/cli/io.d.ts +4 -0
  55. package/dist/src/cli/io.js +2 -0
  56. package/dist/src/cli/io.js.map +1 -0
  57. package/dist/src/cli/main.d.ts +1 -5
  58. package/dist/src/cli/main.js +187 -84
  59. package/dist/src/cli/main.js.map +1 -1
  60. package/dist/src/cli/observation-commands.d.ts +2 -0
  61. package/dist/src/cli/observation-commands.js +350 -0
  62. package/dist/src/cli/observation-commands.js.map +1 -0
  63. package/dist/src/cli/run-status-view.d.ts +87 -0
  64. package/dist/src/cli/run-status-view.js +121 -0
  65. package/dist/src/cli/run-status-view.js.map +1 -0
  66. package/dist/src/core/controller-command-execution.d.ts +2 -2
  67. package/dist/src/core/controller-command-execution.js +82 -16
  68. package/dist/src/core/controller-command-execution.js.map +1 -1
  69. package/dist/src/core/controller-conversation-archive.d.ts +11 -0
  70. package/dist/src/core/controller-conversation-archive.js +109 -0
  71. package/dist/src/core/controller-conversation-archive.js.map +1 -0
  72. package/dist/src/core/controller-loop.d.ts +7 -1
  73. package/dist/src/core/controller-loop.js +136 -23
  74. package/dist/src/core/controller-loop.js.map +1 -1
  75. package/dist/src/core/controller-turn.d.ts +21 -3
  76. package/dist/src/core/controller-turn.js +182 -41
  77. package/dist/src/core/controller-turn.js.map +1 -1
  78. package/dist/src/core/controller-types.d.ts +2 -0
  79. package/dist/src/core/conversation-url.d.ts +3 -0
  80. package/dist/src/core/conversation-url.js +34 -0
  81. package/dist/src/core/conversation-url.js.map +1 -0
  82. package/dist/src/core/run-status.d.ts +17 -2
  83. package/dist/src/core/run-status.js +64 -25
  84. package/dist/src/core/run-status.js.map +1 -1
  85. package/dist/src/core/state-machine.d.ts +36 -1
  86. package/dist/src/core/state-machine.js +242 -13
  87. package/dist/src/core/state-machine.js.map +1 -1
  88. package/dist/src/core/timing.d.ts +9 -0
  89. package/dist/src/core/timing.js +22 -0
  90. package/dist/src/core/timing.js.map +1 -0
  91. package/dist/src/diagnostics/run-doctor.d.ts +22 -0
  92. package/dist/src/diagnostics/run-doctor.js +197 -0
  93. package/dist/src/diagnostics/run-doctor.js.map +1 -0
  94. package/dist/src/jobs/status.d.ts +5 -2
  95. package/dist/src/jobs/status.js +172 -12
  96. package/dist/src/jobs/status.js.map +1 -1
  97. package/dist/src/jobs/supervisor.js +57 -8
  98. package/dist/src/jobs/supervisor.js.map +1 -1
  99. package/dist/src/observation/run-diff.d.ts +39 -0
  100. package/dist/src/observation/run-diff.js +67 -0
  101. package/dist/src/observation/run-diff.js.map +1 -0
  102. package/dist/src/observation/run-graph.d.ts +17 -0
  103. package/dist/src/observation/run-graph.js +77 -0
  104. package/dist/src/observation/run-graph.js.map +1 -0
  105. package/dist/src/observation/run-handoff.d.ts +86 -0
  106. package/dist/src/observation/run-handoff.js +309 -0
  107. package/dist/src/observation/run-handoff.js.map +1 -0
  108. package/dist/src/observation/run-status-at.d.ts +41 -0
  109. package/dist/src/observation/run-status-at.js +65 -0
  110. package/dist/src/observation/run-status-at.js.map +1 -0
  111. package/dist/src/observation/run-timeline.d.ts +32 -0
  112. package/dist/src/observation/run-timeline.js +279 -0
  113. package/dist/src/observation/run-timeline.js.map +1 -0
  114. package/dist/src/observation/run-watch.d.ts +15 -0
  115. package/dist/src/observation/run-watch.js +82 -0
  116. package/dist/src/observation/run-watch.js.map +1 -0
  117. package/dist/src/protocol/limits.d.ts +3 -0
  118. package/dist/src/protocol/limits.js +4 -0
  119. package/dist/src/protocol/limits.js.map +1 -0
  120. package/dist/src/protocol/lint-command.d.ts +24 -0
  121. package/dist/src/protocol/lint-command.js +210 -0
  122. package/dist/src/protocol/lint-command.js.map +1 -0
  123. package/dist/src/protocol/parse-command.js +4 -0
  124. package/dist/src/protocol/parse-command.js.map +1 -1
  125. package/dist/src/protocol/types.d.ts +12 -0
  126. package/dist/src/protocol/validate-command.js +76 -3
  127. package/dist/src/protocol/validate-command.js.map +1 -1
  128. package/dist/src/router/config-loader.js +23 -2
  129. package/dist/src/router/config-loader.js.map +1 -1
  130. package/dist/src/router/explain.d.ts +28 -0
  131. package/dist/src/router/explain.js +86 -0
  132. package/dist/src/router/explain.js.map +1 -0
  133. package/dist/src/router/resolver.d.ts +2 -1
  134. package/dist/src/router/resolver.js +11 -6
  135. package/dist/src/router/resolver.js.map +1 -1
  136. package/dist/src/runners/process-runner.js +55 -12
  137. package/dist/src/runners/process-runner.js.map +1 -1
  138. package/dist/src/runners/runner-adapter.d.ts +2 -0
  139. package/dist/src/runners/runner-adapter.js.map +1 -1
  140. package/dist/src/state/atomic-write.d.ts +6 -0
  141. package/dist/src/state/atomic-write.js +43 -2
  142. package/dist/src/state/atomic-write.js.map +1 -1
  143. package/dist/src/state/cancellation.js +38 -4
  144. package/dist/src/state/cancellation.js.map +1 -1
  145. package/dist/src/state/event-log.js +5 -4
  146. package/dist/src/state/event-log.js.map +1 -1
  147. package/dist/src/state/private-directory.d.ts +5 -0
  148. package/dist/src/state/private-directory.js +37 -0
  149. package/dist/src/state/private-directory.js.map +1 -0
  150. package/dist/src/state/runtime-lease.js +19 -39
  151. package/dist/src/state/runtime-lease.js.map +1 -1
  152. package/dist/src/state/runtime-record-validation.d.ts +6 -0
  153. package/dist/src/state/runtime-record-validation.js +44 -0
  154. package/dist/src/state/runtime-record-validation.js.map +1 -0
  155. package/dist/src/state/runtime-retirement.js +58 -18
  156. package/dist/src/state/runtime-retirement.js.map +1 -1
  157. package/dist/src/state/runtime-takeover-intent.js +2 -2
  158. package/dist/src/state/runtime-takeover-intent.js.map +1 -1
  159. package/dist/src/state/store.d.ts +1 -0
  160. package/dist/src/state/store.js +12 -6
  161. package/dist/src/state/store.js.map +1 -1
  162. package/dist/src/version.d.ts +1 -1
  163. package/dist/src/version.js +1 -1
  164. package/docs/architecture.md +4 -4
  165. package/docs/assets/README.md +3 -1
  166. package/docs/assets/cueline-architecture-en.svg +63 -0
  167. package/docs/assets/cueline-architecture-ja.svg +63 -0
  168. package/docs/assets/cueline-architecture-ko.svg +63 -0
  169. package/docs/assets/cueline-architecture-zh-CN.svg +63 -0
  170. package/docs/assets/cueline-architecture-zh-TW.svg +63 -0
  171. package/docs/assets/cueline-states-en.svg +50 -0
  172. package/docs/assets/cueline-states-ja.svg +48 -0
  173. package/docs/assets/cueline-states-ko.svg +48 -0
  174. package/docs/assets/cueline-states-zh-CN.svg +48 -0
  175. package/docs/assets/cueline-states-zh-TW.svg +48 -0
  176. package/docs/compatibility.md +1 -1
  177. package/docs/controller-protocol.md +44 -7
  178. package/docs/experiments/protocol-lint.md +40 -0
  179. package/docs/experiments/run-doctor.md +39 -0
  180. package/docs/experiments/run-handoff.md +39 -0
  181. package/docs/experiments/run-timeline.md +35 -0
  182. package/docs/experiments/run-watch.md +31 -0
  183. package/docs/multi-model-routing.md +256 -0
  184. package/docs/runner-contract.md +4 -3
  185. package/docs/state-and-recovery.md +45 -5
  186. package/package.json +1 -1
  187. package/schemas/controller-command.schema.json +89 -4
  188. package/skills/cueline/SKILL.md +13 -7
@@ -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>
@@ -54,7 +54,7 @@ The v0.1 adapter relies on accessible textbox/button roles, attachment chips, an
54
54
 
55
55
  ## CLI boundary
56
56
 
57
- `cueline doctor`, `routing`, `jobs`, `run status`, `api path`, and `config path` are read-only. `install`/`uninstall` change only the package-owned skill link. `run reconcile`, `run takeover`, `run reconcile-runtime`, `run cancel` / `run stop`, and `job cancel` append audit evidence or change local durable state; `cueline help` lists their exact positional syntax and options. None drives the ChatGPT page. `run reconcile --manual-send-confirmed --conversation-url URL` can atomically bind the first exact URL created by a manual send; the imported API still performs identity/Pro reconciliation through Codex's IAB browser object. `run takeover` retires only an exact stale owner/heartbeat and refuses a fresh active owner; every process run is directed through `reconcile-runtime` before continuation.
57
+ `cueline doctor`, `routing`, `jobs`, `runs`, `run status`, `run verify`, `api path`, and `config path` are read-only. `doctor --json` returns typed caller/process readiness and keeps invalid routing configuration failures parseable without echoing configuration content. `routing --json` reports each lane's enabled/available state and selected runner ID without exposing candidate argv or environment values. `install`/`uninstall` change only the package-owned skill link. `run reconcile`, `run takeover`, `run reconcile-runtime`, `run cancel` / `run stop`, and `job cancel` append audit evidence or change local durable state; `cueline help` lists their exact positional syntax and options. None drives the ChatGPT page. `run reconcile --manual-send-confirmed --conversation-url URL` can atomically bind the first exact URL created by a manual send; the imported API still performs identity/Pro reconciliation through Codex's IAB browser object. `run takeover` retires only an exact stale owner/heartbeat and refuses a fresh active owner; every process run is directed through `reconcile-runtime` before continuation.
58
58
 
59
59
  ## Live readiness checklist
60
60
 
@@ -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
@@ -28,14 +34,22 @@ A stale or mismatched value is rejected. CueLine parses only the **last complete
28
34
  "job_key": "review",
29
35
  "required": true,
30
36
  "status": "succeeded",
31
- "output": "Review evidence"
37
+ "output": "Review evidence",
38
+ "evidence_window": {
39
+ "field": "output",
40
+ "offset": 0,
41
+ "end": 15,
42
+ "total_chars": 15,
43
+ "next_offset": null,
44
+ "content_hash": "61f2be400c13cff37284a92d51a9e4c3445bf8bbf67b8b8e92e5e1e4e1b337de"
45
+ }
32
46
  }
33
47
  ],
34
48
  "notices": []
35
49
  }
36
50
  ```
37
51
 
38
- Job states are `pending`, `running`, `succeeded`, `failed`, `timed_out`, `cancelled`, or `ambiguous`. Successful jobs with non-empty stdout use stdout as controller evidence instead of a combined stdout/stderr stream. Failed and timed-out jobs retain bounded diagnostic error evidence. All job evidence in one controller observation shares a global 12,000-character budget; omitted content is reported once with the exact omitted count. After an accepted `inspect(job_ids)`, the named jobs receive that budget before unrelated jobs. Full stdout/stderr remain in local job status, and only the most recent 20 notices are sent. The local event log remains the recovery record.
52
+ Job states are `pending`, `running`, `succeeded`, `failed`, `timed_out`, `cancelled`, or `ambiguous`. Successful jobs with non-empty stdout use stdout as controller evidence instead of a combined stdout/stderr stream. Failed and timed-out jobs retain bounded diagnostic error evidence. All job evidence in one controller observation shares a global 12,000-character budget; omitted content is reported once with the exact omitted count. `evidence_window` identifies the preferred field and its raw-character `offset`, exclusive `end`, `total_chars`, deterministic `next_offset`, and SHA-256 `content_hash`. Full stdout/stderr remain in local job status, and only the most recent 20 notices are sent. The local event log remains the recovery record.
39
53
 
40
54
  ## Command envelope
41
55
 
@@ -54,6 +68,14 @@ Job states are `pending`, `running`, `succeeded`, `failed`, `timed_out`, `cancel
54
68
 
55
69
  Text outside the envelope is not executed. CueLine does not request or consume private chain-of-thought; concise user-facing rationale may stay outside the envelope.
56
70
 
71
+ Each action has an exact field set. Unknown top-level fields and fields defined for a different action are rejected before the command is accepted; CueLine never silently drops them and then executes a different interpretation.
72
+
73
+ One control envelope is limited to 131,072 characters before JSON parsing. A
74
+ single `dispatch` may contain at most 64 jobs, and `wait` or `inspect` may name
75
+ at most 256 job IDs. Omit `job_ids` to select all current jobs instead of
76
+ building an oversized reference list. Exceeding a bound requests a repaired
77
+ command with the same pending identity; no job is registered or started.
78
+
57
79
  ## Actions
58
80
 
59
81
  ### `dispatch`
@@ -81,19 +103,34 @@ Schedules one or more local jobs.
81
103
  }
82
104
  ```
83
105
 
84
- `job_key` must be unique inside the command and match the supported identifier form. `mode` is `advise` or `work`. Optional fields are `required`, `timeout_ms`, `runner`, `workdir`, and `background`. `runner_id` is invalid and produces an explicit correction to use `runner`. The local runtime—not ChatGPT—resolves the configured executable. `lane` must be a listed available lane; a runner ID is not a lane name. When `runner` is supplied, it must name an enabled, available candidate in the selected lane. CueLine validates every new route in the dispatch before registering or starting any job. One invalid route rejects the whole command and requests a corrected envelope with the same pending identity. Repeating an already persisted deterministic job is ignored rather than executed again.
106
+ `job_key` must be unique inside the command and match the supported identifier form. `mode` is `advise` or `work`. Optional fields are `required`, `timeout_ms`, `runner`, `workdir`, and `background`. `prompt` is invalid and produces an explicit correction to use `task`; `runner_id` likewise produces a correction to use `runner`. The local runtime—not ChatGPT—resolves the configured executable. `lane` must be a listed available lane; a runner ID is not a lane name. When `runner` is supplied, it must name an enabled, available candidate in the selected lane. CueLine validates every new route in the dispatch before registering or starting any job. One invalid route rejects the whole command and requests a corrected envelope with the same pending identity. Repeating an already persisted deterministic job is ignored rather than executed again.
85
107
 
86
108
  The default executor is `caller`. It persists pending `advise` jobs and returns them as `awaiting_caller`. A caller `work` job must include an absolute `workdir`; it returns as `awaiting_caller_work` without executing. The current Codex must acquire a claim bound to run/job/task hash/workdir/caller/fencing token, durably start it before mutation, heartbeat long work, and submit the result with the exact proof. A non-success after start is reported as `ambiguous`, not as proof that no side effect occurred. A `dispatch` alone never means local work started. The web controller has no local tool access or implicit knowledge of local paths/repository layout. Local evidence must include absolute paths, relevant code excerpts, exact code/error identifiers, and an explicit request for any missing local evidence.
87
109
 
88
- The `process` executor requires both `executor: "process"` and `allowProcessExecution: true`; each non-terminal continuation repeats the second authorization. Independent `advise` jobs run with a default global concurrency of two and a default per-lane limit of two (both configurable). If any job is `work`, the entire batch remains serial in command order.
110
+ The `process` executor requires both `executor: "process"` and `allowProcessExecution: true`; each non-terminal continuation repeats the second authorization. A supplied process `workdir` must be absolute. When omitted, CueLine writes the local runtime's resolved absolute workspace into the accepted command before hashing, registration, or execution. This keeps replay bound to the same directory after a restart. Independent `advise` jobs run with a default global concurrency of two and a default per-lane limit of two (both configurable). If any job is `work`, the entire batch remains serial in command order.
89
111
 
90
112
  ### `wait`
91
113
 
92
- Waits for selected running jobs (`job_ids`) or all current running jobs when omitted. `wait_ms` is part of the v0.1 schema but does not override the supervisor's job timeout.
114
+ Waits for selected running jobs (`job_ids`) or all current running jobs when omitted. When present, `job_ids` must be a non-empty, unique list of valid IDs that exactly match jobs in the current run. One unknown target rejects the whole command with `CONTROL_JOB_TARGET_UNKNOWN` before any wait begins. `wait_ms` is part of the v0.1 schema but does not override the supervisor's job timeout.
93
115
 
94
116
  ### `inspect`
95
117
 
96
- Asks CueLine to present the currently persisted job state on the next round. Named `job_ids` receive the bounded evidence budget before unrelated jobs, so a completed result is not reduced to status merely because earlier jobs consumed the prompt budget. It does not grant the web controller a new local inspection tool.
118
+ Asks CueLine to present the currently persisted job state on the next round. Named `job_ids` must form a non-empty, unique list that exactly matches jobs in the current run and receives the bounded evidence budget before unrelated jobs. One unknown target rejects the entire command before any selected job is inspected. To read a truncated tail, select exactly one job and copy its non-null `evidence_window.next_offset` verbatim:
119
+
120
+ ```json
121
+ {
122
+ "protocol": "cueline/0.1",
123
+ "run_id": "run-example",
124
+ "round": 3,
125
+ "request_id": "msg-example-3",
126
+ "action": "inspect",
127
+ "job_ids": ["job-example"],
128
+ "evidence_offset": 11842,
129
+ "evidence_hash": "61f2be400c13cff37284a92d51a9e4c3445bf8bbf67b8b8e92e5e1e4e1b337de"
130
+ }
131
+ ```
132
+
133
+ `evidence_offset` is a raw-character cursor from 0 through 1,000,000,000. It is valid only with one explicit job ID and the exact lowercase SHA-256 `evidence_hash` copied from that window's `content_hash`. CueLine validates and persists the pair, returns the next bounded window, and never reruns the job. If evidence changes before validation, the command is rejected for repair; if a process refresh changes it after validation, the next observation resets to offset 0 with a notice. An offset beyond unchanged evidence is clamped to the end and reported. This action does not grant the web controller a new local inspection tool.
97
134
 
98
135
  ### `complete`
99
136
 
@@ -105,6 +142,6 @@ Ends the run with a non-empty `reason` and optional `final_delivery_text`. Like
105
142
 
106
143
  ## Validation and repair
107
144
 
108
- CueLine rejects missing markers, malformed JSON, invalid actions, stale identity, invalid supported-field values, duplicate `job_key` values, and unavailable pre-spawn routes with stable error codes. Only the supported fields retained by runtime validation can affect execution. CueLine then sends a validation error back to the same conversation with the same pending identity. By default, two repair attempts are allowed; exhaustion fails the local run rather than guessing a command.
145
+ CueLine rejects missing markers, malformed JSON, invalid actions, stale identity, unknown or action-incompatible fields, invalid supported-field values, empty/duplicate/malformed `job_ids`, duplicate `job_key` values, and unavailable pre-spawn routes with stable error codes. Only the exact fields for the selected action can affect execution. CueLine then sends a validation error back to the same conversation with the same pending identity. By default, two repair attempts are allowed; exhaustion fails the local run rather than guessing a command.
109
146
 
110
147
  The JSON Schema files under `schemas/` are publication references. Runtime structural validation is implemented locally and does not require a schema package.
@@ -0,0 +1,40 @@
1
+ # Experiment: controller protocol lint
2
+
3
+ Branch: `codex/exp-protocol-lint`
4
+
5
+ ## Hypothesis
6
+
7
+ Known Pro contract mistakes should be found offline in one pass before they
8
+ consume another controller round or touch durable run state.
9
+
10
+ ## Surface
11
+
12
+ ```bash
13
+ cueline protocol lint response.txt \
14
+ --run-id run_... \
15
+ --round 3 \
16
+ --request-id msg_... \
17
+ --json
18
+ ```
19
+
20
+ The public API exports `lintControllerCommandText()`. It accepts either a raw
21
+ JSON object or the last complete `<CueLineControl>` envelope.
22
+
23
+ ## Checks unique to this experiment
24
+
25
+ - Exact run, round, and request identity.
26
+ - `prompt` versus required `task`.
27
+ - `runner_id` versus supported `runner`.
28
+ - A runner ID mistakenly placed in `lane`, using active routing evidence.
29
+ - Unknown top-level and job fields.
30
+ - Fields that exist in the protocol but belong to a different action.
31
+ - Required absolute `workdir` for caller `work`.
32
+ - Existing schema, duplicate-key, mode, timeout, and terminal-field validation.
33
+
34
+ Invalid JSON produces bounded diagnostics and does not echo the source. Linting
35
+ never rewrites, sends, accepts, or records a command.
36
+
37
+ ## Review decision
38
+
39
+ Merge if preventing wasted Pro repair rounds is worth a new offline CLI surface.
40
+ Reject if the runtime validator alone should remain the only contract authority.
@@ -0,0 +1,39 @@
1
+ # Experiment: run doctor
2
+
3
+ Branch: `codex/exp-run-doctor`
4
+
5
+ ## Hypothesis
6
+
7
+ A persisted CueLine run should explain *why* it is waiting or blocked without an
8
+ operator reconstructing state from `status`, `jobs`, leases, and event files.
9
+ The diagnosis must be read-only and must never turn ambiguous evidence into a
10
+ retry recommendation.
11
+
12
+ ## Surface
13
+
14
+ ```bash
15
+ cueline run doctor <run-id>
16
+ cueline run doctor <run-id> --json
17
+ ```
18
+
19
+ The public API exports `diagnoseCueLineRun()` for persisted runs and
20
+ `diagnoseCueLineRunStatus()` for already loaded status summaries.
21
+
22
+ Each result includes a stable outcome, phase, event sequence, safe next action,
23
+ and findings with machine-readable codes, bounded evidence, and an operator
24
+ action. A blocked diagnosis exits `1`; healthy and action-required diagnoses
25
+ exit `0` because the command itself completed successfully.
26
+
27
+ ## Safety properties
28
+
29
+ - Controller response pending means observe the exact turn and **do not resend**.
30
+ - Proposed caller work is distinguished from claimed or started work.
31
+ - Stale/unknown ownership, orphaned work, and ambiguous work are blockers.
32
+ - Timeout and failure are evidence for Pro, not implicit retry permission.
33
+ - Diagnosis does not append events, claim leases, cancel jobs, or drive a browser.
34
+
35
+ ## Review decision
36
+
37
+ Merge if a single causal report is more useful than manually correlating the
38
+ existing read-only commands. Reject if the project should keep all policy out of
39
+ diagnostics and expose only raw state.
@@ -0,0 +1,39 @@
1
+ # Experiment: safe run handoff
2
+
3
+ Branch: `codex/exp-run-handoff`
4
+
5
+ ## Hypothesis
6
+
7
+ A new Codex session should be able to resume one exact CueLine run from a single
8
+ read-only packet instead of reconstructing controller identity, paths, jobs, and
9
+ ownership from chat history.
10
+
11
+ ## Surface
12
+
13
+ ```bash
14
+ cueline run handoff <run-id>
15
+ cueline run handoff <run-id> --json
16
+ cueline run handoff <run-id> --include-content --max-content-chars 2000
17
+ ```
18
+
19
+ The public API exports `createCueLineRunHandoff()`,
20
+ `buildCueLineRunHandoff()`, and `renderCueLineRunHandoffMarkdown()`.
21
+
22
+ ## Default packet
23
+
24
+ - Exact run, round, event sequence, phase, executor, and safe next action.
25
+ - Exact ChatGPT conversation and pending request identities.
26
+ - Submission, Pro-label, composer, and write-ahead evidence.
27
+ - Absolute CueLine home, run directory, event log, snapshot, and lease paths.
28
+ - Job IDs, keys, modes, status, task hashes, workdirs, and claim/start flags.
29
+ - Concrete CLI/API continuation example and the warning that Pro has no tools.
30
+
31
+ The default packet contains no user request, controller prompt, task text,
32
+ stdout, stderr, job output, claim proof, or token. `--include-content` adds only
33
+ request/task/final text under one shared total character budget; prompt and
34
+ worker output remain excluded.
35
+
36
+ ## Review decision
37
+
38
+ Merge if cross-session recovery benefits from a canonical local packet. Reject
39
+ if all consumers should reconstruct handoff context directly from public APIs.
@@ -0,0 +1,35 @@
1
+ # Experiment: sanitized run timeline
2
+
3
+ Branch: `codex/exp-run-timeline`
4
+
5
+ ## Hypothesis
6
+
7
+ Operators need an auditable chronological view without reading raw event
8
+ payloads that can contain controller prompts, tasks, stdout, stderr, or local
9
+ secrets.
10
+
11
+ ## Surface
12
+
13
+ ```bash
14
+ cueline run timeline <run-id> [--after <sequence>] [--limit <1..1000>] [--json]
15
+ ```
16
+
17
+ The public API exports `loadCueLineRunTimeline()` and the pure
18
+ `buildCueLineRunTimeline()` formatter.
19
+
20
+ ## Safety properties
21
+
22
+ - Pagination uses an exclusive durable event-sequence cursor.
23
+ - A cursor ahead of the run is rejected instead of returning a false empty page.
24
+ - Entries expose only allowlisted identity/status/model metadata.
25
+ - Prompt, request, task, output, error text, URL, and raw owner ID are excluded.
26
+ - Runtime owner IDs become 12-character one-way fingerprints.
27
+ - Payloads become canonical SHA-256 hashes for correlation, never raw JSON.
28
+ - Event types use an explicit known allowlist; unknown types are redacted.
29
+ - Invalid timestamps are replaced with `null`.
30
+ - Timeline reads never append events or claim ownership.
31
+
32
+ ## Review decision
33
+
34
+ Merge if a safe audit view is worth maintaining an event-type allowlist. Reject
35
+ if direct local event-log access is preferred despite its disclosure risk.
@@ -0,0 +1,31 @@
1
+ # Experiment: bounded run watch
2
+
3
+ Branch: `codex/exp-run-watch`
4
+
5
+ ## Hypothesis
6
+
7
+ Long controller runs should be observed through a short, cursor-based read call,
8
+ not by keeping one foreground tool call and runtime lease alive for minutes.
9
+
10
+ ## Surface
11
+
12
+ ```bash
13
+ cueline run watch <run-id> --after <event-sequence> [--timeout-ms <0..30000>] [--json]
14
+ ```
15
+
16
+ The public API exports `waitForCueLineRunChange()`. It returns `changed`,
17
+ `terminal`, or `timed_out` with the latest full run status. The default timeout
18
+ is five seconds and the hard maximum is thirty seconds.
19
+
20
+ ## Safety properties
21
+
22
+ - The observer never claims a runtime lease or appends an event.
23
+ - A cursor ahead of durable state is rejected instead of waiting forever.
24
+ - Terminal state returns immediately even when no newer event exists.
25
+ - Abort and timeout do not change ownership, retry a turn, or cancel work.
26
+ - Every follow-up call is independently recoverable from its sequence cursor.
27
+
28
+ ## Review decision
29
+
30
+ Merge if short polling with a durable cursor better fits Codex tool lifetimes.
31
+ Reject if consumers should implement polling entirely outside CueLine.
@@ -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.