@gemmein/sdk 0.8.0 → 0.10.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.
@@ -33,3 +33,5 @@ the same surface gets its own file.
33
33
  | File | Since | Action required by |
34
34
  |------|-------|--------------------|
35
35
  | [`list-limit-refusal.md`](./list-limit-refusal.md) | 0.6.0 | 0.6.0 |
36
+ | [`raw-calls-off.md`](./raw-calls-off.md) | 0.9.0 | 0.9.0 |
37
+ | [`secure-store-set-throws.md`](./secure-store-set-throws.md) | 0.10.0 | 0.10.0 |
@@ -0,0 +1,51 @@
1
+ # Raw AI calls are off until the owner switches them on
2
+
3
+ ```
4
+ Since: 0.9.0
5
+ Action required by: 0.9.0
6
+ ```
7
+
8
+ ## What changed
9
+
10
+ Before 0.9.0 `g.ai.chat(body)` and `g.ai.text(body)` forwarded the
11
+ provider request the browser composed to any provider the owner had a key
12
+ for. From 0.9.0 that raw call is refused with `403 raw_calls_off` unless the
13
+ owner has switched raw calls on for that provider's key on the AI tools
14
+ page. The cloud applies this to every key, new and existing, from 7 Sep
15
+ 2026; the local engine (`gemmein dev`) keeps its keyless fake provider open
16
+ and applies the same rule to a configured key.
17
+
18
+ The normal path is a named tool: the owner defines it on the AI tools page
19
+ or your AI writes `gemmein/ai/tools/<name>.json` (instructions, prompt
20
+ template, typed inputs, model, caps) and the app calls it with inputs only.
21
+
22
+ ## Who is affected
23
+
24
+ Apps that call `g.ai.chat` or `g.ai.text` without `tool`, or with a `tool`
25
+ that only prices and gates a browser-composed body. An app that already
26
+ calls `g.ai.run` / `g.ai.runText`, or makes no AI calls, sees no
27
+ difference.
28
+
29
+ ## What to do
30
+
31
+ Move each call to a named tool. The prompt leaves the browser and lives on
32
+ the tool:
33
+
34
+ ```ts
35
+ // before
36
+ const res = await g.ai.chat({ model: "gpt-4o-mini", messages: [{ role: "user", content: `Summarise: ${text}` }] });
37
+
38
+ // after — gemmein/ai/tools/summarise.json carries the instructions and
39
+ // "promptTemplate": "Summarise: {{text}}", "inputs": [{ "name": "text", "type": "text", "required": true }]
40
+ const res = await g.ai.run("summarise", { text });
41
+ const answer = await g.ai.runText("summarise", { text });
42
+ ```
43
+
44
+ An owner who wants the raw path back switches raw calls on for that
45
+ provider's key on the AI tools page; the switch is per key and audited.
46
+
47
+ ## How to tell
48
+
49
+ A 403 with `code: "raw_calls_off"` in the response, `err.code ===
50
+ "raw_calls_off"` in the SDK, and the message naming the provider and the
51
+ switch. On the AI calls record the outcome is `refused` with that reason.
@@ -0,0 +1,75 @@
1
+ # A token store that cannot keep the session says so
2
+
3
+ ```
4
+ Since: 0.10.0
5
+ Action required by: 0.10.0
6
+ ```
7
+
8
+ ## What changed
9
+
10
+ Before 0.10.0 a token store that could not write swallowed the failure.
11
+ `auth.verifyEmailCode()` resolved, the app believed it was signed in, and the
12
+ session was gone on the next read — the app reported itself signed out with
13
+ nothing anywhere saying why.
14
+
15
+ From 0.10.0 `tokenStore.set()` THROWS a `GemmeinError` with code
16
+ `secure_store_unavailable` (status 0) when the store refuses the write, and
17
+ `auth.verifyEmailCode()` carries it to the caller. The law is all three
18
+ stores:
19
+
20
+ - `SecureStoreTokenStore` (`@gemmein/sdk/expo`) — `expo-secure-store` missing
21
+ or refusing (an unsigned simulator build has no keychain access group).
22
+ - `BrowserTokenStore` — a refused `localStorage` write: Safari private mode
23
+ past its quota, a browser set to block site data, a sandboxed iframe whose
24
+ access throws.
25
+ - `KeychainTokenStore` (`GemmeinSwift`) — the same, with the OSStatus and
26
+ `SecCopyErrorMessageString`'s words for it in the sentence.
27
+
28
+ The device's or browser's own error is on `err.cause`. `get()` and `clear()`
29
+ are unchanged and stay lenient: an unreadable store means signed out, which
30
+ every app already handles, and a crash at launch is worse than a sign-in
31
+ screen. `MemoryTokenStore.set()` never throws. The existing `token_too_large`
32
+ refusal is unchanged.
33
+
34
+ ## Who is affected
35
+
36
+ Apps that call `auth.verifyEmailCode()` (or any call that stores a session)
37
+ in a place where the store may refuse the write, and that RELIED on the old
38
+ silence to keep running: a kiosk browser, an embedded or sandboxed iframe, a
39
+ private-mode visitor, an unsigned simulator or ad-hoc mobile build. An app in
40
+ an ordinary browser tab or a signed app sees no difference — nothing that
41
+ stored the token before throws now.
42
+
43
+ ## What to do
44
+
45
+ Catch the one code and rebuild the client with a `MemoryTokenStore`. The
46
+ session is real either way — the server minted it — so an app that would
47
+ rather run than stop can carry it in memory: sign-in works, and ends with the
48
+ process.
49
+
50
+ ```ts
51
+ import { gemmein, MemoryTokenStore, GemmeinError } from "@gemmein/sdk";
52
+
53
+ let g = gemmein({ appKey });
54
+ try {
55
+ await g.auth.verifyEmailCode(email, code);
56
+ } catch (err) {
57
+ if (err instanceof GemmeinError && err.code === "secure_store_unavailable") {
58
+ // This session will not survive a reload. Say so, or carry on knowingly.
59
+ g = gemmein({ appKey, tokenStore: new MemoryTokenStore() });
60
+ await g.auth.verifyEmailCode(email, code);
61
+ } else {
62
+ throw err;
63
+ }
64
+ }
65
+ ```
66
+
67
+ An app that knows it runs where nothing can be stored passes
68
+ `tokenStore: new MemoryTokenStore()` from the start and never sees the code.
69
+
70
+ ## How to tell
71
+
72
+ `err.code === "secure_store_unavailable"` on the SDK (status 0, no HTTP
73
+ response — the request succeeded; the STORE refused), with the store's own
74
+ throw on `err.cause` and the fix in the message. In `GemmeinSwift` the same
75
+ case is a thrown `GemmeinError` whose message carries the keychain's OSStatus.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemmein/sdk",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Gemmein SDK \u2014 passwordless auth, safe storage, and Stripe-driven record flips for AI-built apps. Small enough that one prompt teaches the whole API.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,6 +13,11 @@
13
13
  "import": "./dist/index.js",
14
14
  "require": "./dist/index.cjs"
15
15
  },
16
+ "./expo": {
17
+ "types": "./dist/expo.d.ts",
18
+ "import": "./dist/expo.js",
19
+ "require": "./dist/expo.cjs"
20
+ },
16
21
  "./llms.txt": "./llms.txt",
17
22
  "./REFERENCE.md": "./REFERENCE.md",
18
23
  "./reaffirm.mjs": "./reaffirm.mjs",
@@ -27,6 +32,22 @@
27
32
  "reaffirm.mjs"
28
33
  ],
29
34
  "sideEffects": false,
35
+ "peerDependencies": {
36
+ "expo": "*",
37
+ "expo-secure-store": "*",
38
+ "react-native": "*"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "expo": {
42
+ "optional": true
43
+ },
44
+ "expo-secure-store": {
45
+ "optional": true
46
+ },
47
+ "react-native": {
48
+ "optional": true
49
+ }
50
+ },
30
51
  "engines": {
31
52
  "node": ">=20"
32
53
  },
@@ -49,7 +70,7 @@
49
70
  "scripts": {
50
71
  "version:sync": "node scripts/sync-version.mjs",
51
72
  "prebuild": "node scripts/sync-version.mjs",
52
- "build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json && mv dist/cjs/index.js dist/index.cjs && rm -rf dist/cjs && cp dist/index.d.ts dist/index.d.cts",
73
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json && mv dist/cjs/index.js dist/index.cjs && mv dist/cjs/expo.js dist/expo.cjs && rm -rf dist/cjs && sed -i.bak 's#\"./index.js\"#\"./index.cjs\"#g' dist/expo.cjs && rm -f dist/expo.cjs.bak && cp dist/index.d.ts dist/index.d.cts && cp dist/expo.d.ts dist/expo.d.cts",
53
74
  "prepack": "npm run build",
54
75
  "prepublishOnly": "npm run build"
55
76
  },