feat(coding-runtime): add managed Pi extension host

This commit is contained in:
2026-08-23 09:39:12 +08:00
parent 47159b3cbd
commit 3861c3286a
22 changed files with 1673 additions and 16 deletions

View File

@@ -0,0 +1,190 @@
# Task: Implement PI-070 managed extension host interactions and write lease
## Identity
- Task ID: 20260823-pi-extension-host-7e4c91a2
- Mode: Feature
- Branch: codex/20260823-pi-extension-host-7e4c91a2-pi-extension-host
- Worktree: D:\Datas\OthersProjects\makelore-pi-extension-host-7e4c91a2
- Base commit: 47159b3cbdf06ea66db9a4425e0a9c13bcd73f7f
- Owner: codex
- Status: Completed — pending planner review
## Scope
- Implement `PI-070 — Managed extension host, interactions, and write lease`
from the `【架构】opencode2pi` Spec/ticket graph on cumulative baseline
`47159b3cbdf06ea66db9a4425e0a9c13bcd73f7f`.
- Own the Main-private versioned Makelore Pi extension entry, authenticated
worker bridge/registry, interaction lifecycle, project mutation write lease,
and the smallest PI-060 projector/runtime integration required to expose
safe product state.
- Add focused real-bridge, stale-token/generation, interaction lifecycle, and
lease concurrency/cancellation tests. Do not wire Renderer Host routes,
subagents, product browser/tools, or final packaging owned by later tickets.
## Intent And Constraints
- Follow planner task `20260822-pi-runtime-spec-b6e2c9a4`, ticket `PI-070`, and
Spec `LEASE-001` through `LEASE-006`, `EXT-001` through `EXT-006`, and section
9.3 interaction semantics.
- Load exactly one explicit versioned Makelore extension bundle. Project/user
Pi extension discovery remains disabled; no generic plugin platform,
permission engine, compatibility layer, signature/hash framework, or remote
authorization scaffold is in scope.
- Electron Main owns bridge tokens, worker identity, project identity,
interactions, leases, and diagnostics. Every bridge request must match the
registered conversation, worker generation, run, and short-lived token;
disposed/recovered workers and old generations fail closed without affecting
the current worker.
- `ask_user` supports select/confirm/input/editor and correlates responses only
by the pending interaction ID. Worker abort/crash/dispose cancels pending
interactions. Unknown UI/widget/details are bounded diagnostics, never raw
product state or logs.
- Mutation write/edit/bash work uses one cancellable FIFO project lease; reads
and pure reasoning bypass it, different projects may mutate concurrently,
and complete/error/abort/timeout/crash/dispose always release ownership.
- Draft/editor suggestions are revision-conditional and must not overwrite a
newer user draft. Host routes and Renderer consumption remain for PI-100/
PI-130; this ticket records the Main-side contract only.
- Real external Provider validation remains **Explicitly Waived / Accepted
Risk** with `realTurnVerified=false`; macOS x64/arm64 remains deferred to
mandatory PI-150 validation. Neither is a Pass.
- Do not create subagents. Keep changes surgical and rooted in this isolated
worktree.
## Plan
1. Verify the locked Pi 0.84.2 extension/tool interface and the existing
PI-040/050/060 seams; define the smallest product-owned bridge interface.
2. Implement the versioned managed extension bundle plus authenticated worker
registry/bridge and prove real bridge round trips and stale identity rejection.
3. Implement interaction storage/projection and the cancellable project write
lease, then wire generation/run cleanup into the runtime without widening
Host/Renderer interfaces.
4. Run focused tests, all Pi tests, typecheck, lint, build, and relevant
Electron/package checks; update this task record, pass documentation drift,
commit, and hand the exact implementation HEAD to the planner for review.
## Outcome
- Implemented a Main-private, versioned `makelore-runtime-v1.mjs` bundle and
changed Pi startup from `--no-tools` to an explicit built-in plus
`ask_user` allowlist. Discovery remains disabled with `--no-extensions`,
while the managed opener appends exactly one product-owned `--extension`.
- Added `PiManagedExtensionHost`, a loopback-only HTTP bridge with one random
bearer token per worker generation. Every request must match token,
Conversation, generation, and active run; worker invalidation/dispose makes
the token unusable and releases all owned or waiting resources. Active run
binding is inherited synchronously by a replacement generation before its
child process starts, closing the stale-rebuild race.
- Added the self-contained extension `ask_user` tool for select, confirm,
input, and editor requests. Pi's fire-and-forget
`extension_ui_response` records now use a dedicated one-way transport seam
rather than pretending to be request/response commands.
- Added a Host-side `PiInteractionStore` with exact interaction/option
correlation, current generation/run validation, terminal status projection,
and generation-scoped abort/crash/dispose cancellation. Runtime dialog
events pass through the store before entering product patches.
- Added a FIFO `PiProjectWriteLeaseCoordinator` and extension hooks that make
`write`, `edit`, and `bash` share one mutation lease per project. Reads and
reasoning bypass it; different projects proceed in parallel. Tool result,
agent end, session shutdown, abort, crash, recovery, dispose, and Host
shutdown all reach an idempotent release path; waiting bridge requests are
cancellable.
- Added safe projection for notify/status/registered widget/title/editor-text
events. Unknown widget/UI payloads become bounded method-only diagnostics;
editor text is emitted only when the current draft revision still equals the
run-start revision.
- The Pi 0.84.2 real-child smoke now loads the materialized managed extension
through Electron Node and confirms RPC readiness without extension-load or
stdout-protocol failure. No real external Provider was contacted.
- Real Provider validation remains **Explicitly Waived / Accepted Risk** with
`realTurnVerified=false`; provider concurrency, credential isolation, and
protocol compatibility are not Pass. macOS x64/arm64 remains deferred to
PI-150 and is not Pass.
## Verification
- `corepack pnpm run typecheck` — passed.
- `corepack pnpm run lint:check` — passed with 0 errors; the repository's 6
pre-existing React warnings remain outside PI-070.
- Focused PI-070 plus predecessor regression suite — 9 files / 41 tests
passed, covering worker transport/pool/runtime, exact interaction response,
stale identity, loopback bridge, lease FIFO/cancellation, draft revision,
and managed opener arguments.
- `corepack pnpm vitest run tests/unit/pi-extension-bundle.test.ts` — 1/1
passed by loading the actual materialized bundle, executing its mutation
hooks, and observing real bridge lease blocking/release; a read hook bypassed
the lease.
- `corepack pnpm vitest run tests/unit/pi-worker-process-real.test.ts` — 1/1
passed against locked `@earendil-works/pi-coding-agent@0.84.2` through the
Electron Node child process with the managed extension explicitly loaded.
- `corepack pnpm test` — 199 files / 2192 tests passed before the final bundle
integration test was added; that new test then passed independently, making
all 200 current test files green across the two recorded runs.
- `corepack pnpm run build:vite` — passed for Renderer, Electron Main,
Preload, and release utility output. Existing dynamic-import and chunk-size
warnings remain unchanged.
- `git diff --check` — passed.
- Electron E2E was not applicable: PI-070 intentionally adds no Renderer or
Host API route; PI-100/PI-130 own that user-visible wiring. macOS and real
Provider checks were intentionally not run under the explicit decisions
above.
## Follow-ups
- PI-100 must construct one shared `PiManagedExtensionHost` for the managed
opener and `PiConversationRuntime`, expose sanitized interaction methods
through Host API, and keep bridge tokens/leases Main-private.
- PI-130 must consume safe UI projections, supply the actual per-Conversation
draft revision, register only product-owned widget keys, and render terminal
interaction states without exposing Pi wire payloads.
- PI-080/PI-120 child workers must use this same project lease and worker-run
identity model; they must not introduce a second per-child mutation lock.
- PI-150 owns staging the managed extension in final packaged closures and the
deferred macOS x64/arm64 matrix.
- Real Provider concurrency, credential isolation, authentication, abort,
image/model/session behavior, and protocol variation remain accepted
unverified risks with `realTurnVerified=false`.
## Promotion Candidates
- Target: Pi runtime extension and process architecture.
Proposal: retain discovery-disabled startup and load exactly one explicitly
materialized, versioned Makelore extension; keep its loopback endpoint,
bearer token registry, run context, interactions, and diagnostics inside
Electron Main.
Evidence: managed opener assertions, the locked Pi 0.84.2 real-child smoke,
stale-token/generation bridge tests, and the actual-bundle integration test
all pass.
Future impact: PI-100 and PI-150 can wire Host routes and packaging without
creating a project/user plugin surface or moving Pi protocol objects into
Renderer.
Semantic conflicts: canonical product-state documents still describe the
active OpenCode product and must not claim Pi is live before the later
cutover ticket is integrated.
Human confirmation required: no for the internal architecture; yes before
canonical product-state text claims the Pi cutover is released.
- Target: Pi mutation concurrency architecture.
Proposal: all parent and future child `write`/`edit`/`bash` executions for a
project share one cancellable FIFO Main-owned lease; read-only tools and
other projects bypass that queue.
Evidence: coordinator and real HTTP/bundle tests prove same-project
serialization, cross-project parallelism, cancellation, terminal release,
and current-worker survival after stale identity rejection.
Future impact: PI-080/PI-120 can add subagents without inventing competing
locks or weakening project isolation.
Semantic conflicts: none found.
Human confirmation required: no.
- Target: PI-130 draft and extension UI projection.
Proposal: capture draft revision at run start and apply `set_editor_text`
only if the revision is unchanged; accept status/widget projections only for
registered product keys and retain bounded method-only diagnostics for
unknown payloads.
Evidence: focused projector tests prove accepted and stale draft paths plus
unknown-widget suppression without raw details.
Future impact: prevents asynchronous agent UI events from overwriting newer
user edits and keeps extension detail schemas product-owned.
Semantic conflicts: none found.
Human confirmation required: no.