@kontourai/flow-agents 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/add-to-project.yml +15 -0
- package/.github/workflows/ci.yml +161 -0
- package/CHANGELOG.md +41 -0
- package/CONTEXT.md +5 -1
- package/README.md +19 -8
- package/build/src/builder-flow-run-adapter.d.ts +80 -0
- package/build/src/builder-flow-run-adapter.js +241 -0
- package/build/src/builder-flow-runtime.d.ts +16 -0
- package/build/src/builder-flow-runtime.js +290 -0
- package/build/src/cli/builder-run.d.ts +1 -0
- package/build/src/cli/builder-run.js +27 -0
- package/build/src/cli/effective-backlog-settings.js +70 -2
- package/build/src/cli/init.d.ts +34 -0
- package/build/src/cli/init.js +341 -61
- package/build/src/cli/kit.js +55 -12
- package/build/src/cli/pull-work-provider.js +346 -5
- package/build/src/cli/skill-drift-check.d.ts +1 -0
- package/build/src/cli/skill-drift-check.js +165 -0
- package/build/src/cli/telemetry-doctor.d.ts +37 -0
- package/build/src/cli/telemetry-doctor.js +53 -6
- package/build/src/cli/validate-hook-influence.js +37 -7
- package/build/src/cli/workflow-sidecar.d.ts +93 -8
- package/build/src/cli/workflow-sidecar.js +1175 -158
- package/build/src/cli.js +5 -0
- package/build/src/flow-kit/validate.d.ts +54 -34
- package/build/src/flow-kit/validate.js +237 -26
- package/build/src/index.d.ts +2 -0
- package/build/src/index.js +1 -0
- package/build/src/lib/console-connect-options.d.ts +97 -0
- package/build/src/lib/console-connect-options.js +199 -0
- package/build/src/lib/console-telemetry-validate.d.ts +49 -0
- package/build/src/lib/console-telemetry-validate.js +91 -0
- package/build/src/lib/flow-resolver.d.ts +54 -1
- package/build/src/lib/flow-resolver.js +112 -5
- package/build/src/lib/fs.d.ts +17 -0
- package/build/src/lib/fs.js +172 -0
- package/build/src/lib/local-artifact-root.d.ts +44 -1
- package/build/src/lib/local-artifact-root.js +131 -3
- package/build/src/runtime-adapters.d.ts +39 -3
- package/build/src/runtime-adapters.js +77 -31
- package/build/src/tools/build-universal-bundles.js +40 -2
- package/build/src/tools/codex-agent-routing.d.ts +2 -0
- package/build/src/tools/codex-agent-routing.js +49 -0
- package/build/src/tools/generate-context-map.js +1 -0
- package/build/src/tools/validate-source-tree.js +27 -1
- package/context/scripts/hooks/lib/kit-catalog.js +235 -0
- package/context/scripts/hooks/lib/runnable-command.js +177 -0
- package/context/scripts/hooks/stop-goal-fit.js +278 -48
- package/context/scripts/hooks/workflow-steering.js +121 -21
- package/context/scripts/package.json +3 -0
- package/context/scripts/telemetry/install-console-config.sh +25 -4
- package/context/scripts/telemetry/lib/config.sh +102 -12
- package/context/scripts/telemetry/lib/pricing.sh +50 -0
- package/context/scripts/telemetry/lib/session.sh +3 -0
- package/context/scripts/telemetry/lib/transport.sh +87 -0
- package/context/scripts/telemetry/lib/usage.sh +205 -4
- package/context/scripts/telemetry/telemetry.conf +6 -0
- package/context/scripts/telemetry/telemetry.sh +48 -0
- package/context/settings/workspace-backlog-provider-settings.example.json +48 -0
- package/docs/agent-usage-feedback-loop.md +35 -0
- package/docs/architecture-engine-and-kits.md +110 -0
- package/docs/context-map.md +2 -0
- package/docs/decisions/embeddable-engine.md +152 -0
- package/docs/decisions/index.md +3 -1
- package/docs/decisions/trust-ledger-retention.md +88 -0
- package/docs/decisions/workflow-enforcement.md +31 -9
- package/docs/fixture-ownership.md +3 -0
- package/docs/implementing-trust-reconciliation.md +129 -0
- package/docs/index.md +19 -9
- package/docs/integrations/flow-agents-console.md +167 -0
- package/docs/kit-authoring-guide.md +52 -21
- package/docs/spec/builder-flow-runtime.md +80 -0
- package/docs/spec/runtime-hook-surface.md +45 -1
- package/docs/specs/economics-record-contract.md +270 -0
- package/docs/specs/harness-capability-matrix.md +74 -0
- package/docs/specs/learning-review-proposals-contract.md +340 -0
- package/docs/specs/routing-efficiency-review.md +59 -0
- package/docs/verifiable-trust.md +74 -25
- package/docs/workflow-usage-guide.md +10 -0
- package/evals/acceptance/prove-capture-teeth.sh +132 -0
- package/evals/ci/antigaming-suite.sh +1 -0
- package/evals/ci/run-baseline.sh +72 -4
- package/evals/fixtures/economics/acceptance.json +12 -0
- package/evals/fixtures/economics/agents/tool-worker-1/events.jsonl +2 -0
- package/evals/fixtures/economics/agents/tool-worker-2/events.jsonl +2 -0
- package/evals/fixtures/economics/agents/tool-worker-3/events.jsonl +2 -0
- package/evals/fixtures/economics/agents/tool-worker-4/events.jsonl +1 -0
- package/evals/fixtures/economics/agents/tool-worker-5/events.jsonl +2 -0
- package/evals/fixtures/economics/critique.json +22 -0
- package/evals/fixtures/economics/expected-record.json +71 -0
- package/evals/fixtures/economics/session-usage-event.json +1 -0
- package/evals/fixtures/economics/state.json +11 -0
- package/evals/fixtures/economics/transcript.jsonl +3 -0
- package/evals/fixtures/hook-influence/cases.json +7 -7
- package/evals/fixtures/learning-review-proposals/balanced/economics.jsonl +6 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/economics.jsonl +5 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-1/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-2/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-3/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-4/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-5/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/economics.jsonl +6 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.json +30 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.md +66 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/gate-review.inquiries.json +26 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-5/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-6/trust.bundle +21 -0
- package/evals/fixtures/learning-review-proposals/repeat-window/economics.jsonl +6 -0
- package/evals/fixtures/learning-review-proposals/under-threshold/economics.jsonl +3 -0
- package/evals/fixtures/telemetry/usage-transcript-sample.jsonl +4 -0
- package/evals/fixtures/trust-reconcile-exploits/mcp-degrade.json +42 -0
- package/evals/integration/test_builder_entry_enforcement.sh +241 -0
- package/evals/integration/test_builder_step_producers.sh +18 -10
- package/evals/integration/test_bundle_install.sh +172 -0
- package/evals/integration/test_console_tenant_isolation.sh +167 -0
- package/evals/integration/test_critique_supersession_roundtrip.sh +4 -1
- package/evals/integration/test_dual_emit_flow_step.sh +10 -4
- package/evals/integration/test_economics_record.sh +674 -0
- package/evals/integration/test_effective_backlog_settings.sh +1 -1
- package/evals/integration/test_evidence_capture_hook.sh +17 -2
- package/evals/integration/test_exemption_usage_review.sh +198 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +83 -0
- package/evals/integration/test_flowdef_session_activation.sh +0 -1
- package/evals/integration/test_flowdef_session_history_preservation.sh +13 -3
- package/evals/integration/test_gate_lockdown.sh +7 -0
- package/evals/integration/test_gate_review_inquiry_records.sh +9 -1
- package/evals/integration/test_goal_fit_hook.sh +2031 -0
- package/evals/integration/test_hook_category_behaviors.sh +8 -1
- package/evals/integration/test_hook_influence_cases.sh +25 -1
- package/evals/integration/test_install_merge.sh +227 -2
- package/evals/integration/test_kit_conformance_levels.sh +6 -6
- package/evals/integration/test_learning_review_proposals.sh +329 -0
- package/evals/integration/test_liveness_conflict_injection.sh +26 -22
- package/evals/integration/test_liveness_console_relay.sh +166 -0
- package/evals/integration/test_liveness_heartbeat.sh +17 -17
- package/evals/integration/test_liveness_worktree_root.sh +575 -0
- package/evals/integration/test_phase_map_and_gate_claim.sh +6 -1
- package/evals/integration/test_publish_delivery.sh +331 -1
- package/evals/integration/test_pull_work_board.sh +200 -0
- package/evals/integration/test_pull_work_provider.sh +1 -1
- package/evals/integration/test_record_check.sh +378 -0
- package/evals/integration/test_routing_efficiency.sh +71 -0
- package/evals/integration/test_runtime_adapter_activation.sh +28 -0
- package/evals/integration/test_session_resume_roundtrip.sh +16 -19
- package/evals/integration/test_skill_drift_check.sh +870 -0
- package/evals/integration/test_telemetry.sh +445 -0
- package/evals/integration/test_telemetry_doctor.sh +66 -0
- package/evals/integration/test_telemetry_usage_pipeline.sh +228 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +30 -13
- package/evals/integration/test_trust_reconcile_trailer_diagnostic.sh +247 -0
- package/evals/integration/test_usage_cost.sh +61 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +1395 -0
- package/evals/integration/test_workflow_steering_hook.sh +157 -16
- package/evals/integration/test_workspace_settings.sh +176 -0
- package/evals/lib/env.sh +26 -0
- package/evals/lib/node.sh +8 -0
- package/evals/run.sh +29 -0
- package/evals/static/test_ci_integration_coverage.sh +115 -0
- package/evals/static/test_declared_scope_forms_documented.sh +114 -0
- package/evals/static/test_universal_bundles.sh +34 -0
- package/evals/static/test_validate_source_kit_asset_scope.sh +259 -0
- package/evals/static/test_workflow_skills.sh +1 -1
- package/kits/builder/flows/build.flow.json +9 -18
- package/kits/builder/flows/publish-learn.flow.json +5 -1
- package/kits/builder/kit.json +120 -0
- package/kits/builder/skills/deliver/SKILL.md +42 -0
- package/kits/builder/skills/evidence-gate/SKILL.md +12 -0
- package/kits/builder/skills/execute-plan/SKILL.md +9 -0
- package/kits/builder/skills/learning-review/SKILL.md +51 -0
- package/kits/builder/skills/plan-work/SKILL.md +17 -20
- package/kits/builder/skills/pull-work/SKILL.md +21 -0
- package/kits/builder/skills/release-readiness/SKILL.md +12 -0
- package/kits/knowledge/kit.json +9 -0
- package/kits/veritas-governance/docs/README.md +35 -7
- package/kits/veritas-governance/fixtures/exemption-review/mixed-fresh-stale.DECLARED.json +14 -0
- package/kits/veritas-governance/kit.json +14 -0
- package/kits/veritas-governance/skills/exemption-usage-review/SKILL.md +128 -0
- package/kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs +231 -0
- package/package.json +2 -2
- package/packaging/manifest.json +29 -0
- package/schemas/backlog-provider-settings.schema.json +13 -0
- package/schemas/workflow-state.schema.json +44 -0
- package/scripts/README.md +4 -0
- package/scripts/check-content-boundary.cjs +8 -1
- package/scripts/ci/trust-reconcile.js +136 -0
- package/scripts/hooks/codex-hook-adapter.js +77 -2
- package/scripts/hooks/evidence-capture.js +38 -5
- package/scripts/hooks/lib/codex-exit-code.js +316 -0
- package/scripts/hooks/lib/kit-catalog.js +235 -0
- package/scripts/hooks/lib/liveness-write.js +28 -1
- package/scripts/hooks/lib/local-artifact-paths.js +97 -1
- package/scripts/hooks/lib/runnable-command.js +177 -0
- package/scripts/hooks/lib/skill-drift.js +350 -0
- package/scripts/hooks/stop-goal-fit.js +278 -48
- package/scripts/hooks/workflow-steering.js +121 -21
- package/scripts/install-codex-home.sh +97 -47
- package/scripts/install-merge.js +72 -14
- package/scripts/install-owned-files.js +178 -0
- package/scripts/liveness/relay.sh +84 -0
- package/scripts/telemetry/economics-record.schema.json +145 -0
- package/scripts/telemetry/economics-record.sh +331 -0
- package/scripts/telemetry/install-console-config.sh +25 -4
- package/scripts/telemetry/learning-review-decide.sh +124 -0
- package/scripts/telemetry/learning-review-proposals.schema.json +161 -0
- package/scripts/telemetry/learning-review-proposals.sh +484 -0
- package/scripts/telemetry/lib/config.sh +102 -12
- package/scripts/telemetry/lib/pricing.sh +14 -6
- package/scripts/telemetry/lib/session.sh +3 -0
- package/scripts/telemetry/lib/transport.sh +133 -15
- package/scripts/telemetry/lib/usage.sh +121 -28
- package/scripts/telemetry/routing-efficiency.sh +0 -0
- package/scripts/telemetry/telemetry.conf +6 -0
- package/scripts/telemetry/telemetry.sh +48 -0
- package/src/builder-flow-run-adapter.ts +357 -0
- package/src/builder-flow-runtime.ts +348 -0
- package/src/cli/builder-flow-run-adapter.test.mjs +495 -0
- package/src/cli/builder-flow-runtime.test.mjs +213 -0
- package/src/cli/builder-run.ts +28 -0
- package/src/cli/codex-agent-routing.test.mjs +44 -0
- package/src/cli/codex-exit-code.test.mjs +207 -0
- package/src/cli/console-connect-options.test.mjs +329 -0
- package/src/cli/console-telemetry-validate.test.mjs +157 -0
- package/src/cli/effective-backlog-settings.ts +68 -2
- package/src/cli/flow-resolver-composition.test.mjs +72 -0
- package/src/cli/init.test.mjs +161 -0
- package/src/cli/init.ts +407 -62
- package/src/cli/kit-metadata-security.test.mjs +443 -0
- package/src/cli/kit.ts +50 -12
- package/src/cli/pull-work-provider.ts +377 -3
- package/src/cli/sidecar-pure-helpers.test.mjs +64 -0
- package/src/cli/skill-drift-check.ts +196 -0
- package/src/cli/telemetry-doctor.test.mjs +53 -0
- package/src/cli/telemetry-doctor.ts +50 -7
- package/src/cli/validate-hook-influence.ts +37 -6
- package/src/cli/workflow-sidecar.ts +1150 -151
- package/src/cli.ts +5 -0
- package/src/flow-kit/validate.ts +277 -38
- package/src/index.ts +19 -0
- package/src/lib/console-connect-options.ts +261 -0
- package/src/lib/console-telemetry-validate.ts +88 -0
- package/src/lib/flow-resolver.ts +117 -4
- package/src/lib/fs.ts +160 -0
- package/src/lib/local-artifact-root.ts +129 -3
- package/src/runtime-adapters.ts +113 -33
- package/src/tools/build-universal-bundles.ts +36 -2
- package/src/tools/codex-agent-routing.ts +48 -0
- package/src/tools/generate-context-map.ts +1 -0
- package/src/tools/validate-source-tree.ts +26 -1
|
@@ -66,6 +66,173 @@ Phase 1 delivers the "see everyone's active sessions, and your own usage in team
|
|
|
66
66
|
project/team insight views alongside individual ones — while coordination authority stays exactly
|
|
67
67
|
where it is today (local + GitHub).
|
|
68
68
|
|
|
69
|
+
#### Liveness relay — flow-agents side (shipped)
|
|
70
|
+
|
|
71
|
+
The flow-agents EMIT half of the relay ships as an **optional, off-by-default** sink
|
|
72
|
+
(`scripts/liveness/relay.sh`). When enabled, each liveness event (`claim`/`heartbeat`/`release`) is
|
|
73
|
+
mirrored to the Console as a `kontour.console.liveness` record over the **same transport core** the
|
|
74
|
+
telemetry mirror uses — `console_post_json` in `scripts/telemetry/lib/transport.sh` (endpoint-allow
|
|
75
|
+
gate, `Authorization: Bearer` + `x-console-tenant-id`, timeouts, detached fire), shared not forked.
|
|
76
|
+
|
|
77
|
+
It is **strictly local-first** (ADR 0012 §5): the relay fires *after* the durable local
|
|
78
|
+
`liveness/events.jsonl` write, fully detached and best-effort, so it can never block, slow, or fail
|
|
79
|
+
the local emit — and with the flag off or no console configured it is a true no-op. Enable it with:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
FLOW_AGENTS_CONSOLE_LIVENESS_RELAY=1
|
|
83
|
+
FLOW_AGENTS_CONSOLE_LIVENESS_ENDPOINT_URL=<console>/records # or FLOW_AGENTS_CONSOLE_URL / CONSOLE_TELEMETRY_URL + /records
|
|
84
|
+
CONSOLE_TELEMETRY_TOKEN=<bearer> (or FLOW_AGENTS_CONSOLE_TOKEN_FILE) · CONSOLE_TENANT_ID / FLOW_AGENTS_CONSOLE_TENANT
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Untrusted fields (actor, subjectId, branch, artifact_dir) are JSON-escaped by `jq` at record
|
|
88
|
+
construction, so hostile control bytes are `\u`-escaped, never emitted raw.
|
|
89
|
+
|
|
90
|
+
#### Economics record — flow-agents side (shipped, #349)
|
|
91
|
+
|
|
92
|
+
Every kit-driven run emits **one per-run economics record** — a `kontour.console.economics` v0.1 fact
|
|
93
|
+
carrying `cost`, `time`, `iterations`, and `defects` caught — so "flow kits save money and produce
|
|
94
|
+
more accurate results" becomes a **measurable, falsifiable** claim. It is the measurement substrate
|
|
95
|
+
for the Kit-economics telemetry initiative and is consumed by the baseline harness (#350), the
|
|
96
|
+
small-model headline (#409), and the console value view (console #117). The full field-by-field
|
|
97
|
+
contract is in [`docs/specs/economics-record-contract.md`](../specs/economics-record-contract.md).
|
|
98
|
+
|
|
99
|
+
The emitter (`scripts/telemetry/economics-record.sh`) is modeled byte-for-byte on the liveness relay:
|
|
100
|
+
it assembles the record with a **single `jq -c` filter** (valid JSON + `\u`-escaping of every untrusted
|
|
101
|
+
field — `task_slug`, model names, finding text) and hands off to the **same** `console_post_json`
|
|
102
|
+
transport core (endpoint-allow gate, `Bearer` + `x-console-tenant-id`, detached fire), shared not
|
|
103
|
+
forked. It is wired into the telemetry **stop path** (`add_stop_data_and_emit_usage` in
|
|
104
|
+
`scripts/telemetry/telemetry.sh`): right after the `session.usage` event is emitted, the economics
|
|
105
|
+
record is assembled from that event's `.usage` block (token/cost **ground truth** parsed by
|
|
106
|
+
`usage_parse_transcript` from the transcript — never re-estimated) joined with the run's review
|
|
107
|
+
sidecars (`critique.json` for defects, `state.json` for verdict / phase / iterations).
|
|
108
|
+
|
|
109
|
+
It is **strictly local-first** (ADR 0003 call 6): the record is **always** written to the local
|
|
110
|
+
economics log (`<TELEMETRY_DATA_DIR>/economics.jsonl`) *first*; only then is a **detached, opt-in,
|
|
111
|
+
best-effort** POST fired — a true no-op when the relay flag is off or no console is configured, and
|
|
112
|
+
`exit 0` on every failure path so it can never block, slow, or fail a run. flow-agents emits a per-run
|
|
113
|
+
**fact**, never a rollup — economics aggregation and the value view are console-side **projections**
|
|
114
|
+
over the immutable record stream (ADR 0003 call 3). Tenancy is stamped console-side from the verified
|
|
115
|
+
principal (call 2); the record's `tenant_id` is self-description only. Structurally, `cost` and
|
|
116
|
+
`defects` are **co-required** in the schema (the R7 Goodhart guard) — no consumer can render "cheaper"
|
|
117
|
+
without also rendering "and here is what it caught / missed." Enable the relay with:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
FLOW_AGENTS_CONSOLE_ECONOMICS_RELAY=1
|
|
121
|
+
FLOW_AGENTS_CONSOLE_ECONOMICS_ENDPOINT_URL=<console>/records # or FLOW_AGENTS_CONSOLE_URL / CONSOLE_TELEMETRY_URL + /records
|
|
122
|
+
CONSOLE_TELEMETRY_TOKEN=<bearer> (or FLOW_AGENTS_CONSOLE_TOKEN_FILE) · CONSOLE_TENANT_ID / FLOW_AGENTS_CONSOLE_TENANT
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Economics relay config (default-on, opt-out — #469)
|
|
126
|
+
|
|
127
|
+
Like the telemetry mirror (see the "Owner machine mirror" section of
|
|
128
|
+
[`docs/agent-usage-feedback-loop.md`](../agent-usage-feedback-loop.md)), the economics relay is
|
|
129
|
+
now **config-driven** rather than requiring the env vars above to be exported by hand: once a
|
|
130
|
+
Console telemetry sink is configured (`console_telemetry_url` / `console_telemetry_endpoint_url`
|
|
131
|
+
in a trusted conf), the relay turns on **automatically**. It rides the exact same trusted-conf
|
|
132
|
+
gate as the telemetry mirror — `.kontourai/telemetry-console.conf` or
|
|
133
|
+
`~/.flow-agents/telemetry-console.conf`, mode `600` and owned by the current user — and the same
|
|
134
|
+
`https://`/`localhost` transport allowlist (`console_telemetry_endpoint_allowed` in
|
|
135
|
+
`scripts/telemetry/lib/transport.sh`). The derived endpoint is `<console-origin>/records` — the
|
|
136
|
+
same shared kind-routed ingress the liveness relay uses, and **distinct** from the telemetry
|
|
137
|
+
mirror's `/api/telemetry/records` path. An explicit `console_economics_relay=0` conf key, or
|
|
138
|
+
`install-console-config.sh --no-economics-relay`, opts back out; `console_economics_endpoint_url`
|
|
139
|
+
overrides the derived endpoint when needed. Full resolution order is documented in
|
|
140
|
+
[`docs/specs/economics-record-contract.md`](../specs/economics-record-contract.md#enabling-the-relay-config-driven-opt-out--469).
|
|
141
|
+
|
|
142
|
+
On the console side, `POST /records` already accepts the `kontour.console.economics` kind into a
|
|
143
|
+
per-tenant `EconomicsStore`, read back via `GET /api/economics` (console #117) — deployed.
|
|
144
|
+
|
|
145
|
+
#### Attribution & pricing correctness (#487)
|
|
146
|
+
|
|
147
|
+
Two robustness fixes close the "why is my usage landing `unattributed` at `$0`?" gap that made the
|
|
148
|
+
first hosted-console economics rows misleading:
|
|
149
|
+
|
|
150
|
+
- **Attribution source.** The stop path stamps the record's `task_slug` and `phase` from the
|
|
151
|
+
**canonical** active-session file (`.kontourai/flow-agents/current.json`), falling back to the
|
|
152
|
+
legacy `.flow-agents/` path only when the canonical one is absent — so a run inside a live Builder
|
|
153
|
+
session lands attributed to the *real* slug/phase, not a stale placeholder. A run with **no** active
|
|
154
|
+
session is still recorded, but as `unattributed` by design: there is no session to attribute it to,
|
|
155
|
+
so that is a correct null, not a bug.
|
|
156
|
+
- **Pricing floor.** Cost is resolved from the **bundled** pricing registry
|
|
157
|
+
(`scripts/telemetry/pricing.json`) as the default source. `pricing.sh` validates that any fetched or
|
|
158
|
+
cached body is JSON carrying `current_version` + `versions` before trusting it, and never caches a
|
|
159
|
+
non-JSON body — so a poisoned cache (e.g. a console gate's HTML) or a `401` from an authed pricing
|
|
160
|
+
endpoint **falls through to the bundled registry** instead of degrading cost to `$0`. `config.sh` no
|
|
161
|
+
longer silently derives an authed console pricing URL that 401s unauthenticated; the default path
|
|
162
|
+
uses the valid bundled registry. Net effect: a model the bundled registry can price is **never**
|
|
163
|
+
recorded at `$0` because of a stale/hostile cache or an unreachable remote.
|
|
164
|
+
|
|
165
|
+
Both fixes are forward-looking — they correct what future runs emit. Historical `unattributed`/`$0`
|
|
166
|
+
rows already ingested are not retroactively rewritten; that is a console-side data operation.
|
|
167
|
+
|
|
168
|
+
The paired **console side** — ingesting `kontour.console.liveness`, the fleet OperatingState
|
|
169
|
+
projection (actors + held/reclaimable subjects + last-seen + per-session cost), and the ADR 0021 §4
|
|
170
|
+
janitor — is tracked in the console repo (**console #125**), and feeds the redesigned console
|
|
171
|
+
**Fleet** panel (its per-actor `coordinationState` is already wired to render the pills). Two-machine
|
|
172
|
+
visibility and the janitor sweep are console-side acceptance; this repo ships only the emit half.
|
|
173
|
+
|
|
174
|
+
#### Multi-tenant install runbook (adding tenant #2 to an existing install)
|
|
175
|
+
|
|
176
|
+
Installing a second tenant against the same Console instance is a **one-command** operation, with
|
|
177
|
+
either installer entry point:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
npx @kontourai/flow-agents init --telemetry-sink kontour-hosted-console --console-tenant <tenant-id> --console-token-file <path>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
bash install.sh <workspace> --telemetry-sink kontour-hosted-console --console-tenant <tenant-id> --console-token-file <path>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Both forms drive the same mechanism. `install.sh` collects the Console-related flags into
|
|
188
|
+
`CONSOLE_CONFIG_ARGS` (`install.sh:21,24-27`) and passes them straight through to the installed
|
|
189
|
+
`scripts/telemetry/install-console-config.sh` once the bundle is copied into place (`install.sh:54-55`).
|
|
190
|
+
`flow-agents init`'s `installBundle()` shells out to that same freshly-installed `install.sh` with the
|
|
191
|
+
equivalent flags (`src/cli/init.ts:385-408`); when a raw token value (rather than a file) is supplied,
|
|
192
|
+
`installBundle()` first writes it to a private `mkdtemp` file with `mode: 0o600` before passing
|
|
193
|
+
`--console-token-file` through (`src/cli/init.ts:390-398`), so a bearer token never sits in argv or an
|
|
194
|
+
unprotected file even transiently.
|
|
195
|
+
|
|
196
|
+
Interactive `flow-agents init` (run without `--yes`/`--headless`) offers a third way to reach this same
|
|
197
|
+
mechanism: a guided "Connect to Kontour Console? [Hosted/Local/Self-hosted/Skip]" prompt that collects
|
|
198
|
+
the token and tenant inline, then ends the install by auto-running the same reachability/token/tenant
|
|
199
|
+
check `flow-agents telemetry-doctor` performs headlessly, folded into a post-install summary. Power
|
|
200
|
+
users who prefer the explicit flag-driven forms above can still use them directly; scripted/CI installs
|
|
201
|
+
(`--yes`/`--headless`) are unaffected by the wizard.
|
|
202
|
+
|
|
203
|
+
**Token provisioning** is console-side, not flow-agents-side: see the "Ingest" bullet above — tokens
|
|
204
|
+
are configured through `CONSOLE_AUTH_TOKENS_JSON`. Issuing tenant #2 a token, or rotating an existing
|
|
205
|
+
one, is entirely console-repo scope; this repo only ever consumes a token file/value handed to it by
|
|
206
|
+
that process, it does not mint or manage tokens itself.
|
|
207
|
+
|
|
208
|
+
**Idempotency:** re-running the installer (e.g. to update a tenant's token after rotation, or to add
|
|
209
|
+
`--console-tenant` to an install that didn't originally have one) is safe to repeat. `set_config_key`
|
|
210
|
+
in `install-console-config.sh` (lines 88-100) rewrites `telemetry.conf` by awk-filtering out any
|
|
211
|
+
existing `key=` line for that key and appending the new value, so re-running with the same or updated
|
|
212
|
+
flags updates the same conf keys in place rather than duplicating or appending stale entries.
|
|
213
|
+
|
|
214
|
+
**Post-install verification:** run
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
flow-agents telemetry-doctor --dest <workspace> --json --headless
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
and check the `console` block of the report: `tokenConfigured` and `tenantConfigured`
|
|
221
|
+
(`src/cli/telemetry-doctor.ts:208-209`) should both be `true`, and `reachability`
|
|
222
|
+
(`src/cli/telemetry-doctor.ts:210`) should report `ok: true` for a healthy install. A misconfigured
|
|
223
|
+
tenant install typically shows `tenantConfigured: false` (no `--console-tenant` at install time) or
|
|
224
|
+
`reachability.ok: false` (endpoint unreachable, or the token was rejected).
|
|
225
|
+
|
|
226
|
+
**Isolation proof:** `evals/integration/test_console_tenant_isolation.sh` installs two distinct
|
|
227
|
+
tenants side by side through this exact `install-console-config.sh` → `config.sh` → `transport.sh`
|
|
228
|
+
path against a local HTTP stub, and asserts each tenant's outbound POST carries only its own
|
|
229
|
+
`x-console-tenant-id` and bearer token, never the other's. This proves the **client-side wire
|
|
230
|
+
contract** — that a correctly configured install sends the right headers for the right tenant. It
|
|
231
|
+
does **not** prove server-side tenant isolation or enforcement (that a token cannot be used to read
|
|
232
|
+
another tenant's data); that remains Console-repo scope (console #98–#100 tenant hardening, see
|
|
233
|
+
*Phase 2* below).
|
|
234
|
+
|
|
235
|
+
|
|
69
236
|
### Phase 2 — authoritative fleet coordination
|
|
70
237
|
|
|
71
238
|
Opt-in, for teams that want cross-machine assignment arbitration:
|
|
@@ -54,7 +54,7 @@ Required fields:
|
|
|
54
54
|
| `name` | Non-empty display name |
|
|
55
55
|
| `flows` | Non-empty list; each entry must have `id` and `path` |
|
|
56
56
|
|
|
57
|
-
Optional fields: `product_name`, `description`, `skills`, `docs`, `adapters`, `evals`, `assets`, `dependencies` (cross-kit dependency declarations; see [Cross-kit dependencies](#cross-kit-dependencies)). Optional fields list relative asset paths or objects with `id`, `path`, and optional `description`. `skills` and `docs` assets are activated by both adapters alongside flows. `adapters`, `evals`, and `assets` appear in diagnostics as `skipped_assets` (see the Activate section for the full per-adapter table).
|
|
57
|
+
Optional fields: `product_name`, `description`, `skills`, `docs`, `adapters`, `evals`, `assets`, `dependencies` (cross-kit dependency declarations; see [Cross-kit dependencies](#cross-kit-dependencies)), `workflow_triggers` (structured workflow steering keyed by engine trigger names such as `implementation-work-detected`), `hook_influence_expectations` (kit-owned required hook-influence fixture expectations), and `first_party` (legacy catalog/marketplace metadata; no runtime privilege). Optional asset fields list relative asset paths or objects with `id`, `path`, and optional `description`. `skills` and `docs` assets are activated by both adapters alongside flows. `adapters`, `evals`, and `assets` appear in diagnostics as `skipped_assets` (see the Activate section for the full per-adapter table).
|
|
58
58
|
|
|
59
59
|
## Minimal flow file
|
|
60
60
|
|
|
@@ -181,6 +181,14 @@ When installing through `npx @kontourai/flow-agents init` with the Codex runtime
|
|
|
181
181
|
npx @kontourai/flow-agents init --runtime codex --dest /path/to/workspace --activate-kits --yes
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
`init` activates no kits by default. To activate one or more catalog kits explicitly, pass `--activate-kit <kit-id>` once per selected kit. Declared kit dependencies are auto-included transitively during activation; for example, activating Builder also activates its Knowledge dependency:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx @kontourai/flow-agents init --runtime codex --dest /path/to/workspace --activate-kit builder --yes
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`--activate-kits` remains a backwards-compatible alias for selecting every catalog kit.
|
|
191
|
+
|
|
184
192
|
## Troubleshooting
|
|
185
193
|
|
|
186
194
|
Common validation errors and fixes are documented in the [Flow Kit Repository Contract](flow-kit-repository-contract.md#common-failures). The most frequent:
|
|
@@ -271,6 +279,42 @@ Where it is checked:
|
|
|
271
279
|
|
|
272
280
|
`dependencies` is metadata, not evaluable-gate content, so it does **not** affect K0/K1/K2 conformance scoring (see the K-levels section below).
|
|
273
281
|
|
|
282
|
+
### Structured workflow triggers
|
|
283
|
+
|
|
284
|
+
A kit may declare `workflow_triggers` to let the engine route matching prompts into a kit workflow. Triggers do not contain freeform prose. The engine owns the rendered wording and applies the same template to every kit. Every trigger steering field is an identifier and must match `^[a-z0-9]+(?:[.-][a-z0-9]+)*$`; malformed values fail closed and the trigger is skipped.
|
|
285
|
+
|
|
286
|
+
Shape:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"workflow_triggers": [
|
|
291
|
+
{
|
|
292
|
+
"id": "builder-build-work",
|
|
293
|
+
"when": "implementation-work-detected",
|
|
294
|
+
"target_flow_id": "builder.build",
|
|
295
|
+
"default_skill": "deliver",
|
|
296
|
+
"conditional_skills": [
|
|
297
|
+
{ "when": "user-requested-tdd", "skill": "tdd-workflow" }
|
|
298
|
+
],
|
|
299
|
+
"required_sequence": ["plan-work", "execute-plan", "review-work", "verify-work"],
|
|
300
|
+
"post_verify_targets": ["release-readiness", "learning-review"]
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
| Field | Rule |
|
|
307
|
+
|---|---|
|
|
308
|
+
| `id` | Required trigger id unique within the kit. |
|
|
309
|
+
| `when` | Required engine trigger category such as `implementation-work-detected` or `knowledge-capture-detected`. |
|
|
310
|
+
| `target_flow_id` | Optional flow id the session should stay on. |
|
|
311
|
+
| `default_skill` | Optional skill id to activate when no conditional skill applies. |
|
|
312
|
+
| `conditional_skills` | Optional list of `{ "when": "...", "skill": "..." }` identifier entries. |
|
|
313
|
+
| `required_sequence` | Optional ordered list of workflow skills or steps the engine should name as the required sequence. |
|
|
314
|
+
| `post_verify_targets` | Optional list of post-verification targets such as release readiness or learning capture. |
|
|
315
|
+
|
|
316
|
+
The retired `hint` field is invalid. `display_name` is not a workflow trigger field; keep human-readable names in catalog/listing metadata such as top-level `name` or `product_name`. Do not put natural-language steering instructions in kit metadata; express routing needs through the structured identifier fields above.
|
|
317
|
+
|
|
274
318
|
### When a kit "is" a Flow Agents Kit
|
|
275
319
|
|
|
276
320
|
A kit is a **Flow Agents Kit** when it satisfies both layers:
|
|
@@ -378,36 +422,23 @@ Every kit carries two independent badges:
|
|
|
378
422
|
| Axis | Values | Question answered |
|
|
379
423
|
|---|---|---|
|
|
380
424
|
| **Capability** (K-level) | K0 / K1 / K2 | What does the kit CONTAIN? (derived from assets) |
|
|
381
|
-
| **Trust** |
|
|
425
|
+
| **Trust** | verified / unverified | WHO vouches for it? |
|
|
382
426
|
|
|
383
|
-
A K2 kit can be `unverified`.
|
|
427
|
+
A K2 kit can be `unverified`. The levels are independent.
|
|
384
428
|
|
|
385
|
-
**Marketplace listing format**: `Works with: Flow (gates-only) | K1 |
|
|
429
|
+
**Marketplace listing format**: `Works with: Flow (gates-only) | K1 | Official`
|
|
430
|
+
|
|
431
|
+
`official` / `first_party` is catalog and marketplace metadata only. It grants no runtime privilege: every kit uses the same structured steering mechanism, and no capability decision keys off kit id or load provenance.
|
|
386
432
|
|
|
387
433
|
### Trust levels
|
|
388
434
|
|
|
389
435
|
| Level | Meaning | How it is assigned (v1) |
|
|
390
436
|
|---|---|---|
|
|
391
|
-
| `first-party` | Kontour authored, tested, and ships this kit in the `@kontourai/flow-agents` package. | Kit id is in the internal FIRST_PARTY_KIT_IDS allowlist in `src/flow-kit/validate.ts`. |
|
|
392
437
|
| `verified` | Reserved for a future third-party verification process. | Not yet implemented; the value is reserved but not granted to any kit today. |
|
|
393
|
-
| `unverified` | Default for all kits
|
|
438
|
+
| `unverified` | Default for all kits. | All current kits, including built-ins and third-party community kits. |
|
|
394
439
|
|
|
395
440
|
`unverified` says nothing about the quality of a kit — it only means Kontour has not vouched for it through one of the above channels.
|
|
396
441
|
|
|
397
|
-
### First-party kits (v1)
|
|
398
|
-
|
|
399
|
-
The first-party allowlist in v1 contains the kits authored by Kontour and distributed with the flow-agents package:
|
|
400
|
-
|
|
401
|
-
- `builder` — Builder Kit (shape, build, and deliver work)
|
|
402
|
-
- `knowledge` — Knowledge Kit (durable gated knowledge store)
|
|
403
|
-
|
|
404
|
-
Criteria for a kit to be first-party:
|
|
405
|
-
1. Its directory lives under `kits/` in the `kontourai/flow-agents` repository.
|
|
406
|
-
2. It is published as part of the `@kontourai/flow-agents` npm package.
|
|
407
|
-
3. Kontour owns and maintains the kit's content and release lifecycle.
|
|
408
|
-
|
|
409
|
-
Third-party forks, community kits, or kits published under a different npm package are NOT first-party even if they share a similar id. First-party is tied to provenance in this specific repository and package.
|
|
410
|
-
|
|
411
442
|
### Deferred: verified trust and cryptographic attestation (v2)
|
|
412
443
|
|
|
413
444
|
The `verified` value is reserved for a future verification process. The intended v2 path:
|
|
@@ -438,7 +469,7 @@ npm run kit -- inspect kits/builder
|
|
|
438
469
|
},
|
|
439
470
|
"targets": ["flow", "flow-agents"],
|
|
440
471
|
"third_party_extensions": [],
|
|
441
|
-
"trust": "
|
|
472
|
+
"trust": "unverified"
|
|
442
473
|
}
|
|
443
474
|
```
|
|
444
475
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Builder Flow Runtime
|
|
2
|
+
|
|
3
|
+
Builder Kit build sessions use Flow as the authority for steps, gates, transitions,
|
|
4
|
+
route-backs, and attempt limits. Flow Agents supplies the agent-facing adapter: it
|
|
5
|
+
starts or loads the canonical run, attaches the session trust bundle, and projects
|
|
6
|
+
Flow's current state into the existing workflow sidecars.
|
|
7
|
+
|
|
8
|
+
## Ownership Boundary
|
|
9
|
+
|
|
10
|
+
- Flow persists generated run state under `.kontourai/flow/runs/<run-id>/` and owns
|
|
11
|
+
Flow Definition evaluation.
|
|
12
|
+
- Builder Kit declares agent actions for each Flow step in `kit.json` under
|
|
13
|
+
`flow_step_actions`. Actions name skills or product operations; they do not alter
|
|
14
|
+
Flow transitions or gate outcomes.
|
|
15
|
+
- Flow Agents compiles its kit-level `uses_flow` extension and gate-less `done`
|
|
16
|
+
sentinel into one Flow-native effective definition. The generated definition is
|
|
17
|
+
content-addressed under `.kontourai/flow-agents/runtime-definitions/`; Flow then
|
|
18
|
+
owns all evaluation of that definition.
|
|
19
|
+
- Flow Agents writes product session artifacts under
|
|
20
|
+
`.kontourai/flow-agents/<slug>/`, produces Hachure trust bundles through Surface,
|
|
21
|
+
and projects the canonical Flow run into `state.json` and `current.json`.
|
|
22
|
+
- Durable Flow Definitions remain authored under `kits/builder/flows/`. Generated
|
|
23
|
+
state has no `.flow/runs` fallback.
|
|
24
|
+
|
|
25
|
+
The adapter contains no benchmark, task, filename, or grader-specific guidance.
|
|
26
|
+
It derives its next action from the persisted Flow step, the current gate's declared
|
|
27
|
+
expectations, and Builder Kit's structured action map.
|
|
28
|
+
|
|
29
|
+
## Entry And Synchronization
|
|
30
|
+
|
|
31
|
+
A Builder session must first be created at the Flow Definition's entry step by the
|
|
32
|
+
workflow sidecar. Start the canonical run with:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
flow-agents builder-run start --session-dir .kontourai/flow-agents/<slug>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`start` requires exactly one `state.work_item_refs` entry and uses that stable Work
|
|
39
|
+
Item reference as the Flow run subject. It is idempotent for an existing canonical
|
|
40
|
+
run. A direct primitive session without a Builder Flow stamp remains independent and
|
|
41
|
+
does not create a Flow run.
|
|
42
|
+
|
|
43
|
+
After a gate producer writes `trust.bundle`, the sidecar writer synchronizes an
|
|
44
|
+
existing run automatically. Interrupted clients can replay the projected command:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
flow-agents builder-run sync --session-dir .kontourai/flow-agents/<slug>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Synchronization is digest-idempotent. The same trust bundle is not attached twice.
|
|
51
|
+
Inspection loads the run without evaluating it, so invalid evidence is rejected
|
|
52
|
+
before Flow mutation.
|
|
53
|
+
|
|
54
|
+
## Trust Binding
|
|
55
|
+
|
|
56
|
+
Claims relevant to the current gate must carry
|
|
57
|
+
`metadata.workflow_subject_ref` equal to the persisted Flow run subject. Unrelated
|
|
58
|
+
claims are ignored for that gate. A relevant claim with a missing or different
|
|
59
|
+
subject reference is rejected; Flow is not mutated.
|
|
60
|
+
|
|
61
|
+
A failed gate claim may include a Flow classifier through
|
|
62
|
+
`record-gate-claim --status fail --route-reason <reason>`. Flow validates the reason
|
|
63
|
+
against the gate's `on_route_back` map and owns both the destination and attempt
|
|
64
|
+
budget. Flow Agents only projects the resulting attempt and maximum into `state.json`.
|
|
65
|
+
|
|
66
|
+
## Agent Projection
|
|
67
|
+
|
|
68
|
+
While a run is active, `state.json` contains:
|
|
69
|
+
|
|
70
|
+
- `flow_run`: canonical run identity, current step, open gates, run reference, and
|
|
71
|
+
route-back attempt information when present.
|
|
72
|
+
- `next_action.skills`: ordered Builder skills for the current step.
|
|
73
|
+
- `next_action.operations`: ordered non-skill product operations when present.
|
|
74
|
+
- `next_action.summary`: required gate claims derived from the Flow Definition.
|
|
75
|
+
- `next_action.command`: the exact idempotent synchronization command.
|
|
76
|
+
|
|
77
|
+
Workflow steering surfaces these fields on session start and prompt submission. The
|
|
78
|
+
Stop hook treats an unfinished canonical Flow run as active even during pickup or
|
|
79
|
+
planning, blocks a premature stop in block mode, and does not release its liveness
|
|
80
|
+
claim. A run is complete only when Flow reaches its terminal step.
|
|
@@ -45,6 +45,46 @@ Telemetry channels redact sensitive payload fields before emission. Adapters mus
|
|
|
45
45
|
|
|
46
46
|
These defaults are configurable via `TELEMETRY_CHANNEL_FULL_REDACT` and `TELEMETRY_CHANNEL_ANALYTICS_REDACT` environment variables.
|
|
47
47
|
|
|
48
|
+
### Attribution Fields (canonical, all adapters)
|
|
49
|
+
|
|
50
|
+
Because `context.cwd` is redacted on the analytics/console relay (the full local path must never
|
|
51
|
+
leave the machine), adapters cannot rely on the consumer deriving a project name from it. So the
|
|
52
|
+
attribution label is itself canonical and every adapter — harness **and** framework — must produce it:
|
|
53
|
+
|
|
54
|
+
| Field | Semantics | Redacted? | Producer requirement |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `context.project` | Coarse, path-free project label identifying the project a session is working in. | **No** (path-free by construction; safe to relay) | Adapters SHOULD populate `context.project` before emission when `context.project` is not already set, using the **canonical derivation precedence** below. Never send the full path. |
|
|
57
|
+
|
|
58
|
+
**Canonical derivation precedence (all adapters MUST resolve in this order, first match wins).** A
|
|
59
|
+
bare `basename(cwd)` is *not* sufficient — folder names differ between developers, clones, and
|
|
60
|
+
worktrees, so the same project would report inconsistent labels. Resolve most-stable-first:
|
|
61
|
+
|
|
62
|
+
1. `FLOW_AGENTS_PROJECT` — explicit operator override; always wins.
|
|
63
|
+
2. **Nearest project manifest name** walking up from the working dir — `package.json` `name` today
|
|
64
|
+
(monorepo-granular *and* committed, so every developer of the same package resolves the same
|
|
65
|
+
label). Other ecosystems (`pyproject.toml`, `Cargo.toml`, `go.mod`, …) extend this step — see
|
|
66
|
+
the consistency callout.
|
|
67
|
+
3. **Git remote `org/repo`** (from `remote.origin.url`, `.git` stripped, path-free) — repo-level
|
|
68
|
+
identity that is stable across clones and worktrees.
|
|
69
|
+
4. **Git toplevel directory basename** — the repo dir even when invoked from a worktree or subdir.
|
|
70
|
+
5. **`basename(cwd)`** — last resort.
|
|
71
|
+
|
|
72
|
+
Derivation is deterministic for a given working tree, so adapters SHOULD compute it once per
|
|
73
|
+
project per session and cache it rather than re-resolving per event.
|
|
74
|
+
|
|
75
|
+
Consumers (the console projection) prefer `context.project` and fall back to `basename(context.cwd)`
|
|
76
|
+
only when a non-redacted cwd is present. This keeps project attribution consistent across every
|
|
77
|
+
runtime — a Claude Code shell hook and an in-process Strands adapter derive the same label the same
|
|
78
|
+
way.
|
|
79
|
+
|
|
80
|
+
**Consistency callouts (features that cannot be identical across runtimes — surface, don't hide):**
|
|
81
|
+
- A runtime that exposes no working directory cannot resolve steps 2–5 and attributes to "unknown"
|
|
82
|
+
by design; that gap must be surfaced, not silently bucketed (§"Degradation when host lacks
|
|
83
|
+
trigger").
|
|
84
|
+
- Step 2's manifest matrix is inherently language-specific. Until an adapter's ecosystem manifest is
|
|
85
|
+
supported, it resolves from step 3 onward. Adapters MUST document which manifests they honor so the
|
|
86
|
+
attribution granularity difference is explicit, not surprising.
|
|
87
|
+
|
|
48
88
|
### Exit Code Protocol (Canonical Hook Scripts)
|
|
49
89
|
|
|
50
90
|
Canonical hook scripts in `scripts/hooks/` use the following exit code contract — originally derived from Kiro conventions and shared across all harness adapters via the adapter translation layer:
|
|
@@ -198,7 +238,11 @@ Flow Agents currently ships five canonical policy classes. Each policy class has
|
|
|
198
238
|
{ "command": "npm test", "observedResult": "pass", "exitCode": 0, "capturedAt": "2026-06-23T00:00:00Z", "source": "postToolUse-capture" }
|
|
199
239
|
```
|
|
200
240
|
|
|
201
|
-
**Exit-code handling (deterministic observation only)**: a clean integer exit code is host-dependent. The policy extracts the real exit code where the host surfaces one (`tool_response`/`tool_output` `.exitCode`/`.exit_code`/`.status`/`.code`/`.returnCode`, or top-level equivalents) and sets `observedResult` to `pass` iff that code is `0`. When no clean integer exit code is present, `exitCode` is recorded as `null` and `observedResult` is inferred *only* from deterministic failure signals — a non-empty `error`, a `success:false`/`failed:true`/`is_error:true` flag, or a non-empty stderr with no stdout. Plain stdout text is never scanned for the words "error"/"fail"; the model's narration is never consulted.
|
|
241
|
+
**Exit-code handling (deterministic observation only)**: a clean integer exit code is host-dependent. The policy extracts the real exit code where the host surfaces one (`tool_response`/`tool_output` `.exitCode`/`.exit_code`/`.status`/`.code`/`.returnCode`, or top-level equivalents) and sets `observedResult` to `pass` iff that code is `0`. When no clean integer exit code is present, `exitCode` is recorded as `null` and `observedResult` is inferred *only* from deterministic failure signals — a non-empty `error`, a `success:false`/`failed:true`/`is_error:true` flag, or a non-empty stderr with no stdout. When no clean integer exit code is present AND no deterministic failure signal exists, `exitCode` is recorded as `null` and `observedResult` is **`ambiguous`** — never `pass`. A `pass` always requires positive evidence (a clean integer exit code of 0; no host currently surfaces a positive success flag). Plain stdout text is never scanned for the words "error"/"fail"; the model's narration is never consulted.
|
|
242
|
+
|
|
243
|
+
**Consumer semantics of `ambiguous`**: `ambiguous` is non-confirming everywhere it is consumed — it never confirms a claimed pass and never itself resolves to a `verified` event, regardless of which runtime or extraction path produced it. `workflow-sidecar`'s trust-bundle build (`reduceCaptureLogByCommand` / `captureByCommand`) maps a command-log `ambiguous` capture to the existing canonical `not_verified` status — the same status record-check already uses for an ambiguous grep/diff exit — so the evidence item is `passing:false` and no `verified` event is ever emitted for it. `stop-goal-fit` (§2.3) further distinguishes the ORIGIN of an ambiguous capture: the absence-ambiguous case (a bare `grep`/`diff` that exited exactly `1`, which re-running yields no new information for) keeps its grep/diff-flavored `NOT_VERIFIED (ambiguous)` message and HARD_BLOCKs a terminal stop; a generic no-signal-ambiguous capture — no exit code observed at all, e.g. an unreadable Codex rollout banner or a bounded read that exceeded its caps — gets a grep/diff-free `NOT_VERIFIED —` message that is warn-only at a terminal stop but still blocks a non-terminal (in-flight) stop via the same `NOT_VERIFIED —` pattern. Neither path ever demotes to a confirmed `pass`.
|
|
244
|
+
|
|
245
|
+
**Codex host-banner carve-out**: On the Codex runtime, the host serializes the real exit code as host-generated prose (`Process exited with code N`) inside the tool result / session rollout rather than as a structured field (observed on codex-cli 0.142.5). The codex ADAPTER (`scripts/hooks/codex-hook-adapter.js`, scoped to the evidence-capture invocation on `postToolUse` only) extracts exactly that banner and injects it as a structured `tool_response.exitCode` BEFORE capture observes; this is a deterministic HOST signal (host-authored fixed format), not narration scanning — capture itself still never scans stdout or model narration. Extraction (`scripts/hooks/lib/codex-exit-code.js`) is **preamble-anchored**: the banner in a `function_call_output.output` string sits in the HOST-authored preamble, before the model's own stdout, which the codex CLI appends after a literal `Output:` delimiter; extraction matches the banner only in the portion BEFORE that delimiter (the FIRST match when no delimiter is present) and never scans the post-delimiter model stdout, so a command that deliberately prints a forged `Process exited with code 0` to its own stdout cannot override the real host-reported code. Reads are **head-anchored and bounded**: the target rollout line is located via a bounded backward scan (default 1MB), and only the first ~64KB of that line is read/parsed — the preamble/banner lives within the first few hundred bytes of the `output` field regardless of how much model stdout follows, so a >64KB flood of stdout after the banner can never displace it out of the read window; beyond either bound, extraction yields no signal (`null` → the `ambiguous` default above), never a guess. Extraction **correlates or declines** rather than blindly trusting the newest rollout entry: a payload-carried call_id match is authoritative when present; absent that, the newest `function_call_output` is cross-checked against its paired `function_call`'s command, and a resolvable match uses it while a resolvable **mismatch DECLINES to no signal** (never attributes another call's exit code); only when no correlation signal exists at all (the common single-call case) does it fall back to the newest banner. The `transcript_path` is resolved through `realpath` and required to be a regular file — and, when the codex sessions root itself is resolvable, contained within it — before any read. Extraction is fail-open throughout (missing/unreadable rollout, an unresolvable line, a declined correlation, or no banner → no injection → the `ambiguous` default above, subject to the consumer semantics described above).
|
|
202
246
|
|
|
203
247
|
**Decision contract**: Non-blocking. Always exits 0 and echoes stdin. Idempotent/append-only. Fail-open on any error — a capture failure must never block the agent or corrupt the log. Only records when an active workflow artifact dir resolves (otherwise there is nothing to anchor the log to).
|
|
204
248
|
|