@fitchmultz/pi-subagents 0.0.0-stage → 0.44.5

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 (347) hide show
  1. package/CHANGELOG.md +1654 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1533 -2
  4. package/agents/context-builder.md +97 -0
  5. package/agents/debugger.md +51 -0
  6. package/agents/delegate.md +13 -0
  7. package/agents/fixer.md +37 -0
  8. package/agents/oracle.md +71 -0
  9. package/agents/planner.md +72 -0
  10. package/agents/researcher.md +51 -0
  11. package/agents/reviewer-claude.md +69 -0
  12. package/agents/reviewer-gpt.md +68 -0
  13. package/agents/reviewer-ponytail.md +82 -0
  14. package/agents/reviewer-security.md +81 -0
  15. package/agents/reviewer.md +69 -0
  16. package/agents/scout.md +66 -0
  17. package/agents/ui-designer.md +51 -0
  18. package/agents/watcher.md +26 -0
  19. package/agents/worker.md +46 -0
  20. package/agents/writer.md +36 -0
  21. package/dist/agents/agent-management.js +923 -0
  22. package/dist/agents/agent-scope.js +7 -0
  23. package/dist/agents/agent-selection.js +20 -0
  24. package/dist/agents/agent-serializer.js +100 -0
  25. package/dist/agents/agents.js +752 -0
  26. package/dist/agents/chain-serializer.js +367 -0
  27. package/dist/agents/frontmatter.js +21 -0
  28. package/dist/agents/identity.js +29 -0
  29. package/dist/agents/skills.js +570 -0
  30. package/dist/extension/build-info.js +1 -0
  31. package/dist/extension/compact-tools.js +112 -0
  32. package/dist/extension/config.js +22 -0
  33. package/dist/extension/control-notices.js +29 -0
  34. package/dist/extension/doctor.js +143 -0
  35. package/dist/extension/fanout-child.js +326 -0
  36. package/dist/extension/index.js +577 -0
  37. package/dist/extension/schemas.js +416 -0
  38. package/dist/extension/tool-input.js +20 -0
  39. package/dist/extension/tool-result.js +14 -0
  40. package/dist/history/canonical-result.js +81 -0
  41. package/dist/history/database.js +72 -0
  42. package/dist/history/index.js +199 -0
  43. package/dist/history/ingest.js +468 -0
  44. package/dist/history/queries.js +350 -0
  45. package/dist/history/store.js +216 -0
  46. package/dist/history/types.js +4 -0
  47. package/dist/history/worker.js +466 -0
  48. package/dist/intercom/intercom-bridge.js +112 -0
  49. package/dist/intercom/live-intercom.js +94 -0
  50. package/dist/intercom/result-intercom.js +373 -0
  51. package/dist/pi-intercom/agent-dir.js +10 -0
  52. package/dist/pi-intercom/bridge.js +750 -0
  53. package/dist/pi-intercom/broker/broker.js +426 -0
  54. package/dist/pi-intercom/broker/client.js +492 -0
  55. package/dist/pi-intercom/broker/framing.js +65 -0
  56. package/dist/pi-intercom/broker/paths.js +42 -0
  57. package/dist/pi-intercom/broker/pid.js +66 -0
  58. package/dist/pi-intercom/broker/spawn.js +221 -0
  59. package/dist/pi-intercom/config.js +102 -0
  60. package/dist/pi-intercom/index.js +2942 -0
  61. package/dist/pi-intercom/reply-tracker.js +182 -0
  62. package/dist/pi-intercom/session-targets.js +128 -0
  63. package/dist/pi-intercom/subagent-live-events.js +132 -0
  64. package/dist/pi-intercom/topics.js +157 -0
  65. package/dist/pi-intercom/types.js +163 -0
  66. package/dist/pi-intercom/ui/compose.js +269 -0
  67. package/dist/pi-intercom/ui/inline-message.js +80 -0
  68. package/dist/pi-intercom/ui/session-list.js +124 -0
  69. package/dist/runs/background/async-control.js +95 -0
  70. package/dist/runs/background/async-execution.js +676 -0
  71. package/dist/runs/background/async-job-tracker.js +383 -0
  72. package/dist/runs/background/async-result-file.js +58 -0
  73. package/dist/runs/background/async-resume.js +305 -0
  74. package/dist/runs/background/async-status.js +442 -0
  75. package/dist/runs/background/completion-dedupe.js +69 -0
  76. package/dist/runs/background/completion-delivery.js +245 -0
  77. package/dist/runs/background/notify.js +71 -0
  78. package/dist/runs/background/parallel-groups.js +55 -0
  79. package/dist/runs/background/result-watcher.js +426 -0
  80. package/dist/runs/background/run-id-resolver.js +64 -0
  81. package/dist/runs/background/run-status.js +382 -0
  82. package/dist/runs/background/stale-run-reconciler.js +335 -0
  83. package/dist/runs/background/subagent-runner-launcher.js +55 -0
  84. package/dist/runs/background/subagent-runner.js +1872 -0
  85. package/dist/runs/background/top-level-async.js +5 -0
  86. package/dist/runs/foreground/chain-clarify.js +1152 -0
  87. package/dist/runs/foreground/clarify-invocation.js +72 -0
  88. package/dist/runs/foreground/execution-input.js +423 -0
  89. package/dist/runs/foreground/foreground-control.js +817 -0
  90. package/dist/runs/foreground/question-control.js +88 -0
  91. package/dist/runs/foreground/run-async-path.js +205 -0
  92. package/dist/runs/foreground/subagent-executor.js +442 -0
  93. package/dist/runs/foreground/subagent-params.js +165 -0
  94. package/dist/runs/foreground/wait-run.js +141 -0
  95. package/dist/runs/shared/acceptance-contract.js +253 -0
  96. package/dist/runs/shared/acceptance-evaluation.js +256 -0
  97. package/dist/runs/shared/acceptance-finalization.js +280 -0
  98. package/dist/runs/shared/acceptance-reports.js +147 -0
  99. package/dist/runs/shared/acceptance.js +4 -0
  100. package/dist/runs/shared/chain-outputs.js +110 -0
  101. package/dist/runs/shared/child-attempt.js +693 -0
  102. package/dist/runs/shared/child-execution-cwd.js +96 -0
  103. package/dist/runs/shared/child-observations.js +114 -0
  104. package/dist/runs/shared/claude-code.js +199 -0
  105. package/dist/runs/shared/completion-guard.js +31 -0
  106. package/dist/runs/shared/dynamic-fanout.js +274 -0
  107. package/dist/runs/shared/history-index.js +72 -0
  108. package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
  109. package/dist/runs/shared/model-fallback.js +185 -0
  110. package/dist/runs/shared/mutating-tool-guard.js +170 -0
  111. package/dist/runs/shared/native-finalization.js +101 -0
  112. package/dist/runs/shared/native-usage.js +153 -0
  113. package/dist/runs/shared/nested-events.js +700 -0
  114. package/dist/runs/shared/nested-path.js +48 -0
  115. package/dist/runs/shared/nested-render.js +108 -0
  116. package/dist/runs/shared/parallel-utils.js +61 -0
  117. package/dist/runs/shared/parent-receipts.js +68 -0
  118. package/dist/runs/shared/parent-usage.js +121 -0
  119. package/dist/runs/shared/pi-args.js +310 -0
  120. package/dist/runs/shared/pi-spawn.js +52 -0
  121. package/dist/runs/shared/run-history-queries.js +45 -0
  122. package/dist/runs/shared/run-history.js +110 -0
  123. package/dist/runs/shared/run-id-ambiguity.js +4 -0
  124. package/dist/runs/shared/run-records.js +679 -0
  125. package/dist/runs/shared/session-cwd-preload.js +33 -0
  126. package/dist/runs/shared/single-output.js +222 -0
  127. package/dist/runs/shared/streaming-text.js +15 -0
  128. package/dist/runs/shared/structured-output.js +55 -0
  129. package/dist/runs/shared/subagent-control.js +179 -0
  130. package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
  131. package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
  132. package/dist/runs/shared/supervisor-questions.js +350 -0
  133. package/dist/runs/shared/workflow-graph.js +214 -0
  134. package/dist/runs/shared/workflow-policy.js +84 -0
  135. package/dist/runs/shared/worktree.js +525 -0
  136. package/dist/shared/agent-context-policy.js +126 -0
  137. package/dist/shared/artifacts.js +96 -0
  138. package/dist/shared/atomic-json.js +13 -0
  139. package/dist/shared/cooperative.js +20 -0
  140. package/dist/shared/execution-cwd.js +14 -0
  141. package/dist/shared/file-coalescer.js +21 -0
  142. package/dist/shared/fork-context.js +49 -0
  143. package/dist/shared/formatters.js +85 -0
  144. package/dist/shared/journal-reader.js +407 -0
  145. package/dist/shared/lazy-tools.js +32 -0
  146. package/dist/shared/model-info.js +68 -0
  147. package/dist/shared/native-session.js +15 -0
  148. package/dist/shared/native-tui.js +18 -0
  149. package/dist/shared/native-typebox.js +20 -0
  150. package/dist/shared/post-exit-stdio-guard.js +197 -0
  151. package/dist/shared/prompt-sections.js +8 -0
  152. package/dist/shared/session-entries.js +52 -0
  153. package/dist/shared/session-identity.js +14 -0
  154. package/dist/shared/settings.js +254 -0
  155. package/dist/shared/status-format.js +111 -0
  156. package/dist/shared/temp-root.js +114 -0
  157. package/dist/shared/types.js +207 -0
  158. package/dist/shared/ui-mode.js +3 -0
  159. package/dist/shared/utils.js +334 -0
  160. package/dist/slash/prompt-template-bridge.js +298 -0
  161. package/dist/slash/slash-bridge.js +116 -0
  162. package/dist/slash/slash-commands.js +607 -0
  163. package/dist/slash/slash-live-state.js +246 -0
  164. package/dist/tui/action-hints.js +75 -0
  165. package/dist/tui/agent-history.js +197 -0
  166. package/dist/tui/agent-view.js +1656 -0
  167. package/dist/tui/render.js +1515 -0
  168. package/docs/intercom-bridge.md +212 -0
  169. package/docs/intercom.md +581 -0
  170. package/docs/performance.md +121 -0
  171. package/package.json +102 -4
  172. package/prompts/gather-context-and-clarify.md +14 -0
  173. package/prompts/parallel-cleanup.md +62 -0
  174. package/prompts/parallel-context-build.md +56 -0
  175. package/prompts/parallel-handoff-plan.md +62 -0
  176. package/prompts/parallel-research.md +51 -0
  177. package/prompts/parallel-review.md +59 -0
  178. package/prompts/review-loop.md +42 -0
  179. package/scripts/build.mjs +134 -0
  180. package/scripts/check-compat.mjs +48 -0
  181. package/scripts/compat-host.mjs +19 -0
  182. package/scripts/intercom-bridge-credentials.mjs +143 -0
  183. package/scripts/intercom-remote.mjs +103 -0
  184. package/scripts/linux-smoke.sh +51 -0
  185. package/scripts/local-install-smoke.mjs +66 -0
  186. package/scripts/native-package-smoke.mjs +82 -0
  187. package/scripts/package-smoke.mjs +209 -0
  188. package/scripts/prepare.mjs +67 -0
  189. package/scripts/real-pi-smoke.mjs +415 -0
  190. package/scripts/run-tests.mjs +149 -0
  191. package/skills/pi-intercom/SKILL.md +113 -0
  192. package/skills/pi-intercom/evals/evals.json +29 -0
  193. package/skills/pi-intercom/evals/trigger-evals.json +47 -0
  194. package/skills/pi-intercom/references/peer-sessions.md +69 -0
  195. package/skills/pi-subagents/SKILL.md +76 -0
  196. package/skills/pi-subagents/evals/evals.json +29 -0
  197. package/skills/pi-subagents/evals/trigger-evals.json +42 -0
  198. package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
  199. package/src/agents/agent-management.ts +813 -0
  200. package/src/agents/agent-scope.ts +7 -0
  201. package/src/agents/agent-selection.ts +23 -0
  202. package/src/agents/agent-serializer.ts +99 -0
  203. package/src/agents/agents.ts +916 -0
  204. package/src/agents/chain-serializer.ts +329 -0
  205. package/src/agents/frontmatter.ts +21 -0
  206. package/src/agents/identity.ts +30 -0
  207. package/src/agents/skills.ts +637 -0
  208. package/src/extension/build-info.ts +2 -0
  209. package/src/extension/compact-tools.ts +114 -0
  210. package/src/extension/config.ts +23 -0
  211. package/src/extension/control-notices.ts +47 -0
  212. package/src/extension/doctor.ts +171 -0
  213. package/src/extension/fanout-child.ts +312 -0
  214. package/src/extension/index.ts +649 -0
  215. package/src/extension/schemas.ts +444 -0
  216. package/src/extension/tool-input.ts +19 -0
  217. package/src/extension/tool-result.ts +16 -0
  218. package/src/history/canonical-result.ts +62 -0
  219. package/src/history/database.ts +59 -0
  220. package/src/history/index.ts +156 -0
  221. package/src/history/ingest.ts +349 -0
  222. package/src/history/queries.ts +241 -0
  223. package/src/history/store.ts +203 -0
  224. package/src/history/types.ts +166 -0
  225. package/src/history/worker.ts +321 -0
  226. package/src/intercom/intercom-bridge.ts +123 -0
  227. package/src/intercom/live-intercom.ts +104 -0
  228. package/src/intercom/result-intercom.ts +429 -0
  229. package/src/pi-intercom/agent-dir.ts +9 -0
  230. package/src/pi-intercom/bridge.ts +668 -0
  231. package/src/pi-intercom/broker/broker.ts +463 -0
  232. package/src/pi-intercom/broker/client.ts +558 -0
  233. package/src/pi-intercom/broker/framing.ts +77 -0
  234. package/src/pi-intercom/broker/paths.ts +47 -0
  235. package/src/pi-intercom/broker/pid.ts +59 -0
  236. package/src/pi-intercom/broker/spawn.ts +248 -0
  237. package/src/pi-intercom/config.ts +142 -0
  238. package/src/pi-intercom/index.ts +2929 -0
  239. package/src/pi-intercom/reply-tracker.ts +211 -0
  240. package/src/pi-intercom/session-targets.ts +172 -0
  241. package/src/pi-intercom/subagent-live-events.ts +133 -0
  242. package/src/pi-intercom/topics.ts +125 -0
  243. package/src/pi-intercom/types.ts +271 -0
  244. package/src/pi-intercom/ui/compose.ts +289 -0
  245. package/src/pi-intercom/ui/inline-message.ts +92 -0
  246. package/src/pi-intercom/ui/session-list.ts +139 -0
  247. package/src/runs/background/async-control.ts +89 -0
  248. package/src/runs/background/async-execution.ts +850 -0
  249. package/src/runs/background/async-job-tracker.ts +385 -0
  250. package/src/runs/background/async-result-file.ts +62 -0
  251. package/src/runs/background/async-resume.ts +333 -0
  252. package/src/runs/background/async-status.ts +481 -0
  253. package/src/runs/background/completion-dedupe.ts +79 -0
  254. package/src/runs/background/completion-delivery.ts +195 -0
  255. package/src/runs/background/notify.ts +123 -0
  256. package/src/runs/background/parallel-groups.ts +53 -0
  257. package/src/runs/background/result-watcher.ts +423 -0
  258. package/src/runs/background/run-id-resolver.ts +69 -0
  259. package/src/runs/background/run-status.ts +384 -0
  260. package/src/runs/background/stale-run-reconciler.ts +369 -0
  261. package/src/runs/background/subagent-runner-launcher.ts +56 -0
  262. package/src/runs/background/subagent-runner.ts +2053 -0
  263. package/src/runs/background/top-level-async.ts +13 -0
  264. package/src/runs/foreground/chain-clarify.ts +1340 -0
  265. package/src/runs/foreground/clarify-invocation.ts +77 -0
  266. package/src/runs/foreground/execution-input.ts +467 -0
  267. package/src/runs/foreground/foreground-control.ts +821 -0
  268. package/src/runs/foreground/question-control.ts +82 -0
  269. package/src/runs/foreground/run-async-path.ts +243 -0
  270. package/src/runs/foreground/subagent-executor.ts +501 -0
  271. package/src/runs/foreground/subagent-params.ts +320 -0
  272. package/src/runs/foreground/wait-run.ts +124 -0
  273. package/src/runs/shared/acceptance-contract.ts +257 -0
  274. package/src/runs/shared/acceptance-evaluation.ts +275 -0
  275. package/src/runs/shared/acceptance-finalization.ts +353 -0
  276. package/src/runs/shared/acceptance-reports.ts +152 -0
  277. package/src/runs/shared/acceptance.ts +33 -0
  278. package/src/runs/shared/chain-outputs.ts +114 -0
  279. package/src/runs/shared/child-attempt.ts +623 -0
  280. package/src/runs/shared/child-execution-cwd.ts +91 -0
  281. package/src/runs/shared/child-observations.ts +99 -0
  282. package/src/runs/shared/claude-code.ts +243 -0
  283. package/src/runs/shared/completion-guard.ts +35 -0
  284. package/src/runs/shared/dynamic-fanout.ts +295 -0
  285. package/src/runs/shared/history-index.ts +69 -0
  286. package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
  287. package/src/runs/shared/model-fallback.ts +219 -0
  288. package/src/runs/shared/mutating-tool-guard.ts +194 -0
  289. package/src/runs/shared/native-finalization.ts +128 -0
  290. package/src/runs/shared/native-usage.ts +113 -0
  291. package/src/runs/shared/nested-events.ts +752 -0
  292. package/src/runs/shared/nested-path.ts +52 -0
  293. package/src/runs/shared/nested-render.ts +119 -0
  294. package/src/runs/shared/parallel-utils.ts +153 -0
  295. package/src/runs/shared/parent-receipts.ts +59 -0
  296. package/src/runs/shared/parent-usage.ts +121 -0
  297. package/src/runs/shared/pi-args.ts +350 -0
  298. package/src/runs/shared/pi-spawn.ts +52 -0
  299. package/src/runs/shared/run-history-queries.ts +44 -0
  300. package/src/runs/shared/run-history.ts +100 -0
  301. package/src/runs/shared/run-id-ambiguity.ts +4 -0
  302. package/src/runs/shared/run-records.ts +607 -0
  303. package/src/runs/shared/session-cwd-preload.ts +27 -0
  304. package/src/runs/shared/single-output.ts +267 -0
  305. package/src/runs/shared/streaming-text.ts +18 -0
  306. package/src/runs/shared/structured-output.ts +68 -0
  307. package/src/runs/shared/subagent-control.ts +219 -0
  308. package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
  309. package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
  310. package/src/runs/shared/supervisor-questions.ts +415 -0
  311. package/src/runs/shared/workflow-graph.ts +220 -0
  312. package/src/runs/shared/workflow-policy.ts +125 -0
  313. package/src/runs/shared/worktree.ts +635 -0
  314. package/src/shared/agent-context-policy.ts +184 -0
  315. package/src/shared/artifacts.ts +95 -0
  316. package/src/shared/atomic-json.ts +16 -0
  317. package/src/shared/cooperative.ts +21 -0
  318. package/src/shared/execution-cwd.ts +14 -0
  319. package/src/shared/file-coalescer.ts +29 -0
  320. package/src/shared/fork-context.ts +68 -0
  321. package/src/shared/formatters.ts +84 -0
  322. package/src/shared/journal-reader.ts +333 -0
  323. package/src/shared/lazy-tools.ts +36 -0
  324. package/src/shared/model-info.ts +81 -0
  325. package/src/shared/native-session.ts +8 -0
  326. package/src/shared/native-tui.ts +10 -0
  327. package/src/shared/native-typebox.ts +14 -0
  328. package/src/shared/post-exit-stdio-guard.ts +204 -0
  329. package/src/shared/prompt-sections.ts +10 -0
  330. package/src/shared/session-entries.ts +49 -0
  331. package/src/shared/session-identity.ts +18 -0
  332. package/src/shared/settings.ts +384 -0
  333. package/src/shared/status-format.ts +123 -0
  334. package/src/shared/temp-root.ts +107 -0
  335. package/src/shared/types.ts +1260 -0
  336. package/src/shared/ui-mode.ts +5 -0
  337. package/src/shared/utils.ts +342 -0
  338. package/src/slash/prompt-template-bridge.ts +408 -0
  339. package/src/slash/slash-bridge.ts +178 -0
  340. package/src/slash/slash-commands.ts +617 -0
  341. package/src/slash/slash-live-state.ts +292 -0
  342. package/src/tui/action-hints.ts +69 -0
  343. package/src/tui/agent-history.ts +210 -0
  344. package/src/tui/agent-view.ts +1323 -0
  345. package/src/tui/render.ts +1550 -0
  346. package/tsconfig.build.json +12 -0
  347. package/tsconfig.json +19 -0
@@ -0,0 +1,444 @@
1
+ /**
2
+ * TypeBox schemas for subagent tool parameters
3
+ */
4
+
5
+ import { Type } from "../shared/native-typebox.ts";
6
+ import { SUBAGENT_ACTIONS } from "../shared/types.ts";
7
+
8
+ const requiredObject = (...required: string[]) => ({ type: "object" as const, required });
9
+
10
+ const SkillOverride = Type.Unsafe({
11
+ anyOf: [
12
+ { type: "array", items: { type: "string", minLength: 1 } },
13
+ { type: "boolean" },
14
+ { type: "string" },
15
+ ],
16
+ description: "Skill name(s): string, comma-separated string, array, or false to disable",
17
+ });
18
+
19
+ const OutputOverride = Type.Unsafe({
20
+ anyOf: [
21
+ { type: "string", minLength: 1 },
22
+ { type: "boolean" },
23
+ ],
24
+ description: "Output file path, or false to disable file output.",
25
+ });
26
+
27
+ const OutputModeOverride = Type.Enum(["inline", "file-only"] as const, {
28
+ type: "string",
29
+ description: "inline (default) or file-only; file-only requires output to be a path.",
30
+ });
31
+
32
+ const ReadsOverride = Type.Unsafe({
33
+ anyOf: [
34
+ { type: "array", items: { type: "string", minLength: 1 } },
35
+ { type: "boolean" },
36
+ ],
37
+ description: "Files to read before running, or false to disable",
38
+ });
39
+
40
+ const MaxOutputOverride = Type.Object({
41
+ bytes: Type.Optional(Type.Integer({ minimum: 1, description: "Max output bytes before truncation." })),
42
+ lines: Type.Optional(Type.Integer({ minimum: 1, description: "Max output lines before truncation." })),
43
+ }, {
44
+ additionalProperties: false,
45
+ description: "Final output truncation limits. Defaults: 200KB, 5000 lines.",
46
+ });
47
+
48
+ const JsonSchemaObject = Type.Unsafe({
49
+ type: "object",
50
+ additionalProperties: true,
51
+ description: "JSON Schema (object root) for strict structured output.",
52
+ });
53
+
54
+ const AcceptanceEvidenceKind = Type.Enum([
55
+ "changed-files",
56
+ "tests-added",
57
+ "commands-run",
58
+ "validation-output",
59
+ "residual-risks",
60
+ "no-staged-files",
61
+ "diff-summary",
62
+ "review-findings",
63
+ "manual-notes",
64
+ ] as const, { type: "string" });
65
+
66
+ const AcceptanceGateSchema = Type.Object({
67
+ id: Type.String({ minLength: 1 }),
68
+ must: Type.String({ minLength: 1 }),
69
+ evidence: Type.Optional(Type.Array(AcceptanceEvidenceKind)),
70
+ severity: Type.Optional(Type.Enum(["required", "recommended"] as const, { type: "string" })),
71
+ }, { additionalProperties: false });
72
+
73
+ const AcceptanceVerifyCommandSchema = Type.Object({
74
+ id: Type.String({ minLength: 1 }),
75
+ command: Type.String({ minLength: 1 }),
76
+ timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
77
+ cwd: Type.Optional(Type.String()),
78
+ env: Type.Optional(Type.Unsafe({ type: "object", additionalProperties: { type: "string" } })),
79
+ allowFailure: Type.Optional(Type.Boolean()),
80
+ }, { additionalProperties: false });
81
+
82
+ export const AcceptanceOverride = Type.Unsafe({
83
+ type: "object",
84
+ properties: {
85
+ criteria: {
86
+ type: "array",
87
+ items: {
88
+ anyOf: [
89
+ { type: "string", minLength: 1 },
90
+ AcceptanceGateSchema,
91
+ ],
92
+ },
93
+ },
94
+ evidence: { type: "array", items: AcceptanceEvidenceKind },
95
+ verify: { type: "array", items: AcceptanceVerifyCommandSchema },
96
+ stopRules: { type: "array", items: { type: "string", minLength: 1 } },
97
+ maxFinalizationTurns: { type: "integer", minimum: 1, maximum: 10 },
98
+ },
99
+ additionalProperties: false,
100
+ description: "Optional acceptance contract. criteria=definition of done, evidence/verify=proof, stopRules=constraints, maxFinalizationTurns=self-review budget; at least one required. no-staged-files requires the entire Git index to be empty, including pre-existing staged paths. Continue/resume/answer overrides apply only to newly started continuations, never to a live child's acceptance. See the pi-subagents skill.",
101
+ });
102
+
103
+ // The everyday tools use closed acceptance shapes. Advanced callers retain the full contract below.
104
+ export const DelegateAcceptance = Type.Object({
105
+ criteria: Type.Optional(Type.Array(AcceptanceGateSchema)),
106
+ evidence: Type.Optional(Type.Array(AcceptanceEvidenceKind)),
107
+ verify: Type.Optional(Type.Array(Type.Object({
108
+ ...AcceptanceVerifyCommandSchema.properties,
109
+ env: Type.Optional(Type.Array(Type.Object({ name: Type.String({ minLength: 1 }), value: Type.String() }, { additionalProperties: false }))),
110
+ }, { additionalProperties: false }))),
111
+ stopRules: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
112
+ maxFinalizationTurns: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })),
113
+ }, { additionalProperties: false, description: "Acceptance criteria, evidence, verification commands and stop rules. Criteria use objects; verification environment uses unique name/value pairs. Self-review budget: 1–10 turns." });
114
+
115
+ const ModelOverride = Type.Optional(Type.String({
116
+ description: 'Profiles are defaults; warranted overrides are allowed subject to user instructions and provider authorization. Use model: "provider/model:high" to pin route/effort (no profile fallbacks; same-choice transport retries remain). Omit for profile fallbacks. No standalone thinking argument.',
117
+ }));
118
+
119
+ const TaskItem = Type.Object({
120
+ agent: Type.String({ minLength: 1 }),
121
+ task: Type.String({ minLength: 1 }),
122
+ label: Type.Optional(Type.String({ minLength: 1, description: "Short task label for the Agents strip and conversation." })),
123
+ cwd: Type.Optional(Type.String()),
124
+ count: Type.Optional(Type.Integer({ minimum: 1, description: "Repeat this parallel task N times." })),
125
+ outputSchema: Type.Optional(JsonSchemaObject),
126
+ output: Type.Optional(OutputOverride),
127
+ outputMode: Type.Optional(OutputModeOverride),
128
+ reads: Type.Optional(ReadsOverride),
129
+ progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking for this task" })),
130
+ model: ModelOverride,
131
+ skill: Type.Optional(SkillOverride),
132
+ acceptance: Type.Optional(AcceptanceOverride),
133
+ }, { additionalProperties: false });
134
+
135
+ // Parallel task item (within a parallel step)
136
+ const ParallelTaskSchema = Type.Object({
137
+ agent: Type.String({ minLength: 1 }),
138
+ task: Type.Optional(Type.String({ minLength: 1, description: "Task template with {task}, {previous}, {chain_dir} variables. Defaults to {previous}." })),
139
+ phase: Type.Optional(Type.String({ description: "Phase/group label for status and graph rendering." })),
140
+ label: Type.Optional(Type.String({ description: "User-facing label for this parallel task." })),
141
+ as: Type.Optional(Type.String({ description: "Safe identifier used as {outputs.name} in later chain steps." })),
142
+ outputSchema: Type.Optional(JsonSchemaObject),
143
+ cwd: Type.Optional(Type.String()),
144
+ count: Type.Optional(Type.Integer({ minimum: 1, description: "Repeat this parallel task N times." })),
145
+ output: Type.Optional(OutputOverride),
146
+ outputMode: Type.Optional(OutputModeOverride),
147
+ reads: Type.Optional(ReadsOverride),
148
+ progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
149
+ skill: Type.Optional(SkillOverride),
150
+ model: ModelOverride,
151
+ acceptance: Type.Optional(AcceptanceOverride),
152
+ }, { additionalProperties: false });
153
+
154
+ const DynamicExpandSchema = Type.Object({
155
+ from: Type.Object({
156
+ output: Type.String({ description: "Prior named structured output to expand from." }),
157
+ path: Type.String({ description: "JSON Pointer into the structured output, e.g. /items." }),
158
+ }, { additionalProperties: false }),
159
+ item: Type.Optional(Type.String({ description: "Template variable name for each item. Defaults to item." })),
160
+ key: Type.Optional(Type.String({ description: "JSON Pointer relative to each item for stable child ids." })),
161
+ maxItems: Type.Optional(Type.Integer({ minimum: 0, description: "Required fanout bound unless configured globally." })),
162
+ onEmpty: Type.Optional(Type.Enum(["skip", "fail"] as const, { type: "string", description: "Empty input behavior. Defaults to skip." })),
163
+ }, { additionalProperties: false });
164
+
165
+ const DynamicParallelTemplateSchema = Type.Object({
166
+ agent: Type.String({ minLength: 1 }),
167
+ task: Type.Optional(Type.String({ minLength: 1, description: "Task template with {item}, {item.path}, {task}, {previous}, {chain_dir}, {outputs.name} variables." })),
168
+ phase: Type.Optional(Type.String({ description: "Phase/group label for status and graph rendering." })),
169
+ label: Type.Optional(Type.String({ description: "User-facing label; item templates supported." })),
170
+ outputSchema: Type.Optional(JsonSchemaObject),
171
+ cwd: Type.Optional(Type.String()),
172
+ output: Type.Optional(OutputOverride),
173
+ outputMode: Type.Optional(OutputModeOverride),
174
+ reads: Type.Optional(ReadsOverride),
175
+ progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
176
+ skill: Type.Optional(SkillOverride),
177
+ model: ModelOverride,
178
+ acceptance: Type.Optional(AcceptanceOverride),
179
+ }, { additionalProperties: false });
180
+
181
+ const DynamicCollectSchema = Type.Object({
182
+ as: Type.String({ description: "Safe output name for the ordered collected result array." }),
183
+ outputSchema: Type.Optional(JsonSchemaObject),
184
+ }, { additionalProperties: false });
185
+
186
+ // Flattened so chain steps do not need an object-shape anyOf/oneOf union.
187
+ export const ChainItemSchema = Type.Object({
188
+ agent: Type.Optional(Type.String({ minLength: 1, description: "Sequential step agent name" })),
189
+ task: Type.Optional(Type.String({
190
+ minLength: 1,
191
+ description: "Task template: {task}=original request, {previous}=prior step response, {chain_dir}=shared folder, {outputs.name}=prior named output. Required for first step; defaults to '{previous}'."
192
+ })),
193
+ phase: Type.Optional(Type.String({ description: "Phase/group label for status and graph rendering." })),
194
+ label: Type.Optional(Type.String({ description: "User-facing label for this chain step." })),
195
+ as: Type.Optional(Type.String({ description: "Safe identifier used as {outputs.name} in later chain steps." })),
196
+ outputSchema: Type.Optional(JsonSchemaObject),
197
+ cwd: Type.Optional(Type.String()),
198
+ output: Type.Optional(OutputOverride),
199
+ outputMode: Type.Optional(OutputModeOverride),
200
+ reads: Type.Optional(ReadsOverride),
201
+ progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
202
+ skill: Type.Optional(SkillOverride),
203
+ model: ModelOverride,
204
+ acceptance: Type.Optional(AcceptanceOverride),
205
+ parallel: Type.Optional(Type.Unsafe({
206
+ anyOf: [
207
+ Type.Array(ParallelTaskSchema, { minItems: 1, description: "Tasks to run in parallel" }),
208
+ DynamicParallelTemplateSchema,
209
+ ],
210
+ description: "Static parallel tasks array, or a single dynamic fanout child template when expand/collect are present.",
211
+ })),
212
+ expand: Type.Optional(DynamicExpandSchema),
213
+ collect: Type.Optional(DynamicCollectSchema),
214
+ concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Max concurrent tasks (default: 4)" })),
215
+ failFast: Type.Optional(Type.Boolean({ description: "Stop on first failure (default: false)" })),
216
+ worktree: Type.Optional(Type.Boolean({
217
+ description: "Create isolated git worktrees for each parallel task."
218
+ })),
219
+ }, {
220
+ description: "Chain step: {agent, task?} sequential, {parallel: [...]} concurrent, or {expand, parallel: {...}, collect} dynamic fanout.",
221
+ additionalProperties: false,
222
+ allOf: [
223
+ { anyOf: [requiredObject("agent"), requiredObject("parallel")] },
224
+ { not: { anyOf: [
225
+ requiredObject("agent", "parallel"),
226
+ { ...requiredObject("expand"), properties: { parallel: { type: "array", items: {} } } },
227
+ ] } },
228
+ { if: requiredObject("expand"), then: { ...requiredObject("parallel", "collect"), properties: { parallel: { type: "object" } } } },
229
+ { if: requiredObject("collect"), then: { ...requiredObject("expand", "parallel"), properties: { parallel: { type: "object" } } } },
230
+ { if: { ...requiredObject("parallel"), properties: { parallel: { type: "object" } } }, then: requiredObject("expand", "collect") },
231
+ { if: requiredObject("agent"), then: { not: { anyOf: [requiredObject("concurrency"), requiredObject("failFast"), requiredObject("worktree")] } } },
232
+ { if: { ...requiredObject("parallel"), properties: { parallel: { type: "array", items: {} } } }, then: { not: { anyOf: [
233
+ requiredObject("task"), requiredObject("phase"), requiredObject("label"), requiredObject("as"), requiredObject("outputSchema"),
234
+ requiredObject("output"), requiredObject("outputMode"), requiredObject("reads"), requiredObject("progress"),
235
+ requiredObject("skill"), requiredObject("model"), requiredObject("acceptance"), requiredObject("expand"), requiredObject("collect"),
236
+ ] } } },
237
+ { if: { ...requiredObject("parallel"), properties: { parallel: { type: "object" } } }, then: { not: { anyOf: [
238
+ requiredObject("task"), requiredObject("as"), requiredObject("outputSchema"), requiredObject("cwd"), requiredObject("output"),
239
+ requiredObject("outputMode"), requiredObject("reads"), requiredObject("progress"), requiredObject("skill"),
240
+ requiredObject("model"), requiredObject("acceptance"), requiredObject("worktree"),
241
+ ] } } },
242
+ ],
243
+ });
244
+
245
+ const ControlOverrides = Type.Object({
246
+ enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
247
+ needsAttentionAfterMs: Type.Optional(Type.Integer({ minimum: 1, description: "No-activity window (ms) before a run needs attention. Default 600000 (10 min)." })),
248
+ failedToolAttemptsBeforeAttention: Type.Optional(Type.Integer({ minimum: 1, description: "Consecutive mutating-tool failures before needs_attention (default: 3)" })),
249
+ notifyOn: Type.Optional(Type.Array(Type.Enum(["needs_attention"] as const, { type: "string" }), {
250
+ description: "Control event types that should notify the parent/orchestrator. Defaults to needs_attention.",
251
+ })),
252
+ notifyChannels: Type.Optional(Type.Array(Type.Enum(["event", "async", "intercom"] as const, { type: "string" }), {
253
+ description: "Notification channels to use when available. Defaults to event, async, and intercom.",
254
+ })),
255
+ }, { additionalProperties: false });
256
+
257
+ export const DelegateParams = Type.Object({
258
+ agent: TaskItem.properties.agent,
259
+ task: TaskItem.properties.task,
260
+ label: TaskItem.properties.label,
261
+ cwd: TaskItem.properties.cwd,
262
+ model: TaskItem.properties.model,
263
+ context: Type.Optional(Type.Enum(["fresh", "fork"] as const, { type: "string", description: "Override the profile's context policy." })),
264
+ async: Type.Optional(Type.Boolean({ description: "Background by default; false waits for the result." })),
265
+ worktree: Type.Optional(Type.Boolean({ description: "Isolate this writer in a Git worktree; return its patch. Requires a clean checkout." })),
266
+ output: TaskItem.properties.output,
267
+ acceptance: Type.Optional(DelegateAcceptance),
268
+ }, { additionalProperties: false });
269
+
270
+ const HistoryQueryFields = {
271
+ cursor: Type.Optional(Type.String({ minLength: 1, description: "Opaque page cursor returned by list/history/search; expires when the indexed snapshot changes." })),
272
+ sort: Type.Optional(Type.Enum(["attention", "newest", "oldest", "relevance"] as const, { type: "string", description: "List: attention (default), newest, oldest. Search: relevance (default), newest." })),
273
+ state: Type.Optional(Type.Enum(["live", "completed", "failed", "blocked", "paused", "unknown"] as const, { type: "string", description: "List only: execution-state filter, applied before paging." })),
274
+ text: Type.Optional(Type.String({ minLength: 1, description: "List only: filter saved task/assignment text before paging." })),
275
+ query: Type.Optional(Type.String({ minLength: 1, description: 'Search visible saved text using 1–12 lexical words (all must match) or one double-quoted phrase. Operators, punctuation and prefixes are rejected. Thinking, images, tool arguments and hidden data are excluded.' })),
276
+ before: Type.Optional(Type.Integer({ minimum: 1, description: "History only: exclusive native-entry position returned by the earlier page; omit for the latest page." })),
277
+ };
278
+
279
+ export const AgentRunsValidationParams = Type.Object({
280
+ action: Type.Enum(["list", "inspect", "history", "search", "nudge", "stop", "continue", "profiles", "questions", "answer", "review"] as const, { type: "string" }),
281
+ id: Type.Optional(Type.String({ minLength: 1, description: "Run ID or unambiguous prefix." })),
282
+ questionId: Type.Optional(Type.String({ minLength: 1, description: "Durable supervisor question ID for exact questions lookup or answer." })),
283
+ async: Type.Optional(Type.Boolean({ description: "Continue/answer: false waits for the actual continuation result." })),
284
+ index: Type.Optional(Type.Integer({ minimum: 0, description: "Child index for a multi-child run." })),
285
+ message: Type.Optional(Type.String({ minLength: 1, description: "Guidance, follow-up, answer, or optional parent-only review note. Review notes are not sent to the child; put actionable instructions in continue/nudge. Only continue/answer can start a saved child." })),
286
+ decision: Type.Optional(Type.Enum(["accepted", "needs_changes"] as const, { type: "string", description: "Parent-only review outcome; not sent to the child. Separate from execution and runtime acceptance checks." })),
287
+ offset: Type.Optional(Type.Integer({ minimum: 0, description: "List/questions offset; history is retained regardless of page size." })),
288
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, description: "Results per list/questions/search/history page (default 20; history 100)." })),
289
+ ...HistoryQueryFields,
290
+ full: Type.Optional(Type.Boolean({ description: "Inspect only: include the full task and saved launch configuration. Default is a concise report; stored data is unchanged." })),
291
+ agent: Type.Optional(Type.String({ minLength: 1, description: "List/search: filter by child agent. Continue/answer: adopt this current profile, including model, thinking and fallbacks; a separate model override wins. Otherwise keep saved settings. Required for old runs without a saved profile." })),
292
+ model: TaskItem.properties.model,
293
+ cwd: TaskItem.properties.cwd,
294
+ output: TaskItem.properties.output,
295
+ acceptance: TaskItem.properties.acceptance,
296
+ }, {
297
+ additionalProperties: false,
298
+ allOf: [
299
+ { if: { properties: { action: { enum: ["inspect", "history", "nudge", "stop", "continue", "answer", "review"] } } }, then: requiredObject("id") },
300
+ { if: { properties: { action: { enum: ["search"] } } }, then: requiredObject("query") },
301
+ { if: { properties: { action: { enum: ["nudge", "continue", "answer"] } } }, then: requiredObject("message") },
302
+ { if: { properties: { action: { enum: ["answer"] } } }, then: requiredObject("questionId") },
303
+ { if: { properties: { action: { enum: ["review"] } } }, then: requiredObject("decision") },
304
+ { if: { anyOf: [requiredObject("async"), requiredObject("model"), requiredObject("cwd"), requiredObject("output"), requiredObject("acceptance")] }, then: { properties: { action: { enum: ["continue", "answer"] } } } },
305
+ { if: requiredObject("agent"), then: { properties: { action: { enum: ["list", "search", "continue", "answer"] } } } },
306
+ { if: requiredObject("offset"), then: { properties: { action: { enum: ["list", "questions"] } } } },
307
+ { if: requiredObject("limit"), then: { properties: { action: { enum: ["list", "questions", "history", "search"] } } } },
308
+ { if: requiredObject("cursor"), then: { properties: { action: { enum: ["list", "history", "search"] } } } },
309
+ { if: requiredObject("sort"), then: { properties: { action: { enum: ["list", "search"] } } } },
310
+ { not: { anyOf: [requiredObject("cursor", "offset"), requiredObject("cursor", "before")] } },
311
+ { if: { allOf: [{ properties: { action: { enum: ["search"] } } }, requiredObject("index")] }, then: requiredObject("id") },
312
+ { if: { anyOf: [requiredObject("state"), requiredObject("text")] }, then: { properties: { action: { enum: ["list"] } } } },
313
+ { if: requiredObject("query"), then: { properties: { action: { enum: ["search"] } } } },
314
+ { if: requiredObject("before"), then: { properties: { action: { enum: ["history"] } } } },
315
+ { if: { properties: { action: { enum: ["list"] } } }, then: { properties: { sort: { enum: ["attention", "newest", "oldest"] } } } },
316
+ { if: { properties: { action: { enum: ["search"] } } }, then: { properties: { sort: { enum: ["relevance", "newest"] } } } },
317
+ { if: requiredObject("decision"), then: { properties: { action: { enum: ["review"] } } } },
318
+ { if: requiredObject("full"), then: { properties: { action: { enum: ["inspect"] } } } },
319
+ ],
320
+ });
321
+
322
+ export const AgentRunsParams = Type.Object({
323
+ ...AgentRunsValidationParams.properties,
324
+ acceptance: Type.Optional(DelegateAcceptance),
325
+ }, { additionalProperties: false });
326
+
327
+ export const SubagentParams = Type.Object({
328
+ agent: Type.Optional(Type.String({ minLength: 1, description: "Agent name; on resume/answer, adopt the current profile including model, thinking and fallbacks; a separate model override wins. Otherwise keep saved settings." })),
329
+ task: Type.Optional(Type.String({ minLength: 1, description: "Task (SINGLE mode, optional for self-contained agents)" })),
330
+ label: TaskItem.properties.label,
331
+ // Management action (when present, tool operates in management mode)
332
+ action: Type.Optional(Type.Enum([...SUBAGENT_ACTIONS] as const, {
333
+ type: "string",
334
+ description: "Management/control action. Omit for execution mode. nudge sends a live intercom nudge to a running child."
335
+ })),
336
+ id: Type.Optional(Type.String({
337
+ description: "Run id or prefix for status/interrupt/extend/resume/nudge/questions/answer/review actions."
338
+ })),
339
+ runId: Type.Optional(Type.String({
340
+ description: "Target run ID; prefer id. Defaults to the most recently active controllable run for interrupt/extend/nudge."
341
+ })),
342
+ questionId: AgentRunsValidationParams.properties.questionId,
343
+ decision: Type.Optional(Type.Enum(["accepted", "needs_changes"] as const, { type: "string", description: "Parent-only review decision; not sent to the child. Put actionable instructions in resume/nudge. Does not launch work or change runtime acceptance." })),
344
+ offset: Type.Optional(Type.Integer({ minimum: 0, description: "Status list/questions offset." })),
345
+ limit: AgentRunsValidationParams.properties.limit,
346
+ ...HistoryQueryFields,
347
+ full: Type.Optional(Type.Boolean({ description: "Exact status only: include the full task and saved launch configuration; default is concise." })),
348
+ dir: Type.Optional(Type.String({
349
+ description: "Async run directory for status/resume."
350
+ })),
351
+ index: Type.Optional(Type.Integer({ minimum: 0, description: "Zero-based child index for actions that target a specific child." })),
352
+ message: Type.Optional(Type.String({ description: "Follow-up for resume, nudge text, answer, or parent-only review note. Review notes are not sent to the child; put actionable instructions in resume/nudge. Use index for multi-child runs." })),
353
+ extendMs: Type.Optional(Type.Integer({ minimum: 1, description: "Additional ms for extend; defaults to timeoutMs/maxRuntimeMs." })),
354
+ // Chain identifier for management (can't reuse 'chain' — that's the execution array)
355
+ chainName: Type.Optional(Type.String({
356
+ description: "Chain name for get/update/delete management actions"
357
+ })),
358
+ // Agent/chain configuration for create/update (nested to avoid conflicts with execution fields)
359
+ config: Type.Optional(Type.Unsafe({
360
+ anyOf: [
361
+ { type: "object", additionalProperties: true },
362
+ { type: "string" },
363
+ ],
364
+ description: "Agent or chain config for create/update (object or JSON string). Agent keys: name, package, description, scope ('user'|'project'), systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, defaultContext, model, tools, allowSubagents, extensions, skills, thinking, output, reads, progress, maxSubagentDepth, maxExecutionTimeMs, maxTokens. Chain keys: name, package, description, scope, steps (array of {agent, task?, output?, outputMode?, reads?, model?, skills?, progress?}). Presence of 'steps' creates a chain."
365
+ })),
366
+ tasks: Type.Optional(Type.Array(TaskItem, { minItems: 1, description: "PARALLEL mode: concurrent [{agent, task, ...}] tasks." })),
367
+ concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "PARALLEL mode: max concurrent parallel tasks (default 4)." })),
368
+ timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Foreground wall-clock timeout (ms); on expiry children are soft-interrupted. When async is omitted, a timeout implies foreground execution; explicit async runs reject it. Short reviewer budgets are raised to a floor; planner/researcher budgets only from run-history data." })),
369
+ maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias for timeoutMs; same foreground-only policy." })),
370
+ maxOutput: Type.Optional(MaxOutputOverride),
371
+ worktree: Type.Optional(Type.Boolean({
372
+ description: "Isolated git worktrees per parallel task; requires clean git state; per-worktree diffs included."
373
+ })),
374
+ chain: Type.Optional(Type.Array(ChainItemSchema, { minItems: 1, description: "CHAIN mode: sequential pipeline; each step's response becomes {previous} for the next." })),
375
+ context: Type.Optional(Type.Enum(["fresh", "fork"] as const, {
376
+ type: "string",
377
+ description: "'fresh' or 'fork' (branch from parent session); overrides each agent's defaultContext. Fork is rejected for agents whose effective model uses the anthropic/ provider.",
378
+ })),
379
+ chainDir: Type.Optional(Type.String({ description: "Directory for chain artifacts (default: temp, auto-cleaned after 24h)" })),
380
+ async: Type.Optional(Type.Boolean({ description: "Run in background. Stock top-level default: true; set false for foreground execution." })),
381
+ agentScope: Type.Optional(Type.Enum(["user", "project", "both"] as const, { type: "string", description: "Agent discovery scope: 'user', 'project', or 'both' (default; project wins collisions)" })),
382
+ cwd: Type.Optional(Type.String()),
383
+ artifacts: Type.Optional(Type.Boolean({ description: "Write debug artifacts (default: true)" })),
384
+ includeProgress: Type.Optional(Type.Boolean({ description: "Include full progress in result (default: false)" })),
385
+ progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking for a single agent run" })),
386
+ share: Type.Optional(Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)" })),
387
+ sessionDir: Type.Optional(
388
+ Type.String({ description: "Directory for session logs (default: temp)" }),
389
+ ),
390
+ // Clarification TUI
391
+ clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution; explicit true forces foreground." })),
392
+ control: Type.Optional(ControlOverrides),
393
+ // Solo agent overrides
394
+ output: Type.Optional(Type.Unsafe({
395
+ anyOf: [
396
+ { type: "string" },
397
+ { type: "boolean" },
398
+ ],
399
+ description: "Output file for single agent, or false to disable. Relative paths resolve against cwd.",
400
+ })),
401
+ outputMode: Type.Optional(OutputModeOverride),
402
+ skill: Type.Optional(SkillOverride),
403
+ model: ModelOverride,
404
+ outputSchema: Type.Optional(JsonSchemaObject),
405
+ acceptance: Type.Optional(AcceptanceOverride),
406
+ }, {
407
+ additionalProperties: false,
408
+ allOf: [
409
+ { if: { ...requiredObject("action"), properties: { action: { enum: ["answer"] } } }, then: { allOf: [requiredObject("questionId", "message"), { anyOf: [requiredObject("id"), requiredObject("runId")] }] } },
410
+ { if: { ...requiredObject("action"), properties: { action: { enum: ["review"] } } }, then: { allOf: [requiredObject("decision"), { anyOf: [requiredObject("id"), requiredObject("runId")] }] } },
411
+ { not: { anyOf: [
412
+ requiredObject("agent", "tasks"),
413
+ requiredObject("agent", "chain"),
414
+ requiredObject("tasks", "chain"),
415
+ requiredObject("cursor", "offset"),
416
+ requiredObject("cursor", "before"),
417
+ ] } },
418
+ { if: requiredObject("decision"), then: { ...requiredObject("action"), properties: { action: { enum: ["review"] } } } },
419
+ { if: { ...requiredObject("action"), properties: { action: { enum: ["history"] } } }, then: { anyOf: [requiredObject("id"), requiredObject("runId")] } },
420
+ { if: { ...requiredObject("action"), properties: { action: { enum: ["search"] } } }, then: requiredObject("query") },
421
+ { if: requiredObject("offset"), then: { ...requiredObject("action"), properties: { action: { enum: ["status", "questions"] } } } },
422
+ { if: requiredObject("limit"), then: { ...requiredObject("action"), properties: { action: { enum: ["status", "questions", "history", "search"] } } } },
423
+ { if: requiredObject("cursor"), then: { ...requiredObject("action"), properties: { action: { enum: ["status", "history", "search"] } } } },
424
+ { if: requiredObject("sort"), then: { ...requiredObject("action"), properties: { action: { enum: ["status", "search"] } } } },
425
+ { if: { allOf: [{ ...requiredObject("action"), properties: { action: { enum: ["search"] } } }, requiredObject("index")] }, then: { anyOf: [requiredObject("id"), requiredObject("runId")] } },
426
+ { if: { anyOf: [requiredObject("state"), requiredObject("text")] }, then: { ...requiredObject("action"), properties: { action: { enum: ["status"] } } } },
427
+ { if: requiredObject("query"), then: { ...requiredObject("action"), properties: { action: { enum: ["search"] } } } },
428
+ { if: requiredObject("before"), then: { ...requiredObject("action"), properties: { action: { enum: ["history"] } } } },
429
+ { if: { ...requiredObject("action"), properties: { action: { enum: ["status"] } } }, then: { properties: { sort: { enum: ["attention", "newest", "oldest"] } } } },
430
+ { if: { ...requiredObject("action"), properties: { action: { enum: ["search"] } } }, then: { properties: { sort: { enum: ["relevance", "newest"] } } } },
431
+ { if: requiredObject("full"), then: { allOf: [{ ...requiredObject("action"), properties: { action: { enum: ["status"] } } }, { anyOf: [requiredObject("id"), requiredObject("runId"), requiredObject("dir")] }] } },
432
+ { if: requiredObject("worktree"), then: requiredObject("tasks") },
433
+ { if: requiredObject("concurrency"), then: requiredObject("tasks") },
434
+ { if: requiredObject("chainDir"), then: requiredObject("chain") },
435
+ { if: { anyOf: [
436
+ requiredObject("output"), requiredObject("outputMode"), requiredObject("skill"), requiredObject("model"),
437
+ requiredObject("outputSchema"), requiredObject("progress"),
438
+ ] }, then: { anyOf: [requiredObject("agent"), { ...requiredObject("action"), properties: { action: { enum: ["resume", "answer"] } } }] } },
439
+ { if: requiredObject("acceptance"), then: { anyOf: [
440
+ requiredObject("agent"),
441
+ { ...requiredObject("action"), properties: { action: { enum: ["resume", "answer"] } } },
442
+ ] } },
443
+ ],
444
+ });
@@ -0,0 +1,19 @@
1
+ import { Compile } from "../shared/native-typebox.ts";
2
+ import { AgentRunsValidationParams } from "./schemas.ts";
3
+
4
+ const runArguments = Compile(AgentRunsValidationParams);
5
+
6
+ export function normalizeEverydayParams(params: Record<string, unknown>, control = false): Record<string, unknown> {
7
+ const acceptance = params.acceptance as { verify?: Array<{ env?: Array<{ name: string; value: string }> }> } | undefined;
8
+ const normalized = acceptance?.verify ? {
9
+ ...params,
10
+ acceptance: { ...acceptance, verify: acceptance.verify.map((verify) => {
11
+ if (!Array.isArray(verify.env)) return verify;
12
+ const names = verify.env.map(({ name }) => name);
13
+ if (new Set(names).size !== names.length) throw new Error("Verification environment contains duplicate names.");
14
+ return { ...verify, env: Object.fromEntries(verify.env.map(({ name, value }) => [name, value])) };
15
+ }) },
16
+ } : params;
17
+ if (control && !runArguments.Check(normalized)) throw new Error(`Invalid agent_runs arguments: ${[...runArguments.Errors(normalized)].map((error) => error.message).join("; ")}`);
18
+ return normalized;
19
+ }
@@ -0,0 +1,16 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import type { Details, SubagentExecutionResult } from "../shared/types.ts";
3
+
4
+ export function registerToolResultAdapter(pi: ExtensionAPI, toolNames: readonly string[]) {
5
+ pi.on("tool_result", (event) => {
6
+ const details = event.details as (Details & { isError?: boolean }) | undefined;
7
+ if (!toolNames.includes(event.toolName) || details?.isError !== true) return;
8
+ const { isError, ...originalDetails } = details;
9
+ return { isError, details: originalDetails };
10
+ });
11
+ // Native execute ignores returned isError; throwing discards content/details.
12
+ // Carry the flag to its native result hook, which restores the original details.
13
+ return (result: SubagentExecutionResult) => result.isError
14
+ ? { ...result, details: { ...result.details, isError: true } }
15
+ : result;
16
+ }
@@ -0,0 +1,62 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { JsonProjection, readJsonProjection } from "../shared/journal-reader.ts";
4
+ import { HistoryIndexError } from "./types.ts";
5
+
6
+ const budget = 16 * 1024 * 1024;
7
+ export function readSavedOutput(file: string): string {
8
+ const fd = fs.openSync(file, "r");
9
+ try {
10
+ const before = fs.fstatSync(fd, { bigint: true });
11
+ if (!before.isFile() || before.size > BigInt(budget)) throw new HistoryIndexError("RECORD_TOO_LARGE", "Selected saved output exceeds the 16 MiB detail budget.");
12
+ const bytes = Buffer.alloc(Number(before.size));
13
+ for (let offset = 0; offset < bytes.length;) {
14
+ const count = fs.readSync(fd, bytes, offset, bytes.length - offset, offset);
15
+ if (!count) throw new HistoryIndexError("SOURCE_CHANGED", "Selected saved output was truncated.");
16
+ offset += count;
17
+ }
18
+ const after = fs.fstatSync(fd, { bigint: true }), current = fs.statSync(file, { bigint: true });
19
+ if (before.size !== after.size || before.ctimeNs !== after.ctimeNs || before.dev !== current.dev || before.ino !== current.ino) throw new HistoryIndexError("SOURCE_CHANGED", "Selected saved output changed; retry details.");
20
+ return new TextDecoder("utf8", { fatal: true }).decode(bytes);
21
+ } finally { fs.closeSync(fd); }
22
+ }
23
+
24
+ /** Explicit legacy/compact owner detail: retain only this child's output, never transcript arrays. */
25
+ export function readCanonicalOutput(file: string, index: number): string | undefined {
26
+ let prefix: Array<string | number>;
27
+ if (path.basename(file) === "foreground.json") {
28
+ const metadata = readJsonProjection(file, (keys) => !keys.length || keys[0] === "children" && (keys.length <= 2 || keys.length === 3 && keys[2] === "index"));
29
+ const position = metadata.children?.findIndex((child: { index: number } | null) => child?.index === index);
30
+ if (position === undefined || position < 0) return;
31
+ prefix = ["children", position, "result"];
32
+ } else prefix = path.basename(file) === "result.json" ? ["results", index] : ["result"];
33
+ let length = 0;
34
+ const projection = new JsonProjection((keys) => {
35
+ if (prefix[0] === "children" && keys.length === 3 && keys[0] === "children" && keys[1] === prefix[1] && keys[2] === "index") return true;
36
+ if (keys.length <= prefix.length) return keys.every((key, position) => key === prefix[position]);
37
+ return keys.length === prefix.length + 1 && prefix.every((key, position) => key === keys[position]) && ["finalOutput", "output"].includes(String(keys.at(-1))) ? budget : false;
38
+ }, (keys, text) => {
39
+ if (keys.length === prefix.length + 1 && prefix.every((key, position) => key === keys[position]) && ["finalOutput", "output"].includes(String(keys.at(-1)))) {
40
+ length += Buffer.byteLength(text);
41
+ if (length > budget) throw new HistoryIndexError("RECORD_TOO_LARGE", "Selected canonical output exceeds the 16 MiB detail budget.");
42
+ }
43
+ });
44
+ const fd = fs.openSync(file, "r"), decoder = new TextDecoder("utf8", { fatal: true });
45
+ try {
46
+ const before = fs.fstatSync(fd, { bigint: true }), bytes = Buffer.allocUnsafe(64 * 1024);
47
+ for (let offset = 0; offset < Number(before.size);) {
48
+ const count = fs.readSync(fd, bytes, 0, Math.min(bytes.length, Number(before.size) - offset), offset);
49
+ if (!count) throw new HistoryIndexError("SOURCE_CHANGED", "Selected canonical output was truncated.");
50
+ projection.write(decoder.decode(bytes.subarray(0, count), { stream: true })); offset += count;
51
+ }
52
+ projection.write(decoder.decode());
53
+ const value = projection.finish(), after = fs.fstatSync(fd, { bigint: true }), current = fs.statSync(file, { bigint: true });
54
+ if (prefix[0] === "children" && value?.children?.[prefix[1]]?.index !== index) throw new HistoryIndexError("SOURCE_CHANGED", "Selected canonical child changed; retry details.");
55
+ if (before.size !== after.size || before.ctimeNs !== after.ctimeNs || before.dev !== current.dev || before.ino !== current.ino) throw new HistoryIndexError("SOURCE_CHANGED", "Selected canonical output changed; retry details.");
56
+ let child: unknown = value;
57
+ for (const key of prefix) child = child && typeof child === "object" ? (child as Record<string | number, unknown>)[key] : undefined;
58
+ if (!child || typeof child !== "object") return;
59
+ const result = child as Record<string, unknown>;
60
+ return typeof result.finalOutput === "string" ? result.finalOutput : typeof result.output === "string" ? result.output : undefined;
61
+ } finally { fs.closeSync(fd); }
62
+ }
@@ -0,0 +1,59 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { DatabaseSync } from "node:sqlite";
4
+ import { HistoryIndexError } from "./types.ts";
5
+
6
+ export function historyDirectory(agentDir: string, create = true): string {
7
+ const directory = path.join(fs.realpathSync(agentDir), "history-index");
8
+ if (create) fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
9
+ if (fs.lstatSync(directory).isSymbolicLink()) throw new HistoryIndexError("UNSAFE_PATH", "History directory must not be a symbolic link.");
10
+ if (create) fs.chmodSync(directory, 0o700);
11
+ const filesystem = fs.statfsSync(directory).type;
12
+ if ([0x6969, 0x517b, 0xff534d42].includes(filesystem)) throw new HistoryIndexError("UNSAFE_PATH", "SQLite history requires a local filesystem.");
13
+ return directory;
14
+ }
15
+
16
+ export function openHistoryDatabase(file: string, readOnly = false): DatabaseSync {
17
+ if (fs.existsSync(file) && fs.lstatSync(file).isSymbolicLink()) throw new HistoryIndexError("UNSAFE_PATH", "History database must not be a symbolic link.");
18
+ for (let attempt = 0; ; attempt++) {
19
+ const db = new DatabaseSync(file, { timeout: 100, defensive: true, allowExtension: false, readOnly });
20
+ try {
21
+ const version = String(db.prepare("SELECT sqlite_version() AS version").get()!.version);
22
+ if (version !== "3.53.4") throw new HistoryIndexError("UNSUPPORTED_SQLITE", `History requires qualified SQLite 3.53.4; found ${version}.`);
23
+ db.exec("PRAGMA foreign_keys=ON; PRAGMA trusted_schema=OFF");
24
+ if (!readOnly) {
25
+ if (db.prepare("PRAGMA journal_mode").get()?.journal_mode !== "wal") {
26
+ // WAL conversion upgrades a read lock without waiting for an existing writer.
27
+ // Wait for that writer through native transaction admission before converting.
28
+ db.exec("BEGIN IMMEDIATE; COMMIT; PRAGMA journal_mode=WAL");
29
+ }
30
+ db.exec("PRAGMA synchronous=FULL");
31
+ for (const suffix of ["", "-wal", "-shm"]) {
32
+ try { fs.chmodSync(file + suffix, 0o600); }
33
+ catch (error) {
34
+ // SQLite can remove its sidecars when the last attached connection closes.
35
+ if (!suffix || !(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") throw error;
36
+ }
37
+ }
38
+ }
39
+ return db;
40
+ } catch (error) {
41
+ db.close();
42
+ if (readOnly || attempt >= 2 || !/locked|busy/i.test(String((error as Error).message))) throw error;
43
+ // Concurrent first-open WAL conversion can return BUSY without honoring the native timeout.
44
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
45
+ }
46
+ }
47
+ }
48
+
49
+ export function historyTransaction<T>(db: DatabaseSync, work: () => T): T {
50
+ for (let attempt = 0; ; attempt++) {
51
+ try {
52
+ db.exec("BEGIN IMMEDIATE");
53
+ const result = work(); db.exec("COMMIT"); return result;
54
+ } catch (error) {
55
+ if (db.isTransaction) db.exec("ROLLBACK");
56
+ if (attempt >= 2 || !/locked|busy/i.test(String((error as Error).message))) throw error;
57
+ }
58
+ }
59
+ }