@posthog/wizard 2.24.1 → 2.26.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 (69) hide show
  1. package/dist/{add-mcp-server-to-clients-CfwEQT_z.js → add-mcp-server-to-clients-C58l_KpV.js} +4 -4
  2. package/dist/{add-mcp-server-to-clients-CfwEQT_z.js.map → add-mcp-server-to-clients-C58l_KpV.js.map} +1 -1
  3. package/dist/{agent-interface-D1vtN6Wn.js → agent-interface-Dq_4h2eN.js} +435 -45
  4. package/dist/agent-interface-Dq_4h2eN.js.map +1 -0
  5. package/dist/{agent-runner-CBbkS0Ro.js → agent-runner-BNGW3osc.js} +748 -132
  6. package/dist/agent-runner-BNGW3osc.js.map +1 -0
  7. package/dist/{analytics-CUr82BDl.js → analytics-BX3LKPch.js} +51 -17
  8. package/dist/analytics-BX3LKPch.js.map +1 -0
  9. package/dist/{api-CI3Z74NG.js → api-DCHci5SD.js} +9 -5
  10. package/dist/api-DCHci5SD.js.map +1 -0
  11. package/dist/bin.js +830 -120
  12. package/dist/bin.js.map +1 -1
  13. package/dist/{ci-install-D_kxNmbJ.js → ci-install-CHIbwXio.js} +5 -5
  14. package/dist/{ci-install-D_kxNmbJ.js.map → ci-install-CHIbwXio.js.map} +1 -1
  15. package/dist/{debug-DxA_f5QT.js → debug-BizeRFR0.js} +17 -8
  16. package/dist/debug-BizeRFR0.js.map +1 -0
  17. package/dist/{debug-zMvpNYb2.js → debug-fg4BAKKA.js} +1 -1
  18. package/dist/{environment-CyS37cmM.js → environment-DS5Pq9Wm.js} +3 -3
  19. package/dist/{environment-CyS37cmM.js.map → environment-DS5Pq9Wm.js.map} +1 -1
  20. package/dist/{interactive-CG6FFqSw.js → interactive-DE3WDjk7.js} +3 -3
  21. package/dist/{interactive-CG6FFqSw.js.map → interactive-DE3WDjk7.js.map} +1 -1
  22. package/dist/{mcp-prompt-streaming-DQz4FSb1.js → mcp-prompt-streaming-zsYd1zJx.js} +7 -26
  23. package/dist/mcp-prompt-streaming-zsYd1zJx.js.map +1 -0
  24. package/dist/{non-interactive-DWtHX3ZR.js → non-interactive-DNah9u3t.js} +2 -2
  25. package/dist/{non-interactive-DWtHX3ZR.js.map → non-interactive-DNah9u3t.js.map} +1 -1
  26. package/dist/{package-manager-BWUS4CP0.js → package-manager-Dma9-zGs.js} +2 -2
  27. package/dist/{package-manager-BWUS4CP0.js.map → package-manager-Dma9-zGs.js.map} +1 -1
  28. package/dist/{playground-D7AhMMF5.js → playground-Cwe0Q9HW.js} +146 -49
  29. package/dist/playground-Cwe0Q9HW.js.map +1 -0
  30. package/dist/{posthog-integration-DexZ2uHU.js → posthog-integration-CAYZdk0r.js} +11 -11
  31. package/dist/{posthog-integration-DexZ2uHU.js.map → posthog-integration-CAYZdk0r.js.map} +1 -1
  32. package/dist/{provisioning-9c-AQbsa.js → provisioning-BmL4ro-o.js} +10 -6
  33. package/dist/{provisioning-9c-AQbsa.js.map → provisioning-BmL4ro-o.js.map} +1 -1
  34. package/dist/{registry-CO7JVZyE.js → registry-C3wcDM3X.js} +4 -4
  35. package/dist/{registry-CO7JVZyE.js.map → registry-C3wcDM3X.js.map} +1 -1
  36. package/dist/{setup-utils-0U-_Md2G.js → setup-utils-CNWIMZ-d.js} +71 -16
  37. package/dist/setup-utils-CNWIMZ-d.js.map +1 -0
  38. package/dist/smoke-test.sh +36 -1
  39. package/dist/{start-tui-WNb3ET14.js → start-tui-CS802Ww9.js} +311 -54
  40. package/dist/start-tui-CS802Ww9.js.map +1 -0
  41. package/dist/{steps-BAUXDCC4.js → steps-BX44xr30.js} +6 -6
  42. package/dist/{steps-BAUXDCC4.js.map → steps-BX44xr30.js.map} +1 -1
  43. package/dist/{task-stream-CZawuzlz.js → task-stream-BQNSp0qR.js} +4 -3
  44. package/dist/task-stream-BQNSp0qR.js.map +1 -0
  45. package/dist/{telemetry-ycqCpNPr.js → telemetry-BH-MgWPT.js} +3 -3
  46. package/dist/{telemetry-ycqCpNPr.js.map → telemetry-BH-MgWPT.js.map} +1 -1
  47. package/dist/{AiOptInRequiredScreen-_33FOcVo.js → terminal-BSiupnOQ.js} +1058 -92
  48. package/dist/terminal-BSiupnOQ.js.map +1 -0
  49. package/dist/{urls-C8aJWvgh.js → urls-BuEABcmF.js} +2 -2
  50. package/dist/{urls-C8aJWvgh.js.map → urls-BuEABcmF.js.map} +1 -1
  51. package/dist/{wizard-abort-DWXyJdws.js → wizard-abort-CR3w2Efg.js} +1 -1
  52. package/dist/{wizard-abort-C6gRLxUE.js → wizard-abort-Dl2MJOP9.js} +3 -3
  53. package/dist/{wizard-abort-C6gRLxUE.js.map → wizard-abort-Dl2MJOP9.js.map} +1 -1
  54. package/dist/wizard-session-G3VWD6hv.js.map +1 -1
  55. package/dist/{wizard-ui-YdGFRyu_.js → wizard-ui-WZ48rUgr.js} +2 -1
  56. package/dist/wizard-ui-WZ48rUgr.js.map +1 -0
  57. package/package.json +1 -1
  58. package/dist/AiOptInRequiredScreen-_33FOcVo.js.map +0 -1
  59. package/dist/agent-interface-D1vtN6Wn.js.map +0 -1
  60. package/dist/agent-runner-CBbkS0Ro.js.map +0 -1
  61. package/dist/analytics-CUr82BDl.js.map +0 -1
  62. package/dist/api-CI3Z74NG.js.map +0 -1
  63. package/dist/debug-DxA_f5QT.js.map +0 -1
  64. package/dist/mcp-prompt-streaming-DQz4FSb1.js.map +0 -1
  65. package/dist/playground-D7AhMMF5.js.map +0 -1
  66. package/dist/setup-utils-0U-_Md2G.js.map +0 -1
  67. package/dist/start-tui-WNb3ET14.js.map +0 -1
  68. package/dist/task-stream-CZawuzlz.js.map +0 -1
  69. package/dist/wizard-ui-YdGFRyu_.js.map +0 -1
@@ -1,18 +1,47 @@
1
1
  import { n as __require } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { F as POSTHOG_FLAG_HEADER_PREFIX, X as WIZARD_VARIANTS, Y as WIZARD_USER_AGENT, Z as WIZARD_VARIANT_FLAG_KEY, a as getLogFilePath, f as skillTmpPath, o as initLogFile, p as getUI, q as WIZARD_REMARK_EVENT_NAME, r as debug, s as logToFile, u as WIZARD_YARA_REPORT_FILE, z as POSTHOG_PROPERTY_HEADER_PREFIX } from "./debug-DxA_f5QT.js";
3
- import { t as analytics } from "./analytics-CUr82BDl.js";
4
- import { i as getLlmGatewayUrlFromHost } from "./urls-C8aJWvgh.js";
2
+ import { F as POSTHOG_FLAG_HEADER_PREFIX, J as WIZARD_REMARK_EVENT_NAME, K as WIZARD_ORCHESTRATOR_FLAG_KEY, Q as WIZARD_VARIANT_FLAG_KEY, X as WIZARD_USER_AGENT, Z as WIZARD_VARIANTS, a as getLogFilePath, f as skillTmpPath, o as initLogFile, p as getUI, r as debug, s as logToFile, u as WIZARD_YARA_REPORT_FILE, z as POSTHOG_PROPERTY_HEADER_PREFIX } from "./debug-BizeRFR0.js";
3
+ import { t as analytics } from "./analytics-BX3LKPch.js";
4
+ import { i as getLlmGatewayUrlFromHost } from "./urls-BuEABcmF.js";
5
5
  import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-G3VWD6hv.js";
6
- import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-C6gRLxUE.js";
6
+ import { i as wizardAbort, n as registerCleanup, t as WizardError } from "./wizard-abort-Dl2MJOP9.js";
7
7
  import { createRequire } from "node:module";
8
8
  import * as fs$1 from "fs";
9
9
  import fs from "fs";
10
+ import * as path$1 from "path";
10
11
  import path from "path";
11
12
  import { z } from "zod";
12
13
  import fg from "fast-glob";
13
14
  import { execFileSync } from "child_process";
14
15
  import { randomUUID } from "crypto";
15
16
  import * as os from "os";
17
+ //#region src/utils/atomic-ledger.ts
18
+ /**
19
+ * Small shared primitives for on-disk ledgers: an atomic JSON writer and a
20
+ * single-chain async mutex. Used by the audit tools and by the orchestrator
21
+ * queue. Lifted here so both share one implementation.
22
+ */
23
+ /**
24
+ * Atomically write JSON: write to a `.tmp` file then rename over the target. The
25
+ * rename bumps the file's mtime in one step, which is what a file watcher polls.
26
+ */
27
+ function writeJsonAtomic(targetPath, data) {
28
+ const tmpPath = `${targetPath}.tmp`;
29
+ fs$1.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf8");
30
+ fs$1.renameSync(tmpPath, targetPath);
31
+ }
32
+ /**
33
+ * A single async mutex. Serializes read-modify-write cycles so concurrent callers
34
+ * (parallel task agents, audit tool calls) never interleave a mutation.
35
+ */
36
+ function makeMutex() {
37
+ let chain = Promise.resolve();
38
+ return async function run(fn) {
39
+ const next = chain.then(() => fn());
40
+ chain = next.catch(() => void 0);
41
+ return next;
42
+ };
43
+ }
44
+ //#endregion
16
45
  //#region src/lib/programs/audit/types.ts
17
46
  /** Single source of truth for status glyph + color across audit views. */
18
47
  const AUDIT_SEVERITY_STYLE = {
@@ -103,6 +132,336 @@ function createSecretVault() {
103
132
  };
104
133
  }
105
134
  //#endregion
135
+ //#region src/lib/agent/runner/orchestrator/queue.ts
136
+ /**
137
+ * The orchestrator task queue.
138
+ *
139
+ * In memory, synchronous, single-owner: one Node process drives the run, so
140
+ * there is no locking. The queue imposes no execution policy — `nextRunnable`
141
+ * returns every pending task whose dependencies are satisfied, and how many of
142
+ * those run at once is decided by the task graph, not the queue.
143
+ *
144
+ * Every transition rewrites `<installDir>/.posthog-wizard-cache/queue.json`, a
145
+ * small file holding the whole queue, handoffs included. It is the run's log
146
+ * and the report's source. The whole cache folder is run-scoped and wiped when
147
+ * the run ends.
148
+ */
149
+ const TaskStatus = {
150
+ Pending: "pending",
151
+ Running: "running",
152
+ Done: "done",
153
+ Skipped: "skipped",
154
+ Failed: "failed"
155
+ };
156
+ const QUEUE_DIR_NAME = ".posthog-wizard-cache";
157
+ const DEFAULT_MAX_ATTEMPTS = 2;
158
+ function nowIso() {
159
+ return (/* @__PURE__ */ new Date()).toISOString();
160
+ }
161
+ /** Dropped in the cache folder so an orphaned copy explains itself. */
162
+ const DELETE_ME_FILE = ".DELETE-ME.md";
163
+ const DELETE_ME_BODY = `# Safe to delete
164
+
165
+ This folder contains run artifacts from the PostHog Wizard. This should have
166
+ been deleted if the Wizard has finished running. If this wasn't deleted for
167
+ some reason, you can safely delete the entire \`${QUEUE_DIR_NAME}/\` folder.
168
+ `;
169
+ var QueueStore = class {
170
+ tasks = [];
171
+ onTransition;
172
+ runId;
173
+ queuePath;
174
+ constructor(installDir, runId, opts) {
175
+ this.onTransition = opts?.onTransition;
176
+ this.runId = runId;
177
+ const dir = path$1.join(installDir, QUEUE_DIR_NAME);
178
+ this.queuePath = path$1.join(dir, "queue.json");
179
+ fs$1.mkdirSync(dir, { recursive: true });
180
+ fs$1.writeFileSync(path$1.join(dir, DELETE_ME_FILE), DELETE_ME_BODY);
181
+ }
182
+ list() {
183
+ return this.tasks;
184
+ }
185
+ get(id) {
186
+ return this.tasks.find((t) => t.id === id);
187
+ }
188
+ /**
189
+ * Every pending task whose dependencies are all satisfied (`done` or
190
+ * `skipped`). A skipped dependency does not block downstream work.
191
+ */
192
+ nextRunnable() {
193
+ const doneIds = new Set(this.tasks.filter((t) => t.status === TaskStatus.Done || t.status === TaskStatus.Skipped).map((t) => t.id));
194
+ return this.tasks.filter((t) => t.status === TaskStatus.Pending && t.dependsOn.every((d) => doneIds.has(d)));
195
+ }
196
+ /**
197
+ * True when no task is running and none can be started. Either everything
198
+ * is terminal, or the only pending tasks are blocked by a failed dependency.
199
+ */
200
+ isDrained() {
201
+ if (this.tasks.some((t) => t.status === TaskStatus.Running)) return false;
202
+ return this.nextRunnable().length === 0;
203
+ }
204
+ summary() {
205
+ const counts = {
206
+ [TaskStatus.Pending]: 0,
207
+ [TaskStatus.Running]: 0,
208
+ [TaskStatus.Done]: 0,
209
+ [TaskStatus.Skipped]: 0,
210
+ [TaskStatus.Failed]: 0
211
+ };
212
+ for (const t of this.tasks) counts[t.status] += 1;
213
+ return {
214
+ ...counts,
215
+ total: this.tasks.length
216
+ };
217
+ }
218
+ readHandoff(id) {
219
+ return this.get(id)?.handoff ?? null;
220
+ }
221
+ /** Handoffs of completed tasks of a given type, oldest first. */
222
+ readHandoffsByType(type) {
223
+ return this.tasks.filter((t) => t.type === type && t.handoff).map((t) => t.handoff);
224
+ }
225
+ enqueue(input) {
226
+ const task = {
227
+ id: randomUUID(),
228
+ type: input.type,
229
+ label: input.label,
230
+ status: TaskStatus.Pending,
231
+ dependsOn: input.dependsOn ?? [],
232
+ inputs: input.inputs ?? {},
233
+ model: input.model,
234
+ attempts: 0,
235
+ maxAttempts: input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
236
+ enqueuedBy: input.enqueuedBy ?? "orchestrator",
237
+ createdAt: nowIso()
238
+ };
239
+ this.tasks.push(task);
240
+ this.reflect();
241
+ this.notify("enqueue", task);
242
+ return task;
243
+ }
244
+ start(id) {
245
+ const t = this.require(id);
246
+ t.status = TaskStatus.Running;
247
+ t.startedAt = nowIso();
248
+ t.attempts += 1;
249
+ this.reflect();
250
+ this.notify("start", t);
251
+ return t;
252
+ }
253
+ complete(id, handoff) {
254
+ return this.finish(id, TaskStatus.Done, handoff);
255
+ }
256
+ /** Terminal: the agent could not do the task. Not done, not failed. */
257
+ skip(id, handoff) {
258
+ return this.finish(id, TaskStatus.Skipped, handoff);
259
+ }
260
+ fail(id, error, handoff) {
261
+ const t = this.require(id);
262
+ t.error = error;
263
+ return this.finish(id, TaskStatus.Failed, handoff);
264
+ }
265
+ /** Put a failed/running task back to pending for a retry within the run. */
266
+ requeue(id) {
267
+ const t = this.require(id);
268
+ t.status = TaskStatus.Pending;
269
+ t.startedAt = void 0;
270
+ t.finishedAt = void 0;
271
+ this.reflect();
272
+ this.notify("requeue", t);
273
+ return t;
274
+ }
275
+ finish(id, status, handoff) {
276
+ const t = this.require(id);
277
+ if (handoff) t.handoff = handoff;
278
+ t.status = status;
279
+ t.finishedAt = nowIso();
280
+ this.reflect();
281
+ this.notify(status === TaskStatus.Done ? "complete" : status === TaskStatus.Skipped ? "skip" : "fail", t);
282
+ return t;
283
+ }
284
+ reflect() {
285
+ const file = {
286
+ version: 1,
287
+ runId: this.runId,
288
+ tasks: this.tasks
289
+ };
290
+ writeJsonAtomic(this.queuePath, file);
291
+ }
292
+ notify(event, task) {
293
+ try {
294
+ this.onTransition?.(event, task);
295
+ } catch (error) {
296
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), {
297
+ step: "orchestrator_queue_listener",
298
+ event
299
+ });
300
+ }
301
+ }
302
+ require(id) {
303
+ const t = this.get(id);
304
+ if (!t) throw new Error(`No task ${id} in the queue`);
305
+ return t;
306
+ }
307
+ };
308
+ //#endregion
309
+ //#region src/lib/agent/runner/orchestrator/queue-tools.ts
310
+ /**
311
+ * Orchestrator MCP tools, registered into the existing `wizard-tools` server when
312
+ * a queue is present. They let the orchestrator agent and task agents grow the
313
+ * queue, report completion with a structured handoff, and read prior handoffs.
314
+ *
315
+ * The guard logic and the apply functions are plain, exported, and unit-tested.
316
+ * `buildOrchestratorTools` wraps them in the SDK `tool()` shape.
317
+ */
318
+ function stableStringify(value) {
319
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
320
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
321
+ return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
322
+ }
323
+ function dedupKey(type, inputs) {
324
+ return `${type}::${stableStringify(inputs)}`;
325
+ }
326
+ /**
327
+ * A backstop on total queue size. Tasks can enqueue tasks, so a misbehaving
328
+ * type could grow the queue without bound. Keeping the graph small is the job
329
+ * of good agent and skill design, not this number — it only stops a runaway.
330
+ * The real flow is ~9 tasks, so this sits well clear of it.
331
+ */
332
+ const MAX_QUEUE_TASKS = 30;
333
+ /**
334
+ * Validate an enqueue. Structural checks only — a real type, real dependencies,
335
+ * not a literal duplicate, and not past the runaway backstop. How much runs,
336
+ * and in what shape, is the task graph's business, not a knob's.
337
+ */
338
+ function checkEnqueueGuards(ctx, args) {
339
+ const tasks = ctx.store.list();
340
+ if (tasks.length >= MAX_QUEUE_TASKS) return {
341
+ ok: false,
342
+ guard: "queue-full",
343
+ message: `The queue already holds ${tasks.length} tasks (cap ${MAX_QUEUE_TASKS}). Refine the existing tasks rather than adding more.`
344
+ };
345
+ if (!ctx.validTypes.includes(args.type)) return {
346
+ ok: false,
347
+ guard: "unknown-type",
348
+ message: `Unknown task type "${args.type}". Valid types: ${ctx.validTypes.join(", ")}.`
349
+ };
350
+ for (const dep of args.dependsOn ?? []) if (!ctx.store.get(dep)) return {
351
+ ok: false,
352
+ guard: "unknown-dep",
353
+ message: `Dependency "${dep}" is not a known task id.`
354
+ };
355
+ const key = dedupKey(args.type, args.inputs ?? {});
356
+ if (tasks.some((t) => t.status !== TaskStatus.Failed && dedupKey(t.type, t.inputs) === key)) return {
357
+ ok: false,
358
+ guard: "dedup",
359
+ message: `A "${args.type}" task with these inputs already exists.`
360
+ };
361
+ return { ok: true };
362
+ }
363
+ function applyEnqueue(ctx, args) {
364
+ const guard = checkEnqueueGuards(ctx, args);
365
+ if (!guard.ok) return guard;
366
+ return {
367
+ ok: true,
368
+ task: ctx.store.enqueue({
369
+ type: args.type,
370
+ label: args.label,
371
+ inputs: args.inputs ?? {},
372
+ dependsOn: args.dependsOn ?? [],
373
+ model: args.model,
374
+ enqueuedBy: ctx.currentTaskId ?? "orchestrator"
375
+ })
376
+ };
377
+ }
378
+ function applyComplete(ctx, args) {
379
+ const id = ctx.currentTaskId;
380
+ if (!id) return {
381
+ ok: false,
382
+ message: "complete_task can only be called by a running task agent."
383
+ };
384
+ if (args.status === TaskStatus.Failed) ctx.store.fail(id, {
385
+ type: "self-reported",
386
+ message: args.handoff.forNextAgent
387
+ }, args.handoff);
388
+ else if (args.status === TaskStatus.Skipped) ctx.store.skip(id, args.handoff);
389
+ else ctx.store.complete(id, args.handoff);
390
+ return { ok: true };
391
+ }
392
+ function applyReadHandoffs(ctx, args) {
393
+ if (args.taskId) {
394
+ const h = ctx.store.readHandoff(args.taskId);
395
+ return h ? [h] : [];
396
+ }
397
+ if (args.type) return ctx.store.readHandoffsByType(args.type);
398
+ const currentId = ctx.currentTaskId;
399
+ const current = currentId ? ctx.store.get(currentId) : void 0;
400
+ if (!current) return [];
401
+ return current.dependsOn.map((depId) => ctx.store.readHandoff(depId)).filter((h) => h !== null);
402
+ }
403
+ const HANDOFF_SHAPE = {
404
+ goals: z.string().describe("What this task was asked to achieve."),
405
+ did: z.string().describe("What you actually did."),
406
+ forNextAgent: z.string().describe("What the next agent should know."),
407
+ filesTouched: z.array(z.string()).optional(),
408
+ conflict: z.string().optional().describe("A one-line summary of any conflict you could not cleanly resolve (e.g. a dependency or build conflict). Put full detail in your work; this line is surfaced to the user.")
409
+ };
410
+ function textResult(text, isError = false) {
411
+ return {
412
+ isError,
413
+ content: [{
414
+ type: "text",
415
+ text
416
+ }]
417
+ };
418
+ }
419
+ /**
420
+ * Build the orchestrator tools in the SDK `tool()` shape. Called from
421
+ * createWizardToolsServer only when a queue context is present.
422
+ */
423
+ function buildOrchestratorTools(tool, ctx) {
424
+ return [
425
+ tool("enqueue_task", "Add a task to the orchestrator queue. Use it to seed work and to enqueue follow-up work you discover. Keep tasks small and discrete.", {
426
+ type: z.string().describe(`The task type. One of: ${ctx.validTypes.join(", ")}.`),
427
+ label: z.string().optional().describe("A short label for the UI — the action in a few words (e.g. \"Add the PostHog SDK\", \"Initialize PostHog\"). Leave out file names, class names, and other specifics."),
428
+ inputs: z.record(z.unknown()).optional(),
429
+ dependsOn: z.array(z.string()).optional().describe("Task ids that must be done before this task runs."),
430
+ model: z.string().optional(),
431
+ reason: z.string().describe("One line on why this task is needed.")
432
+ }, ((args) => {
433
+ const res = applyEnqueue(ctx, args);
434
+ if (!res.ok) {
435
+ analytics.wizardCapture("orchestrator guard tripped", {
436
+ guard: res.guard,
437
+ type: args.type
438
+ });
439
+ return textResult(res.message, true);
440
+ }
441
+ return textResult(JSON.stringify({ id: res.task.id }));
442
+ })),
443
+ tool("complete_task", "Report the outcome of your task. Always call this exactly once when you finish, with a structured handoff for the next agent. Use status 'skipped' when the task does not apply to this project and you cannot do it (say why in the handoff) — not 'done'.", {
444
+ status: z.enum([
445
+ "done",
446
+ "failed",
447
+ "skipped"
448
+ ]),
449
+ handoff: z.object(HANDOFF_SHAPE)
450
+ }, ((args) => {
451
+ const res = applyComplete(ctx, args);
452
+ if (!res.ok) return textResult(res.message, true);
453
+ return textResult("ok");
454
+ })),
455
+ tool("read_handoffs", "Read structured handoffs from earlier tasks. With no argument, returns the handoffs of your dependencies.", {
456
+ type: z.string().optional(),
457
+ taskId: z.string().optional()
458
+ }, ((args) => {
459
+ const handoffs = applyReadHandoffs(ctx, args);
460
+ return textResult(JSON.stringify(handoffs, null, 2));
461
+ }))
462
+ ];
463
+ }
464
+ //#endregion
106
465
  //#region src/lib/wizard-tools.ts
107
466
  /**
108
467
  * Unified in-process MCP server for the PostHog wizard.
@@ -204,17 +563,24 @@ async function installSkillById(skillId, installDir, skillsBaseUrl, skillsRoot)
204
563
  * upcoming call should proceed and, if not, the error message to surface
205
564
  * to the agent. Extracted so the policy can be unit-tested without
206
565
  * spinning up an MCP server.
566
+ *
567
+ * The adjacency nudge fires exactly once per run (the caller records it
568
+ * via `adjacencyNudged`) — flows that legitimately need several
569
+ * sequential, answer-dependent asks then proceed up to `maxQuestions`.
570
+ * Without the flag the rejected call would never advance the counter and
571
+ * every later call would be rejected, making caps above the threshold
572
+ * unreachable.
207
573
  */
208
- function evaluateAskCap(callCount, maxQuestions) {
574
+ function evaluateAskCap(callCount, maxQuestions, adjacencyNudged = false) {
209
575
  if (callCount >= maxQuestions) return {
210
576
  kind: "capped",
211
577
  reason: "max_questions",
212
578
  message: `Error: wizard_ask cap reached (${maxQuestions} calls in this run). Proceed with sensible defaults using the answers you already have, or emit [ABORT] requirements-incomplete.`
213
579
  };
214
- if (callCount >= 3) return {
580
+ if (!adjacencyNudged && callCount >= 3) return {
215
581
  kind: "capped",
216
582
  reason: "adjacency",
217
- message: `Error: too many wizard_ask calls in a row (${callCount} so far). Batch the remaining questions into a single call — the schema accepts up to 8 questions per invocation.`
583
+ message: `Error: too many wizard_ask calls in a row (${callCount} so far). Batch the remaining questions into a single call — the schema accepts up to 8 questions per invocation. If the remaining questions truly depend on earlier answers, ask again and they will go through.`
218
584
  };
219
585
  return { kind: "ok" };
220
586
  }
@@ -292,14 +658,9 @@ const auditUpdateSchema = z.object({
292
658
  file: z.string().optional(),
293
659
  details: z.string().optional()
294
660
  });
295
- /**
296
- * Atomically write JSON: write to .tmp then rename. The rename is what bumps
297
- * the file's mtime, which is what the UI's file watcher polls on.
298
- */
661
+ /** Atomically write the audit ledger. Thin typed wrapper over writeJsonAtomic. */
299
662
  function writeLedgerAtomic(targetPath, checks) {
300
- const tmpPath = `${targetPath}.tmp`;
301
- fs.writeFileSync(tmpPath, JSON.stringify(checks, null, 2), "utf8");
302
- fs.renameSync(tmpPath, targetPath);
663
+ writeJsonAtomic(targetPath, checks);
303
664
  }
304
665
  /**
305
666
  * Apply a batch of patches to the ledger by id. Returns the new array and the
@@ -375,27 +736,16 @@ function appendAuditChecksToLedger(targetPath, additions) {
375
736
  added: additions.length
376
737
  };
377
738
  }
378
- /**
379
- * Single async mutex shared by audit tools — guarantees a read-modify-write
380
- * cycle on the ledger is atomic across concurrent tool calls (e.g. future subagents).
381
- */
382
- function makeMutex() {
383
- let chain = Promise.resolve();
384
- return async function run(fn) {
385
- const next = chain.then(() => fn());
386
- chain = next.catch(() => void 0);
387
- return next;
388
- };
389
- }
390
739
  const SERVER_NAME = "wizard-tools";
391
740
  /**
392
741
  * Create the unified in-process MCP server with all wizard tools.
393
742
  * Must be called asynchronously because the SDK is an ESM module loaded via dynamic import.
394
743
  */
395
744
  async function createWizardToolsServer(options) {
396
- const { workingDirectory, detectPackageManager, skillsBaseUrl, askBridge, askMaxQuestions = 10, secretVault = createSecretVault() } = options;
745
+ const { workingDirectory, detectPackageManager, skillsBaseUrl, askBridge, askMaxQuestions = 10, secretVault = createSecretVault(), orchestrator } = options;
397
746
  const { tool, createSdkMcpServer } = await getSDKModule$1();
398
747
  let askCallCount = 0;
748
+ let askAdjacencyNudged = false;
399
749
  let cachedSkillMenu = {};
400
750
  let categoryNames = ["integration"];
401
751
  const menu = await fetchSkillMenu(skillsBaseUrl);
@@ -575,7 +925,8 @@ async function createWizardToolsServer(options) {
575
925
  ]).describe("'single' = pick one option, 'multi' = pick any, 'text' = free-form single-line answer"),
576
926
  options: z.array(z.object({
577
927
  label: z.string(),
578
- value: z.string()
928
+ value: z.string(),
929
+ description: z.string().optional().describe("Optional secondary line shown dimmed and wrapped beneath the label (multi-select only). Use when a choice needs more than a title — e.g. what a custom scout watches and what makes it speak up.")
579
930
  })).optional().describe("Required for kind=single|multi; ignored for kind=text"),
580
931
  required: z.boolean().optional().describe("Defaults to true"),
581
932
  sensitive: z.boolean().optional().describe("Only valid for kind='text'. When true, the user's answer is stored in the wizard's secret vault and returned to you as { secretRef: 'secret:...' } instead of the raw string. Use for API keys, tokens, and any other secret the user types in.")
@@ -600,8 +951,9 @@ async function createWizardToolsServer(options) {
600
951
  }],
601
952
  isError: true
602
953
  };
603
- const capDecision = evaluateAskCap(askCallCount, askMaxQuestions);
954
+ const capDecision = evaluateAskCap(askCallCount, askMaxQuestions, askAdjacencyNudged);
604
955
  if (capDecision.kind === "capped") {
956
+ if (capDecision.reason === "adjacency") askAdjacencyNudged = true;
605
957
  analytics.wizardCapture("wizard_ask capped", {
606
958
  reason: capDecision.reason,
607
959
  call_count: askCallCount,
@@ -673,7 +1025,8 @@ async function createWizardToolsServer(options) {
673
1025
  isError: true
674
1026
  };
675
1027
  }
676
- })
1028
+ }),
1029
+ ...orchestrator ? buildOrchestratorTools(tool, orchestrator) : []
677
1030
  ]
678
1031
  });
679
1032
  }
@@ -687,7 +1040,10 @@ const WIZARD_TOOL_NAMES = {
687
1040
  auditSeedChecks: `mcp__${SERVER_NAME}__audit_seed_checks`,
688
1041
  auditAddChecks: `mcp__${SERVER_NAME}__audit_add_checks`,
689
1042
  auditResolveChecks: `mcp__${SERVER_NAME}__audit_resolve_checks`,
690
- wizardAsk: `mcp__${SERVER_NAME}__wizard_ask`
1043
+ wizardAsk: `mcp__${SERVER_NAME}__wizard_ask`,
1044
+ enqueueTask: `mcp__${SERVER_NAME}__enqueue_task`,
1045
+ completeTask: `mcp__${SERVER_NAME}__complete_task`,
1046
+ readHandoffs: `mcp__${SERVER_NAME}__read_handoffs`
691
1047
  };
692
1048
  //#endregion
693
1049
  //#region src/utils/custom-headers.ts
@@ -1454,6 +1810,19 @@ function getWizardCommandments() {
1454
1810
  return WIZARD_COMMANDMENTS;
1455
1811
  }
1456
1812
  //#endregion
1813
+ //#region src/lib/agent/agent-phase.ts
1814
+ /** Maps a Claude SDK tool name to the phase that tool implies. Returns null
1815
+ * when the tool doesn't drive the visualizer (Task*, TodoWrite, etc.). */
1816
+ function classifyToolToStage(toolName) {
1817
+ if (toolName.includes("install_skill") || toolName.includes("load_skill_menu")) return "skill-install";
1818
+ if (toolName.includes("set_env_values") || toolName.includes("check_env_keys")) return "env-setup";
1819
+ if (toolName.includes("mcp__posthog")) return "dashboards";
1820
+ if (toolName === "Bash") return "dep-install";
1821
+ if (toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") return "code-edits";
1822
+ if (toolName === "Read" || toolName === "Glob" || toolName === "Grep") return "codebase-scan";
1823
+ return null;
1824
+ }
1825
+ //#endregion
1457
1826
  //#region src/lib/agent/signals.ts
1458
1827
  /**
1459
1828
  * Agent signal vocabulary — the marker strings the agent emits and the error
@@ -1761,7 +2130,7 @@ function buildAuthErrorContext(workingDirectory, gatewayUrl, homeDir = os.homedi
1761
2130
  * Phase 2 — collect remark (once): block with remark prompt
1762
2131
  * Phase 3 — allow stop: return {}
1763
2132
  */
1764
- function createStopHook(featureQueue, signals) {
2133
+ function createStopHook(featureQueue, signals, requestRemark = true) {
1765
2134
  let featureIndex = 0;
1766
2135
  let remarkRequested = false;
1767
2136
  return (input) => {
@@ -1784,7 +2153,7 @@ function createStopHook(featureQueue, signals) {
1784
2153
  reason: prompt
1785
2154
  };
1786
2155
  }
1787
- if (!remarkRequested) {
2156
+ if (requestRemark && !remarkRequested) {
1788
2157
  remarkRequested = true;
1789
2158
  logToFile("Stop hook: requesting reflection");
1790
2159
  return {
@@ -1805,6 +2174,14 @@ function buildWizardMetadata(flags = {}) {
1805
2174
  return { ...(variantKey && WIZARD_VARIANTS[variantKey]) ?? WIZARD_VARIANTS["base"] };
1806
2175
  }
1807
2176
  /**
2177
+ * Whether this run uses the experimental task-queue orchestrator. Gated by the
2178
+ * boolean `wizard-orchestrator` feature flag, targeted to the user in the wizard's
2179
+ * analytics project.
2180
+ */
2181
+ function isOrchestratorEnabled(flags = {}) {
2182
+ return flags[WIZARD_ORCHESTRATOR_FLAG_KEY] === "true";
2183
+ }
2184
+ /**
1808
2185
  * Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS, which always
1809
2186
  * includes `x-posthog-use-bedrock-fallback: true` so the LLM gateway falls back to Bedrock on
1810
2187
  * Anthropic 5xx, plus any wizard metadata/flags.
@@ -2008,7 +2385,6 @@ async function initializeAgent(config, options) {
2008
2385
  initLogFile();
2009
2386
  logToFile("Agent initialization starting");
2010
2387
  logToFile("Install directory:", options.installDir);
2011
- getUI().log.step("Initializing Claude agent...");
2012
2388
  try {
2013
2389
  const gatewayUrl = getLlmGatewayUrlFromHost(config.posthogApiHost);
2014
2390
  process.env.ANTHROPIC_BASE_URL = gatewayUrl;
@@ -2038,7 +2414,8 @@ async function initializeAgent(config, options) {
2038
2414
  detectPackageManager: config.detectPackageManager,
2039
2415
  skillsBaseUrl: config.skillsBaseUrl,
2040
2416
  askBridge: config.askBridge,
2041
- askMaxQuestions: config.askMaxQuestions
2417
+ askMaxQuestions: config.askMaxQuestions,
2418
+ orchestrator: config.orchestrator
2042
2419
  });
2043
2420
  const agentRunConfig = {
2044
2421
  workingDirectory: config.workingDirectory,
@@ -2062,8 +2439,6 @@ async function initializeAgent(config, options) {
2062
2439
  gatewayUrl,
2063
2440
  apiKeyPresent: !!config.posthogApiKey
2064
2441
  });
2065
- getUI().log.step(`Verbose logs: ${getLogFilePath()}`);
2066
- getUI().log.success("Agent initialized. Let's get cooking!");
2067
2442
  return agentRunConfig;
2068
2443
  } catch (error) {
2069
2444
  getUI().log.error(`Failed to initialize agent: ${error.message}`);
@@ -2130,9 +2505,18 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2130
2505
  } else logToFile(`Agent run completed in ${durationSeconds}s`);
2131
2506
  const remark = signals.remark();
2132
2507
  if (remark) analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });
2508
+ const usage = lastResultMessage?.usage;
2133
2509
  analytics.wizardCapture("agent completed", {
2134
2510
  duration_ms: durationMs,
2135
- duration_seconds: durationSeconds
2511
+ duration_seconds: durationSeconds,
2512
+ model: agentConfig.model,
2513
+ num_turns: lastResultMessage?.num_turns,
2514
+ total_cost_usd: lastResultMessage?.total_cost_usd,
2515
+ input_tokens: usage?.input_tokens,
2516
+ output_tokens: usage?.output_tokens,
2517
+ cache_creation_input_tokens: usage?.cache_creation_input_tokens,
2518
+ cache_read_input_tokens: usage?.cache_read_input_tokens,
2519
+ ...config?.analyticsProperties
2136
2520
  });
2137
2521
  try {
2138
2522
  middleware?.finalize(lastResultMessage, durationMs);
@@ -2239,7 +2623,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2239
2623
  PreToolUse: createPreToolUseYaraHooks(),
2240
2624
  PostToolUse: createPostToolUseYaraHooks(),
2241
2625
  Stop: [{
2242
- hooks: [createStopHook(config?.additionalFeatureQueue ?? [], signals)],
2626
+ hooks: [createStopHook(config?.additionalFeatureQueue ?? [], signals, config?.requestRemark ?? true)],
2243
2627
  timeout: 30
2244
2628
  }]
2245
2629
  }
@@ -2263,7 +2647,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2263
2647
  }
2264
2648
  loggedInitialContext = true;
2265
2649
  }
2266
- handleSDKMessage(message, options, spinner, signals, receivedSuccessResult, tasks);
2650
+ handleSDKMessage(message, options, spinner, signals, receivedSuccessResult, tasks, isOrchestratorEnabled(agentConfig.wizardFlags ?? {}));
2267
2651
  if (abortCases.length > 0 && !abortReason && message.type === "assistant") {
2268
2652
  const content = message.message?.content;
2269
2653
  if (Array.isArray(content)) {
@@ -2390,7 +2774,9 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
2390
2774
  const durationMs = Date.now() - startTime;
2391
2775
  analytics.wizardCapture("agent aborted", {
2392
2776
  duration_ms: durationMs,
2393
- duration_seconds: Math.round(durationMs / 1e3)
2777
+ duration_seconds: Math.round(durationMs / 1e3),
2778
+ model: agentConfig.model,
2779
+ ...config?.analyticsProperties
2394
2780
  });
2395
2781
  }
2396
2782
  }
@@ -2500,14 +2886,14 @@ function extractTaskIdFromResult(content) {
2500
2886
  }
2501
2887
  }
2502
2888
  }
2503
- function handleSDKMessage(message, options, spinner, signals, receivedSuccessResult = false, tasks) {
2889
+ function handleSDKMessage(message, options, spinner, signals, receivedSuccessResult = false, tasks, suppressTaskRender = false) {
2504
2890
  const STATUS_RANK = {
2505
2891
  completed: 0,
2506
2892
  in_progress: 1
2507
2893
  };
2508
2894
  const rank = (status) => STATUS_RANK[status] ?? 2;
2509
2895
  const syncTasks = () => {
2510
- if (!tasks) return;
2896
+ if (!tasks || suppressTaskRender) return;
2511
2897
  const sorted = Array.from(tasks.values()).sort((a, b) => rank(a.status) - rank(b.status));
2512
2898
  getUI().syncTodos(sorted);
2513
2899
  };
@@ -2537,6 +2923,10 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
2537
2923
  tasks,
2538
2924
  sync: syncTasks
2539
2925
  });
2926
+ if (block.type === "tool_use") {
2927
+ const stage = classifyToolToStage(block.name);
2928
+ if (stage) getUI().setStage(stage);
2929
+ }
2540
2930
  }
2541
2931
  break;
2542
2932
  }
@@ -2586,6 +2976,6 @@ function handleSDKMessage(message, options, spinner, signals, receivedSuccessRes
2586
2976
  }
2587
2977
  }
2588
2978
  //#endregion
2589
- export { AUDIT_CHECKS_KEY as _, backupAndFixClaudeSettings as a, coerceAuditChecks as b, restoreClaudeSettings as c, writeScanReport as d, WIZARD_TOOL_NAMES as f, AUDIT_CHECKS_FILE as g, installSkillById as h, runAgent as i, AgentSignals as l, fetchSkillMenu as m, buildWizardMetadata as n, checkAllSettingsConflicts as o, downloadSkill as p, initializeAgent as r, recoverOrphanedSettingsBackups as s, buildAgentEnv as t, formatScanReport as u, AUDIT_REPORT_FILE as v, getAuditChecks as x, AUDIT_SEVERITY_STYLE as y };
2979
+ export { AUDIT_SEVERITY_STYLE as C, AUDIT_REPORT_FILE as S, getAuditChecks as T, QUEUE_DIR_NAME as _, runAgent as a, AUDIT_CHECKS_FILE as b, recoverOrphanedSettingsBackups as c, formatScanReport as d, writeScanReport as f, installSkillById as g, fetchSkillMenu as h, isOrchestratorEnabled as i, restoreClaudeSettings as l, downloadSkill as m, buildWizardMetadata as n, backupAndFixClaudeSettings as o, WIZARD_TOOL_NAMES as p, initializeAgent as r, checkAllSettingsConflicts as s, buildAgentEnv as t, AgentSignals as u, QueueStore as v, coerceAuditChecks as w, AUDIT_CHECKS_KEY as x, TaskStatus as y };
2590
2980
 
2591
- //# sourceMappingURL=agent-interface-D1vtN6Wn.js.map
2981
+ //# sourceMappingURL=agent-interface-Dq_4h2eN.js.map