@kontourai/flow-agents 3.5.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.
- package/.github/workflows/ci.yml +4 -0
- package/CHANGELOG.md +15 -0
- package/build/src/builder-flow-run-adapter.d.ts +32 -3
- package/build/src/builder-flow-run-adapter.js +113 -20
- package/build/src/builder-flow-runtime.d.ts +26 -3
- package/build/src/builder-flow-runtime.js +502 -49
- package/build/src/builder-lifecycle-authority.d.ts +35 -0
- package/build/src/builder-lifecycle-authority.js +219 -0
- package/build/src/cli/assignment-provider.d.ts +14 -7
- package/build/src/cli/assignment-provider.js +128 -65
- package/build/src/cli/builder-run.js +46 -9
- package/build/src/cli/workflow-artifact-cleanup-audit.js +3 -0
- package/build/src/cli/workflow-sidecar.d.ts +30 -3
- package/build/src/cli/workflow-sidecar.js +825 -99
- package/build/src/cli/workflow.d.ts +2 -0
- package/build/src/cli/workflow.js +769 -0
- package/build/src/cli.js +2 -0
- package/build/src/flow-kit/validate.d.ts +17 -0
- package/build/src/flow-kit/validate.js +340 -2
- package/build/src/index.d.ts +4 -0
- package/build/src/index.js +7 -5
- package/build/src/lib/observed-command.d.ts +7 -0
- package/build/src/lib/observed-command.js +100 -0
- package/build/src/lib/package-version.d.ts +2 -0
- package/build/src/lib/package-version.js +13 -0
- package/build/src/lib/pinned-cli-command.d.ts +6 -0
- package/build/src/lib/pinned-cli-command.js +21 -0
- package/build/src/tools/generate-context-map.js +5 -3
- package/context/contracts/artifact-contract.md +1 -1
- package/context/scripts/hooks/config-protection.js +8 -1
- package/context/scripts/hooks/lib/config-protection-remedies.js +3 -0
- package/context/scripts/hooks/lib/kit-catalog.js +1 -1
- package/context/scripts/hooks/stop-goal-fit.js +79 -10
- package/docs/context-map.md +24 -20
- package/docs/developer-architecture.md +1 -1
- package/docs/public-workflow-cli.md +132 -0
- package/docs/skills-map.md +51 -27
- package/docs/spec/builder-flow-runtime.md +78 -40
- package/docs/workflow-usage-guide.md +110 -38
- package/evals/ci/run-baseline.sh +2 -0
- package/evals/fixtures/hook-influence/cases.json +2 -2
- package/evals/integration/test_builder_entry_enforcement.sh +57 -45
- package/evals/integration/test_builder_step_producers.sh +297 -6
- package/evals/integration/test_bundle_install.sh +258 -55
- package/evals/integration/test_critique_supersession_roundtrip.sh +21 -8
- package/evals/integration/test_current_json_per_actor.sh +12 -0
- package/evals/integration/test_dual_emit_flow_step.sh +62 -43
- package/evals/integration/test_flowdef_session_activation.sh +145 -25
- package/evals/integration/test_flowdef_session_history_preservation.sh +23 -21
- package/evals/integration/test_gate_lockdown.sh +3 -5
- package/evals/integration/test_goal_fit_hook.sh +60 -2
- package/evals/integration/test_liveness_verdict.sh +14 -17
- package/evals/integration/test_phase_map_and_gate_claim.sh +63 -38
- package/evals/integration/test_public_workflow_cli.sh +573 -0
- package/evals/integration/test_pull_work_liveness_preflight.sh +22 -66
- package/evals/integration/test_sidecar_field_preservation.sh +36 -11
- package/evals/integration/test_workflow_sidecar_writer.sh +277 -44
- package/evals/integration/test_workflow_steering_hook.sh +15 -38
- package/evals/run.sh +2 -0
- package/evals/static/test_builder_skill_coherence.sh +247 -0
- package/evals/static/test_library_exports.sh +5 -2
- package/evals/static/test_workflow_skills.sh +13 -325
- package/kits/builder/flows/build.flow.json +22 -0
- package/kits/builder/flows/shape.flow.json +9 -9
- package/kits/builder/kit.json +70 -16
- package/kits/builder/skills/builder-shape/SKILL.md +75 -75
- package/kits/builder/skills/continue-work/SKILL.md +45 -106
- package/kits/builder/skills/deliver/SKILL.md +96 -442
- package/kits/builder/skills/design-probe/SKILL.md +40 -139
- package/kits/builder/skills/evidence-gate/SKILL.md +59 -201
- package/kits/builder/skills/execute-plan/SKILL.md +54 -125
- package/kits/builder/skills/fix-bug/SKILL.md +42 -132
- package/kits/builder/skills/gate-review/SKILL.md +60 -211
- package/kits/builder/skills/idea-to-backlog/SKILL.md +63 -244
- package/kits/builder/skills/learning-review/SKILL.md +63 -170
- package/kits/builder/skills/pickup-probe/SKILL.md +54 -111
- package/kits/builder/skills/plan-work/SKILL.md +51 -185
- package/kits/builder/skills/pull-work/SKILL.md +136 -485
- package/kits/builder/skills/release-readiness/SKILL.md +66 -107
- package/kits/builder/skills/review-work/SKILL.md +89 -176
- package/kits/builder/skills/tdd-workflow/SKILL.md +53 -147
- package/kits/builder/skills/verify-work/SKILL.md +101 -113
- package/package.json +2 -2
- package/schemas/builder-lifecycle-authorization.schema.json +57 -0
- package/schemas/lifecycle-authority-keys.schema.json +25 -0
- package/schemas/workflow-state.schema.json +1 -1
- package/scripts/hooks/config-protection.js +8 -1
- package/scripts/hooks/lib/config-protection-remedies.js +3 -0
- package/scripts/hooks/lib/kit-catalog.js +1 -1
- package/scripts/hooks/stop-goal-fit.js +79 -10
- package/src/builder-flow-run-adapter.ts +156 -23
- package/src/builder-flow-runtime.ts +535 -53
- package/src/builder-lifecycle-authority.ts +218 -0
- package/src/cli/assignment-provider-lock.test.mjs +83 -0
- package/src/cli/assignment-provider.ts +91 -22
- package/src/cli/builder-flow-run-adapter.test.mjs +4 -2
- package/src/cli/builder-flow-runtime.test.mjs +597 -8
- package/src/cli/builder-run.ts +54 -9
- package/src/cli/kit-metadata-security.test.mjs +232 -6
- package/src/cli/public-api.test.mjs +15 -0
- package/src/cli/workflow-artifact-cleanup-audit.ts +3 -0
- package/src/cli/workflow-sidecar-execution-proof.test.mjs +90 -0
- package/src/cli/workflow-sidecar.ts +748 -99
- package/src/cli/workflow.ts +708 -0
- package/src/cli.ts +2 -0
- package/src/flow-kit/validate.ts +320 -2
- package/src/index.ts +20 -5
- package/src/lib/observed-command.ts +96 -0
- package/src/lib/package-version.ts +15 -0
- package/src/lib/pinned-cli-command.ts +23 -0
- package/src/tools/generate-context-map.ts +5 -3
package/.github/workflows/ci.yml
CHANGED
|
@@ -266,6 +266,10 @@ jobs:
|
|
|
266
266
|
continue-on-error: true
|
|
267
267
|
run: bash evals/ci/run-baseline.sh --check bundle-install-integration
|
|
268
268
|
|
|
269
|
+
- name: Public workflow CLI integration
|
|
270
|
+
continue-on-error: true
|
|
271
|
+
run: bash evals/ci/run-baseline.sh --check public-workflow-cli-integration
|
|
272
|
+
|
|
269
273
|
- name: Bundle lifecycle integration
|
|
270
274
|
continue-on-error: true
|
|
271
275
|
run: bash evals/ci/run-baseline.sh --check bundle-lifecycle-integration
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
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
|
+
|
|
10
|
+
## [3.6.0](https://github.com/kontourai/flow-agents/compare/v3.5.0...v3.6.0) (2026-07-11)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **builder:** add authority-aware lifecycle controls ([#546](https://github.com/kontourai/flow-agents/issues/546)) ([5164902](https://github.com/kontourai/flow-agents/commit/51649020bc9a90dd77907f2aabdd9fb593f43504))
|
|
16
|
+
* **cli:** add public workflow command ([#548](https://github.com/kontourai/flow-agents/issues/548)) ([2dad479](https://github.com/kontourai/flow-agents/commit/2dad47974819100ecb850fa0bec645e911d168f8))
|
|
17
|
+
|
|
3
18
|
## [3.5.0](https://github.com/kontourai/flow-agents/compare/v3.4.3...v3.5.0) (2026-07-10)
|
|
4
19
|
|
|
5
20
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { type FlowEvidenceEntry, type FlowRunState, type GateOutcome, type JsonObject } from "@kontourai/flow";
|
|
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
|
/**
|
|
@@ -41,8 +49,12 @@ export interface LoadBuilderBuildRunInput {
|
|
|
41
49
|
runId: string;
|
|
42
50
|
cwd?: string;
|
|
43
51
|
}
|
|
44
|
-
export interface
|
|
45
|
-
|
|
52
|
+
export interface ChangeBuilderBuildRunLifecycleInput extends LoadBuilderBuildRunInput {
|
|
53
|
+
request: FlowLifecycleRequest;
|
|
54
|
+
at?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface BuilderFlowRunResult {
|
|
57
|
+
definitionId: BuilderFlowId;
|
|
46
58
|
definitionVersion: string;
|
|
47
59
|
runId: string;
|
|
48
60
|
dir: string;
|
|
@@ -52,6 +64,9 @@ export interface BuilderBuildRunResult {
|
|
|
52
64
|
manifest: JsonObject;
|
|
53
65
|
freshnessTransitions: JsonObject[];
|
|
54
66
|
}
|
|
67
|
+
export interface BuilderBuildRunResult extends Omit<BuilderFlowRunResult, "definitionId"> {
|
|
68
|
+
definitionId: typeof BUILDER_BUILD_FLOW_ID;
|
|
69
|
+
}
|
|
55
70
|
export type BuilderBuildRunIdentityMismatch = "definition-id" | "definition-version" | "definition-content";
|
|
56
71
|
export declare class BuilderBuildRunInputError extends Error {
|
|
57
72
|
readonly code: "BUILDER_BUILD_RUN_INVALID_INPUT";
|
|
@@ -76,5 +91,19 @@ export declare class BuilderBuildRunIdentityError extends Error {
|
|
|
76
91
|
}
|
|
77
92
|
export declare function resolveBuilderBuildFlowDefinitionPath(startDir?: string): string;
|
|
78
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>;
|
|
79
96
|
export declare function evaluateBuilderBuildRun(input: EvaluateBuilderBuildRunInput): Promise<BuilderBuildRunResult>;
|
|
97
|
+
export declare function evaluateBuilderFlowRun(input: EvaluateBuilderBuildRunInput): Promise<BuilderFlowRunResult>;
|
|
80
98
|
export declare function loadBuilderBuildRun(input: LoadBuilderBuildRunInput): Promise<BuilderBuildRunResult>;
|
|
99
|
+
export declare function loadBuilderFlowRun(input: LoadBuilderBuildRunInput): Promise<BuilderFlowRunResult>;
|
|
100
|
+
export declare function pauseBuilderBuildRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderBuildRunResult>;
|
|
101
|
+
export declare function resumeBuilderBuildRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderBuildRunResult>;
|
|
102
|
+
export declare function cancelBuilderBuildRun(input: ChangeBuilderBuildRunLifecycleInput): Promise<BuilderBuildRunResult & {
|
|
103
|
+
idempotent: boolean;
|
|
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, evaluateRun, expectationsForGate, loadRun, normalizeTrustBundle, openGates,
|
|
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
|
-
|
|
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 =
|
|
49
|
-
const definition = await
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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,13 +113,57 @@ 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
|
|
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
|
}
|
|
127
|
+
export async function pauseBuilderBuildRun(input) {
|
|
128
|
+
const result = await pauseBuilderFlowRun(input);
|
|
129
|
+
return asBuilderBuildResult(result, input.runId);
|
|
130
|
+
}
|
|
131
|
+
export async function resumeBuilderBuildRun(input) {
|
|
132
|
+
const result = await resumeBuilderFlowRun(input);
|
|
133
|
+
return asBuilderBuildResult(result, input.runId);
|
|
134
|
+
}
|
|
135
|
+
export async function cancelBuilderBuildRun(input) {
|
|
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);
|
|
147
|
+
return { ...resultFromRun(changed, input.runId), idempotent: changed.idempotent };
|
|
148
|
+
}
|
|
149
|
+
async function changeBuilderFlowRunLifecycle(input, operation) {
|
|
150
|
+
const changed = await changeBuilderFlowRunLifecycleResult(input, operation);
|
|
151
|
+
return resultFromRun(changed, input.runId);
|
|
152
|
+
}
|
|
153
|
+
async function changeBuilderFlowRunLifecycleResult(input, operation) {
|
|
154
|
+
assertRuntimeInput(input, []);
|
|
155
|
+
if (!isRecord(input.request))
|
|
156
|
+
throw new BuilderBuildRunInputError("request", "must be a lifecycle request object");
|
|
157
|
+
const cwd = input.cwd ?? process.cwd();
|
|
158
|
+
const before = await loadBuilderFlowRun({ runId: input.runId, cwd });
|
|
159
|
+
const changed = await operation(input.runId, { cwd, ...input.request, ...(input.at ? { at: input.at } : {}) });
|
|
160
|
+
const definition = await loadShippedBuilderFlowDefinitionForRun(input.runId, changed.definition);
|
|
161
|
+
assertCanonicalDefinition(input.runId, definition, changed.definition);
|
|
162
|
+
if (changed.state.subject !== before.state.subject) {
|
|
163
|
+
throw new BuilderBuildRunInputError("flow_run.state.subject", "changed during lifecycle transition");
|
|
164
|
+
}
|
|
165
|
+
return changed;
|
|
166
|
+
}
|
|
100
167
|
function resultFromRun(run, runId) {
|
|
101
168
|
return {
|
|
102
169
|
definitionId: run.definition.id,
|
|
@@ -110,33 +177,55 @@ function resultFromRun(run, runId) {
|
|
|
110
177
|
freshnessTransitions: [],
|
|
111
178
|
};
|
|
112
179
|
}
|
|
113
|
-
async function
|
|
180
|
+
async function loadCanonicalBuilderFlowRun(runId, cwd, definition) {
|
|
114
181
|
const run = await loadRun(runId, cwd);
|
|
115
182
|
assertCanonicalDefinition(runId, definition, run.definition);
|
|
116
183
|
return run;
|
|
117
184
|
}
|
|
118
|
-
async function
|
|
185
|
+
async function loadShippedBuilderFlowDefinition(flowId, definitionPath) {
|
|
119
186
|
const packageRoot = findPackageRoot(path.dirname(definitionPath));
|
|
120
|
-
const effective = resolveEffectiveFlowDefinition(
|
|
187
|
+
const effective = resolveEffectiveFlowDefinition(flowId, packageRoot, { allowOverride: false });
|
|
121
188
|
if (!effective) {
|
|
122
189
|
throw new BuilderBuildRunInputError("definition", "could not compile the shipped uses_flow composition");
|
|
123
190
|
}
|
|
124
191
|
const definition = validateDefinition(effective);
|
|
125
|
-
if (definition.id !==
|
|
126
|
-
throw new BuilderBuildRunInputError("definition", `expected shipped definition id ${
|
|
192
|
+
if (definition.id !== flowId) {
|
|
193
|
+
throw new BuilderBuildRunInputError("definition", `expected shipped definition id ${flowId}`);
|
|
127
194
|
}
|
|
128
195
|
return definition;
|
|
129
196
|
}
|
|
130
|
-
function
|
|
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) {
|
|
131
205
|
const content = `${JSON.stringify(definition, null, 2)}\n`;
|
|
132
206
|
const digest = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
133
207
|
const directory = path.join(cwd, ".kontourai", "flow-agents", "runtime-definitions");
|
|
134
208
|
mkdirSync(directory, { recursive: true });
|
|
135
|
-
const file = path.join(directory,
|
|
209
|
+
const file = path.join(directory, `${flowId.replace(".", "-")}-${digest}.flow.json`);
|
|
136
210
|
if (!existsSync(file))
|
|
137
211
|
writeFileSync(file, content);
|
|
138
212
|
return file;
|
|
139
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
|
+
}
|
|
140
229
|
function assertCanonicalDefinition(runId, expectedDefinition, actualDefinition) {
|
|
141
230
|
if (isDeepStrictEqual(actualDefinition, expectedDefinition))
|
|
142
231
|
return;
|
|
@@ -167,6 +256,9 @@ function validateEvidenceInput(evidence) {
|
|
|
167
256
|
if (!isNonEmptyString(evidence.file)) {
|
|
168
257
|
throw new BuilderBuildRunInputError("evidence.file", "must be a non-empty string");
|
|
169
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
|
+
}
|
|
170
262
|
if (evidence.status !== undefined && evidence.status !== "passed" && evidence.status !== "failed") {
|
|
171
263
|
throw new BuilderBuildRunInputError("evidence.status", "must be passed or failed");
|
|
172
264
|
}
|
|
@@ -199,11 +291,12 @@ function assertBundleSubjects(bundle, subject, gate) {
|
|
|
199
291
|
}
|
|
200
292
|
}
|
|
201
293
|
}
|
|
202
|
-
function trustBundleAttachOptions(cwd, evidence) {
|
|
294
|
+
function trustBundleAttachOptions(cwd, evidence, expectedSha256) {
|
|
203
295
|
return {
|
|
204
296
|
cwd,
|
|
205
297
|
gate: evidence.gate,
|
|
206
298
|
file: evidence.file,
|
|
299
|
+
expectedSha256,
|
|
207
300
|
kind: "trust.bundle",
|
|
208
301
|
bundle: true,
|
|
209
302
|
...(evidence.status ? { status: evidence.status } : {}),
|
|
@@ -1,17 +1,40 @@
|
|
|
1
|
-
import { type
|
|
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;
|
|
6
|
+
}
|
|
7
|
+
export interface BuilderFlowAuthorizedLifecycleInput extends BuilderFlowSessionInput {
|
|
8
|
+
authorizationFile: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BuilderFlowAgentLifecycleInput extends BuilderFlowSessionInput {
|
|
11
|
+
reason: string;
|
|
5
12
|
}
|
|
6
13
|
export interface BuilderFlowSessionResult {
|
|
7
14
|
sessionDir: string;
|
|
8
15
|
projectRoot: string;
|
|
9
|
-
run:
|
|
16
|
+
run: BuilderFlowRunResult;
|
|
10
17
|
projection: AnyRecord;
|
|
11
18
|
attached: boolean;
|
|
12
19
|
}
|
|
13
20
|
export declare function startBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
|
|
14
21
|
export declare function syncBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
|
|
15
22
|
export declare function recoverBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
|
|
16
|
-
export declare function
|
|
23
|
+
export declare function inspectBuilderFlowSession(input: BuilderFlowSessionInput): Promise<BuilderFlowSessionResult>;
|
|
24
|
+
export declare function pauseBuilderFlowSession(input: BuilderFlowAgentLifecycleInput): Promise<BuilderFlowSessionResult>;
|
|
25
|
+
export declare function resumeBuilderFlowSession(input: BuilderFlowAgentLifecycleInput): Promise<BuilderFlowSessionResult>;
|
|
26
|
+
export declare function cancelBuilderFlowSession(input: BuilderFlowAuthorizedLifecycleInput): Promise<BuilderFlowSessionResult & {
|
|
27
|
+
assignmentReleased: boolean;
|
|
28
|
+
idempotent: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function releaseBuilderFlowAssignment(input: BuilderFlowAgentLifecycleInput): Promise<BuilderFlowSessionResult & {
|
|
31
|
+
assignmentReleased: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function archiveBuilderFlowSession(input: BuilderFlowAuthorizedLifecycleInput): Promise<BuilderFlowSessionResult & {
|
|
34
|
+
archiveDir: string;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function captureReviewWorkspaceSnapshot(projectRoot: string, reviewedFiles: Array<{
|
|
37
|
+
file: string;
|
|
38
|
+
sha256: string;
|
|
39
|
+
}>): AnyRecord;
|
|
17
40
|
export {};
|