chainlesschain 0.81.0 → 0.143.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 (209) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +62 -0
  4. package/src/commands/activitypub.js +61 -0
  5. package/src/commands/agent-network.js +254 -1
  6. package/src/commands/agent.js +117 -0
  7. package/src/commands/audit.js +302 -0
  8. package/src/commands/automation.js +271 -1
  9. package/src/commands/bi.js +61 -0
  10. package/src/commands/bm25.js +78 -0
  11. package/src/commands/browse.js +64 -0
  12. package/src/commands/ccron.js +78 -0
  13. package/src/commands/codegen.js +224 -0
  14. package/src/commands/collab.js +341 -0
  15. package/src/commands/compliance.js +1075 -0
  16. package/src/commands/compt.js +78 -0
  17. package/src/commands/consol.js +231 -0
  18. package/src/commands/cowork.js +263 -0
  19. package/src/commands/crosschain.js +62 -0
  20. package/src/commands/dao.js +62 -0
  21. package/src/commands/dbevo.js +284 -0
  22. package/src/commands/dev.js +252 -0
  23. package/src/commands/did.js +358 -0
  24. package/src/commands/dlp.js +61 -0
  25. package/src/commands/economy.js +56 -0
  26. package/src/commands/encrypt.js +341 -0
  27. package/src/commands/evolution.js +56 -0
  28. package/src/commands/evomap.js +61 -0
  29. package/src/commands/export.js +256 -1
  30. package/src/commands/fflag.js +178 -0
  31. package/src/commands/fusion.js +258 -0
  32. package/src/commands/git.js +45 -0
  33. package/src/commands/governance.js +325 -0
  34. package/src/commands/hardening.js +411 -0
  35. package/src/commands/hmemory.js +56 -0
  36. package/src/commands/hook.js +148 -0
  37. package/src/commands/import.js +252 -0
  38. package/src/commands/incentive.js +322 -0
  39. package/src/commands/inference.js +42 -0
  40. package/src/commands/infra.js +244 -0
  41. package/src/commands/instinct.js +260 -0
  42. package/src/commands/ipfs.js +318 -0
  43. package/src/commands/itbudget.js +45 -0
  44. package/src/commands/kg.js +387 -0
  45. package/src/commands/llm.js +263 -0
  46. package/src/commands/lowcode.js +44 -0
  47. package/src/commands/matrix.js +62 -0
  48. package/src/commands/mcp.js +221 -0
  49. package/src/commands/mcpscaf.js +41 -0
  50. package/src/commands/meminj.js +41 -0
  51. package/src/commands/memory.js +248 -0
  52. package/src/commands/multimodal.js +296 -0
  53. package/src/commands/nlprog.js +356 -0
  54. package/src/commands/nostr.js +62 -0
  55. package/src/commands/note.js +244 -0
  56. package/src/commands/ops.js +354 -0
  57. package/src/commands/orchestrate.js +166 -0
  58. package/src/commands/orchgov.js +45 -0
  59. package/src/commands/org.js +277 -0
  60. package/src/commands/p2p.js +390 -0
  61. package/src/commands/pdfp.js +78 -0
  62. package/src/commands/perception.js +290 -0
  63. package/src/commands/perf.js +39 -0
  64. package/src/commands/perm.js +45 -0
  65. package/src/commands/permmem.js +251 -0
  66. package/src/commands/pipeline.js +57 -1
  67. package/src/commands/planmode.js +45 -0
  68. package/src/commands/plugin-ecosystem.js +273 -0
  69. package/src/commands/pqc.js +393 -0
  70. package/src/commands/promcomp.js +82 -0
  71. package/src/commands/quantization.js +351 -0
  72. package/src/commands/rcache.js +271 -0
  73. package/src/commands/recommend.js +382 -0
  74. package/src/commands/runtime.js +307 -0
  75. package/src/commands/scim.js +262 -0
  76. package/src/commands/seshhook.js +41 -0
  77. package/src/commands/seshsearch.js +41 -0
  78. package/src/commands/seshtail.js +41 -0
  79. package/src/commands/seshu.js +41 -0
  80. package/src/commands/session.js +258 -0
  81. package/src/commands/sganal.js +78 -0
  82. package/src/commands/siem.js +40 -0
  83. package/src/commands/skill.js +267 -1
  84. package/src/commands/slotfill.js +41 -0
  85. package/src/commands/social.js +290 -0
  86. package/src/commands/sso.js +186 -1
  87. package/src/commands/svccont.js +45 -0
  88. package/src/commands/sync.js +256 -0
  89. package/src/commands/tech.js +338 -0
  90. package/src/commands/tenant.js +351 -0
  91. package/src/commands/tms.js +45 -0
  92. package/src/commands/tokens.js +269 -0
  93. package/src/commands/topiccls.js +45 -0
  94. package/src/commands/trust.js +249 -0
  95. package/src/commands/uprof.js +45 -0
  96. package/src/commands/vcheck.js +78 -0
  97. package/src/commands/wallet.js +277 -0
  98. package/src/commands/webfetch.js +41 -0
  99. package/src/commands/workflow.js +171 -0
  100. package/src/commands/zkp.js +62 -0
  101. package/src/harness/prompt-compressor.js +331 -0
  102. package/src/index.js +65 -1
  103. package/src/lib/a2a-protocol.js +105 -0
  104. package/src/lib/activitypub-bridge.js +105 -0
  105. package/src/lib/agent-coordinator.js +325 -0
  106. package/src/lib/agent-economy.js +105 -0
  107. package/src/lib/agent-network.js +387 -0
  108. package/src/lib/agent-router.js +395 -0
  109. package/src/lib/aiops.js +478 -0
  110. package/src/lib/app-builder.js +105 -0
  111. package/src/lib/audit-logger.js +379 -0
  112. package/src/lib/automation-engine.js +330 -0
  113. package/src/lib/autonomous-agent.js +105 -0
  114. package/src/lib/autonomous-developer.js +350 -0
  115. package/src/lib/bi-engine.js +105 -0
  116. package/src/lib/bm25-search.js +81 -0
  117. package/src/lib/browser-automation.js +105 -0
  118. package/src/lib/code-agent.js +323 -0
  119. package/src/lib/collaboration-governance.js +364 -0
  120. package/src/lib/community-governance.js +436 -0
  121. package/src/lib/compliance-framework-reporter.js +105 -0
  122. package/src/lib/compliance-manager.js +434 -0
  123. package/src/lib/compression-telemetry.js +81 -0
  124. package/src/lib/content-recommendation.js +469 -0
  125. package/src/lib/content-recommender.js +105 -0
  126. package/src/lib/cowork-cron.js +81 -0
  127. package/src/lib/cowork-task-runner.js +105 -0
  128. package/src/lib/cross-chain.js +105 -0
  129. package/src/lib/crypto-manager.js +350 -0
  130. package/src/lib/dao-governance.js +105 -0
  131. package/src/lib/dbevo.js +338 -0
  132. package/src/lib/decentral-infra.js +340 -0
  133. package/src/lib/did-manager.js +367 -0
  134. package/src/lib/dlp-engine.js +105 -0
  135. package/src/lib/evolution-system.js +105 -0
  136. package/src/lib/evomap-manager.js +105 -0
  137. package/src/lib/execution-backend.js +105 -0
  138. package/src/lib/feature-flags.js +85 -0
  139. package/src/lib/git-integration.js +105 -0
  140. package/src/lib/hardening-manager.js +348 -0
  141. package/src/lib/hierarchical-memory.js +105 -0
  142. package/src/lib/hook-manager.js +380 -0
  143. package/src/lib/inference-network.js +105 -0
  144. package/src/lib/instinct-manager.js +332 -0
  145. package/src/lib/ipfs-storage.js +334 -0
  146. package/src/lib/iteration-budget.js +105 -0
  147. package/src/lib/knowledge-exporter.js +381 -0
  148. package/src/lib/knowledge-graph.js +432 -0
  149. package/src/lib/knowledge-importer.js +379 -0
  150. package/src/lib/llm-providers.js +391 -0
  151. package/src/lib/matrix-bridge.js +105 -0
  152. package/src/lib/mcp-registry.js +333 -0
  153. package/src/lib/mcp-scaffold.js +81 -0
  154. package/src/lib/memory-injection.js +81 -0
  155. package/src/lib/memory-manager.js +330 -0
  156. package/src/lib/multimodal.js +346 -0
  157. package/src/lib/nl-programming.js +343 -0
  158. package/src/lib/nostr-bridge.js +105 -0
  159. package/src/lib/note-versioning.js +327 -0
  160. package/src/lib/orchestrator.js +105 -0
  161. package/src/lib/org-manager.js +323 -0
  162. package/src/lib/p2p-manager.js +387 -0
  163. package/src/lib/pdf-parser.js +81 -0
  164. package/src/lib/perception.js +346 -0
  165. package/src/lib/perf-tuning.js +109 -1
  166. package/src/lib/permanent-memory.js +320 -0
  167. package/src/lib/permission-engine.js +81 -0
  168. package/src/lib/pipeline-orchestrator.js +105 -0
  169. package/src/lib/plan-mode.js +81 -0
  170. package/src/lib/plugin-ecosystem.js +377 -0
  171. package/src/lib/pqc-manager.js +368 -0
  172. package/src/lib/prompt-compressor.js +1 -10
  173. package/src/lib/protocol-fusion.js +417 -0
  174. package/src/lib/quantization.js +325 -0
  175. package/src/lib/response-cache.js +327 -0
  176. package/src/lib/scim-manager.js +329 -0
  177. package/src/lib/service-container.js +81 -0
  178. package/src/lib/session-consolidator.js +105 -0
  179. package/src/lib/session-hooks.js +81 -0
  180. package/src/lib/session-manager.js +329 -0
  181. package/src/lib/session-search.js +81 -0
  182. package/src/lib/session-tail.js +81 -0
  183. package/src/lib/session-usage.js +83 -0
  184. package/src/lib/siem-exporter.js +105 -0
  185. package/src/lib/skill-loader.js +377 -0
  186. package/src/lib/slot-filler.js +81 -0
  187. package/src/lib/social-graph-analytics.js +81 -0
  188. package/src/lib/social-graph.js +81 -0
  189. package/src/lib/social-manager.js +326 -0
  190. package/src/lib/sso-manager.js +332 -0
  191. package/src/lib/sub-agent-registry.js +110 -0
  192. package/src/lib/sync-manager.js +326 -0
  193. package/src/lib/task-model-selector.js +81 -0
  194. package/src/lib/tech-learning-engine.js +369 -0
  195. package/src/lib/tenant-saas.js +460 -0
  196. package/src/lib/threat-intel.js +335 -0
  197. package/src/lib/todo-manager.js +105 -0
  198. package/src/lib/token-incentive.js +293 -0
  199. package/src/lib/token-tracker.js +329 -0
  200. package/src/lib/topic-classifier.js +105 -0
  201. package/src/lib/trust-security.js +390 -0
  202. package/src/lib/ueba.js +389 -0
  203. package/src/lib/universal-runtime.js +325 -0
  204. package/src/lib/user-profile.js +81 -0
  205. package/src/lib/version-checker.js +81 -0
  206. package/src/lib/wallet-manager.js +326 -0
  207. package/src/lib/web-fetch.js +81 -0
  208. package/src/lib/workflow-engine.js +322 -0
  209. package/src/lib/zkp-engine.js +105 -0
@@ -368,3 +368,323 @@ export class CLIPermanentMemory {
368
368
  }
369
369
  }
370
370
  }
371
+
372
+ /* ═══════════════════════════════════════════════════════════════
373
+ * V2 Surface — Permanent memory governance layer.
374
+ * Tracks per-owner pin maturity + per-pin retention-job lifecycle
375
+ * independent of file/SQLite/BM25 layers.
376
+ * ═══════════════════════════════════════════════════════════════ */
377
+
378
+ export const PIN_MATURITY_V2 = Object.freeze({
379
+ PENDING: "pending",
380
+ ACTIVE: "active",
381
+ DORMANT: "dormant",
382
+ ARCHIVED: "archived",
383
+ });
384
+
385
+ export const RETENTION_JOB_LIFECYCLE_V2 = Object.freeze({
386
+ QUEUED: "queued",
387
+ RUNNING: "running",
388
+ COMPLETED: "completed",
389
+ FAILED: "failed",
390
+ CANCELLED: "cancelled",
391
+ });
392
+
393
+ const PIN_TRANSITIONS_V2 = new Map([
394
+ ["pending", new Set(["active", "archived"])],
395
+ ["active", new Set(["dormant", "archived"])],
396
+ ["dormant", new Set(["active", "archived"])],
397
+ ["archived", new Set()],
398
+ ]);
399
+ const PIN_TERMINALS_V2 = new Set(["archived"]);
400
+
401
+ const JOB_TRANSITIONS_V2 = new Map([
402
+ ["queued", new Set(["running", "cancelled"])],
403
+ ["running", new Set(["completed", "failed", "cancelled"])],
404
+ ["completed", new Set()],
405
+ ["failed", new Set()],
406
+ ["cancelled", new Set()],
407
+ ]);
408
+ const JOB_TERMINALS_V2 = new Set(["completed", "failed", "cancelled"]);
409
+
410
+ export const PERMMEM_DEFAULT_MAX_ACTIVE_PINS_PER_OWNER = 500;
411
+ export const PERMMEM_DEFAULT_MAX_PENDING_JOBS_PER_PIN = 2;
412
+ export const PERMMEM_DEFAULT_PIN_IDLE_MS = 1000 * 60 * 60 * 24 * 90; // 90 days
413
+ export const PERMMEM_DEFAULT_JOB_STUCK_MS = 1000 * 60 * 15; // 15 min
414
+
415
+ const _pinsV2 = new Map();
416
+ const _jobsV2 = new Map();
417
+ let _maxActivePinsPerOwnerV2 = PERMMEM_DEFAULT_MAX_ACTIVE_PINS_PER_OWNER;
418
+ let _maxPendingJobsPerPinV2 = PERMMEM_DEFAULT_MAX_PENDING_JOBS_PER_PIN;
419
+ let _pinIdleMsV2 = PERMMEM_DEFAULT_PIN_IDLE_MS;
420
+ let _jobStuckMsV2 = PERMMEM_DEFAULT_JOB_STUCK_MS;
421
+
422
+ function _posIntPermMemV2(n, label) {
423
+ const v = Math.floor(Number(n));
424
+ if (!Number.isFinite(v) || v <= 0)
425
+ throw new Error(`${label} must be a positive integer`);
426
+ return v;
427
+ }
428
+
429
+ export function getMaxActivePinsPerOwnerV2() {
430
+ return _maxActivePinsPerOwnerV2;
431
+ }
432
+ export function setMaxActivePinsPerOwnerV2(n) {
433
+ _maxActivePinsPerOwnerV2 = _posIntPermMemV2(n, "maxActivePinsPerOwner");
434
+ }
435
+ export function getMaxPendingJobsPerPinV2() {
436
+ return _maxPendingJobsPerPinV2;
437
+ }
438
+ export function setMaxPendingJobsPerPinV2(n) {
439
+ _maxPendingJobsPerPinV2 = _posIntPermMemV2(n, "maxPendingJobsPerPin");
440
+ }
441
+ export function getPinIdleMsV2() {
442
+ return _pinIdleMsV2;
443
+ }
444
+ export function setPinIdleMsV2(n) {
445
+ _pinIdleMsV2 = _posIntPermMemV2(n, "pinIdleMs");
446
+ }
447
+ export function getJobStuckMsV2() {
448
+ return _jobStuckMsV2;
449
+ }
450
+ export function setJobStuckMsV2(n) {
451
+ _jobStuckMsV2 = _posIntPermMemV2(n, "jobStuckMs");
452
+ }
453
+
454
+ export function getActivePinCountV2(ownerId) {
455
+ let n = 0;
456
+ for (const p of _pinsV2.values()) {
457
+ if (p.ownerId === ownerId && p.status === "active") n += 1;
458
+ }
459
+ return n;
460
+ }
461
+
462
+ export function getPendingJobCountV2(pinId) {
463
+ let n = 0;
464
+ for (const j of _jobsV2.values()) {
465
+ if (j.pinId === pinId && (j.status === "queued" || j.status === "running"))
466
+ n += 1;
467
+ }
468
+ return n;
469
+ }
470
+
471
+ function _copyPinV2(p) {
472
+ return { ...p, metadata: { ...p.metadata } };
473
+ }
474
+ function _copyJobV2(j) {
475
+ return { ...j, metadata: { ...j.metadata } };
476
+ }
477
+
478
+ export function registerPinV2(
479
+ id,
480
+ { ownerId, label, metadata = {}, now = Date.now() } = {},
481
+ ) {
482
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
483
+ if (!ownerId || typeof ownerId !== "string")
484
+ throw new Error("ownerId must be a string");
485
+ if (!label || typeof label !== "string")
486
+ throw new Error("label must be a string");
487
+ if (_pinsV2.has(id)) throw new Error(`pin ${id} already exists`);
488
+ const p = {
489
+ id,
490
+ ownerId,
491
+ label,
492
+ status: "pending",
493
+ createdAt: now,
494
+ lastSeenAt: now,
495
+ activatedAt: null,
496
+ archivedAt: null,
497
+ metadata: { ...metadata },
498
+ };
499
+ _pinsV2.set(id, p);
500
+ return _copyPinV2(p);
501
+ }
502
+
503
+ export function getPinV2(id) {
504
+ const p = _pinsV2.get(id);
505
+ return p ? _copyPinV2(p) : null;
506
+ }
507
+
508
+ export function listPinsV2({ ownerId, status } = {}) {
509
+ const out = [];
510
+ for (const p of _pinsV2.values()) {
511
+ if (ownerId && p.ownerId !== ownerId) continue;
512
+ if (status && p.status !== status) continue;
513
+ out.push(_copyPinV2(p));
514
+ }
515
+ return out;
516
+ }
517
+
518
+ export function setPinStatusV2(id, next, { now = Date.now() } = {}) {
519
+ const p = _pinsV2.get(id);
520
+ if (!p) throw new Error(`pin ${id} not found`);
521
+ if (!PIN_TRANSITIONS_V2.has(next))
522
+ throw new Error(`unknown pin status: ${next}`);
523
+ if (PIN_TERMINALS_V2.has(p.status))
524
+ throw new Error(`pin ${id} is in terminal state ${p.status}`);
525
+ const allowed = PIN_TRANSITIONS_V2.get(p.status);
526
+ if (!allowed.has(next))
527
+ throw new Error(`cannot transition pin from ${p.status} to ${next}`);
528
+ if (next === "active") {
529
+ if (p.status === "pending") {
530
+ const count = getActivePinCountV2(p.ownerId);
531
+ if (count >= _maxActivePinsPerOwnerV2)
532
+ throw new Error(
533
+ `owner ${p.ownerId} already at active-pin cap (${_maxActivePinsPerOwnerV2})`,
534
+ );
535
+ }
536
+ if (!p.activatedAt) p.activatedAt = now;
537
+ }
538
+ if (next === "archived" && !p.archivedAt) p.archivedAt = now;
539
+ p.status = next;
540
+ p.lastSeenAt = now;
541
+ return _copyPinV2(p);
542
+ }
543
+
544
+ export function activatePinV2(id, opts) {
545
+ return setPinStatusV2(id, "active", opts);
546
+ }
547
+ export function dormantPinV2(id, opts) {
548
+ return setPinStatusV2(id, "dormant", opts);
549
+ }
550
+ export function archivePinV2(id, opts) {
551
+ return setPinStatusV2(id, "archived", opts);
552
+ }
553
+
554
+ export function touchPinV2(id, { now = Date.now() } = {}) {
555
+ const p = _pinsV2.get(id);
556
+ if (!p) throw new Error(`pin ${id} not found`);
557
+ p.lastSeenAt = now;
558
+ return _copyPinV2(p);
559
+ }
560
+
561
+ export function createRetentionJobV2(
562
+ id,
563
+ { pinId, kind = "review", metadata = {}, now = Date.now() } = {},
564
+ ) {
565
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
566
+ if (!pinId || typeof pinId !== "string")
567
+ throw new Error("pinId must be a string");
568
+ if (_jobsV2.has(id)) throw new Error(`job ${id} already exists`);
569
+ const count = getPendingJobCountV2(pinId);
570
+ if (count >= _maxPendingJobsPerPinV2)
571
+ throw new Error(
572
+ `pin ${pinId} already at pending-job cap (${_maxPendingJobsPerPinV2})`,
573
+ );
574
+ const j = {
575
+ id,
576
+ pinId,
577
+ kind,
578
+ status: "queued",
579
+ createdAt: now,
580
+ lastSeenAt: now,
581
+ startedAt: null,
582
+ settledAt: null,
583
+ metadata: { ...metadata },
584
+ };
585
+ _jobsV2.set(id, j);
586
+ return _copyJobV2(j);
587
+ }
588
+
589
+ export function getRetentionJobV2(id) {
590
+ const j = _jobsV2.get(id);
591
+ return j ? _copyJobV2(j) : null;
592
+ }
593
+
594
+ export function listRetentionJobsV2({ pinId, status } = {}) {
595
+ const out = [];
596
+ for (const j of _jobsV2.values()) {
597
+ if (pinId && j.pinId !== pinId) continue;
598
+ if (status && j.status !== status) continue;
599
+ out.push(_copyJobV2(j));
600
+ }
601
+ return out;
602
+ }
603
+
604
+ export function setRetentionJobStatusV2(id, next, { now = Date.now() } = {}) {
605
+ const j = _jobsV2.get(id);
606
+ if (!j) throw new Error(`job ${id} not found`);
607
+ if (!JOB_TRANSITIONS_V2.has(next))
608
+ throw new Error(`unknown job status: ${next}`);
609
+ if (JOB_TERMINALS_V2.has(j.status))
610
+ throw new Error(`job ${id} is in terminal state ${j.status}`);
611
+ const allowed = JOB_TRANSITIONS_V2.get(j.status);
612
+ if (!allowed.has(next))
613
+ throw new Error(`cannot transition job from ${j.status} to ${next}`);
614
+ if (next === "running" && !j.startedAt) j.startedAt = now;
615
+ if (JOB_TERMINALS_V2.has(next) && !j.settledAt) j.settledAt = now;
616
+ j.status = next;
617
+ j.lastSeenAt = now;
618
+ return _copyJobV2(j);
619
+ }
620
+
621
+ export function startRetentionJobV2(id, opts) {
622
+ return setRetentionJobStatusV2(id, "running", opts);
623
+ }
624
+ export function completeRetentionJobV2(id, opts) {
625
+ return setRetentionJobStatusV2(id, "completed", opts);
626
+ }
627
+ export function failRetentionJobV2(id, opts) {
628
+ return setRetentionJobStatusV2(id, "failed", opts);
629
+ }
630
+ export function cancelRetentionJobV2(id, opts) {
631
+ return setRetentionJobStatusV2(id, "cancelled", opts);
632
+ }
633
+
634
+ export function autoDormantIdlePinsV2({ now = Date.now() } = {}) {
635
+ const flipped = [];
636
+ for (const p of _pinsV2.values()) {
637
+ if (p.status !== "active") continue;
638
+ if (now - p.lastSeenAt > _pinIdleMsV2) {
639
+ p.status = "dormant";
640
+ p.lastSeenAt = now;
641
+ flipped.push(_copyPinV2(p));
642
+ }
643
+ }
644
+ return flipped;
645
+ }
646
+
647
+ export function autoFailStuckJobsV2({ now = Date.now() } = {}) {
648
+ const flipped = [];
649
+ for (const j of _jobsV2.values()) {
650
+ if (j.status !== "running") continue;
651
+ if (now - j.lastSeenAt > _jobStuckMsV2) {
652
+ j.status = "failed";
653
+ j.lastSeenAt = now;
654
+ if (!j.settledAt) j.settledAt = now;
655
+ flipped.push(_copyJobV2(j));
656
+ }
657
+ }
658
+ return flipped;
659
+ }
660
+
661
+ export function getPermanentMemoryStatsV2() {
662
+ const pinsByStatus = {};
663
+ for (const v of Object.values(PIN_MATURITY_V2)) pinsByStatus[v] = 0;
664
+ for (const p of _pinsV2.values()) pinsByStatus[p.status] += 1;
665
+
666
+ const jobsByStatus = {};
667
+ for (const v of Object.values(RETENTION_JOB_LIFECYCLE_V2))
668
+ jobsByStatus[v] = 0;
669
+ for (const j of _jobsV2.values()) jobsByStatus[j.status] += 1;
670
+
671
+ return {
672
+ totalPinsV2: _pinsV2.size,
673
+ totalJobsV2: _jobsV2.size,
674
+ maxActivePinsPerOwner: _maxActivePinsPerOwnerV2,
675
+ maxPendingJobsPerPin: _maxPendingJobsPerPinV2,
676
+ pinIdleMs: _pinIdleMsV2,
677
+ jobStuckMs: _jobStuckMsV2,
678
+ pinsByStatus,
679
+ jobsByStatus,
680
+ };
681
+ }
682
+
683
+ export function _resetStatePermanentMemoryV2() {
684
+ _pinsV2.clear();
685
+ _jobsV2.clear();
686
+ _maxActivePinsPerOwnerV2 = PERMMEM_DEFAULT_MAX_ACTIVE_PINS_PER_OWNER;
687
+ _maxPendingJobsPerPinV2 = PERMMEM_DEFAULT_MAX_PENDING_JOBS_PER_PIN;
688
+ _pinIdleMsV2 = PERMMEM_DEFAULT_PIN_IDLE_MS;
689
+ _jobStuckMsV2 = PERMMEM_DEFAULT_JOB_STUCK_MS;
690
+ }
@@ -372,3 +372,84 @@ export function listUserRoles(db) {
372
372
 
373
373
  return [...userMap.entries()].map(([userDid, roles]) => ({ userDid, roles }));
374
374
  }
375
+
376
+ // ===== V2 Surface: Permission Engine governance overlay (CLI v0.141.0) =====
377
+ export const PERM_RULE_MATURITY_V2 = Object.freeze({
378
+ PENDING: "pending", ACTIVE: "active", DISABLED: "disabled", RETIRED: "retired",
379
+ });
380
+ export const PERM_CHECK_LIFECYCLE_V2 = Object.freeze({
381
+ QUEUED: "queued", EVALUATING: "evaluating", ALLOWED: "allowed", DENIED: "denied", CANCELLED: "cancelled",
382
+ });
383
+ const _permRTrans = new Map([
384
+ [PERM_RULE_MATURITY_V2.PENDING, new Set([PERM_RULE_MATURITY_V2.ACTIVE, PERM_RULE_MATURITY_V2.RETIRED])],
385
+ [PERM_RULE_MATURITY_V2.ACTIVE, new Set([PERM_RULE_MATURITY_V2.DISABLED, PERM_RULE_MATURITY_V2.RETIRED])],
386
+ [PERM_RULE_MATURITY_V2.DISABLED, new Set([PERM_RULE_MATURITY_V2.ACTIVE, PERM_RULE_MATURITY_V2.RETIRED])],
387
+ [PERM_RULE_MATURITY_V2.RETIRED, new Set()],
388
+ ]);
389
+ const _permRTerminal = new Set([PERM_RULE_MATURITY_V2.RETIRED]);
390
+ const _permCTrans = new Map([
391
+ [PERM_CHECK_LIFECYCLE_V2.QUEUED, new Set([PERM_CHECK_LIFECYCLE_V2.EVALUATING, PERM_CHECK_LIFECYCLE_V2.CANCELLED])],
392
+ [PERM_CHECK_LIFECYCLE_V2.EVALUATING, new Set([PERM_CHECK_LIFECYCLE_V2.ALLOWED, PERM_CHECK_LIFECYCLE_V2.DENIED, PERM_CHECK_LIFECYCLE_V2.CANCELLED])],
393
+ [PERM_CHECK_LIFECYCLE_V2.ALLOWED, new Set()],
394
+ [PERM_CHECK_LIFECYCLE_V2.DENIED, new Set()],
395
+ [PERM_CHECK_LIFECYCLE_V2.CANCELLED, new Set()],
396
+ ]);
397
+ const _permRsV2 = new Map();
398
+ const _permCsV2 = new Map();
399
+ let _permMaxActivePerOwner = 10, _permMaxPendingChecksPerRule = 30, _permIdleMs = 30 * 24 * 60 * 60 * 1000, _permStuckMs = 60 * 1000;
400
+ function _permPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
401
+ function _permCheckR(from, to) { const a = _permRTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid perm rule transition ${from} → ${to}`); }
402
+ function _permCheckC(from, to) { const a = _permCTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid perm check transition ${from} → ${to}`); }
403
+ export function setMaxActivePermRulesPerOwnerV2(n) { _permMaxActivePerOwner = _permPos(n, "maxActivePermRulesPerOwner"); }
404
+ export function getMaxActivePermRulesPerOwnerV2() { return _permMaxActivePerOwner; }
405
+ export function setMaxPendingPermChecksPerRuleV2(n) { _permMaxPendingChecksPerRule = _permPos(n, "maxPendingPermChecksPerRule"); }
406
+ export function getMaxPendingPermChecksPerRuleV2() { return _permMaxPendingChecksPerRule; }
407
+ export function setPermRuleIdleMsV2(n) { _permIdleMs = _permPos(n, "permRuleIdleMs"); }
408
+ export function getPermRuleIdleMsV2() { return _permIdleMs; }
409
+ export function setPermCheckStuckMsV2(n) { _permStuckMs = _permPos(n, "permCheckStuckMs"); }
410
+ export function getPermCheckStuckMsV2() { return _permStuckMs; }
411
+ export function _resetStatePermissionEngineV2() { _permRsV2.clear(); _permCsV2.clear(); _permMaxActivePerOwner = 10; _permMaxPendingChecksPerRule = 30; _permIdleMs = 30 * 24 * 60 * 60 * 1000; _permStuckMs = 60 * 1000; }
412
+ export function registerPermRuleV2({ id, owner, scope, metadata } = {}) {
413
+ if (!id) throw new Error("perm rule id required"); if (!owner) throw new Error("perm rule owner required");
414
+ if (_permRsV2.has(id)) throw new Error(`perm rule ${id} already registered`);
415
+ const now = Date.now();
416
+ const r = { id, owner, scope: scope || "*", status: PERM_RULE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
417
+ _permRsV2.set(id, r); return { ...r, metadata: { ...r.metadata } };
418
+ }
419
+ function _permCountActive(owner) { let n = 0; for (const r of _permRsV2.values()) if (r.owner === owner && r.status === PERM_RULE_MATURITY_V2.ACTIVE) n++; return n; }
420
+ export function activatePermRuleV2(id) {
421
+ const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`);
422
+ _permCheckR(r.status, PERM_RULE_MATURITY_V2.ACTIVE);
423
+ const recovery = r.status === PERM_RULE_MATURITY_V2.DISABLED;
424
+ if (!recovery && _permCountActive(r.owner) >= _permMaxActivePerOwner) throw new Error(`max active perm rules for owner ${r.owner} reached`);
425
+ const now = Date.now(); r.status = PERM_RULE_MATURITY_V2.ACTIVE; r.updatedAt = now; r.lastTouchedAt = now; if (!r.activatedAt) r.activatedAt = now;
426
+ return { ...r, metadata: { ...r.metadata } };
427
+ }
428
+ export function disablePermRuleV2(id) { const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`); _permCheckR(r.status, PERM_RULE_MATURITY_V2.DISABLED); r.status = PERM_RULE_MATURITY_V2.DISABLED; r.updatedAt = Date.now(); return { ...r, metadata: { ...r.metadata } }; }
429
+ export function retirePermRuleV2(id) { const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`); _permCheckR(r.status, PERM_RULE_MATURITY_V2.RETIRED); const now = Date.now(); r.status = PERM_RULE_MATURITY_V2.RETIRED; r.updatedAt = now; if (!r.retiredAt) r.retiredAt = now; return { ...r, metadata: { ...r.metadata } }; }
430
+ export function touchPermRuleV2(id) { const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`); if (_permRTerminal.has(r.status)) throw new Error(`cannot touch terminal perm rule ${id}`); const now = Date.now(); r.lastTouchedAt = now; r.updatedAt = now; return { ...r, metadata: { ...r.metadata } }; }
431
+ export function getPermRuleV2(id) { const r = _permRsV2.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
432
+ export function listPermRulesV2() { return [..._permRsV2.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
433
+ function _permCountPending(ruleId) { let n = 0; for (const c of _permCsV2.values()) if (c.ruleId === ruleId && (c.status === PERM_CHECK_LIFECYCLE_V2.QUEUED || c.status === PERM_CHECK_LIFECYCLE_V2.EVALUATING)) n++; return n; }
434
+ export function createPermCheckV2({ id, ruleId, subject, metadata } = {}) {
435
+ if (!id) throw new Error("perm check id required"); if (!ruleId) throw new Error("perm check ruleId required");
436
+ if (_permCsV2.has(id)) throw new Error(`perm check ${id} already exists`);
437
+ if (!_permRsV2.has(ruleId)) throw new Error(`perm rule ${ruleId} not found`);
438
+ if (_permCountPending(ruleId) >= _permMaxPendingChecksPerRule) throw new Error(`max pending perm checks for rule ${ruleId} reached`);
439
+ const now = Date.now();
440
+ const c = { id, ruleId, subject: subject || "", status: PERM_CHECK_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
441
+ _permCsV2.set(id, c); return { ...c, metadata: { ...c.metadata } };
442
+ }
443
+ export function evaluatePermCheckV2(id) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.EVALUATING); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.EVALUATING; c.updatedAt = now; if (!c.startedAt) c.startedAt = now; return { ...c, metadata: { ...c.metadata } }; }
444
+ export function allowPermCheckV2(id) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.ALLOWED); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.ALLOWED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; return { ...c, metadata: { ...c.metadata } }; }
445
+ export function denyPermCheckV2(id, reason) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.DENIED); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.DENIED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; if (reason) c.metadata.denyReason = String(reason); return { ...c, metadata: { ...c.metadata } }; }
446
+ export function cancelPermCheckV2(id, reason) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.CANCELLED); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.CANCELLED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; if (reason) c.metadata.cancelReason = String(reason); return { ...c, metadata: { ...c.metadata } }; }
447
+ export function getPermCheckV2(id) { const c = _permCsV2.get(id); if (!c) return null; return { ...c, metadata: { ...c.metadata } }; }
448
+ export function listPermChecksV2() { return [..._permCsV2.values()].map((c) => ({ ...c, metadata: { ...c.metadata } })); }
449
+ export function autoDisableIdlePermRulesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _permRsV2.values()) if (r.status === PERM_RULE_MATURITY_V2.ACTIVE && (t - r.lastTouchedAt) >= _permIdleMs) { r.status = PERM_RULE_MATURITY_V2.DISABLED; r.updatedAt = t; flipped.push(r.id); } return { flipped, count: flipped.length }; }
450
+ export function autoDenyStuckPermChecksV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const c of _permCsV2.values()) if (c.status === PERM_CHECK_LIFECYCLE_V2.EVALUATING && c.startedAt != null && (t - c.startedAt) >= _permStuckMs) { c.status = PERM_CHECK_LIFECYCLE_V2.DENIED; c.updatedAt = t; if (!c.settledAt) c.settledAt = t; c.metadata.denyReason = "auto-deny-stuck"; flipped.push(c.id); } return { flipped, count: flipped.length }; }
451
+ export function getPermissionEngineGovStatsV2() {
452
+ const rulesByStatus = {}; for (const v of Object.values(PERM_RULE_MATURITY_V2)) rulesByStatus[v] = 0; for (const r of _permRsV2.values()) rulesByStatus[r.status]++;
453
+ const checksByStatus = {}; for (const v of Object.values(PERM_CHECK_LIFECYCLE_V2)) checksByStatus[v] = 0; for (const c of _permCsV2.values()) checksByStatus[c.status]++;
454
+ return { totalPermRulesV2: _permRsV2.size, totalPermChecksV2: _permCsV2.size, maxActivePermRulesPerOwner: _permMaxActivePerOwner, maxPendingPermChecksPerRule: _permMaxPendingChecksPerRule, permRuleIdleMs: _permIdleMs, permCheckStuckMs: _permStuckMs, rulesByStatus, checksByStatus };
455
+ }
@@ -926,3 +926,108 @@ export function getStats(db) {
926
926
  deploysByStrategy: deployByStrategy,
927
927
  };
928
928
  }
929
+
930
+
931
+ // ===== V2 Surface: Pipeline Orchestrator governance overlay (CLI v0.137.0) =====
932
+ export const PIPELINE_MATURITY_V2 = Object.freeze({
933
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived",
934
+ });
935
+ export const PIPELINE_RUN_LIFECYCLE_V2 = Object.freeze({
936
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
937
+ });
938
+
939
+ const _pipTrans = new Map([
940
+ [PIPELINE_MATURITY_V2.PENDING, new Set([PIPELINE_MATURITY_V2.ACTIVE, PIPELINE_MATURITY_V2.ARCHIVED])],
941
+ [PIPELINE_MATURITY_V2.ACTIVE, new Set([PIPELINE_MATURITY_V2.PAUSED, PIPELINE_MATURITY_V2.ARCHIVED])],
942
+ [PIPELINE_MATURITY_V2.PAUSED, new Set([PIPELINE_MATURITY_V2.ACTIVE, PIPELINE_MATURITY_V2.ARCHIVED])],
943
+ [PIPELINE_MATURITY_V2.ARCHIVED, new Set()],
944
+ ]);
945
+ const _pipTerminal = new Set([PIPELINE_MATURITY_V2.ARCHIVED]);
946
+ const _pipRunTrans = new Map([
947
+ [PIPELINE_RUN_LIFECYCLE_V2.QUEUED, new Set([PIPELINE_RUN_LIFECYCLE_V2.RUNNING, PIPELINE_RUN_LIFECYCLE_V2.CANCELLED])],
948
+ [PIPELINE_RUN_LIFECYCLE_V2.RUNNING, new Set([PIPELINE_RUN_LIFECYCLE_V2.COMPLETED, PIPELINE_RUN_LIFECYCLE_V2.FAILED, PIPELINE_RUN_LIFECYCLE_V2.CANCELLED])],
949
+ [PIPELINE_RUN_LIFECYCLE_V2.COMPLETED, new Set()],
950
+ [PIPELINE_RUN_LIFECYCLE_V2.FAILED, new Set()],
951
+ [PIPELINE_RUN_LIFECYCLE_V2.CANCELLED, new Set()],
952
+ ]);
953
+
954
+ const _pips = new Map();
955
+ const _pipRuns = new Map();
956
+ let _pipMaxActivePerOwner = 10;
957
+ let _pipMaxPendingPerPip = 20;
958
+ let _pipIdleMs = 3 * 24 * 60 * 60 * 1000;
959
+ let _pipRunStuckMs = 10 * 60 * 1000;
960
+
961
+ function _pipPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
962
+
963
+ export function setMaxActivePipelinesPerOwnerV2(n) { _pipMaxActivePerOwner = _pipPos(n, "maxActivePipelinesPerOwner"); }
964
+ export function getMaxActivePipelinesPerOwnerV2() { return _pipMaxActivePerOwner; }
965
+ export function setMaxPendingPipelineRunsPerPipelineV2(n) { _pipMaxPendingPerPip = _pipPos(n, "maxPendingPipelineRunsPerPipeline"); }
966
+ export function getMaxPendingPipelineRunsPerPipelineV2() { return _pipMaxPendingPerPip; }
967
+ export function setPipelineIdleMsV2(n) { _pipIdleMs = _pipPos(n, "pipelineIdleMs"); }
968
+ export function getPipelineIdleMsV2() { return _pipIdleMs; }
969
+ export function setPipelineRunStuckMsV2(n) { _pipRunStuckMs = _pipPos(n, "pipelineRunStuckMs"); }
970
+ export function getPipelineRunStuckMsV2() { return _pipRunStuckMs; }
971
+
972
+ export function _resetStatePipelineOrchestratorV2() {
973
+ _pips.clear(); _pipRuns.clear();
974
+ _pipMaxActivePerOwner = 10; _pipMaxPendingPerPip = 20;
975
+ _pipIdleMs = 3 * 24 * 60 * 60 * 1000; _pipRunStuckMs = 10 * 60 * 1000;
976
+ }
977
+
978
+ export function registerPipelineV2({ id, owner, name, metadata } = {}) {
979
+ if (!id || typeof id !== "string") throw new Error("id is required");
980
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
981
+ if (_pips.has(id)) throw new Error(`pipeline ${id} already registered`);
982
+ const now = Date.now();
983
+ const p = { id, owner, name: name || id, status: PIPELINE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
984
+ _pips.set(id, p);
985
+ return { ...p, metadata: { ...p.metadata } };
986
+ }
987
+ function _pipCheckP(from, to) { const a = _pipTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid pipeline transition ${from} → ${to}`); }
988
+ function _pipCountActive(owner) { let n = 0; for (const p of _pips.values()) if (p.owner === owner && p.status === PIPELINE_MATURITY_V2.ACTIVE) n++; return n; }
989
+
990
+ export function activatePipelineV2(id) {
991
+ const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`);
992
+ _pipCheckP(p.status, PIPELINE_MATURITY_V2.ACTIVE);
993
+ const recovery = p.status === PIPELINE_MATURITY_V2.PAUSED;
994
+ if (!recovery) { const c = _pipCountActive(p.owner); if (c >= _pipMaxActivePerOwner) throw new Error(`max active pipelines per owner (${_pipMaxActivePerOwner}) reached for ${p.owner}`); }
995
+ const now = Date.now(); p.status = PIPELINE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
996
+ return { ...p, metadata: { ...p.metadata } };
997
+ }
998
+ export function pausePipelineV2(id) { const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`); _pipCheckP(p.status, PIPELINE_MATURITY_V2.PAUSED); p.status = PIPELINE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
999
+ export function archivePipelineV2(id) { const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`); _pipCheckP(p.status, PIPELINE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = PIPELINE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
1000
+ export function touchPipelineV2(id) { const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`); if (_pipTerminal.has(p.status)) throw new Error(`cannot touch terminal pipeline ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
1001
+ export function getPipelineV2(id) { const p = _pips.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
1002
+ export function listPipelinesV2() { return [..._pips.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
1003
+
1004
+ function _pipCountPendingRuns(pid) { let n = 0; for (const r of _pipRuns.values()) if (r.pipelineId === pid && (r.status === PIPELINE_RUN_LIFECYCLE_V2.QUEUED || r.status === PIPELINE_RUN_LIFECYCLE_V2.RUNNING)) n++; return n; }
1005
+
1006
+ export function createPipelineRunV2({ id, pipelineId, trigger, metadata } = {}) {
1007
+ if (!id || typeof id !== "string") throw new Error("id is required");
1008
+ if (!pipelineId || typeof pipelineId !== "string") throw new Error("pipelineId is required");
1009
+ if (_pipRuns.has(id)) throw new Error(`pipeline run ${id} already exists`);
1010
+ if (!_pips.has(pipelineId)) throw new Error(`pipeline ${pipelineId} not found`);
1011
+ const pending = _pipCountPendingRuns(pipelineId);
1012
+ if (pending >= _pipMaxPendingPerPip) throw new Error(`max pending pipeline runs per pipeline (${_pipMaxPendingPerPip}) reached for ${pipelineId}`);
1013
+ const now = Date.now();
1014
+ const r = { id, pipelineId, trigger: trigger || "manual", status: PIPELINE_RUN_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
1015
+ _pipRuns.set(id, r);
1016
+ return { ...r, metadata: { ...r.metadata } };
1017
+ }
1018
+ function _pipCheckR(from, to) { const a = _pipRunTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid pipeline run transition ${from} → ${to}`); }
1019
+ export function startPipelineRunV2(id) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.RUNNING); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.RUNNING; r.updatedAt = now; if (!r.startedAt) r.startedAt = now; return { ...r, metadata: { ...r.metadata } }; }
1020
+ export function completePipelineRunV2(id) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.COMPLETED); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.COMPLETED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; return { ...r, metadata: { ...r.metadata } }; }
1021
+ export function failPipelineRunV2(id, reason) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.FAILED); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.FAILED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.failReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
1022
+ export function cancelPipelineRunV2(id, reason) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.CANCELLED); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.CANCELLED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.cancelReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
1023
+ export function getPipelineRunV2(id) { const r = _pipRuns.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
1024
+ export function listPipelineRunsV2() { return [..._pipRuns.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
1025
+
1026
+ export function autoPauseIdlePipelinesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _pips.values()) if (p.status === PIPELINE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _pipIdleMs) { p.status = PIPELINE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
1027
+ export function autoFailStuckPipelineRunsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _pipRuns.values()) if (r.status === PIPELINE_RUN_LIFECYCLE_V2.RUNNING && r.startedAt != null && (t - r.startedAt) >= _pipRunStuckMs) { r.status = PIPELINE_RUN_LIFECYCLE_V2.FAILED; r.updatedAt = t; if (!r.settledAt) r.settledAt = t; r.metadata.failReason = "auto-fail-stuck"; flipped.push(r.id); } return { flipped, count: flipped.length }; }
1028
+
1029
+ export function getPipelineOrchestratorGovStatsV2() {
1030
+ const pipelinesByStatus = {}; for (const s of Object.values(PIPELINE_MATURITY_V2)) pipelinesByStatus[s] = 0; for (const p of _pips.values()) pipelinesByStatus[p.status]++;
1031
+ const runsByStatus = {}; for (const s of Object.values(PIPELINE_RUN_LIFECYCLE_V2)) runsByStatus[s] = 0; for (const r of _pipRuns.values()) runsByStatus[r.status]++;
1032
+ return { totalPipelinesV2: _pips.size, totalRunsV2: _pipRuns.size, maxActivePipelinesPerOwner: _pipMaxActivePerOwner, maxPendingPipelineRunsPerPipeline: _pipMaxPendingPerPip, pipelineIdleMs: _pipIdleMs, pipelineRunStuckMs: _pipRunStuckMs, pipelinesByStatus, runsByStatus };
1033
+ }
@@ -544,3 +544,84 @@ export function destroyPlanModeManager() {
544
544
  _instance = null;
545
545
  }
546
546
  }
547
+
548
+ // ===== V2 Surface: Plan Mode governance overlay (CLI v0.141.0) =====
549
+ export const PLAN_PROFILE_MATURITY_V2 = Object.freeze({
550
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived",
551
+ });
552
+ export const PLAN_STEP_LIFECYCLE_V2 = Object.freeze({
553
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
554
+ });
555
+ const _planPTrans = new Map([
556
+ [PLAN_PROFILE_MATURITY_V2.PENDING, new Set([PLAN_PROFILE_MATURITY_V2.ACTIVE, PLAN_PROFILE_MATURITY_V2.ARCHIVED])],
557
+ [PLAN_PROFILE_MATURITY_V2.ACTIVE, new Set([PLAN_PROFILE_MATURITY_V2.PAUSED, PLAN_PROFILE_MATURITY_V2.ARCHIVED])],
558
+ [PLAN_PROFILE_MATURITY_V2.PAUSED, new Set([PLAN_PROFILE_MATURITY_V2.ACTIVE, PLAN_PROFILE_MATURITY_V2.ARCHIVED])],
559
+ [PLAN_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
560
+ ]);
561
+ const _planPTerminal = new Set([PLAN_PROFILE_MATURITY_V2.ARCHIVED]);
562
+ const _planSTrans = new Map([
563
+ [PLAN_STEP_LIFECYCLE_V2.QUEUED, new Set([PLAN_STEP_LIFECYCLE_V2.RUNNING, PLAN_STEP_LIFECYCLE_V2.CANCELLED])],
564
+ [PLAN_STEP_LIFECYCLE_V2.RUNNING, new Set([PLAN_STEP_LIFECYCLE_V2.COMPLETED, PLAN_STEP_LIFECYCLE_V2.FAILED, PLAN_STEP_LIFECYCLE_V2.CANCELLED])],
565
+ [PLAN_STEP_LIFECYCLE_V2.COMPLETED, new Set()],
566
+ [PLAN_STEP_LIFECYCLE_V2.FAILED, new Set()],
567
+ [PLAN_STEP_LIFECYCLE_V2.CANCELLED, new Set()],
568
+ ]);
569
+ const _planPsV2 = new Map();
570
+ const _planSsV2 = new Map();
571
+ let _planMaxActivePerOwner = 6, _planMaxPendingStepsPerProfile = 15, _planIdleMs = 7 * 24 * 60 * 60 * 1000, _planStuckMs = 30 * 60 * 1000;
572
+ function _planPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
573
+ function _planCheckP(from, to) { const a = _planPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid plan profile transition ${from} → ${to}`); }
574
+ function _planCheckS(from, to) { const a = _planSTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid plan step transition ${from} → ${to}`); }
575
+ export function setMaxActivePlanProfilesPerOwnerV2(n) { _planMaxActivePerOwner = _planPos(n, "maxActivePlanProfilesPerOwner"); }
576
+ export function getMaxActivePlanProfilesPerOwnerV2() { return _planMaxActivePerOwner; }
577
+ export function setMaxPendingPlanStepsPerProfileV2(n) { _planMaxPendingStepsPerProfile = _planPos(n, "maxPendingPlanStepsPerProfile"); }
578
+ export function getMaxPendingPlanStepsPerProfileV2() { return _planMaxPendingStepsPerProfile; }
579
+ export function setPlanProfileIdleMsV2(n) { _planIdleMs = _planPos(n, "planProfileIdleMs"); }
580
+ export function getPlanProfileIdleMsV2() { return _planIdleMs; }
581
+ export function setPlanStepStuckMsV2(n) { _planStuckMs = _planPos(n, "planStepStuckMs"); }
582
+ export function getPlanStepStuckMsV2() { return _planStuckMs; }
583
+ export function _resetStatePlanModeV2() { _planPsV2.clear(); _planSsV2.clear(); _planMaxActivePerOwner = 6; _planMaxPendingStepsPerProfile = 15; _planIdleMs = 7 * 24 * 60 * 60 * 1000; _planStuckMs = 30 * 60 * 1000; }
584
+ export function registerPlanProfileV2({ id, owner, goal, metadata } = {}) {
585
+ if (!id) throw new Error("plan profile id required"); if (!owner) throw new Error("plan profile owner required");
586
+ if (_planPsV2.has(id)) throw new Error(`plan profile ${id} already registered`);
587
+ const now = Date.now();
588
+ const p = { id, owner, goal: goal || "", status: PLAN_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
589
+ _planPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
590
+ }
591
+ function _planCountActive(owner) { let n = 0; for (const p of _planPsV2.values()) if (p.owner === owner && p.status === PLAN_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
592
+ export function activatePlanProfileV2(id) {
593
+ const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`);
594
+ _planCheckP(p.status, PLAN_PROFILE_MATURITY_V2.ACTIVE);
595
+ const recovery = p.status === PLAN_PROFILE_MATURITY_V2.PAUSED;
596
+ if (!recovery && _planCountActive(p.owner) >= _planMaxActivePerOwner) throw new Error(`max active plan profiles for owner ${p.owner} reached`);
597
+ const now = Date.now(); p.status = PLAN_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
598
+ return { ...p, metadata: { ...p.metadata } };
599
+ }
600
+ export function pausePlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`); _planCheckP(p.status, PLAN_PROFILE_MATURITY_V2.PAUSED); p.status = PLAN_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
601
+ export function archivePlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`); _planCheckP(p.status, PLAN_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = PLAN_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
602
+ export function touchPlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`); if (_planPTerminal.has(p.status)) throw new Error(`cannot touch terminal plan profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
603
+ export function getPlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
604
+ export function listPlanProfilesV2() { return [..._planPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
605
+ function _planCountPending(profileId) { let n = 0; for (const s of _planSsV2.values()) if (s.profileId === profileId && (s.status === PLAN_STEP_LIFECYCLE_V2.QUEUED || s.status === PLAN_STEP_LIFECYCLE_V2.RUNNING)) n++; return n; }
606
+ export function createPlanStepV2({ id, profileId, action, metadata } = {}) {
607
+ if (!id) throw new Error("plan step id required"); if (!profileId) throw new Error("plan step profileId required");
608
+ if (_planSsV2.has(id)) throw new Error(`plan step ${id} already exists`);
609
+ if (!_planPsV2.has(profileId)) throw new Error(`plan profile ${profileId} not found`);
610
+ if (_planCountPending(profileId) >= _planMaxPendingStepsPerProfile) throw new Error(`max pending plan steps for profile ${profileId} reached`);
611
+ const now = Date.now();
612
+ const s = { id, profileId, action: action || "", status: PLAN_STEP_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
613
+ _planSsV2.set(id, s); return { ...s, metadata: { ...s.metadata } };
614
+ }
615
+ export function startPlanStepV2(id) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.RUNNING); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.RUNNING; s.updatedAt = now; if (!s.startedAt) s.startedAt = now; return { ...s, metadata: { ...s.metadata } }; }
616
+ export function completePlanStepV2(id) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.COMPLETED); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.COMPLETED; s.updatedAt = now; if (!s.settledAt) s.settledAt = now; return { ...s, metadata: { ...s.metadata } }; }
617
+ export function failPlanStepV2(id, reason) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.FAILED); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.FAILED; s.updatedAt = now; if (!s.settledAt) s.settledAt = now; if (reason) s.metadata.failReason = String(reason); return { ...s, metadata: { ...s.metadata } }; }
618
+ export function cancelPlanStepV2(id, reason) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.CANCELLED); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.CANCELLED; s.updatedAt = now; if (!s.settledAt) s.settledAt = now; if (reason) s.metadata.cancelReason = String(reason); return { ...s, metadata: { ...s.metadata } }; }
619
+ export function getPlanStepV2(id) { const s = _planSsV2.get(id); if (!s) return null; return { ...s, metadata: { ...s.metadata } }; }
620
+ export function listPlanStepsV2() { return [..._planSsV2.values()].map((s) => ({ ...s, metadata: { ...s.metadata } })); }
621
+ export function autoPauseIdlePlanProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _planPsV2.values()) if (p.status === PLAN_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _planIdleMs) { p.status = PLAN_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
622
+ export function autoFailStuckPlanStepsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const s of _planSsV2.values()) if (s.status === PLAN_STEP_LIFECYCLE_V2.RUNNING && s.startedAt != null && (t - s.startedAt) >= _planStuckMs) { s.status = PLAN_STEP_LIFECYCLE_V2.FAILED; s.updatedAt = t; if (!s.settledAt) s.settledAt = t; s.metadata.failReason = "auto-fail-stuck"; flipped.push(s.id); } return { flipped, count: flipped.length }; }
623
+ export function getPlanModeGovStatsV2() {
624
+ const profilesByStatus = {}; for (const v of Object.values(PLAN_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _planPsV2.values()) profilesByStatus[p.status]++;
625
+ const stepsByStatus = {}; for (const v of Object.values(PLAN_STEP_LIFECYCLE_V2)) stepsByStatus[v] = 0; for (const s of _planSsV2.values()) stepsByStatus[s.status]++;
626
+ return { totalPlanProfilesV2: _planPsV2.size, totalPlanStepsV2: _planSsV2.size, maxActivePlanProfilesPerOwner: _planMaxActivePerOwner, maxPendingPlanStepsPerProfile: _planMaxPendingStepsPerProfile, planProfileIdleMs: _planIdleMs, planStepStuckMs: _planStuckMs, profilesByStatus, stepsByStatus };
627
+ }