@studio-foundation/engine 0.3.0-beta.1 → 0.3.0-beta.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 (246) hide show
  1. package/dist/__tests__/engine.conditions-group.test.d.ts +2 -0
  2. package/dist/__tests__/engine.conditions-group.test.d.ts.map +1 -0
  3. package/dist/__tests__/engine.conditions-group.test.js +222 -0
  4. package/dist/__tests__/engine.conditions-group.test.js.map +1 -0
  5. package/dist/__tests__/engine.conditions.test.d.ts +2 -0
  6. package/dist/__tests__/engine.conditions.test.d.ts.map +1 -0
  7. package/dist/__tests__/engine.conditions.test.js +164 -0
  8. package/dist/__tests__/engine.conditions.test.js.map +1 -0
  9. package/dist/__tests__/engine.resume.test.d.ts +2 -0
  10. package/dist/__tests__/engine.resume.test.d.ts.map +1 -0
  11. package/dist/__tests__/engine.resume.test.js +93 -0
  12. package/dist/__tests__/engine.resume.test.js.map +1 -0
  13. package/dist/__tests__/engine.script-stage.test.d.ts +2 -0
  14. package/dist/__tests__/engine.script-stage.test.d.ts.map +1 -0
  15. package/dist/__tests__/engine.script-stage.test.js +108 -0
  16. package/dist/__tests__/engine.script-stage.test.js.map +1 -0
  17. package/dist/db/client.d.ts +3 -0
  18. package/dist/db/client.d.ts.map +1 -0
  19. package/{src/db/client.ts → dist/db/client.js} +1 -2
  20. package/dist/db/client.js.map +1 -0
  21. package/dist/engine.context-event.test.d.ts +2 -0
  22. package/dist/engine.context-event.test.d.ts.map +1 -0
  23. package/dist/engine.context-event.test.js +111 -0
  24. package/dist/engine.context-event.test.js.map +1 -0
  25. package/dist/engine.d.ts +52 -0
  26. package/dist/engine.d.ts.map +1 -0
  27. package/dist/engine.js +364 -0
  28. package/dist/engine.js.map +1 -0
  29. package/dist/events.d.ts +163 -0
  30. package/dist/events.d.ts.map +1 -0
  31. package/dist/events.js +12 -0
  32. package/dist/events.js.map +1 -0
  33. package/{src/index.ts → dist/index.d.ts} +3 -45
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +24 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/pipeline/agent-loader.d.ts +4 -0
  38. package/dist/pipeline/agent-loader.d.ts.map +1 -0
  39. package/dist/pipeline/agent-loader.js +28 -0
  40. package/dist/pipeline/agent-loader.js.map +1 -0
  41. package/dist/pipeline/agent-loader.test.d.ts +2 -0
  42. package/dist/pipeline/agent-loader.test.d.ts.map +1 -0
  43. package/dist/pipeline/agent-loader.test.js +134 -0
  44. package/dist/pipeline/agent-loader.test.js.map +1 -0
  45. package/dist/pipeline/condition-evaluator.d.ts +6 -0
  46. package/dist/pipeline/condition-evaluator.d.ts.map +1 -0
  47. package/dist/pipeline/condition-evaluator.js +105 -0
  48. package/dist/pipeline/condition-evaluator.js.map +1 -0
  49. package/dist/pipeline/condition-evaluator.test.d.ts +2 -0
  50. package/dist/pipeline/condition-evaluator.test.d.ts.map +1 -0
  51. package/dist/pipeline/condition-evaluator.test.js +103 -0
  52. package/dist/pipeline/condition-evaluator.test.js.map +1 -0
  53. package/dist/pipeline/context-pack-loader.d.ts +3 -0
  54. package/dist/pipeline/context-pack-loader.d.ts.map +1 -0
  55. package/dist/pipeline/context-pack-loader.js +51 -0
  56. package/dist/pipeline/context-pack-loader.js.map +1 -0
  57. package/dist/pipeline/context-propagation.d.ts +28 -0
  58. package/dist/pipeline/context-propagation.d.ts.map +1 -0
  59. package/dist/pipeline/context-propagation.js +161 -0
  60. package/dist/pipeline/context-propagation.js.map +1 -0
  61. package/dist/pipeline/context-propagation.test.d.ts +2 -0
  62. package/dist/pipeline/context-propagation.test.d.ts.map +1 -0
  63. package/dist/pipeline/context-propagation.test.js +194 -0
  64. package/dist/pipeline/context-propagation.test.js.map +1 -0
  65. package/dist/pipeline/contract-loader.d.ts +4 -0
  66. package/dist/pipeline/contract-loader.d.ts.map +1 -0
  67. package/dist/pipeline/contract-loader.js +30 -0
  68. package/dist/pipeline/contract-loader.js.map +1 -0
  69. package/dist/pipeline/group-orchestrator.d.ts +19 -0
  70. package/dist/pipeline/group-orchestrator.d.ts.map +1 -0
  71. package/dist/pipeline/group-orchestrator.js +376 -0
  72. package/dist/pipeline/group-orchestrator.js.map +1 -0
  73. package/dist/pipeline/hook-executor.d.ts +28 -0
  74. package/dist/pipeline/hook-executor.d.ts.map +1 -0
  75. package/dist/pipeline/hook-executor.js +63 -0
  76. package/dist/pipeline/hook-executor.js.map +1 -0
  77. package/dist/pipeline/hook-executor.test.d.ts +2 -0
  78. package/dist/pipeline/hook-executor.test.d.ts.map +1 -0
  79. package/dist/pipeline/hook-executor.test.js +71 -0
  80. package/dist/pipeline/hook-executor.test.js.map +1 -0
  81. package/dist/pipeline/invariants-loader.d.ts +6 -0
  82. package/dist/pipeline/invariants-loader.d.ts.map +1 -0
  83. package/dist/pipeline/invariants-loader.js +15 -0
  84. package/dist/pipeline/invariants-loader.js.map +1 -0
  85. package/dist/pipeline/invariants-loader.test.d.ts +2 -0
  86. package/dist/pipeline/invariants-loader.test.d.ts.map +1 -0
  87. package/dist/pipeline/invariants-loader.test.js +39 -0
  88. package/dist/pipeline/invariants-loader.test.js.map +1 -0
  89. package/dist/pipeline/loader.d.ts +5 -0
  90. package/dist/pipeline/loader.d.ts.map +1 -0
  91. package/dist/pipeline/loader.js +132 -0
  92. package/dist/pipeline/loader.js.map +1 -0
  93. package/dist/pipeline/loader.test.d.ts +2 -0
  94. package/dist/pipeline/loader.test.d.ts.map +1 -0
  95. package/dist/pipeline/loader.test.js +120 -0
  96. package/dist/pipeline/loader.test.js.map +1 -0
  97. package/dist/pipeline/output-validator.d.ts +10 -0
  98. package/dist/pipeline/output-validator.d.ts.map +1 -0
  99. package/dist/pipeline/output-validator.js +20 -0
  100. package/dist/pipeline/output-validator.js.map +1 -0
  101. package/dist/pipeline/output-validator.test.d.ts +2 -0
  102. package/dist/pipeline/output-validator.test.d.ts.map +1 -0
  103. package/dist/pipeline/output-validator.test.js +77 -0
  104. package/dist/pipeline/output-validator.test.js.map +1 -0
  105. package/dist/pipeline/post-validator.d.ts +8 -0
  106. package/dist/pipeline/post-validator.d.ts.map +1 -0
  107. package/dist/pipeline/post-validator.js +103 -0
  108. package/dist/pipeline/post-validator.js.map +1 -0
  109. package/dist/pipeline/skill-loader.d.ts +10 -0
  110. package/dist/pipeline/skill-loader.d.ts.map +1 -0
  111. package/dist/pipeline/skill-loader.js +26 -0
  112. package/dist/pipeline/skill-loader.js.map +1 -0
  113. package/dist/pipeline/skill-loader.test.d.ts +2 -0
  114. package/dist/pipeline/skill-loader.test.d.ts.map +1 -0
  115. package/dist/pipeline/skill-loader.test.js +38 -0
  116. package/dist/pipeline/skill-loader.test.js.map +1 -0
  117. package/dist/pipeline/stage-executor.d.ts +26 -0
  118. package/dist/pipeline/stage-executor.d.ts.map +1 -0
  119. package/dist/pipeline/stage-executor.js +540 -0
  120. package/dist/pipeline/stage-executor.js.map +1 -0
  121. package/dist/pipeline/stage-resolver.d.ts +3 -0
  122. package/dist/pipeline/stage-resolver.d.ts.map +1 -0
  123. package/dist/pipeline/stage-resolver.js +6 -0
  124. package/dist/pipeline/stage-resolver.js.map +1 -0
  125. package/dist/pipeline/startup-executor.d.ts +3 -0
  126. package/dist/pipeline/startup-executor.d.ts.map +1 -0
  127. package/dist/pipeline/startup-executor.js +23 -0
  128. package/dist/pipeline/startup-executor.js.map +1 -0
  129. package/dist/pipeline/startup-executor.test.d.ts +2 -0
  130. package/dist/pipeline/startup-executor.test.d.ts.map +1 -0
  131. package/dist/pipeline/startup-executor.test.js +34 -0
  132. package/dist/pipeline/startup-executor.test.js.map +1 -0
  133. package/dist/pipeline/types.d.ts +28 -0
  134. package/dist/pipeline/types.d.ts.map +1 -0
  135. package/dist/pipeline/types.js +12 -0
  136. package/dist/pipeline/types.js.map +1 -0
  137. package/dist/repo-resolver.d.ts +10 -0
  138. package/dist/repo-resolver.d.ts.map +1 -0
  139. package/dist/repo-resolver.js +39 -0
  140. package/dist/repo-resolver.js.map +1 -0
  141. package/dist/spawners/direct-engine-spawner.d.ts +8 -0
  142. package/dist/spawners/direct-engine-spawner.d.ts.map +1 -0
  143. package/dist/spawners/direct-engine-spawner.js +24 -0
  144. package/dist/spawners/direct-engine-spawner.js.map +1 -0
  145. package/dist/state/run-store.d.ts +74 -0
  146. package/dist/state/run-store.d.ts.map +1 -0
  147. package/dist/state/run-store.js +269 -0
  148. package/dist/state/run-store.js.map +1 -0
  149. package/dist/state/run-store.test.d.ts +2 -0
  150. package/dist/state/run-store.test.d.ts.map +1 -0
  151. package/dist/state/run-store.test.js +130 -0
  152. package/dist/state/run-store.test.js.map +1 -0
  153. package/dist/state/state-machine.d.ts +6 -0
  154. package/dist/state/state-machine.d.ts.map +1 -0
  155. package/dist/state/state-machine.js +27 -0
  156. package/dist/state/state-machine.js.map +1 -0
  157. package/dist/state/status-derivation.d.ts +12 -0
  158. package/dist/state/status-derivation.d.ts.map +1 -0
  159. package/dist/state/status-derivation.js +30 -0
  160. package/dist/state/status-derivation.js.map +1 -0
  161. package/package.json +7 -4
  162. package/ARCHITECTURE.md +0 -57
  163. package/src/__tests__/__fixtures__/script-stage/contracts/book-context.contract.yaml +0 -4
  164. package/src/__tests__/engine.conditions-group.test.ts +0 -244
  165. package/src/__tests__/engine.conditions.test.ts +0 -186
  166. package/src/__tests__/engine.resume.test.ts +0 -108
  167. package/src/__tests__/engine.script-stage.test.ts +0 -125
  168. package/src/engine.context-event.test.ts +0 -175
  169. package/src/engine.ts +0 -491
  170. package/src/events.ts +0 -167
  171. package/src/pipeline/agent-loader.test.ts +0 -151
  172. package/src/pipeline/agent-loader.ts +0 -39
  173. package/src/pipeline/condition-evaluator.test.ts +0 -129
  174. package/src/pipeline/condition-evaluator.ts +0 -121
  175. package/src/pipeline/context-pack-loader.ts +0 -63
  176. package/src/pipeline/context-propagation.test.ts +0 -237
  177. package/src/pipeline/context-propagation.ts +0 -235
  178. package/src/pipeline/contract-loader.ts +0 -41
  179. package/src/pipeline/group-orchestrator.ts +0 -483
  180. package/src/pipeline/hook-executor.test.ts +0 -121
  181. package/src/pipeline/hook-executor.ts +0 -87
  182. package/src/pipeline/invariants-loader.test.ts +0 -51
  183. package/src/pipeline/invariants-loader.ts +0 -14
  184. package/src/pipeline/loader.test.ts +0 -128
  185. package/src/pipeline/loader.ts +0 -149
  186. package/src/pipeline/output-validator.test.ts +0 -100
  187. package/src/pipeline/output-validator.ts +0 -40
  188. package/src/pipeline/post-validator.ts +0 -124
  189. package/src/pipeline/skill-loader.test.ts +0 -44
  190. package/src/pipeline/skill-loader.ts +0 -32
  191. package/src/pipeline/stage-executor.ts +0 -654
  192. package/src/pipeline/stage-resolver.ts +0 -8
  193. package/src/pipeline/startup-executor.test.ts +0 -37
  194. package/src/pipeline/startup-executor.ts +0 -32
  195. package/src/pipeline/types.ts +0 -42
  196. package/src/repo-resolver.ts +0 -61
  197. package/src/spawners/direct-engine-spawner.ts +0 -26
  198. package/src/state/run-store.test.ts +0 -157
  199. package/src/state/run-store.ts +0 -362
  200. package/src/state/state-machine.ts +0 -35
  201. package/src/state/status-derivation.ts +0 -36
  202. package/tests/context-pack-loader.test.ts +0 -113
  203. package/tests/context-propagation.test.ts +0 -267
  204. package/tests/direct-engine-spawner.test.ts +0 -102
  205. package/tests/e2e/feature-v5.test.ts +0 -57
  206. package/tests/events.test.ts +0 -56
  207. package/tests/fixtures/agents/test-agent.agent.yaml +0 -5
  208. package/tests/fixtures/contracts/code-gen.contract.yaml +0 -6
  209. package/tests/fixtures/contracts/qa-gate.contract.yaml +0 -15
  210. package/tests/fixtures/contracts/test-contract.contract.yaml +0 -6
  211. package/tests/fixtures/pipelines/group-test.pipeline.yaml +0 -40
  212. package/tests/fixtures/pipelines/simple.pipeline.yaml +0 -15
  213. package/tests/fixtures/pipelines/two-stage.pipeline.yaml +0 -24
  214. package/tests/fixtures/software/pipelines/feature-builder.pipeline.yaml +0 -75
  215. package/tests/fixtures/test-project/agents/test-agent.agent.yaml +0 -5
  216. package/tests/fixtures/test-project/contracts/basic-result.contract.yaml +0 -6
  217. package/tests/fixtures/test-project/contracts/code-gen.contract.yaml +0 -6
  218. package/tests/fixtures/test-project/contracts/maximum-only.contract.yaml +0 -8
  219. package/tests/fixtures/test-project/contracts/qa-gate.contract.yaml +0 -15
  220. package/tests/fixtures/test-project/contracts/strict-result.contract.yaml +0 -7
  221. package/tests/fixtures/test-project/contracts/test-contract.contract.yaml +0 -6
  222. package/tests/fixtures/test-project/pipelines/group-simple.pipeline.yaml +0 -26
  223. package/tests/fixtures/test-project/pipelines/group-test.pipeline.yaml +0 -40
  224. package/tests/fixtures/test-project/pipelines/hook-output-template.pipeline.yaml +0 -19
  225. package/tests/fixtures/test-project/pipelines/hook-reject-on-failure.pipeline.yaml +0 -19
  226. package/tests/fixtures/test-project/pipelines/maximum-only.pipeline.yaml +0 -15
  227. package/tests/fixtures/test-project/pipelines/parallel-collect-all-test.pipeline.yaml +0 -39
  228. package/tests/fixtures/test-project/pipelines/parallel-context-isolation-test.pipeline.yaml +0 -38
  229. package/tests/fixtures/test-project/pipelines/parallel-fail-test.pipeline.yaml +0 -39
  230. package/tests/fixtures/test-project/pipelines/parallel-test.pipeline.yaml +0 -39
  231. package/tests/fixtures/test-project/pipelines/parallel-then-sequential-test.pipeline.yaml +0 -38
  232. package/tests/fixtures/test-project/pipelines/simple.pipeline.yaml +0 -15
  233. package/tests/fixtures/test-project/pipelines/two-stage.pipeline.yaml +0 -24
  234. package/tests/fixtures/test-project/pipelines/with-startup.pipeline.yaml +0 -19
  235. package/tests/loader.test.ts +0 -385
  236. package/tests/post-validator.test.ts +0 -297
  237. package/tests/repo-resolver.test.ts +0 -102
  238. package/tests/run-store.test.ts +0 -143
  239. package/tests/state-machine.test.ts +0 -110
  240. package/tests/unit/context-propagation.test.ts +0 -45
  241. package/tests/unit/engine.test.ts +0 -770
  242. package/tests/unit/group-loop.test.ts +0 -389
  243. package/tests/unit/group-parallel.test.ts +0 -423
  244. package/tests/unit/state/status-derivation.test.ts +0 -88
  245. package/tsconfig.json +0 -24
  246. package/vitest.config.ts +0 -14
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-executor.test.js","sourceRoot":"","sources":["../../src/pipeline/hook-executor.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Load `.studio/invariants.md` from the project directory.
3
+ * Returns content if the file exists, undefined otherwise (non-fatal).
4
+ */
5
+ export declare function loadInvariantsFile(projectDir: string): Promise<string | undefined>;
6
+ //# sourceMappingURL=invariants-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariants-loader.d.ts","sourceRoot":"","sources":["../../src/pipeline/invariants-loader.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAMxF"}
@@ -0,0 +1,15 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Load `.studio/invariants.md` from the project directory.
5
+ * Returns content if the file exists, undefined otherwise (non-fatal).
6
+ */
7
+ export async function loadInvariantsFile(projectDir) {
8
+ try {
9
+ return await readFile(join(projectDir, 'invariants.md'), 'utf-8');
10
+ }
11
+ catch {
12
+ return undefined;
13
+ }
14
+ }
15
+ //# sourceMappingURL=invariants-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariants-loader.js","sourceRoot":"","sources":["../../src/pipeline/invariants-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB;IACzD,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=invariants-loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariants-loader.test.d.ts","sourceRoot":"","sources":["../../src/pipeline/invariants-loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { mkdir, writeFile, rm } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { loadInvariantsFile } from './invariants-loader.js';
5
+ const TMP = join('/tmp', '.studio-invariants-loader-test-' + Date.now());
6
+ const INVARIANTS_PATH = join(TMP, 'invariants.md');
7
+ describe('loadInvariantsFile', () => {
8
+ beforeAll(async () => {
9
+ await mkdir(TMP, { recursive: true });
10
+ await writeFile(INVARIANTS_PATH, '# Domain Invariants\n\nNever reproduce verbatim passages.');
11
+ });
12
+ afterAll(async () => {
13
+ await rm(TMP, { recursive: true, force: true });
14
+ });
15
+ it('returns file content when invariants.md exists', async () => {
16
+ const content = await loadInvariantsFile(TMP);
17
+ expect(content).toBe('# Domain Invariants\n\nNever reproduce verbatim passages.');
18
+ });
19
+ it('returns undefined when invariants.md does not exist', async () => {
20
+ const content = await loadInvariantsFile('/tmp/no-such-studio-dir-xyz');
21
+ expect(content).toBeUndefined();
22
+ });
23
+ });
24
+ describe('invariants content is suitable for system_prompt injection', () => {
25
+ it('non-empty content can be concatenated into a system_prompt string', async () => {
26
+ const dir = join('/tmp', '.studio-invariants-integration-' + Date.now());
27
+ await mkdir(dir, { recursive: true });
28
+ await writeFile(join(dir, 'invariants.md'), '## Invariants\n\n- Never hallucinate entity names\n- Cite sources');
29
+ const content = await loadInvariantsFile(dir);
30
+ expect(content).toBeDefined();
31
+ expect(typeof content).toBe('string');
32
+ expect(content.length).toBeGreaterThan(0);
33
+ const systemPrompt = `You are an agent.\n\n---\n\n## Project Invariants\n\n${content}`;
34
+ expect(systemPrompt).toContain('Never hallucinate entity names');
35
+ expect(systemPrompt).toContain('## Project Invariants');
36
+ await rm(dir, { recursive: true, force: true });
37
+ });
38
+ });
39
+ //# sourceMappingURL=invariants-loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariants-loader.test.js","sourceRoot":"","sources":["../../src/pipeline/invariants-loader.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,iCAAiC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACzE,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAEnD,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CAAC,eAAe,EAAE,2DAA2D,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,6BAA6B,CAAC,CAAC;QACxE,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4DAA4D,EAAE,GAAG,EAAE;IAC1E,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,iCAAiC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACzE,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAC1B,mEAAmE,CACpE,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAE9C,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,OAAQ,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAE3C,MAAM,YAAY,GAAG,wDAAwD,OAAO,EAAE,CAAC;QACvF,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACjE,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAExD,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { PipelineDefinition } from '@studio-foundation/contracts';
2
+ export declare function loadPipeline(path: string): Promise<PipelineDefinition>;
3
+ export declare function loadPipelineByName(name: string, pipelinesDir: string): Promise<PipelineDefinition>;
4
+ export declare function parsePipelineYaml(yamlContent: string, sourcePath?: string): PipelineDefinition;
5
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/pipeline/loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAA0E,MAAM,8BAA8B,CAAC;AAE/I,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAS5E;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAG7B;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,kBAAkB,CA8E9F"}
@@ -0,0 +1,132 @@
1
+ // Load pipeline definitions from YAML files
2
+ import { readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import * as yaml from 'js-yaml';
5
+ export async function loadPipeline(path) {
6
+ let content;
7
+ try {
8
+ content = await readFile(path, 'utf-8');
9
+ }
10
+ catch (err) {
11
+ throw new Error(`Failed to load pipeline at ${path}: ${err.message}`);
12
+ }
13
+ return parsePipelineYaml(content, path);
14
+ }
15
+ export async function loadPipelineByName(name, pipelinesDir) {
16
+ const path = join(pipelinesDir, `${name}.pipeline.yaml`);
17
+ return loadPipeline(path);
18
+ }
19
+ export function parsePipelineYaml(yamlContent, sourcePath) {
20
+ const parsed = yaml.load(yamlContent);
21
+ const context = sourcePath ? ` (from ${sourcePath})` : '';
22
+ if (!parsed || typeof parsed !== 'object') {
23
+ throw new Error(`Invalid pipeline YAML: expected an object${context}`);
24
+ }
25
+ if (!parsed.name || typeof parsed.name !== 'string') {
26
+ throw new Error(`Pipeline missing required field 'name'${context}`);
27
+ }
28
+ if (!Array.isArray(parsed.stages)) {
29
+ throw new Error(`Pipeline missing required field 'stages'${context}`);
30
+ }
31
+ if (parsed.stages.length === 0) {
32
+ throw new Error(`Pipeline must have at least one stage${context}`);
33
+ }
34
+ const stages = [];
35
+ for (const entry of parsed.stages) {
36
+ if (entry.group) {
37
+ // Group entry
38
+ if (!Array.isArray(entry.stages) || entry.stages.length < 2) {
39
+ throw new Error(`Group '${entry.group}' must have at least 2 stages${context}`);
40
+ }
41
+ for (const s of entry.stages) {
42
+ validateStageFields(s, context);
43
+ }
44
+ const mode = entry.mode === 'parallel' ? 'parallel' : undefined;
45
+ let maxIterations = entry.max_iterations ?? 3;
46
+ if (mode === 'parallel' && maxIterations > 1) {
47
+ console.warn(`[studio] parallel group '${entry.group}' has max_iterations > 1 — iterations are ignored in parallel mode, using 1`);
48
+ maxIterations = 1;
49
+ }
50
+ stages.push({
51
+ group: entry.group,
52
+ max_iterations: maxIterations,
53
+ ...(mode ? { mode } : {}),
54
+ ...(entry.on_failure ? { on_failure: entry.on_failure } : {}),
55
+ stages: entry.stages.map((s) => ({ ...s, hooks: parseStageHooks(s) })),
56
+ });
57
+ }
58
+ else {
59
+ // Simple stage
60
+ validateStageFields(entry, context);
61
+ stages.push({ ...entry, hooks: parseStageHooks(entry) });
62
+ }
63
+ }
64
+ // Parse on_pipeline_start commands
65
+ let on_pipeline_start;
66
+ if (Array.isArray(parsed.on_pipeline_start)) {
67
+ on_pipeline_start = [];
68
+ for (const cmd of parsed.on_pipeline_start) {
69
+ if (!cmd.command || typeof cmd.command !== 'string') {
70
+ throw new Error(`on_pipeline_start entry missing 'command'${context}`);
71
+ }
72
+ if (!cmd.inject_as || typeof cmd.inject_as !== 'string') {
73
+ throw new Error(`on_pipeline_start entry missing 'inject_as'${context}`);
74
+ }
75
+ on_pipeline_start.push({ command: cmd.command, inject_as: cmd.inject_as });
76
+ }
77
+ if (on_pipeline_start.length === 0) {
78
+ on_pipeline_start = undefined;
79
+ }
80
+ }
81
+ return {
82
+ ...parsed,
83
+ stages,
84
+ on_pipeline_start,
85
+ };
86
+ }
87
+ function parseStageHooks(entry) {
88
+ if (!entry.hooks || typeof entry.hooks !== 'object')
89
+ return undefined;
90
+ const h = entry.hooks;
91
+ const result = {};
92
+ if (Array.isArray(h.on_stage_start)) {
93
+ result.on_stage_start = h.on_stage_start.map((hk) => ({
94
+ command: String(hk.command ?? ''),
95
+ on_failure: hk.on_failure ?? 'warn',
96
+ }));
97
+ }
98
+ if (Array.isArray(h.on_stage_complete)) {
99
+ result.on_stage_complete = h.on_stage_complete.map((hk) => ({
100
+ command: String(hk.command ?? ''),
101
+ on_failure: hk.on_failure ?? 'warn',
102
+ }));
103
+ }
104
+ if (Array.isArray(h.pre_tool_use)) {
105
+ result.pre_tool_use = h.pre_tool_use.map((hk) => ({
106
+ matcher: String(hk.matcher ?? ''),
107
+ command: String(hk.command ?? ''),
108
+ on_failure: hk.on_failure ?? 'warn',
109
+ }));
110
+ }
111
+ if (Array.isArray(h.post_tool_use)) {
112
+ result.post_tool_use = h.post_tool_use.map((hk) => ({
113
+ matcher: String(hk.matcher ?? ''),
114
+ command: String(hk.command ?? ''),
115
+ on_failure: hk.on_failure ?? 'warn',
116
+ }));
117
+ }
118
+ const hasAny = result.on_stage_start || result.on_stage_complete
119
+ || result.pre_tool_use || result.post_tool_use;
120
+ return hasAny ? result : undefined;
121
+ }
122
+ function validateStageFields(stage, context) {
123
+ if (!stage.name)
124
+ throw new Error(`Stage missing 'name'${context}`);
125
+ if (!stage.kind && !stage.executor)
126
+ throw new Error(`Stage '${stage.name}' missing 'kind'${context}`);
127
+ if (!stage.agent && stage.executor !== 'script')
128
+ throw new Error(`Stage '${stage.name}' missing 'agent'${context}`);
129
+ if (stage.executor === 'script' && !stage.script)
130
+ throw new Error(`Stage '${stage.name}' missing 'script' (required when executor: 'script')${context}`);
131
+ }
132
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/pipeline/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.d.ts","sourceRoot":"","sources":["../../src/pipeline/loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parsePipelineYaml } from './loader.js';
3
+ const MINIMAL_STAGE = `
4
+ - name: analyze
5
+ kind: analysis
6
+ agent: analyst
7
+ `;
8
+ describe('parsePipelineYaml — on_pipeline_start', () => {
9
+ it('parses on_pipeline_start commands', () => {
10
+ const yaml = `
11
+ name: test-pipeline
12
+ description: test
13
+ version: 1
14
+ on_pipeline_start:
15
+ - command: "git status --short"
16
+ inject_as: git_status
17
+ - command: "git log --oneline -5"
18
+ inject_as: recent_commits
19
+ stages:
20
+ ${MINIMAL_STAGE}
21
+ `;
22
+ const result = parsePipelineYaml(yaml);
23
+ expect(result.on_pipeline_start).toEqual([
24
+ { command: 'git status --short', inject_as: 'git_status' },
25
+ { command: 'git log --oneline -5', inject_as: 'recent_commits' },
26
+ ]);
27
+ });
28
+ it('returns undefined on_pipeline_start when absent', () => {
29
+ const yaml = `
30
+ name: test-pipeline
31
+ description: test
32
+ version: 1
33
+ stages:
34
+ ${MINIMAL_STAGE}
35
+ `;
36
+ const result = parsePipelineYaml(yaml);
37
+ expect(result.on_pipeline_start).toBeUndefined();
38
+ });
39
+ it('throws when on_pipeline_start entry is missing command', () => {
40
+ const yaml = `
41
+ name: test-pipeline
42
+ description: test
43
+ version: 1
44
+ on_pipeline_start:
45
+ - inject_as: git_status
46
+ stages:
47
+ ${MINIMAL_STAGE}
48
+ `;
49
+ expect(() => parsePipelineYaml(yaml)).toThrow("on_pipeline_start entry missing 'command'");
50
+ });
51
+ it('throws when on_pipeline_start entry is missing inject_as', () => {
52
+ const yaml = `
53
+ name: test-pipeline
54
+ description: test
55
+ version: 1
56
+ on_pipeline_start:
57
+ - command: "git status"
58
+ stages:
59
+ ${MINIMAL_STAGE}
60
+ `;
61
+ expect(() => parsePipelineYaml(yaml)).toThrow("on_pipeline_start entry missing 'inject_as'");
62
+ });
63
+ });
64
+ const PIPELINE_WITH_HOOKS = `
65
+ name: test-pipeline
66
+ description: test
67
+ version: 1
68
+ stages:
69
+ - name: code-gen
70
+ kind: code
71
+ agent: coder
72
+ hooks:
73
+ on_stage_start:
74
+ - command: "git stash"
75
+ on_failure: warn
76
+ on_stage_complete:
77
+ - command: "npx tsc --noEmit"
78
+ on_failure: reject
79
+ pre_tool_use:
80
+ - matcher: "repo_manager-write_file"
81
+ command: "echo pre {{tool.path}}"
82
+ on_failure: warn
83
+ post_tool_use:
84
+ - matcher: "repo_manager-write_file"
85
+ command: "npx prettier --write {{tool.path}}"
86
+ on_failure: warn
87
+ `;
88
+ describe('parsePipelineYaml — stage hooks', () => {
89
+ it('parses all four hook types from a stage', () => {
90
+ const result = parsePipelineYaml(PIPELINE_WITH_HOOKS);
91
+ const stage = result.stages[0];
92
+ expect(stage.hooks?.on_stage_start).toEqual([
93
+ { command: 'git stash', on_failure: 'warn' },
94
+ ]);
95
+ expect(stage.hooks?.on_stage_complete).toEqual([
96
+ { command: 'npx tsc --noEmit', on_failure: 'reject' },
97
+ ]);
98
+ expect(stage.hooks?.pre_tool_use).toEqual([
99
+ { matcher: 'repo_manager-write_file', command: 'echo pre {{tool.path}}', on_failure: 'warn' },
100
+ ]);
101
+ expect(stage.hooks?.post_tool_use).toEqual([
102
+ { matcher: 'repo_manager-write_file', command: 'npx prettier --write {{tool.path}}', on_failure: 'warn' },
103
+ ]);
104
+ });
105
+ it('returns undefined hooks when stage has no hooks', () => {
106
+ const yaml = `
107
+ name: test-pipeline
108
+ description: test
109
+ version: 1
110
+ stages:
111
+ - name: analyze
112
+ kind: analysis
113
+ agent: analyst
114
+ `;
115
+ const result = parsePipelineYaml(yaml);
116
+ const stage = result.stages[0];
117
+ expect(stage.hooks).toBeUndefined();
118
+ });
119
+ });
120
+ //# sourceMappingURL=loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../src/pipeline/loader.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { OutputContract, ToolCall } from '@studio-foundation/contracts';
2
+ import { type PostValidationResult } from './post-validator.js';
3
+ export interface OutputValidationResult {
4
+ valid: boolean;
5
+ errors: string[];
6
+ warnings: string[];
7
+ post_validation: PostValidationResult;
8
+ }
9
+ export declare function validateOutput(contract: OutputContract, output: unknown, toolCalls?: ToolCall[]): OutputValidationResult;
10
+ //# sourceMappingURL=output-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-validator.d.ts","sourceRoot":"","sources":["../../src/pipeline/output-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAQ7E,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,oBAAoB,CAAC;CACvC;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,OAAO,EACf,SAAS,GAAE,QAAQ,EAAO,GACzB,sBAAsB,CAkBxB"}
@@ -0,0 +1,20 @@
1
+ import { validateSchema, validateToolCalls, validateRequiredTools, validateCountedTools, validateToolGroups, } from '@studio-foundation/ralph';
2
+ import { postValidate } from './post-validator.js';
3
+ export function validateOutput(contract, output, toolCalls = []) {
4
+ const results = [
5
+ validateSchema(output, contract),
6
+ validateToolCalls(toolCalls, contract.tool_calls),
7
+ validateRequiredTools(toolCalls, contract.tool_calls),
8
+ validateCountedTools(toolCalls, contract.tool_calls),
9
+ validateToolGroups(toolCalls, contract.tool_calls),
10
+ ];
11
+ const errors = results.flatMap(r => r.errors);
12
+ const warnings = results.flatMap(r => r.warnings);
13
+ return {
14
+ valid: errors.length === 0,
15
+ errors,
16
+ warnings,
17
+ post_validation: postValidate(output, contract),
18
+ };
19
+ }
20
+ //# sourceMappingURL=output-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-validator.js","sourceRoot":"","sources":["../../src/pipeline/output-validator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAA6B,MAAM,qBAAqB,CAAC;AAS9E,MAAM,UAAU,cAAc,CAC5B,QAAwB,EACxB,MAAe,EACf,YAAwB,EAAE;IAE1B,MAAM,OAAO,GAAG;QACd,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC;QAChC,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC;QACjD,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC;QACrD,oBAAoB,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC;QACpD,kBAAkB,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC;KACnD,CAAC;IAEF,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAElD,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ;QACR,eAAe,EAAE,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC;KAChD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=output-validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-validator.test.d.ts","sourceRoot":"","sources":["../../src/pipeline/output-validator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateOutput } from './output-validator.js';
3
+ const schemaOnlyContract = {
4
+ name: 'test-schema',
5
+ version: 1,
6
+ schema: { required_fields: ['summary', 'files_changed'] },
7
+ };
8
+ const toolCallContract = {
9
+ name: 'test-tool-calls',
10
+ version: 1,
11
+ schema: { required_fields: ['summary'] },
12
+ tool_calls: { minimum: 1, required_tools: ['repo_manager-write_file'] },
13
+ };
14
+ const postValidationContract = {
15
+ name: 'test-post-validation',
16
+ version: 1,
17
+ schema: { required_fields: ['status'] },
18
+ post_validation: {
19
+ rejection_detection: {
20
+ field: 'status',
21
+ approved_values: ['approved'],
22
+ rejected_values: ['rejected'],
23
+ },
24
+ },
25
+ };
26
+ describe('validateOutput', () => {
27
+ describe('schema validation', () => {
28
+ it('returns valid: true when all required fields are present', () => {
29
+ const result = validateOutput(schemaOnlyContract, { summary: 'ok', files_changed: ['a.ts'] });
30
+ expect(result.valid).toBe(true);
31
+ expect(result.errors).toEqual([]);
32
+ });
33
+ it('returns valid: false with error when required field is missing', () => {
34
+ const result = validateOutput(schemaOnlyContract, { summary: 'ok' });
35
+ expect(result.valid).toBe(false);
36
+ expect(result.errors).toContain('Missing required field: files_changed');
37
+ });
38
+ it('returns valid: false when output is not an object', () => {
39
+ const result = validateOutput(schemaOnlyContract, 'not an object');
40
+ expect(result.valid).toBe(false);
41
+ });
42
+ });
43
+ describe('tool_calls validation', () => {
44
+ it('returns valid: false when minimum not met (empty tool_calls)', () => {
45
+ const result = validateOutput(toolCallContract, { summary: 'ok' }, []);
46
+ expect(result.valid).toBe(false);
47
+ expect(result.errors.some(e => e.includes('at least 1 successful tool call'))).toBe(true);
48
+ });
49
+ it('returns valid: false when required tool was not called', () => {
50
+ const result = validateOutput(toolCallContract, { summary: 'ok' }, [{ id: 'call-1', name: 'shell-run_command', arguments: {}, result: 'ok' }]);
51
+ expect(result.valid).toBe(false);
52
+ expect(result.errors.some(e => e.includes('repo_manager-write_file'))).toBe(true);
53
+ });
54
+ it('returns valid: true when required tool was called successfully', () => {
55
+ const result = validateOutput(toolCallContract, { summary: 'ok' }, [{ id: 'call-2', name: 'repo_manager-write_file', arguments: { path: 'a.ts' }, result: 'ok' }]);
56
+ expect(result.valid).toBe(true);
57
+ expect(result.errors).toEqual([]);
58
+ });
59
+ });
60
+ describe('post_validation', () => {
61
+ it('accepted: true when approved value present', () => {
62
+ const result = validateOutput(postValidationContract, { status: 'approved' });
63
+ expect(result.valid).toBe(true);
64
+ });
65
+ it('accepted: false with rejection_reason when rejected value present', () => {
66
+ const result = validateOutput(postValidationContract, { status: 'rejected' });
67
+ expect(result.post_validation.accepted).toBe(false);
68
+ expect(result.post_validation.rejection_reason).toBeTruthy();
69
+ });
70
+ it('post_validation runs independently of schema validity', () => {
71
+ const result = validateOutput({ ...postValidationContract, schema: { required_fields: ['summary', 'status'] } }, { status: 'rejected' });
72
+ expect(result.valid).toBe(false);
73
+ expect(result.post_validation.accepted).toBe(false);
74
+ });
75
+ });
76
+ });
77
+ //# sourceMappingURL=output-validator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-validator.test.js","sourceRoot":"","sources":["../../src/pipeline/output-validator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { OutputContract } from '@studio-foundation/contracts';
2
+ export interface PostValidationResult {
3
+ accepted: boolean;
4
+ rejection_reason?: string;
5
+ rejection_details?: string[];
6
+ }
7
+ export declare function postValidate(output: unknown, contract: OutputContract): PostValidationResult;
8
+ //# sourceMappingURL=post-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-validator.d.ts","sourceRoot":"","sources":["../../src/pipeline/post-validator.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,cAAc,GACvB,oBAAoB,CAwGtB"}
@@ -0,0 +1,103 @@
1
+ // Post-validation sémantique
2
+ //
3
+ // Vérifie le CONTENU de l'output après que ralph a validé le FORMAT.
4
+ // Utilisé pour les stages avec une gate d'approbation — l'agent peut
5
+ // retourner un JSON valide qui dit quand même "non".
6
+ //
7
+ // Configuré dans le contract YAML via la section "post_validation".
8
+ export function postValidate(output, contract) {
9
+ // No post_validation config → everything is accepted
10
+ if (!contract.post_validation?.rejection_detection) {
11
+ return { accepted: true };
12
+ }
13
+ const { field, approved_values, rejected_values, details_field, summary_field, reject_if_non_empty } = contract.post_validation.rejection_detection;
14
+ if (!field) {
15
+ return { accepted: true };
16
+ }
17
+ // Extract field value from output
18
+ if (!output || typeof output !== 'object') {
19
+ return { accepted: true };
20
+ }
21
+ const o = output;
22
+ const actualValue = o[field];
23
+ if (typeof actualValue !== 'string') {
24
+ return { accepted: true };
25
+ }
26
+ // Check reject_if_non_empty before approved_values — a non-empty field always rejects
27
+ if (reject_if_non_empty) {
28
+ const fieldValue = o[reject_if_non_empty];
29
+ if (Array.isArray(fieldValue) && fieldValue.length > 0) {
30
+ const details = fieldValue
31
+ .map(item => {
32
+ if (typeof item === 'string')
33
+ return item;
34
+ if (typeof item !== 'object' || item === null)
35
+ return undefined;
36
+ const obj = item;
37
+ const text = obj.description ?? obj.issue ?? obj.message ?? obj.text ?? obj.error;
38
+ if (typeof text === 'string' && text.length > 0)
39
+ return text;
40
+ return Object.values(obj).find((v) => typeof v === 'string' && v.length > 0);
41
+ })
42
+ .filter((d) => typeof d === 'string');
43
+ const summary = summary_field && typeof o[summary_field] === 'string'
44
+ ? o[summary_field]
45
+ : undefined;
46
+ return {
47
+ accepted: false,
48
+ rejection_reason: `Rejected: ${reject_if_non_empty} is non-empty (${fieldValue.length} items)${summary ? `. ${summary}` : ''}`,
49
+ rejection_details: details.length > 0 ? details : undefined,
50
+ };
51
+ }
52
+ }
53
+ // Check approved values (if specified)
54
+ if (approved_values?.length && approved_values.includes(actualValue)) {
55
+ return { accepted: true };
56
+ }
57
+ // Check rejected values (if specified)
58
+ if (rejected_values?.length && !rejected_values.includes(actualValue)) {
59
+ // Value is not in rejected list and no approved list matched → accept
60
+ if (!approved_values?.length) {
61
+ return { accepted: true };
62
+ }
63
+ }
64
+ // If we have approved_values and the value isn't in them → rejected
65
+ // If we have rejected_values and the value is in them → rejected
66
+ // Extract details from configured field
67
+ const details = [];
68
+ if (details_field) {
69
+ const detailsValue = o[details_field];
70
+ if (typeof detailsValue === 'string' && detailsValue.length > 0) {
71
+ details.push(detailsValue);
72
+ }
73
+ else if (Array.isArray(detailsValue)) {
74
+ for (const item of detailsValue) {
75
+ if (typeof item === 'string') {
76
+ details.push(item);
77
+ }
78
+ else if (typeof item === 'object' && item !== null) {
79
+ const obj = item;
80
+ const text = obj.description ?? obj.issue ?? obj.message ?? obj.text ?? obj.error;
81
+ if (typeof text === 'string' && text.length > 0) {
82
+ details.push(text);
83
+ }
84
+ else {
85
+ const firstStr = Object.values(obj).find((v) => typeof v === 'string' && v.length > 0);
86
+ if (firstStr !== undefined)
87
+ details.push(firstStr);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ // Extract summary from configured field
94
+ const summary = summary_field && typeof o[summary_field] === 'string'
95
+ ? o[summary_field]
96
+ : undefined;
97
+ return {
98
+ accepted: false,
99
+ rejection_reason: `Rejected: ${field} = "${actualValue}" (expected: ${(approved_values ?? []).join(' or ')})${summary ? `. ${summary}` : ''}`,
100
+ rejection_details: details.length > 0 ? details : undefined,
101
+ };
102
+ }
103
+ //# sourceMappingURL=post-validator.js.map