@event4u/agent-config 9.26.0 → 9.28.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 (252) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/AGENTS.md +12 -11
  3. package/CHANGELOG.md +134 -207
  4. package/README.md +1 -1
  5. package/dist/agent-src/commands/analyze/conformance.md +43 -0
  6. package/dist/agent-src/commands/roadmap/next.md +65 -3
  7. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +10 -3
  8. package/dist/agent-src/rules/active-remediation.md +2 -0
  9. package/dist/agent-src/rules/analysis-skill-routing.md +2 -0
  10. package/dist/agent-src/rules/architecture.md +2 -0
  11. package/dist/agent-src/rules/artifact-drafting-protocol.md +2 -0
  12. package/dist/agent-src/rules/artifact-engagement-recording.md +6 -1
  13. package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
  14. package/dist/agent-src/rules/autonomous-execution.md +3 -1
  15. package/dist/agent-src/rules/brand-consistency.md +2 -0
  16. package/dist/agent-src/rules/brand-source-of-truth.md +35 -6
  17. package/dist/agent-src/rules/broken-access-control.md +2 -0
  18. package/dist/agent-src/rules/cli-output-handling.md +2 -0
  19. package/dist/agent-src/rules/code-comment-discipline.md +2 -0
  20. package/dist/agent-src/rules/code-provenance.md +2 -0
  21. package/dist/agent-src/rules/command-suggestion-policy.md +2 -0
  22. package/dist/agent-src/rules/commit-conventions.md +2 -0
  23. package/dist/agent-src/rules/communication-through-line.md +2 -0
  24. package/dist/agent-src/rules/content-quoting-floor.md +2 -0
  25. package/dist/agent-src/rules/context-hygiene.md +13 -1
  26. package/dist/agent-src/rules/copilot-routing.md +2 -0
  27. package/dist/agent-src/rules/council-availability.md +93 -0
  28. package/dist/agent-src/rules/cross-source-consistency.md +5 -0
  29. package/dist/agent-src/rules/decision-revisit-gate.md +2 -0
  30. package/dist/agent-src/rules/delegation-policy.md +31 -5
  31. package/dist/agent-src/rules/design-fidelity.md +5 -0
  32. package/dist/agent-src/rules/design-review-after-ui-write.md +4 -0
  33. package/dist/agent-src/rules/devcontainer-routing.md +2 -0
  34. package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
  35. package/dist/agent-src/rules/docker-commands.md +2 -0
  36. package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
  37. package/dist/agent-src/rules/domain-safety-disclaimer.md +2 -0
  38. package/dist/agent-src/rules/domain-safety-pii.md +2 -0
  39. package/dist/agent-src/rules/domain-safety-retention.md +2 -0
  40. package/dist/agent-src/rules/downstream-changes.md +2 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +14 -3
  43. package/dist/agent-src/rules/external-code-graph-interop.md +4 -1
  44. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -0
  45. package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -0
  46. package/dist/agent-src/rules/finance-safety-floor.md +3 -1
  47. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +2 -0
  48. package/dist/agent-src/rules/git-history-discipline.md +3 -1
  49. package/dist/agent-src/rules/guidelines.md +2 -0
  50. package/dist/agent-src/rules/history-discipline.md +5 -1
  51. package/dist/agent-src/rules/icon-consistency.md +2 -0
  52. package/dist/agent-src/rules/image-likeness-and-rights.md +2 -0
  53. package/dist/agent-src/rules/improve-before-implement.md +2 -0
  54. package/dist/agent-src/rules/invite-challenge.md +2 -0
  55. package/dist/agent-src/rules/laravel-routing.md +2 -0
  56. package/dist/agent-src/rules/laravel-translations.md +2 -0
  57. package/dist/agent-src/rules/legal-safety-floor.md +3 -1
  58. package/dist/agent-src/rules/lethal-trifecta-guard.md +2 -0
  59. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +2 -0
  60. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +2 -0
  61. package/dist/agent-src/rules/markdown-safe-codeblocks.md +2 -0
  62. package/dist/agent-src/rules/media-governance-routing.md +2 -0
  63. package/dist/agent-src/rules/media-sync-ground-truth.md +2 -0
  64. package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
  65. package/dist/agent-src/rules/missing-tool-handling.md +2 -0
  66. package/dist/agent-src/rules/model-recommendation.md +2 -0
  67. package/dist/agent-src/rules/no-attribution-footers.md +2 -0
  68. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +2 -0
  69. package/dist/agent-src/rules/no-pr-progress-comments.md +5 -1
  70. package/dist/agent-src/rules/no-roadmap-references.md +2 -0
  71. package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
  72. package/dist/agent-src/rules/onboarding-gate.md +5 -1
  73. package/dist/agent-src/rules/output-discipline.md +2 -0
  74. package/dist/agent-src/rules/package-ci-checks.md +2 -0
  75. package/dist/agent-src/rules/persona-governance.md +2 -0
  76. package/dist/agent-src/rules/php-coding.md +2 -0
  77. package/dist/agent-src/rules/prefer-enums-over-literals.md +2 -0
  78. package/dist/agent-src/rules/preservation-guard.md +2 -0
  79. package/dist/agent-src/rules/provider-lifecycle-discipline.md +2 -0
  80. package/dist/agent-src/rules/question-not-instruction.md +2 -0
  81. package/dist/agent-src/rules/reviewer-awareness.md +2 -0
  82. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +6 -1
  83. package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
  84. package/dist/agent-src/rules/role-mode-adherence.md +3 -1
  85. package/dist/agent-src/rules/rule-type-governance.md +2 -0
  86. package/dist/agent-src/rules/runtime-safety.md +2 -0
  87. package/dist/agent-src/rules/scale-discipline.md +5 -1
  88. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  89. package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
  90. package/dist/agent-src/rules/self-repair-loop.md +103 -0
  91. package/dist/agent-src/rules/senior-engineering-discipline.md +2 -0
  92. package/dist/agent-src/rules/session-canary.md +62 -19
  93. package/dist/agent-src/rules/settings-ask-protocol.md +4 -0
  94. package/dist/agent-src/rules/size-enforcement.md +2 -0
  95. package/dist/agent-src/rules/skill-improvement-trigger.md +2 -0
  96. package/dist/agent-src/rules/skill-quality.md +2 -0
  97. package/dist/agent-src/rules/slash-command-routing-policy.md +2 -0
  98. package/dist/agent-src/rules/source-confidentiality.md +2 -0
  99. package/dist/agent-src/rules/source-discovery-gate.md +2 -0
  100. package/dist/agent-src/rules/source-of-truth.md +2 -0
  101. package/dist/agent-src/rules/spreadsheet-source-quality.md +2 -0
  102. package/dist/agent-src/rules/strategy-safety-floor.md +3 -1
  103. package/dist/agent-src/rules/symfony-routing.md +2 -0
  104. package/dist/agent-src/rules/think-before-action.md +2 -0
  105. package/dist/agent-src/rules/token-budget-discipline.md +5 -1
  106. package/dist/agent-src/rules/token-efficiency.md +2 -0
  107. package/dist/agent-src/rules/token-optimizer-maintenance.md +2 -0
  108. package/dist/agent-src/rules/tool-safety.md +7 -1
  109. package/dist/agent-src/rules/ui-audit-gate.md +4 -0
  110. package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
  111. package/dist/agent-src/rules/upstream-proposal.md +2 -0
  112. package/dist/agent-src/rules/user-interaction.md +2 -0
  113. package/dist/agent-src/rules/user-interrupt-priority.md +2 -0
  114. package/dist/agent-src/skills/adversarial-review/SKILL.md +1 -1
  115. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  116. package/dist/agent-src/skills/performance-analysis/SKILL.md +1 -1
  117. package/dist/agent-src/skills/persona-improvement/SKILL.md +1 -1
  118. package/dist/agent-src/skills/project-analyzer/SKILL.md +1 -1
  119. package/dist/agent-src/skills/security-audit/SKILL.md +1 -1
  120. package/dist/agent-src/skills/sequential-thinking/SKILL.md +1 -1
  121. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  122. package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
  123. package/dist/agent-src/templates/AGENTS.md +6 -10
  124. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  125. package/dist/cli/registry.js +9 -0
  126. package/dist/cli/registry.js.map +1 -1
  127. package/dist/cli-delegate/chunk-22GCIJ2O.js +270 -0
  128. package/dist/cli-delegate/{chunk-BSLUHV2F.js → chunk-2BE5I2MN.js} +156 -141
  129. package/dist/cli-delegate/{chunk-AIIUU73N.js → chunk-7OMQCH4V.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HJ6DAJHR.js → chunk-GB2FSUEE.js} +57 -7
  131. package/dist/cli-delegate/{chunk-WR6GTRHJ.js → chunk-JXARCGVN.js} +3 -3
  132. package/dist/cli-delegate/{chunk-SJQMJI7M.js → chunk-PQSAQKHW.js} +467 -305
  133. package/dist/cli-delegate/chunk-REKCMYOU.js +2991 -0
  134. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  135. package/dist/cli-delegate/cmd_brand_status.js +134 -0
  136. package/dist/cli-delegate/cmd_conformance.js +7 -7
  137. package/dist/cli-delegate/cmd_converge.js +3 -3
  138. package/dist/cli-delegate/cmd_doctor.js +4 -4
  139. package/dist/cli-delegate/cmd_explain.js +80 -330
  140. package/dist/cli-delegate/cmd_export.js +5 -5
  141. package/dist/cli-delegate/cmd_fleet.js +3 -3
  142. package/dist/cli-delegate/cmd_handoff.js +2 -2
  143. package/dist/cli-delegate/cmd_mcp_available.js +433 -0
  144. package/dist/cli-delegate/cmd_migrate.js +3 -3
  145. package/dist/cli-delegate/cmd_packs_active.js +153 -0
  146. package/dist/cli-delegate/cmd_preflight.js +1 -1
  147. package/dist/cli-delegate/cmd_prune.js +5 -5
  148. package/dist/cli-delegate/cmd_route_audit.js +8 -8
  149. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  150. package/dist/cli-delegate/cmd_settings_get.js +272 -0
  151. package/dist/cli-delegate/cmd_settings_migrate.js +5 -5
  152. package/dist/cli-delegate/cmd_settings_set.js +7 -2967
  153. package/dist/cli-delegate/cmd_sync.js +5 -5
  154. package/dist/cli-delegate/cmd_uninstall.js +5 -5
  155. package/dist/cli-delegate/cmd_update.js +4 -4
  156. package/dist/cli-delegate/cmd_upgrade.js +4 -4
  157. package/dist/cli-delegate/cmd_validate.js +5 -5
  158. package/dist/cli-delegate/cmd_versions.js +4 -4
  159. package/dist/discovery/deprecation-report.md +1 -1
  160. package/dist/discovery/discovery-manifest.json +184 -124
  161. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  162. package/dist/discovery/discovery-manifest.summary.md +3 -3
  163. package/dist/discovery/orphan-report.md +1 -1
  164. package/dist/discovery/packs.json +7 -5
  165. package/dist/discovery/trust-report.md +11 -11
  166. package/dist/discovery/workspaces.json +6 -4
  167. package/dist/hooks/dispatch.js +9080 -5701
  168. package/dist/install/install.mjs +698 -508
  169. package/dist/mcp/registry-manifest.json +3 -3
  170. package/dist/mcp/server.json +2 -2
  171. package/dist/mcp/server.mjs +698 -437
  172. package/dist/router.json +1 -1
  173. package/dist/scripts/_lib/agent_settings.js +80 -15
  174. package/dist/scripts/_lib/agent_settings.js.map +1 -1
  175. package/dist/scripts/condense.js +33 -2
  176. package/dist/scripts/condense.js.map +1 -1
  177. package/dist/server/io/yamlIO.js +85 -0
  178. package/dist/server/io/yamlIO.js.map +1 -1
  179. package/dist/server/routes/settings.js +1 -12
  180. package/dist/server/routes/settings.js.map +1 -1
  181. package/dist/server/routes/wizard.js +52 -2
  182. package/dist/server/routes/wizard.js.map +1 -1
  183. package/dist/shared/settingsCarveOut.js +118 -0
  184. package/dist/shared/settingsCarveOut.js.map +1 -0
  185. package/dist/shared/settingsSurface.js +7 -1
  186. package/dist/shared/settingsSurface.js.map +1 -1
  187. package/dist/ui/assets/index-DmR0mbk4.js.map +1 -1
  188. package/docs/contracts/settings-classes.md +29 -8
  189. package/docs/guidelines/agent-infra/memory-access.md +14 -3
  190. package/package.json +1 -1
  191. package/src/agent-src/templates/AGENTS.md +6 -10
  192. package/src/config/budgets.yml +29 -0
  193. package/src/config/evaluator-budgets.json +4 -4
  194. package/src/config/gate-coverage.yml +26 -0
  195. package/src/scripts/_cli/cmd_brand_status.ts +174 -0
  196. package/src/scripts/_cli/cmd_doctor.ts +6 -21
  197. package/src/scripts/_cli/cmd_mcp_available.ts +266 -0
  198. package/src/scripts/_cli/cmd_packs_active.ts +225 -0
  199. package/src/scripts/_cli/cmd_settings_get.ts +324 -0
  200. package/src/scripts/_dispatch.bash +75 -0
  201. package/src/scripts/_lib/agent_settings.ts +82 -16
  202. package/src/scripts/_lib/carrier_divergence.ts +135 -0
  203. package/src/scripts/_lib/git_common_dir.ts +128 -0
  204. package/src/scripts/_lib/host_capability.ts +84 -1
  205. package/src/scripts/_lib/obligation_frequency.ts +391 -0
  206. package/src/scripts/_lib/prompt_shape.ts +88 -0
  207. package/src/scripts/_lib/review_skipped_record.ts +102 -0
  208. package/src/scripts/_lib/rule_layer_overlap.ts +254 -0
  209. package/src/scripts/_lib/self_repair.ts +342 -0
  210. package/src/scripts/_lib/self_repair_store.ts +100 -0
  211. package/src/scripts/_lib/session_register.ts +361 -0
  212. package/src/scripts/_lib/transcript_entry.ts +80 -0
  213. package/src/scripts/check_branch_freshness.ts +255 -0
  214. package/src/scripts/check_council_config_location.ts +125 -1
  215. package/src/scripts/check_enforcement_coverage.ts +192 -7
  216. package/src/scripts/check_rule_projection_integrity.ts +364 -0
  217. package/src/scripts/check_standing_rule_delivery.ts +314 -0
  218. package/src/scripts/condense.ts +36 -2
  219. package/src/scripts/config/profiles.ts +13 -3
  220. package/src/scripts/conformance_scan.ts +307 -24
  221. package/src/scripts/council_availability_hook.ts +188 -0
  222. package/src/scripts/council_cli.ts +126 -7
  223. package/src/scripts/generate_settings_reference.ts +232 -0
  224. package/src/scripts/hook_manifest.yaml +155 -33
  225. package/src/scripts/hooks/block_no_verify.ts +95 -3
  226. package/src/scripts/hooks/block_unauthorized_git.ts +217 -2
  227. package/src/scripts/hooks/concern_registry.ts +12 -0
  228. package/src/scripts/hooks/delegation_nudge_hook.ts +450 -0
  229. package/src/scripts/hooks/end_review_nudge_hook.ts +726 -0
  230. package/src/scripts/hooks/orchestration_record_hook.ts +342 -0
  231. package/src/scripts/install.ts +151 -0
  232. package/src/scripts/language_mirror_hook.ts +49 -6
  233. package/src/scripts/measure_scope_dedup.ts +19 -13
  234. package/src/scripts/onboarding_gate_hook.ts +40 -1
  235. package/src/scripts/orchestration_backfill.ts +354 -0
  236. package/src/scripts/orchestration_record.ts +4 -1
  237. package/src/scripts/preamble_byte_census.ts +9 -20
  238. package/src/scripts/release.ts +142 -36
  239. package/src/scripts/report_carrier_divergence.ts +364 -0
  240. package/src/scripts/report_skill_activation.ts +257 -0
  241. package/src/scripts/report_skill_obligation_violations.ts +579 -0
  242. package/src/scripts/routing_doctor.ts +77 -6
  243. package/src/scripts/schemas/rule.schema.json +5 -0
  244. package/src/scripts/self_repair_cli.ts +240 -0
  245. package/src/scripts/self_repair_hook.ts +241 -0
  246. package/src/scripts/session_canary_hook.ts +35 -3
  247. package/src/scripts/session_register_hook.ts +288 -0
  248. package/src/scripts/sessions_cli.ts +140 -0
  249. package/src/scripts/validate_frontmatter.ts +68 -0
  250. package/src/server/io/yamlIO.ts +87 -0
  251. package/src/shared/settingsCarveOut.ts +143 -0
  252. package/src/shared/settingsSurface.ts +7 -1
@@ -27,21 +27,50 @@
27
27
  * store. It answers "did the shipped gates have anything to bite on?" —
28
28
  * which is also the only honest way to find out whether they help.
29
29
  */
30
+ import { createHash } from "node:crypto";
30
31
  import fs from "node:fs";
32
+ import os from "node:os";
31
33
  import path from "node:path";
32
34
  import { fileURLToPath, pathToFileURL } from "node:url";
33
35
 
36
+ import { censusRuleDir } from "./_lib/carrier_divergence.js";
37
+ import { entryText, isSidechain } from "./_lib/transcript_entry.js";
38
+
34
39
  import { classify } from "./language_mirror_hook.js";
40
+ import { isSyntheticPrompt } from "./_lib/prompt_shape.js";
35
41
  import { classifyAuthorization, type GitOp } from "./git_authorization_hook.js";
36
42
  import { BLOCK_OPS, commandOp } from "./hooks/block_unauthorized_git.js";
37
43
  import { isVacuousOutput, isCiPoll, pendingCount } from "./before_complete_hook.js";
38
44
  import { isEvaluationPrompt, isSelfScoped, preloadedVerdict } from "./hooks/evidence_independence.js";
39
45
 
46
+ /**
47
+ * This module's own repo root, NOT `process.cwd()`.
48
+ *
49
+ * Both the project carrier and the rate series are anchored here. Resolving them
50
+ * against the cwd made a run from any subdirectory record
51
+ * `delivered_project_tokens: 0` and append the series to a fresh
52
+ * `agents/runtime/state/` tree at that location — outside the ignore rule whose
53
+ * coverage the docstring claims, and silently splitting a series whose whole
54
+ * point is comparability. Under the bundled CLI `import.meta.url` still resolves
55
+ * inside the package, which is the tree whose rules are being measured.
56
+ */
57
+ const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
58
+
40
59
  export interface Violation {
41
60
  check: "language-pin" | "git-authorization" | "vacuous-evidence" | "evidence-steering";
42
61
  session: string;
43
62
  at: string;
44
63
  detail: string;
64
+ /**
65
+ * Language-pin only. Assistant turns since the last genuine user prompt, and
66
+ * whether a compaction boundary fell in between. Together they separate the
67
+ * two failures the round-5 audit found hiding under one count: a pin that was
68
+ * ABSENT because compaction removed it, and a pin that was PRESENT and
69
+ * ignored. The first is a state defect; the second is non-compliance, and
70
+ * only the second is unfixable by re-injection.
71
+ */
72
+ turns_since_prompt?: number;
73
+ compaction_since_prompt?: boolean;
45
74
  }
46
75
 
47
76
  export interface SessionReport {
@@ -51,12 +80,98 @@ export interface SessionReport {
51
80
  violations: Violation[];
52
81
  }
53
82
 
83
+ /**
84
+ * Rule text reaching context, per carrier — round-6 Phase 4.3, and the
85
+ * instrument the `essential` default-flip decision has been waiting on. Token
86
+ * basis: chars/4, the same estimate `preamble_byte_census` and
87
+ * `measure_scope_dedup` use, so the three numbers are comparable.
88
+ *
89
+ * MEASURED AT SCAN TIME, NOT PER SESSION — and the step asked for per-session.
90
+ * The delivered payload is a property of the CARRIERS ON DISK, and the
91
+ * transcript records none of it (verified in `preamble_byte_census`: a
92
+ * transcript carries `message.usage` counts and response content, no system or
93
+ * tools field). The carriers also change under the sessions — this round alone
94
+ * added three rules and refreshed the global install. So attaching today's
95
+ * figure to a session from three weeks ago would be a fabrication dressed as a
96
+ * per-session measurement, and the honest shape is one reading per scan run
97
+ * plus the forward series 4.5 records. That correction is why 4.3 and 4.5 are
98
+ * one mechanism rather than two.
99
+ */
100
+ export interface DeliveredPayload {
101
+ /**
102
+ * `present` distinguishes a measured zero from a carrier that is not there.
103
+ * Without it a run from the wrong directory records `tokens: 0`, halves
104
+ * `union_tokens`, and persists a record indistinguishable from a real reading
105
+ * into a series whose entire purpose is comparability over time. The sibling
106
+ * `report_carrier_divergence` carries the same flag and refuses to substitute a
107
+ * different tree; this is the same refusal.
108
+ */
109
+ project: { dir: string; present: boolean; files: number; tokens: number };
110
+ global: { dir: string; present: boolean; files: number; tokens: number };
111
+ /** What a machine carrying BOTH carriers pays — the figure M5 compared across projects. */
112
+ union_tokens: number;
113
+ }
114
+
115
+ /**
116
+ * The band M5 observed across the only three stores with a corpus worth
117
+ * measuring (`private/capisco` 39.2 %, this package 25.4 %,
118
+ * `private/agent-switch` 9.1 %). Both council members independently named the
119
+ * same falsifier for cancelling the volume test: **a fourth project falling
120
+ * outside this band**. It is a constant here, with its source, because a
121
+ * falsifier nobody can evaluate is not one.
122
+ */
123
+ export const OBSERVED_BAND = { low: 9.1, high: 39.2 } as const;
124
+
125
+ /**
126
+ * The smallest corpus the band was derived from — `private/capisco`, 1 978
127
+ * assistant turns. NOT a chosen threshold: a rate computed over materially fewer
128
+ * turns is not comparable to the three that defined the band, and declaring it
129
+ * out-of-band would fire the falsifier on corpus size rather than on behaviour.
130
+ *
131
+ * The first run of this instrument proved the point: the worktree's own store
132
+ * read 4.1 % over 606 turns and would have announced the falsifier on its second
133
+ * day of existence.
134
+ */
135
+ export const BAND_MIN_TURNS = 1978;
136
+
137
+ export type BandVerdict = "inside" | "outside" | "corpus-too-small";
138
+
139
+ /** Round-6 Phase 4.5 — the forward-capture record for one scan run. */
140
+ export interface RateRecord {
141
+ /** Not the store path: a stable digest of it. See `storeKey`. */
142
+ store_key: string;
143
+ sessions: number;
144
+ assistant_turns: number;
145
+ language_pin: number;
146
+ rate_pct: number;
147
+ /** `outside` is the falsifier firing; `corpus-too-small` is not a reading. */
148
+ band: BandVerdict;
149
+ /**
150
+ * `null` when the scanned store is NOT this repo's own — `measureDelivered` is
151
+ * always anchored on this checkout, so pairing another project's rate with
152
+ * these tokens would persist a figure that belongs to neither. Cross-project
153
+ * `--record` is exactly the use 4.5 exists for, so the mismatch would have been
154
+ * the common case rather than an edge one.
155
+ */
156
+ delivered_project_tokens: number | null;
157
+ delivered_global_tokens: number | null;
158
+ }
159
+
160
+ export function bandVerdict(rate_pct: number, assistant_turns: number): BandVerdict {
161
+ if (assistant_turns < BAND_MIN_TURNS) {
162
+ return "corpus-too-small";
163
+ }
164
+ return rate_pct >= OBSERVED_BAND.low && rate_pct <= OBSERVED_BAND.high ? "inside" : "outside";
165
+ }
166
+
54
167
  export interface ScanReport {
55
168
  scanned_at: string;
56
169
  store: string;
57
170
  sessions: number;
58
171
  totals: Record<string, number>;
59
172
  per_session: SessionReport[];
173
+ delivered: DeliveredPayload;
174
+ rate: RateRecord;
60
175
  }
61
176
 
62
177
  type Entry = Record<string, unknown>;
@@ -67,20 +182,14 @@ function _isObject(v: unknown): v is Record<string, unknown> {
67
182
 
68
183
  /** Text of a user-role entry, or null when it is not a real chat message. */
69
184
  export function userText(entry: Entry): string | null {
70
- if (entry["type"] !== "user" || entry["isSidechain"] === true) {
185
+ if (entry["type"] !== "user" || isSidechain(entry)) {
71
186
  return null;
72
187
  }
73
- const msg = entry["message"];
74
- const content = _isObject(msg) ? msg["content"] : undefined;
75
- let text = "";
76
- if (typeof content === "string") {
77
- text = content;
78
- } else if (Array.isArray(content)) {
79
- text = content
80
- .filter((b) => _isObject(b) && b["type"] === "text")
81
- .map((b) => String((b as Record<string, unknown>)["text"] ?? ""))
82
- .join("\n");
83
- }
188
+ // Shape handling moved to `_lib/transcript_entry.ts` so a second reader of the
189
+ // same field cannot get it wrong. It already did once, in this branch: a
190
+ // string-only reader saw 0 of the 41 injected skill bodies in this store,
191
+ // because they all arrive as content blocks.
192
+ const text = entryText(entry);
84
193
  if (!text.trim()) {
85
194
  return null;
86
195
  }
@@ -92,20 +201,10 @@ export function userText(entry: Entry): string | null {
92
201
 
93
202
  /** Assistant prose, or null. */
94
203
  export function assistantText(entry: Entry): string | null {
95
- if (entry["type"] !== "assistant" || entry["isSidechain"] === true) {
96
- return null;
97
- }
98
- const msg = entry["message"];
99
- const content = _isObject(msg) ? msg["content"] : undefined;
100
- if (!Array.isArray(content)) {
204
+ if (entry["type"] !== "assistant" || isSidechain(entry)) {
101
205
  return null;
102
206
  }
103
- const text = content
104
- .filter((b) => _isObject(b) && b["type"] === "text")
105
- .map((b) => String((b as Record<string, unknown>)["text"] ?? ""))
106
- .join("\n")
107
- .trim();
108
- return text || null;
207
+ return entryText(entry).trim() || null;
109
208
  }
110
209
 
111
210
  /** Tool-use blocks from an assistant entry. */
@@ -165,6 +264,12 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
165
264
  let authorized = new Set<GitOp>();
166
265
  let sawPending = false;
167
266
  let evaluationsThisTurn = 0;
267
+ // Round-5 provenance for language violations. The round-5 audit could only
268
+ // separate "the pin was absent" from "the pin was ignored" by reconstructing
269
+ // both facts from the raw store afterwards; recording them here is what makes
270
+ // that split reproducible against a future corpus instead of re-argued.
271
+ let turnsSincePrompt = 0;
272
+ let compactionSincePrompt = false;
168
273
 
169
274
  for (const line of lines) {
170
275
  if (!line.trim()) {
@@ -196,13 +301,36 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
196
301
  continue;
197
302
  }
198
303
 
304
+ // A compaction boundary removes context the pin lived in. It is not a turn
305
+ // and it is not a prompt, but it changes what the model can still see — so
306
+ // it is recorded, not skipped silently.
307
+ if (entry["type"] === "system" && entry["subtype"] === "compact_boundary") {
308
+ compactionSincePrompt = true;
309
+ continue;
310
+ }
311
+
199
312
  const ut = userText(entry);
200
313
  if (ut !== null) {
314
+ // A compaction SUMMARY arrives as a `user` entry. Treating it as a chat
315
+ // message would let its language reset the pin — the exact defect class
316
+ // that made the first detector report 303 instead of 626.
317
+ if (entry["isCompactSummary"] === true) {
318
+ continue;
319
+ }
320
+ // A harness-generated turn is not a chat message. The HOOK has skipped
321
+ // these since round 5 and this scanner did not, so the two were measuring
322
+ // different populations while reporting one number. Same predicate, one
323
+ // module, so they cannot drift again.
324
+ if (isSyntheticPrompt(ut)) {
325
+ continue;
326
+ }
201
327
  if (isInjectedBody(ut)) {
202
328
  // Not a chat message — it changes neither the pin nor the ledger.
203
329
  continue;
204
330
  }
205
331
  report.user_turns += 1;
332
+ turnsSincePrompt = 0;
333
+ compactionSincePrompt = false;
206
334
  const c = classify(ut);
207
335
  if (c.language !== "und") {
208
336
  pinned = c.language;
@@ -222,6 +350,7 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
222
350
  const prose = assistantText(entry);
223
351
  if (prose !== null) {
224
352
  report.assistant_turns += 1;
353
+ turnsSincePrompt += 1;
225
354
  if (pinned === "de") {
226
355
  const first = prose.split("\n").find((l) => l.trim()) ?? "";
227
356
  if (HARNESS_TEXT.test(first.trim())) {
@@ -234,6 +363,8 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
234
363
  session: sessionId,
235
364
  at,
236
365
  detail: `German pin, English reply opener: "${first.slice(0, 110)}"`,
366
+ turns_since_prompt: turnsSincePrompt,
367
+ compaction_since_prompt: compactionSincePrompt,
237
368
  });
238
369
  }
239
370
  }
@@ -293,6 +424,19 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
293
424
  return report;
294
425
  }
295
426
 
427
+ /**
428
+ * Where `--record` appends by default. Under the gitignored `agents/runtime/`
429
+ * because the series is per-machine observation, and committing it would publish
430
+ * the project set `storeKey` exists to hide.
431
+ */
432
+ export const DEFAULT_RATE_SERIES = path.join(
433
+ REPO_ROOT,
434
+ "agents",
435
+ "runtime",
436
+ "state",
437
+ "conformance-rates.jsonl",
438
+ );
439
+
296
440
  /** Default Claude Code transcript store for a project directory. */
297
441
  export function defaultStore(projectDir: string): string {
298
442
  // Claude Code slugs BOTH separators and dots: /Users/x/.claude → -Users-x--claude.
@@ -300,6 +444,51 @@ export function defaultStore(projectDir: string): string {
300
444
  return path.join(process.env["HOME"] ?? "", ".claude", "projects", slug);
301
445
  }
302
446
 
447
+ /** chars/4, the estimate every other payload measurement in this repo uses. */
448
+ function _tokens(chars: number): number {
449
+ return Math.round(chars / 4);
450
+ }
451
+
452
+ /**
453
+ * Measure what each carrier delivers. Defaults are the two real carriers; both
454
+ * are injectable so a test never reads the developer's home.
455
+ */
456
+ export function measureDelivered(projectRulesDir: string, globalRulesDir: string): DeliveredPayload {
457
+ const p = censusRuleDir(projectRulesDir);
458
+ const g = censusRuleDir(globalRulesDir);
459
+ return {
460
+ project: {
461
+ dir: projectRulesDir,
462
+ present: fs.existsSync(projectRulesDir),
463
+ files: p.files,
464
+ tokens: _tokens(p.chars),
465
+ },
466
+ global: {
467
+ dir: globalRulesDir,
468
+ present: fs.existsSync(globalRulesDir),
469
+ files: g.files,
470
+ tokens: _tokens(g.chars),
471
+ },
472
+ union_tokens: _tokens(p.chars + g.chars),
473
+ };
474
+ }
475
+
476
+ /**
477
+ * A stable, non-reversible key for a store — NEVER the path.
478
+ *
479
+ * The store path is `-Users-<realname>-projects-<client>-…`: a real name, a
480
+ * directory layout, and often a customer identifier. `domain-safety-pii` §
481
+ * Surface 3 forbids exporting a direct identifier and
482
+ * `low-impact-corpus-privacy-floor` names project-rooted paths outright, so the
483
+ * forward series is keyed on a digest. It still answers the only question 4.5
484
+ * asks of it — "is this the same project as last time, and is a FOURTH one
485
+ * outside the band" — because equality and novelty survive hashing while the
486
+ * identity does not.
487
+ */
488
+ export function storeKey(store: string): string {
489
+ return createHash("sha256").update(path.resolve(store)).digest("hex").slice(0, 12);
490
+ }
491
+
303
492
  export function scanStore(store: string, limit: number): ScanReport {
304
493
  const files = fs
305
494
  .readdirSync(store)
@@ -330,15 +519,59 @@ export function scanStore(store: string, limit: number): ScanReport {
330
519
  }
331
520
  }
332
521
 
522
+ const assistant_turns = per_session.reduce((n, s) => n + s.assistant_turns, 0);
523
+ const language_pin = totals["language-pin"] ?? 0;
524
+ // Round ONCE, then use the rounded value for both the record and the verdict.
525
+ // Rounding after the comparison lets a raw 9.06 persist `rate_pct: 9.1` beside
526
+ // `band: "outside"`, and print "9.1%" directly above "OUTSIDE the 9.1-39.2%
527
+ // band" — a rounding artefact announcing the declared falsifier.
528
+ const rate_pct =
529
+ assistant_turns === 0 ? 0 : Number(((100 * language_pin) / assistant_turns).toFixed(1));
530
+ const delivered = measureDelivered(
531
+ path.join(REPO_ROOT, ".claude", "rules"),
532
+ path.join(process.env["HOME"] ?? os.homedir(), ".claude", "rules"),
533
+ );
534
+ const ownStore = path.resolve(store) === path.resolve(defaultStore(REPO_ROOT));
535
+
333
536
  return {
334
537
  scanned_at: new Date().toISOString(),
335
538
  store,
336
539
  sessions: per_session.length,
337
540
  totals,
338
541
  per_session,
542
+ delivered,
543
+ rate: {
544
+ store_key: storeKey(store),
545
+ sessions: per_session.length,
546
+ assistant_turns,
547
+ language_pin,
548
+ rate_pct,
549
+ band: bandVerdict(rate_pct, assistant_turns),
550
+ // Only attach the carrier figures when the rate and the carriers describe
551
+ // the same project.
552
+ delivered_project_tokens: ownStore ? delivered.project.tokens : null,
553
+ delivered_global_tokens: ownStore ? delivered.global.tokens : null,
554
+ },
339
555
  };
340
556
  }
341
557
 
558
+ /**
559
+ * Append one scan's rate record — round-6 Phase 4.5's forward capture.
560
+ *
561
+ * PII-exclusion-by-construction, the shape `artifact-engagement-recording`
562
+ * applies to telemetry: `RateRecord` has NO field able to hold a path, a prompt,
563
+ * a session id, or any free text, so there is no scrubber here that could fail.
564
+ * Keep it that way — never widen it with a `note` or `extra`.
565
+ *
566
+ * Default destination is under the gitignored `agents/runtime/`, because the
567
+ * series is per-machine observation and committing it would publish the very
568
+ * project set the key exists to hide.
569
+ */
570
+ export function recordRate(file: string, scanned_at: string, rate: RateRecord): void {
571
+ fs.mkdirSync(path.dirname(file), { recursive: true });
572
+ fs.appendFileSync(file, `${JSON.stringify({ measured_at: scanned_at, ...rate })}\n`, "utf8");
573
+ }
574
+
342
575
  export function render(report: ScanReport): string {
343
576
  const lines: string[] = [];
344
577
  lines.push(`conformance:behavior · ${report.sessions} session(s) · ${report.store}`);
@@ -356,6 +589,45 @@ export function render(report: ScanReport): string {
356
589
  lines.push(` ${s.session} ${s.violations.length} across ${s.assistant_turns} turns`);
357
590
  }
358
591
  }
592
+ const d = report.delivered;
593
+ lines.push("");
594
+ lines.push(" Delivered rule text (chars/4, carriers as they stand NOW — not per-session):");
595
+ const carrier = (label: string, c: { present: boolean; files: number; tokens: number }): string =>
596
+ c.present
597
+ ? ` ${label} ${String(c.files).padStart(4)} rules ${String(c.tokens).padStart(7)} tok`
598
+ : ` ${label} ABSENT — not a measured zero, this carrier does not exist here`;
599
+ lines.push(carrier("project", d.project));
600
+ lines.push(carrier("global ", d.global));
601
+ lines.push(` union ${String(d.union_tokens).padStart(7)} tok`);
602
+ lines.push(" A session's own payload is NOT recoverable: the transcript records no system");
603
+ lines.push(" or tools field, and the carriers change under the sessions. This is one");
604
+ lines.push(" reading; the series below is what makes it interpretable over time.");
605
+ lines.push("");
606
+ lines.push(
607
+ ` language-pin rate ${report.rate.rate_pct.toFixed(1)}% ` +
608
+ `(${report.rate.language_pin} of ${report.rate.assistant_turns} assistant turns)`,
609
+ );
610
+ if (report.rate.band === "corpus-too-small") {
611
+ lines.push(
612
+ ` Not compared: ${report.rate.assistant_turns} turns is below the ${BAND_MIN_TURNS} of the`,
613
+ );
614
+ lines.push(" smallest corpus the band was derived from, so a verdict here would be about");
615
+ lines.push(" corpus size, not behaviour.");
616
+ } else if (report.rate.band === "inside") {
617
+ lines.push(
618
+ ` Inside the observed ${OBSERVED_BAND.low}-${OBSERVED_BAND.high}% band (M5, n=3).`,
619
+ );
620
+ } else {
621
+ lines.push(
622
+ ` ⚠️ OUTSIDE the observed ${OBSERVED_BAND.low}-${OBSERVED_BAND.high}% band (M5, n=3).`,
623
+ );
624
+ lines.push(" This is the falsifier both council members named when the volume test was");
625
+ lines.push(" cancelled: a project outside the band is the signal that the cancellation");
626
+ lines.push(" was wrong. It is a reason to look, not a defect in this run.");
627
+ lines.push(" CHECK FIRST that this is a different PROJECT: a git worktree gets its own");
628
+ lines.push(" transcript store under the same project, so a store-keyed series counts it");
629
+ lines.push(" as new. Store novelty is not project novelty.");
630
+ }
359
631
  lines.push("");
360
632
  lines.push(" Scanned checks are exactly the mechanised ones. Ask-shape, session-canary,");
361
633
  lines.push(" promissory closings and checkbox batching are left as prose and NOT measured.");
@@ -368,6 +640,7 @@ export function main(argv?: string[]): number {
368
640
  let limit = 30;
369
641
  let json = false;
370
642
  let out: string | null = null;
643
+ let record: string | null = null;
371
644
  for (let i = 0; i < args.length; i += 1) {
372
645
  const a = args[i];
373
646
  if (a === "--store" && args[i + 1] !== undefined) {
@@ -378,6 +651,13 @@ export function main(argv?: string[]): number {
378
651
  json = true;
379
652
  } else if (a === "--output" && args[i + 1] !== undefined) {
380
653
  out = args[++i] as string;
654
+ } else if (a === "--record") {
655
+ // Opt-in, and a bare `--record` takes the default path rather than
656
+ // swallowing the next flag as a filename.
657
+ record =
658
+ args[i + 1] !== undefined && !(args[i + 1] as string).startsWith("-")
659
+ ? (args[++i] as string)
660
+ : DEFAULT_RATE_SERIES;
381
661
  }
382
662
  }
383
663
  const resolved = store ?? defaultStore(process.cwd());
@@ -389,6 +669,9 @@ export function main(argv?: string[]): number {
389
669
  if (out) {
390
670
  fs.writeFileSync(out, `${JSON.stringify(report, null, 2)}\n`, "utf8");
391
671
  }
672
+ if (record) {
673
+ recordRate(record, report.scanned_at, report.rate);
674
+ }
392
675
  // fs.writeSync, not process.stdout.write: `process.exit()` below does not
393
676
  // flush an async pipe write, so `--json | jq` reproducibly received a
394
677
  // truncated 64 KB document with no error (measured: 85,169 vs 65,536 bytes).
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Council-availability fact — `session_start` concern.
4
+ *
5
+ * Carries ONE fact the context does not otherwise contain: whether an AI council
6
+ * is configured for this machine, and where its config was resolved from.
7
+ *
8
+ * WHY THIS EXISTS — the measured failure, not a general reminder.
9
+ *
10
+ * 2026-08-08, in a consumer project with no project-scope install, an agent
11
+ * announced:
12
+ *
13
+ * "Kein Council konfiguriert (keine `.agent-settings.yml`) — ich nutze
14
+ * Subagenten-Fächer mit gegnerischen Linsen als Ersatz"
15
+ *
16
+ * and substituted a weaker path. The council was configured the whole time. The
17
+ * user's correction was blunt and correct. The inference was wrong twice over:
18
+ *
19
+ * 1. Council config has not lived in `.agent-settings.yml` since the Phase-0
20
+ * migration removed that block.
21
+ * 2. Per ADR-104 the project tree is NEVER searched for council config, so the
22
+ * presence or absence of ANY project file says nothing about availability
23
+ * in either direction.
24
+ *
25
+ * The guidance was not missing. `/council default` states it emphatically in
26
+ * four separate places, and it is correct. But a command file loads only when
27
+ * that command is invoked, and the agent was answering a prior question — "does
28
+ * this capability exist?" — before invoking anything. The always-loaded rule
29
+ * layer refers to the council conditionally ("when the council is enabled")
30
+ * and never says how to determine that. Faced with a conditional and no test,
31
+ * the agent guessed the location every other setting lives in, and that guess
32
+ * is the one location this config specifically is not in.
33
+ *
34
+ * So this is not more prose, and it is deliberately not a rule. It is the
35
+ * language-mirror pattern applied to a capability: a hook can supply a fact the
36
+ * transcript cannot express. `council:status` makes the fact *obtainable*; this
37
+ * makes it *present*, which is the difference between a check an agent could
38
+ * have run and one it did not need to.
39
+ *
40
+ * Advisory, always. It never blocks, and a resolution failure degrades to
41
+ * silence rather than to a claim — a hook that guessed here would reproduce the
42
+ * defect it exists to remove.
43
+ */
44
+
45
+ import * as fs from 'node:fs';
46
+ import * as path from 'node:path';
47
+ import { fileURLToPath, pathToFileURL } from 'node:url';
48
+
49
+ import { readHookStdin } from './hooks/hook_stdin.js';
50
+ import {
51
+ COUNCIL_CONFIG_ENV,
52
+ load_council_config,
53
+ resolve_config_path,
54
+ } from './ai_council/config.js';
55
+
56
+ const EXIT_ALLOW = 0;
57
+
58
+ export interface Availability {
59
+ configured: boolean;
60
+ path: string;
61
+ exists: boolean;
62
+ provenance: 'user-global' | 'env-override';
63
+ membersEnabled: number;
64
+ memberNames: string[];
65
+ }
66
+
67
+ /**
68
+ * Resolve availability without throwing. A malformed or unreadable config is
69
+ * reported as not-configured with `exists: true`, which is the distinction a
70
+ * reader needs: "write a config" and "fix your config" are different actions.
71
+ */
72
+ export function resolveAvailability(
73
+ cwd: string,
74
+ env: Record<string, string | undefined>,
75
+ ): Availability | null {
76
+ let p: string;
77
+ try {
78
+ p = String(resolve_config_path(cwd, { env: env as never }));
79
+ } catch {
80
+ return null; // cannot resolve — say nothing rather than guess
81
+ }
82
+ const exists = fs.existsSync(p);
83
+ const provenance = env[COUNCIL_CONFIG_ENV] ? 'env-override' : 'user-global';
84
+ if (!exists) {
85
+ return { configured: false, path: p, exists: false, provenance, membersEnabled: 0, memberNames: [] };
86
+ }
87
+ try {
88
+ const cfg = load_council_config(p);
89
+ const enabled = [...cfg.members.entries()].filter(([, m]) => m.enabled).map(([n]) => n);
90
+ return {
91
+ configured: cfg.enabled && enabled.length > 0,
92
+ path: p,
93
+ exists: true,
94
+ provenance,
95
+ membersEnabled: enabled.length,
96
+ memberNames: enabled,
97
+ };
98
+ } catch {
99
+ return { configured: false, path: p, exists: true, provenance, membersEnabled: 0, memberNames: [] };
100
+ }
101
+ }
102
+
103
+ export function buildFact(a: Availability): string {
104
+ const head = a.configured
105
+ ? `An AI council IS configured: ${String(a.membersEnabled)} enabled member(s) — ${a.memberNames.join(', ')}.`
106
+ : a.exists
107
+ ? 'An AI council config exists but is NOT usable (unreadable, disabled, or no enabled member).'
108
+ : 'No AI council is configured on this machine.';
109
+ return [
110
+ '<council-availability>',
111
+ head,
112
+ `Resolved from ${a.provenance}: ${a.path}`,
113
+ 'The project tree is NEVER searched for council config (ADR-104), and the',
114
+ 'config has not lived in `.agent-settings.yml` since the Phase-0 migration.',
115
+ 'So do NOT infer council availability from any project file, in either',
116
+ 'direction — this line is the answer, and `agent-config council:status`',
117
+ 're-checks it on demand without spending anything.',
118
+ a.configured
119
+ ? 'Never announce that no council is configured, and never substitute a weaker path on that basis.'
120
+ : 'A substitute is legitimate here — say which one you are using and why.',
121
+ '</council-availability>',
122
+ ].join('\n');
123
+ }
124
+
125
+ function _workspaceRoot(env: Record<string, unknown>): string {
126
+ for (const key of ['project_dir', 'projectDir', 'cwd', 'workspace_root']) {
127
+ const v = env[key];
128
+ if (typeof v === 'string' && v.trim() !== '') {
129
+ return v;
130
+ }
131
+ }
132
+ return process.cwd();
133
+ }
134
+
135
+ export function run(stdin: string, options: { env?: Record<string, string | undefined> } = {}): number {
136
+ let envelope: Record<string, unknown> = {};
137
+ if (stdin.trim() !== '') {
138
+ try {
139
+ const parsed = JSON.parse(stdin) as unknown;
140
+ if (parsed !== null && typeof parsed === 'object') {
141
+ envelope = parsed as Record<string, unknown>;
142
+ }
143
+ } catch {
144
+ return EXIT_ALLOW; // never act on a malformed envelope
145
+ }
146
+ }
147
+ const event = envelope['event'];
148
+ const slot = typeof event === 'string' && event !== '' ? event : 'session_start';
149
+ if (slot !== 'session_start') {
150
+ return EXIT_ALLOW;
151
+ }
152
+ const a = resolveAvailability(_workspaceRoot(envelope), options.env ?? process.env);
153
+ if (a === null) {
154
+ return EXIT_ALLOW;
155
+ }
156
+ process.stdout.write(
157
+ `${JSON.stringify({
158
+ decision: 'allow',
159
+ reason: `council-availability: ${a.configured ? 'configured' : 'not configured'}`,
160
+ context: buildFact(a),
161
+ })}\n`,
162
+ );
163
+ return EXIT_ALLOW;
164
+ }
165
+
166
+ export function main(argv?: string[]): number {
167
+ const args = argv ?? process.argv.slice(2);
168
+ let env = process.env;
169
+ void args;
170
+ return run(readHookStdin(), { env });
171
+ }
172
+
173
+ declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
174
+ function _isCliEntry(): boolean {
175
+ if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) {
176
+ return false;
177
+ }
178
+ if (process.argv[1] === undefined) {
179
+ return false;
180
+ }
181
+ return import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href;
182
+ }
183
+
184
+ if (_isCliEntry()) {
185
+ process.exit(main());
186
+ }
187
+
188
+ void fileURLToPath;