From 589ff0e0e32a0ac6a0a13487020e64aa21c03e22 Mon Sep 17 00:00:00 2001 From: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com> Date: Tue, 27 Oct 2026 01:26:50 -0700 Subject: [PATCH 1/3] fix(pi): report the running host package instead of an installed peer --- bt-daemon/docs/protocol.md | 6 ++ bt-daemon/src/translate/pi.rs | 14 ++++- bt-daemon/tests/pi_translator.rs | 27 ++++++++ src/plugins/pi/content/README.md | 6 ++ .../pi/content/src/daemon-adapter.test.ts | 18 ++++++ src/plugins/pi/content/src/index.ts | 5 +- src/plugins/pi/content/src/pi-package.test.ts | 61 +++++++++++++++++-- src/plugins/pi/content/src/pi-package.ts | 19 ++++-- 8 files changed, 142 insertions(+), 14 deletions(-) diff --git a/bt-daemon/docs/protocol.md b/bt-daemon/docs/protocol.md index 8f0eaf3c..eb48e630 100644 --- a/bt-daemon/docs/protocol.md +++ b/bt-daemon/docs/protocol.md @@ -271,6 +271,12 @@ coding-agent span falls back to Rust SDK provenance. Transcript backfills captur versions available at materialization, not unrecorded historical versions. See the [span-origin contract](../../docs/coding-agent-tracing-plugin-spec.md#621-span-origin-versions). +Pi-compatible hosts retain the canonical transport source `pi` so they share its +translator. The Pi extension sends the running host package's version as +`source_version` and its package name as `payload.host_name`. The translator records +`metadata.host_name` and uses a fork's package name as the session-root label; +upstream Pi and older extension payloads retain the `Pi` label. + For acknowledged spans from legacy ledgers without saved snapshots, stateless updates omit `context.span_origin` entirely, including SDK defaults and inherited parent origin fields. This preserves existing backend provenance without guessing diff --git a/bt-daemon/src/translate/pi.rs b/bt-daemon/src/translate/pi.rs index 196b1abf..c0e03e8a 100644 --- a/bt-daemon/src/translate/pi.rs +++ b/bt-daemon/src/translate/pi.rs @@ -573,6 +573,14 @@ impl PiTranslator { metadata.insert("model".into(), json!(model)); } metadata.insert("pi_version".into(), json!(envelope.source_version)); + let host_name = envelope + .payload + .get("host_name") + .and_then(Value::as_str) + .filter(|name| !name.is_empty()); + if let Some(host_name) = host_name { + metadata.insert("host_name".into(), json!(host_name)); + } metadata.insert( "extension_version".into(), envelope @@ -601,7 +609,11 @@ impl PiTranslator { span_id: self.root_span_id.clone(), root_span_id: self.effective_root_span_id.clone(), parent_span_ids: self.external_parent.clone().into_iter().collect(), - name: "Pi".into(), + name: match host_name { + Some("@earendil-works/pi-coding-agent" | "@mariozechner/pi-coding-agent") + | None => "Pi".into(), + Some(name) => name.into(), + }, span_type: SpanType::Task, start_ms: Some(envelope.ts_ms), metadata: Some(Value::Object(metadata)), diff --git a/bt-daemon/tests/pi_translator.rs b/bt-daemon/tests/pi_translator.rs index 03ad6bfe..0e3484fa 100644 --- a/bt-daemon/tests/pi_translator.rs +++ b/bt-daemon/tests/pi_translator.rs @@ -72,6 +72,33 @@ fn reduce(ops: Vec) -> HashMap { rows } +#[test] +fn pi_records_the_running_fork_identity() { + let registry = Registry::default_agents(); + let mut translator = registry.create("pi", "pi-session"); + let ctx = SessionCtx { + session_id: "pi-session".into(), + config: None, + }; + let mut start = event("session_start", 1, json!({"reason":"new"})); + start.source_version = Some("18.12.6".into()); + start.payload["host_name"] = json!("@oh-my-pi/pi-coding-agent"); + let ops = translator.handle(&start, &ctx).unwrap(); + let root = ops + .iter() + .find_map(|op| match op { + SpanOp::Insert(row) if row.parent_span_ids.is_empty() => Some(row), + _ => None, + }) + .unwrap(); + assert_eq!(root.name, "@oh-my-pi/pi-coding-agent"); + assert_eq!( + root.metadata.as_ref().unwrap()["host_name"], + "@oh-my-pi/pi-coding-agent" + ); + assert_eq!(root.metadata.as_ref().unwrap()["pi_version"], "18.12.6"); +} + #[test] fn pi_builds_turn_llm_tool_compaction_and_shutdown_spans() { let registry = Registry::default_agents(); diff --git a/src/plugins/pi/content/README.md b/src/plugins/pi/content/README.md index 119d1705..f188a3e1 100644 --- a/src/plugins/pi/content/README.md +++ b/src/plugins/pi/content/README.md @@ -69,6 +69,12 @@ from each of the last five stable Pi release lines. The compatibility job resolves these versions on each run, including releases from Pi's former npm package name when needed. +Package detection prefers the running CLI to an extension-local peer dependency. +Forks are recognized by a `piConfig` field, a known package name, or `PI_PACKAGE_DIR`. The trace records +the host package name in `metadata.host_name` and its version in `metadata.pi_version`; +fork sessions use their package name instead of the `Pi` root label. +Oh My Pi (`@oh-my-pi/pi-coding-agent`) uses `.omp` as its default config directory. + ## Configuration Settings load in this order, with later values taking precedence: diff --git a/src/plugins/pi/content/src/daemon-adapter.test.ts b/src/plugins/pi/content/src/daemon-adapter.test.ts index 76e10658..059315a4 100644 --- a/src/plugins/pi/content/src/daemon-adapter.test.ts +++ b/src/plugins/pi/content/src/daemon-adapter.test.ts @@ -70,6 +70,14 @@ vi.mock("./legacy-session.ts", () => ({ }, })); +vi.mock("./pi-package.ts", () => ({ + runningPiPackage: () => ({ + name: "@oh-my-pi/pi-coding-agent", + version: "18.12.6", + configDir: ".omp", + }), +})); + type Handler = (...args: unknown[]) => Promise; /** Load the extension against a fake Pi and return its registered handlers. */ @@ -112,6 +120,16 @@ describe("Pi daemon adapter", () => { expect(handlers.size).toBe(0); }); + it("forwards the running host name and version instead of an installed peer", async () => { + const handlers = await loadExtension(); + await handlers.get("session_start")?.({ reason: "new" }, fakeContext()); + expect(mockState.logs[0]).toMatchObject({ + source: "pi", + source_version: "18.12.6", + payload: { host_name: "@oh-my-pi/pi-coding-agent" }, + }); + }); + it("does not block session startup on daemon status", async () => { const ctx = fakeContext(); const handlers = await loadExtension(); diff --git a/src/plugins/pi/content/src/index.ts b/src/plugins/pi/content/src/index.ts index 1d169186..70a8b7eb 100644 --- a/src/plugins/pi/content/src/index.ts +++ b/src/plugins/pi/content/src/index.ts @@ -10,7 +10,7 @@ import { EXTENSION_VERSION } from "./version.ts"; const STATUS_KEY = "braintrust-tracing"; const WIDGET_KEY = "braintrust-trace-link"; const UI_STATUS_TIMEOUT_MS = 250; -const PI_VERSION = runningPiPackage().version; +const PI_HOST = runningPiPackage(); function sessionKeyFor( sessionFile: string | undefined, @@ -138,13 +138,14 @@ export default function braintrustPiExtension(pi: ExtensionAPI): void { if (!sessionId) return; await client.log({ source: "pi", - ...(PI_VERSION ? { source_version: PI_VERSION } : {}), + ...(PI_HOST.version ? { source_version: PI_HOST.version } : {}), session_id: sessionId, event: name, ts_ms: Date.now(), payload: { event: nativePayload(event), extension_version: EXTENSION_VERSION, + host_name: PI_HOST.name, session_file: descriptor?.sessionFile, native_session_id: descriptor?.nativeSessionId, legacy_resume: legacyContinuation, diff --git a/src/plugins/pi/content/src/pi-package.test.ts b/src/plugins/pi/content/src/pi-package.test.ts index e3d8d822..4a7726fa 100644 --- a/src/plugins/pi/content/src/pi-package.test.ts +++ b/src/plugins/pi/content/src/pi-package.test.ts @@ -42,7 +42,7 @@ describe("loadPiPackageMetadata", () => { symlinkSync(installPi(`node_modules/${PI}`, { name: PI, version: "1.0.0" }), bin); return [bin]; }, - { version: "1.0.0", configDir: ".pi" }, + { name: PI, version: "1.0.0", configDir: ".pi" }, ], [ "a fork with its own name and config directory", @@ -53,7 +53,7 @@ describe("loadPiPackageMetadata", () => { piConfig: { configDir: ".acme" }, }), ], - { version: "3.2.1", configDir: ".acme" }, + { name: "@acme/agent", version: "3.2.1", configDir: ".acme" }, ], [ "a PI_PACKAGE_DIR override", @@ -61,7 +61,7 @@ describe("loadPiPackageMetadata", () => { installPi("store/pi", { name: PI, version: "1.0.0" }); return [join(root, "store", "pi", "package.json"), join(root, "store", "bin")]; }, - { version: "1.0.0", configDir: ".pi" }, + { name: PI, version: "1.0.0", configDir: ".pi" }, ], [ "a Bun-compiled binary with package.json beside it", @@ -69,7 +69,7 @@ describe("loadPiPackageMetadata", () => { writeJson(join(root, "pi-linux-x64", "package.json"), { name: PI, version: "1.0.0" }); return [undefined, "/$bunfs/root/pi", join(root, "pi-linux-x64", "pi")]; }, - { version: "1.0.0", configDir: ".pi" }, + { name: PI, version: "1.0.0", configDir: ".pi" }, ], [ "an SDK application that depends on Pi", @@ -78,17 +78,66 @@ describe("loadPiPackageMetadata", () => { const pi = installPi(`app/node_modules/${PI}`, { name: PI, version: "0.99.2" }); return [join(root, "app", "main.js"), pi]; }, - { version: "0.99.2", configDir: ".pi" }, + { name: PI, version: "0.99.2", configDir: ".pi" }, ], [ "no discoverable Pi", () => [join(root, "missing.js")], - { version: undefined, configDir: ".pi" }, + { name: undefined, version: undefined, configDir: ".pi" }, ], ])("finds %s", (_name, setup, expected) => { expect(loadPiPackageMetadata(setup())).toEqual(expected); }); + it("prefers OMP's running manifest over an auto-installed upstream peer", () => { + const host = installPi("omp", { + name: "@oh-my-pi/pi-coding-agent", + version: "18.12.6", + bin: { omp: "dist/cli.js" }, + }); + const peer = installPi(`plugins/node_modules/${PI}`, { name: PI, version: "1.4.0" }); + expect(loadPiPackageMetadata([host, peer])).toEqual({ + name: "@oh-my-pi/pi-coding-agent", + version: "18.12.6", + configDir: ".omp", + }); + }); + + it("reports the embedded Pi for an SDK application's own CLI", () => { + const app = installPi("app", { + name: "acme-agent", + version: "0.3.0", + bin: { acme: "dist/cli.js" }, + dependencies: { [PI]: "^1.4.0" }, + }); + const pi = installPi(`app/node_modules/${PI}`, { name: PI, version: "1.4.0" }); + expect(loadPiPackageMetadata([app, pi])).toEqual({ + name: PI, + version: "1.4.0", + configDir: ".pi", + }); + }); + + it("finds a compiled OMP host beside its executable before the upstream peer", () => { + writeJson(join(root, "omp-release", "package.json"), { + name: "@oh-my-pi/pi-coding-agent", + version: "18.12.6", + }); + const peer = installPi(`plugins/node_modules/${PI}`, { name: PI, version: "1.4.0" }); + expect( + loadPiPackageMetadata(["/$bunfs/root/cli.ts", join(root, "omp-release", "omp"), peer]), + ).toMatchObject({ name: "@oh-my-pi/pi-coding-agent", version: "18.12.6", configDir: ".omp" }); + }); + + it("trusts the explicit PI_PACKAGE_DIR manifest for a custom host", () => { + writeJson(join(root, "custom", "package.json"), { name: "@acme/host", version: "3.2.1" }); + const peer = installPi(`plugins/node_modules/${PI}`, { name: PI, version: "1.4.0" }); + expect(loadPiPackageMetadata([join(root, "custom", "package.json"), peer])).toMatchObject({ + name: "@acme/host", + version: "3.2.1", + }); + }); + it("resolves the installed Pi development dependency through ESM conditions", () => { const metadata = loadPiPackageMetadata([resolvePiPackage()]); expect(metadata.version).toMatch(/^\d+\.\d+\.\d+/); diff --git a/src/plugins/pi/content/src/pi-package.ts b/src/plugins/pi/content/src/pi-package.ts index 4482a6bb..c3dc0341 100644 --- a/src/plugins/pi/content/src/pi-package.ts +++ b/src/plugins/pi/content/src/pi-package.ts @@ -1,10 +1,14 @@ import { readFileSync, realpathSync } from "node:fs"; import { createRequire } from "node:module"; -import { dirname, join } from "node:path"; +import { basename, dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const PI_PACKAGE = "@earendil-works/pi-coding-agent"; -const PI_PACKAGE_NAMES = new Set([PI_PACKAGE, "@mariozechner/pi-coding-agent"]); +const PI_PACKAGE_CONFIG_DIRS = new Map([ + [PI_PACKAGE, ".pi"], + ["@mariozechner/pi-coding-agent", ".pi"], + ["@oh-my-pi/pi-coding-agent", ".omp"], +]); interface PiPackageManifest { name?: unknown; @@ -15,6 +19,7 @@ interface PiPackageManifest { } export interface PiPackageMetadata { + name?: string; version?: string; configDir: string; } @@ -37,9 +42,12 @@ export function loadPiPackageMetadata(candidates: (string | undefined)[]): PiPac if (manifest) break; } return { + name: typeof manifest?.name === "string" ? manifest.name : undefined, version: typeof manifest?.version === "string" ? manifest.version : undefined, configDir: - typeof manifest?.piConfig?.configDir === "string" ? manifest.piConfig.configDir : ".pi", + typeof manifest?.piConfig?.configDir === "string" + ? manifest.piConfig.configDir + : (PI_PACKAGE_CONFIG_DIRS.get(String(manifest?.name)) ?? ".pi"), }; } @@ -50,8 +58,9 @@ function findPiManifest(entry: string | undefined): PiPackageManifest | undefine const manifest = readManifest(join(directory, "package.json")); if ( manifest && - ((typeof manifest.name === "string" && PI_PACKAGE_NAMES.has(manifest.name)) || - typeof manifest.piConfig === "object") + ((typeof manifest.name === "string" && PI_PACKAGE_CONFIG_DIRS.has(manifest.name)) || + (manifest.piConfig !== null && typeof manifest.piConfig === "object") || + (basename(entry) === "package.json" && typeof manifest.name === "string")) ) { return manifest; } From ded8b2c0a026fff78d0ac875d363b5a9536b8599 Mon Sep 17 00:00:00 2001 From: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com> Date: Tue, 27 Oct 2026 02:43:25 -0700 Subject: [PATCH 2/3] fix(pi): limit host detection to identity metadata Keep Oh My Pi sessions on the extension's existing .pi settings path and the Pi session-root label. Only the reported host name and version change. Drop the explicit-manifest rule so a stale PI_PACKAGE_DIR cannot select an unrelated parent package. --- bt-daemon/docs/protocol.md | 3 +-- bt-daemon/src/translate/pi.rs | 12 ++++------ bt-daemon/tests/pi_translator.rs | 4 ++-- src/plugins/pi/content/README.md | 7 +++--- .../pi/content/src/daemon-adapter.test.ts | 2 +- src/plugins/pi/content/src/pi-package.test.ts | 23 ++----------------- src/plugins/pi/content/src/pi-package.ts | 19 +++++++-------- 7 files changed, 21 insertions(+), 49 deletions(-) diff --git a/bt-daemon/docs/protocol.md b/bt-daemon/docs/protocol.md index eb48e630..41583bb0 100644 --- a/bt-daemon/docs/protocol.md +++ b/bt-daemon/docs/protocol.md @@ -274,8 +274,7 @@ versions available at materialization, not unrecorded historical versions. See t Pi-compatible hosts retain the canonical transport source `pi` so they share its translator. The Pi extension sends the running host package's version as `source_version` and its package name as `payload.host_name`. The translator records -`metadata.host_name` and uses a fork's package name as the session-root label; -upstream Pi and older extension payloads retain the `Pi` label. +`metadata.host_name`; the session-root label remains `Pi` for every Pi-compatible host. For acknowledged spans from legacy ledgers without saved snapshots, stateless updates omit `context.span_origin` entirely, including SDK defaults and inherited diff --git a/bt-daemon/src/translate/pi.rs b/bt-daemon/src/translate/pi.rs index c0e03e8a..584b4bd2 100644 --- a/bt-daemon/src/translate/pi.rs +++ b/bt-daemon/src/translate/pi.rs @@ -573,12 +573,12 @@ impl PiTranslator { metadata.insert("model".into(), json!(model)); } metadata.insert("pi_version".into(), json!(envelope.source_version)); - let host_name = envelope + if let Some(host_name) = envelope .payload .get("host_name") .and_then(Value::as_str) - .filter(|name| !name.is_empty()); - if let Some(host_name) = host_name { + .filter(|name| !name.is_empty()) + { metadata.insert("host_name".into(), json!(host_name)); } metadata.insert( @@ -609,11 +609,7 @@ impl PiTranslator { span_id: self.root_span_id.clone(), root_span_id: self.effective_root_span_id.clone(), parent_span_ids: self.external_parent.clone().into_iter().collect(), - name: match host_name { - Some("@earendil-works/pi-coding-agent" | "@mariozechner/pi-coding-agent") - | None => "Pi".into(), - Some(name) => name.into(), - }, + name: "Pi".into(), span_type: SpanType::Task, start_ms: Some(envelope.ts_ms), metadata: Some(Value::Object(metadata)), diff --git a/bt-daemon/tests/pi_translator.rs b/bt-daemon/tests/pi_translator.rs index 0e3484fa..953b1a5d 100644 --- a/bt-daemon/tests/pi_translator.rs +++ b/bt-daemon/tests/pi_translator.rs @@ -73,7 +73,7 @@ fn reduce(ops: Vec) -> HashMap { } #[test] -fn pi_records_the_running_fork_identity() { +fn pi_records_the_running_host_identity() { let registry = Registry::default_agents(); let mut translator = registry.create("pi", "pi-session"); let ctx = SessionCtx { @@ -91,7 +91,7 @@ fn pi_records_the_running_fork_identity() { _ => None, }) .unwrap(); - assert_eq!(root.name, "@oh-my-pi/pi-coding-agent"); + assert_eq!(root.name, "Pi"); assert_eq!( root.metadata.as_ref().unwrap()["host_name"], "@oh-my-pi/pi-coding-agent" diff --git a/src/plugins/pi/content/README.md b/src/plugins/pi/content/README.md index f188a3e1..ea2b1544 100644 --- a/src/plugins/pi/content/README.md +++ b/src/plugins/pi/content/README.md @@ -70,10 +70,9 @@ resolves these versions on each run, including releases from Pi's former npm package name when needed. Package detection prefers the running CLI to an extension-local peer dependency. -Forks are recognized by a `piConfig` field, a known package name, or `PI_PACKAGE_DIR`. The trace records -the host package name in `metadata.host_name` and its version in `metadata.pi_version`; -fork sessions use their package name instead of the `Pi` root label. -Oh My Pi (`@oh-my-pi/pi-coding-agent`) uses `.omp` as its default config directory. +Pi-compatible hosts are recognized by a `piConfig` field or a known package name, +including Oh My Pi (`@oh-my-pi/pi-coding-agent`). The trace records the host +package name in `metadata.host_name` and its version in `metadata.pi_version`. ## Configuration diff --git a/src/plugins/pi/content/src/daemon-adapter.test.ts b/src/plugins/pi/content/src/daemon-adapter.test.ts index 059315a4..5bad8f93 100644 --- a/src/plugins/pi/content/src/daemon-adapter.test.ts +++ b/src/plugins/pi/content/src/daemon-adapter.test.ts @@ -74,7 +74,7 @@ vi.mock("./pi-package.ts", () => ({ runningPiPackage: () => ({ name: "@oh-my-pi/pi-coding-agent", version: "18.12.6", - configDir: ".omp", + configDir: ".pi", }), })); diff --git a/src/plugins/pi/content/src/pi-package.test.ts b/src/plugins/pi/content/src/pi-package.test.ts index 4a7726fa..dae29385 100644 --- a/src/plugins/pi/content/src/pi-package.test.ts +++ b/src/plugins/pi/content/src/pi-package.test.ts @@ -96,10 +96,11 @@ describe("loadPiPackageMetadata", () => { bin: { omp: "dist/cli.js" }, }); const peer = installPi(`plugins/node_modules/${PI}`, { name: PI, version: "1.4.0" }); + // Recognizing OMP must not move the extension's existing `.pi` settings. expect(loadPiPackageMetadata([host, peer])).toEqual({ name: "@oh-my-pi/pi-coding-agent", version: "18.12.6", - configDir: ".omp", + configDir: ".pi", }); }); @@ -118,26 +119,6 @@ describe("loadPiPackageMetadata", () => { }); }); - it("finds a compiled OMP host beside its executable before the upstream peer", () => { - writeJson(join(root, "omp-release", "package.json"), { - name: "@oh-my-pi/pi-coding-agent", - version: "18.12.6", - }); - const peer = installPi(`plugins/node_modules/${PI}`, { name: PI, version: "1.4.0" }); - expect( - loadPiPackageMetadata(["/$bunfs/root/cli.ts", join(root, "omp-release", "omp"), peer]), - ).toMatchObject({ name: "@oh-my-pi/pi-coding-agent", version: "18.12.6", configDir: ".omp" }); - }); - - it("trusts the explicit PI_PACKAGE_DIR manifest for a custom host", () => { - writeJson(join(root, "custom", "package.json"), { name: "@acme/host", version: "3.2.1" }); - const peer = installPi(`plugins/node_modules/${PI}`, { name: PI, version: "1.4.0" }); - expect(loadPiPackageMetadata([join(root, "custom", "package.json"), peer])).toMatchObject({ - name: "@acme/host", - version: "3.2.1", - }); - }); - it("resolves the installed Pi development dependency through ESM conditions", () => { const metadata = loadPiPackageMetadata([resolvePiPackage()]); expect(metadata.version).toMatch(/^\d+\.\d+\.\d+/); diff --git a/src/plugins/pi/content/src/pi-package.ts b/src/plugins/pi/content/src/pi-package.ts index c3dc0341..b1d1aa9b 100644 --- a/src/plugins/pi/content/src/pi-package.ts +++ b/src/plugins/pi/content/src/pi-package.ts @@ -1,13 +1,13 @@ import { readFileSync, realpathSync } from "node:fs"; import { createRequire } from "node:module"; -import { basename, dirname, join } from "node:path"; +import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const PI_PACKAGE = "@earendil-works/pi-coding-agent"; -const PI_PACKAGE_CONFIG_DIRS = new Map([ - [PI_PACKAGE, ".pi"], - ["@mariozechner/pi-coding-agent", ".pi"], - ["@oh-my-pi/pi-coding-agent", ".omp"], +const PI_PACKAGE_NAMES = new Set([ + PI_PACKAGE, + "@mariozechner/pi-coding-agent", + "@oh-my-pi/pi-coding-agent", ]); interface PiPackageManifest { @@ -45,9 +45,7 @@ export function loadPiPackageMetadata(candidates: (string | undefined)[]): PiPac name: typeof manifest?.name === "string" ? manifest.name : undefined, version: typeof manifest?.version === "string" ? manifest.version : undefined, configDir: - typeof manifest?.piConfig?.configDir === "string" - ? manifest.piConfig.configDir - : (PI_PACKAGE_CONFIG_DIRS.get(String(manifest?.name)) ?? ".pi"), + typeof manifest?.piConfig?.configDir === "string" ? manifest.piConfig.configDir : ".pi", }; } @@ -58,9 +56,8 @@ function findPiManifest(entry: string | undefined): PiPackageManifest | undefine const manifest = readManifest(join(directory, "package.json")); if ( manifest && - ((typeof manifest.name === "string" && PI_PACKAGE_CONFIG_DIRS.has(manifest.name)) || - (manifest.piConfig !== null && typeof manifest.piConfig === "object") || - (basename(entry) === "package.json" && typeof manifest.name === "string")) + ((typeof manifest.name === "string" && PI_PACKAGE_NAMES.has(manifest.name)) || + typeof manifest.piConfig === "object") ) { return manifest; } From e70e9902994948e0cd89dfafce937489db642aa2 Mon Sep 17 00:00:00 2001 From: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com> Date: Tue, 27 Oct 2026 02:52:53 -0700 Subject: [PATCH 3/3] chore(pi): keep the existing PI_VERSION constant --- src/plugins/pi/content/src/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/pi/content/src/index.ts b/src/plugins/pi/content/src/index.ts index 70a8b7eb..479349fb 100644 --- a/src/plugins/pi/content/src/index.ts +++ b/src/plugins/pi/content/src/index.ts @@ -10,7 +10,8 @@ import { EXTENSION_VERSION } from "./version.ts"; const STATUS_KEY = "braintrust-tracing"; const WIDGET_KEY = "braintrust-trace-link"; const UI_STATUS_TIMEOUT_MS = 250; -const PI_HOST = runningPiPackage(); +const PI_VERSION = runningPiPackage().version; +const PI_HOST_NAME = runningPiPackage().name; function sessionKeyFor( sessionFile: string | undefined, @@ -138,14 +139,14 @@ export default function braintrustPiExtension(pi: ExtensionAPI): void { if (!sessionId) return; await client.log({ source: "pi", - ...(PI_HOST.version ? { source_version: PI_HOST.version } : {}), + ...(PI_VERSION ? { source_version: PI_VERSION } : {}), session_id: sessionId, event: name, ts_ms: Date.now(), payload: { event: nativePayload(event), extension_version: EXTENSION_VERSION, - host_name: PI_HOST.name, + host_name: PI_HOST_NAME, session_file: descriptor?.sessionFile, native_session_id: descriptor?.nativeSessionId, legacy_resume: legacyContinuation,