@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.
- package/CHANGELOG.md +552 -0
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/dist/cli/add/index.d.ts +14 -0
- package/dist/cli/add/index.js +71 -0
- package/dist/cli/add/index.js.map +1 -0
- package/dist/cli/add/mutate.d.ts +39 -0
- package/dist/cli/add/mutate.js +36 -0
- package/dist/cli/add/mutate.js.map +1 -0
- package/dist/cli/adopt/derive.d.ts +38 -0
- package/dist/cli/adopt/derive.js +94 -0
- package/dist/cli/adopt/derive.js.map +1 -0
- package/dist/cli/adopt/index.d.ts +20 -0
- package/dist/cli/adopt/index.js +156 -0
- package/dist/cli/adopt/index.js.map +1 -0
- package/dist/cli/apply/apply.d.ts +49 -0
- package/dist/cli/apply/apply.js +333 -0
- package/dist/cli/apply/apply.js.map +1 -0
- package/dist/cli/apply/generate-memory-index.d.ts +17 -0
- package/dist/cli/apply/generate-memory-index.js +167 -0
- package/dist/cli/apply/generate-memory-index.js.map +1 -0
- package/dist/cli/apply/generate-settings.d.ts +15 -0
- package/dist/cli/apply/generate-settings.js +87 -0
- package/dist/cli/apply/generate-settings.js.map +1 -0
- package/dist/cli/apply/index.d.ts +1 -0
- package/dist/cli/apply/index.js +2 -0
- package/dist/cli/apply/index.js.map +1 -0
- package/dist/cli/audit.d.ts +36 -0
- package/dist/cli/audit.js +121 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/describe.d.ts +13 -0
- package/dist/cli/describe.js +26 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/diff/engine.d.ts +21 -0
- package/dist/cli/diff/engine.js +161 -0
- package/dist/cli/diff/engine.js.map +1 -0
- package/dist/cli/diff/git.d.ts +6 -0
- package/dist/cli/diff/git.js +32 -0
- package/dist/cli/diff/git.js.map +1 -0
- package/dist/cli/diff/index.d.ts +15 -0
- package/dist/cli/diff/index.js +39 -0
- package/dist/cli/diff/index.js.map +1 -0
- package/dist/cli/diff/since-apply.d.ts +57 -0
- package/dist/cli/diff/since-apply.js +255 -0
- package/dist/cli/diff/since-apply.js.map +1 -0
- package/dist/cli/doctor/format.d.ts +2 -0
- package/dist/cli/doctor/format.js +126 -0
- package/dist/cli/doctor/format.js.map +1 -0
- package/dist/cli/doctor/index.d.ts +14 -0
- package/dist/cli/doctor/index.js +281 -0
- package/dist/cli/doctor/index.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +46 -0
- package/dist/cli/doctor/types.js +2 -0
- package/dist/cli/doctor/types.js.map +1 -0
- package/dist/cli/dry-run.d.ts +46 -0
- package/dist/cli/dry-run.js +168 -0
- package/dist/cli/dry-run.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +10 -0
- package/dist/cli/exit-codes.js +15 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/cli/explain.d.ts +14 -0
- package/dist/cli/explain.js +97 -0
- package/dist/cli/explain.js.map +1 -0
- package/dist/cli/export.d.ts +31 -0
- package/dist/cli/export.js +84 -0
- package/dist/cli/export.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +549 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init/index.d.ts +17 -0
- package/dist/cli/init/index.js +57 -0
- package/dist/cli/init/index.js.map +1 -0
- package/dist/cli/init/templates.d.ts +4 -0
- package/dist/cli/init/templates.js +175 -0
- package/dist/cli/init/templates.js.map +1 -0
- package/dist/cli/list.d.ts +12 -0
- package/dist/cli/list.js +118 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/loader.d.ts +24 -0
- package/dist/cli/loader.js +74 -0
- package/dist/cli/loader.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +6 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/policy/intercept.d.ts +34 -0
- package/dist/cli/policy/intercept.js +172 -0
- package/dist/cli/policy/intercept.js.map +1 -0
- package/dist/cli/remove/index.d.ts +18 -0
- package/dist/cli/remove/index.js +95 -0
- package/dist/cli/remove/index.js.map +1 -0
- package/dist/cli/remove/mutate.d.ts +9 -0
- package/dist/cli/remove/mutate.js +68 -0
- package/dist/cli/remove/mutate.js.map +1 -0
- package/dist/cli/validate/checks.d.ts +23 -0
- package/dist/cli/validate/checks.js +253 -0
- package/dist/cli/validate/checks.js.map +1 -0
- package/dist/cli/validate/index.d.ts +18 -0
- package/dist/cli/validate/index.js +50 -0
- package/dist/cli/validate/index.js.map +1 -0
- package/dist/cli/validate/types.d.ts +7 -0
- package/dist/cli/validate/types.js +5 -0
- package/dist/cli/validate/types.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/io/atomic-write.d.ts +8 -0
- package/dist/io/atomic-write.js +30 -0
- package/dist/io/atomic-write.js.map +1 -0
- package/dist/io/harness-lock.d.ts +33 -0
- package/dist/io/harness-lock.js +260 -0
- package/dist/io/harness-lock.js.map +1 -0
- package/dist/io/last-apply.d.ts +20 -0
- package/dist/io/last-apply.js +123 -0
- package/dist/io/last-apply.js.map +1 -0
- package/dist/io/lock.d.ts +11 -0
- package/dist/io/lock.js +33 -0
- package/dist/io/lock.js.map +1 -0
- package/dist/io/patch.d.ts +10 -0
- package/dist/io/patch.js +8 -0
- package/dist/io/patch.js.map +1 -0
- package/dist/io/restart-hints.d.ts +5 -0
- package/dist/io/restart-hints.js +59 -0
- package/dist/io/restart-hints.js.map +1 -0
- package/dist/io/three-state.d.ts +7 -0
- package/dist/io/three-state.js +20 -0
- package/dist/io/three-state.js.map +1 -0
- package/dist/io/validate-before-write.d.ts +12 -0
- package/dist/io/validate-before-write.js +23 -0
- package/dist/io/validate-before-write.js.map +1 -0
- package/dist/overrides/index.d.ts +2 -0
- package/dist/overrides/index.js +3 -0
- package/dist/overrides/index.js.map +1 -0
- package/dist/overrides/machines.d.ts +12 -0
- package/dist/overrides/machines.js +46 -0
- package/dist/overrides/machines.js.map +1 -0
- package/dist/overrides/merge.d.ts +6 -0
- package/dist/overrides/merge.js +173 -0
- package/dist/overrides/merge.js.map +1 -0
- package/dist/policies/duration.d.ts +5 -0
- package/dist/policies/duration.js +50 -0
- package/dist/policies/duration.js.map +1 -0
- package/dist/policies/extract.d.ts +50 -0
- package/dist/policies/extract.js +190 -0
- package/dist/policies/extract.js.map +1 -0
- package/dist/policies/index.d.ts +5 -0
- package/dist/policies/index.js +6 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/policies/ledger-client.d.ts +39 -0
- package/dist/policies/ledger-client.js +378 -0
- package/dist/policies/ledger-client.js.map +1 -0
- package/dist/policies/requires.d.ts +44 -0
- package/dist/policies/requires.js +146 -0
- package/dist/policies/requires.js.map +1 -0
- package/dist/policies/timestamp.d.ts +14 -0
- package/dist/policies/timestamp.js +36 -0
- package/dist/policies/timestamp.js.map +1 -0
- package/dist/probes/mcp.d.ts +29 -0
- package/dist/probes/mcp.js +226 -0
- package/dist/probes/mcp.js.map +1 -0
- package/dist/probes/memory.d.ts +24 -0
- package/dist/probes/memory.js +89 -0
- package/dist/probes/memory.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/intercept.d.ts +53 -0
- package/dist/runtime/intercept.js +181 -0
- package/dist/runtime/intercept.js.map +1 -0
- package/dist/runtime/ledger-record.d.ts +43 -0
- package/dist/runtime/ledger-record.js +239 -0
- package/dist/runtime/ledger-record.js.map +1 -0
- package/dist/runtime/session-id.d.ts +10 -0
- package/dist/runtime/session-id.js +37 -0
- package/dist/runtime/session-id.js.map +1 -0
- package/dist/schema/extract.d.ts +5 -0
- package/dist/schema/extract.js +23 -0
- package/dist/schema/extract.js.map +1 -0
- package/dist/schema/grounding.d.ts +65 -0
- package/dist/schema/grounding.js +21 -0
- package/dist/schema/grounding.js.map +1 -0
- package/dist/schema/hooks.d.ts +86 -0
- package/dist/schema/hooks.js +42 -0
- package/dist/schema/hooks.js.map +1 -0
- package/dist/schema/index.d.ts +961 -0
- package/dist/schema/index.js +55 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/memory.d.ts +131 -0
- package/dist/schema/memory.js +38 -0
- package/dist/schema/memory.js.map +1 -0
- package/dist/schema/policies.d.ts +412 -0
- package/dist/schema/policies.js +53 -0
- package/dist/schema/policies.js.map +1 -0
- package/dist/schema/requires.d.ts +115 -0
- package/dist/schema/requires.js +57 -0
- package/dist/schema/requires.js.map +1 -0
- package/dist/schema/tools.d.ts +283 -0
- package/dist/schema/tools.js +66 -0
- package/dist/schema/tools.js.map +1 -0
- 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":"AACA,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAyCtD,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,gEAAgE;AAChE,4EAA4E;AAC5E,gDAAgD;AAChD,SAAS,YAAY,CAAC,KAAkB,EAAE,GAAW;IACnD,6DAA6D;IAC7D,mEAAmE;IACnE,+DAA+D;IAC/D,6DAA6D;IAC7D,gDAAgD;IAChD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO,KAAK,CAAC;IACnD,+DAA+D;IAC/D,kEAAkE;IAClE,oEAAoE;IACpE,mEAAmE;IACnE,oEAAoE;IACpE,wCAAwC;IACxC,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,KAAkB;IACnC,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACrE,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,uBAAuB,CAC/B,gBAAgB,KAAK,CAAC,EAAE,+BAA+B,MAAM,CAAC,CAAC,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,aAAa,CAAC,CAAiC;IACtD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC;IAC5E,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;IAC5C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,aAA4B,EAC5B,UAAmC,EAAE;IAErC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC;IAEhC,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,oBAAoB,EAAE,CAAC;gBACxC,MAAM,IAAI,uBAAuB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACjD,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,uBAAuB,CAC/B,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAErE,IAAI,aAAa,GAAG,UAAU,CAAC;IAC/B,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,aAAa,GAAG,IAAI,CAAC;QACpD,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC;IAC1C,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK;QAC/B,CAAC,CAAC;YACE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACpE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACpE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;SAC3E;QACH,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,KAAK,GAAkB;QAC3B,SAAS,EAAE,GAAG;QACd,aAAa;QACb,YAAY,EAAE,aAAa,CAAC,MAAM;QAClC,eAAe;QACf,UAAU;QACV,WAAW;KACZ,CAAC;IAEF,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;QACzB,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,YAAY,GAAG,CAAC,CAAC,GAAG,CAAC;QAC7D,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,YAAY,GAAG,CAAC,CAAC,GAAG,CAAC;QAC7D,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,YAAY,KAAK,CAAC,CAAC,KAAK,CAAC;QACrE,IAAI,QAAQ,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;YACvC,IAAI,MAAc,CAAC;YACnB,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,GAAG,GAAG,YAAY,uCAAuC,CAAC,CAAC,GAAG,EAAE,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACN,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,KAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC;gBAChD,MAAM,GAAG,GAAG,YAAY,gBAAgB,QAAQ,gBAAgB,CAAC;YACnE,CAAC;YACD,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM;gBACN,YAAY;gBACZ,SAAS,EAAE,KAAK;aACjB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,GAAG,YAAY,kCAAkC,aAAa,CAAC,CAAC,CAAC,GAAG;YAC5E,YAAY;YACZ,SAAS,EAAE,KAAK;SACjB,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,aAAa,KAAK,IAAI,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,4BAA4B,QAAQ,CAAC,MAAM,EAAE;gBACrD,YAAY;gBACZ,SAAS,EAAE,KAAK;aACjB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,sCAAsC,GAAG,IAAI;YACrD,YAAY;YACZ,SAAS,EAAE,KAAK;SACjB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,GAAG,YAAY,wBAAwB,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,cAAc,GAAG,IAAI;QACpG,YAAY;QACZ,SAAS,EAAE,KAAK;KACjB,CAAC;AACJ,CAAC"}
|
|
@@ -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"}
|