@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,214 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoLiveVersionToRevokeError = exports.NoLiveVersionToSupersedeError = exports.CrossScopeLineageError = void 0;
4
+ exports.insertLocalRuleVersion = insertLocalRuleVersion;
5
+ exports.getLocalRuleVersion = getLocalRuleVersion;
6
+ exports.getLiveLocalRuleVersion = getLiveLocalRuleVersion;
7
+ exports.listLiveLocalRuleVersions = listLiveLocalRuleVersions;
8
+ exports.listLocalRuleVersionHistory = listLocalRuleVersionHistory;
9
+ exports.supersedeLiveLocalRuleVersion = supersedeLiveLocalRuleVersion;
10
+ exports.revokeLiveLocalRuleVersion = revokeLiveLocalRuleVersion;
11
+ exports.insertVersionEvaluationRecord = insertVersionEvaluationRecord;
12
+ const interception_store_1 = require("./interception-store");
13
+ /** Raised when a version's lineage pointer would cross a runtime scope (FK lineage must stay in-scope). */
14
+ class CrossScopeLineageError extends Error {
15
+ versionId;
16
+ runtimeScopeId;
17
+ supersedesVersionId;
18
+ constructor(versionId, runtimeScopeId, supersedesVersionId) {
19
+ super(`version ${versionId} in scope ${runtimeScopeId} cannot supersede ${supersedesVersionId}: ` +
20
+ `the predecessor is in a different runtime scope`);
21
+ this.versionId = versionId;
22
+ this.runtimeScopeId = runtimeScopeId;
23
+ this.supersedesVersionId = supersedesVersionId;
24
+ this.name = "CrossScopeLineageError";
25
+ }
26
+ }
27
+ exports.CrossScopeLineageError = CrossScopeLineageError;
28
+ /** Raised when a supersession is requested but no LIVE version exists for the (scope, rule) to replace. */
29
+ class NoLiveVersionToSupersedeError extends Error {
30
+ runtimeScopeId;
31
+ ruleId;
32
+ constructor(runtimeScopeId, ruleId) {
33
+ super(`no LIVE version for rule ${ruleId} in scope ${runtimeScopeId} to supersede`);
34
+ this.runtimeScopeId = runtimeScopeId;
35
+ this.ruleId = ruleId;
36
+ this.name = "NoLiveVersionToSupersedeError";
37
+ }
38
+ }
39
+ exports.NoLiveVersionToSupersedeError = NoLiveVersionToSupersedeError;
40
+ /** Raised when a revoke (kill switch) is requested but no LIVE version exists for the (scope, rule). */
41
+ class NoLiveVersionToRevokeError extends Error {
42
+ runtimeScopeId;
43
+ ruleId;
44
+ constructor(runtimeScopeId, ruleId) {
45
+ super(`no LIVE version for rule ${ruleId} in scope ${runtimeScopeId} to revoke`);
46
+ this.runtimeScopeId = runtimeScopeId;
47
+ this.ruleId = ruleId;
48
+ this.name = "NoLiveVersionToRevokeError";
49
+ }
50
+ }
51
+ exports.NoLiveVersionToRevokeError = NoLiveVersionToRevokeError;
52
+ // ---------------------------------------------------------------------------
53
+ // local_rule_version
54
+ // ---------------------------------------------------------------------------
55
+ function mapVersionRow(row) {
56
+ return {
57
+ versionId: row.version_id,
58
+ ruleId: row.rule_id,
59
+ runtimeScopeId: row.runtime_scope_id,
60
+ rulePayload: row.rule_payload,
61
+ canonicalPayloadHash: row.canonical_payload_hash,
62
+ lifecycleStatus: row.lifecycle_status,
63
+ attestationMethod: row.attestation_method,
64
+ attestedBy: row.attested_by,
65
+ supersedesVersionId: row.supersedes_version_id ?? null,
66
+ derivedFromObservedHash: row.derived_from_observed_hash ?? null,
67
+ attestedAt: row.attested_at,
68
+ };
69
+ }
70
+ /** Insert a version exactly as given. Same-scope FK lineage is verified BEFORE the write; everything
71
+ * else (one-LIVE, payload uniqueness, immutability) is left to the schema. */
72
+ function insertLocalRuleVersion(store, rec) {
73
+ if (rec.supersedesVersionId !== null) {
74
+ const predecessor = getLocalRuleVersionAnyScope(store, rec.supersedesVersionId);
75
+ if (predecessor && predecessor.runtimeScopeId !== rec.runtimeScopeId) {
76
+ throw new CrossScopeLineageError(rec.versionId, rec.runtimeScopeId, rec.supersedesVersionId);
77
+ }
78
+ }
79
+ store.db
80
+ .prepare(`INSERT INTO local_rule_version
81
+ (version_id, rule_id, runtime_scope_id, rule_payload, canonical_payload_hash,
82
+ lifecycle_status, attestation_method, attested_by, supersedes_version_id,
83
+ derived_from_observed_hash, attested_at)
84
+ VALUES
85
+ (@version_id, @rule_id, @runtime_scope_id, @rule_payload, @canonical_payload_hash,
86
+ @lifecycle_status, @attestation_method, @attested_by, @supersedes_version_id,
87
+ @derived_from_observed_hash, @attested_at)`)
88
+ .run({
89
+ version_id: rec.versionId,
90
+ rule_id: rec.ruleId,
91
+ runtime_scope_id: rec.runtimeScopeId,
92
+ rule_payload: rec.rulePayload,
93
+ canonical_payload_hash: rec.canonicalPayloadHash,
94
+ lifecycle_status: rec.lifecycleStatus,
95
+ attestation_method: rec.attestationMethod,
96
+ attested_by: rec.attestedBy,
97
+ supersedes_version_id: rec.supersedesVersionId,
98
+ derived_from_observed_hash: rec.derivedFromObservedHash,
99
+ attested_at: rec.attestedAt,
100
+ });
101
+ }
102
+ /** Read one version by the runtime-scope-safe composite key; null if it is absent OR in another scope. */
103
+ function getLocalRuleVersion(store, versionId, runtimeScopeId) {
104
+ const row = store.db
105
+ .prepare(`SELECT * FROM local_rule_version WHERE version_id = ? AND runtime_scope_id = ?`)
106
+ .get(versionId, runtimeScopeId);
107
+ return row ? mapVersionRow(row) : null;
108
+ }
109
+ /** Scope-agnostic lookup used ONLY to validate lineage (a predecessor's scope). Not exported. */
110
+ function getLocalRuleVersionAnyScope(store, versionId) {
111
+ const row = store.db
112
+ .prepare(`SELECT * FROM local_rule_version WHERE version_id = ?`)
113
+ .get(versionId);
114
+ return row ? mapVersionRow(row) : null;
115
+ }
116
+ /** Read the current LIVE version for a (scope, rule); null when none is LIVE. */
117
+ function getLiveLocalRuleVersion(store, runtimeScopeId, ruleId) {
118
+ const row = store.db
119
+ .prepare(`SELECT * FROM local_rule_version
120
+ WHERE runtime_scope_id = ? AND rule_id = ? AND lifecycle_status = 'LIVE'`)
121
+ .get(runtimeScopeId, ruleId);
122
+ return row ? mapVersionRow(row) : null;
123
+ }
124
+ /**
125
+ * List EVERY LIVE rule version in one scope, ordered by ruleId ascending. This is the rule-driven
126
+ * enforce dispatch's input (R4): the seam evaluates all of these against one tool attempt. The ruleId
127
+ * ordering is the deterministic tie-break the dispatch relies on when more than one rule would deny the
128
+ * same action (it emits the deny of the lowest ruleId and records the rest as arms). One LIVE row per
129
+ * (scope, rule) is guaranteed by ux_one_live_version, so this never returns two versions of one rule.
130
+ */
131
+ function listLiveLocalRuleVersions(store, runtimeScopeId) {
132
+ const rows = store.db
133
+ .prepare(`SELECT * FROM local_rule_version
134
+ WHERE runtime_scope_id = ? AND lifecycle_status = 'LIVE'
135
+ ORDER BY rule_id`)
136
+ .all(runtimeScopeId);
137
+ return rows.map(mapVersionRow);
138
+ }
139
+ /** List a rule's version history in one scope, oldest first (attested_at then version_id). */
140
+ function listLocalRuleVersionHistory(store, runtimeScopeId, ruleId) {
141
+ const rows = store.db
142
+ .prepare(`SELECT * FROM local_rule_version
143
+ WHERE runtime_scope_id = ? AND rule_id = ?
144
+ ORDER BY attested_at, version_id`)
145
+ .all(runtimeScopeId, ruleId);
146
+ return rows.map(mapVersionRow);
147
+ }
148
+ /**
149
+ * Atomically supersede the current LIVE version of a (scope, rule) with `successor`. In one
150
+ * BEGIN IMMEDIATE transaction: find the current LIVE; demote it to SUPERSEDED (the only lifecycle
151
+ * transition trg_version_immutable allows); insert `successor` as LIVE with supersedes_version_id
152
+ * pointing at the demoted version. Demote-then-insert is deliberate: it never leaves two LIVE rows
153
+ * for the (scope, rule), so ux_one_live_version is satisfied at every statement boundary. Throws
154
+ * NoLiveVersionToSupersedeError when there is nothing LIVE to replace, and refuses a `successor`
155
+ * whose supersedesVersionId is provided but does not name the actual prior LIVE. Returns the minted
156
+ * successor with its lineage filled in.
157
+ */
158
+ function supersedeLiveLocalRuleVersion(store, successor) {
159
+ const run = store.db.transaction(() => {
160
+ const current = getLiveLocalRuleVersion(store, successor.runtimeScopeId, successor.ruleId);
161
+ if (!current) {
162
+ throw new NoLiveVersionToSupersedeError(successor.runtimeScopeId, successor.ruleId);
163
+ }
164
+ if (successor.supersedesVersionId !== null && successor.supersedesVersionId !== current.versionId) {
165
+ throw new Error(`successor ${successor.versionId} claims to supersede ${successor.supersedesVersionId} ` +
166
+ `but the current LIVE version is ${current.versionId}`);
167
+ }
168
+ store.db
169
+ .prepare(`UPDATE local_rule_version SET lifecycle_status = 'SUPERSEDED' WHERE version_id = ?`)
170
+ .run(current.versionId);
171
+ const minted = {
172
+ ...successor,
173
+ lifecycleStatus: "LIVE",
174
+ supersedesVersionId: current.versionId,
175
+ };
176
+ insertLocalRuleVersion(store, minted);
177
+ return minted;
178
+ });
179
+ return run.immediate();
180
+ }
181
+ /**
182
+ * The kill switch. Atomically disarm a (scope, rule) by flipping its current LIVE version to REVOKED
183
+ * (the LIVE->REVOKED transition trg_version_immutable explicitly permits). After this the (scope, rule)
184
+ * has NO LIVE version, so getLiveLocalRuleVersion returns null and the enforce seam finds NO_LIVE_VERSION
185
+ * and fails open: enforcement stops cleanly without deleting any history. Scoped strictly to the given
186
+ * runtime scope, so a same-rule LIVE version in another scope is never touched. Throws
187
+ * NoLiveVersionToRevokeError when there is nothing LIVE to disarm. Returns the now-REVOKED version.
188
+ */
189
+ function revokeLiveLocalRuleVersion(store, runtimeScopeId, ruleId) {
190
+ const run = store.db.transaction(() => {
191
+ const current = getLiveLocalRuleVersion(store, runtimeScopeId, ruleId);
192
+ if (!current) {
193
+ throw new NoLiveVersionToRevokeError(runtimeScopeId, ruleId);
194
+ }
195
+ store.db
196
+ .prepare(`UPDATE local_rule_version SET lifecycle_status = 'REVOKED' WHERE version_id = ?`)
197
+ .run(current.versionId);
198
+ return { ...current, lifecycleStatus: "REVOKED" };
199
+ });
200
+ return run.immediate();
201
+ }
202
+ // ---------------------------------------------------------------------------
203
+ // rule_evaluation_record (version arm)
204
+ // ---------------------------------------------------------------------------
205
+ /** Write a version-arm verdict: a rule_evaluation_record bound to a LocalRuleVersion. The observed
206
+ * arm is forced null here, so the schema's arm CHECK can only ever see the version arm; the composite
207
+ * (rule_version_id, runtime_scope_id) FK rejects a verdict that references a version in another scope. */
208
+ function insertVersionEvaluationRecord(store, input) {
209
+ (0, interception_store_1.insertRuleEvaluationRecord)(store, {
210
+ ...input,
211
+ observedRuleSnapshot: null,
212
+ observedRuleHash: null,
213
+ });
214
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * MEMORY-REQUIREMENT CLASSIFIER (vendored into the CLI for the UserPromptSubmit hook).
4
+ *
5
+ * Given the operator's current prompt, derive whether asserting an answer WITHOUT
6
+ * consulting governed memory plausibly violates an operator expectation. The CE0
7
+ * obligation forms ONLY on a REQUIRED turn, so this classifier is the relevance
8
+ * trigger for CONSULT_GOVERNED_EVIDENCE_ON_MEMORY_REQUIRED_TURN_V1
9
+ * (notes/20260617-evidence-consultation-forcing-function-proposal.md §1.3).
10
+ *
11
+ * Why vendored, not imported: the CLI intentionally does not depend on
12
+ * @meetless/utils (see kb-candidate.ts). This is a byte-faithful copy of the utils
13
+ * classifier (packages/utils/src/memory-requirement.ts); the seed-set versions
14
+ * (raw-prompt-substring-v1 / seed-v1) are pinned so the two implementations cannot
15
+ * silently diverge on which turns are governed. The classification is pure: no hash,
16
+ * no persistence, no runtime. The TurnMemoryAssessment record and the obligation it
17
+ * may create live in the store and the adapter.
18
+ *
19
+ * Three-valued band (R3 P1.2 removed HELPFUL): REQUIRED | NOT_REQUIRED | UNKNOWN.
20
+ * Only REQUIRED creates an obligation (P0.3); NOT_REQUIRED and UNKNOWN are telemetry.
21
+ * A REQUIRED seed marker wins (the seed catches "why did we choose X" even though
22
+ * "why" is itself an excluded generic lead-in); failing that, an EXCLUSION match is
23
+ * what makes a turn CONFIDENTLY NOT_REQUIRED; matching NEITHER is undecidable from
24
+ * shape, so it is UNKNOWN (the population recall improvements later promote).
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.EXCLUSION_MARKERS = exports.REQUIRED_MARKERS = exports.MEMORY_REQUIREMENT_EXCLUSION_SET_VERSION = exports.MEMORY_REQUIREMENT_MARKER_SET_VERSION = exports.MEMORY_REQUIREMENT_CLASSIFIER_VERSION = void 0;
28
+ exports.normalizeForMarkerMatch = normalizeForMarkerMatch;
29
+ exports.classifyMemoryRequirement = classifyMemoryRequirement;
30
+ /** Frozen seed-set versions, stamped on every classification (P1.3 attestation). */
31
+ exports.MEMORY_REQUIREMENT_CLASSIFIER_VERSION = "raw-prompt-substring-v1";
32
+ exports.MEMORY_REQUIREMENT_MARKER_SET_VERSION = "seed-v1";
33
+ exports.MEMORY_REQUIREMENT_EXCLUSION_SET_VERSION = "seed-v1";
34
+ // Sorted for byte-stable identity; de-duplicated by construction.
35
+ const sortedFrozen = (markers) => Object.freeze(Array.from(new Set(markers)).sort());
36
+ /**
37
+ * Seed REQUIRED markers (proposal lines 290-301): high precision, deliberately low
38
+ * recall. A match means the prompt points at workspace-internal decisions, ownership,
39
+ * policy, architecture, or cross-session history.
40
+ */
41
+ exports.REQUIRED_MARKERS = sortedFrozen([
42
+ "what did we decide",
43
+ "why did we choose",
44
+ "are we still doing",
45
+ "our canonical",
46
+ "previous session",
47
+ "earlier agent",
48
+ "who owns",
49
+ "who approves",
50
+ "our policy",
51
+ "our architecture decision",
52
+ ]);
53
+ /**
54
+ * Explicitly EXCLUDED generic-conceptual lead-ins (proposal lines 305-310), stored as
55
+ * their placeholder-free, matchable prefixes. A match (absent any REQUIRED marker)
56
+ * makes the turn CONFIDENTLY NOT_REQUIRED.
57
+ */
58
+ exports.EXCLUSION_MARKERS = sortedFrozen([
59
+ "why",
60
+ "how does",
61
+ "what is",
62
+ "difference between",
63
+ ]);
64
+ /**
65
+ * Normalize text to a space-padded, lowercased, single-spaced token stream so a marker
66
+ * can be matched on token boundaries via plain substring containment (" who owns " is
67
+ * in " who owns this " but not in " whoever owns "). Any run of non-alphanumeric
68
+ * characters becomes a single space; an empty / all-punctuation input reduces to a
69
+ * single pad space.
70
+ */
71
+ function normalizeForMarkerMatch(text) {
72
+ const core = text
73
+ .toLowerCase()
74
+ .replace(/[^a-z0-9]+/g, " ")
75
+ .trim();
76
+ return core === "" ? " " : ` ${core} `;
77
+ }
78
+ /** Which of `markers` appear as whole-token substrings of the padded prompt. */
79
+ function matched(paddedPrompt, markers) {
80
+ return markers.filter((m) => paddedPrompt.includes(` ${m} `));
81
+ }
82
+ /**
83
+ * Classify the current prompt's MemoryRequirement. Pure and deterministic: REQUIRED if
84
+ * any seed marker matches; else NOT_REQUIRED if any exclusion marker matches; else
85
+ * UNKNOWN.
86
+ */
87
+ function classifyMemoryRequirement(prompt) {
88
+ const padded = normalizeForMarkerMatch(prompt);
89
+ const markersMatched = matched(padded, exports.REQUIRED_MARKERS);
90
+ const exclusionsMatched = matched(padded, exports.EXCLUSION_MARKERS);
91
+ let requirement;
92
+ if (markersMatched.length > 0) {
93
+ requirement = "REQUIRED";
94
+ }
95
+ else if (exclusionsMatched.length > 0) {
96
+ requirement = "NOT_REQUIRED";
97
+ }
98
+ else {
99
+ requirement = "UNKNOWN";
100
+ }
101
+ return {
102
+ requirement,
103
+ markersMatched,
104
+ exclusionsMatched,
105
+ classifierVersion: exports.MEMORY_REQUIREMENT_CLASSIFIER_VERSION,
106
+ markerSetVersion: exports.MEMORY_REQUIREMENT_MARKER_SET_VERSION,
107
+ exclusionSetVersion: exports.MEMORY_REQUIREMENT_EXCLUSION_SET_VERSION,
108
+ };
109
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.observeNotesRule = observeNotesRule;
4
+ const notes_rule_1 = require("./notes-rule");
5
+ const observe_adapter_1 = require("./observe-adapter");
6
+ const notes_path_1 = require("./notes-path");
7
+ const NO_DECISION = {};
8
+ /**
9
+ * Observe a single PreToolUse call against the notes-location pilot. Selects the
10
+ * pilot directive from the scan cache, normalizes it into an ObservedRuleSpec,
11
+ * derives the runtime forbidden-root scope, and hands the call to the observe-only
12
+ * adapter. Always returns an empty (decision-free) response; the verdict travels on
13
+ * the observation side channel.
14
+ */
15
+ async function observeNotesRule(input) {
16
+ // No notes-location rule declared in this workspace: nothing to observe. This is
17
+ // genuinely NOT_APPLICABLE (absence of a rule), not an infrastructure fault.
18
+ const directive = (0, notes_rule_1.selectNotesLocationDirective)(input.directives);
19
+ if (!directive) {
20
+ return { response: NO_DECISION, observation: { kind: "NOT_APPLICABLE" } };
21
+ }
22
+ const built = (0, notes_rule_1.buildObservedNotesRuleSpec)(directive);
23
+ if (!built.ok) {
24
+ return { response: NO_DECISION, observation: { kind: "INFRA", diagnostic: built.diagnostic } };
25
+ }
26
+ const notesScope = {
27
+ canonicalProjectRoot: input.runtimeProjectRoot,
28
+ configuredRelativeForbiddenPath: built.spec.forbiddenRootRelativePath,
29
+ };
30
+ const config = {
31
+ applicability: built.spec.applicability,
32
+ notesScope,
33
+ classify: input.classify ?? notes_path_1.classifyTargetPath,
34
+ timeoutMs: input.timeoutMs,
35
+ };
36
+ // The adapter runs selection + compliance evaluation and always returns
37
+ // `response: {}`. We pass its result straight through.
38
+ return (0, observe_adapter_1.observePreToolUse)(input.rawStdin, config);
39
+ }
@@ -0,0 +1,261 @@
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.isUnderRoot = isUnderRoot;
37
+ exports.classifyTargetPath = classifyTargetPath;
38
+ exports.classifyRuntimeTarget = classifyRuntimeTarget;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const evaluation_input_hash_1 = require("./evaluation-input-hash");
42
+ const moduleCaseCache = new Map();
43
+ /** A tail component must be a single, safe, lexical name (no FS lookup). */
44
+ function isValidTailComponent(component) {
45
+ return (component.length > 0 &&
46
+ component !== "." &&
47
+ component !== ".." &&
48
+ !component.includes("/") &&
49
+ !component.includes(path.sep) &&
50
+ !component.includes("\0"));
51
+ }
52
+ /**
53
+ * Canonicalize an absolute path. Walks up to the nearest existing ancestor,
54
+ * realpaths it (following symlinks), validates each not-yet-existing tail
55
+ * component lexically, and re-appends the tail. Returns null when the path
56
+ * cannot be canonicalized (permission errors, ambiguous `..` past a missing
57
+ * directory, etc.).
58
+ */
59
+ async function canonicalize(absPath) {
60
+ const tail = [];
61
+ let cur = absPath;
62
+ for (;;) {
63
+ let resolved;
64
+ try {
65
+ resolved = await fs.promises.realpath(cur);
66
+ }
67
+ catch (err) {
68
+ const code = err.code;
69
+ if (code !== "ENOENT") {
70
+ // EACCES, ELOOP, ENOTDIR, invalid argument (NUL), etc.: cannot prove.
71
+ return null;
72
+ }
73
+ const base = path.basename(cur);
74
+ const parent = path.dirname(cur);
75
+ if (parent === cur) {
76
+ // Reached the filesystem root without finding an existing ancestor.
77
+ return null;
78
+ }
79
+ if (!isValidTailComponent(base)) {
80
+ return null;
81
+ }
82
+ tail.push(base);
83
+ cur = parent;
84
+ continue;
85
+ }
86
+ const canonical = tail.length > 0 ? path.join(resolved, ...tail.reverse()) : resolved;
87
+ let existingDir;
88
+ try {
89
+ const st = await fs.promises.stat(resolved);
90
+ existingDir = st.isDirectory() ? resolved : path.dirname(resolved);
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ return { canonical, existingDir };
96
+ }
97
+ }
98
+ /**
99
+ * Read-only per-device case-sensitivity probe: flip the case of the deepest
100
+ * existing directory's name and stat the sibling. Same inode -> case-insensitive;
101
+ * ENOENT -> case-sensitive; anything else (or no alphabetic character to flip)
102
+ * -> undeterminable.
103
+ */
104
+ function defaultCaseProbe(existingDir) {
105
+ let real;
106
+ try {
107
+ real = fs.realpathSync(existingDir);
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ const base = path.basename(real);
113
+ const parent = path.dirname(real);
114
+ const flipped = flipCase(base);
115
+ if (flipped === base) {
116
+ // No alphabetic character to flip; cannot probe non-destructively.
117
+ return null;
118
+ }
119
+ let origStat;
120
+ try {
121
+ origStat = fs.statSync(real);
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ try {
127
+ const flipStat = fs.statSync(path.join(parent, flipped));
128
+ return flipStat.ino === origStat.ino && flipStat.dev === origStat.dev;
129
+ }
130
+ catch (err) {
131
+ if (err.code === "ENOENT") {
132
+ return false;
133
+ }
134
+ return null;
135
+ }
136
+ }
137
+ function flipCase(name) {
138
+ let out = "";
139
+ for (const ch of name) {
140
+ const lower = ch.toLowerCase();
141
+ const upper = ch.toUpperCase();
142
+ if (lower !== upper) {
143
+ out += ch === lower ? upper : lower;
144
+ }
145
+ else {
146
+ out += ch;
147
+ }
148
+ }
149
+ return out;
150
+ }
151
+ /**
152
+ * Pure prefix comparison under a case policy. A path is "under" the root when it
153
+ * equals the root or is a descendant (boundary-correct: "/a/notes-archive" is
154
+ * NOT under "/a/notes").
155
+ */
156
+ function isUnderRoot(target, root, caseInsensitive) {
157
+ const t = caseInsensitive ? target.toLowerCase() : target;
158
+ const r = caseInsensitive ? root.toLowerCase() : root;
159
+ if (t === r) {
160
+ return true;
161
+ }
162
+ return t.startsWith(r.endsWith(path.sep) ? r : r + path.sep);
163
+ }
164
+ async function classifyTargetPath(rawFilePath, scope, opts = {}) {
165
+ if (typeof rawFilePath !== "string" || rawFilePath.length === 0) {
166
+ return "INDETERMINATE";
167
+ }
168
+ if (rawFilePath.includes("\0")) {
169
+ return "INDETERMINATE";
170
+ }
171
+ const absTarget = path.isAbsolute(rawFilePath)
172
+ ? rawFilePath
173
+ : scope.canonicalProjectRoot + path.sep + rawFilePath;
174
+ const forbiddenRaw = path.isAbsolute(scope.configuredRelativeForbiddenPath)
175
+ ? scope.configuredRelativeForbiddenPath
176
+ : path.join(scope.canonicalProjectRoot, scope.configuredRelativeForbiddenPath);
177
+ const target = await canonicalize(absTarget);
178
+ if (!target) {
179
+ return "INDETERMINATE";
180
+ }
181
+ const forbidden = await canonicalize(forbiddenRaw);
182
+ if (!forbidden) {
183
+ return "INDETERMINATE";
184
+ }
185
+ const caseInsensitive = resolveCasePolicy(forbidden.existingDir, opts);
186
+ if (caseInsensitive === null) {
187
+ return "INDETERMINATE";
188
+ }
189
+ return isUnderRoot(target.canonical, forbidden.canonical, caseInsensitive)
190
+ ? "UNDER_FORBIDDEN_ROOT"
191
+ : "OUTSIDE_FORBIDDEN_ROOT";
192
+ }
193
+ /**
194
+ * Classify a target path into the evaluation-input-v1 `target` union (the
195
+ * pathCanonicalizerVersion="notes-path-v1" canonicalizer feeding the persisted
196
+ * snapshot). This is the runtime-SCOPE axis, distinct from the forbidden-root denylist
197
+ * of classifyTargetPath: it answers "where does this action write, relative to the
198
+ * runtime project root?", never leaking an absolute home path.
199
+ *
200
+ * RUNTIME_RELATIVE { path } the target canonicalizes under the runtime root; `path`
201
+ * is the posix, machine-independent relative path.
202
+ * OUTSIDE_RUNTIME_SCOPE the target canonicalizes outside the runtime root; no
203
+ * path is carried (privacy).
204
+ * UNKNOWN / CANONICALIZATION_FAILED canonicalization or the case policy cannot prove
205
+ * the answer (mirrors classifyTargetPath's INDETERMINATE).
206
+ *
207
+ * The stored target plus forbiddenRootRelativePath are sufficient for a later replay to
208
+ * recompute the verdict from the snapshot alone, with no second filesystem probe.
209
+ */
210
+ async function classifyRuntimeTarget(rawFilePath, runtimeProjectRoot, opts = {}) {
211
+ const unknown = { kind: "UNKNOWN", reasonCode: evaluation_input_hash_1.CANONICALIZATION_FAILED };
212
+ if (typeof rawFilePath !== "string" || rawFilePath.length === 0) {
213
+ return unknown;
214
+ }
215
+ if (rawFilePath.includes("\0")) {
216
+ return unknown;
217
+ }
218
+ const absTarget = path.isAbsolute(rawFilePath)
219
+ ? rawFilePath
220
+ : runtimeProjectRoot + path.sep + rawFilePath;
221
+ const target = await canonicalize(absTarget);
222
+ if (!target) {
223
+ return unknown;
224
+ }
225
+ const root = await canonicalize(runtimeProjectRoot);
226
+ if (!root) {
227
+ return unknown;
228
+ }
229
+ const caseInsensitive = resolveCasePolicy(root.existingDir, opts);
230
+ if (caseInsensitive === null) {
231
+ return unknown;
232
+ }
233
+ if (!isUnderRoot(target.canonical, root.canonical, caseInsensitive)) {
234
+ return { kind: "OUTSIDE_RUNTIME_SCOPE" };
235
+ }
236
+ const relative = path.relative(root.canonical, target.canonical);
237
+ return { kind: "RUNTIME_RELATIVE", path: relative.split(path.sep).join("/") };
238
+ }
239
+ function resolveCasePolicy(existingDir, opts) {
240
+ const probe = opts.caseProbe ?? defaultCaseProbe;
241
+ // The process-wide cache is only consulted/written for the default probe. An
242
+ // injected probe is an explicit per-call override: it must neither be shadowed
243
+ // by a cached value nor poison the cache for other callers. A caller that wants
244
+ // a custom probe to be cached can pass its own caseCache.
245
+ const cache = opts.caseCache ?? (opts.caseProbe ? undefined : moduleCaseCache);
246
+ let dev;
247
+ try {
248
+ dev = fs.statSync(existingDir).dev;
249
+ }
250
+ catch {
251
+ dev = undefined;
252
+ }
253
+ if (cache && dev !== undefined && cache.has(dev)) {
254
+ return cache.get(dev);
255
+ }
256
+ const result = probe(existingDir);
257
+ if (cache && result !== null && dev !== undefined) {
258
+ cache.set(dev, result);
259
+ }
260
+ return result;
261
+ }