feat(examples): add experimental pi harness example - #2210
Conversation
…e UI Trim the example-local harness to what the demo and test use: drop the execution-environment type surface, routed calls, lane access, and the follow-up, next-run, cancel-queued, and lane-config transport commands. The WebSocket protocol is now snapshot, subscribe, submit, abort, and steer. Rebuild the client on the same Kumo chat layout as the other examples: a pinned header, a scrolling transcript with user bubbles and assistant tool cards, an Empty state with suggestion buttons, and an InputArea composer with a stop button while a turn runs. Rewrite the README and RFC to describe this as an experimental example on a pinned pi dev build, not a package API, and list what must land before it becomes one. Claude-Session: https://claude.ai/code/session_01KEFnjoMnZBMewsuD9qxGrL
|
There was a problem hiding this comment.
Note
This report is out of date. Scroll down for Devin Review's latest report on this PR.
Devin Review found 4 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| default: | ||
| return; |
| error: PiOperationRejectedError, | ||
| submission?: QueuedSubmission | ||
| ): void { | ||
| this.#rejections.set(operationId, error); |
There was a problem hiding this comment.
🔴 Rejected operations lose terminal results
When pi rejects or withdraws queued work, #reject records the outcome only in memory. Eviction loses it, making later waitForResult calls poll forever.
Prompt for agents
PiHarness deletes a rejected submission from durable intake but stores its terminal PiOperationRejectedError only in #rejections. Persist declined and withdrawn outcomes keyed by operation ID, and make waitForResult and getResult consult that durable record. Preserve results for repeated or concurrent waiters instead of consuming them on the first read. Reconcile the durable result with operation_end stream delivery and any retention policy.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const existing = this.#writers.get(operationId); | ||
| if (existing) return existing; |
There was a problem hiding this comment.
🟡 agents import sizesMeasured 294 runtime imports as minified bundles. The primary size is gzip; raw minified size is included for diagnosis. An existing import growing by more than 10% is marked red. This report is informational.
Compared Changed imports (135)
All 267 current runtime imports
Reported by agent-think[bot]. |
Build usePiSession on useAgent from agents/react instead of a hand-rolled WebSocket with its own reconnect loop. The protocol over the WebSockets capability is unchanged. Claude-Session: https://claude.ai/code/session_01KEFnjoMnZBMewsuD9qxGrL
There was a problem hiding this comment.
Note
This report is out of date. Scroll down for Devin Review's latest report on this PR.
Devin Review found 4 new potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| this.#reject( | ||
| lane, | ||
| operationId, | ||
| "run", | ||
| new PiOperationRejectedError( |
| try { | ||
| return ( | ||
| (await routeAgentRequest(request, env, { cors: true })) ?? | ||
| new Response("Not found", { status: 404 }) | ||
| ); |
There was a problem hiding this comment.
| function isClientMessage(value: unknown): value is PiClientMessage { | ||
| return ( | ||
| typeof value === "object" && | ||
| value !== null && | ||
| "type" in value && | ||
| typeof value.type === "string" | ||
| ); |
There was a problem hiding this comment.
| } | ||
| try { | ||
| return ( | ||
| (await routeAgentRequest(request, env, { cors: true })) ?? |
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
Deploy as pi-harness-example, give the Vite dev server its own inspector port so it can run beside the other harness examples, and note CLOUDFLARE_ACCOUNT_ID for logins with several accounts.
agents/react resolved a second React copy from the agents package, which broke every hook call in the browser. Also load the agents Vite plugin for the bundled skill.
Resync the transcript from the durable snapshot at operation boundaries so the prompt entry appears, and ignore a message event whose id is already in the transcript. Shorten the empty-state copy.
The client resyncs its transcript from the durable snapshot at operation boundaries, so the reply must arrive as a snapshot rather than a generic result.
There was a problem hiding this comment.
Devin Review found 1 new potential issue.
4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| submissions.insert(lane, operationId, request); | ||
| await this.#ensureLaneDriver(lane); |
There was a problem hiding this comment.
🔴 Queued work can lose its wake
submit commits work before #ensureLaneDriver establishes its wake, while duplicate retries skip repair. Enqueue failures or exiting drivers can strand accepted work.
Prompt for agents
Make submission persistence and lane-driver wake establishment uphold one durable invariant. In examples/next/harnesses/pi/src/harness/pi-harness.ts, submit() currently inserts before #ensureLaneDriver(), duplicate submissions return without repairing a missing wake, and #startLaneDriver() can observe a running driver just before that driver exits idle. Ensure every pending intake row has a durable future driver even after enqueue errors and the driver-exit race. Retries of an existing submission must repair that wake before returning. Consider a deterministic per-lane task identity or idempotency key plus a final pending-work check coordinated with driver completion.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Reduces #2197 to an example. Nothing is added to the
agentspackage.examples/next/harnesses/pihosts pi's durableAgentHarnesson a plainDurableObjectcomposed withLifecycle,Tasks,Streams,WebSockets, andagents/skills.PiHarness, the Workers AI provider, and the pi session adapter are example-local code on a pinned pi dev build (c4b0e35a, vendored undervendor/pi-dev).design/rfc-pi-harness-example.mdrecords the composition, the known costs, and what must land before this becomes a package export.Why not a package export yet
agents/sessions(feat(agents): move sessions into a Lifecycle capability #2196) has not landed, so pi's session lives in a namespaced SQLite adapter carrying pi's own schema.Supersedes #2197.