@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,322 @@
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.parseArgs = parseArgs;
37
+ exports.renderAdvisoryText = renderAdvisoryText;
38
+ exports.runInternalActiveReview = runInternalActiveReview;
39
+ const fs_1 = require("fs");
40
+ const path = __importStar(require("path"));
41
+ const config_1 = require("../lib/config");
42
+ const active_memory_1 = require("../lib/active-memory");
43
+ const http_1 = require("../lib/http");
44
+ const active_review_runner_1 = require("../lib/active-review-runner");
45
+ const tagged_reference_1 = require("../lib/tagged-reference");
46
+ // `mla _internal active-review --session <sid>` (Phase 1, Active Review).
47
+ //
48
+ // Fired by the UserPromptSubmit hook's Layer 3 AFTER the turn counter advances.
49
+ // It reviews the PRIOR turn's produced docs (the Active Memory store the
50
+ // PostToolUse hook appends to ~/.meetless/logs/kb-knowledge.jsonl) for conflict
51
+ // with the workspace's approved knowledge and prints an advisory the hook
52
+ // injects as additionalContext. Two contracts mirror the runner:
53
+ // - dry-run only (no persistence; the detect call is always dryRun:true).
54
+ // - advise-never-block (P6): this command NEVER throws past the dispatcher and
55
+ // NEVER exits non-zero on a review miss; a failure prints an empty advisory
56
+ // and exits 0 so the hook simply injects nothing.
57
+ //
58
+ // Hermetic test seam: when MEETLESS_ACTIVE_REVIEW_STUB_DETECT is set (non-empty)
59
+ // the intel client is a stub that returns that parsed JSON instead of making a
60
+ // real HTTP call, so the hook test stays offline. Otherwise the real client
61
+ // POSTs to intel /internal/v1/active-review/detect.
62
+ //
63
+ // A3 (Phase 2): on top of the Phase 1 conflict advisories, this command also
64
+ // renders supersession/contradiction advisories for the docs the user NAMED this
65
+ // session (the tagged_reference captures the UserPromptSubmit hook appends). The
66
+ // pure engine (tagged-reference.ts) joins those paths against APPROVED relation
67
+ // facts; the facts come from control's relationship-candidates list endpoint
68
+ // (statusId=ACCEPTED, posture=LIVE), the same read kb pending uses. Its hermetic
69
+ // seam is MEETLESS_TAGGED_FACTS_STUB (parsed as a KbRelationFact[] JSON). The
70
+ // supersession fetch is best-effort and advise-never-block (P6): any failure
71
+ // degrades to an empty fact list, so the supersession advisory simply does not
72
+ // render; the join engages the moment approved facts exist for a named doc.
73
+ // The store the PostToolUse hook appends to. Path + filename are byte-identical
74
+ // to common.sh LOG_DIR/kb-knowledge.jsonl so both sides resolve the same file
75
+ // under MEETLESS_HOME.
76
+ function activeMemoryStorePath() {
77
+ return path.join(config_1.HOME, "logs", "kb-knowledge.jsonl");
78
+ }
79
+ // TTL + cap for the read-time reduction. 48h matches the Active Memory store's
80
+ // own dedup/TTL intent; 100 is a generous per-review cap (a single turn produces
81
+ // only a handful of docs).
82
+ const TTL_HOURS = 48;
83
+ const MAX_RECORDS = 100;
84
+ // V1 confidence floor for the conflict-advisory policy. A detection below this
85
+ // is a weak signal and produces no advisory (the policy logs but does not flag).
86
+ const MIN_CONFIDENCE = 0.6;
87
+ // Stub client: returns the operator-supplied detect response verbatim. Used by
88
+ // the hook test so it never touches the network. The stub JSON is the same shape
89
+ // the real endpoint returns ({ detections, persisted }).
90
+ function stubIntelClient(stubJson) {
91
+ return {
92
+ detect: async () => {
93
+ const parsed = JSON.parse(stubJson);
94
+ return { detections: parsed.detections ?? [], persisted: parsed.persisted ?? false };
95
+ },
96
+ };
97
+ }
98
+ // Read the produced doc's CURRENT on-disk content so the in-process detector has
99
+ // real text to score against the owner's approved corpus. Mirrors the Zone 2
100
+ // auto-index eligibility (auto-index.ts selectIndexTargets): only a produced_doc
101
+ // that carries a repoRoot is resolvable. A tagged_reference is a doc the user
102
+ // NAMED (handled by the A3 supersession join, not detect), and a record without a
103
+ // repoRoot predates Phase A and cannot be located on disk. The absolute repoRoot
104
+ // is LOCAL-only and never leaves the machine: we join it with the RELATIVE
105
+ // canonicalPath, read the file, and put only the CONTENT on the wire. Best-effort
106
+ // (P6): an ineligible record or an unreadable file (moved/deleted since capture)
107
+ // yields an empty body, which the detect endpoint skips as a no-op, so a review
108
+ // can never throw on a vanished doc.
109
+ function readCandidateBody(rec) {
110
+ if (rec.kind !== "produced_doc")
111
+ return "";
112
+ const root = (rec.repoRoot || "").trim();
113
+ if (!root)
114
+ return "";
115
+ try {
116
+ return (0, fs_1.readFileSync)(path.join(root, rec.canonicalPath), "utf8");
117
+ }
118
+ catch {
119
+ return "";
120
+ }
121
+ }
122
+ // Real client: POST the dry-run review request to intel. Carries the env-pinned
123
+ // workspace AND the owner so the detection scope is built server-side, never a
124
+ // parameter the caller widens.
125
+ function realIntelClient(cfg) {
126
+ return {
127
+ detect: async (req) => {
128
+ // INV-DETECTION-OWNER-SCOPED: the endpoint builds an owner-scoped corpus,
129
+ // so ownerUserId is required. Without a configured actor we cannot name an
130
+ // owner; throw so the runner's P6 catch degrades to an empty advisory
131
+ // rather than POSTing a request the endpoint validates away as a 422
132
+ // (which would degrade silently and look like "no conflicts").
133
+ const ownerUserId = (cfg.actorUserId || "").trim();
134
+ if (!ownerUserId) {
135
+ throw new Error("active-review detect requires actorUserId (owner scope)");
136
+ }
137
+ // Map each metadata-only Active Memory record to the endpoint's candidate
138
+ // wire shape (ActiveReviewCandidate {canonicalPath, body, kind}). The Zone 1
139
+ // spool captures metadata only (contentHash, no doc body, by privacy
140
+ // design), so at review time we read the produced doc's CURRENT content from
141
+ // disk (readCandidateBody: repoRoot-resolved, LOCAL-only, best-effort) and
142
+ // send THAT as body, which the in-process detector embeds and scores against
143
+ // the owner-scoped corpus. Sending the raw record would 422 (no `body`) and
144
+ // leak internal field names onto the wire.
145
+ const candidates = req.candidates.map((c) => ({
146
+ canonicalPath: c.canonicalPath,
147
+ body: readCandidateBody(c),
148
+ kind: c.kind,
149
+ }));
150
+ return (0, http_1.intelPost)(cfg, "/internal/v1/active-review/detect", { workspaceId: cfg.workspaceId, ownerUserId, dryRun: req.dryRun, candidates }, 8000);
151
+ },
152
+ };
153
+ }
154
+ // Stub client: returns the operator-supplied facts verbatim (MEETLESS_TAGGED_FACTS_STUB,
155
+ // parsed as a KbRelationFact[] JSON). Used by tests so the merge runs offline.
156
+ function stubTaggedFactsClient(stubJson) {
157
+ return {
158
+ fetch: async () => {
159
+ const parsed = JSON.parse(stubJson);
160
+ return Array.isArray(parsed) ? parsed : [];
161
+ },
162
+ };
163
+ }
164
+ // Real client: read APPROVED relation facts (statusId=ACCEPTED, posture=LIVE) for
165
+ // each referenced doc from control's relationship-candidates list endpoint, the
166
+ // same read kb pending uses (GET /internal/v1/relationship-candidates). We query
167
+ // per path by notePath, keep only conflict relations (SUPERSEDED_BY / CONTRADICTS),
168
+ // and map each row into a KbRelationFact whose fromPath is the doc the user named
169
+ // and whose toKbId/toPath is the row's target artifact. Best-effort: any error on
170
+ // any path is swallowed and yields no facts for it, so the supersession advisory
171
+ // degrades to empty rather than throwing (P6). Only the ACCEPTED/LIVE filter is
172
+ // trusted; the pure engine re-checks posture/status so an unapproved row can never
173
+ // leak even if the server filter were ever loosened.
174
+ //
175
+ // Config is loaded LAZILY, inside fetch and only if there is at least one path to
176
+ // resolve, via the supplied getter. The Phase 1 path must never pay a config load
177
+ // (or its possible throw) when the A3 path has nothing to do; loading eagerly here
178
+ // would couple a config failure to the unrelated conflict advisory.
179
+ function realTaggedFactsClient(getCfg) {
180
+ return {
181
+ fetch: async (referencedPaths) => {
182
+ if (referencedPaths.length === 0)
183
+ return [];
184
+ const cfg = getCfg();
185
+ const facts = [];
186
+ for (const p of referencedPaths) {
187
+ try {
188
+ const qs = new URLSearchParams();
189
+ qs.set("workspaceId", cfg.workspaceId);
190
+ qs.set("statusId", "ACCEPTED");
191
+ qs.set("posture", "LIVE");
192
+ qs.set("limit", "20");
193
+ if (p.includes(":"))
194
+ qs.set("artifactId", p);
195
+ else
196
+ qs.set("notePath", p);
197
+ const res = await (0, http_1.get)(cfg, `/internal/v1/relationship-candidates?${qs.toString()}`, 8000);
198
+ for (const row of res.items ?? []) {
199
+ const relationType = row.relationTypeId ?? "";
200
+ if (relationType !== "SUPERSEDED_BY" && relationType !== "CONTRADICTS")
201
+ continue;
202
+ const toId = row.targetArtifactId ?? "";
203
+ if (!toId)
204
+ continue;
205
+ facts.push({
206
+ fromPath: p,
207
+ relationType,
208
+ toPath: toId,
209
+ toKbId: toId,
210
+ posture: row.postureId ?? "LIVE",
211
+ status: row.statusId ?? "ACCEPTED",
212
+ });
213
+ }
214
+ }
215
+ catch {
216
+ // best-effort: this path contributes no facts; never a throw (P6).
217
+ }
218
+ }
219
+ return facts;
220
+ },
221
+ };
222
+ }
223
+ // Strict argv parsing: `mla _internal active-review --session <sid>`. The
224
+ // --session flag is optional (absent reviews every record in the store); any
225
+ // other flag is rejected so a hook template typo surfaces loudly rather than
226
+ // silently binding the wrong value.
227
+ function parseArgs(argv) {
228
+ let sessionId = null;
229
+ for (let i = 0; i < argv.length; i++) {
230
+ const a = argv[i];
231
+ if (a === "--session") {
232
+ sessionId = argv[i + 1] ?? null;
233
+ i++;
234
+ continue;
235
+ }
236
+ if (a.startsWith("-")) {
237
+ throw new Error(`Unknown flag: ${a}. \`mla _internal active-review\` takes only [--session <sid>].`);
238
+ }
239
+ throw new Error(`Unexpected positional argument: ${a}. \`mla _internal active-review\` takes only [--session <sid>].`);
240
+ }
241
+ return { sessionId };
242
+ }
243
+ // Render the advisory text the hook injects: one terse line per cited doc naming
244
+ // the candidate path, the conflict relation, the cited id, and the cited quote.
245
+ // Plain text (no markdown headers); multiple advisories are newline-joined.
246
+ function renderAdvisoryText(advisories) {
247
+ if (advisories.length === 0)
248
+ return "";
249
+ return advisories
250
+ .map((a) => `Active Review: ${a.candidatePath} may ${a.relationType} approved ${a.citedKbId} ("${a.citedQuote}").`)
251
+ .join("\n");
252
+ }
253
+ async function runInternalActiveReview(argv) {
254
+ let sessionId;
255
+ try {
256
+ ({ sessionId } = parseArgs(argv));
257
+ }
258
+ catch (e) {
259
+ console.error(e.message);
260
+ return 2;
261
+ }
262
+ // advise-never-block (P6): from here on, every failure path prints an empty
263
+ // advisory and exits 0 so the hook injects nothing rather than the turn seeing
264
+ // a non-zero review.
265
+ try {
266
+ const records = (0, active_memory_1.reduceActiveMemory)(activeMemoryStorePath(), {
267
+ nowMs: Date.now(),
268
+ ttlHours: TTL_HOURS,
269
+ maxRecords: MAX_RECORDS,
270
+ });
271
+ const scoped = sessionId ? records.filter((r) => r.sessionId === sessionId) : records;
272
+ // Build the clients. The stub paths are hermetic and need NO config (the hook
273
+ // test has only intelUrl, no control credentials), so read the stub env BEFORE
274
+ // any loadWorkspaceConfig(): only the real HTTP clients require a valid config.
275
+ // Config is loaded at most once and shared by both real clients.
276
+ const detectStub = process.env.MEETLESS_ACTIVE_REVIEW_STUB_DETECT;
277
+ const factsStub = process.env.MEETLESS_TAGGED_FACTS_STUB;
278
+ let cfg = null;
279
+ const ensureCfg = () => {
280
+ if (cfg === null)
281
+ cfg = (0, config_1.loadWorkspaceConfig)();
282
+ return cfg;
283
+ };
284
+ const intel = detectStub && detectStub.length > 0 ? stubIntelClient(detectStub) : realIntelClient(ensureCfg());
285
+ // The real facts client takes the lazy getter, not a resolved config, so config
286
+ // loads only inside its fetch (and only when there are referenced paths). This
287
+ // keeps the Phase 1 path (detect stub set, no facts stub) from ever paying a
288
+ // config load: a config failure must not couple into the conflict advisory.
289
+ const facts = factsStub && factsStub.length > 0 ? stubTaggedFactsClient(factsStub) : realTaggedFactsClient(ensureCfg);
290
+ // Phase 1: conflict advisories over the prior turn's produced docs.
291
+ const result = await (0, active_review_runner_1.runActiveReview)({ records: scoped, intel, minConfidence: MIN_CONFIDENCE });
292
+ // A3 (Phase 2): supersession/contradiction advisories over the docs the user
293
+ // NAMED this session (the tagged_reference captures). The pure engine joins
294
+ // those paths against approved (LIVE/ACCEPTED) relation facts; the fetch is
295
+ // best-effort (P6: a fetch failure swallows to no supersession advisory, never
296
+ // a throw). The supersession lines ride AFTER the Phase 1 conflict lines.
297
+ const referencedPaths = Array.from(new Set(scoped.filter((r) => r.kind === "tagged_reference").map((r) => r.canonicalPath)));
298
+ let supersessionLines = [];
299
+ if (referencedPaths.length > 0) {
300
+ try {
301
+ const relationFacts = await facts.fetch(referencedPaths);
302
+ supersessionLines = (0, tagged_reference_1.supersessionAdvisory)(referencedPaths, relationFacts).map((a) => a.message);
303
+ }
304
+ catch {
305
+ // best-effort: no supersession advisory on any fetch/join failure (P6).
306
+ supersessionLines = [];
307
+ }
308
+ }
309
+ const advisoryText = [renderAdvisoryText(result.advisories), ...supersessionLines]
310
+ .filter((s) => s.length > 0)
311
+ .join("\n");
312
+ console.log(JSON.stringify({ advisoryText, advisories: result.advisories }));
313
+ return 0;
314
+ }
315
+ catch {
316
+ // Any unexpected error (missing config, etc.) is silent: print an empty
317
+ // advisory and exit 0. Active Review is best-effort; it must never break the
318
+ // turn it rides on.
319
+ console.log(JSON.stringify({ advisoryText: "", advisories: [] }));
320
+ return 0;
321
+ }
322
+ }
@@ -0,0 +1,188 @@
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.parseArgs = parseArgs;
37
+ exports.runInternalAutoIndex = runInternalAutoIndex;
38
+ // tools/meetless-agent/src/commands/internal-auto-index.ts
39
+ // `mla _internal auto-index --session <sid>` (Zone 2 auto-index loop).
40
+ //
41
+ // Fired detached from the Stop hook (spawn_auto_index). It reads this session's
42
+ // produced-doc captures from the Zone 1 Active Review spool and indexes each into
43
+ // the owner's Personal KB as a SHADOW / agent_distilled doc via the idempotent
44
+ // `mla kb add` path. SHADOW never grounds anyone (INV-GROUNDING-APPROVED), so this
45
+ // auto-ingest cannot pollute retrieval; the explicit human gate moves to
46
+ // `mla kb promote` (SHADOW -> LIVE), which is unchanged.
47
+ //
48
+ // advise-never-block (P6): every failure path is swallowed; the command prints a
49
+ // JSON summary and exits 0 (except a strict argv parse error -> 2, and an
50
+ // owner-check denial -> 3). It runs off the session's hot path and must never
51
+ // disturb the session it rides on; the denial halt only stops THIS detached
52
+ // batch, never the session.
53
+ //
54
+ // Owner-denial halt (fix B3): a non-OWNER actor used to fail-soft once PER DOC
55
+ // (154 denial lines in the incident) because runKbAdd swallows KbOwnerCheckError
56
+ // into stderr + exit 2, invisible to this loop. The denial is run-fatal, not
57
+ // doc-local: the same actor is denied for every doc. So the run halts on the
58
+ // first denial with ONE message. The OWNER-only gate itself (kb_acl.ts + the
59
+ // control-side check) is locked design and is NOT touched here.
60
+ // See notes/20260605-mla-auto-index-loop-implementation-plan.md.
61
+ const fs = __importStar(require("fs"));
62
+ const path = __importStar(require("path"));
63
+ const config_1 = require("../lib/config");
64
+ const active_memory_1 = require("../lib/active-memory");
65
+ const auto_index_1 = require("../lib/auto-index");
66
+ const kb_acl_1 = require("../lib/kb_acl");
67
+ const kb_add_1 = require("./kb_add");
68
+ function activeMemoryStorePath() {
69
+ return path.join(config_1.HOME, "logs", "kb-knowledge.jsonl");
70
+ }
71
+ // Mirror the Active Review reader's window so the two zones see the same record set.
72
+ const TTL_HOURS = 48;
73
+ const MAX_RECORDS = 100;
74
+ function parseArgs(argv) {
75
+ let sessionId = null;
76
+ for (let i = 0; i < argv.length; i++) {
77
+ const a = argv[i];
78
+ if (a === "--session") {
79
+ sessionId = argv[i + 1] ?? null;
80
+ i++;
81
+ continue;
82
+ }
83
+ if (a.startsWith("-")) {
84
+ throw new Error(`Unknown flag: ${a}. \`mla _internal auto-index\` takes only [--session <sid>].`);
85
+ }
86
+ throw new Error(`Unexpected positional argument: ${a}. \`mla _internal auto-index\` takes only [--session <sid>].`);
87
+ }
88
+ return { sessionId };
89
+ }
90
+ // runKbAdd converts KbOwnerCheckError into stderr + exit 2 before it reaches
91
+ // this loop, so a thrown denial only arrives from injected add fns or future
92
+ // boundary changes. Match the class, the name (survives module-duplication
93
+ // boundaries), or the stable message prefix kb_acl.ts stamps on every denial.
94
+ function isOwnerDenial(e) {
95
+ if (e instanceof kb_acl_1.KbOwnerCheckError)
96
+ return true;
97
+ const err = e;
98
+ if (err && err.name === "KbOwnerCheckError")
99
+ return true;
100
+ return Boolean(err && typeof err.message === "string" && err.message.includes("KB owner check failed"));
101
+ }
102
+ function haltOnOwnerDenial(e, summary) {
103
+ const detail = e instanceof Error && e.message ? e.message : String(e);
104
+ // One clear line for the whole run instead of one denial per doc. The
105
+ // kb_acl message already names the actor, its role, and the OWNER
106
+ // requirement.
107
+ console.error(`auto-index halted: owner-check denial; remaining docs not attempted. ${detail}`);
108
+ console.log(JSON.stringify({ ...summary, halted: "owner_check_denied" }));
109
+ return 3;
110
+ }
111
+ async function runInternalAutoIndex(argv, deps = {}) {
112
+ let sessionId;
113
+ try {
114
+ ({ sessionId } = parseArgs(argv));
115
+ }
116
+ catch (e) {
117
+ console.error(e.message);
118
+ return 2;
119
+ }
120
+ const add = deps.add ?? kb_add_1.runKbAdd;
121
+ const storePath = deps.storePath ?? activeMemoryStorePath();
122
+ const verifyOwner = deps.verifyOwner ??
123
+ (deps.add ? null : async (ws) => (0, kb_acl_1.verifyKbActorIsOwner)((0, config_1.readKbConfig)(ws)));
124
+ try {
125
+ // Scope to this session BEFORE dedup: the content-keyed dedup identity omits
126
+ // sessionId, so a post-reduce filter could drop this session's doc in favor of
127
+ // an identical-content record from another session. sessionId === null (no flag)
128
+ // reduces the whole spool, matching the active-review reader's window.
129
+ const records = (0, active_memory_1.reduceActiveMemory)(storePath, {
130
+ nowMs: Date.now(),
131
+ ttlHours: TTL_HOURS,
132
+ maxRecords: MAX_RECORDS,
133
+ ...(sessionId ? { sessionId } : {}),
134
+ });
135
+ const targets = (0, auto_index_1.selectIndexTargets)(records);
136
+ let indexed = 0;
137
+ let skipped = 0;
138
+ let failed = 0;
139
+ const verifiedWorkspaces = new Set();
140
+ for (const t of targets) {
141
+ if (!fs.existsSync(t.absPath)) {
142
+ skipped++;
143
+ continue;
144
+ }
145
+ // Owner-gate preflight, once per workspace: runKbAdd would swallow a
146
+ // denial into exit 2 per doc; checking here lets a denial halt the
147
+ // whole run before the spam starts. Non-denial preflight failures
148
+ // (missing config, control unreachable) stay fail-soft and let the add
149
+ // surface its own per-doc outcome.
150
+ if (verifyOwner && !verifiedWorkspaces.has(t.workspaceId)) {
151
+ try {
152
+ await verifyOwner(t.workspaceId);
153
+ verifiedWorkspaces.add(t.workspaceId);
154
+ }
155
+ catch (e) {
156
+ if (isOwnerDenial(e)) {
157
+ return haltOnOwnerDenial(e, { indexed, skipped, failed, total: targets.length });
158
+ }
159
+ }
160
+ }
161
+ try {
162
+ // Pass this run's raw session UUID (from `--session`, kept raw for spool
163
+ // scoping above) so the add carries `--agent-session` to the intel route.
164
+ // buildKbAddArgv canonicalizes it; null/invalid simply omits the flag.
165
+ const code = await add((0, auto_index_1.buildKbAddArgv)(t, sessionId));
166
+ if (code === 0)
167
+ indexed++;
168
+ else
169
+ failed++;
170
+ }
171
+ catch (e) {
172
+ if (isOwnerDenial(e)) {
173
+ // Run-fatal, not doc-local: the same denial would repeat for every
174
+ // remaining doc.
175
+ return haltOnOwnerDenial(e, { indexed, skipped, failed, total: targets.length });
176
+ }
177
+ failed++; // fail-soft: one bad add never aborts the batch.
178
+ }
179
+ }
180
+ console.log(JSON.stringify({ indexed, skipped, failed, total: targets.length }));
181
+ return 0;
182
+ }
183
+ catch {
184
+ // Any unexpected error (unreadable store, etc.) degrades to a quiet no-op.
185
+ console.log(JSON.stringify({ indexed: 0, skipped: 0, failed: 0, total: 0 }));
186
+ return 0;
187
+ }
188
+ }