cueline 0.1.5 → 0.1.6

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 (65) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +2 -2
  3. package/CHANGELOG.md +22 -0
  4. package/README.ja.md +27 -13
  5. package/README.ko.md +27 -13
  6. package/README.md +40 -15
  7. package/README.zh-CN.md +29 -13
  8. package/README.zh-TW.md +41 -16
  9. package/config/routing.default.json +1 -0
  10. package/dist/src/api-caller-work.d.ts +18 -0
  11. package/dist/src/api-caller-work.js +397 -0
  12. package/dist/src/api-caller-work.js.map +1 -0
  13. package/dist/src/api-contracts.d.ts +37 -0
  14. package/dist/src/api-controller-handoff.d.ts +2 -2
  15. package/dist/src/api-controller-handoff.js +82 -7
  16. package/dist/src/api-controller-handoff.js.map +1 -1
  17. package/dist/src/api-runtime-lifecycle.js +6 -1
  18. package/dist/src/api-runtime-lifecycle.js.map +1 -1
  19. package/dist/src/api.d.ts +3 -1
  20. package/dist/src/api.js +32 -3
  21. package/dist/src/api.js.map +1 -1
  22. package/dist/src/browser/codex-iab/bootstrap.js +35 -1
  23. package/dist/src/browser/codex-iab/bootstrap.js.map +1 -1
  24. package/dist/src/cli/main.js +13 -2
  25. package/dist/src/cli/main.js.map +1 -1
  26. package/dist/src/core/controller-command-execution.js +26 -2
  27. package/dist/src/core/controller-command-execution.js.map +1 -1
  28. package/dist/src/core/controller-loop.js +62 -17
  29. package/dist/src/core/controller-loop.js.map +1 -1
  30. package/dist/src/core/controller-turn.js +5 -0
  31. package/dist/src/core/controller-turn.js.map +1 -1
  32. package/dist/src/core/controller-types.d.ts +3 -1
  33. package/dist/src/core/ids.d.ts +1 -0
  34. package/dist/src/core/ids.js +3 -0
  35. package/dist/src/core/ids.js.map +1 -1
  36. package/dist/src/core/run-status.d.ts +30 -3
  37. package/dist/src/core/run-status.js +132 -4
  38. package/dist/src/core/run-status.js.map +1 -1
  39. package/dist/src/core/state-machine.d.ts +29 -1
  40. package/dist/src/core/state-machine.js +199 -2
  41. package/dist/src/core/state-machine.js.map +1 -1
  42. package/dist/src/jobs/status.d.ts +5 -0
  43. package/dist/src/jobs/status.js.map +1 -1
  44. package/dist/src/jobs/supervisor.js +32 -6
  45. package/dist/src/jobs/supervisor.js.map +1 -1
  46. package/dist/src/router/materialize.js +1 -0
  47. package/dist/src/router/materialize.js.map +1 -1
  48. package/dist/src/runners/process-runner.js +54 -0
  49. package/dist/src/runners/process-runner.js.map +1 -1
  50. package/dist/src/runners/runner-adapter.d.ts +9 -0
  51. package/dist/src/runners/runner-adapter.js.map +1 -1
  52. package/dist/src/version.d.ts +1 -1
  53. package/dist/src/version.js +1 -1
  54. package/docs/architecture.md +8 -7
  55. package/docs/assets/cueline-loop-en.svg +1 -1
  56. package/docs/assets/cueline-loop-ja.svg +1 -1
  57. package/docs/assets/cueline-loop-ko.svg +1 -1
  58. package/docs/assets/cueline-loop-zh-CN.svg +1 -1
  59. package/docs/assets/cueline-loop-zh-TW.svg +1 -1
  60. package/docs/compatibility.md +7 -7
  61. package/docs/controller-protocol.md +4 -4
  62. package/docs/runner-contract.md +20 -7
  63. package/docs/state-and-recovery.md +11 -6
  64. package/package.json +2 -2
  65. package/skills/cueline/SKILL.md +48 -8
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cueline",
3
- "version": "0.1.5",
4
- "description": "Use a ChatGPT web conversation as the text controller for durable local advice executed by the current Codex.",
3
+ "version": "0.1.6",
4
+ "description": "Use a ChatGPT web conversation as the text controller for durable local advice or explicitly claimed work executed by the current Codex.",
5
5
  "author": {
6
6
  "name": "CueLine contributors"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cueline",
3
- "version": "0.1.5",
4
- "description": "Use a ChatGPT web conversation as the text controller for durable local advice executed by the current Codex.",
3
+ "version": "0.1.6",
4
+ "description": "Use a ChatGPT web conversation as the text controller for durable local advice or explicitly claimed work executed by the current Codex.",
5
5
  "author": {
6
6
  "name": "CueLine contributors"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.6 - 2026-07-15
4
+
5
+ ### Added
6
+
7
+ - Add a durable caller-work claim protocol for local mutations. A `work` dispatch in caller mode now pauses as `caller_work_pending`; `claimCueLineCallerJob`, `startCueLineCallerJob`, heartbeat, release, and result submission bind the exact run, job, task hash, absolute workdir, caller identity, expiry, and monotonically increasing fencing token. An unstarted expired claim is safely reclaimable; started work that loses its claim becomes `ambiguous` and is never automatically retried.
8
+ - Expose separate controller, caller-work, and process-execution status surfaces. Process status includes the resolved runner, model, provider, PID, phase, and last-progress timestamp; model/provider discovery keeps the first valid Codex header instead of allowing later untrusted job output to spoof it. Caller work reports pending, claimed, and running phases without pretending that Pro used local tools.
9
+
10
+ ### Fixed
11
+
12
+ - Ignore hidden, disabled, inert, ancestor-hidden, zero-geometry, or otherwise non-actionable residual `Stop answering` buttons. A completed Pro response can now reconcile even when ChatGPT retains a hidden stop control in the DOM.
13
+ - Preserve exact `inspect(job_ids)` targets and allocate the bounded controller-evidence budget to those jobs first, so Pro receives the requested stdout instead of repeatedly seeing only terminal status.
14
+ - Recover only one precisely fenced, normally submitted caller observation after an outer timeout, and release every observation lease in `finally`. Ambiguous/manual/multiple turns, URL mismatches, jobs, commands, and cancellations remain in explicit reconciliation paths.
15
+ - Make caller-work result submission crash-recoverable with a durable result-intent event; reject clock regression, invalid or reversed timestamps, forged or out-of-order claims, duplicate terminal results, and release-after-start. Any non-success result after work starts is normalized to `ambiguous`.
16
+ - Require both `executor: "process"` and `allowProcessExecution: true` at run creation and every nonterminal continuation. Process work never exposes a caller-claim surface, and a failing diagnostic progress hook cannot break process supervision.
17
+ - Launch the bundled `codex-default` process route with `--ignore-user-config`, preventing hidden inheritance of user-configured MCP commands and their process arguments. The runner still uses registered argv directly without a shell.
18
+ - Prefer the current injected built-in Browser binding before fallback discovery, while keeping all controller observation read-only and never invoking `Answer now`, `Respond now`, `Stop`, or equivalent interruption controls.
19
+
20
+ ### Verification
21
+
22
+ - Added adversarial coverage for hidden stop controls and hidden ancestors, inspected-output prioritization, stale observer fencing, caller claim races/restarts/expiry/result crashes/clock attacks, process double authorization, caller-vs-process status separation, diagnostic-hook failure, and bundled route isolation.
23
+ - Verified the real ChatGPT Web Pro path with a new conversation and new caller run: each prompt was sent once, no process or work job was started, bounded local evidence was returned through caller advice, and Pro was allowed to finish every reasoning turn without interruption. The terminal Pro verdict was `complete`, with no additional evidence requested for the five scoped blockers.
24
+
3
25
  ## 0.1.5 - 2026-07-15
4
26
 
5
27
  ### Fixed
package/README.ja.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  **CueLine は、開いている ChatGPT のウェブ会話に判断を任せます。会話側はテキストコマンドを出し、CueLine が検証し、現在の Codex が許可されたローカル作業を実行します。**
15
15
 
16
- ウェブページにローカルツールはありません。既定の `caller` 実行では、CueLine が `advise` ジョブを永続化し、現在の Codex が実行して結果を提出します。登録済みワーカーを起動するには `process` executor を明示します。
16
+ ウェブページにローカルツールはありません。既定の `caller` 実行では、`advise` は協調用の引き渡し、`work` は永続 claim と start が必要です。登録済みワーカーを起動する process executor には二重の明示的承認が必要です。
17
17
 
18
18
  CueLine は独立した実装で、**ランタイムの npm 依存はゼロ**です。Omnilane や GPT Relay のラッパーではありません。
19
19
 
@@ -25,9 +25,9 @@ CueLine は独立した実装で、**ランタイムの npm 依存はゼロ**で
25
25
 
26
26
  既定値以外の `maxRounds` は run 作成時に固定され、owner 不在の一時停止をまたいでコントローラーの総ラウンド数を数えます。後の続行では通常省略して永続値を再利用し、異なる値を渡すと予算を暗黙にリセットまたは拡張せず拒否します。
27
27
 
28
- `startCueLineRun` と `runCueLine` の既定は `caller` です。組み込みブラウザーでは、CueLine 1 回だけ送信して正確な会話 URL を保存し、Pro の思考中に 1 回のツール呼び出しを保持せず、runtime lease を解放して `awaiting_controller` を返します。後の `continueCueLineRun` は読み取り専用の観測を 1 回だけ行い、未完了なら再送せず再び `awaiting_controller` を返します。`dispatch` の後に `awaiting_caller` を返し、現在の Codex が各 `advise` を実行して `submitCueLineCallerJobResult` で提出し、同じ run を続行します。Pro コントローラー自身がローカルツールを使ったわけではありません。Caller advice には execution claim がないため、1 つの session が実行するよう調整してください。2 つの session が同じ確認を行う可能性はありますが、最初に提出された終端証拠だけが採用されます。Caller の `work` は拒否されます。
28
+ `startCueLineRun` と `runCueLine` の既定は `caller` です。送信後は `awaiting_controller` を返して lease を解放し、続行は 1 回の読み取り専用観測だけを行い、再送しません。`advise` `awaiting_caller`、`work` `awaiting_caller_work` を返します。work は現在の Codex `claimCueLineCallerJob` `startCueLineCallerJob` を成功させるまで開始されません。claim run、job、task hash、絶対 workdir、caller identity、fencing token に結び付けられ、開始済み work は自動再試行されず、期限切れなら `ambiguous` になります。Pro はテキスト命令を提案・審査するだけで、ローカルツールは使いません。
29
29
 
30
- コントローラーは*何が起こるべきか*を選びます。ローカル側は*それを許すか、どう許すか*を選びます。レーンが有効であること、候補がプロセス起動の**前に**利用可能だと確認されていること、`argv[0]` があなたのルーティング設定によってすでに登録されていること。シェルを経由するものは何もありません。ワーカーがいったん起動したら、黙って 2 番目の候補にフォールバックすることはありません。失敗は再試行ではなく、証拠として返ります。
30
+ Process モードは `executor: "process"` と `allowProcessExecution: true` の両方が必要で、非終端の続行でも第 2 の承認を再度渡します。組み込み route はさらに `--ignore-user-config` を使い、隠れた worker がユーザー設定の MCP server やそのコマンド引数を読み込まないようにします。レーンと候補は起動前に検証され、シェルも起動後の自動フォールバックも使いません。
31
31
 
32
32
  コントローラープロトコルでは、ルーティングの階層を明確に区別します。`lane` に指定するのはレーン名の `default` であり、`codex-default` はそのレーン内の候補ランナーであって、レーンではありません。CueLine はジョブを一つでも登録する前に `dispatch` 全体を検証します。無効なレーンまたはランナーが一つでもあれば、途中まで実行せず、`dispatch` 全体を修正のために差し戻します。
33
33
 
@@ -48,15 +48,15 @@ ChatGPT Pro のサブスクリプションと、選択された Pro モデルは
48
48
  npm レジストリからインストールします。
49
49
 
50
50
  ```bash
51
- npm install -g cueline@0.1.5
51
+ npm install -g cueline@0.1.6
52
52
  cueline install
53
53
  cueline doctor
54
54
  ```
55
55
 
56
- フォールバックとして、[v0.1.5 リリース](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.5) のパッケージ済み tarball をインストールすることもできます。同じリリースに `.sha256` チェックサムも置いてあります。
56
+ フォールバックとして、[v0.1.6 リリース](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.6) のパッケージ済み tarball をインストールすることもできます。同じリリースに `.sha256` チェックサムも置いてあります。
57
57
 
58
58
  ```bash
59
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.5/cueline-0.1.5.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.6/cueline-0.1.6.tgz
60
60
  cueline install
61
61
  cueline doctor
62
62
  ```
@@ -89,23 +89,26 @@ cueline doctor
89
89
 
90
90
  ```js
91
91
  import {
92
+ claimCueLineCallerJob,
92
93
  continueCueLineRun,
93
94
  createCodexIabAdapter,
95
+ heartbeatCueLineCallerJob,
94
96
  runCueLine,
97
+ startCueLineCallerJob,
95
98
  submitCueLineCallerJobResult,
96
99
  } from "cueline";
97
100
 
98
101
  let result = await runCueLine({
99
102
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
100
- browser: createCodexIabAdapter(),
103
+ browser: createCodexIabAdapter({ browser: globalThis.browser }),
101
104
  // 任意:conversationUrl、routingConfig / routingConfigPath、home、cwd、
102
105
  // runTimeoutMs、signal、ジョブごと/既定の期限。
103
106
  }); // 既定は executor: "caller"
104
107
 
105
- while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
108
+ while (["awaiting_controller", "awaiting_caller", "awaiting_caller_work"].includes(result.status)) {
106
109
  if (result.status === "awaiting_controller") {
107
110
  await waitBeforeNextObservation(); // 有界バックオフ。再送しない
108
- } else {
111
+ } else if (result.status === "awaiting_caller") {
109
112
  for (const job of result.pendingJobs ?? []) {
110
113
  const stdout = await executeExactLocalAdvice(job.spec.task);
111
114
  await submitCueLineCallerJobResult(result.runId, job.jobId, {
@@ -113,6 +116,17 @@ while (result.status === "awaiting_controller" || result.status === "awaiting_ca
113
116
  stdout,
114
117
  });
115
118
  }
119
+ } else {
120
+ for (const job of result.pendingJobs ?? []) {
121
+ if (job.spec.mode !== "work") continue;
122
+ const claim = await claimCueLineCallerJob(result.runId, job.jobId, { callerId: "stable-codex-task-identity" });
123
+ const proof = { claimId: claim.claimId, callerId: claim.callerId, fencingToken: claim.fencingToken };
124
+ await startCueLineCallerJob(result.runId, job.jobId, proof);
125
+ const stdout = await executeExactLocalWork(job.spec.task, claim.workdir, {
126
+ heartbeat: () => heartbeatCueLineCallerJob(result.runId, job.jobId, proof),
127
+ });
128
+ await submitCueLineCallerJobResult(result.runId, job.jobId, { status: "succeeded", stdout }, { claim: proof });
129
+ }
116
130
  }
117
131
  result = await continueCueLineRun({ runId: result.runId });
118
132
  }
@@ -122,7 +136,7 @@ if (result.status === "complete") {
122
136
  }
123
137
  ```
124
138
 
125
- `awaiting_controller` が返った場合、同じ正確な request は送信済みですが、Pro の応答はまだ観測されていません。後の続行は読み取り専用で観測し、再送しません。`awaiting_caller` が返ったら、現在の Codex `result.pendingJobs` の各 `advise` を実行し、実際の結果を `submitCueLineCallerJobResult` で提出してから `continueCueLineRun` を呼びます。Pro のウェブページがローカルツールを直接使うわけではありません。
139
+ `awaiting_controller` は再送なしの読み取り専用観測、`awaiting_caller` advise の引き渡し、`awaiting_caller_work` claim、start、実行、heartbeat、claim proof 付き提出の順です。Pro はローカルツールを直接使いません。
126
140
 
127
141
  Codex のランタイムでは、`cueline api path` が出力する絶対パスのモジュールを import します。それがインストールしたパッケージのビルド済み API です。
128
142
 
@@ -137,7 +151,7 @@ $ cueline install
137
151
  CueLine skill installed: /Users/you/.codex/skills/cueline
138
152
 
139
153
  $ cueline doctor
140
- CueLine 0.1.5
154
+ CueLine 0.1.6
141
155
  status ok
142
156
  node 22.14.0 ok
143
157
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
@@ -179,7 +193,7 @@ Node が古すぎる場合、または有効な caller レーンが一つもな
179
193
 
180
194
  `CUELINE_CONFIG` はルーティング設定ファイルを選び、`CUELINE_HOME` はローカル状態の置き場所を移します(既定は `~/.cueline`)。
181
195
 
182
- Caller はプロセスを起動しません。`process` executor を明示した場合だけ、`default` レーンの `codex-default` `codex exec` を実行します。独立した `advise` の既定同時実行数は全体/レーンごとに 2、`work` を含むバッチは直列です。
196
+ Caller はプロセスを起動しません。`executor: "process"` `allowProcessExecution: true` を同時に指定した場合だけ、`default` レーンの `codex-default` が隔離された `codex exec --ignore-user-config` を実行します。独立した `advise` の既定同時実行数は全体/レーンごとに 2、`work` を含むバッチは直列です。
183
197
 
184
198
  状態は `CUELINE_HOME` の下に置かれます:
185
199
 
@@ -212,7 +226,7 @@ npm pack --dry-run
212
226
 
213
227
  ## 0.1 の制限
214
228
 
215
- テキストコマンドのみ。長文の自動添付変換は対応しますが、意図的なファイルアップロード、画像、Deep Research、Projects、Apps は非対応です。Caller は `advise` のみで、`work` には明示的な process executor が必要です。曖昧な送信や開始済みジョブを自動再試行しません。
229
+ テキストコマンドのみ。長文の自動添付変換は対応しますが、意図的なファイルアップロード、画像、Deep Research、Projects、Apps は非対応です。Caller work は明示的な claim/start、process 実行は二重承認が必要です。曖昧な送信や開始済みジョブを自動再試行しません。
216
230
 
217
231
  完全な対応表は [compatibility](docs/compatibility.md) を参照してください。
218
232
 
package/README.ko.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  **CueLine은 열린 ChatGPT 웹 대화에 판단을 맡깁니다. 대화는 텍스트 명령을 내리고, CueLine이 검증하며, 현재 Codex가 허용된 로컬 작업을 수행합니다.**
15
15
 
16
- 웹 페이지에는 로컬 도구가 없습니다. 기본 `caller` 실행에서는 CueLine이 `advise` 작업을 지속 상태로 넘기고 현재 Codex실행해 결과를 제출합니다. 등록된 워커를 띄우려면 `process` executor 명시해야 합니다.
16
+ 웹 페이지에는 로컬 도구가 없습니다. 기본 `caller` 실행에서 `advise`는 조정용 인계이며, `work`는 지속 claim과 start필요합니다. 등록된 워커를 띄우는 process executor 이중 명시 승인이 필요합니다.
17
17
 
18
18
  CueLine은 독립적인 구현이며 **런타임 npm 의존성이 전혀 없습니다**. Omnilane이나 GPT Relay를 감싼 래퍼가 아닙니다.
19
19
 
@@ -25,9 +25,9 @@ CueLine은 독립적인 구현이며 **런타임 npm 의존성이 전혀 없습
25
25
 
26
26
  기본값이 아닌 `maxRounds`는 run 생성 시 고정되며 owner가 없는 일시 중지를 가로질러 컨트롤러 총 라운드 수를 셉니다. 이후 계속하기에서는 보통 생략해 지속 값을 재사용하고, 다른 값을 전달하면 예산을 몰래 재설정하거나 늘리지 않고 거부합니다.
27
27
 
28
- `startCueLineRun`과 `runCueLine`의 기본값은 `caller`입니다. 내장 브라우저에서는 CueLine이 번만 전송하고 정확한 대화 URL을 저장한 뒤, Pro가 생각하는 동안 한 도구 호출을 붙잡지 않고 runtime lease를 해제하여 `awaiting_controller`를 반환합니다. 이후 `continueCueLineRun`은 읽기 전용 관측을 한 번만 수행하며, 미완료이면 재전송 없이 다시 `awaiting_controller`를 반환합니다. `dispatch` 뒤에는 `awaiting_caller`를 반환하고, 현재 Codex가 `advise`를 실행해 `submitCueLineCallerJobResult`로 제출한 같은 run 계속합니다. Pro 컨트롤러 자체가 로컬 도구를 사용한 것은 아닙니다. Caller advice에는 execution claim이 없으므로 session만 실행하도록 조정해야 합니다. session이 같은 점검을 수행할 있지만, 먼저 제출된 종료 증거만 채택됩니다. Caller의 `work`는 거부됩니다.
28
+ `startCueLineRun`과 `runCueLine`의 기본값은 `caller`입니다. 전송 `awaiting_controller`를 반환하고 lease를 해제하며, 계속하기는 재전송 없이 읽기 전용 관측 한 번만 수행합니다. `advise`는 `awaiting_caller`, `work`는 `awaiting_caller_work`를 반환합니다. work는 현재 Codex가 `claimCueLineCallerJob`과 `startCueLineCallerJob`을 성공시키기 전에는 시작되지 않습니다. claim은 run, job, task hash, 절대 workdir, caller identity, fencing token에 묶이며 시작된 work는 자동 재시도되지 않고 만료 `ambiguous`가 됩니다. Pro는 텍스트 명령을 제안하고 검토할 로컬 도구를 쓰지 않습니다.
29
29
 
30
- 컨트롤러는 *무엇이 일어나야 하는지*를 고릅니다. 로컬 쪽은 *그것이 허용되는지, 어떻게 허용되는지*를 고릅니다. 레인이 활성화되어 있어야 하고, 후보는 어떤 프로세스가 뜨기 **전에** 사용 가능함이 확인되어야 하며, `argv[0]`은 당신의 라우팅 설정에 이미 등록되어 있어야 합니다. 셸을 거치는 것은 아무것도 없습니다. 워커가 일단 시작되면 두 번째 후보로 조용히 넘어가는 폴백은 없습니다. 실패는 재시도가 아니라 증거로 돌아옵니다.
30
+ Process 모드는 `executor: "process"`와 `allowProcessExecution: true`가 모두 필요하며, 비종료 계속하기에서도 번째 승인을 다시 전달해야 합니다. 번들 route는 `--ignore-user-config`도 사용하므로 숨은 worker가 사용자 설정 MCP server나 명령 인자를 로드하지 않습니다. 레인과 후보는 시작 전에 검증되고 셸이나 시작 자동 폴백은 사용하지 않습니다.
31
31
 
32
32
  컨트롤러 프로토콜은 라우팅 계층을 명확히 구분합니다. `lane`에는 레인 이름인 `default`를 써야 하며, `codex-default`는 그 레인 안의 후보 러너이지 레인이 아닙니다. CueLine은 작업을 하나라도 등록하기 전에 `dispatch` 전체를 검증합니다. 잘못된 레인이나 러너가 하나라도 있으면 일부를 먼저 실행하지 않고 `dispatch` 전체를 수정하도록 돌려보냅니다.
33
33
 
@@ -48,15 +48,15 @@ ChatGPT Pro 구독과 선택된 Pro 모델은 서로 다른 것입니다. 계정
48
48
  npm 레지스트리에서 설치합니다:
49
49
 
50
50
  ```bash
51
- npm install -g cueline@0.1.5
51
+ npm install -g cueline@0.1.6
52
52
  cueline install
53
53
  cueline doctor
54
54
  ```
55
55
 
56
- 대안으로, [v0.1.5 릴리스](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.5)의 패키지 tarball을 설치할 수도 있습니다. 같은 릴리스에 `.sha256` 체크섬도 함께 있습니다.
56
+ 대안으로, [v0.1.6 릴리스](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.6)의 패키지 tarball을 설치할 수도 있습니다. 같은 릴리스에 `.sha256` 체크섬도 함께 있습니다.
57
57
 
58
58
  ```bash
59
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.5/cueline-0.1.5.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.6/cueline-0.1.6.tgz
60
60
  cueline install
61
61
  cueline doctor
62
62
  ```
@@ -89,23 +89,26 @@ cueline doctor
89
89
 
90
90
  ```js
91
91
  import {
92
+ claimCueLineCallerJob,
92
93
  continueCueLineRun,
93
94
  createCodexIabAdapter,
95
+ heartbeatCueLineCallerJob,
94
96
  runCueLine,
97
+ startCueLineCallerJob,
95
98
  submitCueLineCallerJobResult,
96
99
  } from "cueline";
97
100
 
98
101
  let result = await runCueLine({
99
102
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
100
- browser: createCodexIabAdapter(),
103
+ browser: createCodexIabAdapter({ browser: globalThis.browser }),
101
104
  // 선택: conversationUrl, routingConfig / routingConfigPath, home, cwd,
102
105
  // runTimeoutMs, signal, 작업별/기본 제한 시간.
103
106
  }); // 기본 executor: "caller"
104
107
 
105
- while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
108
+ while (["awaiting_controller", "awaiting_caller", "awaiting_caller_work"].includes(result.status)) {
106
109
  if (result.status === "awaiting_controller") {
107
110
  await waitBeforeNextObservation(); // 제한된 백오프. 재전송 금지
108
- } else {
111
+ } else if (result.status === "awaiting_caller") {
109
112
  for (const job of result.pendingJobs ?? []) {
110
113
  const stdout = await executeExactLocalAdvice(job.spec.task);
111
114
  await submitCueLineCallerJobResult(result.runId, job.jobId, {
@@ -113,6 +116,17 @@ while (result.status === "awaiting_controller" || result.status === "awaiting_ca
113
116
  stdout,
114
117
  });
115
118
  }
119
+ } else {
120
+ for (const job of result.pendingJobs ?? []) {
121
+ if (job.spec.mode !== "work") continue;
122
+ const claim = await claimCueLineCallerJob(result.runId, job.jobId, { callerId: "stable-codex-task-identity" });
123
+ const proof = { claimId: claim.claimId, callerId: claim.callerId, fencingToken: claim.fencingToken };
124
+ await startCueLineCallerJob(result.runId, job.jobId, proof);
125
+ const stdout = await executeExactLocalWork(job.spec.task, claim.workdir, {
126
+ heartbeat: () => heartbeatCueLineCallerJob(result.runId, job.jobId, proof),
127
+ });
128
+ await submitCueLineCallerJobResult(result.runId, job.jobId, { status: "succeeded", stdout }, { claim: proof });
129
+ }
116
130
  }
117
131
  result = await continueCueLineRun({ runId: result.runId });
118
132
  }
@@ -122,7 +136,7 @@ if (result.status === "complete") {
122
136
  }
123
137
  ```
124
138
 
125
- `awaiting_controller`가 반환되면 같은 정확한 request는 이미 전송되었지만 Pro 응답은 아직 관측되지 않은 것입니다. 이후 계속하기는 읽기 전용으로 관측하며 재전송하지 않습니다. `awaiting_caller`가 반환되면 현재 Codex가 `result.pendingJobs`의 `advise`를 실행하고 실제 결과를 `submitCueLineCallerJobResult`로 제출한 `continueCueLineRun`을 호출합니다. Pro 웹 페이지가 로컬 도구를 직접 쓰는 것은 아닙니다.
139
+ `awaiting_controller`는 재전송 없는 읽기 전용 관측, `awaiting_caller`는 advise 인계, `awaiting_caller_work`는 claim, start, 실행, heartbeat, claim proof 제출 순서입니다. Pro 로컬 도구를 직접 쓰지 않습니다.
126
140
 
127
141
  Codex 런타임에서는 `cueline api path`가 출력하는 절대 경로 모듈을 import하세요. 그것이 설치한 패키지의 빌드된 API입니다.
128
142
 
@@ -137,7 +151,7 @@ $ cueline install
137
151
  CueLine skill installed: /Users/you/.codex/skills/cueline
138
152
 
139
153
  $ cueline doctor
140
- CueLine 0.1.5
154
+ CueLine 0.1.6
141
155
  status ok
142
156
  node 22.14.0 ok
143
157
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
@@ -179,7 +193,7 @@ Node 버전이 너무 낮거나 활성화된 caller 레인이 하나도 없으
179
193
 
180
194
  `CUELINE_CONFIG`는 라우팅 설정 파일을 고르고, `CUELINE_HOME`은 로컬 상태의 위치를 옮깁니다(기본값 `~/.cueline`).
181
195
 
182
- Caller는 프로세스를 띄우지 않습니다. `process` executor를 명시한 경우에만 `default` 레인의 `codex-default`가 `codex exec`를 실행합니다. 독립 `advise`의 기본 동시 실행 상한은 전체/레인당 2이고, `work`가 포함된 배치는 직렬입니다.
196
+ Caller는 프로세스를 띄우지 않습니다. `executor: "process"`와 `allowProcessExecution: true`를 함께 지정한 경우에만 `default` 레인의 `codex-default`가 격리된 `codex exec --ignore-user-config`를 실행합니다. 독립 `advise`의 기본 동시 실행 상한은 전체/레인당 2이고, `work`가 포함된 배치는 직렬입니다.
183
197
 
184
198
  상태는 `CUELINE_HOME` 아래에 놓입니다:
185
199
 
@@ -212,7 +226,7 @@ npm pack --dry-run
212
226
 
213
227
  ## 0.1의 한계
214
228
 
215
- 텍스트 명령 전용입니다. 긴 텍스트의 자동 첨부 변환은 지원하지만 의도적 파일 업로드, 이미지, Deep Research, Projects, Apps는 지원하지 않습니다. Caller `advise`만 허용하며 `work`에는 명시적 process executor가 필요합니다. 모호한 전송이나 이미 시작된 작업은 자동 재시도하지 않습니다.
229
+ 텍스트 명령 전용입니다. 긴 텍스트의 자동 첨부 변환은 지원하지만 의도적 파일 업로드, 이미지, Deep Research, Projects, Apps는 지원하지 않습니다. Caller work 명시적 claim/start, process 실행은 이중 승인이 필요합니다. 모호한 전송이나 이미 시작된 작업은 자동 재시도하지 않습니다.
216
230
 
217
231
  전체 표는 [compatibility](docs/compatibility.md)를 보세요.
218
232
 
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  **CueLine hands the wheel to an open ChatGPT web conversation: it plans the run and calls each next step, while CueLine checks every text command and the current Codex does the permitted local work.**
15
15
 
16
- The web page never touches your machine and has no local tools. It only emits one text command per round. CueLine decides whether that command is well-formed and belongs to this run. By default, it persists `advise` jobs for the current Codex to execute and submit; an explicitly selected process executor can instead run registered local workers. CueLine keeps bounded controller evidence and the full local record.
16
+ The web page never touches your machine and has no local tools. It only emits one text command per round. CueLine decides whether that command is well-formed and belongs to this run. By default, it persists caller jobs for the current Codex: `advise` is a coordination-only handoff, while `work` requires a durable claim and start before any mutation. An explicitly double-authorized process executor can instead run registered local workers. CueLine keeps bounded controller evidence and the full local record.
17
17
 
18
18
  CueLine is a standalone implementation with **no runtime npm dependencies**. It is not a wrapper around Omnilane or GPT Relay.
19
19
 
@@ -25,9 +25,9 @@ Each round: CueLine writes down what it is about to ask, sends one observation i
25
25
 
26
26
  A non-default `maxRounds` is fixed when the run is created and counts total controller rounds across every ownerless pause. Later continuations normally omit it and reuse the durable value; supplying a different value is rejected rather than silently resetting or widening the budget.
27
27
 
28
- `caller` is the default executor for both `startCueLineRun` and `runCueLine`. With the built-in browser, CueLine submits once, captures the exact conversation URL, returns `awaiting_controller`, and releases the runtime lease instead of holding one tool call open while Pro thinks. A later `continueCueLineRun` performs one read-only observation: unfinished work returns `awaiting_controller` again without resending. A dispatch then produces durable pending `advise` jobs and returns `awaiting_caller`; the current Codex executes each exact task with its local tools, calls `submitCueLineCallerJobResult`, and continues the same run. ChatGPT planned the work—it did not perform the inspection. Caller advice has no execution claim, so coordinate one session: two sessions may both perform it, while only the first terminal evidence submitted wins. Caller-mode `work` is rejected until CueLine can issue a duplicate-safe execution claim.
28
+ `caller` is the default executor for both `startCueLineRun` and `runCueLine`. With the built-in browser, CueLine submits once, captures the exact conversation URL, returns `awaiting_controller`, and releases the runtime lease instead of holding one tool call open while Pro thinks. A later `continueCueLineRun` performs one read-only observation: unfinished work returns `awaiting_controller` again without resending. A dispatch produces durable pending jobs. `advise` returns `awaiting_caller`; it has no side-effect claim, so coordinate one session. `work` returns `awaiting_caller_work` and remains unstarted until the current Codex calls `claimCueLineCallerJob` and `startCueLineCallerJob`. The claim is bound to run, job, task hash, absolute workdir, caller identity, and a fencing token. Started work is never automatically retried; an expired started claim becomes `ambiguous`. ChatGPT proposed and reviewed the work—it did not use local tools or perform the work itself.
29
29
 
30
- For explicit `executor: "process"`, the controller chooses *what should happen* and the local side chooses *whether and how it may happen*: the lane must be enabled, the candidate must be available **before** anything spawns, and `argv[0]` must already be registered by your routing config. Nothing is passed through a shell. Independent advice defaults to two concurrent jobs globally and per lane; a batch containing `work` is serial. Once a worker starts, there is no silent fallback to a second candidate.
30
+ Process execution requires both `executor: "process"` and `allowProcessExecution: true`; a non-terminal continuation must pass the second authorization again. The bundled route also uses `--ignore-user-config`, preventing hidden workers from loading user-configured MCP servers or their command arguments. The controller chooses *what should happen* and the local side chooses *whether and how it may happen*: the lane must be enabled, the candidate must be available **before** anything spawns, and `argv[0]` must already be registered by your routing config. Nothing is passed through a shell. Independent advice defaults to two concurrent jobs globally and per lane; a batch containing `work` is serial. Once a worker starts, there is no silent fallback to a second candidate. Status exposes the resolved runner, PID, phase, last progress time, and safely observed model/provider metadata.
31
31
 
32
32
  The controller protocol keeps routing levels explicit: `lane` names the lane (`default`), while `codex-default` is a candidate runner inside that lane, not a lane. CueLine validates the entire `dispatch` before registering any job; an invalid lane or runner rejects the whole dispatch for repair, so no valid-looking subset starts early.
33
33
 
@@ -48,15 +48,15 @@ You need Node.js 22+, Codex with its built-in Browser, and — for the bundled d
48
48
  Install from the npm registry:
49
49
 
50
50
  ```bash
51
- npm install -g cueline@0.1.5
51
+ npm install -g cueline@0.1.6
52
52
  cueline install
53
53
  cueline doctor
54
54
  ```
55
55
 
56
- As a fallback, install the packaged tarball from the [v0.1.5 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.5), which also carries its `.sha256` checksum:
56
+ As a fallback, install the packaged tarball from the [v0.1.6 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.6), which also carries its `.sha256` checksum:
57
57
 
58
58
  ```bash
59
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.5/cueline-0.1.5.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.6/cueline-0.1.6.tgz
60
60
  cueline install
61
61
  cueline doctor
62
62
  ```
@@ -89,23 +89,26 @@ The bundled `cueline` skill drives the package from Codex's own Node runtime, wh
89
89
 
90
90
  ```js
91
91
  import {
92
+ claimCueLineCallerJob,
92
93
  continueCueLineRun,
93
94
  createCodexIabAdapter,
95
+ heartbeatCueLineCallerJob,
94
96
  runCueLine,
97
+ startCueLineCallerJob,
95
98
  submitCueLineCallerJobResult,
96
99
  } from "cueline";
97
100
 
98
101
  let result = await runCueLine({
99
102
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
100
- browser: createCodexIabAdapter(),
103
+ browser: createCodexIabAdapter({ browser: globalThis.browser }),
101
104
  // Optional: conversationUrl, routingConfig / routingConfigPath, home, cwd,
102
105
  // runTimeoutMs, signal, and per-job/default limits.
103
106
  }); // defaults to executor: "caller"
104
107
 
105
- while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
108
+ while (["awaiting_controller", "awaiting_caller", "awaiting_caller_work"].includes(result.status)) {
106
109
  if (result.status === "awaiting_controller") {
107
110
  await waitBeforeNextObservation(); // bounded backoff; never resend
108
- } else {
111
+ } else if (result.status === "awaiting_caller") {
109
112
  for (const job of result.pendingJobs ?? []) {
110
113
  const stdout = await executeExactLocalAdvice(job.spec.task);
111
114
  await submitCueLineCallerJobResult(result.runId, job.jobId, {
@@ -113,6 +116,28 @@ while (result.status === "awaiting_controller" || result.status === "awaiting_ca
113
116
  stdout,
114
117
  });
115
118
  }
119
+ } else {
120
+ for (const job of result.pendingJobs ?? []) {
121
+ if (job.spec.mode !== "work") continue;
122
+ const claim = await claimCueLineCallerJob(result.runId, job.jobId, {
123
+ callerId: "stable-codex-task-identity",
124
+ });
125
+ const proof = {
126
+ claimId: claim.claimId,
127
+ callerId: claim.callerId,
128
+ fencingToken: claim.fencingToken,
129
+ };
130
+ await startCueLineCallerJob(result.runId, job.jobId, proof);
131
+ const stdout = await executeExactLocalWork(job.spec.task, claim.workdir, {
132
+ heartbeat: () => heartbeatCueLineCallerJob(result.runId, job.jobId, proof),
133
+ });
134
+ await submitCueLineCallerJobResult(
135
+ result.runId,
136
+ job.jobId,
137
+ { status: "succeeded", stdout },
138
+ { claim: proof },
139
+ );
140
+ }
116
141
  }
117
142
  result = await continueCueLineRun({ runId: result.runId });
118
143
  }
@@ -122,7 +147,7 @@ if (result.status === "complete") {
122
147
  }
123
148
  ```
124
149
 
125
- `startCueLineRun` creates the durable run and returns `ready` without driving the browser. `runCueLine` creates and advances it to a durable controller-observation pause, caller handoff, or terminal state. `continueCueLineRun({ runId })` advances the same conversation and reuses its stored URL. `loadCueLineRunState(runId)` is read-only. A terminal run is returned as-is. Before continuation, run `cueline run status <run-id> --json`: ownerless `controller_response_pending` with exactly one normally submitted turn and `safeNextAction: observe` means Pro's response has not yet been observed; wait briefly and continue it without resend. `phase: prompt_not_sent` with `safeNextAction: retry` is used only when the exact request has built-in write-ahead evidence or request-correlated `definitely_not_sent` evidence. `safeNextAction: reconcile` is reserved for ambiguous, manually submitted, or multiple pending turns. Ownerless `caller_jobs_pending` is a healthy local handoff; an accepted response plus `jobs_running` means an explicit process executor is active.
150
+ `startCueLineRun` creates the durable run and returns `ready` without driving the browser. `runCueLine` creates and advances it to a durable controller-observation pause, caller handoff, or terminal state. `continueCueLineRun({ runId })` advances the same conversation and reuses its stored URL. `loadCueLineRunState(runId)` is read-only. A terminal run is returned as-is. Before continuation, run `cueline run status <run-id> --json`: `controller_response_pending` with exactly one normally submitted turn and `safeNextAction: observe` means Pro's response has not yet been observed; wait briefly and continue it without resend. That exact read-only observer can be safely fenced even after its lease becomes stale, but ambiguous/manual submissions, jobs, pending commands, cancellation, or a missing/mismatched URL still require explicit recovery. `phase: prompt_not_sent` with `safeNextAction: retry` is used only with write-ahead or request-correlated `definitely_not_sent` evidence. Caller work phases report `claim_caller_work`, `start_caller_work`, or `continue_caller_work`; a dispatch alone is not local execution. An accepted response plus `jobs_running` means a double-authorized process executor is active.
126
151
 
127
152
  Inside Codex's runtime, import the absolute module that `cueline api path` prints — that is the built API of the package you installed.
128
153
 
@@ -135,7 +160,7 @@ $ cueline install
135
160
  CueLine skill installed: /Users/you/.codex/skills/cueline
136
161
 
137
162
  $ cueline doctor
138
- CueLine 0.1.5
163
+ CueLine 0.1.6
139
164
  status ok
140
165
  node 22.14.0 ok
141
166
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
@@ -180,7 +205,7 @@ Use `run takeover` only when `run status` reports an exact stale owner. It refus
180
205
 
181
206
  `CUELINE_CONFIG` selects a routing file; `CUELINE_HOME` moves local state (default `~/.cueline`).
182
207
 
183
- Caller execution needs no spawned route. When `executor: "process"` is explicitly selected, the bundled `default` lane holds one candidate, `codex-default`: `codex exec` with the task on stdin, `read-only` for `advise`, and `workspace-write` for `work`. To register a different process worker, copy [`config/routing.default.json`](config/routing.default.json), add your candidate, and point `CUELINE_CONFIG` at it.
208
+ Caller execution needs no spawned route. When `executor: "process"` and `allowProcessExecution: true` are both selected, the bundled `default` lane holds one candidate, `codex-default`: isolated `codex exec --ignore-user-config` with the task on stdin, `read-only` for `advise`, and `workspace-write` for `work`. To register a different process worker, copy [`config/routing.default.json`](config/routing.default.json), add your candidate, and point `CUELINE_CONFIG` at it.
184
209
 
185
210
  State lives under `CUELINE_HOME`:
186
211
 
@@ -196,11 +221,11 @@ jobs/<job-id>.json per-job execution evidence
196
221
 
197
222
  The event log is the record: the controller turn is written before it is sent, and a job is registered before its process starts, so an interruption between intent and side effect leaves a trace. A corrupt snapshot is ignored and rebuilt from event 1 rather than trusted.
198
223
 
199
- Recovery reattaches only to the exact recorded conversation URL. CueLine recognizes long prompts that ChatGPT automatically converts into attachment chips and makes at most one send attempt. Contenteditable block newlines are normalized before readiness comparison. An ambiguous click is `possibly_sent` and is never clicked again. If an operator manually sends that attachment after ChatGPT creates the first `/c/...` URL, `cueline run reconcile ... --manual-send-confirmed --conversation-url URL` atomically binds the exact URL and records an append-only confirmation; CueLine then requires exact conversation, Pro model, and protocol/run/round/request identity before importing the response without resend or duplicate dispatch.
224
+ Recovery reattaches only to the exact recorded conversation URL. CueLine recognizes long prompts that ChatGPT automatically converts into attachment chips and makes at most one send attempt. Contenteditable block newlines are normalized before readiness comparison. An ambiguous click is `possibly_sent` and is never clicked again. A response is considered in progress only while a visible, enabled, actionable Stop control exists; hidden residual buttons do not suppress a completed Pro response. If an operator manually sends an attachment after ChatGPT creates the first `/c/...` URL, `cueline run reconcile ... --manual-send-confirmed --conversation-url URL` atomically binds the exact URL and records an append-only confirmation; CueLine then requires exact conversation, Pro model, and protocol/run/round/request identity before importing the response without resend or duplicate dispatch.
200
225
 
201
226
  Never interrupt Pro or use `Answer now`, `Respond now`, `Stop`, or an equivalent acceleration control while it is answering. Pro has no local tools and no default knowledge of repository layout or local paths. Caller evidence must include exact code/error identifiers, relevant code excerpts, and absolute local paths, then explicitly ask whether Pro needs more local evidence.
202
227
 
203
- Controller observations prefer successful non-empty stdout, retain full stdout/stderr in local job status, and share one 12,000-character evidence budget with an explicit truncation marker.
228
+ Controller observations prefer successful non-empty stdout, retain full stdout/stderr in local job status, and share one 12,000-character evidence budget with an explicit truncation marker. An accepted `inspect(job_ids)` reserves that budget for the named jobs before unrelated evidence, so the next Pro turn receives the requested output instead of only its terminal status.
204
229
 
205
230
  ## Verify
206
231
 
@@ -217,7 +242,7 @@ npm pack --dry-run
217
242
 
218
243
  ## Limits in 0.1
219
244
 
220
- Text commands only. One conversation per run. Selecting `Pro` is the only model switch CueLine makes. Automatic long-text-to-attachment conversion is supported, but deliberate file upload, images, Deep Research, Projects, and Apps are not. Caller mode is `advise` only; process `work` must be explicit. No automatic retry or fallback starts a worker twice. macOS is the primary desktop target and Linux is the CI target; Windows is unverified. The adapter depends on the current ChatGPT web UI, so a UI change surfaces explicitly, never as a fabricated answer.
245
+ Text commands only. One conversation per run. Selecting `Pro` is the only model switch CueLine makes. Automatic long-text-to-attachment conversion is supported, but deliberate file upload, images, Deep Research, Projects, and Apps are not. Caller `work` requires an explicit durable claim/start and a heartbeat for long work; process execution requires two explicit authorization fields. No automatic retry or fallback starts work twice. macOS is the primary desktop target and Linux is the CI target; Windows is unverified. The adapter depends on the current ChatGPT web UI, so a UI change surfaces explicitly, never as a fabricated answer.
221
246
 
222
247
  See [compatibility](docs/compatibility.md) for the full matrix.
223
248
 
package/README.zh-CN.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  **CueLine 把方向盘交给一个已经打开的 ChatGPT 网页会话:由它规划运行、发出每一步文本指令;CueLine 负责校验,当前 Codex 才在本机执行获准的工作。**
15
15
 
16
- 那个网页碰不到你的机器,也没有本地工具。CueLine 默认把 `advise` 作业持久化并交回当前 Codex 执行;只有显式选择 `process` executor,才会启动已注册的本地 worker。
16
+ 那个网页碰不到你的机器,也没有本地工具。CueLine 默认把 caller 作业持久化:`advise` 是协调式交接;`work` 必须先获得持久 claim 并正式 start。只有双重显式授权 `process` executor,才会启动已注册的本地 worker。
17
17
 
18
18
  CueLine 是独立实现,**没有任何运行时 npm 依赖**,也不是 Omnilane 或 GPT Relay 的包装层。
19
19
 
@@ -25,9 +25,9 @@ CueLine 是独立实现,**没有任何运行时 npm 依赖**,也不是 Omnil
25
25
 
26
26
  非默认的 `maxRounds` 会在创建 run 时固定,并跨所有无 owner 的暂停累计控制器总轮次。后续继续通常省略它并复用持久值;传入不同数值会被拒绝,不会暗中重置或放宽预算。
27
27
 
28
- `startCueLineRun` 与 `runCueLine` 都默认使用 `caller`。使用内置浏览器时,CueLine 只发送一次、保存精确会话 URL,然后返回 `awaiting_controller` 并释放 runtime lease,不会让单次工具调用一直等待 Pro。之后的 `continueCueLineRun` 只做一次只读观测;若尚未完成,会再次返回 `awaiting_controller`,绝不重发。`dispatch` 才返回 `awaiting_caller`;当前 Codex 执行各个 `advise` 任务、调用 `submitCueLineCallerJobResult`,再继续同一 run。Pro 控制器只下文本命令,不能使用本地工具。Caller advice 没有 execution claim,请协调由单一 session 执行;两个 session 可能都做同一项检查,但只有第一份提交的终态证据生效。Caller 模式明确拒绝 `work`。
28
+ `startCueLineRun` 与 `runCueLine` 都默认使用 `caller`。CueLine 发送一次后返回 `awaiting_controller` 并释放 lease;继续只做一次只读观测,绝不重发。`advise` 返回 `awaiting_caller`,没有副作用 claim;`work` 返回 `awaiting_caller_work`,必须由当前 Codex 调用 `claimCueLineCallerJob` `startCueLineCallerJob` 后才能修改。claim 绑定 run、job、task hash、绝对 workdir、caller identity fencing token;已开始的工作不会自动重试,过期后成为 `ambiguous`。Pro 只提出和审查文本指令,不会使用本地工具。
29
29
 
30
- 控制器决定*应该发生什么*;本地这一侧决定*是否允许发生、以何种方式发生*:通道(lane)必须启用、候选必须在任何进程启动**之前**确认可用、`argv[0]` 必须早已由你的路由配置注册。没有任何内容会经过 shell。worker 一旦启动,就不会悄悄退回到第二个候选——失败以证据的形式返回,而不是自动重试。
30
+ Process 模式必须同时指定 `executor: "process"` 与 `allowProcessExecution: true`,非终态继续也要再次传入第二道授权。内置 route 还使用 `--ignore-user-config`,不会让隐藏 worker 加载用户配置的 MCP server 或其命令参数。通道必须启用、候选必须在任何进程启动**之前**确认可用、`argv[0]` 必须已注册。没有内容经过 shell,也不会在启动后自动换候选。
31
31
 
32
32
  控制器协议有意区分路由层级:`lane` 填的是通道名称 `default`;`codex-default` 是该通道内的候选执行器,不是通道。CueLine 会在注册任何作业之前先验证整份 `dispatch`;只要包含无效通道或执行器,整份派工就会被退回修复,不会先执行其中一部分。
33
33
 
@@ -48,15 +48,15 @@ ChatGPT Pro 订阅套餐与“选定的 Pro 模型”是两回事。账号或个
48
48
  从 npm registry 安装:
49
49
 
50
50
  ```bash
51
- npm install -g cueline@0.1.5
51
+ npm install -g cueline@0.1.6
52
52
  cueline install
53
53
  cueline doctor
54
54
  ```
55
55
 
56
- 作为后备,也可以安装 [v0.1.5 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.5) 上的打包 tarball,该 release 同时附带它的 `.sha256` 校验值:
56
+ 作为后备,也可以安装 [v0.1.6 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.6) 上的打包 tarball,该 release 同时附带它的 `.sha256` 校验值:
57
57
 
58
58
  ```bash
59
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.5/cueline-0.1.5.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.6/cueline-0.1.6.tgz
60
60
  cueline install
61
61
  cueline doctor
62
62
  ```
@@ -89,23 +89,26 @@ cueline doctor
89
89
 
90
90
  ```js
91
91
  import {
92
+ claimCueLineCallerJob,
92
93
  continueCueLineRun,
93
94
  createCodexIabAdapter,
95
+ heartbeatCueLineCallerJob,
94
96
  runCueLine,
97
+ startCueLineCallerJob,
95
98
  submitCueLineCallerJobResult,
96
99
  } from "cueline";
97
100
 
98
101
  let result = await runCueLine({
99
102
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
100
- browser: createCodexIabAdapter(),
103
+ browser: createCodexIabAdapter({ browser: globalThis.browser }),
101
104
  // 可选:conversationUrl、routingConfig / routingConfigPath、home、cwd、
102
105
  // runTimeoutMs、signal,以及作业/默认期限。
103
106
  }); // 默认 executor: "caller"
104
107
 
105
- while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
108
+ while (["awaiting_controller", "awaiting_caller", "awaiting_caller_work"].includes(result.status)) {
106
109
  if (result.status === "awaiting_controller") {
107
110
  await waitBeforeNextObservation(); // 有界退避;绝不重发
108
- } else {
111
+ } else if (result.status === "awaiting_caller") {
109
112
  for (const job of result.pendingJobs ?? []) {
110
113
  const stdout = await executeExactLocalAdvice(job.spec.task);
111
114
  await submitCueLineCallerJobResult(result.runId, job.jobId, {
@@ -113,6 +116,19 @@ while (result.status === "awaiting_controller" || result.status === "awaiting_ca
113
116
  stdout,
114
117
  });
115
118
  }
119
+ } else {
120
+ for (const job of result.pendingJobs ?? []) {
121
+ if (job.spec.mode !== "work") continue;
122
+ const claim = await claimCueLineCallerJob(result.runId, job.jobId, {
123
+ callerId: "stable-codex-task-identity",
124
+ });
125
+ const proof = { claimId: claim.claimId, callerId: claim.callerId, fencingToken: claim.fencingToken };
126
+ await startCueLineCallerJob(result.runId, job.jobId, proof);
127
+ const stdout = await executeExactLocalWork(job.spec.task, claim.workdir, {
128
+ heartbeat: () => heartbeatCueLineCallerJob(result.runId, job.jobId, proof),
129
+ });
130
+ await submitCueLineCallerJobResult(result.runId, job.jobId, { status: "succeeded", stdout }, { claim: proof });
131
+ }
116
132
  }
117
133
  result = await continueCueLineRun({ runId: result.runId });
118
134
  }
@@ -122,7 +138,7 @@ if (result.status === "complete") {
122
138
  }
123
139
  ```
124
140
 
125
- 若返回 `awaiting_controller`,表示同一个精确 request 已发送、但 Pro 回复尚未被观测;稍后继续只会执行只读观测,不会重发。若返回 `awaiting_caller`,由当前 Codex 执行 `result.pendingJobs` 中的每个 `advise`,用 `submitCueLineCallerJobResult` 提交真实结果,再调用 `continueCueLineRun`。这不是 Pro 网页直接使用本地工具。
141
+ `awaiting_controller` 只读观测且不重发;`awaiting_caller` 交接 `advise`;`awaiting_caller_work` 必须依次 claim、start、执行、heartbeat 并带 claim proof 提交。Pro 网页从不直接使用本地工具。
126
142
 
127
143
  在 Codex 的 runtime 里,import `cueline api path` 打印出的那个绝对路径模块——那就是你安装的那份包构建出来的 API。
128
144
 
@@ -137,7 +153,7 @@ $ cueline install
137
153
  CueLine skill installed: /Users/you/.codex/skills/cueline
138
154
 
139
155
  $ cueline doctor
140
- CueLine 0.1.5
156
+ CueLine 0.1.6
141
157
  status ok
142
158
  node 22.14.0 ok
143
159
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
@@ -179,7 +195,7 @@ CueLine skill removed: /Users/you/.codex/skills/cueline
179
195
 
180
196
  `CUELINE_CONFIG` 用于指定路由配置文件;`CUELINE_HOME` 用于迁移本地状态(默认 `~/.cueline`)。
181
197
 
182
- Caller 模式不会启动路由进程。只有显式选择 `process` executor 时,内置 `default` 通道才以 `codex-default` 运行 `codex exec`;独立 `advise` 默认全局/每 lane 并发上限均为 2,包含 `work` 的批次保持串行。
198
+ Caller 模式不会启动路由进程。只有同时选择 `executor: "process"` `allowProcessExecution: true` 时,内置 `default` 通道才以 `codex-default` 运行隔离的 `codex exec --ignore-user-config`;独立 `advise` 默认全局/每 lane 并发上限均为 2,包含 `work` 的批次保持串行。
183
199
 
184
200
  状态位于 `CUELINE_HOME` 之下:
185
201
 
@@ -212,7 +228,7 @@ npm pack --dry-run
212
228
 
213
229
  ## 0.1 的限制
214
230
 
215
- 仅支持文本控制命令。一次运行只对应一个会话。支持 ChatGPT 自动将长文本转为附件,但不支持主动文件上传、图片、Deep Research、Projects 或 Apps。Caller 模式只允许 `advise`;`work` 必须显式选择 process executor。模糊发送和已启动工作都不会被自动重试。
231
+ 仅支持文本控制命令。一次运行只对应一个会话。支持 ChatGPT 自动将长文本转为附件,但不支持主动文件上传、图片、Deep Research、Projects 或 Apps。Caller `work` 必须显式 claim/start;process 执行必须双重授权。模糊发送和已启动工作都不会被自动重试。
216
232
 
217
233
  完整矩阵见 [compatibility](docs/compatibility.md)。
218
234