@narumitw/pi-subagents 2.1.3 → 3.0.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 (314) hide show
  1. package/README.md +181 -1250
  2. package/dist/child-communication-bridge.ts +136 -0
  3. package/dist/child-communication-bridge.ts.map +7 -0
  4. package/dist/chunks/chunk-UKTWUQRM.js +667 -0
  5. package/dist/chunks/chunk-UKTWUQRM.js.map +7 -0
  6. package/dist/index.ts +1448 -1598
  7. package/dist/index.ts.map +4 -4
  8. package/docs/design-principles.md +31 -0
  9. package/docs/ipc-transport-idea.md +103 -0
  10. package/docs/tools.md +109 -0
  11. package/package.json +11 -19
  12. package/src/broker-credentials.ts +71 -0
  13. package/src/child-communication-bridge.ts +153 -0
  14. package/src/child-communication-tools.ts +147 -0
  15. package/src/completion-renderer.ts +60 -0
  16. package/src/index.ts +1 -1
  17. package/src/message-broker.ts +628 -0
  18. package/src/model-output.ts +42 -0
  19. package/src/process.ts +688 -0
  20. package/src/runtime.ts +538 -0
  21. package/src/subagents.ts +33 -34
  22. package/src/tools.ts +416 -0
  23. package/src/types.ts +85 -0
  24. package/src/widget.ts +130 -0
  25. package/dist/chunks/auto-transport-FUUKFDIG.ts +0 -109
  26. package/dist/chunks/auto-transport-FUUKFDIG.ts.map +0 -7
  27. package/dist/chunks/capability-grant-PR72SWWS.ts +0 -19
  28. package/dist/chunks/capability-grant-PR72SWWS.ts.map +0 -7
  29. package/dist/chunks/child-peer-bridge.ts +0 -108
  30. package/dist/chunks/child-peer-bridge.ts.map +0 -7
  31. package/dist/chunks/chunk-2LMJU25E.ts +0 -1641
  32. package/dist/chunks/chunk-2LMJU25E.ts.map +0 -7
  33. package/dist/chunks/chunk-47SK2QYC.ts +0 -56
  34. package/dist/chunks/chunk-47SK2QYC.ts.map +0 -7
  35. package/dist/chunks/chunk-4AQSF7AS.ts +0 -29
  36. package/dist/chunks/chunk-4AQSF7AS.ts.map +0 -7
  37. package/dist/chunks/chunk-5DGRMKNV.ts +0 -126
  38. package/dist/chunks/chunk-5DGRMKNV.ts.map +0 -7
  39. package/dist/chunks/chunk-6H6TBBED.ts +0 -108
  40. package/dist/chunks/chunk-6H6TBBED.ts.map +0 -7
  41. package/dist/chunks/chunk-6KJ34M6S.ts +0 -159
  42. package/dist/chunks/chunk-6KJ34M6S.ts.map +0 -7
  43. package/dist/chunks/chunk-6NSJVPXX.ts +0 -480
  44. package/dist/chunks/chunk-6NSJVPXX.ts.map +0 -7
  45. package/dist/chunks/chunk-7AAJEUSL.ts +0 -238
  46. package/dist/chunks/chunk-7AAJEUSL.ts.map +0 -7
  47. package/dist/chunks/chunk-BFK2Z4ZF.ts +0 -189
  48. package/dist/chunks/chunk-BFK2Z4ZF.ts.map +0 -7
  49. package/dist/chunks/chunk-BOAXY55Y.ts +0 -153
  50. package/dist/chunks/chunk-BOAXY55Y.ts.map +0 -7
  51. package/dist/chunks/chunk-D4CR7T73.ts +0 -353
  52. package/dist/chunks/chunk-D4CR7T73.ts.map +0 -7
  53. package/dist/chunks/chunk-FEVPWRMU.ts +0 -300
  54. package/dist/chunks/chunk-FEVPWRMU.ts.map +0 -7
  55. package/dist/chunks/chunk-G6VZTCFH.ts +0 -70
  56. package/dist/chunks/chunk-G6VZTCFH.ts.map +0 -7
  57. package/dist/chunks/chunk-H3FP6DLR.ts +0 -160
  58. package/dist/chunks/chunk-H3FP6DLR.ts.map +0 -7
  59. package/dist/chunks/chunk-HA36LPCO.ts +0 -130
  60. package/dist/chunks/chunk-HA36LPCO.ts.map +0 -7
  61. package/dist/chunks/chunk-HOP5FXDT.ts +0 -27
  62. package/dist/chunks/chunk-HOP5FXDT.ts.map +0 -7
  63. package/dist/chunks/chunk-IGAMVWFN.ts +0 -139
  64. package/dist/chunks/chunk-IGAMVWFN.ts.map +0 -7
  65. package/dist/chunks/chunk-ILEQ27AL.ts +0 -45
  66. package/dist/chunks/chunk-ILEQ27AL.ts.map +0 -7
  67. package/dist/chunks/chunk-ITVWPNU4.ts +0 -819
  68. package/dist/chunks/chunk-ITVWPNU4.ts.map +0 -7
  69. package/dist/chunks/chunk-IWC32VPY.ts +0 -172
  70. package/dist/chunks/chunk-IWC32VPY.ts.map +0 -7
  71. package/dist/chunks/chunk-JSZIP73U.ts +0 -362
  72. package/dist/chunks/chunk-JSZIP73U.ts.map +0 -7
  73. package/dist/chunks/chunk-JU6LUNLP.ts +0 -380
  74. package/dist/chunks/chunk-JU6LUNLP.ts.map +0 -7
  75. package/dist/chunks/chunk-KMGKCEO4.ts +0 -38
  76. package/dist/chunks/chunk-KMGKCEO4.ts.map +0 -7
  77. package/dist/chunks/chunk-LASD73CM.ts +0 -339
  78. package/dist/chunks/chunk-LASD73CM.ts.map +0 -7
  79. package/dist/chunks/chunk-LEOYDZI3.ts +0 -22
  80. package/dist/chunks/chunk-LEOYDZI3.ts.map +0 -7
  81. package/dist/chunks/chunk-LL4LP2T7.ts +0 -737
  82. package/dist/chunks/chunk-LL4LP2T7.ts.map +0 -7
  83. package/dist/chunks/chunk-N2T5IN4X.ts +0 -18
  84. package/dist/chunks/chunk-N2T5IN4X.ts.map +0 -7
  85. package/dist/chunks/chunk-N7BLVXKK.ts +0 -73
  86. package/dist/chunks/chunk-N7BLVXKK.ts.map +0 -7
  87. package/dist/chunks/chunk-NLT67IZS.ts +0 -322
  88. package/dist/chunks/chunk-NLT67IZS.ts.map +0 -7
  89. package/dist/chunks/chunk-NTRPLF46.ts +0 -63
  90. package/dist/chunks/chunk-NTRPLF46.ts.map +0 -7
  91. package/dist/chunks/chunk-ONDTY4EL.ts +0 -91
  92. package/dist/chunks/chunk-ONDTY4EL.ts.map +0 -7
  93. package/dist/chunks/chunk-OVPHFGKO.ts +0 -1769
  94. package/dist/chunks/chunk-OVPHFGKO.ts.map +0 -7
  95. package/dist/chunks/chunk-P7OH4XMF.ts +0 -60
  96. package/dist/chunks/chunk-P7OH4XMF.ts.map +0 -7
  97. package/dist/chunks/chunk-PBZMBTNJ.ts +0 -422
  98. package/dist/chunks/chunk-PBZMBTNJ.ts.map +0 -7
  99. package/dist/chunks/chunk-PGLSFLYW.ts +0 -52
  100. package/dist/chunks/chunk-PGLSFLYW.ts.map +0 -7
  101. package/dist/chunks/chunk-RRR66UWR.ts +0 -28
  102. package/dist/chunks/chunk-RRR66UWR.ts.map +0 -7
  103. package/dist/chunks/chunk-RSUXZD6S.ts +0 -275
  104. package/dist/chunks/chunk-RSUXZD6S.ts.map +0 -7
  105. package/dist/chunks/chunk-RTCVYIZA.ts +0 -221
  106. package/dist/chunks/chunk-RTCVYIZA.ts.map +0 -7
  107. package/dist/chunks/chunk-RY2AEGMZ.ts +0 -34
  108. package/dist/chunks/chunk-RY2AEGMZ.ts.map +0 -7
  109. package/dist/chunks/chunk-SWGQLFSD.ts +0 -60
  110. package/dist/chunks/chunk-SWGQLFSD.ts.map +0 -7
  111. package/dist/chunks/chunk-TM2R67J3.ts +0 -52
  112. package/dist/chunks/chunk-TM2R67J3.ts.map +0 -7
  113. package/dist/chunks/chunk-TMZRHIIK.ts +0 -497
  114. package/dist/chunks/chunk-TMZRHIIK.ts.map +0 -7
  115. package/dist/chunks/chunk-TZ34IQ3M.ts +0 -59
  116. package/dist/chunks/chunk-TZ34IQ3M.ts.map +0 -7
  117. package/dist/chunks/chunk-UMBPMVJW.ts +0 -22
  118. package/dist/chunks/chunk-UMBPMVJW.ts.map +0 -7
  119. package/dist/chunks/chunk-VDG7LTYE.ts +0 -80
  120. package/dist/chunks/chunk-VDG7LTYE.ts.map +0 -7
  121. package/dist/chunks/chunk-X4NMONPE.ts +0 -73
  122. package/dist/chunks/chunk-X4NMONPE.ts.map +0 -7
  123. package/dist/chunks/chunk-YPJEN6NU.ts +0 -65
  124. package/dist/chunks/chunk-YPJEN6NU.ts.map +0 -7
  125. package/dist/chunks/chunk-YU53SHA7.ts +0 -328
  126. package/dist/chunks/chunk-YU53SHA7.ts.map +0 -7
  127. package/dist/chunks/completion-delivery-RSJU6BXL.ts +0 -17
  128. package/dist/chunks/completion-delivery-RSJU6BXL.ts.map +0 -7
  129. package/dist/chunks/config-status-FKGDECZ3.ts +0 -35
  130. package/dist/chunks/config-status-FKGDECZ3.ts.map +0 -7
  131. package/dist/chunks/config-ui-ABHYNGQ7.ts +0 -1212
  132. package/dist/chunks/config-ui-ABHYNGQ7.ts.map +0 -7
  133. package/dist/chunks/consult-LJU3IQY5.ts +0 -519
  134. package/dist/chunks/consult-LJU3IQY5.ts.map +0 -7
  135. package/dist/chunks/context-QDKLVQXE.ts +0 -11
  136. package/dist/chunks/context-QDKLVQXE.ts.map +0 -7
  137. package/dist/chunks/create-stateful-transport-JWC2EFYL.ts +0 -130
  138. package/dist/chunks/create-stateful-transport-JWC2EFYL.ts.map +0 -7
  139. package/dist/chunks/cwd-policy-NB6XZ5GF.ts +0 -17
  140. package/dist/chunks/cwd-policy-NB6XZ5GF.ts.map +0 -7
  141. package/dist/chunks/delegation-contract-LA56I5DT.ts +0 -24
  142. package/dist/chunks/delegation-contract-LA56I5DT.ts.map +0 -7
  143. package/dist/chunks/discovery-MIFB2U4Y.ts +0 -12
  144. package/dist/chunks/discovery-MIFB2U4Y.ts.map +0 -7
  145. package/dist/chunks/execution-Q2JZLKJA.ts +0 -4037
  146. package/dist/chunks/execution-Q2JZLKJA.ts.map +0 -7
  147. package/dist/chunks/in-process-transport-HJ6TZXC3.ts +0 -39
  148. package/dist/chunks/in-process-transport-HJ6TZXC3.ts.map +0 -7
  149. package/dist/chunks/inspect-UH2TKH6E.ts +0 -634
  150. package/dist/chunks/inspect-UH2TKH6E.ts.map +0 -7
  151. package/dist/chunks/peer-communication-KL36Z6CO.ts +0 -294
  152. package/dist/chunks/peer-communication-KL36Z6CO.ts.map +0 -7
  153. package/dist/chunks/persistence-UY3PY6E5.ts +0 -348
  154. package/dist/chunks/persistence-UY3PY6E5.ts.map +0 -7
  155. package/dist/chunks/registry-BT54L6CY.ts +0 -1445
  156. package/dist/chunks/registry-BT54L6CY.ts.map +0 -7
  157. package/dist/chunks/retained-semantic-state-GM75FZGE.ts +0 -81
  158. package/dist/chunks/retained-semantic-state-GM75FZGE.ts.map +0 -7
  159. package/dist/chunks/rpc-transport-7R7DVCEB.ts +0 -1222
  160. package/dist/chunks/rpc-transport-7R7DVCEB.ts.map +0 -7
  161. package/dist/chunks/runtime-policy-5YELCOVC.ts +0 -15
  162. package/dist/chunks/runtime-policy-5YELCOVC.ts.map +0 -7
  163. package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts +0 -19
  164. package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts.map +0 -7
  165. package/dist/chunks/spawn-idempotency-BNHOSMZW.ts +0 -13
  166. package/dist/chunks/spawn-idempotency-BNHOSMZW.ts.map +0 -7
  167. package/dist/chunks/stateful-lifecycle-JAR6K55H.ts +0 -15
  168. package/dist/chunks/stateful-lifecycle-JAR6K55H.ts.map +0 -7
  169. package/dist/chunks/subprocess-transport-VHZJRBTW.ts +0 -160
  170. package/dist/chunks/subprocess-transport-VHZJRBTW.ts.map +0 -7
  171. package/dist/chunks/usage-recording-store-CW3EH2SZ.ts +0 -164
  172. package/dist/chunks/usage-recording-store-CW3EH2SZ.ts.map +0 -7
  173. package/dist/chunks/workspace-WDKLN72J.ts +0 -11
  174. package/dist/chunks/workspace-WDKLN72J.ts.map +0 -7
  175. package/docs/async-runtime-protocol.md +0 -84
  176. package/docs/implementation-notes/pi-subagents-capability-matrix.md +0 -62
  177. package/docs/implementation-notes/pi-subagents-current-direction.md +0 -99
  178. package/docs/implementation-notes/pi-subagents-rpc-v1.md +0 -153
  179. package/docs/pi-subagents-diagrams.md +0 -183
  180. package/src/adaptive-scheduler.ts +0 -224
  181. package/src/admission-benchmark.ts +0 -95
  182. package/src/admission-policy.ts +0 -78
  183. package/src/agent-projection.ts +0 -53
  184. package/src/agents/built-ins.ts +0 -71
  185. package/src/agents/catalog.ts +0 -241
  186. package/src/agents/discovery.ts +0 -265
  187. package/src/agents/types.ts +0 -100
  188. package/src/agents.ts +0 -51
  189. package/src/async-subagent-benchmark.ts +0 -532
  190. package/src/auto-transport.ts +0 -121
  191. package/src/blocking-status.ts +0 -63
  192. package/src/cached-module-loader.ts +0 -18
  193. package/src/capabilities.ts +0 -145
  194. package/src/capability-grant.ts +0 -115
  195. package/src/capability-router.ts +0 -107
  196. package/src/child-peer-bridge.ts +0 -124
  197. package/src/child-peer-tools.ts +0 -132
  198. package/src/completion-delivery.ts +0 -409
  199. package/src/completion-render.ts +0 -190
  200. package/src/completion-requirement.ts +0 -479
  201. package/src/completion-routing.ts +0 -24
  202. package/src/config-registration.ts +0 -114
  203. package/src/config-status.ts +0 -217
  204. package/src/config-ui.ts +0 -983
  205. package/src/consult-policy.ts +0 -15
  206. package/src/consult-registration.ts +0 -125
  207. package/src/consult-render.ts +0 -194
  208. package/src/consult-resources.ts +0 -51
  209. package/src/consult-tool.ts +0 -95
  210. package/src/consult.ts +0 -566
  211. package/src/context.ts +0 -126
  212. package/src/create-stateful-transport.ts +0 -157
  213. package/src/cwd-policy.ts +0 -183
  214. package/src/delegation-contract.ts +0 -463
  215. package/src/execution/budget.ts +0 -56
  216. package/src/execution/runtime-policy.ts +0 -19
  217. package/src/execution-plan.ts +0 -322
  218. package/src/execution-ui.ts +0 -259
  219. package/src/execution.ts +0 -1679
  220. package/src/in-process-transport.ts +0 -941
  221. package/src/inspect-registration.ts +0 -64
  222. package/src/inspect-render.ts +0 -334
  223. package/src/inspect-tool.ts +0 -45
  224. package/src/inspect.ts +0 -769
  225. package/src/integration-controller.ts +0 -98
  226. package/src/limits.ts +0 -75
  227. package/src/orchestration-metrics.ts +0 -116
  228. package/src/outcome.ts +0 -61
  229. package/src/panel-child-group.ts +0 -35
  230. package/src/panel-contract.ts +0 -343
  231. package/src/panel-evidence.ts +0 -59
  232. package/src/panel-execution.ts +0 -769
  233. package/src/panel-failure.ts +0 -56
  234. package/src/panel-planning.ts +0 -175
  235. package/src/panel-presets.ts +0 -3
  236. package/src/panel-prompts.ts +0 -132
  237. package/src/panel-reconciliation.ts +0 -57
  238. package/src/panel-render.ts +0 -103
  239. package/src/parallel-limit-ui.ts +0 -113
  240. package/src/params.ts +0 -278
  241. package/src/peer-communication.ts +0 -352
  242. package/src/peer-transport.ts +0 -49
  243. package/src/persistence.ts +0 -522
  244. package/src/pi-args.ts +0 -43
  245. package/src/pi-invocation.ts +0 -168
  246. package/src/process-control.ts +0 -43
  247. package/src/prompt-resources.ts +0 -38
  248. package/src/prompt-source-safety.ts +0 -48
  249. package/src/protocol.ts +0 -76
  250. package/src/registry-types.ts +0 -204
  251. package/src/registry.ts +0 -1698
  252. package/src/render-common.ts +0 -252
  253. package/src/render.ts +0 -704
  254. package/src/result-contract.ts +0 -431
  255. package/src/retained-semantic-state.ts +0 -100
  256. package/src/rpc-timeout-finalization.ts +0 -207
  257. package/src/rpc-transport-metadata.ts +0 -65
  258. package/src/rpc-transport.ts +0 -1035
  259. package/src/rpc-turn-capture.ts +0 -217
  260. package/src/runner-outcome.ts +0 -31
  261. package/src/runner-result.ts +0 -55
  262. package/src/runner-types.ts +0 -102
  263. package/src/runner-usage.ts +0 -48
  264. package/src/runner.ts +0 -866
  265. package/src/safe-text.ts +0 -67
  266. package/src/semantic-snapshot.ts +0 -214
  267. package/src/session-guidance-contract.ts +0 -399
  268. package/src/settings/inspection.ts +0 -302
  269. package/src/settings/schema.ts +0 -195
  270. package/src/settings-reader.ts +0 -215
  271. package/src/settings.ts +0 -452
  272. package/src/spawn-idempotency.ts +0 -68
  273. package/src/stateful-agent-view.ts +0 -85
  274. package/src/stateful-config.ts +0 -13
  275. package/src/stateful-guidance.ts +0 -29
  276. package/src/stateful-lifecycle.ts +0 -74
  277. package/src/stateful-limit-ui.ts +0 -249
  278. package/src/stateful-limits.ts +0 -96
  279. package/src/stateful-prompt.ts +0 -49
  280. package/src/stateful-registration.ts +0 -1320
  281. package/src/stateful-render.ts +0 -318
  282. package/src/stateful-safety.ts +0 -47
  283. package/src/stateful-tool-params.ts +0 -215
  284. package/src/stateful.ts +0 -12
  285. package/src/subagent-details.ts +0 -43
  286. package/src/subagents-extension.ts +0 -355
  287. package/src/subprocess-transport.ts +0 -145
  288. package/src/supervision.ts +0 -104
  289. package/src/task-path.ts +0 -65
  290. package/src/timeout-checkpoint.ts +0 -305
  291. package/src/timeout-finalization.ts +0 -75
  292. package/src/tool-schema-compatibility.ts +0 -73
  293. package/src/transport-types.ts +0 -74
  294. package/src/transport-ui.ts +0 -135
  295. package/src/transport.ts +0 -43
  296. package/src/turn-budget.ts +0 -109
  297. package/src/usage-format.ts +0 -42
  298. package/src/usage-recording-config.ts +0 -13
  299. package/src/usage-recording-store.ts +0 -183
  300. package/src/usage-recording.ts +0 -478
  301. package/src/verification-harness.ts +0 -516
  302. package/src/verification-policy.ts +0 -67
  303. package/src/verification-receipt.ts +0 -275
  304. package/src/verified-execution-benchmark.ts +0 -86
  305. package/src/verified-execution-contract.ts +0 -191
  306. package/src/verified-execution-schema.ts +0 -32
  307. package/src/work-item-ledger.ts +0 -1404
  308. package/src/work-item-persistence.ts +0 -254
  309. package/src/workflow-completion-controller.ts +0 -397
  310. package/src/workflow-planning.ts +0 -172
  311. package/src/workflow-tree-identity.ts +0 -289
  312. package/src/workflow-ui.ts +0 -69
  313. package/src/workflow-verification.ts +0 -296
  314. package/src/workspace.ts +0 -174
@@ -1,56 +0,0 @@
1
- import type { SingleResult } from "./runner-types.js";
2
-
3
- export const PANEL_FAILURE_KINDS = [
4
- "transient-launch",
5
- "transient-transport",
6
- "invalid-contract",
7
- "semantic-stall",
8
- "permission-denied",
9
- "budget-exhausted",
10
- "cancelled",
11
- "task-failed",
12
- ] as const;
13
-
14
- export type PanelFailureKind = (typeof PANEL_FAILURE_KINDS)[number];
15
-
16
- export interface PanelFailure {
17
- reviewerId?: string;
18
- kind: PanelFailureKind;
19
- retryable: boolean;
20
- message?: string;
21
- }
22
-
23
- export function classifyPanelFailure(
24
- result: Partial<
25
- Pick<
26
- SingleResult,
27
- | "launchFailed"
28
- | "resultContractInvalid"
29
- | "stopReason"
30
- | "timedOut"
31
- | "aborted"
32
- | "errorMessage"
33
- | "stderr"
34
- | "termination"
35
- >
36
- >,
37
- ): Omit<PanelFailure, "reviewerId" | "message"> {
38
- if (result.launchFailed) return { kind: "transient-launch", retryable: true };
39
- if (result.resultContractInvalid) return { kind: "invalid-contract", retryable: false };
40
- if (result.stopReason === "semantic-stall" || result.termination?.reason === "idle_timeout") {
41
- return { kind: "semantic-stall", retryable: false };
42
- }
43
- if (result.aborted || result.stopReason === "aborted")
44
- return { kind: "cancelled", retryable: false };
45
- if (result.timedOut || result.stopReason === "timeout") {
46
- return { kind: "budget-exhausted", retryable: false };
47
- }
48
- const message = `${result.errorMessage ?? ""}\n${result.stderr ?? ""}`;
49
- if (/permission|denied|not allowed|unauthori[sz]ed/iu.test(message)) {
50
- return { kind: "permission-denied", retryable: false };
51
- }
52
- if (/transport|socket|connection reset|temporar|econnreset|eai_again/iu.test(message)) {
53
- return { kind: "transient-transport", retryable: true };
54
- }
55
- return { kind: "task-failed", retryable: false };
56
- }
@@ -1,175 +0,0 @@
1
- import type { AgentConfig } from "./agents/types.js";
2
- import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8 } from "./limits.js";
3
- import type { PanelPreset } from "./panel-presets.js";
4
- import { type WorkItemDefinition, WorkItemLedger } from "./work-item-ledger.js";
5
-
6
- export type { PanelPreset } from "./panel-presets.js";
7
-
8
- export interface PanelReviewerRequest {
9
- id: string;
10
- agent: string;
11
- focus?: string;
12
- timeoutMs?: number;
13
- idleTimeoutMs?: number;
14
- maxTurns?: number;
15
- maxToolCalls?: number;
16
- thinkingLevel?: string;
17
- }
18
-
19
- export interface PanelSynthesizerRequest {
20
- agent: string;
21
- timeoutMs?: number;
22
- idleTimeoutMs?: number;
23
- maxTurns?: number;
24
- maxToolCalls?: number;
25
- thinkingLevel?: string;
26
- }
27
-
28
- export interface PanelRequestLike {
29
- id?: string;
30
- preset?: PanelPreset;
31
- task: string;
32
- context?: string;
33
- reviewers: PanelReviewerRequest[];
34
- synthesizer: PanelSynthesizerRequest;
35
- minValidReviews?: number;
36
- }
37
-
38
- export interface PanelPhaseBudgets {
39
- totalMs: number;
40
- reviewMs: number;
41
- finalizationMs: number;
42
- synthesisMs: number;
43
- cleanupMs: number;
44
- reviewerTimeoutMs: number;
45
- }
46
-
47
- const MIN_PHASE_MS = 1;
48
- const MAX_ID_BYTES = 256;
49
- const MAX_FOCUS_BYTES = 8 * 1024;
50
-
51
- export function planPanelBudgets(totalMs: number, _reviewerCount: number): PanelPhaseBudgets {
52
- const normalized = Math.floor(totalMs);
53
- if (!Number.isSafeInteger(normalized) || normalized < 4 * MIN_PHASE_MS) {
54
- throw new Error(
55
- "Panel total timeout is too small to reserve review, finalization, synthesis, and cleanup phases",
56
- );
57
- }
58
- const cleanupMs = Math.max(MIN_PHASE_MS, Math.floor(normalized * 0.05));
59
- const synthesisMs = Math.max(MIN_PHASE_MS, Math.floor(normalized * 0.2));
60
- const finalizationMs = Math.max(MIN_PHASE_MS, Math.floor(normalized * 0.1));
61
- const reviewMs = normalized - cleanupMs - synthesisMs - finalizationMs;
62
- if (reviewMs < MIN_PHASE_MS) {
63
- throw new Error("Panel total timeout is too small for the review phase");
64
- }
65
- return {
66
- totalMs: normalized,
67
- reviewMs,
68
- finalizationMs,
69
- synthesisMs,
70
- cleanupMs,
71
- reviewerTimeoutMs: reviewMs,
72
- };
73
- }
74
-
75
- export function createPanelWorkLedger(
76
- panelId: string,
77
- panel: PanelRequestLike,
78
- agents: readonly AgentConfig[],
79
- ): WorkItemLedger {
80
- const taskPreview = truncateUtf8(panel.task, 256).text;
81
- const reviewerItems: WorkItemDefinition[] = panel.reviewers.map((reviewer) => {
82
- const agent = agents.find((candidate) => candidate.name === reviewer.agent);
83
- return {
84
- id: `review:${reviewer.id}`,
85
- objective: `Panel review ${reviewer.id}: ${taskPreview}`,
86
- dependencies: [],
87
- inputArtifacts: [],
88
- inputArtifactVersions: {},
89
- requiredCapabilities: ["independent-review"],
90
- requiredTools: [],
91
- selectedAgentName: reviewer.agent,
92
- sideEffectPolicy:
93
- agent?.capabilityManifest?.authority?.filesystem === "read" ? "read-only" : "mutating",
94
- readPaths: [],
95
- writePaths: [],
96
- ownershipKeys: [],
97
- acceptanceCriteria: ["Return one valid panel-review:v1 evidence artifact"],
98
- };
99
- });
100
- return WorkItemLedger.create({
101
- workflowId: panelId,
102
- items: [
103
- ...reviewerItems,
104
- {
105
- id: "synthesis",
106
- objective: "Reconcile valid panel evidence while preserving objections and dissent",
107
- dependencies: reviewerItems.map((item) => item.id),
108
- dependencyPolicy: "settled",
109
- inputArtifacts: [],
110
- inputArtifactVersions: {},
111
- requiredCapabilities: ["evidence-review"],
112
- requiredTools: [],
113
- selectedAgentName: panel.synthesizer.agent,
114
- sideEffectPolicy: "read-only",
115
- readPaths: [],
116
- writePaths: [],
117
- ownershipKeys: [],
118
- acceptanceCriteria: [
119
- "Preserve every blocking objection and disagreement with evidence-backed resolution state",
120
- ],
121
- integrationOwner: true,
122
- },
123
- ],
124
- });
125
- }
126
-
127
- export function validatePanelRequest(panel: PanelRequestLike, maxReviewers = 64): void {
128
- if (
129
- panel.id !== undefined &&
130
- (!/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(panel.id) ||
131
- Buffer.byteLength(panel.id, "utf8") > MAX_ID_BYTES)
132
- ) {
133
- throw new Error("Panel id must use 1-256 safe identifier bytes");
134
- }
135
- if (!panel.task.trim()) throw new Error("Panel task must not be empty");
136
- if (Buffer.byteLength(panel.task, "utf8") > DEFAULT_MAX_CONTEXT_BYTES) {
137
- throw new Error("Panel task exceeds the bounded context limit");
138
- }
139
- if (panel.context && Buffer.byteLength(panel.context, "utf8") > DEFAULT_MAX_CONTEXT_BYTES) {
140
- throw new Error("Panel shared context exceeds the bounded context limit");
141
- }
142
- if (panel.reviewers.length < 2) throw new Error("Panel mode requires at least two reviewers");
143
- if (panel.reviewers.length > maxReviewers) {
144
- throw new Error(
145
- `Panel has too many reviewers (${panel.reviewers.length}); configured max is ${maxReviewers}`,
146
- );
147
- }
148
- const ids = panel.reviewers.map((reviewer) => reviewer.id.trim());
149
- if (
150
- ids.some(
151
- (id) =>
152
- !id ||
153
- Buffer.byteLength(id, "utf8") > MAX_ID_BYTES ||
154
- !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(id),
155
- )
156
- ) {
157
- throw new Error("Panel reviewer ids must use 1-256 safe identifier bytes");
158
- }
159
- if (new Set(ids).size !== ids.length) throw new Error("Panel reviewer ids must be unique");
160
- if (panel.reviewers.some((reviewer) => !reviewer.agent.trim())) {
161
- throw new Error("Every panel reviewer must name an agent");
162
- }
163
- if (
164
- panel.reviewers.some(
165
- (reviewer) => reviewer.focus && Buffer.byteLength(reviewer.focus, "utf8") > MAX_FOCUS_BYTES,
166
- )
167
- ) {
168
- throw new Error("Panel reviewer focus exceeds the bounded field limit");
169
- }
170
- if (!panel.synthesizer.agent.trim()) throw new Error("Panel synthesizer must name an agent");
171
- const minValid = panel.minValidReviews ?? 2;
172
- if (!Number.isInteger(minValid) || minValid < 2 || minValid > panel.reviewers.length) {
173
- throw new Error("minValidReviews must be between two and the reviewer count");
174
- }
175
- }
@@ -1,3 +0,0 @@
1
- export const PANEL_PRESETS = ["code-review", "research", "security-review", "custom"] as const;
2
-
3
- export type PanelPreset = (typeof PANEL_PRESETS)[number];
@@ -1,132 +0,0 @@
1
- import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8 } from "./limits.js";
2
- import {
3
- type PanelReview,
4
- panelReviewInstruction,
5
- panelSynthesisInstruction,
6
- } from "./panel-contract.js";
7
- import type { PanelFailure } from "./panel-failure.js";
8
- import type { PanelPreset } from "./panel-planning.js";
9
-
10
- const PRESET_GUIDANCE: Record<PanelPreset, string> = {
11
- "code-review":
12
- "Review correctness, regressions, baseline security, maintainability, tests, and integration risk.",
13
- research:
14
- "Assess source quality, evidence traceability, contradictory evidence, uncertainty, and unsupported claims.",
15
- "security-review":
16
- "Review trust boundaries, authority, injection, data exposure, unsafe side effects, and missing mitigations.",
17
- custom: "Apply the shared task and your assigned focus without inventing unstated requirements.",
18
- };
19
-
20
- const PROMPT_SEPARATOR = "\n\n";
21
- const MAX_REVIEWER_SUFFIX_BYTES = 12 * 1024;
22
- const REVIEWER_SHARED_BYTES = DEFAULT_MAX_CONTEXT_BYTES - MAX_REVIEWER_SUFFIX_BYTES;
23
-
24
- export function buildPanelReviewerPrompt(input: {
25
- panelId: string;
26
- preset: PanelPreset;
27
- task: string;
28
- context?: string;
29
- reviewerId: string;
30
- focus?: string;
31
- }): string {
32
- const shared = buildReviewerSharedBlock(input);
33
- const reviewer = [
34
- `Reviewer id: ${input.reviewerId}`,
35
- input.focus ? `Reviewer focus:\n${input.focus}` : undefined,
36
- "Work independently and do not assume another reviewer will catch or resolve a problem.",
37
- `Panel review contract:\n${panelReviewInstruction(input.reviewerId)}`,
38
- ]
39
- .filter((part): part is string => Boolean(part))
40
- .join(PROMPT_SEPARATOR);
41
- return joinWithRequiredSuffix(shared, reviewer);
42
- }
43
-
44
- export function buildPanelSynthesisPrompt(input: {
45
- panelId: string;
46
- task: string;
47
- reviews: readonly PanelReview[];
48
- failures: readonly PanelFailure[];
49
- }): string {
50
- const validIds = input.reviews.map((review) => review.reviewerId);
51
- const failedIds = input.failures
52
- .map((failure) => failure.reviewerId)
53
- .filter((id): id is string => Boolean(id));
54
- const artifacts = input.reviews.map((review) => ({
55
- reviewerId: review.reviewerId,
56
- disposition: review.disposition,
57
- blocking: review.blocking,
58
- findings: review.findings,
59
- missingChecks: review.missingChecks,
60
- limitations: review.limitations,
61
- provenance: review.provenance,
62
- }));
63
- const requiredSuffix = [
64
- "Reconcile evidence without majority voting and without erasing dissent.",
65
- `Panel synthesis contract:\n${panelSynthesisInstruction(validIds, failedIds)}`,
66
- ].join(PROMPT_SEPARATOR);
67
- const fixedPrefixParts = [
68
- `Panel: ${input.panelId}`,
69
- "Shared task:\n",
70
- `Panel evidence artifacts:\n${JSON.stringify(artifacts)}`,
71
- `Failed or invalid reviewers:\n${JSON.stringify(input.failures)}`,
72
- ];
73
- const taskBudget = Math.max(
74
- 0,
75
- DEFAULT_MAX_CONTEXT_BYTES -
76
- byteLength(fixedPrefixParts.join(PROMPT_SEPARATOR)) -
77
- byteLength(PROMPT_SEPARATOR) -
78
- byteLength(requiredSuffix),
79
- );
80
- const prefix = [
81
- fixedPrefixParts[0],
82
- `Shared task:\n${truncateUtf8(input.task, taskBudget).text}`,
83
- fixedPrefixParts[2],
84
- fixedPrefixParts[3],
85
- ].join(PROMPT_SEPARATOR);
86
- return joinWithRequiredSuffix(prefix, requiredSuffix);
87
- }
88
-
89
- function buildReviewerSharedBlock(input: {
90
- panelId: string;
91
- preset: PanelPreset;
92
- task: string;
93
- context?: string;
94
- }): string {
95
- const context = input.context || undefined;
96
- const hasContext = context !== undefined;
97
- const fixedParts = [
98
- `Panel: ${input.panelId}`,
99
- `Preset: ${input.preset}`,
100
- "Shared task:\n",
101
- hasContext ? "Shared context:\n" : undefined,
102
- `Preset guidance:\n${PRESET_GUIDANCE[input.preset]}`,
103
- ].filter((part): part is string => part !== undefined);
104
- const contentBudget = Math.max(
105
- 0,
106
- REVIEWER_SHARED_BYTES - byteLength(fixedParts.join(PROMPT_SEPARATOR)),
107
- );
108
- const taskBudget = hasContext ? Math.floor(contentBudget / 2) : contentBudget;
109
- const contextBudget = contentBudget - taskBudget;
110
- return [
111
- fixedParts[0],
112
- fixedParts[1],
113
- `Shared task:\n${truncateUtf8(input.task, taskBudget).text}`,
114
- hasContext ? `Shared context:\n${truncateUtf8(context, contextBudget).text}` : undefined,
115
- fixedParts.at(-1),
116
- ]
117
- .filter((part): part is string => part !== undefined)
118
- .join(PROMPT_SEPARATOR);
119
- }
120
-
121
- function joinWithRequiredSuffix(prefix: string, suffix: string): string {
122
- const suffixBytes = byteLength(PROMPT_SEPARATOR) + byteLength(suffix);
123
- if (suffixBytes > DEFAULT_MAX_CONTEXT_BYTES) {
124
- throw new Error("Panel prompt contract exceeds the bounded context limit");
125
- }
126
- const boundedPrefix = truncateUtf8(prefix, DEFAULT_MAX_CONTEXT_BYTES - suffixBytes).text;
127
- return `${boundedPrefix}${PROMPT_SEPARATOR}${suffix}`;
128
- }
129
-
130
- function byteLength(value: string): number {
131
- return Buffer.byteLength(value, "utf8");
132
- }
@@ -1,57 +0,0 @@
1
- import type { PanelFinding, PanelReview } from "./panel-contract.js";
2
- import type { PanelFailure } from "./panel-failure.js";
3
-
4
- export interface PanelBlockingObjection {
5
- reviewerId: string;
6
- finding: PanelFinding;
7
- }
8
-
9
- export type PanelReconciliation =
10
- | {
11
- kind: "ready-for-synthesis";
12
- reviews: PanelReview[];
13
- failures: PanelFailure[];
14
- blockingObjections: PanelBlockingObjection[];
15
- verified: false;
16
- }
17
- | {
18
- kind: "insufficient-panel";
19
- partialReviews: PanelReview[];
20
- failures: PanelFailure[];
21
- blockingObjections: PanelBlockingObjection[];
22
- consensus: false;
23
- };
24
-
25
- export function reconcilePanel(input: {
26
- reviews: PanelReview[];
27
- failures: PanelFailure[];
28
- minValidReviews: number;
29
- }): PanelReconciliation {
30
- const reviews = [...input.reviews].sort((left, right) =>
31
- left.reviewerId.localeCompare(right.reviewerId),
32
- );
33
- const failures = [...input.failures].sort((left, right) =>
34
- (left.reviewerId ?? "").localeCompare(right.reviewerId ?? ""),
35
- );
36
- const blockingObjections = reviews.flatMap((review) =>
37
- review.blocking
38
- ? review.findings.map((finding) => ({ reviewerId: review.reviewerId, finding }))
39
- : [],
40
- );
41
- if (reviews.length < input.minValidReviews) {
42
- return {
43
- kind: "insufficient-panel",
44
- partialReviews: reviews,
45
- failures,
46
- blockingObjections,
47
- consensus: false,
48
- };
49
- }
50
- return {
51
- kind: "ready-for-synthesis",
52
- reviews,
53
- failures,
54
- blockingObjections,
55
- verified: false,
56
- };
57
- }
@@ -1,103 +0,0 @@
1
- import type { Theme } from "@earendil-works/pi-coding-agent";
2
- import { Text } from "@earendil-works/pi-tui";
3
- import type { SubagentParams } from "./params.js";
4
- import { safeBlock, safeLine } from "./render-common.js";
5
- import type { SubagentDetails } from "./subagent-details.js";
6
-
7
- export function renderPanelCall(args: SubagentParams, theme: Theme): Text | undefined {
8
- const panel = args.panel;
9
- if (!panel) return undefined;
10
- const scope = args.agentScope ?? "user";
11
- let text =
12
- theme.fg("toolTitle", theme.bold("subagent ")) +
13
- theme.fg("accent", `panel ${panel.preset ?? "custom"} (${panel.reviewers.length} reviewers)`) +
14
- theme.fg("muted", ` [${scope}]`);
15
- text += `\n ${theme.fg("dim", preview(panel.task, 80))}`;
16
- for (const reviewer of panel.reviewers.slice(0, 3)) {
17
- text += `\n ${theme.fg("muted", `${safeLine(reviewer.id, "reviewer", 256)}:`)} ${theme.fg("accent", safeLine(reviewer.agent, "agent", 256))}`;
18
- if (reviewer.focus) text += theme.fg("dim", ` ${preview(reviewer.focus, 40)}`);
19
- }
20
- if (panel.reviewers.length > 3) {
21
- text += `\n ${theme.fg("muted", `... +${panel.reviewers.length - 3} more`)}`;
22
- }
23
- text += `\n ${theme.fg("muted", "synthesis → ")}${theme.fg("accent", safeLine(panel.synthesizer.agent, "agent", 256))}`;
24
- return new Text(text, 0, 0);
25
- }
26
-
27
- export function renderPanelResult(
28
- details: SubagentDetails,
29
- expanded: boolean,
30
- isPartial: boolean,
31
- theme: Theme,
32
- ): Text | undefined {
33
- const panel = details.panel;
34
- if (details.mode !== "panel" || !panel) return undefined;
35
- const running = isPartial || panel.state === "running";
36
- const status = running ? "running" : panel.state;
37
- const color =
38
- status === "completed"
39
- ? "success"
40
- : status === "running" || status === "degraded"
41
- ? "warning"
42
- : "error";
43
- let text = `${theme.fg(color, status)} ${theme.fg("accent", safeLine(panel.preset, "panel", 128))}`;
44
- text += theme.fg(
45
- "muted",
46
- ` · valid ${panel.validReviewCount}/${panel.reviewerIds.length} · failed ${panel.failedReviewCount} · blockers ${panel.blockingObjectionCount} · dissent ${panel.dissentCount}`,
47
- );
48
- if (panel.synthesis) {
49
- text += `\n${theme.fg("toolOutput", safeBlock(panel.synthesis.summary, "", 8 * 1024))}`;
50
- } else if (panel.state === "insufficient-panel") {
51
- text += `\n${theme.fg("warning", "Synthesis skipped because too few valid reviews remained.")}`;
52
- }
53
- if (!expanded) return new Text(text, 0, 0);
54
-
55
- text += `\n\n${theme.fg("muted", "Shared task: ")}${theme.fg("dim", safeBlock(panel.sharedTaskPreview, "", 2 * 1024))}`;
56
- text += `\n\n${theme.fg("muted", "Reviewers:")}`;
57
- for (const reviewerId of panel.reviewerIds) {
58
- const artifact = panel.evidence.find((candidate) => candidate.reviewerId === reviewerId);
59
- const failure = panel.failures.find((candidate) => candidate.reviewerId === reviewerId);
60
- const result = details.results.find(
61
- (candidate) => candidate.step === panel.reviewerIds.indexOf(reviewerId) + 1,
62
- );
63
- const model = result?.actualModel ?? result?.model;
64
- text += `\n ${theme.fg("accent", safeLine(reviewerId, "reviewer", 256))}`;
65
- if (model) text += theme.fg("muted", ` · ${safeLine(model, "", 256)}`);
66
- if (artifact) {
67
- text += theme.fg(
68
- "muted",
69
- ` · ${artifact.review.disposition} · revision ${artifact.revision}`,
70
- );
71
- for (const finding of artifact.review.findings) {
72
- text += `\n ${theme.fg(finding.severity === "critical" || finding.severity === "high" ? "error" : "warning", `[${finding.severity}]`)} ${safeLine(finding.title, "finding", 512)}`;
73
- }
74
- } else if (failure) {
75
- text += theme.fg("error", ` · ${safeLine(failure.kind, "failed", 128)}`);
76
- }
77
- }
78
- if (panel.synthesis?.disagreements.length) {
79
- text += `\n\n${theme.fg("muted", "Disagreements:")}`;
80
- for (const disagreement of panel.synthesis.disagreements) {
81
- text += `\n ${safeLine(disagreement.summary, "disagreement", 1024)}`;
82
- }
83
- }
84
- if (panel.synthesis?.objections.length) {
85
- text += `\n\n${theme.fg("muted", "Blocking objections:")}`;
86
- for (const objection of panel.synthesis.objections) {
87
- text += `\n ${safeLine(`${objection.reviewerId}/${objection.findingId}: ${objection.resolution}`, "objection", 1024)}`;
88
- }
89
- }
90
- const limitations = panel.synthesis?.limitations ?? [];
91
- if (limitations.length) {
92
- text += `\n\n${theme.fg("muted", "Limitations:")}`;
93
- for (const limitation of limitations) text += `\n ${safeLine(limitation, "", 1024)}`;
94
- }
95
- text += `\n\n${theme.fg("muted", `Budget: review ${panel.budgets.reviewMs}ms · finalization ${panel.budgets.finalizationMs}ms · synthesis ${panel.budgets.synthesisMs}ms · cleanup ${panel.budgets.cleanupMs}ms`)}`;
96
- text += `\n${theme.fg("muted", `Cleanup: ${panel.cleanupComplete ? "complete" : "pending"}`)}`;
97
- return new Text(text, 0, 0);
98
- }
99
-
100
- function preview(value: unknown, maxLength: number): string {
101
- const text = safeLine(value, "", 2 * 1024);
102
- return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
103
- }
@@ -1,113 +0,0 @@
1
- import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
- import { MAX_BLOCKING_PARALLEL_CONCURRENCY, MAX_CONFIGURABLE_PARALLEL_TASKS } from "./limits.js";
3
- import {
4
- inspectBlockingParallelLimitSettings,
5
- updateBlockingMaxParallelTasksSetting,
6
- } from "./settings.js";
7
-
8
- export interface BlockingParallelLimitRuntime {
9
- getMaxParallelTasks(): number;
10
- setMaxParallelTasks(value: number): void;
11
- }
12
-
13
- export function blockingParallelLimitScreen(runtime: BlockingParallelLimitRuntime) {
14
- const limit = inspectBlockingParallelLimitSettings();
15
- return {
16
- kind: "input" as const,
17
- title: "Blocking Worker Limit",
18
- lines: [
19
- "This applies only to requests that make Pi wait for delegated work.",
20
- `Current: ${runtime.getMaxParallelTasks()} subagents per blocking request`,
21
- `Allowed: 1-${MAX_CONFIGURABLE_PARALLEL_TASKS}`,
22
- `Pi still starts at most ${MAX_BLOCKING_PARALLEL_CONCURRENCY} of them at once.`,
23
- safeTerminalText(limit.path),
24
- ],
25
- placeholder: "Enter a whole number",
26
- action: "set-parallel-limit" as const,
27
- hint: "back" as const,
28
- };
29
- }
30
-
31
- export function applyBlockingParallelLimitSetting(
32
- value: string | undefined,
33
- ctx: ExtensionCommandContext,
34
- runtime: BlockingParallelLimitRuntime,
35
- ) {
36
- const normalized = value?.trim() ?? "";
37
- if (!/^\d+$/u.test(normalized)) {
38
- notifyValidationError(ctx);
39
- return { kind: "rejected" as const };
40
- }
41
- const next = Number(normalized);
42
- if (!Number.isSafeInteger(next) || next < 1 || next > MAX_CONFIGURABLE_PARALLEL_TASKS) {
43
- notifyValidationError(ctx);
44
- return { kind: "rejected" as const };
45
- }
46
- const configured = inspectBlockingParallelLimitSettings();
47
- if (configured.error) {
48
- ctx.ui.notify(
49
- `Subagent settings cannot be edited: ${safeTerminalText(configured.error)}`,
50
- "error",
51
- );
52
- return { kind: "rejected" as const };
53
- }
54
- const previousRuntime = runtime.getMaxParallelTasks();
55
- const runtimeChanged = next !== previousRuntime;
56
- const settingsChanged = next !== configured.value;
57
- if (!runtimeChanged && !settingsChanged) return { kind: "back" as const };
58
-
59
- if (runtimeChanged) {
60
- try {
61
- runtime.setMaxParallelTasks(next);
62
- } catch (error) {
63
- ctx.ui.notify(
64
- `Parallel-worker limit was not applied; user settings are unchanged: ${formatError(error)}`,
65
- "error",
66
- );
67
- return { kind: "rejected" as const };
68
- }
69
- }
70
- if (settingsChanged) {
71
- try {
72
- updateBlockingMaxParallelTasksSetting(next);
73
- } catch (saveError) {
74
- if (runtimeChanged) {
75
- try {
76
- runtime.setMaxParallelTasks(previousRuntime);
77
- } catch (rollbackError) {
78
- ctx.ui.notify(
79
- `Subagent settings were not saved, and the runtime rollback failed: ${formatError(new AggregateError([saveError, rollbackError]))}`,
80
- "error",
81
- );
82
- return { kind: "rejected" as const };
83
- }
84
- }
85
- ctx.ui.notify(
86
- "Subagent settings were not saved; the previous runtime limit was restored.",
87
- "error",
88
- );
89
- return { kind: "rejected" as const };
90
- }
91
- }
92
- ctx.ui.notify(
93
- `${settingsChanged ? "Saved and " : ""}applied: maximum ${next} subagents per blocking request.`,
94
- "info",
95
- );
96
- return { kind: "back" as const };
97
- }
98
-
99
- function notifyValidationError(ctx: ExtensionCommandContext): void {
100
- ctx.ui.notify(
101
- `Blocking worker limit must be a whole number from 1 to ${MAX_CONFIGURABLE_PARALLEL_TASKS}.`,
102
- "warning",
103
- );
104
- }
105
-
106
- function safeTerminalText(value: string): string {
107
- // biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.
108
- return value.replace(/[\u0000-\u001f\u007f-\u009f]/gu, "?");
109
- }
110
-
111
- function formatError(error: unknown): string {
112
- return safeTerminalText(error instanceof Error ? error.message : String(error));
113
- }