chainlesschain 0.81.0 → 0.132.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/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/commands/agent-network.js +254 -1
- package/src/commands/audit.js +302 -0
- package/src/commands/automation.js +271 -1
- package/src/commands/codegen.js +224 -0
- package/src/commands/collab.js +341 -0
- package/src/commands/compliance.js +1035 -0
- package/src/commands/cowork.js +221 -0
- package/src/commands/dbevo.js +284 -0
- package/src/commands/dev.js +252 -0
- package/src/commands/did.js +358 -0
- package/src/commands/encrypt.js +341 -0
- package/src/commands/export.js +256 -1
- package/src/commands/fusion.js +258 -0
- package/src/commands/governance.js +325 -0
- package/src/commands/hardening.js +411 -0
- package/src/commands/hook.js +148 -0
- package/src/commands/import.js +252 -0
- package/src/commands/incentive.js +322 -0
- package/src/commands/infra.js +244 -0
- package/src/commands/instinct.js +260 -0
- package/src/commands/ipfs.js +318 -0
- package/src/commands/kg.js +387 -0
- package/src/commands/llm.js +263 -0
- package/src/commands/mcp.js +221 -0
- package/src/commands/memory.js +248 -0
- package/src/commands/multimodal.js +296 -0
- package/src/commands/nlprog.js +356 -0
- package/src/commands/note.js +244 -0
- package/src/commands/ops.js +354 -0
- package/src/commands/orchestrate.js +166 -0
- package/src/commands/org.js +277 -0
- package/src/commands/p2p.js +390 -0
- package/src/commands/perception.js +290 -0
- package/src/commands/permmem.js +251 -0
- package/src/commands/plugin-ecosystem.js +273 -0
- package/src/commands/pqc.js +393 -0
- package/src/commands/quantization.js +351 -0
- package/src/commands/rcache.js +271 -0
- package/src/commands/recommend.js +340 -0
- package/src/commands/runtime.js +307 -0
- package/src/commands/scim.js +262 -0
- package/src/commands/session.js +258 -0
- package/src/commands/skill.js +267 -1
- package/src/commands/social.js +256 -0
- package/src/commands/sso.js +186 -1
- package/src/commands/sync.js +256 -0
- package/src/commands/tech.js +338 -0
- package/src/commands/tenant.js +351 -0
- package/src/commands/tokens.js +269 -0
- package/src/commands/trust.js +249 -0
- package/src/commands/wallet.js +277 -0
- package/src/commands/workflow.js +171 -0
- package/src/index.js +4 -0
- package/src/lib/agent-coordinator.js +325 -0
- package/src/lib/agent-network.js +387 -0
- package/src/lib/agent-router.js +395 -0
- package/src/lib/aiops.js +478 -0
- package/src/lib/audit-logger.js +379 -0
- package/src/lib/automation-engine.js +330 -0
- package/src/lib/autonomous-developer.js +350 -0
- package/src/lib/code-agent.js +323 -0
- package/src/lib/collaboration-governance.js +364 -0
- package/src/lib/community-governance.js +436 -0
- package/src/lib/compliance-manager.js +434 -0
- package/src/lib/content-recommendation.js +469 -0
- package/src/lib/crypto-manager.js +350 -0
- package/src/lib/dbevo.js +338 -0
- package/src/lib/decentral-infra.js +340 -0
- package/src/lib/did-manager.js +367 -0
- package/src/lib/hardening-manager.js +348 -0
- package/src/lib/hook-manager.js +380 -0
- package/src/lib/instinct-manager.js +332 -0
- package/src/lib/ipfs-storage.js +334 -0
- package/src/lib/knowledge-exporter.js +381 -0
- package/src/lib/knowledge-graph.js +432 -0
- package/src/lib/knowledge-importer.js +379 -0
- package/src/lib/llm-providers.js +391 -0
- package/src/lib/mcp-registry.js +333 -0
- package/src/lib/memory-manager.js +330 -0
- package/src/lib/multimodal.js +346 -0
- package/src/lib/nl-programming.js +343 -0
- package/src/lib/note-versioning.js +327 -0
- package/src/lib/org-manager.js +323 -0
- package/src/lib/p2p-manager.js +387 -0
- package/src/lib/perception.js +346 -0
- package/src/lib/perf-tuning.js +4 -1
- package/src/lib/permanent-memory.js +320 -0
- package/src/lib/plugin-ecosystem.js +377 -0
- package/src/lib/pqc-manager.js +368 -0
- package/src/lib/protocol-fusion.js +417 -0
- package/src/lib/quantization.js +325 -0
- package/src/lib/response-cache.js +327 -0
- package/src/lib/scim-manager.js +329 -0
- package/src/lib/session-manager.js +329 -0
- package/src/lib/skill-loader.js +377 -0
- package/src/lib/social-manager.js +326 -0
- package/src/lib/sso-manager.js +332 -0
- package/src/lib/sync-manager.js +326 -0
- package/src/lib/tech-learning-engine.js +369 -0
- package/src/lib/tenant-saas.js +460 -0
- package/src/lib/threat-intel.js +335 -0
- package/src/lib/token-incentive.js +293 -0
- package/src/lib/token-tracker.js +329 -0
- package/src/lib/trust-security.js +390 -0
- package/src/lib/ueba.js +389 -0
- package/src/lib/universal-runtime.js +325 -0
- package/src/lib/wallet-manager.js +326 -0
- package/src/lib/workflow-engine.js +322 -0
package/src/lib/scim-manager.js
CHANGED
|
@@ -210,3 +210,332 @@ export function _resetState() {
|
|
|
210
210
|
_connectors.clear();
|
|
211
211
|
_syncLog.length = 0;
|
|
212
212
|
}
|
|
213
|
+
|
|
214
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
215
|
+
* V2 Surface — SCIM provisioning lifecycle layer.
|
|
216
|
+
* Tracks identities and sync-job lifecycle independent of legacy
|
|
217
|
+
* createUser/syncProvision flows above.
|
|
218
|
+
* ═══════════════════════════════════════════════════════════════ */
|
|
219
|
+
|
|
220
|
+
export const IDENTITY_LIFECYCLE_V2 = Object.freeze({
|
|
221
|
+
PENDING: "pending",
|
|
222
|
+
PROVISIONED: "provisioned",
|
|
223
|
+
SUSPENDED: "suspended",
|
|
224
|
+
DEPROVISIONED: "deprovisioned",
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export const SYNC_JOB_V2 = Object.freeze({
|
|
228
|
+
QUEUED: "queued",
|
|
229
|
+
RUNNING: "running",
|
|
230
|
+
SUCCEEDED: "succeeded",
|
|
231
|
+
FAILED: "failed",
|
|
232
|
+
CANCELLED: "cancelled",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const IDENTITY_TRANSITIONS_V2 = new Map([
|
|
236
|
+
["pending", new Set(["provisioned", "deprovisioned"])],
|
|
237
|
+
["provisioned", new Set(["suspended", "deprovisioned"])],
|
|
238
|
+
["suspended", new Set(["provisioned", "deprovisioned"])],
|
|
239
|
+
["deprovisioned", new Set()],
|
|
240
|
+
]);
|
|
241
|
+
const IDENTITY_TERMINALS_V2 = new Set(["deprovisioned"]);
|
|
242
|
+
|
|
243
|
+
const SYNC_TRANSITIONS_V2 = new Map([
|
|
244
|
+
["queued", new Set(["running", "cancelled"])],
|
|
245
|
+
["running", new Set(["succeeded", "failed", "cancelled"])],
|
|
246
|
+
["succeeded", new Set()],
|
|
247
|
+
["failed", new Set()],
|
|
248
|
+
["cancelled", new Set()],
|
|
249
|
+
]);
|
|
250
|
+
const SYNC_TERMINALS_V2 = new Set(["succeeded", "failed", "cancelled"]);
|
|
251
|
+
|
|
252
|
+
export const SCIM_DEFAULT_MAX_PROVISIONED_PER_CONNECTOR = 1000;
|
|
253
|
+
export const SCIM_DEFAULT_MAX_RUNNING_SYNC_PER_CONNECTOR = 2;
|
|
254
|
+
export const SCIM_DEFAULT_IDENTITY_IDLE_MS = 1000 * 60 * 60 * 24 * 90; // 90 days
|
|
255
|
+
export const SCIM_DEFAULT_SYNC_STUCK_MS = 1000 * 60 * 30; // 30 min
|
|
256
|
+
|
|
257
|
+
const _identitiesV2 = new Map();
|
|
258
|
+
const _syncJobsV2 = new Map();
|
|
259
|
+
let _maxProvisionedPerConnectorV2 = SCIM_DEFAULT_MAX_PROVISIONED_PER_CONNECTOR;
|
|
260
|
+
let _maxRunningSyncPerConnectorV2 = SCIM_DEFAULT_MAX_RUNNING_SYNC_PER_CONNECTOR;
|
|
261
|
+
let _identityIdleMsV2 = SCIM_DEFAULT_IDENTITY_IDLE_MS;
|
|
262
|
+
let _syncStuckMsV2 = SCIM_DEFAULT_SYNC_STUCK_MS;
|
|
263
|
+
|
|
264
|
+
function _posIntScimV2(n, label) {
|
|
265
|
+
const v = Math.floor(Number(n));
|
|
266
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
267
|
+
throw new Error(`${label} must be a positive integer`);
|
|
268
|
+
return v;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function getMaxProvisionedPerConnectorV2() {
|
|
272
|
+
return _maxProvisionedPerConnectorV2;
|
|
273
|
+
}
|
|
274
|
+
export function setMaxProvisionedPerConnectorV2(n) {
|
|
275
|
+
_maxProvisionedPerConnectorV2 = _posIntScimV2(
|
|
276
|
+
n,
|
|
277
|
+
"maxProvisionedPerConnector",
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
export function getMaxRunningSyncPerConnectorV2() {
|
|
281
|
+
return _maxRunningSyncPerConnectorV2;
|
|
282
|
+
}
|
|
283
|
+
export function setMaxRunningSyncPerConnectorV2(n) {
|
|
284
|
+
_maxRunningSyncPerConnectorV2 = _posIntScimV2(
|
|
285
|
+
n,
|
|
286
|
+
"maxRunningSyncPerConnector",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
export function getIdentityIdleMsV2() {
|
|
290
|
+
return _identityIdleMsV2;
|
|
291
|
+
}
|
|
292
|
+
export function setIdentityIdleMsV2(n) {
|
|
293
|
+
_identityIdleMsV2 = _posIntScimV2(n, "identityIdleMs");
|
|
294
|
+
}
|
|
295
|
+
export function getSyncStuckMsV2() {
|
|
296
|
+
return _syncStuckMsV2;
|
|
297
|
+
}
|
|
298
|
+
export function setSyncStuckMsV2(n) {
|
|
299
|
+
_syncStuckMsV2 = _posIntScimV2(n, "syncStuckMs");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function getProvisionedCountV2(connectorId) {
|
|
303
|
+
let n = 0;
|
|
304
|
+
for (const i of _identitiesV2.values()) {
|
|
305
|
+
if (i.connectorId === connectorId && i.status === "provisioned") n += 1;
|
|
306
|
+
}
|
|
307
|
+
return n;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function getRunningSyncCountV2(connectorId) {
|
|
311
|
+
let n = 0;
|
|
312
|
+
for (const j of _syncJobsV2.values()) {
|
|
313
|
+
if (j.connectorId === connectorId && j.status === "running") n += 1;
|
|
314
|
+
}
|
|
315
|
+
return n;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function _copyIdentityV2(i) {
|
|
319
|
+
return { ...i, metadata: { ...i.metadata } };
|
|
320
|
+
}
|
|
321
|
+
function _copySyncV2(j) {
|
|
322
|
+
return { ...j, metadata: { ...j.metadata } };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function registerIdentityV2(
|
|
326
|
+
id,
|
|
327
|
+
{ connectorId, externalId, metadata = {}, now = Date.now() } = {},
|
|
328
|
+
) {
|
|
329
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
330
|
+
if (!connectorId || typeof connectorId !== "string")
|
|
331
|
+
throw new Error("connectorId must be a string");
|
|
332
|
+
if (!externalId || typeof externalId !== "string")
|
|
333
|
+
throw new Error("externalId must be a string");
|
|
334
|
+
if (_identitiesV2.has(id)) throw new Error(`identity ${id} already exists`);
|
|
335
|
+
const i = {
|
|
336
|
+
id,
|
|
337
|
+
connectorId,
|
|
338
|
+
externalId,
|
|
339
|
+
status: "pending",
|
|
340
|
+
createdAt: now,
|
|
341
|
+
lastSeenAt: now,
|
|
342
|
+
provisionedAt: null,
|
|
343
|
+
deprovisionedAt: null,
|
|
344
|
+
metadata: { ...metadata },
|
|
345
|
+
};
|
|
346
|
+
_identitiesV2.set(id, i);
|
|
347
|
+
return _copyIdentityV2(i);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function getIdentityV2(id) {
|
|
351
|
+
const i = _identitiesV2.get(id);
|
|
352
|
+
return i ? _copyIdentityV2(i) : null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function listIdentitiesV2({ connectorId, status } = {}) {
|
|
356
|
+
const out = [];
|
|
357
|
+
for (const i of _identitiesV2.values()) {
|
|
358
|
+
if (connectorId && i.connectorId !== connectorId) continue;
|
|
359
|
+
if (status && i.status !== status) continue;
|
|
360
|
+
out.push(_copyIdentityV2(i));
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function setIdentityStatusV2(id, next, { now = Date.now() } = {}) {
|
|
366
|
+
const i = _identitiesV2.get(id);
|
|
367
|
+
if (!i) throw new Error(`identity ${id} not found`);
|
|
368
|
+
if (!IDENTITY_TRANSITIONS_V2.has(next))
|
|
369
|
+
throw new Error(`unknown identity status: ${next}`);
|
|
370
|
+
if (IDENTITY_TERMINALS_V2.has(i.status))
|
|
371
|
+
throw new Error(`identity ${id} is in terminal state ${i.status}`);
|
|
372
|
+
const allowed = IDENTITY_TRANSITIONS_V2.get(i.status);
|
|
373
|
+
if (!allowed.has(next))
|
|
374
|
+
throw new Error(`cannot transition identity from ${i.status} to ${next}`);
|
|
375
|
+
if (next === "provisioned") {
|
|
376
|
+
if (i.status === "pending") {
|
|
377
|
+
const count = getProvisionedCountV2(i.connectorId);
|
|
378
|
+
if (count >= _maxProvisionedPerConnectorV2)
|
|
379
|
+
throw new Error(
|
|
380
|
+
`connector ${i.connectorId} already at provisioned cap (${_maxProvisionedPerConnectorV2})`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (!i.provisionedAt) i.provisionedAt = now;
|
|
384
|
+
}
|
|
385
|
+
if (next === "deprovisioned" && !i.deprovisionedAt) i.deprovisionedAt = now;
|
|
386
|
+
i.status = next;
|
|
387
|
+
i.lastSeenAt = now;
|
|
388
|
+
return _copyIdentityV2(i);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function provisionIdentityV2(id, opts) {
|
|
392
|
+
return setIdentityStatusV2(id, "provisioned", opts);
|
|
393
|
+
}
|
|
394
|
+
export function suspendIdentityV2(id, opts) {
|
|
395
|
+
return setIdentityStatusV2(id, "suspended", opts);
|
|
396
|
+
}
|
|
397
|
+
export function deprovisionIdentityV2(id, opts) {
|
|
398
|
+
return setIdentityStatusV2(id, "deprovisioned", opts);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function touchIdentityV2(id, { now = Date.now() } = {}) {
|
|
402
|
+
const i = _identitiesV2.get(id);
|
|
403
|
+
if (!i) throw new Error(`identity ${id} not found`);
|
|
404
|
+
i.lastSeenAt = now;
|
|
405
|
+
return _copyIdentityV2(i);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function createSyncJobV2(
|
|
409
|
+
id,
|
|
410
|
+
{ connectorId, kind = "full", metadata = {}, now = Date.now() } = {},
|
|
411
|
+
) {
|
|
412
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
413
|
+
if (!connectorId || typeof connectorId !== "string")
|
|
414
|
+
throw new Error("connectorId must be a string");
|
|
415
|
+
if (_syncJobsV2.has(id)) throw new Error(`syncJob ${id} already exists`);
|
|
416
|
+
const j = {
|
|
417
|
+
id,
|
|
418
|
+
connectorId,
|
|
419
|
+
kind,
|
|
420
|
+
status: "queued",
|
|
421
|
+
createdAt: now,
|
|
422
|
+
lastSeenAt: now,
|
|
423
|
+
startedAt: null,
|
|
424
|
+
finishedAt: null,
|
|
425
|
+
metadata: { ...metadata },
|
|
426
|
+
};
|
|
427
|
+
_syncJobsV2.set(id, j);
|
|
428
|
+
return _copySyncV2(j);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function getSyncJobV2(id) {
|
|
432
|
+
const j = _syncJobsV2.get(id);
|
|
433
|
+
return j ? _copySyncV2(j) : null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function listSyncJobsV2({ connectorId, status } = {}) {
|
|
437
|
+
const out = [];
|
|
438
|
+
for (const j of _syncJobsV2.values()) {
|
|
439
|
+
if (connectorId && j.connectorId !== connectorId) continue;
|
|
440
|
+
if (status && j.status !== status) continue;
|
|
441
|
+
out.push(_copySyncV2(j));
|
|
442
|
+
}
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function setSyncJobStatusV2(id, next, { now = Date.now() } = {}) {
|
|
447
|
+
const j = _syncJobsV2.get(id);
|
|
448
|
+
if (!j) throw new Error(`syncJob ${id} not found`);
|
|
449
|
+
if (!SYNC_TRANSITIONS_V2.has(next))
|
|
450
|
+
throw new Error(`unknown syncJob status: ${next}`);
|
|
451
|
+
if (SYNC_TERMINALS_V2.has(j.status))
|
|
452
|
+
throw new Error(`syncJob ${id} is in terminal state ${j.status}`);
|
|
453
|
+
const allowed = SYNC_TRANSITIONS_V2.get(j.status);
|
|
454
|
+
if (!allowed.has(next))
|
|
455
|
+
throw new Error(`cannot transition syncJob from ${j.status} to ${next}`);
|
|
456
|
+
if (next === "running" && j.status === "queued") {
|
|
457
|
+
const count = getRunningSyncCountV2(j.connectorId);
|
|
458
|
+
if (count >= _maxRunningSyncPerConnectorV2)
|
|
459
|
+
throw new Error(
|
|
460
|
+
`connector ${j.connectorId} already at running-sync cap (${_maxRunningSyncPerConnectorV2})`,
|
|
461
|
+
);
|
|
462
|
+
if (!j.startedAt) j.startedAt = now;
|
|
463
|
+
}
|
|
464
|
+
if (SYNC_TERMINALS_V2.has(next) && !j.finishedAt) j.finishedAt = now;
|
|
465
|
+
j.status = next;
|
|
466
|
+
j.lastSeenAt = now;
|
|
467
|
+
return _copySyncV2(j);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export function startSyncJobV2(id, opts) {
|
|
471
|
+
return setSyncJobStatusV2(id, "running", opts);
|
|
472
|
+
}
|
|
473
|
+
export function succeedSyncJobV2(id, opts) {
|
|
474
|
+
return setSyncJobStatusV2(id, "succeeded", opts);
|
|
475
|
+
}
|
|
476
|
+
export function failSyncJobV2(id, opts) {
|
|
477
|
+
return setSyncJobStatusV2(id, "failed", opts);
|
|
478
|
+
}
|
|
479
|
+
export function cancelSyncJobV2(id, opts) {
|
|
480
|
+
return setSyncJobStatusV2(id, "cancelled", opts);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export function autoDeprovisionIdleIdentitiesV2({ now = Date.now() } = {}) {
|
|
484
|
+
const flipped = [];
|
|
485
|
+
for (const i of _identitiesV2.values()) {
|
|
486
|
+
if (i.status === "deprovisioned" || i.status === "pending") continue;
|
|
487
|
+
if (now - i.lastSeenAt > _identityIdleMsV2) {
|
|
488
|
+
i.status = "deprovisioned";
|
|
489
|
+
i.lastSeenAt = now;
|
|
490
|
+
if (!i.deprovisionedAt) i.deprovisionedAt = now;
|
|
491
|
+
flipped.push(_copyIdentityV2(i));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return flipped;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export function autoFailStuckSyncJobsV2({ now = Date.now() } = {}) {
|
|
498
|
+
const flipped = [];
|
|
499
|
+
for (const j of _syncJobsV2.values()) {
|
|
500
|
+
if (j.status !== "running") continue;
|
|
501
|
+
const ref = j.startedAt ?? j.lastSeenAt;
|
|
502
|
+
if (now - ref > _syncStuckMsV2) {
|
|
503
|
+
j.status = "failed";
|
|
504
|
+
j.lastSeenAt = now;
|
|
505
|
+
if (!j.finishedAt) j.finishedAt = now;
|
|
506
|
+
flipped.push(_copySyncV2(j));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return flipped;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export function getScimManagerStatsV2() {
|
|
513
|
+
const identitiesByStatus = {};
|
|
514
|
+
for (const v of Object.values(IDENTITY_LIFECYCLE_V2))
|
|
515
|
+
identitiesByStatus[v] = 0;
|
|
516
|
+
for (const i of _identitiesV2.values()) identitiesByStatus[i.status] += 1;
|
|
517
|
+
|
|
518
|
+
const syncJobsByStatus = {};
|
|
519
|
+
for (const v of Object.values(SYNC_JOB_V2)) syncJobsByStatus[v] = 0;
|
|
520
|
+
for (const j of _syncJobsV2.values()) syncJobsByStatus[j.status] += 1;
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
totalIdentitiesV2: _identitiesV2.size,
|
|
524
|
+
totalSyncJobsV2: _syncJobsV2.size,
|
|
525
|
+
maxProvisionedPerConnector: _maxProvisionedPerConnectorV2,
|
|
526
|
+
maxRunningSyncPerConnector: _maxRunningSyncPerConnectorV2,
|
|
527
|
+
identityIdleMs: _identityIdleMsV2,
|
|
528
|
+
syncStuckMs: _syncStuckMsV2,
|
|
529
|
+
identitiesByStatus,
|
|
530
|
+
syncJobsByStatus,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function _resetStateScimManagerV2() {
|
|
535
|
+
_identitiesV2.clear();
|
|
536
|
+
_syncJobsV2.clear();
|
|
537
|
+
_maxProvisionedPerConnectorV2 = SCIM_DEFAULT_MAX_PROVISIONED_PER_CONNECTOR;
|
|
538
|
+
_maxRunningSyncPerConnectorV2 = SCIM_DEFAULT_MAX_RUNNING_SYNC_PER_CONNECTOR;
|
|
539
|
+
_identityIdleMsV2 = SCIM_DEFAULT_IDENTITY_IDLE_MS;
|
|
540
|
+
_syncStuckMsV2 = SCIM_DEFAULT_SYNC_STUCK_MS;
|
|
541
|
+
}
|
|
@@ -262,3 +262,332 @@ export function exportSessionMarkdown(session) {
|
|
|
262
262
|
|
|
263
263
|
return lines.join("\n");
|
|
264
264
|
}
|
|
265
|
+
|
|
266
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
267
|
+
* V2 Surface — Session governance layer.
|
|
268
|
+
* Tracks conversation maturity + turn lifecycle independent of
|
|
269
|
+
* legacy llm_sessions SQLite store above.
|
|
270
|
+
* ═══════════════════════════════════════════════════════════════ */
|
|
271
|
+
|
|
272
|
+
export const CONVERSATION_MATURITY_V2 = Object.freeze({
|
|
273
|
+
DRAFT: "draft",
|
|
274
|
+
ACTIVE: "active",
|
|
275
|
+
PAUSED: "paused",
|
|
276
|
+
ARCHIVED: "archived",
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
export const TURN_LIFECYCLE_V2 = Object.freeze({
|
|
280
|
+
PENDING: "pending",
|
|
281
|
+
STREAMING: "streaming",
|
|
282
|
+
COMPLETED: "completed",
|
|
283
|
+
FAILED: "failed",
|
|
284
|
+
CANCELLED: "cancelled",
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const CONV_TRANSITIONS_V2 = new Map([
|
|
288
|
+
["draft", new Set(["active", "archived"])],
|
|
289
|
+
["active", new Set(["paused", "archived"])],
|
|
290
|
+
["paused", new Set(["active", "archived"])],
|
|
291
|
+
["archived", new Set()],
|
|
292
|
+
]);
|
|
293
|
+
const CONV_TERMINALS_V2 = new Set(["archived"]);
|
|
294
|
+
|
|
295
|
+
const TURN_TRANSITIONS_V2 = new Map([
|
|
296
|
+
["pending", new Set(["streaming", "cancelled"])],
|
|
297
|
+
["streaming", new Set(["completed", "failed", "cancelled"])],
|
|
298
|
+
["completed", new Set()],
|
|
299
|
+
["failed", new Set()],
|
|
300
|
+
["cancelled", new Set()],
|
|
301
|
+
]);
|
|
302
|
+
const TURN_TERMINALS_V2 = new Set(["completed", "failed", "cancelled"]);
|
|
303
|
+
|
|
304
|
+
export const SESSION_DEFAULT_MAX_ACTIVE_CONV_PER_USER = 50;
|
|
305
|
+
export const SESSION_DEFAULT_MAX_PENDING_TURNS_PER_CONV = 3;
|
|
306
|
+
export const SESSION_DEFAULT_CONV_IDLE_MS = 1000 * 60 * 60 * 24 * 14; // 14 days
|
|
307
|
+
export const SESSION_DEFAULT_TURN_STUCK_MS = 1000 * 60 * 5; // 5 min
|
|
308
|
+
|
|
309
|
+
const _conversationsV2 = new Map();
|
|
310
|
+
const _turnsV2 = new Map();
|
|
311
|
+
let _maxActiveConvPerUserV2 = SESSION_DEFAULT_MAX_ACTIVE_CONV_PER_USER;
|
|
312
|
+
let _maxPendingTurnsPerConvV2 = SESSION_DEFAULT_MAX_PENDING_TURNS_PER_CONV;
|
|
313
|
+
let _convIdleMsV2 = SESSION_DEFAULT_CONV_IDLE_MS;
|
|
314
|
+
let _turnStuckMsV2 = SESSION_DEFAULT_TURN_STUCK_MS;
|
|
315
|
+
|
|
316
|
+
function _posIntSessionV2(n, label) {
|
|
317
|
+
const v = Math.floor(Number(n));
|
|
318
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
319
|
+
throw new Error(`${label} must be a positive integer`);
|
|
320
|
+
return v;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function getMaxActiveConvPerUserV2() {
|
|
324
|
+
return _maxActiveConvPerUserV2;
|
|
325
|
+
}
|
|
326
|
+
export function setMaxActiveConvPerUserV2(n) {
|
|
327
|
+
_maxActiveConvPerUserV2 = _posIntSessionV2(n, "maxActiveConvPerUser");
|
|
328
|
+
}
|
|
329
|
+
export function getMaxPendingTurnsPerConvV2() {
|
|
330
|
+
return _maxPendingTurnsPerConvV2;
|
|
331
|
+
}
|
|
332
|
+
export function setMaxPendingTurnsPerConvV2(n) {
|
|
333
|
+
_maxPendingTurnsPerConvV2 = _posIntSessionV2(n, "maxPendingTurnsPerConv");
|
|
334
|
+
}
|
|
335
|
+
export function getConvIdleMsV2() {
|
|
336
|
+
return _convIdleMsV2;
|
|
337
|
+
}
|
|
338
|
+
export function setConvIdleMsV2(n) {
|
|
339
|
+
_convIdleMsV2 = _posIntSessionV2(n, "convIdleMs");
|
|
340
|
+
}
|
|
341
|
+
export function getTurnStuckMsV2() {
|
|
342
|
+
return _turnStuckMsV2;
|
|
343
|
+
}
|
|
344
|
+
export function setTurnStuckMsV2(n) {
|
|
345
|
+
_turnStuckMsV2 = _posIntSessionV2(n, "turnStuckMs");
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function getActiveConvCountV2(userId) {
|
|
349
|
+
let n = 0;
|
|
350
|
+
for (const c of _conversationsV2.values()) {
|
|
351
|
+
if (c.userId === userId && c.status === "active") n += 1;
|
|
352
|
+
}
|
|
353
|
+
return n;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function getPendingTurnCountV2(conversationId) {
|
|
357
|
+
let n = 0;
|
|
358
|
+
for (const t of _turnsV2.values()) {
|
|
359
|
+
if (
|
|
360
|
+
t.conversationId === conversationId &&
|
|
361
|
+
(t.status === "pending" || t.status === "streaming")
|
|
362
|
+
)
|
|
363
|
+
n += 1;
|
|
364
|
+
}
|
|
365
|
+
return n;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function _copyConvV2(c) {
|
|
369
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
370
|
+
}
|
|
371
|
+
function _copyTurnV2(t) {
|
|
372
|
+
return { ...t, metadata: { ...t.metadata } };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function registerConversationV2(
|
|
376
|
+
id,
|
|
377
|
+
{ userId, model, metadata = {}, now = Date.now() } = {},
|
|
378
|
+
) {
|
|
379
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
380
|
+
if (!userId || typeof userId !== "string")
|
|
381
|
+
throw new Error("userId must be a string");
|
|
382
|
+
if (!model || typeof model !== "string")
|
|
383
|
+
throw new Error("model must be a string");
|
|
384
|
+
if (_conversationsV2.has(id))
|
|
385
|
+
throw new Error(`conversation ${id} already exists`);
|
|
386
|
+
const c = {
|
|
387
|
+
id,
|
|
388
|
+
userId,
|
|
389
|
+
model,
|
|
390
|
+
status: "draft",
|
|
391
|
+
createdAt: now,
|
|
392
|
+
lastSeenAt: now,
|
|
393
|
+
activatedAt: null,
|
|
394
|
+
archivedAt: null,
|
|
395
|
+
metadata: { ...metadata },
|
|
396
|
+
};
|
|
397
|
+
_conversationsV2.set(id, c);
|
|
398
|
+
return _copyConvV2(c);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function getConversationV2(id) {
|
|
402
|
+
const c = _conversationsV2.get(id);
|
|
403
|
+
return c ? _copyConvV2(c) : null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function listConversationsV2({ userId, status } = {}) {
|
|
407
|
+
const out = [];
|
|
408
|
+
for (const c of _conversationsV2.values()) {
|
|
409
|
+
if (userId && c.userId !== userId) continue;
|
|
410
|
+
if (status && c.status !== status) continue;
|
|
411
|
+
out.push(_copyConvV2(c));
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export function setConversationStatusV2(id, next, { now = Date.now() } = {}) {
|
|
417
|
+
const c = _conversationsV2.get(id);
|
|
418
|
+
if (!c) throw new Error(`conversation ${id} not found`);
|
|
419
|
+
if (!CONV_TRANSITIONS_V2.has(next))
|
|
420
|
+
throw new Error(`unknown conversation status: ${next}`);
|
|
421
|
+
if (CONV_TERMINALS_V2.has(c.status))
|
|
422
|
+
throw new Error(`conversation ${id} is in terminal state ${c.status}`);
|
|
423
|
+
const allowed = CONV_TRANSITIONS_V2.get(c.status);
|
|
424
|
+
if (!allowed.has(next))
|
|
425
|
+
throw new Error(
|
|
426
|
+
`cannot transition conversation from ${c.status} to ${next}`,
|
|
427
|
+
);
|
|
428
|
+
if (next === "active") {
|
|
429
|
+
if (c.status === "draft") {
|
|
430
|
+
const count = getActiveConvCountV2(c.userId);
|
|
431
|
+
if (count >= _maxActiveConvPerUserV2)
|
|
432
|
+
throw new Error(
|
|
433
|
+
`user ${c.userId} already at active-conversation cap (${_maxActiveConvPerUserV2})`,
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if (!c.activatedAt) c.activatedAt = now;
|
|
437
|
+
}
|
|
438
|
+
if (next === "archived" && !c.archivedAt) c.archivedAt = now;
|
|
439
|
+
c.status = next;
|
|
440
|
+
c.lastSeenAt = now;
|
|
441
|
+
return _copyConvV2(c);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function activateConversationV2(id, opts) {
|
|
445
|
+
return setConversationStatusV2(id, "active", opts);
|
|
446
|
+
}
|
|
447
|
+
export function pauseConversationV2(id, opts) {
|
|
448
|
+
return setConversationStatusV2(id, "paused", opts);
|
|
449
|
+
}
|
|
450
|
+
export function archiveConversationV2(id, opts) {
|
|
451
|
+
return setConversationStatusV2(id, "archived", opts);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function touchConversationV2(id, { now = Date.now() } = {}) {
|
|
455
|
+
const c = _conversationsV2.get(id);
|
|
456
|
+
if (!c) throw new Error(`conversation ${id} not found`);
|
|
457
|
+
c.lastSeenAt = now;
|
|
458
|
+
return _copyConvV2(c);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function createTurnV2(
|
|
462
|
+
id,
|
|
463
|
+
{ conversationId, role = "user", metadata = {}, now = Date.now() } = {},
|
|
464
|
+
) {
|
|
465
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
466
|
+
if (!conversationId || typeof conversationId !== "string")
|
|
467
|
+
throw new Error("conversationId must be a string");
|
|
468
|
+
if (_turnsV2.has(id)) throw new Error(`turn ${id} already exists`);
|
|
469
|
+
const count = getPendingTurnCountV2(conversationId);
|
|
470
|
+
if (count >= _maxPendingTurnsPerConvV2)
|
|
471
|
+
throw new Error(
|
|
472
|
+
`conversation ${conversationId} already at pending-turn cap (${_maxPendingTurnsPerConvV2})`,
|
|
473
|
+
);
|
|
474
|
+
const t = {
|
|
475
|
+
id,
|
|
476
|
+
conversationId,
|
|
477
|
+
role,
|
|
478
|
+
status: "pending",
|
|
479
|
+
createdAt: now,
|
|
480
|
+
lastSeenAt: now,
|
|
481
|
+
streamingStartedAt: null,
|
|
482
|
+
settledAt: null,
|
|
483
|
+
metadata: { ...metadata },
|
|
484
|
+
};
|
|
485
|
+
_turnsV2.set(id, t);
|
|
486
|
+
return _copyTurnV2(t);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export function getTurnV2(id) {
|
|
490
|
+
const t = _turnsV2.get(id);
|
|
491
|
+
return t ? _copyTurnV2(t) : null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export function listTurnsV2({ conversationId, status } = {}) {
|
|
495
|
+
const out = [];
|
|
496
|
+
for (const t of _turnsV2.values()) {
|
|
497
|
+
if (conversationId && t.conversationId !== conversationId) continue;
|
|
498
|
+
if (status && t.status !== status) continue;
|
|
499
|
+
out.push(_copyTurnV2(t));
|
|
500
|
+
}
|
|
501
|
+
return out;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export function setTurnStatusV2(id, next, { now = Date.now() } = {}) {
|
|
505
|
+
const t = _turnsV2.get(id);
|
|
506
|
+
if (!t) throw new Error(`turn ${id} not found`);
|
|
507
|
+
if (!TURN_TRANSITIONS_V2.has(next))
|
|
508
|
+
throw new Error(`unknown turn status: ${next}`);
|
|
509
|
+
if (TURN_TERMINALS_V2.has(t.status))
|
|
510
|
+
throw new Error(`turn ${id} is in terminal state ${t.status}`);
|
|
511
|
+
const allowed = TURN_TRANSITIONS_V2.get(t.status);
|
|
512
|
+
if (!allowed.has(next))
|
|
513
|
+
throw new Error(`cannot transition turn from ${t.status} to ${next}`);
|
|
514
|
+
if (next === "streaming" && !t.streamingStartedAt) t.streamingStartedAt = now;
|
|
515
|
+
if (TURN_TERMINALS_V2.has(next) && !t.settledAt) t.settledAt = now;
|
|
516
|
+
t.status = next;
|
|
517
|
+
t.lastSeenAt = now;
|
|
518
|
+
return _copyTurnV2(t);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function streamTurnV2(id, opts) {
|
|
522
|
+
return setTurnStatusV2(id, "streaming", opts);
|
|
523
|
+
}
|
|
524
|
+
export function completeTurnV2(id, opts) {
|
|
525
|
+
return setTurnStatusV2(id, "completed", opts);
|
|
526
|
+
}
|
|
527
|
+
export function failTurnV2(id, opts) {
|
|
528
|
+
return setTurnStatusV2(id, "failed", opts);
|
|
529
|
+
}
|
|
530
|
+
export function cancelTurnV2(id, opts) {
|
|
531
|
+
return setTurnStatusV2(id, "cancelled", opts);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function autoArchiveIdleConversationsV2({ now = Date.now() } = {}) {
|
|
535
|
+
const flipped = [];
|
|
536
|
+
for (const c of _conversationsV2.values()) {
|
|
537
|
+
if (c.status === "archived" || c.status === "draft") continue;
|
|
538
|
+
if (now - c.lastSeenAt > _convIdleMsV2) {
|
|
539
|
+
c.status = "archived";
|
|
540
|
+
c.lastSeenAt = now;
|
|
541
|
+
if (!c.archivedAt) c.archivedAt = now;
|
|
542
|
+
flipped.push(_copyConvV2(c));
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return flipped;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export function autoFailStuckTurnsV2({ now = Date.now() } = {}) {
|
|
549
|
+
const flipped = [];
|
|
550
|
+
for (const t of _turnsV2.values()) {
|
|
551
|
+
if (t.status !== "streaming") continue;
|
|
552
|
+
const ref = t.streamingStartedAt ?? t.lastSeenAt;
|
|
553
|
+
if (now - ref > _turnStuckMsV2) {
|
|
554
|
+
t.status = "failed";
|
|
555
|
+
t.lastSeenAt = now;
|
|
556
|
+
if (!t.settledAt) t.settledAt = now;
|
|
557
|
+
flipped.push(_copyTurnV2(t));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return flipped;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export function getSessionManagerStatsV2() {
|
|
564
|
+
const conversationsByStatus = {};
|
|
565
|
+
for (const v of Object.values(CONVERSATION_MATURITY_V2))
|
|
566
|
+
conversationsByStatus[v] = 0;
|
|
567
|
+
for (const c of _conversationsV2.values())
|
|
568
|
+
conversationsByStatus[c.status] += 1;
|
|
569
|
+
|
|
570
|
+
const turnsByStatus = {};
|
|
571
|
+
for (const v of Object.values(TURN_LIFECYCLE_V2)) turnsByStatus[v] = 0;
|
|
572
|
+
for (const t of _turnsV2.values()) turnsByStatus[t.status] += 1;
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
totalConversationsV2: _conversationsV2.size,
|
|
576
|
+
totalTurnsV2: _turnsV2.size,
|
|
577
|
+
maxActiveConvPerUser: _maxActiveConvPerUserV2,
|
|
578
|
+
maxPendingTurnsPerConv: _maxPendingTurnsPerConvV2,
|
|
579
|
+
convIdleMs: _convIdleMsV2,
|
|
580
|
+
turnStuckMs: _turnStuckMsV2,
|
|
581
|
+
conversationsByStatus,
|
|
582
|
+
turnsByStatus,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
export function _resetStateSessionManagerV2() {
|
|
587
|
+
_conversationsV2.clear();
|
|
588
|
+
_turnsV2.clear();
|
|
589
|
+
_maxActiveConvPerUserV2 = SESSION_DEFAULT_MAX_ACTIVE_CONV_PER_USER;
|
|
590
|
+
_maxPendingTurnsPerConvV2 = SESSION_DEFAULT_MAX_PENDING_TURNS_PER_CONV;
|
|
591
|
+
_convIdleMsV2 = SESSION_DEFAULT_CONV_IDLE_MS;
|
|
592
|
+
_turnStuckMsV2 = SESSION_DEFAULT_TURN_STUCK_MS;
|
|
593
|
+
}
|