cueline 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +3 -3
  3. package/CHANGELOG.md +21 -0
  4. package/README.ja.md +57 -22
  5. package/README.ko.md +57 -22
  6. package/README.md +62 -24
  7. package/README.zh-CN.md +57 -22
  8. package/README.zh-TW.md +60 -23
  9. package/dist/src/api-contracts.d.ts +77 -0
  10. package/dist/src/api-contracts.js +2 -0
  11. package/dist/src/api-contracts.js.map +1 -0
  12. package/dist/src/api-controller-handoff.d.ts +6 -0
  13. package/dist/src/api-controller-handoff.js +253 -0
  14. package/dist/src/api-controller-handoff.js.map +1 -0
  15. package/dist/src/api-runtime-lifecycle.d.ts +22 -0
  16. package/dist/src/api-runtime-lifecycle.js +568 -0
  17. package/dist/src/api-runtime-lifecycle.js.map +1 -0
  18. package/dist/src/api.d.ts +6 -46
  19. package/dist/src/api.js +125 -133
  20. package/dist/src/api.js.map +1 -1
  21. package/dist/src/browser/browser-adapter.d.ts +10 -1
  22. package/dist/src/browser/codex-iab/bootstrap.d.ts +10 -0
  23. package/dist/src/browser/codex-iab/bootstrap.js +49 -0
  24. package/dist/src/browser/codex-iab/bootstrap.js.map +1 -1
  25. package/dist/src/browser/codex-iab/chatgpt-client.js +236 -107
  26. package/dist/src/browser/codex-iab/chatgpt-client.js.map +1 -1
  27. package/dist/src/browser/codex-iab/recovery-evidence.d.ts +6 -0
  28. package/dist/src/browser/codex-iab/recovery-evidence.js +37 -0
  29. package/dist/src/browser/codex-iab/recovery-evidence.js.map +1 -0
  30. package/dist/src/browser/codex-iab/submission-url.d.ts +2 -0
  31. package/dist/src/browser/codex-iab/submission-url.js +53 -0
  32. package/dist/src/browser/codex-iab/submission-url.js.map +1 -0
  33. package/dist/src/cli/main.js +188 -45
  34. package/dist/src/cli/main.js.map +1 -1
  35. package/dist/src/core/controller-abort.d.ts +1 -0
  36. package/dist/src/core/controller-abort.js +11 -0
  37. package/dist/src/core/controller-abort.js.map +1 -0
  38. package/dist/src/core/controller-command-execution.d.ts +9 -0
  39. package/dist/src/core/controller-command-execution.js +299 -0
  40. package/dist/src/core/controller-command-execution.js.map +1 -0
  41. package/dist/src/core/controller-loop.d.ts +3 -43
  42. package/dist/src/core/controller-loop.js +260 -331
  43. package/dist/src/core/controller-loop.js.map +1 -1
  44. package/dist/src/core/controller-turn.d.ts +16 -0
  45. package/dist/src/core/controller-turn.js +309 -0
  46. package/dist/src/core/controller-turn.js.map +1 -0
  47. package/dist/src/core/controller-types.d.ts +58 -0
  48. package/dist/src/core/controller-types.js +2 -0
  49. package/dist/src/core/controller-types.js.map +1 -0
  50. package/dist/src/core/persisted-run.d.ts +4 -0
  51. package/dist/src/core/persisted-run.js +19 -0
  52. package/dist/src/core/persisted-run.js.map +1 -0
  53. package/dist/src/core/process-liveness.d.ts +2 -0
  54. package/dist/src/core/process-liveness.js +44 -0
  55. package/dist/src/core/process-liveness.js.map +1 -0
  56. package/dist/src/core/run-status.d.ts +5 -2
  57. package/dist/src/core/run-status.js +65 -10
  58. package/dist/src/core/run-status.js.map +1 -1
  59. package/dist/src/core/state-machine.d.ts +15 -3
  60. package/dist/src/core/state-machine.js +148 -15
  61. package/dist/src/core/state-machine.js.map +1 -1
  62. package/dist/src/jobs/status.d.ts +1 -1
  63. package/dist/src/jobs/supervisor.js +3 -1
  64. package/dist/src/jobs/supervisor.js.map +1 -1
  65. package/dist/src/router/resolver.d.ts +2 -1
  66. package/dist/src/router/resolver.js +31 -19
  67. package/dist/src/router/resolver.js.map +1 -1
  68. package/dist/src/runners/process-runner.js +59 -6
  69. package/dist/src/runners/process-runner.js.map +1 -1
  70. package/dist/src/state/atomic-write.js +17 -2
  71. package/dist/src/state/atomic-write.js.map +1 -1
  72. package/dist/src/state/cancellation.d.ts +1 -1
  73. package/dist/src/state/cancellation.js +10 -2
  74. package/dist/src/state/cancellation.js.map +1 -1
  75. package/dist/src/state/event-log.d.ts +17 -1
  76. package/dist/src/state/event-log.js +328 -18
  77. package/dist/src/state/event-log.js.map +1 -1
  78. package/dist/src/state/paths.d.ts +1 -0
  79. package/dist/src/state/paths.js +1 -0
  80. package/dist/src/state/paths.js.map +1 -1
  81. package/dist/src/state/runtime-lease.d.ts +25 -0
  82. package/dist/src/state/runtime-lease.js +607 -59
  83. package/dist/src/state/runtime-lease.js.map +1 -1
  84. package/dist/src/state/runtime-retirement.d.ts +16 -0
  85. package/dist/src/state/runtime-retirement.js +95 -0
  86. package/dist/src/state/runtime-retirement.js.map +1 -0
  87. package/dist/src/state/runtime-takeover-intent.d.ts +1 -0
  88. package/dist/src/state/runtime-takeover-intent.js +18 -0
  89. package/dist/src/state/runtime-takeover-intent.js.map +1 -0
  90. package/dist/src/state/store.d.ts +12 -1
  91. package/dist/src/state/store.js +246 -40
  92. package/dist/src/state/store.js.map +1 -1
  93. package/dist/src/version.d.ts +1 -1
  94. package/dist/src/version.js +1 -1
  95. package/docs/architecture.md +16 -11
  96. package/docs/assets/cueline-loop-en.svg +11 -10
  97. package/docs/assets/cueline-loop-ja.svg +11 -10
  98. package/docs/assets/cueline-loop-ko.svg +11 -10
  99. package/docs/assets/cueline-loop-zh-CN.svg +11 -10
  100. package/docs/assets/cueline-loop-zh-TW.svg +11 -10
  101. package/docs/compatibility.md +15 -9
  102. package/docs/controller-protocol.md +7 -5
  103. package/docs/runner-contract.md +19 -5
  104. package/docs/state-and-recovery.md +49 -21
  105. package/package.json +2 -2
  106. package/skills/cueline/SKILL.md +65 -17
package/README.md CHANGED
@@ -11,23 +11,27 @@
11
11
  <b>English</b> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a>
12
12
  </p>
13
13
 
14
- **CueLine hands the wheel to an open ChatGPT web conversation: it plans the run and calls each next step, while CueLine checks every command and does the actual work here, on your machine.**
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. It only ever emits one small text command per round. CueLine decides whether that command is well-formed, whether it belongs to this run, which local worker it maps to and then runs it, keeps the evidence, and hands the evidence back.
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.
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
 
20
20
  ## How a run actually goes
21
21
 
22
- <img alt="A CueLine run read as a promptbook: the machine reports an observation, the controller calls one command, the registered runner executes it, until the controller calls complete." src="docs/assets/cueline-loop-en.svg" width="100%">
22
+ <img alt="A caller-first CueLine run: ChatGPT emits text commands, the current Codex performs local advice, and CueLine returns bounded evidence until complete." src="docs/assets/cueline-loop-en.svg" width="100%">
23
23
 
24
- Each round: CueLine writes down what it is about to ask, sends one observation into the conversation, and reads back exactly one `<CueLineControl>` envelope. The controller picks one of five actions — `dispatch`, `wait`, `inspect`, `complete`, `blocked` — and nothing outside that envelope is ever executed. A command that names the wrong run, the wrong round, or a malformed job is sent back for a bounded repair attempt rather than guessed at. The loop stops at `complete` or `blocked`, or when it runs out of rounds (12 by default).
24
+ Each round: CueLine writes down what it is about to ask, sends one observation into the conversation, and later reads back exactly one `<CueLineControl>` envelope. The controller picks one of five actions — `dispatch`, `wait`, `inspect`, `complete`, `blocked` — and nothing outside that envelope is ever executed. A command that names the wrong run, the wrong round, or a malformed job is sent back for a bounded repair attempt rather than guessed at. The loop pauses at `awaiting_controller` after one durable send, at a caller handoff, or stops at `complete`, `blocked`, or the round limit (12 by default).
25
25
 
26
- The controller chooses *what should happen*. 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. Once a worker starts, there is no silent fallback to a second candidate a failure comes back as evidence, not as a retry.
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
+
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.
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.
27
31
 
28
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.
29
33
 
30
- That is an allow-list, not a sandbox. A registered worker runs with the same permissions as the CueLine process itself; `advise` maps to a read-only Codex sandbox and `work` to `workspace-write`, but what you register is what you have authorized.
34
+ That process route is an allow-list, not a sandbox. A registered worker runs with the same permissions as the CueLine process itself; `advise` maps to a read-only Codex sandbox and `work` to `workspace-write`, but what you register is what you have authorized.
31
35
 
32
36
  ## The controller must be a Pro model
33
37
 
@@ -44,15 +48,15 @@ You need Node.js 22+, Codex with its built-in Browser, and — for the bundled d
44
48
  Install from the npm registry:
45
49
 
46
50
  ```bash
47
- npm install -g cueline@0.1.3
51
+ npm install -g cueline@0.1.4
48
52
  cueline install
49
53
  cueline doctor
50
54
  ```
51
55
 
52
- As a fallback, install the packaged tarball from the [v0.1.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.3), which also carries its `.sha256` checksum:
56
+ As a fallback, install the packaged tarball from the [v0.1.4 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.4), which also carries its `.sha256` checksum:
53
57
 
54
58
  ```bash
55
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.3/cueline-0.1.3.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.4/cueline-0.1.4.tgz
56
60
  cueline install
57
61
  cueline doctor
58
62
  ```
@@ -84,39 +88,61 @@ The bundled `cueline` skill drives the package from Codex's own Node runtime, wh
84
88
  ## Driving it from code
85
89
 
86
90
  ```js
87
- import { createCodexIabAdapter, runCueLine } from "cueline";
88
-
89
- const result = await runCueLine({
91
+ import {
92
+ continueCueLineRun,
93
+ createCodexIabAdapter,
94
+ runCueLine,
95
+ submitCueLineCallerJobResult,
96
+ } from "cueline";
97
+
98
+ let result = await runCueLine({
90
99
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
91
100
  browser: createCodexIabAdapter(),
92
101
  // Optional: conversationUrl, routingConfig / routingConfigPath, home, cwd,
93
102
  // runTimeoutMs, signal, and per-job/default limits.
94
- });
103
+ }); // defaults to executor: "caller"
104
+
105
+ while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
106
+ if (result.status === "awaiting_controller") {
107
+ await waitBeforeNextObservation(); // bounded backoff; never resend
108
+ } else {
109
+ for (const job of result.pendingJobs ?? []) {
110
+ const stdout = await executeExactLocalAdvice(job.spec.task);
111
+ await submitCueLineCallerJobResult(result.runId, job.jobId, {
112
+ status: "succeeded",
113
+ stdout,
114
+ });
115
+ }
116
+ }
117
+ result = await continueCueLineRun({ runId: result.runId });
118
+ }
95
119
 
96
120
  if (result.status === "complete") {
97
121
  console.log(result.finalDeliveryText);
98
122
  }
99
123
  ```
100
124
 
101
- `startCueLineRun` is the explicit start (`runCueLine` is its alias). `continueCueLineRun({ runId })` resumes an interrupted run in the same conversation, and reuses the stored conversation URL unless you hand it a new adapter. `loadCueLineRunState(runId)` reads persisted state without driving anything. A run that already reached `complete`, `blocked`, or `cancelled` is returned as-is, never dispatched twice. Before continuation, run `cueline run status <run-id> --json`: an accepted response plus `jobs_running` means ChatGPT already answered and local jobs are executing.
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. `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.
102
126
 
103
127
  Inside Codex's runtime, import the absolute module that `cueline api path` prints — that is the built API of the package you installed.
104
128
 
105
129
  ## The CLI
106
130
 
107
- The CLI does not drive the browser. It manages the skill link and tells you whether the local half is sound.
131
+ The CLI does not drive the browser. `doctor`, `routing`, `jobs`, `run status`, `api path`, and `config path` are read-only. `install`/`uninstall` change the package-owned skill link. `run reconcile`, `run takeover`, `run reconcile-runtime`, `run cancel`/`run stop`, and `job cancel` append evidence or change durable local run/job state. Run `cueline help` for every positional argument and option before using a state-changing command.
108
132
 
109
133
  ```console
110
134
  $ cueline install
111
135
  CueLine skill installed: /Users/you/.codex/skills/cueline
112
136
 
113
137
  $ cueline doctor
114
- CueLine 0.1.3
138
+ CueLine 0.1.4
115
139
  status ok
116
140
  node 22.14.0 ok
117
141
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
118
142
  home /Users/you/.cueline
119
- available_lanes 1
143
+ caller_ready yes
144
+ caller_lanes 1
145
+ process_available_lanes 1
120
146
 
121
147
  $ cueline api path
122
148
  /usr/local/lib/node_modules/cueline/dist/src/api.js
@@ -128,7 +154,13 @@ $ cueline jobs
128
154
  No jobs.
129
155
 
130
156
  $ cueline run status run_... --json
131
- {"status":"running","phase":"jobs_running","runtime":{"ownership":"active"},...}
157
+ {"status":"running","executor":"caller","phase":"caller_jobs_pending","runtime":{"ownership":"missing"},...}
158
+
159
+ $ cueline run takeover stale_run_... --json
160
+ {"runId":"stale_run_...","outcome":"taken_over","next":"continue",...}
161
+
162
+ $ cueline run reconcile run_... --request-id msg_... --manual-send-confirmed
163
+ run_...\tmsg_...\tconfirmed
132
164
 
133
165
  $ cueline run cancel run_...
134
166
  run_... requested affected_jobs=0
@@ -140,19 +172,23 @@ $ cueline uninstall
140
172
  CueLine skill removed: /Users/you/.codex/skills/cueline
141
173
  ```
142
174
 
143
- `cueline doctor` exits non-zero when Node is too old or no lane can resolve, which makes it usable as a preflight check. `cueline routing` shows why a lane is unavailable instead of quietly selecting something else. `cueline api path` is what the skill imports, so a packaged install needs no repository checkout. `cueline help` lists everything.
175
+ `cueline doctor` exits non-zero when Node is too old or no enabled caller lane exists. `process_available_lanes` may be zero without degrading caller mode; use `cueline routing` to inspect process availability before explicitly selecting that executor. `cueline api path` is what the skill imports, so a packaged install needs no repository checkout. `cueline help` lists every command's exact syntax, including `--json` and the manual-reconcile confirmation flags.
176
+
177
+ Use `run takeover` only when `run status` reports an exact stale owner. It refuses a fresh active heartbeat and returns `next: continue` or `next: reconcile_runtime`; follow that value instead of guessing.
144
178
 
145
179
  ## Configuration
146
180
 
147
181
  `CUELINE_CONFIG` selects a routing file; `CUELINE_HOME` moves local state (default `~/.cueline`).
148
182
 
149
- The bundled `default` lane holds one candidate, `codex-default`: `codex exec` with the task on stdin, `read-only` for `advise`, `workspace-write` for `work`. To register a different worker, copy [`config/routing.default.json`](config/routing.default.json), add your candidate, and point `CUELINE_CONFIG` at it — the executable in `argv[0]` becomes registered by that act, and must also be on `PATH` before a lane resolves.
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.
150
184
 
151
185
  State lives under `CUELINE_HOME`:
152
186
 
153
187
  ```text
154
- runs/<run-id>/events.jsonl append-only, authoritative
155
- runs/<run-id>/runtime.json live-owner heartbeat evidence
188
+ runs/<run-id>/events.jsonl + events.jsonl.segments/ append-only, authoritative
189
+ runs/<run-id>/runtime.json.fence + runtime.json.epochs/ fenced live-owner heartbeat evidence
190
+ runs/<run-id>/runtime.json.retired-owners/ immutable stale-owner event cutoffs
191
+ runs/<run-id>/runtime.json.takeover-intents/ immutable exact takeover attempts
156
192
  runs/<run-id>/cancel.json durable cancellation request, when present
157
193
  runs/<run-id>/snapshot.json a replay optimization, disposable
158
194
  jobs/<job-id>.json per-job execution evidence
@@ -160,7 +196,9 @@ jobs/<job-id>.json per-job execution evidence
160
196
 
161
197
  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.
162
198
 
163
- Recovery reattaches only to the exact conversation URL the run recorded — never to a lookalike tab. For a pending controller turn, CueLine first looks in that conversation for a completed response correlated to the exact request; if found, it reconciles the response read-only instead of resending. If legacy state contains multiple pending turns, the caller must select one explicitly. CueLine retries automatically only when the sole pending prompt is proven `definitely_not_sent`; an ambiguous submission or a vanished tab throws `TAB_RECOVERY_UNSAFE` and stops.
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. An ambiguous click is `possibly_sent` and is never clicked again. If an operator manually sends that attachment, `cueline run reconcile ... --manual-send-confirmed` 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
+
201
+ 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.
164
202
 
165
203
  ## Verify
166
204
 
@@ -177,7 +215,7 @@ npm pack --dry-run
177
215
 
178
216
  ## Limits in 0.1
179
217
 
180
- Text only. One conversation per run. Selecting `Pro` is the only model switch CueLine makes; no images, no file upload, no Deep Research, Projects, or Apps. No automatic retry or fallback once a worker has started — a failed `work` job is reported with its side effects flagged as ambiguous, because CueLine cannot prove how far it got. macOS is the primary desktop target and Linux is the CI target; Windows is unverified, and `install.sh` is not a Windows installer. The adapter depends on the current ChatGPT web UI, so a UI change surfaces as an explicit `COMPOSER_MISSING`, `SEND_BUTTON_MISSING`, or response timeout — never as a fabricated answer.
218
+ 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.
181
219
 
182
220
  See [compatibility](docs/compatibility.md) for the full matrix.
183
221
 
package/README.zh-CN.md CHANGED
@@ -11,17 +11,21 @@
11
11
  <a href="README.md">English</a> · <a href="README.zh-TW.md">繁體中文</a> · <b>简体中文</b> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a>
12
12
  </p>
13
13
 
14
- **CueLine 把方向盘交给一个已经打开的 ChatGPT 网页会话:由它规划整轮运行、发出每一步指令;而 CueLine 负责校验每一条指令,并在你这台机器上把活儿真正干完。**
14
+ **CueLine 把方向盘交给一个已经打开的 ChatGPT 网页会话:由它规划运行、发出每一步文本指令;CueLine 负责校验,当前 Codex 才在本机执行获准的工作。**
15
15
 
16
- 那个网页碰不到你的机器。它每一轮只输出一小段文本指令。CueLine 判断这条指令格式是否合法、是否属于本次运行、对应到哪个本地 worker——然后才执行它、保留证据,再把证据交回去。
16
+ 那个网页碰不到你的机器,也没有本地工具。CueLine 默认把 `advise` 作业持久化并交回当前 Codex 执行;只有显式选择 `process` executor,才会启动已注册的本地 worker
17
17
 
18
18
  CueLine 是独立实现,**没有任何运行时 npm 依赖**,也不是 Omnilane 或 GPT Relay 的包装层。
19
19
 
20
20
  ## 一次运行实际是怎么走的
21
21
 
22
- <img alt="一次 CueLine 运行像一本提示本:机器发出观测,控制器发出一条指令,已注册的 runner 执行它,直到控制器发出 complete。" src="docs/assets/cueline-loop-zh-CN.svg" width="100%">
22
+ <img alt="Caller-first CueLine 运行:ChatGPT 发出文本命令,当前 Codex 执行本地只读检查,CueLine 回传有界证据直至 complete。" src="docs/assets/cueline-loop-zh-CN.svg" width="100%">
23
23
 
24
- 每一轮:CueLine 先把“接下来要问什么”写入记录,向会话发送一份观测(observation),再读回**恰好一个** `<CueLineControl>` 信封。控制器从五个动作中选一个——`dispatch`、`wait`、`inspect`、`complete`、`blocked`——信封之外的任何文本都不会被执行。指令若指向错误的 run、错误的轮次,或作业定义有误,会被退回做次数有上限的修复,而不是靠猜。循环停在 `complete` `blocked`,或轮次耗尽(默认 12 轮)。
24
+ 每一轮:CueLine 先把“接下来要问什么”写入记录,向会话发送一份观测(observation),之后再读回**恰好一个** `<CueLineControl>` 信封。控制器从五个动作中选一个——`dispatch`、`wait`、`inspect`、`complete`、`blocked`——信封之外的任何文本都不会被执行。循环会在一次可靠发送后以 `awaiting_controller` 暂停,也会停在 caller 交接、`complete`、`blocked` 或轮次上限(默认 12 轮)。
25
+
26
+ 非默认的 `maxRounds` 会在创建 run 时固定,并跨所有无 owner 的暂停累计控制器总轮次。后续继续通常省略它并复用持久值;传入不同数值会被拒绝,不会暗中重置或放宽预算。
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`。
25
29
 
26
30
  控制器决定*应该发生什么*;本地这一侧决定*是否允许发生、以何种方式发生*:通道(lane)必须启用、候选必须在任何进程启动**之前**确认可用、`argv[0]` 必须早已由你的路由配置注册。没有任何内容会经过 shell。worker 一旦启动,就不会悄悄退回到第二个候选——失败以证据的形式返回,而不是自动重试。
27
31
 
@@ -44,15 +48,15 @@ ChatGPT Pro 订阅套餐与“选定的 Pro 模型”是两回事。账号或个
44
48
  从 npm registry 安装:
45
49
 
46
50
  ```bash
47
- npm install -g cueline@0.1.3
51
+ npm install -g cueline@0.1.4
48
52
  cueline install
49
53
  cueline doctor
50
54
  ```
51
55
 
52
- 作为后备,也可以安装 [v0.1.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.3) 上的打包 tarball,该 release 同时附带它的 `.sha256` 校验值:
56
+ 作为后备,也可以安装 [v0.1.4 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.4) 上的打包 tarball,该 release 同时附带它的 `.sha256` 校验值:
53
57
 
54
58
  ```bash
55
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.3/cueline-0.1.3.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.4/cueline-0.1.4.tgz
56
60
  cueline install
57
61
  cueline doctor
58
62
  ```
@@ -84,39 +88,63 @@ cueline doctor
84
88
  ## 从代码驱动
85
89
 
86
90
  ```js
87
- import { createCodexIabAdapter, runCueLine } from "cueline";
88
-
89
- const result = await runCueLine({
91
+ import {
92
+ continueCueLineRun,
93
+ createCodexIabAdapter,
94
+ runCueLine,
95
+ submitCueLineCallerJobResult,
96
+ } from "cueline";
97
+
98
+ let result = await runCueLine({
90
99
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
91
100
  browser: createCodexIabAdapter(),
92
101
  // 可选:conversationUrl、routingConfig / routingConfigPath、home、cwd、
93
102
  // runTimeoutMs、signal,以及作业/默认期限。
94
- });
103
+ }); // 默认 executor: "caller"
104
+
105
+ while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
106
+ if (result.status === "awaiting_controller") {
107
+ await waitBeforeNextObservation(); // 有界退避;绝不重发
108
+ } else {
109
+ for (const job of result.pendingJobs ?? []) {
110
+ const stdout = await executeExactLocalAdvice(job.spec.task);
111
+ await submitCueLineCallerJobResult(result.runId, job.jobId, {
112
+ status: "succeeded",
113
+ stdout,
114
+ });
115
+ }
116
+ }
117
+ result = await continueCueLineRun({ runId: result.runId });
118
+ }
95
119
 
96
120
  if (result.status === "complete") {
97
121
  console.log(result.finalDeliveryText);
98
122
  }
99
123
  ```
100
124
 
125
+ 若返回 `awaiting_controller`,表示同一个精确 request 已发送、但 Pro 回复尚未被观测;稍后继续只会执行只读观测,不会重发。若返回 `awaiting_caller`,由当前 Codex 执行 `result.pendingJobs` 中的每个 `advise`,用 `submitCueLineCallerJobResult` 提交真实结果,再调用 `continueCueLineRun`。这不是 Pro 网页直接使用本地工具。
126
+
101
127
  在 Codex 的 runtime 里,import `cueline api path` 打印出的那个绝对路径模块——那就是你安装的那份包构建出来的 API。
102
128
 
103
- `startCueLineRun` 是显式的启动入口(`runCueLine` 是它的别名)。`continueCueLineRun({ runId })` 会在同一个会话中续跑被中断的运行,并复用已保存的会话链接,除非你传入新的 adapter。`loadCueLineRunState(runId)` 只读取已持久化的状态,不驱动任何东西。已经到达 `complete`、`blocked` `cancelled` 的运行会原样返回,绝不会被再次派发。续跑前先执行 `cueline run status <run-id> --json`;已接受回复且 phase `jobs_running` 表示 ChatGPT 已回复,本地作业正在执行。
129
+ `startCueLineRun` 只创建持久 run 并返回 `ready`;`runCueLine` 创建并推进到持久 controller 观测暂停、caller 交接或终态。缺少 owner `controller_response_pending` 若只有一个正常发送的回合且显示 `safeNextAction: observe`,表示同一个 Pro 回复仍待只读观测;稍后继续即可且不得重发。`safeNextAction: reconcile` 只用于模糊、人工发送或多个待对账回合。缺少 owner `caller_jobs_pending` 是正常本地交接,并非 orphan,也不是仍在等 ChatGPT
104
130
 
105
131
  ## CLI
106
132
 
107
- CLI 不驱动浏览器。它负责管理 skill 链接,并告诉你本地这一半是否健康。
133
+ CLI 不驱动浏览器。`doctor`、`routing`、`jobs`、`run status`、`api path`、`config path` 都是只读;`install`/`uninstall` 只修改包所拥有的 skill 链接;`run reconcile`、`run takeover`、`run reconcile-runtime`、`run cancel`/`run stop`、`job cancel` 会追加审计证据或修改持久 run/job 状态。执行写入状态的命令前,先用 `cueline help` 核对完整参数。
108
134
 
109
135
  ```console
110
136
  $ cueline install
111
137
  CueLine skill installed: /Users/you/.codex/skills/cueline
112
138
 
113
139
  $ cueline doctor
114
- CueLine 0.1.3
140
+ CueLine 0.1.4
115
141
  status ok
116
142
  node 22.14.0 ok
117
143
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
118
144
  home /Users/you/.cueline
119
- available_lanes 1
145
+ caller_ready yes
146
+ caller_lanes 1
147
+ process_available_lanes 1
120
148
 
121
149
  $ cueline api path
122
150
  /usr/local/lib/node_modules/cueline/dist/src/api.js
@@ -128,7 +156,10 @@ $ cueline jobs
128
156
  No jobs.
129
157
 
130
158
  $ cueline run status run_... --json
131
- {"status":"running","phase":"jobs_running","runtime":{"ownership":"active"},...}
159
+ {"status":"running","executor":"caller","phase":"caller_jobs_pending","runtime":{"ownership":"missing"},...}
160
+
161
+ $ cueline run takeover stale_run_... --json
162
+ {"runId":"stale_run_...","outcome":"taken_over","next":"continue",...}
132
163
 
133
164
  $ cueline run cancel run_...
134
165
  run_... requested affected_jobs=0
@@ -140,19 +171,23 @@ $ cueline uninstall
140
171
  CueLine skill removed: /Users/you/.codex/skills/cueline
141
172
  ```
142
173
 
143
- 当 Node 版本过旧、或没有任何通道可解析时,`cueline doctor` 会以非零状态退出,因此可直接用作预检。`cueline routing` 会说明某个通道为何不可用,而不是悄悄改选别的。`cueline api path` 打印的就是 skill 会 import 的模块,所以使用打包安装时完全不需要 clone 源码。`cueline help` 会列出全部。
174
+ 当 Node 版本过旧、或没有任何已启用的 caller 通道时,`cueline doctor` 会以非零状态退出。`process_available_lanes` 可以为 0 而不影响 caller 模式;只有显式选择 process executor 前才需要用 `cueline routing` 检查 process 可用性。`cueline api path` 打印的就是 skill 会 import 的模块,所以使用打包安装时完全不需要 clone 源码。`cueline help` 会列出每个命令的精确语法,包括 `--json` 和人工 reconcile 的必需确认参数。
175
+
176
+ 只有 `run status` 明确显示 stale owner 时才能使用 `run takeover`。新鲜的 active heartbeat 会被拒绝;命令返回 `next: continue` 或 `next: reconcile_runtime`,请按该值行动,不要自行猜测。
144
177
 
145
178
  ## 配置
146
179
 
147
180
  `CUELINE_CONFIG` 用于指定路由配置文件;`CUELINE_HOME` 用于迁移本地状态(默认 `~/.cueline`)。
148
181
 
149
- 内置的 `default` 通道只有一个候选 `codex-default`:通过 stdin 传入任务运行 `codex exec`,`advise` `read-only`、`work` `workspace-write`。要注册别的 worker,复制一份 [`config/routing.default.json`](config/routing.default.json)、加入你的候选,再把 `CUELINE_CONFIG` 指过去——`argv[0]` 中的可执行文件正是通过这个动作被注册的,并且它也必须在 `PATH` 上,通道才能解析成功。
182
+ Caller 模式不会启动路由进程。只有显式选择 `process` executor 时,内置 `default` 通道才以 `codex-default` 运行 `codex exec`;独立 `advise` 默认全局/每 lane 并发上限均为 2,包含 `work` 的批次保持串行。
150
183
 
151
184
  状态位于 `CUELINE_HOME` 之下:
152
185
 
153
186
  ```text
154
- runs/<run-id>/events.jsonl 仅追加、具权威性
155
- runs/<run-id>/runtime.json 活跃 owner heartbeat 证据
187
+ runs/<run-id>/events.jsonl + events.jsonl.segments/ 仅追加、具权威性
188
+ runs/<run-id>/runtime.json.fence + runtime.json.epochs/ 带世代隔离的活跃 owner heartbeat 证据
189
+ runs/<run-id>/runtime.json.retired-owners/ 不可变的旧 owner 事件截止点
190
+ runs/<run-id>/runtime.json.takeover-intents/ 不可变的精确 takeover 尝试记录
156
191
  runs/<run-id>/cancel.json 存在时表示持久取消请求
157
192
  runs/<run-id>/snapshot.json 重放优化产物,可丢弃
158
193
  jobs/<job-id>.json 每个作业的执行证据
@@ -160,7 +195,7 @@ jobs/<job-id>.json 每个作业的执行证据
160
195
 
161
196
  事件日志才是记录本身:控制器这一轮在发送之前先写入、作业在进程启动之前先注册,因此“意图”与“副作用”之间若被中断,会留下痕迹。损坏的快照会被忽略并从第 1 号事件重建,而不是被信任。
162
197
 
163
- 续跑只会重新接回该次运行记录下来的那个会话 URL,绝不接到长得像的标签页。对仍待处理的控制器回合,它会先在该会话中查找与确切请求对应的已完成回复;找到后以只读方式接回,而不是重发。若旧状态中同时有多个待处理回合,调用方必须明确选择一个。只有当唯一待处理提示能被证明为 `definitely_not_sent` 时,CueLine 才会自动重试;提交状态不明或标签页消失时,它会抛出 `TAB_RECOVERY_UNSAFE` 并停下。
198
+ 续跑只接回完全相同的会话 URL。ChatGPT 自动把长文本转换成附件时,CueLine 识别 `attachment_ready` 且最多点击一次;模糊点击记为 `possibly_sent`,绝不补点或重发。人工发送附件后,使用 `cueline run reconcile RUN_ID --request-id REQUEST_ID --manual-send-confirmed` 写入正式确认;仍须通过完全一致的 conversation、Pro 证据与 protocol/run/round/request identity。控制器证据优先使用成功且非空的 stdout,全局上限 12,000 字符;完整 stdout/stderr 保留在本地。
164
199
 
165
200
  ## 验证
166
201
 
@@ -177,7 +212,7 @@ npm pack --dry-run
177
212
 
178
213
  ## 0.1 的限制
179
214
 
180
- 仅支持纯文本。一次运行只对应一个会话。选成 `Pro` 是 CueLine 唯一会做的模型切换;不支持图片、不支持文件上传,也不支持 Deep Research、Projects 或 Apps。worker 一旦启动便没有自动重试或回退——失败的 `work` 作业会在副作用被标记为不确定后回报,因为 CueLine 无法证明它执行到了哪一步。macOS 是主要的桌面目标、Linux 是 CI 目标;Windows 未经验证,且 `install.sh` 不是 Windows 安装程序。adapter 依赖 ChatGPT 网页当前的界面,因此界面变更会以明确的 `COMPOSER_MISSING`、`SEND_BUTTON_MISSING` 或响应超时暴露出来——绝不会变成编造的答案。
215
+ 仅支持文本控制命令。一次运行只对应一个会话。支持 ChatGPT 自动将长文本转为附件,但不支持主动文件上传、图片、Deep Research、Projects 或 Apps。Caller 模式只允许 `advise`;`work` 必须显式选择 process executor。模糊发送和已启动工作都不会被自动重试。
181
216
 
182
217
  完整矩阵见 [compatibility](docs/compatibility.md)。
183
218
 
package/README.zh-TW.md CHANGED
@@ -11,19 +11,23 @@
11
11
  <a href="README.md">English</a> · <b>繁體中文</b> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a>
12
12
  </p>
13
13
 
14
- **CueLine 把方向盤交給一個已經開著的 ChatGPT 網頁對話:由它規劃整趟執行、喊出每一步;而 CueLine 負責檢查每一道指令,並在你這台機器上把事情真的做完。**
14
+ **CueLine 把方向盤交給一個已經開著的 ChatGPT 網頁對話:由它規劃整趟執行、喊出每一步;CueLine 檢查每一道文字指令,現在這個 Codex 才在本機執行獲准的工作。**
15
15
 
16
- 那個網頁碰不到你的機器。它每一輪只吐出一小段文字指令。CueLine 判斷這道指令格式對不對、屬不屬於這次執行、對應到哪個本機工作行程(worker)——然後才執行它、保留證據,再把證據交回去。
16
+ 那個網頁碰不到你的機器,也沒有本機工具。它每一輪只吐出文字控制指令。CueLine 預設把 `advise` 工作保存成待辦,交給現在這個 Codex 執行並提交結果;只有明確指定 `process` executor 時,才會啟動已註冊的本機工作行程。
17
17
 
18
18
  CueLine 是獨立實作,**沒有任何 runtime npm 相依套件**,也不是 Omnilane 或 GPT Relay 的包裝層。
19
19
 
20
20
  ## 一次執行實際上怎麼跑
21
21
 
22
- <img alt="一次 CueLine 執行像一本提示本:機器送出觀測,主控端喊出一道指令,已註冊的 runner 執行它,直到主控端喊 complete。" src="docs/assets/cueline-loop-zh-TW.svg" width="100%">
22
+ <img alt="Caller-first CueLine 執行:ChatGPT 下文字命令,目前的 Codex 做本機唯讀查驗,CueLine 回送有界證據直到 complete。" src="docs/assets/cueline-loop-zh-TW.svg" width="100%">
23
23
 
24
- 每一輪:CueLine 先把自己「即將問什麼」寫進紀錄,送出一份觀測(observation)到對話裡,再讀回**恰好一個** `<CueLineControl>` 封包。主控端從五個動作裡挑一個——`dispatch`、`wait`、`inspect`、`complete`、`blocked`——封包以外的任何文字都不會被執行。指令若寫錯 run、寫錯輪次,或工作定義有問題,會被退回去做有次數上限的修正,而不是靠猜。迴圈停在 `complete` `blocked`,或是輪數用完(預設 12 輪)。
24
+ 每一輪:CueLine 先把自己「即將問什麼」寫進紀錄,送出一份觀測(observation)到對話裡,之後再讀回**恰好一個** `<CueLineControl>` 封包。主控端從五個動作裡挑一個——`dispatch`、`wait`、`inspect`、`complete`、`blocked`——封包以外的任何文字都不會被執行。指令若寫錯 run、寫錯輪次,或工作定義有問題,會被退回去做有次數上限的修正,而不是靠猜。迴圈會在單次可靠送出後以 `awaiting_controller` 暫停,也會停在 caller 交接、`complete`、`blocked` 或輪數用完(預設 12 輪)。
25
25
 
26
- 主控端決定*該發生什麼*;本機這一側決定*能不能發生、怎麼發生*:通道(lane)必須啟用、候選項必須在任何程序啟動**之前**就確認可用、`argv[0]` 必須早已由你的路由設定註冊過。沒有任何東西會經過 shell。工作行程一旦啟動,就不會偷偷改用第二個候選項——失敗是以證據的形式回報,不是自動重試。
26
+ 非預設的 `maxRounds` 會在建立 run 時固定,並跨所有無 owner 的暫停累計主控總輪數。之後續跑通常省略它、沿用持久值;若傳入不同數字,CueLine 會拒絕,不會偷偷重設或放寬預算。
27
+
28
+ `startCueLineRun` 與 `runCueLine` 都預設使用 `caller` executor。使用內建瀏覽器時,CueLine 只送一次、保存精確對話 URL,然後回傳 `awaiting_controller` 並釋放 runtime lease,不會讓單一工具呼叫卡著等 Pro 思考。之後的 `continueCueLineRun` 只做一次唯讀觀測;若仍未完成,就再次回傳 `awaiting_controller`,絕不重送。`dispatch` 才會回傳 `awaiting_caller` 與持久化的 `advise` 工作;目前的 Codex 執行各項工作、呼叫 `submitCueLineCallerJobResult`,再沿用同一個 run 續跑。Pro 主控只負責下文字命令,沒有親自查 repo。Caller advise 沒有 execution claim,請協調由單一 session 執行;兩個 session 可能都做同一項查驗,但只有第一份送出的終態證據生效。Caller 模式會明確拒絕 `work`。
29
+
30
+ 若明確選擇 `executor: "process"`,通道(lane)必須啟用、候選項必須在任何程序啟動**之前**就確認可用、`argv[0]` 必須早已由你的路由設定註冊過。沒有任何東西會經過 shell。唯讀工作預設全域與每 lane 最多同時 2 個;只要批次包含 `work` 就維持串行。
27
31
 
28
32
  主控協定刻意區分路由層級:`lane` 填的是通道名稱 `default`;`codex-default` 是該通道內的候選執行器,不是通道。CueLine 會在註冊任何工作前先驗證整份 `dispatch`;只要包含無效通道或執行器,整份派工就會被退回修正,不會先執行其中一部分。
29
33
 
@@ -44,15 +48,15 @@ ChatGPT Pro 訂閱方案與「選定的 Pro 模型」是兩回事。帳號或個
44
48
  從 npm registry 安裝:
45
49
 
46
50
  ```bash
47
- npm install -g cueline@0.1.3
51
+ npm install -g cueline@0.1.4
48
52
  cueline install
49
53
  cueline doctor
50
54
  ```
51
55
 
52
- 作為備援,也可以安裝 [v0.1.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.3) 上的打包 tarball,該 release 同時附上它的 `.sha256` 校驗碼:
56
+ 作為備援,也可以安裝 [v0.1.4 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.1.4) 上的打包 tarball,該 release 同時附上它的 `.sha256` 校驗碼:
53
57
 
54
58
  ```bash
55
- npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.3/cueline-0.1.3.tgz
59
+ npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.1.4/cueline-0.1.4.tgz
56
60
  cueline install
57
61
  cueline doctor
58
62
  ```
@@ -84,39 +88,63 @@ cueline doctor
84
88
  ## 從程式碼驅動
85
89
 
86
90
  ```js
87
- import { createCodexIabAdapter, runCueLine } from "cueline";
88
-
89
- const result = await runCueLine({
91
+ import {
92
+ continueCueLineRun,
93
+ createCodexIabAdapter,
94
+ runCueLine,
95
+ submitCueLineCallerJobResult,
96
+ } from "cueline";
97
+
98
+ let result = await runCueLine({
90
99
  request: "Inspect the repository, delegate an implementation plan, and report the evidence.",
91
100
  browser: createCodexIabAdapter(),
92
101
  // 選填:conversationUrl、routingConfig / routingConfigPath、home、cwd、
93
102
  // runTimeoutMs、signal,以及各工作/預設期限。
94
- });
103
+ }); // 預設 executor: "caller"
104
+
105
+ while (result.status === "awaiting_controller" || result.status === "awaiting_caller") {
106
+ if (result.status === "awaiting_controller") {
107
+ await waitBeforeNextObservation(); // 有界退避;絕不重送
108
+ } else {
109
+ for (const job of result.pendingJobs ?? []) {
110
+ const stdout = await executeExactLocalAdvice(job.spec.task);
111
+ await submitCueLineCallerJobResult(result.runId, job.jobId, {
112
+ status: "succeeded",
113
+ stdout,
114
+ });
115
+ }
116
+ }
117
+ result = await continueCueLineRun({ runId: result.runId });
118
+ }
95
119
 
96
120
  if (result.status === "complete") {
97
121
  console.log(result.finalDeliveryText);
98
122
  }
99
123
  ```
100
124
 
125
+ 若回傳 `awaiting_controller`,代表同一個精確 request 已送出、但 Pro 回覆尚未被觀測;稍後續跑只會唯讀觀測,不會重送。若回傳 `awaiting_caller`,請由目前的 Codex 執行 `result.pendingJobs` 內每一項 `advise`,用 `submitCueLineCallerJobResult` 提交真實結果,再呼叫 `continueCueLineRun`。這不是 Pro 網頁直接使用本機工具。
126
+
101
127
  在 Codex 的 runtime 裡,import `cueline api path` 印出的那個絕對路徑模組——那就是你安裝的那份套件建置出來的 API。
102
128
 
103
- `startCueLineRun` 是明確的啟動入口(`runCueLine` 是它的別名)。`continueCueLineRun({ runId })` 會在同一個對話裡續跑被中斷的執行,並沿用已保存的對話網址,除非你另外傳入新的 adapter。`loadCueLineRunState(runId)` 只讀取已保存的狀態,不驅動任何東西。已經走到 `complete`、`blocked` 或 `cancelled` 的執行會原樣回傳,絕不會被再派工一次。續跑前先執行 `cueline run status <run-id> --json`;若回覆已採用且 phase `jobs_running`,代表 ChatGPT 已回覆,現在跑的是本機工作。
129
+ `startCueLineRun` 只建立持久 run 並回傳 `ready`;`runCueLine` 會建立並推進到持久 controller 觀測暫停、caller 交接或終態。`continueCueLineRun({ runId })` 沿用同一個對話。續跑前先執行 `cueline run status <run-id> --json`;缺少 owner `controller_response_pending` 若只有一個正常送出的回合且顯示 `safeNextAction: observe`,代表同一個 Pro 回覆尚待唯讀觀測,稍後續跑即可且不得重送。`safeNextAction: reconcile` 只保留給模糊、人工送出或多個待對帳回合。缺少 owner 的 `caller_jobs_pending` 是正常本機交接,不是 orphan,也不是還在等 ChatGPT
104
130
 
105
131
  ## CLI
106
132
 
107
- CLI 不驅動瀏覽器。它負責管理 skill 連結,並告訴你本機這一半健不健康。
133
+ CLI 不驅動瀏覽器。`doctor`、`routing`、`jobs`、`run status`、`api path`、`config path` 都是唯讀;`install`/`uninstall` 只改套件擁有的 skill 連結;`run reconcile`、`run takeover`、`run reconcile-runtime`、`run cancel`/`run stop`、`job cancel` 會追加稽核證據或修改持久 run/job 狀態。執行寫入狀態的命令前,先用 `cueline help` 核對完整參數。
108
134
 
109
135
  ```console
110
136
  $ cueline install
111
137
  CueLine skill installed: /Users/you/.codex/skills/cueline
112
138
 
113
139
  $ cueline doctor
114
- CueLine 0.1.3
140
+ CueLine 0.1.4
115
141
  status ok
116
142
  node 22.14.0 ok
117
143
  config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
118
144
  home /Users/you/.cueline
119
- available_lanes 1
145
+ caller_ready yes
146
+ caller_lanes 1
147
+ process_available_lanes 1
120
148
 
121
149
  $ cueline api path
122
150
  /usr/local/lib/node_modules/cueline/dist/src/api.js
@@ -128,7 +156,10 @@ $ cueline jobs
128
156
  No jobs.
129
157
 
130
158
  $ cueline run status run_... --json
131
- {"status":"running","phase":"jobs_running","runtime":{"ownership":"active"},...}
159
+ {"status":"running","executor":"caller","phase":"caller_jobs_pending","runtime":{"ownership":"missing"},...}
160
+
161
+ $ cueline run takeover stale_run_... --json
162
+ {"runId":"stale_run_...","outcome":"taken_over","next":"continue",...}
132
163
 
133
164
  $ cueline run cancel run_...
134
165
  run_... requested affected_jobs=0
@@ -140,19 +171,23 @@ $ cueline uninstall
140
171
  CueLine skill removed: /Users/you/.codex/skills/cueline
141
172
  ```
142
173
 
143
- Node 版本太舊、或沒有任何通道解析得出來時,`cueline doctor` 會以非零狀態結束,因此可以直接拿來當前置檢查。`cueline routing` 會告訴你某個通道為什麼不可用,而不是安靜地改選別的。`cueline api path` 印出的就是 skill 會 import 的模組,所以用打包安裝時完全不需要 clone 原始碼。`cueline help` 會列出全部。
174
+ Node 版本太舊、或沒有任何已啟用的 caller 通道時,`cueline doctor` 會以非零狀態結束。`process_available_lanes` 可以是 0 而不影響 caller 模式;只有明確選用 process executor 前才需要用 `cueline routing` 檢查 process 可用性。`cueline api path` 印出的就是 skill 會 import 的模組,所以用打包安裝時完全不需要 clone 原始碼。`cueline help` 會列出每個命令的精確語法,包括 `--json` 與人工 reconcile 的必要確認參數。
175
+
176
+ 只有 `run status` 明確顯示 stale owner 時才能用 `run takeover`。新鮮的 active heartbeat 會被拒絕;命令回傳 `next: continue` 或 `next: reconcile_runtime`,請照該值行動,不要自行猜測。
144
177
 
145
178
  ## 設定
146
179
 
147
180
  `CUELINE_CONFIG` 用來指定路由設定檔;`CUELINE_HOME` 用來搬動本機狀態(預設 `~/.cueline`)。
148
181
 
149
- 內建的 `default` 通道只有一個候選項 `codex-default`:以 stdin 傳入任務執行 `codex exec`,`advise` 用 `read-only`、`work` 用 `workspace-write`。要註冊別的工作行程,複製一份 [`config/routing.default.json`](config/routing.default.json)、加入你的候選項,再把 `CUELINE_CONFIG` 指過去——`argv[0]` 裡的執行檔就是透過這個動作被註冊的,而且它也必須在 `PATH` 上,通道才解析得出來。
182
+ Caller 模式不會啟動路由行程。只有明確選擇 `process` executor 時,內建的 `default` 通道才會以 `codex-default` 執行 `codex exec`;`advise` 用 `read-only`、`work` 用 `workspace-write`。
150
183
 
151
184
  狀態放在 `CUELINE_HOME` 底下:
152
185
 
153
186
  ```text
154
- runs/<run-id>/events.jsonl 只追加、具權威性
155
- runs/<run-id>/runtime.json owner heartbeat 證據
187
+ runs/<run-id>/events.jsonl + events.jsonl.segments/ 只追加、具權威性
188
+ runs/<run-id>/runtime.json.fence + runtime.json.epochs/ 有世代隔離的活 owner heartbeat 證據
189
+ runs/<run-id>/runtime.json.retired-owners/ 不可變的舊 owner 事件截止點
190
+ runs/<run-id>/runtime.json.takeover-intents/ 不可變的精確 takeover 嘗試紀錄
156
191
  runs/<run-id>/cancel.json 存在時代表持久取消要求
157
192
  runs/<run-id>/snapshot.json 重播的最佳化產物,可丟棄
158
193
  jobs/<job-id>.json 每個工作的執行證據
@@ -160,7 +195,9 @@ jobs/<job-id>.json 每個工作的執行證據
160
195
 
161
196
  事件日誌才是紀錄本身:主控端的這一輪在送出之前就先寫下、工作在行程啟動之前就先註冊,所以「意圖」與「副作用」之間若被中斷,會留下痕跡。壞掉的快照會被忽略、從第 1 號事件重建,而不是硬信它。
162
197
 
163
- 續跑只會重新接回該次執行記錄下來的那個對話網址,絕不接到長得像的分頁。對尚待處理的主控回合,它會先在該對話尋找與確切請求相符的已完成回覆;找到就以唯讀方式接回,不會重送。若舊狀態中同時有多個待處理回合,呼叫端必須明確指定一個。只有當唯一待處理提示能被證明為 `definitely_not_sent` 時,CueLine 才會自動重試;提交狀態不明或分頁消失時,會丟出 `TAB_RECOVERY_UNSAFE` 並停下。
198
+ 續跑只會接回完全相同的對話網址。ChatGPT 把長文字自動轉成附件時,CueLine 會辨識 `attachment_ready`,且最多只點一次;模糊點擊一律記為 `possibly_sent`,絕不補點或重送。若操作者手動送出附件,用 `cueline run reconcile RUN_ID --request-id REQUEST_ID --manual-send-confirmed` 寫入正式確認;之後仍須通過完全相同的 conversation、Pro 證據與 protocol/run/round/request identity 才能唯讀接回。
199
+
200
+ 送給主控的工作證據優先採用成功且非空的 stdout,全體共用 12,000 字元上限;完整 stdout/stderr 仍保留在本機 job status。
164
201
 
165
202
  ## 驗證
166
203
 
@@ -177,7 +214,7 @@ npm pack --dry-run
177
214
 
178
215
  ## 0.1 的限制
179
216
 
180
- 只支援純文字。一次執行只對應一個對話。選成 `Pro` 是 CueLine 唯一會做的模型切換;不支援圖片、不支援檔案上傳,也不支援 Deep Research、Projects 或 Apps。工作行程一旦啟動就沒有自動重試或改道——失敗的 `work` 工作會被標記為副作用不明確後回報,因為 CueLine 無法證明它做到哪裡。macOS 是主要的桌面目標、Linux 是 CI 目標;Windows 未經驗證,而且 `install.sh` 不是 Windows 安裝程式。adapter 依賴 ChatGPT 網頁目前的介面,所以介面一改,會以明確的 `COMPOSER_MISSING`、`SEND_BUTTON_MISSING` 或回應逾時浮現——絕不會變成捏造的答案。
217
+ 只支援文字控制命令。一次執行只對應一個對話。選成 `Pro` 是 CueLine 唯一會做的模型切換。支援 ChatGPT 自動把長文字轉成附件,但不支援主動上傳檔案、圖片、Deep Research、Projects 或 Apps。Caller 模式只允許 `advise`;`work` 必須明確選用 process executor。任何模糊送出或已啟動工作都不會被自動重試。
181
218
 
182
219
  完整矩陣見 [compatibility](docs/compatibility.md)。
183
220