@posthog/wizard 2.73.1 → 2.74.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 (108) hide show
  1. package/README.md +32 -28
  2. package/dist/README.md +11 -4
  3. package/dist/{add-mcp-server-to-clients-D47ONFCD.js → add-mcp-server-to-clients-BtWwRG7O.js} +4 -4
  4. package/dist/{add-mcp-server-to-clients-D47ONFCD.js.map → add-mcp-server-to-clients-BtWwRG7O.js.map} +1 -1
  5. package/dist/{agent-interface-CQ_iz7g7.js → agent-interface-CK6pmvkF.js} +485 -340
  6. package/dist/agent-interface-CK6pmvkF.js.map +1 -0
  7. package/dist/{agent-prompt-loader-Dh2CZEw4.js → agent-prompt-loader-CPCNMvGh.js} +6 -7
  8. package/dist/agent-prompt-loader-CPCNMvGh.js.map +1 -0
  9. package/dist/{agent-runner-kj8juD_r.js → agent-runner-jz4yiJfh.js} +49 -67
  10. package/dist/agent-runner-jz4yiJfh.js.map +1 -0
  11. package/dist/analytics-CfadnLFG.js +2 -0
  12. package/dist/{analytics-DHkqXgxG.js → analytics-DZNb3aRN.js} +2 -2
  13. package/dist/{analytics-DHkqXgxG.js.map → analytics-DZNb3aRN.js.map} +1 -1
  14. package/dist/{api-BM5Gaf2x.js → api-DXGaiK94.js} +3 -3
  15. package/dist/{api-BM5Gaf2x.js.map → api-DXGaiK94.js.map} +1 -1
  16. package/dist/bin.js +59 -54
  17. package/dist/bin.js.map +1 -1
  18. package/dist/{bounded-fs-Byv2xRSy.js → bounded-fs-CErPpkN2.js} +3 -3
  19. package/dist/{bounded-fs-Byv2xRSy.js.map → bounded-fs-CErPpkN2.js.map} +1 -1
  20. package/dist/{ci-install-BUUD2hSG.js → ci-install-CzWzi_qu.js} +21 -24
  21. package/dist/ci-install-CzWzi_qu.js.map +1 -0
  22. package/dist/{debug-C14qe0b-.js → debug-D4y9vVWt.js} +1 -1
  23. package/dist/{debug-D0k3Quos.js → debug-ECdmmlDM.js} +21 -33
  24. package/dist/debug-ECdmmlDM.js.map +1 -0
  25. package/dist/{env-api-key-CBjWUJTZ.js → env-api-key-g5w775Ve.js} +2 -2
  26. package/dist/{env-api-key-CBjWUJTZ.js.map → env-api-key-g5w775Ve.js.map} +1 -1
  27. package/dist/{environment-DqdmG3WX.js → environment-DDkdsWUr.js} +4 -4
  28. package/dist/{environment-DqdmG3WX.js.map → environment-DDkdsWUr.js.map} +1 -1
  29. package/dist/{errors-u8z27V-x.js → errors--OqLxmWR.js} +34 -3
  30. package/dist/errors--OqLxmWR.js.map +1 -0
  31. package/dist/{file-watcher-CxStDuTR.js → file-watcher-BNdFPlI8.js} +2 -2
  32. package/dist/{file-watcher-CxStDuTR.js.map → file-watcher-BNdFPlI8.js.map} +1 -1
  33. package/dist/{headless-ui-DC1Qus7M.js → headless-ui-DBZCl4CG.js} +2 -2
  34. package/dist/{headless-ui-DC1Qus7M.js.map → headless-ui-DBZCl4CG.js.map} +1 -1
  35. package/dist/{interactive-DeNrHRZE.js → interactive-BKxXUB0T.js} +3 -3
  36. package/dist/{interactive-DeNrHRZE.js.map → interactive-BKxXUB0T.js.map} +1 -1
  37. package/dist/{mcp-Bnjc-Qsz.js → mcp-CwM45_Kp.js} +2 -2
  38. package/dist/{mcp-Bnjc-Qsz.js.map → mcp-CwM45_Kp.js.map} +1 -1
  39. package/dist/{mcp-prompt-streaming-Va6vRL7E.js → mcp-prompt-streaming-R8H7uhuO.js} +5 -5
  40. package/dist/mcp-prompt-streaming-R8H7uhuO.js.map +1 -0
  41. package/dist/{non-interactive-ZACUU6DC.js → non-interactive-DxzBwNxJ.js} +3 -3
  42. package/dist/{non-interactive-ZACUU6DC.js.map → non-interactive-DxzBwNxJ.js.map} +1 -1
  43. package/dist/{orchestrator-tools-BI3suN6p.js → orchestrator-tools-CTpKnk5-.js} +3 -3
  44. package/dist/{orchestrator-tools-BI3suN6p.js.map → orchestrator-tools-CTpKnk5-.js.map} +1 -1
  45. package/dist/{package-manager-DgRcS6CY.js → package-manager-Bi6gaPa4.js} +2 -2
  46. package/dist/{package-manager-DgRcS6CY.js.map → package-manager-Bi6gaPa4.js.map} +1 -1
  47. package/dist/{pi-DbEYScTs.js → pi-DpWFwz5u.js} +35 -20
  48. package/dist/pi-DpWFwz5u.js.map +1 -0
  49. package/dist/{playground-xa1G6pym.js → playground-BtTCVaSD.js} +7 -12
  50. package/dist/playground-BtTCVaSD.js.map +1 -0
  51. package/dist/{posthog-Dk8S5c6h.js → posthog-B99H8Zyk.js} +2 -2
  52. package/dist/{posthog-Dk8S5c6h.js.map → posthog-B99H8Zyk.js.map} +1 -1
  53. package/dist/{posthog-integration-BG5Y9Fb4.js → posthog-integration-CxvkKY1j.js} +13 -13
  54. package/dist/{posthog-integration-BG5Y9Fb4.js.map → posthog-integration-CxvkKY1j.js.map} +1 -1
  55. package/dist/{provisioning-e7J3SvpF.js → provisioning-BewSEuFX.js} +4 -10
  56. package/dist/provisioning-BewSEuFX.js.map +1 -0
  57. package/dist/{queue-tools-C3D_nwy_.js → queue-tools-CbRM-OET.js} +4 -31
  58. package/dist/queue-tools-CbRM-OET.js.map +1 -0
  59. package/dist/{registry-Bw-e1Fes.js → registry-Bt2Mc4vC.js} +5 -5
  60. package/dist/{registry-Bw-e1Fes.js.map → registry-Bt2Mc4vC.js.map} +1 -1
  61. package/dist/{security-BUg5_2uC.js → security-Cmxb64bz.js} +5 -5
  62. package/dist/{security-BUg5_2uC.js.map → security-Cmxb64bz.js.map} +1 -1
  63. package/dist/{setup-utils-CzMg2Vpy.js → setup-utils-CCphwDIz.js} +9 -15
  64. package/dist/setup-utils-CCphwDIz.js.map +1 -0
  65. package/dist/smoke-test-ci.sh +5 -2
  66. package/dist/{start-tui-Kt61g7LE.js → start-tui-BRwZzlWb.js} +22 -22
  67. package/dist/{start-tui-Kt61g7LE.js.map → start-tui-BRwZzlWb.js.map} +1 -1
  68. package/dist/{steps-De2a1a0X.js → steps-CJrx1t72.js} +7 -7
  69. package/dist/{steps-De2a1a0X.js.map → steps-CJrx1t72.js.map} +1 -1
  70. package/dist/{store-DDrR7nF6.js → store-BhcCTIn4.js} +6 -10
  71. package/dist/store-BhcCTIn4.js.map +1 -0
  72. package/dist/{subagent-cD2JOzo1.js → subagent-BeMPBF5r.js} +2 -2
  73. package/dist/{subagent-cD2JOzo1.js.map → subagent-BeMPBF5r.js.map} +1 -1
  74. package/dist/{task-FBY3mllc.js → task-DX3eZgBL.js} +36 -21
  75. package/dist/task-DX3eZgBL.js.map +1 -0
  76. package/dist/{task-stream-BjTFIY4l.js → task-stream-BW7BG70C.js} +5 -5
  77. package/dist/{task-stream-BjTFIY4l.js.map → task-stream-BW7BG70C.js.map} +1 -1
  78. package/dist/{tasks-BjevyqMO.js → tasks-Did1AR7v.js} +2 -2
  79. package/dist/{tasks-BjevyqMO.js.map → tasks-Did1AR7v.js.map} +1 -1
  80. package/dist/{telemetry-Asp60nwm.js → telemetry-CPWHrsXK.js} +3 -3
  81. package/dist/{telemetry-Asp60nwm.js.map → telemetry-CPWHrsXK.js.map} +1 -1
  82. package/dist/{terminal-CVgx_zHz.js → terminal-CpVFsbGu.js} +12 -12
  83. package/dist/{terminal-CVgx_zHz.js.map → terminal-CpVFsbGu.js.map} +1 -1
  84. package/dist/{tools-CPwnElLN.js → tools-DjiwCd53.js} +6 -6
  85. package/dist/{tools-CPwnElLN.js.map → tools-DjiwCd53.js.map} +1 -1
  86. package/dist/{wizard-abort-owSE9WC8.js → wizard-abort-DjJ-6y6S.js} +1 -1
  87. package/dist/{wizard-abort-DKpVH-t6.js → wizard-abort-RUnY1Dax.js} +4 -4
  88. package/dist/{wizard-abort-DKpVH-t6.js.map → wizard-abort-RUnY1Dax.js.map} +1 -1
  89. package/dist/yara-hooks-CBMSmD9F.js +2 -0
  90. package/dist/{yara-hooks-D-QD1ZCB.js → yara-hooks-jBqAkgxw.js} +4 -4
  91. package/dist/{yara-hooks-D-QD1ZCB.js.map → yara-hooks-jBqAkgxw.js.map} +1 -1
  92. package/package.json +1 -1
  93. package/dist/agent-interface-CQ_iz7g7.js.map +0 -1
  94. package/dist/agent-prompt-loader-Dh2CZEw4.js.map +0 -1
  95. package/dist/agent-runner-kj8juD_r.js.map +0 -1
  96. package/dist/analytics-BYknS_x4.js +0 -2
  97. package/dist/ci-install-BUUD2hSG.js.map +0 -1
  98. package/dist/debug-D0k3Quos.js.map +0 -1
  99. package/dist/errors-u8z27V-x.js.map +0 -1
  100. package/dist/mcp-prompt-streaming-Va6vRL7E.js.map +0 -1
  101. package/dist/pi-DbEYScTs.js.map +0 -1
  102. package/dist/playground-xa1G6pym.js.map +0 -1
  103. package/dist/provisioning-e7J3SvpF.js.map +0 -1
  104. package/dist/queue-tools-C3D_nwy_.js.map +0 -1
  105. package/dist/setup-utils-CzMg2Vpy.js.map +0 -1
  106. package/dist/store-DDrR7nF6.js.map +0 -1
  107. package/dist/task-FBY3mllc.js.map +0 -1
  108. package/dist/yara-hooks-DPo3t8nU.js +0 -2
@@ -1,12 +1,12 @@
1
1
  import { n as __require } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { a as AgentSignals, o as REMARK_INSTRUCTION, s as classifyAuthFailure } from "./errors-u8z27V-x.js";
3
- import { D as CONTEXT_MILL_URL, St as runtimeEnv, T as AWS_SKILLS_BASE_URL, X as POSTHOG_PROPERTY_HEADER_PREFIX, a as getLogFilePath, f as getUI, ft as WIZARD_REMARK_EVENT_NAME, gt as wizardUserAgentForProgram, o as initLogFile, q as POSTHOG_FLAG_HEADER_PREFIX, r as debug, s as logToFile } from "./debug-D0k3Quos.js";
2
+ import { c as RESUME_INSTRUCTION, d as ErrorCodes, l as classifyAuthFailure, o as AgentSignals, s as REMARK_INSTRUCTION } from "./errors--OqLxmWR.js";
3
+ import { D as CONTEXT_MILL_URL, T as AWS_SKILLS_BASE_URL, a as getLogFilePath, f as getUI, lt as WIZARD_REMARK_EVENT_NAME, o as initLogFile, pt as wizardUserAgentForProgram, r as debug, s as logToFile, yt as runtimeEnv } from "./debug-ECdmmlDM.js";
4
4
  import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-GdDN_aMS.js";
5
- import { n as analytics } from "./analytics-DHkqXgxG.js";
6
- import { a as safeReadFile, i as readProjectFile, o as walkProjectFiles } from "./bounded-fs-Byv2xRSy.js";
7
- import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-DKpVH-t6.js";
8
- import { b as coerceAuditChecks, g as AUDIT_CHECKS_FILE, l as prewarmYaraScanner, n as createPostToolUseYaraHooks, p as scanInstalledSkill, r as createPreToolUseYaraHooks } from "./yara-hooks-D-QD1ZCB.js";
9
- import { S as writeJsonAtomic, b as triageModelFor, c as buildOrchestratorTools, v as modelCapabilities, x as makeMutex } from "./queue-tools-C3D_nwy_.js";
5
+ import { n as analytics } from "./analytics-DZNb3aRN.js";
6
+ import { a as safeReadFile, i as readProjectFile, o as walkProjectFiles } from "./bounded-fs-CErPpkN2.js";
7
+ import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-RUnY1Dax.js";
8
+ import { b as coerceAuditChecks, g as AUDIT_CHECKS_FILE, l as prewarmYaraScanner, n as createPostToolUseYaraHooks, p as scanInstalledSkill, r as createPreToolUseYaraHooks } from "./yara-hooks-jBqAkgxw.js";
9
+ import { S as writeJsonAtomic, b as triageModelFor, c as buildOrchestratorTools, v as modelCapabilities, x as makeMutex } from "./queue-tools-CbRM-OET.js";
10
10
  import { createRequire } from "node:module";
11
11
  import * as fs$1 from "fs";
12
12
  import fs from "fs";
@@ -185,27 +185,53 @@ function createCustomHeaders() {
185
185
  value
186
186
  });
187
187
  },
188
- addFlag(flagKey, variant) {
189
- const headerName = POSTHOG_FLAG_HEADER_PREFIX + flagKey.toUpperCase();
190
- entries.push({
191
- key: headerName,
192
- value: variant
193
- });
194
- },
195
188
  encode() {
196
189
  return entries.map(({ key, value }) => `${key}: ${value}`).join("\n");
197
190
  }
198
191
  };
199
192
  }
200
193
  //#endregion
194
+ //#region src/lib/legacy-gateway.ts
195
+ let enabled = false;
196
+ /** Only the `--ci` runner turns this on. */
197
+ function setLegacyGatewayFallback(on) {
198
+ enabled = on;
199
+ }
200
+ /** Legacy auth when the fallback is on and the mint cannot serve this credential (401, or 404 without the endpoint). */
201
+ function legacyGatewayAuth(host, accessToken, mintStatus) {
202
+ if (!enabled || mintStatus !== 401 && mintStatus !== 404) return void 0;
203
+ return {
204
+ gatewayUrl: legacyGatewayUrl(host),
205
+ token: accessToken,
206
+ legacy: true,
207
+ refreshAtMs: Number.POSITIVE_INFINITY
208
+ };
209
+ }
210
+ function legacyGatewayUrl(host) {
211
+ const { hostname } = new URL(host.apiHost);
212
+ if (hostname === "host.docker.internal") return "http://host.docker.internal:3308/wizard";
213
+ if (hostname === "localhost" || hostname === "127.0.0.1") return "http://localhost:3308/wizard";
214
+ return `https://gateway.${host.region}.posthog.com/wizard`;
215
+ }
216
+ /** The legacy gateway reads per-key metadata and flag headers and needs an explicit Bedrock opt-in. */
217
+ function legacyGatewayHeaders(wizardMetadata, wizardFlags) {
218
+ const headers = { "x-posthog-use-bedrock-fallback": "true" };
219
+ for (const [key, value] of Object.entries(wizardMetadata)) headers[key.startsWith("X-POSTHOG-PROPERTY-") ? key : `X-POSTHOG-PROPERTY-${key}`] = value;
220
+ for (const [flagKey, variant] of Object.entries(wizardFlags)) {
221
+ if (!flagKey.toLowerCase().startsWith("wizard")) continue;
222
+ headers[`X-POSTHOG-FLAG-${flagKey.toUpperCase()}`] = variant;
223
+ }
224
+ return headers;
225
+ }
226
+ //#endregion
201
227
  //#region src/lib/gateway-session.ts
202
228
  /**
203
229
  * Gateway auth for a wizard run: a `phe_` scoped token the backend mints, with
204
230
  * pinned attribution, a spend cap and an expiry.
205
231
  *
206
- * A 404 resolves to the legacy posture (OAuth token, Python gateway); every
207
- * other failure throws, because the legacy path enforces none of those, so a
208
- * silent downgrade spends unattributed money to hide an outage.
232
+ * Every mint failure throws, since a silent downgrade would spend uncapped,
233
+ * unattributed money to hide an outage. The CI-only exception lives in
234
+ * legacy-gateway.ts.
209
235
  */
210
236
  let cached = null;
211
237
  /**
@@ -214,15 +240,17 @@ let cached = null;
214
240
  */
215
241
  let inFlight = null;
216
242
  /**
217
- * Adoption floor. The anthropic subprocess holds its credential for the whole
218
- * session, so a token below this 401s mid-run.
243
+ * Adoption floor. The anthropic subprocess holds its credential until a 401
244
+ * forces a re-mint, so a token below this would churn mints.
219
245
  */
220
246
  const MIN_USABLE_TTL_MS = 120 * 1e3;
221
247
  /** Re-resolve at this fraction of the token's life, leaving a usable remainder. */
222
248
  const REFRESH_AT_FRACTION = .8;
223
- /** How long a legacy fallback sticks before the mint endpoint is retried. */
224
- const LEGACY_RETRY_MS = 600 * 1e3;
225
249
  const MINT_TIMEOUT_MS = 2e4;
250
+ /** Longer than any refusal the mint writes; a body past this is not a message. */
251
+ const MAX_REFUSAL_DETAIL_LENGTH = 500;
252
+ /** Outcomes are short snake_case labels; anything longer is not one. */
253
+ const MAX_REFUSAL_OUTCOME_LENGTH = 64;
226
254
  /** Resolve this run's gateway auth, minting and re-minting near expiry. */
227
255
  async function gatewayAuth(host, accessToken, program) {
228
256
  const key = `${host.apiHost}\n${accessToken}\n${program ?? ""}`;
@@ -244,16 +272,20 @@ async function resolveGatewayAuth(host, accessToken, key, program) {
244
272
  logToFile("[gateway] run has no program id; failing the run");
245
273
  throw new GatewayMintFailed("this run has no program to attribute its spend to");
246
274
  }
247
- const minted = await mintGatewayToken(host, accessToken, program);
248
- if (!minted) {
249
- analytics.setTag("gateway_edition", "legacy");
250
- const auth = legacyAuth(host, accessToken);
275
+ let minted;
276
+ try {
277
+ minted = await mintGatewayToken(host, accessToken, program);
278
+ } catch (e) {
279
+ if (!(e instanceof GatewayMintRefused)) throw e;
280
+ const legacy = legacyGatewayAuth(host, accessToken, e.status);
281
+ if (!legacy) throw e;
282
+ logToFile(`[gateway] mint refused this credential (HTTP ${e.status}); CI run staying on the legacy gateway`);
251
283
  cached = {
252
284
  key,
253
- auth,
254
- staleAtMs: Date.now() + LEGACY_RETRY_MS
285
+ auth: legacy,
286
+ staleAtMs: legacy.refreshAtMs
255
287
  };
256
- return auth;
288
+ return legacy;
257
289
  }
258
290
  const expiresAtMs = Date.parse(minted.expiresAt);
259
291
  const ttlMs = expiresAtMs - Date.now();
@@ -262,13 +294,12 @@ async function resolveGatewayAuth(host, accessToken, key, program) {
262
294
  throw new GatewayMintFailed(`the PostHog gateway issued a token with ${ttlMs}ms of life`);
263
295
  }
264
296
  const staleAtMs = Date.now() + ttlMs * REFRESH_AT_FRACTION;
265
- analytics.setTag("gateway_edition", "v2");
266
297
  logToFile(`[gateway] minted a scoped token: program=${program} team=${minted.teamId ?? "unknown"} ttl=${Math.round(ttlMs / 1e3)}s url=${minted.gatewayUrl}`);
267
298
  const auth = {
268
299
  gatewayUrl: minted.gatewayUrl,
269
300
  token: minted.token,
270
- edition: "v2",
271
- teamId: minted.teamId
301
+ teamId: minted.teamId,
302
+ refreshAtMs: staleAtMs
272
303
  };
273
304
  cached = {
274
305
  key,
@@ -277,13 +308,9 @@ async function resolveGatewayAuth(host, accessToken, key, program) {
277
308
  };
278
309
  return auth;
279
310
  }
280
- /** The legacy posture: the user's OAuth token against the Python gateway. */
281
- function legacyAuth(host, accessToken) {
282
- return {
283
- gatewayUrl: host.gatewayUrl,
284
- token: accessToken,
285
- edition: "legacy"
286
- };
311
+ /** Whether a 401 on this bearer may be age (past its refresh instant) rather than a bad credential. */
312
+ function isPastRefresh(auth, now = Date.now()) {
313
+ return now >= auth.refreshAtMs;
287
314
  }
288
315
  /**
289
316
  * Whether a server-supplied origin may receive a bearer and prompt content:
@@ -309,41 +336,75 @@ function isTrustedGatewayUrl(value, apiHost) {
309
336
  }
310
337
  /**
311
338
  * A deliberate refusal from the mint endpoint, as opposed to the mint being
312
- * unavailable. Thrown rather than folded into the legacy fallback, so the run
313
- * stops instead of proceeding without the controls the refusal was enforcing.
339
+ * unavailable. Thrown so the run stops instead of proceeding without the
340
+ * controls the refusal was enforcing. A WizardError, so the runners print its
341
+ * message as-is and `wizardAbort` resolves its code.
314
342
  */
315
- var GatewayMintRefused = class extends Error {
343
+ var GatewayMintRefused = class extends WizardError {
316
344
  status;
317
- constructor(status, message) {
318
- super(message);
345
+ /** The backend's refusal outcome (`blocked`, `throttled`, ...), when it sent one. */
346
+ outcome;
347
+ constructor(status, message, outcome) {
348
+ super(message, {
349
+ status,
350
+ outcome
351
+ }, ErrorCodes.GatewayMintRefused);
319
352
  this.name = "GatewayMintRefused";
320
353
  this.status = status;
354
+ this.outcome = outcome;
321
355
  }
322
356
  };
323
357
  /**
324
358
  * The mint could not produce a usable credential: unreachable, a 5xx, or a
325
359
  * response the client cannot use.
326
360
  */
327
- var GatewayMintFailed = class extends Error {
361
+ var GatewayMintFailed = class extends WizardError {
328
362
  constructor(message) {
329
- super(message);
363
+ super(message, void 0, ErrorCodes.GatewayMintFailed);
330
364
  this.name = "GatewayMintFailed";
331
365
  }
332
366
  };
333
367
  /**
334
368
  * Whether a mint status means "refused this run" rather than "not available".
335
- * These are the statuses the endpoint returns after it has authenticated the
336
- * caller: 429 the daily run limit, 403 revoked project access, 400 a login
337
- * covering more than one project. 404 and 401 fall back instead.
369
+ * 429 the daily run limit, 403 revoked project access, 400 a login covering
370
+ * more than one project, 401 a credential the mint does not accept, 404 an
371
+ * instance without the mint endpoint.
338
372
  */
339
373
  function isMintRefusal(status) {
340
- return status === 400 || status === 403 || status === 429;
374
+ return status === 400 || status === 401 || status === 403 || status === 404 || status === 429;
375
+ }
376
+ /**
377
+ * The server's own reason for a refusal, when it sent one. DRF answers every
378
+ * refusal as `{"detail": "...", "code": "<outcome>"}`; the blocklist's detail
379
+ * names the contact address, which the fixed messages below cannot. `code` is
380
+ * the backend's own label for the refusal (`outcome` on older backends) and
381
+ * rides the client event.
382
+ */
383
+ function cleanRefusalText(value) {
384
+ if (typeof value !== "string") return "";
385
+ return value.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").trim();
386
+ }
387
+ async function readRefusal(resp) {
388
+ try {
389
+ const body = await resp.json();
390
+ const detail = cleanRefusalText(body?.detail);
391
+ const outcome = cleanRefusalText(body?.code) || cleanRefusalText(body?.outcome);
392
+ return {
393
+ detail: detail.length > 0 && detail.length <= MAX_REFUSAL_DETAIL_LENGTH ? detail : void 0,
394
+ outcome: outcome.length > 0 && outcome.length <= MAX_REFUSAL_OUTCOME_LENGTH ? outcome : void 0
395
+ };
396
+ } catch {
397
+ return {};
398
+ }
341
399
  }
342
- function mintRefusalMessage(status) {
400
+ function mintRefusalMessage(status, detail) {
401
+ if (detail) return detail;
343
402
  switch (status) {
344
403
  case 429: return "This wizard program has used its daily run limit. Try again tomorrow.";
345
404
  case 403: return "Your access to this project has changed. Re-authenticate and try again.";
346
405
  case 400: return "Your PostHog login must cover exactly one project. Re-authenticate and try again.";
406
+ case 401: return "PostHog did not accept this login. Re-authenticate with `npx @posthog/wizard@latest`.";
407
+ case 404: return "This PostHog instance does not issue gateway tokens. Upgrade with `npx @posthog/wizard@latest` and try again.";
347
408
  default: return "The PostHog gateway refused this run.";
348
409
  }
349
410
  }
@@ -355,17 +416,22 @@ async function mintGatewayToken(host, accessToken, program) {
355
416
  Authorization: `Bearer ${accessToken}`,
356
417
  "Content-Type": "application/json"
357
418
  },
358
- body: JSON.stringify({ program }),
419
+ body: JSON.stringify({
420
+ program,
421
+ reads_refusal_reason: true
422
+ }),
359
423
  signal: AbortSignal.timeout(MINT_TIMEOUT_MS)
360
424
  });
361
425
  if (!resp.ok) {
362
426
  if (isMintRefusal(resp.status)) {
363
- logToFile(`[gateway] mint refused with HTTP ${resp.status}; failing the run`);
364
- throw new GatewayMintRefused(resp.status, mintRefusalMessage(resp.status));
365
- }
366
- if (resp.status === 404 || resp.status === 401) {
367
- logToFile(`[gateway] mint unavailable for this credential (HTTP ${resp.status}); staying on the existing gateway`);
368
- return null;
427
+ const refusal = await readRefusal(resp);
428
+ logToFile(`[gateway] mint refused with HTTP ${resp.status} (${refusal.outcome ?? "no outcome"}); failing the run`);
429
+ analytics.wizardCapture("gateway mint refused", {
430
+ status: resp.status,
431
+ outcome: refusal.outcome,
432
+ program
433
+ });
434
+ throw new GatewayMintRefused(resp.status, mintRefusalMessage(resp.status, refusal.detail), refusal.outcome);
369
435
  }
370
436
  logToFile(`[gateway] mint failed with HTTP ${resp.status}; failing the run`);
371
437
  throw new GatewayMintFailed(`the PostHog gateway could not issue a token (HTTP ${resp.status})`);
@@ -407,7 +473,7 @@ async function mintGatewayToken(host, accessToken, program) {
407
473
  * post-cutover).
408
474
  */
409
475
  function buildWizardPropertiesBlob(wizardMetadata, wizardFlags, teamId) {
410
- const props = {};
476
+ const props = { ai_product: "wizard" };
411
477
  if (teamId !== void 0) props.team_id = teamId;
412
478
  for (const [key, value] of Object.entries(wizardMetadata)) props[stripPropertyPrefix(key)] = value;
413
479
  for (const [flagKey, variant] of Object.entries(wizardFlags)) {
@@ -2162,11 +2228,11 @@ async function createWizardToolsServer(options) {
2162
2228
  //#endregion
2163
2229
  //#region src/lib/agent/runner/harness/pi/gateway.ts
2164
2230
  /**
2165
- * PostHog LLM gateway provider spec for pi sessions — shared by the linear run
2231
+ * PostHog AI gateway provider spec for pi sessions, shared by the linear run
2166
2232
  * and the orchestrator's per-task runs so both speak to the gateway
2167
- * identically: bearer auth, Bedrock-fallback + wizard metadata/flag headers,
2168
- * transport shape inferred from the model id. The caller registers the spec on
2169
- * its own (lazily imported, properly typed) pi ModelRegistry.
2233
+ * identically: bearer auth, the wizard properties header, transport shape
2234
+ * inferred from the model id. The caller registers the spec on its own
2235
+ * (lazily imported, properly typed) pi ModelRegistry.
2170
2236
  */
2171
2237
  /** Provider registered on the in-memory registry for this run. */
2172
2238
  const GATEWAY_PROVIDER = "posthog-gateway";
@@ -2177,38 +2243,24 @@ const GATEWAY_PROVIDER = "posthog-gateway";
2177
2243
  * `/v1/responses` or `/v1/chat/completions` (base URL keeps `/v1`). Infer the
2178
2244
  * shape from the model id so a pair's model selects the right transport.
2179
2245
  *
2180
- * v2 takes the Responses API because OpenAI rejects function tools combined
2181
- * with `reasoning_effort` on chat completions and every task sends both. Legacy
2182
- * stays on chat completions, where litellm does that routing itself.
2246
+ * OpenAI models take the Responses API because OpenAI rejects function tools
2247
+ * combined with `reasoning_effort` on chat completions and every task sends both.
2183
2248
  */
2184
- function gatewayApiFor(modelId, edition = "legacy") {
2249
+ function gatewayApiFor(modelId, legacy) {
2185
2250
  if (!modelId.startsWith("openai/")) return "anthropic-messages";
2186
- return edition === "v2" ? "openai-responses" : "openai-completions";
2251
+ return legacy ? "openai-completions" : "openai-responses";
2187
2252
  }
2188
2253
  /**
2189
- * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path. The
2190
- * shape follows the gateway edition: legacy sends per-key metadata/flag
2191
- * headers plus the explicit Bedrock-fallback opt-in; v2 (the Go ai-gateway)
2192
- * takes one `X-PostHog-Properties` JSON blob and falls back natively. The 1M
2193
- * context beta rides both, since pi otherwise runs at 200k and overflows on larger
2194
- * projects (the post-run compaction failures).
2254
+ * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: one
2255
+ * `X-PostHog-Properties` JSON blob (Bedrock fallback is native, so no opt-in)
2256
+ * plus the 1M context beta, since pi otherwise runs at 200k and overflows on
2257
+ * larger projects (the post-run compaction failures).
2195
2258
  */
2196
- function buildGatewayHeaders(wizardMetadata, wizardFlags, edition = "legacy", teamId) {
2197
- const headers = { "anthropic-beta": "context-1m-2025-08-07" };
2198
- if (edition === "v2") {
2199
- headers["X-PostHog-Properties"] = buildWizardPropertiesBlob(wizardMetadata, wizardFlags, teamId);
2200
- return headers;
2201
- }
2202
- headers["x-posthog-use-bedrock-fallback"] = "true";
2203
- for (const [key, value] of Object.entries(wizardMetadata)) {
2204
- const name = key.startsWith("X-POSTHOG-PROPERTY-") ? key : `${POSTHOG_PROPERTY_HEADER_PREFIX}${key}`;
2205
- headers[name] = value;
2206
- }
2207
- for (const [flagKey, variant] of Object.entries(wizardFlags)) {
2208
- if (!flagKey.toLowerCase().startsWith("wizard")) continue;
2209
- headers[POSTHOG_FLAG_HEADER_PREFIX + flagKey.toUpperCase()] = variant;
2210
- }
2211
- return headers;
2259
+ function buildGatewayHeaders(wizardMetadata, wizardFlags, teamId, legacy) {
2260
+ return {
2261
+ "anthropic-beta": "context-1m-2025-08-07",
2262
+ ...legacy ? legacyGatewayHeaders(wizardMetadata, wizardFlags) : { "X-PostHog-Properties": buildWizardPropertiesBlob(wizardMetadata, wizardFlags, teamId) }
2263
+ };
2212
2264
  }
2213
2265
  /**
2214
2266
  * One gateway model spec — the single description of how to reach a model on
@@ -2216,8 +2268,8 @@ function buildGatewayHeaders(wizardMetadata, wizardFlags, edition = "legacy", te
2216
2268
  * callers (scan triage) hand it straight to `completeSimple`.
2217
2269
  */
2218
2270
  function buildGatewayModel(inputs) {
2219
- const { gatewayUrl, wizardMetadata, wizardFlags, modelId, edition, teamId } = inputs;
2220
- const api = gatewayApiFor(modelId, edition);
2271
+ const { gatewayUrl, wizardMetadata, wizardFlags, modelId, teamId, legacy } = inputs;
2272
+ const api = gatewayApiFor(modelId, legacy);
2221
2273
  return {
2222
2274
  id: modelId,
2223
2275
  name: `${modelId} (PostHog Gateway)`,
@@ -2234,7 +2286,7 @@ function buildGatewayModel(inputs) {
2234
2286
  },
2235
2287
  contextWindow: 1e6,
2236
2288
  maxTokens: 64e3,
2237
- headers: buildGatewayHeaders(wizardMetadata, wizardFlags, edition, teamId)
2289
+ headers: buildGatewayHeaders(wizardMetadata, wizardFlags, teamId, legacy)
2238
2290
  };
2239
2291
  }
2240
2292
  /**
@@ -2242,8 +2294,8 @@ function buildGatewayModel(inputs) {
2242
2294
  * plus the derived traits the session setup needs (`caps.thinkingLevel`).
2243
2295
  */
2244
2296
  function buildGatewayProvider(inputs) {
2245
- const { gatewayUrl, accessToken, modelId, effort, edition } = inputs;
2246
- const api = gatewayApiFor(modelId, edition);
2297
+ const { gatewayUrl, accessToken, modelId, effort, legacy } = inputs;
2298
+ const api = gatewayApiFor(modelId, legacy);
2247
2299
  const tableCaps = modelCapabilities(modelId);
2248
2300
  const caps = effort && effort !== "off" && tableCaps.reasoning ? modelCapabilities(modelId, effort) : effort === "off" && tableCaps.reasoning ? {
2249
2301
  ...tableCaps,
@@ -2266,6 +2318,53 @@ function buildGatewayProvider(inputs) {
2266
2318
  baseUrl: model.baseUrl
2267
2319
  };
2268
2320
  }
2321
+ /**
2322
+ * Whether a turn's error is the gateway rejecting the bearer. pi attaches the
2323
+ * SDK's own error to `diagnostics`, so its code decides when one is present.
2324
+ * The message match is the fallback for a turn that failed before pi built a
2325
+ * diagnostic, where the status survives only as prose.
2326
+ */
2327
+ function isGatewayAuthRejection(turn) {
2328
+ const { errorMessage, diagnostics } = typeof turn === "string" ? {
2329
+ errorMessage: turn,
2330
+ diagnostics: void 0
2331
+ } : turn ?? {};
2332
+ for (const diagnostic of diagnostics ?? []) {
2333
+ const code = diagnostic.error?.code;
2334
+ if (code === 401 || code === "401") return true;
2335
+ if (/^authentication_?error$/i.test(diagnostic.error?.name ?? "")) return true;
2336
+ }
2337
+ return /\b401\b|authentication_error|unauthorized/i.test(errorMessage ?? "");
2338
+ }
2339
+ /**
2340
+ * Wraps a pi session's prompt(): when a turn ends on a 401 from a bearer past
2341
+ * its refresh instant, mint once, re-register the provider with the new
2342
+ * bearer (pi resolves the apiKey per request), and continue. A 401 on a fresh
2343
+ * bearer, or a second one, is left to the harness's normal failure path.
2344
+ */
2345
+ function withGatewayRemint(opts) {
2346
+ let auth = opts.auth;
2347
+ let rejected = false;
2348
+ let reminted = false;
2349
+ return {
2350
+ noteAssistantTurn(message) {
2351
+ const turn = message;
2352
+ rejected = turn?.stopReason === "error" && isGatewayAuthRejection(turn);
2353
+ },
2354
+ async prompt(text) {
2355
+ rejected = false;
2356
+ await opts.session.prompt(text);
2357
+ if (!rejected || reminted || !isPastRefresh(auth)) return;
2358
+ reminted = true;
2359
+ auth = await opts.refreshAuth();
2360
+ opts.registry.registerProvider(GATEWAY_PROVIDER, buildGatewayProvider(opts.providerInputs(auth)).provider);
2361
+ opts.onRemint?.();
2362
+ rejected = false;
2363
+ const next = opts.continueText;
2364
+ await opts.session.prompt(typeof next === "function" ? next() : next);
2365
+ }
2366
+ };
2367
+ }
2269
2368
  //#endregion
2270
2369
  //#region src/lib/agent/triage-provider.ts
2271
2370
  const TRIAGE_MAX_TOKENS = 16384;
@@ -2274,24 +2373,28 @@ const TRIAGE_TIMEOUT_MS = 2e4;
2274
2373
  * Triage provider for a harness. Auth is always explicit: every caller already
2275
2374
  * holds the gateway url and the run's token, and reading them back out of
2276
2375
  * ANTHROPIC_* made an unauthed provider silent — it returned undefined, the
2277
- * caller failed closed, and a clean first-party skill got deleted.
2376
+ * caller failed closed, and a clean first-party skill got deleted. A resolver
2377
+ * is re-read on every call, so a run that re-mints mid-way scans with the
2378
+ * current bearer rather than the one it started with.
2278
2379
  */
2279
2380
  function createTriageLLMProvider(auth, harness) {
2280
- const { baseURL, authToken } = auth;
2381
+ const resolveAuth = typeof auth === "function" ? auth : () => Promise.resolve(auth);
2281
2382
  const modelId = triageModelFor(harness);
2282
- const model = buildGatewayModel({
2283
- gatewayUrl: baseURL,
2284
- accessToken: authToken,
2285
- edition: auth.edition,
2286
- teamId: auth.teamId,
2287
- wizardMetadata: auth?.wizardMetadata ?? {},
2288
- wizardFlags: auth?.wizardFlags ?? {},
2289
- modelId
2290
- });
2291
2383
  const { reasoning, thinkingLevel } = modelCapabilities(modelId);
2292
- logToFile(`[YARA] triage provider ready (model: ${modelId}, api: ${model.api})`);
2384
+ logToFile(`[YARA] triage provider ready (model: ${modelId}, api: ${gatewayApiFor(modelId)})`);
2293
2385
  return async (prompt) => {
2294
2386
  const { completeSimple } = await import("@earendil-works/pi-ai");
2387
+ const current = await resolveAuth();
2388
+ const authToken = current.authToken;
2389
+ const model = buildGatewayModel({
2390
+ gatewayUrl: current.baseURL,
2391
+ accessToken: authToken,
2392
+ teamId: current.teamId,
2393
+ legacy: current.legacy,
2394
+ wizardMetadata: current.wizardMetadata ?? {},
2395
+ wizardFlags: current.wizardFlags ?? {},
2396
+ modelId
2397
+ });
2295
2398
  const controller = new AbortController();
2296
2399
  const timer = setTimeout(() => controller.abort(), TRIAGE_TIMEOUT_MS);
2297
2400
  try {
@@ -2503,6 +2606,15 @@ var AgentOutputSignals = class {
2503
2606
  if (SIGNAL_NEEDLES.some((n) => text.includes(n))) this.lines.push(text);
2504
2607
  }
2505
2608
  /**
2609
+ * Drop the retained API-error lines and keep every other signal. Used after
2610
+ * a re-mint so the run is judged on the resumed session, not on the 401
2611
+ * that ended the first one.
2612
+ */
2613
+ forgetApiErrors() {
2614
+ const kept = this.lines.filter((line) => !line.includes(OUTPUT_SIGNALS.API_ERROR));
2615
+ this.lines.splice(0, this.lines.length, ...kept);
2616
+ }
2617
+ /**
2506
2618
  * Record the SDK's `apiKeySource` from its `init` message (e.g.
2507
2619
  * `"/login managed key"`, `"ANTHROPIC_API_KEY"`). Used to triage a 401:
2508
2620
  * a managed-login source means the SDK authenticated with a stored Claude
@@ -3118,10 +3230,10 @@ function createStopHook(featureQueue, signals, requestRemark = true) {
3118
3230
  }
3119
3231
  /**
3120
3232
  * Global identifiers attached to every LLM gateway trace for a run. They ride on
3121
- * each `$ai_generation` the gateway emits (as `X-POSTHOG-PROPERTY-*` headers via
3122
- * `buildAgentEnv`), so traces are filterable by program, framework, run, and build
3123
- * type for cost attribution and dashboards. `skill_id` is omitted when the run has
3124
- * none.
3233
+ * each `$ai_generation` the gateway emits (in the `X-PostHog-Properties` blob
3234
+ * `buildAgentEnv` builds), so traces are filterable by program, framework, run,
3235
+ * and build type for cost attribution and dashboards. `skill_id` is omitted when
3236
+ * the run has none.
3125
3237
  */
3126
3238
  function buildRunTags(args) {
3127
3239
  return {
@@ -3145,24 +3257,15 @@ function isWarlockDisabled() {
3145
3257
  return runtimeEnv("POSTHOG_WIZARD_WARLOCK_DISABLED") === "true";
3146
3258
  }
3147
3259
  /**
3148
- * Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS.
3149
- * The header shape follows the gateway edition. Legacy (Python gateway):
3150
- * per-key `X-POSTHOG-PROPERTY-*`/`X-POSTHOG-FLAG-*` plus the explicit
3151
- * `x-posthog-use-bedrock-fallback` opt-in. v2 (Go ai-gateway): one
3152
- * `X-PostHog-Properties` JSON blob. Bedrock fallback is native there, and
3153
- * per-key metadata headers are not read.
3260
+ * Build ANTHROPIC_CUSTOM_HEADERS for the SDK subprocess: the run's metadata and
3261
+ * flags as one `X-PostHog-Properties` JSON blob. Bedrock fallback is native to
3262
+ * the gateway, so there is no opt-in header. The CI fallback sends the legacy
3263
+ * gateway's shape instead.
3154
3264
  */
3155
3265
  function buildAgentEnv(wizardMetadata, wizardFlags, auth) {
3156
3266
  const headers = createCustomHeaders();
3157
- if (auth?.edition === "v2") headers.add("X-PostHog-Properties", buildWizardPropertiesBlob(wizardMetadata, wizardFlags, auth.teamId));
3158
- else {
3159
- headers.add("x-posthog-use-bedrock-fallback", "true");
3160
- for (const [key, value] of Object.entries(wizardMetadata)) headers.add(key.startsWith("X-POSTHOG-PROPERTY-") ? key : `${POSTHOG_PROPERTY_HEADER_PREFIX}${key}`, value);
3161
- for (const [flagKey, variant] of Object.entries(wizardFlags)) {
3162
- if (!flagKey.toLowerCase().startsWith("wizard")) continue;
3163
- headers.addFlag(flagKey, variant);
3164
- }
3165
- }
3267
+ const shaped = auth.legacy ? legacyGatewayHeaders(wizardMetadata, wizardFlags) : { "X-PostHog-Properties": buildWizardPropertiesBlob(wizardMetadata, wizardFlags, auth.teamId) };
3268
+ for (const [key, value] of Object.entries(shaped)) headers.add(key, value);
3166
3269
  const encoded = headers.encode();
3167
3270
  logToFile("ANTHROPIC_CUSTOM_HEADERS", encoded);
3168
3271
  return encoded;
@@ -3256,22 +3359,26 @@ async function initializeAgent(config, options) {
3256
3359
  logToFile("Install directory:", options.installDir);
3257
3360
  try {
3258
3361
  process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "true";
3259
- const auth = await gatewayAuth(config.host, config.posthogApiKey, config.programId);
3362
+ const currentGatewayAuth = () => gatewayAuth(config.host, config.posthogApiKey, config.programId);
3363
+ const auth = await currentGatewayAuth();
3260
3364
  const gatewayUrl = auth.gatewayUrl;
3261
3365
  process.env.ANTHROPIC_BASE_URL = gatewayUrl;
3262
3366
  process.env.ANTHROPIC_AUTH_TOKEN = auth.token;
3263
3367
  process.env.CLAUDE_CODE_OAUTH_TOKEN = auth.token;
3264
- logToFile("Gateway edition:", auth.edition);
3265
- const triageProvider = createTriageLLMProvider({
3266
- baseURL: gatewayUrl,
3267
- authToken: auth.token,
3268
- edition: auth.edition,
3269
- teamId: auth.teamId,
3270
- wizardMetadata: {
3271
- ...config.wizardMetadata ?? {},
3272
- call_type: "yara-triage"
3273
- },
3274
- wizardFlags: config.wizardFlags ?? {}
3368
+ const triageMetadata = {
3369
+ ...config.wizardMetadata ?? {},
3370
+ call_type: "yara-triage"
3371
+ };
3372
+ const triageProvider = createTriageLLMProvider(async () => {
3373
+ const current = await currentGatewayAuth();
3374
+ return {
3375
+ baseURL: current.gatewayUrl,
3376
+ authToken: current.token,
3377
+ teamId: current.teamId,
3378
+ legacy: current.legacy,
3379
+ wizardMetadata: triageMetadata,
3380
+ wizardFlags: config.wizardFlags ?? {}
3381
+ };
3275
3382
  }, "anthropic");
3276
3383
  logToFile("Configured LLM gateway:", gatewayUrl);
3277
3384
  logToFile("API key prefix:", config.posthogApiKey ? `${config.posthogApiKey.slice(0, 4)}***` : "(missing)");
@@ -3309,7 +3416,7 @@ async function initializeAgent(config, options) {
3309
3416
  orchestrator: config.orchestrator,
3310
3417
  triageProvider
3311
3418
  });
3312
- const model = config.modelOverride ?? "claude-sonnet-4-6";
3419
+ const model = config.modelOverride ?? "claude-sonnet-5";
3313
3420
  const agentRunConfig = {
3314
3421
  workingDirectory: config.workingDirectory,
3315
3422
  mcpServers,
@@ -3324,6 +3431,7 @@ async function initializeAgent(config, options) {
3324
3431
  capture: config.capture,
3325
3432
  triageProvider,
3326
3433
  gatewayAuth: auth,
3434
+ refreshGatewayAuth: currentGatewayAuth,
3327
3435
  program: config.integrationLabel,
3328
3436
  sequence: config.orchestrator ? "orchestrator" : "linear"
3329
3437
  };
@@ -3369,21 +3477,23 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3369
3477
  let loggedInitialContext = false;
3370
3478
  let lastResultMessage = null;
3371
3479
  const tasks = /* @__PURE__ */ new Map();
3372
- let signalDone;
3373
- const resultReceived = new Promise((resolve) => {
3374
- signalDone = resolve;
3375
- });
3376
- const createPromptStream = async function* () {
3377
- yield {
3378
- type: "user",
3379
- session_id: "",
3380
- message: {
3381
- role: "user",
3382
- content: prompt
3383
- },
3384
- parent_tool_use_id: null
3385
- };
3386
- await resultReceived;
3480
+ let signalDone = () => void 0;
3481
+ const createPromptStream = (text) => {
3482
+ const resultReceived = new Promise((resolve) => {
3483
+ signalDone = resolve;
3484
+ });
3485
+ return (async function* () {
3486
+ yield {
3487
+ type: "user",
3488
+ session_id: "",
3489
+ message: {
3490
+ role: "user",
3491
+ content: text
3492
+ },
3493
+ parent_tool_use_id: null
3494
+ };
3495
+ await resultReceived;
3496
+ })();
3387
3497
  };
3388
3498
  const completeWithSuccess = (suppressedError) => {
3389
3499
  const durationMs = Date.now() - startTime;
@@ -3419,9 +3529,13 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3419
3529
  spinner.stop(successMessage);
3420
3530
  return {};
3421
3531
  };
3422
- const abortController = new AbortController();
3532
+ let abortController = new AbortController();
3423
3533
  let abortReason = null;
3424
3534
  let yaraViolationReason = null;
3535
+ let sessionId;
3536
+ let reminted = false;
3537
+ let remintRequested = false;
3538
+ const agentConfigDir = createIsolatedAgentConfigDir();
3425
3539
  try {
3426
3540
  const disallow = new Set(agentConfig.disallowedTools ?? []);
3427
3541
  const allowedTools = [...BASE_ALLOWED_TOOLS, ...agentConfig.allowedTools ?? []].filter((t) => !disallow.has(t));
@@ -3440,197 +3554,228 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
3440
3554
  analytics.wizardCapture("warlock disabled", { reason: "env-override" });
3441
3555
  }
3442
3556
  agentConfig.capture?.setInitialPrompt(prompt);
3443
- const response = query({
3444
- prompt: createPromptStream(),
3445
- options: {
3446
- abortController,
3447
- model: agentConfig.model,
3448
- cwd: agentConfig.workingDirectory,
3449
- permissionMode: "acceptEdits",
3450
- betas: ["context-1m-2025-08-07"],
3451
- mcpServers: agentConfig.mcpServers,
3452
- agents: { "general-purpose": {
3453
- description: "General-purpose subagent. Inherits the parent run's tools plus the PostHog and wizard-tools MCP servers, so it can call mcp__posthog-wizard__* directly instead of curling the REST API.",
3454
- prompt: "You are a general-purpose subagent for the PostHog wizard. Prefer the authenticated mcp__posthog-wizard__* MCP tools over raw HTTP — they are already authenticated for this project. Only fall back to other transports if no MCP tool covers the operation.",
3455
- mcpServers: inheritedMcpServerNames,
3456
- disallowedTools: agentConfig.disallowedTools ? [...agentConfig.disallowedTools] : void 0
3457
- } },
3458
- settingSources: ["project"],
3459
- skills: "all",
3460
- allowedTools,
3461
- sandbox: {
3462
- enabled: true,
3463
- failIfUnavailable: false,
3464
- allowUnsandboxedCommands: false,
3465
- filesystem: { allowWrite: [
3466
- "/" + agentConfig.workingDirectory,
3467
- "/" + agentConfig.workingDirectory + "/**",
3468
- "//tmp",
3469
- "//tmp/**",
3470
- "//private/tmp",
3471
- "//private/tmp/**",
3472
- "~/Library/pnpm/**",
3473
- "~/.local/share/pnpm/**",
3474
- "~/.pnpm-store/**",
3475
- "~/.npm/**",
3476
- "~/.yarn/**",
3477
- "~/.bun/install/**",
3478
- "~/.cache/node/corepack/**",
3479
- "~/Library/Caches/node/corepack/**",
3480
- "~/.volta/**",
3481
- "~/.cache/pip/**",
3482
- "~/Library/Caches/pip/**",
3483
- "~/.cache/uv/**",
3484
- "~/Library/Caches/uv/**",
3485
- "~/.cache/pypoetry/**",
3486
- "~/Library/Caches/pypoetry/**",
3487
- "~/.bundle/**",
3488
- "~/.gem/**"
3489
- ] },
3490
- network: { allowedDomains: [
3491
- "github.com",
3492
- "api.github.com",
3493
- "raw.githubusercontent.com",
3494
- "release-assets.githubusercontent.com",
3495
- "objects.githubusercontent.com"
3496
- ] }
3497
- },
3498
- env: {
3499
- ...sanitizeAgentSubprocessEnv(process.env),
3500
- ANTHROPIC_BASE_URL: agentConfig.gatewayAuth.gatewayUrl,
3501
- ANTHROPIC_AUTH_TOKEN: agentConfig.gatewayAuth.token,
3502
- CLAUDE_CODE_OAUTH_TOKEN: agentConfig.gatewayAuth.token,
3503
- CLAUDE_CONFIG_DIR: createIsolatedAgentConfigDir(),
3504
- CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "true",
3505
- POSTHOG_MCP_TOKEN: agentConfig.posthogApiKey,
3506
- MCP_CONNECTION_NONBLOCKING: "0",
3507
- ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(agentConfig.wizardMetadata ?? {}, agentConfig.wizardFlags ?? {}, agentConfig.gatewayAuth)
3508
- },
3509
- canUseTool: (toolName, input) => {
3510
- logToFile("canUseTool called:", {
3511
- toolName,
3512
- input
3513
- });
3514
- const result = wizardCanUseTool(toolName, input, {
3515
- wizardAskPending: agentConfig.getPendingQuestion?.() != null,
3516
- disallowedTools: agentConfig.disallowedTools
3517
- });
3518
- logToFile("canUseTool result:", result);
3519
- return Promise.resolve(result);
3520
- },
3521
- systemPrompt: {
3522
- type: "preset",
3523
- preset: "claude_code",
3524
- append: assembleCommandments({
3525
- program: agentConfig.program,
3526
- sequence: agentConfig.sequence,
3527
- harness: "anthropic"
3528
- })
3529
- },
3530
- tools: {
3531
- type: "preset",
3532
- preset: "claude_code"
3533
- },
3534
- stderr: (data) => {
3535
- logToFile("CLI stderr:", data);
3536
- if (options.debug) debug("CLI stderr:", data);
3537
- },
3538
- hooks: {
3539
- PreToolUse: warlockDisabled ? [] : createPreToolUseYaraHooks(triageProvider, onYaraTerminate),
3540
- PostToolUse: warlockDisabled ? [] : createPostToolUseYaraHooks(triageProvider, onYaraTerminate),
3541
- Stop: [{
3542
- hooks: [createStopHook(config?.additionalFeatureQueue ?? [], signals, config?.requestRemark ?? true)],
3543
- timeout: 30
3544
- }]
3545
- }
3546
- }
3547
- });
3548
- for await (const message of response) {
3549
- if (!loggedInitialContext && message.type === "assistant") {
3550
- const usage = message.message?.usage;
3551
- if (usage) {
3552
- const input = usage.input_tokens ?? 0;
3553
- const cacheCreation = usage.cache_creation_input_tokens ?? 0;
3554
- const cacheRead = usage.cache_read_input_tokens ?? 0;
3555
- const initialTokens = input + cacheCreation + cacheRead;
3556
- logToFile(`Initial context: ${initialTokens} tokens (input=${input}, cache_creation=${cacheCreation}, cache_read=${cacheRead})`);
3557
- analytics.wizardCapture("agent initial context", {
3558
- initial_tokens: initialTokens,
3559
- input_tokens: input,
3560
- cache_creation_input_tokens: cacheCreation,
3561
- cache_read_input_tokens: cacheRead
3562
- });
3557
+ const runQuery = async (resume) => {
3558
+ const response = query({
3559
+ prompt: createPromptStream(resume ? RESUME_INSTRUCTION : prompt),
3560
+ options: {
3561
+ abortController,
3562
+ resume,
3563
+ model: agentConfig.model,
3564
+ cwd: agentConfig.workingDirectory,
3565
+ permissionMode: "acceptEdits",
3566
+ betas: ["context-1m-2025-08-07"],
3567
+ mcpServers: agentConfig.mcpServers,
3568
+ agents: { "general-purpose": {
3569
+ description: "General-purpose subagent. Inherits the parent run's tools plus the PostHog and wizard-tools MCP servers, so it can call mcp__posthog-wizard__* directly instead of curling the REST API.",
3570
+ prompt: "You are a general-purpose subagent for the PostHog wizard. Prefer the authenticated mcp__posthog-wizard__* MCP tools over raw HTTP — they are already authenticated for this project. Only fall back to other transports if no MCP tool covers the operation.",
3571
+ mcpServers: inheritedMcpServerNames,
3572
+ disallowedTools: agentConfig.disallowedTools ? [...agentConfig.disallowedTools] : void 0
3573
+ } },
3574
+ settingSources: ["project"],
3575
+ skills: "all",
3576
+ allowedTools,
3577
+ sandbox: {
3578
+ enabled: true,
3579
+ failIfUnavailable: false,
3580
+ allowUnsandboxedCommands: false,
3581
+ filesystem: { allowWrite: [
3582
+ "/" + agentConfig.workingDirectory,
3583
+ "/" + agentConfig.workingDirectory + "/**",
3584
+ "//tmp",
3585
+ "//tmp/**",
3586
+ "//private/tmp",
3587
+ "//private/tmp/**",
3588
+ "~/Library/pnpm/**",
3589
+ "~/.local/share/pnpm/**",
3590
+ "~/.pnpm-store/**",
3591
+ "~/.npm/**",
3592
+ "~/.yarn/**",
3593
+ "~/.bun/install/**",
3594
+ "~/.cache/node/corepack/**",
3595
+ "~/Library/Caches/node/corepack/**",
3596
+ "~/.volta/**",
3597
+ "~/.cache/pip/**",
3598
+ "~/Library/Caches/pip/**",
3599
+ "~/.cache/uv/**",
3600
+ "~/Library/Caches/uv/**",
3601
+ "~/.cache/pypoetry/**",
3602
+ "~/Library/Caches/pypoetry/**",
3603
+ "~/.bundle/**",
3604
+ "~/.gem/**"
3605
+ ] },
3606
+ network: { allowedDomains: [
3607
+ "github.com",
3608
+ "api.github.com",
3609
+ "raw.githubusercontent.com",
3610
+ "release-assets.githubusercontent.com",
3611
+ "objects.githubusercontent.com",
3612
+ new URL(AWS_SKILLS_BASE_URL).hostname
3613
+ ] }
3614
+ },
3615
+ env: {
3616
+ ...sanitizeAgentSubprocessEnv(process.env),
3617
+ ANTHROPIC_BASE_URL: agentConfig.gatewayAuth.gatewayUrl,
3618
+ ANTHROPIC_AUTH_TOKEN: agentConfig.gatewayAuth.token,
3619
+ CLAUDE_CODE_OAUTH_TOKEN: agentConfig.gatewayAuth.token,
3620
+ CLAUDE_CONFIG_DIR: agentConfigDir,
3621
+ CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "true",
3622
+ POSTHOG_MCP_TOKEN: agentConfig.posthogApiKey,
3623
+ MCP_CONNECTION_NONBLOCKING: "0",
3624
+ ANTHROPIC_CUSTOM_HEADERS: buildAgentEnv(agentConfig.wizardMetadata ?? {}, agentConfig.wizardFlags ?? {}, agentConfig.gatewayAuth)
3625
+ },
3626
+ canUseTool: (toolName, input) => {
3627
+ logToFile("canUseTool called:", {
3628
+ toolName,
3629
+ input
3630
+ });
3631
+ const result = wizardCanUseTool(toolName, input, {
3632
+ wizardAskPending: agentConfig.getPendingQuestion?.() != null,
3633
+ disallowedTools: agentConfig.disallowedTools
3634
+ });
3635
+ logToFile("canUseTool result:", result);
3636
+ return Promise.resolve(result);
3637
+ },
3638
+ systemPrompt: {
3639
+ type: "preset",
3640
+ preset: "claude_code",
3641
+ append: assembleCommandments({
3642
+ program: agentConfig.program,
3643
+ sequence: agentConfig.sequence,
3644
+ harness: "anthropic"
3645
+ })
3646
+ },
3647
+ tools: {
3648
+ type: "preset",
3649
+ preset: "claude_code"
3650
+ },
3651
+ stderr: (data) => {
3652
+ logToFile("CLI stderr:", data);
3653
+ if (options.debug) debug("CLI stderr:", data);
3654
+ },
3655
+ hooks: {
3656
+ PreToolUse: warlockDisabled ? [] : createPreToolUseYaraHooks(triageProvider, onYaraTerminate),
3657
+ PostToolUse: warlockDisabled ? [] : createPostToolUseYaraHooks(triageProvider, onYaraTerminate),
3658
+ Stop: [{
3659
+ hooks: [createStopHook(config?.additionalFeatureQueue ?? [], signals, config?.requestRemark ?? true)],
3660
+ timeout: 30
3661
+ }]
3662
+ }
3563
3663
  }
3564
- loggedInitialContext = true;
3565
- }
3566
- agentConfig.capture?.captureFromAnthropicSDKMessage(message);
3567
- handleSDKMessage(message, options, spinner, signals, receivedSuccessResult, tasks, agentConfig.suppressTaskRender ?? false, emitStepEvents, resolveStepKey);
3568
- if (abortCases.length > 0 && !abortReason && message.type === "assistant") {
3569
- const content = message.message?.content;
3570
- if (Array.isArray(content)) {
3571
- for (const block of content) if (block.type === "text" && typeof block.text === "string") {
3572
- const match = block.text.match(/\[ABORT\]\s*(.+?)(?:\n|$)/);
3573
- if (match) {
3574
- abortReason = match[1].trim();
3575
- logToFile(`Agent emitted [ABORT]: ${abortReason}`);
3664
+ });
3665
+ try {
3666
+ for await (const message of response) {
3667
+ if (typeof message.session_id === "string" && message.session_id) sessionId = message.session_id;
3668
+ if (!loggedInitialContext && message.type === "assistant") {
3669
+ const usage = message.message?.usage;
3670
+ if (usage) {
3671
+ const input = usage.input_tokens ?? 0;
3672
+ const cacheCreation = usage.cache_creation_input_tokens ?? 0;
3673
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
3674
+ const initialTokens = input + cacheCreation + cacheRead;
3675
+ logToFile(`Initial context: ${initialTokens} tokens (input=${input}, cache_creation=${cacheCreation}, cache_read=${cacheRead})`);
3676
+ analytics.wizardCapture("agent initial context", {
3677
+ initial_tokens: initialTokens,
3678
+ input_tokens: input,
3679
+ cache_creation_input_tokens: cacheCreation,
3680
+ cache_read_input_tokens: cacheRead
3681
+ });
3682
+ }
3683
+ loggedInitialContext = true;
3684
+ }
3685
+ agentConfig.capture?.captureFromAnthropicSDKMessage(message);
3686
+ handleSDKMessage(message, options, spinner, signals, receivedSuccessResult, tasks, agentConfig.suppressTaskRender ?? false, emitStepEvents, resolveStepKey);
3687
+ if (abortCases.length > 0 && !abortReason && message.type === "assistant") {
3688
+ const content = message.message?.content;
3689
+ if (Array.isArray(content)) {
3690
+ for (const block of content) if (block.type === "text" && typeof block.text === "string") {
3691
+ const match = block.text.match(/\[ABORT\]\s*(.+?)(?:\n|$)/);
3692
+ if (match) {
3693
+ abortReason = match[1].trim();
3694
+ logToFile(`Agent emitted [ABORT]: ${abortReason}`);
3695
+ abortController.abort();
3696
+ signalDone();
3697
+ break;
3698
+ }
3699
+ }
3700
+ }
3701
+ }
3702
+ if (message.type === "assistant" && signals.hasApiErrorStatus(401)) {
3703
+ signalDone();
3704
+ if (agentConfig.refreshGatewayAuth && !reminted && isPastRefresh(agentConfig.gatewayAuth)) {
3705
+ logToFile("Agent error: 401 on an aged gateway bearer; re-minting");
3706
+ remintRequested = true;
3576
3707
  abortController.abort();
3577
- signalDone();
3578
3708
  break;
3579
3709
  }
3710
+ spinner.stop("Authentication failed");
3711
+ const authError = buildAuthErrorContext(options.installDir, agentConfig.gatewayAuth.gatewayUrl, os.homedir(), signals.apiKeySource);
3712
+ const sessionExpired = isGrantRevoked();
3713
+ const authCode = classifyAuthFailure({
3714
+ hasSettingsConflict: authError.hasSettingsConflict,
3715
+ usingManagedLogin: authError.usingManagedLogin,
3716
+ sessionExpired,
3717
+ apiKey: options.apiKey,
3718
+ gatewayRegion: authError.region,
3719
+ sessionRegion: options.cloudRegion
3720
+ });
3721
+ logToFile("Agent error: 401, showing auth error screen", {
3722
+ ...authError,
3723
+ sessionExpired
3724
+ });
3725
+ getUI().showAuthError({
3726
+ hasSettingsConflict: authError.hasSettingsConflict,
3727
+ conflicts: authError.conflicts,
3728
+ usingManagedLogin: authError.usingManagedLogin,
3729
+ credentialPlaces: authError.credentialPlaces,
3730
+ sessionExpired,
3731
+ logFilePath: getLogFilePath()
3732
+ });
3733
+ await wizardAbort({
3734
+ code: authCode,
3735
+ message: "Authentication failed (401)",
3736
+ error: new WizardError("Authentication failed", {
3737
+ hasSettingsConflict: authError.hasSettingsConflict,
3738
+ conflictSources: authError.conflictSources,
3739
+ conflictKeys: authError.conflictKeys,
3740
+ gatewayUrl: authError.gatewayUrl,
3741
+ region: authError.region,
3742
+ usingManagedLogin: authError.usingManagedLogin,
3743
+ apiKeySource: authError.apiKeySource
3744
+ }, authCode)
3745
+ });
3746
+ }
3747
+ try {
3748
+ middleware?.onMessage(message);
3749
+ } catch (e) {
3750
+ logToFile(`${AgentSignals.BENCHMARK} Middleware onMessage error:`, e);
3751
+ }
3752
+ if (message.type === "result") {
3753
+ if (message.subtype === "success" && !message.is_error) {
3754
+ receivedSuccessResult = true;
3755
+ lastResultMessage = message;
3756
+ }
3757
+ signalDone();
3580
3758
  }
3581
3759
  }
3760
+ } catch (error) {
3761
+ if (remintRequested) return "remint";
3762
+ throw error;
3582
3763
  }
3583
- if (message.type === "assistant" && signals.hasApiErrorStatus(401)) {
3584
- signalDone();
3585
- spinner.stop("Authentication failed");
3586
- const authError = buildAuthErrorContext(options.installDir, agentConfig.gatewayAuth.gatewayUrl, os.homedir(), signals.apiKeySource);
3587
- const sessionExpired = isGrantRevoked();
3588
- const authCode = classifyAuthFailure({
3589
- hasSettingsConflict: authError.hasSettingsConflict,
3590
- usingManagedLogin: authError.usingManagedLogin,
3591
- sessionExpired,
3592
- apiKey: options.apiKey,
3593
- gatewayRegion: authError.region,
3594
- sessionRegion: options.cloudRegion
3595
- });
3596
- logToFile("Agent error: 401, showing auth error screen", {
3597
- ...authError,
3598
- sessionExpired
3599
- });
3600
- getUI().showAuthError({
3601
- hasSettingsConflict: authError.hasSettingsConflict,
3602
- conflicts: authError.conflicts,
3603
- usingManagedLogin: authError.usingManagedLogin,
3604
- credentialPlaces: authError.credentialPlaces,
3605
- sessionExpired,
3606
- logFilePath: getLogFilePath()
3607
- });
3608
- await wizardAbort({
3609
- code: authCode,
3610
- message: "Authentication failed (401)",
3611
- error: new WizardError("Authentication failed", {
3612
- hasSettingsConflict: authError.hasSettingsConflict,
3613
- conflictSources: authError.conflictSources,
3614
- conflictKeys: authError.conflictKeys,
3615
- gatewayUrl: authError.gatewayUrl,
3616
- region: authError.region,
3617
- usingManagedLogin: authError.usingManagedLogin,
3618
- apiKeySource: authError.apiKeySource
3619
- }, authCode)
3620
- });
3621
- }
3622
- try {
3623
- middleware?.onMessage(message);
3624
- } catch (e) {
3625
- logToFile(`${AgentSignals.BENCHMARK} Middleware onMessage error:`, e);
3626
- }
3627
- if (message.type === "result") {
3628
- if (message.subtype === "success" && !message.is_error) {
3629
- receivedSuccessResult = true;
3630
- lastResultMessage = message;
3631
- }
3632
- signalDone();
3633
- }
3764
+ return remintRequested ? "remint" : "done";
3765
+ };
3766
+ const refreshGatewayAuth = agentConfig.refreshGatewayAuth;
3767
+ if (await runQuery() === "remint" && refreshGatewayAuth) {
3768
+ reminted = true;
3769
+ remintRequested = false;
3770
+ abortController = new AbortController();
3771
+ signals.forgetApiErrors();
3772
+ spinner.message("Renewing the gateway token...");
3773
+ const stale = agentConfig.gatewayAuth;
3774
+ agentConfig.gatewayAuth = await refreshGatewayAuth();
3775
+ logToFile(`Gateway token renewed after a 401 (${Math.round((Date.now() - stale.refreshAtMs) / 1e3)}s past refresh); resuming session ${sessionId ?? "(none: fresh session)"}`);
3776
+ analytics.wizardCapture("gateway token reminted", { resumed: sessionId !== void 0 });
3777
+ spinner.message(spinnerMessage);
3778
+ await runQuery(sessionId);
3634
3779
  }
3635
3780
  if (yaraViolationReason) {
3636
3781
  logToFile("Agent error: YARA_VIOLATION");
@@ -4000,6 +4145,6 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
4000
4145
  }
4001
4146
  }
4002
4147
  //#endregion
4003
- export { checkEnvKeys as A, vaultSensitiveAnswers as B, CHECK_ENV_KEYS_DESCRIPTION as C, WIZARD_ASK_SUBJECT_DESCRIPTION as D, WIZARD_ASK_SENSITIVE_DESCRIPTION as E, mergeEnvValues as F, MAX_ENV_KEY_SET as G, createWizardAskBridge as H, normaliseAskSubject as I, toPromptSafeRelativePath as J, isEnvFileName as K, resolveEnvPath as L, downloadSkill as M, fetchSkillMenu as N, WIZARD_ASK_TOOL_DESCRIPTION as O, installSkillById as P, resolveEnvSecretRefs as R, publishHandoff as S, ENV_FILE_PATH_DESCRIPTION as T, isFullyCancelled as U, fetchWithRetry as V, gatewayAuth as W, markGrantRevoked as Y, buildGatewayProvider as _, wizardCanUseTool as a, PUBLISH_HANDOFF_DESCRIPTION as b, checkAllSettingsConflicts as c, restoreClaudeSettings as d, sanitizeAgentSubprocessEnv as f, GATEWAY_PROVIDER as g, createTriageLLMProvider as h, runAgent as i, createAskAccounting as j, WIZARD_TOOL_NAMES as k, classifySettingsConflicts as l, assembleCommandments as m, buildRunTags as n, createIsolatedAgentConfigDir as o, AgentOutputSignals as p, parseEnvKeyNames as q, initializeAgent as r, backupAndFixClaudeSettings as s, buildAgentEnv as t, recoverOrphanedSettingsBackups as u, createSecretVault as v, CHECK_ENV_KEYS_FILE_PATH_DESCRIPTION as w, PUBLISH_HANDOFF_TOOL_NAME as x, PUBLISH_HANDOFF_CONTENT_DESCRIPTION as y, templateEnvWriteRefusal as z };
4148
+ export { WIZARD_TOOL_NAMES as A, templateEnvWriteRefusal as B, publishHandoff as C, WIZARD_ASK_SENSITIVE_DESCRIPTION as D, ENV_FILE_PATH_DESCRIPTION as E, installSkillById as F, gatewayAuth as G, fetchWithRetry as H, mergeEnvValues as I, isEnvFileName as J, setLegacyGatewayFallback as K, normaliseAskSubject as L, createAskAccounting as M, downloadSkill as N, WIZARD_ASK_SUBJECT_DESCRIPTION as O, fetchSkillMenu as P, resolveEnvPath as R, PUBLISH_HANDOFF_TOOL_NAME as S, CHECK_ENV_KEYS_FILE_PATH_DESCRIPTION as T, createWizardAskBridge as U, vaultSensitiveAnswers as V, isFullyCancelled as W, toPromptSafeRelativePath as X, parseEnvKeyNames as Y, markGrantRevoked as Z, buildGatewayProvider as _, wizardCanUseTool as a, PUBLISH_HANDOFF_CONTENT_DESCRIPTION as b, checkAllSettingsConflicts as c, restoreClaudeSettings as d, sanitizeAgentSubprocessEnv as f, GATEWAY_PROVIDER as g, createTriageLLMProvider as h, runAgent as i, checkEnvKeys as j, WIZARD_ASK_TOOL_DESCRIPTION as k, classifySettingsConflicts as l, assembleCommandments as m, buildRunTags as n, createIsolatedAgentConfigDir as o, AgentOutputSignals as p, MAX_ENV_KEY_SET as q, initializeAgent as r, backupAndFixClaudeSettings as s, buildAgentEnv as t, recoverOrphanedSettingsBackups as u, withGatewayRemint as v, CHECK_ENV_KEYS_DESCRIPTION as w, PUBLISH_HANDOFF_DESCRIPTION as x, createSecretVault as y, resolveEnvSecretRefs as z };
4004
4149
 
4005
- //# sourceMappingURL=agent-interface-CQ_iz7g7.js.map
4150
+ //# sourceMappingURL=agent-interface-CK6pmvkF.js.map