@posthog/wizard 2.65.0 → 2.67.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 (119) hide show
  1. package/dist/{add-mcp-server-to-clients-ArtlMX9-.js → add-mcp-server-to-clients-DHTRbyJX.js} +228 -64
  2. package/dist/add-mcp-server-to-clients-DHTRbyJX.js.map +1 -0
  3. package/dist/{agent-interface-ORzGEFNM.js → agent-interface-tM_b0l__.js} +458 -76
  4. package/dist/agent-interface-tM_b0l__.js.map +1 -0
  5. package/dist/{agent-prompt-loader-D3w6sc34.js → agent-prompt-loader-BSMh5bfD.js} +5 -5
  6. package/dist/{agent-prompt-loader-D3w6sc34.js.map → agent-prompt-loader-BSMh5bfD.js.map} +1 -1
  7. package/dist/{agent-runner-Ca-e2TOQ.js → agent-runner-DD65OMkB.js} +153 -71
  8. package/dist/agent-runner-DD65OMkB.js.map +1 -0
  9. package/dist/analytics-B4Igh5nv.js +2 -0
  10. package/dist/{analytics-zBrIVjMV.js → analytics-CtIYzkK4.js} +2 -2
  11. package/dist/{analytics-zBrIVjMV.js.map → analytics-CtIYzkK4.js.map} +1 -1
  12. package/dist/{api-Wgb7AhrP.js → api-BJft9v6K.js} +3 -3
  13. package/dist/{api-Wgb7AhrP.js.map → api-BJft9v6K.js.map} +1 -1
  14. package/dist/bin.js +245 -102
  15. package/dist/bin.js.map +1 -1
  16. package/dist/{bounded-fs-DqsVmh4r.js → bounded-fs-BWqoFQvQ.js} +3 -3
  17. package/dist/{bounded-fs-DqsVmh4r.js.map → bounded-fs-BWqoFQvQ.js.map} +1 -1
  18. package/dist/{ci-install-D1QqmJIc.js → ci-install-BXGnVI7T.js} +18 -9
  19. package/dist/ci-install-BXGnVI7T.js.map +1 -0
  20. package/dist/{debug-Cm1hvkVy.js → debug-BsHD83Vy.js} +4 -4
  21. package/dist/debug-BsHD83Vy.js.map +1 -0
  22. package/dist/{debug-CtQ13sCW.js → debug-mO3yg4Z7.js} +1 -1
  23. package/dist/{defaults-BHhXrX9F.js → defaults-DI642M3n.js} +1 -1
  24. package/dist/{defaults-BHhXrX9F.js.map → defaults-DI642M3n.js.map} +1 -1
  25. package/dist/{env-api-key-CdI2XZIz.js → env-api-key-DS0ghlcO.js} +2 -2
  26. package/dist/{env-api-key-CdI2XZIz.js.map → env-api-key-DS0ghlcO.js.map} +1 -1
  27. package/dist/{environment-BhFvfkQg.js → environment-C7CKGIOE.js} +4 -4
  28. package/dist/{environment-BhFvfkQg.js.map → environment-C7CKGIOE.js.map} +1 -1
  29. package/dist/errors-BHervYGs.js +156 -0
  30. package/dist/errors-BHervYGs.js.map +1 -0
  31. package/dist/{file-watcher-Dciz09Rr.js → file-watcher-D1bdo9mq.js} +2 -2
  32. package/dist/{file-watcher-Dciz09Rr.js.map → file-watcher-D1bdo9mq.js.map} +1 -1
  33. package/dist/{headless-ui-B4FnKm11.js → headless-ui-6QuByWZ_.js} +2 -2
  34. package/dist/{headless-ui-B4FnKm11.js.map → headless-ui-6QuByWZ_.js.map} +1 -1
  35. package/dist/{interactive-DxHFVYr2.js → interactive-DUpW84WU.js} +2 -2
  36. package/dist/{interactive-DxHFVYr2.js.map → interactive-DUpW84WU.js.map} +1 -1
  37. package/dist/{local-dev-BN6NKQ0B.js → local-dev-DPZiIPCK.js} +1 -1
  38. package/dist/{local-dev-BN6NKQ0B.js.map → local-dev-DPZiIPCK.js.map} +1 -1
  39. package/dist/{mcp-t0UxAPur.js → mcp-CHo9h-WV.js} +2 -2
  40. package/dist/{mcp-t0UxAPur.js.map → mcp-CHo9h-WV.js.map} +1 -1
  41. package/dist/mcp-install-smoke-test.ts +477 -0
  42. package/dist/{mcp-prompt-streaming-ro98iUDc.js → mcp-prompt-streaming-CHRPxxd0.js} +4 -4
  43. package/dist/{mcp-prompt-streaming-ro98iUDc.js.map → mcp-prompt-streaming-CHRPxxd0.js.map} +1 -1
  44. package/dist/{non-interactive-BA2L_jDT.js → non-interactive-Cr-chffe.js} +7 -2
  45. package/dist/non-interactive-Cr-chffe.js.map +1 -0
  46. package/dist/{orchestrator-tools-vZmNsHy9.js → orchestrator-tools-Tx18_TMb.js} +3 -3
  47. package/dist/{orchestrator-tools-vZmNsHy9.js.map → orchestrator-tools-Tx18_TMb.js.map} +1 -1
  48. package/dist/{package-json-D0DCWpe5.js → package-json-GEU2VZlS.js} +1 -1
  49. package/dist/{package-json-D0DCWpe5.js.map → package-json-GEU2VZlS.js.map} +1 -1
  50. package/dist/{package-manager-DgqJdAQk.js → package-manager-CLfc31yf.js} +4 -4
  51. package/dist/package-manager-CLfc31yf.js.map +1 -0
  52. package/dist/{pi-nwrCARRp.js → pi-RlnYUHdb.js} +12 -11
  53. package/dist/{pi-nwrCARRp.js.map → pi-RlnYUHdb.js.map} +1 -1
  54. package/dist/{playground-xkEke_h0.js → playground-DQ633rje.js} +7 -7
  55. package/dist/{playground-xkEke_h0.js.map → playground-DQ633rje.js.map} +1 -1
  56. package/dist/{posthog-9To56cSd.js → posthog-DSn9bIZc.js} +2 -2
  57. package/dist/{posthog-9To56cSd.js.map → posthog-DSn9bIZc.js.map} +1 -1
  58. package/dist/{posthog-integration-CoPvN6YZ.js → posthog-integration-DFfBRGOC.js} +667 -37
  59. package/dist/posthog-integration-DFfBRGOC.js.map +1 -0
  60. package/dist/{provisioning-BYFSHSHY.js → provisioning-Cvy_vQHV.js} +3 -3
  61. package/dist/{provisioning-BYFSHSHY.js.map → provisioning-Cvy_vQHV.js.map} +1 -1
  62. package/dist/{queue-tools-BREbgGSC.js → queue-tools-BC9HxOV6.js} +33 -7
  63. package/dist/queue-tools-BC9HxOV6.js.map +1 -0
  64. package/dist/{registry-DGJQ_8QI.js → registry-DCAElOyj.js} +81 -81
  65. package/dist/{registry-DGJQ_8QI.js.map → registry-DCAElOyj.js.map} +1 -1
  66. package/dist/{security-wc-naGq5.js → security-CuSX3XVo.js} +21 -9
  67. package/dist/security-CuSX3XVo.js.map +1 -0
  68. package/dist/{setup-utils-Cz0wPLsQ.js → setup-utils-D1tv0v_R.js} +13 -13
  69. package/dist/{setup-utils-Cz0wPLsQ.js.map → setup-utils-D1tv0v_R.js.map} +1 -1
  70. package/dist/{start-tui-DDW9uVnD.js → start-tui-B5YfmqNr.js} +57 -25
  71. package/dist/start-tui-B5YfmqNr.js.map +1 -0
  72. package/dist/{steps-CZI0B24_.js → steps-CLaSMwfR.js} +7 -7
  73. package/dist/{steps-CZI0B24_.js.map → steps-CLaSMwfR.js.map} +1 -1
  74. package/dist/{store-xMkU8xAa.js → store-DzEgsAPN.js} +7 -6
  75. package/dist/{store-xMkU8xAa.js.map → store-DzEgsAPN.js.map} +1 -1
  76. package/dist/{subagent-BztacyoV.js → subagent-BUV6aD7p.js} +2 -2
  77. package/dist/{subagent-BztacyoV.js.map → subagent-BUV6aD7p.js.map} +1 -1
  78. package/dist/{task-CJ7x3McC.js → task-GnWId75q.js} +13 -12
  79. package/dist/{task-CJ7x3McC.js.map → task-GnWId75q.js.map} +1 -1
  80. package/dist/{task-stream-BK7JQ27G.js → task-stream-DSzvwJrD.js} +16 -9
  81. package/dist/task-stream-DSzvwJrD.js.map +1 -0
  82. package/dist/{tasks-FGzp_zBd.js → tasks-BO0W8ocx.js} +2 -2
  83. package/dist/{tasks-FGzp_zBd.js.map → tasks-BO0W8ocx.js.map} +1 -1
  84. package/dist/{telemetry-By56gAtF.js → telemetry-Vi9dWddJ.js} +3 -3
  85. package/dist/{telemetry-By56gAtF.js.map → telemetry-Vi9dWddJ.js.map} +1 -1
  86. package/dist/{terminal-C_rmkaku.js → terminal-B5wZK7aS.js} +73 -80
  87. package/dist/terminal-B5wZK7aS.js.map +1 -0
  88. package/dist/{tools-BpCJgMez.js → tools-C9cNluqc.js} +54 -47
  89. package/dist/tools-C9cNluqc.js.map +1 -0
  90. package/dist/{wizard-abort-BlDla_Jd.js → wizard-abort-BsNbguJt.js} +1 -1
  91. package/dist/wizard-abort-CGv3lVxw.js +71 -0
  92. package/dist/wizard-abort-CGv3lVxw.js.map +1 -0
  93. package/dist/{wizard-session-Bn6dO1gE.js → wizard-session-Cex1BiXs.js} +3 -2
  94. package/dist/wizard-session-Cex1BiXs.js.map +1 -0
  95. package/dist/{wizard-session-UxA_A0cA.js → wizard-session-DHbUcjI7.js} +1 -1
  96. package/dist/yara-hooks-C3Vm2Gow.js +2 -0
  97. package/dist/{yara-hooks-BrisBebM.js → yara-hooks-DZIVzhA6.js} +57 -8
  98. package/dist/yara-hooks-DZIVzhA6.js.map +1 -0
  99. package/package.json +2 -1
  100. package/dist/add-mcp-server-to-clients-ArtlMX9-.js.map +0 -1
  101. package/dist/agent-interface-ORzGEFNM.js.map +0 -1
  102. package/dist/agent-runner-Ca-e2TOQ.js.map +0 -1
  103. package/dist/analytics-C4j6grao.js +0 -2
  104. package/dist/ci-install-D1QqmJIc.js.map +0 -1
  105. package/dist/debug-Cm1hvkVy.js.map +0 -1
  106. package/dist/non-interactive-BA2L_jDT.js.map +0 -1
  107. package/dist/package-manager-DgqJdAQk.js.map +0 -1
  108. package/dist/posthog-integration-CoPvN6YZ.js.map +0 -1
  109. package/dist/queue-tools-BREbgGSC.js.map +0 -1
  110. package/dist/security-wc-naGq5.js.map +0 -1
  111. package/dist/start-tui-DDW9uVnD.js.map +0 -1
  112. package/dist/task-stream-BK7JQ27G.js.map +0 -1
  113. package/dist/terminal-C_rmkaku.js.map +0 -1
  114. package/dist/tools-BpCJgMez.js.map +0 -1
  115. package/dist/wizard-abort-CzwID162.js +0 -49
  116. package/dist/wizard-abort-CzwID162.js.map +0 -1
  117. package/dist/wizard-session-Bn6dO1gE.js.map +0 -1
  118. package/dist/yara-hooks-BrisBebM.js.map +0 -1
  119. package/dist/yara-hooks-D2B7Fd9G.js +0 -2
@@ -1,9 +1,10 @@
1
- import { dt as WIZARD_REMARK_EVENT_NAME, f as getUI, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-Cm1hvkVy.js";
2
- import { n as analytics } from "./analytics-zBrIVjMV.js";
3
- import { d as TaskStatus } from "./queue-tools-BREbgGSC.js";
4
- import { L as GATEWAY_PROVIDER, R as buildGatewayProvider, f as AgentOutputSignals, h as assembleCommandments, m as REMARK_INSTRUCTION } from "./agent-interface-ORzGEFNM.js";
5
- import { i as lastStatusLine, n as buildScrubbedEnv, o as withMode, r as extractText, s as createAioCapture, t as applyOutroMarkers } from "./pi-nwrCARRp.js";
6
- import { c as queueTools, l as renderToolInventory, r as allowsPostHogMcp } from "./agent-prompt-loader-D3w6sc34.js";
1
+ import { o as REMARK_INSTRUCTION } from "./errors-BHervYGs.js";
2
+ import { dt as WIZARD_REMARK_EVENT_NAME, f as getUI, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-BsHD83Vy.js";
3
+ import { n as analytics } from "./analytics-CtIYzkK4.js";
4
+ import { f as TaskStatus } from "./queue-tools-BC9HxOV6.js";
5
+ import { G as buildGatewayProvider, W as GATEWAY_PROVIDER, f as AgentOutputSignals, p as assembleCommandments } from "./agent-interface-tM_b0l__.js";
6
+ import { i as lastStatusLine, n as buildScrubbedEnv, o as withMode, r as extractText, s as createAioCapture, t as applyOutroMarkers } from "./pi-RlnYUHdb.js";
7
+ import { c as queueTools, l as renderToolInventory, r as allowsPostHogMcp } from "./agent-prompt-loader-BSMh5bfD.js";
7
8
  //#region src/lib/agent/runner/harness/pi/task.ts
8
9
  /**
9
10
  * Orchestrator-mode execution on pi: one fresh pi session per unit of work —
@@ -136,19 +137,19 @@ async function runPiTask(inputs) {
136
137
  message: "pi: gateway model could not be resolved"
137
138
  };
138
139
  const askState = { pending: false };
139
- const { createSecurityExtension } = await import("./security-wc-naGq5.js");
140
+ const { createSecurityExtension } = await import("./security-CuSX3XVo.js");
140
141
  const security = createSecurityExtension({
141
142
  disallowedTools: fenceDisallowList(disallowedTools),
142
143
  triageProvider: boot.triageProvider,
143
144
  getWizardAskPending: () => askState.pending
144
145
  });
145
- const { prewarmYaraScanner } = await import("./yara-hooks-D2B7Fd9G.js");
146
+ const { prewarmYaraScanner } = await import("./yara-hooks-C3Vm2Gow.js");
146
147
  prewarmYaraScanner();
147
148
  const extensionFactories = [security.factory];
148
149
  let mcpCleanup;
149
150
  let posthogMcp = false;
150
151
  if (allowsPostHogMcp(allowedTools)) try {
151
- const { setupPostHogMcp } = await import("./mcp-t0UxAPur.js");
152
+ const { setupPostHogMcp } = await import("./mcp-CHo9h-WV.js");
152
153
  const mcp = await setupPostHogMcp({
153
154
  mcpUrl: boot.credentials.host.mcpUrl,
154
155
  accessToken: boot.credentials.accessToken,
@@ -200,7 +201,7 @@ async function runPiTask(inputs) {
200
201
  find: () => withMode(createFindToolDefinition(dir), "parallel"),
201
202
  grep: () => withMode(createGrepToolDefinition(dir), "parallel")
202
203
  }).filter(([name]) => codingTools.has(name)).map(([, make]) => make());
203
- const { createWizardPiTools } = await import("./tools-BpCJgMez.js");
204
+ const { createWizardPiTools } = await import("./tools-C9cNluqc.js");
204
205
  const wizardToolNames = allowedPiWizardTools(allowedTools);
205
206
  const wizardTools = createWizardPiTools({
206
207
  workingDirectory: dir,
@@ -211,7 +212,7 @@ async function runPiTask(inputs) {
211
212
  askState.pending = pending;
212
213
  }
213
214
  }).filter((t) => wizardToolNames.has(t.name));
214
- const { createPiOrchestratorTools } = await import("./orchestrator-tools-vZmNsHy9.js");
215
+ const { createPiOrchestratorTools } = await import("./orchestrator-tools-Tx18_TMb.js");
215
216
  const queueTools = createPiOrchestratorTools(orchestrator).filter((t) => orchestratorTools.has(t.name));
216
217
  const customTools = [
217
218
  ...codingToolDefs,
@@ -325,4 +326,4 @@ async function runPiTask(inputs) {
325
326
  //#endregion
326
327
  export { runPiTask };
327
328
 
328
- //# sourceMappingURL=task-CJ7x3McC.js.map
329
+ //# sourceMappingURL=task-GnWId75q.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"task-CJ7x3McC.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/task.ts"],"sourcesContent":["/**\n * Orchestrator-mode execution on pi: one fresh pi session per unit of work —\n * the seed plan, or one drained task. The linear pipeline's concerns (skill\n * menu, todo panel, event-plan cleanup) stay in `index.ts`; this module builds\n * the leaner per-task session: gateway model, security fence, the task's\n * allowed coding tools, the wizard env tools, and the in-process orchestrator\n * queue tools.\n *\n * The task's `allowedTools` / `disallowedTools` arrive in the wizard's tool\n * vocabulary (`Read`, `Edit`, `Glob`, …, plus MCP-qualified orchestrator names\n * from `agentRunTools`). pi is where they become real: allowed names decide\n * which pi tool definitions get registered at all, and the disallow list is\n * ALSO handed to the security fence, so a name that never got registered stays\n * blocked even if the model hallucinates it.\n *\n * Loaded lazily from `index.ts` (typebox/ESM constraint, same as tools.ts).\n */\n\nimport { getUI } from '@ui';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport {\n Harness,\n Sequence,\n WIZARD_REMARK_EVENT_NAME,\n WIZARD_USER_AGENT,\n} from '@lib/constants';\nimport {\n allowsPostHogMcp,\n queueTools,\n renderToolInventory,\n} from '@lib/agent/agent-prompt-loader';\nimport { AgentErrorType } from '@lib/agent/agent-interface';\nimport { REMARK_INSTRUCTION } from '@lib/agent/signals';\nimport { AgentOutputSignals } from '@lib/agent/output-signals';\nimport { TaskStatus } from '../../sequence/orchestrator/queue';\nimport type { OrchestratorToolsContext } from '../../sequence/orchestrator/queue-tools';\nimport type { AgentResult, TaskRunInputs } from '../types';\nimport { buildGatewayProvider, GATEWAY_PROVIDER } from './gateway';\nimport { assembleCommandments } from '../../switchboard/commandments';\nimport {\n applyOutroMarkers,\n buildScrubbedEnv,\n extractText,\n lastStatusLine,\n withMode,\n} from './index';\nimport { createAioCapture } from '@lib/agent/aio-capture';\n\n/** wizard tool vocabulary → the pi tool definitions it unlocks. */\nconst CODING_TOOL_MAP: Record<string, readonly string[]> = {\n Read: ['read'],\n Edit: ['edit'],\n Write: ['write'],\n Bash: ['bash'],\n Glob: ['find', 'ls'],\n Grep: ['grep'],\n};\n\n/** `mcp__wizard-tools__wizard_ask` → `wizard_ask`; native names pass through. */\nfunction shortToolName(name: string): string {\n return name.replace(/^mcp__.+?__/, '');\n}\n\n/**\n * The pi coding tools a task may use. An empty allow list means \"no\n * restriction\" (mirrors the SDK), so every coding tool registers.\n */\nexport function allowedPiCodingTools(\n allowedTools: readonly string[] | undefined,\n): Set<string> {\n const allowed = (allowedTools ?? []).map(shortToolName);\n const names = allowed.length\n ? allowed.flatMap((name) => CODING_TOOL_MAP[name] ?? [])\n : Object.values(CODING_TOOL_MAP).flat();\n return new Set(names);\n}\n\n/**\n * The orchestrator queue tools this agent gets. Everything not disallowed:\n * the seed's frontmatter disallows `complete_task` (it is not a task), a\n * task's disallows `enqueue_task` (the seed owns the graph).\n */\nexport function allowedOrchestratorTools(\n disallowedTools: readonly string[] | undefined,\n): Set<string> {\n return new Set(queueTools(disallowedTools ?? []));\n}\n\n/**\n * The wizard tools a task gets. Four are always on — their handlers are fenced\n * and the coding tasks depend on them. The rest are opt-in per task through\n * its frontmatter: `wizard_ask` stops the run until a person answers, and the\n * skill-menu pair (`load_skill_menu`, `install_skill`) lets a task pull its\n * own skill variant.\n */\nconst ALWAYS_ON_WIZARD_TOOLS = [\n 'check_env_keys',\n 'set_env_values',\n 'detect_package_manager',\n 'publish_handoff',\n];\n\nconst OPT_IN_WIZARD_TOOLS = ['wizard_ask', 'load_skill_menu', 'install_skill'];\n\nexport function allowedPiWizardTools(\n allowedTools: readonly string[] | undefined,\n): Set<string> {\n const allowed = (allowedTools ?? []).map(shortToolName);\n return new Set([\n ...ALWAYS_ON_WIZARD_TOOLS,\n ...OPT_IN_WIZARD_TOOLS.filter((tool) => allowed.includes(tool)),\n ]);\n}\n\n/**\n * The disallow list for the security fence: the wizard-vocabulary names as\n * given (the fence translates pi built-ins to the same vocabulary) plus the\n * short names, so a disallowed orchestrator tool is blocked under the name pi\n * would call it by.\n */\nexport function fenceDisallowList(\n disallowedTools: readonly string[] | undefined,\n): string[] {\n const names = disallowedTools ?? [];\n return [...new Set([...names, ...names.map(shortToolName)])];\n}\n\n/** Nudges when the session returns without the work reaching a terminal state. */\nconst MAX_TASK_NUDGES = 3;\n\nconst TASK_NUDGE =\n 'You have not called complete_task yet. Finish your task now: if the work is done, call complete_task with your handoff; if it cannot be done, call it with status \"failed\" or \"not needed\" and say why.';\n\nconst SEED_NUDGE =\n 'The queue is still empty. Seed it now with enqueue_task calls for the task graph you planned.';\n\n/** Whether this unit of work has reached its terminal state. */\nfunction isSettled(ctx: OrchestratorToolsContext): boolean {\n if (!ctx.currentTaskId) {\n // The seed's job is a seeded queue, not a complete_task call.\n return ctx.store.list().length > 0;\n }\n const task = ctx.store.get(ctx.currentTaskId);\n return (\n !!task &&\n (task.status === TaskStatus.Done ||\n task.status === TaskStatus.Failed ||\n task.status === TaskStatus.Skipped)\n );\n}\n\nexport async function runPiTask(inputs: TaskRunInputs): Promise<AgentResult> {\n const {\n session,\n programConfig,\n boot,\n prompt,\n spinner,\n model: modelId,\n effort,\n allowedTools,\n disallowedTools,\n askBridge,\n orchestrator,\n spinnerMessage,\n successMessage,\n errorMessage,\n requestRemark,\n analyticsProperties,\n } = inputs;\n\n if (spinnerMessage) spinner.start(spinnerMessage);\n\n const capture = createAioCapture({\n enabled: session.captureAio,\n projectApiKey: boot.credentials.projectApiKey,\n apiHost: boot.credentials.host.apiHost,\n runTags: boot.wizardMetadata,\n });\n\n const startTime = Date.now();\n const signals = new AgentOutputSignals();\n let assistantTurns = 0;\n const runDurations = () => {\n const durationMs = Date.now() - startTime;\n return {\n duration_ms: durationMs,\n duration_seconds: Math.round(durationMs / 1000),\n };\n };\n const captureAborted = () =>\n analytics.wizardCapture('agent aborted', {\n ...runDurations(),\n model: modelId,\n ...analyticsProperties,\n });\n\n try {\n const sdk = await import('@earendil-works/pi-coding-agent');\n const {\n createAgentSession,\n DefaultResourceLoader,\n SessionManager,\n AuthStorage,\n ModelRegistry,\n getAgentDir,\n createLsToolDefinition,\n createFindToolDefinition,\n createGrepToolDefinition,\n createBashToolDefinition,\n createReadToolDefinition,\n createEditToolDefinition,\n createWriteToolDefinition,\n } = sdk;\n\n const { provider, caps } = buildGatewayProvider({\n gatewayUrl: boot.credentials.host.gatewayUrl,\n accessToken: boot.credentials.accessToken,\n wizardMetadata: boot.wizardMetadata,\n wizardFlags: boot.wizardFlags,\n modelId,\n // Per-task agents own their effort via the prompt frontmatter, falling\n // back to the model table.\n effort,\n });\n const registry = ModelRegistry.inMemory(AuthStorage.create());\n registry.registerProvider(GATEWAY_PROVIDER, provider as never);\n const model = registry.find(GATEWAY_PROVIDER, modelId);\n if (!model) {\n return {\n error: AgentErrorType.API_ERROR,\n message: 'pi: gateway model could not be resolved',\n };\n }\n\n // Shared flag: true while a wizard_ask overlay is open. The ask tool sets\n // it (onAskPendingChange, below); the security fence reads it to pause\n // Write/Edit until the answer comes back. Wired the same way as the linear\n // pi run — without it the warehouse task could mutate files while its\n // credential prompt sits open (up to TASK_ASK_TIMEOUT_MS).\n const askState = { pending: false };\n\n // The same fail-closed fence as the linear run, with the task's disallow\n // list layered in (both the wizard-vocabulary and pi-short names).\n const { createSecurityExtension } = await import('./security');\n const security = createSecurityExtension({\n disallowedTools: fenceDisallowList(disallowedTools),\n triageProvider: boot.triageProvider,\n getWizardAskPending: () => askState.pending,\n });\n const { prewarmYaraScanner } = await import('@lib/yara-hooks');\n void prewarmYaraScanner();\n\n // PostHog MCP, for the tasks whose prompt requests it. Tasks that never\n // asked skip the handshake and never see a posthog tool.\n const extensionFactories = [security.factory] as Array<\n (pi: unknown) => void\n >;\n let mcpCleanup: (() => void) | undefined;\n let posthogMcp = false;\n if (allowsPostHogMcp(allowedTools)) {\n try {\n const { setupPostHogMcp } = await import('./mcp');\n const mcp = await setupPostHogMcp({\n mcpUrl: boot.credentials.host.mcpUrl,\n accessToken: boot.credentials.accessToken,\n userAgent: WIZARD_USER_AGENT,\n });\n extensionFactories.push(mcp.extensionFactory);\n mcpCleanup = mcp.cleanup;\n posthogMcp = true;\n } catch (err) {\n // Silent here reads as a task failure minutes later: a task that asked\n // for this tool can only skip or fail without it.\n logToFile(`[pi-task] PostHog MCP setup skipped: ${String(err)}`);\n analytics.wizardCapture('mcp setup failed', {\n harness: 'pi',\n scope: 'task',\n error: String(err).slice(0, 300),\n });\n }\n }\n\n const codingTools = allowedPiCodingTools(allowedTools);\n const orchestratorTools = allowedOrchestratorTools(disallowedTools);\n\n const resourceLoader = new DefaultResourceLoader({\n cwd: session.installDir,\n agentDir: getAgentDir(),\n systemPrompt: assembleCommandments({\n program: programConfig.id,\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n caps: { bash: codingTools.has('bash'), posthogMcp },\n }),\n noExtensions: true,\n noSkills: true,\n noContextFiles: true,\n noPromptTemplates: true,\n noThemes: true,\n extensionFactories,\n });\n await resourceLoader.reload();\n\n // The task's coding tools, gated by its allow list. Reads and searches run\n // in parallel; mutating tools stay sequential. Bash subprocesses get the\n // scrubbed env, same as the linear run.\n const dir = session.installDir;\n const codingToolFactories = {\n read: () => withMode(createReadToolDefinition(dir), 'parallel'),\n edit: () => withMode(createEditToolDefinition(dir), 'sequential'),\n write: () => withMode(createWriteToolDefinition(dir), 'sequential'),\n bash: () =>\n withMode(\n createBashToolDefinition(dir, {\n spawnHook: (ctx) => ({ ...ctx, env: buildScrubbedEnv() }),\n }),\n 'sequential',\n ),\n ls: () => withMode(createLsToolDefinition(dir), 'parallel'),\n find: () => withMode(createFindToolDefinition(dir), 'parallel'),\n grep: () => withMode(createGrepToolDefinition(dir), 'parallel'),\n } as const;\n const codingToolDefs = Object.entries(codingToolFactories)\n .filter(([name]) => codingTools.has(name))\n .map(([, make]) => make());\n\n // Wizard env + package-manager tools are always on — their handlers are\n // fenced, and init/build tasks depend on them. publish_handoff rides\n // along (store-only handler) so the report task can publish the handoff.\n const { createWizardPiTools } = await import('./tools');\n const wizardToolNames = allowedPiWizardTools(allowedTools);\n const wizardTools = createWizardPiTools({\n workingDirectory: dir,\n skillsBaseUrl: boot.skillsBaseUrl,\n triageProvider: boot.triageProvider,\n // Present only for a task allowed to ask; without it wizard_ask errors\n // instead of hanging on a prompt nobody will ever see.\n askBridge,\n // Pause Write/Edit while the ask overlay is open (see askState above).\n onAskPendingChange: (pending) => {\n askState.pending = pending;\n },\n }).filter((t) => wizardToolNames.has(t.name));\n\n const { createPiOrchestratorTools } = await import('./orchestrator-tools');\n const queueTools = createPiOrchestratorTools(orchestrator).filter((t) =>\n orchestratorTools.has(t.name),\n );\n\n const customTools = [...codingToolDefs, ...wizardTools, ...queueTools];\n const { session: agentSession } = await createAgentSession({\n model,\n modelRegistry: registry,\n thinkingLevel: caps.thinkingLevel,\n cwd: dir,\n sessionManager: SessionManager.inMemory(dir),\n resourceLoader,\n noTools: 'builtin',\n customTools,\n });\n await agentSession.bindExtensions({});\n\n // The one complete list: exactly the tools registered on this session, in\n // the names the agent will call them by. posthog_exec binds as an extension.\n const toolNames = [\n ...customTools.map((t) => t.name),\n ...(posthogMcp ? ['posthog_exec'] : []),\n ];\n logToFile(`[pi-task] tools passed to model: ${toolNames.join(', ')}`);\n const taskPrompt = `${prompt}\\n\\n${renderToolInventory(toolNames)}`;\n\n const unsubscribe = agentSession.subscribe((event) => {\n // Mirror the turn into AIO. No-op when --capture-aio is off. Runs\n // before the role guard so the module's own filter is authoritative.\n capture.captureFromPiMessageEndEvent(event);\n\n switch (event.type) {\n case 'message_end': {\n // User prompts also emit message_end; only assistant turns count.\n if ((event.message as { role?: string })?.role !== 'assistant') {\n break;\n }\n assistantTurns += 1;\n const assistant = extractText(event.message).trim();\n if (assistant) {\n logToFile(`[pi-task] assistant: ${assistant.slice(0, 1000)}`);\n applyOutroMarkers(assistant);\n const statusText = lastStatusLine(assistant);\n if (statusText) {\n getUI().pushStatus(statusText);\n spinner.message(statusText);\n }\n for (const line of assistant.split('\\n')) signals.push(line);\n }\n break;\n }\n case 'tool_execution_start': {\n const args = JSON.stringify(event.args ?? {}).slice(0, 200);\n logToFile(`[pi-task] → ${event.toolName} ${args}`);\n break;\n }\n case 'tool_execution_end': {\n if (event.isError) {\n const detail =\n typeof event.result === 'string'\n ? event.result\n : JSON.stringify(event.result ?? '');\n logToFile(`[pi-task] ✗ ${event.toolName}: ${detail.slice(0, 300)}`);\n }\n break;\n }\n default:\n break;\n }\n });\n\n // Seed AIO capture with this task's prompt — includes any handoff data\n // from prior tasks (the orchestrator bakes it into the prompt string\n // before it reaches this call site).\n capture.setInitialPrompt(taskPrompt);\n\n try {\n await agentSession.prompt(taskPrompt);\n\n // pi's prompt() resolves the moment a turn carries no tool call — which\n // an agent mid-plan does emit. While the work has not reached its\n // terminal state (task not reported, seed queue still empty), nudge.\n let nudges = 0;\n while (\n nudges < MAX_TASK_NUDGES &&\n !security.state.criticalViolation &&\n !isSettled(orchestrator)\n ) {\n nudges += 1;\n logToFile(\n `[pi-task] completion guard: not settled, nudge ${nudges}/${MAX_TASK_NUDGES}`,\n );\n await agentSession.prompt(\n orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE,\n );\n }\n\n if (requestRemark && !security.state.criticalViolation) {\n try {\n await agentSession.prompt(REMARK_INSTRUCTION);\n } catch (err) {\n logToFile(`[pi-task] remark request failed: ${String(err)}`);\n }\n }\n } finally {\n unsubscribe();\n mcpCleanup?.();\n }\n\n if (security.state.criticalViolation) {\n spinner.stop('Security violation detected');\n logToFile(\n `[pi-task] terminated: YARA violation (blocked ${security.state.blockedCount} call(s))`,\n );\n captureAborted();\n return { error: AgentErrorType.YARA_VIOLATION };\n }\n\n const remark = signals.remark();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\n }\n\n const stats = agentSession.getSessionStats();\n const durations = runDurations();\n analytics.wizardCapture('agent completed', {\n ...durations,\n model: modelId,\n num_turns: assistantTurns,\n input_tokens: stats.tokens.input,\n output_tokens: stats.tokens.output,\n cache_creation_input_tokens: stats.tokens.cacheWrite,\n cache_read_input_tokens: stats.tokens.cacheRead,\n ...analyticsProperties,\n });\n // Per-task usage on one parseable line so a run's per-task time and cost are\n // observable from the log, not only from analytics.\n const taskType =\n typeof (analyticsProperties as { task_type?: unknown })?.task_type ===\n 'string'\n ? (analyticsProperties as { task_type: string }).task_type\n : modelId;\n logToFile(\n `[pi-task] usage task=${taskType} model=${modelId} effort=${caps.thinkingLevel} dur=${durations.duration_seconds}s turns=${assistantTurns} in=${stats.tokens.input} out=${stats.tokens.output} cacheR=${stats.tokens.cacheRead} cacheW=${stats.tokens.cacheWrite}`,\n );\n if (successMessage) spinner.stop(successMessage);\n return {};\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logToFile(`[pi-task] run error: ${message}`);\n if (errorMessage || spinnerMessage) {\n spinner.stop(errorMessage ?? 'Task failed');\n }\n captureAborted();\n const lower = message.toLowerCase();\n if (lower.includes('rate limit') || lower.includes('429')) {\n return { error: AgentErrorType.RATE_LIMIT, message };\n }\n return { error: AgentErrorType.API_ERROR, message };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,MAAM,kBAAqD;CACzD,MAAM,CAAC,OAAO;CACd,MAAM,CAAC,OAAO;CACd,OAAO,CAAC,QAAQ;CAChB,MAAM,CAAC,OAAO;CACd,MAAM,CAAC,QAAQ,KAAK;CACpB,MAAM,CAAC,OAAO;CACf;;AAGD,SAAS,cAAc,MAAsB;AAC3C,QAAO,KAAK,QAAQ,eAAe,GAAG;;;;;;AAOxC,SAAgB,qBACd,cACa;CACb,MAAM,WAAW,gBAAgB,EAAE,EAAE,IAAI,cAAc;CACvD,MAAM,QAAQ,QAAQ,SAClB,QAAQ,SAAS,SAAS,gBAAgB,SAAS,EAAE,CAAC,GACtD,OAAO,OAAO,gBAAgB,CAAC,MAAM;AACzC,QAAO,IAAI,IAAI,MAAM;;;;;;;AAQvB,SAAgB,yBACd,iBACa;AACb,QAAO,IAAI,IAAI,WAAW,mBAAmB,EAAE,CAAC,CAAC;;;;;;;;;AAUnD,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACD;AAED,MAAM,sBAAsB;CAAC;CAAc;CAAmB;CAAgB;AAE9E,SAAgB,qBACd,cACa;CACb,MAAM,WAAW,gBAAgB,EAAE,EAAE,IAAI,cAAc;AACvD,QAAO,IAAI,IAAI,CACb,GAAG,wBACH,GAAG,oBAAoB,QAAQ,SAAS,QAAQ,SAAS,KAAK,CAAC,CAChE,CAAC;;;;;;;;AASJ,SAAgB,kBACd,iBACU;CACV,MAAM,QAAQ,mBAAmB,EAAE;AACnC,QAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,GAAG,MAAM,IAAI,cAAc,CAAC,CAAC,CAAC;;;AAI9D,MAAM,kBAAkB;AAExB,MAAM,aACJ;AAEF,MAAM,aACJ;;AAGF,SAAS,UAAU,KAAwC;AACzD,KAAI,CAAC,IAAI,cAEP,QAAO,IAAI,MAAM,MAAM,CAAC,SAAS;CAEnC,MAAM,OAAO,IAAI,MAAM,IAAI,IAAI,cAAc;AAC7C,QACE,CAAC,CAAC,SACD,KAAK,WAAW,WAAW,QAC1B,KAAK,WAAW,WAAW,UAC3B,KAAK,WAAW,WAAW;;AAIjC,eAAsB,UAAU,QAA6C;CAC3E,MAAM,EACJ,SACA,eACA,MACA,QACA,SACA,OAAO,SACP,QACA,cACA,iBACA,WACA,cACA,gBACA,gBACA,cACA,eACA,wBACE;AAEJ,KAAI,eAAgB,SAAQ,MAAM,eAAe;CAEjD,MAAM,UAAU,iBAAiB;EAC/B,SAAS,QAAQ;EACjB,eAAe,KAAK,YAAY;EAChC,SAAS,KAAK,YAAY,KAAK;EAC/B,SAAS,KAAK;EACf,CAAC;CAEF,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,UAAU,IAAI,oBAAoB;CACxC,IAAI,iBAAiB;CACrB,MAAM,qBAAqB;EACzB,MAAM,aAAa,KAAK,KAAK,GAAG;AAChC,SAAO;GACL,aAAa;GACb,kBAAkB,KAAK,MAAM,aAAa,IAAK;GAChD;;CAEH,MAAM,uBACJ,UAAU,cAAc,iBAAiB;EACvC,GAAG,cAAc;EACjB,OAAO;EACP,GAAG;EACJ,CAAC;AAEJ,KAAI;EAEF,MAAM,EACJ,oBACA,uBACA,gBACA,aACA,eACA,aACA,wBACA,0BACA,0BACA,0BACA,0BACA,0BACA,8BAdU,MAAM,OAAO;EAiBzB,MAAM,EAAE,UAAU,SAAS,qBAAqB;GAC9C,YAAY,KAAK,YAAY,KAAK;GAClC,aAAa,KAAK,YAAY;GAC9B,gBAAgB,KAAK;GACrB,aAAa,KAAK;GAClB;GAGA;GACD,CAAC;EACF,MAAM,WAAW,cAAc,SAAS,YAAY,QAAQ,CAAC;AAC7D,WAAS,iBAAiB,kBAAkB,SAAkB;EAC9D,MAAM,QAAQ,SAAS,KAAK,kBAAkB,QAAQ;AACtD,MAAI,CAAC,MACH,QAAO;GACL,OAAA;GACA,SAAS;GACV;EAQH,MAAM,WAAW,EAAE,SAAS,OAAO;EAInC,MAAM,EAAE,4BAA4B,MAAM,OAAO;EACjD,MAAM,WAAW,wBAAwB;GACvC,iBAAiB,kBAAkB,gBAAgB;GACnD,gBAAgB,KAAK;GACrB,2BAA2B,SAAS;GACrC,CAAC;EACF,MAAM,EAAE,uBAAuB,MAAM,OAAO;AACvC,sBAAoB;EAIzB,MAAM,qBAAqB,CAAC,SAAS,QAAQ;EAG7C,IAAI;EACJ,IAAI,aAAa;AACjB,MAAI,iBAAiB,aAAa,CAChC,KAAI;GACF,MAAM,EAAE,oBAAoB,MAAM,OAAO;GACzC,MAAM,MAAM,MAAM,gBAAgB;IAChC,QAAQ,KAAK,YAAY,KAAK;IAC9B,aAAa,KAAK,YAAY;IAC9B,WAAW;IACZ,CAAC;AACF,sBAAmB,KAAK,IAAI,iBAAiB;AAC7C,gBAAa,IAAI;AACjB,gBAAa;WACN,KAAK;AAGZ,aAAU,wCAAwC,OAAO,IAAI,GAAG;AAChE,aAAU,cAAc,oBAAoB;IAC1C,SAAS;IACT,OAAO;IACP,OAAO,OAAO,IAAI,CAAC,MAAM,GAAG,IAAI;IACjC,CAAC;;EAIN,MAAM,cAAc,qBAAqB,aAAa;EACtD,MAAM,oBAAoB,yBAAyB,gBAAgB;EAEnE,MAAM,iBAAiB,IAAI,sBAAsB;GAC/C,KAAK,QAAQ;GACb,UAAU,aAAa;GACvB,cAAc,qBAAqB;IACjC,SAAS,cAAc;IACvB,UAAA;IACA,SAAA;IACA,MAAM;KAAE,MAAM,YAAY,IAAI,OAAO;KAAE;KAAY;IACpD,CAAC;GACF,cAAc;GACd,UAAU;GACV,gBAAgB;GAChB,mBAAmB;GACnB,UAAU;GACV;GACD,CAAC;AACF,QAAM,eAAe,QAAQ;EAK7B,MAAM,MAAM,QAAQ;EAgBpB,MAAM,iBAAiB,OAAO,QAfF;GAC1B,YAAY,SAAS,yBAAyB,IAAI,EAAE,WAAW;GAC/D,YAAY,SAAS,yBAAyB,IAAI,EAAE,aAAa;GACjE,aAAa,SAAS,0BAA0B,IAAI,EAAE,aAAa;GACnE,YACE,SACE,yBAAyB,KAAK,EAC5B,YAAY,SAAS;IAAE,GAAG;IAAK,KAAK,kBAAkB;IAAE,GACzD,CAAC,EACF,aACD;GACH,UAAU,SAAS,uBAAuB,IAAI,EAAE,WAAW;GAC3D,YAAY,SAAS,yBAAyB,IAAI,EAAE,WAAW;GAC/D,YAAY,SAAS,yBAAyB,IAAI,EAAE,WAAW;GAChE,CACyD,CACvD,QAAQ,CAAC,UAAU,YAAY,IAAI,KAAK,CAAC,CACzC,KAAK,GAAG,UAAU,MAAM,CAAC;EAK5B,MAAM,EAAE,wBAAwB,MAAM,OAAO;EAC7C,MAAM,kBAAkB,qBAAqB,aAAa;EAC1D,MAAM,cAAc,oBAAoB;GACtC,kBAAkB;GAClB,eAAe,KAAK;GACpB,gBAAgB,KAAK;GAGrB;GAEA,qBAAqB,YAAY;AAC/B,aAAS,UAAU;;GAEtB,CAAC,CAAC,QAAQ,MAAM,gBAAgB,IAAI,EAAE,KAAK,CAAC;EAE7C,MAAM,EAAE,8BAA8B,MAAM,OAAO;EACnD,MAAM,aAAa,0BAA0B,aAAa,CAAC,QAAQ,MACjE,kBAAkB,IAAI,EAAE,KAAK,CAC9B;EAED,MAAM,cAAc;GAAC,GAAG;GAAgB,GAAG;GAAa,GAAG;GAAW;EACtE,MAAM,EAAE,SAAS,iBAAiB,MAAM,mBAAmB;GACzD;GACA,eAAe;GACf,eAAe,KAAK;GACpB,KAAK;GACL,gBAAgB,eAAe,SAAS,IAAI;GAC5C;GACA,SAAS;GACT;GACD,CAAC;AACF,QAAM,aAAa,eAAe,EAAE,CAAC;EAIrC,MAAM,YAAY,CAChB,GAAG,YAAY,KAAK,MAAM,EAAE,KAAK,EACjC,GAAI,aAAa,CAAC,eAAe,GAAG,EAAE,CACvC;AACD,YAAU,oCAAoC,UAAU,KAAK,KAAK,GAAG;EACrE,MAAM,aAAa,GAAG,OAAO,MAAM,oBAAoB,UAAU;EAEjE,MAAM,cAAc,aAAa,WAAW,UAAU;AAGpD,WAAQ,6BAA6B,MAAM;AAE3C,WAAQ,MAAM,MAAd;IACE,KAAK,eAAe;AAElB,SAAK,MAAM,SAA+B,SAAS,YACjD;AAEF,uBAAkB;KAClB,MAAM,YAAY,YAAY,MAAM,QAAQ,CAAC,MAAM;AACnD,SAAI,WAAW;AACb,gBAAU,wBAAwB,UAAU,MAAM,GAAG,IAAK,GAAG;AAC7D,wBAAkB,UAAU;MAC5B,MAAM,aAAa,eAAe,UAAU;AAC5C,UAAI,YAAY;AACd,cAAO,CAAC,WAAW,WAAW;AAC9B,eAAQ,QAAQ,WAAW;;AAE7B,WAAK,MAAM,QAAQ,UAAU,MAAM,KAAK,CAAE,SAAQ,KAAK,KAAK;;AAE9D;;IAEF,KAAK,wBAAwB;KAC3B,MAAM,OAAO,KAAK,UAAU,MAAM,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI;AAC3D,eAAU,eAAe,MAAM,SAAS,GAAG,OAAO;AAClD;;IAEF,KAAK;AACH,SAAI,MAAM,SAAS;MACjB,MAAM,SACJ,OAAO,MAAM,WAAW,WACpB,MAAM,SACN,KAAK,UAAU,MAAM,UAAU,GAAG;AACxC,gBAAU,eAAe,MAAM,SAAS,IAAI,OAAO,MAAM,GAAG,IAAI,GAAG;;AAErE;IAEF,QACE;;IAEJ;AAKF,UAAQ,iBAAiB,WAAW;AAEpC,MAAI;AACF,SAAM,aAAa,OAAO,WAAW;GAKrC,IAAI,SAAS;AACb,UACE,SAAS,mBACT,CAAC,SAAS,MAAM,qBAChB,CAAC,UAAU,aAAa,EACxB;AACA,cAAU;AACV,cACE,kDAAkD,OAAO,GAAG,kBAC7D;AACD,UAAM,aAAa,OACjB,aAAa,gBAAgB,aAAa,WAC3C;;AAGH,OAAI,iBAAiB,CAAC,SAAS,MAAM,kBACnC,KAAI;AACF,UAAM,aAAa,OAAO,mBAAmB;YACtC,KAAK;AACZ,cAAU,oCAAoC,OAAO,IAAI,GAAG;;YAGxD;AACR,gBAAa;AACb,iBAAc;;AAGhB,MAAI,SAAS,MAAM,mBAAmB;AACpC,WAAQ,KAAK,8BAA8B;AAC3C,aACE,iDAAiD,SAAS,MAAM,aAAa,WAC9E;AACD,mBAAgB;AAChB,UAAO,EAAE,OAAA,yBAAsC;;EAGjD,MAAM,SAAS,QAAQ,QAAQ;AAC/B,MAAI,OACF,WAAU,QAAQ,0BAA0B,EAAE,QAAQ,CAAC;EAGzD,MAAM,QAAQ,aAAa,iBAAiB;EAC5C,MAAM,YAAY,cAAc;AAChC,YAAU,cAAc,mBAAmB;GACzC,GAAG;GACH,OAAO;GACP,WAAW;GACX,cAAc,MAAM,OAAO;GAC3B,eAAe,MAAM,OAAO;GAC5B,6BAA6B,MAAM,OAAO;GAC1C,yBAAyB,MAAM,OAAO;GACtC,GAAG;GACJ,CAAC;AAQF,YACE,wBALA,OAAQ,qBAAiD,cACzD,WACK,oBAA8C,YAC/C,QAE6B,SAAS,QAAQ,UAAU,KAAK,cAAc,OAAO,UAAU,iBAAiB,UAAU,eAAe,MAAM,MAAM,OAAO,MAAM,OAAO,MAAM,OAAO,OAAO,UAAU,MAAM,OAAO,UAAU,UAAU,MAAM,OAAO,aACvP;AACD,MAAI,eAAgB,SAAQ,KAAK,eAAe;AAChD,SAAO,EAAE;UACF,KAAK;EACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,YAAU,wBAAwB,UAAU;AAC5C,MAAI,gBAAgB,eAClB,SAAQ,KAAK,gBAAgB,cAAc;AAE7C,kBAAgB;EAChB,MAAM,QAAQ,QAAQ,aAAa;AACnC,MAAI,MAAM,SAAS,aAAa,IAAI,MAAM,SAAS,MAAM,CACvD,QAAO;GAAE,OAAA;GAAkC;GAAS;AAEtD,SAAO;GAAE,OAAA;GAAiC;GAAS"}
1
+ {"version":3,"file":"task-GnWId75q.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/task.ts"],"sourcesContent":["/**\n * Orchestrator-mode execution on pi: one fresh pi session per unit of work —\n * the seed plan, or one drained task. The linear pipeline's concerns (skill\n * menu, todo panel, event-plan cleanup) stay in `index.ts`; this module builds\n * the leaner per-task session: gateway model, security fence, the task's\n * allowed coding tools, the wizard env tools, and the in-process orchestrator\n * queue tools.\n *\n * The task's `allowedTools` / `disallowedTools` arrive in the wizard's tool\n * vocabulary (`Read`, `Edit`, `Glob`, …, plus MCP-qualified orchestrator names\n * from `agentRunTools`). pi is where they become real: allowed names decide\n * which pi tool definitions get registered at all, and the disallow list is\n * ALSO handed to the security fence, so a name that never got registered stays\n * blocked even if the model hallucinates it.\n *\n * Loaded lazily from `index.ts` (typebox/ESM constraint, same as tools.ts).\n */\n\nimport { getUI } from '@ui';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport {\n Harness,\n Sequence,\n WIZARD_REMARK_EVENT_NAME,\n WIZARD_USER_AGENT,\n} from '@lib/constants';\nimport {\n allowsPostHogMcp,\n queueTools,\n renderToolInventory,\n} from '@lib/agent/agent-prompt-loader';\nimport { AgentErrorType } from '@lib/agent/agent-interface';\nimport { REMARK_INSTRUCTION } from '@lib/agent/signals';\nimport { AgentOutputSignals } from '@lib/agent/output-signals';\nimport { TaskStatus } from '../../sequence/orchestrator/queue';\nimport type { OrchestratorToolsContext } from '../../sequence/orchestrator/queue-tools';\nimport type { AgentResult, TaskRunInputs } from '../types';\nimport { buildGatewayProvider, GATEWAY_PROVIDER } from './gateway';\nimport { assembleCommandments } from '../../switchboard/commandments';\nimport {\n applyOutroMarkers,\n buildScrubbedEnv,\n extractText,\n lastStatusLine,\n withMode,\n} from './index';\nimport { createAioCapture } from '@lib/agent/aio-capture';\n\n/** wizard tool vocabulary → the pi tool definitions it unlocks. */\nconst CODING_TOOL_MAP: Record<string, readonly string[]> = {\n Read: ['read'],\n Edit: ['edit'],\n Write: ['write'],\n Bash: ['bash'],\n Glob: ['find', 'ls'],\n Grep: ['grep'],\n};\n\n/** `mcp__wizard-tools__wizard_ask` → `wizard_ask`; native names pass through. */\nfunction shortToolName(name: string): string {\n return name.replace(/^mcp__.+?__/, '');\n}\n\n/**\n * The pi coding tools a task may use. An empty allow list means \"no\n * restriction\" (mirrors the SDK), so every coding tool registers.\n */\nexport function allowedPiCodingTools(\n allowedTools: readonly string[] | undefined,\n): Set<string> {\n const allowed = (allowedTools ?? []).map(shortToolName);\n const names = allowed.length\n ? allowed.flatMap((name) => CODING_TOOL_MAP[name] ?? [])\n : Object.values(CODING_TOOL_MAP).flat();\n return new Set(names);\n}\n\n/**\n * The orchestrator queue tools this agent gets. Everything not disallowed:\n * the seed's frontmatter disallows `complete_task` (it is not a task), a\n * task's disallows `enqueue_task` (the seed owns the graph).\n */\nexport function allowedOrchestratorTools(\n disallowedTools: readonly string[] | undefined,\n): Set<string> {\n return new Set(queueTools(disallowedTools ?? []));\n}\n\n/**\n * The wizard tools a task gets. Four are always on — their handlers are fenced\n * and the coding tasks depend on them. The rest are opt-in per task through\n * its frontmatter: `wizard_ask` stops the run until a person answers, and the\n * skill-menu pair (`load_skill_menu`, `install_skill`) lets a task pull its\n * own skill variant.\n */\nconst ALWAYS_ON_WIZARD_TOOLS = [\n 'check_env_keys',\n 'set_env_values',\n 'detect_package_manager',\n 'publish_handoff',\n];\n\nconst OPT_IN_WIZARD_TOOLS = ['wizard_ask', 'load_skill_menu', 'install_skill'];\n\nexport function allowedPiWizardTools(\n allowedTools: readonly string[] | undefined,\n): Set<string> {\n const allowed = (allowedTools ?? []).map(shortToolName);\n return new Set([\n ...ALWAYS_ON_WIZARD_TOOLS,\n ...OPT_IN_WIZARD_TOOLS.filter((tool) => allowed.includes(tool)),\n ]);\n}\n\n/**\n * The disallow list for the security fence: the wizard-vocabulary names as\n * given (the fence translates pi built-ins to the same vocabulary) plus the\n * short names, so a disallowed orchestrator tool is blocked under the name pi\n * would call it by.\n */\nexport function fenceDisallowList(\n disallowedTools: readonly string[] | undefined,\n): string[] {\n const names = disallowedTools ?? [];\n return [...new Set([...names, ...names.map(shortToolName)])];\n}\n\n/** Nudges when the session returns without the work reaching a terminal state. */\nconst MAX_TASK_NUDGES = 3;\n\nconst TASK_NUDGE =\n 'You have not called complete_task yet. Finish your task now: if the work is done, call complete_task with your handoff; if it cannot be done, call it with status \"failed\" or \"not needed\" and say why.';\n\nconst SEED_NUDGE =\n 'The queue is still empty. Seed it now with enqueue_task calls for the task graph you planned.';\n\n/** Whether this unit of work has reached its terminal state. */\nfunction isSettled(ctx: OrchestratorToolsContext): boolean {\n if (!ctx.currentTaskId) {\n // The seed's job is a seeded queue, not a complete_task call.\n return ctx.store.list().length > 0;\n }\n const task = ctx.store.get(ctx.currentTaskId);\n return (\n !!task &&\n (task.status === TaskStatus.Done ||\n task.status === TaskStatus.Failed ||\n task.status === TaskStatus.Skipped)\n );\n}\n\nexport async function runPiTask(inputs: TaskRunInputs): Promise<AgentResult> {\n const {\n session,\n programConfig,\n boot,\n prompt,\n spinner,\n model: modelId,\n effort,\n allowedTools,\n disallowedTools,\n askBridge,\n orchestrator,\n spinnerMessage,\n successMessage,\n errorMessage,\n requestRemark,\n analyticsProperties,\n } = inputs;\n\n if (spinnerMessage) spinner.start(spinnerMessage);\n\n const capture = createAioCapture({\n enabled: session.captureAio,\n projectApiKey: boot.credentials.projectApiKey,\n apiHost: boot.credentials.host.apiHost,\n runTags: boot.wizardMetadata,\n });\n\n const startTime = Date.now();\n const signals = new AgentOutputSignals();\n let assistantTurns = 0;\n const runDurations = () => {\n const durationMs = Date.now() - startTime;\n return {\n duration_ms: durationMs,\n duration_seconds: Math.round(durationMs / 1000),\n };\n };\n const captureAborted = () =>\n analytics.wizardCapture('agent aborted', {\n ...runDurations(),\n model: modelId,\n ...analyticsProperties,\n });\n\n try {\n const sdk = await import('@earendil-works/pi-coding-agent');\n const {\n createAgentSession,\n DefaultResourceLoader,\n SessionManager,\n AuthStorage,\n ModelRegistry,\n getAgentDir,\n createLsToolDefinition,\n createFindToolDefinition,\n createGrepToolDefinition,\n createBashToolDefinition,\n createReadToolDefinition,\n createEditToolDefinition,\n createWriteToolDefinition,\n } = sdk;\n\n const { provider, caps } = buildGatewayProvider({\n gatewayUrl: boot.credentials.host.gatewayUrl,\n accessToken: boot.credentials.accessToken,\n wizardMetadata: boot.wizardMetadata,\n wizardFlags: boot.wizardFlags,\n modelId,\n // Per-task agents own their effort via the prompt frontmatter, falling\n // back to the model table.\n effort,\n });\n const registry = ModelRegistry.inMemory(AuthStorage.create());\n registry.registerProvider(GATEWAY_PROVIDER, provider as never);\n const model = registry.find(GATEWAY_PROVIDER, modelId);\n if (!model) {\n return {\n error: AgentErrorType.API_ERROR,\n message: 'pi: gateway model could not be resolved',\n };\n }\n\n // Shared flag: true while a wizard_ask overlay is open. The ask tool sets\n // it (onAskPendingChange, below); the security fence reads it to pause\n // Write/Edit until the answer comes back. Wired the same way as the linear\n // pi run — without it the warehouse task could mutate files while its\n // credential prompt sits open (up to TASK_ASK_TIMEOUT_MS).\n const askState = { pending: false };\n\n // The same fail-closed fence as the linear run, with the task's disallow\n // list layered in (both the wizard-vocabulary and pi-short names).\n const { createSecurityExtension } = await import('./security');\n const security = createSecurityExtension({\n disallowedTools: fenceDisallowList(disallowedTools),\n triageProvider: boot.triageProvider,\n getWizardAskPending: () => askState.pending,\n });\n const { prewarmYaraScanner } = await import('@lib/yara-hooks');\n void prewarmYaraScanner();\n\n // PostHog MCP, for the tasks whose prompt requests it. Tasks that never\n // asked skip the handshake and never see a posthog tool.\n const extensionFactories = [security.factory] as Array<\n (pi: unknown) => void\n >;\n let mcpCleanup: (() => void) | undefined;\n let posthogMcp = false;\n if (allowsPostHogMcp(allowedTools)) {\n try {\n const { setupPostHogMcp } = await import('./mcp');\n const mcp = await setupPostHogMcp({\n mcpUrl: boot.credentials.host.mcpUrl,\n accessToken: boot.credentials.accessToken,\n userAgent: WIZARD_USER_AGENT,\n });\n extensionFactories.push(mcp.extensionFactory);\n mcpCleanup = mcp.cleanup;\n posthogMcp = true;\n } catch (err) {\n // Silent here reads as a task failure minutes later: a task that asked\n // for this tool can only skip or fail without it.\n logToFile(`[pi-task] PostHog MCP setup skipped: ${String(err)}`);\n analytics.wizardCapture('mcp setup failed', {\n harness: 'pi',\n scope: 'task',\n error: String(err).slice(0, 300),\n });\n }\n }\n\n const codingTools = allowedPiCodingTools(allowedTools);\n const orchestratorTools = allowedOrchestratorTools(disallowedTools);\n\n const resourceLoader = new DefaultResourceLoader({\n cwd: session.installDir,\n agentDir: getAgentDir(),\n systemPrompt: assembleCommandments({\n program: programConfig.id,\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n caps: { bash: codingTools.has('bash'), posthogMcp },\n }),\n noExtensions: true,\n noSkills: true,\n noContextFiles: true,\n noPromptTemplates: true,\n noThemes: true,\n extensionFactories,\n });\n await resourceLoader.reload();\n\n // The task's coding tools, gated by its allow list. Reads and searches run\n // in parallel; mutating tools stay sequential. Bash subprocesses get the\n // scrubbed env, same as the linear run.\n const dir = session.installDir;\n const codingToolFactories = {\n read: () => withMode(createReadToolDefinition(dir), 'parallel'),\n edit: () => withMode(createEditToolDefinition(dir), 'sequential'),\n write: () => withMode(createWriteToolDefinition(dir), 'sequential'),\n bash: () =>\n withMode(\n createBashToolDefinition(dir, {\n spawnHook: (ctx) => ({ ...ctx, env: buildScrubbedEnv() }),\n }),\n 'sequential',\n ),\n ls: () => withMode(createLsToolDefinition(dir), 'parallel'),\n find: () => withMode(createFindToolDefinition(dir), 'parallel'),\n grep: () => withMode(createGrepToolDefinition(dir), 'parallel'),\n } as const;\n const codingToolDefs = Object.entries(codingToolFactories)\n .filter(([name]) => codingTools.has(name))\n .map(([, make]) => make());\n\n // Wizard env + package-manager tools are always on — their handlers are\n // fenced, and init/build tasks depend on them. publish_handoff rides\n // along (store-only handler) so the report task can publish the handoff.\n const { createWizardPiTools } = await import('./tools');\n const wizardToolNames = allowedPiWizardTools(allowedTools);\n const wizardTools = createWizardPiTools({\n workingDirectory: dir,\n skillsBaseUrl: boot.skillsBaseUrl,\n triageProvider: boot.triageProvider,\n // Present only for a task allowed to ask; without it wizard_ask errors\n // instead of hanging on a prompt nobody will ever see.\n askBridge,\n // Pause Write/Edit while the ask overlay is open (see askState above).\n onAskPendingChange: (pending) => {\n askState.pending = pending;\n },\n }).filter((t) => wizardToolNames.has(t.name));\n\n const { createPiOrchestratorTools } = await import('./orchestrator-tools');\n const queueTools = createPiOrchestratorTools(orchestrator).filter((t) =>\n orchestratorTools.has(t.name),\n );\n\n const customTools = [...codingToolDefs, ...wizardTools, ...queueTools];\n const { session: agentSession } = await createAgentSession({\n model,\n modelRegistry: registry,\n thinkingLevel: caps.thinkingLevel,\n cwd: dir,\n sessionManager: SessionManager.inMemory(dir),\n resourceLoader,\n noTools: 'builtin',\n customTools,\n });\n await agentSession.bindExtensions({});\n\n // The one complete list: exactly the tools registered on this session, in\n // the names the agent will call them by. posthog_exec binds as an extension.\n const toolNames = [\n ...customTools.map((t) => t.name),\n ...(posthogMcp ? ['posthog_exec'] : []),\n ];\n logToFile(`[pi-task] tools passed to model: ${toolNames.join(', ')}`);\n const taskPrompt = `${prompt}\\n\\n${renderToolInventory(toolNames)}`;\n\n const unsubscribe = agentSession.subscribe((event) => {\n // Mirror the turn into AIO. No-op when --capture-aio is off. Runs\n // before the role guard so the module's own filter is authoritative.\n capture.captureFromPiMessageEndEvent(event);\n\n switch (event.type) {\n case 'message_end': {\n // User prompts also emit message_end; only assistant turns count.\n if ((event.message as { role?: string })?.role !== 'assistant') {\n break;\n }\n assistantTurns += 1;\n const assistant = extractText(event.message).trim();\n if (assistant) {\n logToFile(`[pi-task] assistant: ${assistant.slice(0, 1000)}`);\n applyOutroMarkers(assistant);\n const statusText = lastStatusLine(assistant);\n if (statusText) {\n getUI().pushStatus(statusText);\n spinner.message(statusText);\n }\n for (const line of assistant.split('\\n')) signals.push(line);\n }\n break;\n }\n case 'tool_execution_start': {\n const args = JSON.stringify(event.args ?? {}).slice(0, 200);\n logToFile(`[pi-task] → ${event.toolName} ${args}`);\n break;\n }\n case 'tool_execution_end': {\n if (event.isError) {\n const detail =\n typeof event.result === 'string'\n ? event.result\n : JSON.stringify(event.result ?? '');\n logToFile(`[pi-task] ✗ ${event.toolName}: ${detail.slice(0, 300)}`);\n }\n break;\n }\n default:\n break;\n }\n });\n\n // Seed AIO capture with this task's prompt — includes any handoff data\n // from prior tasks (the orchestrator bakes it into the prompt string\n // before it reaches this call site).\n capture.setInitialPrompt(taskPrompt);\n\n try {\n await agentSession.prompt(taskPrompt);\n\n // pi's prompt() resolves the moment a turn carries no tool call — which\n // an agent mid-plan does emit. While the work has not reached its\n // terminal state (task not reported, seed queue still empty), nudge.\n let nudges = 0;\n while (\n nudges < MAX_TASK_NUDGES &&\n !security.state.criticalViolation &&\n !isSettled(orchestrator)\n ) {\n nudges += 1;\n logToFile(\n `[pi-task] completion guard: not settled, nudge ${nudges}/${MAX_TASK_NUDGES}`,\n );\n await agentSession.prompt(\n orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE,\n );\n }\n\n if (requestRemark && !security.state.criticalViolation) {\n try {\n await agentSession.prompt(REMARK_INSTRUCTION);\n } catch (err) {\n logToFile(`[pi-task] remark request failed: ${String(err)}`);\n }\n }\n } finally {\n unsubscribe();\n mcpCleanup?.();\n }\n\n if (security.state.criticalViolation) {\n spinner.stop('Security violation detected');\n logToFile(\n `[pi-task] terminated: YARA violation (blocked ${security.state.blockedCount} call(s))`,\n );\n captureAborted();\n return { error: AgentErrorType.YARA_VIOLATION };\n }\n\n const remark = signals.remark();\n if (remark) {\n analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });\n }\n\n const stats = agentSession.getSessionStats();\n const durations = runDurations();\n analytics.wizardCapture('agent completed', {\n ...durations,\n model: modelId,\n num_turns: assistantTurns,\n input_tokens: stats.tokens.input,\n output_tokens: stats.tokens.output,\n cache_creation_input_tokens: stats.tokens.cacheWrite,\n cache_read_input_tokens: stats.tokens.cacheRead,\n ...analyticsProperties,\n });\n // Per-task usage on one parseable line so a run's per-task time and cost are\n // observable from the log, not only from analytics.\n const taskType =\n typeof (analyticsProperties as { task_type?: unknown })?.task_type ===\n 'string'\n ? (analyticsProperties as { task_type: string }).task_type\n : modelId;\n logToFile(\n `[pi-task] usage task=${taskType} model=${modelId} effort=${caps.thinkingLevel} dur=${durations.duration_seconds}s turns=${assistantTurns} in=${stats.tokens.input} out=${stats.tokens.output} cacheR=${stats.tokens.cacheRead} cacheW=${stats.tokens.cacheWrite}`,\n );\n if (successMessage) spinner.stop(successMessage);\n return {};\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logToFile(`[pi-task] run error: ${message}`);\n if (errorMessage || spinnerMessage) {\n spinner.stop(errorMessage ?? 'Task failed');\n }\n captureAborted();\n const lower = message.toLowerCase();\n if (lower.includes('rate limit') || lower.includes('429')) {\n return { error: AgentErrorType.RATE_LIMIT, message };\n }\n return { error: AgentErrorType.API_ERROR, message };\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
- import { s as logToFile } from "./debug-Cm1hvkVy.js";
2
- import "./wizard-session-Bn6dO1gE.js";
3
- import { PostHogDestination } from "./posthog-9To56cSd.js";
4
- import { t as startFileWatcher } from "./file-watcher-Dciz09Rr.js";
1
+ import { t as sanitizeErrorDetail } from "./errors-BHervYGs.js";
2
+ import { s as logToFile } from "./debug-BsHD83Vy.js";
3
+ import "./wizard-session-Cex1BiXs.js";
4
+ import { PostHogDestination } from "./posthog-DSn9bIZc.js";
5
+ import { t as startFileWatcher } from "./file-watcher-D1bdo9mq.js";
5
6
  //#region src/lib/task-stream/event-plan-watcher.ts
6
7
  const MAX_EVENT_PLAN_FILE_BYTES = 256 * 1024;
7
8
  const MAX_EVENT_COUNT = 50;
@@ -93,10 +94,16 @@ function sanitizeChannelId(value) {
93
94
  }
94
95
  function buildError(phase, outroData) {
95
96
  if (phase !== "error") return void 0;
96
- if (outroData?.kind === "error") return {
97
- type: "wizard_error",
98
- message: outroData.message ?? outroData.body ?? "Wizard run failed"
99
- };
97
+ if (outroData?.kind === "error") {
98
+ const error = {
99
+ type: "wizard_error",
100
+ message: outroData.message ?? outroData.body ?? "Wizard run failed"
101
+ };
102
+ if (outroData.errorCode) error.code = outroData.errorCode;
103
+ const safeDetail = sanitizeErrorDetail(outroData.errorDetail);
104
+ if (safeDetail) error.detail = safeDetail;
105
+ return error;
106
+ }
100
107
  return {
101
108
  type: "wizard_error",
102
109
  message: "Wizard run failed"
@@ -281,4 +288,4 @@ var TaskStreamPush = class {
281
288
  //#endregion
282
289
  export { PostHogDestination, TaskStreamPush };
283
290
 
284
- //# sourceMappingURL=task-stream-BK7JQ27G.js.map
291
+ //# sourceMappingURL=task-stream-DSzvwJrD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-stream-DSzvwJrD.js","names":[],"sources":["../src/lib/task-stream/event-plan-watcher.ts","../src/lib/task-stream/task-stream-push.ts"],"sourcesContent":["import type { PlannedEvent, WizardStore } from '@ui/tui/store';\nimport {\n startFileWatcher,\n type FileWatcherHandle,\n type FileWatcherOptions,\n} from '@lib/file-watcher';\n\nconst MAX_EVENT_PLAN_FILE_BYTES = 256 * 1024;\nconst MAX_EVENT_COUNT = 50;\nconst MAX_EVENT_NAME_LENGTH = 400;\nconst MAX_EVENT_DESCRIPTION_LENGTH = 4000;\n\nfunction firstString(...values: unknown[]): string | null {\n const value = values.find((candidate) => typeof candidate === 'string');\n return typeof value === 'string' ? value : null;\n}\n\nexport function normalizeEventPlan(parsed: unknown): PlannedEvent[] | null {\n if (!Array.isArray(parsed)) return null;\n\n const events: PlannedEvent[] = [];\n for (const value of parsed) {\n if (events.length >= MAX_EVENT_COUNT) break;\n\n const entry =\n value && typeof value === 'object'\n ? (value as Record<string, unknown>)\n : {};\n const name = firstString(entry.event_name, entry.name, entry.event);\n if (!name || !name.trim() || name.length > MAX_EVENT_NAME_LENGTH) continue;\n\n const description =\n firstString(entry.event_description, entry.description) ?? '';\n events.push({\n name,\n description: description.slice(0, MAX_EVENT_DESCRIPTION_LENGTH),\n });\n }\n\n return events;\n}\n\nexport class EventPlanWatcher {\n private handle: FileWatcherHandle | null = null;\n private captured = false;\n\n constructor(\n private readonly store: WizardStore,\n private readonly path: string,\n private readonly options: FileWatcherOptions = {},\n ) {}\n\n start(): void {\n if (this.handle || this.captured) return;\n\n this.handle = startFileWatcher(\n this.path,\n (parsed) => {\n const events = normalizeEventPlan(parsed);\n if (!events || events.length === 0) return;\n\n this.captured = true;\n this.store.setEventPlan(events);\n this.stop();\n },\n {\n ignoreInitialFile: true,\n maxFileSizeBytes: MAX_EVENT_PLAN_FILE_BYTES,\n ...this.options,\n },\n );\n }\n\n refresh(): void {\n if (this.captured) return;\n this.handle?.refresh();\n }\n\n stop(): void {\n this.handle?.stop();\n this.handle = null;\n }\n}\n","/**\n * Task-stream push — subscribes to WizardStore, builds payloads,\n * and fans out async to all registered destinations.\n *\n * Behaviour:\n * - `attach(store)` subscribe to store changes\n * - task updates debounced 250ms (trailing edge)\n * - phase transitions flush immediately, bypass debounce\n * - RunPhase.Idle skipped (no push)\n * - enabled === false destination delivery is disabled\n * - shutdown(timeoutMs) cancel pending, flush terminal phase\n * with timeout, never throw\n *\n * Concurrency: only one fan-out at a time. Emits during an in-flight\n * push are coalesced — at most one follow-up push fires with the\n * latest state once the current one settles.\n */\n\nimport type { WizardStore, TaskItem } from '@ui/tui/store';\nimport { TaskStatus } from '@ui/wizard-ui';\nimport {\n RunPhase,\n OutroKind,\n type OutroData,\n type PendingQuestion,\n} from '@lib/wizard-session';\nimport {\n type TaskStreamDestination,\n type TaskStreamUpdate,\n type StreamTask,\n type TaskStreamError,\n type StreamPendingInput,\n StreamTaskStatus,\n StreamEvent,\n} from './types';\nimport { EventPlanWatcher } from './event-plan-watcher';\nimport { logToFile } from '@utils/debug';\nimport { sanitizeErrorDetail } from '@lib/errors';\n\n/** Trailing-edge debounce window for non-phase-change emits. */\nconst DEBOUNCE_MS = 250;\n/** Default shutdown timeout for the final terminal flush. */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2000;\n\nconst STATUS_MAP: Record<TaskStatus, StreamTaskStatus> = {\n [TaskStatus.Pending]: StreamTaskStatus.Pending,\n [TaskStatus.InProgress]: StreamTaskStatus.InProgress,\n [TaskStatus.Completed]: StreamTaskStatus.Completed,\n // The stream has no skipped state; skipped is terminal, so report it resolved.\n [TaskStatus.Skipped]: StreamTaskStatus.Completed,\n};\n\nfunction buildTasks(items: TaskItem[]): StreamTask[] {\n return items.map((item, i) => ({\n id: String(i),\n title: item.label,\n status: STATUS_MAP[item.status] ?? StreamTaskStatus.Pending,\n }));\n}\n\n/** Drop \".SSSZ\" → \"Z\" so session_id segments stay routing-safe. */\nfunction secondPrecisionIso(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n\n/**\n * `workflow_id` and `skill_id` end up unescaped in Redis pub/sub\n * channel names, so the backend rejects anything outside\n * `^[A-Za-z0-9_.-]{1,255}$` with a 400. All current values already\n * comply; this is defence in depth in case a future caller passes\n * something with `:`, spaces, or other separators.\n */\nfunction sanitizeChannelId(value: string): string {\n return value.replace(/[^A-Za-z0-9_.-]/g, '-').slice(0, 255);\n}\n\nfunction buildError(\n phase: RunPhase,\n outroData: OutroData | null,\n): TaskStreamError | undefined {\n if (phase !== RunPhase.Error) return undefined;\n if (outroData?.kind === OutroKind.Error) {\n const message = outroData.message ?? outroData.body ?? 'Wizard run failed';\n const error: TaskStreamError = { type: 'wizard_error', message };\n if (outroData.errorCode) error.code = outroData.errorCode;\n const safeDetail = sanitizeErrorDetail(outroData.errorDetail);\n if (safeDetail) error.detail = safeDetail;\n return error;\n }\n return { type: 'wizard_error', message: 'Wizard run failed' };\n}\n\n/**\n * Sensitive asks (secrets, API keys) publish only the fact that input is\n * required — never the prompt text. The session row is team-visible and\n * outlives the prompt.\n */\nfunction buildPendingInput(\n question: PendingQuestion | null | undefined,\n): StreamPendingInput | undefined {\n if (!question) return undefined;\n const sensitive = question.questions.some((q) => q.sensitive === true);\n return {\n id: question.id,\n asked_at: question.askedAt ?? new Date().toISOString(),\n question_count: question.questions.length,\n sensitive,\n prompts: sensitive ? undefined : question.questions.map((q) => q.prompt),\n };\n}\n\nexport interface TaskStreamPushOptions {\n store: WizardStore;\n programId: string;\n destinations: TaskStreamDestination[];\n /** Optional absolute event-plan path to load into the store once. */\n eventPlanPath?: string;\n /** When false, destination subscription/delivery remains disabled. */\n enabled?: boolean;\n}\n\nexport class TaskStreamPush {\n private readonly store: WizardStore;\n private readonly destinations: TaskStreamDestination[];\n private readonly startedAt: string;\n private readonly programId: string;\n private readonly sessionId: string;\n private readonly eventPlanWatcher: EventPlanWatcher | null;\n\n private enabled: boolean;\n private created = false;\n private lastPushedPhase: RunPhase | null = null;\n private lastPushedQuestionId: string | null = null;\n\n private unsubscribe: (() => void) | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private inFlight: Promise<void> | null = null;\n private needsAnotherPush = false;\n private shuttingDown = false;\n\n constructor(opts: TaskStreamPushOptions) {\n this.store = opts.store;\n this.programId = sanitizeChannelId(opts.programId);\n this.destinations = opts.destinations;\n this.enabled = opts.enabled ?? true;\n const startedAt = new Date();\n this.eventPlanWatcher = opts.eventPlanPath\n ? new EventPlanWatcher(this.store, opts.eventPlanPath)\n : null;\n this.startedAt = secondPrecisionIso(startedAt);\n // skillId may not be set yet — fall back to programId so the\n // session_id is stable for the whole run regardless of when the\n // program metadata is populated.\n const skillId = sanitizeChannelId(\n this.store.session.skillId ?? this.programId,\n );\n this.sessionId = `${this.programId}-${skillId}-${this.startedAt}`;\n }\n\n /**\n * Load the event plan and subscribe to store changes. Destination delivery\n * remains disabled when `enabled === false`, but the plan still populates the\n * store for local and headless consumers.\n */\n attach(store?: WizardStore): void {\n this.eventPlanWatcher?.start();\n if (!this.enabled) return;\n if (this.unsubscribe) return;\n const target = store ?? this.store;\n this.unsubscribe = target.subscribe(() => this.onStoreChange());\n }\n\n /** Stop subscribing. Does not flush. */\n detach(): void {\n this.eventPlanWatcher?.stop();\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n /**\n * Cancel pending debounce, flush one final push if the current\n * phase is terminal, and resolve. Never throws. Bounded by\n * `timeoutMs` — if a destination hangs, this returns anyway.\n */\n async shutdown(\n timeoutMs: number = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n ): Promise<void> {\n this.shuttingDown = true;\n this.eventPlanWatcher?.refresh();\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.detach();\n if (!this.enabled) return;\n\n const phase = this.store.session.runPhase;\n const isTerminal = phase === RunPhase.Completed || phase === RunPhase.Error;\n if (!isTerminal) return;\n\n const flush = this.flush();\n if (timeoutMs <= 0) return;\n await Promise.race([\n flush,\n new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),\n ]);\n }\n\n /**\n * Imperative push — fires immediately regardless of phase. Kept as\n * the building block for both subscription-driven and direct calls.\n */\n async push(): Promise<void> {\n await this.flush();\n }\n\n // ── Internal ────────────────────────────────────────────────────\n\n private onStoreChange(): void {\n if (!this.enabled || this.shuttingDown) return;\n const phase = this.store.session.runPhase;\n if (phase === RunPhase.Idle) return;\n\n // A push is already in flight — coalesce. The in-flight push's\n // settle handler will trigger one follow-up with the latest state.\n if (this.inFlight) {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.needsAnotherPush = true;\n return;\n }\n\n const phaseChanged = phase !== this.lastPushedPhase;\n const questionId = this.store.session.pendingQuestion?.id ?? null;\n const questionChanged = questionId !== this.lastPushedQuestionId;\n if (phaseChanged || questionChanged) {\n // Phase transitions bypass the debounce: the web app needs to\n // see Running → Completed as soon as it lands. A wizard_ask\n // opening or closing is the same shape — the whole point of\n // publishing it is that the user is looking at the web app, so\n // a 250ms-stale \"needs your input\" defeats the purpose.\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n void this.flush();\n return;\n }\n\n // Task updates can arrive faster than we want to push. Debounce\n // them — the last update in a burst wins.\n if (this.debounceTimer) return;\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n void this.flush();\n }, DEBOUNCE_MS);\n }\n\n /**\n * Fan out the current state to every destination. Serialized — if\n * a flush is already running, mark \"needs another\" and let the\n * in-flight one schedule the follow-up when it settles.\n */\n private flush(): Promise<void> {\n if (this.inFlight) {\n this.needsAnotherPush = true;\n return this.inFlight;\n }\n\n const run = async (): Promise<void> => {\n try {\n await this.sendOnce();\n } finally {\n this.inFlight = null;\n if (this.needsAnotherPush) {\n this.needsAnotherPush = false;\n // Re-enter to push the latest snapshot.\n await this.flush();\n }\n }\n };\n\n this.inFlight = run();\n return this.inFlight;\n }\n\n private async sendOnce(): Promise<void> {\n const { session, tasks, eventPlan, handoffText } = this.store;\n const skillId = sanitizeChannelId(session.skillId ?? this.programId);\n const phase = session.runPhase;\n\n const payload: TaskStreamUpdate = {\n session_id: this.sessionId,\n workflow_id: this.programId,\n skill_id: skillId,\n started_at: this.startedAt,\n run_phase: phase,\n tasks: buildTasks(tasks),\n event_plan: eventPlan.length > 0 ? { events: eventPlan } : undefined,\n error: buildError(phase, session.outroData),\n pending_input: buildPendingInput(session.pendingQuestion),\n // Included on every push once captured; the backend keeps it sticky, so\n // pushes that raced the capture cannot un-set it.\n handoff_text: handoffText ?? undefined,\n timestamp: new Date().toISOString(),\n };\n logToFile(\n `[task-stream-push] push phase=${phase} handoff_text=${\n handoffText === null ? 'absent' : `${handoffText.length} chars`\n }`,\n );\n\n let event: StreamEvent;\n if (!this.created) {\n this.created = true;\n event = StreamEvent.Create;\n } else if (phase === RunPhase.Completed) {\n event = StreamEvent.Complete;\n } else if (phase === RunPhase.Error) {\n event = StreamEvent.Error;\n } else {\n event = StreamEvent.Update;\n }\n\n this.lastPushedPhase = phase;\n this.lastPushedQuestionId = payload.pending_input?.id ?? null;\n\n await Promise.all(\n this.destinations.map((d) =>\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n d.send(event, payload).catch(() => {}),\n ),\n );\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { f as getUI } from "./debug-Cm1hvkVy.js";
1
+ import { f as getUI } from "./debug-BsHD83Vy.js";
2
2
  import { Type } from "typebox";
3
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  //#region src/lib/agent/runner/harness/pi/tasks.ts
@@ -109,4 +109,4 @@ function createWizardPiTaskTools() {
109
109
  //#endregion
110
110
  export { createWizardPiTaskTools };
111
111
 
112
- //# sourceMappingURL=tasks-FGzp_zBd.js.map
112
+ //# sourceMappingURL=tasks-BO0W8ocx.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks-FGzp_zBd.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/tasks.ts"],"sourcesContent":["/**\n * Task/todo parity for pi (#526). The same four Task tools the anthropic path\n * exposes (TaskCreate/Update/Get/List), as pi `defineTool` tools backed by a\n * shared in-memory store. Every mutation pushes the list to the TUI via\n * `getUI().syncTodos`, so the todo panel updates live under pi exactly like the\n * anthropic path — the thing that was missing before.\n */\n\nimport { Type } from 'typebox';\nimport { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport { getUI } from '@ui';\n\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed';\nexport interface TaskEntry {\n content: string;\n status: TaskStatus;\n activeForm?: string;\n}\nexport type TaskStore = Map<string, TaskEntry>;\n\nfunction text(s: string): {\n content: [{ type: 'text'; text: string }];\n details: unknown;\n} {\n return { content: [{ type: 'text', text: s }], details: {} };\n}\n\nfunction syncToTui(store: TaskStore): void {\n getUI().syncTodos(\n Array.from(store.values()).map((t) => ({\n content: t.content,\n status: t.status,\n activeForm: t.activeForm,\n })),\n );\n}\n\n/** Build the four Task tools over a fresh store. */\nexport function createWizardPiTaskTools(): {\n tools: ToolDefinition[];\n store: TaskStore;\n} {\n const store: TaskStore = new Map();\n\n const taskCreate = defineTool({\n name: 'TaskCreate',\n label: 'Create task',\n description:\n 'Create a task in the shared todo list. Returns its assigned id.',\n promptSnippet:\n 'TaskCreate(content) — add a todo (surfaces progress in the UI)',\n parameters: Type.Object({\n content: Type.String({ description: 'Imperative task description' }),\n activeForm: Type.Optional(\n Type.String({ description: 'Present-continuous form for the spinner' }),\n ),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const id = `task-${store.size + 1}`;\n store.set(id, {\n content: args.content,\n status: 'pending',\n activeForm: args.activeForm,\n });\n syncToTui(store);\n return text(`Created ${id}`);\n },\n });\n\n const taskUpdate = defineTool({\n name: 'TaskUpdate',\n label: 'Update task',\n description:\n 'Update an existing task by id (status, content, or activeForm).',\n promptSnippet:\n 'TaskUpdate(taskId, status) — mark a todo in_progress/completed',\n parameters: Type.Object({\n taskId: Type.String(),\n status: Type.Optional(\n Type.Union([\n Type.Literal('pending'),\n Type.Literal('in_progress'),\n Type.Literal('completed'),\n ]),\n ),\n content: Type.Optional(Type.String()),\n activeForm: Type.Optional(Type.String()),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const existing = store.get(args.taskId);\n if (!existing) return text(`No such task: ${args.taskId}`);\n store.set(args.taskId, {\n content: args.content ?? existing.content,\n status: (args.status as TaskStatus) ?? existing.status,\n activeForm: args.activeForm ?? existing.activeForm,\n });\n syncToTui(store);\n return text(`Updated ${args.taskId}`);\n },\n });\n\n const taskGet = defineTool({\n name: 'TaskGet',\n label: 'Get task',\n description: 'Fetch a single task by id.',\n parameters: Type.Object({ taskId: Type.String() }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const t = store.get(args.taskId);\n return text(\n t\n ? JSON.stringify({ id: args.taskId, ...t })\n : `No such task: ${args.taskId}`,\n );\n },\n });\n\n const taskList = defineTool({\n name: 'TaskList',\n label: 'List tasks',\n description: 'List all tasks in the shared todo list.',\n parameters: Type.Object({}),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute() {\n return text(\n JSON.stringify(\n Array.from(store.entries()).map(([id, t]) => ({ id, ...t })),\n ),\n );\n },\n });\n\n return { tools: [taskCreate, taskUpdate, taskGet, taskList], store };\n}\n"],"mappings":";;;;;;;;;;;AAqBA,SAAS,KAAK,GAGZ;AACA,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAG,CAAC;EAAE,SAAS,EAAE;EAAE;;AAG9D,SAAS,UAAU,OAAwB;AACzC,QAAO,CAAC,UACN,MAAM,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAK,OAAO;EACrC,SAAS,EAAE;EACX,QAAQ,EAAE;EACV,YAAY,EAAE;EACf,EAAE,CACJ;;;AAIH,SAAgB,0BAGd;CACA,MAAM,wBAAmB,IAAI,KAAK;AA4FlC,QAAO;EAAE,OAAO;GA1FG,WAAW;IAC5B,MAAM;IACN,OAAO;IACP,aACE;IACF,eACE;IACF,YAAY,KAAK,OAAO;KACtB,SAAS,KAAK,OAAO,EAAE,aAAa,+BAA+B,CAAC;KACpE,YAAY,KAAK,SACf,KAAK,OAAO,EAAE,aAAa,2CAA2C,CAAC,CACxE;KACF,CAAC;IAEF,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,KAAK,QAAQ,MAAM,OAAO;AAChC,WAAM,IAAI,IAAI;MACZ,SAAS,KAAK;MACd,QAAQ;MACR,YAAY,KAAK;MAClB,CAAC;AACF,eAAU,MAAM;AAChB,YAAO,KAAK,WAAW,KAAK;;IAE/B,CAAC;GAEiB,WAAW;IAC5B,MAAM;IACN,OAAO;IACP,aACE;IACF,eACE;IACF,YAAY,KAAK,OAAO;KACtB,QAAQ,KAAK,QAAQ;KACrB,QAAQ,KAAK,SACX,KAAK,MAAM;MACT,KAAK,QAAQ,UAAU;MACvB,KAAK,QAAQ,cAAc;MAC3B,KAAK,QAAQ,YAAY;MAC1B,CAAC,CACH;KACD,SAAS,KAAK,SAAS,KAAK,QAAQ,CAAC;KACrC,YAAY,KAAK,SAAS,KAAK,QAAQ,CAAC;KACzC,CAAC;IAEF,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,WAAW,MAAM,IAAI,KAAK,OAAO;AACvC,SAAI,CAAC,SAAU,QAAO,KAAK,iBAAiB,KAAK,SAAS;AAC1D,WAAM,IAAI,KAAK,QAAQ;MACrB,SAAS,KAAK,WAAW,SAAS;MAClC,QAAS,KAAK,UAAyB,SAAS;MAChD,YAAY,KAAK,cAAc,SAAS;MACzC,CAAC;AACF,eAAU,MAAM;AAChB,YAAO,KAAK,WAAW,KAAK,SAAS;;IAExC,CAAC;GAEc,WAAW;IACzB,MAAM;IACN,OAAO;IACP,aAAa;IACb,YAAY,KAAK,OAAO,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;IAElD,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,IAAI,MAAM,IAAI,KAAK,OAAO;AAChC,YAAO,KACL,IACI,KAAK,UAAU;MAAE,IAAI,KAAK;MAAQ,GAAG;MAAG,CAAC,GACzC,iBAAiB,KAAK,SAC3B;;IAEJ,CAAC;GAEe,WAAW;IAC1B,MAAM;IACN,OAAO;IACP,aAAa;IACb,YAAY,KAAK,OAAO,EAAE,CAAC;IAE3B,MAAM,UAAU;AACd,YAAO,KACL,KAAK,UACH,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ;MAAE;MAAI,GAAG;MAAG,EAAE,CAC7D,CACF;;IAEJ,CAAC;GAEyD;EAAE;EAAO"}
1
+ {"version":3,"file":"tasks-BO0W8ocx.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/tasks.ts"],"sourcesContent":["/**\n * Task/todo parity for pi (#526). The same four Task tools the anthropic path\n * exposes (TaskCreate/Update/Get/List), as pi `defineTool` tools backed by a\n * shared in-memory store. Every mutation pushes the list to the TUI via\n * `getUI().syncTodos`, so the todo panel updates live under pi exactly like the\n * anthropic path — the thing that was missing before.\n */\n\nimport { Type } from 'typebox';\nimport { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport { getUI } from '@ui';\n\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed';\nexport interface TaskEntry {\n content: string;\n status: TaskStatus;\n activeForm?: string;\n}\nexport type TaskStore = Map<string, TaskEntry>;\n\nfunction text(s: string): {\n content: [{ type: 'text'; text: string }];\n details: unknown;\n} {\n return { content: [{ type: 'text', text: s }], details: {} };\n}\n\nfunction syncToTui(store: TaskStore): void {\n getUI().syncTodos(\n Array.from(store.values()).map((t) => ({\n content: t.content,\n status: t.status,\n activeForm: t.activeForm,\n })),\n );\n}\n\n/** Build the four Task tools over a fresh store. */\nexport function createWizardPiTaskTools(): {\n tools: ToolDefinition[];\n store: TaskStore;\n} {\n const store: TaskStore = new Map();\n\n const taskCreate = defineTool({\n name: 'TaskCreate',\n label: 'Create task',\n description:\n 'Create a task in the shared todo list. Returns its assigned id.',\n promptSnippet:\n 'TaskCreate(content) — add a todo (surfaces progress in the UI)',\n parameters: Type.Object({\n content: Type.String({ description: 'Imperative task description' }),\n activeForm: Type.Optional(\n Type.String({ description: 'Present-continuous form for the spinner' }),\n ),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const id = `task-${store.size + 1}`;\n store.set(id, {\n content: args.content,\n status: 'pending',\n activeForm: args.activeForm,\n });\n syncToTui(store);\n return text(`Created ${id}`);\n },\n });\n\n const taskUpdate = defineTool({\n name: 'TaskUpdate',\n label: 'Update task',\n description:\n 'Update an existing task by id (status, content, or activeForm).',\n promptSnippet:\n 'TaskUpdate(taskId, status) — mark a todo in_progress/completed',\n parameters: Type.Object({\n taskId: Type.String(),\n status: Type.Optional(\n Type.Union([\n Type.Literal('pending'),\n Type.Literal('in_progress'),\n Type.Literal('completed'),\n ]),\n ),\n content: Type.Optional(Type.String()),\n activeForm: Type.Optional(Type.String()),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const existing = store.get(args.taskId);\n if (!existing) return text(`No such task: ${args.taskId}`);\n store.set(args.taskId, {\n content: args.content ?? existing.content,\n status: (args.status as TaskStatus) ?? existing.status,\n activeForm: args.activeForm ?? existing.activeForm,\n });\n syncToTui(store);\n return text(`Updated ${args.taskId}`);\n },\n });\n\n const taskGet = defineTool({\n name: 'TaskGet',\n label: 'Get task',\n description: 'Fetch a single task by id.',\n parameters: Type.Object({ taskId: Type.String() }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const t = store.get(args.taskId);\n return text(\n t\n ? JSON.stringify({ id: args.taskId, ...t })\n : `No such task: ${args.taskId}`,\n );\n },\n });\n\n const taskList = defineTool({\n name: 'TaskList',\n label: 'List tasks',\n description: 'List all tasks in the shared todo list.',\n parameters: Type.Object({}),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute() {\n return text(\n JSON.stringify(\n Array.from(store.entries()).map(([id, t]) => ({ id, ...t })),\n ),\n );\n },\n });\n\n return { tools: [taskCreate, taskUpdate, taskGet, taskList], store };\n}\n"],"mappings":";;;;;;;;;;;AAqBA,SAAS,KAAK,GAGZ;AACA,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAG,CAAC;EAAE,SAAS,EAAE;EAAE;;AAG9D,SAAS,UAAU,OAAwB;AACzC,QAAO,CAAC,UACN,MAAM,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAK,OAAO;EACrC,SAAS,EAAE;EACX,QAAQ,EAAE;EACV,YAAY,EAAE;EACf,EAAE,CACJ;;;AAIH,SAAgB,0BAGd;CACA,MAAM,wBAAmB,IAAI,KAAK;AA4FlC,QAAO;EAAE,OAAO;GA1FG,WAAW;IAC5B,MAAM;IACN,OAAO;IACP,aACE;IACF,eACE;IACF,YAAY,KAAK,OAAO;KACtB,SAAS,KAAK,OAAO,EAAE,aAAa,+BAA+B,CAAC;KACpE,YAAY,KAAK,SACf,KAAK,OAAO,EAAE,aAAa,2CAA2C,CAAC,CACxE;KACF,CAAC;IAEF,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,KAAK,QAAQ,MAAM,OAAO;AAChC,WAAM,IAAI,IAAI;MACZ,SAAS,KAAK;MACd,QAAQ;MACR,YAAY,KAAK;MAClB,CAAC;AACF,eAAU,MAAM;AAChB,YAAO,KAAK,WAAW,KAAK;;IAE/B,CAAC;GAEiB,WAAW;IAC5B,MAAM;IACN,OAAO;IACP,aACE;IACF,eACE;IACF,YAAY,KAAK,OAAO;KACtB,QAAQ,KAAK,QAAQ;KACrB,QAAQ,KAAK,SACX,KAAK,MAAM;MACT,KAAK,QAAQ,UAAU;MACvB,KAAK,QAAQ,cAAc;MAC3B,KAAK,QAAQ,YAAY;MAC1B,CAAC,CACH;KACD,SAAS,KAAK,SAAS,KAAK,QAAQ,CAAC;KACrC,YAAY,KAAK,SAAS,KAAK,QAAQ,CAAC;KACzC,CAAC;IAEF,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,WAAW,MAAM,IAAI,KAAK,OAAO;AACvC,SAAI,CAAC,SAAU,QAAO,KAAK,iBAAiB,KAAK,SAAS;AAC1D,WAAM,IAAI,KAAK,QAAQ;MACrB,SAAS,KAAK,WAAW,SAAS;MAClC,QAAS,KAAK,UAAyB,SAAS;MAChD,YAAY,KAAK,cAAc,SAAS;MACzC,CAAC;AACF,eAAU,MAAM;AAChB,YAAO,KAAK,WAAW,KAAK,SAAS;;IAExC,CAAC;GAEc,WAAW;IACzB,MAAM;IACN,OAAO;IACP,aAAa;IACb,YAAY,KAAK,OAAO,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;IAElD,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,IAAI,MAAM,IAAI,KAAK,OAAO;AAChC,YAAO,KACL,IACI,KAAK,UAAU;MAAE,IAAI,KAAK;MAAQ,GAAG;MAAG,CAAC,GACzC,iBAAiB,KAAK,SAC3B;;IAEJ,CAAC;GAEe,WAAW;IAC1B,MAAM;IACN,OAAO;IACP,aAAa;IACb,YAAY,KAAK,OAAO,EAAE,CAAC;IAE3B,MAAM,UAAU;AACd,YAAO,KACL,KAAK,UACH,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ;MAAE;MAAI,GAAG;MAAG,EAAE,CAC7D,CACF;;IAEJ,CAAC;GAEyD;EAAE;EAAO"}
@@ -1,5 +1,5 @@
1
- import "./debug-Cm1hvkVy.js";
2
- import { n as analytics } from "./analytics-zBrIVjMV.js";
1
+ import "./debug-BsHD83Vy.js";
2
+ import { n as analytics } from "./analytics-CtIYzkK4.js";
3
3
  import opn from "opn";
4
4
  //#region src/utils/links.ts
5
5
  /**
@@ -65,4 +65,4 @@ function updateProgress(step) {
65
65
  //#endregion
66
66
  export { withUtm as i, openTrackedLink as n, setEntryCommand as r, withProgress as t };
67
67
 
68
- //# sourceMappingURL=telemetry-By56gAtF.js.map
68
+ //# sourceMappingURL=telemetry-Vi9dWddJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-By56gAtF.js","names":[],"sources":["../src/utils/links.ts","../src/telemetry.ts"],"sourcesContent":["/**\n * Outbound links: UTM tagging and tracked opens.\n *\n * Every URL the wizard sends a user to — auto-opened or printed in the TUI —\n * carries `utm_source=wizard`, `utm_medium=cli`, `utm_content=<which link>`.\n * The command dimension rides on every wizard event as the `command` tag.\n * Opening a link also captures a wizard event.\n */\nimport opn from 'opn';\nimport { NODE_ENV } from '@env';\nimport { analytics } from './analytics';\n\n/**\n * Record the CLI command this run was started with (e.g. `integrate`,\n * `slack`, `mcp-add`). Set once at dispatch; tagged onto every wizard\n * event so wizard-side events segment by command.\n */\nexport function setEntryCommand(command: string): void {\n analytics.setTag('command', command);\n}\n\n/**\n * Tag a URL with the wizard's UTM params. `content` names the specific link\n * (e.g. `oauth-signup`, `slack-connect-setup`). URLs that already carry a\n * utm_source — or don't parse — are returned untouched.\n */\nexport function withUtm(url: string, content: string): string {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return url;\n }\n if (parsed.searchParams.has('utm_source')) return url;\n parsed.searchParams.set('utm_source', 'wizard');\n parsed.searchParams.set('utm_medium', 'cli');\n parsed.searchParams.set('utm_content', content);\n return parsed.toString();\n}\n\n/**\n * Open a URL in the user's browser, UTM-tagged (pass `skipUtm` for\n * destinations that aren't PostHog properties or are already final), and\n * capture the interaction. `auto` marks opens the wizard initiated itself,\n * as opposed to a user picking a link on screen. opn throws in headless\n * environments — the URL is always also printed on screen, so the failure\n * is swallowed.\n */\nexport function openTrackedLink(\n url: string,\n content: string,\n opts?: { auto?: boolean; skipUtm?: boolean },\n): void {\n const finalUrl = opts?.skipUtm ? url : withUtm(url, content);\n analytics.wizardCapture('link opened', {\n content,\n url: finalUrl,\n auto: opts?.auto ?? false,\n });\n if (NODE_ENV !== 'test') {\n opn(finalUrl, { wait: false }).catch(() => {\n // No browser available — the printed URL is the fallback.\n });\n }\n}\n","import { analytics } from '@utils/analytics';\n\nexport function withProgress<T>(step: string, callback: () => T): T {\n updateProgress(step);\n return callback();\n}\n\nexport function updateProgress(step: string) {\n analytics.setTag('progress', step);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,gBAAgB,SAAuB;AACrD,WAAU,OAAO,WAAW,QAAQ;;;;;;;AAQtC,SAAgB,QAAQ,KAAa,SAAyB;CAC5D,IAAI;AACJ,KAAI;AACF,WAAS,IAAI,IAAI,IAAI;SACf;AACN,SAAO;;AAET,KAAI,OAAO,aAAa,IAAI,aAAa,CAAE,QAAO;AAClD,QAAO,aAAa,IAAI,cAAc,SAAS;AAC/C,QAAO,aAAa,IAAI,cAAc,MAAM;AAC5C,QAAO,aAAa,IAAI,eAAe,QAAQ;AAC/C,QAAO,OAAO,UAAU;;;;;;;;;;AAW1B,SAAgB,gBACd,KACA,SACA,MACM;CACN,MAAM,WAAW,MAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ;AAC5D,WAAU,cAAc,eAAe;EACrC;EACA,KAAK;EACL,MAAM,MAAM,QAAQ;EACrB,CAAC;AAEA,KAAI,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,YAAY,GAEzC;;;;AC5DN,SAAgB,aAAgB,MAAc,UAAsB;AAClE,gBAAe,KAAK;AACpB,QAAO,UAAU;;AAGnB,SAAgB,eAAe,MAAc;AAC3C,WAAU,OAAO,YAAY,KAAK"}
1
+ {"version":3,"file":"telemetry-Vi9dWddJ.js","names":[],"sources":["../src/utils/links.ts","../src/telemetry.ts"],"sourcesContent":["/**\n * Outbound links: UTM tagging and tracked opens.\n *\n * Every URL the wizard sends a user to — auto-opened or printed in the TUI —\n * carries `utm_source=wizard`, `utm_medium=cli`, `utm_content=<which link>`.\n * The command dimension rides on every wizard event as the `command` tag.\n * Opening a link also captures a wizard event.\n */\nimport opn from 'opn';\nimport { NODE_ENV } from '@env';\nimport { analytics } from './analytics';\n\n/**\n * Record the CLI command this run was started with (e.g. `integrate`,\n * `slack`, `mcp-add`). Set once at dispatch; tagged onto every wizard\n * event so wizard-side events segment by command.\n */\nexport function setEntryCommand(command: string): void {\n analytics.setTag('command', command);\n}\n\n/**\n * Tag a URL with the wizard's UTM params. `content` names the specific link\n * (e.g. `oauth-signup`, `slack-connect-setup`). URLs that already carry a\n * utm_source — or don't parse — are returned untouched.\n */\nexport function withUtm(url: string, content: string): string {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return url;\n }\n if (parsed.searchParams.has('utm_source')) return url;\n parsed.searchParams.set('utm_source', 'wizard');\n parsed.searchParams.set('utm_medium', 'cli');\n parsed.searchParams.set('utm_content', content);\n return parsed.toString();\n}\n\n/**\n * Open a URL in the user's browser, UTM-tagged (pass `skipUtm` for\n * destinations that aren't PostHog properties or are already final), and\n * capture the interaction. `auto` marks opens the wizard initiated itself,\n * as opposed to a user picking a link on screen. opn throws in headless\n * environments — the URL is always also printed on screen, so the failure\n * is swallowed.\n */\nexport function openTrackedLink(\n url: string,\n content: string,\n opts?: { auto?: boolean; skipUtm?: boolean },\n): void {\n const finalUrl = opts?.skipUtm ? url : withUtm(url, content);\n analytics.wizardCapture('link opened', {\n content,\n url: finalUrl,\n auto: opts?.auto ?? false,\n });\n if (NODE_ENV !== 'test') {\n opn(finalUrl, { wait: false }).catch(() => {\n // No browser available — the printed URL is the fallback.\n });\n }\n}\n","import { analytics } from '@utils/analytics';\n\nexport function withProgress<T>(step: string, callback: () => T): T {\n updateProgress(step);\n return callback();\n}\n\nexport function updateProgress(step: string) {\n analytics.setTag('progress', step);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,gBAAgB,SAAuB;AACrD,WAAU,OAAO,WAAW,QAAQ;;;;;;;AAQtC,SAAgB,QAAQ,KAAa,SAAyB;CAC5D,IAAI;AACJ,KAAI;AACF,WAAS,IAAI,IAAI,IAAI;SACf;AACN,SAAO;;AAET,KAAI,OAAO,aAAa,IAAI,aAAa,CAAE,QAAO;AAClD,QAAO,aAAa,IAAI,cAAc,SAAS;AAC/C,QAAO,aAAa,IAAI,cAAc,MAAM;AAC5C,QAAO,aAAa,IAAI,eAAe,QAAQ;AAC/C,QAAO,OAAO,UAAU;;;;;;;;;;AAW1B,SAAgB,gBACd,KACA,SACA,MACM;CACN,MAAM,WAAW,MAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ;AAC5D,WAAU,cAAc,eAAe;EACrC;EACA,KAAK;EACL,MAAM,MAAM,QAAQ;EACrB,CAAC;AAEA,KAAI,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,YAAY,GAEzC;;;;AC5DN,SAAgB,aAAgB,MAAc,UAAsB;AAClE,gBAAe,KAAK;AACpB,QAAO,UAAU;;AAGnB,SAAgB,eAAe,MAAc;AAC3C,WAAU,OAAO,YAAY,KAAK"}
@@ -1,16 +1,16 @@
1
- import { V as POSTHOG_APP_URL, h as SERVICE_LABELS, mt as getSkillsBaseUrl, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-Cm1hvkVy.js";
2
- import { n as analytics } from "./analytics-zBrIVjMV.js";
3
- import { i as withUtm, n as openTrackedLink } from "./telemetry-By56gAtF.js";
4
- import { t as getOrAskForProjectData } from "./setup-utils-Cz0wPLsQ.js";
5
- import { a as fetchUserData, i as fetchSlackConnected } from "./api-Wgb7AhrP.js";
6
- import "./wizard-session-Bn6dO1gE.js";
7
- import { a as formatCostUsd, o as formatTokenCount } from "./agent-runner-Ca-e2TOQ.js";
8
- import { E as fetchSkillMenu } from "./agent-interface-ORzGEFNM.js";
9
- import { y as AUDIT_SEVERITY_STYLE } from "./yara-hooks-BrisBebM.js";
10
- import { c as TextBlock, d as isClearBlock, f as isLinesBlock, h as Icons, l as computeVisibleRange, m as Colors, p as isObjectBlock } from "./posthog-integration-CoPvN6YZ.js";
1
+ import { V as POSTHOG_APP_URL, h as SERVICE_LABELS, mt as getSkillsBaseUrl, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-BsHD83Vy.js";
2
+ import { n as analytics } from "./analytics-CtIYzkK4.js";
3
+ import { i as withUtm, n as openTrackedLink } from "./telemetry-Vi9dWddJ.js";
4
+ import { t as getOrAskForProjectData } from "./setup-utils-D1tv0v_R.js";
5
+ import { a as fetchUserData, i as fetchSlackConnected } from "./api-BJft9v6K.js";
6
+ import "./wizard-session-Cex1BiXs.js";
7
+ import { a as formatCostUsd, o as formatTokenCount } from "./agent-runner-DD65OMkB.js";
8
+ import { k as fetchSkillMenu } from "./agent-interface-tM_b0l__.js";
9
+ import { y as AUDIT_SEVERITY_STYLE } from "./yara-hooks-DZIVzhA6.js";
10
+ import { c as TextBlock, d as isClearBlock, f as isLinesBlock, h as Icons, l as computeVisibleRange, m as Colors, p as isObjectBlock } from "./posthog-integration-DFfBRGOC.js";
11
11
  import { E as getKindMeta, a as useStdoutDimensions, f as Program, i as useKeyboardHintsContext, n as useKeyBindings, o as ConfirmButton, r as KeyboardHintsProvider, s as PromptLabel, t as PickerMenu } from "./bin.js";
12
- import { r as totalTokenCount } from "./store-xMkU8xAa.js";
13
- import { c as namesWithStatus, n as AVAILABLE_FEATURES, o as isAllFeaturesSelected, s as isOk, t as ALL_FEATURE_VALUES, u as summarizeFailure } from "./defaults-BHhXrX9F.js";
12
+ import { r as totalTokenCount } from "./store-DzEgsAPN.js";
13
+ import { c as namesWithStatus, n as AVAILABLE_FEATURES, o as isAllFeaturesSelected, s as isOk, t as ALL_FEATURE_VALUES, u as summarizeFailure } from "./defaults-DI642M3n.js";
14
14
  import opn from "opn";
15
15
  import * as fs$1 from "fs";
16
16
  import axios from "axios";
@@ -2700,8 +2700,25 @@ const IssueRow = ({ issue, docsWidth }) => {
2700
2700
  *
2701
2701
  * When done, calls store.setMcpComplete(). The router resolves to outro.
2702
2702
  */
2703
- const markDone = (store, outcome, clients = [], featuresSelected) => {
2704
- store.setMcpComplete(outcome, clients, featuresSelected);
2703
+ const markDone = (store, outcome, clients = [], featuresSelected, loginCommands = []) => {
2704
+ store.setMcpComplete(outcome, clients, featuresSelected, loginCommands);
2705
+ };
2706
+ /**
2707
+ * The editor-owned login commands still to run after this install: fresh
2708
+ * config entries use the client's own server name, plugin-provided servers
2709
+ * their `plugin:` name. One entry per client.
2710
+ */
2711
+ const pendingLoginCommands = (clients, mcpResult, pluginResult) => {
2712
+ const commands = /* @__PURE__ */ new Map();
2713
+ for (const r of pluginResult) {
2714
+ const command = clients.find((c) => c.name === r.name)?.pluginLoginCommand;
2715
+ if (isOk(r) && command) commands.set(r.name, command);
2716
+ }
2717
+ for (const r of mcpResult) {
2718
+ const command = clients.find((c) => c.name === r.name)?.loginCommand;
2719
+ if (r.status === "changed" && command) commands.set(r.name, command);
2720
+ }
2721
+ return [...commands.values()];
2705
2722
  };
2706
2723
  const reportFeatures = (features) => isAllFeaturesSelected(features) ? "all" : features;
2707
2724
  const errorText = (err) => summarizeFailure(err instanceof Error ? err.message : String(err)) ?? "unknown error";
@@ -2777,7 +2794,6 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2777
2794
  const [selectedClientNames, setSelectedClientNames] = useState([]);
2778
2795
  const [mcpResults, setMcpResults] = useState([]);
2779
2796
  const [pluginResults, setPluginResults] = useState([]);
2780
- const [installMode, setInstallMode] = useState("custom");
2781
2797
  const [detectError, setDetectError] = useState(null);
2782
2798
  const [flowError, setFlowError] = useState(null);
2783
2799
  const finishFlow = useRef(null);
@@ -2799,65 +2815,46 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2799
2815
  }
2800
2816
  })();
2801
2817
  }, [installer]);
2802
- const proceedAfterClientPick = (clientNames, chosenMode) => {
2818
+ const oauthFlow = !store.session.apiKey;
2819
+ const proceedAfterClientPick = (clientNames) => {
2803
2820
  setSelectedClientNames(clientNames);
2804
- if (chosenMode === "all") {
2805
- doInstall(clientNames, [...ALL_FEATURE_VALUES], chosenMode);
2821
+ if (clientNames.some((name) => clients.find((c) => c.name === name)?.finish)) {
2822
+ setPhase("connector");
2806
2823
  return;
2807
2824
  }
2808
- if (store.session.mcpFeatures) {
2809
- doInstall(clientNames, store.session.mcpFeatures, chosenMode);
2825
+ if (oauthFlow) {
2826
+ doInstall(clientNames);
2810
2827
  return;
2811
2828
  }
2812
- if (clientNames.some((name) => clients.find((c) => c.name === name)?.finish)) {
2813
- setPhase("connector");
2829
+ if (store.session.mcpFeatures) {
2830
+ doInstall(clientNames, store.session.mcpFeatures);
2814
2831
  return;
2815
2832
  }
2816
2833
  setPhase("feature-select");
2817
2834
  };
2818
2835
  const handleConfirm = () => {
2819
2836
  if (isRemove) doRemove();
2820
- else if (clients.length === 1) proceedAfterClientPick([clients[0].name], "custom");
2821
- else setPhase("pick");
2822
- };
2823
- const handleTriStateChoice = (choice) => {
2824
- if (choice === "skip") {
2825
- handleSkip();
2826
- return;
2827
- }
2828
- setInstallMode(choice);
2829
- if (clients.length === 1) proceedAfterClientPick([clients[0].name], choice);
2837
+ else if (clients.length === 1) proceedAfterClientPick([clients[0].name]);
2830
2838
  else setPhase("pick");
2831
2839
  };
2832
2840
  const handleSkip = () => {
2833
2841
  markDone(store, "skipped");
2834
2842
  };
2835
- /**
2836
- * `chosenMode` is passed in rather than read from `installMode`: the tri-state
2837
- * picker sets that state and installs in the same tick when a single client
2838
- * is detected, so the closure would still hold the previous value and a
2839
- * one-editor machine would silently get the MCP-only path.
2840
- */
2841
- const doInstall = async (names, features, chosenMode = installMode) => {
2843
+ const doInstall = async (names, features) => {
2842
2844
  setPhase("working");
2843
2845
  let mcpResult = [];
2844
2846
  let pluginResult = [];
2845
2847
  const pluginCapableSet = new Set(clients.filter((c) => c.supportsPlugin).map((c) => c.name));
2848
+ const bundledSet = new Set(clients.filter((c) => c.pluginBundlesMcp).map((c) => c.name));
2846
2849
  const pluginCapableNames = names.filter((n) => pluginCapableSet.has(n));
2847
- const directNames = names.filter((n) => !pluginCapableSet.has(n));
2848
- if (chosenMode === "all") {
2849
- try {
2850
- mcpResult = await installer.install(directNames, features, store.session.apiKey);
2851
- } catch (err) {
2852
- setFlowError(errorText(err));
2853
- }
2854
- try {
2855
- pluginResult = await installer.installPlugins(pluginCapableNames);
2856
- } catch (err) {
2857
- setFlowError(errorText(err));
2858
- }
2859
- } else try {
2860
- mcpResult = await installer.install(names, features, store.session.apiKey);
2850
+ const directNames = names.filter((n) => !bundledSet.has(n));
2851
+ try {
2852
+ mcpResult = await installer.install(oauthFlow ? directNames : names, features, store.session.apiKey);
2853
+ } catch (err) {
2854
+ setFlowError(errorText(err));
2855
+ }
2856
+ if (oauthFlow) try {
2857
+ pluginResult = await installer.installPlugins(pluginCapableNames);
2861
2858
  } catch (err) {
2862
2859
  setFlowError(errorText(err));
2863
2860
  }
@@ -2866,7 +2863,8 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2866
2863
  const ready = [...mcpResult, ...pluginResult].filter(isOk);
2867
2864
  const outcome = ready.length > 0 ? "installed" : "failed";
2868
2865
  const featuresReport = reportFeatures(features ?? [...ALL_FEATURE_VALUES]);
2869
- finishFlow.current = () => markDone(store, outcome, ready.map((r) => r.name), featuresReport);
2866
+ const logins = oauthFlow ? pendingLoginCommands(clients, mcpResult, pluginResult) : [];
2867
+ finishFlow.current = () => markDone(store, outcome, ready.map((r) => r.name), featuresReport, logins);
2870
2868
  setPhase("done");
2871
2869
  };
2872
2870
  const doRemove = async () => {
@@ -2961,26 +2959,7 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2961
2959
  }),
2962
2960
  /* @__PURE__ */ jsx(Box, {
2963
2961
  marginTop: 1,
2964
- children: !isRemove && !store.session.mcpFeatures ? /* @__PURE__ */ jsx(PickerMenu, {
2965
- message: `Install the PostHog MCP server${clients.some((c) => c.supportsPlugin) ? " and plugin" : ""}?`,
2966
- options: [
2967
- {
2968
- label: "Install with all features",
2969
- value: "all",
2970
- hint: "recommended"
2971
- },
2972
- {
2973
- label: "Customize features",
2974
- value: "custom"
2975
- },
2976
- {
2977
- label: "No thanks",
2978
- value: "skip"
2979
- }
2980
- ],
2981
- mode: "single",
2982
- onSelect: (choice) => handleTriStateChoice(choice)
2983
- }) : /* @__PURE__ */ jsx(ConfirmationInput, {
2962
+ children: /* @__PURE__ */ jsx(ConfirmationInput, {
2984
2963
  message: `${isRemove ? "Remove" : "Install"} the PostHog MCP server${clients.some((c) => c.supportsPlugin) ? " and plugin" : ""}?`,
2985
2964
  confirmLabel: isRemove ? "Remove" : "Install",
2986
2965
  cancelLabel: "No thanks",
@@ -2990,16 +2969,16 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2990
2969
  })
2991
2970
  ] }),
2992
2971
  phase === "pick" && /* @__PURE__ */ jsx(PickerMenu, {
2993
- message: installMode === "all" ? "Select editor to install" : "Select editor to install MCP server",
2972
+ message: "Select editor to install",
2994
2973
  options: clients.map((c) => ({
2995
2974
  label: c.name,
2996
2975
  value: c.name,
2997
2976
  exclusive: Boolean(c.finish),
2998
- hint: installMode === "all" ? c.finish ? "connector" : c.supportsPlugin ? "plugin" : "MCP" : void 0
2977
+ hint: c.finish ? "connector" : c.supportsPlugin ? "plugin" : "MCP"
2999
2978
  })),
3000
2979
  mode: "multi",
3001
2980
  onSelect: (selected) => {
3002
- proceedAfterClientPick(Array.isArray(selected) ? selected : [selected], installMode);
2981
+ proceedAfterClientPick(Array.isArray(selected) ? selected : [selected]);
3003
2982
  }
3004
2983
  }),
3005
2984
  phase === "feature-select" && /* @__PURE__ */ jsx(GroupedPickerMenu, {
@@ -4632,7 +4611,7 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
4632
4611
  ...properties
4633
4612
  });
4634
4613
  }, [programId]);
4635
- const [phase, setPhase] = useState("choose");
4614
+ const [phase, setPhase] = useState(store.router.activeProgram === Program.McpTutorial ? "choose" : "goodbye");
4636
4615
  const [profile, setProfile] = useState(null);
4637
4616
  const greeting = useMemo(() => getRoleGreeting(session.roleAtOrganization), [session.roleAtOrganization]);
4638
4617
  const pickerCandidates = useMemo(() => getTutorialPicker(session.roleAtOrganization, session.integration, profile), [
@@ -4986,6 +4965,7 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
4986
4965
  integration: session.integration,
4987
4966
  profile,
4988
4967
  engaged: branchHistory.length > 0,
4968
+ loginCommands: session.mcpLoginCommands,
4989
4969
  onClose: closeWizard
4990
4970
  })
4991
4971
  ]
@@ -5415,7 +5395,7 @@ const FollowUpPhase = ({ lastToolName, lastToolCommand, lastPrompt, chunks, role
5415
5395
  })]
5416
5396
  });
5417
5397
  };
5418
- const GoodbyePhase = ({ installedClients, role, integration, profile, engaged, onClose }) => {
5398
+ const GoodbyePhase = ({ installedClients, role, integration, profile, engaged, loginCommands, onClose }) => {
5419
5399
  const generated = getGeneratedQuests(profile);
5420
5400
  const samples = (generated.length > 0 ? generated : getRolePrompts(role, integration)).slice(0, 3);
5421
5401
  const headline = engaged ? "Nice work. You can keep talking to PostHog anytime." : "You're all set — PostHog MCP is here when you're ready.";
@@ -5444,6 +5424,19 @@ const GoodbyePhase = ({ installedClients, role, integration, profile, engaged, o
5444
5424
  marginBottom: 1,
5445
5425
  children: introLine
5446
5426
  }),
5427
+ loginCommands.length > 0 && /* @__PURE__ */ jsxs(Box, {
5428
+ flexDirection: "column",
5429
+ marginBottom: 1,
5430
+ children: [/* @__PURE__ */ jsxs(Text, {
5431
+ color: "green",
5432
+ bold: true,
5433
+ children: ["✔", " Authenticate to finish (opens your browser):"]
5434
+ }), loginCommands.map((command) => /* @__PURE__ */ jsxs(Text, { children: [" ", /* @__PURE__ */ jsx(Text, {
5435
+ bold: true,
5436
+ color: "green",
5437
+ children: command
5438
+ })] }, command))]
5439
+ }),
5447
5440
  /* @__PURE__ */ jsx(Box, {
5448
5441
  marginBottom: 1,
5449
5442
  flexDirection: "column",
@@ -7226,4 +7219,4 @@ function releaseTerminal() {
7226
7219
  //#endregion
7227
7220
  export { EventPlanViewer as A, CardLayout as B, VisualizerTab as C, HNViewer as D, ContentSequencer as E, ConfirmationInput as F, GroupedPickerMenu as I, ProgressList as L, LinkText as M, extractUrls as N, TabContainer as O, ModalOverlay as P, LoadingBox as R, ServiceHealthList as S, LearnCard as T, TAILORED_ROLES as _, useSkillEntry as a, SEVERITY_LABEL as b, SlackConnectScreen as c, AuditChecksViewer as d, seedDemoEvents as f, probeProjectData as g, assembleProfile as h, SkillSourceInfo as i, LogViewer as j, ScreenContainer as k, AUDIT_AREA_SLIDES as l, McpSuggestedPromptsScreen as m, releaseTerminal as n, OutroScreen as o, seededProfile as p, AiOptInRequiredScreen as r, useDismissOnAnyKey as s, enterDarkTerminal as t, VisualBox as u, McpScreen as v, TipsCard as w, SEVERITY_ORDER as x, IssueTable as y, SplitView as z };
7228
7221
 
7229
- //# sourceMappingURL=terminal-C_rmkaku.js.map
7222
+ //# sourceMappingURL=terminal-B5wZK7aS.js.map