From 26305d43ef64fe6dbf7c957013a32730db2b3b22 Mon Sep 17 00:00:00 2001 From: seekskyworld Date: Mon, 7 Sep 2026 01:04:37 +0800 Subject: [PATCH 1/2] fix(workflows): follow Pi effective child idle timeout Signed-off-by: seekskyworld --- README.md | 2 +- .../ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md | 68 +++++++++++++++++++ docs/research/README.md | 1 + extensions/workflows/runner.ts | 12 ++-- tests/extensions/workflows/runner.test.ts | 16 ++++- 5 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md diff --git a/README.md b/README.md index 16c6b9e6..ded088e2 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,7 @@ return agent("Synthesize the verified findings", { | `pipeline()` | 每个 item 完成上阶段后立即进入下一阶段;多阶段 fan-out 的默认选择 | | `parallel()` | 并发 barrier;只在下一阶段确实需要全部结果时使用 | -Workflow 默认并发 8 个 Agent,单次最多 128 次调用;可配置到 64 和 1024。前台运行可实时查看,后台运行完成后自动回传;`/workflows` 展示阶段、Agent、Transcript、Graph、用量与产物。每个 Child Provider turn 必须在 45 秒内产生模型可见的 thinking、text、tool call 或完成事件,并在持续输出时按进展续期;空 stream start 与 transport heartbeat 不算进展。用户显式配置了更宽的 Pi `httpIdleTimeoutMs` 时沿用该上限。超时会 abort 当前 Child、保留已有 Transcript/usage/evidence,并让 sibling 与后续阶段继续结算。 +Workflow 默认并发 8 个 Agent,单次最多 128 次调用;可配置到 64 和 1024。前台运行可实时查看,后台运行完成后自动回传;`/workflows` 展示阶段、Agent、Transcript、Graph、用量与产物。每个 Child Provider turn 的进度 watchdog 跟随 Pi 的有效 `httpIdleTimeoutMs`(当前默认 300 秒),并保留 45 秒最低边界;它必须产生模型可见的 thinking、text、tool call 或完成事件,并在持续输出时按进展续期。空 stream start 与 transport heartbeat 不算进展。即使 Pi 的传输超时显式设为 `0`,子会话仍保留这个有界最低值。超时会 abort 当前 Child、保留已有 Transcript/usage/evidence,并让 sibling 与后续阶段继续结算。 --- diff --git a/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md b/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md new file mode 100644 index 00000000..ba2e9854 --- /dev/null +++ b/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md @@ -0,0 +1,68 @@ +--- +status: validated +created: 2026-09-07 +last-verified: 2026-09-07 +applies-to: OpenPI at a7455cd378ef7befa9c7cf099c1fa1e4ee5dc3e3 with Pi 0.85.1 +source-boundary: OpenPI source, deterministic tests, and the installed Pi 0.85.1 package +related-issues: "#425, #424" +related-prs: "待创建" +supersedes: none +--- + +# Issue 425:Workflow 子会话进度 watchdog + +## 问题与范围 + +Workflow 子会话用额外的进度 watchdog 保护 provider turn。修复前, +`extensions/workflows/runner.ts` 直接读取项目和全局设置对象里的原始 +`httpIdleTimeoutMs` 字段。用户没有显式配置时,这两个可选字段都为空, +OpenPI 便使用 45 秒默认值;Pi 实际传输层仍使用自己的有效默认值 300 秒。 +慢但健康的 provider turn 因此可能在 Pi 传输层超时之前被 OpenPI 错误中止。 + +本记录只覆盖超时来源和边界,不覆盖 #424 中的 Cursor 工具协议、跨目录权限、 +allowlist 或真实 provider 接受测试。 + +## 已核验事实 + +- 当前依赖声明和安装包为 `@earendil-works/pi-coding-agent` `0.85.1`。 +- Pi `dist/core/http-dispatcher.js` 定义 `DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000`。 +- Pi `SettingsManager.getHttpIdleTimeoutMs()` 读取合并后的有效设置;原始字段缺失 + 时回退到上述默认值。因此直接读取 `getProjectSettings()` 和 + `getGlobalSettings()` 不能代表 Pi 的有效超时。 +- OpenPI 仍需要一个有界的模型可见进度保护。修复保留 45 秒最低值:有效 Pi + 设置低于该值时(包括显式 `0`,表示关闭 Pi 传输空闲超时)仍使用 45 秒。 +- 测试覆盖的 `override` 继续直接返回调用方提供的值,以保留确定性测试和既有 + `runHarnessAgent` 行为。 + +## 实施与取舍 + +`resolveModelProgressTimeoutMs()` 现在调用 Pi 原生 +`SettingsManager.getHttpIdleTimeoutMs()`,再取不小于 `MODEL_PROGRESS_TIMEOUT_MS` +的整数值。这样未配置时跟随 Pi 的 300 秒有效默认值;显式较宽值继续生效; +低值和 `0` 仍不会移除 OpenPI 子会话的有界保护。watchdog 的 abort、progress +续期、completion 和 cleanup 路径没有改变。 + +## 验证 + +- `npx --yes bun@1.3.14 run test`:Node 测试 `1430 passed, 1 skipped, 0 failed`; + Vitest `30 passed`。 +- `npx --yes bun@1.3.14 run typecheck`:通过。 +- 变更文件的 Biome lint/format 检查与 `git diff --check`:通过。 +- 定向 Workflow 测试覆盖有效默认值 `300000`、显式宽值、测试 override、45 秒 + floor 和显式 `0`。 +- 完整 `bun run check` 的唯一失败来自既有未跟踪 `.golutra/local.json` 和 + `.golutra/workspace.json` 与格式化器的差异;这些协作证据未被改写。其余源码 + lint、typecheck 和定向格式检查通过。 + +## 证据边界与未知项 + +本记录验证了 Pi accessor 的源码契约和 OpenPI 的确定性回归边界,没有调用真实 +provider,也没有声称测量慢 provider 的端到端成功率。Pi 或 OpenPI 后续改变 +超时语义时,需要重新核对 accessor、最低边界和相关测试。该切片不能单独关闭 +#424 的综合可靠性目标。 + +## 追踪链接 + +- Issue:[#425](https://github.com/openpi-dev/openpi/issues/425) +- 上游综合 Issue:[#424](https://github.com/openpi-dev/openpi/issues/424) +- PR:待创建,创建后回填此处并在 Issue 留言互链。 diff --git a/docs/research/README.md b/docs/research/README.md index 07707793..e986b9d6 100644 --- a/docs/research/README.md +++ b/docs/research/README.md @@ -5,6 +5,7 @@ Research records preserve sourced investigation and distinguish observations, in ## Validated investigations - [`WORKFLOW_DASHBOARD_REFRESH_2026-09-07.md`](WORKFLOW_DASHBOARD_REFRESH_2026-09-07.md) — repeated synchronous history loading on dashboard animation ticks, its regression boundary, and measurement limits ([#420](https://github.com/openpi-dev/openpi/issues/420)). +- [`ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md`](ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md) — Pi effective HTTP idle timeout provenance, the bounded child progress watchdog floor, and deterministic validation ([#425](https://github.com/openpi-dev/openpi/issues/425), [#424](https://github.com/openpi-dev/openpi/issues/424)). ## Legacy records diff --git a/extensions/workflows/runner.ts b/extensions/workflows/runner.ts index 12c9686e..7214e46d 100644 --- a/extensions/workflows/runner.ts +++ b/extensions/workflows/runner.ts @@ -256,12 +256,12 @@ export function resolveModelProgressTimeoutMs( override?: number, ) { if (override !== undefined) return override; - const configured = - settingsManager.getProjectSettings().httpIdleTimeoutMs ?? - settingsManager.getGlobalSettings().httpIdleTimeoutMs; - return typeof configured === "number" && Number.isFinite(configured) - ? Math.max(MODEL_PROGRESS_TIMEOUT_MS, Math.floor(configured)) - : MODEL_PROGRESS_TIMEOUT_MS; + // Pi 的原始设置不包含有效默认值;复用原生传输层的 accessor,避免未配置时 + // 把健康的慢速子会话错误缩短到 OpenPI 的 45 秒下限。 + return Math.max( + MODEL_PROGRESS_TIMEOUT_MS, + Math.floor(settingsManager.getHttpIdleTimeoutMs()), + ); } /** Abort any provider turn that stops producing model-visible progress. */ diff --git a/tests/extensions/workflows/runner.test.ts b/tests/extensions/workflows/runner.test.ts index caa3a4f1..734f28a3 100644 --- a/tests/extensions/workflows/runner.test.ts +++ b/tests/extensions/workflows/runner.test.ts @@ -1027,10 +1027,10 @@ test("model-progress watchdog aborts a silent provider turn", async () => { assert.equal(aborted, true); }); -test("model-progress timeout preserves the default and honors wider Pi idle settings", () => { +test("model-progress timeout follows Pi's effective idle setting with a floor", () => { assert.equal( resolveModelProgressTimeoutMs(SettingsManager.inMemory()), - 45_000, + 300_000, ); assert.equal( resolveModelProgressTimeoutMs( @@ -1045,6 +1045,18 @@ test("model-progress timeout preserves the default and honors wider Pi idle sett ), 5, ); + assert.equal( + resolveModelProgressTimeoutMs( + SettingsManager.inMemory({ httpIdleTimeoutMs: 10_000 }), + ), + 45_000, + ); + assert.equal( + resolveModelProgressTimeoutMs( + SettingsManager.inMemory({ httpIdleTimeoutMs: 0 }), + ), + 45_000, + ); }); test("model progress refreshes its turn while completion leaves tool time unrestricted", async () => { From 6f3a26bfdc356fa2b5e004c734816fc2d5b27bcc Mon Sep 17 00:00:00 2001 From: seekskyworld Date: Mon, 7 Sep 2026 01:06:01 +0800 Subject: [PATCH 2/2] docs(research): link workflow watchdog PR Signed-off-by: seekskyworld --- docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md b/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md index ba2e9854..6d8b4158 100644 --- a/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md +++ b/docs/research/ISSUE_425_WORKFLOW_WATCHDOG_2026-09-07.md @@ -5,7 +5,7 @@ last-verified: 2026-09-07 applies-to: OpenPI at a7455cd378ef7befa9c7cf099c1fa1e4ee5dc3e3 with Pi 0.85.1 source-boundary: OpenPI source, deterministic tests, and the installed Pi 0.85.1 package related-issues: "#425, #424" -related-prs: "待创建" +related-prs: "#427" supersedes: none --- @@ -65,4 +65,4 @@ provider,也没有声称测量慢 provider 的端到端成功率。Pi 或 Open - Issue:[#425](https://github.com/openpi-dev/openpi/issues/425) - 上游综合 Issue:[#424](https://github.com/openpi-dev/openpi/issues/424) -- PR:待创建,创建后回填此处并在 Issue 留言互链。 +- PR:[#427](https://github.com/openpi-dev/openpi/pull/427)