@gemmein/sdk 0.5.0 → 0.7.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,224 @@
1
+ # Changelog — @gemmein/sdk
2
+
3
+ All notable changes to the SDK are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
+ semver. A change that asks something of your app has a note in
6
+ [`migrations/`](./migrations/README.md) naming the version it arrived in
7
+ and the version by which the action is required.
8
+
9
+ The SDK and the local engine (`@gemmein/cli`) share a minor version from
10
+ 0.6.0 / 0.7.0 onward: a feature that lands in one lands in the other in
11
+ the same release.
12
+
13
+ ## [Unreleased]
14
+
15
+ ## [0.7.0] — 2026-09-04
16
+
17
+ ### Credits and the AI route
18
+
19
+ Credits are a general consumable: a balance your customers hold and your
20
+ product spends. A product carries `grantsCredits` (1..1,000,000) and each
21
+ confirmed purchase adds that many to the buyer's balance, idempotent on the
22
+ payment; a full refund claws back what is still unspent, floor zero. The
23
+ browser reads the balance with `g.credits.balance()`; a server spends with
24
+ `gemmeinServer(sk).spendCredits(personId, { amount?, reason, key? })` behind
25
+ the fourth key capability, "Spend a person's credits" — one atomic
26
+ decrement, refused whole below zero with `402 credits_exhausted` and the
27
+ balance in the message, at-most-once on `key`. `Holdings.credits` is
28
+ `{ balance }` from engine 0.8.0 (the type is unchanged; an older local engine
29
+ still answers `null`). A relay adds with `grant_credits`; the owner comps
30
+ from the person's page. Credits never expire and are never priced by token.
31
+
32
+ The AI route lets a browser session call OpenAI, Anthropic or Google through
33
+ Gemmein on the owner's own provider key, pasted once in the Keys room and
34
+ write-only from then on. `g.ai.chat(body, { provider?, signal? })` spends one
35
+ credit, forwards the provider's own request body as sent (minus `provider`,
36
+ and for Google `model` and `stream`, which ride the URL) with the auth
37
+ headers, `content-type` and `accept` added, and returns the fetch `Response`
38
+ untouched — the provider's answer, 2xx or not, is yours to read; a stream
39
+ stays a stream. `g.ai.text(body)` collects a non-stream answer to one string
40
+ per provider and throws `provider_error` on a provider's own non-2xx. A
41
+ credit is refunded only when the provider fails before its first byte
42
+ (`x-gemmein-credit: refunded`); a stream that dies mid-way does not refund,
43
+ and hanging up before the headers does not refund. A secret key is refused
44
+ (`403 scope_denied`): a server calls the provider directly. The codes that
45
+ join `GemmeinError`: `credits_exhausted`, `credits_ceiling`,
46
+ `dedupe_conflict`, `invalid_amount`, `invalid_reason`, `invalid_key`,
47
+ `person_not_found`, `capability_required`, `invalid_body`,
48
+ `ai_not_configured`, `provider_required`, `model_not_allowed`, `ai_capped`,
49
+ `payload_too_large`, `session_required`, `scope_denied`,
50
+ `provider_unreachable`, and `provider_error` from `g.ai.text`. `REFERENCE.md`
51
+ carries the Credits and AI chapters; `llms.txt` carries both capability
52
+ cards. `gemmein dev` answers a fake provider without a key and its pay
53
+ simulator honours the `grantsCredits` a product declares in
54
+ `gemmein/payments.json` (`gemmein payments setup` asks for it).
55
+
56
+ The ledger's laws, stated: a full refund claws back what that purchase's own
57
+ ledger row granted, whatever the product says today; a balance carries at
58
+ most 1,000,000,000 (`credits_ceiling`, nothing added past it); a server
59
+ spend's `key` is scoped to the person, so one order id reused for two people
60
+ charges both and a key that names a different movement is `dedupe_conflict`;
61
+ the person's page shows the credits that person spent in the last 30 days; a
62
+ provider that echoes the key in a refusal reaches the app as `***<hint>`.
63
+
64
+ ### Added
65
+ - `g.credits.balance()` → `GET /auth/credits` → `{ balance }` (session required).
66
+ - `g.ai.chat(body, { provider?, signal? })` → `POST /ai/chat`; answers the
67
+ fetch `Response` untouched (streaming intact) whenever the answer passed
68
+ the spend — the provider's own status and body, 2xx or not; Gemmein's
69
+ refusals throw `GemmeinError` with the engine's code — `credits_exhausted`
70
+ (402), `ai_not_configured` (409), `provider_required` (400),
71
+ `model_not_allowed` (403), `ai_capped` (429), `payload_too_large` (413),
72
+ `invalid_body` (400), `session_required` (401), `scope_denied` (403),
73
+ `provider_unreachable` (502).
74
+ - `g.ai.text(body, options?)` — the non-streaming answer as a string
75
+ (OpenAI `choices[0].message.content`; Anthropic `content[].text`;
76
+ Google `candidates[0].content.parts[].text`); `invalid_response` when
77
+ no text came back; `provider_error` (the provider's status and message)
78
+ on a provider's own non-2xx.
79
+ - `gemmeinServer(sk).spendCredits(personId, { amount?, reason, key? })` →
80
+ `POST /server/people/:id/credits/spend` → `{ ok, spent, deduped,
81
+ balance: { before, after }, event: { id, reason, actor } }` (a deduped
82
+ repeat answers `spent: 0` and the same event); needs the key's "Spend a
83
+ person's credits" capability; `402 credits_exhausted`.
84
+ - `Holdings.credits` is filled (`{ balance }`) from engine 0.8.0 — the
85
+ type is unchanged.
86
+ - Relays: `grant_credits { amount 1..10,000, reason? }` action, mirrored
87
+ in `@gemmein/mcp`'s offline validator.
88
+ - Console: the Keys room's AI provider card (set · test · replace ·
89
+ remove), "Grants credits" on a product, CREDITS + Grant credits + the
90
+ ledger on a person, the Usage room's "AI calls" row.
91
+
92
+ ## [0.6.0] — 2026-09-04
93
+
94
+ ### Changed
95
+ - `list({ limit })` is honoured or refused, never clamped: a `limit`
96
+ outside 1..100 answers 400 `invalid_limit` ("limit must be between 1 and
97
+ 100"). The default stays 25 and the maximum 100. Apps that asked for more
98
+ than 100 and read the first page as the whole set now get the refusal —
99
+ page with `cursor`. See `migrations/list-limit-refusal.md`.
100
+ - `verifyEmailCode` throws `GemmeinError` with code `invalid_response`
101
+ (status 0) when a 200 answer carries no session token, where it threw a
102
+ bare `Error` before. Every error the SDK raises is now a `GemmeinError`.
103
+ - `engines.node` is `>=20`. Node 18 reached end of life in April 2025; the
104
+ SDK is built and tested on 20 and 22.
105
+
106
+ ### Added
107
+ - Relays. One JSON file, `gemmein/relays/<name>.json`: a trigger (a
108
+ provider's signed webhook at `POST /hooks/<appId>/<name>`, a schedule, or a
109
+ record change) and one to ten of Gemmein's own verbs (write a record, grant
110
+ or revoke access, email the person, call your URL), run in order with
111
+ retries and a replay button in the dashboard. No SDK method: the surface is
112
+ the file and the dashboard. `REFERENCE.md` carries the definition, the
113
+ triggers, the actions, the `call_url` contract and its signature header,
114
+ the templates, the lifecycle and the codes; `llms.txt` carries the
115
+ GoCardless worked example. A `call_url` that writes back through the SDK
116
+ fires `data_change` like any write. Stripe stays built in; any provider that
117
+ signs its webhooks drives access the same way.
118
+ - `GrantSource` gains `relay`: a grant a relay's `grant_access`
119
+ wrote. `holdings` and `verifySession` list it by kind like the other six;
120
+ `ManualGrantSource` is unchanged — no key can mint it.
121
+ - `gemmeinServer(...).invitePerson(email)` creates a person by email before
122
+ they sign in, or returns the one that exists (`{ person, created }`, type
123
+ `InvitedPerson`). Behind the third key capability, "Create a person by
124
+ email before they sign in". Refusals: `invalid_email`, `invite_capped`
125
+ (500 accepted calls per app per day, raised on request). An invited person
126
+ shows as invited on the person page until their first sign-in and is not
127
+ counted in the people band (the billable unit) until then.
128
+ - Every request carries `x-client-info: gemmein-sdk/<version>`. The Keys
129
+ page shows the SDK build behind a key's most recent call ("Last seen
130
+ from gemmein-sdk/0.6.0, as reported"). `SDK_VERSION` and `CLIENT_INFO`
131
+ are exported.
132
+ - `CHANGELOG.md` and `migrations/` ship in the package.
133
+
134
+ ## [0.5.0] — 2026-09-03
135
+
136
+ ### Added
137
+ - The server gate on `gemmeinServer(sk)`: `verifySession(token)`,
138
+ `holdings(personId)`, `grantAccess(personId, { entitlement, reason? })`,
139
+ `revokeAccess(personId, grantId, { reason? })`. Grant and revoke require
140
+ the key's `grantAccess` capability (minted on the Keys page).
141
+ - Exported types `Holdings`, `Grant`, `GatePerson`, `GrantSource`,
142
+ `ManualGrantSource`.
143
+
144
+ ## [0.4.7] — 2026-09-02
145
+
146
+ ### Changed
147
+ - Documentation-only release. The packaged `llms.txt` and `REFERENCE.md`
148
+ carry the W5 teaching: a promote run carries new fields as well as new
149
+ collections.
150
+
151
+ ## [0.4.6] — 2026-08-26
152
+
153
+ ### Changed
154
+ - `reaffirm.mjs` runs the 16-probe harness; its teachings are rewritten.
155
+
156
+ ## [0.4.5] — 2026-08-26
157
+
158
+ ### Changed
159
+ - Documentation-only release. `REFERENCE.md` catches up with documents,
160
+ `since`, `watch()`, `notify()`, the file laws and `purchases.mine()`.
161
+
162
+ ## [0.4.4] — 2026-08-26
163
+
164
+ ### Added
165
+ - `upload(file, { for })` addresses a file to one person; the file-kind
166
+ (image or document) and file-class teaching.
167
+
168
+ ## [0.4.3] — 2026-08-25
169
+
170
+ ### Added
171
+ - `gemmeinServer(sk).notify(personId, { subject, text, kind?, key? })`.
172
+
173
+ ### Changed
174
+ - `watch()` stops on an auth-terminal answer instead of retrying it.
175
+
176
+ ## [0.4.2] — 2026-08-25
177
+
178
+ ### Added
179
+ - `list({ since })` with `ListResult.deleted` and `ListResult.watermark`;
180
+ `watch()` on a collection.
181
+
182
+ ## [0.4.1] — 2026-08-22
183
+
184
+ ### Added
185
+ - `GemmeinError.requires` (and `requiresAny`) on 403 `entitlement_required`,
186
+ on both the collection path and the runtime clients.
187
+
188
+ ## [0.4.0] — 2026-07-30
189
+
190
+ ### Changed
191
+ - A product's file lives on the product card and the purchase is the
192
+ authorisation to read it.
193
+
194
+ ## [0.3.2] — 2026-07-29
195
+
196
+ ### Fixed
197
+ - The wave-audit fixes for the 0.3 line.
198
+
199
+ ## [0.3.1] — 2026-07-29
200
+
201
+ ### Changed
202
+ - Documentation: the guide teaches paid access.
203
+
204
+ ## [0.3.0] — 2026-07-29
205
+
206
+ ### Added
207
+ - The file reference contract: records store references, files resolve
208
+ through the SDK.
209
+
210
+ ## [0.2.1] — 2026-07-22
211
+
212
+ ### Changed
213
+ - Documentation-only refresh.
214
+
215
+ ## [0.2.0] — 2026-07-22
216
+
217
+ ### Changed
218
+ - Two-layer shape: `g.collection(name)` plus the self-service primitives.
219
+
220
+ ## [0.1.0] — 2026-07-11
221
+
222
+ ### Added
223
+ - First published release: built `dist`, persistent sessions, journey
224
+ signposts.