@kontourai/flow-agents 0.4.0 → 1.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.
- package/.github/workflows/kit-gates-demo.yml +171 -0
- package/CHANGELOG.md +35 -0
- package/CONTEXT.md +1 -1
- package/README.md +13 -2
- package/build/src/cli/flow-kit.js +41 -2
- package/build/src/flow-kit/validate.js +98 -0
- package/build/src/tools/validate-source-tree.js +2 -1
- package/context/scripts/hooks/config-protection.js +217 -15
- package/docs/fixture-ownership.md +1 -0
- package/docs/index.md +9 -1
- package/docs/kit-authoring-guide.md +126 -0
- package/docs/knowledge-kit.md +69 -0
- package/docs/vision.md +22 -0
- package/evals/fixtures/kit-conformance-levels/k0-flows-only/flows/review.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k0-flows-only/kit.json +13 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/docs/README.md +3 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/flows/build.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/kit.json +20 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/docs/README.md +3 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/eval-suites/contract-suite/suite.test.js +1 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/flows/synthesize.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/kit.json +27 -0
- package/evals/fixtures/kit-conformance-levels/third-party-extension/flows/review.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/third-party-extension/kit.json +19 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_hook_category_behaviors.sh +51 -0
- package/evals/integration/test_kit_conformance_levels.sh +209 -0
- package/evals/run.sh +2 -0
- package/kits/catalog.json +6 -0
- package/kits/knowledge/adapters/default-store/index.js +2 -2
- package/kits/knowledge/adapters/flow-runner/entity-extractor.js +194 -0
- package/kits/knowledge/adapters/flow-runner/index.js +349 -0
- package/kits/knowledge/adapters/obsidian-store/README.md +141 -0
- package/kits/knowledge/adapters/obsidian-store/demo.js +181 -0
- package/kits/knowledge/adapters/obsidian-store/index.js +868 -0
- package/kits/knowledge/adapters/shared/codec.js +325 -0
- package/kits/knowledge/docs/store-contract.md +72 -0
- package/kits/knowledge/evals/entities/demo-acme.js +125 -0
- package/kits/knowledge/evals/entities/suite.test.js +722 -0
- package/kits/knowledge/kit.json +10 -0
- package/kits/release-evidence/fixtures/claims/README.md +14 -0
- package/kits/release-evidence/fixtures/claims/fail-rejected-release.trust.json +22 -0
- package/kits/release-evidence/fixtures/claims/pass-trusted-release.trust.json +22 -0
- package/kits/release-evidence/flows/release-evidence.flow.json +38 -0
- package/kits/release-evidence/kit.json +13 -0
- package/package.json +1 -1
- package/packaging/conformance/fixtures/config-protection--allow-no-verify-in-string.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-git-no-verify.json +23 -0
- package/scripts/hooks/config-protection.js +217 -15
- package/src/cli/flow-kit.ts +40 -2
- package/src/flow-kit/validate.ts +127 -0
- package/src/tools/validate-source-tree.ts +2 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
name: Kit Gates Demo (Agentless)
|
|
2
|
+
|
|
3
|
+
# Proves that Flow Kit gates can be evaluated over claim files in CI with no
|
|
4
|
+
# agent in the loop. The workflow runs:
|
|
5
|
+
# flow validate-definition — validates the Flow Definition in the kit
|
|
6
|
+
# flow validate-kit — validates kit.json container (requires
|
|
7
|
+
# @kontourai/flow >= 0.1.21; merged in source
|
|
8
|
+
# PR #67 but not yet in the published package
|
|
9
|
+
# as of 0.1.20 — step is continue-on-error)
|
|
10
|
+
# flow start — creates a Flow run
|
|
11
|
+
# flow attach-evidence --trust-artifact — attaches a claim file to the gate
|
|
12
|
+
# flow evaluate --exit-code — evaluates gate; exits 1 when gate does not pass
|
|
13
|
+
#
|
|
14
|
+
# Two jobs exercise one passing and one failing claim fixture:
|
|
15
|
+
# pass-case: trusted claim → evaluate exits 0 → job succeeds
|
|
16
|
+
# fail-case: rejected claim → evaluate exits 1 → workflow asserts the
|
|
17
|
+
# failure is detected (non-zero exit) rather than silently ignored
|
|
18
|
+
#
|
|
19
|
+
# Triggered on: workflow_dispatch or PR touching kits/release-evidence/** or
|
|
20
|
+
# this workflow file.
|
|
21
|
+
|
|
22
|
+
on:
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
pull_request:
|
|
25
|
+
paths:
|
|
26
|
+
- "kits/release-evidence/**"
|
|
27
|
+
- ".github/workflows/kit-gates-demo.yml"
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
pass-case:
|
|
34
|
+
name: Gate pass — trusted release.evidence claim
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
timeout-minutes: 10
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout
|
|
40
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
41
|
+
|
|
42
|
+
- name: Set up Node.js
|
|
43
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
44
|
+
with:
|
|
45
|
+
node-version: "22"
|
|
46
|
+
|
|
47
|
+
- name: Install Flow CLI
|
|
48
|
+
# Mirrors the FLOW_CLI_ROOT install pattern used in ci.yml.
|
|
49
|
+
run: |
|
|
50
|
+
mkdir -p .flow-cli
|
|
51
|
+
cd .flow-cli
|
|
52
|
+
printf '{"name":"flow-cli-host","private":true}\n' > package.json
|
|
53
|
+
npm install --no-save @kontourai/flow
|
|
54
|
+
env:
|
|
55
|
+
FLOW_CLI_ROOT: ${{ github.workspace }}/.flow-cli/node_modules/@kontourai/flow
|
|
56
|
+
|
|
57
|
+
- name: Validate Flow Definition (available in published CLI)
|
|
58
|
+
run: |
|
|
59
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
60
|
+
validate-definition \
|
|
61
|
+
kits/release-evidence/flows/release-evidence.flow.json \
|
|
62
|
+
--json
|
|
63
|
+
|
|
64
|
+
- name: Validate kit container (requires @kontourai/flow >= next release after 0.1.20)
|
|
65
|
+
# validate-kit is merged in kontourai/flow source (PR #67) but not yet
|
|
66
|
+
# validate-kit ships in @kontourai/flow >= 0.2.0 (published 2026-06-12).
|
|
67
|
+
run: |
|
|
68
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
69
|
+
validate-kit kits/release-evidence
|
|
70
|
+
|
|
71
|
+
- name: Init Flow workspace and start run
|
|
72
|
+
run: |
|
|
73
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js init
|
|
74
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
75
|
+
start kits/release-evidence/flows/release-evidence.flow.json \
|
|
76
|
+
--run-id gate-pass-demo
|
|
77
|
+
|
|
78
|
+
- name: Attach trusted release.evidence claim
|
|
79
|
+
run: |
|
|
80
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
81
|
+
attach-evidence gate-pass-demo \
|
|
82
|
+
--gate release-evidence-gate \
|
|
83
|
+
--file kits/release-evidence/fixtures/claims/pass-trusted-release.trust.json \
|
|
84
|
+
--trust-artifact
|
|
85
|
+
|
|
86
|
+
- name: Evaluate gate — trusted claim must produce pass and exit 0
|
|
87
|
+
run: |
|
|
88
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
89
|
+
evaluate gate-pass-demo --exit-code
|
|
90
|
+
|
|
91
|
+
- name: Show run status
|
|
92
|
+
if: always()
|
|
93
|
+
run: |
|
|
94
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
95
|
+
status gate-pass-demo --format summary
|
|
96
|
+
|
|
97
|
+
fail-case:
|
|
98
|
+
name: Gate fail — rejected claim (failure MUST be detected)
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
timeout-minutes: 10
|
|
101
|
+
|
|
102
|
+
steps:
|
|
103
|
+
- name: Checkout
|
|
104
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
105
|
+
|
|
106
|
+
- name: Set up Node.js
|
|
107
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
108
|
+
with:
|
|
109
|
+
node-version: "22"
|
|
110
|
+
|
|
111
|
+
- name: Install Flow CLI
|
|
112
|
+
run: |
|
|
113
|
+
mkdir -p .flow-cli
|
|
114
|
+
cd .flow-cli
|
|
115
|
+
printf '{"name":"flow-cli-host","private":true}\n' > package.json
|
|
116
|
+
npm install --no-save @kontourai/flow
|
|
117
|
+
env:
|
|
118
|
+
FLOW_CLI_ROOT: ${{ github.workspace }}/.flow-cli/node_modules/@kontourai/flow
|
|
119
|
+
|
|
120
|
+
- name: Validate Flow Definition
|
|
121
|
+
run: |
|
|
122
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
123
|
+
validate-definition \
|
|
124
|
+
kits/release-evidence/flows/release-evidence.flow.json \
|
|
125
|
+
--json
|
|
126
|
+
|
|
127
|
+
- name: Validate kit container (requires @kontourai/flow >= next release after 0.1.20)
|
|
128
|
+
continue-on-error: true
|
|
129
|
+
run: |
|
|
130
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
131
|
+
validate-kit kits/release-evidence
|
|
132
|
+
|
|
133
|
+
- name: Init Flow workspace and start run
|
|
134
|
+
run: |
|
|
135
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js init
|
|
136
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
137
|
+
start kits/release-evidence/flows/release-evidence.flow.json \
|
|
138
|
+
--run-id gate-fail-demo
|
|
139
|
+
|
|
140
|
+
- name: Attach rejected release.evidence claim
|
|
141
|
+
run: |
|
|
142
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
143
|
+
attach-evidence gate-fail-demo \
|
|
144
|
+
--gate release-evidence-gate \
|
|
145
|
+
--file kits/release-evidence/fixtures/claims/fail-rejected-release.trust.json \
|
|
146
|
+
--trust-artifact
|
|
147
|
+
|
|
148
|
+
- name: Evaluate gate — rejected claim must produce non-pass and exit 1
|
|
149
|
+
# --exit-code makes the CLI exit 1 when the gate does not pass.
|
|
150
|
+
# continue-on-error: true captures the outcome without failing the job here.
|
|
151
|
+
id: evaluate_fail
|
|
152
|
+
continue-on-error: true
|
|
153
|
+
run: |
|
|
154
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
155
|
+
evaluate gate-fail-demo --exit-code
|
|
156
|
+
|
|
157
|
+
- name: Assert failure was detected (gate must NOT pass a rejected claim)
|
|
158
|
+
# If evaluate exited 0 (outcome == success), the gate incorrectly
|
|
159
|
+
# accepted a rejected claim — false negative in gate evaluation.
|
|
160
|
+
run: |
|
|
161
|
+
if [ "${{ steps.evaluate_fail.outcome }}" = "success" ]; then
|
|
162
|
+
echo "ERROR: gate passed a rejected claim — false negative in gate evaluation"
|
|
163
|
+
exit 1
|
|
164
|
+
fi
|
|
165
|
+
echo "PASS: gate correctly did not pass the rejected claim (outcome=${{ steps.evaluate_fail.outcome }})"
|
|
166
|
+
|
|
167
|
+
- name: Show run status
|
|
168
|
+
if: always()
|
|
169
|
+
run: |
|
|
170
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
171
|
+
status gate-fail-demo --format summary
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0](https://github.com/kontourai/flow-agents/compare/v0.4.0...v1.0.0) (2026-06-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* agentless Flow Kit gate evaluation proof (issue [#52](https://github.com/kontourai/flow-agents/issues/52) item 3) ([f7857ec](https://github.com/kontourai/flow-agents/commit/f7857ec8ba69d614b0c3c70548d724f7a97c164a))
|
|
9
|
+
* agentless Flow Kit gate evaluation proof (issue [#52](https://github.com/kontourai/flow-agents/issues/52) item 3) ([86c881f](https://github.com/kontourai/flow-agents/commit/86c881f579a08ab75787cd32e401f83b77952c39))
|
|
10
|
+
* config-protection blocks git hook-skip flags ([#41](https://github.com/kontourai/flow-agents/issues/41)) ([6d9e981](https://github.com/kontourai/flow-agents/commit/6d9e9810b3d4e60fe172ade340e61dbe4053d0c9))
|
|
11
|
+
* K-level conformance, degradation invariant, and consumer-target derivation ([#52](https://github.com/kontourai/flow-agents/issues/52) items 1+2) ([d5c332a](https://github.com/kontourai/flow-agents/commit/d5c332a3f4400eb29e9f8fd4e845ec34cf30ae0b))
|
|
12
|
+
* K-level conformance, degradation invariant, and consumer-target derivation (issue [#52](https://github.com/kontourai/flow-agents/issues/52) items 1+2) ([6ac62eb](https://github.com/kontourai/flow-agents/commit/6ac62eb4cec195baca3d039b398f29e45e5d62de))
|
|
13
|
+
* **knowledge-kit:** obsidian layout — insights at node root, sources nested; dimension frontmatter ([5d6489b](https://github.com/kontourai/flow-agents/commit/5d6489b6dc30eacc3a4d0c51487c1a7d3a004f00))
|
|
14
|
+
* **knowledge-kit:** Obsidian store adapter — file-is-the-record spike ([#43](https://github.com/kontourai/flow-agents/issues/43)) ([83d9ff4](https://github.com/kontourai/flow-agents/commit/83d9ff43c2e1d59ac8d3235ae7250fc43be47725))
|
|
15
|
+
* **knowledge-kit:** Obsidian store adapter spike — file-is-the-record RATIFIED ([#43](https://github.com/kontourai/flow-agents/issues/43)) ([467c8dc](https://github.com/kontourai/flow-agents/commit/467c8dc60180a5f6bf15b30a4f0e29e486803fb8))
|
|
16
|
+
* **knowledge-kit:** person entity cards with backlinks + gated resolution ([#48](https://github.com/kontourai/flow-agents/issues/48)) ([9456cef](https://github.com/kontourai/flow-agents/commit/9456cef3b55c639bd50a9aeaea675bef425ea0be))
|
|
17
|
+
* **knowledge-kit:** person entity cards with backlinks + gated resolution ([#48](https://github.com/kontourai/flow-agents/issues/48)) ([ac5ccb0](https://github.com/kontourai/flow-agents/commit/ac5ccb08e40bb6adadd35ff165a617be29e8d23a))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Fixes
|
|
21
|
+
|
|
22
|
+
* **entity-extractor:** parse trailing-period last-entry correctly ([76abd87](https://github.com/kontourai/flow-agents/commit/76abd87ce2e6bfa3650c3d20de7309d498a446f8))
|
|
23
|
+
* **entity-extractor:** parse trailing-period last-entry correctly ([#48](https://github.com/kontourai/flow-agents/issues/48)) ([ac64fc1](https://github.com/kontourai/flow-agents/commit/ac64fc1cb3151af9032948ac463337da3eeaf907))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
|
|
28
|
+
* elevate Flow Kits as the authorable-ecosystem pillar ([#45](https://github.com/kontourai/flow-agents/issues/45)) ([fa81820](https://github.com/kontourai/flow-agents/commit/fa8182089c4e3c404e3020c6d516be93353a897b))
|
|
29
|
+
* elevate Flow Kits as the authorable-ecosystem pillar ([#45](https://github.com/kontourai/flow-agents/issues/45)) ([7ffa44e](https://github.com/kontourai/flow-agents/commit/7ffa44ea8be799e709b41d4ac4220948e3819fb8))
|
|
30
|
+
* kit container/extension layering — container contract is Flow-owned ([#50](https://github.com/kontourai/flow-agents/issues/50)) ([33d6ec0](https://github.com/kontourai/flow-agents/commit/33d6ec0bf0fefd9095c19dc76b995ee7dd8079fb))
|
|
31
|
+
* kit container/extension layering — container contract is Flow-owned ([#50](https://github.com/kontourai/flow-agents/issues/50)) ([fd87366](https://github.com/kontourai/flow-agents/commit/fd873663d64e205e1a8c2b898b913a56d410591f))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Maintenance
|
|
35
|
+
|
|
36
|
+
* cut v1.0.0 ([5f88ac5](https://github.com/kontourai/flow-agents/commit/5f88ac51598fe3f13b16360572ff851b822013a2))
|
|
37
|
+
|
|
3
38
|
## [0.4.0](https://github.com/kontourai/flow-agents/compare/v0.3.0...v0.4.0) (2026-06-12)
|
|
4
39
|
|
|
5
40
|
|
package/CONTEXT.md
CHANGED
|
@@ -75,7 +75,7 @@ A reusable procedure Flow Agents can invoke to carry out part of a work mode. Sk
|
|
|
75
75
|
|
|
76
76
|
### Flow Kit
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Flow's distribution unit for portable workflow bundles. A Flow Kit has two layers: (1) the **container** (owned by Kontour Flow) — a `kit.json` manifest with `schema_version`, `id`, `name`, and a non-empty `flows` list, plus optional `description` and `product_name`; and (2) the **agent extension** (owned by Flow Agents) — optional `skills`, `docs`, `adapters`, `evals`, and `assets` fields that make it a Flow Agents Kit. The container contract permits unknown top-level fields so consumers can extend it without breaking core validation. A kit with only core container fields is a valid Flow Kit; adding Flow Agents extension fields makes it a Flow Agents Kit.
|
|
79
79
|
_Avoid_: Pack, plugin, marketplace package
|
|
80
80
|
|
|
81
81
|
### Flow Kit Repository
|
package/README.md
CHANGED
|
@@ -71,6 +71,8 @@ L2 means all four policy classes with blocking; L1 means steering and stop-goal-
|
|
|
71
71
|
|
|
72
72
|
## Install
|
|
73
73
|
|
|
74
|
+
Requires Node.js 22 or newer.
|
|
75
|
+
|
|
74
76
|
```bash
|
|
75
77
|
# guided install into your workspace (auto-detects runtime)
|
|
76
78
|
npx @kontourai/flow-agents init --dest /path/to/workspace
|
|
@@ -111,9 +113,15 @@ The [Workflow Usage Guide](docs/workflow-usage-guide.md) has example prompts and
|
|
|
111
113
|
|
|
112
114
|
## Flow Kits
|
|
113
115
|
|
|
114
|
-
A Flow Kit
|
|
116
|
+
A Flow Kit bundles a workflow AND its opinionated output shape into a single validated unit: a `kit.json` manifest (schema version 1.0), one or more Flow Definitions, and optional skills, docs, adapters, evals, and assets. Authoring a kit means deciding not just _what_ an agent does but _how the result is rendered_ — the same pipeline produces different representations depending on which store adapter is active. Kits are the extension model for Flow Agents: validated by the `flow-kit` CLI, installed through a single command, and activatable into any workspace that runs Flow Agents.
|
|
117
|
+
|
|
118
|
+
**Builder Kit** — ships with `builder.shape` (shape a problem into slices and fileable work items) and `builder.build` (pull ready work through design probing, planning, execution, verification, PR readiness, merge readiness, and learning). Installed automatically by `npx @kontourai/flow-agents init`.
|
|
119
|
+
|
|
120
|
+
**Knowledge Kit** — a Flow Kit for durable, gated knowledge storage. It ships a store contract with four record types (`raw`, `compiled`, `concept`, `snapshot`), five pipeline flows (`ingest`, `compile`, `synthesize`, `consolidate`, `retire`), and a mutation policy of propose→evidence-gate→apply/reject with supersede-not-delete. All mutations require provenance; nothing is silently overwritten or deleted. Ships with 198 tests.
|
|
115
121
|
|
|
116
|
-
|
|
122
|
+
The output-shape story is the core reason kits matter. The Knowledge Kit store contract is representation-neutral: two adapters ship today. The **default adapter** stores records as flat markdown files with YAML frontmatter and a JSON graph index. The **Obsidian adapter** renders the same workflow into the shape a human already thinks in — one canonical note per record, category→folder hierarchy, configurable frontmatter dimensions (e.g. territory/customer/initiative as filterable fields), living overview notes with sources nested below, and superseded records moved to an `archive/` folder rather than deleted. Same flows, same mutation gates, different rendering layer. (The Obsidian adapter is shipped; layout/dimensions refinements and person/entity card support are in development.)
|
|
123
|
+
|
|
124
|
+
The Knowledge Kit is also LIVE-proven: the default adapter passes the parameterized contract suite; keyless operation is validated via a Strands agent + local ollama acceptance harness; vector similarity clustering uses ollama embeddings (`nomic-embed-text`) with a pluggable detector interface.
|
|
117
125
|
|
|
118
126
|
Install a local kit:
|
|
119
127
|
|
|
@@ -123,6 +131,9 @@ npx @kontourai/flow-agents flow-kit install-local path/to/my-kit --dest /path/to
|
|
|
123
131
|
|
|
124
132
|
- [Kit Authoring Guide](docs/kit-authoring-guide.md) — build your own kit from scratch: directory layout, `kit.json`, a flow file, validation, install, and activation.
|
|
125
133
|
- [Flow Kit Repository Contract](docs/flow-kit-repository-contract.md) — the full validation rules, registry schema, and activation diagnostics.
|
|
134
|
+
- [Knowledge Kit docs](kits/knowledge/docs/README.md) — store contract, record types, mutation ops, similarity detectors, and the Obsidian adapter.
|
|
135
|
+
|
|
136
|
+
**Direction** (not shipped): domain kits that compose this substrate — a Sales Kit (territory/customer/initiative schema with side-effect adapters for CRM logging), a Research Kit (transcript capture→compile→recall). Distribution follows sequencing: authoring tooling and covetable reference kits first, then a registry, then a marketplace. No marketplace claims are shipped.
|
|
126
137
|
|
|
127
138
|
## Framework adapters
|
|
128
139
|
|
|
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { parseArgs, flagBool, flagString } from "../lib/args.js";
|
|
5
5
|
import { assertPathContained, copyDir, isoNow, readJson, walkFiles, writeJson } from "../lib/fs.js";
|
|
6
|
-
import { assertKitRepository } from "../flow-kit/validate.js";
|
|
6
|
+
import { assertKitRepository, deriveKitTargets } from "../flow-kit/validate.js";
|
|
7
7
|
import { activateCodexLocal, activateStrandsLocal } from "../runtime-adapters.js";
|
|
8
8
|
const REGISTRY_REL = path.join("kits", "local", "installed-kits.json");
|
|
9
9
|
const REPOSITORIES_REL = path.join("kits", "local", "repositories");
|
|
@@ -126,6 +126,43 @@ function activate(argv) {
|
|
|
126
126
|
console.log(JSON.stringify(result, null, 2));
|
|
127
127
|
return Array.isArray(result.errors) && result.errors.length ? 1 : 0;
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* inspect <kit-dir> [--json]
|
|
131
|
+
*
|
|
132
|
+
* Derives conformance level (K0/K1/K2) and consumer targets from a kit's
|
|
133
|
+
* observable asset classes. Exits 1 if the kit fails core container validation.
|
|
134
|
+
* Outputs stable JSON suitable for use by catalog tooling and CI.
|
|
135
|
+
*
|
|
136
|
+
* K-levels (issue #52):
|
|
137
|
+
* K0 valid core Flow Kit container — gates evaluable agentlessly by any Flow consumer.
|
|
138
|
+
* K1 K0 + Flow Agents extension assets present (skills/docs/adapters/evals/assets).
|
|
139
|
+
* K2 K1 + evals present (live evidence layer).
|
|
140
|
+
*
|
|
141
|
+
* Consumer targets derived from observable asset classes:
|
|
142
|
+
* flow always present at K0 (any Flow consumer: gates/definition-of-done)
|
|
143
|
+
* flow-agents present at K1+ (Flow Agents extension activated)
|
|
144
|
+
* <namespace> unknown top-level keys list verbatim as third-party consumer targets
|
|
145
|
+
*/
|
|
146
|
+
function inspect(argv) {
|
|
147
|
+
const args = parseArgs(argv);
|
|
148
|
+
const kitDir = path.resolve(args.positionals[0] ?? ".");
|
|
149
|
+
const manifestPath = path.join(kitDir, "kit.json");
|
|
150
|
+
if (!fs.existsSync(manifestPath)) {
|
|
151
|
+
console.error(`inspect: kit.json not found at ${manifestPath}`);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
let manifest;
|
|
155
|
+
try {
|
|
156
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
console.error(`inspect: invalid JSON in ${manifestPath}: ${err.message}`);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
const result = deriveKitTargets(manifest);
|
|
163
|
+
console.log(JSON.stringify(result, null, 2));
|
|
164
|
+
return result.conformance.k0 ? 0 : 1;
|
|
165
|
+
}
|
|
129
166
|
export function main(argv = process.argv.slice(2)) {
|
|
130
167
|
const [command, ...rest] = argv;
|
|
131
168
|
if (command === "install-local")
|
|
@@ -136,7 +173,9 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
136
173
|
return status(rest);
|
|
137
174
|
if (command === "activate")
|
|
138
175
|
return activate(rest);
|
|
139
|
-
|
|
176
|
+
if (command === "inspect")
|
|
177
|
+
return inspect(rest);
|
|
178
|
+
console.error("usage: flow-kit <install-local|list|status|activate|inspect> ...");
|
|
140
179
|
return 2;
|
|
141
180
|
}
|
|
142
181
|
if (import.meta.url === `file://${process.argv[1]}`)
|
|
@@ -2,6 +2,91 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { readJson } from "../lib/fs.js";
|
|
4
4
|
const ASSET_CLASSES = ["flows", "skills", "docs", "adapters", "evals", "assets"];
|
|
5
|
+
// Core container fields owned by kontourai/flow (flow-kit-container.schema.json).
|
|
6
|
+
// agent-extension fields are skills, docs, adapters, evals, assets.
|
|
7
|
+
const CORE_CONTAINER_FIELDS = new Set(["schema_version", "id", "name", "description", "product_name", "flows"]);
|
|
8
|
+
const AGENT_EXTENSION_CLASSES = new Set(["skills", "docs", "adapters", "evals", "assets"]);
|
|
9
|
+
/**
|
|
10
|
+
* Validates that the manifest satisfies the core Flow Kit container contract
|
|
11
|
+
* (as specified by kontourai/flow PR #67) with all agent-extension fields stripped.
|
|
12
|
+
* Returns a list of violation messages (empty = valid).
|
|
13
|
+
*
|
|
14
|
+
* The degradation invariant: every Flow Agents Kit MUST remain a valid core
|
|
15
|
+
* Flow Kit container when agent-extension fields are ignored.
|
|
16
|
+
*/
|
|
17
|
+
export function validateCoreContainer(manifest, label) {
|
|
18
|
+
const errors = [];
|
|
19
|
+
if (manifest.schema_version !== "1.0") {
|
|
20
|
+
errors.push(`${label}: .schema_version must be "1.0"`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof manifest.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(manifest.id)) {
|
|
23
|
+
errors.push(`${label}: .id must be a stable kebab-case string`);
|
|
24
|
+
}
|
|
25
|
+
if (typeof manifest.name !== "string" || !manifest.name.trim()) {
|
|
26
|
+
errors.push(`${label}: .name must be a non-empty string`);
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(manifest.flows) || manifest.flows.length === 0) {
|
|
29
|
+
errors.push(`${label}: .flows must be a non-empty list`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
manifest.flows.forEach((entry, index) => {
|
|
33
|
+
if (typeof entry !== "object" || entry === null) {
|
|
34
|
+
errors.push(`${label}: flows[${index}] must be an object`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const flow = entry;
|
|
38
|
+
if (typeof flow.id !== "string" || !flow.id) {
|
|
39
|
+
errors.push(`${label}: flows[${index}].id must be a string`);
|
|
40
|
+
}
|
|
41
|
+
if (typeof flow.path !== "string" || !flow.path) {
|
|
42
|
+
errors.push(`${label}: flows[${index}].path must be a string`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return errors;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Derives the consumer-target level (K0/K1/K2) and target audience list from
|
|
50
|
+
* observable asset classes in the kit manifest. Does not require file I/O.
|
|
51
|
+
*
|
|
52
|
+
* Derivation rules (from kontourai/flow-agents#52 and Brian's layering review):
|
|
53
|
+
* - K0: valid core container (schema_version, id, name, flows non-empty).
|
|
54
|
+
* - K1: K0 + any Flow Agents extension field present (skills/docs/adapters/evals/assets).
|
|
55
|
+
* - K2: K1 + evals present.
|
|
56
|
+
* - targets.flow: always present when K0 (any Flow consumer can evaluate gates).
|
|
57
|
+
* - targets.flow-agents: present when K1 (agent extension assets activate in >=1 harness).
|
|
58
|
+
* - third-party: any top-level keys that are not core fields and not Flow Agents extension classes.
|
|
59
|
+
*/
|
|
60
|
+
export function deriveKitTargets(manifest) {
|
|
61
|
+
const kitId = typeof manifest.id === "string" ? manifest.id : "<unknown>";
|
|
62
|
+
const kitName = typeof manifest.name === "string" ? manifest.name : "<unknown>";
|
|
63
|
+
const coreErrors = validateCoreContainer(manifest, "kit.json");
|
|
64
|
+
const k0 = coreErrors.length === 0;
|
|
65
|
+
const hasAgentExtension = AGENT_EXTENSION_CLASSES.size > 0 &&
|
|
66
|
+
[...AGENT_EXTENSION_CLASSES].some((cls) => Array.isArray(manifest[cls]) && manifest[cls].length > 0);
|
|
67
|
+
const hasEvals = Array.isArray(manifest["evals"]) && manifest["evals"].length > 0;
|
|
68
|
+
const k1 = k0 && hasAgentExtension;
|
|
69
|
+
const k2 = k1 && hasEvals;
|
|
70
|
+
// Detect third-party extension namespaces: top-level keys that are neither
|
|
71
|
+
// core fields nor Flow Agents extension classes.
|
|
72
|
+
const thirdPartyExtensions = Object.keys(manifest)
|
|
73
|
+
.filter((key) => !CORE_CONTAINER_FIELDS.has(key) && !AGENT_EXTENSION_CLASSES.has(key))
|
|
74
|
+
.sort();
|
|
75
|
+
const targets = [];
|
|
76
|
+
if (k0)
|
|
77
|
+
targets.push("flow");
|
|
78
|
+
if (k1)
|
|
79
|
+
targets.push("flow-agents");
|
|
80
|
+
for (const ns of thirdPartyExtensions)
|
|
81
|
+
targets.push(ns);
|
|
82
|
+
return {
|
|
83
|
+
kit_id: kitId,
|
|
84
|
+
kit_name: kitName,
|
|
85
|
+
conformance: { k0, k1, k2 },
|
|
86
|
+
targets,
|
|
87
|
+
third_party_extensions: thirdPartyExtensions,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
5
90
|
export function validateKitRepository(kitDir) {
|
|
6
91
|
const errors = [];
|
|
7
92
|
const manifestPath = path.join(kitDir, "kit.json");
|
|
@@ -20,6 +105,19 @@ export function validateKitRepository(kitDir) {
|
|
|
20
105
|
}
|
|
21
106
|
if (typeof manifest.name !== "string" || !manifest.name.trim())
|
|
22
107
|
errors.push(`${manifestPath}: .name must be a non-empty string`);
|
|
108
|
+
// Degradation invariant: every Flow Agents Kit must remain a valid core Flow Kit container
|
|
109
|
+
// when agent-extension fields are stripped. Strip extensions and re-validate core contract.
|
|
110
|
+
const coreManifest = {};
|
|
111
|
+
for (const key of Object.keys(manifest)) {
|
|
112
|
+
if (CORE_CONTAINER_FIELDS.has(key))
|
|
113
|
+
coreManifest[key] = manifest[key];
|
|
114
|
+
}
|
|
115
|
+
const coreErrors = validateCoreContainer(coreManifest, manifestPath);
|
|
116
|
+
for (const err of coreErrors) {
|
|
117
|
+
// Deduplicate: only add if not already covered by top-level checks above.
|
|
118
|
+
if (!errors.some((existing) => existing === err))
|
|
119
|
+
errors.push(err);
|
|
120
|
+
}
|
|
23
121
|
for (const section of ASSET_CLASSES) {
|
|
24
122
|
const entries = manifest[section];
|
|
25
123
|
if (entries === undefined)
|
|
@@ -85,6 +85,7 @@ const fixtureOwnerPolicies = new Map([
|
|
|
85
85
|
["evals/fixtures/builder-kit-workflow-state", { owners: ["evals/static/test_workflow_skills.sh"], classification: "Builder Kit workflow-state fixtures" }],
|
|
86
86
|
["evals/fixtures/console-learning-projection", { owners: ["evals/integration/test_console_learning_projection.sh"], classification: "console learning projection fixtures" }],
|
|
87
87
|
["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
|
|
88
|
+
["evals/fixtures/kit-conformance-levels", { owners: ["evals/integration/test_kit_conformance_levels.sh"], classification: "K-level conformance and consumer-target derivation fixtures" }],
|
|
88
89
|
["evals/fixtures/hook-influence", { owners: ["evals/integration/test_hook_influence_cases.sh", "evals/static/test_workflow_skills.sh", "scripts/validate-hook-influence-cases.js"], classification: "hook influence behavioral cases" }],
|
|
89
90
|
["evals/fixtures/pull-work-provider", { owners: ["evals/integration/test_pull_work_provider.sh"], classification: "work item provider normalization fixtures" }],
|
|
90
91
|
["evals/fixtures/pull-work-wip-shepherding", { owners: ["evals/static/test_workflow_skills.sh"], classification: "WIP shepherding state fixtures" }],
|
|
@@ -359,7 +360,7 @@ function validateKits(reporter) {
|
|
|
359
360
|
if (flowSchemaPath && fs.existsSync(flowSchemaPath))
|
|
360
361
|
console.log(fs.existsSync(localCli) ? `info: validating kit Flow Definitions with Flow CLI at ${localCli}` : `warning: Flow validator unavailable; source-tree check only verifies Flow Definition top-level shape`);
|
|
361
362
|
else
|
|
362
|
-
console.log("warning: Flow schema not configured; source-tree check only verifies Flow Definition top-level shape. Set FLOW_CLI_ROOT to enable Flow CLI validation.");
|
|
363
|
+
console.log("warning: Flow schema not configured; source-tree check only verifies Flow Definition top-level shape. Set FLOW_CLI_ROOT to enable Flow CLI validation. Container validation (kit.json core fields) will delegate to 'flow validate-kit' from @kontourai/flow when FLOW_CLI_ROOT is available.");
|
|
363
364
|
kits.forEach((entry, index) => {
|
|
364
365
|
const kitText = typeof entry === "string" ? entry : ["path", "directory", "dir", "id", "name"].map((key) => entry?.[key]).find((value) => typeof value === "string" && value);
|
|
365
366
|
if (!kitText) {
|