feat(beehive): execute Pi with saved providers and owned refresh

Signed-off-by: Logan Johnson <loganj@squareup.com>
This commit is contained in:
Logan Johnson
2026-09-12 12:09:50 -04:00
parent 344585fb77
commit 8f02dd3c2b
21 changed files with 481 additions and 28 deletions

View File

@@ -162,3 +162,30 @@ merely sending BUZZ_AGENT_PROVIDER to an unrelated process.
Evidence for this continuation, final suite and remaining work:
`/Users/loganj/.buzz/artifacts/beehive-adapter-integration-9b0f8958/INTEGRATION_RESULT.md`.
## Pi runtime continuation (supersedes Pi-disabled statements above)
Pi now uses the Desktop `buzz-pi-acp` fork plus a separately resolved `pi` CLI
with saved OpenAI and Databricks providers. Desktop imposes no Pi semver floor:
execution requires fork identity, ACP protocol 1, and positive exact model and
thinking config-option evidence before prompts. Both probe and conversation broker
use `_meta.systemPrompt` and immutable spawn-time provider/model/effort settings.
A run-owned wrapper creates an isolated Pi directory. Its public `models.json`
contains `$BEEHIVE_PI_KEY`, never a credential. OpenAI uses native Pi Responses;
Databricks uses shared-manifest Responses/Anthropic/MLflow routing, preserving raw
model/FQN and workspace. Its adapter receives only a private loopback capability;
every provider request enters the existing native workspace-bound token source.
No Desktop credentials, persistent Pi auth files, native OAuth copy or plugin
framework. Unknown/custom models remain exact and do not gain guessed effort.
Supported Pi thinking levels are the intersection of model capabilities and Pi's
native vocabulary. No selection is explicitly `off`, not retained user settings.
Save/registration remain inert; existing runs keep their snapshots. Explicit
Start/Stop own wrapper, adapter, Pi descendants, proxy and temporary directory.
No native binary change; exports still require `src/*.ts`, `src/*.json` and the
existing `bin/beehive-databricks`. Tests use explicit synthetic module/process
seams (`pi-isolation-loader.mjs`), fake external ACP executables and loopback
provider servers, not installed harnesses or real OS stores. Targeted cases live
in `pi-host.test.ts`, `pi-conversation.test.ts`, and `pi.test.ts`. This slice does
not claim completion of Codex/Claude or erase prior combined-suite failures.

View File

@@ -686,3 +686,15 @@ The fixture reuses the private NIP42/59 relay via an optional HTTP handler; it d
not change that relay's existing default admission behavior. No production relay,
OS credential or installed-native public metadata evidence is claimed.
See PUBLIC_METADATA.md for the editing and explicit publication path and limits.
### Pi saved-provider runtime slice
From package CWD, direct pinned Node: `node --test test/pi.test.ts
test/pi-host.test.ts test/pi-conversation.test.ts test/runtime-integration.test.ts`.
Pi host/conversation tests import `pi-isolation-loader.mjs` before dynamically
loading product modules. The hook explicitly injects itself into the owned Pi
wrapper and replaces only the native Databricks invocation with workspace/reference
checked synthetic refresh. HTTPS destinations are denied except the exact fixture
route, rewritten to a test-created loopback provider. Do not replace this with
HOME isolation or run these fixtures against an installed harness. The negative
Pi boundary tests use only saved synthetic OpenAI keys and make no provider call.

View File

@@ -1,3 +1,4 @@
import { PI_ADAPTER, piEnvironment, piConfigEvidence } from './pi.ts';
import { fileURLToPath } from 'node:url';
import { buzzProviderEnvironment, type BuzzProvider } from './buzz-provider.ts';
import { validateCustom, type CustomAcp } from './custom-acp.ts';
@@ -10,7 +11,7 @@ import { createHash } from 'node:crypto';
import { spawnOwned, type OwnedProcess } from './owned.ts';
/** Host-prepared launch: executable/provider binding stays local; instructions are a validated public snapshot. Never accept remote env/argv. */
export type AgentLaunch = Readonly<{ resolvedProviderKey?: string; buzzProvider?: BuzzProvider; custom?: CustomAcp; executable: string; args: readonly string[]; workspace: string; home: string; configDirectory: string; databricksHost: string; harness?: 'goose' | 'claude' | 'codex'; codex?: CodexSetup; claude?: ClaudeSetup; provider?: string; model: string; instructions?: string }>;
export type AgentLaunch = Readonly<{ resolvedProviderKey?: string; buzzProvider?: BuzzProvider; custom?: CustomAcp; executable: string; args: readonly string[]; workspace: string; home: string; configDirectory: string; databricksHost: string; harness?: 'goose' | 'claude' | 'codex' | 'pi'; piCli?: string; codex?: CodexSetup; claude?: ClaudeSetup; provider?: string; model: string; instructions?: string }>;
/** ACP catalogs can be fallback data; even a nonempty result is NOT auth evidence. */
export type Catalog = { state: 'reported' | 'empty' | 'filtered'; models: string[]; authentication: 'unverified' };
/** Same child/session acknowledgement plus completed text response, not provider attestation. */
@@ -36,6 +37,10 @@ export function prepareAgent(input: AgentLaunch) {
const d = validateCustom(plan.custom);
if (plan.harness !== 'goose' || d.contract !== 'goose-native' || d.executable !== plan.executable || JSON.stringify(d.args) !== JSON.stringify(plan.args)) throw Error('Unsupported custom launch contract');
}
if (plan.harness === 'pi') {
if (plan.args.length || !plan.piCli || !plan.buzzProvider) throw Error('Invalid Pi launch');
return Object.freeze({ plan, executableHash: hash(readFileSync(plan.executable)), env: Object.freeze(piEnvironment(plan.piCli, plan.buzzProvider, plan.home, plan.model, plan.resolvedProviderKey)), cliExecutableHash: hash(readFileSync(plan.piCli)) });
}
if (plan.harness === 'codex') {
identifier(plan.model);
if (plan.args.length || !plan.codex || !plan.codex.models.includes(plan.model) || plan.home === plan.configDirectory) throw Error('Codex requires zero-argument adapter, approved model and distinct HOME/CODEX_HOME');
@@ -89,6 +94,7 @@ export class AgentSession {
private stoppingProbe = false;
private session = '';
private codexModelVerified = false;
private piModelVerified = false;
private response = '';
private prompting = false;
private timeoutMs: number;
@@ -162,10 +168,12 @@ export class AgentSession {
// Only observations belonging to this owned session can invalidate its proof.
// Model acknowledgement is not permanent: keep this guard active after completion.
if (p.sessionId !== this.session) return;
if (update.sessionUpdate === 'current_model_update' && update.currentModelId !== this.prepared.plan.model) throw Error('ACP model changed');
if (this.prepared.plan.harness === 'pi' && update.sessionUpdate === 'current_mode_update' && update.currentModeId !== (this.prepared.plan.buzzProvider?.effort ?? 'off')) throw Error('Pi effort changed');
if (update.sessionUpdate === 'current_model_update' && update.currentModelId !== (this.prepared.plan.harness === 'pi' ? `beehive/${this.prepared.plan.model}` : this.prepared.plan.model)) throw Error('ACP model changed');
if (update.sessionUpdate === 'config_option_update') {
if (this.prepared.plan.harness === 'pi') piConfigEvidence(update, this.prepared.plan.model, this.prepared.plan.buzzProvider?.effort);
if (!Array.isArray(update.configOptions)) throw Error('Invalid ACP model update');
for (const option of update.configOptions) if (record(option).category === 'model' && record(option).currentValue !== this.prepared.plan.model) throw Error('ACP model changed');
for (const option of update.configOptions) if (record(option).category === 'model' && record(option).currentValue !== (this.prepared.plan.harness === 'pi' ? `beehive/${this.prepared.plan.model}` : this.prepared.plan.model)) throw Error('ACP model changed');
}
if (p.sessionId !== this.session || !this.prompting) return;
if (update.sessionUpdate === 'agent_message_chunk') {
@@ -202,9 +210,13 @@ export class AgentSession {
const nativeBuzz = !this.prepared.plan.harness;
const protocol = this.prepared.plan.harness === 'codex' || nativeBuzz ? 2 : 1;
const init = record(await this.request('initialize', { protocolVersion: protocol, clientCapabilities: {}, clientInfo: { name: 'beehive', version: '0.0.1' } }));
if (init.protocolVersion !== protocol || record(init.agentInfo).name !== (this.prepared.plan.harness === 'codex' ? CODEX_ADAPTER : this.prepared.plan.harness === 'claude' ? CLAUDE_ADAPTER : this.prepared.plan.harness === 'goose' ? 'goose' : 'buzz-agent')) throw Error('Unsupported harness capabilities');
const created = record(await this.request('session/new', { cwd: this.prepared.plan.workspace, mcpServers: [], ...((this.prepared.plan.harness === 'codex' || nativeBuzz) && this.prepared.plan.instructions !== undefined ? { systemPrompt: this.prepared.plan.instructions } : {}), ...(this.prepared.plan.harness === 'claude' && this.prepared.plan.instructions !== undefined ? { _meta: { systemPrompt: { append: this.prepared.plan.instructions } } } : {}) }));
if (init.protocolVersion !== protocol || record(init.agentInfo).name !== (this.prepared.plan.harness === 'pi' ? PI_ADAPTER : this.prepared.plan.harness === 'codex' ? CODEX_ADAPTER : this.prepared.plan.harness === 'claude' ? CLAUDE_ADAPTER : this.prepared.plan.harness === 'goose' ? 'goose' : 'buzz-agent')) throw Error('Unsupported harness capabilities');
const created = record(await this.request('session/new', { cwd: this.prepared.plan.workspace, mcpServers: [], ...(this.prepared.plan.harness === 'pi' && this.prepared.plan.instructions !== undefined ? { _meta: { systemPrompt: this.prepared.plan.instructions } } : {}), ...((this.prepared.plan.harness === 'codex' || nativeBuzz) && this.prepared.plan.instructions !== undefined ? { systemPrompt: this.prepared.plan.instructions } : {}), ...(this.prepared.plan.harness === 'claude' && this.prepared.plan.instructions !== undefined ? { _meta: { systemPrompt: { append: this.prepared.plan.instructions } } } : {}) }));
this.session = identifier(created.sessionId);
if (this.prepared.plan.harness === 'pi') {
const models = piConfigEvidence(created, this.prepared.plan.model, this.prepared.plan.buzzProvider?.effort);
this.piModelVerified = true; return { state: 'reported', models, authentication: 'unverified' };
}
if (this.prepared.plan.harness === 'codex') {
const models = codexModels(created, this.prepared.plan.model);
this.codexModelVerified = true;
@@ -221,6 +233,7 @@ export class AgentSession {
/** Require explicit exact-model acknowledgement, then a response in the SAME session/process. */
async verify(): Promise<Evidence> {
if (!this.session) throw Error('Create ACP session first');
if (this.prepared.plan.harness === 'pi' && !this.piModelVerified) throw Error('Pi exact model/effort evidence required');
if (this.prepared.plan.harness === 'codex' && !this.codexModelVerified) throw Error('Codex exact fresh model evidence required before prompts');
const model = this.prepared.plan.model;
if (!this.prepared.plan.harness) {
@@ -245,6 +258,7 @@ export class AgentSession {
export function spawnAgent(prepared: ReturnType<typeof prepareAgent>, extra: Record<string, string> = {}) {
const { plan } = prepared;
const env = { ...prepared.env, ...extra };
if (plan.harness === 'pi') return spawnOwned(process.execPath, [fileURLToPath(new URL('./pi-runtime-child.ts', import.meta.url)), plan.executable], plan.workspace, env);
if (plan.buzzProvider?.provider === 'databricks_v2' && plan.buzzProvider.credential) {
delete env.DATABRICKS_TOKEN;
env.BEEHIVE_DATABRICKS_RUNTIME = JSON.stringify({ host: plan.buzzProvider.baseUrl, key: plan.buzzProvider.credential, model: plan.model });

View File

@@ -1,3 +1,4 @@
import { PI_ADAPTER, piConfigEvidence } from './pi.ts';
import { CODEX_ADAPTER, codexModels } from './codex.ts';
import { CLAUDE_ADAPTER, claudeModels } from './claude.ts';
import { gooseModels } from './acp.ts';
@@ -158,6 +159,7 @@ export class ConversationSession {
const pending = new Map<string | number, Pending>();
const sessions = new Set<string>();
const customProfiles = new Set<string>();
let piNative = false;
const modelConfigs = new Set<string>(['model']);
const prompts = new Map<string, { id: string | number; hash: ReturnType<typeof createHash>; text: boolean; cancelled: boolean }>();
this.cancellations.push(() => {
@@ -194,6 +196,15 @@ export class ConversationSession {
if (p?.mode !== 'set' || p.key !== 'buzz' || typeof p.text !== 'string' || !p.text.includes(this.prepared.plan.instructions)) throw Error('Custom exact native profile missing');
}
}
if (this.prepared.plan.harness === 'pi') {
if (msg.method === 'initialize') p.protocolVersion = 1;
if (['session/load','session/resume','session/set_model','session/set_mode','session/set_config_option'].includes(msg.method)) throw Error('Pi requires fresh session/Restart');
if (msg.method === 'session/new') {
if (!piNative) throw Error('Pi initialize required');
delete p.systemPrompt;
if (this.prepared.plan.instructions !== undefined) p._meta = { ...p._meta, systemPrompt: this.prepared.plan.instructions };
}
}
if (this.prepared.plan.harness === 'codex') {
if (msg.method === 'initialize') p.protocolVersion = 2;
if (['session/load', 'session/resume', 'session/set_model', 'session/set_config_option'].includes(msg.method)) throw Error('Codex requires fresh session/Restart');
@@ -260,6 +271,10 @@ export class ConversationSession {
if (request.method === 'initialize' && this.prepared.plan.custom) {
if (msg.result?.protocolVersion !== 1 || msg.result?.agentInfo?.name !== 'goose') throw Error('Custom Goose-native contract unavailable');
}
if (request.method === 'initialize' && this.prepared.plan.harness === 'pi') {
if (msg.result?.protocolVersion !== 1 || msg.result?.agentInfo?.name !== PI_ADAPTER) throw Error('Pi fork capability missing');
piNative = true;
}
if (request.method === 'initialize' && this.prepared.plan.harness === 'codex') {
if (msg.result?.protocolVersion !== 2 || msg.result?.agentInfo?.name !== CODEX_ADAPTER) throw Error('Codex native profile capability unavailable');
codexNative = true;
@@ -282,6 +297,10 @@ export class ConversationSession {
}
if (modelConfigs.size > 128) throw Error();
sessions.delete(session);
if (this.prepared.plan.harness === 'pi') {
piConfigEvidence(msg.result, this.prepared.plan.model, this.prepared.plan.buzzProvider?.effort);
sessions.add(session); send(socket, msg); return;
}
if (this.prepared.plan.harness === 'codex') {
codexModels(msg.result, this.prepared.plan.model);
sessions.add(session); send(socket, msg); return;
@@ -313,9 +332,13 @@ export class ConversationSession {
}
if (fromHarness && msg.method === 'session/update') {
const p = msg.params; const u = p?.update;
if (u?.sessionUpdate === 'current_model_update' && u.currentModelId !== this.prepared.plan.model) throw Error();
if (this.prepared.plan.harness === 'pi') {
if (u?.sessionUpdate === 'config_option_update') piConfigEvidence(u, this.prepared.plan.model, this.prepared.plan.buzzProvider?.effort);
if (u?.sessionUpdate === 'current_mode_update' && u.currentModeId !== (this.prepared.plan.buzzProvider?.effort ?? 'off')) throw Error('Pi effort changed');
}
if (u?.sessionUpdate === 'current_model_update' && u.currentModelId !== (this.prepared.plan.harness === 'pi' ? `beehive/${this.prepared.plan.model}` : this.prepared.plan.model)) throw Error();
if (u?.sessionUpdate === 'config_option_update' && Array.isArray(u.configOptions)) {
for (const option of u.configOptions) if (option.category === 'model' && option.currentValue !== this.prepared.plan.model) throw Error();
for (const option of u.configOptions) if (option.category === 'model' && option.currentValue !== (this.prepared.plan.harness === 'pi' ? `beehive/${this.prepared.plan.model}` : this.prepared.plan.model)) throw Error();
}
const prompt = prompts.get(p?.sessionId);
if (prompt && u?.sessionUpdate === 'agent_message_chunk' && u.content?.type === 'text' && typeof u.content.text === 'string') {

View File

@@ -36,6 +36,7 @@ export function prepareConversationBinding(input: ConversationSetup, agent: Agen
if (prepared.plan.args.some(a => a.includes('\0'))) throw Error('ACP arguments cannot contain NUL');
if (input.authTag !== undefined && (typeof input.authTag !== 'string' || input.authTag.length > 16384 || !input.authTag.length)) throw Error('Invalid local owner attestation');
const providerEnv = { ...prepared.env };
if (agent.harness === 'pi') { delete providerEnv.BEEHIVE_PI_KEY; delete providerEnv.BEEHIVE_PI_RUNTIME; }
// The relay transport/shim never needs the OS-backed Databricks bearer.
// The actual model consumer obtains a run-local capability in spawnAgent.
if (agent.buzzProvider?.provider === 'databricks_v2' && agent.buzzProvider.credential) delete providerEnv.DATABRICKS_TOKEN;
@@ -53,7 +54,7 @@ export function prepareConversationBinding(input: ConversationSetup, agent: Agen
BUZZ_RELAY_URL: url.href,
BUZZ_ACP_AGENT_OWNER: owner,
...(input.authTag === undefined ? {} : { BUZZ_AUTH_TAG: input.authTag }),
...((prepared.plan.harness === 'claude' || prepared.plan.harness === 'codex') ? {} : { BUZZ_ACP_MODEL: prepared.plan.model }),
...((prepared.plan.harness === 'claude' || prepared.plan.harness === 'codex' || prepared.plan.harness === 'pi') ? {} : { BUZZ_ACP_MODEL: prepared.plan.model }),
...(prepared.plan.instructions === undefined ? {} : { BUZZ_ACP_SYSTEM_PROMPT: prepared.plan.instructions }),
BUZZ_ACP_MCP_COMMAND: '',
BUZZ_ACP_AGENTS: '1',

View File

@@ -45,6 +45,7 @@ export async function databricksRuntime(input: { host: string; key: ProviderRefe
controller.signal.throwIfAborted();
if (!token.secret || /\s/.test(token.secret)) throw Error();
const headers = new Headers({ authorization: `Bearer ${token.secret}`, 'content-type': 'application/json' });
if (url.pathname === '/ai-gateway/anthropic/v1/messages') headers.set('anthropic-version', '2023-06-01');
token.secret = undefined;
const upstream = await fetcher(host + url.pathname + url.search, { method: req.method, headers, ...(body ? { body: body.toString() } : {}), signal: controller.signal, redirect: 'error' });
if (upstream.status === 401 || upstream.status === 403) { denied = true; await upstream.body?.cancel(); error(res, 401); return; }
@@ -56,7 +57,7 @@ export async function databricksRuntime(input: { host: string; key: ProviderRefe
finally { await reader.cancel(); reader.releaseLock(); }
controller.signal.throwIfAborted();
if (stopped || res.destroyed) return;
res.writeHead(200, { 'content-type': 'application/json' }); res.end(Buffer.concat(chunks));
res.writeHead(200, { 'content-type': upstream.headers.get('content-type')?.startsWith('text/event-stream') ? 'text/event-stream' : 'application/json' }); res.end(Buffer.concat(chunks));
} catch { error(res, 503); }
finally { clearTimeout(timer); req.removeListener('aborted', abort); res.removeListener('close', abort); active.delete(controller); }
}

View File

@@ -78,7 +78,7 @@ export async function discoverHarnesses(signal: AbortSignal, options: { home?: s
let state: DetectedHarness['state'] = !binary ? 'not-installed' : row.underlyingCli && !cli ? 'cli-missing' : 'available';
if (state === 'available' && row.id === 'codex' && !compatibleCodex(await probe(binary!, ['--version'], [...managed, path].join(delimiter), home, signal) ?? '')) state = 'incompatible';
signal.throwIfAborted();
const providers = state === 'available' && row.id === 'buzz-agent' ? ['openai', 'databricks_v2'] : state === 'available' && row.id === 'codex' ? ['openai'] : [];
const providers = state === 'available' && ['buzz-agent','pi'].includes(row.id) ? ['openai', 'databricks_v2'] : state === 'available' && row.id === 'codex' ? ['openai'] : [];
const reason = state === 'not-installed' ? 'Not installed' : state === 'cli-missing' ? `Underlying ${row.underlyingCli} CLI not installed` : state === 'incompatible' ? 'Codex ACP 1.10.0 or newer required; version unknown/outdated' : providers.length ? 'Detected; provider/model access unverified' : 'Detected; this catalog’s OS-backed providers are not supported by this adapter. Use existing local setup for native authentication.';
result.push({ id: row.id, label: row.label!, executable: binary, cli, state, providers, reason });
}

View File

@@ -1,3 +1,4 @@
import { validatePi } from './pi.ts';
import { readSettings } from './settings.ts';
import { runtimeBindings } from './settings-runtime.ts';
import { managerCredential } from './manager-credential.ts';
@@ -25,7 +26,7 @@ import { spawnOwned, type OwnedProcess } from './owned.ts';
import { AgentSession, prepareAgent, type AgentLaunch, type Catalog, type Evidence } from './acp.ts';
import { prepareConversation, conversationSummary, type ConversationSetup } from './conversation.ts';
export type Setup = { host: string; ownerSecret?: string; ownerPublic?: string; agentSecret?: string; runner: string; args: string[]; workspace: string; allowedWorkspaces?: string[]; custom?: CustomAcp; buzzProvider?: BuzzProvider; mode: 'buzz-agent-api-key' | 'diagnostic-acp' | 'fixture' | 'buzz-agent-databricks-v2' | 'goose' | 'claude' | 'codex'; codex?: CodexSetup; claude?: ClaudeSetup; gooseProvider?: string; gooseModels?: string[]; databricksHost?: string; serviceHome?: string; configDirectory?: string; conversation?: ConversationSetup };
export type Setup = { host: string; ownerSecret?: string; ownerPublic?: string; agentSecret?: string; runner: string; args: string[]; workspace: string; allowedWorkspaces?: string[]; custom?: CustomAcp; buzzProvider?: BuzzProvider; mode: 'buzz-agent-api-key' | 'diagnostic-acp' | 'fixture' | 'buzz-agent-databricks-v2' | 'goose' | 'claude' | 'codex' | 'pi'; piCli?: string; codex?: CodexSetup; claude?: ClaudeSetup; gooseProvider?: string; gooseModels?: string[]; databricksHost?: string; serviceHome?: string; configDirectory?: string; conversation?: ConversationSetup };
/** Definition-only fingerprint: common host authority and agent key are excluded. */
export function bindingFingerprint(setup: Setup): string {
const { host: _host, ownerSecret: _owner, ownerPublic: _ownerPublic, agentSecret: _agent, ...harness } = setup;
@@ -45,16 +46,18 @@ export function setupOwner(setup: Pick<Setup, 'ownerPublic' | 'ownerSecret'>): s
return owner;
}
/** Operator-approved compatible models, not an authenticated provider catalog. */
export function setupModels(s: Setup): string[] { return s.mode === 'buzz-agent-api-key' ? [...s.buzzProvider!.models] : s.mode === 'diagnostic-acp' ? [] : s.mode === 'codex' ? [...s.codex!.models] : s.mode === 'claude' ? [...s.claude!.models] : s.mode === 'goose' ? [...s.gooseModels!] : [s.mode === 'fixture' ? 'fixture-model' : 'databricks-claude-haiku-4-5']; }
export function setupModels(s: Setup): string[] { return (s.mode === 'buzz-agent-api-key' || s.mode === 'pi') ? [...s.buzzProvider!.models] : s.mode === 'diagnostic-acp' ? [] : s.mode === 'codex' ? [...s.codex!.models] : s.mode === 'claude' ? [...s.claude!.models] : s.mode === 'goose' ? [...s.gooseModels!] : [s.mode === 'fixture' ? 'fixture-model' : 'databricks-claude-haiku-4-5']; }
export function validateSetup(value: unknown): Setup {
const s = object(value);
for (const key of ['host','runner','workspace']) text(s[key]);
if (s.allowedWorkspaces !== undefined && (!Array.isArray(s.allowedWorkspaces) || s.allowedWorkspaces.length > 32 || s.allowedWorkspaces.some(w => typeof w !== 'string' || !isAbsolute(w)))) throw Error('Invalid allowed workspaces');
setupOwner(s as Setup);
if (s.agentSecret !== undefined) publicKey(String(s.agentSecret));
if (!isAbsolute(String(s.runner)) || !isAbsolute(String(s.workspace)) || !Array.isArray(s.args) || s.args.some(a => typeof a !== 'string') || !['buzz-agent-api-key','diagnostic-acp','fixture','buzz-agent-databricks-v2','goose','claude','codex'].includes(String(s.mode))) throw Error('Invalid local setup');
if (s.buzzProvider !== undefined && s.mode !== 'buzz-agent-api-key') throw Error('Buzz provider belongs only to Buzz Agent API-key bindings');
if (s.mode === 'buzz-agent-api-key') {
if (!isAbsolute(String(s.runner)) || !isAbsolute(String(s.workspace)) || !Array.isArray(s.args) || s.args.some(a => typeof a !== 'string') || !['buzz-agent-api-key','diagnostic-acp','fixture','buzz-agent-databricks-v2','goose','claude','codex','pi'].includes(String(s.mode))) throw Error('Invalid local setup');
if (s.piCli !== undefined && s.mode !== 'pi') throw Error('Pi CLI belongs only to Pi bindings');
if (s.buzzProvider !== undefined && s.mode !== 'buzz-agent-api-key' && s.mode !== 'pi') throw Error('Buzz provider belongs only to Buzz Agent API-key bindings');
if (s.mode === 'buzz-agent-api-key' || s.mode === 'pi') {
if (s.mode === 'pi') validatePi(String(s.piCli), s.buzzProvider as BuzzProvider);
validateBuzzProvider(s.buzzProvider as BuzzProvider);
if (s.args.length || typeof s.serviceHome !== 'string' || !isAbsolute(s.serviceHome) || typeof s.configDirectory !== 'string' || !isAbsolute(s.configDirectory) || s.databricksHost !== undefined || s.gooseProvider !== undefined || s.claude !== undefined || s.codex !== undefined || s.custom !== undefined) throw Error('Buzz Agent requires dedicated local context and zero args, not another harness auth contract');
}
@@ -251,7 +254,7 @@ function slot(setup: Setup, path: string, agent: string, currentSetup: (id: stri
resolvedProviderKey = result.secret; result.secret = undefined;
} finally { if (credentialRead === controller) credentialRead = undefined; }
}
const launch: AgentLaunch | undefined = setup.mode === 'fixture' ? undefined : { ...(resolvedProviderKey ? { resolvedProviderKey } : {}), executable: setup.runner, args: setup.args, workspace: selected.workspace, home: text(setup.serviceHome), configDirectory: text(setup.configDirectory), ...(setup.buzzProvider ? { buzzProvider: setup.buzzProvider } : {}), databricksHost: setup.mode === 'buzz-agent-api-key' || setup.mode === 'goose' || setup.mode === 'claude' || setup.mode === 'codex' ? '' : text(setup.databricksHost), ...(setup.mode === 'codex' ? { harness: 'codex' as const, codex: setup.codex } : {}), ...(setup.mode === 'claude' ? { harness: 'claude' as const, claude: setup.claude } : {}), ...(setup.mode === 'goose' ? { harness: 'goose' as const, provider: setup.gooseProvider, ...(setup.custom ? { custom: setup.custom } : {}) } : {}), model: selected.model, ...(selected.behavior ? { instructions: selected.behavior.instructions } : {}) };
const launch: AgentLaunch | undefined = setup.mode === 'fixture' ? undefined : { ...(resolvedProviderKey ? { resolvedProviderKey } : {}), executable: setup.runner, args: setup.args, workspace: selected.workspace, home: text(setup.serviceHome), configDirectory: text(setup.configDirectory), ...(setup.buzzProvider ? { buzzProvider: setup.buzzProvider } : {}), databricksHost: setup.mode === 'buzz-agent-api-key' || setup.mode === 'pi' || setup.mode === 'goose' || setup.mode === 'claude' || setup.mode === 'codex' ? '' : text(setup.databricksHost), ...(setup.mode === 'pi' ? { harness: 'pi' as const, piCli: setup.piCli } : {}), ...(setup.mode === 'codex' ? { harness: 'codex' as const, codex: setup.codex } : {}), ...(setup.mode === 'claude' ? { harness: 'claude' as const, claude: setup.claude } : {}), ...(setup.mode === 'goose' ? { harness: 'goose' as const, provider: setup.gooseProvider, ...(setup.custom ? { custom: setup.custom } : {}) } : {}), model: selected.model, ...(selected.behavior ? { instructions: selected.behavior.instructions } : {}) };
const prepared = launch ? prepareAgent(launch) : undefined;
const conversation = setup.conversation && launch ? prepareConversation(setup.conversation, launch, executionSecret, state.binding.owner) : undefined;
// Local hashes only; no setup or secret values leave the host. Include existing

View File

@@ -276,8 +276,8 @@ A Stop result is not a recent host report that confirms the agent is stopped.` }
const selected = this.harnesses?.find(h => h.id === (v.harness ?? 'buzz-agent'));
const previous = readSettings(this.hostDirectory);
const provider = previous.providers.find(p => p.id === v.provider);
if (!selected?.executable || !provider || !selected.providers.includes(provider.type) || !['buzz-agent','codex'].includes(selected.id)) throw plain('No supported executable/provider combination found.');
saveSettings(this.hostDirectory,{ ...previous, runtimes: [...previous.runtimes,{ id: settingsId(), name: v.name ?? '', harness: selected.id as 'buzz-agent' | 'codex', executable: selected.executable, ...(selected.id === 'codex' ? { cli: selected.cli } : {}), providerId: v.provider ?? '', model: v.model ?? '', ...(v.effort ? { effort: v.effort } : {}) }] },previous.revision);
if (!selected?.executable || !provider || !selected.providers.includes(provider.type) || !['buzz-agent','codex','pi'].includes(selected.id)) throw plain('No supported executable/provider combination found.');
saveSettings(this.hostDirectory,{ ...previous, runtimes: [...previous.runtimes,{ id: settingsId(), name: v.name ?? '', harness: selected.id as 'buzz-agent' | 'codex' | 'pi', executable: selected.executable, ...(['codex','pi'].includes(selected.id) ? { cli: selected.cli } : {}), providerId: v.provider ?? '', model: v.model ?? '', ...(v.effort ? { effort: v.effort } : {}) }] },previous.revision);
this.status = 'Runtime saved for new runs. Running agents did not change. Host loading is reported separately.';
} else if (request.action === 'host-start') {
this.service = await startService(this.hostDirectory); check(); this.status = 'Host running. Registration and settings do not start agents.';

View File

@@ -0,0 +1,30 @@
import { spawn } from 'node:child_process';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { databricksRuntime } from './databricks-runtime.ts';
import { piModelConfig } from './pi.ts';
// Entire wrapper, adapter, Pi children and native helpers are in the existing
// supervisor group. No provider secret is persisted, including on forced exit.
let stopping = false;
let bridge: Awaited<ReturnType<typeof databricksRuntime>> | undefined;
let child: ReturnType<typeof spawn> | undefined;
let directory: string | undefined;
const stop = async () => { stopping = true; child?.kill('SIGTERM'); await bridge?.stop(); };
process.on('SIGTERM', () => { void stop(); });
process.on('SIGINT', () => { void stop(); });
try {
const { provider, model } = JSON.parse(process.env.BEEHIVE_PI_RUNTIME ?? '');
delete process.env.BEEHIVE_PI_RUNTIME;
if (provider.provider === 'databricks_v2') bridge = await databricksRuntime({ host: provider.baseUrl, key: provider.credential, model });
if (!stopping) {
directory = mkdtempSync(join(tmpdir(), 'beehive-pi-'));
const agent = join(directory, 'agent'); mkdirSync(agent, { mode: 0o700 });
writeFileSync(join(agent, 'models.json'), JSON.stringify(piModelConfig(provider, model, bridge?.endpoint)), { mode: 0o600 });
const executable = process.argv[2]; if (!executable) throw Error();
child = spawn(executable, ['--', '--provider', 'beehive', '--model', model, '--thinking', provider.effort ?? 'off', '--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes'], { stdio: ['inherit','inherit','ignore'], env: { ...process.env, HOME: directory, PI_CODING_AGENT_DIR: agent, BEEHIVE_PI_KEY: bridge?.capability ?? process.env.BEEHIVE_PI_KEY! } });
await new Promise<void>((resolve,reject) => { child!.once('error',reject); child!.once('close', code => { process.exitCode = stopping ? 0 : code ?? 1; resolve(); }); });
}
} catch { process.exitCode = 1; }
finally { await stop(); if (directory) rmSync(directory, { recursive: true, force: true }); }

45
beehive/src/pi.ts Normal file
View File

@@ -0,0 +1,45 @@
import { accessSync, constants, realpathSync } from 'node:fs';
import { dirname, isAbsolute } from 'node:path';
import { validateBuzzProvider, type BuzzProvider } from './buzz-provider.ts';
import { runtimeEfforts, runtimeCapabilities } from './runtime-effort.ts';
/** Desktop 78618804: fork identity (not upstream pi-acp) gates native metadata.
* The fork has no Desktop semver floor; positive session config evidence is required. */
export const PI_ADAPTER = 'buzz-pi-acp';
export function piEfforts(provider: string, model: string) {
return runtimeEfforts(provider, model).filter(e => ['minimal','low','medium','high','xhigh'].includes(e));
}
export function validatePi(cli: string, provider: BuzzProvider) {
validateBuzzProvider(provider);
if (!isAbsolute(cli) || !provider.credential || !['openai-compat','databricks_v2'].includes(provider.provider) || provider.apiKeyFile !== undefined) throw Error('Pi requires a saved OS provider and explicit CLI');
if (provider.effort !== undefined && provider.models.some(m => !piEfforts(provider.provider, m).includes(provider.effort!))) throw Error('Unsupported Pi effort');
}
export function piEnvironment(cli: string, provider: BuzzProvider, home: string, model: string, secret?: string): Record<string,string> {
validatePi(cli, provider);
if (realpathSync(cli) !== cli || !provider.models.includes(model)) throw Error('Pi CLI/model changed');
accessSync(cli, constants.X_OK);
if (!secret || secret.length > 16384 || /[\s\0]/.test(secret)) throw Error('Pi OS provider admission required');
return { PATH: `${dirname(cli)}:${dirname(process.execPath)}:/usr/bin:/bin`, HOME: home, PI_ACP_PI_COMMAND: cli,
BEEHIVE_PI_KEY: provider.provider === 'databricks_v2' ? 'native-refresh-owned' : secret,
BEEHIVE_PI_RUNTIME: JSON.stringify({ provider, model }) };
}
/** Public-only models.json: apiKey names an environment variable, never a token.
* Provider name is private to this run; raw model/FQN remains unchanged on wire. */
export function piModelConfig(provider: BuzzProvider, model: string, endpoint = provider.baseUrl) {
const db = provider.provider === 'databricks_v2';
const route = runtimeCapabilities(provider.provider,model)?.databricks_v2_wire_route;
const anthropic = db && route === 'anthropic-messages';
const responses = db && route === 'openai-responses';
const api = anthropic ? 'anthropic-messages' : responses || !db ? 'openai-responses' : 'openai-completions';
const baseUrl = endpoint + (db ? anthropic ? '/ai-gateway/anthropic' : responses ? '/ai-gateway/openai/v1' : '/ai-gateway/mlflow/v1' : '');
return { providers: { beehive: { baseUrl, api, apiKey: '$BEEHIVE_PI_KEY', authHeader: true, models: [{ id: model, name: model, reasoning: piEfforts(provider.provider,model).length > 0, input: ['text'] }] } } };
}
export function piConfigEvidence(value: any, model: string, effort?: string): string[] {
const options = value?.configOptions;
if (!Array.isArray(options) || options.length > 128) throw Error('Pi config capability missing');
for (const [category, expected] of [['model', `beehive/${model}`], ['thought_level', effort ?? 'off']]) {
const matches = options.filter((o: any) => o.category === category);
if (matches.length !== 1 || matches[0].currentValue !== expected || !Array.isArray(matches[0].options) || !matches[0].options.some((o: any) => o.value === expected)) throw Error('Pi exact model/effort evidence mismatch');
}
return [model];
}

View File

@@ -3,12 +3,25 @@ import manifest from './model-capabilities.json' with { type: 'json' };
/** Packaged verbatim from scripts/model-capabilities.json. Interpretation mirrors
* Desktop modelCapabilities.ts exact/family precedence. No fallback effort picker:
* unknown custom names and UC FQNs deliberately omit tuning rather than guessing. */
export function runtimeEfforts(provider: string, model: string): readonly string[] {
export function runtimeCapabilities(provider: string, model: string) {
const canon = provider === 'openai-compat' ? 'openai' : provider;
if (canon === 'databricks_v2' && model.split('.').length === 3 && model.split('.').every(v => v.length > 0 && !/[\s/]/.test(v))) return [];
if (canon === 'databricks_v2' && model.split('.').length === 3 && model.split('.').every(v => v.length > 0 && !/[\s/]/.test(v))) {
const service = model.split('.')[2]!.toLowerCase();
const tokens = manifest.family_tokens.flatMap(token => {
let from = 0;
while (true) {
const i = service.indexOf(token, from); if (i < 0) return [];
if (i === 0 || !/[a-z0-9]/.test(service[i-1]!)) return [i];
from = i + 1;
}
});
const stripped = service.slice(tokens.length ? Math.min(...tokens) : 0);
const version = /^gpt-(\d+)(?=[^a-z0-9]|$)/.exec(stripped);
return { ...manifest.provider_fallbacks.databricks_v2.concrete_unknown, supported_efforts: [] as string[], databricks_v2_wire_route: version && Number(version[1]) >= 5 ? 'openai-responses' : 'mlflow-chat' };
}
const lower = model.toLowerCase();
const exact = manifest.exact_records.find(r => r.provider === canon && r.raw_model_id.toLowerCase() === lower);
if (exact) return exact.thinking_mode === 'omit-fields' ? [] : exact.supported_efforts;
if (exact) return exact;
let start = lower.length;
for (const token of manifest.family_tokens) {
let from = 0;
@@ -26,7 +39,14 @@ export function runtimeEfforts(provider: string, model: string): readonly string
return lengths.length ? [{ rule, length: Math.max(...lengths) }] : [];
}).sort((a, b) => b.length - a.length || (a.rule.id < b.rule.id ? -1 : 1));
const rule = matches[0]?.rule;
return !rule || rule.thinking_mode === 'omit-fields' ? [] : rule.supported_efforts;
if (rule) return rule;
const fallback = manifest.provider_fallbacks[canon as keyof typeof manifest.provider_fallbacks]?.concrete_unknown;
return fallback ? { ...fallback, supported_efforts: [] as string[] } : undefined;
}
export function runtimeEfforts(provider: string, model: string): readonly string[] {
const result = runtimeCapabilities(provider, model);
return !result || result.thinking_mode === 'omit-fields' ? [] : result.supported_efforts;
}
/** Recheck at save AND actual launch; no caller can inject a universal level. */

View File

@@ -1,3 +1,4 @@
import { piEfforts } from './pi.ts';
import { runtimeEfforts } from './runtime-effort.ts';
import type { ManagerSnapshot } from './manager-controller.ts';
@@ -23,7 +24,7 @@ export async function runtimeForm(screen: {
const models = snapshot().models;
const choiceModel = await screen.choose(models ? 'Model' : 'Model listing unavailable · Custom model allowed', [...(models ?? []), 'Custom model']); if (!choiceModel) return;
const model = choiceModel === 'Custom model' ? await screen.input('Exact model ID. Custom does not verify provider access.') : choiceModel; if (!model) return;
const efforts = selected.id === 'buzz-agent' ? runtimeEfforts(provider.type, model) : [];
const efforts = selected.id === 'buzz-agent' ? runtimeEfforts(provider.type, model) : selected.id === 'pi' ? piEfforts(provider.type, model) : [];
const effort = efforts.length ? await screen.choose('Effort · new runs only', ['Inherit', ...efforts]) : 'Inherit'; if (!effort) return;
const name = await screen.input('Runtime name', model); if (!name) return;
if (await screen.confirm(`Save ${name}?\n${selected.label} · ${provider.name} · ${model}\n${efforts.length ? `Effort: ${effort}` : 'No supported effort control for this model.'}\nSaved for new runs. Running agents do not change.`)) await request('add-runtime', { name, model, provider: provider.id, harness: selected.id, ...(effort !== 'Inherit' ? { effort } : {}) });

View File

@@ -14,7 +14,7 @@ export function runtimeBindings(base: Setup, settings: Settings): Record<string,
host: base.host, ownerPublic: base.ownerPublic, ...(base.ownerSecret ? { ownerSecret: base.ownerSecret } : {}), ...(base.agentSecret ? { agentSecret: base.agentSecret } : {}),
runner: runtime.executable, args: [], workspace: base.workspace, allowedWorkspaces: base.allowedWorkspaces,
serviceHome: base.serviceHome, configDirectory: base.configDirectory,
...(base.conversation ? { conversation: base.conversation } : {}), ...(runtime.harness === 'codex' ? { mode: 'codex', codex: { cli: runtime.cli!, credential: provider.key, models: [runtime.model] } } : { mode: 'buzz-agent-api-key',
...(base.conversation ? { conversation: base.conversation } : {}), ...(runtime.harness === 'codex' ? { mode: 'codex', codex: { cli: runtime.cli!, credential: provider.key, models: [runtime.model] } } : { mode: runtime.harness === 'pi' ? 'pi' : 'buzz-agent-api-key', ...(runtime.harness === 'pi' ? { piCli: runtime.cli } : {}),
buzzProvider: provider.type === 'databricks_v2' ? { provider:'databricks_v2',auth:'token',baseUrl:provider.endpoint,credential:provider.key,models:[runtime.model],...(runtime.effort ? { effort:runtime.effort } : {}) } : { provider: 'openai-compat', baseUrl: provider.endpoint, credential: provider.key, wire: 'auto', models: [runtime.model], ...(runtime.effort ? { effort:runtime.effort } : {}) } }),
})];
}));

View File

@@ -1,3 +1,4 @@
import { piEfforts } from './pi.ts';
import { validateRuntimeEffort } from './runtime-effort.ts';
import { databricksHost } from './databricks.ts';
import { existsSync, mkdirSync, rmdirSync, lstatSync } from 'node:fs';
@@ -11,7 +12,7 @@ export type RegisteredAgent = { publicKey: string; key: CredentialReference; pro
/** Each provider version owns an exact immutable OS credential entry. */
export type ProviderReference = { service: 'beehive'; account: string };
export type SavedProvider = { id: string; name: string; type: 'openai' | 'databricks_v2'; endpoint: string; key: ProviderReference };
export type SavedRuntime = { id: string; name: string; harness: 'buzz-agent' | 'codex'; cli?: string; executable: string; providerId: string; model: string; effort?: string };
export type SavedRuntime = { id: string; name: string; harness: 'buzz-agent' | 'codex' | 'pi'; cli?: string; executable: string; providerId: string; model: string; effort?: string };
export type Settings = { version: 1; revision: number; agents: RegisteredAgent[]; providers: SavedProvider[]; runtimes: SavedRuntime[] };
const path = (directory: string) => join(directory, 'settings.json');
const label = (s: unknown) => typeof s === 'string' && s.length > 0 && s.length <= 128 && !/[\x00-\x1f\x7f]/.test(s);
@@ -26,9 +27,9 @@ export function validateSettings(value: Settings): Settings {
if (a.profile && (typeof a.profile.relay !== 'string' || Object.values(a.profile).some(v => typeof v !== 'string' || v.length > 2048 || /[\x00-\x1f\x7f]/.test(v)))) throw Error('Invalid public profile');
}
for (const p of value.providers) if (!label(p.name) || !label(p.id) || !['openai','databricks_v2'].includes(p.type) || (p.type === 'openai' ? p.endpoint !== 'https://api.openai.com/v1' : databricksHost(p.endpoint) !== p.endpoint) || p.key?.service !== 'beehive' || !/^provider:[0-9a-f-]{36}$/.test(p.key.account)) throw Error('Invalid saved provider');
for (const r of value.runtimes) if (!label(r.id) || !label(r.name) || !['buzz-agent','codex'].includes(r.harness) || !isAbsolute(r.executable) || !/^[a-zA-Z0-9_.:/-]{1,200}$/.test(r.model) || !value.providers.some(p => p.id === r.providerId)) throw Error('Invalid saved runtime');
for (const r of value.runtimes) if (!label(r.id) || !label(r.name) || !['buzz-agent','codex','pi'].includes(r.harness) || !isAbsolute(r.executable) || !/^[a-zA-Z0-9_.:/-]{1,200}$/.test(r.model) || !value.providers.some(p => p.id === r.providerId)) throw Error('Invalid saved runtime');
for (const r of value.runtimes) {
if (r.harness === 'codex' ? !r.cli || !isAbsolute(r.cli) || r.effort !== undefined || value.providers.find(p => p.id === r.providerId)?.type !== 'openai' : r.cli !== undefined) throw Error('Unsupported runtime provider/CLI/effort combination');
if (r.harness === 'codex' ? !r.cli || !isAbsolute(r.cli) || r.effort !== undefined || value.providers.find(p => p.id === r.providerId)?.type !== 'openai' : r.harness === 'pi' ? !r.cli || !isAbsolute(r.cli) || r.effort !== undefined && !piEfforts(value.providers.find(p => p.id === r.providerId)!.type, r.model).includes(r.effort) : r.cli !== undefined) throw Error('Unsupported runtime provider/CLI/effort combination');
}
for (const r of value.runtimes) validateRuntimeEffort(value.providers.find(p => p.id === r.providerId)!.type, r.model, r.effort);
// Reject unknown fields, including accidental secret-bearing input.

View File

@@ -0,0 +1,44 @@
import { createInterface } from 'node:readline';
import { readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { spawn } from 'node:child_process';
import assert from 'node:assert/strict';
const config = JSON.parse(readFileSync(join(process.cwd(),'pi-fixture.json'),'utf8'));
const agent = process.env.PI_CODING_AGENT_DIR!;
const document = readFileSync(join(agent,'models.json'),'utf8');
const provider = JSON.parse(document).providers.beehive;
assert.equal(provider.apiKey,'$BEEHIVE_PI_KEY');
assert.equal(provider.models[0].id,config.model);
assert.equal(provider.models[0].name,config.model);
assert.equal(document.includes('synthetic'),false);
const args = process.argv.slice(2);
assert.equal(args[args.indexOf('--model')+1],config.model);
assert.equal(args[args.indexOf('--thinking')+1],config.effort ?? 'off');
assert.equal(process.env.PI_ACP_PI_COMMAND,process.execPath);
assert.equal(process.env.OPENAI_API_KEY,undefined);
assert.equal(process.env.DATABRICKS_TOKEN,undefined);
assert.equal(process.env.BEEHIVE_PI_RUNTIME,undefined);
const descendant = spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'});
writeFileSync(join(process.cwd(),'pi-owned.json'),JSON.stringify({adapter:process.pid,descendant:descendant.pid,directory:process.env.HOME,endpoint:provider.baseUrl}));
const sessionId = 'pi-fixture-session';
const configOptions = [{id:'model',category:'model',currentValue:`beehive/${config.model}`,options:[{value:`beehive/${config.model}`} ]},{id:'thought_level',category:'thought_level',currentValue:config.effort ?? 'off',options:[{value:config.effort ?? 'off'}]}];
const send = (m: unknown) => console.log(JSON.stringify({jsonrpc:'2.0',...m as object}));
createInterface({input:process.stdin}).on('line',async line => {
const m=JSON.parse(line); let result;
if(m.method==='initialize') result={protocolVersion:config.mode==='wrong-protocol'?2:1,agentInfo:{name:config.mode==='wrong-fork'?'pi-acp':'buzz-pi-acp',version:'0.0.33'}};
else if(m.method==='session/new') { if(config.instructions) assert.equal(m.params._meta?.systemPrompt,config.instructions); if(config.mode==='wrong-model') configOptions[0].currentValue='beehive/wrong'; if(config.mode==='wrong-effort') configOptions[1].currentValue='low'; result={sessionId,configOptions}; }
else if(m.method==='session/prompt') {
assert.equal(m.params.sessionId,sessionId);
if(config.mode==='drift-effort' || config.mode==='drift-model') { send({method:'session/update',params:{sessionId,update:config.mode==='drift-effort'?{sessionUpdate:'current_mode_update',currentModeId:'low'}:{sessionUpdate:'current_model_update',currentModelId:'beehive/wrong'}}});return; }
for(let n=0;n<(config.db?2:1);n++) {
const endpoint=config.db?provider.baseUrl+'/responses':config.endpoint+'/v1/responses';
if(!config.db) assert.equal(provider.baseUrl,'https://api.openai.com/v1');
const response=await fetch(endpoint,{method:'POST',headers:{authorization:'Bearer '+process.env[provider.apiKey.slice(1)]},body:JSON.stringify({model:provider.models[0].id, ...(config.effort?{reasoning:{effort:args[args.indexOf('--thinking')+1]}}:{}),input:'synthetic'})});
assert.equal(response.status,200); await response.text();
if(config.db) await new Promise(r=>setTimeout(r,75));
}
send({method:'session/update',params:{sessionId,update:{sessionUpdate:'agent_message_chunk',content:{type:'text',text:'Synthetic Pi complete'}}}}); result={stopReason:'end_turn'};
} else if(m.method==='session/cancel') return;
else throw Error('Unexpected Pi RPC '+m.method);
send({id:m.id,result});
});

View File

@@ -0,0 +1,51 @@
import './pi-isolation-loader.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';
import { createServer } from 'node:http';
import { once } from 'node:events';
import { mkdtempSync, writeFileSync, readFileSync, realpathSync, rmSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
const {ConversationSession}=await import('../src/broker.ts');
const {newKey,publicKey}=await import('../src/protocol.ts');
for(const variant of ['openai','databricks','cancel']) test(`Pi ${variant} conversation broker proves model/effort, native metadata and owned Stop`,async t=>{
const db=variant!=='openai',cancel=variant==='cancel';
const root=realpathSync(mkdtempSync(join(tmpdir(),'beehive-pi-conversation-')));t.after(()=>rmSync(root,{recursive:true,force:true}));
const model=db?'databricks-gpt-5-4':'gpt-5',effort='high',instructions='Pi exact native standing instructions';
const key={service:'beehive' as const,account:'provider:00000000-0000-0000-0000-000000000000'};
const observed:any[]=[];let requested!:()=>void;const requestStarted=new Promise<void>(resolve=>{requested=resolve;});
const server=createServer(async(req,res)=>{let body='';for await(const c of req)body+=c;observed.push({path:req.url,auth:req.headers.authorization,body:JSON.parse(body)});requested();if(!cancel)res.end('{}');});
server.listen(0,'127.0.0.1');await once(server,'listening');t.after(()=>new Promise<void>(r=>server.close(()=>r())));
const endpoint=`http://127.0.0.1:${(server.address() as any).port}`,route=db?'/ai-gateway/openai/v1/responses':'/v1/responses';
writeFileSync(join(root,'pi-fixture.json'),JSON.stringify({model,effort,db,endpoint,route,key,instructions}));
const executable=join(root,'buzz-pi-acp'),runtime=join(root,'fake-relay-runtime');
writeFileSync(executable,`#!${process.execPath}\nawait import(${JSON.stringify(new URL('./pi-acp-fixture.ts',import.meta.url).href)});\n`,{mode:0o700});
writeFileSync(runtime,`#!${process.execPath}
import {spawn} from 'node:child_process';import {createInterface} from 'node:readline';import {writeFileSync} from 'node:fs';
if(process.env.BEEHIVE_PI_KEY || process.env.BEEHIVE_PI_RUNTIME || process.env.BUZZ_ACP_MODEL) throw Error('Relay runtime received provider control');
const child=spawn(process.env.BUZZ_ACP_AGENT_COMMAND,process.env.BUZZ_ACP_AGENT_ARGS.split(','),{detached:true,stdio:['pipe','pipe','ignore'],env:{PATH:'/usr/bin:/bin'}});
writeFileSync('pi-transport-pids',JSON.stringify([process.pid,child.pid]));
const pending=new Map();let seq=0;
createInterface({input:child.stdout}).on('line',line=>{const m=JSON.parse(line);pending.get(m.id)?.(m);});
const request=(method,params)=>new Promise(resolve=>{const id=++seq;pending.set(id,resolve);child.stdin.write(JSON.stringify({jsonrpc:'2.0',id,method,params})+'\\n');});
await request('initialize',{protocolVersion:2,clientCapabilities:{}});
const created=await request('session/new',{cwd:process.cwd(),mcpServers:[]});
await request('session/prompt',{sessionId:created.result.sessionId,prompt:[{type:'text',text:'Synthetic conversation'}]});
`,{mode:0o700});
const session=new ConversationSession({executable:runtime,relay:'ws://127.0.0.1:1'},{executable,args:[],workspace:root,home:root,configDirectory:root,databricksHost:'',harness:'pi',piCli:realpathSync(process.execPath),model,instructions,resolvedProviderKey:'synthetic-key',buzzProvider:{provider:db?'databricks_v2':'openai-compat',baseUrl:db?'https://pi-workspace.example':'https://api.openai.com/v1',credential:key,models:[model],effort,...(db?{auth:'token' as const}:{wire:'auto' as const})}},newKey(),publicKey(newKey()));
t.after(()=>session.stop());
const proof=session.verify();
if(cancel) {
const rejected=assert.rejects(proof);await Promise.race([requestStarted,proof]);session.cancel();await rejected;
} else {
const evidence=await proof;assert.equal(evidence.model,model);assert.equal(evidence.source,'external-buzz-conversation');
assert.equal(observed.length,db?2:1);
for(const [i,row]of observed.entries()){assert.equal(row.path,route);assert.equal(row.body.model,model);assert.equal(row.body.reasoning.effort,effort);assert.equal(row.auth,db?`Bearer synthetic-pi-generation-${i+1}`:'Bearer synthetic-key');}
}
await session.stop();
const owned=JSON.parse(readFileSync(join(root,'pi-owned.json'),'utf8'));
const transport=JSON.parse(readFileSync(join(root,'pi-transport-pids'),'utf8'));
for(const pid of [...transport,owned.adapter,owned.descendant])assert.throws(()=>process.kill(pid,0),(e:any)=>e.code==='ESRCH');
assert.equal(existsSync(owned.directory),false);
if(db)await assert.rejects(fetch(owned.endpoint+'/responses'));
});

View File

@@ -0,0 +1,98 @@
import './pi-isolation-loader.mjs';
import type { Setup } from '../src/host.ts';
import type { Message } from '../src/protocol.ts';
import { createServer } from 'node:http';
import { once } from 'node:events';
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync, existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
// Load product modules only AFTER the explicit subprocess isolation hooks.
const { addDatabricks } = await import('../src/databricks.ts');
const { addOpenAI } = await import('../src/settings-credentials.ts');
const { readSettings, saveSettings, settingsId } = await import('../src/settings.ts');
const { newKey, publicKey, message } = await import('../src/protocol.ts');
const { host, provision } = await import('../src/host.ts');
const { createGenesis } = await import('../src/assignment.ts');
const { writePrivate } = await import('../src/storage.ts');
function fixture(t: test.TestContext) { const root = realpathSync(mkdtempSync(join(tmpdir(),'beehive-pairing-cli-settings-'))); t.after(() => rmSync(root,{ recursive: true, force: true })); return root; }
const sleep = (ms: number) => new Promise(r => setTimeout(r,ms));
async function wait(fn: () => boolean) { for(let n=0;n<150;n++) { if(fn()) return; await sleep(20); } throw Error('Fixture wait expired'); }
for (const providerType of ['pi-openai','pi-databricks','pi-custom','pi-databricks-custom'] as const) test(`${providerType} saved runtime reaches host Save/new Start while the prior active run remains immutable`, async t => {
const root = fixture(t), owner = newKey(), agent = newKey(), hostKey = publicKey(newKey());
const db = providerType.startsWith('pi-databricks');
const home = root, config = root;
const setup: Setup = { host: hostKey, ownerSecret: owner, agentSecret: agent, runner: realpathSync(process.execPath), args: ['-e','setInterval(()=>{},1000)'], workspace: root, serviceHome: home, configDirectory: config, mode: 'fixture' };
provision(root,setup,createGenesis(publicKey(owner),publicKey(agent),hostKey));
let receive: (m: Message) => void = () => {}; const reports: Message[] = [];
const transport = { binding: { host: hostKey, owner: publicKey(owner) }, validate() {}, connect(_url: string,_secret: string,handle: (m: Message) => void) { receive = handle; return { ready: Promise.resolve(), send(m: Message) { reports.push(m); }, close() {} }; } };
let providerReads = 0;
const running = await host(root,'ws://127.0.0.1',undefined,transport,undefined,async (input,signal) => { signal.throwIfAborted(); assert.equal((input as any).provider,!db ? 'openai-compat' : 'databricks_v2'); if (db) assert.equal((input as any).host,'https://pi-workspace.example'); providerReads++; return {ok:true,secret:'synthetic-provider-key'}; });
t.after(() => running.close());
const start = message('start',hostKey,publicKey(agent),0); receive(start);
await wait(() => reports.some(m => m.type === 'inventory' && m.body.phase === 'running'));
const active = reports.filter(m => m.type === 'inventory').at(-1)!.body.actualRun;
let stored: string | null = null;
if (!db) addOpenAI(root,'Fixture','synthetic',{ read: () => stored, create(_r,v) { stored = v; } });
else await addDatabricks(root,'Workspace','https://pi-workspace.example',new AbortController().signal,async input => { assert.equal(input.action,'login'); return {ok:true}; });
const prior = readSettings(root), id = settingsId();
const model = providerType === 'pi-databricks-custom' ? 'tenant.schema.gpt-6-service' : providerType === 'pi-custom' ? 'retained-custom-model' : db ? 'databricks-gpt-5-4' : 'gpt-5';
const effort = providerType.endsWith('custom') ? undefined : 'high';
const executable = join(root,'buzz-pi-acp');
const observations: any[] = [];
const server = createServer(async (req,res) => {
let body=''; for await(const chunk of req) body+=chunk;
observations.push({path:req.url,authorization:req.headers.authorization,body:JSON.parse(body)});
res.setHeader('content-type','text/event-stream');res.end('data: {"type":"response.completed"}\n\n');
});
server.listen(0,'127.0.0.1');await once(server,'listening');
t.after(() => new Promise<void>(resolve=>server.close(()=>resolve())));
const endpoint=`http://127.0.0.1:${(server.address() as any).port}`;
const route=db?'/ai-gateway/openai/v1/responses':'/v1/responses';
writeFileSync(join(root,'pi-fixture.json'),JSON.stringify({model,effort,db,endpoint,route,key:prior.providers[0]!.key}));
writeFileSync(executable,`#!${process.execPath}\nawait import(${JSON.stringify(new URL('./pi-acp-fixture.ts',import.meta.url).href)});\n`,{mode:0o700});
saveSettings(root,{ ...prior, runtimes: [{ id, name: 'Pi custom name', harness: 'pi', executable, providerId: prior.providers[0]!.id, model, cli:realpathSync(process.execPath), ...(effort?{effort}:{}) }] },prior.revision);
assert.equal(readSettings(root).runtimes[0]!.name,'Pi custom name');
assert.equal(providerReads,0,'Save does not accept secrets or start Pi');
await wait(() => running.settingsRevision === 2);
await wait(() => reports.some(m => m.type === 'inventory' && (m.body.harnessSetups as any[]).some(r => r.id === `runtime:${id}`)));
const inventory = reports.filter(m => m.type === 'inventory').at(-1)!;
assert.deepEqual(inventory.body.actualRun,active);
const binding = (inventory.body.harnessSetups as any[]).find(r => r.id === `runtime:${id}`);
const save = message('save',hostKey,publicKey(agent),inventory.revision,{ model, workspace: root, profile: 'default', harnessSetup: { id: binding.id, fingerprint: binding.fingerprint } });
receive(save); await wait(() => reports.some(m => m.body.operation === save.id));
assert.equal(reports.find(m => m.body.operation === save.id)?.body.result,'saved; running configuration unchanged');
assert.deepEqual(reports.filter(m => m.type === 'inventory').at(-1)!.body.actualRun,active);
const current = reports.filter(m => m.type === 'inventory').at(-1)!;
const stop = message('stop',hostKey,publicKey(agent),current.revision); receive(stop); await wait(() => reports.some(m => m.body.operation === stop.id));
const stopped = reports.filter(m => m.type === 'inventory').at(-1)!;
assert.equal(stopped.body.phase,'stopped');
const nextStart = message('start',hostKey,publicKey(agent),stopped.revision); receive(nextStart); await wait(() => reports.some(m => m.body.operation === nextStart.id));
assert.equal(reports.find(m => m.body.operation === nextStart.id)?.body.result,'accepted');
assert.equal((reports.filter(m => m.type === 'inventory').at(-1)!.body.actualRun as any).selection.model,model);
assert.equal(providerReads,1);
assert.equal(observations.length,db?2:1);
for(const [i,observed] of observations.entries()) {
assert.equal(observed.path,route);
assert.equal(observed.body.model,model);
assert.equal(observed.body.reasoning?.effort,effort);
assert.equal(observed.authorization,db?`Bearer synthetic-pi-generation-${i+1}`:'Bearer synthetic-provider-key');
}
const owned=JSON.parse(readFileSync(join(root,'pi-owned.json'),'utf8'));
const beforeStop=reports.filter(m=>m.type==='inventory').at(-1)!;
const piStop=message('stop',hostKey,publicKey(agent),beforeStop.revision);receive(piStop);
await wait(()=>reports.some(m=>m.body.operation===piStop.id));
assert.equal(reports.filter(m=>m.type==='inventory').at(-1)!.body.phase,'stopped');
for(const pid of [owned.adapter,owned.descendant]) assert.throws(()=>process.kill(pid,0),(e:any)=>e.code==='ESRCH');
assert.equal(existsSync(owned.directory),false);
if(db) { await assert.rejects(fetch(owned.endpoint+'/responses')); const native=readFileSync(join(root,'native-calls'),'utf8').trim().split('\n').map(v=>JSON.parse(v)); assert.equal(native.length,2); assert.deepEqual(native.map(v=>v.input.key),[prior.providers[0]!.key,prior.providers[0]!.key]); }
assert.ok(!JSON.stringify(reports).includes('synthetic-provider-key'));
assert.ok(!readFileSync(join(root,'journal.json'),'utf8').includes('synthetic-provider-key'));
const invalid = { ...readSettings(root), revision: 3, runtimes: [] }; writePrivate(join(root,'settings.json'),invalid);
await sleep(2100); assert.equal(running.settingsRevision,2,'invalid replacement retains effective catalog');
await running.close();
});

View File

@@ -0,0 +1,33 @@
// Explicit test-only subprocess seam: never installed or imported by production.
import { registerHooks } from 'node:module';
import { readFileSync, appendFileSync } from 'node:fs';
import { join } from 'node:path';
const loader = import.meta.url;
registerHooks({ load(url,context,next) {
const result = next(url,context);
if (url.endsWith('/src/acp.ts')) return { ...result, source: String(result.source).replace("[fileURLToPath(new URL('./pi-runtime-child.ts', import.meta.url)),", `[ '--import', ${JSON.stringify(loader)}, fileURLToPath(new URL('./pi-runtime-child.ts', import.meta.url)),`) };
if (url.endsWith('/src/databricks.ts')) {
const source = String(result.source);
const start = source.indexOf('export async function databricksNative(');
const end = source.indexOf('/** Native grant',start);
if(start<0 || end<0) throw Error('Native isolation seam changed');
return {...result,source:source.slice(0,start)+`export async function databricksNative(input,signal) { return globalThis.piFixtureNative(input,signal); }\n`+source.slice(end)};
}
return result;
}});
let expires = 0, generation = 0;
globalThis.piFixtureNative = async (input,signal) => {
signal.throwIfAborted();
const fixture = JSON.parse(readFileSync(join(process.cwd(),'pi-fixture.json'),'utf8'));
if (input.action !== 'token' || input.host !== 'https://pi-workspace.example' || JSON.stringify(input.key) !== JSON.stringify(fixture.key)) throw Error('Fixture native boundary mismatch');
if (Date.now() >= expires) { generation++; expires=Date.now()+50; }
appendFileSync(join(process.cwd(),'native-calls'),JSON.stringify({input,generation})+'\n');
return {ok:true,secret:`synthetic-pi-generation-${generation}`};
};
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url,options) => {
if (!String(url).startsWith('https://')) return originalFetch(url,options);
const fixture = JSON.parse(readFileSync(join(process.cwd(),'pi-fixture.json'),'utf8'));
if (url !== 'https://pi-workspace.example'+fixture.route) throw Error('Nonfixture network forbidden');
return originalFetch(fixture.endpoint+fixture.route,options);
};

48
beehive/test/pi.test.ts Normal file
View File

@@ -0,0 +1,48 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, writeFileSync, realpathSync, rmSync, readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { AgentSession, prepareAgent, type AgentLaunch } from '../src/acp.ts';
import { piEfforts, piModelConfig } from '../src/pi.ts';
import { runtimeForm } from '../src/runtime-form.ts';
import type { ManagerSnapshot } from '../src/manager-controller.ts';
import { runtimeCapabilities } from '../src/runtime-effort.ts';
const key={service:'beehive' as const,account:'provider:00000000-0000-0000-0000-000000000000'};
const provider={provider:'openai-compat' as const,baseUrl:'https://api.openai.com/v1',wire:'auto' as const,credential:key,models:['gpt-5'],effort:'high'};
for(const mode of ['wrong-model','wrong-effort','wrong-fork','wrong-protocol','drift-effort','drift-model']) test(`Pi rejects ${mode} before any provider request, Stop owns children`,async t=>{
const root=realpathSync(mkdtempSync(join(tmpdir(),'beehive-pi-negative-')));t.after(()=>rmSync(root,{recursive:true,force:true}));
const executable=join(root,'buzz-pi-acp');
writeFileSync(executable,`#!${process.execPath}\nawait import(${JSON.stringify(new URL('./pi-acp-fixture.ts',import.meta.url).href)});\n`,{mode:0o700});
writeFileSync(join(root,'pi-fixture.json'),JSON.stringify({mode,model:'gpt-5',effort:'high',db:false,endpoint:'http://127.0.0.1:1'}));
const launch:AgentLaunch={executable,args:[],workspace:root,home:root,configDirectory:root,databricksHost:'',harness:'pi',piCli:realpathSync(process.execPath),buzzProvider:provider,model:'gpt-5',resolvedProviderKey:'synthetic-secret'};
assert.throws(()=>prepareAgent({...launch,resolvedProviderKey:undefined}),/admission/);
const session=new AgentSession(launch);let stopped=false;t.after(()=>stopped?undefined:session.owned.stop());
if(mode.startsWith('drift-')) await session.catalog(); else await assert.rejects(session.catalog());
await assert.rejects(session.verify());
await session.owned.stop();stopped=true;
const owned=JSON.parse(readFileSync(join(root,'pi-owned.json'),'utf8'));
for(const pid of [owned.adapter,owned.descendant]) assert.throws(()=>process.kill(pid,0),(e:any)=>e.code==='ESRCH');
assert.equal(existsSync(owned.directory),false);
});
test('Pi model routes use the shared capability owner; UC tenant components never choose protocol or effort',()=>{
const db={...provider,provider:'databricks_v2' as const,wire:undefined,auth:'token' as const,baseUrl:'https://pi-workspace.example'};
for(const [model,api,suffix] of [['databricks-gpt-5-4','openai-responses','/ai-gateway/openai/v1'],['databricks-claude-sonnet-4-5','anthropic-messages','/ai-gateway/anthropic'],['gpt-5.schema.unknown','openai-completions','/ai-gateway/mlflow/v1'],['catalog.schema.gpt-6-service','openai-responses','/ai-gateway/openai/v1'],['catalog.schema.agpt-5-gpt-6','openai-responses','/ai-gateway/openai/v1'],['catalog.schema.claude-gpt-5','openai-completions','/ai-gateway/mlflow/v1']]) {
const p=piModelConfig(db,model).providers.beehive;
assert.equal(p.api,api);assert.equal(p.baseUrl,db.baseUrl+suffix);assert.equal(p.models[0].id,model);
}
assert.deepEqual(piEfforts('databricks_v2','catalog.schema.gpt-6-service'),[]);
assert.deepEqual(piEfforts('openai','unknown-custom'),[]);
assert.equal(runtimeCapabilities('databricks_v2','gpt-5.schema.unknown')!.supported_efforts.length,0);
});
test('actual runtime form offers Pi both providers, retains custom names, submits only supported model effort',async()=>{
for(const type of ['openai','databricks_v2'] as const) {
const model=type==='openai'?'gpt-5':'databricks-gpt-5-4';
const snapshot:ManagerSnapshot={local:[],agents:[],status:'fixture',harnesses:[{id:'pi',label:'Pi',executable:'/fake',cli:'/fake-pi',state:'available',providers:['openai','databricks_v2'],reason:'synthetic'}],models:[model],settings:{version:1,revision:1,agents:[],runtimes:[],providers:[{id:'p',name:'Saved',type,endpoint:type==='openai'?'https://api.openai.com/v1':'https://pi-workspace.example',key}]}};
const choices=['Pi · Available','Saved · p',model,'high'];const calls:any[]=[];
await runtimeForm({async choose(_label,options){const value=choices.shift();assert.ok(options.includes(value!));return value;},async input(){return 'My Pi';},async confirm(){return true;},notice(){throw Error('unexpected notice');}},()=>snapshot,async(action,values)=>{calls.push({action,values});});
assert.deepEqual(calls.at(-1),{action:'add-runtime',values:{name:'My Pi',model,provider:'p',harness:'pi',effort:'high'}});
}
});

View File

@@ -101,10 +101,11 @@ test('Desktop contract detects fake adapter/CLI independently, strict Codex vers
const rows = await discoverHarnesses(new AbortController().signal, {home:root,path:root,bundled:[],common:[],loginShells:[]});
assert.equal(rows.find(r => r.id === 'pi')?.executable,join(root,'buzz-pi-acp'));
assert.equal(rows.find(r => r.id === 'pi')?.state,'available');
assert.deepEqual(rows.find(r => r.id === 'pi')?.providers,['openai','databricks_v2']);
assert.equal(rows.find(r => r.id === 'codex')?.state,'available');
assert.equal(rows.find(r => r.id === 'claude')?.state,'cli-missing');
assert.equal(rows.find(r => r.id === 'goose')?.state,'not-installed');
assert.deepEqual(rows.filter(r => r.providers.length).map(r => r.id),['codex','buzz-agent']);
assert.deepEqual(rows.filter(r => r.providers.length).map(r => r.id),['codex','buzz-agent','pi']);
for (const value of ['1.9.99','1.10','1.10.0-rc1','not-version']) assert.equal(compatibleCodex(value),false);
for (const value of ['1.10.0','pkg 2.0.0']) assert.equal(compatibleCodex(value),true);
const slow = join(root,'slow'); writeFileSync(slow,'#!/bin/sh\nexec /bin/sleep 60\n',{mode:0o700});