@farmslot/agent-runtime 0.20.1 → 0.21.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@
4
4
 
5
5
  - Active-development baseline; add user-facing changes here before release or package publication.
6
6
 
7
+ ## 0.21.0 - 2026-10-14
8
+
9
+ - **BREAKING:** Make acceptance commands read-only, transfer only schema-2 engine claims, and refuse retained schema-1 verdicts for current completion.
10
+ - Resolve completion's evidence-manifest requirement through the selected engine attempt, matching artifact validation without a root copy or symlink.
11
+
7
12
  ## 0.20.1 - 2026-10-14
8
13
 
9
14
  - Use the coordinated recipe release for content-based dependency readiness and engine-owned evidence manifests.
@@ -16,7 +16,7 @@ function usage(exitCode = 0) {
16
16
  ' mark <task-md> <signal-json> <args...>',
17
17
  ' mark <task-dir> sub <start <id> --step N --from src | <id> <n> | <id> complete | <id> blocked | <id> status>',
18
18
  ' artifact-check <task-dir> [args...]',
19
- ' ac <set <AC-N> <verdict> [--proof-mode m] [--evidence path]... | list | render> [--task-dir path]',
19
+ ' ac <list | render> [--task-dir path]',
20
20
  ' task init <task-dir> --flow f --platform p --template id --title t [--run-mode m] [options]',
21
21
  ' recipe-quality build [--input input.json] [--output artifacts/recipe-quality.json]',
22
22
  ' (flags override top-level input fields; training fields are merged)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farmslot/agent-runtime",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "scripts": {
40
40
  "typecheck": "yarn workspace @farmslot/protocol build && tsc --noEmit --project tsconfig.json",
41
- "test": "node --test test/claim-review-gate.test.mjs test/deterministic-checks.test.cjs && node test/mark-checklist-step.test.cjs && node test/mark-subtask.test.cjs && node test/acceptance-ledger.test.cjs && node test/checklist-target-sync.test.mjs && node test/acceptance-ledger-sync.test.mjs && node test/package-exports.test.cjs && node test/check-task-artifact-contract.test.cjs && node test/recipe-quality-builder.test.cjs && node test/task-init.test.mjs && node --import tsx ../../scripts/e2e-subtask-mark.mts && node --import tsx ../../scripts/e2e-acceptance-ledger.mts && node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json src/execution-template && node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json src/task-init && node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json src/native",
41
+ "test": "node --test test/claim-review-gate.test.mjs test/deterministic-checks.test.cjs && node test/mark-checklist-step.test.cjs && node test/mark-subtask.test.cjs && node --test test/acceptance-ledger.test.cjs test/acceptance-ledger-cutover.test.cjs && node test/checklist-target-sync.test.mjs && node test/acceptance-ledger-sync.test.mjs && node test/package-exports.test.cjs && node test/check-task-artifact-contract.test.cjs && node test/recipe-quality-builder.test.cjs && node test/task-init.test.mjs && node --import tsx ../../scripts/e2e-subtask-mark.mts && node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json src/execution-template && node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json src/task-init && node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json src/native",
42
42
  "format": "prettier --write --ignore-path ../../.prettierignore .",
43
43
  "format:check": "prettier --check --ignore-path ../../.prettierignore .",
44
44
  "lint": "eslint \"src/**/*.ts\"",
@@ -46,7 +46,8 @@
46
46
  "quality": "prettier --check --ignore-path ../../.prettierignore . && eslint \"src/**/*.ts\" && yarn typecheck && yarn build && yarn test",
47
47
  "build": "yarn workspace @farmslot/protocol build && rm -rf dist && tsc --project tsconfig.build.json",
48
48
  "prepack": "cd ../.. && node scripts/quality/check-farmslot-package-readiness.mjs --require-changelog --packages @farmslot/agent-runtime && yarn workspace @farmslot/agent-runtime build",
49
- "prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/agent-runtime"
49
+ "prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/agent-runtime",
50
+ "test:acceptance-live": "node --import tsx ../../scripts/e2e-acceptance-ledger.mts"
50
51
  },
51
52
  "dependencies": {
52
53
  "@farmslot/adapter-sdk": "0.12.0",
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * `farmslot-agent ac`: record and read the acceptance-criteria ledger
4
- * (`artifacts/acceptance-status.json`), the one writer of that file.
3
+ * `farmslot-agent ac`: read the engine-owned acceptance-criteria projection
4
+ * (`artifacts/acceptance-status.json`). Retained schema-1 data is display only.
5
5
  *
6
6
  * The ids come from `inputs/handoff.json`, which task init fills from the
7
7
  * criteria it received; this CLI never invents an id or a criterion text.
@@ -10,14 +10,11 @@ const fs = require('node:fs');
10
10
  const path = require('node:path');
11
11
 
12
12
  const {
13
- ACCEPTANCE_PROOF_MODES,
14
- ACCEPTANCE_VERDICTS,
15
13
  AcceptanceRefusal,
16
14
  acceptanceStatusList,
17
15
  handoffAcceptanceCriteria,
18
16
  renderAcceptanceCoverage,
19
17
  readAcceptanceLedger,
20
- setAcceptanceVerdict,
21
18
  } = require('./acceptance-ledger.cjs');
22
19
 
23
20
  const HANDOFF_INPUT = path.join('inputs', 'handoff.json');
@@ -25,12 +22,6 @@ const HANDOFF_INPUT = path.join('inputs', 'handoff.json');
25
22
  const USAGE = [
26
23
  'usage: farmslot-agent ac <command> [--task-dir <path>]',
27
24
  '',
28
- ` ac set <AC-N> <${ACCEPTANCE_VERDICTS.join('|')}> [options]`,
29
- ` --proof-mode <${ACCEPTANCE_PROOF_MODES.join('|')}> how the criterion is proven`,
30
- ' --evidence <task-dir relative path> repeatable; the file must exist',
31
- ' --recipe-node <id> repeatable; recipe node proving it',
32
- ' --actor <identity> required for an automatic-ledger override',
33
- ' --note "..." why, especially for untestable',
34
25
  ' ac list every criterion with its current verdict (JSON, verdict null when unrecorded)',
35
26
  ' ac render the coverage table, ending with the Overall recipe coverage line',
36
27
  '',
@@ -52,20 +43,10 @@ function parseArgs(argv) {
52
43
  const opts = {
53
44
  taskDir: null,
54
45
  positional: [],
55
- proofMode: undefined,
56
- evidence: [],
57
- recipeNodes: [],
58
- note: undefined,
59
- actor: undefined,
60
46
  };
61
47
  for (let i = 0; i < argv.length; i += 1) {
62
48
  const arg = argv[i];
63
49
  if (arg === '--task-dir') opts.taskDir = takeValue(argv, i++, arg);
64
- else if (arg === '--proof-mode') opts.proofMode = takeValue(argv, i++, arg);
65
- else if (arg === '--evidence') opts.evidence.push(takeValue(argv, i++, arg));
66
- else if (arg === '--recipe-node') opts.recipeNodes.push(takeValue(argv, i++, arg));
67
- else if (arg === '--actor') opts.actor = takeValue(argv, i++, arg);
68
- else if (arg === '--note') opts.note = takeValue(argv, i++, arg);
69
50
  else if (arg === '-h' || arg === '--help') usage(0);
70
51
  else if (arg.startsWith('-')) throw new AcceptanceRefusal(`unknown option ${arg}`, 2);
71
52
  else opts.positional.push(arg);
@@ -86,27 +67,14 @@ function resolveTaskDir(explicit) {
86
67
  function main(argv) {
87
68
  const [command, ...rest] = argv;
88
69
  if (!command || command === '-h' || command === '--help') usage(command ? 0 : 2);
70
+ if (command === 'set') {
71
+ throw new AcceptanceRefusal(
72
+ 'Verdicts are engine-owned. Run eligible recipe assertions; a verified person uses farmslot run gate --action mark-untestable for an exception.',
73
+ );
74
+ }
89
75
  const opts = parseArgs(rest);
90
76
  const taskDir = resolveTaskDir(opts.taskDir);
91
77
 
92
- if (command === 'set') {
93
- const [id, verdict, ...extra] = opts.positional;
94
- if (!id || !verdict || extra.length > 0) {
95
- throw new AcceptanceRefusal('usage: ac set <AC-N> <verdict> [options]', 2);
96
- }
97
- const ledger = setAcceptanceVerdict(taskDir, {
98
- id,
99
- verdict,
100
- proofMode: opts.proofMode,
101
- evidence: opts.evidence,
102
- recipeNodes: opts.recipeNodes,
103
- note: opts.note,
104
- actor: opts.actor,
105
- });
106
- const entry = ledger.criteria.find((criterion) => criterion.id === id);
107
- console.log(`${id}: ${entry.verdict}${entry.proofMode ? ` (${entry.proofMode})` : ''}`);
108
- return;
109
- }
110
78
  if (opts.positional.length > 0) {
111
79
  throw new AcceptanceRefusal(`ac ${command} takes no positional arguments`, 2);
112
80
  }
@@ -118,11 +86,15 @@ function main(argv) {
118
86
  const ledger = readAcceptanceLedger(taskDir);
119
87
  if (!ledger) {
120
88
  throw new AcceptanceRefusal(
121
- 'no acceptance ledger yet — record verdicts with `farmslot-agent ac set <id> <verdict>`',
89
+ 'no acceptance ledger yet. Run the task recipe to derive engine verdicts.',
122
90
  );
123
91
  }
124
92
  // The registered criteria, not just the recorded rows: a partial ledger must
125
93
  // not report 1/1 when the task has three criteria.
94
+ if (ledger.schemaVersion === 1)
95
+ process.stdout.write(
96
+ 'Legacy schema-1 ledger: retained display only; no measured recipe proof.\n\n',
97
+ );
126
98
  process.stdout.write(renderAcceptanceCoverage(ledger, handoffAcceptanceCriteria(taskDir)));
127
99
  return;
128
100
  }
@@ -6,11 +6,10 @@ const { atomicWrite, readJson } = require('./mark-io.cjs');
6
6
  // Acceptance-criteria ledger (ADR-060; contract in docs/reference/agent-runtime.md
7
7
  // and docs/reference/task-directory-contract.md).
8
8
  //
9
- // This module is the only writer of artifacts/acceptance-status.json, the way
10
- // `mark` is the only writer of SIGNAL.json. Two commands call it: `farmslot-agent ac`
11
- // and a recipe run with a task dir, which records its proof targets
12
- // (@farmslot/recipe-cli). The ids come from task init, which records the criteria in
13
- // inputs/handoff.json; this module never invents one.
9
+ // The engine derives schema-2 verdicts. This module transfers the selected engine
10
+ // ledger into the task namespace; `farmslot-agent ac` only reads it. Retained
11
+ // schema-1 ledgers remain display input, never current completion proof. The ids
12
+ // come from task init in inputs/handoff.json; this module never invents one.
14
13
  //
15
14
  // Portable contract helpers are shared with protocol clients, including unbuilt slot scripts.
16
15
 
@@ -114,6 +113,11 @@ function readAcceptanceLedger(taskDir) {
114
113
  }
115
114
 
116
115
  function writeAcceptanceLedger(taskDir, ledger) {
116
+ if (ledger?.schemaVersion !== 2) {
117
+ throw new AcceptanceRefusal(
118
+ 'Only schema-2 engine claims may be written; schema-1 ledgers are retained display only.',
119
+ );
120
+ }
117
121
  const issues = validateAcceptanceStatusLedger(ledger);
118
122
  if (issues.length > 0) {
119
123
  throw new AcceptanceRefusal(
@@ -138,70 +142,6 @@ function assertEvidencePath(taskDir, rawPath) {
138
142
  return normalized;
139
143
  }
140
144
 
141
- /**
142
- * Record one verdict. The criterion must be one task init registered: the ledger
143
- * carries exactly the handoff ids, never an id the worker made up.
144
- */
145
- function setAcceptanceVerdict(taskDir, input) {
146
- const criteria = handoffAcceptanceCriteria(taskDir);
147
- if (criteria.length === 0) {
148
- throw new AcceptanceRefusal(
149
- `${HANDOFF_INPUT} lists no acceptance criteria; there is no ledger to write`,
150
- );
151
- }
152
- const criterion = criteria.find((entry) => entry.id === input.id);
153
- if (!criterion) {
154
- throw new AcceptanceRefusal(
155
- `unknown acceptance criterion ${input.id}; ${HANDOFF_INPUT} lists ${criteria
156
- .map((entry) => entry.id)
157
- .join(', ')}`,
158
- );
159
- }
160
- if (!ACCEPTANCE_VERDICTS.includes(input.verdict)) {
161
- throw new AcceptanceRefusal(
162
- `unknown verdict ${input.verdict}; expected one of ${ACCEPTANCE_VERDICTS.join(', ')}`,
163
- );
164
- }
165
- if (input.proofMode !== undefined && !ACCEPTANCE_PROOF_MODES.includes(input.proofMode)) {
166
- throw new AcceptanceRefusal(
167
- `unknown proof mode ${input.proofMode}; expected one of ${ACCEPTANCE_PROOF_MODES.join(', ')}`,
168
- );
169
- }
170
- const evidence = (input.evidence ?? []).map((entry) => assertEvidencePath(taskDir, entry));
171
- const stored = readAcceptanceLedger(taskDir);
172
- if (stored) {
173
- const issues = validateAcceptanceStatusLedger(stored);
174
- if (issues.length > 0) {
175
- throw new AcceptanceRefusal(
176
- `${ACCEPTANCE_STATUS_ARTIFACT} does not match the ledger contract:\n- ${issues.join('\n- ')}`,
177
- );
178
- }
179
- }
180
- if (stored?.schemaVersion === 2) {
181
- throw new AcceptanceRefusal(
182
- 'Automatic verdicts are engine-owned. Request human review; a verified person uses farmslot run gate --action mark-untestable.',
183
- );
184
- }
185
- const byId = new Map((stored?.criteria ?? []).map((entry) => [entry.id, entry]));
186
- byId.set(criterion.id, {
187
- id: criterion.id,
188
- text: criterion.text,
189
- verdict: input.verdict,
190
- ...(input.proofMode ? { proofMode: input.proofMode } : {}),
191
- evidence,
192
- recipeNodes: [...(input.recipeNodes ?? [])],
193
- ...(input.note ? { note: input.note } : {}),
194
- updatedAt: input.now ?? new Date().toISOString(),
195
- });
196
- // Handoff order, so the ledger and the rendered table always read like TASK.md.
197
- const ledger = {
198
- schemaVersion: 1,
199
- criteria: criteria.map((entry) => byId.get(entry.id)).filter(Boolean),
200
- };
201
- writeAcceptanceLedger(taskDir, ledger);
202
- return ledger;
203
- }
204
-
205
145
  /** Transfer one engine-owned execution. Never aggregate sibling attempts. */
206
146
  function setDerivedClaims(taskDir, claims) {
207
147
  if (claims.schemaVersion !== 2) throw new AcceptanceRefusal('Derived claims require schema 2.');
@@ -268,24 +208,26 @@ function acceptanceContractIssues(taskDir, options = {}) {
268
208
  if (!stored) {
269
209
  return [
270
210
  `${ACCEPTANCE_STATUS_ARTIFACT} is missing but ${HANDOFF_INPUT} lists ${criteria.length} ` +
271
- 'acceptance criteria — record a verdict for each with `farmslot-agent ac set <id> <verdict>`',
211
+ 'acceptance criteria. Run the task recipe with an eligible assertion for each criterion.',
212
+ ];
213
+ }
214
+ if (stored.schemaVersion !== 2) {
215
+ return [
216
+ `${ACCEPTANCE_STATUS_ARTIFACT}: legacy schema-1 verdicts are display only. Run the task recipe to obtain schema-2 engine claims.`,
272
217
  ];
273
218
  }
274
219
  const issues = validateAcceptanceStatusLedger(stored);
275
220
  if (issues.length > 0) return issues;
276
221
  const known = new Set(criteria.map((criterion) => criterion.id));
277
222
  for (const entry of stored.criteria) {
278
- if (
279
- !known.has(entry.id) &&
280
- (stored.schemaVersion === 1 || ACCEPTANCE_CRITERION_ID_PATTERN.test(entry.id))
281
- ) {
223
+ if (!known.has(entry.id) && ACCEPTANCE_CRITERION_ID_PATTERN.test(entry.id)) {
282
224
  issues.push(
283
225
  `${ACCEPTANCE_STATUS_ARTIFACT}: ${entry.id} is not an acceptance criterion of this task`,
284
226
  );
285
227
  }
286
228
  }
287
229
  const byId = new Map(stored.criteria.map((entry) => [entry.id, entry]));
288
- if (stored.schemaVersion === 2 && stored.criteria.some((entry) => entry.overrides !== undefined))
230
+ if (stored.criteria.some((entry) => entry.overrides !== undefined))
289
231
  issues.push(
290
232
  'Unverified local overrides require a canonical engine projection; they grant no review authority.',
291
233
  );
@@ -293,21 +235,18 @@ function acceptanceContractIssues(taskDir, options = {}) {
293
235
  const entry = byId.get(criterion.id);
294
236
  if (!entry) {
295
237
  issues.push(
296
- `${criterion.id} has no verdict — run \`farmslot-agent ac set ${criterion.id} <verdict>\``,
238
+ `${criterion.id} has no engine verdict. Run the task recipe with an eligible assertion for this criterion.`,
297
239
  );
298
240
  continue;
299
241
  }
300
242
  if (
301
- !(stored.schemaVersion === 2 && options.deferClaimVerdicts === true) &&
243
+ options.deferClaimVerdicts !== true &&
302
244
  (entry.verdict === 'failed' ||
303
245
  entry.verdict === 'awaiting-review' ||
304
246
  (!options.allowWeak && (entry.verdict === 'missing' || entry.verdict === 'weak')))
305
247
  ) {
306
248
  issues.push(
307
- stored.schemaVersion === 2
308
- ? `${criterion.id} is ${entry.verdict}: run an eligible recipe assertion or request human evidence review.`
309
- : `${criterion.id} is ${entry.verdict}: prove it, or record \`untestable\` with a note ` +
310
- '(a flow may waive this with worker_terminal.acceptance.allowWeak)',
249
+ `${criterion.id} is ${entry.verdict}: run an eligible recipe assertion or request human evidence review.`,
311
250
  );
312
251
  }
313
252
  }
@@ -328,7 +267,6 @@ module.exports = {
328
267
  requireHandoffAcceptanceCriteria,
329
268
  readAcceptanceLedger,
330
269
  renderAcceptanceCoverage,
331
- setAcceptanceVerdict,
332
270
  setDerivedClaims,
333
271
  summarizeAcceptanceStatus,
334
272
  validateAcceptanceStatusLedger,
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'node:url';
7
7
  const require = createRequire(import.meta.url);
8
8
  const {
9
9
  expandedArtifactsForCommand,
10
+ resolveWorkerTerminalArtifactPath,
10
11
  deterministicChecks,
11
12
  } = require('./worker-terminal-contract.cjs');
12
13
  const { SUBTASK_INDEX_REL, openSubtaskUnits } = require('./subtask-unit.cjs');
@@ -164,18 +165,8 @@ function resolveTaskArtifactPath(rel) {
164
165
  }
165
166
 
166
167
  function publicationArtifactPath(rel) {
167
- if (rel !== 'artifacts/evidence-manifest.json') return rel;
168
- const pointerPath = path.join(taskDir, 'artifacts/latest-valid-recipe-run.json');
169
- if (!existsSync(pointerPath)) return rel;
170
- const pointer = recipeProtocol?.sanitizeLatestValidRecipeRunPointer?.(
171
- JSON.parse(readFileSync(pointerPath, 'utf8')),
172
- );
173
- if (!pointer) throw new Error('latest-valid-recipe-run.json: invalid pointer.');
174
- const selected = `artifacts/${pointer.relativeArtifactRoot}/evidence-manifest.json`;
175
168
  const version = terminalContract ? (terminalContract.recordedEvidenceContractVersion ?? 0) : 1;
176
- if (version !== 0 && version !== 1) throw new Error('Unsupported recorded evidence contract.');
177
- if (version === 0 && !existsSync(path.resolve(taskDir, selected))) return rel;
178
- return selected;
169
+ return resolveWorkerTerminalArtifactPath(taskDir, rel, version);
179
170
  }
180
171
 
181
172
  function isRecord(value) {
@@ -6,6 +6,7 @@ const { spawnSync } = require('node:child_process');
6
6
  const {
7
7
  expandedArtifactsForCommand,
8
8
  resolveWorkerTerminalContract,
9
+ resolveWorkerTerminalArtifactPath,
9
10
  deterministicChecks,
10
11
  } = require('./worker-terminal-contract.cjs');
11
12
  const {
@@ -225,7 +226,14 @@ function assertRequiredArtifacts(taskDir, terminalCommand, contract) {
225
226
  );
226
227
  for (const storedPath of expanded.artifacts) {
227
228
  if (storedPath === LEARNINGS_ARTIFACT && opts['skip-learnings']) continue;
228
- assertArtifactFile(taskDir, storedPath);
229
+ assertArtifactFile(
230
+ taskDir,
231
+ resolveWorkerTerminalArtifactPath(
232
+ taskDir,
233
+ storedPath,
234
+ contract.recordedEvidenceContractVersion ?? 0,
235
+ ),
236
+ );
229
237
  }
230
238
  }
231
239
 
@@ -174,6 +174,7 @@ const BUILTIN_FLOW_COMMANDS = {
174
174
  const DEFAULT_WHEN_PRESENT = [
175
175
  {
176
176
  path: 'artifacts/recipe.json',
177
+ // The manifest requirement resolves to the selected engine attempt, not a root copy.
177
178
  alsoRequire: ['artifacts/recipe-coverage.md', 'artifacts/evidence-manifest.json'],
178
179
  requireRecipeQuality: true,
179
180
  requireRecipeCoverage: true,
@@ -432,6 +433,21 @@ function expandedArtifactsForCommand(contract, command, fileExists) {
432
433
 
433
434
  const DETERMINISTIC_REFERENCE = /\[DETERMINISTIC:\s*([A-Za-z_][A-Za-z0-9_.]*)\s*\]/;
434
435
 
436
+ /** Resolve the logical manifest requirement through the existing retained selection. */
437
+ function resolveWorkerTerminalArtifactPath(taskDir, relPath, recordedEvidenceContractVersion = 1) {
438
+ if (relPath !== 'artifacts/evidence-manifest.json') return relPath;
439
+ if (!fs.existsSync(path.join(taskDir, 'artifacts/latest-valid-recipe-run.json'))) return relPath;
440
+ const { readRecipeAttemptSelection } = require('./retained-recipe-attempt.cjs');
441
+ const pointer = readRecipeAttemptSelection(taskDir);
442
+ if (!pointer) throw new Error('Invalid retained recipe attempt selection.');
443
+ const selected = `artifacts/${pointer.relativeArtifactRoot}/evidence-manifest.json`;
444
+ if (recordedEvidenceContractVersion !== 0 && recordedEvidenceContractVersion !== 1)
445
+ throw new Error('Unsupported recorded evidence contract.');
446
+ if (recordedEvidenceContractVersion === 0 && !fs.existsSync(path.resolve(taskDir, selected)))
447
+ return relPath;
448
+ return selected;
449
+ }
450
+
435
451
  function deterministicDeclarations(markdown, checklist) {
436
452
  return enumerateChecklistCheckboxes(markdown).flatMap((item) => {
437
453
  const match = item.rawLabel.match(DETERMINISTIC_REFERENCE);
@@ -770,5 +786,6 @@ module.exports = {
770
786
  templateTerminalCommands,
771
787
  templateBodyScope,
772
788
  expandedArtifactsForCommand,
789
+ resolveWorkerTerminalArtifactPath,
773
790
  deterministicChecks,
774
791
  };