@kontourai/flow-agents 3.2.0 → 3.4.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/add-to-project.yml +15 -0
- package/.github/workflows/ci.yml +173 -0
- package/CHANGELOG.md +54 -0
- package/CONTEXT.md +5 -1
- package/README.md +19 -8
- package/build/src/builder-flow-run-adapter.d.ts +80 -0
- package/build/src/builder-flow-run-adapter.js +241 -0
- package/build/src/builder-flow-runtime.d.ts +16 -0
- package/build/src/builder-flow-runtime.js +290 -0
- package/build/src/cli/assignment-provider.js +10 -1
- package/build/src/cli/builder-run.d.ts +1 -0
- package/build/src/cli/builder-run.js +27 -0
- package/build/src/cli/effective-backlog-settings.js +70 -2
- package/build/src/cli/init.d.ts +34 -0
- package/build/src/cli/init.js +341 -61
- package/build/src/cli/kit.js +55 -12
- package/build/src/cli/pull-work-provider.js +346 -5
- package/build/src/cli/skill-drift-check.d.ts +1 -0
- package/build/src/cli/skill-drift-check.js +165 -0
- package/build/src/cli/telemetry-doctor.d.ts +37 -0
- package/build/src/cli/telemetry-doctor.js +53 -6
- package/build/src/cli/validate-hook-influence.js +37 -7
- package/build/src/cli/workflow-artifact-cleanup-audit.js +418 -11
- package/build/src/cli/workflow-sidecar.d.ts +310 -1
- package/build/src/cli/workflow-sidecar.js +1914 -126
- package/build/src/cli.js +5 -0
- package/build/src/flow-kit/validate.d.ts +54 -34
- package/build/src/flow-kit/validate.js +237 -26
- package/build/src/index.d.ts +2 -0
- package/build/src/index.js +1 -0
- package/build/src/lib/console-connect-options.d.ts +97 -0
- package/build/src/lib/console-connect-options.js +199 -0
- package/build/src/lib/console-telemetry-validate.d.ts +49 -0
- package/build/src/lib/console-telemetry-validate.js +91 -0
- package/build/src/lib/flow-resolver.d.ts +54 -1
- package/build/src/lib/flow-resolver.js +112 -5
- package/build/src/lib/fs.d.ts +17 -0
- package/build/src/lib/fs.js +172 -0
- package/build/src/lib/local-artifact-root.d.ts +44 -1
- package/build/src/lib/local-artifact-root.js +131 -3
- package/build/src/runtime-adapters.d.ts +39 -3
- package/build/src/runtime-adapters.js +77 -31
- package/build/src/tools/build-universal-bundles.js +40 -2
- package/build/src/tools/codex-agent-routing.d.ts +2 -0
- package/build/src/tools/codex-agent-routing.js +49 -0
- package/build/src/tools/generate-context-map.js +1 -0
- package/build/src/tools/validate-source-tree.js +30 -3
- package/context/contracts/artifact-contract.md +16 -2
- package/context/scripts/hooks/lib/kit-catalog.js +235 -0
- package/context/scripts/hooks/lib/runnable-command.js +177 -0
- package/context/scripts/hooks/stop-goal-fit.js +278 -48
- package/context/scripts/hooks/workflow-steering.js +194 -22
- package/context/scripts/package.json +3 -0
- package/context/scripts/telemetry/install-console-config.sh +25 -4
- package/context/scripts/telemetry/lib/config.sh +102 -12
- package/context/scripts/telemetry/lib/pricing.sh +50 -0
- package/context/scripts/telemetry/lib/session.sh +3 -0
- package/context/scripts/telemetry/lib/transport.sh +87 -0
- package/context/scripts/telemetry/lib/usage.sh +205 -4
- package/context/scripts/telemetry/telemetry.conf +6 -0
- package/context/scripts/telemetry/telemetry.sh +48 -0
- package/context/settings/workspace-backlog-provider-settings.example.json +48 -0
- package/docs/agent-usage-feedback-loop.md +35 -0
- package/docs/architecture-engine-and-kits.md +110 -0
- package/docs/context-map.md +2 -0
- package/docs/coordination-guide.md +370 -0
- package/docs/decisions/agent-coordination.md +26 -9
- package/docs/decisions/embeddable-engine.md +152 -0
- package/docs/decisions/index.md +5 -3
- package/docs/decisions/trust-ledger-retention.md +88 -0
- package/docs/decisions/trust-reconcile.md +42 -9
- package/docs/decisions/workflow-enforcement.md +31 -9
- package/docs/fixture-ownership.md +6 -2
- package/docs/implementing-trust-reconciliation.md +129 -0
- package/docs/index.md +23 -9
- package/docs/integrations/flow-agents-console.md +275 -0
- package/docs/integrations/index.md +4 -0
- package/docs/kit-authoring-guide.md +52 -21
- package/docs/spec/builder-flow-runtime.md +80 -0
- package/docs/spec/runtime-hook-surface.md +45 -1
- package/docs/specs/economics-record-contract.md +270 -0
- package/docs/specs/harness-capability-matrix.md +74 -0
- package/docs/specs/learning-review-proposals-contract.md +340 -0
- package/docs/specs/routing-efficiency-review.md +59 -0
- package/docs/verifiable-trust.md +74 -25
- package/docs/workflow-artifact-lifecycle.md +38 -1
- package/docs/workflow-usage-guide.md +10 -0
- package/evals/acceptance/prove-capture-teeth.sh +132 -0
- package/evals/ci/antigaming-suite.sh +2 -0
- package/evals/ci/run-baseline.sh +78 -4
- package/evals/fixtures/economics/acceptance.json +12 -0
- package/evals/fixtures/economics/agents/tool-worker-1/events.jsonl +2 -0
- package/evals/fixtures/economics/agents/tool-worker-2/events.jsonl +2 -0
- package/evals/fixtures/economics/agents/tool-worker-3/events.jsonl +2 -0
- package/evals/fixtures/economics/agents/tool-worker-4/events.jsonl +1 -0
- package/evals/fixtures/economics/agents/tool-worker-5/events.jsonl +2 -0
- package/evals/fixtures/economics/critique.json +22 -0
- package/evals/fixtures/economics/expected-record.json +71 -0
- package/evals/fixtures/economics/session-usage-event.json +1 -0
- package/evals/fixtures/economics/state.json +11 -0
- package/evals/fixtures/economics/transcript.jsonl +3 -0
- package/evals/fixtures/hook-influence/cases.json +7 -7
- package/evals/fixtures/learning-review-proposals/balanced/economics.jsonl +6 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/economics.jsonl +5 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-1/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-2/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-3/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-4/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-5/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/economics.jsonl +6 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.json +30 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.md +66 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-5/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-6/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/repeat-window/economics.jsonl +6 -0
- package/evals/fixtures/learning-review-proposals/under-threshold/economics.jsonl +3 -0
- package/evals/fixtures/reconcile-preflight/disputed-critique-unsuperseded.json +48 -0
- package/evals/fixtures/reconcile-preflight/standalone-disputed-session-local.json +59 -0
- package/evals/fixtures/telemetry/usage-transcript-sample.jsonl +4 -0
- package/evals/fixtures/trust-reconcile-exploits/mcp-degrade.json +42 -0
- package/evals/integration/test_builder_entry_enforcement.sh +241 -0
- package/evals/integration/test_builder_step_producers.sh +18 -10
- package/evals/integration/test_bundle_install.sh +172 -0
- package/evals/integration/test_checkpoint_signing.sh +10 -2
- package/evals/integration/test_ci_actor_identity.sh +221 -0
- package/evals/integration/test_console_tenant_isolation.sh +167 -0
- package/evals/integration/test_critique_supersession_roundtrip.sh +4 -1
- package/evals/integration/test_dual_emit_flow_step.sh +10 -4
- package/evals/integration/test_economics_record.sh +674 -0
- package/evals/integration/test_effective_backlog_settings.sh +1 -1
- package/evals/integration/test_evidence_capture_hook.sh +17 -2
- package/evals/integration/test_exemption_usage_review.sh +198 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +83 -0
- package/evals/integration/test_flowdef_session_activation.sh +0 -1
- package/evals/integration/test_flowdef_session_history_preservation.sh +13 -3
- package/evals/integration/test_gate_lockdown.sh +7 -0
- package/evals/integration/test_gate_review_inquiry_records.sh +9 -1
- package/evals/integration/test_goal_fit_hook.sh +2031 -0
- package/evals/integration/test_hook_category_behaviors.sh +8 -1
- package/evals/integration/test_hook_influence_cases.sh +25 -1
- package/evals/integration/test_install_merge.sh +227 -2
- package/evals/integration/test_kit_conformance_levels.sh +6 -6
- package/evals/integration/test_learning_review_proposals.sh +329 -0
- package/evals/integration/test_liveness_conflict_injection.sh +26 -22
- package/evals/integration/test_liveness_console_relay.sh +166 -0
- package/evals/integration/test_liveness_heartbeat.sh +17 -17
- package/evals/integration/test_liveness_worktree_root.sh +575 -0
- package/evals/integration/test_phase_map_and_gate_claim.sh +6 -1
- package/evals/integration/test_publish_delivery.sh +389 -2
- package/evals/integration/test_pull_work_board.sh +200 -0
- package/evals/integration/test_pull_work_provider.sh +1 -1
- package/evals/integration/test_reconcile_preflight.sh +304 -0
- package/evals/integration/test_record_check.sh +378 -0
- package/evals/integration/test_routing_efficiency.sh +71 -0
- package/evals/integration/test_runtime_adapter_activation.sh +28 -0
- package/evals/integration/test_session_resume_roundtrip.sh +16 -19
- package/evals/integration/test_skill_drift_check.sh +870 -0
- package/evals/integration/test_takeover_protocol.sh +340 -0
- package/evals/integration/test_telemetry.sh +445 -0
- package/evals/integration/test_telemetry_doctor.sh +66 -0
- package/evals/integration/test_telemetry_usage_pipeline.sh +228 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +121 -13
- package/evals/integration/test_trust_reconcile_trailer_diagnostic.sh +247 -0
- package/evals/integration/test_usage_cost.sh +61 -0
- package/evals/integration/test_verify_hold.sh +910 -0
- package/evals/integration/test_veritas_governance_kit.sh +257 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +575 -3
- package/evals/integration/test_workflow_sidecar_writer.sh +1395 -0
- package/evals/integration/test_workflow_steering_hook.sh +157 -16
- package/evals/integration/test_workspace_settings.sh +176 -0
- package/evals/lib/env.sh +26 -0
- package/evals/lib/node.sh +8 -0
- package/evals/run.sh +37 -0
- package/evals/static/test_ci_integration_coverage.sh +115 -0
- package/evals/static/test_declared_scope_forms_documented.sh +114 -0
- package/evals/static/test_universal_bundles.sh +34 -0
- package/evals/static/test_validate_source_kit_asset_scope.sh +259 -0
- package/evals/static/test_workflow_skills.sh +1 -1
- package/kits/builder/flows/build.flow.json +9 -18
- package/kits/builder/flows/publish-learn.flow.json +5 -1
- package/kits/builder/kit.json +120 -0
- package/kits/builder/skills/continue-work/SKILL.md +2 -0
- package/kits/builder/skills/deliver/SKILL.md +115 -0
- package/kits/builder/skills/evidence-gate/SKILL.md +12 -0
- package/kits/builder/skills/execute-plan/SKILL.md +9 -0
- package/kits/builder/skills/learning-review/SKILL.md +51 -0
- package/kits/builder/skills/plan-work/SKILL.md +17 -20
- package/kits/builder/skills/pull-work/SKILL.md +33 -2
- package/kits/builder/skills/release-readiness/SKILL.md +12 -0
- package/kits/knowledge/kit.json +9 -0
- package/kits/veritas-governance/docs/README.md +113 -7
- package/kits/veritas-governance/fixtures/exemption/approved.trust-bundle.json +74 -0
- package/kits/veritas-governance/fixtures/exemption/not-approved.trust-bundle.json +74 -0
- package/kits/veritas-governance/fixtures/exemption-review/mixed-fresh-stale.DECLARED.json +14 -0
- package/kits/veritas-governance/flows/exemption-issuance.flow.json +35 -0
- package/kits/veritas-governance/kit.json +19 -0
- package/kits/veritas-governance/skills/exemption-usage-review/SKILL.md +128 -0
- package/kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs +231 -0
- package/package.json +2 -2
- package/packaging/manifest.json +29 -0
- package/schemas/backlog-provider-settings.schema.json +13 -0
- package/schemas/workflow-state.schema.json +44 -0
- package/scripts/README.md +4 -0
- package/scripts/check-content-boundary.cjs +8 -1
- package/scripts/ci/trust-reconcile.js +214 -253
- package/scripts/hooks/codex-hook-adapter.js +77 -2
- package/scripts/hooks/evidence-capture.js +38 -5
- package/scripts/hooks/lib/actor-identity.js +82 -0
- package/scripts/hooks/lib/codex-exit-code.js +316 -0
- package/scripts/hooks/lib/kit-catalog.js +235 -0
- package/scripts/hooks/lib/liveness-write.js +28 -1
- package/scripts/hooks/lib/local-artifact-paths.js +97 -1
- package/scripts/hooks/lib/runnable-command.js +177 -0
- package/scripts/hooks/lib/skill-drift.js +350 -0
- package/scripts/hooks/stop-goal-fit.js +278 -48
- package/scripts/hooks/workflow-steering.js +194 -22
- package/scripts/install-codex-home.sh +97 -47
- package/scripts/install-merge.js +72 -14
- package/scripts/install-owned-files.js +178 -0
- package/scripts/lib/reconcile-shape.js +381 -0
- package/scripts/liveness/relay.sh +84 -0
- package/scripts/telemetry/economics-record.schema.json +145 -0
- package/scripts/telemetry/economics-record.sh +331 -0
- package/scripts/telemetry/install-console-config.sh +25 -4
- package/scripts/telemetry/learning-review-decide.sh +124 -0
- package/scripts/telemetry/learning-review-proposals.schema.json +161 -0
- package/scripts/telemetry/learning-review-proposals.sh +484 -0
- package/scripts/telemetry/lib/config.sh +102 -12
- package/scripts/telemetry/lib/pricing.sh +14 -6
- package/scripts/telemetry/lib/session.sh +3 -0
- package/scripts/telemetry/lib/transport.sh +133 -15
- package/scripts/telemetry/lib/usage.sh +121 -28
- package/scripts/telemetry/routing-efficiency.sh +0 -0
- package/scripts/telemetry/telemetry.conf +6 -0
- package/scripts/telemetry/telemetry.sh +48 -0
- package/src/builder-flow-run-adapter.ts +357 -0
- package/src/builder-flow-runtime.ts +348 -0
- package/src/cli/assignment-provider.ts +12 -1
- package/src/cli/builder-flow-run-adapter.test.mjs +495 -0
- package/src/cli/builder-flow-runtime.test.mjs +213 -0
- package/src/cli/builder-run.ts +28 -0
- package/src/cli/codex-agent-routing.test.mjs +44 -0
- package/src/cli/codex-exit-code.test.mjs +207 -0
- package/src/cli/console-connect-options.test.mjs +329 -0
- package/src/cli/console-telemetry-validate.test.mjs +157 -0
- package/src/cli/effective-backlog-settings.ts +68 -2
- package/src/cli/flow-resolver-composition.test.mjs +72 -0
- package/src/cli/init.test.mjs +161 -0
- package/src/cli/init.ts +407 -62
- package/src/cli/kit-metadata-security.test.mjs +443 -0
- package/src/cli/kit.ts +50 -12
- package/src/cli/pull-work-provider.ts +377 -3
- package/src/cli/sidecar-pure-helpers.test.mjs +64 -0
- package/src/cli/skill-drift-check.ts +196 -0
- package/src/cli/telemetry-doctor.test.mjs +53 -0
- package/src/cli/telemetry-doctor.ts +50 -7
- package/src/cli/validate-hook-influence.ts +37 -6
- package/src/cli/workflow-artifact-cleanup-audit.ts +483 -10
- package/src/cli/workflow-sidecar.ts +1980 -119
- package/src/cli.ts +5 -0
- package/src/flow-kit/validate.ts +277 -38
- package/src/index.ts +19 -0
- package/src/lib/console-connect-options.ts +261 -0
- package/src/lib/console-telemetry-validate.ts +88 -0
- package/src/lib/flow-resolver.ts +117 -4
- package/src/lib/fs.ts +160 -0
- package/src/lib/local-artifact-root.ts +129 -3
- package/src/runtime-adapters.ts +113 -33
- package/src/tools/build-universal-bundles.ts +36 -2
- package/src/tools/codex-agent-routing.ts +48 -0
- package/src/tools/generate-context-map.ts +1 -0
- package/src/tools/validate-source-tree.ts +29 -3
|
@@ -318,6 +318,18 @@ add_stop_data_and_emit_usage() {
|
|
|
318
318
|
transcript_usage=$(usage_parse_transcript "$transcript_path")
|
|
319
319
|
[[ -z "$transcript_usage" ]] && transcript_usage='null'
|
|
320
320
|
|
|
321
|
+
# Prefer the transcript-derived model (runtime-agnostic — works for any
|
|
322
|
+
# runtime that exposes a transcript) over the kiro-only ~/.kiro/agents
|
|
323
|
+
# lookup above, which never resolves for non-kiro agent names (e.g.
|
|
324
|
+
# Claude Code's fixed "dev" hook arg) and falls through to "unknown".
|
|
325
|
+
# Falls back to usage_get_model's kiro result when no transcript usage
|
|
326
|
+
# is available (kiro-cli path is unchanged: transcript_usage stays null).
|
|
327
|
+
if [[ "$transcript_usage" != "null" ]]; then
|
|
328
|
+
local transcript_model
|
|
329
|
+
transcript_model=$(usage_model_from_transcript_usage "$transcript_usage")
|
|
330
|
+
[[ -n "$transcript_model" ]] && model="$transcript_model"
|
|
331
|
+
fi
|
|
332
|
+
|
|
321
333
|
local usage_event
|
|
322
334
|
usage_event=$(echo "$event" | jq -c \
|
|
323
335
|
--arg m "$model" \
|
|
@@ -340,6 +352,42 @@ add_stop_data_and_emit_usage() {
|
|
|
340
352
|
})
|
|
341
353
|
}')
|
|
342
354
|
transport_emit "$usage_event"
|
|
355
|
+
|
|
356
|
+
# Per-run kit-economics record (#349, console ADR 0003). Best-effort + DETACHED so it can never
|
|
357
|
+
# alter existing telemetry timing or fail the stop hook: assemble one kontour.console.economics
|
|
358
|
+
# fact from this session.usage event + the run's review sidecars, write it local-first, then
|
|
359
|
+
# opt-in relay it. Resolve the sidecar paths from the run cwd's active-session pointer; the
|
|
360
|
+
# emitter defaults every field cleanly when a sidecar is absent.
|
|
361
|
+
local econ_script="${TELEMETRY_DIR}/economics-record.sh"
|
|
362
|
+
if [[ -f "$econ_script" ]]; then
|
|
363
|
+
local econ_cwd econ_slug econ_state econ_acceptance econ_critique
|
|
364
|
+
econ_cwd=$(echo "$usage_event" | jq -r '.context.cwd // ""' 2>/dev/null)
|
|
365
|
+
[[ -z "$econ_cwd" || ! -d "$econ_cwd" ]] && econ_cwd="$PWD"
|
|
366
|
+
# Active slug from the canonical current pointer first, falling back to the legacy pointer.
|
|
367
|
+
econ_slug=""
|
|
368
|
+
if [[ -f "$econ_cwd/.kontourai/flow-agents/current.json" ]]; then
|
|
369
|
+
econ_slug=$(jq -r '.active_slug // .artifact_dir // empty' "$econ_cwd/.kontourai/flow-agents/current.json" 2>/dev/null)
|
|
370
|
+
elif [[ -f "$econ_cwd/.flow-agents/current.json" ]]; then
|
|
371
|
+
econ_slug=$(jq -r '.active_slug // .artifact_dir // empty' "$econ_cwd/.flow-agents/current.json" 2>/dev/null)
|
|
372
|
+
fi
|
|
373
|
+
econ_state="" econ_acceptance="" econ_critique="" econ_agents_dir=""
|
|
374
|
+
if [[ -n "$econ_slug" ]]; then
|
|
375
|
+
# state.json under .kontourai/flow-agents/<slug>/ (fallback .flow-agents/<slug>/); the run's
|
|
376
|
+
# per-agent event logs live alongside it in <slug>/agents/ (#415 delegations[] source).
|
|
377
|
+
for d in "$econ_cwd/.kontourai/flow-agents/$econ_slug" "$econ_cwd/.flow-agents/$econ_slug"; do
|
|
378
|
+
[[ -f "$d/state.json" ]] && { econ_state="$d/state.json"; [[ -d "$d/agents" ]] && econ_agents_dir="$d/agents"; break; }
|
|
379
|
+
done
|
|
380
|
+
[[ -f "$econ_cwd/.flow-agents/$econ_slug/acceptance.json" ]] && econ_acceptance="$econ_cwd/.flow-agents/$econ_slug/acceptance.json"
|
|
381
|
+
[[ -f "$econ_cwd/.flow-agents/$econ_slug/critique.json" ]] && econ_critique="$econ_cwd/.flow-agents/$econ_slug/critique.json"
|
|
382
|
+
fi
|
|
383
|
+
local econ_args=("$usage_event")
|
|
384
|
+
[[ -n "$econ_state" ]] && econ_args+=(--state "$econ_state")
|
|
385
|
+
[[ -n "$econ_acceptance" ]] && econ_args+=(--acceptance "$econ_acceptance")
|
|
386
|
+
[[ -n "$econ_critique" ]] && econ_args+=(--critique "$econ_critique")
|
|
387
|
+
[[ -n "$econ_agents_dir" ]] && econ_args+=(--agents-dir "$econ_agents_dir")
|
|
388
|
+
(bash "$econ_script" "${econ_args[@]}") </dev/null >/dev/null 2>&1 &
|
|
389
|
+
disown 2>/dev/null || true
|
|
390
|
+
fi
|
|
343
391
|
fi
|
|
344
392
|
|
|
345
393
|
echo "$event"
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { isDeepStrictEqual } from "node:util";
|
|
6
|
+
import {
|
|
7
|
+
attachEvidence,
|
|
8
|
+
evaluateRun,
|
|
9
|
+
expectationsForGate,
|
|
10
|
+
loadRun,
|
|
11
|
+
normalizeTrustBundle,
|
|
12
|
+
openGates,
|
|
13
|
+
readJson,
|
|
14
|
+
startRun,
|
|
15
|
+
validateDefinition,
|
|
16
|
+
type FlowEvidenceEntry,
|
|
17
|
+
type FlowRunState,
|
|
18
|
+
type GateOutcome,
|
|
19
|
+
type JsonObject,
|
|
20
|
+
} from "@kontourai/flow";
|
|
21
|
+
import { resolveEffectiveFlowDefinition } from "./lib/flow-resolver.js";
|
|
22
|
+
|
|
23
|
+
export const BUILDER_BUILD_FLOW_ID = "builder.build";
|
|
24
|
+
export const BUILDER_BUILD_FLOW_RELATIVE_PATH = "kits/builder/flows/build.flow.json";
|
|
25
|
+
|
|
26
|
+
export interface BuilderBuildTrustBundleEvidenceInput {
|
|
27
|
+
gate: string;
|
|
28
|
+
/**
|
|
29
|
+
* Trusted local evidence path interpreted by Flow relative to `cwd`.
|
|
30
|
+
* Callers must not pass raw user-controlled paths to this local runtime API.
|
|
31
|
+
*/
|
|
32
|
+
file: string;
|
|
33
|
+
status?: "passed" | "failed";
|
|
34
|
+
producer?: string;
|
|
35
|
+
authorityTrace?: string;
|
|
36
|
+
routeReason?: string;
|
|
37
|
+
expectationIds?: string[];
|
|
38
|
+
supersede?: string | string[];
|
|
39
|
+
classifier?: JsonObject;
|
|
40
|
+
diagnostics?: JsonObject;
|
|
41
|
+
analytics?: JsonObject;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface StartBuilderBuildRunInput {
|
|
45
|
+
subject: string;
|
|
46
|
+
params?: JsonObject;
|
|
47
|
+
/**
|
|
48
|
+
* Trusted local runtime root. Flow owns persistence and interprets attached
|
|
49
|
+
* evidence files relative to this directory.
|
|
50
|
+
*/
|
|
51
|
+
cwd?: string;
|
|
52
|
+
runId?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface EvaluateBuilderBuildRunInput {
|
|
56
|
+
runId: string;
|
|
57
|
+
/**
|
|
58
|
+
* Trusted local runtime root. Flow owns persistence and interprets attached
|
|
59
|
+
* evidence files relative to this directory.
|
|
60
|
+
*/
|
|
61
|
+
cwd?: string;
|
|
62
|
+
evidence?: BuilderBuildTrustBundleEvidenceInput;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface LoadBuilderBuildRunInput {
|
|
66
|
+
runId: string;
|
|
67
|
+
cwd?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface BuilderBuildRunResult {
|
|
71
|
+
definitionId: typeof BUILDER_BUILD_FLOW_ID;
|
|
72
|
+
definitionVersion: string;
|
|
73
|
+
runId: string;
|
|
74
|
+
dir: string;
|
|
75
|
+
state: FlowRunState;
|
|
76
|
+
attachedEvidence: FlowEvidenceEntry[];
|
|
77
|
+
outcomes: GateOutcome[];
|
|
78
|
+
manifest: JsonObject;
|
|
79
|
+
freshnessTransitions: JsonObject[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type BuilderBuildRunIdentityMismatch = "definition-id" | "definition-version" | "definition-content";
|
|
83
|
+
|
|
84
|
+
export class BuilderBuildRunInputError extends Error {
|
|
85
|
+
readonly code = "BUILDER_BUILD_RUN_INVALID_INPUT" as const;
|
|
86
|
+
readonly field: string;
|
|
87
|
+
|
|
88
|
+
constructor(field: string, reason: string) {
|
|
89
|
+
super(`invalid Builder build run input for ${field}: ${reason}`);
|
|
90
|
+
this.name = "BuilderBuildRunInputError";
|
|
91
|
+
this.field = field;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class BuilderBuildRunIdentityError extends Error {
|
|
96
|
+
readonly code = "BUILDER_BUILD_RUN_IDENTITY_MISMATCH" as const;
|
|
97
|
+
readonly expectedDefinitionId: string;
|
|
98
|
+
readonly expectedDefinitionVersion: string;
|
|
99
|
+
readonly actualDefinitionId: string;
|
|
100
|
+
readonly actualDefinitionVersion: string;
|
|
101
|
+
readonly mismatch: BuilderBuildRunIdentityMismatch;
|
|
102
|
+
readonly runId: string;
|
|
103
|
+
|
|
104
|
+
constructor(
|
|
105
|
+
runId: string,
|
|
106
|
+
expectedDefinition: { id: string; version: string },
|
|
107
|
+
actualDefinition: { id: string; version: string },
|
|
108
|
+
mismatch: BuilderBuildRunIdentityMismatch,
|
|
109
|
+
) {
|
|
110
|
+
super(`expected canonical ${expectedDefinition.id}@${expectedDefinition.version} run, received ${actualDefinition.id}@${actualDefinition.version} for ${runId}`);
|
|
111
|
+
this.name = "BuilderBuildRunIdentityError";
|
|
112
|
+
this.expectedDefinitionId = expectedDefinition.id;
|
|
113
|
+
this.expectedDefinitionVersion = expectedDefinition.version;
|
|
114
|
+
this.actualDefinitionId = actualDefinition.id;
|
|
115
|
+
this.actualDefinitionVersion = actualDefinition.version;
|
|
116
|
+
this.mismatch = mismatch;
|
|
117
|
+
this.runId = runId;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function resolveBuilderBuildFlowDefinitionPath(startDir = moduleDirectory()): string {
|
|
122
|
+
const root = findPackageRoot(startDir);
|
|
123
|
+
return path.join(root, BUILDER_BUILD_FLOW_RELATIVE_PATH);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function startBuilderBuildRun(input: StartBuilderBuildRunInput): Promise<BuilderBuildRunResult> {
|
|
127
|
+
assertRuntimeInput(input, ["evidence", "now", "gate"]);
|
|
128
|
+
if (!isNonEmptyString(input.subject)) {
|
|
129
|
+
throw new BuilderBuildRunInputError("subject", "must be a non-empty string");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const cwd = input.cwd ?? process.cwd();
|
|
133
|
+
const definitionPath = resolveBuilderBuildFlowDefinitionPath();
|
|
134
|
+
const definition = await loadShippedBuilderBuildDefinition(definitionPath);
|
|
135
|
+
const runtimeDefinitionPath = materializeRuntimeDefinition(cwd, definition);
|
|
136
|
+
const started = await startRun(runtimeDefinitionPath, {
|
|
137
|
+
cwd,
|
|
138
|
+
runId: input.runId,
|
|
139
|
+
params: {
|
|
140
|
+
...(input.params ?? {}),
|
|
141
|
+
subject: input.subject,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
const run = await loadCanonicalBuilderBuildRun(started.runId, cwd, definition);
|
|
145
|
+
|
|
146
|
+
return resultFromRun(run, started.runId);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function evaluateBuilderBuildRun(input: EvaluateBuilderBuildRunInput): Promise<BuilderBuildRunResult> {
|
|
150
|
+
assertRuntimeInput(input, ["now", "gate"]);
|
|
151
|
+
if (Array.isArray(input.evidence)) {
|
|
152
|
+
throw new BuilderBuildRunInputError("evidence", "must be zero or one evidence object, not an array");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const cwd = input.cwd ?? process.cwd();
|
|
156
|
+
const run = await loadRun(input.runId, cwd);
|
|
157
|
+
const definition = await loadShippedBuilderBuildDefinition(resolveBuilderBuildFlowDefinitionPath());
|
|
158
|
+
assertCanonicalDefinition(input.runId, definition, run.definition);
|
|
159
|
+
|
|
160
|
+
let attachedEvidence: FlowEvidenceEntry[] = [];
|
|
161
|
+
if (input.evidence !== undefined) {
|
|
162
|
+
const evidence = validateEvidenceInput(input.evidence);
|
|
163
|
+
assertCurrentOpenGate(run.definition, run.state, evidence.gate);
|
|
164
|
+
const normalized = normalizeTrustBundle(await readJson(path.resolve(cwd, evidence.file)));
|
|
165
|
+
assertBundleSubjects(normalized.bundle, run.state.subject, openGates(run.definition, run.state)[0]);
|
|
166
|
+
attachedEvidence = [await attachEvidence(input.runId, trustBundleAttachOptions(cwd, evidence))];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const evaluated = await evaluateRun(input.runId, { cwd });
|
|
170
|
+
return {
|
|
171
|
+
definitionId: evaluated.definition.id,
|
|
172
|
+
definitionVersion: evaluated.definition.version,
|
|
173
|
+
runId: input.runId,
|
|
174
|
+
dir: evaluated.dir,
|
|
175
|
+
state: evaluated.state,
|
|
176
|
+
attachedEvidence,
|
|
177
|
+
outcomes: evaluated.outcomes,
|
|
178
|
+
manifest: evaluated.manifest,
|
|
179
|
+
freshnessTransitions: evaluated.freshness_transitions,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function loadBuilderBuildRun(input: LoadBuilderBuildRunInput): Promise<BuilderBuildRunResult> {
|
|
184
|
+
assertRuntimeInput(input, ["evidence", "now", "gate"]);
|
|
185
|
+
const cwd = input.cwd ?? process.cwd();
|
|
186
|
+
const run = await loadRun(input.runId, cwd);
|
|
187
|
+
const definition = await loadShippedBuilderBuildDefinition(resolveBuilderBuildFlowDefinitionPath());
|
|
188
|
+
assertCanonicalDefinition(input.runId, definition, run.definition);
|
|
189
|
+
return resultFromRun(run, input.runId);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function resultFromRun(run: Awaited<ReturnType<typeof loadRun>>, runId: string): BuilderBuildRunResult {
|
|
193
|
+
return {
|
|
194
|
+
definitionId: run.definition.id,
|
|
195
|
+
definitionVersion: run.definition.version,
|
|
196
|
+
runId,
|
|
197
|
+
dir: run.dir,
|
|
198
|
+
state: run.state,
|
|
199
|
+
attachedEvidence: [],
|
|
200
|
+
outcomes: [],
|
|
201
|
+
manifest: run.manifest,
|
|
202
|
+
freshnessTransitions: [],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function loadCanonicalBuilderBuildRun(
|
|
207
|
+
runId: string,
|
|
208
|
+
cwd: string,
|
|
209
|
+
definition: { id: string; version: string },
|
|
210
|
+
): Promise<Awaited<ReturnType<typeof loadRun>>> {
|
|
211
|
+
const run = await loadRun(runId, cwd);
|
|
212
|
+
assertCanonicalDefinition(runId, definition, run.definition);
|
|
213
|
+
return run;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function loadShippedBuilderBuildDefinition(definitionPath: string): Promise<{ id: string; version: string }> {
|
|
217
|
+
const packageRoot = findPackageRoot(path.dirname(definitionPath));
|
|
218
|
+
const effective = resolveEffectiveFlowDefinition(BUILDER_BUILD_FLOW_ID, packageRoot, { allowOverride: false });
|
|
219
|
+
if (!effective) {
|
|
220
|
+
throw new BuilderBuildRunInputError("definition", "could not compile the shipped uses_flow composition");
|
|
221
|
+
}
|
|
222
|
+
const definition = validateDefinition(effective);
|
|
223
|
+
if (definition.id !== BUILDER_BUILD_FLOW_ID) {
|
|
224
|
+
throw new BuilderBuildRunInputError("definition", `expected shipped definition id ${BUILDER_BUILD_FLOW_ID}`);
|
|
225
|
+
}
|
|
226
|
+
return definition;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function materializeRuntimeDefinition(cwd: string, definition: unknown): string {
|
|
230
|
+
const content = `${JSON.stringify(definition, null, 2)}\n`;
|
|
231
|
+
const digest = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
232
|
+
const directory = path.join(cwd, ".kontourai", "flow-agents", "runtime-definitions");
|
|
233
|
+
mkdirSync(directory, { recursive: true });
|
|
234
|
+
const file = path.join(directory, `builder-build-${digest}.flow.json`);
|
|
235
|
+
if (!existsSync(file)) writeFileSync(file, content);
|
|
236
|
+
return file;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function assertCanonicalDefinition(
|
|
240
|
+
runId: string,
|
|
241
|
+
expectedDefinition: { id: string; version: string },
|
|
242
|
+
actualDefinition: { id: string; version: string },
|
|
243
|
+
): void {
|
|
244
|
+
if (isDeepStrictEqual(actualDefinition, expectedDefinition)) return;
|
|
245
|
+
|
|
246
|
+
const mismatch: BuilderBuildRunIdentityMismatch = actualDefinition.id !== expectedDefinition.id
|
|
247
|
+
? "definition-id"
|
|
248
|
+
: actualDefinition.version !== expectedDefinition.version
|
|
249
|
+
? "definition-version"
|
|
250
|
+
: "definition-content";
|
|
251
|
+
throw new BuilderBuildRunIdentityError(runId, expectedDefinition, actualDefinition, mismatch);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function assertRuntimeInput(input: unknown, forbiddenFields: string[]): asserts input is Record<string, unknown> {
|
|
255
|
+
if (!isRecord(input)) {
|
|
256
|
+
throw new BuilderBuildRunInputError("input", "must be an object");
|
|
257
|
+
}
|
|
258
|
+
for (const field of forbiddenFields) {
|
|
259
|
+
if (field in input) {
|
|
260
|
+
throw new BuilderBuildRunInputError(field, "is not supported by this API");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function validateEvidenceInput(evidence: unknown): BuilderBuildTrustBundleEvidenceInput {
|
|
266
|
+
if (!isRecord(evidence)) {
|
|
267
|
+
throw new BuilderBuildRunInputError("evidence", "must be an object");
|
|
268
|
+
}
|
|
269
|
+
if (!isNonEmptyString(evidence.gate)) {
|
|
270
|
+
throw new BuilderBuildRunInputError("evidence.gate", "must be a non-empty string");
|
|
271
|
+
}
|
|
272
|
+
if (!isNonEmptyString(evidence.file)) {
|
|
273
|
+
throw new BuilderBuildRunInputError("evidence.file", "must be a non-empty string");
|
|
274
|
+
}
|
|
275
|
+
if (evidence.status !== undefined && evidence.status !== "passed" && evidence.status !== "failed") {
|
|
276
|
+
throw new BuilderBuildRunInputError("evidence.status", "must be passed or failed");
|
|
277
|
+
}
|
|
278
|
+
return evidence as unknown as BuilderBuildTrustBundleEvidenceInput;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function assertCurrentOpenGate(definition: unknown, state: FlowRunState, evidenceGate: string): void {
|
|
282
|
+
const gates = openGates(definition, state);
|
|
283
|
+
if (gates.length !== 1) {
|
|
284
|
+
throw new BuilderBuildRunInputError("evidence.gate", "requires exactly one current open gate");
|
|
285
|
+
}
|
|
286
|
+
if (gates[0].id !== evidenceGate) {
|
|
287
|
+
throw new BuilderBuildRunInputError("evidence.gate", "must target the persisted current open gate");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function assertBundleSubjects(bundle: unknown, subject: string, gate: unknown): void {
|
|
292
|
+
if (!isRecord(bundle) || !Array.isArray(bundle.claims)) {
|
|
293
|
+
throw new BuilderBuildRunInputError("evidence", "contains no normalized claims");
|
|
294
|
+
}
|
|
295
|
+
const selectors = expectationsForGate(gate).map((expectation: any) => expectation.bundle_claim);
|
|
296
|
+
const relevant = bundle.claims.filter((claim) =>
|
|
297
|
+
isRecord(claim)
|
|
298
|
+
&& selectors.some((selector: any) =>
|
|
299
|
+
selector.claimType === claim.claimType
|
|
300
|
+
&& (!selector.subjectType || selector.subjectType === claim.subjectType)
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
if (relevant.length === 0) {
|
|
304
|
+
throw new BuilderBuildRunInputError("evidence.claims", "contains no claim matching the persisted current open gate");
|
|
305
|
+
}
|
|
306
|
+
for (const claim of relevant) {
|
|
307
|
+
const metadata = isRecord(claim.metadata) ? claim.metadata : null;
|
|
308
|
+
if (!metadata || metadata.workflow_subject_ref !== subject) {
|
|
309
|
+
throw new BuilderBuildRunInputError("evidence.claims.metadata.workflow_subject_ref", "must match the persisted run subject");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function trustBundleAttachOptions(cwd: string, evidence: BuilderBuildTrustBundleEvidenceInput): JsonObject {
|
|
315
|
+
return {
|
|
316
|
+
cwd,
|
|
317
|
+
gate: evidence.gate,
|
|
318
|
+
file: evidence.file,
|
|
319
|
+
kind: "trust.bundle",
|
|
320
|
+
bundle: true,
|
|
321
|
+
...(evidence.status ? { status: evidence.status } : {}),
|
|
322
|
+
...(evidence.producer ? { producer: evidence.producer } : {}),
|
|
323
|
+
...(evidence.authorityTrace ? { authorityTrace: evidence.authorityTrace } : {}),
|
|
324
|
+
...(evidence.routeReason ? { route_reason: evidence.routeReason } : {}),
|
|
325
|
+
...(evidence.expectationIds ? { expectation_ids: evidence.expectationIds } : {}),
|
|
326
|
+
...(evidence.supersede ? { supersede: evidence.supersede } : {}),
|
|
327
|
+
...(evidence.classifier ? { classifier: evidence.classifier } : {}),
|
|
328
|
+
...(evidence.diagnostics ? { diagnostics: evidence.diagnostics } : {}),
|
|
329
|
+
...(evidence.analytics ? { analytics: evidence.analytics } : {}),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
334
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
338
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function moduleDirectory(): string {
|
|
342
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function findPackageRoot(startDir: string): string {
|
|
346
|
+
let dir = startDir;
|
|
347
|
+
for (;;) {
|
|
348
|
+
if (existsSync(path.join(dir, "package.json")) && existsSync(path.join(dir, BUILDER_BUILD_FLOW_RELATIVE_PATH))) {
|
|
349
|
+
return dir;
|
|
350
|
+
}
|
|
351
|
+
const parent = path.dirname(dir);
|
|
352
|
+
if (parent === dir) {
|
|
353
|
+
throw new Error(`unable to locate ${BUILDER_BUILD_FLOW_RELATIVE_PATH} from ${startDir}`);
|
|
354
|
+
}
|
|
355
|
+
dir = parent;
|
|
356
|
+
}
|
|
357
|
+
}
|