@prismer/sdk 1.7.1 → 1.7.4
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/LICENSE +21 -0
- package/README.md +515 -190
- package/dist/cli.js +3559 -416
- package/dist/index.d.mts +1100 -4
- package/dist/index.d.ts +1100 -4
- package/dist/index.js +1256 -10
- package/dist/index.mjs +1252 -9
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ 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,
|
|
@@ -39,11 +40,16 @@ __export(index_exports, {
|
|
|
39
40
|
DirectClient: () => DirectClient,
|
|
40
41
|
E2EEncryption: () => E2EEncryption,
|
|
41
42
|
ENVIRONMENTS: () => ENVIRONMENTS,
|
|
43
|
+
EvolutionCache: () => EvolutionCache,
|
|
44
|
+
EvolutionClient: () => EvolutionClient,
|
|
45
|
+
EvolutionRuntime: () => EvolutionRuntime,
|
|
42
46
|
FilesClient: () => FilesClient,
|
|
43
47
|
GroupsClient: () => GroupsClient,
|
|
44
48
|
IMClient: () => IMClient,
|
|
45
49
|
IMRealtimeClient: () => IMRealtimeClient,
|
|
50
|
+
IdentityClient: () => IdentityClient,
|
|
46
51
|
IndexedDBStorage: () => IndexedDBStorage,
|
|
52
|
+
MemoryClient: () => MemoryClient,
|
|
47
53
|
MemoryStorage: () => MemoryStorage,
|
|
48
54
|
MessagesClient: () => MessagesClient,
|
|
49
55
|
OfflineManager: () => OfflineManager,
|
|
@@ -51,10 +57,23 @@ __export(index_exports, {
|
|
|
51
57
|
RealtimeSSEClient: () => RealtimeSSEClient,
|
|
52
58
|
RealtimeWSClient: () => RealtimeWSClient,
|
|
53
59
|
SQLiteStorage: () => SQLiteStorage,
|
|
60
|
+
SecurityClient: () => SecurityClient,
|
|
54
61
|
TabCoordinator: () => TabCoordinator,
|
|
62
|
+
TasksClient: () => TasksClient,
|
|
55
63
|
WorkspaceClient: () => WorkspaceClient,
|
|
56
64
|
createClient: () => createClient,
|
|
57
|
-
|
|
65
|
+
createEnrichedExtractor: () => createEnrichedExtractor,
|
|
66
|
+
decryptContext: () => decryptContext,
|
|
67
|
+
decryptFile: () => decryptFile,
|
|
68
|
+
decryptMessages: () => decryptMessages,
|
|
69
|
+
decryptOnReceive: () => decryptOnReceive,
|
|
70
|
+
default: () => index_default,
|
|
71
|
+
encryptContext: () => encryptContext,
|
|
72
|
+
encryptFile: () => encryptFile,
|
|
73
|
+
encryptForSend: () => encryptForSend,
|
|
74
|
+
extractSignals: () => extractSignals,
|
|
75
|
+
guessMimeType: () => guessMimeType,
|
|
76
|
+
safeSlug: () => safeSlug
|
|
58
77
|
});
|
|
59
78
|
module.exports = __toCommonJS(index_exports);
|
|
60
79
|
|
|
@@ -252,10 +271,11 @@ var RealtimeWSClient = class extends TypedEmitter {
|
|
|
252
271
|
joinConversation(conversationId) {
|
|
253
272
|
this.sendRaw({ type: "conversation.join", payload: { conversationId } });
|
|
254
273
|
}
|
|
255
|
-
sendMessage(conversationId, content,
|
|
274
|
+
sendMessage(conversationId, content, options) {
|
|
275
|
+
const opts = typeof options === "string" ? { type: options } : options;
|
|
256
276
|
this.sendRaw({
|
|
257
277
|
type: "message.send",
|
|
258
|
-
payload: { conversationId, content, type },
|
|
278
|
+
payload: { conversationId, content, type: opts?.type ?? "text", ...opts?.metadata ? { metadata: opts.metadata } : {}, ...opts?.parentId ? { parentId: opts.parentId } : {} },
|
|
259
279
|
requestId: `msg-${++this.pingCounter}`
|
|
260
280
|
});
|
|
261
281
|
}
|
|
@@ -1262,6 +1282,12 @@ var ENVIRONMENTS = {
|
|
|
1262
1282
|
production: "https://prismer.cloud"
|
|
1263
1283
|
};
|
|
1264
1284
|
|
|
1285
|
+
// src/aip.ts
|
|
1286
|
+
var import_aip_sdk = require("@prismer/aip-sdk");
|
|
1287
|
+
var import_aip_sdk2 = require("@prismer/aip-sdk");
|
|
1288
|
+
var import_aip_sdk3 = require("@prismer/aip-sdk");
|
|
1289
|
+
var import_aip_sdk4 = require("@prismer/aip-sdk");
|
|
1290
|
+
|
|
1265
1291
|
// src/storage.ts
|
|
1266
1292
|
var MemoryStorage = class {
|
|
1267
1293
|
constructor() {
|
|
@@ -2146,20 +2172,27 @@ var PBKDF2_ITERATIONS = 1e5;
|
|
|
2146
2172
|
var SALT_LENGTH = 16;
|
|
2147
2173
|
var IV_LENGTH = 12;
|
|
2148
2174
|
var KEY_LENGTH = 256;
|
|
2149
|
-
var
|
|
2175
|
+
var _E2EEncryption = class _E2EEncryption {
|
|
2150
2176
|
constructor() {
|
|
2151
2177
|
this.masterKey = null;
|
|
2152
2178
|
this.keyPair = null;
|
|
2153
2179
|
this.sessionKeys = /* @__PURE__ */ new Map();
|
|
2154
2180
|
// conversationId → AES key
|
|
2155
2181
|
this.salt = null;
|
|
2182
|
+
// ─── Pipeline Functions ──────────────────────────────────
|
|
2183
|
+
this.messageCount = 0;
|
|
2184
|
+
this.lastRotation = Date.now();
|
|
2156
2185
|
}
|
|
2157
2186
|
/**
|
|
2158
2187
|
* Initialize encryption with user passphrase.
|
|
2159
2188
|
* Derives a master key via PBKDF2 and generates an ECDH key pair.
|
|
2189
|
+
*
|
|
2190
|
+
* @param passphrase - User passphrase for master key derivation
|
|
2191
|
+
* @param salt - Optional Base64-encoded salt. If omitted, a random 16-byte salt is generated.
|
|
2192
|
+
* Store the salt (via exportSalt()) so you can re-derive the same master key later.
|
|
2160
2193
|
*/
|
|
2161
|
-
async init(passphrase) {
|
|
2162
|
-
this.salt = getRandomValues(new Uint8Array(SALT_LENGTH));
|
|
2194
|
+
async init(passphrase, salt) {
|
|
2195
|
+
this.salt = salt ? new Uint8Array(base64ToArrayBuffer(salt)) : getRandomValues(new Uint8Array(SALT_LENGTH));
|
|
2163
2196
|
const passphraseKey = await subtle().importKey(
|
|
2164
2197
|
"raw",
|
|
2165
2198
|
new TextEncoder().encode(passphrase),
|
|
@@ -2170,7 +2203,7 @@ var E2EEncryption = class {
|
|
|
2170
2203
|
this.masterKey = await subtle().deriveKey(
|
|
2171
2204
|
{
|
|
2172
2205
|
name: "PBKDF2",
|
|
2173
|
-
salt: this.salt,
|
|
2206
|
+
salt: new Uint8Array(this.salt.buffer, this.salt.byteOffset, this.salt.byteLength).buffer,
|
|
2174
2207
|
iterations: PBKDF2_ITERATIONS,
|
|
2175
2208
|
hash: "SHA-256"
|
|
2176
2209
|
},
|
|
@@ -2185,6 +2218,14 @@ var E2EEncryption = class {
|
|
|
2185
2218
|
["deriveKey"]
|
|
2186
2219
|
);
|
|
2187
2220
|
}
|
|
2221
|
+
/**
|
|
2222
|
+
* Export the salt as Base64 string for persistent storage.
|
|
2223
|
+
* You must store this and pass it back to init() to re-derive the same master key.
|
|
2224
|
+
*/
|
|
2225
|
+
exportSalt() {
|
|
2226
|
+
if (!this.salt) throw new Error("E2E not initialized. Call init() first.");
|
|
2227
|
+
return arrayBufferToBase64(this.salt.buffer);
|
|
2228
|
+
}
|
|
2188
2229
|
/**
|
|
2189
2230
|
* Export public key for sharing with conversation peers.
|
|
2190
2231
|
*/
|
|
@@ -2297,8 +2338,93 @@ var E2EEncryption = class {
|
|
|
2297
2338
|
this.keyPair = null;
|
|
2298
2339
|
this.sessionKeys.clear();
|
|
2299
2340
|
this.salt = null;
|
|
2341
|
+
this.messageCount = 0;
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* High-level encrypt-for-send pipeline.
|
|
2345
|
+
* Encrypts content, builds metadata, and handles key rotation.
|
|
2346
|
+
*
|
|
2347
|
+
* Returns { encryptedContent, metadata } ready to send.
|
|
2348
|
+
*/
|
|
2349
|
+
async encryptForSend(conversationId, content) {
|
|
2350
|
+
if (!this.hasSessionKey(conversationId)) {
|
|
2351
|
+
throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
|
|
2352
|
+
}
|
|
2353
|
+
const needsRotation = this.shouldRotateKey();
|
|
2354
|
+
const encryptedContent = await this.encrypt(conversationId, content);
|
|
2355
|
+
this.messageCount++;
|
|
2356
|
+
return {
|
|
2357
|
+
encryptedContent,
|
|
2358
|
+
metadata: {
|
|
2359
|
+
encrypted: true,
|
|
2360
|
+
encryptionVersion: 1,
|
|
2361
|
+
...needsRotation && { keyRotationRequested: true }
|
|
2362
|
+
}
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
/**
|
|
2366
|
+
* High-level decrypt-on-receive pipeline.
|
|
2367
|
+
* Decrypts content and validates metadata.
|
|
2368
|
+
*/
|
|
2369
|
+
async decryptOnReceive(conversationId, encryptedContent, metadata) {
|
|
2370
|
+
if (!this.hasSessionKey(conversationId)) {
|
|
2371
|
+
throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
|
|
2372
|
+
}
|
|
2373
|
+
return this.decrypt(conversationId, encryptedContent);
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* High-level file encryption pipeline.
|
|
2377
|
+
*/
|
|
2378
|
+
async encryptFile(conversationId, fileData) {
|
|
2379
|
+
const base64Data = arrayBufferToBase64(fileData);
|
|
2380
|
+
const encryptedData = await this.encrypt(conversationId, base64Data);
|
|
2381
|
+
return {
|
|
2382
|
+
encryptedData,
|
|
2383
|
+
metadata: {
|
|
2384
|
+
encrypted: true,
|
|
2385
|
+
encryptionVersion: 1,
|
|
2386
|
+
fileEncrypted: true
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* High-level file decryption pipeline.
|
|
2392
|
+
*/
|
|
2393
|
+
async decryptFile(conversationId, encryptedData) {
|
|
2394
|
+
const base64Data = await this.decrypt(conversationId, encryptedData);
|
|
2395
|
+
return base64ToArrayBuffer(base64Data);
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Check if key rotation is needed (1000 messages or 24 hours).
|
|
2399
|
+
*/
|
|
2400
|
+
shouldRotateKey() {
|
|
2401
|
+
if (this.messageCount >= _E2EEncryption.KEY_ROTATION_THRESHOLD) {
|
|
2402
|
+
return true;
|
|
2403
|
+
}
|
|
2404
|
+
if (Date.now() - this.lastRotation >= _E2EEncryption.KEY_ROTATION_INTERVAL_MS) {
|
|
2405
|
+
return true;
|
|
2406
|
+
}
|
|
2407
|
+
return false;
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Perform key rotation: generate new ECDH keypair and reset counters.
|
|
2411
|
+
* The caller is responsible for re-exchanging keys with peers.
|
|
2412
|
+
*/
|
|
2413
|
+
async rotateKeys() {
|
|
2414
|
+
this.keyPair = await subtle().generateKey(
|
|
2415
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
2416
|
+
false,
|
|
2417
|
+
["deriveKey"]
|
|
2418
|
+
);
|
|
2419
|
+
this.messageCount = 0;
|
|
2420
|
+
this.lastRotation = Date.now();
|
|
2421
|
+
this.sessionKeys.clear();
|
|
2422
|
+
return this.exportPublicKey();
|
|
2300
2423
|
}
|
|
2301
2424
|
};
|
|
2425
|
+
_E2EEncryption.KEY_ROTATION_THRESHOLD = 1e3;
|
|
2426
|
+
_E2EEncryption.KEY_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2427
|
+
var E2EEncryption = _E2EEncryption;
|
|
2302
2428
|
function arrayBufferToBase64(buffer) {
|
|
2303
2429
|
if (typeof btoa !== "undefined") {
|
|
2304
2430
|
const bytes = new Uint8Array(buffer);
|
|
@@ -2323,6 +2449,548 @@ function base64ToArrayBuffer(base64) {
|
|
|
2323
2449
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
2324
2450
|
}
|
|
2325
2451
|
|
|
2452
|
+
// src/encryption-pipeline.ts
|
|
2453
|
+
async function encryptForSend(e2e, conversationId, content, metadata) {
|
|
2454
|
+
if (!e2e.hasSessionKey(conversationId)) {
|
|
2455
|
+
return { content, metadata: metadata ?? {} };
|
|
2456
|
+
}
|
|
2457
|
+
const ciphertext = await e2e.encrypt(conversationId, content);
|
|
2458
|
+
return {
|
|
2459
|
+
content: ciphertext,
|
|
2460
|
+
metadata: { ...metadata, encrypted: true, encKeyId: `conv-${conversationId}` }
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
async function decryptOnReceive(e2e, conversationId, content, metadata) {
|
|
2464
|
+
if (!metadata?.encrypted) {
|
|
2465
|
+
return { content, decrypted: false };
|
|
2466
|
+
}
|
|
2467
|
+
if (!e2e.hasSessionKey(conversationId)) {
|
|
2468
|
+
return { content, decrypted: false, error: "no_session_key" };
|
|
2469
|
+
}
|
|
2470
|
+
try {
|
|
2471
|
+
const plain = await e2e.decrypt(conversationId, content);
|
|
2472
|
+
return { content: plain, decrypted: true };
|
|
2473
|
+
} catch (err) {
|
|
2474
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2475
|
+
return { content, decrypted: false, error: message };
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
async function encryptFile(e2e, conversationId, data) {
|
|
2479
|
+
if (!e2e.hasSessionKey(conversationId)) return null;
|
|
2480
|
+
const b64 = typeof Buffer !== "undefined" ? Buffer.from(data).toString("base64") : uint8ArrayToBase64(data);
|
|
2481
|
+
const ciphertext = await e2e.encrypt(conversationId, b64);
|
|
2482
|
+
return {
|
|
2483
|
+
data: ciphertext,
|
|
2484
|
+
metadata: { encrypted: true, encKeyId: `conv-${conversationId}` }
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
async function decryptFile(e2e, conversationId, ciphertext) {
|
|
2488
|
+
if (!e2e.hasSessionKey(conversationId)) return null;
|
|
2489
|
+
try {
|
|
2490
|
+
const b64 = await e2e.decrypt(conversationId, ciphertext);
|
|
2491
|
+
if (typeof Buffer !== "undefined") {
|
|
2492
|
+
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
2493
|
+
}
|
|
2494
|
+
return base64ToUint8Array(b64);
|
|
2495
|
+
} catch {
|
|
2496
|
+
return null;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
async function encryptContext(e2e, content, contextId = "context-cache") {
|
|
2500
|
+
if (!e2e.hasSessionKey(contextId)) return null;
|
|
2501
|
+
const ciphertext = await e2e.encrypt(contextId, content);
|
|
2502
|
+
return { content: ciphertext, encrypted: true };
|
|
2503
|
+
}
|
|
2504
|
+
async function decryptContext(e2e, ciphertext, contextId = "context-cache") {
|
|
2505
|
+
if (!e2e.hasSessionKey(contextId)) return null;
|
|
2506
|
+
try {
|
|
2507
|
+
return await e2e.decrypt(contextId, ciphertext);
|
|
2508
|
+
} catch {
|
|
2509
|
+
return null;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
async function decryptMessages(e2e, messages, conversationId) {
|
|
2513
|
+
let decryptedCount = 0;
|
|
2514
|
+
const errors = [];
|
|
2515
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2516
|
+
const msg = messages[i];
|
|
2517
|
+
const convId = conversationId ?? msg.conversationId;
|
|
2518
|
+
if (!convId) continue;
|
|
2519
|
+
const result = await decryptOnReceive(e2e, convId, msg.content, msg.metadata);
|
|
2520
|
+
if (result.decrypted) {
|
|
2521
|
+
msg.content = result.content;
|
|
2522
|
+
if (msg.metadata) {
|
|
2523
|
+
msg.metadata._decrypted = true;
|
|
2524
|
+
}
|
|
2525
|
+
decryptedCount++;
|
|
2526
|
+
} else if (result.error) {
|
|
2527
|
+
errors.push({ index: i, error: result.error });
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
return { decryptedCount, errors };
|
|
2531
|
+
}
|
|
2532
|
+
function uint8ArrayToBase64(bytes) {
|
|
2533
|
+
let binary = "";
|
|
2534
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2535
|
+
binary += String.fromCharCode(bytes[i]);
|
|
2536
|
+
}
|
|
2537
|
+
return btoa(binary);
|
|
2538
|
+
}
|
|
2539
|
+
function base64ToUint8Array(b64) {
|
|
2540
|
+
const binary = atob(b64);
|
|
2541
|
+
const bytes = new Uint8Array(binary.length);
|
|
2542
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2543
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2544
|
+
}
|
|
2545
|
+
return bytes;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
// src/evolution-cache.ts
|
|
2549
|
+
var EvolutionCache = class {
|
|
2550
|
+
constructor() {
|
|
2551
|
+
this._genes = /* @__PURE__ */ new Map();
|
|
2552
|
+
this._edges = /* @__PURE__ */ new Map();
|
|
2553
|
+
// key = signal_key
|
|
2554
|
+
this._globalPrior = /* @__PURE__ */ new Map();
|
|
2555
|
+
this._cursor = 0;
|
|
2556
|
+
}
|
|
2557
|
+
get cursor() {
|
|
2558
|
+
return this._cursor;
|
|
2559
|
+
}
|
|
2560
|
+
get geneCount() {
|
|
2561
|
+
return this._genes.size;
|
|
2562
|
+
}
|
|
2563
|
+
/** Load from a full snapshot */
|
|
2564
|
+
loadSnapshot(snapshot) {
|
|
2565
|
+
this._genes.clear();
|
|
2566
|
+
this._edges.clear();
|
|
2567
|
+
this._globalPrior.clear();
|
|
2568
|
+
for (const gene of snapshot.genes) {
|
|
2569
|
+
this._genes.set(gene.id, gene);
|
|
2570
|
+
}
|
|
2571
|
+
for (const edge of snapshot.edges) {
|
|
2572
|
+
const list = this._edges.get(edge.signal_key) ?? [];
|
|
2573
|
+
list.push(edge);
|
|
2574
|
+
this._edges.set(edge.signal_key, list);
|
|
2575
|
+
}
|
|
2576
|
+
for (const [key, val] of Object.entries(snapshot.globalPrior)) {
|
|
2577
|
+
this._globalPrior.set(key, val);
|
|
2578
|
+
}
|
|
2579
|
+
this._cursor = snapshot.cursor;
|
|
2580
|
+
}
|
|
2581
|
+
/** Apply incremental delta (alias: loadDelta) */
|
|
2582
|
+
applyDelta(delta) {
|
|
2583
|
+
const pulled = delta.pulled;
|
|
2584
|
+
for (const gene of pulled.genes) {
|
|
2585
|
+
this._genes.set(gene.id, gene);
|
|
2586
|
+
}
|
|
2587
|
+
for (const id of pulled.quarantines) {
|
|
2588
|
+
this._genes.delete(id);
|
|
2589
|
+
}
|
|
2590
|
+
for (const edge of pulled.edges) {
|
|
2591
|
+
const list = this._edges.get(edge.signal_key) ?? [];
|
|
2592
|
+
const idx = list.findIndex((e) => e.gene_id === edge.gene_id);
|
|
2593
|
+
if (idx >= 0) list[idx] = edge;
|
|
2594
|
+
else list.push(edge);
|
|
2595
|
+
this._edges.set(edge.signal_key, list);
|
|
2596
|
+
}
|
|
2597
|
+
for (const [key, val] of Object.entries(pulled.globalPrior)) {
|
|
2598
|
+
this._globalPrior.set(key, val);
|
|
2599
|
+
}
|
|
2600
|
+
this._cursor = pulled.cursor;
|
|
2601
|
+
}
|
|
2602
|
+
/** Apply incremental delta (alias for applyDelta) */
|
|
2603
|
+
loadDelta(delta) {
|
|
2604
|
+
this.applyDelta(delta);
|
|
2605
|
+
}
|
|
2606
|
+
/** Select best gene locally using Thompson Sampling — pure CPU, <1ms */
|
|
2607
|
+
selectGene(signals) {
|
|
2608
|
+
if (this._genes.size === 0) {
|
|
2609
|
+
return { action: "none", confidence: 0, reason: "no genes in cache", fromCache: true };
|
|
2610
|
+
}
|
|
2611
|
+
const signalKeys = signals.map((s) => s.type);
|
|
2612
|
+
const candidates = [];
|
|
2613
|
+
for (const gene of this._genes.values()) {
|
|
2614
|
+
if (gene.visibility === "quarantined") continue;
|
|
2615
|
+
const geneSignalTypes = (gene.signals_match || []).map(
|
|
2616
|
+
(s) => typeof s === "string" ? s : s.type
|
|
2617
|
+
);
|
|
2618
|
+
if (geneSignalTypes.length === 0) continue;
|
|
2619
|
+
const matchCount = signalKeys.filter((k) => geneSignalTypes.includes(k)).length;
|
|
2620
|
+
const coverageScore = matchCount / geneSignalTypes.length;
|
|
2621
|
+
if (coverageScore === 0) continue;
|
|
2622
|
+
let alpha = gene.success_count + 1;
|
|
2623
|
+
let beta = gene.failure_count + 1;
|
|
2624
|
+
for (const key of signalKeys) {
|
|
2625
|
+
const prior = this._globalPrior.get(key);
|
|
2626
|
+
if (prior) {
|
|
2627
|
+
alpha += 0.3 * prior.alpha;
|
|
2628
|
+
beta += 0.3 * prior.beta;
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
const sampledScore = alpha / (alpha + beta);
|
|
2632
|
+
const totalObs = gene.success_count + gene.failure_count;
|
|
2633
|
+
if (totalObs >= 10 && gene.success_count / totalObs < 0.18) continue;
|
|
2634
|
+
const rankScore = coverageScore * 0.4 + sampledScore * 0.6;
|
|
2635
|
+
candidates.push({ gene, rankScore, coverageScore, sampledScore });
|
|
2636
|
+
}
|
|
2637
|
+
if (candidates.length === 0) {
|
|
2638
|
+
return {
|
|
2639
|
+
action: "create_suggested",
|
|
2640
|
+
confidence: 0,
|
|
2641
|
+
reason: "no matching genes for signals",
|
|
2642
|
+
fromCache: true
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
candidates.sort((a, b) => b.rankScore - a.rankScore);
|
|
2646
|
+
const best = candidates[0];
|
|
2647
|
+
const alternatives = candidates.slice(1, 4).map((c) => ({
|
|
2648
|
+
gene_id: c.gene.id,
|
|
2649
|
+
confidence: Math.round(c.rankScore * 100) / 100,
|
|
2650
|
+
title: c.gene.title
|
|
2651
|
+
}));
|
|
2652
|
+
return {
|
|
2653
|
+
action: "apply_gene",
|
|
2654
|
+
gene_id: best.gene.id,
|
|
2655
|
+
gene: best.gene,
|
|
2656
|
+
strategy: best.gene.strategy,
|
|
2657
|
+
confidence: Math.round(best.rankScore * 100) / 100,
|
|
2658
|
+
coverageScore: Math.round(best.coverageScore * 100) / 100,
|
|
2659
|
+
alternatives,
|
|
2660
|
+
reason: `local cache selection (${this._genes.size} genes)`,
|
|
2661
|
+
fromCache: true
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2666
|
+
// src/signal-enrichment.ts
|
|
2667
|
+
var ERROR_PATTERNS = [
|
|
2668
|
+
{ pattern: /timeout|timed?\s*out|deadline\s*exceeded|context\s*deadline/i, type: "timeout" },
|
|
2669
|
+
{ pattern: /econnrefused|connection\s*refused/i, type: "connection_refused" },
|
|
2670
|
+
{ pattern: /enotfound|dns|getaddrinfo|resolve/i, type: "dns_error" },
|
|
2671
|
+
{ pattern: /rate\s*limit|too\s*many\s*requests|429/i, type: "rate_limit" },
|
|
2672
|
+
{ pattern: /401|unauthorized|unauthenticated|auth.*fail/i, type: "auth_error" },
|
|
2673
|
+
{ pattern: /403|forbidden|access\s*denied|permission/i, type: "permission_error" },
|
|
2674
|
+
{ pattern: /404|not\s*found/i, type: "not_found" },
|
|
2675
|
+
{ pattern: /5\d{2}|internal\s*server|server\s*error|502|503|504/i, type: "server_error" },
|
|
2676
|
+
{ pattern: /type\s*error|typeerror/i, type: "type_error" },
|
|
2677
|
+
{ pattern: /syntax\s*error|syntaxerror|unexpected\s*token/i, type: "syntax_error" },
|
|
2678
|
+
{ pattern: /reference\s*error|referenceerror|is\s*not\s*defined/i, type: "reference_error" },
|
|
2679
|
+
{ pattern: /out\s*of\s*memory|oom|heap|allocation\s*failed/i, type: "oom" },
|
|
2680
|
+
{ pattern: /crash|panic|segfault|sigsegv|sigabrt/i, type: "crash" },
|
|
2681
|
+
{ pattern: /quota|limit\s*exceeded|insufficient/i, type: "quota_exceeded" },
|
|
2682
|
+
{ pattern: /tls|ssl|certificate|cert\s*verify/i, type: "tls_error" },
|
|
2683
|
+
{ pattern: /deadlock|lock\s*timeout|lock\s*wait/i, type: "deadlock" }
|
|
2684
|
+
];
|
|
2685
|
+
function extractSignals(ctx) {
|
|
2686
|
+
const tags = [];
|
|
2687
|
+
if (ctx.error) {
|
|
2688
|
+
let matched = false;
|
|
2689
|
+
for (const { pattern, type } of ERROR_PATTERNS) {
|
|
2690
|
+
if (pattern.test(ctx.error)) {
|
|
2691
|
+
const tag = { type: `error:${type}` };
|
|
2692
|
+
if (ctx.provider) tag.provider = ctx.provider;
|
|
2693
|
+
if (ctx.stage) tag.stage = ctx.stage;
|
|
2694
|
+
if (ctx.severity) tag.severity = ctx.severity;
|
|
2695
|
+
tags.push(tag);
|
|
2696
|
+
matched = true;
|
|
2697
|
+
break;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
if (!matched) {
|
|
2701
|
+
const normalized = ctx.error.slice(0, 50).toLowerCase().replace(/[^a-z0-9_]/g, "_");
|
|
2702
|
+
const tag = { type: `error:${normalized}` };
|
|
2703
|
+
if (ctx.provider) tag.provider = ctx.provider;
|
|
2704
|
+
if (ctx.stage) tag.stage = ctx.stage;
|
|
2705
|
+
tags.push(tag);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
if (ctx.taskStatus === "failed") tags.push({ type: "task.failed" });
|
|
2709
|
+
if (ctx.taskStatus === "completed") tags.push({ type: "task.completed" });
|
|
2710
|
+
if (ctx.taskCapability) {
|
|
2711
|
+
tags.push({ type: `capability:${ctx.taskCapability}` });
|
|
2712
|
+
}
|
|
2713
|
+
if (ctx.tags) {
|
|
2714
|
+
for (const tag of ctx.tags) {
|
|
2715
|
+
tags.push({ type: tag });
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
return tags;
|
|
2719
|
+
}
|
|
2720
|
+
function createEnrichedExtractor(config) {
|
|
2721
|
+
if (config.mode === "rules") {
|
|
2722
|
+
return async (ctx) => extractSignals(ctx);
|
|
2723
|
+
}
|
|
2724
|
+
const { llmExtract, timeoutMs = 3e3 } = config;
|
|
2725
|
+
if (!llmExtract) return async (ctx) => extractSignals(ctx);
|
|
2726
|
+
return async (ctx) => {
|
|
2727
|
+
try {
|
|
2728
|
+
const result = await Promise.race([
|
|
2729
|
+
llmExtract(ctx),
|
|
2730
|
+
new Promise(
|
|
2731
|
+
(_, reject) => setTimeout(() => reject(new Error("llm_timeout")), timeoutMs)
|
|
2732
|
+
)
|
|
2733
|
+
]);
|
|
2734
|
+
return result;
|
|
2735
|
+
} catch {
|
|
2736
|
+
return extractSignals(ctx);
|
|
2737
|
+
}
|
|
2738
|
+
};
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2741
|
+
// src/evolution-runtime.ts
|
|
2742
|
+
var EvolutionRuntime = class {
|
|
2743
|
+
constructor(client, config) {
|
|
2744
|
+
this.client = client;
|
|
2745
|
+
this.outbox = [];
|
|
2746
|
+
this.started = false;
|
|
2747
|
+
// Session tracking
|
|
2748
|
+
this._sessions = [];
|
|
2749
|
+
this._sessionCounter = 0;
|
|
2750
|
+
this.config = {
|
|
2751
|
+
syncIntervalMs: config?.syncIntervalMs ?? 6e4,
|
|
2752
|
+
enrichment: config?.enrichment ?? { mode: "rules" },
|
|
2753
|
+
scope: config?.scope ?? "global",
|
|
2754
|
+
outboxMaxSize: config?.outboxMaxSize ?? 50,
|
|
2755
|
+
outboxFlushMs: config?.outboxFlushMs ?? 5e3
|
|
2756
|
+
};
|
|
2757
|
+
this.scope = this.config.scope;
|
|
2758
|
+
this.cache = new EvolutionCache();
|
|
2759
|
+
this.enricher = config?.enrichment ? createEnrichedExtractor(config.enrichment) : async (ctx) => extractSignals(ctx);
|
|
2760
|
+
}
|
|
2761
|
+
// ─── Lifecycle ──────────────────────────────────────
|
|
2762
|
+
/** Initialize: load snapshot + start sync + start outbox flush */
|
|
2763
|
+
async start() {
|
|
2764
|
+
if (this.started) return;
|
|
2765
|
+
this.started = true;
|
|
2766
|
+
try {
|
|
2767
|
+
const snapshot = await this.client.getSyncSnapshot(0);
|
|
2768
|
+
if (snapshot.data) {
|
|
2769
|
+
this.cache.loadSnapshot(snapshot.data);
|
|
2770
|
+
}
|
|
2771
|
+
} catch {
|
|
2772
|
+
}
|
|
2773
|
+
if (this.config.syncIntervalMs > 0) {
|
|
2774
|
+
this.syncTimer = setInterval(() => this.sync(), this.config.syncIntervalMs);
|
|
2775
|
+
}
|
|
2776
|
+
this.flushTimer = setInterval(() => this.flush(), this.config.outboxFlushMs);
|
|
2777
|
+
}
|
|
2778
|
+
/** Stop: clear timers + flush remaining outbox */
|
|
2779
|
+
async stop() {
|
|
2780
|
+
if (this.syncTimer) clearInterval(this.syncTimer);
|
|
2781
|
+
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
2782
|
+
await this.flush();
|
|
2783
|
+
this.started = false;
|
|
2784
|
+
}
|
|
2785
|
+
// ─── High-Level API ─────────────────────────────────
|
|
2786
|
+
/**
|
|
2787
|
+
* Get a strategy recommendation for an error/context.
|
|
2788
|
+
*
|
|
2789
|
+
* Flow: extract signals → try local cache (<1ms) → fallback to server (~30ms)
|
|
2790
|
+
*
|
|
2791
|
+
* @param error - Error message or Error object
|
|
2792
|
+
* @param context - Optional additional context (provider, stage, etc.)
|
|
2793
|
+
*/
|
|
2794
|
+
async suggest(error, context) {
|
|
2795
|
+
const errorStr = error instanceof Error ? error.message : error;
|
|
2796
|
+
const ctx = {
|
|
2797
|
+
error: errorStr,
|
|
2798
|
+
...context
|
|
2799
|
+
};
|
|
2800
|
+
const signals = await this.enricher(ctx);
|
|
2801
|
+
if (signals.length === 0) {
|
|
2802
|
+
return {
|
|
2803
|
+
action: "none",
|
|
2804
|
+
confidence: 0,
|
|
2805
|
+
signals: [],
|
|
2806
|
+
fromCache: false,
|
|
2807
|
+
reason: "no signals extracted from error"
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
const buildSuggestion = (action, geneId, gene, strategy, confidence, fromCache, reason, alternatives) => {
|
|
2811
|
+
this.lastSuggestedGeneId = geneId;
|
|
2812
|
+
this._activeSession = {
|
|
2813
|
+
id: `ses_${++this._sessionCounter}_${Date.now()}`,
|
|
2814
|
+
suggestedAt: Date.now(),
|
|
2815
|
+
suggestedGeneId: geneId,
|
|
2816
|
+
signals,
|
|
2817
|
+
adopted: false,
|
|
2818
|
+
confidence,
|
|
2819
|
+
fromCache
|
|
2820
|
+
};
|
|
2821
|
+
return {
|
|
2822
|
+
action,
|
|
2823
|
+
geneId,
|
|
2824
|
+
gene,
|
|
2825
|
+
strategy,
|
|
2826
|
+
confidence,
|
|
2827
|
+
signals,
|
|
2828
|
+
fromCache,
|
|
2829
|
+
reason,
|
|
2830
|
+
alternatives
|
|
2831
|
+
};
|
|
2832
|
+
};
|
|
2833
|
+
if (this.cache.geneCount > 0) {
|
|
2834
|
+
const local = this.cache.selectGene(signals);
|
|
2835
|
+
if (local.action === "apply_gene" && local.confidence > 0.3) {
|
|
2836
|
+
return buildSuggestion(
|
|
2837
|
+
local.action,
|
|
2838
|
+
local.gene_id,
|
|
2839
|
+
local.gene,
|
|
2840
|
+
local.strategy,
|
|
2841
|
+
local.confidence,
|
|
2842
|
+
true,
|
|
2843
|
+
local.reason,
|
|
2844
|
+
local.alternatives
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
try {
|
|
2849
|
+
const result = await this.client.analyze({
|
|
2850
|
+
signals,
|
|
2851
|
+
scope: this.scope
|
|
2852
|
+
});
|
|
2853
|
+
if (result.data) {
|
|
2854
|
+
return buildSuggestion(
|
|
2855
|
+
result.data.action,
|
|
2856
|
+
result.data.gene_id,
|
|
2857
|
+
result.data.gene,
|
|
2858
|
+
result.data.strategy,
|
|
2859
|
+
result.data.confidence ?? 0,
|
|
2860
|
+
false,
|
|
2861
|
+
result.data.reason,
|
|
2862
|
+
result.data.alternatives
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
2865
|
+
} catch {
|
|
2866
|
+
const local = this.cache.selectGene(signals);
|
|
2867
|
+
return buildSuggestion(
|
|
2868
|
+
local.action,
|
|
2869
|
+
local.gene_id,
|
|
2870
|
+
local.gene,
|
|
2871
|
+
local.strategy,
|
|
2872
|
+
local.confidence,
|
|
2873
|
+
true,
|
|
2874
|
+
"server unreachable, using cache fallback",
|
|
2875
|
+
local.alternatives
|
|
2876
|
+
);
|
|
2877
|
+
}
|
|
2878
|
+
return {
|
|
2879
|
+
action: "none",
|
|
2880
|
+
confidence: 0,
|
|
2881
|
+
signals,
|
|
2882
|
+
fromCache: false,
|
|
2883
|
+
reason: "no recommendation from server"
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
/**
|
|
2887
|
+
* Record an outcome. Fire-and-forget — never blocks, never throws.
|
|
2888
|
+
*
|
|
2889
|
+
* @param error - The error that was encountered
|
|
2890
|
+
* @param outcome - 'success' or 'failed'
|
|
2891
|
+
* @param summary - One-line summary of what happened
|
|
2892
|
+
* @param geneId - Gene that was used (auto-detected from last suggest() if omitted)
|
|
2893
|
+
*/
|
|
2894
|
+
learned(error, outcome, summary, geneId, metadata) {
|
|
2895
|
+
const errorStr = error instanceof Error ? error.message : error;
|
|
2896
|
+
const ctx = { error: errorStr };
|
|
2897
|
+
const signals = extractSignals(ctx);
|
|
2898
|
+
const resolvedGeneId = geneId || this.lastSuggestedGeneId;
|
|
2899
|
+
if (!resolvedGeneId) return;
|
|
2900
|
+
if (this._activeSession) {
|
|
2901
|
+
const session = this._activeSession;
|
|
2902
|
+
session.usedGeneId = resolvedGeneId;
|
|
2903
|
+
session.adopted = resolvedGeneId === session.suggestedGeneId;
|
|
2904
|
+
session.completedAt = Date.now();
|
|
2905
|
+
session.outcome = outcome;
|
|
2906
|
+
session.durationMs = session.completedAt - session.suggestedAt;
|
|
2907
|
+
this._sessions.push(session);
|
|
2908
|
+
this._activeSession = void 0;
|
|
2909
|
+
}
|
|
2910
|
+
this.outbox.push({
|
|
2911
|
+
geneId: resolvedGeneId,
|
|
2912
|
+
signals,
|
|
2913
|
+
outcome,
|
|
2914
|
+
summary,
|
|
2915
|
+
metadata,
|
|
2916
|
+
timestamp: Date.now(),
|
|
2917
|
+
sessionId: this._sessions[this._sessions.length - 1]?.id
|
|
2918
|
+
});
|
|
2919
|
+
if (this.outbox.length >= this.config.outboxMaxSize) {
|
|
2920
|
+
this.flush().catch(() => {
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
// ─── Session Metrics ────────────────────────────────
|
|
2925
|
+
/** Get all completed sessions. */
|
|
2926
|
+
get sessions() {
|
|
2927
|
+
return this._sessions;
|
|
2928
|
+
}
|
|
2929
|
+
/** Get aggregate metrics for benchmarking. */
|
|
2930
|
+
getMetrics() {
|
|
2931
|
+
const sessions = this._sessions;
|
|
2932
|
+
const totalSuggestions = sessions.length;
|
|
2933
|
+
const suggestionsWithGene = sessions.filter((s) => s.suggestedGeneId).length;
|
|
2934
|
+
const totalLearned = sessions.filter((s) => s.completedAt).length;
|
|
2935
|
+
const adoptedSessions = sessions.filter((s) => s.adopted && s.completedAt);
|
|
2936
|
+
const adoptedCount = adoptedSessions.length;
|
|
2937
|
+
const nonAdopted = sessions.filter((s) => !s.adopted && s.completedAt);
|
|
2938
|
+
const durations = sessions.filter((s) => s.durationMs != null).map((s) => s.durationMs);
|
|
2939
|
+
const avgDurationMs = durations.length > 0 ? Math.round(durations.reduce((a, b) => a + b, 0) / durations.length) : 0;
|
|
2940
|
+
const adoptedSuccess = adoptedSessions.filter((s) => s.outcome === "success").length;
|
|
2941
|
+
const nonAdoptedSuccess = nonAdopted.filter((s) => s.outcome === "success").length;
|
|
2942
|
+
const cacheHits = sessions.filter((s) => s.fromCache).length;
|
|
2943
|
+
return {
|
|
2944
|
+
totalSuggestions,
|
|
2945
|
+
suggestionsWithGene,
|
|
2946
|
+
totalLearned,
|
|
2947
|
+
adoptedCount,
|
|
2948
|
+
geneUtilizationRate: suggestionsWithGene > 0 ? Math.round(adoptedCount / suggestionsWithGene * 100) / 100 : 0,
|
|
2949
|
+
avgDurationMs,
|
|
2950
|
+
adoptedSuccessRate: adoptedCount > 0 ? Math.round(adoptedSuccess / adoptedCount * 100) / 100 : 0,
|
|
2951
|
+
nonAdoptedSuccessRate: nonAdopted.length > 0 ? Math.round(nonAdoptedSuccess / nonAdopted.length * 100) / 100 : 0,
|
|
2952
|
+
cacheHitRate: totalSuggestions > 0 ? Math.round(cacheHits / totalSuggestions * 100) / 100 : 0
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2955
|
+
/** Reset session history. */
|
|
2956
|
+
resetMetrics() {
|
|
2957
|
+
this._sessions = [];
|
|
2958
|
+
}
|
|
2959
|
+
// ─── Internal ───────────────────────────────────────
|
|
2960
|
+
/** Sync cache with server */
|
|
2961
|
+
async sync() {
|
|
2962
|
+
try {
|
|
2963
|
+
const result = await this.client.sync({
|
|
2964
|
+
pull: { since: this.cache.cursor },
|
|
2965
|
+
scope: this.scope
|
|
2966
|
+
});
|
|
2967
|
+
if (result.data?.pulled) {
|
|
2968
|
+
this.cache.applyDelta({ pulled: result.data.pulled });
|
|
2969
|
+
}
|
|
2970
|
+
} catch {
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
/** Flush outbox to server */
|
|
2974
|
+
async flush() {
|
|
2975
|
+
if (this.outbox.length === 0) return;
|
|
2976
|
+
const batch = this.outbox.splice(0, this.config.outboxMaxSize);
|
|
2977
|
+
const promises = batch.map(
|
|
2978
|
+
(entry) => this.client.record({
|
|
2979
|
+
gene_id: entry.geneId,
|
|
2980
|
+
signals: entry.signals.map((s) => s.type),
|
|
2981
|
+
outcome: entry.outcome,
|
|
2982
|
+
summary: entry.summary,
|
|
2983
|
+
score: entry.score,
|
|
2984
|
+
metadata: entry.metadata,
|
|
2985
|
+
scope: this.scope
|
|
2986
|
+
}).catch(() => {
|
|
2987
|
+
this.outbox.push(entry);
|
|
2988
|
+
})
|
|
2989
|
+
);
|
|
2990
|
+
await Promise.allSettled(promises);
|
|
2991
|
+
}
|
|
2992
|
+
};
|
|
2993
|
+
|
|
2326
2994
|
// src/index.ts
|
|
2327
2995
|
var AccountClient = class {
|
|
2328
2996
|
constructor(_r) {
|
|
@@ -2448,8 +3116,8 @@ var MessagesClient = class {
|
|
|
2448
3116
|
return this._r("GET", `/api/im/messages/${conversationId}`, void 0, query);
|
|
2449
3117
|
}
|
|
2450
3118
|
/** Edit a message */
|
|
2451
|
-
async edit(conversationId, messageId, content) {
|
|
2452
|
-
return this._r("PATCH", `/api/im/messages/${conversationId}/${messageId}`, { content });
|
|
3119
|
+
async edit(conversationId, messageId, content, options) {
|
|
3120
|
+
return this._r("PATCH", `/api/im/messages/${conversationId}/${messageId}`, { content, ...options?.metadata ? { metadata: options.metadata } : {} });
|
|
2453
3121
|
}
|
|
2454
3122
|
/** Delete a message */
|
|
2455
3123
|
async delete(conversationId, messageId) {
|
|
@@ -2536,6 +3204,560 @@ var WorkspaceClient = class {
|
|
|
2536
3204
|
return this._r("GET", "/api/im/workspace/mentions/autocomplete", void 0, q);
|
|
2537
3205
|
}
|
|
2538
3206
|
};
|
|
3207
|
+
var TasksClient = class {
|
|
3208
|
+
constructor(_r) {
|
|
3209
|
+
this._r = _r;
|
|
3210
|
+
}
|
|
3211
|
+
/** Create a new task */
|
|
3212
|
+
async create(options) {
|
|
3213
|
+
return this._r("POST", "/api/im/tasks", options);
|
|
3214
|
+
}
|
|
3215
|
+
/** List tasks with optional filters */
|
|
3216
|
+
async list(options) {
|
|
3217
|
+
const query = {};
|
|
3218
|
+
if (options?.status) query.status = options.status;
|
|
3219
|
+
if (options?.capability) query.capability = options.capability;
|
|
3220
|
+
if (options?.assigneeId) query.assigneeId = options.assigneeId;
|
|
3221
|
+
if (options?.creatorId) query.creatorId = options.creatorId;
|
|
3222
|
+
if (options?.scheduleType) query.scheduleType = options.scheduleType;
|
|
3223
|
+
if (options?.limit != null) query.limit = String(options.limit);
|
|
3224
|
+
if (options?.cursor) query.cursor = options.cursor;
|
|
3225
|
+
return this._r("GET", "/api/im/tasks", void 0, query);
|
|
3226
|
+
}
|
|
3227
|
+
/** Get task details with logs */
|
|
3228
|
+
async get(taskId) {
|
|
3229
|
+
return this._r("GET", `/api/im/tasks/${taskId}`);
|
|
3230
|
+
}
|
|
3231
|
+
/** Update a task */
|
|
3232
|
+
async update(taskId, options) {
|
|
3233
|
+
return this._r("PATCH", `/api/im/tasks/${taskId}`, options);
|
|
3234
|
+
}
|
|
3235
|
+
/** Claim a pending task */
|
|
3236
|
+
async claim(taskId) {
|
|
3237
|
+
return this._r("POST", `/api/im/tasks/${taskId}/claim`);
|
|
3238
|
+
}
|
|
3239
|
+
/** Report progress on a task */
|
|
3240
|
+
async progress(taskId, options) {
|
|
3241
|
+
return this._r("POST", `/api/im/tasks/${taskId}/progress`, options);
|
|
3242
|
+
}
|
|
3243
|
+
/** Complete a task with result */
|
|
3244
|
+
async complete(taskId, options) {
|
|
3245
|
+
return this._r("POST", `/api/im/tasks/${taskId}/complete`, options);
|
|
3246
|
+
}
|
|
3247
|
+
/** Fail a task with error */
|
|
3248
|
+
async fail(taskId, error, metadata) {
|
|
3249
|
+
return this._r("POST", `/api/im/tasks/${taskId}/fail`, { error, metadata });
|
|
3250
|
+
}
|
|
3251
|
+
};
|
|
3252
|
+
var MemoryClient = class {
|
|
3253
|
+
constructor(_r) {
|
|
3254
|
+
this._r = _r;
|
|
3255
|
+
}
|
|
3256
|
+
/** Create a memory file */
|
|
3257
|
+
async createFile(options) {
|
|
3258
|
+
return this._r("POST", "/api/im/memory/files", options);
|
|
3259
|
+
}
|
|
3260
|
+
/** List memory files */
|
|
3261
|
+
async listFiles(options) {
|
|
3262
|
+
const query = {};
|
|
3263
|
+
if (options?.scope) query.scope = options.scope;
|
|
3264
|
+
if (options?.path) query.path = options.path;
|
|
3265
|
+
return this._r("GET", "/api/im/memory/files", void 0, query);
|
|
3266
|
+
}
|
|
3267
|
+
/** Get a memory file by ID */
|
|
3268
|
+
async getFile(fileId) {
|
|
3269
|
+
return this._r("GET", `/api/im/memory/files/${fileId}`);
|
|
3270
|
+
}
|
|
3271
|
+
/** Update a memory file (append, replace, or replace_section) */
|
|
3272
|
+
async updateFile(fileId, options) {
|
|
3273
|
+
return this._r("PATCH", `/api/im/memory/files/${fileId}`, options);
|
|
3274
|
+
}
|
|
3275
|
+
/** Delete a memory file */
|
|
3276
|
+
async deleteFile(fileId) {
|
|
3277
|
+
return this._r("DELETE", `/api/im/memory/files/${fileId}`);
|
|
3278
|
+
}
|
|
3279
|
+
/** Compact conversation messages into a summary */
|
|
3280
|
+
async compact(options) {
|
|
3281
|
+
return this._r("POST", "/api/im/memory/compact", options);
|
|
3282
|
+
}
|
|
3283
|
+
/** Get compaction summaries for a conversation */
|
|
3284
|
+
async getCompaction(conversationId) {
|
|
3285
|
+
return this._r("GET", `/api/im/memory/compact/${conversationId}`);
|
|
3286
|
+
}
|
|
3287
|
+
/** Load memory for session context */
|
|
3288
|
+
async load(scope) {
|
|
3289
|
+
const query = {};
|
|
3290
|
+
if (scope) query.scope = scope;
|
|
3291
|
+
return this._r("GET", "/api/im/memory/load", void 0, query);
|
|
3292
|
+
}
|
|
3293
|
+
};
|
|
3294
|
+
var IdentityClient = class {
|
|
3295
|
+
constructor(_r) {
|
|
3296
|
+
this._r = _r;
|
|
3297
|
+
}
|
|
3298
|
+
/** Get server public key */
|
|
3299
|
+
async getServerKey() {
|
|
3300
|
+
return this._r("GET", "/api/im/keys/server");
|
|
3301
|
+
}
|
|
3302
|
+
/** Register or rotate an identity key */
|
|
3303
|
+
async registerKey(options) {
|
|
3304
|
+
return this._r("PUT", "/api/im/keys/identity", options);
|
|
3305
|
+
}
|
|
3306
|
+
/** Get a user's identity key */
|
|
3307
|
+
async getKey(userId) {
|
|
3308
|
+
return this._r("GET", `/api/im/keys/identity/${userId}`);
|
|
3309
|
+
}
|
|
3310
|
+
/** Revoke own identity key */
|
|
3311
|
+
async revokeKey() {
|
|
3312
|
+
return this._r("POST", "/api/im/keys/identity/revoke");
|
|
3313
|
+
}
|
|
3314
|
+
/** Get key audit log for a user */
|
|
3315
|
+
async getAuditLog(userId) {
|
|
3316
|
+
return this._r("GET", `/api/im/keys/audit/${userId}`);
|
|
3317
|
+
}
|
|
3318
|
+
/** Verify key audit log integrity */
|
|
3319
|
+
async verifyAuditLog(userId) {
|
|
3320
|
+
return this._r("GET", `/api/im/keys/audit/${userId}/verify`);
|
|
3321
|
+
}
|
|
3322
|
+
};
|
|
3323
|
+
var SecurityClient = class {
|
|
3324
|
+
constructor(_r) {
|
|
3325
|
+
this._r = _r;
|
|
3326
|
+
}
|
|
3327
|
+
/** Get conversation security settings */
|
|
3328
|
+
async getConversationSecurity(conversationId) {
|
|
3329
|
+
return this._r("GET", `/api/im/conversations/${conversationId}/security`);
|
|
3330
|
+
}
|
|
3331
|
+
/** Update conversation security settings */
|
|
3332
|
+
async setConversationSecurity(conversationId, options) {
|
|
3333
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}/security`, options);
|
|
3334
|
+
}
|
|
3335
|
+
/** Upload a public key for a conversation */
|
|
3336
|
+
async uploadKey(conversationId, publicKey, algorithm) {
|
|
3337
|
+
const body = { publicKey };
|
|
3338
|
+
if (algorithm) body.algorithm = algorithm;
|
|
3339
|
+
return this._r("POST", `/api/im/conversations/${conversationId}/keys`, body);
|
|
3340
|
+
}
|
|
3341
|
+
/** Get keys for a conversation */
|
|
3342
|
+
async getKeys(conversationId) {
|
|
3343
|
+
return this._r("GET", `/api/im/conversations/${conversationId}/keys`);
|
|
3344
|
+
}
|
|
3345
|
+
/** Revoke a key for a specific user in a conversation */
|
|
3346
|
+
async revokeKey(conversationId, keyUserId) {
|
|
3347
|
+
return this._r("DELETE", `/api/im/conversations/${conversationId}/keys/${keyUserId}`);
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
3350
|
+
var EvolutionClient = class {
|
|
3351
|
+
constructor(_r) {
|
|
3352
|
+
this._r = _r;
|
|
3353
|
+
}
|
|
3354
|
+
// ── Public endpoints (no auth required) ──
|
|
3355
|
+
/** Get evolution stats */
|
|
3356
|
+
async getStats() {
|
|
3357
|
+
return this._r("GET", "/api/im/evolution/public/stats");
|
|
3358
|
+
}
|
|
3359
|
+
/** Get hot/trending genes */
|
|
3360
|
+
async getHotGenes(limit) {
|
|
3361
|
+
const query = {};
|
|
3362
|
+
if (limit != null) query.limit = String(limit);
|
|
3363
|
+
return this._r("GET", "/api/im/evolution/public/hot", void 0, query);
|
|
3364
|
+
}
|
|
3365
|
+
/** Browse published genes */
|
|
3366
|
+
async browseGenes(options) {
|
|
3367
|
+
const query = {};
|
|
3368
|
+
if (options?.category) query.category = options.category;
|
|
3369
|
+
if (options?.search) query.search = options.search;
|
|
3370
|
+
if (options?.sort) query.sort = options.sort;
|
|
3371
|
+
if (options?.page != null) query.page = String(options.page);
|
|
3372
|
+
if (options?.limit != null) query.limit = String(options.limit);
|
|
3373
|
+
return this._r("GET", "/api/im/evolution/public/genes", void 0, query);
|
|
3374
|
+
}
|
|
3375
|
+
/** Get a public gene by ID */
|
|
3376
|
+
async getPublicGene(geneId) {
|
|
3377
|
+
return this._r("GET", `/api/im/evolution/public/genes/${geneId}`);
|
|
3378
|
+
}
|
|
3379
|
+
/** Get capsules for a public gene */
|
|
3380
|
+
async getGeneCapsules(geneId, limit) {
|
|
3381
|
+
const query = {};
|
|
3382
|
+
if (limit != null) query.limit = String(limit);
|
|
3383
|
+
return this._r("GET", `/api/im/evolution/public/genes/${geneId}/capsules`, void 0, query);
|
|
3384
|
+
}
|
|
3385
|
+
/** Get gene lineage (parent + children) */
|
|
3386
|
+
async getGeneLineage(geneId) {
|
|
3387
|
+
return this._r("GET", `/api/im/evolution/public/genes/${geneId}/lineage`);
|
|
3388
|
+
}
|
|
3389
|
+
/** Get public evolution feed */
|
|
3390
|
+
async getFeed(limit) {
|
|
3391
|
+
const query = {};
|
|
3392
|
+
if (limit != null) query.limit = String(limit);
|
|
3393
|
+
return this._r("GET", "/api/im/evolution/public/feed", void 0, query);
|
|
3394
|
+
}
|
|
3395
|
+
// ── Authenticated endpoints ──
|
|
3396
|
+
/** Analyze signals and get gene recommendation */
|
|
3397
|
+
async analyze(options) {
|
|
3398
|
+
const { scope, ...body } = options;
|
|
3399
|
+
const q = {};
|
|
3400
|
+
if (scope) q.scope = scope;
|
|
3401
|
+
return this._r("POST", "/api/im/evolution/analyze", body, q);
|
|
3402
|
+
}
|
|
3403
|
+
/** Record an outcome (success/failure) for a gene */
|
|
3404
|
+
async record(options) {
|
|
3405
|
+
const { scope, ...body } = options;
|
|
3406
|
+
const q = {};
|
|
3407
|
+
if (scope) q.scope = scope;
|
|
3408
|
+
return this._r("POST", "/api/im/evolution/record", body, q);
|
|
3409
|
+
}
|
|
3410
|
+
/**
|
|
3411
|
+
* One-step evolution: analyze context → get gene recommendation → auto-record outcome.
|
|
3412
|
+
* Combines analyze() + record() into a single call for the common case.
|
|
3413
|
+
*
|
|
3414
|
+
* Usage:
|
|
3415
|
+
* const result = await client.evolution.evolve({
|
|
3416
|
+
* error: 'Connection timeout after 10s',
|
|
3417
|
+
* outcome: 'success',
|
|
3418
|
+
* score: 0.85,
|
|
3419
|
+
* summary: 'Fixed with exponential backoff',
|
|
3420
|
+
* });
|
|
3421
|
+
*/
|
|
3422
|
+
async evolve(options) {
|
|
3423
|
+
const { outcome, score, summary, strategy_used, scope, ...analyzeOpts } = options;
|
|
3424
|
+
const analysis = await this.analyze({ ...analyzeOpts, ...scope ? { scope } : {} });
|
|
3425
|
+
if (!analysis.ok || !analysis.data) {
|
|
3426
|
+
return { ok: false, error: analysis.error };
|
|
3427
|
+
}
|
|
3428
|
+
const data = analysis.data;
|
|
3429
|
+
const geneId = data.gene_id;
|
|
3430
|
+
if (geneId && (data.action === "apply_gene" || data.action === "explore")) {
|
|
3431
|
+
const recordResult = await this.record({
|
|
3432
|
+
gene_id: geneId,
|
|
3433
|
+
signals: data.signals || analyzeOpts.signals || [],
|
|
3434
|
+
outcome,
|
|
3435
|
+
score: score ?? (outcome === "success" ? 0.8 : 0.2),
|
|
3436
|
+
summary: summary || `${outcome === "success" ? "Resolved" : "Failed to resolve"} using ${geneId}`,
|
|
3437
|
+
strategy_used,
|
|
3438
|
+
...scope ? { scope } : {}
|
|
3439
|
+
});
|
|
3440
|
+
return {
|
|
3441
|
+
ok: true,
|
|
3442
|
+
data: {
|
|
3443
|
+
analysis: data,
|
|
3444
|
+
recorded: true,
|
|
3445
|
+
edge_updated: recordResult.data?.edge_updated
|
|
3446
|
+
}
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
return {
|
|
3450
|
+
ok: true,
|
|
3451
|
+
data: { analysis: data, recorded: false }
|
|
3452
|
+
};
|
|
3453
|
+
}
|
|
3454
|
+
/** Trigger gene distillation */
|
|
3455
|
+
async distill(dryRun) {
|
|
3456
|
+
const query = {};
|
|
3457
|
+
if (dryRun) query.dry_run = "true";
|
|
3458
|
+
return this._r("POST", "/api/im/evolution/distill", void 0, query);
|
|
3459
|
+
}
|
|
3460
|
+
/** List own genes */
|
|
3461
|
+
async listGenes(signals, scope) {
|
|
3462
|
+
const query = {};
|
|
3463
|
+
if (signals) query.signals = signals;
|
|
3464
|
+
if (scope) query.scope = scope;
|
|
3465
|
+
return this._r("GET", "/api/im/evolution/genes", void 0, query);
|
|
3466
|
+
}
|
|
3467
|
+
/** Create a new gene */
|
|
3468
|
+
async createGene(options) {
|
|
3469
|
+
const { scope, ...body } = options;
|
|
3470
|
+
const q = {};
|
|
3471
|
+
if (scope) q.scope = scope;
|
|
3472
|
+
return this._r("POST", "/api/im/evolution/genes", body, q);
|
|
3473
|
+
}
|
|
3474
|
+
/** Delete a gene */
|
|
3475
|
+
async deleteGene(geneId) {
|
|
3476
|
+
return this._r("DELETE", `/api/im/evolution/genes/${encodeURIComponent(geneId)}`);
|
|
3477
|
+
}
|
|
3478
|
+
/** Publish a gene. Pass skipCanary=true to bypass canary validation (MVP/admin). */
|
|
3479
|
+
async publishGene(geneId, options) {
|
|
3480
|
+
return this._r("POST", `/api/im/evolution/genes/${encodeURIComponent(geneId)}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
|
|
3481
|
+
}
|
|
3482
|
+
/** Import a published gene */
|
|
3483
|
+
async importGene(geneId) {
|
|
3484
|
+
return this._r("POST", "/api/im/evolution/genes/import", { gene_id: geneId });
|
|
3485
|
+
}
|
|
3486
|
+
/** Fork a gene with modifications */
|
|
3487
|
+
async forkGene(options) {
|
|
3488
|
+
return this._r("POST", "/api/im/evolution/genes/fork", options);
|
|
3489
|
+
}
|
|
3490
|
+
/** Get signal-gene edges */
|
|
3491
|
+
async getEdges(options) {
|
|
3492
|
+
const query = {};
|
|
3493
|
+
if (options?.signalKey) query.signal_key = options.signalKey;
|
|
3494
|
+
if (options?.geneId) query.gene_id = options.geneId;
|
|
3495
|
+
if (options?.limit != null) query.limit = String(options.limit);
|
|
3496
|
+
if (options?.scope) query.scope = options.scope;
|
|
3497
|
+
return this._r("GET", "/api/im/evolution/edges", void 0, query);
|
|
3498
|
+
}
|
|
3499
|
+
/** Get agent personality profile */
|
|
3500
|
+
async getPersonality(agentId) {
|
|
3501
|
+
return this._r("GET", `/api/im/evolution/personality/${agentId}`);
|
|
3502
|
+
}
|
|
3503
|
+
/** Get own capsule history */
|
|
3504
|
+
async getCapsules(options) {
|
|
3505
|
+
const query = {};
|
|
3506
|
+
if (options?.page != null) query.page = String(options.page);
|
|
3507
|
+
if (options?.limit != null) query.limit = String(options.limit);
|
|
3508
|
+
if (options?.scope) query.scope = options.scope;
|
|
3509
|
+
return this._r("GET", "/api/im/evolution/capsules", void 0, query);
|
|
3510
|
+
}
|
|
3511
|
+
/** Get evolution report */
|
|
3512
|
+
async getReport(agentId, scope) {
|
|
3513
|
+
const query = {};
|
|
3514
|
+
if (agentId) query.agent_id = agentId;
|
|
3515
|
+
if (scope) query.scope = scope;
|
|
3516
|
+
return this._r("GET", "/api/im/evolution/report", void 0, query);
|
|
3517
|
+
}
|
|
3518
|
+
/** List available evolution scopes */
|
|
3519
|
+
async listScopes() {
|
|
3520
|
+
return this._r("GET", "/api/im/evolution/scopes");
|
|
3521
|
+
}
|
|
3522
|
+
// ─── v0.3.1: Stories, Metrics, Skills ──────────────
|
|
3523
|
+
/** Get recent evolution stories (for L1 narrative embedding) */
|
|
3524
|
+
async getStories(options) {
|
|
3525
|
+
const query = {};
|
|
3526
|
+
if (options?.limit != null) query.limit = String(options.limit);
|
|
3527
|
+
if (options?.since != null) query.since = String(options.since);
|
|
3528
|
+
return this._r("GET", "/api/im/evolution/stories", void 0, query);
|
|
3529
|
+
}
|
|
3530
|
+
/** Get north-star metrics comparison (standard vs hypergraph) */
|
|
3531
|
+
async getMetrics() {
|
|
3532
|
+
return this._r("GET", "/api/im/evolution/metrics");
|
|
3533
|
+
}
|
|
3534
|
+
/** Trigger metrics collection snapshot */
|
|
3535
|
+
async collectMetrics(windowHours) {
|
|
3536
|
+
return this._r("POST", "/api/im/evolution/metrics/collect", { window_hours: windowHours ?? 1 });
|
|
3537
|
+
}
|
|
3538
|
+
/** Search skills catalog */
|
|
3539
|
+
async searchSkills(options) {
|
|
3540
|
+
const q = {};
|
|
3541
|
+
if (options?.query) q.query = options.query;
|
|
3542
|
+
if (options?.category) q.category = options.category;
|
|
3543
|
+
if (options?.limit != null) q.limit = String(options.limit);
|
|
3544
|
+
return this._r("GET", "/api/im/skills/search", void 0, q);
|
|
3545
|
+
}
|
|
3546
|
+
/** Get skill catalog stats */
|
|
3547
|
+
async getSkillStats() {
|
|
3548
|
+
return this._r("GET", "/api/im/skills/stats");
|
|
3549
|
+
}
|
|
3550
|
+
/** Install a skill — creates Gene + returns content + install guide */
|
|
3551
|
+
async installSkill(slugOrId) {
|
|
3552
|
+
return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`);
|
|
3553
|
+
}
|
|
3554
|
+
/** Uninstall a skill */
|
|
3555
|
+
async uninstallSkill(slugOrId) {
|
|
3556
|
+
return this._r("DELETE", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`);
|
|
3557
|
+
}
|
|
3558
|
+
/** List installed skills for this agent */
|
|
3559
|
+
async installedSkills() {
|
|
3560
|
+
return this._r("GET", "/api/im/skills/installed");
|
|
3561
|
+
}
|
|
3562
|
+
/** Get full skill content (SKILL.md + package info) */
|
|
3563
|
+
async getSkillContent(slugOrId) {
|
|
3564
|
+
return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
|
|
3565
|
+
}
|
|
3566
|
+
/** Create/submit a community skill */
|
|
3567
|
+
async createSkill(input) {
|
|
3568
|
+
return this._r("POST", "/api/im/skills", input);
|
|
3569
|
+
}
|
|
3570
|
+
/** Star a skill (increment community rating) */
|
|
3571
|
+
async starSkill(skillId) {
|
|
3572
|
+
return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
|
|
3573
|
+
}
|
|
3574
|
+
/**
|
|
3575
|
+
* Install a skill and write SKILL.md to local filesystem.
|
|
3576
|
+
* Combines cloud install + local file sync for Claude Code / OpenClaw / OpenCode.
|
|
3577
|
+
* @param slugOrId - Skill slug or ID
|
|
3578
|
+
* @param options - Local install options
|
|
3579
|
+
*/
|
|
3580
|
+
async installSkillLocal(slugOrId, options) {
|
|
3581
|
+
const result = await this.installSkill(slugOrId);
|
|
3582
|
+
if (!result.ok || !result.data) return result;
|
|
3583
|
+
let content = result.data.skill?.content || "";
|
|
3584
|
+
if (!content) {
|
|
3585
|
+
const contentResult = await this.getSkillContent(slugOrId);
|
|
3586
|
+
content = contentResult.data?.content || "";
|
|
3587
|
+
}
|
|
3588
|
+
if (!content) {
|
|
3589
|
+
return { ...result, data: { ...result.data, localPaths: [] } };
|
|
3590
|
+
}
|
|
3591
|
+
const rawSlug = result.data.skill?.slug || slugOrId;
|
|
3592
|
+
const slug = rawSlug.replace(/[\/\\]/g, "").replace(/\.\./g, "");
|
|
3593
|
+
if (!slug) {
|
|
3594
|
+
return { ...result, data: { ...result.data, localPaths: [] } };
|
|
3595
|
+
}
|
|
3596
|
+
const localPaths = [];
|
|
3597
|
+
try {
|
|
3598
|
+
const fs = await import("fs");
|
|
3599
|
+
const path = await import("path");
|
|
3600
|
+
const os = await import("os");
|
|
3601
|
+
const home = os.homedir();
|
|
3602
|
+
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
|
|
3603
|
+
const platformPaths = options?.project ? {
|
|
3604
|
+
"claude-code": path.join(options.projectRoot || ".", ".claude", "skills", slug),
|
|
3605
|
+
"openclaw": path.join(options.projectRoot || ".", "skills", slug),
|
|
3606
|
+
"opencode": path.join(options.projectRoot || ".", ".opencode", "skills", slug),
|
|
3607
|
+
"plugin": path.join(options.projectRoot || ".", ".claude", "plugins", "prismer", "skills", slug)
|
|
3608
|
+
} : {
|
|
3609
|
+
"claude-code": path.join(home, ".claude", "skills", slug),
|
|
3610
|
+
"openclaw": path.join(home, ".openclaw", "skills", slug),
|
|
3611
|
+
"opencode": path.join(home, ".config", "opencode", "skills", slug),
|
|
3612
|
+
"plugin": path.join(pluginBase, "skills", slug)
|
|
3613
|
+
};
|
|
3614
|
+
const targets = options?.platforms || Object.keys(platformPaths);
|
|
3615
|
+
for (const platform of targets) {
|
|
3616
|
+
const dir = platformPaths[platform];
|
|
3617
|
+
if (!dir) continue;
|
|
3618
|
+
try {
|
|
3619
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
3620
|
+
const filePath = path.join(dir, "SKILL.md");
|
|
3621
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
3622
|
+
localPaths.push(filePath);
|
|
3623
|
+
} catch {
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
} catch {
|
|
3627
|
+
}
|
|
3628
|
+
return { ...result, data: { ...result.data, localPaths } };
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Uninstall a skill and remove local SKILL.md files.
|
|
3632
|
+
*/
|
|
3633
|
+
async uninstallSkillLocal(slugOrId) {
|
|
3634
|
+
const result = await this.uninstallSkill(slugOrId);
|
|
3635
|
+
const removedPaths = [];
|
|
3636
|
+
const slug = safeSlug(slugOrId);
|
|
3637
|
+
if (!slug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
|
|
3638
|
+
try {
|
|
3639
|
+
const fs = await import("fs");
|
|
3640
|
+
const path = await import("path");
|
|
3641
|
+
const os = await import("os");
|
|
3642
|
+
const home = os.homedir();
|
|
3643
|
+
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
|
|
3644
|
+
const dirs = [
|
|
3645
|
+
path.join(home, ".claude", "skills", slug),
|
|
3646
|
+
path.join(home, ".openclaw", "skills", slug),
|
|
3647
|
+
path.join(home, ".config", "opencode", "skills", slug),
|
|
3648
|
+
path.join(pluginBase, "skills", slug)
|
|
3649
|
+
];
|
|
3650
|
+
for (const dir of dirs) {
|
|
3651
|
+
try {
|
|
3652
|
+
if (fs.existsSync(dir)) {
|
|
3653
|
+
fs.rmSync(dir, { recursive: true });
|
|
3654
|
+
removedPaths.push(dir);
|
|
3655
|
+
}
|
|
3656
|
+
} catch {
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
} catch {
|
|
3660
|
+
}
|
|
3661
|
+
return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
|
|
3662
|
+
}
|
|
3663
|
+
/**
|
|
3664
|
+
* Sync all installed skills to local filesystem.
|
|
3665
|
+
*/
|
|
3666
|
+
async syncSkillsLocal(options) {
|
|
3667
|
+
const installed = await this.installedSkills();
|
|
3668
|
+
if (!installed.ok || !installed.data) return { synced: 0, failed: 0, paths: [] };
|
|
3669
|
+
let synced = 0;
|
|
3670
|
+
let failed = 0;
|
|
3671
|
+
const paths = [];
|
|
3672
|
+
for (const record of installed.data) {
|
|
3673
|
+
const rawSlug = record.skill?.slug;
|
|
3674
|
+
if (!rawSlug) {
|
|
3675
|
+
failed++;
|
|
3676
|
+
continue;
|
|
3677
|
+
}
|
|
3678
|
+
const slug = rawSlug.replace(/[\/\\]/g, "").replace(/\.\./g, "");
|
|
3679
|
+
if (!slug) {
|
|
3680
|
+
failed++;
|
|
3681
|
+
continue;
|
|
3682
|
+
}
|
|
3683
|
+
try {
|
|
3684
|
+
const contentResult = await this.getSkillContent(slug);
|
|
3685
|
+
const content = contentResult.data?.content;
|
|
3686
|
+
if (!content) {
|
|
3687
|
+
failed++;
|
|
3688
|
+
continue;
|
|
3689
|
+
}
|
|
3690
|
+
const fs = await import("fs");
|
|
3691
|
+
const path = await import("path");
|
|
3692
|
+
const os = await import("os");
|
|
3693
|
+
const home = os.homedir();
|
|
3694
|
+
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
|
|
3695
|
+
const platformPaths = {
|
|
3696
|
+
"claude-code": path.join(home, ".claude", "skills", slug),
|
|
3697
|
+
"openclaw": path.join(home, ".openclaw", "skills", slug),
|
|
3698
|
+
"opencode": path.join(home, ".config", "opencode", "skills", slug),
|
|
3699
|
+
"plugin": path.join(pluginBase, "skills", slug)
|
|
3700
|
+
};
|
|
3701
|
+
const targets = options?.platforms || Object.keys(platformPaths);
|
|
3702
|
+
for (const platform of targets) {
|
|
3703
|
+
const dir = platformPaths[platform];
|
|
3704
|
+
if (!dir) continue;
|
|
3705
|
+
try {
|
|
3706
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
3707
|
+
const filePath = path.join(dir, "SKILL.md");
|
|
3708
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
3709
|
+
paths.push(filePath);
|
|
3710
|
+
} catch {
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
synced++;
|
|
3714
|
+
} catch {
|
|
3715
|
+
failed++;
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return { synced, failed, paths };
|
|
3719
|
+
}
|
|
3720
|
+
/** Export a Gene as a Skill */
|
|
3721
|
+
async exportAsSkill(geneId, options) {
|
|
3722
|
+
return this._r("POST", `/api/im/evolution/genes/${geneId}/export-skill`, options);
|
|
3723
|
+
}
|
|
3724
|
+
// ─── P0: Report, Achievements, Sync ──────────────
|
|
3725
|
+
/** Submit a raw-context evolution report (auto-creates signals + gene match) */
|
|
3726
|
+
async submitReport(options) {
|
|
3727
|
+
return this._r("POST", "/api/im/evolution/report", {
|
|
3728
|
+
raw_context: options.rawContext,
|
|
3729
|
+
outcome: options.outcome,
|
|
3730
|
+
task_context: options.taskContext,
|
|
3731
|
+
task_error: options.taskError,
|
|
3732
|
+
task_id: options.taskId,
|
|
3733
|
+
metadata: options.metadata
|
|
3734
|
+
});
|
|
3735
|
+
}
|
|
3736
|
+
/** Get status of a submitted report by traceId */
|
|
3737
|
+
async getReportStatus(traceId) {
|
|
3738
|
+
return this._r("GET", `/api/im/evolution/report/${traceId}`);
|
|
3739
|
+
}
|
|
3740
|
+
/** Get evolution achievements for the current agent */
|
|
3741
|
+
async getAchievements() {
|
|
3742
|
+
return this._r("GET", "/api/im/evolution/achievements");
|
|
3743
|
+
}
|
|
3744
|
+
/** Get a sync snapshot (global gene/edge state since a sequence number) */
|
|
3745
|
+
async getSyncSnapshot(since) {
|
|
3746
|
+
const query = { scope: "global" };
|
|
3747
|
+
if (since != null) query.since = String(since);
|
|
3748
|
+
return this._r("GET", "/api/im/evolution/sync/snapshot", void 0, query);
|
|
3749
|
+
}
|
|
3750
|
+
/** Bidirectional sync: push local outcomes and pull remote updates */
|
|
3751
|
+
async sync(options) {
|
|
3752
|
+
const body = {};
|
|
3753
|
+
if (options?.pushOutcomes) body.push = { outcomes: options.pushOutcomes };
|
|
3754
|
+
if (options?.pullSince != null) body.pull = { since: options.pullSince };
|
|
3755
|
+
return this._r("POST", "/api/im/evolution/sync", body);
|
|
3756
|
+
}
|
|
3757
|
+
};
|
|
3758
|
+
function safeSlug(input) {
|
|
3759
|
+
return input.replace(/[\/\\]/g, "").replace(/\.\./g, "").replace(/\0/g, "");
|
|
3760
|
+
}
|
|
2539
3761
|
function guessMimeType(fileName) {
|
|
2540
3762
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
2541
3763
|
const map = {
|
|
@@ -2774,6 +3996,11 @@ var IMClient = class {
|
|
|
2774
3996
|
this.bindings = new BindingsClient(request);
|
|
2775
3997
|
this.credits = new CreditsClient(request);
|
|
2776
3998
|
this.workspace = new WorkspaceClient(request);
|
|
3999
|
+
this.tasks = new TasksClient(request);
|
|
4000
|
+
this.memory = new MemoryClient(request);
|
|
4001
|
+
this.identity = new IdentityClient(request);
|
|
4002
|
+
this.security = new SecurityClient(request);
|
|
4003
|
+
this.evolution = new EvolutionClient(request);
|
|
2777
4004
|
this.files = new FilesClient(request, wsBase, fetchFn, getAuthHeaders);
|
|
2778
4005
|
this.realtime = new IMRealtimeClient(wsBase);
|
|
2779
4006
|
this.offline = offlineManager ?? null;
|
|
@@ -2948,6 +4175,7 @@ function createClient(config) {
|
|
|
2948
4175
|
}
|
|
2949
4176
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2950
4177
|
0 && (module.exports = {
|
|
4178
|
+
AIPIdentity,
|
|
2951
4179
|
AccountClient,
|
|
2952
4180
|
AttachmentQueue,
|
|
2953
4181
|
BindingsClient,
|
|
@@ -2957,11 +4185,16 @@ function createClient(config) {
|
|
|
2957
4185
|
DirectClient,
|
|
2958
4186
|
E2EEncryption,
|
|
2959
4187
|
ENVIRONMENTS,
|
|
4188
|
+
EvolutionCache,
|
|
4189
|
+
EvolutionClient,
|
|
4190
|
+
EvolutionRuntime,
|
|
2960
4191
|
FilesClient,
|
|
2961
4192
|
GroupsClient,
|
|
2962
4193
|
IMClient,
|
|
2963
4194
|
IMRealtimeClient,
|
|
4195
|
+
IdentityClient,
|
|
2964
4196
|
IndexedDBStorage,
|
|
4197
|
+
MemoryClient,
|
|
2965
4198
|
MemoryStorage,
|
|
2966
4199
|
MessagesClient,
|
|
2967
4200
|
OfflineManager,
|
|
@@ -2969,7 +4202,20 @@ function createClient(config) {
|
|
|
2969
4202
|
RealtimeSSEClient,
|
|
2970
4203
|
RealtimeWSClient,
|
|
2971
4204
|
SQLiteStorage,
|
|
4205
|
+
SecurityClient,
|
|
2972
4206
|
TabCoordinator,
|
|
4207
|
+
TasksClient,
|
|
2973
4208
|
WorkspaceClient,
|
|
2974
|
-
createClient
|
|
4209
|
+
createClient,
|
|
4210
|
+
createEnrichedExtractor,
|
|
4211
|
+
decryptContext,
|
|
4212
|
+
decryptFile,
|
|
4213
|
+
decryptMessages,
|
|
4214
|
+
decryptOnReceive,
|
|
4215
|
+
encryptContext,
|
|
4216
|
+
encryptFile,
|
|
4217
|
+
encryptForSend,
|
|
4218
|
+
extractSignals,
|
|
4219
|
+
guessMimeType,
|
|
4220
|
+
safeSlug
|
|
2975
4221
|
});
|