@sweny-ai/core 0.1.146 → 0.1.148

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -57,6 +57,15 @@ export declare const CODEX_ENV_PREFIXES: readonly string[];
57
57
  * Pure: returns a copy.
58
58
  */
59
59
  export declare function resolveCodexAuthEnv(env: Record<string, string>): Record<string, string>;
60
+ /**
61
+ * pi's provider credentials (badlogic/pi-mono v0.99.2, docs/providers.md).
62
+ * sweny has no model opinion, so a pi run may need any provider's key; only
63
+ * these names (never the rest of the env) reach the pi process. A stored
64
+ * `auth.json` is not used: pi runs with a scratch `PI_CODING_AGENT_DIR`.
65
+ */
66
+ export declare const PI_AUTH_VARS: readonly string[];
67
+ /** Prefixes for a pi run: locale only. `ANTHROPIC_*` / `CLAUDE_*` beyond the names above never reach pi. */
68
+ export declare const PI_ENV_PREFIXES: readonly string[];
60
69
  /** A stored Codex login (`codex login`): `auth.json` under `CODEX_HOME`, default `~/.codex`. */
61
70
  export declare function hasCodexLogin(env?: Record<string, string | undefined>, exists?: (p: string) => boolean): boolean;
62
71
  export interface BuildAgentEnvOpts {
package/dist/agent-env.js CHANGED
@@ -158,6 +158,67 @@ export function resolveCodexAuthEnv(env) {
158
158
  out.CODEX_API_KEY = out.OPENAI_API_KEY;
159
159
  return out;
160
160
  }
161
+ /**
162
+ * pi's provider credentials (badlogic/pi-mono v0.99.2, docs/providers.md).
163
+ * sweny has no model opinion, so a pi run may need any provider's key; only
164
+ * these names (never the rest of the env) reach the pi process. A stored
165
+ * `auth.json` is not used: pi runs with a scratch `PI_CODING_AGENT_DIR`.
166
+ */
167
+ export const PI_AUTH_VARS = [
168
+ "ANTHROPIC_API_KEY",
169
+ "ANTHROPIC_OAUTH_TOKEN",
170
+ "ANTHROPIC_AUTH_TOKEN",
171
+ "OPENAI_API_KEY",
172
+ "GEMINI_API_KEY",
173
+ "DEEPSEEK_API_KEY",
174
+ "MISTRAL_API_KEY",
175
+ "GROQ_API_KEY",
176
+ "CEREBRAS_API_KEY",
177
+ "XAI_API_KEY",
178
+ "OPENROUTER_API_KEY",
179
+ "AI_GATEWAY_API_KEY",
180
+ "ZAI_API_KEY",
181
+ "ZAI_CODING_CN_API_KEY",
182
+ "OPENCODE_API_KEY",
183
+ "RADIUS_API_KEY",
184
+ "TYPESAFE_API_KEY",
185
+ "HF_TOKEN",
186
+ "FIREWORKS_API_KEY",
187
+ "TOGETHER_API_KEY",
188
+ "BASETEN_API_KEY",
189
+ "KIMI_API_KEY",
190
+ "META_API_KEY",
191
+ "MINIMAX_API_KEY",
192
+ "MINIMAX_CN_API_KEY",
193
+ "MOONSHOT_API_KEY",
194
+ "NVIDIA_API_KEY",
195
+ "ANT_LING_API_KEY",
196
+ "QWEN_TOKEN_PLAN_API_KEY",
197
+ "QWEN_TOKEN_PLAN_CN_API_KEY",
198
+ "XIAOMI_API_KEY",
199
+ "XIAOMI_TOKEN_PLAN_CN_API_KEY",
200
+ "XIAOMI_TOKEN_PLAN_AMS_API_KEY",
201
+ "XIAOMI_TOKEN_PLAN_SGP_API_KEY",
202
+ "COPILOT_GITHUB_TOKEN",
203
+ "AZURE_OPENAI_API_KEY",
204
+ "AZURE_OPENAI_BASE_URL",
205
+ "AZURE_OPENAI_RESOURCE_NAME",
206
+ "CLOUDFLARE_API_KEY",
207
+ "CLOUDFLARE_ACCOUNT_ID",
208
+ "CLOUDFLARE_GATEWAY_ID",
209
+ "GOOGLE_CLOUD_API_KEY",
210
+ "GOOGLE_CLOUD_PROJECT",
211
+ "GCLOUD_PROJECT",
212
+ "GOOGLE_CLOUD_LOCATION",
213
+ "AWS_ACCESS_KEY_ID",
214
+ "AWS_SECRET_ACCESS_KEY",
215
+ "AWS_SESSION_TOKEN",
216
+ "AWS_BEARER_TOKEN_BEDROCK",
217
+ "AWS_REGION",
218
+ "AWS_DEFAULT_REGION",
219
+ ];
220
+ /** Prefixes for a pi run: locale only. `ANTHROPIC_*` / `CLAUDE_*` beyond the names above never reach pi. */
221
+ export const PI_ENV_PREFIXES = ["LC_"];
161
222
  /** A stored Codex login (`codex login`): `auth.json` under `CODEX_HOME`, default `~/.codex`. */
162
223
  export function hasCodexLogin(env = process.env, exists = existsSync) {
163
224
  const home = env.CODEX_HOME || path.join(homedir(), ".codex");
@@ -19,4 +19,4 @@ export type FileConfig = Record<string, string | string[] | Record<string, unkno
19
19
  */
20
20
  export declare function loadConfigFile(cwd?: string): FileConfig;
21
21
  /** Starter config written by `sweny new`. */
22
- export declare const STARTER_CONFIG = "# .sweny.yml \u2014 SWEny project configuration\n# Commit this file. Secrets (API keys, tokens) go in .env (gitignored).\n#\n# Every key matches a CLI flag: \"time-range: 4h\" is the same as \"--time-range 4h\".\n# CLI flags override this file; env vars override this file; this file overrides defaults.\n\n# \u2500\u2500 Providers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# observability-provider: datadog # datadog | sentry | cloudwatch | splunk | elastic | newrelic | loki | prometheus | pagerduty | heroku | opsgenie | vercel | supabase | netlify | fly | render | file\n# issue-tracker-provider: github-issues # github-issues | linear | jira\n# source-control-provider: github # github | gitlab\n# coding-agent-provider: claude # claude | codex\n# notification-provider: console # console | slack | teams | discord | email | webhook\n\n# \u2500\u2500 Investigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# time-range: 24h\n# severity-focus: errors\n# service-filter: \"*\"\n# investigation-depth: standard # quick | standard | thorough\n\n# \u2500\u2500 PR / branch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# base-branch: main\n# pr-labels: agent,triage,needs-review\n\n# \u2500\u2500 Paths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# service-map-path: .github/service-map.yml\n# log-file: ./logs/errors.json # required when observability-provider is \"file\"\n\n# \u2500\u2500 Cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# cache-dir: .sweny/cache\n# cache-ttl: 86400\n\n# \u2500\u2500 Run reporting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Opt-in and not currently available: token minting is not yet exposed, so\n# leaving this unset means no reporting request is ever made.\n# cloud-token: sweny_pk_...\n# Or set SWENY_CLOUD_TOKEN in your environment.\n\n# \u2500\u2500 Agent sandbox \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# See https://docs.sweny.ai/advanced/agent-sandbox/\n# sandbox: off # off (local default) | auto (CI default: sandbox if supported, else warn) | strict\n# sandbox-allowed-domains: [internal.example.com] # extra hosts agent commands may reach\n# env-scope: off # off (local default: full env) | on (CI default: allowlist)\n# env-passthrough: [NPM_TOKEN] # extra env vars the agent may see when scoped (\"*\" = all)\n\n# \u2500\u2500 MCP servers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Extend the coding agent with additional tools via MCP.\n# Value is a JSON object \u2014 each key is a server name you choose.\n# See https://docs.sweny.ai/advanced/mcp-servers/ for a full catalog with copy-paste configs.\n#\n# Example: GitHub MCP server (query PRs, issues, CI run logs)\n# mcp-servers-json: '{\"github\":{\"type\":\"stdio\",\"command\":\"npx\",\"args\":[\"-y\",\"@modelcontextprotocol/server-github@latest\"],\"env\":{\"GITHUB_PERSONAL_ACCESS_TOKEN\":\"ghp_...\"}}}'\n\n# \u2500\u2500 Local-only quick start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Uncomment to run without any external services (just an LLM API key):\n# observability-provider: file\n# log-file: ./sample-errors.json\n# issue-tracker-provider: file\n# source-control-provider: file\n# notification-provider: file\n# output-dir: .sweny/output\n\n# \u2500\u2500 Credentials (.env) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Copy the relevant block into your .env file and fill in the values.\n#\n# Claude (coding agent) \u2014 https://console.anthropic.com/settings/api-keys\n# ANTHROPIC_API_KEY=sk-ant-...\n#\n# GitHub (source control + issue tracker)\n# GITHUB_TOKEN=ghp_... # https://github.com/settings/tokens (repo + issues scopes)\n#\n# Datadog (observability) \u2014 https://app.datadoghq.com/organization-settings\n# DD_API_KEY=... # Organization Settings > API Keys\n# DD_APP_KEY=... # Organization Settings > Application Keys\n# DD_SITE=datadoghq.com # or datadoghq.eu, us3.datadoghq.com, etc.\n#\n# Sentry (observability) \u2014 https://sentry.io/settings/auth-tokens/\n# SENTRY_AUTH_TOKEN=sntrys_...\n# SENTRY_ORG=your-org-slug # from sentry.io/organizations/<slug>/\n# SENTRY_PROJECT=your-project # Project Settings > General > Project Slug\n#\n# Linear (issue tracker) \u2014 https://linear.app/settings/api\n# LINEAR_API_KEY=lin_api_...\n# LINEAR_TEAM_ID=... # Settings > Workspace > Teams > [team] > copy ID from URL\n# LINEAR_BUG_LABEL_ID=... # Settings > Labels > [label] > copy ID from URL\n#\n# Jira (issue tracker) \u2014 https://your-org.atlassian.net\n# JIRA_BASE_URL=https://your-org.atlassian.net\n# JIRA_EMAIL=you@company.com # your Atlassian account email\n# JIRA_API_TOKEN=... # https://id.atlassian.com/manage-profile/security/api-tokens\n#\n# Vercel (observability) \u2014 https://vercel.com/account/tokens\n# VERCEL_TOKEN=...\n# VERCEL_PROJECT_ID=prj_... # Project Settings > General > Project ID\n# VERCEL_TEAM_ID=team_... # optional, for team-owned projects\n#\n# Supabase (observability) \u2014 https://supabase.com/dashboard/account/tokens\n# SUPABASE_MANAGEMENT_KEY=...\n# SUPABASE_PROJECT_REF=... # Project Settings > General > Reference ID\n#\n# Netlify (observability) \u2014 https://app.netlify.com/user/applications#personal-access-tokens\n# NETLIFY_TOKEN=...\n# NETLIFY_SITE_ID=... # Site Settings > General > Site ID\n#\n# Fly.io (observability) \u2014 https://fly.io/user/personal_access_tokens\n# FLY_TOKEN=...\n# FLY_APP_NAME=... # the name of your Fly.io application\n#\n# Render (observability) \u2014 https://dashboard.render.com/u/settings\n# RENDER_API_KEY=...\n# RENDER_SERVICE_ID=srv-... # from your service's Settings page\n#\n# Prometheus (observability) \u2014 self-hosted or Grafana Cloud\n# PROMETHEUS_URL=http://prometheus.internal:9090\n# PROMETHEUS_TOKEN=... # optional, for secured instances\n#\n# PagerDuty (observability) \u2014 https://your-account.pagerduty.com/api_keys\n# PAGERDUTY_API_KEY=...\n#\n# Honeycomb (observability) \u2014 https://docs.honeycomb.io/api/\n# HONEYCOMB_API_KEY=...\n# HONEYCOMB_DATASET=... # dataset name (e.g. production)\n#\n# Heroku (observability) \u2014 https://devcenter.heroku.com/articles/platform-api-reference\n# HEROKU_API_KEY=... # https://dashboard.heroku.com/account\n# HEROKU_APP_NAME=... # the name of your Heroku application\n#\n# OpsGenie (observability) \u2014 https://support.atlassian.com/opsgenie/docs/api-key-management/\n# OPSGENIE_API_KEY=...\n# OPSGENIE_REGION=us # or eu for EU-hosted accounts\n#\n# Slack (notifications) \u2014 https://api.slack.com/apps\n# NOTIFICATION_WEBHOOK_URL=https://hooks.slack.com/services/...\n# # or use a bot token: SLACK_BOT_TOKEN=xoxb-...\n";
22
+ export declare const STARTER_CONFIG = "# .sweny.yml \u2014 SWEny project configuration\n# Commit this file. Secrets (API keys, tokens) go in .env (gitignored).\n#\n# Every key matches a CLI flag: \"time-range: 4h\" is the same as \"--time-range 4h\".\n# CLI flags override this file; env vars override this file; this file overrides defaults.\n\n# \u2500\u2500 Providers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# observability-provider: datadog # datadog | sentry | cloudwatch | splunk | elastic | newrelic | loki | prometheus | pagerduty | heroku | opsgenie | vercel | supabase | netlify | fly | render | file\n# issue-tracker-provider: github-issues # github-issues | linear | jira\n# source-control-provider: github # github | gitlab\n# coding-agent-provider: claude # claude | codex | pi\n# notification-provider: console # console | slack | teams | discord | email | webhook\n\n# \u2500\u2500 Investigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# time-range: 24h\n# severity-focus: errors\n# service-filter: \"*\"\n# investigation-depth: standard # quick | standard | thorough\n\n# \u2500\u2500 PR / branch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# base-branch: main\n# pr-labels: agent,triage,needs-review\n\n# \u2500\u2500 Paths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# service-map-path: .github/service-map.yml\n# log-file: ./logs/errors.json # required when observability-provider is \"file\"\n\n# \u2500\u2500 Cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# cache-dir: .sweny/cache\n# cache-ttl: 86400\n\n# \u2500\u2500 Run reporting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Opt-in and not currently available: token minting is not yet exposed, so\n# leaving this unset means no reporting request is ever made.\n# cloud-token: sweny_pk_...\n# Or set SWENY_CLOUD_TOKEN in your environment.\n\n# \u2500\u2500 Agent sandbox \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# See https://docs.sweny.ai/advanced/agent-sandbox/\n# sandbox: off # off (local default) | auto (CI default: sandbox if supported, else warn) | strict\n# sandbox-allowed-domains: [internal.example.com] # extra hosts agent commands may reach\n# env-scope: off # off (local default: full env) | on (CI default: allowlist)\n# env-passthrough: [NPM_TOKEN] # extra env vars the agent may see when scoped (\"*\" = all)\n\n# \u2500\u2500 MCP servers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Extend the coding agent with additional tools via MCP.\n# Value is a JSON object \u2014 each key is a server name you choose.\n# See https://docs.sweny.ai/advanced/mcp-servers/ for a full catalog with copy-paste configs.\n#\n# Example: GitHub MCP server (query PRs, issues, CI run logs)\n# mcp-servers-json: '{\"github\":{\"type\":\"stdio\",\"command\":\"npx\",\"args\":[\"-y\",\"@modelcontextprotocol/server-github@latest\"],\"env\":{\"GITHUB_PERSONAL_ACCESS_TOKEN\":\"ghp_...\"}}}'\n\n# \u2500\u2500 Local-only quick start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Uncomment to run without any external services (just an LLM API key):\n# observability-provider: file\n# log-file: ./sample-errors.json\n# issue-tracker-provider: file\n# source-control-provider: file\n# notification-provider: file\n# output-dir: .sweny/output\n\n# \u2500\u2500 Credentials (.env) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Copy the relevant block into your .env file and fill in the values.\n#\n# Claude (coding agent) \u2014 https://console.anthropic.com/settings/api-keys\n# ANTHROPIC_API_KEY=sk-ant-...\n#\n# GitHub (source control + issue tracker)\n# GITHUB_TOKEN=ghp_... # https://github.com/settings/tokens (repo + issues scopes)\n#\n# Datadog (observability) \u2014 https://app.datadoghq.com/organization-settings\n# DD_API_KEY=... # Organization Settings > API Keys\n# DD_APP_KEY=... # Organization Settings > Application Keys\n# DD_SITE=datadoghq.com # or datadoghq.eu, us3.datadoghq.com, etc.\n#\n# Sentry (observability) \u2014 https://sentry.io/settings/auth-tokens/\n# SENTRY_AUTH_TOKEN=sntrys_...\n# SENTRY_ORG=your-org-slug # from sentry.io/organizations/<slug>/\n# SENTRY_PROJECT=your-project # Project Settings > General > Project Slug\n#\n# Linear (issue tracker) \u2014 https://linear.app/settings/api\n# LINEAR_API_KEY=lin_api_...\n# LINEAR_TEAM_ID=... # Settings > Workspace > Teams > [team] > copy ID from URL\n# LINEAR_BUG_LABEL_ID=... # Settings > Labels > [label] > copy ID from URL\n#\n# Jira (issue tracker) \u2014 https://your-org.atlassian.net\n# JIRA_BASE_URL=https://your-org.atlassian.net\n# JIRA_EMAIL=you@company.com # your Atlassian account email\n# JIRA_API_TOKEN=... # https://id.atlassian.com/manage-profile/security/api-tokens\n#\n# Vercel (observability) \u2014 https://vercel.com/account/tokens\n# VERCEL_TOKEN=...\n# VERCEL_PROJECT_ID=prj_... # Project Settings > General > Project ID\n# VERCEL_TEAM_ID=team_... # optional, for team-owned projects\n#\n# Supabase (observability) \u2014 https://supabase.com/dashboard/account/tokens\n# SUPABASE_MANAGEMENT_KEY=...\n# SUPABASE_PROJECT_REF=... # Project Settings > General > Reference ID\n#\n# Netlify (observability) \u2014 https://app.netlify.com/user/applications#personal-access-tokens\n# NETLIFY_TOKEN=...\n# NETLIFY_SITE_ID=... # Site Settings > General > Site ID\n#\n# Fly.io (observability) \u2014 https://fly.io/user/personal_access_tokens\n# FLY_TOKEN=...\n# FLY_APP_NAME=... # the name of your Fly.io application\n#\n# Render (observability) \u2014 https://dashboard.render.com/u/settings\n# RENDER_API_KEY=...\n# RENDER_SERVICE_ID=srv-... # from your service's Settings page\n#\n# Prometheus (observability) \u2014 self-hosted or Grafana Cloud\n# PROMETHEUS_URL=http://prometheus.internal:9090\n# PROMETHEUS_TOKEN=... # optional, for secured instances\n#\n# PagerDuty (observability) \u2014 https://your-account.pagerduty.com/api_keys\n# PAGERDUTY_API_KEY=...\n#\n# Honeycomb (observability) \u2014 https://docs.honeycomb.io/api/\n# HONEYCOMB_API_KEY=...\n# HONEYCOMB_DATASET=... # dataset name (e.g. production)\n#\n# Heroku (observability) \u2014 https://devcenter.heroku.com/articles/platform-api-reference\n# HEROKU_API_KEY=... # https://dashboard.heroku.com/account\n# HEROKU_APP_NAME=... # the name of your Heroku application\n#\n# OpsGenie (observability) \u2014 https://support.atlassian.com/opsgenie/docs/api-key-management/\n# OPSGENIE_API_KEY=...\n# OPSGENIE_REGION=us # or eu for EU-hosted accounts\n#\n# Slack (notifications) \u2014 https://api.slack.com/apps\n# NOTIFICATION_WEBHOOK_URL=https://hooks.slack.com/services/...\n# # or use a bot token: SLACK_BOT_TOKEN=xoxb-...\n";
@@ -148,7 +148,7 @@ export const STARTER_CONFIG = `# .sweny.yml — SWEny project configuration
148
148
  # observability-provider: datadog # datadog | sentry | cloudwatch | splunk | elastic | newrelic | loki | prometheus | pagerduty | heroku | opsgenie | vercel | supabase | netlify | fly | render | file
149
149
  # issue-tracker-provider: github-issues # github-issues | linear | jira
150
150
  # source-control-provider: github # github | gitlab
151
- # coding-agent-provider: claude # claude | codex
151
+ # coding-agent-provider: claude # claude | codex | pi
152
152
  # notification-provider: console # console | slack | teams | discord | email | webhook
153
153
 
154
154
  # ── Investigation ────────────────────────────────────────────────────
@@ -8,7 +8,7 @@ export function registerTriageCommand(program) {
8
8
  return program
9
9
  .command("triage")
10
10
  .description("Run the SWEny triage workflow")
11
- .option("--agent <provider>", "Coding agent: claude (default) or codex")
11
+ .option("--agent <provider>", "Coding agent: claude (default), codex, or pi (experimental)")
12
12
  .option("--coding-agent-provider <provider>", "Coding agent provider (alias for --agent)")
13
13
  .option("--observability-provider <provider>", "Observability provider (default: none)")
14
14
  .option("--issue-tracker-provider <provider>", "Issue tracker provider (default: github-issues)")
@@ -238,6 +238,10 @@ export function validateInputs(config) {
238
238
  errors.push("Missing: CODEX_API_KEY or OPENAI_API_KEY, or a Codex login (`codex login`), for --agent codex");
239
239
  }
240
240
  break;
241
+ case "pi":
242
+ // pi takes any provider's key, or none (a local model in models.json), so
243
+ // there is no single credential to check here; pi reports its own auth error.
244
+ break;
241
245
  default:
242
246
  // Honest --agent (#330): only agents with an adapter are accepted.
243
247
  // Anything else would silently run a different agent under that name.
@@ -658,7 +662,7 @@ export function registerImplementCommand(program) {
658
662
  return program
659
663
  .command("implement <issueId>")
660
664
  .description("Implement a fix for a specific issue and open a PR")
661
- .option("--agent <provider>", "Coding agent: claude (default) or codex")
665
+ .option("--agent <provider>", "Coding agent: claude (default), codex, or pi (experimental)")
662
666
  .option("--coding-agent-provider <provider>", "Coding agent provider (alias for --agent)")
663
667
  .option("--issue-tracker-provider <provider>", "Issue tracker (linear|jira|github-issues|file)")
664
668
  .option("--source-control-provider <provider>", "Source control (github|gitlab|file)")
@@ -215,7 +215,7 @@ export const WORKFLOW_RUN_OPTIONS = [
215
215
  "Write a PR-comment markdown (run receipt, status-colored DAG, per-node table; metadata only) to <path> so any CI can post it",
216
216
  ],
217
217
  ["--input <json>", "JSON string of input data to pass to the workflow"],
218
- ["--agent <id>", "Coding agent that runs the nodes: claude (default) or codex"],
218
+ ["--agent <id>", "Coding agent that runs the nodes: claude (default), codex, or pi (experimental)"],
219
219
  [
220
220
  "--harness-policy <mode>",
221
221
  "strict: refuse a node whose policy the agent cannot enforce; warn: run it and report what was not enforced (default: strict under GitHub Actions, warn elsewhere; env SWENY_HARNESS_POLICY)",
@@ -4,7 +4,7 @@
4
4
  * runs one agent under another's name. No runtime imports (the CLI config and
5
5
  * the harness factory share it).
6
6
  */
7
- export declare const SUPPORTED_AGENTS: readonly ["claude", "codex"];
7
+ export declare const SUPPORTED_AGENTS: readonly ["claude", "codex", "pi"];
8
8
  export type SupportedAgent = (typeof SUPPORTED_AGENTS)[number];
9
9
  export declare function isSupportedAgent(id: string): id is SupportedAgent;
10
10
  /** The error for an agent id with no adapter. */
@@ -4,12 +4,12 @@
4
4
  * runs one agent under another's name. No runtime imports (the CLI config and
5
5
  * the harness factory share it).
6
6
  */
7
- export const SUPPORTED_AGENTS = ["claude", "codex"];
7
+ export const SUPPORTED_AGENTS = ["claude", "codex", "pi"];
8
8
  export function isSupportedAgent(id) {
9
9
  return SUPPORTED_AGENTS.includes(id);
10
10
  }
11
11
  /** The error for an agent id with no adapter. */
12
12
  export function unsupportedAgentError(id) {
13
13
  return (`Unsupported coding agent "${id}": supported agents are "claude" (headless Claude Code) ` +
14
- `and "codex" (Codex CLI). Remove --agent / coding-agent-provider or set it to one of them.`);
14
+ `and "codex" (Codex CLI), or "pi" (pi coding agent, experimental). Remove --agent / coding-agent-provider or set it to one of them.`);
15
15
  }
@@ -28,3 +28,31 @@ export declare const CLAUDE_CODE_CAPABILITIES: HarnessCapabilities;
28
28
  * - usage tokens only: `turn.completed.usage`; Codex reports no cost.
29
29
  */
30
30
  export declare const CODEX_CAPABILITIES: HarnessCapabilities;
31
+ /**
32
+ * pi (`pi --mode rpc`), declared from the pi coding-agent docs and source at
33
+ * badlogic/pi-mono v0.99.2 (`packages/coding-agent/docs/{cli,rpc,rpc-commands,json,mcp,
34
+ * security,message-types}.md`, `src/core/agent-session.ts`, `src/extensions/mcp/`)
35
+ * and probed by the contract suite against the scripted fake:
36
+ * - structuredOutput prompt: pi has no output-schema flag; sweny prompts for
37
+ * the JSON, parses and validates it.
38
+ * - toolTrace full: `tool_execution_start|end` events carry `toolCallId`,
39
+ * `toolName`, `args`, `isError` for built-in and MCP tools alike.
40
+ * - builtinDeny shell-only, plus write, edit and subagent: `--exclude-tools` and
41
+ * `--tools` take tool names (`bash`, `write`, `edit`), and `-ne` leaves no
42
+ * extension, so no subagent tool exists. pi has no built-in net tool, so `net`
43
+ * is not deniable as a class (`bash` can still curl): it degrades or, in
44
+ * strict, refuses.
45
+ * - mcp home-isolation: MCP is a built-in extension that reads `mcp.json` from
46
+ * `$PI_CODING_AGENT_DIR`. sweny points that at a scratch dir holding only the
47
+ * generated `mcp.json`, and passes `-na` so a project `.pi/mcp.json` never loads.
48
+ * - sandbox none: "no built-in sandbox" (docs/security.md). The process wrapper
49
+ * (sandbox-wrapper.ts) is the only containment; strict refuses without it.
50
+ * - readOnly tool-allowlist: `--tools read,grep,find,ls` plus sweny's own MCP
51
+ * tools; no shell, write or edit tool exists in the run.
52
+ * - turnLimit watchdog: pi has no turn limit flag; sweny counts
53
+ * `tool_execution_start` events and aborts.
54
+ * - usage tokens and cost: `get_session_stats` returns tokens and cost for the
55
+ * session. Read once at the end, so `live` is false.
56
+ * - cancel rpc: the `abort` command, then SIGTERM and SIGKILL.
57
+ */
58
+ export declare const PI_CAPABILITIES: HarnessCapabilities;
@@ -50,3 +50,43 @@ export const CODEX_CAPABILITIES = {
50
50
  cancel: "kill",
51
51
  resume: false,
52
52
  };
53
+ /**
54
+ * pi (`pi --mode rpc`), declared from the pi coding-agent docs and source at
55
+ * badlogic/pi-mono v0.99.2 (`packages/coding-agent/docs/{cli,rpc,rpc-commands,json,mcp,
56
+ * security,message-types}.md`, `src/core/agent-session.ts`, `src/extensions/mcp/`)
57
+ * and probed by the contract suite against the scripted fake:
58
+ * - structuredOutput prompt: pi has no output-schema flag; sweny prompts for
59
+ * the JSON, parses and validates it.
60
+ * - toolTrace full: `tool_execution_start|end` events carry `toolCallId`,
61
+ * `toolName`, `args`, `isError` for built-in and MCP tools alike.
62
+ * - builtinDeny shell-only, plus write, edit and subagent: `--exclude-tools` and
63
+ * `--tools` take tool names (`bash`, `write`, `edit`), and `-ne` leaves no
64
+ * extension, so no subagent tool exists. pi has no built-in net tool, so `net`
65
+ * is not deniable as a class (`bash` can still curl): it degrades or, in
66
+ * strict, refuses.
67
+ * - mcp home-isolation: MCP is a built-in extension that reads `mcp.json` from
68
+ * `$PI_CODING_AGENT_DIR`. sweny points that at a scratch dir holding only the
69
+ * generated `mcp.json`, and passes `-na` so a project `.pi/mcp.json` never loads.
70
+ * - sandbox none: "no built-in sandbox" (docs/security.md). The process wrapper
71
+ * (sandbox-wrapper.ts) is the only containment; strict refuses without it.
72
+ * - readOnly tool-allowlist: `--tools read,grep,find,ls` plus sweny's own MCP
73
+ * tools; no shell, write or edit tool exists in the run.
74
+ * - turnLimit watchdog: pi has no turn limit flag; sweny counts
75
+ * `tool_execution_start` events and aborts.
76
+ * - usage tokens and cost: `get_session_stats` returns tokens and cost for the
77
+ * session. Read once at the end, so `live` is false.
78
+ * - cancel rpc: the `abort` command, then SIGTERM and SIGKILL.
79
+ */
80
+ export const PI_CAPABILITIES = {
81
+ structuredOutput: "prompt",
82
+ toolTrace: "full",
83
+ builtinDeny: "shell-only",
84
+ denyClasses: ["shell", "write", "edit", "subagent"],
85
+ mcp: { inject: true, exclusive: "home-isolation" },
86
+ sandbox: { fs: false, network: false },
87
+ readOnly: "tool-allowlist",
88
+ turnLimit: "watchdog",
89
+ usage: { tokens: true, costUsd: true, live: false },
90
+ cancel: "rpc",
91
+ resume: false,
92
+ };
@@ -175,7 +175,8 @@ export declare class ClaudeCodeHarness implements Claude, AgentHarness {
175
175
  * is a read-only run; `policy.deny` classes become native `disallowedTools`
176
176
  * names, merged with `nativeDeny` and the legacy `disallowedTools`; and
177
177
  * `policy.strict` makes MCP exclusive (`strictMcpConfig`) even for a
178
- * write-capable node.
178
+ * write-capable node. Per-request sandbox mode overrides the client default,
179
+ * and policy egress supplies the node hosts without dropping scoped env access.
179
180
  */
180
181
  run(req: HarnessRunRequest): Promise<HarnessRunResult>;
181
182
  private runQuery;
@@ -272,7 +272,8 @@ export class ClaudeCodeHarness {
272
272
  * is a read-only run; `policy.deny` classes become native `disallowedTools`
273
273
  * names, merged with `nativeDeny` and the legacy `disallowedTools`; and
274
274
  * `policy.strict` makes MCP exclusive (`strictMcpConfig`) even for a
275
- * write-capable node.
275
+ * write-capable node. Per-request sandbox mode overrides the client default,
276
+ * and policy egress supplies the node hosts without dropping scoped env access.
276
277
  */
277
278
  async run(req) {
278
279
  const policy = req.policy ?? {
@@ -301,6 +302,8 @@ export class ClaudeCodeHarness {
301
302
  readOnly,
302
303
  disallowedTools: disallowedTools.length > 0 ? disallowedTools : undefined,
303
304
  strictMcp: readOnly || policy.strict,
305
+ sandboxMode: policy.sandbox,
306
+ agentAccess: { envVars: req.agentAccess?.envVars ?? [], domains: policy.egress },
304
307
  });
305
308
  return { ...result, harness: this.info(), degraded: gate.degraded };
306
309
  }
@@ -318,7 +321,7 @@ export class ClaudeCodeHarness {
318
321
  // `strict` fails the node here instead.
319
322
  const sandbox = resolveAgentSandbox({
320
323
  env: process.env,
321
- mode: this.sandboxMode,
324
+ mode: opts.sandboxMode ?? this.sandboxMode,
322
325
  allowedDomains: this.sandboxAllowedDomains,
323
326
  nodeDomains: agentAccess?.domains,
324
327
  probe: this.sandboxProbe,
@@ -326,7 +329,7 @@ export class ClaudeCodeHarness {
326
329
  });
327
330
  if (sandbox.error) {
328
331
  this.logger.error(sandbox.error);
329
- return { status: "failed", data: { error: sandbox.error }, toolCalls: [] };
332
+ return { status: "failed", data: { error: sandbox.error, refused: true }, toolCalls: [] };
330
333
  }
331
334
  if (sandbox.warning && !this.sandboxWarned) {
332
335
  this.sandboxWarned = true;
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { ClaudeCodeHarness, type ClaudeCodeHarnessOptions } from "./claude-code.js";
6
6
  import { CodexHarness, type CodexHarnessOptions } from "./codex.js";
7
+ import { PiHarness, type PiHarnessOptions } from "./pi.js";
7
8
  export { claudeCompat, asClaude } from "./compat.js";
8
9
  export type { AgentHarness, HarnessCapabilities, HarnessCompleteRequest, HarnessId, HarnessInfo, HarnessRunRequest, HarnessRunResult, NodePolicy, PolicyGateResult, PolicyWrappers, ToolClass, } from "./types.js";
9
10
  export { policyGate, nativeDenyClasses, resolveHarnessPolicy, isToolClass, TOOL_CLASSES } from "./policy.js";
@@ -13,6 +14,8 @@ export { ClaudeCodeHarness, CLAUDE_CODE_CAPABILITIES } from "./claude-code.js";
13
14
  export type { ClaudeCodeHarnessOptions } from "./claude-code.js";
14
15
  export { CodexHarness, CODEX_CAPABILITIES, MIN_CODEX_VERSION } from "./codex.js";
15
16
  export type { CodexHarnessOptions } from "./codex.js";
17
+ export { PiHarness, PI_CAPABILITIES, MIN_PI_VERSION } from "./pi.js";
18
+ export type { PiHarnessOptions } from "./pi.js";
16
19
  export { startToolBridge } from "./tool-bridge/server.js";
17
20
  export type { ToolBridge, ToolBridgeOptions } from "./tool-bridge/server.js";
18
21
  export { SUPPORTED_AGENTS, isSupportedAgent, unsupportedAgentError } from "./agents.js";
@@ -24,4 +27,5 @@ export type { SupportedAgent } from "./agents.js";
24
27
  */
25
28
  export declare function createHarness(id: "claude" | "claude-code", opts?: ClaudeCodeHarnessOptions): ClaudeCodeHarness;
26
29
  export declare function createHarness(id: "codex", opts?: CodexHarnessOptions): CodexHarness;
27
- export declare function createHarness(id: string, opts?: ClaudeCodeHarnessOptions & CodexHarnessOptions): ClaudeCodeHarness | CodexHarness;
30
+ export declare function createHarness(id: "pi", opts?: PiHarnessOptions): PiHarness;
31
+ export declare function createHarness(id: string, opts?: ClaudeCodeHarnessOptions & CodexHarnessOptions & PiHarnessOptions): ClaudeCodeHarness | CodexHarness | PiHarness;
@@ -4,12 +4,14 @@
4
4
  */
5
5
  import { ClaudeCodeHarness } from "./claude-code.js";
6
6
  import { CodexHarness } from "./codex.js";
7
+ import { PiHarness } from "./pi.js";
7
8
  import { unsupportedAgentError } from "./agents.js";
8
9
  export { claudeCompat, asClaude } from "./compat.js";
9
10
  export { policyGate, nativeDenyClasses, resolveHarnessPolicy, isToolClass, TOOL_CLASSES } from "./policy.js";
10
11
  export { ask, evaluate, buildAskPrompt, buildEvaluatePrompt } from "./prompts.js";
11
12
  export { ClaudeCodeHarness, CLAUDE_CODE_CAPABILITIES } from "./claude-code.js";
12
13
  export { CodexHarness, CODEX_CAPABILITIES, MIN_CODEX_VERSION } from "./codex.js";
14
+ export { PiHarness, PI_CAPABILITIES, MIN_PI_VERSION } from "./pi.js";
13
15
  export { startToolBridge } from "./tool-bridge/server.js";
14
16
  export { SUPPORTED_AGENTS, isSupportedAgent, unsupportedAgentError } from "./agents.js";
15
17
  export function createHarness(id, opts = {}) {
@@ -19,6 +21,8 @@ export function createHarness(id, opts = {}) {
19
21
  return new ClaudeCodeHarness(opts);
20
22
  case "codex":
21
23
  return new CodexHarness(opts);
24
+ case "pi":
25
+ return new PiHarness(opts);
22
26
  default:
23
27
  throw new Error(unsupportedAgentError(id));
24
28
  }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * pi harness (#415): runs a node on the pi coding agent (bring any model)
3
+ * through `pi --mode rpc`.
4
+ *
5
+ * Interfaces used, all read from badlogic/pi-mono at v0.99.2
6
+ * (`packages/coding-agent`, now also published as earendil-works/pi):
7
+ * - `docs/cli.md`: `--mode rpc`, `--no-session`, `--tools` (allowlist over
8
+ * built-in, extension and MCP tools), `--exclude-tools`, `--no-tools`,
9
+ * `--no-extensions` with explicit `-e builtin:mcp`, `--no-context-files`,
10
+ * `--no-skills`, `--no-prompt-templates`, `--no-themes`, `--no-approve`,
11
+ * `--model`, `--append-system-prompt`.
12
+ * - `docs/rpc.md`, `docs/rpc-commands.md`: JSONL framing on LF only, the
13
+ * `prompt`, `abort` and `get_session_stats` commands, `agent_settled`.
14
+ * - `docs/json.md`, `docs/message-types.md`: `message_end` (assistant message
15
+ * with `stopReason` and `errorMessage`), `tool_execution_start|end`,
16
+ * `auto_retry_end`.
17
+ * - `docs/mcp.md`, `src/extensions/mcp/`: `mcp.json` in `$PI_CODING_AGENT_DIR`,
18
+ * `exposure: "direct"` so tools are declared to the model, `${VAR}` in
19
+ * `env`, tool names `mcp__<server>__<tool>`.
20
+ * - `docs/security.md`: "no built-in sandbox", so the whole process runs
21
+ * inside the sweny process wrapper (sandbox-wrapper.ts) and strict refuses
22
+ * without it.
23
+ * - `src/core/agent-session.ts` `_refreshToolRegistry`: a `--tools` allowlist
24
+ * filters every registered tool, MCP tools included, by exact name.
25
+ *
26
+ * Every sweny opinion in the pi column of the enforcement matrix is enforced
27
+ * natively, kept by sweny (wrapper, tool bridge, prompt + parse + validate,
28
+ * watchdog), reported in `degraded`, or refused under strict policy by
29
+ * `policyGate`. Nothing is dropped silently.
30
+ */
31
+ import type { Logger, McpServerConfig, NodeUsage, ToolContext, Tool } from "../types.js";
32
+ import { type SandboxMode } from "../agent-env.js";
33
+ import type { AgentHarness, HarnessCapabilities, HarnessCompleteRequest, HarnessInfo, HarnessRunRequest, HarnessRunResult, ToolClass } from "./types.js";
34
+ import { PI_CAPABILITIES } from "./capabilities.js";
35
+ import { type HarnessPolicyMode } from "./policy.js";
36
+ import { type SandboxWrapper } from "./sandbox-wrapper.js";
37
+ export { PI_CAPABILITIES };
38
+ /**
39
+ * Oldest pi this adapter is written against. The docs and source were read at
40
+ * v0.99.2; older releases are unverified, so they are refused.
41
+ */
42
+ export declare const MIN_PI_VERSION = "0.99.2";
43
+ /** Name of the MCP server that carries sweny's skill tools (the tool bridge). */
44
+ export declare const PI_BRIDGE_SERVER = "sweny-core";
45
+ /**
46
+ * Hosts pi itself needs through the sandbox wrapper: the API host of every
47
+ * provider whose key is in the env, and of the provider named in `--model`
48
+ * (`provider/id`). Other providers (custom endpoints in `models.json`, cloud
49
+ * providers) are added with `SWENY_SANDBOX_ALLOWED_DOMAINS`.
50
+ */
51
+ export declare function piBackendHosts(env?: Record<string, string | undefined>, model?: string): string[];
52
+ export interface PiHarnessOptions {
53
+ /** Model passed to `--model`. Free text (`provider/id`, `id:thinking`); sweny has no model opinion. */
54
+ model?: string;
55
+ /** Tool-call budget the watchdog enforces when a node sets no `max_turns` (default: 20). */
56
+ maxTurns?: number;
57
+ /** Working directory (default: process.cwd()). */
58
+ cwd?: string;
59
+ logger?: Logger;
60
+ /** Default tool context for standalone usage (not via the executor). */
61
+ defaultContext?: ToolContext;
62
+ /** Catalog defaults, overridden by per-run skill servers and explicit mcpServers. */
63
+ defaultMcpServers?: Record<string, McpServerConfig>;
64
+ /** Explicit external MCP servers, overriding defaults and per-run skill servers. */
65
+ mcpServers?: Record<string, McpServerConfig>;
66
+ /** Extra env var names passed through (see agent-env.ts). Default: `SWENY_ENV_PASSTHROUGH`. */
67
+ envPassthrough?: string[];
68
+ /** Scope the pi env to the allowlist. Default: `SWENY_ENV_SCOPE`, else on in CI. */
69
+ envScope?: boolean;
70
+ /**
71
+ * Process sandbox mode (`SWENY_SANDBOX`): `strict` refuses the node without
72
+ * the process wrapper, `auto` wraps when a wrapper is available and otherwise
73
+ * degrades, `off` never wraps. Default: `SWENY_SANDBOX`, else `auto` in CI
74
+ * and `off` locally.
75
+ */
76
+ sandbox?: SandboxMode;
77
+ /**
78
+ * The host's process sandbox wrapper (#360 step 2). `undefined` = detect
79
+ * srt once per process; `null` = none (test seam).
80
+ */
81
+ sandboxWrapper?: SandboxWrapper | null;
82
+ /** `strict` refuses a node with unenforceable opinions. Default: {@link resolveHarnessPolicy}. */
83
+ policy?: HarnessPolicyMode;
84
+ /** The pi CLI. Default: `SWENY_PI_PATH`, else `pi` on PATH. Test seam for the fake. */
85
+ piCommand?: {
86
+ command: string;
87
+ args: string[];
88
+ };
89
+ /**
90
+ * A `models.json` (custom providers and models) copied into pi's scratch
91
+ * agent dir. Default: `SWENY_PI_MODELS_JSON`. Without it pi sees only its
92
+ * built-in model catalog; the operator's own `~/.pi/agent` is never read.
93
+ */
94
+ modelsFile?: string;
95
+ /** Shim command override for the tool bridge (test seam). */
96
+ toolBridgeShim?: {
97
+ command: string;
98
+ args: string[];
99
+ };
100
+ /** How long to wait for pi to stop after the `abort` command before SIGTERM (default: 3000 ms). */
101
+ abortGraceMs?: number;
102
+ /** Grace period between SIGTERM and SIGKILL when stopping pi (default: 2000 ms). */
103
+ killGraceMs?: number;
104
+ /** How long to wait for `get_session_stats` after the run settles (default: 5000 ms). */
105
+ statsTimeoutMs?: number;
106
+ }
107
+ /**
108
+ * Split `disallowed_tools` names into portable classes pi can act on, pi tools
109
+ * it can exclude by name, and the names it has no tool for.
110
+ */
111
+ export declare function translateDenyNames(names?: readonly string[]): {
112
+ classes: ToolClass[];
113
+ piTools: string[];
114
+ unknown: string[];
115
+ };
116
+ /** The name pi registers an MCP tool under (`src/extensions/mcp/tools.ts` createMcpToolName). */
117
+ export declare function piMcpToolName(server: string, tool: string, isTaken?: (name: string) => boolean): string;
118
+ /** pi registered names of the bridge's tools, and the way back to the sweny names. */
119
+ export declare function bridgeToolNames(tools: readonly Tool[]): {
120
+ names: string[];
121
+ back: Map<string, string>;
122
+ };
123
+ /**
124
+ * `mcp.json` entries for the external servers. Returns what pi cannot load
125
+ * (reported in `degraded`). Values using pi's `!command` or `${VAR}` syntax are
126
+ * refused, so a sweny config value can never run a command inside pi.
127
+ */
128
+ export declare function piMcpServers(servers: Record<string, McpServerConfig>): {
129
+ entries: Record<string, Record<string, unknown>>;
130
+ unsupported: string[];
131
+ };
132
+ /**
133
+ * `get_session_stats` data onto NodeUsage. pi reports 0 for what a provider did
134
+ * not report (and for a model it has no price for), so only positive values are
135
+ * kept: absent stays absent, never a guessed 0 or a fake free run.
136
+ */
137
+ export declare function mapStats(data: unknown): NodeUsage | undefined;
138
+ export declare class PiHarness implements AgentHarness {
139
+ readonly id: "pi";
140
+ readonly capabilities: HarnessCapabilities;
141
+ /**
142
+ * Empty on purpose: judges use the model pi would use anyway. Leaving this
143
+ * undefined would fall back to the Claude judge model, which pi may not have.
144
+ */
145
+ readonly defaultJudgeModel = "";
146
+ readonly logger: Logger;
147
+ private model;
148
+ private maxTurns;
149
+ private cwd;
150
+ private defaultContext;
151
+ private mcpServers;
152
+ private defaultMcpServers;
153
+ private envPassthrough;
154
+ private envScope;
155
+ private sandboxMode;
156
+ private sandboxWrapper;
157
+ private policyMode;
158
+ private piCommand;
159
+ private modelsFile;
160
+ private toolBridgeShim;
161
+ private abortGraceMs;
162
+ private killGraceMs;
163
+ private statsTimeoutMs;
164
+ private egressWarned;
165
+ private preflightResult;
166
+ private version;
167
+ constructor(opts?: PiHarnessOptions);
168
+ info(): HarnessInfo;
169
+ /** `pi --version`, checked against {@link MIN_PI_VERSION}. Cached per instance. */
170
+ preflight(): Promise<{
171
+ ok: true;
172
+ version: string;
173
+ } | {
174
+ ok: false;
175
+ reason: string;
176
+ }>;
177
+ /** Env for the pi process: scoped like Claude Code's, with pi's provider credentials instead of Anthropic's. */
178
+ private buildEnv;
179
+ /** The node policy after `disallowed_tools` names are translated into pi classes. */
180
+ private compilePolicy;
181
+ /** argv shared by run() and complete(); the prompt travels as an RPC command, never in argv. */
182
+ private baseArgs;
183
+ /** Spawn pi in rpc mode, send the prompt, and read events until it settles and exits. */
184
+ private exec;
185
+ /** Write pi's scratch agent dir: generated `mcp.json` only (plus an operator-supplied `models.json`). */
186
+ private writeAgentDir;
187
+ /**
188
+ * Run a node. The gate runs first: in strict mode a node whose opinions pi
189
+ * cannot honor is refused before pi starts. pi has no native sandbox, so the
190
+ * whole process runs inside the host's process sandbox wrapper (#360 step 2)
191
+ * when the sandbox mode is not off; `SWENY_SANDBOX=strict` refuses without one.
192
+ */
193
+ run(req: HarnessRunRequest): Promise<HarnessRunResult>;
194
+ /** Map one rpc outcome onto a NodeResult with Claude Code's semantics. */
195
+ private toResult;
196
+ /**
197
+ * One completion: no tools (`--no-tools`), no MCP, no extensions, an empty
198
+ * scratch agent dir. Nothing can act, so no process wrapper is needed.
199
+ * null when it failed (callers fail closed).
200
+ */
201
+ complete(req: HarnessCompleteRequest): Promise<string | null>;
202
+ }