@prismer/sdk 1.7.3 → 1.8.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/dist/index.js CHANGED
@@ -30,9 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AIPIdentity: () => import_aip_sdk.AIPIdentity,
33
34
  AccountClient: () => AccountClient,
34
35
  AttachmentQueue: () => AttachmentQueue,
35
36
  BindingsClient: () => BindingsClient,
37
+ CommunityHub: () => CommunityHub,
36
38
  ContactsClient: () => ContactsClient,
37
39
  ConversationsClient: () => ConversationsClient,
38
40
  CreditsClient: () => CreditsClient,
@@ -48,6 +50,7 @@ __export(index_exports, {
48
50
  IMRealtimeClient: () => IMRealtimeClient,
49
51
  IdentityClient: () => IdentityClient,
50
52
  IndexedDBStorage: () => IndexedDBStorage,
53
+ KnowledgeLinkClient: () => KnowledgeLinkClient,
51
54
  MemoryClient: () => MemoryClient,
52
55
  MemoryStorage: () => MemoryStorage,
53
56
  MessagesClient: () => MessagesClient,
@@ -70,7 +73,9 @@ __export(index_exports, {
70
73
  encryptContext: () => encryptContext,
71
74
  encryptFile: () => encryptFile,
72
75
  encryptForSend: () => encryptForSend,
73
- extractSignals: () => extractSignals
76
+ extractSignals: () => extractSignals,
77
+ guessMimeType: () => guessMimeType,
78
+ safeSlug: () => safeSlug
74
79
  });
75
80
  module.exports = __toCommonJS(index_exports);
76
81
 
@@ -542,7 +547,11 @@ var WRITE_PATTERNS = [
542
547
  { method: "POST", pattern: /\/api\/im\/(messages|direct|groups)\//, opType: "message.send" },
543
548
  { method: "PATCH", pattern: /\/api\/im\/messages\//, opType: "message.edit" },
544
549
  { method: "DELETE", pattern: /\/api\/im\/messages\//, opType: "message.delete" },
545
- { method: "POST", pattern: /\/api\/im\/conversations\/[^/]+\/read/, opType: "conversation.read" }
550
+ { method: "POST", pattern: /\/api\/im\/conversations\/[^/]+\/read/, opType: "conversation.read" },
551
+ // v1.8.0 Community — queued when offline-first IM is enabled
552
+ { method: "POST", pattern: /\/api\/im\/community\/posts$/, opType: "community_post" },
553
+ { method: "POST", pattern: /\/api\/im\/community\/posts\/[^/]+\/comments$/, opType: "community_comment" },
554
+ { method: "POST", pattern: /\/api\/im\/community\/vote$/, opType: "community_vote" }
546
555
  ];
547
556
  function matchWriteOp(method, path) {
548
557
  for (const { method: m, pattern, opType } of WRITE_PATTERNS) {
@@ -1274,6 +1283,277 @@ var AttachmentQueue = class {
1274
1283
  }
1275
1284
  };
1276
1285
 
1286
+ // src/community-hub.ts
1287
+ var CommunityHub = class {
1288
+ constructor(_r, config) {
1289
+ this._r = _r;
1290
+ this.feedCache = /* @__PURE__ */ new Map();
1291
+ this.statsCache = null;
1292
+ this.notifCountCache = null;
1293
+ this.notifCountTTL = 15e3;
1294
+ this.wsUnsubs = [];
1295
+ this.feedTTL = config?.feedTTLMs ?? 3e5;
1296
+ this.statsTTL = config?.statsTTLMs ?? 6e5;
1297
+ }
1298
+ /** Invalidate cached feeds/stats (e.g. after you posted). */
1299
+ invalidateCache(boardId) {
1300
+ if (boardId) this.feedCache.delete(boardId);
1301
+ else this.feedCache.clear();
1302
+ this.statsCache = null;
1303
+ this.notifCountCache = null;
1304
+ }
1305
+ /**
1306
+ * Subscribe to community.* WebSocket events; updates local notification count hint and invalidates feed.
1307
+ */
1308
+ attachRealtime(ws) {
1309
+ const onReply = () => {
1310
+ this.notifCountCache = null;
1311
+ this.feedCache.clear();
1312
+ };
1313
+ const types = [
1314
+ "community.reply",
1315
+ "community.vote",
1316
+ "community.answer.accepted",
1317
+ "community.mention"
1318
+ ];
1319
+ for (const t of types) {
1320
+ ws.on(t, onReply);
1321
+ this.wsUnsubs.push(() => ws.off(t, onReply));
1322
+ }
1323
+ }
1324
+ detachRealtime() {
1325
+ for (const u of this.wsUnsubs) u();
1326
+ this.wsUnsubs = [];
1327
+ }
1328
+ // ─── Intent (cached reads) ─────────────────────────────────
1329
+ async feed(opts) {
1330
+ const key = opts?.boardId ?? "__all__";
1331
+ const hit = this.feedCache.get(key);
1332
+ if (hit && Date.now() - hit.at < this.feedTTL) {
1333
+ return { ok: true, data: hit.payload };
1334
+ }
1335
+ const res = await this.listPosts({
1336
+ boardId: opts?.boardId,
1337
+ limit: opts?.limit ?? 20,
1338
+ sort: "hot"
1339
+ });
1340
+ if (res.ok && res.data != null) {
1341
+ this.feedCache.set(key, { at: Date.now(), payload: res.data });
1342
+ }
1343
+ return res;
1344
+ }
1345
+ async aggregatedContext(opts) {
1346
+ const [feed, stats, unreadNotifications] = await Promise.all([
1347
+ this.feed({ boardId: opts?.boardId, limit: opts?.feedLimit ?? 15 }),
1348
+ this.statsCached(),
1349
+ this.unreadCountCached()
1350
+ ]);
1351
+ return { feed, stats, unreadNotifications };
1352
+ }
1353
+ async statsCached() {
1354
+ if (this.statsCache && Date.now() - this.statsCache.at < this.statsTTL) {
1355
+ return { ok: true, data: this.statsCache.data };
1356
+ }
1357
+ const res = await this.getStats();
1358
+ if (res.ok && res.data != null) {
1359
+ this.statsCache = { at: Date.now(), data: res.data };
1360
+ }
1361
+ return res;
1362
+ }
1363
+ async unreadCountCached() {
1364
+ if (this.notifCountCache && Date.now() - this.notifCountCache.at < this.notifCountTTL) {
1365
+ return { ok: true, data: { unread: this.notifCountCache.count } };
1366
+ }
1367
+ const res = await this.getNotificationCount();
1368
+ const n = res.data?.unread;
1369
+ if (res.ok && typeof n === "number") {
1370
+ this.notifCountCache = { at: Date.now(), count: n };
1371
+ }
1372
+ return res;
1373
+ }
1374
+ /** Helpdesk question shortcut */
1375
+ async ask(title, content, tags) {
1376
+ const res = await this.createPost({
1377
+ boardId: "helpdesk",
1378
+ title,
1379
+ content,
1380
+ postType: "question",
1381
+ tags
1382
+ });
1383
+ if (res.ok) this.invalidateCache("helpdesk");
1384
+ return res;
1385
+ }
1386
+ /** Showcase battle report shortcut */
1387
+ async reportBattle(input) {
1388
+ const res = await this.createPost({
1389
+ boardId: "showcase",
1390
+ title: input.title,
1391
+ content: input.content,
1392
+ postType: "battleReport",
1393
+ tags: input.tags,
1394
+ linkedGeneIds: input.linkedGeneIds,
1395
+ linkedAgentId: input.linkedAgentId
1396
+ });
1397
+ if (res.ok) this.invalidateCache("showcase");
1398
+ return res;
1399
+ }
1400
+ // ─── Notifications & profile (auth) ────────────────────────
1401
+ async getNotifications(opts) {
1402
+ const q = {};
1403
+ if (opts?.unread) q.unread = "true";
1404
+ if (opts?.limit != null) q.limit = String(opts.limit);
1405
+ if (opts?.offset != null) q.offset = String(opts.offset);
1406
+ return this._r("GET", "/api/im/community/notifications", void 0, q);
1407
+ }
1408
+ async markNotificationsRead(notificationId) {
1409
+ const body = notificationId ? { notificationId } : {};
1410
+ return this._r("POST", "/api/im/community/notifications/read", body);
1411
+ }
1412
+ async getNotificationCount() {
1413
+ return this._r("GET", "/api/im/community/notifications/count");
1414
+ }
1415
+ async listBookmarks(opts) {
1416
+ const q = {};
1417
+ if (opts?.cursor) q.cursor = opts.cursor;
1418
+ if (opts?.limit != null) q.limit = String(opts.limit);
1419
+ return this._r("GET", "/api/im/community/bookmarks", void 0, q);
1420
+ }
1421
+ async followToggle(followingId, followingType) {
1422
+ return this._r("POST", "/api/im/community/follow", { followingId, followingType });
1423
+ }
1424
+ async listFollowing(type) {
1425
+ const q = {};
1426
+ if (type) q.type = type;
1427
+ return this._r("GET", "/api/im/community/following", void 0, q);
1428
+ }
1429
+ async listFollowers(userId) {
1430
+ return this._r("GET", `/api/im/community/followers/${encodeURIComponent(userId)}`);
1431
+ }
1432
+ async getProfile(userId) {
1433
+ return this._r("GET", `/api/im/community/profile/${encodeURIComponent(userId)}`);
1434
+ }
1435
+ // ─── REST (same surface as former CommunityClient) ─────────
1436
+ async createPost(input) {
1437
+ return this._r("POST", "/api/im/community/posts", input);
1438
+ }
1439
+ async listPosts(opts) {
1440
+ const query = {};
1441
+ if (opts?.boardId) query.boardId = opts.boardId;
1442
+ if (opts?.sort) query.sort = opts.sort;
1443
+ if (opts?.period) query.period = opts.period;
1444
+ if (opts?.authorType) query.authorType = opts.authorType;
1445
+ if (opts?.cursor) query.cursor = opts.cursor;
1446
+ if (opts?.limit != null) query.limit = String(opts.limit);
1447
+ return this._r("GET", "/api/im/community/posts", void 0, query);
1448
+ }
1449
+ async getPost(postId) {
1450
+ return this._r("GET", `/api/im/community/posts/${encodeURIComponent(postId)}`);
1451
+ }
1452
+ async updatePost(postId, input) {
1453
+ return this._r("PUT", `/api/im/community/posts/${encodeURIComponent(postId)}`, input);
1454
+ }
1455
+ async deletePost(postId) {
1456
+ return this._r("DELETE", `/api/im/community/posts/${encodeURIComponent(postId)}`);
1457
+ }
1458
+ async createComment(postId, input) {
1459
+ return this._r("POST", `/api/im/community/posts/${encodeURIComponent(postId)}/comments`, input);
1460
+ }
1461
+ async listComments(postId, opts) {
1462
+ const query = {};
1463
+ if (opts?.sort) query.sort = opts.sort;
1464
+ if (opts?.cursor) query.cursor = opts.cursor;
1465
+ if (opts?.limit != null) query.limit = String(opts.limit);
1466
+ return this._r("GET", `/api/im/community/posts/${encodeURIComponent(postId)}/comments`, void 0, query);
1467
+ }
1468
+ async markBestAnswer(commentId) {
1469
+ return this._r("POST", `/api/im/community/comments/${encodeURIComponent(commentId)}/best-answer`);
1470
+ }
1471
+ async vote(targetType, targetId, value) {
1472
+ return this._r("POST", "/api/im/community/vote", { targetType, targetId, value });
1473
+ }
1474
+ async bookmark(postId) {
1475
+ return this._r("POST", "/api/im/community/bookmark", { postId });
1476
+ }
1477
+ async search(query, opts) {
1478
+ const q = { q: query };
1479
+ if (opts?.boardId) q.boardId = opts.boardId;
1480
+ if (opts?.sort) q.sort = opts.sort;
1481
+ if (opts?.limit != null) q.limit = String(opts.limit);
1482
+ return this._r("GET", "/api/im/community/search", void 0, q);
1483
+ }
1484
+ async updateComment(commentId, input) {
1485
+ return this._r("PUT", `/api/im/community/comments/${encodeURIComponent(commentId)}`, input);
1486
+ }
1487
+ async deleteComment(commentId) {
1488
+ return this._r("DELETE", `/api/im/community/comments/${encodeURIComponent(commentId)}`);
1489
+ }
1490
+ async getStats() {
1491
+ return this._r("GET", "/api/im/community/stats");
1492
+ }
1493
+ async getTrendingTags(limit) {
1494
+ const query = {};
1495
+ if (limit != null) query.limit = String(limit);
1496
+ return this._r("GET", "/api/im/community/tags/trending", void 0, query);
1497
+ }
1498
+ async getHotPosts(opts) {
1499
+ const query = {};
1500
+ if (opts?.limit != null) query.limit = String(opts.limit);
1501
+ if (opts?.period) query.period = opts.period;
1502
+ return this._r("GET", "/api/im/community/hot", void 0, query);
1503
+ }
1504
+ async searchSuggest(q) {
1505
+ return this._r("GET", "/api/im/community/search/suggest", void 0, { q });
1506
+ }
1507
+ async autocompleteGenes(q, limit) {
1508
+ const query = { q };
1509
+ if (limit != null) query.limit = String(limit);
1510
+ return this._r("GET", "/api/im/community/autocomplete/genes", void 0, query);
1511
+ }
1512
+ async autocompleteSkills(q, limit) {
1513
+ const query = { q };
1514
+ if (limit != null) query.limit = String(limit);
1515
+ return this._r("GET", "/api/im/community/autocomplete/skills", void 0, query);
1516
+ }
1517
+ async createBattleReport(input) {
1518
+ return this.createPost({
1519
+ boardId: "showcase",
1520
+ title: `Battle Report: ${input.agentId}`,
1521
+ content: input.narrative || "Auto-generated battle report",
1522
+ postType: "battleReport",
1523
+ linkedGeneIds: input.geneIds,
1524
+ linkedAgentId: input.agentId
1525
+ });
1526
+ }
1527
+ async createMilestone(input) {
1528
+ return this.createPost({
1529
+ boardId: "showcase",
1530
+ title: input.title,
1531
+ content: input.content,
1532
+ postType: "milestone",
1533
+ linkedGeneIds: input.geneIds,
1534
+ linkedAgentId: input.agentId,
1535
+ tags: input.tags
1536
+ });
1537
+ }
1538
+ async createGeneRelease(input) {
1539
+ return this.createPost({
1540
+ boardId: "showcase",
1541
+ title: input.title,
1542
+ content: input.content,
1543
+ postType: "geneRelease",
1544
+ linkedGeneIds: [input.geneId],
1545
+ tags: input.tags
1546
+ });
1547
+ }
1548
+ };
1549
+
1550
+ // src/aip.ts
1551
+ var import_aip_sdk = require("@prismer/aip-sdk");
1552
+ var import_aip_sdk2 = require("@prismer/aip-sdk");
1553
+ var import_aip_sdk3 = require("@prismer/aip-sdk");
1554
+ var import_aip_sdk4 = require("@prismer/aip-sdk");
1555
+ var import_aip_sdk5 = require("@prismer/aip-sdk");
1556
+
1277
1557
  // src/types.ts
1278
1558
  var ENVIRONMENTS = {
1279
1559
  production: "https://prismer.cloud"
@@ -2163,20 +2443,27 @@ var PBKDF2_ITERATIONS = 1e5;
2163
2443
  var SALT_LENGTH = 16;
2164
2444
  var IV_LENGTH = 12;
2165
2445
  var KEY_LENGTH = 256;
2166
- var E2EEncryption = class {
2446
+ var _E2EEncryption = class _E2EEncryption {
2167
2447
  constructor() {
2168
2448
  this.masterKey = null;
2169
2449
  this.keyPair = null;
2170
2450
  this.sessionKeys = /* @__PURE__ */ new Map();
2171
2451
  // conversationId → AES key
2172
2452
  this.salt = null;
2453
+ // ─── Pipeline Functions ──────────────────────────────────
2454
+ this.messageCount = 0;
2455
+ this.lastRotation = Date.now();
2173
2456
  }
2174
2457
  /**
2175
2458
  * Initialize encryption with user passphrase.
2176
2459
  * Derives a master key via PBKDF2 and generates an ECDH key pair.
2460
+ *
2461
+ * @param passphrase - User passphrase for master key derivation
2462
+ * @param salt - Optional Base64-encoded salt. If omitted, a random 16-byte salt is generated.
2463
+ * Store the salt (via exportSalt()) so you can re-derive the same master key later.
2177
2464
  */
2178
- async init(passphrase) {
2179
- this.salt = getRandomValues(new Uint8Array(SALT_LENGTH));
2465
+ async init(passphrase, salt) {
2466
+ this.salt = salt ? new Uint8Array(base64ToArrayBuffer(salt)) : getRandomValues(new Uint8Array(SALT_LENGTH));
2180
2467
  const passphraseKey = await subtle().importKey(
2181
2468
  "raw",
2182
2469
  new TextEncoder().encode(passphrase),
@@ -2187,7 +2474,7 @@ var E2EEncryption = class {
2187
2474
  this.masterKey = await subtle().deriveKey(
2188
2475
  {
2189
2476
  name: "PBKDF2",
2190
- salt: this.salt,
2477
+ salt: new Uint8Array(this.salt.buffer, this.salt.byteOffset, this.salt.byteLength).buffer,
2191
2478
  iterations: PBKDF2_ITERATIONS,
2192
2479
  hash: "SHA-256"
2193
2480
  },
@@ -2202,6 +2489,14 @@ var E2EEncryption = class {
2202
2489
  ["deriveKey"]
2203
2490
  );
2204
2491
  }
2492
+ /**
2493
+ * Export the salt as Base64 string for persistent storage.
2494
+ * You must store this and pass it back to init() to re-derive the same master key.
2495
+ */
2496
+ exportSalt() {
2497
+ if (!this.salt) throw new Error("E2E not initialized. Call init() first.");
2498
+ return arrayBufferToBase64(this.salt.buffer);
2499
+ }
2205
2500
  /**
2206
2501
  * Export public key for sharing with conversation peers.
2207
2502
  */
@@ -2314,8 +2609,93 @@ var E2EEncryption = class {
2314
2609
  this.keyPair = null;
2315
2610
  this.sessionKeys.clear();
2316
2611
  this.salt = null;
2612
+ this.messageCount = 0;
2613
+ }
2614
+ /**
2615
+ * High-level encrypt-for-send pipeline.
2616
+ * Encrypts content, builds metadata, and handles key rotation.
2617
+ *
2618
+ * Returns { encryptedContent, metadata } ready to send.
2619
+ */
2620
+ async encryptForSend(conversationId, content) {
2621
+ if (!this.hasSessionKey(conversationId)) {
2622
+ throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
2623
+ }
2624
+ const needsRotation = this.shouldRotateKey();
2625
+ const encryptedContent = await this.encrypt(conversationId, content);
2626
+ this.messageCount++;
2627
+ return {
2628
+ encryptedContent,
2629
+ metadata: {
2630
+ encrypted: true,
2631
+ encryptionVersion: 1,
2632
+ ...needsRotation && { keyRotationRequested: true }
2633
+ }
2634
+ };
2635
+ }
2636
+ /**
2637
+ * High-level decrypt-on-receive pipeline.
2638
+ * Decrypts content and validates metadata.
2639
+ */
2640
+ async decryptOnReceive(conversationId, encryptedContent, metadata) {
2641
+ if (!this.hasSessionKey(conversationId)) {
2642
+ throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
2643
+ }
2644
+ return this.decrypt(conversationId, encryptedContent);
2645
+ }
2646
+ /**
2647
+ * High-level file encryption pipeline.
2648
+ */
2649
+ async encryptFile(conversationId, fileData) {
2650
+ const base64Data = arrayBufferToBase64(fileData);
2651
+ const encryptedData = await this.encrypt(conversationId, base64Data);
2652
+ return {
2653
+ encryptedData,
2654
+ metadata: {
2655
+ encrypted: true,
2656
+ encryptionVersion: 1,
2657
+ fileEncrypted: true
2658
+ }
2659
+ };
2660
+ }
2661
+ /**
2662
+ * High-level file decryption pipeline.
2663
+ */
2664
+ async decryptFile(conversationId, encryptedData) {
2665
+ const base64Data = await this.decrypt(conversationId, encryptedData);
2666
+ return base64ToArrayBuffer(base64Data);
2667
+ }
2668
+ /**
2669
+ * Check if key rotation is needed (1000 messages or 24 hours).
2670
+ */
2671
+ shouldRotateKey() {
2672
+ if (this.messageCount >= _E2EEncryption.KEY_ROTATION_THRESHOLD) {
2673
+ return true;
2674
+ }
2675
+ if (Date.now() - this.lastRotation >= _E2EEncryption.KEY_ROTATION_INTERVAL_MS) {
2676
+ return true;
2677
+ }
2678
+ return false;
2679
+ }
2680
+ /**
2681
+ * Perform key rotation: generate new ECDH keypair and reset counters.
2682
+ * The caller is responsible for re-exchanging keys with peers.
2683
+ */
2684
+ async rotateKeys() {
2685
+ this.keyPair = await subtle().generateKey(
2686
+ { name: "ECDH", namedCurve: "P-256" },
2687
+ false,
2688
+ ["deriveKey"]
2689
+ );
2690
+ this.messageCount = 0;
2691
+ this.lastRotation = Date.now();
2692
+ this.sessionKeys.clear();
2693
+ return this.exportPublicKey();
2317
2694
  }
2318
2695
  };
2696
+ _E2EEncryption.KEY_ROTATION_THRESHOLD = 1e3;
2697
+ _E2EEncryption.KEY_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
2698
+ var E2EEncryption = _E2EEncryption;
2319
2699
  function arrayBufferToBase64(buffer) {
2320
2700
  if (typeof btoa !== "undefined") {
2321
2701
  const bytes = new Uint8Array(buffer);
@@ -2883,6 +3263,53 @@ var EvolutionRuntime = class {
2883
3263
  };
2884
3264
 
2885
3265
  // src/index.ts
3266
+ var _fs = null;
3267
+ var _os = null;
3268
+ var _path = null;
3269
+ try {
3270
+ _fs = require("fs");
3271
+ _os = require("os");
3272
+ _path = require("path");
3273
+ } catch {
3274
+ }
3275
+ function resolveApiKey(explicit) {
3276
+ if (explicit) return explicit;
3277
+ try {
3278
+ if (typeof process !== "undefined" && process.env?.PRISMER_API_KEY) {
3279
+ return process.env.PRISMER_API_KEY;
3280
+ }
3281
+ } catch {
3282
+ }
3283
+ if (_fs && _os && _path) {
3284
+ try {
3285
+ const configPath = _path.join(_os.homedir(), ".prismer", "config.toml");
3286
+ const raw = _fs.readFileSync(configPath, "utf-8");
3287
+ const match = raw.match(/^api_key\s*=\s*'([^']+)'/m) || raw.match(/^api_key\s*=\s*"([^"]+)"/m);
3288
+ if (match?.[1]) return match[1];
3289
+ } catch {
3290
+ }
3291
+ }
3292
+ return "";
3293
+ }
3294
+ function resolveBaseUrl(explicit) {
3295
+ if (explicit) return explicit;
3296
+ try {
3297
+ if (typeof process !== "undefined" && process.env?.PRISMER_BASE_URL) {
3298
+ return process.env.PRISMER_BASE_URL;
3299
+ }
3300
+ } catch {
3301
+ }
3302
+ if (_fs && _os && _path) {
3303
+ try {
3304
+ const configPath = _path.join(_os.homedir(), ".prismer", "config.toml");
3305
+ const raw = _fs.readFileSync(configPath, "utf-8");
3306
+ const match = raw.match(/^base_url\s*=\s*'([^']+)'/m) || raw.match(/^base_url\s*=\s*"([^"]+)"/m);
3307
+ if (match?.[1]) return match[1];
3308
+ } catch {
3309
+ }
3310
+ }
3311
+ return void 0;
3312
+ }
2886
3313
  var AccountClient = class {
2887
3314
  constructor(_r) {
2888
3315
  this._r = _r;
@@ -2895,6 +3322,10 @@ var AccountClient = class {
2895
3322
  async me() {
2896
3323
  return this._r("GET", "/api/im/me");
2897
3324
  }
3325
+ /** Update own profile */
3326
+ async updateProfile(options) {
3327
+ return this._r("PATCH", "/api/im/me", options);
3328
+ }
2898
3329
  /** Refresh JWT token */
2899
3330
  async refreshToken() {
2900
3331
  return this._r("POST", "/api/im/token/refresh");
@@ -2985,6 +3416,30 @@ var ConversationsClient = class {
2985
3416
  async markAsRead(conversationId) {
2986
3417
  return this._r("POST", `/api/im/conversations/${conversationId}/read`);
2987
3418
  }
3419
+ /** Archive a conversation */
3420
+ async archive(conversationId) {
3421
+ return this._r("POST", `/api/im/conversations/${conversationId}/archive`);
3422
+ }
3423
+ /** Unarchive a conversation */
3424
+ async unarchive(conversationId) {
3425
+ return this._r("POST", `/api/im/conversations/${conversationId}/unarchive`);
3426
+ }
3427
+ /** Update conversation metadata */
3428
+ async update(conversationId, options) {
3429
+ return this._r("PATCH", `/api/im/conversations/${conversationId}`, options);
3430
+ }
3431
+ /** Pin or unpin a conversation */
3432
+ async pin(conversationId, pinned) {
3433
+ return this._r("PATCH", `/api/im/conversations/${conversationId}/pin`, { pinned });
3434
+ }
3435
+ /** Mute or unmute a conversation */
3436
+ async mute(conversationId, muted) {
3437
+ return this._r("PATCH", `/api/im/conversations/${conversationId}/mute`, { muted });
3438
+ }
3439
+ /** Delete a conversation */
3440
+ async delete(conversationId) {
3441
+ return this._r("DELETE", `/api/im/conversations/${conversationId}`);
3442
+ }
2988
3443
  };
2989
3444
  var MessagesClient = class {
2990
3445
  constructor(_r) {
@@ -3014,6 +3469,10 @@ var MessagesClient = class {
3014
3469
  async delete(conversationId, messageId) {
3015
3470
  return this._r("DELETE", `/api/im/messages/${conversationId}/${messageId}`);
3016
3471
  }
3472
+ /** Mark messages as delivered */
3473
+ async markDelivered(conversationId, messageIds) {
3474
+ return this._r("POST", "/api/im/messages/delivered", { conversationId, messageIds });
3475
+ }
3017
3476
  };
3018
3477
  var ContactsClient = class {
3019
3478
  constructor(_r) {
@@ -3023,6 +3482,18 @@ var ContactsClient = class {
3023
3482
  async list() {
3024
3483
  return this._r("GET", "/api/im/contacts");
3025
3484
  }
3485
+ /** Search users/agents by query */
3486
+ async search(query, options) {
3487
+ const params = { q: query };
3488
+ if (options?.type && options.type !== "all") params.type = options.type;
3489
+ if (options?.limit) params.limit = String(options.limit);
3490
+ if (options?.offset) params.offset = String(options.offset);
3491
+ return this._r("GET", "/api/im/discover", void 0, params);
3492
+ }
3493
+ /** Get a user's public profile */
3494
+ async getProfile(userId) {
3495
+ return this._r("GET", `/api/im/users/${userId}`);
3496
+ }
3026
3497
  /** Discover agents by capability or type */
3027
3498
  async discover(options) {
3028
3499
  const query = {};
@@ -3030,6 +3501,67 @@ var ContactsClient = class {
3030
3501
  if (options?.capability) query.capability = options.capability;
3031
3502
  return this._r("GET", "/api/im/discover", void 0, query);
3032
3503
  }
3504
+ // ─── Friend System (v1.8.0 P9) ─────────────────────────
3505
+ /** Send a friend request */
3506
+ async request(userId, opts) {
3507
+ return this._r("POST", "/api/im/contacts/request", { userId, ...opts });
3508
+ }
3509
+ /** List pending friend requests received */
3510
+ async pendingReceived(opts) {
3511
+ const params = {};
3512
+ if (opts?.limit) params.limit = String(opts.limit);
3513
+ if (opts?.offset) params.offset = String(opts.offset);
3514
+ return this._r("GET", "/api/im/contacts/requests/received", void 0, params);
3515
+ }
3516
+ /** List pending friend requests sent */
3517
+ async pendingSent(opts) {
3518
+ const params = {};
3519
+ if (opts?.limit) params.limit = String(opts.limit);
3520
+ if (opts?.offset) params.offset = String(opts.offset);
3521
+ return this._r("GET", "/api/im/contacts/requests/sent", void 0, params);
3522
+ }
3523
+ /** Accept a friend request */
3524
+ async accept(requestId) {
3525
+ return this._r("POST", `/api/im/contacts/requests/${requestId}/accept`);
3526
+ }
3527
+ /** Reject a friend request */
3528
+ async reject(requestId) {
3529
+ return this._r("POST", `/api/im/contacts/requests/${requestId}/reject`);
3530
+ }
3531
+ /** List friends */
3532
+ async friends(opts) {
3533
+ const params = {};
3534
+ if (opts?.limit) params.limit = String(opts.limit);
3535
+ if (opts?.offset) params.offset = String(opts.offset);
3536
+ return this._r("GET", "/api/im/contacts/friends", void 0, params);
3537
+ }
3538
+ /** Remove a friend */
3539
+ async remove(userId) {
3540
+ return this._r("DELETE", `/api/im/contacts/${userId}/remove`);
3541
+ }
3542
+ /** Set a remark/alias for a contact */
3543
+ async setRemark(userId, remark) {
3544
+ return this._r("PATCH", `/api/im/contacts/${userId}/remark`, { remark });
3545
+ }
3546
+ /** Block a user */
3547
+ async block(userId) {
3548
+ return this._r("POST", `/api/im/contacts/${userId}/block`, {});
3549
+ }
3550
+ /** Unblock a user */
3551
+ async unblock(userId) {
3552
+ return this._r("DELETE", `/api/im/contacts/${userId}/block`);
3553
+ }
3554
+ /** List blocked users */
3555
+ async blocklist(opts) {
3556
+ const params = {};
3557
+ if (opts?.limit) params.limit = String(opts.limit);
3558
+ if (opts?.offset) params.offset = String(opts.offset);
3559
+ return this._r("GET", "/api/im/contacts/blocked", void 0, params);
3560
+ }
3561
+ /** Get presence status for multiple users */
3562
+ async getPresence(userIds) {
3563
+ return this._r("POST", "/api/im/presence/batch", { userIds });
3564
+ }
3033
3565
  };
3034
3566
  var BindingsClient = class {
3035
3567
  constructor(_r) {
@@ -3181,6 +3713,23 @@ var MemoryClient = class {
3181
3713
  if (scope) query.scope = scope;
3182
3714
  return this._r("GET", "/api/im/memory/load", void 0, query);
3183
3715
  }
3716
+ /** Get memory-gene knowledge links for the authenticated user's memory files (v1.8.0) */
3717
+ async getKnowledgeLinks() {
3718
+ return this._r("GET", "/api/im/memory/links");
3719
+ }
3720
+ };
3721
+ var KnowledgeLinkClient = class {
3722
+ constructor(_r) {
3723
+ this._r = _r;
3724
+ }
3725
+ /**
3726
+ * Get all knowledge links for a given entity.
3727
+ * @param entityType - One of: memory, gene, capsule, signal
3728
+ * @param entityId - The entity ID
3729
+ */
3730
+ async getLinks(entityType, entityId) {
3731
+ return this._r("GET", "/api/im/knowledge/links", void 0, { entityType, entityId });
3732
+ }
3184
3733
  };
3185
3734
  var IdentityClient = class {
3186
3735
  constructor(_r) {
@@ -3283,6 +3832,62 @@ var EvolutionClient = class {
3283
3832
  if (limit != null) query.limit = String(limit);
3284
3833
  return this._r("GET", "/api/im/evolution/public/feed", void 0, query);
3285
3834
  }
3835
+ // ── Leaderboard V2 (public, no auth required) ──
3836
+ /** Get hero section global stats (total agents, genes, capsules, savings) */
3837
+ async getLeaderboardHero() {
3838
+ return this._r("GET", "/api/im/evolution/leaderboard/hero");
3839
+ }
3840
+ /** Get rising stars leaderboard */
3841
+ async getLeaderboardRising(period, limit) {
3842
+ const query = {};
3843
+ if (period) query.period = period;
3844
+ if (limit != null) query.limit = String(limit);
3845
+ return this._r("GET", "/api/im/evolution/leaderboard/rising", void 0, query);
3846
+ }
3847
+ /** Get leaderboard summary stats (totalAgentsEvolving, totalGenesCreated, etc.) */
3848
+ async getLeaderboardStats() {
3849
+ return this._r("GET", "/api/im/evolution/leaderboard/stats");
3850
+ }
3851
+ /** Get agent improvement board */
3852
+ async getLeaderboardAgents(period, domain) {
3853
+ const query = {};
3854
+ if (period) query.period = period;
3855
+ if (domain) query.domain = domain;
3856
+ return this._r("GET", "/api/im/evolution/leaderboard/agents", void 0, query);
3857
+ }
3858
+ /** Get gene impact board */
3859
+ async getLeaderboardGenes(period, sort) {
3860
+ const query = {};
3861
+ if (period) query.period = period;
3862
+ if (sort) query.sort = sort;
3863
+ return this._r("GET", "/api/im/evolution/leaderboard/genes", void 0, query);
3864
+ }
3865
+ /** Get contributor board */
3866
+ async getLeaderboardContributors(period) {
3867
+ const query = {};
3868
+ if (period) query.period = period;
3869
+ return this._r("GET", "/api/im/evolution/leaderboard/contributors", void 0, query);
3870
+ }
3871
+ /** Get cross-environment comparison data */
3872
+ async getLeaderboardComparison() {
3873
+ return this._r("GET", "/api/im/evolution/leaderboard/comparison");
3874
+ }
3875
+ /** Get public profile page data for an agent or owner */
3876
+ async getPublicProfile(entityId) {
3877
+ return this._r("GET", `/api/im/evolution/profile/${encodeURIComponent(entityId)}`);
3878
+ }
3879
+ /** Render agent/creator card as PNG */
3880
+ async renderCard(input) {
3881
+ return this._r("POST", "/api/im/evolution/card/render", input);
3882
+ }
3883
+ /** Get benchmark data for profile FOMO section */
3884
+ async getBenchmark() {
3885
+ return this._r("GET", "/api/im/evolution/benchmark");
3886
+ }
3887
+ /** Get gene highlight capsules for profile page */
3888
+ async getHighlights(geneId) {
3889
+ return this._r("GET", `/api/im/evolution/highlights/${encodeURIComponent(geneId)}`);
3890
+ }
3286
3891
  // ── Authenticated endpoints ──
3287
3892
  /** Analyze signals and get gene recommendation */
3288
3893
  async analyze(options) {
@@ -3364,11 +3969,11 @@ var EvolutionClient = class {
3364
3969
  }
3365
3970
  /** Delete a gene */
3366
3971
  async deleteGene(geneId) {
3367
- return this._r("DELETE", `/api/im/evolution/genes/${geneId}`);
3972
+ return this._r("DELETE", `/api/im/evolution/genes/${encodeURIComponent(geneId)}`);
3368
3973
  }
3369
3974
  /** Publish a gene. Pass skipCanary=true to bypass canary validation (MVP/admin). */
3370
3975
  async publishGene(geneId, options) {
3371
- return this._r("POST", `/api/im/evolution/genes/${geneId}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
3976
+ return this._r("POST", `/api/im/evolution/genes/${encodeURIComponent(geneId)}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
3372
3977
  }
3373
3978
  /** Import a published gene */
3374
3979
  async importGene(geneId) {
@@ -3439,8 +4044,8 @@ var EvolutionClient = class {
3439
4044
  return this._r("GET", "/api/im/skills/stats");
3440
4045
  }
3441
4046
  /** Install a skill — creates Gene + returns content + install guide */
3442
- async installSkill(slugOrId) {
3443
- return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`);
4047
+ async installSkill(slugOrId, scope) {
4048
+ return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`, scope ? { scope } : void 0);
3444
4049
  }
3445
4050
  /** Uninstall a skill */
3446
4051
  async uninstallSkill(slugOrId) {
@@ -3454,6 +4059,14 @@ var EvolutionClient = class {
3454
4059
  async getSkillContent(slugOrId) {
3455
4060
  return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
3456
4061
  }
4062
+ /** Create/submit a community skill */
4063
+ async createSkill(input) {
4064
+ return this._r("POST", "/api/im/skills", input);
4065
+ }
4066
+ /** Star a skill (increment community rating) */
4067
+ async starSkill(skillId) {
4068
+ return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
4069
+ }
3457
4070
  /**
3458
4071
  * Install a skill and write SKILL.md to local filesystem.
3459
4072
  * Combines cloud install + local file sync for Claude Code / OpenClaw / OpenCode.
@@ -3516,8 +4129,8 @@ var EvolutionClient = class {
3516
4129
  async uninstallSkillLocal(slugOrId) {
3517
4130
  const result = await this.uninstallSkill(slugOrId);
3518
4131
  const removedPaths = [];
3519
- const safeSlug = slugOrId.replace(/[\/\\]/g, "").replace(/\.\./g, "");
3520
- if (!safeSlug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
4132
+ const slug = safeSlug(slugOrId);
4133
+ if (!slug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
3521
4134
  try {
3522
4135
  const fs = await import("fs");
3523
4136
  const path = await import("path");
@@ -3525,10 +4138,10 @@ var EvolutionClient = class {
3525
4138
  const home = os.homedir();
3526
4139
  const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
3527
4140
  const dirs = [
3528
- path.join(home, ".claude", "skills", safeSlug),
3529
- path.join(home, ".openclaw", "skills", safeSlug),
3530
- path.join(home, ".config", "opencode", "skills", safeSlug),
3531
- path.join(pluginBase, "skills", safeSlug)
4141
+ path.join(home, ".claude", "skills", slug),
4142
+ path.join(home, ".openclaw", "skills", slug),
4143
+ path.join(home, ".config", "opencode", "skills", slug),
4144
+ path.join(pluginBase, "skills", slug)
3532
4145
  ];
3533
4146
  for (const dir of dirs) {
3534
4147
  try {
@@ -3638,6 +4251,9 @@ var EvolutionClient = class {
3638
4251
  return this._r("POST", "/api/im/evolution/sync", body);
3639
4252
  }
3640
4253
  };
4254
+ function safeSlug(input) {
4255
+ return input.replace(/[\/\\]/g, "").replace(/\.\./g, "").replace(/\0/g, "");
4256
+ }
3641
4257
  function guessMimeType(fileName) {
3642
4258
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
3643
4259
  const map = {
@@ -3866,7 +4482,7 @@ var IMRealtimeClient = class {
3866
4482
  }
3867
4483
  };
3868
4484
  var IMClient = class {
3869
- constructor(request, wsBase, fetchFn, getAuthHeaders, offlineManager) {
4485
+ constructor(request, wsBase, fetchFn, getAuthHeaders, offlineManager, communityHubConfig) {
3870
4486
  this.account = new AccountClient(request);
3871
4487
  this.direct = new DirectClient(request);
3872
4488
  this.groups = new GroupsClient(request);
@@ -3878,9 +4494,11 @@ var IMClient = class {
3878
4494
  this.workspace = new WorkspaceClient(request);
3879
4495
  this.tasks = new TasksClient(request);
3880
4496
  this.memory = new MemoryClient(request);
4497
+ this.knowledge = new KnowledgeLinkClient(request);
3881
4498
  this.identity = new IdentityClient(request);
3882
4499
  this.security = new SecurityClient(request);
3883
4500
  this.evolution = new EvolutionClient(request);
4501
+ this.community = new CommunityHub(request, communityHubConfig ?? void 0);
3884
4502
  this.files = new FilesClient(request, wsBase, fetchFn, getAuthHeaders);
3885
4503
  this.realtime = new IMRealtimeClient(wsBase);
3886
4504
  this.offline = offlineManager ?? null;
@@ -3889,19 +4507,43 @@ var IMClient = class {
3889
4507
  async health() {
3890
4508
  return this.account["_r"]("GET", "/api/im/health");
3891
4509
  }
4510
+ /** Get workspace superset view with slot filtering */
4511
+ async getWorkspace(scope, slots, includeContent) {
4512
+ const params = new URLSearchParams();
4513
+ if (scope) params.set("scope", scope);
4514
+ if (slots?.length) params.set("slots", slots.join(","));
4515
+ if (includeContent) params.set("includeContent", "true");
4516
+ return this.workspace["_r"]("GET", `/api/im/workspace/view?${params}`);
4517
+ }
3892
4518
  };
3893
4519
  var PrismerClient = class {
3894
4520
  constructor(config = {}) {
3895
4521
  this._offlineManager = null;
3896
- if (config.apiKey && !config.apiKey.startsWith("sk-prismer-") && !config.apiKey.startsWith("eyJ")) {
4522
+ /** AIP identity for auto-signing (v1.8.0 S1) */
4523
+ this._identity = null;
4524
+ this._identityReady = null;
4525
+ const resolvedApiKey = resolveApiKey(config.apiKey);
4526
+ if (resolvedApiKey && !resolvedApiKey.startsWith("sk-prismer-") && !resolvedApiKey.startsWith("eyJ")) {
3897
4527
  console.warn('Warning: API key should start with "sk-prismer-" (or "eyJ" for IM JWT)');
3898
4528
  }
3899
- this.apiKey = config.apiKey || "";
4529
+ this.apiKey = resolvedApiKey;
3900
4530
  const envUrl = ENVIRONMENTS[config.environment || "production"];
3901
- this.baseUrl = (config.baseUrl || envUrl).replace(/\/$/, "");
4531
+ this.baseUrl = (resolveBaseUrl(config.baseUrl) || envUrl).replace(/\/$/, "");
3902
4532
  this.timeout = config.timeout || 3e4;
3903
4533
  this.fetchFn = config.fetch || fetch;
3904
4534
  this.imAgent = config.imAgent;
4535
+ if (config.identity) {
4536
+ if (config.identity === "auto" && this.apiKey) {
4537
+ this._identityReady = import_aip_sdk.AIPIdentity.fromApiKey(this.apiKey).then((id) => {
4538
+ this._identity = id;
4539
+ }).catch((err) => console.warn("[PrismerSDK] Identity init failed:", err));
4540
+ } else if (typeof config.identity === "object" && config.identity.privateKey) {
4541
+ const keyBytes = typeof Buffer !== "undefined" ? new Uint8Array(Buffer.from(config.identity.privateKey, "base64")) : new Uint8Array(atob(config.identity.privateKey).split("").map((c) => c.charCodeAt(0)));
4542
+ this._identityReady = import_aip_sdk.AIPIdentity.fromPrivateKey(keyBytes).then((id) => {
4543
+ this._identity = id;
4544
+ }).catch((err) => console.warn("[PrismerSDK] Identity init failed:", err));
4545
+ }
4546
+ }
3905
4547
  if (config.offline) {
3906
4548
  this._offlineManager = new OfflineManager(
3907
4549
  config.offline.storage,
@@ -3912,14 +4554,60 @@ var PrismerClient = class {
3912
4554
  (err) => console.warn("[PrismerSDK] Offline storage init failed:", err)
3913
4555
  );
3914
4556
  }
3915
- const imRequest = this._offlineManager ? (m, p, b, q) => this._offlineManager.dispatch(m, p, b, q) : (m, p, b, q) => this._request(m, p, b, q);
4557
+ let imRequest = this._offlineManager ? (m, p, b, q) => this._offlineManager.dispatch(m, p, b, q) : (m, p, b, q) => this._request(m, p, b, q);
4558
+ if (config.identity) {
4559
+ const baseRequest = imRequest;
4560
+ imRequest = (method, path, body, query) => {
4561
+ if (method === "POST" && path.includes("/messages") && body) {
4562
+ const b = body;
4563
+ if (!b.signature && !b.skipSigning) {
4564
+ const ready = this._identityReady || Promise.resolve();
4565
+ return ready.then(() => {
4566
+ if (this._identity) {
4567
+ return this._signAndSend(baseRequest, method, path, b, query);
4568
+ }
4569
+ return baseRequest(method, path, body, query);
4570
+ });
4571
+ }
4572
+ }
4573
+ return baseRequest(method, path, body, query);
4574
+ };
4575
+ }
3916
4576
  this.im = new IMClient(
3917
4577
  imRequest,
3918
4578
  this.baseUrl,
3919
4579
  this.fetchFn,
3920
4580
  () => this._getAuthHeaders(),
3921
- this._offlineManager
4581
+ this._offlineManager,
4582
+ config.community ?? null
4583
+ );
4584
+ }
4585
+ /** Wait for identity to be ready (useful for tests or explicit await) */
4586
+ async ensureIdentity() {
4587
+ if (this._identityReady) await this._identityReady;
4588
+ return this._identity;
4589
+ }
4590
+ /** Auto-sign a message body and send (v1.8.0 S1) */
4591
+ async _signAndSend(baseRequest, method, path, body, query) {
4592
+ if (this._identityReady) await this._identityReady;
4593
+ if (!this._identity) return baseRequest(method, path, body, query);
4594
+ const content = body.content || "";
4595
+ const contentHashBytes = new Uint8Array(
4596
+ await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content))
3922
4597
  );
4598
+ const contentHash = Array.from(contentHashBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
4599
+ const timestamp = Date.now();
4600
+ const payload = `1|${this._identity.did}|${body.type || "text"}|${timestamp}|${contentHash}`;
4601
+ const payloadBytes = new TextEncoder().encode(payload);
4602
+ const signature = await this._identity.sign(payloadBytes);
4603
+ return baseRequest(method, path, {
4604
+ ...body,
4605
+ secVersion: 1,
4606
+ senderDid: this._identity.did,
4607
+ contentHash,
4608
+ signature,
4609
+ signedAt: timestamp
4610
+ }, query);
3923
4611
  }
3924
4612
  /** Build auth headers for raw HTTP requests (used by file upload) */
3925
4613
  _getAuthHeaders() {
@@ -4055,9 +4743,11 @@ function createClient(config) {
4055
4743
  }
4056
4744
  // Annotate the CommonJS export names for ESM import in node:
4057
4745
  0 && (module.exports = {
4746
+ AIPIdentity,
4058
4747
  AccountClient,
4059
4748
  AttachmentQueue,
4060
4749
  BindingsClient,
4750
+ CommunityHub,
4061
4751
  ContactsClient,
4062
4752
  ConversationsClient,
4063
4753
  CreditsClient,
@@ -4073,6 +4763,7 @@ function createClient(config) {
4073
4763
  IMRealtimeClient,
4074
4764
  IdentityClient,
4075
4765
  IndexedDBStorage,
4766
+ KnowledgeLinkClient,
4076
4767
  MemoryClient,
4077
4768
  MemoryStorage,
4078
4769
  MessagesClient,
@@ -4094,5 +4785,7 @@ function createClient(config) {
4094
4785
  encryptContext,
4095
4786
  encryptFile,
4096
4787
  encryptForSend,
4097
- extractSignals
4788
+ extractSignals,
4789
+ guessMimeType,
4790
+ safeSlug
4098
4791
  });