chainlesschain 0.132.0 → 0.145.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.
Files changed (135) hide show
  1. package/package.json +1 -1
  2. package/src/commands/a2a.js +230 -0
  3. package/src/commands/activitypub.js +191 -0
  4. package/src/commands/agent.js +601 -0
  5. package/src/commands/audit.js +206 -0
  6. package/src/commands/bi.js +186 -0
  7. package/src/commands/bm25.js +162 -0
  8. package/src/commands/browse.js +225 -0
  9. package/src/commands/ccron.js +178 -0
  10. package/src/commands/chat.js +207 -0
  11. package/src/commands/compliance.js +420 -0
  12. package/src/commands/compt.js +176 -0
  13. package/src/commands/consol.js +237 -0
  14. package/src/commands/cowork.js +588 -0
  15. package/src/commands/crosschain.js +216 -0
  16. package/src/commands/dao.js +216 -0
  17. package/src/commands/dlp.js +206 -0
  18. package/src/commands/economy.js +211 -0
  19. package/src/commands/evolution.js +209 -0
  20. package/src/commands/evomap.js +216 -0
  21. package/src/commands/fflag.js +230 -0
  22. package/src/commands/git.js +185 -0
  23. package/src/commands/hardening.js +209 -0
  24. package/src/commands/hmemory.js +210 -0
  25. package/src/commands/incentive.js +209 -0
  26. package/src/commands/inference.js +178 -0
  27. package/src/commands/itbudget.js +161 -0
  28. package/src/commands/kg.js +206 -0
  29. package/src/commands/lowcode.js +201 -0
  30. package/src/commands/marketplace.js +206 -0
  31. package/src/commands/matrix.js +214 -0
  32. package/src/commands/mcpscaf.js +153 -0
  33. package/src/commands/meminj.js +153 -0
  34. package/src/commands/nostr.js +213 -0
  35. package/src/commands/orchestrate.js +217 -0
  36. package/src/commands/orchgov.js +156 -0
  37. package/src/commands/pdfp.js +160 -0
  38. package/src/commands/perf.js +176 -0
  39. package/src/commands/perm.js +156 -0
  40. package/src/commands/pipeline.js +211 -0
  41. package/src/commands/planmode.js +154 -0
  42. package/src/commands/privacy.js +203 -0
  43. package/src/commands/promcomp.js +166 -0
  44. package/src/commands/recommend.js +185 -0
  45. package/src/commands/reputation.js +208 -0
  46. package/src/commands/sandbox.js +206 -0
  47. package/src/commands/seshhook.js +153 -0
  48. package/src/commands/seshsearch.js +149 -0
  49. package/src/commands/seshtail.js +152 -0
  50. package/src/commands/seshu.js +160 -0
  51. package/src/commands/sganal.js +172 -0
  52. package/src/commands/siem.js +207 -0
  53. package/src/commands/sla.js +212 -0
  54. package/src/commands/slotfill.js +154 -0
  55. package/src/commands/social.js +159 -0
  56. package/src/commands/stress.js +206 -0
  57. package/src/commands/svccont.js +157 -0
  58. package/src/commands/terraform.js +206 -0
  59. package/src/commands/tms.js +183 -0
  60. package/src/commands/topiccls.js +158 -0
  61. package/src/commands/uprof.js +154 -0
  62. package/src/commands/vcheck.js +172 -0
  63. package/src/commands/webfetch.js +150 -0
  64. package/src/commands/zkp.js +218 -0
  65. package/src/harness/prompt-compressor.js +331 -0
  66. package/src/index.js +101 -1
  67. package/src/lib/a2a-protocol.js +373 -0
  68. package/src/lib/activitypub-bridge.js +343 -0
  69. package/src/lib/agent-economy.js +358 -0
  70. package/src/lib/app-builder.js +338 -0
  71. package/src/lib/audit-logger.js +321 -0
  72. package/src/lib/autonomous-agent.js +341 -0
  73. package/src/lib/bi-engine.js +339 -0
  74. package/src/lib/bm25-search.js +333 -0
  75. package/src/lib/browser-automation.js +352 -0
  76. package/src/lib/chat-core.js +336 -0
  77. package/src/lib/claude-code-bridge.js +341 -0
  78. package/src/lib/compliance-framework-reporter.js +359 -0
  79. package/src/lib/compliance-manager.js +330 -0
  80. package/src/lib/compression-telemetry.js +333 -0
  81. package/src/lib/content-recommender.js +370 -0
  82. package/src/lib/cowork-cron.js +330 -0
  83. package/src/lib/cowork-learning.js +333 -0
  84. package/src/lib/cowork-task-runner.js +362 -0
  85. package/src/lib/cowork-workflow.js +327 -0
  86. package/src/lib/cross-chain.js +365 -0
  87. package/src/lib/dao-governance.js +339 -0
  88. package/src/lib/dlp-engine.js +343 -0
  89. package/src/lib/evolution-system.js +336 -0
  90. package/src/lib/evomap-manager.js +339 -0
  91. package/src/lib/execution-backend.js +351 -0
  92. package/src/lib/feature-flags.js +330 -0
  93. package/src/lib/git-integration.js +343 -0
  94. package/src/lib/hardening-manager.js +341 -0
  95. package/src/lib/hierarchical-memory.js +341 -0
  96. package/src/lib/inference-network.js +362 -0
  97. package/src/lib/iteration-budget.js +357 -0
  98. package/src/lib/knowledge-graph.js +333 -0
  99. package/src/lib/matrix-bridge.js +339 -0
  100. package/src/lib/mcp-scaffold.js +345 -0
  101. package/src/lib/memory-injection.js +320 -0
  102. package/src/lib/nostr-bridge.js +342 -0
  103. package/src/lib/orchestrator.js +350 -0
  104. package/src/lib/pdf-parser.js +330 -0
  105. package/src/lib/perf-tuning.js +364 -0
  106. package/src/lib/permission-engine.js +319 -0
  107. package/src/lib/pipeline-orchestrator.js +345 -0
  108. package/src/lib/plan-mode.js +328 -0
  109. package/src/lib/privacy-computing.js +335 -0
  110. package/src/lib/prompt-compressor.js +1 -10
  111. package/src/lib/reputation-optimizer.js +340 -0
  112. package/src/lib/sandbox-v2.js +327 -0
  113. package/src/lib/service-container.js +342 -0
  114. package/src/lib/session-consolidator.js +352 -0
  115. package/src/lib/session-hooks.js +340 -0
  116. package/src/lib/session-search.js +334 -0
  117. package/src/lib/session-tail.js +320 -0
  118. package/src/lib/session-usage.js +329 -0
  119. package/src/lib/siem-exporter.js +352 -0
  120. package/src/lib/skill-marketplace.js +345 -0
  121. package/src/lib/sla-manager.js +341 -0
  122. package/src/lib/slot-filler.js +333 -0
  123. package/src/lib/social-graph-analytics.js +327 -0
  124. package/src/lib/social-graph.js +304 -0
  125. package/src/lib/stress-tester.js +342 -0
  126. package/src/lib/sub-agent-registry.js +359 -0
  127. package/src/lib/task-model-selector.js +333 -0
  128. package/src/lib/terraform-manager.js +333 -0
  129. package/src/lib/todo-manager.js +339 -0
  130. package/src/lib/token-incentive.js +341 -0
  131. package/src/lib/topic-classifier.js +353 -0
  132. package/src/lib/user-profile.js +325 -0
  133. package/src/lib/version-checker.js +335 -0
  134. package/src/lib/web-fetch.js +322 -0
  135. package/src/lib/zkp-engine.js +342 -0
@@ -320,3 +320,336 @@ export class BM25Search {
320
320
  };
321
321
  }
322
322
  }
323
+
324
+ // =====================================================================
325
+ // BM25 Search V2 governance overlay
326
+ // =====================================================================
327
+ export const BM25_PROFILE_MATURITY_V2 = Object.freeze({
328
+ PENDING: "pending",
329
+ ACTIVE: "active",
330
+ STALE: "stale",
331
+ ARCHIVED: "archived",
332
+ });
333
+ export const BM25_QUERY_LIFECYCLE_V2 = Object.freeze({
334
+ QUEUED: "queued",
335
+ SEARCHING: "searching",
336
+ COMPLETED: "completed",
337
+ FAILED: "failed",
338
+ CANCELLED: "cancelled",
339
+ });
340
+ const _bm25PTrans = new Map([
341
+ [
342
+ BM25_PROFILE_MATURITY_V2.PENDING,
343
+ new Set([
344
+ BM25_PROFILE_MATURITY_V2.ACTIVE,
345
+ BM25_PROFILE_MATURITY_V2.ARCHIVED,
346
+ ]),
347
+ ],
348
+ [
349
+ BM25_PROFILE_MATURITY_V2.ACTIVE,
350
+ new Set([
351
+ BM25_PROFILE_MATURITY_V2.STALE,
352
+ BM25_PROFILE_MATURITY_V2.ARCHIVED,
353
+ ]),
354
+ ],
355
+ [
356
+ BM25_PROFILE_MATURITY_V2.STALE,
357
+ new Set([
358
+ BM25_PROFILE_MATURITY_V2.ACTIVE,
359
+ BM25_PROFILE_MATURITY_V2.ARCHIVED,
360
+ ]),
361
+ ],
362
+ [BM25_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
363
+ ]);
364
+ const _bm25PTerminal = new Set([BM25_PROFILE_MATURITY_V2.ARCHIVED]);
365
+ const _bm25JTrans = new Map([
366
+ [
367
+ BM25_QUERY_LIFECYCLE_V2.QUEUED,
368
+ new Set([
369
+ BM25_QUERY_LIFECYCLE_V2.SEARCHING,
370
+ BM25_QUERY_LIFECYCLE_V2.CANCELLED,
371
+ ]),
372
+ ],
373
+ [
374
+ BM25_QUERY_LIFECYCLE_V2.SEARCHING,
375
+ new Set([
376
+ BM25_QUERY_LIFECYCLE_V2.COMPLETED,
377
+ BM25_QUERY_LIFECYCLE_V2.FAILED,
378
+ BM25_QUERY_LIFECYCLE_V2.CANCELLED,
379
+ ]),
380
+ ],
381
+ [BM25_QUERY_LIFECYCLE_V2.COMPLETED, new Set()],
382
+ [BM25_QUERY_LIFECYCLE_V2.FAILED, new Set()],
383
+ [BM25_QUERY_LIFECYCLE_V2.CANCELLED, new Set()],
384
+ ]);
385
+ const _bm25PsV2 = new Map();
386
+ const _bm25JsV2 = new Map();
387
+ let _bm25MaxActive = 8,
388
+ _bm25MaxPending = 20,
389
+ _bm25IdleMs = 30 * 24 * 60 * 60 * 1000,
390
+ _bm25StuckMs = 30 * 1000;
391
+ function _bm25Pos(n, label) {
392
+ const v = Math.floor(Number(n));
393
+ if (!Number.isFinite(v) || v <= 0)
394
+ throw new Error(`${label} must be positive integer`);
395
+ return v;
396
+ }
397
+ function _bm25CheckP(from, to) {
398
+ const a = _bm25PTrans.get(from);
399
+ if (!a || !a.has(to))
400
+ throw new Error(`invalid bm25 profile transition ${from} → ${to}`);
401
+ }
402
+ function _bm25CheckJ(from, to) {
403
+ const a = _bm25JTrans.get(from);
404
+ if (!a || !a.has(to))
405
+ throw new Error(`invalid bm25 query transition ${from} → ${to}`);
406
+ }
407
+ function _bm25CountActive(owner) {
408
+ let c = 0;
409
+ for (const p of _bm25PsV2.values())
410
+ if (p.owner === owner && p.status === BM25_PROFILE_MATURITY_V2.ACTIVE) c++;
411
+ return c;
412
+ }
413
+ function _bm25CountPending(profileId) {
414
+ let c = 0;
415
+ for (const j of _bm25JsV2.values())
416
+ if (
417
+ j.profileId === profileId &&
418
+ (j.status === BM25_QUERY_LIFECYCLE_V2.QUEUED ||
419
+ j.status === BM25_QUERY_LIFECYCLE_V2.SEARCHING)
420
+ )
421
+ c++;
422
+ return c;
423
+ }
424
+ export function setMaxActiveBm25ProfilesPerOwnerV2(n) {
425
+ _bm25MaxActive = _bm25Pos(n, "maxActiveBm25ProfilesPerOwner");
426
+ }
427
+ export function getMaxActiveBm25ProfilesPerOwnerV2() {
428
+ return _bm25MaxActive;
429
+ }
430
+ export function setMaxPendingBm25QueriesPerProfileV2(n) {
431
+ _bm25MaxPending = _bm25Pos(n, "maxPendingBm25QueriesPerProfile");
432
+ }
433
+ export function getMaxPendingBm25QueriesPerProfileV2() {
434
+ return _bm25MaxPending;
435
+ }
436
+ export function setBm25ProfileIdleMsV2(n) {
437
+ _bm25IdleMs = _bm25Pos(n, "bm25ProfileIdleMs");
438
+ }
439
+ export function getBm25ProfileIdleMsV2() {
440
+ return _bm25IdleMs;
441
+ }
442
+ export function setBm25QueryStuckMsV2(n) {
443
+ _bm25StuckMs = _bm25Pos(n, "bm25QueryStuckMs");
444
+ }
445
+ export function getBm25QueryStuckMsV2() {
446
+ return _bm25StuckMs;
447
+ }
448
+ export function _resetStateBm25SearchV2() {
449
+ _bm25PsV2.clear();
450
+ _bm25JsV2.clear();
451
+ _bm25MaxActive = 8;
452
+ _bm25MaxPending = 20;
453
+ _bm25IdleMs = 30 * 24 * 60 * 60 * 1000;
454
+ _bm25StuckMs = 30 * 1000;
455
+ }
456
+ export function registerBm25ProfileV2({ id, owner, field, metadata } = {}) {
457
+ if (!id || !owner) throw new Error("id and owner required");
458
+ if (_bm25PsV2.has(id)) throw new Error(`bm25 profile ${id} already exists`);
459
+ const now = Date.now();
460
+ const p = {
461
+ id,
462
+ owner,
463
+ field: field || "content",
464
+ status: BM25_PROFILE_MATURITY_V2.PENDING,
465
+ createdAt: now,
466
+ updatedAt: now,
467
+ lastTouchedAt: now,
468
+ activatedAt: null,
469
+ archivedAt: null,
470
+ metadata: { ...(metadata || {}) },
471
+ };
472
+ _bm25PsV2.set(id, p);
473
+ return { ...p, metadata: { ...p.metadata } };
474
+ }
475
+ export function activateBm25ProfileV2(id) {
476
+ const p = _bm25PsV2.get(id);
477
+ if (!p) throw new Error(`bm25 profile ${id} not found`);
478
+ const isInitial = p.status === BM25_PROFILE_MATURITY_V2.PENDING;
479
+ _bm25CheckP(p.status, BM25_PROFILE_MATURITY_V2.ACTIVE);
480
+ if (isInitial && _bm25CountActive(p.owner) >= _bm25MaxActive)
481
+ throw new Error(`max active bm25 profiles for owner ${p.owner} reached`);
482
+ const now = Date.now();
483
+ p.status = BM25_PROFILE_MATURITY_V2.ACTIVE;
484
+ p.updatedAt = now;
485
+ p.lastTouchedAt = now;
486
+ if (!p.activatedAt) p.activatedAt = now;
487
+ return { ...p, metadata: { ...p.metadata } };
488
+ }
489
+ export function staleBm25ProfileV2(id) {
490
+ const p = _bm25PsV2.get(id);
491
+ if (!p) throw new Error(`bm25 profile ${id} not found`);
492
+ _bm25CheckP(p.status, BM25_PROFILE_MATURITY_V2.STALE);
493
+ p.status = BM25_PROFILE_MATURITY_V2.STALE;
494
+ p.updatedAt = Date.now();
495
+ return { ...p, metadata: { ...p.metadata } };
496
+ }
497
+ export function archiveBm25ProfileV2(id) {
498
+ const p = _bm25PsV2.get(id);
499
+ if (!p) throw new Error(`bm25 profile ${id} not found`);
500
+ _bm25CheckP(p.status, BM25_PROFILE_MATURITY_V2.ARCHIVED);
501
+ const now = Date.now();
502
+ p.status = BM25_PROFILE_MATURITY_V2.ARCHIVED;
503
+ p.updatedAt = now;
504
+ if (!p.archivedAt) p.archivedAt = now;
505
+ return { ...p, metadata: { ...p.metadata } };
506
+ }
507
+ export function touchBm25ProfileV2(id) {
508
+ const p = _bm25PsV2.get(id);
509
+ if (!p) throw new Error(`bm25 profile ${id} not found`);
510
+ if (_bm25PTerminal.has(p.status))
511
+ throw new Error(`cannot touch terminal bm25 profile ${id}`);
512
+ const now = Date.now();
513
+ p.lastTouchedAt = now;
514
+ p.updatedAt = now;
515
+ return { ...p, metadata: { ...p.metadata } };
516
+ }
517
+ export function getBm25ProfileV2(id) {
518
+ const p = _bm25PsV2.get(id);
519
+ if (!p) return null;
520
+ return { ...p, metadata: { ...p.metadata } };
521
+ }
522
+ export function listBm25ProfilesV2() {
523
+ return [..._bm25PsV2.values()].map((p) => ({
524
+ ...p,
525
+ metadata: { ...p.metadata },
526
+ }));
527
+ }
528
+ export function createBm25QueryV2({ id, profileId, q, metadata } = {}) {
529
+ if (!id || !profileId) throw new Error("id and profileId required");
530
+ if (_bm25JsV2.has(id)) throw new Error(`bm25 query ${id} already exists`);
531
+ if (!_bm25PsV2.has(profileId))
532
+ throw new Error(`bm25 profile ${profileId} not found`);
533
+ if (_bm25CountPending(profileId) >= _bm25MaxPending)
534
+ throw new Error(
535
+ `max pending bm25 queries for profile ${profileId} reached`,
536
+ );
537
+ const now = Date.now();
538
+ const j = {
539
+ id,
540
+ profileId,
541
+ q: q || "",
542
+ status: BM25_QUERY_LIFECYCLE_V2.QUEUED,
543
+ createdAt: now,
544
+ updatedAt: now,
545
+ startedAt: null,
546
+ settledAt: null,
547
+ metadata: { ...(metadata || {}) },
548
+ };
549
+ _bm25JsV2.set(id, j);
550
+ return { ...j, metadata: { ...j.metadata } };
551
+ }
552
+ export function searchingBm25QueryV2(id) {
553
+ const j = _bm25JsV2.get(id);
554
+ if (!j) throw new Error(`bm25 query ${id} not found`);
555
+ _bm25CheckJ(j.status, BM25_QUERY_LIFECYCLE_V2.SEARCHING);
556
+ const now = Date.now();
557
+ j.status = BM25_QUERY_LIFECYCLE_V2.SEARCHING;
558
+ j.updatedAt = now;
559
+ if (!j.startedAt) j.startedAt = now;
560
+ return { ...j, metadata: { ...j.metadata } };
561
+ }
562
+ export function completeBm25QueryV2(id) {
563
+ const j = _bm25JsV2.get(id);
564
+ if (!j) throw new Error(`bm25 query ${id} not found`);
565
+ _bm25CheckJ(j.status, BM25_QUERY_LIFECYCLE_V2.COMPLETED);
566
+ const now = Date.now();
567
+ j.status = BM25_QUERY_LIFECYCLE_V2.COMPLETED;
568
+ j.updatedAt = now;
569
+ if (!j.settledAt) j.settledAt = now;
570
+ return { ...j, metadata: { ...j.metadata } };
571
+ }
572
+ export function failBm25QueryV2(id, reason) {
573
+ const j = _bm25JsV2.get(id);
574
+ if (!j) throw new Error(`bm25 query ${id} not found`);
575
+ _bm25CheckJ(j.status, BM25_QUERY_LIFECYCLE_V2.FAILED);
576
+ const now = Date.now();
577
+ j.status = BM25_QUERY_LIFECYCLE_V2.FAILED;
578
+ j.updatedAt = now;
579
+ if (!j.settledAt) j.settledAt = now;
580
+ if (reason) j.metadata.failReason = String(reason);
581
+ return { ...j, metadata: { ...j.metadata } };
582
+ }
583
+ export function cancelBm25QueryV2(id, reason) {
584
+ const j = _bm25JsV2.get(id);
585
+ if (!j) throw new Error(`bm25 query ${id} not found`);
586
+ _bm25CheckJ(j.status, BM25_QUERY_LIFECYCLE_V2.CANCELLED);
587
+ const now = Date.now();
588
+ j.status = BM25_QUERY_LIFECYCLE_V2.CANCELLED;
589
+ j.updatedAt = now;
590
+ if (!j.settledAt) j.settledAt = now;
591
+ if (reason) j.metadata.cancelReason = String(reason);
592
+ return { ...j, metadata: { ...j.metadata } };
593
+ }
594
+ export function getBm25QueryV2(id) {
595
+ const j = _bm25JsV2.get(id);
596
+ if (!j) return null;
597
+ return { ...j, metadata: { ...j.metadata } };
598
+ }
599
+ export function listBm25QueriesV2() {
600
+ return [..._bm25JsV2.values()].map((j) => ({
601
+ ...j,
602
+ metadata: { ...j.metadata },
603
+ }));
604
+ }
605
+ export function autoStaleIdleBm25ProfilesV2({ now } = {}) {
606
+ const t = now ?? Date.now();
607
+ const flipped = [];
608
+ for (const p of _bm25PsV2.values())
609
+ if (
610
+ p.status === BM25_PROFILE_MATURITY_V2.ACTIVE &&
611
+ t - p.lastTouchedAt >= _bm25IdleMs
612
+ ) {
613
+ p.status = BM25_PROFILE_MATURITY_V2.STALE;
614
+ p.updatedAt = t;
615
+ flipped.push(p.id);
616
+ }
617
+ return { flipped, count: flipped.length };
618
+ }
619
+ export function autoFailStuckBm25QueriesV2({ now } = {}) {
620
+ const t = now ?? Date.now();
621
+ const flipped = [];
622
+ for (const j of _bm25JsV2.values())
623
+ if (
624
+ j.status === BM25_QUERY_LIFECYCLE_V2.SEARCHING &&
625
+ j.startedAt != null &&
626
+ t - j.startedAt >= _bm25StuckMs
627
+ ) {
628
+ j.status = BM25_QUERY_LIFECYCLE_V2.FAILED;
629
+ j.updatedAt = t;
630
+ if (!j.settledAt) j.settledAt = t;
631
+ j.metadata.failReason = "auto-fail-stuck";
632
+ flipped.push(j.id);
633
+ }
634
+ return { flipped, count: flipped.length };
635
+ }
636
+ export function getBm25SearchGovStatsV2() {
637
+ const profilesByStatus = {};
638
+ for (const v of Object.values(BM25_PROFILE_MATURITY_V2))
639
+ profilesByStatus[v] = 0;
640
+ for (const p of _bm25PsV2.values()) profilesByStatus[p.status]++;
641
+ const queriesByStatus = {};
642
+ for (const v of Object.values(BM25_QUERY_LIFECYCLE_V2))
643
+ queriesByStatus[v] = 0;
644
+ for (const j of _bm25JsV2.values()) queriesByStatus[j.status]++;
645
+ return {
646
+ totalBm25ProfilesV2: _bm25PsV2.size,
647
+ totalBm25QueriesV2: _bm25JsV2.size,
648
+ maxActiveBm25ProfilesPerOwner: _bm25MaxActive,
649
+ maxPendingBm25QueriesPerProfile: _bm25MaxPending,
650
+ bm25ProfileIdleMs: _bm25IdleMs,
651
+ bm25QueryStuckMs: _bm25StuckMs,
652
+ profilesByStatus,
653
+ queriesByStatus,
654
+ };
655
+ }
@@ -214,3 +214,355 @@ export async function takeScreenshot(url, outputPath, options = {}) {
214
214
  throw err;
215
215
  }
216
216
  }
217
+
218
+ // ===== V2 Surface: Browser Automation governance overlay (CLI v0.134.0) =====
219
+ export const BROWSE_TARGET_MATURITY_V2 = Object.freeze({
220
+ PENDING: "pending",
221
+ ACTIVE: "active",
222
+ DEGRADED: "degraded",
223
+ RETIRED: "retired",
224
+ });
225
+ export const BROWSE_ACTION_LIFECYCLE_V2 = Object.freeze({
226
+ QUEUED: "queued",
227
+ RUNNING: "running",
228
+ COMPLETED: "completed",
229
+ FAILED: "failed",
230
+ CANCELLED: "cancelled",
231
+ });
232
+
233
+ const _brTargTrans = new Map([
234
+ [
235
+ BROWSE_TARGET_MATURITY_V2.PENDING,
236
+ new Set([
237
+ BROWSE_TARGET_MATURITY_V2.ACTIVE,
238
+ BROWSE_TARGET_MATURITY_V2.RETIRED,
239
+ ]),
240
+ ],
241
+ [
242
+ BROWSE_TARGET_MATURITY_V2.ACTIVE,
243
+ new Set([
244
+ BROWSE_TARGET_MATURITY_V2.DEGRADED,
245
+ BROWSE_TARGET_MATURITY_V2.RETIRED,
246
+ ]),
247
+ ],
248
+ [
249
+ BROWSE_TARGET_MATURITY_V2.DEGRADED,
250
+ new Set([
251
+ BROWSE_TARGET_MATURITY_V2.ACTIVE,
252
+ BROWSE_TARGET_MATURITY_V2.RETIRED,
253
+ ]),
254
+ ],
255
+ [BROWSE_TARGET_MATURITY_V2.RETIRED, new Set()],
256
+ ]);
257
+ const _brTargTerminal = new Set([BROWSE_TARGET_MATURITY_V2.RETIRED]);
258
+ const _brActTrans = new Map([
259
+ [
260
+ BROWSE_ACTION_LIFECYCLE_V2.QUEUED,
261
+ new Set([
262
+ BROWSE_ACTION_LIFECYCLE_V2.RUNNING,
263
+ BROWSE_ACTION_LIFECYCLE_V2.CANCELLED,
264
+ ]),
265
+ ],
266
+ [
267
+ BROWSE_ACTION_LIFECYCLE_V2.RUNNING,
268
+ new Set([
269
+ BROWSE_ACTION_LIFECYCLE_V2.COMPLETED,
270
+ BROWSE_ACTION_LIFECYCLE_V2.FAILED,
271
+ BROWSE_ACTION_LIFECYCLE_V2.CANCELLED,
272
+ ]),
273
+ ],
274
+ [BROWSE_ACTION_LIFECYCLE_V2.COMPLETED, new Set()],
275
+ [BROWSE_ACTION_LIFECYCLE_V2.FAILED, new Set()],
276
+ [BROWSE_ACTION_LIFECYCLE_V2.CANCELLED, new Set()],
277
+ ]);
278
+
279
+ const _brTargets = new Map();
280
+ const _brActions = new Map();
281
+ let _brMaxActivePerOwner = 8;
282
+ let _brMaxPendingPerTarget = 20;
283
+ let _brTargetIdleMs = 12 * 60 * 60 * 1000;
284
+ let _brActionStuckMs = 3 * 60 * 1000;
285
+
286
+ function _brPos(n, lbl) {
287
+ const v = Math.floor(Number(n));
288
+ if (!Number.isFinite(v) || v <= 0)
289
+ throw new Error(`${lbl} must be positive integer`);
290
+ return v;
291
+ }
292
+
293
+ export function setMaxActiveBrowseTargetsPerOwnerV2(n) {
294
+ _brMaxActivePerOwner = _brPos(n, "maxActiveBrowseTargetsPerOwner");
295
+ }
296
+ export function getMaxActiveBrowseTargetsPerOwnerV2() {
297
+ return _brMaxActivePerOwner;
298
+ }
299
+ export function setMaxPendingBrowseActionsPerTargetV2(n) {
300
+ _brMaxPendingPerTarget = _brPos(n, "maxPendingBrowseActionsPerTarget");
301
+ }
302
+ export function getMaxPendingBrowseActionsPerTargetV2() {
303
+ return _brMaxPendingPerTarget;
304
+ }
305
+ export function setBrowseTargetIdleMsV2(n) {
306
+ _brTargetIdleMs = _brPos(n, "browseTargetIdleMs");
307
+ }
308
+ export function getBrowseTargetIdleMsV2() {
309
+ return _brTargetIdleMs;
310
+ }
311
+ export function setBrowseActionStuckMsV2(n) {
312
+ _brActionStuckMs = _brPos(n, "browseActionStuckMs");
313
+ }
314
+ export function getBrowseActionStuckMsV2() {
315
+ return _brActionStuckMs;
316
+ }
317
+
318
+ export function _resetStateBrowserAutomationV2() {
319
+ _brTargets.clear();
320
+ _brActions.clear();
321
+ _brMaxActivePerOwner = 8;
322
+ _brMaxPendingPerTarget = 20;
323
+ _brTargetIdleMs = 12 * 60 * 60 * 1000;
324
+ _brActionStuckMs = 3 * 60 * 1000;
325
+ }
326
+
327
+ export function registerBrowseTargetV2({ id, owner, url, metadata } = {}) {
328
+ if (!id || typeof id !== "string") throw new Error("id is required");
329
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
330
+ if (_brTargets.has(id))
331
+ throw new Error(`browse target ${id} already registered`);
332
+ const now = Date.now();
333
+ const t = {
334
+ id,
335
+ owner,
336
+ url: url || "",
337
+ status: BROWSE_TARGET_MATURITY_V2.PENDING,
338
+ createdAt: now,
339
+ updatedAt: now,
340
+ activatedAt: null,
341
+ retiredAt: null,
342
+ lastTouchedAt: now,
343
+ metadata: { ...(metadata || {}) },
344
+ };
345
+ _brTargets.set(id, t);
346
+ return { ...t, metadata: { ...t.metadata } };
347
+ }
348
+ function _brCheckT(from, to) {
349
+ const a = _brTargTrans.get(from);
350
+ if (!a || !a.has(to))
351
+ throw new Error(`invalid browse target transition ${from} → ${to}`);
352
+ }
353
+ function _brCountActive(owner) {
354
+ let n = 0;
355
+ for (const t of _brTargets.values())
356
+ if (t.owner === owner && t.status === BROWSE_TARGET_MATURITY_V2.ACTIVE) n++;
357
+ return n;
358
+ }
359
+
360
+ export function activateBrowseTargetV2(id) {
361
+ const t = _brTargets.get(id);
362
+ if (!t) throw new Error(`browse target ${id} not found`);
363
+ _brCheckT(t.status, BROWSE_TARGET_MATURITY_V2.ACTIVE);
364
+ const recovery = t.status === BROWSE_TARGET_MATURITY_V2.DEGRADED;
365
+ if (!recovery) {
366
+ const a = _brCountActive(t.owner);
367
+ if (a >= _brMaxActivePerOwner)
368
+ throw new Error(
369
+ `max active browse targets per owner (${_brMaxActivePerOwner}) reached for ${t.owner}`,
370
+ );
371
+ }
372
+ const now = Date.now();
373
+ t.status = BROWSE_TARGET_MATURITY_V2.ACTIVE;
374
+ t.updatedAt = now;
375
+ t.lastTouchedAt = now;
376
+ if (!t.activatedAt) t.activatedAt = now;
377
+ return { ...t, metadata: { ...t.metadata } };
378
+ }
379
+ export function degradeBrowseTargetV2(id) {
380
+ const t = _brTargets.get(id);
381
+ if (!t) throw new Error(`browse target ${id} not found`);
382
+ _brCheckT(t.status, BROWSE_TARGET_MATURITY_V2.DEGRADED);
383
+ t.status = BROWSE_TARGET_MATURITY_V2.DEGRADED;
384
+ t.updatedAt = Date.now();
385
+ return { ...t, metadata: { ...t.metadata } };
386
+ }
387
+ export function retireBrowseTargetV2(id) {
388
+ const t = _brTargets.get(id);
389
+ if (!t) throw new Error(`browse target ${id} not found`);
390
+ _brCheckT(t.status, BROWSE_TARGET_MATURITY_V2.RETIRED);
391
+ const now = Date.now();
392
+ t.status = BROWSE_TARGET_MATURITY_V2.RETIRED;
393
+ t.updatedAt = now;
394
+ if (!t.retiredAt) t.retiredAt = now;
395
+ return { ...t, metadata: { ...t.metadata } };
396
+ }
397
+ export function touchBrowseTargetV2(id) {
398
+ const t = _brTargets.get(id);
399
+ if (!t) throw new Error(`browse target ${id} not found`);
400
+ if (_brTargTerminal.has(t.status))
401
+ throw new Error(`cannot touch terminal browse target ${id}`);
402
+ const now = Date.now();
403
+ t.lastTouchedAt = now;
404
+ t.updatedAt = now;
405
+ return { ...t, metadata: { ...t.metadata } };
406
+ }
407
+ export function getBrowseTargetV2(id) {
408
+ const t = _brTargets.get(id);
409
+ if (!t) return null;
410
+ return { ...t, metadata: { ...t.metadata } };
411
+ }
412
+ export function listBrowseTargetsV2() {
413
+ return [..._brTargets.values()].map((t) => ({
414
+ ...t,
415
+ metadata: { ...t.metadata },
416
+ }));
417
+ }
418
+
419
+ function _brCountPending(tid) {
420
+ let n = 0;
421
+ for (const a of _brActions.values())
422
+ if (
423
+ a.targetId === tid &&
424
+ (a.status === BROWSE_ACTION_LIFECYCLE_V2.QUEUED ||
425
+ a.status === BROWSE_ACTION_LIFECYCLE_V2.RUNNING)
426
+ )
427
+ n++;
428
+ return n;
429
+ }
430
+
431
+ export function createBrowseActionV2({ id, targetId, kind, metadata } = {}) {
432
+ if (!id || typeof id !== "string") throw new Error("id is required");
433
+ if (!targetId || typeof targetId !== "string")
434
+ throw new Error("targetId is required");
435
+ if (_brActions.has(id)) throw new Error(`browse action ${id} already exists`);
436
+ if (!_brTargets.has(targetId))
437
+ throw new Error(`browse target ${targetId} not found`);
438
+ const pending = _brCountPending(targetId);
439
+ if (pending >= _brMaxPendingPerTarget)
440
+ throw new Error(
441
+ `max pending browse actions per target (${_brMaxPendingPerTarget}) reached for ${targetId}`,
442
+ );
443
+ const now = Date.now();
444
+ const a = {
445
+ id,
446
+ targetId,
447
+ kind: kind || "fetch",
448
+ status: BROWSE_ACTION_LIFECYCLE_V2.QUEUED,
449
+ createdAt: now,
450
+ updatedAt: now,
451
+ startedAt: null,
452
+ settledAt: null,
453
+ metadata: { ...(metadata || {}) },
454
+ };
455
+ _brActions.set(id, a);
456
+ return { ...a, metadata: { ...a.metadata } };
457
+ }
458
+ function _brCheckA(from, to) {
459
+ const al = _brActTrans.get(from);
460
+ if (!al || !al.has(to))
461
+ throw new Error(`invalid browse action transition ${from} → ${to}`);
462
+ }
463
+ export function startBrowseActionV2(id) {
464
+ const a = _brActions.get(id);
465
+ if (!a) throw new Error(`browse action ${id} not found`);
466
+ _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.RUNNING);
467
+ const now = Date.now();
468
+ a.status = BROWSE_ACTION_LIFECYCLE_V2.RUNNING;
469
+ a.updatedAt = now;
470
+ if (!a.startedAt) a.startedAt = now;
471
+ return { ...a, metadata: { ...a.metadata } };
472
+ }
473
+ export function completeBrowseActionV2(id) {
474
+ const a = _brActions.get(id);
475
+ if (!a) throw new Error(`browse action ${id} not found`);
476
+ _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.COMPLETED);
477
+ const now = Date.now();
478
+ a.status = BROWSE_ACTION_LIFECYCLE_V2.COMPLETED;
479
+ a.updatedAt = now;
480
+ if (!a.settledAt) a.settledAt = now;
481
+ return { ...a, metadata: { ...a.metadata } };
482
+ }
483
+ export function failBrowseActionV2(id, reason) {
484
+ const a = _brActions.get(id);
485
+ if (!a) throw new Error(`browse action ${id} not found`);
486
+ _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.FAILED);
487
+ const now = Date.now();
488
+ a.status = BROWSE_ACTION_LIFECYCLE_V2.FAILED;
489
+ a.updatedAt = now;
490
+ if (!a.settledAt) a.settledAt = now;
491
+ if (reason) a.metadata.failReason = String(reason);
492
+ return { ...a, metadata: { ...a.metadata } };
493
+ }
494
+ export function cancelBrowseActionV2(id, reason) {
495
+ const a = _brActions.get(id);
496
+ if (!a) throw new Error(`browse action ${id} not found`);
497
+ _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.CANCELLED);
498
+ const now = Date.now();
499
+ a.status = BROWSE_ACTION_LIFECYCLE_V2.CANCELLED;
500
+ a.updatedAt = now;
501
+ if (!a.settledAt) a.settledAt = now;
502
+ if (reason) a.metadata.cancelReason = String(reason);
503
+ return { ...a, metadata: { ...a.metadata } };
504
+ }
505
+ export function getBrowseActionV2(id) {
506
+ const a = _brActions.get(id);
507
+ if (!a) return null;
508
+ return { ...a, metadata: { ...a.metadata } };
509
+ }
510
+ export function listBrowseActionsV2() {
511
+ return [..._brActions.values()].map((a) => ({
512
+ ...a,
513
+ metadata: { ...a.metadata },
514
+ }));
515
+ }
516
+
517
+ export function autoDegradeIdleBrowseTargetsV2({ now } = {}) {
518
+ const t = now ?? Date.now();
519
+ const flipped = [];
520
+ for (const tg of _brTargets.values())
521
+ if (
522
+ tg.status === BROWSE_TARGET_MATURITY_V2.ACTIVE &&
523
+ t - tg.lastTouchedAt >= _brTargetIdleMs
524
+ ) {
525
+ tg.status = BROWSE_TARGET_MATURITY_V2.DEGRADED;
526
+ tg.updatedAt = t;
527
+ flipped.push(tg.id);
528
+ }
529
+ return { flipped, count: flipped.length };
530
+ }
531
+ export function autoFailStuckBrowseActionsV2({ now } = {}) {
532
+ const t = now ?? Date.now();
533
+ const flipped = [];
534
+ for (const a of _brActions.values())
535
+ if (
536
+ a.status === BROWSE_ACTION_LIFECYCLE_V2.RUNNING &&
537
+ a.startedAt != null &&
538
+ t - a.startedAt >= _brActionStuckMs
539
+ ) {
540
+ a.status = BROWSE_ACTION_LIFECYCLE_V2.FAILED;
541
+ a.updatedAt = t;
542
+ if (!a.settledAt) a.settledAt = t;
543
+ a.metadata.failReason = "auto-fail-stuck";
544
+ flipped.push(a.id);
545
+ }
546
+ return { flipped, count: flipped.length };
547
+ }
548
+
549
+ export function getBrowserAutomationStatsV2() {
550
+ const targetsByStatus = {};
551
+ for (const s of Object.values(BROWSE_TARGET_MATURITY_V2))
552
+ targetsByStatus[s] = 0;
553
+ for (const t of _brTargets.values()) targetsByStatus[t.status]++;
554
+ const actionsByStatus = {};
555
+ for (const s of Object.values(BROWSE_ACTION_LIFECYCLE_V2))
556
+ actionsByStatus[s] = 0;
557
+ for (const a of _brActions.values()) actionsByStatus[a.status]++;
558
+ return {
559
+ totalTargetsV2: _brTargets.size,
560
+ totalActionsV2: _brActions.size,
561
+ maxActiveBrowseTargetsPerOwner: _brMaxActivePerOwner,
562
+ maxPendingBrowseActionsPerTarget: _brMaxPendingPerTarget,
563
+ browseTargetIdleMs: _brTargetIdleMs,
564
+ browseActionStuckMs: _brActionStuckMs,
565
+ targetsByStatus,
566
+ actionsByStatus,
567
+ };
568
+ }