@intentproof/sdk 0.1.0 → 0.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.
- package/README.md +71 -138
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
## **Logs narrate; IntentProof gives you proof.**
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turn your function calls into **verifiable** execution records designed to be reconciled.
|
|
4
|
+
Observability captures what happened. **IntentProof** tells you whether it matched what was **meant to happen**.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Every wrapped call emits one **`ExecutionEvent`** containing:
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
- **`intent`**: what this invocation was meant to prove
|
|
9
|
+
- **`action`**: the stable operation id for this step
|
|
10
|
+
- **`status`**: success or error
|
|
11
|
+
- **`inputs`** and **`output`**: what the runtime saw going in and coming out
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
## Why this matters
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
Modern systems—especially AI agents—do not only compute; they act:
|
|
16
|
+
issuing refunds, sending emails, updating databases.
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
When something goes wrong, logs tell you what ran.
|
|
19
|
+
They don't tell you:
|
|
20
|
+
|
|
21
|
+
- what was supposed to happen
|
|
22
|
+
- whether all steps completed
|
|
23
|
+
- whether systems ended up in a consistent state
|
|
24
|
+
|
|
25
|
+
**IntentProof** exists to bridge that gap.
|
|
26
|
+
|
|
27
|
+
It records intent alongside execution so systems can be verified, not just observed.
|
|
14
28
|
|
|
15
29
|
## Requirements
|
|
16
30
|
|
|
@@ -24,124 +38,101 @@ npm install @intentproof/sdk
|
|
|
24
38
|
|
|
25
39
|
## Quick start
|
|
26
40
|
|
|
27
|
-
Below, the object printed from **`getEvents()`** is the **proof artifact** for a single call: intent and action you chose at wrap time, inputs and output the runtime saw, plus timing and id.
|
|
28
|
-
|
|
29
|
-
Use a **`MemoryExporter`** so you can inspect that record immediately (the default singleton client also uses memory, but you need your own instance to call **`getEvents()`**).
|
|
30
|
-
|
|
31
41
|
```ts
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
const memory = new MemoryExporter({ maxEvents: 50 });
|
|
35
|
-
const client = createIntentProofClient({ exporters: [memory] });
|
|
42
|
+
import { client } from "@intentproof/sdk";
|
|
36
43
|
|
|
37
|
-
const
|
|
38
|
-
{ intent: "
|
|
39
|
-
(
|
|
44
|
+
const refund = client.wrap(
|
|
45
|
+
{ intent: "Initiate refund", action: "stripe.refunds.create" },
|
|
46
|
+
async (input) => stripe.refunds.create(input),
|
|
40
47
|
);
|
|
41
|
-
|
|
42
|
-
const sum = add(2, 3);
|
|
43
|
-
console.assert(sum === 5);
|
|
44
|
-
|
|
45
|
-
const [event] = memory.getEvents();
|
|
46
|
-
console.log(event);
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{
|
|
53
|
-
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
54
|
-
"intent": "demo",
|
|
55
|
-
"action": "math.add",
|
|
56
|
-
"inputs": [2, 3],
|
|
57
|
-
"status": "ok",
|
|
58
|
-
"output": 5,
|
|
59
|
-
"startedAt": "2026-05-02T12:00:00.000Z",
|
|
60
|
-
"completedAt": "2026-05-02T12:00:00.003Z",
|
|
61
|
-
"durationMs": 3
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
- **`id`** — Random UUID for this invocation.
|
|
66
|
-
- **`inputs` / `output`** — Produced via **`snapshot()`** (see `SerializeOptions` / `WrapOptions`) unless you override with **`captureInput`** / **`captureOutput`**.
|
|
67
|
-
- **`correlationId`** — Omitted here; present when async context or wrap options supply one (see examples below).
|
|
68
|
-
- **`attributes`** — Omitted when empty; otherwise merged from client **`defaultAttributes`** and per-wrap **`attributes`**.
|
|
50
|
+
Each refund call emits one **`ExecutionEvent`** with the **`intent`** and **`action`** you chose, the **`inputs`** and **`output`** (or **`error`** + **`status: "error"`**), and timing fields—an execution record you can inspect, export, or verify later.
|
|
69
51
|
|
|
70
52
|
## `IntentProofClient` API
|
|
71
53
|
|
|
54
|
+
|
|
72
55
|
| Member | Description |
|
|
73
56
|
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
57
|
| **`constructor(config?)`** | Creates a client. Default exporters: a single **`MemoryExporter`** if you omit **`config.exporters`**. |
|
|
75
58
|
| **`configure(config)`** | Re-applies **`IntentProofConfig`** fields (exporters, error hook, defaults, stack policy). |
|
|
76
59
|
| **`wrap(options, fn)`** | Returns a function that records one **`ExecutionEvent`** per call (sync or async). **`options`** must satisfy **`assertWrapOptionsShape`** (`intent` / `action` non-empty strings, etc.). |
|
|
77
|
-
| **`flush()`** | Awaits
|
|
78
|
-
| **`shutdown()`** |
|
|
79
|
-
| **`getCorrelationId()`** | Returns the correlation
|
|
80
|
-
| **`withCorrelation(fn)`** | Runs **`fn`** with a **fresh UUID** as correlation
|
|
60
|
+
| **`flush()`** | Awaits **`flush()`** on every **`Exporter`** that implements it, in parallel. |
|
|
61
|
+
| **`shutdown()`** | For each **`Exporter`**, awaits **`shutdown()`** if implemented, otherwise **`flush()`** if implemented. |
|
|
62
|
+
| **`getCorrelationId()`** | Returns the correlation ID from **`AsyncLocalStorage`**, if any. |
|
|
63
|
+
| **`withCorrelation(fn)`** | Runs **`fn`** with a **fresh UUID** as correlation ID for nested wraps. |
|
|
81
64
|
| **`withCorrelation(id, fn)`** | Runs **`fn`** with **`id`** trimmed; blank / whitespace-only **`id`** falls back to a UUID. |
|
|
82
65
|
|
|
66
|
+
|
|
83
67
|
### Module-level helpers (same module as the client)
|
|
84
68
|
|
|
85
69
|
These use the same async correlation store as **`IntentProofClient`** instances:
|
|
86
70
|
|
|
71
|
+
|
|
87
72
|
| Export | Description |
|
|
88
73
|
| -------------------------------------- | ---------------------------------------------------------------------- |
|
|
89
74
|
| **`createIntentProofClient(config?)`** | New isolated client (tests, workers, multi-tenant). |
|
|
90
75
|
| **`getIntentProofClient()`** | Lazy singleton used by **`client`**. |
|
|
91
76
|
| **`client`** | Default singleton instance. |
|
|
92
77
|
| **`getCorrelationId()`** | Same behavior as the instance method. |
|
|
93
|
-
| **`runWithCorrelationId(id, fn)`** | Requires a **non-empty** correlation
|
|
94
|
-
| **`assertCorrelationId(id)`** | Runtime assertion for correlation
|
|
78
|
+
| **`runWithCorrelationId(id, fn)`** | Requires a **non-empty** correlation ID after trim; throws if invalid. |
|
|
79
|
+
| **`assertCorrelationId(id)`** | Runtime assertion for correlation ID shape. |
|
|
95
80
|
| **`assertWrapOptionsShape(options)`** | Runtime validation for **`WrapOptions`**. |
|
|
96
81
|
|
|
82
|
+
|
|
97
83
|
## `ExecutionEvent` fields
|
|
98
84
|
|
|
99
|
-
| Field
|
|
100
|
-
|
|
|
101
|
-
| **`id`**
|
|
102
|
-
| **`correlationId`**
|
|
103
|
-
| **`intent`**
|
|
104
|
-
| **`action`**
|
|
105
|
-
| **`inputs`**
|
|
106
|
-
| **`output`**
|
|
107
|
-
| **`error`**
|
|
108
|
-
| **`status`**
|
|
109
|
-
| **`startedAt`**
|
|
110
|
-
| **`completedAt`**
|
|
111
|
-
| **`durationMs`**
|
|
112
|
-
| **`attributes`**
|
|
85
|
+
| Field | Description |
|
|
86
|
+
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| **`id`** | Unique event id (UUID). |
|
|
88
|
+
| **`correlationId`** | Request or trace correlation ID when present—usually from context or **`WrapOptions`**. |
|
|
89
|
+
| **`intent`** | Human-readable label for what this invocation is meant to prove (outcome, policy goal, or domain). |
|
|
90
|
+
| **`action`** | Stable operation id for this step (often dotted or namespaced). |
|
|
91
|
+
| **`inputs`** | JSON-safe snapshot of call arguments (default) or **`captureInput`** result. |
|
|
92
|
+
| **`output`** | JSON-safe return value or **`captureOutput`** result on success. When **`status`** is **`"error"`**, set only if **`captureError`** returned a value. |
|
|
93
|
+
| **`error`** | On failure: **`name`**, **`message`**, and optional **`stack`** (see **`includeErrorStack`**). |
|
|
94
|
+
| **`status`** | **`"ok"`** if the wrapped call completed normally; **`"error"`** if it threw. |
|
|
95
|
+
| **`startedAt`** | Start time (ISO 8601). |
|
|
96
|
+
| **`completedAt`** | Completion time (ISO 8601). |
|
|
97
|
+
| **`durationMs`** | Wall time between start and completion, in milliseconds. |
|
|
98
|
+
| **`attributes`** | Optional plain record (string / number / boolean values only), merged from client defaults and wrap options. |
|
|
99
|
+
|
|
113
100
|
|
|
114
101
|
## `WrapOptions` and `IntentProofConfig`
|
|
115
102
|
|
|
116
103
|
### `WrapOptions` (passed to **`wrap`**)
|
|
117
104
|
|
|
105
|
+
|
|
118
106
|
| Field | Description |
|
|
119
107
|
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
120
108
|
| **`intent`**, **`action`** | Required, non-empty after trim. |
|
|
121
|
-
| **`correlationId`** | Optional; when set, non-empty after trim. Otherwise the active context
|
|
109
|
+
| **`correlationId`** | Optional; when set, non-empty after trim. Otherwise the active correlation ID from context is used, if any. |
|
|
122
110
|
| **`attributes`** | Per-invocation dimensions merged over **`defaultAttributes`**. |
|
|
123
111
|
| **`captureInput`**, **`captureOutput`**, **`captureError`** | Optional hooks to replace default **`snapshot`** behavior for inputs, success output, or error-side extra **`output`**. |
|
|
124
112
|
| **`includeErrorStack`** | When `false`, omit **`error.stack`** for this wrap (overrides client default). |
|
|
125
113
|
| **`maxDepth`**, **`maxKeys`**, **`redactKeys`**, **`maxStringLength`** | Forwarded to **`snapshot`** for inputs and outputs (see **`SerializeOptions`** in types). |
|
|
126
114
|
|
|
115
|
+
|
|
127
116
|
### `IntentProofConfig` (constructor / **`configure`**)
|
|
128
117
|
|
|
129
|
-
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
| **`
|
|
133
|
-
| **`
|
|
134
|
-
| **`
|
|
118
|
+
|
|
119
|
+
| Field | Description |
|
|
120
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| **`exporters`** | Ordered list of **`Exporter`** instances; each receives every **`ExecutionEvent`**. |
|
|
122
|
+
| **`onExporterError`** | Called when any exporter’s **`export()`** throws or returns a rejected promise. Defaults to **`console.error`**. |
|
|
123
|
+
| **`defaultAttributes`** | Merged into every event’s **`attributes`** (wrap-specific attributes win on key collision). |
|
|
124
|
+
| **`includeErrorStack`** | Default `true`; set `false` in production if stacks must not leave the trust zone. |
|
|
125
|
+
|
|
135
126
|
|
|
136
127
|
---
|
|
137
128
|
|
|
138
129
|
## Examples
|
|
139
130
|
|
|
140
|
-
### 1 — Refund
|
|
131
|
+
### 1 — Refund and customer receipt
|
|
141
132
|
|
|
142
|
-
Support approves **order `ORD-1042`**. Your service
|
|
133
|
+
Support approves **order `ORD-1042`**. Your service creates the **Stripe refund**, then emails the customer a receipt. **`runWithCorrelationId`** ties both calls to **`req_refund_ord_1042`**. Each **`wrap`** defines its own **`intent`** (the outcome you are proving for that step) and **`action`** (how it is done); **`correlationId`** is what stitches them together.
|
|
143
134
|
|
|
144
|
-
**`captureInput
|
|
135
|
+
**`captureInput`** / **`captureOutput`** trim each record to the fields you want in proof (refund id, amounts, message id)—not full vendor payloads.
|
|
145
136
|
|
|
146
137
|
```ts
|
|
147
138
|
const createRefund = client.wrap(
|
|
@@ -221,35 +212,6 @@ const sendRefundReceipt = client.wrap(
|
|
|
221
212
|
}) => ({ messageId: "msg_49401_sample", status: "queued" as const }),
|
|
222
213
|
);
|
|
223
214
|
|
|
224
|
-
const notifyOpsRefund = client.wrap(
|
|
225
|
-
{
|
|
226
|
-
intent: "Surface the completed refund to billing operations for review",
|
|
227
|
-
action: "slack.operations.refund_posted",
|
|
228
|
-
attributes: { channel: "slack", step: "notify_ops" },
|
|
229
|
-
captureInput: (args) => {
|
|
230
|
-
const [p] = args as [{ refundId: string; orderId: string; amountCents: number }];
|
|
231
|
-
return {
|
|
232
|
-
refundId: p.refundId,
|
|
233
|
-
orderId: p.orderId,
|
|
234
|
-
amountCents: p.amountCents,
|
|
235
|
-
};
|
|
236
|
-
},
|
|
237
|
-
captureOutput: (result) => {
|
|
238
|
-
const r = result as {
|
|
239
|
-
ok: boolean;
|
|
240
|
-
channel: string;
|
|
241
|
-
ts: string;
|
|
242
|
-
};
|
|
243
|
-
return { ok: r.ok, channel: r.channel, ts: r.ts };
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
(p: { refundId: string; orderId: string; amountCents: number }) => ({
|
|
247
|
-
ok: true,
|
|
248
|
-
channel: "#billing-alerts",
|
|
249
|
-
ts: "1714648800.000100",
|
|
250
|
-
}),
|
|
251
|
-
);
|
|
252
|
-
|
|
253
215
|
await runWithCorrelationId("req_refund_ord_1042", async () => {
|
|
254
216
|
const refund = createRefund({
|
|
255
217
|
paymentIntentId: "pi_3SAMPLEabcdefghijklmnop",
|
|
@@ -264,17 +226,10 @@ await runWithCorrelationId("req_refund_ord_1042", async () => {
|
|
|
264
226
|
amountCents: refund.amountCents,
|
|
265
227
|
}),
|
|
266
228
|
);
|
|
267
|
-
await Promise.resolve(
|
|
268
|
-
notifyOpsRefund({
|
|
269
|
-
refundId: refund.id,
|
|
270
|
-
orderId: "ORD-1042",
|
|
271
|
-
amountCents: refund.amountCents,
|
|
272
|
-
}),
|
|
273
|
-
);
|
|
274
229
|
});
|
|
275
230
|
```
|
|
276
231
|
|
|
277
|
-
Emitted
|
|
232
|
+
Emitted **`ExecutionEvent`** values (same **`correlationId`** on each; distinct **`intent`** per step; **`id`** / timestamps omitted):
|
|
278
233
|
|
|
279
234
|
```json
|
|
280
235
|
[
|
|
@@ -318,28 +273,6 @@ Emitted events (same **`correlationId`** on each; distinct **`intent`** per step
|
|
|
318
273
|
"channel": "email",
|
|
319
274
|
"step": "notify_customer"
|
|
320
275
|
}
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
"correlationId": "req_refund_ord_1042",
|
|
324
|
-
"intent": "Surface the completed refund to billing operations for review",
|
|
325
|
-
"action": "slack.operations.refund_posted",
|
|
326
|
-
"inputs": {
|
|
327
|
-
"refundId": "re_3SAMPLEabcdefghijklmnop",
|
|
328
|
-
"orderId": "ORD-1042",
|
|
329
|
-
"amountCents": 4999
|
|
330
|
-
},
|
|
331
|
-
"status": "ok",
|
|
332
|
-
"output": {
|
|
333
|
-
"ok": true,
|
|
334
|
-
"channel": "#billing-alerts",
|
|
335
|
-
"ts": "1714648800.000100"
|
|
336
|
-
},
|
|
337
|
-
"attributes": {
|
|
338
|
-
"service": "billing-api",
|
|
339
|
-
"env": "test",
|
|
340
|
-
"channel": "slack",
|
|
341
|
-
"step": "notify_ops"
|
|
342
|
-
}
|
|
343
276
|
}
|
|
344
277
|
]
|
|
345
278
|
```
|
|
@@ -385,7 +318,7 @@ try {
|
|
|
385
318
|
}
|
|
386
319
|
```
|
|
387
320
|
|
|
388
|
-
### 3 — Proof delivery over HTTP (same
|
|
321
|
+
### 3 — Proof delivery over HTTP (same **`ExecutionEvent`** shape)
|
|
389
322
|
|
|
390
323
|
**`HttpExporter`** POSTs the same **`ExecutionEvent`** your verifiers see in memory—here alongside **`MemoryExporter`** so tests can assert the wire without a real collector. The request uses **`credentials: "omit"`**; the body is **`{ intentproof: "1", event: … }`** (see exporter implementation). For authenticated collectors, pass **`headers`** (e.g. **`Authorization`**, API keys) — see [Security](#security).
|
|
391
324
|
|
|
@@ -408,9 +341,9 @@ runProbe();
|
|
|
408
341
|
|
|
409
342
|
## Security
|
|
410
343
|
|
|
411
|
-
|
|
344
|
+
Every **`ExecutionEvent`** you emit is data you may ship off-process. Treat them like audit-grade execution records: they can include PII, secrets, stack traces, and business identifiers depending on your **`snapshot`** / **`capture*`** hooks.
|
|
412
345
|
|
|
413
|
-
- **Minimize payload:** Use **`redactKeys`**, **`maxDepth
|
|
346
|
+
- **Minimize payload:** Use **`redactKeys`**, **`maxDepth`** / **`maxKeys`** / **`maxStringLength`**, and narrow **`captureInput`** / **`captureOutput`** / **`captureError`** so proof records contain only what verifiers need.
|
|
414
347
|
- **Stacks:** Set **`includeErrorStack: false`** on the client (or per wrap) when traces must not leave your trust zone.
|
|
415
348
|
- **HTTP ingest:** Keep collector **`url`** and any redirect behavior under **trusted configuration** (avoid SSRF if URLs were ever influenced by untrusted input). Prefer **HTTPS** and **short-lived credentials** end-to-end.
|
|
416
349
|
- **`HttpExporter` auth:** Pass credentials in **`headers`** (for example **`Authorization: Bearer …`**, **`x-api-key`**, or whatever your collector expects). The SDK does **not** log header values; use short-lived tokens and scope them to ingest only.
|
|
@@ -431,7 +364,7 @@ Custom **`body`** serializers: if **`body(event)`** throws, **`HttpExporter`** n
|
|
|
431
364
|
|
|
432
365
|
This repository is an npm workspace; the publishable package is [`packages/sdk`](packages/sdk).
|
|
433
366
|
|
|
434
|
-
Requires **Node.js 22
|
|
367
|
+
Requires **Node.js** 22 or newer (see `.nvmrc` and workspace `engines`).
|
|
435
368
|
|
|
436
369
|
```bash
|
|
437
370
|
npm ci
|
|
@@ -440,4 +373,4 @@ npm run ci
|
|
|
440
373
|
|
|
441
374
|
## License
|
|
442
375
|
|
|
443
|
-
Apache-2.0 (see `LICENSE` at the repository root and in the published npm package).
|
|
376
|
+
Apache-2.0 (see `LICENSE` at the repository root and in the published npm package).
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED