@posthog/wizard 2.77.0 → 2.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +30 -15
  2. package/dist/{add-mcp-server-to-clients-BnRyE4pw.js → add-mcp-server-to-clients-JMv8LP-O.js} +6 -5
  3. package/dist/{add-mcp-server-to-clients-BnRyE4pw.js.map → add-mcp-server-to-clients-JMv8LP-O.js.map} +1 -1
  4. package/dist/agent-bCZ3i3fh.js +3085 -0
  5. package/dist/agent-bCZ3i3fh.js.map +1 -0
  6. package/dist/{agent-interface-Avtr_5M5.js → agent-interface-BAOIHuXS.js} +429 -378
  7. package/dist/agent-interface-BAOIHuXS.js.map +1 -0
  8. package/dist/{agent-prompt-loader-CHixNPgY.js → agent-prompt-loader-7Pd4CfZO.js} +6 -6
  9. package/dist/agent-prompt-loader-7Pd4CfZO.js.map +1 -0
  10. package/dist/analytics-BcV-WCM4.js +2 -0
  11. package/dist/{analytics-C67F2vee.js → analytics-CHCZ_qUR.js} +10 -6
  12. package/dist/analytics-CHCZ_qUR.js.map +1 -0
  13. package/dist/{api-Dj1yabTj.js → api-Diy_2fp8.js} +4 -17
  14. package/dist/api-Diy_2fp8.js.map +1 -0
  15. package/dist/bin.js +410 -68
  16. package/dist/bin.js.map +1 -1
  17. package/dist/{bounded-fs-CWRYuLAn.js → bounded-fs-Bx9zTe6m.js} +4 -4
  18. package/dist/bounded-fs-Bx9zTe6m.js.map +1 -0
  19. package/dist/check-screens.tsx +1 -1
  20. package/dist/{ci-install-CvXL5h13.js → ci-install-CDdQYsPe.js} +6 -5
  21. package/dist/ci-install-CDdQYsPe.js.map +1 -0
  22. package/dist/constants-jFI2SaE1.js +260 -0
  23. package/dist/constants-jFI2SaE1.js.map +1 -0
  24. package/dist/debug-Ch4BDOtD.js +171 -0
  25. package/dist/debug-Ch4BDOtD.js.map +1 -0
  26. package/dist/{debug-CziFS0cA.js → debug-M2gV2iDq.js} +1 -1
  27. package/dist/{defaults-4c4YwTmW.js → defaults-C6J7yL4O.js} +1 -1
  28. package/dist/{defaults-4c4YwTmW.js.map → defaults-C6J7yL4O.js.map} +1 -1
  29. package/dist/endpoints-DCVbM06L.js +108 -0
  30. package/dist/endpoints-DCVbM06L.js.map +1 -0
  31. package/dist/{env-api-key-BZ5wE3Y4.js → env-api-key-Qx8NnYHm.js} +3 -3
  32. package/dist/env-api-key-Qx8NnYHm.js.map +1 -0
  33. package/dist/{environment-BEvsey6i.js → environment-B07Bmxae.js} +6 -6
  34. package/dist/environment-B07Bmxae.js.map +1 -0
  35. package/dist/{errors-BoJvrdw8.js → errors-CQ9kttJD.js} +27 -34
  36. package/dist/errors-CQ9kttJD.js.map +1 -0
  37. package/dist/{file-BoD3REGB.js → file-BcLTjU16.js} +2 -2
  38. package/dist/{file-BoD3REGB.js.map → file-BcLTjU16.js.map} +1 -1
  39. package/dist/{file-watcher-BPFazvG_.js → file-watcher-DWc_bTs6.js} +2 -2
  40. package/dist/{file-watcher-BPFazvG_.js.map → file-watcher-DWc_bTs6.js.map} +1 -1
  41. package/dist/{gateway-session-xPFv6p5c.js → gateway-CjoShdLa.js} +175 -20
  42. package/dist/gateway-CjoShdLa.js.map +1 -0
  43. package/dist/generate-version.cjs +1 -1
  44. package/dist/{headless-ui-CsmQrDIl.js → headless-ui-BFFmYC4v.js} +13 -2
  45. package/dist/headless-ui-BFFmYC4v.js.map +1 -0
  46. package/dist/{interactive-Dej-sxvR.js → interactive-D6zV7brJ.js} +2 -2
  47. package/dist/{interactive-Dej-sxvR.js.map → interactive-D6zV7brJ.js.map} +1 -1
  48. package/dist/{local-dev-DPZiIPCK.js → local-dev-McsurOC-.js} +2 -2
  49. package/dist/local-dev-McsurOC-.js.map +1 -0
  50. package/dist/logging-ui-_TwoMNbG.js +226 -0
  51. package/dist/logging-ui-_TwoMNbG.js.map +1 -0
  52. package/dist/mcp-DAgaoGk5.js +97 -0
  53. package/dist/mcp-DAgaoGk5.js.map +1 -0
  54. package/dist/{mcp-prompt-streaming-BU_kuur_.js → mcp-prompt-streaming-I2U_X7BC.js} +7 -6
  55. package/dist/mcp-prompt-streaming-I2U_X7BC.js.map +1 -0
  56. package/dist/{mint-failure-BrleGqMd.js → mint-failure-D4nd2XmZ.js} +82 -25
  57. package/dist/mint-failure-D4nd2XmZ.js.map +1 -0
  58. package/dist/models-DDdBJKwT.js +83 -0
  59. package/dist/models-DDdBJKwT.js.map +1 -0
  60. package/dist/{non-interactive-DcOjH2fw.js → non-interactive-Dzow7YKY.js} +3 -3
  61. package/dist/non-interactive-Dzow7YKY.js.map +1 -0
  62. package/dist/oauth-session-DJ8h6GFa.js +74 -0
  63. package/dist/oauth-session-DJ8h6GFa.js.map +1 -0
  64. package/dist/{orchestrator-tools-DXwr8Sp3.js → orchestrator-tools-szcdscwn.js} +4 -4
  65. package/dist/orchestrator-tools-szcdscwn.js.map +1 -0
  66. package/dist/{package-json-xt3eeyBI.js → package-json-DY70kkNp.js} +2 -2
  67. package/dist/package-json-DY70kkNp.js.map +1 -0
  68. package/dist/{pi-mY8I2XkG.js → pi-D3h5Ok0i.js} +329 -65
  69. package/dist/pi-D3h5Ok0i.js.map +1 -0
  70. package/dist/{playground-CDUUKLkC.js → playground-5AB4WgNF.js} +11 -7
  71. package/dist/playground-5AB4WgNF.js.map +1 -0
  72. package/dist/{posthog-Hc_-9wrr.js → posthog-DNn574nW.js} +8 -3
  73. package/dist/posthog-DNn574nW.js.map +1 -0
  74. package/dist/{provisioning-xfdjhFtA.js → provisioning-B8uSqRZk.js} +6 -5
  75. package/dist/provisioning-B8uSqRZk.js.map +1 -0
  76. package/dist/{queue-tools-C8y_xZJi.js → queue-tools-9OOVx5TE.js} +7 -86
  77. package/dist/queue-tools-9OOVx5TE.js.map +1 -0
  78. package/dist/{registry-DTmREJ-f.js → registry-8YG-kuXk.js} +265 -6
  79. package/dist/registry-8YG-kuXk.js.map +1 -0
  80. package/dist/run-agent-legacy-d7rWlzQR.js +3739 -0
  81. package/dist/run-agent-legacy-d7rWlzQR.js.map +1 -0
  82. package/dist/{security-Dw2oFJjP.js → security-Cg_4a7vB.js} +6 -6
  83. package/dist/security-Cg_4a7vB.js.map +1 -0
  84. package/dist/{setup-utils-fjJTBhaB.js → setup-utils-CmTCTPkX.js} +46 -18
  85. package/dist/setup-utils-CmTCTPkX.js.map +1 -0
  86. package/dist/smoke-test.sh +1 -1
  87. package/dist/{start-tui-UNe2xQ0U.js → start-tui-CB2PZs1x.js} +38 -44
  88. package/dist/start-tui-CB2PZs1x.js.map +1 -0
  89. package/dist/{steps-C2yqOk_h.js → steps-BZcXRJNY.js} +7 -7
  90. package/dist/{steps-C2yqOk_h.js.map → steps-BZcXRJNY.js.map} +1 -1
  91. package/dist/{store-Bic4Cr3L.js → store-UN7c-ASf.js} +18 -28
  92. package/dist/store-UN7c-ASf.js.map +1 -0
  93. package/dist/{subagent-Dz41vV9j.js → subagent-BofIDK-O.js} +8 -3
  94. package/dist/subagent-BofIDK-O.js.map +1 -0
  95. package/dist/{task-5-fw_Vw3.js → task-r9t1UhQP.js} +124 -36
  96. package/dist/task-r9t1UhQP.js.map +1 -0
  97. package/dist/{task-stream-dVzl6b_q.js → task-stream-CMUilu54.js} +45 -15
  98. package/dist/task-stream-CMUilu54.js.map +1 -0
  99. package/dist/{tasks-D1c2z1aw.js → tasks-Bbv_lOBG.js} +17 -13
  100. package/dist/tasks-Bbv_lOBG.js.map +1 -0
  101. package/dist/{telemetry-BRnxBRWK.js → telemetry-D6ruCtnv.js} +5 -5
  102. package/dist/telemetry-D6ruCtnv.js.map +1 -0
  103. package/dist/{terminal-9bvRa4Qt.js → terminal-DrGyci_a.js} +39 -74
  104. package/dist/terminal-DrGyci_a.js.map +1 -0
  105. package/dist/{tools-Dp6i9k70.js → tools-Dyfh9aCD.js} +11 -11
  106. package/dist/tools-Dyfh9aCD.js.map +1 -0
  107. package/dist/ui-BBVuTM1v.js +15 -0
  108. package/dist/ui-BBVuTM1v.js.map +1 -0
  109. package/dist/version-aXxpgRKx.js +6 -0
  110. package/dist/version-aXxpgRKx.js.map +1 -0
  111. package/dist/warlock-smoke-test.ts +2 -2
  112. package/dist/{wizard-abort-lE6umflt.js → wizard-abort-BBkiUxfS.js} +23 -20
  113. package/dist/wizard-abort-BBkiUxfS.js.map +1 -0
  114. package/dist/wizard-abort-BU4aLyvh.js +3 -0
  115. package/dist/{wizard-session-FSJCkTcP.js → wizard-session-2w4Ru-Wz.js} +12 -16
  116. package/dist/wizard-session-2w4Ru-Wz.js.map +1 -0
  117. package/dist/wizard-session-DGaUHOF3.js +2 -0
  118. package/dist/wizard-ui-DoyOZxK8.js +16 -0
  119. package/dist/wizard-ui-DoyOZxK8.js.map +1 -0
  120. package/dist/{yara-hooks-BTNJ6MGW.js → yara-hooks-DCeMDd1o.js} +19 -68
  121. package/dist/yara-hooks-DCeMDd1o.js.map +1 -0
  122. package/dist/yara-hooks-DSGuCU9A.js +2 -0
  123. package/package.json +1 -1
  124. package/dist/agent-interface-Avtr_5M5.js.map +0 -1
  125. package/dist/agent-prompt-loader-CHixNPgY.js.map +0 -1
  126. package/dist/agent-runner-COOIUJCP.js +0 -6188
  127. package/dist/agent-runner-COOIUJCP.js.map +0 -1
  128. package/dist/analytics-C67F2vee.js.map +0 -1
  129. package/dist/analytics-CJA1uLlk.js +0 -2
  130. package/dist/api-Dj1yabTj.js.map +0 -1
  131. package/dist/bounded-fs-CWRYuLAn.js.map +0 -1
  132. package/dist/ci-install-CvXL5h13.js.map +0 -1
  133. package/dist/debug-B4kzIZPi.js +0 -754
  134. package/dist/debug-B4kzIZPi.js.map +0 -1
  135. package/dist/env-api-key-BZ5wE3Y4.js.map +0 -1
  136. package/dist/environment-BEvsey6i.js.map +0 -1
  137. package/dist/errors-BoJvrdw8.js.map +0 -1
  138. package/dist/gateway-session-C118uACz.js +0 -2
  139. package/dist/gateway-session-xPFv6p5c.js.map +0 -1
  140. package/dist/headless-ui-CsmQrDIl.js.map +0 -1
  141. package/dist/local-dev-DPZiIPCK.js.map +0 -1
  142. package/dist/mcp-Cbuw_xss.js +0 -64
  143. package/dist/mcp-Cbuw_xss.js.map +0 -1
  144. package/dist/mcp-prompt-streaming-BU_kuur_.js.map +0 -1
  145. package/dist/mint-failure-BrleGqMd.js.map +0 -1
  146. package/dist/non-interactive-DcOjH2fw.js.map +0 -1
  147. package/dist/orchestrator-tools-DXwr8Sp3.js.map +0 -1
  148. package/dist/package-json-xt3eeyBI.js.map +0 -1
  149. package/dist/package-manager-DR23m5Jg.js +0 -290
  150. package/dist/package-manager-DR23m5Jg.js.map +0 -1
  151. package/dist/pi-mY8I2XkG.js.map +0 -1
  152. package/dist/playground-CDUUKLkC.js.map +0 -1
  153. package/dist/posthog-Hc_-9wrr.js.map +0 -1
  154. package/dist/provisioning-xfdjhFtA.js.map +0 -1
  155. package/dist/queue-tools-C8y_xZJi.js.map +0 -1
  156. package/dist/registry-DTmREJ-f.js.map +0 -1
  157. package/dist/security-Dw2oFJjP.js.map +0 -1
  158. package/dist/setup-utils-fjJTBhaB.js.map +0 -1
  159. package/dist/start-tui-UNe2xQ0U.js.map +0 -1
  160. package/dist/store-Bic4Cr3L.js.map +0 -1
  161. package/dist/subagent-Dz41vV9j.js.map +0 -1
  162. package/dist/task-5-fw_Vw3.js.map +0 -1
  163. package/dist/task-stream-dVzl6b_q.js.map +0 -1
  164. package/dist/tasks-D1c2z1aw.js.map +0 -1
  165. package/dist/telemetry-BRnxBRWK.js.map +0 -1
  166. package/dist/terminal-9bvRa4Qt.js.map +0 -1
  167. package/dist/tools-Dp6i9k70.js.map +0 -1
  168. package/dist/wizard-abort-CAcInAxg.js +0 -2
  169. package/dist/wizard-abort-lE6umflt.js.map +0 -1
  170. package/dist/wizard-session-CYvyYnti.js +0 -2
  171. package/dist/wizard-session-FSJCkTcP.js.map +0 -1
  172. package/dist/yara-hooks-BTNJ6MGW.js.map +0 -1
  173. package/dist/yara-hooks-CFY4pGcW.js +0 -2
@@ -1,13 +1,14 @@
1
1
  import { n as __require } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { c as RESUME_INSTRUCTION, l as classifyAuthFailure, o as AgentSignals, s as REMARK_INSTRUCTION } from "./errors-BoJvrdw8.js";
3
- import { A as CONTEXT_MILL_URL, Ct as runtimeEnv, O as AWS_SKILLS_BASE_URL, _t as wizardUserAgentForProgram, a as getLogFilePath, o as initLogFile, p as getUI, pt as WIZARD_REMARK_EVENT_NAME, r as debug, s as logToFile } from "./debug-B4kzIZPi.js";
4
- import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-FSJCkTcP.js";
5
- import { n as analytics } from "./analytics-C67F2vee.js";
6
- import { a as safeReadFile, i as readProjectFile, o as walkProjectFiles } from "./bounded-fs-CWRYuLAn.js";
7
- import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-lE6umflt.js";
8
- import { o as gatewayAuth, r as buildWizardPropertiesBlob, s as isPastRefresh } from "./gateway-session-xPFv6p5c.js";
9
- import { b as coerceAuditChecks, g as AUDIT_CHECKS_FILE, l as prewarmYaraScanner, n as createPostToolUseYaraHooks, p as scanInstalledSkill, r as createPreToolUseYaraHooks } from "./yara-hooks-BTNJ6MGW.js";
10
- import { C as makeMutex, S as triageModelFor, b as modelCapabilities, l as buildOrchestratorTools, w as writeJsonAtomic } from "./queue-tools-C8y_xZJi.js";
2
+ import { a as AgentSignals, c as classifyAuthFailure, o as REMARK_INSTRUCTION, s as RESUME_INSTRUCTION } from "./errors-CQ9kttJD.js";
3
+ import { a as getLogFilePath, o as initLogFile, r as debug, s as logToFile, v as runtimeEnv } from "./debug-Ch4BDOtD.js";
4
+ import { Y as WIZARD_REMARK_EVENT_NAME, c as CONTEXT_MILL_URL, et as wizardUserAgentForProgram, o as AWS_SKILLS_BASE_URL } from "./constants-jFI2SaE1.js";
5
+ import { n as analytics } from "./analytics-CHCZ_qUR.js";
6
+ import { a as safeReadFile, i as readProjectFile, o as walkProjectFiles } from "./bounded-fs-Bx9zTe6m.js";
7
+ import { t as registerCleanup } from "./wizard-abort-BBkiUxfS.js";
8
+ import { i as gatewayApiFor, l as gatewayAuth, n as buildGatewayModel, s as buildWizardPropertiesBlob, u as isPastRefresh } from "./gateway-CjoShdLa.js";
9
+ import { i as modelCapabilities, o as triageModelFor } from "./models-DDdBJKwT.js";
10
+ import { _ as coerceAuditChecks, h as AUDIT_CHECKS_FILE, l as prewarmYaraScanner, n as createPostToolUseYaraHooks, p as scanInstalledSkill, r as createPreToolUseYaraHooks } from "./yara-hooks-DCeMDd1o.js";
11
+ import { l as buildOrchestratorTools, v as makeMutex, y as writeJsonAtomic } from "./queue-tools-9OOVx5TE.js";
11
12
  import { createRequire } from "node:module";
12
13
  import * as fs$1 from "fs";
13
14
  import fs from "fs";
@@ -15,37 +16,60 @@ import path from "path";
15
16
  import path$1 from "node:path";
16
17
  import { spawnSync } from "node:child_process";
17
18
  import { closeSync, constants, fchmodSync, lstatSync, openSync, renameSync, unlinkSync, writeSync } from "node:fs";
18
- import { randomBytes } from "node:crypto";
19
+ import { randomBytes, randomUUID } from "node:crypto";
19
20
  import { z } from "zod";
20
21
  import * as os from "os";
21
22
  import { unzipSync } from "fflate";
22
- import { randomUUID } from "crypto";
23
- //#region src/lib/auth-session-state.ts
24
- /**
25
- * Whether this run's OAuth grant is known to be dead.
26
- *
27
- * Process-global on purpose. A run has exactly one login, but the two sides of
28
- * this fact are far apart: the pre-run refresh learns it in `authenticate.ts`,
29
- * and the only consumer is the 401 handler inside the agent message loop, which
30
- * holds no session. Threading it would mean a field on `AgentConfig` set at four
31
- * `initializeAgent` call sites — and the session it would read from may be a
32
- * shallow copy (`prepareRunSession`), so the value could be written to one
33
- * object and read from another.
34
- *
35
- * A leaf module with no imports, so either side can depend on it without a cycle.
36
- */
37
- let grantRevoked = false;
23
+ import { randomUUID as randomUUID$1 } from "crypto";
24
+ //#region src/agent/triage-provider.ts
25
+ const TRIAGE_MAX_TOKENS = 16384;
26
+ const TRIAGE_TIMEOUT_MS = 2e4;
38
27
  /**
39
- * Record that the token endpoint permanently refused this grant. Not an abort:
40
- * the access token in hand may still have minutes of life, so the run continues
41
- * and this only decides what a later 401 gets blamed on.
28
+ * Triage provider for a harness. Auth is always explicit: every caller already
29
+ * holds the gateway url and the run's token, and reading them back out of
30
+ * ANTHROPIC_* made an unauthed provider silent — it returned undefined, the
31
+ * caller failed closed, and a clean first-party skill got deleted. A resolver
32
+ * is re-read on every call, so a run that re-mints mid-way scans with the
33
+ * current bearer rather than the one it started with.
42
34
  */
43
- function markGrantRevoked() {
44
- grantRevoked = true;
45
- }
46
- /** True once a token refresh failed because the grant itself is gone. */
47
- function isGrantRevoked() {
48
- return grantRevoked;
35
+ function createTriageLLMProvider(auth, harness) {
36
+ const resolveAuth = typeof auth === "function" ? auth : () => Promise.resolve(auth);
37
+ const modelId = triageModelFor(harness);
38
+ const { reasoning, thinkingLevel } = modelCapabilities(modelId);
39
+ logToFile(`[YARA] triage provider ready (model: ${modelId}, api: ${gatewayApiFor(modelId)})`);
40
+ return async (prompt) => {
41
+ const { completeSimple } = await import("@earendil-works/pi-ai");
42
+ const current = await resolveAuth();
43
+ const authToken = current.authToken;
44
+ const model = buildGatewayModel({
45
+ gatewayUrl: current.baseURL,
46
+ accessToken: authToken,
47
+ teamId: current.teamId,
48
+ wizardMetadata: current.wizardMetadata ?? {},
49
+ wizardFlags: current.wizardFlags ?? {},
50
+ modelId
51
+ });
52
+ const controller = new AbortController();
53
+ const timer = setTimeout(() => controller.abort(), TRIAGE_TIMEOUT_MS);
54
+ try {
55
+ const message = await completeSimple(model, { messages: [{
56
+ role: "user",
57
+ content: prompt,
58
+ timestamp: Date.now()
59
+ }] }, {
60
+ apiKey: authToken,
61
+ temperature: model.api === "anthropic-messages" ? 0 : void 0,
62
+ maxTokens: TRIAGE_MAX_TOKENS,
63
+ reasoning: reasoning && thinkingLevel !== "off" ? thinkingLevel : void 0,
64
+ signal: controller.signal
65
+ });
66
+ const text = message.content.filter((c) => c.type === "text").map((c) => c.text).join("");
67
+ if (message.stopReason === "error") logToFile(`[YARA] triage call failed on ${model.id}: ${message.errorMessage ?? "unknown"} — every flagged match will be acted on`);
68
+ return text;
69
+ } finally {
70
+ clearTimeout(timer);
71
+ }
72
+ };
49
73
  }
50
74
  /** A scan retains at most this many distinct key names. */
51
75
  const MAX_ENV_KEY_SET = 5e3;
@@ -172,7 +196,7 @@ function collectProjectEnvKeys(rootDir) {
172
196
  return locations;
173
197
  }
174
198
  //#endregion
175
- //#region src/utils/custom-headers.ts
199
+ //#region src/shared/utils/custom-headers.ts
176
200
  /**
177
201
  * Builds a list of custom headers for ANTHROPIC_CUSTOM_HEADERS.
178
202
  */
@@ -192,7 +216,7 @@ function createCustomHeaders() {
192
216
  };
193
217
  }
194
218
  //#endregion
195
- //#region src/lib/safe-tools.ts
219
+ //#region src/agent/safe-tools.ts
196
220
  const LINTING_TOOLS = [
197
221
  "codespell",
198
222
  "cspell",
@@ -330,7 +354,7 @@ const LINTING_TOOLS = [
330
354
  "yamllint"
331
355
  ];
332
356
  //#endregion
333
- //#region src/lib/agent/bash-fence.ts
357
+ //#region src/agent/bash-fence.ts
334
358
  /**
335
359
  * Bash command fence: exact per-manager allowlists + shell-shape gates.
336
360
  *
@@ -688,7 +712,7 @@ function evaluateBashCommand(rawCommand) {
688
712
  return commandDecision(normalized);
689
713
  }
690
714
  //#endregion
691
- //#region src/lib/wizard-ask-bridge.ts
715
+ //#region src/agent/wizard-ask-bridge.ts
692
716
  /**
693
717
  * WizardAskBridge — host-side promise broker for the `wizard_ask` MCP tool.
694
718
  *
@@ -722,51 +746,78 @@ function isFullyCancelled(answers) {
722
746
  }
723
747
  function createWizardAskBridge(opts) {
724
748
  const timeoutMs = opts.timeoutMs ?? 3e5;
725
- return { async request({ questions, subject }) {
726
- const pending = {
727
- id: randomUUID(),
728
- questions,
729
- source: opts.getSource(),
730
- richLinks: opts.richLinks ?? false,
731
- askedAt: (/* @__PURE__ */ new Date()).toISOString()
732
- };
733
- const startedAt = Date.now();
734
- let timer;
735
- let timedOut = false;
736
- const timeoutPromise = new Promise((resolve) => {
737
- timer = setTimeout(() => {
738
- timedOut = true;
739
- opts.cancelQuestion?.();
740
- resolve(buildCancelledAnswers(questions));
741
- }, timeoutMs);
742
- });
743
- try {
744
- const answers = await Promise.race([opts.showQuestion(pending), timeoutPromise]);
745
- const durationMs = Date.now() - startedAt;
746
- if (isFullyCancelled(answers)) analytics.wizardCapture("wizard_ask cancelled", {
747
- source: pending.source,
748
- subject,
749
- question_count: questions.length,
750
- duration_ms: durationMs,
751
- timed_out: timedOut
749
+ const pendingQuestions = /* @__PURE__ */ new Map();
750
+ return {
751
+ getPendingQuestion: () => {
752
+ for (const pending of pendingQuestions.values()) return pending;
753
+ return null;
754
+ },
755
+ async request({ questions, subject }) {
756
+ if (opts.signal?.aborted) return {
757
+ answers: buildCancelledAnswers(questions),
758
+ timedOut: false
759
+ };
760
+ const pending = {
761
+ id: randomUUID$1(),
762
+ questions,
763
+ source: opts.getSource(),
764
+ richLinks: opts.richLinks ?? false,
765
+ askedAt: (/* @__PURE__ */ new Date()).toISOString()
766
+ };
767
+ pendingQuestions.set(pending.id, pending);
768
+ const startedAt = Date.now();
769
+ const controller = new AbortController();
770
+ let timer;
771
+ let timedOut = false;
772
+ let cancelForAbort;
773
+ const timeoutPromise = new Promise((resolve) => {
774
+ timer = setTimeout(() => {
775
+ timedOut = true;
776
+ resolve(buildCancelledAnswers(questions));
777
+ controller.abort();
778
+ }, timeoutMs);
752
779
  });
753
- else analytics.wizardCapture("wizard_ask answered", {
754
- source: pending.source,
755
- subject,
756
- question_count: questions.length,
757
- duration_ms: durationMs
780
+ const aborted = new Promise((resolve) => {
781
+ cancelForAbort = () => {
782
+ resolve(buildCancelledAnswers(questions));
783
+ controller.abort();
784
+ };
785
+ opts.signal?.addEventListener("abort", cancelForAbort, { once: true });
758
786
  });
759
- return {
760
- answers,
761
- timedOut
762
- };
763
- } finally {
764
- if (timer) clearTimeout(timer);
787
+ try {
788
+ const answers = await Promise.race([
789
+ opts.showQuestion(pending, { signal: controller.signal }),
790
+ timeoutPromise,
791
+ aborted
792
+ ]);
793
+ const durationMs = Date.now() - startedAt;
794
+ if (isFullyCancelled(answers)) analytics.wizardCapture("wizard_ask cancelled", {
795
+ source: pending.source,
796
+ subject,
797
+ question_count: questions.length,
798
+ duration_ms: durationMs,
799
+ timed_out: timedOut
800
+ });
801
+ else analytics.wizardCapture("wizard_ask answered", {
802
+ source: pending.source,
803
+ subject,
804
+ question_count: questions.length,
805
+ duration_ms: durationMs
806
+ });
807
+ return {
808
+ answers,
809
+ timedOut
810
+ };
811
+ } finally {
812
+ if (timer) clearTimeout(timer);
813
+ if (cancelForAbort) opts.signal?.removeEventListener("abort", cancelForAbort);
814
+ pendingQuestions.delete(pending.id);
815
+ }
765
816
  }
766
- } };
817
+ };
767
818
  }
768
819
  //#endregion
769
- //#region src/lib/fetch-retry.ts
820
+ //#region src/shared/fetch-retry.ts
770
821
  /**
771
822
  * One retry + failover policy for every critical-path fetch. GitHub is primary,
772
823
  * AWS is the fallback. A status a retry can't heal (404, 403) spends no retry
@@ -879,13 +930,12 @@ async function fetchWithRetry(url, opts = {}) {
879
930
  throw new Error(`fetch ${url} failed — ${failures.join(" | ")}`);
880
931
  }
881
932
  //#endregion
882
- //#region src/lib/wizard-tools/tools.ts
933
+ //#region src/shared/skill-menu.ts
883
934
  /**
884
- * Shared wizard-tool behavior — skill discovery/install, env-file helpers,
885
- * the wizard_ask cap policy, secret-vault plumbing, and the audit ledger.
886
- * One implementation consumed by both protocol facades: the MCP server
887
- * (`./mcp`) and the pi-native tools (`harness/pi/tools.ts`) — tool behavior
888
- * cannot drift between harnesses.
935
+ * The skill menu: what context-mill publishes as `skill-menu.json` and how the
936
+ * wizard reads it. Skill-backed CLI commands, the family picker, the skill
937
+ * source screen and the agent's install path all resolve skills through this
938
+ * one fetch, so it lives in shared and depends on nothing in the agent.
889
939
  */
890
940
  /** Expand a bundle entry into one entry per variant, so the menu reads the same whether a group ships bundled or as zips. */
891
941
  function expandBundleEntry(entry) {
@@ -915,6 +965,15 @@ async function fetchSkillMenu(skillsBaseUrl, opts = {}) {
915
965
  return null;
916
966
  }
917
967
  }
968
+ //#endregion
969
+ //#region src/agent/tools/tools.ts
970
+ /**
971
+ * Shared wizard-tool behavior — skill discovery/install, env-file helpers,
972
+ * the wizard_ask cap policy, secret-vault plumbing, and the audit ledger.
973
+ * One implementation consumed by both protocol facades: the MCP server
974
+ * (`./mcp`) and the pi-native tools (`harness/pi/tools.ts`) — tool behavior
975
+ * cannot drift between harnesses.
976
+ */
918
977
  /** Extract a zip buffer, refusing entries that escape destDir (zip-slip). */
919
978
  function extractZipArchive(zip, destDir) {
920
979
  const root = path.resolve(destDir);
@@ -1097,7 +1156,7 @@ const WIZARD_ASK_SUBJECT_DESCRIPTION = "Short, stable tag naming what this call
1097
1156
  * server in `./mcp` and the pi-native mirror in `harness/pi/tools.ts`) so the
1098
1157
  * batching and cancellation contract reads identically in both harnesses.
1099
1158
  */
1100
- const WIZARD_ASK_TOOL_DESCRIPTION = "Ask the user one or more structured questions and wait for their answers. Use this whenever you would otherwise inline a question in your text output. Batch every question about one subject into a single call (up to 8) rather than asking one at a time, and tag the call with `subject`. Walking a list — one call per data-warehouse source, one call per integration step — is expected and is never blocked, because the batching guard counts consecutive calls per subject. A fully cancelled or timed-out response does NOT count against the per-run cap, so nothing is wasted. When a field is not collected the result carries a `cancelled` object naming those question ids, whether the user dismissed the prompt or it timed out, and what to do next: read that instead of inspecting the answer values, and fall back gracefully (e.g. hand over a deep link) rather than re-asking.";
1159
+ const WIZARD_ASK_TOOL_DESCRIPTION = `Ask the user one or more structured questions and wait for their answers. Use this whenever you would otherwise inline a question in your text output. Batch every question about one subject into a single call (up to 12) rather than asking one at a time, and tag the call with \`subject\`. A subject needing more fields than one call carries — a connection form wider than the limit — is collected over consecutive calls reusing the same \`subject\`: that is expected, and is never a reason to abandon it for a link the user has to follow themselves. Walking a list — one call per data-warehouse source, one call per integration step — is expected and is never blocked, because the batching guard counts consecutive calls per subject. A fully cancelled or timed-out response does NOT count against the per-run cap, so nothing is wasted. When a field is not collected the result carries a \`cancelled\` object naming those question ids, whether the user dismissed the prompt or it timed out, and what to do next: read that instead of inspecting the answer values, and fall back gracefully (e.g. hand over a deep link) rather than re-asking.`;
1101
1160
  /**
1102
1161
  * Guidance returned with a `wizard_ask` result when the user dismissed the
1103
1162
  * prompt. Shared by both harness facades, like the descriptions above.
@@ -1176,7 +1235,7 @@ function evaluateAskCap({ callCount, maxQuestions, subject, subjectRunLength, ad
1176
1235
  reason: "adjacency",
1177
1236
  subject,
1178
1237
  subjectRunLength,
1179
- message: `Not an error — this ask was not sent (a one-time nudge). You have sent ${subjectRunLength} wizard_ask calls in a row about the same subject (${subject === "(unspecified)" ? "they all declared no `subject`, so they count as one subject" : `they all used subject "${subject}"`}). Batch every question you still need for that subject into one call (up to 8 questions) and send wizard_ask again now. If your next questions are about something else — another data-warehouse source, another integration step — set a different \`subject\` on the call. Adjacency is counted per subject, so one call per source is never blocked, and you must not try to squeeze several sources into one 8-question call. Either way the next call is sent. Do not abandon the task, and do not fall back to browser setup because of this message.`
1238
+ message: `Not an error — this ask was not sent (a one-time nudge). You have sent ${subjectRunLength} wizard_ask calls in a row about the same subject (${subject === "(unspecified)" ? "they all declared no `subject`, so they count as one subject" : `they all used subject "${subject}"`}). Batch every question you still need for that subject into one call (up to 12 questions) and send wizard_ask again now. If your next questions are about something else — another data-warehouse source, another integration step — set a different \`subject\` on the call. Adjacency is counted per subject, so one call per source is never blocked, and you must not try to squeeze several sources into one 12-question call. Either way the next call is sent. Do not abandon the task, and do not fall back to browser setup because of this message.`
1180
1239
  };
1181
1240
  return { kind: "ok" };
1182
1241
  }
@@ -1567,11 +1626,7 @@ const WIZARD_TOOL_NAMES = {
1567
1626
  readHandoffs: `mcp__${SERVER_NAME}__read_handoffs`
1568
1627
  };
1569
1628
  //#endregion
1570
- //#region src/lib/wizard-tools/handoff.ts
1571
- /**
1572
- * publish_handoff — the agent publishes the run's handoff doc (the report
1573
- * markdown) in one explicit call, replacing the report file + watcher path.
1574
- */
1629
+ //#region src/agent/tools/handoff.ts
1575
1630
  const MAX_HANDOFF_TEXT_CHARS = 64 * 1024;
1576
1631
  const PUBLISH_HANDOFF_TOOL_NAME = "publish_handoff";
1577
1632
  /** Shared between the MCP server and the pi facade so the contract can't drift. */
@@ -1637,7 +1692,7 @@ function writeHandoffFileAtomically(targetPath, text) {
1637
1692
  };
1638
1693
  }
1639
1694
  }
1640
- function publishHandoff(content) {
1695
+ function publishHandoff(content, emit) {
1641
1696
  if (content.trim() === "") {
1642
1697
  analytics.wizardCapture("handoff published", {
1643
1698
  handoff_ok: false,
@@ -1650,7 +1705,10 @@ function publishHandoff(content) {
1650
1705
  }
1651
1706
  const truncated = content.length > MAX_HANDOFF_TEXT_CHARS;
1652
1707
  const text = truncated ? content.slice(0, MAX_HANDOFF_TEXT_CHARS) : content;
1653
- getUI().setHandoffText(text);
1708
+ emit?.({
1709
+ kind: "handoff",
1710
+ text
1711
+ });
1654
1712
  const handoffOutputPath = runtimeEnv("POSTHOG_HANDOFF_OUTPUT_PATH");
1655
1713
  let handoffOutputWritten;
1656
1714
  let handoffOutputError;
@@ -1677,7 +1735,7 @@ function publishHandoff(content) {
1677
1735
  };
1678
1736
  }
1679
1737
  //#endregion
1680
- //#region src/lib/secret-vault.ts
1738
+ //#region src/shared/secret-vault.ts
1681
1739
  /**
1682
1740
  * Session-scoped secret vault.
1683
1741
  *
@@ -1697,7 +1755,7 @@ function createSecretVault() {
1697
1755
  const store = /* @__PURE__ */ new Map();
1698
1756
  return {
1699
1757
  put(value, meta) {
1700
- const ref = `${REF_PREFIX}${randomUUID()}`;
1758
+ const ref = `${REF_PREFIX}${randomUUID$1()}`;
1701
1759
  store.set(ref, {
1702
1760
  value,
1703
1761
  meta: {
@@ -1724,7 +1782,7 @@ function createSecretVault() {
1724
1782
  };
1725
1783
  }
1726
1784
  //#endregion
1727
- //#region src/lib/wizard-tools/mcp.ts
1785
+ //#region src/agent/tools/mcp.ts
1728
1786
  /**
1729
1787
  * MCP facade over `./tools` — the unified in-process server the anthropic
1730
1788
  * harness mounts. Declarations (zod schemas, descriptions) and the MCP result
@@ -1759,7 +1817,7 @@ async function getSDKModule$1() {
1759
1817
  * Must be called asynchronously because the SDK is an ESM module loaded via dynamic import.
1760
1818
  */
1761
1819
  async function createWizardToolsServer(options) {
1762
- const { workingDirectory, detectPackageManager, skillsBaseUrl, askBridge, askMaxQuestions = 10, secretVault = createSecretVault(), orchestrator, triageProvider } = options;
1820
+ const { workingDirectory, detectPackageManager, skillsBaseUrl, askBridge, askMaxQuestions = 10, secretVault = createSecretVault(), orchestrator, triageProvider, emit } = options;
1763
1821
  const { tool, createSdkMcpServer } = await getSDKModule$1();
1764
1822
  const askAccounting = createAskAccounting(askMaxQuestions);
1765
1823
  let cachedSkillMenu = {};
@@ -1961,7 +2019,7 @@ async function createWizardToolsServer(options) {
1961
2019
  auditAddChecks,
1962
2020
  auditResolveChecks,
1963
2021
  tool("wizard_ask", WIZARD_ASK_TOOL_DESCRIPTION, {
1964
- questions: z.array(askQuestionSchema).min(1).max(8),
2022
+ questions: z.array(askQuestionSchema).min(1).max(12),
1965
2023
  subject: z.string().optional().describe(WIZARD_ASK_SUBJECT_DESCRIPTION)
1966
2024
  }, async (args) => {
1967
2025
  if (!askBridge) return {
@@ -2046,7 +2104,7 @@ async function createWizardToolsServer(options) {
2046
2104
  }
2047
2105
  }),
2048
2106
  tool(PUBLISH_HANDOFF_TOOL_NAME, PUBLISH_HANDOFF_DESCRIPTION, { content: z.string().describe(PUBLISH_HANDOFF_CONTENT_DESCRIPTION) }, (args) => {
2049
- const result = publishHandoff(args.content);
2107
+ const result = publishHandoff(args.content, emit);
2050
2108
  logToFile(`publish_handoff: ${result.message}`);
2051
2109
  return {
2052
2110
  content: [{
@@ -2061,197 +2119,7 @@ async function createWizardToolsServer(options) {
2061
2119
  });
2062
2120
  }
2063
2121
  //#endregion
2064
- //#region src/lib/agent/runner/harness/pi/gateway.ts
2065
- /**
2066
- * PostHog AI gateway provider spec for pi sessions, shared by the linear run
2067
- * and the orchestrator's per-task runs so both speak to the gateway
2068
- * identically: bearer auth, the wizard properties header, transport shape
2069
- * inferred from the model id. The caller registers the spec on its own
2070
- * (lazily imported, properly typed) pi ModelRegistry.
2071
- */
2072
- /** Provider registered on the in-memory registry for this run. */
2073
- const GATEWAY_PROVIDER = "posthog-gateway";
2074
- /**
2075
- * The gateway speaks two shapes on two endpoints: Anthropic models over
2076
- * `anthropic-messages` (the SDK appends `/v1/messages`, so the base URL has no
2077
- * `/v1`), and OpenAI-class models (`openai/gpt-5`, …) over an OpenAI shape at
2078
- * `/v1/responses` or `/v1/chat/completions` (base URL keeps `/v1`). Infer the
2079
- * shape from the model id so a pair's model selects the right transport.
2080
- *
2081
- * OpenAI models take the Responses API because OpenAI rejects function tools
2082
- * combined with `reasoning_effort` on chat completions and every task sends both.
2083
- */
2084
- function gatewayApiFor(modelId) {
2085
- return modelId.startsWith("openai/") ? "openai-responses" : "anthropic-messages";
2086
- }
2087
- /**
2088
- * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: one
2089
- * `X-PostHog-Properties` JSON blob (Bedrock fallback is native, so no opt-in)
2090
- * plus the 1M context beta, since pi otherwise runs at 200k and overflows on
2091
- * larger projects (the post-run compaction failures).
2092
- */
2093
- function buildGatewayHeaders(wizardMetadata, wizardFlags, teamId) {
2094
- return {
2095
- "anthropic-beta": "context-1m-2025-08-07",
2096
- "X-PostHog-Properties": buildWizardPropertiesBlob(wizardMetadata, wizardFlags, teamId)
2097
- };
2098
- }
2099
- /**
2100
- * One gateway model spec — the single description of how to reach a model on
2101
- * the gateway. The provider spec below wraps it for pi's registry; one-shot
2102
- * callers (scan triage) hand it straight to `completeSimple`.
2103
- */
2104
- function buildGatewayModel(inputs) {
2105
- const { gatewayUrl, wizardMetadata, wizardFlags, modelId, teamId } = inputs;
2106
- const api = gatewayApiFor(modelId);
2107
- return {
2108
- id: modelId,
2109
- name: `${modelId} (PostHog Gateway)`,
2110
- api,
2111
- provider: GATEWAY_PROVIDER,
2112
- baseUrl: api === "anthropic-messages" ? gatewayUrl : `${gatewayUrl}/v1`,
2113
- reasoning: modelCapabilities(modelId).reasoning,
2114
- input: ["text"],
2115
- cost: {
2116
- input: 0,
2117
- output: 0,
2118
- cacheRead: 0,
2119
- cacheWrite: 0
2120
- },
2121
- contextWindow: 1e6,
2122
- maxTokens: 64e3,
2123
- headers: buildGatewayHeaders(wizardMetadata, wizardFlags, teamId)
2124
- };
2125
- }
2126
- /**
2127
- * The provider object for `registry.registerProvider(GATEWAY_PROVIDER, …)`,
2128
- * plus the derived traits the session setup needs (`caps.thinkingLevel`).
2129
- */
2130
- function buildGatewayProvider(inputs) {
2131
- const { gatewayUrl, accessToken, modelId, effort } = inputs;
2132
- const api = gatewayApiFor(modelId);
2133
- const tableCaps = modelCapabilities(modelId);
2134
- const caps = effort && effort !== "off" && tableCaps.reasoning ? modelCapabilities(modelId, effort) : effort === "off" && tableCaps.reasoning ? {
2135
- ...tableCaps,
2136
- thinkingLevel: "off"
2137
- } : tableCaps;
2138
- const model = buildGatewayModel(inputs);
2139
- return {
2140
- provider: {
2141
- name: "PostHog Gateway",
2142
- baseUrl: model.baseUrl,
2143
- apiKey: accessToken,
2144
- authHeader: true,
2145
- api,
2146
- headers: model.headers,
2147
- models: [model]
2148
- },
2149
- api,
2150
- caps,
2151
- gatewayUrl,
2152
- baseUrl: model.baseUrl
2153
- };
2154
- }
2155
- /**
2156
- * Whether a turn's error is the gateway rejecting the bearer. pi attaches the
2157
- * SDK's own error to `diagnostics`, so its code decides when one is present.
2158
- * The message match is the fallback for a turn that failed before pi built a
2159
- * diagnostic, where the status survives only as prose.
2160
- */
2161
- function isGatewayAuthRejection(turn) {
2162
- const { errorMessage, diagnostics } = typeof turn === "string" ? {
2163
- errorMessage: turn,
2164
- diagnostics: void 0
2165
- } : turn ?? {};
2166
- for (const diagnostic of diagnostics ?? []) {
2167
- const code = diagnostic.error?.code;
2168
- if (code === 401 || code === "401") return true;
2169
- if (/^authentication_?error$/i.test(diagnostic.error?.name ?? "")) return true;
2170
- }
2171
- return /\b401\b|authentication_error|unauthorized/i.test(errorMessage ?? "");
2172
- }
2173
- /**
2174
- * Wraps a pi session's prompt(): when a turn ends on a 401 from a bearer past
2175
- * its refresh instant, mint once, re-register the provider with the new
2176
- * bearer (pi resolves the apiKey per request), and continue. A 401 on a fresh
2177
- * bearer, or a second one, is left to the harness's normal failure path.
2178
- */
2179
- function withGatewayRemint(opts) {
2180
- let auth = opts.auth;
2181
- let rejected = false;
2182
- let reminted = false;
2183
- return {
2184
- noteAssistantTurn(message) {
2185
- const turn = message;
2186
- rejected = turn?.stopReason === "error" && isGatewayAuthRejection(turn);
2187
- },
2188
- async prompt(text) {
2189
- rejected = false;
2190
- await opts.session.prompt(text);
2191
- if (!rejected || reminted || !isPastRefresh(auth)) return;
2192
- reminted = true;
2193
- auth = await opts.refreshAuth();
2194
- opts.registry.registerProvider(GATEWAY_PROVIDER, buildGatewayProvider(opts.providerInputs(auth)).provider);
2195
- opts.onRemint?.();
2196
- rejected = false;
2197
- const next = opts.continueText;
2198
- await opts.session.prompt(typeof next === "function" ? next() : next);
2199
- }
2200
- };
2201
- }
2202
- //#endregion
2203
- //#region src/lib/agent/triage-provider.ts
2204
- const TRIAGE_MAX_TOKENS = 16384;
2205
- const TRIAGE_TIMEOUT_MS = 2e4;
2206
- /**
2207
- * Triage provider for a harness. Auth is always explicit: every caller already
2208
- * holds the gateway url and the run's token, and reading them back out of
2209
- * ANTHROPIC_* made an unauthed provider silent — it returned undefined, the
2210
- * caller failed closed, and a clean first-party skill got deleted. A resolver
2211
- * is re-read on every call, so a run that re-mints mid-way scans with the
2212
- * current bearer rather than the one it started with.
2213
- */
2214
- function createTriageLLMProvider(auth, harness) {
2215
- const resolveAuth = typeof auth === "function" ? auth : () => Promise.resolve(auth);
2216
- const modelId = triageModelFor(harness);
2217
- const { reasoning, thinkingLevel } = modelCapabilities(modelId);
2218
- logToFile(`[YARA] triage provider ready (model: ${modelId}, api: ${gatewayApiFor(modelId)})`);
2219
- return async (prompt) => {
2220
- const { completeSimple } = await import("@earendil-works/pi-ai");
2221
- const current = await resolveAuth();
2222
- const authToken = current.authToken;
2223
- const model = buildGatewayModel({
2224
- gatewayUrl: current.baseURL,
2225
- accessToken: authToken,
2226
- teamId: current.teamId,
2227
- wizardMetadata: current.wizardMetadata ?? {},
2228
- wizardFlags: current.wizardFlags ?? {},
2229
- modelId
2230
- });
2231
- const controller = new AbortController();
2232
- const timer = setTimeout(() => controller.abort(), TRIAGE_TIMEOUT_MS);
2233
- try {
2234
- const message = await completeSimple(model, { messages: [{
2235
- role: "user",
2236
- content: prompt,
2237
- timestamp: Date.now()
2238
- }] }, {
2239
- apiKey: authToken,
2240
- temperature: model.api === "anthropic-messages" ? 0 : void 0,
2241
- maxTokens: TRIAGE_MAX_TOKENS,
2242
- reasoning: reasoning && thinkingLevel !== "off" ? thinkingLevel : void 0,
2243
- signal: controller.signal
2244
- });
2245
- const text = message.content.filter((c) => c.type === "text").map((c) => c.text).join("");
2246
- if (message.stopReason === "error") logToFile(`[YARA] triage call failed on ${model.id}: ${message.errorMessage ?? "unknown"} — every flagged match will be acted on`);
2247
- return text;
2248
- } finally {
2249
- clearTimeout(timer);
2250
- }
2251
- };
2252
- }
2253
- //#endregion
2254
- //#region src/lib/agent/commandments.ts
2122
+ //#region src/agent/commandments.ts
2255
2123
  /**
2256
2124
  * Commandments true for every run, whatever program, sequence, harness, or model
2257
2125
  * it resolves to. Anything narrower belongs to its axis in
@@ -2284,7 +2152,7 @@ const WIZARD_COMMANDMENTS = [
2284
2152
  ].join("\n")
2285
2153
  ];
2286
2154
  //#endregion
2287
- //#region src/lib/agent/runner/harness/pi/runtime-notes.ts
2155
+ //#region src/agent/runner/harness/pi/runtime-notes.ts
2288
2156
  const HEADER = ["## This runtime", "Below are important guidance on the harness constraints you are bound to. Follow them as commandments."];
2289
2157
  const PARALLEL_CALLS = "- When you need several INDEPENDENT operations — reading or searching multiple files, creating several insights — issue them as multiple tool calls in a SINGLE turn. They run in parallel and save round-trips; doing them one-per-turn is much slower. Only sequence calls when one needs a previous call’s output.";
2290
2158
  const EXPLORE = "- Explore with the `ls`, `find`, and `grep` tools (list a directory, find files by name, search file contents). `read` is for FILES only — reading a directory errors. NEVER inspect files through `bash`; `ls`, `find`, `cat`, `sed`, `head`, `xxd`, `python -c` and the like are all blocked. To see the exact bytes of a file (e.g. whitespace before a precise `edit`), use `read`.";
@@ -2342,7 +2210,7 @@ function piRuntimeNotes(sequence, caps) {
2342
2210
  return notes.join("\n");
2343
2211
  }
2344
2212
  //#endregion
2345
- //#region src/lib/agent/runner/switchboard/commandments.ts
2213
+ //#region src/agent/runner/switchboard/commandments.ts
2346
2214
  /**
2347
2215
  * LINEAR only. One session drives the whole run and holds the Task tools; an
2348
2216
  * orchestrator task session holds none of them and reports through
@@ -2389,7 +2257,7 @@ function assembleCommandments(axes) {
2389
2257
  ].filter(Boolean).join("\n");
2390
2258
  }
2391
2259
  //#endregion
2392
- //#region src/lib/agent/agent-phase.ts
2260
+ //#region src/shared/agent-phase.ts
2393
2261
  /** Maps a Claude SDK tool name to the phase that tool implies. Returns null
2394
2262
  * when the tool doesn't drive the visualizer (Task*, TodoWrite, etc.). */
2395
2263
  function classifyToolToStage(toolName) {
@@ -2402,7 +2270,35 @@ function classifyToolToStage(toolName) {
2402
2270
  return null;
2403
2271
  }
2404
2272
  //#endregion
2405
- //#region src/lib/agent/output-signals.ts
2273
+ //#region src/shared/auth-session-state.ts
2274
+ /**
2275
+ * Whether this run's OAuth grant is known to be dead.
2276
+ *
2277
+ * Process-global on purpose. A run has exactly one login, but the two sides of
2278
+ * this fact are far apart: the pre-run refresh learns it in `authenticate.ts`,
2279
+ * and the only consumer is the 401 handler inside the agent message loop, which
2280
+ * holds no session. Threading it would mean a field on `AgentConfig` set at four
2281
+ * `initializeAgent` call sites — and the session it would read from may be a
2282
+ * shallow copy (`prepareRunSession`), so the value could be written to one
2283
+ * object and read from another.
2284
+ *
2285
+ * A leaf module with no imports, so either side can depend on it without a cycle.
2286
+ */
2287
+ let grantRevoked = false;
2288
+ /**
2289
+ * Record that the token endpoint permanently refused this grant. Not an abort:
2290
+ * the access token in hand may still have minutes of life, so the run continues
2291
+ * and this only decides what a later 401 gets blamed on.
2292
+ */
2293
+ function markGrantRevoked() {
2294
+ grantRevoked = true;
2295
+ }
2296
+ /** True once a token refresh failed because the grant itself is gone. */
2297
+ function isGrantRevoked() {
2298
+ return grantRevoked;
2299
+ }
2300
+ //#endregion
2301
+ //#region src/agent/output-signals.ts
2406
2302
  /**
2407
2303
  * Parses the signal-bearing lines out of agent output and discards the rest.
2408
2304
  *
@@ -2509,7 +2405,7 @@ var AgentOutputSignals = class {
2509
2405
  }
2510
2406
  };
2511
2407
  //#endregion
2512
- //#region src/lib/agent/agent-env-isolation.ts
2408
+ //#region src/agent/agent-env-isolation.ts
2513
2409
  /**
2514
2410
  * Credential isolation for the Claude Agent SDK subprocess.
2515
2411
  *
@@ -2573,6 +2469,7 @@ const BLOCKED_OFF_NAMESPACE_KEYS = new Set(["AWS_BEARER_TOKEN_BEDROCK"]);
2573
2469
  const HOST_ONLY_ENV_KEYS = new Set([
2574
2470
  "POSTHOG_HANDOFF_OUTPUT_PATH",
2575
2471
  "POSTHOG_TASK_RUN_ID",
2472
+ "POSTHOG_WIZARD_RUN_ID",
2576
2473
  "POSTHOG_TASK_ID"
2577
2474
  ]);
2578
2475
  /**
@@ -2658,7 +2555,7 @@ function sanitizeAgentSubprocessEnv(env = process.env) {
2658
2555
  return sanitized;
2659
2556
  }
2660
2557
  //#endregion
2661
- //#region src/lib/agent/claude-settings.ts
2558
+ //#region src/shared/claude-settings.ts
2662
2559
  /**
2663
2560
  * Claude Code settings handling: conflict detection, backup/restore, and
2664
2561
  * orphan recovery.
@@ -2889,7 +2786,7 @@ function recoverOrphanedSettingsBackups(workingDirectory) {
2889
2786
  }
2890
2787
  }
2891
2788
  //#endregion
2892
- //#region src/lib/agent/stored-login.ts
2789
+ //#region src/agent/stored-login.ts
2893
2790
  /**
2894
2791
  * Detect a pre-existing Claude Code login on the machine.
2895
2792
  *
@@ -2969,7 +2866,7 @@ function detectStoredClaudeLogin(homeDir = os.homedir(), platform = process.plat
2969
2866
  };
2970
2867
  }
2971
2868
  //#endregion
2972
- //#region src/lib/agent/agent-interface.ts
2869
+ //#region src/agent/agent-interface.ts
2973
2870
  /**
2974
2871
  * Shared agent interface for PostHog wizards
2975
2872
  * Uses Claude Agent SDK directly with PostHog LLM gateway
@@ -3018,37 +2915,23 @@ function buildAuthErrorContext(workingDirectory, gatewayUrl, homeDir = os.homedi
3018
2915
  };
3019
2916
  }
3020
2917
  /**
3021
- * Create a stop hook callback that drains the additional feature queue,
3022
- * then collects a remark, then allows stop.
2918
+ * Create a stop hook callback that collects a remark, then allows stop.
3023
2919
  *
3024
- * Three-phase logic using closure state:
3025
- * Phase 1 — drain queue: block with each feature prompt in order
3026
- * Phase 2 — collect remark (once): block with remark prompt
3027
- * Phase 3 — allow stop: return {}
2920
+ * Two-phase logic using closure state:
2921
+ * Phase 1 — collect remark (once): block with remark prompt
2922
+ * Phase 2 — allow stop: return {}
3028
2923
  */
3029
- function createStopHook(featureQueue, signals, requestRemark = true) {
3030
- let featureIndex = 0;
2924
+ function createStopHook(signals, requestRemark = true) {
3031
2925
  let remarkRequested = false;
3032
2926
  return (input) => {
3033
2927
  logToFile("Stop hook triggered", {
3034
2928
  stop_hook_active: input.stop_hook_active,
3035
- featureIndex,
3036
- remarkRequested,
3037
- queueLength: featureQueue.length
2929
+ remarkRequested
3038
2930
  });
3039
2931
  if (signals?.hasApiError()) {
3040
2932
  logToFile("Stop hook: API error detected, allowing immediate stop");
3041
2933
  return {};
3042
2934
  }
3043
- if (featureIndex < featureQueue.length) {
3044
- const feature = featureQueue[featureIndex++];
3045
- const prompt = ADDITIONAL_FEATURE_PROMPTS[feature];
3046
- logToFile(`Stop hook: injecting feature prompt for ${feature}`);
3047
- return {
3048
- decision: "block",
3049
- reason: prompt
3050
- };
3051
- }
3052
2935
  if (requestRemark && !remarkRequested) {
3053
2936
  remarkRequested = true;
3054
2937
  logToFile("Stop hook: requesting reflection");
@@ -3061,6 +2944,7 @@ function createStopHook(featureQueue, signals, requestRemark = true) {
3061
2944
  return {};
3062
2945
  };
3063
2946
  }
2947
+ const NO_PROGRESS = () => void 0;
3064
2948
  /**
3065
2949
  * Global identifiers attached to every LLM gateway trace for a run. They ride on
3066
2950
  * each `$ai_generation` the gateway emits (in the `X-PostHog-Properties` blob
@@ -3188,9 +3072,10 @@ async function initializeAgent(config, options) {
3188
3072
  initLogFile();
3189
3073
  logToFile("Agent initialization starting");
3190
3074
  logToFile("Install directory:", options.installDir);
3075
+ const emit = config.emit ?? NO_PROGRESS;
3191
3076
  try {
3192
3077
  process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "true";
3193
- const currentGatewayAuth = () => gatewayAuth(config.host, config.posthogApiKey, config.programId);
3078
+ const currentGatewayAuth = async () => gatewayAuth(config.host, await config.currentPosthogApiKey?.() ?? config.posthogApiKey, config.programId);
3194
3079
  const auth = await currentGatewayAuth();
3195
3080
  const gatewayUrl = auth.gatewayUrl;
3196
3081
  process.env.ANTHROPIC_BASE_URL = gatewayUrl;
@@ -3244,7 +3129,8 @@ async function initializeAgent(config, options) {
3244
3129
  askBridge: config.askBridge,
3245
3130
  askMaxQuestions: config.askMaxQuestions,
3246
3131
  orchestrator: config.orchestrator,
3247
- triageProvider
3132
+ triageProvider,
3133
+ emit: config.emit
3248
3134
  });
3249
3135
  const model = config.modelOverride ?? "claude-sonnet-5";
3250
3136
  const agentRunConfig = {
@@ -3252,6 +3138,7 @@ async function initializeAgent(config, options) {
3252
3138
  mcpServers,
3253
3139
  model,
3254
3140
  posthogApiKey: config.posthogApiKey,
3141
+ currentPosthogApiKey: config.currentPosthogApiKey,
3255
3142
  wizardFlags: config.wizardFlags,
3256
3143
  wizardMetadata: config.wizardMetadata,
3257
3144
  allowedTools: config.allowedTools,
@@ -3263,7 +3150,8 @@ async function initializeAgent(config, options) {
3263
3150
  gatewayAuth: auth,
3264
3151
  refreshGatewayAuth: currentGatewayAuth,
3265
3152
  program: config.integrationLabel,
3266
- sequence: config.orchestrator ? "orchestrator" : "linear"
3153
+ sequence: config.orchestrator ? "orchestrator" : "linear",
3154
+ emit
3267
3155
  };
3268
3156
  logToFile("Agent config:", {
3269
3157
  workingDirectory: agentRunConfig.workingDirectory,
@@ -3280,7 +3168,11 @@ async function initializeAgent(config, options) {
3280
3168
  prewarmYaraScanner();
3281
3169
  return agentRunConfig;
3282
3170
  } catch (error) {
3283
- getUI().log.error(`Failed to initialize agent: ${error.message}`);
3171
+ emit({
3172
+ kind: "log",
3173
+ level: "error",
3174
+ message: `Failed to initialize agent: ${error.message}`
3175
+ });
3284
3176
  logToFile("Agent initialization error:", error);
3285
3177
  debug("Agent initialization error:", error);
3286
3178
  throw error;
@@ -3292,7 +3184,34 @@ async function initializeAgent(config, options) {
3292
3184
  *
3293
3185
  * @returns An object containing any error detected in the agent's output
3294
3186
  */
3187
+ function sdkErrorStatus(value) {
3188
+ if (typeof value === "number" && value >= 400 && value < 600) return value;
3189
+ if (typeof value === "string") {
3190
+ const match = value.match(/(?:^|\b)(4\d\d|5\d\d)(?:\b|$)/);
3191
+ return match ? Number(match[1]) : void 0;
3192
+ }
3193
+ if (value && typeof value === "object") {
3194
+ const record = value;
3195
+ return sdkErrorStatus(record.status) ?? sdkErrorStatus(record.statusCode) ?? sdkErrorStatus(record.code) ?? sdkErrorStatus(record.error);
3196
+ }
3197
+ }
3198
+ function sdkResultFailure(message) {
3199
+ if (message.subtype === "success" && message.is_error !== true) return void 0;
3200
+ const errors = Array.isArray(message.errors) ? message.errors : [];
3201
+ const detail = errors.map((error) => typeof error === "string" ? error : JSON.stringify(error)).join("; ") || (typeof message.result === "string" ? message.result : String(message.subtype ?? "SDK result failed"));
3202
+ return {
3203
+ kind: "failure",
3204
+ classification: (sdkErrorStatus(message.api_error_status) ?? sdkErrorStatus(message.status) ?? errors.map(sdkErrorStatus).find((code) => code !== void 0) ?? sdkErrorStatus(message.result)) === 429 ? "WIZARD_RATE_LIMIT" : "WIZARD_API_ERROR",
3205
+ message: detail
3206
+ };
3207
+ }
3295
3208
  async function runAgent(agentConfig, prompt, options, spinner, config, middleware) {
3209
+ if (agentConfig.signal?.aborted) return {
3210
+ kind: "abort",
3211
+ classification: "WIZARD_ABORT",
3212
+ message: "Agent run cancelled"
3213
+ };
3214
+ const emit = agentConfig.emit ?? NO_PROGRESS;
3296
3215
  const { spinnerMessage = "Customizing your PostHog setup...", successMessage = "PostHog integration complete", errorMessage = "Integration failed", abortCases = [], emitStepEvents = false, resolveStepKey } = config ?? {};
3297
3216
  logToFile("Starting agent run");
3298
3217
  const { query } = await getSDKModule();
@@ -3306,6 +3225,8 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3306
3225
  let receivedSuccessResult = false;
3307
3226
  let loggedInitialContext = false;
3308
3227
  let lastResultMessage = null;
3228
+ let terminalFailure;
3229
+ let assistantErrorStatus;
3309
3230
  const tasks = /* @__PURE__ */ new Map();
3310
3231
  let signalDone = () => void 0;
3311
3232
  const createPromptStream = (text) => {
@@ -3350,23 +3271,33 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3350
3271
  ...config?.analyticsProperties
3351
3272
  });
3352
3273
  const totalCostUsd = Number(lastResultMessage?.total_cost_usd ?? 0);
3353
- if (totalCostUsd > 0) getUI().setFinalTokenCostUsd(totalCostUsd);
3274
+ if (totalCostUsd > 0) emit({
3275
+ kind: "finalCost",
3276
+ usd: totalCostUsd
3277
+ });
3354
3278
  try {
3355
3279
  middleware?.finalize(lastResultMessage, durationMs);
3356
3280
  } catch (e) {
3357
3281
  logToFile(`${AgentSignals.BENCHMARK} Middleware finalize error:`, e);
3358
3282
  }
3359
3283
  spinner.stop(successMessage);
3360
- return {};
3284
+ return { kind: "success" };
3361
3285
  };
3362
3286
  let abortController = new AbortController();
3287
+ const onExternalAbort = () => {
3288
+ abortController.abort();
3289
+ signalDone();
3290
+ };
3363
3291
  let timedOut = false;
3364
3292
  let abortReason = null;
3365
3293
  let yaraViolationReason = null;
3366
3294
  let sessionId;
3367
3295
  let reminted = false;
3368
3296
  let remintRequested = false;
3297
+ let authFailure;
3369
3298
  const agentConfigDir = createIsolatedAgentConfigDir();
3299
+ agentConfig.signal?.addEventListener("abort", onExternalAbort, { once: true });
3300
+ if (agentConfig.signal?.aborted) onExternalAbort();
3370
3301
  const timeoutMs = config?.timeoutMs;
3371
3302
  const timeoutId = timeoutMs ? setTimeout(() => {
3372
3303
  if (receivedSuccessResult) return;
@@ -3457,7 +3388,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3457
3388
  CLAUDE_CODE_OAUTH_TOKEN: agentConfig.gatewayAuth.token,
3458
3389
  CLAUDE_CONFIG_DIR: agentConfigDir,
3459
3390
  CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "true",
3460
- POSTHOG_MCP_TOKEN: agentConfig.posthogApiKey,
3391
+ POSTHOG_MCP_TOKEN: await agentConfig.currentPosthogApiKey?.() ?? agentConfig.posthogApiKey,
3461
3392
  MCP_CONNECTION_NONBLOCKING: "0",
3462
3393
  ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(agentConfig.wizardMetadata ?? {}, agentConfig.wizardFlags ?? {}, agentConfig.gatewayAuth.teamId)
3463
3394
  },
@@ -3494,7 +3425,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3494
3425
  PreToolUse: warlockDisabled ? [] : createPreToolUseYaraHooks(triageProvider, onYaraTerminate),
3495
3426
  PostToolUse: warlockDisabled ? [] : createPostToolUseYaraHooks(triageProvider, onYaraTerminate),
3496
3427
  Stop: [{
3497
- hooks: [createStopHook(config?.additionalFeatureQueue ?? [], signals, config?.requestRemark ?? true)],
3428
+ hooks: [createStopHook(signals, config?.requestRemark ?? true)],
3498
3429
  timeout: 30
3499
3430
  }]
3500
3431
  }
@@ -3521,7 +3452,8 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3521
3452
  loggedInitialContext = true;
3522
3453
  }
3523
3454
  agentConfig.capture?.captureFromAnthropicSDKMessage(message);
3524
- handleSDKMessage(message, options, spinner, signals, receivedSuccessResult, tasks, agentConfig.suppressTaskRender ?? false, emitStepEvents, resolveStepKey);
3455
+ handleSDKMessage(message, options, spinner, signals, emit, receivedSuccessResult, tasks, agentConfig.suppressTaskRender ?? false, emitStepEvents, resolveStepKey);
3456
+ if (message.type === "assistant") assistantErrorStatus = sdkErrorStatus(message.error) ?? assistantErrorStatus;
3525
3457
  if (abortCases.length > 0 && !abortReason && message.type === "assistant") {
3526
3458
  const content = message.message?.content;
3527
3459
  if (Array.isArray(content)) {
@@ -3537,9 +3469,9 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3537
3469
  }
3538
3470
  }
3539
3471
  }
3540
- if (message.type === "assistant" && signals.hasApiErrorStatus(401)) {
3472
+ if (message.type === "assistant" && sdkErrorStatus(message.error) === 401 || message.type === "result" && (sdkErrorStatus(message.api_error_status) === 401 || sdkErrorStatus(message.status) === 401) || message.type === "result" && Array.isArray(message.errors) && message.errors.some((error) => sdkErrorStatus(error) === 401)) {
3541
3473
  signalDone();
3542
- if (agentConfig.refreshGatewayAuth && !reminted && isPastRefresh(agentConfig.gatewayAuth)) {
3474
+ if (agentConfig.refreshGatewayAuth && !reminted && isPastRefresh(agentConfig.gatewayAuth) && !agentConfig.signal?.aborted) {
3543
3475
  logToFile("Agent error: 401 on an aged gateway bearer; re-minting");
3544
3476
  remintRequested = true;
3545
3477
  abortController.abort();
@@ -3560,27 +3492,33 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3560
3492
  ...authError,
3561
3493
  sessionExpired
3562
3494
  });
3563
- getUI().showAuthError({
3564
- hasSettingsConflict: authError.hasSettingsConflict,
3565
- conflicts: authError.conflicts,
3566
- usingManagedLogin: authError.usingManagedLogin,
3567
- credentialPlaces: authError.credentialPlaces,
3568
- sessionExpired,
3569
- logFilePath: getLogFilePath()
3570
- });
3571
- await wizardAbort({
3495
+ authFailure = {
3572
3496
  code: authCode,
3573
3497
  message: "Authentication failed (401)",
3574
- error: new WizardError("Authentication failed", {
3498
+ authErrorDetail: {
3499
+ hasSettingsConflict: authError.hasSettingsConflict,
3500
+ conflicts: authError.conflicts,
3501
+ usingManagedLogin: authError.usingManagedLogin,
3502
+ credentialPlaces: authError.credentialPlaces,
3503
+ sessionExpired,
3504
+ logFilePath: getLogFilePath()
3505
+ },
3506
+ detail: {
3575
3507
  hasSettingsConflict: authError.hasSettingsConflict,
3576
3508
  conflictSources: authError.conflictSources,
3577
3509
  conflictKeys: authError.conflictKeys,
3510
+ conflicts: authError.conflicts,
3511
+ credentialPlaces: authError.credentialPlaces,
3512
+ sessionExpired,
3513
+ logFilePath: getLogFilePath(),
3578
3514
  gatewayUrl: authError.gatewayUrl,
3579
3515
  region: authError.region,
3580
3516
  usingManagedLogin: authError.usingManagedLogin,
3581
3517
  apiKeySource: authError.apiKeySource
3582
- }, authCode)
3583
- });
3518
+ }
3519
+ };
3520
+ abortController.abort();
3521
+ break;
3584
3522
  }
3585
3523
  try {
3586
3524
  middleware?.onMessage(message);
@@ -3591,66 +3529,112 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3591
3529
  if (message.subtype === "success" && !message.is_error) {
3592
3530
  receivedSuccessResult = true;
3593
3531
  lastResultMessage = message;
3532
+ } else if (!receivedSuccessResult) {
3533
+ terminalFailure = sdkResultFailure(message);
3534
+ if (terminalFailure && assistantErrorStatus === 429) terminalFailure = {
3535
+ ...terminalFailure,
3536
+ classification: "WIZARD_RATE_LIMIT"
3537
+ };
3594
3538
  }
3595
3539
  signalDone();
3596
3540
  }
3597
3541
  }
3598
3542
  } catch (error) {
3599
3543
  if (remintRequested) return "remint";
3544
+ if (authFailure) return "done";
3600
3545
  throw error;
3601
3546
  }
3602
3547
  return remintRequested ? "remint" : "done";
3603
3548
  };
3604
3549
  const refreshGatewayAuth = agentConfig.refreshGatewayAuth;
3605
- if (await runQuery() === "remint" && refreshGatewayAuth) {
3550
+ if (await runQuery() === "remint" && refreshGatewayAuth && !agentConfig.signal?.aborted) {
3606
3551
  reminted = true;
3607
3552
  remintRequested = false;
3608
3553
  abortController = new AbortController();
3554
+ if (agentConfig.signal?.aborted) abortController.abort();
3609
3555
  signals.forgetApiErrors();
3610
3556
  spinner.message("Renewing the gateway token...");
3611
3557
  const stale = agentConfig.gatewayAuth;
3612
3558
  agentConfig.gatewayAuth = await refreshGatewayAuth();
3559
+ if (agentConfig.signal?.aborted) return {
3560
+ kind: "abort",
3561
+ classification: "WIZARD_ABORT",
3562
+ message: "Agent run cancelled"
3563
+ };
3613
3564
  logToFile(`Gateway token renewed after a 401 (${Math.round((Date.now() - stale.refreshAtMs) / 1e3)}s past refresh); resuming session ${sessionId ?? "(none: fresh session)"}`);
3614
3565
  analytics.wizardCapture("gateway token reminted", { resumed: sessionId !== void 0 });
3615
3566
  spinner.message(spinnerMessage);
3616
3567
  await runQuery(sessionId);
3617
3568
  }
3569
+ if (authFailure) return {
3570
+ kind: "decided_failure",
3571
+ failure: authFailure
3572
+ };
3573
+ if (agentConfig.signal?.aborted) return {
3574
+ kind: "abort",
3575
+ classification: "WIZARD_ABORT",
3576
+ message: "Agent run cancelled"
3577
+ };
3618
3578
  if (yaraViolationReason) {
3619
3579
  logToFile("Agent error: YARA_VIOLATION");
3620
3580
  spinner.stop("Security check stopped the setup");
3621
- return { error: "WIZARD_YARA_VIOLATION" };
3581
+ return {
3582
+ kind: "failure",
3583
+ classification: "WIZARD_YARA_VIOLATION"
3584
+ };
3622
3585
  }
3623
3586
  if (abortReason) {
3624
3587
  spinner.stop("Wizard aborted");
3625
3588
  return {
3626
- error: "WIZARD_ABORT",
3589
+ kind: "abort",
3590
+ classification: "WIZARD_ABORT",
3627
3591
  message: abortReason
3628
3592
  };
3629
3593
  }
3594
+ if (agentConfig.signal?.aborted) {
3595
+ spinner.stop("Wizard aborted");
3596
+ return {
3597
+ kind: "abort",
3598
+ classification: "WIZARD_ABORT",
3599
+ message: "Agent run cancelled"
3600
+ };
3601
+ }
3630
3602
  if (timedOut) {
3631
3603
  spinner.stop("Agent run timed out");
3632
3604
  return {
3633
- error: "WIZARD_AGENTIC_DETECTION_TIMEOUT",
3605
+ kind: "failure",
3606
+ classification: "WIZARD_AGENTIC_DETECTION_TIMEOUT",
3634
3607
  message: `Agent run timed out after ${timeoutMs / 1e3}s`
3635
3608
  };
3636
3609
  }
3637
3610
  if (signals.has("MCP_MISSING")) {
3638
3611
  logToFile("Agent error: MCP_MISSING");
3639
3612
  spinner.stop("Agent could not access PostHog MCP");
3640
- return { error: "WIZARD_MCP_MISSING" };
3613
+ return {
3614
+ kind: "failure",
3615
+ classification: "WIZARD_MCP_MISSING"
3616
+ };
3641
3617
  }
3642
3618
  if (signals.has("RESOURCE_MISSING")) {
3643
3619
  logToFile("Agent error: RESOURCE_MISSING");
3644
3620
  spinner.stop("Agent could not access setup resource");
3645
- return { error: "WIZARD_RESOURCE_MISSING" };
3621
+ return {
3622
+ kind: "failure",
3623
+ classification: "WIZARD_RESOURCE_MISSING"
3624
+ };
3646
3625
  }
3647
3626
  if (receivedSuccessResult) return completeWithSuccess();
3627
+ if (terminalFailure) {
3628
+ spinner.stop(errorMessage);
3629
+ return terminalFailure;
3630
+ }
3648
3631
  const apiErrorMessage = signals.apiErrorMessage() ?? "Unknown API error";
3649
3632
  if (signals.hasApiErrorStatus(429)) {
3650
3633
  logToFile("Agent error: RATE_LIMIT");
3651
3634
  spinner.stop("Rate limit exceeded");
3652
3635
  return {
3653
- error: "WIZARD_RATE_LIMIT",
3636
+ kind: "failure",
3637
+ classification: "WIZARD_RATE_LIMIT",
3654
3638
  message: apiErrorMessage
3655
3639
  };
3656
3640
  }
@@ -3658,30 +3642,49 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3658
3642
  logToFile("Agent error: API_ERROR");
3659
3643
  spinner.stop("API error occurred");
3660
3644
  return {
3661
- error: "WIZARD_API_ERROR",
3645
+ kind: "failure",
3646
+ classification: "WIZARD_API_ERROR",
3662
3647
  message: apiErrorMessage
3663
3648
  };
3664
3649
  }
3665
- return completeWithSuccess();
3650
+ spinner.stop(errorMessage);
3651
+ return {
3652
+ kind: "failure",
3653
+ classification: "WIZARD_API_ERROR",
3654
+ message: "SDK stream ended without a success result"
3655
+ };
3666
3656
  } catch (error) {
3667
3657
  signalDone();
3668
3658
  if (yaraViolationReason) {
3669
3659
  logToFile("Agent error: YARA_VIOLATION");
3670
3660
  spinner.stop("Security check stopped the setup");
3671
- return { error: "WIZARD_YARA_VIOLATION" };
3661
+ return {
3662
+ kind: "failure",
3663
+ classification: "WIZARD_YARA_VIOLATION"
3664
+ };
3672
3665
  }
3673
3666
  if (abortReason) {
3674
3667
  spinner.stop("Wizard aborted");
3675
3668
  return {
3676
- error: "WIZARD_ABORT",
3669
+ kind: "abort",
3670
+ classification: "WIZARD_ABORT",
3677
3671
  message: abortReason
3678
3672
  };
3679
3673
  }
3674
+ if (agentConfig.signal?.aborted) {
3675
+ spinner.stop("Wizard aborted");
3676
+ return {
3677
+ kind: "abort",
3678
+ classification: "WIZARD_ABORT",
3679
+ message: "Agent run cancelled"
3680
+ };
3681
+ }
3680
3682
  if (receivedSuccessResult) return completeWithSuccess(error);
3681
3683
  if (timedOut) {
3682
3684
  spinner.stop("Agent run timed out");
3683
3685
  return {
3684
- error: "WIZARD_AGENTIC_DETECTION_TIMEOUT",
3686
+ kind: "failure",
3687
+ classification: "WIZARD_AGENTIC_DETECTION_TIMEOUT",
3685
3688
  message: `Agent run timed out after ${timeoutMs / 1e3}s`
3686
3689
  };
3687
3690
  }
@@ -3690,33 +3693,44 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3690
3693
  logToFile("Agent error (caught): RATE_LIMIT");
3691
3694
  spinner.stop("Rate limit exceeded");
3692
3695
  return {
3693
- error: "WIZARD_RATE_LIMIT",
3694
- message: apiErrorMessage
3696
+ kind: "failure",
3697
+ classification: "WIZARD_RATE_LIMIT",
3698
+ message: apiErrorMessage,
3699
+ error: error instanceof Error ? error : void 0
3695
3700
  };
3696
3701
  }
3697
3702
  if (signals.hasApiError()) {
3698
3703
  logToFile("Agent error (caught): API_ERROR");
3699
3704
  spinner.stop("API error occurred");
3700
3705
  return {
3701
- error: "WIZARD_API_ERROR",
3702
- message: apiErrorMessage
3706
+ kind: "failure",
3707
+ classification: "WIZARD_API_ERROR",
3708
+ message: apiErrorMessage,
3709
+ error: error instanceof Error ? error : void 0
3703
3710
  };
3704
3711
  }
3705
3712
  spinner.stop(errorMessage);
3706
- getUI().log.error(`Error: ${error.message}`);
3713
+ emit({
3714
+ kind: "log",
3715
+ level: "error",
3716
+ message: `Error: ${error.message}`
3717
+ });
3707
3718
  logToFile("Agent run failed:", error);
3708
3719
  debug("Full error:", error);
3709
3720
  throw error;
3710
3721
  } finally {
3722
+ agentConfig.signal?.removeEventListener("abort", onExternalAbort);
3711
3723
  if (timeoutId) clearTimeout(timeoutId);
3712
- if (!receivedSuccessResult) {
3724
+ if (!receivedSuccessResult && !authFailure) {
3713
3725
  const durationMs = Date.now() - startTime;
3714
- analytics.wizardCapture("agent aborted", {
3715
- duration_ms: durationMs,
3716
- duration_seconds: Math.round(durationMs / 1e3),
3717
- model: agentConfig.model,
3718
- ...config?.analyticsProperties
3719
- });
3726
+ try {
3727
+ analytics.wizardCapture("agent aborted", {
3728
+ duration_ms: durationMs,
3729
+ duration_seconds: Math.round(durationMs / 1e3),
3730
+ model: agentConfig.model,
3731
+ ...config?.analyticsProperties
3732
+ });
3733
+ } catch {}
3720
3734
  }
3721
3735
  }
3722
3736
  }
@@ -3761,10 +3775,18 @@ const BASE_ALLOWED_TOOLS = [
3761
3775
  "ListMcpResourcesTool",
3762
3776
  ...Object.values(WIZARD_TOOL_NAMES)
3763
3777
  ];
3778
+ const taskSources = /* @__PURE__ */ new WeakMap();
3764
3779
  function handleTaskCreate(block, store) {
3765
3780
  const input = block.input;
3766
3781
  if (!input?.subject) return;
3782
+ let source = taskSources.get(store.tasks);
3783
+ if (!source) {
3784
+ source = randomUUID();
3785
+ taskSources.set(store.tasks, source);
3786
+ }
3767
3787
  store.tasks.set(block.id, {
3788
+ id: randomUUID(),
3789
+ source,
3768
3790
  content: input.subject,
3769
3791
  status: "pending",
3770
3792
  activeForm: input.activeForm
@@ -3790,6 +3812,8 @@ function handleTaskUpdate(block, store) {
3790
3812
  });
3791
3813
  }
3792
3814
  store.tasks.set(input.taskId, {
3815
+ id: existing.id,
3816
+ source: existing.source,
3793
3817
  content: input.subject ?? existing.content,
3794
3818
  status: input.status ?? existing.status,
3795
3819
  activeForm: input.activeForm ?? existing.activeForm
@@ -3899,7 +3923,7 @@ function reportMcpSetup(message) {
3899
3923
  mcp_servers: servers.map((s) => `${s.name}:${s.status}`).join(",")
3900
3924
  });
3901
3925
  }
3902
- function handleSDKMessage(message, options, spinner, signals, receivedSuccessResult = false, tasks, suppressTaskRender = false, emitStepEvents = false, resolveStepKey) {
3926
+ function handleSDKMessage(message, options, spinner, signals, emit, receivedSuccessResult = false, tasks, suppressTaskRender = false, emitStepEvents = false, resolveStepKey) {
3903
3927
  const STATUS_RANK = {
3904
3928
  completed: 0,
3905
3929
  in_progress: 1
@@ -3907,15 +3931,20 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3907
3931
  const rank = (status) => STATUS_RANK[status] ?? 2;
3908
3932
  const syncTasks = () => {
3909
3933
  if (!tasks || suppressTaskRender) return;
3910
- const sorted = Array.from(tasks.values()).sort((a, b) => rank(a.status) - rank(b.status));
3911
- getUI().syncTodos(sorted);
3934
+ emit({
3935
+ kind: "tasks",
3936
+ tasks: Array.from(tasks.values()).sort((a, b) => rank(a.status) - rank(b.status)).map((t) => ({ ...t }))
3937
+ });
3912
3938
  };
3913
3939
  logToFile(`SDK Message: ${message.type}`, JSON.stringify(message, null, 2));
3914
3940
  if (options.debug) debug(`SDK Message type: ${message.type}`);
3915
3941
  switch (message.type) {
3916
3942
  case "assistant": {
3917
3943
  const tokenUsageDelta = extractTokenUsageDelta(message);
3918
- if (tokenUsageDelta) getUI().addTokenUsage(tokenUsageDelta);
3944
+ if (tokenUsageDelta) emit({
3945
+ kind: "usage",
3946
+ delta: tokenUsageDelta
3947
+ });
3919
3948
  const content = message.message?.content;
3920
3949
  if (Array.isArray(content)) for (const block of content) {
3921
3950
  if (block.type === "text" && typeof block.text === "string") {
@@ -3924,15 +3953,26 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3924
3953
  const statusMatch = block.text.match(statusRegex);
3925
3954
  if (statusMatch) {
3926
3955
  const statusText = statusMatch[1].trim();
3927
- getUI().pushStatus(statusText);
3956
+ emit({
3957
+ kind: "status",
3958
+ message: statusText
3959
+ });
3928
3960
  spinner.message(statusText);
3929
3961
  }
3930
3962
  const dashboardRegex = new RegExp(`${AgentSignals.DASHBOARD_URL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*(\\S+)`, "m");
3931
3963
  const dashboardMatch = block.text.match(dashboardRegex);
3932
- if (dashboardMatch) getUI().setDashboardUrl(dashboardMatch[1].trim());
3964
+ if (dashboardMatch) emit({
3965
+ kind: "url",
3966
+ which: "dashboard",
3967
+ url: dashboardMatch[1].trim()
3968
+ });
3933
3969
  const notebookRegex = new RegExp(`${AgentSignals.NOTEBOOK_URL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*(\\S+)`, "m");
3934
3970
  const notebookMatch = block.text.match(notebookRegex);
3935
- if (notebookMatch) getUI().setNotebookUrl(notebookMatch[1].trim());
3971
+ if (notebookMatch) emit({
3972
+ kind: "url",
3973
+ which: "notebook",
3974
+ url: notebookMatch[1].trim()
3975
+ });
3936
3976
  }
3937
3977
  if (block.type === "tool_use" && tasks && Object.values(TaskTool).includes(block.name)) dispatchTaskToolUse(block, {
3938
3978
  tasks,
@@ -3942,7 +3982,10 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3942
3982
  });
3943
3983
  if (block.type === "tool_use") {
3944
3984
  const stage = classifyToolToStage(block.name);
3945
- if (stage) getUI().setStage(stage);
3985
+ if (stage) emit({
3986
+ kind: "stage",
3987
+ stage
3988
+ });
3946
3989
  }
3947
3990
  }
3948
3991
  break;
@@ -3966,7 +4009,11 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3966
4009
  logToFile("Agent result with error:", message.result);
3967
4010
  if (typeof message.result === "string") signals.push(message.result);
3968
4011
  if (message.errors && !receivedSuccessResult) for (const err of message.errors) {
3969
- getUI().log.error(`Error: ${err}`);
4012
+ emit({
4013
+ kind: "log",
4014
+ level: "error",
4015
+ message: `Error: ${err}`
4016
+ });
3970
4017
  logToFile("ERROR:", err);
3971
4018
  }
3972
4019
  } else if (message.subtype === "success") {
@@ -3975,7 +4022,11 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3975
4022
  } else {
3976
4023
  logToFile("Agent result with error:", message.result);
3977
4024
  if (message.errors && !receivedSuccessResult) for (const err of message.errors) {
3978
- getUI().log.error(`Error: ${err}`);
4025
+ emit({
4026
+ kind: "log",
4027
+ level: "error",
4028
+ message: `Error: ${err}`
4029
+ });
3979
4030
  logToFile("ERROR:", err);
3980
4031
  }
3981
4032
  }
@@ -3998,6 +4049,6 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
3998
4049
  }
3999
4050
  }
4000
4051
  //#endregion
4001
- export { resolveEnvSecretRefs as $, AUDIT_STATUSES as A, checkEnvKeys as B, publishHandoff as C, AUDIT_RESOLVE_CHECKS_PARAM_DESCRIPTION as D, AUDIT_RESOLVE_CHECKS_DESCRIPTION as E, WIZARD_ASK_SENSITIVE_DESCRIPTION as F, fetchSkillMenu as G, describeAskCancellation as H, WIZARD_ASK_SUBJECT_DESCRIPTION as I, mergeEnvValues as J, installSkillById as K, WIZARD_ASK_TOOL_DESCRIPTION as L, CHECK_ENV_KEYS_FILE_PATH_DESCRIPTION as M, ENV_FILE_PATH_DESCRIPTION as N, AUDIT_SEED_CHECKS_DESCRIPTION as O, WIZARD_ASK_KIND_DESCRIPTION as P, resolveEnvPath as Q, WIZARD_TOOL_NAMES as R, PUBLISH_HANDOFF_TOOL_NAME as S, AUDIT_ADD_CHECKS_PARAM_DESCRIPTION as T, downloadSkill as U, createAskAccounting as V, ensureGitignoreCoverage as W, resolveAskQuestionKinds as X, normaliseAskSubject as Y, resolveAuditChecks as Z, buildGatewayProvider as _, wizardCanUseTool as a, createWizardAskBridge as at, PUBLISH_HANDOFF_CONTENT_DESCRIPTION as b, checkAllSettingsConflicts as c, isEnvFileName as ct, restoreClaudeSettings as d, markGrantRevoked as dt, seedAuditChecks as et, sanitizeAgentSubprocessEnv as f, GATEWAY_PROVIDER as g, createTriageLLMProvider as h, runAgent as i, LONGER_ASK_TIMEOUT_MS as it, CHECK_ENV_KEYS_DESCRIPTION as j, AUDIT_SEED_CHECKS_PARAM_DESCRIPTION as k, classifySettingsConflicts as l, parseEnvKeyNames as lt, assembleCommandments as m, buildRunTags as n, vaultSensitiveAnswers as nt, createIsolatedAgentConfigDir as o, isFullyCancelled as ot, AgentOutputSignals as p, legacyKeyNameRefusal as q, initializeAgent as r, fetchWithRetry as rt, backupAndFixClaudeSettings as s, MAX_ENV_KEY_SET as st, buildAgentEnv as t, templateEnvWriteRefusal as tt, recoverOrphanedSettingsBackups as u, toPromptSafeRelativePath as ut, withGatewayRemint as v, AUDIT_ADD_CHECKS_DESCRIPTION as w, PUBLISH_HANDOFF_DESCRIPTION as x, createSecretVault as y, addAuditChecks as z };
4052
+ export { vaultSensitiveAnswers as $, CHECK_ENV_KEYS_FILE_PATH_DESCRIPTION as A, describeAskCancellation as B, AUDIT_ADD_CHECKS_PARAM_DESCRIPTION as C, AUDIT_SEED_CHECKS_PARAM_DESCRIPTION as D, AUDIT_SEED_CHECKS_DESCRIPTION as E, WIZARD_ASK_TOOL_DESCRIPTION as F, mergeEnvValues as G, ensureGitignoreCoverage as H, WIZARD_TOOL_NAMES as I, resolveAuditChecks as J, normaliseAskSubject as K, addAuditChecks as L, WIZARD_ASK_KIND_DESCRIPTION as M, WIZARD_ASK_SENSITIVE_DESCRIPTION as N, AUDIT_STATUSES as O, WIZARD_ASK_SUBJECT_DESCRIPTION as P, templateEnvWriteRefusal as Q, checkEnvKeys as R, AUDIT_ADD_CHECKS_DESCRIPTION as S, AUDIT_RESOLVE_CHECKS_PARAM_DESCRIPTION as T, installSkillById as U, downloadSkill as V, legacyKeyNameRefusal as W, resolveEnvSecretRefs as X, resolveEnvPath as Y, seedAuditChecks as Z, createSecretVault as _, wizardCanUseTool as a, MAX_ENV_KEY_SET as at, PUBLISH_HANDOFF_TOOL_NAME as b, checkAllSettingsConflicts as c, toPromptSafeRelativePath as ct, restoreClaudeSettings as d, fetchSkillMenu as et, sanitizeAgentSubprocessEnv as f, assembleCommandments as g, markGrantRevoked as h, runAgent as i, isFullyCancelled as it, ENV_FILE_PATH_DESCRIPTION as j, CHECK_ENV_KEYS_DESCRIPTION as k, classifySettingsConflicts as l, createTriageLLMProvider as lt, isGrantRevoked as m, buildRunTags as n, LONGER_ASK_TIMEOUT_MS as nt, createIsolatedAgentConfigDir as o, isEnvFileName as ot, AgentOutputSignals as p, resolveAskQuestionKinds as q, initializeAgent as r, createWizardAskBridge as rt, backupAndFixClaudeSettings as s, parseEnvKeyNames as st, buildAgentEnv as t, fetchWithRetry as tt, recoverOrphanedSettingsBackups as u, PUBLISH_HANDOFF_CONTENT_DESCRIPTION as v, AUDIT_RESOLVE_CHECKS_DESCRIPTION as w, publishHandoff as x, PUBLISH_HANDOFF_DESCRIPTION as y, createAskAccounting as z };
4002
4053
 
4003
- //# sourceMappingURL=agent-interface-Avtr_5M5.js.map
4054
+ //# sourceMappingURL=agent-interface-BAOIHuXS.js.map