@posthog/wizard 2.77.0 → 2.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +30 -15
  2. package/dist/{add-mcp-server-to-clients-BnRyE4pw.js → add-mcp-server-to-clients-JMv8LP-O.js} +6 -5
  3. package/dist/{add-mcp-server-to-clients-BnRyE4pw.js.map → add-mcp-server-to-clients-JMv8LP-O.js.map} +1 -1
  4. package/dist/agent-bCZ3i3fh.js +3085 -0
  5. package/dist/agent-bCZ3i3fh.js.map +1 -0
  6. package/dist/{agent-interface-Avtr_5M5.js → agent-interface-BAOIHuXS.js} +429 -378
  7. package/dist/agent-interface-BAOIHuXS.js.map +1 -0
  8. package/dist/{agent-prompt-loader-CHixNPgY.js → agent-prompt-loader-7Pd4CfZO.js} +6 -6
  9. package/dist/agent-prompt-loader-7Pd4CfZO.js.map +1 -0
  10. package/dist/analytics-BcV-WCM4.js +2 -0
  11. package/dist/{analytics-C67F2vee.js → analytics-CHCZ_qUR.js} +10 -6
  12. package/dist/analytics-CHCZ_qUR.js.map +1 -0
  13. package/dist/{api-Dj1yabTj.js → api-Diy_2fp8.js} +4 -17
  14. package/dist/api-Diy_2fp8.js.map +1 -0
  15. package/dist/bin.js +410 -68
  16. package/dist/bin.js.map +1 -1
  17. package/dist/{bounded-fs-CWRYuLAn.js → bounded-fs-Bx9zTe6m.js} +4 -4
  18. package/dist/bounded-fs-Bx9zTe6m.js.map +1 -0
  19. package/dist/check-screens.tsx +1 -1
  20. package/dist/{ci-install-CvXL5h13.js → ci-install-CDdQYsPe.js} +6 -5
  21. package/dist/ci-install-CDdQYsPe.js.map +1 -0
  22. package/dist/constants-jFI2SaE1.js +260 -0
  23. package/dist/constants-jFI2SaE1.js.map +1 -0
  24. package/dist/debug-Ch4BDOtD.js +171 -0
  25. package/dist/debug-Ch4BDOtD.js.map +1 -0
  26. package/dist/{debug-CziFS0cA.js → debug-M2gV2iDq.js} +1 -1
  27. package/dist/{defaults-4c4YwTmW.js → defaults-C6J7yL4O.js} +1 -1
  28. package/dist/{defaults-4c4YwTmW.js.map → defaults-C6J7yL4O.js.map} +1 -1
  29. package/dist/endpoints-DCVbM06L.js +108 -0
  30. package/dist/endpoints-DCVbM06L.js.map +1 -0
  31. package/dist/{env-api-key-BZ5wE3Y4.js → env-api-key-Qx8NnYHm.js} +3 -3
  32. package/dist/env-api-key-Qx8NnYHm.js.map +1 -0
  33. package/dist/{environment-BEvsey6i.js → environment-B07Bmxae.js} +6 -6
  34. package/dist/environment-B07Bmxae.js.map +1 -0
  35. package/dist/{errors-BoJvrdw8.js → errors-CQ9kttJD.js} +27 -34
  36. package/dist/errors-CQ9kttJD.js.map +1 -0
  37. package/dist/{file-BoD3REGB.js → file-BcLTjU16.js} +2 -2
  38. package/dist/{file-BoD3REGB.js.map → file-BcLTjU16.js.map} +1 -1
  39. package/dist/{file-watcher-BPFazvG_.js → file-watcher-DWc_bTs6.js} +2 -2
  40. package/dist/{file-watcher-BPFazvG_.js.map → file-watcher-DWc_bTs6.js.map} +1 -1
  41. package/dist/{gateway-session-xPFv6p5c.js → gateway-CjoShdLa.js} +175 -20
  42. package/dist/gateway-CjoShdLa.js.map +1 -0
  43. package/dist/generate-version.cjs +1 -1
  44. package/dist/{headless-ui-CsmQrDIl.js → headless-ui-BFFmYC4v.js} +13 -2
  45. package/dist/headless-ui-BFFmYC4v.js.map +1 -0
  46. package/dist/{interactive-Dej-sxvR.js → interactive-D6zV7brJ.js} +2 -2
  47. package/dist/{interactive-Dej-sxvR.js.map → interactive-D6zV7brJ.js.map} +1 -1
  48. package/dist/{local-dev-DPZiIPCK.js → local-dev-McsurOC-.js} +2 -2
  49. package/dist/local-dev-McsurOC-.js.map +1 -0
  50. package/dist/logging-ui-_TwoMNbG.js +226 -0
  51. package/dist/logging-ui-_TwoMNbG.js.map +1 -0
  52. package/dist/mcp-DAgaoGk5.js +97 -0
  53. package/dist/mcp-DAgaoGk5.js.map +1 -0
  54. package/dist/{mcp-prompt-streaming-BU_kuur_.js → mcp-prompt-streaming-I2U_X7BC.js} +7 -6
  55. package/dist/mcp-prompt-streaming-I2U_X7BC.js.map +1 -0
  56. package/dist/{mint-failure-BrleGqMd.js → mint-failure-D4nd2XmZ.js} +82 -25
  57. package/dist/mint-failure-D4nd2XmZ.js.map +1 -0
  58. package/dist/models-DDdBJKwT.js +83 -0
  59. package/dist/models-DDdBJKwT.js.map +1 -0
  60. package/dist/{non-interactive-DcOjH2fw.js → non-interactive-Dzow7YKY.js} +3 -3
  61. package/dist/non-interactive-Dzow7YKY.js.map +1 -0
  62. package/dist/oauth-session-DJ8h6GFa.js +74 -0
  63. package/dist/oauth-session-DJ8h6GFa.js.map +1 -0
  64. package/dist/{orchestrator-tools-DXwr8Sp3.js → orchestrator-tools-szcdscwn.js} +4 -4
  65. package/dist/orchestrator-tools-szcdscwn.js.map +1 -0
  66. package/dist/{package-json-xt3eeyBI.js → package-json-DY70kkNp.js} +2 -2
  67. package/dist/package-json-DY70kkNp.js.map +1 -0
  68. package/dist/{pi-mY8I2XkG.js → pi-D3h5Ok0i.js} +329 -65
  69. package/dist/pi-D3h5Ok0i.js.map +1 -0
  70. package/dist/{playground-CDUUKLkC.js → playground-5AB4WgNF.js} +11 -7
  71. package/dist/playground-5AB4WgNF.js.map +1 -0
  72. package/dist/{posthog-Hc_-9wrr.js → posthog-DNn574nW.js} +8 -3
  73. package/dist/posthog-DNn574nW.js.map +1 -0
  74. package/dist/{provisioning-xfdjhFtA.js → provisioning-B8uSqRZk.js} +6 -5
  75. package/dist/provisioning-B8uSqRZk.js.map +1 -0
  76. package/dist/{queue-tools-C8y_xZJi.js → queue-tools-9OOVx5TE.js} +7 -86
  77. package/dist/queue-tools-9OOVx5TE.js.map +1 -0
  78. package/dist/{registry-DTmREJ-f.js → registry-8YG-kuXk.js} +265 -6
  79. package/dist/registry-8YG-kuXk.js.map +1 -0
  80. package/dist/run-agent-legacy-d7rWlzQR.js +3739 -0
  81. package/dist/run-agent-legacy-d7rWlzQR.js.map +1 -0
  82. package/dist/{security-Dw2oFJjP.js → security-Cg_4a7vB.js} +6 -6
  83. package/dist/security-Cg_4a7vB.js.map +1 -0
  84. package/dist/{setup-utils-fjJTBhaB.js → setup-utils-CmTCTPkX.js} +46 -18
  85. package/dist/setup-utils-CmTCTPkX.js.map +1 -0
  86. package/dist/smoke-test.sh +1 -1
  87. package/dist/{start-tui-UNe2xQ0U.js → start-tui-CB2PZs1x.js} +38 -44
  88. package/dist/start-tui-CB2PZs1x.js.map +1 -0
  89. package/dist/{steps-C2yqOk_h.js → steps-BZcXRJNY.js} +7 -7
  90. package/dist/{steps-C2yqOk_h.js.map → steps-BZcXRJNY.js.map} +1 -1
  91. package/dist/{store-Bic4Cr3L.js → store-UN7c-ASf.js} +18 -28
  92. package/dist/store-UN7c-ASf.js.map +1 -0
  93. package/dist/{subagent-Dz41vV9j.js → subagent-BofIDK-O.js} +8 -3
  94. package/dist/subagent-BofIDK-O.js.map +1 -0
  95. package/dist/{task-5-fw_Vw3.js → task-r9t1UhQP.js} +124 -36
  96. package/dist/task-r9t1UhQP.js.map +1 -0
  97. package/dist/{task-stream-dVzl6b_q.js → task-stream-CMUilu54.js} +45 -15
  98. package/dist/task-stream-CMUilu54.js.map +1 -0
  99. package/dist/{tasks-D1c2z1aw.js → tasks-Bbv_lOBG.js} +17 -13
  100. package/dist/tasks-Bbv_lOBG.js.map +1 -0
  101. package/dist/{telemetry-BRnxBRWK.js → telemetry-D6ruCtnv.js} +5 -5
  102. package/dist/telemetry-D6ruCtnv.js.map +1 -0
  103. package/dist/{terminal-9bvRa4Qt.js → terminal-DrGyci_a.js} +39 -74
  104. package/dist/terminal-DrGyci_a.js.map +1 -0
  105. package/dist/{tools-Dp6i9k70.js → tools-Dyfh9aCD.js} +11 -11
  106. package/dist/tools-Dyfh9aCD.js.map +1 -0
  107. package/dist/ui-BBVuTM1v.js +15 -0
  108. package/dist/ui-BBVuTM1v.js.map +1 -0
  109. package/dist/version-aXxpgRKx.js +6 -0
  110. package/dist/version-aXxpgRKx.js.map +1 -0
  111. package/dist/warlock-smoke-test.ts +2 -2
  112. package/dist/{wizard-abort-lE6umflt.js → wizard-abort-BBkiUxfS.js} +23 -20
  113. package/dist/wizard-abort-BBkiUxfS.js.map +1 -0
  114. package/dist/wizard-abort-BU4aLyvh.js +3 -0
  115. package/dist/{wizard-session-FSJCkTcP.js → wizard-session-2w4Ru-Wz.js} +12 -16
  116. package/dist/wizard-session-2w4Ru-Wz.js.map +1 -0
  117. package/dist/wizard-session-DGaUHOF3.js +2 -0
  118. package/dist/wizard-ui-DoyOZxK8.js +16 -0
  119. package/dist/wizard-ui-DoyOZxK8.js.map +1 -0
  120. package/dist/{yara-hooks-BTNJ6MGW.js → yara-hooks-DCeMDd1o.js} +19 -68
  121. package/dist/yara-hooks-DCeMDd1o.js.map +1 -0
  122. package/dist/yara-hooks-DSGuCU9A.js +2 -0
  123. package/package.json +1 -1
  124. package/dist/agent-interface-Avtr_5M5.js.map +0 -1
  125. package/dist/agent-prompt-loader-CHixNPgY.js.map +0 -1
  126. package/dist/agent-runner-COOIUJCP.js +0 -6188
  127. package/dist/agent-runner-COOIUJCP.js.map +0 -1
  128. package/dist/analytics-C67F2vee.js.map +0 -1
  129. package/dist/analytics-CJA1uLlk.js +0 -2
  130. package/dist/api-Dj1yabTj.js.map +0 -1
  131. package/dist/bounded-fs-CWRYuLAn.js.map +0 -1
  132. package/dist/ci-install-CvXL5h13.js.map +0 -1
  133. package/dist/debug-B4kzIZPi.js +0 -754
  134. package/dist/debug-B4kzIZPi.js.map +0 -1
  135. package/dist/env-api-key-BZ5wE3Y4.js.map +0 -1
  136. package/dist/environment-BEvsey6i.js.map +0 -1
  137. package/dist/errors-BoJvrdw8.js.map +0 -1
  138. package/dist/gateway-session-C118uACz.js +0 -2
  139. package/dist/gateway-session-xPFv6p5c.js.map +0 -1
  140. package/dist/headless-ui-CsmQrDIl.js.map +0 -1
  141. package/dist/local-dev-DPZiIPCK.js.map +0 -1
  142. package/dist/mcp-Cbuw_xss.js +0 -64
  143. package/dist/mcp-Cbuw_xss.js.map +0 -1
  144. package/dist/mcp-prompt-streaming-BU_kuur_.js.map +0 -1
  145. package/dist/mint-failure-BrleGqMd.js.map +0 -1
  146. package/dist/non-interactive-DcOjH2fw.js.map +0 -1
  147. package/dist/orchestrator-tools-DXwr8Sp3.js.map +0 -1
  148. package/dist/package-json-xt3eeyBI.js.map +0 -1
  149. package/dist/package-manager-DR23m5Jg.js +0 -290
  150. package/dist/package-manager-DR23m5Jg.js.map +0 -1
  151. package/dist/pi-mY8I2XkG.js.map +0 -1
  152. package/dist/playground-CDUUKLkC.js.map +0 -1
  153. package/dist/posthog-Hc_-9wrr.js.map +0 -1
  154. package/dist/provisioning-xfdjhFtA.js.map +0 -1
  155. package/dist/queue-tools-C8y_xZJi.js.map +0 -1
  156. package/dist/registry-DTmREJ-f.js.map +0 -1
  157. package/dist/security-Dw2oFJjP.js.map +0 -1
  158. package/dist/setup-utils-fjJTBhaB.js.map +0 -1
  159. package/dist/start-tui-UNe2xQ0U.js.map +0 -1
  160. package/dist/store-Bic4Cr3L.js.map +0 -1
  161. package/dist/subagent-Dz41vV9j.js.map +0 -1
  162. package/dist/task-5-fw_Vw3.js.map +0 -1
  163. package/dist/task-stream-dVzl6b_q.js.map +0 -1
  164. package/dist/tasks-D1c2z1aw.js.map +0 -1
  165. package/dist/telemetry-BRnxBRWK.js.map +0 -1
  166. package/dist/terminal-9bvRa4Qt.js.map +0 -1
  167. package/dist/tools-Dp6i9k70.js.map +0 -1
  168. package/dist/wizard-abort-CAcInAxg.js +0 -2
  169. package/dist/wizard-abort-lE6umflt.js.map +0 -1
  170. package/dist/wizard-session-CYvyYnti.js +0 -2
  171. package/dist/wizard-session-FSJCkTcP.js.map +0 -1
  172. package/dist/yara-hooks-BTNJ6MGW.js.map +0 -1
  173. package/dist/yara-hooks-CFY4pGcW.js +0 -2
@@ -1,12 +1,14 @@
1
- import { s as REMARK_INSTRUCTION } from "./errors-BoJvrdw8.js";
2
- import { ht as WIZARD_USER_AGENT, p as getUI, pt as WIZARD_REMARK_EVENT_NAME, s as logToFile } from "./debug-B4kzIZPi.js";
3
- import { n as analytics } from "./analytics-C67F2vee.js";
4
- import { _ as buildGatewayProvider, g as GATEWAY_PROVIDER, m as assembleCommandments, p as AgentOutputSignals, v as withGatewayRemint } from "./agent-interface-Avtr_5M5.js";
5
- import { o as gatewayAuth } from "./gateway-session-xPFv6p5c.js";
6
- import { _ as TaskStatus } from "./queue-tools-C8y_xZJi.js";
7
- import { c as createAioCapture, i as lastStatusLine, n as buildScrubbedEnv, o as withMode, r as extractText, s as runErrorType, t as applyOutroMarkers } from "./pi-mY8I2XkG.js";
8
- import { c as queueTools, l as renderToolInventory, r as allowsPostHogMcp } from "./agent-prompt-loader-CHixNPgY.js";
9
- //#region src/lib/agent/runner/harness/pi/task.ts
1
+ import { d as ErrorCodes, o as REMARK_INSTRUCTION, t as classifyRunFailure } from "./errors-CQ9kttJD.js";
2
+ import { s as logToFile } from "./debug-Ch4BDOtD.js";
3
+ import { Q as WIZARD_USER_AGENT, Y as WIZARD_REMARK_EVENT_NAME } from "./constants-jFI2SaE1.js";
4
+ import { n as analytics } from "./analytics-CHCZ_qUR.js";
5
+ import { n as currentAccessToken } from "./oauth-session-DJ8h6GFa.js";
6
+ import { l as gatewayAuth, o as withGatewayRemint, r as buildGatewayProvider, t as GATEWAY_PROVIDER } from "./gateway-CjoShdLa.js";
7
+ import { g as assembleCommandments, p as AgentOutputSignals } from "./agent-interface-BAOIHuXS.js";
8
+ import { c as runErrorType, i as lastStatusLine, l as createAioCapture, n as buildScrubbedEnv, o as withMode, r as extractText, s as bindPiCancellation, t as applyOutroMarkers } from "./pi-D3h5Ok0i.js";
9
+ import { _ as TaskStatus } from "./queue-tools-9OOVx5TE.js";
10
+ import { c as queueTools, l as renderToolInventory, r as allowsPostHogMcp } from "./agent-prompt-loader-7Pd4CfZO.js";
11
+ //#region src/agent/runner/harness/pi/task.ts
10
12
  /**
11
13
  * Orchestrator-mode execution on pi: one fresh pi session per unit of work —
12
14
  * the seed plan, or one drained task. The linear pipeline's concerns (skill
@@ -100,10 +102,15 @@ function isSettled(ctx) {
100
102
  return !!task && (task.status === TaskStatus.Done || task.status === TaskStatus.Failed || task.status === TaskStatus.Skipped);
101
103
  }
102
104
  async function runPiTask(inputs) {
103
- const { session, programConfig, boot, prompt, spinner, model: modelId, effort, allowedTools, disallowedTools, askBridge, orchestrator, spinnerMessage, successMessage, errorMessage, requestRemark, analyticsProperties } = inputs;
105
+ if (inputs.signal?.aborted) return {
106
+ kind: "abort",
107
+ classification: "WIZARD_ABORT",
108
+ message: "Agent run cancelled"
109
+ };
110
+ const { config, input, boot, emit, prompt, spinner, model: modelId, effort, allowedTools, disallowedTools, askBridge, orchestrator, spinnerMessage, successMessage, errorMessage, requestRemark, analyticsProperties } = inputs;
104
111
  if (spinnerMessage) spinner.start(spinnerMessage);
105
112
  const capture = createAioCapture({
106
- enabled: session.captureAio,
113
+ enabled: input.flags.captureAio,
107
114
  projectApiKey: boot.credentials.projectApiKey,
108
115
  apiHost: boot.credentials.host.apiHost,
109
116
  runTags: boot.wizardMetadata
@@ -124,9 +131,12 @@ async function runPiTask(inputs) {
124
131
  model: modelId,
125
132
  ...analyticsProperties
126
133
  });
134
+ let mcpCleanup;
135
+ let aioFailed = true;
136
+ let cancellation;
127
137
  try {
128
138
  const { createAgentSession, DefaultResourceLoader, SessionManager, AuthStorage, ModelRegistry, getAgentDir, createLsToolDefinition, createFindToolDefinition, createGrepToolDefinition, createBashToolDefinition, createReadToolDefinition, createEditToolDefinition, createWriteToolDefinition } = await import("@earendil-works/pi-coding-agent");
129
- const refreshAuth = () => gatewayAuth(boot.credentials.host, boot.credentials.accessToken, boot.programId);
139
+ const refreshAuth = async () => gatewayAuth(boot.credentials.host, await currentAccessToken(boot.credentials), boot.programId);
130
140
  const auth = await refreshAuth();
131
141
  const providerInputs = (current) => ({
132
142
  gatewayUrl: current.gatewayUrl,
@@ -142,32 +152,36 @@ async function runPiTask(inputs) {
142
152
  registry.registerProvider(GATEWAY_PROVIDER, provider);
143
153
  const model = registry.find(GATEWAY_PROVIDER, modelId);
144
154
  if (!model) return {
145
- error: "WIZARD_API_ERROR",
155
+ kind: "failure",
156
+ classification: "WIZARD_API_ERROR",
146
157
  message: "pi: gateway model could not be resolved"
147
158
  };
148
159
  const askState = { pending: false };
149
- const { createSecurityExtension } = await import("./security-Dw2oFJjP.js");
160
+ const { createSecurityExtension } = await import("./security-Cg_4a7vB.js");
150
161
  const security = createSecurityExtension({
151
162
  disallowedTools: fenceDisallowList(disallowedTools),
152
163
  triageProvider: boot.triageProvider,
153
164
  getWizardAskPending: () => askState.pending
154
165
  });
155
- const { prewarmYaraScanner } = await import("./yara-hooks-CFY4pGcW.js");
166
+ const { prewarmYaraScanner } = await import("./yara-hooks-DSGuCU9A.js");
156
167
  prewarmYaraScanner();
157
168
  const extensionFactories = [security.factory];
158
- let mcpCleanup;
159
169
  let posthogMcp = false;
160
170
  if (allowsPostHogMcp(allowedTools)) try {
161
- const { setupPostHogMcp } = await import("./mcp-Cbuw_xss.js");
171
+ const { setupPostHogMcp } = await import("./mcp-DAgaoGk5.js");
162
172
  const mcp = await setupPostHogMcp({
163
173
  mcpUrl: boot.credentials.host.mcpUrl,
164
- accessToken: boot.credentials.accessToken,
174
+ accessToken: await currentAccessToken(boot.credentials),
165
175
  userAgent: WIZARD_USER_AGENT
166
176
  });
167
177
  extensionFactories.push(mcp.extensionFactory);
168
178
  mcpCleanup = mcp.cleanup;
169
179
  posthogMcp = true;
170
180
  } catch (err) {
181
+ try {
182
+ mcpCleanup?.();
183
+ } catch {}
184
+ mcpCleanup = void 0;
171
185
  logToFile(`[pi-task] PostHog MCP setup skipped: ${String(err)}`);
172
186
  analytics.wizardCapture("mcp setup failed", {
173
187
  harness: "pi",
@@ -178,10 +192,10 @@ async function runPiTask(inputs) {
178
192
  const codingTools = allowedPiCodingTools(allowedTools);
179
193
  const orchestratorTools = allowedOrchestratorTools(disallowedTools);
180
194
  const resourceLoader = new DefaultResourceLoader({
181
- cwd: session.installDir,
195
+ cwd: input.installDir,
182
196
  agentDir: getAgentDir(),
183
197
  systemPrompt: assembleCommandments({
184
- program: programConfig.id,
198
+ program: config.programId,
185
199
  sequence: "orchestrator",
186
200
  harness: "pi",
187
201
  caps: {
@@ -197,7 +211,7 @@ async function runPiTask(inputs) {
197
211
  extensionFactories
198
212
  });
199
213
  await resourceLoader.reload();
200
- const dir = session.installDir;
214
+ const dir = input.installDir;
201
215
  const codingToolDefs = Object.entries({
202
216
  read: () => withMode(createReadToolDefinition(dir), "parallel"),
203
217
  edit: () => withMode(createEditToolDefinition(dir), "sequential"),
@@ -210,18 +224,19 @@ async function runPiTask(inputs) {
210
224
  find: () => withMode(createFindToolDefinition(dir), "parallel"),
211
225
  grep: () => withMode(createGrepToolDefinition(dir), "parallel")
212
226
  }).filter(([name]) => codingTools.has(name)).map(([, make]) => make());
213
- const { createWizardPiTools } = await import("./tools-Dp6i9k70.js");
227
+ const { createWizardPiTools } = await import("./tools-Dyfh9aCD.js");
214
228
  const wizardToolNames = allowedPiWizardTools(allowedTools);
215
229
  const wizardTools = createWizardPiTools({
216
230
  workingDirectory: dir,
217
231
  skillsBaseUrl: boot.skillsBaseUrl,
218
232
  triageProvider: boot.triageProvider,
233
+ emit,
219
234
  askBridge,
220
235
  onAskPendingChange: (pending) => {
221
236
  askState.pending = pending;
222
237
  }
223
238
  }).filter((t) => wizardToolNames.has(t.name));
224
- const { createPiOrchestratorTools } = await import("./orchestrator-tools-DXwr8Sp3.js");
239
+ const { createPiOrchestratorTools } = await import("./orchestrator-tools-szcdscwn.js");
225
240
  const queueTools = createPiOrchestratorTools(orchestrator).filter((t) => orchestratorTools.has(t.name));
226
241
  const customTools = [
227
242
  ...codingToolDefs,
@@ -238,8 +253,12 @@ async function runPiTask(inputs) {
238
253
  noTools: "builtin",
239
254
  customTools
240
255
  });
256
+ cancellation = bindPiCancellation(inputs.signal, agentSession, (error) => {
257
+ logToFile(`[pi-task] abort failed: ${String(error)}`);
258
+ });
241
259
  await agentSession.bindExtensions({});
242
260
  const turns = withGatewayRemint({
261
+ signal: inputs.signal,
243
262
  session: agentSession,
244
263
  registry,
245
264
  auth,
@@ -264,10 +283,13 @@ async function runPiTask(inputs) {
264
283
  const assistant = extractText(event.message).trim();
265
284
  if (assistant) {
266
285
  logToFile(`[pi-task] assistant: ${assistant.slice(0, 1e3)}`);
267
- applyOutroMarkers(assistant);
286
+ applyOutroMarkers(assistant, emit);
268
287
  const statusText = lastStatusLine(assistant);
269
288
  if (statusText) {
270
- getUI().pushStatus(statusText);
289
+ emit({
290
+ kind: "status",
291
+ message: statusText
292
+ });
271
293
  spinner.message(statusText);
272
294
  }
273
295
  for (const line of assistant.split("\n")) signals.push(line);
@@ -289,28 +311,66 @@ async function runPiTask(inputs) {
289
311
  }
290
312
  });
291
313
  capture.setInitialPrompt(taskPrompt);
314
+ let terminal = turns.terminalFailure();
292
315
  try {
316
+ if (inputs.signal?.aborted) return {
317
+ kind: "abort",
318
+ classification: "WIZARD_ABORT",
319
+ message: "Agent run cancelled"
320
+ };
293
321
  await turns.prompt(taskPrompt);
322
+ terminal = turns.terminalFailure();
294
323
  let nudges = 0;
295
- while (nudges < MAX_TASK_NUDGES && !security.state.criticalViolation && !isSettled(orchestrator)) {
324
+ while (nudges < MAX_TASK_NUDGES && !security.state.criticalViolation && !inputs.signal?.aborted && !terminal && !isSettled(orchestrator)) {
296
325
  nudges += 1;
297
326
  logToFile(`[pi-task] completion guard: not settled, nudge ${nudges}/${MAX_TASK_NUDGES}`);
298
327
  await turns.prompt(orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE);
328
+ terminal = turns.terminalFailure();
299
329
  }
300
- if (requestRemark && !security.state.criticalViolation) try {
330
+ if (requestRemark && !security.state.criticalViolation && !terminal && !inputs.signal?.aborted) try {
301
331
  await agentSession.prompt(REMARK_INSTRUCTION);
302
332
  } catch (err) {
303
333
  logToFile(`[pi-task] remark request failed: ${String(err)}`);
304
334
  }
305
335
  } finally {
306
- unsubscribe();
307
- mcpCleanup?.();
336
+ try {
337
+ unsubscribe();
338
+ } catch {}
339
+ }
340
+ if (inputs.signal?.aborted) return {
341
+ kind: "abort",
342
+ classification: "WIZARD_ABORT",
343
+ message: "Agent run cancelled"
344
+ };
345
+ if (terminal && !security.state.criticalViolation) {
346
+ if (errorMessage || spinnerMessage) spinner.stop(errorMessage ?? "Task failed");
347
+ captureAborted(terminal.classification);
348
+ if (terminal.status === 401) return {
349
+ kind: "decided_failure",
350
+ failure: {
351
+ code: ErrorCodes.AuthInvalidOrExpired,
352
+ message: "Authentication failed (401)",
353
+ detail: { providerMessage: terminal.message }
354
+ }
355
+ };
356
+ return terminal.classification === "WIZARD_ABORT" ? {
357
+ kind: "abort",
358
+ classification: "WIZARD_ABORT",
359
+ message: terminal.message
360
+ } : {
361
+ kind: "failure",
362
+ classification: terminal.classification,
363
+ message: terminal.message
364
+ };
308
365
  }
309
366
  if (security.state.criticalViolation) {
310
367
  spinner.stop("Security violation detected");
311
368
  logToFile(`[pi-task] terminated: YARA violation (blocked ${security.state.blockedCount} call(s))`);
312
369
  captureAborted("WIZARD_YARA_VIOLATION");
313
- return { error: "WIZARD_YARA_VIOLATION" };
370
+ return {
371
+ kind: "failure",
372
+ classification: "WIZARD_YARA_VIOLATION"
373
+ };
314
374
  }
315
375
  const remark = signals.remark();
316
376
  if (remark) analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });
@@ -328,20 +388,48 @@ async function runPiTask(inputs) {
328
388
  });
329
389
  logToFile(`[pi-task] usage task=${typeof analyticsProperties?.task_type === "string" ? analyticsProperties.task_type : modelId} 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}`);
330
390
  if (successMessage) spinner.stop(successMessage);
331
- return {};
391
+ aioFailed = false;
392
+ return { kind: "success" };
332
393
  } catch (err) {
394
+ if (inputs.signal?.aborted) return {
395
+ kind: "abort",
396
+ classification: "WIZARD_ABORT",
397
+ message: "Agent run cancelled"
398
+ };
333
399
  const message = err instanceof Error ? err.message : String(err);
334
400
  logToFile(`[pi-task] run error: ${message}`);
335
401
  if (errorMessage || spinnerMessage) spinner.stop(errorMessage ?? "Task failed");
336
- const error = runErrorType(message);
337
- captureAborted(error);
402
+ const coded = classifyRunFailure(err);
403
+ if (coded.coded && err instanceof Error) {
404
+ captureAborted("WIZARD_API_ERROR");
405
+ return {
406
+ kind: "decided_failure",
407
+ failure: {
408
+ code: coded.code,
409
+ message: coded.message,
410
+ error: err
411
+ }
412
+ };
413
+ }
414
+ const classification = runErrorType(message);
415
+ captureAborted(classification);
338
416
  return {
339
- error,
340
- message
417
+ kind: "failure",
418
+ classification,
419
+ message,
420
+ error: err instanceof Error ? err : void 0
341
421
  };
422
+ } finally {
423
+ await cancellation?.settle();
424
+ try {
425
+ mcpCleanup?.();
426
+ } catch {}
427
+ try {
428
+ capture.finishPiRun(aioFailed);
429
+ } catch {}
342
430
  }
343
431
  }
344
432
  //#endregion
345
433
  export { runPiTask };
346
434
 
347
- //# sourceMappingURL=task-5-fw_Vw3.js.map
435
+ //# sourceMappingURL=task-r9t1UhQP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-r9t1UhQP.js","names":[],"sources":["../src/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 { 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 '@shared/constants';\nimport {\n allowsPostHogMcp,\n queueTools,\n renderToolInventory,\n} from '@agent/agent-prompt-loader';\nimport { AgentErrorType } from '@agent/agent-interface';\nimport { REMARK_INSTRUCTION } from '@agent/signals';\nimport { AgentOutputSignals } from '@agent/output-signals';\nimport { TaskStatus } from '../../sequence/orchestrator/queue';\nimport type { OrchestratorToolsContext } from '../../sequence/orchestrator/queue-tools';\nimport type { AgentResult, TaskRunInputs } from '../types';\nimport { gatewayAuth, type GatewayAuth } from '@agent/gateway-session';\nimport { currentAccessToken } from '@shared/oauth-session';\nimport {\n buildGatewayProvider,\n GATEWAY_PROVIDER,\n withGatewayRemint,\n} from './gateway';\nimport { runErrorType } from './completion';\nimport { bindPiCancellation } from './cancellation';\nimport { classifyRunFailure, ErrorCodes } from '@shared/errors';\nimport { assembleCommandments } from '../../switchboard/commandments';\nimport {\n applyOutroMarkers,\n buildScrubbedEnv,\n extractText,\n lastStatusLine,\n withMode,\n} from './index';\nimport { createAioCapture } from '@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 = [\n 'wizard_ask',\n 'load_skill_menu',\n 'install_skill',\n // Audit programs only: they declare the ledger tools on `allowedTools`.\n 'audit_seed_checks',\n 'audit_add_checks',\n 'audit_resolve_checks',\n];\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 if (inputs.signal?.aborted) {\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n }\n const {\n config,\n input,\n boot,\n emit,\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: input.flags.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 // How this task run failed: the closed AgentErrorType it is about to return.\n // Without it every terminal path below — a security termination, a rate\n // limit, a gateway error — arrived as the same unlabelled event, so a task\n // agent that died could be counted but not diagnosed. A security stop is not\n // an error, hence \"failure mode\" over \"error\".\n //\n // Not `reason`: the linear sequence emits this same event with a `reason`\n // holding the agent's free-text [ABORT] string, and one property cannot be\n // both a closed enum and unbounded prose without making either unreadable.\n const captureAborted = (failureMode: AgentErrorType) =>\n analytics.wizardCapture('agent aborted', {\n failure_mode: failureMode,\n ...runDurations(),\n model: modelId,\n ...analyticsProperties,\n });\n\n let mcpCleanup: (() => void) | undefined;\n let aioFailed = true;\n let cancellation: ReturnType<typeof bindPiCancellation> | undefined;\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 // Reads the live OAuth token, so a mid-run rotation re-mints on the new one.\n const refreshAuth = async () =>\n gatewayAuth(\n boot.credentials.host,\n await currentAccessToken(boot.credentials),\n boot.programId,\n );\n const auth = await refreshAuth();\n const providerInputs = (current: GatewayAuth) => ({\n gatewayUrl: current.gatewayUrl,\n accessToken: current.token,\n teamId: current.teamId,\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 { provider, caps } = buildGatewayProvider(providerInputs(auth));\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 kind: 'failure',\n classification: 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 LONGER_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('@agent/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 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: await currentAccessToken(boot.credentials),\n userAgent: WIZARD_USER_AGENT,\n });\n extensionFactories.push(mcp.extensionFactory);\n mcpCleanup = mcp.cleanup;\n posthogMcp = true;\n } catch (err) {\n try {\n mcpCleanup?.();\n } catch {\n /* Setup cleanup is best effort. */\n }\n mcpCleanup = undefined;\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: input.installDir,\n agentDir: getAgentDir(),\n systemPrompt: assembleCommandments({\n program: config.programId,\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 = input.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 (it only emits) 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 emit,\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 cancellation = bindPiCancellation(inputs.signal, agentSession, (error) => {\n logToFile(`[pi-task] abort failed: ${String(error)}`);\n });\n await agentSession.bindExtensions({});\n\n // A turn that ends on a 401 from an aged bearer re-mints once and\n // continues with the nudge the task would get anyway.\n const turns = withGatewayRemint({\n signal: inputs.signal,\n session: agentSession,\n registry,\n auth,\n refreshAuth,\n providerInputs,\n continueText: () =>\n orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE,\n onRemint: () => {\n logToFile('[pi-task] gateway token renewed after a 401; continuing');\n analytics.wizardCapture('gateway token reminted', { harness: 'pi' });\n },\n });\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 turns.noteAssistantTurn(event.message);\n const assistant = extractText(event.message).trim();\n if (assistant) {\n logToFile(`[pi-task] assistant: ${assistant.slice(0, 1000)}`);\n applyOutroMarkers(assistant, emit);\n const statusText = lastStatusLine(assistant);\n if (statusText) {\n emit({ kind: 'status', message: 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 let terminal = turns.terminalFailure();\n try {\n if (inputs.signal?.aborted)\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n await turns.prompt(taskPrompt);\n terminal = turns.terminalFailure();\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 !inputs.signal?.aborted &&\n !terminal &&\n !isSettled(orchestrator)\n ) {\n nudges += 1;\n logToFile(\n `[pi-task] completion guard: not settled, nudge ${nudges}/${MAX_TASK_NUDGES}`,\n );\n await turns.prompt(\n orchestrator.currentTaskId ? TASK_NUDGE : SEED_NUDGE,\n );\n terminal = turns.terminalFailure();\n }\n\n if (\n requestRemark &&\n !security.state.criticalViolation &&\n !terminal &&\n !inputs.signal?.aborted\n ) {\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 try {\n unsubscribe();\n } catch {\n /* Keep the terminal result. */\n }\n }\n\n if (inputs.signal?.aborted) {\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n }\n\n if (terminal && !security.state.criticalViolation) {\n if (errorMessage || spinnerMessage)\n spinner.stop(errorMessage ?? 'Task failed');\n captureAborted(terminal.classification);\n if (terminal.status === 401) {\n return {\n kind: 'decided_failure',\n failure: {\n code: ErrorCodes.AuthInvalidOrExpired,\n message: 'Authentication failed (401)',\n detail: { providerMessage: terminal.message },\n },\n };\n }\n return terminal.classification === AgentErrorType.ABORT\n ? {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: terminal.message,\n }\n : {\n kind: 'failure',\n classification: terminal.classification,\n message: terminal.message,\n };\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(AgentErrorType.YARA_VIOLATION);\n return { kind: 'failure', classification: 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 aioFailed = false;\n return { kind: 'success' };\n } catch (err) {\n if (inputs.signal?.aborted) {\n return {\n kind: 'abort',\n classification: AgentErrorType.ABORT,\n message: 'Agent run cancelled',\n };\n }\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 const coded = classifyRunFailure(err);\n if (coded.coded && err instanceof Error) {\n captureAborted(AgentErrorType.API_ERROR);\n return {\n kind: 'decided_failure',\n failure: { code: coded.code, message: coded.message, error: err },\n };\n }\n const classification = runErrorType(message);\n captureAborted(classification);\n return {\n kind: 'failure',\n classification,\n message,\n error: err instanceof Error ? err : undefined,\n };\n } finally {\n await cancellation?.settle();\n try {\n mcpCleanup?.();\n } catch {\n /* Keep the terminal result. */\n }\n try {\n capture.finishPiRun(aioFailed);\n } catch {\n /* Telemetry is best effort. */\n }\n }\n}\n"],"mappings":"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"}
@@ -1,9 +1,11 @@
1
- import { n as sanitizeErrorDetail } from "./errors-BoJvrdw8.js";
2
- import { s as logToFile } from "./debug-B4kzIZPi.js";
3
- import "./wizard-session-FSJCkTcP.js";
4
- import { t as startFileWatcher } from "./file-watcher-BPFazvG_.js";
5
- import { createFileDestination } from "./file-BoD3REGB.js";
6
- import { PostHogDestination } from "./posthog-Hc_-9wrr.js";
1
+ import { n as sanitizeErrorDetail } from "./errors-CQ9kttJD.js";
2
+ import { s as logToFile } from "./debug-Ch4BDOtD.js";
3
+ import "./constants-jFI2SaE1.js";
4
+ import "./wizard-session-2w4Ru-Wz.js";
5
+ import "./wizard-ui-DoyOZxK8.js";
6
+ import { t as startFileWatcher } from "./file-watcher-DWc_bTs6.js";
7
+ import { t as PostHogDestination } from "./posthog-DNn574nW.js";
8
+ import { createFileDestination } from "./file-BcLTjU16.js";
7
9
  //#region src/lib/task-stream/event-plan-watcher.ts
8
10
  const MAX_EVENT_PLAN_FILE_BYTES = 256 * 1024;
9
11
  const MAX_EVENT_COUNT = 50;
@@ -107,6 +109,7 @@ const STATUS_MAP = {
107
109
  ["pending"]: "pending",
108
110
  ["in_progress"]: "in_progress",
109
111
  ["completed"]: "completed",
112
+ ["failed"]: "failed",
110
113
  ["skipped"]: "completed"
111
114
  };
112
115
  function buildTasks(items) {
@@ -171,6 +174,10 @@ var TaskStreamPush = class {
171
174
  sessionId;
172
175
  eventPlanWatcher;
173
176
  auditChecks;
177
+ runSync;
178
+ getFlags;
179
+ remoteSync;
180
+ shutdownPromise;
174
181
  enabled;
175
182
  created = false;
176
183
  lastPushedPhase = null;
@@ -182,6 +189,9 @@ var TaskStreamPush = class {
182
189
  shuttingDown = false;
183
190
  constructor(opts) {
184
191
  this.store = opts.store;
192
+ this.runSync = opts.runSync;
193
+ this.getFlags = opts.getFlags;
194
+ this.remoteSync = !opts.runSync ? "wizard-session" : opts.getFlags ? void 0 : "wizard-run";
185
195
  this.programId = sanitizeChannelId(opts.programId);
186
196
  this.destinations = opts.destinations;
187
197
  this.enabled = opts.enabled ?? true;
@@ -216,12 +226,14 @@ var TaskStreamPush = class {
216
226
  this.debounceTimer = null;
217
227
  }
218
228
  }
219
- /**
220
- * Cancel pending debounce, flush one final push if the current
221
- * phase is terminal, and resolve. Never throws. Bounded by
222
- * `timeoutMs` — if a destination hangs, this returns anyway.
223
- */
224
- async shutdown(timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS) {
229
+ async finishRun(outcome, timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS) {
230
+ await this.runSync?.shutdown(outcome, timeoutMs);
231
+ }
232
+ shutdown(timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS, outcome = this.store.session.runPhase === "completed" ? "completed" : "failed") {
233
+ this.shutdownPromise ??= Promise.all([this.runSync?.shutdown(outcome, timeoutMs), this.shutdownLegacy(timeoutMs)]).then(() => void 0);
234
+ return this.shutdownPromise;
235
+ }
236
+ async shutdownLegacy(timeoutMs) {
225
237
  this.shuttingDown = true;
226
238
  this.eventPlanWatcher?.refresh();
227
239
  if (this.debounceTimer) {
@@ -234,7 +246,14 @@ var TaskStreamPush = class {
234
246
  if (!(phase === "completed" || phase === "error")) return;
235
247
  const flush = this.flush();
236
248
  if (timeoutMs <= 0) return;
237
- await Promise.race([flush, new Promise((resolve) => setTimeout(resolve, timeoutMs))]);
249
+ let timer;
250
+ try {
251
+ await Promise.race([flush, new Promise((resolve) => {
252
+ timer = setTimeout(resolve, timeoutMs);
253
+ })]);
254
+ } finally {
255
+ clearTimeout(timer);
256
+ }
238
257
  }
239
258
  /**
240
259
  * Imperative push — fires immediately regardless of phase. Kept as
@@ -247,6 +266,8 @@ var TaskStreamPush = class {
247
266
  if (!this.enabled || this.shuttingDown) return;
248
267
  const phase = this.store.session.runPhase;
249
268
  if (phase === "idle") return;
269
+ this.selectRemoteSync();
270
+ if (this.remoteSync === "wizard-run") this.runSync?.capture(this.store.tasks);
250
271
  if (this.inFlight) {
251
272
  if (this.debounceTimer) {
252
273
  clearTimeout(this.debounceTimer);
@@ -296,6 +317,8 @@ var TaskStreamPush = class {
296
317
  return this.inFlight;
297
318
  }
298
319
  async sendOnce() {
320
+ this.selectRemoteSync();
321
+ if (!this.remoteSync) return;
299
322
  const { session, tasks, eventPlan, handoffText } = this.store;
300
323
  const skillId = sanitizeChannelId(session.skillId ?? this.programId);
301
324
  const phase = session.runPhase;
@@ -324,10 +347,17 @@ var TaskStreamPush = class {
324
347
  else event = "UPDATE";
325
348
  this.lastPushedPhase = phase;
326
349
  this.lastPushedQuestionId = payload.pending_input?.id ?? null;
327
- await Promise.all(this.destinations.map((d) => d.send(event, payload).catch(() => {})));
350
+ await Promise.all(this.destinations.filter((d) => d.name !== "posthog" || this.remoteSync === "wizard-session").map((d) => d.send(event, payload).catch(() => {})));
351
+ }
352
+ selectRemoteSync() {
353
+ if (this.remoteSync || !this.store.session.credentials) return;
354
+ if (this.store.session.runPhase === "idle") return;
355
+ const flags = this.getFlags?.();
356
+ if (!flags) return;
357
+ this.remoteSync = flags["wizard-run-sync"] === "wizard-run" ? "wizard-run" : "wizard-session";
328
358
  }
329
359
  };
330
360
  //#endregion
331
361
  export { PostHogDestination, TaskStreamPush, createFileDestination };
332
362
 
333
- //# sourceMappingURL=task-stream-dVzl6b_q.js.map
363
+ //# sourceMappingURL=task-stream-CMUilu54.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-stream-CMUilu54.js","names":[],"sources":["../src/lib/task-stream/event-plan-watcher.ts","../src/lib/task-stream/audit-areas.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 * Audit ledger → stream tasks: one row per audit *area*, never per check.\n *\n * A run resolves tens of checks, and each row carries a `file` path and a\n * `details` string derived from the user's code. Neither belongs on the wire.\n * A finding is progress, not a failure, so an area never reports `failed`.\n */\n\nimport type { AuditCheck, AuditStatus } from '@lib/programs/audit/types';\nimport { StreamTaskStatus, type StreamTask } from './types';\n\nexport const MAX_AUDIT_AREAS = 24;\nconst MAX_AREA_TITLE_LENGTH = 80;\n\nconst RESOLVED: readonly AuditStatus[] = [\n 'pass',\n 'error',\n 'warning',\n 'suggestion',\n];\n\nfunction areaTitle(value: unknown): string | null {\n if (typeof value !== 'string' || !value.trim()) return null;\n return value.trim().slice(0, MAX_AREA_TITLE_LENGTH);\n}\n\n/** `idOffset` continues the program's task ids, which are stringified integers. */\nexport function rollUpAuditAreas(checks: unknown, idOffset = 0): StreamTask[] {\n if (!Array.isArray(checks)) return [];\n\n // Map keeps insertion order, which is the order the ledger seeds its areas in.\n const areas = new Map<string, { total: number; resolved: number }>();\n\n for (const entry of checks) {\n if (!entry || typeof entry !== 'object') continue;\n const title = areaTitle((entry as Partial<AuditCheck>).area);\n if (!title) continue;\n\n let counts = areas.get(title);\n if (!counts) {\n if (areas.size >= MAX_AUDIT_AREAS) continue;\n counts = { total: 0, resolved: 0 };\n areas.set(title, counts);\n }\n counts.total += 1;\n // The ledger is agent-written: anything unrecognized counts as unresolved.\n if (RESOLVED.includes((entry as Partial<AuditCheck>).status as AuditStatus))\n counts.resolved += 1;\n }\n\n return [...areas.entries()].map(([title, { total, resolved }], index) => ({\n id: String(idOffset + index),\n title,\n status:\n resolved === 0\n ? StreamTaskStatus.Pending\n : resolved < total\n ? StreamTaskStatus.InProgress\n : StreamTaskStatus.Completed,\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 { rollUpAuditAreas } from './audit-areas';\nimport type { WizardRunSync, RunOutcome } from './wizard-run-sync';\nimport { logToFile } from '@utils/debug';\nimport { WIZARD_RUN_SYNC_FLAG_KEY } from '@shared/constants';\nimport { sanitizeErrorDetail } from '@shared/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 [TaskStatus.Failed]: StreamTaskStatus.Failed,\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 runSync?: WizardRunSync;\n getFlags?: () => Readonly<Record<string, string>> | null;\n programId: string;\n destinations: TaskStreamDestination[];\n /** Optional absolute event-plan path to load into the store once. */\n eventPlanPath?: string;\n /** The run's audit ledger, when it has one. The runner owns the watcher. */\n auditChecks?: () => unknown;\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 private readonly auditChecks: (() => unknown) | null;\n\n private readonly runSync?: WizardRunSync;\n private readonly getFlags?: TaskStreamPushOptions['getFlags'];\n private remoteSync?: 'wizard-session' | 'wizard-run';\n private shutdownPromise?: Promise<void>;\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.runSync = opts.runSync;\n this.getFlags = opts.getFlags;\n this.remoteSync = !opts.runSync\n ? 'wizard-session'\n : opts.getFlags\n ? undefined\n : 'wizard-run';\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.auditChecks = opts.auditChecks ?? 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 // Finalize execution while the legacy session continues through the outro.\n async finishRun(\n outcome: RunOutcome,\n timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n ): Promise<void> {\n await this.runSync?.shutdown(outcome, timeoutMs);\n }\n\n shutdown(\n timeoutMs: number = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n outcome: RunOutcome = this.store.session.runPhase === RunPhase.Completed\n ? 'completed'\n : 'failed',\n ): Promise<void> {\n this.shutdownPromise ??= Promise.all([\n this.runSync?.shutdown(outcome, timeoutMs),\n this.shutdownLegacy(timeoutMs),\n ]).then(() => undefined);\n return this.shutdownPromise;\n }\n\n private async shutdownLegacy(timeoutMs: number): 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 let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n flush,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n ]);\n } finally {\n clearTimeout(timer);\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 this.selectRemoteSync();\n if (this.remoteSync === 'wizard-run') {\n this.runSync?.capture(this.store.tasks);\n }\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 this.selectRemoteSync();\n if (!this.remoteSync) return;\n const { session, tasks, eventPlan, handoffText } = this.store;\n const skillId = sanitizeChannelId(session.skillId ?? this.programId);\n const phase = session.runPhase;\n\n // Program rows carry the phase; the area rows carry the audit's progress.\n const programTasks = buildTasks(tasks);\n const auditAreas = this.auditChecks\n ? rollUpAuditAreas(this.auditChecks(), programTasks.length)\n : [];\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: [...programTasks, ...auditAreas],\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\n .filter(\n (d) => d.name !== 'posthog' || this.remoteSync === 'wizard-session',\n )\n .map((d) =>\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n d.send(event, payload).catch(() => {}),\n ),\n );\n }\n\n private selectRemoteSync(): void {\n if (this.remoteSync || !this.store.session.credentials) return;\n if (this.store.session.runPhase === RunPhase.Idle) return;\n const flags = this.getFlags?.();\n if (!flags) return;\n this.remoteSync =\n flags[WIZARD_RUN_SYNC_FLAG_KEY] === 'wizard-run'\n ? 'wizard-run'\n : 'wizard-session';\n }\n}\n"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
- import { p as getUI } from "./debug-B4kzIZPi.js";
1
+ import { randomUUID } from "node:crypto";
2
2
  import { Type } from "typebox";
3
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
4
- //#region src/lib/agent/runner/harness/pi/tasks.ts
4
+ //#region src/agent/runner/harness/pi/tasks.ts
5
5
  /**
6
6
  * Task/todo parity for pi (#526). The same four Task tools the anthropic path
7
7
  * exposes (TaskCreate/Update/Get/List), as pi `defineTool` tools backed by a
8
- * shared in-memory store. Every mutation pushes the list to the TUI via
9
- * `getUI().syncTodos`, so the todo panel updates live under pi exactly like the
10
- * anthropic path — the thing that was missing before.
8
+ * shared in-memory store. Every mutation reports the list through `onSync`
9
+ * (a `tasks` progress event), so the todo panel updates live under pi exactly
10
+ * like the anthropic path — the thing that was missing before.
11
11
  */
12
12
  function text(s) {
13
13
  return {
@@ -18,16 +18,20 @@ function text(s) {
18
18
  details: {}
19
19
  };
20
20
  }
21
- function syncToTui(store) {
22
- getUI().syncTodos(Array.from(store.values()).map((t) => ({
21
+ function snapshot(store, source) {
22
+ return Array.from(store.entries()).map(([id, t]) => ({
23
+ id: `${source}:${id}`,
24
+ source,
23
25
  content: t.content,
24
26
  status: t.status,
25
27
  activeForm: t.activeForm
26
- })));
28
+ }));
27
29
  }
28
- /** Build the four Task tools over a fresh store. */
29
- function createWizardPiTaskTools() {
30
+ /** Build the four Task tools over a fresh store. `onSync` gets the list after every mutation. */
31
+ function createWizardPiTaskTools(onSync = () => void 0) {
30
32
  const store = /* @__PURE__ */ new Map();
33
+ const source = randomUUID();
34
+ const syncToTui = () => onSync(snapshot(store, source));
31
35
  return {
32
36
  tools: [
33
37
  defineTool({
@@ -46,7 +50,7 @@ function createWizardPiTaskTools() {
46
50
  status: "pending",
47
51
  activeForm: args.activeForm
48
52
  });
49
- syncToTui(store);
53
+ syncToTui();
50
54
  return text(`Created ${id}`);
51
55
  }
52
56
  }),
@@ -73,7 +77,7 @@ function createWizardPiTaskTools() {
73
77
  status: args.status ?? existing.status,
74
78
  activeForm: args.activeForm ?? existing.activeForm
75
79
  });
76
- syncToTui(store);
80
+ syncToTui();
77
81
  return text(`Updated ${args.taskId}`);
78
82
  }
79
83
  }),
@@ -109,4 +113,4 @@ function createWizardPiTaskTools() {
109
113
  //#endregion
110
114
  export { createWizardPiTaskTools };
111
115
 
112
- //# sourceMappingURL=tasks-D1c2z1aw.js.map
116
+ //# sourceMappingURL=tasks-Bbv_lOBG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-Bbv_lOBG.js","names":[],"sources":["../src/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 reports the list through `onSync`\n * (a `tasks` progress event), so the todo panel updates live under pi exactly\n * like the anthropic path — the thing that was missing before.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { Type } from 'typebox';\nimport { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport type { TaskSnapshot } from '@agent/progress';\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 snapshot(store: TaskStore, source: string): TaskSnapshot[] {\n return Array.from(store.entries()).map(([id, t]) => ({\n id: `${source}:${id}`,\n source,\n content: t.content,\n status: t.status,\n activeForm: t.activeForm,\n }));\n}\n\n/** Build the four Task tools over a fresh store. `onSync` gets the list after every mutation. */\nexport function createWizardPiTaskTools(\n onSync: (tasks: TaskSnapshot[]) => void = () => undefined,\n): {\n tools: ToolDefinition[];\n store: TaskStore;\n} {\n const store: TaskStore = new Map();\n const source = randomUUID();\n const syncToTui = (): void => onSync(snapshot(store, source));\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();\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();\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":";;;;;;;;;;;AAsBA,SAAS,KAAK,GAGZ;AACA,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAG,CAAC;EAAE,SAAS,EAAE;EAAE;;AAG9D,SAAS,SAAS,OAAkB,QAAgC;AAClE,QAAO,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ;EACnD,IAAI,GAAG,OAAO,GAAG;EACjB;EACA,SAAS,EAAE;EACX,QAAQ,EAAE;EACV,YAAY,EAAE;EACf,EAAE;;;AAIL,SAAgB,wBACd,eAAgD,KAAA,GAIhD;CACA,MAAM,wBAAmB,IAAI,KAAK;CAClC,MAAM,SAAS,YAAY;CAC3B,MAAM,kBAAwB,OAAO,SAAS,OAAO,OAAO,CAAC;AA4F7D,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,gBAAW;AACX,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,gBAAW;AACX,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"}