@aithos/sdk 0.1.0-alpha.35 → 0.1.0-alpha.36

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/dist/src/auth.js CHANGED
@@ -1294,6 +1294,24 @@ export class AithosAuth {
1294
1294
  }
1295
1295
  const json = (await res.json());
1296
1296
  if (json.error) {
1297
+ // Backward-compat shim for backends without the semantic-equality
1298
+ // fix on publish-identity (alpha.33+ regression): the server may
1299
+ // reject a republish with -32022 because the client regenerates
1300
+ // `aithos.created_at` (and `proof.created`) on every
1301
+ // `signedDidDocument()` call, breaking the strict byte-equal
1302
+ // idempotence the server enforces. For an honest signer (same
1303
+ // root key, same DID) the only way to hit this code path is the
1304
+ // timestamp-drift case — which is semantically a no-op. Treat as
1305
+ // success.
1306
+ //
1307
+ // Server-side fix (publish-identity.ts switched to semantic
1308
+ // equality on cryptographic fields only) makes this branch dead
1309
+ // code on upgraded backends. Kept here as defense-in-depth for
1310
+ // SDK consumers pointing at older deployments.
1311
+ if (json.error.code === -32022 &&
1312
+ /different did\.json already published/i.test(json.error.message)) {
1313
+ return; // already published with same crypto material — no-op
1314
+ }
1297
1315
  // JSON-RPC error: don't retry — these are deterministic
1298
1316
  // (validation, permission, identity-already-tombstoned, …).
1299
1317
  throw new AithosSDKError("ethos_bootstrap_failed", `publish_identity rejected: ${json.error.message}`, {
@@ -218,5 +218,94 @@ describe("AithosAuth.signUp — Ethos bootstrap", () => {
218
218
  assert.equal(publishCalls, 3);
219
219
  assert.equal(auth.canSignAsOwner(), false);
220
220
  });
221
+ /* ------------------------------------------------------------------------ */
222
+ /* alpha.36 — defense-in-depth for legacy backends without semantic-equal */
223
+ /* ------------------------------------------------------------------------ */
224
+ it("treats -32022 'different did.json already published' as a no-op success", async () => {
225
+ // This is the regression introduced in alpha.33: republishing the same
226
+ // identity returns -32022 because `signedDidDocument()` regenerates
227
+ // `aithos.created_at` on every call. For an honest signer, this is
228
+ // semantically a no-op — the Ethos is published, crypto material matches.
229
+ // The SDK swallows this specific case so chatty publish_identity callers
230
+ // (signInCustodial, verifyEmail) don't break on every subsequent sign-in.
231
+ const { fetch: f, calls } = makeMockFetch([
232
+ { url: "/auth/register", method: "POST", respond: fakeRegisterOk },
233
+ {
234
+ url: "/mcp/primitives/write",
235
+ method: "POST",
236
+ respond: () => ({
237
+ json: {
238
+ jsonrpc: "2.0",
239
+ id: "publish_identity",
240
+ error: {
241
+ code: -32022,
242
+ message: "different did.json already published for this DID",
243
+ data: { existing_doc_url: "https://cdn.aithos.be/did.json" },
244
+ },
245
+ },
246
+ }),
247
+ },
248
+ ]);
249
+ const auth = makeAuth(f);
250
+ // Must succeed, not throw — the republish-conflict is masked.
251
+ await auth.signUp(validInput);
252
+ // Hydrate worked: owner is loaded.
253
+ assert.equal(auth.canSignAsOwner(), true);
254
+ // No retry on -32022 (deterministic) — exactly 1 publish call.
255
+ assert.equal(calls.filter((c) => c.url.includes("/mcp/primitives/write")).length, 1, "publish_identity must not retry on -32022");
256
+ });
257
+ it("still throws ethos_bootstrap_failed on -32022 with a DIFFERENT message", async () => {
258
+ // The shim is narrow: it ONLY swallows the specific
259
+ // "different did.json already published" message. Other -32022 cases
260
+ // (server might use the same code for different semantics) must still
261
+ // bubble up as ethos_bootstrap_failed.
262
+ const { fetch: f } = makeMockFetch([
263
+ { url: "/auth/register", method: "POST", respond: fakeRegisterOk },
264
+ {
265
+ url: "/mcp/primitives/write",
266
+ method: "POST",
267
+ respond: () => ({
268
+ json: {
269
+ jsonrpc: "2.0",
270
+ id: "publish_identity",
271
+ error: {
272
+ code: -32022,
273
+ message: "subject identity is tombstoned",
274
+ },
275
+ },
276
+ }),
277
+ },
278
+ ]);
279
+ const auth = makeAuth(f);
280
+ await assert.rejects(() => auth.signUp(validInput), (e) => e instanceof AithosSDKError &&
281
+ e.code === "ethos_bootstrap_failed" &&
282
+ /tombstoned/i.test(e.message));
283
+ assert.equal(auth.canSignAsOwner(), false);
284
+ });
285
+ it("still throws ethos_bootstrap_failed on the conflict message under a different code", async () => {
286
+ // Conversely, an error matching the message but with a code OTHER than
287
+ // -32022 is not swallowed — we anchor on both (code, message) to keep
288
+ // the shim narrow.
289
+ const { fetch: f } = makeMockFetch([
290
+ { url: "/auth/register", method: "POST", respond: fakeRegisterOk },
291
+ {
292
+ url: "/mcp/primitives/write",
293
+ method: "POST",
294
+ respond: () => ({
295
+ json: {
296
+ jsonrpc: "2.0",
297
+ id: "publish_identity",
298
+ error: {
299
+ code: -32000,
300
+ message: "different did.json already published for this DID",
301
+ },
302
+ },
303
+ }),
304
+ },
305
+ ]);
306
+ const auth = makeAuth(f);
307
+ await assert.rejects(() => auth.signUp(validInput), (e) => e instanceof AithosSDKError && e.code === "ethos_bootstrap_failed");
308
+ assert.equal(auth.canSignAsOwner(), false);
309
+ });
221
310
  });
222
311
  //# sourceMappingURL=signup-bootstrap.test.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aithos/sdk",
3
- "version": "0.1.0-alpha.35",
4
- "description": "Aithos SDK \u2014 high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
3
+ "version": "0.1.0-alpha.36",
4
+ "description": "Aithos SDK high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
5
5
  "keywords": [
6
6
  "aithos",
7
7
  "sdk",
@@ -39,15 +39,6 @@
39
39
  "README.md",
40
40
  "LICENSE"
41
41
  ],
42
- "scripts": {
43
- "build": "tsc",
44
- "build:test": "tsc -p tsconfig.test.json",
45
- "check-types": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
46
- "test": "npm run clean && npm run build && npm run build:test && cd dist && node --test",
47
- "test:watch": "cd dist && node --test --watch",
48
- "clean": "rm -rf dist",
49
- "prepublishOnly": "npm run clean && npm run build && npm test"
50
- },
51
42
  "engines": {
52
43
  "node": ">=20"
53
44
  },
@@ -63,5 +54,13 @@
63
54
  "publishConfig": {
64
55
  "access": "public",
65
56
  "tag": "alpha"
57
+ },
58
+ "scripts": {
59
+ "build": "tsc",
60
+ "build:test": "tsc -p tsconfig.test.json",
61
+ "check-types": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
62
+ "test": "npm run clean && npm run build && npm run build:test && cd dist && node --test",
63
+ "test:watch": "cd dist && node --test --watch",
64
+ "clean": "rm -rf dist"
66
65
  }
67
- }
66
+ }