harnery 0.22.1 → 0.23.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 (99) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/workflow.d.ts.map +1 -1
  3. package/dist/commands/workflow.js +91 -2
  4. package/dist/core/work/state.d.ts +7 -0
  5. package/dist/core/work/state.d.ts.map +1 -1
  6. package/dist/core/work/state.js +112 -23
  7. package/dist/core/workflow/child-env.d.ts +10 -6
  8. package/dist/core/workflow/child-env.d.ts.map +1 -1
  9. package/dist/core/workflow/child-env.js +15 -7
  10. package/dist/core/workflow/durable-record.d.ts +7 -0
  11. package/dist/core/workflow/durable-record.d.ts.map +1 -0
  12. package/dist/core/workflow/durable-record.js +83 -0
  13. package/dist/core/workflow/engine.d.ts.map +1 -1
  14. package/dist/core/workflow/engine.js +208 -29
  15. package/dist/core/workflow/index.d.ts +2 -0
  16. package/dist/core/workflow/index.d.ts.map +1 -1
  17. package/dist/core/workflow/index.js +1 -0
  18. package/dist/core/workflow/proof.d.ts +4 -0
  19. package/dist/core/workflow/proof.d.ts.map +1 -1
  20. package/dist/core/workflow/proof.js +50 -15
  21. package/dist/core/workflow/run-state.d.ts +3 -0
  22. package/dist/core/workflow/run-state.d.ts.map +1 -1
  23. package/dist/core/workflow/run-state.js +25 -13
  24. package/dist/core/workflow/types.d.ts +10 -0
  25. package/dist/core/workflow/types.d.ts.map +1 -1
  26. package/dist/core/workflow/workspaces/attestation-error.d.ts +7 -0
  27. package/dist/core/workflow/workspaces/attestation-error.d.ts.map +1 -0
  28. package/dist/core/workflow/workspaces/attestation-error.js +25 -0
  29. package/dist/core/workflow/workspaces/cancellation.d.ts +8 -0
  30. package/dist/core/workflow/workspaces/cancellation.d.ts.map +1 -0
  31. package/dist/core/workflow/workspaces/cancellation.js +95 -0
  32. package/dist/core/workflow/workspaces/cleanup.d.ts +9 -0
  33. package/dist/core/workflow/workspaces/cleanup.d.ts.map +1 -0
  34. package/dist/core/workflow/workspaces/cleanup.js +223 -0
  35. package/dist/core/workflow/workspaces/execution.d.ts +25 -0
  36. package/dist/core/workflow/workspaces/execution.d.ts.map +1 -0
  37. package/dist/core/workflow/workspaces/execution.js +316 -0
  38. package/dist/core/workflow/workspaces/git.d.ts +25 -0
  39. package/dist/core/workflow/workspaces/git.d.ts.map +1 -0
  40. package/dist/core/workflow/workspaces/git.js +102 -0
  41. package/dist/core/workflow/workspaces/index.d.ts +15 -0
  42. package/dist/core/workflow/workspaces/index.d.ts.map +1 -0
  43. package/dist/core/workflow/workspaces/index.js +7 -0
  44. package/dist/core/workflow/workspaces/inspect.d.ts +85 -0
  45. package/dist/core/workflow/workspaces/inspect.d.ts.map +1 -0
  46. package/dist/core/workflow/workspaces/inspect.js +255 -0
  47. package/dist/core/workflow/workspaces/integration.d.ts +24 -0
  48. package/dist/core/workflow/workspaces/integration.d.ts.map +1 -0
  49. package/dist/core/workflow/workspaces/integration.js +617 -0
  50. package/dist/core/workflow/workspaces/leases.d.ts +37 -0
  51. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -0
  52. package/dist/core/workflow/workspaces/leases.js +380 -0
  53. package/dist/core/workflow/workspaces/lifecycle.d.ts +43 -0
  54. package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -0
  55. package/dist/core/workflow/workspaces/lifecycle.js +622 -0
  56. package/dist/core/workflow/workspaces/local-git.d.ts +8 -0
  57. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -0
  58. package/dist/core/workflow/workspaces/local-git.js +1474 -0
  59. package/dist/core/workflow/workspaces/paths.d.ts +23 -0
  60. package/dist/core/workflow/workspaces/paths.d.ts.map +1 -0
  61. package/dist/core/workflow/workspaces/paths.js +191 -0
  62. package/dist/core/workflow/workspaces/recovery.d.ts +3 -0
  63. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -0
  64. package/dist/core/workflow/workspaces/recovery.js +50 -0
  65. package/dist/core/workflow/workspaces/state.d.ts +60 -0
  66. package/dist/core/workflow/workspaces/state.d.ts.map +1 -0
  67. package/dist/core/workflow/workspaces/state.js +296 -0
  68. package/dist/core/workflow/workspaces/types.d.ts +445 -0
  69. package/dist/core/workflow/workspaces/types.d.ts.map +1 -0
  70. package/dist/core/workflow/workspaces/types.js +2 -0
  71. package/dist/core/workflow/workspaces/validate.d.ts +14 -0
  72. package/dist/core/workflow/workspaces/validate.d.ts.map +1 -0
  73. package/dist/core/workflow/workspaces/validate.js +476 -0
  74. package/package.json +5 -3
  75. package/src/commands/workflow.ts +110 -4
  76. package/src/core/work/state.ts +156 -28
  77. package/src/core/workflow/child-env.ts +18 -7
  78. package/src/core/workflow/durable-record.ts +94 -0
  79. package/src/core/workflow/engine.ts +230 -30
  80. package/src/core/workflow/index.ts +73 -0
  81. package/src/core/workflow/proof.ts +70 -22
  82. package/src/core/workflow/run-state.ts +46 -11
  83. package/src/core/workflow/types.ts +14 -0
  84. package/src/core/workflow/workspaces/attestation-error.ts +30 -0
  85. package/src/core/workflow/workspaces/cancellation.ts +137 -0
  86. package/src/core/workflow/workspaces/cleanup.ts +308 -0
  87. package/src/core/workflow/workspaces/execution.ts +412 -0
  88. package/src/core/workflow/workspaces/git.ts +125 -0
  89. package/src/core/workflow/workspaces/index.ts +85 -0
  90. package/src/core/workflow/workspaces/inspect.ts +421 -0
  91. package/src/core/workflow/workspaces/integration.ts +829 -0
  92. package/src/core/workflow/workspaces/leases.ts +495 -0
  93. package/src/core/workflow/workspaces/lifecycle.ts +792 -0
  94. package/src/core/workflow/workspaces/local-git.ts +1892 -0
  95. package/src/core/workflow/workspaces/paths.ts +247 -0
  96. package/src/core/workflow/workspaces/recovery.ts +56 -0
  97. package/src/core/workflow/workspaces/state.ts +481 -0
  98. package/src/core/workflow/workspaces/types.ts +517 -0
  99. package/src/core/workflow/workspaces/validate.ts +595 -0
@@ -33,12 +33,16 @@ import { evaluatePolicy, normalizePolicy, PolicyDeniedError, policyDigest, summa
33
33
  import { assertWorkflowRunId, createWorkflowApproval } from "./approvals.js";
34
34
  import { freezeWorkflowAttemptContext } from "./attempt-context.js";
35
35
  import { probeBilling } from "./billing.js";
36
+ import { stableDigest } from "./durable-record.js";
36
37
  import { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, writeWorkflowProof, } from "./proof.js";
37
38
  import { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
38
39
  import { normalizeWorkflowSpecialists, resolveSpecialistAssignment } from "./specialists.js";
39
40
  import { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
40
41
  import { parseStageOutput, validateAgainstSchema } from "./validate.js";
41
42
  import { freezeWorkflowWorkContext } from "./work-context.js";
43
+ import { attestTerminal, attestWorkspaceFailure, resolveWorkspaceBinding, } from "./workspaces/execution.js";
44
+ import { appendWorkflowJournalEvent } from "./workspaces/state.js";
45
+ import { isWorkspaceAttestation } from "./workspaces/validate.js";
42
46
  const DEFAULT_MAX_AGENTS = 50;
43
47
  const DEFAULT_CONCURRENCY = 4;
44
48
  const DEFAULT_MAX_ATTEMPTS = 2;
@@ -136,13 +140,19 @@ export async function runWorkflow(scriptPath, opts) {
136
140
  }
137
141
  }
138
142
  async function executeWorkflow(scriptPath, absScript, opts, resumeState, workContext, attemptContext) {
139
- const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(absScript).href)));
140
- if (typeof mod.default !== "function") {
141
- throw new Error(`${scriptPath}: workflow script must \`export default async (ctx) => …\``);
143
+ const frozen = resumeState?.manifest.execution;
144
+ const isolation = frozen?.isolation ?? opts.isolation ?? "shared";
145
+ let sharedWorkflow;
146
+ let sharedMeta;
147
+ if (isolation === "shared") {
148
+ const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(absScript).href)));
149
+ if (typeof mod.default !== "function") {
150
+ throw new Error(`${scriptPath}: workflow script must \`export default async (ctx) => …\``);
151
+ }
152
+ const fallbackName = scriptPath.replace(/^.*\//, "").replace(/\.[cm]?js$/, "");
153
+ sharedWorkflow = mod.default;
154
+ sharedMeta = normalizeWorkflowMeta(mod.meta, fallbackName);
142
155
  }
143
- const fallbackName = scriptPath.replace(/^.*\//, "").replace(/\.[cm]?js$/, "");
144
- const meta = normalizeWorkflowMeta(mod.meta, fallbackName);
145
- const name = meta.name;
146
156
  const runId = opts.resumeRunId ??
147
157
  opts.runId ??
148
158
  `wf-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}`;
@@ -150,7 +160,6 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
150
160
  mkdirSync(runDir, { recursive: true });
151
161
  const journalPath = join(runDir, "journal.jsonl");
152
162
  const proofPath = join(runDir, "proof.json");
153
- const frozen = resumeState?.manifest.execution;
154
163
  const maxAgents = frozen?.max_agents ?? opts.maxAgents ?? DEFAULT_MAX_AGENTS;
155
164
  const concurrency = frozen?.concurrency ?? opts.concurrency ?? DEFAULT_CONCURRENCY;
156
165
  assertPositiveWorkflowBound(maxAgents, "maxAgents");
@@ -159,16 +168,105 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
159
168
  const log = opts.onLog ?? ((line) => process.stderr.write(`${line}\n`));
160
169
  const defaultHarness = frozen?.default_harness ?? opts.defaultHarness ?? "claude-code";
161
170
  const specialists = frozen?.specialists ?? (resumeState ? {} : normalizeWorkflowSpecialists(opts.specialists));
162
- const isolation = frozen?.isolation ?? opts.isolation ?? "shared";
163
171
  const networkAccess = frozen?.network_access ?? opts.networkAccess ?? "unknown";
164
172
  const policy = frozen?.policy ?? (opts.policy ? normalizePolicy(opts.policy, { baseDir: cwd }) : undefined);
165
173
  const approvalMode = frozen?.approval_mode ?? opts.approvalMode ?? "deny";
166
174
  const approvalAddressee = frozen?.approval_addressee ?? opts.approvalAddressee ?? "operator";
167
175
  const subscriptionOnly = frozen?.subscription_only ?? Boolean(opts.subscriptionOnly);
168
176
  const allowApiBilling = frozen?.allow_api_billing ?? Boolean(opts.allowApiBilling);
169
- const repoBefore = resumeState?.manifest.repository_before ?? snapshotRepo(cwd);
170
177
  const startedAt = resumeState?.manifest.started_at ?? new Date().toISOString();
171
178
  const t0 = resumeState ? Date.parse(startedAt) : Date.now();
179
+ const scriptSha256 = resumeState?.manifest.script.sha256 ?? workflowScriptDigest(absScript);
180
+ const fallbackName = scriptPath.replace(/^.*\//, "").replace(/\.[cm]?js$/, "");
181
+ const fallbackMeta = normalizeWorkflowMeta(undefined, fallbackName);
182
+ const workspaceProvider = isolation === "shared" ? undefined : opts.workspace?.provider;
183
+ let workspaceBinding;
184
+ let workspaceFallback = resumeState?.manifest.execution.workspace_fallback;
185
+ try {
186
+ workspaceBinding = await resolveWorkspaceBinding({
187
+ opts,
188
+ resumeState,
189
+ runId,
190
+ cwd,
191
+ isolation,
192
+ absScript,
193
+ scriptSha256,
194
+ provider: workspaceProvider,
195
+ workContext,
196
+ attemptContext,
197
+ policy,
198
+ onUnsupportedFallback: (evidence) => {
199
+ workspaceFallback = evidence;
200
+ },
201
+ });
202
+ }
203
+ catch (error) {
204
+ const frozenBinding = resumeState?.manifest.execution.workspace_binding;
205
+ if (!resumeState || !frozenBinding)
206
+ throw error;
207
+ const failure = error instanceof Error ? error : new Error(String(error));
208
+ const endedAt = new Date().toISOString();
209
+ const durationMs = Date.now() - t0;
210
+ const failedAttestation = await attestWorkspaceFailure(workspaceProvider, frozenBinding, error);
211
+ appendWorkflowJournalEvent(opts.coordRoot, runId, "run.resume", {
212
+ approval_id: resumeState.approvalId,
213
+ script: absScript,
214
+ resumed_at: new Date().toISOString(),
215
+ });
216
+ appendWorkflowJournalEvent(opts.coordRoot, runId, "workspace.reattach.failed", {
217
+ binding_id: frozenBinding.binding_id,
218
+ provider_id: frozenBinding.provider.id,
219
+ status: failedAttestation.status,
220
+ reason: failure.message,
221
+ attestation_sha256: stableDigest(failedAttestation),
222
+ });
223
+ appendWorkflowJournalEvent(opts.coordRoot, runId, "run.end", {
224
+ ok: false,
225
+ error: failure.message,
226
+ agents: 0,
227
+ cached: 0,
228
+ cost_usd: 0,
229
+ duration_ms: durationMs,
230
+ });
231
+ try {
232
+ const proof = buildWorkflowProof({
233
+ runId,
234
+ workItemId: opts.workItemId ?? resumeState.manifest.work_item_id,
235
+ workContext,
236
+ attemptContext,
237
+ meta: fallbackMeta,
238
+ status: "failed",
239
+ startedAt,
240
+ endedAt,
241
+ durationMs,
242
+ journalPath,
243
+ before: resumeState.manifest.repository_before,
244
+ after: resumeState.manifest.repository_before,
245
+ agents: [],
246
+ evidence: [],
247
+ harnessEvidence: opts.harnessEvidence,
248
+ policy: policy
249
+ ? {
250
+ config: policy,
251
+ decisions: [],
252
+ isolation,
253
+ networkAccess,
254
+ }
255
+ : undefined,
256
+ workspaceBinding: frozenBinding,
257
+ workspaceAttestation: failedAttestation,
258
+ error: failure.message,
259
+ });
260
+ writeWorkflowProof(proofPath, proof);
261
+ }
262
+ catch (proofError) {
263
+ throw new WorkflowRunError(`${failure.message}; recovery proof packet write also failed: ${proofError.message}`, runId, proofPath, failure);
264
+ }
265
+ throw new WorkflowRunError(failure.message, runId, proofPath, failure);
266
+ }
267
+ const effectiveIsolation = workspaceFallback?.effective_isolation ?? isolation;
268
+ const executionCwd = workspaceBinding?.active_root ?? cwd;
269
+ const executionRepoBefore = resumeState?.manifest.repository_before ?? snapshotRepo(executionCwd);
172
270
  if (!resumeState) {
173
271
  writeWorkflowRunManifest({
174
272
  coordRoot: opts.coordRoot,
@@ -178,12 +276,12 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
178
276
  work_item_id: opts.workItemId,
179
277
  work_context: workContext,
180
278
  attempt_context: attemptContext,
181
- name,
279
+ name: sharedMeta?.name ?? fallbackMeta.name,
182
280
  started_at: startedAt,
183
- script: { path: absScript, sha256: workflowScriptDigest(absScript) },
184
- repository_before: repoBefore,
281
+ script: { path: absScript, sha256: scriptSha256 },
282
+ repository_before: executionRepoBefore,
185
283
  execution: {
186
- cwd,
284
+ cwd: executionCwd,
187
285
  default_harness: defaultHarness,
188
286
  max_agents: maxAgents,
189
287
  concurrency,
@@ -195,17 +293,70 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
195
293
  network_access: networkAccess,
196
294
  policy: policy ? policy : undefined,
197
295
  specialists,
296
+ workspace_binding: workspaceBinding,
297
+ workspace_fallback: workspaceFallback,
198
298
  },
199
299
  },
200
300
  });
201
301
  }
302
+ let workflow = sharedWorkflow;
303
+ let meta = sharedMeta ?? fallbackMeta;
304
+ let initializationError;
305
+ let initializationAttestation;
306
+ if (isolation !== "shared") {
307
+ try {
308
+ const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(absScript).href)));
309
+ if (typeof mod.default !== "function") {
310
+ throw new Error(`${scriptPath}: workflow script must \`export default async (ctx) => …\``);
311
+ }
312
+ workflow = mod.default;
313
+ meta = normalizeWorkflowMeta(mod.meta, fallbackName);
314
+ }
315
+ catch (error) {
316
+ initializationError = error instanceof Error ? error : new Error(String(error));
317
+ }
318
+ }
319
+ if (workspaceBinding && workspaceProvider) {
320
+ try {
321
+ const postImport = await workspaceProvider.reattach(workspaceBinding);
322
+ if (!isWorkspaceAttestation(postImport, workspaceBinding) || postImport.status !== "ok") {
323
+ initializationAttestation = isWorkspaceAttestation(postImport, workspaceBinding)
324
+ ? postImport
325
+ : undefined;
326
+ throw new Error("workspace authority changed during workflow module initialization");
327
+ }
328
+ appendWorkflowJournalEvent(opts.coordRoot, runId, "workspace.reattach.module_initialized", {
329
+ binding_id: workspaceBinding.binding_id,
330
+ provider_id: workspaceBinding.provider.id,
331
+ attestation_sha256: stableDigest(postImport),
332
+ });
333
+ }
334
+ catch (error) {
335
+ const reattachmentError = error instanceof Error ? error : new Error(String(error));
336
+ initializationAttestation ??= await attestWorkspaceFailure(workspaceProvider, workspaceBinding, error);
337
+ initializationError = initializationError
338
+ ? new Error(`${initializationError.message}; ${reattachmentError.message}`, {
339
+ cause: reattachmentError,
340
+ })
341
+ : reattachmentError;
342
+ appendWorkflowJournalEvent(opts.coordRoot, runId, "workspace.reattach.module_initialization_failed", {
343
+ binding_id: workspaceBinding.binding_id,
344
+ provider_id: workspaceBinding.provider.id,
345
+ status: initializationAttestation.status,
346
+ reason: reattachmentError.message,
347
+ attestation_sha256: stableDigest(initializationAttestation),
348
+ });
349
+ }
350
+ }
351
+ const name = meta.name;
352
+ let observedWorkspaceAttestation = initializationAttestation;
202
353
  // Per-child fixed context overhead: children spawn in `cwd` and load its
203
354
  // repo-instructions file into their system prompt, cache-writing it once
204
355
  // per child. A fan-out multiplies this, so surface it BEFORE the burn.
205
- const contextTokensPerChildEstimate = estimateInstructionTokens(cwd);
356
+ const contextTokensPerChildEstimate = estimateInstructionTokens(executionCwd);
206
357
  if (contextTokensPerChildEstimate > 0) {
207
358
  log(`[context] each child cache-writes ~${Math.round(contextTokensPerChildEstimate / 1000)}K tokens of repo ` +
208
- `instructions from ${cwd}; a fan-out multiplies this per agent`);
359
+ `instructions from ${executionCwd}; a fan-out multiplies this per agent`);
209
360
  }
210
361
  // Resume: journaled results of a prior run, keyed by agent-call identity.
211
362
  const resumeSource = opts.resumeRunId ?? opts.resumeFrom;
@@ -434,7 +585,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
434
585
  const estimateRequest = summarizePolicyRequest({
435
586
  phase: "dispatch",
436
587
  action: "spawn agent",
437
- path: cwd,
588
+ path: executionCwd,
438
589
  harness,
439
590
  model: agentOpts.model,
440
591
  effort: agentOpts.effort,
@@ -442,7 +593,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
442
593
  max_turns: agentOpts.maxTurns ?? DEFAULT_MAX_TURNS,
443
594
  timeout_ms: agentOpts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
444
595
  prompt_bytes: Buffer.byteLength(dispatchPrompt),
445
- isolation,
596
+ isolation: effectiveIsolation,
446
597
  network_access: networkAccess,
447
598
  current_cost_usd: round4(costUsd + reservedCostUsd),
448
599
  projected_cost_usd: null,
@@ -571,7 +722,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
571
722
  effort: agentOpts.effort,
572
723
  timeoutMs: agentOpts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
573
724
  maxTurns: agentOpts.maxTurns ?? DEFAULT_MAX_TURNS,
574
- cwd,
725
+ cwd: executionCwd,
575
726
  runId,
576
727
  subscriptionOnly,
577
728
  });
@@ -689,8 +840,8 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
689
840
  return authorizePolicyRequest({
690
841
  phase: "external_mutation",
691
842
  action: input.action,
692
- path: input.path ? resolve(cwd, input.path) : undefined,
693
- isolation,
843
+ path: input.path ? resolve(executionCwd, input.path) : undefined,
844
+ isolation: effectiveIsolation,
694
845
  network_access: network,
695
846
  service: input.service,
696
847
  target: input.target,
@@ -728,13 +879,27 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
728
879
  concurrency,
729
880
  specialists: Object.keys(specialists),
730
881
  policy: policy ? { name: policy.name, sha256: policyDigest(policy) } : null,
731
- isolation,
882
+ isolation: effectiveIsolation,
732
883
  network_access: networkAccess,
884
+ workspace_binding: workspaceBinding
885
+ ? { binding_id: workspaceBinding.binding_id, provider_id: workspaceBinding.provider.id }
886
+ : null,
733
887
  });
734
888
  }
735
- const result = await mod.default(ctx);
889
+ if (initializationError)
890
+ throw initializationError;
891
+ if (!workflow)
892
+ throw new Error("workflow module initialization did not produce an executable");
893
+ const result = await workflow(ctx);
736
894
  const endedAt = new Date().toISOString();
737
895
  const durationMs = Date.now() - t0;
896
+ const workspaceAttestation = workspaceBinding
897
+ ? await attestTerminal(workspaceProvider, workspaceBinding)
898
+ : undefined;
899
+ observedWorkspaceAttestation = workspaceAttestation;
900
+ if (workspaceAttestation && workspaceAttestation.status !== "ok") {
901
+ throw new Error(`terminal workspace attestation ${workspaceAttestation.status}: ${workspaceAttestation.provider_drift.join("; ") || "provider could not prove the workspace"}`);
902
+ }
738
903
  journal("run.end", {
739
904
  ok: true,
740
905
  agents: agentsSpawned,
@@ -755,8 +920,8 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
755
920
  endedAt,
756
921
  durationMs,
757
922
  journalPath,
758
- before: repoBefore,
759
- after: snapshotRepo(cwd),
923
+ before: executionRepoBefore,
924
+ after: snapshotRepo(executionCwd),
760
925
  agents: Array.from(agentProofs.values()),
761
926
  evidence: evidenceRecords,
762
927
  harnessEvidence: opts.harnessEvidence,
@@ -764,10 +929,13 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
764
929
  ? {
765
930
  config: policy,
766
931
  decisions: Array.from(policyDecisions.values()),
767
- isolation,
932
+ isolation: effectiveIsolation,
768
933
  networkAccess,
769
934
  }
770
935
  : undefined,
936
+ workspaceBinding,
937
+ workspaceAttestation,
938
+ workspaceFallback,
771
939
  result,
772
940
  });
773
941
  writeWorkflowProof(proofPath, proof);
@@ -793,11 +961,15 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
793
961
  mode: subscriptionOnly ? "subscription" : p.mode,
794
962
  })),
795
963
  policy: proof.policy?.summary,
964
+ workspaceBinding,
796
965
  };
797
966
  return report;
798
967
  }
799
968
  catch (err) {
800
- if (err instanceof WorkflowParkedError || err instanceof WorkflowRunError)
969
+ if (err instanceof WorkflowParkedError) {
970
+ throw err;
971
+ }
972
+ if (err instanceof WorkflowRunError)
801
973
  throw err;
802
974
  const endedAt = new Date().toISOString();
803
975
  const durationMs = Date.now() - t0;
@@ -810,6 +982,10 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
810
982
  duration_ms: durationMs,
811
983
  });
812
984
  try {
985
+ const workspaceAttestation = workspaceBinding
986
+ ? (observedWorkspaceAttestation ??
987
+ (await attestTerminal(workspaceProvider, workspaceBinding)))
988
+ : undefined;
813
989
  const proof = buildWorkflowProof({
814
990
  runId,
815
991
  workItemId: opts.workItemId ?? resumeState?.manifest.work_item_id,
@@ -821,8 +997,8 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
821
997
  endedAt,
822
998
  durationMs,
823
999
  journalPath,
824
- before: repoBefore,
825
- after: snapshotRepo(cwd),
1000
+ before: executionRepoBefore,
1001
+ after: snapshotRepo(executionCwd),
826
1002
  agents: Array.from(agentProofs.values()),
827
1003
  evidence: evidenceRecords,
828
1004
  harnessEvidence: opts.harnessEvidence,
@@ -830,10 +1006,13 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
830
1006
  ? {
831
1007
  config: policy,
832
1008
  decisions: Array.from(policyDecisions.values()),
833
- isolation,
1009
+ isolation: effectiveIsolation,
834
1010
  networkAccess,
835
1011
  }
836
1012
  : undefined,
1013
+ workspaceBinding,
1014
+ workspaceAttestation,
1015
+ workspaceFallback,
837
1016
  error: err.message,
838
1017
  });
839
1018
  writeWorkflowProof(proofPath, proof);
@@ -7,4 +7,6 @@ export type { WorkflowRunManifest } from "./run-state.js";
7
7
  export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
8
8
  export type { AcceptanceCriterion, AcceptanceResult, AcceptanceStatus, AcceptanceSummary, AgentOpts, EngineOpts, EvidenceKind, EvidenceSource, EvidenceStatus, HarnessEvidenceCapability, HarnessEvidenceCoverage, HarnessName, ResultDigest, RunReport, Spawner, SpawnRequest, SpawnResult, StageSchema, WorkflowAgentProof, WorkflowApprovalMode, WorkflowAttemptContext, WorkflowAttemptFailureCause, WorkflowAttemptPriorContext, WorkflowAttemptUnresolvedCriterion, WorkflowContext, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowModule, WorkflowPolicyProof, WorkflowProof, WorkflowProofUnknown, WorkflowRepoEvidence, WorkflowRepoSnapshot, WorkflowRunStatus, WorkflowSpecialistProfile, WorkflowWorkContext, } from "./types.js";
9
9
  export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
10
+ export type { ApplyIntegrationInput, AuthorizedIntegrationPlan, AuthorizedProviderIntegrationInput, CancelWorkspaceInput, CleanupWorkspaceInput, FilesystemIdentity, IntegrationApplyAttempt, IntegrationApplyReceipt, IntegrationAuthorization, IntegrationPlan, IntegrationReviewRecord, IntegrationTarget, LocalGitWorktreeProviderOptions, PrepareIntegrationInput, ProviderIntegrationInput, ProviderIntegrationPreview, ProviderIntegrationResult, ProviderUnknown, ProviderUnsupportedReason, RepositoryBinding, ValidatedFilesystemPath, WorkflowWorkspaceInspection, WorkflowWorkspaceSelection, WorkflowWorkspaceStatus, WorkflowWorkspaceVerification, WorkspaceAllocationRequest, WorkspaceAttestation, WorkspaceBinding, WorkspaceBoundExecutionEvidence, WorkspaceCancellationReceipt, WorkspaceCancellationResult, WorkspaceCleanupAttempt, WorkspaceCleanupIntent, WorkspaceCleanupMode, WorkspaceCleanupReceipt, WorkspaceCleanupResult, WorkspaceCompatibilityExecutionEvidence, WorkspaceExecutionEvidence, WorkspaceIntegrationState, WorkspaceIsolation, WorkspaceLifecycleProjection, WorkspaceLifecycleProjectionInput, WorkspaceLifecycleState, WorkspaceObservation, WorkspaceOwner, WorkspaceProbeInput, WorkspaceProbeResult, WorkspaceProofOutcome, WorkspaceProvider, WorkspaceProviderCapabilities, WorkspaceProviderRef, WorkspaceResourceState, WorkspaceUnsupportedExecutionEvidence, } from "./workspaces/index.js";
11
+ export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,4BAA4B,EAC5B,iCAAiC,EACjC,+BAA+B,GAChC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,EAC7B,uBAAuB,EACvB,oCAAoC,EACpC,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,yBAAyB,EACzB,uBAAuB,EACvB,WAAW,EACX,YAAY,EACZ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,kCAAkC,EAClC,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uCAAuC,EACvC,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"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"}
@@ -4,3 +4,4 @@ export { runWorkflow, WorkflowParkedError, WorkflowRunError } from "./engine.js"
4
4
  export { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
5
5
  export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
6
6
  export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
7
+ export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
@@ -1,6 +1,7 @@
1
1
  import type { RepoSnapshot } from "../context/index.js";
2
2
  import type { NormalizedPolicy, PolicyDecision, PolicyIsolation, PolicyNetworkAccess } from "../policy/index.js";
3
3
  import type { AcceptanceCriterion, AcceptanceResult, AcceptanceSummary, HarnessEvidenceCapability, ResultDigest, WorkflowAgentProof, WorkflowAttemptContext, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowProof, WorkflowWorkContext } from "./types.js";
4
+ import type { WorkspaceAttestation, WorkspaceBinding, WorkspaceUnsupportedExecutionEvidence } from "./workspaces/index.js";
4
5
  export interface NormalizedWorkflowMeta {
5
6
  name: string;
6
7
  description?: string;
@@ -29,6 +30,9 @@ export interface BuildWorkflowProofInput {
29
30
  isolation: PolicyIsolation;
30
31
  networkAccess: PolicyNetworkAccess;
31
32
  };
33
+ workspaceBinding?: WorkspaceBinding;
34
+ workspaceAttestation?: WorkspaceAttestation;
35
+ workspaceFallback?: WorkspaceUnsupportedExecutionEvidence;
32
36
  result?: unknown;
33
37
  error?: string;
34
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,13 @@
1
- import { createHash, randomUUID } from "node:crypto";
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
3
- import { dirname, join } from "node:path";
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { join } from "node:path";
4
4
  import { policyDigest } from "../policy/index.js";
5
5
  import { isCanonicalWorkflowAttemptContext } from "./attempt-context.js";
6
+ import { stableDigest, writeImmutableJson } from "./durable-record.js";
7
+ import { readWorkflowRunManifest } from "./run-state.js";
6
8
  import { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
7
9
  import { isCanonicalWorkflowWorkContext } from "./work-context.js";
10
+ import { isWorkspaceBoundExecutionEvidence, isWorkspaceExecutionEvidence, isWorkspaceUnsupportedExecutionEvidence, workspaceProofLifecycle, } from "./workspaces/validate.js";
8
11
  const MAX_ACCEPTANCE_CRITERIA = 50;
9
12
  const MAX_EVIDENCE_RECORDS = 200;
10
13
  const MAX_PACKET_BYTES = 512 * 1024;
@@ -139,6 +142,9 @@ export function buildWorkflowProof(input) {
139
142
  agents,
140
143
  evidence: input.evidence,
141
144
  policy: input.policy ? buildPolicyProof(input.policy) : undefined,
145
+ execution: input.workspaceBinding && input.workspaceAttestation
146
+ ? buildExecutionEvidence(input.status, input.workspaceBinding, input.workspaceAttestation)
147
+ : input.workspaceFallback,
142
148
  repository,
143
149
  harnesses,
144
150
  unknowns,
@@ -184,17 +190,7 @@ export function writeWorkflowProof(path, proof) {
184
190
  if (bytes > MAX_PACKET_BYTES) {
185
191
  throw new Error(`workflow proof is ${bytes} bytes; limit is ${MAX_PACKET_BYTES}`);
186
192
  }
187
- mkdirSync(dirname(path), { recursive: true });
188
- const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
189
- try {
190
- writeFileSync(temporary, body, { encoding: "utf8", flag: "wx", mode: 0o600 });
191
- renameSync(temporary, path);
192
- chmodSync(path, 0o600);
193
- }
194
- finally {
195
- if (existsSync(temporary))
196
- unlinkSync(temporary);
197
- }
193
+ writeImmutableJson(path, proof);
198
194
  }
199
195
  export function readWorkflowProof(coordRoot, runId) {
200
196
  if (!RUN_ID.test(runId))
@@ -222,11 +218,50 @@ export function readWorkflowProof(coordRoot, runId) {
222
218
  (proof.run.attempt_context !== undefined &&
223
219
  (!proof.run.work_item_id ||
224
220
  !proof.run.work_context ||
225
- !isCanonicalWorkflowAttemptContext(proof.run.attempt_context)))) {
221
+ !isCanonicalWorkflowAttemptContext(proof.run.attempt_context))) ||
222
+ (proof.execution !== undefined &&
223
+ !isWorkspaceExecutionEvidence(proof.execution, runId, proof.run.status))) {
226
224
  throw new Error(`workflow proof at ${path} has an unsupported or mismatched schema`);
227
225
  }
226
+ const manifestPath = join(coordRoot, ".harnery", "workflows", runId, "run.json");
227
+ if (proof.execution === undefined && !existsSync(manifestPath))
228
+ return proof;
229
+ const manifest = readWorkflowRunManifest(coordRoot, runId);
230
+ const manifestBinding = manifest.execution.workspace_binding;
231
+ const manifestFallback = manifest.execution.workspace_fallback;
232
+ const proofExecution = proof.execution;
233
+ const executionMatches = manifestBinding !== undefined
234
+ ? isWorkspaceBoundExecutionEvidence(proofExecution, runId) &&
235
+ stableDigest(proofExecution.binding) === stableDigest(manifestBinding)
236
+ : manifestFallback !== undefined
237
+ ? isWorkspaceUnsupportedExecutionEvidence(proofExecution, runId) &&
238
+ stableDigest(proofExecution) === stableDigest(manifestFallback)
239
+ : proofExecution === undefined;
240
+ if (!executionMatches) {
241
+ throw new Error(`workflow proof at ${path} does not match the frozen execution manifest`);
242
+ }
228
243
  return proof;
229
244
  }
245
+ function buildExecutionEvidence(workflowStatus, binding, terminalAttestation) {
246
+ return {
247
+ schema_version: 1,
248
+ binding,
249
+ terminal_attestation: terminalAttestation,
250
+ terminal_lifecycle_state: workspaceProofLifecycle(workflowStatus, terminalAttestation),
251
+ drift: [...terminalAttestation.provider_drift],
252
+ unsupported: [...terminalAttestation.unsupported],
253
+ unknowns: [...terminalAttestation.unknowns],
254
+ receipts: {
255
+ request: "workspace-request.json",
256
+ cancellation_outcome: "cancellation/outcome.json",
257
+ integration_plan: "integration/plan.json",
258
+ integration_authorization: "integration/authorization.json",
259
+ integration_apply: "integration/receipt.json",
260
+ cleanup_intent: "cleanup/intent.json",
261
+ cleanup_receipt: "cleanup/receipt.json",
262
+ },
263
+ };
264
+ }
230
265
  export function renderWorkflowProof(proof) {
231
266
  const lines = [
232
267
  `run ${proof.run.id} (${proof.run.name}): ${proof.run.status}`,
@@ -1,6 +1,7 @@
1
1
  import type { RepoSnapshot } from "../context/index.js";
2
2
  import { type NormalizedPolicy, type PolicyIsolation, type PolicyNetworkAccess } from "../policy/index.js";
3
3
  import type { WorkflowAttemptContext, WorkflowSpecialistProfile, WorkflowWorkContext } from "./types.js";
4
+ import type { WorkspaceBinding, WorkspaceUnsupportedExecutionEvidence } from "./workspaces/index.js";
4
5
  export declare const WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION: 1;
5
6
  export interface WorkflowRunManifest {
6
7
  schema_version: typeof WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION;
@@ -28,6 +29,8 @@ export interface WorkflowRunManifest {
28
29
  network_access: PolicyNetworkAccess;
29
30
  policy?: NormalizedPolicy;
30
31
  specialists?: Record<string, WorkflowSpecialistProfile>;
32
+ workspace_binding?: WorkspaceBinding;
33
+ workspace_fallback?: WorkspaceUnsupportedExecutionEvidence;
31
34
  };
32
35
  }
33
36
  export interface WriteWorkflowRunManifestInput {
@@ -1 +1 @@
1
- {"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,oCAAoC,EAAG,CAAU,CAAC;AAK/D,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,oCAAoC,CAAC;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,iBAAiB,EAAE,YAAY,CAAC;IAChC,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,iBAAiB,EAAE,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;QAC/B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,SAAS,EAAE,eAAe,CAAC;QAC3B,cAAc,EAAE,mBAAmB,CAAC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;KACzD,CAAC;CACH;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,mBAAmB,CAAC;CAC/B;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAmBrF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAwC7F;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA4BvD;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CASjF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhF;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,CA0CvF"}
1
+ {"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAEV,gBAAgB,EAChB,qCAAqC,EACtC,MAAM,uBAAuB,CAAC;AAO/B,eAAO,MAAM,oCAAoC,EAAG,CAAU,CAAC;AAK/D,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,oCAAoC,CAAC;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,iBAAiB,EAAE,YAAY,CAAC;IAChC,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,iBAAiB,EAAE,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;QAC/B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,SAAS,EAAE,eAAe,CAAC;QAC3B,cAAc,EAAE,mBAAmB,CAAC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;QACxD,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;QACrC,kBAAkB,CAAC,EAAE,qCAAqC,CAAC;KAC5D,CAAC;CACH;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,mBAAmB,CAAC;CAC/B;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAgBrF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAyC7F;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA4BvD;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CASjF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhF;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,CA0CvF"}