usertrust 1.4.0 → 2.0.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/README.md +17 -1
- package/dist/anomaly/detector.d.ts.map +1 -1
- package/dist/anomaly/detector.js +36 -11
- package/dist/anomaly/detector.js.map +1 -1
- package/dist/anomaly/index.d.ts +2 -2
- package/dist/anomaly/index.d.ts.map +1 -1
- package/dist/anomaly/index.js +1 -1
- package/dist/anomaly/index.js.map +1 -1
- package/dist/anomaly/signals/spend-velocity.d.ts +39 -12
- package/dist/anomaly/signals/spend-velocity.d.ts.map +1 -1
- package/dist/anomaly/signals/spend-velocity.js +66 -43
- package/dist/anomaly/signals/spend-velocity.js.map +1 -1
- package/dist/anomaly/signals/token-rate.d.ts +33 -13
- package/dist/anomaly/signals/token-rate.d.ts.map +1 -1
- package/dist/anomaly/signals/token-rate.js +124 -38
- package/dist/anomaly/signals/token-rate.js.map +1 -1
- package/dist/anomaly/types.d.ts +28 -6
- package/dist/anomaly/types.d.ts.map +1 -1
- package/dist/audit/anchor-doctor.d.ts +42 -0
- package/dist/audit/anchor-doctor.d.ts.map +1 -0
- package/dist/audit/anchor-doctor.js +285 -0
- package/dist/audit/anchor-doctor.js.map +1 -0
- package/dist/audit/anchor-verify.d.ts +229 -0
- package/dist/audit/anchor-verify.d.ts.map +1 -0
- package/dist/audit/anchor-verify.js +809 -0
- package/dist/audit/anchor-verify.js.map +1 -0
- package/dist/audit/anchor.d.ts +177 -0
- package/dist/audit/anchor.d.ts.map +1 -0
- package/dist/audit/anchor.js +922 -0
- package/dist/audit/anchor.js.map +1 -0
- package/dist/audit/canonical.js +1 -1
- package/dist/audit/canonical.js.map +1 -1
- package/dist/audit/chain.d.ts.map +1 -1
- package/dist/audit/chain.js +21 -7
- package/dist/audit/chain.js.map +1 -1
- package/dist/audit/read.d.ts +19 -0
- package/dist/audit/read.d.ts.map +1 -0
- package/dist/audit/read.js +94 -0
- package/dist/audit/read.js.map +1 -0
- package/dist/audit/rekor-verify.d.ts +116 -0
- package/dist/audit/rekor-verify.d.ts.map +1 -0
- package/dist/audit/rekor-verify.js +533 -0
- package/dist/audit/rekor-verify.js.map +1 -0
- package/dist/audit/rekor.d.ts +71 -0
- package/dist/audit/rekor.d.ts.map +1 -0
- package/dist/audit/rekor.js +556 -0
- package/dist/audit/rekor.js.map +1 -0
- package/dist/audit/rotation.js +1 -1
- package/dist/audit/rotation.js.map +1 -1
- package/dist/audit/sigv4.d.ts +44 -0
- package/dist/audit/sigv4.d.ts.map +1 -0
- package/dist/audit/sigv4.js +134 -0
- package/dist/audit/sigv4.js.map +1 -0
- package/dist/audit/verify.d.ts +78 -0
- package/dist/audit/verify.d.ts.map +1 -1
- package/dist/audit/verify.js +195 -1
- package/dist/audit/verify.js.map +1 -1
- package/dist/cli/anchor.d.ts +3 -0
- package/dist/cli/anchor.d.ts.map +1 -0
- package/dist/cli/anchor.js +626 -0
- package/dist/cli/anchor.js.map +1 -0
- package/dist/cli/completions.js +1 -1
- package/dist/cli/export.d.ts +3 -0
- package/dist/cli/export.d.ts.map +1 -0
- package/dist/cli/export.js +58 -0
- package/dist/cli/export.js.map +1 -0
- package/dist/cli/health.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +99 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/inspect.d.ts.map +1 -1
- package/dist/cli/inspect.js +4 -34
- package/dist/cli/inspect.js.map +1 -1
- package/dist/cli/main.d.ts +2 -2
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +20 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/pricing.js +1 -1
- package/dist/cli/pricing.js.map +1 -1
- package/dist/cli/ui.d.ts +3 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +35 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +342 -3
- package/dist/cli/verify.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/detect.d.ts +71 -16
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +141 -6
- package/dist/detect.js.map +1 -1
- package/dist/export/markdown.d.ts +12 -0
- package/dist/export/markdown.d.ts.map +1 -0
- package/dist/export/markdown.js +142 -0
- package/dist/export/markdown.js.map +1 -0
- package/dist/govern.d.ts +67 -3
- package/dist/govern.d.ts.map +1 -1
- package/dist/govern.js +1011 -219
- package/dist/govern.js.map +1 -1
- package/dist/headless.d.ts +41 -2
- package/dist/headless.d.ts.map +1 -1
- package/dist/headless.js +58 -7
- package/dist/headless.js.map +1 -1
- package/dist/index.d.ts +33 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -23
- package/dist/index.js.map +1 -1
- package/dist/ledger/client.d.ts.map +1 -1
- package/dist/ledger/client.js +57 -46
- package/dist/ledger/client.js.map +1 -1
- package/dist/ledger/engine.d.ts.map +1 -1
- package/dist/ledger/engine.js +22 -13
- package/dist/ledger/engine.js.map +1 -1
- package/dist/ledger/pricing.d.ts +54 -0
- package/dist/ledger/pricing.d.ts.map +1 -1
- package/dist/ledger/pricing.js +128 -8
- package/dist/ledger/pricing.js.map +1 -1
- package/dist/shared/types.d.ts +154 -340
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +67 -17
- package/dist/shared/types.js.map +1 -1
- package/dist/snapshot/checkpoint.d.ts.map +1 -1
- package/dist/snapshot/checkpoint.js +20 -2
- package/dist/snapshot/checkpoint.js.map +1 -1
- package/dist/streaming.d.ts +13 -3
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +88 -30
- package/dist/streaming.js.map +1 -1
- package/dist/supply-chain/sign.d.ts.map +1 -1
- package/dist/supply-chain/sign.js +3 -0
- package/dist/supply-chain/sign.js.map +1 -1
- package/package.json +6 -4
package/dist/detect.js
CHANGED
|
@@ -3,14 +3,25 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Detect which LLM SDK a client belongs to by inspecting its shape.
|
|
5
5
|
*
|
|
6
|
-
* Governance boundary:
|
|
7
|
-
* - Anthropic: `client.messages.create()`
|
|
8
|
-
*
|
|
6
|
+
* Governance boundary: the following methods are intercepted and governed:
|
|
7
|
+
* - Anthropic: `client.messages.create()`, `client.messages.stream()`,
|
|
8
|
+
* `client.messages.parse()`, `client.beta.messages.create()`,
|
|
9
|
+
* `client.beta.messages.stream()`, `client.beta.messages.parse()`
|
|
10
|
+
* - OpenAI: `client.chat.completions.create()`, and `client.responses.create()`
|
|
11
|
+
* when present (feature-detected — the Responses API postdates the peer-range
|
|
12
|
+
* floor, so older clients simply lack it and it stays a pass-through). A streaming
|
|
13
|
+
* `responses.create({ stream: true })` is governed; the `responses.stream()`
|
|
14
|
+
* helper is NOT.
|
|
9
15
|
* - Google: `client.models.generateContent()`
|
|
10
16
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
17
|
+
* Remaining ungoverned pass-throughs: `client.messages.batches`,
|
|
18
|
+
* `client.beta.messages.batches`, `client.beta.models`, `client.beta.files`, the
|
|
19
|
+
* OpenAI Responses non-create methods (`client.responses.retrieve/cancel/delete`,
|
|
20
|
+
* plus the `client.responses.stream()` and `client.responses.parse()` helpers,
|
|
21
|
+
* which drive the SDK's raw client and bypass the governed `create`), the OpenAI
|
|
22
|
+
* `client.beta.*` namespace (assistants/threads/realtime), and the legacy
|
|
23
|
+
* `client.completions.create`. These bypass governance, audit, and budget
|
|
24
|
+
* enforcement (they do not mirror the `create` request/response shape).
|
|
14
25
|
*
|
|
15
26
|
* @throws {Error} if the client does not match any known SDK shape
|
|
16
27
|
*/
|
|
@@ -47,4 +58,128 @@ export function detectClientKind(client) {
|
|
|
47
58
|
}
|
|
48
59
|
throw new Error("Unsupported LLM client: could not detect Anthropic, OpenAI, or Google SDK");
|
|
49
60
|
}
|
|
61
|
+
// ── Endpoint classification (M2 local-model governance) ──
|
|
62
|
+
// Compared against normalizeHostname(url.hostname) — the WHATWG-serialized,
|
|
63
|
+
// bracket-stripped form. `::ffff:7f00:1` is how Node's URL serializes the
|
|
64
|
+
// IPv4-mapped IPv6 loopback `::ffff:127.0.0.1` (the last 32 bits render as hex,
|
|
65
|
+
// never dotted-quad), so the map entry MUST be the serialized form to match.
|
|
66
|
+
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "::ffff:7f00:1"]);
|
|
67
|
+
/** Best-effort runtime hint by conventional default port (metadata only). */
|
|
68
|
+
const RUNTIME_BY_PORT = {
|
|
69
|
+
"11434": "ollama",
|
|
70
|
+
"1234": "lmstudio",
|
|
71
|
+
"8000": "vllm",
|
|
72
|
+
};
|
|
73
|
+
const CLOUD_DEFAULT = { class: "cloud", runtime: "unknown" };
|
|
74
|
+
function parseUrl(value) {
|
|
75
|
+
try {
|
|
76
|
+
return new URL(value);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Normalize a URL hostname for comparison: lowercase, IPv6 brackets stripped
|
|
84
|
+
* (Node's WHATWG URL keeps brackets in `hostname` for IPv6 literals).
|
|
85
|
+
*/
|
|
86
|
+
function normalizeHostname(hostname) {
|
|
87
|
+
const lower = hostname.toLowerCase();
|
|
88
|
+
if (lower.startsWith("[") && lower.endsWith("]"))
|
|
89
|
+
return lower.slice(1, -1);
|
|
90
|
+
return lower;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Match a parsed baseURL against one config.endpoints[].match entry (A1).
|
|
94
|
+
* Exactly three entry forms — never raw string prefixing:
|
|
95
|
+
* (i) scheme entry ("http://gpu-box:8000") → parsed; match iff origins are equal
|
|
96
|
+
* (path/query/trailing slash ignored; kills "http://gpu-box:8000.evil.com"
|
|
97
|
+
* and "http://gpu-box:8000@evil.com" bypass shapes);
|
|
98
|
+
* (ii) leading-star entry ("*.gpu.internal") → hostname SUFFIX match: matches
|
|
99
|
+
* "a.gpu.internal" and "x.y.gpu.internal", NOT "gpu.internal" itself;
|
|
100
|
+
* (iii) bare hostname → case-insensitive hostname equality (any port).
|
|
101
|
+
* Unparseable scheme entries never match (and never throw).
|
|
102
|
+
*/
|
|
103
|
+
function matchesEndpointEntry(entry, url) {
|
|
104
|
+
if (/^https?:\/\//i.test(entry)) {
|
|
105
|
+
const entryUrl = parseUrl(entry);
|
|
106
|
+
return entryUrl !== null && entryUrl.origin === url.origin;
|
|
107
|
+
}
|
|
108
|
+
const host = normalizeHostname(url.hostname);
|
|
109
|
+
if (entry.startsWith("*.")) {
|
|
110
|
+
const suffix = entry.slice(1).toLowerCase(); // ".gpu.internal"
|
|
111
|
+
return host.endsWith(suffix);
|
|
112
|
+
}
|
|
113
|
+
return host === entry.toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
/** Read a string `baseURL` property off an SDK client instance, if present. */
|
|
116
|
+
function readBaseURL(client) {
|
|
117
|
+
if (client != null && typeof client === "object" && "baseURL" in client) {
|
|
118
|
+
const value = client.baseURL;
|
|
119
|
+
if (typeof value === "string")
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Classify the endpoint a client points at — local (self-hosted) or cloud —
|
|
126
|
+
* selecting the settlement regime for every governed call. Runs BESIDE
|
|
127
|
+
* `detectClientKind` (which stays the transport detector); the endpoint class,
|
|
128
|
+
* not the model string, picks local vs cloud metering, so model-name spoofing
|
|
129
|
+
* (`ollama cp llama3.2 gpt-4o`) cannot change the regime.
|
|
130
|
+
*
|
|
131
|
+
* Classification order:
|
|
132
|
+
* 1. `override.class` provided → override wins (runtime defaults to "unknown").
|
|
133
|
+
* 2. `client.baseURL` read when it is a string; absent or malformed (URL parse
|
|
134
|
+
* failure) → treated as absent → cloud default. Never throws.
|
|
135
|
+
* 3. `config.endpoints[]` first match (origin / leading-star hostname suffix /
|
|
136
|
+
* bare hostname — see matchesEndpointEntry).
|
|
137
|
+
* 4. Loopback autodetect (config.local.autoDetectLoopback): hostname
|
|
138
|
+
* localhost | 127.0.0.1 | ::1 | ::ffff:127.0.0.1 (case-insensitive, IPv6
|
|
139
|
+
* brackets stripped, IPv4-mapped form matched by its WHATWG serialization) →
|
|
140
|
+
* local, runtime hinted by port (11434 ollama, 1234 lmstudio, 8000 vllm,
|
|
141
|
+
* else "openai-compat").
|
|
142
|
+
* 5. Default `{ class: "cloud", runtime: "unknown" }` — fail-EXPENSIVE:
|
|
143
|
+
* over-charging at cloud rates is recoverable, under-charging a paid
|
|
144
|
+
* endpoint is not.
|
|
145
|
+
*
|
|
146
|
+
* **Security posture (trusted-operator boundary):** endpoint classification —
|
|
147
|
+
* config matchers, overrides, and loopback autodetect — is a TRUSTED-OPERATOR
|
|
148
|
+
* decision. Never wire it to end-user or request input. In server/multi-tenant
|
|
149
|
+
* deployments set `local.autoDetectLoopback: false` and classify via explicit
|
|
150
|
+
* `endpoints[]` config: loopback inside a container can be a forwarding sidecar
|
|
151
|
+
* to a paid API. This is the same trust boundary as `budget`/`customRates` —
|
|
152
|
+
* the config author already controls billing entirely. Note that a compromised
|
|
153
|
+
* local server can under-report usage; receipts expose `usageSource` and
|
|
154
|
+
* `meter.rateSource` precisely so that this is auditable.
|
|
155
|
+
*/
|
|
156
|
+
export function classifyEndpoint(client, config, override) {
|
|
157
|
+
const baseURL = readBaseURL(client);
|
|
158
|
+
// (1) Explicit override wins over everything.
|
|
159
|
+
if (override?.class !== undefined) {
|
|
160
|
+
const info = { class: override.class, runtime: override.runtime ?? "unknown" };
|
|
161
|
+
const overrideBase = override.baseURL ?? baseURL;
|
|
162
|
+
if (overrideBase !== undefined)
|
|
163
|
+
info.baseURL = overrideBase;
|
|
164
|
+
return info;
|
|
165
|
+
}
|
|
166
|
+
// (2) No readable baseURL → cloud default. Malformed → treat as absent, never throw.
|
|
167
|
+
if (baseURL === undefined)
|
|
168
|
+
return { ...CLOUD_DEFAULT };
|
|
169
|
+
const url = parseUrl(baseURL);
|
|
170
|
+
if (url === null)
|
|
171
|
+
return { ...CLOUD_DEFAULT };
|
|
172
|
+
// (3) config.endpoints[] — first match wins.
|
|
173
|
+
for (const entry of config.endpoints) {
|
|
174
|
+
if (matchesEndpointEntry(entry.match, url)) {
|
|
175
|
+
return { class: entry.class, runtime: entry.runtime, baseURL };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// (4) Loopback autodetect.
|
|
179
|
+
if (config.local.autoDetectLoopback && LOOPBACK_HOSTS.has(normalizeHostname(url.hostname))) {
|
|
180
|
+
return { class: "local", runtime: RUNTIME_BY_PORT[url.port] ?? "openai-compat", baseURL };
|
|
181
|
+
}
|
|
182
|
+
// (5) Cloud default.
|
|
183
|
+
return { class: "cloud", runtime: "unknown", baseURL };
|
|
184
|
+
}
|
|
50
185
|
//# sourceMappingURL=detect.js.map
|
package/dist/detect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ExportResult {
|
|
2
|
+
written: number;
|
|
3
|
+
outDir: string;
|
|
4
|
+
/** Parsed-chain walk verdict (hash linkage over readable events). */
|
|
5
|
+
chainValid: boolean;
|
|
6
|
+
breakIndex: number | null;
|
|
7
|
+
/** Full vault verification (segments, malformed lines, .meta anchor). */
|
|
8
|
+
vaultValid: boolean;
|
|
9
|
+
vaultErrors: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function exportMarkdown(vaultPath: string, outDir: string): ExportResult;
|
|
12
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/export/markdown.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yEAAyE;IACzE,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAkGD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAiD9E"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Usertools, Inc.
|
|
3
|
+
/**
|
|
4
|
+
* Markdown / Obsidian exporter.
|
|
5
|
+
*
|
|
6
|
+
* Writes one frontmatter-rich note per ledger event, a Bases view
|
|
7
|
+
* (`Receipts.base`), and a `Ledger Index.md` summary. Notes wikilink their
|
|
8
|
+
* previous event so the hash chain renders as a literal chain in graph view.
|
|
9
|
+
* Read-only against the vault; writes only into `outDir`.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { deriveChainIntegrity, readLedgerEvents } from "../audit/read.js";
|
|
14
|
+
import { verifyVault } from "../audit/verify.js";
|
|
15
|
+
const UT_TO_USD = 0.0001;
|
|
16
|
+
/** Mirrors packages/verify/src/receipt.ts detectProvider (verify stays zero-dep). */
|
|
17
|
+
function detectProvider(model) {
|
|
18
|
+
if (model.startsWith("claude"))
|
|
19
|
+
return "anthropic";
|
|
20
|
+
if (model.startsWith("gpt") || model.startsWith("o1") || model.startsWith("o3"))
|
|
21
|
+
return "openai";
|
|
22
|
+
if (model.startsWith("gemini"))
|
|
23
|
+
return "google";
|
|
24
|
+
if (model.startsWith("command"))
|
|
25
|
+
return "cohere";
|
|
26
|
+
if (model.startsWith("mistral") || model.startsWith("mixtral"))
|
|
27
|
+
return "mistral";
|
|
28
|
+
return "unknown";
|
|
29
|
+
}
|
|
30
|
+
// ANCHOR-INTEGRATION: swap for real anchor verification when feat/external-anchoring lands.
|
|
31
|
+
function anchorState(vaultPath) {
|
|
32
|
+
return existsSync(join(vaultPath, "audit", "anchors", "anchors.jsonl"))
|
|
33
|
+
? "present"
|
|
34
|
+
: "unanchored";
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Event ids come from the ledger on disk — never trust them as path
|
|
38
|
+
* components. Used for both the `.md` filename and the wikilink target so
|
|
39
|
+
* links keep resolving after sanitization.
|
|
40
|
+
*/
|
|
41
|
+
function sanitizeId(id) {
|
|
42
|
+
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
43
|
+
}
|
|
44
|
+
function yamlString(value) {
|
|
45
|
+
return /^[A-Za-z0-9._:-]+$/.test(value) ? value : JSON.stringify(value);
|
|
46
|
+
}
|
|
47
|
+
function integrityFor(index, breakIndex) {
|
|
48
|
+
return breakIndex !== null && index >= breakIndex ? "after-break" : "verified";
|
|
49
|
+
}
|
|
50
|
+
function noteFor(e, index, prev, breakIndex, anchor) {
|
|
51
|
+
const model = typeof e.data.model === "string" ? e.data.model : undefined;
|
|
52
|
+
const cost = typeof e.data.cost === "number" ? e.data.cost : undefined;
|
|
53
|
+
const fm = ["---"];
|
|
54
|
+
fm.push(`ts: ${yamlString(e.timestamp)}`);
|
|
55
|
+
fm.push(`kind: ${yamlString(e.kind)}`);
|
|
56
|
+
fm.push(`actor: ${yamlString(e.actor)}`);
|
|
57
|
+
if (model) {
|
|
58
|
+
fm.push(`model: ${yamlString(model)}`);
|
|
59
|
+
fm.push(`provider: ${detectProvider(model)}`);
|
|
60
|
+
}
|
|
61
|
+
if (cost !== undefined) {
|
|
62
|
+
fm.push(`cost_ut: ${cost}`);
|
|
63
|
+
fm.push(`cost_usd: ${cost * UT_TO_USD}`);
|
|
64
|
+
}
|
|
65
|
+
if (typeof e.data.settled === "boolean")
|
|
66
|
+
fm.push(`settled: ${e.data.settled}`);
|
|
67
|
+
if (typeof e.data.transferId === "string")
|
|
68
|
+
fm.push(`transfer_id: ${yamlString(e.data.transferId)}`);
|
|
69
|
+
if (typeof e.sequence === "number")
|
|
70
|
+
fm.push(`seq: ${e.sequence}`);
|
|
71
|
+
fm.push(`hash: ${yamlString(e.hash)}`);
|
|
72
|
+
fm.push(`previous_hash: ${yamlString(e.previousHash)}`);
|
|
73
|
+
fm.push(`integrity: ${integrityFor(index, breakIndex)}`);
|
|
74
|
+
fm.push(`anchor_state: ${anchor}`);
|
|
75
|
+
fm.push("---");
|
|
76
|
+
const body = [""];
|
|
77
|
+
body.push(`# ${e.kind} — ${e.timestamp}`);
|
|
78
|
+
body.push("");
|
|
79
|
+
body.push("```");
|
|
80
|
+
body.push(JSON.stringify(e.data, null, 2));
|
|
81
|
+
body.push("```");
|
|
82
|
+
body.push("");
|
|
83
|
+
body.push(prev ? `Previous: [[${sanitizeId(prev.id)}]]` : "Previous: _genesis_");
|
|
84
|
+
body.push("");
|
|
85
|
+
return fm.join("\n") + body.join("\n");
|
|
86
|
+
}
|
|
87
|
+
const BASE_VIEW = `views:
|
|
88
|
+
- type: table
|
|
89
|
+
name: Receipts
|
|
90
|
+
order:
|
|
91
|
+
- ts
|
|
92
|
+
- kind
|
|
93
|
+
- actor
|
|
94
|
+
- model
|
|
95
|
+
- cost_ut
|
|
96
|
+
- settled
|
|
97
|
+
- integrity
|
|
98
|
+
- transfer_id
|
|
99
|
+
sort:
|
|
100
|
+
- property: ts
|
|
101
|
+
direction: DESC
|
|
102
|
+
`;
|
|
103
|
+
export function exportMarkdown(vaultPath, outDir) {
|
|
104
|
+
const events = readLedgerEvents(vaultPath);
|
|
105
|
+
const integrity = deriveChainIntegrity(events);
|
|
106
|
+
// Parsed-chain walk alone misses vault-level failures (torn trailing line,
|
|
107
|
+
// tail truncation vs the .meta anchor, sequence gaps) — run the full vault
|
|
108
|
+
// verification too so an export from a tampered vault says so.
|
|
109
|
+
const vault = verifyVault(vaultPath);
|
|
110
|
+
const anchor = anchorState(vaultPath);
|
|
111
|
+
mkdirSync(join(outDir, "receipts"), { recursive: true });
|
|
112
|
+
let written = 0;
|
|
113
|
+
for (let i = 0; i < events.length; i++) {
|
|
114
|
+
const e = events[i];
|
|
115
|
+
const day = sanitizeId(e.timestamp.slice(0, 10)) || "unknown-date";
|
|
116
|
+
const dir = join(outDir, "receipts", day);
|
|
117
|
+
mkdirSync(dir, { recursive: true });
|
|
118
|
+
writeFileSync(join(dir, `${sanitizeId(e.id)}.md`), noteFor(e, i, i > 0 ? events[i - 1] : undefined, integrity.breakIndex, anchor), "utf-8");
|
|
119
|
+
written++;
|
|
120
|
+
}
|
|
121
|
+
writeFileSync(join(outDir, "Receipts.base"), BASE_VIEW, "utf-8");
|
|
122
|
+
const index = ["# Ledger Index", ""];
|
|
123
|
+
index.push(`- Events exported: ${written}`);
|
|
124
|
+
index.push(`- Chain integrity: ${integrity.valid ? "verified" : `BROKEN at seq ${(events[integrity.breakIndex ?? 0]?.sequence ?? integrity.breakIndex ?? 0).toString()}`}`);
|
|
125
|
+
index.push(`- Vault verification: ${vault.valid ? "passed" : `FAILED — ${vault.errors[0] ?? "unknown error"}`}`);
|
|
126
|
+
index.push(`- Anchor state: ${anchor}`);
|
|
127
|
+
index.push(`- Vault: ${vaultPath}`);
|
|
128
|
+
const newest = events.at(-1);
|
|
129
|
+
if (newest)
|
|
130
|
+
index.push(`- Newest receipt: [[${sanitizeId(newest.id)}]]`);
|
|
131
|
+
index.push("");
|
|
132
|
+
writeFileSync(join(outDir, "Ledger Index.md"), index.join("\n"), "utf-8");
|
|
133
|
+
return {
|
|
134
|
+
written,
|
|
135
|
+
outDir,
|
|
136
|
+
chainValid: integrity.valid,
|
|
137
|
+
breakIndex: integrity.breakIndex,
|
|
138
|
+
vaultValid: vault.valid,
|
|
139
|
+
vaultErrors: vault.errors,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/export/markdown.ts"],"names":[],"mappings":"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"}
|
package/dist/govern.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AuditWriter } from "./audit/chain.js";
|
|
2
|
-
import type { ActionDescriptor, GovernedActionResult } from "./shared/types.js";
|
|
2
|
+
import type { ActionDescriptor, EndpointInfo, GovernedActionResult, LLMClientKind, TrustReceipt } from "./shared/types.js";
|
|
3
3
|
export interface TrustOpts {
|
|
4
4
|
/** Path to usertrust.config.json. Defaults to `.usertrust/usertrust.config.json`. */
|
|
5
5
|
configPath?: string;
|
|
@@ -25,6 +25,13 @@ export interface TrustOpts {
|
|
|
25
25
|
dryRun?: boolean;
|
|
26
26
|
/** Vault directory override (default: cwd). */
|
|
27
27
|
vaultBase?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Explicit endpoint classification override (M2) — wins over config.endpoints
|
|
30
|
+
* matchers and loopback autodetect. TRUSTED-OPERATOR input, same trust
|
|
31
|
+
* boundary as budget/customRates: never derive it from end-user or request
|
|
32
|
+
* data (A10).
|
|
33
|
+
*/
|
|
34
|
+
endpoint?: Partial<EndpointInfo> | undefined;
|
|
28
35
|
/**
|
|
29
36
|
* Inject a mock/test engine. When set, used instead of TigerBeetle.
|
|
30
37
|
* Primarily for testing failure modes.
|
|
@@ -55,10 +62,67 @@ export interface TrustEngine {
|
|
|
55
62
|
voidAllPending?(): Promise<void>;
|
|
56
63
|
destroy?(): void;
|
|
57
64
|
}
|
|
58
|
-
/**
|
|
59
|
-
|
|
65
|
+
/** `stream` becomes `(...args) => Promise<MessageStream & { receipt: Promise<TrustReceipt> }>`. */
|
|
66
|
+
type GovernedStreamMethod<F> = F extends (...args: infer A) => infer R ? (...args: A) => Promise<Awaited<R> & {
|
|
67
|
+
receipt: Promise<TrustReceipt>;
|
|
68
|
+
}> : F;
|
|
69
|
+
/** `parse` becomes `(...args) => Promise<ParsedMessage & { receipt: TrustReceipt }>`. */
|
|
70
|
+
type GovernedParseMethod<F> = F extends (...args: infer A) => infer R ? (...args: A) => Promise<Awaited<R> & {
|
|
71
|
+
receipt: TrustReceipt;
|
|
72
|
+
}> : F;
|
|
73
|
+
/** Rewrite `stream`/`parse` on a `messages` resource; leave `create`/everything else. */
|
|
74
|
+
type GovernedMessages<M> = M extends object ? Omit<M, "stream" | "parse"> & ("stream" extends keyof M ? {
|
|
75
|
+
stream: GovernedStreamMethod<M["stream"]>;
|
|
76
|
+
} : unknown) & ("parse" extends keyof M ? {
|
|
77
|
+
parse: GovernedParseMethod<M["parse"]>;
|
|
78
|
+
} : unknown) : M;
|
|
79
|
+
/** Rewrite `beta.messages` when present; otherwise leave `beta` untouched. */
|
|
80
|
+
type GovernedBeta<B> = B extends {
|
|
81
|
+
messages: infer BM;
|
|
82
|
+
} ? Omit<B, "messages"> & {
|
|
83
|
+
messages: GovernedMessages<BM>;
|
|
84
|
+
} : B;
|
|
85
|
+
/**
|
|
86
|
+
* Rewrite only the Anthropic `messages` / `beta.messages` surfaces. A client
|
|
87
|
+
* without a `messages` resource (OpenAI, Google) is returned unchanged — its
|
|
88
|
+
* governed `create`/`responses` shapes are unaffected by this rewrite.
|
|
89
|
+
*/
|
|
90
|
+
type GovernedShape<T> = T extends {
|
|
91
|
+
messages: infer M;
|
|
92
|
+
} ? Omit<T, "messages" | "beta"> & {
|
|
93
|
+
messages: GovernedMessages<M>;
|
|
94
|
+
} & (T extends {
|
|
95
|
+
beta: infer B;
|
|
96
|
+
} ? {
|
|
97
|
+
beta: GovernedBeta<B>;
|
|
98
|
+
} : unknown) : T;
|
|
99
|
+
/** The trusted client: governed client shape (F5) + governance methods. */
|
|
100
|
+
export type TrustedClient<T> = GovernedShape<T> & {
|
|
60
101
|
destroy(): Promise<void>;
|
|
61
102
|
governAction<R>(action: ActionDescriptor, execute: () => Promise<R>): Promise<GovernedActionResult<R>>;
|
|
62
103
|
};
|
|
63
104
|
export declare function trust<T>(client: T, opts?: TrustOpts): Promise<TrustedClient<T>>;
|
|
105
|
+
/**
|
|
106
|
+
* Normalize the prompt-bearing payload across providers so PII/injection scanning
|
|
107
|
+
* and token estimation cover ALL shapes, not just Anthropic/OpenAI `messages`:
|
|
108
|
+
* - Anthropic/OpenAI: `params.messages` (+ Anthropic top-level `system` string)
|
|
109
|
+
* - Google: `params.contents` (the prompt lives here, not `messages`)
|
|
110
|
+
*
|
|
111
|
+
* Without this, a Google `generateContent({ model, contents })` call has an empty
|
|
112
|
+
* `messages` array, so every PII/injection scan sees nothing and PII egresses.
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Normalize the prompt-bearing payload across providers into a flat parts array
|
|
116
|
+
* that PII/injection scanning, token estimation, redaction, and pattern hashing
|
|
117
|
+
* all consume. Exported for the A8 byte-exact regression test (kept internal to
|
|
118
|
+
* the package — not re-exported from index.ts).
|
|
119
|
+
*
|
|
120
|
+
* Branch order is load-bearing (A8): the OpenAI Responses branch is shape-
|
|
121
|
+
* discriminated (`kind === "openai"` AND no `messages` key) and placed so it can
|
|
122
|
+
* NEVER preempt the chat.completions / Anthropic `messages` branch — every
|
|
123
|
+
* chat.completions or Anthropic call carries `messages` and skips it, so existing
|
|
124
|
+
* extraction stays byte-unchanged.
|
|
125
|
+
*/
|
|
126
|
+
export declare function extractPromptParts(params: Record<string, unknown>, kind: LLMClientKind): unknown[];
|
|
127
|
+
export {};
|
|
64
128
|
//# sourceMappingURL=govern.d.ts.map
|
package/dist/govern.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"govern.d.ts","sourceRoot":"","sources":["../src/govern.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"govern.d.ts","sourceRoot":"","sources":["../src/govern.ts"],"names":[],"mappings":"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"}
|