@posthog/wizard 2.79.1 → 2.80.1

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