@relayflows/core 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/agent-handle.d.ts +27 -0
  2. package/dist/agent-handle.d.ts.map +1 -0
  3. package/dist/agent-handle.js +32 -0
  4. package/dist/agent-handle.js.map +1 -0
  5. package/dist/api-executor.d.ts +16 -0
  6. package/dist/api-executor.d.ts.map +1 -0
  7. package/dist/api-executor.js +94 -0
  8. package/dist/api-executor.js.map +1 -0
  9. package/dist/barrier.d.ts +72 -0
  10. package/dist/barrier.d.ts.map +1 -0
  11. package/dist/barrier.js +162 -0
  12. package/dist/barrier.js.map +1 -0
  13. package/dist/budget-tracker.d.ts +75 -0
  14. package/dist/budget-tracker.d.ts.map +1 -0
  15. package/dist/budget-tracker.js +184 -0
  16. package/dist/budget-tracker.js.map +1 -0
  17. package/dist/builder.d.ts +229 -0
  18. package/dist/builder.d.ts.map +1 -0
  19. package/dist/builder.js +430 -0
  20. package/dist/builder.js.map +1 -0
  21. package/dist/builtin-templates/bug-fix.yaml +139 -0
  22. package/dist/builtin-templates/code-review.yaml +137 -0
  23. package/dist/builtin-templates/competitive.yaml +107 -0
  24. package/dist/builtin-templates/documentation.yaml +128 -0
  25. package/dist/builtin-templates/feature-dev.yaml +146 -0
  26. package/dist/builtin-templates/refactor.yaml +145 -0
  27. package/dist/builtin-templates/review-loop.yaml +227 -0
  28. package/dist/builtin-templates/security-audit.yaml +139 -0
  29. package/dist/channel-messenger.d.ts +28 -0
  30. package/dist/channel-messenger.d.ts.map +1 -0
  31. package/dist/channel-messenger.js +275 -0
  32. package/dist/channel-messenger.js.map +1 -0
  33. package/dist/cli-registry.d.ts +77 -0
  34. package/dist/cli-registry.d.ts.map +1 -0
  35. package/dist/cli-registry.js +268 -0
  36. package/dist/cli-registry.js.map +1 -0
  37. package/dist/cli-session-collector.d.ts +39 -0
  38. package/dist/cli-session-collector.d.ts.map +1 -0
  39. package/dist/cli-session-collector.js +23 -0
  40. package/dist/cli-session-collector.js.map +1 -0
  41. package/dist/cli.d.ts +11 -0
  42. package/dist/cli.d.ts.map +1 -0
  43. package/dist/cli.js +395 -0
  44. package/dist/cli.js.map +1 -0
  45. package/dist/cloud-runner.d.ts +15 -0
  46. package/dist/cloud-runner.d.ts.map +1 -0
  47. package/dist/cloud-runner.js +41 -0
  48. package/dist/cloud-runner.js.map +1 -0
  49. package/dist/cloud-schedules.d.ts +3 -0
  50. package/dist/cloud-schedules.d.ts.map +1 -0
  51. package/dist/cloud-schedules.js +2 -0
  52. package/dist/cloud-schedules.js.map +1 -0
  53. package/dist/collectors/claude.d.ts +6 -0
  54. package/dist/collectors/claude.d.ts.map +1 -0
  55. package/dist/collectors/claude.js +330 -0
  56. package/dist/collectors/claude.js.map +1 -0
  57. package/dist/collectors/codex.d.ts +18 -0
  58. package/dist/collectors/codex.d.ts.map +1 -0
  59. package/dist/collectors/codex.js +265 -0
  60. package/dist/collectors/codex.js.map +1 -0
  61. package/dist/collectors/opencode.d.ts +6 -0
  62. package/dist/collectors/opencode.d.ts.map +1 -0
  63. package/dist/collectors/opencode.js +204 -0
  64. package/dist/collectors/opencode.js.map +1 -0
  65. package/dist/coordinator.d.ts +73 -0
  66. package/dist/coordinator.d.ts.map +1 -0
  67. package/dist/coordinator.js +647 -0
  68. package/dist/coordinator.js.map +1 -0
  69. package/dist/custom-steps.d.ts +73 -0
  70. package/dist/custom-steps.d.ts.map +1 -0
  71. package/dist/custom-steps.js +321 -0
  72. package/dist/custom-steps.js.map +1 -0
  73. package/dist/default-logger.d.ts +9 -0
  74. package/dist/default-logger.d.ts.map +1 -0
  75. package/dist/default-logger.js +104 -0
  76. package/dist/default-logger.js.map +1 -0
  77. package/dist/dry-run-format.d.ts +6 -0
  78. package/dist/dry-run-format.d.ts.map +1 -0
  79. package/dist/dry-run-format.js +76 -0
  80. package/dist/dry-run-format.js.map +1 -0
  81. package/dist/file-db.d.ts +85 -0
  82. package/dist/file-db.d.ts.map +1 -0
  83. package/dist/file-db.js +215 -0
  84. package/dist/file-db.js.map +1 -0
  85. package/dist/index.d.ts +37 -1
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +34 -3
  88. package/dist/index.js.map +1 -0
  89. package/dist/integrations/browser.d.ts +99 -0
  90. package/dist/integrations/browser.d.ts.map +1 -0
  91. package/dist/integrations/browser.js +419 -0
  92. package/dist/integrations/browser.js.map +1 -0
  93. package/dist/integrations/github.d.ts +79 -0
  94. package/dist/integrations/github.d.ts.map +1 -0
  95. package/dist/integrations/github.js +459 -0
  96. package/dist/integrations/github.js.map +1 -0
  97. package/dist/integrations/slack.d.ts +80 -0
  98. package/dist/integrations/slack.d.ts.map +1 -0
  99. package/dist/integrations/slack.js +355 -0
  100. package/dist/integrations/slack.js.map +1 -0
  101. package/dist/listr-renderer.d.ts +26 -0
  102. package/dist/listr-renderer.d.ts.map +1 -0
  103. package/dist/listr-renderer.js +230 -0
  104. package/dist/listr-renderer.js.map +1 -0
  105. package/dist/memory-db.d.ts +17 -0
  106. package/dist/memory-db.d.ts.map +1 -0
  107. package/dist/memory-db.js +33 -0
  108. package/dist/memory-db.js.map +1 -0
  109. package/dist/process-backend-executor.d.ts +18 -0
  110. package/dist/process-backend-executor.d.ts.map +1 -0
  111. package/dist/process-backend-executor.js +74 -0
  112. package/dist/process-backend-executor.js.map +1 -0
  113. package/dist/process-spawner.d.ts +35 -0
  114. package/dist/process-spawner.d.ts.map +1 -0
  115. package/dist/process-spawner.js +173 -0
  116. package/dist/process-spawner.js.map +1 -0
  117. package/dist/provisioner.d.ts +64 -0
  118. package/dist/provisioner.d.ts.map +1 -0
  119. package/dist/provisioner.js +269 -0
  120. package/dist/provisioner.js.map +1 -0
  121. package/dist/proxy-env.d.ts +52 -0
  122. package/dist/proxy-env.d.ts.map +1 -0
  123. package/dist/proxy-env.js +92 -0
  124. package/dist/proxy-env.js.map +1 -0
  125. package/dist/run-script.d.ts +82 -0
  126. package/dist/run-script.d.ts.map +1 -0
  127. package/dist/run-script.js +527 -0
  128. package/dist/run-script.js.map +1 -0
  129. package/dist/run-summary-table.d.ts +5 -0
  130. package/dist/run-summary-table.d.ts.map +1 -0
  131. package/dist/run-summary-table.js +132 -0
  132. package/dist/run-summary-table.js.map +1 -0
  133. package/dist/run.d.ts +45 -0
  134. package/dist/run.d.ts.map +1 -0
  135. package/dist/run.js +37 -0
  136. package/dist/run.js.map +1 -0
  137. package/dist/runner.d.ts +528 -0
  138. package/dist/runner.d.ts.map +1 -0
  139. package/dist/runner.js +6269 -0
  140. package/dist/runner.js.map +1 -0
  141. package/dist/schema.d.ts +275 -0
  142. package/dist/schema.d.ts.map +1 -0
  143. package/dist/schema.js +27 -0
  144. package/dist/schema.js.map +1 -0
  145. package/dist/schema.json +940 -0
  146. package/dist/sibling-links.d.ts +100 -0
  147. package/dist/sibling-links.d.ts.map +1 -0
  148. package/dist/sibling-links.js +205 -0
  149. package/dist/sibling-links.js.map +1 -0
  150. package/dist/state.d.ts +77 -0
  151. package/dist/state.d.ts.map +1 -0
  152. package/dist/state.js +140 -0
  153. package/dist/state.js.map +1 -0
  154. package/dist/step-executor.d.ts +95 -0
  155. package/dist/step-executor.d.ts.map +1 -0
  156. package/dist/step-executor.js +393 -0
  157. package/dist/step-executor.js.map +1 -0
  158. package/dist/template-resolver.d.ts +33 -0
  159. package/dist/template-resolver.d.ts.map +1 -0
  160. package/dist/template-resolver.js +144 -0
  161. package/dist/template-resolver.js.map +1 -0
  162. package/dist/templates.d.ts +47 -0
  163. package/dist/templates.d.ts.map +1 -0
  164. package/dist/templates.js +405 -0
  165. package/dist/templates.js.map +1 -0
  166. package/dist/trajectory.d.ts +87 -0
  167. package/dist/trajectory.d.ts.map +1 -0
  168. package/dist/trajectory.js +412 -0
  169. package/dist/trajectory.js.map +1 -0
  170. package/dist/types.d.ts +471 -0
  171. package/dist/types.d.ts.map +1 -0
  172. package/dist/types.js +37 -0
  173. package/dist/types.js.map +1 -0
  174. package/dist/validator.d.ts +11 -0
  175. package/dist/validator.d.ts.map +1 -0
  176. package/dist/validator.js +186 -0
  177. package/dist/validator.js.map +1 -0
  178. package/dist/verification.d.ts +53 -0
  179. package/dist/verification.d.ts.map +1 -0
  180. package/dist/verification.js +238 -0
  181. package/dist/verification.js.map +1 -0
  182. package/package.json +12 -8
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Sibling-package link setup for workflows that consume a package living in
3
+ * a sibling repo / worktree on disk.
4
+ *
5
+ * Problem it solves: agents running inside a workflow sometimes find that
6
+ * `npm install` (or `pip install`) resolved an older version of a package
7
+ * than the one the workflow actually needs — for example when the consumer
8
+ * workflow runs before the producer has published a new release. Rather
9
+ * than letting agents see a stale interface (and react by augmenting the
10
+ * module or writing fallback implementations), linking redirects the
11
+ * package resolution at dev-time to the sibling's on-disk build output.
12
+ *
13
+ * Usage (ESM):
14
+ *
15
+ * import { workflow, applySiblingLinks } from '@relayflows/core';
16
+ *
17
+ * const base = workflow('my-feature').pattern('dag').agent('impl', ...);
18
+ * const wf = applySiblingLinks(base, {
19
+ * dependsOn: ['install-deps'],
20
+ * links: [
21
+ * {
22
+ * name: '@agent-assistant/proactive',
23
+ * path: '../agent-assistant/packages/proactive',
24
+ * expect: ['recordSignal', 'drainSignals'],
25
+ * },
26
+ * {
27
+ * name: '@agent-assistant/surfaces',
28
+ * path: '../agent-assistant/packages/surfaces',
29
+ * expect: ['classifySlackPresenceSignal'],
30
+ * },
31
+ * ],
32
+ * });
33
+ *
34
+ * await wf.step('plan', { agent: 'impl', dependsOn: ['setup-sibling-links'], task: ... })
35
+ * .run({ cwd: process.cwd() });
36
+ *
37
+ * MVP language support: npm (package.json), Python (pyproject.toml /
38
+ * setup.py / setup.cfg). Auto-detects from the sibling's manifest. Fails
39
+ * fast on missing path, unknown manifest, or missing expected exports.
40
+ */
41
+ /** A single sibling package to link into the workflow's working directory. */
42
+ export interface SiblingLink {
43
+ /**
44
+ * Package name as it appears in imports (e.g. "@agent-assistant/proactive",
45
+ * "my_python_pkg"). For Python, use the import name (underscored), not the
46
+ * distribution name.
47
+ */
48
+ name: string;
49
+ /**
50
+ * Path to the sibling package root, relative to the workflow's cwd.
51
+ * For npm, this is the directory containing package.json.
52
+ * For Python, the directory containing pyproject.toml / setup.py.
53
+ */
54
+ path: string;
55
+ /**
56
+ * Optional list of top-level named exports / attributes the workflow
57
+ * expects to find on the linked package post-setup. When provided, a
58
+ * language-appropriate import smoke test runs and fails the step if any
59
+ * are missing.
60
+ */
61
+ expect?: string[];
62
+ }
63
+ export interface SiblingLinkOptions {
64
+ /** Link declarations. All must succeed (fail-fast on any error). */
65
+ links: SiblingLink[];
66
+ /**
67
+ * Step name for the setup step emitted by this helper.
68
+ * Defaults to `"setup-sibling-links"`.
69
+ */
70
+ stepName?: string;
71
+ /**
72
+ * dependsOn for the setup step. Typically `['install-deps']` so that
73
+ * `npm install` / `pip install` has run first.
74
+ * Defaults to `['install-deps']`.
75
+ */
76
+ dependsOn?: string[];
77
+ }
78
+ /**
79
+ * Adds a single deterministic step to the workflow that links each sibling
80
+ * package into the workflow's working directory using the appropriate
81
+ * language-specific mechanism, then smoke-tests each linked package for
82
+ * expected exports.
83
+ *
84
+ * The step fails fast on:
85
+ * - Sibling path missing
86
+ * - Unknown manifest (no package.json / pyproject.toml / setup.py)
87
+ * - Link command failure
88
+ * - Missing expected export
89
+ */
90
+ export declare function applySiblingLinks<T>(wf: T, opts: SiblingLinkOptions): T;
91
+ /**
92
+ * Builds a bash script that:
93
+ * 1. For each link, detects its manifest and applies the right link command.
94
+ * 2. After all links succeed, runs one import smoke test per link that
95
+ * declared expected exports.
96
+ *
97
+ * Exported for test visibility; not part of the public API.
98
+ */
99
+ export declare function buildSiblingLinkScript(links: SiblingLink[]): string;
100
+ //# sourceMappingURL=sibling-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sibling-links.d.ts","sourceRoot":"","sources":["../src/sibling-links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,KAAK,EAAE,WAAW,EAAE,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,kBAAkB,GAAG,CAAC,CAkBvE;AAkBD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAsBnE"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Sibling-package link setup for workflows that consume a package living in
3
+ * a sibling repo / worktree on disk.
4
+ *
5
+ * Problem it solves: agents running inside a workflow sometimes find that
6
+ * `npm install` (or `pip install`) resolved an older version of a package
7
+ * than the one the workflow actually needs — for example when the consumer
8
+ * workflow runs before the producer has published a new release. Rather
9
+ * than letting agents see a stale interface (and react by augmenting the
10
+ * module or writing fallback implementations), linking redirects the
11
+ * package resolution at dev-time to the sibling's on-disk build output.
12
+ *
13
+ * Usage (ESM):
14
+ *
15
+ * import { workflow, applySiblingLinks } from '@relayflows/core';
16
+ *
17
+ * const base = workflow('my-feature').pattern('dag').agent('impl', ...);
18
+ * const wf = applySiblingLinks(base, {
19
+ * dependsOn: ['install-deps'],
20
+ * links: [
21
+ * {
22
+ * name: '@agent-assistant/proactive',
23
+ * path: '../agent-assistant/packages/proactive',
24
+ * expect: ['recordSignal', 'drainSignals'],
25
+ * },
26
+ * {
27
+ * name: '@agent-assistant/surfaces',
28
+ * path: '../agent-assistant/packages/surfaces',
29
+ * expect: ['classifySlackPresenceSignal'],
30
+ * },
31
+ * ],
32
+ * });
33
+ *
34
+ * await wf.step('plan', { agent: 'impl', dependsOn: ['setup-sibling-links'], task: ... })
35
+ * .run({ cwd: process.cwd() });
36
+ *
37
+ * MVP language support: npm (package.json), Python (pyproject.toml /
38
+ * setup.py / setup.cfg). Auto-detects from the sibling's manifest. Fails
39
+ * fast on missing path, unknown manifest, or missing expected exports.
40
+ */
41
+ /**
42
+ * Adds a single deterministic step to the workflow that links each sibling
43
+ * package into the workflow's working directory using the appropriate
44
+ * language-specific mechanism, then smoke-tests each linked package for
45
+ * expected exports.
46
+ *
47
+ * The step fails fast on:
48
+ * - Sibling path missing
49
+ * - Unknown manifest (no package.json / pyproject.toml / setup.py)
50
+ * - Link command failure
51
+ * - Missing expected export
52
+ */
53
+ export function applySiblingLinks(wf, opts) {
54
+ if (opts.links.length === 0) {
55
+ return wf;
56
+ }
57
+ const stepName = opts.stepName ?? 'setup-sibling-links';
58
+ const dependsOn = opts.dependsOn ?? ['install-deps'];
59
+ const script = buildSiblingLinkScript(opts.links);
60
+ const chain = wf;
61
+ chain.step(stepName, {
62
+ type: 'deterministic',
63
+ dependsOn,
64
+ command: `bash -c ${shSingleQuote(script)}`,
65
+ captureOutput: true,
66
+ failOnError: true,
67
+ });
68
+ return wf;
69
+ }
70
+ // ─── Internal: shell-script generation ─────────────────────────────────────
71
+ /**
72
+ * Shell-quote a string for safe single-quoted inclusion in a bash command.
73
+ * Single-quoted strings in bash are literal for every character except the
74
+ * single quote itself, so `$` and backticks are NOT interpreted — which is
75
+ * exactly what we want for link.name / link.path / JSON payloads that must
76
+ * pass through bash unchanged.
77
+ *
78
+ * Embedded single quotes are escaped via the standard `'\''` POSIX idiom
79
+ * (close, escape, reopen).
80
+ */
81
+ function shSingleQuote(value) {
82
+ return `'${value.replace(/'/g, `'\\''`)}'`;
83
+ }
84
+ /**
85
+ * Builds a bash script that:
86
+ * 1. For each link, detects its manifest and applies the right link command.
87
+ * 2. After all links succeed, runs one import smoke test per link that
88
+ * declared expected exports.
89
+ *
90
+ * Exported for test visibility; not part of the public API.
91
+ */
92
+ export function buildSiblingLinkScript(links) {
93
+ const lines = ['set -euo pipefail', 'echo "=== applySiblingLinks: setting up ==="'];
94
+ for (const link of links) {
95
+ // Use SINGLE-quoted shell literals for the assignments. Double-quoted
96
+ // literals (via JSON.stringify) would let `$`, backticks, and `\` still
97
+ // trigger substitution or escaping — single-quoted is literal end-to-end.
98
+ const escapedName = shSingleQuote(link.name);
99
+ const escapedPath = shSingleQuote(link.path);
100
+ lines.push(linkOneBlock(link, escapedName, escapedPath));
101
+ }
102
+ lines.push('echo "=== applySiblingLinks: verifying exports ==="');
103
+ for (const link of links) {
104
+ if (!link.expect || link.expect.length === 0) {
105
+ continue;
106
+ }
107
+ lines.push(verifyExportsBlock(link));
108
+ }
109
+ lines.push('echo "APPLY_SIBLING_LINKS_OK"');
110
+ return lines.join('\n');
111
+ }
112
+ function linkOneBlock(link, escapedName, escapedPath) {
113
+ void link;
114
+ return [
115
+ `SIBLING_PATH=${escapedPath}`,
116
+ `SIBLING_NAME=${escapedName}`,
117
+ 'echo "--- link: $SIBLING_NAME <- $SIBLING_PATH ---"',
118
+ 'if [ ! -d "$SIBLING_PATH" ]; then',
119
+ ' echo "SIBLING_PATH_MISSING: $SIBLING_PATH" >&2',
120
+ ' exit 1',
121
+ 'fi',
122
+ 'if [ -f "$SIBLING_PATH/package.json" ]; then',
123
+ ' echo "detected: npm"',
124
+ ' ( cd "$SIBLING_PATH" && npm link --silent )',
125
+ ' npm link --silent "$SIBLING_NAME"',
126
+ 'elif [ -f "$SIBLING_PATH/pyproject.toml" ] || [ -f "$SIBLING_PATH/setup.py" ] || [ -f "$SIBLING_PATH/setup.cfg" ]; then',
127
+ ' echo "detected: python"',
128
+ // Try uv first (fastest when available), but uv refuses to install
129
+ // outside a venv without --system. Pass --system explicitly so uv
130
+ // works in non-venv sandboxes (common CI/agent runner shape).
131
+ // If uv still fails (e.g. broken install), fall through to pip/pip3
132
+ // via the explicit OR chain rather than relying on `set -e` to
133
+ // short-circuit between elif branches.
134
+ ' if command -v uv >/dev/null 2>&1 && uv pip install --system -e "$SIBLING_PATH" --quiet 2>/dev/null; then',
135
+ ' :',
136
+ ' elif command -v pip >/dev/null 2>&1; then',
137
+ ' pip install -e "$SIBLING_PATH" --quiet',
138
+ ' elif command -v pip3 >/dev/null 2>&1; then',
139
+ ' pip3 install -e "$SIBLING_PATH" --quiet',
140
+ ' else',
141
+ ' echo "NO_PYTHON_INSTALLER: uv / pip / pip3 not found or all failed" >&2',
142
+ ' exit 1',
143
+ ' fi',
144
+ 'else',
145
+ ' echo "UNKNOWN_MANIFEST: expected package.json / pyproject.toml / setup.py / setup.cfg at $SIBLING_PATH" >&2',
146
+ ' exit 1',
147
+ 'fi',
148
+ ].join('\n');
149
+ }
150
+ function verifyExportsBlock(link) {
151
+ const escapedName = shSingleQuote(link.name);
152
+ const escapedPath = shSingleQuote(link.path);
153
+ const expectJson = JSON.stringify(link.expect ?? []);
154
+ // Pick the smoke-test runtime based on what manifest type the sibling had.
155
+ // Single-quoted assignments are literal — the JSON payload inside EXPECT
156
+ // survives bash untouched and downstream Node/Python JSON.parse it back.
157
+ return [
158
+ `SIBLING_PATH=${escapedPath}`,
159
+ `SIBLING_NAME=${escapedName}`,
160
+ `EXPECT=${shSingleQuote(expectJson)}`,
161
+ 'if [ -f "$SIBLING_PATH/package.json" ]; then',
162
+ nodeVerifyCommand(),
163
+ 'else',
164
+ pythonVerifyCommand(),
165
+ 'fi',
166
+ ].join('\n');
167
+ }
168
+ function nodeVerifyCommand() {
169
+ const script = [
170
+ 'const want = JSON.parse(process.env.APPLY_SIBLING_LINKS_EXPECT);',
171
+ 'const name = process.env.APPLY_SIBLING_LINKS_NAME;',
172
+ 'const mod = await import(name);',
173
+ 'const missing = want.filter((k) => !(k in mod));',
174
+ 'if (missing.length) {',
175
+ ' console.error(`MISSING_EXPORTS in ${name}: ${missing.join(",")}`);',
176
+ ' process.exit(1);',
177
+ '}',
178
+ 'console.log(`${name} OK: ${want.join(",")}`);',
179
+ ].join(' ');
180
+ return ` APPLY_SIBLING_LINKS_NAME="$SIBLING_NAME" APPLY_SIBLING_LINKS_EXPECT="$EXPECT" node --input-type=module -e ${shSingleQuote(script)}`;
181
+ }
182
+ function pythonVerifyCommand() {
183
+ // Python < 3.12 forbids backslashes inside f-string expressions, so we
184
+ // can't inline `{",".join(missing)}` (which needs `\",\".` when written
185
+ // as a JS string literal). Bind the separator to a name outside the
186
+ // f-string first.
187
+ const script = [
188
+ 'import json, os, importlib',
189
+ 'name = os.environ["APPLY_SIBLING_LINKS_NAME"]',
190
+ 'want = json.loads(os.environ["APPLY_SIBLING_LINKS_EXPECT"])',
191
+ 'mod = importlib.import_module(name)',
192
+ 'missing = [k for k in want if not hasattr(mod, k)]',
193
+ 'sep = ","',
194
+ 'if missing:',
195
+ ' print(f"MISSING_EXPORTS in {name}: {sep.join(missing)}", flush=True)',
196
+ ' raise SystemExit(1)',
197
+ 'print(f"{name} OK: {sep.join(want)}", flush=True)',
198
+ ].join('\n');
199
+ return [
200
+ ' APPLY_SIBLING_LINKS_NAME="$SIBLING_NAME" APPLY_SIBLING_LINKS_EXPECT="$EXPECT" \\',
201
+ ` python3 -c ${shSingleQuote(script)} 2>/dev/null || \\`,
202
+ ` APPLY_SIBLING_LINKS_NAME="$SIBLING_NAME" APPLY_SIBLING_LINKS_EXPECT="$EXPECT" python -c ${shSingleQuote(script)}`,
203
+ ].join('\n');
204
+ }
205
+ //# sourceMappingURL=sibling-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sibling-links.js","sourceRoot":"","sources":["../src/sibling-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * State Store — CRUD on swarm_state with optional consensus-gated writes.
3
+ *
4
+ * Provides a key-value store scoped to a workflow run and namespace.
5
+ * When consensus gating is enabled, writes require approval from a
6
+ * ConsensusEngine before being committed.
7
+ */
8
+ import { EventEmitter } from 'node:events';
9
+ import type { DbClient } from './coordinator.js';
10
+ export interface StateEntry {
11
+ id: string;
12
+ runId: string;
13
+ namespace: string;
14
+ key: string;
15
+ value: unknown;
16
+ expiresAt: string | null;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ }
20
+ export interface StateStoreOptions {
21
+ /** Default namespace for keys. */
22
+ namespace?: string;
23
+ /** Default TTL in milliseconds for new entries. */
24
+ defaultTtlMs?: number;
25
+ }
26
+ export interface WriteOptions {
27
+ namespace?: string;
28
+ ttlMs?: number;
29
+ }
30
+ export interface ReadOptions {
31
+ namespace?: string;
32
+ }
33
+ /** Callback invoked to gate a write. Return true to allow, false to reject. */
34
+ export type ConsensusGate = (runId: string, key: string, value: unknown, agent: string) => Promise<boolean>;
35
+ export interface StateStoreEvents {
36
+ 'state:set': (entry: StateEntry) => void;
37
+ 'state:deleted': (runId: string, key: string, namespace: string) => void;
38
+ 'state:gated': (runId: string, key: string, agent: string) => void;
39
+ }
40
+ export declare class StateStore extends EventEmitter {
41
+ private db;
42
+ private defaultNamespace;
43
+ private defaultTtlMs;
44
+ private consensusGate;
45
+ constructor(db: DbClient, options?: StateStoreOptions);
46
+ /**
47
+ * Enable consensus-gated writes. When set, every `set()` call will
48
+ * invoke the gate function before persisting. If the gate returns false,
49
+ * the write is rejected.
50
+ */
51
+ setConsensusGate(gate: ConsensusGate): void;
52
+ clearConsensusGate(): void;
53
+ /**
54
+ * Set a key-value pair. If consensus gating is enabled, the write is
55
+ * subject to approval.
56
+ *
57
+ * @param agent - The agent requesting the write (used for consensus gating).
58
+ */
59
+ set(runId: string, key: string, value: unknown, agent: string, options?: WriteOptions): Promise<StateEntry>;
60
+ get(runId: string, key: string, options?: ReadOptions): Promise<unknown | null>;
61
+ getEntry(runId: string, key: string, options?: ReadOptions): Promise<StateEntry | null>;
62
+ getAll(runId: string, options?: ReadOptions): Promise<StateEntry[]>;
63
+ keys(runId: string, options?: ReadOptions): Promise<string[]>;
64
+ delete(runId: string, key: string, options?: ReadOptions): Promise<boolean>;
65
+ deleteAll(runId: string, options?: ReadOptions): Promise<number>;
66
+ /**
67
+ * Remove all expired entries for a run (or globally if runId is omitted).
68
+ * Returns the number of entries purged.
69
+ */
70
+ purgeExpired(runId?: string): Promise<number>;
71
+ /**
72
+ * Take a snapshot of all state for a run as a plain object.
73
+ * Useful for persisting into workflow_runs.state_snapshot.
74
+ */
75
+ snapshot(runId: string, options?: ReadOptions): Promise<Record<string, unknown>>;
76
+ }
77
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIjD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,CAC1B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACzC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpE;AAID,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,aAAa,CAA8B;gBAEvC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,iBAAsB;IASzD;;;;OAIG;IACH,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAI3C,kBAAkB,IAAI,IAAI;IAM1B;;;;;OAKG;IACG,GAAG,CACP,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,UAAU,CAAC;IAmChB,GAAG,CACP,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAcpB,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAavB,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,EAAE,CAAC;IAclB,IAAI,CACR,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBd,MAAM,CACV,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,OAAO,CAAC;IAgBb,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,CAAC;IAalB;;;OAGG;IACG,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBnD;;;OAGG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAQpC"}
package/dist/state.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * State Store — CRUD on swarm_state with optional consensus-gated writes.
3
+ *
4
+ * Provides a key-value store scoped to a workflow run and namespace.
5
+ * When consensus gating is enabled, writes require approval from a
6
+ * ConsensusEngine before being committed.
7
+ */
8
+ import { randomBytes } from 'node:crypto';
9
+ import { EventEmitter } from 'node:events';
10
+ // ── Store ───────────────────────────────────────────────────────────────────
11
+ export class StateStore extends EventEmitter {
12
+ db;
13
+ defaultNamespace;
14
+ defaultTtlMs;
15
+ consensusGate = null;
16
+ constructor(db, options = {}) {
17
+ super();
18
+ this.db = db;
19
+ this.defaultNamespace = options.namespace ?? 'default';
20
+ this.defaultTtlMs = options.defaultTtlMs ?? null;
21
+ }
22
+ // ── Consensus gating ──────────────────────────────────────────────────
23
+ /**
24
+ * Enable consensus-gated writes. When set, every `set()` call will
25
+ * invoke the gate function before persisting. If the gate returns false,
26
+ * the write is rejected.
27
+ */
28
+ setConsensusGate(gate) {
29
+ this.consensusGate = gate;
30
+ }
31
+ clearConsensusGate() {
32
+ this.consensusGate = null;
33
+ }
34
+ // ── Write ─────────────────────────────────────────────────────────────
35
+ /**
36
+ * Set a key-value pair. If consensus gating is enabled, the write is
37
+ * subject to approval.
38
+ *
39
+ * @param agent - The agent requesting the write (used for consensus gating).
40
+ */
41
+ async set(runId, key, value, agent, options = {}) {
42
+ // Consensus gate check.
43
+ if (this.consensusGate) {
44
+ const allowed = await this.consensusGate(runId, key, value, agent);
45
+ if (!allowed) {
46
+ this.emit('state:gated', runId, key, agent);
47
+ throw new Error(`Write to "${key}" rejected by consensus gate for agent "${agent}"`);
48
+ }
49
+ }
50
+ const namespace = options.namespace ?? this.defaultNamespace;
51
+ const ttlMs = options.ttlMs ?? this.defaultTtlMs;
52
+ const expiresAt = ttlMs ? new Date(Date.now() + ttlMs).toISOString() : null;
53
+ const id = `st_${Date.now()}_${randomBytes(4).toString('hex')}`;
54
+ const now = new Date().toISOString();
55
+ // Upsert: use the unique (run_id, namespace, key) constraint.
56
+ const { rows } = await this.db.query(`INSERT INTO swarm_state (id, run_id, namespace, key, value, expires_at, created_at, updated_at)
57
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $7)
58
+ ON CONFLICT (run_id, namespace, key)
59
+ DO UPDATE SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at, updated_at = EXCLUDED.updated_at
60
+ RETURNING *`, [id, runId, namespace, key, JSON.stringify(value), expiresAt, now]);
61
+ const entry = rows[0];
62
+ this.emit('state:set', entry);
63
+ return entry;
64
+ }
65
+ // ── Read ──────────────────────────────────────────────────────────────
66
+ async get(runId, key, options = {}) {
67
+ const namespace = options.namespace ?? this.defaultNamespace;
68
+ const { rows } = await this.db.query(`SELECT * FROM swarm_state
69
+ WHERE run_id = $1 AND namespace = $2 AND key = $3
70
+ AND (expires_at IS NULL OR expires_at > now())`, [runId, namespace, key]);
71
+ if (rows.length === 0)
72
+ return null;
73
+ return rows[0].value;
74
+ }
75
+ async getEntry(runId, key, options = {}) {
76
+ const namespace = options.namespace ?? this.defaultNamespace;
77
+ const { rows } = await this.db.query(`SELECT * FROM swarm_state
78
+ WHERE run_id = $1 AND namespace = $2 AND key = $3
79
+ AND (expires_at IS NULL OR expires_at > now())`, [runId, namespace, key]);
80
+ return rows[0] ?? null;
81
+ }
82
+ async getAll(runId, options = {}) {
83
+ const namespace = options.namespace ?? this.defaultNamespace;
84
+ const { rows } = await this.db.query(`SELECT * FROM swarm_state
85
+ WHERE run_id = $1 AND namespace = $2
86
+ AND (expires_at IS NULL OR expires_at > now())
87
+ ORDER BY key ASC`, [runId, namespace]);
88
+ return rows;
89
+ }
90
+ async keys(runId, options = {}) {
91
+ const namespace = options.namespace ?? this.defaultNamespace;
92
+ const { rows } = await this.db.query(`SELECT key FROM swarm_state
93
+ WHERE run_id = $1 AND namespace = $2
94
+ AND (expires_at IS NULL OR expires_at > now())
95
+ ORDER BY key ASC`, [runId, namespace]);
96
+ return rows.map((r) => r.key);
97
+ }
98
+ // ── Delete ────────────────────────────────────────────────────────────
99
+ async delete(runId, key, options = {}) {
100
+ const namespace = options.namespace ?? this.defaultNamespace;
101
+ const { rows } = await this.db.query(`DELETE FROM swarm_state WHERE run_id = $1 AND namespace = $2 AND key = $3 RETURNING id`, [runId, namespace, key]);
102
+ if (rows.length > 0) {
103
+ this.emit('state:deleted', runId, key, namespace);
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+ async deleteAll(runId, options = {}) {
109
+ const namespace = options.namespace ?? this.defaultNamespace;
110
+ const { rows } = await this.db.query(`DELETE FROM swarm_state WHERE run_id = $1 AND namespace = $2 RETURNING id`, [runId, namespace]);
111
+ return rows.length;
112
+ }
113
+ // ── Expiry cleanup ────────────────────────────────────────────────────
114
+ /**
115
+ * Remove all expired entries for a run (or globally if runId is omitted).
116
+ * Returns the number of entries purged.
117
+ */
118
+ async purgeExpired(runId) {
119
+ if (runId) {
120
+ const { rows } = await this.db.query(`DELETE FROM swarm_state WHERE run_id = $1 AND expires_at IS NOT NULL AND expires_at <= now() RETURNING id`, [runId]);
121
+ return rows.length;
122
+ }
123
+ const { rows } = await this.db.query(`DELETE FROM swarm_state WHERE expires_at IS NOT NULL AND expires_at <= now() RETURNING id`, []);
124
+ return rows.length;
125
+ }
126
+ // ── Snapshot ───────────────────────────────────────────────────────────
127
+ /**
128
+ * Take a snapshot of all state for a run as a plain object.
129
+ * Useful for persisting into workflow_runs.state_snapshot.
130
+ */
131
+ async snapshot(runId, options = {}) {
132
+ const entries = await this.getAll(runId, options);
133
+ const result = {};
134
+ for (const entry of entries) {
135
+ result[entry.key] = entry.value;
136
+ }
137
+ return result;
138
+ }
139
+ }
140
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8C3C,+EAA+E;AAE/E,MAAM,OAAO,UAAW,SAAQ,YAAY;IAClC,EAAE,CAAW;IACb,gBAAgB,CAAS;IACzB,YAAY,CAAgB;IAC5B,aAAa,GAAyB,IAAI,CAAC;IAEnD,YAAY,EAAY,EAAE,UAA6B,EAAE;QACvD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC;IACnD,CAAC;IAED,yEAAyE;IAEzE;;;;OAIG;IACH,gBAAgB,CAAC,IAAmB;QAClC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED,kBAAkB;QAChB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED,yEAAyE;IAEzE;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CACP,KAAa,EACb,GAAW,EACX,KAAc,EACd,KAAa,EACb,UAAwB,EAAE;QAE1B,wBAAwB;QACxB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACnE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC5C,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,2CAA2C,KAAK,GAAG,CACpE,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC;QACjD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5E,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,8DAA8D;QAC9D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC;;;;mBAIa,EACb,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,CACnE,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,yEAAyE;IAEzE,KAAK,CAAC,GAAG,CACP,KAAa,EACb,GAAW,EACX,UAAuB,EAAE;QAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAE7D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC;;wDAEkD,EAClD,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CACxB,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACnC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,KAAa,EACb,GAAW,EACX,UAAuB,EAAE;QAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAE7D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC;;wDAEkD,EAClD,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CACxB,CAAC;QAEF,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAAa,EACb,UAAuB,EAAE;QAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAE7D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC;;;wBAGkB,EAClB,CAAC,KAAK,EAAE,SAAS,CAAC,CACnB,CAAC;QAEF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAI,CACR,KAAa,EACb,UAAuB,EAAE;QAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAE7D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC;;;wBAGkB,EAClB,CAAC,KAAK,EAAE,SAAS,CAAC,CACnB,CAAC;QAEF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,yEAAyE;IAEzE,KAAK,CAAC,MAAM,CACV,KAAa,EACb,GAAW,EACX,UAAuB,EAAE;QAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAE7D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC,wFAAwF,EACxF,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CACxB,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,SAAS,CACb,KAAa,EACb,UAAuB,EAAE;QAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAE7D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC,2EAA2E,EAC3E,CAAC,KAAK,EAAE,SAAS,CAAC,CACnB,CAAC;QAEF,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,yEAAyE;IAEzE;;;OAGG;IACH,KAAK,CAAC,YAAY,CAAC,KAAc;QAC/B,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC,2GAA2G,EAC3G,CAAC,KAAK,CAAC,CACR,CAAC;YACF,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAClC,2FAA2F,EAC3F,EAAE,CACH,CAAC;QACF,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,0EAA0E;IAE1E;;;OAGG;IACH,KAAK,CAAC,QAAQ,CACZ,KAAa,EACb,UAAuB,EAAE;QAEzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;QAClC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,95 @@
1
+ import { ChannelMessenger } from './channel-messenger.js';
2
+ import type { ProcessSpawner } from './process-spawner.js';
3
+ import { TemplateResolver } from './template-resolver.js';
4
+ import type { StepOutcome } from './trajectory.js';
5
+ import type { AgentDefinition, ErrorHandlingConfig, StepCompletionMode, VerificationCheck, WorkflowStep, WorkflowStepCompletionReason, WorkflowStepRow, WorkflowStepStatus } from './types.js';
6
+ import { type VerificationOptions, type VerificationResult, type VerificationSideEffects } from './verification.js';
7
+ type StateLike = {
8
+ row: WorkflowStepRow;
9
+ };
10
+ export interface StepResult {
11
+ status: WorkflowStepStatus;
12
+ output: string;
13
+ exitCode?: number;
14
+ exitSignal?: string;
15
+ duration: number;
16
+ retries: number;
17
+ completionReason?: WorkflowStepCompletionReason;
18
+ error?: string;
19
+ }
20
+ export interface StepSchedule {
21
+ step: WorkflowStep;
22
+ readyAt: number;
23
+ staggerDelay: number;
24
+ }
25
+ export interface StepExecutorDeps<TState extends StateLike = StateLike> {
26
+ cwd: string;
27
+ runId?: string;
28
+ postToChannel?: (text: string) => void;
29
+ persistStepRow?: (stepId: string, patch: Partial<WorkflowStepRow>) => Promise<void>;
30
+ persistStepOutput?: (runId: string, stepName: string, output: string) => Promise<void>;
31
+ resolveTemplate?: (template: string, context: Record<string, unknown>) => string;
32
+ getStepOutput?: (stepName: string) => string | undefined;
33
+ loadStepOutput?: (runId: string, stepName: string) => string | undefined;
34
+ checkAborted?: () => void;
35
+ waitIfPaused?: () => Promise<void>;
36
+ log?: (message: string) => void;
37
+ processSpawner?: ProcessSpawner;
38
+ templateResolver?: TemplateResolver;
39
+ channelMessenger?: ChannelMessenger;
40
+ verificationRunner?: (check: VerificationCheck, output: string, stepName: string, injectedTaskText?: string, options?: VerificationOptions, sideEffects?: VerificationSideEffects) => VerificationResult;
41
+ executeStep?: (step: WorkflowStep, state: TState, agentMap: Map<string, AgentDefinition>, errorHandling?: ErrorHandlingConfig) => Promise<Partial<StepResult> | void>;
42
+ onStepStarted?: (step: WorkflowStep, state: TState) => Promise<void> | void;
43
+ onStepRetried?: (step: WorkflowStep, state: TState, attempt: number, maxRetries: number) => Promise<void> | void;
44
+ onStepCompleted?: (step: WorkflowStep, state: TState, result: StepResult) => Promise<void> | void;
45
+ onStepFailed?: (step: WorkflowStep, state: TState, result: StepResult) => Promise<void> | void;
46
+ onBeginTrack?: (steps: WorkflowStep[]) => Promise<void> | void;
47
+ onConverge?: (steps: WorkflowStep[], outcomes: StepOutcome[]) => Promise<void> | void;
48
+ markDownstreamSkipped?: (failedStepName: string) => Promise<void>;
49
+ buildCompletionMode?: (stepName: string, completionReason?: WorkflowStepCompletionReason) => StepCompletionMode | undefined;
50
+ }
51
+ export interface MonitorStepOptions<TState extends StateLike, TResult> {
52
+ maxRetries?: number;
53
+ retryDelayMs?: number;
54
+ startMessage?: string;
55
+ onStart?: (attempt: number, state: TState) => Promise<void> | void;
56
+ onRetry?: (attempt: number, maxRetries: number, state: TState) => Promise<void> | void;
57
+ execute: (attempt: number, state: TState) => Promise<TResult>;
58
+ toCompletionResult: (result: TResult, attempt: number, state: TState) => Partial<StepResult>;
59
+ onAttemptFailed?: (error: unknown, attempt: number, state: TState) => Promise<void> | void;
60
+ getFailureResult?: (error: unknown, attempt: number, state: TState) => Partial<StepResult>;
61
+ }
62
+ export declare class StepExecutor<TState extends StateLike = StateLike> {
63
+ private readonly deps;
64
+ private readonly templateResolver;
65
+ private readonly channelMessenger;
66
+ private readonly verificationRunner;
67
+ constructor(deps: StepExecutorDeps<TState>);
68
+ findReady(steps: WorkflowStep[], statuses: Map<string, WorkflowStepStatus> | Map<string, TState>): WorkflowStep[];
69
+ /** @deprecated Use {@link findReady} instead. This is an alias kept for backward compatibility. */
70
+ findReadySteps(steps: WorkflowStep[], statuses: Map<string, WorkflowStepStatus> | Map<string, TState>): WorkflowStep[];
71
+ scheduleStep(step: WorkflowStep, options?: {
72
+ readyAt?: number;
73
+ staggerDelay?: number;
74
+ }): StepSchedule;
75
+ startStep(step: WorkflowStep, state: TState, startMessage?: string): Promise<void>;
76
+ retryStep(step: WorkflowStep, state: TState, attempt: number, maxRetries: number): Promise<void>;
77
+ completeStep(step: WorkflowStep, state: TState, result: Partial<StepResult>): Promise<StepResult>;
78
+ monitorStep<TResult>(step: WorkflowStep, state: TState, options: MonitorStepOptions<TState, TResult>): Promise<StepResult>;
79
+ executeAll(steps: WorkflowStep[], agentMap: Map<string, AgentDefinition>, errorHandling?: ErrorHandlingConfig, providedStates?: Map<string, TState>): Promise<Map<string, StepResult>>;
80
+ executeOne(step: WorkflowStep, agentMap: Map<string, AgentDefinition>, errorHandling?: ErrorHandlingConfig, providedState?: TState): Promise<StepResult>;
81
+ markFailed(stepName: string, error: string): Promise<void>;
82
+ buildStepOutputContext(stepStates: Map<string, TState>): Record<string, {
83
+ output: string;
84
+ }>;
85
+ resolveStepTemplate(template: string, context: Record<string, unknown>): string;
86
+ getChannelMessenger(): ChannelMessenger;
87
+ runVerification(check: VerificationCheck, output: string, stepName: string, injectedTaskText?: string, options?: VerificationOptions): VerificationResult;
88
+ private executeScheduledStep;
89
+ private executeWithProcessSpawner;
90
+ private createEphemeralStates;
91
+ private createEphemeralState;
92
+ private getStatus;
93
+ }
94
+ export {};
95
+ //# sourceMappingURL=step-executor.d.ts.map