Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions src/lib/agent/runner/backends/pi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
* follow-ups (#525, #524 skills) — v1 uses pi's built-in coding tools.
*/

import { getUI } from '../../../../ui';
import { logToFile } from '../../../../utils/debug';
import { getLlmGatewayUrlFromHost } from '../../../../utils/urls';
import fs from 'fs';
import path from 'path';
import { getUI } from '@ui';
import { getLogFilePath, logToFile } from '@utils/debug';
import { getLlmGatewayUrlFromHost } from '@utils/urls';
import {
POSTHOG_FLAG_HEADER_PREFIX,
POSTHOG_PROPERTY_HEADER_PREFIX,
Expand All @@ -41,6 +43,23 @@ function gatewayApiFor(
: 'anthropic-messages';
}

/**
* pi-specific runtime guidance appended to the shared commandments. Targets the
* top run-slowness causes (profiled): the agent reaching for blocked `bash
* ls/find` to explore (each retry is a model round-trip), re-fetching the skill
* menu, and writing literal PostHog URLs that the YARA scanner blocks at write
* time. Steering it once up front avoids the retry spirals.
*/
const PI_RUNTIME_NOTES = [
'',
'## This runtime',
"- To see a directory's files, call the `read` tool with the directory path (e.g. read '.' or read 'src/'); it returns the listing. Use `read` for files too. NEVER run `ls`, `find`, `cat`, or `grep` through `bash` — they are blocked and waste a turn.",
'- `bash` is ONLY for install/build/typecheck/lint/format. Run installs SYNCHRONOUSLY (e.g. `npm install <pkg>`); do not background with `&`, chain with `&&`, or pipe — all are blocked.',
'- Call `load_skill_menu` once to choose the skill, then `install_skill`. Do not call `load_skill_menu` again this session.',
"- Never write a PostHog URL or token as a literal in source (e.g. 'https://us.i.posthog.com') — it is blocked. Read them from environment variables (process.env.POSTHOG_HOST, os.environ['POSTHOG_HOST'], etc.).",
'- Update the task list FREQUENTLY as you work — mark items `completed` the moment you finish them and `in_progress` as you pick them up, so the displayed step always reflects where you actually are. Keep titles broad and action-oriented (the area of work), not specific files or sub-steps.',
].join('\n');

/**
* Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: always
* the Bedrock-fallback header, plus wizard metadata (`X-POSTHOG-PROPERTY-*`) and
Expand Down Expand Up @@ -89,6 +108,11 @@ export const piBackend: AgentRunner = {
const { session, boot, prompt, spinner, config, programConfig } = inputs;
const modelId = inputs.model;

// Init banner (parity #5).
getUI().log.step('Initializing Wizard agent...');
getUI().log.step(`Verbose logs: ${getLogFilePath()}`);
getUI().log.success("Agent initialized. Let's get cooking!");

spinner.start(config.spinnerMessage ?? 'Customizing your PostHog setup...');

try {
Expand Down Expand Up @@ -158,7 +182,7 @@ export const piBackend: AgentRunner = {
const resourceLoader = new DefaultResourceLoader({
cwd: session.installDir,
agentDir: getAgentDir(),
systemPrompt: getWizardCommandments(),
systemPrompt: getWizardCommandments() + '\n' + PI_RUNTIME_NOTES,
noExtensions: true,
noSkills: true,
noContextFiles: true,
Expand Down Expand Up @@ -239,7 +263,7 @@ export const piBackend: AgentRunner = {
break;
}
case 'agent_end': {
logToFile(`[pi] agent_end (willRetry=${event.willRetry})`);
logToFile(`[pi] agent_end (willRetry=${String(event.willRetry)})`);
break;
}
default:
Expand All @@ -265,6 +289,16 @@ export const piBackend: AgentRunner = {
return { error: AgentErrorType.YARA_VIOLATION };
}

// The skill plans events into .posthog-events.json then asks to remove it
// on completion; pi's `rm` is fence-blocked, so the agent can't — clean it
// up host-side rather than leave a stale (often empty) artifact (#15).
try {
const planFile = path.join(session.installDir, '.posthog-events.json');
if (fs.existsSync(planFile)) await fs.promises.rm(planFile);
} catch (err) {
logToFile(`[pi] .posthog-events.json cleanup skipped: ${String(err)}`);
}

spinner.stop(config.successMessage ?? 'PostHog integration complete');
return {};
} catch (err) {
Expand Down
Loading