@kontourai/flow-agents 1.3.0 → 2.0.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 (214) hide show
  1. package/.github/CODEOWNERS +29 -0
  2. package/.github/actions/trust-verify/action.yml +145 -0
  3. package/.github/workflows/ci.yml +11 -4
  4. package/.github/workflows/kit-gates-demo.yml +2 -2
  5. package/.github/workflows/publish-npm.yml +10 -2
  6. package/.github/workflows/release-please.yml +1 -1
  7. package/.github/workflows/trust-reconcile.yml +113 -0
  8. package/AGENTS.md +13 -0
  9. package/CHANGELOG.md +103 -0
  10. package/CONTRIBUTING.md +4 -4
  11. package/README.md +1 -0
  12. package/agents/tool-planner.json +1 -1
  13. package/build/src/cli/console-learning-projection.d.ts +1 -0
  14. package/build/src/cli/effective-backlog-settings.d.ts +1 -0
  15. package/build/src/cli/fixture-retirement-audit.d.ts +2 -0
  16. package/build/src/cli/init.d.ts +17 -0
  17. package/build/src/cli/init.js +242 -20
  18. package/build/src/cli/kit.d.ts +1 -0
  19. package/build/src/cli/promote-workflow-artifact.d.ts +1 -0
  20. package/build/src/cli/publish-change-helper.d.ts +1 -0
  21. package/build/src/cli/pull-work-provider.d.ts +1 -0
  22. package/build/src/cli/runtime-adapter.d.ts +1 -0
  23. package/build/src/cli/telemetry-doctor.d.ts +1 -0
  24. package/build/src/cli/usage-feedback.d.ts +1 -0
  25. package/build/src/cli/utterance-check.d.ts +1 -0
  26. package/build/src/cli/validate-hook-influence.d.ts +1 -0
  27. package/build/src/cli/validate-source-tree.d.ts +1 -0
  28. package/build/src/cli/validate-workflow-artifacts.d.ts +2 -0
  29. package/build/src/cli/validate-workflow-artifacts.js +19 -2
  30. package/build/src/cli/verify.d.ts +1 -0
  31. package/build/src/cli/verify.js +90 -0
  32. package/build/src/cli/veritas-governance.d.ts +1 -0
  33. package/build/src/cli/workflow-artifact-cleanup-audit.d.ts +1 -0
  34. package/build/src/cli/workflow-sidecar.d.ts +324 -0
  35. package/build/src/cli/workflow-sidecar.js +1973 -90
  36. package/build/src/cli.d.ts +2 -0
  37. package/build/src/cli.js +2 -3
  38. package/build/src/flow-kit/validate.d.ts +81 -0
  39. package/build/src/index.d.ts +5 -0
  40. package/build/src/index.js +36 -0
  41. package/build/src/lib/args.d.ts +8 -0
  42. package/build/src/lib/flow-resolver.d.ts +82 -0
  43. package/build/src/lib/flow-resolver.js +237 -0
  44. package/build/src/lib/fs.d.ts +7 -0
  45. package/build/src/lib/workflow-learning-projection.d.ts +132 -0
  46. package/build/src/runtime-adapters.d.ts +18 -0
  47. package/build/src/tools/build-universal-bundles.d.ts +2 -0
  48. package/build/src/tools/build-universal-bundles.js +34 -22
  49. package/build/src/tools/common.d.ts +9 -0
  50. package/build/src/tools/generate-context-map.d.ts +2 -0
  51. package/build/src/tools/generate-context-map.js +3 -16
  52. package/build/src/tools/validate-package.d.ts +2 -0
  53. package/build/src/tools/validate-source-tree.d.ts +2 -0
  54. package/build/src/tools/validate-source-tree.js +42 -162
  55. package/context/contracts/artifact-contract.md +10 -0
  56. package/context/contracts/delivery-contract.md +1 -0
  57. package/context/contracts/review-contract.md +1 -0
  58. package/context/contracts/verification-contract.md +2 -0
  59. package/context/gate-awareness.md +39 -0
  60. package/context/scripts/hooks/stop-goal-fit.js +632 -70
  61. package/docs/adr/0001-flow-agents-consumes-flow.md +1 -1
  62. package/docs/adr/0002-flow-kits-as-extension-unit.md +1 -1
  63. package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
  64. package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
  65. package/docs/adr/0007-skill-audit.md +1 -1
  66. package/docs/adr/0009-canonical-hook-core-kit-boundary.md +95 -0
  67. package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +139 -0
  68. package/docs/adr/0011-mcp-posture.md +100 -0
  69. package/docs/adr/0012-agent-coordination-as-liveness-claims.md +119 -0
  70. package/docs/adr/0013-context-lifecycle.md +151 -0
  71. package/docs/adr/0014-core-vs-domain-kit-boundary.md +143 -0
  72. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +120 -0
  73. package/docs/adr/0016-three-hard-boundary-model.md +71 -0
  74. package/docs/adr/0017-anti-gaming-trust-security-model.md +155 -0
  75. package/docs/agent-system-guidebook.md +5 -12
  76. package/docs/context-map.md +4 -10
  77. package/docs/developer-architecture.md +14 -0
  78. package/docs/index.md +3 -2
  79. package/docs/integrations/framework-adapter.md +19 -6
  80. package/docs/integrations/index.md +2 -2
  81. package/docs/north-star.md +4 -4
  82. package/docs/operating-layers.md +3 -3
  83. package/docs/plans/adr-0010-phase2-gate-recompute.md +55 -0
  84. package/docs/repository-structure.md +2 -2
  85. package/docs/skills-map.md +1 -0
  86. package/docs/spec/runtime-hook-surface.md +78 -10
  87. package/docs/standards-register.md +3 -3
  88. package/docs/survey-utterance-check.md +1 -1
  89. package/docs/trust-anchor-adoption.md +197 -0
  90. package/docs/verifiable-trust.md +95 -0
  91. package/docs/veritas-integration.md +2 -2
  92. package/docs/workflow-usage-guide.md +69 -0
  93. package/evals/acceptance/DEMO-false-completion.md +144 -0
  94. package/evals/acceptance/demo-cast.sh +92 -0
  95. package/evals/acceptance/demo-false-completion.sh +72 -0
  96. package/evals/acceptance/demo-real-evidence.sh +104 -0
  97. package/evals/acceptance/demo.tape +29 -0
  98. package/evals/acceptance/prove-capture-teeth-declared.sh +335 -0
  99. package/evals/acceptance/prove-capture-teeth.sh +114 -0
  100. package/evals/acceptance/prove-teeth.sh +105 -0
  101. package/evals/ci/antigaming-suite.sh +54 -0
  102. package/evals/ci/run-baseline.sh +2 -0
  103. package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/flows/review.flow.json +26 -0
  104. package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/kit.json +20 -0
  105. package/evals/fixtures/flow-kit-repository/valid-unknown-extension/flows/review.flow.json +26 -0
  106. package/evals/fixtures/flow-kit-repository/valid-unknown-extension/kit.json +18 -0
  107. package/evals/integration/test_builder_step_producers.sh +379 -0
  108. package/evals/integration/test_bundle_install.sh +35 -71
  109. package/evals/integration/test_bundle_lifecycle.sh +39 -2
  110. package/evals/integration/test_captured_fail_reconciliation.sh +820 -0
  111. package/evals/integration/test_checkpoint_signing.sh +489 -0
  112. package/evals/integration/test_claim_lookup.sh +352 -0
  113. package/evals/integration/test_command_log_integrity.sh +275 -0
  114. package/evals/integration/test_context_map.sh +0 -2
  115. package/evals/integration/test_dual_emit_flow_step.sh +278 -0
  116. package/evals/integration/test_enforcer_expects_driven.sh +281 -0
  117. package/evals/integration/test_evidence_capture_hook.sh +185 -0
  118. package/evals/integration/test_flow_kit_repository.sh +2 -0
  119. package/evals/integration/test_flowdef_session_activation.sh +273 -0
  120. package/evals/integration/test_flowdef_session_history_preservation.sh +250 -0
  121. package/evals/integration/test_gate_bypass_chain.sh +448 -0
  122. package/evals/integration/test_gate_lockdown.sh +1137 -0
  123. package/evals/integration/test_gate_review_inquiry_records.sh +399 -0
  124. package/evals/integration/test_goal_fit_escape_hatch.sh +73 -0
  125. package/evals/integration/test_goal_fit_hook.sh +69 -4
  126. package/evals/integration/test_goal_fit_rederive.sh +263 -0
  127. package/evals/integration/test_hook_category_behaviors.sh +14 -0
  128. package/evals/integration/test_install_merge.sh +1176 -0
  129. package/evals/integration/test_mint_attestation.sh +373 -0
  130. package/evals/integration/test_phase_map_and_gate_claim.sh +365 -0
  131. package/evals/integration/test_publish_delivery.sh +269 -0
  132. package/evals/integration/test_reconcile_soundness.sh +528 -0
  133. package/evals/integration/test_resolvefirststep_security.sh +208 -0
  134. package/evals/integration/test_session_resume_roundtrip.sh +286 -0
  135. package/evals/integration/test_trust_checkpoint.sh +325 -0
  136. package/evals/integration/test_trust_reconcile.sh +293 -0
  137. package/evals/integration/test_verify_cli.sh +208 -0
  138. package/evals/integration/test_workflow_sidecar_writer.sh +549 -34
  139. package/evals/lib/node.sh +0 -6
  140. package/evals/run.sh +47 -0
  141. package/evals/static/test_library_exports.sh +85 -0
  142. package/evals/static/test_universal_bundles.sh +15 -0
  143. package/evals/static/test_workflow_skills.sh +6 -13
  144. package/install.sh +0 -7
  145. package/integrations/strands-ts/README.md +25 -15
  146. package/integrations/veritas/flow-agents.adapter.json +1 -2
  147. package/kits/builder/flows/build.flow.json +59 -12
  148. package/kits/builder/kit.json +85 -15
  149. package/kits/builder/skills/continue-work/SKILL.md +116 -0
  150. package/kits/builder/skills/deliver/SKILL.md +36 -6
  151. package/kits/builder/skills/design-probe/SKILL.md +28 -0
  152. package/kits/builder/skills/execute-plan/SKILL.md +9 -1
  153. package/kits/builder/skills/gate-review/SKILL.md +234 -0
  154. package/kits/builder/skills/learning-review/SKILL.md +30 -0
  155. package/kits/builder/skills/pickup-probe/SKILL.md +29 -0
  156. package/kits/builder/skills/plan-work/SKILL.md +13 -1
  157. package/kits/builder/skills/pull-work/SKILL.md +19 -0
  158. package/kits/knowledge/adapters/default-store/index.js +38 -0
  159. package/kits/knowledge/adapters/flow-runner/index.js +1620 -0
  160. package/kits/knowledge/adapters/obsidian-store/index.js +36 -6
  161. package/kits/knowledge/docs/store-contract.md +314 -0
  162. package/kits/knowledge/evals/audit-freshness/suite.test.js +368 -0
  163. package/kits/knowledge/evals/canonicalize-category/suite.test.js +383 -0
  164. package/kits/knowledge/evals/contract-suite/suite.test.js +111 -0
  165. package/kits/knowledge/evals/detect-contradictions/suite.test.js +324 -0
  166. package/kits/knowledge/evals/entities/suite.test.js +40 -0
  167. package/kits/knowledge/evals/glossary-sync/suite.test.js +416 -0
  168. package/kits/knowledge/evals/hygiene-review/suite.test.js +396 -0
  169. package/kits/knowledge/evals/retirement/suite.test.js +145 -0
  170. package/kits/knowledge/flows/audit-freshness.flow.json +44 -0
  171. package/kits/knowledge/flows/canonicalize-category.flow.json +44 -0
  172. package/kits/knowledge/flows/detect-contradictions.flow.json +44 -0
  173. package/kits/knowledge/flows/glossary-sync.flow.json +61 -0
  174. package/kits/knowledge/flows/hygiene-review.flow.json +43 -0
  175. package/kits/knowledge/kit.json +51 -1
  176. package/package.json +13 -4
  177. package/packaging/conformance/README.md +10 -2
  178. package/packaging/conformance/fixtures/evidence-capture--allow-records-command.json +29 -0
  179. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +29 -0
  180. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +30 -0
  181. package/packaging/conformance/fixtures/stop-goal-fit--block-mode.json +23 -0
  182. package/packaging/conformance/fixtures/stop-goal-fit--off-mode.json +24 -0
  183. package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +5 -2
  184. package/packaging/conformance/fixtures/stop-goal-fit--warn-no-bundle.json +23 -0
  185. package/packaging/conformance/fixtures/workflow-steering--reground-active-prompt.json +30 -0
  186. package/packaging/conformance/fixtures/workflow-steering--reground-session-start.json +30 -0
  187. package/packaging/conformance/run-conformance.js +1 -1
  188. package/scripts/README.md +2 -1
  189. package/scripts/build-universal-bundles.js +0 -1
  190. package/scripts/ci/mint-attestation.js +221 -0
  191. package/scripts/ci/trust-reconcile.js +545 -0
  192. package/scripts/hooks/config-protection.js +423 -1
  193. package/scripts/hooks/evidence-capture.js +348 -0
  194. package/scripts/hooks/lib/liveness-read.js +113 -0
  195. package/scripts/hooks/run-hook.js +6 -1
  196. package/scripts/hooks/stop-goal-fit.js +1471 -79
  197. package/scripts/hooks/workflow-steering.js +135 -5
  198. package/scripts/install-codex-home.sh +39 -0
  199. package/scripts/install-merge.js +330 -0
  200. package/src/cli/init.ts +218 -20
  201. package/src/cli/validate-workflow-artifacts.ts +18 -2
  202. package/src/cli/verify.ts +100 -0
  203. package/src/cli/workflow-sidecar.ts +2093 -84
  204. package/src/cli.ts +2 -3
  205. package/src/index.ts +53 -0
  206. package/src/lib/flow-resolver.ts +284 -0
  207. package/src/tools/build-universal-bundles.ts +34 -21
  208. package/src/tools/generate-context-map.ts +3 -17
  209. package/src/tools/validate-source-tree.ts +44 -104
  210. package/tsconfig.json +1 -0
  211. package/build/src/tools/filter-installed-packs.js +0 -135
  212. package/packaging/packs.json +0 -49
  213. package/scripts/filter-installed-packs.js +0 -2
  214. package/src/tools/filter-installed-packs.ts +0 -132
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/src/cli.js CHANGED
@@ -14,18 +14,17 @@ import { main as veritasGovernance } from "./cli/veritas-governance.js";
14
14
  import { main as workflowArtifactCleanupAudit } from "./cli/workflow-artifact-cleanup-audit.js";
15
15
  import { main as buildBundles } from "./tools/build-universal-bundles.js";
16
16
  import { main as contextMap } from "./tools/generate-context-map.js";
17
- import { main as filterInstalledPacks } from "./tools/filter-installed-packs.js";
18
17
  import { main as validateSource } from "./tools/validate-source-tree.js";
19
18
  import { main as validatePackage } from "./tools/validate-package.js";
20
19
  import { main as validateHookInfluence } from "./cli/validate-hook-influence.js";
21
20
  import { main as runtimeAdapter } from "./cli/runtime-adapter.js";
22
21
  import { main as utteranceCheck } from "./cli/utterance-check.js";
22
+ import { main as verify } from "./cli/verify.js";
23
23
  const availableCommands = new Map([
24
24
  ["build-bundles", () => buildBundles()],
25
25
  ["console-learning-projection", consoleLearningProjection],
26
26
  ["context-map", contextMap],
27
27
  ["effective-backlog-settings", effectiveBacklogSettings],
28
- ["filter-installed-packs", filterInstalledPacks],
29
28
  ["fixture-retirement-audit", fixtureRetirementAudit],
30
29
  ["kit", kit],
31
30
  ["init", init],
@@ -39,6 +38,7 @@ const availableCommands = new Map([
39
38
  ["veritas-governance", veritasGovernance],
40
39
  ["validate-package", validatePackage],
41
40
  ["validate-hook-influence", validateHookInfluence],
41
+ ["verify", verify],
42
42
  ["validate-source", validateSource],
43
43
  ["workflow-artifact-cleanup-audit", workflowArtifactCleanupAudit],
44
44
  ]);
@@ -47,7 +47,6 @@ const aliases = new Map([
47
47
  ["flow-agents-console-learning-projection", "console-learning-projection"],
48
48
  ["flow-agents-context-map", "context-map"],
49
49
  ["flow-agents-effective-backlog-settings", "effective-backlog-settings"],
50
- ["flow-agents-filter-installed-packs", "filter-installed-packs"],
51
50
  ["flow-agents-fixture-retirement-audit", "fixture-retirement-audit"],
52
51
  ["flow-agents-kit", "kit"],
53
52
  ["flow-agents-promote-workflow-artifact", "promote-workflow-artifact"],
@@ -0,0 +1,81 @@
1
+ export type KitTargetConsumer = "flow" | "flow-agents" | string;
2
+ export interface KitConformanceLevel {
3
+ /** K0: valid core Flow Kit container with at least one flow (gates evaluable agentlessly). */
4
+ k0: boolean;
5
+ /** K1: K0 + at least one Flow Agents extension asset class present (skills/docs/adapters/evals/assets). */
6
+ k1: boolean;
7
+ /** K2: K1 + evals present (live evidence layer). */
8
+ k2: boolean;
9
+ }
10
+ /**
11
+ * Kit trust level — WHO vouches for a kit, orthogonal to the K-level capability axis.
12
+ *
13
+ * - "first-party": the kit is authored and published by Kontour (kontourai); its id is in the
14
+ * FIRST_PARTY_KIT_IDS allowlist maintained in this repository. These kits are built, tested,
15
+ * and distributed with the flow-agents package.
16
+ * - "verified": reserved for a future third-party verification process (e.g. self-certification
17
+ * via the conformance kit + cryptographic attestation / Veritas claims). Not yet implemented.
18
+ * - "unverified": default for all kits not in the first-party allowlist. This says nothing about
19
+ * the kit's quality — it only means Kontour has not vouched for it.
20
+ *
21
+ * The v2 path for "verified": cryptographic signing / attestation against the conformance kit
22
+ * and Veritas claims substrate is the natural next step and is intentionally deferred.
23
+ */
24
+ export type KitTrustLevel = "first-party" | "verified" | "unverified";
25
+ /**
26
+ * Allowlist of kit IDs that Kontour authors, tests, and ships with the flow-agents package.
27
+ *
28
+ * Criteria for inclusion:
29
+ * 1. The kit directory lives under kits/ in the kontourai/flow-agents repository.
30
+ * 2. The kit is published by @kontourai (npm package @kontourai/flow-agents).
31
+ * 3. Kontour owns and maintains the kit's content and release lifecycle.
32
+ *
33
+ * To add a new first-party kit: add its id here AND ensure it lives under kits/ in this repo.
34
+ * Third-party forks or community kits published elsewhere are NOT first-party, even if they
35
+ * share a similar id — first-party is tied to provenance in this specific repository.
36
+ */
37
+ export declare const FIRST_PARTY_KIT_IDS: ReadonlySet<string>;
38
+ /**
39
+ * Derive the trust level for a kit id.
40
+ *
41
+ * v1 determination: allowlist check against FIRST_PARTY_KIT_IDS.
42
+ * "verified" is reserved for future third-party verification (not yet granted to any kit).
43
+ */
44
+ export declare function deriveKitTrust(kitId: string): KitTrustLevel;
45
+ export interface KitTargetsResult {
46
+ kit_id: string;
47
+ kit_name: string;
48
+ conformance: KitConformanceLevel;
49
+ /** Derived consumer targets based on observable asset classes. */
50
+ targets: KitTargetConsumer[];
51
+ /** Extension field namespaces that are not Flow or Flow Agents-owned. */
52
+ third_party_extensions: string[];
53
+ /**
54
+ * Trust level: who vouches for this kit. Orthogonal to the K-level capability axis.
55
+ * "first-party" = Kontour-published; "verified" = reserved (future); "unverified" = default.
56
+ */
57
+ trust: KitTrustLevel;
58
+ }
59
+ /**
60
+ * Derives the consumer-target level (K0/K1/K2), target audience list, and trust level from
61
+ * observable asset classes in the kit manifest. Does not require file I/O.
62
+ *
63
+ * Derivation rules (from kontourai/flow-agents#52 and Brian's layering review):
64
+ * - K0: valid core container (schema_version, id, name, flows non-empty).
65
+ * - K1: K0 + any Flow Agents extension field present (skills/docs/adapters/evals/assets).
66
+ * - K2: K1 + evals present.
67
+ * - targets.flow: always present when K0 (any Flow consumer can evaluate gates).
68
+ * - targets.flow-agents: present when K1 (agent extension assets activate in >=1 harness).
69
+ * - third-party: any top-level keys that are not core fields and not Flow Agents extension classes.
70
+ *
71
+ * Trust derivation (from kontourai/flow-agents#79):
72
+ * - "first-party": kit id is in FIRST_PARTY_KIT_IDS (Kontour-authored kits in this repo).
73
+ * - "unverified": all other kits (default; "verified" is reserved for a future process).
74
+ *
75
+ * @param manifest The kit.json manifest object.
76
+ * @param kitDir Kit directory for flow file-existence checks. Defaults to "" (structural-only).
77
+ * Pass the real kit directory from `inspect` to get authoritative K0 validation.
78
+ */
79
+ export declare function deriveKitTargets(manifest: Record<string, unknown>, kitDir?: string): Promise<KitTargetsResult>;
80
+ export declare function validateKitRepository(kitDir: string): Promise<string[]>;
81
+ export declare function assertKitRepository(kitDir: string): Promise<Record<string, unknown>>;
@@ -0,0 +1,5 @@
1
+ export { validateTrustBundle, normalizeCheck, normalizeFinding, normalizeLearning, normalizeEvidenceRefs, validateEvidenceRef, validateLearningCorrection, loadJson, writeJson, appendJsonl, sidecarBase, writeState, statuses, phases, checkKinds, checkStatuses, verdicts, } from "./cli/workflow-sidecar.js";
2
+ /** Read a sidecar JSON file from a workflow artifact directory; returns `{}` if absent. */
3
+ export declare function readSidecar(dir: string, name: string): Record<string, any>;
4
+ /** Write a sidecar JSON file into a workflow artifact directory (pretty-printed, trailing newline). */
5
+ export declare function writeSidecar(dir: string, name: string, payload: Record<string, any>): void;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Public library surface for `@kontourai/flow-agents`.
3
+ *
4
+ * Native orchestration hosts can import the canonical workflow-sidecar
5
+ * writer/validator here instead of shelling out to the
6
+ * `flow-agents-workflow-sidecar` CLI or reimplementing validated
7
+ * read / merge / write of workflow evidence. This is the same code the CLI
8
+ * runs — importing it does not execute the CLI.
9
+ *
10
+ * The sidecar JSON Schemas ship under `schemas/` and can be validated against
11
+ * directly; the helpers below are the canonical writer/validator that produce
12
+ * and check conforming artifacts.
13
+ *
14
+ * @module
15
+ */
16
+ import * as path from "node:path";
17
+ import { loadJson as _loadJson, writeJson as _writeJson } from "./cli/workflow-sidecar.js";
18
+ export {
19
+ // Trust-bundle (Hachure) validation — the same validator the writer uses.
20
+ validateTrustBundle,
21
+ // Evidence / check / learning validation + normalization. These throw on
22
+ // invalid input (with the same messages the CLI surfaces) and return the
23
+ // normalized object on success.
24
+ normalizeCheck, normalizeFinding, normalizeLearning, normalizeEvidenceRefs, validateEvidenceRef, validateLearningCorrection,
25
+ // Sidecar read / merge / write primitives.
26
+ loadJson, writeJson, appendJsonl, sidecarBase, writeState,
27
+ // Schema vocabularies (the allowed status/phase/kind values).
28
+ statuses, phases, checkKinds, checkStatuses, verdicts, } from "./cli/workflow-sidecar.js";
29
+ /** Read a sidecar JSON file from a workflow artifact directory; returns `{}` if absent. */
30
+ export function readSidecar(dir, name) {
31
+ return _loadJson(path.join(dir, name));
32
+ }
33
+ /** Write a sidecar JSON file into a workflow artifact directory (pretty-printed, trailing newline). */
34
+ export function writeSidecar(dir, name, payload) {
35
+ _writeJson(path.join(dir, name), payload);
36
+ }
@@ -0,0 +1,8 @@
1
+ export type ParsedArgs = {
2
+ positionals: string[];
3
+ flags: Record<string, string | boolean | string[]>;
4
+ };
5
+ export declare function parseArgs(argv: string[]): ParsedArgs;
6
+ export declare function flagString(flags: ParsedArgs["flags"], key: string, fallback?: string): string | undefined;
7
+ export declare function flagBool(flags: ParsedArgs["flags"], key: string): boolean;
8
+ export declare function flagList(flags: ParsedArgs["flags"], key: string): string[];
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Flow-Definition resolver — ADR 0016 Abstraction A (Option B), Phase P-a.
3
+ *
4
+ * Shared resolver consumed by both the producer (workflow-sidecar.ts) and,
5
+ * later, the enforcer (stop-goal-fit.js via P-c). This is the SINGLE source
6
+ * of truth for (active_flow_id, active_step_id) → FlowDefinition gate
7
+ * expects[] resolution. Neither consumer duplicates this logic.
8
+ *
9
+ * Design:
10
+ * - Pure and synchronous — no async, no throws.
11
+ * - Returns null on ENOENT, parse error, or missing gate (fail-open).
12
+ * - Kit-agnostic: kitId = flowId.split(".")[0]; no hardcoded kit list.
13
+ * - Honors FLOW_AGENTS_FLOW_DEFS_DIR env-var override for custom installs.
14
+ */
15
+ /**
16
+ * Build and validate the FlowDefinition file path.
17
+ *
18
+ * Returns the validated absolute file path, or null when:
19
+ * - kitId or flowName contains chars outside SLUG_RE (rejects traversal)
20
+ * - FLOW_AGENTS_FLOW_DEFS_DIR resolves into a .flow-agents directory
21
+ * - The resolved path escapes the expected root (belt-and-suspenders)
22
+ *
23
+ * When the override is unsafe (points into .flow-agents), falls back silently
24
+ * to the repoRoot/kits/ path so the resolver still works for legit flows.
25
+ */
26
+ export declare function resolveFlowFilePath(kitId: string, flowName: string, flowId: string, repoRoot: string): string | null;
27
+ /** A single gate expectation from a FlowDefinition expects[] entry. */
28
+ export type GateExpectation = {
29
+ id: string;
30
+ kind: string;
31
+ required: boolean;
32
+ bundle_claim: {
33
+ claimType: string;
34
+ subjectType: string;
35
+ accepted_statuses: string[];
36
+ };
37
+ };
38
+ /** The resolved result from the active flow step. */
39
+ export type ActiveFlowStep = {
40
+ flowId: string;
41
+ stepId: string;
42
+ gateId: string;
43
+ gateExpects: GateExpectation[];
44
+ };
45
+ /**
46
+ * Resolve the gate expects[] for a specific (flowId, stepId) pair.
47
+ *
48
+ * @param flowId e.g. "builder.build" — kitId is extracted as the prefix before the first ".".
49
+ * @param stepId e.g. "verify" — matched against gate.step values in the FlowDefinition.
50
+ * @param repoRoot Absolute path to the repository root (kits/ lives here).
51
+ * Honored only when FLOW_AGENTS_FLOW_DEFS_DIR is not set.
52
+ * @returns ActiveFlowStep with the matched gate's expects[], or null on any error.
53
+ */
54
+ export declare function resolveFlowStep(flowId: string, stepId: string, repoRoot: string): ActiveFlowStep | null;
55
+ /**
56
+ * Resolve the phase→step mapping from a FlowDefinition's phase_map field.
57
+ *
58
+ * Returns the phase_map object (e.g. {"execution":"execute","planning":"plan",...})
59
+ * or null when the flow file cannot be loaded, the phase_map field is absent, or
60
+ * the field is not a plain Record<string,string>.
61
+ *
62
+ * Pure and synchronous — no throws, fail-open on any error.
63
+ *
64
+ * @param flowId e.g. "builder.build" — kitId is the prefix before the first ".".
65
+ * @param repoRoot Absolute path to the repository root (kits/ lives here).
66
+ * Honored only when FLOW_AGENTS_FLOW_DEFS_DIR is not set.
67
+ * @returns Record<string,string> phase→stepId map, or null on absence/error.
68
+ */
69
+ export declare function resolvePhaseMap(flowId: string, repoRoot: string): Record<string, string> | null;
70
+ /**
71
+ * Resolve the active flow step from current.json.
72
+ *
73
+ * Reads active_flow_id and active_step_id from <flowAgentsDir>/current.json.
74
+ * If both are present, delegates to resolveFlowStep. The repoRoot is derived by
75
+ * walking upward from flowAgentsDir to find the nearest ancestor containing kits/,
76
+ * with a fallback to process.cwd(). This handles temp dirs, CI workspaces, and
77
+ * subproject layouts without hardcoding the repo structure.
78
+ *
79
+ * @param flowAgentsDir Path to the .flow-agents directory (contains current.json).
80
+ * @returns ActiveFlowStep or null when fields are absent or resolution fails.
81
+ */
82
+ export declare function resolveActiveFlowStep(flowAgentsDir: string): ActiveFlowStep | null;
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Flow-Definition resolver — ADR 0016 Abstraction A (Option B), Phase P-a.
3
+ *
4
+ * Shared resolver consumed by both the producer (workflow-sidecar.ts) and,
5
+ * later, the enforcer (stop-goal-fit.js via P-c). This is the SINGLE source
6
+ * of truth for (active_flow_id, active_step_id) → FlowDefinition gate
7
+ * expects[] resolution. Neither consumer duplicates this logic.
8
+ *
9
+ * Design:
10
+ * - Pure and synchronous — no async, no throws.
11
+ * - Returns null on ENOENT, parse error, or missing gate (fail-open).
12
+ * - Kit-agnostic: kitId = flowId.split(".")[0]; no hardcoded kit list.
13
+ * - Honors FLOW_AGENTS_FLOW_DEFS_DIR env-var override for custom installs.
14
+ */
15
+ import * as fs from "node:fs";
16
+ import * as path from "node:path";
17
+ // ─── Security: Layer 1 traversal defense ─────────────────────────────────────
18
+ //
19
+ // Both kitId and flowName originate from agent-writable sources (active_flow_id
20
+ // in current.json, and FLOW_AGENTS_FLOW_DEFS_DIR set by the runtime). A crafted
21
+ // value like "builder.../../../.flow-agents/slug/fake-flow" produces:
22
+ // kitId = "builder"
23
+ // flowName = "../../../.flow-agents/slug/fake-flow"
24
+ // which resolves OUTSIDE kits/ via path.join traversal.
25
+ //
26
+ // SLUG_RE closes this: it rejects any value containing path separators, dots,
27
+ // or characters outside the safe identifier alphabet. Only [a-zA-Z0-9_-] is
28
+ // allowed, making traversal sequences impossible.
29
+ //
30
+ // Belt-and-suspenders: after building the path we also confirm the resolved
31
+ // absolute path stays inside the expected root directory.
32
+ /** Strict slug pattern — allows only URL-safe identifier chars. */
33
+ const SLUG_RE = /^[a-zA-Z0-9_-]+$/;
34
+ /**
35
+ * Returns true when the given resolved absolute path falls within a .flow-agents
36
+ * directory (an agent-writable area). Used to reject FLOW_AGENTS_FLOW_DEFS_DIR
37
+ * overrides that point into agent-controlled storage.
38
+ */
39
+ function isAgentWritableDir(resolvedDir) {
40
+ return resolvedDir.split(path.sep).includes(".flow-agents");
41
+ }
42
+ /**
43
+ * Build and validate the FlowDefinition file path.
44
+ *
45
+ * Returns the validated absolute file path, or null when:
46
+ * - kitId or flowName contains chars outside SLUG_RE (rejects traversal)
47
+ * - FLOW_AGENTS_FLOW_DEFS_DIR resolves into a .flow-agents directory
48
+ * - The resolved path escapes the expected root (belt-and-suspenders)
49
+ *
50
+ * When the override is unsafe (points into .flow-agents), falls back silently
51
+ * to the repoRoot/kits/ path so the resolver still works for legit flows.
52
+ */
53
+ export function resolveFlowFilePath(kitId, flowName, flowId, repoRoot) {
54
+ // Primary defense: reject any slug containing traversal chars or non-identifier chars.
55
+ if (!SLUG_RE.test(kitId) || !SLUG_RE.test(flowName))
56
+ return null;
57
+ const override = process.env["FLOW_AGENTS_FLOW_DEFS_DIR"];
58
+ let expectedRoot;
59
+ let flowFilePath;
60
+ if (override) {
61
+ const resolvedOverride = path.resolve(override);
62
+ if (isAgentWritableDir(resolvedOverride)) {
63
+ // Override targets an agent-writable .flow-agents path — reject it and
64
+ // fall back to the kit root. The session will resolve the real kit flow.
65
+ expectedRoot = path.resolve(repoRoot, "kits");
66
+ flowFilePath = path.join(repoRoot, "kits", kitId, "flows", `${flowName}.flow.json`);
67
+ }
68
+ else {
69
+ expectedRoot = resolvedOverride;
70
+ // flowId = kitId + "." + flowName; after slug validation this contains only
71
+ // [a-zA-Z0-9_-.] — no slashes, no traversal.
72
+ flowFilePath = path.join(resolvedOverride, `${flowId}.flow.json`);
73
+ }
74
+ }
75
+ else {
76
+ expectedRoot = path.resolve(repoRoot, "kits");
77
+ flowFilePath = path.join(repoRoot, "kits", kitId, "flows", `${flowName}.flow.json`);
78
+ }
79
+ // Belt-and-suspenders: confirm the resolved path stays within the expected root.
80
+ // After slug validation this is theoretically unreachable, but defense-in-depth
81
+ // verifies the invariant rather than merely asserting it.
82
+ const resolvedPath = path.resolve(flowFilePath);
83
+ if (!resolvedPath.startsWith(expectedRoot + path.sep) && resolvedPath !== expectedRoot) {
84
+ return null; // traversal still detected — paranoid fallback
85
+ }
86
+ return resolvedPath;
87
+ }
88
+ /**
89
+ * Resolve the gate expects[] for a specific (flowId, stepId) pair.
90
+ *
91
+ * @param flowId e.g. "builder.build" — kitId is extracted as the prefix before the first ".".
92
+ * @param stepId e.g. "verify" — matched against gate.step values in the FlowDefinition.
93
+ * @param repoRoot Absolute path to the repository root (kits/ lives here).
94
+ * Honored only when FLOW_AGENTS_FLOW_DEFS_DIR is not set.
95
+ * @returns ActiveFlowStep with the matched gate's expects[], or null on any error.
96
+ */
97
+ export function resolveFlowStep(flowId, stepId, repoRoot) {
98
+ if (!flowId || !stepId)
99
+ return null;
100
+ const dotIdx = flowId.indexOf(".");
101
+ if (dotIdx < 1)
102
+ return null; // flowId must have at least one "." to derive kitId
103
+ const kitId = flowId.slice(0, dotIdx);
104
+ // The flow filename is the part after the first "." (e.g. "build" from "builder.build")
105
+ const flowName = flowId.slice(dotIdx + 1);
106
+ if (!kitId || !flowName)
107
+ return null;
108
+ // Layer 1 defense: validate stepId too — it is matched against gate.step values but
109
+ // still originates from agent-writable current.json active_step_id.
110
+ if (!SLUG_RE.test(stepId))
111
+ return null;
112
+ // Determine the FlowDefinition file path with slug validation + path containment check.
113
+ // Returns null for traversal attempts (e.g. flowName = "../../../.flow-agents/fake").
114
+ const flowFilePath = resolveFlowFilePath(kitId, flowName, flowId, repoRoot);
115
+ if (!flowFilePath)
116
+ return null;
117
+ let flowDef;
118
+ try {
119
+ const raw = fs.readFileSync(flowFilePath, "utf8");
120
+ flowDef = JSON.parse(raw);
121
+ }
122
+ catch {
123
+ return null; // ENOENT, permission error, or parse error → fail-open
124
+ }
125
+ if (!flowDef || typeof flowDef !== "object" || !flowDef.gates)
126
+ return null;
127
+ // Find the gate whose .step matches stepId.
128
+ for (const [gateId, gate] of Object.entries(flowDef.gates)) {
129
+ if (!gate || gate.step !== stepId)
130
+ continue;
131
+ const expects = Array.isArray(gate.expects) ? gate.expects : [];
132
+ return { flowId, stepId, gateId, gateExpects: expects };
133
+ }
134
+ return null; // no gate matched the given stepId
135
+ }
136
+ /**
137
+ * Resolve the phase→step mapping from a FlowDefinition's phase_map field.
138
+ *
139
+ * Returns the phase_map object (e.g. {"execution":"execute","planning":"plan",...})
140
+ * or null when the flow file cannot be loaded, the phase_map field is absent, or
141
+ * the field is not a plain Record<string,string>.
142
+ *
143
+ * Pure and synchronous — no throws, fail-open on any error.
144
+ *
145
+ * @param flowId e.g. "builder.build" — kitId is the prefix before the first ".".
146
+ * @param repoRoot Absolute path to the repository root (kits/ lives here).
147
+ * Honored only when FLOW_AGENTS_FLOW_DEFS_DIR is not set.
148
+ * @returns Record<string,string> phase→stepId map, or null on absence/error.
149
+ */
150
+ export function resolvePhaseMap(flowId, repoRoot) {
151
+ if (!flowId)
152
+ return null;
153
+ const dotIdx = flowId.indexOf(".");
154
+ if (dotIdx < 1)
155
+ return null;
156
+ const kitId = flowId.slice(0, dotIdx);
157
+ const flowName = flowId.slice(dotIdx + 1);
158
+ if (!kitId || !flowName)
159
+ return null;
160
+ // Layer 1 defense: same slug validation + path containment as resolveFlowStep.
161
+ const flowFilePath = resolveFlowFilePath(kitId, flowName, flowId, repoRoot);
162
+ if (!flowFilePath)
163
+ return null;
164
+ let flowDef;
165
+ try {
166
+ const raw = fs.readFileSync(flowFilePath, "utf8");
167
+ flowDef = JSON.parse(raw);
168
+ }
169
+ catch {
170
+ return null; // ENOENT, permission error, or parse error → fail-open
171
+ }
172
+ if (!flowDef || typeof flowDef !== "object")
173
+ return null;
174
+ const pm = flowDef.phase_map;
175
+ if (!pm || typeof pm !== "object" || Array.isArray(pm))
176
+ return null;
177
+ // Validate: all values must be strings
178
+ for (const v of Object.values(pm)) {
179
+ if (typeof v !== "string")
180
+ return null;
181
+ }
182
+ return pm;
183
+ }
184
+ /**
185
+ * Find the repository root from a starting directory by walking upward to locate
186
+ * the nearest ancestor that contains a `kits/` subdirectory. If none is found,
187
+ * falls back to `process.cwd()` so the default "run from repo root" case still works.
188
+ *
189
+ * This is required because the .flow-agents directory can live anywhere (temp dirs,
190
+ * subprojects, CI workspaces) while the kits/ directory is always at the repo root.
191
+ */
192
+ function findRepoRoot(startDir) {
193
+ // Walk up from startDir looking for a kits/ directory
194
+ let dir = startDir;
195
+ for (let i = 0; i < 16; i++) {
196
+ if (fs.existsSync(path.join(dir, "kits")))
197
+ return dir;
198
+ const parent = path.dirname(dir);
199
+ if (parent === dir)
200
+ break; // reached filesystem root
201
+ dir = parent;
202
+ }
203
+ // Fallback: process.cwd() covers the common "run from repo root" case
204
+ return process.cwd();
205
+ }
206
+ /**
207
+ * Resolve the active flow step from current.json.
208
+ *
209
+ * Reads active_flow_id and active_step_id from <flowAgentsDir>/current.json.
210
+ * If both are present, delegates to resolveFlowStep. The repoRoot is derived by
211
+ * walking upward from flowAgentsDir to find the nearest ancestor containing kits/,
212
+ * with a fallback to process.cwd(). This handles temp dirs, CI workspaces, and
213
+ * subproject layouts without hardcoding the repo structure.
214
+ *
215
+ * @param flowAgentsDir Path to the .flow-agents directory (contains current.json).
216
+ * @returns ActiveFlowStep or null when fields are absent or resolution fails.
217
+ */
218
+ export function resolveActiveFlowStep(flowAgentsDir) {
219
+ if (!flowAgentsDir)
220
+ return null;
221
+ const currentFile = path.join(flowAgentsDir, "current.json");
222
+ let current;
223
+ try {
224
+ const raw = fs.readFileSync(currentFile, "utf8");
225
+ current = JSON.parse(raw);
226
+ }
227
+ catch {
228
+ return null;
229
+ }
230
+ const flowId = typeof current["active_flow_id"] === "string" ? current["active_flow_id"] : null;
231
+ const stepId = typeof current["active_step_id"] === "string" ? current["active_step_id"] : null;
232
+ if (!flowId || !stepId)
233
+ return null;
234
+ // Find repoRoot: walk up from flowAgentsDir to find kits/, fallback to cwd
235
+ const repoRoot = findRepoRoot(path.dirname(flowAgentsDir));
236
+ return resolveFlowStep(flowId, stepId, repoRoot);
237
+ }
@@ -0,0 +1,7 @@
1
+ export declare function readJson(file: string): unknown;
2
+ export declare function writeJson(file: string, value: unknown): void;
3
+ export declare function copyDir(src: string, dest: string): void;
4
+ export declare function assertPathContained(root: string, target: string): void;
5
+ export declare function walkFiles(root: string): string[];
6
+ export declare function relPath(root: string, file: string): string;
7
+ export declare function isoNow(): string;
@@ -0,0 +1,132 @@
1
+ export type WorkflowLearningStatus = "pending" | "learned" | "followup_required" | "blocked";
2
+ export type WorkflowLearningOutcome = "success" | "failure" | "mixed" | "unknown";
3
+ export type WorkflowLearningRouteTarget = "rule" | "skill" | "power" | "agent" | "eval" | "doc" | "backlog" | "knowledge" | "none";
4
+ export type WorkflowLearningRouteStatus = "open" | "completed" | "accepted" | "deferred" | "rejected";
5
+ export type WorkflowLearningCorrectionType = "workflow" | "skill" | "agent" | "tooling" | "test" | "doc" | "process" | "product" | "provider" | "none";
6
+ export type WorkflowLearningRoute = {
7
+ target: WorkflowLearningRouteTarget;
8
+ status: WorkflowLearningRouteStatus;
9
+ ref?: string;
10
+ };
11
+ export type WorkflowLearningCorrection = {
12
+ needed: boolean;
13
+ type?: WorkflowLearningCorrectionType;
14
+ recurrence_key?: string;
15
+ intended_behavior?: string;
16
+ observed_behavior?: string;
17
+ gap?: string;
18
+ evidence?: string;
19
+ no_change_rationale?: string;
20
+ prevention?: WorkflowLearningRoute;
21
+ };
22
+ export type WorkflowLearningRecord = {
23
+ id: string;
24
+ recorded_at: string;
25
+ source_refs: string[];
26
+ outcome: WorkflowLearningOutcome;
27
+ facts: string[];
28
+ interpretation: string;
29
+ routing: WorkflowLearningRoute[];
30
+ correction?: WorkflowLearningCorrection;
31
+ };
32
+ export type WorkflowLearningSidecar = {
33
+ schema_version: "1.0";
34
+ task_slug: string;
35
+ status: WorkflowLearningStatus;
36
+ updated_at: string;
37
+ records: WorkflowLearningRecord[];
38
+ };
39
+ export type WorkflowLearningSource = {
40
+ path: string;
41
+ relativePath: string;
42
+ slug: string;
43
+ learning: WorkflowLearningSidecar;
44
+ };
45
+ export type ConsoleProjectionRef = {
46
+ product: string;
47
+ kind: string;
48
+ id: string;
49
+ label?: string;
50
+ };
51
+ export type ConsoleProjectionScope = {
52
+ kind: string;
53
+ id: string;
54
+ };
55
+ export type ConsoleLearningProjection = {
56
+ id: string;
57
+ family: "workflow";
58
+ nonAuthority: true;
59
+ subjectRef: ConsoleProjectionRef;
60
+ sourceRef: ConsoleProjectionRef;
61
+ summary: string;
62
+ extensions: {
63
+ "flow-agents": {
64
+ task_slug: string;
65
+ record_id: string;
66
+ source_refs: string[];
67
+ routing: {
68
+ count: number;
69
+ open: number;
70
+ completed: number;
71
+ accepted: number;
72
+ deferred: number;
73
+ rejected: number;
74
+ targets: WorkflowLearningRouteTarget[];
75
+ statuses: WorkflowLearningRouteStatus[];
76
+ refs: string[];
77
+ };
78
+ correction: {
79
+ needed: boolean;
80
+ type?: WorkflowLearningCorrectionType;
81
+ recurrence_key?: string;
82
+ prevention?: {
83
+ target: WorkflowLearningRouteTarget;
84
+ status: WorkflowLearningRouteStatus;
85
+ ref?: string;
86
+ };
87
+ };
88
+ outcome: WorkflowLearningOutcome;
89
+ learning_status: WorkflowLearningStatus;
90
+ recorded_at: string;
91
+ updated_at: string;
92
+ source_path: string;
93
+ };
94
+ };
95
+ };
96
+ export type ConsoleLearningProjectionEnvelope = {
97
+ schema: "kontour.console.projection";
98
+ version: "0.1";
99
+ generatedAt: string;
100
+ scope: ConsoleProjectionScope;
101
+ producer: {
102
+ id: string;
103
+ product: "flow-agents";
104
+ };
105
+ derivedFrom: {
106
+ mode: "direct_snapshot";
107
+ eventHistory: "unavailable";
108
+ directSnapshot: {
109
+ id: string;
110
+ emittedAt: string;
111
+ producer: {
112
+ id: string;
113
+ product: "flow-agents";
114
+ };
115
+ reason: string;
116
+ sourceRef: ConsoleProjectionRef;
117
+ };
118
+ };
119
+ learnings: ConsoleLearningProjection[];
120
+ };
121
+ export type BuildWorkflowLearningProjectionOptions = {
122
+ scope: string | ConsoleProjectionScope;
123
+ generatedAt?: string;
124
+ producer?: {
125
+ id?: string;
126
+ product?: "flow-agents";
127
+ };
128
+ };
129
+ export declare function readWorkflowLearningSources(artifactRoot: string): WorkflowLearningSource[];
130
+ export declare function buildWorkflowLearningProjection(sources: WorkflowLearningSource[], options: BuildWorkflowLearningProjectionOptions): ConsoleLearningProjectionEnvelope;
131
+ export declare function validateWorkflowLearningProjectionSourceShape(value: unknown, label?: string): WorkflowLearningSidecar;
132
+ export declare const validateWorkflowLearningSidecar: typeof validateWorkflowLearningProjectionSourceShape;
@@ -0,0 +1,18 @@
1
+ export type KitAsset = {
2
+ kit_id: string;
3
+ kit_name: string;
4
+ asset_class: string;
5
+ asset_id: string | null;
6
+ relative_path: string;
7
+ source_path: string;
8
+ source_kind: string;
9
+ description?: string;
10
+ };
11
+ export type KitInventory = {
12
+ assets: KitAsset[];
13
+ warnings: string[];
14
+ errors: string[];
15
+ };
16
+ export declare function readKitInventory(sourceRoot: string, dest: string): KitInventory;
17
+ export declare function activateCodexLocal(sourceRoot: string, dest: string): Record<string, unknown>;
18
+ export declare function activateStrandsLocal(sourceRoot: string, dest: string): Record<string, unknown>;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(): number;