@kontourai/flow-agents 0.4.0 → 1.0.1

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 (58) hide show
  1. package/.github/workflows/kit-gates-demo.yml +171 -0
  2. package/CHANGELOG.md +43 -0
  3. package/CONTEXT.md +1 -1
  4. package/README.md +13 -2
  5. package/build/src/cli/flow-kit.js +175 -6
  6. package/build/src/cli/validate-source-tree.js +19 -2
  7. package/build/src/flow-kit/validate.js +98 -0
  8. package/build/src/runtime-adapters.js +1 -1
  9. package/build/src/tools/validate-source-tree.js +3 -2
  10. package/context/scripts/hooks/config-protection.js +217 -15
  11. package/docs/fixture-ownership.md +2 -1
  12. package/docs/index.md +9 -1
  13. package/docs/kit-authoring-guide.md +126 -0
  14. package/docs/knowledge-kit.md +69 -0
  15. package/docs/vision.md +22 -0
  16. package/evals/fixtures/kit-conformance-levels/k0-flows-only/flows/review.flow.json +26 -0
  17. package/evals/fixtures/kit-conformance-levels/k0-flows-only/kit.json +13 -0
  18. package/evals/fixtures/kit-conformance-levels/k1-agent-extension/docs/README.md +3 -0
  19. package/evals/fixtures/kit-conformance-levels/k1-agent-extension/flows/build.flow.json +26 -0
  20. package/evals/fixtures/kit-conformance-levels/k1-agent-extension/kit.json +20 -0
  21. package/evals/fixtures/kit-conformance-levels/k2-with-evals/docs/README.md +3 -0
  22. package/evals/fixtures/kit-conformance-levels/k2-with-evals/eval-suites/contract-suite/suite.test.js +1 -0
  23. package/evals/fixtures/kit-conformance-levels/k2-with-evals/flows/synthesize.flow.json +26 -0
  24. package/evals/fixtures/kit-conformance-levels/k2-with-evals/kit.json +27 -0
  25. package/evals/fixtures/kit-conformance-levels/third-party-extension/flows/review.flow.json +26 -0
  26. package/evals/fixtures/kit-conformance-levels/third-party-extension/kit.json +19 -0
  27. package/evals/integration/test_activate_npx_context.sh +134 -0
  28. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  29. package/evals/integration/test_flow_kit_install_git.sh +163 -0
  30. package/evals/integration/test_hook_category_behaviors.sh +51 -0
  31. package/evals/integration/test_kit_conformance_levels.sh +209 -0
  32. package/evals/run.sh +2 -0
  33. package/kits/catalog.json +6 -0
  34. package/kits/knowledge/adapters/default-store/index.js +2 -2
  35. package/kits/knowledge/adapters/flow-runner/entity-extractor.js +194 -0
  36. package/kits/knowledge/adapters/flow-runner/index.js +349 -0
  37. package/kits/knowledge/adapters/obsidian-store/README.md +141 -0
  38. package/kits/knowledge/adapters/obsidian-store/demo.js +181 -0
  39. package/kits/knowledge/adapters/obsidian-store/index.js +868 -0
  40. package/kits/knowledge/adapters/shared/codec.js +325 -0
  41. package/kits/knowledge/docs/store-contract.md +72 -0
  42. package/kits/knowledge/evals/entities/demo-acme.js +125 -0
  43. package/kits/knowledge/evals/entities/suite.test.js +722 -0
  44. package/kits/knowledge/kit.json +10 -0
  45. package/kits/release-evidence/fixtures/claims/README.md +14 -0
  46. package/kits/release-evidence/fixtures/claims/fail-rejected-release.trust.json +22 -0
  47. package/kits/release-evidence/fixtures/claims/pass-trusted-release.trust.json +22 -0
  48. package/kits/release-evidence/flows/release-evidence.flow.json +38 -0
  49. package/kits/release-evidence/kit.json +13 -0
  50. package/package.json +1 -1
  51. package/packaging/conformance/fixtures/config-protection--allow-no-verify-in-string.json +20 -0
  52. package/packaging/conformance/fixtures/config-protection--block-git-no-verify.json +23 -0
  53. package/scripts/hooks/config-protection.js +217 -15
  54. package/src/cli/flow-kit.ts +162 -5
  55. package/src/cli/validate-source-tree.ts +7 -1
  56. package/src/flow-kit/validate.ts +127 -0
  57. package/src/runtime-adapters.ts +1 -1
  58. package/src/tools/validate-source-tree.ts +3 -2
@@ -4,6 +4,121 @@ import { readJson } from "../lib/fs.js";
4
4
 
5
5
  const ASSET_CLASSES = ["flows", "skills", "docs", "adapters", "evals", "assets"] as const;
6
6
 
7
+ // Core container fields owned by kontourai/flow (flow-kit-container.schema.json).
8
+ // agent-extension fields are skills, docs, adapters, evals, assets.
9
+ const CORE_CONTAINER_FIELDS = new Set(["schema_version", "id", "name", "description", "product_name", "flows"]);
10
+ const AGENT_EXTENSION_CLASSES = new Set(["skills", "docs", "adapters", "evals", "assets"]);
11
+
12
+ export type KitTargetConsumer =
13
+ | "flow"
14
+ | "flow-agents"
15
+ | string; // third-party extension namespaces listed verbatim
16
+
17
+ export interface KitConformanceLevel {
18
+ /** K0: valid core Flow Kit container with at least one flow (gates evaluable agentlessly). */
19
+ k0: boolean;
20
+ /** K1: K0 + at least one Flow Agents extension asset class present (skills/docs/adapters/evals/assets). */
21
+ k1: boolean;
22
+ /** K2: K1 + evals present (live evidence layer). */
23
+ k2: boolean;
24
+ }
25
+
26
+ export interface KitTargetsResult {
27
+ kit_id: string;
28
+ kit_name: string;
29
+ conformance: KitConformanceLevel;
30
+ /** Derived consumer targets based on observable asset classes. */
31
+ targets: KitTargetConsumer[];
32
+ /** Extension field namespaces that are not Flow or Flow Agents-owned. */
33
+ third_party_extensions: string[];
34
+ }
35
+
36
+ /**
37
+ * Validates that the manifest satisfies the core Flow Kit container contract
38
+ * (as specified by kontourai/flow PR #67) with all agent-extension fields stripped.
39
+ * Returns a list of violation messages (empty = valid).
40
+ *
41
+ * The degradation invariant: every Flow Agents Kit MUST remain a valid core
42
+ * Flow Kit container when agent-extension fields are ignored.
43
+ */
44
+ export function validateCoreContainer(manifest: Record<string, unknown>, label: string): string[] {
45
+ const errors: string[] = [];
46
+ if (manifest.schema_version !== "1.0") {
47
+ errors.push(`${label}: .schema_version must be "1.0"`);
48
+ }
49
+ if (typeof manifest.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(manifest.id)) {
50
+ errors.push(`${label}: .id must be a stable kebab-case string`);
51
+ }
52
+ if (typeof manifest.name !== "string" || !manifest.name.trim()) {
53
+ errors.push(`${label}: .name must be a non-empty string`);
54
+ }
55
+ if (!Array.isArray(manifest.flows) || manifest.flows.length === 0) {
56
+ errors.push(`${label}: .flows must be a non-empty list`);
57
+ } else {
58
+ manifest.flows.forEach((entry: unknown, index: number) => {
59
+ if (typeof entry !== "object" || entry === null) {
60
+ errors.push(`${label}: flows[${index}] must be an object`);
61
+ return;
62
+ }
63
+ const flow = entry as Record<string, unknown>;
64
+ if (typeof flow.id !== "string" || !flow.id) {
65
+ errors.push(`${label}: flows[${index}].id must be a string`);
66
+ }
67
+ if (typeof flow.path !== "string" || !flow.path) {
68
+ errors.push(`${label}: flows[${index}].path must be a string`);
69
+ }
70
+ });
71
+ }
72
+ return errors;
73
+ }
74
+
75
+ /**
76
+ * Derives the consumer-target level (K0/K1/K2) and target audience list from
77
+ * observable asset classes in the kit manifest. Does not require file I/O.
78
+ *
79
+ * Derivation rules (from kontourai/flow-agents#52 and Brian's layering review):
80
+ * - K0: valid core container (schema_version, id, name, flows non-empty).
81
+ * - K1: K0 + any Flow Agents extension field present (skills/docs/adapters/evals/assets).
82
+ * - K2: K1 + evals present.
83
+ * - targets.flow: always present when K0 (any Flow consumer can evaluate gates).
84
+ * - targets.flow-agents: present when K1 (agent extension assets activate in >=1 harness).
85
+ * - third-party: any top-level keys that are not core fields and not Flow Agents extension classes.
86
+ */
87
+ export function deriveKitTargets(manifest: Record<string, unknown>): KitTargetsResult {
88
+ const kitId = typeof manifest.id === "string" ? manifest.id : "<unknown>";
89
+ const kitName = typeof manifest.name === "string" ? manifest.name : "<unknown>";
90
+
91
+ const coreErrors = validateCoreContainer(manifest, "kit.json");
92
+ const k0 = coreErrors.length === 0;
93
+
94
+ const hasAgentExtension = AGENT_EXTENSION_CLASSES.size > 0 &&
95
+ [...AGENT_EXTENSION_CLASSES].some((cls) => Array.isArray(manifest[cls]) && (manifest[cls] as unknown[]).length > 0);
96
+
97
+ const hasEvals = Array.isArray(manifest["evals"]) && (manifest["evals"] as unknown[]).length > 0;
98
+
99
+ const k1 = k0 && hasAgentExtension;
100
+ const k2 = k1 && hasEvals;
101
+
102
+ // Detect third-party extension namespaces: top-level keys that are neither
103
+ // core fields nor Flow Agents extension classes.
104
+ const thirdPartyExtensions: string[] = Object.keys(manifest)
105
+ .filter((key) => !CORE_CONTAINER_FIELDS.has(key) && !AGENT_EXTENSION_CLASSES.has(key))
106
+ .sort();
107
+
108
+ const targets: KitTargetConsumer[] = [];
109
+ if (k0) targets.push("flow");
110
+ if (k1) targets.push("flow-agents");
111
+ for (const ns of thirdPartyExtensions) targets.push(ns);
112
+
113
+ return {
114
+ kit_id: kitId,
115
+ kit_name: kitName,
116
+ conformance: { k0, k1, k2 },
117
+ targets,
118
+ third_party_extensions: thirdPartyExtensions,
119
+ };
120
+ }
121
+
7
122
  export function validateKitRepository(kitDir: string): string[] {
8
123
  const errors: string[] = [];
9
124
  const manifestPath = path.join(kitDir, "kit.json");
@@ -20,6 +135,18 @@ export function validateKitRepository(kitDir: string): string[] {
20
135
  }
21
136
  if (typeof manifest.name !== "string" || !manifest.name.trim()) errors.push(`${manifestPath}: .name must be a non-empty string`);
22
137
 
138
+ // Degradation invariant: every Flow Agents Kit must remain a valid core Flow Kit container
139
+ // when agent-extension fields are stripped. Strip extensions and re-validate core contract.
140
+ const coreManifest: Record<string, unknown> = {};
141
+ for (const key of Object.keys(manifest)) {
142
+ if (CORE_CONTAINER_FIELDS.has(key)) coreManifest[key] = manifest[key];
143
+ }
144
+ const coreErrors = validateCoreContainer(coreManifest, manifestPath);
145
+ for (const err of coreErrors) {
146
+ // Deduplicate: only add if not already covered by top-level checks above.
147
+ if (!errors.some((existing) => existing === err)) errors.push(err);
148
+ }
149
+
23
150
  for (const section of ASSET_CLASSES) {
24
151
  const entries = manifest[section];
25
152
  if (entries === undefined) continue;
@@ -86,7 +86,7 @@ export function readKitInventory(sourceRoot: string, dest: string): KitInventory
86
86
  const errors: string[] = [];
87
87
  const assets: KitAsset[] = [];
88
88
  const catalogPath = path.join(sourceRoot, "kits", "catalog.json");
89
- if (!fs.existsSync(catalogPath)) errors.push(`${catalogPath}: missing Kit Catalog`);
89
+ if (!fs.existsSync(catalogPath)) warnings.push(`${catalogPath}: built-in Kit Catalog not found; skipping built-in kits (this is normal when running outside a flow-agents checkout)`);
90
90
  else {
91
91
  const catalog = readJson(catalogPath) as Record<string, unknown>;
92
92
  const kits = catalog.kits;
@@ -84,7 +84,8 @@ const fixtureOwnerPolicies = new Map<string, { owners: string[]; classification:
84
84
  ["evals/fixtures/backlog-provider-settings", { owners: ["evals/integration/test_effective_backlog_settings.sh"], classification: "settings precedence fixtures" }],
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
- ["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" }],
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/integration/test_activate_npx_context.sh", "evals/integration/test_flow_kit_install_git.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" }],
@@ -274,7 +275,7 @@ function validateKits(reporter: Reporter): void {
274
275
  if (!Array.isArray(kits)) return;
275
276
  const localCli = flowCliPath;
276
277
  if (flowSchemaPath && fs.existsSync(flowSchemaPath)) 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`);
277
- else 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.");
278
+ else 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.");
278
279
  kits.forEach((entry: any, index: number) => {
279
280
  const kitText = typeof entry === "string" ? entry : ["path", "directory", "dir", "id", "name"].map((key) => entry?.[key]).find((value) => typeof value === "string" && value);
280
281
  if (!kitText) { reporter.fail(`${rel(kitsCatalogPath)}: kits[${index}] missing path, directory, dir, id, or name`); return; }