cueline 0.4.1 → 0.4.3
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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +54 -0
- package/README.ja.md +6 -6
- package/README.ko.md +6 -6
- package/README.md +11 -7
- package/README.zh-CN.md +6 -6
- package/README.zh-TW.md +6 -6
- package/config/routing.multimodel.example.json +77 -0
- package/dist/src/api-controller-handoff.js +114 -23
- package/dist/src/api-controller-handoff.js.map +1 -1
- package/dist/src/api-run-prune.d.ts +11 -1
- package/dist/src/api-run-prune.js +21 -3
- package/dist/src/api-run-prune.js.map +1 -1
- package/dist/src/api.js +9 -0
- package/dist/src/api.js.map +1 -1
- package/dist/src/browser/browser-adapter.d.ts +2 -0
- package/dist/src/browser/codex-iab/chatgpt-client.js +31 -9
- package/dist/src/browser/codex-iab/chatgpt-client.js.map +1 -1
- package/dist/src/browser/validate-browser-adapter.d.ts +2 -0
- package/dist/src/browser/validate-browser-adapter.js +20 -0
- package/dist/src/browser/validate-browser-adapter.js.map +1 -0
- package/dist/src/cli/main.js +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/core/controller-loop.js +31 -7
- package/dist/src/core/controller-loop.js.map +1 -1
- package/dist/src/core/controller-types.d.ts +1 -0
- package/dist/src/core/ids.js +9 -0
- package/dist/src/core/ids.js.map +1 -1
- package/dist/src/{cli → core}/run-status-view.d.ts +6 -6
- package/dist/src/core/run-status-view.js.map +1 -0
- package/dist/src/core/state-machine.d.ts +3 -1
- package/dist/src/core/state-machine.js +8 -1
- package/dist/src/core/state-machine.js.map +1 -1
- package/dist/src/diagnostics/upgrade-preflight.d.ts +1 -0
- package/dist/src/diagnostics/upgrade-preflight.js +53 -2
- package/dist/src/diagnostics/upgrade-preflight.js.map +1 -1
- package/dist/src/jobs/status.d.ts +9 -0
- package/dist/src/jobs/status.js +25 -5
- package/dist/src/jobs/status.js.map +1 -1
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/observation/run-bundle.d.ts +1 -1
- package/dist/src/observation/run-bundle.js +1 -1
- package/dist/src/observation/run-bundle.js.map +1 -1
- package/dist/src/observation/run-timeline.js +16 -4
- package/dist/src/observation/run-timeline.js.map +1 -1
- package/dist/src/runners/process-runner.js +15 -0
- package/dist/src/runners/process-runner.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/multi-model-routing.md +41 -0
- package/package.json +1 -1
- package/schemas/cli-runs-prune.schema.json +1 -0
- package/dist/src/cli/run-status-view.js.map +0 -1
- /package/dist/src/{cli → core}/run-status-view.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueline",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueline",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
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"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.3 - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Reject injected browser objects before any durable continuation write unless
|
|
8
|
+
`sendTurn` is callable and split `submitTurn` / `observeTurn` methods are
|
|
9
|
+
supplied as a callable pair. The stable `BROWSER_ADAPTER_INVALID` error lists
|
|
10
|
+
only missing method names. A narrow legacy recovery recognizes only the exact
|
|
11
|
+
pre-submission `browser.sendTurn is not a function` event shape, requires a
|
|
12
|
+
fresh idle Pro observation of the exact conversation with the request absent,
|
|
13
|
+
and then abandons the old request for one round-preserving retry.
|
|
14
|
+
- Capture child stdin/stdout/stderr stream errors in the process runner. An
|
|
15
|
+
early-exiting worker can emit `EPIPE` while CueLine is still writing a large
|
|
16
|
+
stdin task; the stream error is now bounded into job evidence while the real
|
|
17
|
+
child exit status remains authoritative, instead of crashing the controller
|
|
18
|
+
loop and concurrent jobs.
|
|
19
|
+
|
|
20
|
+
### Verification
|
|
21
|
+
|
|
22
|
+
- Release candidate preflight passes all 649 tests plus metadata agreement,
|
|
23
|
+
typecheck, plugin validation, shell install tests, package contents, and diff
|
|
24
|
+
hygiene.
|
|
25
|
+
- Runtime regression proves an invalid built-in IAB module object returns
|
|
26
|
+
`BROWSER_ADAPTER_INVALID` with round, pending turns, and event sequence
|
|
27
|
+
unchanged; the legacy recovery fixture abandons and retries exactly once.
|
|
28
|
+
- A 5 MB stdin regression against an immediately exiting child completes
|
|
29
|
+
without an uncaught `EPIPE`.
|
|
30
|
+
|
|
31
|
+
## 0.4.2 - 2026-07-18
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Reuse CueLine's own leftover attachment when retrying an operator-confirmed
|
|
36
|
+
not-sent turn. A long controller prompt is auto-converted by ChatGPT into a
|
|
37
|
+
composer attachment; when the submit click was ambiguous and the operator
|
|
38
|
+
confirmed the prompt was not sent, that attachment stays in the composer. The
|
|
39
|
+
pre-existing-attachment guard previously refused it as foreign
|
|
40
|
+
(`CONTROLLER_PROMPT_NOT_READY`), deadlocking the run at `prompt_not_sent` /
|
|
41
|
+
`safeNextAction=retry`. The retry now reuses the single leftover attachment
|
|
42
|
+
only when it is provably CueLine's own — `notSentRecovery` is set,
|
|
43
|
+
`attachmentPromptExpected` is true, exactly one attachment is present, and
|
|
44
|
+
undoing the retry request-id swap reproduces the operator-confirmed
|
|
45
|
+
`promptHash` — skipping the re-fill instead of refusing. Any other
|
|
46
|
+
pre-existing attachment is still refused, so a user's own attachment is never
|
|
47
|
+
mixed in or cleared.
|
|
48
|
+
|
|
49
|
+
### Verification
|
|
50
|
+
|
|
51
|
+
- Red/green regression: a new unit test reuses the leftover attachment without
|
|
52
|
+
re-filling and clicks send exactly once; it failed against the old guard and
|
|
53
|
+
passes after the fix.
|
|
54
|
+
- Full suite green: unit 250/250, integration 304/304, smoke 6/6, `tsc
|
|
55
|
+
--noEmit` clean.
|
|
56
|
+
|
|
3
57
|
## 0.4.1 - 2026-07-18
|
|
4
58
|
|
|
5
59
|
### Fixed
|
package/README.ja.md
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
|
|
23
23
|
CueLine は独立した実装で、**ランタイムの npm 依存はゼロ**です。Omnilane のラッパーではありません。
|
|
24
24
|
|
|
25
|
-
## 最新リリース:0.4.
|
|
25
|
+
## 最新リリース:0.4.3
|
|
26
26
|
|
|
27
27
|
- 再起動後、完了済みの添付型 ChatGPT Pro 応答を受理できない問題を修正しました。新しいページのハイドレーションで永続 baseline より少ない履歴 assistant ノードしかマウントされない場合でも、正確な run/round/request envelope で応答を受理できます。exact URL、二重の Pro 証拠、user count、ハイドレーション、idle の安全チェックは fail-closed のままです。実際の round 35 は再送ゼロ・重複 job ゼロで復旧し、559/559 テストに合格しました。
|
|
28
28
|
|
|
29
|
-
詳細は [changelog](CHANGELOG.md#
|
|
29
|
+
詳細は [changelog](CHANGELOG.md#043---2026-07-19) またはバージョン指定の [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3) を参照してください。
|
|
30
30
|
|
|
31
31
|
## 1 回の実行は実際にどう進むか
|
|
32
32
|
|
|
@@ -67,15 +67,15 @@ ChatGPT Pro のサブスクリプションと、選択された Pro モデルは
|
|
|
67
67
|
npm レジストリからインストールします。
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
npm install -g cueline@0.4.
|
|
70
|
+
npm install -g cueline@0.4.3
|
|
71
71
|
cueline install
|
|
72
72
|
cueline doctor
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
フォールバックとして、[v0.4.
|
|
75
|
+
フォールバックとして、[v0.4.3 リリース](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3) のパッケージ済み tarball をインストールすることもできます。同じリリースに `.sha256` チェックサムも置いてあります。
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.
|
|
78
|
+
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.3/cueline-0.4.3.tgz
|
|
79
79
|
cueline install
|
|
80
80
|
cueline doctor
|
|
81
81
|
```
|
|
@@ -182,7 +182,7 @@ CLI はブラウザーを駆動しません。状態を書き込むコマンド
|
|
|
182
182
|
|
|
183
183
|
```console
|
|
184
184
|
$ cueline doctor
|
|
185
|
-
CueLine 0.4.
|
|
185
|
+
CueLine 0.4.3
|
|
186
186
|
status ok
|
|
187
187
|
node 22.14.0 ok
|
|
188
188
|
config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
|
package/README.ko.md
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
|
|
23
23
|
CueLine은 독립적인 구현이며 **런타임 npm 의존성이 전혀 없습니다**. Omnilane을 감싼 래퍼가 아닙니다.
|
|
24
24
|
|
|
25
|
-
## 최신 릴리스: 0.4.
|
|
25
|
+
## 최신 릴리스: 0.4.3
|
|
26
26
|
|
|
27
27
|
- 재시작 후 완료된 첨부형 ChatGPT Pro 응답을 수락하지 못하는 문제를 수정했습니다. 새 페이지 하이드레이션에서 영속 baseline보다 적은 과거 assistant 노드만 마운트되어도 정확한 run/round/request envelope로 응답을 수락합니다. exact URL, 이중 Pro 증거, user count, 하이드레이션, idle 안전 검사는 fail-closed 상태로 유지됩니다. 실제 round 35는 재전송과 중복 job 없이 복구되었고 559/559 테스트를 통과했습니다.
|
|
28
28
|
|
|
29
|
-
전체 내용은 [changelog](CHANGELOG.md#
|
|
29
|
+
전체 내용은 [changelog](CHANGELOG.md#043---2026-07-19) 또는 버전이 지정된 [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3)에서 확인할 수 있습니다.
|
|
30
30
|
|
|
31
31
|
## 실행 한 번은 실제로 이렇게 흘러갑니다
|
|
32
32
|
|
|
@@ -67,15 +67,15 @@ ChatGPT Pro 구독과 선택된 Pro 모델은 서로 다른 것입니다. 계정
|
|
|
67
67
|
npm 레지스트리에서 설치합니다:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
npm install -g cueline@0.4.
|
|
70
|
+
npm install -g cueline@0.4.3
|
|
71
71
|
cueline install
|
|
72
72
|
cueline doctor
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
대안으로, [v0.4.
|
|
75
|
+
대안으로, [v0.4.3 릴리스](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3)의 패키지 tarball을 설치할 수도 있습니다. 같은 릴리스에 `.sha256` 체크섬도 함께 있습니다.
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.
|
|
78
|
+
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.3/cueline-0.4.3.tgz
|
|
79
79
|
cueline install
|
|
80
80
|
cueline doctor
|
|
81
81
|
```
|
|
@@ -182,7 +182,7 @@ CLI는 브라우저를 구동하지 않습니다. 상태를 쓰는 명령 전에
|
|
|
182
182
|
|
|
183
183
|
```console
|
|
184
184
|
$ cueline doctor
|
|
185
|
-
CueLine 0.4.
|
|
185
|
+
CueLine 0.4.3
|
|
186
186
|
status ok
|
|
187
187
|
node 22.14.0 ok
|
|
188
188
|
config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
|
package/README.md
CHANGED
|
@@ -22,11 +22,15 @@ The web page never touches your machine and has no local tools. It only emits on
|
|
|
22
22
|
|
|
23
23
|
CueLine is a standalone implementation with **no runtime npm dependencies**. It is not a wrapper around Omnilane.
|
|
24
24
|
|
|
25
|
-
## Latest release: 0.4.
|
|
25
|
+
## Latest release: 0.4.3
|
|
26
26
|
|
|
27
|
-
-
|
|
27
|
+
- Rejects malformed injected browser adapters before any durable continuation
|
|
28
|
+
write and safely recovers the exact legacy pre-submission missing-method
|
|
29
|
+
failure only after fresh idle Pro evidence proves the request absent. Process
|
|
30
|
+
runners also contain child stdio `EPIPE` errors so one early-exiting worker
|
|
31
|
+
cannot crash the whole CueLine process.
|
|
28
32
|
|
|
29
|
-
Read the complete [changelog](CHANGELOG.md#
|
|
33
|
+
Read the complete [changelog](CHANGELOG.md#043---2026-07-19) or the versioned [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3).
|
|
30
34
|
|
|
31
35
|
## How a run actually goes
|
|
32
36
|
|
|
@@ -69,15 +73,15 @@ You need Node.js 22+, Codex with its built-in Browser, and — for the bundled d
|
|
|
69
73
|
Install from the npm registry:
|
|
70
74
|
|
|
71
75
|
```bash
|
|
72
|
-
npm install -g cueline@0.4.
|
|
76
|
+
npm install -g cueline@0.4.3
|
|
73
77
|
cueline install
|
|
74
78
|
cueline doctor
|
|
75
79
|
```
|
|
76
80
|
|
|
77
|
-
As a fallback, install the packaged tarball from the [v0.4.
|
|
81
|
+
As a fallback, install the packaged tarball from the [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3), which also carries its `.sha256` checksum:
|
|
78
82
|
|
|
79
83
|
```bash
|
|
80
|
-
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.
|
|
84
|
+
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.3/cueline-0.4.3.tgz
|
|
81
85
|
cueline install
|
|
82
86
|
cueline doctor
|
|
83
87
|
```
|
|
@@ -210,7 +214,7 @@ The CLI does not drive the browser. Run `cueline help` for every positional argu
|
|
|
210
214
|
|
|
211
215
|
```console
|
|
212
216
|
$ cueline doctor
|
|
213
|
-
CueLine 0.4.
|
|
217
|
+
CueLine 0.4.3
|
|
214
218
|
status ok
|
|
215
219
|
node 22.14.0 ok
|
|
216
220
|
config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
|
package/README.zh-CN.md
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
|
|
23
23
|
CueLine 是独立实现,**没有任何运行时 npm 依赖**,也不是 Omnilane 的包装层。
|
|
24
24
|
|
|
25
|
-
## 最新版本:0.4.
|
|
25
|
+
## 最新版本:0.4.3
|
|
26
26
|
|
|
27
27
|
- 修复重启后无法接收已完成附件型 ChatGPT Pro 回复的问题。即使全新页面水合后挂载的历史 assistant 节点少于持久 baseline,CueLine 也能用精确的 run/round/request envelope 接受回复;exact URL、双 Pro 证据、user count、水合与 idle 安全门仍保持 fail-closed。真实 round 35 已零重发、零重复 job 恢复;559/559 测试通过。
|
|
28
28
|
|
|
29
|
-
完整内容请查看 [changelog](CHANGELOG.md#
|
|
29
|
+
完整内容请查看 [changelog](CHANGELOG.md#043---2026-07-19) 或版本化的 [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3)。
|
|
30
30
|
|
|
31
31
|
## 一次运行实际是怎么走的
|
|
32
32
|
|
|
@@ -67,15 +67,15 @@ ChatGPT Pro 订阅套餐与“选定的 Pro 模型”是两回事。账号或个
|
|
|
67
67
|
从 npm registry 安装:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
npm install -g cueline@0.4.
|
|
70
|
+
npm install -g cueline@0.4.3
|
|
71
71
|
cueline install
|
|
72
72
|
cueline doctor
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
作为后备,也可以安装 [v0.4.
|
|
75
|
+
作为后备,也可以安装 [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3) 上的打包 tarball,该 release 同时附带它的 `.sha256` 校验值:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.
|
|
78
|
+
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.3/cueline-0.4.3.tgz
|
|
79
79
|
cueline install
|
|
80
80
|
cueline doctor
|
|
81
81
|
```
|
|
@@ -184,7 +184,7 @@ CLI 不驱动浏览器。执行写入状态的命令前,先用 `cueline help`
|
|
|
184
184
|
|
|
185
185
|
```console
|
|
186
186
|
$ cueline doctor
|
|
187
|
-
CueLine 0.4.
|
|
187
|
+
CueLine 0.4.3
|
|
188
188
|
status ok
|
|
189
189
|
node 22.14.0 ok
|
|
190
190
|
config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
|
package/README.zh-TW.md
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
|
|
23
23
|
CueLine 是獨立實作,**沒有任何 runtime npm 相依套件**,也不是 Omnilane 的包裝層。
|
|
24
24
|
|
|
25
|
-
## 最新版本:0.4.
|
|
25
|
+
## 最新版本:0.4.3
|
|
26
26
|
|
|
27
27
|
- 修復重開機後無法接收已完成附件型 ChatGPT Pro 回覆的問題。即使全新頁面水合後掛載的歷史 assistant 節點少於持久 baseline,CueLine 也能用精確的 run/round/request envelope 接受回覆;exact URL、雙 Pro 證據、user count、水合與 idle 安全閘門仍維持 fail-closed。真實 round 35 已零重送、零重複 job 復原;559/559 測試通過。
|
|
28
28
|
|
|
29
|
-
完整內容請看 [changelog](CHANGELOG.md#
|
|
29
|
+
完整內容請看 [changelog](CHANGELOG.md#043---2026-07-19) 或版本化的 [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3)。
|
|
30
30
|
|
|
31
31
|
## 一次執行實際上怎麼跑
|
|
32
32
|
|
|
@@ -67,15 +67,15 @@ ChatGPT Pro 訂閱方案與「選定的 Pro 模型」是兩回事。帳號或個
|
|
|
67
67
|
從 npm registry 安裝:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
npm install -g cueline@0.4.
|
|
70
|
+
npm install -g cueline@0.4.3
|
|
71
71
|
cueline install
|
|
72
72
|
cueline doctor
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
作為備援,也可以安裝 [v0.4.
|
|
75
|
+
作為備援,也可以安裝 [v0.4.3 release](https://github.com/Seraphim0916/cueline/releases/tag/v0.4.3) 上的打包 tarball,該 release 同時附上它的 `.sha256` 校驗碼:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.
|
|
78
|
+
npm install -g https://github.com/Seraphim0916/cueline/releases/download/v0.4.3/cueline-0.4.3.tgz
|
|
79
79
|
cueline install
|
|
80
80
|
cueline doctor
|
|
81
81
|
```
|
|
@@ -193,7 +193,7 @@ CLI 不驅動瀏覽器。執行寫入狀態的命令前,先用 `cueline help`
|
|
|
193
193
|
|
|
194
194
|
```console
|
|
195
195
|
$ cueline doctor
|
|
196
|
-
CueLine 0.4.
|
|
196
|
+
CueLine 0.4.3
|
|
197
197
|
status ok
|
|
198
198
|
node 22.14.0 ok
|
|
199
199
|
config /usr/local/lib/node_modules/cueline/config/routing.default.json valid
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./routing.schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"lanes": {
|
|
5
|
+
"default": {
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"candidates": [
|
|
8
|
+
{
|
|
9
|
+
"id": "codex-default",
|
|
10
|
+
"argv": [
|
|
11
|
+
"codex",
|
|
12
|
+
"exec",
|
|
13
|
+
"--ignore-user-config",
|
|
14
|
+
"--skip-git-repo-check",
|
|
15
|
+
"--ephemeral",
|
|
16
|
+
"--color",
|
|
17
|
+
"never",
|
|
18
|
+
"-C",
|
|
19
|
+
"{workdir}",
|
|
20
|
+
"-s",
|
|
21
|
+
"{sandbox}",
|
|
22
|
+
"-"
|
|
23
|
+
],
|
|
24
|
+
"task_input": "stdin"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"live-search": {
|
|
29
|
+
"enabled": true,
|
|
30
|
+
"candidates": [
|
|
31
|
+
{
|
|
32
|
+
"id": "grok-advise-search",
|
|
33
|
+
"argv": [
|
|
34
|
+
"grok",
|
|
35
|
+
"--no-memory",
|
|
36
|
+
"--no-subagents",
|
|
37
|
+
"--no-plan",
|
|
38
|
+
"--no-alt-screen",
|
|
39
|
+
"--output-format",
|
|
40
|
+
"plain",
|
|
41
|
+
"--verbatim",
|
|
42
|
+
"--permission-mode",
|
|
43
|
+
"plan",
|
|
44
|
+
"-p",
|
|
45
|
+
"{task}"
|
|
46
|
+
],
|
|
47
|
+
"task_input": "argv"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"taste-final": {
|
|
52
|
+
"enabled": true,
|
|
53
|
+
"candidates": [
|
|
54
|
+
{
|
|
55
|
+
"id": "claude-opus-4-8-advise",
|
|
56
|
+
"argv": [
|
|
57
|
+
"claude",
|
|
58
|
+
"--disable-slash-commands",
|
|
59
|
+
"--model",
|
|
60
|
+
"claude-opus-4-8",
|
|
61
|
+
"--output-format",
|
|
62
|
+
"text",
|
|
63
|
+
"--effort",
|
|
64
|
+
"high",
|
|
65
|
+
"--tools",
|
|
66
|
+
"Read",
|
|
67
|
+
"Glob",
|
|
68
|
+
"Grep",
|
|
69
|
+
"-p",
|
|
70
|
+
"{task}"
|
|
71
|
+
],
|
|
72
|
+
"task_input": "argv"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -8,7 +8,74 @@ import { JobStatusStore } from "./jobs/status.js";
|
|
|
8
8
|
import { defaultCueLineHome } from "./state/paths.js";
|
|
9
9
|
import { readRuntimeLease, retireDeadRuntimeLease, RuntimeLease, } from "./state/runtime-lease.js";
|
|
10
10
|
import { readAuthoritativeRunEvents } from "./state/store.js";
|
|
11
|
+
import { readCancellationObservation } from "./state/cancellation.js";
|
|
11
12
|
import { isDefinitelyNotSentObservation, isSubmittedTurnRecoveryCandidate, } from "./core/submitted-turn-recovery.js";
|
|
13
|
+
function eventPayload(event) {
|
|
14
|
+
return typeof event.payload === "object" &&
|
|
15
|
+
event.payload !== null &&
|
|
16
|
+
!Array.isArray(event.payload)
|
|
17
|
+
? event.payload
|
|
18
|
+
: {};
|
|
19
|
+
}
|
|
20
|
+
function isLegacyPreSubmissionAdapterFailure(events, turn) {
|
|
21
|
+
if (turn.submissionState !== "possibly_sent" ||
|
|
22
|
+
turn.selectedModelLabel !== null ||
|
|
23
|
+
turn.baselineUserMessageCount !== null ||
|
|
24
|
+
turn.baselineAssistantMessageCount !== null ||
|
|
25
|
+
turn.baselineLastUserMessageHash !== null ||
|
|
26
|
+
turn.composerPromptState !== null ||
|
|
27
|
+
turn.submissionCheckpointContract !== null) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const requestedIndexes = events.flatMap((event, index) => {
|
|
31
|
+
const payload = eventPayload(event);
|
|
32
|
+
return event.type === "controller_turn_requested" &&
|
|
33
|
+
payload.request_id === turn.requestId &&
|
|
34
|
+
payload.round === turn.round
|
|
35
|
+
? [index]
|
|
36
|
+
: [];
|
|
37
|
+
});
|
|
38
|
+
if (requestedIndexes.length !== 1)
|
|
39
|
+
return false;
|
|
40
|
+
const requestedIndex = requestedIndexes[0];
|
|
41
|
+
const immediateFailure = events[requestedIndex + 1];
|
|
42
|
+
if (immediateFailure?.type !== "run_failed")
|
|
43
|
+
return false;
|
|
44
|
+
const failure = eventPayload(immediateFailure);
|
|
45
|
+
if (failure.code !== "CUELINE_INTERNAL" ||
|
|
46
|
+
failure.message !== "browser.sendTurn is not a function" ||
|
|
47
|
+
failure.request_id !== turn.requestId ||
|
|
48
|
+
failure.stage !== "controller_turn" ||
|
|
49
|
+
failure.submission_state !== "requested") {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return !events.slice(requestedIndex + 1).some((event) => {
|
|
53
|
+
const payload = eventPayload(event);
|
|
54
|
+
if ((event.type === "controller_turn_submission_started" ||
|
|
55
|
+
event.type === "controller_turn_submitted" ||
|
|
56
|
+
event.type === "controller_response_received") &&
|
|
57
|
+
payload.request_id === turn.requestId) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (event.type !== "controller_command_accepted")
|
|
61
|
+
return false;
|
|
62
|
+
const command = typeof payload.command === "object" &&
|
|
63
|
+
payload.command !== null &&
|
|
64
|
+
!Array.isArray(payload.command)
|
|
65
|
+
? payload.command
|
|
66
|
+
: {};
|
|
67
|
+
return (command.request_id === turn.requestId ||
|
|
68
|
+
(typeof command.round === "number" && command.round >= turn.round));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function isLegacyDefinitelyNotSentObservation(evidence, conversationUrl) {
|
|
72
|
+
return (sameChatGptConversationUrl(evidence.conversationUrl, conversationUrl) &&
|
|
73
|
+
/^Pro(?:\s|$)/i.test(evidence.selectedModelLabel ?? "") &&
|
|
74
|
+
evidence.hydrated === true &&
|
|
75
|
+
evidence.requestMessageFound === false &&
|
|
76
|
+
evidence.isAnswering === false &&
|
|
77
|
+
evidence.observedUserMessageCount !== null);
|
|
78
|
+
}
|
|
12
79
|
export async function confirmManualControllerSubmission(runId, options) {
|
|
13
80
|
const environment = options.environment ?? runtimeEnvironment();
|
|
14
81
|
const home = options.home ?? defaultCueLineHome(environment);
|
|
@@ -149,6 +216,11 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
149
216
|
if ((state.pendingControllerTurns ?? []).length !== 1) {
|
|
150
217
|
throw new CueLineError("OTHER_CONTROLLER_TURNS_PENDING", "Operator-confirmed not-sent recovery requires exactly one pending controller turn.");
|
|
151
218
|
}
|
|
219
|
+
const cancellation = await readCancellationObservation(home, runId);
|
|
220
|
+
if (cancellation.runRequested || cancellation.jobRequests.length > 0) {
|
|
221
|
+
throw new CueLineError("CONTROLLER_NOT_SENT_STATE_INVALID", "Not-sent recovery is forbidden while run or job cancellation is pending.");
|
|
222
|
+
}
|
|
223
|
+
const events = await readAuthoritativeRunEvents(home, runId);
|
|
152
224
|
const exactAmbiguousFailure = turn.submissionState !== "possibly_sent" ||
|
|
153
225
|
state.lastFailure?.code !== "CONTROLLER_SUBMISSION_AMBIGUOUS" ||
|
|
154
226
|
state.lastFailure.requestId !== turn.requestId
|
|
@@ -160,7 +232,12 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
160
232
|
const evidenceGatedSubmittedTurn = options.browser !== undefined &&
|
|
161
233
|
candidateConversationUrl !== undefined &&
|
|
162
234
|
isSubmittedTurnRecoveryCandidate(turn, candidateConversationUrl);
|
|
163
|
-
|
|
235
|
+
const legacyPreSubmissionFailure = options.browser !== undefined &&
|
|
236
|
+
candidateConversationUrl !== undefined &&
|
|
237
|
+
isLegacyPreSubmissionAdapterFailure(events, turn);
|
|
238
|
+
if (!exactAmbiguousFailure &&
|
|
239
|
+
!evidenceGatedSubmittedTurn &&
|
|
240
|
+
!legacyPreSubmissionFailure) {
|
|
164
241
|
throw new CueLineError("CONTROLLER_NOT_SENT_STATE_INVALID", "Not-sent recovery requires either the exact ambiguous submission failure or a fresh evidence-gated submitted turn.");
|
|
165
242
|
}
|
|
166
243
|
if (turn.retryOfRequestId !== undefined && turn.retryOfRequestId !== null) {
|
|
@@ -181,19 +258,15 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
181
258
|
if (!isExactChatGptConversationUrl(conversationUrl)) {
|
|
182
259
|
throw new CueLineError("CONTROLLER_RECONCILIATION_CONVERSATION_MISMATCH", "Operator-confirmed not-sent recovery requires an exact ChatGPT conversation URL.");
|
|
183
260
|
}
|
|
184
|
-
if (
|
|
261
|
+
if (!legacyPreSubmissionFailure &&
|
|
262
|
+
(turn.selectedModelLabel === null || !/^Pro(?:\s|$)/i.test(turn.selectedModelLabel))) {
|
|
185
263
|
throw new CueLineError("CONTROLLER_RECONCILIATION_MODEL_UNVERIFIED", "The pending turn lacks exact Pro composer model evidence.");
|
|
186
264
|
}
|
|
187
265
|
if (!/^[0-9a-f]{64}$/.test(turn.promptHash)) {
|
|
188
266
|
throw new CueLineError("CONTROLLER_RECONCILIATION_PROMPT_HASH_INVALID", "The pending turn lacks a valid prompt hash.");
|
|
189
267
|
}
|
|
190
|
-
const events = await readAuthoritativeRunEvents(home, runId);
|
|
191
268
|
for (const event of events) {
|
|
192
|
-
const payload =
|
|
193
|
-
event.payload !== null &&
|
|
194
|
-
!Array.isArray(event.payload)
|
|
195
|
-
? event.payload
|
|
196
|
-
: {};
|
|
269
|
+
const payload = eventPayload(event);
|
|
197
270
|
if (event.type === "controller_response_received" &&
|
|
198
271
|
payload.request_id === options.requestId) {
|
|
199
272
|
throw new CueLineError("CONTROLLER_RECONCILIATION_SUPERSEDED", "A controller response for this request was already received; refusing not-sent recovery.");
|
|
@@ -211,7 +284,7 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
211
284
|
}
|
|
212
285
|
}
|
|
213
286
|
let submittedEvidence;
|
|
214
|
-
if (evidenceGatedSubmittedTurn) {
|
|
287
|
+
if (evidenceGatedSubmittedTurn || legacyPreSubmissionFailure) {
|
|
215
288
|
const browser = options.browser;
|
|
216
289
|
if (browser.observeSubmittedTurn === undefined) {
|
|
217
290
|
throw new CueLineError("CONTROLLER_NOT_SENT_EVIDENCE_REQUIRED", "The normally submitted turn requires a fresh read-only Browser observation before it can be confirmed not sent.");
|
|
@@ -221,16 +294,23 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
221
294
|
round: turn.round,
|
|
222
295
|
requestId: turn.requestId,
|
|
223
296
|
prompt: turn.prompt,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
297
|
+
...(typeof turn.baselineUserMessageCount === "number"
|
|
298
|
+
? { baselineUserMessageCount: turn.baselineUserMessageCount }
|
|
299
|
+
: {}),
|
|
300
|
+
...(typeof turn.baselineAssistantMessageCount === "number"
|
|
301
|
+
? { baselineAssistantMessageCount: turn.baselineAssistantMessageCount }
|
|
302
|
+
: {}),
|
|
228
303
|
...(turn.composerPromptState === "attachment_ready"
|
|
229
304
|
? { attachmentPromptExpected: true }
|
|
230
305
|
: {}),
|
|
306
|
+
...(legacyPreSubmissionFailure
|
|
307
|
+
? { legacyPreSubmissionRecovery: true }
|
|
308
|
+
: {}),
|
|
231
309
|
});
|
|
232
310
|
if (observation.status !== "definitely_not_sent" ||
|
|
233
|
-
!
|
|
311
|
+
!(legacyPreSubmissionFailure
|
|
312
|
+
? isLegacyDefinitelyNotSentObservation(observation.evidence, conversationUrl)
|
|
313
|
+
: isDefinitelyNotSentObservation(turn, conversationUrl, observation.evidence))) {
|
|
234
314
|
throw new CueLineError("CONTROLLER_NOT_SENT_EVIDENCE_INSUFFICIENT", "The fresh conversation observation did not prove baseline-equal, request-absent, Pro-idle not-sent evidence.");
|
|
235
315
|
}
|
|
236
316
|
submittedEvidence = observation.evidence;
|
|
@@ -246,14 +326,19 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
246
326
|
: {};
|
|
247
327
|
return typeof payload.round === "number" && payload.round < turn.round;
|
|
248
328
|
}).length;
|
|
329
|
+
const selectedModelLabel = submittedEvidence?.selectedModelLabel ?? turn.selectedModelLabel;
|
|
330
|
+
if (selectedModelLabel === null) {
|
|
331
|
+
throw new CueLineError("CONTROLLER_RECONCILIATION_MODEL_UNVERIFIED", "Not-sent recovery lacks exact Pro composer model evidence.");
|
|
332
|
+
}
|
|
333
|
+
const recoveryBaselineUserMessageCount = submittedEvidence?.baselineUserMessageCount ?? derivedBaselineUserMessageCount;
|
|
249
334
|
if (existingRecovery === null) {
|
|
250
335
|
await store.append("controller_turn_not_sent_confirmed", {
|
|
251
336
|
round: turn.round,
|
|
252
337
|
request_id: turn.requestId,
|
|
253
338
|
prompt_hash: turn.promptHash,
|
|
254
339
|
conversation_url: conversationUrl,
|
|
255
|
-
selected_model_label:
|
|
256
|
-
baseline_user_message_count:
|
|
340
|
+
selected_model_label: selectedModelLabel,
|
|
341
|
+
baseline_user_message_count: recoveryBaselineUserMessageCount,
|
|
257
342
|
...(submittedEvidence === undefined
|
|
258
343
|
? { operator_confirmation: true }
|
|
259
344
|
: {
|
|
@@ -262,8 +347,10 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
262
347
|
is_answering: false,
|
|
263
348
|
page_hydrated: true,
|
|
264
349
|
submission_state: "definitely_not_sent",
|
|
265
|
-
confirmation_source:
|
|
266
|
-
|
|
350
|
+
confirmation_source: legacyPreSubmissionFailure
|
|
351
|
+
? "legacy_pre_submission_adapter_failure"
|
|
352
|
+
: "fresh_read_only_observation",
|
|
353
|
+
operator_confirmation: !legacyPreSubmissionFailure,
|
|
267
354
|
}),
|
|
268
355
|
});
|
|
269
356
|
}
|
|
@@ -271,12 +358,14 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
271
358
|
await store.append("controller_turn_abandoned", {
|
|
272
359
|
round: turn.round,
|
|
273
360
|
request_id: turn.requestId,
|
|
274
|
-
reason:
|
|
361
|
+
reason: legacyPreSubmissionFailure
|
|
362
|
+
? "legacy_pre_submission_adapter_failure"
|
|
363
|
+
: "operator_confirmed_not_sent",
|
|
275
364
|
round_not_consumed: true,
|
|
276
365
|
prompt_hash: turn.promptHash,
|
|
277
366
|
conversation_url: conversationUrl,
|
|
278
|
-
selected_model_label:
|
|
279
|
-
baseline_user_message_count:
|
|
367
|
+
selected_model_label: selectedModelLabel,
|
|
368
|
+
baseline_user_message_count: recoveryBaselineUserMessageCount,
|
|
280
369
|
...(submittedEvidence === undefined
|
|
281
370
|
? { operator_confirmation: true }
|
|
282
371
|
: {
|
|
@@ -285,8 +374,10 @@ export async function confirmControllerTurnNotSent(runId, options) {
|
|
|
285
374
|
is_answering: false,
|
|
286
375
|
page_hydrated: true,
|
|
287
376
|
submission_state: "definitely_not_sent",
|
|
288
|
-
confirmation_source:
|
|
289
|
-
|
|
377
|
+
confirmation_source: legacyPreSubmissionFailure
|
|
378
|
+
? "legacy_pre_submission_adapter_failure"
|
|
379
|
+
: "fresh_read_only_observation",
|
|
380
|
+
operator_confirmation: !legacyPreSubmissionFailure,
|
|
290
381
|
}),
|
|
291
382
|
});
|
|
292
383
|
}
|