@selesai/code 0.3.0 → 0.3.1

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 (230) hide show
  1. package/dist/core/diagnostics.d.ts +1 -0
  2. package/dist/core/diagnostics.d.ts.map +1 -1
  3. package/dist/core/diagnostics.js.map +1 -1
  4. package/dist/core/package-manager.d.ts +10 -0
  5. package/dist/core/package-manager.d.ts.map +1 -1
  6. package/dist/core/package-manager.js +93 -8
  7. package/dist/core/package-manager.js.map +1 -1
  8. package/dist/core/package-manager.test.d.ts +2 -0
  9. package/dist/core/package-manager.test.d.ts.map +1 -0
  10. package/dist/core/package-manager.test.js +20 -0
  11. package/dist/core/package-manager.test.js.map +1 -0
  12. package/dist/core/resource-loader.d.ts +3 -0
  13. package/dist/core/resource-loader.d.ts.map +1 -1
  14. package/dist/core/resource-loader.js +10 -0
  15. package/dist/core/resource-loader.js.map +1 -1
  16. package/dist/core/settings-manager.d.ts +1 -0
  17. package/dist/core/settings-manager.d.ts.map +1 -1
  18. package/dist/core/settings-manager.js.map +1 -1
  19. package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
  20. package/dist/extensions/caveman/index.js +116 -0
  21. package/dist/extensions/caveman/package.json +8 -0
  22. package/dist/extensions/caveman/test/extension.test.js +185 -0
  23. package/dist/extensions/caveman/test/helpers.test.js +67 -0
  24. package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
  25. package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
  26. package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
  27. package/dist/extensions/pi-intercom/config.ts +2 -2
  28. package/dist/extensions/pi-intercom/index.ts +2 -2
  29. package/dist/extensions/pi-intercom/package.json +2 -2
  30. package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
  31. package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
  32. package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
  33. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
  34. package/dist/extensions/pi-powerline-footer/index.ts +9 -16
  35. package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
  36. package/dist/extensions/pi-powerline-footer/working-vibes.ts +2 -9
  37. package/dist/extensions/pi-rewind-hook/index.ts +1 -1
  38. package/dist/extensions/pi-rewind-hook/package.json +1 -1
  39. package/dist/extensions/pi-subagents/README.md +224 -52
  40. package/dist/extensions/pi-subagents/install.mjs +93 -0
  41. package/dist/extensions/pi-subagents/package.json +4 -5
  42. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  43. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  44. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
  45. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
  46. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
  47. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
  48. package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
  49. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
  50. package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
  51. package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
  52. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
  53. package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
  54. package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
  55. package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
  56. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
  57. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
  58. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
  59. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
  60. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
  61. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
  62. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
  63. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
  64. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
  65. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
  66. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
  67. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
  68. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
  69. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
  70. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
  71. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
  72. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
  73. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
  74. package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
  75. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
  76. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
  77. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
  78. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
  79. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
  80. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
  81. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
  82. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
  83. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
  87. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
  88. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
  89. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
  90. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
  92. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
  93. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
  94. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
  95. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
  96. package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
  97. package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
  98. package/dist/extensions/pi-subagents/src/shared/utils.ts +100 -15
  99. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
  100. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
  101. package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
  102. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
  103. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
  104. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
  105. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
  106. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
  107. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
  108. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
  109. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
  110. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
  111. package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
  112. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
  113. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
  114. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
  115. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
  116. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
  117. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
  118. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
  119. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
  120. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
  121. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
  122. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
  123. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
  124. package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
  125. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
  126. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
  127. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
  128. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
  129. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
  130. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
  131. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
  132. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
  133. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
  134. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
  135. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
  136. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
  137. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
  138. package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
  139. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
  140. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
  141. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
  142. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
  143. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
  144. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
  145. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
  146. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
  147. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
  148. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
  149. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
  150. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
  151. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
  152. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
  153. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
  154. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
  155. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
  156. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
  157. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
  158. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
  159. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
  160. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
  161. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
  162. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
  163. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
  164. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
  165. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
  166. package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
  167. package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
  168. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
  169. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
  170. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
  171. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
  172. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
  173. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
  174. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
  175. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
  176. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
  177. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
  178. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
  179. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
  180. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
  181. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
  182. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
  183. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
  184. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
  185. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
  186. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
  187. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
  188. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
  189. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
  190. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
  191. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
  192. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
  193. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
  194. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
  195. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
  196. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
  197. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
  198. package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
  199. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
  200. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
  201. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
  202. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
  203. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
  204. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
  205. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
  206. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
  207. package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
  208. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
  209. package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
  210. package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
  211. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
  212. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
  213. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
  214. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
  215. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
  216. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
  217. package/dist/main.d.ts.map +1 -1
  218. package/dist/main.js +4 -0
  219. package/dist/main.js.map +1 -1
  220. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  221. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  222. package/dist/modes/interactive/components/first-time-setup.js +19 -5
  223. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  224. package/dist/skills/caveman/SKILL.md +49 -0
  225. package/dist/skills/handoff-text/SKILL.md +13 -0
  226. package/docs/docs.json +4 -0
  227. package/docs/extensions.md +2 -0
  228. package/docs/settings.md +15 -0
  229. package/docs/shared-host-extensions.md +109 -0
  230. package/package.json +1 -1
@@ -0,0 +1,562 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ type JsonSchemaNode = Record<string, unknown>;
5
+
6
+ interface SubagentParamsSchema {
7
+ properties?: {
8
+ context?: {
9
+ type?: string;
10
+ enum?: string[];
11
+ description?: string;
12
+ };
13
+ tasks?: {
14
+ items?: {
15
+ properties?: {
16
+ count?: {
17
+ minimum?: number;
18
+ description?: string;
19
+ };
20
+ };
21
+ };
22
+ };
23
+ concurrency?: {
24
+ minimum?: number;
25
+ description?: string;
26
+ };
27
+ timeoutMs?: {
28
+ minimum?: number;
29
+ description?: string;
30
+ };
31
+ maxRuntimeMs?: {
32
+ minimum?: number;
33
+ description?: string;
34
+ };
35
+ turnBudget?: {
36
+ properties?: {
37
+ maxTurns?: { minimum?: number };
38
+ graceTurns?: { minimum?: number };
39
+ };
40
+ };
41
+ id?: {
42
+ type?: string;
43
+ description?: string;
44
+ };
45
+ runId?: {
46
+ type?: string;
47
+ description?: string;
48
+ };
49
+ dir?: {
50
+ type?: string;
51
+ description?: string;
52
+ };
53
+ action?: {
54
+ type?: string;
55
+ enum?: string[];
56
+ description?: string;
57
+ };
58
+ view?: {
59
+ type?: string;
60
+ enum?: string[];
61
+ description?: string;
62
+ };
63
+ lines?: {
64
+ minimum?: number;
65
+ maximum?: number;
66
+ description?: string;
67
+ };
68
+ control?: {
69
+ properties?: {
70
+ needsAttentionAfterMs?: { minimum?: number };
71
+ activeNoticeAfterMs?: { minimum?: number };
72
+ activeNoticeAfterTurns?: { minimum?: number };
73
+ activeNoticeAfterTokens?: { minimum?: number };
74
+ failedToolAttemptsBeforeAttention?: { minimum?: number };
75
+ notifyOn?: { items?: { enum?: string[] } };
76
+ notifyChannels?: { items?: { enum?: string[] } };
77
+ };
78
+ };
79
+ skill?: JsonSchemaNode;
80
+ output?: JsonSchemaNode;
81
+ config?: JsonSchemaNode;
82
+ chain?: {
83
+ items?: JsonSchemaNode & {
84
+ properties?: Record<string, JsonSchemaNode>;
85
+ };
86
+ };
87
+ };
88
+ }
89
+
90
+ function missingPackageName(error: unknown): string | undefined {
91
+ const message = error instanceof Error ? error.message : String(error);
92
+ return message.match(/Cannot find package ['"]([^'"]+)['"]/i)?.[1];
93
+ }
94
+
95
+ function anyOfBranches(schema: JsonSchemaNode | undefined): JsonSchemaNode[] {
96
+ const anyOf = schema?.anyOf;
97
+ if (!Array.isArray(anyOf)) return [];
98
+ return anyOf.filter((branch): branch is JsonSchemaNode => !!branch && typeof branch === "object");
99
+ }
100
+
101
+ function hasAnyOfType(schema: JsonSchemaNode | undefined, type: string): boolean {
102
+ return anyOfBranches(schema).some((branch) => branch.type === type);
103
+ }
104
+
105
+ function hasAnyOfArrayWithStringItems(schema: JsonSchemaNode | undefined): boolean {
106
+ return anyOfBranches(schema).some((branch) => {
107
+ if (branch.type !== "array") return false;
108
+ const items = branch.items;
109
+ return !!items && typeof items === "object" && (items as JsonSchemaNode).type === "string";
110
+ });
111
+ }
112
+
113
+ function getPropertySchema(schema: JsonSchemaNode | undefined, path: string[]): JsonSchemaNode | undefined {
114
+ let current: unknown = schema;
115
+ for (const key of path) {
116
+ if (!current || typeof current !== "object") return undefined;
117
+ current = (current as JsonSchemaNode).properties;
118
+ if (!current || typeof current !== "object") return undefined;
119
+ current = (current as Record<string, unknown>)[key];
120
+ }
121
+ return current && typeof current === "object" ? current as JsonSchemaNode : undefined;
122
+ }
123
+
124
+ let schemas: Record<string, JsonSchemaNode> = {};
125
+ let SubagentParams: SubagentParamsSchema | undefined;
126
+ let schemasAvailable = true;
127
+ try {
128
+ schemas = await import("../../src/extension/schemas.ts") as Record<string, JsonSchemaNode>;
129
+ SubagentParams = schemas.SubagentParams as SubagentParamsSchema;
130
+ } catch (error) {
131
+ if (missingPackageName(error) !== "typebox") throw error;
132
+ schemasAvailable = false;
133
+ }
134
+ let CompileSchema: ((schema: unknown) => { Check(value: unknown): boolean; Errors(value: unknown): Iterable<{ message: string }> }) | undefined;
135
+ try {
136
+ const compileModule = await import("typebox/compile") as { Compile: typeof CompileSchema };
137
+ CompileSchema = compileModule.Compile;
138
+ } catch (error) {
139
+ if (missingPackageName(error) !== "typebox") throw error;
140
+ // The structural schema assertions below do not need the optional compiler package.
141
+ }
142
+
143
+ describe("SubagentParams schema", { skip: !schemasAvailable ? "typebox not available" : undefined }, () => {
144
+ it("includes context field for fresh/fork execution mode", () => {
145
+ const contextSchema = SubagentParams?.properties?.context;
146
+ assert.ok(contextSchema, "context schema should exist");
147
+ assert.equal(contextSchema.type, "string");
148
+ assert.deepEqual(contextSchema.enum, ["fresh", "fork"]);
149
+ const description = String(contextSchema.description ?? "");
150
+ assert.match(description, /fresh/);
151
+ assert.match(description, /fork/);
152
+ assert.match(description, /each requested agent/);
153
+ assert.match(description, /overrides every child/);
154
+ });
155
+
156
+ it("includes count and concurrency on top-level parallel mode", () => {
157
+ const taskSchema = SubagentParams?.properties?.tasks?.items?.properties;
158
+ const taskCountSchema = taskSchema?.count;
159
+ assert.ok(taskCountSchema, "tasks[].count schema should exist");
160
+ assert.equal(taskCountSchema.minimum, 1);
161
+ const outputSchema = taskSchema?.output as JsonSchemaNode | undefined;
162
+ assert.equal(outputSchema?.type, undefined);
163
+ assert.equal(hasAnyOfType(outputSchema, "string"), true);
164
+ assert.equal(hasAnyOfType(outputSchema, "boolean"), true);
165
+ const readsSchema = taskSchema?.reads as JsonSchemaNode | undefined;
166
+ assert.equal(readsSchema?.type, undefined);
167
+ assert.equal(hasAnyOfArrayWithStringItems(readsSchema), true);
168
+ assert.equal(hasAnyOfType(readsSchema, "boolean"), true);
169
+ assert.equal(taskSchema?.progress?.type, "boolean");
170
+
171
+ const concurrencySchema = SubagentParams?.properties?.concurrency;
172
+ assert.ok(concurrencySchema, "concurrency schema should exist");
173
+ assert.equal(concurrencySchema.minimum, 1);
174
+ assert.match(String(concurrencySchema.description ?? ""), /parallel/i);
175
+ });
176
+
177
+ it("allows runtime validation of management and control action strings", () => {
178
+ const actionSchema = SubagentParams?.properties?.action;
179
+ assert.ok(actionSchema, "action schema should exist");
180
+ assert.equal(actionSchema.type, "string");
181
+ assert.equal(actionSchema.enum, undefined);
182
+ const description = String(actionSchema.description ?? "");
183
+ assert.match(description, /Management\/control action only/);
184
+ assert.match(description, /Must be omitted for execution mode/);
185
+ assert.match(description, /single, parallel, or chain/);
186
+ assert.doesNotMatch(description, /orchestration\./);
187
+ });
188
+
189
+ it("includes foreground timeout aliases and turn budget", () => {
190
+ const timeoutSchema = SubagentParams?.properties?.timeoutMs;
191
+ const maxRuntimeSchema = SubagentParams?.properties?.maxRuntimeMs;
192
+ const turnBudgetSchema = SubagentParams?.properties?.turnBudget;
193
+ const toolBudgetSchema = SubagentParams?.properties?.toolBudget;
194
+ assert.ok(timeoutSchema, "timeoutMs schema should exist");
195
+ assert.ok(maxRuntimeSchema, "maxRuntimeMs schema should exist");
196
+ assert.equal(timeoutSchema.minimum, 1);
197
+ assert.equal(maxRuntimeSchema.minimum, 1);
198
+ assert.match(String(timeoutSchema.description ?? ""), /foreground and async\/background/i);
199
+ assert.doesNotMatch(String(timeoutSchema.description ?? ""), /foreground-only/i);
200
+ assert.match(String(maxRuntimeSchema.description ?? ""), /timeoutMs/i);
201
+ assert.match(String(maxRuntimeSchema.description ?? ""), /foreground and async\/background/i);
202
+ assert.equal(turnBudgetSchema?.properties?.maxTurns?.minimum, 1);
203
+ assert.equal(turnBudgetSchema?.properties?.graceTurns?.minimum, 0);
204
+ assert.equal(toolBudgetSchema?.properties?.soft?.minimum, 1);
205
+ assert.equal(toolBudgetSchema?.properties?.hard?.minimum, 1);
206
+ });
207
+
208
+ it("includes subagent control fields", () => {
209
+ const idSchema = SubagentParams?.properties?.id;
210
+ assert.ok(idSchema, "id schema should exist");
211
+ assert.equal(idSchema.type, "string");
212
+ assert.match(String(idSchema.description ?? ""), /status/i);
213
+ assert.match(String(idSchema.description ?? ""), /interrupt/i);
214
+ assert.match(String(idSchema.description ?? ""), /steer/i);
215
+ assert.match(String(idSchema.description ?? ""), /append-step/i);
216
+
217
+ const runIdSchema = SubagentParams?.properties?.runId;
218
+ assert.ok(runIdSchema, "runId schema should exist");
219
+ assert.equal(runIdSchema.type, "string");
220
+ assert.match(String(runIdSchema.description ?? ""), /interrupt/i);
221
+ assert.match(String(runIdSchema.description ?? ""), /steer/i);
222
+ assert.match(String(runIdSchema.description ?? ""), /append-step/i);
223
+
224
+ const dirSchema = SubagentParams?.properties?.dir;
225
+ assert.ok(dirSchema, "dir schema should exist");
226
+ assert.equal(dirSchema.type, "string");
227
+ assert.match(String(dirSchema.description ?? ""), /status/i);
228
+ assert.match(String(dirSchema.description ?? ""), /steer/i);
229
+
230
+ const viewSchema = SubagentParams?.properties?.view;
231
+ assert.ok(viewSchema, "view schema should exist");
232
+ assert.equal(viewSchema.type, "string");
233
+ assert.deepEqual(viewSchema.enum, ["fleet", "transcript"]);
234
+ assert.match(String(viewSchema.description ?? ""), /status view/i);
235
+ assert.match(String(viewSchema.description ?? ""), /transcript/i);
236
+
237
+ const linesSchema = SubagentParams?.properties?.lines;
238
+ assert.ok(linesSchema, "lines schema should exist");
239
+ assert.equal(linesSchema.minimum, 1);
240
+ assert.equal(linesSchema.maximum, 500);
241
+ assert.match(String(linesSchema.description ?? ""), /transcript/i);
242
+
243
+ const controlSchema = SubagentParams?.properties?.control;
244
+ assert.ok(controlSchema, "control schema should exist");
245
+ assert.equal(controlSchema.properties?.needsAttentionAfterMs?.minimum, 1);
246
+ assert.equal(controlSchema.properties?.activeNoticeAfterMs?.minimum, 1);
247
+ assert.equal(controlSchema.properties?.activeNoticeAfterTurns?.minimum, 1);
248
+ assert.equal(controlSchema.properties?.activeNoticeAfterTokens?.minimum, 1);
249
+ assert.equal(controlSchema.properties?.failedToolAttemptsBeforeAttention?.minimum, 1);
250
+ assert.deepEqual(controlSchema.properties?.notifyOn?.items?.enum, ["active_long_running", "needs_attention"]);
251
+ assert.deepEqual(controlSchema.properties?.notifyChannels?.items?.enum, ["event", "async", "intercom"]);
252
+ });
253
+
254
+ it("does not emit description-only schema nodes", () => {
255
+ const descriptionOnlyPaths: string[] = [];
256
+
257
+ for (const [name, schema] of Object.entries(schemas)) {
258
+ const stack: Array<{ path: string; value: unknown }> = [{ path: name, value: schema }];
259
+ while (stack.length > 0) {
260
+ const current = stack.pop()!;
261
+ if (!current.value || typeof current.value !== "object") continue;
262
+
263
+ const node = current.value as JsonSchemaNode;
264
+ if (Object.hasOwn(node, "description") && !Object.hasOwn(node, "type") && !Object.hasOwn(node, "anyOf")) {
265
+ descriptionOnlyPaths.push(current.path);
266
+ }
267
+
268
+ if (Array.isArray(current.value)) {
269
+ current.value.forEach((value, index) => stack.push({ path: `${current.path}[${index}]`, value }));
270
+ continue;
271
+ }
272
+
273
+ for (const [key, value] of Object.entries(node)) {
274
+ stack.push({ path: `${current.path}.${key}`, value });
275
+ }
276
+ }
277
+ }
278
+
279
+ assert.deepEqual(descriptionOnlyPaths, []);
280
+ });
281
+
282
+ it("does not emit array-typed schema nodes without items", () => {
283
+ const missingItemsPaths: string[] = [];
284
+
285
+ for (const [name, schema] of Object.entries(schemas)) {
286
+ const stack: Array<{ path: string; value: unknown }> = [{ path: name, value: schema }];
287
+ while (stack.length > 0) {
288
+ const current = stack.pop()!;
289
+ if (!current.value || typeof current.value !== "object") continue;
290
+
291
+ const node = current.value as JsonSchemaNode;
292
+ if (node.type === "array" && !Object.hasOwn(node, "items")) {
293
+ missingItemsPaths.push(current.path);
294
+ }
295
+
296
+ if (Array.isArray(current.value)) {
297
+ current.value.forEach((value, index) => stack.push({ path: `${current.path}[${index}]`, value }));
298
+ continue;
299
+ }
300
+
301
+ for (const [key, value] of Object.entries(node)) {
302
+ stack.push({ path: `${current.path}.${key}`, value });
303
+ }
304
+ }
305
+ }
306
+
307
+ assert.deepEqual(missingItemsPaths, []);
308
+ });
309
+
310
+ it("keeps only top-level parameter descriptions to keep the provider payload compact", () => {
311
+ assert.ok(SubagentParams, "SubagentParams schema should exist");
312
+ const schema = SubagentParams as unknown as JsonSchemaNode;
313
+ const serialized = JSON.stringify(schema);
314
+ assert.ok(serialized.length < 15_000, `expected compact schema under 15k chars, got ${serialized.length}`);
315
+ assert.equal(serialized.includes('"$ref"'), false);
316
+ assert.equal(serialized.includes('"$defs"'), false);
317
+ assert.equal(serialized.split("Optional acceptance policy.").length - 1, 1);
318
+ assert.match(String((schema.properties as Record<string, JsonSchemaNode> | undefined)?.agent?.description ?? ""), /SINGLE mode/);
319
+ assert.match(String((schema.properties as Record<string, JsonSchemaNode> | undefined)?.acceptance?.description ?? ""), /acceptance policy/);
320
+
321
+ const nestedDescriptionPaths: string[] = [];
322
+ const stack: Array<{ path: string; value: unknown }> = [{ path: "SubagentParams", value: schema }];
323
+ while (stack.length > 0) {
324
+ const current = stack.pop()!;
325
+ if (!current.value || typeof current.value !== "object") continue;
326
+ const node = current.value as JsonSchemaNode;
327
+ const pathParts = current.path.split(".");
328
+ const isTopLevelParameter = pathParts.length === 3 && pathParts[0] === "SubagentParams" && pathParts[1] === "properties";
329
+ if (typeof node.description === "string" && !isTopLevelParameter) nestedDescriptionPaths.push(`${current.path}.description`);
330
+ if (Array.isArray(current.value)) {
331
+ current.value.forEach((value, index) => stack.push({ path: `${current.path}[${index}]`, value }));
332
+ } else {
333
+ for (const [key, value] of Object.entries(node)) stack.push({ path: `${current.path}.${key}`, value });
334
+ }
335
+ }
336
+ assert.deepEqual(nestedDescriptionPaths, []);
337
+ });
338
+
339
+ it("preserves TypeBox metadata while pruning provider-visible descriptions", () => {
340
+ assert.ok(SubagentParams, "SubagentParams schema should exist");
341
+ const schema = SubagentParams as unknown as JsonSchemaNode;
342
+ const rootKind = Object.getOwnPropertyDescriptor(schema, "~kind");
343
+ assert.equal(rootKind?.value, "Object");
344
+ assert.equal(rootKind?.enumerable, false);
345
+
346
+ const agentSchema = getPropertySchema(schema, ["agent"]);
347
+ assert.equal(Object.getOwnPropertyDescriptor(agentSchema, "~kind")?.enumerable, false);
348
+ assert.equal(Object.getOwnPropertyDescriptor(agentSchema, "~optional")?.value, true);
349
+ assert.equal(Object.getOwnPropertyDescriptor(agentSchema, "~optional")?.enumerable, false);
350
+
351
+ const tasksSchema = getPropertySchema(schema, ["tasks"]);
352
+ const taskItemsSchema = tasksSchema?.items as JsonSchemaNode | undefined;
353
+ const taskCountSchema = getPropertySchema(taskItemsSchema, ["count"]);
354
+ assert.equal(Object.getOwnPropertyDescriptor(taskCountSchema, "~kind")?.enumerable, false);
355
+ assert.equal(Object.getOwnPropertyDescriptor(taskCountSchema, "~optional")?.value, true);
356
+ assert.equal(Object.getOwnPropertyDescriptor(taskCountSchema, "~optional")?.enumerable, false);
357
+ });
358
+
359
+ it("does not emit provider-rejected schema shapes", () => {
360
+ const rejectedPaths: string[] = [];
361
+ const rejectedKeywords = ["allOf", "const", "if", "then", "not"];
362
+
363
+ for (const [name, schema] of Object.entries(schemas)) {
364
+ const stack: Array<{ path: string; value: unknown }> = [{ path: name, value: schema }];
365
+ while (stack.length > 0) {
366
+ const current = stack.pop()!;
367
+ if (!current.value || typeof current.value !== "object") continue;
368
+
369
+ const node = current.value as JsonSchemaNode;
370
+ if (Array.isArray(node.type)) {
371
+ rejectedPaths.push(`${current.path}.type`);
372
+ }
373
+ if (Object.hasOwn(node, "anyOf") && Object.hasOwn(node, "type")) {
374
+ rejectedPaths.push(`${current.path}.type+anyOf`);
375
+ }
376
+ for (const keyword of rejectedKeywords) {
377
+ if (Object.hasOwn(node, keyword)) rejectedPaths.push(`${current.path}.${keyword}`);
378
+ }
379
+
380
+ if (Array.isArray(current.value)) {
381
+ current.value.forEach((value, index) => stack.push({ path: `${current.path}[${index}]`, value }));
382
+ continue;
383
+ }
384
+
385
+ for (const [key, value] of Object.entries(node)) {
386
+ stack.push({ path: `${current.path}.${key}`, value });
387
+ }
388
+ }
389
+ }
390
+
391
+ assert.deepEqual(rejectedPaths, []);
392
+ });
393
+
394
+ it("uses provider-friendly anyOf unions for flexible fields and chain items", () => {
395
+ const skillSchema = SubagentParams?.properties?.skill;
396
+ assert.ok(skillSchema, "skill schema should exist");
397
+ assert.equal(skillSchema.type, undefined);
398
+ assert.equal(hasAnyOfArrayWithStringItems(skillSchema), true);
399
+ assert.equal(hasAnyOfType(skillSchema, "boolean"), true);
400
+ assert.equal(hasAnyOfType(skillSchema, "string"), true);
401
+
402
+ const outputSchema = SubagentParams?.properties?.output;
403
+ assert.ok(outputSchema, "output schema should exist");
404
+ assert.equal(outputSchema.type, undefined);
405
+ assert.equal(hasAnyOfType(outputSchema, "string"), true);
406
+ assert.equal(hasAnyOfType(outputSchema, "boolean"), true);
407
+
408
+ const configSchema = SubagentParams?.properties?.config;
409
+ assert.ok(configSchema, "config schema should exist");
410
+ assert.equal(configSchema.type, undefined);
411
+ assert.equal(anyOfBranches(configSchema).some((branch) => branch.type === "object" && branch.additionalProperties === true), true);
412
+ assert.equal(hasAnyOfType(configSchema, "string"), true);
413
+
414
+ const acceptanceSchema = SubagentParams?.properties?.acceptance;
415
+ assert.ok(acceptanceSchema, "acceptance schema should exist");
416
+ assert.equal(acceptanceSchema.type, undefined);
417
+ assert.equal(hasAnyOfType(acceptanceSchema, "string"), true);
418
+ assert.equal(hasAnyOfType(acceptanceSchema, "boolean"), true);
419
+ const acceptanceObjectBranch = anyOfBranches(acceptanceSchema).find((branch) => branch.type === "object");
420
+ assert.ok(acceptanceObjectBranch, "acceptance should support object config");
421
+ assert.equal(acceptanceObjectBranch.additionalProperties, true);
422
+ assert.equal(JSON.stringify(acceptanceObjectBranch).includes('"anyOf"'), false);
423
+
424
+ const chainItem = SubagentParams?.properties?.chain?.items;
425
+ assert.ok(chainItem, "chain item schema should exist");
426
+ assert.equal(chainItem.type, "object");
427
+ assert.equal(chainItem.anyOf, undefined);
428
+ assert.equal(chainItem.allOf, undefined);
429
+ assert.equal(chainItem.oneOf, undefined);
430
+ assert.equal(chainItem.additionalProperties, false);
431
+ assert.equal(chainItem.properties?.agent?.type, "string");
432
+ assert.equal(chainItem.properties?.phase?.type, "string");
433
+ assert.equal(chainItem.properties?.label?.type, "string");
434
+ assert.equal(chainItem.properties?.as?.type, "string");
435
+ assert.equal(chainItem.properties?.outputSchema?.type, "object");
436
+ assert.equal(chainItem.properties?.parallel?.type, undefined);
437
+ const parallelBranches = anyOfBranches(chainItem.properties?.parallel);
438
+ const staticParallelBranch = parallelBranches.find((branch) => branch.type === "array");
439
+ const dynamicParallelBranch = parallelBranches.find((branch) => branch.type === "object");
440
+ assert.ok(staticParallelBranch, "parallel should support static task arrays");
441
+ assert.ok(dynamicParallelBranch, "parallel should support a dynamic task template object");
442
+ const chainParallelTask = (staticParallelBranch.items as { properties?: Record<string, JsonSchemaNode> } | undefined)?.properties;
443
+ assert.equal(chainParallelTask?.agent?.type, "string");
444
+ assert.equal(chainParallelTask?.phase?.type, "string");
445
+ assert.equal(chainParallelTask?.label?.type, "string");
446
+ assert.equal(chainParallelTask?.as?.type, "string");
447
+ assert.equal(chainParallelTask?.outputSchema?.type, "object");
448
+ const chainParallelOutputSchema = chainParallelTask?.output;
449
+ assert.equal(chainParallelOutputSchema?.type, undefined);
450
+ assert.equal(hasAnyOfType(chainParallelOutputSchema, "string"), true);
451
+ assert.equal(hasAnyOfType(chainParallelOutputSchema, "boolean"), true);
452
+ const chainParallelReadsSchema = chainParallelTask?.reads;
453
+ assert.equal(chainParallelReadsSchema?.type, undefined);
454
+ assert.equal(hasAnyOfArrayWithStringItems(chainParallelReadsSchema), true);
455
+ assert.equal(hasAnyOfType(chainParallelReadsSchema, "boolean"), true);
456
+ assert.equal(chainItem.properties?.expand?.type, "object");
457
+ assert.equal(chainItem.properties?.collect?.type, "object");
458
+ const chainParallelSkillSchema = chainParallelTask?.skill;
459
+ assert.equal(chainParallelSkillSchema?.type, undefined);
460
+ assert.equal(hasAnyOfArrayWithStringItems(chainParallelSkillSchema), true);
461
+ assert.equal(hasAnyOfType(chainParallelSkillSchema, "boolean"), true);
462
+ assert.equal(hasAnyOfType(chainParallelSkillSchema, "string"), true);
463
+ const chainOutputSchema = chainItem.properties?.output as JsonSchemaNode | undefined;
464
+ assert.equal(chainOutputSchema?.type, undefined);
465
+ assert.equal(hasAnyOfType(chainOutputSchema, "string"), true);
466
+ assert.equal(hasAnyOfType(chainOutputSchema, "boolean"), true);
467
+ const chainReadsSchema = chainItem.properties?.reads as JsonSchemaNode | undefined;
468
+ assert.equal(chainReadsSchema?.type, undefined);
469
+ assert.equal(hasAnyOfArrayWithStringItems(chainReadsSchema), true);
470
+ assert.equal(hasAnyOfType(chainReadsSchema, "boolean"), true);
471
+ });
472
+
473
+ it("validates representative flexible field values with TypeBox compiler", { skip: !CompileSchema ? "typebox compiler not available" : undefined }, () => {
474
+ assert.ok(SubagentParams, "SubagentParams schema should exist");
475
+ assert.ok(CompileSchema, "TypeBox compiler should exist");
476
+ const validator = CompileSchema(SubagentParams);
477
+ const validValues = [
478
+ { skill: "review" },
479
+ { skill: false },
480
+ { tasks: [{ agent: "reviewer", task: "check this", reads: false }] },
481
+ { tasks: [{ agent: "reviewer", task: "check this", skill: "review" }] },
482
+ { tasks: [{ agent: "reviewer", task: "check this", skill: false }] },
483
+ { tasks: [{ agent: "reviewer", task: "check this", output: "review.md", reads: ["input.md"], progress: true }] },
484
+ { chain: [{ agent: "reviewer", reads: false }] },
485
+ { chain: [{ agent: "reviewer", phase: "Review", label: "Correctness", as: "findings", outputSchema: { type: "object" } }] },
486
+ { chain: [{ agent: "reviewer", skill: "review" }] },
487
+ { chain: [{ agent: "reviewer", skill: false }] },
488
+ { chain: [{ parallel: [{ agent: "reviewer", reads: false, skill: false }] }] },
489
+ { chain: [{ parallel: [{ agent: "reviewer", phase: "Review", label: "Security", as: "security", outputSchema: { type: "object" } }] }] },
490
+ { chain: [{ parallel: [{ agent: "reviewer", output: "review.md", reads: ["input.md"], skill: "review" }] }] },
491
+ { chain: [{ expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 }, parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } }, collect: { as: "reviews" } }] },
492
+ { agent: "worker", task: "Fix", acceptance: false },
493
+ { agent: "worker", task: "Fix", timeoutMs: 1000 },
494
+ { action: "steer", id: "run-1", message: "focus on tests" },
495
+ { action: "steer", id: "run-1", index: 0, message: "focus on tests" },
496
+ { action: "single", agent: "worker", task: "Fix" },
497
+ { action: "PARALLEL", tasks: [{ agent: "worker", task: "Fix" }] },
498
+ { action: "not-a-real-action" },
499
+ { tasks: [{ agent: "worker", task: "Fix" }], maxRuntimeMs: 1000 },
500
+ { chain: [{ agent: "worker", task: "Fix" }], timeoutMs: 1000, maxRuntimeMs: 1000 },
501
+ { agent: "worker", task: "Fix", acceptance: "checked" },
502
+ { agent: "worker", task: "Fix", acceptance: { level: "checked", review: false } },
503
+ { tasks: [{ agent: "worker", task: "Fix", acceptance: false }] },
504
+ { chain: [{ agent: "worker", acceptance: { level: "checked" } }] },
505
+ { chain: [{ parallel: [{ agent: "worker", acceptance: { level: "verified", verify: [{ id: "unit", command: "npm test" }] } }] }] },
506
+ { chain: [{ expand: { from: { output: "targets", path: "/items" }, maxItems: 4 }, parallel: { agent: "worker", acceptance: { level: "checked", review: false } }, collect: { as: "reviews" } }] },
507
+ { config: { name: "reviewer", description: "Review things" } },
508
+ { config: JSON.stringify({ name: "reviewer", description: "Review things" }) },
509
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 5, graceTurns: 1 } },
510
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 1 } },
511
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 3, graceTurns: 0 } },
512
+ { agent: "worker", task: "Fix", toolBudget: { soft: 5, hard: 8, block: ["read", "grep"] } },
513
+ { agent: "worker", task: "Fix", toolBudget: { hard: 8, block: "*" } },
514
+ { tasks: [{ agent: "worker", task: "Fix", toolBudget: { hard: 3 } }] },
515
+ { chain: [{ agent: "worker", toolBudget: { hard: 3 } }] },
516
+ { chain: [{ parallel: [{ agent: "worker", toolBudget: { hard: 3 } }] }] },
517
+ ];
518
+ const invalidValues = [
519
+ { skill: 123 },
520
+ { skill: [123] },
521
+ { output: 123 },
522
+ { timeoutMs: 0 },
523
+ { maxRuntimeMs: -1 },
524
+ { tasks: [{ agent: "reviewer", task: "check this", reads: "input.md" }] },
525
+ { chain: [{ parallel: [{ agent: "reviewer", output: 123 }] }] },
526
+ { chain: [{ parallel: [{ agent: "reviewer", reads: "input.md" }] }] },
527
+ { chain: [{ parallel: [{ agent: "reviewer", skill: 123 }] }] },
528
+ { chain: [{ agent: "reviewer", outputSchema: "schema.json" }] },
529
+ { chain: [{ parallel: [{ agent: "reviewer", outputSchema: "schema.json" }] }] },
530
+ { chain: [{ expand: { from: { output: "targets", path: "/items" }, maxItems: 4, expression: "items" }, parallel: { agent: "reviewer" }, collect: { as: "reviews" } }] },
531
+ { chain: [{ expand: { from: { output: "targets", path: "/items" }, maxItems: 4 }, parallel: { agent: "reviewer", as: "child" }, collect: { as: "reviews" } }] },
532
+ { chain: [{ expand: { from: { output: "targets", path: "/items" }, maxItems: 4 }, parallel: { agent: "reviewer" }, collect: { as: "reviews" }, when: "later" }] },
533
+ { agent: "worker", task: "Fix", acceptance: true },
534
+ { tasks: [{ agent: "worker", task: "Fix", acceptance: true }] },
535
+ { chain: [{ expand: { from: { output: "targets", path: "/items" }, maxItems: 4 }, parallel: { agent: "worker", acceptance: true }, collect: { as: "reviews" } }] },
536
+ { config: [] },
537
+ { config: null },
538
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 0 } },
539
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 5, graceTurns: -1 } },
540
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 1.5 } },
541
+ { agent: "worker", task: "Fix", turnBudget: { graceTurns: 1 } },
542
+ { agent: "worker", task: "Fix", turnBudget: { maxTurns: 5, graceTurns: 1, extra: true } },
543
+ { agent: "worker", task: "Fix", toolBudget: { hard: 0 } },
544
+ { agent: "worker", task: "Fix", toolBudget: { hard: 3, soft: 0 } },
545
+ { agent: "worker", task: "Fix", toolBudget: { hard: 3, block: [123] } },
546
+ { agent: "worker", task: "Fix", toolBudget: { hard: 3, block: [] } },
547
+ { agent: "worker", task: "Fix", toolBudget: { hard: 3, block: "read" } },
548
+ ];
549
+
550
+ for (const value of validValues) {
551
+ assert.doesNotThrow(() => validator.Check(value), `validator should not throw for ${JSON.stringify(value)}`);
552
+ assert.equal(
553
+ validator.Check(value),
554
+ true,
555
+ `${JSON.stringify(value)} should validate: ${[...validator.Errors(value)].map((error) => error.message).join(", ")}`,
556
+ );
557
+ }
558
+ for (const value of invalidValues) {
559
+ assert.equal(validator.Check(value), false, `${JSON.stringify(value)} should not validate`);
560
+ }
561
+ });
562
+ });