Skip to content

Commit 7388b0d

Browse files
committed
feat(harness): expose native Pi runtime
1 parent fc33dfb commit 7388b0d

29 files changed

Lines changed: 537 additions & 321 deletions

.github/workflows/build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ jobs:
7777
- name: Build enricher
7878
run: pnpm --filter @posthog/enricher build
7979

80+
- name: Build harness
81+
run: pnpm --filter @posthog/harness build
82+
8083
- name: Build agent
8184
run: pnpm --filter agent build
8285

.github/workflows/code-build-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ jobs:
8888
pnpm --filter @posthog/shared run build
8989
pnpm --filter @posthog/git run build
9090
pnpm --filter @posthog/enricher run build
91+
pnpm --filter @posthog/harness run build
9192
pnpm --filter @posthog/agent run build
9293
9394
# build/Assets.car is gitignored; regenerate it so the packaged app ships
@@ -205,6 +206,7 @@ jobs:
205206
pnpm --filter @posthog/shared run build
206207
pnpm --filter @posthog/git run build
207208
pnpm --filter @posthog/enricher run build
209+
pnpm --filter @posthog/harness run build
208210
pnpm --filter @posthog/agent run build
209211
210212
- name: Build app
@@ -275,6 +277,7 @@ jobs:
275277
pnpm --filter @posthog/shared run build
276278
pnpm --filter @posthog/git run build
277279
pnpm --filter @posthog/enricher run build
280+
pnpm --filter @posthog/harness run build
278281
pnpm --filter @posthog/agent run build
279282
280283
- name: Build app

.github/workflows/code-release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ jobs:
140140
- name: Build enricher package
141141
run: pnpm --filter @posthog/enricher run build
142142

143+
- name: Build harness package
144+
run: pnpm --filter @posthog/harness run build
145+
143146
- name: Build agent package
144147
run: pnpm --filter @posthog/agent run build
145148

@@ -331,6 +334,9 @@ jobs:
331334
- name: Build enricher package
332335
run: pnpm --filter @posthog/enricher run build
333336

337+
- name: Build harness package
338+
run: pnpm --filter @posthog/harness run build
339+
334340
- name: Build agent package
335341
run: pnpm --filter @posthog/agent run build
336342

@@ -457,6 +463,9 @@ jobs:
457463
- name: Build enricher package
458464
run: pnpm --filter @posthog/enricher run build
459465

466+
- name: Build harness package
467+
run: pnpm --filter @posthog/harness run build
468+
460469
- name: Build agent package
461470
run: pnpm --filter @posthog/agent run build
462471

.github/workflows/code-storybook.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,20 @@ jobs:
149149
pid2=$!
150150
wait_all "$pid1" "$pid2"
151151
152-
# @posthog/agent imports @posthog/git's dist, so git must finish
153-
# before the last group starts.
154-
pnpm --filter @posthog/git build
155-
156-
pnpm --filter @posthog/enricher build &
152+
# @posthog/agent imports the dist of @posthog/git and
153+
# @posthog/harness, so both must finish before the last group starts.
154+
pnpm --filter @posthog/git build &
157155
pid3=$!
158-
pnpm --filter @posthog/agent build &
156+
pnpm --filter @posthog/harness build &
159157
pid4=$!
160158
wait_all "$pid3" "$pid4"
161159
160+
pnpm --filter @posthog/enricher build &
161+
pid5=$!
162+
pnpm --filter @posthog/agent build &
163+
pid6=$!
164+
wait_all "$pid5" "$pid6"
165+
162166
- name: Build Storybook
163167
working-directory: apps/code
164168
run: pnpm build-storybook

.github/workflows/code-update-e2e.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ jobs:
106106
pnpm --filter @posthog/shared run build
107107
pnpm --filter @posthog/git run build
108108
pnpm --filter @posthog/enricher run build
109+
pnpm --filter @posthog/harness run build
109110
pnpm --filter @posthog/agent run build
110111
111112
- name: Build old + new update pair

.github/workflows/pr-build-installer.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
pnpm --filter @posthog/shared run build
9494
pnpm --filter @posthog/git run build
9595
pnpm --filter @posthog/enricher run build
96+
pnpm --filter @posthog/harness run build
9697
pnpm --filter @posthog/agent run build
9798
9899
# build/Assets.car is gitignored; regenerate it so the packaged app ships

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ jobs:
140140
pnpm --filter @posthog/shared build
141141
pnpm --filter @posthog/git build
142142
pnpm --filter @posthog/enricher build
143-
pnpm --filter agent build &
144143
wait
144+
pnpm --filter @posthog/harness build
145+
pnpm --filter agent build
145146
146147
- name: Package Electron app
147148
run: pnpm --filter code run package

packages/agent/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@
8484
"types": "./dist/handoff-checkpoint.d.ts",
8585
"import": "./dist/handoff-checkpoint.js"
8686
},
87+
"./pi": {
88+
"types": "./dist/pi/rpc-client.d.ts",
89+
"import": "./dist/pi/rpc-client.js"
90+
},
8791
"./server": {
8892
"types": "./dist/server/agent-server.d.ts",
8993
"import": "./dist/server/agent-server.js"
@@ -136,13 +140,15 @@
136140
"@agentclientprotocol/sdk": "1.1.0",
137141
"@anthropic-ai/claude-agent-sdk": "0.3.197",
138142
"@anthropic-ai/sdk": "0.109.0",
143+
"@earendil-works/pi-coding-agent": "catalog:",
139144
"@hono/node-server": "^1.19.9",
140145
"@openai/codex": "0.140.0",
141146
"@opentelemetry/api-logs": "^0.208.0",
142147
"@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
143148
"@opentelemetry/resources": "^2.0.0",
144149
"@opentelemetry/sdk-logs": "^0.208.0",
145150
"@opentelemetry/semantic-conventions": "^1.28.0",
151+
"@posthog/harness": "workspace:*",
146152
"@types/jsonwebtoken": "^9.0.10",
147153
"commander": "^14.0.2",
148154
"fflate": "^0.8.2",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { RpcClient } from "@earendil-works/pi-coding-agent";
2+
import { describe, expect, it } from "vitest";
3+
import { createPiRpcClient } from "./rpc-client";
4+
5+
describe("createPiRpcClient", () => {
6+
it("does not put provider credentials in the child environment", () => {
7+
const client = createPiRpcClient({
8+
cwd: "/workspace",
9+
model: "claude-opus-4-8",
10+
providerOptions: { apiKey: "token", region: "us" },
11+
});
12+
13+
expect(client).toBeInstanceOf(RpcClient);
14+
expect(client).toMatchObject({
15+
options: {
16+
cwd: "/workspace",
17+
model: "claude-opus-4-8",
18+
provider: "posthog",
19+
},
20+
});
21+
expect(
22+
(client as unknown as { options: { env?: Record<string, string> } })
23+
.options.env,
24+
).toBeUndefined();
25+
});
26+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2+
import { tmpdir } from "node:os";
3+
import { dirname, join } from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
import {
6+
RpcClient,
7+
type RpcClientOptions,
8+
} from "@earendil-works/pi-coding-agent";
9+
import type { PosthogProviderOptions } from "@posthog/harness/extensions/posthog-provider/provider";
10+
11+
export type PiRpcClient = RpcClient;
12+
13+
interface RpcClientOptionsAccess {
14+
options: RpcClientOptions;
15+
}
16+
17+
export type PiRpcClientOptions = Pick<RpcClientOptions, "cwd" | "model"> & {
18+
providerOptions?: PosthogProviderOptions;
19+
};
20+
21+
export function createPiRpcClient(
22+
options: PiRpcClientOptions = {},
23+
): PiRpcClient {
24+
const { providerOptions, ...rpcOptions } = options;
25+
const client = new RpcClient({
26+
...rpcOptions,
27+
cliPath: fileURLToPath(new URL("./rpc-host.js", import.meta.url)),
28+
provider: "posthog",
29+
});
30+
const start = client.start.bind(client);
31+
const clientOptions = (client as unknown as RpcClientOptionsAccess).options;
32+
const baseArgs = clientOptions.args ?? [];
33+
34+
client.start = async () => {
35+
const directory = mkdtempSync(join(tmpdir(), "posthog-pi-bootstrap-"));
36+
const bootstrapFile = join(directory, "bootstrap.json");
37+
writeFileSync(bootstrapFile, JSON.stringify({ providerOptions }), {
38+
mode: 0o600,
39+
});
40+
clientOptions.args = [
41+
...baseArgs,
42+
"--posthog-bootstrap-file",
43+
bootstrapFile,
44+
];
45+
46+
try {
47+
await start();
48+
} catch (error) {
49+
rmSync(dirname(bootstrapFile), { force: true, recursive: true });
50+
throw error;
51+
}
52+
};
53+
54+
return client;
55+
}

0 commit comments

Comments
 (0)