@posthog/wizard 2.80.1 → 2.81.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.
- package/README.md +19 -17
- package/dist/README.md +6 -4
- package/dist/{agent-interface-CemBvTAA.js → agent-interface-BFzBnz10.js} +184 -25
- package/dist/agent-interface-BFzBnz10.js.map +1 -0
- package/dist/{agent-prompt-loader-DPnj6SwU.js → agent-prompt-loader-CbCW6YbP.js} +6 -6
- package/dist/{agent-prompt-loader-DPnj6SwU.js.map → agent-prompt-loader-CbCW6YbP.js.map} +1 -1
- package/dist/{analytics-D21z3E5l.js → analytics-CGGE7OSa.js} +6 -3
- package/dist/analytics-CGGE7OSa.js.map +1 -0
- package/dist/analytics-D6NAIJxP.js +2 -0
- package/dist/api-DM4uMJ00.js +2 -0
- package/dist/{api-DtGIqQ2U.js → api-ZZhiRtbb.js} +3 -2
- package/dist/{api-DtGIqQ2U.js.map → api-ZZhiRtbb.js.map} +1 -1
- package/dist/api-key-login-C30LRht2.js +2 -0
- package/dist/{api-key-login-h3E2G3zu.js → api-key-login-D0KBr1dx.js} +5 -5
- package/dist/{api-key-login-h3E2G3zu.js.map → api-key-login-D0KBr1dx.js.map} +1 -1
- package/dist/bin.js +2 -2
- package/dist/{bounded-fs-CAsuF6al.js → bounded-fs-DwL2Tasp.js} +3 -3
- package/dist/{bounded-fs-CAsuF6al.js.map → bounded-fs-DwL2Tasp.js.map} +1 -1
- package/dist/{ci-install-CEYE02t-.js → ci-install-DKXrt_ow.js} +7 -7
- package/dist/{ci-install-CEYE02t-.js.map → ci-install-DKXrt_ow.js.map} +1 -1
- package/dist/{console-log-O828zyiq.js → console-log-DJiTqYT2.js} +3 -3
- package/dist/{console-log-O828zyiq.js.map → console-log-DJiTqYT2.js.map} +1 -1
- package/dist/{debug-BfQPD2Hg.js → debug-sB20p-Ek.js} +3 -3
- package/dist/{debug-BfQPD2Hg.js.map → debug-sB20p-Ek.js.map} +1 -1
- package/dist/{emit-RfAewcWv.js → emit-BqLCdV8Y.js} +2 -1
- package/dist/{emit-RfAewcWv.js.map → emit-BqLCdV8Y.js.map} +1 -1
- package/dist/{errors-CHrJzAZb.js → errors-DhFeR8Sp.js} +3 -3
- package/dist/{errors-CHrJzAZb.js.map → errors-DhFeR8Sp.js.map} +1 -1
- package/dist/{gateway-D3gIlR6b.js → gateway-Dg4Fm-nC.js} +7 -7
- package/dist/{gateway-D3gIlR6b.js.map → gateway-Dg4Fm-nC.js.map} +1 -1
- package/dist/{headless-DIX68UUA.js → headless-B3Ol1nkU.js} +1 -1
- package/dist/{headless-BJjhP0SI.js → headless-DhTDC9ga.js} +2 -2
- package/dist/{headless-BJjhP0SI.js.map → headless-DhTDC9ga.js.map} +1 -1
- package/dist/{host-resolution-D8GhEVoX.js → host-resolution-DDYJXiAQ.js} +4 -4
- package/dist/{host-resolution-D8GhEVoX.js.map → host-resolution-DDYJXiAQ.js.map} +1 -1
- package/dist/{install-Bw8tvDvS.js → install-CZeR38cD.js} +1 -1
- package/dist/{install-paPCzPHI.js → install-Dh8Z5R15.js} +41 -55
- package/dist/install-Dh8Z5R15.js.map +1 -0
- package/dist/{interactive-tdxZKWax.js → interactive-hqHk6kz4.js} +3 -3
- package/dist/{interactive-tdxZKWax.js.map → interactive-hqHk6kz4.js.map} +1 -1
- package/dist/{links-Cyj6v8kg.js → links-rMot83W2.js} +3 -3
- package/dist/{links-Cyj6v8kg.js.map → links-rMot83W2.js.map} +1 -1
- package/dist/{main-0p-YxUNa.js → main-DNIeasXx.js} +25 -25
- package/dist/main-DNIeasXx.js.map +1 -0
- package/dist/{mcp-D3Z5MXhA.js → mcp-SZcGfaOz.js} +3 -3
- package/dist/{mcp-D3Z5MXhA.js.map → mcp-SZcGfaOz.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-COVbWnHt.js → mcp-prompt-streaming-BuRMBgbo.js} +5 -5
- package/dist/{mcp-prompt-streaming-COVbWnHt.js.map → mcp-prompt-streaming-BuRMBgbo.js.map} +1 -1
- package/dist/{models-D1SnCzvl.js → models-G1j-wgwe.js} +2 -2
- package/dist/{models-D1SnCzvl.js.map → models-G1j-wgwe.js.map} +1 -1
- package/dist/{non-interactive-DxRBc73O.js → non-interactive-DkldwWDM.js} +4 -4
- package/dist/{non-interactive-DxRBc73O.js.map → non-interactive-DkldwWDM.js.map} +1 -1
- package/dist/{oauth-session-Dde79Ycm.js → oauth-session-C2fVSGfr.js} +2 -2
- package/dist/{oauth-session-Dde79Ycm.js.map → oauth-session-C2fVSGfr.js.map} +1 -1
- package/dist/{orchestrator-tools-BSNhzzkT.js → orchestrator-tools-BchqG7Qi.js} +3 -3
- package/dist/{orchestrator-tools-BSNhzzkT.js.map → orchestrator-tools-BchqG7Qi.js.map} +1 -1
- package/dist/{package-manager-BRXlXo0p.js → package-manager-B3_qSybg.js} +4 -4
- package/dist/{package-manager-BRXlXo0p.js.map → package-manager-B3_qSybg.js.map} +1 -1
- package/dist/{pi-cM_4Gd_9.js → pi-Cw7CPXSl.js} +15 -15
- package/dist/{pi-cM_4Gd_9.js.map → pi-Cw7CPXSl.js.map} +1 -1
- package/dist/{playground-DTKy0XHT.js → playground-CPNrQfb3.js} +4 -4
- package/dist/{playground-DTKy0XHT.js.map → playground-CPNrQfb3.js.map} +1 -1
- package/dist/{posthog-integration-bwHwE87a.js → posthog-integration-FAWfClH3.js} +63 -50
- package/dist/posthog-integration-FAWfClH3.js.map +1 -0
- package/dist/programs-DjCmEcxb.js +3 -0
- package/dist/{programs-SeT1qNik.js → programs-Ml6OLybj.js} +88 -34
- package/dist/programs-Ml6OLybj.js.map +1 -0
- package/dist/{provisioning-JbJtq7hq.js → provisioning-BTVL49jD.js} +4 -4
- package/dist/{provisioning-JbJtq7hq.js.map → provisioning-BTVL49jD.js.map} +1 -1
- package/dist/provisioning-DL0xCJXH.js +2 -0
- package/dist/{queue-tools-BIZSJyt6.js → queue-tools-BQfRI_uD.js} +4 -4
- package/dist/{queue-tools-BIZSJyt6.js.map → queue-tools-BQfRI_uD.js.map} +1 -1
- package/dist/{results-DtKICAdz.js → results-ycm_Tf31.js} +37 -2
- package/dist/results-ycm_Tf31.js.map +1 -0
- package/dist/{run-CDurA5tO.js → run-Bgg-ctbA.js} +31 -15
- package/dist/run-Bgg-ctbA.js.map +1 -0
- package/dist/{run-DDCvLILJ.js → run-C6Ei6vvb.js} +10 -10
- package/dist/run-C6Ei6vvb.js.map +1 -0
- package/dist/{run-tool-DML8JJvt.js → run-tool-CrN2xWzU.js} +32 -16
- package/dist/{run-tool-DML8JJvt.js.map → run-tool-CrN2xWzU.js.map} +1 -1
- package/dist/run-tool-tNjm8S-E.js +2 -0
- package/dist/{runners-Dhu4pZTN.js → runners-GGYJs0Ny.js} +37 -19
- package/dist/runners-GGYJs0Ny.js.map +1 -0
- package/dist/{security-DEH8Rn0_.js → security-C36p8lu6.js} +44 -52
- package/dist/security-C36p8lu6.js.map +1 -0
- package/dist/{setup-utils-C5-WM-hS.js → setup-utils-Dfgse6pY.js} +3 -3
- package/dist/{setup-utils-C5-WM-hS.js.map → setup-utils-Dfgse6pY.js.map} +1 -1
- package/dist/{start-playground-DL4mVEUx.js → start-playground-D0TrdLmc.js} +7 -6
- package/dist/{start-playground-DL4mVEUx.js.map → start-playground-D0TrdLmc.js.map} +1 -1
- package/dist/{subagent-CnFvAq6E.js → subagent-DR5caRzL.js} +7 -7
- package/dist/subagent-DR5caRzL.js.map +1 -0
- package/dist/{task-BOScSm4W.js → task-C1qKgsAf.js} +18 -17
- package/dist/task-C1qKgsAf.js.map +1 -0
- package/dist/{telemetry-CP4rGicd.js → telemetry-Ch9ZJvm7.js} +2 -2
- package/dist/{telemetry-CP4rGicd.js.map → telemetry-Ch9ZJvm7.js.map} +1 -1
- package/dist/{terminal-J3OD3dN0.js → terminal-D20JFN3J.js} +97 -71
- package/dist/terminal-D20JFN3J.js.map +1 -0
- package/dist/{tools-CCMV3DmB.js → tools-BZseGChD.js} +15 -15
- package/dist/{tools-CCMV3DmB.js.map → tools-BZseGChD.js.map} +1 -1
- package/dist/{tools-Cz8tDptL.js → tools-CmSMclOD.js} +8 -8
- package/dist/{tools-Cz8tDptL.js.map → tools-CmSMclOD.js.map} +1 -1
- package/dist/{tui-Csf7VJxt.js → tui-BXe1D0tc.js} +4 -4
- package/dist/{tui-Csf7VJxt.js.map → tui-BXe1D0tc.js.map} +1 -1
- package/dist/{tui-BUZ2PWqN.js → tui-DJf3ZPRh.js} +1 -1
- package/dist/{upload-step-CEacB2RV.js → upload-step-BlAN7MC5.js} +3 -3
- package/dist/{upload-step-CEacB2RV.js.map → upload-step-BlAN7MC5.js.map} +1 -1
- package/dist/{urls-BDELGZHq.js → urls-eFaChZ9d.js} +2 -2
- package/dist/{urls-BDELGZHq.js.map → urls-eFaChZ9d.js.map} +1 -1
- package/dist/{wizard-abort-D3-75KyV.js → wizard-abort-BX3O4Hg4.js} +5 -5
- package/dist/{wizard-abort-D3-75KyV.js.map → wizard-abort-BX3O4Hg4.js.map} +1 -1
- package/dist/{yara-hooks-D8YDyX7o.js → yara-hooks-C4HOysrM.js} +4 -4
- package/dist/{yara-hooks-D8YDyX7o.js.map → yara-hooks-C4HOysrM.js.map} +1 -1
- package/dist/{yara-hooks-CZ1tKxy_.js → yara-hooks-DV4EkkZY.js} +1 -1
- package/package.json +2 -1
- package/dist/agent-interface-CemBvTAA.js.map +0 -1
- package/dist/analytics-B3ROGuIr.js +0 -2
- package/dist/analytics-D21z3E5l.js.map +0 -1
- package/dist/api-IhROOQNv.js +0 -2
- package/dist/api-key-login-OEntFvu5.js +0 -2
- package/dist/install-paPCzPHI.js.map +0 -1
- package/dist/main-0p-YxUNa.js.map +0 -1
- package/dist/posthog-integration-bwHwE87a.js.map +0 -1
- package/dist/programs-BhtyA3lE.js +0 -3
- package/dist/programs-SeT1qNik.js.map +0 -1
- package/dist/provisioning-BcXfeCMc.js +0 -2
- package/dist/results-DtKICAdz.js.map +0 -1
- package/dist/run-CDurA5tO.js.map +0 -1
- package/dist/run-DDCvLILJ.js.map +0 -1
- package/dist/run-tool-CTq9OOdT.js +0 -2
- package/dist/runners-Dhu4pZTN.js.map +0 -1
- package/dist/security-DEH8Rn0_.js.map +0 -1
- package/dist/subagent-CnFvAq6E.js.map +0 -1
- package/dist/task-BOScSm4W.js.map +0 -1
- package/dist/terminal-J3OD3dN0.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"results-DtKICAdz.js","names":[],"sources":["../src/shared/mcp-clients/results.ts"],"sourcesContent":["/**\n * Per-client outcome of an MCP server / plugin install or removal.\n *\n * \"Nothing to do\" is a first-class outcome rather than a silent no-op:\n * re-running `mcp add` on a machine that's already set up is the common case,\n * and collapsing it into an empty result set is what made the flow report\n * \"Installation skipped.\" with no explanation. Failures carry a short reason\n * for the same reason — an empty list tells the user nothing.\n *\n * The names are action-neutral because both `mcp add` and `mcp remove` report\n * through them: `Changed` is \"installed\" or \"removed\" depending on the flow.\n */\n\nexport enum McpClientStatus {\n /** We made the change — wrote the config, installed or removed the server. */\n Changed = 'changed',\n /** Already in the requested state; nothing was touched. */\n Unchanged = 'unchanged',\n Failed = 'failed',\n}\n\nexport interface McpClientResult {\n name: string;\n status: McpClientStatus;\n /** Short, user-facing explanation. Set for failures. */\n detail?: string;\n}\n\n/** Result shape every client's addServer/removeServer/installPlugin returns. */\nexport interface InstallResult {\n success: boolean;\n /** Already in the requested state — nothing was written or removed. */\n alreadyInstalled?: boolean;\n /** Raw failure text from the underlying CLI or filesystem error. */\n reason?: string;\n}\n\n/**\n * Failure text comes from CLIs we invoke with the user's personal API key on the\n * command line (`--header \"Authorization: Bearer phx_...\"`), so it can carry the\n * key into the log file, the TUI and exception reports. Mask it first.\n *\n * The plugin stages clone git repositories and read the user's own config, so\n * the text reaching here is no longer only ours: a failing clone echoes the\n * remote URL with any credentials embedded in it, and a config error can quote\n * a foreign provider's key. Orchestrator task remarks reach analytics through\n * here too, from a step that collects database and API credentials. Each\n * pattern below is a shape one of these sources can carry, not a guess at\n * every possible secret.\n */\nexport const redactSecrets = (raw: string): string =>\n raw\n .replace(/Bearer\\s+\\S+/gi, 'Bearer [redacted]')\n .replace(/ph[xspc]_[A-Za-z0-9_-]+/g, '[redacted]')\n // `https://user:token@github.com/...` — git prints the whole remote back;\n // `postgres://user:password@host/db` is the same shape for a database.\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/)[^/\\s:@]+:[^/\\s@]+@/gi, '$1[redacted]@')\n // GitHub tokens, which a clone failure quotes verbatim.\n .replace(/\\bgh[pousr]_[A-Za-z0-9]{16,}/g, '[redacted]')\n .replace(/\\bgithub_pat_[A-Za-z0-9_]{20,}/g, '[redacted]')\n // OpenAI keys: `OPENAI_API_KEY` is already a failure codex reports on.\n .replace(/\\bsk-[A-Za-z0-9_-]{16,}/g, '[redacted]')\n // Stripe secret and restricted keys, which the warehouse step collects.\n .replace(/\\b[rs]k_(?:live|test)_[A-Za-z0-9]{16,}/g, '[redacted]')\n // Google keys, which a config error quotes the same way.\n .replace(/\\bAIza[A-Za-z0-9_-]{16,}/g, '[redacted]')\n // The catch-all for providers we have no shape for: a config error prints\n // the offending line, and the variable's own name says it holds a secret.\n // The name is kept — `OPENAI_API_KEY` is wording an expected-failure hint\n // matches on, and masking it would turn that hint back into an exception.\n .replace(\n /\\b([A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIALS?)[A-Za-z0-9_]*)(\\s*[=:]\\s*)[\"']?[A-Za-z0-9_\\-./+]{8,}[\"']?/gi,\n '$1$2[redacted]',\n );\n\n/**\n * Replace the user's home directory with `~`. Error tracking fingerprints on\n * the message, so a path that differs per user splits one root cause into one\n * issue per user — and the username itself never needs to leave the machine.\n */\nexport const scrubHomePaths = (raw: string): string =>\n raw\n .replace(/\\/(?:Users|home)\\/[^/\\s'\"]+/g, '~')\n .replace(/[A-Za-z]:\\\\Users\\\\[^\\\\\\s'\"]+/gi, '~');\n\n/**\n * A failure that lives entirely in the user's environment: a CLI too old, a\n * broken install, an unreadable config. We can't fix these, so we hand back a\n * hint instead of filing an issue nobody can action.\n *\n * Silencing a failure is the expensive direction: a pattern that matches too\n * much retires a real bug of ours into a hint nobody reads. So an entry says\n * where it applies (`stages`) and what disqualifies it (`unless`), and anything\n * it cannot claim confidently goes back to being reported.\n */\nexport interface ExpectedFailure {\n match: RegExp;\n /**\n * Stages this hint is allowed to fire on. Omitted means any stage. Wording\n * that means one thing during `plugin install` can mean something else during\n * `mcp add`, and a hint naming the wrong command is worse than none.\n */\n stages?: string[];\n /**\n * Wording that disqualifies the match even when `match` hits — the tell that\n * the failure is ours, not the environment's.\n */\n unless?: RegExp;\n /** One short line, shown to the user as the failure detail. */\n hint: string;\n}\n\n/**\n * The first matching hint, or undefined when the failure is worth reporting.\n *\n * `stage` is the caller's name for what it was doing. Passing it lets an entry\n * narrow itself; omitting it only considers entries that claim every stage,\n * because an unscoped call cannot honour a scoped entry.\n */\nexport const expectedFailureHint = (\n text: string,\n table: ExpectedFailure[],\n stage?: string,\n): string | undefined => {\n if (!text.trim()) return undefined;\n return table.find(\n (f) =>\n (f.stages === undefined ||\n (stage !== undefined && f.stages.includes(stage))) &&\n f.match.test(text) &&\n !f.unless?.test(text),\n )?.hint;\n};\n\n/** First non-empty line of an error, trimmed to something a TUI line can hold. */\nexport const summarizeFailure = (raw?: string): string | undefined => {\n const line = redactSecrets(raw ?? '')\n .split('\\n')\n .map((l) => l.trim())\n .find((l) => l.length > 0);\n if (!line) return undefined;\n return line.length > 120 ? `${line.slice(0, 117)}...` : line;\n};\n\nexport const toClientResult = (\n name: string,\n result: InstallResult | undefined,\n): McpClientResult => {\n if (!result?.success) {\n return {\n name,\n status: McpClientStatus.Failed,\n detail: summarizeFailure(result?.reason),\n };\n }\n return {\n name,\n status: result.alreadyInstalled\n ? McpClientStatus.Unchanged\n : McpClientStatus.Changed,\n };\n};\n\nexport const isOk = (r: McpClientResult): boolean =>\n r.status !== McpClientStatus.Failed;\n\nexport const namesWithStatus = (\n results: McpClientResult[],\n status: McpClientStatus,\n): string[] => results.filter((r) => r.status === status).map((r) => r.name);\n"],"mappings":";;;;;;;;;;;;;;AAkDA,MAAa,iBAAiB,QAC5B,IACG,QAAQ,kBAAkB,oBAAoB,CAC9C,QAAQ,4BAA4B,aAAa,CAGjD,QAAQ,mDAAmD,gBAAgB,CAE3E,QAAQ,iCAAiC,aAAa,CACtD,QAAQ,mCAAmC,aAAa,CAExD,QAAQ,4BAA4B,aAAa,CAEjD,QAAQ,2CAA2C,aAAa,CAEhE,QAAQ,6BAA6B,aAAa,CAKlD,QACC,gIACA,iBACD;;;;;;AAOL,MAAa,kBAAkB,QAC7B,IACG,QAAQ,gCAAgC,IAAI,CAC5C,QAAQ,kCAAkC,IAAI;;;;;;;;AAoCnD,MAAa,uBACX,MACA,OACA,UACuB;AACvB,KAAI,CAAC,KAAK,MAAM,CAAE,QAAO,KAAA;AACzB,QAAO,MAAM,MACV,OACE,EAAE,WAAW,KAAA,KACX,UAAU,KAAA,KAAa,EAAE,OAAO,SAAS,MAAM,KAClD,EAAE,MAAM,KAAK,KAAK,IAClB,CAAC,EAAE,QAAQ,KAAK,KAAK,CACxB,EAAE;;;AAIL,MAAa,oBAAoB,QAAqC;CACpE,MAAM,OAAO,cAAc,OAAO,GAAG,CAClC,MAAM,KAAK,CACX,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,MAAM,MAAM,EAAE,SAAS,EAAE;AAC5B,KAAI,CAAC,KAAM,QAAO,KAAA;AAClB,QAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO;;AAG1D,MAAa,kBACX,MACA,WACoB;AACpB,KAAI,CAAC,QAAQ,QACX,QAAO;EACL;EACA,QAAA;EACA,QAAQ,iBAAiB,QAAQ,OAAO;EACzC;AAEH,QAAO;EACL;EACA,QAAQ,OAAO,mBAAA,cAAA;EAGhB;;AAGH,MAAa,QAAQ,MACnB,EAAE,WAAA;AAEJ,MAAa,mBACX,SACA,WACa,QAAQ,QAAQ,MAAM,EAAE,WAAW,OAAO,CAAC,KAAK,MAAM,EAAE,KAAK"}
|
package/dist/run-CDurA5tO.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-CDurA5tO.js","names":[],"sources":["../src/tui/workflow.ts","../src/tui/run.ts"],"sourcesContent":["/** The TUI's answers to the steps `runProgram` waits on: its screens, gates and overlays. */\nimport { RunOutcome } from '@programs';\nimport type { ProgramId, ProgramWorkflowConnector } from '@programs/types';\nimport type { WizardStore } from './store.js';\n\nexport function tuiWorkflow(\n store: WizardStore,\n programId: ProgramId,\n): ProgramWorkflowConnector {\n return {\n async confirmStep(step) {\n switch (step.kind) {\n case 'ai-approval':\n // AiOptInRequiredScreen holds here, before any source leaves the machine.\n await store.getGate('ai-opt-in');\n return true;\n case 'service-outage':\n // The health-check screen shows the outage; the run stops.\n store.setReadinessResult(step.readiness);\n return false;\n case 'settings-conflict':\n await store.showSettingsOverride(step.conflicts, step.fix);\n return true;\n case 'run':\n // Each screen before the run's step settles first; a hidden step doesn't run.\n return store.reachStep(step.stepId);\n }\n },\n finishStep(step, result) {\n // Only a composed sub-run that succeeded completes its step; a failed one keeps its tasks for the outro and the stream. The program's run step follows its phase.\n if (\n step.programId !== programId &&\n result.outcome === RunOutcome.Success\n ) {\n store.completeRunStep(step.stepId);\n }\n },\n };\n}\n","/**\n * The TUI host: the full-screen wizard. It renders its screens over its own\n * store, settles the intro, and calls `runProgram` once with the OAuth login,\n * the WizardAsk screen as the answerer and its screens as the workflow. An\n * intro that hands off to a tool drives the tool's screens instead. The CLI\n * builds the launch values, owns the signals and applies the exit code.\n */\nimport { join } from 'node:path';\nimport {\n buildSession,\n createFileDestination,\n createWizardRunSync,\n getAuditChecks,\n getProgramConfig,\n loadWizardFlags,\n PostHogDestination,\n ProgramAbort,\n RunOutcome,\n runProgram,\n storeInteraction,\n TaskStreamPush,\n} from '@programs';\nimport type { CredentialsProvider, ProgramConfig } from '@programs/types';\nimport { classifyRunFailure } from '@shared/errors';\nimport { OutroKind } from '@shared/outro';\nimport { RunPhase } from '@shared/run-state';\nimport { VERSION } from '@shared/version';\nimport { analytics } from '@utils/analytics';\nimport { runCleanups } from '@utils/cleanup';\nimport { initLogFile, logToFile } from '@utils/debug';\nimport { flushAnalytics } from '@utils/flush-analytics';\nimport {\n registerShutdown,\n startHostExit,\n wizardAbort,\n} from '@host/wizard-abort';\nimport { printAbortOutro } from '@shared/console-log';\nimport { getTuiTool } from '@tui/tools/index';\nimport { abortOnScreens } from './abort.js';\nimport { displayProgress } from './agent-progress.js';\nimport { oauthCredentials } from './auth/login.js';\nimport { isRunFailure } from './mint-failure.js';\nimport { driveTuiTool, localServicesError, reportFatal } from './run-tool.js';\nimport { startTUI } from './start-tui.js';\nimport type { WizardStore } from './store.js';\nimport type { TuiLaunch } from './launch.js';\nimport { tuiWorkflow } from './workflow.js';\n\n/**\n * Run `config` in the TUI. Resolves with the exit code: the run's, a screen's\n * exit request, a decided failure's through `wizardAbort`, or 130 or 143 on a signal.\n */\nexport async function runTui(\n config: ProgramConfig,\n launch: TuiLaunch,\n): Promise<number> {\n const exit = startHostExit();\n let tui: ReturnType<typeof startTUI> | null = null;\n let stream: TaskStreamPush | null = null;\n let unregisterShutdown: (() => void) | undefined;\n // A signal cancels the run and any pending question before the exit.\n const runAbort = new AbortController();\n let exitInProgress = false;\n let signalled = false;\n // A tool the intro handed off to ends the run once its analytics flush.\n let handedOff = false;\n\n // Flush a terminal-phase push so the web app sees the run ended in error\n // rather than hanging on the last \"running\" snapshot. Registered before the\n // stream exists: Ctrl-C on the intro must still restore the terminal and run\n // the cleanups, and there is no run to report yet.\n const onSignal = (signal: 'SIGINT' | 'SIGTERM'): void => {\n if (signalled || exitInProgress || exit.ended) return;\n signalled = true;\n runAbort.abort();\n logToFile('[run-wizard] signal received, flushing task stream');\n // Settings restore is sync fs work, so it runs before a flush that may time out.\n runCleanups();\n if (tui?.store.session.runPhase === RunPhase.Running) {\n tui.store.setRunPhase(RunPhase.Error);\n }\n void Promise.all([\n stream?.shutdown(2000, 'cancelled'),\n analytics.shutdown('cancelled'),\n ])\n .catch(() => logToFile('[run-wizard] cancellation shutdown failed'))\n .finally(() => {\n unregisterShutdown?.();\n try {\n tui?.unmount();\n } catch {\n // terminal may already be torn down\n }\n exit.end(signal === 'SIGTERM' ? 143 : 130);\n });\n };\n const onAbort = () =>\n onSignal(launch.signal.reason === 'SIGTERM' ? 'SIGTERM' : 'SIGINT');\n // A signal that landed before this host subscribed ends the run too.\n if (launch.signal.aborted) onAbort();\n else launch.signal.addEventListener('abort', onAbort, { once: true });\n\n // Before the TUI mounts: once Ink owns the alt screen, anything written\n // to it is wiped on unmount (see the catch block below), so an abort here\n // would leave the user on a loading screen with no message.\n const localError = await localServicesError(launch.session.baseUrl);\n if (signalled) return exit.exited;\n if (localError) {\n wizardAbort(\n { present: printAbortOutro, exit },\n { message: localError },\n ).catch((error: unknown) => exit.fail(error));\n return exit.exited;\n }\n\n const main = async (): Promise<void> => {\n // Ink handles Ctrl-C itself in raw mode; it arrives here as an interrupt.\n tui = startTUI(VERSION, config.id, () => onSignal('SIGINT'));\n const activeTui = tui;\n const { store } = activeTui;\n // A screen's exit request ends the run with no shutdown; start-tui's exit listener unmounts.\n store.subscribe(() => {\n if (store.exitRequest !== null && !handedOff) exit.end(store.exitRequest);\n });\n\n const session = buildSession(launch.session);\n if (launch.skillId) {\n session.skillId = launch.skillId;\n } else if (config.skillId) {\n session.skillId = config.skillId;\n }\n store.launch(session, launch.session, config.id);\n\n const credentials = launch.credentials ?? oauthCredentials(store);\n launch.onStore?.(store);\n\n // Detection, then the intro, where the user may switch program.\n for (;;) {\n try {\n await store.runReadyHooks();\n } catch (error) {\n if (!(error instanceof ProgramAbort)) throw error;\n await abortOnScreens(store, {\n code: error.code,\n message: error.message,\n });\n }\n await store.getGate('intro');\n const active = store.router.activeProgram;\n if (active === config.id) break;\n const tool = getTuiTool(active);\n if (tool) {\n handedOff = true;\n const code = await driveTuiTool(store, tool, {\n toolId: active,\n signal: runAbort.signal,\n });\n if (signalled || exit.ended) return;\n exitInProgress = true;\n launch.signal.removeEventListener('abort', onAbort);\n activeTui.unmount();\n await flushAnalytics();\n exit.end(code);\n return;\n }\n config = getProgramConfig(active);\n }\n\n // After the switch loop, not before: the stream bakes its program id,\n // session id, and event-plan path in at construction, so a stream built\n // for the launch program would report the whole run under a program the\n // user left on the intro screen. Nothing before this point produces a\n // task to push.\n // Consent gates the push, not the dump: `--no-telemetry` still logs.\n const fileDestination = createFileDestination(launch.taskStreamLog);\n const destinations = [\n ...(session.noTelemetry\n ? []\n : [\n new PostHogDestination({\n getCredentials: () => store.session.credentials,\n onError: (err) => logToFile('[task-stream-push]', err.message),\n }),\n ]),\n ...(fileDestination ? [fileDestination] : []),\n ];\n const programConfig = config;\n const activeStream = new TaskStreamPush({\n store: store.sessions,\n getFlags: () => analytics.getCachedWizardFlags(),\n programId: programConfig.streamWorkflowId ?? programConfig.id,\n runSync: createWizardRunSync({\n mode: 'local',\n programId: programConfig.id,\n assignedId: launch.runId,\n noTelemetry: session.noTelemetry,\n getSession: () => store.session,\n }),\n destinations,\n eventPlanPath: programConfig.eventPlanFile\n ? join(session.installDir, programConfig.eventPlanFile)\n : undefined,\n auditChecks: programConfig.auditLedgerFile\n ? () => getAuditChecks(store.session)\n : undefined,\n enabled: destinations.length > 0,\n });\n stream = activeStream;\n activeStream.attach();\n unregisterShutdown = registerShutdown((outcome) => {\n if (store.session.runPhase === RunPhase.Running) {\n store.setRunPhase(RunPhase.Error);\n }\n return activeStream.shutdown(2000, outcome);\n });\n\n await store.getGate('integration-check');\n await store.getGate('health-check');\n\n try {\n await runProgramOnScreens(programConfig, store, {\n credentials,\n signal: runAbort.signal,\n });\n } catch (error) {\n // The run threw before its own error handling rendered an outro.\n // Show the handoff screen and let the user's agent take over.\n const failure = classifyRunFailure(error);\n logToFile('[run-wizard] run failed, handing off:', error);\n runCleanups();\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { error_code: failure.code },\n );\n store.showOutroError({\n kind: OutroKind.Error,\n errorCode: failure.code,\n message: failure.message,\n });\n }\n\n if (signalled) return;\n const runFailed = isRunFailure(store.session);\n await activeStream.finishRun(runFailed ? 'failed' : 'completed');\n await store.waitUntil((s) => s.mintHandoff === 'exit' || s.skillsComplete);\n // A screen already ended the run (KeepSkills after a success): start no flush it would cut off.\n if (exit.ended) return;\n\n exitInProgress = true;\n await activeStream.shutdown(2000);\n unregisterShutdown?.();\n launch.signal.removeEventListener('abort', onAbort);\n if (runFailed) await analytics.shutdown('error');\n activeTui.unmount();\n exit.end(runFailed ? 1 : 0);\n };\n\n main().catch(async (err: unknown) => {\n if (signalled || exit.ended) return;\n // File-log first — the cleanup below can throw or exit.\n logToFile('[run-wizard] FATAL:', err);\n // Run cleanups before anything async so settings are restored even if\n // the stream shutdown hangs.\n runCleanups();\n // The task-stream debounce timer keeps the event loop alive, so\n // we have to drain it before exiting on the error path.\n exitInProgress = true;\n launch.signal.removeEventListener('abort', onAbort);\n try {\n await stream?.shutdown(2000, 'failed');\n } catch {\n // ignore\n }\n unregisterShutdown?.();\n try {\n tui?.unmount();\n } catch {\n // ignore\n }\n reportFatal(err);\n exit.end(1);\n });\n\n return exit.exited;\n}\n\n/**\n * The program's run, answered by the screens: `credentials` logs in, the\n * WizardAsk screen answers, and the flow's screens settle each step. A failure\n * shows its outro and ends the run through `wizardAbort`.\n */\nexport async function runProgramOnScreens(\n config: ProgramConfig,\n store: WizardStore,\n {\n credentials,\n signal,\n }: { credentials: CredentialsProvider; signal?: AbortSignal },\n): Promise<void> {\n initLogFile();\n logToFile(`[agent-runner] START ${config.id} build=${analytics.build}`);\n\n // runProgram turns a throwing capability into a failed run; the TUI shows\n // the handoff for those, so the throw is kept and rethrown.\n let capabilityFailure: { error: unknown } | undefined;\n const keepFailure = <T>(work: Promise<T>): Promise<T> =>\n work.catch((error: unknown) => {\n capabilityFailure ??= { error };\n throw error;\n });\n const result = await runProgram(\n config.id,\n { store: store.sessions, config },\n {\n credentials: {\n resolve: (programId, context) =>\n keepFailure(credentials.resolve(programId, context)),\n },\n interaction: storeInteraction(store.sessions),\n workflow: tuiWorkflow(store, config.id),\n onProgress: displayProgress(store),\n featureFlags: () => keepFailure(loadWizardFlags()),\n signal,\n },\n );\n if (capabilityFailure) throw capabilityFailure.error;\n // A signal cancelled the run; the handler that aborted it settles.\n if (signal?.aborted) return;\n\n if (result.outcome === RunOutcome.Crashed) throw result.failure?.error;\n if (result.outcome !== RunOutcome.Success) {\n if (result.failure?.authErrorDetail) {\n store.showAuthError(result.failure.authErrorDetail);\n }\n // The terminal status follows how the run ended, not whether an Error came back.\n await abortOnScreens(store, {\n ...result.failure,\n status: result.outcome === RunOutcome.Aborted ? 'cancelled' : 'error',\n });\n return;\n }\n // The run already succeeded: a failed flush is logged, never the outcome.\n try {\n await analytics.shutdown('success');\n } catch (error) {\n logToFile('[agent-runner] analytics shutdown failed:', error);\n }\n}\n"],"mappings":"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"}
|
package/dist/run-DDCvLILJ.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-DDCvLILJ.js","names":[],"sources":["../src/shared/ci-gateway.ts","../src/headless/renderers/progress-log.ts","../src/headless/renderers/logging-ui.ts","../src/headless/run.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { IS_PRODUCTION_BUILD, runtimeEnv } from '@env';\nimport type { GatewayCredential } from '@shared/api';\nimport type { CloudRegion } from '@utils/types';\n\n/** The pre-issued gateway token a dev or test `--ci` run uses, read once and then cleared from the environment. */\nexport function readCiGatewayCredential(\n region: CloudRegion,\n): GatewayCredential {\n if (IS_PRODUCTION_BUILD)\n throw new Error('CI gateway auth requires a non-production build');\n const path = runtimeEnv('WIZARD_CI_GATEWAY_TOKEN_FILE');\n if (!path) throw new Error('WIZARD_CI_GATEWAY_TOKEN_FILE is required for CI');\n const token = readFileSync(path, 'utf8');\n delete process.env.WIZARD_CI_GATEWAY_TOKEN_FILE;\n return {\n token,\n url:\n runtimeEnv('WIZARD_CI_GATEWAY_URL') ||\n `https://ai-gateway.${region}.posthog.com`,\n };\n}\n","/** A run's progress as log lines: one `LoggingUI` call per event that has something to print. */\nimport type { SpinnerHandle } from '@agent/types';\nimport type { ProgramProgress } from '@programs/types';\nimport type { LoggingUI } from './logging-ui';\n\nexport function logProgress(\n log: LoggingUI,\n): (progress: ProgramProgress) => void {\n let spinner: SpinnerHandle | undefined;\n return ({ event }) => {\n switch (event.kind) {\n case 'lifecycle':\n if (event.phase === 'completed') log.outro(event.message);\n return;\n case 'spinner': {\n const handle = (spinner ??= log.spinner());\n handle[event.action](event.message);\n return;\n }\n case 'log':\n log.log[event.level](event.message);\n return;\n case 'status':\n log.pushStatus(event.message);\n return;\n case 'tasks':\n log.syncTodos(event.tasks);\n return;\n case 'authError':\n log.showAuthError(event.detail);\n return;\n case 'stage':\n case 'url':\n case 'usage':\n case 'finalCost':\n case 'handoff':\n case 'completion':\n case 'binding':\n return;\n case 'activity':\n // Step lines belong to the caller that asked for them, not the run UI.\n return;\n default: {\n const unhandled: never = event;\n throw new Error(\n `Unhandled agent progress: ${JSON.stringify(unhandled)}`,\n );\n }\n }\n };\n}\n","/* eslint-disable no-console */\n/**\n * LoggingUI — Logging-only implementation for CI mode.\n * No prompts, no TUI, no interactivity. Just console output.\n */\n\nimport { consoleLog } from '@shared/console-log';\nimport { TaskStatus } from '@shared/task-status';\nimport type { AuthErrorDetail, SpinnerHandle } from '@agent/types';\n\nexport class LoggingUI {\n intro = (message: string): void => consoleLog.intro(message);\n outro = (message: string): void => consoleLog.outro(message);\n log = consoleLog.log;\n\n spinner(): SpinnerHandle {\n return {\n start(message?: string) {\n if (message) console.log(`◌ ${message}`);\n },\n stop(message?: string) {\n if (message) console.log(`● ${message}`);\n },\n message(msg?: string) {\n if (msg) console.log(`◌ ${msg}`);\n },\n };\n }\n\n pushStatus(message: string): void {\n console.log(`◇ ${message}`);\n }\n\n showAuthError(detail?: AuthErrorDetail): void {\n console.log(`✖ Authentication failed (401)`);\n if (detail?.hasSettingsConflict) {\n console.log(\n `│ Claude Code auth is conflicting with the wizard. Please try again after logging out:`,\n );\n console.log(`│ claude auth logout`);\n } else {\n console.log(\n `│ The PostHog LLM Gateway rejected the API key. Common causes:`,\n );\n console.log(\n `│ - Wrong key type: pass a personal API key (phx_xxx). pha_ is an OAuth access token, phc_ is a project key.`,\n );\n console.log(\n `│ - Missing scope: the personal API key needs the \"llm_gateway:read\" scope.`,\n );\n console.log(`│ - Expired or revoked key.`);\n console.log(\n `│ - Region mismatch: --region must match the region the key was issued in (us vs eu).`,\n );\n }\n if (detail?.logFilePath) {\n console.log(`│ Verbose log: ${detail.logFilePath}`);\n }\n }\n\n private lastTodoLine = '';\n\n syncTodos(\n todos: Array<{\n id?: string;\n source?: string;\n content: string;\n status: string;\n activeForm?: string;\n }>,\n ): void {\n const completed = todos.filter(\n (t) => t.status === TaskStatus.Completed,\n ).length;\n const active = todos.filter((t) => t.status === TaskStatus.InProgress);\n if (active.length === 0) return;\n const labels = active.map((t) => t.activeForm || t.content).join(' · ');\n const line = `◌ [${completed}/${todos.length}] ${labels}`;\n // The queue re-renders on every transition; print only what changed.\n if (line === this.lastTodoLine) return;\n this.lastTodoLine = line;\n console.log(line);\n }\n}\n","/**\n * The headless host: one program run with no screens. It builds its own\n * session store, prints progress as log lines, streams the run's state, and\n * calls `runProgram`. The CLI builds the launch values and owns the signals.\n */\nimport { join } from 'node:path';\nimport {\n apiKeyCredentials,\n buildSession,\n createFileDestination,\n createWizardRunSync,\n getAuditChecks,\n loadWizardFlags,\n PostHogDestination,\n RunOutcome,\n runProgram,\n SessionStore,\n TaskStreamPush,\n} from '@programs';\nimport type {\n ProgramConfig,\n SessionArgs,\n TaskStreamOutcome,\n} from '@programs/types';\nimport { readCiGatewayCredential } from '@shared/ci-gateway';\nimport { ErrorCodes, classifyRunFailure } from '@shared/errors';\nimport {\n checkLocalServices,\n getLocalDev,\n POSTHOG_LOCAL_URL,\n} from '@shared/local-dev';\nimport { OutroKind } from '@shared/outro';\nimport { RunPhase } from '@shared/run-state';\nimport { POSTHOG_DOCS_URL } from '@shared/constants';\nimport { analytics } from '@utils/analytics';\nimport { runCleanups } from '@utils/cleanup';\nimport { logToFile } from '@utils/debug';\nimport { printAbortOutro } from '@shared/console-log';\nimport {\n registerShutdown,\n startHostExit,\n wizardAbort,\n type HostExit,\n} from '@host/wizard-abort';\nimport { modeLabel } from './mode-label';\nimport { logProgress } from './renderers/progress-log';\nimport { LoggingUI } from './renderers/logging-ui';\n\n/**\n * The two non-interactive run modes. Both drive the same pipeline today; the\n * mode is threaded explicitly (rather than sniffed from a flag) so the dispatch\n * picks an entry point — runWizardCI vs runWizardHeadless — and this core stays\n * mode-agnostic except at the few documented forks. The string values double as\n * the analytics `build` tag, so they segment runs in analytics and on\n * LLM-gateway traces (which read analytics.build).\n */\nexport type NonInteractiveMode = 'ci' | 'headless';\n\nexport type HeadlessLaunch = {\n mode: NonInteractiveMode;\n session: SessionArgs; // launch values, flags already merged over the environment\n taskStreamLog?: string; // --task-stream-log: a path, or '' for the default one\n runId?: string; // the cloud WizardRun this run reports under\n signal: AbortSignal; // the CLI aborts it on SIGINT or SIGTERM, with the signal name as the reason\n};\n\n/** Run `config` headlessly. Resolves with the exit code: 0, a failure's through `wizardAbort`, or 130 or 143 on a signal. */\nexport function runHeadless(\n config: ProgramConfig,\n launch: HeadlessLaunch,\n): Promise<number> {\n const exit = startHostExit();\n void main(config, launch, exit).catch((error: unknown) => exit.fail(error));\n return exit.exited;\n}\n\nasync function main(\n config: ProgramConfig,\n launch: HeadlessLaunch,\n exit: HostExit,\n): Promise<void> {\n const log = new LoggingUI();\n const abortHost = { present: printAbortOutro, exit };\n analytics.setTag('build', launch.mode);\n\n const store = new SessionStore(buildSession({ ...launch.session, ci: true }));\n if (config.skillId) store.update({ skillId: config.skillId });\n const { session } = store;\n\n log.intro('Welcome to the PostHog setup wizard');\n log.log.info(`Running ${config.id} in ${modeLabel(launch.mode)} mode`);\n\n // Before auth: a dead local PostHog otherwise surfaces as \"Failed to fetch\n // user data\". Aborts even non-interactively — a CI run pointed at a local\n // server that isn't there is testing nothing.\n const localServicesError = await checkLocalServices({\n ...getLocalDev(),\n localMcp: session.localMcp,\n localPosthog: session.baseUrl === POSTHOG_LOCAL_URL,\n });\n if (localServicesError) {\n await wizardAbort(abortHost, {\n code: ErrorCodes.EnvLocalServicesDown,\n message: localServicesError,\n });\n return;\n }\n\n // Headless streams run state to the PostHog backend so the web app can show\n // live progress. Headless pushes to PostHog (the web app is that run's only\n // UI); `--ci` is synthetic, so it dumps locally and pushes nothing. Telemetry\n // consent gates the push only.\n // `''` resolves to the default path, so `--ci` always dumps.\n const logTarget =\n launch.mode === 'ci' ? launch.taskStreamLog ?? '' : launch.taskStreamLog;\n const fileDestination = createFileDestination(logTarget);\n const posthogDestination =\n launch.mode === 'headless' && !session.noTelemetry\n ? new PostHogDestination({\n getCredentials: () => store.session.credentials,\n onError: (e) => logToFile('[headless task-stream]', e.message),\n })\n : null;\n const destinations = [\n ...(posthogDestination ? [posthogDestination] : []),\n ...(fileDestination ? [fileDestination] : []),\n ];\n const taskStream = new TaskStreamPush({\n store,\n getFlags: () => analytics.getCachedWizardFlags(),\n programId: config.streamWorkflowId ?? config.id,\n runSync: createWizardRunSync({\n mode: launch.mode,\n programId: config.id,\n assignedId: launch.runId,\n noTelemetry: session.noTelemetry,\n getSession: () => store.session,\n }),\n destinations,\n eventPlanPath: config.eventPlanFile\n ? join(session.installDir, config.eventPlanFile)\n : undefined,\n auditChecks: config.auditLedgerFile\n ? () => getAuditChecks(store.session)\n : undefined,\n enabled: destinations.length > 0,\n });\n taskStream.attach();\n if (fileDestination) {\n logToFile(`[task-stream] ${launch.mode} dump: ${fileDestination.path}`);\n }\n\n // wizardAbort ends the run, so flush the terminal phase before any exit.\n // No-op for CI.\n const settleStream = async (outcome: TaskStreamOutcome): Promise<void> => {\n if (outcome !== 'completed') store.setRunPhase(RunPhase.Error);\n await taskStream.shutdown(2000, outcome);\n unregisterShutdown();\n };\n const unregisterShutdown = registerShutdown((outcome) =>\n settleStream(outcome),\n );\n\n // A signal cancels the run and any pending question, then ends it 130 or 143.\n const runAbort = new AbortController();\n const onSignal = (): void => {\n runAbort.abort();\n runCleanups();\n void settleStream('cancelled').then(() =>\n wizardAbort(abortHost, {\n exitCode: launch.signal.reason === 'SIGTERM' ? 143 : 130,\n }),\n );\n };\n if (launch.signal.aborted) return onSignal();\n launch.signal.addEventListener('abort', onSignal, { once: true });\n\n const credentials = apiKeyCredentials(session.apiKey ?? '', {\n region: session.region,\n baseUrl: session.baseUrl,\n localMcp: session.localMcp,\n projectId: session.projectId,\n onInfo: (message) => log.log.info(message),\n onWarning: (message) => log.log.warn(message),\n });\n\n try {\n if (launch.mode === 'ci') {\n store.update({\n ciGateway: readCiGatewayCredential(session.region ?? 'us'),\n });\n }\n\n const result = await runProgram(\n config.id,\n { store, config },\n {\n credentials,\n onProgress: logProgress(log),\n featureFlags: loadWizardFlags,\n signal: runAbort.signal,\n },\n );\n // A signal cancelled the run; its handler settles and ends it.\n if (runAbort.signal.aborted) return;\n\n if (result.outcome === RunOutcome.Crashed) throw result.failure?.error;\n if (result.outcome !== RunOutcome.Success) {\n if (result.failure?.authErrorDetail) {\n log.showAuthError(result.failure.authErrorDetail);\n }\n // The store already holds the error outro, so the stream's last push carries its code.\n await wizardAbort(abortHost, {\n ...result.failure,\n status: result.outcome === RunOutcome.Aborted ? 'cancelled' : 'error',\n });\n return;\n }\n try {\n await analytics.shutdown('success');\n } catch (error) {\n logToFile('[agent-runner] analytics shutdown failed:', error);\n }\n await settleStream('completed');\n launch.signal.removeEventListener('abort', onSignal);\n exit.end(0);\n } catch (error) {\n if (runAbort.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorStack =\n error instanceof Error && error.stack ? error.stack : undefined;\n logToFile(`[${launch.mode}] ERROR: ${errorMessage}`);\n if (errorStack) logToFile(`[${launch.mode}] STACK: ${errorStack}`);\n\n const debugInfo =\n store.session.debug && errorStack ? `\\n\\n${errorStack}` : '';\n const docsUrl =\n store.session.frameworkConfig?.metadata.docsUrl ??\n (typeof config.run === 'object' ? config.run.docsUrl : undefined) ??\n POSTHOG_DOCS_URL;\n // A coded failure is a decision with its own message; anything else is\n // unexpected and gets the generic framing.\n const failure = classifyRunFailure(error);\n store.setOutroData({\n kind: OutroKind.Error,\n message: errorMessage,\n errorCode: failure.code,\n });\n store.setRunPhase(RunPhase.Error);\n await wizardAbort(abortHost, {\n code: failure.code,\n message: failure.coded\n ? `${errorMessage}${debugInfo}`\n : `Something went wrong: ${errorMessage}\\n\\nYou can read the documentation at ${docsUrl} to set up manually.${debugInfo}`,\n error: error as Error,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runners-Dhu4pZTN.js","names":["path"],"sources":["../src/shared/utils/environment.ts","../src/cli/runners/resolve-no-telemetry.ts","../src/cli/runners/signals.ts","../src/cli/runners/run-wizard.ts","../src/shared/utils/env-api-key.ts","../src/cli/runners/run-non-interactive.ts","../src/cli/runners/run-wizard-ci.ts","../src/cli/runners/run-wizard-headless.ts"],"sourcesContent":["import readEnvModule from 'read-env';\n\nconst readEnv =\n typeof readEnvModule === 'function'\n ? readEnvModule\n : (readEnvModule as any).default;\nimport { tryGetPackageJson } from './setup-utils';\nimport type { WizardRunOptions } from './types';\nimport { boundedGlob } from './bounded-fs';\nimport { IS_DEV } from '@shared/constants';\n\nexport function isNonInteractiveEnvironment(): boolean {\n if (IS_DEV) {\n return false;\n }\n\n if (!process.stdout.isTTY || !process.stderr.isTTY) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Session fields the environment must never set, matched case-insensitively\n * against the camel-cased key `read-env` produces.\n *\n * `e2eAsk` re-wires the `wizard_ask` bridge in an otherwise non-interactive\n * run. Only the e2e TUI host may set it: a real `--ci` run has nobody to answer,\n * so every question would stall for the bridge timeout instead of failing fast\n * with an actionable error. See `shouldDisableAsk`.\n */\n// logFile is process config the CLI applies (`--log-file`), not a session value.\nconst NEVER_FROM_ENV = ['e2eAsk', 'runId', 'logFile'];\n\n/**\n * Session args from the `POSTHOG_WIZARD_*` environment variables.\n *\n * `read-env` camel-cases every prefixed variable into a key, and the CI runner\n * spreads the whole bag into `buildSession` — so adding a field to the session\n * silently adds an environment variable that sets it. Most of them are meant to\n * work that way (`POSTHOG_WIZARD_DEBUG` → `debug`); the ones in\n * {@link NEVER_FROM_ENV} are not, and are dropped here rather than at the call\n * site, so a second caller cannot reopen the door.\n */\nexport function readEnvironment(): Record<string, unknown> {\n const result = readEnv('POSTHOG_WIZARD') as Record<string, unknown>;\n\n for (const key of Object.keys(result)) {\n if (NEVER_FROM_ENV.some((k) => k.toLowerCase() === key.toLowerCase())) {\n delete result[key];\n }\n }\n\n return result;\n}\n\nexport async function detectEnvVarPrefix(\n options: WizardRunOptions,\n): Promise<string> {\n const packageJson = await tryGetPackageJson(options);\n if (!packageJson) return 'VITE_PUBLIC_';\n\n const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };\n const has = (name: string) => name in deps;\n // O(1) matches per probe: one config file existing anywhere answers it.\n const hasAnyFile = async (patterns: string[]) => {\n const matches = await boundedGlob(patterns, {\n cwd: options.installDir,\n limit: 1,\n });\n return matches.length > 0;\n };\n\n // --- Next.js\n if (has('next') || (await hasAnyFile(['**/next.config.{js,ts,mjs,cjs}']))) {\n return 'NEXT_PUBLIC_';\n }\n\n // --- Create React App\n if (\n has('react-scripts') ||\n has('create-react-app') ||\n (await hasAnyFile(['**/config-overrides.js']))\n ) {\n return 'REACT_APP_';\n }\n\n // --- Vite (vanilla, TanStack, Solid, etc.)\n // Note: Vite does not need PUBLIC_ but we use it to follow the docs, to improve the chances of an LLM getting it right.\n if (has('vite') || (await hasAnyFile(['**/vite.config.{js,ts,mjs,cjs}']))) {\n return 'VITE_PUBLIC_';\n }\n\n // --- SvelteKit\n if (\n has('@sveltejs/kit') ||\n (await hasAnyFile(['**/svelte.config.{js,ts}']))\n ) {\n return 'PUBLIC_';\n }\n\n // --- TanStack Start (uses Vite)\n if (\n has('@tanstack/start') ||\n (await hasAnyFile(['**/tanstack.config.{js,ts}']))\n ) {\n return 'VITE_PUBLIC_';\n }\n\n // --- SolidStart (uses Vite)\n if (has('solid-start') || (await hasAnyFile(['**/solid.config.{js,ts}']))) {\n return 'VITE_PUBLIC_';\n }\n\n // --- Astro\n if (has('astro') || (await hasAnyFile(['**/astro.config.{js,ts,mjs}']))) {\n return 'PUBLIC_';\n }\n\n // We default to Vite if we can't detect a specific framework, since it's the most commonly used.\n return 'VITE_PUBLIC_';\n}\n","/**\n * `--no-telemetry` flips `telemetry: false` via yargs negation;\n * `POSTHOG_WIZARD_NO_TELEMETRY` is honoured separately so the env-var\n * form documented in the README keeps working.\n */\nexport function resolveNoTelemetry(options: Record<string, unknown>): boolean {\n if (options.telemetry === false) return true;\n const env = process.env.POSTHOG_WIZARD_NO_TELEMETRY;\n if (env == null || env === '') return false;\n const norm = env.toLowerCase();\n return norm !== '0' && norm !== 'false';\n}\n","/** The CLI owns the process: SIGINT and SIGTERM abort the host's signal, and the host's code is the exit. */\nimport { ErrorCodes, emitWizardError } from '@shared/errors';\n\nconst drain = (stream: NodeJS.WriteStream): Promise<void> =>\n new Promise((resolve) => stream.write('', () => resolve()));\n\n/** Exit once stdout and stderr have flushed, so a slow pipe keeps the whole output. */\nasync function flushThenExit(code: number): Promise<void> {\n await Promise.all([drain(process.stdout), drain(process.stderr)]);\n process.exit(code);\n}\n\n/**\n * Exit with the code `run` resolves; the only way a host's run ends the\n * process. A rejection prints the PHW error line and exits 1.\n */\nexport function exitWith(run: () => Promise<number>): void {\n void run().then(\n (code) => flushThenExit(code),\n (error: unknown) => {\n emitWizardError({\n code: ErrorCodes.InternalUnhandled,\n message: error instanceof Error ? error.message : String(error),\n });\n return flushThenExit(1);\n },\n );\n}\n\n/**\n * Run a host with a signal SIGINT and SIGTERM abort (the reason is the signal\n * name), and settle as it does. The listeners go once it settles, so what runs\n * after it, such as a fallback with no screens, ends on Ctrl-C as Node does.\n */\nexport async function underSignals(\n run: (signal: AbortSignal) => Promise<number>,\n): Promise<number> {\n const controller = new AbortController();\n const onSignal = (name: NodeJS.Signals) => controller.abort(name);\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n try {\n return await run(controller.signal);\n } finally {\n process.off('SIGINT', onSignal);\n process.off('SIGTERM', onSignal);\n }\n}\n\n/** Run a host under the signals, then exit with the code it resolves. */\nexport function withSignals(\n run: (signal: AbortSignal) => Promise<number>,\n): void {\n exitWith(() => underSignals(run));\n}\n","/** The TUI command runner: parse the launch values, start the TUI host, and exit with its code. */\nimport type { ProgramConfig, SessionArgs } from '@programs/types';\nimport type { Harness, Sequence } from '@shared/constants';\nimport { runtimeEnv } from '@env';\nimport { resolveNoTelemetry } from './resolve-no-telemetry';\nimport { withSignals } from './signals';\n\n/** The TUI session a command's parsed options launch: a program's run or a tool's screens. */\nexport function tuiSessionArgs(\n options: Record<string, unknown>,\n): SessionArgs & { integrate?: boolean } {\n return {\n debug: options.debug as boolean | undefined,\n localDev: options.localDev as boolean | undefined,\n localMcp: options.localMcp as boolean | undefined,\n localPosthog: options.localPosthog as boolean | undefined,\n installDir: (options.installDir as string) || process.cwd(),\n ci: false,\n signup: options.signup as boolean | undefined,\n apiKey: options.apiKey as string | undefined,\n projectId: options.projectId as string | undefined,\n email: options.email as string | undefined,\n baseUrl: options.baseUrl as string | undefined,\n benchmark: options.benchmark as boolean | undefined,\n yaraReport: options.yaraReport as boolean | undefined,\n noTelemetry: resolveNoTelemetry(options),\n harness: options.harness as Harness | undefined,\n sequence: options.sequence as Sequence | undefined,\n model: options.model as string | undefined,\n integrate: options.integrate as boolean | undefined,\n captureAio: options.captureAio as boolean | undefined,\n };\n}\n\n/** Run a full wizard program in the TUI. */\nexport function runWizard(\n config: ProgramConfig,\n options: Record<string, unknown>,\n): void {\n withSignals(async (signal) => {\n // Loaded here, not at startup: a headless run never loads the TUI.\n const { runTui } = await import('@tui');\n return runTui(config, {\n session: tuiSessionArgs(options),\n skillId: options.skillId as string | undefined,\n taskStreamLog: options.taskStreamLog as string | undefined,\n runId:\n (options.runId as string | undefined) ??\n runtimeEnv('POSTHOG_WIZARD_RUN_ID'),\n signal,\n });\n });\n}\n","import * as path from 'path';\nimport { readProjectFile } from './bounded-fs';\n\n/**\n * Read POSTHOG_PERSONAL_API_KEY from .env.local or .env in the current\n * working directory. Returns undefined when no key is found.\n */\nexport function readApiKeyFromEnv(): string | undefined {\n for (const envFile of ['.env.local', '.env']) {\n // null when missing or unreadable, e.g. a `.env` directory (a Python virtualenv).\n const content = readProjectFile(path.join(process.cwd(), envFile));\n const match = content?.match(/^POSTHOG_PERSONAL_API_KEY=(.+)$/m);\n if (match) return match[1].trim();\n }\n return undefined;\n}\n","/** The non-interactive command runner: check the arguments, start the headless host, and exit with its code. */\nimport path from 'path';\nimport type { ProgramConfig } from '@programs/types';\nimport { modeLabel, type NonInteractiveMode } from '@headless';\nimport type { Harness, Sequence } from '@shared/constants';\nimport { ErrorCodes, emitWizardError } from '@shared/errors';\nimport type { CloudRegion } from '@utils/types';\nimport { readEnvironment } from '@utils/environment';\nimport { readApiKeyFromEnv } from '@utils/env-api-key';\nimport { runtimeEnv } from '@env';\nimport { configureLogFileFromEnvironment } from '@utils/debug';\nimport { consoleLog } from '@shared/console-log';\nimport { resolveNoTelemetry } from './resolve-no-telemetry';\nimport { withSignals } from './signals';\n\n/** The credentials every non-interactive mode accepts, for error messages. */\nexport const API_KEY_HINT =\n 'personal API key phx_xxx or wizard-app OAuth access token pha_xxx';\n\n/**\n * The single non-interactive validation layer: requires api-key and\n * install-dir. Every non-interactive entry point routes through\n * `runNonInteractive`, so this is the one place these checks live.\n */\nexport function validateNonInteractiveOptions(\n options: Record<string, unknown>,\n mode: NonInteractiveMode,\n): void {\n const label = modeLabel(mode);\n if (!options.apiKey) {\n consoleLog.intro('PostHog Wizard');\n consoleLog.log.error(`${label} mode requires --api-key (${API_KEY_HINT})`);\n emitWizardError({\n code: ErrorCodes.ArgsMissingApiKey,\n message: `${label} mode requires --api-key (${API_KEY_HINT})`,\n });\n process.exit(1);\n }\n if (!options.installDir) {\n consoleLog.intro('PostHog Wizard');\n consoleLog.log.error(\n `${label} mode requires --install-dir (directory to install in)`,\n );\n emitWizardError({\n code: ErrorCodes.ArgsMissingInstallDir,\n message: `${label} mode requires --install-dir`,\n });\n process.exit(1);\n }\n}\n\n/** Run `config` in the headless host, for CI (`runWizardCI`) and headless (`runWizardHeadless`) runs. */\nexport function runNonInteractive(\n config: ProgramConfig,\n options: Record<string, unknown>,\n mode: NonInteractiveMode,\n): void {\n validateNonInteractiveOptions(options, mode);\n configureLogFileFromEnvironment();\n\n const env = readEnvironment();\n const installDir = path.isAbsolute(options.installDir as string)\n ? (options.installDir as string)\n : path.join(process.cwd(), options.installDir as string);\n withSignals(async (signal) => {\n const { runHeadless } = await import('@headless');\n return runHeadless(config, {\n mode,\n session: {\n debug: options.debug as boolean | undefined,\n installDir,\n ci: true,\n signup: options.signup as boolean | undefined,\n localDev: options.localDev as boolean | undefined,\n localMcp: options.localMcp as boolean | undefined,\n localPosthog: options.localPosthog as boolean | undefined,\n apiKey: (options.apiKey as string) ?? readApiKeyFromEnv() ?? undefined,\n email: options.email as string | undefined,\n projectId: options.projectId as string | undefined,\n baseUrl: options.baseUrl as string | undefined,\n benchmark: options.benchmark as boolean | undefined,\n yaraReport: options.yaraReport as boolean | undefined,\n noTelemetry: resolveNoTelemetry(options),\n harness: options.harness as Harness | undefined,\n sequence: options.sequence as Sequence | undefined,\n model: options.model as string | undefined,\n captureAio: options.captureAio as boolean | undefined,\n ...env,\n // After the spread: yargs already resolves flag-over-env for --region,\n // so the parsed value must win over the raw env bag.\n region: (options.region ?? env.region) as CloudRegion | undefined,\n },\n taskStreamLog: options.taskStreamLog as string | undefined,\n runId:\n (options.runId as string | undefined) ??\n runtimeEnv('POSTHOG_WIZARD_RUN_ID'),\n signal,\n });\n });\n}\n","import type { ProgramConfig } from '@programs/types';\nimport { runNonInteractive } from './run-non-interactive';\n\n/**\n * CI-mode entry point (`--ci`, dev/test builds). A thin shell over the shared\n * non-interactive pipeline — see `runNonInteractive`. Kept as its own function\n * so CI and headless (`runWizardHeadless`) can diverge without re-threading the\n * many callers that route here.\n */\nexport function runWizardCI(\n config: ProgramConfig,\n options: Record<string, unknown>,\n): void {\n runNonInteractive(config, options, 'ci');\n}\n","import type { ProgramConfig } from '@programs/types';\nimport { runNonInteractive } from './run-non-interactive';\n\n/**\n * Headless entry point (the experimental published-build, non-interactive run\n * path; see @shared/headless-mode). A thin shell over the shared non-interactive\n * pipeline — see `runNonInteractive`. Today it behaves exactly like\n * `runWizardCI`; it exists as a separate function so headless can diverge later\n * (its own auth handling, telemetry, prompts, …) without touching CI or its\n * callers. Diverge by branching on the mode inside `runNonInteractive`, or by\n * giving this function its own body.\n */\nexport function runWizardHeadless(\n config: ProgramConfig,\n options: Record<string, unknown>,\n): void {\n runNonInteractive(config, options, 'headless');\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"security-DEH8Rn0_.js","names":[],"sources":["../src/agent/runner/harness/pi/security.ts"],"sourcesContent":["/**\n * Fail-closed security for the pi backend (#525). pi has no built-in\n * permission layer, so we attach an extension that intercepts every tool call\n * — built-in (bash/read/edit/write/grep) AND custom — through pi's `tool_call`\n * hook and reuses the EXACT anthropic policy: `wizardCanUseTool` (the bash\n * allowlist + .env fencing) plus the YARA pre-scan. A `tool_result` hook\n * post-scans output. Both fail closed: a scanner error blocks, and a critical\n * post-scan violation latches so every subsequent tool call is blocked and the\n * run terminates as a YARA violation.\n *\n * Scanning runs through @posthog/warlock via yara-hooks' `scanAndTriage` — the\n * same engine, rules, chunking, and LLM triage policy as the anthropic\n * harness. pi handlers are async (pi's ExtensionHandler accepts promises), so\n * the WASM scan awaits inline.\n *\n * This is the one fence. Subagents run their own pi session with the SAME\n * extension installed (see subagent.ts), so a child cannot escape it.\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport type { LLMProvider, ScanMatch } from '@posthog/warlock';\nimport { wizardCanUseTool } from '../../../agent-interface';\nimport {\n createRepeatBlockTracker,\n isWizardDocumentationPath,\n recordExternalScan,\n repeatBlockReason,\n scanAndTriage,\n type RepeatBlockTracker,\n} from '../../../yara-hooks';\nimport {\n publishBlockingMatch,\n scanVerdict,\n type ScanContext,\n} from '../../../yara-policy';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\n\n/** warlock ScanMatch → the report shape `recordExternalScan` expects. */\nfunction toReportViolation(m: ScanMatch) {\n return {\n rule: m.rule,\n severity: m.metadata.severity ?? 'unknown',\n category: m.metadata.category ?? 'unknown',\n description: m.metadata.description ?? '',\n };\n}\n\n/** Runaway backstop: hard cap on tool calls per (sub)agent session. */\nexport const MAX_TOOL_CALLS = 250;\n\n/**\n * Passive telemetry for an unfixed upstream failure: gpt-5.x streaming with\n * tools leaks its function-call grammar into string values, and the leak lands\n * on disk (run 9704a73e shipped `…functions.complete_task (commentary…` plus a\n * DEL byte inside a customer's global-error.tsx). Observe-only — never blocks.\n * Prior art: https://github.com/BerriAI/litellm/issues/14260 (closed stale),\n * https://community.openai.com/t/1386422 (gpt-5.6-luna, no fix).\n */\nconst TRANSPORT_LEAK_PATTERNS: readonly { pattern: RegExp; label: string }[] = [\n // The litellm#14260 signature, scoped to the wizard's own tool names — a\n // generic `functions.*` would false-positive on real code (Firebase).\n {\n pattern: /functions\\.(?:complete_task|enqueue_task|read_handoffs)/,\n label: 'leaked tool-call tokens',\n },\n {\n pattern: /<\\|(?:channel|constrain|message|call|end|start|return)\\|>/,\n label: 'leaked channel markers',\n },\n // C0 controls and DEL minus tab/newline/CR — never valid in source text.\n // eslint-disable-next-line no-control-regex\n { pattern: /[\\0-\\x08\\x0B\\f\\x0E-\\x1F\\x7F]/, label: 'control characters' },\n];\n\n/** Capture one event per leaking write/edit: which pattern fired and where it sat — no matched text, ever. */\nexport function observeTransportLeak(tool: string, content: string): void {\n for (const { pattern, label } of TRANSPORT_LEAK_PATTERNS) {\n const match = pattern.exec(content);\n if (!match) continue;\n analytics.wizardCapture('file content leak observed', {\n tool,\n leak: label,\n leak_offset: match.index,\n content_length: content.length,\n // End-of-string leaks are the upstream decoder signature.\n at_end: content.length - match.index < 80,\n });\n logToFile(\n `[transport-leak] observed ${tool}: ${label} at ${match.index}/${content.length}`,\n );\n return;\n }\n}\n\nexport const READ_ONLY_PI_TOOLS: ReadonlySet<string> = new Set([\n 'read',\n 'grep',\n 'find',\n 'ls',\n]);\n\nexport interface ToolGateContext {\n readOnly?: boolean;\n disallowedTools?: readonly string[];\n /** True while a wizard_ask overlay is open (interactive); blocks Write/Edit. */\n getWizardAskPending?: () => boolean;\n /** Scan-triage classifier, resolved once in bootstrap. Absent → every flagged match is acted on. */\n triageProvider?: LLMProvider;\n /**\n * Per-run tracker of YARA-blocked payloads. When the agent retries content\n * that was already blocked, the block reason escalates (\"change the code,\n * not the retry\" → \"report it and move on\"). The extension wires one in;\n * absent (tests calling `evaluateToolCall` directly) every block reads as\n * a first attempt.\n */\n repeatTracker?: RepeatBlockTracker;\n /** Project root; rm is only allowed for files resolving strictly inside it. */\n workingDirectory?: string;\n}\n\nexport interface GateDecision {\n block: boolean;\n reason?: string;\n /** End the run, don't just refuse the call. Set when a blocked publish_handoff would otherwise leave no report. */\n terminate?: boolean;\n}\n\nconst str = (v: unknown): string => (typeof v === 'string' ? v : '');\n\n// Shell metacharacters that chain, substitute, or redirect. A command carrying\n// any of these is more than one action, so we never treat it as a plain `rm`.\nconst SHELL_OPERATORS = /[;&|`$(){}<>\\n'\"\\\\]/;\n\n/** True when a target resolves to a file strictly inside the project root. */\nfunction isDeletableProjectFile(\n target: string,\n root: string,\n p: typeof path,\n): boolean {\n if (target.startsWith('-')) return false; // a flag, not a file\n if (/[*?[\\]~]/.test(target)) return false; // glob / home expansion\n if (p.basename(target).startsWith('.env')) return false; // secrets\n\n const resolved = p.resolve(root, target);\n return resolved !== root && resolved.startsWith(root + p.sep);\n}\n\n/**\n * A plain `rm [-f] <file...>` whose every target is a file inside the project\n * root — the deletion shape the anthropic arm permits (there bash isn't\n * allowlisted; the shared YARA destructive-delete rules catch the dangerous\n * forms). pi rescues the same shape so the fall-through YARA scan can judge\n * it, but refuses any shell operator so it can't smuggle a second command.\n * Path checks run through the host's `path` (win32 on Windows, posix elsewhere);\n * pi always executes commands via a POSIX bash, so targets use forward slashes.\n */\nexport function isScopedFileRemoval(\n command: string,\n rawRoot: string | undefined,\n p: typeof path = path,\n): boolean {\n if (!rawRoot) return false; // no root to contain against → never rescue\n const root = p.resolve(rawRoot);\n const trimmed = command.trim();\n if (SHELL_OPERATORS.test(trimmed)) return false;\n\n const [executable, ...args] = trimmed.split(/\\s+/);\n if (executable !== 'rm') return false;\n\n if (args[0] === '-f') args.shift();\n if (args.length === 0) return false;\n\n return args.every((arg) => isDeletableProjectFile(arg, root, p));\n}\n\n/** A write that removes more than this fraction of an existing file's\n * non-whitespace content reads as a destructive whole-file rewrite. */\nconst OVERWRITE_SHRINK_LIMIT = 0.3;\n\n/** Existing files below this many non-whitespace chars are stubs; replacing one\n * wholesale is legitimate, so the shrink guard leaves them alone. */\nconst OVERWRITE_MIN_CHARS = 400;\n\nconst nonWhitespaceLength = (s: string): number => s.replace(/\\s+/g, '').length;\n\n/**\n * Refuse a write that replaces an existing file with far less content than it\n * held, steering the agent back to targeted edits. Prior art — the destructive\n * whole-file rewrite is a known model failure that targeted-edit formats exist to\n * prevent: Aider's search/replace and unified-diff, OpenAI's apply_patch,\n * Anthropic's str_replace with replace_all; pi's edit tool has no replace_all, so\n * a rejected scoped edit can fall back to a lossy full write. Additive by design:\n * the wizard only adds instrumentation, so a large shrink is the rewrite, not intent.\n */\nexport function overwriteShrinkReason(\n existing: string,\n next: string,\n): string | undefined {\n const before = nonWhitespaceLength(existing);\n if (before < OVERWRITE_MIN_CHARS) return undefined;\n const after = nonWhitespaceLength(next);\n if (after >= before * (1 - OVERWRITE_SHRINK_LIMIT)) return undefined;\n const removed = Math.round((1 - after / before) * 100);\n return `This write replaces an existing file and removes ~${removed}% of its content. Make targeted edits instead of rewriting the whole file; only write a file in full when creating it.`;\n}\n\n/**\n * Read the file a write would replace and flag a destructive shrink. A path that\n * does not exist yet is a new file, so it never triggers; with no project root to\n * resolve against (unit tests calling `evaluateToolCall` bare), it stays inert.\n */\nasync function overwriteShrinkBlock(\n input: Record<string, unknown>,\n workingDirectory: string | undefined,\n): Promise<string | undefined> {\n const target = str(input.path);\n if (!workingDirectory || !target) return undefined;\n let existing: string;\n try {\n existing = await fs.promises.readFile(\n path.resolve(workingDirectory, target),\n 'utf8',\n );\n } catch {\n return undefined; // new file (ENOENT) or unreadable — nothing to preserve\n }\n return overwriteShrinkReason(existing, str(input.content));\n}\n\n/**\n * Translate a pi tool name to the claude-cased name + input the shared policy\n * expects. pi field names (from the live tool stream): bash{command},\n * read/edit/write{path}, write adds {content}, edit adds {edits}, grep{path}.\n */\nfunction toClaudePolicyCall(\n toolName: string,\n input: Record<string, unknown>,\n): { name: string; input: Record<string, unknown> } {\n switch (toolName) {\n case 'bash':\n return { name: 'Bash', input: { command: str(input.command) } };\n case 'read':\n return { name: 'Read', input: { file_path: input.path } };\n case 'write':\n return { name: 'Write', input: { file_path: input.path } };\n case 'edit':\n return { name: 'Edit', input: { file_path: input.path } };\n case 'grep':\n return { name: 'Grep', input: { path: input.path } };\n default:\n // Custom tools (load_skill_menu, set_env_values, dispatch_agent, …) +\n // find/ls: no path/command, policy allows (their own handlers are fenced).\n return { name: toolName, input };\n }\n}\n\n/**\n * The agent-facing block message. Includes the rule's remediation so a blocked\n * agent can self-correct (e.g. \"move PII to identify()/$set\") instead of\n * retrying near-identical content.\n */\nfunction blockMessage(m: ScanMatch): string {\n const remediation = m.metadata.remediation\n ? ` Fix: ${m.metadata.remediation}.`\n : '';\n return `[YARA] ${m.rule}: ${\n m.metadata.description ?? 'security rule matched'\n }.${remediation} Blocked for security.`;\n}\n\n/**\n * YARA scan of the content a tool is about to act on, BEFORE it executes.\n * - bash → scan the command ('command'-context rules: exfiltration,\n * destructive, force-push). No triage — a flagged command always blocks,\n * same as the anthropic PreToolUse hook, so the LLM call would be wasted.\n * - write/edit → scan the content being written ('output'-context rules:\n * hardcoded keys, PII), WITH triage, and with the same wizard-doc\n * `posthog_pii` suppression the anthropic path uses so the agent's own\n * event-plan files aren't blocked.\n * - publish_handoff → scan the report ('output'-context rules) with triage,\n * but only a CRITICAL match refuses the publish — it's the only channel.\n * Returns a block reason, or undefined to allow. Read/grep are post-scanned on\n * their output (in the tool_result hook), not here.\n */\nasync function preExecutionYaraBlock(\n toolName: string,\n input: Record<string, unknown>,\n llmProvider: LLMProvider | undefined,\n repeatTracker?: RepeatBlockTracker,\n): Promise<string | undefined> {\n let content: string;\n let ctx: ScanContext;\n let triage: LLMProvider | undefined;\n // Report labels for the scan report (phase/tool), matching the anthropic\n // hooks so pi and anthropic runs read the same in telemetry.\n let phase: string;\n let tool: string;\n switch (toolName) {\n case 'bash':\n content = str(input.command);\n ctx = 'command';\n triage = undefined;\n phase = 'PreToolUse';\n tool = 'Bash';\n break;\n case 'write':\n content = str(input.content);\n ctx = 'output';\n triage = llmProvider;\n phase = 'PostToolUse';\n tool = 'Write';\n break;\n case 'edit':\n // Scan ONLY the replacement text. `edits` is [{oldText, newText}] and\n // oldText is pre-existing file content the agent is changing — scanning\n // it blocks edits whose *surroundings* contain a violation, and even\n // edits that are removing one (field FPs: capture edits adjacent to\n // existing identify() PII; pre-existing violations blocking their own\n // replacement). Matches the anthropic path, which scans new_string only.\n content = (Array.isArray(input.edits) ? input.edits : [])\n .map((e) => str((e as Record<string, unknown>)?.newText))\n .join('\\n');\n ctx = 'output';\n triage = llmProvider;\n phase = 'PostToolUse';\n tool = 'Edit';\n break;\n case 'publish_handoff':\n // Agent-authored content persisted to the task stream and the host file.\n // Own labels, not Write's — the block bar differs (critical only).\n content = str(input.content);\n ctx = 'output';\n triage = llmProvider;\n phase = 'PreToolUse';\n tool = 'publish_handoff';\n break;\n default:\n return undefined;\n }\n if (!content) return undefined;\n if (ctx === 'output') observeTransportLeak(tool, content);\n\n let matches = await scanAndTriage(content, ctx, triage);\n if (ctx === 'output' && isWizardDocumentationPath(str(input.path))) {\n matches = matches.filter((m) => m.metadata.category !== 'posthog_pii');\n }\n // Any match blocks — except publish_handoff, critical only.\n const blocking =\n toolName === 'publish_handoff'\n ? publishBlockingMatch(matches)\n : matches[0] ?? null;\n\n recordExternalScan(\n phase,\n tool,\n matches.map(toReportViolation),\n blocking ? 'blocked' : 'warned',\n );\n if (!blocking) return undefined;\n\n const attempt = repeatTracker?.attempt(toolName, content) ?? 1;\n return repeatBlockReason(attempt, toolName, blockMessage(blocking));\n}\n\n/**\n * The pure gate decision for a single tool call. Reuses `wizardCanUseTool`\n * (deny → block) then the YARA content scan (match → block). Fail-closed: any\n * thrown error blocks.\n */\nexport async function evaluateToolCall(\n toolName: string,\n input: Record<string, unknown>,\n ctx: ToolGateContext = {},\n llmProvider?: LLMProvider,\n): Promise<GateDecision> {\n try {\n if (ctx.readOnly && !READ_ONLY_PI_TOOLS.has(toolName)) {\n return {\n block: true,\n reason: `Tool ${toolName} is disabled for read-only runs.`,\n };\n }\n const policy = toClaudePolicyCall(toolName, input);\n const decision = wizardCanUseTool(policy.name, policy.input, {\n disallowedTools: ctx.disallowedTools,\n wizardAskPending: ctx.getWizardAskPending?.() ?? false,\n });\n // The allowlist is a pi-only restriction; the anthropic arm runs bash\n // unrestricted and leans on the shared YARA scan. Let a plain `rm` of\n // project files through to that same scan so pi matches that behavior.\n const allowedLikeAnthropic =\n toolName === 'bash' &&\n isScopedFileRemoval(str(input.command), ctx.workingDirectory);\n\n if (decision.behavior === 'deny' && !allowedLikeAnthropic) {\n return { block: true, reason: decision.message };\n }\n\n const yaraReason = await preExecutionYaraBlock(\n toolName,\n input,\n llmProvider,\n ctx.repeatTracker,\n );\n if (yaraReason) return { block: true, reason: yaraReason };\n\n if (toolName === 'write') {\n const shrinkReason = await overwriteShrinkBlock(\n input,\n ctx.workingDirectory,\n );\n if (shrinkReason) return { block: true, reason: shrinkReason };\n }\n\n return { block: false };\n } catch (err) {\n logToFile('[pi-security] gate error — failing closed:', err);\n // A publish_handoff the gate can't clear leaves the run with no report at\n // all, so end it visibly instead of letting the agent reword and retry.\n return {\n block: true,\n reason: 'Security check failed; tool blocked (fail-closed).',\n terminate: toolName === 'publish_handoff',\n };\n }\n}\n\n/** pi result tool names whose OUTPUT gets scanned for 'input'-context rules. */\nfunction isPostScanTool(toolName: string): boolean {\n return toolName === 'read' || toolName === 'bash';\n}\n\n/** Mutable state the backend reads after the run to classify the outcome. */\nexport interface SecurityState {\n criticalViolation: boolean;\n blockedCount: number;\n toolCalls: number;\n}\n\n/**\n * Build the pi security extension + the shared state the backend inspects.\n * Install the returned factory via `extensionFactories`; pass the same factory\n * into every subagent session so the fence is inherited.\n */\nexport function createSecurityExtension(ctx: ToolGateContext = {}): {\n factory: (pi: PiExtensionApiLike) => void;\n state: SecurityState;\n} {\n const state: SecurityState = {\n criticalViolation: false,\n blockedCount: 0,\n toolCalls: 0,\n };\n\n // One triage provider per extension. Undefined (no gateway auth) means\n // triage is skipped and every flagged match is acted on — fail closed.\n const llmProvider = ctx.triageProvider;\n\n // One tracker per extension = per (sub)agent session, so an identical\n // payload retried after a block gets the escalating reason.\n const gateCtx: ToolGateContext = {\n ...ctx,\n repeatTracker: ctx.repeatTracker ?? createRepeatBlockTracker(),\n };\n\n const factory = (pi: PiExtensionApiLike): void => {\n pi.on('tool_call', async (event) => {\n // A latched post-scan violation blocks everything that follows.\n if (state.criticalViolation) {\n return {\n block: true,\n reason: 'Run terminated by a security violation.',\n };\n }\n state.toolCalls += 1;\n if (state.toolCalls > MAX_TOOL_CALLS) {\n return {\n block: true,\n reason: `Stopped: exceeded ${MAX_TOOL_CALLS} tool calls (runaway guard).`,\n };\n }\n const decision = await evaluateToolCall(\n event.toolName,\n event.input ?? {},\n gateCtx,\n llmProvider,\n );\n if (decision.block) {\n state.blockedCount += 1;\n if (decision.terminate) state.criticalViolation = true;\n logToFile(`[pi-security] BLOCK ${event.toolName}: ${decision.reason}`);\n return { block: true, reason: decision.reason };\n }\n return {};\n });\n\n pi.on('tool_result', async (event) => {\n if (!isPostScanTool(event.toolName)) return {};\n const text = (event.content ?? [])\n .map((c) => (c && c.type === 'text' ? c.text : ''))\n .join('\\n');\n if (!text) return {};\n try {\n // 'input'-context rules (prompt injection in read content), with\n // triage — same policy as the anthropic PostToolUse Read hook.\n const matches = await scanAndTriage(text, 'input', llmProvider);\n const verdict = scanVerdict(matches);\n // Count the scan even when clean, like the anthropic hooks do.\n recordExternalScan(\n 'PostToolUse',\n event.toolName === 'read' ? 'Read' : 'Bash',\n matches.map(toReportViolation),\n verdict?.action ?? 'warned',\n );\n if (!verdict) return {};\n if (verdict.terminal) {\n state.criticalViolation = true;\n logToFile(\n `[pi-security] POST-SCAN VIOLATION ${event.toolName}: ${verdict.match.rule}`,\n );\n } else {\n logToFile(\n `[pi-security] POST-SCAN WARNING ${event.toolName}: ${\n verdict.match.rule\n } (severity: ${verdict.match.metadata.severity ?? 'unknown'})`,\n );\n }\n } catch (err) {\n // Fail closed: a scanner error on output latches a violation.\n state.criticalViolation = true;\n logToFile('[pi-security] post-scan error — failing closed:', err);\n }\n return {};\n });\n };\n\n return { factory, state };\n}\n\n/**\n * Minimal structural type for pi's ExtensionAPI — just the `on` overloads we\n * use. Kept local so this module has no value import from the pi SDK (so the\n * CommonJS unit tests can load it directly). Handlers may return promises —\n * pi's real ExtensionHandler is `(event, ctx) => Promise<R | void> | R | void`.\n */\nexport interface PiExtensionApiLike {\n on(\n event: 'tool_call',\n handler: (event: {\n toolName: string;\n input?: Record<string, unknown>;\n }) =>\n | { block?: boolean; reason?: string }\n | Promise<{ block?: boolean; reason?: string }>,\n ): void;\n on(\n event: 'tool_result',\n handler: (event: {\n toolName: string;\n content?: Array<{ type: string; text?: string }>;\n isError?: boolean;\n }) => Record<string, never> | Promise<Record<string, never>>,\n ): void;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-CnFvAq6E.js","names":[],"sources":["../src/agent/runner/harness/pi/subagent.ts"],"sourcesContent":["/**\n * Controlled subagent dispatch for pi (#526). pi has no native subagent\n * mechanism, so a subagent is a nested `createAgentSession` we construct — which\n * means WE decide its powers, closing the leak the claude-agent-sdk path warns\n * about (it can't propagate the parent's disallowedTools into subagents).\n *\n * Controls on every child:\n * - the SAME security extension (canUseTool + YARA, fail-closed) — shared state,\n * so the child shares the parent's tool-call cap and violation latch;\n * - a read-only built-in toolset (read/grep/find/ls + allowlisted bash) — no\n * write/edit, so a subagent can research but never mutate the project;\n * - no custom tools — no .env writes, and crucially no `dispatch_agent`, so a\n * child cannot recurse (depth is hard-capped at 1).\n */\n\nimport { Type } from 'typebox';\nimport { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport { logToFile } from '@utils/debug';\nimport { gatewayTerminalFailure } from './gateway';\n\n/**\n * Read-only built-ins a subagent may use. bash is supplied separately as the\n * parent's env-scrubbed tool (below), not the built-in, so a subagent's\n * subprocesses are locked down too.\n */\nconst SUBAGENT_TOOLS = ['read', 'grep', 'find', 'ls'];\n\nconst SUBAGENT_SYSTEM_PROMPT = [\n 'You are a read-only research subagent for the PostHog wizard.',\n 'You can read and search files and run safe build/inspect shell commands.',\n 'You cannot edit files, modify .env, or dispatch further subagents.',\n 'Investigate the task you are given and report concise findings as your final message.',\n].join('\\n');\n\nfunction text(s: string): {\n content: [{ type: 'text'; text: string }];\n details: unknown;\n} {\n return { content: [{ type: 'text', text: s }], details: {} };\n}\n\nfunction extractText(message: unknown): string {\n const content = (message as { content?: unknown })?.content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content\n .filter((c): c is { type: string; text: string } => {\n const b = c as { type?: string; text?: unknown };\n return b?.type === 'text' && typeof b.text === 'string';\n })\n .map((c) => c.text)\n .join('');\n }\n return '';\n}\n\nexport interface SubagentContext {\n /** Resolved gateway model (same as the parent). */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n model: import('@earendil-works/pi-ai').Model<any>;\n /** Registry holding the gateway provider. */\n modelRegistry: import('@earendil-works/pi-coding-agent').ModelRegistry;\n cwd: string;\n agentDir: string;\n /** The parent's security extension factory — reused so the fence is inherited. */\n securityFactory: (pi: unknown) => void;\n /** The parent's env-scrubbed bash, so a subagent's subprocesses are locked down too. */\n bashTool: ToolDefinition;\n /** pi SDK entrypoints, already imported by the backend. */\n sdk: {\n createAgentSession: typeof import('@earendil-works/pi-coding-agent')['createAgentSession'];\n DefaultResourceLoader: typeof import('@earendil-works/pi-coding-agent')['DefaultResourceLoader'];\n SessionManager: typeof import('@earendil-works/pi-coding-agent')['SessionManager'];\n };\n}\n\nexport function createDispatchAgentTool(ctx: SubagentContext): ToolDefinition {\n return defineTool({\n name: 'dispatch_agent',\n label: 'Dispatch subagent',\n description:\n 'Delegate a focused, read-only research subtask to a subagent (e.g. \"find where events are captured\"). The subagent can read/search files and run safe shell, but CANNOT edit files, change .env, or dispatch further subagents. Returns its findings.',\n promptSnippet:\n 'dispatch_agent(description, prompt) — delegate a read-only research subtask',\n parameters: Type.Object({\n description: Type.String({ description: 'Short label for the subtask' }),\n prompt: Type.String({ description: 'Full instruction for the subagent' }),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const { createAgentSession, DefaultResourceLoader, SessionManager } =\n ctx.sdk;\n\n const loader = new DefaultResourceLoader({\n cwd: ctx.cwd,\n agentDir: ctx.agentDir,\n systemPrompt: SUBAGENT_SYSTEM_PROMPT,\n noExtensions: true,\n noSkills: true,\n noContextFiles: true,\n noPromptTemplates: true,\n noThemes: true,\n extensionFactories: [ctx.securityFactory],\n });\n await loader.reload();\n\n const { session: child } = await createAgentSession({\n model: ctx.model,\n modelRegistry: ctx.modelRegistry,\n cwd: ctx.cwd,\n sessionManager: SessionManager.inMemory(ctx.cwd),\n resourceLoader: loader,\n tools: SUBAGENT_TOOLS, // read-only built-ins; no write/edit, no dispatch_agent\n customTools: [ctx.bashTool], // env-scrubbed bash only (still allowlist-fenced)\n });\n\n let result = '';\n let finalTurn:\n | {\n stopReason?: string;\n errorMessage?: string;\n diagnostics?: {\n error?: { name?: string; code?: string | number };\n }[];\n }\n | undefined;\n const unsub = child.subscribe((e) => {\n if (e.type === 'message_end') {\n if ((e.message as { role?: string })?.role === 'assistant')\n finalTurn = e.message as typeof finalTurn;\n const t = extractText(e.message).trim();\n if (t) result = t;\n }\n });\n logToFile(`[pi] subagent dispatch: ${args.description}`);\n try {\n await child.prompt(args.prompt);\n const failure = gatewayTerminalFailure(finalTurn);\n if (failure) throw new Error(`Subagent failed: ${failure.message}`);\n } finally {\n unsub();\n }\n logToFile(`[pi] subagent \"${args.description}\" → ${result.length} chars`);\n return text(result || 'Subagent completed with no textual result.');\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,iBAAiB;CAAC;CAAQ;CAAQ;CAAQ;CAAK;AAErD,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAEZ,SAAS,KAAK,GAGZ;AACA,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAG,CAAC;EAAE,SAAS,EAAE;EAAE;;AAG9D,SAAS,YAAY,SAA0B;CAC7C,MAAM,UAAW,SAAmC;AACpD,KAAI,OAAO,YAAY,SAAU,QAAO;AACxC,KAAI,MAAM,QAAQ,QAAQ,CACxB,QAAO,QACJ,QAAQ,MAA2C;EAClD,MAAM,IAAI;AACV,SAAO,GAAG,SAAS,UAAU,OAAO,EAAE,SAAS;GAC/C,CACD,KAAK,MAAM,EAAE,KAAK,CAClB,KAAK,GAAG;AAEb,QAAO;;AAuBT,SAAgB,wBAAwB,KAAsC;AAC5E,QAAO,WAAW;EAChB,MAAM;EACN,OAAO;EACP,aACE;EACF,eACE;EACF,YAAY,KAAK,OAAO;GACtB,aAAa,KAAK,OAAO,EAAE,aAAa,+BAA+B,CAAC;GACxE,QAAQ,KAAK,OAAO,EAAE,aAAa,qCAAqC,CAAC;GAC1E,CAAC;EAEF,MAAM,QAAQ,KAAK,MAAM;GACvB,MAAM,EAAE,oBAAoB,uBAAuB,mBACjD,IAAI;GAEN,MAAM,SAAS,IAAI,sBAAsB;IACvC,KAAK,IAAI;IACT,UAAU,IAAI;IACd,cAAc;IACd,cAAc;IACd,UAAU;IACV,gBAAgB;IAChB,mBAAmB;IACnB,UAAU;IACV,oBAAoB,CAAC,IAAI,gBAAgB;IAC1C,CAAC;AACF,SAAM,OAAO,QAAQ;GAErB,MAAM,EAAE,SAAS,UAAU,MAAM,mBAAmB;IAClD,OAAO,IAAI;IACX,eAAe,IAAI;IACnB,KAAK,IAAI;IACT,gBAAgB,eAAe,SAAS,IAAI,IAAI;IAChD,gBAAgB;IAChB,OAAO;IACP,aAAa,CAAC,IAAI,SAAS;IAC5B,CAAC;GAEF,IAAI,SAAS;GACb,IAAI;GASJ,MAAM,QAAQ,MAAM,WAAW,MAAM;AACnC,QAAI,EAAE,SAAS,eAAe;AAC5B,SAAK,EAAE,SAA+B,SAAS,YAC7C,aAAY,EAAE;KAChB,MAAM,IAAI,YAAY,EAAE,QAAQ,CAAC,MAAM;AACvC,SAAI,EAAG,UAAS;;KAElB;AACF,aAAU,2BAA2B,KAAK,cAAc;AACxD,OAAI;AACF,UAAM,MAAM,OAAO,KAAK,OAAO;IAC/B,MAAM,UAAU,uBAAuB,UAAU;AACjD,QAAI,QAAS,OAAM,IAAI,MAAM,oBAAoB,QAAQ,UAAU;aAC3D;AACR,WAAO;;AAET,aAAU,kBAAkB,KAAK,YAAY,MAAM,OAAO,OAAO,QAAQ;AACzE,UAAO,KAAK,UAAU,6CAA6C;;EAEtE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"task-BOScSm4W.js","names":[],"sources":["../src/agent/runner/harness/pi/task.ts"],"sourcesContent":["/**\n * Orchestrator-mode execution on pi: one fresh pi session per unit of work —\n * the seed plan, or one drained task. The linear pipeline's concerns (skill\n * menu, todo panel, event-plan cleanup) stay in `index.ts`; this module builds\n * the leaner per-task session: gateway model, security fence, the task's\n * allowed coding tools, the wizard env tools, and the in-process orchestrator\n * queue tools.\n *\n * The task's `allowedTools` / `disallowedTools` arrive in the wizard's tool\n * vocabulary (`Read`, `Edit`, `Glob`, …, plus MCP-qualified orchestrator names\n * from `agentRunTools`). pi is where they become real: allowed names decide\n * which pi tool definitions get registered at all, and the disallow list is\n * ALSO handed to the security fence, so a name that never got registered stays\n * blocked even if the model hallucinates it.\n *\n * Loaded lazily from `index.ts` (typebox/ESM constraint, same as tools.ts).\n */\n\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport {\n Harness,\n Sequence,\n WIZARD_REMARK_EVENT_NAME,\n wizardUserAgentForProgram,\n} from '@shared/constants';\nimport {\n allowsPostHogMcp,\n queueTools,\n renderToolInventory,\n} from '../../../agent-prompt-loader';\nimport { AgentErrorType } from '../../../agent-interface';\nimport { REMARK_INSTRUCTION } from '../../../signals';\nimport { AgentOutputSignals } from '../../../output-signals';\nimport { TaskStatus } from '../../sequence/orchestrator/queue';\nimport type { OrchestratorToolsContext } from '../../sequence/orchestrator/queue-tools';\nimport type { AgentResult, TaskRunInputs } from '../types';\nimport { gatewayAuth, type GatewayAuth } from '../../../gateway-session';\nimport { currentAccessToken } from '@shared/oauth-session';\nimport {\n buildGatewayProvider,\n GATEWAY_PROVIDER,\n withGatewayRemint,\n} from './gateway';\nimport { runErrorType } from './completion';\nimport { bindPiCancellation } from './cancellation';\nimport { classifyRunFailure, ErrorCodes } from '@shared/errors';\nimport { assembleCommandments } from '../../switchboard/commandments';\nimport {\n applyOutroMarkers,\n buildScrubbedEnv,\n extractText,\n lastStatusLine,\n withMode,\n} from './index';\nimport { createAioCapture } from '../../../aio-capture';\n\n/** wizard tool vocabulary → the pi tool definitions it unlocks. */\nconst CODING_TOOL_MAP: Record<string, readonly string[]> = {\n Read: ['read'],\n Edit: ['edit'],\n Write: ['write'],\n Bash: ['bash'],\n Glob: ['find', 'ls'],\n Grep: ['grep'],\n};\n\n/** `mcp__wizard-tools__wizard_ask` → `wizard_ask`; native names pass through. */\nfunction shortToolName(name: string): string {\n return name.replace(/^mcp__.+?__/, '');\n}\n\n/**\n * The pi coding tools a task may use. An empty allow list means \"no\n * restriction\" (mirrors the SDK), so every coding tool registers.\n */\nexport function allowedPiCodingTools(\n allowedTools: readonly string[] | undefined,\n): Set<string> {\n const allowed = (allowedTools ?? []).map(shortToolName);\n const names = allowed.length\n ? allowed.flatMap((name) => CODING_TOOL_MAP[name] ?? [])\n : Object.values(CODING_TOOL_MAP).flat();\n return new Set(names);\n}\n\n/**\n * The orchestrator queue tools this agent gets. Everything not disallowed:\n * the seed's frontmatter disallows `complete_task` (it is not a task), a\n * task's disallows `enqueue_task` (the seed owns the graph).\n */\nexport function allowedOrchestratorTools(\n disallowedTools: readonly string[] | undefined,\n): Set<string> {\n return new Set(queueTools(disallowedTools ?? []));\n}\n\n/**\n * The wizard tools a task gets. Four are always on — their handlers are fenced\n * and the coding tasks depend on them. The rest are opt-in per task through\n * its frontmatter: `wizard_ask` stops the run until a person answers, and the\n * skill-menu pair (`load_skill_menu`, `install_skill`) lets a task pull its\n * own skill variant.\n */\nconst ALWAYS_ON_WIZARD_TOOLS = [\n 'check_env_keys',\n 'set_env_values',\n 'detect_package_manager',\n 'publish_handoff',\n];\n\nconst OPT_IN_WIZARD_TOOLS = [\n 'wizard_ask',\n 'load_skill_menu',\n 'install_skill',\n // Audit programs only: they declare the ledger tools on `allowedTools`.\n 'audit_seed_checks',\n 'audit_add_checks',\n 'audit_resolve_checks',\n];\n\nexport function allowedPiWizardTools(\n allowedTools: readonly string[] | undefined,\n): Set<string> {\n const allowed = (allowedTools ?? []).map(shortToolName);\n return new Set([\n ...ALWAYS_ON_WIZARD_TOOLS,\n ...OPT_IN_WIZARD_TOOLS.filter((tool) => allowed.includes(tool)),\n ]);\n}\n\n/**\n * The disallow list for the security fence: the wizard-vocabulary names as\n * given (the fence translates pi built-ins to the same vocabulary) plus the\n * short names, so a disallowed orchestrator tool is blocked under the name pi\n * would call it by.\n */\nexport function fenceDisallowList(\n disallowedTools: readonly string[] | undefined,\n): string[] {\n const names = disallowedTools ?? [];\n return [...new Set([...names, ...names.map(shortToolName)])];\n}\n\n/** Nudges when the session returns without the work reaching a terminal state. */\nconst MAX_TASK_NUDGES = 3;\n\nconst TASK_NUDGE =\n 'You have not called complete_task yet. Finish your task now: if the work is done, call complete_task with your handoff; if it cannot be done, call it with status \"failed\" or \"not needed\" and say why.';\n\nconst SEED_NUDGE =\n 'The queue is still empty. Seed it now with enqueue_task calls for the task graph you planned.';\n\n/** Whether this unit of work has reached its terminal state. */\nfunction isSettled(ctx: OrchestratorToolsContext): boolean {\n if (!ctx.currentTaskId) {\n // The seed's job is a seeded queue, not a complete_task call.\n return ctx.store.list().length > 0;\n }\n const task = ctx.store.get(ctx.currentTaskId);\n return (\n !!task &&\n (task.status === TaskStatus.Done ||\n task.status === TaskStatus.Failed ||\n task.status === TaskStatus.Skipped)\n );\n}\n\nexport async function runPiTask(inputs: TaskRunInputs): Promise<AgentResult> {\n if (inputs.signal?.aborted) {\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n }\n const {\n config,\n input,\n boot,\n emit,\n prompt,\n spinner,\n model: modelId,\n effort,\n allowedTools,\n disallowedTools,\n askBridge,\n orchestrator,\n spinnerMessage,\n successMessage,\n errorMessage,\n requestRemark,\n analyticsProperties,\n } = inputs;\n\n if (spinnerMessage) spinner.start(spinnerMessage);\n\n const capture = createAioCapture({\n enabled: input.flags.captureAio,\n projectApiKey: boot.credentials.projectApiKey,\n apiHost: boot.credentials.host.apiHost,\n runTags: boot.wizardMetadata,\n });\n\n const startTime = Date.now();\n const signals = new AgentOutputSignals();\n let assistantTurns = 0;\n const runDurations = () => {\n const durationMs = Date.now() - startTime;\n return {\n duration_ms: durationMs,\n duration_seconds: Math.round(durationMs / 1000),\n };\n };\n // How this task run failed: the closed AgentErrorType it is about to return.\n // Without it every terminal path below — a security termination, a rate\n // limit, a gateway error — arrived as the same unlabelled event, so a task\n // agent that died could be counted but not diagnosed. A security stop is not\n // an error, hence \"failure mode\" over \"error\".\n //\n // Not `reason`: the linear sequence emits this same event with a `reason`\n // holding the agent's free-text [ABORT] string, and one property cannot be\n // both a closed enum and unbounded prose without making either unreadable.\n const captureAborted = (failureMode: AgentErrorType) =>\n analytics.wizardCapture('agent aborted', {\n failure_mode: failureMode,\n ...runDurations(),\n model: modelId,\n ...analyticsProperties,\n });\n\n let mcpCleanup: (() => void) | undefined;\n let aioFailed = true;\n let cancellation: ReturnType<typeof bindPiCancellation> | undefined;\n try {\n const sdk = await import('@earendil-works/pi-coding-agent');\n const {\n createAgentSession,\n DefaultResourceLoader,\n SessionManager,\n AuthStorage,\n ModelRegistry,\n getAgentDir,\n createLsToolDefinition,\n createFindToolDefinition,\n createGrepToolDefinition,\n createBashToolDefinition,\n createReadToolDefinition,\n createEditToolDefinition,\n createWriteToolDefinition,\n } = sdk;\n\n // Reads the live OAuth token, so a mid-run rotation re-mints on the new one.\n const refreshAuth = async () =>\n gatewayAuth(\n boot.credentials.host,\n await currentAccessToken(boot.credentials),\n boot.programId,\n );\n const auth = await refreshAuth();\n const providerInputs = (current: GatewayAuth) => ({\n gatewayUrl: current.gatewayUrl,\n accessToken: current.token,\n teamId: current.teamId,\n wizardMetadata: boot.wizardMetadata,\n wizardFlags: boot.wizardFlags,\n modelId,\n // Per-task agents own their effort via the prompt frontmatter, falling\n // back to the model table.\n effort,\n });\n const { provider, caps } = buildGatewayProvider(providerInputs(auth));\n const registry = ModelRegistry.inMemory(AuthStorage.create());\n registry.registerProvider(GATEWAY_PROVIDER, provider as never);\n const model = registry.find(GATEWAY_PROVIDER, modelId);\n if (!model) {\n return {\n kind: 'failure',\n classification: AgentErrorType.API_ERROR,\n message: 'pi: gateway model could not be resolved',\n };\n }\n\n // Shared flag: true while a wizard_ask overlay is open. The ask tool sets\n // it (onAskPendingChange, below); the security fence reads it to pause\n // Write/Edit until the answer comes back. Wired the same way as the linear\n // pi run — without it the warehouse task could mutate files while its\n // credential prompt sits open (up to LONGER_ASK_TIMEOUT_MS).\n const askState = { pending: false };\n\n // The same fail-closed fence as the linear run, with the task's disallow\n // list layered in (both the wizard-vocabulary and pi-short names).\n const { createSecurityExtension } = await import('./security');\n const security = createSecurityExtension({\n disallowedTools: fenceDisallowList(disallowedTools),\n triageProvider: boot.triageProvider,\n getWizardAskPending: () => askState.pending,\n });\n const { prewarmYaraScanner } = await import('../../../yara-hooks');\n void prewarmYaraScanner();\n\n // PostHog MCP, for the tasks whose prompt requests it. Tasks that never\n // asked skip the handshake and never see a posthog tool.\n const extensionFactories = [security.factory] as Array<\n (pi: unknown) => void\n >;\n let posthogMcp = false;\n if (allowsPostHogMcp(allowedTools)) {\n try {\n const { setupPostHogMcp } = await import('./mcp');\n const mcp = await setupPostHogMcp({\n mcpUrl: boot.credentials.host.mcpUrl,\n accessToken: await currentAccessToken(boot.credentials),\n // Same `program:` marker as the linear run, so a task's MCP writes are\n // attributed to the program that queued it. `anthropic/index.ts` uses\n // `programId` as the label on the task path too.\n userAgent: wizardUserAgentForProgram(config.programId),\n });\n extensionFactories.push(mcp.extensionFactory);\n mcpCleanup = mcp.cleanup;\n posthogMcp = true;\n } catch (err) {\n try {\n mcpCleanup?.();\n } catch {\n /* Setup cleanup is best effort. */\n }\n mcpCleanup = undefined;\n // Silent here reads as a task failure minutes later: a task that asked\n // for this tool can only skip or fail without it.\n logToFile(`[pi-task] PostHog MCP setup skipped: ${String(err)}`);\n analytics.wizardCapture('mcp setup failed', {\n harness: 'pi',\n scope: 'task',\n error: String(err).slice(0, 300),\n });\n }\n }\n\n const codingTools = allowedPiCodingTools(allowedTools);\n const orchestratorTools = allowedOrchestratorTools(disallowedTools);\n\n const resourceLoader = new DefaultResourceLoader({\n cwd: input.installDir,\n agentDir: getAgentDir(),\n systemPrompt: assembleCommandments({\n program: config.programId,\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n caps: { bash: codingTools.has('bash'), posthogMcp },\n }),\n noExtensions: true,\n noSkills: true,\n noContextFiles: true,\n noPromptTemplates: true,\n noThemes: true,\n extensionFactories,\n });\n await resourceLoader.reload();\n\n // The task's coding tools, gated by its allow list. Reads and searches run\n // in parallel; mutating tools stay sequential. Bash subprocesses get the\n // scrubbed env, same as the linear run.\n const dir = input.installDir;\n const codingToolFactories = {\n read: () => withMode(createReadToolDefinition(dir), 'parallel'),\n edit: () => withMode(createEditToolDefinition(dir), 'sequential'),\n write: () => withMode(createWriteToolDefinition(dir), 'sequential'),\n bash: () =>\n withMode(\n createBashToolDefinition(dir, {\n spawnHook: (ctx) => ({ ...ctx, env: buildScrubbedEnv() }),\n }),\n 'sequential',\n ),\n ls: () => withMode(createLsToolDefinition(dir), 'parallel'),\n find: () => withMode(createFindToolDefinition(dir), 'parallel'),\n grep: () => withMode(createGrepToolDefinition(dir), 'parallel'),\n } as const;\n const codingToolDefs = Object.entries(codingToolFactories)\n .filter(([name]) => codingTools.has(name))\n .map(([, make]) => make());\n\n // Wizard env + package-manager tools are always on — their handlers are\n // fenced, and init/build tasks depend on them. publish_handoff rides\n // along (it only emits) so the report task can publish the handoff; every\n // other task is told, on the call, that it still owes a complete_task.\n const { createWizardPiTools } = await import('./tools');\n const wizardToolNames = allowedPiWizardTools(allowedTools);\n const wizardTools = createWizardPiTools({\n workingDirectory: dir,\n skillsBaseUrl: boot.skillsBaseUrl,\n triageProvider: boot.triageProvider,\n emit,\n taskAgent: orchestrator.currentTaskId !== undefined,\n // Present only for a task allowed to ask; without it wizard_ask errors\n // instead of hanging on a prompt nobody will ever see.\n askBridge,\n // Pause Write/Edit while the ask overlay is open (see askState above).\n onAskPendingChange: (pending) => {\n askState.pending = pending;\n },\n }).filter((t) => wizardToolNames.has(t.name));\n\n const { createPiOrchestratorTools } = await import('./orchestrator-tools');\n const queueTools = createPiOrchestratorTools(orchestrator).filter((t) =>\n orchestratorTools.has(t.name),\n );\n\n const customTools = [...codingToolDefs, ...wizardTools, ...queueTools];\n const { session: agentSession } = await createAgentSession({\n model,\n modelRegistry: registry,\n thinkingLevel: caps.thinkingLevel,\n cwd: dir,\n sessionManager: SessionManager.inMemory(dir),\n resourceLoader,\n noTools: 'builtin',\n customTools,\n });\n cancellation = bindPiCancellation(inputs.signal, agentSession, (error) => {\n logToFile(`[pi-task] abort failed: ${String(error)}`);\n });\n await agentSession.bindExtensions({});\n\n // A turn that ends on a 401 from an aged bearer re-mints once and\n // continues with the nudge the task would get anyway.\n const turns = withGatewayRemint({\n signal: inputs.signal,\n session: agentSession,\n registry,\n auth,\n refreshAuth,\n providerInputs,\n continueText: () =>\n orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE,\n onRemint: () => {\n logToFile('[pi-task] gateway token renewed after a 401; continuing');\n analytics.wizardCapture('gateway token reminted', { harness: 'pi' });\n },\n });\n\n // The one complete list: exactly the tools registered on this session, in\n // the names the agent will call them by. posthog_exec binds as an extension.\n const toolNames = [\n ...customTools.map((t) => t.name),\n ...(posthogMcp ? ['posthog_exec'] : []),\n ];\n logToFile(`[pi-task] tools passed to model: ${toolNames.join(', ')}`);\n const taskPrompt = `${prompt}\\n\\n${renderToolInventory(toolNames)}`;\n\n const unsubscribe = agentSession.subscribe((event) => {\n // Mirror the turn into AIO. No-op when --capture-aio is off. Runs\n // before the role guard so the module's own filter is authoritative.\n capture.captureFromPiMessageEndEvent(event);\n\n switch (event.type) {\n case 'message_end': {\n // User prompts also emit message_end; only assistant turns count.\n if ((event.message as { role?: string })?.role !== 'assistant') {\n break;\n }\n assistantTurns += 1;\n turns.noteAssistantTurn(event.message);\n const assistant = extractText(event.message).trim();\n if (assistant) {\n logToFile(`[pi-task] assistant: ${assistant.slice(0, 1000)}`);\n applyOutroMarkers(assistant, emit);\n const statusText = lastStatusLine(assistant);\n if (statusText) {\n emit({ kind: 'status', message: statusText });\n spinner.message(statusText);\n }\n for (const line of assistant.split('\\n')) signals.push(line);\n }\n break;\n }\n case 'tool_execution_start': {\n const args = JSON.stringify(event.args ?? {}).slice(0, 200);\n logToFile(`[pi-task] → ${event.toolName} ${args}`);\n break;\n }\n case 'tool_execution_end': {\n if (event.isError) {\n const detail =\n typeof event.result === 'string'\n ? event.result\n : JSON.stringify(event.result ?? '');\n logToFile(`[pi-task] ✗ ${event.toolName}: ${detail.slice(0, 300)}`);\n }\n break;\n }\n default:\n break;\n }\n });\n\n // Seed AIO capture with this task's prompt — includes any handoff data\n // from prior tasks (the orchestrator bakes it into the prompt string\n // before it reaches this call site).\n capture.setInitialPrompt(taskPrompt);\n\n let terminal = turns.terminalFailure();\n try {\n if (inputs.signal?.aborted)\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n await turns.prompt(taskPrompt);\n terminal = turns.terminalFailure();\n\n // pi's prompt() resolves the moment a turn carries no tool call — which\n // an agent mid-plan does emit. While the work has not reached its\n // terminal state (task not reported, seed queue still empty), nudge.\n let nudges = 0;\n while (\n nudges < MAX_TASK_NUDGES &&\n !security.state.criticalViolation &&\n !inputs.signal?.aborted &&\n !terminal &&\n !isSettled(orchestrator)\n ) {\n nudges += 1;\n logToFile(\n `[pi-task] completion guard: not settled, nudge ${nudges}/${MAX_TASK_NUDGES}`,\n );\n await turns.prompt(\n orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE,\n );\n terminal = turns.terminalFailure();\n }\n\n if (\n requestRemark &&\n !security.state.criticalViolation &&\n !terminal &&\n !inputs.signal?.aborted\n ) {\n try {\n await agentSession.prompt(REMARK_INSTRUCTION);\n } catch (err) {\n logToFile(`[pi-task] remark request failed: ${String(err)}`);\n }\n }\n } finally {\n try {\n unsubscribe();\n } catch {\n /* Keep the terminal result. */\n }\n }\n\n if (inputs.signal?.aborted) {\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n }\n\n if (terminal && !security.state.criticalViolation) {\n if (errorMessage || spinnerMessage)\n spinner.stop(errorMessage ?? 'Task failed');\n captureAborted(terminal.classification);\n if (terminal.status === 401) {\n return {\n kind: 'decided_failure',\n failure: {\n code: ErrorCodes.AuthInvalidOrExpired,\n message: 'Authentication failed (401)',\n detail: { providerMessage: terminal.message },\n },\n };\n }\n return terminal.classification === AgentErrorType.ABORT\n ? {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: terminal.message,\n }\n : {\n kind: 'failure',\n classification: terminal.classification,\n message: terminal.message,\n };\n }\n\n if (security.state.criticalViolation) {\n spinner.stop('Security violation detected');\n logToFile(\n `[pi-task] terminated: YARA violation (blocked ${security.state.blockedCount} call(s))`,\n );\n captureAborted(AgentErrorType.YARA_VIOLATION);\n return { kind: 'failure', classification: AgentErrorType.YARA_VIOLATION };\n }\n\n const remark = signals.remark();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\n }\n\n const stats = agentSession.getSessionStats();\n const durations = runDurations();\n analytics.wizardCapture('agent completed', {\n ...durations,\n model: modelId,\n num_turns: assistantTurns,\n input_tokens: stats.tokens.input,\n output_tokens: stats.tokens.output,\n cache_creation_input_tokens: stats.tokens.cacheWrite,\n cache_read_input_tokens: stats.tokens.cacheRead,\n ...analyticsProperties,\n });\n // Per-task usage on one parseable line so a run's per-task time and cost are\n // observable from the log, not only from analytics.\n const taskType =\n typeof (analyticsProperties as { task_type?: unknown })?.task_type ===\n 'string'\n ? (analyticsProperties as { task_type: string }).task_type\n : modelId;\n logToFile(\n `[pi-task] usage task=${taskType} model=${modelId} effort=${caps.thinkingLevel} dur=${durations.duration_seconds}s turns=${assistantTurns} in=${stats.tokens.input} out=${stats.tokens.output} cacheR=${stats.tokens.cacheRead} cacheW=${stats.tokens.cacheWrite}`,\n );\n if (successMessage) spinner.stop(successMessage);\n aioFailed = false;\n return { kind: 'success' };\n } catch (err) {\n if (inputs.signal?.aborted) {\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n }\n const message = err instanceof Error ? err.message : String(err);\n logToFile(`[pi-task] run error: ${message}`);\n if (errorMessage || spinnerMessage) {\n spinner.stop(errorMessage ?? 'Task failed');\n }\n const coded = classifyRunFailure(err);\n if (coded.coded && err instanceof Error) {\n captureAborted(AgentErrorType.API_ERROR);\n return {\n kind: 'decided_failure',\n failure: { code: coded.code, message: coded.message, error: err },\n };\n }\n const classification = runErrorType(message);\n captureAborted(classification);\n return {\n kind: 'failure',\n classification,\n message,\n error: err instanceof Error ? err : undefined,\n };\n } finally {\n await cancellation?.settle();\n try {\n mcpCleanup?.();\n } catch {\n /* Keep the terminal result. */\n }\n try {\n capture.finishPiRun(aioFailed);\n } catch {\n /* Telemetry is best effort. */\n }\n }\n}\n"],"mappings":"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"}
|