@kontourai/flow-agents 2.3.0 → 3.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/CODEOWNERS +16 -0
- package/.github/workflows/ci.yml +44 -0
- package/.github/workflows/trust-reconcile.yml +62 -4
- package/AGENTS.md +4 -4
- package/CHANGELOG.md +62 -0
- package/CONTEXT.md +35 -0
- package/README.md +9 -3
- package/agents/dev.json +1 -1
- package/agents/tool-code-reviewer.json +1 -1
- package/agents/tool-planner.json +3 -3
- package/agents/tool-verifier.json +3 -3
- package/build/src/cli/assignment-provider.d.ts +1 -0
- package/build/src/cli/assignment-provider.js +748 -0
- package/build/src/cli/console-learning-projection.js +3 -2
- package/build/src/cli/effective-assignment-provider-settings.d.ts +1 -0
- package/build/src/cli/effective-assignment-provider-settings.js +125 -0
- package/build/src/cli/init.js +104 -21
- package/build/src/cli/kit.js +37 -6
- package/build/src/cli/pull-work-provider.js +1 -1
- package/build/src/cli/usage-feedback.js +3 -3
- package/build/src/cli/validate-hook-influence.js +1 -0
- package/build/src/cli/validate-workflow-artifacts.js +27 -7
- package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
- package/build/src/cli/workflow-sidecar.d.ts +14 -7
- package/build/src/cli/workflow-sidecar.js +938 -168
- package/build/src/cli.js +6 -0
- package/build/src/flow-kit/validate.d.ts +23 -0
- package/build/src/flow-kit/validate.js +67 -1
- package/build/src/index.d.ts +1 -1
- package/build/src/index.js +1 -1
- package/build/src/lib/flow-resolver.d.ts +6 -4
- package/build/src/lib/flow-resolver.js +125 -58
- package/build/src/lib/local-artifact-root.d.ts +14 -2
- package/build/src/lib/local-artifact-root.js +22 -5
- package/build/src/lib/workflow-learning-projection.js +2 -2
- package/build/src/runtime-adapters.d.ts +12 -0
- package/build/src/runtime-adapters.js +56 -16
- package/build/src/tools/build-universal-bundles.js +49 -16
- package/build/src/tools/generate-context-map.js +10 -8
- package/build/src/tools/validate-source-tree.js +24 -1
- package/console.telemetry.json +22 -0
- package/context/contracts/artifact-contract.md +40 -7
- package/context/contracts/assignment-provider-contract.md +239 -0
- package/context/contracts/builder-kit-workflow-state-contract.md +5 -2
- package/context/contracts/decision-registry-contract.md +265 -0
- package/context/contracts/delivery-contract.md +3 -1
- package/context/contracts/execution-contract.md +25 -0
- package/context/contracts/governance-adapter-contract.md +2 -0
- package/context/contracts/knowledge-store-contract.md +197 -0
- package/context/contracts/planning-contract.md +4 -1
- package/context/contracts/review-contract.md +7 -3
- package/context/contracts/sandbox-policy.md +2 -0
- package/context/contracts/standing-directives.md +13 -0
- package/context/contracts/verification-contract.md +11 -1
- package/context/contracts/work-item-contract.md +3 -1
- package/context/deferred/parallelization.md +1 -1
- package/context/gate-awareness.md +4 -4
- package/context/scripts/git-status.sh +2 -2
- package/context/scripts/hooks/config-protection.js +502 -2
- package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
- package/context/scripts/hooks/run-hook.js +14 -1
- package/context/scripts/hooks/stop-goal-fit.js +1140 -99
- package/context/scripts/hooks/workflow-steering.js +212 -6
- package/context/settings/assignment-provider-settings.json +33 -0
- package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
- package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
- package/docs/adr/0019-kit-dependency-ownership.md +62 -0
- package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
- package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +463 -0
- package/docs/agent-system-guidebook.md +3 -3
- package/docs/agent-usage-feedback-loop.md +6 -6
- package/docs/context-map.md +10 -7
- package/docs/decisions/decision-records.md +57 -0
- package/docs/decisions/index.md +17 -0
- package/docs/decisions/knowledge-store-provider.md +51 -0
- package/docs/decisions/model-routing.md +63 -0
- package/docs/decisions/promotion-gate.md +52 -0
- package/docs/decisions/standing-directives.md +66 -0
- package/docs/developer-architecture.md +1 -1
- package/docs/fixture-ownership.md +4 -0
- package/docs/flow-kit-repository-contract.md +5 -5
- package/docs/getting-started.md +12 -2
- package/docs/integrations/harness-install.md +2 -1
- package/docs/kit-authoring-guide.md +64 -5
- package/docs/knowledge-kit.md +5 -1
- package/docs/learnings/2026-07-improvement-program.md +203 -0
- package/docs/migrations.md +6 -5
- package/docs/north-star.md +2 -2
- package/docs/operating-layers.md +4 -3
- package/docs/repository-structure.md +5 -5
- package/docs/skills-map.md +8 -8
- package/docs/spec/runtime-hook-surface.md +19 -15
- package/docs/spikes/graph-provider-2026-07.md +155 -0
- package/docs/standards-register.md +7 -7
- package/docs/trust-anchor-adoption.md +105 -4
- package/docs/work-item-adapters.md +1 -1
- package/docs/workflow-artifact-lifecycle.md +72 -9
- package/docs/workflow-eval-strategy.md +3 -3
- package/docs/workflow-shared-contracts.md +3 -2
- package/docs/workflow-usage-guide.md +260 -21
- package/evals/acceptance/DEMO-false-completion.md +20 -11
- package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
- package/evals/acceptance/prove-capture-teeth.sh +11 -11
- package/evals/acceptance/test_kiro_harness.sh +1 -1
- package/evals/ci/antigaming-suite.sh +5 -0
- package/evals/ci/run-baseline.sh +71 -1
- package/evals/fixtures/assignment-provider/actor-a.json +6 -0
- package/evals/fixtures/assignment-provider/actor-b.json +6 -0
- package/evals/fixtures/assignment-provider/github-issue-claimed.json +27 -0
- package/evals/fixtures/assignment-provider/github-issue-unassigned.json +7 -0
- package/evals/fixtures/assignment-provider/liveness-fresh.json +9 -0
- package/evals/fixtures/assignment-provider/liveness-stale.json +9 -0
- package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
- package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
- package/evals/fixtures/hook-influence/cases.json +32 -0
- package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
- package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
- package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
- package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
- package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
- package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
- package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
- package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
- package/evals/integration/test_actor_identity.sh +254 -0
- package/evals/integration/test_assignment_provider_github.sh +318 -0
- package/evals/integration/test_assignment_provider_local_file.sh +222 -0
- package/evals/integration/test_builder_step_producers.sh +33 -23
- package/evals/integration/test_bundle_install.sh +22 -16
- package/evals/integration/test_bundle_lifecycle.sh +11 -11
- package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
- package/evals/integration/test_checkpoint_signing.sh +43 -0
- package/evals/integration/test_claim_lookup.sh +4 -4
- package/evals/integration/test_codex_hook_resolution.sh +114 -0
- package/evals/integration/test_command_log_concurrency.sh +8 -8
- package/evals/integration/test_command_log_fork_classification.sh +3 -3
- package/evals/integration/test_command_log_integrity.sh +16 -16
- package/evals/integration/test_console_learning_projection.sh +1 -1
- package/evals/integration/test_critique_supersession_roundtrip.sh +182 -0
- package/evals/integration/test_dual_emit_flow_step.sh +145 -0
- package/evals/integration/test_enforcer_expects_driven.sh +12 -12
- package/evals/integration/test_evidence_capture_hook.sh +14 -13
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_agents_statusline.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +52 -0
- package/evals/integration/test_flow_kit_repository.sh +2 -0
- package/evals/integration/test_flowdef_session_activation.sh +6 -6
- package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
- package/evals/integration/test_gate_bypass_chain.sh +52 -40
- package/evals/integration/test_gate_lockdown.sh +97 -73
- package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
- package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
- package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
- package/evals/integration/test_goal_fit_hook.sh +32 -32
- package/evals/integration/test_goal_fit_rederive.sh +9 -9
- package/evals/integration/test_hook_category_behaviors.sh +103 -0
- package/evals/integration/test_hook_influence_cases.sh +1 -0
- package/evals/integration/test_install_merge.sh +262 -1
- package/evals/integration/test_kit_identity_trust.sh +6 -6
- package/evals/integration/test_liveness_conflict_injection.sh +587 -0
- package/evals/integration/test_liveness_heartbeat.sh +677 -0
- package/evals/integration/test_liveness_verdict.sh +394 -0
- package/evals/integration/test_local_flow_kit_install.sh +15 -0
- package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
- package/evals/integration/test_promote_gate.sh +178 -0
- package/evals/integration/test_publish_delivery.sh +23 -6
- package/evals/integration/test_pull_work_assignment_join.sh +132 -0
- package/evals/integration/test_pull_work_liveness_preflight.sh +394 -0
- package/evals/integration/test_pull_work_provider.sh +2 -2
- package/evals/integration/test_reconcile_soundness.sh +37 -13
- package/evals/integration/test_resolvefirststep_security.sh +22 -0
- package/evals/integration/test_runtime_adapter_activation.sh +87 -18
- package/evals/integration/test_session_resume_roundtrip.sh +147 -0
- package/evals/integration/test_sidecar_field_preservation.sh +249 -0
- package/evals/integration/test_trust_reconcile.sh +11 -10
- package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
- package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +759 -0
- package/evals/integration/test_usage_feedback_global.sh +5 -5
- package/evals/integration/test_validate_artifacts_portability.sh +60 -0
- package/evals/integration/test_verify_cli.sh +2 -2
- package/evals/integration/test_veritas_governance_kit.sh +117 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
- package/evals/integration/test_workflow_artifacts.sh +33 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +802 -74
- package/evals/integration/test_workflow_steering_hook.sh +175 -5
- package/evals/lib/codex-provider.sh +1 -1
- package/evals/lib/node.sh +19 -1
- package/evals/run.sh +32 -0
- package/evals/static/test_decisions.sh +150 -0
- package/evals/static/test_flowdef_codeowners_coverage.sh +62 -0
- package/evals/static/test_knowledge_providers.sh +23 -0
- package/evals/static/test_package.sh +19 -10
- package/evals/static/test_universal_bundles.sh +48 -3
- package/evals/static/test_workflow_skills.sh +41 -8
- package/kits/builder/flows/build.flow.json +3 -74
- package/kits/builder/flows/publish-learn.flow.json +90 -0
- package/kits/builder/kit.json +11 -0
- package/kits/builder/skills/builder-shape/SKILL.md +1 -1
- package/kits/builder/skills/deliver/SKILL.md +32 -11
- package/kits/builder/skills/design-probe/SKILL.md +4 -4
- package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
- package/kits/builder/skills/execute-plan/SKILL.md +2 -2
- package/kits/builder/skills/fix-bug/SKILL.md +2 -0
- package/kits/builder/skills/gate-review/SKILL.md +6 -6
- package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
- package/kits/builder/skills/learning-review/SKILL.md +7 -7
- package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
- package/kits/builder/skills/plan-work/SKILL.md +27 -5
- package/kits/builder/skills/pull-work/SKILL.md +148 -5
- package/kits/builder/skills/release-readiness/SKILL.md +4 -4
- package/kits/builder/skills/review-work/SKILL.md +24 -1
- package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
- package/kits/builder/skills/verify-work/SKILL.md +8 -1
- package/kits/catalog.json +6 -0
- package/kits/knowledge/kit.json +35 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/CONTEXT.md +12 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/old-sprocket-shape.md +13 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/sprocket-shape.md +14 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/widget-format.md +14 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/learnings/fixture-learning.md +7 -0
- package/kits/knowledge/providers/conformance/fixtures/work-item/issues.json +30 -0
- package/kits/knowledge/providers/conformance/suite.test.js +125 -0
- package/kits/knowledge/providers/git-repo/index.js +236 -0
- package/kits/knowledge/providers/health/health-pass.test.js +99 -0
- package/kits/knowledge/providers/health/index.js +153 -0
- package/kits/knowledge/providers/index.js +24 -0
- package/kits/knowledge/providers/lib/model.js +91 -0
- package/kits/knowledge/providers/lib/schema-validate.js +119 -0
- package/kits/knowledge/providers/markdown-vault/index.js +169 -0
- package/kits/knowledge/providers/work-item/index.js +204 -0
- package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
- package/kits/veritas-governance/docs/README.md +75 -0
- package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
- package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
- package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
- package/kits/veritas-governance/kit.json +16 -0
- package/package.json +11 -5
- package/packaging/README.md +2 -2
- package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
- package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
- package/packaging/manifest.json +9 -10
- package/schemas/assignment-provider-settings.schema.json +125 -0
- package/schemas/decision-record.schema.json +109 -0
- package/schemas/knowledge/edge.schema.json +54 -0
- package/schemas/knowledge/health-report.schema.json +45 -0
- package/schemas/knowledge/node.schema.json +49 -0
- package/schemas/knowledge/proposal.schema.json +53 -0
- package/schemas/workflow-evidence.schema.json +1 -1
- package/schemas/workflow-state.schema.json +5 -0
- package/scripts/README.md +8 -3
- package/scripts/check-content-boundary.cjs +1 -1
- package/scripts/check-decisions.cjs +356 -0
- package/scripts/ci/derive-claim-status.mjs +74 -0
- package/scripts/ci/trust-reconcile.js +957 -103
- package/scripts/git-status.sh +2 -2
- package/scripts/hooks/claude-telemetry-hook.js +37 -3
- package/scripts/hooks/codex-telemetry-hook.js +36 -2
- package/scripts/hooks/config-protection.js +102 -30
- package/scripts/hooks/evidence-capture.js +3 -3
- package/scripts/hooks/lib/actor-identity.js +368 -0
- package/scripts/hooks/lib/config-protection-remedies.js +69 -0
- package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
- package/scripts/hooks/lib/liveness-policy.js +127 -0
- package/scripts/hooks/lib/liveness-read.js +105 -12
- package/scripts/hooks/lib/liveness-write.js +46 -0
- package/scripts/hooks/lib/local-artifact-paths.js +12 -6
- package/scripts/hooks/opencode-telemetry-hook.js +27 -1
- package/scripts/hooks/pi-telemetry-hook.js +27 -1
- package/scripts/hooks/stop-goal-fit.js +157 -26
- package/scripts/hooks/workflow-steering.js +77 -3
- package/scripts/install-codex-home.sh +138 -38
- package/scripts/statusline/flow-agents-statusline.js +2 -2
- package/src/cli/assignment-provider.ts +845 -0
- package/src/cli/console-learning-projection.ts +3 -2
- package/src/cli/effective-assignment-provider-settings.ts +112 -0
- package/src/cli/init.ts +101 -21
- package/src/cli/kit.ts +37 -6
- package/src/cli/public-api.test.mjs +58 -6
- package/src/cli/pull-work-provider.ts +1 -1
- package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
- package/src/cli/usage-feedback.ts +3 -3
- package/src/cli/validate-hook-influence.ts +1 -0
- package/src/cli/validate-workflow-artifacts.ts +27 -7
- package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
- package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
- package/src/cli/workflow-sidecar.ts +933 -168
- package/src/cli.ts +6 -0
- package/src/flow-kit/validate.ts +74 -1
- package/src/index.ts +6 -2
- package/src/lib/flow-resolver.ts +123 -55
- package/src/lib/local-artifact-root.ts +24 -5
- package/src/lib/workflow-learning-projection.ts +2 -2
- package/src/runtime-adapters.ts +61 -15
- package/src/tools/build-universal-bundles.ts +49 -16
- package/src/tools/generate-context-map.ts +10 -8
- package/src/tools/validate-source-tree.ts +24 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { readJson, relPath, writeJson } from "./lib/fs.js";
|
|
4
|
+
import { flowAgentsArtifactRoot } from "./lib/local-artifact-root.js";
|
|
5
|
+
import { parseKitDependencies } from "./flow-kit/validate.js";
|
|
4
6
|
const ASSET_CLASSES = ["flows", "skills", "docs", "adapters", "evals", "assets"];
|
|
5
7
|
function assetPath(root, value) {
|
|
6
8
|
if (path.isAbsolute(value))
|
|
@@ -13,7 +15,7 @@ function loadKitAssets(kitRoot, sourceKind, warnings, errors) {
|
|
|
13
15
|
const manifestPath = path.join(kitRoot, "kit.json");
|
|
14
16
|
if (!fs.existsSync(manifestPath)) {
|
|
15
17
|
errors.push(`${kitRoot}: missing kit.json`);
|
|
16
|
-
return [];
|
|
18
|
+
return { kit_id: "", assets: [], dependencies: [] };
|
|
17
19
|
}
|
|
18
20
|
let manifest;
|
|
19
21
|
try {
|
|
@@ -21,13 +23,13 @@ function loadKitAssets(kitRoot, sourceKind, warnings, errors) {
|
|
|
21
23
|
}
|
|
22
24
|
catch (error) {
|
|
23
25
|
errors.push(`${manifestPath}: ${error.message}`);
|
|
24
|
-
return [];
|
|
26
|
+
return { kit_id: "", assets: [], dependencies: [] };
|
|
25
27
|
}
|
|
26
28
|
const kitId = typeof manifest.id === "string" ? manifest.id : "";
|
|
27
29
|
const kitName = typeof manifest.name === "string" ? manifest.name : kitId;
|
|
28
30
|
if (!kitId) {
|
|
29
31
|
errors.push(`${manifestPath}: missing kit id`);
|
|
30
|
-
return [];
|
|
32
|
+
return { kit_id: "", assets: [], dependencies: [] };
|
|
31
33
|
}
|
|
32
34
|
const assets = [];
|
|
33
35
|
for (const assetClass of ASSET_CLASSES) {
|
|
@@ -66,12 +68,20 @@ function loadKitAssets(kitRoot, sourceKind, warnings, errors) {
|
|
|
66
68
|
assets.push({ kit_id: kitId, kit_name: kitName, asset_class: assetClass, asset_id: assetId, relative_path: rel, source_path: source, source_kind: sourceKind, description });
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
|
|
71
|
+
// Parse cross-kit dependency declarations (extension-layer metadata; see
|
|
72
|
+
// docs/adr/0019-kit-dependency-ownership.md). Shape errors surface at
|
|
73
|
+
// install/inspect/validate time via validateKitRepository; here we only collect
|
|
74
|
+
// the edges so activation can enforce presence against the full inventory.
|
|
75
|
+
const dependencies = parseKitDependencies(manifest, manifestPath).entries
|
|
76
|
+
.map((dep) => ({ from_kit_id: kitId, kit_id: dep.kit_id, ...(dep.reason ? { reason: dep.reason } : {}) }));
|
|
77
|
+
return { kit_id: kitId, assets, dependencies };
|
|
70
78
|
}
|
|
71
79
|
export function readKitInventory(sourceRoot, dest) {
|
|
72
80
|
const warnings = [];
|
|
73
81
|
const errors = [];
|
|
74
82
|
const assets = [];
|
|
83
|
+
const dependencies = [];
|
|
84
|
+
const presentKitIds = new Set();
|
|
75
85
|
const catalogPath = path.join(sourceRoot, "kits", "catalog.json");
|
|
76
86
|
if (!fs.existsSync(catalogPath))
|
|
77
87
|
warnings.push(`${catalogPath}: built-in Kit Catalog not found; skipping built-in kits (this is normal when running outside a flow-agents checkout)`);
|
|
@@ -88,8 +98,13 @@ export function readKitInventory(sourceRoot, dest) {
|
|
|
88
98
|
const kitRoot = assetPath(sourceRoot, rel);
|
|
89
99
|
if (!kitRoot || !fs.existsSync(kitRoot))
|
|
90
100
|
warnings.push(`${catalogPath}: kits[${index}].path unavailable: ${rel}`);
|
|
91
|
-
else
|
|
92
|
-
|
|
101
|
+
else {
|
|
102
|
+
const loaded = loadKitAssets(kitRoot, "builtin", warnings, errors);
|
|
103
|
+
assets.push(...loaded.assets);
|
|
104
|
+
dependencies.push(...loaded.dependencies);
|
|
105
|
+
if (loaded.kit_id)
|
|
106
|
+
presentKitIds.add(loaded.kit_id);
|
|
107
|
+
}
|
|
93
108
|
});
|
|
94
109
|
}
|
|
95
110
|
}
|
|
@@ -108,11 +123,24 @@ export function readKitInventory(sourceRoot, dest) {
|
|
|
108
123
|
const kitRoot = path.join(dest, "kits", "local", "repositories", id);
|
|
109
124
|
if (!fs.existsSync(kitRoot))
|
|
110
125
|
warnings.push(`${id}: installed kit copy missing at ${kitRoot}; skipping`);
|
|
111
|
-
else
|
|
112
|
-
|
|
126
|
+
else {
|
|
127
|
+
const loaded = loadKitAssets(kitRoot, "local", warnings, errors);
|
|
128
|
+
assets.push(...loaded.assets);
|
|
129
|
+
dependencies.push(...loaded.dependencies);
|
|
130
|
+
if (loaded.kit_id)
|
|
131
|
+
presentKitIds.add(loaded.kit_id);
|
|
132
|
+
}
|
|
113
133
|
});
|
|
114
134
|
}
|
|
115
135
|
}
|
|
136
|
+
// Activation-time cross-kit dependency enforcement: any declared dependency whose
|
|
137
|
+
// kit_id is not present among the union of built-in catalog kits and locally-installed
|
|
138
|
+
// kits is a hard error (feeds the errors[] -> activation-manifest -> non-zero exit path).
|
|
139
|
+
for (const dep of dependencies) {
|
|
140
|
+
if (!presentKitIds.has(dep.kit_id)) {
|
|
141
|
+
errors.push(`${dep.from_kit_id}: declares a dependency on kit '${dep.kit_id}'${dep.reason ? ` (${dep.reason})` : ""} which is not installed or activated. Install it with 'flow-agents kit install <source>' and re-activate, or remove the dependency declaration if it is no longer needed.`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
116
144
|
return { assets, warnings, errors };
|
|
117
145
|
}
|
|
118
146
|
function safeSegment(value) {
|
|
@@ -125,7 +153,7 @@ function safeSegment(value) {
|
|
|
125
153
|
const ACTIVATED_ASSET_CLASSES = new Set(["flows", "skills", "docs"]);
|
|
126
154
|
export function activateCodexLocal(sourceRoot, dest) {
|
|
127
155
|
const inventory = readKitInventory(sourceRoot, dest);
|
|
128
|
-
const runtimeDir = path.join(dest, "
|
|
156
|
+
const runtimeDir = path.join(flowAgentsArtifactRoot(dest), "projections", "codex");
|
|
129
157
|
const generated = [];
|
|
130
158
|
const skipped = [];
|
|
131
159
|
for (const asset of inventory.assets) {
|
|
@@ -144,7 +172,13 @@ export function activateCodexLocal(sourceRoot, dest) {
|
|
|
144
172
|
// agent's guidance index (AGENTS.md) can reference them and they are co-located
|
|
145
173
|
// with flow definitions for the same kit.
|
|
146
174
|
const filename = path.basename(asset.source_path);
|
|
147
|
-
|
|
175
|
+
// Namespace skills by BOTH kit id AND the skill's own directory name: every skill
|
|
176
|
+
// file is literally named SKILL.md, so keying only on kit id collides all of a
|
|
177
|
+
// kit's skills onto one .../<kit-id>/SKILL.md file. Docs keep the flat
|
|
178
|
+
// <kit-id>/<filename> layout (no per-directory collision found).
|
|
179
|
+
const output = asset.asset_class === "skills"
|
|
180
|
+
? path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), safeSegment(path.basename(path.dirname(asset.source_path))), filename)
|
|
181
|
+
: path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), filename);
|
|
148
182
|
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
149
183
|
fs.copyFileSync(asset.source_path, output);
|
|
150
184
|
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id ?? "", source_path: asset.source_path.split(path.sep).join("/") });
|
|
@@ -169,11 +203,11 @@ export function activateCodexLocal(sourceRoot, dest) {
|
|
|
169
203
|
// layout knowledge. The Strands steering layer then reads the written runtime files.
|
|
170
204
|
export function activateStrandsLocal(sourceRoot, dest) {
|
|
171
205
|
const inventory = readKitInventory(sourceRoot, dest);
|
|
172
|
-
// Runtime flows land at .flow-agents/
|
|
206
|
+
// Runtime flows land at .kontourai/flow-agents/projections/strands/flows/<kit-id>/<asset-id>.flow.json
|
|
173
207
|
// so the Strands steering context can glob for *.flow.json under this path.
|
|
174
|
-
// Runtime skills land at .flow-agents/
|
|
175
|
-
// docs at .flow-agents/
|
|
176
|
-
const runtimeDir = path.join(dest, "
|
|
208
|
+
// Runtime skills land at .kontourai/flow-agents/projections/strands/skills/<kit-id>/<filename> and
|
|
209
|
+
// docs at .kontourai/flow-agents/projections/strands/docs/<kit-id>/<filename> for system-prompt injection.
|
|
210
|
+
const runtimeDir = path.join(flowAgentsArtifactRoot(dest), "projections", "strands");
|
|
177
211
|
const generated = [];
|
|
178
212
|
const skipped = [];
|
|
179
213
|
for (const asset of inventory.assets) {
|
|
@@ -190,9 +224,15 @@ export function activateStrandsLocal(sourceRoot, dest) {
|
|
|
190
224
|
else if (asset.asset_class === "skills" || asset.asset_class === "docs") {
|
|
191
225
|
// Mirror the codex-local layout: strands/<class>/<kit-id>/<filename>.
|
|
192
226
|
// The Strands system-prompt injection layer can glob for all *.md files under
|
|
193
|
-
// .flow-agents/
|
|
227
|
+
// .kontourai/flow-agents/projections/strands/skills/ to include agent guidance in the context.
|
|
194
228
|
const filename = path.basename(asset.source_path);
|
|
195
|
-
|
|
229
|
+
// Namespace skills by BOTH kit id AND the skill's own directory name: every skill
|
|
230
|
+
// file is literally named SKILL.md, so keying only on kit id collides all of a
|
|
231
|
+
// kit's skills onto one .../<kit-id>/SKILL.md file. Docs keep the flat
|
|
232
|
+
// <kit-id>/<filename> layout (no per-directory collision found).
|
|
233
|
+
const output = asset.asset_class === "skills"
|
|
234
|
+
? path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), safeSegment(path.basename(path.dirname(asset.source_path))), filename)
|
|
235
|
+
: path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), filename);
|
|
196
236
|
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
197
237
|
fs.copyFileSync(asset.source_path, output);
|
|
198
238
|
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id ?? "", source_path: asset.source_path.split(path.sep).join("/") });
|
|
@@ -3,11 +3,19 @@ import fs from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { loadJson, readText, root, walkFiles, writeText } from "./common.js";
|
|
6
|
+
import { DURABLE_FLOW_AGENTS_DIR, FLOW_AGENTS_RUNTIME_DIR } from "../lib/local-artifact-root.js";
|
|
6
7
|
const dist = process.env.FLOW_AGENTS_DIST_DIR ? path.resolve(process.env.FLOW_AGENTS_DIST_DIR) : path.join(root, "dist");
|
|
7
8
|
const manifest = loadJson(path.join(root, "packaging/manifest.json"));
|
|
8
9
|
const pkgVersion = loadJson(path.join(root, "package.json"))["version"] ?? "0.0.0";
|
|
10
|
+
const runtimeTaskDir = FLOW_AGENTS_RUNTIME_DIR;
|
|
11
|
+
const durableInstallRecordRel = `${DURABLE_FLOW_AGENTS_DIR}/install.json`;
|
|
9
12
|
const textExtensions = new Set([".css", ".html", ".js", ".json", ".md", ".sh", ".toml", ".txt", ".yaml", ".yml", ".ts"]);
|
|
10
13
|
const dropDiagnostics = [];
|
|
14
|
+
// Set by collectAllSkills() when two kits (or a kit and the top-level skills/
|
|
15
|
+
// dir) declare the same skill directory name. Reported via the same
|
|
16
|
+
// console.error + process.exitCode diagnostic idiom as the rest of this file
|
|
17
|
+
// (see main()) instead of an uncaught thrown Error / stack trace.
|
|
18
|
+
let skillCollisionDiagnostic = null;
|
|
11
19
|
const printDiagnostics = !["0", "false", "no"].includes(String(process.env.FLOW_AGENTS_EXPORT_DIAGNOSTICS ?? "1").toLowerCase());
|
|
12
20
|
/**
|
|
13
21
|
* Collect all skill source paths across skills/ and kit-owned skills.
|
|
@@ -18,16 +26,28 @@ const printDiagnostics = !["0", "false", "no"].includes(String(process.env.FLOW_
|
|
|
18
26
|
*/
|
|
19
27
|
function collectAllSkills() {
|
|
20
28
|
const results = [];
|
|
21
|
-
const seen = new
|
|
29
|
+
const seen = new Map(); // skill name -> source description of first declaration
|
|
30
|
+
const collisions = [];
|
|
31
|
+
// Compiled host bundles ship skills flat at .claude/skills/<name>/SKILL.md etc., so
|
|
32
|
+
// the install name (skill directory name) must be globally unique across skills/ and
|
|
33
|
+
// every kit. A duplicate is a build-FAILING collision, never a silent drop — the
|
|
34
|
+
// previous Set-based dedupe silently kept only the first and discarded the rest.
|
|
35
|
+
const recordSkill = (name, src, source) => {
|
|
36
|
+
const prior = seen.get(name);
|
|
37
|
+
if (prior !== undefined) {
|
|
38
|
+
collisions.push(` - '${name}' is declared by both ${prior} and ${source}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
seen.set(name, source);
|
|
42
|
+
results.push({ name, src });
|
|
43
|
+
};
|
|
22
44
|
// 1. Top-level skills/ directory (tools pending reclassification).
|
|
23
45
|
const skillsDir = path.join(root, "skills");
|
|
24
46
|
if (fs.existsSync(skillsDir)) {
|
|
25
47
|
for (const skill of fs.readdirSync(skillsDir).sort()) {
|
|
26
48
|
const skillPath = path.join(skillsDir, skill, "SKILL.md");
|
|
27
|
-
if (fs.existsSync(skillPath)
|
|
28
|
-
|
|
29
|
-
results.push({ name: skill, src: skillPath });
|
|
30
|
-
}
|
|
49
|
+
if (fs.existsSync(skillPath))
|
|
50
|
+
recordSkill(skill, skillPath, "skills/");
|
|
31
51
|
}
|
|
32
52
|
}
|
|
33
53
|
// 2. Kit-owned skills declared in kits/<kit>/kit.json `skills` arrays.
|
|
@@ -55,13 +75,18 @@ function collectAllSkills() {
|
|
|
55
75
|
// Derive install name from the directory containing SKILL.md (one level up).
|
|
56
76
|
const absPath = path.resolve(path.join(kitsDir, kitName), relPath);
|
|
57
77
|
const skillName = path.basename(path.dirname(absPath));
|
|
58
|
-
if (fs.existsSync(absPath)
|
|
59
|
-
|
|
60
|
-
results.push({ name: skillName, src: absPath });
|
|
61
|
-
}
|
|
78
|
+
if (fs.existsSync(absPath))
|
|
79
|
+
recordSkill(skillName, absPath, `kits/${kitName}`);
|
|
62
80
|
}
|
|
63
81
|
}
|
|
64
82
|
}
|
|
83
|
+
if (collisions.length) {
|
|
84
|
+
skillCollisionDiagnostic =
|
|
85
|
+
"flow-agents: skill name collision — skill directory names must be unique across " +
|
|
86
|
+
"skills/ and all kits/*/kit.json:\n" +
|
|
87
|
+
collisions.join("\n") +
|
|
88
|
+
"\nRename one of the colliding skill directories so every compiled bundle skill has a unique install name.";
|
|
89
|
+
}
|
|
65
90
|
return results.sort((a, b) => a.name.localeCompare(b.name));
|
|
66
91
|
}
|
|
67
92
|
function resetDir(dir) {
|
|
@@ -190,7 +215,7 @@ function generatedAgentsSummary(agents) {
|
|
|
190
215
|
function exportRootAgentsMd(label, agents, taskDir) {
|
|
191
216
|
return `# Universal Agent Bundle (${label})\n\nThis bundle was generated from the canonical source in this repo. Treat the repo root as the source of truth and regenerate the bundle instead of editing exported agent files by hand.\n\n## Shared Conventions\n\n- \`skills/\`, \`context/\`, \`powers/\`, \`prompts/\`, \`scripts/\`, and \`evals/\` were copied from the canonical source.\n- Cross-session task artifacts should live under \`${taskDir}\`.\n- Kiro-only hook wiring was stripped from exported non-Kiro agents to keep the package portable.\n\n## Exported Agents\n\n${generatedAgentsSummary(agents)}\n`;
|
|
192
217
|
}
|
|
193
|
-
const CODEX_LIVE_HOOKS_README = `\n## Running with hooks active (live)\n\n\`install.sh\` lays the bundle into a workspace, but a live Codex session needs a \`CODEX_HOME\` that has both the bundle's hooks/scripts AND your real credentials. Use the dedicated installer, which flattens the config to the home root and copies your auth from \`~/.codex
|
|
218
|
+
const CODEX_LIVE_HOOKS_README = `\n## Running with hooks active (live)\n\n\`install.sh\` lays the bundle into a workspace, but a live Codex session needs a \`CODEX_HOME\` that has both the bundle's hooks/scripts AND your real credentials. Use the dedicated installer, which defaults to \`CODEX_HOME\` when set and otherwise \`~/.codex\`, flattens the config to the home root, and copies your auth from \`~/.codex\` when installing elsewhere:\n\n\`\`\`bash\nbash scripts/install-codex-home.sh\ncodex exec --dangerously-bypass-hook-trust -C /path/to/project \"<prompt>\"\n\`\`\`\n\nPass a positional destination to install into an explicit alternate Codex home.\n\nThe goal-fit Stop hook then enforces by default (\`FLOW_AGENTS_GOAL_FIT_MODE=block\`); set it to \`warn\` or \`off\` to override.\n`;
|
|
194
219
|
function exportTargetReadme(label, installHint, extra = "") {
|
|
195
220
|
return `# ${label} Bundle\n\nGenerated from the canonical source in this repository.\n\n## Install\n\n\`\`\`bash\n${installHint}\n\`\`\`\n\nThe install ships the full standalone base (skills, agents, powers) plus the\nFlow Kits. Kit depth is activated through the Kit Catalog, not at install time.\n\n## Contents\n\n- Harness-specific agents\n- Shared skills\n- Shared context, powers, prompts, scripts, and evals\n${extra}`;
|
|
196
221
|
}
|
|
@@ -282,7 +307,7 @@ function claudePolicy(event, script, envPrefix = "") {
|
|
|
282
307
|
return `bash -lc 'root="\${CLAUDE_PROJECT_DIR:-$(pwd)}"; ${envPrefix}node "$root/scripts/hooks/claude-hook-adapter.js" ${event} ${script.replace(/\.js$/, "")} ${script} default'`;
|
|
283
308
|
}
|
|
284
309
|
function codexRoot(scriptPath) {
|
|
285
|
-
return `root="\${CODEX_HOME:-}"; if [ -z "$root" ] || [ ! -f "$root/${scriptPath}" ]; then root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); fi`;
|
|
310
|
+
return `root="\${CODEX_HOME:-}"; if [ -z "$root" ] || [ ! -f "$root/${scriptPath}" ]; then root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); fi; if [ ! -f "$root/${scriptPath}" ] && [ -f "$HOME/.codex/${scriptPath}" ]; then root="$HOME/.codex"; fi; if [ ! -f "$root/${scriptPath}" ]; then echo "flow-agents: hook script not found at $root/${scriptPath} (checked \\$CODEX_HOME, git toplevel/cwd, and \\$HOME/.codex) - skipping hook" >&2; exit 0; fi`;
|
|
286
311
|
}
|
|
287
312
|
function codexTelemetry(event) {
|
|
288
313
|
if (event === "PermissionRequest")
|
|
@@ -358,9 +383,9 @@ function installScript(label, defaultDestDisplay, token, destFallbackShell, merg
|
|
|
358
383
|
const requiredCheck = destRequired ? `if [[ -z "$DEST" ]]; then\n usage\n exit 2\nfi\n` : "";
|
|
359
384
|
const usageDest = destRequired ? "/path/to/workspace" : defaultDestDisplay;
|
|
360
385
|
const mergeBlock = mergeConfig
|
|
361
|
-
? `\nif command -v node >/dev/null 2>&1; then\n node "$DEST/scripts/install-merge.js" --config "$DEST/${mergeConfig.configRelPath}" --managed-hooks "$SRC/${mergeConfig.managedConfigRelPath}" --version "${mergeConfig.version}" --install-record "$DEST
|
|
386
|
+
? `\nif command -v node >/dev/null 2>&1; then\n node "$DEST/scripts/install-merge.js" --config "$DEST/${mergeConfig.configRelPath}" --managed-hooks "$SRC/${mergeConfig.managedConfigRelPath}" --version "${mergeConfig.version}" --install-record "$DEST/${durableInstallRecordRel}" --runtime "${mergeConfig.runtime}" || true\nfi`
|
|
362
387
|
: stampConfig
|
|
363
|
-
? `\nif command -v node >/dev/null 2>&1; then\n node "$DEST/scripts/install-merge.js" --stamp-only --version "${stampConfig.version}" --install-record "$DEST
|
|
388
|
+
? `\nif command -v node >/dev/null 2>&1; then\n node "$DEST/scripts/install-merge.js" --stamp-only --version "${stampConfig.version}" --install-record "$DEST/${durableInstallRecordRel}" --runtime "${stampConfig.runtime}" || true\nfi`
|
|
364
389
|
: "";
|
|
365
390
|
return `#!/usr/bin/env bash\nset -euo pipefail\n\nusage() {\n cat >&2 <<'EOF'\nusage: bash install.sh ${usageDest} [options]\n\nOptions:\n --telemetry-sink NAME local-files, local-kontour-console,\n kontour-hosted-console, user-hosted-console,\n or legacy aliases. May be repeated.\n --console-url URL Persist Console telemetry base URL.\n --console-endpoint URL Persist full Console telemetry records endpoint URL.\n --console-token-file PATH\n Read Console telemetry bearer token from a file.\n --console-tenant ID Persist Console tenant identifier.\nEOF\n}\n\nDEST=""\nDEST_SET=0\nCONSOLE_CONFIG_ARGS=()\nwhile [[ $# -gt 0 ]]; do\n case "$1" in\n --telemetry-sink|--telemetry-sinks|--console-url|--console-endpoint|--console-endpoint-url|--console-token-file|--console-tenant|--console-tenant-id)\n [[ $# -ge 2 ]] || { echo "install.sh: $1 requires a value" >&2; exit 2; }\n CONSOLE_CONFIG_ARGS+=("$1" "$2")\n shift 2\n ;;\n --help|-h)\n usage\n exit 0\n ;;\n -*)\n echo "install.sh: unknown option: $1" >&2\n usage\n exit 2\n ;;\n *)\n if [[ "$DEST_SET" -eq 1 ]]; then\n echo "install.sh: unexpected argument: $1" >&2\n usage\n exit 2\n fi\n DEST="$1"\n DEST_SET=1\n shift\n ;;\n esac\ndone${destFallback}\n${requiredCheck}SRC="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"\n\nmkdir -p "$DEST"\nrsync -a ${token ? "--delete " : ""}${mergeConfig ? `--exclude="${mergeConfig.configRelPath}" ` : ""}"$SRC"/ "$DEST"/${replaceBlock}${mergeBlock}\nif [[ \${#CONSOLE_CONFIG_ARGS[@]} -gt 0 || -n "\${FLOW_AGENTS_TELEMETRY_SINK:-}" || -n "\${FLOW_AGENTS_TELEMETRY_SINKS:-}" || -n "\${FLOW_AGENTS_CONSOLE_URL:-}" || -n "\${CONSOLE_TELEMETRY_URL:-}" || -n "\${CONSOLE_URL:-}" || -n "\${FLOW_AGENTS_CONSOLE_TOKEN_FILE:-}" || -n "\${CONSOLE_TELEMETRY_TOKEN_FILE:-}" ]]; then\n bash "$DEST/scripts/telemetry/install-console-config.sh" "$DEST/scripts/telemetry/telemetry.conf" "\${CONSOLE_CONFIG_ARGS[@]}"\nfi\necho "Installed ${label} bundle ${token ? "to" : "into"} $DEST"\n`;
|
|
366
391
|
}
|
|
@@ -368,8 +393,8 @@ function buildBase(agents) {
|
|
|
368
393
|
const bundle = path.join(dist, "base");
|
|
369
394
|
resetDir(bundle);
|
|
370
395
|
copySharedContent(bundle, "base", "<bundle-root>");
|
|
371
|
-
writeText(path.join(bundle,
|
|
372
|
-
writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Base", agents,
|
|
396
|
+
writeText(path.join(bundle, runtimeTaskDir, ".gitkeep"), "");
|
|
397
|
+
writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Base", agents, runtimeTaskDir));
|
|
373
398
|
writeText(path.join(bundle, "README.md"), exportTargetReadme("Base", "bash install.sh /path/to/workspace"));
|
|
374
399
|
writeText(path.join(bundle, "install.sh"), installScript("Base", "/path/to/workspace", undefined, undefined, undefined, { runtime: "base", version: pkgVersion }));
|
|
375
400
|
fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
|
|
@@ -381,7 +406,7 @@ function buildKiro(agents) {
|
|
|
381
406
|
copySharedContent(bundle, "kiro", token);
|
|
382
407
|
for (const spec of agents)
|
|
383
408
|
writeText(path.join(bundle, "agents", `${spec.name}.json`), sanitizeText(`${JSON.stringify(sanitizeAgentJson(spec), null, 2)}\n`, "kiro", token));
|
|
384
|
-
writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Kiro", agents,
|
|
409
|
+
writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Kiro", agents, runtimeTaskDir));
|
|
385
410
|
writeText(path.join(bundle, "README.md"), exportTargetReadme("Kiro", "bash install.sh $HOME/.flow-agents"));
|
|
386
411
|
writeText(path.join(bundle, "install.sh"), installScript("Kiro", "$HOME/.flow-agents", token, '${FLOW_AGENTS_DEST:-$HOME/.flow-agents}', undefined, { runtime: "kiro", version: pkgVersion }));
|
|
387
412
|
fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
|
|
@@ -729,6 +754,14 @@ function buildCatalog(agents) {
|
|
|
729
754
|
}
|
|
730
755
|
export function main() {
|
|
731
756
|
fs.mkdirSync(dist, { recursive: true });
|
|
757
|
+
// Populate (and, on collision, set) skillCollisionDiagnostic before any
|
|
758
|
+
// build step writes output -- fail fast with a clear diagnostic instead of
|
|
759
|
+
// partially building bundles from a deduped/ambiguous skill set.
|
|
760
|
+
collectAllSkills();
|
|
761
|
+
if (skillCollisionDiagnostic) {
|
|
762
|
+
console.error(skillCollisionDiagnostic);
|
|
763
|
+
return 1;
|
|
764
|
+
}
|
|
732
765
|
const agents = fs.readdirSync(path.join(root, "agents")).filter((name) => name.endsWith(".json")).sort().map((name) => loadJson(path.join(root, "agents", name)));
|
|
733
766
|
buildBase(agents);
|
|
734
767
|
buildKiro(agents);
|
|
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { exists, loadJson, markdownTable, oneLine, readText, rel, root, writeText } from "./common.js";
|
|
6
|
+
import { DURABLE_FLOW_AGENTS_DIR, FLOW_AGENTS_RUNTIME_DIR } from "../lib/local-artifact-root.js";
|
|
6
7
|
const defaultOutput = path.join(root, "docs/context-map.md");
|
|
7
8
|
const dirDescriptions = {
|
|
8
9
|
agents: "Canonical agent specs and routing prompts.",
|
|
@@ -21,7 +22,7 @@ const commands = [
|
|
|
21
22
|
["Source tree", "npm run validate:source"],
|
|
22
23
|
["Static suite", "bash evals/run.sh static"],
|
|
23
24
|
["Integration suite", "bash evals/run.sh integration"],
|
|
24
|
-
["Workflow artifacts",
|
|
25
|
+
["Workflow artifacts", `npm run workflow:validate-artifacts -- --require-sidecars --require-critique ${FLOW_AGENTS_RUNTIME_DIR}/<slug>`],
|
|
25
26
|
["Workflow sidecars", "npm run workflow:sidecar -- --help"],
|
|
26
27
|
["Claim lookup", "npm run workflow:sidecar -- claim <id> <dir>"],
|
|
27
28
|
["Context map drift", "npm run context-map:check"],
|
|
@@ -73,7 +74,8 @@ function repoShape(manifest) {
|
|
|
73
74
|
rows.push([dir, "optional", "Optional local/user pack copied when present."]);
|
|
74
75
|
}
|
|
75
76
|
rows.push(["dist", "generated", "Generated bundle exports. Do not edit by hand."]);
|
|
76
|
-
rows.push([
|
|
77
|
+
rows.push([FLOW_AGENTS_RUNTIME_DIR, "runtime", "Non-durable workflow artifacts, sidecars, and generated projections. Not committed by default."]);
|
|
78
|
+
rows.push([DURABLE_FLOW_AGENTS_DIR, "durable local state", "Explicit Flow Agents config/install state. Not a runtime artifact fallback."]);
|
|
77
79
|
return rows;
|
|
78
80
|
}
|
|
79
81
|
/** Collect all skill {name, absPath} pairs from skills/ and kit-owned skills. */
|
|
@@ -159,15 +161,15 @@ function latestRuntimeStates(includeRuntime) {
|
|
|
159
161
|
if (!includeRuntime) {
|
|
160
162
|
return [
|
|
161
163
|
"Runtime workflow state is excluded from the committed map.",
|
|
162
|
-
|
|
164
|
+
`Regenerate locally with \`npm run context-map -- --include-runtime\` to include recent \`${FLOW_AGENTS_RUNTIME_DIR}\` state.`,
|
|
163
165
|
];
|
|
164
166
|
}
|
|
165
|
-
const workflowDir = path.join(root,
|
|
167
|
+
const workflowDir = path.join(root, FLOW_AGENTS_RUNTIME_DIR);
|
|
166
168
|
if (!exists(workflowDir))
|
|
167
|
-
return [
|
|
169
|
+
return [`No local workflow state found under \`${FLOW_AGENTS_RUNTIME_DIR}\`.`];
|
|
168
170
|
const states = fs.readdirSync(workflowDir).map((name) => path.join(workflowDir, name, "state.json")).filter(exists).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
169
171
|
if (!states.length)
|
|
170
|
-
return [
|
|
172
|
+
return [`No local workflow state found under \`${FLOW_AGENTS_RUNTIME_DIR}\`.`];
|
|
171
173
|
const rows = states.slice(0, 8).map((file) => {
|
|
172
174
|
const data = loadJson(file);
|
|
173
175
|
const next = typeof data.next_action === "object" && data.next_action ? data.next_action : {};
|
|
@@ -184,11 +186,11 @@ function render(includeRuntime) {
|
|
|
184
186
|
"## How To Use This", "",
|
|
185
187
|
"- Start here when a session is long, resumed, or context-constrained.",
|
|
186
188
|
"- Load only the specific skill, contract, schema, or doc that matches the task.",
|
|
187
|
-
|
|
189
|
+
`- Treat \`${FLOW_AGENTS_RUNTIME_DIR}\` as runtime state and \`dist/\` as generated output.`, "",
|
|
188
190
|
"## Repository Shape", "", ...markdownTable(["Path", "Role", "Purpose"], repoShape(manifest)), "",
|
|
189
191
|
"## Core Commands", "", ...markdownTable(["Use", "Command"], commands), "",
|
|
190
192
|
"## Workflow Sidecars", "",
|
|
191
|
-
|
|
193
|
+
`Machine-readable workflow state lives beside Markdown artifacts in \`${FLOW_AGENTS_RUNTIME_DIR}/<slug>/\`.`, "",
|
|
192
194
|
...markdownTable(["Schema", "Title", "ID"], schemas()), "",
|
|
193
195
|
"Primary tools: `npm run workflow:sidecar`, `npm run workflow:validate-artifacts`, `scripts/hooks/stop-goal-fit.js`, and `scripts/hooks/workflow-steering.js`.", "",
|
|
194
196
|
"## Workflow Skills", "", ...markdownTable(["Skill", "Source", "When To Load"], workflowRows), "",
|
|
@@ -25,6 +25,15 @@ const mirroredFiles = new Map([
|
|
|
25
25
|
["scripts/telemetry/console-presets.sh", { mirror: "context/scripts/telemetry/console-presets.sh", allowedDifferences: [] }],
|
|
26
26
|
["scripts/telemetry/install-console-config.sh", { mirror: "context/scripts/telemetry/install-console-config.sh", allowedDifferences: [] }],
|
|
27
27
|
["scripts/discover-agents.sh", { mirror: "context/scripts/discover-agents.sh", allowedDifferences: [['ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"', 'ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"']] }],
|
|
28
|
+
// WS8 (AC9): scripts/hooks/* is the actively-maintained source of truth; context/ holds
|
|
29
|
+
// the exported mirror. These four hooks (and the config-protection remedy lib) had drifted
|
|
30
|
+
// silently because nothing validated them. Enforce byte-identical mirrors so future drift
|
|
31
|
+
// fails `npm run validate:source` instead of silently accumulating.
|
|
32
|
+
["scripts/hooks/config-protection.js", { mirror: "context/scripts/hooks/config-protection.js", allowedDifferences: [] }],
|
|
33
|
+
["scripts/hooks/lib/config-protection-remedies.js", { mirror: "context/scripts/hooks/lib/config-protection-remedies.js", allowedDifferences: [] }],
|
|
34
|
+
["scripts/hooks/stop-goal-fit.js", { mirror: "context/scripts/hooks/stop-goal-fit.js", allowedDifferences: [] }],
|
|
35
|
+
["scripts/hooks/run-hook.js", { mirror: "context/scripts/hooks/run-hook.js", allowedDifferences: [] }],
|
|
36
|
+
["scripts/hooks/workflow-steering.js", { mirror: "context/scripts/hooks/workflow-steering.js", allowedDifferences: [] }],
|
|
28
37
|
]);
|
|
29
38
|
const publicScriptWrappers = new Map([
|
|
30
39
|
["scripts/build-universal-bundles.js", { target: "../build/src/tools/build-universal-bundles.js", significantLines: [
|
|
@@ -71,14 +80,20 @@ const hookFilePolicies = new Map([
|
|
|
71
80
|
["scripts/hooks/utterance-check.js", { category: "policy hook", requiredNeedles: ["Utterance Check Hook", "FLOW_AGENTS_UTTERANCE_CHECK_ENABLED"] }],
|
|
72
81
|
["scripts/hooks/workflow-steering.js", { category: "policy hook", requiredNeedles: ["Workflow Steering Hook"] }],
|
|
73
82
|
["scripts/hooks/desktop-notify.sh", { category: "local notification helper", requiredNeedles: ["desktop-notify.sh", "osascript"] }],
|
|
83
|
+
["scripts/hooks/lib/actor-identity.js", { category: "shared hook library", requiredNeedles: ["resolveActor", "FLOW_AGENTS_ACTOR"] }],
|
|
74
84
|
["scripts/hooks/lib/audit-transport.sh", { category: "shared hook library", requiredNeedles: ["audit_emit"] }],
|
|
85
|
+
["scripts/hooks/lib/config-protection-remedies.js", { category: "shared hook library", requiredNeedles: ["SANCTIONED_REMEDIES", "REMEDY_COMMAND_CANDIDATES"] }],
|
|
75
86
|
["scripts/hooks/lib/hook-flags.js", { category: "shared hook library", requiredNeedles: ["isHookEnabled"] }],
|
|
87
|
+
["scripts/hooks/lib/liveness-heartbeat.js", { category: "shared hook library", requiredNeedles: ["maybeEmitHeartbeat", "resolveHeartbeatThrottleSeconds"] }],
|
|
88
|
+
["scripts/hooks/lib/liveness-policy.js", { category: "shared hook library", requiredNeedles: ["isLivenessEnabled", "resolveTtlSeconds"] }],
|
|
76
89
|
["scripts/hooks/lib/liveness-read.js", { category: "shared hook library", requiredNeedles: ["freshHolders", "readLivenessEvents"] }],
|
|
90
|
+
["scripts/hooks/lib/liveness-write.js", { category: "shared hook library", requiredNeedles: ["appendLivenessEvent", "livenessStreamFile"] }],
|
|
77
91
|
["scripts/hooks/lib/local-artifact-paths.js", { category: "shared hook library", requiredNeedles: ["flowAgentsArtifactRoot", "defaultArtifactRootForRead"] }],
|
|
78
92
|
["scripts/hooks/lib/patterns.sh", { category: "shared hook library", requiredNeedles: ["_detect_secrets"] }],
|
|
79
93
|
["scripts/hooks/lib/resolve-formatter.js", { category: "shared hook library", requiredNeedles: ["resolveFormatter"] }],
|
|
80
94
|
]);
|
|
81
95
|
const fixtureOwnerPolicies = new Map([
|
|
96
|
+
["evals/fixtures/assignment-provider", { owners: ["evals/integration/test_assignment_provider_local_file.sh", "evals/integration/test_assignment_provider_github.sh", "evals/integration/test_pull_work_assignment_join.sh"], classification: "AssignmentProvider local-file and GitHub render/status fixtures (#290)" }],
|
|
82
97
|
["evals/fixtures/backlog-provider-settings", { owners: ["evals/integration/test_effective_backlog_settings.sh"], classification: "settings precedence fixtures" }],
|
|
83
98
|
["evals/fixtures/builder-kit-workflow-state", { owners: ["evals/static/test_workflow_skills.sh"], classification: "Builder Kit workflow-state fixtures" }],
|
|
84
99
|
["evals/fixtures/console-learning-projection", { owners: ["evals/integration/test_console_learning_projection.sh"], classification: "console learning projection fixtures" }],
|
|
@@ -88,6 +103,9 @@ const fixtureOwnerPolicies = new Map([
|
|
|
88
103
|
["evals/fixtures/pull-work-provider", { owners: ["evals/integration/test_pull_work_provider.sh"], classification: "work item provider normalization fixtures" }],
|
|
89
104
|
["evals/fixtures/pull-work-wip-shepherding", { owners: ["evals/static/test_workflow_skills.sh"], classification: "WIP shepherding state fixtures" }],
|
|
90
105
|
["evals/fixtures/surface-trust", { owners: ["evals/integration/test_workflow_sidecar_writer.sh"], classification: "Surface trust evidence fixtures" }],
|
|
106
|
+
["evals/fixtures/trust-reconcile-exploits", { owners: ["evals/integration/test_trust_reconcile_negatives.sh"], classification: "WS8 trust-reconcile anti-gaming exploit fixtures (frozen negative regressions)" }],
|
|
107
|
+
["evals/fixtures/trust-reconcile-mixed-bundle", { owners: ["evals/integration/test_trust_reconcile_mixed_bundle.sh"], classification: "WS8 trust-reconcile mixed-evidence end-to-end proof fixture" }],
|
|
108
|
+
["evals/fixtures/trust-reconcile-ws3", { owners: ["evals/integration/test_trust_reconcile_negatives.sh"], classification: "WS8 AC6 backward-compat fixture: real ws3-kit-dependencies-namespacing old-style bundle" }],
|
|
91
109
|
["evals/fixtures/usage-feedback", { owners: ["evals/integration/test_usage_feedback_import.sh", "evals/integration/test_usage_feedback_outcomes.sh", "evals/integration/test_usage_feedback_report.sh"], classification: "usage feedback import/outcome fixtures" }],
|
|
92
110
|
["evals/fixtures/veritas-governance-adapter", { owners: ["evals/integration/test_veritas_governance_adapter.sh"], classification: "Veritas governance adapter fixtures" }],
|
|
93
111
|
]);
|
|
@@ -98,13 +116,18 @@ const requiredUsageFeedbackFiles = [
|
|
|
98
116
|
const fixtureOwnershipSelfAuditRefs = new Set([
|
|
99
117
|
"evals/integration/test_fixture_retirement_audit.sh",
|
|
100
118
|
]);
|
|
101
|
-
const pythonInventoryExcludes = new Set([".git", ".flow-agents", "node_modules", ".venv", "dist", "__pycache__", ".pytest_cache", ".cache", "build", "integrations"]);
|
|
119
|
+
const pythonInventoryExcludes = new Set([".git", ".flow-agents", ".kontourai", "node_modules", ".venv", "dist", "__pycache__", ".pytest_cache", ".cache", "build", "integrations"]);
|
|
102
120
|
const pythonCommandScanRoots = ["README.md", "docs", "context", "skills", "prompts", "agents", "evals", "scripts", "packaging", "package.json"];
|
|
103
121
|
const allowedPythonCommandFiles = [
|
|
104
122
|
/^agents\/tool-explore-deps\.json$/,
|
|
105
123
|
/^evals\/results\//,
|
|
106
124
|
/^evals\/lib\/python\.sh$/,
|
|
107
125
|
/(^|\/)telemetry\/lib\/enrich\.sh$/,
|
|
126
|
+
// WS8 (AC10b): stop-goal-fit.js lists python/python3 in its first-token command-RECOGNIZER
|
|
127
|
+
// allowlist (so honest command evidence from a polyglot adopter project is not misclassified
|
|
128
|
+
// as prose). These are literal recognizer tokens, NOT a flow-agents Python dependency — no
|
|
129
|
+
// .py file is ever invoked — so the two byte-identical mirror copies are exempt here.
|
|
130
|
+
/^(context\/)?scripts\/hooks\/stop-goal-fit\.js$/,
|
|
108
131
|
];
|
|
109
132
|
function tryLoadJson(file, reporter) {
|
|
110
133
|
try {
|
package/console.telemetry.json
CHANGED
|
@@ -217,6 +217,28 @@
|
|
|
217
217
|
},
|
|
218
218
|
"limit": 10
|
|
219
219
|
},
|
|
220
|
+
{
|
|
221
|
+
"id": "builder.publish-learn",
|
|
222
|
+
"label": "Builder publish and learn",
|
|
223
|
+
"match": {
|
|
224
|
+
"attribute": "flow",
|
|
225
|
+
"includes": "builder.publish-learn"
|
|
226
|
+
},
|
|
227
|
+
"titleAttribute": "title",
|
|
228
|
+
"detailAttributes": {
|
|
229
|
+
"Project": "project",
|
|
230
|
+
"Repository": "repo",
|
|
231
|
+
"Task": "taskSlug",
|
|
232
|
+
"Status": "status",
|
|
233
|
+
"Outcome": "outcome",
|
|
234
|
+
"Phase": "phase",
|
|
235
|
+
"Skill": "skill",
|
|
236
|
+
"Agent": "agent",
|
|
237
|
+
"Model": "model",
|
|
238
|
+
"Observed": "observedAt"
|
|
239
|
+
},
|
|
240
|
+
"limit": 10
|
|
241
|
+
},
|
|
220
242
|
{
|
|
221
243
|
"id": "delivery",
|
|
222
244
|
"label": "Delivery workflows",
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# Workflow Artifact Contract
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
5
|
+
Workflow artifacts are the resumable local handoff surface between orchestrators, workers, verifiers, hooks, evals, and future sessions.
|
|
4
6
|
|
|
5
7
|
## Artifact Root
|
|
6
8
|
|
|
7
|
-
Use the task artifact root defined by the active distribution's bundle instructions. This source repo
|
|
9
|
+
Use the task artifact root defined by the active distribution's bundle instructions. This source repo writes and discovers non-durable task artifacts under `.kontourai/flow-agents/<slug>/` by default.
|
|
8
10
|
|
|
9
11
|
Do not hard-code a different root inside a skill or agent when the distribution has already defined one.
|
|
10
12
|
|
|
11
|
-
In this source tree, `.flow-agents/<slug>/` is the local runtime/session state root by default. Exported agent bundles may map the runtime root to a distribution-specific path through their bundle instructions; treat those paths as local runtime roots, not durable product documentation.
|
|
13
|
+
In this source tree, `.kontourai/flow-agents/<slug>/` is the local runtime/session state root by default. `.flow-agents/` is reserved for Flow Agents-owned durable config/install state; do not put non-durable workflow sessions there. Exported agent bundles may map the runtime root to a distribution-specific path through their bundle instructions; treat those paths as local runtime roots, not durable product documentation.
|
|
12
14
|
|
|
13
15
|
The artifact root is local working memory unless a workflow explicitly promotes or publishes it:
|
|
14
16
|
|
|
@@ -17,8 +19,39 @@ The artifact root is local working memory unless a workflow explicitly promotes
|
|
|
17
19
|
- Archive completed local records under `<artifact-root>/<slug>/archive/<date>/` when they are useful for audit or recovery but should not remain the active session.
|
|
18
20
|
- Promote stable decisions, usage guidance, release notes, and accepted architecture changes into durable docs such as `docs/`, ADRs, changelogs, or provider-backed descriptions/comments.
|
|
19
21
|
- Publish provider records only through the provider adapter or explicit publish-change step. Provider records may link back to local artifacts, but they do not make local runtime files durable by themselves.
|
|
20
|
-
- Do not commit local workflow runtime roots such as `.flow-agents/<slug>/` as durable policy unless a repository-specific contract explicitly says that artifact is promoted.
|
|
21
|
-
- Do not commit local workflow runtime roots such as `.flow-agents/<slug>/`; final acceptance must promote durable content before merge.
|
|
22
|
+
- Do not commit local workflow runtime roots such as `.kontourai/flow-agents/<slug>/` as durable policy unless a repository-specific contract explicitly says that artifact is promoted.
|
|
23
|
+
- Do not commit local workflow runtime roots such as `.kontourai/flow-agents/<slug>/`; final acceptance must promote durable content before merge.
|
|
24
|
+
|
|
25
|
+
## Session Archival Policy
|
|
26
|
+
|
|
27
|
+
Two archive locations exist and serve distinct purposes — do not conflate them:
|
|
28
|
+
|
|
29
|
+
- **Per-session, in-place history:** `<artifact-root>/<slug>/archive/<date>/` (above) holds
|
|
30
|
+
point-in-time snapshots of an *active* session's own records for audit/recovery while the
|
|
31
|
+
session remains in the active listing.
|
|
32
|
+
- **Whole-session archival (canonical):** `<artifact-root>/archive/<slug>/` is where an
|
|
33
|
+
entire terminal session is *moved out of the active listing*. This is the root-level
|
|
34
|
+
`archive/` that `src/cli/workflow-artifact-cleanup-audit.ts` already treats as a skipped
|
|
35
|
+
root entry (`SKIPPED_ROOT_ENTRIES`), so an archived session is not re-scanned as active
|
|
36
|
+
WIP. Use this location when relocating a completed session; do not invent a third path.
|
|
37
|
+
|
|
38
|
+
Retention rule (selection, not automatic deletion): a session is an **archival candidate**
|
|
39
|
+
when `workflow-artifact-cleanup-audit` classifies it `terminal_done` (a `delivered`/
|
|
40
|
+
`accepted`/`archived` session in phase `done`, or an `accepted`/`archived` session with no
|
|
41
|
+
open learning routing) **and** its `state.json` `updatedAt` is older than a retention
|
|
42
|
+
window (default 30 days). Use the audit tool's existing read-only classification as the
|
|
43
|
+
candidate selector — do not build new discovery logic. `active_wip`,
|
|
44
|
+
`active_learning_followup`, `cleanup_candidate`, and `invalid` sessions are **never**
|
|
45
|
+
archival candidates on age alone.
|
|
46
|
+
|
|
47
|
+
Procedure (documented; archival itself remains a deliberate, non-automated move — the audit
|
|
48
|
+
tool "does not delete, archive," by design):
|
|
49
|
+
|
|
50
|
+
1. `node build/src/cli/workflow-artifact-cleanup-audit.js <artifact-root>` to classify.
|
|
51
|
+
2. From the `terminal_done` bucket, select those whose `state.json` `updatedAt` predates the
|
|
52
|
+
retention window.
|
|
53
|
+
3. Move each selected `<artifact-root>/<slug>/` to `<artifact-root>/archive/<slug>/`
|
|
54
|
+
(a human-reviewed or explicitly-invoked move — not a silent background job).
|
|
22
55
|
|
|
23
56
|
## Persistence Integrity
|
|
24
57
|
|
|
@@ -34,7 +67,7 @@ records under concurrency.)
|
|
|
34
67
|
|
|
35
68
|
### Structured Sidecars
|
|
36
69
|
|
|
37
|
-
Markdown artifacts remain the human-readable handoff surface. JSON sidecars are the machine-readable recovery and gate surface. When a workflow creates or updates the corresponding information, write the sidecar beside the Markdown artifacts in `.flow-agents/<slug>/`.
|
|
70
|
+
Markdown artifacts remain the human-readable handoff surface. JSON sidecars are the machine-readable recovery and gate surface. When a workflow creates or updates the corresponding information, write the sidecar beside the Markdown artifacts in `.kontourai/flow-agents/<slug>/`.
|
|
38
71
|
|
|
39
72
|
Draft sidecars:
|
|
40
73
|
|
|
@@ -51,7 +84,7 @@ Sidecar rules:
|
|
|
51
84
|
- Keep `schema_version` at `1.0` until the schema changes incompatibly.
|
|
52
85
|
- Keep `task_slug` stable across all sidecars for a workflow.
|
|
53
86
|
- Prefer `npm run workflow:sidecar --` for creating and updating sidecars. If a harness cannot run the writer, produce equivalent JSON and validate it with `npm run workflow:validate-artifacts --`.
|
|
54
|
-
- Use `npm run workflow:sidecar -- ensure-session` when available to create or select the current `.flow-agents/<slug>/` session artifact before substantial work starts.
|
|
87
|
+
- Use `npm run workflow:sidecar -- ensure-session` when available to create or select the current `.kontourai/flow-agents/<slug>/` session artifact before substantial work starts.
|
|
55
88
|
- Update `state.json` at phase transitions.
|
|
56
89
|
- Create or update `acceptance.json` when planning defines or changes acceptance criteria.
|
|
57
90
|
- Create or update `evidence.json` when verification or evidence-gate records proof.
|