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.
Files changed (133) hide show
  1. package/README.md +17 -1
  2. package/dist/anomaly/detector.d.ts.map +1 -1
  3. package/dist/anomaly/detector.js +36 -11
  4. package/dist/anomaly/detector.js.map +1 -1
  5. package/dist/anomaly/index.d.ts +2 -2
  6. package/dist/anomaly/index.d.ts.map +1 -1
  7. package/dist/anomaly/index.js +1 -1
  8. package/dist/anomaly/index.js.map +1 -1
  9. package/dist/anomaly/signals/spend-velocity.d.ts +39 -12
  10. package/dist/anomaly/signals/spend-velocity.d.ts.map +1 -1
  11. package/dist/anomaly/signals/spend-velocity.js +66 -43
  12. package/dist/anomaly/signals/spend-velocity.js.map +1 -1
  13. package/dist/anomaly/signals/token-rate.d.ts +33 -13
  14. package/dist/anomaly/signals/token-rate.d.ts.map +1 -1
  15. package/dist/anomaly/signals/token-rate.js +124 -38
  16. package/dist/anomaly/signals/token-rate.js.map +1 -1
  17. package/dist/anomaly/types.d.ts +28 -6
  18. package/dist/anomaly/types.d.ts.map +1 -1
  19. package/dist/audit/anchor-doctor.d.ts +42 -0
  20. package/dist/audit/anchor-doctor.d.ts.map +1 -0
  21. package/dist/audit/anchor-doctor.js +285 -0
  22. package/dist/audit/anchor-doctor.js.map +1 -0
  23. package/dist/audit/anchor-verify.d.ts +229 -0
  24. package/dist/audit/anchor-verify.d.ts.map +1 -0
  25. package/dist/audit/anchor-verify.js +809 -0
  26. package/dist/audit/anchor-verify.js.map +1 -0
  27. package/dist/audit/anchor.d.ts +177 -0
  28. package/dist/audit/anchor.d.ts.map +1 -0
  29. package/dist/audit/anchor.js +922 -0
  30. package/dist/audit/anchor.js.map +1 -0
  31. package/dist/audit/canonical.js +1 -1
  32. package/dist/audit/canonical.js.map +1 -1
  33. package/dist/audit/chain.d.ts.map +1 -1
  34. package/dist/audit/chain.js +21 -7
  35. package/dist/audit/chain.js.map +1 -1
  36. package/dist/audit/read.d.ts +19 -0
  37. package/dist/audit/read.d.ts.map +1 -0
  38. package/dist/audit/read.js +94 -0
  39. package/dist/audit/read.js.map +1 -0
  40. package/dist/audit/rekor-verify.d.ts +116 -0
  41. package/dist/audit/rekor-verify.d.ts.map +1 -0
  42. package/dist/audit/rekor-verify.js +533 -0
  43. package/dist/audit/rekor-verify.js.map +1 -0
  44. package/dist/audit/rekor.d.ts +71 -0
  45. package/dist/audit/rekor.d.ts.map +1 -0
  46. package/dist/audit/rekor.js +556 -0
  47. package/dist/audit/rekor.js.map +1 -0
  48. package/dist/audit/rotation.js +1 -1
  49. package/dist/audit/rotation.js.map +1 -1
  50. package/dist/audit/sigv4.d.ts +44 -0
  51. package/dist/audit/sigv4.d.ts.map +1 -0
  52. package/dist/audit/sigv4.js +134 -0
  53. package/dist/audit/sigv4.js.map +1 -0
  54. package/dist/audit/verify.d.ts +78 -0
  55. package/dist/audit/verify.d.ts.map +1 -1
  56. package/dist/audit/verify.js +195 -1
  57. package/dist/audit/verify.js.map +1 -1
  58. package/dist/cli/anchor.d.ts +3 -0
  59. package/dist/cli/anchor.d.ts.map +1 -0
  60. package/dist/cli/anchor.js +626 -0
  61. package/dist/cli/anchor.js.map +1 -0
  62. package/dist/cli/completions.js +1 -1
  63. package/dist/cli/export.d.ts +3 -0
  64. package/dist/cli/export.d.ts.map +1 -0
  65. package/dist/cli/export.js +58 -0
  66. package/dist/cli/export.js.map +1 -0
  67. package/dist/cli/health.js.map +1 -1
  68. package/dist/cli/init.d.ts.map +1 -1
  69. package/dist/cli/init.js +99 -2
  70. package/dist/cli/init.js.map +1 -1
  71. package/dist/cli/inspect.d.ts.map +1 -1
  72. package/dist/cli/inspect.js +4 -34
  73. package/dist/cli/inspect.js.map +1 -1
  74. package/dist/cli/main.d.ts +2 -2
  75. package/dist/cli/main.d.ts.map +1 -1
  76. package/dist/cli/main.js +20 -1
  77. package/dist/cli/main.js.map +1 -1
  78. package/dist/cli/pricing.js +1 -1
  79. package/dist/cli/pricing.js.map +1 -1
  80. package/dist/cli/ui.d.ts +3 -0
  81. package/dist/cli/ui.d.ts.map +1 -0
  82. package/dist/cli/ui.js +35 -0
  83. package/dist/cli/ui.js.map +1 -0
  84. package/dist/cli/verify.d.ts.map +1 -1
  85. package/dist/cli/verify.js +342 -3
  86. package/dist/cli/verify.js.map +1 -1
  87. package/dist/config.d.ts.map +1 -1
  88. package/dist/config.js.map +1 -1
  89. package/dist/detect.d.ts +71 -16
  90. package/dist/detect.d.ts.map +1 -1
  91. package/dist/detect.js +141 -6
  92. package/dist/detect.js.map +1 -1
  93. package/dist/export/markdown.d.ts +12 -0
  94. package/dist/export/markdown.d.ts.map +1 -0
  95. package/dist/export/markdown.js +142 -0
  96. package/dist/export/markdown.js.map +1 -0
  97. package/dist/govern.d.ts +67 -3
  98. package/dist/govern.d.ts.map +1 -1
  99. package/dist/govern.js +1011 -219
  100. package/dist/govern.js.map +1 -1
  101. package/dist/headless.d.ts +41 -2
  102. package/dist/headless.d.ts.map +1 -1
  103. package/dist/headless.js +58 -7
  104. package/dist/headless.js.map +1 -1
  105. package/dist/index.d.ts +33 -25
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +30 -23
  108. package/dist/index.js.map +1 -1
  109. package/dist/ledger/client.d.ts.map +1 -1
  110. package/dist/ledger/client.js +57 -46
  111. package/dist/ledger/client.js.map +1 -1
  112. package/dist/ledger/engine.d.ts.map +1 -1
  113. package/dist/ledger/engine.js +22 -13
  114. package/dist/ledger/engine.js.map +1 -1
  115. package/dist/ledger/pricing.d.ts +54 -0
  116. package/dist/ledger/pricing.d.ts.map +1 -1
  117. package/dist/ledger/pricing.js +128 -8
  118. package/dist/ledger/pricing.js.map +1 -1
  119. package/dist/shared/types.d.ts +154 -340
  120. package/dist/shared/types.d.ts.map +1 -1
  121. package/dist/shared/types.js +67 -17
  122. package/dist/shared/types.js.map +1 -1
  123. package/dist/snapshot/checkpoint.d.ts.map +1 -1
  124. package/dist/snapshot/checkpoint.js +20 -2
  125. package/dist/snapshot/checkpoint.js.map +1 -1
  126. package/dist/streaming.d.ts +13 -3
  127. package/dist/streaming.d.ts.map +1 -1
  128. package/dist/streaming.js +88 -30
  129. package/dist/streaming.js.map +1 -1
  130. package/dist/supply-chain/sign.d.ts.map +1 -1
  131. package/dist/supply-chain/sign.js +3 -0
  132. package/dist/supply-chain/sign.js.map +1 -1
  133. 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: only the following methods are intercepted and governed:
7
- * - Anthropic: `client.messages.create()`
8
- * - OpenAI: `client.chat.completions.create()`
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
- * Alternative methods (e.g., `client.messages.stream`, `client.beta.*`, streaming APIs)
12
- * are NOT intercepted and will bypass governance, audit, and budget enforcement.
13
- * This is a known limitation of duck-typed proxy interception.
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
@@ -1 +1 @@
1
- {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAuBjC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAe;IAC/C,IACC,MAAM,IAAI,IAAI;QACd,OAAO,MAAM,KAAK,QAAQ;QAC1B,UAAU,IAAI,MAAM;QACpB,MAAM,CAAC,QAAQ,IAAI,IAAI;QACvB,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QACnC,QAAQ,IAAI,MAAM,CAAC,QAAQ;QAC3B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,UAAU,EAC3C,CAAC;QACF,OAAO,WAAW,CAAC;IACpB,CAAC;IAED,IACC,MAAM,IAAI,IAAI;QACd,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,IAAI,MAAM;QAChB,MAAM,CAAC,IAAI,IAAI,IAAI;QACnB,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;QAC/B,aAAa,IAAI,MAAM,CAAC,IAAI;QAC5B,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI;QAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,KAAK,QAAQ;QAC3C,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW;QACnC,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,UAAU,EACnD,CAAC;QACF,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,IACC,MAAM,IAAI,IAAI;QACd,OAAO,MAAM,KAAK,QAAQ;QAC1B,QAAQ,IAAI,MAAM;QAClB,MAAM,CAAC,MAAM,IAAI,IAAI;QACrB,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;QACjC,iBAAiB,IAAI,MAAM,CAAC,MAAM;QAClC,OAAO,MAAM,CAAC,MAAM,CAAC,eAAe,KAAK,UAAU,EAClD,CAAC;QACF,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;AAC9F,CAAC"}
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
- /** The trusted client: original client shape + governance methods. */
59
- export type TrustedClient<T> = T & {
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
@@ -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;AA0BvE,OAAO,KAAK,EACX,gBAAgB,EAChB,oBAAoB,EAKpB,MAAM,mBAAmB,CAAC;AAK3B,MAAM,WAAW,SAAS;IACzB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC3B,YAAY,CAAC,MAAM,EAAE;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpC;;;OAGG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,gEAAgE;IAChE,cAAc,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,CAAC,IAAI,IAAI,CAAC;CACjB;AAED,sEAAsE;AACtE,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG;IAClC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,YAAY,CAAC,CAAC,EACb,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;CACpC,CAAC;AAuFF,wBAAsB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAorCrF"}
1
+ {"version":3,"file":"govern.d.ts","sourceRoot":"","sources":["../src/govern.ts"],"names":[],"mappings":"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"}