@kontourai/flow-agents 3.3.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/add-to-project.yml +15 -0
- package/.github/workflows/ci.yml +161 -0
- package/CHANGELOG.md +48 -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 +56 -3
- package/build/src/lib/flow-resolver.js +151 -11
- 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 +101 -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 +153 -10
- 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
|
@@ -91,6 +91,25 @@
|
|
|
91
91
|
* 0022 addendum, part 4) — see
|
|
92
92
|
* resolveEnforcementEvent(). Default (unset/
|
|
93
93
|
* unrecognized): treated as a gating event.
|
|
94
|
+
* TRUST_RECONCILE_BASE_REF Optional PR base ref (falls back to
|
|
95
|
+
* GITHUB_BASE_REF, set by GitHub Actions on
|
|
96
|
+
* `pull_request` events only) used SOLELY by the
|
|
97
|
+
* audit-only runtime-session-trailer diagnostic
|
|
98
|
+
* below to bound its commit-log scan
|
|
99
|
+
* (`git log <base>..<sha>`); unset/unresolvable →
|
|
100
|
+
* diagnostic narrows to scanning just the head
|
|
101
|
+
* commit's own message (documented, accepted
|
|
102
|
+
* fallback — never a crash, never widens scope).
|
|
103
|
+
* Never affects Step 1/Step 2/exit-code logic.
|
|
104
|
+
*
|
|
105
|
+
* Runtime-session commit-trailer diagnostic (ADR 0022 §1, issue #305 — AUDIT ONLY, never
|
|
106
|
+
* gating): every run logs a `[trust-reconcile] identified: runtime-session trailer
|
|
107
|
+
* '<trailer>' on <ref>` line for each distinct `Claude-Session:` / `Codex-Session:` /
|
|
108
|
+
* `Opencode-Session:` / `Pi-Session:` commit trailer found in the resolved commit range
|
|
109
|
+
* (see findRuntimeSessionTrailers()/logRuntimeSessionTrailers()). Logs the trailer KEY only
|
|
110
|
+
* (e.g. `Claude-Session`) — never the trailer VALUE (typically a session URL) — per the
|
|
111
|
+
* ADR's own quoted line format. This is diagnostic/attribution only: it never writes to
|
|
112
|
+
* ciResults/issues[] and never changes the exit code, on any path.
|
|
94
113
|
*
|
|
95
114
|
* Auto-discovery (when no --bundle or TRUST_RECONCILE_BUNDLE is set):
|
|
96
115
|
* Checks delivery/trust.bundle, then delivery/trust.checkpoint.json under repo root
|
|
@@ -673,6 +692,111 @@ function isShaInRange(repoRoot, sha, from, to) {
|
|
|
673
692
|
return isAncestorCommit(repoRoot, from, sha) && isAncestorCommit(repoRoot, sha, to);
|
|
674
693
|
}
|
|
675
694
|
|
|
695
|
+
/**
|
|
696
|
+
* Runtime-session commit-trailer names this reconciler recognizes for the audit-only
|
|
697
|
+
* diagnostic below (ADR 0022 §1, table row 1). These are commit-message TRAILER keys
|
|
698
|
+
* (e.g. `Claude-Session: https://claude.ai/code/session_...`), a distinct vocabulary from
|
|
699
|
+
* scripts/hooks/lib/actor-identity.js's RUNTIME_SESSION_ID_VARS (env var names) — but the
|
|
700
|
+
* same four-runtime enumeration (claude-code/codex/opencode/pi).
|
|
701
|
+
*/
|
|
702
|
+
const RUNTIME_SESSION_TRAILER_NAMES = [
|
|
703
|
+
'Claude-Session',
|
|
704
|
+
'Codex-Session',
|
|
705
|
+
'Opencode-Session',
|
|
706
|
+
'Pi-Session',
|
|
707
|
+
];
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Diagnostic-only (ADR 0022 §1): find runtime-session commit trailers (`Claude-Session:`,
|
|
711
|
+
* `Codex-Session:`, `Opencode-Session:`, `Pi-Session:`) across the commit range this run is
|
|
712
|
+
* evaluating, and report which trailer KEYS were found — never the trailer VALUE (typically
|
|
713
|
+
* a session URL). "Commit trailer present — logged by the reconciler ... for audit; does not
|
|
714
|
+
* change the exit path" (ADR 0022 §1). This function is write-only diagnostics: its return
|
|
715
|
+
* value / any failure NEVER feeds ciResults, issues[], or the exit code.
|
|
716
|
+
*
|
|
717
|
+
* Range resolution (accepted, documented narrower fallback — ADR 0022 §1 Residuals):
|
|
718
|
+
* - Prefer `git log <baseRef>..<sha>` when a PR base ref is resolvable
|
|
719
|
+
* (TRUST_RECONCILE_BASE_REF env override, else GITHUB_BASE_REF — set by GitHub Actions
|
|
720
|
+
* on `pull_request` events only).
|
|
721
|
+
* - No base ref resolvable (local/manual invocation, `push`/`workflow_dispatch` events,
|
|
722
|
+
* which have no PR base) — falls back to scanning just `ctx.sha`'s own commit message.
|
|
723
|
+
* This is a narrower-than-ideal default (misses trailers on other commits in an
|
|
724
|
+
* unresolvable range) but is NEVER a crash and NEVER silently expands scope beyond what
|
|
725
|
+
* is actually derivable — documented gap, not a silent limitation.
|
|
726
|
+
* - No usable sha at all — zero trailers found (nothing to scan).
|
|
727
|
+
*
|
|
728
|
+
* @param {string} repoRoot - Repository root (cwd for git subprocess calls).
|
|
729
|
+
* @param {{ref: string, sha: string}} ctx - Resolved scope context (resolveScopeContext()).
|
|
730
|
+
* @returns {Array<{trailerName: string, ref: string}>} Distinct (trailerName) diagnostics
|
|
731
|
+
* found in range, each paired with the ref to log against. Empty array on any failure or
|
|
732
|
+
* when no trailer is present — never throws.
|
|
733
|
+
*/
|
|
734
|
+
function findRuntimeSessionTrailers(repoRoot, ctx) {
|
|
735
|
+
const sha = (ctx && ctx.sha) || '';
|
|
736
|
+
if (!sha) return [];
|
|
737
|
+
const ref = (ctx && ctx.ref) || sha;
|
|
738
|
+
|
|
739
|
+
const baseRef = (process.env.TRUST_RECONCILE_BASE_REF || process.env.GITHUB_BASE_REF || '').trim();
|
|
740
|
+
|
|
741
|
+
let commitMessages = [];
|
|
742
|
+
try {
|
|
743
|
+
if (baseRef) {
|
|
744
|
+
const res = spawnSync('git', ['log', '--format=%B%x00', `${baseRef}..${sha}`], {
|
|
745
|
+
cwd: repoRoot, encoding: 'utf8',
|
|
746
|
+
});
|
|
747
|
+
if (res && !res.error && res.status === 0 && typeof res.stdout === 'string') {
|
|
748
|
+
commitMessages = res.stdout.split('\0').map((m) => m.trim()).filter(Boolean);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
// No base ref resolvable, or the ranged log produced nothing usable — fall back to
|
|
752
|
+
// just the head commit's own message (accepted narrower default, never a crash).
|
|
753
|
+
if (!baseRef || commitMessages.length === 0) {
|
|
754
|
+
const res = spawnSync('git', ['log', '--format=%B', '-1', sha], {
|
|
755
|
+
cwd: repoRoot, encoding: 'utf8',
|
|
756
|
+
});
|
|
757
|
+
if (res && !res.error && res.status === 0 && typeof res.stdout === 'string' && res.stdout.trim()) {
|
|
758
|
+
commitMessages = [res.stdout.trim()];
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
} catch {
|
|
762
|
+
return [];
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const found = [];
|
|
766
|
+
const seen = new Set();
|
|
767
|
+
for (const message of commitMessages) {
|
|
768
|
+
for (const trailerName of RUNTIME_SESSION_TRAILER_NAMES) {
|
|
769
|
+
if (seen.has(trailerName)) continue;
|
|
770
|
+
const prefix = `${trailerName}:`;
|
|
771
|
+
const hasTrailer = message.split('\n').some((line) => line.trim().startsWith(prefix));
|
|
772
|
+
if (hasTrailer) {
|
|
773
|
+
seen.add(trailerName);
|
|
774
|
+
found.push({ trailerName, ref });
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return found;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Diagnostic-only (ADR 0022 §1): write the audit line for each runtime-session commit
|
|
783
|
+
* trailer found in range. Never touches ciResults/issues[]/exit code — wrapped so any
|
|
784
|
+
* unexpected failure degrades to zero lines written, never a thrown error.
|
|
785
|
+
*
|
|
786
|
+
* @param {string} repoRoot
|
|
787
|
+
* @param {{ref: string, sha: string}} ctx
|
|
788
|
+
*/
|
|
789
|
+
function logRuntimeSessionTrailers(repoRoot, ctx) {
|
|
790
|
+
try {
|
|
791
|
+
const trailers = findRuntimeSessionTrailers(repoRoot, ctx);
|
|
792
|
+
for (const { trailerName, ref } of trailers) {
|
|
793
|
+
process.stdout.write(`[trust-reconcile] identified: runtime-session trailer '${trailerName}' on ${ref}\n`);
|
|
794
|
+
}
|
|
795
|
+
} catch {
|
|
796
|
+
// Diagnostic-only — any failure here must never affect the reconciler's outcome.
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
676
800
|
/**
|
|
677
801
|
* Match a SINGLE scope condition (one of the four forms) against the resolved context.
|
|
678
802
|
* Extracted from matchesScope() so a compound (space-separated, AND) scope can validate
|
|
@@ -910,6 +1034,13 @@ function runTrustReconcile({ bundle = null, commands = [], repoRoot = null, mani
|
|
|
910
1034
|
// to pick, among per-session candidates, the one that attests THIS change.
|
|
911
1035
|
const scopeCtx = resolveScopeContext(resolvedRepoRoot);
|
|
912
1036
|
|
|
1037
|
+
// Diagnostic-only (ADR 0022 §1, issue #305): audit-log any runtime-session commit
|
|
1038
|
+
// trailer found in range — logged for attribution, never gating. Runs unconditionally,
|
|
1039
|
+
// regardless of bundle/DECLARED branch, immediately after scopeCtx resolves and before
|
|
1040
|
+
// bundle auto-discovery/Step 1, so it always fires the same way whether or not a bundle
|
|
1041
|
+
// or DECLARED exemption is later found. Never affects ciResults/issues[]/exit code.
|
|
1042
|
+
logRuntimeSessionTrailers(resolvedRepoRoot, scopeCtx);
|
|
1043
|
+
|
|
913
1044
|
let bundlePath = explicitBundlePath || discoverBundle(resolvedRepoRoot, scopeCtx.sha) || null;
|
|
914
1045
|
const bundleWasAutoDiscovered = !explicitBundlePath && !!bundlePath;
|
|
915
1046
|
|
|
@@ -1321,6 +1452,11 @@ module.exports.noEvidenceCommandIssues = noEvidenceCommandIssues;
|
|
|
1321
1452
|
module.exports.reconcilableManifestIssues = reconcilableManifestIssues;
|
|
1322
1453
|
module.exports.sessionLocalShapeIssues = sessionLocalShapeIssues;
|
|
1323
1454
|
|
|
1455
|
+
// Exported for evals (#305 trailer-diagnostic coverage) — read-only diagnostic helpers,
|
|
1456
|
+
// never gating; see the header comment's "Runtime-session commit-trailer diagnostic" note.
|
|
1457
|
+
module.exports.findRuntimeSessionTrailers = findRuntimeSessionTrailers;
|
|
1458
|
+
module.exports.logRuntimeSessionTrailers = logRuntimeSessionTrailers;
|
|
1459
|
+
|
|
1324
1460
|
if (require.main === module) {
|
|
1325
1461
|
main();
|
|
1326
1462
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const { spawnSync } = require('child_process');
|
|
15
|
+
const { extractExitCodeFromBanner, readExitCodeFromRollout } = require('./lib/codex-exit-code');
|
|
15
16
|
|
|
16
17
|
const MAX_STDIN = 1024 * 1024;
|
|
17
18
|
|
|
@@ -127,6 +128,73 @@ function blockedOutput(event, reason) {
|
|
|
127
128
|
};
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
/**
|
|
132
|
+
* enrichEvidenceCapturePayload(raw, hookId, event) → string
|
|
133
|
+
*
|
|
134
|
+
* Codex-only, evidence-capture-only enrichment (#470): codex's PostToolUse
|
|
135
|
+
* payload carries no structured exit code, so `evidence-capture.js`'s rule 3
|
|
136
|
+
* (no signal) would otherwise fire on every codex command, including
|
|
137
|
+
* genuinely failing ones. This injects a deterministic `tool_response.exitCode`
|
|
138
|
+
* extracted from the codex host banner (`Process exited with code N`) BEFORE
|
|
139
|
+
* the payload reaches evidence-capture, so capture stays host-agnostic and
|
|
140
|
+
* never itself scans narration (docs/spec/runtime-hook-surface.md §2.5).
|
|
141
|
+
*
|
|
142
|
+
* Narrowly gated to hookId === 'evidence-capture' && event === 'PostToolUse'
|
|
143
|
+
* so every other codex hook (quality-gate, workflow-steering, stop-goal-fit,
|
|
144
|
+
* ...) is byte-unchanged. Fail-open: any parse error, missing field, or
|
|
145
|
+
* missing/oversized/unreadable rollout returns `raw` unchanged — a hook must
|
|
146
|
+
* never crash the agent, and a miss simply lets evidence-capture's own rule 3
|
|
147
|
+
* (ambiguous default) apply.
|
|
148
|
+
*/
|
|
149
|
+
function enrichEvidenceCapturePayload(raw, hookId, event) {
|
|
150
|
+
if (hookId !== 'evidence-capture' || event !== 'PostToolUse') return raw;
|
|
151
|
+
try {
|
|
152
|
+
const payload = JSON.parse(raw);
|
|
153
|
+
if (!payload || typeof payload !== 'object') return raw;
|
|
154
|
+
|
|
155
|
+
// Tier 1: a payload output field, if codex ever surfaces one directly
|
|
156
|
+
// (cheap; observed absent on codex today — checked defensively).
|
|
157
|
+
const outputFields = [
|
|
158
|
+
payload.tool_response && payload.tool_response.output,
|
|
159
|
+
payload.tool_response && payload.tool_response.stdout,
|
|
160
|
+
payload.tool_output && payload.tool_output.output,
|
|
161
|
+
payload.tool_output && payload.tool_output.stdout,
|
|
162
|
+
payload.output,
|
|
163
|
+
];
|
|
164
|
+
let exitCode = null;
|
|
165
|
+
for (const field of outputFields) {
|
|
166
|
+
if (typeof field !== 'string' || !field) continue;
|
|
167
|
+
const found = extractExitCodeFromBanner(field);
|
|
168
|
+
if (found !== null) { exitCode = found; break; }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Tier 2: bounded tail read of the session rollout at transcript_path.
|
|
172
|
+
if (exitCode === null && typeof payload.transcript_path === 'string' && payload.transcript_path) {
|
|
173
|
+
// No documented codex field correlates a PostToolUse payload to a
|
|
174
|
+
// rollout function_call_output's call_id today; try the plausible
|
|
175
|
+
// candidates defensively and fall back to "last banner in the tail"
|
|
176
|
+
// when none are present (see codex-exit-code.js doc comment). `command`
|
|
177
|
+
// is passed alongside for the command cross-check correlation (Decision
|
|
178
|
+
// B, #470 iteration 2): absent a call_id match, it lets the helper
|
|
179
|
+
// decline (rather than mis-attribute) when the newest rollout banner's
|
|
180
|
+
// paired call doesn't match the command that triggered this PostToolUse.
|
|
181
|
+
const callId = payload.call_id || payload.tool_call_id || payload.id || null;
|
|
182
|
+
const command = payload.tool_input && payload.tool_input.command;
|
|
183
|
+
exitCode = readExitCodeFromRollout(payload.transcript_path, { callId, command });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (exitCode === null) return raw;
|
|
187
|
+
|
|
188
|
+
const existingResponse = payload.tool_response && typeof payload.tool_response === 'object'
|
|
189
|
+
? payload.tool_response
|
|
190
|
+
: {};
|
|
191
|
+
payload.tool_response = { ...existingResponse, exitCode };
|
|
192
|
+
return JSON.stringify(payload);
|
|
193
|
+
} catch {
|
|
194
|
+
return raw; // fail-open: malformed payload or helper throw — pass through unchanged
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
130
198
|
async function main() {
|
|
131
199
|
const [, , hookId, relScriptPath, profilesCsv] = process.argv;
|
|
132
200
|
const { raw, truncated } = await readStdinRaw();
|
|
@@ -138,9 +206,16 @@ async function main() {
|
|
|
138
206
|
return;
|
|
139
207
|
}
|
|
140
208
|
|
|
209
|
+
// Enrich only the evidence-capture PostToolUse invocation with a structured
|
|
210
|
+
// exit code extracted from the codex host banner (#470). `effectiveRaw` is
|
|
211
|
+
// used for BOTH the spawn input and the stdout echo-diff below so the two
|
|
212
|
+
// stay consistent (an enriched-vs-original mismatch would leak the raw JSON
|
|
213
|
+
// as additionalContext via guidanceFromStdout).
|
|
214
|
+
const effectiveRaw = enrichEvidenceCapturePayload(raw, hookId, event);
|
|
215
|
+
|
|
141
216
|
const runHookPath = path.resolve(__dirname, 'run-hook.js');
|
|
142
217
|
const result = spawnSync(process.execPath, [runHookPath, hookId, relScriptPath, profilesCsv || ''], {
|
|
143
|
-
input:
|
|
218
|
+
input: effectiveRaw,
|
|
144
219
|
encoding: 'utf8',
|
|
145
220
|
cwd: process.cwd(),
|
|
146
221
|
env: {
|
|
@@ -166,7 +241,7 @@ async function main() {
|
|
|
166
241
|
}
|
|
167
242
|
|
|
168
243
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
169
|
-
const output = successOutput(event, guidanceFromStdout(
|
|
244
|
+
const output = successOutput(event, guidanceFromStdout(effectiveRaw, result.stdout));
|
|
170
245
|
if (output) process.stdout.write(`${JSON.stringify(output)}\n`);
|
|
171
246
|
}
|
|
172
247
|
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
* Records to `.kontourai/flow-agents/<slug>/command-log.jsonl`, one JSON object per line:
|
|
13
13
|
* {
|
|
14
14
|
* "command": "<the command string the agent ran>",
|
|
15
|
-
* "observedResult": "pass" | "fail",
|
|
15
|
+
* "observedResult": "pass" | "fail" | "ambiguous", // deterministically inferred
|
|
16
|
+
* // ("ambiguous": a bare, non-self-asserting
|
|
17
|
+
* // grep/diff exited 1 — could mean zero
|
|
18
|
+
* // matches/no differences (PASS for an
|
|
19
|
+
* // absence check) or an unintended miss
|
|
20
|
+
* // (FAIL for a presence check); #362)
|
|
16
21
|
* "exitCode": <integer> | null, // null when only pass/fail is inferable
|
|
17
22
|
* "capturedAt": "<ISO-8601 timestamp>",
|
|
18
23
|
* "source": "postToolUse-capture",
|
|
@@ -38,7 +43,10 @@
|
|
|
38
43
|
* (per docs/spec/runtime-hook-surface.md §1, postToolUse). A clean integer exit
|
|
39
44
|
* code is host-dependent. We extract the real exit code where present; otherwise
|
|
40
45
|
* we derive `observedResult` from `error`/stderr-style failure indication and
|
|
41
|
-
* record `exitCode: null`.
|
|
46
|
+
* record `exitCode: null`. Absent positive success evidence (a clean integer
|
|
47
|
+
* exit code of 0; no host currently surfaces a positive success flag), the
|
|
48
|
+
* result is 'ambiguous', never 'pass'. We never record the model's words about
|
|
49
|
+
* the outcome.
|
|
42
50
|
*
|
|
43
51
|
* Non-blocking — always exits 0. Idempotent/append-only. Fail-open on any error:
|
|
44
52
|
* a capture failure must never block the agent or corrupt the log.
|
|
@@ -51,6 +59,7 @@ const path = require('path');
|
|
|
51
59
|
const { flowAgentsArtifactRootsForRead } = require('./lib/local-artifact-paths');
|
|
52
60
|
const { resolveActor } = require('./lib/actor-identity.js');
|
|
53
61
|
const { readCurrentPointer } = require('./lib/current-pointer.js');
|
|
62
|
+
const { isAmbiguousAbsenceCommand } = require('./lib/runnable-command.js');
|
|
54
63
|
const crypto = require('crypto');
|
|
55
64
|
|
|
56
65
|
const MAX_STDIN = 1024 * 1024;
|
|
@@ -248,15 +257,32 @@ function cleanExitCode(value) {
|
|
|
248
257
|
*
|
|
249
258
|
* Priority:
|
|
250
259
|
* 1. A clean integer exit code anywhere the host surfaces it → exitCode set;
|
|
251
|
-
* observedResult = pass iff exitCode === 0
|
|
260
|
+
* observedResult = pass iff exitCode === 0, EXCEPT (#362) a bare,
|
|
261
|
+
* non-self-asserting `grep`/`diff` invocation (per
|
|
262
|
+
* `isAmbiguousAbsenceCommand`, ./lib/runnable-command.js) that exits
|
|
263
|
+
* EXACTLY 1 → observedResult = 'ambiguous' instead of 'fail'. Exit 1 for
|
|
264
|
+
* such a command could mean zero matches/no differences (PASS for an
|
|
265
|
+
* absence check) or an unintended miss (FAIL for a presence check) —
|
|
266
|
+
* this is never coerced to 'pass' (that would trade a false-block for a
|
|
267
|
+
* false-pass) nor silently left as 'fail' (that is the exact false
|
|
268
|
+
* caught-completion #362 reports). Exit codes >= 2 for these two
|
|
269
|
+
* binaries are real tool errors and remain 'fail', unchanged.
|
|
252
270
|
* 2. Else, a non-empty `error` field or stderr-style failure indication →
|
|
253
271
|
* observedResult = fail, exitCode = null.
|
|
254
|
-
* 3. Else → observedResult =
|
|
272
|
+
* 3. Else → observedResult = ambiguous, exitCode = null. Absent positive
|
|
273
|
+
* success evidence (a clean integer exit code of 0; no host currently
|
|
274
|
+
* surfaces a positive success flag), the result is 'ambiguous', never
|
|
275
|
+
* 'pass'.
|
|
276
|
+
*
|
|
277
|
+
* `input.command` (the raw command string, already in scope at the run()
|
|
278
|
+
* call site) is required to evaluate the #362 carve-out; when absent, the
|
|
279
|
+
* carve-out simply never fires and behavior is byte-identical to before #362.
|
|
255
280
|
*/
|
|
256
281
|
function observeResult(input) {
|
|
257
282
|
const response = input.tool_response;
|
|
258
283
|
const output = input.tool_output;
|
|
259
284
|
const error = input.error;
|
|
285
|
+
const command = typeof input.command === 'string' ? input.command : '';
|
|
260
286
|
|
|
261
287
|
// Candidate locations for a host-provided exit code.
|
|
262
288
|
const candidates = [];
|
|
@@ -274,6 +300,9 @@ function observeResult(input) {
|
|
|
274
300
|
}
|
|
275
301
|
|
|
276
302
|
if (exitCode !== null) {
|
|
303
|
+
if (exitCode === 1 && command && isAmbiguousAbsenceCommand(command)) {
|
|
304
|
+
return { exitCode, observedResult: 'ambiguous' };
|
|
305
|
+
}
|
|
277
306
|
return { exitCode, observedResult: exitCode === 0 ? 'pass' : 'fail' };
|
|
278
307
|
}
|
|
279
308
|
|
|
@@ -281,7 +310,7 @@ function observeResult(input) {
|
|
|
281
310
|
if (isFailureIndicated(error, response, output)) {
|
|
282
311
|
return { exitCode: null, observedResult: 'fail' };
|
|
283
312
|
}
|
|
284
|
-
return { exitCode: null, observedResult: '
|
|
313
|
+
return { exitCode: null, observedResult: 'ambiguous' };
|
|
285
314
|
}
|
|
286
315
|
|
|
287
316
|
// True when the host surfaces a deterministic failure signal: a non-empty
|
|
@@ -326,6 +355,10 @@ function run(rawInput) {
|
|
|
326
355
|
exit_code: input.exit_code,
|
|
327
356
|
status: input.status,
|
|
328
357
|
code: input.code,
|
|
358
|
+
// #362: thread the raw command string through so observeResult can apply the
|
|
359
|
+
// bare-grep/diff-exit-1 ambiguous carve-out. `command` is already in scope here
|
|
360
|
+
// (extracted above from input.tool_input.command).
|
|
361
|
+
command,
|
|
329
362
|
});
|
|
330
363
|
|
|
331
364
|
const record = {
|