@kontourai/flow-agents 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.github/dependabot.yml +23 -0
  2. package/.github/workflows/release-please.yml +31 -0
  3. package/.github/workflows/runtime-compat.yml +118 -0
  4. package/CHANGELOG.md +23 -0
  5. package/CONTRIBUTING.md +4 -0
  6. package/README.md +53 -10
  7. package/build/src/cli/init.js +215 -5
  8. package/build/src/cli/utterance-check.js +65 -1
  9. package/build/src/tools/build-universal-bundles.js +268 -0
  10. package/build/src/tools/filter-installed-packs.js +3 -0
  11. package/build/src/tools/validate-source-tree.js +5 -1
  12. package/context/scripts/telemetry/lib/config.sh +5 -1
  13. package/context/settings/flow-agents-settings.json +7 -0
  14. package/docs/context-map.md +1 -0
  15. package/docs/index.md +45 -4
  16. package/docs/integrations/conformance.md +246 -0
  17. package/docs/integrations/framework-adapter.md +275 -0
  18. package/docs/integrations/harness-install.md +213 -0
  19. package/docs/integrations/index.md +54 -0
  20. package/docs/north-star.md +2 -2
  21. package/docs/spec/runtime-hook-surface.md +472 -0
  22. package/docs/survey-utterance-check.md +211 -94
  23. package/docs/vision.md +45 -0
  24. package/evals/acceptance/run.sh +4 -2
  25. package/evals/acceptance/test_opencode_harness.sh +121 -0
  26. package/evals/acceptance/test_pi_harness.sh +98 -0
  27. package/evals/integration/test_bundle_install.sh +226 -1
  28. package/evals/integration/test_bundle_lifecycle.sh +641 -0
  29. package/evals/integration/test_utterance_check.sh +291 -44
  30. package/evals/run.sh +2 -0
  31. package/evals/static/test_universal_bundles.sh +137 -2
  32. package/integrations/strands/README.md +256 -0
  33. package/integrations/strands/example.py +74 -0
  34. package/integrations/strands/flow_agents_strands/__init__.py +27 -0
  35. package/integrations/strands/flow_agents_strands/hooks.py +194 -0
  36. package/integrations/strands/flow_agents_strands/policy.py +348 -0
  37. package/integrations/strands/flow_agents_strands/steering.py +172 -0
  38. package/integrations/strands/flow_agents_strands/telemetry.py +238 -0
  39. package/integrations/strands/pyproject.toml +38 -0
  40. package/integrations/strands/tests/__init__.py +0 -0
  41. package/integrations/strands/tests/test_hooks.py +304 -0
  42. package/integrations/strands/tests/test_policy.py +315 -0
  43. package/integrations/strands/tests/test_telemetry.py +184 -0
  44. package/integrations/strands-ts/README.md +224 -0
  45. package/integrations/strands-ts/bin/conformance-shim.mjs +257 -0
  46. package/integrations/strands-ts/package.json +53 -0
  47. package/integrations/strands-ts/src/hooks.ts +208 -0
  48. package/integrations/strands-ts/src/index.ts +22 -0
  49. package/integrations/strands-ts/src/policy.ts +345 -0
  50. package/integrations/strands-ts/src/telemetry.ts +251 -0
  51. package/integrations/strands-ts/test/test-policy.ts +322 -0
  52. package/integrations/strands-ts/test/test-telemetry.ts +226 -0
  53. package/integrations/strands-ts/tsconfig.json +20 -0
  54. package/package.json +7 -2
  55. package/packaging/conformance/README.md +142 -0
  56. package/packaging/conformance/fixtures/config-protection--allow-no-path.json +18 -0
  57. package/packaging/conformance/fixtures/config-protection--allow-safe-file.json +20 -0
  58. package/packaging/conformance/fixtures/config-protection--block-biome.json +20 -0
  59. package/packaging/conformance/fixtures/config-protection--block-eslintrc.json +20 -0
  60. package/packaging/conformance/fixtures/quality-gate--allow-no-path.json +17 -0
  61. package/packaging/conformance/fixtures/quality-gate--allow-nonexistent-file.json +19 -0
  62. package/packaging/conformance/fixtures/stop-goal-fit--allow-clean-cwd.json +17 -0
  63. package/packaging/conformance/fixtures/stop-goal-fit--block-strict-mode.json +23 -0
  64. package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +21 -0
  65. package/packaging/conformance/fixtures/workflow-steering--allow-no-state.json +16 -0
  66. package/packaging/conformance/fixtures/workflow-steering--inject-active-state.json +29 -0
  67. package/packaging/conformance/fixtures/workflow-steering--inject-subagent-steering.json +25 -0
  68. package/packaging/conformance/package.json +4 -0
  69. package/packaging/conformance/run-conformance.js +322 -0
  70. package/packaging/manifest.json +59 -0
  71. package/schemas/flow-agents-settings.schema.json +48 -0
  72. package/scripts/README.md +4 -0
  73. package/scripts/dogfood.js +16 -0
  74. package/scripts/hooks/opencode-hook-adapter.js +123 -0
  75. package/scripts/hooks/opencode-telemetry-hook.js +101 -0
  76. package/scripts/hooks/pi-hook-adapter.js +123 -0
  77. package/scripts/hooks/pi-telemetry-hook.js +105 -0
  78. package/scripts/hooks/run-hook.js +8 -0
  79. package/scripts/hooks/utterance-check.js +124 -22
  80. package/scripts/telemetry/lib/config.sh +5 -1
  81. package/src/cli/init.ts +219 -6
  82. package/src/cli/utterance-check.ts +71 -1
  83. package/src/tools/build-universal-bundles.ts +266 -0
  84. package/src/tools/filter-installed-packs.ts +3 -0
  85. package/src/tools/validate-source-tree.ts +5 -1
@@ -0,0 +1,23 @@
1
+ # Keeps pinned dependencies current; the runtime-compat workflow separately
2
+ # canaries LATEST runtime binaries and framework SDKs on a weekly cron.
3
+ version: 2
4
+ updates:
5
+ - package-ecosystem: npm
6
+ directory: "/"
7
+ schedule:
8
+ interval: weekly
9
+ groups:
10
+ dev-dependencies:
11
+ dependency-type: development
12
+ - package-ecosystem: npm
13
+ directory: "/integrations/strands-ts"
14
+ schedule:
15
+ interval: weekly
16
+ - package-ecosystem: pip
17
+ directory: "/integrations/strands"
18
+ schedule:
19
+ interval: weekly
20
+ - package-ecosystem: github-actions
21
+ directory: "/"
22
+ schedule:
23
+ interval: weekly
@@ -0,0 +1,31 @@
1
+ name: Release Please
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: write
11
+ pull-requests: write
12
+ actions: write
13
+
14
+ jobs:
15
+ release-please:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Run release-please
19
+ id: release
20
+ uses: googleapis/release-please-action@v4
21
+ with:
22
+ config-file: release-please-config.json
23
+ manifest-file: .release-please-manifest.json
24
+
25
+ # Tags created with GITHUB_TOKEN do not trigger tag-push workflows, so
26
+ # dispatch the existing publish pipeline at the new tag explicitly.
27
+ - name: Dispatch publish workflow
28
+ if: steps.release.outputs.release_created == 'true'
29
+ env:
30
+ GH_TOKEN: ${{ github.token }}
31
+ run: gh workflow run publish-npm.yml --repo "$GITHUB_REPOSITORY" --ref "${{ steps.release.outputs.tag_name }}"
@@ -0,0 +1,118 @@
1
+ name: Runtime Compatibility
2
+
3
+ # Weekly canary against the LATEST released versions of supported host
4
+ # runtimes and framework SDKs. Catches upstream drift (config schema changes,
5
+ # plugin/extension API changes, SDK breaking releases) before users do.
6
+ #
7
+ # Report-only: failures open/refresh a tracking issue; they do not block CI.
8
+ # Live model loops are out of scope here (no provider credentials in CI) —
9
+ # coverage is install + config acceptance + plugin/extension load + the
10
+ # mechanical hook chain, which is where every upstream break found so far
11
+ # (opencode config schema, plugin spawn semantics, pi extension parsing)
12
+ # would have surfaced.
13
+
14
+ on:
15
+ schedule:
16
+ - cron: "17 6 * * 1" # Mondays 06:17 UTC
17
+ workflow_dispatch:
18
+
19
+ permissions:
20
+ contents: read
21
+ issues: write
22
+
23
+ jobs:
24
+ harness-canary:
25
+ name: Harness canary (${{ matrix.runtime }})
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 20
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ include:
32
+ - runtime: opencode
33
+ install: npm install -g opencode-ai@latest
34
+ version: opencode --version
35
+ - runtime: pi
36
+ install: npm install -g @earendil-works/pi-coding-agent@latest
37
+ version: pi --version
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@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
44
+ with:
45
+ node-version: 24
46
+
47
+ - name: Install latest ${{ matrix.runtime }}
48
+ run: |
49
+ ${{ matrix.install }}
50
+ ${{ matrix.version }}
51
+
52
+ - name: Build bundles
53
+ run: npm run build:bundles
54
+
55
+ - name: Mechanical hook chain (lifecycle suite)
56
+ run: bash evals/integration/test_bundle_lifecycle.sh
57
+
58
+ - name: Live harness (binary-gated, provider assertions skip)
59
+ run: bash evals/acceptance/test_${{ matrix.runtime }}_harness.sh
60
+
61
+ framework-sdk-canary:
62
+ name: Framework SDK canary
63
+ runs-on: ubuntu-latest
64
+ timeout-minutes: 20
65
+ steps:
66
+ - name: Checkout
67
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
68
+
69
+ - name: Set up Node.js
70
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
71
+ with:
72
+ node-version: 24
73
+
74
+ - name: Set up Python
75
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
76
+ with:
77
+ python-version: "3.12"
78
+
79
+ - name: Root install + build
80
+ run: |
81
+ npm ci
82
+ npm run build
83
+
84
+ - name: Strands TS adapter against latest @strands-agents/sdk
85
+ run: |
86
+ cd integrations/strands-ts
87
+ npm view @strands-agents/sdk version
88
+ npm test
89
+ npm run conformance
90
+
91
+ - name: Strands Python adapter against latest strands-agents
92
+ run: |
93
+ python3 -m venv /tmp/sdk-canary
94
+ /tmp/sdk-canary/bin/pip install -q strands-agents
95
+ /tmp/sdk-canary/bin/pip show strands-agents | head -2
96
+ cd integrations/strands
97
+ /tmp/sdk-canary/bin/python -m unittest discover
98
+
99
+ report:
100
+ name: Open tracking issue on failure
101
+ runs-on: ubuntu-latest
102
+ needs: [harness-canary, framework-sdk-canary]
103
+ if: failure()
104
+ steps:
105
+ - name: Create or comment on tracking issue
106
+ env:
107
+ GH_TOKEN: ${{ github.token }}
108
+ run: |
109
+ TITLE="Runtime compatibility canary failed ($(date -u +%Y-%m-%d))"
110
+ BODY="The weekly runtime/SDK canary failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
111
+
112
+ One or more of: latest opencode, latest pi, latest @strands-agents/sdk, latest strands-agents (Python) broke install, hook-chain, harness, adapter tests, or conformance. Triage the run logs and either fix the adapter or document the gap in the conformance declaration."
113
+ EXISTING=$(gh issue list --repo "${{ github.repository }}" --search "Runtime compatibility canary failed in:title state:open" --json number --jq '.[0].number // empty')
114
+ if [ -n "$EXISTING" ]; then
115
+ gh issue comment "$EXISTING" --repo "${{ github.repository }}" --body "$BODY"
116
+ else
117
+ gh issue create --repo "${{ github.repository }}" --title "$TITLE" --body "$BODY" --label upstream-drift
118
+ fi
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0](https://github.com/kontourai/flow-agents/compare/v0.1.2...v0.2.0) (2026-06-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * engine contract 1.0, conformance kit, Strands rebind, integration docs ([fd94f58](https://github.com/kontourai/flow-agents/commit/fd94f583f52c874d901e06da0ee338830b3d469a))
9
+ * install lifecycle tests, dogfood command, collision marker fix ([a0fb2e3](https://github.com/kontourai/flow-agents/commit/a0fb2e31d897426db435801c8a637a9736d99ad1))
10
+ * live acceptance harnesses for opencode and pi ([181382b](https://github.com/kontourai/flow-agents/commit/181382b8dfe05cce41c0471a030e7d795950cd09))
11
+ * Strands TypeScript adapter — first native-import engine consumer ([0f387ab](https://github.com/kontourai/flow-agents/commit/0f387ab8e5a8b7f65e511af2fc33340f51e2d047))
12
+ * weekly runtime/SDK compatibility canary + dependabot ([9a371b1](https://github.com/kontourai/flow-agents/commit/9a371b1af86394fe1f7febebe3a35d3f05321f8e))
13
+
14
+
15
+ ### Fixes
16
+
17
+ * opencode.json — emit schema-valid config (instructions must be array) ([35a01ec](https://github.com/kontourai/flow-agents/commit/35a01ec508b2f99d4a7bca854e5f09740bac4fb3))
18
+ * opencode/pi hook chain — node resolution, stdin payloads, telemetry escape ([be4e4f8](https://github.com/kontourai/flow-agents/commit/be4e4f8d3b81fc7b67d6e45f4c9c1515407268a7))
19
+ * pi extension template escaping; parse-gate generated hook artifacts ([6fe40c5](https://github.com/kontourai/flow-agents/commit/6fe40c5079b8ee89a58c4dfecd6df2992c46cf59))
20
+
21
+
22
+ ### Documentation
23
+
24
+ * roadmap rows reflect the shipped utterance evidence-check hook ([#24](https://github.com/kontourai/flow-agents/issues/24)) ([617c755](https://github.com/kontourai/flow-agents/commit/617c75567b692c02564f457577d1ab3c01c1ea8e))
25
+
3
26
  ## 0.1.2
4
27
 
5
28
  - Source validation resolves the Flow CLI at `dist/cli.js` (with a
package/CONTRIBUTING.md CHANGED
@@ -34,6 +34,10 @@ gem install --user-install jekyll -v 3.9.5 jekyll-optional-front-matter \
34
34
  Set `FLOW_CLI_ROOT` to a Flow checkout or installed `@kontourai/flow` package
35
35
  root to enable full Flow Definition validation in `npm run validate:source`.
36
36
 
37
+ ## Releases
38
+
39
+ Releases are automated with release-please: merges to main accumulate into a release PR, and merging it tags the version and dispatches the npm publish workflow. Use conventional commit prefixes (feat:, fix:, docs:, chore:) so version inference works.
40
+
37
41
  ## Validation
38
42
 
39
43
  - `npm run validate:source` — source-tree integrity (paths, packs, manifests)
package/README.md CHANGED
@@ -2,48 +2,78 @@
2
2
 
3
3
  # Kontour Flow Agents
4
4
 
5
- **The discipline of Kontour Flow, inside the agent tools you already use.**
5
+ **A portable process-discipline layer for agentic work — canonical policies, evidence, and telemetry that compile to whatever hook surface a host exposes.**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/%40kontourai%2Fflow-agents)](https://www.npmjs.com/package/@kontourai/flow-agents)
8
8
  [![CI](https://github.com/kontourai/flow-agents/actions/workflows/ci.yml/badge.svg)](https://github.com/kontourai/flow-agents/actions/workflows/ci.yml)
9
9
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
10
10
  [![Node >= 22](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](package.json)
11
11
 
12
- [Documentation](https://kontourai.github.io/flow-agents/) · [Workflow Guide](docs/workflow-usage-guide.md) · [System Guidebook](docs/agent-system-guidebook.md) · [Kontour Flow](https://kontourai.github.io/flow/)
12
+ [Documentation](https://kontourai.github.io/flow-agents/) · [Workflow Guide](docs/workflow-usage-guide.md) · [System Guidebook](docs/agent-system-guidebook.md) · [Runtime Hook Spec](docs/spec/runtime-hook-surface.md) · [Kontour Flow](https://kontourai.github.io/flow/)
13
13
 
14
14
  </div>
15
15
 
16
16
  ---
17
17
 
18
- Coding agents are powerful and forgetful. They plan well, then drift. They skip verification when context gets crowded. They call partial work done, and after a compaction nobody — including the agent — can say where the work actually stands.
18
+ Agents are powerful and forgetful. They plan well, then drift. They skip verification when context gets crowded. They call partial work done, and after a compaction nobody — including the agent — can say where the work actually stands.
19
19
 
20
- Flow Agents wraps Codex, Claude Code, Kiro, and CI agents in an operating layer that makes long-running work inspectable: workflow skills that route requests into the right procedure, durable sidecar state that survives compaction and handoff, hooks that catch stop-short behavior, evidence gates before release decisions, and learning loops that feed corrections back into the system. [Kontour Flow](https://kontourai.github.io/flow/) owns the gate semantics underneath; Flow Agents makes that enforcement native inside agent harnesses.
20
+ Flow Agents addresses this with a process-discipline layer that sits between the user and the agent: four canonical policy classes (workflow steering, quality gate, stop-goal-fit, config protection), durable sidecar state that survives compaction and handoff, evidence gates before release decisions, and telemetry that feeds corrections back into the system. [Kontour Flow](https://kontourai.github.io/flow/) owns the gate semantics underneath; Flow Agents compiles those policies to whatever hook surface a host exposes — coding-agent harnesses today, agent frameworks next.
21
21
 
22
22
  **You ask for outcomes. The system supplies the path, the state, the checks, and the proof.**
23
23
 
24
24
  ## What you get
25
25
 
26
- - **One workflow across runtimes** — the same `idea → backlog → plan → build → review → verify → evidence → release → learning` path installs into Codex, Claude Code, and Kiro without rewriting it per tool.
26
+ - **One workflow across runtimes** — the same `idea → backlog → plan → build → review → verify → evidence → release → learning` path installs into Claude Code, Codex, Kiro, opencode, and pi without rewriting it per tool.
27
27
  - **Workflow skills** — `idea-to-backlog`, `pull-work`, `plan-work`, `execute-plan`, `review-work`, `verify-work`, `evidence-gate`, `release-readiness`, `learning-review`, and orchestrators like `deliver` and `fix-bug` that chain them.
28
28
  - **Durable workflow state** — schema-validated sidecars under `.flow-agents/` record acceptance criteria, evidence, critique, handoff, and learning, so any session can resume from recorded state instead of chat memory.
29
- - **Stop-short protection** — runtime hooks check sidecar state and route the agent back when required evidence is missing, instead of letting it summarize past the gap.
29
+ - **Four canonical policies** — workflow steering (phase reminders at each turn), quality gate (per-file checks after edits), stop-goal-fit (evidence check before the agent stops), and config protection (veto writes to linter/formatter configs). Each policy class has a canonical script under `scripts/hooks/` and compiles to the host's native hook format.
30
30
  - **Evidence over confidence** — important work ends with tests, browser checks, CI results, review findings, governance reports, or an explicit `NOT_VERIFIED` gap. Optional [Veritas](docs/veritas-integration.md) integration attaches repo-governance evidence without making it mandatory.
31
- - **Evals that keep the bundle honest** — static, integration, and behavioral eval lanes validate the skills, contracts, fixtures, and hook influence as the bundle evolves.
31
+ - **Evals that keep the bundle honest** — 77 integration and 36 static bundle assertions validate the skills, contracts, fixtures, and hook influence as the bundle evolves.
32
+
33
+ ## Flow Agents as a process-discipline layer
34
+
35
+ The four canonical policy classes are defined in the [Runtime Hook Surface spec](docs/spec/runtime-hook-surface.md) using a runtime-neutral vocabulary. Adapters translate them to whatever hook surface a host exposes:
36
+
37
+ | Policy Class | What it does | Hook trigger |
38
+ | --- | --- | --- |
39
+ | Workflow steering | Injects phase-transition reminders so the agent does not lose track of where it is in the delivery pipeline | `userPromptSubmit` |
40
+ | Quality gate | Runs format and lint checks immediately after edit tool calls | `postToolUse` |
41
+ | Stop-goal-fit | Warns (or blocks) when the agent is about to stop but required evidence is missing | `stop` |
42
+ | Config protection | Vetoes writes to linter and formatter configuration files | `preToolUse` |
43
+
44
+ The spec defines three conformance levels: **L0** (telemetry only), **L1** (steering + stop-goal-fit warning), and **L2** (all four policies with blocking). Claude Code and Codex are the current L2 reference implementations.
45
+
46
+ ## Runtime and support matrix
47
+
48
+ | Tier | Runtime | Ships | Tested | Conformance |
49
+ | --- | --- | --- | --- | --- |
50
+ | **Core harness** | Claude Code | install + hooks + bundle | 77 integration + 36 static assertions | L2 — reference implementation |
51
+ | **Core harness** | Codex | install + hooks + bundle | 77 integration + 36 static assertions | L2 — reference implementation |
52
+ | **Core harness** | Kiro | install + hooks + bundle | included in bundle assertions | L2 |
53
+ | **Core harness** | opencode | `.opencode/agents/`, `.opencode/skills/`, `.opencode/plugins/flow-agents.js`, `opencode.json` | included in bundle assertions | L1 — no prompt-submit hook; steering wired to `session.created` + `tool.execute.before` |
54
+ | **Core harness** | pi | `.pi/extensions/flow-agents.ts`, `.pi/skills/`, `AGENTS.md` | included in bundle assertions | L1 — no stop hook; stop-goal-fit unavailable |
55
+ | **Official framework adapter** | AWS Strands (Python) | `integrations/strands/` — `flow-agents-strands` PyPI package | 50 unit tests (no Strands SDK required) | Spike/preview — see [integrations/strands/README.md](integrations/strands/README.md) |
56
+ | **Conformance-certified** | Community / third-party | Self-certify using the conformance kit | — | Conformance kit in development; not yet shipped |
57
+
58
+ Honest gaps are documented in the artifacts: opencode has no native `prompt.submit`-equivalent event; Codex live hook influence is limited to installed-command and protocol coverage; pi has no `permissionRequest` equivalent and no stop hook. The [Runtime Hook Surface spec](docs/spec/runtime-hook-surface.md) names every gap explicitly.
32
59
 
33
60
  ## Install
34
61
 
35
62
  ```bash
36
- # guided install into your workspace
63
+ # guided install into your workspace (auto-detects runtime)
37
64
  npx @kontourai/flow-agents init --dest /path/to/workspace
38
65
 
39
66
  # headless, for CI or scripts
40
67
  npx @kontourai/flow-agents init --dest /path/to/workspace --telemetry-sink local-files --yes
41
68
 
42
- # with runtime-specific wiring and kit activation
69
+ # runtime-specific wiring
70
+ npx @kontourai/flow-agents init --runtime claude-code --dest /path/to/workspace --yes
43
71
  npx @kontourai/flow-agents init --runtime codex --dest /path/to/workspace --activate-kits --yes
72
+ npx @kontourai/flow-agents init --runtime opencode --dest /path/to/workspace --yes
73
+ npx @kontourai/flow-agents init --runtime pi --dest /path/to/workspace --yes
44
74
  ```
45
75
 
46
- Working from a checkout (for contributors) is the same flow: `npm install && npm run build`, then `node build/src/cli.js init --dest /path/to/workspace`.
76
+ Working from a checkout (for contributors): `npm install && npm run build`, then `node build/src/cli.js init --dest /path/to/workspace`.
47
77
 
48
78
  The installer copies the bundled agents, skills, context, scripts, evals, Flow Kit assets, and the Flow Agents-owned `console.telemetry.json` descriptor into the target workspace. Telemetry writes to local files by default; optional sinks mirror it to a local, hosted, or self-hosted Kontour Console (`--telemetry-sink local-kontour-console | kontour-hosted-console | user-hosted-console --console-url …`).
49
79
 
@@ -75,6 +105,19 @@ Use fix-bug. Reproduce the issue, diagnose root cause, plan the fix, implement i
75
105
 
76
106
  The [Workflow Usage Guide](docs/workflow-usage-guide.md) walks every stage with example prompts and expected behavior; the [Agent System Guidebook](docs/agent-system-guidebook.md) is the plain-language map of how the pieces fit.
77
107
 
108
+ ## Framework adapters
109
+
110
+ The same canonical policies that wire into coding-agent harnesses via file-based hook scripts can also wire into agent frameworks as in-process language-native packages.
111
+
112
+ `integrations/strands/` contains `flow-agents-strands`, a Python package implementing a Strands `HookProvider` that:
113
+ - emits the canonical telemetry taxonomy (`agentSpawn`, `preToolUse`, `postToolUse`, `stop`, etc.) to the same JSONL format as the harness adapters
114
+ - enforces config protection via `BeforeToolCallEvent` cancellation (the Strands equivalent of a blocking `preToolUse` hook)
115
+ - injects workflow steering context at agent construction via `steering_context()`
116
+
117
+ This is a spike/preview — 50 unit tests pass without requiring the Strands SDK, and the README documents 7 limitations honestly. It demonstrates that the policy engine is not harness-specific.
118
+
119
+ The [Runtime Hook Surface spec](docs/spec/runtime-hook-surface.md) documents the full framework adapter mapping, including VoltAgent, LangGraph, and OpenAI Agents SDK hook surfaces, and the minimum viable adapter pseudocode.
120
+
78
121
  ## Where Flow Agents fits
79
122
 
80
123
  Kontour AI shows the work behind AI. Each product stands alone; together they cohere:
@@ -13,12 +13,67 @@ const runtimeBundles = {
13
13
  codex: "codex",
14
14
  "claude-code": "claude-code",
15
15
  kiro: "kiro",
16
+ opencode: "opencode",
17
+ pi: "pi",
16
18
  };
19
+ // Stable marker present in every Flow Agents claude-code hook command.
20
+ // Used by scope-collision detection to identify an existing flow-agents install.
21
+ // Marker must be distinctive to Flow Agents generated settings. Sibling
22
+ // products from the same lineage ship identically named hook scripts such
23
+ // as claude-hook-adapter.js, so script filenames are NOT a safe marker.
24
+ export const COLLISION_MARKER = "Recording Flow Agents telemetry";
25
+ /**
26
+ * Check whether a user-level Claude Code settings file already contains
27
+ * Flow Agents hook commands. If it does, print a WARNING explaining that
28
+ * Claude Code merges user-level and project-level settings and runs ALL
29
+ * matching hooks, so having flow-agents in both places causes duplicate
30
+ * hook execution (double telemetry, double policy enforcement).
31
+ *
32
+ * The check does NOT block the install; it is advisory only.
33
+ *
34
+ * @param userSettingsFile Path to inspect (defaults to $HOME/.claude/settings.json;
35
+ * overridable via FLOW_AGENTS_USER_CLAUDE_SETTINGS env var for testability).
36
+ * @returns true if a collision was detected, false otherwise.
37
+ */
38
+ export function checkScopeCollision(userSettingsFile) {
39
+ const filePath = userSettingsFile
40
+ ?? process.env["FLOW_AGENTS_USER_CLAUDE_SETTINGS"]
41
+ ?? path.join(os.homedir(), ".claude", "settings.json");
42
+ if (!fs.existsSync(filePath))
43
+ return false;
44
+ let text;
45
+ try {
46
+ text = fs.readFileSync(filePath, "utf8");
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ if (!text.includes(COLLISION_MARKER))
52
+ return false;
53
+ console.warn(`\nWARNING: Flow Agents scope collision detected.\n` +
54
+ ` ${filePath}\n` +
55
+ `already contains Flow Agents hook commands (marker: ${COLLISION_MARKER}).\n` +
56
+ `\n` +
57
+ `Claude Code merges user-level (~/.claude/settings.json) and project-level\n` +
58
+ `(.claude/settings.json) settings, then runs ALL matching hooks from both files.\n` +
59
+ `Installing Flow Agents at the project level while it is also present at the\n` +
60
+ `user level will cause duplicate hook execution: telemetry events are recorded\n` +
61
+ `twice and policy hooks (workflow-steering, config-protection, quality-gate,\n` +
62
+ `stop-goal-fit) run twice per event.\n` +
63
+ `\n` +
64
+ `To resolve:\n` +
65
+ ` - Remove the hooks section from ${filePath} and rely solely on the\n` +
66
+ ` project-level .claude/settings.json installed by flow-agents init, OR\n` +
67
+ ` - Remove the project-level install and keep only the user-level one.\n` +
68
+ `\n` +
69
+ `The install will continue; resolve the collision before running Claude Code.\n`);
70
+ return true;
71
+ }
17
72
  function usage() {
18
73
  console.error(`usage: flow-agents init [options]
19
74
 
20
75
  Options:
21
- --runtime base|codex|claude-code|kiro
76
+ --runtime base|codex|claude-code|kiro|opencode|pi
22
77
  --dest PATH
23
78
  --telemetry-sink local-files|local-kontour-console|kontour-hosted-console|user-hosted-console
24
79
  --console-url URL
@@ -35,9 +90,9 @@ function normalizeRuntime(value) {
35
90
  return undefined;
36
91
  if (value === "claude")
37
92
  return "claude-code";
38
- if (value === "base" || value === "codex" || value === "claude-code" || value === "kiro")
93
+ if (value === "base" || value === "codex" || value === "claude-code" || value === "kiro" || value === "opencode" || value === "pi")
39
94
  return value;
40
- throw new Error(`unknown runtime '${value}'; expected base, codex, claude-code, or kiro`);
95
+ throw new Error(`unknown runtime '${value}'; expected base, codex, claude-code, kiro, opencode, or pi`);
41
96
  }
42
97
  function normalizeTelemetrySink(value) {
43
98
  if (value === "local-files" || value === "local-kontour-console" || value === "kontour-hosted-console" || value === "user-hosted-console" || value === "kontour-cloud" || value === "hosted-kontour-console")
@@ -67,7 +122,7 @@ async function questionHidden(prompt) {
67
122
  let value = "";
68
123
  const onData = (buffer) => {
69
124
  const text = buffer.toString("utf8");
70
- if (text === "\u0003") {
125
+ if (text === "") {
71
126
  stdout.write("\n");
72
127
  process.exit(130);
73
128
  }
@@ -78,7 +133,7 @@ async function questionHidden(prompt) {
78
133
  resolve(value);
79
134
  return;
80
135
  }
81
- if (text === "\u007f") {
136
+ if (text === "") {
82
137
  value = value.slice(0, -1);
83
138
  return;
84
139
  }
@@ -224,6 +279,18 @@ export async function main(argv = process.argv.slice(2)) {
224
279
  const headless = argv.includes("--yes") || argv.includes("--headless") || !process.stdin.isTTY;
225
280
  try {
226
281
  const options = headless ? headlessOptions(argv) : await interactiveOptions(argv);
282
+ // Scope-collision check for claude-code: Claude Code merges user-level
283
+ // (~/.claude/settings.json) and project-level (.claude/settings.json) settings
284
+ // and runs ALL matching hooks from both files. If a user-level settings file
285
+ // already contains flow-agents hooks, installing at the project level will
286
+ // cause duplicate hook execution. We warn but do not block.
287
+ //
288
+ // Codex note: Codex hooks live in .codex/hooks.json (project-level only).
289
+ // There is no well-known user-level codex hooks file in our install paths,
290
+ // so no collision check is needed for codex.
291
+ if (options.runtime === "claude-code") {
292
+ checkScopeCollision();
293
+ }
227
294
  const bundle = ensureBundle(options.runtime);
228
295
  const installed = installBundle(bundle, options);
229
296
  if (installed !== 0)
@@ -235,5 +302,148 @@ export async function main(argv = process.argv.slice(2)) {
235
302
  return 2;
236
303
  }
237
304
  }
305
+ // ---------------------------------------------------------------------------
306
+ // Dogfood subcommand
307
+ //
308
+ // `flow-agents dogfood --runtime claude-code [--dest PATH]`
309
+ //
310
+ // Writes only the hook-wiring artifacts for the specified runtime into the
311
+ // target directory (default: cwd). Unlike a full install, dogfood:
312
+ // - Does NOT rsync the full bundle (no agents/skills duplication).
313
+ // - Reads the generated settings/config from dist/<runtime>/ so the output
314
+ // cannot drift from what the bundle generates (DRY guarantee).
315
+ // - For claude-code: OMITS permissions.defaultMode and
316
+ // skipDangerousModePermissionPrompt (permissive defaults are for installed
317
+ // workspaces, not source repos).
318
+ // - Runs the same scope-collision warning as init.
319
+ // ---------------------------------------------------------------------------
320
+ function dogfoodUsage() {
321
+ console.error(`usage: flow-agents dogfood [options]
322
+
323
+ Options:
324
+ --runtime claude-code|codex|opencode|pi (required)
325
+ --dest PATH (default: cwd)
326
+ --yes, --headless
327
+ `);
328
+ }
329
+ function normalizeDogfoodRuntime(value) {
330
+ if (!value)
331
+ return undefined;
332
+ if (value === "claude" || value === "claude-code")
333
+ return "claude-code";
334
+ if (value === "codex" || value === "opencode" || value === "pi")
335
+ return value;
336
+ throw new Error(`dogfood: unsupported runtime '${value}'; expected claude-code, codex, opencode, or pi`);
337
+ }
338
+ /**
339
+ * Write the claude-code hook-wiring artifacts into dest.
340
+ * Reads dist/claude-code/.claude/settings.json (generated by build-bundles),
341
+ * strips the permissive-mode permission keys (defaultMode, skipDangerousModePermissionPrompt),
342
+ * and writes .claude/settings.json to dest.
343
+ */
344
+ function dogfoodClaudeCode(bundleRoot, dest) {
345
+ const sourcePath = path.join(bundleRoot, ".claude", "settings.json");
346
+ if (!fs.existsSync(sourcePath))
347
+ throw new Error(`dogfood: bundle settings missing: ${sourcePath}`);
348
+ const settings = JSON.parse(fs.readFileSync(sourcePath, "utf8"));
349
+ // Remove permissive defaults that are only appropriate for installed workspaces.
350
+ // These keys must not be present in the source repo's .claude/settings.json.
351
+ delete settings["permissions"];
352
+ delete settings["skipDangerousModePermissionPrompt"];
353
+ const outDir = path.join(dest, ".claude");
354
+ fs.mkdirSync(outDir, { recursive: true });
355
+ fs.writeFileSync(path.join(outDir, "settings.json"), `${JSON.stringify(settings, null, 2)}\n`, "utf8");
356
+ }
357
+ /**
358
+ * Write the codex hook-wiring artifacts into dest.
359
+ * Reads dist/codex/.codex/hooks.json and writes .codex/hooks.json to dest.
360
+ * The monolithic .codex/config.toml is not written here because it contains
361
+ * workspace settings (approvals_reviewer, features) that would override the
362
+ * developer's existing codex configuration. Only the hooks file is written.
363
+ */
364
+ function dogfoodCodex(bundleRoot, dest) {
365
+ const sourcePath = path.join(bundleRoot, ".codex", "hooks.json");
366
+ if (!fs.existsSync(sourcePath))
367
+ throw new Error(`dogfood: bundle hooks.json missing: ${sourcePath}`);
368
+ const hooks = fs.readFileSync(sourcePath, "utf8");
369
+ const outDir = path.join(dest, ".codex");
370
+ fs.mkdirSync(outDir, { recursive: true });
371
+ fs.writeFileSync(path.join(outDir, "hooks.json"), hooks, "utf8");
372
+ }
373
+ /**
374
+ * Write the opencode hook-wiring artifacts into dest.
375
+ * Reads dist/opencode/.opencode/plugins/flow-agents.js and opencode.json,
376
+ * and writes them into dest. These are the minimal hook-wiring files; the
377
+ * full skill/agent tree is not copied.
378
+ */
379
+ function dogfoodOpencode(bundleRoot, dest) {
380
+ const pluginSource = path.join(bundleRoot, ".opencode", "plugins", "flow-agents.js");
381
+ const configSource = path.join(bundleRoot, "opencode.json");
382
+ if (!fs.existsSync(pluginSource))
383
+ throw new Error(`dogfood: bundle plugin missing: ${pluginSource}`);
384
+ const pluginDir = path.join(dest, ".opencode", "plugins");
385
+ fs.mkdirSync(pluginDir, { recursive: true });
386
+ fs.copyFileSync(pluginSource, path.join(pluginDir, "flow-agents.js"));
387
+ // Write opencode.json only if it does not already exist to avoid clobbering
388
+ // any workspace-specific opencode configuration.
389
+ const destConfig = path.join(dest, "opencode.json");
390
+ if (!fs.existsSync(destConfig) && fs.existsSync(configSource)) {
391
+ fs.copyFileSync(configSource, destConfig);
392
+ }
393
+ }
394
+ /**
395
+ * Write the pi hook-wiring artifacts into dest.
396
+ * Reads dist/pi/.pi/extensions/flow-agents.ts and writes it to dest.
397
+ * The extension is the only hook-wiring file needed for pi.
398
+ */
399
+ function dogfoodPi(bundleRoot, dest) {
400
+ const extSource = path.join(bundleRoot, ".pi", "extensions", "flow-agents.ts");
401
+ if (!fs.existsSync(extSource))
402
+ throw new Error(`dogfood: bundle extension missing: ${extSource}`);
403
+ const extDir = path.join(dest, ".pi", "extensions");
404
+ fs.mkdirSync(extDir, { recursive: true });
405
+ fs.copyFileSync(extSource, path.join(extDir, "flow-agents.ts"));
406
+ }
407
+ export async function mainDogfood(argv = process.argv.slice(2)) {
408
+ if (argv.includes("--help") || argv.includes("-h")) {
409
+ dogfoodUsage();
410
+ return 0;
411
+ }
412
+ const args = parseArgs(argv);
413
+ try {
414
+ const runtimeRaw = flagString(args.flags, "runtime");
415
+ const runtime = normalizeDogfoodRuntime(runtimeRaw);
416
+ if (!runtime) {
417
+ console.error("dogfood: --runtime is required (claude-code, codex, opencode, or pi)");
418
+ dogfoodUsage();
419
+ return 2;
420
+ }
421
+ const dest = path.resolve(flagString(args.flags, "dest") ?? process.cwd());
422
+ // Ensure the bundle for the requested runtime is built.
423
+ const bundleRuntime = runtime;
424
+ const bundleRoot = ensureBundle(bundleRuntime);
425
+ // Scope-collision check: warn if user-level claude settings already has flow-agents.
426
+ // Codex: no user-level hooks file in our install paths — skip with note above.
427
+ if (runtime === "claude-code") {
428
+ checkScopeCollision();
429
+ }
430
+ // Write only the hook-wiring artifacts, not the full bundle.
431
+ fs.mkdirSync(dest, { recursive: true });
432
+ if (runtime === "claude-code")
433
+ dogfoodClaudeCode(bundleRoot, dest);
434
+ else if (runtime === "codex")
435
+ dogfoodCodex(bundleRoot, dest);
436
+ else if (runtime === "opencode")
437
+ dogfoodOpencode(bundleRoot, dest);
438
+ else if (runtime === "pi")
439
+ dogfoodPi(bundleRoot, dest);
440
+ console.log(`Flow Agents dogfood hooks wired for ${runtime} in ${dest}`);
441
+ return 0;
442
+ }
443
+ catch (error) {
444
+ console.error(`flow-agents dogfood: ${error.message}`);
445
+ return 2;
446
+ }
447
+ }
238
448
  if (import.meta.url === `file://${process.argv[1]}`)
239
449
  process.exit(await main());