patchwork-os 0.2.0-alpha.2 → 0.2.0-alpha.22

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 (281) hide show
  1. package/README.bridge.md +6 -0
  2. package/README.md +40 -15
  3. package/deploy/bootstrap-vps.sh +184 -0
  4. package/dist/approvalHttp.d.ts +11 -2
  5. package/dist/approvalHttp.js +98 -10
  6. package/dist/approvalHttp.js.map +1 -1
  7. package/dist/approvalQueue.d.ts +12 -1
  8. package/dist/approvalQueue.js +25 -3
  9. package/dist/approvalQueue.js.map +1 -1
  10. package/dist/automation.d.ts +20 -0
  11. package/dist/automation.js +35 -0
  12. package/dist/automation.js.map +1 -1
  13. package/dist/bridge.js +145 -23
  14. package/dist/bridge.js.map +1 -1
  15. package/dist/bridgeToken.js +57 -19
  16. package/dist/bridgeToken.js.map +1 -1
  17. package/dist/claudeDriver.d.ts +3 -1
  18. package/dist/claudeDriver.js +48 -0
  19. package/dist/claudeDriver.js.map +1 -1
  20. package/dist/claudeOrchestrator.d.ts +1 -1
  21. package/dist/claudeOrchestrator.js +14 -8
  22. package/dist/claudeOrchestrator.js.map +1 -1
  23. package/dist/commands/launchd.d.ts +2 -0
  24. package/dist/commands/launchd.js +94 -0
  25. package/dist/commands/launchd.js.map +1 -0
  26. package/dist/commands/recipe.d.ts +256 -0
  27. package/dist/commands/recipe.js +1313 -0
  28. package/dist/commands/recipe.js.map +1 -0
  29. package/dist/config.d.ts +15 -2
  30. package/dist/config.js +94 -8
  31. package/dist/config.js.map +1 -1
  32. package/dist/connectors/baseConnector.d.ts +117 -0
  33. package/dist/connectors/baseConnector.js +213 -0
  34. package/dist/connectors/baseConnector.js.map +1 -0
  35. package/dist/connectors/confluence.d.ts +111 -0
  36. package/dist/connectors/confluence.js +406 -0
  37. package/dist/connectors/confluence.js.map +1 -0
  38. package/dist/connectors/fixtureLibrary.d.ts +21 -0
  39. package/dist/connectors/fixtureLibrary.js +70 -0
  40. package/dist/connectors/fixtureLibrary.js.map +1 -0
  41. package/dist/connectors/fixtureRecorder.d.ts +1 -0
  42. package/dist/connectors/fixtureRecorder.js +35 -0
  43. package/dist/connectors/fixtureRecorder.js.map +1 -0
  44. package/dist/connectors/github.d.ts +58 -8
  45. package/dist/connectors/github.js +312 -84
  46. package/dist/connectors/github.js.map +1 -1
  47. package/dist/connectors/gmail.d.ts +4 -1
  48. package/dist/connectors/gmail.js +93 -16
  49. package/dist/connectors/gmail.js.map +1 -1
  50. package/dist/connectors/googleCalendar.d.ts +60 -0
  51. package/dist/connectors/googleCalendar.js +345 -0
  52. package/dist/connectors/googleCalendar.js.map +1 -0
  53. package/dist/connectors/jira.d.ts +98 -0
  54. package/dist/connectors/jira.js +379 -0
  55. package/dist/connectors/jira.js.map +1 -0
  56. package/dist/connectors/linear.d.ts +117 -0
  57. package/dist/connectors/linear.js +239 -0
  58. package/dist/connectors/linear.js.map +1 -0
  59. package/dist/connectors/mcpClient.d.ts +56 -0
  60. package/dist/connectors/mcpClient.js +189 -0
  61. package/dist/connectors/mcpClient.js.map +1 -0
  62. package/dist/connectors/mcpOAuth.d.ts +84 -0
  63. package/dist/connectors/mcpOAuth.js +389 -0
  64. package/dist/connectors/mcpOAuth.js.map +1 -0
  65. package/dist/connectors/mockConnector.d.ts +28 -0
  66. package/dist/connectors/mockConnector.js +81 -0
  67. package/dist/connectors/mockConnector.js.map +1 -0
  68. package/dist/connectors/notion.d.ts +143 -0
  69. package/dist/connectors/notion.js +424 -0
  70. package/dist/connectors/notion.js.map +1 -0
  71. package/dist/connectors/sentry.d.ts +43 -0
  72. package/dist/connectors/sentry.js +188 -0
  73. package/dist/connectors/sentry.js.map +1 -0
  74. package/dist/connectors/slack.d.ts +50 -0
  75. package/dist/connectors/slack.js +324 -0
  76. package/dist/connectors/slack.js.map +1 -0
  77. package/dist/connectors/tokenStorage.d.ts +35 -0
  78. package/dist/connectors/tokenStorage.js +394 -0
  79. package/dist/connectors/tokenStorage.js.map +1 -0
  80. package/dist/connectors/zendesk.d.ts +104 -0
  81. package/dist/connectors/zendesk.js +424 -0
  82. package/dist/connectors/zendesk.js.map +1 -0
  83. package/dist/drivers/claude/api.d.ts +11 -0
  84. package/dist/drivers/claude/api.js +54 -0
  85. package/dist/drivers/claude/api.js.map +1 -0
  86. package/dist/drivers/claude/envSanitizer.d.ts +7 -0
  87. package/dist/drivers/claude/envSanitizer.js +18 -0
  88. package/dist/drivers/claude/envSanitizer.js.map +1 -0
  89. package/dist/drivers/claude/streamParser.d.ts +38 -0
  90. package/dist/drivers/claude/streamParser.js +34 -0
  91. package/dist/drivers/claude/streamParser.js.map +1 -0
  92. package/dist/drivers/claude/subprocess.d.ts +19 -0
  93. package/dist/drivers/claude/subprocess.js +216 -0
  94. package/dist/drivers/claude/subprocess.js.map +1 -0
  95. package/dist/drivers/claude/subprocessSettings.d.ts +9 -0
  96. package/dist/drivers/claude/subprocessSettings.js +55 -0
  97. package/dist/drivers/claude/subprocessSettings.js.map +1 -0
  98. package/dist/drivers/gemini/index.d.ts +18 -0
  99. package/dist/drivers/gemini/index.js +210 -0
  100. package/dist/drivers/gemini/index.js.map +1 -0
  101. package/dist/drivers/grok/index.d.ts +11 -0
  102. package/dist/drivers/grok/index.js +22 -0
  103. package/dist/drivers/grok/index.js.map +1 -0
  104. package/dist/drivers/index.d.ts +23 -0
  105. package/dist/drivers/index.js +31 -0
  106. package/dist/drivers/index.js.map +1 -0
  107. package/dist/drivers/openai/index.d.ts +24 -0
  108. package/dist/drivers/openai/index.js +110 -0
  109. package/dist/drivers/openai/index.js.map +1 -0
  110. package/dist/drivers/types.d.ts +72 -0
  111. package/dist/drivers/types.js +30 -0
  112. package/dist/drivers/types.js.map +1 -0
  113. package/dist/featureFlags.d.ts +73 -0
  114. package/dist/featureFlags.js +203 -0
  115. package/dist/featureFlags.js.map +1 -0
  116. package/dist/fp/automationInterpreter.js +1 -0
  117. package/dist/fp/automationInterpreter.js.map +1 -1
  118. package/dist/fp/automationProgram.d.ts +1 -1
  119. package/dist/fp/automationProgram.js.map +1 -1
  120. package/dist/fp/policyParser.js +17 -0
  121. package/dist/fp/policyParser.js.map +1 -1
  122. package/dist/index.js +543 -37
  123. package/dist/index.js.map +1 -1
  124. package/dist/installGuard.d.ts +25 -0
  125. package/dist/installGuard.js +48 -0
  126. package/dist/installGuard.js.map +1 -0
  127. package/dist/oauth.d.ts +4 -1
  128. package/dist/oauth.js +50 -14
  129. package/dist/oauth.js.map +1 -1
  130. package/dist/patchworkConfig.d.ts +9 -0
  131. package/dist/patchworkConfig.js.map +1 -1
  132. package/dist/recipes/chainedRunner.d.ts +104 -0
  133. package/dist/recipes/chainedRunner.js +359 -0
  134. package/dist/recipes/chainedRunner.js.map +1 -0
  135. package/dist/recipes/dependencyGraph.d.ts +39 -0
  136. package/dist/recipes/dependencyGraph.js +199 -0
  137. package/dist/recipes/dependencyGraph.js.map +1 -0
  138. package/dist/recipes/legacyRecipeCompat.d.ts +1 -0
  139. package/dist/recipes/legacyRecipeCompat.js +97 -0
  140. package/dist/recipes/legacyRecipeCompat.js.map +1 -0
  141. package/dist/recipes/nestedRecipeStep.d.ts +58 -0
  142. package/dist/recipes/nestedRecipeStep.js +95 -0
  143. package/dist/recipes/nestedRecipeStep.js.map +1 -0
  144. package/dist/recipes/outputRegistry.d.ts +28 -0
  145. package/dist/recipes/outputRegistry.js +52 -0
  146. package/dist/recipes/outputRegistry.js.map +1 -0
  147. package/dist/recipes/scheduler.d.ts +23 -7
  148. package/dist/recipes/scheduler.js +135 -41
  149. package/dist/recipes/scheduler.js.map +1 -1
  150. package/dist/recipes/schemaGenerator.d.ts +28 -0
  151. package/dist/recipes/schemaGenerator.js +484 -0
  152. package/dist/recipes/schemaGenerator.js.map +1 -0
  153. package/dist/recipes/templateEngine.d.ts +62 -0
  154. package/dist/recipes/templateEngine.js +182 -0
  155. package/dist/recipes/templateEngine.js.map +1 -0
  156. package/dist/recipes/toolRegistry.d.ts +181 -0
  157. package/dist/recipes/toolRegistry.js +300 -0
  158. package/dist/recipes/toolRegistry.js.map +1 -0
  159. package/dist/recipes/tools/calendar.d.ts +6 -0
  160. package/dist/recipes/tools/calendar.js +61 -0
  161. package/dist/recipes/tools/calendar.js.map +1 -0
  162. package/dist/recipes/tools/confluence.d.ts +6 -0
  163. package/dist/recipes/tools/confluence.js +254 -0
  164. package/dist/recipes/tools/confluence.js.map +1 -0
  165. package/dist/recipes/tools/diagnostics.d.ts +6 -0
  166. package/dist/recipes/tools/diagnostics.js +36 -0
  167. package/dist/recipes/tools/diagnostics.js.map +1 -0
  168. package/dist/recipes/tools/file.d.ts +6 -0
  169. package/dist/recipes/tools/file.js +170 -0
  170. package/dist/recipes/tools/file.js.map +1 -0
  171. package/dist/recipes/tools/git.d.ts +6 -0
  172. package/dist/recipes/tools/git.js +63 -0
  173. package/dist/recipes/tools/git.js.map +1 -0
  174. package/dist/recipes/tools/github.d.ts +6 -0
  175. package/dist/recipes/tools/github.js +91 -0
  176. package/dist/recipes/tools/github.js.map +1 -0
  177. package/dist/recipes/tools/gmail.d.ts +6 -0
  178. package/dist/recipes/tools/gmail.js +210 -0
  179. package/dist/recipes/tools/gmail.js.map +1 -0
  180. package/dist/recipes/tools/index.d.ts +18 -0
  181. package/dist/recipes/tools/index.js +21 -0
  182. package/dist/recipes/tools/index.js.map +1 -0
  183. package/dist/recipes/tools/linear.d.ts +6 -0
  184. package/dist/recipes/tools/linear.js +83 -0
  185. package/dist/recipes/tools/linear.js.map +1 -0
  186. package/dist/recipes/tools/notion.d.ts +6 -0
  187. package/dist/recipes/tools/notion.js +278 -0
  188. package/dist/recipes/tools/notion.js.map +1 -0
  189. package/dist/recipes/tools/slack.d.ts +6 -0
  190. package/dist/recipes/tools/slack.js +72 -0
  191. package/dist/recipes/tools/slack.js.map +1 -0
  192. package/dist/recipes/tools/zendesk.d.ts +6 -0
  193. package/dist/recipes/tools/zendesk.js +245 -0
  194. package/dist/recipes/tools/zendesk.js.map +1 -0
  195. package/dist/recipes/yamlRunner.d.ts +79 -0
  196. package/dist/recipes/yamlRunner.js +612 -346
  197. package/dist/recipes/yamlRunner.js.map +1 -1
  198. package/dist/recipesHttp.d.ts +14 -1
  199. package/dist/recipesHttp.js +21 -4
  200. package/dist/recipesHttp.js.map +1 -1
  201. package/dist/riskTier.js +1 -0
  202. package/dist/riskTier.js.map +1 -1
  203. package/dist/runLog.d.ts +23 -0
  204. package/dist/runLog.js +56 -1
  205. package/dist/runLog.js.map +1 -1
  206. package/dist/server.d.ts +19 -1
  207. package/dist/server.js +682 -31
  208. package/dist/server.js.map +1 -1
  209. package/dist/streamableHttp.js +2 -0
  210. package/dist/streamableHttp.js.map +1 -1
  211. package/dist/tools/addLinearComment.d.ts +55 -0
  212. package/dist/tools/addLinearComment.js +72 -0
  213. package/dist/tools/addLinearComment.js.map +1 -0
  214. package/dist/tools/bridgeDoctor.js +2 -2
  215. package/dist/tools/bridgeDoctor.js.map +1 -1
  216. package/dist/tools/createLinearIssue.d.ts +84 -0
  217. package/dist/tools/createLinearIssue.js +146 -0
  218. package/dist/tools/createLinearIssue.js.map +1 -0
  219. package/dist/tools/ctxGetTaskContext.d.ts +4 -1
  220. package/dist/tools/ctxGetTaskContext.js +45 -2
  221. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  222. package/dist/tools/fetchCalendarEvents.d.ts +94 -0
  223. package/dist/tools/fetchCalendarEvents.js +97 -0
  224. package/dist/tools/fetchCalendarEvents.js.map +1 -0
  225. package/dist/tools/fetchGithubIssue.d.ts +80 -0
  226. package/dist/tools/fetchGithubIssue.js +84 -0
  227. package/dist/tools/fetchGithubIssue.js.map +1 -0
  228. package/dist/tools/fetchGithubPR.d.ts +89 -0
  229. package/dist/tools/fetchGithubPR.js +96 -0
  230. package/dist/tools/fetchGithubPR.js.map +1 -0
  231. package/dist/tools/fetchLinearIssue.d.ts +112 -0
  232. package/dist/tools/fetchLinearIssue.js +129 -0
  233. package/dist/tools/fetchLinearIssue.js.map +1 -0
  234. package/dist/tools/fetchSentryIssue.d.ts +143 -0
  235. package/dist/tools/fetchSentryIssue.js +150 -0
  236. package/dist/tools/fetchSentryIssue.js.map +1 -0
  237. package/dist/tools/fetchSlackProfile.d.ts +43 -0
  238. package/dist/tools/fetchSlackProfile.js +46 -0
  239. package/dist/tools/fetchSlackProfile.js.map +1 -0
  240. package/dist/tools/getConnectorStatus.d.ts +58 -0
  241. package/dist/tools/getConnectorStatus.js +56 -0
  242. package/dist/tools/getConnectorStatus.js.map +1 -0
  243. package/dist/tools/github/actions.js +4 -2
  244. package/dist/tools/github/actions.js.map +1 -1
  245. package/dist/tools/github/composite.d.ts +339 -0
  246. package/dist/tools/github/composite.js +343 -0
  247. package/dist/tools/github/composite.js.map +1 -0
  248. package/dist/tools/github/index.d.ts +2 -1
  249. package/dist/tools/github/index.js +2 -1
  250. package/dist/tools/github/index.js.map +1 -1
  251. package/dist/tools/github/issues.js +8 -4
  252. package/dist/tools/github/issues.js.map +1 -1
  253. package/dist/tools/github/pr.d.ts +122 -0
  254. package/dist/tools/github/pr.js +195 -5
  255. package/dist/tools/github/pr.js.map +1 -1
  256. package/dist/tools/index.js +36 -1
  257. package/dist/tools/index.js.map +1 -1
  258. package/dist/tools/searchTools.js +1 -1
  259. package/dist/tools/searchTools.js.map +1 -1
  260. package/dist/tools/slackListChannels.d.ts +65 -0
  261. package/dist/tools/slackListChannels.js +70 -0
  262. package/dist/tools/slackListChannels.js.map +1 -0
  263. package/dist/tools/slackPostMessage.d.ts +57 -0
  264. package/dist/tools/slackPostMessage.js +77 -0
  265. package/dist/tools/slackPostMessage.js.map +1 -0
  266. package/dist/tools/updateLinearIssue.d.ts +89 -0
  267. package/dist/tools/updateLinearIssue.js +117 -0
  268. package/dist/tools/updateLinearIssue.js.map +1 -0
  269. package/dist/transport.d.ts +7 -1
  270. package/dist/transport.js +85 -11
  271. package/dist/transport.js.map +1 -1
  272. package/package.json +4 -2
  273. package/scripts/start-all.sh +56 -19
  274. package/templates/automation-policies/recipe-authoring.json +25 -0
  275. package/templates/automation-policy.example.json +6 -0
  276. package/templates/co.patchwork-os.bridge.plist +34 -0
  277. package/templates/recipes/ctx-loop-test.yaml +75 -0
  278. package/templates/recipes/lint-on-save.yaml +1 -2
  279. package/templates/recipes/morning-brief-slack.yaml +57 -0
  280. package/templates/recipes/morning-brief.yaml +21 -5
  281. package/templates/recipes/sentry-to-linear.yaml +77 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * DependencyGraph — builds and executes recipe step dependencies.
3
+ *
4
+ * Supports:
5
+ * - Parallel execution up to maxConcurrency
6
+ * - Explicit dependencies (awaits: [stepA, stepB])
7
+ * - Conditional execution (when: condition)
8
+ * - Cycle detection
9
+ */
10
+ export interface StepDependency {
11
+ stepId: string;
12
+ awaits: string[];
13
+ index: number;
14
+ }
15
+ export interface DependencyGraph {
16
+ steps: StepDependency[];
17
+ hasCycles: boolean;
18
+ topologicalOrder: string[];
19
+ }
20
+ export interface ExecutionOptions {
21
+ maxConcurrency: number;
22
+ onStepStart?: (stepId: string) => void;
23
+ onStepComplete?: (stepId: string, error?: Error) => void;
24
+ }
25
+ export type StepExecutor = (stepId: string) => Promise<void>;
26
+ /** Build dependency graph from step definitions */
27
+ export declare function buildDependencyGraph(steps: Array<{
28
+ id: string;
29
+ awaits?: string[];
30
+ }>): DependencyGraph;
31
+ /** Format cycle for error message */
32
+ export declare function formatCycle(cycles: string[][]): string;
33
+ /** Execute steps respecting dependencies with limited concurrency */
34
+ export declare function executeWithDependencies(graph: DependencyGraph, executeStep: StepExecutor, options: ExecutionOptions): Promise<Map<string, {
35
+ success: boolean;
36
+ error?: Error;
37
+ }>>;
38
+ /** Get steps that can run immediately (no unresolved dependencies) */
39
+ export declare function getReadySteps(graph: DependencyGraph, completed: Set<string>): string[];
@@ -0,0 +1,199 @@
1
+ /**
2
+ * DependencyGraph — builds and executes recipe step dependencies.
3
+ *
4
+ * Supports:
5
+ * - Parallel execution up to maxConcurrency
6
+ * - Explicit dependencies (awaits: [stepA, stepB])
7
+ * - Conditional execution (when: condition)
8
+ * - Cycle detection
9
+ */
10
+ /** Build dependency graph from step definitions */
11
+ export function buildDependencyGraph(steps) {
12
+ const nodes = steps.map((s, index) => ({
13
+ stepId: s.id,
14
+ awaits: s.awaits ?? [],
15
+ index,
16
+ }));
17
+ // Detect cycles using DFS
18
+ const visited = new Set();
19
+ const visiting = new Set();
20
+ const cycles = [];
21
+ function visit(node, path) {
22
+ if (visiting.has(node.stepId)) {
23
+ // Found cycle - extract cycle from path
24
+ const cycleStart = path.indexOf(node.stepId);
25
+ cycles.push(path.slice(cycleStart).concat(node.stepId));
26
+ return;
27
+ }
28
+ if (visited.has(node.stepId))
29
+ return;
30
+ visiting.add(node.stepId);
31
+ path.push(node.stepId);
32
+ for (const depId of node.awaits) {
33
+ const dep = nodes.find((n) => n.stepId === depId);
34
+ if (dep)
35
+ visit(dep, path);
36
+ }
37
+ path.pop();
38
+ visiting.delete(node.stepId);
39
+ visited.add(node.stepId);
40
+ }
41
+ for (const node of nodes) {
42
+ if (!visited.has(node.stepId)) {
43
+ visit(node, []);
44
+ }
45
+ }
46
+ // Calculate topological order (Kahn's algorithm)
47
+ const inDegree = new Map();
48
+ const adjacency = new Map();
49
+ for (const node of nodes) {
50
+ inDegree.set(node.stepId, node.awaits.length);
51
+ for (const dep of node.awaits) {
52
+ if (!adjacency.has(dep))
53
+ adjacency.set(dep, []);
54
+ adjacency.get(dep)?.push(node.stepId);
55
+ }
56
+ }
57
+ const queue = [];
58
+ for (const [stepId, degree] of inDegree) {
59
+ if (degree === 0)
60
+ queue.push(stepId);
61
+ }
62
+ const topologicalOrder = [];
63
+ while (queue.length > 0) {
64
+ const stepId = queue.shift();
65
+ topologicalOrder.push(stepId);
66
+ const dependents = adjacency.get(stepId) ?? [];
67
+ for (const dep of dependents) {
68
+ const newDegree = (inDegree.get(dep) ?? 0) - 1;
69
+ inDegree.set(dep, newDegree);
70
+ if (newDegree === 0)
71
+ queue.push(dep);
72
+ }
73
+ }
74
+ return {
75
+ steps: nodes,
76
+ hasCycles: cycles.length > 0,
77
+ topologicalOrder,
78
+ };
79
+ }
80
+ /** Format cycle for error message */
81
+ export function formatCycle(cycles) {
82
+ if (cycles.length === 0)
83
+ return "No cycles detected";
84
+ return cycles
85
+ .map((cycle, i) => ` Cycle ${i + 1}: ${cycle.join(" → ")}`)
86
+ .join("\n");
87
+ }
88
+ /** Execute steps respecting dependencies with limited concurrency */
89
+ export async function executeWithDependencies(graph, executeStep, options) {
90
+ if (graph.hasCycles) {
91
+ throw new Error(`Dependency graph has cycles - cannot execute`);
92
+ }
93
+ const results = new Map();
94
+ const completed = new Set();
95
+ const failed = new Set(); // C1: track which completed steps failed
96
+ const inProgress = new Set();
97
+ // Store resolver functions to signal when steps complete
98
+ const resolvers = new Map();
99
+ // Track which steps are waiting for which dependencies
100
+ const waitingFor = new Map();
101
+ for (const node of graph.steps) {
102
+ waitingFor.set(node.stepId, [...node.awaits]);
103
+ }
104
+ function waitForStep(stepId) {
105
+ if (completed.has(stepId))
106
+ return Promise.resolve();
107
+ return new Promise((resolve) => {
108
+ const list = resolvers.get(stepId);
109
+ if (list) {
110
+ list.push(resolve);
111
+ }
112
+ else {
113
+ resolvers.set(stepId, [resolve]);
114
+ }
115
+ });
116
+ }
117
+ async function runStep(stepId) {
118
+ if (inProgress.has(stepId) || completed.has(stepId))
119
+ return;
120
+ // Check if all dependencies are satisfied
121
+ const deps = waitingFor.get(stepId) ?? [];
122
+ const unresolved = deps.filter((d) => !completed.has(d));
123
+ if (unresolved.length > 0) {
124
+ await Promise.all(unresolved.map(waitForStep));
125
+ }
126
+ // C1: skip if any upstream dependency failed
127
+ const failedDep = deps.find((d) => failed.has(d));
128
+ if (failedDep) {
129
+ const err = new Error(`Skipped: upstream step "${failedDep}" failed`);
130
+ results.set(stepId, { success: false, error: err });
131
+ completed.add(stepId);
132
+ failed.add(stepId);
133
+ options.onStepComplete?.(stepId, err);
134
+ // Unblock any steps waiting on this one
135
+ const waiting = resolvers.get(stepId) ?? [];
136
+ for (const resolve of waiting)
137
+ resolve();
138
+ resolvers.delete(stepId);
139
+ return;
140
+ }
141
+ inProgress.add(stepId);
142
+ options.onStepStart?.(stepId);
143
+ let stepErr;
144
+ try {
145
+ await executeStep(stepId);
146
+ results.set(stepId, { success: true });
147
+ completed.add(stepId);
148
+ }
149
+ catch (error) {
150
+ stepErr = error instanceof Error ? error : new Error(String(error));
151
+ results.set(stepId, { success: false, error: stepErr });
152
+ completed.add(stepId);
153
+ failed.add(stepId); // C1: record failure so dependents are skipped
154
+ }
155
+ finally {
156
+ options.onStepComplete?.(stepId, stepErr);
157
+ inProgress.delete(stepId);
158
+ // Signal dependents
159
+ const waiting = resolvers.get(stepId) ?? [];
160
+ for (const resolve of waiting) {
161
+ resolve();
162
+ }
163
+ resolvers.delete(stepId);
164
+ }
165
+ }
166
+ // Execute with concurrency limit
167
+ const executing = [];
168
+ const queue = [...graph.topologicalOrder];
169
+ async function processQueue() {
170
+ while (queue.length > 0 || executing.length > 0) {
171
+ // Start new steps if under concurrency limit
172
+ while (executing.length < options.maxConcurrency && queue.length > 0) {
173
+ const stepId = queue.shift();
174
+ // C2: push before attaching .then() so indexOf is never -1
175
+ let resolveSlot;
176
+ const slot = new Promise((r) => {
177
+ resolveSlot = r;
178
+ });
179
+ executing.push(slot);
180
+ runStep(stepId).then(() => {
181
+ executing.splice(executing.indexOf(slot), 1);
182
+ resolveSlot();
183
+ });
184
+ }
185
+ if (executing.length > 0) {
186
+ await Promise.race(executing);
187
+ }
188
+ }
189
+ }
190
+ await processQueue();
191
+ return results;
192
+ }
193
+ /** Get steps that can run immediately (no unresolved dependencies) */
194
+ export function getReadySteps(graph, completed) {
195
+ return graph.steps
196
+ .filter((s) => !completed.has(s.stepId) && s.awaits.every((dep) => completed.has(dep)))
197
+ .map((s) => s.stepId);
198
+ }
199
+ //# sourceMappingURL=dependencyGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependencyGraph.js","sourceRoot":"","sources":["../../src/recipes/dependencyGraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsBH,mDAAmD;AACnD,MAAM,UAAU,oBAAoB,CAClC,KAA+C;IAE/C,MAAM,KAAK,GAAqB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACvD,MAAM,EAAE,CAAC,CAAC,EAAE;QACZ,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;QACtB,KAAK;KACN,CAAC,CAAC,CAAC;IAEJ,0BAA0B;IAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,MAAM,GAAe,EAAE,CAAC;IAE9B,SAAS,KAAK,CAAC,IAAoB,EAAE,IAAc;QACjD,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,wCAAwC;YACxC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO;QAErC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;YAClD,IAAI,GAAG;gBAAE,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAChD,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACxC,IAAI,MAAM,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC9B,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE9B,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/C,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC7B,IAAI,SAAS,KAAK,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC;QAC5B,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,WAAW,CAAC,MAAkB;IAC5C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,oBAAoB,CAAC;IACrD,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;SAC3D,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAsB,EACtB,WAAyB,EACzB,OAAyB;IAEzB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAA+C,CAAC;IACvE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC,CAAC,yCAAyC;IAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,yDAAyD;IACzD,MAAM,SAAS,GAAG,IAAI,GAAG,EAA6B,CAAC;IAEvD,uDAAuD;IACvD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,SAAS,WAAW,CAAC,MAAc;QACjC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACpD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACnC,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,MAAc;QACnC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO;QAE5D,0CAA0C;QAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,6CAA6C;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,2BAA2B,SAAS,UAAU,CAAC,CAAC;YACtE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACpD,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACnB,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACtC,wCAAwC;YACxC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,KAAK,MAAM,OAAO,IAAI,OAAO;gBAAE,OAAO,EAAE,CAAC;YACzC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzB,OAAO;QACT,CAAC;QAED,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,OAAO,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC;QAE9B,IAAI,OAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACxD,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,+CAA+C;QACrE,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC1C,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC1B,oBAAoB;YACpB,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,MAAM,SAAS,GAAoB,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAE1C,KAAK,UAAU,YAAY;QACzB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,6CAA6C;YAC7C,OAAO,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;gBAC9B,2DAA2D;gBAC3D,IAAI,WAAwB,CAAC;gBAC7B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE;oBACnC,WAAW,GAAG,CAAC,CAAC;gBAClB,CAAC,CAAC,CAAC;gBACH,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrB,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;oBACxB,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;oBAC7C,WAAW,EAAE,CAAC;gBAChB,CAAC,CAAC,CAAC;YACL,CAAC;YAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,YAAY,EAAE,CAAC;IACrB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,aAAa,CAC3B,KAAsB,EACtB,SAAsB;IAEtB,OAAO,KAAK,CAAC,KAAK;SACf,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAC1E;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function normalizeRecipeForRuntime(recipe: unknown): unknown;
@@ -0,0 +1,97 @@
1
+ function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ export function normalizeRecipeForRuntime(recipe) {
5
+ if (!isRecord(recipe)) {
6
+ return recipe;
7
+ }
8
+ const normalized = {
9
+ ...recipe,
10
+ };
11
+ if (isRecord(normalized.trigger)) {
12
+ normalized.trigger = normalizeLegacyTriggerForRuntime(normalized.trigger);
13
+ }
14
+ if (Array.isArray(normalized.steps)) {
15
+ normalized.steps = normalized.steps.map((step) => normalizeLegacyRuntimeStep(step));
16
+ }
17
+ return normalized;
18
+ }
19
+ function normalizeLegacyTriggerForRuntime(trigger) {
20
+ const normalized = { ...trigger };
21
+ if (normalized.type === "cron" &&
22
+ typeof normalized.schedule === "string" &&
23
+ typeof normalized.at !== "string") {
24
+ normalized.at = normalized.schedule;
25
+ }
26
+ delete normalized.schedule;
27
+ return normalized;
28
+ }
29
+ function normalizeLegacyRuntimeStep(step) {
30
+ if (!isRecord(step)) {
31
+ return step;
32
+ }
33
+ const normalized = {};
34
+ for (const [key, value] of Object.entries(step)) {
35
+ if (key === "params" || key === "output" || key === "prompt") {
36
+ continue;
37
+ }
38
+ if (key === "agent" && typeof value === "boolean") {
39
+ continue;
40
+ }
41
+ if (key === "parallel" && Array.isArray(value)) {
42
+ normalized.parallel = value.map((entry) => normalizeLegacyRuntimeStep(entry));
43
+ continue;
44
+ }
45
+ if (key === "branch" && Array.isArray(value)) {
46
+ normalized.branch = value.map((entry) => normalizeLegacyBranchEntry(entry));
47
+ continue;
48
+ }
49
+ normalized[key] = value;
50
+ }
51
+ if (step.agent === true || isRecord(step.agent)) {
52
+ const agentConfig = isRecord(step.agent)
53
+ ? { ...step.agent }
54
+ : {};
55
+ if (typeof step.prompt === "string" &&
56
+ typeof agentConfig.prompt !== "string") {
57
+ agentConfig.prompt = step.prompt;
58
+ }
59
+ if (typeof step.output === "string" &&
60
+ typeof agentConfig.into !== "string") {
61
+ agentConfig.into = step.output;
62
+ }
63
+ normalized.agent = agentConfig;
64
+ return normalized;
65
+ }
66
+ if (isRecord(step.params)) {
67
+ Object.assign(normalized, step.params);
68
+ }
69
+ if (typeof normalized.into !== "string" && typeof step.output === "string") {
70
+ normalized.into = step.output;
71
+ }
72
+ if (normalized.tool === "file.append" &&
73
+ typeof normalized.content !== "string" &&
74
+ typeof normalized.line === "string") {
75
+ normalized.content = normalized.line;
76
+ delete normalized.line;
77
+ }
78
+ return normalized;
79
+ }
80
+ function normalizeLegacyBranchEntry(entry) {
81
+ if (!isRecord(entry)) {
82
+ return entry;
83
+ }
84
+ const normalized = {};
85
+ for (const [key, value] of Object.entries(entry)) {
86
+ if (key === "otherwise" && isRecord(value)) {
87
+ normalized.otherwise = normalizeLegacyRuntimeStep(value);
88
+ continue;
89
+ }
90
+ normalized[key] = value;
91
+ }
92
+ if (Object.hasOwn(normalized, "otherwise")) {
93
+ return normalized;
94
+ }
95
+ return normalizeLegacyRuntimeStep(entry);
96
+ }
97
+ //# sourceMappingURL=legacyRecipeCompat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyRecipeCompat.js","sourceRoot":"","sources":["../../src/recipes/legacyRecipeCompat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * NestedRecipeStep — handler for calling recipes from within recipes.
3
+ *
4
+ * Supports:
5
+ * - Variable passing via template resolution
6
+ * - Isolated OutputRegistry for child
7
+ * - Risk escalation (child risk > parent risk uses child's)
8
+ * - Depth limiting (prevent infinite recursion)
9
+ */
10
+ import type { OutputRegistry } from "./outputRegistry.js";
11
+ import type { TemplateContext, TemplateError } from "./templateEngine.js";
12
+ export interface NestedRecipeConfig {
13
+ recipe: string;
14
+ vars: Record<string, string>;
15
+ output?: string;
16
+ risk?: "low" | "medium" | "high";
17
+ id: string;
18
+ }
19
+ export interface NestedRecipeContext {
20
+ parentRegistry: OutputRegistry;
21
+ parentEnv: Record<string, string | undefined>;
22
+ recipeMaxDepth: number;
23
+ currentDepth: number;
24
+ dryRun: boolean;
25
+ }
26
+ export interface NestedRecipeResult {
27
+ success: boolean;
28
+ data?: unknown;
29
+ error?: string;
30
+ childOutputs?: Record<string, unknown>;
31
+ }
32
+ export interface RecipeLoader {
33
+ loadRecipe(name: string): Promise<{
34
+ steps: unknown[];
35
+ } | null>;
36
+ }
37
+ /** Resolve template variables against parent context */
38
+ export declare function resolveNestedVars(vars: Record<string, string>, context: TemplateContext): {
39
+ resolved: Record<string, string>;
40
+ errors: TemplateError[];
41
+ };
42
+ /** Validate nested recipe call before execution */
43
+ export declare function validateNestedRecipe(config: NestedRecipeConfig, context: NestedRecipeContext): {
44
+ valid: boolean;
45
+ error?: string;
46
+ };
47
+ /** Calculate effective risk tier */
48
+ export declare function calculateNestedRisk(parentRisk: "low" | "medium" | "high" | undefined, childRisk: "low" | "medium" | "high" | undefined): "low" | "medium" | "high";
49
+ /** Format nested recipe result for parent registry */
50
+ export declare function formatNestedOutput(result: NestedRecipeResult, config: NestedRecipeConfig): {
51
+ stepId: string;
52
+ output: {
53
+ status: "success" | "error" | "skipped";
54
+ data: unknown;
55
+ };
56
+ };
57
+ /** Mock nested recipe execution for dry-run mode */
58
+ export declare function mockNestedRecipe(config: NestedRecipeConfig, context: NestedRecipeContext): Promise<NestedRecipeResult>;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * NestedRecipeStep — handler for calling recipes from within recipes.
3
+ *
4
+ * Supports:
5
+ * - Variable passing via template resolution
6
+ * - Isolated OutputRegistry for child
7
+ * - Risk escalation (child risk > parent risk uses child's)
8
+ * - Depth limiting (prevent infinite recursion)
9
+ */
10
+ import { compileTemplate } from "./templateEngine.js";
11
+ /** Resolve template variables against parent context */
12
+ export function resolveNestedVars(vars, context) {
13
+ const resolved = {};
14
+ const errors = [];
15
+ for (const [key, templateStr] of Object.entries(vars)) {
16
+ const compiled = compileTemplate(templateStr);
17
+ const result = compiled.evaluate(context);
18
+ if ("error" in result) {
19
+ errors.push(result.error);
20
+ resolved[key] = "";
21
+ }
22
+ else {
23
+ resolved[key] = result.value;
24
+ }
25
+ }
26
+ return { resolved, errors };
27
+ }
28
+ /** Validate nested recipe call before execution */
29
+ export function validateNestedRecipe(config, context) {
30
+ if (context.currentDepth > context.recipeMaxDepth) {
31
+ return {
32
+ valid: false,
33
+ error: `Recipe nesting depth limit (${context.recipeMaxDepth}) exceeded. ` +
34
+ `Step "${config.id}" attempted to call "${config.recipe}" at depth ${context.currentDepth + 1}.`,
35
+ };
36
+ }
37
+ if (!config.recipe || typeof config.recipe !== "string") {
38
+ return {
39
+ valid: false,
40
+ error: `Invalid recipe reference in step "${config.id}": recipe name is required`,
41
+ };
42
+ }
43
+ return { valid: true };
44
+ }
45
+ /** Calculate effective risk tier */
46
+ export function calculateNestedRisk(parentRisk, childRisk) {
47
+ const tiers = { low: 1, medium: 2, high: 3 };
48
+ const parentTier = tiers[parentRisk ?? "low"];
49
+ const childTier = tiers[childRisk ?? "low"];
50
+ const effective = Math.max(parentTier, childTier);
51
+ return effective === 1 ? "low" : effective === 2 ? "medium" : "high";
52
+ }
53
+ /** Format nested recipe result for parent registry */
54
+ export function formatNestedOutput(result, config) {
55
+ return {
56
+ stepId: config.output ?? config.id,
57
+ output: {
58
+ status: result.success ? "success" : "error",
59
+ data: result.success
60
+ ? {
61
+ recipe: config.recipe,
62
+ result: result.data,
63
+ childOutputs: result.childOutputs,
64
+ }
65
+ : { error: result.error },
66
+ },
67
+ };
68
+ }
69
+ /** Mock nested recipe execution for dry-run mode */
70
+ export async function mockNestedRecipe(config, context) {
71
+ const validation = validateNestedRecipe(config, context);
72
+ if (!validation.valid) {
73
+ return { success: false, error: validation.error };
74
+ }
75
+ // Resolve templates to show what would be passed
76
+ const parentContext = context.parentRegistry.toTemplateContext(context.parentEnv);
77
+ const { resolved, errors } = resolveNestedVars(config.vars, parentContext);
78
+ if (errors.length > 0) {
79
+ return {
80
+ success: false,
81
+ error: `Template errors: ${errors.map((e) => e.message).join(", ")}`,
82
+ };
83
+ }
84
+ return {
85
+ success: true,
86
+ data: {
87
+ dryRun: true,
88
+ recipe: config.recipe,
89
+ resolvedVars: resolved,
90
+ effectiveRisk: calculateNestedRisk(undefined, config.risk),
91
+ wouldExecuteAtDepth: context.currentDepth + 1,
92
+ },
93
+ };
94
+ }
95
+ //# sourceMappingURL=nestedRecipeStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nestedRecipeStep.js","sourceRoot":"","sources":["../../src/recipes/nestedRecipeStep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OutputRegistry — per-recipe-run state container for step outputs.
3
+ *
4
+ * Isolated from other runs, no persistence. Used by template engine
5
+ * to resolve {{steps.X.data.field}} references.
6
+ */
7
+ import type { StepOutput, TemplateContext } from "./templateEngine.js";
8
+ export interface OutputRegistry {
9
+ /** Store output from a completed step */
10
+ set(stepId: string, output: StepOutput): void;
11
+ /** Get output for a step, or undefined if not yet run */
12
+ get(stepId: string): StepOutput | undefined;
13
+ /** Check if a step has completed */
14
+ has(stepId: string): boolean;
15
+ /** Get all step IDs that have outputs */
16
+ keys(): string[];
17
+ /** Convert to TemplateContext for template resolution */
18
+ toTemplateContext(env: Record<string, string | undefined>): TemplateContext;
19
+ /** Summary for logging/debugging */
20
+ summary(): {
21
+ total: number;
22
+ succeeded: number;
23
+ failed: number;
24
+ skipped: number;
25
+ };
26
+ }
27
+ /** Create a new isolated OutputRegistry for a recipe run */
28
+ export declare function createOutputRegistry(): OutputRegistry;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * OutputRegistry — per-recipe-run state container for step outputs.
3
+ *
4
+ * Isolated from other runs, no persistence. Used by template engine
5
+ * to resolve {{steps.X.data.field}} references.
6
+ */
7
+ class OutputRegistryImpl {
8
+ outputs = new Map();
9
+ set(stepId, output) {
10
+ this.outputs.set(stepId, output);
11
+ }
12
+ get(stepId) {
13
+ return this.outputs.get(stepId);
14
+ }
15
+ has(stepId) {
16
+ return this.outputs.has(stepId);
17
+ }
18
+ keys() {
19
+ return Array.from(this.outputs.keys());
20
+ }
21
+ toTemplateContext(env) {
22
+ const steps = {};
23
+ for (const [key, value] of this.outputs) {
24
+ steps[key] = value;
25
+ }
26
+ return { steps, env };
27
+ }
28
+ summary() {
29
+ let succeeded = 0;
30
+ let failed = 0;
31
+ let skipped = 0;
32
+ for (const output of this.outputs.values()) {
33
+ if (output.status === "success")
34
+ succeeded++;
35
+ else if (output.status === "error")
36
+ failed++;
37
+ else if (output.status === "skipped")
38
+ skipped++;
39
+ }
40
+ return {
41
+ total: this.outputs.size,
42
+ succeeded,
43
+ failed,
44
+ skipped,
45
+ };
46
+ }
47
+ }
48
+ /** Create a new isolated OutputRegistry for a recipe run */
49
+ export function createOutputRegistry() {
50
+ return new OutputRegistryImpl();
51
+ }
52
+ //# sourceMappingURL=outputRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outputRegistry.js","sourceRoot":"","sources":["../../src/recipes/outputRegistry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6BH,MAAM,kBAAkB;IACd,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEhD,GAAG,CAAC,MAAc,EAAE,MAAkB;QACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,iBAAiB,CAAC,GAAuC;QACvD,MAAM,KAAK,GAA+B,EAAE,CAAC;QAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QAML,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS,EAAE,CAAC;iBACxC,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO;gBAAE,MAAM,EAAE,CAAC;iBACxC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;QAClD,CAAC;QACD,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACxB,SAAS;YACT,MAAM;YACN,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,kBAAkB,EAAE,CAAC;AAClC,CAAC"}
@@ -1,10 +1,12 @@
1
+ import cron from "node-cron";
1
2
  import type { Logger } from "../logger.js";
2
3
  /**
3
- * RecipeScheduler — runs cron-triggered recipes on a simple interval.
4
+ * RecipeScheduler — runs cron-triggered recipes on a simple interval or
5
+ * standard 5-field cron expression.
4
6
  *
5
- * Phase-0 scope supports the `@every Ns|Nm|Nh` schedule form, which is
6
- * enough to demonstrate "works while you're away" without pulling in a full
7
- * cron dependency. Standard 5-field expressions can be added later.
7
+ * Supported schedule forms:
8
+ * @every Ns|Nm|Nh — simple interval (setInterval-based)
9
+ * <5-field cron> — standard cron expression (node-cron-based)
8
10
  *
9
11
  * Scheduler is a pure consumer of the recipes-on-disk contract and an
10
12
  * injected enqueue fn, so it's trivial to unit test without the orchestrator.
@@ -13,15 +15,20 @@ export type SchedulerEnqueue = (opts: {
13
15
  prompt: string;
14
16
  triggerSource: string;
15
17
  }) => string;
18
+ export type SchedulerRunYaml = (name: string) => Promise<void>;
16
19
  export interface ScheduledRecipe {
17
20
  name: string;
18
21
  schedule: string;
19
22
  intervalMs: number;
20
23
  timer: ReturnType<typeof setInterval>;
24
+ /** Present only for cron5-kind recipes. */
25
+ cronJob?: cron.ScheduledTask;
21
26
  }
22
27
  export interface SchedulerOptions {
23
28
  recipesDir: string;
24
29
  enqueue: SchedulerEnqueue;
30
+ /** Called for YAML recipes instead of enqueue. */
31
+ runYaml?: SchedulerRunYaml;
25
32
  logger?: Logger;
26
33
  /** Override for tests — defaults to setInterval. */
27
34
  setInterval?: typeof setInterval;
@@ -36,10 +43,19 @@ export declare class RecipeScheduler {
36
43
  constructor(opts: SchedulerOptions);
37
44
  start(): ScheduledRecipe[];
38
45
  stop(): void;
39
- list(): ReadonlyArray<Omit<ScheduledRecipe, "timer">>;
46
+ restart(): void;
47
+ list(): ReadonlyArray<Omit<ScheduledRecipe, "timer" | "cronJob">>;
40
48
  /** Test hook: dispatch a recipe immediately without waiting for the interval. */
41
49
  fireForTest(name: string): void;
42
50
  private fire;
43
51
  }
44
- /** Parse @every forms into milliseconds. Returns null for unsupported schedules. */
45
- export declare function parseSchedule(schedule: string): number | null;
52
+ type ParsedSchedule = {
53
+ kind: "interval";
54
+ intervalMs: number;
55
+ } | {
56
+ kind: "cron5";
57
+ expression: string;
58
+ };
59
+ /** Parse @every forms into milliseconds, or detect a 5-field cron expression. Returns null for unsupported schedules. */
60
+ export declare function parseSchedule(schedule: string): ParsedSchedule | null;
61
+ export {};