@posthog/wizard 2.84.0 → 2.84.1
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/dist/{agent-interface-BNssNetq.js → agent-interface-D8mrarFD.js} +10 -10
- package/dist/{agent-interface-BNssNetq.js.map → agent-interface-D8mrarFD.js.map} +1 -1
- package/dist/{agent-prompt-loader-iV11XVEF.js → agent-prompt-loader-Bt6mrV7q.js} +6 -6
- package/dist/{agent-prompt-loader-iV11XVEF.js.map → agent-prompt-loader-Bt6mrV7q.js.map} +1 -1
- package/dist/{analytics-Drox5LSW.js → analytics-CX2kvq0R.js} +2 -2
- package/dist/{analytics-Drox5LSW.js.map → analytics-CX2kvq0R.js.map} +1 -1
- package/dist/analytics-DgLAjlIK.js +2 -0
- package/dist/{api-Dfu655Qt.js → api-BOP8gF35.js} +2 -2
- package/dist/{api-Dfu655Qt.js.map → api-BOP8gF35.js.map} +1 -1
- package/dist/api-BphZo4f2.js +2 -0
- package/dist/api-key-login-BEi3wymJ.js +2 -0
- package/dist/{api-key-login-BgwfAXfv.js → api-key-login-DbrZknHp.js} +5 -5
- package/dist/{api-key-login-BgwfAXfv.js.map → api-key-login-DbrZknHp.js.map} +1 -1
- package/dist/bin.js +1 -1
- package/dist/{bounded-fs-TRL6IsoU.js → bounded-fs-BENh-Jzc.js} +3 -3
- package/dist/{bounded-fs-TRL6IsoU.js.map → bounded-fs-BENh-Jzc.js.map} +1 -1
- package/dist/{ci-install-nc1IKuX2.js → ci-install-DnjuAl1l.js} +4 -4
- package/dist/{ci-install-nc1IKuX2.js.map → ci-install-DnjuAl1l.js.map} +1 -1
- package/dist/{debug-BfO_Txo8.js → debug-C_9xDg9j.js} +3 -3
- package/dist/{debug-BfO_Txo8.js.map → debug-C_9xDg9j.js.map} +1 -1
- package/dist/{gateway-CLQQb96F.js → gateway-C6_zPLyx.js} +5 -5
- package/dist/{gateway-CLQQb96F.js.map → gateway-C6_zPLyx.js.map} +1 -1
- package/dist/{headless-DMO8N_FD.js → headless-BIQpNAHc.js} +2 -2
- package/dist/{headless-DMO8N_FD.js.map → headless-BIQpNAHc.js.map} +1 -1
- package/dist/{headless-CHGNEzO3.js → headless-sWzDbxAu.js} +1 -1
- package/dist/{host-resolution-DftfSjJD.js → host-resolution-C3ctTKNg.js} +4 -4
- package/dist/{host-resolution-DftfSjJD.js.map → host-resolution-C3ctTKNg.js.map} +1 -1
- package/dist/{install-MWoo1hXL.js → install-CDhljVka.js} +1 -1
- package/dist/{install-C9FPM37m.js → install-Dayhuaun.js} +5 -5
- package/dist/{install-C9FPM37m.js.map → install-Dayhuaun.js.map} +1 -1
- package/dist/{interactive-DOnolJHB.js → interactive-CjbttghI.js} +3 -3
- package/dist/{interactive-DOnolJHB.js.map → interactive-CjbttghI.js.map} +1 -1
- package/dist/{links-CatXPRxH.js → links-BOOGSL_G.js} +3 -3
- package/dist/{links-CatXPRxH.js.map → links-BOOGSL_G.js.map} +1 -1
- package/dist/{main-CINmZtoW.js → main-MzQaWN8w.js} +20 -20
- package/dist/{main-CINmZtoW.js.map → main-MzQaWN8w.js.map} +1 -1
- package/dist/{mcp-KVV6Z_bq.js → mcp-BwlD260T.js} +3 -3
- package/dist/{mcp-KVV6Z_bq.js.map → mcp-BwlD260T.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-M6UnLgGX.js → mcp-prompt-streaming-CoplNPI8.js} +5 -5
- package/dist/{mcp-prompt-streaming-M6UnLgGX.js.map → mcp-prompt-streaming-CoplNPI8.js.map} +1 -1
- package/dist/{models-B0huHItG.js → models-HwzgIr2E.js} +2 -2
- package/dist/{models-B0huHItG.js.map → models-HwzgIr2E.js.map} +1 -1
- package/dist/{oauth-session-9EU3ky0v.js → oauth-session-C4JV-yA9.js} +2 -2
- package/dist/{oauth-session-9EU3ky0v.js.map → oauth-session-C4JV-yA9.js.map} +1 -1
- package/dist/{orchestrator-tools-QWyDLu_7.js → orchestrator-tools-Dr4w80Tn.js} +3 -3
- package/dist/{orchestrator-tools-QWyDLu_7.js.map → orchestrator-tools-Dr4w80Tn.js.map} +1 -1
- package/dist/{package-manager-B-9I_fAT.js → package-manager-Zw0JN1Kh.js} +4 -4
- package/dist/{package-manager-B-9I_fAT.js.map → package-manager-Zw0JN1Kh.js.map} +1 -1
- package/dist/{pi-BSnSk6rv.js → pi-qzRJcTgu.js} +12 -12
- package/dist/{pi-BSnSk6rv.js.map → pi-qzRJcTgu.js.map} +1 -1
- package/dist/{playground-Drz1qszs.js → playground-B0SpcGYQ.js} +4 -4
- package/dist/{playground-Drz1qszs.js.map → playground-B0SpcGYQ.js.map} +1 -1
- package/dist/{posthog-integration-mIQGBc1f.js → posthog-integration-4vQjbU-f.js} +21 -21
- package/dist/{posthog-integration-mIQGBc1f.js.map → posthog-integration-4vQjbU-f.js.map} +1 -1
- package/dist/{programs-mxhRXwP-.js → programs-7aIrstFy.js} +15 -15
- package/dist/{programs-mxhRXwP-.js.map → programs-7aIrstFy.js.map} +1 -1
- package/dist/programs-B9Iu3Mye.js +3 -0
- package/dist/provisioning-BG_lh4hk.js +2 -0
- package/dist/{provisioning-CHUjae9V.js → provisioning-BMJ3J0Vb.js} +4 -4
- package/dist/{provisioning-CHUjae9V.js.map → provisioning-BMJ3J0Vb.js.map} +1 -1
- package/dist/{queue-tools-PFG6H93Q.js → queue-tools-D8HPaGck.js} +4 -4
- package/dist/{queue-tools-PFG6H93Q.js.map → queue-tools-D8HPaGck.js.map} +1 -1
- package/dist/{results-5yCKf2cI.js → results-8xNiBbaP.js} +2 -2
- package/dist/{results-5yCKf2cI.js.map → results-8xNiBbaP.js.map} +1 -1
- package/dist/{run-D5TKJuSL.js → run-DUcJFhzP.js} +7 -7
- package/dist/{run-D5TKJuSL.js.map → run-DUcJFhzP.js.map} +1 -1
- package/dist/{run-W03b6jNM.js → run-Dte7AuRg.js} +10 -10
- package/dist/{run-W03b6jNM.js.map → run-Dte7AuRg.js.map} +1 -1
- package/dist/run-tool-BXpuefYa.js +2 -0
- package/dist/{run-tool-DTQF8mmI.js → run-tool-sdhoPp9H.js} +13 -13
- package/dist/{run-tool-DTQF8mmI.js.map → run-tool-sdhoPp9H.js.map} +1 -1
- package/dist/{runners-B_84kPyf.js → runners-DxbNGuKH.js} +8 -8
- package/dist/{runners-B_84kPyf.js.map → runners-DxbNGuKH.js.map} +1 -1
- package/dist/{security-pApVawYb.js → security-BMqK129i.js} +5 -5
- package/dist/{security-pApVawYb.js.map → security-BMqK129i.js.map} +1 -1
- package/dist/{setup-utils-CQY8PHjR.js → setup-utils-DBOw3lnu.js} +3 -3
- package/dist/{setup-utils-CQY8PHjR.js.map → setup-utils-DBOw3lnu.js.map} +1 -1
- package/dist/{start-playground-DAw1GbLt.js → start-playground-Uf3nte-P.js} +7 -7
- package/dist/{start-playground-DAw1GbLt.js.map → start-playground-Uf3nte-P.js.map} +1 -1
- package/dist/{subagent-YLGSvgoZ.js → subagent-DYza5kN6.js} +3 -3
- package/dist/{subagent-YLGSvgoZ.js.map → subagent-DYza5kN6.js.map} +1 -1
- package/dist/{task-3hRSovmK.js → task-CiStV2CD.js} +14 -14
- package/dist/{task-3hRSovmK.js.map → task-CiStV2CD.js.map} +1 -1
- package/dist/{telemetry-BPBzqsb_.js → telemetry-DLLuQiCt.js} +2 -2
- package/dist/{telemetry-BPBzqsb_.js.map → telemetry-DLLuQiCt.js.map} +1 -1
- package/dist/{terminal-DZsYFbm9.js → terminal-B4EXuZra.js} +18 -18
- package/dist/{terminal-DZsYFbm9.js.map → terminal-B4EXuZra.js.map} +1 -1
- package/dist/{tools-D_AtrhbO.js → tools-BTAi_RrA.js} +13 -13
- package/dist/{tools-D_AtrhbO.js.map → tools-BTAi_RrA.js.map} +1 -1
- package/dist/{tools-DyzRi_dQ.js → tools-M5ENAiPc.js} +8 -8
- package/dist/{tools-DyzRi_dQ.js.map → tools-M5ENAiPc.js.map} +1 -1
- package/dist/{tui-UI6ctBQJ.js → tui-DU9MmsAK.js} +1 -1
- package/dist/{tui-CRp0iCuT.js → tui-DigZ-Q1i.js} +4 -4
- package/dist/{tui-CRp0iCuT.js.map → tui-DigZ-Q1i.js.map} +1 -1
- package/dist/{upload-step-DrYXnpzI.js → upload-step-VNWq-3aL.js} +3 -3
- package/dist/{upload-step-DrYXnpzI.js.map → upload-step-VNWq-3aL.js.map} +1 -1
- package/dist/{urls-WeqfBLP4.js → urls-Bgngk5HI.js} +2 -2
- package/dist/{urls-WeqfBLP4.js.map → urls-Bgngk5HI.js.map} +1 -1
- package/dist/{wizard-abort-BwEefm6P.js → wizard-abort-CdKk1lpi.js} +4 -4
- package/dist/{wizard-abort-BwEefm6P.js.map → wizard-abort-CdKk1lpi.js.map} +1 -1
- package/dist/{yara-hooks-MDtFbuoM.js → yara-hooks-DCgSsXFP.js} +1 -1
- package/dist/{yara-hooks-aDbdsitJ.js → yara-hooks-DPqy6eVo.js} +4 -4
- package/dist/{yara-hooks-aDbdsitJ.js.map → yara-hooks-DPqy6eVo.js.map} +1 -1
- package/package.json +6 -1
- package/dist/analytics-Ix9HEra3.js +0 -2
- package/dist/api-H2UaPgjv.js +0 -2
- package/dist/api-key-login-Cow3UDAN.js +0 -2
- package/dist/programs-Bv0_YHzE.js +0 -3
- package/dist/provisioning-BVsxSNve.js +0 -2
- package/dist/run-tool-DjERhu7G.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-CLQQb96F.js","names":[],"sources":["../src/shared/health-checks/endpoints.ts","../src/agent/gateway-session.ts","../src/agent/signals.ts","../src/agent/runner/harness/pi/gateway.ts"],"sourcesContent":["import { AWS_SKILLS_BASE_URL, GITHUB_SKILLS_BASE_URL } from '@shared/constants';\nimport { logToFile } from '@utils/debug';\nimport { ServiceHealthStatus, type BaseHealthResult } from './types';\n\n// Direct gateway and skill-origin checks distinguish HTTP failures from connection failures.\n\nfunction noConnectionResult(error: string, attempts: number): BaseHealthResult {\n return {\n status: ServiceHealthStatus.NoConnection,\n error,\n rawIndicator: attempts > 1 ? `attempts=${attempts}` : undefined,\n };\n}\n\nfunction downResult(error: string): BaseHealthResult {\n return { status: ServiceHealthStatus.Down, error };\n}\n\n// Backoffs sized to cover typical wifi flakiness — a single dropped\n// packet recovers via the 500ms retry; a wifi access point reconnect\n// or wifi↔LTE handoff (2-5s) is caught by the 2000ms retry. Tighter\n// schedules miss multi-second blips because all retries land in the\n// same dead window.\nconst RETRY_BACKOFFS_MS = [500, 2000];\n\nasync function attemptFetch(\n url: string,\n timeoutMs: number,\n redirect: 'follow' | 'manual' | 'error',\n): Promise<\n | { kind: 'response'; res: Response }\n | { kind: 'error'; error: Error; timedOut: boolean }\n> {\n const controller = new AbortController();\n const tid = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal, redirect });\n clearTimeout(tid);\n return { kind: 'response', res };\n } catch (e) {\n clearTimeout(tid);\n const err = e instanceof Error ? e : new Error('Unknown error');\n return { kind: 'error', error: err, timedOut: err.name === 'AbortError' };\n }\n}\n\n// Exported so tests can pin the retry/taxonomy machinery directly.\nexport async function fetchEndpointHealth(\n url: string,\n timeoutMs = 5000,\n isExpectedStatus: (status: number) => boolean = (s) => s === 200,\n redirect: 'follow' | 'manual' | 'error' = 'follow',\n): Promise<BaseHealthResult> {\n // Total attempts = 1 initial + RETRY_BACKOFFS_MS.length retries. Both\n // unexpected HTTP statuses (4xx/5xx) and network errors trigger a retry:\n // transient 5xx and Cloudflare edge blips often recover on a retry, and\n // even nominally deterministic 4xx can be transient (CDN propagation\n // lag after a release, token rotation, rate-limit window resets). GETs\n // are idempotent so retrying is safe.\n //\n // Final status if every attempt fails:\n // - At least one HTTP response observed → `Down` (server-side evidence)\n // - Only network errors observed → `NoConnection`\n let lastHttpStatus: number | null = null;\n let lastError = 'Unknown error';\n let attempts = 0;\n\n for (let i = 0; i <= RETRY_BACKOFFS_MS.length; i++) {\n if (i > 0) {\n const wait = RETRY_BACKOFFS_MS[i - 1];\n logToFile(\n `[health-checks] retry ${i}/${RETRY_BACKOFFS_MS.length} for ${url} in ${wait}ms (last: ${lastError})`,\n );\n await new Promise((r) => setTimeout(r, wait));\n }\n attempts++;\n\n const outcome = await attemptFetch(url, timeoutMs, redirect);\n\n if (outcome.kind === 'response') {\n const res = outcome.res;\n if (isExpectedStatus(res.status)) {\n const result: BaseHealthResult = {\n status: ServiceHealthStatus.Healthy,\n rawIndicator:\n attempts > 1\n ? `HTTP ${res.status} (attempts=${attempts})`\n : `HTTP ${res.status}`,\n };\n logToFile(\n `[health-checks] GET ${url} -> ${result.status}` +\n ` (${result.rawIndicator})`,\n );\n return result;\n }\n lastHttpStatus = res.status;\n lastError = `HTTP ${res.status}`;\n continue;\n }\n\n lastError = outcome.timedOut\n ? `Request timed out after ${timeoutMs}ms`\n : outcome.error.message;\n }\n\n const result =\n lastHttpStatus !== null\n ? downResult(`HTTP ${lastHttpStatus} (attempts=${attempts})`)\n : noConnectionResult(lastError, attempts);\n logToFile(\n `[health-checks] GET ${url} -> ${result.status}` +\n ` (attempts=${attempts}, ${result.error})`,\n );\n return result;\n}\n\nexport const checkLlmGatewayHealth = (\n gatewayUrl: string,\n): Promise<BaseHealthResult> =>\n fetchEndpointHealth(new URL('/readyz', gatewayUrl).href);\n\n/**\n * Skills are published to two origins under the same filenames and\n * `fetchWithRetry` fails over between them, so the run is only blocked when\n * neither answers. Probed in parallel — sequential probes would double the\n * worst case past `READINESS_TIMEOUT_MS`.\n */\nexport const checkSkillsOriginHealth = async (): Promise<BaseHealthResult> => {\n const [github, aws] = await Promise.all([\n fetchEndpointHealth(`${GITHUB_SKILLS_BASE_URL}/skill-menu.json`),\n fetchEndpointHealth(`${AWS_SKILLS_BASE_URL}/skill-menu.json`),\n ]);\n return combineOriginHealth(github, aws);\n};\n\n/**\n * Mirrors `fetchWithRetry`: a download tries GitHub, then AWS, so the run is\n * only blocked when neither origin answers. Whichever failure the probes saw,\n * one origin serving means skills are reachable.\n */\nfunction combineOriginHealth(\n github: BaseHealthResult,\n aws: BaseHealthResult,\n): BaseHealthResult {\n if (github.status === ServiceHealthStatus.Healthy) {\n // Naming the dead origin makes a one-sided outage legible in the log and\n // in the readiness reasons, where the status alone reads as \"fine\".\n return aws.status === ServiceHealthStatus.Healthy\n ? github\n : withIndicatorSuffix(github, 'aws unavailable');\n }\n\n if (aws.status === ServiceHealthStatus.Healthy) {\n return withIndicatorSuffix(aws, 'via aws, github unavailable');\n }\n\n const error = `github: ${github.error ?? 'unknown'} | aws: ${\n aws.error ?? 'unknown'\n }`;\n const confirmedDown =\n github.status === ServiceHealthStatus.Down ||\n aws.status === ServiceHealthStatus.Down;\n return {\n status: confirmedDown\n ? ServiceHealthStatus.Down\n : ServiceHealthStatus.NoConnection,\n error,\n // Keeps the `attempts=N` the blocked-readiness analytics parses.\n rawIndicator: github.rawIndicator ?? aws.rawIndicator,\n };\n}\n\nfunction withIndicatorSuffix(\n result: BaseHealthResult,\n suffix: string,\n): BaseHealthResult {\n return {\n ...result,\n rawIndicator: result.rawIndicator\n ? `${result.rawIndicator} (${suffix})`\n : suffix,\n };\n}\n","/**\n * Gateway auth for a wizard run: a `phe_` scoped token the backend mints, with\n * pinned attribution, a spend cap and an expiry.\n *\n * Every mint failure throws: a silent downgrade would spend uncapped,\n * unattributed money to hide an outage.\n */\n\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { ErrorCodes, WizardError } from '@shared/errors';\nimport type { GatewayCredential } from '@shared/api';\nimport type { HostResolution } from '@shared/host-resolution';\nimport { oauthLoginKey } from '@shared/oauth-session';\nimport { checkLlmGatewayHealth } from '@shared/health-checks/endpoints';\nimport { ServiceHealthStatus } from '@shared/health-checks/types';\nimport { IS_PRODUCTION_BUILD } from '@env';\n\nexport interface GatewayAuth {\n /** Base URL for model calls (no `/v1`; transports append their route). */\n gatewayUrl: string;\n /** Gateway bearer, minted normally or supplied directly by CI. */\n token: string;\n /** Team verified by the mint, or explicitly supplied for CI attribution. */\n teamId?: number;\n /**\n * Instant past which a 401 on this bearer is age rather than a bad\n * credential: the cache re-mints past it, and a session still holding the\n * old bearer may re-mint once. Before it the mint has to be trusted.\n */\n refreshAtMs: number;\n}\n\ninterface CachedAuth {\n key: string;\n auth: GatewayAuth;\n /** Re-resolve once past this instant. */\n staleAtMs: number;\n}\n\nlet cached: CachedAuth | null = null;\n/**\n * Shared so concurrent callers mint once. The orchestrator starts every runnable\n * task at once, and each would otherwise take its own token and its own cap.\n */\nlet inFlight: { key: string; promise: Promise<GatewayAuth> } | null = null;\nlet ciAuth: GatewayAuth | null = null;\n\n// Snapshot CI supplies a gateway bearer without minting or re-minting.\nexport function configureGatewayCredentialsForCI(\n token: string,\n projectId: number,\n gatewayUrl: string,\n): void {\n if (IS_PRODUCTION_BUILD)\n throw new Error('CI gateway auth requires a non-production build');\n if (!token.trim() || !Number.isSafeInteger(projectId) || projectId <= 0) {\n throw new Error('CI gateway auth requires a token and valid project ID');\n }\n if (\n !/^https?:\\/\\//.test(gatewayUrl) ||\n !isTrustedGatewayUrl(gatewayUrl, '')\n ) {\n throw new Error('CI gateway auth requires a trusted gateway origin');\n }\n resetGatewaySession();\n ciAuth = {\n token: token.trim(),\n teamId: projectId,\n gatewayUrl: gatewayUrl.replace(/\\/+$/, ''),\n refreshAtMs: Infinity,\n };\n}\n\n/** Use this run's pre-issued gateway token, or mint when it has none; the keyed mint cache stays. */\nexport function useRunGatewayCredential(\n gateway: GatewayCredential | undefined,\n projectId: number,\n): void {\n if (gateway) {\n configureGatewayCredentialsForCI(gateway.token, projectId, gateway.url);\n return;\n }\n ciAuth = null;\n}\n\n/**\n * Adoption floor. The anthropic subprocess holds its credential until a 401\n * forces a re-mint, so a token below this would churn mints.\n */\nconst MIN_USABLE_TTL_MS = 2 * 60 * 1000;\n/** Re-resolve at this fraction of the token's life, leaving a usable remainder. */\nconst REFRESH_AT_FRACTION = 0.8;\n// Exceeds the backend's own 10s gateway timeout: a slow mint that lands after the\n// CLI hangs up spends a daily mint and orphans a live token.\nconst MINT_TIMEOUT_MS = 20_000;\n/** Longer than any refusal the mint writes; a body past this is not a message. */\nconst MAX_REFUSAL_DETAIL_LENGTH = 500;\n/** Outcomes are short snake_case labels; anything longer is not one. */\nconst MAX_REFUSAL_OUTCOME_LENGTH = 64;\n\n/** Resolve this run's gateway auth, minting and re-minting near expiry. */\nexport async function gatewayAuth(\n host: HostResolution,\n accessToken: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (ciAuth) return ciAuth;\n // Keyed by program: a token pins `wizard:<program>`, so reusing one across\n // programs bills the wrong budget. Keyed by login rather than OAuth token:\n // the token rotates mid-run, and each new one would spend a weekly mint.\n const key = `${host.apiHost}\\n${oauthLoginKey(accessToken)}\\n${\n program ?? ''\n }`;\n if (cached && cached.key === key && Date.now() < cached.staleAtMs) {\n return cached.auth;\n }\n if (inFlight && inFlight.key === key) return inFlight.promise;\n const promise = resolveGatewayAuth(host, accessToken, key, program);\n inFlight = { key, promise };\n try {\n return await promise;\n } finally {\n if (inFlight?.promise === promise) inFlight = null;\n }\n}\n\nasync function resolveGatewayAuth(\n host: HostResolution,\n accessToken: string,\n key: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (!program) {\n // Every run has one; absent means a caller was not wired, and the spend would\n // be unattributable.\n logToFile('[gateway] run has no program id; failing the run');\n throw new GatewayMintFailed(\n 'this run has no program to attribute its spend to',\n );\n }\n const minted = await mintGatewayToken(host, accessToken, program);\n const health = await checkLlmGatewayHealth(minted.gatewayUrl);\n if (health.status !== ServiceHealthStatus.Healthy) {\n throw new WizardError(\n 'The PostHog AI gateway is unavailable. Please try again later.',\n undefined,\n ErrorCodes.EnvServiceOutage,\n );\n }\n const expiresAtMs = Date.parse(minted.expiresAt);\n const ttlMs = expiresAtMs - Date.now();\n if (!Number.isFinite(expiresAtMs) || ttlMs < MIN_USABLE_TTL_MS) {\n // Expired, unreadable, or too short to serve a session. Adopting it would\n // 401 mid-run, and downgrading would spend the rest of the run uncapped.\n logToFile(\n `[gateway] mint returned a token with ${ttlMs}ms of life; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway issued a token with ${ttlMs}ms of life`,\n );\n }\n const staleAtMs = Date.now() + ttlMs * REFRESH_AT_FRACTION;\n // Only failures are logged otherwise, so a successful run leaves no\n // local trace. Never log the token itself.\n logToFile(\n `[gateway] minted a scoped token: program=${program} team=${\n minted.teamId ?? 'unknown'\n } ttl=${Math.round(ttlMs / 1000)}s url=${minted.gatewayUrl}`,\n );\n const auth: GatewayAuth = {\n gatewayUrl: minted.gatewayUrl,\n token: minted.token,\n teamId: minted.teamId,\n refreshAtMs: staleAtMs,\n };\n cached = { key, auth, staleAtMs };\n return auth;\n}\n\n/** Test hook: drop the cached auth so the next call re-resolves. */\nexport function resetGatewaySession(): void {\n cached = null;\n inFlight = null;\n ciAuth = null;\n}\n\n/** Whether a 401 on this bearer may be age (past its refresh instant) rather than a bad credential. */\nexport function isPastRefresh(auth: GatewayAuth, now = Date.now()): boolean {\n return now >= auth.refreshAtMs;\n}\n\n/**\n * Whether a server-supplied origin may receive a bearer and prompt content:\n * https (loopback excepted), and either a current cloud gateway or the host the run\n * authenticated against.\n */\nexport function isTrustedGatewayUrl(value: string, apiHost: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n // Consumers append routes to this value, so anything beyond an origin\n // (path, query, fragment, userinfo) would build a malformed endpoint.\n if (\n url.pathname !== '/' ||\n url.search ||\n url.hash ||\n url.username ||\n url.password\n ) {\n return false;\n }\n const localhost =\n url.hostname === 'localhost' ||\n url.hostname === '127.0.0.1' ||\n url.hostname === 'host.docker.internal';\n // Loopback is the dev gateway, and is the one case allowed over http.\n if (localhost) return true;\n if (url.protocol !== 'https:') return false;\n if (url.hostname.endsWith('.posthog.com')) {\n return (\n url.origin === 'https://ai-gateway.us.posthog.com' ||\n url.origin === 'https://ai-gateway.eu.posthog.com'\n );\n }\n try {\n return url.hostname === new URL(apiHost).hostname;\n } catch {\n return false;\n }\n}\n\ninterface MintedToken {\n token: string;\n expiresAt: string;\n gatewayUrl: string;\n teamId?: number;\n}\n\n/**\n * A deliberate refusal from the mint endpoint, as opposed to the mint being\n * unavailable. Thrown so the run stops instead of proceeding without the\n * controls the refusal was enforcing. A WizardError, so the runners print its\n * message as-is and `wizardAbort` resolves its code.\n */\nexport class GatewayMintRefused extends WizardError {\n readonly status: number;\n /** The backend's refusal outcome (`blocked`, `throttled`, ...), when it sent one. */\n readonly outcome?: string;\n\n constructor(status: number, message: string, outcome?: string) {\n super(message, { status, outcome }, ErrorCodes.GatewayMintRefused);\n this.name = 'GatewayMintRefused';\n this.status = status;\n this.outcome = outcome;\n }\n}\n\n/**\n * The mint could not produce a usable credential: unreachable, a 5xx, or a\n * response the client cannot use.\n */\nexport class GatewayMintFailed extends WizardError {\n constructor(message: string) {\n super(message, undefined, ErrorCodes.GatewayMintFailed);\n this.name = 'GatewayMintFailed';\n }\n}\n\n/**\n * Whether a mint status means \"refused this run\" rather than \"not available\".\n * 429 the daily run limit, 403 revoked project access, 400 a login covering\n * more than one project, 401 a credential the mint does not accept, 404 an\n * instance without the mint endpoint.\n */\nfunction isMintRefusal(status: number): boolean {\n return (\n status === 400 ||\n status === 401 ||\n status === 403 ||\n status === 404 ||\n status === 429\n );\n}\n\ninterface MintRefusal {\n detail?: string;\n outcome?: string;\n}\n\n/**\n * The server's own reason for a refusal, when it sent one. DRF answers every\n * refusal as `{\"detail\": \"...\", \"code\": \"<outcome>\"}`; the blocklist's detail\n * names the contact address, which the fixed messages below cannot. `code` is\n * the backend's own label for the refusal (`outcome` on older backends) and\n * rides the client event.\n */\nfunction cleanRefusalText(value: unknown): string {\n if (typeof value !== 'string') return '';\n // Not because the server sends escapes, but because this string is printed\n // straight to a terminal: sanitizing at the boundary means no later message\n // can move the cursor or repaint the screen.\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, ' ').trim();\n}\n\nasync function readRefusal(resp: Response): Promise<MintRefusal> {\n try {\n const body = (await resp.json()) as {\n detail?: unknown;\n code?: unknown;\n outcome?: unknown;\n };\n const detail = cleanRefusalText(body?.detail);\n // The DRF handler flattens a dict detail, so the outcome rides as `code`.\n // A `code` that cleans to nothing does not shadow a usable `outcome`.\n const outcome =\n cleanRefusalText(body?.code) || cleanRefusalText(body?.outcome);\n return {\n detail:\n detail.length > 0 && detail.length <= MAX_REFUSAL_DETAIL_LENGTH\n ? detail\n : undefined,\n outcome:\n outcome.length > 0 && outcome.length <= MAX_REFUSAL_OUTCOME_LENGTH\n ? outcome\n : undefined,\n };\n } catch {\n return {};\n }\n}\n\nfunction mintRefusalMessage(status: number, detail?: string): string {\n if (detail) return detail;\n switch (status) {\n case 429:\n return 'This wizard program has used its daily run limit. Try again tomorrow.';\n case 403:\n return 'Your access to this project has changed. Re-authenticate and try again.';\n case 400:\n // The only 400 the mint answers is the exactly-one-project check.\n return 'Your PostHog login must cover exactly one project. Re-authenticate and try again.';\n case 401:\n return 'PostHog did not accept this login. Re-authenticate with `npx @posthog/wizard@latest`.';\n case 404:\n return 'This PostHog instance does not issue gateway tokens. Upgrade with `npx @posthog/wizard@latest` and try again.';\n default:\n return 'The PostHog gateway refused this run.';\n }\n}\n\nasync function mintGatewayToken(\n host: HostResolution,\n accessToken: string,\n program: string,\n): Promise<MintedToken> {\n try {\n const resp = await fetch(`${host.apiHost}/api/wizard/gateway_token/`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n // The flag tells the server this build reads a refusal, so it may answer\n // with the reason. A build that omits it gets a 404, which is its signal\n // to fall back to the legacy gateway.\n body: JSON.stringify({ program, reads_refusal_reason: true }),\n signal: AbortSignal.timeout(MINT_TIMEOUT_MS),\n });\n if (!resp.ok) {\n if (isMintRefusal(resp.status)) {\n const refusal = await readRefusal(resp);\n logToFile(\n `[gateway] mint refused with HTTP ${resp.status} (${\n refusal.outcome ?? 'no outcome'\n }); failing the run`,\n );\n // The terminal denial event for this run. The backend's own event has\n // no run id, so this is what joins a refusal to the session.\n analytics.wizardCapture('gateway mint refused', {\n status: resp.status,\n outcome: refusal.outcome,\n program,\n });\n throw new GatewayMintRefused(\n resp.status,\n mintRefusalMessage(resp.status, refusal.detail),\n refusal.outcome,\n );\n }\n logToFile(\n `[gateway] mint failed with HTTP ${resp.status}; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway could not issue a token (HTTP ${resp.status})`,\n );\n }\n const body = (await resp.json()) as {\n token?: string;\n expires_at?: string;\n gateway_url?: string;\n team_id?: number;\n };\n // Checked one at a time, not in a loop, so each clause narrows the optional\n // field for the return below and each names itself in the failure.\n if (!body.token) {\n logToFile('[gateway] mint response omitted token; failing the run');\n throw new GatewayMintFailed('mint response omitted token');\n }\n if (!body.expires_at) {\n logToFile('[gateway] mint response omitted expires_at; failing the run');\n throw new GatewayMintFailed('mint response omitted expires_at');\n }\n if (!body.gateway_url) {\n logToFile('[gateway] mint response omitted gateway_url; failing the run');\n throw new GatewayMintFailed('mint response omitted gateway_url');\n }\n if (!isTrustedGatewayUrl(body.gateway_url, host.apiHost)) {\n logToFile(\n '[gateway] mint returned an untrusted gateway url; failing the run',\n );\n throw new GatewayMintFailed('mint returned an untrusted gateway url');\n }\n const gatewayUrl = new URL(body.gateway_url);\n if (\n ['localhost', '127.0.0.1'].includes(new URL(host.apiHost).hostname) &&\n gatewayUrl.hostname === 'host.docker.internal'\n ) {\n gatewayUrl.hostname = 'localhost';\n }\n return {\n token: body.token,\n expiresAt: body.expires_at,\n gatewayUrl: gatewayUrl.origin,\n teamId: body.team_id,\n };\n } catch (e) {\n // Decisions and failures both pass through: this catch exists for transport\n // errors, and folding the others into it would lose the reason.\n if (e instanceof GatewayMintRefused || e instanceof GatewayMintFailed)\n throw e;\n logToFile(\n `[gateway] mint transport failure (${String(e)}); failing the run`,\n );\n throw new GatewayMintFailed(\n `could not reach the PostHog gateway (${String(e)})`,\n );\n }\n}\n\n/**\n * The v2 run-metadata carrier: one JSON blob for the `X-PostHog-Properties`\n * header. Plain keys only, since the gateway strips `$`-prefixed keys as reserved,\n * so feature-flag variants land as `wizard_flag_<key>` instead of the legacy\n * `$feature/<key>` (dashboards keying on `$feature/wizard-*` read the new key\n * post-cutover).\n */\nexport function buildWizardPropertiesBlob(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): string {\n // The gateway pins `$ai_product` to `wizard:<program>`, and rejects a legacy\n // product override on a scoped token, so the unprefixed key every cost and\n // error consumer reads is only present if this blob declares it.\n const props: Record<string, string | number> = { ai_product: 'wizard' };\n if (teamId !== undefined) props.team_id = teamId;\n for (const [key, value] of Object.entries(wizardMetadata)) {\n props[stripPropertyPrefix(key)] = value;\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n props[`wizard_flag_${flagKey.toLowerCase()}`] = variant;\n }\n return JSON.stringify(props);\n}\n\nconst LEGACY_PROPERTY_PREFIX = 'X-POSTHOG-PROPERTY-';\n\nfunction stripPropertyPrefix(key: string): string {\n return key.toUpperCase().startsWith(LEGACY_PROPERTY_PREFIX)\n ? key.slice(LEGACY_PROPERTY_PREFIX.length).toLowerCase()\n : key;\n}\n","/**\n * Agent signal vocabulary — the marker strings the agent emits and the error\n * taxonomy the runner returns. Kept as a dependency-free leaf module so both\n * `agent-interface.ts` and `output-signals.ts` can import it without a cycle.\n */\n\nexport const AgentSignals = {\n /** Signal emitted when the agent reports progress to the user */\n STATUS: '[STATUS]',\n /** Signal emitted when the agent cannot access the PostHog MCP server */\n ERROR_MCP_MISSING: '[ERROR-MCP-MISSING]',\n /** Signal emitted when the agent cannot access the setup resource */\n ERROR_RESOURCE_MISSING: '[ERROR-RESOURCE-MISSING]',\n /**\n * Signal emitted when `install_skill` failed and the agent is continuing\n * the integration WITHOUT the skill (best-effort from its own knowledge).\n * Format: \"[SKILL-INSTALL-FAILED] <skill id — reason>\". Non-fatal by\n * design: a freestyled integration beats an outright failure, but the run\n * must be measurable — the runner captures it as an analytics event.\n */\n SKILL_INSTALL_FAILED: '[SKILL-INSTALL-FAILED]',\n /**\n * Signal emitted when the agent cannot complete the program and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Programs can declare an onAbort handler to render a custom screen.\n */\n ABORT: '[ABORT]',\n /**\n * The AI gateway's refusal marker. A reply that starts with\n * \"[posthog:abort] <reason>\" is a refusal, and the run ends as an abort.\n * The marker elsewhere in a reply is ordinary text.\n */\n POLICY_ABORT: '[posthog:abort]',\n /** Signal emitted when the agent provides a remark about its run */\n WIZARD_REMARK: '[WIZARD-REMARK]',\n /** Signal prefix for benchmark logging */\n BENCHMARK: '[BENCHMARK]',\n /**\n * Signal emitted when the agent has created a PostHog dashboard for the\n * user. Format: `[DASHBOARD_URL] <full https url>`. The URL is captured\n * onto `session.dashboardUrl` and surfaced by programs in their outro.\n */\n DASHBOARD_URL: '[DASHBOARD_URL]',\n /**\n * Signal emitted when the agent has uploaded a report to a PostHog\n * notebook. Format: `[NOTEBOOK_URL] <full https url>`. The URL is captured\n * onto `session.notebookUrl` and surfaced by programs in their outro.\n */\n NOTEBOOK_URL: '[NOTEBOOK_URL]',\n} as const;\n\nexport type AgentSignal = (typeof AgentSignals)[keyof typeof AgentSignals];\n\n/**\n * The reason of a reply that starts with the policy abort marker, or undefined\n * for any other reply. The marker ends at a non-word character or the end of\n * the text, and the reason is the rest of that first line.\n */\nexport function policyAbortReason(text: string): string | undefined {\n const reply = text.trimStart();\n if (!reply.startsWith(AgentSignals.POLICY_ABORT)) return undefined;\n const rest = reply.slice(AgentSignals.POLICY_ABORT.length);\n if (/^\\w/.test(rest)) return undefined;\n return rest.split('\\n')[0].trim() || 'The AI gateway refused this request';\n}\n\n/**\n * End-of-run reflection ask, shared by both harnesses so remarks are\n * comparable. The format clause comes FIRST and nothing trails the marker —\n * literal models (gpt-5-mini) echo whatever text follows it. The parser also\n * drops remarks that quote this instruction (see AgentOutputSignals.remark).\n */\nexport const REMARK_INSTRUCTION = `Reply with a single line that starts with ${AgentSignals.WIZARD_REMARK} and no other lines. In that line, state briefly what information or guidance would have been useful to have in the integration prompt or documentation for this run — specifically anything that would have prevented tool failures, erroneous edits, or other wasted turns.`;\n\n/**\n * First prompt of a session resumed after a mid-run re-mint. The transcript\n * carries the work so far; the model only needs to pick it up.\n */\nexport const RESUME_INSTRUCTION =\n 'Your previous request failed with a transient gateway authentication error that has since been fixed. Continue the task from where you left off.';\n\n/**\n * Error types that can be returned from agent execution.\n * These correspond to the error signals that the agent emits.\n */\nexport enum AgentErrorType {\n /** The configured run deadline elapsed and the SDK query was aborted */\n AGENTIC_DETECTION_TIMEOUT = 'WIZARD_AGENTIC_DETECTION_TIMEOUT',\n /** Agent could not access the PostHog MCP server */\n MCP_MISSING = 'WIZARD_MCP_MISSING',\n /** Agent could not access the setup resource */\n RESOURCE_MISSING = 'WIZARD_RESOURCE_MISSING',\n /** API rate limit exceeded */\n RATE_LIMIT = 'WIZARD_RATE_LIMIT',\n /** Generic API error */\n API_ERROR = 'WIZARD_API_ERROR',\n /** YARA scanner detected a security violation */\n YARA_VIOLATION = 'WIZARD_YARA_VIOLATION',\n /** Agent intentionally aborted the program (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n /** Agent ended without making a single tool call — a no-op run that did no work */\n NO_PROGRESS = 'WIZARD_NO_PROGRESS',\n /** A schema-bound run ended without output matching its schema */\n INVALID_STRUCTURED_OUTPUT = 'WIZARD_INVALID_STRUCTURED_OUTPUT',\n /** Agent acted but stopped short — planned tasks left open and/or no skill installed */\n INCOMPLETE_TASKS = 'WIZARD_INCOMPLETE_TASKS',\n}\n","/**\n * PostHog AI gateway provider spec for pi sessions, shared by the linear run\n * and the orchestrator's per-task runs so both speak to the gateway\n * identically: bearer auth, the wizard properties header, transport shape\n * inferred from the model id. `createGatewayRuntime` registers the spec on a\n * pi ModelRuntime that holds nothing else.\n */\n\nimport {\n buildWizardPropertiesBlob,\n isPastRefresh,\n type GatewayAuth,\n} from '../../../gateway-session';\nimport {\n modelCapabilities,\n type ThinkingLevel,\n} from '../../switchboard/models';\nimport { AgentErrorType } from '../../../signals';\n\n/** Provider registered on the in-memory registry for this run. */\nexport const GATEWAY_PROVIDER = 'posthog-gateway';\n\n/** The pi transports the gateway serves. */\nexport type GatewayApi =\n | 'anthropic-messages'\n | 'openai-completions'\n | 'openai-responses';\n\n/**\n * The gateway speaks two shapes on two endpoints: Anthropic models over\n * `anthropic-messages` (the SDK appends `/v1/messages`, so the base URL has no\n * `/v1`), and OpenAI-class models (`openai/gpt-5`, …) over an OpenAI shape at\n * `/v1/responses` or `/v1/chat/completions` (base URL keeps `/v1`). Infer the\n * shape from the model id so a pair's model selects the right transport.\n *\n * OpenAI models take the Responses API because OpenAI rejects function tools\n * combined with `reasoning_effort` on chat completions and every task sends both.\n */\nexport function gatewayApiFor(modelId: string): GatewayApi {\n return modelId.startsWith('openai/')\n ? 'openai-responses'\n : 'anthropic-messages';\n}\n\n/**\n * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: one\n * `X-PostHog-Properties` JSON blob (Bedrock fallback is native, so no opt-in)\n * plus the 1M context beta, since pi otherwise runs at 200k and overflows on\n * larger projects (the post-run compaction failures).\n */\nexport function buildGatewayHeaders(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): Record<string, string> {\n return {\n 'anthropic-beta': 'context-1m-2025-08-07',\n 'X-PostHog-Properties': buildWizardPropertiesBlob(\n wizardMetadata,\n wizardFlags,\n teamId,\n ),\n };\n}\n\nexport interface GatewayProviderInputs {\n gatewayUrl: string;\n accessToken: string;\n /** Customer team for the properties blob (from the mint response). */\n teamId?: number;\n wizardMetadata: Record<string, string>;\n wizardFlags: Record<string, string>;\n modelId: string;\n // Resolved effort override — the switchboard's flag/payload pick on linear\n // runs, the prompt-frontmatter effort on per-task agents. Overrides the\n // table default for a reasoning model when set.\n effort?: ThinkingLevel;\n}\n\n/**\n * One gateway model spec — the single description of how to reach a model on\n * the gateway. The provider spec below wraps it for pi's registry; one-shot\n * callers (scan triage) hand it straight to `completeSimple`.\n */\nexport function buildGatewayModel(inputs: GatewayProviderInputs) {\n const { gatewayUrl, wizardMetadata, wizardFlags, modelId, teamId } = inputs;\n const api = gatewayApiFor(modelId);\n return {\n id: modelId,\n name: `${modelId} (PostHog Gateway)`,\n api,\n provider: GATEWAY_PROVIDER,\n // Anthropic drops /v1 (the SDK appends /v1/messages); the OpenAI shape keeps it.\n baseUrl: api === 'anthropic-messages' ? gatewayUrl : `${gatewayUrl}/v1`,\n // A model trait resolved by the switchboard, not a harness guess:\n // non-reasoning openai models reject `reasoning_effort` (gpt-4o → gateway\n // UnsupportedParamsError → the run no-ops).\n reasoning: modelCapabilities(modelId).reasoning,\n // The gateway refuses `developer` messages on pinned tokens, so pi sends `system`.\n compat: { supportsDeveloperRole: false },\n input: ['text' as const],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 1_000_000,\n maxTokens: 64_000,\n headers: buildGatewayHeaders(wizardMetadata, wizardFlags, teamId),\n };\n}\n\n/**\n * The provider object for `registry.registerProvider(GATEWAY_PROVIDER, …)`,\n * plus the derived traits the session setup needs (`caps.thinkingLevel`).\n */\nexport function buildGatewayProvider(inputs: GatewayProviderInputs): {\n provider: Record<string, unknown>;\n api: GatewayApi;\n caps: ReturnType<typeof modelCapabilities>;\n gatewayUrl: string;\n baseUrl: string;\n} {\n const { gatewayUrl, accessToken, modelId, effort } = inputs;\n const api = gatewayApiFor(modelId);\n // One resolution point for the model's traits and the run's effort override.\n // pi clamps whatever comes out of here against the levels this spec declares,\n // so a level the spec doesn't carry is silently reduced by the session.\n const tableCaps = modelCapabilities(modelId);\n const caps =\n effort && effort !== 'off' && tableCaps.reasoning\n ? modelCapabilities(modelId, effort)\n : effort === 'off' && tableCaps.reasoning\n ? { ...tableCaps, thinkingLevel: 'off' as const }\n : tableCaps;\n\n const model = buildGatewayModel(inputs);\n const provider = {\n name: 'PostHog Gateway',\n baseUrl: model.baseUrl,\n apiKey: accessToken,\n authHeader: true,\n api,\n headers: model.headers,\n models: [model],\n };\n return { provider, api, caps, gatewayUrl, baseUrl: model.baseUrl };\n}\n\n/** The part of a pi assistant turn that says why it failed. */\nexport interface GatewayTurnError {\n errorMessage?: string;\n diagnostics?: { error?: { name?: string; code?: string | number } }[];\n}\n\nexport type GatewayTerminalFailure = {\n classification:\n | AgentErrorType.API_ERROR\n | AgentErrorType.RATE_LIMIT\n | AgentErrorType.ABORT;\n message: string;\n status?: number;\n};\n\nexport function gatewayTerminalFailure(\n turn: ({ stopReason?: string } & GatewayTurnError) | undefined,\n): GatewayTerminalFailure | undefined {\n if (!turn || (turn.stopReason !== 'error' && turn.stopReason !== 'aborted'))\n return undefined;\n const message =\n turn.errorMessage ||\n (turn.stopReason === 'aborted'\n ? 'Agent turn aborted'\n : 'Gateway request failed');\n if (turn.stopReason === 'aborted') {\n return { classification: AgentErrorType.ABORT, message };\n }\n const codes =\n turn.diagnostics\n ?.map((d) => d.error?.code)\n .filter((code) => code !== undefined) ?? [];\n const structuredStatus = codes\n .map(Number)\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n const rateLimited =\n structuredStatus !== undefined\n ? structuredStatus === 429\n : /\\b429\\b|rate limit/i.test(message);\n return {\n classification: rateLimited\n ? AgentErrorType.RATE_LIMIT\n : AgentErrorType.API_ERROR,\n message,\n status:\n structuredStatus ?? (isGatewayAuthRejection(turn) ? 401 : undefined),\n };\n}\n\n/**\n * Whether a turn's error is the gateway rejecting the bearer. pi attaches the\n * SDK's own error to `diagnostics`, so its code decides when one is present.\n * The message match is the fallback for a turn that failed before pi built a\n * diagnostic, where the status survives only as prose.\n */\nexport function isGatewayAuthRejection(\n turn: GatewayTurnError | string | undefined,\n): boolean {\n const { errorMessage, diagnostics } =\n typeof turn === 'string'\n ? { errorMessage: turn, diagnostics: undefined }\n : turn ?? {};\n const structuredStatus = (diagnostics ?? [])\n .map((diagnostic) => Number(diagnostic.error?.code))\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n if (structuredStatus !== undefined) return structuredStatus === 401;\n for (const diagnostic of diagnostics ?? []) {\n const code = diagnostic.error?.code;\n if (code === 401 || code === '401') return true;\n if (/^authentication_?error$/i.test(diagnostic.error?.name ?? ''))\n return true;\n }\n return /\\b401\\b|authentication_error|unauthorized/i.test(errorMessage ?? '');\n}\n\n/** The model runtime a pi session runs on. */\nexport type GatewayRuntime =\n import('@earendil-works/pi-coding-agent').ModelRuntime;\n\n/**\n * A model runtime holding only the gateway provider. Credentials stay in memory,\n * and it reads no models.json and refreshes no catalog over the network, so a\n * run reads nothing of the user's own pi setup.\n */\nexport async function createGatewayRuntime(\n provider: Record<string, unknown>,\n): Promise<GatewayRuntime> {\n const [{ ModelRuntime }, { InMemoryCredentialStore }] = await Promise.all([\n import('@earendil-works/pi-coding-agent'),\n import('@earendil-works/pi-ai'),\n ]);\n const runtime = await ModelRuntime.create({\n credentials: new InMemoryCredentialStore(),\n modelsPath: null,\n allowModelNetwork: false,\n refreshOnCreate: false,\n });\n runtime.registerProvider(GATEWAY_PROVIDER, provider as never);\n return runtime;\n}\n\nexport interface GatewayRemintOptions {\n signal?: AbortSignal;\n session: { prompt(text: string): Promise<void> };\n registry: { registerProvider(providerName: string, config: never): void };\n auth: GatewayAuth;\n /** The cache: the same token while fresh, a new mint past the refresh point. */\n refreshAuth: () => Promise<GatewayAuth>;\n providerInputs: (auth: GatewayAuth) => GatewayProviderInputs;\n /** The prompt that resumes the work after a re-mint. */\n continueText: string | (() => string);\n onRemint?: () => void;\n}\n\n/**\n * Wraps a pi session's prompt(): when a turn ends on a 401 from a bearer past\n * its refresh instant, mint once, re-register the provider with the new\n * bearer (pi resolves the apiKey per request), and continue. A 401 on a fresh\n * bearer, or a second one, is left to the harness's normal failure path.\n */\nexport function withGatewayRemint(opts: GatewayRemintOptions): {\n prompt(text: string): Promise<void>;\n /** Feed every assistant `message_end`; the last turn decides. */\n noteAssistantTurn(message: unknown): void;\n terminalFailure(): GatewayTerminalFailure | undefined;\n} {\n let auth = opts.auth;\n let rejected = false;\n let reminted = false;\n let lastTurn: ({ stopReason?: string } & GatewayTurnError) | undefined;\n return {\n noteAssistantTurn(message) {\n lastTurn = message as\n | ({ stopReason?: string } & GatewayTurnError)\n | undefined;\n rejected =\n lastTurn?.stopReason === 'error' && isGatewayAuthRejection(lastTurn);\n },\n terminalFailure: () => gatewayTerminalFailure(lastTurn),\n async prompt(text) {\n if (opts.signal?.aborted) return;\n rejected = false;\n lastTurn = undefined;\n await opts.session.prompt(text);\n if (opts.signal?.aborted || !rejected || reminted || !isPastRefresh(auth))\n return;\n reminted = true;\n auth = await opts.refreshAuth();\n if (opts.signal?.aborted) return;\n opts.registry.registerProvider(\n GATEWAY_PROVIDER,\n buildGatewayProvider(opts.providerInputs(auth)).provider as never,\n );\n opts.onRemint?.();\n rejected = false;\n lastTurn = undefined;\n const next = opts.continueText;\n if (opts.signal?.aborted) return;\n await opts.session.prompt(typeof next === 'function' ? next() : next);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gateway-C6_zPLyx.js","names":[],"sources":["../src/shared/health-checks/endpoints.ts","../src/agent/gateway-session.ts","../src/agent/signals.ts","../src/agent/runner/harness/pi/gateway.ts"],"sourcesContent":["import { AWS_SKILLS_BASE_URL, GITHUB_SKILLS_BASE_URL } from '@shared/constants';\nimport { logToFile } from '@utils/debug';\nimport { ServiceHealthStatus, type BaseHealthResult } from './types';\n\n// Direct gateway and skill-origin checks distinguish HTTP failures from connection failures.\n\nfunction noConnectionResult(error: string, attempts: number): BaseHealthResult {\n return {\n status: ServiceHealthStatus.NoConnection,\n error,\n rawIndicator: attempts > 1 ? `attempts=${attempts}` : undefined,\n };\n}\n\nfunction downResult(error: string): BaseHealthResult {\n return { status: ServiceHealthStatus.Down, error };\n}\n\n// Backoffs sized to cover typical wifi flakiness — a single dropped\n// packet recovers via the 500ms retry; a wifi access point reconnect\n// or wifi↔LTE handoff (2-5s) is caught by the 2000ms retry. Tighter\n// schedules miss multi-second blips because all retries land in the\n// same dead window.\nconst RETRY_BACKOFFS_MS = [500, 2000];\n\nasync function attemptFetch(\n url: string,\n timeoutMs: number,\n redirect: 'follow' | 'manual' | 'error',\n): Promise<\n | { kind: 'response'; res: Response }\n | { kind: 'error'; error: Error; timedOut: boolean }\n> {\n const controller = new AbortController();\n const tid = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal, redirect });\n clearTimeout(tid);\n return { kind: 'response', res };\n } catch (e) {\n clearTimeout(tid);\n const err = e instanceof Error ? e : new Error('Unknown error');\n return { kind: 'error', error: err, timedOut: err.name === 'AbortError' };\n }\n}\n\n// Exported so tests can pin the retry/taxonomy machinery directly.\nexport async function fetchEndpointHealth(\n url: string,\n timeoutMs = 5000,\n isExpectedStatus: (status: number) => boolean = (s) => s === 200,\n redirect: 'follow' | 'manual' | 'error' = 'follow',\n): Promise<BaseHealthResult> {\n // Total attempts = 1 initial + RETRY_BACKOFFS_MS.length retries. Both\n // unexpected HTTP statuses (4xx/5xx) and network errors trigger a retry:\n // transient 5xx and Cloudflare edge blips often recover on a retry, and\n // even nominally deterministic 4xx can be transient (CDN propagation\n // lag after a release, token rotation, rate-limit window resets). GETs\n // are idempotent so retrying is safe.\n //\n // Final status if every attempt fails:\n // - At least one HTTP response observed → `Down` (server-side evidence)\n // - Only network errors observed → `NoConnection`\n let lastHttpStatus: number | null = null;\n let lastError = 'Unknown error';\n let attempts = 0;\n\n for (let i = 0; i <= RETRY_BACKOFFS_MS.length; i++) {\n if (i > 0) {\n const wait = RETRY_BACKOFFS_MS[i - 1];\n logToFile(\n `[health-checks] retry ${i}/${RETRY_BACKOFFS_MS.length} for ${url} in ${wait}ms (last: ${lastError})`,\n );\n await new Promise((r) => setTimeout(r, wait));\n }\n attempts++;\n\n const outcome = await attemptFetch(url, timeoutMs, redirect);\n\n if (outcome.kind === 'response') {\n const res = outcome.res;\n if (isExpectedStatus(res.status)) {\n const result: BaseHealthResult = {\n status: ServiceHealthStatus.Healthy,\n rawIndicator:\n attempts > 1\n ? `HTTP ${res.status} (attempts=${attempts})`\n : `HTTP ${res.status}`,\n };\n logToFile(\n `[health-checks] GET ${url} -> ${result.status}` +\n ` (${result.rawIndicator})`,\n );\n return result;\n }\n lastHttpStatus = res.status;\n lastError = `HTTP ${res.status}`;\n continue;\n }\n\n lastError = outcome.timedOut\n ? `Request timed out after ${timeoutMs}ms`\n : outcome.error.message;\n }\n\n const result =\n lastHttpStatus !== null\n ? downResult(`HTTP ${lastHttpStatus} (attempts=${attempts})`)\n : noConnectionResult(lastError, attempts);\n logToFile(\n `[health-checks] GET ${url} -> ${result.status}` +\n ` (attempts=${attempts}, ${result.error})`,\n );\n return result;\n}\n\nexport const checkLlmGatewayHealth = (\n gatewayUrl: string,\n): Promise<BaseHealthResult> =>\n fetchEndpointHealth(new URL('/readyz', gatewayUrl).href);\n\n/**\n * Skills are published to two origins under the same filenames and\n * `fetchWithRetry` fails over between them, so the run is only blocked when\n * neither answers. Probed in parallel — sequential probes would double the\n * worst case past `READINESS_TIMEOUT_MS`.\n */\nexport const checkSkillsOriginHealth = async (): Promise<BaseHealthResult> => {\n const [github, aws] = await Promise.all([\n fetchEndpointHealth(`${GITHUB_SKILLS_BASE_URL}/skill-menu.json`),\n fetchEndpointHealth(`${AWS_SKILLS_BASE_URL}/skill-menu.json`),\n ]);\n return combineOriginHealth(github, aws);\n};\n\n/**\n * Mirrors `fetchWithRetry`: a download tries GitHub, then AWS, so the run is\n * only blocked when neither origin answers. Whichever failure the probes saw,\n * one origin serving means skills are reachable.\n */\nfunction combineOriginHealth(\n github: BaseHealthResult,\n aws: BaseHealthResult,\n): BaseHealthResult {\n if (github.status === ServiceHealthStatus.Healthy) {\n // Naming the dead origin makes a one-sided outage legible in the log and\n // in the readiness reasons, where the status alone reads as \"fine\".\n return aws.status === ServiceHealthStatus.Healthy\n ? github\n : withIndicatorSuffix(github, 'aws unavailable');\n }\n\n if (aws.status === ServiceHealthStatus.Healthy) {\n return withIndicatorSuffix(aws, 'via aws, github unavailable');\n }\n\n const error = `github: ${github.error ?? 'unknown'} | aws: ${\n aws.error ?? 'unknown'\n }`;\n const confirmedDown =\n github.status === ServiceHealthStatus.Down ||\n aws.status === ServiceHealthStatus.Down;\n return {\n status: confirmedDown\n ? ServiceHealthStatus.Down\n : ServiceHealthStatus.NoConnection,\n error,\n // Keeps the `attempts=N` the blocked-readiness analytics parses.\n rawIndicator: github.rawIndicator ?? aws.rawIndicator,\n };\n}\n\nfunction withIndicatorSuffix(\n result: BaseHealthResult,\n suffix: string,\n): BaseHealthResult {\n return {\n ...result,\n rawIndicator: result.rawIndicator\n ? `${result.rawIndicator} (${suffix})`\n : suffix,\n };\n}\n","/**\n * Gateway auth for a wizard run: a `phe_` scoped token the backend mints, with\n * pinned attribution, a spend cap and an expiry.\n *\n * Every mint failure throws: a silent downgrade would spend uncapped,\n * unattributed money to hide an outage.\n */\n\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { ErrorCodes, WizardError } from '@shared/errors';\nimport type { GatewayCredential } from '@shared/api';\nimport type { HostResolution } from '@shared/host-resolution';\nimport { oauthLoginKey } from '@shared/oauth-session';\nimport { checkLlmGatewayHealth } from '@shared/health-checks/endpoints';\nimport { ServiceHealthStatus } from '@shared/health-checks/types';\nimport { IS_PRODUCTION_BUILD } from '@env';\n\nexport interface GatewayAuth {\n /** Base URL for model calls (no `/v1`; transports append their route). */\n gatewayUrl: string;\n /** Gateway bearer, minted normally or supplied directly by CI. */\n token: string;\n /** Team verified by the mint, or explicitly supplied for CI attribution. */\n teamId?: number;\n /**\n * Instant past which a 401 on this bearer is age rather than a bad\n * credential: the cache re-mints past it, and a session still holding the\n * old bearer may re-mint once. Before it the mint has to be trusted.\n */\n refreshAtMs: number;\n}\n\ninterface CachedAuth {\n key: string;\n auth: GatewayAuth;\n /** Re-resolve once past this instant. */\n staleAtMs: number;\n}\n\nlet cached: CachedAuth | null = null;\n/**\n * Shared so concurrent callers mint once. The orchestrator starts every runnable\n * task at once, and each would otherwise take its own token and its own cap.\n */\nlet inFlight: { key: string; promise: Promise<GatewayAuth> } | null = null;\nlet ciAuth: GatewayAuth | null = null;\n\n// Snapshot CI supplies a gateway bearer without minting or re-minting.\nexport function configureGatewayCredentialsForCI(\n token: string,\n projectId: number,\n gatewayUrl: string,\n): void {\n if (IS_PRODUCTION_BUILD)\n throw new Error('CI gateway auth requires a non-production build');\n if (!token.trim() || !Number.isSafeInteger(projectId) || projectId <= 0) {\n throw new Error('CI gateway auth requires a token and valid project ID');\n }\n if (\n !/^https?:\\/\\//.test(gatewayUrl) ||\n !isTrustedGatewayUrl(gatewayUrl, '')\n ) {\n throw new Error('CI gateway auth requires a trusted gateway origin');\n }\n resetGatewaySession();\n ciAuth = {\n token: token.trim(),\n teamId: projectId,\n gatewayUrl: gatewayUrl.replace(/\\/+$/, ''),\n refreshAtMs: Infinity,\n };\n}\n\n/** Use this run's pre-issued gateway token, or mint when it has none; the keyed mint cache stays. */\nexport function useRunGatewayCredential(\n gateway: GatewayCredential | undefined,\n projectId: number,\n): void {\n if (gateway) {\n configureGatewayCredentialsForCI(gateway.token, projectId, gateway.url);\n return;\n }\n ciAuth = null;\n}\n\n/**\n * Adoption floor. The anthropic subprocess holds its credential until a 401\n * forces a re-mint, so a token below this would churn mints.\n */\nconst MIN_USABLE_TTL_MS = 2 * 60 * 1000;\n/** Re-resolve at this fraction of the token's life, leaving a usable remainder. */\nconst REFRESH_AT_FRACTION = 0.8;\n// Exceeds the backend's own 10s gateway timeout: a slow mint that lands after the\n// CLI hangs up spends a daily mint and orphans a live token.\nconst MINT_TIMEOUT_MS = 20_000;\n/** Longer than any refusal the mint writes; a body past this is not a message. */\nconst MAX_REFUSAL_DETAIL_LENGTH = 500;\n/** Outcomes are short snake_case labels; anything longer is not one. */\nconst MAX_REFUSAL_OUTCOME_LENGTH = 64;\n\n/** Resolve this run's gateway auth, minting and re-minting near expiry. */\nexport async function gatewayAuth(\n host: HostResolution,\n accessToken: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (ciAuth) return ciAuth;\n // Keyed by program: a token pins `wizard:<program>`, so reusing one across\n // programs bills the wrong budget. Keyed by login rather than OAuth token:\n // the token rotates mid-run, and each new one would spend a weekly mint.\n const key = `${host.apiHost}\\n${oauthLoginKey(accessToken)}\\n${\n program ?? ''\n }`;\n if (cached && cached.key === key && Date.now() < cached.staleAtMs) {\n return cached.auth;\n }\n if (inFlight && inFlight.key === key) return inFlight.promise;\n const promise = resolveGatewayAuth(host, accessToken, key, program);\n inFlight = { key, promise };\n try {\n return await promise;\n } finally {\n if (inFlight?.promise === promise) inFlight = null;\n }\n}\n\nasync function resolveGatewayAuth(\n host: HostResolution,\n accessToken: string,\n key: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (!program) {\n // Every run has one; absent means a caller was not wired, and the spend would\n // be unattributable.\n logToFile('[gateway] run has no program id; failing the run');\n throw new GatewayMintFailed(\n 'this run has no program to attribute its spend to',\n );\n }\n const minted = await mintGatewayToken(host, accessToken, program);\n const health = await checkLlmGatewayHealth(minted.gatewayUrl);\n if (health.status !== ServiceHealthStatus.Healthy) {\n throw new WizardError(\n 'The PostHog AI gateway is unavailable. Please try again later.',\n undefined,\n ErrorCodes.EnvServiceOutage,\n );\n }\n const expiresAtMs = Date.parse(minted.expiresAt);\n const ttlMs = expiresAtMs - Date.now();\n if (!Number.isFinite(expiresAtMs) || ttlMs < MIN_USABLE_TTL_MS) {\n // Expired, unreadable, or too short to serve a session. Adopting it would\n // 401 mid-run, and downgrading would spend the rest of the run uncapped.\n logToFile(\n `[gateway] mint returned a token with ${ttlMs}ms of life; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway issued a token with ${ttlMs}ms of life`,\n );\n }\n const staleAtMs = Date.now() + ttlMs * REFRESH_AT_FRACTION;\n // Only failures are logged otherwise, so a successful run leaves no\n // local trace. Never log the token itself.\n logToFile(\n `[gateway] minted a scoped token: program=${program} team=${\n minted.teamId ?? 'unknown'\n } ttl=${Math.round(ttlMs / 1000)}s url=${minted.gatewayUrl}`,\n );\n const auth: GatewayAuth = {\n gatewayUrl: minted.gatewayUrl,\n token: minted.token,\n teamId: minted.teamId,\n refreshAtMs: staleAtMs,\n };\n cached = { key, auth, staleAtMs };\n return auth;\n}\n\n/** Test hook: drop the cached auth so the next call re-resolves. */\nexport function resetGatewaySession(): void {\n cached = null;\n inFlight = null;\n ciAuth = null;\n}\n\n/** Whether a 401 on this bearer may be age (past its refresh instant) rather than a bad credential. */\nexport function isPastRefresh(auth: GatewayAuth, now = Date.now()): boolean {\n return now >= auth.refreshAtMs;\n}\n\n/**\n * Whether a server-supplied origin may receive a bearer and prompt content:\n * https (loopback excepted), and either a current cloud gateway or the host the run\n * authenticated against.\n */\nexport function isTrustedGatewayUrl(value: string, apiHost: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n // Consumers append routes to this value, so anything beyond an origin\n // (path, query, fragment, userinfo) would build a malformed endpoint.\n if (\n url.pathname !== '/' ||\n url.search ||\n url.hash ||\n url.username ||\n url.password\n ) {\n return false;\n }\n const localhost =\n url.hostname === 'localhost' ||\n url.hostname === '127.0.0.1' ||\n url.hostname === 'host.docker.internal';\n // Loopback is the dev gateway, and is the one case allowed over http.\n if (localhost) return true;\n if (url.protocol !== 'https:') return false;\n if (url.hostname.endsWith('.posthog.com')) {\n return (\n url.origin === 'https://ai-gateway.us.posthog.com' ||\n url.origin === 'https://ai-gateway.eu.posthog.com'\n );\n }\n try {\n return url.hostname === new URL(apiHost).hostname;\n } catch {\n return false;\n }\n}\n\ninterface MintedToken {\n token: string;\n expiresAt: string;\n gatewayUrl: string;\n teamId?: number;\n}\n\n/**\n * A deliberate refusal from the mint endpoint, as opposed to the mint being\n * unavailable. Thrown so the run stops instead of proceeding without the\n * controls the refusal was enforcing. A WizardError, so the runners print its\n * message as-is and `wizardAbort` resolves its code.\n */\nexport class GatewayMintRefused extends WizardError {\n readonly status: number;\n /** The backend's refusal outcome (`blocked`, `throttled`, ...), when it sent one. */\n readonly outcome?: string;\n\n constructor(status: number, message: string, outcome?: string) {\n super(message, { status, outcome }, ErrorCodes.GatewayMintRefused);\n this.name = 'GatewayMintRefused';\n this.status = status;\n this.outcome = outcome;\n }\n}\n\n/**\n * The mint could not produce a usable credential: unreachable, a 5xx, or a\n * response the client cannot use.\n */\nexport class GatewayMintFailed extends WizardError {\n constructor(message: string) {\n super(message, undefined, ErrorCodes.GatewayMintFailed);\n this.name = 'GatewayMintFailed';\n }\n}\n\n/**\n * Whether a mint status means \"refused this run\" rather than \"not available\".\n * 429 the daily run limit, 403 revoked project access, 400 a login covering\n * more than one project, 401 a credential the mint does not accept, 404 an\n * instance without the mint endpoint.\n */\nfunction isMintRefusal(status: number): boolean {\n return (\n status === 400 ||\n status === 401 ||\n status === 403 ||\n status === 404 ||\n status === 429\n );\n}\n\ninterface MintRefusal {\n detail?: string;\n outcome?: string;\n}\n\n/**\n * The server's own reason for a refusal, when it sent one. DRF answers every\n * refusal as `{\"detail\": \"...\", \"code\": \"<outcome>\"}`; the blocklist's detail\n * names the contact address, which the fixed messages below cannot. `code` is\n * the backend's own label for the refusal (`outcome` on older backends) and\n * rides the client event.\n */\nfunction cleanRefusalText(value: unknown): string {\n if (typeof value !== 'string') return '';\n // Not because the server sends escapes, but because this string is printed\n // straight to a terminal: sanitizing at the boundary means no later message\n // can move the cursor or repaint the screen.\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, ' ').trim();\n}\n\nasync function readRefusal(resp: Response): Promise<MintRefusal> {\n try {\n const body = (await resp.json()) as {\n detail?: unknown;\n code?: unknown;\n outcome?: unknown;\n };\n const detail = cleanRefusalText(body?.detail);\n // The DRF handler flattens a dict detail, so the outcome rides as `code`.\n // A `code` that cleans to nothing does not shadow a usable `outcome`.\n const outcome =\n cleanRefusalText(body?.code) || cleanRefusalText(body?.outcome);\n return {\n detail:\n detail.length > 0 && detail.length <= MAX_REFUSAL_DETAIL_LENGTH\n ? detail\n : undefined,\n outcome:\n outcome.length > 0 && outcome.length <= MAX_REFUSAL_OUTCOME_LENGTH\n ? outcome\n : undefined,\n };\n } catch {\n return {};\n }\n}\n\nfunction mintRefusalMessage(status: number, detail?: string): string {\n if (detail) return detail;\n switch (status) {\n case 429:\n return 'This wizard program has used its daily run limit. Try again tomorrow.';\n case 403:\n return 'Your access to this project has changed. Re-authenticate and try again.';\n case 400:\n // The only 400 the mint answers is the exactly-one-project check.\n return 'Your PostHog login must cover exactly one project. Re-authenticate and try again.';\n case 401:\n return 'PostHog did not accept this login. Re-authenticate with `npx @posthog/wizard@latest`.';\n case 404:\n return 'This PostHog instance does not issue gateway tokens. Upgrade with `npx @posthog/wizard@latest` and try again.';\n default:\n return 'The PostHog gateway refused this run.';\n }\n}\n\nasync function mintGatewayToken(\n host: HostResolution,\n accessToken: string,\n program: string,\n): Promise<MintedToken> {\n try {\n const resp = await fetch(`${host.apiHost}/api/wizard/gateway_token/`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n // The flag tells the server this build reads a refusal, so it may answer\n // with the reason. A build that omits it gets a 404, which is its signal\n // to fall back to the legacy gateway.\n body: JSON.stringify({ program, reads_refusal_reason: true }),\n signal: AbortSignal.timeout(MINT_TIMEOUT_MS),\n });\n if (!resp.ok) {\n if (isMintRefusal(resp.status)) {\n const refusal = await readRefusal(resp);\n logToFile(\n `[gateway] mint refused with HTTP ${resp.status} (${\n refusal.outcome ?? 'no outcome'\n }); failing the run`,\n );\n // The terminal denial event for this run. The backend's own event has\n // no run id, so this is what joins a refusal to the session.\n analytics.wizardCapture('gateway mint refused', {\n status: resp.status,\n outcome: refusal.outcome,\n program,\n });\n throw new GatewayMintRefused(\n resp.status,\n mintRefusalMessage(resp.status, refusal.detail),\n refusal.outcome,\n );\n }\n logToFile(\n `[gateway] mint failed with HTTP ${resp.status}; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway could not issue a token (HTTP ${resp.status})`,\n );\n }\n const body = (await resp.json()) as {\n token?: string;\n expires_at?: string;\n gateway_url?: string;\n team_id?: number;\n };\n // Checked one at a time, not in a loop, so each clause narrows the optional\n // field for the return below and each names itself in the failure.\n if (!body.token) {\n logToFile('[gateway] mint response omitted token; failing the run');\n throw new GatewayMintFailed('mint response omitted token');\n }\n if (!body.expires_at) {\n logToFile('[gateway] mint response omitted expires_at; failing the run');\n throw new GatewayMintFailed('mint response omitted expires_at');\n }\n if (!body.gateway_url) {\n logToFile('[gateway] mint response omitted gateway_url; failing the run');\n throw new GatewayMintFailed('mint response omitted gateway_url');\n }\n if (!isTrustedGatewayUrl(body.gateway_url, host.apiHost)) {\n logToFile(\n '[gateway] mint returned an untrusted gateway url; failing the run',\n );\n throw new GatewayMintFailed('mint returned an untrusted gateway url');\n }\n const gatewayUrl = new URL(body.gateway_url);\n if (\n ['localhost', '127.0.0.1'].includes(new URL(host.apiHost).hostname) &&\n gatewayUrl.hostname === 'host.docker.internal'\n ) {\n gatewayUrl.hostname = 'localhost';\n }\n return {\n token: body.token,\n expiresAt: body.expires_at,\n gatewayUrl: gatewayUrl.origin,\n teamId: body.team_id,\n };\n } catch (e) {\n // Decisions and failures both pass through: this catch exists for transport\n // errors, and folding the others into it would lose the reason.\n if (e instanceof GatewayMintRefused || e instanceof GatewayMintFailed)\n throw e;\n logToFile(\n `[gateway] mint transport failure (${String(e)}); failing the run`,\n );\n throw new GatewayMintFailed(\n `could not reach the PostHog gateway (${String(e)})`,\n );\n }\n}\n\n/**\n * The v2 run-metadata carrier: one JSON blob for the `X-PostHog-Properties`\n * header. Plain keys only, since the gateway strips `$`-prefixed keys as reserved,\n * so feature-flag variants land as `wizard_flag_<key>` instead of the legacy\n * `$feature/<key>` (dashboards keying on `$feature/wizard-*` read the new key\n * post-cutover).\n */\nexport function buildWizardPropertiesBlob(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): string {\n // The gateway pins `$ai_product` to `wizard:<program>`, and rejects a legacy\n // product override on a scoped token, so the unprefixed key every cost and\n // error consumer reads is only present if this blob declares it.\n const props: Record<string, string | number> = { ai_product: 'wizard' };\n if (teamId !== undefined) props.team_id = teamId;\n for (const [key, value] of Object.entries(wizardMetadata)) {\n props[stripPropertyPrefix(key)] = value;\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n props[`wizard_flag_${flagKey.toLowerCase()}`] = variant;\n }\n return JSON.stringify(props);\n}\n\nconst LEGACY_PROPERTY_PREFIX = 'X-POSTHOG-PROPERTY-';\n\nfunction stripPropertyPrefix(key: string): string {\n return key.toUpperCase().startsWith(LEGACY_PROPERTY_PREFIX)\n ? key.slice(LEGACY_PROPERTY_PREFIX.length).toLowerCase()\n : key;\n}\n","/**\n * Agent signal vocabulary — the marker strings the agent emits and the error\n * taxonomy the runner returns. Kept as a dependency-free leaf module so both\n * `agent-interface.ts` and `output-signals.ts` can import it without a cycle.\n */\n\nexport const AgentSignals = {\n /** Signal emitted when the agent reports progress to the user */\n STATUS: '[STATUS]',\n /** Signal emitted when the agent cannot access the PostHog MCP server */\n ERROR_MCP_MISSING: '[ERROR-MCP-MISSING]',\n /** Signal emitted when the agent cannot access the setup resource */\n ERROR_RESOURCE_MISSING: '[ERROR-RESOURCE-MISSING]',\n /**\n * Signal emitted when `install_skill` failed and the agent is continuing\n * the integration WITHOUT the skill (best-effort from its own knowledge).\n * Format: \"[SKILL-INSTALL-FAILED] <skill id — reason>\". Non-fatal by\n * design: a freestyled integration beats an outright failure, but the run\n * must be measurable — the runner captures it as an analytics event.\n */\n SKILL_INSTALL_FAILED: '[SKILL-INSTALL-FAILED]',\n /**\n * Signal emitted when the agent cannot complete the program and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Programs can declare an onAbort handler to render a custom screen.\n */\n ABORT: '[ABORT]',\n /**\n * The AI gateway's refusal marker. A reply that starts with\n * \"[posthog:abort] <reason>\" is a refusal, and the run ends as an abort.\n * The marker elsewhere in a reply is ordinary text.\n */\n POLICY_ABORT: '[posthog:abort]',\n /** Signal emitted when the agent provides a remark about its run */\n WIZARD_REMARK: '[WIZARD-REMARK]',\n /** Signal prefix for benchmark logging */\n BENCHMARK: '[BENCHMARK]',\n /**\n * Signal emitted when the agent has created a PostHog dashboard for the\n * user. Format: `[DASHBOARD_URL] <full https url>`. The URL is captured\n * onto `session.dashboardUrl` and surfaced by programs in their outro.\n */\n DASHBOARD_URL: '[DASHBOARD_URL]',\n /**\n * Signal emitted when the agent has uploaded a report to a PostHog\n * notebook. Format: `[NOTEBOOK_URL] <full https url>`. The URL is captured\n * onto `session.notebookUrl` and surfaced by programs in their outro.\n */\n NOTEBOOK_URL: '[NOTEBOOK_URL]',\n} as const;\n\nexport type AgentSignal = (typeof AgentSignals)[keyof typeof AgentSignals];\n\n/**\n * The reason of a reply that starts with the policy abort marker, or undefined\n * for any other reply. The marker ends at a non-word character or the end of\n * the text, and the reason is the rest of that first line.\n */\nexport function policyAbortReason(text: string): string | undefined {\n const reply = text.trimStart();\n if (!reply.startsWith(AgentSignals.POLICY_ABORT)) return undefined;\n const rest = reply.slice(AgentSignals.POLICY_ABORT.length);\n if (/^\\w/.test(rest)) return undefined;\n return rest.split('\\n')[0].trim() || 'The AI gateway refused this request';\n}\n\n/**\n * End-of-run reflection ask, shared by both harnesses so remarks are\n * comparable. The format clause comes FIRST and nothing trails the marker —\n * literal models (gpt-5-mini) echo whatever text follows it. The parser also\n * drops remarks that quote this instruction (see AgentOutputSignals.remark).\n */\nexport const REMARK_INSTRUCTION = `Reply with a single line that starts with ${AgentSignals.WIZARD_REMARK} and no other lines. In that line, state briefly what information or guidance would have been useful to have in the integration prompt or documentation for this run — specifically anything that would have prevented tool failures, erroneous edits, or other wasted turns.`;\n\n/**\n * First prompt of a session resumed after a mid-run re-mint. The transcript\n * carries the work so far; the model only needs to pick it up.\n */\nexport const RESUME_INSTRUCTION =\n 'Your previous request failed with a transient gateway authentication error that has since been fixed. Continue the task from where you left off.';\n\n/**\n * Error types that can be returned from agent execution.\n * These correspond to the error signals that the agent emits.\n */\nexport enum AgentErrorType {\n /** The configured run deadline elapsed and the SDK query was aborted */\n AGENTIC_DETECTION_TIMEOUT = 'WIZARD_AGENTIC_DETECTION_TIMEOUT',\n /** Agent could not access the PostHog MCP server */\n MCP_MISSING = 'WIZARD_MCP_MISSING',\n /** Agent could not access the setup resource */\n RESOURCE_MISSING = 'WIZARD_RESOURCE_MISSING',\n /** API rate limit exceeded */\n RATE_LIMIT = 'WIZARD_RATE_LIMIT',\n /** Generic API error */\n API_ERROR = 'WIZARD_API_ERROR',\n /** YARA scanner detected a security violation */\n YARA_VIOLATION = 'WIZARD_YARA_VIOLATION',\n /** Agent intentionally aborted the program (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n /** Agent ended without making a single tool call — a no-op run that did no work */\n NO_PROGRESS = 'WIZARD_NO_PROGRESS',\n /** A schema-bound run ended without output matching its schema */\n INVALID_STRUCTURED_OUTPUT = 'WIZARD_INVALID_STRUCTURED_OUTPUT',\n /** Agent acted but stopped short — planned tasks left open and/or no skill installed */\n INCOMPLETE_TASKS = 'WIZARD_INCOMPLETE_TASKS',\n}\n","/**\n * PostHog AI gateway provider spec for pi sessions, shared by the linear run\n * and the orchestrator's per-task runs so both speak to the gateway\n * identically: bearer auth, the wizard properties header, transport shape\n * inferred from the model id. `createGatewayRuntime` registers the spec on a\n * pi ModelRuntime that holds nothing else.\n */\n\nimport {\n buildWizardPropertiesBlob,\n isPastRefresh,\n type GatewayAuth,\n} from '../../../gateway-session';\nimport {\n modelCapabilities,\n type ThinkingLevel,\n} from '../../switchboard/models';\nimport { AgentErrorType } from '../../../signals';\n\n/** Provider registered on the in-memory registry for this run. */\nexport const GATEWAY_PROVIDER = 'posthog-gateway';\n\n/** The pi transports the gateway serves. */\nexport type GatewayApi =\n | 'anthropic-messages'\n | 'openai-completions'\n | 'openai-responses';\n\n/**\n * The gateway speaks two shapes on two endpoints: Anthropic models over\n * `anthropic-messages` (the SDK appends `/v1/messages`, so the base URL has no\n * `/v1`), and OpenAI-class models (`openai/gpt-5`, …) over an OpenAI shape at\n * `/v1/responses` or `/v1/chat/completions` (base URL keeps `/v1`). Infer the\n * shape from the model id so a pair's model selects the right transport.\n *\n * OpenAI models take the Responses API because OpenAI rejects function tools\n * combined with `reasoning_effort` on chat completions and every task sends both.\n */\nexport function gatewayApiFor(modelId: string): GatewayApi {\n return modelId.startsWith('openai/')\n ? 'openai-responses'\n : 'anthropic-messages';\n}\n\n/**\n * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: one\n * `X-PostHog-Properties` JSON blob (Bedrock fallback is native, so no opt-in)\n * plus the 1M context beta, since pi otherwise runs at 200k and overflows on\n * larger projects (the post-run compaction failures).\n */\nexport function buildGatewayHeaders(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): Record<string, string> {\n return {\n 'anthropic-beta': 'context-1m-2025-08-07',\n 'X-PostHog-Properties': buildWizardPropertiesBlob(\n wizardMetadata,\n wizardFlags,\n teamId,\n ),\n };\n}\n\nexport interface GatewayProviderInputs {\n gatewayUrl: string;\n accessToken: string;\n /** Customer team for the properties blob (from the mint response). */\n teamId?: number;\n wizardMetadata: Record<string, string>;\n wizardFlags: Record<string, string>;\n modelId: string;\n // Resolved effort override — the switchboard's flag/payload pick on linear\n // runs, the prompt-frontmatter effort on per-task agents. Overrides the\n // table default for a reasoning model when set.\n effort?: ThinkingLevel;\n}\n\n/**\n * One gateway model spec — the single description of how to reach a model on\n * the gateway. The provider spec below wraps it for pi's registry; one-shot\n * callers (scan triage) hand it straight to `completeSimple`.\n */\nexport function buildGatewayModel(inputs: GatewayProviderInputs) {\n const { gatewayUrl, wizardMetadata, wizardFlags, modelId, teamId } = inputs;\n const api = gatewayApiFor(modelId);\n return {\n id: modelId,\n name: `${modelId} (PostHog Gateway)`,\n api,\n provider: GATEWAY_PROVIDER,\n // Anthropic drops /v1 (the SDK appends /v1/messages); the OpenAI shape keeps it.\n baseUrl: api === 'anthropic-messages' ? gatewayUrl : `${gatewayUrl}/v1`,\n // A model trait resolved by the switchboard, not a harness guess:\n // non-reasoning openai models reject `reasoning_effort` (gpt-4o → gateway\n // UnsupportedParamsError → the run no-ops).\n reasoning: modelCapabilities(modelId).reasoning,\n // The gateway refuses `developer` messages on pinned tokens, so pi sends `system`.\n compat: { supportsDeveloperRole: false },\n input: ['text' as const],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 1_000_000,\n maxTokens: 64_000,\n headers: buildGatewayHeaders(wizardMetadata, wizardFlags, teamId),\n };\n}\n\n/**\n * The provider object for `registry.registerProvider(GATEWAY_PROVIDER, …)`,\n * plus the derived traits the session setup needs (`caps.thinkingLevel`).\n */\nexport function buildGatewayProvider(inputs: GatewayProviderInputs): {\n provider: Record<string, unknown>;\n api: GatewayApi;\n caps: ReturnType<typeof modelCapabilities>;\n gatewayUrl: string;\n baseUrl: string;\n} {\n const { gatewayUrl, accessToken, modelId, effort } = inputs;\n const api = gatewayApiFor(modelId);\n // One resolution point for the model's traits and the run's effort override.\n // pi clamps whatever comes out of here against the levels this spec declares,\n // so a level the spec doesn't carry is silently reduced by the session.\n const tableCaps = modelCapabilities(modelId);\n const caps =\n effort && effort !== 'off' && tableCaps.reasoning\n ? modelCapabilities(modelId, effort)\n : effort === 'off' && tableCaps.reasoning\n ? { ...tableCaps, thinkingLevel: 'off' as const }\n : tableCaps;\n\n const model = buildGatewayModel(inputs);\n const provider = {\n name: 'PostHog Gateway',\n baseUrl: model.baseUrl,\n apiKey: accessToken,\n authHeader: true,\n api,\n headers: model.headers,\n models: [model],\n };\n return { provider, api, caps, gatewayUrl, baseUrl: model.baseUrl };\n}\n\n/** The part of a pi assistant turn that says why it failed. */\nexport interface GatewayTurnError {\n errorMessage?: string;\n diagnostics?: { error?: { name?: string; code?: string | number } }[];\n}\n\nexport type GatewayTerminalFailure = {\n classification:\n | AgentErrorType.API_ERROR\n | AgentErrorType.RATE_LIMIT\n | AgentErrorType.ABORT;\n message: string;\n status?: number;\n};\n\nexport function gatewayTerminalFailure(\n turn: ({ stopReason?: string } & GatewayTurnError) | undefined,\n): GatewayTerminalFailure | undefined {\n if (!turn || (turn.stopReason !== 'error' && turn.stopReason !== 'aborted'))\n return undefined;\n const message =\n turn.errorMessage ||\n (turn.stopReason === 'aborted'\n ? 'Agent turn aborted'\n : 'Gateway request failed');\n if (turn.stopReason === 'aborted') {\n return { classification: AgentErrorType.ABORT, message };\n }\n const codes =\n turn.diagnostics\n ?.map((d) => d.error?.code)\n .filter((code) => code !== undefined) ?? [];\n const structuredStatus = codes\n .map(Number)\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n const rateLimited =\n structuredStatus !== undefined\n ? structuredStatus === 429\n : /\\b429\\b|rate limit/i.test(message);\n return {\n classification: rateLimited\n ? AgentErrorType.RATE_LIMIT\n : AgentErrorType.API_ERROR,\n message,\n status:\n structuredStatus ?? (isGatewayAuthRejection(turn) ? 401 : undefined),\n };\n}\n\n/**\n * Whether a turn's error is the gateway rejecting the bearer. pi attaches the\n * SDK's own error to `diagnostics`, so its code decides when one is present.\n * The message match is the fallback for a turn that failed before pi built a\n * diagnostic, where the status survives only as prose.\n */\nexport function isGatewayAuthRejection(\n turn: GatewayTurnError | string | undefined,\n): boolean {\n const { errorMessage, diagnostics } =\n typeof turn === 'string'\n ? { errorMessage: turn, diagnostics: undefined }\n : turn ?? {};\n const structuredStatus = (diagnostics ?? [])\n .map((diagnostic) => Number(diagnostic.error?.code))\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n if (structuredStatus !== undefined) return structuredStatus === 401;\n for (const diagnostic of diagnostics ?? []) {\n const code = diagnostic.error?.code;\n if (code === 401 || code === '401') return true;\n if (/^authentication_?error$/i.test(diagnostic.error?.name ?? ''))\n return true;\n }\n return /\\b401\\b|authentication_error|unauthorized/i.test(errorMessage ?? '');\n}\n\n/** The model runtime a pi session runs on. */\nexport type GatewayRuntime =\n import('@earendil-works/pi-coding-agent').ModelRuntime;\n\n/**\n * A model runtime holding only the gateway provider. Credentials stay in memory,\n * and it reads no models.json and refreshes no catalog over the network, so a\n * run reads nothing of the user's own pi setup.\n */\nexport async function createGatewayRuntime(\n provider: Record<string, unknown>,\n): Promise<GatewayRuntime> {\n const [{ ModelRuntime }, { InMemoryCredentialStore }] = await Promise.all([\n import('@earendil-works/pi-coding-agent'),\n import('@earendil-works/pi-ai'),\n ]);\n const runtime = await ModelRuntime.create({\n credentials: new InMemoryCredentialStore(),\n modelsPath: null,\n allowModelNetwork: false,\n refreshOnCreate: false,\n });\n runtime.registerProvider(GATEWAY_PROVIDER, provider as never);\n return runtime;\n}\n\nexport interface GatewayRemintOptions {\n signal?: AbortSignal;\n session: { prompt(text: string): Promise<void> };\n registry: { registerProvider(providerName: string, config: never): void };\n auth: GatewayAuth;\n /** The cache: the same token while fresh, a new mint past the refresh point. */\n refreshAuth: () => Promise<GatewayAuth>;\n providerInputs: (auth: GatewayAuth) => GatewayProviderInputs;\n /** The prompt that resumes the work after a re-mint. */\n continueText: string | (() => string);\n onRemint?: () => void;\n}\n\n/**\n * Wraps a pi session's prompt(): when a turn ends on a 401 from a bearer past\n * its refresh instant, mint once, re-register the provider with the new\n * bearer (pi resolves the apiKey per request), and continue. A 401 on a fresh\n * bearer, or a second one, is left to the harness's normal failure path.\n */\nexport function withGatewayRemint(opts: GatewayRemintOptions): {\n prompt(text: string): Promise<void>;\n /** Feed every assistant `message_end`; the last turn decides. */\n noteAssistantTurn(message: unknown): void;\n terminalFailure(): GatewayTerminalFailure | undefined;\n} {\n let auth = opts.auth;\n let rejected = false;\n let reminted = false;\n let lastTurn: ({ stopReason?: string } & GatewayTurnError) | undefined;\n return {\n noteAssistantTurn(message) {\n lastTurn = message as\n | ({ stopReason?: string } & GatewayTurnError)\n | undefined;\n rejected =\n lastTurn?.stopReason === 'error' && isGatewayAuthRejection(lastTurn);\n },\n terminalFailure: () => gatewayTerminalFailure(lastTurn),\n async prompt(text) {\n if (opts.signal?.aborted) return;\n rejected = false;\n lastTurn = undefined;\n await opts.session.prompt(text);\n if (opts.signal?.aborted || !rejected || reminted || !isPastRefresh(auth))\n return;\n reminted = true;\n auth = await opts.refreshAuth();\n if (opts.signal?.aborted) return;\n opts.registry.registerProvider(\n GATEWAY_PROVIDER,\n buildGatewayProvider(opts.providerInputs(auth)).provider as never,\n );\n opts.onRemint?.();\n rejected = false;\n lastTurn = undefined;\n const next = opts.continueText;\n if (opts.signal?.aborted) return;\n await opts.session.prompt(typeof next === 'function' ? next() : next);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//#region src/headless/index.ts
|
|
2
2
|
/** Run `config` headlessly; resolves with the exit code. */
|
|
3
3
|
async function runHeadless(config, launch) {
|
|
4
|
-
const { runHeadless: run } = await import("./run-
|
|
4
|
+
const { runHeadless: run } = await import("./run-DUcJFhzP.js");
|
|
5
5
|
return run(config, launch);
|
|
6
6
|
}
|
|
7
7
|
//#endregion
|
|
8
8
|
export { runHeadless as t };
|
|
9
9
|
|
|
10
|
-
//# sourceMappingURL=headless-
|
|
10
|
+
//# sourceMappingURL=headless-BIQpNAHc.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-
|
|
1
|
+
{"version":3,"file":"headless-BIQpNAHc.js","names":[],"sources":["../src/headless/index.ts"],"sourcesContent":["/** Headless's one entry for other layers; `runHeadless` loads the host on first call. */\nimport type { ProgramConfig } from '@programs/types';\nimport type { HeadlessLaunch } from './run';\n\nexport type { HeadlessLaunch, NonInteractiveMode } from './run';\nexport { modeLabel } from './mode-label';\n\n/** Run `config` headlessly; resolves with the exit code. */\nexport async function runHeadless(\n config: ProgramConfig,\n launch: HeadlessLaunch,\n): Promise<number> {\n const { runHeadless: run } = await import('./run');\n return run(config, launch);\n}\n"],"mappings":";;AAQA,eAAsB,YACpB,QACA,QACiB;CACjB,MAAM,EAAE,aAAa,QAAQ,MAAM,OAAO;AAC1C,QAAO,IAAI,QAAQ,OAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./debug-
|
|
2
|
-
import { st as MCP_LOCAL_URL } from "./analytics-
|
|
3
|
-
import { n as getCloudUrl, o as getUiHostFromHost, r as getHost, s as resolveBaseUrl, t as detectRegion } from "./urls-
|
|
1
|
+
import "./debug-C_9xDg9j.js";
|
|
2
|
+
import { st as MCP_LOCAL_URL } from "./analytics-CX2kvq0R.js";
|
|
3
|
+
import { n as getCloudUrl, o as getUiHostFromHost, r as getHost, s as resolveBaseUrl, t as detectRegion } from "./urls-Bgngk5HI.js";
|
|
4
4
|
//#region src/shared/host-resolution.ts
|
|
5
5
|
/**
|
|
6
6
|
* HostResolution — the single, immutable snapshot of where the wizard talks to
|
|
@@ -111,4 +111,4 @@ var HostResolution = class HostResolution {
|
|
|
111
111
|
//#endregion
|
|
112
112
|
export { mcpUrlFor as n, HostResolution as t };
|
|
113
113
|
|
|
114
|
-
//# sourceMappingURL=host-resolution-
|
|
114
|
+
//# sourceMappingURL=host-resolution-C3ctTKNg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-resolution-
|
|
1
|
+
{"version":3,"file":"host-resolution-C3ctTKNg.js","names":[],"sources":["../src/shared/host-resolution.ts"],"sourcesContent":["/**\n * HostResolution — the single, immutable snapshot of where the wizard talks to\n * PostHog for one run.\n *\n * One cloud region (or a pinned `--base-url`) implies a whole family of hosts:\n * the event-ingestion/API host, the user-facing web app host, the CDN asset\n * host, the LLM gateway, and the MCP server. Rather than re-deriving each of\n * these at every call site and threading `region` + `baseUrl` around as loose\n * params, resolve once at auth time and pass this frozen object around — read\n * the property you need.\n *\n * The actual region→URL math (and the `--base-url` override) lives in\n * `@utils/urls`; this class is a thin, immutable façade over those resolvers so\n * the override flows through every field without callers having to know about\n * it. The OAuth-server URL stays in `@utils/urls` (`getOAuthUrl`) because it is\n * needed *before* a region is known, so it can't come from this post-auth object.\n */\n\nimport {\n getHost,\n getCloudUrl,\n getUiHostFromHost,\n detectRegion,\n resolveBaseUrl,\n} from '@utils/urls';\nimport { IS_PRODUCTION_BUILD, runtimeEnv } from '@env';\nimport { MCP_LOCAL_URL } from '@shared/local-dev';\nimport type { CloudRegion } from '@utils/types';\n\n// The wizard's client gets CLI mode (a single `exec` tool) by server default,\n// so no mode is pinned; the dev env override (see env.ts) is taken verbatim.\nconst MCP_PROD_URL = 'https://mcp.posthog.com/mcp';\n\n/** Construction-time inputs that aren't implied by the region. */\nexport interface HostResolutionOptions {\n /** `--local-mcp`: the MCP url only; skills follow `--local-context-mill`. */\n localMcp?: boolean;\n /** `--base-url`: pin every PostHog origin to one URL, bypassing region resolution. */\n baseUrl?: string;\n /** `--region`: trust the explicitly-provided region. */\n region?: CloudRegion;\n}\n\nfunction assetHostFor(region: CloudRegion, baseUrl?: string): string {\n const override = resolveBaseUrl(baseUrl);\n if (override) return override;\n return region === 'eu'\n ? 'https://eu-assets.i.posthog.com'\n : 'https://us-assets.i.posthog.com';\n}\n\nfunction assetHostFromApiHost(apiHost: string): string {\n if (apiHost.includes('us.i.posthog.com')) {\n return 'https://us-assets.i.posthog.com';\n }\n if (apiHost.includes('eu.i.posthog.com')) {\n return 'https://eu-assets.i.posthog.com';\n }\n return apiHost;\n}\n\n/**\n * Dev/CI only: the env override (see env.ts) wins even under --local-mcp;\n * published builds strip the read. Don't name that env var in comments here —\n * smoke-test.sh greps dist/*.js for it, and JSDoc survives bundling.\n */\nexport function mcpUrlFor(localMcp: boolean): string {\n const override = IS_PRODUCTION_BUILD ? undefined : runtimeEnv('MCP_URL');\n if (override) return override;\n return localMcp ? MCP_LOCAL_URL : MCP_PROD_URL;\n}\n\nexport class HostResolution {\n /** The resolved cloud region. `'us'` when a base URL is pinned. */\n readonly region: CloudRegion;\n /**\n * Event-ingestion / REST API host (e.g. `https://us.i.posthog.com`, or the\n * pinned `--base-url`). The SDK `host` written into the user's `.env`, the\n * base for the wizard-session REST calls, and the host shown to the agent.\n */\n readonly apiHost: string;\n /**\n * User-facing web app host (e.g. `https://us.posthog.com`). Use for any link\n * we hand to the user or open in their browser (dashboards, settings, inbox,\n * deep-link base).\n */\n readonly appHost: string;\n /** CDN asset host (e.g. `https://us-assets.i.posthog.com`). */\n readonly assetHost: string;\n /**\n * PostHog MCP server URL the agent connects to. Region-independent — the\n * server resolves the user's region from the bearer token — so this is driven\n * only by `--local-mcp` / `--local-dev` and the dev env override, not by\n * region/base-url.\n */\n readonly mcpUrl: string;\n\n private constructor(fields: {\n region: CloudRegion;\n apiHost: string;\n appHost: string;\n assetHost: string;\n mcpUrl: string;\n }) {\n this.region = fields.region;\n this.apiHost = fields.apiHost;\n this.appHost = fields.appHost;\n this.assetHost = fields.assetHost;\n this.mcpUrl = fields.mcpUrl;\n Object.freeze(this);\n }\n\n /**\n * Canonical path: build the host family from a resolved region. A pinned\n * `--base-url` (via `opts.baseUrl`) wins for every origin; otherwise the\n * region's standard hosts are used. Honors IS_DEV through `resolveBaseUrl`.\n */\n static fromRegion(\n region: CloudRegion,\n opts: HostResolutionOptions = {},\n ): HostResolution {\n const apiHost = getHost(region, opts.baseUrl);\n return new HostResolution({\n region,\n apiHost,\n appHost: getCloudUrl(region, opts.baseUrl),\n assetHost: assetHostFor(region, opts.baseUrl),\n mcpUrl: mcpUrlFor(opts.localMcp ?? false),\n });\n }\n\n /**\n * Build from an ingestion host string (the provisioning API returns one). The\n * given host is preserved verbatim as `apiHost`; the region and the derived\n * app/asset/gateway hosts are inferred from it.\n */\n static fromApiHost(\n apiHost: string,\n opts: Pick<HostResolutionOptions, 'localMcp'> = {},\n ): HostResolution {\n const region: CloudRegion = apiHost.includes('eu.') ? 'eu' : 'us';\n return new HostResolution({\n region,\n apiHost,\n appHost: getUiHostFromHost(apiHost),\n assetHost: assetHostFromApiHost(apiHost),\n mcpUrl: mcpUrlFor(opts.localMcp ?? false),\n });\n }\n\n /**\n * Resolve the region from an access token (the us/eu probe, skipped when a\n * base URL is pinned), then build the host family. Used after OAuth and after\n * CI-mode API-key auth.\n */\n static async fromAccessToken(\n accessToken: string,\n opts: HostResolutionOptions = {},\n ): Promise<HostResolution> {\n const region =\n opts.region ?? (await detectRegion(accessToken, opts.baseUrl));\n return HostResolution.fromRegion(region, opts);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,eAAe;AAYrB,SAAS,aAAa,QAAqB,SAA0B;CACnE,MAAM,WAAW,eAAe,QAAQ;AACxC,KAAI,SAAU,QAAO;AACrB,QAAO,WAAW,OACd,oCACA;;AAGN,SAAS,qBAAqB,SAAyB;AACrD,KAAI,QAAQ,SAAS,mBAAmB,CACtC,QAAO;AAET,KAAI,QAAQ,SAAS,mBAAmB,CACtC,QAAO;AAET,QAAO;;;;;;;AAQT,SAAgB,UAAU,UAA2B;AAGnD,QAAO,WAAW,gBAAgB;;AAGpC,IAAa,iBAAb,MAAa,eAAe;;CAE1B;;;;;;CAMA;;;;;;CAMA;;CAEA;;;;;;;CAOA;CAEA,YAAoB,QAMjB;AACD,OAAK,SAAS,OAAO;AACrB,OAAK,UAAU,OAAO;AACtB,OAAK,UAAU,OAAO;AACtB,OAAK,YAAY,OAAO;AACxB,OAAK,SAAS,OAAO;AACrB,SAAO,OAAO,KAAK;;;;;;;CAQrB,OAAO,WACL,QACA,OAA8B,EAAE,EAChB;AAEhB,SAAO,IAAI,eAAe;GACxB;GACA,SAHc,QAAQ,QAAQ,KAAK,QAAQ;GAI3C,SAAS,YAAY,QAAQ,KAAK,QAAQ;GAC1C,WAAW,aAAa,QAAQ,KAAK,QAAQ;GAC7C,QAAQ,UAAU,KAAK,YAAY,MAAM;GAC1C,CAAC;;;;;;;CAQJ,OAAO,YACL,SACA,OAAgD,EAAE,EAClC;AAEhB,SAAO,IAAI,eAAe;GACxB,QAF0B,QAAQ,SAAS,MAAM,GAAG,OAAO;GAG3D;GACA,SAAS,kBAAkB,QAAQ;GACnC,WAAW,qBAAqB,QAAQ;GACxC,QAAQ,UAAU,KAAK,YAAY,MAAM;GAC1C,CAAC;;;;;;;CAQJ,aAAa,gBACX,aACA,OAA8B,EAAE,EACP;EACzB,MAAM,SACJ,KAAK,UAAW,MAAM,aAAa,aAAa,KAAK,QAAQ;AAC/D,SAAO,eAAe,WAAW,QAAQ,KAAK"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as getInstalledClients, o as removeMCPServer, r as getSupportedClients, t as addMCPServer } from "./install-
|
|
1
|
+
import { n as getInstalledClients, o as removeMCPServer, r as getSupportedClients, t as addMCPServer } from "./install-Dayhuaun.js";
|
|
2
2
|
export { addMCPServer, getInstalledClients, getSupportedClients, removeMCPServer };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { o as logToFile, v as runtimeEnv } from "./debug-
|
|
2
|
-
import { n as analytics } from "./analytics-
|
|
3
|
-
import { t as openTrackedLink } from "./links-
|
|
4
|
-
import { a as scrubHomePaths, i as reportSpawnFailure$1, r as redactSecrets, s as toClientResult } from "./results-
|
|
1
|
+
import { o as logToFile, v as runtimeEnv } from "./debug-C_9xDg9j.js";
|
|
2
|
+
import { n as analytics } from "./analytics-CX2kvq0R.js";
|
|
3
|
+
import { t as openTrackedLink } from "./links-BOOGSL_G.js";
|
|
4
|
+
import { a as scrubHomePaths, i as reportSpawnFailure$1, r as redactSecrets, s as toClientResult } from "./results-8xNiBbaP.js";
|
|
5
5
|
import { a as getNativeHTTPServerConfig, i as getDefaultServerConfig, r as buildMCPUrl } from "./defaults-BWLIffh5.js";
|
|
6
6
|
import * as fs$2 from "fs";
|
|
7
7
|
import * as path$3 from "path";
|
|
@@ -1124,4 +1124,4 @@ const removeMCPServer = async (clients, local) => {
|
|
|
1124
1124
|
//#endregion
|
|
1125
1125
|
export { installPlugins as a, getSupportedPluginClients as i, getInstalledClients as n, removeMCPServer as o, getSupportedClients as r, isPluginCapable as s, addMCPServer as t };
|
|
1126
1126
|
|
|
1127
|
-
//# sourceMappingURL=install-
|
|
1127
|
+
//# sourceMappingURL=install-Dayhuaun.js.map
|