@kontourai/flow-agents 3.9.0 → 3.10.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 (106) hide show
  1. package/.github/workflows/ci.yml +8 -0
  2. package/CHANGELOG.md +17 -0
  3. package/README.md +1 -1
  4. package/build/src/cli/assignment-provider.js +4 -18
  5. package/build/src/cli/continuation-adapter.d.ts +10 -8
  6. package/build/src/cli/continuation-adapter.js +22 -4
  7. package/build/src/cli/validate-workflow-artifacts.js +21 -1
  8. package/build/src/cli/workflow-sidecar.js +15 -20
  9. package/build/src/cli/workflow.js +58 -11
  10. package/build/src/continuation-driver.d.ts +27 -3
  11. package/build/src/continuation-driver.js +97 -14
  12. package/build/src/index.d.ts +2 -2
  13. package/build/src/index.js +1 -1
  14. package/build/src/tools/build-universal-bundles.js +52 -25
  15. package/build/src/tools/validate-source-tree.js +5 -2
  16. package/context/scripts/hooks/lib/continuation-turn-authority.js +386 -0
  17. package/context/scripts/hooks/stop-goal-fit.js +244 -73
  18. package/context/scripts/hooks/workflow-steering.js +24 -15
  19. package/context/scripts/telemetry/lib/pricing.sh +18 -6
  20. package/context/scripts/telemetry/lib/transport.sh +139 -37
  21. package/context/scripts/telemetry/lib/usage.sh +282 -32
  22. package/context/scripts/telemetry/lib/usage_model_guard.sh +42 -0
  23. package/context/scripts/telemetry/telemetry.sh +76 -1
  24. package/docs/coordination-guide.md +21 -5
  25. package/docs/fixture-ownership.md +2 -1
  26. package/docs/public-workflow-cli.md +44 -0
  27. package/docs/spec/runtime-hook-surface.md +8 -4
  28. package/evals/acceptance/prove-capture-teeth-declared.sh +13 -0
  29. package/evals/acceptance/prove-capture-teeth.sh +13 -0
  30. package/evals/ci/run-baseline.sh +2 -0
  31. package/evals/fixtures/codex-legacy-agents/5273878130bdafc8a024a650bb5b66c9b003f1f859b5dc6e5b588cbf4ab23228.md +25 -0
  32. package/evals/fixtures/telemetry/usage-transcript-adversarial.jsonl +2 -0
  33. package/evals/fixtures/telemetry/usage-transcript-malformed-sandwich.jsonl +3 -0
  34. package/evals/fixtures/telemetry/usage-transcript-negative-tokens.jsonl +1 -0
  35. package/evals/fixtures/telemetry/usage-transcript-oversized-prefix.jsonl +201 -0
  36. package/evals/fixtures/telemetry/usage-transcript-oversized-tokens.jsonl +1 -0
  37. package/evals/fixtures/telemetry/usage-transcript-prefixed-secret-lower.jsonl +1 -0
  38. package/evals/fixtures/telemetry/usage-transcript-prefixed-secret-upper.jsonl +1 -0
  39. package/evals/fixtures/telemetry/usage-transcript-secret-model-jwt.jsonl +1 -0
  40. package/evals/fixtures/telemetry/usage-transcript-secret-model.jsonl +1 -0
  41. package/evals/fixtures/telemetry/usage-transcript-torn-trailing.jsonl +2 -0
  42. package/evals/integration/test_actor_identity.sh +63 -0
  43. package/evals/integration/test_assignment_provider_local_file.sh +34 -0
  44. package/evals/integration/test_builder_entry_enforcement.sh +5 -1
  45. package/evals/integration/test_bundle_install.sh +106 -17
  46. package/evals/integration/test_bundle_lifecycle.sh +17 -3
  47. package/evals/integration/test_captured_fail_reconciliation.sh +11 -0
  48. package/evals/integration/test_ci_actor_identity.sh +2 -2
  49. package/evals/integration/test_command_log_concurrency.sh +11 -0
  50. package/evals/integration/test_command_log_fork_classification.sh +11 -0
  51. package/evals/integration/test_command_log_integrity.sh +11 -0
  52. package/evals/integration/test_current_json_per_actor.sh +416 -21
  53. package/evals/integration/test_enforcer_expects_driven.sh +11 -0
  54. package/evals/integration/test_ensure_session_ownership_guard.sh +39 -0
  55. package/evals/integration/test_evidence_capture_hook.sh +25 -6
  56. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  57. package/evals/integration/test_flowdef_union_floor_regression.sh +11 -0
  58. package/evals/integration/test_gate_bypass_chain.sh +11 -0
  59. package/evals/integration/test_gate_lockdown.sh +10 -0
  60. package/evals/integration/test_goal_fit_ghost_session.sh +11 -0
  61. package/evals/integration/test_goal_fit_hook.sh +257 -9
  62. package/evals/integration/test_goal_fit_rederive.sh +11 -0
  63. package/evals/integration/test_install_merge.sh +141 -3
  64. package/evals/integration/test_liveness_conflict_injection.sh +39 -3
  65. package/evals/integration/test_liveness_heartbeat.sh +46 -9
  66. package/evals/integration/test_phase_map_and_gate_claim.sh +10 -1
  67. package/evals/integration/test_public_workflow_cli.sh +6 -6
  68. package/evals/integration/test_session_resume_roundtrip.sh +30 -0
  69. package/evals/integration/test_stop_hook_release.sh +111 -11
  70. package/evals/integration/test_telemetry_tool_usage.sh +394 -0
  71. package/evals/integration/test_verify_hold.sh +64 -1
  72. package/evals/integration/test_workflow_sidecar_writer.sh +10 -1
  73. package/evals/integration/test_workflow_steering_hook.sh +20 -4
  74. package/evals/run.sh +2 -0
  75. package/evals/static/test_universal_bundles.sh +123 -0
  76. package/package.json +1 -1
  77. package/packaging/README.md +21 -0
  78. package/packaging/codex-legacy-agents-fingerprints.json +33 -0
  79. package/scripts/README.md +2 -1
  80. package/scripts/audit-codex-legacy-agents.js +43 -0
  81. package/scripts/classify-codex-legacy-agents.js +71 -0
  82. package/scripts/hooks/evidence-capture.js +18 -6
  83. package/scripts/hooks/lib/actor-identity.js +62 -29
  84. package/scripts/hooks/lib/continuation-turn-authority.js +386 -0
  85. package/scripts/hooks/lib/current-pointer.js +114 -12
  86. package/scripts/hooks/lib/liveness-heartbeat.js +15 -12
  87. package/scripts/hooks/stop-goal-fit.js +244 -73
  88. package/scripts/hooks/workflow-steering.js +24 -15
  89. package/scripts/install-codex-home.sh +8 -7
  90. package/scripts/telemetry/lib/pricing.sh +18 -6
  91. package/scripts/telemetry/lib/transport.sh +71 -0
  92. package/scripts/telemetry/lib/usage.sh +282 -32
  93. package/scripts/telemetry/lib/usage_model_guard.sh +42 -0
  94. package/scripts/telemetry/telemetry.sh +76 -1
  95. package/src/cli/assignment-provider.ts +5 -26
  96. package/src/cli/builder-flow-runtime.test.mjs +93 -6
  97. package/src/cli/continuation-adapter.ts +33 -7
  98. package/src/cli/continuation-driver.test.mjs +741 -2
  99. package/src/cli/public-api.test.mjs +1 -0
  100. package/src/cli/validate-workflow-artifacts.ts +19 -1
  101. package/src/cli/workflow-sidecar.ts +18 -23
  102. package/src/cli/workflow.ts +71 -11
  103. package/src/continuation-driver.ts +125 -19
  104. package/src/index.ts +4 -0
  105. package/src/tools/build-universal-bundles.ts +61 -25
  106. package/src/tools/validate-source-tree.ts +5 -2
@@ -294,6 +294,72 @@ add_tool_data_and_emit_delegation() {
294
294
  echo "$event"
295
295
  }
296
296
 
297
+ # add_tool_usage_data — populates .usage on tool.invoke/tool.result events
298
+ # (preToolUse/postToolUse only; see add_event_specific_data's explicit
299
+ # permissionRequest exclusion) with the model/token/cost usage of the turn
300
+ # that produced this specific tool call (#568 slice 1). Same field shape
301
+ # session.usage already emits (model, input_tokens, output_tokens,
302
+ # cache_creation_input_tokens, cache_read_input_tokens, estimated_cost_usd,
303
+ # pricing_version) so console consumers reuse the same parsing path.
304
+ #
305
+ # Fallback tiers (never invent a number):
306
+ # 1. transcript-tail join succeeds (usage_last_turn_usage) -> full usage,
307
+ # attributable to this exact turn (not the session aggregate).
308
+ # 2. transcript join fails but hook.model is present (and not "unknown")
309
+ # -> .usage.model only, every token/cost field explicitly null.
310
+ # 3. neither available -> all .usage.* fields null (today's pre-existing,
311
+ # unchanged no-data state).
312
+ add_tool_usage_data() {
313
+ local event="$1"
314
+ local transcript_path hook_model
315
+ transcript_path=$(echo "$event" | jq -r '.hook.transcript_path // ""')
316
+ hook_model=$(echo "$event" | jq -r '.hook.model // ""')
317
+
318
+ local turn_usage
319
+ turn_usage=""
320
+ if [[ -n "$transcript_path" ]]; then
321
+ turn_usage=$(usage_last_turn_usage "$transcript_path")
322
+ fi
323
+
324
+ if [[ -n "$turn_usage" ]]; then
325
+ echo "$event" | jq -c --argjson tu "$turn_usage" '. + {
326
+ usage: {
327
+ model: $tu.model,
328
+ input_tokens: $tu.input_tokens,
329
+ output_tokens: $tu.output_tokens,
330
+ cache_creation_input_tokens: $tu.cache_creation_input_tokens,
331
+ cache_read_input_tokens: $tu.cache_read_input_tokens,
332
+ estimated_cost_usd: $tu.estimated_cost_usd,
333
+ pricing_version: $tu.pricing_version
334
+ }
335
+ }'
336
+ elif [[ -n "$hook_model" && "$hook_model" != "unknown" ]]; then
337
+ echo "$event" | jq -c --arg m "$hook_model" '. + {
338
+ usage: {
339
+ model: $m,
340
+ input_tokens: null,
341
+ output_tokens: null,
342
+ cache_creation_input_tokens: null,
343
+ cache_read_input_tokens: null,
344
+ estimated_cost_usd: null,
345
+ pricing_version: null
346
+ }
347
+ }'
348
+ else
349
+ echo "$event" | jq -c '. + {
350
+ usage: {
351
+ model: null,
352
+ input_tokens: null,
353
+ output_tokens: null,
354
+ cache_creation_input_tokens: null,
355
+ cache_read_input_tokens: null,
356
+ estimated_cost_usd: null,
357
+ pricing_version: null
358
+ }
359
+ }'
360
+ fi
361
+ }
362
+
297
363
  add_stop_data_and_emit_usage() {
298
364
  local event="$1" agent_name="$2"
299
365
  local duration_s
@@ -399,7 +465,16 @@ add_event_specific_data() {
399
465
  userPromptSubmit|UserPromptSubmit)
400
466
  add_user_prompt_data "$event" "$stdin_json"
401
467
  ;;
402
- preToolUse|PreToolUse|permissionRequest|PermissionRequest|postToolUse|PostToolUse|PostToolUseFailure)
468
+ preToolUse|PreToolUse|postToolUse|PostToolUse|PostToolUseFailure)
469
+ event=$(add_tool_data_and_emit_delegation "$event" "$event_type" "$stdin_json")
470
+ if [[ "$TELEMETRY_USAGE_TRACKING" == "true" ]]; then
471
+ event=$(add_tool_usage_data "$event")
472
+ fi
473
+ echo "$event"
474
+ ;;
475
+ permissionRequest|PermissionRequest)
476
+ # Explicit scope boundary (#568 slice 1): tool.permission_request does
477
+ # NOT receive .usage enrichment — only preToolUse/postToolUse do.
403
478
  add_tool_data_and_emit_delegation "$event" "$event_type" "$stdin_json"
404
479
  ;;
405
480
  stop|Stop|SessionEnd)
@@ -67,16 +67,18 @@ this section before the rest.
67
67
 
68
68
  ### The resolution chain
69
69
 
70
- `resolveActor(env)` (in `scripts/hooks/lib/actor-identity.js`, mirrored in `src/cli`) returns
70
+ `resolveActor(env)` (in the canonical `scripts/hooks/lib/actor-identity.js`, loaded by TypeScript consumers rather than mirrored) returns
71
71
  `{ actor, source }` by trying four sources in strict priority order:
72
72
 
73
73
  1. **`explicit-override`** — `FLOW_AGENTS_ACTOR` is set. The actor is the **bare token** you provided,
74
74
  verbatim. Source string: `"explicit-override"`.
75
- 2. **`runtime-session-id:<runtime>`** — the host runtime exposes a native session id (e.g. Claude
76
- Code). The actor is a **serialized triple** `runtime:session:host`. Source string:
75
+ 2. **`runtime-session-id:<runtime>`** — the host runtime exposes a native session id. For Codex,
76
+ `CODEX_THREAD_ID` is preferred over the legacy-compatible `CODEX_SESSION_ID`. The raw thread id
77
+ is never persisted: it becomes a domain-separated 96-bit `thread-<digest>` token. The actor is a **serialized triple** `runtime:session:host`. Source string:
77
78
  `"runtime-session-id:<runtime>"`.
78
- 3. **`process-ancestry`** — no session id; identity is derived by walking the process tree. Also a
79
- serialized triple. Source string: `"process-ancestry"`.
79
+ 3. **`process-ancestry`** — no session id; identity is derived from the parent process and start
80
+ time. It is serialized explicitly as `process-ancestry:anc-<digest>:<host>`, never as an
81
+ `unknown` runtime. Source string: `"process-ancestry"`; this remains unstable/advisory.
80
82
  4. **`unresolved`** — nothing worked. Source string: `"unresolved"`.
81
83
 
82
84
  The `"local"` literal default from the old design is **retired as an error, not a fallback** — a
@@ -116,6 +118,10 @@ A record written by a current session has `actor_key` and compares canonically;
116
118
  it falls back to `serializeActor`, reproducing pre-#291 behavior exactly. This one field is why the
117
119
  override/derived divergence no longer bites.
118
120
 
121
+ Historical `unknown:anc-*` values remain opaque, readable actor keys and are not rewritten or
122
+ aliased. Because ancestry is intentionally unstable, a process crossing this upgrade boundary may
123
+ need to release/reclaim rather than treating the old and new display forms as the same identity.
124
+
119
125
  ### Stable vs. unstable identity (this powers the publish gate)
120
126
 
121
127
  The `source` matters beyond attribution. An identity is **stable** if it came from
@@ -231,6 +237,16 @@ state rather than guesswork. A crash skips this — and that's fine: the livenes
231
237
  to `stale`, the assignment becomes `reclaimable`, and takeover (§10) handles it. Clean release just
232
238
  makes the common case instant instead of TTL-delayed.
233
239
 
240
+ **Ownership scoping (#440):** the Stop hook's evidence scanning, its non-terminal release (above), the
241
+ tool-activity liveness heartbeat, and the SessionStart/UserPromptSubmit re-ground banner all resolve
242
+ **only** from the stopping/heartbeating/steered actor's own per-actor `current/<actor>.json` pointer
243
+ when that actor is resolved — never from the shared legacy `current.json` or a repo-wide newest-mtime
244
+ scan, which would otherwise let a session co-located with another actor's work inherit that actor's gate
245
+ debt or steer onto its slug. Accepted gap: a resolved actor with no per-actor pointer yet (pre any
246
+ `workflow-sidecar` command in this session) is simply ungated/unbannered until its next sidecar command
247
+ establishes one — never gated on another actor's unrelated work. An unresolved actor keeps the pre-#440
248
+ legacy-fallback behavior unchanged (compat + anti-gaming: identity cannot be unset to escape the gate).
249
+
234
250
  ## 8. Guard point 3 — the verify-hold publish gate (the one hard fence)
235
251
 
236
252
  This is the **only** place coordination *blocks*, and it earned three fix iterations, so its design is
@@ -17,6 +17,7 @@ run `npm run validate:source --` and `npm run fixture:retirement-audit --`.
17
17
  | `evals/fixtures/backlog-provider-settings` | settings precedence fixtures | `evals/integration/test_effective_backlog_settings.sh` | Keep while backlog provider settings resolution supports global defaults and project overrides. |
18
18
  | `evals/fixtures/builder-kit-workflow-state` | Builder Kit workflow-state fixtures | `evals/static/test_workflow_skills.sh` | Keep while Builder Kit state contract and resume behavior are documented in workflow skill contracts. |
19
19
  | `evals/fixtures/console-learning-projection` | console learning projection fixtures | `evals/integration/test_console_learning_projection.sh` | Keep while learning projection supports correction and open-route examples. |
20
+ | `evals/fixtures/codex-legacy-agents` | exact historical Flow Agents-generated Codex AGENTS.md migration fixtures (#563) | `evals/integration/test_install_merge.sh` | Keep while the Codex home installer recognizes audited historical payload hashes, refuses before mutation, and provides explicit operator remediation without fuzzy ownership matching. |
20
21
  | `evals/fixtures/economics` | per-run kit-economics record fixtures (#349): a transcript with .message.usage blocks, state.json/acceptance.json/critique.json join sources, a session.usage event, and the golden expected kontour.console.economics record | `evals/integration/test_economics_record.sh` | Keep while the `kontour.console.economics` v0.1 record contract (docs/specs/economics-record-contract.md) is enforced; the golden proves cost-from-transcript, defects-from-critique, verdict-from-state, and the R7 co-required guard. |
21
22
  | `evals/fixtures/flow-kit-repository` | Flow Kit repository contract fixtures | `evals/integration/test_flow_kit_repository.sh`, `evals/integration/test_local_flow_kit_install.sh`, `evals/integration/test_runtime_adapter_activation.sh`, `evals/integration/test_activate_npx_context.sh`, `evals/integration/test_flow_kit_install_git.sh`, `evals/static/test_workflow_skills.sh` | Keep valid and invalid cases paired with the Flow Kit repository contract. |
22
23
  | `evals/fixtures/kit-conformance-levels` | K-level conformance and consumer-target derivation fixtures | `evals/integration/test_kit_conformance_levels.sh` | Keep while K-level derivation, degradation invariant, and consumer-target badge rules are tested. |
@@ -26,7 +27,7 @@ run `npm run validate:source --` and `npm run fixture:retirement-audit --`.
26
27
  | `evals/fixtures/pull-work-wip-shepherding` | WIP shepherding state fixtures | `evals/static/test_workflow_skills.sh` | Keep while pull-work documents personal versus global WIP behavior. |
27
28
  | `evals/fixtures/reconcile-preflight` | #356 reconcile-preflight shape fixtures not already covered by trust-reconcile-exploits (un-superseded disputed critique, standalone disputed session-local claim) | `evals/integration/test_reconcile_preflight.sh` | Keep while the local `reconcile-preflight` subcommand (#356) is proven against the two shapes trust-reconcile-exploits does not already fixture (un-superseded disputed critique, standalone disputed session-local claim); the other four shapes reuse trust-reconcile-exploits/trust-reconcile-mixed-bundle directly rather than forking near-duplicates. |
28
29
  | `evals/fixtures/surface-trust` | Surface trust evidence fixtures | `evals/integration/test_workflow_sidecar_writer.sh` | Keep while sidecar writer maps Surface trust evidence into workflow records. |
29
- | `evals/fixtures/telemetry` | hermetic Stop-hook usage transcript fixture (#defect-2026-07 telemetry-usage-cost-extraction): a multi-model JSONL transcript with .message.model + .message.usage token blocks proving real token/cost/model extraction end-to-end | `evals/integration/test_usage_cost.sh`, `evals/integration/test_telemetry_usage_pipeline.sh` | Keep while telemetry.sh's Stop-hook usage pipeline (usage_parse_transcript / usage_model_from_transcript_usage) is proven against a hermetic multi-model transcript rather than relying on live runtime data. |
30
+ | `evals/fixtures/telemetry` | hermetic Stop-hook usage transcript fixture (#defect-2026-07 telemetry-usage-cost-extraction): a multi-model JSONL transcript with .message.model + .message.usage token blocks proving real token/cost/model extraction end-to-end; also reused by the #568 slice 1 per-tool-call usage-enrichment eval (test_telemetry_tool_usage.sh), which adds usage-transcript-oversized-prefix.jsonl to prove the bounded tail-read | `evals/integration/test_usage_cost.sh`, `evals/integration/test_telemetry_usage_pipeline.sh`, `evals/integration/test_telemetry_tool_usage.sh` | Keep while telemetry.sh's Stop-hook usage pipeline (usage_parse_transcript / usage_model_from_transcript_usage / usage_last_turn_usage) is proven against hermetic multi-model and oversized-prefix transcripts rather than relying on live runtime data. |
30
31
  | `evals/fixtures/trust-reconcile-exploits` | WS8 trust-reconcile anti-gaming exploit fixtures (frozen negative regressions); also reused by the #356 local reconcile-preflight eval (same shapes, no forked copies) | `evals/integration/test_trust_reconcile_negatives.sh`, `evals/integration/test_reconcile_preflight.sh` | Keep while trust-reconcile.js enforces the WS8 iteration-2 soundness properties (no-label test_output, unwaived-assumed, status-misassertion, waiver-on-command); each fixture is a permanent negative regression. |
31
32
  | `evals/fixtures/trust-reconcile-mixed-bundle` | WS8 trust-reconcile mixed-evidence end-to-end proof fixture; also reused by the #356 reconcile-preflight eval as its CLEAN-BUNDLE (AC4) case | `evals/integration/test_trust_reconcile_mixed_bundle.sh`, `evals/integration/test_reconcile_preflight.sh` | Keep while the trust-reconcile manifest/classification/waiver contract (ADR 0020) is enforced; proves a mixed test_output + session-local + waived bundle passes the CI anchor. |
32
33
  | `evals/fixtures/trust-reconcile-ws3` | WS8 AC6 backward-compat fixture: real ws3-kit-dependencies-namespacing old-style bundle | `evals/integration/test_trust_reconcile_negatives.sh` | Keep while backward compatibility with pre-classification (all-test_output) bundles is asserted; proves an old-style bundle still FAILS the same way (no silent pass). |
@@ -167,6 +167,50 @@ state rollback or deletion is rejected when its append-only event history proves
167
167
  started. These local coordination records detect accidental or in-process rollback; they are not a
168
168
  cryptographic boundary against a process that can rewrite the entire artifact directory.
169
169
 
170
+ Immediately before spawning an adapter turn, the driver writes a transient, schema-versioned
171
+ `active-turn.json` beside its mission state and passes a raw 32-byte turn secret plus the path-safe,
172
+ signed run id in `FLOW_AGENTS_CONTINUATION_TURN_SECRET` and
173
+ `FLOW_AGENTS_CONTINUATION_RUN_ID` to that child. Only the secret's SHA-256 is persisted in the signed
174
+ record. The driver's schema-1-compatible mission state separately stores the ephemeral public-key
175
+ digest before the adapter starts, so replacing and correctly re-signing the entire active-turn
176
+ record with another key does not replace the signer anchor. The driver clears that digest with the
177
+ active-turn step on every completion, error, cleanup, terminal, waiting, and budget path. It replaces
178
+ every inherited continuation capability variable but preserves ordinary
179
+ `FLOW_AGENTS_ACTOR`. The shared actor
180
+ resolver remains ordinary: it never accepts continuation data as an identity override. When an
181
+ assignment-gated public workflow command finds that ordinary resolution does not match, it may use
182
+ only a live signed active-turn record bound to the exact session, turn secret, run id, assignment file and
183
+ actor struct, mission, adapter identity, expiry, and driver lock; it returns the signed assignment
184
+ identity only for that active-turn evidence gate. Pause, resume, release, cancel, and archive remain
185
+ control-plane operations: they require ordinary actor identity or their existing explicit external
186
+ authority and never accept this turn capability. The private key remains only in the driver process.
187
+ The Stop hook resolves the signed run id under the canonical artifact root and fully validates that
188
+ exact session before consulting ordinary current pointers. Once the base signed turn remains valid,
189
+ that exact session stays selected even if canonical Flow has become paused, blocked, completed, or
190
+ another canonical disposition; Stop never falls back to a conflicting actor or global pointer. It
191
+ securely validates canonical state against the run's definition and treats only canonical `active`
192
+ as authority to make the ordinary unfinished
193
+ canonical-gate warning advisory so the adapter can return control to the driver. It never advances
194
+ a Flow gate, releases assignment or liveness for a continuation-owned nonterminal run, or relaxes evidence, integrity, false-completion,
195
+ malformed-state, or configuration blocks. The record is removed when the child completes, errors,
196
+ or times out; parent identity changes leave it to expire instead of unlinking through a replacement.
197
+
198
+ This is cooperative same-user protection, not cryptographic filesystem isolation from a hostile
199
+ same-UID process. A same-UID process that can rewrite both mission state and authority records or
200
+ control the driver process remains outside this boundary. Within the cooperative boundary, the
201
+ mission digest anchors the driver's ephemeral signer, while descriptor reads, final-path inode rechecks, and realpath/device/inode parent
202
+ rechecks detect practical replacement races. Active-turn and lock records are capped at 16 KiB;
203
+ canonical assignment and continuation mission records are capped at a conservative 1 MiB so valid
204
+ provider metadata is not rejected. PID liveness is best-effort only: PID reuse and same-UID process control remain outside this
205
+ local coordination boundary.
206
+
207
+ The event stream records `turn_completed`, `gate_not_advanced`, `turn_failed`, and best-effort
208
+ `authority_cleanup_failed`. Cleanup failures are audited but do not replace the adapter or canonical
209
+ outcome. Failed turns carry
210
+ `failure_kind` of `timeout` or `adapter_error`; a completed adapter turn whose canonical run remains
211
+ active at the same current step records `gate_not_advanced`. These events describe driver execution only and do not
212
+ change canonical Flow state.
213
+
170
214
  ## Compatibility Doctor
171
215
 
172
216
  Run doctor through the exact isolated package helper defined above:
@@ -27,13 +27,15 @@ Every Flow Agents event has a canonical name that is runtime-neutral. Adapters m
27
27
  | --- | --- | --- | --- |
28
28
  | `agentSpawn` | A new agent session starts. Maps from `SessionStart` on Claude Code and Codex. | `hook_event_name`, `cwd` | `session_id`, `agent_id`, `model`, `runtime` |
29
29
  | `userPromptSubmit` | The user submits a new turn or message. Maps from `UserPromptSubmit`. | `hook_event_name` | `turn.prompt_text` (redacted by default), `cwd` |
30
- | `preToolUse` | Immediately before a tool call is executed. Maps from `PreToolUse`. | `hook_event_name`, `tool_name`, `tool_input` | `tool_id`, `cwd` |
30
+ | `preToolUse` | Immediately before a tool call is executed. Maps from `PreToolUse`. | `hook_event_name`, `tool_name`, `tool_input` | `tool_id`, `cwd`, `usage.model`, `usage.input_tokens`, `usage.output_tokens`, `usage.cache_creation_input_tokens`, `usage.cache_read_input_tokens`, `usage.estimated_cost_usd`, `usage.pricing_version` |
31
31
  | `permissionRequest` | The runtime is asking for permission to run a tool or action. Maps from `PermissionRequest`. | `hook_event_name`, `tool_name` | `tool_input`, `cwd` |
32
- | `postToolUse` | After a tool call completes (success or failure). Maps from `PostToolUse` and `PostToolUseFailure`. | `hook_event_name`, `tool_name`, `tool_response` | `tool_input`, `tool_output`, `error`, `cwd` |
32
+ | `postToolUse` | After a tool call completes (success or failure). Maps from `PostToolUse` and `PostToolUseFailure`. | `hook_event_name`, `tool_name`, `tool_response` | `tool_input`, `tool_output`, `error`, `cwd`, `usage.model`, `usage.input_tokens`, `usage.output_tokens`, `usage.cache_creation_input_tokens`, `usage.cache_read_input_tokens`, `usage.estimated_cost_usd`, `usage.pricing_version` |
33
33
  | `stop` | The agent is about to stop and return control to the user. Maps from `Stop` and `SessionEnd`. | `hook_event_name` | `stop_reason`, `cwd` |
34
34
  | `subagentStart` | A subagent or specialist delegate is spawning. Maps from `SubagentStart` (Claude Code). | `hook_event_name` | `agent_name`, `agent_type` |
35
35
  | `subagentStop` | A subagent or specialist delegate has stopped. Maps from `SubagentStop` (Claude Code). | `hook_event_name` | `agent_name`, `outcome` |
36
36
 
37
+ **`usage.*` on `preToolUse`/`postToolUse` (#568 slice 1).** These two events carry an optional `.usage` object — `model`, `input_tokens`, `output_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, `estimated_cost_usd`, `pricing_version` — sourced from the runtime transcript's LAST assistant turn (the turn that produced this specific tool call), joined via a bounded tail-read of `hook.transcript_path`. This is **per-turn, not a per-tool-call cost fraction**: multiple tool calls inside the same assistant turn (parallel tool_use blocks) report the *same* whole-turn usage figures — do not sum `estimated_cost_usd` across `tool.invoke`/`tool.result` rows within one turn, or cost will be double-counted; `session.usage`'s cumulative totals remain the authoritative aggregate. When the transcript join is unavailable but the runtime's `.model` hook field is present, only `usage.model` is populated and every token/cost field is explicitly `null` (never a guessed number); `.model` itself is best-effort on these two events (see §8.2), not a contractually guaranteed field. `tool.permission_request` (`permissionRequest`) is explicitly excluded from this enrichment.
38
+
37
39
  ### Redaction Defaults
38
40
 
39
41
  Telemetry channels redact sensitive payload fields before emission. Adapters must honor these defaults and must not log raw hook payloads without applying channel redaction.
@@ -518,11 +520,13 @@ All payloads are a single JSON object on stdin. Required and optional fields:
518
520
 
519
521
  | Canonical event | Required fields | Optional fields |
520
522
  |----------------|-----------------|-----------------|
521
- | `preToolUse` | `hook_event_name` | `tool_name`, `tool_input.path`, `tool_input.file_path`, `cwd` |
522
- | `postToolUse` | `hook_event_name` | `tool_name`, `tool_input.path`, `tool_input.file_path`, `tool_response`, `cwd` |
523
+ | `preToolUse` | `hook_event_name` | `tool_name`, `tool_input.path`, `tool_input.file_path`, `cwd`, `usage.model`, `usage.input_tokens`, `usage.output_tokens`, `usage.cache_creation_input_tokens`, `usage.cache_read_input_tokens`, `usage.estimated_cost_usd`, `usage.pricing_version` |
524
+ | `postToolUse` | `hook_event_name` | `tool_name`, `tool_input.path`, `tool_input.file_path`, `tool_response`, `cwd`, `usage.model`, `usage.input_tokens`, `usage.output_tokens`, `usage.cache_creation_input_tokens`, `usage.cache_read_input_tokens`, `usage.estimated_cost_usd`, `usage.pricing_version` |
523
525
  | `userPromptSubmit` | `hook_event_name` | `tool_input` (for subagent calls), `cwd` |
524
526
  | `stop` | `hook_event_name` | `cwd`, `stop_reason` |
525
527
 
528
+ `usage.*` on `preToolUse`/`postToolUse` above is emitted output, not required input — it is populated by the telemetry adapter from the runtime transcript's last assistant turn (or degrades to `usage.model`-only / fully-null per the documented fallback tiers in §1), not read from the hook's stdin payload. It is **per-turn**, not a per-tool-call cost fraction: consumers must not sum `estimated_cost_usd` across tool events within the same assistant turn (see §1). The runtime-native `.model` field these events derive part of this from is best-effort, not contractually guaranteed by any host today.
529
+
526
530
  `hook_event_name` is the **host-native** event name (e.g., `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`) or may be omitted — policy scripts read the canonical fields (`tool_input.path`, `cwd`) directly and do not require the event name field for their decisions.
527
531
 
528
532
  ### 8.3 Decision schema (stdout / exit code)
@@ -22,6 +22,19 @@
22
22
  set -uo pipefail
23
23
 
24
24
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
25
+ # #440 FIXTURE-GAP: this suite's fixtures were written before #440's per-actor ownership scoping
26
+ # and never establish a per-actor current pointer for the invoking actor -- under a RESOLVED
27
+ # ambient actor (ancestry-derived locally, GITHUB_RUN_ID-derived CI-runtime in CI), stop-goal-fit.js's
28
+ # analyze() (and evidence-capture.js's resolveArtifactDir) now scope to that actor's own
29
+ # (nonexistent) pointer and never reach the fixture-under-test at all. This suite is about
30
+ # anti-gaming/capture-teeth mechanics, not #440's ownership scoping, so forcing the documented
31
+ # test-only unresolved-actor escape hatch restores EXACTLY this suite's pre-#440 behavior (D3
32
+ # compat: an unresolved actor keeps the unchanged legacy-fallback/global-scan discovery every
33
+ # assertion below was written against). `env`-prefixed subprocess invocations below inherit this
34
+ # exported pair (env without -i does not clear the parent environment).
35
+ export FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED=1
36
+ export NODE_ENV=test
37
+
25
38
  GATE="$ROOT/scripts/hooks/stop-goal-fit.js"
26
39
 
27
40
  export FLOW_AGENTS_GOAL_FIT_MAX_BLOCKS=100000
@@ -12,6 +12,19 @@
12
12
  set -uo pipefail
13
13
 
14
14
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
15
+ # #440 FIXTURE-GAP: this suite's fixtures were written before #440's per-actor ownership scoping
16
+ # and never establish a per-actor current pointer for the invoking actor -- under a RESOLVED
17
+ # ambient actor (ancestry-derived locally, GITHUB_RUN_ID-derived CI-runtime in CI), stop-goal-fit.js's
18
+ # analyze() (and evidence-capture.js's resolveArtifactDir) now scope to that actor's own
19
+ # (nonexistent) pointer and never reach the fixture-under-test at all. This suite is about
20
+ # anti-gaming/capture-teeth mechanics, not #440's ownership scoping, so forcing the documented
21
+ # test-only unresolved-actor escape hatch restores EXACTLY this suite's pre-#440 behavior (D3
22
+ # compat: an unresolved actor keeps the unchanged legacy-fallback/global-scan discovery every
23
+ # assertion below was written against). `env`-prefixed subprocess invocations below inherit this
24
+ # exported pair (env without -i does not clear the parent environment).
25
+ export FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED=1
26
+ export NODE_ENV=test
27
+
15
28
  pass=0; fail=0
16
29
  _p(){ echo " ✓ $1"; pass=$((pass+1)); }
17
30
  _f(){ echo " ✗ $1"; fail=$((fail+1)); }
@@ -59,6 +59,7 @@ CHECKS=(
59
59
  "Telemetry doctor integration|bash evals/integration/test_telemetry_doctor.sh"
60
60
  "Usage and cost integration|bash evals/integration/test_usage_cost.sh"
61
61
  "Telemetry usage pipeline integration|bash evals/integration/test_telemetry_usage_pipeline.sh"
62
+ "Telemetry tool usage integration|bash evals/integration/test_telemetry_tool_usage.sh"
62
63
  "Economics record integration|bash evals/integration/test_economics_record.sh"
63
64
  "Learning review proposals integration|bash evals/integration/test_learning_review_proposals.sh"
64
65
  "Utterance check integration|bash evals/integration/test_utterance_check.sh"
@@ -152,6 +153,7 @@ LANE_RUNTIME_AND_KIT=(
152
153
  "Telemetry doctor integration"
153
154
  "Usage and cost integration"
154
155
  "Telemetry usage pipeline integration"
156
+ "Telemetry tool usage integration"
155
157
  "Economics record integration"
156
158
  "Learning review proposals integration"
157
159
  "Utterance check integration"
@@ -0,0 +1,25 @@
1
+ # Universal Agent Bundle (Codex)
2
+
3
+ This 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.
4
+
5
+ ## Shared Conventions
6
+
7
+ - `skills/`, `context/`, `powers/`, `prompts/`, `scripts/`, and `evals/` were copied from the canonical source.
8
+ - Cross-session task artifacts should live under `.kontourai/flow-agents`.
9
+ - Kiro-only hook wiring was stripped from exported non-Kiro agents to keep the package portable.
10
+
11
+ ## Exported Agents
12
+
13
+ - `tool-code-reviewer` — Delegate to me for code quality review. Analyzes readability, maintainability, patterns, DRY compliance, and produces structured review with severity levels. Separate from verification (build/test/lint).
14
+ - `tool-dependencies-updater` — Delegate to me for updating your project dependencies - checks latest versions, identifies outdated packages, and finds security advisories across npm, PyPI, Cargo, Maven/Gradle, Go, NuGet, Ruby, PHP, Swift, Dart, Docker, Helm, Terraform, and GitHub Actions
15
+ - `tool-explore-config` — Delegate to me for project configuration inspection - finds and summarizes configuration files and environment variables within a project
16
+ - `tool-explore-deps` — Delegate to me for Dependency analysis - parses package manifests to identify tech stack and dependencies
17
+ - `tool-explore-entry` — Delegate to me to find the Entry point of a project - locates main files, CLI commands, API routes, and exports
18
+ - `tool-explore-patterns` — Delegate to me for Pattern detection - identifies architectural patterns, frameworks, and coding conventions
19
+ - `tool-explore-structure` — Delegate to me to scout out the project structure - maps directory layout and identifies key folders in a codebase
20
+ - `tool-explore-tests` — Delegate to me to find and understand testing strategies - locates test files and understands testing strategy
21
+ - `tool-planner` — Delegate to me for codebase analysis and execution planning. Explores code, identifies patterns and dependencies, and writes plan/sidecar artifacts under .kontourai/flow-agents. No production file modifications.
22
+ - `tool-playwright` — Delegate to me for browser automation, testing, and debugging - loading real pages, testing navigation, checking accessibility via structured snapshots, evaluating scripts, and visual verification. Anything that would otherwise require a browser. Do NOT use for general web search or fetching content
23
+ - `tool-security-reviewer` — Delegate to me for security analysis. Checks OWASP Top 10, secrets detection, input validation, injection vulnerabilities, auth/authz, and rate limiting. Read-only analysis with shell for scanning tools.
24
+ - `tool-verifier` — Delegate to me for implementation verification. Read-only + shell for source code; writes review/evidence artifacts under .kontourai/flow-agents. Verifies acceptance criteria and produces PASS/FAIL/NOT_VERIFIED verdicts with evidence. No production file modifications.
25
+ - `tool-worker` — Delegate to me for writing and developing source code for a project. Works best when a detailed plan can be provided. NO access to web tools. Can be used in parallel for any coding tasks that require trusted access to the write and shell tools. WARNING: May spawn a `git worktree`
@@ -0,0 +1,2 @@
1
+ {"type":"user","message":{"role":"user","content":"benign filler line so the adversarial assistant line is not the only content"}}
2
+ {"type":"assistant","message":{"model":"SYSTEM PROMPT LEAK: the user's SSN is 000-00-0000 and API key sk-FAKE-adversarial-fixture","usage":{"input_tokens":"the user's private prompt said: attack the server at 203.0.113.7 with admin/hunter2","output_tokens":"also-not-a-number","cache_creation_input_tokens":{"nested":"object"},"cache_read_input_tokens":[1,2,3]}}}
@@ -0,0 +1,3 @@
1
+ {"type":"assistant","message":{"model":"bad-before" this line is not valid JSON at all
2
+ {"type": "assistant", "message": {"model": "claude-malformed-sandwich-safe", "usage": {"input_tokens": 10, "output_tokens": 20, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}}}
3
+ {this is not json either, noise after the valid line
@@ -0,0 +1 @@
1
+ {"type": "assistant", "message": {"model": "claude-opus-4-8", "usage": {"input_tokens": -500, "output_tokens": 20, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}}}