@meetless/mla 0.1.4

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 (202) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +81 -0
  3. package/dist/build-info.json +9 -0
  4. package/dist/bundles/ask-core.js +396 -0
  5. package/dist/bundles/mcp.js +16592 -0
  6. package/dist/bundles/trace-core.js +263 -0
  7. package/dist/cli.js +828 -0
  8. package/dist/commands/activate.js +781 -0
  9. package/dist/commands/adoption.js +130 -0
  10. package/dist/commands/ask.js +290 -0
  11. package/dist/commands/context.js +114 -0
  12. package/dist/commands/debug.js +313 -0
  13. package/dist/commands/doctor.js +1021 -0
  14. package/dist/commands/enrich.js +427 -0
  15. package/dist/commands/evidence.js +229 -0
  16. package/dist/commands/flush.js +184 -0
  17. package/dist/commands/graph.js +104 -0
  18. package/dist/commands/init.js +272 -0
  19. package/dist/commands/internal-active-review.js +322 -0
  20. package/dist/commands/internal-auto-index.js +188 -0
  21. package/dist/commands/internal-capture-decisions.js +320 -0
  22. package/dist/commands/internal-evidence-correlate.js +239 -0
  23. package/dist/commands/internal-evidence-hooks.js +240 -0
  24. package/dist/commands/internal-evidence-inject.js +231 -0
  25. package/dist/commands/internal-finalize.js +221 -0
  26. package/dist/commands/internal-pretool-observe.js +225 -0
  27. package/dist/commands/internal-refresh.js +136 -0
  28. package/dist/commands/internal-session-nudge.js +120 -0
  29. package/dist/commands/internal-steer-sync.js +117 -0
  30. package/dist/commands/internal-turn-recap.js +140 -0
  31. package/dist/commands/kb.js +375 -0
  32. package/dist/commands/kb_add.js +681 -0
  33. package/dist/commands/kb_forget.js +283 -0
  34. package/dist/commands/kb_move.js +45 -0
  35. package/dist/commands/kb_pending.js +410 -0
  36. package/dist/commands/kb_personal.js +149 -0
  37. package/dist/commands/kb_promote.js +188 -0
  38. package/dist/commands/kb_purge.js +168 -0
  39. package/dist/commands/kb_reingest.js +335 -0
  40. package/dist/commands/kb_retime.js +170 -0
  41. package/dist/commands/kb_review.js +391 -0
  42. package/dist/commands/kb_revision.js +179 -0
  43. package/dist/commands/kb_show.js +385 -0
  44. package/dist/commands/label.js +226 -0
  45. package/dist/commands/login.js +295 -0
  46. package/dist/commands/logout.js +108 -0
  47. package/dist/commands/mcp-supervisor.js +93 -0
  48. package/dist/commands/mcp.js +227 -0
  49. package/dist/commands/queue-prune.js +98 -0
  50. package/dist/commands/review.js +358 -0
  51. package/dist/commands/rewire.js +124 -0
  52. package/dist/commands/rules.js +728 -0
  53. package/dist/commands/scan-context.js +67 -0
  54. package/dist/commands/session.js +347 -0
  55. package/dist/commands/stats.js +479 -0
  56. package/dist/commands/status.js +61 -0
  57. package/dist/commands/summary.js +250 -0
  58. package/dist/commands/turn.js +114 -0
  59. package/dist/commands/uninstall.js +222 -0
  60. package/dist/commands/whoami.js +102 -0
  61. package/dist/commands/workspace.js +130 -0
  62. package/dist/hooks-template/ce0-post-tool-use.sh +34 -0
  63. package/dist/hooks-template/ce0-session-start.sh +49 -0
  64. package/dist/hooks-template/ce0-stop.sh +29 -0
  65. package/dist/hooks-template/ce0-user-prompt-submit.sh +38 -0
  66. package/dist/hooks-template/common.sh +934 -0
  67. package/dist/hooks-template/event-batch-filter.jq +67 -0
  68. package/dist/hooks-template/flush.sh +503 -0
  69. package/dist/hooks-template/post-tool-use.sh +423 -0
  70. package/dist/hooks-template/pre-tool-use.sh +69 -0
  71. package/dist/hooks-template/session-start.sh +140 -0
  72. package/dist/hooks-template/stop.sh +308 -0
  73. package/dist/hooks-template/user-prompt-submit.sh +1162 -0
  74. package/dist/lib/activation.js +79 -0
  75. package/dist/lib/active-conflict-cache.js +141 -0
  76. package/dist/lib/active-memory.js +59 -0
  77. package/dist/lib/active-review-runner.js +26 -0
  78. package/dist/lib/agent-decision/index.js +25 -0
  79. package/dist/lib/agent-decision/keys.js +49 -0
  80. package/dist/lib/agent-decision/normalize-claude.js +183 -0
  81. package/dist/lib/agent-decision/types.js +21 -0
  82. package/dist/lib/agent-decision/validate.js +216 -0
  83. package/dist/lib/analytics/capture.js +96 -0
  84. package/dist/lib/analytics/command-event.js +267 -0
  85. package/dist/lib/analytics/consent.js +58 -0
  86. package/dist/lib/analytics/coverage-gap.js +96 -0
  87. package/dist/lib/analytics/envelope.js +236 -0
  88. package/dist/lib/analytics/event-id.js +86 -0
  89. package/dist/lib/analytics/evidence.js +150 -0
  90. package/dist/lib/analytics/followthrough.js +194 -0
  91. package/dist/lib/analytics/forwarder.js +109 -0
  92. package/dist/lib/analytics/logs.js +78 -0
  93. package/dist/lib/analytics/metrics.js +78 -0
  94. package/dist/lib/analytics/recorder.js +92 -0
  95. package/dist/lib/analytics/review-analytics.js +75 -0
  96. package/dist/lib/analytics/sequence.js +77 -0
  97. package/dist/lib/analytics/store.js +131 -0
  98. package/dist/lib/analytics/turn-recap.js +279 -0
  99. package/dist/lib/artifact_id.js +108 -0
  100. package/dist/lib/auth-breaker.js +161 -0
  101. package/dist/lib/auto-index.js +112 -0
  102. package/dist/lib/classifier.js +88 -0
  103. package/dist/lib/config.js +298 -0
  104. package/dist/lib/conflict-advisory.js +64 -0
  105. package/dist/lib/debug-bundle.js +520 -0
  106. package/dist/lib/enrichment/ingest.js +301 -0
  107. package/dist/lib/enrichment/plan.js +253 -0
  108. package/dist/lib/enrichment/protocol.js +359 -0
  109. package/dist/lib/enrichment/scout-brief.js +176 -0
  110. package/dist/lib/failure-telemetry.js +444 -0
  111. package/dist/lib/git.js +200 -0
  112. package/dist/lib/governance-cache.js +77 -0
  113. package/dist/lib/governed-path-cache.js +76 -0
  114. package/dist/lib/http.js +677 -0
  115. package/dist/lib/identity-envelope.js +23 -0
  116. package/dist/lib/kb-candidate.js +65 -0
  117. package/dist/lib/kb_acl.js +98 -0
  118. package/dist/lib/login.js +353 -0
  119. package/dist/lib/mcp-fetchers.js +130 -0
  120. package/dist/lib/mcp-restart.js +47 -0
  121. package/dist/lib/observability.js +805 -0
  122. package/dist/lib/open-url.js +33 -0
  123. package/dist/lib/orphan-guard.js +70 -0
  124. package/dist/lib/packaged.js +21 -0
  125. package/dist/lib/reconcile-sessions.js +171 -0
  126. package/dist/lib/redactor.js +89 -0
  127. package/dist/lib/relationship-candidate-query.js +27 -0
  128. package/dist/lib/render.js +611 -0
  129. package/dist/lib/rules/applicability.js +64 -0
  130. package/dist/lib/rules/attest-code-rule-version.js +47 -0
  131. package/dist/lib/rules/attest-notes-location.js +217 -0
  132. package/dist/lib/rules/attest-rule-version.js +69 -0
  133. package/dist/lib/rules/canonical-json.js +97 -0
  134. package/dist/lib/rules/ce0-emit.js +64 -0
  135. package/dist/lib/rules/ce0-evidence.js +281 -0
  136. package/dist/lib/rules/ce0-recall-sample.js +82 -0
  137. package/dist/lib/rules/ce0-rule.js +55 -0
  138. package/dist/lib/rules/ce0-sampling-bucket.js +15 -0
  139. package/dist/lib/rules/ce0-store.js +683 -0
  140. package/dist/lib/rules/ce0-telemetry-project.js +93 -0
  141. package/dist/lib/rules/ce0-telemetry.js +158 -0
  142. package/dist/lib/rules/code-rule-registry.js +17 -0
  143. package/dist/lib/rules/command-match.js +185 -0
  144. package/dist/lib/rules/consult-evidence-binding.js +27 -0
  145. package/dist/lib/rules/consultation-capture-adapter.js +193 -0
  146. package/dist/lib/rules/content-match.js +56 -0
  147. package/dist/lib/rules/deny-admission.js +99 -0
  148. package/dist/lib/rules/durable-observation.js +190 -0
  149. package/dist/lib/rules/enforce-notes-version.js +421 -0
  150. package/dist/lib/rules/evaluation-input-hash.js +126 -0
  151. package/dist/lib/rules/evaluator.js +108 -0
  152. package/dist/lib/rules/inert-rule-families.js +51 -0
  153. package/dist/lib/rules/input-authority-resolver.js +241 -0
  154. package/dist/lib/rules/interception-schema.js +170 -0
  155. package/dist/lib/rules/interception-store.js +267 -0
  156. package/dist/lib/rules/live-input-authority.js +66 -0
  157. package/dist/lib/rules/local-matcher.js +108 -0
  158. package/dist/lib/rules/local-observe.js +79 -0
  159. package/dist/lib/rules/local-rule-version-repo.js +214 -0
  160. package/dist/lib/rules/memory-requirement.js +109 -0
  161. package/dist/lib/rules/notes-observe.js +39 -0
  162. package/dist/lib/rules/notes-path.js +261 -0
  163. package/dist/lib/rules/notes-rule.js +75 -0
  164. package/dist/lib/rules/observe-adapter.js +114 -0
  165. package/dist/lib/rules/observed-rule-hash.js +119 -0
  166. package/dist/lib/rules/prompt-submit-adapter.js +132 -0
  167. package/dist/lib/rules/requirement-subject.js +240 -0
  168. package/dist/lib/rules/rule-activity.js +67 -0
  169. package/dist/lib/rules/rule-version-hash.js +151 -0
  170. package/dist/lib/rules/runtime-scope.js +55 -0
  171. package/dist/lib/rules/stop-adapter.js +116 -0
  172. package/dist/lib/rules/stop-response-snapshot.js +174 -0
  173. package/dist/lib/rules/types.js +10 -0
  174. package/dist/lib/rules/ulid.js +46 -0
  175. package/dist/lib/rules/version-evaluation.js +156 -0
  176. package/dist/lib/scanner/agent-memory.js +99 -0
  177. package/dist/lib/scanner/bootstrap-summary.js +87 -0
  178. package/dist/lib/scanner/cache.js +59 -0
  179. package/dist/lib/scanner/frontmatter.js +42 -0
  180. package/dist/lib/scanner/parse-directives.js +69 -0
  181. package/dist/lib/scanner/parse-structured.js +72 -0
  182. package/dist/lib/scanner/render.js +73 -0
  183. package/dist/lib/scanner/scan.js +132 -0
  184. package/dist/lib/scanner/score.js +38 -0
  185. package/dist/lib/scanner/scout-mission.js +126 -0
  186. package/dist/lib/scanner/types.js +7 -0
  187. package/dist/lib/session-scope.js +195 -0
  188. package/dist/lib/spool.js +355 -0
  189. package/dist/lib/staleness.js +100 -0
  190. package/dist/lib/steer-cache.js +87 -0
  191. package/dist/lib/tagged-reference.js +20 -0
  192. package/dist/lib/temporal.js +109 -0
  193. package/dist/lib/turn-recap-emit.js +67 -0
  194. package/dist/lib/unwire.js +253 -0
  195. package/dist/lib/update-check.js +469 -0
  196. package/dist/lib/update-notifier.js +217 -0
  197. package/dist/lib/upgrade-apply.js +643 -0
  198. package/dist/lib/wire.js +1087 -0
  199. package/dist/lib/workspace.js +96 -0
  200. package/dist/lib/zip.js +154 -0
  201. package/dist/pretool-entry.js +37 -0
  202. package/package.json +75 -0
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ // The CE0 hook subcommands: the live wiring between Claude Code's UserPromptSubmit,
3
+ // PostToolUse, and Stop hooks and the CE0 durable store
4
+ // (notes/20260617-evidence-consultation-forcing-function-proposal.md §4.1, the one
5
+ // remaining durable-layer piece). The managed ce0-*.sh hooks pipe their raw hook stdin
6
+ // into one of these subcommands; each is a thin IO shell over a committed adapter.
7
+ //
8
+ // Every subcommand obeys the same RECORD_ONLY discipline as the adapters it wraps:
9
+ // - It ALWAYS writes the empty `{}` body to stdout and exits 0. It can never inject an
10
+ // additionalContext, never deny, never block a turn. Injection is a CE2 concern that
11
+ // demands a new immutable rule version; CE0 is a measurement harness.
12
+ // - It is dormant when no workspace resolves (a repo CE0 is not bound to): it opens no
13
+ // store and writes nothing, it just emits the pass-through body. The workspace
14
+ // resolution IS the activation gate; the scripts carry no activation check.
15
+ // - It fails soft on every error. Malformed stdin, a missing store directory, a
16
+ // persistence fault: all are swallowed, leaving the clean `{}` exit-0 pass-through.
17
+ // A CE0 bookkeeping failure must never disturb the turn it observed.
18
+ //
19
+ // The subcommands take no argv flags (the hook pipes everything on stdin), mirroring
20
+ // `_internal pretool-observe`.
21
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.runInternalEvidenceTurnOpen = runInternalEvidenceTurnOpen;
56
+ exports.runInternalEvidenceCapture = runInternalEvidenceCapture;
57
+ exports.runInternalEvidenceStop = runInternalEvidenceStop;
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const perf_hooks_1 = require("perf_hooks");
61
+ const evidence_1 = require("./evidence");
62
+ const workspace_1 = require("../lib/workspace");
63
+ const ce0_store_1 = require("../lib/rules/ce0-store");
64
+ const prompt_submit_adapter_1 = require("../lib/rules/prompt-submit-adapter");
65
+ const consultation_capture_adapter_1 = require("../lib/rules/consultation-capture-adapter");
66
+ const stop_adapter_1 = require("../lib/rules/stop-adapter");
67
+ const runtime_scope_1 = require("../lib/rules/runtime-scope");
68
+ const consult_evidence_binding_1 = require("../lib/rules/consult-evidence-binding");
69
+ const ce0_telemetry_1 = require("../lib/rules/ce0-telemetry");
70
+ const ce0_emit_1 = require("../lib/rules/ce0-emit");
71
+ /** The single response a CE0 hook is ever allowed to emit: the empty no-decision body. */
72
+ const CE0_HOOK_PASS_THROUGH = "{}";
73
+ function readStdinReal() {
74
+ return new Promise((resolve, reject) => {
75
+ const chunks = [];
76
+ process.stdin.on("data", (c) => chunks.push(c));
77
+ process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
78
+ process.stdin.on("error", reject);
79
+ });
80
+ }
81
+ /** Best-effort pull of the hook payload's session_id; null when stdin is absent or malformed. The
82
+ * STOP operationIdentity and the telemetry envelope both join on it, so it is parsed once in the shell. */
83
+ function parseSessionId(raw) {
84
+ try {
85
+ const p = JSON.parse(raw);
86
+ return typeof p.session_id === "string" ? p.session_id : null;
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ /**
93
+ * The shared IO shell for every CE0 hook subcommand. Reads stdin best-effort, resolves the
94
+ * workspace (the activation gate), opens the store, runs `body`, closes the store, and ALWAYS
95
+ * writes the empty pass-through body + returns exit 0. Every fault is swallowed so the hook
96
+ * never blocks a turn. `body` runs one adapter (discarding its injection-free response) and returns
97
+ * the coordinate it acted on; when a coordinate is present the shell appends the §6.4 evidence_hook_health
98
+ * watchdog event via the fail-soft live-telemetry sink (durable store write already committed; telemetry
99
+ * is strictly best-effort on top, P0.2).
100
+ */
101
+ async function runCe0Hook(io, hook, body) {
102
+ const monotonicNow = io.monotonicNowMs ?? (() => perf_hooks_1.performance.now());
103
+ const enteredAtMs = monotonicNow();
104
+ const writeOut = io.writeOut ?? ((s) => process.stdout.write(s));
105
+ let raw = "";
106
+ try {
107
+ raw = await (io.readStdin ?? readStdinReal)();
108
+ }
109
+ catch {
110
+ raw = "";
111
+ }
112
+ const sessionId = parseSessionId(raw);
113
+ try {
114
+ const workspaceId = (io.resolveWorkspaceId ?? workspace_1.resolveWorkspaceIdWithEnv)();
115
+ if (workspaceId) {
116
+ const dbPath = io.storePath ?? (0, evidence_1.defaultCe0StorePath)();
117
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
118
+ const store = (io.openStore ?? ce0_store_1.openCe0Store)(dbPath);
119
+ let result;
120
+ try {
121
+ result = body(store, workspaceId, raw, sessionId);
122
+ }
123
+ finally {
124
+ (0, ce0_store_1.closeCe0Store)(store);
125
+ }
126
+ const emit = io.emit ?? ce0_emit_1.emitCe0Event;
127
+ const now = io.now ?? Date.now;
128
+ const coords = { workspaceId, sessionId, nowMs: now() };
129
+ // The primary event (the fact the hook produced) ships first; the health watchdog's monotonic
130
+ // durationMs is sampled after, so it covers the primary append (§6.4 P0.2).
131
+ if (result.primaryEvent) {
132
+ emit(result.primaryEvent, coords);
133
+ }
134
+ if (result.operationIdentity) {
135
+ const durationMs = Math.round(monotonicNow() - enteredAtMs);
136
+ const event = (0, ce0_telemetry_1.buildEvidenceHookHealthEvent)({
137
+ hook,
138
+ operationIdentity: result.operationIdentity,
139
+ durationMs,
140
+ failed: false,
141
+ reason: null,
142
+ });
143
+ emit(event, coords);
144
+ }
145
+ }
146
+ }
147
+ catch {
148
+ // Fail-soft: a CE0 record (or telemetry) failure must never escalate into a blocking hook.
149
+ }
150
+ writeOut(CE0_HOOK_PASS_THROUGH);
151
+ return 0;
152
+ }
153
+ /**
154
+ * `mla _internal evidence-turn-open` -- the UserPromptSubmit hook entry. Classifies the
155
+ * turn's memory requirement, persists its assessment, and (only for a REQUIRED turn) creates
156
+ * the turn's TurnRuleObligation. Always emits `{}` exit 0.
157
+ */
158
+ async function runInternalEvidenceTurnOpen(_argv, deps = {}) {
159
+ return runCe0Hook(deps, "USER_PROMPT_SUBMIT", (store, workspaceId, raw) => {
160
+ // Resolve the obligation's rule identity from the active runtime scope (P0.51): the LIVE attested
161
+ // consult-evidence version when an operator has armed this checkout, the frozen compile-time identity
162
+ // otherwise. The obligation is stamped with whichever this returns, so arming binds with no behavior
163
+ // change (the rule stays RECORD_ONLY -- arming only gives the obligation a durable version to claim).
164
+ const runtimeScopeId = (deps.resolveRuntimeScopeId ?? runtime_scope_1.resolveActiveRuntimeScopeId)();
165
+ const ruleBinding = (0, consult_evidence_binding_1.resolveConsultEvidenceRuleBinding)(store, runtimeScopeId);
166
+ const { outcome } = (0, prompt_submit_adapter_1.observeUserPromptSubmit)(raw, {
167
+ store,
168
+ workspaceId,
169
+ ruleBinding,
170
+ now: deps.now,
171
+ newId: deps.newId,
172
+ });
173
+ // §6.4: the UserPromptSubmit health event's operation identity is the assessmentId of the turn it
174
+ // opened. An INFRA outcome opened no turn and has no assessmentId, so it emits no health event.
175
+ return { operationIdentity: outcome.kind === "ASSESSED" ? outcome.assessmentId : undefined };
176
+ });
177
+ }
178
+ /**
179
+ * `mla _internal evidence-capture` -- the PostToolUse hook entry. When the agent calls a
180
+ * governed-memory pull, records the FACT of that consultation as a ConsultationAttempt under
181
+ * the live turn's identity; a non-governed tool is a no-op. Always emits `{}` exit 0.
182
+ */
183
+ async function runInternalEvidenceCapture(_argv, deps = {}) {
184
+ return runCe0Hook(deps, "CONSULTATION_CAPTURE", (store, workspaceId, raw) => {
185
+ const { outcome } = (0, consultation_capture_adapter_1.captureMemoryConsultation)(raw, {
186
+ store,
187
+ workspaceId,
188
+ now: deps.now,
189
+ newId: deps.newId,
190
+ });
191
+ if (outcome.kind !== "CAPTURED") {
192
+ // A non-governed tool (NOT_APPLICABLE) or an INFRA outcome records no consultation: no primary event
193
+ // and no operation identity, so the shell emits nothing for this invocation (§6.4 P0.2).
194
+ return {};
195
+ }
196
+ // §6.4: a governed-memory pull emits one evidence_consultation_completed primary event keyed by the
197
+ // consultationId, plus the health watchdog under the same consultationId. A CE0 capture carries neither
198
+ // a rule version (the obligation is finalized offline) nor a timed retrieval latency, so both stay
199
+ // absent (R4 P1.2 / P0.2). delivered_to_answering_context is true: the agent pulled the evidence into
200
+ // its own answering context, mirroring the persisted ConsultationAttempt row.
201
+ const primaryEvent = (0, ce0_telemetry_1.buildEvidenceConsultationCompletedEvent)({
202
+ consultationId: outcome.consultationId,
203
+ localTurnSequence: outcome.localTurnSequence,
204
+ source: outcome.source,
205
+ execution: outcome.execution,
206
+ result: outcome.result,
207
+ deliveredToAnsweringContext: true,
208
+ });
209
+ return { operationIdentity: outcome.consultationId, primaryEvent };
210
+ });
211
+ }
212
+ /**
213
+ * `mla _internal evidence-stop` -- the Stop hook entry. The first Stop of a turn runs §2.3's two
214
+ * stages: Stage A stamps stopObservedAt and freezes the obligation's eligibility boundary at the
215
+ * high-water consultation token (I/O-free), then Stage B best-effort snapshots the response from the
216
+ * payload's transcript_path. A later Stop is an idempotent no-op on both. The boundary is a stored
217
+ * token and the clock defaults to Date.now inside the adapter, so this entry needs no minter. Always
218
+ * emits `{}` exit 0.
219
+ */
220
+ async function runInternalEvidenceStop(_argv, deps = {}) {
221
+ return runCe0Hook(deps, "STOP", (store, workspaceId, raw, sessionId) => {
222
+ // Re-resolve the same binding the turn-open adapter stamped the obligation with (P0.51): claimFirstStop
223
+ // joins the obligation on (ws, session, seq, ruleVersionId), so the Stop MUST claim with the active
224
+ // runtime scope's bound version (the LIVE attested one when armed, the frozen compile-time identity
225
+ // when unarmed), or it would orphan the armed obligation and never freeze its boundary. This is the
226
+ // symmetric Stop half of the binding; the rule stays RECORD_ONLY (the version only gives Stop a durable
227
+ // identity to claim against).
228
+ const runtimeScopeId = (deps.resolveRuntimeScopeId ?? runtime_scope_1.resolveActiveRuntimeScopeId)();
229
+ const ruleBinding = (0, consult_evidence_binding_1.resolveConsultEvidenceRuleBinding)(store, runtimeScopeId);
230
+ const { outcome } = (0, stop_adapter_1.observeStop)(raw, { store, workspaceId, ruleVersionId: ruleBinding.ruleVersionId });
231
+ // §6.4: the Stop health event's operation identity is the rendered LocalTurnIdentity of the turn it
232
+ // claimed (`${workspaceId}:${sessionId}:${localTurnSequence}`). CLAIMED/ALREADY_CLAIMED carry the
233
+ // localTurnSequence but not the workspace or session, so the shell composes the key from the body's
234
+ // workspaceId and the parsed sessionId. A NOT_APPLICABLE / INFRA Stop claimed no obligation and a
235
+ // sessionId-less payload cannot form the key, so either emits no health event.
236
+ const claimed = outcome.kind === "CLAIMED" || outcome.kind === "ALREADY_CLAIMED";
237
+ const operationIdentity = claimed && sessionId !== null ? `${workspaceId}:${sessionId}:${outcome.localTurnSequence}` : undefined;
238
+ return { operationIdentity };
239
+ });
240
+ }
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ // `mla _internal evidence-inject` -- emit one mla_evidence_inject analytics event.
3
+ //
4
+ // Fired DETACHED from the user-prompt-submit hook (spawn_evidence_inject) on a turn
5
+ // where mla actually injected evidence (INJECTED=true). It builds the typed,
6
+ // PII-bounded inject payload (buildInjectPayload), records it to the local
7
+ // events.jsonl immediately as the start of a pending window (the Stop-hook
8
+ // correlator closes the window later and appends mla_evidence_outcome,
9
+ // INV-CORRELATOR-1), and best-effort forwards to control when telemetry is on.
10
+ //
11
+ // It rides OFF the session's hot path: every failure is swallowed and the command
12
+ // exits 0 (except a strict argv parse error -> 2), so analytics can never disturb
13
+ // the session it spawned from.
14
+ //
15
+ // The spawn is a fresh process with NO run context, so trace_id arrives via
16
+ // --trace-id (the SAME trace as the enrichment that produced the inject, so the
17
+ // inject joins to it in Langfuse) and a fresh run_id is minted for this invocation
18
+ // (INV-RUN-1: one run_id per invocation, never derived from trace_id).
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.parseArgs = parseArgs;
21
+ exports.runInternalEvidenceInject = runInternalEvidenceInject;
22
+ const config_1 = require("../lib/config");
23
+ const observability_1 = require("../lib/observability");
24
+ const evidence_1 = require("../lib/analytics/evidence");
25
+ const coverage_gap_1 = require("../lib/analytics/coverage-gap");
26
+ const recorder_1 = require("../lib/analytics/recorder");
27
+ const store_1 = require("../lib/analytics/store");
28
+ function parseArgs(argv) {
29
+ const out = {
30
+ turnIndex: null,
31
+ offered: null,
32
+ offeredIds: [],
33
+ tokens: 0,
34
+ confidence: "low",
35
+ latencyMs: 0,
36
+ traceId: null,
37
+ workspaceId: null,
38
+ sessionId: null,
39
+ injectId: null,
40
+ retrievalError: false,
41
+ permissionFiltered: false,
42
+ stale: false,
43
+ topicCategory: null,
44
+ };
45
+ for (let i = 0; i < argv.length; i++) {
46
+ const a = argv[i];
47
+ const value = () => {
48
+ const v = argv[++i];
49
+ if (v === undefined)
50
+ throw new Error(`Flag ${a} requires a value.`);
51
+ return v;
52
+ };
53
+ switch (a) {
54
+ case "--turn-index": {
55
+ const v = Number(value());
56
+ out.turnIndex = Number.isInteger(v) ? v : null;
57
+ break;
58
+ }
59
+ case "--offered": {
60
+ const v = Number(value());
61
+ out.offered = Number.isFinite(v) ? v : null;
62
+ break;
63
+ }
64
+ // Source ids are filename-derived (e.g. NT:20260529-notes.md) and never
65
+ // contain commas, so a comma-separated list is unambiguous from a bash hook.
66
+ case "--offered-ids":
67
+ out.offeredIds = value()
68
+ .split(",")
69
+ .map((s) => s.trim())
70
+ .filter(Boolean);
71
+ break;
72
+ case "--tokens": {
73
+ const v = Number(value());
74
+ out.tokens = Number.isFinite(v) ? v : 0;
75
+ break;
76
+ }
77
+ case "--latency-ms": {
78
+ const v = Number(value());
79
+ out.latencyMs = Number.isFinite(v) ? v : 0;
80
+ break;
81
+ }
82
+ case "--confidence":
83
+ out.confidence = value();
84
+ break;
85
+ case "--trace-id":
86
+ out.traceId = value();
87
+ break;
88
+ case "--workspace-id":
89
+ out.workspaceId = value();
90
+ break;
91
+ case "--session-id":
92
+ out.sessionId = value();
93
+ break;
94
+ case "--inject-id":
95
+ out.injectId = value();
96
+ break;
97
+ // Coverage-gap signal flags. Boolean flags take no value (a retrieval that
98
+ // errored / was permission-filtered / returned stale candidates); the
99
+ // topic category is a closed-enum string coerced at emit time.
100
+ case "--retrieval-error":
101
+ out.retrievalError = true;
102
+ break;
103
+ case "--permission-filtered":
104
+ out.permissionFiltered = true;
105
+ break;
106
+ case "--stale":
107
+ out.stale = true;
108
+ break;
109
+ case "--topic-category":
110
+ out.topicCategory = value();
111
+ break;
112
+ default:
113
+ throw new Error(`Unknown flag for \`mla _internal evidence-inject\`: ${a}`);
114
+ }
115
+ }
116
+ return out;
117
+ }
118
+ async function runInternalEvidenceInject(argv, deps = {}) {
119
+ let args;
120
+ try {
121
+ args = parseArgs(argv);
122
+ }
123
+ catch (e) {
124
+ console.error(e.message);
125
+ return 2;
126
+ }
127
+ const env = deps.env ?? process.env;
128
+ try {
129
+ const sessionId = args.sessionId ?? ((env.CLAUDE_CODE_SESSION_ID || "").trim() || null);
130
+ // trace_id is mandatory for a joinable event; the spawn passes it explicitly.
131
+ // Without one, record nothing (a local line with no trace cannot join the
132
+ // enrichment) and no-op.
133
+ const traceId = args.traceId ?? (0, observability_1.getRunTraceId)();
134
+ if (!traceId) {
135
+ console.log(JSON.stringify({ recorded: false, reason: "no_trace_id" }));
136
+ return 0;
137
+ }
138
+ const nowMs = deps.nowMs ?? Date.now();
139
+ const offeredCount = args.offered ?? args.offeredIds.length;
140
+ const payload = (0, evidence_1.buildInjectPayload)({
141
+ turn_index: args.turnIndex,
142
+ evidence_offered: offeredCount,
143
+ offered_source_ids: args.offeredIds,
144
+ evidence_tokens: args.tokens,
145
+ retrieval_confidence: args.confidence,
146
+ retrieval_latency_ms: args.latencyMs,
147
+ createdAtMs: nowMs,
148
+ ...(args.injectId ? { injectId: args.injectId } : {}),
149
+ });
150
+ const readCfg = deps.readCfg ??
151
+ (() => {
152
+ try {
153
+ return (0, config_1.readConfig)();
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ });
159
+ const cfg = readCfg();
160
+ const mId = (deps.machineId ?? store_1.machineId)();
161
+ const runId = (deps.mintRunId ?? observability_1.mintRunId)();
162
+ const ctx = {
163
+ workspaceId: args.workspaceId,
164
+ sessionId,
165
+ // The hook cannot cheaply resolve the actor cuid; prefer the configured
166
+ // actor when present, else the hashed machine id (a workspace-scoped
167
+ // anonymous id, never end-user PII).
168
+ distinctId: cfg?.actorUserId ?? mId,
169
+ runId,
170
+ traceId,
171
+ source: "hook",
172
+ now: new Date(nowMs).toISOString(),
173
+ };
174
+ const record = deps.record ?? recorder_1.recordAnalyticsEvent;
175
+ // event_id == inject_id: one inject produces exactly one inject event, so the
176
+ // business key IS the idempotency key. A re-ship dedupes on
177
+ // (workspace_id, inject_id) in control (§10.2, INV-REMOTE-DEDUPE-1).
178
+ record(ctx, {
179
+ eventType: "mla_evidence_inject",
180
+ eventId: payload.inject_id,
181
+ payload: payload,
182
+ }, env);
183
+ // Typed coverage gap (spec §7.5, INV-COVERAGE-GAP-1): if this inject failed
184
+ // to help (errored / permission-filtered / empty / stale / low-confidence),
185
+ // emit a paired mla_coverage_gap keyed to the same inject_id so `mla stats`
186
+ // can sort the backlog by cause. A confident, non-empty retrieval classifies
187
+ // to null and emits nothing. Reuses the inject's already-coerced confidence
188
+ // and zero_results so the two events never disagree.
189
+ const coverageGapType = (0, coverage_gap_1.classifyCoverageGap)({
190
+ retrievalError: args.retrievalError,
191
+ permissionFiltered: args.permissionFiltered,
192
+ zeroResults: payload.zero_results,
193
+ staleOrConflicting: args.stale,
194
+ retrievalConfidence: payload.retrieval_confidence,
195
+ });
196
+ if (coverageGapType) {
197
+ record(ctx, {
198
+ eventType: "mla_coverage_gap",
199
+ eventId: (0, coverage_gap_1.coverageGapEventId)(payload.inject_id),
200
+ payload: (0, coverage_gap_1.buildCoverageGapPayload)({
201
+ injectId: payload.inject_id,
202
+ coverageGapType,
203
+ queryTopicCategory: (0, coverage_gap_1.coerceTopicCategory)(args.topicCategory),
204
+ retrievalConfidence: payload.retrieval_confidence,
205
+ zeroResults: payload.zero_results,
206
+ }),
207
+ }, env);
208
+ }
209
+ // Best-effort, bounded, telemetry-gated forward (the consent gate is inside
210
+ // the forwarder). Skipped entirely when the run has no control config.
211
+ if (cfg) {
212
+ const flush = deps.flush ?? recorder_1.flushAnalyticsEvents;
213
+ await flush(cfg, env);
214
+ }
215
+ console.log(JSON.stringify({
216
+ recorded: true,
217
+ inject_id: payload.inject_id,
218
+ turn_index: payload.turn_index,
219
+ offered: payload.evidence_offered,
220
+ outcome: "pending",
221
+ window_deadline: payload.window_deadline,
222
+ coverage_gap_type: coverageGapType,
223
+ }));
224
+ return 0;
225
+ }
226
+ catch {
227
+ // Fail-soft: an inject failing to record never disturbs the session.
228
+ console.log(JSON.stringify({ recorded: false, reason: "error" }));
229
+ return 0;
230
+ }
231
+ }