@kontourai/flow-agents 0.1.2 → 0.3.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 (117) hide show
  1. package/.github/dependabot.yml +23 -0
  2. package/.github/workflows/release-please.yml +31 -0
  3. package/.github/workflows/runtime-compat.yml +118 -0
  4. package/CHANGELOG.md +46 -0
  5. package/CONTRIBUTING.md +4 -0
  6. package/README.md +80 -18
  7. package/build/src/cli/flow-kit.js +9 -4
  8. package/build/src/cli/init.js +215 -5
  9. package/build/src/cli/runtime-adapter.js +9 -5
  10. package/build/src/cli/telemetry-doctor.js +4 -1
  11. package/build/src/cli/utterance-check.js +65 -1
  12. package/build/src/runtime-adapters.js +34 -0
  13. package/build/src/tools/build-universal-bundles.js +285 -0
  14. package/build/src/tools/filter-installed-packs.js +3 -0
  15. package/build/src/tools/validate-source-tree.js +5 -1
  16. package/console.telemetry.json +115 -20
  17. package/context/scripts/telemetry/lib/config.sh +5 -1
  18. package/context/settings/flow-agents-settings.json +7 -0
  19. package/docs/_layouts/default.html +2 -0
  20. package/docs/context-map.md +1 -0
  21. package/docs/index.md +53 -4
  22. package/docs/integrations/conformance.md +246 -0
  23. package/docs/integrations/framework-adapter.md +275 -0
  24. package/docs/integrations/harness-install.md +213 -0
  25. package/docs/integrations/index.md +58 -0
  26. package/docs/integrations/knowledge-kit-live.md +211 -0
  27. package/docs/kit-authoring-guide.md +169 -0
  28. package/docs/north-star.md +2 -2
  29. package/docs/spec/runtime-hook-surface.md +525 -0
  30. package/docs/survey-utterance-check.md +211 -94
  31. package/docs/vision.md +45 -0
  32. package/evals/acceptance/run.sh +13 -2
  33. package/evals/acceptance/test_knowledge_kit_live.sh +221 -0
  34. package/evals/acceptance/test_opencode_harness.sh +121 -0
  35. package/evals/acceptance/test_pi_harness.sh +113 -0
  36. package/evals/integration/test_bundle_install.sh +226 -1
  37. package/evals/integration/test_bundle_lifecycle.sh +641 -0
  38. package/evals/integration/test_runtime_adapter_activation.sh +113 -1
  39. package/evals/integration/test_utterance_check.sh +291 -44
  40. package/evals/run.sh +2 -0
  41. package/evals/static/test_universal_bundles.sh +137 -2
  42. package/integrations/strands/README.md +256 -0
  43. package/integrations/strands/example.py +74 -0
  44. package/integrations/strands/examples/knowledge_kit_live.py +461 -0
  45. package/integrations/strands/flow_agents_strands/__init__.py +27 -0
  46. package/integrations/strands/flow_agents_strands/hooks.py +194 -0
  47. package/integrations/strands/flow_agents_strands/policy.py +348 -0
  48. package/integrations/strands/flow_agents_strands/steering.py +225 -0
  49. package/integrations/strands/flow_agents_strands/telemetry.py +238 -0
  50. package/integrations/strands/pyproject.toml +38 -0
  51. package/integrations/strands/tests/__init__.py +0 -0
  52. package/integrations/strands/tests/test_hooks.py +392 -0
  53. package/integrations/strands/tests/test_policy.py +315 -0
  54. package/integrations/strands/tests/test_telemetry.py +184 -0
  55. package/integrations/strands-ts/README.md +224 -0
  56. package/integrations/strands-ts/bin/conformance-shim.mjs +257 -0
  57. package/integrations/strands-ts/package.json +53 -0
  58. package/integrations/strands-ts/src/hooks.ts +312 -0
  59. package/integrations/strands-ts/src/index.ts +22 -0
  60. package/integrations/strands-ts/src/policy.ts +345 -0
  61. package/integrations/strands-ts/src/telemetry.ts +251 -0
  62. package/integrations/strands-ts/test/test-policy.ts +322 -0
  63. package/integrations/strands-ts/test/test-steering.ts +159 -0
  64. package/integrations/strands-ts/test/test-telemetry.ts +226 -0
  65. package/integrations/strands-ts/tsconfig.json +20 -0
  66. package/kits/catalog.json +6 -0
  67. package/kits/knowledge/adapters/default-store/index.js +821 -0
  68. package/kits/knowledge/adapters/flow-runner/index.js +1179 -0
  69. package/kits/knowledge/adapters/flow-runner/telemetry.js +174 -0
  70. package/kits/knowledge/docs/README.md +135 -0
  71. package/kits/knowledge/docs/store-contract.md +526 -0
  72. package/kits/knowledge/evals/consolidation/suite.test.js +1234 -0
  73. package/kits/knowledge/evals/contract-suite/suite.test.js +670 -0
  74. package/kits/knowledge/evals/ingest-compile/suite.test.js +574 -0
  75. package/kits/knowledge/evals/synthesis/suite.test.js +909 -0
  76. package/kits/knowledge/flows/compile.flow.json +60 -0
  77. package/kits/knowledge/flows/consolidate.flow.json +77 -0
  78. package/kits/knowledge/flows/ingest.flow.json +60 -0
  79. package/kits/knowledge/flows/store-contract.flow.json +48 -0
  80. package/kits/knowledge/flows/synthesize.flow.json +77 -0
  81. package/kits/knowledge/kit.json +78 -0
  82. package/package.json +7 -2
  83. package/packaging/conformance/README.md +142 -0
  84. package/packaging/conformance/fixtures/config-protection--allow-no-path.json +18 -0
  85. package/packaging/conformance/fixtures/config-protection--allow-safe-file.json +20 -0
  86. package/packaging/conformance/fixtures/config-protection--block-biome.json +20 -0
  87. package/packaging/conformance/fixtures/config-protection--block-eslintrc.json +20 -0
  88. package/packaging/conformance/fixtures/quality-gate--allow-no-path.json +17 -0
  89. package/packaging/conformance/fixtures/quality-gate--allow-nonexistent-file.json +19 -0
  90. package/packaging/conformance/fixtures/stop-goal-fit--allow-clean-cwd.json +17 -0
  91. package/packaging/conformance/fixtures/stop-goal-fit--block-strict-mode.json +23 -0
  92. package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +21 -0
  93. package/packaging/conformance/fixtures/workflow-steering--allow-no-state.json +16 -0
  94. package/packaging/conformance/fixtures/workflow-steering--inject-active-state.json +29 -0
  95. package/packaging/conformance/fixtures/workflow-steering--inject-subagent-steering.json +25 -0
  96. package/packaging/conformance/package.json +4 -0
  97. package/packaging/conformance/run-conformance.js +322 -0
  98. package/packaging/manifest.json +59 -0
  99. package/schemas/flow-agents-settings.schema.json +48 -0
  100. package/scripts/README.md +4 -0
  101. package/scripts/dogfood.js +16 -0
  102. package/scripts/hooks/opencode-hook-adapter.js +123 -0
  103. package/scripts/hooks/opencode-telemetry-hook.js +101 -0
  104. package/scripts/hooks/pi-hook-adapter.js +123 -0
  105. package/scripts/hooks/pi-telemetry-hook.js +105 -0
  106. package/scripts/hooks/run-hook.js +8 -0
  107. package/scripts/hooks/utterance-check.js +124 -22
  108. package/scripts/telemetry/lib/config.sh +5 -1
  109. package/src/cli/flow-kit.ts +10 -4
  110. package/src/cli/init.ts +219 -6
  111. package/src/cli/runtime-adapter.ts +10 -5
  112. package/src/cli/telemetry-doctor.ts +4 -1
  113. package/src/cli/utterance-check.ts +71 -1
  114. package/src/runtime-adapters.ts +35 -0
  115. package/src/tools/build-universal-bundles.ts +283 -0
  116. package/src/tools/filter-installed-packs.ts +3 -0
  117. package/src/tools/validate-source-tree.ts +5 -1
@@ -0,0 +1,60 @@
1
+ {
2
+ "id": "knowledge.compile",
3
+ "version": "1.0",
4
+ "steps": [
5
+ { "id": "select-raws", "next": "compile" },
6
+ { "id": "compile", "next": "link" },
7
+ { "id": "link", "next": "done" },
8
+ { "id": "done", "next": null }
9
+ ],
10
+ "gates": {
11
+ "select-raws-gate": {
12
+ "step": "select-raws",
13
+ "expects": [
14
+ {
15
+ "id": "raw-ids-selected",
16
+ "kind": "surface.claim",
17
+ "required": true,
18
+ "description": "One or more raw record IDs have been selected for compilation. Each ID must resolve to an existing raw record in the store.",
19
+ "claim": {
20
+ "type": "knowledge.compile.selection",
21
+ "subject": "artifact",
22
+ "accepted_statuses": ["trusted", "accepted"]
23
+ }
24
+ }
25
+ ]
26
+ },
27
+ "compile-gate": {
28
+ "step": "compile",
29
+ "expects": [
30
+ {
31
+ "id": "compiled-record-provenance",
32
+ "kind": "surface.claim",
33
+ "required": true,
34
+ "description": "The compiled record has been created with provenance.source_ids listing every consumed raw ID, and source links (kind='source') from the compiled record to each raw record — satisfying the store contract's compiled record requirements.",
35
+ "claim": {
36
+ "type": "knowledge.compile.provenance",
37
+ "subject": "artifact",
38
+ "accepted_statuses": ["trusted", "accepted"]
39
+ }
40
+ }
41
+ ]
42
+ },
43
+ "link-gate": {
44
+ "step": "link",
45
+ "expects": [
46
+ {
47
+ "id": "provenance-refs-resolve",
48
+ "kind": "surface.claim",
49
+ "required": true,
50
+ "description": "Every provenance ref in the compiled record resolves to a retrievable raw record via the store's get() operation. The graph index reflects all source links.",
51
+ "claim": {
52
+ "type": "knowledge.compile.link-integrity",
53
+ "subject": "artifact",
54
+ "accepted_statuses": ["trusted", "accepted"]
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,77 @@
1
+ {
2
+ "id": "knowledge.consolidate",
3
+ "version": "1.0",
4
+ "steps": [
5
+ { "id": "related-event", "next": "propose" },
6
+ { "id": "propose", "next": "evidence-gate" },
7
+ { "id": "evidence-gate", "next": "apply-or-reject" },
8
+ { "id": "apply-or-reject", "next": "done" },
9
+ { "id": "done", "next": null }
10
+ ],
11
+ "gates": {
12
+ "related-event-gate": {
13
+ "step": "related-event",
14
+ "expects": [
15
+ {
16
+ "id": "related-compiled-records-found",
17
+ "kind": "surface.claim",
18
+ "required": true,
19
+ "description": "One or more compiled records linked to the snapshot's topic have been identified. The trigger fires when new compiled records matching the snapshot topic (by category or explicit topic tag) are present. The result is a cluster of compiled record IDs that form the consolidation input.",
20
+ "claim": {
21
+ "type": "knowledge.consolidate.trigger",
22
+ "subject": "artifact",
23
+ "accepted_statuses": ["trusted", "accepted"]
24
+ }
25
+ }
26
+ ]
27
+ },
28
+ "propose-gate": {
29
+ "step": "propose",
30
+ "expects": [
31
+ {
32
+ "id": "consolidation-proposal-recorded",
33
+ "kind": "surface.claim",
34
+ "required": true,
35
+ "description": "A consolidation proposal has been recorded. The proposal carries: the updated snapshot body (latest decisions), source_ids referencing every compiled record that contributed, and supersedes_ids referencing any prior snapshot(s) for the same topic. The snapshot record is NOT mutated at this step — only a consolidation proposal record and proposes link are created.",
36
+ "claim": {
37
+ "type": "knowledge.consolidate.proposal",
38
+ "subject": "artifact",
39
+ "accepted_statuses": ["trusted", "accepted"]
40
+ }
41
+ }
42
+ ]
43
+ },
44
+ "evidence-gate": {
45
+ "step": "evidence-gate",
46
+ "expects": [
47
+ {
48
+ "id": "proposal-carries-source-refs",
49
+ "kind": "surface.claim",
50
+ "required": true,
51
+ "description": "The consolidation proposal evidence includes source_ids referencing every compiled record that contributed to the proposed snapshot body. Gate rejects if source_ids is empty or any referenced record does not exist. Also verifies that the proposer record has a 'proposes' link to the snapshot target.",
52
+ "claim": {
53
+ "type": "knowledge.consolidate.evidence",
54
+ "subject": "artifact",
55
+ "accepted_statuses": ["trusted", "accepted"]
56
+ }
57
+ }
58
+ ]
59
+ },
60
+ "apply-gate": {
61
+ "step": "apply-or-reject",
62
+ "expects": [
63
+ {
64
+ "id": "consolidation-gate-decision",
65
+ "kind": "surface.claim",
66
+ "required": true,
67
+ "description": "A gate decision (apply or reject) has been recorded. If applied: snapshot body is updated via the store update op with the proposed body; supersede op links the new snapshot to any prior snapshots for the same topic; all superseded snapshots remain queryable with provenance intact. If rejected: the snapshot body is byte-identical to its pre-proposal state.",
68
+ "claim": {
69
+ "type": "knowledge.consolidate.gate-decision",
70
+ "subject": "artifact",
71
+ "accepted_statuses": ["trusted", "accepted"]
72
+ }
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "id": "knowledge.ingest",
3
+ "version": "1.0",
4
+ "steps": [
5
+ { "id": "capture", "next": "classify" },
6
+ { "id": "classify", "next": "route" },
7
+ { "id": "route", "next": "done" },
8
+ { "id": "done", "next": null }
9
+ ],
10
+ "gates": {
11
+ "capture-gate": {
12
+ "step": "capture",
13
+ "expects": [
14
+ {
15
+ "id": "raw-text-received",
16
+ "kind": "surface.claim",
17
+ "required": true,
18
+ "description": "Raw text and metadata have been received and are non-empty.",
19
+ "claim": {
20
+ "type": "knowledge.ingest.capture",
21
+ "subject": "artifact",
22
+ "accepted_statuses": ["trusted", "accepted"]
23
+ }
24
+ }
25
+ ]
26
+ },
27
+ "classify-gate": {
28
+ "step": "classify",
29
+ "expects": [
30
+ {
31
+ "id": "classification-recorded",
32
+ "kind": "surface.claim",
33
+ "required": true,
34
+ "description": "The raw record has been classified and stored with a non-empty category and type='raw', satisfying the store contract's create op provenance requirements.",
35
+ "claim": {
36
+ "type": "knowledge.ingest.classification",
37
+ "subject": "artifact",
38
+ "accepted_statuses": ["trusted", "accepted"]
39
+ }
40
+ }
41
+ ]
42
+ },
43
+ "route-gate": {
44
+ "step": "route",
45
+ "expects": [
46
+ {
47
+ "id": "routing-decision",
48
+ "kind": "surface.claim",
49
+ "required": true,
50
+ "description": "A routing decision has been recorded for the classified raw record (e.g. queue for compile, discard, or hold).",
51
+ "claim": {
52
+ "type": "knowledge.ingest.routing",
53
+ "subject": "decision",
54
+ "accepted_statuses": ["trusted", "accepted"]
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "id": "knowledge.store-contract",
3
+ "version": "1.0",
4
+ "steps": [
5
+ { "id": "verify-contract", "next": "done" },
6
+ { "id": "done", "next": null }
7
+ ],
8
+ "gates": {
9
+ "verify-contract-gate": {
10
+ "step": "verify-contract",
11
+ "expects": [
12
+ {
13
+ "id": "contract-suite-passed",
14
+ "kind": "surface.claim",
15
+ "required": true,
16
+ "description": "The contract test suite has passed 100% against the target adapter, verifying round-trip integrity, required-evidence enforcement, and graph-index consistency.",
17
+ "claim": {
18
+ "type": "knowledge.store-contract.suite-result",
19
+ "subject": "artifact",
20
+ "accepted_statuses": ["trusted", "accepted"]
21
+ }
22
+ },
23
+ {
24
+ "id": "mutation-provenance-enforced",
25
+ "kind": "surface.claim",
26
+ "required": true,
27
+ "description": "Every mutation operation (create, update, link, propose, apply, reject) rejects requests missing required provenance fields, as verified by the contract suite.",
28
+ "claim": {
29
+ "type": "knowledge.store-contract.provenance-enforcement",
30
+ "subject": "artifact",
31
+ "accepted_statuses": ["trusted", "accepted"]
32
+ }
33
+ },
34
+ {
35
+ "id": "round-trip-integrity",
36
+ "kind": "surface.claim",
37
+ "required": true,
38
+ "description": "Records round-trip raw to stored to queried with category and links intact, as verified by the contract suite.",
39
+ "claim": {
40
+ "type": "knowledge.store-contract.round-trip",
41
+ "subject": "artifact",
42
+ "accepted_statuses": ["trusted", "accepted"]
43
+ }
44
+ }
45
+ ]
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,77 @@
1
+ {
2
+ "id": "knowledge.synthesize",
3
+ "version": "1.0",
4
+ "steps": [
5
+ { "id": "detect-cluster", "next": "propose" },
6
+ { "id": "propose", "next": "evidence-gate" },
7
+ { "id": "evidence-gate", "next": "apply-or-reject" },
8
+ { "id": "apply-or-reject", "next": "done" },
9
+ { "id": "done", "next": null }
10
+ ],
11
+ "gates": {
12
+ "detect-cluster-gate": {
13
+ "step": "detect-cluster",
14
+ "expects": [
15
+ {
16
+ "id": "similar-sources-found",
17
+ "kind": "surface.claim",
18
+ "required": true,
19
+ "description": "At least one compiled record similar to the target concept has been identified via the similarity detector. Similarity v1 uses category match + link-overlap heuristic. The result is a cluster of source record IDs to synthesize from.",
20
+ "claim": {
21
+ "type": "knowledge.synthesize.cluster",
22
+ "subject": "artifact",
23
+ "accepted_statuses": ["trusted", "accepted"]
24
+ }
25
+ }
26
+ ]
27
+ },
28
+ "propose-gate": {
29
+ "step": "propose",
30
+ "expects": [
31
+ {
32
+ "id": "proposal-recorded",
33
+ "kind": "surface.claim",
34
+ "required": true,
35
+ "description": "A proposal carrying source refs (proposer_id + source_ids in evidence) has been recorded via the store propose op. The concept body is NOT modified at this step — only a proposes link and mutation log entry are created.",
36
+ "claim": {
37
+ "type": "knowledge.synthesize.proposal",
38
+ "subject": "artifact",
39
+ "accepted_statuses": ["trusted", "accepted"]
40
+ }
41
+ }
42
+ ]
43
+ },
44
+ "evidence-gate": {
45
+ "step": "evidence-gate",
46
+ "expects": [
47
+ {
48
+ "id": "proposal-carries-source-refs",
49
+ "kind": "surface.claim",
50
+ "required": true,
51
+ "description": "The proposal evidence includes source_ids referencing every compiled record that contributed to the proposed summary. Gate rejects if source_ids is empty or any referenced record does not exist.",
52
+ "claim": {
53
+ "type": "knowledge.synthesize.evidence",
54
+ "subject": "artifact",
55
+ "accepted_statuses": ["trusted", "accepted"]
56
+ }
57
+ }
58
+ ]
59
+ },
60
+ "apply-gate": {
61
+ "step": "apply-or-reject",
62
+ "expects": [
63
+ {
64
+ "id": "mutation-gate-decision",
65
+ "kind": "surface.claim",
66
+ "required": true,
67
+ "description": "A gate decision (apply or reject) has been recorded. If applied: concept body is updated via the store apply op with rationale and all contributing source_ids in provenance. If rejected: the store reject op is called, the concept body is byte-identical to its pre-proposal state, and only a rejection log entry is appended.",
68
+ "claim": {
69
+ "type": "knowledge.synthesize.gate-decision",
70
+ "subject": "artifact",
71
+ "accepted_statuses": ["trusted", "accepted"]
72
+ }
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "id": "knowledge",
4
+ "name": "Knowledge Kit",
5
+ "product_name": "Knowledge Kit",
6
+ "description": "A Flow Kit for durable, gated knowledge storage. Provides a store contract with defined record types, mutation operations, and provenance rules \u2014 plus a default adapter backed by markdown files, YAML frontmatter, wikilinks, and a graph index.",
7
+ "flows": [
8
+ {
9
+ "id": "knowledge.store-contract",
10
+ "path": "flows/store-contract.flow.json",
11
+ "description": "Gate that requires contract-suite evidence before a store implementation is accepted."
12
+ },
13
+ {
14
+ "id": "knowledge.ingest",
15
+ "path": "flows/ingest.flow.json",
16
+ "description": "Ingest raw captures: capture \u2192 classify \u2192 route. Gate on classify requires category + type='raw' classification evidence per the store contract create op."
17
+ },
18
+ {
19
+ "id": "knowledge.compile",
20
+ "path": "flows/compile.flow.json",
21
+ "description": "Compile classified raws into durable notes: select-raws \u2192 compile \u2192 link. Gate on compile requires provenance refs to every consumed raw record."
22
+ },
23
+ {
24
+ "id": "knowledge.synthesize",
25
+ "path": "flows/synthesize.flow.json",
26
+ "description": "Synthesize compiled sources into concept summaries: detect-cluster \u2192 propose \u2192 evidence-gate \u2192 apply-or-reject. Mutation-gate policy: no summary mutates without an approved proposal carrying source refs; rejection leaves store untouched."
27
+ },
28
+ {
29
+ "id": "knowledge.consolidate",
30
+ "path": "flows/consolidate.flow.json",
31
+ "description": "Consolidate related compiled records into a living decision snapshot: related-event trigger -> consolidation proposal -> evidence gate -> apply-or-reject. Creates or updates a snapshot record; supersedes prior snapshots via supersede op (never deletes). Reuses S3 mutation-gate machinery."
32
+ }
33
+ ],
34
+ "docs": [
35
+ {
36
+ "id": "knowledge.readme",
37
+ "path": "docs/README.md"
38
+ },
39
+ {
40
+ "id": "knowledge.store-contract-doc",
41
+ "path": "docs/store-contract.md"
42
+ }
43
+ ],
44
+ "adapters": [
45
+ {
46
+ "id": "knowledge.default-store",
47
+ "path": "adapters/default-store/index.js",
48
+ "description": "Default store adapter: markdown files + YAML frontmatter + wikilinks + JSON graph index."
49
+ },
50
+ {
51
+ "id": "knowledge.flow-runner",
52
+ "path": "adapters/flow-runner/index.js",
53
+ "description": "Executable flow logic: capture(rawText, meta) \u2192 classified raw record; compile(rawIds[]) \u2192 compiled record with provenance links; synthesize(conceptId | topicSelector, options) \u2192 concept summary proposal with mutation gate; consolidate(snapshotId | topicSelector, options) \u2192 decision snapshot consolidation with supersede-not-delete. Emits canonical telemetry events at gate points."
54
+ }
55
+ ],
56
+ "evals": [
57
+ {
58
+ "id": "knowledge.contract-suite",
59
+ "path": "evals/contract-suite/suite.test.js",
60
+ "description": "Parameterized contract test suite for any Knowledge Kit store adapter."
61
+ },
62
+ {
63
+ "id": "knowledge.ingest-compile-suite",
64
+ "path": "evals/ingest-compile/suite.test.js",
65
+ "description": "Eval cases for ingest (AC-mapped), compile provenance gate, telemetry emission, and ref resolution."
66
+ },
67
+ {
68
+ "id": "knowledge.synthesis-suite",
69
+ "path": "evals/synthesis/suite.test.js",
70
+ "description": "Eval cases for synthesize: AC1 (similar source \u2192 proposal not mutation), AC2 (rejection leaves concept byte-identical), AC3 (apply updates with provenance to all contributing sources), pluggable-similarity interface test, gate telemetry."
71
+ },
72
+ {
73
+ "id": "knowledge.consolidation-suite",
74
+ "path": "evals/consolidation/suite.test.js",
75
+ "description": "Eval cases for consolidate: AC1 (related event -> proposal, not mutation); AC2 (apply updates exactly ONE snapshot, links supersedes refs, superseded sources still queryable); AC3 fixture (decision changed across 3 events -> snapshot reflects ONLY latest decision, provenance to all 3); supersede-not-delete invariant; gate telemetry; contract suite extensions for snapshot semantics."
76
+ }
77
+ ]
78
+ }
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "@kontourai/flow-agents",
3
- "version": "0.1.2",
4
- "description": "Flow Agents — a Kontour product that applies Flow and Veritas discipline inside the agent tools you already use: Claude Code, Codex, Kiro, and GitHub Actions.",
3
+ "version": "0.3.0",
4
+ "description": "Flow Agents — a Kontour product that applies Flow and Veritas discipline as a portable process layer inside the agent tools you already use: Claude Code, Codex, Kiro, opencode, pi, and GitHub Actions — with framework adapters (AWS Strands preview) on the same policy-engine contract.",
5
5
  "keywords": [
6
6
  "agents",
7
7
  "ai-agents",
8
8
  "workflow",
9
9
  "skills",
10
+ "hooks",
10
11
  "claude-code",
11
12
  "codex",
12
13
  "kiro",
14
+ "opencode",
15
+ "pi",
16
+ "strands",
13
17
  "evidence",
14
18
  "process-transparency"
15
19
  ],
@@ -108,6 +112,7 @@
108
112
  "validate:hook-influence": "npm run build --silent && node build/src/cli.js validate-hook-influence",
109
113
  "workflow-artifact-cleanup-audit": "npm run build --silent && node build/src/cli.js workflow-artifact-cleanup-audit",
110
114
  "fixture:retirement-audit": "npm run build --silent && node build/src/cli.js fixture-retirement-audit",
115
+ "dogfood": "npm run build --silent && node scripts/dogfood.js",
111
116
  "setup:repo-hooks": "bash scripts/setup-repo-hooks.sh",
112
117
  "validate:repo-hooks": "bash evals/static/test_repo_hooks.sh",
113
118
  "eval": "bash evals/run.sh",
@@ -0,0 +1,142 @@
1
+ # Flow Agents Conformance Kit
2
+
3
+ The conformance kit lets third-party adapter authors self-certify their implementation against the Flow Agents policy engine contract.
4
+
5
+ ---
6
+
7
+ ## What is the conformance kit?
8
+
9
+ Flow Agents ships four canonical policy classes (config-protection, quality-gate, stop-goal-fit, workflow-steering) that adapters must invoke via subprocess or native import. The conformance kit provides:
10
+
11
+ 1. **Golden fixtures** (`fixtures/`) — payload→expected-decision JSON pairs, one per policy class × canonical event × case, extracted from real engine behavior.
12
+ 2. **Conformance runner** (`run-conformance.js`) — a standalone Node.js script (no npm deps) that pipes each fixture through an adapter command and reports per-level verdict.
13
+
14
+ ---
15
+
16
+ ## Conformance levels
17
+
18
+ | Level | What is required |
19
+ |-------|-----------------|
20
+ | **L0** | No policy fixtures required. Adapter wires telemetry only. |
21
+ | **L1** | Workflow steering (`userPromptSubmit`) and stop-goal-fit (`stop`) in warning mode must pass. |
22
+ | **L2** | All L1 requirements plus config-protection (`preToolUse`, blocking) and quality-gate (`postToolUse`, non-blocking). |
23
+
24
+ These levels match the definitions in `docs/spec/runtime-hook-surface.md` §4.
25
+
26
+ ---
27
+
28
+ ## Quick start
29
+
30
+ ```bash
31
+ # Self-test the canonical engine (must report L2):
32
+ node packaging/conformance/run-conformance.js --self
33
+
34
+ # Test a third-party adapter:
35
+ node packaging/conformance/run-conformance.js \
36
+ --adapter-cmd "node /path/to/your-adapter.js" \
37
+ --level L2
38
+
39
+ # Test at L1 only:
40
+ node packaging/conformance/run-conformance.js \
41
+ --adapter-cmd "node /path/to/your-adapter.js" \
42
+ --level L1
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Adapter contract
48
+
49
+ Your adapter command:
50
+ - Receives a canonical JSON payload on **stdin** (one JSON object, see §Payload schema).
51
+ - Writes the input JSON (or augmented form) to **stdout** on allow.
52
+ - Writes nothing meaningful to stdout on block (or empty/echoed input).
53
+ - Exits **0** to allow, **2** to block, any other code for error (treated as allow / fail-open).
54
+
55
+ The runner invokes your command exactly once per fixture via `sh -c "<your-cmd>"`.
56
+
57
+ ---
58
+
59
+ ## Payload schema (contract_version "1.0")
60
+
61
+ All payloads are JSON objects with:
62
+
63
+ | Field | Type | Description |
64
+ |-------|------|-------------|
65
+ | `hook_event_name` | string | Canonical event name: `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop` |
66
+ | `tool_name` | string? | Tool name (for tool-call events) |
67
+ | `tool_input` | object? | Tool input (for tool-call events); contains `path` or `file_path` for write tools |
68
+ | `cwd` | string? | Current working directory of the agent session |
69
+
70
+ Full payload/decision schema is documented in `docs/spec/runtime-hook-surface.md` §8 (Engine Contract).
71
+
72
+ ---
73
+
74
+ ## Fixture inventory
75
+
76
+ | Fixture | Policy class | Event | Level | What it tests |
77
+ |---------|-------------|-------|-------|---------------|
78
+ | `config-protection--block-eslintrc.json` | config-protection | preToolUse | L2 | Block write to `.eslintrc.json` |
79
+ | `config-protection--block-biome.json` | config-protection | preToolUse | L2 | Block edit to `biome.json` via `file_path` |
80
+ | `config-protection--allow-safe-file.json` | config-protection | preToolUse | L2 | Allow write to `src/main.ts` |
81
+ | `config-protection--allow-no-path.json` | config-protection | preToolUse | L2 | Allow when no path in tool_input |
82
+ | `quality-gate--allow-nonexistent-file.json` | quality-gate | postToolUse | L2 | Non-blocking for missing .ts file |
83
+ | `quality-gate--allow-no-path.json` | quality-gate | postToolUse | L2 | Non-blocking when no path in tool_input |
84
+ | `stop-goal-fit--allow-clean-cwd.json` | stop-goal-fit | stop | L1 | No warnings in clean workspace |
85
+ | `stop-goal-fit--warn-active-delivery.json` | stop-goal-fit | stop | L1 | Warnings for active delivery without DOD/GoalFit |
86
+ | `stop-goal-fit--block-strict-mode.json` | stop-goal-fit | stop | L2 | Exit 2 with FLOW_AGENTS_GOAL_FIT_STRICT=true |
87
+ | `workflow-steering--allow-no-state.json` | workflow-steering | userPromptSubmit | L1 | Pass-through when no active workflow state |
88
+ | `workflow-steering--inject-active-state.json` | workflow-steering | userPromptSubmit | L1 | Injects STATE hint for blocked task |
89
+ | `workflow-steering--inject-subagent-steering.json` | workflow-steering | postToolUse | L1 | Injects EXECUTION COMPLETE hint after tool-worker |
90
+
91
+ Fixtures with `workspace_setup` create a temporary directory with the listed files before invoking the adapter, and clean it up afterward. The `cwd` field in those payloads is replaced with the temp directory path at runtime.
92
+
93
+ ---
94
+
95
+ ## How to declare conformance
96
+
97
+ After running the conformance kit, include a conformance declaration in your adapter documentation:
98
+
99
+ ```yaml
100
+ conformance_level: L2 # or L0 / L1
101
+ engine_contract_version: "1.0"
102
+ runner_version: "run-conformance.js"
103
+ test_date: 2026-06-11
104
+ verdict: PASS
105
+ fixture_count: 12
106
+ fixtures_passed: 12
107
+ gaps: [] # List any declared gaps here
108
+ ```
109
+
110
+ If any fixtures fail, list them under `gaps` with a description of the degradation behavior.
111
+
112
+ ---
113
+
114
+ ## Declaring gaps
115
+
116
+ If your adapter legitimately cannot satisfy a fixture (e.g., the host runtime has no blocking `preToolUse` equivalent), declare it explicitly:
117
+
118
+ ```yaml
119
+ gaps:
120
+ - fixture: config-protection--block-eslintrc.json
121
+ reason: "Host does not support blocking tool calls; config-protection fails open"
122
+ degradation: "Agent may modify linter configs without interception"
123
+ workaround: "Run config-protection as a linting step in CI instead"
124
+ ```
125
+
126
+ Declared gaps do not prevent reaching a lower conformance level.
127
+
128
+ ---
129
+
130
+ ## CLI reference
131
+
132
+ ```
133
+ node packaging/conformance/run-conformance.js [options]
134
+
135
+ --self Run against the canonical engine (target L2)
136
+ --adapter-cmd CMD Shell command to pipe fixtures to (adapter under test)
137
+ --level L0|L1|L2 Minimum conformance level to enforce (default: L2 for --self, L0 for --adapter-cmd)
138
+ --fixtures DIR Override fixture directory (default: packaging/conformance/fixtures/)
139
+ --verbose Print fixture payloads and full output in per-fixture results
140
+ ```
141
+
142
+ Exit codes: `0` = target level reached, `1` = target level not reached, `2` = usage error.
@@ -0,0 +1,18 @@
1
+ {
2
+ "description": "config-protection allows when no file path is present in tool_input",
3
+ "policy_class": "config-protection",
4
+ "canonical_event": "preToolUse",
5
+ "conformance_level": "L2",
6
+ "hook_id": "config-protection",
7
+ "hook_script": "config-protection.js",
8
+ "payload": {
9
+ "hook_event_name": "PreToolUse",
10
+ "tool_name": "write",
11
+ "tool_input": {}
12
+ },
13
+ "expected": {
14
+ "exit_code": 0,
15
+ "stderr_is_empty": true,
16
+ "stdout_echoes_input": true
17
+ }
18
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "description": "config-protection allows a write to a regular source file",
3
+ "policy_class": "config-protection",
4
+ "canonical_event": "preToolUse",
5
+ "conformance_level": "L2",
6
+ "hook_id": "config-protection",
7
+ "hook_script": "config-protection.js",
8
+ "payload": {
9
+ "hook_event_name": "PreToolUse",
10
+ "tool_name": "write",
11
+ "tool_input": {
12
+ "path": "/repo/src/main.ts"
13
+ }
14
+ },
15
+ "expected": {
16
+ "exit_code": 0,
17
+ "stderr_is_empty": true,
18
+ "stdout_echoes_input": true
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "description": "config-protection blocks an edit to biome.json (protected Biome config)",
3
+ "policy_class": "config-protection",
4
+ "canonical_event": "preToolUse",
5
+ "conformance_level": "L2",
6
+ "hook_id": "config-protection",
7
+ "hook_script": "config-protection.js",
8
+ "payload": {
9
+ "hook_event_name": "PreToolUse",
10
+ "tool_name": "edit",
11
+ "tool_input": {
12
+ "file_path": "biome.json"
13
+ }
14
+ },
15
+ "expected": {
16
+ "exit_code": 2,
17
+ "stderr_contains": ["BLOCKED", "biome.json"],
18
+ "stdout_is_empty": true
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "description": "config-protection blocks a write to .eslintrc.json (protected ESLint config)",
3
+ "policy_class": "config-protection",
4
+ "canonical_event": "preToolUse",
5
+ "conformance_level": "L2",
6
+ "hook_id": "config-protection",
7
+ "hook_script": "config-protection.js",
8
+ "payload": {
9
+ "hook_event_name": "PreToolUse",
10
+ "tool_name": "write",
11
+ "tool_input": {
12
+ "path": "/repo/.eslintrc.json"
13
+ }
14
+ },
15
+ "expected": {
16
+ "exit_code": 2,
17
+ "stderr_contains": ["BLOCKED"],
18
+ "stdout_is_empty": true
19
+ }
20
+ }