@posthog/wizard 2.80.0 → 2.81.0

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