@lannguyensi/harness 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/CHANGELOG.md +552 -0
  2. package/LICENSE +21 -0
  3. package/README.md +154 -0
  4. package/dist/cli/add/index.d.ts +14 -0
  5. package/dist/cli/add/index.js +71 -0
  6. package/dist/cli/add/index.js.map +1 -0
  7. package/dist/cli/add/mutate.d.ts +39 -0
  8. package/dist/cli/add/mutate.js +36 -0
  9. package/dist/cli/add/mutate.js.map +1 -0
  10. package/dist/cli/adopt/derive.d.ts +38 -0
  11. package/dist/cli/adopt/derive.js +94 -0
  12. package/dist/cli/adopt/derive.js.map +1 -0
  13. package/dist/cli/adopt/index.d.ts +20 -0
  14. package/dist/cli/adopt/index.js +156 -0
  15. package/dist/cli/adopt/index.js.map +1 -0
  16. package/dist/cli/apply/apply.d.ts +49 -0
  17. package/dist/cli/apply/apply.js +333 -0
  18. package/dist/cli/apply/apply.js.map +1 -0
  19. package/dist/cli/apply/generate-memory-index.d.ts +17 -0
  20. package/dist/cli/apply/generate-memory-index.js +167 -0
  21. package/dist/cli/apply/generate-memory-index.js.map +1 -0
  22. package/dist/cli/apply/generate-settings.d.ts +15 -0
  23. package/dist/cli/apply/generate-settings.js +87 -0
  24. package/dist/cli/apply/generate-settings.js.map +1 -0
  25. package/dist/cli/apply/index.d.ts +1 -0
  26. package/dist/cli/apply/index.js +2 -0
  27. package/dist/cli/apply/index.js.map +1 -0
  28. package/dist/cli/audit.d.ts +36 -0
  29. package/dist/cli/audit.js +121 -0
  30. package/dist/cli/audit.js.map +1 -0
  31. package/dist/cli/describe.d.ts +13 -0
  32. package/dist/cli/describe.js +26 -0
  33. package/dist/cli/describe.js.map +1 -0
  34. package/dist/cli/diff/engine.d.ts +21 -0
  35. package/dist/cli/diff/engine.js +161 -0
  36. package/dist/cli/diff/engine.js.map +1 -0
  37. package/dist/cli/diff/git.d.ts +6 -0
  38. package/dist/cli/diff/git.js +32 -0
  39. package/dist/cli/diff/git.js.map +1 -0
  40. package/dist/cli/diff/index.d.ts +15 -0
  41. package/dist/cli/diff/index.js +39 -0
  42. package/dist/cli/diff/index.js.map +1 -0
  43. package/dist/cli/diff/since-apply.d.ts +57 -0
  44. package/dist/cli/diff/since-apply.js +255 -0
  45. package/dist/cli/diff/since-apply.js.map +1 -0
  46. package/dist/cli/doctor/format.d.ts +2 -0
  47. package/dist/cli/doctor/format.js +126 -0
  48. package/dist/cli/doctor/format.js.map +1 -0
  49. package/dist/cli/doctor/index.d.ts +14 -0
  50. package/dist/cli/doctor/index.js +281 -0
  51. package/dist/cli/doctor/index.js.map +1 -0
  52. package/dist/cli/doctor/types.d.ts +46 -0
  53. package/dist/cli/doctor/types.js +2 -0
  54. package/dist/cli/doctor/types.js.map +1 -0
  55. package/dist/cli/dry-run.d.ts +46 -0
  56. package/dist/cli/dry-run.js +168 -0
  57. package/dist/cli/dry-run.js.map +1 -0
  58. package/dist/cli/exit-codes.d.ts +10 -0
  59. package/dist/cli/exit-codes.js +15 -0
  60. package/dist/cli/exit-codes.js.map +1 -0
  61. package/dist/cli/explain.d.ts +14 -0
  62. package/dist/cli/explain.js +97 -0
  63. package/dist/cli/explain.js.map +1 -0
  64. package/dist/cli/export.d.ts +31 -0
  65. package/dist/cli/export.js +84 -0
  66. package/dist/cli/export.js.map +1 -0
  67. package/dist/cli/index.d.ts +8 -0
  68. package/dist/cli/index.js +549 -0
  69. package/dist/cli/index.js.map +1 -0
  70. package/dist/cli/init/index.d.ts +17 -0
  71. package/dist/cli/init/index.js +57 -0
  72. package/dist/cli/init/index.js.map +1 -0
  73. package/dist/cli/init/templates.d.ts +4 -0
  74. package/dist/cli/init/templates.js +175 -0
  75. package/dist/cli/init/templates.js.map +1 -0
  76. package/dist/cli/list.d.ts +12 -0
  77. package/dist/cli/list.js +118 -0
  78. package/dist/cli/list.js.map +1 -0
  79. package/dist/cli/loader.d.ts +24 -0
  80. package/dist/cli/loader.js +74 -0
  81. package/dist/cli/loader.js.map +1 -0
  82. package/dist/cli/main.d.ts +2 -0
  83. package/dist/cli/main.js +6 -0
  84. package/dist/cli/main.js.map +1 -0
  85. package/dist/cli/policy/intercept.d.ts +34 -0
  86. package/dist/cli/policy/intercept.js +172 -0
  87. package/dist/cli/policy/intercept.js.map +1 -0
  88. package/dist/cli/remove/index.d.ts +18 -0
  89. package/dist/cli/remove/index.js +95 -0
  90. package/dist/cli/remove/index.js.map +1 -0
  91. package/dist/cli/remove/mutate.d.ts +9 -0
  92. package/dist/cli/remove/mutate.js +68 -0
  93. package/dist/cli/remove/mutate.js.map +1 -0
  94. package/dist/cli/validate/checks.d.ts +23 -0
  95. package/dist/cli/validate/checks.js +253 -0
  96. package/dist/cli/validate/checks.js.map +1 -0
  97. package/dist/cli/validate/index.d.ts +18 -0
  98. package/dist/cli/validate/index.js +50 -0
  99. package/dist/cli/validate/index.js.map +1 -0
  100. package/dist/cli/validate/types.d.ts +7 -0
  101. package/dist/cli/validate/types.js +5 -0
  102. package/dist/cli/validate/types.js.map +1 -0
  103. package/dist/index.d.ts +15 -0
  104. package/dist/index.js +16 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/io/atomic-write.d.ts +8 -0
  107. package/dist/io/atomic-write.js +30 -0
  108. package/dist/io/atomic-write.js.map +1 -0
  109. package/dist/io/harness-lock.d.ts +33 -0
  110. package/dist/io/harness-lock.js +260 -0
  111. package/dist/io/harness-lock.js.map +1 -0
  112. package/dist/io/last-apply.d.ts +20 -0
  113. package/dist/io/last-apply.js +123 -0
  114. package/dist/io/last-apply.js.map +1 -0
  115. package/dist/io/lock.d.ts +11 -0
  116. package/dist/io/lock.js +33 -0
  117. package/dist/io/lock.js.map +1 -0
  118. package/dist/io/patch.d.ts +10 -0
  119. package/dist/io/patch.js +8 -0
  120. package/dist/io/patch.js.map +1 -0
  121. package/dist/io/restart-hints.d.ts +5 -0
  122. package/dist/io/restart-hints.js +59 -0
  123. package/dist/io/restart-hints.js.map +1 -0
  124. package/dist/io/three-state.d.ts +7 -0
  125. package/dist/io/three-state.js +20 -0
  126. package/dist/io/three-state.js.map +1 -0
  127. package/dist/io/validate-before-write.d.ts +12 -0
  128. package/dist/io/validate-before-write.js +23 -0
  129. package/dist/io/validate-before-write.js.map +1 -0
  130. package/dist/overrides/index.d.ts +2 -0
  131. package/dist/overrides/index.js +3 -0
  132. package/dist/overrides/index.js.map +1 -0
  133. package/dist/overrides/machines.d.ts +12 -0
  134. package/dist/overrides/machines.js +46 -0
  135. package/dist/overrides/machines.js.map +1 -0
  136. package/dist/overrides/merge.d.ts +6 -0
  137. package/dist/overrides/merge.js +173 -0
  138. package/dist/overrides/merge.js.map +1 -0
  139. package/dist/policies/duration.d.ts +5 -0
  140. package/dist/policies/duration.js +50 -0
  141. package/dist/policies/duration.js.map +1 -0
  142. package/dist/policies/extract.d.ts +50 -0
  143. package/dist/policies/extract.js +190 -0
  144. package/dist/policies/extract.js.map +1 -0
  145. package/dist/policies/index.d.ts +5 -0
  146. package/dist/policies/index.js +6 -0
  147. package/dist/policies/index.js.map +1 -0
  148. package/dist/policies/ledger-client.d.ts +39 -0
  149. package/dist/policies/ledger-client.js +378 -0
  150. package/dist/policies/ledger-client.js.map +1 -0
  151. package/dist/policies/requires.d.ts +44 -0
  152. package/dist/policies/requires.js +146 -0
  153. package/dist/policies/requires.js.map +1 -0
  154. package/dist/policies/timestamp.d.ts +14 -0
  155. package/dist/policies/timestamp.js +36 -0
  156. package/dist/policies/timestamp.js.map +1 -0
  157. package/dist/probes/mcp.d.ts +29 -0
  158. package/dist/probes/mcp.js +226 -0
  159. package/dist/probes/mcp.js.map +1 -0
  160. package/dist/probes/memory.d.ts +24 -0
  161. package/dist/probes/memory.js +89 -0
  162. package/dist/probes/memory.js.map +1 -0
  163. package/dist/runtime/index.d.ts +3 -0
  164. package/dist/runtime/index.js +4 -0
  165. package/dist/runtime/index.js.map +1 -0
  166. package/dist/runtime/intercept.d.ts +53 -0
  167. package/dist/runtime/intercept.js +181 -0
  168. package/dist/runtime/intercept.js.map +1 -0
  169. package/dist/runtime/ledger-record.d.ts +43 -0
  170. package/dist/runtime/ledger-record.js +239 -0
  171. package/dist/runtime/ledger-record.js.map +1 -0
  172. package/dist/runtime/session-id.d.ts +10 -0
  173. package/dist/runtime/session-id.js +37 -0
  174. package/dist/runtime/session-id.js.map +1 -0
  175. package/dist/schema/extract.d.ts +5 -0
  176. package/dist/schema/extract.js +23 -0
  177. package/dist/schema/extract.js.map +1 -0
  178. package/dist/schema/grounding.d.ts +65 -0
  179. package/dist/schema/grounding.js +21 -0
  180. package/dist/schema/grounding.js.map +1 -0
  181. package/dist/schema/hooks.d.ts +86 -0
  182. package/dist/schema/hooks.js +42 -0
  183. package/dist/schema/hooks.js.map +1 -0
  184. package/dist/schema/index.d.ts +961 -0
  185. package/dist/schema/index.js +55 -0
  186. package/dist/schema/index.js.map +1 -0
  187. package/dist/schema/memory.d.ts +131 -0
  188. package/dist/schema/memory.js +38 -0
  189. package/dist/schema/memory.js.map +1 -0
  190. package/dist/schema/policies.d.ts +412 -0
  191. package/dist/schema/policies.js +53 -0
  192. package/dist/schema/policies.js.map +1 -0
  193. package/dist/schema/requires.d.ts +115 -0
  194. package/dist/schema/requires.js +57 -0
  195. package/dist/schema/requires.js.map +1 -0
  196. package/dist/schema/tools.d.ts +283 -0
  197. package/dist/schema/tools.js +66 -0
  198. package/dist/schema/tools.js.map +1 -0
  199. package/package.json +63 -0
@@ -0,0 +1,378 @@
1
+ // Phase 4 #3 — grounding-mcp client adapter.
2
+ //
3
+ // Spawns the configured grounding-mcp server, performs the standard
4
+ // init / notifications/initialized / tools/call handshake, and returns
5
+ // ledger entries normalised to harness's LedgerEntry shape (see
6
+ // src/policies/requires.ts). Any failure path resolves to
7
+ // `{ kind: "degraded", reason }` so the policy evaluator can fall back to
8
+ // warn-mode per ROADMAP §"Phase 4 — Policy layer" acceptance: "When the
9
+ // evidence ledger is unreachable, policy evaluation defaults to
10
+ // enforcement: warn-equivalent behaviour".
11
+ import { spawn } from "node:child_process";
12
+ const DEFAULT_TIMEOUT_MS = 5_000;
13
+ function expandHomePath(p) {
14
+ if (p === "~")
15
+ return process.env.HOME ?? "";
16
+ if (p.startsWith("~/"))
17
+ return `${process.env.HOME ?? ""}/${p.slice(2)}`;
18
+ return p;
19
+ }
20
+ function normaliseEntry(raw, bucketType) {
21
+ if (raw.id === undefined || raw.id === null)
22
+ return null;
23
+ if (typeof raw.content !== "string")
24
+ return null;
25
+ const createdAt = typeof raw.createdAt === "string"
26
+ ? raw.createdAt
27
+ : typeof raw.created_at === "string"
28
+ ? raw.created_at
29
+ : undefined;
30
+ if (createdAt === undefined)
31
+ return null;
32
+ // Prefer the bucket-derived type (Phase 5 #4 — bucket name is the
33
+ // canonical type signal); fall back to the row's own `type` field
34
+ // if the wire payload happens to carry it.
35
+ const type = bucketType ?? (typeof raw.type === "string" ? raw.type : undefined);
36
+ return {
37
+ id: String(raw.id),
38
+ content: raw.content,
39
+ source: typeof raw.source === "string" ? raw.source : undefined,
40
+ createdAt,
41
+ ...(type !== undefined && { type }),
42
+ };
43
+ }
44
+ /**
45
+ * Flatten the bucketed arrays returned by `ledger_summary` into one list.
46
+ * Tolerates both snake_case (`created_at`) and camelCase (`createdAt`) since
47
+ * the MCP wire format is JSON.stringify of whatever the underlying lib emits.
48
+ *
49
+ * Phase 5 #4 — surfaces the wire bucket as `LedgerEntry.type`
50
+ * (`facts` → `fact`, `policyDecisions` → `policy_decision`, etc.) so
51
+ * downstream filters can distinguish evidence from audit records.
52
+ * `flattenSummary` returns ALL buckets; consumers filter by type.
53
+ *
54
+ * Returns `null` when the payload doesn't carry an `entries` object — the
55
+ * caller treats that as a contract-drift degraded path.
56
+ */
57
+ const BUCKET_TO_TYPE = {
58
+ facts: "fact",
59
+ hypotheses: "hypothesis",
60
+ rejected: "rejected",
61
+ unknowns: "unknown",
62
+ policyDecisions: "policy_decision",
63
+ };
64
+ function flattenSummary(payload) {
65
+ if (!payload || typeof payload !== "object")
66
+ return null;
67
+ const root = payload;
68
+ const entries = root.entries;
69
+ if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
70
+ return null;
71
+ }
72
+ const buckets = entries;
73
+ const out = [];
74
+ for (const [bucketKey, type] of Object.entries(BUCKET_TO_TYPE)) {
75
+ const arr = buckets[bucketKey];
76
+ if (!Array.isArray(arr))
77
+ continue;
78
+ for (const raw of arr) {
79
+ const norm = normaliseEntry(raw, type);
80
+ if (norm)
81
+ out.push(norm);
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+ function startSubprocess(opts) {
87
+ const list = opts.mcpCommand;
88
+ if (!list || list.length === 0) {
89
+ return { ok: false, reason: "grounding-mcp command is empty" };
90
+ }
91
+ const exe = expandHomePath(list[0]);
92
+ const args = list.slice(1).map(expandHomePath);
93
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
94
+ let child;
95
+ try {
96
+ child = spawn(exe, args, {
97
+ cwd: opts.cwd,
98
+ env: { ...process.env, ...(opts.mcpEnv ?? {}) },
99
+ stdio: ["pipe", "pipe", "pipe"],
100
+ });
101
+ }
102
+ catch (err) {
103
+ return {
104
+ ok: false,
105
+ reason: `grounding-mcp spawn failed: ${err.message}`,
106
+ };
107
+ }
108
+ const stderrBuf = { value: "" };
109
+ let stdoutBuf = "";
110
+ let processExited = false;
111
+ let exitCode = null;
112
+ let exitSignal = null;
113
+ const pending = new Map();
114
+ const timers = new Set();
115
+ child.stderr.on("data", (chunk) => {
116
+ stderrBuf.value += chunk.toString("utf8");
117
+ });
118
+ child.stdin.on("error", () => {
119
+ /* EPIPE on shutdown; surfaced via the exit listener. */
120
+ });
121
+ let spawnError = null;
122
+ child.on("error", (err) => {
123
+ spawnError = err;
124
+ processExited = true;
125
+ });
126
+ child.on("exit", (code, signal) => {
127
+ processExited = true;
128
+ exitCode = code;
129
+ exitSignal = signal;
130
+ });
131
+ child.stdout.on("data", (chunk) => {
132
+ stdoutBuf += chunk.toString("utf8");
133
+ let nl = stdoutBuf.indexOf("\n");
134
+ while (nl !== -1) {
135
+ const line = stdoutBuf.slice(0, nl).trim();
136
+ stdoutBuf = stdoutBuf.slice(nl + 1);
137
+ if (line) {
138
+ try {
139
+ const msg = JSON.parse(line);
140
+ if (typeof msg.id === "number") {
141
+ const handler = pending.get(msg.id);
142
+ if (handler) {
143
+ pending.delete(msg.id);
144
+ handler.resolve(msg);
145
+ }
146
+ }
147
+ }
148
+ catch {
149
+ /* ignore non-JSON lines */
150
+ }
151
+ }
152
+ nl = stdoutBuf.indexOf("\n");
153
+ }
154
+ });
155
+ const exitPromise = new Promise((resolve) => {
156
+ if (processExited) {
157
+ resolve("exit");
158
+ return;
159
+ }
160
+ const done = () => resolve("exit");
161
+ child.once("exit", done);
162
+ child.once("error", done);
163
+ });
164
+ function timeoutPromise() {
165
+ return new Promise((resolve) => {
166
+ const t = setTimeout(() => {
167
+ timers.delete(t);
168
+ resolve("timeout");
169
+ }, timeoutMs);
170
+ t.unref();
171
+ timers.add(t);
172
+ });
173
+ }
174
+ function send(id, method, params) {
175
+ return new Promise((resolve) => {
176
+ pending.set(id, { id, resolve });
177
+ try {
178
+ if (!processExited && !child.stdin.destroyed && child.stdin.writable) {
179
+ child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);
180
+ }
181
+ }
182
+ catch {
183
+ /* server closed stdin; the exit-promise race resolves the call */
184
+ }
185
+ });
186
+ }
187
+ function call(id, method, params) {
188
+ return Promise.race([send(id, method, params), exitPromise, timeoutPromise()]);
189
+ }
190
+ function notify(method, params = {}) {
191
+ try {
192
+ if (!processExited && !child.stdin.destroyed && child.stdin.writable) {
193
+ child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}\n`);
194
+ }
195
+ }
196
+ catch {
197
+ /* best effort */
198
+ }
199
+ }
200
+ function cleanup() {
201
+ for (const t of timers)
202
+ clearTimeout(t);
203
+ timers.clear();
204
+ if (!processExited)
205
+ child.kill("SIGTERM");
206
+ }
207
+ return {
208
+ ok: true,
209
+ ctl: {
210
+ child,
211
+ stderrBuf,
212
+ pending,
213
+ call,
214
+ notify,
215
+ exited: () => processExited,
216
+ exitCode: () => exitCode,
217
+ exitSignal: () => exitSignal,
218
+ spawnError: () => spawnError,
219
+ cleanup,
220
+ },
221
+ };
222
+ }
223
+ /**
224
+ * Phase 5 #5 — read the inputSchema for `ledger_summary` from a
225
+ * `tools/list` response and project the set of advertised property
226
+ * names (which are the args harness can send without tripping a zod
227
+ * rejection on an older server).
228
+ *
229
+ * Defensive: any unexpected shape (timeout, exit, non-array, missing
230
+ * inputSchema) returns the conservative `{ "sessionId" }` set so the
231
+ * caller falls back to client-side filtering instead of speculatively
232
+ * including args that an old server would reject.
233
+ */
234
+ async function detectLedgerSummaryArgs(ctl, _timeoutMs) {
235
+ const fallback = new Set(["sessionId"]);
236
+ const result = await ctl.call(2, "tools/list", {});
237
+ if (result === "exit" || result === "timeout")
238
+ return fallback;
239
+ if (result.error)
240
+ return fallback;
241
+ const r = result.result;
242
+ if (!r || !Array.isArray(r.tools))
243
+ return fallback;
244
+ for (const tool of r.tools) {
245
+ const t = tool;
246
+ if (t.name !== "ledger_summary")
247
+ continue;
248
+ const schema = t.inputSchema;
249
+ if (!schema || !schema.properties || typeof schema.properties !== "object") {
250
+ return fallback;
251
+ }
252
+ return new Set(Object.keys(schema.properties));
253
+ }
254
+ return fallback;
255
+ }
256
+ function exitDiagnostic(ctl) {
257
+ const err = ctl.spawnError();
258
+ if (err)
259
+ return `spawn failed: ${err.message}`;
260
+ const tail = ctl.stderrBuf.value.trim().split("\n").pop()?.trim() || "(no stderr)";
261
+ const code = ctl.exitCode();
262
+ const sig = ctl.exitSignal();
263
+ const status = code !== null ? `exit ${code}` : sig ? `signal ${sig}` : "exited";
264
+ return `${status}: ${tail}`;
265
+ }
266
+ function extractToolPayload(result) {
267
+ if (!result || typeof result !== "object")
268
+ return null;
269
+ const r = result;
270
+ if (r.structuredContent && typeof r.structuredContent === "object") {
271
+ return r.structuredContent;
272
+ }
273
+ if (Array.isArray(r.content)) {
274
+ for (const block of r.content) {
275
+ const b = block;
276
+ if (b.type === "text" && typeof b.text === "string") {
277
+ try {
278
+ return JSON.parse(b.text);
279
+ }
280
+ catch {
281
+ /* not JSON; fall through */
282
+ }
283
+ }
284
+ }
285
+ }
286
+ return null;
287
+ }
288
+ export async function queryLedgerByTag(opts) {
289
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
290
+ const start = startSubprocess(opts);
291
+ if (!start.ok)
292
+ return { kind: "degraded", reason: start.reason };
293
+ const ctl = start.ctl;
294
+ try {
295
+ const initResult = await ctl.call(1, "initialize", {
296
+ protocolVersion: "2024-11-05",
297
+ capabilities: {},
298
+ clientInfo: { name: "harness-policies", version: "0.4.0" },
299
+ });
300
+ if (initResult === "exit") {
301
+ return {
302
+ kind: "degraded",
303
+ reason: `grounding-mcp ${exitDiagnostic(ctl)}`,
304
+ };
305
+ }
306
+ if (initResult === "timeout") {
307
+ return {
308
+ kind: "degraded",
309
+ reason: `grounding-mcp timeout after ${timeoutMs}ms`,
310
+ };
311
+ }
312
+ if (initResult.error) {
313
+ return {
314
+ kind: "degraded",
315
+ reason: `grounding-mcp initialize error: ${initResult.error.message ?? "unknown"}`,
316
+ };
317
+ }
318
+ ctl.notify("notifications/initialized");
319
+ // Phase 5 #5 — capability detection only when we actually want to
320
+ // push a filter server-side. Older grounding-mcp builds accept only
321
+ // `sessionId` on `ledger_summary`; harness spawns a binary path
322
+ // chosen by the operator, so we cannot assume a version. Skipping
323
+ // tools/list when no filter is requested keeps the unfiltered hot
324
+ // path one round-trip fewer.
325
+ const wantsFilter = opts.sinceIso !== undefined || opts.contentPrefix !== undefined;
326
+ const supportedArgs = wantsFilter
327
+ ? await detectLedgerSummaryArgs(ctl, timeoutMs)
328
+ : new Set(["sessionId"]);
329
+ const callArgs = { sessionId: opts.sessionId };
330
+ if (opts.sinceIso !== undefined && supportedArgs.has("sinceIso")) {
331
+ callArgs.sinceIso = opts.sinceIso;
332
+ }
333
+ if (opts.contentPrefix !== undefined && supportedArgs.has("contentPrefix")) {
334
+ callArgs.contentPrefix = opts.contentPrefix;
335
+ }
336
+ const callResult = await ctl.call(3, "tools/call", {
337
+ name: "ledger_summary",
338
+ arguments: callArgs,
339
+ });
340
+ if (callResult === "exit") {
341
+ return {
342
+ kind: "degraded",
343
+ reason: `grounding-mcp ${exitDiagnostic(ctl)}`,
344
+ };
345
+ }
346
+ if (callResult === "timeout") {
347
+ return {
348
+ kind: "degraded",
349
+ reason: `grounding-mcp timeout after ${timeoutMs}ms`,
350
+ };
351
+ }
352
+ if (callResult.error) {
353
+ return {
354
+ kind: "degraded",
355
+ reason: `ledger_summary error: ${callResult.error.message ?? "unknown"}`,
356
+ };
357
+ }
358
+ const payload = extractToolPayload(callResult.result);
359
+ if (payload === null) {
360
+ return {
361
+ kind: "degraded",
362
+ reason: "ledger_summary returned no parseable payload",
363
+ };
364
+ }
365
+ const entries = flattenSummary(payload);
366
+ if (entries === null) {
367
+ return {
368
+ kind: "degraded",
369
+ reason: "ledger_summary payload missing `entries` shape",
370
+ };
371
+ }
372
+ return { kind: "ok", entries };
373
+ }
374
+ finally {
375
+ ctl.cleanup();
376
+ }
377
+ }
378
+ //# sourceMappingURL=ledger-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-client.js","sourceRoot":"","sources":["../../src/policies/ledger-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { Requires } from "../schema/requires.js";
2
+ export interface LedgerEntry {
3
+ id: string;
4
+ content: string;
5
+ source?: string;
6
+ createdAt: string | Date;
7
+ /**
8
+ * Phase 5 #4 — the wire bucket the entry came from. `flattenSummary`
9
+ * populates this from the `entries.<bucket>` key in the ledger_summary
10
+ * payload, mapping bucket names back to evidence-ledger types
11
+ * (`facts` → `fact`, `policyDecisions` → `policy_decision`, etc.).
12
+ *
13
+ * The requires evaluator uses this to skip `policy_decision` rows so
14
+ * past audit-log entries don't substring-match the tag they're about
15
+ * (the substring-pollution bug from PR #39's dogfood).
16
+ */
17
+ type?: string;
18
+ }
19
+ export interface RequiresEvaluation {
20
+ allowed: boolean;
21
+ reason: string;
22
+ matchedCount: number;
23
+ traceData: RequiresTrace;
24
+ }
25
+ export interface RequiresTrace {
26
+ ledgerTag: string;
27
+ windowSeconds: number | null;
28
+ totalEntries: number;
29
+ matchedEntryIds: string[];
30
+ countBound: {
31
+ min?: number;
32
+ max?: number;
33
+ exact?: number;
34
+ } | null;
35
+ evaluatedAt: string;
36
+ }
37
+ export interface EvaluateRequiresOptions {
38
+ /** Override "now" for deterministic testing. */
39
+ now?: Date;
40
+ }
41
+ export declare class RequiresEvaluationError extends Error {
42
+ constructor(message: string);
43
+ }
44
+ export declare function evaluateRequires(requires: Requires, ledgerEntries: LedgerEntry[], options?: EvaluateRequiresOptions): RequiresEvaluation;
@@ -0,0 +1,146 @@
1
+ import { InvalidDurationError, parseDurationSeconds } from "./duration.js";
2
+ import { parseLedgerTimestamp } from "./timestamp.js";
3
+ export class RequiresEvaluationError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "RequiresEvaluationError";
7
+ }
8
+ }
9
+ // Matches against `content` and `source` per ARCHITECTURE.md §6
10
+ // ("substring/regex match against ledger entries' content/source columns").
11
+ // v1 is substring-only; regex is a v2 deferral.
12
+ function entryMatches(entry, tag) {
13
+ // Phase 5 #4 — `policy_decision` rows are audit records, not
14
+ // evidence. Their serialised payload incidentally contains the tag
15
+ // they're about ("ledgerTag":"review:42"), which under the old
16
+ // substring filter inflated matchedCount on the same tag the
17
+ // decision was about. Skip them at the matcher.
18
+ if (entry.type === "policy_decision")
19
+ return false;
20
+ // Legacy backstop: pre-Phase-5-#4 audit rows were written with
21
+ // type='fact' + a `policy_decision:` content prefix. They land in
22
+ // the `facts` bucket and would otherwise sneak past the type guard.
23
+ // The content-prefix check is exact and cheap, so a user upgrading
24
+ // harness without flushing their dev ledger doesn't keep paying the
25
+ // pollution tax until the rows age out.
26
+ if (entry.content.startsWith("policy_decision:"))
27
+ return false;
28
+ if (entry.content.includes(tag))
29
+ return true;
30
+ if (entry.source && entry.source.includes(tag))
31
+ return true;
32
+ return false;
33
+ }
34
+ function entryTime(entry) {
35
+ const v = entry.createdAt;
36
+ const ms = v instanceof Date ? v.getTime() : parseLedgerTimestamp(v);
37
+ if (Number.isNaN(ms)) {
38
+ throw new RequiresEvaluationError(`ledger entry ${entry.id} has unparseable createdAt: ${String(v)}`);
39
+ }
40
+ return ms;
41
+ }
42
+ function describeBound(c) {
43
+ if (c.exact !== undefined)
44
+ return String(c.exact);
45
+ if (c.min !== undefined && c.max !== undefined)
46
+ return `${c.min}..${c.max}`;
47
+ if (c.min !== undefined)
48
+ return String(c.min);
49
+ if (c.max !== undefined)
50
+ return `≤${c.max}`;
51
+ return "?";
52
+ }
53
+ export function evaluateRequires(requires, ledgerEntries, options = {}) {
54
+ const now = options.now ?? new Date();
55
+ const evaluatedAt = now.toISOString();
56
+ const tag = requires.ledger_tag;
57
+ let windowSeconds = null;
58
+ if (requires.within !== undefined) {
59
+ try {
60
+ windowSeconds = parseDurationSeconds(requires.within);
61
+ }
62
+ catch (err) {
63
+ if (err instanceof InvalidDurationError) {
64
+ throw new RequiresEvaluationError(err.message);
65
+ }
66
+ throw err;
67
+ }
68
+ }
69
+ if (requires.count?.min !== undefined && requires.count.min === 0) {
70
+ throw new RequiresEvaluationError("requires.count.min must be > 0 (count.min:0 is a no-op)");
71
+ }
72
+ const tagMatched = ledgerEntries.filter((e) => entryMatches(e, tag));
73
+ let windowMatched = tagMatched;
74
+ if (windowSeconds !== null) {
75
+ const cutoff = now.getTime() - windowSeconds * 1000;
76
+ windowMatched = tagMatched.filter((e) => entryTime(e) >= cutoff);
77
+ }
78
+ const matchedCount = windowMatched.length;
79
+ const matchedEntryIds = windowMatched.map((e) => e.id);
80
+ const countBound = requires.count
81
+ ? {
82
+ ...(requires.count.min !== undefined && { min: requires.count.min }),
83
+ ...(requires.count.max !== undefined && { max: requires.count.max }),
84
+ ...(requires.count.exact !== undefined && { exact: requires.count.exact }),
85
+ }
86
+ : null;
87
+ const trace = {
88
+ ledgerTag: tag,
89
+ windowSeconds,
90
+ totalEntries: ledgerEntries.length,
91
+ matchedEntryIds,
92
+ countBound,
93
+ evaluatedAt,
94
+ };
95
+ if (requires.count !== undefined) {
96
+ const c = requires.count;
97
+ const failsMin = c.min !== undefined && matchedCount < c.min;
98
+ const failsMax = c.max !== undefined && matchedCount > c.max;
99
+ const failsExact = c.exact !== undefined && matchedCount !== c.exact;
100
+ if (failsMin || failsMax || failsExact) {
101
+ let reason;
102
+ if (failsMax) {
103
+ reason = `${matchedCount} matching entries exceeds count.max ${c.max}`;
104
+ }
105
+ else {
106
+ const required = failsExact ? c.exact : c.min;
107
+ reason = `${matchedCount} of required ${required} entries found`;
108
+ }
109
+ return {
110
+ allowed: false,
111
+ reason,
112
+ matchedCount,
113
+ traceData: trace,
114
+ };
115
+ }
116
+ return {
117
+ allowed: true,
118
+ reason: `${matchedCount} entries matched (count bound: ${describeBound(c)})`,
119
+ matchedCount,
120
+ traceData: trace,
121
+ };
122
+ }
123
+ if (matchedCount === 0) {
124
+ if (windowSeconds !== null && tagMatched.length > 0) {
125
+ return {
126
+ allowed: false,
127
+ reason: `no matching entry within ${requires.within}`,
128
+ matchedCount,
129
+ traceData: trace,
130
+ };
131
+ }
132
+ return {
133
+ allowed: false,
134
+ reason: `no matching ledger entry for tag \`${tag}\``,
135
+ matchedCount,
136
+ traceData: trace,
137
+ };
138
+ }
139
+ return {
140
+ allowed: true,
141
+ reason: `${matchedCount} matching ledger entr${matchedCount === 1 ? "y" : "ies"} for tag \`${tag}\``,
142
+ matchedCount,
143
+ traceData: trace,
144
+ };
145
+ }
146
+ //# sourceMappingURL=requires.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requires.js","sourceRoot":"","sources":["../../src/policies/requires.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalise a ledger `createdAt` string to an epoch-millis value.
3
+ *
4
+ * Accepted shapes:
5
+ * - `YYYY-MM-DD HH:MM:SS` (SQLite `datetime('now')`, UTC)
6
+ * - `YYYY-MM-DD HH:MM:SS.sss` (SQLite with subsecond precision)
7
+ * - `YYYY-MM-DDTHH:MM:SS[.sss]` (ISO-8601 without zone) — treated as UTC
8
+ * - `YYYY-MM-DDTHH:MM:SS[.sss]Z` (already-UTC ISO) — passed through
9
+ * - `YYYY-MM-DDTHH:MM:SS[.sss]±HH:MM` (zoned ISO) — passed through
10
+ *
11
+ * Returns `NaN` when the input does not match any of these. Callers that
12
+ * want a hard failure should check `Number.isNaN`.
13
+ */
14
+ export declare function parseLedgerTimestamp(raw: string): number;
@@ -0,0 +1,36 @@
1
+ // Phase 5 #8 — shared ledger-timestamp parser.
2
+ //
3
+ // `evidence-ledger`'s `created_at` column is populated by SQLite's
4
+ // `datetime('now')`, which writes UTC text in `YYYY-MM-DD HH:MM:SS`
5
+ // format (no `T`, no trailing `Z`). `Date.parse` of a space-separated
6
+ // timestamp without an explicit zone is implementation-defined; modern
7
+ // V8 interprets it as local time, so on any non-UTC host the parsed
8
+ // epoch lands `(host TZ offset)` hours away from the actual UTC moment.
9
+ //
10
+ // This helper normalises the input before delegating to `Date.parse` so
11
+ // every caller sees a UTC-correct epoch regardless of `TZ`.
12
+ /**
13
+ * Normalise a ledger `createdAt` string to an epoch-millis value.
14
+ *
15
+ * Accepted shapes:
16
+ * - `YYYY-MM-DD HH:MM:SS` (SQLite `datetime('now')`, UTC)
17
+ * - `YYYY-MM-DD HH:MM:SS.sss` (SQLite with subsecond precision)
18
+ * - `YYYY-MM-DDTHH:MM:SS[.sss]` (ISO-8601 without zone) — treated as UTC
19
+ * - `YYYY-MM-DDTHH:MM:SS[.sss]Z` (already-UTC ISO) — passed through
20
+ * - `YYYY-MM-DDTHH:MM:SS[.sss]±HH:MM` (zoned ISO) — passed through
21
+ *
22
+ * Returns `NaN` when the input does not match any of these. Callers that
23
+ * want a hard failure should check `Number.isNaN`.
24
+ */
25
+ export function parseLedgerTimestamp(raw) {
26
+ if (typeof raw !== "string" || raw.length === 0)
27
+ return NaN;
28
+ // Already-zoned (ends with Z or ±HH:MM after the time portion).
29
+ if (/[Zz]$/.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
30
+ return Date.parse(raw);
31
+ }
32
+ // Space-separated SQL DATETIME → coerce to ISO-with-Z.
33
+ const iso = raw.includes("T") ? `${raw}Z` : `${raw.replace(" ", "T")}Z`;
34
+ return Date.parse(iso);
35
+ }
36
+ //# sourceMappingURL=timestamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp.js","sourceRoot":"","sources":["../../src/policies/timestamp.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,sEAAsE;AACtE,uEAAuE;AACvE,oEAAoE;AACpE,wEAAwE;AACxE,EAAE;AACF,wEAAwE;AACxE,4DAA4D;AAE5D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5D,gEAAgE;IAChE,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,uDAAuD;IACvD,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC;IACxE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}