@kontourai/flow-agents 3.2.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 +173 -0
- package/CHANGELOG.md +54 -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/assignment-provider.js +10 -1
- 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-artifact-cleanup-audit.js +418 -11
- package/build/src/cli/workflow-sidecar.d.ts +310 -1
- package/build/src/cli/workflow-sidecar.js +1914 -126
- 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 +30 -3
- package/context/contracts/artifact-contract.md +16 -2
- 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 +194 -22
- 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/coordination-guide.md +370 -0
- package/docs/decisions/agent-coordination.md +26 -9
- package/docs/decisions/embeddable-engine.md +152 -0
- package/docs/decisions/index.md +5 -3
- package/docs/decisions/trust-ledger-retention.md +88 -0
- package/docs/decisions/trust-reconcile.md +42 -9
- package/docs/decisions/workflow-enforcement.md +31 -9
- package/docs/fixture-ownership.md +6 -2
- package/docs/implementing-trust-reconciliation.md +129 -0
- package/docs/index.md +23 -9
- package/docs/integrations/flow-agents-console.md +275 -0
- package/docs/integrations/index.md +4 -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-artifact-lifecycle.md +38 -1
- package/docs/workflow-usage-guide.md +10 -0
- package/evals/acceptance/prove-capture-teeth.sh +132 -0
- package/evals/ci/antigaming-suite.sh +2 -0
- package/evals/ci/run-baseline.sh +78 -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/reconcile-preflight/disputed-critique-unsuperseded.json +48 -0
- package/evals/fixtures/reconcile-preflight/standalone-disputed-session-local.json +59 -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_checkpoint_signing.sh +10 -2
- package/evals/integration/test_ci_actor_identity.sh +221 -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 +389 -2
- package/evals/integration/test_pull_work_board.sh +200 -0
- package/evals/integration/test_pull_work_provider.sh +1 -1
- package/evals/integration/test_reconcile_preflight.sh +304 -0
- 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_takeover_protocol.sh +340 -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 +121 -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_verify_hold.sh +910 -0
- package/evals/integration/test_veritas_governance_kit.sh +257 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +575 -3
- 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 +37 -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/continue-work/SKILL.md +2 -0
- package/kits/builder/skills/deliver/SKILL.md +115 -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 +33 -2
- package/kits/builder/skills/release-readiness/SKILL.md +12 -0
- package/kits/knowledge/kit.json +9 -0
- package/kits/veritas-governance/docs/README.md +113 -7
- package/kits/veritas-governance/fixtures/exemption/approved.trust-bundle.json +74 -0
- package/kits/veritas-governance/fixtures/exemption/not-approved.trust-bundle.json +74 -0
- package/kits/veritas-governance/fixtures/exemption-review/mixed-fresh-stale.DECLARED.json +14 -0
- package/kits/veritas-governance/flows/exemption-issuance.flow.json +35 -0
- package/kits/veritas-governance/kit.json +19 -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 +214 -253
- package/scripts/hooks/codex-hook-adapter.js +77 -2
- package/scripts/hooks/evidence-capture.js +38 -5
- package/scripts/hooks/lib/actor-identity.js +82 -0
- 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 +194 -22
- 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/lib/reconcile-shape.js +381 -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/assignment-provider.ts +12 -1
- 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-artifact-cleanup-audit.ts +483 -10
- package/src/cli/workflow-sidecar.ts +1980 -119
- 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 +29 -3
|
@@ -83,6 +83,451 @@ for lib in config.sh session.sh enrich.sh transport.sh redact.sh; do
|
|
|
83
83
|
fi
|
|
84
84
|
done
|
|
85
85
|
|
|
86
|
+
# --- 1b. Config file resolution precedence ---
|
|
87
|
+
# scripts/telemetry/lib/config.sh must resolve TELEMETRY_CONFIG_FILE in this
|
|
88
|
+
# order: (1) explicit env always wins; (2) a gitignored per-workspace conf at
|
|
89
|
+
# .kontourai/telemetry-console.conf if present AND operator-trusted
|
|
90
|
+
# (project-specific override); (3) a gitignored user-global conf at
|
|
91
|
+
# ~/.flow-agents/telemetry-console.conf if present AND operator-trusted
|
|
92
|
+
# (one machine-wide install, matching the existing ~/.flow-agents
|
|
93
|
+
# install-home convention); (4) the shipped telemetry.conf default. The
|
|
94
|
+
# shipped conf is tracked and ships into dist bundles, so owner credentials
|
|
95
|
+
# must never land there — hence the per-workspace/per-machine fallbacks.
|
|
96
|
+
# "Operator-trusted" means mode 600 and owned by the current user, since
|
|
97
|
+
# install-console-config.sh always chmod 600s the conf it writes while git
|
|
98
|
+
# can only store 644/755 — never 600 — so a conf smuggled in via
|
|
99
|
+
# clone/tarball/PR/supply-chain cannot pass the gate even at the expected
|
|
100
|
+
# path. Symlinks are rejected outright regardless of the link's own mode,
|
|
101
|
+
# since the trust check uses lstat semantics but the later conf read follows
|
|
102
|
+
# the link — a mismatched check/read object. A fake HOME isolates these
|
|
103
|
+
# checks from any real ~/.flow-agents on the machine running the suite.
|
|
104
|
+
echo ""
|
|
105
|
+
echo "--- Config File Resolution ---"
|
|
106
|
+
CONFIG_SH="$ROOT_DIR/scripts/telemetry/lib/config.sh"
|
|
107
|
+
CONFIG_TEST_ROOT=$(mktemp -d /tmp/eval-telemetry-config-test.XXXXXX)
|
|
108
|
+
mkdir -p "$CONFIG_TEST_ROOT/scripts/telemetry/lib"
|
|
109
|
+
cp "$CONFIG_SH" "$CONFIG_TEST_ROOT/scripts/telemetry/lib/config.sh"
|
|
110
|
+
: > "$CONFIG_TEST_ROOT/scripts/telemetry/telemetry.conf"
|
|
111
|
+
FAKE_HOME="$CONFIG_TEST_ROOT/home"
|
|
112
|
+
mkdir -p "$FAKE_HOME"
|
|
113
|
+
|
|
114
|
+
_resolve_config_file() {
|
|
115
|
+
env -i PATH="$PATH" HOME="$FAKE_HOME" "$@" \
|
|
116
|
+
bash -c "source '$CONFIG_TEST_ROOT/scripts/telemetry/lib/config.sh' 2>/dev/null; echo \"\$TELEMETRY_CONFIG_FILE\""
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Same as _resolve_config_file but captures config.sh's stderr into
|
|
120
|
+
# $1 instead of discarding it, so the new "conf exists but is not trusted"
|
|
121
|
+
# warning (AC2, install-flow-foundations Thread B) can be asserted on
|
|
122
|
+
# directly. Call as: _resolve_config_file_with_stderr <stderr-file> [env=val ...]
|
|
123
|
+
_resolve_config_file_with_stderr() {
|
|
124
|
+
local stderr_file="$1"
|
|
125
|
+
shift
|
|
126
|
+
env -i PATH="$PATH" HOME="$FAKE_HOME" "$@" \
|
|
127
|
+
bash -c "source '$CONFIG_TEST_ROOT/scripts/telemetry/lib/config.sh' 2>'$stderr_file'; echo \"\$TELEMETRY_CONFIG_FILE\""
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
STDERR_CAPTURE="$CONFIG_TEST_ROOT/config-stderr.txt"
|
|
131
|
+
|
|
132
|
+
default_resolved=$(_resolve_config_file)
|
|
133
|
+
if [[ "$default_resolved" == "$CONFIG_TEST_ROOT/scripts/telemetry/telemetry.conf" ]]; then
|
|
134
|
+
_pass "config.sh: falls back to shipped telemetry.conf default"
|
|
135
|
+
else
|
|
136
|
+
_fail "config.sh: default expected '$CONFIG_TEST_ROOT/scripts/telemetry/telemetry.conf', got '$default_resolved'"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
: > "$STDERR_CAPTURE"
|
|
140
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
141
|
+
if [[ ! -s "$STDERR_CAPTURE" ]]; then
|
|
142
|
+
_pass "config.sh: no conf on disk at all produces no untrusted-conf warning"
|
|
143
|
+
else
|
|
144
|
+
_fail "config.sh: baseline (no conf) should produce no warning, got: $(cat "$STDERR_CAPTURE")"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
mkdir -p "$FAKE_HOME/.flow-agents"
|
|
148
|
+
GLOBAL_CONF="$FAKE_HOME/.flow-agents/telemetry-console.conf"
|
|
149
|
+
: > "$GLOBAL_CONF"
|
|
150
|
+
chmod 644 "$GLOBAL_CONF"
|
|
151
|
+
untrusted_global_resolved=$(_resolve_config_file)
|
|
152
|
+
if [[ "$untrusted_global_resolved" == "$CONFIG_TEST_ROOT/scripts/telemetry/telemetry.conf" ]]; then
|
|
153
|
+
_pass "config.sh: untrusted (644) user-global conf falls through to shipped default"
|
|
154
|
+
else
|
|
155
|
+
_fail "config.sh: untrusted global conf should fall through, got '$untrusted_global_resolved'"
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
: > "$STDERR_CAPTURE"
|
|
159
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
160
|
+
if grep -qF "$GLOBAL_CONF" "$STDERR_CAPTURE" 2>/dev/null; then
|
|
161
|
+
_pass "config.sh: untrusted (644) user-global conf prints a visible warning naming its path"
|
|
162
|
+
else
|
|
163
|
+
_fail "config.sh: expected a warning naming '$GLOBAL_CONF', got: $(cat "$STDERR_CAPTURE" 2>/dev/null)"
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
chmod 600 "$GLOBAL_CONF"
|
|
167
|
+
global_resolved=$(_resolve_config_file)
|
|
168
|
+
if [[ "$global_resolved" == "$GLOBAL_CONF" ]]; then
|
|
169
|
+
_pass "config.sh: trusted (600) user-global telemetry-console.conf wins over shipped default"
|
|
170
|
+
else
|
|
171
|
+
_fail "config.sh: global conf expected '$GLOBAL_CONF', got '$global_resolved'"
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
: > "$STDERR_CAPTURE"
|
|
175
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
176
|
+
if [[ ! -s "$STDERR_CAPTURE" ]]; then
|
|
177
|
+
_pass "config.sh: trusted (600) user-global conf produces no warning"
|
|
178
|
+
else
|
|
179
|
+
_fail "config.sh: trusted global conf should produce no warning, got: $(cat "$STDERR_CAPTURE")"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
mkdir -p "$CONFIG_TEST_ROOT/.kontourai"
|
|
183
|
+
LOCAL_CONF="$CONFIG_TEST_ROOT/.kontourai/telemetry-console.conf"
|
|
184
|
+
: > "$LOCAL_CONF"
|
|
185
|
+
chmod 644 "$LOCAL_CONF"
|
|
186
|
+
untrusted_local_resolved=$(_resolve_config_file)
|
|
187
|
+
if [[ "$untrusted_local_resolved" == "$GLOBAL_CONF" ]]; then
|
|
188
|
+
_pass "config.sh: untrusted (644) workspace-local conf falls through to trusted global conf"
|
|
189
|
+
else
|
|
190
|
+
_fail "config.sh: untrusted local conf should fall through to global, got '$untrusted_local_resolved'"
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# Local is untrusted here while global is trusted (600) and wins the
|
|
194
|
+
# resolution -- the local warning must still fire even though local isn't
|
|
195
|
+
# the file config.sh ultimately picks, since the local conf is still being
|
|
196
|
+
# silently ignored on its own terms.
|
|
197
|
+
: > "$STDERR_CAPTURE"
|
|
198
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
199
|
+
if grep -qF "$LOCAL_CONF" "$STDERR_CAPTURE" 2>/dev/null; then
|
|
200
|
+
_pass "config.sh: untrusted (644) workspace-local conf prints a visible warning naming its path even though global wins"
|
|
201
|
+
else
|
|
202
|
+
_fail "config.sh: expected a warning naming '$LOCAL_CONF', got: $(cat "$STDERR_CAPTURE" 2>/dev/null)"
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
chmod 600 "$LOCAL_CONF"
|
|
206
|
+
local_resolved=$(_resolve_config_file)
|
|
207
|
+
if [[ "$local_resolved" == "$LOCAL_CONF" ]]; then
|
|
208
|
+
_pass "config.sh: trusted (600) workspace-local telemetry-console.conf wins over user-global conf"
|
|
209
|
+
else
|
|
210
|
+
_fail "config.sh: local conf expected '$LOCAL_CONF', got '$local_resolved'"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
: > "$STDERR_CAPTURE"
|
|
214
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
215
|
+
if [[ ! -s "$STDERR_CAPTURE" ]]; then
|
|
216
|
+
_pass "config.sh: trusted (600) workspace-local conf produces no warning"
|
|
217
|
+
else
|
|
218
|
+
_fail "config.sh: trusted local conf should produce no warning, got: $(cat "$STDERR_CAPTURE")"
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# A symlink at the workspace conf path must be ignored outright, even if the
|
|
222
|
+
# link's own mode is 600 (macOS `chmod -h` sets the link's own bits without
|
|
223
|
+
# following it) and it points at a genuinely trusted 600 regular file, since
|
|
224
|
+
# the later conf read follows the link to a possibly different target.
|
|
225
|
+
TARGET_CONF="$CONFIG_TEST_ROOT/.kontourai/telemetry-console-target.conf"
|
|
226
|
+
: > "$TARGET_CONF"
|
|
227
|
+
chmod 600 "$TARGET_CONF"
|
|
228
|
+
rm -f "$LOCAL_CONF"
|
|
229
|
+
ln -s "$TARGET_CONF" "$LOCAL_CONF"
|
|
230
|
+
chmod -h 600 "$LOCAL_CONF" 2>/dev/null || true
|
|
231
|
+
symlink_resolved=$(_resolve_config_file)
|
|
232
|
+
if [[ "$symlink_resolved" == "$GLOBAL_CONF" ]]; then
|
|
233
|
+
_pass "config.sh: symlinked workspace-local conf is ignored, falls through to trusted global conf"
|
|
234
|
+
else
|
|
235
|
+
_fail "config.sh: symlinked local conf should fall through to global, got '$symlink_resolved'"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
: > "$STDERR_CAPTURE"
|
|
239
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
240
|
+
if grep -qF "$LOCAL_CONF" "$STDERR_CAPTURE" 2>/dev/null; then
|
|
241
|
+
_pass "config.sh: symlinked workspace-local conf also prints a visible untrusted-conf warning ('-f' follows the link to a real file)"
|
|
242
|
+
else
|
|
243
|
+
_fail "config.sh: expected a warning naming '$LOCAL_CONF' for the symlink case, got: $(cat "$STDERR_CAPTURE" 2>/dev/null)"
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
# A DANGLING symlink (target does not exist) at the workspace conf path is
|
|
247
|
+
# rejected by the trust gate just like the symlink-to-a-real-file case above,
|
|
248
|
+
# but `-f` (which follows the link) returns false for a dangling target --
|
|
249
|
+
# so the warning gate must additionally check `-L` to still fire here
|
|
250
|
+
# (LOW fix: telemetry_conf_warn_untrusted broadened from `-f` alone to
|
|
251
|
+
# `-e || -L`).
|
|
252
|
+
rm -f "$LOCAL_CONF"
|
|
253
|
+
ln -s "$CONFIG_TEST_ROOT/.kontourai/telemetry-console-target-missing.conf" "$LOCAL_CONF"
|
|
254
|
+
dangling_symlink_resolved=$(_resolve_config_file)
|
|
255
|
+
if [[ "$dangling_symlink_resolved" == "$GLOBAL_CONF" ]]; then
|
|
256
|
+
_pass "config.sh: dangling-symlink workspace-local conf is ignored, falls through to trusted global conf"
|
|
257
|
+
else
|
|
258
|
+
_fail "config.sh: dangling-symlink local conf should fall through to global, got '$dangling_symlink_resolved'"
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
: > "$STDERR_CAPTURE"
|
|
262
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
263
|
+
if grep -qF "$LOCAL_CONF" "$STDERR_CAPTURE" 2>/dev/null; then
|
|
264
|
+
_pass "config.sh: dangling-symlink workspace-local conf prints a visible untrusted-conf warning ('-f' alone would miss this; '-L' catches it)"
|
|
265
|
+
else
|
|
266
|
+
_fail "config.sh: expected a warning naming '$LOCAL_CONF' for the dangling-symlink case, got: $(cat "$STDERR_CAPTURE" 2>/dev/null)"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
# A symlink pointing at a DIRECTORY (not a regular file) hits the same `-f`
|
|
270
|
+
# gap: `-f` is false for a directory target, so the pre-fix warning gate
|
|
271
|
+
# silently skipped it too.
|
|
272
|
+
rm -f "$LOCAL_CONF"
|
|
273
|
+
mkdir -p "$CONFIG_TEST_ROOT/.kontourai/telemetry-console-target-dir"
|
|
274
|
+
ln -s "$CONFIG_TEST_ROOT/.kontourai/telemetry-console-target-dir" "$LOCAL_CONF"
|
|
275
|
+
symlink_to_dir_resolved=$(_resolve_config_file)
|
|
276
|
+
if [[ "$symlink_to_dir_resolved" == "$GLOBAL_CONF" ]]; then
|
|
277
|
+
_pass "config.sh: symlink-to-directory workspace-local conf is ignored, falls through to trusted global conf"
|
|
278
|
+
else
|
|
279
|
+
_fail "config.sh: symlink-to-directory local conf should fall through to global, got '$symlink_to_dir_resolved'"
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
: > "$STDERR_CAPTURE"
|
|
283
|
+
_resolve_config_file_with_stderr "$STDERR_CAPTURE" >/dev/null
|
|
284
|
+
if grep -qF "$LOCAL_CONF" "$STDERR_CAPTURE" 2>/dev/null; then
|
|
285
|
+
_pass "config.sh: symlink-to-directory workspace-local conf prints a visible untrusted-conf warning ('-f' alone would miss this; '-L' catches it)"
|
|
286
|
+
else
|
|
287
|
+
_fail "config.sh: expected a warning naming '$LOCAL_CONF' for the symlink-to-directory case, got: $(cat "$STDERR_CAPTURE" 2>/dev/null)"
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
rm -f "$LOCAL_CONF"
|
|
291
|
+
|
|
292
|
+
explicit_resolved=$(_resolve_config_file TELEMETRY_CONFIG_FILE="/tmp/explicit-telemetry.conf")
|
|
293
|
+
if [[ "$explicit_resolved" == "/tmp/explicit-telemetry.conf" ]]; then
|
|
294
|
+
_pass "config.sh: explicit TELEMETRY_CONFIG_FILE env wins over workspace-local conf"
|
|
295
|
+
else
|
|
296
|
+
_fail "config.sh: explicit env expected '/tmp/explicit-telemetry.conf', got '$explicit_resolved'"
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
_resolve_pricing_url() {
|
|
300
|
+
env -i PATH="$PATH" HOME="$FAKE_HOME" "$@" \
|
|
301
|
+
bash -c "source '$CONFIG_TEST_ROOT/scripts/telemetry/lib/config.sh' 2>/dev/null; printf '%s\n' \"\${TELEMETRY_PRICING_URL:-}\""
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
PRICING_EMPTY_CONF="$CONFIG_TEST_ROOT/pricing-empty.conf"
|
|
305
|
+
: > "$PRICING_EMPTY_CONF"
|
|
306
|
+
pricing_from_console=$(_resolve_pricing_url TELEMETRY_CONFIG_FILE="$PRICING_EMPTY_CONF" CONSOLE_TELEMETRY_URL="https://console.example.test")
|
|
307
|
+
if [[ -z "$pricing_from_console" ]]; then
|
|
308
|
+
_pass "config.sh: console telemetry URL alone does not derive TELEMETRY_PRICING_URL (bundled default)"
|
|
309
|
+
else
|
|
310
|
+
_fail "config.sh: expected no derived pricing URL from console URL alone, got '$pricing_from_console'"
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
pricing_from_env=$(_resolve_pricing_url TELEMETRY_CONFIG_FILE="$PRICING_EMPTY_CONF" CONSOLE_TELEMETRY_URL="https://console.example.test" FLOW_AGENTS_PRICING_URL="https://pricing.example.test/registry.json")
|
|
314
|
+
if [[ "$pricing_from_env" == "https://pricing.example.test/registry.json" ]]; then
|
|
315
|
+
_pass "config.sh: explicit FLOW_AGENTS_PRICING_URL remains honored"
|
|
316
|
+
else
|
|
317
|
+
_fail "config.sh: expected explicit FLOW_AGENTS_PRICING_URL to win, got '$pricing_from_env'"
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
PRICING_CONF="$CONFIG_TEST_ROOT/pricing-explicit.conf"
|
|
321
|
+
printf '%s\n' 'console_pricing_url=https://pricing.example.test/from-config.json' > "$PRICING_CONF"
|
|
322
|
+
pricing_from_config=$(_resolve_pricing_url TELEMETRY_CONFIG_FILE="$PRICING_CONF" CONSOLE_TELEMETRY_URL="https://console.example.test")
|
|
323
|
+
if [[ "$pricing_from_config" == "https://pricing.example.test/from-config.json" ]]; then
|
|
324
|
+
_pass "config.sh: explicit console_pricing_url config remains honored"
|
|
325
|
+
else
|
|
326
|
+
_fail "config.sh: expected console_pricing_url config to win, got '$pricing_from_config'"
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
rm -rf "$CONFIG_TEST_ROOT"
|
|
330
|
+
|
|
331
|
+
# --- 1c. Endpoint allowlist drop warning ---
|
|
332
|
+
# transport.sh's console_post_json silently dropped an endpoint rejected by
|
|
333
|
+
# console_telemetry_endpoint_allowed before this change (AC2, Thread B). It
|
|
334
|
+
# now prints a one-time (per shell process) "warning: transport.sh: ..." line
|
|
335
|
+
# to stderr instead, guarded by a plain global var so a second call in the
|
|
336
|
+
# same sourced shell does not repeat it. Sourcing transport.sh directly
|
|
337
|
+
# (mirroring the Config File Resolution block's own cp-then-source pattern)
|
|
338
|
+
# with a disallowed http (non-local) endpoint means console_post_json must
|
|
339
|
+
# return before ever invoking curl, so PATH is left untouched deliberately.
|
|
340
|
+
echo ""
|
|
341
|
+
echo "--- Endpoint Allowlist Drop Warning ---"
|
|
342
|
+
TRANSPORT_TEST_ROOT=$(mktemp -d /tmp/eval-telemetry-transport-test.XXXXXX)
|
|
343
|
+
mkdir -p "$TRANSPORT_TEST_ROOT/scripts/telemetry/lib"
|
|
344
|
+
cp "$ROOT_DIR/scripts/telemetry/lib/transport.sh" "$TRANSPORT_TEST_ROOT/scripts/telemetry/lib/transport.sh"
|
|
345
|
+
cp "$ROOT_DIR/scripts/telemetry/lib/redact.sh" "$TRANSPORT_TEST_ROOT/scripts/telemetry/lib/redact.sh"
|
|
346
|
+
TRANSPORT_STDERR="$TRANSPORT_TEST_ROOT/transport-stderr.txt"
|
|
347
|
+
TRANSPORT_CURL_MARKER="$TRANSPORT_TEST_ROOT/curl-invoked"
|
|
348
|
+
NO_CURL_BIN="$TRANSPORT_TEST_ROOT/no-curl-bin"
|
|
349
|
+
mkdir -p "$NO_CURL_BIN"
|
|
350
|
+
cat > "$NO_CURL_BIN/curl" <<SH
|
|
351
|
+
#!/usr/bin/env bash
|
|
352
|
+
touch "$TRANSPORT_CURL_MARKER"
|
|
353
|
+
SH
|
|
354
|
+
chmod +x "$NO_CURL_BIN/curl"
|
|
355
|
+
TELEMETRY_DIR="$TRANSPORT_TEST_ROOT/scripts/telemetry" PATH="$NO_CURL_BIN:$PATH" bash -c "
|
|
356
|
+
source '$TRANSPORT_TEST_ROOT/scripts/telemetry/lib/transport.sh'
|
|
357
|
+
console_post_json 'http://example.test' '{}'
|
|
358
|
+
console_post_json 'http://example.test' '{}'
|
|
359
|
+
" 2>"$TRANSPORT_STDERR"
|
|
360
|
+
|
|
361
|
+
warning_count=$(grep -c "warning: transport.sh:" "$TRANSPORT_STDERR" 2>/dev/null || echo 0)
|
|
362
|
+
if [[ "$warning_count" -eq 1 ]] && grep -qF "http://example.test" "$TRANSPORT_STDERR" 2>/dev/null; then
|
|
363
|
+
_pass "transport.sh: disallowed endpoint prints exactly one drop warning across two calls in the same shell"
|
|
364
|
+
else
|
|
365
|
+
_fail "transport.sh: expected exactly 1 warning naming the endpoint, got $warning_count: $(cat "$TRANSPORT_STDERR" 2>/dev/null)"
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
if [[ ! -f "$TRANSPORT_CURL_MARKER" ]]; then
|
|
369
|
+
_pass "transport.sh: disallowed endpoint never invokes curl"
|
|
370
|
+
else
|
|
371
|
+
_fail "transport.sh: disallowed endpoint should never invoke curl, but it did"
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
rm -rf "$TRANSPORT_TEST_ROOT"
|
|
375
|
+
|
|
376
|
+
# --- Console relay project attribution (canonical precedence, path-free) ---
|
|
377
|
+
echo ""
|
|
378
|
+
echo "--- Console Relay Project Attribution ---"
|
|
379
|
+
PROJ_TEST_ROOT=$(mktemp -d /tmp/eval-telemetry-project.XXXXXX)
|
|
380
|
+
mkdir -p "$PROJ_TEST_ROOT/work/pkg" "$PROJ_TEST_ROOT/plain-dir-name"
|
|
381
|
+
printf '{"name":"@kontourai/inner-pkg"}\n' > "$PROJ_TEST_ROOT/work/pkg/package.json"
|
|
382
|
+
# git fixtures (no package.json ancestor) for the git-remote precedence tier and host-strip hardening
|
|
383
|
+
git init -q "$PROJ_TEST_ROOT/gitrepo" && git -C "$PROJ_TEST_ROOT/gitrepo" remote add origin 'https://github.com/test-org/test-repo.git'
|
|
384
|
+
git init -q "$PROJ_TEST_ROOT/gitslash" && git -C "$PROJ_TEST_ROOT/gitslash" remote add origin 'https://github.com/test-org/test-repo/'
|
|
385
|
+
git init -q "$PROJ_TEST_ROOT/gitsingle" && git -C "$PROJ_TEST_ROOT/gitsingle" remote add origin 'https://github.com/loneseg.git'
|
|
386
|
+
git init -q "$PROJ_TEST_ROOT/githostless" && git -C "$PROJ_TEST_ROOT/githostless" remote add origin 'https://gitserver/reponame.git'
|
|
387
|
+
git init -q "$PROJ_TEST_ROOT/gitselfhost" && git -C "$PROJ_TEST_ROOT/gitselfhost" remote add origin 'https://gitea.example.com/team/proj.git'
|
|
388
|
+
git init -q "$PROJ_TEST_ROOT/gitscphost" && git -C "$PROJ_TEST_ROOT/gitscphost" remote add origin 'git@gitserver:reponame.git'
|
|
389
|
+
git init -q "$PROJ_TEST_ROOT/gitfile" && git -C "$PROJ_TEST_ROOT/gitfile" remote add origin 'file:///srv/git/secretuser/reponame.git'
|
|
390
|
+
git init -q "$PROJ_TEST_ROOT/gitlocal" && git -C "$PROJ_TEST_ROOT/gitlocal" remote add origin '/srv/git/secretuser/reponame'
|
|
391
|
+
|
|
392
|
+
# Unit-test the precedence engine console_project_label directly. It is byte-identical between the
|
|
393
|
+
# source copy and the context/ mirror (their emit wrappers differ: source delegates to
|
|
394
|
+
# console_post_json, the mirror inlines curl), so calling the function directly is the way to catch
|
|
395
|
+
# a future edit landing in one copy but not the other. Echoes the derived label.
|
|
396
|
+
# args: <full path to a transport.sh> <cwd> [bash prefix run before the call]. TELEMETRY_DIR is
|
|
397
|
+
# derived from the transport path, and only the two full copy paths below are written as literals
|
|
398
|
+
# (both resolve), so the source-tree path validator has nothing unresolvable to flag.
|
|
399
|
+
_label_of() {
|
|
400
|
+
local transport="$1" cwd="$2" pre="${3:-}"
|
|
401
|
+
local teldir; teldir=$(dirname "$(dirname "$transport")")
|
|
402
|
+
bash -c "
|
|
403
|
+
$pre
|
|
404
|
+
export TELEMETRY_DIR='$teldir'
|
|
405
|
+
source \"$transport\"
|
|
406
|
+
console_project_label '$cwd'
|
|
407
|
+
"
|
|
408
|
+
}
|
|
409
|
+
_proj_of() { printf '%s' "$1" | jq -r '.context.project // "MISSING"' 2>/dev/null; }
|
|
410
|
+
|
|
411
|
+
# Precedence matrix against BOTH the source copy and the context/ mirror.
|
|
412
|
+
for transport in \
|
|
413
|
+
"$ROOT_DIR/scripts/telemetry/lib/transport.sh" \
|
|
414
|
+
"$ROOT_DIR/context/scripts/telemetry/lib/transport.sh"; do
|
|
415
|
+
[[ -f "$transport" ]] || continue
|
|
416
|
+
case "$transport" in *"/context/"*) tag="context-mirror" ;; *) tag="source" ;; esac
|
|
417
|
+
|
|
418
|
+
# step 2: nearest package.json name wins
|
|
419
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/work/pkg")
|
|
420
|
+
[[ "$lbl" == "@kontourai/inner-pkg" ]] \
|
|
421
|
+
&& _pass "console_project_label ($tag): nearest package.json name wins" \
|
|
422
|
+
|| _fail "console_project_label ($tag): expected @kontourai/inner-pkg, got '$lbl'"
|
|
423
|
+
|
|
424
|
+
# step 1: FLOW_AGENTS_PROJECT override beats a present package.json
|
|
425
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/work/pkg" "export FLOW_AGENTS_PROJECT=my-explicit-proj")
|
|
426
|
+
[[ "$lbl" == "my-explicit-proj" ]] \
|
|
427
|
+
&& _pass "console_project_label ($tag): FLOW_AGENTS_PROJECT overrides all other tiers" \
|
|
428
|
+
|| _fail "console_project_label ($tag): expected my-explicit-proj, got '$lbl'"
|
|
429
|
+
|
|
430
|
+
# step 3: git remote org/repo when no manifest is found on the walk-up
|
|
431
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitrepo")
|
|
432
|
+
[[ "$lbl" == "test-org/test-repo" ]] \
|
|
433
|
+
&& _pass "console_project_label ($tag): git remote resolves to org/repo" \
|
|
434
|
+
|| _fail "console_project_label ($tag): expected test-org/test-repo, got '$lbl'"
|
|
435
|
+
|
|
436
|
+
# step 5: no manifest/git remote -> cwd basename fallback
|
|
437
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/plain-dir-name")
|
|
438
|
+
[[ "$lbl" == "plain-dir-name" ]] \
|
|
439
|
+
&& _pass "console_project_label ($tag): falls back to cwd basename when no manifest/git remote" \
|
|
440
|
+
|| _fail "console_project_label ($tag): expected plain-dir-name, got '$lbl'"
|
|
441
|
+
|
|
442
|
+
# sed hardening: trailing-slash remote still reduces to a clean org/repo (not the raw URL)
|
|
443
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitslash")
|
|
444
|
+
[[ "$lbl" == "test-org/test-repo" ]] \
|
|
445
|
+
&& _pass "console_project_label ($tag): trailing-slash git remote reduces to org/repo" \
|
|
446
|
+
|| _fail "console_project_label ($tag): expected test-org/test-repo (trailing slash), got '$lbl'"
|
|
447
|
+
|
|
448
|
+
# host-strip: single-segment remote (dotted host, no org tier) must NOT leak host; falls through
|
|
449
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitsingle")
|
|
450
|
+
[[ "$lbl" == "gitsingle" ]] \
|
|
451
|
+
&& _pass "console_project_label ($tag): single-segment git remote does not leak host" \
|
|
452
|
+
|| _fail "console_project_label ($tag): expected gitsingle fallthrough, got '$lbl'"
|
|
453
|
+
|
|
454
|
+
# host-strip: DOT-LESS self-hosted host (URL), single path segment -> no host leak, falls through
|
|
455
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/githostless")
|
|
456
|
+
[[ "$lbl" == "githostless" ]] \
|
|
457
|
+
&& _pass "console_project_label ($tag): dot-less self-hosted host does not leak; falls through" \
|
|
458
|
+
|| _fail "console_project_label ($tag): expected githostless fallthrough, got '$lbl'"
|
|
459
|
+
|
|
460
|
+
# host-strip: dotted self-hosted FQDN WITH an org tier reduces to org/repo (host stripped)
|
|
461
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitselfhost")
|
|
462
|
+
[[ "$lbl" == "team/proj" ]] \
|
|
463
|
+
&& _pass "console_project_label ($tag): self-hosted FQDN with org tier reduces to org/repo" \
|
|
464
|
+
|| _fail "console_project_label ($tag): expected team/proj, got '$lbl'"
|
|
465
|
+
|
|
466
|
+
# host-strip: scp-form dot-less host, single path segment -> no user@host leak, falls through
|
|
467
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitscphost")
|
|
468
|
+
[[ "$lbl" == "gitscphost" ]] \
|
|
469
|
+
&& _pass "console_project_label ($tag): scp-form dot-less host does not leak; falls through" \
|
|
470
|
+
|| _fail "console_project_label ($tag): expected gitscphost fallthrough, got '$lbl'"
|
|
471
|
+
|
|
472
|
+
# path-free: a file:// remote must not leak local path components; falls through to basename
|
|
473
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitfile")
|
|
474
|
+
[[ "$lbl" == "gitfile" ]] \
|
|
475
|
+
&& _pass "console_project_label ($tag): file:// remote does not leak local path; falls through" \
|
|
476
|
+
|| _fail "console_project_label ($tag): expected gitfile fallthrough, got '$lbl'"
|
|
477
|
+
|
|
478
|
+
# path-free: a bare absolute-local-path remote must not leak path components; falls through
|
|
479
|
+
lbl=$(_label_of "$transport" "$PROJ_TEST_ROOT/gitlocal")
|
|
480
|
+
[[ "$lbl" == "gitlocal" ]] \
|
|
481
|
+
&& _pass "console_project_label ($tag): absolute-local-path remote does not leak; falls through" \
|
|
482
|
+
|| _fail "console_project_label ($tag): expected gitlocal fallthrough, got '$lbl'"
|
|
483
|
+
done
|
|
484
|
+
|
|
485
|
+
# Integration: exercise the real shipping emit wrapper (source copy) end-to-end — proves the label
|
|
486
|
+
# is stamped AND the full cwd is still redacted to null in the same relayed event.
|
|
487
|
+
_relay_source() {
|
|
488
|
+
local cwd="$1" preset="${2:-}"
|
|
489
|
+
local ev="{\"type\":\"tool\",\"context\":{\"cwd\":\"$cwd\"}}"
|
|
490
|
+
[[ -n "$preset" ]] && ev="{\"type\":\"tool\",\"context\":{\"cwd\":\"$cwd\",\"project\":\"$preset\"}}"
|
|
491
|
+
bash -c "
|
|
492
|
+
export TELEMETRY_DIR='$ROOT_DIR/scripts/telemetry'
|
|
493
|
+
source '$ROOT_DIR/scripts/telemetry/lib/transport.sh'
|
|
494
|
+
console_telemetry_endpoint_url() { echo 'https://console.example.test/records'; }
|
|
495
|
+
console_post_json() { printf '%s' \"\$2\"; }
|
|
496
|
+
CONSOLE_TELEMETRY_REDACT='context.cwd' TELEMETRY_SESSION_DIR='' \
|
|
497
|
+
console_telemetry_emit '$ev'
|
|
498
|
+
"
|
|
499
|
+
}
|
|
500
|
+
cap=$(_relay_source "$PROJ_TEST_ROOT/work/pkg")
|
|
501
|
+
if [[ "$(_proj_of "$cap")" == "@kontourai/inner-pkg" && "$(printf '%s' "$cap" | jq -r '.context.cwd // "null"')" == "null" ]]; then
|
|
502
|
+
_pass "console_telemetry_emit: stamps context.project while the full cwd stays redacted to null"
|
|
503
|
+
else
|
|
504
|
+
_fail "console_telemetry_emit: expected project=@kontourai/inner-pkg cwd=null, got '$cap'"
|
|
505
|
+
fi
|
|
506
|
+
# no-clobber: an adapter-supplied context.project is never overwritten
|
|
507
|
+
cap=$(_relay_source "$PROJ_TEST_ROOT/work/pkg" "adapter-supplied")
|
|
508
|
+
[[ "$(_proj_of "$cap")" == "adapter-supplied" ]] \
|
|
509
|
+
&& _pass "console_telemetry_emit: pre-set context.project is left untouched" \
|
|
510
|
+
|| _fail "console_telemetry_emit: expected project=adapter-supplied, got '$(_proj_of "$cap")'"
|
|
511
|
+
|
|
512
|
+
# set -e safety: a missing/stale cwd must relay the event unchanged, never abort the caller
|
|
513
|
+
sete_out=$(bash -c "
|
|
514
|
+
set -euo pipefail
|
|
515
|
+
export TELEMETRY_DIR='$ROOT_DIR/scripts/telemetry'
|
|
516
|
+
source '$ROOT_DIR/scripts/telemetry/lib/transport.sh'
|
|
517
|
+
console_telemetry_endpoint_url() { echo 'https://console.example.test/records'; }
|
|
518
|
+
console_post_json() { printf '%s' \"\$2\"; }
|
|
519
|
+
CONSOLE_TELEMETRY_REDACT='context.cwd' TELEMETRY_SESSION_DIR='' \
|
|
520
|
+
console_telemetry_emit '{\"type\":\"tool\",\"context\":{\"cwd\":\"/no/such/dir/xyz\"}}'
|
|
521
|
+
echo REACHED_END
|
|
522
|
+
")
|
|
523
|
+
sete_rc=$?
|
|
524
|
+
if [[ $sete_rc -eq 0 && "$sete_out" == *REACHED_END* ]]; then
|
|
525
|
+
_pass "console_telemetry_emit: under set -e survives a missing cwd (relays, no abort)"
|
|
526
|
+
else
|
|
527
|
+
_fail "console_telemetry_emit: set -e aborted on missing cwd (rc=$sete_rc, out='$sete_out')"
|
|
528
|
+
fi
|
|
529
|
+
rm -rf "$PROJ_TEST_ROOT"
|
|
530
|
+
|
|
86
531
|
# --- 2. Event type mapping ---
|
|
87
532
|
echo ""
|
|
88
533
|
echo "--- Event Type Mapping ---"
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
+
# shellcheck source=/dev/null
|
|
6
|
+
source "$ROOT_DIR/scripts/telemetry/console-presets.sh"
|
|
7
|
+
KONTOUR_HOSTED_CONSOLE_URL="$(flow_agents_kontour_hosted_console_url)"
|
|
5
8
|
FLOW_AGENTS_CLI="${FLOW_AGENTS_CLI:-node}"
|
|
6
9
|
FLOW_AGENTS_CLI_ARGS=()
|
|
7
10
|
if [[ "$FLOW_AGENTS_CLI" == "node" ]]; then
|
|
@@ -186,6 +189,69 @@ else
|
|
|
186
189
|
fi
|
|
187
190
|
fi
|
|
188
191
|
|
|
192
|
+
# --- Hosted Console reachability by default (AC3, install-flow-foundations Thread C) ---
|
|
193
|
+
# console.kontourai.io (or FLOW_AGENTS_KONTOUR_CLOUD_CONSOLE_URL when
|
|
194
|
+
# overridden) is the ONE known hosted Console hostname that is reachability-
|
|
195
|
+
# checkable without --allow-network. Real network reachability is not
|
|
196
|
+
# guaranteed in a CI sandbox, so only endpointAllowed/reachability.checked
|
|
197
|
+
# are asserted, not .ok.
|
|
198
|
+
cat > "$DEST/scripts/telemetry/telemetry.conf" <<CONF
|
|
199
|
+
enabled=true
|
|
200
|
+
channels=full
|
|
201
|
+
console_telemetry_url=$KONTOUR_HOSTED_CONSOLE_URL
|
|
202
|
+
CONF
|
|
203
|
+
|
|
204
|
+
$FLOW_AGENTS_CLI "${FLOW_AGENTS_CLI_ARGS[@]}" telemetry-doctor --dest "$DEST" --json --headless --timeout-ms 250 >"$TMPDIR_EVAL/hosted.json" 2>&1 || true
|
|
205
|
+
if jq -e '
|
|
206
|
+
.console.endpointAllowed == true and
|
|
207
|
+
.console.reachability.checked == true
|
|
208
|
+
' "$TMPDIR_EVAL/hosted.json" >/dev/null 2>&1; then
|
|
209
|
+
_pass "doctor checks known hosted Console reachability by default (no --allow-network)"
|
|
210
|
+
else
|
|
211
|
+
_fail "doctor hosted-Console-by-default JSON did not match expected shape: $(cat "$TMPDIR_EVAL/hosted.json" 2>/dev/null)"
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# Regression guard: a generic non-local https endpoint (not the known hosted
|
|
215
|
+
# hostname) must still require --allow-network exactly as before.
|
|
216
|
+
cat > "$DEST/scripts/telemetry/telemetry.conf" <<'CONF'
|
|
217
|
+
enabled=true
|
|
218
|
+
channels=full
|
|
219
|
+
console_telemetry_url=https://console.example.test
|
|
220
|
+
CONF
|
|
221
|
+
|
|
222
|
+
if $FLOW_AGENTS_CLI "${FLOW_AGENTS_CLI_ARGS[@]}" telemetry-doctor --dest "$DEST" --json --headless --timeout-ms 250 >"$TMPDIR_EVAL/generic-nonlocal.json"; then
|
|
223
|
+
_fail "doctor returned success for generic non-local Console without --allow-network"
|
|
224
|
+
else
|
|
225
|
+
if jq -e '
|
|
226
|
+
.console.endpointAllowed == false and
|
|
227
|
+
.console.reachability.checked == false
|
|
228
|
+
' "$TMPDIR_EVAL/generic-nonlocal.json" >/dev/null; then
|
|
229
|
+
_pass "doctor still blocks a generic (non-hosted) non-local Console without --allow-network"
|
|
230
|
+
else
|
|
231
|
+
_fail "doctor generic non-local block JSON did not match expected shape"
|
|
232
|
+
fi
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
# --- Untrusted telemetry-console.conf surfaces a doctor warning (AC2, Thread B/C wiring) ---
|
|
236
|
+
mkdir -p "$DEST/.kontourai"
|
|
237
|
+
UNTRUSTED_CONF="$DEST/.kontourai/telemetry-console.conf"
|
|
238
|
+
: > "$UNTRUSTED_CONF"
|
|
239
|
+
chmod 644 "$UNTRUSTED_CONF"
|
|
240
|
+
cp "$ROOT_DIR/scripts/telemetry/telemetry.conf" "$DEST/scripts/telemetry/telemetry.conf"
|
|
241
|
+
|
|
242
|
+
if $FLOW_AGENTS_CLI "${FLOW_AGENTS_CLI_ARGS[@]}" telemetry-doctor --dest "$DEST" --json --headless >"$TMPDIR_EVAL/untrusted-conf.json"; then
|
|
243
|
+
if jq -e --arg path "$UNTRUSTED_CONF" '
|
|
244
|
+
(.warnings | any(contains($path)))
|
|
245
|
+
' "$TMPDIR_EVAL/untrusted-conf.json" >/dev/null; then
|
|
246
|
+
_pass "doctor warns about an untrusted-but-present local telemetry-console.conf"
|
|
247
|
+
else
|
|
248
|
+
_fail "doctor did not surface a warning for the untrusted local conf: $(cat "$TMPDIR_EVAL/untrusted-conf.json")"
|
|
249
|
+
fi
|
|
250
|
+
else
|
|
251
|
+
_fail "doctor failed for untrusted-conf-present case"
|
|
252
|
+
fi
|
|
253
|
+
rm -f "$UNTRUSTED_CONF"
|
|
254
|
+
|
|
189
255
|
echo ""
|
|
190
256
|
echo "Passed: $pass"
|
|
191
257
|
echo "Failed: $fail"
|