@spendguard/sdk 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,190 @@
1
+ # `@spendguard/sdk` Changelog
2
+
3
+ All notable changes to the TypeScript half of the SpendGuard SDK.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ This package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ The Python SDK ([spendguard-sdk on PyPI](https://pypi.org/project/spendguard-sdk/),
9
+ currently v0.5.1) and this TypeScript SDK are kept in lockstep on the public
10
+ surface — see
11
+ [`docs/specs/coverage/D05_ts_sdk_substrate/design.md`](../../docs/specs/coverage/D05_ts_sdk_substrate/design.md)
12
+ §9 for the lockstep contract.
13
+
14
+ ---
15
+
16
+ ## [0.5.0] — 2026-06-19
17
+
18
+ First public npm release of `@spendguard/sdk`. Version chosen to track the
19
+ Python SDK line (PyPI at 0.6.0) rather than continue the internal 0.1.x
20
+ sequence; no 0.2–0.4 npm releases exist.
21
+
22
+ ### Added
23
+
24
+ - `UnitRef.unitId` — optional canonical-truth UUID of the ledger unit row.
25
+ Adapters that issue ledger-backed reserve calls now thread this through to
26
+ `BudgetClaim.unit.unit_id` on the wire. Closes the HARDEN_D05_UR substrate
27
+ gap that previously blocked DENY+STREAM full assertion across ~14 adapter
28
+ demos. Backward-compat: omitting `unitId` matches prior behavior.
29
+
30
+ ---
31
+
32
+ ## [0.1.0] — 2026-06-07
33
+
34
+ First public release. Mirrors `spendguard-sdk` (Python) v0.5.1 public surface.
35
+ Closes deliverable D05 (TypeScript SDK substrate).
36
+
37
+ ### Added
38
+
39
+ #### Client (SLICE 3-5)
40
+
41
+ - `SpendGuardClient` — gRPC-over-UDS client for the SpendGuard sidecar.
42
+ - `handshake()` — protocol-version + capabilities negotiation.
43
+ - `reserve()` — pre-flight budget decision (the canonical method name).
44
+ - `requestDecision()` — `reserve()` alias for migration ergonomics; `===`
45
+ identity is asserted as a P0 surface invariant
46
+ (review-standards §1.5).
47
+ - `commitEstimated()` — observed-output commit (cost rounded up).
48
+ - `release()` — terminal reservation release with explicit
49
+ `releaseReason`.
50
+ - `queryBudget()` — wire stub (throws "not yet wired" until cross-component
51
+ slice; documented in JSDoc per review-standards §11.3).
52
+ - `SpendGuardClientOptions`, `HandshakeOutcome`, `DecisionOutcome`,
53
+ `ReleaseOutcome` types.
54
+ - Default deadlines: `DEFAULT_DECISION_TIMEOUT_MS`,
55
+ `DEFAULT_HANDSHAKE_TIMEOUT_MS`, `DEFAULT_PUBLISH_TIMEOUT_MS`,
56
+ `DEFAULT_TRACE_TIMEOUT_MS`.
57
+ - `unix:` URI handling matches Python (sets
58
+ `grpc.default_authority: "localhost"` so `tonic` does not 400 with
59
+ `PROTOCOL_ERROR` on the URL-encoded path).
60
+
61
+ #### IDs (SLICE 6)
62
+
63
+ - `newUuid7()` — UUIDv7 generator (sortable, embedded ms timestamp).
64
+ - `deriveIdempotencyKey()` — deterministic `sg-…` key. **P0 cross-language
65
+ byte-equivalent with Python `spendguard.derive_idempotency_key()` and the
66
+ Rust sidecar's `audit_chain::derive_key`** (sidecar-side dedup correctness).
67
+ - `deriveUuidFromSignature()` — BLAKE2b-based UUID derivation from an
68
+ application signature + scope. **Byte-equivalent with Python.** Uses
69
+ `@noble/hashes` BLAKE2b implementation.
70
+ - `defaultCallSignature()` — framework-agnostic signature helper.
71
+ - `workloadInstanceId()` — process-stable identifier.
72
+
73
+ #### Pricing (SLICE 6)
74
+
75
+ - `PricingLookup` — typed lookup with provider+model+kind keys.
76
+ - `USD_MICROS_PER_USD` — wire-unit conversion constant.
77
+ - `DEMO_PRICING` — embedded pricing snapshot for the demo seed
78
+ (`pricing_version: v2026.05.09-1`, regenerated at release time from
79
+ `deploy/demo/init/pricing/seed.yaml`).
80
+
81
+ #### Prompt hash (SLICE 6)
82
+
83
+ - `computePromptHash()` — HMAC-SHA256 lowercase hex. **P0 cross-language
84
+ byte-equivalent with Python `spendguard.compute_prompt_hash()` and the
85
+ sidecar `prompt_hash::compute`**. Tenant ID is canonicalised to lowercase
86
+ before hashing — the `crossLanguage` test suite pins this.
87
+
88
+ #### Run plan / Signal 3 (SLICE 7)
89
+
90
+ - `withRunPlan()` + `currentRunPlan()` — `AsyncLocalStorage`-scoped
91
+ budget-hint context. Adapters set the plan once at run boundary; nested
92
+ `reserve()` calls read it transparently.
93
+
94
+ #### OTel (SLICE 8)
95
+
96
+ - `otelTracer` config field — optional `@opentelemetry/api` `Tracer`.
97
+ - Per-RPC spans named `spendguard.<rpc>` with attributes documented in
98
+ `design.md` §6.4 + `SPENDGUARD_OTEL_ATTR` constant export.
99
+ - `@opentelemetry/api` is a peer dep, marked optional in
100
+ `peerDependenciesMeta`.
101
+
102
+ #### Retry (SLICE 8)
103
+
104
+ - Bounded retry for the `UNAVAILABLE` / `DEADLINE_EXCEEDED` / `CANCELLED`
105
+ cluster. Max 2 attempts, constant 25 ms + jitter backoff. Idempotency-key
106
+ required (the substrate enforces).
107
+
108
+ #### Idempotency cache (SLICE 8)
109
+
110
+ - In-process LRU keyed by `(tenantId, idempotencyKey)` so re-attempts after
111
+ retry observation surface the cached decision rather than re-RPC'ing.
112
+
113
+ #### Cross-language fixtures (SLICE 9)
114
+
115
+ - `sdk/fixtures/cross-language/v1.json` — the SINGLE SOURCE OF TRUTH for
116
+ byte-equivalence between the Rust sidecar, Python SDK, and TS SDK. v1.json
117
+ ships with ≥20 fixtures spanning `compute_prompt_hash`,
118
+ `derive_idempotency_key`, and `derive_uuid_from_signature` (see
119
+ `sdk/fixtures/cross-language/README.md` for the add-a-fixture / mint-v2
120
+ runbook).
121
+ - The fixture file is **included in the published npm tarball** under
122
+ `fixtures/cross-language/v1.json` so consumer-side conformance suites can
123
+ pin against the exact same vectors.
124
+
125
+ #### Errors
126
+
127
+ - `SpendGuardError`, `HandshakeError`, `SidecarUnavailable`,
128
+ `DecisionDenied`, `DecisionStopped`, `DecisionSkipped`, `ApprovalRequired`,
129
+ `ApprovalDeniedError`, `ApprovalLapsedError`,
130
+ `ApprovalBundleHotReloadedError`, `MutationApplyFailed`,
131
+ `SpendGuardConfigError` — full hierarchy mirror of the Python SDK.
132
+
133
+ ### Locked invariants
134
+
135
+ - **P0 cross-language byte-equivalence** with the Python SDK (and the Rust
136
+ sidecar) on `computePromptHash`, `deriveIdempotencyKey`, and
137
+ `deriveUuidFromSignature`. Drift breaks audit-chain dedup and the
138
+ idempotency replay collapse contract. Enforced by the
139
+ `tests/crossLanguage.test.ts` suite consuming
140
+ `sdk/fixtures/cross-language/v1.json` (also consumed by
141
+ `sdk/python/tests/test_cross_language_fixtures.py`).
142
+ - **`reserve()` === `requestDecision()` reference identity** (P0 surface
143
+ invariant, `tests/locked-surface.test.ts`).
144
+ - **ESM-only.** No CJS shim. `"type": "module"`.
145
+ - **camelCase on the public surface, snake_case on the wire** — every wire
146
+ conversion is centralised in `src/client.ts` so adapter authors never see
147
+ snake_case.
148
+
149
+ ### Compatibility
150
+
151
+ - Node 20.10+ (uses `using` / `await using`, stable `AsyncLocalStorage`,
152
+ Web Crypto).
153
+ - Bun 1.1+ tested as secondary target.
154
+ - Deno 1.46+ tested as secondary target.
155
+ - **Browser is NOT supported in v0.1.x.** UDS gRPC is server-only. A future
156
+ v0.x ASP HTTP gateway transport is forward-reserved in the config type but
157
+ not built.
158
+
159
+ ### Known limitations (deferred to future slices)
160
+
161
+ - `queryBudget()` wire body — stub-throws "not yet wired". Cross-component
162
+ slice unblocks.
163
+ - LLM_CALL_OUTCOME proto bump — deferred to a cross-component slice that
164
+ spans D05 + Rust sidecar + Python SDK simultaneously.
165
+ - Per-framework `defaultCallSignature()` defaults for LangChain / Vercel AI
166
+ / OpenAI Agents / Inngest — each adapter package (D04 / D06 / D08 / D29)
167
+ provides its own message-type-specific signature.
168
+ - Identity-propagation `RunContext` (parentRunId / traceparent threading) —
169
+ deferred per SLICE 7 R2. Use the explicit `ReserveRequest` fields for now.
170
+
171
+ ### Verification
172
+
173
+ - 366 tests pass under vitest 2.x (Node 20).
174
+ - `npm pack` tarball ≤ 250 KB (`scripts/size-budget.sh`).
175
+ - Minified `dist/index.js` ≤ 120 KB (design.md §10 budget).
176
+ - `package.json#version` == `src/version.ts#VERSION` (`scripts/version-check.sh`).
177
+ - All P0 byte-equivalence fixtures match the Python SDK.
178
+
179
+ ---
180
+
181
+ ## Future versions
182
+
183
+ The lockstep contract with `spendguard-sdk` (Python) v0.5.1 means the next
184
+ minor (v0.2.x) will track whatever Python's next minor mints. Any change to
185
+ the v0.1.0 public surface listed under "Added" requires a coordinated
186
+ v0.minor bump on both packages — see
187
+ [`docs/specs/coverage/D05_ts_sdk_substrate/design.md`](../../docs/specs/coverage/D05_ts_sdk_substrate/design.md)
188
+ §§4 / 9 for the contract.
189
+
190
+ [0.1.0]: https://github.com/m24927605/agentic-spendguard/releases/tag/ts-sdk-v0.1.0
@@ -0,0 +1,127 @@
1
+ # `@spendguard/sdk` — Third-Party License Notices
2
+
3
+ This package (`@spendguard/sdk`) is itself licensed under Apache License 2.0
4
+ — see the repository root `LICENSE` file
5
+ (<https://github.com/m24927605/agentic-spendguard/blob/main/LICENSE>) for the
6
+ full Apache-2.0 text.
7
+
8
+ This document satisfies review-standards §11.6 by listing every direct
9
+ runtime dependency and its license. Transitive deps that flow in via these
10
+ direct deps follow their own license; this notice is required only for the
11
+ direct edges below.
12
+
13
+ ---
14
+
15
+ ## Runtime dependencies (shipped to consumers)
16
+
17
+ These are the packages declared under `"dependencies"` in `package.json`
18
+ that ship inside the consumer's `node_modules/@spendguard/sdk` tree and
19
+ that the published `dist/*.js` imports at runtime.
20
+
21
+ ### `@grpc/grpc-js`
22
+
23
+ - License: **Apache License 2.0**
24
+ - Project: <https://github.com/grpc/grpc-node/tree/master/packages/grpc-js>
25
+ - Use: Node-native gRPC client transport for the UDS sidecar connection
26
+ (`SpendGuardClient.handshake() / reserve() / commitEstimated() / release()`).
27
+ - Full Apache-2.0 text:
28
+ <https://github.com/grpc/grpc-node/blob/master/LICENSE>
29
+
30
+ ### `@noble/hashes`
31
+
32
+ - License: **MIT**
33
+ - Project: <https://github.com/paulmillr/noble-hashes>
34
+ - Use: BLAKE2b primitive backing `deriveUuidFromSignature()` (P0
35
+ byte-equivalent with the Python SDK and Rust sidecar). The package is also
36
+ used via Node's `node:crypto` HMAC-SHA256 for `computePromptHash()` — the
37
+ `@noble/hashes` dependency is BLAKE2b-only.
38
+ - Full MIT text:
39
+ <https://github.com/paulmillr/noble-hashes/blob/main/LICENSE>
40
+
41
+ > Copyright (c) 2022 Paul Miller (https://paulmillr.com)
42
+ >
43
+ > Permission is hereby granted, free of charge, to any person obtaining
44
+ > a copy of this software and associated documentation files (the
45
+ > "Software"), to deal in the Software without restriction, including
46
+ > without limitation the rights to use, copy, modify, merge, publish,
47
+ > distribute, sublicense, and/or sell copies of the Software, and to
48
+ > permit persons to whom the Software is furnished to do so, subject to
49
+ > the following conditions:
50
+ >
51
+ > The above copyright notice and this permission notice shall be included
52
+ > in all copies or substantial portions of the Software.
53
+ >
54
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
55
+ > OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
56
+ > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
57
+ > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
58
+ > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
59
+ > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
60
+ > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
61
+
62
+ ### `@protobuf-ts/runtime`
63
+
64
+ - License: **Apache License 2.0**
65
+ - Project: <https://github.com/timostamm/protobuf-ts>
66
+ - Use: Generated `src/_proto/**/*.ts` modules import this runtime for
67
+ message encode/decode.
68
+
69
+ ### `@protobuf-ts/runtime-rpc`
70
+
71
+ - License: **Apache License 2.0**
72
+ - Project: <https://github.com/timostamm/protobuf-ts>
73
+ - Use: gRPC service-client surface that `_proto/spendguard/sidecar_adapter/v1/adapter.client.ts`
74
+ builds against.
75
+
76
+ ### `@protobuf-ts/grpc-transport`
77
+
78
+ - License: **Apache License 2.0**
79
+ - Project: <https://github.com/timostamm/protobuf-ts>
80
+ - Use: Adapter from the generated `runtime-rpc` clients to `@grpc/grpc-js`
81
+ channels (the UDS transport).
82
+
83
+ ---
84
+
85
+ ## Optional peer dependencies
86
+
87
+ ### `@opentelemetry/api`
88
+
89
+ - License: **Apache License 2.0**
90
+ - Project: <https://github.com/open-telemetry/opentelemetry-js-api>
91
+ - Use: Optional. Consumers that supply an `otelTracer` config field get
92
+ per-RPC spans (design.md §6.4). Not shipped inside `@spendguard/sdk`'s
93
+ tree because it is declared under `"peerDependencies"`; consumers install
94
+ the version they want.
95
+
96
+ ---
97
+
98
+ ## Build/Dev-only dependencies
99
+
100
+ Dev-only dependencies (`tsup`, `vitest`, `typescript`, `@biomejs/biome`,
101
+ `@protobuf-ts/plugin`, `tsx`, `@types/node`) are NOT shipped to consumers
102
+ — they live under `"devDependencies"` and are stripped from the published
103
+ tarball. They do not require notice attribution because they do not appear
104
+ in the consumer's runtime tree.
105
+
106
+ ---
107
+
108
+ ## Generated proto sources
109
+
110
+ `src/_proto/spendguard/**` is generated from `proto/spendguard/**` at
111
+ build time via the `@protobuf-ts/plugin` codegen. The `.proto` source files
112
+ in this repository are Apache-2.0 licensed under the repo root `LICENSE`.
113
+
114
+ The generated TypeScript files inherit the original proto's license
115
+ (Apache-2.0); they are not third-party code and do not need separate notice
116
+ beyond the repo root `LICENSE`.
117
+
118
+ ---
119
+
120
+ ## Apache-2.0 compliance summary
121
+
122
+ Per Apache-2.0 §4(c), this `LICENSE_NOTICES.md` file is the NOTICE document
123
+ for redistribution. Consumers that re-distribute `@spendguard/sdk` in
124
+ binary form (e.g. as part of a larger Node application's deploy artifact)
125
+ should preserve this file. Consumers that depend on `@spendguard/sdk` via
126
+ npm and ship it as-installed are already compliant — npm's install tree
127
+ preserves this file under `node_modules/@spendguard/sdk/LICENSE_NOTICES.md`.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # `@spendguard/sdk`
2
+
3
+ [![npm](https://img.shields.io/npm/v/@spendguard/sdk.svg)](https://www.npmjs.com/package/@spendguard/sdk)
4
+ [![license](https://img.shields.io/npm/l/@spendguard/sdk.svg)](https://github.com/m24927605/agentic-spendguard/blob/main/LICENSE)
5
+ [![publish](https://github.com/m24927605/agentic-spendguard/actions/workflows/sdk-ts-publish.yml/badge.svg)](https://github.com/m24927605/agentic-spendguard/actions/workflows/sdk-ts-publish.yml)
6
+
7
+ > Runtime safety-layer client for AI-agent frameworks (TypeScript).
8
+ > Mirror of [`spendguard-sdk`](https://pypi.org/project/spendguard-sdk/) (Python).
9
+
10
+ `@spendguard/sdk` is the shared substrate that per-framework adapters
11
+ (`@spendguard/langchain`, `@spendguard/vercel-ai`, `@spendguard/openai-agents`,
12
+ `@spendguard/inngest-agentkit`) build against. It implements the gRPC-over-UDS
13
+ client for the SpendGuard sidecar, plus the deterministic helpers that produce
14
+ **byte-identical** output to the Python SDK and the Rust sidecar — the
15
+ audit-chain invariants that make idempotency replay and dedup correct
16
+ across languages.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pnpm add @spendguard/sdk @opentelemetry/api
22
+ # or
23
+ npm install @spendguard/sdk @opentelemetry/api
24
+ # or
25
+ bun add @spendguard/sdk @opentelemetry/api
26
+ ```
27
+
28
+ `@opentelemetry/api` is an **optional** peer dep — install it only if you
29
+ plan to pass an `otelTracer`. Adapters that never enable OTel pay zero
30
+ extra dependency cost.
31
+
32
+ ## Quick start
33
+
34
+ ```ts
35
+ // reserve -> commitEstimated -> release (the v0.1.0 happy path)
36
+ import {
37
+ SpendGuardClient,
38
+ deriveIdempotencyKey,
39
+ newUuid7,
40
+ USD_MICROS_PER_USD,
41
+ } from "@spendguard/sdk";
42
+
43
+ const client = new SpendGuardClient({
44
+ tenantId: "acme-prod",
45
+ socketPath: "/run/spendguard/sidecar.sock",
46
+ handshake: { protocolVersion: 1, capabilities: {} },
47
+ });
48
+
49
+ await client.handshake();
50
+
51
+ const decision = await client.reserve({
52
+ trigger: "LLM_CALL_PRE",
53
+ runId: newUuid7(),
54
+ stepId: newUuid7(),
55
+ llmCallId: newUuid7(),
56
+ decisionId: newUuid7(),
57
+ route: "openai:gpt-4o-mini",
58
+ projectedClaims: [
59
+ { scopeId: "team:eng", amountAtomic: "150000", unit: { unit: "usd_micros", denomination: 1 } },
60
+ ],
61
+ idempotencyKey: deriveIdempotencyKey({
62
+ tenantId: "acme-prod",
63
+ sessionId: "sess-1",
64
+ runId: "run-1",
65
+ stepId: "step-1",
66
+ llmCallId: "call-1",
67
+ trigger: "LLM_CALL_PRE",
68
+ }),
69
+ });
70
+
71
+ if (decision.decision === "CONTINUE") {
72
+ // ...call the provider (OpenAI, Anthropic, etc.) here...
73
+ await client.commitEstimated({
74
+ runId: "run-1", stepId: "step-1", llmCallId: "call-1",
75
+ decisionId: decision.decisionId,
76
+ reservationId: decision.reservationIds[0]!,
77
+ estimatedAmountAtomic: String(123 * USD_MICROS_PER_USD / 1_000_000),
78
+ unit: { unit: "usd_micros", denomination: 1 },
79
+ pricing: { pricingVersion: "v2026.05.09-1", pricingHash: new Uint8Array() },
80
+ providerEventId: "openai:chatcmpl-abc",
81
+ outcome: "SUCCESS",
82
+ });
83
+ await client.release({
84
+ runId: "run-1", stepId: "step-1", llmCallId: "call-1",
85
+ decisionId: decision.decisionId,
86
+ reservationId: decision.reservationIds[0]!,
87
+ releaseReason: "COMPLETED",
88
+ });
89
+ }
90
+ ```
91
+
92
+ The substrate also exposes `requestDecision()` as a referential alias for
93
+ `reserve()` (`client.reserve === client.requestDecision`) so call-sites
94
+ written against the Python SDK transliterate without semantic surprise.
95
+
96
+ ## Subpath exports
97
+
98
+ Tree-shake what you need:
99
+
100
+ | Subpath | Contents |
101
+ |---|---|
102
+ | `@spendguard/sdk` | Full barrel (everything below). |
103
+ | `@spendguard/sdk/client` | `SpendGuardClient` + request/response types. |
104
+ | `@spendguard/sdk/errors` | Typed error hierarchy. |
105
+ | `@spendguard/sdk/ids` | `newUuid7`, `deriveIdempotencyKey`, `deriveUuidFromSignature`. |
106
+ | `@spendguard/sdk/pricing` | `PricingLookup`, `USD_MICROS_PER_USD`. |
107
+ | `@spendguard/sdk/pricing/demo` | Embedded `DEMO_PRICING` snapshot. |
108
+ | `@spendguard/sdk/promptHash` | `computePromptHash`. |
109
+ | `@spendguard/sdk/runPlan` | `withRunPlan`, `currentRunPlan` (AsyncLocalStorage). |
110
+ | `@spendguard/sdk/otel` | Optional OTel span wrapper. |
111
+ | `@spendguard/sdk/retry` | Bounded retry helper. |
112
+ | `@spendguard/sdk/cache` | In-memory idempotency cache. |
113
+ | `@spendguard/sdk/proto` | Generated proto types. |
114
+
115
+ ## Cross-language byte-equivalence (P0)
116
+
117
+ Three functions in this SDK MUST produce byte-identical output to the
118
+ Python SDK and the Rust sidecar:
119
+
120
+ - `computePromptHash(text, tenantId)` — HMAC-SHA256 lowercase hex.
121
+ - `deriveIdempotencyKey({ ... })` — `sg-` + 32 hex chars.
122
+ - `deriveUuidFromSignature(sig, { scope })` — BLAKE2b-based UUID.
123
+
124
+ Drift breaks audit-chain rule dedup and the idempotency-replay collapse
125
+ contract. The corpus that pins this is shipped inside the npm tarball at
126
+ `fixtures/cross-language/v1.json` — both this SDK and the Python SDK consume
127
+ the same file. See `sdk/fixtures/cross-language/README.md` in the source repo
128
+ for the runbook.
129
+
130
+ ## Compatibility
131
+
132
+ - **Node 20.10+** is the primary target.
133
+ - **Bun 1.1+** and **Deno 1.46+** are tested as secondary targets.
134
+ - Browser is **NOT supported in v0.1.x** — UDS transport is server-only.
135
+
136
+ ## Links
137
+
138
+ - [Full design spec](https://github.com/m24927605/agentic-spendguard/blob/main/docs/specs/coverage/D05_ts_sdk_substrate/design.md)
139
+ — public surface (§4), architecture (§6), locked decisions (§9), bundle-size budget (§10).
140
+ - [Implementation spec](https://github.com/m24927605/agentic-spendguard/blob/main/docs/specs/coverage/D05_ts_sdk_substrate/implementation.md)
141
+ — repo layout + codegen pipeline.
142
+ - [Review standards](https://github.com/m24927605/agentic-spendguard/blob/main/docs/specs/coverage/D05_ts_sdk_substrate/review-standards.md)
143
+ — P0/P1/P2 review gates applied to every D05 slice.
144
+ - [Python SDK on PyPI](https://pypi.org/project/spendguard-sdk/) — the
145
+ lockstep counterpart.
146
+ - [CHANGELOG.md](./CHANGELOG.md) — release history.
147
+ - [LICENSE_NOTICES.md](./LICENSE_NOTICES.md) — third-party license attribution.
148
+
149
+ ## License
150
+
151
+ Apache-2.0. See `LICENSE` in the repository root.