From 5666b3014af1cc78430edd4a5092d8898000d8a2 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 2 Jun 2026 18:09:50 +0800 Subject: [PATCH 1/3] Fix Pi extension command turns (#1290) --- .../server/agent/providers/pi/agent.test.ts | 34 +++++++++++++++++++ .../src/server/agent/providers/pi/agent.ts | 14 ++++++++ .../server/agent/providers/pi/rpc-types.ts | 4 +++ 3 files changed, 52 insertions(+) diff --git a/packages/server/src/server/agent/providers/pi/agent.test.ts b/packages/server/src/server/agent/providers/pi/agent.test.ts index 76589743d..5b4eb5838 100644 --- a/packages/server/src/server/agent/providers/pi/agent.test.ts +++ b/packages/server/src/server/agent/providers/pi/agent.test.ts @@ -97,6 +97,18 @@ class SessionEvents { .map((event) => event.item); } + timelineAndCompletionEvents() { + return this.events.flatMap((event) => { + if (event.type === "timeline") { + return [{ type: "timeline" as const, item: event.item }]; + } + if (event.type === "turn_completed") { + return [{ type: "turn_completed" as const }]; + } + return []; + }); + } + nextTurnCompletion(): Promise> { return this.nextEvent( (event): event is Extract => @@ -459,6 +471,28 @@ describe("PiRpcAgentSession", () => { ]); }); + test("surfaces Pi extension command messages and completes when no agent turn starts", async () => { + const { pi, session, events } = await createSession(); + const fakeSession = pi.latestSession(); + + await session.startTurn("/show-status"); + fakeSession.emit({ + type: "message_end", + message: { + role: "custom", + content: [{ type: "text", text: "Extension command output" }], + }, + }); + + expect(events.timelineAndCompletionEvents()).toEqual([ + { + type: "timeline", + item: { type: "assistant_message", text: "Extension command output" }, + }, + { type: "turn_completed" }, + ]); + }); + test("adds Pi assistant context to generic provider finish errors", async () => { const { pi, session, events } = await createSession(); diff --git a/packages/server/src/server/agent/providers/pi/agent.ts b/packages/server/src/server/agent/providers/pi/agent.ts index eb0e1de5e..b76ab5c7b 100644 --- a/packages/server/src/server/agent/providers/pi/agent.ts +++ b/packages/server/src/server/agent/providers/pi/agent.ts @@ -1499,6 +1499,20 @@ export class PiRpcAgentSession implements AgentSession { event: Extract, turnId: string | undefined, ): void { + if (event.message.role === "custom") { + const text = getUserMessageText(event.message.content); + if (text) { + this.emit({ + type: "timeline", + provider: PI_PROVIDER, + turnId, + item: { type: "assistant_message", text }, + }); + } + this.completeTurn(turnId, []); + return; + } + if (event.message.role !== "user") { return; } diff --git a/packages/server/src/server/agent/providers/pi/rpc-types.ts b/packages/server/src/server/agent/providers/pi/rpc-types.ts index aab5f9424..90f9aa4e1 100644 --- a/packages/server/src/server/agent/providers/pi/rpc-types.ts +++ b/packages/server/src/server/agent/providers/pi/rpc-types.ts @@ -30,6 +30,10 @@ export type PiAgentMessage = role: "user"; content: string | Array; } + | { + role: "custom"; + content: string | Array; + } | { role: "assistant"; content: PiAssistantContent[]; From 4f3116d28a777c120d3764329506be8491cfe3d9 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 2 Jun 2026 19:49:58 +0800 Subject: [PATCH 2/3] Unify workspace service URLs (#1288) * Unify workspace service proxying * Clean up service proxy review issues --- docs/development.md | 4 +- docs/service-proxy.md | 58 +- .../src/utils/workspace-script-links.test.ts | 123 +- .../app/src/utils/workspace-script-links.ts | 26 +- packages/protocol/src/messages.ts | 2 + .../protocol/src/messages.workspaces.test.ts | 48 + .../server/src/server/bootstrap.smoke.test.ts | 251 ++++ packages/server/src/server/bootstrap.ts | 428 +++---- .../server/src/server/config-relay.test.ts | 33 +- packages/server/src/server/config.ts | 35 +- .../server/src/server/persisted-config.ts | 3 + .../src/server/script-health-monitor.test.ts | 22 +- .../src/server/script-health-monitor.ts | 22 +- .../server/src/server/script-proxy.test.ts | 484 +------- packages/server/src/server/script-proxy.ts | 360 +----- .../script-route-branch-handler.test.ts | 149 ++- .../src/server/script-route-branch-handler.ts | 72 +- .../server/script-status-projection.test.ts | 104 +- .../src/server/script-status-projection.ts | 199 +-- .../server/src/server/service-proxy.test.ts | 295 +++++ packages/server/src/server/service-proxy.ts | 1082 +++++++++++++++++ packages/server/src/server/session.test.ts | 6 +- packages/server/src/server/session.ts | 24 +- .../session.workspace-git-watch.test.ts | 28 +- .../src/server/test-utils/paseo-daemon.ts | 2 + .../src/server/test-utils/session-stubs.ts | 8 +- .../server/src/server/websocket-server.ts | 14 +- .../src/server/workspace-service-env.test.ts | 35 +- .../src/server/workspace-service-env.ts | 22 +- .../server/worktree-bootstrap.posix.test.ts | 22 +- .../src/server/worktree-bootstrap.test.ts | 108 +- .../server/src/server/worktree-bootstrap.ts | 41 +- .../src/server/worktree-session.test.ts | 4 +- .../server/src/server/worktree-session.ts | 4 +- .../server/src/utils/script-hostname.test.ts | 14 +- packages/server/src/utils/script-hostname.ts | 57 +- .../public/schemas/paseo.config.v1.json | 3 + public-docs/worktrees.md | 10 +- 38 files changed, 2664 insertions(+), 1538 deletions(-) create mode 100644 packages/server/src/server/service-proxy.test.ts create mode 100644 packages/server/src/server/service-proxy.ts diff --git a/docs/development.md b/docs/development.md index c76e87b61..982d80996 100644 --- a/docs/development.md +++ b/docs/development.md @@ -158,12 +158,14 @@ Every `scripts` entry with `"type": "service"` receives these environment variab | Variable | Value | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| `PASEO_SERVICE__URL` | Proxied daemon URL for a declared peer service. Prefer this for peer discovery; it survives peer restarts. | +| `PASEO_SERVICE__URL` | Proxied URL for a declared peer service. Prefer this for peer discovery; it survives peer restarts. | | `PASEO_SERVICE__PORT` | Raw ephemeral port for a declared peer service. Use only as a bypass escape hatch; it can go stale if that peer restarts. | | `PASEO_URL` | Self alias for `PASEO_SERVICE__URL`. | | `PASEO_PORT` | Self alias for `PASEO_SERVICE__PORT`. | | `HOST` | Bind host for the service process. | +Service proxy hostnames use the double-dash shape: `web--feature-auth--project.localhost` or, on the default branch, `web--project.localhost`. Optional public aliases use the same leftmost label under the configured public base host. + `` is normalized from the script name by uppercasing it, replacing each run of non-`A-Z0-9` characters with `_`, and trimming leading or trailing `_`. For example, `app-server` and `app.server` both normalize to `APP_SERVER`; that collision fails at spawn time with an actionable error. `PORT` is not injected by default. If a framework requires `PORT`, set it in the command: diff --git a/docs/service-proxy.md b/docs/service-proxy.md index 822a4c3e5..6e11f3e99 100644 --- a/docs/service-proxy.md +++ b/docs/service-proxy.md @@ -1,6 +1,6 @@ # Service Proxy -Paseo can proxy HTTP traffic to services running inside your workspaces and expose them at stable, publicly-accessible URLs. This lets you open a running dev server from your phone or share it with a teammate without any tunneling tools. +Paseo proxies HTTP traffic to services running inside your workspaces. Localhost service URLs are always enabled; optional public aliases and a separate service-only listener can be layered on through config. ## How it works @@ -9,22 +9,22 @@ When a `paseo.json` script of `"type": "service"` starts, Paseo assigns it a loc The generated hostname is built from the script name, branch, and project: ``` -