@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,427 @@
1
+ "use strict";
2
+ // `mla enrich`: the two CLI bookends for agent-orchestrated onboarding enrichment.
3
+ //
4
+ // enrich plan -> derive the workspace + git root, mint a runId, scan the repo into
5
+ // an immutable run record (ranked doc targets + a bounded git-history
6
+ // allowlist), persist it locally, and print the plan the agent reads.
7
+ // enrich ingest -> the agent dispatched read-only scouts against that plan and reports
8
+ // candidates; this loads the authoritative run record, re-verifies it,
9
+ // validates + verifies every candidate, and persists the survivors to
10
+ // the governed KB born PENDING.
11
+ //
12
+ // The agent never supplies plan data: it gets a runId from `plan` and returns only the
13
+ // scout results. All trust enforcement (realpath containment, exist-at-HEAD, line range,
14
+ // commit allowlist, plan-digest match) lives in lib/enrichment, exercised here with the
15
+ // real filesystem, git, and the kb-add route. See
16
+ // notes/20260626-mla-agent-onboarding-enrichment-plan.md (§5, §5b, §6, §6b).
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.resolveBudgetMs = resolveBudgetMs;
19
+ exports.parsePlanArgs = parsePlanArgs;
20
+ exports.parseIngestArgs = parseIngestArgs;
21
+ exports.extractResults = extractResults;
22
+ exports.parseBriefArgs = parseBriefArgs;
23
+ exports.runEnrich = runEnrich;
24
+ const node_child_process_1 = require("node:child_process");
25
+ const node_fs_1 = require("node:fs");
26
+ const node_crypto_1 = require("node:crypto");
27
+ const config_1 = require("../lib/config");
28
+ const workspace_1 = require("../lib/workspace");
29
+ const http_1 = require("../lib/http");
30
+ const plan_1 = require("../lib/enrichment/plan");
31
+ const ingest_1 = require("../lib/enrichment/ingest");
32
+ const scout_brief_1 = require("../lib/enrichment/scout-brief");
33
+ const protocol_1 = require("../lib/enrichment/protocol");
34
+ const USAGE = `mla enrich: agent-orchestrated onboarding enrichment (two bookends).
35
+
36
+ mla enrich plan [--json] [--budget-ms <n>] [--workspace <id>]
37
+ Scan this repository into an immutable run record and print the plan the
38
+ agent reads to dispatch its read-only scouts. --json prints the machine
39
+ plan (the agent contract); without it, a human summary. The runId in the
40
+ output is what you pass back to \`enrich ingest\`.
41
+
42
+ mla enrich brief --run-id <id> --role <documentation|history> [--workspace <id>]
43
+ Print the exact subagent brief for one scout role, rendered from the run
44
+ record named by --run-id. Read-only; used by \`/mla onboard\` to dispatch each
45
+ scout with the run-specific prompt \`enrich ingest\` will validate against.
46
+
47
+ mla enrich ingest --run-id <id> [--results-file <path>] [--json] [--workspace <id>]
48
+ Validate + persist the scouts' candidates against the run record named by
49
+ --run-id. Reads the scout results as JSON from --results-file, or from
50
+ stdin when no file is given (an array of scout results, or an object with a
51
+ \`results\` array). Candidates land in the governed KB born PENDING.
52
+ Exit: 0 clean, 1 a scout needs attention (persistence failed / malformed),
53
+ 2 the request was rejected (unknown run, mismatch, corrupt record).`;
54
+ // Mirror kb_add's ingest timeout heuristic (it is module-private there). Generous,
55
+ // scales with document count: the kb-add route runs the full atomic-claim pipeline.
56
+ function ingestTimeoutMs(docCount) {
57
+ return Math.max(120_000, docCount * 20_000);
58
+ }
59
+ // The git toplevel is the enrichment repository root: `git ls-files` / `git log` must
60
+ // run from it so the paths the scouts cite are repo-root-relative and the realpath
61
+ // containment check has the right base. Throws a clean error outside a git repo.
62
+ function resolveRepositoryRoot(startDir) {
63
+ try {
64
+ return (0, node_child_process_1.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
65
+ cwd: startDir,
66
+ encoding: "utf8",
67
+ }).trim();
68
+ }
69
+ catch {
70
+ throw new Error(`mla enrich requires a git repository. No git toplevel found at ${startDir}. ` +
71
+ `Run it from inside an activated, git-tracked repository.`);
72
+ }
73
+ }
74
+ // The §8 budget knob. `--budget-ms` wins; else MLA_ENRICH_BUDGET_MS; else the protocol
75
+ // default (createPlan applies it). An invalid env value is IGNORED with a warning rather
76
+ // than failing the command (an explicit flag with a bad value still hard-errors upstream).
77
+ //
78
+ // Contract honesty (§8, Phase 0B): this knob sets the run's `deadlineAt`; it is a SOFT
79
+ // budget. The CLI does not supervise the scouts (the agent does), so the deadline steers
80
+ // the skill's dispatch/wait and the scouts self-limit. Ingest already records a scout that
81
+ // reports `timed_out` as partial-and-rerunnable; no late arrival is rejected on time. Do
82
+ // not call this a hard ceiling until a live hang-test proves the runtime can abandon a
83
+ // straggler at the deadline (background dispatch + scheduled wake + task-stop).
84
+ function resolveBudgetMs(flagBudget, rawEnv) {
85
+ if (flagBudget !== undefined)
86
+ return { budgetMs: flagBudget };
87
+ if (rawEnv === undefined || rawEnv.trim() === "")
88
+ return {};
89
+ const v = Number(rawEnv);
90
+ if (!Number.isFinite(v) || v <= 0) {
91
+ return { warning: `ignoring invalid MLA_ENRICH_BUDGET_MS=${rawEnv} (expected a positive number of milliseconds)` };
92
+ }
93
+ return { budgetMs: v };
94
+ }
95
+ // Exported for unit tests: the pure flag/payload helpers are the only new logic in this
96
+ // shell worth isolating (createPlan/ingestRun are covered by their own specs).
97
+ function parsePlanArgs(argv) {
98
+ const flags = { json: false };
99
+ for (let i = 0; i < argv.length; i++) {
100
+ const a = argv[i];
101
+ if (a === "--json")
102
+ flags.json = true;
103
+ else if (a === "--budget-ms") {
104
+ const v = Number(argv[++i]);
105
+ if (!Number.isFinite(v) || v <= 0)
106
+ throw new Error("--budget-ms must be a positive number of milliseconds");
107
+ flags.budgetMs = v;
108
+ }
109
+ else if (a === "--workspace") {
110
+ flags.workspace = argv[++i];
111
+ if (!flags.workspace)
112
+ throw new Error("--workspace requires a workspace id");
113
+ }
114
+ else
115
+ throw new Error(`Unknown flag for \`mla enrich plan\`: ${a}`);
116
+ }
117
+ return flags;
118
+ }
119
+ async function runEnrichPlan(argv) {
120
+ let flags;
121
+ try {
122
+ flags = parsePlanArgs(argv);
123
+ }
124
+ catch (e) {
125
+ console.error(e.message);
126
+ return 2;
127
+ }
128
+ let cfg;
129
+ try {
130
+ cfg = (0, config_1.readKbConfig)(flags.workspace);
131
+ }
132
+ catch (e) {
133
+ console.error(e.message);
134
+ return 2;
135
+ }
136
+ let repositoryRoot;
137
+ try {
138
+ const ctx = (0, workspace_1.resolveWorkspaceContext)();
139
+ repositoryRoot = resolveRepositoryRoot(ctx.markerDir);
140
+ }
141
+ catch (e) {
142
+ console.error(e.message);
143
+ return 2;
144
+ }
145
+ const budget = resolveBudgetMs(flags.budgetMs, process.env.MLA_ENRICH_BUDGET_MS);
146
+ if (budget.warning)
147
+ console.error(budget.warning);
148
+ const runId = `run-${(0, node_crypto_1.randomUUID)()}`;
149
+ const { run, recordPath, pruned, historyTruncated } = (0, plan_1.createPlan)({
150
+ runId,
151
+ workspaceId: cfg.workspaceId,
152
+ repositoryRoot,
153
+ home: config_1.HOME,
154
+ now: new Date().toISOString(),
155
+ budgetMs: budget.budgetMs,
156
+ });
157
+ if (flags.json) {
158
+ // The agent contract: the run record plus the truncation signal. The agent reads
159
+ // documentationTargets + historyEvidence to dispatch its scouts and passes runId
160
+ // back to `enrich ingest`. It is the SAME record persisted on disk (no divergence).
161
+ console.log(JSON.stringify({ ...run, historyTruncated }, null, 2));
162
+ return 0;
163
+ }
164
+ const lines = [
165
+ `Onboarding enrichment plan ready.`,
166
+ ``,
167
+ ` runId: ${run.runId}`,
168
+ ` workspace: ${run.workspaceId}`,
169
+ ` repository: ${run.repositoryRoot}`,
170
+ ` budget: ${run.limits.budgetMs} ms (deadline ${run.deadlineAt})`,
171
+ ` doc targets: ${run.documentationTargets.length}`,
172
+ ` history commits: ${run.historyEvidence.length}${historyTruncated ? " (truncated)" : ""}`,
173
+ ` max candidates: ${run.limits.maxCandidatesTotal}`,
174
+ ` record: ${recordPath}${pruned ? ` (pruned ${pruned} stale)` : ""}`,
175
+ ``,
176
+ `Run \`mla enrich plan --json\` for the machine plan, then dispatch scouts and`,
177
+ `report with \`mla enrich ingest --run-id ${run.runId}\`.`,
178
+ ];
179
+ console.log(lines.join("\n"));
180
+ return 0;
181
+ }
182
+ function parseIngestArgs(argv) {
183
+ const flags = { json: false };
184
+ for (let i = 0; i < argv.length; i++) {
185
+ const a = argv[i];
186
+ if (a === "--json")
187
+ flags.json = true;
188
+ else if (a === "--run-id") {
189
+ flags.runId = argv[++i];
190
+ if (!flags.runId)
191
+ throw new Error("--run-id requires a value");
192
+ }
193
+ else if (a === "--results-file") {
194
+ flags.resultsFile = argv[++i];
195
+ if (!flags.resultsFile)
196
+ throw new Error("--results-file requires a path");
197
+ }
198
+ else if (a === "--workspace") {
199
+ flags.workspace = argv[++i];
200
+ if (!flags.workspace)
201
+ throw new Error("--workspace requires a workspace id");
202
+ }
203
+ else
204
+ throw new Error(`Unknown flag for \`mla enrich ingest\`: ${a}`);
205
+ }
206
+ if (!flags.runId)
207
+ throw new Error("--run-id is required (the id printed by `mla enrich plan`)");
208
+ return flags;
209
+ }
210
+ function readStdin() {
211
+ return new Promise((resolve, reject) => {
212
+ let data = "";
213
+ process.stdin.setEncoding("utf8");
214
+ process.stdin.on("data", (chunk) => (data += chunk));
215
+ process.stdin.on("end", () => resolve(data));
216
+ process.stdin.on("error", reject);
217
+ });
218
+ }
219
+ // Normalize the agent's payload into the results array. Accept three shapes for
220
+ // ergonomics: a bare array, `{results:[...]}`, or the full `{runId, results}` request.
221
+ // When a runId is present in the body it MUST match --run-id (defense against a stale
222
+ // paste pointing the wrong run's results at this record).
223
+ function extractResults(raw, runId) {
224
+ let parsed;
225
+ try {
226
+ parsed = JSON.parse(raw);
227
+ }
228
+ catch (e) {
229
+ throw new Error(`scout results are not valid JSON: ${e.message}`);
230
+ }
231
+ if (Array.isArray(parsed))
232
+ return parsed;
233
+ if (parsed && typeof parsed === "object") {
234
+ const obj = parsed;
235
+ if (obj.runId !== undefined && obj.runId !== runId) {
236
+ throw new Error(`results runId (${String(obj.runId)}) does not match --run-id (${runId})`);
237
+ }
238
+ if (Array.isArray(obj.results))
239
+ return obj.results;
240
+ }
241
+ throw new Error("scout results must be a JSON array, or an object with a `results` array");
242
+ }
243
+ function renderIngestSummary(outcomes, status) {
244
+ const lines = [`Onboarding ingest complete (state: ${status ?? "unknown"}).`, ``];
245
+ for (const o of outcomes) {
246
+ lines.push(` ${o.scout}: ${o.accepted} accepted, ${o.rejected} rejected, ${o.persisted} persisted (received ${o.received})`);
247
+ for (const e of o.errors) {
248
+ const where = e.index >= 0 ? `candidate ${e.index}` : "scout";
249
+ lines.push(` - ${where}: ${e.code} (${e.message})`);
250
+ }
251
+ }
252
+ return lines.join("\n");
253
+ }
254
+ async function runEnrichIngest(argv) {
255
+ let flags;
256
+ try {
257
+ flags = parseIngestArgs(argv);
258
+ }
259
+ catch (e) {
260
+ console.error(e.message);
261
+ return 2;
262
+ }
263
+ let cfg;
264
+ try {
265
+ cfg = (0, config_1.readKbConfig)(flags.workspace);
266
+ }
267
+ catch (e) {
268
+ console.error(e.message);
269
+ return 2;
270
+ }
271
+ let repositoryRoot;
272
+ try {
273
+ const ctx = (0, workspace_1.resolveWorkspaceContext)();
274
+ repositoryRoot = resolveRepositoryRoot(ctx.markerDir);
275
+ }
276
+ catch (e) {
277
+ console.error(e.message);
278
+ return 2;
279
+ }
280
+ // Source the scout results: an explicit file, or piped stdin. Refuse to hang on a TTY.
281
+ let rawResults;
282
+ try {
283
+ if (flags.resultsFile) {
284
+ rawResults = (0, node_fs_1.readFileSync)(flags.resultsFile, "utf8");
285
+ }
286
+ else if (!process.stdin.isTTY) {
287
+ rawResults = await readStdin();
288
+ }
289
+ else {
290
+ console.error("provide --results-file <path> or pipe the scout results JSON to stdin");
291
+ return 2;
292
+ }
293
+ }
294
+ catch (e) {
295
+ console.error(`could not read scout results: ${e.message}`);
296
+ return 2;
297
+ }
298
+ let results;
299
+ try {
300
+ results = extractResults(rawResults, flags.runId);
301
+ }
302
+ catch (e) {
303
+ console.error(e.message);
304
+ return 2;
305
+ }
306
+ // The real kb-add persister: born-PENDING governed notes. provenance is advisory
307
+ // (the server derives the recorded value from the envelope); the count reflects
308
+ // receipts that landed (ingested or already-present), not failures.
309
+ const persist = async (docs) => {
310
+ const body = {
311
+ workspaceId: cfg.workspaceId,
312
+ actor: cfg.actorUserId,
313
+ documents: docs.map((d) => ({ relPath: d.relPath, content: d.content })),
314
+ provenance: "agent_distilled",
315
+ profile: "markdown_atomic_v1",
316
+ mode: "file",
317
+ };
318
+ const res = await (0, http_1.intelPost)(cfg, "/internal/v1/kb/add", body, ingestTimeoutMs(docs.length));
319
+ const receipts = res.receipts ?? [];
320
+ return { persisted: receipts.filter((r) => r.outcome !== "failed").length };
321
+ };
322
+ const res = await (0, ingest_1.ingestRun)({
323
+ env: { home: config_1.HOME, workspaceId: cfg.workspaceId, repositoryRoot },
324
+ request: { protocolVersion: protocol_1.PROTOCOL_VERSION, runId: flags.runId, results },
325
+ persist,
326
+ now: new Date().toISOString(),
327
+ });
328
+ if (!res.ok) {
329
+ if (flags.json)
330
+ console.log(JSON.stringify(res, null, 2));
331
+ else
332
+ console.error(`enrich ingest rejected: ${res.rejectionReason}`);
333
+ return 2;
334
+ }
335
+ if (flags.json) {
336
+ console.log(JSON.stringify(res, null, 2));
337
+ }
338
+ else {
339
+ console.log(renderIngestSummary(res.outcomes, res.state?.status));
340
+ }
341
+ // 1 when a scout needs attention (infra failure or a malformed envelope worth a retry);
342
+ // 0 otherwise. A scout that merely "timed_out" is rerunnable state, not an error here.
343
+ const needsAttention = (res.state ? Object.values(res.state.scouts) : []).some((s) => s.status === "persistence_failed" || s.status === "malformed");
344
+ return needsAttention ? 1 : 0;
345
+ }
346
+ function parseBriefArgs(argv) {
347
+ const flags = {};
348
+ for (let i = 0; i < argv.length; i++) {
349
+ const a = argv[i];
350
+ if (a === "--run-id") {
351
+ flags.runId = argv[++i];
352
+ if (!flags.runId)
353
+ throw new Error("--run-id requires a value");
354
+ }
355
+ else if (a === "--role") {
356
+ const v = argv[++i];
357
+ if (!v)
358
+ throw new Error("--role requires a value");
359
+ if (!protocol_1.SCOUT_NAMES.includes(v)) {
360
+ throw new Error(`--role must be one of: ${protocol_1.SCOUT_NAMES.join(", ")}`);
361
+ }
362
+ flags.role = v;
363
+ }
364
+ else if (a === "--workspace") {
365
+ flags.workspace = argv[++i];
366
+ if (!flags.workspace)
367
+ throw new Error("--workspace requires a workspace id");
368
+ }
369
+ else
370
+ throw new Error(`Unknown flag for \`mla enrich brief\`: ${a}`);
371
+ }
372
+ if (!flags.runId)
373
+ throw new Error("--run-id is required (the id printed by `mla enrich plan`)");
374
+ if (!flags.role)
375
+ throw new Error(`--role is required (one of: ${protocol_1.SCOUT_NAMES.join(", ")})`);
376
+ return flags;
377
+ }
378
+ // Print one scout's run-specific brief. Pure read of the persisted run record plus
379
+ // buildScoutPrompt; no git, no network, no mutation. `/mla onboard` calls this to get
380
+ // the exact prompt it hands each subagent, so the brief logic stays in tested TS and
381
+ // every scout input matches what `enrich ingest` re-validates against the same record.
382
+ function runEnrichBrief(argv) {
383
+ let flags;
384
+ try {
385
+ flags = parseBriefArgs(argv);
386
+ }
387
+ catch (e) {
388
+ console.error(e.message);
389
+ return 2;
390
+ }
391
+ let cfg;
392
+ try {
393
+ cfg = (0, config_1.readKbConfig)(flags.workspace);
394
+ }
395
+ catch (e) {
396
+ console.error(e.message);
397
+ return 2;
398
+ }
399
+ const run = (0, plan_1.loadRunRecord)(config_1.HOME, cfg.workspaceId, flags.runId);
400
+ if (!run) {
401
+ console.error(`no onboarding run record for ${flags.runId} in workspace ${cfg.workspaceId}. ` +
402
+ "Run `mla enrich plan` first, from the same workspace.");
403
+ return 2;
404
+ }
405
+ console.log((0, scout_brief_1.buildScoutPrompt)(run, flags.role));
406
+ return 0;
407
+ }
408
+ async function runEnrich(argv) {
409
+ const sub = argv[0];
410
+ const rest = argv.slice(1);
411
+ if (sub === undefined || sub === "help" || sub === "--help" || sub === "-h") {
412
+ console.log(USAGE);
413
+ return 0;
414
+ }
415
+ switch (sub) {
416
+ case "plan":
417
+ return runEnrichPlan(rest);
418
+ case "brief":
419
+ return runEnrichBrief(rest);
420
+ case "ingest":
421
+ return runEnrichIngest(rest);
422
+ default:
423
+ console.error(`unknown \`mla enrich\` subcommand: ${sub}\n`);
424
+ console.error(USAGE);
425
+ return 2;
426
+ }
427
+ }
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.defaultCe0StorePath = void 0;
37
+ exports.runEvidence = runEvidence;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const config_1 = require("../lib/config");
41
+ const workspace_1 = require("../lib/workspace");
42
+ const observability_1 = require("../lib/observability");
43
+ const ce0_store_1 = require("../lib/rules/ce0-store");
44
+ Object.defineProperty(exports, "defaultCe0StorePath", { enumerable: true, get: function () { return ce0_store_1.defaultCe0StorePath; } });
45
+ const ce0_evidence_1 = require("../lib/rules/ce0-evidence");
46
+ const ce0_recall_sample_1 = require("../lib/rules/ce0-recall-sample");
47
+ const ce0_telemetry_project_1 = require("../lib/rules/ce0-telemetry-project");
48
+ const recorder_1 = require("../lib/analytics/recorder");
49
+ const store_1 = require("../lib/analytics/store");
50
+ // `mla evidence` -- the one human-only CE0 labeling workflow
51
+ // (notes/20260617-evidence-consultation-forcing-function-proposal.md §2.3). CE0 is a measurement
52
+ // harness: the runtime hooks only record facts (the per-turn assessment, the consultation attempts)
53
+ // and the first Stop freezes the eligibility boundary. Satisfaction and coverage are graded OFFLINE,
54
+ // by a human, through this command. There is no model call and no external egress here; the response
55
+ // ceiling stays RECORD_ONLY.
56
+ //
57
+ // mla evidence ce0-export Write the JSONL a labeler audits: every deadline-claimed,
58
+ // not-yet-finalized obligation with the deterministic
59
+ // machine baseline recomputed over its eligible consultations.
60
+ // mla evidence ce0-import-labels <file> Read a labeled JSONL back, validate each label against the
61
+ // current export snapshot, and CAS-finalize the matched
62
+ // obligations. Prints the finalize / conflict / reject /
63
+ // agreement report.
64
+ //
65
+ // The command is a thin IO shell over the pure ce0-evidence core; the store path, workspace
66
+ // resolution, and the stdout / stderr sinks are injectable so the workflow is testable end to end.
67
+ const USAGE = "usage: mla evidence <ce0-export | ce0-import-labels <file> | ce0-emit-telemetry>";
68
+ const RECALL_SAMPLE_RATE_FLAG = "--recall-sample-rate";
69
+ /**
70
+ * Parse the optional `--recall-sample-rate <value>` (or `=<value>`) flag for `ce0-export`. Absent ->
71
+ * the pinned DEFAULT_RECALL_SAMPLE_RATE (sample every unflagged turn). Present -> a finite fraction in
72
+ * [0, 1]; anything else (non-numeric, out of range, missing value) is an operator error reported with a
73
+ * non-zero exit so a bad rate never silently narrows the recall denominator
74
+ * (notes/20260617-evidence-consultation-forcing-function-proposal.md lines 2129, 2145).
75
+ */
76
+ function parseRecallSampleRate(args) {
77
+ let raw;
78
+ for (let i = 0; i < args.length; i++) {
79
+ const tok = args[i];
80
+ if (tok === RECALL_SAMPLE_RATE_FLAG) {
81
+ raw = args[i + 1];
82
+ i++;
83
+ }
84
+ else if (tok.startsWith(`${RECALL_SAMPLE_RATE_FLAG}=`)) {
85
+ raw = tok.slice(RECALL_SAMPLE_RATE_FLAG.length + 1);
86
+ }
87
+ }
88
+ if (raw === undefined)
89
+ return { value: ce0_recall_sample_1.DEFAULT_RECALL_SAMPLE_RATE };
90
+ const value = Number(raw);
91
+ if (raw.trim() === "" || !Number.isFinite(value) || value < 0 || value > 1) {
92
+ return { error: `${RECALL_SAMPLE_RATE_FLAG} must be a number in [0, 1] (got "${raw}")` };
93
+ }
94
+ return { value };
95
+ }
96
+ async function runEvidence(argv, deps = {}) {
97
+ const out = deps.out ?? ((line) => console.log(line));
98
+ const err = deps.err ?? ((line) => console.error(line));
99
+ const sub = argv[0];
100
+ if (sub === "ce0-export") {
101
+ const rate = parseRecallSampleRate(argv.slice(1));
102
+ if ("error" in rate) {
103
+ err(`mla evidence ce0-export: ${rate.error}\n${USAGE}`);
104
+ return 2;
105
+ }
106
+ return withWorkspaceAndStore(deps, err, (workspaceId, store) => {
107
+ out((0, ce0_evidence_1.runCe0Export)(store, workspaceId, rate.value));
108
+ return 0;
109
+ });
110
+ }
111
+ if (sub === "ce0-import-labels") {
112
+ const file = argv[1];
113
+ if (!file) {
114
+ err(`mla evidence ce0-import-labels: missing labels file argument\n${USAGE}`);
115
+ return 2;
116
+ }
117
+ const readFile = deps.readFile ?? ((p) => fs.readFileSync(p, "utf8"));
118
+ const labelJsonl = readFile(file);
119
+ return withWorkspaceAndStore(deps, err, (workspaceId, store) => {
120
+ const report = (0, ce0_evidence_1.runCe0ImportLabels)(store, workspaceId, labelJsonl);
121
+ out(JSON.stringify(report));
122
+ return 0;
123
+ });
124
+ }
125
+ if (sub === "ce0-emit-telemetry") {
126
+ return runEmitTelemetry(deps, out, err);
127
+ }
128
+ err(`mla evidence: unknown subcommand ${sub ? `"${sub}"` : "(none)"}\n${USAGE}`);
129
+ return 2;
130
+ }
131
+ /** `mla evidence ce0-emit-telemetry`: project the CE0 store into the two §6.4 events it honestly
132
+ * backs (memory_requirement_assessed per assessment, evidence_obligation_finalized per FINALIZED
133
+ * obligation), record each locally, then best-effort forward to control. A repeated sweep is
134
+ * idempotent two ways: the deterministic event_id dedupes on the remote sink, and a local skip-set
135
+ * (the event_ids already in the local log for these two types) avoids re-appending the same lines.
136
+ * Each event carries the ORIGINAL turn's session, so the analytics side joins it to the turn it
137
+ * describes, not to this emit run. */
138
+ async function runEmitTelemetry(deps, out, err) {
139
+ const env = deps.env ?? process.env;
140
+ const record = deps.record ?? recorder_1.recordAnalyticsEvent;
141
+ const read = deps.readEvents ?? store_1.readEvents;
142
+ const readCfg = deps.readCfg ??
143
+ (() => {
144
+ try {
145
+ return (0, config_1.readConfig)();
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ });
151
+ const cfg = readCfg();
152
+ // One run_id per invocation (never derived from trace); reuse the bootstrap-set run/trace when
153
+ // present so all events in this sweep share them, else mint fresh (a standalone measurement run).
154
+ const runId = deps.runId ?? (0, observability_1.getRunId)() ?? (0, observability_1.mintRunId)();
155
+ const traceId = deps.traceId ?? (0, observability_1.getRunTraceId)() ?? (0, observability_1.mintTraceId)();
156
+ const distinctId = deps.distinctId ?? cfg?.actorUserId ?? (deps.machineId ?? store_1.machineId)();
157
+ const nowIso = new Date(deps.nowMs ?? Date.now()).toISOString();
158
+ let assessed = 0;
159
+ let finalized = 0;
160
+ let skipped = 0;
161
+ const code = withWorkspaceAndStore(deps, err, (workspaceId, store) => {
162
+ // Skip-set: every event_id already logged locally for the two projected types. Keeps a repeated
163
+ // sweep from re-appending lines the deterministic event_id would otherwise dedupe only remotely.
164
+ const emitted = new Set();
165
+ for (const ev of read(env)) {
166
+ if ((ev.event_type === "memory_requirement_assessed" ||
167
+ ev.event_type === "evidence_obligation_finalized") &&
168
+ typeof ev.event_id === "string") {
169
+ emitted.add(ev.event_id);
170
+ }
171
+ }
172
+ const emit = (sessionId, input) => {
173
+ if (input.eventId && emitted.has(input.eventId)) {
174
+ skipped++;
175
+ return false;
176
+ }
177
+ const ctx = { workspaceId, sessionId, distinctId, runId, traceId, now: nowIso };
178
+ record(ctx, input, env);
179
+ if (input.eventId)
180
+ emitted.add(input.eventId);
181
+ return true;
182
+ };
183
+ for (const a of (0, ce0_store_1.listTurnMemoryAssessments)(store, workspaceId)) {
184
+ if (emit(a.sessionId, (0, ce0_telemetry_project_1.projectAssessedEvent)(a)))
185
+ assessed++;
186
+ }
187
+ for (const o of (0, ce0_store_1.listDeadlineClaimedObligations)(store, workspaceId)) {
188
+ if (o.status !== "FINALIZED")
189
+ continue;
190
+ const consultations = (0, ce0_store_1.listConsultationsForTurn)(store, {
191
+ workspaceId: o.workspaceId,
192
+ sessionId: o.sessionId,
193
+ localTurnSequence: o.localTurnSequence,
194
+ });
195
+ if (emit(o.sessionId, (0, ce0_telemetry_project_1.projectFinalizedEvent)(o, consultations)))
196
+ finalized++;
197
+ }
198
+ return 0;
199
+ });
200
+ if (code !== 0)
201
+ return code;
202
+ if (cfg) {
203
+ const flush = deps.flush ?? recorder_1.flushAnalyticsEvents;
204
+ await flush(cfg, env);
205
+ }
206
+ out(JSON.stringify({ emitted: { assessed, finalized }, skipped }));
207
+ return 0;
208
+ }
209
+ /** Resolve the workspace, ensure the store directory exists, open it, run `body`, and close it. A
210
+ * missing workspace is exit 1 (an operator problem: run from a marked repo or set the env var). */
211
+ function withWorkspaceAndStore(deps, err, body) {
212
+ const resolve = deps.resolveWorkspaceId ?? workspace_1.resolveWorkspaceIdWithEnv;
213
+ const workspaceId = resolve();
214
+ if (!workspaceId) {
215
+ err("mla evidence: no workspace resolved. Run from a directory with a .meetless.json marker, " +
216
+ "or set MEETLESS_WORKSPACE_ID.");
217
+ return 1;
218
+ }
219
+ const dbPath = deps.storePath ?? (0, ce0_store_1.defaultCe0StorePath)();
220
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
221
+ const open = deps.openStore ?? ce0_store_1.openCe0Store;
222
+ const store = open(dbPath);
223
+ try {
224
+ return body(workspaceId, store);
225
+ }
226
+ finally {
227
+ (0, ce0_store_1.closeCe0Store)(store);
228
+ }
229
+ }