codex-overleaf-link 1.3.8 → 1.3.9
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/README.md +21 -21
- package/extension/src/shared/agentTranscript.js +33 -13
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/failureReasons.js +16 -1
- package/extension/src/shared/i18n.js +14 -2
- package/extension/src/shared/pathRedaction.js +2 -28
- package/extension/src/shared/sensitiveScan.js +1 -4
- package/extension/src/shared/sessionState.js +4 -4
- package/extension/src/shared/staleGuard.js +0 -2
- package/extension/src/shared/storageDb.js +3 -3
- package/native-host/src/codexSessionRunner.js +40 -0
- package/native-host/src/taskRunnerRuntime.js +53 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<h1>Codex Overleaf Link</h1>
|
|
4
4
|
<p><strong>Empower Overleaf with Codex.</strong></p>
|
|
5
5
|
<p>
|
|
6
|
-
<img src="https://img.shields.io/badge/version-1.3.
|
|
6
|
+
<img src="https://img.shields.io/badge/version-1.3.9-blue" alt="version">
|
|
7
7
|
<img src="https://img.shields.io/badge/platform-macOS%20%2F%20Windows%20%2F%20Linux-lightgrey" alt="platform">
|
|
8
8
|
<img src="https://img.shields.io/badge/chrome-MV3-green" alt="chrome manifest v3">
|
|
9
9
|
<img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="node version">
|
|
@@ -38,14 +38,14 @@ One command installs the native host **and** sets up the extension: the script r
|
|
|
38
38
|
macOS / Linux:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
CODEX_OVERLEAF_REF=v1.3.
|
|
41
|
+
CODEX_OVERLEAF_REF=v1.3.9 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.9/install.sh)"
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Windows PowerShell:
|
|
45
45
|
|
|
46
46
|
```powershell
|
|
47
|
-
iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.
|
|
48
|
-
$env:CODEX_OVERLEAF_REF='v1.3.
|
|
47
|
+
iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.9/install.ps1 -OutFile install.ps1
|
|
48
|
+
$env:CODEX_OVERLEAF_REF='v1.3.9'
|
|
49
49
|
powershell -ExecutionPolicy Bypass -File install.ps1
|
|
50
50
|
```
|
|
51
51
|
|
|
@@ -56,10 +56,10 @@ Then, in the `chrome://extensions` tab the script opened: enable **Developer mod
|
|
|
56
56
|
`npm exec` installs and updates the **native host only** — it does not include the Chrome extension. Use it if you prefer a pinned npm package to a source checkout.
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
npm exec --yes codex-overleaf-link@1.3.
|
|
59
|
+
npm exec --yes codex-overleaf-link@1.3.9 -- install-native
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Then add the extension yourself: download `codex-overleaf-link-extension-v1.3.
|
|
62
|
+
Then add the extension yourself: download `codex-overleaf-link-extension-v1.3.9.zip` from the [v1.3.9 GitHub Release](https://github.com/Ghqqqq/codex-overleaf-link/releases/tag/v1.3.9), unzip it to a stable folder, and in `chrome://extensions` enable **Developer mode**, click **Load unpacked**, and select that folder.
|
|
63
63
|
|
|
64
64
|
### Open Overleaf
|
|
65
65
|
|
|
@@ -86,9 +86,9 @@ npm installs, updates, uninstalls, and diagnoses the native host only. npm does
|
|
|
86
86
|
|
|
87
87
|
| Action | Command |
|
|
88
88
|
|--------|---------|
|
|
89
|
-
| Install / update | `npm exec --yes codex-overleaf-link@1.3.
|
|
90
|
-
| Diagnose | `npm exec --yes codex-overleaf-link@1.3.
|
|
91
|
-
| Uninstall | `npm exec --yes codex-overleaf-link@1.3.
|
|
89
|
+
| Install / update | `npm exec --yes codex-overleaf-link@1.3.9 -- install-native` |
|
|
90
|
+
| Diagnose | `npm exec --yes codex-overleaf-link@1.3.9 -- doctor` |
|
|
91
|
+
| Uninstall | `npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native` |
|
|
92
92
|
|
|
93
93
|
Use `--extension-id <chrome-extension-id>` only for a custom/dev unpacked extension id that differs from the official bundled id.
|
|
94
94
|
|
|
@@ -98,13 +98,13 @@ To update, re-run any of the [native host installers](#install) — they install
|
|
|
98
98
|
|
|
99
99
|
## GitHub Release Artifacts
|
|
100
100
|
|
|
101
|
-
The v1.3.
|
|
101
|
+
The v1.3.9 GitHub Release contains:
|
|
102
102
|
|
|
103
|
-
- `codex-overleaf-link-extension-v1.3.
|
|
104
|
-
- `codex-overleaf-native-host-v1.3.
|
|
105
|
-
- `codex-overleaf-link-1.3.
|
|
106
|
-
- `install.sh`: release-pinned macOS / Linux installer that defaults to `v1.3.
|
|
107
|
-
- `install.ps1`: release-pinned Windows PowerShell installer that defaults to `v1.3.
|
|
103
|
+
- `codex-overleaf-link-extension-v1.3.9.zip`: loadable Chrome extension package for manual unpacked installation.
|
|
104
|
+
- `codex-overleaf-native-host-v1.3.9.tar.gz`: native host runtime files used by the installer and release verification.
|
|
105
|
+
- `codex-overleaf-link-1.3.9.tgz`: npm native host CLI package for pinned install, doctor, and uninstall flows.
|
|
106
|
+
- `install.sh`: release-pinned macOS / Linux installer that defaults to `v1.3.9` when run directly from the release artifact.
|
|
107
|
+
- `install.ps1`: release-pinned Windows PowerShell installer that defaults to `v1.3.9` when run directly from the release artifact.
|
|
108
108
|
- `uninstall-native-host.mjs`: native host uninstaller that removes the Chrome Native Messaging manifest, bridge executable, and runtime copy.
|
|
109
109
|
- `nativeHostPlatform.js`, `manifest.js`, `runtimeInstaller.js`: helper files required by the loose uninstaller asset.
|
|
110
110
|
- `SHA256SUMS` and `release-manifest.json`: checksum and artifact metadata for release verification.
|
|
@@ -115,7 +115,7 @@ The v1.3.8 GitHub Release contains:
|
|
|
115
115
|
Remove the native host (use `--browser chromium` on Linux Chromium):
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
npm exec --yes codex-overleaf-link@1.3.
|
|
118
|
+
npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
The same command works on Windows PowerShell. If you installed from a manual checkout or source installer, you can also run `npm run uninstall:native` inside the repo, use `node ~/.codex-overleaf/source/scripts/uninstall-native-host.mjs` on macOS / Linux, or use `node $env:LOCALAPPDATA\CodexOverleaf\source\scripts\uninstall-native-host.mjs` on Windows PowerShell.
|
|
@@ -150,13 +150,13 @@ Then remove the extension from `chrome://extensions`. To delete local data: on m
|
|
|
150
150
|
Linux Chromium install or update:
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
|
-
CODEX_OVERLEAF_REF=v1.3.
|
|
153
|
+
CODEX_OVERLEAF_REF=v1.3.9 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v1.3.9/install.sh)" -- --browser chromium
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
Linux Chromium uninstall:
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
|
-
npm exec --yes codex-overleaf-link@1.3.
|
|
159
|
+
npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native --browser chromium
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
## Features
|
|
@@ -289,7 +289,7 @@ Composer attachments are turn-scoped Codex context. Limits are 8 attachments per
|
|
|
289
289
|
Re-run any [native host installer](#install), reload the extension in `chrome://extensions`, then refresh the Overleaf tab. This also fixes extension/native version mismatch and native protocol mismatch.
|
|
290
290
|
|
|
291
291
|
```bash
|
|
292
|
-
npm exec --yes codex-overleaf-link@1.3.
|
|
292
|
+
npm exec --yes codex-overleaf-link@1.3.9 -- install-native
|
|
293
293
|
```
|
|
294
294
|
|
|
295
295
|
**The Windows popup or panel shows a Bash recovery command**
|
|
@@ -338,8 +338,8 @@ Use this matrix for release-candidate signoff and compatibility reports. Record
|
|
|
338
338
|
| Browser/channel/version | Google Chrome channel and version. | Google Chrome channel and version. | Google Chrome channel and version. | Chromium channel/package and version. |
|
|
339
339
|
| Install mode | Manual unpacked extension from GitHub Release zip or checkout. | Manual unpacked extension from GitHub Release zip or checkout. | Manual unpacked extension from GitHub Release zip or checkout. | Manual unpacked extension from GitHub Release zip or checkout; native host installed with `--browser chromium`. |
|
|
340
340
|
| Extension id | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. |
|
|
341
|
-
| Installer/update command | `npm exec --yes codex-overleaf-link@1.3.
|
|
342
|
-
| Uninstall command | `npm exec --yes codex-overleaf-link@1.3.
|
|
341
|
+
| Installer/update command | `npm exec --yes codex-overleaf-link@1.3.9 -- install-native` | `npm exec --yes codex-overleaf-link@1.3.9 -- install-native` | `npm exec --yes codex-overleaf-link@1.3.9 -- install-native` | `npm exec --yes codex-overleaf-link@1.3.9 -- install-native --browser chromium` |
|
|
342
|
+
| Uninstall command | `npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native` | `npm exec --yes codex-overleaf-link@1.3.9 -- uninstall-native --browser chromium` |
|
|
343
343
|
| Manifest/registry path | `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.codex.overleaf.json` | `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.codex.overleaf` -> `%LOCALAPPDATA%\CodexOverleaf\native-host-runtime\com.codex.overleaf.json` | `~/.config/google-chrome/NativeMessagingHosts/com.codex.overleaf.json` | `~/.config/chromium/NativeMessagingHosts/com.codex.overleaf.json` |
|
|
344
344
|
| Bridge/runtime/source path | Bridge `~/.codex-overleaf/codex-overleaf-bridge`; runtime `~/.codex-overleaf/native-host-runtime`; source `~/.codex-overleaf/source`. | Bridge `%LOCALAPPDATA%\CodexOverleaf\codex-overleaf-bridge.cmd`; runtime `%LOCALAPPDATA%\CodexOverleaf\native-host-runtime`; source `%LOCALAPPDATA%\CodexOverleaf\source`. | Bridge `~/.codex-overleaf/codex-overleaf-bridge`; runtime `~/.codex-overleaf/native-host-runtime`; source `~/.codex-overleaf/source`. | Bridge `~/.codex-overleaf/codex-overleaf-bridge`; runtime `~/.codex-overleaf/native-host-runtime`; source `~/.codex-overleaf/source`. |
|
|
345
345
|
| Node/Git/Codex/TeX | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. | Node.js >= 20; Git; Codex CLI installed and logged in; TeX optional. |
|
|
@@ -502,40 +502,53 @@
|
|
|
502
502
|
function translateRawError(message, context = {}) {
|
|
503
503
|
const locale = normalizeLocale(context);
|
|
504
504
|
const text = String(message || '');
|
|
505
|
+
// Each branch returns the user-visible {conclusion, nextStep} pair and,
|
|
506
|
+
// when the error maps to a structured FailureReason code in the v1.3.8
|
|
507
|
+
// catalog, the `failureCode`. Callers that emit a content-side failure
|
|
508
|
+
// event can attach the catalog code so the run record carries structured
|
|
509
|
+
// failure data alongside the human-readable text. The string text is kept
|
|
510
|
+
// verbatim because it has mode-aware nuance (e.g. "No files were written"
|
|
511
|
+
// vs "This run did not start") the catalog's fallback strings don't capture.
|
|
505
512
|
if (/Mode must be "confirm" or "auto"/i.test(text)) {
|
|
506
513
|
return {
|
|
507
514
|
conclusion: textFor(locale, '这轮没有写入:当前是“只问不改”,但这个任务需要写入权限。', 'No files were written: this task needs write access, but the current mode is Ask.'),
|
|
508
|
-
nextStep: textFor(locale, '请切换到“建议修改”或“自动写入”后重新运行。', 'Switch to Suggest or Auto and run the task again.')
|
|
515
|
+
nextStep: textFor(locale, '请切换到“建议修改”或“自动写入”后重新运行。', 'Switch to Suggest or Auto and run the task again.'),
|
|
516
|
+
failureCode: null // no direct catalog match — preflight / governance
|
|
509
517
|
};
|
|
510
518
|
}
|
|
511
519
|
if (/Agent returned invalid JSON/i.test(text)) {
|
|
512
520
|
return {
|
|
513
521
|
conclusion: textFor(locale, '这轮没有写入:Codex 已结束,但本地桥接器没有读到可用结果。', 'No files were written: Codex finished, but the local bridge did not receive a usable result.'),
|
|
514
|
-
nextStep: textFor(locale, '请重新运行一次;如果再次失败,请打开技术详情查看本地 Codex 输出。', 'Run it again. If it fails again, open Technical Details to inspect local Codex output.')
|
|
522
|
+
nextStep: textFor(locale, '请重新运行一次;如果再次失败,请打开技术详情查看本地 Codex 输出。', 'Run it again. If it fails again, open Technical Details to inspect local Codex output.'),
|
|
523
|
+
failureCode: 'codex_result_parse_failed'
|
|
515
524
|
};
|
|
516
525
|
}
|
|
517
526
|
if (/Could not parse Codex output/i.test(text)) {
|
|
518
527
|
return {
|
|
519
528
|
conclusion: textFor(locale, '这轮没有写入:Codex 返回的结果格式不完整。', 'No files were written: Codex returned an incomplete result format.'),
|
|
520
|
-
nextStep: textFor(locale, '请重新运行一次;如果再次失败,请打开技术详情查看原始输出。', 'Run it again. If it fails again, open Technical Details to inspect the raw output.')
|
|
529
|
+
nextStep: textFor(locale, '请重新运行一次;如果再次失败,请打开技术详情查看原始输出。', 'Run it again. If it fails again, open Technical Details to inspect the raw output.'),
|
|
530
|
+
failureCode: 'codex_result_parse_failed'
|
|
521
531
|
};
|
|
522
532
|
}
|
|
523
533
|
if (/timed out/i.test(text)) {
|
|
524
534
|
return {
|
|
525
535
|
conclusion: textFor(locale, '这轮没有写入:本地 Codex 长时间没有完成。', 'No files were written: local Codex took too long to finish.'),
|
|
526
|
-
nextStep: textFor(locale, '请检查本机 Codex 是否仍在运行;如果没有进展,可以中断后缩小 @context 再重试。', 'Check whether local Codex is still running. If there is no progress, cancel and retry with smaller @context.')
|
|
536
|
+
nextStep: textFor(locale, '请检查本机 Codex 是否仍在运行;如果没有进展,可以中断后缩小 @context 再重试。', 'Check whether local Codex is still running. If there is no progress, cancel and retry with smaller @context.'),
|
|
537
|
+
failureCode: 'codex_timeout'
|
|
527
538
|
};
|
|
528
539
|
}
|
|
529
540
|
if (/output limit exceeded/i.test(text)) {
|
|
530
541
|
return {
|
|
531
542
|
conclusion: textFor(locale, '这轮没有写入:本地 Codex 输出过长,桥接器停止读取。', 'No files were written: local Codex output was too large, so the bridge stopped reading.'),
|
|
532
|
-
nextStep: textFor(locale, '请缩小 @context 后重试,或在技术详情中查看输出限制。', 'Retry with smaller @context, or open Technical Details to inspect the output limit.')
|
|
543
|
+
nextStep: textFor(locale, '请缩小 @context 后重试,或在技术详情中查看输出限制。', 'Retry with smaller @context, or open Technical Details to inspect the output limit.'),
|
|
544
|
+
failureCode: 'codex_output_limit'
|
|
533
545
|
};
|
|
534
546
|
}
|
|
535
547
|
if (/codex_not_found|Codex CLI was not found|ENOENT/i.test(text)) {
|
|
536
548
|
return {
|
|
537
549
|
conclusion: textFor(locale, '这轮没有启动:本机没有找到 Codex CLI。', 'This run did not start: Codex CLI was not found locally.'),
|
|
538
|
-
nextStep: textFor(locale, '请确认终端里可以运行 `codex`,然后重新安装 native host 或刷新扩展后重试。', 'Confirm `codex` works in Terminal, then reinstall the native host or reload the extension.')
|
|
550
|
+
nextStep: textFor(locale, '请确认终端里可以运行 `codex`,然后重新安装 native host 或刷新扩展后重试。', 'Confirm `codex` works in Terminal, then reinstall the native host or reload the extension.'),
|
|
551
|
+
failureCode: 'codex_not_found'
|
|
539
552
|
};
|
|
540
553
|
}
|
|
541
554
|
if (/project_locked|currently in use by codex\.run/i.test(text)) {
|
|
@@ -545,31 +558,36 @@
|
|
|
545
558
|
'This run did not start: another Codex task is already running for this Overleaf project.'),
|
|
546
559
|
nextStep: textFor(locale,
|
|
547
560
|
'请等待当前任务完成,或先取消当前任务后再重试。',
|
|
548
|
-
'Wait for the current task to finish, or cancel it before retrying.')
|
|
561
|
+
'Wait for the current task to finish, or cancel it before retrying.'),
|
|
562
|
+
failureCode: 'codex_project_locked'
|
|
549
563
|
};
|
|
550
564
|
}
|
|
551
565
|
if (/unsupported[_ ]parameter/i.test(text) && /reasoning\.summary|summary/i.test(text)) {
|
|
552
566
|
return {
|
|
553
567
|
conclusion: textFor(locale, '这轮没有继续:当前 Codex 模型不支持插件请求的推理摘要参数。', 'This run did not continue: the selected Codex model does not support the requested reasoning summary parameter.'),
|
|
554
|
-
nextStep: textFor(locale, '请刷新扩展并重新运行;插件会按模型能力自动去掉不兼容参数。', 'Reload the extension and run again; the plugin will omit incompatible parameters based on model capability.')
|
|
568
|
+
nextStep: textFor(locale, '请刷新扩展并重新运行;插件会按模型能力自动去掉不兼容参数。', 'Reload the extension and run again; the plugin will omit incompatible parameters based on model capability.'),
|
|
569
|
+
failureCode: 'native_protocol_incompatible'
|
|
555
570
|
};
|
|
556
571
|
}
|
|
557
572
|
if (/quota|kQuotaBytes|QUOTA_BYTES/i.test(text)) {
|
|
558
573
|
return {
|
|
559
574
|
conclusion: textFor(locale, 'Codex 结果已经生成,但本地会话记录超出 Chrome 存储配额。', 'Codex produced a result, but local session history exceeded Chrome storage quota.'),
|
|
560
|
-
nextStep: textFor(locale, '请删除一些旧 session,或刷新扩展后重试;这不是论文分析本身失败。', 'Delete older sessions or reload the extension and retry. The paper analysis itself did not fail.')
|
|
575
|
+
nextStep: textFor(locale, '请删除一些旧 session,或刷新扩展后重试;这不是论文分析本身失败。', 'Delete older sessions or reload the extension and retry. The paper analysis itself did not fail.'),
|
|
576
|
+
failureCode: 'storage_quota_exceeded'
|
|
561
577
|
};
|
|
562
578
|
}
|
|
563
579
|
if (/checkpoint/i.test(text)) {
|
|
564
580
|
return {
|
|
565
581
|
conclusion: textFor(locale, '这轮没有自动写入:Codex 没有拿到可恢复版本。', 'This run did not auto-write: Codex did not get a recoverable version.'),
|
|
566
|
-
nextStep: textFor(locale, '请切换到“建议修改”,或确认 Overleaf Reviewing 已开启后再用“自动写入”。', 'Switch to Suggest, or confirm Overleaf Reviewing is enabled before using Auto.')
|
|
582
|
+
nextStep: textFor(locale, '请切换到“建议修改”,或确认 Overleaf Reviewing 已开启后再用“自动写入”。', 'Switch to Suggest, or confirm Overleaf Reviewing is enabled before using Auto.'),
|
|
583
|
+
failureCode: null // no current catalog code; preflight reviewing issue
|
|
567
584
|
};
|
|
568
585
|
}
|
|
569
586
|
if (/changed while Codex was working|任务执行期间被你或协作者改过/i.test(text)) {
|
|
570
587
|
return {
|
|
571
588
|
conclusion: textFor(locale, '这轮没有覆盖文件:任务执行期间文件被你或协作者改过。', 'No file was overwritten: a file changed while Codex was working.'),
|
|
572
|
-
nextStep: textFor(locale, '请先确认 Overleaf 当前内容,再重新运行任务。', 'Review the current Overleaf content, then run the task again.')
|
|
589
|
+
nextStep: textFor(locale, '请先确认 Overleaf 当前内容,再重新运行任务。', 'Review the current Overleaf content, then run the task again.'),
|
|
590
|
+
failureCode: 'stale_source_changed'
|
|
573
591
|
};
|
|
574
592
|
}
|
|
575
593
|
|
|
@@ -585,14 +603,16 @@
|
|
|
585
603
|
'Codex returned a result, but local post-processing of this run failed.'),
|
|
586
604
|
nextStep: textFor(locale,
|
|
587
605
|
'请打开技术详情查看错误。Codex 的回答仍保留在会话中。',
|
|
588
|
-
'Open Technical Details to inspect the error. Codex\'s answer is preserved in the conversation.')
|
|
606
|
+
'Open Technical Details to inspect the error. Codex\'s answer is preserved in the conversation.'),
|
|
607
|
+
failureCode: null // post-processing path; the real error already attached its own structured failure
|
|
589
608
|
};
|
|
590
609
|
}
|
|
591
610
|
return {
|
|
592
611
|
conclusion: context.mode === 'ask'
|
|
593
612
|
? textFor(locale, '这轮只问不改没有完成:本地 Codex 没有正常完成,因此没有生成最终说明。', 'This Ask run did not complete: local Codex did not finish normally, so no final answer was generated.')
|
|
594
613
|
: textFor(locale, '这轮任务失败:本地 Codex 没有返回可用结果,未确认任何写入。', 'This task failed: local Codex returned no usable result, so no writes were confirmed.'),
|
|
595
|
-
nextStep: textFor(locale, '请查看技术详情,处理本地 Codex 错误后重试。', 'Open Technical Details, resolve the local Codex error, and retry.')
|
|
614
|
+
nextStep: textFor(locale, '请查看技术详情,处理本地 Codex 错误后重试。', 'Open Technical Details, resolve the local Codex error, and retry.'),
|
|
615
|
+
failureCode: 'codex_no_usable_result'
|
|
596
616
|
};
|
|
597
617
|
}
|
|
598
618
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const MIN_NATIVE_VERSION = '1.0.0';
|
|
13
13
|
const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
|
|
14
14
|
const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
|
|
15
|
-
const BUILD_TARGET_VERSION = '1.3.
|
|
15
|
+
const BUILD_TARGET_VERSION = '1.3.9';
|
|
16
16
|
const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
|
|
17
17
|
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
18
18
|
'bridgePing',
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
fallbackNextAction: 'Review written files; use Undo written parts if needed.'
|
|
165
165
|
},
|
|
166
166
|
|
|
167
|
-
// 9.0+ — write-safety primitive (welcome-panel + writeback project-ID guard
|
|
167
|
+
// 9.0+ — write-safety primitive (welcome-panel + writeback project-ID guard)
|
|
168
168
|
aborted_project_changed: {
|
|
169
169
|
stage: 'write', severity: 'blocked', defaultRetryable: true,
|
|
170
170
|
fallbackUserMessage: 'Codex stopped a write because Overleaf switched to a different project mid-run.',
|
|
@@ -303,6 +303,21 @@
|
|
|
303
303
|
fallbackUserMessage: 'The local run was cancelled.',
|
|
304
304
|
fallbackNextAction: 'Start a new run.'
|
|
305
305
|
},
|
|
306
|
+
codex_timeout: {
|
|
307
|
+
stage: 'codex', severity: 'error', defaultRetryable: true,
|
|
308
|
+
fallbackUserMessage: 'Local Codex took too long to finish.',
|
|
309
|
+
fallbackNextAction: 'Check whether local Codex is still running; cancel and retry with smaller @context.'
|
|
310
|
+
},
|
|
311
|
+
codex_output_limit: {
|
|
312
|
+
stage: 'codex', severity: 'error', defaultRetryable: true,
|
|
313
|
+
fallbackUserMessage: 'Local Codex output was too large; the bridge stopped reading.',
|
|
314
|
+
fallbackNextAction: 'Retry with smaller @context, or open Technical Details to inspect the output limit.'
|
|
315
|
+
},
|
|
316
|
+
codex_not_found: {
|
|
317
|
+
stage: 'codex', severity: 'blocked', defaultRetryable: true,
|
|
318
|
+
fallbackUserMessage: 'Codex CLI was not found on this machine.',
|
|
319
|
+
fallbackNextAction: 'Confirm `codex` works in Terminal, then reinstall the native host or reload the extension.'
|
|
320
|
+
},
|
|
306
321
|
|
|
307
322
|
// 9.8 Storage
|
|
308
323
|
storage_quota_exceeded: {
|
|
@@ -344,13 +344,19 @@
|
|
|
344
344
|
failureReason_codex_no_usable_result_next: 'Open Technical Details and resolve the local Codex error.',
|
|
345
345
|
failureReason_codex_project_locked_user: 'Another Codex task is already running for this Overleaf project.',
|
|
346
346
|
failureReason_codex_project_locked_next: 'Wait for the active task to finish, or cancel it before retrying.',
|
|
347
|
+
failureReason_codex_timeout_user: 'Local Codex took too long to finish.',
|
|
348
|
+
failureReason_codex_timeout_next: 'Check whether local Codex is still running. If there is no progress, cancel and retry with smaller @context.',
|
|
349
|
+
failureReason_codex_output_limit_user: 'Local Codex output was too large, so the bridge stopped reading.',
|
|
350
|
+
failureReason_codex_output_limit_next: 'Retry with smaller @context, or open Technical Details to inspect the output limit.',
|
|
351
|
+
failureReason_codex_not_found_user: 'Codex CLI was not found on this machine.',
|
|
352
|
+
failureReason_codex_not_found_next: 'Confirm `codex` works in Terminal, then reinstall the native host or reload the extension.',
|
|
347
353
|
failureReason_storage_quota_exceeded_user: 'Browser storage quota was exceeded.',
|
|
348
354
|
failureReason_storage_quota_exceeded_next: 'Clear old run history or reduce attachments.',
|
|
349
355
|
failureReason_aborted_project_changed_user: 'Codex stopped a write because Overleaf switched to a different project mid-run.',
|
|
350
356
|
failureReason_aborted_project_changed_next: 'Reopen the original project and rerun the task if you still want this change.',
|
|
351
357
|
failureReason_editor_project_id_unavailable_user: 'Codex could not confirm which Overleaf project the editor is showing, so it did not write.',
|
|
352
358
|
failureReason_editor_project_id_unavailable_next: 'Refresh the Overleaf tab and retry; if it persists, reload the extension.',
|
|
353
|
-
// Welcome-panel + write-guard
|
|
359
|
+
// Welcome-panel + write-guard: Recent-projects
|
|
354
360
|
// variant copy. Spec §5.3–§5.5, §5.8, §5.10 are the source of truth.
|
|
355
361
|
recentProjects_welcome: 'Codex Overleaf Link',
|
|
356
362
|
recentProjects_welcome_subtitle: 'Open a project from the Overleaf list on the left to start.',
|
|
@@ -701,13 +707,19 @@
|
|
|
701
707
|
failureReason_codex_no_usable_result_next: '请打开“技术细节”排查本地 Codex 错误。',
|
|
702
708
|
failureReason_codex_project_locked_user: '同一个 Overleaf 项目里已经有一轮 Codex 任务正在运行。',
|
|
703
709
|
failureReason_codex_project_locked_next: '请等待当前任务完成,或先取消当前任务后再重试。',
|
|
710
|
+
failureReason_codex_timeout_user: '本地 Codex 长时间没有完成。',
|
|
711
|
+
failureReason_codex_timeout_next: '请检查本机 Codex 是否仍在运行;如果没有进展,可以中断后缩小 @context 再重试。',
|
|
712
|
+
failureReason_codex_output_limit_user: '本地 Codex 输出过长,桥接器停止读取。',
|
|
713
|
+
failureReason_codex_output_limit_next: '请缩小 @context 后重试,或在技术详情中查看输出限制。',
|
|
714
|
+
failureReason_codex_not_found_user: '本机没有找到 Codex CLI。',
|
|
715
|
+
failureReason_codex_not_found_next: '请确认终端里可以运行 `codex`,然后重新安装 native host 或刷新扩展后重试。',
|
|
704
716
|
failureReason_storage_quota_exceeded_user: '浏览器本地存储配额已超出。',
|
|
705
717
|
failureReason_storage_quota_exceeded_next: '请清理旧的运行历史,或减少附件大小。',
|
|
706
718
|
failureReason_aborted_project_changed_user: 'Codex 已停止一次写入,因为 Overleaf 在写入过程中切换到了另一个项目。',
|
|
707
719
|
failureReason_aborted_project_changed_next: '如仍需此次改动,请回到原项目后重新运行任务。',
|
|
708
720
|
failureReason_editor_project_id_unavailable_user: 'Codex 无法确认当前 Overleaf 编辑器对应的项目,因此没有写入。',
|
|
709
721
|
failureReason_editor_project_id_unavailable_next: '请刷新 Overleaf 页面后重试;如果仍然失败,请重新加载扩展。',
|
|
710
|
-
// Welcome-panel + write-guard
|
|
722
|
+
// Welcome-panel + write-guard: Recent-projects
|
|
711
723
|
// variant copy. 见 spec §5.3–§5.5、§5.8、§5.10。
|
|
712
724
|
recentProjects_welcome: 'Codex Overleaf Link',
|
|
713
725
|
recentProjects_welcome_subtitle: '从左侧 Overleaf 项目列表中打开任意项目即可开始。',
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
// -------------------------------------------------------------------------
|
|
11
|
-
// Welcome-panel + write-guard
|
|
11
|
+
// Welcome-panel + write-guard:
|
|
12
12
|
//
|
|
13
13
|
// Canonical local-path sanitizer shared between `computeSafeTaskSummary`
|
|
14
14
|
// (sessionState) and the storage-side audit redaction helpers
|
|
@@ -95,19 +95,6 @@
|
|
|
95
95
|
'gi'
|
|
96
96
|
);
|
|
97
97
|
|
|
98
|
-
// Quick predicate — used by callers that only want to do the (more
|
|
99
|
-
// expensive) substitution pass when there's actually something to strip.
|
|
100
|
-
const MIGHT_CONTAIN_LOCAL_PATH_PATTERN = new RegExp(
|
|
101
|
-
[
|
|
102
|
-
'file:\\/{2,3}',
|
|
103
|
-
'(?:\\\\\\\\|\\/\\/)[A-Za-z0-9._-]+[\\\\\\/]',
|
|
104
|
-
'[A-Za-z]:[\\\\\\/]',
|
|
105
|
-
'\\/(?:' + UNIX_TOPLEVELS.join('|') + ')\\/',
|
|
106
|
-
'\\.codex-overleaf[\\\\\\/]projects[\\\\\\/]'
|
|
107
|
-
].join('|'),
|
|
108
|
-
'i'
|
|
109
|
-
);
|
|
110
|
-
|
|
111
98
|
// Replace every absolute-local-path token in `value` with `placeholder`.
|
|
112
99
|
// Default placeholder is `<local-path>` to match the prior summary-side
|
|
113
100
|
// sanitizer. The storage-side audit redactor uses a richer `[local path]`
|
|
@@ -124,20 +111,7 @@
|
|
|
124
111
|
return value.replace(ABSOLUTE_PATH_PATTERN, token);
|
|
125
112
|
}
|
|
126
113
|
|
|
127
|
-
function mightContainLocalPath(value) {
|
|
128
|
-
if (typeof value !== 'string' || !value) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
return MIGHT_CONTAIN_LOCAL_PATH_PATTERN.test(value);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
114
|
return {
|
|
135
|
-
redactLocalPaths
|
|
136
|
-
mightContainLocalPath,
|
|
137
|
-
// Exposed so the storage-side audit redactor (which formats per-token
|
|
138
|
-
// placeholders with line-suffix preservation) can iterate the same set
|
|
139
|
-
// without duplicating the regex source.
|
|
140
|
-
ABSOLUTE_PATH_PATTERN,
|
|
141
|
-
UNIX_TOPLEVELS
|
|
115
|
+
redactLocalPaths
|
|
142
116
|
};
|
|
143
117
|
});
|
|
@@ -602,7 +602,7 @@
|
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
-
// Welcome-panel + write-guard
|
|
605
|
+
// Welcome-panel + write-guard: the run lifecycle now
|
|
606
606
|
// settles on three additional values when the user navigates away mid-run
|
|
607
607
|
// and the original run completes (or aborts) in the background. They land on
|
|
608
608
|
// the ORIGINAL project's session record, not the active one. The catalog is
|
|
@@ -906,7 +906,7 @@
|
|
|
906
906
|
requireReviewing: session.requireReviewing !== false,
|
|
907
907
|
focusFiles: normalizeFocusFiles(session.focusFiles)
|
|
908
908
|
};
|
|
909
|
-
// Welcome-panel + write-guard
|
|
909
|
+
// Welcome-panel + write-guard: preserve the four
|
|
910
910
|
// Recent-projects fields through compaction so they round-trip when state
|
|
911
911
|
// is reloaded from chrome.storage.local. The active record builder in
|
|
912
912
|
// `buildSessionRecord` is the canonical writer; this branch preserves an
|
|
@@ -1129,7 +1129,7 @@
|
|
|
1129
1129
|
return Math.round(Math.min(760, Math.max(340, width)));
|
|
1130
1130
|
}
|
|
1131
1131
|
|
|
1132
|
-
// Welcome-panel + write-guard
|
|
1132
|
+
// Welcome-panel + write-guard: the Recent-projects
|
|
1133
1133
|
// dashboard variant renders one sanitized line per project. `computeSafeTaskSummary`
|
|
1134
1134
|
// is the privacy floor for that line. It is written on every `saveState` and
|
|
1135
1135
|
// stored on the session record (`session.safeTaskSummary`), so the dashboard
|
|
@@ -1148,7 +1148,7 @@
|
|
|
1148
1148
|
if (typeof task !== 'string' || !task) return '';
|
|
1149
1149
|
let s = task;
|
|
1150
1150
|
// Strip absolute local paths via the canonical shared helper
|
|
1151
|
-
// (spec §5.6.2
|
|
1151
|
+
// (spec §5.6.2). The helper covers Unix (/Users, /home,
|
|
1152
1152
|
// /private/var, /tmp, /var/folders, /Volumes, /etc, /opt, /usr, ...),
|
|
1153
1153
|
// Windows drive letters with both `\\` and `/`, UNC `\\server\share`,
|
|
1154
1154
|
// and `file:///` URLs. Adding a new path shape is a one-line change
|
|
@@ -239,7 +239,7 @@
|
|
|
239
239
|
var now = new Date().toISOString();
|
|
240
240
|
var titleSource = input.titleSource === 'manual' ? 'manual' : 'auto';
|
|
241
241
|
var updatedAt = typeof input.updatedAt === 'string' ? input.updatedAt : now;
|
|
242
|
-
// Welcome-panel + write-guard
|
|
242
|
+
// Welcome-panel + write-guard: persist the four
|
|
243
243
|
// Recent-projects fields on every session record so the cross-project
|
|
244
244
|
// query (`listRecentProjectsAcrossAccount`) can filter / sort / render
|
|
245
245
|
// without touching the raw `task` text.
|
|
@@ -845,7 +845,7 @@
|
|
|
845
845
|
}
|
|
846
846
|
|
|
847
847
|
function normalizeRunStatus(status) {
|
|
848
|
-
// Welcome-panel + write-guard
|
|
848
|
+
// Welcome-panel + write-guard: the run-status
|
|
849
849
|
// enum gained three post-navigation values. The storage normalizer must
|
|
850
850
|
// accept them so a settled run round-trips intact through `buildSessionRecord`.
|
|
851
851
|
// Unknown legacy values fall through to `completed` (the historical default).
|
|
@@ -1065,7 +1065,7 @@
|
|
|
1065
1065
|
return result;
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
-
// Welcome-panel + write-guard
|
|
1068
|
+
// Welcome-panel + write-guard: the Recent-projects
|
|
1069
1069
|
// dashboard variant calls `listRecentProjectsAcrossAccount` to get the
|
|
1070
1070
|
// sorted, deduped, capped list of projects in the current account scope.
|
|
1071
1071
|
//
|
|
@@ -677,9 +677,23 @@ function runCodexAppServerSession(input) {
|
|
|
677
677
|
const assistantMessages = new Map();
|
|
678
678
|
const assistantMessageOrder = [];
|
|
679
679
|
let settled = false;
|
|
680
|
+
// Two-layer timeout strategy:
|
|
681
|
+
// 1. Optional absolute deadline (CODEX_OVERLEAF_CODEX_TIMEOUT_MS) —
|
|
682
|
+
// legacy override; off by default. When set, the whole run must
|
|
683
|
+
// finish within that envelope.
|
|
684
|
+
// 2. Idle watchdog — fires after a stretch of silence from the
|
|
685
|
+
// app-server (no stdout / no messages). Default 10 minutes; the
|
|
686
|
+
// runtime resets it on every incoming line and on every outgoing
|
|
687
|
+
// request. This catches the failure mode where Codex sends
|
|
688
|
+
// turn/started, then hangs without ever emitting completed/error
|
|
689
|
+
// and the project lock would otherwise be held forever.
|
|
680
690
|
const timeout = createOptionalTimeout(childEnv.CODEX_OVERLEAF_CODEX_TIMEOUT_MS, timeoutMs => {
|
|
681
691
|
fail(new Error(`Codex app-server did not complete within configured timeout (${timeoutMs}ms)`));
|
|
682
692
|
});
|
|
693
|
+
const idleTimeoutMs = parseOptionalPositiveInteger(childEnv.CODEX_OVERLEAF_CODEX_IDLE_TIMEOUT_MS) || 600000;
|
|
694
|
+
const idleWatchdog = createCodexIdleWatchdog(idleTimeoutMs, ms => {
|
|
695
|
+
fail(new Error(`Codex app-server produced no events for ${ms}ms (idle watchdog); the run was aborted to release the project lock.`));
|
|
696
|
+
});
|
|
683
697
|
const onAbort = () => {
|
|
684
698
|
fail(getAbortReason(input.signal));
|
|
685
699
|
};
|
|
@@ -688,6 +702,7 @@ function runCodexAppServerSession(input) {
|
|
|
688
702
|
child.stdout.setEncoding('utf8');
|
|
689
703
|
child.stderr.setEncoding('utf8');
|
|
690
704
|
child.stdout.on('data', chunk => {
|
|
705
|
+
idleWatchdog.reset();
|
|
691
706
|
stdoutBuffer += chunk;
|
|
692
707
|
const lines = stdoutBuffer.split(/\r?\n/);
|
|
693
708
|
stdoutBuffer = lines.pop() || '';
|
|
@@ -924,6 +939,7 @@ function runCodexAppServerSession(input) {
|
|
|
924
939
|
|
|
925
940
|
function cleanup() {
|
|
926
941
|
timeout.cancel();
|
|
942
|
+
idleWatchdog.cancel();
|
|
927
943
|
input.signal?.removeEventListener('abort', onAbort);
|
|
928
944
|
}
|
|
929
945
|
});
|
|
@@ -1141,6 +1157,30 @@ function createOptionalTimeout(value, onTimeout) {
|
|
|
1141
1157
|
};
|
|
1142
1158
|
}
|
|
1143
1159
|
|
|
1160
|
+
// Idle-style watchdog: fires only after the app-server has been silent for
|
|
1161
|
+
// `idleMs`. Callers must invoke .reset() on every signal of liveness
|
|
1162
|
+
// (incoming line, outgoing request). cancel() stops the timer on settle.
|
|
1163
|
+
// Returns no-op when idleMs is non-positive (defensive guard against bad env
|
|
1164
|
+
// var values).
|
|
1165
|
+
function createCodexIdleWatchdog(idleMs, onIdle) {
|
|
1166
|
+
if (!(idleMs > 0)) {
|
|
1167
|
+
return {
|
|
1168
|
+
reset() {},
|
|
1169
|
+
cancel() {}
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
let timer = setTimeout(() => onIdle(idleMs), idleMs);
|
|
1173
|
+
return {
|
|
1174
|
+
reset() {
|
|
1175
|
+
clearTimeout(timer);
|
|
1176
|
+
timer = setTimeout(() => onIdle(idleMs), idleMs);
|
|
1177
|
+
},
|
|
1178
|
+
cancel() {
|
|
1179
|
+
clearTimeout(timer);
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1144
1184
|
function parseOptionalPositiveInteger(value) {
|
|
1145
1185
|
if (value === undefined || value === null || value === '') {
|
|
1146
1186
|
return 0;
|
|
@@ -25,6 +25,11 @@ const { version: PACKAGE_VERSION } = require('../../package.json');
|
|
|
25
25
|
|
|
26
26
|
const activeProjectLocks = new Map();
|
|
27
27
|
const activeRunControllers = new Map();
|
|
28
|
+
// Parallel index of active runs by projectKey so codex.cancel can find a
|
|
29
|
+
// controller even when the original request id is unknown (e.g. after the
|
|
30
|
+
// Overleaf tab was reloaded — the requestId lived in content-side JS state
|
|
31
|
+
// and is gone, but the native-host-side controller is still running).
|
|
32
|
+
const activeRunByProject = new Map();
|
|
28
33
|
const pendingPlans = new Map();
|
|
29
34
|
const PENDING_PLAN_TTL_MS = 30 * 60 * 1000;
|
|
30
35
|
const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed']);
|
|
@@ -138,6 +143,7 @@ async function handleCodexRun(request, env, emit) {
|
|
|
138
143
|
const abortController = new AbortController();
|
|
139
144
|
if (request.id) {
|
|
140
145
|
activeRunControllers.set(request.id, abortController);
|
|
146
|
+
activeRunByProject.set(projectKey, { id: request.id, controller: abortController });
|
|
141
147
|
}
|
|
142
148
|
try {
|
|
143
149
|
if (params.useExistingMirror) {
|
|
@@ -213,24 +219,63 @@ async function handleCodexRun(request, env, emit) {
|
|
|
213
219
|
if (request.id && activeRunControllers.get(request.id) === abortController) {
|
|
214
220
|
activeRunControllers.delete(request.id);
|
|
215
221
|
}
|
|
222
|
+
if (activeRunByProject.get(projectKey)?.controller === abortController) {
|
|
223
|
+
activeRunByProject.delete(projectKey);
|
|
224
|
+
}
|
|
216
225
|
releaseProjectLock(projectKey, lockToken);
|
|
217
226
|
}
|
|
218
227
|
}
|
|
219
228
|
|
|
229
|
+
// Cancel paths, in priority order:
|
|
230
|
+
// 1. By requestId (legacy + primary, when the caller still has it)
|
|
231
|
+
// 2. By projectKey (after page refresh — requestId is lost but projectKey
|
|
232
|
+
// is derivable from the Overleaf URL)
|
|
233
|
+
// 3. Force-release the project lock when no controller is registered for
|
|
234
|
+
// the given projectKey. Covers the zombie-lock case where a previous
|
|
235
|
+
// run leaked the lock (unhandled error path, process bug, etc.) and
|
|
236
|
+
// the user otherwise has no way to recover short of restarting Chrome.
|
|
237
|
+
// Only fires when `force: true` is explicitly set so accidental calls
|
|
238
|
+
// can't punch through a real live run.
|
|
220
239
|
function handleCodexCancel(request) {
|
|
221
|
-
const
|
|
222
|
-
|
|
240
|
+
const params = request.params || {};
|
|
241
|
+
const targetId = params.requestId || params.id;
|
|
242
|
+
const projectKey = typeof params.projectKey === 'string' ? params.projectKey : '';
|
|
243
|
+
const force = params.force === true;
|
|
244
|
+
|
|
245
|
+
if (targetId && activeRunControllers.has(targetId)) {
|
|
246
|
+
activeRunControllers.get(targetId).abort(createCancellationError());
|
|
223
247
|
return okResponse(request.id, {
|
|
224
|
-
cancelled:
|
|
225
|
-
|
|
248
|
+
cancelled: true,
|
|
249
|
+
requestId: targetId
|
|
226
250
|
});
|
|
227
251
|
}
|
|
228
252
|
|
|
229
|
-
|
|
230
|
-
|
|
253
|
+
if (projectKey) {
|
|
254
|
+
const entry = activeRunByProject.get(projectKey);
|
|
255
|
+
if (entry?.controller) {
|
|
256
|
+
entry.controller.abort(createCancellationError());
|
|
257
|
+
return okResponse(request.id, {
|
|
258
|
+
cancelled: true,
|
|
259
|
+
projectKey,
|
|
260
|
+
requestId: entry.id || ''
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
if (force && activeProjectLocks.has(projectKey)) {
|
|
264
|
+
activeProjectLocks.delete(projectKey);
|
|
265
|
+
activeRunByProject.delete(projectKey);
|
|
266
|
+
logDebug('codex.cancel.force_released_zombie_lock', { projectKey });
|
|
267
|
+
return okResponse(request.id, {
|
|
268
|
+
cancelled: false,
|
|
269
|
+
lockReleased: true,
|
|
270
|
+
projectKey,
|
|
271
|
+
reason: 'No active controller; force-released the project lock entry'
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
231
276
|
return okResponse(request.id, {
|
|
232
|
-
cancelled:
|
|
233
|
-
|
|
277
|
+
cancelled: false,
|
|
278
|
+
reason: 'No active Codex run matched the cancellation request'
|
|
234
279
|
});
|
|
235
280
|
}
|
|
236
281
|
|