@posthog/wizard 2.40.0 → 2.41.0

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.
Files changed (80) hide show
  1. package/dist/{add-mcp-server-to-clients-Dtvqw5DO.js → add-mcp-server-to-clients-Bvv6Vh5y.js} +4 -4
  2. package/dist/{add-mcp-server-to-clients-Dtvqw5DO.js.map → add-mcp-server-to-clients-Bvv6Vh5y.js.map} +1 -1
  3. package/dist/{agent-interface-BTVAlvLW.js → agent-interface-CFeI8jzW.js} +11 -9
  4. package/dist/agent-interface-CFeI8jzW.js.map +1 -0
  5. package/dist/{agent-runner-CqNL9x2D.js → agent-runner-B0pWHl5j.js} +20 -19
  6. package/dist/agent-runner-B0pWHl5j.js.map +1 -0
  7. package/dist/{analytics-__4kRqZr.js → analytics-C1qxjbYZ.js} +2 -2
  8. package/dist/{analytics-__4kRqZr.js.map → analytics-C1qxjbYZ.js.map} +1 -1
  9. package/dist/analytics-_8xKpUAa.js +2 -0
  10. package/dist/{api-D0RqJPWO.js → api-CEMR_Aa6.js} +3 -3
  11. package/dist/{api-D0RqJPWO.js.map → api-CEMR_Aa6.js.map} +1 -1
  12. package/dist/bin.js +38 -38
  13. package/dist/{ci-install-SXJrlScZ.js → ci-install-BBZscv3z.js} +4 -4
  14. package/dist/{ci-install-SXJrlScZ.js.map → ci-install-BBZscv3z.js.map} +1 -1
  15. package/dist/{debug-D_mboDpg.js → debug-BMkCmZdo.js} +3 -3
  16. package/dist/{debug-D_mboDpg.js.map → debug-BMkCmZdo.js.map} +1 -1
  17. package/dist/{debug-dh-I1XvJ.js → debug-DBrsvcym.js} +1 -1
  18. package/dist/{environment-4zKc7kWr.js → environment-B7DmpnXD.js} +3 -3
  19. package/dist/{environment-4zKc7kWr.js.map → environment-B7DmpnXD.js.map} +1 -1
  20. package/dist/{file-utils-Cb6HLPhu.js → file-utils-BCxAu-vq.js} +2 -2
  21. package/dist/{file-utils-Cb6HLPhu.js.map → file-utils-BCxAu-vq.js.map} +1 -1
  22. package/dist/{headless-ui-CjdRKkTG.js → headless-ui-NFf-5XOW.js} +2 -2
  23. package/dist/{headless-ui-CjdRKkTG.js.map → headless-ui-NFf-5XOW.js.map} +1 -1
  24. package/dist/{host-resolution-Czw5O33G.js → host-resolution-UZELMVTQ.js} +5 -5
  25. package/dist/host-resolution-UZELMVTQ.js.map +1 -0
  26. package/dist/{interactive-C3Ty8H5M.js → interactive-CBrzEFly.js} +2 -2
  27. package/dist/{interactive-C3Ty8H5M.js.map → interactive-CBrzEFly.js.map} +1 -1
  28. package/dist/{mcp-B_utnKAq.js → mcp-C7dn0DIC.js} +2 -2
  29. package/dist/{mcp-B_utnKAq.js.map → mcp-C7dn0DIC.js.map} +1 -1
  30. package/dist/{mcp-prompt-streaming-BzFu8dP6.js → mcp-prompt-streaming-uXF2xskx.js} +6 -6
  31. package/dist/{mcp-prompt-streaming-BzFu8dP6.js.map → mcp-prompt-streaming-uXF2xskx.js.map} +1 -1
  32. package/dist/{non-interactive-B2a00UId.js → non-interactive-CDYGf19o.js} +2 -2
  33. package/dist/{non-interactive-B2a00UId.js.map → non-interactive-CDYGf19o.js.map} +1 -1
  34. package/dist/{package-manager-CRYusNaP.js → package-manager-BHAvpGav.js} +2 -2
  35. package/dist/{package-manager-CRYusNaP.js.map → package-manager-BHAvpGav.js.map} +1 -1
  36. package/dist/{playground-CVIGzLGh.js → playground-B8OhYzyc.js} +5 -5
  37. package/dist/{playground-CVIGzLGh.js.map → playground-B8OhYzyc.js.map} +1 -1
  38. package/dist/{posthog-integration-D2S9mCAv.js → posthog-integration-G839iLK6.js} +14 -14
  39. package/dist/{posthog-integration-D2S9mCAv.js.map → posthog-integration-G839iLK6.js.map} +1 -1
  40. package/dist/{provisioning-B1KoQFFK.js → provisioning-BOhuDX6i.js} +4 -4
  41. package/dist/{provisioning-B1KoQFFK.js.map → provisioning-BOhuDX6i.js.map} +1 -1
  42. package/dist/{registry-CgrvMaYt.js → registry-CC26CjWO.js} +4 -4
  43. package/dist/{registry-CgrvMaYt.js.map → registry-CC26CjWO.js.map} +1 -1
  44. package/dist/{security-DX5ey9g8.js → security-wlqqLk44.js} +101 -5
  45. package/dist/security-wlqqLk44.js.map +1 -0
  46. package/dist/{setup-utils-CUeGnxBk.js → setup-utils-NwB2Qj6N.js} +9 -9
  47. package/dist/{setup-utils-CUeGnxBk.js.map → setup-utils-NwB2Qj6N.js.map} +1 -1
  48. package/dist/{start-tui-BC3yp_wW.js → start-tui-DQndpyTU.js} +17 -17
  49. package/dist/{start-tui-BC3yp_wW.js.map → start-tui-DQndpyTU.js.map} +1 -1
  50. package/dist/{steps-5YGQK7la.js → steps-CC_Ge4sR.js} +7 -7
  51. package/dist/{steps-5YGQK7la.js.map → steps-CC_Ge4sR.js.map} +1 -1
  52. package/dist/{store-DQ0-oE_Y.js → store-1-pQpHlY.js} +4 -4
  53. package/dist/{store-DQ0-oE_Y.js.map → store-1-pQpHlY.js.map} +1 -1
  54. package/dist/{subagent-DkTSX2ew.js → subagent-FMDybA8X.js} +2 -2
  55. package/dist/{subagent-DkTSX2ew.js.map → subagent-FMDybA8X.js.map} +1 -1
  56. package/dist/{tasks-SbUmyQPS.js → tasks-BfDJpHgK.js} +2 -2
  57. package/dist/{tasks-SbUmyQPS.js.map → tasks-BfDJpHgK.js.map} +1 -1
  58. package/dist/{telemetry-_7NXwx7o.js → telemetry-CcysC4ji.js} +3 -3
  59. package/dist/{telemetry-_7NXwx7o.js.map → telemetry-CcysC4ji.js.map} +1 -1
  60. package/dist/{terminal-NhR2Seu_.js → terminal-CWpmzTgT.js} +11 -11
  61. package/dist/{terminal-NhR2Seu_.js.map → terminal-CWpmzTgT.js.map} +1 -1
  62. package/dist/{tools-WDPzlrXc.js → tools-BsOywKLp.js} +4 -4
  63. package/dist/{tools-WDPzlrXc.js.map → tools-BsOywKLp.js.map} +1 -1
  64. package/dist/{urls-DYIMfv29.js → urls-PfZ1TYOf.js} +2 -2
  65. package/dist/{urls-DYIMfv29.js.map → urls-PfZ1TYOf.js.map} +1 -1
  66. package/dist/{wizard-abort-CWaKUNMI.js → wizard-abort-56bo4Zhs.js} +3 -3
  67. package/dist/{wizard-abort-CWaKUNMI.js.map → wizard-abort-56bo4Zhs.js.map} +1 -1
  68. package/dist/{wizard-abort-DGM_sAA9.js → wizard-abort-7oUXxutu.js} +1 -1
  69. package/dist/{wizard-tools-CrpRaYkE.js → wizard-tools-BXd4PCO3.js} +3 -3
  70. package/dist/{wizard-tools-CrpRaYkE.js.map → wizard-tools-BXd4PCO3.js.map} +1 -1
  71. package/dist/{yara-hooks-BPQ3VE-l.js → yara-hooks-BZmYgr9V.js} +3 -3
  72. package/dist/{yara-hooks-BPQ3VE-l.js.map → yara-hooks-BZmYgr9V.js.map} +1 -1
  73. package/dist/yara-hooks-D0h7Vu7A.js +2 -0
  74. package/package.json +1 -1
  75. package/dist/agent-interface-BTVAlvLW.js.map +0 -1
  76. package/dist/agent-runner-CqNL9x2D.js.map +0 -1
  77. package/dist/analytics-D6RSYOPO.js +0 -2
  78. package/dist/host-resolution-Czw5O33G.js.map +0 -1
  79. package/dist/security-DX5ey9g8.js.map +0 -1
  80. package/dist/yara-hooks-yy8OLJ43.js +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-interface-CFeI8jzW.js","names":["fs","fs"],"sources":["../src/utils/custom-headers.ts","../src/lib/safe-tools.ts","../src/lib/agent/triage-provider.ts","../src/lib/agent/commandments.ts","../src/lib/agent/agent-phase.ts","../src/lib/agent/signals.ts","../src/lib/agent/output-signals.ts","../src/lib/agent/agent-env-isolation.ts","../src/lib/agent/claude-settings.ts","../src/lib/agent/stored-login.ts","../src/lib/agent/agent-interface.ts"],"sourcesContent":["import { POSTHOG_FLAG_HEADER_PREFIX } from '@lib/constants';\n\n/**\n * Builds a list of custom headers for ANTHROPIC_CUSTOM_HEADERS.\n */\nexport function createCustomHeaders(): {\n add(key: string, value: string): void;\n /** Add a feature flag for PostHog ($feature/<flagKey>: variant). */\n addFlag(flagKey: string, variant: string): void;\n encode(): string;\n} {\n const entries: Array<{ key: string; value: string }> = [];\n\n return {\n add(key: string, value: string): void {\n const name =\n key.startsWith('x-') || key.startsWith('X-') ? key : `X-${key}`;\n entries.push({ key: name, value });\n },\n\n addFlag(flagKey: string, variant: string): void {\n const headerName = POSTHOG_FLAG_HEADER_PREFIX + flagKey.toUpperCase();\n entries.push({ key: headerName, value: variant });\n },\n\n encode(): string {\n return entries.map(({ key, value }) => `${key}: ${value}`).join('\\n');\n },\n };\n}\n","export const LINTING_TOOLS: string[] = [\n // All (general purpose)\n 'codespell',\n 'cspell',\n 'git-diff-check',\n 'gitleaks',\n 'trufflehog',\n\n // Amazon States Language\n 'asl-validator',\n\n // Ansible\n 'ansible-lint',\n\n // Apex, Java\n 'pmd',\n\n // Astro, CSS, GraphQL, GritQL, HTML, JavaScript, JSON, JSONC, JSON5, JSX, TSX, Svelte, TypeScript, Vue\n 'biome',\n\n // AWS CloudFormation templates\n 'cfn-lint',\n 'cfnlint',\n\n // AWS CloudFormation, Azure ARM, Dockerfile, Helm, Kubernetes, Security, Terraform\n 'checkov',\n\n // AWS CloudFormation, Azure ARM, Dockerfile, Kubernetes, Secrets, Security, Terraform, Vulnerabilities\n 'trivy',\n\n // Azure Resource Manager (ARM)\n 'test-aztemplate',\n\n // Bash / Shell\n 'shellcheck',\n 'shfmt',\n\n // Bazel, Starlark\n 'buildifier',\n\n // C, C++\n 'cpplint',\n 'clang-format',\n 'clang-tidy',\n 'cmake-format',\n 'iwyu',\n 'pragma-once',\n\n // C#, Dotnet (.NET)\n 'dotnet-format',\n\n // CircleCI Config\n 'circleci',\n\n // Clojure\n 'clj-kondo',\n\n // CoffeeScript\n 'coffeelint',\n\n // Commit messages\n 'commitlint',\n\n // Copy/paste detection\n 'jscpd',\n\n // CSS, SCSS, Sass\n 'stylelint',\n\n // CSS, GraphQL, HTML, JavaScript, JSON, JSONC, JSON5, JSX, TSX, Markdown, TypeScript, Vue, YAML\n 'prettier',\n\n // Cue\n 'cue-fmt',\n\n // Dart\n 'dart',\n\n // Dockerfile / Docker\n 'hadolint',\n\n // Dotenv\n 'dotenv-linter',\n\n // EditorConfig\n 'editorconfig-checker',\n\n // GitHub Actions\n 'actionlint',\n 'zizmor',\n\n // Go\n 'gofmt',\n 'gofumpt',\n 'goimports',\n 'gokart',\n 'golangci-lint',\n 'golines',\n\n // Go, Java, JavaScript, JSON, Python, Ruby, TypeScript, YAML\n 'semgrep',\n\n // GoReleaser\n 'goreleaser',\n\n // GraphQL\n 'graphql-schema-linter',\n\n // Groovy\n 'npm-groovy-lint',\n\n // HAML\n 'haml-lint',\n\n // HTML\n 'htmlhint',\n\n // HTML Templates\n 'djlint',\n\n // Java\n 'checkstyle',\n 'google-java-format',\n\n // JavaScript, JSON, TypeScript\n 'eslint',\n\n // Next.js\n 'next lint',\n\n // JavaScript, JSON, Markdown, TypeScript\n 'deno',\n\n // JavaScript, TypeScript\n 'rome',\n\n // JSON, JSONC, JSON5\n 'eslint-plugin-jsonc',\n 'eslint-plugin-json',\n\n // JSX, TSX\n 'eslint-plugin-jsx-a11y',\n 'eslint-plugin-react',\n\n // Jupyter Notebook\n 'nbqa',\n\n // Kotlin\n 'detekt',\n 'ktlint',\n\n // Kubernetes\n 'kubeconform',\n 'kube-linter',\n\n // LaTeX\n 'chktex',\n\n // Lua\n 'luacheck',\n 'stylua',\n\n // Markdown\n 'markdownlint',\n 'markdownlint-cli2',\n 'markdown-link-check',\n 'markdown-table-prettify',\n 'remark-lint',\n\n // Natural language / Prose\n 'textlint',\n 'vale',\n\n // Nix\n 'nixpkgs-fmt',\n\n // OpenAPI\n 'spectral',\n\n // package.json\n 'sort-package-json',\n\n // Perl\n 'perlcritic',\n 'perltidy',\n\n // PHP\n 'php-cs-fixer',\n 'phpcs',\n 'phpstan',\n 'psalm',\n\n // PNG\n 'oxipng',\n\n // PowerShell\n 'psscriptanalyzer',\n\n // Prisma\n 'prisma',\n\n // Protocol Buffers (Protobuf)\n 'protolint',\n 'buf',\n\n // Python\n 'pylint',\n 'flake8',\n 'isort',\n 'ruff',\n 'black',\n 'autopep8',\n 'bandit',\n 'mypy',\n 'pyright',\n 'sourcery',\n 'yapf',\n\n // R\n 'lintr',\n\n // Rego\n 'opa',\n 'regal',\n\n // Ruby\n 'rubocop',\n 'brakeman',\n 'rufo',\n 'standardrb',\n\n // Rust\n 'clippy',\n 'rustfmt',\n\n // Scala\n 'scalafmt',\n\n // Security, Vulnerabilities\n 'osv-scanner',\n\n // Security, Terraform\n 'terrascan',\n 'tfsec',\n\n // Snakemake\n 'snakemake --lint',\n 'snakefmt',\n\n // SQL\n 'sqlfluff',\n 'sql-formatter',\n 'sqlfmt',\n 'squawk',\n\n // SVG\n 'svgo',\n\n // Swift\n 'stringslint',\n 'swiftformat',\n 'swiftlint',\n\n // Terraform\n 'tflint',\n 'terraform',\n 'tofu',\n\n // Terragrunt\n 'terragrunt',\n\n // Textproto\n 'txtpbfmt',\n\n // TOML\n 'taplo',\n\n // Vue\n 'eslint-plugin-vue',\n\n // XML\n 'xmllint',\n\n // YAML\n 'yamllint',\n] as const;\n\nexport type LintingTool = (typeof LINTING_TOOLS)[number];\n","/**\n * LLM provider for warlock security-scan triage.\n *\n * Warlock's triageMatches() takes a consumer-supplied `(prompt) => Promise<string>`\n * to run a second pass that filters false positives out of YARA matches. This\n * builds that provider on top of the wizard's existing PostHog LLM gateway auth\n * — the same ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN that initializeAgent()\n * sets for the agent SDK. The gateway speaks the standard Anthropic Messages API,\n * so we POST to it directly with axios (the plain @anthropic-ai/sdk isn't a dep).\n */\n\nimport axios from 'axios';\nimport { logToFile } from '@utils/debug';\nimport type { LLMProvider } from '@posthog/warlock';\n\n// Haiku 4.5: triage is a fast, narrow \"true_positive | false_positive\" verdict\n// on already-flagged matches — Haiku's the right tier (cheap, fast, plenty\n// capable for boolean classification). Do NOT swap to Sonnet without reason;\n// the cost/latency difference matters on every flagged scan. temperature 0\n// keeps verdicts deterministic across identical inputs.\nconst TRIAGE_MODEL = 'claude-haiku-4-5';\nconst TRIAGE_MAX_TOKENS = 16_384;\n// Shorter than the hook timeout so a hung triage fails *inside* the hook's\n// try/catch (→ fail-closed) rather than tripping the SDK hook timeout.\nconst TRIAGE_TIMEOUT_MS = 20_000;\n\ninterface AnthropicTextBlock {\n type: string;\n text?: string;\n}\n\nexport interface TriageGatewayAuth {\n baseURL: string;\n authToken: string;\n}\n\n/**\n * Build the triage LLM provider. Auth comes from the explicit `auth` param\n * when given (the pi harness — it auths the gateway programmatically and never\n * sets the env vars), otherwise from the gateway auth on process.env (set by\n * initializeAgent on the anthropic path). Returns undefined if neither is\n * configured — callers then skip triage and fail closed (act on every flagged\n * match), so a missing key never silently disables the scanner.\n *\n * The triage model is independent of the agent's model: it is a classifier\n * judging scan matches, and the gateway routes it by model id regardless of\n * which model runs the coding agent.\n */\nexport function createTriageLLMProvider(\n auth?: TriageGatewayAuth,\n): LLMProvider | undefined {\n const baseURL = auth?.baseURL ?? process.env.ANTHROPIC_BASE_URL;\n const authToken = auth?.authToken ?? process.env.ANTHROPIC_AUTH_TOKEN;\n\n if (!baseURL || !authToken) {\n logToFile(\n '[YARA] triage provider unavailable (no gateway auth) — flagged scans will fail closed',\n );\n return undefined;\n }\n\n logToFile(`[YARA] triage provider ready (model: ${TRIAGE_MODEL})`);\n\n return async (prompt: string): Promise<string> => {\n const res = await axios.post(\n `${baseURL}/v1/messages`,\n {\n model: TRIAGE_MODEL,\n max_tokens: TRIAGE_MAX_TOKENS,\n temperature: 0,\n messages: [{ role: 'user', content: prompt }],\n },\n {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'anthropic-version': '2023-06-01',\n 'content-type': 'application/json',\n },\n timeout: TRIAGE_TIMEOUT_MS,\n },\n );\n\n const data = res.data as { content?: AnthropicTextBlock[] } | undefined;\n const content = data?.content;\n if (Array.isArray(content)) {\n return content\n .filter(\n (b: AnthropicTextBlock) =>\n b?.type === 'text' && typeof b.text === 'string',\n )\n .map((b: AnthropicTextBlock) => b.text)\n .join('');\n }\n return '';\n };\n}\n","/**\n * Wizard-wide commandments that are always appended as a system prompt.\n *\n * Keep this as a simple string so it can be inlined into the compiled bundle\n * without extra files, copying, or runtime I/O.\n */\nconst WIZARD_COMMANDMENTS = [\n 'Never hallucinate a PostHog project token, host, or any other secret. Always use the real values that have been configured for this project (for example via environment variables).',\n\n 'Never write API keys, access tokens, or other secrets directly into source code. Always reference environment variables instead, and rely on the wizard-tools MCP server (check_env_keys / set_env_values) to create or update .env files.',\n\n 'Always use the detect_package_manager tool from the wizard-tools MCP server to determine the package manager. Do not guess based on lockfiles or hard-code npm, yarn, pnpm, bun, pip, etc.',\n\n 'Before writing to any file, you MUST read that exact file immediately beforehand using the Read tool, even if you have already read it earlier in the run. This avoids tool failures and stale edits.',\n\n 'Treat feature flags, custom properties, and event names as part of an analytics contract. Prefer reusing existing names and patterns in the project. When you must introduce new ones, make them clear, descriptive, and consistent with existing conventions, and avoid scattering the same flag or property across many unrelated callsites.',\n\n 'Keep PostHog data capture at its defaults unless the user explicitly asks otherwise. Do not disable autocapture, do not disable session recording, and never set opt_out_capturing (or opted_out) to true in the SDK init config — these turn off data the user almost always wants. Note: posthog.opt_out_capturing() called at runtime for GDPR consent flows is legitimate; the rule is about the init configuration.',\n\n \"Never put personally identifiable information — emails, full or partial names, phone numbers, physical or IP addresses, or other user-entered PII — in capture() event properties. PII belongs on the PERSON: send it via identify()/$set (or the SDK's person-property API), and capture events with a stable distinct id derived from the authenticated user or session, never a raw email or name. This holds for every SDK, client and server.\",\n\n 'Prefer minimal, targeted edits that achieve the requested behavior while preserving existing structure and style. Avoid large refactors, broad reformatting, or unrelated changes unless explicitly requested.',\n\n 'When you change a file that already exists, edit it in place. If an edit fails because the text you are matching is not unique, add just enough surrounding context to make it unique, or apply the change to each occurrence — never fall back to rewriting the whole file. Write a file in full only when you are creating it, never to modify one that already exists.',\n\n 'Before you overwrite or delete anything that already exists, look at what you would remove. This integration only adds instrumentation, so keep every part of the project unrelated to PostHog exactly as it was — never drop existing code, comments, or markup to make room for a change.',\n\n 'Do not spawn subagents unless explicitly instructed to do so.',\n\n 'Create tasks as soon as you understand the work you are going to carry out. Break the list into distinct stages of work that the user can follow through. Create all tasks in a single tool call, in the order you will be performing them. Drive the work with TaskUpdate: status in_progress when you begin a task, completed when done.',\n\n 'Keep task titles broad and stage-oriented — describe the purpose or area of work, not the specific files, paths, or symbols involved. Do not name individual files, modules, or directories inside task titles, and do not include illustrative examples within a task title.',\n\n 'Do not assume \"PostHog provider\" or \"PostHogProvider\" in task labels, status messages, or code. Only use a provider if the skill workflow explicitly requires one for the specific framework.',\n\n 'If you see a [YARA WARNING] or [YARA VIOLATION] message, handle it silently. Fix the issue (revert the write, avoid the flagged pattern) without mentioning security warnings or YARA to the user. These are internal guardrails, not user-facing alerts. Never show scary security language to the user.',\n\n // wizard_ask Path A — translate prose question lists into a single tool call.\n // The skill prose is intentionally underspecified; this commandment carries\n // most of the discipline. Tightening: prefer adding rules here over editing\n // individual skill markdown.\n [\n 'When a skill instructs you to gather information from the user, use the `wizard_ask` MCP tool from the wizard-tools server. Never inline questions in your text output expecting a reply — the user has no way to answer text.',\n 'When a skill provides a numbered or bulleted list of questions, translate the entire list into a single `wizard_ask` tool call:',\n ' - One tool call per skill step. Batch every question from that step into the `questions` array — never split into multiple calls.',\n ' - Infer `kind` from the question phrasing: comma-separated alternatives (\"React, Vue, or vanilla JS?\") → `single`; phrasing like \"all that apply\" or \"any of\" → `multi`; everything else → `text`.',\n ' - For `single` and `multi`, extract the alternatives from the prose into `options` as `{ label, value }` pairs. Use the human phrase as `label` and a lowercase-hyphenated form as `value` (e.g., `label: \"Vanilla JS\"`, `value: \"vanilla-js\"`).',\n ' - Use a kebab-case slug of the question label as `id` (e.g., \"Tech stack\" → `tech-stack`, \"Show frequency\" → `show-frequency`).',\n ' - Do not invent fields the schema does not define (no `source`, `category`, `priority`, etc.) — the tool rejects unknown fields and the wizard already knows which skill is running.',\n 'After `wizard_ask` returns, use the answers directly — do not re-ask in text or call `wizard_ask` again for the same fields.',\n ].join('\\n'),\n].join('\\n');\n\nexport function getWizardCommandments(): string {\n return WIZARD_COMMANDMENTS;\n}\n","/**\n * Agent run phases — what the wizard agent is currently doing, derived from\n * the tool it's running. The Visualizer reads `store.currentStage.stage`\n * and renders the matching ASCII visual.\n */\n\nexport enum AgentPhase {\n CodebaseScan = 'codebase-scan',\n SkillInstall = 'skill-install',\n DepInstall = 'dep-install',\n CodeEdits = 'code-edits',\n EnvSetup = 'env-setup',\n Dashboards = 'dashboards',\n}\n\n/** Maps a Claude SDK tool name to the phase that tool implies. Returns null\n * when the tool doesn't drive the visualizer (Task*, TodoWrite, etc.). */\nexport function classifyToolToStage(toolName: string): AgentPhase | null {\n if (\n toolName.includes('install_skill') ||\n toolName.includes('load_skill_menu')\n ) {\n return AgentPhase.SkillInstall;\n }\n if (\n toolName.includes('set_env_values') ||\n toolName.includes('check_env_keys')\n ) {\n return AgentPhase.EnvSetup;\n }\n if (toolName.includes('mcp__posthog')) {\n return AgentPhase.Dashboards;\n }\n if (toolName === 'Bash') return AgentPhase.DepInstall;\n if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {\n return AgentPhase.CodeEdits;\n }\n if (toolName === 'Read' || toolName === 'Glob' || toolName === 'Grep') {\n return AgentPhase.CodebaseScan;\n }\n return null;\n}\n","/**\n * Agent signal vocabulary — the marker strings the agent emits and the error\n * taxonomy the runner returns. Kept as a dependency-free leaf module so both\n * `agent-interface.ts` and `output-signals.ts` can import it without a cycle.\n */\n\nexport const AgentSignals = {\n /** Signal emitted when the agent reports progress to the user */\n STATUS: '[STATUS]',\n /** Signal emitted when the agent cannot access the PostHog MCP server */\n ERROR_MCP_MISSING: '[ERROR-MCP-MISSING]',\n /** Signal emitted when the agent cannot access the setup resource */\n ERROR_RESOURCE_MISSING: '[ERROR-RESOURCE-MISSING]',\n /**\n * Signal emitted when `install_skill` failed and the agent is continuing\n * the integration WITHOUT the skill (best-effort from its own knowledge).\n * Format: \"[SKILL-INSTALL-FAILED] <skill id — reason>\". Non-fatal by\n * design: a freestyled integration beats an outright failure, but the run\n * must be measurable — the runner captures it as an analytics event.\n */\n SKILL_INSTALL_FAILED: '[SKILL-INSTALL-FAILED]',\n /**\n * Signal emitted when the agent cannot complete the program and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Programs can declare an onAbort handler to render a custom screen.\n */\n ABORT: '[ABORT]',\n /** Signal emitted when the agent provides a remark about its run */\n WIZARD_REMARK: '[WIZARD-REMARK]',\n /** Signal prefix for benchmark logging */\n BENCHMARK: '[BENCHMARK]',\n /**\n * Signal emitted when the agent has created a PostHog dashboard for the\n * user. Format: `[DASHBOARD_URL] <full https url>`. The URL is captured\n * onto `session.dashboardUrl` and surfaced by programs in their outro.\n */\n DASHBOARD_URL: '[DASHBOARD_URL]',\n /**\n * Signal emitted when the agent has uploaded a report to a PostHog\n * notebook. Format: `[NOTEBOOK_URL] <full https url>`. The URL is captured\n * onto `session.notebookUrl` and surfaced by programs in their outro.\n */\n NOTEBOOK_URL: '[NOTEBOOK_URL]',\n} as const;\n\nexport type AgentSignal = (typeof AgentSignals)[keyof typeof AgentSignals];\n\n/**\n * End-of-run reflection ask, shared by both harnesses so remarks are\n * comparable. The format clause comes FIRST and nothing trails the marker —\n * literal models (gpt-5-mini) echo whatever text follows it. The parser also\n * drops remarks that quote this instruction (see AgentOutputSignals.remark).\n */\nexport const REMARK_INSTRUCTION = `Reply with a single line that starts with ${AgentSignals.WIZARD_REMARK} and no other lines. In that line, state briefly what information or guidance would have been useful to have in the integration prompt or documentation for this run — specifically anything that would have prevented tool failures, erroneous edits, or other wasted turns.`;\n\n/**\n * Error types that can be returned from agent execution.\n * These correspond to the error signals that the agent emits.\n */\nexport enum AgentErrorType {\n /** Agent could not access the PostHog MCP server */\n MCP_MISSING = 'WIZARD_MCP_MISSING',\n /** Agent could not access the setup resource */\n RESOURCE_MISSING = 'WIZARD_RESOURCE_MISSING',\n /** API rate limit exceeded */\n RATE_LIMIT = 'WIZARD_RATE_LIMIT',\n /** Generic API error */\n API_ERROR = 'WIZARD_API_ERROR',\n /** YARA scanner detected a security violation */\n YARA_VIOLATION = 'WIZARD_YARA_VIOLATION',\n /** Agent intentionally aborted the program (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n}\n","/**\n * Parses the signal-bearing lines out of agent output and discards the rest.\n *\n * The agent and SDK communicate non-content events (auth/API errors, missing\n * MCP/resource, the end-of-run remark) by emitting marker strings inside\n * their prose. `AgentOutputSignals` keeps only the lines that carry such a\n * marker, so the buffer stays bounded regardless of run length.\n *\n * YARA violations are deliberately NOT detected here: scanning the agent's\n * prose for \"[YARA ...]\" markers false-positives when the agent merely\n * *mentions* a non-terminal block. Terminal YARA outcomes flow through the\n * hooks' onTerminate callback (`yaraViolationReason` in runAgent) instead.\n */\n\nimport { AgentSignals, REMARK_INSTRUCTION } from './signals';\n\n/**\n * Single source of truth for the substrings runAgent scans agent output for.\n * `push()` retains a line iff it contains one of these values; every query\n * reads the same table, so retention and consumers cannot drift. API-error\n * status codes are not separate entries — `API_ERROR` is the one needle and\n * the code is a parameter to `hasApiErrorStatus`.\n */\nconst OUTPUT_SIGNALS = {\n API_ERROR: 'API Error:',\n MCP_MISSING: AgentSignals.ERROR_MCP_MISSING,\n RESOURCE_MISSING: AgentSignals.ERROR_RESOURCE_MISSING,\n SKILL_INSTALL_FAILED: AgentSignals.SKILL_INSTALL_FAILED,\n WIZARD_REMARK: AgentSignals.WIZARD_REMARK,\n} as const;\n\ntype OutputSignal = keyof typeof OUTPUT_SIGNALS;\nconst SIGNAL_NEEDLES = Object.values(OUTPUT_SIGNALS);\n\nexport class AgentOutputSignals {\n private readonly lines: string[] = [];\n private apiKeySourceValue: string | undefined;\n\n /** Parse step: keep the line only if it carries a known signal; drop prose. */\n push(text: string): void {\n if (SIGNAL_NEEDLES.some((n) => text.includes(n))) this.lines.push(text);\n }\n\n /**\n * Record the SDK's `apiKeySource` from its `init` message (e.g.\n * `\"/login managed key\"`, `\"ANTHROPIC_API_KEY\"`). Used to triage a 401:\n * a managed-login source means the SDK authenticated with a stored Claude\n * login rather than the gateway token the wizard injected.\n */\n recordApiKeySource(source: string | undefined): void {\n if (source) this.apiKeySourceValue = source;\n }\n\n get apiKeySource(): string | undefined {\n return this.apiKeySourceValue;\n }\n\n /**\n * True when the SDK authed from a stored `/login` credential (the\n * \"managed key\") instead of an explicit `ANTHROPIC_API_KEY` — the signature\n * of conflicting Anthropic credentials behind a gateway 401.\n */\n usedManagedLogin(): boolean {\n return /login|managed key/i.test(this.apiKeySourceValue ?? '');\n }\n\n private get text(): string {\n return this.lines.join('\\n');\n }\n\n /** True if any retained line contains the given signal's marker. */\n has(signal: OutputSignal): boolean {\n return this.text.includes(OUTPUT_SIGNALS[signal]);\n }\n\n hasApiError(): boolean {\n return this.has('API_ERROR');\n }\n\n /** True for a specific HTTP status, e.g. 401 (auth) or 429 (rate limit). */\n hasApiErrorStatus(code: number): boolean {\n return this.text.includes(`${OUTPUT_SIGNALS.API_ERROR} ${code}`);\n }\n\n /** Joined `API Error: …` lines for the user-facing message, or undefined. */\n apiErrorMessage(): string | undefined {\n const m = this.text.match(\n new RegExp(`${OUTPUT_SIGNALS.API_ERROR} [^\\\\n]+`, 'g'),\n );\n return m ? m.join('\\n') : undefined;\n }\n\n /**\n * Text after the `[SKILL-INSTALL-FAILED]` marker (skill id + reason),\n * trimmed — or undefined when the skill installed fine. A marker with no\n * trailing text still reports as '' so the failure is never lost.\n */\n skillInstallFailure(): string | undefined {\n const marker = OUTPUT_SIGNALS.SKILL_INSTALL_FAILED;\n for (const line of this.lines) {\n const idx = line.indexOf(marker);\n if (idx !== -1) return line.slice(idx + marker.length).trim();\n }\n return undefined;\n }\n\n /** Text after the single `[WIZARD-REMARK]` marker, trimmed, or undefined. */\n remark(): string | undefined {\n const re = new RegExp(\n `${OUTPUT_SIGNALS.WIZARD_REMARK.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)(?:\\\\n|$)`,\n 'gs',\n );\n // Return the first marker match whose text isn't an echo of the ask.\n let match: RegExpExecArray | null;\n while ((match = re.exec(this.text)) !== null) {\n const text = match[1]?.trim();\n if (text && !REMARK_INSTRUCTION.includes(text)) return text;\n }\n return undefined;\n }\n}\n","/**\n * Credential isolation for the Claude Agent SDK subprocess.\n *\n * The SDK doesn't authenticate in-process — `query()` extracts and spawns a\n * native `claude` binary that resolves model routing and credentials from its\n * own environment. That binary honors a large, ever-growing surface of\n * `ANTHROPIC_*` / `CLAUDE_CODE_*` env vars: provider-activation flags\n * (`CLAUDE_CODE_USE_BEDROCK` / `_VERTEX` / `_FOUNDRY` / `_MANTLE` /\n * `_ANTHROPIC_AWS`), alternate base URLs, file-descriptor / indirection token\n * sources, inline identity / OAuth / bearer tokens, and host-auth-deferral\n * flags. ANY of them, inherited from the user's shell, can route the agent OFF\n * the PostHog LLM gateway — billing the user's own Anthropic / Bedrock / Vertex\n * account (\"wrong credits\") or redirecting the binary to an arbitrary endpoint\n * and leaking the gateway token.\n *\n * Rather than chase each new knob by name — a denylist the binary outpaces every\n * release — we DROP THE ENTIRE PROVIDER NAMESPACE from the subprocess env and\n * re-inject the wizard's own gateway routing explicitly at the spawn site (see\n * `agent-interface.ts` / `mcp-prompt-streaming.ts`). The spawned binary then\n * runs with exactly the `ANTHROPIC_*` / `CLAUDE_CODE_*` values the wizard sets\n * and nothing else — no user shell/env value can leak in or outrank them.\n *\n * Generic system / build env (`PATH`, `HOME`, proxies, toolchain, `AWS_*` /\n * `GOOGLE_*`) is deliberately preserved: the agent's install / build commands\n * need it, and it's inert for model routing once every provider-activation flag\n * is stripped.\n */\n\n/**\n * The model-provider env namespace. Every routing / credential knob the binary\n * honors lives under one of these prefixes, so stripping the whole namespace is\n * a complete, maintenance-free replacement for an enumerated denylist.\n */\nconst PROVIDER_ENV_NAMESPACE = /^(ANTHROPIC_|CLAUDE_CODE_)/;\n\n/**\n * Off-namespace credential that the binary can use without a provider-activation\n * flag, so the namespace rule alone wouldn't catch it. (Bedrock ignores it once\n * `CLAUDE_CODE_USE_BEDROCK` is gone, but strip it anyway — defense in depth.)\n */\nconst BLOCKED_OFF_NAMESPACE_KEYS = new Set(['AWS_BEARER_TOKEN_BEDROCK']);\n\n/**\n * Credential / routing knobs that, set in an on-disk Claude **settings** `env`\n * block, redirect the binary. A settings file is applied by the binary itself,\n * so the subprocess-env strip below cannot remove it — `claude-settings.ts`\n * detects these in settings files and backs up / fails closed instead. Kept as\n * a curated list there (not the broad namespace rule) so a benign\n * `CLAUDE_CODE_*` toggle in an org-managed settings file doesn't needlessly\n * fail the run closed. The subprocess env needs no such list — it strips the\n * whole namespace (`sanitizeAgentSubprocessEnv`).\n *\n * Keep `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN`\n * OUT of this list — `claude-settings.ts` adds those (the gateway routing the\n * wizard sets) to its own detection set separately.\n */\nexport const BLOCKED_AGENT_ENV_KEYS = [\n // Direct API key — outranks the gateway AUTH_TOKEN if present.\n 'ANTHROPIC_API_KEY',\n // Provider activation — route to a non-gateway backend (the user's cloud\n // bill). The binary OR-s all five together at the same precedence tier.\n 'CLAUDE_CODE_USE_BEDROCK',\n 'CLAUDE_CODE_USE_VERTEX',\n 'CLAUDE_CODE_USE_FOUNDRY',\n 'CLAUDE_CODE_USE_MANTLE',\n 'CLAUDE_CODE_USE_ANTHROPIC_AWS',\n // Alternate base URLs — redirect the binary off the gateway.\n 'ANTHROPIC_BEDROCK_BASE_URL',\n 'ANTHROPIC_AWS_BASE_URL',\n 'ANTHROPIC_VERTEX_BASE_URL',\n 'ANTHROPIC_FOUNDRY_BASE_URL',\n 'ANTHROPIC_BEDROCK_MANTLE_BASE_URL',\n 'CLAUDE_CODE_API_BASE_URL',\n // Third-party / alternate provider keys.\n 'ANTHROPIC_AWS_API_KEY',\n 'ANTHROPIC_FOUNDRY_API_KEY',\n 'AWS_BEARER_TOKEN_BEDROCK',\n // Vertex project selector.\n 'ANTHROPIC_VERTEX_PROJECT_ID',\n // Profile / environment credential selectors.\n 'ANTHROPIC_PROFILE',\n 'ANTHROPIC_ENVIRONMENT_KEY',\n // Workload-identity / federation auth (inline token + selectors).\n 'ANTHROPIC_IDENTITY_TOKEN',\n 'ANTHROPIC_FEDERATION_RULE_ID',\n 'ANTHROPIC_SERVICE_ACCOUNT_ID',\n // File-descriptor / file-path token sources.\n 'CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR',\n 'CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR',\n 'CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR',\n 'ANTHROPIC_IDENTITY_TOKEN_FILE',\n // Session / client credential injection.\n 'CLAUDE_CODE_SESSION_ACCESS_TOKEN',\n 'CLAUDE_CODE_CLIENT_KEY',\n // OAuth refresh + alternate bearer tokens.\n 'CLAUDE_CODE_OAUTH_REFRESH_TOKEN',\n 'CLAUDE_CODE_HFI_BEARER_TOKEN',\n // Indirection — names another env var to read auth from.\n 'CLAUDE_CODE_HOST_AUTH_ENV_VAR',\n // Host-managed provider / auth deferral.\n 'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST',\n 'CLAUDE_CODE_SDK_HAS_HOST_AUTH_REFRESH',\n 'CLAUDE_CODE_SDK_HAS_OAUTH_REFRESH',\n // apiKeyHelper cache control.\n 'CLAUDE_CODE_API_KEY_HELPER_TTL_MS',\n] as const;\n\n/**\n * Pattern-matched settings-file knobs (companion to {@link BLOCKED_AGENT_ENV_KEYS}):\n * - `CLAUDE_CODE_SKIP_*_AUTH` — skip-auth flags for every alt provider.\n * - `ANTHROPIC_*_BASE_URL` — alternate base URLs (NOT bare `ANTHROPIC_BASE_URL`,\n * which is the gateway routing the wizard sets).\n */\nexport const BLOCKED_AGENT_ENV_PATTERNS: readonly RegExp[] = [\n /^CLAUDE_CODE_SKIP_.+_AUTH$/,\n /^ANTHROPIC_.+_BASE_URL$/,\n];\n\n/**\n * True if `key` must NOT be inherited by the agent subprocess: anything in the\n * provider namespace (the wizard re-injects its own values at the spawn site),\n * plus the lone off-namespace credential.\n */\nexport function isBlockedAgentEnvKey(key: string): boolean {\n return (\n PROVIDER_ENV_NAMESPACE.test(key) || BLOCKED_OFF_NAMESPACE_KEYS.has(key)\n );\n}\n\n/**\n * Return a copy of `env` with the entire model-provider namespace removed, so\n * the spawned `claude` binary inherits no `ANTHROPIC_*` / `CLAUDE_CODE_*` value\n * from the user's shell. The wizard's own gateway routing is injected back\n * explicitly at the spawn site, so the binary can authenticate only through it.\n * Removed keys are absent from the result (Node's spawn omits them), not set to\n * `undefined`.\n */\nexport function sanitizeAgentSubprocessEnv(\n env: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const sanitized: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(env)) {\n if (isBlockedAgentEnvKey(key)) continue;\n sanitized[key] = value;\n }\n return sanitized;\n}\n","/**\n * Claude Code settings handling: conflict detection, backup/restore, and\n * orphan recovery.\n *\n * Lives apart from agent-interface so bin.ts can run orphan recovery at\n * process start without dragging in the agent stack (wizard-tools, yara\n * hooks, the SDK loader).\n */\n\nimport path from 'path';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport { analytics } from '@utils/analytics';\nimport { registerCleanup } from '@utils/wizard-abort';\nimport {\n BLOCKED_AGENT_ENV_KEYS,\n BLOCKED_AGENT_ENV_PATTERNS,\n} from './agent-env-isolation';\n\n/** The file names Claude Code accepts for project settings, in priority order. */\nconst SETTINGS_FILENAMES = ['settings.json', 'settings'] as const;\n/** Suffix for the wizard's pristine-copy backup of a settings file. */\nconst BACKUP_SUFFIX = '.wizard-backup';\n\n/**\n * The candidate settings files under `<workingDirectory>/.claude` and their\n * backup paths. Centralizes the file-name list + backup suffix that backup,\n * restore, and orphan-recovery all walk.\n */\nfunction settingsBackupTargets(\n workingDirectory: string,\n): { claudeDir: string; original: string; backup: string }[] {\n const claudeDir = path.join(workingDirectory, '.claude');\n return SETTINGS_FILENAMES.map((name) => {\n const original = path.join(claudeDir, name);\n return { claudeDir, original, backup: original + BACKUP_SUFFIX };\n });\n}\n\n// A settings.json `env` block is applied by the native binary itself, so the\n// subprocess-env sanitizer can't strip it — the only defense is to detect the\n// file and back it up / remove it. Flag the gateway-routing vars (a project\n// pinning these overrides the wizard) plus every credential/routing knob the\n// subprocess sanitizer strips (a settings env block can re-introduce any).\nconst BLOCKING_ENV_KEYS = [\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_AUTH_TOKEN',\n 'CLAUDE_CODE_OAUTH_TOKEN',\n ...BLOCKED_AGENT_ENV_KEYS,\n];\nconst BLOCKING_SETTINGS_KEYS = ['apiKeyHelper'];\n\n/** Where a settings conflict was found. */\nexport type SettingsConflictSource =\n | 'project'\n | 'project-local'\n | 'user'\n | 'managed';\n\n/** A single settings conflict detected during startup. */\nexport interface SettingsConflict {\n /** Where the conflict was found. */\n source: SettingsConflictSource;\n /** Absolute path of the file holding the override. */\n path: string;\n /** The blocking keys found (e.g. 'ANTHROPIC_BASE_URL', 'apiKeyHelper'). */\n keys: string[];\n /**\n * Whether the wizard can back up / remove this file. Only the project\n * `settings.json` is writable: managed settings are root-owned, and the\n * user's global config and gitignored `*.local.json` are left untouched so\n * we never mutate config outside the project we were pointed at.\n */\n writable: boolean;\n}\n\n/**\n * Check a single settings file for blocking env keys and top-level settings keys.\n * Returns matched key names, or an empty array if none found.\n */\nfunction checkSettingsFile(filePath: string): string[] {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n const matched: string[] = [];\n\n // Check env block for blocking env keys (exact names + provider-variant\n // patterns like ANTHROPIC_*_BASE_URL / CLAUDE_CODE_SKIP_*_AUTH).\n const envBlock = parsed?.env;\n if (envBlock && typeof envBlock === 'object') {\n matched.push(...BLOCKING_ENV_KEYS.filter((key) => key in envBlock));\n matched.push(\n ...Object.keys(envBlock).filter((key) =>\n BLOCKED_AGENT_ENV_PATTERNS.some((pattern) => pattern.test(key)),\n ),\n );\n }\n\n // Check top-level settings keys\n matched.push(\n ...BLOCKING_SETTINGS_KEYS.filter(\n (key) => key in parsed && parsed[key] !== '' && parsed[key] != null,\n ),\n );\n\n // A key can match both the exact list and a pattern — dedupe.\n return [...new Set(matched)];\n } catch {\n // File doesn't exist or isn't valid JSON — skip\n return [];\n }\n}\n\n/**\n * Org-managed (IT/MDM-deployed) settings path, per platform. Readable by all\n * users, writable only by root/admin. Claude Code always reads this file\n * regardless of `settingSources`, so an `apiKeyHelper` / `env` override here\n * redirects the agent off the gateway and the wizard cannot remove it — it\n * must fail closed. Checking only the macOS path (the prior behavior) let a\n * Linux/Windows managed override slip past the fail-closed gate undetected.\n */\nexport function managedSettingsPath(\n platform: NodeJS.Platform = process.platform,\n): string {\n switch (platform) {\n case 'darwin':\n return '/Library/Application Support/ClaudeCode/managed-settings.json';\n case 'win32':\n return path.join(\n process.env.ProgramData || 'C:\\\\ProgramData',\n 'ClaudeCode',\n 'managed-settings.json',\n );\n default:\n // Linux and other POSIX platforms.\n return '/etc/claude-code/managed-settings.json';\n }\n}\n\n/**\n * Check every settings file Claude Code reads for blocking keys that conflict\n * with the wizard's gateway auth: org-managed, the user's global config, the\n * project config, and the gitignored project-local override. Each is a place\n * an `apiKeyHelper` or `ANTHROPIC_*` override commonly lives, and any of them\n * can outrank the env the wizard sets and make every agent call 401.\n */\nexport function checkAllSettingsConflicts(\n workingDirectory: string,\n homeDir: string = os.homedir(),\n platform: NodeJS.Platform = process.platform,\n): SettingsConflict[] {\n const conflicts: SettingsConflict[] = [];\n const home = homeDir;\n\n const sources: {\n source: SettingsConflictSource;\n paths: string[];\n writable: boolean;\n }[] = [\n {\n source: 'managed',\n paths: [managedSettingsPath(platform)],\n writable: false,\n },\n {\n source: 'user',\n paths: [\n path.join(home, '.claude', 'settings.json'),\n path.join(home, '.claude', 'settings.local.json'),\n ],\n writable: false,\n },\n {\n source: 'project',\n paths: SETTINGS_FILENAMES.map((name) =>\n path.join(workingDirectory, '.claude', name),\n ),\n writable: true,\n },\n {\n source: 'project-local',\n paths: [path.join(workingDirectory, '.claude', 'settings.local.json')],\n writable: false,\n },\n ];\n\n for (const { source, paths, writable } of sources) {\n for (const filePath of paths) {\n const keys = checkSettingsFile(filePath);\n if (keys.length > 0) {\n conflicts.push({ source, path: filePath, keys, writable });\n break; // Only one conflict per source (settings.json vs settings fallback)\n }\n }\n }\n\n return conflicts;\n}\n\n/** A settings conflict sorted into how the wizard should respond to it. */\nexport interface ClassifiedSettingsConflicts {\n /**\n * Writable project `.claude/settings.json` — the SDK reads it under\n * `settingSources: ['project']`, but the wizard can back it up and remove it\n * (restored at outro). Neutralize automatically, no prompt.\n */\n autoFix: SettingsConflict[];\n /**\n * Org-managed settings — the SDK reads them on every run regardless of\n * `settingSources`, and they are root/admin-owned so the wizard cannot remove\n * them. Cannot be neutralized: the user (or their IT admin) must fix the file.\n */\n failClosed: SettingsConflict[];\n /**\n * User global (`~/.claude/…`) and project-local (`settings.local.json`)\n * settings. Already neutralized: `settingSources: ['project']` makes the SDK\n * ignore both (verified empirically). Warn for the record, but don't block.\n */\n warnOnly: SettingsConflict[];\n}\n\n/**\n * Sort detected conflicts by how the wizard can respond, following the rule:\n * **neutralize where possible; where it can't, let the user make the change.**\n *\n * NOTE: the `warnOnly` bucket assumes the agent runs with\n * `settingSources: ['project']` (set in `agent-interface.ts` and\n * `mcp-prompt-streaming.ts`). If that ever widens to include `'user'` / `'local'`,\n * those sources stop being neutralized and must move out of `warnOnly`.\n */\nexport function classifySettingsConflicts(\n conflicts: SettingsConflict[],\n): ClassifiedSettingsConflicts {\n return {\n autoFix: conflicts.filter((c) => c.writable),\n failClosed: conflicts.filter((c) => !c.writable && c.source === 'managed'),\n warnOnly: conflicts.filter((c) => !c.writable && c.source !== 'managed'),\n };\n}\n\n/**\n * Ensure `.claude/.gitignore` excludes `*.wizard-backup` so a backup left\n * orphaned by an interrupted run can't get committed to git — settings files\n * commonly hold `apiKeyHelper` or `ANTHROPIC_API_KEY`, which we don't want\n * leaking into a repo.\n */\nfunction ensureBackupGitignored(claudeDir: string): void {\n const gitignorePath = path.join(claudeDir, '.gitignore');\n const entry = `*${BACKUP_SUFFIX}`;\n try {\n const existing = fs.existsSync(gitignorePath)\n ? fs.readFileSync(gitignorePath, 'utf-8')\n : '';\n const lines = existing.split('\\n').map((l) => l.trim());\n if (lines.includes(entry)) return;\n const sep = existing && !existing.endsWith('\\n') ? '\\n' : '';\n fs.writeFileSync(gitignorePath, `${existing}${sep}${entry}\\n`);\n } catch (error) {\n analytics.captureException(error);\n }\n}\n\n/**\n * Copy .claude/settings.json to .wizard-backup, then remove the original so\n * the SDK doesn't load the blocking overrides. Restored at outro and on\n * cleanup.\n */\nexport function backupAndFixClaudeSettings(workingDirectory: string): boolean {\n for (const { claudeDir, original, backup } of settingsBackupTargets(\n workingDirectory,\n )) {\n if (!fs.existsSync(original)) continue;\n try {\n // Don't clobber a backup an earlier run already wrote — it holds the\n // pristine original. recoverOrphanedSettingsBackups resolves cross-run\n // orphans at process start, so a backup still present at this point\n // is from the current process and the current file is the modified one.\n if (!fs.existsSync(backup)) {\n fs.copyFileSync(original, backup);\n // Mirror the source's mode so a 0600 settings.json (apiKeyHelper,\n // API keys) doesn't become a 0644 backup readable by other local\n // users. copyFileSync would otherwise apply default umask.\n const mode = fs.statSync(original).mode & 0o777;\n fs.chmodSync(backup, mode);\n }\n ensureBackupGitignored(claudeDir);\n fs.unlinkSync(original);\n analytics.wizardCapture('backedup-claude-settings');\n registerCleanup(() => {\n try {\n restoreClaudeSettings(workingDirectory);\n } catch (error) {\n analytics.captureException(error);\n }\n });\n return true;\n } catch (error) {\n analytics.captureException(error);\n }\n }\n return false;\n}\n\n/**\n * Restore .claude/settings.json from .wizard-backup and remove the backup.\n *\n * Runs on every outro and cleanup, including runs that never had a settings\n * conflict (and so never wrote a backup). A missing backup is the normal,\n * expected state for those runs, so skip it silently — only report a genuine\n * copy failure. Removing the backup after a successful restore keeps repeat\n * calls (outro then cleanup) idempotent and avoids leaving an orphan behind.\n */\nexport function restoreClaudeSettings(workingDirectory: string): void {\n for (const { original, backup } of settingsBackupTargets(workingDirectory)) {\n if (!fs.existsSync(backup)) continue;\n try {\n fs.copyFileSync(backup, original);\n fs.rmSync(backup, { force: true });\n analytics.wizardCapture('restored-claude-settings');\n return;\n } catch (error) {\n analytics.captureException(error);\n }\n }\n}\n\n/**\n * Restore an orphaned settings backup left by a previous interrupted run.\n *\n * If a run is killed after backupAndFixClaudeSettings moved the original\n * aside but before restore ran, the user is left with no settings.json and a\n * stray .wizard-backup. The next run's conflict check then sees no file and\n * skips restore, so the user's settings stay silently gone. Detect that exact\n * state (backup present, original absent) and put the original back.\n *\n * Runs once per process, from bin.ts, before anything reads Claude settings —\n * conflict detection must see the user's real settings file, and a recovery\n * any later would resurface the conflicting overrides after the backup step\n * already moved them aside.\n */\nexport function recoverOrphanedSettingsBackups(workingDirectory: string): void {\n for (const { original, backup } of settingsBackupTargets(workingDirectory)) {\n if (!fs.existsSync(backup) || fs.existsSync(original)) continue;\n try {\n fs.copyFileSync(backup, original);\n fs.rmSync(backup, { force: true });\n analytics.wizardCapture('recovered-orphaned-settings');\n } catch (error) {\n analytics.captureException(error);\n }\n }\n}\n","/**\n * Detect a pre-existing Claude Code login on the machine.\n *\n * The wizard authenticates the agent's LLM calls by injecting its PostHog OAuth\n * token via `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_AUTH_TOKEN`. If the user is\n * already logged into Claude Code/Desktop, the Agent SDK can instead use that\n * stored `/login` credential (reported as `apiKeySource: \"/login managed key\"`)\n * and send it to the PostHog gateway, which rejects it with a 401.\n *\n * The settings-conflict scan ({@link checkAllSettingsConflicts}) only inspects\n * `settings.json` for `ANTHROPIC_*` overrides / `apiKeyHelper`; it has no\n * awareness of an actual logged-in session. This fills that gap so a run can\n * log and report the potential conflict behind a gateway 401.\n *\n * Existence-only: it never reads the credential value (no `-w`), so no secret\n * is ever exposed. The locations checked mirror the SDK's own resolution\n * (npmjs.com/package/@anthropic-ai/claude-agent-sdk → sdk.mjs): a\n * `.credentials.json` under `CLAUDE_CONFIG_DIR` / `~/.claude`, and the macOS\n * keychain item below.\n */\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport path from 'path';\nimport { spawnSync } from 'node:child_process';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\n\n/** macOS keychain service name Claude Code stores its login under. */\nconst KEYCHAIN_SERVICE = 'Claude Code-credentials';\n\nexport interface StoredClaudeLogin {\n /** `$CLAUDE_CONFIG_DIR/.credentials.json` (or `~/.claude/.credentials.json`) exists. */\n credentialsFile: boolean;\n /** macOS keychain holds a `Claude Code-credentials` item. */\n keychain: boolean;\n}\n\n/** True when any stored login was found. */\nexport const hasStoredClaudeLogin = (login: StoredClaudeLogin): boolean =>\n login.credentialsFile || login.keychain;\n\n/**\n * The directory Claude Code resolves credentials/config from — `CLAUDE_CONFIG_DIR`\n * if set, else `~/.claude`. Mirrors the SDK's own resolution; single source of\n * truth so callers can't drift from it.\n */\nexport const claudeConfigDir = (homeDir: string = os.homedir()): string =>\n process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, '.claude');\n\n/**\n * Look for a stored Claude login. `homeDir` / `platform` are injectable for\n * tests; production uses the real home dir and platform.\n */\nexport function detectStoredClaudeLogin(\n homeDir: string = os.homedir(),\n platform: NodeJS.Platform = process.platform,\n): StoredClaudeLogin {\n const credentialsPath = path.join(\n claudeConfigDir(homeDir),\n '.credentials.json',\n );\n\n let credentialsFile = false;\n try {\n credentialsFile = fs.existsSync(credentialsPath);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logToFile(\n `[stored-login] checked ${credentialsPath} — unreadable (${message}), treating as absent`,\n );\n analytics.wizardCapture('stored login check failed', {\n step: 'credentials-file',\n });\n }\n\n let keychain = false;\n if (platform === 'darwin') {\n try {\n // No `-w`: this returns only attributes (exit 0 if the item exists) and\n // never the password, so the stored token is not read or printed.\n const res = spawnSync(\n 'security',\n ['find-generic-password', '-s', KEYCHAIN_SERVICE],\n { stdio: 'ignore', timeout: 5000 },\n );\n keychain = res.status === 0;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logToFile(\n `[stored-login] checked keychain '${KEYCHAIN_SERVICE}' — lookup cleared (${message}), treating as absent`,\n );\n analytics.wizardCapture('stored login check failed', {\n step: 'keychain',\n });\n }\n }\n\n return { credentialsFile, keychain };\n}\n","/**\n * Shared agent interface for PostHog wizards\n * Uses Claude Agent SDK directly with PostHog LLM gateway\n */\n\nimport path from 'path';\nimport * as os from 'os';\nimport { createRequire } from 'node:module';\nimport { getUI, type SpinnerHandle } from '@ui';\nimport type { TokenUsageDelta } from '@ui/wizard-ui';\nimport { debug, logToFile, initLogFile, getLogFilePath } from '@utils/debug';\nimport type { WizardRunOptions } from '@utils/types';\nimport { analytics } from '@utils/analytics';\nimport { runtimeEnv } from '@env';\nimport {\n WIZARD_REMARK_EVENT_NAME,\n POSTHOG_PROPERTY_HEADER_PREFIX,\n WIZARD_ORCHESTRATOR_FLAG_KEY,\n WIZARD_USER_AGENT,\n DEFAULT_AGENT_MODEL,\n} from '@lib/constants';\nimport {\n type AdditionalFeature,\n ADDITIONAL_FEATURE_PROMPTS,\n} from '@lib/wizard-session';\nimport { wizardAbort, WizardError } from '@utils/wizard-abort';\nimport { createCustomHeaders } from '@utils/custom-headers';\nimport { HostResolution } from '@lib/host-resolution';\nimport { LINTING_TOOLS } from '@lib/safe-tools';\nimport { createWizardToolsServer, WIZARD_TOOL_NAMES } from '@lib/wizard-tools';\nimport {\n createPreToolUseYaraHooks,\n createPostToolUseYaraHooks,\n prewarmYaraScanner,\n} from '@lib/yara-hooks';\nimport { createTriageLLMProvider } from './triage-provider';\nimport { getWizardCommandments } from './commandments';\nimport { classifyToolToStage } from './agent-phase';\nimport type { PackageManagerDetector } from '@lib/detection/package-manager';\nimport { AgentSignals, AgentErrorType, REMARK_INSTRUCTION } from './signals';\nimport { AgentOutputSignals } from './output-signals';\n\n// Signal vocabulary and the output parser live in dedicated modules; re-export\n// so existing importers of these from agent-interface keep working.\nexport { AgentSignals, AgentErrorType } from './signals';\nexport type { AgentSignal } from './signals';\nexport { AgentOutputSignals } from './output-signals';\nimport {\n checkAllSettingsConflicts,\n type SettingsConflict,\n type SettingsConflictSource,\n} from './claude-settings';\nimport {\n detectStoredClaudeLogin,\n hasStoredClaudeLogin,\n claudeConfigDir,\n} from './stored-login';\nimport { sanitizeAgentSubprocessEnv } from './agent-env-isolation';\n\n// Dynamic import cache for ESM module\nlet _sdkModule: any = null;\nasync function getSDKModule(): Promise<any> {\n if (!_sdkModule) {\n _sdkModule = await import('@anthropic-ai/claude-agent-sdk');\n }\n return _sdkModule;\n}\n\n/**\n * Get the path to the bundled Claude Code CLI from the SDK package.\n * This ensures we use the SDK's bundled version rather than the user's installed Claude Code.\n */\nfunction getClaudeCodeExecutablePath(): string {\n // Bare `require` is undefined in ESM (tsx dev runs) — fall back to createRequire.\n const resolver =\n typeof require !== 'undefined'\n ? require\n : createRequire(process.argv[1] ?? `${process.cwd()}/`);\n // resolve finds the package's main entry, then we get cli.js from same dir\n const sdkPackagePath = resolver.resolve('@anthropic-ai/claude-agent-sdk');\n return path.join(path.dirname(sdkPackagePath), 'cli.js');\n}\n\n// Using `any` because typed imports from ESM modules require import attributes\n// syntax which prettier cannot parse. See PR discussion for details.\ntype SDKMessage = any;\ntype McpServersConfig = any;\ntype AbortCaseMatcher = { match: RegExp };\n\n/** Region implied by the resolved gateway URL, for telemetry and display. */\nfunction regionFromGatewayUrl(gatewayUrl: string): 'eu' | 'us' | 'local' {\n if (gatewayUrl.includes('localhost')) return 'local';\n return gatewayUrl.includes('gateway.eu.') ? 'eu' : 'us';\n}\n\n/**\n * Diagnostic context for a gateway 401, used by the auth-error screen and the\n * captured exception.\n *\n * A bare \"Authentication failed\" can't be triaged: the 401 could be a settings\n * file overriding the credential, a region mismatch, or a rejected key. This\n * records which, so the screen can name an actionable next step and telemetry\n * can tell the causes apart. Absolute conflict paths stay out of the telemetry\n * payload (they contain the user's home dir) — only sources/keys are reported.\n */\nexport interface AuthErrorContext {\n hasSettingsConflict: boolean;\n conflicts: SettingsConflict[];\n conflictSources: SettingsConflictSource[];\n conflictKeys: string[];\n gatewayUrl: string;\n region: 'eu' | 'us' | 'local';\n /** SDK `apiKeySource` from the init message, when known. */\n apiKeySource?: string;\n /**\n * True when the SDK authenticated from a stored Claude login (`apiKeySource`\n * is a \"/login managed key\") — i.e. conflicting Anthropic credentials, not\n * the gateway token the wizard injected.\n */\n usingManagedLogin: boolean;\n /** Human-readable places a conflicting Anthropic credential may live. */\n credentialPlaces: string[];\n}\n\n/** Places the agent could have picked up a non-PostHog credential. */\nfunction findCredentialPlaces(\n conflicts: SettingsConflict[],\n homeDir: string,\n): string[] {\n const places: string[] = [];\n\n const stored = detectStoredClaudeLogin(homeDir);\n const configDir = claudeConfigDir(homeDir);\n if (stored.credentialsFile) {\n places.push(\n `A logged-in Claude session: ${path.join(\n configDir,\n '.credentials.json',\n )}`,\n );\n }\n if (stored.keychain) {\n places.push(\n 'A logged-in Claude session: macOS keychain item \"Claude Code-credentials\"',\n );\n }\n for (const c of conflicts) {\n places.push(`${c.path} sets ${c.keys.join(', ')}`);\n }\n return places;\n}\n\nexport function buildAuthErrorContext(\n workingDirectory: string,\n gatewayUrl: string,\n homeDir: string = os.homedir(),\n apiKeySource?: string,\n): AuthErrorContext {\n const conflicts = checkAllSettingsConflicts(workingDirectory, homeDir);\n // The SDK reports a stored Claude login as a \"/login managed key\".\n const usingManagedLogin = /login|managed key/i.test(apiKeySource ?? '');\n return {\n hasSettingsConflict: conflicts.length > 0,\n conflicts,\n conflictSources: conflicts.map((c) => c.source),\n conflictKeys: [...new Set(conflicts.flatMap((c) => c.keys))],\n gatewayUrl,\n region: regionFromGatewayUrl(gatewayUrl),\n apiKeySource,\n usingManagedLogin,\n credentialPlaces: findCredentialPlaces(conflicts, homeDir),\n };\n}\n\nexport type AgentConfig = {\n workingDirectory: string;\n posthogMcpUrl: string;\n posthogApiKey: string;\n posthogApiHost: string;\n additionalMcpServers?: Record<string, { url: string }>;\n detectPackageManager: PackageManagerDetector;\n /** Base URL for the skills server (context-mill dev or GitHub releases) */\n skillsBaseUrl: string;\n /** Feature flag key -> variant (evaluated at start of run). */\n wizardFlags?: Record<string, string>;\n wizardMetadata?: Record<string, string>;\n /** Program identifier — selects the model for that program. */\n integrationLabel?: string;\n /**\n * Override the agent model for this run. Defaults to DEFAULT_AGENT_MODEL.\n * Use for cheap mechanical runs (e.g. source-map detection on HAIKU_MODEL).\n */\n modelOverride?: string;\n /** Bridge that drives the `wizard_ask` overlay. Omit in non-interactive hosts. */\n askBridge?: import('@lib/wizard-ask-bridge').WizardAskBridge;\n /** Per-run cap on `wizard_ask` invocations. Defaults to 10. */\n askMaxQuestions?: number;\n /** Extra tools added on top of BASE_ALLOWED_TOOLS for this run. */\n allowedTools?: readonly string[];\n /** Tools removed from BASE_ALLOWED_TOOLS for this run. */\n disallowedTools?: readonly string[];\n /**\n * Read accessor for the active pending question. Used by canUseTool to\n * block Write/Edit while the overlay is open (defense in depth).\n */\n getPendingQuestion?: () =>\n | import('@lib/wizard-session').PendingQuestion\n | null;\n /**\n * Orchestrator queue context. Present only when the `wizard-orchestrator`\n * flag routes the run here; threaded into wizard-tools so the orchestrator\n * tools register.\n */\n orchestrator?: import('@lib/agent/runner/sequence/orchestrator/queue-tools').OrchestratorToolsContext;\n};\n\n/**\n * Stop hook return type: either allow stop or block with a reason.\n */\nexport type StopHookResult =\n | Record<string, never>\n | { decision: 'block'; reason: string };\n\n/**\n * Create a stop hook callback that drains the additional feature queue,\n * then collects a remark, then allows stop.\n *\n * Three-phase logic using closure state:\n * Phase 1 — drain queue: block with each feature prompt in order\n * Phase 2 — collect remark (once): block with remark prompt\n * Phase 3 — allow stop: return {}\n */\nexport function createStopHook(\n featureQueue: readonly AdditionalFeature[],\n signals?: AgentOutputSignals,\n requestRemark = true,\n): (input: { stop_hook_active: boolean }) => StopHookResult {\n let featureIndex = 0;\n let remarkRequested = false;\n\n return (input: { stop_hook_active: boolean }): StopHookResult => {\n logToFile('Stop hook triggered', {\n stop_hook_active: input.stop_hook_active,\n featureIndex,\n remarkRequested,\n queueLength: featureQueue.length,\n });\n\n // On API errors, allow stop immediately — blocking with remark/feature\n // prompts would just fail again. The auth error screen is shown separately.\n if (signals?.hasApiError()) {\n logToFile('Stop hook: API error detected, allowing immediate stop');\n return {};\n }\n\n // Phase 1: drain feature queue\n if (featureIndex < featureQueue.length) {\n const feature = featureQueue[featureIndex++];\n const prompt = ADDITIONAL_FEATURE_PROMPTS[feature];\n logToFile(`Stop hook: injecting feature prompt for ${feature}`);\n return { decision: 'block', reason: prompt };\n }\n\n // Phase 2: collect remark (once). Skipped when the caller opts out — the\n // orchestrator suppresses it per task so it does not fire on every agent.\n if (requestRemark && !remarkRequested) {\n remarkRequested = true;\n logToFile('Stop hook: requesting reflection');\n return {\n decision: 'block',\n reason: REMARK_INSTRUCTION,\n };\n }\n\n // Phase 3: allow stop\n logToFile('Stop hook: allowing stop');\n return {};\n };\n}\n\n/**\n * Internal configuration object returned by initializeAgent\n */\ntype AgentRunConfig = {\n workingDirectory: string;\n mcpServers: McpServersConfig;\n model: string;\n wizardFlags?: Record<string, string>;\n wizardMetadata?: Record<string, string>;\n /** Extra tools added on top of BASE_ALLOWED_TOOLS for this run. */\n allowedTools?: readonly string[];\n /** Tools removed from BASE_ALLOWED_TOOLS for this run. */\n disallowedTools?: readonly string[];\n /**\n * Read accessor for the active pending question. canUseTool reads this\n * to block Write/Edit while the overlay is open.\n */\n getPendingQuestion?: () =>\n | import('@lib/wizard-session').PendingQuestion\n | null;\n};\n\n/**\n * Global identifiers attached to every LLM gateway trace for a run. They ride on\n * each `$ai_generation` the gateway emits (as `X-POSTHOG-PROPERTY-*` headers via\n * `buildAgentEnv`), so traces are filterable by program, framework, run, and build\n * type for cost attribution and dashboards. `skill_id` is omitted when the run has\n * none.\n */\nexport function buildRunTags(args: {\n programId: string;\n integration: string;\n runId: string;\n build: string;\n skillId?: string;\n}): Record<string, string> {\n return {\n program_id: args.programId,\n integration: args.integration,\n run_id: args.runId,\n build: args.build,\n ...(args.skillId ? { skill_id: args.skillId } : {}),\n };\n}\n\n/**\n * Whether Warlock/YARA scanning is disabled for this run. Off by default:\n * scanning is disabled only by the local POSTHOG_WIZARD_WARLOCK_DISABLED env\n * override, set to the explicit string 'true'. This is a local/CI escape hatch\n * only — there is deliberately no remote flag to switch off a security control\n * (we rely on version-locking @posthog/warlock + the release-gate smoke test\n * instead). Any other value leaves scanning ON.\n */\nexport function isWarlockDisabled(): boolean {\n return runtimeEnv('POSTHOG_WIZARD_WARLOCK_DISABLED') === 'true';\n}\n\n/**\n * Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS, which always\n * includes `x-posthog-use-bedrock-fallback: true` so the LLM gateway falls back to Bedrock on\n * Anthropic 5xx, plus any wizard metadata/flags.\n */\nexport function buildAgentEnv(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n): string {\n const headers = createCustomHeaders();\n headers.add('x-posthog-use-bedrock-fallback', 'true');\n for (const [key, value] of Object.entries(wizardMetadata)) {\n headers.add(\n key.startsWith(POSTHOG_PROPERTY_HEADER_PREFIX)\n ? key\n : `${POSTHOG_PROPERTY_HEADER_PREFIX}${key}`,\n value,\n );\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n headers.addFlag(flagKey, variant);\n }\n const encoded = headers.encode();\n logToFile('ANTHROPIC_CUSTOM_HEADERS', encoded);\n return encoded;\n}\n\n/**\n * Package managers that can be used to run commands.\n */\nconst PACKAGE_MANAGERS = [\n // JavaScript\n 'npm',\n 'pnpm',\n 'yarn',\n 'bun',\n 'npx',\n // Python\n 'pip',\n 'pip3',\n 'poetry',\n 'pipenv',\n 'uv',\n];\n\n/**\n * Safe scripts/commands that can be run with any package manager.\n * Uses startsWith matching, so 'build' matches 'build', 'build:prod', etc.\n * Note: Linting tools are in LINTING_TOOLS and checked separately.\n */\nconst SAFE_SCRIPTS = [\n // Package installation\n 'install',\n 'add',\n 'ci',\n // Build\n 'build',\n // Type checking (various naming conventions)\n 'tsc',\n 'typecheck',\n 'type-check',\n 'check-types',\n 'types',\n // Linting/formatting script names (actual tools are in LINTING_TOOLS)\n 'lint',\n 'format',\n];\n\n/**\n * Dangerous shell operators that could allow command injection.\n * Note: We handle `2>&1` and `| tail/head` separately as safe patterns.\n */\nconst DANGEROUS_OPERATORS = /[;`$()]/;\n\n// Re-export for backwards compatibility — canonical source is skill-install.ts\nexport { isSkillInstallCommand } from '@lib/skill-install';\n\n/**\n * Check if command is an allowed package manager command.\n * Matches: <pkg-manager> [run|exec] <safe-script> [args...]\n */\nfunction matchesAllowedPrefix(command: string): boolean {\n const parts = command.split(/\\s+/);\n if (parts.length === 0 || !PACKAGE_MANAGERS.includes(parts[0])) {\n return false;\n }\n\n // Skip 'run' or 'exec' if present\n let scriptIndex = 1;\n if (parts[scriptIndex] === 'run' || parts[scriptIndex] === 'exec') {\n scriptIndex++;\n }\n\n // `i` is the npm/pnpm/bun shorthand for `install`. Exact-token match —\n // adding 'i' to SAFE_SCRIPTS would startsWith-allow anything i-prefixed.\n if (parts[scriptIndex] === 'i') return true;\n\n // Get the script/command portion (may include args)\n const scriptPart = parts.slice(scriptIndex).join(' ');\n\n // Check if script starts with any safe script name or linting tool\n return (\n SAFE_SCRIPTS.some((safe) => scriptPart.startsWith(safe)) ||\n LINTING_TOOLS.some((tool) => scriptPart.startsWith(tool))\n );\n}\n\n/**\n * Permission hook that allows only safe commands.\n * - Package manager install commands\n * - Build/typecheck/lint commands for verification\n * - Piping to tail/head for output limiting is allowed\n * - Stderr redirection (2>&1) is allowed\n *\n * `wizardAskPending` is true while a wizard_ask overlay is open — when set,\n * Write/Edit calls are denied as a defense-in-depth measure against a\n * misbehaving agent that races to mutate files before the question is\n * answered. The SDK's tool-result protocol already pauses the agent here;\n * this guard is a belt-and-suspenders second line.\n */\nexport function wizardCanUseTool(\n toolName: string,\n input: Record<string, unknown>,\n context: {\n wizardAskPending?: boolean;\n disallowedTools?: readonly string[];\n } = {},\n):\n | { behavior: 'allow'; updatedInput: Record<string, unknown> }\n | { behavior: 'deny'; message: string } {\n // Hard gate on the program's disallow list. The SDK's own disallowedTools\n // option blocks tools at the parent level, but does NOT reliably propagate\n // to dispatched subagents (their AgentDefinition has its own field which the\n // SDK appears to ignore for MCP tools). canUseTool is invoked for every\n // tool call regardless of which agent layer emitted it, so denying here is\n // the only certain block.\n if (context.disallowedTools?.includes(toolName)) {\n logToFile(`Denying disallowed tool: ${toolName}`);\n return {\n behavior: 'deny',\n message: `Tool ${toolName} is disabled for this program.`,\n };\n }\n\n if (\n context.wizardAskPending &&\n (toolName === 'Write' || toolName === 'Edit')\n ) {\n logToFile(`Denying ${toolName} while wizard_ask overlay is open`);\n return {\n behavior: 'deny',\n message: `${toolName} is paused while a wizard_ask question is open. Wait for the user's answer to come back as a tool result before writing files.`,\n };\n }\n\n // Block direct reads/writes of .env files — use wizard-tools MCP instead\n if (toolName === 'Read' || toolName === 'Write' || toolName === 'Edit') {\n const filePath = typeof input.file_path === 'string' ? input.file_path : '';\n const basename = path.basename(filePath);\n if (basename.startsWith('.env')) {\n logToFile(`Denying ${toolName} on env file: ${filePath}`);\n return {\n behavior: 'deny',\n message: `Direct ${toolName} of ${basename} is not allowed. Use the wizard-tools MCP server (check_env_keys / set_env_values) to read or modify environment variables.`,\n };\n }\n return { behavior: 'allow', updatedInput: input };\n }\n\n // Block Grep when it directly targets a .env file.\n // Note: ripgrep skips dotfiles (like .env*) by default during directory traversal,\n // so broad searches like `Grep { path: \".\" }` are already safe.\n if (toolName === 'Grep') {\n const grepPath = typeof input.path === 'string' ? input.path : '';\n if (grepPath && path.basename(grepPath).startsWith('.env')) {\n logToFile(`Denying Grep on env file: ${grepPath}`);\n return {\n behavior: 'deny',\n message: `Grep on ${path.basename(\n grepPath,\n )} is not allowed. Use the wizard-tools MCP server (check_env_keys) to check environment variables.`,\n };\n }\n return { behavior: 'allow', updatedInput: input };\n }\n\n // Allow all other non-Bash tools\n if (toolName !== 'Bash') {\n return { behavior: 'allow', updatedInput: input };\n }\n\n const command = (\n typeof input.command === 'string' ? input.command : ''\n ).trim();\n\n // Block definitely dangerous operators: ; ` $ ( )\n if (DANGEROUS_OPERATORS.test(command)) {\n logToFile(`Denying bash command with dangerous operators: ${command}`);\n debug(`Denying bash command with dangerous operators: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'dangerous operators',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Shell operators like ; \\` $ ( ) are not permitted.`,\n };\n }\n\n // Normalize: remove safe stderr redirection (2>&1, 2>&2, etc.)\n const normalized = command.replace(/\\s*\\d*>&\\d+\\s*/g, ' ').trim();\n\n // Check for pipe to tail/head (safe output limiting)\n const pipeMatch = normalized.match(/^(.+?)\\s*\\|\\s*(tail|head)(\\s+\\S+)*\\s*$/);\n if (pipeMatch) {\n const baseCommand = pipeMatch[1].trim();\n\n // Block if base command has pipes or & (multiple chaining)\n if (/[|&]/.test(baseCommand)) {\n logToFile(`Denying bash command with multiple pipes: ${command}`);\n debug(`Denying bash command with multiple pipes: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'multiple pipes',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Only single pipe to tail/head is permitted.`,\n };\n }\n\n if (matchesAllowedPrefix(baseCommand)) {\n logToFile(`Allowing bash command with output limiter: ${command}`);\n debug(`Allowing bash command with output limiter: ${command}`);\n return { behavior: 'allow', updatedInput: input };\n }\n }\n\n // Block remaining pipes and & (not covered by tail/head case above)\n if (/[|&]/.test(normalized)) {\n logToFile(`Denying bash command with pipe/&: ${command}`);\n debug(`Denying bash command with pipe/&: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'disallowed pipe',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Pipes are only permitted with tail/head for output limiting.`,\n };\n }\n\n // Check if command starts with any allowed prefix (package manager commands)\n if (matchesAllowedPrefix(normalized)) {\n logToFile(`Allowing bash command: ${command}`);\n debug(`Allowing bash command: ${command}`);\n return { behavior: 'allow', updatedInput: input };\n }\n\n logToFile(`Denying bash command: ${command}`);\n debug(`Denying bash command: ${command}`);\n analytics.wizardCapture('bash denied', {\n reason: 'not in allowlist',\n command,\n });\n return {\n behavior: 'deny',\n message: `Bash command not allowed. Only install, build, typecheck, lint, and formatting commands are permitted.`,\n };\n}\n\n/**\n * Initialize agent configuration for the LLM gateway\n */\nexport async function initializeAgent(\n config: AgentConfig,\n options: WizardRunOptions,\n): Promise<AgentRunConfig> {\n // Initialize log file for this run\n initLogFile();\n logToFile('Agent initialization starting');\n logToFile('Install directory:', options.installDir);\n\n try {\n // TODO: clean up in #755\n const gatewayUrl = HostResolution.fromApiHost(\n config.posthogApiHost,\n ).gatewayUrl;\n\n // Configure model routing (inherited by the SDK subprocess). All model\n // calls route through the PostHog LLM gateway, authed with the user's\n // OAuth token.\n // Disable experimental betas (like input_examples) the gateway doesn't support.\n process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = 'true';\n process.env.ANTHROPIC_BASE_URL = gatewayUrl;\n process.env.ANTHROPIC_AUTH_TOKEN = config.posthogApiKey;\n\n // Use CLAUDE_CODE_OAUTH_TOKEN to override any stored /login credentials\n process.env.CLAUDE_CODE_OAUTH_TOKEN = config.posthogApiKey;\n\n logToFile('Configured LLM gateway:', gatewayUrl);\n logToFile(\n 'API key prefix:',\n config.posthogApiKey\n ? `${config.posthogApiKey.slice(0, 4)}***`\n : '(missing)',\n );\n\n // A pre-existing Claude login (the SDK's \"/login managed key\") can outrank\n // the gateway token we just set and get sent to the PostHog gateway, which\n // 401s it. The settings-conflict scan can't see it, so detect + report it\n // here — this is the leading suspect behind the gateway auth_failed reports.\n const storedLogin = detectStoredClaudeLogin();\n if (hasStoredClaudeLogin(storedLogin)) {\n logToFile(\n `Pre-existing Claude login detected (credentialsFile=${storedLogin.credentialsFile}, ` +\n `keychain=${storedLogin.keychain}). It can outrank the wizard's gateway token ` +\n `and cause a 401 — 'claude auth logout' clears it.`,\n );\n analytics.wizardCapture('claude stored login detected', {\n credentials_file: storedLogin.credentialsFile,\n keychain: storedLogin.keychain,\n });\n }\n\n const initConflicts = checkAllSettingsConflicts(options.installDir);\n logToFile(\n 'Settings conflicts at agent init:',\n initConflicts.length > 0\n ? initConflicts\n .map((c) => `${c.source}(${c.keys.join(',')})`)\n .join('; ')\n : 'none',\n );\n\n // Configure MCP server with PostHog authentication\n const mcpServers: McpServersConfig = {\n 'posthog-wizard': {\n type: 'http',\n url: config.posthogMcpUrl,\n headers: {\n Authorization: `Bearer ${config.posthogApiKey}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // CLI mode's single `exec` tool carries the full command reference on\n // its schema — keep it in context, never deferred behind tool search.\n alwaysLoad: true,\n },\n ...Object.fromEntries(\n Object.entries(config.additionalMcpServers ?? {}).map(\n ([name, { url }]) => [name, { type: 'http', url }],\n ),\n ),\n };\n\n // Add in-process wizard tools (env files, package manager detection, skill loading)\n const wizardToolsServer = await createWizardToolsServer({\n workingDirectory: config.workingDirectory,\n detectPackageManager: config.detectPackageManager,\n skillsBaseUrl: config.skillsBaseUrl,\n askBridge: config.askBridge,\n askMaxQuestions: config.askMaxQuestions,\n orchestrator: config.orchestrator,\n });\n mcpServers['wizard-tools'] = wizardToolsServer;\n\n // Bare model IDs (no `anthropic/` prefix) so the LLM gateway's Bedrock\n // fallback can match map_to_bedrock_model()'s strict lookup.\n const model = config.modelOverride ?? DEFAULT_AGENT_MODEL;\n\n const agentRunConfig: AgentRunConfig = {\n workingDirectory: config.workingDirectory,\n mcpServers,\n model,\n wizardFlags: config.wizardFlags,\n wizardMetadata: config.wizardMetadata,\n allowedTools: config.allowedTools,\n disallowedTools: config.disallowedTools,\n getPendingQuestion: config.getPendingQuestion,\n };\n\n logToFile('Agent config:', {\n workingDirectory: agentRunConfig.workingDirectory,\n posthogMcpUrl: config.posthogMcpUrl,\n gatewayUrl,\n apiKeyPresent: !!config.posthogApiKey,\n });\n\n if (options.debug) {\n debug('Agent config:', {\n workingDirectory: agentRunConfig.workingDirectory,\n posthogMcpUrl: config.posthogMcpUrl,\n gatewayUrl,\n apiKeyPresent: !!config.posthogApiKey,\n });\n }\n\n // Pre-warm the warlock scanner (WASM init + rule compile) off the hook path\n // so the first tool-call scan doesn't pay cold-start under a hook timeout.\n // Fire-and-forget: the warlock module promise is cached, so the first real\n // scan awaits the same in-flight promise. Awaiting here would just move\n // the cold-start cost to user-visible startup.\n // Best-effort — a failure is non-fatal (hooks still fail closed per scan).\n void prewarmYaraScanner();\n\n return agentRunConfig;\n } catch (error) {\n getUI().log.error(\n `Failed to initialize agent: ${(error as Error).message}`,\n );\n logToFile('Agent initialization error:', error);\n debug('Agent initialization error:', error);\n throw error;\n }\n}\n\n/**\n * Execute an agent with the provided prompt and options\n * Handles the full lifecycle: spinner, execution, error handling\n *\n * @returns An object containing any error detected in the agent's output\n */\nexport async function runAgent(\n agentConfig: AgentRunConfig,\n prompt: string,\n options: WizardRunOptions,\n spinner: SpinnerHandle,\n config?: {\n estimatedDurationMinutes?: number;\n spinnerMessage?: string;\n successMessage?: string;\n errorMessage?: string;\n additionalFeatureQueue?: readonly AdditionalFeature[];\n abortCases?: readonly AbortCaseMatcher[];\n /**\n * Emit a `wizard: step` event on each agent task transition. Threaded from\n * `ProgramRun.trackStepProgress`; defaults off for every other caller.\n */\n emitStepEvents?: boolean;\n /** Request the end-of-run reflection remark. Defaults to true. */\n requestRemark?: boolean;\n /**\n * Extra properties attached to this run's `agent completed` / `agent\n * aborted` events (e.g. the orchestrator's task type and id).\n */\n analyticsProperties?: Record<string, unknown>;\n },\n middleware?: {\n onMessage(message: any): void;\n finalize(resultMessage: any, totalDurationMs: number): any;\n },\n): Promise<{ error?: AgentErrorType; message?: string }> {\n const {\n spinnerMessage = 'Customizing your PostHog setup...',\n successMessage = 'PostHog integration complete',\n errorMessage = 'Integration failed',\n abortCases = [],\n emitStepEvents = false,\n } = config ?? {};\n\n logToFile('Starting agent run');\n const { query } = await getSDKModule();\n\n spinner.start(spinnerMessage);\n\n const cliPath = getClaudeCodeExecutablePath();\n logToFile('Starting agent run');\n logToFile('Claude Code executable:', cliPath);\n logToFile('Prompt:', prompt);\n\n const startTime = Date.now();\n const signals = new AgentOutputSignals();\n // Track if we received a successful result (before any cleanup errors)\n let receivedSuccessResult = false;\n let loggedInitialContext = false;\n let lastResultMessage: any = null;\n\n // SDK >=0.3.142 replaced TodoWrite (snapshot) with TaskCreate/TaskUpdate (accumulate by id).\n // The agent's TaskCreate tool_use doesn't know the assigned taskId — the SDK returns it\n // in the matching tool_result. Keep one map keyed by whatever id we have right now:\n // tool_use_id while pending, then rekeyed to taskId once the result arrives.\n const tasks = new Map<string, TaskEntry>();\n\n // Workaround for SDK bug: stdin closes before canUseTool responses can be sent.\n // The fix is to use an async generator for the prompt that stays open until\n // the result is received, keeping the stdin stream alive for permission responses.\n // See: https://github.com/anthropics/claude-code/issues/4775\n // See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/41\n let signalDone: () => void;\n const resultReceived = new Promise<void>((resolve) => {\n signalDone = resolve;\n });\n\n const createPromptStream = async function* () {\n yield {\n type: 'user',\n session_id: '',\n message: { role: 'user', content: prompt },\n parent_tool_use_id: null,\n };\n await resultReceived;\n };\n\n // Helper to handle successful completion (used in normal path and race condition recovery)\n const completeWithSuccess = (\n suppressedError?: Error,\n ): { error?: AgentErrorType; message?: string } => {\n const durationMs = Date.now() - startTime;\n const durationSeconds = Math.round(durationMs / 1000);\n\n if (suppressedError) {\n logToFile(\n `Ignoring post-completion error, agent completed successfully in ${durationSeconds}s`,\n );\n logToFile('Suppressed error:', suppressedError.message);\n } else {\n logToFile(`Agent run completed in ${durationSeconds}s`);\n }\n\n // Extract and capture the agent's reflection on the run\n const remark = signals.remark();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\n }\n\n // A failed install_skill is non-fatal — the agent continues best-effort\n // without the skill — but every such run must be measurable.\n const skillFailure = signals.skillInstallFailure();\n if (skillFailure !== undefined) {\n analytics.wizardCapture('agent continued without skill', {\n detail: skillFailure,\n });\n }\n\n // Token usage comes from the SDK result message and is per agent run —\n // for the orchestrator that means per task, the secondary cost to watch.\n const usage = lastResultMessage?.usage as\n | {\n input_tokens?: number;\n output_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n analytics.wizardCapture('agent completed', {\n duration_ms: durationMs,\n duration_seconds: durationSeconds,\n model: agentConfig.model,\n num_turns: lastResultMessage?.num_turns,\n total_cost_usd: lastResultMessage?.total_cost_usd,\n input_tokens: usage?.input_tokens,\n output_tokens: usage?.output_tokens,\n cache_creation_input_tokens: usage?.cache_creation_input_tokens,\n cache_read_input_tokens: usage?.cache_read_input_tokens,\n ...config?.analyticsProperties,\n });\n // Reconcile the hidden Ctrl+T HUD's running cost estimate to the SDK's\n // authoritative total — same trick the benchmark's\n // CostTrackerPlugin.onFinalize uses to correct per-turn drift.\n const totalCostUsd = Number(lastResultMessage?.total_cost_usd ?? 0);\n if (totalCostUsd > 0) {\n getUI().setFinalTokenCostUsd(totalCostUsd);\n }\n try {\n middleware?.finalize(lastResultMessage, durationMs);\n } catch (e) {\n logToFile(`${AgentSignals.BENCHMARK} Middleware finalize error:`, e);\n }\n spinner.stop(successMessage);\n return {};\n };\n\n // Abort controller — lets us force-kill the SDK query when we detect an\n // [ABORT] signal in the agent's output. Also stashes the reason so the\n // runner can surface it via outroData after we unwind.\n const abortController = new AbortController();\n let abortReason: string | null = null;\n // Set when a YARA hook detects a terminal violation. Returning `stopReason`\n // from a PostToolUse hook does NOT stop the SDK, so we abort the query and\n // surface a YARA_VIOLATION below — mirroring the [ABORT] mechanism.\n let yaraViolationReason: string | null = null;\n\n try {\n // Per-program allow/disallow lists tweak BASE_ALLOWED_TOOLS. Skills are\n // enabled via the `skills` query option; PostHog MCP tools come through\n // `mcpServers`. Neither belongs in this list.\n const disallow = new Set(agentConfig.disallowedTools ?? []);\n const allowedTools = [\n ...BASE_ALLOWED_TOOLS,\n ...(agentConfig.allowedTools ?? []),\n ].filter((t) => !disallow.has(t));\n\n // Subagents dispatched via the Agent tool don't inherit the parent's\n // MCP servers by default — so general-purpose subagents can't see the\n // PostHog MCP and fall back to curl with whatever key they can find\n // (then 401, then start asking the user for keys). Override\n // general-purpose to forward parent MCP servers by name; SDK resolves\n // each string against the parent's mcpServers map.\n const inheritedMcpServerNames = Object.keys(agentConfig.mcpServers);\n\n // LLM provider for warlock triage (reuses the gateway auth set on\n // process.env by initializeAgent). Undefined if auth is missing — hooks\n // then skip triage and fail closed.\n const triageProvider = createTriageLLMProvider();\n\n // Actually stop the run when a YARA hook hits a terminal violation. The SDK\n // ignores `stopReason` from PostToolUse hooks, so we abort the query (like\n // [ABORT]) and return YARA_VIOLATION from the loop-end / catch below.\n const onYaraTerminate = (reason: string) => {\n if (yaraViolationReason) return; // first violation wins\n yaraViolationReason = reason;\n logToFile(`[YARA] terminating run: ${reason}`);\n abortController.abort();\n signalDone!();\n };\n\n // Local/CI escape hatch for Warlock/YARA scanning (off by default — see\n // isWarlockDisabled for the fail-safe semantics).\n const warlockDisabled = isWarlockDisabled();\n if (warlockDisabled) {\n logToFile('[warlock] scanning disabled for run (local env override)');\n analytics.wizardCapture('warlock disabled', { reason: 'env-override' });\n }\n\n const response = query({\n prompt: createPromptStream(),\n options: {\n abortController,\n model: agentConfig.model,\n cwd: agentConfig.workingDirectory,\n permissionMode: 'acceptEdits',\n betas: ['context-1m-2025-08-07'],\n mcpServers: agentConfig.mcpServers,\n agents: {\n 'general-purpose': {\n description:\n \"General-purpose subagent. Inherits the parent run's tools plus the PostHog and wizard-tools MCP servers, so it can call mcp__posthog-wizard__* directly instead of curling the REST API.\",\n prompt:\n 'You are a general-purpose subagent for the PostHog wizard. Prefer the authenticated mcp__posthog-wizard__* MCP tools over raw HTTP — they are already authenticated for this project. Only fall back to other transports if no MCP tool covers the operation.',\n mcpServers: inheritedMcpServerNames,\n // SDK does not propagate the parent's disallowedTools to subagents\n // (sdk.d.ts: AgentDefinition has its own disallowedTools, and\n // `tools: undefined` means \"inherit all\"). Without this, a program\n // that disallows wizard_ask still leaks it to dispatched subagents.\n disallowedTools: agentConfig.disallowedTools\n ? [...agentConfig.disallowedTools]\n : undefined,\n },\n },\n // Load skills from project's .claude/skills/ directory\n settingSources: ['project'],\n // Enable all discovered skills. Omitting this is NOT \"skills off\" —\n // it just means no SDK auto-config — so we set 'all' explicitly to\n // preserve the prior behavior where 'Skill' in allowedTools exposed\n // everything under .claude/skills/. (SDK ≥0.2.133 deprecates passing\n // 'Skill' in allowedTools in favor of this option.)\n skills: 'all',\n allowedTools,\n sandbox: {\n enabled: true,\n // SDK 0.2.91 made failIfUnavailable default to true when enabled is\n // set, which would abort wizard runs on hosts that lack sandbox\n // dependencies (e.g. Linux without bubblewrap). Wizard targets a\n // broad set of user machines, so prefer graceful degradation —\n // commands still respect allowUnsandboxedCommands below.\n failIfUnavailable: false,\n allowUnsandboxedCommands: false,\n filesystem: {\n allowWrite: [\n '/' + agentConfig.workingDirectory,\n '/' + agentConfig.workingDirectory + '/**',\n '//tmp',\n '//tmp/**',\n '//private/tmp',\n '//private/tmp/**',\n // Package manager stores and toolchain installs — allow writes\n // so pnpm/npm/yarn/bun and version managers (corepack, volta)\n // can install packages and self-update without breaking the\n // user's existing setup.\n '~/Library/pnpm/**', // pnpm root (macOS) — store + .tools/ for packageManager pinning\n '~/.local/share/pnpm/**', // pnpm root (Linux)\n '~/.pnpm-store/**', // pnpm alternate store\n '~/.npm/**', // npm cache (covers _npx too)\n '~/.yarn/**', // yarn classic + berry cache\n '~/.bun/install/**', // bun cache + global installs\n '~/.cache/node/corepack/**', // corepack version downloads (Linux/macOS)\n '~/Library/Caches/node/corepack/**', // corepack on older macOS layouts\n '~/.volta/**', // Volta toolchain (referenced by workbench package.json)\n // Python — used by django/flask/fastapi wizards\n '~/.cache/pip/**',\n '~/Library/Caches/pip/**',\n '~/.cache/uv/**',\n '~/Library/Caches/uv/**',\n '~/.cache/pypoetry/**',\n '~/Library/Caches/pypoetry/**',\n // Ruby — used by rails wizard\n '~/.bundle/**',\n '~/.gem/**',\n ],\n },\n network: {\n allowedDomains: [\n 'github.com',\n 'api.github.com',\n 'raw.githubusercontent.com',\n 'release-assets.githubusercontent.com',\n 'objects.githubusercontent.com',\n ],\n },\n },\n env: {\n // Drop the ENTIRE ANTHROPIC_*/CLAUDE_CODE_* namespace from the\n // inherited env so no shell/settings value can leak into or outrank\n // the agent's routing; the wizard's own gateway routing is injected\n // fresh below. See agent-env-isolation.ts.\n ...sanitizeAgentSubprocessEnv(process.env),\n // Gateway routing — injected explicitly (initializeAgent set these on\n // process.env for in-process readers; the strip above removed them\n // from the inherited copy, so re-add the wizard's own values here).\n ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL,\n ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN,\n CLAUDE_CODE_OAUTH_TOKEN: process.env.CLAUDE_CODE_OAUTH_TOKEN,\n CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: 'true',\n // SDK 0.3.142 made MCP servers connect in the background by default;\n // the agent may start its first turn before posthog-wizard is ready\n // (audit programs call audit_seed_checks on turn 1, integration\n // programs call load_skill_menu / install_skill). Restore the prior\n // blocking behavior so the SDK waits up to 5s for MCP connect before\n // turn 1.\n MCP_CONNECTION_NONBLOCKING: '0',\n // PostHog gateway headers: Bedrock fallback + property/flag tags.\n ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(\n agentConfig.wizardMetadata ?? {},\n agentConfig.wizardFlags ?? {},\n ),\n },\n canUseTool: (toolName: string, input: unknown) => {\n logToFile('canUseTool called:', { toolName, input });\n const result = wizardCanUseTool(\n toolName,\n input as Record<string, unknown>,\n {\n wizardAskPending: agentConfig.getPendingQuestion?.() != null,\n disallowedTools: agentConfig.disallowedTools,\n },\n );\n logToFile('canUseTool result:', result);\n return Promise.resolve(result);\n },\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n // Append wizard-wide commandments rather than replacing\n // the preset so we keep default Claude Code behaviors.\n append: getWizardCommandments(),\n },\n tools: { type: 'preset', preset: 'claude_code' },\n // Capture stderr from CLI subprocess for debugging\n stderr: (data: string) => {\n logToFile('CLI stderr:', data);\n if (options.debug) {\n debug('CLI stderr:', data);\n }\n },\n // Stop hook: drain additional feature queue, then collect remark, then allow stop\n hooks: {\n PreToolUse: warlockDisabled\n ? []\n : createPreToolUseYaraHooks(triageProvider),\n PostToolUse: warlockDisabled\n ? []\n : createPostToolUseYaraHooks(triageProvider, onYaraTerminate),\n Stop: [\n {\n hooks: [\n createStopHook(\n config?.additionalFeatureQueue ?? [],\n signals,\n config?.requestRemark ?? true,\n ),\n ],\n timeout: 30,\n },\n ],\n },\n },\n });\n\n // Process the async generator\n for await (const message of response) {\n // Log initial context size on the first assistant response so we can\n // detect sudden shifts in starting context (e.g. MCP schema bloat).\n if (!loggedInitialContext && message.type === 'assistant') {\n const usage = message.message?.usage as\n | {\n input_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n if (usage) {\n const input = usage.input_tokens ?? 0;\n const cacheCreation = usage.cache_creation_input_tokens ?? 0;\n const cacheRead = usage.cache_read_input_tokens ?? 0;\n const initialTokens = input + cacheCreation + cacheRead;\n logToFile(\n `Initial context: ${initialTokens} tokens (input=${input}, cache_creation=${cacheCreation}, cache_read=${cacheRead})`,\n );\n analytics.wizardCapture('agent initial context', {\n initial_tokens: initialTokens,\n input_tokens: input,\n cache_creation_input_tokens: cacheCreation,\n cache_read_input_tokens: cacheRead,\n });\n }\n loggedInitialContext = true;\n }\n\n // Pass receivedSuccessResult so handleSDKMessage can suppress user-facing error\n // output for post-success cleanup errors while still logging them to file\n handleSDKMessage(\n message,\n options,\n spinner,\n signals,\n receivedSuccessResult,\n tasks,\n (agentConfig.wizardFlags ?? {})[WIZARD_ORCHESTRATOR_FLAG_KEY] ===\n 'true',\n emitStepEvents,\n );\n\n // [ABORT] detection: the skill emits \"[ABORT] <reason>\" when it\n // cannot complete the program. Kill the SDK query immediately —\n // the prompt doesn't need to cooperate with \"and exit\" because the\n // abort is enforced here. The reason is surfaced via the returned\n // AgentErrorType.ABORT so the runner can render a custom screen.\n if (\n abortCases.length > 0 &&\n !abortReason &&\n message.type === 'assistant'\n ) {\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n const match = block.text.match(/\\[ABORT\\]\\s*(.+?)(?:\\n|$)/);\n if (match) {\n abortReason = match[1].trim();\n logToFile(`Agent emitted [ABORT]: ${abortReason}`);\n abortController.abort();\n signalDone!();\n break;\n }\n }\n }\n }\n }\n\n // 401: show auth error screen and exit immediately\n if (message.type === 'assistant' && signals.hasApiErrorStatus(401)) {\n signalDone!();\n spinner.stop('Authentication failed');\n // Re-check at error time: a settings conflict can be the *real* cause\n // of a 401, distinct from bad PAT / wrong region / expired key.\n // Only the conflict case warrants telling the user to log out of\n // Claude Code.\n const authError = buildAuthErrorContext(\n options.installDir,\n process.env.ANTHROPIC_BASE_URL ?? '',\n os.homedir(),\n signals.apiKeySource,\n );\n logToFile('Agent error: 401, showing auth error screen', authError);\n getUI().showAuthError({\n hasSettingsConflict: authError.hasSettingsConflict,\n conflicts: authError.conflicts,\n usingManagedLogin: authError.usingManagedLogin,\n credentialPlaces: authError.credentialPlaces,\n logFilePath: getLogFilePath(),\n });\n await wizardAbort({\n message: 'Authentication failed (401)',\n error: new WizardError('Authentication failed', {\n hasSettingsConflict: authError.hasSettingsConflict,\n conflictSources: authError.conflictSources,\n conflictKeys: authError.conflictKeys,\n gatewayUrl: authError.gatewayUrl,\n region: authError.region,\n usingManagedLogin: authError.usingManagedLogin,\n apiKeySource: authError.apiKeySource,\n }),\n });\n }\n\n try {\n middleware?.onMessage(message);\n } catch (e) {\n logToFile(`${AgentSignals.BENCHMARK} Middleware onMessage error:`, e);\n }\n\n // Signal completion when result received\n if (message.type === 'result') {\n // Track successful results before any potential cleanup errors\n // The SDK may emit a second error result during cleanup due to a race condition\n if (message.subtype === 'success' && !message.is_error) {\n receivedSuccessResult = true;\n lastResultMessage = message;\n }\n signalDone!();\n }\n }\n\n // A YARA hook detected a terminal violation and aborted the run.\n if (yaraViolationReason) {\n logToFile('Agent error: YARA_VIOLATION');\n spinner.stop('Security check stopped the setup');\n return { error: AgentErrorType.YARA_VIOLATION };\n }\n\n // If the middleware caught an [ABORT] and aborted the SDK query, surface\n // it as a structured error before checking other signals.\n if (abortReason) {\n spinner.stop('Wizard aborted');\n return { error: AgentErrorType.ABORT, message: abortReason };\n }\n\n // Check for error markers in the agent's output\n if (signals.has('MCP_MISSING')) {\n logToFile('Agent error: MCP_MISSING');\n spinner.stop('Agent could not access PostHog MCP');\n return { error: AgentErrorType.MCP_MISSING };\n }\n\n if (signals.has('RESOURCE_MISSING')) {\n logToFile('Agent error: RESOURCE_MISSING');\n spinner.stop('Agent could not access setup resource');\n return { error: AgentErrorType.RESOURCE_MISSING };\n }\n\n // A clean success result already arrived. The Claude SDK can emit a second\n // error result during teardown (e.g. \"API Error: The socket connection was\n // closed unexpectedly\" when the streaming connection drops on cleanup),\n // whose text lands in `signals` — so the API-error checks below would\n // escalate that teardown noise to a fatal error. A finished run is\n // finished; mirror the catch-path guard and complete successfully.\n if (receivedSuccessResult) {\n return completeWithSuccess();\n }\n\n // Check for API errors (rate limits, etc.)\n // Surface just the API error line(s), not the entire output\n const apiErrorMessage = signals.apiErrorMessage() ?? 'Unknown API error';\n\n if (signals.hasApiErrorStatus(429)) {\n logToFile('Agent error: RATE_LIMIT');\n spinner.stop('Rate limit exceeded');\n return { error: AgentErrorType.RATE_LIMIT, message: apiErrorMessage };\n }\n\n if (signals.hasApiError()) {\n logToFile('Agent error: API_ERROR');\n spinner.stop('API error occurred');\n return { error: AgentErrorType.API_ERROR, message: apiErrorMessage };\n }\n\n return completeWithSuccess();\n } catch (error) {\n // Signal done to unblock the async generator\n signalDone!();\n\n // A YARA hook aborted the run (the SDK throws AbortError once the hook\n // calls abortController.abort()). Surface it before anything else so it is\n // never mistaken for a success-cleanup race or a generic abort.\n if (yaraViolationReason) {\n logToFile('Agent error: YARA_VIOLATION');\n spinner.stop('Security check stopped the setup');\n return { error: AgentErrorType.YARA_VIOLATION };\n }\n\n // If the middleware caught an [ABORT] and triggered abortController.abort(),\n // the SDK will throw an AbortError — surface it as a clean abort result.\n if (abortReason) {\n spinner.stop('Wizard aborted');\n return { error: AgentErrorType.ABORT, message: abortReason };\n }\n\n // If we already received a successful result, the error is from SDK cleanup\n // This happens due to a race condition: the SDK tries to send a cleanup command\n // after the prompt stream closes, but streaming mode is still active.\n // See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/41\n if (receivedSuccessResult) {\n return completeWithSuccess(error as Error);\n }\n\n // Check if we collected an error signal before the exception was thrown.\n // Surface just the API error line(s), not the entire output.\n const apiErrorMessage = signals.apiErrorMessage() ?? 'Unknown API error';\n\n if (signals.hasApiErrorStatus(429)) {\n logToFile('Agent error (caught): RATE_LIMIT');\n spinner.stop('Rate limit exceeded');\n return { error: AgentErrorType.RATE_LIMIT, message: apiErrorMessage };\n }\n\n if (signals.hasApiError()) {\n logToFile('Agent error (caught): API_ERROR');\n spinner.stop('API error occurred');\n return { error: AgentErrorType.API_ERROR, message: apiErrorMessage };\n }\n\n // No API error found, re-throw the original exception\n spinner.stop(errorMessage);\n getUI().log.error(`Error: ${(error as Error).message}`);\n logToFile('Agent run failed:', error);\n debug('Full error:', error);\n throw error;\n } finally {\n // Always capture run duration, even on abort/error, so we can alert on\n // long runs where the user gave up before completion.\n if (!receivedSuccessResult) {\n const durationMs = Date.now() - startTime;\n analytics.wizardCapture('agent aborted', {\n duration_ms: durationMs,\n duration_seconds: Math.round(durationMs / 1000),\n model: agentConfig.model,\n ...config?.analyticsProperties,\n });\n }\n }\n}\n\n/**\n * Handle SDK messages and provide user feedback\n *\n * @param receivedSuccessResult - If true, suppress user-facing error output for cleanup errors\n * while still logging to file. The SDK may emit a second error\n * result after success due to cleanup race conditions.\n */\n/**\n * SDK >=0.3.142 replaced TodoWrite with four discrete Task* tools.\n * Create / Update mutate the task list; Get / List are read-only.\n */\nexport enum TaskTool {\n Create = 'TaskCreate',\n Update = 'TaskUpdate',\n Get = 'TaskGet',\n List = 'TaskList',\n}\n\n/**\n * Tools every program gets unless its ProgramConfig.disallowedTools says\n * otherwise. Programs add more via ProgramConfig.allowedTools (e.g.\n * `'Agent'` to opt into subagent dispatch). Skills and PostHog MCP tools\n * are enabled separately (skills option / mcpServers).\n */\nexport const BASE_ALLOWED_TOOLS: readonly string[] = [\n 'Read',\n 'Write',\n 'Edit',\n 'Glob',\n 'Grep',\n 'Bash',\n // Task list tools (replaced TodoWrite in 0.3.142). Commandments instruct\n // the agent to call TaskCreate/TaskUpdate to surface progress in the TUI.\n ...Object.values(TaskTool),\n 'ListMcpResourcesTool',\n ...Object.values(WIZARD_TOOL_NAMES),\n];\n\ntype TaskEntry = { content: string; status: string; activeForm?: string };\n\ninterface TaskStore {\n tasks: Map<string, TaskEntry>;\n sync: () => void;\n /** When true, emit a `wizard: step` event on each status transition. */\n emitStepEvents?: boolean;\n}\n\ninterface ToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input?: unknown;\n}\n\nfunction handleTaskCreate(block: ToolUseBlock, store: TaskStore): void {\n const input = block.input as\n | { subject?: string; activeForm?: string }\n | undefined;\n if (!input?.subject) return;\n // Key by tool_use_id for now — the rekey to the SDK-assigned taskId happens\n // when the matching tool_result arrives.\n store.tasks.set(block.id, {\n content: input.subject,\n status: 'pending',\n activeForm: input.activeForm,\n });\n store.sync();\n}\n\nfunction handleTaskUpdate(block: ToolUseBlock, store: TaskStore): void {\n const input = block.input as\n | {\n taskId?: string;\n subject?: string;\n status?: string;\n activeForm?: string;\n }\n | undefined;\n if (!input?.taskId) return;\n const existing = store.tasks.get(input.taskId);\n if (!existing) return;\n if (input.status === 'deleted') {\n store.tasks.delete(input.taskId);\n } else {\n // Per-step drop-off signal for programs that opt in via `trackStepProgress`\n // (threaded here as `emitStepEvents`). Emit `wizard: step` on each real\n // status transition so analytics can see how far a run got — even a silent\n // step (no wizard_ask) that dies mid-run surfaces as its last `in_progress`\n // with no matching `completed`. Generic: the step name is whatever the\n // agent set; the `command` tag already identifies the program.\n if (\n store.emitStepEvents &&\n input.status &&\n input.status !== existing.status &&\n (input.status === 'in_progress' || input.status === 'completed')\n ) {\n const keys = [...store.tasks.keys()];\n analytics.wizardCapture('step', {\n // The task's display label lives on `activeForm` (what the TUI renders,\n // e.g. \"Checking access\"); `content`/`subject` are typically empty on a\n // status-only TaskUpdate. Prefer the stored entry, then the update, so\n // the name is never null. Named `step_name` (not `step`): a bare\n // `properties.step` doesn't resolve in HogQL — `step_name` queries\n // cleanly, like `step_index` / `step_count`.\n step_name:\n existing.activeForm ??\n input.activeForm ??\n existing.content ??\n input.subject,\n status: input.status,\n step_index: keys.indexOf(input.taskId),\n step_count: keys.length,\n });\n }\n store.tasks.set(input.taskId, {\n content: input.subject ?? existing.content,\n status: input.status ?? existing.status,\n activeForm: input.activeForm ?? existing.activeForm,\n });\n }\n store.sync();\n}\n\nfunction handleTaskGet(_block: ToolUseBlock, _store: TaskStore): void {\n // Read-only — the agent is querying state, not mutating it.\n}\n\nfunction handleTaskList(_block: ToolUseBlock, _store: TaskStore): void {\n // Read-only — the agent is querying state, not mutating it.\n}\n\nfunction dispatchTaskToolUse(block: ToolUseBlock, store: TaskStore): void {\n switch (block.name as TaskTool) {\n case TaskTool.Create:\n return handleTaskCreate(block, store);\n case TaskTool.Update:\n return handleTaskUpdate(block, store);\n case TaskTool.Get:\n return handleTaskGet(block, store);\n case TaskTool.List:\n return handleTaskList(block, store);\n }\n}\n\n/**\n * Pull the SDK-assigned task id off a SDKUserMessage.tool_use_result payload.\n * The SDK already deserialises TaskCreateOutput here, so the shape is the\n * structured `{ task: { id, subject } }` object — no JSON parsing needed.\n */\nfunction extractTaskIdFromToolResult(result: unknown): string | undefined {\n if (!result || typeof result !== 'object') return undefined;\n const obj = result as Record<string, unknown>;\n const task = obj.task as Record<string, unknown> | undefined;\n if (task && typeof task.id === 'string') return task.id;\n if (typeof obj.taskId === 'string') return obj.taskId;\n if (typeof obj.id === 'string') return obj.id;\n return undefined;\n}\n\n/**\n * Fallback id extractor for the inner tool_result block.content — used only\n * when message.tool_use_result is absent. In current SDK versions the inner\n * content is a human-readable string (\"Task #1 created successfully: …\") so\n * this path almost always returns undefined.\n */\nfunction extractTaskIdFromResult(content: unknown): string | undefined {\n const tryParse = (s: string): string | undefined => {\n try {\n const parsed = JSON.parse(s);\n const id = parsed?.task?.id ?? parsed?.taskId ?? parsed?.id;\n return typeof id === 'string' ? id : undefined;\n } catch {\n return undefined;\n }\n };\n if (typeof content === 'string') return tryParse(content);\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block?.type === 'text' && typeof block.text === 'string') {\n const id = tryParse(block.text);\n if (id) return id;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Extracts a `TokenUsageDelta` for the hidden Ctrl+T token/cost HUD from an\n * SDK assistant message, or `null` when the message carries no usage (e.g.\n * a duplicate/retried turn). `message.message` is a BetaMessage (the raw\n * Anthropic API response), which carries `model` alongside `usage` — read\n * per turn, not from the run's nominal model, since a subagent dispatched\n * via the Agent tool can genuinely run on a different model than the main\n * session.\n */\nfunction extractTokenUsageDelta(message: SDKMessage): TokenUsageDelta | null {\n const apiMessage = message.message as\n | {\n model?: string;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_creation?: {\n ephemeral_5m_input_tokens?: number;\n ephemeral_1h_input_tokens?: number;\n };\n };\n }\n | undefined;\n const usage = apiMessage?.usage;\n if (!usage) return null;\n return {\n inputTokens: Number(usage.input_tokens ?? 0),\n outputTokens: Number(usage.output_tokens ?? 0),\n cacheReadTokens: Number(usage.cache_read_input_tokens ?? 0),\n cacheCreationTokens: Number(usage.cache_creation_input_tokens ?? 0),\n cacheCreation5m: Number(\n usage.cache_creation?.ephemeral_5m_input_tokens ?? 0,\n ),\n cacheCreation1h: Number(\n usage.cache_creation?.ephemeral_1h_input_tokens ?? 0,\n ),\n model: apiMessage?.model,\n };\n}\n\nfunction handleSDKMessage(\n message: SDKMessage,\n options: WizardRunOptions,\n spinner: SpinnerHandle,\n signals: AgentOutputSignals,\n receivedSuccessResult = false,\n tasks?: Map<string, TaskEntry>,\n // The orchestrator owns the TUI task panel (it renders its queue). Suppress the\n // agent's own TaskCreate/TaskUpdate rendering so it does not clobber the queue.\n suppressTaskRender = false,\n // Opt-in per-step analytics, threaded from runAgent's `emitStepEvents`\n // (ProgramRun.trackStepProgress). Off for every program that doesn't opt in.\n emitStepEvents = false,\n): void {\n // Map preserves insertion order (the order the agent created the tasks).\n // Within that, group by status: completed first, then in_progress, then\n // everything else (pending). Array.prototype.sort is stable, so creation\n // order is preserved inside each group.\n const STATUS_RANK: Record<string, number> = {\n completed: 0,\n in_progress: 1,\n };\n const rank = (status: string): number => STATUS_RANK[status] ?? 2;\n const syncTasks = (): void => {\n if (!tasks || suppressTaskRender) return;\n const sorted = Array.from(tasks.values()).sort(\n (a, b) => rank(a.status) - rank(b.status),\n );\n getUI().syncTodos(sorted);\n };\n logToFile(`SDK Message: ${message.type}`, JSON.stringify(message, null, 2));\n\n if (options.debug) {\n debug(`SDK Message type: ${message.type}`);\n }\n\n switch (message.type) {\n case 'assistant': {\n // Feed the hidden Ctrl+T token/cost HUD. Mirrors the benchmark\n // middleware's TokenTrackerPlugin/CacheTrackerPlugin extraction (no\n // dedup for SDK-retried turns — see addTokenUsage's doc comment), so\n // this stays live-updating for every run, not just `--benchmark`.\n const tokenUsageDelta = extractTokenUsageDelta(message);\n if (tokenUsageDelta) getUI().addTokenUsage(tokenUsageDelta);\n\n // Extract text content from assistant messages\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n signals.push(block.text);\n\n // Check for [STATUS] markers\n const statusRegex = new RegExp(\n `^.*${AgentSignals.STATUS.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(.+?)$`,\n 'm',\n );\n const statusMatch = block.text.match(statusRegex);\n if (statusMatch) {\n const statusText = statusMatch[1].trim();\n getUI().pushStatus(statusText);\n spinner.message(statusText);\n }\n\n // Check for [DASHBOARD_URL] markers\n const dashboardRegex = new RegExp(\n `${AgentSignals.DASHBOARD_URL.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(\\\\S+)`,\n 'm',\n );\n const dashboardMatch = block.text.match(dashboardRegex);\n if (dashboardMatch) {\n getUI().setDashboardUrl(dashboardMatch[1].trim());\n }\n\n // Check for [NOTEBOOK_URL] markers\n const notebookRegex = new RegExp(\n `${AgentSignals.NOTEBOOK_URL.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n )}\\\\s*(\\\\S+)`,\n 'm',\n );\n const notebookMatch = block.text.match(notebookRegex);\n if (notebookMatch) {\n getUI().setNotebookUrl(notebookMatch[1].trim());\n }\n }\n\n // Intercept Task* tool_use blocks for task progression.\n // SDK >=0.3.142 replaced TodoWrite with TaskCreate/TaskUpdate/TaskGet/TaskList;\n // consumers must accumulate by task id rather than replacing a snapshot list.\n if (\n block.type === 'tool_use' &&\n tasks &&\n (Object.values(TaskTool) as string[]).includes(block.name)\n ) {\n dispatchTaskToolUse(block as ToolUseBlock, {\n tasks,\n sync: syncTasks,\n emitStepEvents,\n });\n }\n\n // Mirror the active tool into the Visualizer's \"stage\" indicator.\n if (block.type === 'tool_use') {\n const stage = classifyToolToStage((block as ToolUseBlock).name);\n if (stage) getUI().setStage(stage);\n }\n }\n }\n break;\n }\n\n case 'user': {\n // Rekey pending TaskCreate entries from tool_use_id to the SDK-assigned\n // taskId. The structured `{task: {id, subject}}` payload is at\n // message.tool_use_result (top-level); the inner content[].tool_result\n // blocks only carry the human-readable status text, which is not JSON.\n if (tasks && tasks.size > 0) {\n const content = message.message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (\n block.type !== 'tool_result' ||\n typeof block.tool_use_id !== 'string' ||\n !tasks.has(block.tool_use_id)\n ) {\n continue;\n }\n const taskId =\n extractTaskIdFromToolResult(\n (message as { tool_use_result?: unknown }).tool_use_result,\n ) ?? extractTaskIdFromResult(block.content);\n // No taskId means we leave the entry under tool_use_id so it stays\n // visible; later TaskUpdate calls won't match it, but at least the\n // task list doesn't vanish.\n if (!taskId) continue;\n const entry = tasks.get(block.tool_use_id)!;\n tasks.delete(block.tool_use_id);\n tasks.set(taskId, entry);\n syncTasks();\n }\n }\n }\n break;\n }\n\n case 'result': {\n // Check is_error flag - can be true even when subtype is 'success'\n if (message.is_error) {\n logToFile('Agent result with error:', message.result);\n if (typeof message.result === 'string') {\n signals.push(message.result);\n }\n // Only show errors to user if we haven't already succeeded.\n // Post-success errors are SDK cleanup noise (telemetry failures, streaming\n // mode race conditions). Full message already logged above via JSON dump.\n if (message.errors && !receivedSuccessResult) {\n for (const err of message.errors) {\n getUI().log.error(`Error: ${err}`);\n logToFile('ERROR:', err);\n }\n }\n } else if (message.subtype === 'success') {\n logToFile('Agent completed successfully');\n if (typeof message.result === 'string') {\n signals.push(message.result);\n }\n } else {\n logToFile('Agent result with error:', message.result);\n // Error result - only show to user if we haven't already succeeded.\n // Full message already logged above via JSON dump.\n if (message.errors && !receivedSuccessResult) {\n for (const err of message.errors) {\n getUI().log.error(`Error: ${err}`);\n logToFile('ERROR:', err);\n }\n }\n }\n break;\n }\n\n case 'system': {\n if (message.subtype === 'init') {\n // Capture which credential the SDK authenticated with. A managed-login\n // source (`\"/login managed key\"`) means it used a stored Claude login\n // rather than the gateway token we injected — the prime suspect for a\n // subsequent 401 (consumed by the auth-error handler below).\n signals.recordApiKeySource(message.apiKeySource);\n logToFile('Agent session initialized', {\n model: message.model,\n tools: message.tools?.length,\n mcpServers: message.mcp_servers,\n apiKeySource: message.apiKeySource,\n });\n }\n break;\n }\n\n default:\n // Log other message types for debugging\n if (options.debug) {\n debug(`Unhandled message type: ${message.type}`);\n }\n break;\n }\n}\n"],"mappings":"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"}
@@ -1,16 +1,16 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { A as GPT5_MODEL, L as OPUS_MODEL, O as GPT5_4_MODEL, V as POSTHOG_FLAG_HEADER_PREFIX, W as POSTHOG_PROPERTY_HEADER_PREFIX, X as SONNET_5_MODEL, _ as evaluateWizardReadiness, a as getLogFilePath, at as WIZARD_REMARK_EVENT_NAME, c as WIZARD_BENCHMARK_FILE, ct as WIZARD_USE_PI_HARNESS_FLAG_KEY, f as getUI, ft as RUN_SURFACE, g as SIGNUP_WIZARD_READINESS_CONFIG, h as SERVICE_LABELS, i as enableDebugLogs, j as HAIKU_MODEL, k as GPT5_MINI_MODEL, l as WIZARD_LOG_FILE, lt as getSkillsBaseUrl, nt as WIZARD_PI_EFFORT_FLAG_KEY, o as initLogFile, pt as runtimeEnv, rt as WIZARD_PI_MODEL_FLAG_KEY, s as logToFile, st as WIZARD_USER_AGENT, t as configureLogFile, tt as WIZARD_ORCHESTRATOR_FLAG_KEY, v as getBlockingServiceKeys, w as DEFAULT_AGENT_MODEL } from "./debug-D_mboDpg.js";
3
- import { a as ciExcludedTaskTypes, n as analytics, r as groupsFromUser } from "./analytics-__4kRqZr.js";
4
- import { t as getOrAskForProjectData } from "./setup-utils-CUeGnxBk.js";
5
- import { i as getLlmGatewayUrl } from "./urls-DYIMfv29.js";
6
- import { n as mcpUrlFor, t as HostResolution } from "./host-resolution-Czw5O33G.js";
2
+ import { A as GPT5_MODEL, L as OPUS_MODEL, O as GPT5_4_MODEL, V as POSTHOG_FLAG_HEADER_PREFIX, W as POSTHOG_PROPERTY_HEADER_PREFIX, X as SONNET_5_MODEL, _ as evaluateWizardReadiness, a as getLogFilePath, at as WIZARD_REMARK_EVENT_NAME, c as WIZARD_BENCHMARK_FILE, ct as WIZARD_USE_PI_HARNESS_FLAG_KEY, f as getUI, ft as RUN_SURFACE, g as SIGNUP_WIZARD_READINESS_CONFIG, h as SERVICE_LABELS, i as enableDebugLogs, j as HAIKU_MODEL, k as GPT5_MINI_MODEL, l as WIZARD_LOG_FILE, lt as getSkillsBaseUrl, nt as WIZARD_PI_EFFORT_FLAG_KEY, o as initLogFile, pt as runtimeEnv, rt as WIZARD_PI_MODEL_FLAG_KEY, s as logToFile, st as WIZARD_USER_AGENT, t as configureLogFile, tt as WIZARD_ORCHESTRATOR_FLAG_KEY, v as getBlockingServiceKeys, w as DEFAULT_AGENT_MODEL } from "./debug-BMkCmZdo.js";
3
+ import { a as ciExcludedTaskTypes, n as analytics, r as groupsFromUser } from "./analytics-C1qxjbYZ.js";
4
+ import { t as getOrAskForProjectData } from "./setup-utils-NwB2Qj6N.js";
5
+ import { i as getLlmGatewayUrl } from "./urls-PfZ1TYOf.js";
6
+ import { n as mcpUrlFor, t as HostResolution } from "./host-resolution-UZELMVTQ.js";
7
7
  import "./wizard-session-BoXAZEOf.js";
8
- import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-CWaKUNMI.js";
9
- import { n as isNonInteractiveEnvironment } from "./environment-4zKc7kWr.js";
10
- import { c as classifySettingsConflicts, f as AgentOutputSignals, h as getWizardCommandments, i as runAgent$1, m as REMARK_INSTRUCTION, n as buildRunTags, o as backupAndFixClaudeSettings, p as AgentSignals, r as initializeAgent, s as checkAllSettingsConflicts, u as restoreClaudeSettings } from "./agent-interface-BTVAlvLW.js";
11
- import { a as installSkillById, d as TaskStatus, f as createWizardAskBridge, i as fetchSkillMenu, l as QUEUE_DIR_NAME, u as QueueStore } from "./wizard-tools-CrpRaYkE.js";
12
- import { a as flushScanReport, o as formatScanReport, p as writeScanReport, s as formatYaraAbortMessage } from "./yara-hooks-BPQ3VE-l.js";
13
- import { r as detectNodePackageManagers } from "./package-manager-CRYusNaP.js";
8
+ import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-56bo4Zhs.js";
9
+ import { n as isNonInteractiveEnvironment } from "./environment-B7DmpnXD.js";
10
+ import { c as classifySettingsConflicts, f as AgentOutputSignals, h as getWizardCommandments, i as runAgent$1, m as REMARK_INSTRUCTION, n as buildRunTags, o as backupAndFixClaudeSettings, p as AgentSignals, r as initializeAgent, s as checkAllSettingsConflicts, u as restoreClaudeSettings } from "./agent-interface-CFeI8jzW.js";
11
+ import { a as installSkillById, d as TaskStatus, f as createWizardAskBridge, i as fetchSkillMenu, l as QUEUE_DIR_NAME, u as QueueStore } from "./wizard-tools-BXd4PCO3.js";
12
+ import { a as flushScanReport, o as formatScanReport, p as writeScanReport, s as formatYaraAbortMessage } from "./yara-hooks-BZmYgr9V.js";
13
+ import { r as detectNodePackageManagers } from "./package-manager-BHAvpGav.js";
14
14
  import fs, { existsSync, rmSync } from "fs";
15
15
  import * as path$1 from "path";
16
16
  import path from "path";
@@ -548,20 +548,21 @@ const piBackend = {
548
548
  message: "pi: gateway model could not be resolved"
549
549
  };
550
550
  logToFile(`[pi] gateway ${baseUrl} model ${modelId} (${api})`);
551
- const { createSecurityExtension } = await import("./security-DX5ey9g8.js");
551
+ const { createSecurityExtension } = await import("./security-wlqqLk44.js");
552
552
  const security = createSecurityExtension({
553
553
  disallowedTools: programConfig.disallowedTools,
554
554
  triageAuth: {
555
555
  baseURL: gatewayUrl,
556
556
  authToken: boot.accessToken
557
- }
557
+ },
558
+ workingDirectory: session.installDir
558
559
  });
559
- const { prewarmYaraScanner } = await import("./yara-hooks-yy8OLJ43.js");
560
+ const { prewarmYaraScanner } = await import("./yara-hooks-D0h7Vu7A.js");
560
561
  prewarmYaraScanner();
561
562
  const extensionFactories = [security.factory];
562
563
  let mcpCleanup;
563
564
  try {
564
- const { setupPostHogMcp } = await import("./mcp-B_utnKAq.js");
565
+ const { setupPostHogMcp } = await import("./mcp-C7dn0DIC.js");
565
566
  const mcp = await setupPostHogMcp({
566
567
  agentDir: getAgentDir(),
567
568
  mcpUrl: boot.mcpUrl,
@@ -585,9 +586,9 @@ const piBackend = {
585
586
  extensionFactories
586
587
  });
587
588
  await resourceLoader.reload();
588
- const { createWizardPiTools } = await import("./tools-WDPzlrXc.js");
589
- const { createWizardPiTaskTools } = await import("./tasks-SbUmyQPS.js");
590
- const { createDispatchAgentTool } = await import("./subagent-DkTSX2ew.js");
589
+ const { createWizardPiTools } = await import("./tools-BsOywKLp.js");
590
+ const { createWizardPiTaskTools } = await import("./tasks-BfDJpHgK.js");
591
+ const { createDispatchAgentTool } = await import("./subagent-FMDybA8X.js");
591
592
  const wizardTaskTools = createWizardPiTaskTools();
592
593
  const scrubbedBash = withMode(createBashToolDefinition(session.installDir, { spawnHook: (ctx) => ({
593
594
  ...ctx,
@@ -2751,4 +2752,4 @@ var agent_runner_exports = /* @__PURE__ */ __exportAll({ runAgent: () => runAgen
2751
2752
  //#endregion
2752
2753
  export { formatTokenCount as a, formatCostUsd as i, runAgent as n, authenticate as o, computeTokenCostUsd as r, agent_runner_exports as t };
2753
2754
 
2754
- //# sourceMappingURL=agent-runner-CqNL9x2D.js.map
2755
+ //# sourceMappingURL=agent-runner-B0pWHl5j.js.map