Skip to content

fix(runtime): 共享子执行准入池,并在中断时释放已分配的槽位 - #828

Merged
tt-a1i merged 9 commits into
openpi-dev:mainfrom
6iKUN6:fix/child-admission-shared-and-abort
Oct 31, 2026
Merged

tt-a1i merged 9 commits into
openpi-dev:mainfrom
6iKUN6:fix/child-admission-shared-and-abort

Conversation

@6iKUN6

@6iKUN6 6iKUN6 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Pi evaluates each extension in its own module graph, so module-local admission registries let Workflow and Direct/BTW children exceed the configured Session child-execution limit. A cancellation arriving as a queued Direct spawn or restart acquired its permit could also leave that permit held indefinitely. Explicitly cancelling a restart in that window could still dispatch its backend send, and a second send while waiting could bypass admission entirely.

Value

The configured limit now bounds all child origins in one parent Session. Cancelling queued or starting Direct work no longer leaves later children blocked by an abandoned permit.

Approach

Share the existing Session-owner WeakMap through a versioned process-global symbol and replace a closed pool when Pi reloads the same SessionManager. Capture acquired permits in the promise continuation so cleanup owns them even when the Effect fiber is interrupted before resuming. Check cancellation before backend handoff, keep cleanup scoped to its own restart controller/lease without overriding a newer pending controller, and reject a second send while that restart is waiting for admission. Normal backend-active steering remains available.

Validation

Validated head: 17d767cbaea3d11a3ff3224564d60baafcf9f966, based on current main f98d6a4737461ba8d8ee100f113b5a7c3a0001a0. Original contributor history is preserved.

  • bun install --frozen-lockfile: passed; Pi SDK 1.2.0.

  • node --test --experimental-strip-types tests/extensions/shared/child-execution-admission.test.ts tests/extensions/subagents/manager.test.ts tests/extensions/subagents/pi-backend.test.ts tests/extensions/subagents/pi-backend-lifecycle.test.ts with isolated PI_CODING_AGENT_DIR: 68 passed, including the regression cases that failed before the restart repairs and backend-rejection recovery coverage.

  • Deterministic native SDK smoke: Pi's loader gives separately loaded extension fixtures the same pool; real child AgentSessions through the production Pi backend release permits after successful completion and cancellation of an active local faux-provider stream; actual AgentSession.reload() closes and recreates the pool for the same owner. Local Pi list reports this checkout as the single OpenPI source. No paid provider calls.

  • bun run check: passed (configuration/docs/discipline contracts, Web build, format, lint and typecheck).

  • bun run test: passed, 2,458 Node tests passed / 9 skipped / 0 failed; 1,338 UI tests passed in 88 files.

  • Interactive TUI and real-provider acceptance were not run.

  • Two independent final source reviews passed on this exact head. Current-head CI run 38694495923 passed all required checks, including Windows. Independent last-push approval remains required before merge.

Impact

  • User-visible behavior: the configured shared limit is enforced across Workflow, Direct and BTW; cancelling work no longer strands permits.
  • Model-visible context/tools: unchanged.
  • Runtime/lifecycle: process-shared, Session-scoped admission ownership; reload creates a fresh pool; abandoned asynchronous acquisitions release their own leases.
  • Persisted config/data: unchanged.
  • Compatibility/risk: no new dependency or tool surface; rollback is a source revert. Native smoke used the pinned Pi 1.2.0 SDK; minimum-host validation remains CI's responsibility.

…elease abandoned leases

Pi loads every extension with `moduleCache: false`, so Workflows and
Subagents each had their own module-level admission WeakMap and the
Session child execution limit was never shared. Keep the Session map on a
versioned `Symbol.for` global, and start a fresh pool when a reload finds
the previous one shut down.

A Direct spawn or restart interrupted after admission granted its slot,
but before the fiber resumed, dropped the lease and held the permit
forever. Take the lease from the promise continuation and release it
there once the caller's finalizer has run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the area:subagents Subagent delegation, skills, or tests label Oct 14, 2026
@6iKUN6
6iKUN6 requested a review from tt-a1i October 30, 2026 08:56
@6iKUN6

6iKUN6 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

@tt-a1i 麻烦帮忙 review 一下,已经合入最新 main,没有冲突。

补充一下现状:main 后来自己也把准入池挂到了 globalThis,所以 PR 里写的「两个扩展不共享上限」那部分已经被 main 解决了。但下面这几个问题在最新 main 上我重新跑过,还在:

  • /reload 之后子执行全被拒:reload 时同一个 SessionManager 会拿回已经 shutdown() 的旧池,之后每次 acquire 都报 The parent Session is shutting down,子代理和 Workflow 都起不来。这个 PR 遇到已关闭的池会换一个新的。
  • spawn 时按 Esc 漏槽位:3 个回归测试在 main 上都失败,这个 PR 上通过。
  • 你之前推到这个分支上的两个 restart 修复(079a69a、17d767c)也还没进 main。

CI 上红的那一项是 Windows UI 的 provider-settings-page.spec.ts 超时,main 从 #904 开始自己的 CI 也挂在同一个用例上,和这个 PR 的改动无关。

@github-actions github-actions Bot added the area:workflows Workflow engine, capability, skills, or tests label Oct 31, 2026
@tt-a1i
tt-a1i merged commit 5046400 into openpi-dev:main Oct 31, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:subagents Subagent delegation, skills, or tests area:workflows Workflow engine, capability, skills, or tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants