usertrust-openclaw 1.1.1

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.
@@ -0,0 +1,46 @@
1
+ import type { Governor } from "usertrust";
2
+ import type { OpenClawPluginApi, StreamFn, UsertrustPluginConfig } from "./types.js";
3
+ export { wrapStreamWithGovernance, wrapCompleteWithGovernance } from "./stream-governor.js";
4
+ export { createAccumulator, extractUsageFromEvent } from "./token-extractor.js";
5
+ export type { StreamEvent, StreamFn, StreamContext, StreamUsage, UsertrustPluginConfig, GovernedStreamMeta, } from "./types.js";
6
+ /**
7
+ * OpenClaw plugin entry point.
8
+ *
9
+ * Called by OpenClaw's plugin loader. Initializes the usertrust
10
+ * governance engine and registers the stream wrapper.
11
+ */
12
+ export default function register(api: OpenClawPluginApi): void;
13
+ /**
14
+ * Programmatic API for non-OpenClaw usage.
15
+ *
16
+ * Use this when integrating usertrust governance into a custom
17
+ * pi-ai setup without the full OpenClaw plugin system.
18
+ *
19
+ * ```ts
20
+ * import { createGovernedStreamFn } from "@usertrust/openclaw";
21
+ *
22
+ * const governed = await createGovernedStreamFn(myStreamFn, {
23
+ * budget: 100_000,
24
+ * dryRun: true,
25
+ * });
26
+ *
27
+ * for await (const event of governed("claude-sonnet-4-6", context)) {
28
+ * // events flow through with governance applied
29
+ * }
30
+ * ```
31
+ */
32
+ export declare function createGovernedStreamFn(streamFn: StreamFn, config: UsertrustPluginConfig): Promise<{
33
+ governedStreamFn: StreamFn;
34
+ governor: Governor;
35
+ }>;
36
+ /**
37
+ * Get the active governor instance.
38
+ * Returns null if the plugin hasn't been initialized yet.
39
+ */
40
+ export declare function getGovernor(): Governor | null;
41
+ /**
42
+ * Graceful shutdown — call this when OpenClaw exits.
43
+ * Voids all pending holds and flushes the audit chain.
44
+ */
45
+ export declare function shutdown(): Promise<void>;
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,KAAK,EACX,iBAAiB,EAGjB,QAAQ,EACR,qBAAqB,EACrB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAChF,YAAY,EACX,WAAW,EACX,QAAQ,EACR,aAAa,EACb,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GAClB,MAAM,YAAY,CAAC;AAKpB;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CA0B7D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,sBAAsB,CAC3C,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,qBAAqB,GAC3B,OAAO,CAAC;IAAE,gBAAgB,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAI7D;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,QAAQ,GAAG,IAAI,CAE7C;AAED;;;GAGG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9C"}
package/dist/index.js ADDED
@@ -0,0 +1,148 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Usertools, Inc.
3
+ /**
4
+ * @usertrust/openclaw — usertrust governance plugin for OpenClaw
5
+ *
6
+ * Adds budget enforcement, policy gates, and hash-chained audit trails
7
+ * to every LLM call in OpenClaw. Zero code changes required — install
8
+ * the plugin and every call is governed.
9
+ *
10
+ * Installation:
11
+ * openclaw plugins install @usertrust/openclaw
12
+ *
13
+ * Or manual setup in openclaw.json:
14
+ * {
15
+ * "plugins": {
16
+ * "entries": {
17
+ * "usertrust": {
18
+ * "enabled": true,
19
+ * "config": { "budget": 100000, "dryRun": true }
20
+ * }
21
+ * }
22
+ * }
23
+ * }
24
+ *
25
+ * How it works:
26
+ * User's LLM call → OpenClaw → wrapStreamFn (usertrust) →
27
+ * 1. Check budget (PENDING hold)
28
+ * 2. Forward to real stream
29
+ * 3. Accumulate token usage from stream
30
+ * 4. POST settle with actual cost
31
+ * 5. Return governed stream to OpenClaw
32
+ */
33
+ import { createGovernor } from "usertrust";
34
+ import { wrapStreamWithGovernance } from "./stream-governor.js";
35
+ // Re-export for consumers
36
+ export { wrapStreamWithGovernance, wrapCompleteWithGovernance } from "./stream-governor.js";
37
+ export { createAccumulator, extractUsageFromEvent } from "./token-extractor.js";
38
+ /** Active governor instance — singleton per plugin lifecycle. */
39
+ let governor = null;
40
+ /**
41
+ * OpenClaw plugin entry point.
42
+ *
43
+ * Called by OpenClaw's plugin loader. Initializes the usertrust
44
+ * governance engine and registers the stream wrapper.
45
+ */
46
+ export default function register(api) {
47
+ // Config is injected by OpenClaw from openclaw.json → plugins.entries.usertrust.config
48
+ // We register a hook that initializes on first use (lazy)
49
+ // since config isn't available at register time in all OpenClaw versions.
50
+ const plugin = {
51
+ id: "usertrust",
52
+ label: "usertrust Governance",
53
+ /**
54
+ * wrapStreamFn — the core integration point.
55
+ *
56
+ * OpenClaw calls this with the existing stream function.
57
+ * We return a wrapped version that adds governance.
58
+ */
59
+ wrapStreamFn(originalStreamFn, config) {
60
+ // Lazy-initialize governor on first call
61
+ const getGovernor = lazyGovernor(config);
62
+ return (model, context, options) => {
63
+ return governedStreamLazy(getGovernor, originalStreamFn, model, context, options);
64
+ };
65
+ },
66
+ };
67
+ api.registerProvider(plugin);
68
+ }
69
+ /**
70
+ * Programmatic API for non-OpenClaw usage.
71
+ *
72
+ * Use this when integrating usertrust governance into a custom
73
+ * pi-ai setup without the full OpenClaw plugin system.
74
+ *
75
+ * ```ts
76
+ * import { createGovernedStreamFn } from "@usertrust/openclaw";
77
+ *
78
+ * const governed = await createGovernedStreamFn(myStreamFn, {
79
+ * budget: 100_000,
80
+ * dryRun: true,
81
+ * });
82
+ *
83
+ * for await (const event of governed("claude-sonnet-4-6", context)) {
84
+ * // events flow through with governance applied
85
+ * }
86
+ * ```
87
+ */
88
+ export async function createGovernedStreamFn(streamFn, config) {
89
+ const gov = await initGovernor(config);
90
+ const governedStreamFn = wrapStreamWithGovernance(streamFn, gov);
91
+ return { governedStreamFn, governor: gov };
92
+ }
93
+ /**
94
+ * Get the active governor instance.
95
+ * Returns null if the plugin hasn't been initialized yet.
96
+ */
97
+ export function getGovernor() {
98
+ return governor;
99
+ }
100
+ /**
101
+ * Graceful shutdown — call this when OpenClaw exits.
102
+ * Voids all pending holds and flushes the audit chain.
103
+ */
104
+ export async function shutdown() {
105
+ if (governor != null) {
106
+ await governor.destroy();
107
+ governor = null;
108
+ initPromise = null;
109
+ }
110
+ }
111
+ // ── Internal ──
112
+ /** Module-level promise to prevent concurrent initialization race. */
113
+ let initPromise = null;
114
+ function initGovernor(config) {
115
+ if (governor != null) {
116
+ return Promise.resolve(governor);
117
+ }
118
+ // Memoize the init promise to prevent TOCTOU race — two concurrent
119
+ // callers both see governor == null but only one createGovernor runs.
120
+ if (initPromise == null) {
121
+ initPromise = createGovernor({
122
+ budget: config.budget,
123
+ ...(config.dryRun != null ? { dryRun: config.dryRun } : {}),
124
+ ...(config.configPath != null ? { configPath: config.configPath } : {}),
125
+ ...(config.proxy != null ? { proxy: config.proxy } : {}),
126
+ ...(config.proxyKey != null ? { key: config.proxyKey } : {}),
127
+ }).then((gov) => {
128
+ governor = gov;
129
+ return gov;
130
+ });
131
+ }
132
+ return initPromise;
133
+ }
134
+ function lazyGovernor(config) {
135
+ let promise = null;
136
+ return () => {
137
+ if (promise == null) {
138
+ promise = initGovernor(config);
139
+ }
140
+ return promise;
141
+ };
142
+ }
143
+ async function* governedStreamLazy(getGovernor, streamFn, model, context, options) {
144
+ const gov = await getGovernor();
145
+ const governed = wrapStreamWithGovernance(streamFn, gov);
146
+ yield* governed(model, context, options);
147
+ }
148
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * stream-governor.ts — Governed Stream Wrapper for pi-ai
3
+ *
4
+ * Wraps a pi-ai stream function with usertrust governance:
5
+ * 1. Before stream: authorize (budget check, PENDING hold)
6
+ * 2. During stream: forward events, accumulate token usage
7
+ * 3. After stream: settle with actual cost
8
+ * 4. On error: abort (VOID the hold)
9
+ *
10
+ * The wrapped function has the same signature as the original —
11
+ * it's a transparent middleware layer.
12
+ */
13
+ import type { Governor } from "usertrust";
14
+ import type { StreamContext, StreamFn } from "./types.js";
15
+ /**
16
+ * Wrap a pi-ai stream function with usertrust governance.
17
+ *
18
+ * Returns a new stream function with the same signature. Every call:
19
+ * - Checks budget and creates a PENDING hold
20
+ * - Forwards all stream events unchanged
21
+ * - Settles with actual token usage on completion
22
+ * - Voids the hold on error
23
+ *
24
+ * The governance receipt is emitted as a custom `usertrust:receipt`
25
+ * property on the `done` event for consumers that want it.
26
+ */
27
+ export declare function wrapStreamWithGovernance(streamFn: StreamFn, governor: Governor): StreamFn;
28
+ /**
29
+ * Wrap a non-streaming completion function with governance.
30
+ *
31
+ * For pi-ai's `completeSimple()` / `complete()` functions that
32
+ * return a Promise instead of an async iterable.
33
+ */
34
+ export declare function wrapCompleteWithGovernance<T extends {
35
+ usage?: {
36
+ inputTokens: number;
37
+ outputTokens: number;
38
+ };
39
+ }>(completeFn: (model: string, context: StreamContext, options?: Record<string, unknown>) => Promise<T>, governor: Governor): (model: string, context: StreamContext, options?: Record<string, unknown>) => Promise<T>;
40
+ //# sourceMappingURL=stream-governor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-governor.d.ts","sourceRoot":"","sources":["../src/stream-governor.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAiB,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEzD,OAAO,KAAK,EAAE,aAAa,EAAe,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAQzF;AA+CD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACzC,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAEnE,UAAU,EAAE,CACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,OAAO,CAAC,CAAC,CAAC,EACf,QAAQ,EAAE,QAAQ,GAChB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAsC1F"}
@@ -0,0 +1,94 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Usertools, Inc.
3
+ import { createAccumulator } from "./token-extractor.js";
4
+ /**
5
+ * Wrap a pi-ai stream function with usertrust governance.
6
+ *
7
+ * Returns a new stream function with the same signature. Every call:
8
+ * - Checks budget and creates a PENDING hold
9
+ * - Forwards all stream events unchanged
10
+ * - Settles with actual token usage on completion
11
+ * - Voids the hold on error
12
+ *
13
+ * The governance receipt is emitted as a custom `usertrust:receipt`
14
+ * property on the `done` event for consumers that want it.
15
+ */
16
+ export function wrapStreamWithGovernance(streamFn, governor) {
17
+ return (model, context, options) => {
18
+ return governedStream(streamFn, governor, model, context, options);
19
+ };
20
+ }
21
+ async function* governedStream(streamFn, governor, model, context, options) {
22
+ // 1. Authorize — budget check, policy gate, PENDING hold
23
+ const auth = await governor.authorize({
24
+ model,
25
+ messages: context.messages,
26
+ ...(context.maxTokens != null ? { maxOutputTokens: context.maxTokens } : {}),
27
+ params: {
28
+ ...(context.temperature != null ? { temperature: context.temperature } : {}),
29
+ },
30
+ });
31
+ // 2. Stream with token accumulation
32
+ const accumulator = createAccumulator();
33
+ try {
34
+ const stream = streamFn(model, context, options);
35
+ for await (const event of stream) {
36
+ accumulator.update(event);
37
+ yield event;
38
+ }
39
+ // 3. Settle — POST actual cost
40
+ const usage = accumulator.result();
41
+ await governor.settle(auth, {
42
+ ...(usage.usageReported
43
+ ? { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens }
44
+ : {}),
45
+ chunksDelivered: usage.chunksDelivered,
46
+ usageSource: usage.usageReported ? "provider" : "estimated",
47
+ });
48
+ }
49
+ catch (err) {
50
+ // 4. Abort — VOID the hold (catch abort errors to preserve original)
51
+ await governor.abort(auth, err).catch(() => { });
52
+ throw err;
53
+ }
54
+ }
55
+ /**
56
+ * Wrap a non-streaming completion function with governance.
57
+ *
58
+ * For pi-ai's `completeSimple()` / `complete()` functions that
59
+ * return a Promise instead of an async iterable.
60
+ */
61
+ export function wrapCompleteWithGovernance(completeFn, governor) {
62
+ return async (model, context, options) => {
63
+ // 1. Authorize
64
+ const auth = await governor.authorize({
65
+ model,
66
+ messages: context.messages,
67
+ ...(context.maxTokens != null ? { maxOutputTokens: context.maxTokens } : {}),
68
+ params: {
69
+ ...(context.temperature != null ? { temperature: context.temperature } : {}),
70
+ },
71
+ });
72
+ try {
73
+ // 2. Execute
74
+ const result = await completeFn(model, context, options);
75
+ // 3. Settle with actual usage if available
76
+ await governor.settle(auth, {
77
+ ...(result.usage != null
78
+ ? {
79
+ inputTokens: result.usage.inputTokens,
80
+ outputTokens: result.usage.outputTokens,
81
+ usageSource: "provider",
82
+ }
83
+ : { usageSource: "estimated" }),
84
+ });
85
+ return result;
86
+ }
87
+ catch (err) {
88
+ // 4. Abort (catch abort errors to preserve original)
89
+ await governor.abort(auth, err).catch(() => { });
90
+ throw err;
91
+ }
92
+ };
93
+ }
94
+ //# sourceMappingURL=stream-governor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-governor.js","sourceRoot":"","sources":["../src/stream-governor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * token-extractor.ts — pi-ai Stream Token Extraction
3
+ *
4
+ * Extracts token usage from pi-ai's normalized stream events.
5
+ * pi-ai reports usage on `done` and `error` events, unlike provider
6
+ * SDKs which scatter it across multiple event types.
7
+ */
8
+ import type { StreamEvent, StreamUsage } from "./types.js";
9
+ /** Accumulated usage from a pi-ai stream. */
10
+ export interface AccumulatedUsage {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ chunksDelivered: number;
14
+ usageReported: boolean;
15
+ }
16
+ export declare function extractUsageFromEvent(event: StreamEvent): StreamUsage | null;
17
+ /**
18
+ * Creates a token accumulator for tracking usage across a stream.
19
+ * Call `update()` for each event and `result()` when done.
20
+ */
21
+ export declare function createAccumulator(): {
22
+ update(event: StreamEvent): void;
23
+ result(): AccumulatedUsage;
24
+ };
25
+ //# sourceMappingURL=token-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-extractor.d.ts","sourceRoot":"","sources":["../src/token-extractor.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3D,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACvB;AAaD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAgB5E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI;IACpC,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,MAAM,IAAI,gBAAgB,CAAC;CAC3B,CAsBA"}
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Usertools, Inc.
3
+ /**
4
+ * Extract token usage from a pi-ai stream event.
5
+ * Returns non-zero usage only for `done` and `error` events.
6
+ */
7
+ /** Max tokens to accept from a provider (prevents Infinity/overflow in cost math). */
8
+ const MAX_TOKENS = 2_000_000;
9
+ function clampTokens(n) {
10
+ return Math.min(Math.max(0, n), MAX_TOKENS);
11
+ }
12
+ export function extractUsageFromEvent(event) {
13
+ if (event.type === "done" && event.usage != null) {
14
+ return {
15
+ ...event.usage,
16
+ inputTokens: clampTokens(event.usage.inputTokens),
17
+ outputTokens: clampTokens(event.usage.outputTokens),
18
+ };
19
+ }
20
+ if (event.type === "error" && event.usage != null) {
21
+ return {
22
+ ...event.usage,
23
+ inputTokens: clampTokens(event.usage.inputTokens),
24
+ outputTokens: clampTokens(event.usage.outputTokens),
25
+ };
26
+ }
27
+ return null;
28
+ }
29
+ /**
30
+ * Creates a token accumulator for tracking usage across a stream.
31
+ * Call `update()` for each event and `result()` when done.
32
+ */
33
+ export function createAccumulator() {
34
+ let inputTokens = 0;
35
+ let outputTokens = 0;
36
+ let chunksDelivered = 0;
37
+ let usageReported = false;
38
+ return {
39
+ update(event) {
40
+ chunksDelivered++;
41
+ const usage = extractUsageFromEvent(event);
42
+ if (usage != null) {
43
+ inputTokens = usage.inputTokens;
44
+ outputTokens = usage.outputTokens;
45
+ usageReported = true;
46
+ }
47
+ },
48
+ result() {
49
+ return { inputTokens, outputTokens, chunksDelivered, usageReported };
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=token-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-extractor.js","sourceRoot":"","sources":["../src/token-extractor.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAoBjC;;;GAGG;AACH,sFAAsF;AACtF,MAAM,UAAU,GAAG,SAAS,CAAC;AAE7B,SAAS,WAAW,CAAC,CAAS;IAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAkB;IACvD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QAClD,OAAO;YACN,GAAG,KAAK,CAAC,KAAK;YACd,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;YACjD,YAAY,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC;SACnD,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QACnD,OAAO;YACN,GAAG,KAAK,CAAC,KAAK;YACd,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;YACjD,YAAY,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC;SACnD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAIhC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,OAAO;QACN,MAAM,CAAC,KAAkB;YACxB,eAAe,EAAE,CAAC;YAElB,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBACnB,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;gBAChC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;gBAClC,aAAa,GAAG,IAAI,CAAC;YACtB,CAAC;QACF,CAAC;QAED,MAAM;YACL,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC;QACtE,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * types.ts — OpenClaw Plugin API Types
3
+ *
4
+ * Type definitions for the OpenClaw plugin system and pi-ai streaming
5
+ * events. These mirror the OpenClaw/pi-ai interfaces without requiring
6
+ * the packages at compile time.
7
+ */
8
+ export interface StreamUsage {
9
+ inputTokens: number;
10
+ outputTokens: number;
11
+ cacheReadTokens?: number;
12
+ cacheWriteTokens?: number;
13
+ }
14
+ export interface TextDeltaEvent {
15
+ type: "text_delta";
16
+ text: string;
17
+ }
18
+ export interface TextStartEvent {
19
+ type: "text_start";
20
+ }
21
+ export interface TextEndEvent {
22
+ type: "text_end";
23
+ }
24
+ export interface ThinkingDeltaEvent {
25
+ type: "thinking_delta";
26
+ text: string;
27
+ }
28
+ export interface ThinkingStartEvent {
29
+ type: "thinking_start";
30
+ }
31
+ export interface ThinkingEndEvent {
32
+ type: "thinking_end";
33
+ }
34
+ export interface ToolCallStartEvent {
35
+ type: "toolcall_start";
36
+ name: string;
37
+ id: string;
38
+ }
39
+ export interface ToolCallDeltaEvent {
40
+ type: "toolcall_delta";
41
+ args: string;
42
+ }
43
+ export interface ToolCallEndEvent {
44
+ type: "toolcall_end";
45
+ }
46
+ export interface StartEvent {
47
+ type: "start";
48
+ }
49
+ export interface DoneEvent {
50
+ type: "done";
51
+ stopReason: "stop" | "toolUse" | "length" | "error" | "aborted";
52
+ usage: StreamUsage;
53
+ }
54
+ export interface ErrorEvent {
55
+ type: "error";
56
+ error: unknown;
57
+ usage?: StreamUsage;
58
+ }
59
+ export type StreamEvent = StartEvent | TextStartEvent | TextDeltaEvent | TextEndEvent | ThinkingStartEvent | ThinkingDeltaEvent | ThinkingEndEvent | ToolCallStartEvent | ToolCallDeltaEvent | ToolCallEndEvent | DoneEvent | ErrorEvent;
60
+ export interface OpenClawPluginApi {
61
+ registerTool(tool: unknown): void;
62
+ registerProvider(provider: unknown): void;
63
+ registerChannel(channel: unknown): void;
64
+ registerHttpRoute(route: unknown): void;
65
+ }
66
+ /** Configuration passed to the plugin from openclaw.json. */
67
+ export interface UsertrustPluginConfig {
68
+ budget: number;
69
+ tier?: "free" | "mini" | "pro" | "mega" | "ultra";
70
+ dryRun?: boolean;
71
+ configPath?: string;
72
+ proxy?: string;
73
+ proxyKey?: string;
74
+ }
75
+ export interface StreamContext {
76
+ messages: unknown[];
77
+ model: string;
78
+ maxTokens?: number;
79
+ temperature?: number;
80
+ [key: string]: unknown;
81
+ }
82
+ export type StreamFn = (model: string, context: StreamContext, options?: Record<string, unknown>) => AsyncIterable<StreamEvent>;
83
+ /** Receipt attached to governed stream events. */
84
+ export interface GovernedStreamMeta {
85
+ transferId: string;
86
+ estimatedCost: number;
87
+ model: string;
88
+ }
89
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAIH,MAAM,WAAW,WAAW;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,YAAY,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,gBAAgB,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,cAAc,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,cAAc,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAChE,KAAK,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GACpB,UAAU,GACV,cAAc,GACd,cAAc,GACd,YAAY,GACZ,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,SAAS,GACT,UAAU,CAAC;AAId,MAAM,WAAW,iBAAiB;IACjC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1C,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACxC;AAED,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,QAAQ,GAAG,CACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,aAAa,CAAC,WAAW,CAAC,CAAC;AAEhC,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACd"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Usertools, Inc.
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC"}
@@ -0,0 +1,59 @@
1
+ {
2
+ "id": "usertrust",
3
+ "name": "usertrust",
4
+ "description": "Financial governance for AI agents. Every LLM call becomes an immutable, auditable transaction with budget enforcement, policy gates, and hash-chained audit trails.",
5
+ "kind": "provider",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "budget": {
11
+ "type": "number",
12
+ "description": "Token budget in usertokens. 1 usertoken = $0.0001."
13
+ },
14
+ "tier": {
15
+ "type": "string",
16
+ "enum": ["free", "mini", "pro", "mega", "ultra"],
17
+ "description": "Budget tier for policy evaluation."
18
+ },
19
+ "dryRun": {
20
+ "type": "boolean",
21
+ "description": "Skip TigerBeetle ledger — audit-chain-only mode."
22
+ },
23
+ "configPath": {
24
+ "type": "string",
25
+ "description": "Path to usertrust.config.json. Defaults to .usertrust/usertrust.config.json."
26
+ },
27
+ "proxy": {
28
+ "type": "string",
29
+ "description": "Remote governance proxy URL."
30
+ },
31
+ "proxyKey": {
32
+ "type": "string",
33
+ "description": "API key for the governance proxy."
34
+ }
35
+ },
36
+ "required": ["budget"]
37
+ },
38
+ "uiHints": {
39
+ "budget": {
40
+ "label": "Token Budget",
41
+ "placeholder": "100000"
42
+ },
43
+ "tier": {
44
+ "label": "Budget Tier",
45
+ "placeholder": "pro"
46
+ },
47
+ "dryRun": {
48
+ "label": "Dry Run Mode"
49
+ },
50
+ "proxy": {
51
+ "label": "Proxy URL",
52
+ "placeholder": "https://proxy.usertools.ai"
53
+ },
54
+ "proxyKey": {
55
+ "label": "Proxy API Key",
56
+ "sensitive": true
57
+ }
58
+ }
59
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "usertrust-openclaw",
3
+ "version": "1.1.1",
4
+ "description": "usertrust governance plugin for OpenClaw. Budget enforcement, audit trails, and policy gates for every LLM call — zero code changes.",
5
+ "license": "Apache-2.0",
6
+ "author": "Usertools, Inc. <hello@usertools.ai> (https://usertrust.ai)",
7
+ "homepage": "https://usertrust.ai",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/usertools-ai/usertrust.git",
11
+ "directory": "packages/openclaw"
12
+ },
13
+ "bugs": "https://github.com/usertools-ai/usertrust/issues",
14
+ "keywords": [
15
+ "openclaw",
16
+ "usertrust",
17
+ "ai",
18
+ "governance",
19
+ "budget",
20
+ "audit",
21
+ "llm",
22
+ "plugin"
23
+ ],
24
+ "type": "module",
25
+ "main": "dist/index.js",
26
+ "types": "dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.js",
30
+ "types": "./dist/index.d.ts"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "prepublishOnly": "tsc"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "openclaw.plugin.json"
40
+ ],
41
+ "dependencies": {
42
+ "usertrust": "*"
43
+ },
44
+ "peerDependencies": {
45
+ "@mariozechner/pi-ai": ">=0.1.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@mariozechner/pi-ai": {
49
+ "optional": true
50
+ }
51
+ }
52
+ }