@kontourai/flow-agents 3.6.0 → 3.7.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 (86) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/src/builder-flow-run-adapter.d.ts +22 -2
  3. package/build/src/builder-flow-run-adapter.js +93 -28
  4. package/build/src/builder-flow-runtime.d.ts +8 -3
  5. package/build/src/builder-flow-runtime.js +308 -47
  6. package/build/src/cli/assignment-provider.d.ts +4 -7
  7. package/build/src/cli/assignment-provider.js +67 -13
  8. package/build/src/cli/builder-run.js +14 -18
  9. package/build/src/cli/workflow-sidecar.d.ts +28 -4
  10. package/build/src/cli/workflow-sidecar.js +801 -97
  11. package/build/src/cli/workflow.js +290 -42
  12. package/build/src/flow-kit/validate.d.ts +17 -0
  13. package/build/src/flow-kit/validate.js +340 -2
  14. package/build/src/index.js +5 -5
  15. package/build/src/lib/observed-command.d.ts +7 -0
  16. package/build/src/lib/observed-command.js +100 -0
  17. package/build/src/tools/generate-context-map.js +5 -3
  18. package/context/scripts/hooks/lib/kit-catalog.js +1 -1
  19. package/context/scripts/hooks/stop-goal-fit.js +78 -9
  20. package/docs/context-map.md +22 -20
  21. package/docs/developer-architecture.md +1 -1
  22. package/docs/public-workflow-cli.md +76 -7
  23. package/docs/skills-map.md +51 -27
  24. package/docs/spec/builder-flow-runtime.md +41 -40
  25. package/docs/workflow-usage-guide.md +109 -42
  26. package/evals/fixtures/hook-influence/cases.json +2 -2
  27. package/evals/integration/test_builder_entry_enforcement.sh +52 -41
  28. package/evals/integration/test_builder_step_producers.sh +297 -6
  29. package/evals/integration/test_bundle_install.sh +212 -63
  30. package/evals/integration/test_critique_supersession_roundtrip.sh +21 -8
  31. package/evals/integration/test_current_json_per_actor.sh +12 -0
  32. package/evals/integration/test_dual_emit_flow_step.sh +62 -43
  33. package/evals/integration/test_flowdef_session_activation.sh +145 -25
  34. package/evals/integration/test_flowdef_session_history_preservation.sh +23 -21
  35. package/evals/integration/test_gate_lockdown.sh +3 -5
  36. package/evals/integration/test_goal_fit_hook.sh +60 -2
  37. package/evals/integration/test_liveness_verdict.sh +14 -17
  38. package/evals/integration/test_phase_map_and_gate_claim.sh +63 -38
  39. package/evals/integration/test_public_workflow_cli.sh +325 -11
  40. package/evals/integration/test_pull_work_liveness_preflight.sh +22 -66
  41. package/evals/integration/test_sidecar_field_preservation.sh +36 -11
  42. package/evals/integration/test_workflow_sidecar_writer.sh +277 -44
  43. package/evals/integration/test_workflow_steering_hook.sh +15 -38
  44. package/evals/run.sh +2 -0
  45. package/evals/static/test_builder_skill_coherence.sh +247 -0
  46. package/evals/static/test_library_exports.sh +5 -2
  47. package/evals/static/test_workflow_skills.sh +13 -325
  48. package/kits/builder/flows/build.flow.json +22 -0
  49. package/kits/builder/flows/shape.flow.json +9 -9
  50. package/kits/builder/kit.json +70 -16
  51. package/kits/builder/skills/builder-shape/SKILL.md +75 -75
  52. package/kits/builder/skills/continue-work/SKILL.md +45 -106
  53. package/kits/builder/skills/deliver/SKILL.md +96 -442
  54. package/kits/builder/skills/design-probe/SKILL.md +40 -139
  55. package/kits/builder/skills/evidence-gate/SKILL.md +59 -201
  56. package/kits/builder/skills/execute-plan/SKILL.md +54 -125
  57. package/kits/builder/skills/fix-bug/SKILL.md +42 -132
  58. package/kits/builder/skills/gate-review/SKILL.md +60 -211
  59. package/kits/builder/skills/idea-to-backlog/SKILL.md +63 -244
  60. package/kits/builder/skills/learning-review/SKILL.md +63 -170
  61. package/kits/builder/skills/pickup-probe/SKILL.md +54 -111
  62. package/kits/builder/skills/plan-work/SKILL.md +51 -185
  63. package/kits/builder/skills/pull-work/SKILL.md +136 -485
  64. package/kits/builder/skills/release-readiness/SKILL.md +66 -107
  65. package/kits/builder/skills/review-work/SKILL.md +89 -176
  66. package/kits/builder/skills/tdd-workflow/SKILL.md +53 -147
  67. package/kits/builder/skills/verify-work/SKILL.md +101 -113
  68. package/package.json +2 -2
  69. package/scripts/hooks/lib/kit-catalog.js +1 -1
  70. package/scripts/hooks/stop-goal-fit.js +78 -9
  71. package/src/builder-flow-run-adapter.ts +118 -32
  72. package/src/builder-flow-runtime.ts +331 -61
  73. package/src/cli/assignment-provider-lock.test.mjs +83 -0
  74. package/src/cli/assignment-provider.ts +62 -13
  75. package/src/cli/builder-flow-run-adapter.test.mjs +4 -2
  76. package/src/cli/builder-flow-runtime.test.mjs +194 -8
  77. package/src/cli/builder-run.ts +3 -9
  78. package/src/cli/kit-metadata-security.test.mjs +232 -6
  79. package/src/cli/public-api.test.mjs +15 -0
  80. package/src/cli/workflow-sidecar-execution-proof.test.mjs +90 -0
  81. package/src/cli/workflow-sidecar.ts +724 -97
  82. package/src/cli/workflow.ts +277 -40
  83. package/src/flow-kit/validate.ts +320 -2
  84. package/src/index.ts +6 -5
  85. package/src/lib/observed-command.ts +96 -0
  86. package/src/tools/generate-context-map.ts +5 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.7.0](https://github.com/kontourai/flow-agents/compare/v3.6.0...v3.7.0) (2026-07-12)
4
+
5
+
6
+ ### Features
7
+
8
+ * **builder:** align kit skills with canonical Flow ([#552](https://github.com/kontourai/flow-agents/issues/552)) ([2d672ed](https://github.com/kontourai/flow-agents/commit/2d672ede2dc16f96ef2d5186a3f0b6fc1c9d162f))
9
+
3
10
  ## [3.6.0](https://github.com/kontourai/flow-agents/compare/v3.5.0...v3.6.0) (2026-07-11)
4
11
 
5
12
 
@@ -1,6 +1,9 @@
1
1
  import { type FlowEvidenceEntry, type FlowLifecycleRequest, type FlowRunState, type GateOutcome, type JsonObject } from "@kontourai/flow";
2
2
  export declare const BUILDER_BUILD_FLOW_ID = "builder.build";
3
3
  export declare const BUILDER_BUILD_FLOW_RELATIVE_PATH = "kits/builder/flows/build.flow.json";
4
+ export declare const BUILDER_SHAPE_FLOW_ID = "builder.shape";
5
+ export declare const BUILDER_SHAPE_FLOW_RELATIVE_PATH = "kits/builder/flows/shape.flow.json";
6
+ export type BuilderFlowId = typeof BUILDER_BUILD_FLOW_ID | typeof BUILDER_SHAPE_FLOW_ID;
4
7
  export interface BuilderBuildTrustBundleEvidenceInput {
5
8
  gate: string;
6
9
  /**
@@ -8,6 +11,8 @@ export interface BuilderBuildTrustBundleEvidenceInput {
8
11
  * Callers must not pass raw user-controlled paths to this local runtime API.
9
12
  */
10
13
  file: string;
14
+ /** SHA-256 of the immutable snapshot validated by Flow Agents before Flow attaches it. */
15
+ expectedSha256?: string;
11
16
  status?: "passed" | "failed";
12
17
  producer?: string;
13
18
  authorityTrace?: string;
@@ -28,6 +33,9 @@ export interface StartBuilderBuildRunInput {
28
33
  cwd?: string;
29
34
  runId?: string;
30
35
  }
36
+ export interface StartBuilderFlowRunInput extends StartBuilderBuildRunInput {
37
+ flowId: BuilderFlowId;
38
+ }
31
39
  export interface EvaluateBuilderBuildRunInput {
32
40
  runId: string;
33
41
  /**
@@ -45,8 +53,8 @@ export interface ChangeBuilderBuildRunLifecycleInput extends LoadBuilderBuildRun
45
53
  request: FlowLifecycleRequest;
46
54
  at?: string;
47
55
  }
48
- export interface BuilderBuildRunResult {
49
- definitionId: typeof BUILDER_BUILD_FLOW_ID;
56
+ export interface BuilderFlowRunResult {
57
+ definitionId: BuilderFlowId;
50
58
  definitionVersion: string;
51
59
  runId: string;
52
60
  dir: string;
@@ -56,6 +64,9 @@ export interface BuilderBuildRunResult {
56
64
  manifest: JsonObject;
57
65
  freshnessTransitions: JsonObject[];
58
66
  }
67
+ export interface BuilderBuildRunResult extends Omit<BuilderFlowRunResult, "definitionId"> {
68
+ definitionId: typeof BUILDER_BUILD_FLOW_ID;
69
+ }
59
70
  export type BuilderBuildRunIdentityMismatch = "definition-id" | "definition-version" | "definition-content";
60
71
  export declare class BuilderBuildRunInputError extends Error {
61
72
  readonly code: "BUILDER_BUILD_RUN_INVALID_INPUT";
@@ -80,10 +91,19 @@ export declare class BuilderBuildRunIdentityError extends Error {
80
91
  }
81
92
  export declare function resolveBuilderBuildFlowDefinitionPath(startDir?: string): string;
82
93
  export declare function startBuilderBuildRun(input: StartBuilderBuildRunInput): Promise<BuilderBuildRunResult>;
94
+ export declare function resolveBuilderFlowDefinitionPath(flowId: BuilderFlowId, startDir?: string): string;
95
+ export declare function startBuilderFlowRun(input: StartBuilderFlowRunInput): Promise<BuilderFlowRunResult>;
83
96
  export declare function evaluateBuilderBuildRun(input: EvaluateBuilderBuildRunInput): Promise<BuilderBuildRunResult>;
97
+ export declare function evaluateBuilderFlowRun(input: EvaluateBuilderBuildRunInput): Promise<BuilderFlowRunResult>;
84
98
  export declare function loadBuilderBuildRun(input: LoadBuilderBuildRunInput): Promise<BuilderBuildRunResult>;
99
+ export declare function loadBuilderFlowRun(input: LoadBuilderBuildRunInput): Promise<BuilderFlowRunResult>;
85
100
  export declare function pauseBuilderBuildRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderBuildRunResult>;
86
101
  export declare function resumeBuilderBuildRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderBuildRunResult>;
87
102
  export declare function cancelBuilderBuildRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderBuildRunResult & {
88
103
  idempotent: boolean;
89
104
  }>;
105
+ export declare function pauseBuilderFlowRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderFlowRunResult>;
106
+ export declare function resumeBuilderFlowRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderFlowRunResult>;
107
+ export declare function cancelBuilderFlowRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderFlowRunResult & {
108
+ idempotent: boolean;
109
+ }>;
@@ -1,12 +1,14 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { isDeepStrictEqual } from "node:util";
6
- import { attachEvidence, cancelRun, evaluateRun, expectationsForGate, loadRun, normalizeTrustBundle, openGates, pauseRun, readJson, resumeRun, startRun, validateDefinition, } from "@kontourai/flow";
6
+ import { attachEvidence, cancelRun, evaluateRun, expectationsForGate, loadRun, normalizeTrustBundle, openGates, pauseRun, resumeRun, startRun, validateDefinition, } from "@kontourai/flow";
7
7
  import { resolveEffectiveFlowDefinition } from "./lib/flow-resolver.js";
8
8
  export const BUILDER_BUILD_FLOW_ID = "builder.build";
9
9
  export const BUILDER_BUILD_FLOW_RELATIVE_PATH = "kits/builder/flows/build.flow.json";
10
+ export const BUILDER_SHAPE_FLOW_ID = "builder.shape";
11
+ export const BUILDER_SHAPE_FLOW_RELATIVE_PATH = "kits/builder/flows/shape.flow.json";
10
12
  export class BuilderBuildRunInputError extends Error {
11
13
  code = "BUILDER_BUILD_RUN_INVALID_INPUT";
12
14
  field;
@@ -36,18 +38,25 @@ export class BuilderBuildRunIdentityError extends Error {
36
38
  }
37
39
  }
38
40
  export function resolveBuilderBuildFlowDefinitionPath(startDir = moduleDirectory()) {
39
- const root = findPackageRoot(startDir);
40
- return path.join(root, BUILDER_BUILD_FLOW_RELATIVE_PATH);
41
+ return resolveBuilderFlowDefinitionPath(BUILDER_BUILD_FLOW_ID, startDir);
41
42
  }
42
43
  export async function startBuilderBuildRun(input) {
44
+ const result = await startBuilderFlowRun({ ...input, flowId: BUILDER_BUILD_FLOW_ID });
45
+ return asBuilderBuildResult(result, input.runId ?? result.runId);
46
+ }
47
+ export function resolveBuilderFlowDefinitionPath(flowId, startDir = moduleDirectory()) {
48
+ const root = findPackageRoot(startDir);
49
+ return path.join(root, flowRelativePath(flowId));
50
+ }
51
+ export async function startBuilderFlowRun(input) {
43
52
  assertRuntimeInput(input, ["evidence", "now", "gate"]);
44
53
  if (!isNonEmptyString(input.subject)) {
45
54
  throw new BuilderBuildRunInputError("subject", "must be a non-empty string");
46
55
  }
47
56
  const cwd = input.cwd ?? process.cwd();
48
- const definitionPath = resolveBuilderBuildFlowDefinitionPath();
49
- const definition = await loadShippedBuilderBuildDefinition(definitionPath);
50
- const runtimeDefinitionPath = materializeRuntimeDefinition(cwd, definition);
57
+ const definitionPath = resolveBuilderFlowDefinitionPath(input.flowId);
58
+ const definition = await loadShippedBuilderFlowDefinition(input.flowId, definitionPath);
59
+ const runtimeDefinitionPath = materializeRuntimeDefinition(cwd, input.flowId, definition);
51
60
  const started = await startRun(runtimeDefinitionPath, {
52
61
  cwd,
53
62
  runId: input.runId,
@@ -56,25 +65,39 @@ export async function startBuilderBuildRun(input) {
56
65
  subject: input.subject,
57
66
  },
58
67
  });
59
- const run = await loadCanonicalBuilderBuildRun(started.runId, cwd, definition);
68
+ const run = await loadCanonicalBuilderFlowRun(started.runId, cwd, definition);
60
69
  return resultFromRun(run, started.runId);
61
70
  }
62
71
  export async function evaluateBuilderBuildRun(input) {
72
+ const result = await evaluateBuilderFlowRun(input);
73
+ return asBuilderBuildResult(result, input.runId);
74
+ }
75
+ export async function evaluateBuilderFlowRun(input) {
63
76
  assertRuntimeInput(input, ["now", "gate"]);
64
77
  if (Array.isArray(input.evidence)) {
65
78
  throw new BuilderBuildRunInputError("evidence", "must be zero or one evidence object, not an array");
66
79
  }
67
80
  const cwd = input.cwd ?? process.cwd();
68
81
  const run = await loadRun(input.runId, cwd);
69
- const definition = await loadShippedBuilderBuildDefinition(resolveBuilderBuildFlowDefinitionPath());
82
+ const definition = await loadShippedBuilderFlowDefinitionForRun(input.runId, run.definition);
70
83
  assertCanonicalDefinition(input.runId, definition, run.definition);
71
84
  let attachedEvidence = [];
72
85
  if (input.evidence !== undefined) {
73
86
  const evidence = validateEvidenceInput(input.evidence);
74
87
  assertCurrentOpenGate(run.definition, run.state, evidence.gate);
75
- const normalized = normalizeTrustBundle(await readJson(path.resolve(cwd, evidence.file)));
88
+ const source = path.resolve(cwd, evidence.file);
89
+ const bytes = readFileSync(source);
90
+ const validatedSha256 = createHash("sha256").update(bytes).digest("hex");
91
+ if (evidence.expectedSha256 && evidence.expectedSha256 !== validatedSha256) {
92
+ throw new BuilderBuildRunInputError("evidence.expectedSha256", "does not match the bytes presented for validation");
93
+ }
94
+ const normalized = normalizeTrustBundle(JSON.parse(bytes.toString("utf8")));
76
95
  assertBundleSubjects(normalized.bundle, run.state.subject, openGates(run.definition, run.state)[0]);
77
- attachedEvidence = [await attachEvidence(input.runId, trustBundleAttachOptions(cwd, evidence))];
96
+ const attached = await attachEvidence(input.runId, trustBundleAttachOptions(cwd, evidence, validatedSha256));
97
+ if (attached.sha256 !== validatedSha256) {
98
+ throw new BuilderBuildRunInputError("evidence.file", "changed after validation before Flow attachment");
99
+ }
100
+ attachedEvidence = [attached];
78
101
  }
79
102
  const evaluated = await evaluateRun(input.runId, { cwd });
80
103
  return {
@@ -90,35 +113,51 @@ export async function evaluateBuilderBuildRun(input) {
90
113
  };
91
114
  }
92
115
  export async function loadBuilderBuildRun(input) {
116
+ const result = await loadBuilderFlowRun(input);
117
+ return asBuilderBuildResult(result, input.runId);
118
+ }
119
+ export async function loadBuilderFlowRun(input) {
93
120
  assertRuntimeInput(input, ["evidence", "now", "gate"]);
94
121
  const cwd = input.cwd ?? process.cwd();
95
122
  const run = await loadRun(input.runId, cwd);
96
- const definition = await loadShippedBuilderBuildDefinition(resolveBuilderBuildFlowDefinitionPath());
123
+ const definition = await loadShippedBuilderFlowDefinitionForRun(input.runId, run.definition);
97
124
  assertCanonicalDefinition(input.runId, definition, run.definition);
98
125
  return resultFromRun(run, input.runId);
99
126
  }
100
127
  export async function pauseBuilderBuildRun(input) {
101
- return changeBuilderBuildRunLifecycle(input, pauseRun);
128
+ const result = await pauseBuilderFlowRun(input);
129
+ return asBuilderBuildResult(result, input.runId);
102
130
  }
103
131
  export async function resumeBuilderBuildRun(input) {
104
- return changeBuilderBuildRunLifecycle(input, resumeRun);
132
+ const result = await resumeBuilderFlowRun(input);
133
+ return asBuilderBuildResult(result, input.runId);
105
134
  }
106
135
  export async function cancelBuilderBuildRun(input) {
107
- const changed = await changeBuilderBuildRunLifecycleResult(input, cancelRun);
136
+ const changed = await changeBuilderFlowRunLifecycleResult(input, cancelRun);
137
+ return { ...asBuilderBuildResult(resultFromRun(changed, input.runId), input.runId), idempotent: changed.idempotent };
138
+ }
139
+ export async function pauseBuilderFlowRun(input) {
140
+ return changeBuilderFlowRunLifecycle(input, pauseRun);
141
+ }
142
+ export async function resumeBuilderFlowRun(input) {
143
+ return changeBuilderFlowRunLifecycle(input, resumeRun);
144
+ }
145
+ export async function cancelBuilderFlowRun(input) {
146
+ const changed = await changeBuilderFlowRunLifecycleResult(input, cancelRun);
108
147
  return { ...resultFromRun(changed, input.runId), idempotent: changed.idempotent };
109
148
  }
110
- async function changeBuilderBuildRunLifecycle(input, operation) {
111
- const changed = await changeBuilderBuildRunLifecycleResult(input, operation);
149
+ async function changeBuilderFlowRunLifecycle(input, operation) {
150
+ const changed = await changeBuilderFlowRunLifecycleResult(input, operation);
112
151
  return resultFromRun(changed, input.runId);
113
152
  }
114
- async function changeBuilderBuildRunLifecycleResult(input, operation) {
153
+ async function changeBuilderFlowRunLifecycleResult(input, operation) {
115
154
  assertRuntimeInput(input, []);
116
155
  if (!isRecord(input.request))
117
156
  throw new BuilderBuildRunInputError("request", "must be a lifecycle request object");
118
157
  const cwd = input.cwd ?? process.cwd();
119
- const before = await loadBuilderBuildRun({ runId: input.runId, cwd });
158
+ const before = await loadBuilderFlowRun({ runId: input.runId, cwd });
120
159
  const changed = await operation(input.runId, { cwd, ...input.request, ...(input.at ? { at: input.at } : {}) });
121
- const definition = await loadShippedBuilderBuildDefinition(resolveBuilderBuildFlowDefinitionPath());
160
+ const definition = await loadShippedBuilderFlowDefinitionForRun(input.runId, changed.definition);
122
161
  assertCanonicalDefinition(input.runId, definition, changed.definition);
123
162
  if (changed.state.subject !== before.state.subject) {
124
163
  throw new BuilderBuildRunInputError("flow_run.state.subject", "changed during lifecycle transition");
@@ -138,33 +177,55 @@ function resultFromRun(run, runId) {
138
177
  freshnessTransitions: [],
139
178
  };
140
179
  }
141
- async function loadCanonicalBuilderBuildRun(runId, cwd, definition) {
180
+ async function loadCanonicalBuilderFlowRun(runId, cwd, definition) {
142
181
  const run = await loadRun(runId, cwd);
143
182
  assertCanonicalDefinition(runId, definition, run.definition);
144
183
  return run;
145
184
  }
146
- async function loadShippedBuilderBuildDefinition(definitionPath) {
185
+ async function loadShippedBuilderFlowDefinition(flowId, definitionPath) {
147
186
  const packageRoot = findPackageRoot(path.dirname(definitionPath));
148
- const effective = resolveEffectiveFlowDefinition(BUILDER_BUILD_FLOW_ID, packageRoot, { allowOverride: false });
187
+ const effective = resolveEffectiveFlowDefinition(flowId, packageRoot, { allowOverride: false });
149
188
  if (!effective) {
150
189
  throw new BuilderBuildRunInputError("definition", "could not compile the shipped uses_flow composition");
151
190
  }
152
191
  const definition = validateDefinition(effective);
153
- if (definition.id !== BUILDER_BUILD_FLOW_ID) {
154
- throw new BuilderBuildRunInputError("definition", `expected shipped definition id ${BUILDER_BUILD_FLOW_ID}`);
192
+ if (definition.id !== flowId) {
193
+ throw new BuilderBuildRunInputError("definition", `expected shipped definition id ${flowId}`);
155
194
  }
156
195
  return definition;
157
196
  }
158
- function materializeRuntimeDefinition(cwd, definition) {
197
+ async function loadShippedBuilderFlowDefinitionForRun(runId, actualDefinition) {
198
+ const flowId = actualDefinition.id;
199
+ if (!isBuilderFlowId(flowId)) {
200
+ throw new BuilderBuildRunIdentityError(runId, { id: BUILDER_BUILD_FLOW_ID, version: "unknown" }, actualDefinition, "definition-id");
201
+ }
202
+ return loadShippedBuilderFlowDefinition(flowId, resolveBuilderFlowDefinitionPath(flowId));
203
+ }
204
+ function materializeRuntimeDefinition(cwd, flowId, definition) {
159
205
  const content = `${JSON.stringify(definition, null, 2)}\n`;
160
206
  const digest = createHash("sha256").update(content).digest("hex").slice(0, 16);
161
207
  const directory = path.join(cwd, ".kontourai", "flow-agents", "runtime-definitions");
162
208
  mkdirSync(directory, { recursive: true });
163
- const file = path.join(directory, `builder-build-${digest}.flow.json`);
209
+ const file = path.join(directory, `${flowId.replace(".", "-")}-${digest}.flow.json`);
164
210
  if (!existsSync(file))
165
211
  writeFileSync(file, content);
166
212
  return file;
167
213
  }
214
+ function flowRelativePath(flowId) {
215
+ return flowId === BUILDER_BUILD_FLOW_ID ? BUILDER_BUILD_FLOW_RELATIVE_PATH : BUILDER_SHAPE_FLOW_RELATIVE_PATH;
216
+ }
217
+ function isBuilderFlowId(value) {
218
+ return value === BUILDER_BUILD_FLOW_ID || value === BUILDER_SHAPE_FLOW_ID;
219
+ }
220
+ function assertExpectedFlow(runId, actual, expected) {
221
+ if (actual === expected)
222
+ return;
223
+ throw new BuilderBuildRunIdentityError(runId, { id: expected, version: "unknown" }, { id: actual, version: "unknown" }, "definition-id");
224
+ }
225
+ function asBuilderBuildResult(result, runId) {
226
+ assertExpectedFlow(runId, result.definitionId, BUILDER_BUILD_FLOW_ID);
227
+ return result;
228
+ }
168
229
  function assertCanonicalDefinition(runId, expectedDefinition, actualDefinition) {
169
230
  if (isDeepStrictEqual(actualDefinition, expectedDefinition))
170
231
  return;
@@ -195,6 +256,9 @@ function validateEvidenceInput(evidence) {
195
256
  if (!isNonEmptyString(evidence.file)) {
196
257
  throw new BuilderBuildRunInputError("evidence.file", "must be a non-empty string");
197
258
  }
259
+ if (evidence.expectedSha256 !== undefined && (!isNonEmptyString(evidence.expectedSha256) || !/^[a-f0-9]{64}$/i.test(evidence.expectedSha256))) {
260
+ throw new BuilderBuildRunInputError("evidence.expectedSha256", "must be a SHA-256 hex digest");
261
+ }
198
262
  if (evidence.status !== undefined && evidence.status !== "passed" && evidence.status !== "failed") {
199
263
  throw new BuilderBuildRunInputError("evidence.status", "must be passed or failed");
200
264
  }
@@ -227,11 +291,12 @@ function assertBundleSubjects(bundle, subject, gate) {
227
291
  }
228
292
  }
229
293
  }
230
- function trustBundleAttachOptions(cwd, evidence) {
294
+ function trustBundleAttachOptions(cwd, evidence, expectedSha256) {
231
295
  return {
232
296
  cwd,
233
297
  gate: evidence.gate,
234
298
  file: evidence.file,
299
+ expectedSha256,
235
300
  kind: "trust.bundle",
236
301
  bundle: true,
237
302
  ...(evidence.status ? { status: evidence.status } : {}),
@@ -1,7 +1,8 @@
1
- import { type BuilderBuildRunResult } from "./builder-flow-run-adapter.js";
1
+ import { type BuilderFlowId, type BuilderFlowRunResult } from "./builder-flow-run-adapter.js";
2
2
  type AnyRecord = Record<string, any>;
3
3
  export interface BuilderFlowSessionInput {
4
4
  sessionDir: string;
5
+ flowId?: BuilderFlowId;
5
6
  }
6
7
  export interface BuilderFlowAuthorizedLifecycleInput extends BuilderFlowSessionInput {
7
8
  authorizationFile: string;
@@ -12,13 +13,14 @@ export interface BuilderFlowAgentLifecycleInput extends BuilderFlowSessionInput
12
13
  export interface BuilderFlowSessionResult {
13
14
  sessionDir: string;
14
15
  projectRoot: string;
15
- run: BuilderBuildRunResult;
16
+ run: BuilderFlowRunResult;
16
17
  projection: AnyRecord;
17
18
  attached: boolean;
18
19
  }
19
20
  export declare function startBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
20
21
  export declare function syncBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
21
22
  export declare function recoverBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
23
+ export declare function inspectBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
22
24
  export declare function pauseBuilderFlowSession(input: BuilderFlowAgentLifecycleInput): Promise<BuilderFlowSessionResult>;
23
25
  export declare function resumeBuilderFlowSession(input: BuilderFlowAgentLifecycleInput): Promise<BuilderFlowSessionResult>;
24
26
  export declare function cancelBuilderFlowSession(input: BuilderFlowAuthorizedLifecycleInput): Promise<BuilderFlowSessionResult & {
@@ -31,5 +33,8 @@ export declare function releaseBuilderFlowAssignment(input: BuilderFlowAgentLife
31
33
  export declare function archiveBuilderFlowSession(input: BuilderFlowAuthorizedLifecycleInput): Promise<BuilderFlowSessionResult & {
32
34
  archiveDir: string;
33
35
  }>;
34
- export declare function syncBuilderFlowSessionIfPresent(sessionDir: string): Promise<BuilderFlowSessionResult | null>;
36
+ export declare function captureReviewWorkspaceSnapshot(projectRoot: string, reviewedFiles: Array<{
37
+ file: string;
38
+ sha256: string;
39
+ }>): AnyRecord;
35
40
  export {};