@posthog/wizard 2.77.0 → 2.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +30 -15
  2. package/dist/{add-mcp-server-to-clients-BnRyE4pw.js → add-mcp-server-to-clients-JMv8LP-O.js} +6 -5
  3. package/dist/{add-mcp-server-to-clients-BnRyE4pw.js.map → add-mcp-server-to-clients-JMv8LP-O.js.map} +1 -1
  4. package/dist/agent-bCZ3i3fh.js +3085 -0
  5. package/dist/agent-bCZ3i3fh.js.map +1 -0
  6. package/dist/{agent-interface-Avtr_5M5.js → agent-interface-BAOIHuXS.js} +429 -378
  7. package/dist/agent-interface-BAOIHuXS.js.map +1 -0
  8. package/dist/{agent-prompt-loader-CHixNPgY.js → agent-prompt-loader-7Pd4CfZO.js} +6 -6
  9. package/dist/agent-prompt-loader-7Pd4CfZO.js.map +1 -0
  10. package/dist/analytics-BcV-WCM4.js +2 -0
  11. package/dist/{analytics-C67F2vee.js → analytics-CHCZ_qUR.js} +10 -6
  12. package/dist/analytics-CHCZ_qUR.js.map +1 -0
  13. package/dist/{api-Dj1yabTj.js → api-Diy_2fp8.js} +4 -17
  14. package/dist/api-Diy_2fp8.js.map +1 -0
  15. package/dist/bin.js +410 -68
  16. package/dist/bin.js.map +1 -1
  17. package/dist/{bounded-fs-CWRYuLAn.js → bounded-fs-Bx9zTe6m.js} +4 -4
  18. package/dist/bounded-fs-Bx9zTe6m.js.map +1 -0
  19. package/dist/check-screens.tsx +1 -1
  20. package/dist/{ci-install-CvXL5h13.js → ci-install-CDdQYsPe.js} +6 -5
  21. package/dist/ci-install-CDdQYsPe.js.map +1 -0
  22. package/dist/constants-jFI2SaE1.js +260 -0
  23. package/dist/constants-jFI2SaE1.js.map +1 -0
  24. package/dist/debug-Ch4BDOtD.js +171 -0
  25. package/dist/debug-Ch4BDOtD.js.map +1 -0
  26. package/dist/{debug-CziFS0cA.js → debug-M2gV2iDq.js} +1 -1
  27. package/dist/{defaults-4c4YwTmW.js → defaults-C6J7yL4O.js} +1 -1
  28. package/dist/{defaults-4c4YwTmW.js.map → defaults-C6J7yL4O.js.map} +1 -1
  29. package/dist/endpoints-DCVbM06L.js +108 -0
  30. package/dist/endpoints-DCVbM06L.js.map +1 -0
  31. package/dist/{env-api-key-BZ5wE3Y4.js → env-api-key-Qx8NnYHm.js} +3 -3
  32. package/dist/env-api-key-Qx8NnYHm.js.map +1 -0
  33. package/dist/{environment-BEvsey6i.js → environment-B07Bmxae.js} +6 -6
  34. package/dist/environment-B07Bmxae.js.map +1 -0
  35. package/dist/{errors-BoJvrdw8.js → errors-CQ9kttJD.js} +27 -34
  36. package/dist/errors-CQ9kttJD.js.map +1 -0
  37. package/dist/{file-BoD3REGB.js → file-BcLTjU16.js} +2 -2
  38. package/dist/{file-BoD3REGB.js.map → file-BcLTjU16.js.map} +1 -1
  39. package/dist/{file-watcher-BPFazvG_.js → file-watcher-DWc_bTs6.js} +2 -2
  40. package/dist/{file-watcher-BPFazvG_.js.map → file-watcher-DWc_bTs6.js.map} +1 -1
  41. package/dist/{gateway-session-xPFv6p5c.js → gateway-CjoShdLa.js} +175 -20
  42. package/dist/gateway-CjoShdLa.js.map +1 -0
  43. package/dist/generate-version.cjs +1 -1
  44. package/dist/{headless-ui-CsmQrDIl.js → headless-ui-BFFmYC4v.js} +13 -2
  45. package/dist/headless-ui-BFFmYC4v.js.map +1 -0
  46. package/dist/{interactive-Dej-sxvR.js → interactive-D6zV7brJ.js} +2 -2
  47. package/dist/{interactive-Dej-sxvR.js.map → interactive-D6zV7brJ.js.map} +1 -1
  48. package/dist/{local-dev-DPZiIPCK.js → local-dev-McsurOC-.js} +2 -2
  49. package/dist/local-dev-McsurOC-.js.map +1 -0
  50. package/dist/logging-ui-_TwoMNbG.js +226 -0
  51. package/dist/logging-ui-_TwoMNbG.js.map +1 -0
  52. package/dist/mcp-DAgaoGk5.js +97 -0
  53. package/dist/mcp-DAgaoGk5.js.map +1 -0
  54. package/dist/{mcp-prompt-streaming-BU_kuur_.js → mcp-prompt-streaming-I2U_X7BC.js} +7 -6
  55. package/dist/mcp-prompt-streaming-I2U_X7BC.js.map +1 -0
  56. package/dist/{mint-failure-BrleGqMd.js → mint-failure-D4nd2XmZ.js} +82 -25
  57. package/dist/mint-failure-D4nd2XmZ.js.map +1 -0
  58. package/dist/models-DDdBJKwT.js +83 -0
  59. package/dist/models-DDdBJKwT.js.map +1 -0
  60. package/dist/{non-interactive-DcOjH2fw.js → non-interactive-Dzow7YKY.js} +3 -3
  61. package/dist/non-interactive-Dzow7YKY.js.map +1 -0
  62. package/dist/oauth-session-DJ8h6GFa.js +74 -0
  63. package/dist/oauth-session-DJ8h6GFa.js.map +1 -0
  64. package/dist/{orchestrator-tools-DXwr8Sp3.js → orchestrator-tools-szcdscwn.js} +4 -4
  65. package/dist/orchestrator-tools-szcdscwn.js.map +1 -0
  66. package/dist/{package-json-xt3eeyBI.js → package-json-DY70kkNp.js} +2 -2
  67. package/dist/package-json-DY70kkNp.js.map +1 -0
  68. package/dist/{pi-mY8I2XkG.js → pi-D3h5Ok0i.js} +329 -65
  69. package/dist/pi-D3h5Ok0i.js.map +1 -0
  70. package/dist/{playground-CDUUKLkC.js → playground-5AB4WgNF.js} +11 -7
  71. package/dist/playground-5AB4WgNF.js.map +1 -0
  72. package/dist/{posthog-Hc_-9wrr.js → posthog-DNn574nW.js} +8 -3
  73. package/dist/posthog-DNn574nW.js.map +1 -0
  74. package/dist/{provisioning-xfdjhFtA.js → provisioning-B8uSqRZk.js} +6 -5
  75. package/dist/provisioning-B8uSqRZk.js.map +1 -0
  76. package/dist/{queue-tools-C8y_xZJi.js → queue-tools-9OOVx5TE.js} +7 -86
  77. package/dist/queue-tools-9OOVx5TE.js.map +1 -0
  78. package/dist/{registry-DTmREJ-f.js → registry-8YG-kuXk.js} +265 -6
  79. package/dist/registry-8YG-kuXk.js.map +1 -0
  80. package/dist/run-agent-legacy-d7rWlzQR.js +3739 -0
  81. package/dist/run-agent-legacy-d7rWlzQR.js.map +1 -0
  82. package/dist/{security-Dw2oFJjP.js → security-Cg_4a7vB.js} +6 -6
  83. package/dist/security-Cg_4a7vB.js.map +1 -0
  84. package/dist/{setup-utils-fjJTBhaB.js → setup-utils-CmTCTPkX.js} +46 -18
  85. package/dist/setup-utils-CmTCTPkX.js.map +1 -0
  86. package/dist/smoke-test.sh +1 -1
  87. package/dist/{start-tui-UNe2xQ0U.js → start-tui-CB2PZs1x.js} +38 -44
  88. package/dist/start-tui-CB2PZs1x.js.map +1 -0
  89. package/dist/{steps-C2yqOk_h.js → steps-BZcXRJNY.js} +7 -7
  90. package/dist/{steps-C2yqOk_h.js.map → steps-BZcXRJNY.js.map} +1 -1
  91. package/dist/{store-Bic4Cr3L.js → store-UN7c-ASf.js} +18 -28
  92. package/dist/store-UN7c-ASf.js.map +1 -0
  93. package/dist/{subagent-Dz41vV9j.js → subagent-BofIDK-O.js} +8 -3
  94. package/dist/subagent-BofIDK-O.js.map +1 -0
  95. package/dist/{task-5-fw_Vw3.js → task-r9t1UhQP.js} +124 -36
  96. package/dist/task-r9t1UhQP.js.map +1 -0
  97. package/dist/{task-stream-dVzl6b_q.js → task-stream-CMUilu54.js} +45 -15
  98. package/dist/task-stream-CMUilu54.js.map +1 -0
  99. package/dist/{tasks-D1c2z1aw.js → tasks-Bbv_lOBG.js} +17 -13
  100. package/dist/tasks-Bbv_lOBG.js.map +1 -0
  101. package/dist/{telemetry-BRnxBRWK.js → telemetry-D6ruCtnv.js} +5 -5
  102. package/dist/telemetry-D6ruCtnv.js.map +1 -0
  103. package/dist/{terminal-9bvRa4Qt.js → terminal-DrGyci_a.js} +39 -74
  104. package/dist/terminal-DrGyci_a.js.map +1 -0
  105. package/dist/{tools-Dp6i9k70.js → tools-Dyfh9aCD.js} +11 -11
  106. package/dist/tools-Dyfh9aCD.js.map +1 -0
  107. package/dist/ui-BBVuTM1v.js +15 -0
  108. package/dist/ui-BBVuTM1v.js.map +1 -0
  109. package/dist/version-aXxpgRKx.js +6 -0
  110. package/dist/version-aXxpgRKx.js.map +1 -0
  111. package/dist/warlock-smoke-test.ts +2 -2
  112. package/dist/{wizard-abort-lE6umflt.js → wizard-abort-BBkiUxfS.js} +23 -20
  113. package/dist/wizard-abort-BBkiUxfS.js.map +1 -0
  114. package/dist/wizard-abort-BU4aLyvh.js +3 -0
  115. package/dist/{wizard-session-FSJCkTcP.js → wizard-session-2w4Ru-Wz.js} +12 -16
  116. package/dist/wizard-session-2w4Ru-Wz.js.map +1 -0
  117. package/dist/wizard-session-DGaUHOF3.js +2 -0
  118. package/dist/wizard-ui-DoyOZxK8.js +16 -0
  119. package/dist/wizard-ui-DoyOZxK8.js.map +1 -0
  120. package/dist/{yara-hooks-BTNJ6MGW.js → yara-hooks-DCeMDd1o.js} +19 -68
  121. package/dist/yara-hooks-DCeMDd1o.js.map +1 -0
  122. package/dist/yara-hooks-DSGuCU9A.js +2 -0
  123. package/package.json +1 -1
  124. package/dist/agent-interface-Avtr_5M5.js.map +0 -1
  125. package/dist/agent-prompt-loader-CHixNPgY.js.map +0 -1
  126. package/dist/agent-runner-COOIUJCP.js +0 -6188
  127. package/dist/agent-runner-COOIUJCP.js.map +0 -1
  128. package/dist/analytics-C67F2vee.js.map +0 -1
  129. package/dist/analytics-CJA1uLlk.js +0 -2
  130. package/dist/api-Dj1yabTj.js.map +0 -1
  131. package/dist/bounded-fs-CWRYuLAn.js.map +0 -1
  132. package/dist/ci-install-CvXL5h13.js.map +0 -1
  133. package/dist/debug-B4kzIZPi.js +0 -754
  134. package/dist/debug-B4kzIZPi.js.map +0 -1
  135. package/dist/env-api-key-BZ5wE3Y4.js.map +0 -1
  136. package/dist/environment-BEvsey6i.js.map +0 -1
  137. package/dist/errors-BoJvrdw8.js.map +0 -1
  138. package/dist/gateway-session-C118uACz.js +0 -2
  139. package/dist/gateway-session-xPFv6p5c.js.map +0 -1
  140. package/dist/headless-ui-CsmQrDIl.js.map +0 -1
  141. package/dist/local-dev-DPZiIPCK.js.map +0 -1
  142. package/dist/mcp-Cbuw_xss.js +0 -64
  143. package/dist/mcp-Cbuw_xss.js.map +0 -1
  144. package/dist/mcp-prompt-streaming-BU_kuur_.js.map +0 -1
  145. package/dist/mint-failure-BrleGqMd.js.map +0 -1
  146. package/dist/non-interactive-DcOjH2fw.js.map +0 -1
  147. package/dist/orchestrator-tools-DXwr8Sp3.js.map +0 -1
  148. package/dist/package-json-xt3eeyBI.js.map +0 -1
  149. package/dist/package-manager-DR23m5Jg.js +0 -290
  150. package/dist/package-manager-DR23m5Jg.js.map +0 -1
  151. package/dist/pi-mY8I2XkG.js.map +0 -1
  152. package/dist/playground-CDUUKLkC.js.map +0 -1
  153. package/dist/posthog-Hc_-9wrr.js.map +0 -1
  154. package/dist/provisioning-xfdjhFtA.js.map +0 -1
  155. package/dist/queue-tools-C8y_xZJi.js.map +0 -1
  156. package/dist/registry-DTmREJ-f.js.map +0 -1
  157. package/dist/security-Dw2oFJjP.js.map +0 -1
  158. package/dist/setup-utils-fjJTBhaB.js.map +0 -1
  159. package/dist/start-tui-UNe2xQ0U.js.map +0 -1
  160. package/dist/store-Bic4Cr3L.js.map +0 -1
  161. package/dist/subagent-Dz41vV9j.js.map +0 -1
  162. package/dist/task-5-fw_Vw3.js.map +0 -1
  163. package/dist/task-stream-dVzl6b_q.js.map +0 -1
  164. package/dist/tasks-D1c2z1aw.js.map +0 -1
  165. package/dist/telemetry-BRnxBRWK.js.map +0 -1
  166. package/dist/terminal-9bvRa4Qt.js.map +0 -1
  167. package/dist/tools-Dp6i9k70.js.map +0 -1
  168. package/dist/wizard-abort-CAcInAxg.js +0 -2
  169. package/dist/wizard-abort-lE6umflt.js.map +0 -1
  170. package/dist/wizard-session-CYvyYnti.js +0 -2
  171. package/dist/wizard-session-FSJCkTcP.js.map +0 -1
  172. package/dist/yara-hooks-BTNJ6MGW.js.map +0 -1
  173. package/dist/yara-hooks-CFY4pGcW.js +0 -2
@@ -0,0 +1,3085 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
+ import { a as AgentSignals, d as ErrorCodes, i as AGENT_ERROR_CODE, l as WizardError, r as skillErrorCode, t as classifyRunFailure } from "./errors-CQ9kttJD.js";
3
+ import { a as getLogFilePath, h as RUN_SURFACE, l as WIZARD_BENCHMARK_FILE, s as logToFile, t as configureLogFile, u as WIZARD_LOG_FILE, v as runtimeEnv } from "./debug-Ch4BDOtD.js";
4
+ import { t as VERSION } from "./version-aXxpgRKx.js";
5
+ import { B as WIZARD_CONTACT_EMAIL, G as WIZARD_ORCHESTRATOR_FLAG_KEY, J as WIZARD_PROVISIONING_SCOPES, K as WIZARD_ORCHESTRATOR_OVERRIDE_FLAG_KEY, L as SONNET_5_MODEL, R as Sequence, S as Harness, W as WIZARD_OAUTH_SCOPES, Z as WIZARD_SELF_DRIVING_USE_PI_HARNESS_FLAG_KEY, _ as GPT5_6_LUNA_MODEL, b as HAIKU_MODEL, l as DEFAULT_AGENT_MODEL, q as WIZARD_ORCHESTRATOR_SEEDED_TASKS_FLAG_KEY, v as GPT5_6_SOL_MODEL, y as GPT5_6_TERRA_MODEL } from "./constants-jFI2SaE1.js";
6
+ import "./wizard-session-2w4Ru-Wz.js";
7
+ import { a as ciExcludedTaskTypes, n as analytics } from "./analytics-CHCZ_qUR.js";
8
+ import { d as mcpUrlFor, p as detectNodePackageManagers } from "./setup-utils-CmTCTPkX.js";
9
+ import { n as currentAccessToken } from "./oauth-session-DJ8h6GFa.js";
10
+ import { c as configureGatewayFromCIEnvironment, l as gatewayAuth } from "./gateway-CjoShdLa.js";
11
+ import { a as requireKnownModel, r as isValidModel } from "./models-DDdBJKwT.js";
12
+ import { U as installSkillById, et as fetchSkillMenu, i as runAgent$1, lt as createTriageLLMProvider, nt as LONGER_ASK_TIMEOUT_MS, r as initializeAgent, rt as createWizardAskBridge } from "./agent-interface-BAOIHuXS.js";
13
+ import { a as piBackend, d as createEmitSpinner, f as createProgressCollector, l as createAioCapture, u as createEmitLog } from "./pi-D3h5Ok0i.js";
14
+ import { a as flushScanReport, s as formatYaraAbortMessage } from "./yara-hooks-DCeMDd1o.js";
15
+ import { _ as TaskStatus, d as uncoveredBySink, h as SkipReason, m as QueueStore, p as QUEUE_DIR_NAME, u as dependencyClosure } from "./queue-tools-9OOVx5TE.js";
16
+ import { a as assembleTaskPrompt, d as taskModelSpec, i as assembleSeedPrompt, n as agentRunTools, o as loadAgentRegistry, s as promptModelFor, t as ASK_TOOL, u as resolveTask } from "./agent-prompt-loader-7Pd4CfZO.js";
17
+ import fs, { cpSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs";
18
+ import * as path$1 from "path";
19
+ import path from "path";
20
+ import { z } from "zod";
21
+ import { randomUUID } from "crypto";
22
+ //#region src/agent/runner/shared/bootstrap.ts
23
+ /**
24
+ * Shared preparation for the runner pipeline.
25
+ *
26
+ * Runs before the fork into the linear or orchestrator arm: logging targets,
27
+ * the gateway mint and the scan-triage classifier built on it. Everything the
28
+ * caller must decide first — health gates, settings conflicts, authentication,
29
+ * the AI opt-in gate, post-auth gates, feature flags, run tags, token refresh —
30
+ * arrives already resolved in `RunConfig` and `RunInput`.
31
+ */
32
+ /**
33
+ * Decide whether the `wizard_ask` overlay should be wired for this run.
34
+ * Disabled in non-interactive modes (CI, signup) — there's no human to
35
+ * answer. Per-program disabling is done by adding WIZARD_ASK_TOOL_NAME to
36
+ * the program's `disallowedTools` so the SDK rejects calls outright.
37
+ * Extracted so the policy can be unit-tested directly.
38
+ *
39
+ * `e2eAsk` is the one escape hatch. The e2e harness runs a `ci`
40
+ * session, but it does have an answerer — the driver loop answers each
41
+ * `wizard_ask` batch from the program's e2e profile. Without the flag the
42
+ * agent-in-the-loop layer (the ask bridge in both sequence arms, and the
43
+ * orchestrator's seeded warehouse task) stays unreachable from a test.
44
+ *
45
+ * Only the e2e TUI host sets the flag, from the `E2E_ASK` env var. No CLI flag
46
+ * populates it, so plain `--ci` and `--signup` runs behave exactly as before.
47
+ */
48
+ function shouldDisableAsk(flags) {
49
+ return (flags.ci || flags.signup) && !flags.e2eAsk;
50
+ }
51
+ /** The option bag the agent interface and the middleware read. */
52
+ function runOptions(input) {
53
+ return {
54
+ installDir: input.installDir,
55
+ debug: input.flags.debug,
56
+ signup: input.flags.signup,
57
+ ci: input.flags.ci,
58
+ benchmark: input.flags.benchmark,
59
+ projectId: input.host.projectId,
60
+ apiKey: input.host.apiKey,
61
+ yaraReport: input.flags.yaraReport
62
+ };
63
+ }
64
+ /**
65
+ * Shared setup for both arms: logging targets, then the gateway mint and the
66
+ * triage classifier. Throws when the mint is refused, so the run fails before
67
+ * any agent starts — the caller maps that the way it maps any unexpected error.
68
+ */
69
+ async function prepareRun(config, input) {
70
+ const { skillsBaseUrl } = config;
71
+ logToFile(`[agent-runner] targets build=${VERSION} skills=${skillsBaseUrl} mcp=${mcpUrlFor(input.flags.localMcp)} posthog=${input.host.baseUrl ?? "region-resolved"}`);
72
+ const { credentials } = input;
73
+ const { wizardFlags, wizardFlagPayloads, wizardMetadata, programId } = config;
74
+ const currentGatewayAuth = () => currentAccessToken(credentials).then((token) => gatewayAuth(credentials.host, token, programId));
75
+ await currentGatewayAuth();
76
+ return {
77
+ skillsBaseUrl,
78
+ credentials,
79
+ programId,
80
+ wizardFlags,
81
+ wizardFlagPayloads,
82
+ wizardMetadata,
83
+ project: input.project,
84
+ triageProvider: createTriageLLMProvider(async () => {
85
+ const auth = await currentGatewayAuth();
86
+ return {
87
+ baseURL: auth.gatewayUrl,
88
+ authToken: auth.token,
89
+ teamId: auth.teamId,
90
+ wizardMetadata: {
91
+ ...wizardMetadata,
92
+ call_type: "yara-triage"
93
+ },
94
+ wizardFlags
95
+ };
96
+ }, config.binding.harness)
97
+ };
98
+ }
99
+ //#endregion
100
+ //#region src/agent/runner/harness/anthropic/index.ts
101
+ const anthropicBackend = {
102
+ name: "anthropic",
103
+ async run(inputs) {
104
+ const { config: runConfig, input, boot, emit, prompt, spinner, askBridge, middleware, model } = inputs;
105
+ const config = runConfig.run;
106
+ const { skillsBaseUrl, credentials, wizardFlags, wizardMetadata } = boot;
107
+ const { accessToken, host, projectApiKey } = credentials;
108
+ const log = createEmitLog(emit);
109
+ const capture = createAioCapture({
110
+ enabled: input.flags.captureAio,
111
+ projectApiKey,
112
+ apiHost: host.apiHost,
113
+ runTags: wizardMetadata
114
+ });
115
+ log.step("Initializing Claude agent...");
116
+ const agent = await initializeAgent({
117
+ workingDirectory: input.installDir,
118
+ posthogMcpUrl: host.mcpUrl,
119
+ posthogApiKey: accessToken,
120
+ currentPosthogApiKey: () => currentAccessToken(credentials),
121
+ host,
122
+ additionalMcpServers: config.additionalMcpServers,
123
+ detectPackageManager: config.detectPackageManager ?? detectNodePackageManagers,
124
+ skillsBaseUrl,
125
+ wizardFlags,
126
+ wizardMetadata,
127
+ programId: boot.programId,
128
+ integrationLabel: config.integrationLabel,
129
+ askBridge,
130
+ getPendingQuestion: askBridge?.getPendingQuestion,
131
+ askMaxQuestions: config.maxQuestions,
132
+ allowedTools: runConfig.allowedTools,
133
+ disallowedTools: runConfig.disallowedTools,
134
+ modelOverride: model,
135
+ capture,
136
+ emit
137
+ }, runOptions(input));
138
+ log.step(`Verbose logs: ${getLogFilePath()}`);
139
+ log.success("Agent initialized. Let's get cooking!");
140
+ logToFile("[agent-runner] agent initialized");
141
+ return runAgent$1({
142
+ ...agent,
143
+ signal: inputs.signal
144
+ }, prompt, runOptions(input), spinner, {
145
+ estimatedDurationMinutes: config.estimatedDurationMinutes,
146
+ spinnerMessage: config.spinnerMessage,
147
+ successMessage: config.successMessage,
148
+ errorMessage: config.errorMessage ?? `${config.integrationLabel} failed`,
149
+ abortCases: config.abortCases,
150
+ emitStepEvents: config.trackStepProgress ?? false,
151
+ resolveStepKey: config.resolveStepKey,
152
+ triageProvider: boot.triageProvider
153
+ }, middleware);
154
+ },
155
+ async runTask(inputs) {
156
+ const { config, input, boot, emit, prompt, spinner, model, allowedTools, disallowedTools, askBridge, orchestrator, spinnerMessage, successMessage, errorMessage, requestRemark, analyticsProperties } = inputs;
157
+ const options = runOptions(input);
158
+ const capture = createAioCapture({
159
+ enabled: input.flags.captureAio,
160
+ projectApiKey: boot.credentials.projectApiKey,
161
+ apiHost: boot.credentials.host.apiHost,
162
+ runTags: boot.wizardMetadata
163
+ });
164
+ return runAgent$1({
165
+ ...await initializeAgent({
166
+ workingDirectory: input.installDir,
167
+ posthogMcpUrl: boot.credentials.host.mcpUrl,
168
+ posthogApiKey: boot.credentials.accessToken,
169
+ currentPosthogApiKey: () => currentAccessToken(boot.credentials),
170
+ host: boot.credentials.host,
171
+ detectPackageManager: detectNodePackageManagers,
172
+ skillsBaseUrl: boot.skillsBaseUrl,
173
+ programId: boot.programId,
174
+ wizardFlags: boot.wizardFlags,
175
+ wizardMetadata: boot.wizardMetadata,
176
+ integrationLabel: config.programId,
177
+ askBridge,
178
+ getPendingQuestion: askBridge?.getPendingQuestion,
179
+ orchestrator,
180
+ capture,
181
+ emit
182
+ }, options),
183
+ model,
184
+ allowedTools,
185
+ disallowedTools,
186
+ signal: inputs.signal
187
+ }, prompt, options, spinner, {
188
+ spinnerMessage,
189
+ successMessage,
190
+ errorMessage,
191
+ requestRemark,
192
+ analyticsProperties
193
+ });
194
+ }
195
+ };
196
+ //#endregion
197
+ //#region src/agent/runner/switchboard/flags/orchestrator.ts
198
+ /** Orchestrator gate: wizard-orchestrator on/off. On routes the declared programs to the orchestrator on pi; every model/effort comes from prompt frontmatter. */
199
+ const ORCHESTRATOR_SEQUENCE_ROUTE = {
200
+ programs: ["posthog-integration"],
201
+ flag: WIZARD_ORCHESTRATOR_FLAG_KEY,
202
+ sequence: "orchestrator"
203
+ };
204
+ /** Same flag, harness axis: the orchestrator drives tasks through pi. */
205
+ const ORCHESTRATOR_HARNESS_ROUTE = {
206
+ program: "posthog-integration",
207
+ flags: {
208
+ useFlag: WIZARD_ORCHESTRATOR_FLAG_KEY,
209
+ harness: "pi"
210
+ }
211
+ };
212
+ /** Same programs, per-stage overrides: the variant payload (`{stage: {model?, effort?}}`) outranks prompt frontmatter for the stages it names. */
213
+ const ORCHESTRATOR_STAGE_OVERRIDES = {
214
+ programs: ORCHESTRATOR_SEQUENCE_ROUTE.programs,
215
+ flag: WIZARD_ORCHESTRATOR_OVERRIDE_FLAG_KEY
216
+ };
217
+ /**
218
+ * Kill switch for the runner-seeded task mechanism (the optional warehouse
219
+ * step). Off or unset, the orchestrator queues nothing itself and the run is
220
+ * identical to a project with no detected sources.
221
+ */
222
+ function areSeededTasksEnabled(flags = {}) {
223
+ return flags[WIZARD_ORCHESTRATOR_SEEDED_TASKS_FLAG_KEY] === "true";
224
+ }
225
+ /** Raw flag read — for telemetry/log lines only, never for routing. */
226
+ function isOrchestratorEnabled(flags = {}) {
227
+ return flags[ORCHESTRATOR_SEQUENCE_ROUTE.flag] === "true";
228
+ }
229
+ //#endregion
230
+ //#region src/agent/runner/switchboard/flags/self-driving.ts
231
+ /**
232
+ * Self-driving pi experiment.
233
+ *
234
+ * Flag: wizard-self-driving-use-pi-harness (bool; payload `{model, effort?,
235
+ * harness?, sequence?}`)
236
+ * Routes: ONLY `self-driving` — any axis the payload pins. A missing or
237
+ * invalid payload keeps the non-flagged binding default.
238
+ */
239
+ const SELF_DRIVING_EXPERIMENT = {
240
+ program: "self-driving",
241
+ flags: { useFlag: WIZARD_SELF_DRIVING_USE_PI_HARNESS_FLAG_KEY }
242
+ };
243
+ //#endregion
244
+ //#region src/agent/runner/switchboard/flags/schemes.ts
245
+ /**
246
+ * Flag config schemes — the shapes a PostHog flag set can take to route a
247
+ * program to the pi harness, plus the shared vocabulary and the per-config
248
+ * resolution. Experiment modules in this folder declare *which* flags they
249
+ * use; this module owns *how* a config resolves to a route.
250
+ */
251
+ /** Model variant key → gateway id. A key the gateway no longer mints is dropped
252
+ * rather than re-aimed: the payload then fails validation and the binding
253
+ * default stands, which is visible in the log instead of silently rerouting. */
254
+ const MODEL_FLAG_VARIANTS = {
255
+ "gpt-5-6-luna": GPT5_6_LUNA_MODEL,
256
+ "gpt-5-6-terra": GPT5_6_TERRA_MODEL,
257
+ "gpt-5-6-sol": GPT5_6_SOL_MODEL,
258
+ "sonnet-5": SONNET_5_MODEL
259
+ };
260
+ /** Valid effort variants; anything else leaves the model's table default. */
261
+ const EFFORT_FLAG_VARIANTS = [
262
+ "minimal",
263
+ "low",
264
+ "medium",
265
+ "high",
266
+ "xhigh"
267
+ ];
268
+ /** Variant key validated against the shared vocabulary, transformed to its gateway id. */
269
+ const modelVariantSchema = z.string().refine((key) => key in MODEL_FLAG_VARIANTS).transform((key) => MODEL_FLAG_VARIANTS[key]);
270
+ /** `{model, effort?, harness?, sequence?}` payload shape; extra keys tolerated for forward compat. */
271
+ const payloadConfigFlagSchema = z.object({
272
+ model: modelVariantSchema,
273
+ effort: z.enum(EFFORT_FLAG_VARIANTS).optional(),
274
+ harness: z.nativeEnum(Harness).optional(),
275
+ sequence: z.nativeEnum(Sequence).optional()
276
+ });
277
+ /**
278
+ * PostHog serves a variant's payload as a JSON string, e.g. the
279
+ * wizard-orchestrator-override `terra-review` variant arrives as
280
+ * `'{"review":{"model":"gpt-5-6-terra","effort":"medium"}}'`; local CI
281
+ * overrides inject the same payloads as already-parsed objects. Coerce both
282
+ * to the value; anything unparseable is undefined.
283
+ */
284
+ function coerceJson(raw) {
285
+ if (typeof raw !== "string") return raw;
286
+ try {
287
+ return JSON.parse(raw);
288
+ } catch {
289
+ return;
290
+ }
291
+ }
292
+ /** Per-stage overrides keyed by stage; a whole-payload parse failure fails closed to frontmatter everywhere. */
293
+ const stageOverridesSchema = z.record(z.object({
294
+ model: modelVariantSchema.optional(),
295
+ effort: z.enum(EFFORT_FLAG_VARIANTS).optional()
296
+ }));
297
+ /** Validate a stage-override payload (object or JSON string); undefined on any unexpected shape. */
298
+ function stageOverridesFromPayload(raw) {
299
+ const parsed = stageOverridesSchema.safeParse(coerceJson(raw));
300
+ return parsed.success ? parsed.data : void 0;
301
+ }
302
+ /** Validate a payload (object or JSON string) into a route; undefined on any unexpected shape. */
303
+ function parseFlagPayload(raw) {
304
+ const parsed = payloadConfigFlagSchema.safeParse(coerceJson(raw));
305
+ if (!parsed.success) return void 0;
306
+ return {
307
+ model: parsed.data.model,
308
+ thinkingLevel: parsed.data.effort,
309
+ harness: parsed.data.harness,
310
+ sequence: parsed.data.sequence
311
+ };
312
+ }
313
+ /**
314
+ * Resolve one config against the flag snapshot, or undefined when it doesn't
315
+ * validly route: use flag off, or a payload that fails validation — the
316
+ * caller then keeps the non-flagged binding default.
317
+ */
318
+ function routeFromConfigFlag(cfg, flags, flagPayloads) {
319
+ if (flags[cfg.useFlag] !== "true") return void 0;
320
+ if (cfg.harness) return { harness: cfg.harness };
321
+ const route = parseFlagPayload(flagPayloads?.[cfg.useFlag]);
322
+ if (!route) logToFile(`[switchboard] ${cfg.useFlag} on but payload missing/invalid — keeping the non-flagged default`);
323
+ return route;
324
+ }
325
+ //#endregion
326
+ //#region src/agent/runner/switchboard/flags/index.ts
327
+ /**
328
+ * Switchboard flags, one module per experiment. Every experiment declares its
329
+ * own scope — which program(s) its flags route — so the flag→program mapping
330
+ * is readable per file, never implied. `schemes.ts` owns the shared config
331
+ * shapes and resolution.
332
+ */
333
+ /** Every experiment on each axis. An experiment routes ONLY by being listed here, and only for the programs it declares. */
334
+ const HARNESS_EXPERIMENTS = [ORCHESTRATOR_HARNESS_ROUTE, SELF_DRIVING_EXPERIMENT];
335
+ const SEQUENCE_EXPERIMENTS = [ORCHESTRATOR_SEQUENCE_ROUTE];
336
+ /** The flag-driven route for a program, or undefined when no experiment covers it or its flags don't validly route. */
337
+ function resolveFlagRoute(program, flags, flagPayloads) {
338
+ const experiment = HARNESS_EXPERIMENTS.find((e) => e.program === program);
339
+ return experiment && routeFromConfigFlag(experiment.flags, flags, flagPayloads);
340
+ }
341
+ /** The flag-driven sequence for a program, or undefined when no sequence experiment covers it with its flag on. Surface/build scoping is the flag's own job (see `flagPersonProperties`). */
342
+ function resolveFlagSequence(program, flags) {
343
+ return SEQUENCE_EXPERIMENTS.find((e) => e.programs.includes(program) && flags[e.flag] === "true")?.sequence;
344
+ }
345
+ /** The per-stage overrides for a program's run, or undefined (prompt frontmatter stays). Applied once, where the agent prompts are loaded. */
346
+ function resolveStageOverrides(program, flags, flagPayloads) {
347
+ if (RUN_SURFACE === "cloud") return void 0;
348
+ const exp = ORCHESTRATOR_STAGE_OVERRIDES;
349
+ if (!exp.programs.includes(program)) return void 0;
350
+ const variant = flags[exp.flag];
351
+ if (!variant || variant === "false") return void 0;
352
+ const overrides = stageOverridesFromPayload(flagPayloads?.[exp.flag]);
353
+ if (!overrides) logToFile(`[switchboard] ${exp.flag}=${variant} but payload missing/invalid — keeping frontmatter`);
354
+ return overrides;
355
+ }
356
+ //#endregion
357
+ //#region src/agent/runner/switchboard/harness.ts
358
+ /**
359
+ * Harness axis: registry, middleware, resolver. Mirrors `sequence.ts`.
360
+ */
361
+ const HARNESS_OPTIONS = {
362
+ ["anthropic"]: anthropicBackend,
363
+ ["pi"]: piBackend
364
+ };
365
+ function getHarness(name) {
366
+ const harness = HARNESS_OPTIONS[name];
367
+ if (!harness) throw new Error(`No harness registered for '${name}'.`);
368
+ return harness;
369
+ }
370
+ /**
371
+ * PostHog-flag routing to pi (see `./flags`). No valid route — flag off, no
372
+ * config, or an invalid payload — keeps the non-flagged binding default.
373
+ */
374
+ const flagRunnerOverride = (ctx, next) => {
375
+ const pick = next();
376
+ const route = resolveFlagRoute(ctx.program, ctx.flags, ctx.flagPayloads);
377
+ if (!route) return pick;
378
+ if (ctx.trace) {
379
+ ctx.trace.harness = "flag";
380
+ if (route.model) ctx.trace.model = "flag";
381
+ }
382
+ return {
383
+ harness: route.harness ?? "pi",
384
+ model: route.model ?? pick.model,
385
+ thinkingLevel: route.thinkingLevel ?? pick.thinkingLevel
386
+ };
387
+ };
388
+ const HARNESS_MIDDLEWARE = [...[], flagRunnerOverride];
389
+ /**
390
+ * Resolve the harness for a role. Linear callers omit `role`; orchestrator
391
+ * callers pass `'seed'` or `task.type`. `contextMillOverride[role]` overlays.
392
+ */
393
+ function resolveHarness(ctx, role = "default") {
394
+ const pick = runChain(HARNESS_MIDDLEWARE, ctx, () => {
395
+ if (ctx.trace) Object.assign(ctx.trace, {
396
+ harness: "binding",
397
+ model: "binding"
398
+ });
399
+ const binding = PROGRAM_BINDINGS[ctx.program] ?? DEFAULT_BINDING;
400
+ return {
401
+ harness: binding.harness,
402
+ model: binding.model,
403
+ thinkingLevel: binding.thinkingLevel,
404
+ ...binding.contextMillOverride?.[role]
405
+ };
406
+ });
407
+ logToFile(`[switchboard] resolved: program=${ctx.program} harness=${pick.harness}${ctx.trace?.harness ? ` (${ctx.trace.harness})` : ""} model=${pick.model}${ctx.trace?.model ? ` (${ctx.trace.model})` : ""}`);
408
+ return pick;
409
+ }
410
+ //#endregion
411
+ //#region src/agent/middleware/phase-detector.ts
412
+ /** Phase transitions from [STATUS] in assistant text. Keep in sync with program "Status to report" bullets. */
413
+ const PHASES_ORDER = [
414
+ "1.0-begin",
415
+ "1.1-edit",
416
+ "1.2-revise",
417
+ "1.3-conclude"
418
+ ];
419
+ const STATUS_PHRASES_BY_PHASE = {
420
+ "1.0-begin": [
421
+ "Checking project structure",
422
+ "Verifying PostHog dependencies",
423
+ "Generating events based on project"
424
+ ],
425
+ "1.1-edit": ["Inserting PostHog capture code"],
426
+ "1.2-revise": [
427
+ "Finding and correcting errors",
428
+ "Report details of any errors you fix",
429
+ "Linting, building and prettying"
430
+ ],
431
+ "1.3-conclude": ["Configured dashboard", "Created setup report"]
432
+ };
433
+ var PhaseDetector = class {
434
+ currentPhase = "setup";
435
+ detect(message) {
436
+ if (message.type !== "assistant") return null;
437
+ const nextPhase = this.getNextPhase();
438
+ if (nextPhase === null) return null;
439
+ const content = message.message?.content;
440
+ if (!Array.isArray(content)) return null;
441
+ for (const block of content) {
442
+ if (block.type !== "text" || typeof block.text !== "string") continue;
443
+ if (!block.text.includes("[STATUS]")) continue;
444
+ const phrases = STATUS_PHRASES_BY_PHASE[nextPhase];
445
+ for (const phrase of phrases) if (block.text.includes(phrase)) {
446
+ this.currentPhase = nextPhase;
447
+ return nextPhase;
448
+ }
449
+ }
450
+ return null;
451
+ }
452
+ getNextPhase() {
453
+ if (this.currentPhase === "setup") return "1.0-begin";
454
+ const i = PHASES_ORDER.indexOf(this.currentPhase);
455
+ if (i < 0 || i >= PHASES_ORDER.length - 1) return null;
456
+ return PHASES_ORDER[i + 1];
457
+ }
458
+ reset() {
459
+ this.currentPhase = "setup";
460
+ }
461
+ };
462
+ //#endregion
463
+ //#region src/agent/middleware/pipeline.ts
464
+ /**
465
+ * Middleware pipeline orchestrator.
466
+ *
467
+ * Implements the same { onMessage, finalize } interface that runAgent() expects,
468
+ * while internally dispatching to an ordered list of middleware plugins.
469
+ */
470
+ var MiddlewarePipeline = class {
471
+ middlewares;
472
+ store = /* @__PURE__ */ new Map();
473
+ phaseDetector;
474
+ autoDetectPhases;
475
+ _currentPhase = "setup";
476
+ _currentPhaseFreshContext = true;
477
+ constructor(middlewares, opts) {
478
+ this.middlewares = middlewares;
479
+ this.phaseDetector = opts?.phaseDetector ?? new PhaseDetector();
480
+ this.autoDetectPhases = opts?.autoDetectPhases ?? true;
481
+ const ctx = this.createContext();
482
+ for (const mw of this.middlewares) mw.onInit?.(ctx);
483
+ }
484
+ /** Feed an SDK message through all middleware (satisfies tracker.onMessage) */
485
+ onMessage(message) {
486
+ if (this.autoDetectPhases) {
487
+ const newPhase = this.phaseDetector.detect(message);
488
+ if (newPhase && newPhase !== this._currentPhase) this.transitionPhase(newPhase, false);
489
+ }
490
+ const ctx = this.createContext();
491
+ const storeHandle = this.createStore();
492
+ for (const mw of this.middlewares) mw.onMessage?.(message, ctx, storeHandle);
493
+ }
494
+ /** Finalize the run (satisfies tracker.finalize) */
495
+ finalize(resultMessage, totalDurationMs) {
496
+ const ctx = this.createContext();
497
+ const storeHandle = this.createStore();
498
+ let result;
499
+ for (const mw of this.middlewares) {
500
+ const r = mw.onFinalize?.(resultMessage, totalDurationMs, ctx, storeHandle);
501
+ if (r !== void 0) result = r;
502
+ }
503
+ return result;
504
+ }
505
+ /** Explicit phase start (for phased runner support) */
506
+ startPhase(name, freshContext) {
507
+ this.transitionPhase(name, freshContext);
508
+ }
509
+ transitionPhase(newPhase, freshContext) {
510
+ const oldPhase = this._currentPhase;
511
+ this._currentPhase = newPhase;
512
+ this._currentPhaseFreshContext = freshContext;
513
+ const ctx = this.createContext();
514
+ const storeHandle = this.createStore();
515
+ for (const mw of this.middlewares) mw.onPhaseTransition?.(oldPhase, newPhase, ctx, storeHandle);
516
+ }
517
+ createContext() {
518
+ return {
519
+ currentPhase: this._currentPhase,
520
+ currentPhaseFreshContext: this._currentPhaseFreshContext,
521
+ get: (key) => this.store.get(key)
522
+ };
523
+ }
524
+ createStore() {
525
+ return { set: (key, value) => this.store.set(key, value) };
526
+ }
527
+ };
528
+ //#endregion
529
+ //#region src/agent/middleware/config.ts
530
+ /**
531
+ * Benchmark configuration loader.
532
+ *
533
+ * Loads .benchmark-config.json from the working directory with sensible defaults.
534
+ * All fields are optional — missing fields fall back to defaults.
535
+ */
536
+ const DEFAULT_CONFIG = {
537
+ plugins: {
538
+ tokens: true,
539
+ cache: true,
540
+ turns: true,
541
+ compactions: true,
542
+ contextSize: true,
543
+ cost: true,
544
+ duration: true,
545
+ summary: true,
546
+ jsonWriter: true
547
+ },
548
+ output: {
549
+ benchmarkPath: WIZARD_BENCHMARK_FILE,
550
+ benchmarkEnabled: true,
551
+ logPath: WIZARD_LOG_FILE,
552
+ logEnabled: true,
553
+ suppressWizardLogs: false
554
+ }
555
+ };
556
+ function loadBenchmarkConfig(installDir) {
557
+ const configPath = runtimeEnv("POSTHOG_WIZARD_BENCHMARK_CONFIG") ?? path.join(installDir, ".benchmark-config.json");
558
+ try {
559
+ const raw = fs.readFileSync(configPath, "utf-8");
560
+ const parsed = JSON.parse(raw);
561
+ const config = {
562
+ plugins: {
563
+ ...DEFAULT_CONFIG.plugins,
564
+ ...parsed.plugins
565
+ },
566
+ output: {
567
+ ...DEFAULT_CONFIG.output,
568
+ ...parsed.output
569
+ }
570
+ };
571
+ const benchFile = runtimeEnv("POSTHOG_WIZARD_BENCHMARK_FILE");
572
+ if (benchFile) config.output.benchmarkPath = benchFile;
573
+ const logDir = runtimeEnv("POSTHOG_WIZARD_LOG_DIR");
574
+ if (logDir) config.output.logPath = path.join(logDir, "posthog-wizard.log");
575
+ if (!config.output.benchmarkEnabled) config.plugins.jsonWriter = false;
576
+ logToFile(`${AgentSignals.BENCHMARK} Loaded config from ${configPath}`);
577
+ return config;
578
+ } catch {
579
+ const config = structuredClone(DEFAULT_CONFIG);
580
+ const benchFile2 = runtimeEnv("POSTHOG_WIZARD_BENCHMARK_FILE");
581
+ if (benchFile2) config.output.benchmarkPath = benchFile2;
582
+ const logDir2 = runtimeEnv("POSTHOG_WIZARD_LOG_DIR");
583
+ if (logDir2) config.output.logPath = path.join(logDir2, "posthog-wizard.log");
584
+ return config;
585
+ }
586
+ }
587
+ //#endregion
588
+ //#region src/agent/middleware/benchmarks/turn-counter.ts
589
+ var TurnCounterPlugin = class {
590
+ name = "turns";
591
+ lastMessageId = null;
592
+ phaseTurns = 0;
593
+ totalTurns = 0;
594
+ isDuplicate = false;
595
+ phaseSnapshots = [];
596
+ currentPhase = "setup";
597
+ onMessage(message, _ctx, store) {
598
+ if (message.type !== "assistant") {
599
+ this.isDuplicate = false;
600
+ store.set("turns", this.getData());
601
+ return;
602
+ }
603
+ const msgId = message.message?.id;
604
+ this.isDuplicate = msgId != null && msgId === this.lastMessageId;
605
+ if (msgId) this.lastMessageId = msgId;
606
+ if (!this.isDuplicate) {
607
+ this.phaseTurns++;
608
+ this.totalTurns++;
609
+ }
610
+ store.set("turns", this.getData());
611
+ }
612
+ onPhaseTransition(fromPhase, _toPhase, _ctx, store) {
613
+ this.phaseSnapshots.push({
614
+ phase: fromPhase,
615
+ turns: this.phaseTurns
616
+ });
617
+ this.currentPhase = _toPhase;
618
+ this.phaseTurns = 0;
619
+ this.lastMessageId = null;
620
+ store.set("turns", this.getData());
621
+ }
622
+ onFinalize(_resultMessage, _totalDurationMs, _ctx, store) {
623
+ this.phaseSnapshots.push({
624
+ phase: this.currentPhase,
625
+ turns: this.phaseTurns
626
+ });
627
+ store.set("turns", this.getData());
628
+ }
629
+ getData() {
630
+ return {
631
+ isDuplicate: this.isDuplicate,
632
+ phaseTurns: this.phaseTurns,
633
+ totalTurns: this.totalTurns,
634
+ phaseSnapshots: [...this.phaseSnapshots]
635
+ };
636
+ }
637
+ };
638
+ //#endregion
639
+ //#region src/agent/middleware/benchmarks/token-tracker.ts
640
+ var TokenTrackerPlugin = class {
641
+ name = "tokens";
642
+ phaseInput = 0;
643
+ phaseOutput = 0;
644
+ totalInput = 0;
645
+ totalOutput = 0;
646
+ lastUsage = null;
647
+ phaseSnapshots = [];
648
+ currentPhase = "setup";
649
+ phaseMessagesWithUsage = 0;
650
+ onMessage(message, ctx, store) {
651
+ if (message.type !== "assistant") return;
652
+ if (ctx.get("turns")?.isDuplicate) return;
653
+ const usage = message.message?.usage;
654
+ if (usage) {
655
+ const input = Number(usage.input_tokens ?? 0) + Number(usage.cache_read_input_tokens ?? 0) + Number(usage.cache_creation_input_tokens ?? 0);
656
+ const output = Number(usage.output_tokens ?? 0);
657
+ this.phaseInput += input;
658
+ this.phaseOutput += output;
659
+ this.totalInput += input;
660
+ this.totalOutput += output;
661
+ this.lastUsage = usage;
662
+ this.phaseMessagesWithUsage += 1;
663
+ }
664
+ store.set("tokens", this.getData());
665
+ }
666
+ onPhaseTransition(fromPhase, toPhase, _ctx, store) {
667
+ this.phaseSnapshots.push({
668
+ phase: fromPhase,
669
+ inputTokens: this.phaseInput,
670
+ outputTokens: this.phaseOutput,
671
+ messagesWithUsage: this.phaseMessagesWithUsage
672
+ });
673
+ this.currentPhase = toPhase;
674
+ this.phaseInput = 0;
675
+ this.phaseOutput = 0;
676
+ this.phaseMessagesWithUsage = 0;
677
+ store.set("tokens", this.getData());
678
+ }
679
+ onFinalize(_resultMessage, _totalDurationMs, _ctx, store) {
680
+ this.phaseSnapshots.push({
681
+ phase: this.currentPhase,
682
+ inputTokens: this.phaseInput,
683
+ outputTokens: this.phaseOutput,
684
+ messagesWithUsage: this.phaseMessagesWithUsage
685
+ });
686
+ store.set("tokens", this.getData());
687
+ }
688
+ getData() {
689
+ return {
690
+ phaseInput: this.phaseInput,
691
+ phaseOutput: this.phaseOutput,
692
+ totalInput: this.totalInput,
693
+ totalOutput: this.totalOutput,
694
+ lastUsage: this.lastUsage,
695
+ phaseSnapshots: [...this.phaseSnapshots]
696
+ };
697
+ }
698
+ };
699
+ //#endregion
700
+ //#region src/agent/middleware/benchmarks/cache-tracker.ts
701
+ var CacheTrackerPlugin = class {
702
+ name = "cache";
703
+ phaseRead = 0;
704
+ phaseCreation = 0;
705
+ phaseCreation5m = 0;
706
+ phaseCreation1h = 0;
707
+ totalRead = 0;
708
+ totalCreation = 0;
709
+ totalCreation5m = 0;
710
+ totalCreation1h = 0;
711
+ phaseSnapshots = [];
712
+ currentPhase = "setup";
713
+ onMessage(message, ctx, store) {
714
+ if (message.type !== "assistant") return;
715
+ if (ctx.get("turns")?.isDuplicate) return;
716
+ const usage = message.message?.usage;
717
+ if (usage) {
718
+ const read = Number(usage.cache_read_input_tokens ?? 0);
719
+ const creation = Number(usage.cache_creation_input_tokens ?? 0);
720
+ const cc = usage.cache_creation;
721
+ const creation5m = Number(cc?.ephemeral_5m_input_tokens ?? 0);
722
+ const creation1h = Number(cc?.ephemeral_1h_input_tokens ?? 0);
723
+ this.phaseRead += read;
724
+ this.phaseCreation += creation;
725
+ this.phaseCreation5m += creation5m;
726
+ this.phaseCreation1h += creation1h;
727
+ this.totalRead += read;
728
+ this.totalCreation += creation;
729
+ }
730
+ store.set("cache", this.getData());
731
+ }
732
+ onPhaseTransition(fromPhase, toPhase, _ctx, store) {
733
+ this.phaseSnapshots.push({
734
+ phase: fromPhase,
735
+ cacheReadTokens: this.phaseRead,
736
+ cacheCreationTokens: this.phaseCreation,
737
+ cacheCreation5m: this.phaseCreation5m,
738
+ cacheCreation1h: this.phaseCreation1h
739
+ });
740
+ this.currentPhase = toPhase;
741
+ this.phaseRead = 0;
742
+ this.phaseCreation = 0;
743
+ this.phaseCreation5m = 0;
744
+ this.phaseCreation1h = 0;
745
+ store.set("cache", this.getData());
746
+ }
747
+ onFinalize(_resultMessage, _totalDurationMs, _ctx, store) {
748
+ this.phaseSnapshots.push({
749
+ phase: this.currentPhase,
750
+ cacheReadTokens: this.phaseRead,
751
+ cacheCreationTokens: this.phaseCreation,
752
+ cacheCreation5m: this.phaseCreation5m,
753
+ cacheCreation1h: this.phaseCreation1h
754
+ });
755
+ store.set("cache", this.getData());
756
+ }
757
+ getData() {
758
+ return {
759
+ phaseRead: this.phaseRead,
760
+ phaseCreation: this.phaseCreation,
761
+ totalRead: this.totalRead,
762
+ totalCreation: this.totalCreation,
763
+ totalCreation5m: this.totalCreation5m,
764
+ totalCreation1h: this.totalCreation1h,
765
+ phaseSnapshots: [...this.phaseSnapshots]
766
+ };
767
+ }
768
+ };
769
+ //#endregion
770
+ //#region src/agent/middleware/benchmarks/compaction-tracker.ts
771
+ var CompactionTrackerPlugin = class {
772
+ name = "compactions";
773
+ phaseCompactions = 0;
774
+ phasePreTokens = [];
775
+ totalCompactions = 0;
776
+ phaseSnapshots = [];
777
+ currentPhase = "setup";
778
+ onMessage(message, ctx, store) {
779
+ if (message.type !== "system" || message.subtype !== "compact_boundary") return;
780
+ const preTokens = message.compact_metadata?.pre_tokens ?? 0;
781
+ const trigger = message.compact_metadata?.trigger ?? "unknown";
782
+ this.phaseCompactions++;
783
+ this.totalCompactions++;
784
+ this.phasePreTokens.push(preTokens);
785
+ logToFile(`${AgentSignals.BENCHMARK} [COMPACTION] Context compacted during "${ctx.currentPhase}" (trigger: ${trigger}, pre_tokens: ${preTokens})`);
786
+ store.set("compactions", this.getData());
787
+ }
788
+ onPhaseTransition(fromPhase, toPhase, _ctx, store) {
789
+ this.phaseSnapshots.push({
790
+ phase: fromPhase,
791
+ compactions: this.phaseCompactions,
792
+ preTokens: [...this.phasePreTokens]
793
+ });
794
+ this.currentPhase = toPhase;
795
+ this.phaseCompactions = 0;
796
+ this.phasePreTokens = [];
797
+ store.set("compactions", this.getData());
798
+ }
799
+ onFinalize(_resultMessage, _totalDurationMs, _ctx, store) {
800
+ this.phaseSnapshots.push({
801
+ phase: this.currentPhase,
802
+ compactions: this.phaseCompactions,
803
+ preTokens: [...this.phasePreTokens]
804
+ });
805
+ store.set("compactions", this.getData());
806
+ }
807
+ getData() {
808
+ return {
809
+ phaseCompactions: this.phaseCompactions,
810
+ phasePreTokens: [...this.phasePreTokens],
811
+ totalCompactions: this.totalCompactions,
812
+ phaseSnapshots: [...this.phaseSnapshots]
813
+ };
814
+ }
815
+ };
816
+ //#endregion
817
+ //#region src/agent/middleware/benchmarks/context-size-tracker.ts
818
+ var ContextSizeTrackerPlugin = class {
819
+ name = "contextSize";
820
+ phaseSnapshots = [];
821
+ lastContextTokensOut;
822
+ onPhaseTransition(fromPhase, _toPhase, ctx, store) {
823
+ const tokens = ctx.get("tokens");
824
+ const contextTokensOut = this.computeContextTokensOut(tokens?.lastUsage);
825
+ this.phaseSnapshots.push({
826
+ phase: fromPhase,
827
+ contextTokensIn: ctx.currentPhaseFreshContext ? void 0 : this.lastContextTokensOut,
828
+ contextTokensOut,
829
+ freshContext: ctx.currentPhaseFreshContext
830
+ });
831
+ this.lastContextTokensOut = contextTokensOut;
832
+ store.set("contextSize", this.getData());
833
+ }
834
+ onFinalize(_resultMessage, _totalDurationMs, ctx, store) {
835
+ const tokens = ctx.get("tokens");
836
+ const contextTokensOut = this.computeContextTokensOut(tokens?.lastUsage);
837
+ this.phaseSnapshots.push({
838
+ phase: ctx.currentPhase,
839
+ contextTokensIn: ctx.currentPhaseFreshContext ? void 0 : this.lastContextTokensOut,
840
+ contextTokensOut,
841
+ freshContext: ctx.currentPhaseFreshContext
842
+ });
843
+ store.set("contextSize", this.getData());
844
+ }
845
+ computeContextTokensOut(usage) {
846
+ if (!usage) return void 0;
847
+ return Number(usage.input_tokens ?? 0) + Number(usage.cache_read_input_tokens ?? 0) + Number(usage.cache_creation_input_tokens ?? 0);
848
+ }
849
+ getData() {
850
+ return { phaseSnapshots: [...this.phaseSnapshots] };
851
+ }
852
+ };
853
+ //#endregion
854
+ //#region src/shared/token-pricing.ts
855
+ /**
856
+ * Shared exact-match USD-per-token pricing by model id, and the cost formula
857
+ * applied to a token usage delta.
858
+ *
859
+ * Single source of truth for both the benchmark's `CostTrackerPlugin`
860
+ * (`@lib/middleware/benchmarks/cost-tracker`) and the live token/cost HUD
861
+ * (`agent-interface.ts`'s per-turn usage accumulation), so the two estimates
862
+ * never drift apart. Both reconcile against the SDK's own authoritative
863
+ * `total_cost_usd` once the run finishes — this table only prices the
864
+ * *live-updating* estimate shown while the agent is still running.
865
+ *
866
+ * Model-aware because a run's model isn't fixed: `AgentConfig.modelOverride`
867
+ * switches specific programs to Haiku (e.g. source-map detection), and the
868
+ * SDK reports a `model` string on every individual assistant turn (subagents
869
+ * dispatched via the Agent tool can run on a different model than the main
870
+ * session).
871
+ *
872
+ * Deliberately matched by *exact* model id, not family name (no
873
+ * `model.includes('opus')`-style fallback): different versions of the same
874
+ * family are priced differently — e.g. Claude Opus 4.5 ($5/$25 per Mtok) is
875
+ * half of Opus 4.1's rate ($15/$75) — so matching on "opus" or "sonnet"
876
+ * alone silently mis-prices a newer/older sibling at the wrong rate. The
877
+ * only normalization applied is stripping a trailing release-date suffix
878
+ * (`stripDateSuffix`), since Anthropic's dated and undated ids for the
879
+ * *same* version always carry the same price (verified for every entry
880
+ * below). Claude Sonnet 5 is the one exception with a *time-dependent*
881
+ * price — see `sonnet5Price` — rather than a fixed table entry.
882
+ *
883
+ * Prices verified against https://models.dev/api.json (Anthropic section)
884
+ * on 2026-07-01 — re-verify there when adding an entry or bumping
885
+ * DEFAULT_AGENT_MODEL/HAIKU_MODEL, since this table isn't fetched live.
886
+ */
887
+ /**
888
+ * `cacheCreation1h` isn't published by models.dev (it only has one
889
+ * cache-write rate); extrapolated at `input × 2`, the ratio that holds for
890
+ * every model below (mirrors `cacheCreation5m` = `input × 1.25`, `cacheRead`
891
+ * = `input × 0.1` — Anthropic's standard prompt-caching multipliers).
892
+ * Rounded to 6 decimal places to clean up binary float noise from the
893
+ * multiplication (e.g. `3 * 0.1` is `0.30000000000000004`, not `0.3`) — well
894
+ * past cent-level precision at the 1M-token scale these prices are quoted at.
895
+ */
896
+ function pricingFromInput(input, output) {
897
+ const round = (n) => Math.round(n * 1e6) / 1e6;
898
+ return {
899
+ input,
900
+ output,
901
+ cacheRead: round(input * .1),
902
+ cacheCreation5m: round(input * 1.25),
903
+ cacheCreation1h: round(input * 2)
904
+ };
905
+ }
906
+ /**
907
+ * Exact-match price table, keyed by the *undated* model id prefix —
908
+ * `pricePerMtokForModel` strips a dated suffix before falling back to this
909
+ * table, so a dated Anthropic id resolves via its undated entry without
910
+ * needing a duplicate key. Ids the wizard no longer dispatches on stay
911
+ * priced, since a subagent turn or the SDK can still report one (a turn on
912
+ * an id NOT in this table contributes $0 to the live estimate rather than
913
+ * guessing, see `pricePerMtokForModel`).
914
+ *
915
+ * `SONNET_5_MODEL` is intentionally absent: its price is time-dependent
916
+ * (promotional launch discount), so `pricePerMtokForModel` resolves it via
917
+ * `sonnet5Price`. It is also `DEFAULT_AGENT_MODEL`, so the no-model fallback
918
+ * goes through that branch.
919
+ */
920
+ const PRICE_TABLE = {
921
+ "claude-sonnet-4-6": pricingFromInput(3, 15),
922
+ [HAIKU_MODEL]: pricingFromInput(1, 5),
923
+ "claude-opus-4-5": pricingFromInput(5, 25)
924
+ };
925
+ /**
926
+ * Claude Sonnet 5 launched at a promotional discount through 2026-08-31
927
+ * UTC; from 2026-09-01 it reverts to the same rate as Sonnet 4.6 ($3/$15)
928
+ * — not a coincidence, Anthropic prices it identically to 4.6 once the
929
+ * promo ends. Re-verify against https://models.dev/api.json if this ever
930
+ * looks off, and fold this special case into a flat `PRICE_TABLE` entry at
931
+ * the post-promo rate once the window is well past.
932
+ */
933
+ const SONNET_5_PROMO_ENDS_UTC = /* @__PURE__ */ new Date("2026-09-01T00:00:00Z");
934
+ function sonnet5Price(now) {
935
+ return now < SONNET_5_PROMO_ENDS_UTC ? pricingFromInput(2, 10) : pricingFromInput(3, 15);
936
+ }
937
+ /** Strips a trailing 8-digit release-date suffix (e.g. `-20251001`) — never
938
+ * the version segment, so differently-versioned models are never conflated.
939
+ * `claude-haiku-4-5-20251001` → `claude-haiku-4-5`; `claude-sonnet-4-6` (no
940
+ * date suffix) is returned unchanged. */
941
+ function stripDateSuffix(model) {
942
+ return model.replace(/-\d{8}$/, "");
943
+ }
944
+ /**
945
+ * Exact-match pricing lookup. `undefined` model (a caller with no model
946
+ * context, e.g. the benchmark's `CostTrackerPlugin`) falls back to
947
+ * `DEFAULT_AGENT_MODEL`'s price, the default for interactive runs. A
948
+ * *given* but unrecognized model id returns `undefined` rather than
949
+ * guessing from its family name — seeing `undefined` and skipping that
950
+ * delta is strictly more accurate than the family-fallback this replaced,
951
+ * which silently priced e.g. every Opus turn at Opus 4.1's rate.
952
+ *
953
+ * `now` is injectable (defaults to the real clock) only so
954
+ * `sonnet5Price`'s promo-window check is deterministic in tests — no
955
+ * other entry in this table is time-dependent.
956
+ */
957
+ function pricePerMtokForModel(model, now = /* @__PURE__ */ new Date()) {
958
+ const id = model ?? "claude-sonnet-5";
959
+ const undated = stripDateSuffix(id);
960
+ if (undated === "claude-sonnet-5") return sonnet5Price(now);
961
+ return PRICE_TABLE[id] ?? PRICE_TABLE[undated];
962
+ }
963
+ /**
964
+ * Cost in USD for one usage delta, priced at `usage.model`'s exact rates
965
+ * (see `pricePerMtokForModel`) — 0 when `model` is given but not in the
966
+ * table, rather than guessing. `cacheCreation5m`/`cacheCreation1h` come from
967
+ * the SDK's `usage.cache_creation` breakdown, which is only reported on
968
+ * some turns — when both are 0, `cacheCreationTokens` (the plain
969
+ * `cache_creation_input_tokens` total) is priced at the 5m rate instead, so
970
+ * a turn without the breakdown still gets a reasonable estimate rather than
971
+ * being priced at $0.
972
+ *
973
+ * Takes the same shape as `TokenUsageDelta` (`@ui/wizard-ui`) so a caller
974
+ * that already has one — `WizardStore.addTokenUsage` — can pass it straight
975
+ * through instead of re-listing its fields in a fixed positional order.
976
+ */
977
+ function computeTokenCostUsd(usage) {
978
+ const price = pricePerMtokForModel(usage.model);
979
+ if (!price) return 0;
980
+ const hasBreakdown = usage.cacheCreation5m > 0 || usage.cacheCreation1h > 0;
981
+ return usage.inputTokens * (price.input / 1e6) + usage.outputTokens * (price.output / 1e6) + usage.cacheReadTokens * (price.cacheRead / 1e6) + (hasBreakdown ? usage.cacheCreation5m * (price.cacheCreation5m / 1e6) + usage.cacheCreation1h * (price.cacheCreation1h / 1e6) : usage.cacheCreationTokens * (price.cacheCreation5m / 1e6));
982
+ }
983
+ /**
984
+ * Shared display formatters for the hidden Ctrl+T HUD (`TokenCostHud`) and
985
+ * the post-exit scrollback line (`exit-line.ts`) — kept here, not in either
986
+ * of those files, since `exit-line.ts` is deliberately free of `ink`/React
987
+ * imports (it must stay a pure, unit-testable function), and importing a
988
+ * named export from a `.tsx` file would pull `ink` into its module graph
989
+ * anyway. This module has no such dependency.
990
+ */
991
+ function formatTokenCount(n) {
992
+ if (n >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
993
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
994
+ return n.toLocaleString();
995
+ }
996
+ function formatCostUsd(usd) {
997
+ if (usd > 0 && usd < .01) return `$${usd.toFixed(4)}`;
998
+ return `$${usd.toFixed(2)}`;
999
+ }
1000
+ //#endregion
1001
+ //#region src/agent/middleware/benchmarks/cost-tracker.ts
1002
+ var CostTrackerPlugin = class {
1003
+ name = "cost";
1004
+ phaseCosts = [];
1005
+ totalCost = 0;
1006
+ onPhaseTransition(fromPhase, _toPhase, ctx, store) {
1007
+ const tokens = ctx.get("tokens");
1008
+ const cache = ctx.get("cache");
1009
+ const tokenSnap = tokens?.phaseSnapshots.at(-1);
1010
+ const cacheSnap = cache?.phaseSnapshots.at(-1);
1011
+ const totalIn = tokenSnap?.inputTokens ?? 0;
1012
+ const read = cacheSnap?.cacheReadTokens ?? 0;
1013
+ const creation = cacheSnap?.cacheCreationTokens ?? 0;
1014
+ const c5m = cacheSnap?.cacheCreation5m ?? 0;
1015
+ const c1h = cacheSnap?.cacheCreation1h ?? 0;
1016
+ const phaseCost = computeTokenCostUsd({
1017
+ inputTokens: Math.max(0, totalIn - read - creation),
1018
+ outputTokens: tokenSnap?.outputTokens ?? 0,
1019
+ cacheReadTokens: read,
1020
+ cacheCreation5m: c5m,
1021
+ cacheCreation1h: c1h,
1022
+ cacheCreationTokens: creation
1023
+ });
1024
+ this.phaseCosts.push({
1025
+ phase: fromPhase,
1026
+ cost: phaseCost
1027
+ });
1028
+ this.totalCost += phaseCost;
1029
+ store.set("cost", this.getData());
1030
+ }
1031
+ onFinalize(resultMessage, _totalDurationMs, ctx, store) {
1032
+ const tokens = ctx.get("tokens");
1033
+ const cache = ctx.get("cache");
1034
+ const tokenSnap = tokens?.phaseSnapshots.at(-1);
1035
+ const cacheSnap = cache?.phaseSnapshots.at(-1);
1036
+ const totalIn = tokenSnap?.inputTokens ?? 0;
1037
+ const read = cacheSnap?.cacheReadTokens ?? 0;
1038
+ const creation = cacheSnap?.cacheCreationTokens ?? 0;
1039
+ const c5m = cacheSnap?.cacheCreation5m ?? 0;
1040
+ const c1h = cacheSnap?.cacheCreation1h ?? 0;
1041
+ const lastPhaseCost = computeTokenCostUsd({
1042
+ inputTokens: Math.max(0, totalIn - read - creation),
1043
+ outputTokens: tokenSnap?.outputTokens ?? 0,
1044
+ cacheReadTokens: read,
1045
+ cacheCreation5m: c5m,
1046
+ cacheCreation1h: c1h,
1047
+ cacheCreationTokens: creation
1048
+ });
1049
+ this.phaseCosts.push({
1050
+ phase: ctx.currentPhase,
1051
+ cost: lastPhaseCost
1052
+ });
1053
+ this.totalCost += lastPhaseCost;
1054
+ const sdkTotal = Number(resultMessage?.usage?.total_cost_usd ?? 0) || Number(resultMessage?.total_cost_usd ?? 0);
1055
+ if (sdkTotal > 0 && this.totalCost > 0) {
1056
+ const scale = sdkTotal / this.totalCost;
1057
+ this.phaseCosts = this.phaseCosts.map((p) => ({
1058
+ phase: p.phase,
1059
+ cost: p.cost * scale
1060
+ }));
1061
+ this.totalCost = sdkTotal;
1062
+ }
1063
+ store.set("cost", this.getData());
1064
+ }
1065
+ getData() {
1066
+ return {
1067
+ totalCost: this.totalCost,
1068
+ phaseCosts: [...this.phaseCosts]
1069
+ };
1070
+ }
1071
+ };
1072
+ //#endregion
1073
+ //#region src/agent/middleware/benchmarks/duration-tracker.ts
1074
+ var DurationTrackerPlugin = class {
1075
+ name = "duration";
1076
+ phaseStartTime = Date.now();
1077
+ phaseSnapshots = [];
1078
+ onPhaseTransition(fromPhase, _toPhase, _ctx, store) {
1079
+ const now = Date.now();
1080
+ this.phaseSnapshots.push({
1081
+ phase: fromPhase,
1082
+ startTime: this.phaseStartTime,
1083
+ endTime: now,
1084
+ durationMs: now - this.phaseStartTime
1085
+ });
1086
+ this.phaseStartTime = now;
1087
+ store.set("duration", {
1088
+ phaseSnapshots: [...this.phaseSnapshots],
1089
+ totalDurationMs: 0
1090
+ });
1091
+ }
1092
+ onFinalize(_resultMessage, totalDurationMs, ctx, store) {
1093
+ const now = Date.now();
1094
+ this.phaseSnapshots.push({
1095
+ phase: ctx.currentPhase,
1096
+ startTime: this.phaseStartTime,
1097
+ endTime: now,
1098
+ durationMs: now - this.phaseStartTime
1099
+ });
1100
+ store.set("duration", {
1101
+ phaseSnapshots: [...this.phaseSnapshots],
1102
+ totalDurationMs
1103
+ });
1104
+ }
1105
+ };
1106
+ //#endregion
1107
+ //#region src/agent/middleware/benchmarks/summary.ts
1108
+ function fmtDuration(ms) {
1109
+ const s = Math.round(ms / 1e3);
1110
+ const m = Math.floor(s / 60);
1111
+ return m > 0 ? `${m}m ${s % 60}s` : `${s}s`;
1112
+ }
1113
+ function fmtTok(n) {
1114
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
1115
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
1116
+ return n.toLocaleString();
1117
+ }
1118
+ function fmtCost(usd) {
1119
+ if (usd > 0 && usd < .01) return `$${usd.toFixed(4)}`;
1120
+ return `$${usd.toFixed(2)}`;
1121
+ }
1122
+ function printPhase(s) {
1123
+ const baseIn = Math.max(0, s.inputTokens - s.cacheRead - s.cacheCreation5m - s.cacheCreation1h);
1124
+ return [
1125
+ `${s.phase}: ${fmtDuration(s.durationMs)}, ${s.turns} turns, cost: ${fmtCost(s.cost)}`,
1126
+ ` in: ${fmtTok(baseIn)}, out: ${fmtTok(s.outputTokens)}, cache_read: ${fmtTok(s.cacheRead)}, cache_5m: ${fmtTok(s.cacheCreation5m)}, cache_1h: ${fmtTok(s.cacheCreation1h)}`,
1127
+ s.compactions > 0 ? ` ${s.compactions} compaction(s)` : null,
1128
+ s.contextOut !== void 0 ? ` ctx_out: ${fmtTok(s.contextOut)}` : null
1129
+ ].filter(Boolean).join("\n");
1130
+ }
1131
+ function getPhaseStats(i, ctx) {
1132
+ const dur = ctx.get("duration")?.phaseSnapshots[i];
1133
+ if (!dur) return null;
1134
+ const tokens = ctx.get("tokens");
1135
+ const turns = ctx.get("turns");
1136
+ const cost = ctx.get("cost");
1137
+ const compactions = ctx.get("compactions");
1138
+ const contextSize = ctx.get("contextSize");
1139
+ const cache = ctx.get("cache");
1140
+ return {
1141
+ phase: dur.phase,
1142
+ durationMs: dur.durationMs,
1143
+ turns: turns?.phaseSnapshots[i]?.turns ?? 0,
1144
+ inputTokens: tokens?.phaseSnapshots[i]?.inputTokens ?? 0,
1145
+ outputTokens: tokens?.phaseSnapshots[i]?.outputTokens ?? 0,
1146
+ cacheRead: cache?.phaseSnapshots[i]?.cacheReadTokens ?? 0,
1147
+ cacheCreation5m: cache?.phaseSnapshots[i]?.cacheCreation5m ?? 0,
1148
+ cacheCreation1h: cache?.phaseSnapshots[i]?.cacheCreation1h ?? 0,
1149
+ cost: cost?.phaseCosts[i]?.cost ?? 0,
1150
+ compactions: compactions?.phaseSnapshots[i]?.compactions ?? 0,
1151
+ contextOut: contextSize?.phaseSnapshots[i]?.contextTokensOut
1152
+ };
1153
+ }
1154
+ var SummaryPlugin = class {
1155
+ name = "summary";
1156
+ spinner;
1157
+ info;
1158
+ constructor(spinner, emit) {
1159
+ this.spinner = spinner;
1160
+ this.info = (message) => emit({
1161
+ kind: "log",
1162
+ level: "info",
1163
+ message
1164
+ });
1165
+ }
1166
+ onPhaseTransition(fromPhase, toPhase, ctx, _store) {
1167
+ const stats = getPhaseStats((ctx.get("duration")?.phaseSnapshots.length ?? 1) - 1, ctx);
1168
+ if (stats) this.spinner.stop(`${AgentSignals.BENCHMARK} ${printPhase(stats)}`);
1169
+ else this.spinner.stop(`${AgentSignals.BENCHMARK} ${fromPhase}`);
1170
+ this.info(`${AgentSignals.BENCHMARK} Starting phase: ${toPhase}`);
1171
+ this.spinner.start(`Integrating PostHog (${toPhase})...`);
1172
+ }
1173
+ onFinalize(_resultMessage, totalDurationMs, ctx, _store) {
1174
+ const duration = ctx.get("duration");
1175
+ const cost = ctx.get("cost");
1176
+ const tokens = ctx.get("tokens");
1177
+ const cache = ctx.get("cache");
1178
+ const phaseCount = duration?.phaseSnapshots.length ?? 0;
1179
+ const totalCost = cost?.totalCost ?? 0;
1180
+ this.info("");
1181
+ this.info(`◇ ${AgentSignals.BENCHMARK} ${phaseCount} phases in ${fmtDuration(totalDurationMs)}, cost: ${fmtCost(totalCost)}`);
1182
+ this.info(` total in: ${fmtTok(tokens?.totalInput ?? 0)}, out: ${fmtTok(tokens?.totalOutput ?? 0)}, cache_read: ${fmtTok(cache?.totalRead ?? 0)}, cache_5m: ${fmtTok(cache?.totalCreation5m ?? 0)}, cache_1h: ${fmtTok(cache?.totalCreation1h ?? 0)}`);
1183
+ this.info("");
1184
+ this.info(`● ${AgentSignals.BENCHMARK} Summary by phase:`);
1185
+ if (duration?.phaseSnapshots) for (let i = 0; i < duration.phaseSnapshots.length; i++) {
1186
+ const stats = getPhaseStats(i, ctx);
1187
+ if (stats) this.info(printPhase(stats));
1188
+ }
1189
+ this.info("");
1190
+ }
1191
+ };
1192
+ //#endregion
1193
+ //#region src/agent/middleware/benchmarks/json-writer.ts
1194
+ /**
1195
+ * JSON file output plugin.
1196
+ *
1197
+ * Assembles the BenchmarkData structure from all upstream plugin data
1198
+ * and writes it to a JSON file. Returns the BenchmarkData for backward compat.
1199
+ */
1200
+ /**
1201
+ * Sum token usage across all models from the SDK's modelUsage field.
1202
+ */
1203
+ function sumModelUsage(modelUsage) {
1204
+ let input_tokens = 0;
1205
+ let output_tokens = 0;
1206
+ let cache_creation_input_tokens = 0;
1207
+ let cache_read_input_tokens = 0;
1208
+ for (const model of Object.values(modelUsage)) {
1209
+ input_tokens += model.inputTokens ?? 0;
1210
+ output_tokens += model.outputTokens ?? 0;
1211
+ cache_creation_input_tokens += model.cacheCreationInputTokens ?? 0;
1212
+ cache_read_input_tokens += model.cacheReadInputTokens ?? 0;
1213
+ }
1214
+ return {
1215
+ input_tokens,
1216
+ output_tokens,
1217
+ cache_creation_input_tokens,
1218
+ cache_read_input_tokens
1219
+ };
1220
+ }
1221
+ var JsonWriterPlugin = class {
1222
+ name = "jsonWriter";
1223
+ outputPath;
1224
+ info;
1225
+ constructor(outputPath, emit) {
1226
+ this.outputPath = outputPath;
1227
+ this.info = (message) => emit({
1228
+ kind: "log",
1229
+ level: "info",
1230
+ message
1231
+ });
1232
+ }
1233
+ onFinalize(resultMessage, totalDurationMs, ctx, _store) {
1234
+ const tokens = ctx.get("tokens");
1235
+ const cache = ctx.get("cache");
1236
+ const turns = ctx.get("turns");
1237
+ const cost = ctx.get("cost");
1238
+ const duration = ctx.get("duration");
1239
+ const compactions = ctx.get("compactions");
1240
+ const contextSize = ctx.get("contextSize");
1241
+ const aggregateUsage = sumModelUsage(resultMessage?.modelUsage ?? {});
1242
+ const phaseCount = duration?.phaseSnapshots.length ?? 0;
1243
+ const steps = [];
1244
+ for (let i = 0; i < phaseCount; i++) {
1245
+ const dur = duration.phaseSnapshots[i];
1246
+ const tokenSnap = tokens?.phaseSnapshots[i];
1247
+ const cacheSnap = cache?.phaseSnapshots[i];
1248
+ const turnSnap = turns?.phaseSnapshots[i];
1249
+ const costSnap = cost?.phaseCosts[i];
1250
+ const compSnap = compactions?.phaseSnapshots[i];
1251
+ const ctxSnap = contextSize?.phaseSnapshots[i];
1252
+ const step = {
1253
+ name: dur.phase,
1254
+ usage: {
1255
+ input_tokens: tokenSnap?.inputTokens ?? 0,
1256
+ output_tokens: tokenSnap?.outputTokens ?? 0,
1257
+ cache_creation_input_tokens: cacheSnap?.cacheCreationTokens ?? 0,
1258
+ cache_read_input_tokens: cacheSnap?.cacheReadTokens ?? 0,
1259
+ ...(cacheSnap?.cacheCreation5m ?? 0) + (cacheSnap?.cacheCreation1h ?? 0) > 0 && { cache_creation: {
1260
+ ephemeral_5m_input_tokens: cacheSnap?.cacheCreation5m ?? 0,
1261
+ ephemeral_1h_input_tokens: cacheSnap?.cacheCreation1h ?? 0
1262
+ } }
1263
+ },
1264
+ modelUsage: {},
1265
+ totalCostUsd: costSnap?.cost ?? 0,
1266
+ durationMs: dur.durationMs,
1267
+ durationApiMs: 0,
1268
+ numTurns: turnSnap?.turns ?? 0,
1269
+ ...ctxSnap?.contextTokensIn !== void 0 && { contextTokensIn: ctxSnap.contextTokensIn },
1270
+ ...ctxSnap?.contextTokensOut !== void 0 && { contextTokensOut: ctxSnap.contextTokensOut },
1271
+ ...compSnap && compSnap.compactions > 0 ? {
1272
+ compactions: compSnap.compactions,
1273
+ compactionPreTokens: compSnap.preTokens
1274
+ } : {}
1275
+ };
1276
+ steps.push(step);
1277
+ }
1278
+ const totalTurns = turns?.totalTurns ?? 0;
1279
+ const totalCost = cost?.totalCost ?? 0;
1280
+ const totalCompactions = compactions?.totalCompactions ?? 0;
1281
+ const fromModelUsage = aggregateUsage.input_tokens + aggregateUsage.cache_read_input_tokens + aggregateUsage.cache_creation_input_tokens;
1282
+ const inputTokens = fromModelUsage > 0 ? fromModelUsage : (tokens?.totalInput ?? 0) + (cache?.totalRead ?? 0) + (cache?.totalCreation ?? 0);
1283
+ const outputTokens = aggregateUsage.output_tokens > 0 ? aggregateUsage.output_tokens : tokens?.totalOutput ?? 0;
1284
+ const benchmark = {
1285
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1286
+ steps,
1287
+ totals: {
1288
+ totalCostUsd: totalCost,
1289
+ durationMs: totalDurationMs,
1290
+ inputTokens,
1291
+ outputTokens,
1292
+ numTurns: resultMessage?.num_turns ?? totalTurns,
1293
+ totalCompactions,
1294
+ totalCacheReadTokens: cache?.totalRead ?? 0,
1295
+ totalCacheCreation5mTokens: cache?.totalCreation5m ?? 0,
1296
+ totalCacheCreation1hTokens: cache?.totalCreation1h ?? 0
1297
+ }
1298
+ };
1299
+ this.writeBenchmarkData(benchmark);
1300
+ return benchmark;
1301
+ }
1302
+ writeBenchmarkData(data) {
1303
+ try {
1304
+ fs.writeFileSync(this.outputPath, JSON.stringify(data, null, 2));
1305
+ logToFile(`Benchmark data written to ${this.outputPath}`);
1306
+ this.info(`● ${AgentSignals.BENCHMARK} Results written to ${this.outputPath}`);
1307
+ } catch (error) {
1308
+ logToFile("Failed to write benchmark data:", error);
1309
+ }
1310
+ }
1311
+ };
1312
+ //#endregion
1313
+ //#region src/agent/middleware/benchmarks/index.ts
1314
+ const PLUGIN_REGISTRY = {
1315
+ turns: () => new TurnCounterPlugin(),
1316
+ tokens: () => new TokenTrackerPlugin(),
1317
+ cache: () => new CacheTrackerPlugin(),
1318
+ compactions: () => new CompactionTrackerPlugin(),
1319
+ contextSize: () => new ContextSizeTrackerPlugin(),
1320
+ cost: () => new CostTrackerPlugin(),
1321
+ duration: () => new DurationTrackerPlugin(),
1322
+ summary: (opts) => new SummaryPlugin(opts.spinner, opts.emit),
1323
+ jsonWriter: (opts) => new JsonWriterPlugin(opts.outputPath, opts.emit)
1324
+ };
1325
+ /**
1326
+ * Execution order — data producers before consumers:
1327
+ * turns (dedup) -> tokens -> cache -> compactions -> contextSize -> cost -> duration -> summary -> jsonWriter
1328
+ */
1329
+ const PLUGIN_ORDER = [
1330
+ "turns",
1331
+ "tokens",
1332
+ "cache",
1333
+ "compactions",
1334
+ "contextSize",
1335
+ "cost",
1336
+ "duration",
1337
+ "summary",
1338
+ "jsonWriter"
1339
+ ];
1340
+ function createPluginsFromConfig(config, opts) {
1341
+ const resolvedOpts = {
1342
+ ...opts,
1343
+ outputPath: opts.outputPath ?? config.output.benchmarkPath
1344
+ };
1345
+ const effectivePlugins = { ...config.plugins };
1346
+ if (config.output.suppressWizardLogs) effectivePlugins.summary = false;
1347
+ return PLUGIN_ORDER.filter((name) => effectivePlugins[name] !== false).map((name) => PLUGIN_REGISTRY[name]).filter(Boolean).map((factory) => factory(resolvedOpts));
1348
+ }
1349
+ //#endregion
1350
+ //#region src/agent/middleware/benchmark.ts
1351
+ /**
1352
+ * Create a middleware pipeline configured for benchmarking.
1353
+ * Loads .benchmark-config.json from the install dir, falls back to defaults.
1354
+ */
1355
+ function createBenchmarkPipeline(emit, spinner, options, configOverride) {
1356
+ const config = configOverride ?? loadBenchmarkConfig(options.installDir);
1357
+ const info = (message) => emit({
1358
+ kind: "log",
1359
+ level: "info",
1360
+ message
1361
+ });
1362
+ configureLogFile({
1363
+ path: config.output.logPath,
1364
+ enabled: config.output.logEnabled
1365
+ });
1366
+ const plugins = createPluginsFromConfig(config, {
1367
+ emit,
1368
+ spinner,
1369
+ phased: false,
1370
+ outputPath: config.output.benchmarkPath
1371
+ });
1372
+ if (!config.output.suppressWizardLogs) {
1373
+ info(`${AgentSignals.BENCHMARK} Verbose logs: ${getLogFilePath()}`);
1374
+ info(`${AgentSignals.BENCHMARK} Benchmark data will be written to: ${config.output.benchmarkPath}`);
1375
+ }
1376
+ logToFile(`${AgentSignals.BENCHMARK} Tracking enabled, starting with setup phase`);
1377
+ return new MiddlewarePipeline(plugins, {
1378
+ phaseDetector: new PhaseDetector(),
1379
+ autoDetectPhases: true
1380
+ });
1381
+ }
1382
+ //#endregion
1383
+ //#region src/agent/agent-prompt.ts
1384
+ function defaultProjectPrompt(ctx) {
1385
+ return `You have access to the PostHog MCP server.
1386
+
1387
+ Project context:
1388
+ - PostHog Project ID: ${ctx.projectId}
1389
+ - PostHog public token: ${ctx.projectApiKey}
1390
+ - PostHog Host: ${ctx.host.apiHost}`;
1391
+ }
1392
+ function skillPrompt(skillPath, reportFile) {
1393
+ return `A PostHog skill has been installed at ${skillPath}/. Read ${skillPath}/SKILL.md and follow its instructions completely.
1394
+
1395
+ After completing the skill workflow, write a brief markdown report to ./${reportFile} summarizing:
1396
+ - What changes were made to the project
1397
+ - Which files were modified or created
1398
+ - Any manual steps the user should take next
1399
+
1400
+ Important: You must read a file immediately before attempting to write it, even if you have previously read it; failure to do so will cause a tool failure.`;
1401
+ }
1402
+ /**
1403
+ * Assemble the final agent prompt from the program's run config.
1404
+ */
1405
+ function assemblePrompt(runDef, ctx) {
1406
+ const parts = [];
1407
+ parts.push(defaultProjectPrompt(ctx));
1408
+ if (runDef.customPrompt) parts.push(runDef.customPrompt(ctx));
1409
+ if (ctx.skillPath) parts.push(skillPrompt(ctx.skillPath, runDef.reportFile));
1410
+ return parts.join("\n\n");
1411
+ }
1412
+ //#endregion
1413
+ //#region src/agent/runner/shared/errors.ts
1414
+ const failed = (failure) => ({
1415
+ outcome: "failed",
1416
+ failure
1417
+ });
1418
+ /** The failure a skill install error decides. The caller reports and exits. */
1419
+ function installFailure(integrationLabel, result) {
1420
+ const code = skillErrorCode(result) ?? ErrorCodes.InternalUnhandled;
1421
+ return {
1422
+ message: (() => {
1423
+ switch (result.kind) {
1424
+ case "menu-fetch-failed": return "Could not fetch the skill menu from context-mill.\nCheck your network connection and try again.";
1425
+ case "skill-not-found": return `Could not find the "${result.skillId}" skill in the context-mill menu.\nPlease try again later.`;
1426
+ case "download-failed": return `Failed to install skill: ${result.message}\nPlease try again.`;
1427
+ }
1428
+ })(),
1429
+ code,
1430
+ detail: {
1431
+ integration: integrationLabel,
1432
+ error_type: result.kind,
1433
+ platform: process.platform,
1434
+ ...result.kind === "download-failed" ? { error_detail: result.message.slice(0, 500) } : {}
1435
+ }
1436
+ };
1437
+ }
1438
+ //#endregion
1439
+ //#region src/agent/runner/shared/ask.ts
1440
+ /**
1441
+ * The ask bridge over an injected answerer.
1442
+ *
1443
+ * `createWizardAskBridge` already owns request ids, the timeout race, the
1444
+ * `__cancelled__` sentinel and the analytics; it only ever needed a
1445
+ * `showQuestion` that honours each question's own signal. Here that comes from
1446
+ * `AgentInteraction` instead of `getUI()`. With no answerer there is no bridge,
1447
+ * so `wizard_ask` reports its existing "not available" error rather than
1448
+ * hanging on a question nobody can see.
1449
+ */
1450
+ function createAskBridge(interaction, options) {
1451
+ const ask = interaction?.ask;
1452
+ if (!ask) return void 0;
1453
+ return createWizardAskBridge({
1454
+ getSource: options.getSource,
1455
+ showQuestion: (question, context) => {
1456
+ options.beforeShow?.();
1457
+ return ask(question, context);
1458
+ },
1459
+ richLinks: options.richLinks,
1460
+ timeoutMs: options.timeoutMs,
1461
+ signal: options.signal
1462
+ });
1463
+ }
1464
+ //#endregion
1465
+ //#region src/agent/runner/sequence/linear.ts
1466
+ async function runLinearProgram(context) {
1467
+ const controller = new AbortController();
1468
+ const abortFromHost = () => controller.abort();
1469
+ context.signal?.addEventListener("abort", abortFromHost, { once: true });
1470
+ if (context.signal?.aborted) abortFromHost();
1471
+ try {
1472
+ return await executeLinear(context, controller.signal);
1473
+ } finally {
1474
+ context.signal?.removeEventListener("abort", abortFromHost);
1475
+ controller.abort();
1476
+ }
1477
+ }
1478
+ /** The host's `signal` decides the outcome; `runSignal` also ends with the run. */
1479
+ async function executeLinear({ config, input, boot, emit, interaction, signal }, runSignal) {
1480
+ const { run, composed } = config;
1481
+ const { skillsBaseUrl, credentials, project } = boot;
1482
+ const { projectApiKey, host, projectId } = credentials;
1483
+ const aborted = () => ({
1484
+ outcome: "aborted",
1485
+ failure: {
1486
+ code: ErrorCodes.AgentAbort,
1487
+ message: "Agent run cancelled"
1488
+ }
1489
+ });
1490
+ if (signal?.aborted) return aborted();
1491
+ let skillPath;
1492
+ if (run.skillId) {
1493
+ logToFile(`[agent-runner] installing skill ${run.skillId}`);
1494
+ const installResult = await installSkillById(run.skillId, input.installDir, skillsBaseUrl, { triage: boot.triageProvider });
1495
+ if (signal?.aborted) return aborted();
1496
+ if (installResult.kind !== "ok") return failed(installFailure(run.integrationLabel, installResult));
1497
+ skillPath = installResult.path;
1498
+ logToFile(`[agent-runner] skill installed at ${skillPath}`);
1499
+ }
1500
+ const spinner = createEmitSpinner(emit);
1501
+ emit({
1502
+ kind: "lifecycle",
1503
+ phase: "started"
1504
+ });
1505
+ const ask = shouldDisableAsk(input.flags) && process.env.WIZARD_ASK_AUTODRIVE !== "1" ? void 0 : createAskBridge(interaction, {
1506
+ getSource: () => input.skillId ?? run.integrationLabel,
1507
+ richLinks: run.richLinks ?? false,
1508
+ timeoutMs: run.askTimeoutMs,
1509
+ signal: runSignal
1510
+ });
1511
+ const middleware = input.flags.benchmark ? createBenchmarkPipeline(emit, spinner, runOptions(input)) : void 0;
1512
+ const prompt = assemblePrompt(run, {
1513
+ projectId,
1514
+ projectApiKey,
1515
+ host,
1516
+ skillPath,
1517
+ orgAiDataProcessingApproved: input.apiUser?.organization?.is_ai_data_processing_approved ?? null,
1518
+ teamProductOptIns: project ? {
1519
+ sessionReplay: project.session_recording_opt_in ?? null,
1520
+ exceptionAutocapture: project.autocapture_exceptions_opt_in ?? null,
1521
+ surveys: project.surveys_opt_in ?? null
1522
+ } : null
1523
+ });
1524
+ logToFile(`[agent-runner] prompt assembled (${prompt.length} chars)`);
1525
+ if (signal?.aborted) return aborted();
1526
+ const { harness, model, thinkingLevel } = config.binding;
1527
+ const agentResult = await getHarness(harness).run({
1528
+ config,
1529
+ input,
1530
+ boot,
1531
+ emit,
1532
+ prompt,
1533
+ skillPath,
1534
+ spinner,
1535
+ askBridge: ask,
1536
+ middleware,
1537
+ model,
1538
+ thinkingLevel,
1539
+ signal: runSignal
1540
+ });
1541
+ if (signal?.aborted && agentResult.kind === "success") return aborted();
1542
+ if (agentResult.kind === "decided_failure") return failed(agentResult.failure);
1543
+ if (agentResult.kind === "success") {} else if (agentResult.kind !== "abort" && agentResult.kind !== "failure") return agentResult;
1544
+ if (agentResult.kind === "abort") {
1545
+ const reason = agentResult.message ?? "";
1546
+ const matched = run.abortCases?.find((c) => c.match.test(reason));
1547
+ const abortCode = matched?.errorCode ?? ErrorCodes.AgentAbort;
1548
+ const outroData = matched ? {
1549
+ kind: "error",
1550
+ message: matched.message,
1551
+ body: matched.body,
1552
+ docsUrl: matched.docsUrl,
1553
+ errorCode: abortCode,
1554
+ errorDetail: { reason }
1555
+ } : {
1556
+ kind: "error",
1557
+ message: `${run.integrationLabel} aborted`,
1558
+ body: reason || "The agent aborted the program.",
1559
+ docsUrl: run.docsUrl,
1560
+ errorCode: abortCode,
1561
+ errorDetail: { reason }
1562
+ };
1563
+ analytics.wizardCapture("agent aborted", {
1564
+ integration: run.integrationLabel,
1565
+ reason,
1566
+ matched: matched?.message ?? null
1567
+ });
1568
+ return {
1569
+ outcome: signal?.aborted ? "aborted" : "failed",
1570
+ failure: {
1571
+ message: matched?.message ?? `${run.integrationLabel} aborted`,
1572
+ outroData,
1573
+ code: abortCode,
1574
+ error: agentResult.error
1575
+ }
1576
+ };
1577
+ }
1578
+ const classification = agentResult.kind === "failure" ? agentResult.classification : void 0;
1579
+ const failureMessage = agentResult.kind === "failure" ? agentResult.message : void 0;
1580
+ if (classification === "WIZARD_MCP_MISSING") return failed({
1581
+ code: AGENT_ERROR_CODE["WIZARD_MCP_MISSING"],
1582
+ message: `Could not access the PostHog MCP server
1583
+
1584
+ The wizard was unable to connect to the PostHog MCP server.
1585
+ This could be due to a network issue or a configuration problem.
1586
+
1587
+ Please try again, or check the documentation:\n${run.docsUrl}`,
1588
+ error: agentResult.kind === "failure" ? agentResult.error : void 0
1589
+ });
1590
+ if (classification === "WIZARD_RESOURCE_MISSING") return failed({
1591
+ code: AGENT_ERROR_CODE["WIZARD_RESOURCE_MISSING"],
1592
+ message: `Could not access the setup resource
1593
+
1594
+ This may indicate a version mismatch or a temporary service issue.
1595
+
1596
+ Please try again, or check the documentation:\n${run.docsUrl}`,
1597
+ error: agentResult.kind === "failure" ? agentResult.error : void 0
1598
+ });
1599
+ if (classification === "WIZARD_YARA_VIOLATION") return failed({
1600
+ code: AGENT_ERROR_CODE["WIZARD_YARA_VIOLATION"],
1601
+ message: formatYaraAbortMessage(),
1602
+ error: agentResult.kind === "failure" ? agentResult.error : void 0
1603
+ });
1604
+ if (classification === "WIZARD_NO_PROGRESS") {
1605
+ analytics.wizardCapture("agent no progress", {
1606
+ integration: run.integrationLabel,
1607
+ error_type: "WIZARD_NO_PROGRESS"
1608
+ });
1609
+ return failed({
1610
+ code: AGENT_ERROR_CODE["WIZARD_NO_PROGRESS"],
1611
+ message: "The Wizard exited without changing your project. Please contact the PostHog team with wizard@posthog.com about this error.",
1612
+ error: agentResult.kind === "failure" ? agentResult.error : void 0
1613
+ });
1614
+ }
1615
+ if (classification === "WIZARD_INCOMPLETE_TASKS") {
1616
+ analytics.wizardCapture("agent incomplete tasks", {
1617
+ integration: run.integrationLabel,
1618
+ error_type: "WIZARD_INCOMPLETE_TASKS"
1619
+ });
1620
+ return failed({
1621
+ code: AGENT_ERROR_CODE["WIZARD_INCOMPLETE_TASKS"],
1622
+ message: "The Wizard exited without completing its planned tasks. Please contact the PostHog team with wizard@posthog.com about this error.",
1623
+ error: agentResult.kind === "failure" ? agentResult.error : void 0
1624
+ });
1625
+ }
1626
+ if (classification === "WIZARD_RATE_LIMIT" || classification === "WIZARD_API_ERROR") {
1627
+ analytics.wizardCapture("agent api error", {
1628
+ integration: run.integrationLabel,
1629
+ error_type: classification,
1630
+ error_message: failureMessage
1631
+ });
1632
+ return failed({
1633
+ code: AGENT_ERROR_CODE[classification],
1634
+ message: `API Error\n\n${failureMessage || "Unknown error"}\n\nPlease report this to: wizard@posthog.com`,
1635
+ error: agentResult.kind === "failure" ? agentResult.error : void 0
1636
+ });
1637
+ }
1638
+ if (agentResult.kind === "failure") return failed({
1639
+ code: AGENT_ERROR_CODE[agentResult.classification],
1640
+ message: agentResult.message ?? "Agent failed",
1641
+ error: agentResult.error
1642
+ });
1643
+ if (config.hooks?.postRun) {
1644
+ await config.hooks.postRun(credentials);
1645
+ if (signal?.aborted) return aborted();
1646
+ }
1647
+ if (composed) return { outcome: "success" };
1648
+ const outroData = config.hooks?.buildOutroData ? config.hooks.buildOutroData(credentials) : {
1649
+ kind: "success",
1650
+ message: run.successMessage,
1651
+ reportFile: run.reportFile,
1652
+ docsUrl: run.docsUrl,
1653
+ continueUrl: input.flags.signup ? `${host.appHost}/products?source=wizard` : void 0
1654
+ };
1655
+ if (outroData) emit({
1656
+ kind: "completion",
1657
+ outro: outroData
1658
+ });
1659
+ emit({
1660
+ kind: "lifecycle",
1661
+ phase: "completed",
1662
+ message: run.successMessage
1663
+ });
1664
+ return {
1665
+ outcome: "success",
1666
+ outro: outroData
1667
+ };
1668
+ }
1669
+ //#endregion
1670
+ //#region src/shared/utils/terminal-bell.ts
1671
+ /**
1672
+ * Ring the terminal bell (BEL, U+0007).
1673
+ *
1674
+ * The orchestrator takes consent for the warehouse step at the top of the run
1675
+ * and asks for the credentials at the end of it, several autonomous minutes
1676
+ * later. The person who agreed is often looking at something else by then. A
1677
+ * bell is the one attention signal a terminal in a background tab can still
1678
+ * deliver, and it costs a user who is watching nothing.
1679
+ *
1680
+ * Written to stderr, not stdout: Ink owns stdout and repaints whole frames, so
1681
+ * a byte written there can land inside one. Silent when stderr is not a TTY —
1682
+ * a pipe or a CI log cannot ring, and the byte would only pollute the capture.
1683
+ */
1684
+ function ringTerminalBell() {
1685
+ if (!process.stderr.isTTY) return;
1686
+ try {
1687
+ process.stderr.write("\x07");
1688
+ } catch {}
1689
+ }
1690
+ //#endregion
1691
+ //#region src/agent/runner/sequence/orchestrator/executor.ts
1692
+ /**
1693
+ * The executor drains the queue. It starts every runnable task (dependencies
1694
+ * satisfied) as soon as it becomes runnable — parallelism is decided by the
1695
+ * task graph, not by an executor knob. Each task runs through an injected
1696
+ * `runTask` function and reports its outcome via `complete_task`; a task that
1697
+ * ends without reporting is retried while attempts remain, then failed. A
1698
+ * `maxStarts` backstop guarantees termination.
1699
+ *
1700
+ * The drain loop is independent of how a task actually runs. `runTask` is
1701
+ * injected: the real one spins up a fresh agent, the tests use a fake.
1702
+ */
1703
+ /**
1704
+ * Thrown by a `RunTask` when its harness returned a decided failure that ends
1705
+ * the whole run (a 401 the auth screen already reported), not just the task.
1706
+ * `drainQueue` lets it through so the sequence can return the failure where
1707
+ * the harness used to exit the process.
1708
+ */
1709
+ var RunTaskFatal = class extends Error {
1710
+ constructor(failure, outcome = "failed", taskType) {
1711
+ super(failure.message ?? "agent run failed");
1712
+ this.failure = failure;
1713
+ this.outcome = outcome;
1714
+ this.taskType = taskType;
1715
+ this.name = "RunTaskFatal";
1716
+ }
1717
+ };
1718
+ const DEFAULT_DRAIN_OPTIONS = { maxStarts: 200 };
1719
+ async function runOne(store, runTask, task, signal) {
1720
+ if (signal?.aborted) return;
1721
+ store.start(task.id);
1722
+ try {
1723
+ await runTask(task);
1724
+ } catch (error) {
1725
+ if (signal?.aborted) return;
1726
+ if (error instanceof RunTaskFatal) throw error;
1727
+ logToFile(`[executor] runTask threw for ${task.type}:`, error);
1728
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), {
1729
+ step: "orchestrator_run_task",
1730
+ task_type: task.type
1731
+ });
1732
+ }
1733
+ if (signal?.aborted) return;
1734
+ const after = store.get(task.id);
1735
+ if (!after) return;
1736
+ if (after.status === TaskStatus.Running) {
1737
+ if (after.attempts < after.maxAttempts) store.requeue(task.id);
1738
+ else store.fail(task.id, {
1739
+ type: "no-report",
1740
+ message: "Task ended without calling complete_task."
1741
+ });
1742
+ return;
1743
+ }
1744
+ if (after.status === TaskStatus.Failed && after.attempts < after.maxAttempts) store.requeue(task.id);
1745
+ }
1746
+ /**
1747
+ * Drain the queue to a terminal state. Every runnable task starts the moment
1748
+ * its dependencies finish; independent branches run concurrently. Returns when
1749
+ * every task is done, failed, or blocked by a failed dependency, or when the
1750
+ * start backstop trips.
1751
+ */
1752
+ async function drainQueue(store, runTask, opts = DEFAULT_DRAIN_OPTIONS) {
1753
+ const running = /* @__PURE__ */ new Map();
1754
+ let starts = 0;
1755
+ let failure;
1756
+ try {
1757
+ for (;;) {
1758
+ if (opts.signal?.aborted) break;
1759
+ if (failure) throw failure.error;
1760
+ for (const task of store.nextRunnable()) {
1761
+ if (opts.signal?.aborted) break;
1762
+ if (++starts > opts.maxStarts) break;
1763
+ const p = runOne(store, runTask, task, opts.signal).catch((error) => {
1764
+ if (!failure) {
1765
+ failure = { error };
1766
+ try {
1767
+ opts.onFatal?.();
1768
+ } catch (abortError) {
1769
+ try {
1770
+ logToFile("[executor] fatal cancellation failed:", abortError);
1771
+ } catch {}
1772
+ }
1773
+ }
1774
+ }).finally(() => running.delete(task.id));
1775
+ running.set(task.id, p);
1776
+ }
1777
+ if (running.size === 0) break;
1778
+ await Promise.race(running.values());
1779
+ }
1780
+ } finally {
1781
+ await Promise.allSettled(running.values());
1782
+ }
1783
+ if (failure) throw failure.error;
1784
+ }
1785
+ //#endregion
1786
+ //#region src/agent/runner/sequence/orchestrator/run-metrics.ts
1787
+ var RunMetrics = class {
1788
+ firstStartMs;
1789
+ lastStartMs;
1790
+ firstCompleteMs;
1791
+ firstAskMs;
1792
+ lastVisibleMs;
1793
+ maxGapMs = 0;
1794
+ constructor(runStartMs) {
1795
+ this.runStartMs = runStartMs;
1796
+ }
1797
+ /** A task started. Returns the per-start-event timing for the start event. */
1798
+ recordStart(nowMs) {
1799
+ const timing = {
1800
+ ms_since_run_start: nowMs - this.runStartMs,
1801
+ gap_since_prev_start_ms: this.lastStartMs === void 0 ? void 0 : nowMs - this.lastStartMs
1802
+ };
1803
+ this.firstStartMs ??= nowMs;
1804
+ this.lastStartMs = nowMs;
1805
+ this.markVisible(nowMs);
1806
+ return timing;
1807
+ }
1808
+ /** A task completed. */
1809
+ recordComplete(nowMs) {
1810
+ this.firstCompleteMs ??= nowMs;
1811
+ this.markVisible(nowMs);
1812
+ }
1813
+ /** A task reached a terminal non-complete state the user sees (skip/fail). */
1814
+ recordTerminal(nowMs) {
1815
+ this.markVisible(nowMs);
1816
+ }
1817
+ /**
1818
+ * A question was put to the user. Only the first matters here, and it is not a
1819
+ * queue transition — waiting on a person is not the silence `max_gap_ms`
1820
+ * measures, so this deliberately does not mark visible progress.
1821
+ */
1822
+ recordAsk(nowMs) {
1823
+ this.firstAskMs ??= nowMs;
1824
+ }
1825
+ /**
1826
+ * The run-level responsiveness summary. Timings are `undefined` when the
1827
+ * relevant transition never happened (e.g. a run that started no task), so a
1828
+ * no-task run stays distinguishable from a genuine zero.
1829
+ */
1830
+ summary() {
1831
+ return {
1832
+ time_to_first_task_ms: this.firstStartMs === void 0 ? void 0 : this.firstStartMs - this.runStartMs,
1833
+ time_to_first_completion_ms: this.firstCompleteMs === void 0 ? void 0 : this.firstCompleteMs - this.runStartMs,
1834
+ max_gap_ms: this.lastVisibleMs === void 0 ? void 0 : this.maxGapMs,
1835
+ time_to_first_ask_ms: this.firstAskMs === void 0 ? void 0 : this.firstAskMs - this.runStartMs
1836
+ };
1837
+ }
1838
+ /** requeue is not user-visible, so a retry stall counts as silence here. */
1839
+ markVisible(nowMs) {
1840
+ if (this.lastVisibleMs !== void 0) this.maxGapMs = Math.max(this.maxGapMs, nowMs - this.lastVisibleMs);
1841
+ this.lastVisibleMs = nowMs;
1842
+ }
1843
+ };
1844
+ //#endregion
1845
+ //#region src/agent/runner/sequence/orchestrator/seeded-deps.ts
1846
+ /**
1847
+ * Resolve a runner-seeded task's dependencies against the planned queue.
1848
+ *
1849
+ * Two exclusions apply to both the declared and the default path:
1850
+ *
1851
+ * - **Sinks are never dependencies.** A sink runs last and must depend on the
1852
+ * whole queue, this task included. Depending on it back would invert the edge
1853
+ * and cost the report this task's `reportSection`. Leaving sinks out means a
1854
+ * planner that forgot the sink→seeded edge is still caught by the sink
1855
+ * invariant check rather than silently papered over here.
1856
+ * - **Anything already downstream of this task is never a dependency**, which is
1857
+ * what makes the result cycle-free by construction rather than by the queue's
1858
+ * per-edge refusal.
1859
+ */
1860
+ function seededDependencies(store, seededTaskId, declaredTypes, sinkTypes) {
1861
+ const eligible = store.list().filter((task) => {
1862
+ if (task.id === seededTaskId) return false;
1863
+ if (sinkTypes.includes(task.type)) return false;
1864
+ return !dependencyClosure(store, [task.id]).has(seededTaskId);
1865
+ });
1866
+ if (declaredTypes.length === 0) return {
1867
+ depIds: eligible.map((t) => t.id),
1868
+ unresolvedTypes: [],
1869
+ declared: false
1870
+ };
1871
+ const depIds = [];
1872
+ const unresolvedTypes = [];
1873
+ for (const type of declaredTypes) {
1874
+ if (!store.list().some((t) => t.type === type)) {
1875
+ unresolvedTypes.push(type);
1876
+ continue;
1877
+ }
1878
+ for (const task of eligible) if (task.type === type && !depIds.includes(task.id)) depIds.push(task.id);
1879
+ }
1880
+ return {
1881
+ depIds,
1882
+ unresolvedTypes,
1883
+ declared: true
1884
+ };
1885
+ }
1886
+ /**
1887
+ * Give every runner-seeded task the dependencies it could not name at enqueue,
1888
+ * and report what happened per task.
1889
+ *
1890
+ * The runner calls this once, after the planner has seeded the queue and before
1891
+ * the sink invariant is checked. Kept here rather than inline in the runner so
1892
+ * the wiring — which prompt's types feed which task — is testable without
1893
+ * standing up a whole run.
1894
+ */
1895
+ function deferSeededTasks(store, seededTasks, declaredTypesFor, sinkTypes) {
1896
+ return seededTasks.map((seeded) => {
1897
+ const declaredTypes = [...declaredTypesFor(seeded.type)];
1898
+ const resolved = seededDependencies(store, seeded.id, declaredTypes, sinkTypes);
1899
+ const { added, refused } = store.addDependencies(seeded.id, resolved.depIds);
1900
+ return {
1901
+ type: seeded.type,
1902
+ declaredTypes,
1903
+ declared: resolved.declared,
1904
+ added,
1905
+ refused,
1906
+ unresolvedTypes: resolved.unresolvedTypes
1907
+ };
1908
+ });
1909
+ }
1910
+ //#endregion
1911
+ //#region src/agent/runner/sequence/orchestrator/orchestrator-runner.ts
1912
+ /**
1913
+ * Experimental task-queue orchestrator runner.
1914
+ *
1915
+ * Branches from the linear runner when the `wizard-orchestrator` flag is on. An
1916
+ * orchestrator agent inspects the repo and seeds an in-memory task queue; an
1917
+ * executor drains it, running one fresh agent per task.
1918
+ *
1919
+ * Both the WHAT (agent prompts: model, goal, success criteria, tools) and the
1920
+ * HOW (mini-skills) are markdown served from context-mill — the seed and every
1921
+ * task resolve to a prompt fetched at startup into the registry. The wizard side
1922
+ * stays product-ignorant: it is the queue, the executor, and the loader.
1923
+ */
1924
+ /** Docs page (`django.md`, `nuxt-js-3-6.md`) — steps start with a digit, agent artifacts (`SKILL.md`, `EXAMPLE*`, `COMMANDMENTS.md`) have uppercase. */
1925
+ const isDocPage = (name) => name.endsWith(".md") && name === name.toLowerCase() && !/^\d/.test(name);
1926
+ /** Copy only the framework docs pages out of the run cache into .claude/skills — the one durable artifact an orchestrator run leaves. Never clobbers an existing install. */
1927
+ function promoteReferenceSkill(referenceDir, claudeSkillsDir, referenceSkillId) {
1928
+ const target = path$1.join(claudeSkillsDir, referenceSkillId);
1929
+ const refs = path$1.join(referenceDir, "references");
1930
+ if (!existsSync(refs) || existsSync(target)) return;
1931
+ const docs = readdirSync(refs).filter(isDocPage);
1932
+ if (docs.length === 0) return;
1933
+ mkdirSync(path$1.join(target, "references"), { recursive: true });
1934
+ for (const f of docs) cpSync(path$1.join(refs, f), path$1.join(target, "references", f));
1935
+ writeFileSync(path$1.join(target, ".posthog-wizard"), "");
1936
+ }
1937
+ /**
1938
+ * Remove skills that task agents installed durably mid-run (load_skill):
1939
+ * wizard-marked, new this run, and not the framework reference docs.
1940
+ * User-authored and pre-existing skills stay.
1941
+ */
1942
+ function sweepRunInstalledSkills(claudeSkillsDir, preexistingSkills, referenceSkillId) {
1943
+ if (!existsSync(claudeSkillsDir)) return;
1944
+ for (const id of readdirSync(claudeSkillsDir)) {
1945
+ if (preexistingSkills.has(id) || id === referenceSkillId) continue;
1946
+ if (!existsSync(path$1.join(claudeSkillsDir, id, ".posthog-wizard"))) continue;
1947
+ rmSync(path$1.join(claudeSkillsDir, id), {
1948
+ recursive: true,
1949
+ force: true
1950
+ });
1951
+ logToFile(`[orchestrator] removed run-installed skill ${id}`);
1952
+ }
1953
+ }
1954
+ function toTodoStatus(status) {
1955
+ switch (status) {
1956
+ case TaskStatus.Running: return "in_progress";
1957
+ case TaskStatus.Done: return "completed";
1958
+ case TaskStatus.Failed: return "failed";
1959
+ case TaskStatus.Skipped: return "skipped";
1960
+ default: return "pending";
1961
+ }
1962
+ }
1963
+ /**
1964
+ * Look up the harness impl for a resolved pick and enforce the `runTask`
1965
+ * capability. Pi trips this today with the honest impl-gap error instead of
1966
+ * silently downgrading to anthropic.
1967
+ */
1968
+ function requireTaskHarness(pick) {
1969
+ const harness = getHarness(pick.harness);
1970
+ if (!harness.runTask) throw new Error(`Harness "${pick.harness}" does not implement runTask; orchestrator mode requires it.`);
1971
+ return harness;
1972
+ }
1973
+ function terminalResult(result) {
1974
+ switch (result.kind) {
1975
+ case "success": return;
1976
+ case "decided_failure": return {
1977
+ outcome: "failed",
1978
+ failure: result.failure
1979
+ };
1980
+ case "abort": return {
1981
+ outcome: "failed",
1982
+ failure: {
1983
+ code: AGENT_ERROR_CODE[result.classification],
1984
+ message: result.message ?? "Agent aborted",
1985
+ error: result.error
1986
+ }
1987
+ };
1988
+ case "failure": return {
1989
+ outcome: "failed",
1990
+ failure: {
1991
+ code: AGENT_ERROR_CODE[result.classification],
1992
+ message: result.message ?? "Agent failed",
1993
+ error: result.error
1994
+ }
1995
+ };
1996
+ }
1997
+ }
1998
+ function cancelledRun() {
1999
+ return {
2000
+ outcome: "aborted",
2001
+ failure: {
2002
+ code: ErrorCodes.AgentAbort,
2003
+ message: "Agent run cancelled"
2004
+ }
2005
+ };
2006
+ }
2007
+ /** Every skill entry the menu knows, across categories. */
2008
+ async function fetchSkillMenuEntries(skillsBaseUrl) {
2009
+ const menu = await fetchSkillMenu(skillsBaseUrl);
2010
+ if (!menu) return [];
2011
+ return Object.values(menu.categories).flat();
2012
+ }
2013
+ /** Menu id for a bare skill id + framework via the menu's declared group/framework/default fields; undefined when nothing matches. */
2014
+ function resolveSkillVariantId(entries, skillId, framework) {
2015
+ if (entries.some((e) => e.id === skillId)) return skillId;
2016
+ if (!framework) return void 0;
2017
+ const family = entries.filter((e) => e.group === skillId && e.framework === framework);
2018
+ return (family.find((e) => e.default) ?? family[0])?.id;
2019
+ }
2020
+ /**
2021
+ * The framework reference is the full `integration` skill. `input.skillId` is
2022
+ * the bare framework (e.g. `django`), but the skill menu ids it as
2023
+ * `integration-<variant>`.
2024
+ */
2025
+ function resolveReferenceSkillId(entries, framework) {
2026
+ return resolveSkillVariantId(entries, "integration", framework);
2027
+ }
2028
+ /**
2029
+ * How long an optional step's notice waits for an answer.
2030
+ *
2031
+ * Much shorter than LONGER_ASK_TIMEOUT_MS, because it is asking for something
2032
+ * much smaller: one keypress to accept or decline, not "go mint a restricted
2033
+ * Stripe key". It cannot be unbounded either — the notice is a modal, and a run
2034
+ * that stops forever behind one nobody is looking at is worse than one that
2035
+ * takes the safe answer and carries on.
2036
+ *
2037
+ * The offer is made at seed time, seconds into the run, so five minutes is a
2038
+ * generous allowance for a person who is by then still watching the wizard
2039
+ * start. It was not: from 2.63.0 to 2.65.0 the offer was made at the moment the
2040
+ * step became runnable — a median seven minutes in, after every coding task —
2041
+ * and this timeout became the answer for about a quarter of all runs.
2042
+ */
2043
+ const TASK_NOTICE_TIMEOUT_MS = 300 * 1e3;
2044
+ /**
2045
+ * Offer an optional step, defaulting to declining it if nobody answers.
2046
+ *
2047
+ * Skip is the right default on a timeout: continuing would send the step on to
2048
+ * ask for credentials that the same absent user cannot supply either, burning
2049
+ * {@link LONGER_ASK_TIMEOUT_MS} per question before falling back to the same
2050
+ * links declining gives immediately.
2051
+ */
2052
+ async function offerSeededTask(notice, { timeoutMs = TASK_NOTICE_TIMEOUT_MS, interaction, signal } = {}) {
2053
+ if (signal?.aborted) return {
2054
+ keep: false,
2055
+ timedOut: false
2056
+ };
2057
+ if (!interaction?.taskNotice) return {
2058
+ keep: false,
2059
+ timedOut: false
2060
+ };
2061
+ const { taskNotice } = interaction;
2062
+ const controller = new AbortController();
2063
+ let timer;
2064
+ let timedOut = false;
2065
+ let cancelForAbort;
2066
+ const timeout = new Promise((resolve) => {
2067
+ timer = setTimeout(() => {
2068
+ timedOut = true;
2069
+ resolve(false);
2070
+ controller.abort();
2071
+ }, timeoutMs);
2072
+ });
2073
+ const aborted = new Promise((resolve) => {
2074
+ cancelForAbort = () => {
2075
+ resolve(false);
2076
+ controller.abort();
2077
+ };
2078
+ signal?.addEventListener("abort", cancelForAbort, { once: true });
2079
+ });
2080
+ try {
2081
+ return {
2082
+ keep: await Promise.race([
2083
+ taskNotice(notice, { signal: controller.signal }),
2084
+ timeout,
2085
+ aborted
2086
+ ]),
2087
+ timedOut
2088
+ };
2089
+ } finally {
2090
+ if (timer) clearTimeout(timer);
2091
+ if (cancelForAbort) signal?.removeEventListener("abort", cancelForAbort);
2092
+ }
2093
+ }
2094
+ /**
2095
+ * Which runner-seeded task types finished successfully.
2096
+ *
2097
+ * Handed to the program's `buildOutroNextSteps` so it can drop a next step its
2098
+ * own seeded task already carried out. Only `Done` counts: a skipped, declined
2099
+ * or failed step left the work undone, and that is exactly when the outro
2100
+ * bullet pointing at the app is the one thing the user still needs.
2101
+ */
2102
+ function completedSeededTypes(store, seededTasks) {
2103
+ return seededTasks.filter((task) => store.get(task.id)?.status === TaskStatus.Done).map((task) => task.type);
2104
+ }
2105
+ /** Which skip reason a negative {@link SeededConsent} carries onto the event. */
2106
+ function consentSkipReason(consent) {
2107
+ if (consent.errored) return SkipReason.NoticeError;
2108
+ return consent.timedOut ? SkipReason.NoticeTimeout : SkipReason.UserDeclined;
2109
+ }
2110
+ /**
2111
+ * Apply the seed-time answers to the queue, before the drain starts.
2112
+ *
2113
+ * A declined task is skipped rather than dropped, so the graph the planner saw
2114
+ * is the graph that ran — the sink already depends on this task, and
2115
+ * `nextRunnable` treats a skipped dependency as satisfied, so the report still
2116
+ * runs and can say the step was declined. The decline also stays in the funnel,
2117
+ * arriving as a `skipped` event carrying the reason that caused it rather than
2118
+ * as a task that silently never existed.
2119
+ *
2120
+ * It happens here rather than inside `runTask` because the executor marks a
2121
+ * task running — and fires `orchestrator task started` — before it hands the
2122
+ * task over. A decline applied on the far side of that emitted a start for a
2123
+ * task no agent ever ran, so every rate measured against starts counted the
2124
+ * declines twice: once in the denominator as a task that began, and again in
2125
+ * the numerator as a task that was skipped. Nothing upstream of the drain reads
2126
+ * task status, so applying the answers here changes only what the drain sees.
2127
+ *
2128
+ * Returns how many tasks were skipped, so the caller can redraw once.
2129
+ */
2130
+ function skipDeclinedSeededTasks(store, consentByTaskId, labelFor) {
2131
+ let skipped = 0;
2132
+ for (const [taskId, consent] of consentByTaskId) {
2133
+ if (consent.keep) continue;
2134
+ const task = store.get(taskId);
2135
+ if (!task) continue;
2136
+ const reason = consentSkipReason(consent);
2137
+ logToFile(`[orchestrator] runner-seeded ${task.type} skipped: ${reason}`);
2138
+ const declinedByUser = reason === SkipReason.UserDeclined;
2139
+ store.skip(taskId, reason, {
2140
+ goals: labelFor(task),
2141
+ did: declinedByUser ? "Nothing — the user chose to skip this step when offered it." : "Nothing — the step was offered at the start of the run and never accepted.",
2142
+ forNextAgent: declinedByUser ? "This step was offered and declined, so it did no work. Report it as skipped at the user’s request, not as failed." : "This step was offered and never accepted, so it did no work. Report it as not set up, and point the user at how to do it later."
2143
+ });
2144
+ skipped += 1;
2145
+ }
2146
+ return skipped;
2147
+ }
2148
+ /**
2149
+ * Ask for one seeded task's consent, and record how the answer came about.
2150
+ *
2151
+ * Consent and execution are separate concerns, and this is the consent half.
2152
+ * It runs at seed time — seconds into the run, with the user still watching —
2153
+ * while `seeded-deps.ts` keeps the work itself at the end of the queue. Asking
2154
+ * at the moment of execution instead, as 2.63.0 did, put the question in front
2155
+ * of a user who had long since tabbed away.
2156
+ *
2157
+ * Fails closed. The step this gates goes on to ask for live database and API
2158
+ * credentials, so a question that could not be put to the user is never read as
2159
+ * a yes.
2160
+ */
2161
+ async function askSeededConsent(type, notice, options = {}) {
2162
+ const consent = await offerSeededTask(notice, options).then((answer) => ({
2163
+ ...answer,
2164
+ errored: false
2165
+ }), (err) => {
2166
+ logToFile(`[orchestrator] notice failed for ${type}, declining: ${String(err)}`);
2167
+ analytics.captureException(err instanceof Error ? err : new Error(String(err)), { step: "orchestrator_task_notice" });
2168
+ return {
2169
+ keep: false,
2170
+ timedOut: false,
2171
+ errored: true
2172
+ };
2173
+ });
2174
+ analytics.wizardCapture("orchestrator task notice answered", {
2175
+ type,
2176
+ kept: consent.keep,
2177
+ timed_out: consent.timedOut,
2178
+ errored: consent.errored
2179
+ });
2180
+ return consent;
2181
+ }
2182
+ /** Whether a task's prompt lets it ask the user, and so needs the ask bridge. */
2183
+ function canAsk(prompt) {
2184
+ return (prompt?.allowedTools ?? []).includes(ASK_TOOL);
2185
+ }
2186
+ /** Splits terminal failures into run-failing (required) and reported-only (optional). */
2187
+ function drainVerdict(tasks) {
2188
+ const failed = tasks.filter((t) => t.status === TaskStatus.Failed);
2189
+ const pending = tasks.filter((t) => t.status === TaskStatus.Pending);
2190
+ return {
2191
+ requiredFailedTypes: failed.filter((t) => t.optional !== true).map((t) => t.type),
2192
+ optionalFailedTypes: failed.filter((t) => t.optional === true).map((t) => t.type),
2193
+ blocked: pending.length,
2194
+ blockedTypes: pending.map((t) => t.type)
2195
+ };
2196
+ }
2197
+ /**
2198
+ * The one-line "what went wrong" the abort message leads with.
2199
+ *
2200
+ * Both halves are named. A drain that ends with work still pending used to
2201
+ * report only how many steps never ran, which is the least useful fact about
2202
+ * them: a user who agreed to connect their data sources and then read that
2203
+ * "2 steps never ran" had no way to tell whether that step was one of them.
2204
+ */
2205
+ function describeDrainFailure(verdict) {
2206
+ const parts = [];
2207
+ if (verdict.requiredFailedTypes.length > 0) parts.push(`the ${verdict.requiredFailedTypes.join(", ")} step failed`);
2208
+ if (verdict.blockedTypes.length > 0) parts.push(`the ${verdict.blockedTypes.join(", ")} step${verdict.blockedTypes.length === 1 ? "" : "s"} never ran`);
2209
+ return parts.join(", so ");
2210
+ }
2211
+ /** One `orchestrator task blocked` event per pending task, best effort once the outcome is decided. */
2212
+ function reportBlockedTasks(tasks, failedTypes) {
2213
+ for (const task of tasks) {
2214
+ if (task.status !== TaskStatus.Pending) continue;
2215
+ try {
2216
+ analytics.wizardCapture("orchestrator task blocked", {
2217
+ type: task.type,
2218
+ optional: task.optional === true,
2219
+ failed_types: failedTypes.join(",") || "none"
2220
+ });
2221
+ } catch {}
2222
+ }
2223
+ }
2224
+ /** How many tasks deep in the graph a task sits — 0 when it depends on nothing. */
2225
+ function graphDepth(task, byId, memo) {
2226
+ const cached = memo.get(task.id);
2227
+ if (cached !== void 0) return cached;
2228
+ memo.set(task.id, 0);
2229
+ const depth = task.dependsOn.reduce((deepest, id) => {
2230
+ const dep = byId.get(id);
2231
+ return dep ? Math.max(deepest, graphDepth(dep, byId, memo) + 1) : deepest;
2232
+ }, 0);
2233
+ memo.set(task.id, depth);
2234
+ return depth;
2235
+ }
2236
+ /**
2237
+ * The queue in the order the user should read it, which is not the order tasks
2238
+ * were queued. Tasks the wizard seeded before the planner ran are queued first
2239
+ * but are optional side quests, so they sit at the end of the tier they run in
2240
+ * — the list reads as the run unfolds, and the first line is work that actually
2241
+ * started. Ordering only; nothing here changes what runs when.
2242
+ */
2243
+ function displayOrder(tasks, isOptional) {
2244
+ const byId = new Map(tasks.map((t) => [t.id, t]));
2245
+ const memo = /* @__PURE__ */ new Map();
2246
+ return tasks.map((task, index) => ({
2247
+ task,
2248
+ depth: graphDepth(task, byId, memo),
2249
+ optional: isOptional(task) ? 1 : 0,
2250
+ index
2251
+ })).sort((a, b) => a.depth - b.depth || a.optional - b.optional || a.index - b.index).map((entry) => entry.task);
2252
+ }
2253
+ /**
2254
+ * The run's excluded task types: CI gates plus the program's flag mapping.
2255
+ * Exported so a test can pin the flag→exclusion hookup against the real
2256
+ * program config — the registry and seed note both read this one list.
2257
+ */
2258
+ function effectiveExcludedTaskTypes(source, flags) {
2259
+ return [...ciExcludedTaskTypes(), ...source.excludedTaskTypes?.(flags) ?? []];
2260
+ }
2261
+ async function runOrchestrator(context) {
2262
+ const controller = new AbortController();
2263
+ const abortFromHost = () => controller.abort();
2264
+ context.signal?.addEventListener("abort", abortFromHost, { once: true });
2265
+ if (context.signal?.aborted) abortFromHost();
2266
+ let cleaned = false;
2267
+ const cleanupQueue = () => {
2268
+ if (cleaned) return;
2269
+ cleaned = true;
2270
+ try {
2271
+ rmSync(path$1.join(context.input.installDir, QUEUE_DIR_NAME), {
2272
+ recursive: true,
2273
+ force: true
2274
+ });
2275
+ } catch (error) {
2276
+ try {
2277
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), { step: "orchestrator_cache_cleanup" });
2278
+ } catch {}
2279
+ }
2280
+ };
2281
+ try {
2282
+ return await executeOrchestrator({
2283
+ ...context,
2284
+ signal: controller.signal
2285
+ }, cleanupQueue, controller);
2286
+ } finally {
2287
+ context.signal?.removeEventListener("abort", abortFromHost);
2288
+ cleanupQueue();
2289
+ }
2290
+ }
2291
+ async function executeOrchestrator({ config, input, boot, emit, interaction, signal }, cleanupQueue, controller) {
2292
+ if (signal?.aborted) return cancelledRun();
2293
+ const runId = randomUUID();
2294
+ const { run } = config;
2295
+ const programId = config.programId;
2296
+ const switchboardCtx = {
2297
+ ...config.switchboard,
2298
+ trace: void 0
2299
+ };
2300
+ const flow = config.agentFlow ?? programId;
2301
+ const registry = await loadAgentRegistry(boot.skillsBaseUrl, flow, {
2302
+ exclude: effectiveExcludedTaskTypes(config, boot.wizardFlags),
2303
+ overrides: resolveStageOverrides(programId, boot.wizardFlags, boot.wizardFlagPayloads)
2304
+ });
2305
+ if (signal?.aborted) return cancelledRun();
2306
+ const seedPrompt = registry.seed;
2307
+ if (!seedPrompt) throw new Error(`No seed agent prompt (frontmatter \`seed: true\`) for flow "${flow}" is available from ${boot.skillsBaseUrl}.`);
2308
+ const taskModels = Object.fromEntries(["seed", ...registry.types].map((type) => {
2309
+ const prompt = type === "seed" ? seedPrompt : registry.get(type);
2310
+ const pick = resolveHarness(switchboardCtx, type);
2311
+ const specModel = prompt && promptModelFor(prompt, pick.harness).model;
2312
+ return [type, isValidModel(specModel) ? specModel : pick.model];
2313
+ }));
2314
+ logToFile(`[orchestrator] task models: ${Object.entries(taskModels).map(([t, m]) => `${t}=${m}`).join(" ")}`);
2315
+ analytics.wizardCapture("orchestrator task models", taskModels);
2316
+ const runStartMs = Date.now();
2317
+ const metrics = new RunMetrics(runStartMs);
2318
+ const durationMs = (t) => t.startedAt && t.finishedAt ? Date.parse(t.finishedAt) - Date.parse(t.startedAt) : void 0;
2319
+ const store = new QueueStore(input.installDir, runId, { onTransition: (event, task) => {
2320
+ const pick = resolveHarness(switchboardCtx, task.type);
2321
+ const specModel = taskModelSpec(registry, task, pick.harness).model;
2322
+ const base = {
2323
+ type: task.type,
2324
+ model: isValidModel(specModel) ? specModel : pick.model,
2325
+ attempts: task.attempts,
2326
+ optional: task.optional === true
2327
+ };
2328
+ switch (event) {
2329
+ case "enqueue":
2330
+ analytics.wizardCapture("orchestrator task enqueued", {
2331
+ type: task.type,
2332
+ enqueued_by: task.enqueuedBy,
2333
+ dynamic: task.enqueuedBy !== "orchestrator"
2334
+ });
2335
+ break;
2336
+ case "start":
2337
+ analytics.wizardCapture("orchestrator task started", {
2338
+ ...base,
2339
+ ...metrics.recordStart(Date.now())
2340
+ });
2341
+ break;
2342
+ case "complete":
2343
+ metrics.recordComplete(Date.now());
2344
+ analytics.wizardCapture("orchestrator task completed", {
2345
+ ...base,
2346
+ duration_ms: durationMs(task)
2347
+ });
2348
+ break;
2349
+ case "skip":
2350
+ metrics.recordTerminal(Date.now());
2351
+ analytics.wizardCapture("orchestrator task skipped", {
2352
+ ...base,
2353
+ duration_ms: durationMs(task),
2354
+ reason: task.skipReason,
2355
+ not_needed_reason: task.notNeededReason
2356
+ });
2357
+ break;
2358
+ case "fail":
2359
+ metrics.recordTerminal(Date.now());
2360
+ analytics.wizardCapture("orchestrator task failed", {
2361
+ ...base,
2362
+ duration_ms: durationMs(task),
2363
+ error: task.error?.type
2364
+ });
2365
+ break;
2366
+ case "requeue": break;
2367
+ }
2368
+ } });
2369
+ let examplePath;
2370
+ let commandmentsPath;
2371
+ let referenceInstallPath;
2372
+ const menuSkillEntries = await fetchSkillMenuEntries(boot.skillsBaseUrl);
2373
+ if (signal?.aborted) return cancelledRun();
2374
+ const framework = input.integration ?? input.skillId ?? void 0;
2375
+ const referenceSkillId = framework ? resolveReferenceSkillId(menuSkillEntries, framework) : void 0;
2376
+ if (referenceSkillId) {
2377
+ const ref = await installSkillById(referenceSkillId, input.installDir, boot.skillsBaseUrl, {
2378
+ skillsRoot: path$1.join(QUEUE_DIR_NAME, "reference"),
2379
+ triage: boot.triageProvider
2380
+ });
2381
+ if (signal?.aborted) return cancelledRun();
2382
+ if (ref.kind === "ok") {
2383
+ referenceInstallPath = ref.path;
2384
+ const example = path$1.join(ref.path, "references", "EXAMPLE.md");
2385
+ if (existsSync(path$1.join(input.installDir, example))) examplePath = example;
2386
+ const commandments = path$1.join(ref.path, "references", "COMMANDMENTS.md");
2387
+ if (existsSync(path$1.join(input.installDir, commandments))) commandmentsPath = commandments;
2388
+ } else logToFile(`[orchestrator] reference unavailable: ${ref.kind} (${referenceSkillId})`);
2389
+ } else if (framework) logToFile(`[orchestrator] no integration skill for framework "${framework}"`);
2390
+ const missingVariants = [];
2391
+ for (const type of registry.types) for (const skillId of registry.get(type)?.skills ?? []) {
2392
+ if (resolveSkillVariantId(menuSkillEntries, skillId, framework)) continue;
2393
+ missingVariants.push(`${type}/${skillId}`);
2394
+ logToFile(`[orchestrator] no skill variant type=${type} skill=${skillId} framework=${framework ?? "none"}`);
2395
+ analytics.wizardCapture("orchestrator skill variant missing", {
2396
+ task_type: type,
2397
+ skill: skillId,
2398
+ framework
2399
+ });
2400
+ }
2401
+ if (missingVariants.length > 0) {
2402
+ const docsUrl = framework ? input.frameworkDocsUrl : void 0;
2403
+ return failed({
2404
+ code: ErrorCodes.AgentOrchestratorSkillVariantMissing,
2405
+ message: `Setup instructions for this project failed to download.
2406
+ Please try again, or contact wizard@posthog.com.
2407
+
2408
+ You can also set up with your agent by downloading the skills here:
2409
+ https://github.com/PostHog/context-mill/releases
2410
+ or integrate manually here:
2411
+ ${docsUrl ?? "https://posthog.com/docs"}`,
2412
+ error: new WizardError("Orchestrator preflight: skill variant missing", {
2413
+ missing: missingVariants.join(", "),
2414
+ framework
2415
+ }, ErrorCodes.AgentOrchestratorSkillVariantMissing)
2416
+ });
2417
+ }
2418
+ const promptContext = {
2419
+ projectId: boot.credentials.projectId,
2420
+ projectApiKey: boot.credentials.projectApiKey,
2421
+ host: boot.credentials.host,
2422
+ examplePath,
2423
+ commandmentsPath
2424
+ };
2425
+ logToFile(`[orchestrator] START program=${programId} dir=${input.installDir} run=${runId}`);
2426
+ analytics.wizardCapture("orchestrator started", { program_id: programId });
2427
+ emit({
2428
+ kind: "lifecycle",
2429
+ phase: "started"
2430
+ });
2431
+ const labelFor = (t) => t.label ?? registry.get(t.type)?.label ?? t.type;
2432
+ const renderQueue = () => emit({
2433
+ kind: "tasks",
2434
+ tasks: displayOrder(store.list(), (t) => registry.runnerSeededTypes.includes(t.type)).map((t) => ({
2435
+ id: `${runId}:${t.id}`,
2436
+ source: runId,
2437
+ content: labelFor(t),
2438
+ status: toTodoStatus(t.status),
2439
+ activeForm: labelFor(t)
2440
+ }))
2441
+ });
2442
+ const orchestratorCtx = (currentTaskId) => ({
2443
+ store,
2444
+ validTypes: registry.enqueueableTypes,
2445
+ sinkTypes: registry.sinkTypes,
2446
+ runnerSeededTypes: registry.runnerSeededTypes,
2447
+ optionalTypes: registry.optionalTypes,
2448
+ currentTaskId
2449
+ });
2450
+ const seedEntries = areSeededTasksEnabled(boot.wizardFlags) ? config.seedTasks?.() ?? [] : [];
2451
+ const seededTypes = [];
2452
+ const seededTasks = [];
2453
+ const seededConsent = /* @__PURE__ */ new Map();
2454
+ for (const seeded of seedEntries) {
2455
+ if (!registry.runnerSeededTypes.includes(seeded.type)) {
2456
+ logToFile(`[orchestrator] skipping runner-seeded task "${seeded.type}": not a runner-seeded type in this flow`);
2457
+ continue;
2458
+ }
2459
+ const task = store.enqueue({
2460
+ type: seeded.type,
2461
+ label: seeded.label,
2462
+ inputs: seeded.inputs,
2463
+ enqueuedBy: "orchestrator",
2464
+ optional: true
2465
+ });
2466
+ seededTypes.push(seeded.type);
2467
+ seededTasks.push(task);
2468
+ if (seeded.notice) {
2469
+ seededConsent.set(task.id, await askSeededConsent(seeded.type, seeded.notice, {
2470
+ interaction,
2471
+ signal
2472
+ }));
2473
+ if (signal?.aborted) return cancelledRun();
2474
+ }
2475
+ logToFile(`[orchestrator] runner-seeded task ${seeded.type}`);
2476
+ }
2477
+ const askingTypes = seededTasks.filter((task) => seededConsent.get(task.id)?.keep !== false).map((task) => task.type).filter((type) => canAsk(registry.get(type)));
2478
+ analytics.setTag("orchestrator_awaits_user", askingTypes.length > 0);
2479
+ analytics.setTag("orchestrator_runner_seeded_types", seededTypes.join(",") || "none");
2480
+ if (askingTypes.length > 0) analytics.wizardCapture("orchestrator awaits user", {
2481
+ task_types: askingTypes,
2482
+ ask_timeout_ms: LONGER_ASK_TIMEOUT_MS
2483
+ });
2484
+ const askBridge = shouldDisableAsk(input.flags) ? void 0 : createAskBridge(interaction, {
2485
+ signal,
2486
+ getSource: () => input.skillId ?? programId,
2487
+ beforeShow: () => {
2488
+ metrics.recordAsk(Date.now());
2489
+ ringTerminalBell();
2490
+ },
2491
+ richLinks: run.richLinks ?? false,
2492
+ timeoutMs: LONGER_ASK_TIMEOUT_MS
2493
+ });
2494
+ const spinner = createEmitSpinner(emit);
2495
+ const seedPick = resolveHarness(switchboardCtx, "seed");
2496
+ const seedHarness = requireTaskHarness(seedPick);
2497
+ const seedModel = promptModelFor(seedPrompt, seedPick.harness);
2498
+ const seedResult = await seedHarness.runTask({
2499
+ signal,
2500
+ config,
2501
+ input,
2502
+ boot,
2503
+ emit,
2504
+ prompt: assembleSeedPrompt(promptContext, seedPrompt.body, store.list(), registry.excludedTypes),
2505
+ spinner,
2506
+ model: requireKnownModel(seedModel.model, seedPick.model),
2507
+ effort: seedModel.effort,
2508
+ ...agentRunTools(seedPrompt),
2509
+ orchestrator: orchestratorCtx(),
2510
+ spinnerMessage: "Planning the integration...",
2511
+ successMessage: "Planned the integration",
2512
+ requestRemark: false,
2513
+ analyticsProperties: {
2514
+ task_type: "seed",
2515
+ harness: seedPick.harness
2516
+ }
2517
+ });
2518
+ if (signal?.aborted) return cancelledRun();
2519
+ const seedTerminal = terminalResult(seedResult);
2520
+ if (seedTerminal) return seedTerminal;
2521
+ analytics.wizardCapture("orchestrator seeded", {
2522
+ task_count: store.list().length,
2523
+ types: store.list().map((t) => t.type)
2524
+ });
2525
+ renderQueue();
2526
+ const deferred = deferSeededTasks(store, seededTasks, (type) => registry.get(type)?.dependsOn ?? [], registry.sinkTypes);
2527
+ for (const entry of deferred) {
2528
+ logToFile(`[orchestrator] deferred runner-seeded ${entry.type}: ${entry.added.length} deps (${entry.declared ? `declared: ${entry.declaredTypes.join(", ")}` : "default"})${entry.refused.length > 0 ? `, ${entry.refused.length} refused` : ""}${entry.unresolvedTypes.length > 0 ? `, unresolved: ${entry.unresolvedTypes.join(", ")}` : ""}`);
2529
+ analytics.wizardCapture("orchestrator seeded task deferred", {
2530
+ type: entry.type,
2531
+ dep_count: entry.added.length,
2532
+ declared: entry.declared,
2533
+ declared_types: entry.declaredTypes,
2534
+ unresolved_types: entry.unresolvedTypes,
2535
+ refused_count: entry.refused.length
2536
+ });
2537
+ }
2538
+ if (deferred.length > 0) renderQueue();
2539
+ for (const seeded of seededTasks) {
2540
+ const dependants = store.list().filter((t) => !registry.sinkTypes.includes(t.type) && t.id !== seeded.id && dependencyClosure(store, [t.id]).has(seeded.id));
2541
+ if (dependants.length === 0) continue;
2542
+ logToFile(`[orchestrator] one-way rule broken: ${dependants.map((t) => t.type).join(", ")} depend on runner-seeded ${seeded.type}`);
2543
+ analytics.wizardCapture("orchestrator seeded task depended on", {
2544
+ type: seeded.type,
2545
+ dependant_types: dependants.map((t) => t.type)
2546
+ });
2547
+ }
2548
+ const unwaited = store.list().filter((t) => registry.sinkTypes.includes(t.type)).flatMap((sink) => uncoveredBySink({
2549
+ store,
2550
+ validTypes: registry.types,
2551
+ sinkTypes: registry.sinkTypes
2552
+ }, {
2553
+ type: sink.type,
2554
+ dependsOn: sink.dependsOn
2555
+ }).filter((t) => t.id !== sink.id));
2556
+ if (unwaited.length > 0) {
2557
+ analytics.wizardCapture("orchestrator sink invariant violated", { uncovered_types: unwaited.map((t) => t.type) });
2558
+ return failed({
2559
+ code: ErrorCodes.AgentOrchestratorSinkInvariant,
2560
+ message: `The wizard could not plan this setup: the final step would have skipped ${unwaited.map((t) => t.type).join(", ")}.\n\nPlease report this to: ${WIZARD_CONTACT_EMAIL}`,
2561
+ error: new WizardError("orchestrator sink does not cover the queue", {
2562
+ uncovered: unwaited.map((t) => `${t.type} (${t.id})`).join(", "),
2563
+ queue_state: JSON.stringify(store.list())
2564
+ }, ErrorCodes.AgentOrchestratorSinkInvariant)
2565
+ });
2566
+ }
2567
+ const taskSkillsRoot = path$1.join(QUEUE_DIR_NAME, "skills");
2568
+ const claudeSkillsDir = path$1.join(input.installDir, ".claude", "skills");
2569
+ const preexistingSkills = new Set(existsSync(claudeSkillsDir) ? readdirSync(claudeSkillsDir) : []);
2570
+ const runTask = async (task) => {
2571
+ if (signal?.aborted) return;
2572
+ renderQueue();
2573
+ try {
2574
+ const resolved = resolveTask(registry, task, store);
2575
+ const skillPaths = [];
2576
+ for (const skillId of resolved.skills) {
2577
+ if (signal?.aborted) return;
2578
+ const variantId = resolveSkillVariantId(menuSkillEntries, skillId, framework);
2579
+ if (!variantId) {
2580
+ logToFile(`[orchestrator] no skill variant type=${task.type} skill=${skillId} framework=${framework ?? "none"}`);
2581
+ continue;
2582
+ }
2583
+ const result = await installSkillById(variantId, input.installDir, boot.skillsBaseUrl, {
2584
+ skillsRoot: taskSkillsRoot,
2585
+ triage: boot.triageProvider
2586
+ });
2587
+ if (signal?.aborted) return;
2588
+ if (result.kind === "ok") skillPaths.push(path$1.join(result.path, "SKILL.md"));
2589
+ else {
2590
+ logToFile(`[orchestrator] skill install failed type=${task.type} skill=${variantId} ${result.kind}`);
2591
+ throw new Error(`Skill "${variantId}" for task "${task.type}" could not be installed (${result.kind}). If this is a permissions error, check that the project directory is writable.`);
2592
+ }
2593
+ }
2594
+ const taskPick = resolveHarness(switchboardCtx, task.type);
2595
+ const taskHarness = requireTaskHarness(taskPick);
2596
+ const taskModel = taskModelSpec(registry, task, taskPick.harness);
2597
+ let taskResult;
2598
+ try {
2599
+ taskResult = await taskHarness.runTask({
2600
+ signal,
2601
+ config,
2602
+ input,
2603
+ boot,
2604
+ emit,
2605
+ prompt: assembleTaskPrompt(promptContext, resolved.prompt, skillPaths),
2606
+ spinner,
2607
+ model: requireKnownModel(taskModel.model, taskPick.model),
2608
+ effort: taskModel.effort,
2609
+ allowedTools: resolved.allowedTools,
2610
+ disallowedTools: resolved.disallowedTools,
2611
+ askBridge: canAsk(registry.get(task.type)) ? askBridge : void 0,
2612
+ orchestrator: orchestratorCtx(task.id),
2613
+ spinnerMessage: "",
2614
+ successMessage: "",
2615
+ requestRemark: false,
2616
+ analyticsProperties: {
2617
+ task_type: task.type,
2618
+ task_id: task.id,
2619
+ harness: taskPick.harness
2620
+ }
2621
+ });
2622
+ } catch (error) {
2623
+ if (signal?.aborted) return;
2624
+ if (error instanceof RunTaskFatal) throw error;
2625
+ const failure = classifyRunFailure(error);
2626
+ throw new RunTaskFatal({
2627
+ code: failure.code,
2628
+ message: failure.message,
2629
+ error: error instanceof Error ? error : void 0
2630
+ }, "failed", task.type);
2631
+ }
2632
+ if (signal?.aborted) return;
2633
+ const terminal = terminalResult(taskResult);
2634
+ if (terminal) throw new RunTaskFatal(terminal.failure, terminal.outcome, task.type);
2635
+ } finally {
2636
+ try {
2637
+ sweepRunInstalledSkills(claudeSkillsDir, preexistingSkills, referenceSkillId);
2638
+ } catch (err) {
2639
+ try {
2640
+ logToFile("[orchestrator] per-task skill sweep failed:", err);
2641
+ } catch {}
2642
+ }
2643
+ try {
2644
+ renderQueue();
2645
+ } catch (err) {
2646
+ try {
2647
+ logToFile("[orchestrator] per-task queue render failed:", err);
2648
+ } catch {}
2649
+ }
2650
+ }
2651
+ };
2652
+ if (skipDeclinedSeededTasks(store, seededConsent, labelFor) > 0) renderQueue();
2653
+ let fatal;
2654
+ try {
2655
+ await drainQueue(store, runTask, {
2656
+ ...DEFAULT_DRAIN_OPTIONS,
2657
+ signal,
2658
+ onFatal: () => controller.abort()
2659
+ });
2660
+ } catch (error) {
2661
+ if (!(error instanceof RunTaskFatal)) throw error;
2662
+ fatal = error;
2663
+ } finally {
2664
+ config.hooks?.recordTaskOutcomes?.(store.list().map((t) => ({
2665
+ type: t.type,
2666
+ status: t.status,
2667
+ optional: t.optional === true
2668
+ })));
2669
+ try {
2670
+ if (!signal?.aborted && referenceSkillId && referenceInstallPath) promoteReferenceSkill(path$1.join(input.installDir, referenceInstallPath), claudeSkillsDir, referenceSkillId);
2671
+ } catch (err) {
2672
+ try {
2673
+ analytics.captureException(err instanceof Error ? err : new Error(String(err)), { step: "orchestrator_reference_promote" });
2674
+ } catch {}
2675
+ }
2676
+ cleanupQueue();
2677
+ try {
2678
+ sweepRunInstalledSkills(claudeSkillsDir, preexistingSkills, referenceSkillId);
2679
+ } catch (err) {
2680
+ try {
2681
+ analytics.captureException(err instanceof Error ? err : new Error(String(err)), { step: "orchestrator_skill_sweep" });
2682
+ } catch {}
2683
+ }
2684
+ }
2685
+ if (fatal) {
2686
+ const stoppedBy = drainVerdict(store.list()).requiredFailedTypes;
2687
+ if (fatal.taskType && !stoppedBy.includes(fatal.taskType)) stoppedBy.push(fatal.taskType);
2688
+ reportBlockedTasks(store.list(), stoppedBy);
2689
+ return {
2690
+ outcome: fatal.outcome,
2691
+ failure: fatal.failure
2692
+ };
2693
+ }
2694
+ if (signal?.aborted) return cancelledRun();
2695
+ renderQueue();
2696
+ const summary = store.summary();
2697
+ logToFile(`[orchestrator] DONE done=${summary.done} failed=${summary.failed} total=${summary.total}`);
2698
+ analytics.wizardCapture("orchestrator run finished", {
2699
+ tasks_total: summary.total,
2700
+ tasks_done: summary.done,
2701
+ tasks_failed: summary.failed,
2702
+ tasks_skipped: summary[TaskStatus.Skipped],
2703
+ total_duration_ms: Date.now() - runStartMs,
2704
+ ...metrics.summary(),
2705
+ dynamic_enqueue_count: store.list().filter((t) => t.enqueuedBy !== "orchestrator").length,
2706
+ retried_task_count: store.list().filter((t) => t.attempts > 1).length
2707
+ });
2708
+ const reviewTask = store.list().find((t) => t.type === "review");
2709
+ const conflict = reviewTask ? store.readHandoff(reviewTask.id)?.conflict : void 0;
2710
+ const notRequired = summary[TaskStatus.Skipped];
2711
+ const verdict = drainVerdict(store.list());
2712
+ const blocked = verdict.blocked;
2713
+ reportBlockedTasks(store.list(), verdict.requiredFailedTypes);
2714
+ if (verdict.requiredFailedTypes.length > 0 || blocked > 0) {
2715
+ const failedTypes = verdict.requiredFailedTypes.join(", ");
2716
+ const whatFailed = describeDrainFailure(verdict);
2717
+ const missingScopes = (boot.credentials.missingScopes ?? []).filter((scope) => WIZARD_PROVISIONING_SCOPES.some((required) => required === scope) || !WIZARD_OAUTH_SCOPES.some((requested) => requested === scope));
2718
+ const message = missingScopes.length > 0 ? `The wizard could not finish setup: ${whatFailed}, and this run was authorized without the following permission${missingScopes.length === 1 ? "" : "s"}: ${missingScopes.join(", ")}.\n\nPlease try again, approving all permissions on the PostHog authorization screen. If it still fails, report it to: ${WIZARD_CONTACT_EMAIL}` : `The wizard was unable to set up PostHog: ${whatFailed}.\n\nPlease report this to: ${WIZARD_CONTACT_EMAIL}`;
2719
+ return failed({
2720
+ code: ErrorCodes.AgentOrchestratorTasksFailed,
2721
+ message,
2722
+ error: new WizardError("orchestrator drain ended with failed tasks", {
2723
+ tasks_failed: summary.failed,
2724
+ tasks_blocked: blocked,
2725
+ failed_types: failedTypes,
2726
+ missing_oauth_scopes: missingScopes.join(" "),
2727
+ queue_state: JSON.stringify(store.list())
2728
+ }, ErrorCodes.AgentOrchestratorTasksFailed)
2729
+ });
2730
+ }
2731
+ if (summary.total === 0) return failed({
2732
+ code: ErrorCodes.AgentOrchestratorHollowRun,
2733
+ message: `The wizard was unable to set up PostHog: the planning step produced no work, so nothing ran.\n\nPlease try again — and if it happens again, report it to: ${WIZARD_CONTACT_EMAIL}`,
2734
+ error: new WizardError("orchestrator drain produced zero tasks", { queue_state: JSON.stringify(store.list()) }, ErrorCodes.AgentOrchestratorHollowRun)
2735
+ });
2736
+ const optionalFailedCount = verdict.optionalFailedTypes.length;
2737
+ const stepNotes = [notRequired > 0 ? `${notRequired} skipped as not required` : "", optionalFailedCount > 0 ? `${optionalFailedCount} optional step failed` : ""].filter(Boolean);
2738
+ const message = conflict ? "PostHog set up, with one conflict to review." : `PostHog set up: ${summary.done}/${summary.total - notRequired - optionalFailedCount} steps completed${stepNotes.length > 0 ? ` (${stepNotes.join(", ")})` : ""}.`;
2739
+ const outro = {
2740
+ kind: "success",
2741
+ message,
2742
+ body: conflict ? `⚠ Build conflict: ${conflict}\nFull details are in the setup report.` : void 0,
2743
+ docsUrl: "https://posthog.com/docs/ai-engineering/ai-wizard",
2744
+ nextSteps: config.hooks?.buildOutroNextSteps?.(boot.credentials, completedSeededTypes(store, seededTasks))
2745
+ };
2746
+ emit({
2747
+ kind: "completion",
2748
+ outro
2749
+ });
2750
+ emit({
2751
+ kind: "lifecycle",
2752
+ phase: "completed",
2753
+ message
2754
+ });
2755
+ return {
2756
+ outcome: "success",
2757
+ outro
2758
+ };
2759
+ }
2760
+ //#endregion
2761
+ //#region src/agent/runner/switchboard/sequence.ts
2762
+ /**
2763
+ * Sequence axis: gate helpers, registry, middleware, resolver.
2764
+ * Percentage rollouts are PostHog-side — the gate just reads the resolved bool.
2765
+ */
2766
+ const SEQUENCE_OPTIONS = {
2767
+ ["linear"]: {
2768
+ name: "linear",
2769
+ run: (ctx) => runLinearProgram(ctx)
2770
+ },
2771
+ ["orchestrator"]: {
2772
+ name: "orchestrator",
2773
+ run: (ctx) => runOrchestrator(ctx)
2774
+ }
2775
+ };
2776
+ function getSequence(name) {
2777
+ const sequence = SEQUENCE_OPTIONS[name];
2778
+ if (!sequence) throw new Error(`No sequence registered for '${name}'.`);
2779
+ return sequence;
2780
+ }
2781
+ /**
2782
+ * A composed sub-run (integration inside self-driving) is structurally
2783
+ * linear: the orchestrator owns the full run lifecycle (queue, outro) and
2784
+ * cannot nest. Sits above every override, including CLI.
2785
+ */
2786
+ const composedClampMw = (ctx, next) => {
2787
+ if (!ctx.composed) return next();
2788
+ if (ctx.trace) ctx.trace.sequence = "composed";
2789
+ return "linear";
2790
+ };
2791
+ /** A program's own flag route may pin the sequence; wins over the global orchestrator flag. Traced as 'payload' to stay distinguishable from sequence experiments ('flag'). */
2792
+ const flagRouteSequenceMw = (ctx, next) => {
2793
+ const route = resolveFlagRoute(ctx.program, ctx.flags, ctx.flagPayloads);
2794
+ if (!route?.sequence) return next();
2795
+ if (ctx.trace) ctx.trace.sequence = "payload";
2796
+ return route.sequence;
2797
+ };
2798
+ /** Sequence experiments (e.g. wizard-orchestrator), each inert outside its declared programs. */
2799
+ const sequenceExperimentMw = (ctx, next) => {
2800
+ const sequence = resolveFlagSequence(ctx.program, ctx.flags);
2801
+ if (!sequence) return next();
2802
+ if (ctx.trace) ctx.trace.sequence = "flag";
2803
+ return sequence;
2804
+ };
2805
+ /**
2806
+ * The orchestrator drives harnesses through `runTask`; a harness that has not
2807
+ * implemented it clamps the run to linear. A capability check, not a harness
2808
+ * identity check — a harness gains orchestrator support by implementing the
2809
+ * method, with no switchboard change. Sits below the CLI override so
2810
+ * `--sequence orchestrator` still reproduces the hard error in dev builds.
2811
+ */
2812
+ const runTaskCapabilityClampMw = (ctx, next) => {
2813
+ const pick = resolveHarness(ctx);
2814
+ if (getHarness(pick.harness).runTask) return next();
2815
+ if (isOrchestratorEnabled(ctx.flags)) logToFile(`[switchboard] wizard-orchestrator ignored: ${pick.harness} has no runTask, clamping to linear`);
2816
+ if (ctx.trace) ctx.trace.sequence = "runtask-clamp";
2817
+ return "linear";
2818
+ };
2819
+ const SEQUENCE_MIDDLEWARE = [
2820
+ composedClampMw,
2821
+ ...[],
2822
+ runTaskCapabilityClampMw,
2823
+ flagRouteSequenceMw,
2824
+ sequenceExperimentMw
2825
+ ];
2826
+ /** CLI wins over `wizard-orchestrator` flag wins over binding default. */
2827
+ function resolveSequence(ctx) {
2828
+ const sequence = runChain(SEQUENCE_MIDDLEWARE, ctx, () => {
2829
+ if (ctx.trace) ctx.trace.sequence = "binding";
2830
+ return (PROGRAM_BINDINGS[ctx.program] ?? DEFAULT_BINDING).sequence;
2831
+ });
2832
+ logToFile(`[switchboard] resolved: program=${ctx.program} sequence=${sequence}${ctx.trace?.sequence ? ` (${ctx.trace.sequence})` : ""}`);
2833
+ return sequence;
2834
+ }
2835
+ //#endregion
2836
+ //#region src/agent/runner/switchboard/index.ts
2837
+ /**
2838
+ * Run a middleware chain over `ctx`. Each middleware receives `next` (which
2839
+ * runs the rest of the chain) and can either:
2840
+ * - defer: call `next()` and optionally modify its result (overlay pattern)
2841
+ * - short-circuit: return a value without calling `next()` (skip the rest)
2842
+ *
2843
+ * **Earlier in the array = higher precedence.** Index 0 runs first and can
2844
+ * short-circuit the rest; index 1 only runs if index 0 deferred. So
2845
+ * `[cliSequenceMw, orchestratorFeatureFlagMw]` means CLI takes precedence over the
2846
+ * flag, not the other way around.
2847
+ *
2848
+ * `fallback` runs at the end — reached only when every middleware deferred.
2849
+ * Typically the map read for the base value.
2850
+ */
2851
+ function runChain(chain, ctx, fallback) {
2852
+ function step(index) {
2853
+ if (index >= chain.length) return fallback();
2854
+ const middleware = chain[index];
2855
+ const next = () => step(index + 1);
2856
+ return middleware(ctx, next);
2857
+ }
2858
+ return step(0);
2859
+ }
2860
+ const DEFAULT_BINDING = {
2861
+ sequence: "linear",
2862
+ harness: "pi",
2863
+ model: GPT5_6_SOL_MODEL,
2864
+ thinkingLevel: "medium"
2865
+ };
2866
+ /**
2867
+ * Per-program routing. Kept in lockstep with `PROGRAM_REGISTRY` by the
2868
+ * switchboard test. Anything absent falls back to `DEFAULT_BINDING`.
2869
+ */
2870
+ const PROGRAM_BINDINGS = {
2871
+ "posthog-integration": DEFAULT_BINDING,
2872
+ "revenue-analytics-setup": DEFAULT_BINDING,
2873
+ "warehouse-source": DEFAULT_BINDING,
2874
+ "error-tracking-upload-source-maps": {
2875
+ sequence: "linear",
2876
+ harness: "pi",
2877
+ model: GPT5_6_SOL_MODEL,
2878
+ thinkingLevel: "medium"
2879
+ },
2880
+ audit: DEFAULT_BINDING,
2881
+ "events-audit": DEFAULT_BINDING,
2882
+ "posthog-doctor": DEFAULT_BINDING,
2883
+ "web-analytics-doctor": DEFAULT_BINDING,
2884
+ migration: DEFAULT_BINDING,
2885
+ "self-driving": DEFAULT_BINDING,
2886
+ "agent-skill": DEFAULT_BINDING,
2887
+ "mcp-add": DEFAULT_BINDING,
2888
+ "mcp-remove": DEFAULT_BINDING,
2889
+ "mcp-tutorial": DEFAULT_BINDING,
2890
+ "mcp-analytics": DEFAULT_BINDING,
2891
+ metrics: {
2892
+ sequence: "orchestrator",
2893
+ harness: "pi",
2894
+ model: DEFAULT_AGENT_MODEL
2895
+ },
2896
+ "replay-vision": {
2897
+ sequence: "orchestrator",
2898
+ harness: "anthropic",
2899
+ model: DEFAULT_AGENT_MODEL
2900
+ },
2901
+ "error-tracking": {
2902
+ sequence: "orchestrator",
2903
+ harness: "pi",
2904
+ model: DEFAULT_AGENT_MODEL
2905
+ },
2906
+ "ai-observability": {
2907
+ sequence: "linear",
2908
+ harness: "pi",
2909
+ model: GPT5_6_TERRA_MODEL,
2910
+ thinkingLevel: "high"
2911
+ },
2912
+ slack: DEFAULT_BINDING
2913
+ };
2914
+ /** Compose both axes. Callers needing only one axis use the per-axis resolver. */
2915
+ function resolveBinding(ctx, role = "default") {
2916
+ ctx.trace ??= {};
2917
+ const sequence = resolveSequence(ctx);
2918
+ const { harness, model, thinkingLevel } = resolveHarness(ctx, role);
2919
+ return {
2920
+ sequence,
2921
+ harness,
2922
+ model,
2923
+ thinkingLevel
2924
+ };
2925
+ }
2926
+ //#endregion
2927
+ //#region src/agent/runner/index.ts
2928
+ /**
2929
+ * Run a program's agent pipeline.
2930
+ *
2931
+ * Prepare → dispatch to the sequence the binding names → return its result
2932
+ * with the agent's own snapshot of what it reported. Missing observers change
2933
+ * nothing; a throwing observer is logged and the run continues.
2934
+ */
2935
+ async function runAgent(config, input, options = {}) {
2936
+ let collector;
2937
+ const snapshot = () => {
2938
+ try {
2939
+ if (collector) return collector.snapshot();
2940
+ } catch {}
2941
+ return {
2942
+ tasks: [],
2943
+ statusMessages: [],
2944
+ usage: {
2945
+ inputTokens: 0,
2946
+ outputTokens: 0,
2947
+ cacheReadTokens: 0,
2948
+ cacheCreationTokens: 0
2949
+ }
2950
+ };
2951
+ };
2952
+ const flushReport = () => {
2953
+ try {
2954
+ const report = flushScanReport({ yaraReport: input.flags.yaraReport });
2955
+ if (report) collector?.emit({
2956
+ kind: "log",
2957
+ level: "info",
2958
+ message: report
2959
+ });
2960
+ } catch {}
2961
+ };
2962
+ let result;
2963
+ try {
2964
+ collector = createProgressCollector(options.onProgress);
2965
+ const { emit } = collector;
2966
+ const log = (message) => emit({
2967
+ kind: "log",
2968
+ level: "info",
2969
+ message
2970
+ });
2971
+ if (options.signal?.aborted) {
2972
+ flushReport();
2973
+ return {
2974
+ outcome: "aborted",
2975
+ skillId: input.skillId,
2976
+ failure: {
2977
+ code: ErrorCodes.AgentAbort,
2978
+ message: "Agent run cancelled"
2979
+ },
2980
+ snapshot: snapshot()
2981
+ };
2982
+ }
2983
+ const boot = await prepareRun(config, input);
2984
+ if (config.binding.sequence === "orchestrator") log("Task-queue orchestrator enabled.");
2985
+ try {
2986
+ logToFile(`[agent-runner] run program=${config.programId} sequence=${config.binding.sequence} harness=${config.binding.harness} composed=${config.composed}`);
2987
+ } catch {}
2988
+ const sequenceResult = await getSequence(config.binding.sequence).run({
2989
+ config,
2990
+ input,
2991
+ boot,
2992
+ emit,
2993
+ interaction: options.interaction,
2994
+ signal: options.signal
2995
+ });
2996
+ result = {
2997
+ ...options.signal?.aborted && sequenceResult.outcome === "success" ? {
2998
+ outcome: "aborted",
2999
+ failure: {
3000
+ code: ErrorCodes.AgentAbort,
3001
+ message: "Agent run cancelled"
3002
+ }
3003
+ } : sequenceResult,
3004
+ skillId: input.skillId,
3005
+ snapshot: snapshot()
3006
+ };
3007
+ } catch (error) {
3008
+ const original = error instanceof Error ? error : new Error(safeErrorMessage(error));
3009
+ let failure;
3010
+ try {
3011
+ failure = classifyRunFailure(original);
3012
+ } catch {
3013
+ failure = {
3014
+ code: ErrorCodes.InternalUnhandled,
3015
+ message: "Unexpected agent error",
3016
+ coded: false
3017
+ };
3018
+ }
3019
+ try {
3020
+ logToFile("[agent-runner] run failed:", original);
3021
+ } catch {}
3022
+ let abortError = false;
3023
+ try {
3024
+ abortError = original.name === "AbortError";
3025
+ } catch {}
3026
+ if (failure.coded) result = {
3027
+ outcome: "failed",
3028
+ skillId: input?.skillId,
3029
+ failure: {
3030
+ code: failure.code,
3031
+ message: failure.message,
3032
+ error: original
3033
+ },
3034
+ snapshot: snapshot()
3035
+ };
3036
+ else if (options.signal?.aborted && abortError) result = {
3037
+ outcome: "aborted",
3038
+ skillId: input?.skillId,
3039
+ failure: {
3040
+ code: ErrorCodes.AgentAbort,
3041
+ message: "Agent run cancelled"
3042
+ },
3043
+ snapshot: snapshot()
3044
+ };
3045
+ else result = {
3046
+ outcome: "crashed",
3047
+ skillId: input?.skillId,
3048
+ failure: {
3049
+ code: failure.code,
3050
+ message: failure.message,
3051
+ error: original
3052
+ },
3053
+ snapshot: snapshot()
3054
+ };
3055
+ }
3056
+ flushReport();
3057
+ return result;
3058
+ }
3059
+ function safeErrorMessage(error) {
3060
+ try {
3061
+ return String(error);
3062
+ } catch {
3063
+ return "Unknown thrown value";
3064
+ }
3065
+ }
3066
+ //#endregion
3067
+ //#region src/agent/index.ts
3068
+ var agent_exports = /* @__PURE__ */ __exportAll({
3069
+ LONGER_ASK_TIMEOUT_MS: () => LONGER_ASK_TIMEOUT_MS,
3070
+ TASK_OUTCOMES_KEY: () => TASK_OUTCOMES_KEY,
3071
+ configureGatewayFromCIEnvironment: () => configureGatewayFromCIEnvironment,
3072
+ runMcpPromptViaSdk: () => runMcpPromptViaSdk
3073
+ });
3074
+ /**
3075
+ * Leaves in C2. The TUI receives agent data through program state. Until
3076
+ * then the suggested-prompts screen streams through this wrapper, which loads
3077
+ * the streaming module on first call so the startup chunk does not grow.
3078
+ */
3079
+ async function* runMcpPromptViaSdk(args) {
3080
+ yield* (await import("./mcp-prompt-streaming-I2U_X7BC.js")).runMcpPromptViaSdk(args);
3081
+ }
3082
+ //#endregion
3083
+ export { formatCostUsd as a, computeTokenCostUsd as i, runAgent as n, formatTokenCount as o, resolveBinding as r, shouldDisableAsk as s, agent_exports as t };
3084
+
3085
+ //# sourceMappingURL=agent-bCZ3i3fh.js.map