chainlesschain 0.132.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 (106) hide show
  1. package/package.json +1 -1
  2. package/src/commands/a2a.js +62 -0
  3. package/src/commands/activitypub.js +61 -0
  4. package/src/commands/agent.js +117 -0
  5. package/src/commands/bi.js +61 -0
  6. package/src/commands/bm25.js +78 -0
  7. package/src/commands/browse.js +64 -0
  8. package/src/commands/ccron.js +78 -0
  9. package/src/commands/compliance.js +40 -0
  10. package/src/commands/compt.js +78 -0
  11. package/src/commands/consol.js +231 -0
  12. package/src/commands/cowork.js +42 -0
  13. package/src/commands/crosschain.js +62 -0
  14. package/src/commands/dao.js +62 -0
  15. package/src/commands/dlp.js +61 -0
  16. package/src/commands/economy.js +56 -0
  17. package/src/commands/evolution.js +56 -0
  18. package/src/commands/evomap.js +61 -0
  19. package/src/commands/fflag.js +178 -0
  20. package/src/commands/git.js +45 -0
  21. package/src/commands/hmemory.js +56 -0
  22. package/src/commands/inference.js +42 -0
  23. package/src/commands/itbudget.js +45 -0
  24. package/src/commands/lowcode.js +44 -0
  25. package/src/commands/matrix.js +62 -0
  26. package/src/commands/mcpscaf.js +41 -0
  27. package/src/commands/meminj.js +41 -0
  28. package/src/commands/nostr.js +62 -0
  29. package/src/commands/orchgov.js +45 -0
  30. package/src/commands/pdfp.js +78 -0
  31. package/src/commands/perf.js +39 -0
  32. package/src/commands/perm.js +45 -0
  33. package/src/commands/pipeline.js +57 -1
  34. package/src/commands/planmode.js +45 -0
  35. package/src/commands/promcomp.js +82 -0
  36. package/src/commands/recommend.js +42 -0
  37. package/src/commands/seshhook.js +41 -0
  38. package/src/commands/seshsearch.js +41 -0
  39. package/src/commands/seshtail.js +41 -0
  40. package/src/commands/seshu.js +41 -0
  41. package/src/commands/sganal.js +78 -0
  42. package/src/commands/siem.js +40 -0
  43. package/src/commands/slotfill.js +41 -0
  44. package/src/commands/social.js +34 -0
  45. package/src/commands/svccont.js +45 -0
  46. package/src/commands/tms.js +45 -0
  47. package/src/commands/topiccls.js +45 -0
  48. package/src/commands/uprof.js +45 -0
  49. package/src/commands/vcheck.js +78 -0
  50. package/src/commands/webfetch.js +41 -0
  51. package/src/commands/zkp.js +62 -0
  52. package/src/harness/prompt-compressor.js +331 -0
  53. package/src/index.js +61 -1
  54. package/src/lib/a2a-protocol.js +105 -0
  55. package/src/lib/activitypub-bridge.js +105 -0
  56. package/src/lib/agent-economy.js +105 -0
  57. package/src/lib/app-builder.js +105 -0
  58. package/src/lib/autonomous-agent.js +105 -0
  59. package/src/lib/bi-engine.js +105 -0
  60. package/src/lib/bm25-search.js +81 -0
  61. package/src/lib/browser-automation.js +105 -0
  62. package/src/lib/compliance-framework-reporter.js +105 -0
  63. package/src/lib/compression-telemetry.js +81 -0
  64. package/src/lib/content-recommender.js +105 -0
  65. package/src/lib/cowork-cron.js +81 -0
  66. package/src/lib/cowork-task-runner.js +105 -0
  67. package/src/lib/cross-chain.js +105 -0
  68. package/src/lib/dao-governance.js +105 -0
  69. package/src/lib/dlp-engine.js +105 -0
  70. package/src/lib/evolution-system.js +105 -0
  71. package/src/lib/evomap-manager.js +105 -0
  72. package/src/lib/execution-backend.js +105 -0
  73. package/src/lib/feature-flags.js +85 -0
  74. package/src/lib/git-integration.js +105 -0
  75. package/src/lib/hierarchical-memory.js +105 -0
  76. package/src/lib/inference-network.js +105 -0
  77. package/src/lib/iteration-budget.js +105 -0
  78. package/src/lib/matrix-bridge.js +105 -0
  79. package/src/lib/mcp-scaffold.js +81 -0
  80. package/src/lib/memory-injection.js +81 -0
  81. package/src/lib/nostr-bridge.js +105 -0
  82. package/src/lib/orchestrator.js +105 -0
  83. package/src/lib/pdf-parser.js +81 -0
  84. package/src/lib/perf-tuning.js +105 -0
  85. package/src/lib/permission-engine.js +81 -0
  86. package/src/lib/pipeline-orchestrator.js +105 -0
  87. package/src/lib/plan-mode.js +81 -0
  88. package/src/lib/prompt-compressor.js +1 -10
  89. package/src/lib/service-container.js +81 -0
  90. package/src/lib/session-consolidator.js +105 -0
  91. package/src/lib/session-hooks.js +81 -0
  92. package/src/lib/session-search.js +81 -0
  93. package/src/lib/session-tail.js +81 -0
  94. package/src/lib/session-usage.js +83 -0
  95. package/src/lib/siem-exporter.js +105 -0
  96. package/src/lib/slot-filler.js +81 -0
  97. package/src/lib/social-graph-analytics.js +81 -0
  98. package/src/lib/social-graph.js +81 -0
  99. package/src/lib/sub-agent-registry.js +110 -0
  100. package/src/lib/task-model-selector.js +81 -0
  101. package/src/lib/todo-manager.js +105 -0
  102. package/src/lib/topic-classifier.js +105 -0
  103. package/src/lib/user-profile.js +81 -0
  104. package/src/lib/version-checker.js +81 -0
  105. package/src/lib/web-fetch.js +81 -0
  106. package/src/lib/zkp-engine.js +105 -0
@@ -225,3 +225,108 @@ export class EvoMapManager {
225
225
  };
226
226
  }
227
227
  }
228
+
229
+
230
+ // ===== V2 Surface: EvoMap Manager governance overlay (CLI v0.135.0) =====
231
+ export const EVOMAP_MAP_MATURITY_V2 = Object.freeze({
232
+ PENDING: "pending", ACTIVE: "active", STALE: "stale", ARCHIVED: "archived",
233
+ });
234
+ export const EVOMAP_EVOLUTION_LIFECYCLE_V2 = Object.freeze({
235
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
236
+ });
237
+
238
+ const _emMapTrans = new Map([
239
+ [EVOMAP_MAP_MATURITY_V2.PENDING, new Set([EVOMAP_MAP_MATURITY_V2.ACTIVE, EVOMAP_MAP_MATURITY_V2.ARCHIVED])],
240
+ [EVOMAP_MAP_MATURITY_V2.ACTIVE, new Set([EVOMAP_MAP_MATURITY_V2.STALE, EVOMAP_MAP_MATURITY_V2.ARCHIVED])],
241
+ [EVOMAP_MAP_MATURITY_V2.STALE, new Set([EVOMAP_MAP_MATURITY_V2.ACTIVE, EVOMAP_MAP_MATURITY_V2.ARCHIVED])],
242
+ [EVOMAP_MAP_MATURITY_V2.ARCHIVED, new Set()],
243
+ ]);
244
+ const _emMapTerminal = new Set([EVOMAP_MAP_MATURITY_V2.ARCHIVED]);
245
+ const _emEvoTrans = new Map([
246
+ [EVOMAP_EVOLUTION_LIFECYCLE_V2.QUEUED, new Set([EVOMAP_EVOLUTION_LIFECYCLE_V2.RUNNING, EVOMAP_EVOLUTION_LIFECYCLE_V2.CANCELLED])],
247
+ [EVOMAP_EVOLUTION_LIFECYCLE_V2.RUNNING, new Set([EVOMAP_EVOLUTION_LIFECYCLE_V2.COMPLETED, EVOMAP_EVOLUTION_LIFECYCLE_V2.FAILED, EVOMAP_EVOLUTION_LIFECYCLE_V2.CANCELLED])],
248
+ [EVOMAP_EVOLUTION_LIFECYCLE_V2.COMPLETED, new Set()],
249
+ [EVOMAP_EVOLUTION_LIFECYCLE_V2.FAILED, new Set()],
250
+ [EVOMAP_EVOLUTION_LIFECYCLE_V2.CANCELLED, new Set()],
251
+ ]);
252
+
253
+ const _emMaps = new Map();
254
+ const _emEvos = new Map();
255
+ let _emMaxActivePerOwner = 10;
256
+ let _emMaxPendingPerMap = 15;
257
+ let _emMapIdleMs = 7 * 24 * 60 * 60 * 1000;
258
+ let _emEvoStuckMs = 5 * 60 * 1000;
259
+
260
+ function _emPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
261
+
262
+ export function setMaxActiveEvoMapsPerOwnerV2(n) { _emMaxActivePerOwner = _emPos(n, "maxActiveEvoMapsPerOwner"); }
263
+ export function getMaxActiveEvoMapsPerOwnerV2() { return _emMaxActivePerOwner; }
264
+ export function setMaxPendingEvoEvolutionsPerMapV2(n) { _emMaxPendingPerMap = _emPos(n, "maxPendingEvoEvolutionsPerMap"); }
265
+ export function getMaxPendingEvoEvolutionsPerMapV2() { return _emMaxPendingPerMap; }
266
+ export function setEvoMapIdleMsV2(n) { _emMapIdleMs = _emPos(n, "evoMapIdleMs"); }
267
+ export function getEvoMapIdleMsV2() { return _emMapIdleMs; }
268
+ export function setEvoEvolutionStuckMsV2(n) { _emEvoStuckMs = _emPos(n, "evoEvolutionStuckMs"); }
269
+ export function getEvoEvolutionStuckMsV2() { return _emEvoStuckMs; }
270
+
271
+ export function _resetStateEvoMapManagerV2() {
272
+ _emMaps.clear(); _emEvos.clear();
273
+ _emMaxActivePerOwner = 10; _emMaxPendingPerMap = 15;
274
+ _emMapIdleMs = 7 * 24 * 60 * 60 * 1000; _emEvoStuckMs = 5 * 60 * 1000;
275
+ }
276
+
277
+ export function registerEvoMapV2({ id, owner, name, metadata } = {}) {
278
+ if (!id || typeof id !== "string") throw new Error("id is required");
279
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
280
+ if (_emMaps.has(id)) throw new Error(`evomap ${id} already registered`);
281
+ const now = Date.now();
282
+ const m = { id, owner, name: name || id, status: EVOMAP_MAP_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
283
+ _emMaps.set(id, m);
284
+ return { ...m, metadata: { ...m.metadata } };
285
+ }
286
+ function _emCheckM(from, to) { const a = _emMapTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid evomap transition ${from} → ${to}`); }
287
+ function _emCountActive(owner) { let n = 0; for (const m of _emMaps.values()) if (m.owner === owner && m.status === EVOMAP_MAP_MATURITY_V2.ACTIVE) n++; return n; }
288
+
289
+ export function activateEvoMapV2(id) {
290
+ const m = _emMaps.get(id); if (!m) throw new Error(`evomap ${id} not found`);
291
+ _emCheckM(m.status, EVOMAP_MAP_MATURITY_V2.ACTIVE);
292
+ const recovery = m.status === EVOMAP_MAP_MATURITY_V2.STALE;
293
+ if (!recovery) { const a = _emCountActive(m.owner); if (a >= _emMaxActivePerOwner) throw new Error(`max active evomaps per owner (${_emMaxActivePerOwner}) reached for ${m.owner}`); }
294
+ const now = Date.now(); m.status = EVOMAP_MAP_MATURITY_V2.ACTIVE; m.updatedAt = now; m.lastTouchedAt = now; if (!m.activatedAt) m.activatedAt = now;
295
+ return { ...m, metadata: { ...m.metadata } };
296
+ }
297
+ export function staleEvoMapV2(id) { const m = _emMaps.get(id); if (!m) throw new Error(`evomap ${id} not found`); _emCheckM(m.status, EVOMAP_MAP_MATURITY_V2.STALE); m.status = EVOMAP_MAP_MATURITY_V2.STALE; m.updatedAt = Date.now(); return { ...m, metadata: { ...m.metadata } }; }
298
+ export function archiveEvoMapV2(id) { const m = _emMaps.get(id); if (!m) throw new Error(`evomap ${id} not found`); _emCheckM(m.status, EVOMAP_MAP_MATURITY_V2.ARCHIVED); const now = Date.now(); m.status = EVOMAP_MAP_MATURITY_V2.ARCHIVED; m.updatedAt = now; if (!m.archivedAt) m.archivedAt = now; return { ...m, metadata: { ...m.metadata } }; }
299
+ export function touchEvoMapV2(id) { const m = _emMaps.get(id); if (!m) throw new Error(`evomap ${id} not found`); if (_emMapTerminal.has(m.status)) throw new Error(`cannot touch terminal evomap ${id}`); const now = Date.now(); m.lastTouchedAt = now; m.updatedAt = now; return { ...m, metadata: { ...m.metadata } }; }
300
+ export function getEvoMapV2(id) { const m = _emMaps.get(id); if (!m) return null; return { ...m, metadata: { ...m.metadata } }; }
301
+ export function listEvoMapsV2() { return [..._emMaps.values()].map((m) => ({ ...m, metadata: { ...m.metadata } })); }
302
+
303
+ function _emCountPending(mid) { let n = 0; for (const e of _emEvos.values()) if (e.mapId === mid && (e.status === EVOMAP_EVOLUTION_LIFECYCLE_V2.QUEUED || e.status === EVOMAP_EVOLUTION_LIFECYCLE_V2.RUNNING)) n++; return n; }
304
+
305
+ export function createEvoEvolutionV2({ id, mapId, strategy, metadata } = {}) {
306
+ if (!id || typeof id !== "string") throw new Error("id is required");
307
+ if (!mapId || typeof mapId !== "string") throw new Error("mapId is required");
308
+ if (_emEvos.has(id)) throw new Error(`evo evolution ${id} already exists`);
309
+ if (!_emMaps.has(mapId)) throw new Error(`evomap ${mapId} not found`);
310
+ const pending = _emCountPending(mapId);
311
+ if (pending >= _emMaxPendingPerMap) throw new Error(`max pending evo evolutions per map (${_emMaxPendingPerMap}) reached for ${mapId}`);
312
+ const now = Date.now();
313
+ const e = { id, mapId, strategy: strategy || "default", status: EVOMAP_EVOLUTION_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
314
+ _emEvos.set(id, e);
315
+ return { ...e, metadata: { ...e.metadata } };
316
+ }
317
+ function _emCheckE(from, to) { const a = _emEvoTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid evo evolution transition ${from} → ${to}`); }
318
+ export function startEvoEvolutionV2(id) { const e = _emEvos.get(id); if (!e) throw new Error(`evo evolution ${id} not found`); _emCheckE(e.status, EVOMAP_EVOLUTION_LIFECYCLE_V2.RUNNING); const now = Date.now(); e.status = EVOMAP_EVOLUTION_LIFECYCLE_V2.RUNNING; e.updatedAt = now; if (!e.startedAt) e.startedAt = now; return { ...e, metadata: { ...e.metadata } }; }
319
+ export function completeEvoEvolutionV2(id) { const e = _emEvos.get(id); if (!e) throw new Error(`evo evolution ${id} not found`); _emCheckE(e.status, EVOMAP_EVOLUTION_LIFECYCLE_V2.COMPLETED); const now = Date.now(); e.status = EVOMAP_EVOLUTION_LIFECYCLE_V2.COMPLETED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; return { ...e, metadata: { ...e.metadata } }; }
320
+ export function failEvoEvolutionV2(id, reason) { const e = _emEvos.get(id); if (!e) throw new Error(`evo evolution ${id} not found`); _emCheckE(e.status, EVOMAP_EVOLUTION_LIFECYCLE_V2.FAILED); const now = Date.now(); e.status = EVOMAP_EVOLUTION_LIFECYCLE_V2.FAILED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.failReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
321
+ export function cancelEvoEvolutionV2(id, reason) { const e = _emEvos.get(id); if (!e) throw new Error(`evo evolution ${id} not found`); _emCheckE(e.status, EVOMAP_EVOLUTION_LIFECYCLE_V2.CANCELLED); const now = Date.now(); e.status = EVOMAP_EVOLUTION_LIFECYCLE_V2.CANCELLED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.cancelReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
322
+ export function getEvoEvolutionV2(id) { const e = _emEvos.get(id); if (!e) return null; return { ...e, metadata: { ...e.metadata } }; }
323
+ export function listEvoEvolutionsV2() { return [..._emEvos.values()].map((e) => ({ ...e, metadata: { ...e.metadata } })); }
324
+
325
+ export function autoStaleIdleEvoMapsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const m of _emMaps.values()) if (m.status === EVOMAP_MAP_MATURITY_V2.ACTIVE && (t - m.lastTouchedAt) >= _emMapIdleMs) { m.status = EVOMAP_MAP_MATURITY_V2.STALE; m.updatedAt = t; flipped.push(m.id); } return { flipped, count: flipped.length }; }
326
+ export function autoFailStuckEvoEvolutionsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const e of _emEvos.values()) if (e.status === EVOMAP_EVOLUTION_LIFECYCLE_V2.RUNNING && e.startedAt != null && (t - e.startedAt) >= _emEvoStuckMs) { e.status = EVOMAP_EVOLUTION_LIFECYCLE_V2.FAILED; e.updatedAt = t; if (!e.settledAt) e.settledAt = t; e.metadata.failReason = "auto-fail-stuck"; flipped.push(e.id); } return { flipped, count: flipped.length }; }
327
+
328
+ export function getEvoMapManagerStatsV2() {
329
+ const mapsByStatus = {}; for (const s of Object.values(EVOMAP_MAP_MATURITY_V2)) mapsByStatus[s] = 0; for (const m of _emMaps.values()) mapsByStatus[m.status]++;
330
+ const evosByStatus = {}; for (const s of Object.values(EVOMAP_EVOLUTION_LIFECYCLE_V2)) evosByStatus[s] = 0; for (const e of _emEvos.values()) evosByStatus[e.status]++;
331
+ return { totalMapsV2: _emMaps.size, totalEvolutionsV2: _emEvos.size, maxActiveEvoMapsPerOwner: _emMaxActivePerOwner, maxPendingEvoEvolutionsPerMap: _emMaxPendingPerMap, evoMapIdleMs: _emMapIdleMs, evoEvolutionStuckMs: _emEvoStuckMs, mapsByStatus, evosByStatus };
332
+ }
@@ -237,3 +237,108 @@ export function createBackend(config = {}) {
237
237
  return new LocalBackend(config.options || {});
238
238
  }
239
239
  }
240
+
241
+
242
+ // ===== V2 Surface: Execution Backend governance overlay (CLI v0.133.0) =====
243
+ export const EXECBE_BACKEND_MATURITY_V2 = Object.freeze({
244
+ PENDING: "pending", ACTIVE: "active", DEGRADED: "degraded", RETIRED: "retired",
245
+ });
246
+ export const EXECBE_JOB_LIFECYCLE_V2 = Object.freeze({
247
+ QUEUED: "queued", RUNNING: "running", SUCCEEDED: "succeeded", FAILED: "failed", CANCELLED: "cancelled",
248
+ });
249
+
250
+ const _ebBackendTrans = new Map([
251
+ [EXECBE_BACKEND_MATURITY_V2.PENDING, new Set([EXECBE_BACKEND_MATURITY_V2.ACTIVE, EXECBE_BACKEND_MATURITY_V2.RETIRED])],
252
+ [EXECBE_BACKEND_MATURITY_V2.ACTIVE, new Set([EXECBE_BACKEND_MATURITY_V2.DEGRADED, EXECBE_BACKEND_MATURITY_V2.RETIRED])],
253
+ [EXECBE_BACKEND_MATURITY_V2.DEGRADED, new Set([EXECBE_BACKEND_MATURITY_V2.ACTIVE, EXECBE_BACKEND_MATURITY_V2.RETIRED])],
254
+ [EXECBE_BACKEND_MATURITY_V2.RETIRED, new Set()],
255
+ ]);
256
+ const _ebBackendTerminal = new Set([EXECBE_BACKEND_MATURITY_V2.RETIRED]);
257
+ const _ebJobTrans = new Map([
258
+ [EXECBE_JOB_LIFECYCLE_V2.QUEUED, new Set([EXECBE_JOB_LIFECYCLE_V2.RUNNING, EXECBE_JOB_LIFECYCLE_V2.CANCELLED])],
259
+ [EXECBE_JOB_LIFECYCLE_V2.RUNNING, new Set([EXECBE_JOB_LIFECYCLE_V2.SUCCEEDED, EXECBE_JOB_LIFECYCLE_V2.FAILED, EXECBE_JOB_LIFECYCLE_V2.CANCELLED])],
260
+ [EXECBE_JOB_LIFECYCLE_V2.SUCCEEDED, new Set()],
261
+ [EXECBE_JOB_LIFECYCLE_V2.FAILED, new Set()],
262
+ [EXECBE_JOB_LIFECYCLE_V2.CANCELLED, new Set()],
263
+ ]);
264
+
265
+ const _ebBackends = new Map();
266
+ const _ebJobs = new Map();
267
+ let _ebMaxActivePerOwner = 6;
268
+ let _ebMaxPendingPerBackend = 20;
269
+ let _ebBackendIdleMs = 12 * 60 * 60 * 1000;
270
+ let _ebJobStuckMs = 10 * 60 * 1000;
271
+
272
+ function _ebPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
273
+
274
+ export function setMaxActiveBackendsPerOwnerV2(n) { _ebMaxActivePerOwner = _ebPos(n, "maxActiveBackendsPerOwner"); }
275
+ export function getMaxActiveBackendsPerOwnerV2() { return _ebMaxActivePerOwner; }
276
+ export function setMaxPendingJobsPerBackendV2(n) { _ebMaxPendingPerBackend = _ebPos(n, "maxPendingJobsPerBackend"); }
277
+ export function getMaxPendingJobsPerBackendV2() { return _ebMaxPendingPerBackend; }
278
+ export function setBackendIdleMsV2(n) { _ebBackendIdleMs = _ebPos(n, "backendIdleMs"); }
279
+ export function getBackendIdleMsV2() { return _ebBackendIdleMs; }
280
+ export function setExecJobStuckMsV2(n) { _ebJobStuckMs = _ebPos(n, "execJobStuckMs"); }
281
+ export function getExecJobStuckMsV2() { return _ebJobStuckMs; }
282
+
283
+ export function _resetStateExecutionBackendV2() {
284
+ _ebBackends.clear(); _ebJobs.clear();
285
+ _ebMaxActivePerOwner = 6; _ebMaxPendingPerBackend = 20;
286
+ _ebBackendIdleMs = 12 * 60 * 60 * 1000; _ebJobStuckMs = 10 * 60 * 1000;
287
+ }
288
+
289
+ export function registerBackendV2({ id, owner, kind, metadata } = {}) {
290
+ if (!id || typeof id !== "string") throw new Error("id is required");
291
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
292
+ if (_ebBackends.has(id)) throw new Error(`backend ${id} already registered`);
293
+ const now = Date.now();
294
+ const b = { id, owner, kind: kind || "local", status: EXECBE_BACKEND_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
295
+ _ebBackends.set(id, b);
296
+ return { ...b, metadata: { ...b.metadata } };
297
+ }
298
+ function _ebCheckB(from, to) { const allowed = _ebBackendTrans.get(from); if (!allowed || !allowed.has(to)) throw new Error(`invalid backend transition ${from} → ${to}`); }
299
+ function _ebCountActive(owner) { let n = 0; for (const b of _ebBackends.values()) if (b.owner === owner && b.status === EXECBE_BACKEND_MATURITY_V2.ACTIVE) n++; return n; }
300
+
301
+ export function activateBackendV2(id) {
302
+ const b = _ebBackends.get(id); if (!b) throw new Error(`backend ${id} not found`);
303
+ _ebCheckB(b.status, EXECBE_BACKEND_MATURITY_V2.ACTIVE);
304
+ const recovery = b.status === EXECBE_BACKEND_MATURITY_V2.DEGRADED;
305
+ if (!recovery) { const a = _ebCountActive(b.owner); if (a >= _ebMaxActivePerOwner) throw new Error(`max active backends per owner (${_ebMaxActivePerOwner}) reached for ${b.owner}`); }
306
+ const now = Date.now(); b.status = EXECBE_BACKEND_MATURITY_V2.ACTIVE; b.updatedAt = now; b.lastTouchedAt = now; if (!b.activatedAt) b.activatedAt = now;
307
+ return { ...b, metadata: { ...b.metadata } };
308
+ }
309
+ export function degradeBackendV2(id) { const b = _ebBackends.get(id); if (!b) throw new Error(`backend ${id} not found`); _ebCheckB(b.status, EXECBE_BACKEND_MATURITY_V2.DEGRADED); b.status = EXECBE_BACKEND_MATURITY_V2.DEGRADED; b.updatedAt = Date.now(); return { ...b, metadata: { ...b.metadata } }; }
310
+ export function retireBackendV2(id) { const b = _ebBackends.get(id); if (!b) throw new Error(`backend ${id} not found`); _ebCheckB(b.status, EXECBE_BACKEND_MATURITY_V2.RETIRED); const now = Date.now(); b.status = EXECBE_BACKEND_MATURITY_V2.RETIRED; b.updatedAt = now; if (!b.retiredAt) b.retiredAt = now; return { ...b, metadata: { ...b.metadata } }; }
311
+ export function touchBackendV2(id) { const b = _ebBackends.get(id); if (!b) throw new Error(`backend ${id} not found`); if (_ebBackendTerminal.has(b.status)) throw new Error(`cannot touch terminal backend ${id}`); const now = Date.now(); b.lastTouchedAt = now; b.updatedAt = now; return { ...b, metadata: { ...b.metadata } }; }
312
+ export function getBackendV2(id) { const b = _ebBackends.get(id); if (!b) return null; return { ...b, metadata: { ...b.metadata } }; }
313
+ export function listBackendsV2() { return [..._ebBackends.values()].map((b) => ({ ...b, metadata: { ...b.metadata } })); }
314
+
315
+ function _ebCountPending(bid) { let n = 0; for (const j of _ebJobs.values()) if (j.backendId === bid && (j.status === EXECBE_JOB_LIFECYCLE_V2.QUEUED || j.status === EXECBE_JOB_LIFECYCLE_V2.RUNNING)) n++; return n; }
316
+
317
+ export function createExecJobV2({ id, backendId, command, metadata } = {}) {
318
+ if (!id || typeof id !== "string") throw new Error("id is required");
319
+ if (!backendId || typeof backendId !== "string") throw new Error("backendId is required");
320
+ if (_ebJobs.has(id)) throw new Error(`exec job ${id} already exists`);
321
+ if (!_ebBackends.has(backendId)) throw new Error(`backend ${backendId} not found`);
322
+ const pending = _ebCountPending(backendId);
323
+ if (pending >= _ebMaxPendingPerBackend) throw new Error(`max pending jobs per backend (${_ebMaxPendingPerBackend}) reached for ${backendId}`);
324
+ const now = Date.now();
325
+ const j = { id, backendId, command: command || "", status: EXECBE_JOB_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
326
+ _ebJobs.set(id, j);
327
+ return { ...j, metadata: { ...j.metadata } };
328
+ }
329
+ function _ebCheckJ(from, to) { const allowed = _ebJobTrans.get(from); if (!allowed || !allowed.has(to)) throw new Error(`invalid exec job transition ${from} → ${to}`); }
330
+ export function startExecJobV2(id) { const j = _ebJobs.get(id); if (!j) throw new Error(`exec job ${id} not found`); _ebCheckJ(j.status, EXECBE_JOB_LIFECYCLE_V2.RUNNING); const now = Date.now(); j.status = EXECBE_JOB_LIFECYCLE_V2.RUNNING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
331
+ export function succeedExecJobV2(id) { const j = _ebJobs.get(id); if (!j) throw new Error(`exec job ${id} not found`); _ebCheckJ(j.status, EXECBE_JOB_LIFECYCLE_V2.SUCCEEDED); const now = Date.now(); j.status = EXECBE_JOB_LIFECYCLE_V2.SUCCEEDED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
332
+ export function failExecJobV2(id, reason) { const j = _ebJobs.get(id); if (!j) throw new Error(`exec job ${id} not found`); _ebCheckJ(j.status, EXECBE_JOB_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = EXECBE_JOB_LIFECYCLE_V2.FAILED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.failReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
333
+ export function cancelExecJobV2(id, reason) { const j = _ebJobs.get(id); if (!j) throw new Error(`exec job ${id} not found`); _ebCheckJ(j.status, EXECBE_JOB_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = EXECBE_JOB_LIFECYCLE_V2.CANCELLED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.cancelReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
334
+ export function getExecJobV2(id) { const j = _ebJobs.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
335
+ export function listExecJobsV2() { return [..._ebJobs.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
336
+
337
+ export function autoDegradeIdleBackendsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const b of _ebBackends.values()) if (b.status === EXECBE_BACKEND_MATURITY_V2.ACTIVE && (t - b.lastTouchedAt) >= _ebBackendIdleMs) { b.status = EXECBE_BACKEND_MATURITY_V2.DEGRADED; b.updatedAt = t; flipped.push(b.id); } return { flipped, count: flipped.length }; }
338
+ export function autoFailStuckExecJobsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _ebJobs.values()) if (j.status === EXECBE_JOB_LIFECYCLE_V2.RUNNING && j.startedAt != null && (t - j.startedAt) >= _ebJobStuckMs) { j.status = EXECBE_JOB_LIFECYCLE_V2.FAILED; j.updatedAt = t; if (!j.settledAt) j.settledAt = t; j.metadata.failReason = "auto-fail-stuck"; flipped.push(j.id); } return { flipped, count: flipped.length }; }
339
+
340
+ export function getExecutionBackendStatsV2() {
341
+ const backendsByStatus = {}; for (const s of Object.values(EXECBE_BACKEND_MATURITY_V2)) backendsByStatus[s] = 0; for (const b of _ebBackends.values()) backendsByStatus[b.status]++;
342
+ const jobsByStatus = {}; for (const s of Object.values(EXECBE_JOB_LIFECYCLE_V2)) jobsByStatus[s] = 0; for (const j of _ebJobs.values()) jobsByStatus[j.status]++;
343
+ return { totalBackendsV2: _ebBackends.size, totalJobsV2: _ebJobs.size, maxActiveBackendsPerOwner: _ebMaxActivePerOwner, maxPendingJobsPerBackend: _ebMaxPendingPerBackend, backendIdleMs: _ebBackendIdleMs, execJobStuckMs: _ebJobStuckMs, backendsByStatus, jobsByStatus };
344
+ }
@@ -5,3 +5,88 @@ export {
5
5
  setFeature,
6
6
  getFlagInfo,
7
7
  } from "../harness/feature-flags.js";
8
+
9
+
10
+ // =====================================================================
11
+ // Feature Flags V2 governance overlay (in-memory, atop legacy flags)
12
+ // =====================================================================
13
+ export const FFLAG_PROFILE_MATURITY_V2 = Object.freeze({
14
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived",
15
+ });
16
+ export const FFLAG_EVAL_LIFECYCLE_V2 = Object.freeze({
17
+ QUEUED: "queued", EVALUATING: "evaluating", EVALUATED: "evaluated", FAILED: "failed", CANCELLED: "cancelled",
18
+ });
19
+ const _fflagPTrans = new Map([
20
+ [FFLAG_PROFILE_MATURITY_V2.PENDING, new Set([FFLAG_PROFILE_MATURITY_V2.ACTIVE, FFLAG_PROFILE_MATURITY_V2.ARCHIVED])],
21
+ [FFLAG_PROFILE_MATURITY_V2.ACTIVE, new Set([FFLAG_PROFILE_MATURITY_V2.PAUSED, FFLAG_PROFILE_MATURITY_V2.ARCHIVED])],
22
+ [FFLAG_PROFILE_MATURITY_V2.PAUSED, new Set([FFLAG_PROFILE_MATURITY_V2.ACTIVE, FFLAG_PROFILE_MATURITY_V2.ARCHIVED])],
23
+ [FFLAG_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
24
+ ]);
25
+ const _fflagPTerminal = new Set([FFLAG_PROFILE_MATURITY_V2.ARCHIVED]);
26
+ const _fflagETrans = new Map([
27
+ [FFLAG_EVAL_LIFECYCLE_V2.QUEUED, new Set([FFLAG_EVAL_LIFECYCLE_V2.EVALUATING, FFLAG_EVAL_LIFECYCLE_V2.CANCELLED])],
28
+ [FFLAG_EVAL_LIFECYCLE_V2.EVALUATING, new Set([FFLAG_EVAL_LIFECYCLE_V2.EVALUATED, FFLAG_EVAL_LIFECYCLE_V2.FAILED, FFLAG_EVAL_LIFECYCLE_V2.CANCELLED])],
29
+ [FFLAG_EVAL_LIFECYCLE_V2.EVALUATED, new Set()],
30
+ [FFLAG_EVAL_LIFECYCLE_V2.FAILED, new Set()],
31
+ [FFLAG_EVAL_LIFECYCLE_V2.CANCELLED, new Set()],
32
+ ]);
33
+ const _fflagPsV2 = new Map();
34
+ const _fflagEsV2 = new Map();
35
+ let _fflagMaxActive = 15, _fflagMaxPending = 30, _fflagIdleMs = 30 * 24 * 60 * 60 * 1000, _fflagStuckMs = 30 * 1000;
36
+ function _fflagPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
37
+ function _fflagCheckP(from, to) { const a = _fflagPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid fflag profile transition ${from} → ${to}`); }
38
+ function _fflagCheckE(from, to) { const a = _fflagETrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid fflag eval transition ${from} → ${to}`); }
39
+ function _fflagCountActive(owner) { let c = 0; for (const p of _fflagPsV2.values()) if (p.owner === owner && p.status === FFLAG_PROFILE_MATURITY_V2.ACTIVE) c++; return c; }
40
+ function _fflagCountPending(profileId) { let c = 0; for (const e of _fflagEsV2.values()) if (e.profileId === profileId && (e.status === FFLAG_EVAL_LIFECYCLE_V2.QUEUED || e.status === FFLAG_EVAL_LIFECYCLE_V2.EVALUATING)) c++; return c; }
41
+ export function setMaxActiveFflagProfilesPerOwnerV2(n) { _fflagMaxActive = _fflagPos(n, "maxActiveFflagProfilesPerOwner"); }
42
+ export function getMaxActiveFflagProfilesPerOwnerV2() { return _fflagMaxActive; }
43
+ export function setMaxPendingFflagEvalsPerProfileV2(n) { _fflagMaxPending = _fflagPos(n, "maxPendingFflagEvalsPerProfile"); }
44
+ export function getMaxPendingFflagEvalsPerProfileV2() { return _fflagMaxPending; }
45
+ export function setFflagProfileIdleMsV2(n) { _fflagIdleMs = _fflagPos(n, "fflagProfileIdleMs"); }
46
+ export function getFflagProfileIdleMsV2() { return _fflagIdleMs; }
47
+ export function setFflagEvalStuckMsV2(n) { _fflagStuckMs = _fflagPos(n, "fflagEvalStuckMs"); }
48
+ export function getFflagEvalStuckMsV2() { return _fflagStuckMs; }
49
+ export function _resetStateFeatureFlagsV2() { _fflagPsV2.clear(); _fflagEsV2.clear(); _fflagMaxActive = 15; _fflagMaxPending = 30; _fflagIdleMs = 30 * 24 * 60 * 60 * 1000; _fflagStuckMs = 30 * 1000; }
50
+ export function registerFflagProfileV2({ id, owner, scope, metadata } = {}) {
51
+ if (!id || !owner) throw new Error("id and owner required");
52
+ if (_fflagPsV2.has(id)) throw new Error(`fflag profile ${id} already exists`);
53
+ const now = Date.now();
54
+ const p = { id, owner, scope: scope || "*", status: FFLAG_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, lastTouchedAt: now, activatedAt: null, archivedAt: null, metadata: { ...(metadata || {}) } };
55
+ _fflagPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
56
+ }
57
+ export function activateFflagProfileV2(id) {
58
+ const p = _fflagPsV2.get(id); if (!p) throw new Error(`fflag profile ${id} not found`);
59
+ const isInitial = p.status === FFLAG_PROFILE_MATURITY_V2.PENDING;
60
+ _fflagCheckP(p.status, FFLAG_PROFILE_MATURITY_V2.ACTIVE);
61
+ if (isInitial && _fflagCountActive(p.owner) >= _fflagMaxActive) throw new Error(`max active fflag profiles for owner ${p.owner} reached`);
62
+ const now = Date.now(); p.status = FFLAG_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now;
63
+ if (!p.activatedAt) p.activatedAt = now;
64
+ return { ...p, metadata: { ...p.metadata } };
65
+ }
66
+ export function pauseFflagProfileV2(id) { const p = _fflagPsV2.get(id); if (!p) throw new Error(`fflag profile ${id} not found`); _fflagCheckP(p.status, FFLAG_PROFILE_MATURITY_V2.PAUSED); p.status = FFLAG_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
67
+ export function archiveFflagProfileV2(id) { const p = _fflagPsV2.get(id); if (!p) throw new Error(`fflag profile ${id} not found`); _fflagCheckP(p.status, FFLAG_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = FFLAG_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
68
+ export function touchFflagProfileV2(id) { const p = _fflagPsV2.get(id); if (!p) throw new Error(`fflag profile ${id} not found`); if (_fflagPTerminal.has(p.status)) throw new Error(`cannot touch terminal fflag profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
69
+ export function getFflagProfileV2(id) { const p = _fflagPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
70
+ export function listFflagProfilesV2() { return [..._fflagPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
71
+ export function createFflagEvalV2({ id, profileId, key, metadata } = {}) {
72
+ if (!id || !profileId) throw new Error("id and profileId required");
73
+ if (_fflagEsV2.has(id)) throw new Error(`fflag eval ${id} already exists`);
74
+ if (!_fflagPsV2.has(profileId)) throw new Error(`fflag profile ${profileId} not found`);
75
+ if (_fflagCountPending(profileId) >= _fflagMaxPending) throw new Error(`max pending fflag evals for profile ${profileId} reached`);
76
+ const now = Date.now();
77
+ const e = { id, profileId, key: key || "", status: FFLAG_EVAL_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
78
+ _fflagEsV2.set(id, e); return { ...e, metadata: { ...e.metadata } };
79
+ }
80
+ export function evaluatingFflagEvalV2(id) { const e = _fflagEsV2.get(id); if (!e) throw new Error(`fflag eval ${id} not found`); _fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.EVALUATING); const now = Date.now(); e.status = FFLAG_EVAL_LIFECYCLE_V2.EVALUATING; e.updatedAt = now; if (!e.startedAt) e.startedAt = now; return { ...e, metadata: { ...e.metadata } }; }
81
+ export function evaluateFflagEvalV2(id) { const e = _fflagEsV2.get(id); if (!e) throw new Error(`fflag eval ${id} not found`); _fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.EVALUATED); const now = Date.now(); e.status = FFLAG_EVAL_LIFECYCLE_V2.EVALUATED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; return { ...e, metadata: { ...e.metadata } }; }
82
+ export function failFflagEvalV2(id, reason) { const e = _fflagEsV2.get(id); if (!e) throw new Error(`fflag eval ${id} not found`); _fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.FAILED); const now = Date.now(); e.status = FFLAG_EVAL_LIFECYCLE_V2.FAILED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.failReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
83
+ export function cancelFflagEvalV2(id, reason) { const e = _fflagEsV2.get(id); if (!e) throw new Error(`fflag eval ${id} not found`); _fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.CANCELLED); const now = Date.now(); e.status = FFLAG_EVAL_LIFECYCLE_V2.CANCELLED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.cancelReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
84
+ export function getFflagEvalV2(id) { const e = _fflagEsV2.get(id); if (!e) return null; return { ...e, metadata: { ...e.metadata } }; }
85
+ export function listFflagEvalsV2() { return [..._fflagEsV2.values()].map((e) => ({ ...e, metadata: { ...e.metadata } })); }
86
+ export function autoPauseIdleFflagProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _fflagPsV2.values()) if (p.status === FFLAG_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _fflagIdleMs) { p.status = FFLAG_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
87
+ export function autoFailStuckFflagEvalsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const e of _fflagEsV2.values()) if (e.status === FFLAG_EVAL_LIFECYCLE_V2.EVALUATING && e.startedAt != null && (t - e.startedAt) >= _fflagStuckMs) { e.status = FFLAG_EVAL_LIFECYCLE_V2.FAILED; e.updatedAt = t; if (!e.settledAt) e.settledAt = t; e.metadata.failReason = "auto-fail-stuck"; flipped.push(e.id); } return { flipped, count: flipped.length }; }
88
+ export function getFeatureFlagsGovStatsV2() {
89
+ const profilesByStatus = {}; for (const v of Object.values(FFLAG_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _fflagPsV2.values()) profilesByStatus[p.status]++;
90
+ const evalsByStatus = {}; for (const v of Object.values(FFLAG_EVAL_LIFECYCLE_V2)) evalsByStatus[v] = 0; for (const e of _fflagEsV2.values()) evalsByStatus[e.status]++;
91
+ return { totalFflagProfilesV2: _fflagPsV2.size, totalFflagEvalsV2: _fflagEsV2.size, maxActiveFflagProfilesPerOwner: _fflagMaxActive, maxPendingFflagEvalsPerProfile: _fflagMaxPending, fflagProfileIdleMs: _fflagIdleMs, fflagEvalStuckMs: _fflagStuckMs, profilesByStatus, evalsByStatus };
92
+ }
@@ -218,3 +218,108 @@ echo "ChainlessChain: pre-commit hook running"
218
218
 
219
219
  return { installed: true, hook: "pre-commit", path: hookPath };
220
220
  }
221
+
222
+
223
+ // ===== V2 Surface: Git Integration governance overlay (CLI v0.139.0) =====
224
+ export const GIT_REPO_MATURITY_V2 = Object.freeze({
225
+ PENDING: "pending", ACTIVE: "active", ARCHIVED: "archived", DECOMMISSIONED: "decommissioned",
226
+ });
227
+ export const GIT_COMMIT_LIFECYCLE_V2 = Object.freeze({
228
+ QUEUED: "queued", COMMITTING: "committing", COMMITTED: "committed", FAILED: "failed", CANCELLED: "cancelled",
229
+ });
230
+
231
+ const _grTrans = new Map([
232
+ [GIT_REPO_MATURITY_V2.PENDING, new Set([GIT_REPO_MATURITY_V2.ACTIVE, GIT_REPO_MATURITY_V2.DECOMMISSIONED])],
233
+ [GIT_REPO_MATURITY_V2.ACTIVE, new Set([GIT_REPO_MATURITY_V2.ARCHIVED, GIT_REPO_MATURITY_V2.DECOMMISSIONED])],
234
+ [GIT_REPO_MATURITY_V2.ARCHIVED, new Set([GIT_REPO_MATURITY_V2.ACTIVE, GIT_REPO_MATURITY_V2.DECOMMISSIONED])],
235
+ [GIT_REPO_MATURITY_V2.DECOMMISSIONED, new Set()],
236
+ ]);
237
+ const _grTerminal = new Set([GIT_REPO_MATURITY_V2.DECOMMISSIONED]);
238
+ const _gcTrans = new Map([
239
+ [GIT_COMMIT_LIFECYCLE_V2.QUEUED, new Set([GIT_COMMIT_LIFECYCLE_V2.COMMITTING, GIT_COMMIT_LIFECYCLE_V2.CANCELLED])],
240
+ [GIT_COMMIT_LIFECYCLE_V2.COMMITTING, new Set([GIT_COMMIT_LIFECYCLE_V2.COMMITTED, GIT_COMMIT_LIFECYCLE_V2.FAILED, GIT_COMMIT_LIFECYCLE_V2.CANCELLED])],
241
+ [GIT_COMMIT_LIFECYCLE_V2.COMMITTED, new Set()],
242
+ [GIT_COMMIT_LIFECYCLE_V2.FAILED, new Set()],
243
+ [GIT_COMMIT_LIFECYCLE_V2.CANCELLED, new Set()],
244
+ ]);
245
+
246
+ const _grsV2 = new Map();
247
+ const _gcsV2 = new Map();
248
+ let _grMaxActivePerOwner = 10;
249
+ let _grMaxPendingCommitsPerRepo = 20;
250
+ let _grIdleMs = 30 * 24 * 60 * 60 * 1000;
251
+ let _gcStuckMs = 5 * 60 * 1000;
252
+
253
+ function _grPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
254
+
255
+ export function setMaxActiveGitReposPerOwnerV2(n) { _grMaxActivePerOwner = _grPos(n, "maxActiveGitReposPerOwner"); }
256
+ export function getMaxActiveGitReposPerOwnerV2() { return _grMaxActivePerOwner; }
257
+ export function setMaxPendingGitCommitsPerRepoV2(n) { _grMaxPendingCommitsPerRepo = _grPos(n, "maxPendingGitCommitsPerRepo"); }
258
+ export function getMaxPendingGitCommitsPerRepoV2() { return _grMaxPendingCommitsPerRepo; }
259
+ export function setGitRepoIdleMsV2(n) { _grIdleMs = _grPos(n, "gitRepoIdleMs"); }
260
+ export function getGitRepoIdleMsV2() { return _grIdleMs; }
261
+ export function setGitCommitStuckMsV2(n) { _gcStuckMs = _grPos(n, "gitCommitStuckMs"); }
262
+ export function getGitCommitStuckMsV2() { return _gcStuckMs; }
263
+
264
+ export function _resetStateGitIntegrationV2() {
265
+ _grsV2.clear(); _gcsV2.clear();
266
+ _grMaxActivePerOwner = 10; _grMaxPendingCommitsPerRepo = 20;
267
+ _grIdleMs = 30 * 24 * 60 * 60 * 1000; _gcStuckMs = 5 * 60 * 1000;
268
+ }
269
+
270
+ export function registerGitRepoV2({ id, owner, branch, metadata } = {}) {
271
+ if (!id || typeof id !== "string") throw new Error("id is required");
272
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
273
+ if (_grsV2.has(id)) throw new Error(`git repo ${id} already registered`);
274
+ const now = Date.now();
275
+ const r = { id, owner, branch: branch || "main", status: GIT_REPO_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, decommissionedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
276
+ _grsV2.set(id, r);
277
+ return { ...r, metadata: { ...r.metadata } };
278
+ }
279
+ function _grCheckR(from, to) { const a = _grTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid git repo transition ${from} → ${to}`); }
280
+ function _grCountActive(owner) { let n = 0; for (const r of _grsV2.values()) if (r.owner === owner && r.status === GIT_REPO_MATURITY_V2.ACTIVE) n++; return n; }
281
+
282
+ export function activateGitRepoV2(id) {
283
+ const r = _grsV2.get(id); if (!r) throw new Error(`git repo ${id} not found`);
284
+ _grCheckR(r.status, GIT_REPO_MATURITY_V2.ACTIVE);
285
+ const recovery = r.status === GIT_REPO_MATURITY_V2.ARCHIVED;
286
+ if (!recovery) { const c = _grCountActive(r.owner); if (c >= _grMaxActivePerOwner) throw new Error(`max active git repos per owner (${_grMaxActivePerOwner}) reached for ${r.owner}`); }
287
+ const now = Date.now(); r.status = GIT_REPO_MATURITY_V2.ACTIVE; r.updatedAt = now; r.lastTouchedAt = now; if (!r.activatedAt) r.activatedAt = now;
288
+ return { ...r, metadata: { ...r.metadata } };
289
+ }
290
+ export function archiveGitRepoV2(id) { const r = _grsV2.get(id); if (!r) throw new Error(`git repo ${id} not found`); _grCheckR(r.status, GIT_REPO_MATURITY_V2.ARCHIVED); r.status = GIT_REPO_MATURITY_V2.ARCHIVED; r.updatedAt = Date.now(); return { ...r, metadata: { ...r.metadata } }; }
291
+ export function decommissionGitRepoV2(id) { const r = _grsV2.get(id); if (!r) throw new Error(`git repo ${id} not found`); _grCheckR(r.status, GIT_REPO_MATURITY_V2.DECOMMISSIONED); const now = Date.now(); r.status = GIT_REPO_MATURITY_V2.DECOMMISSIONED; r.updatedAt = now; if (!r.decommissionedAt) r.decommissionedAt = now; return { ...r, metadata: { ...r.metadata } }; }
292
+ export function touchGitRepoV2(id) { const r = _grsV2.get(id); if (!r) throw new Error(`git repo ${id} not found`); if (_grTerminal.has(r.status)) throw new Error(`cannot touch terminal git repo ${id}`); const now = Date.now(); r.lastTouchedAt = now; r.updatedAt = now; return { ...r, metadata: { ...r.metadata } }; }
293
+ export function getGitRepoV2(id) { const r = _grsV2.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
294
+ export function listGitReposV2() { return [..._grsV2.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
295
+
296
+ function _gcCountPending(repoId) { let n = 0; for (const c of _gcsV2.values()) if (c.repoId === repoId && (c.status === GIT_COMMIT_LIFECYCLE_V2.QUEUED || c.status === GIT_COMMIT_LIFECYCLE_V2.COMMITTING)) n++; return n; }
297
+
298
+ export function createGitCommitV2({ id, repoId, message, metadata } = {}) {
299
+ if (!id || typeof id !== "string") throw new Error("id is required");
300
+ if (!repoId || typeof repoId !== "string") throw new Error("repoId is required");
301
+ if (_gcsV2.has(id)) throw new Error(`git commit ${id} already exists`);
302
+ if (!_grsV2.has(repoId)) throw new Error(`git repo ${repoId} not found`);
303
+ const pending = _gcCountPending(repoId);
304
+ if (pending >= _grMaxPendingCommitsPerRepo) throw new Error(`max pending git commits per repo (${_grMaxPendingCommitsPerRepo}) reached for ${repoId}`);
305
+ const now = Date.now();
306
+ const c = { id, repoId, message: message || "", status: GIT_COMMIT_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
307
+ _gcsV2.set(id, c);
308
+ return { ...c, metadata: { ...c.metadata } };
309
+ }
310
+ function _gcCheckC(from, to) { const a = _gcTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid git commit transition ${from} → ${to}`); }
311
+ export function startGitCommitV2(id) { const c = _gcsV2.get(id); if (!c) throw new Error(`git commit ${id} not found`); _gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.COMMITTING); const now = Date.now(); c.status = GIT_COMMIT_LIFECYCLE_V2.COMMITTING; c.updatedAt = now; if (!c.startedAt) c.startedAt = now; return { ...c, metadata: { ...c.metadata } }; }
312
+ export function commitGitCommitV2(id) { const c = _gcsV2.get(id); if (!c) throw new Error(`git commit ${id} not found`); _gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.COMMITTED); const now = Date.now(); c.status = GIT_COMMIT_LIFECYCLE_V2.COMMITTED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; return { ...c, metadata: { ...c.metadata } }; }
313
+ export function failGitCommitV2(id, reason) { const c = _gcsV2.get(id); if (!c) throw new Error(`git commit ${id} not found`); _gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.FAILED); const now = Date.now(); c.status = GIT_COMMIT_LIFECYCLE_V2.FAILED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; if (reason) c.metadata.failReason = String(reason); return { ...c, metadata: { ...c.metadata } }; }
314
+ export function cancelGitCommitV2(id, reason) { const c = _gcsV2.get(id); if (!c) throw new Error(`git commit ${id} not found`); _gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.CANCELLED); const now = Date.now(); c.status = GIT_COMMIT_LIFECYCLE_V2.CANCELLED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; if (reason) c.metadata.cancelReason = String(reason); return { ...c, metadata: { ...c.metadata } }; }
315
+ export function getGitCommitV2(id) { const c = _gcsV2.get(id); if (!c) return null; return { ...c, metadata: { ...c.metadata } }; }
316
+ export function listGitCommitsV2() { return [..._gcsV2.values()].map((c) => ({ ...c, metadata: { ...c.metadata } })); }
317
+
318
+ export function autoArchiveIdleGitReposV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _grsV2.values()) if (r.status === GIT_REPO_MATURITY_V2.ACTIVE && (t - r.lastTouchedAt) >= _grIdleMs) { r.status = GIT_REPO_MATURITY_V2.ARCHIVED; r.updatedAt = t; flipped.push(r.id); } return { flipped, count: flipped.length }; }
319
+ export function autoFailStuckGitCommitsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const c of _gcsV2.values()) if (c.status === GIT_COMMIT_LIFECYCLE_V2.COMMITTING && c.startedAt != null && (t - c.startedAt) >= _gcStuckMs) { c.status = GIT_COMMIT_LIFECYCLE_V2.FAILED; c.updatedAt = t; if (!c.settledAt) c.settledAt = t; c.metadata.failReason = "auto-fail-stuck"; flipped.push(c.id); } return { flipped, count: flipped.length }; }
320
+
321
+ export function getGitIntegrationGovStatsV2() {
322
+ const reposByStatus = {}; for (const s of Object.values(GIT_REPO_MATURITY_V2)) reposByStatus[s] = 0; for (const r of _grsV2.values()) reposByStatus[r.status]++;
323
+ const commitsByStatus = {}; for (const s of Object.values(GIT_COMMIT_LIFECYCLE_V2)) commitsByStatus[s] = 0; for (const c of _gcsV2.values()) commitsByStatus[c.status]++;
324
+ return { totalGitReposV2: _grsV2.size, totalGitCommitsV2: _gcsV2.size, maxActiveGitReposPerOwner: _grMaxActivePerOwner, maxPendingGitCommitsPerRepo: _grMaxPendingCommitsPerRepo, gitRepoIdleMs: _grIdleMs, gitCommitStuckMs: _gcStuckMs, reposByStatus, commitsByStatus };
325
+ }
@@ -1068,3 +1068,108 @@ export function _resetV2State() {
1068
1068
  _consolidationTimer = null;
1069
1069
  }
1070
1070
  }
1071
+
1072
+
1073
+ // ===== V2 Surface: Hierarchical Memory governance overlay (CLI v0.137.0) =====
1074
+ export const HMEM_TIER_MATURITY_V2 = Object.freeze({
1075
+ PENDING: "pending", ACTIVE: "active", DORMANT: "dormant", RETIRED: "retired",
1076
+ });
1077
+ export const HMEM_PROMOTION_LIFECYCLE_V2 = Object.freeze({
1078
+ QUEUED: "queued", PROMOTING: "promoting", PROMOTED: "promoted", FAILED: "failed", CANCELLED: "cancelled",
1079
+ });
1080
+
1081
+ const _hmemTierTrans = new Map([
1082
+ [HMEM_TIER_MATURITY_V2.PENDING, new Set([HMEM_TIER_MATURITY_V2.ACTIVE, HMEM_TIER_MATURITY_V2.RETIRED])],
1083
+ [HMEM_TIER_MATURITY_V2.ACTIVE, new Set([HMEM_TIER_MATURITY_V2.DORMANT, HMEM_TIER_MATURITY_V2.RETIRED])],
1084
+ [HMEM_TIER_MATURITY_V2.DORMANT, new Set([HMEM_TIER_MATURITY_V2.ACTIVE, HMEM_TIER_MATURITY_V2.RETIRED])],
1085
+ [HMEM_TIER_MATURITY_V2.RETIRED, new Set()],
1086
+ ]);
1087
+ const _hmemTierTerminal = new Set([HMEM_TIER_MATURITY_V2.RETIRED]);
1088
+ const _hmemPromoTrans = new Map([
1089
+ [HMEM_PROMOTION_LIFECYCLE_V2.QUEUED, new Set([HMEM_PROMOTION_LIFECYCLE_V2.PROMOTING, HMEM_PROMOTION_LIFECYCLE_V2.CANCELLED])],
1090
+ [HMEM_PROMOTION_LIFECYCLE_V2.PROMOTING, new Set([HMEM_PROMOTION_LIFECYCLE_V2.PROMOTED, HMEM_PROMOTION_LIFECYCLE_V2.FAILED, HMEM_PROMOTION_LIFECYCLE_V2.CANCELLED])],
1091
+ [HMEM_PROMOTION_LIFECYCLE_V2.PROMOTED, new Set()],
1092
+ [HMEM_PROMOTION_LIFECYCLE_V2.FAILED, new Set()],
1093
+ [HMEM_PROMOTION_LIFECYCLE_V2.CANCELLED, new Set()],
1094
+ ]);
1095
+
1096
+ const _hmemTiers = new Map();
1097
+ const _hmemPromos = new Map();
1098
+ let _hmemMaxActivePerOwner = 12;
1099
+ let _hmemMaxPendingPerTier = 30;
1100
+ let _hmemTierIdleMs = 30 * 24 * 60 * 60 * 1000;
1101
+ let _hmemPromoStuckMs = 5 * 60 * 1000;
1102
+
1103
+ function _hmemPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
1104
+
1105
+ export function setMaxActiveHmemTiersPerOwnerV2(n) { _hmemMaxActivePerOwner = _hmemPos(n, "maxActiveHmemTiersPerOwner"); }
1106
+ export function getMaxActiveHmemTiersPerOwnerV2() { return _hmemMaxActivePerOwner; }
1107
+ export function setMaxPendingHmemPromotionsPerTierV2(n) { _hmemMaxPendingPerTier = _hmemPos(n, "maxPendingHmemPromotionsPerTier"); }
1108
+ export function getMaxPendingHmemPromotionsPerTierV2() { return _hmemMaxPendingPerTier; }
1109
+ export function setHmemTierIdleMsV2(n) { _hmemTierIdleMs = _hmemPos(n, "hmemTierIdleMs"); }
1110
+ export function getHmemTierIdleMsV2() { return _hmemTierIdleMs; }
1111
+ export function setHmemPromotionStuckMsV2(n) { _hmemPromoStuckMs = _hmemPos(n, "hmemPromotionStuckMs"); }
1112
+ export function getHmemPromotionStuckMsV2() { return _hmemPromoStuckMs; }
1113
+
1114
+ export function _resetStateHierarchicalMemoryV2() {
1115
+ _hmemTiers.clear(); _hmemPromos.clear();
1116
+ _hmemMaxActivePerOwner = 12; _hmemMaxPendingPerTier = 30;
1117
+ _hmemTierIdleMs = 30 * 24 * 60 * 60 * 1000; _hmemPromoStuckMs = 5 * 60 * 1000;
1118
+ }
1119
+
1120
+ export function registerHmemTierV2({ id, owner, level, metadata } = {}) {
1121
+ if (!id || typeof id !== "string") throw new Error("id is required");
1122
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
1123
+ if (_hmemTiers.has(id)) throw new Error(`hmem tier ${id} already registered`);
1124
+ const now = Date.now();
1125
+ const t = { id, owner, level: level || "short-term", status: HMEM_TIER_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
1126
+ _hmemTiers.set(id, t);
1127
+ return { ...t, metadata: { ...t.metadata } };
1128
+ }
1129
+ function _hmemCheckT(from, to) { const a = _hmemTierTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid hmem tier transition ${from} → ${to}`); }
1130
+ function _hmemCountActive(owner) { let n = 0; for (const t of _hmemTiers.values()) if (t.owner === owner && t.status === HMEM_TIER_MATURITY_V2.ACTIVE) n++; return n; }
1131
+
1132
+ export function activateHmemTierV2(id) {
1133
+ const t = _hmemTiers.get(id); if (!t) throw new Error(`hmem tier ${id} not found`);
1134
+ _hmemCheckT(t.status, HMEM_TIER_MATURITY_V2.ACTIVE);
1135
+ const recovery = t.status === HMEM_TIER_MATURITY_V2.DORMANT;
1136
+ if (!recovery) { const c = _hmemCountActive(t.owner); if (c >= _hmemMaxActivePerOwner) throw new Error(`max active hmem tiers per owner (${_hmemMaxActivePerOwner}) reached for ${t.owner}`); }
1137
+ const now = Date.now(); t.status = HMEM_TIER_MATURITY_V2.ACTIVE; t.updatedAt = now; t.lastTouchedAt = now; if (!t.activatedAt) t.activatedAt = now;
1138
+ return { ...t, metadata: { ...t.metadata } };
1139
+ }
1140
+ export function dormantHmemTierV2(id) { const t = _hmemTiers.get(id); if (!t) throw new Error(`hmem tier ${id} not found`); _hmemCheckT(t.status, HMEM_TIER_MATURITY_V2.DORMANT); t.status = HMEM_TIER_MATURITY_V2.DORMANT; t.updatedAt = Date.now(); return { ...t, metadata: { ...t.metadata } }; }
1141
+ export function retireHmemTierV2(id) { const t = _hmemTiers.get(id); if (!t) throw new Error(`hmem tier ${id} not found`); _hmemCheckT(t.status, HMEM_TIER_MATURITY_V2.RETIRED); const now = Date.now(); t.status = HMEM_TIER_MATURITY_V2.RETIRED; t.updatedAt = now; if (!t.retiredAt) t.retiredAt = now; return { ...t, metadata: { ...t.metadata } }; }
1142
+ export function touchHmemTierV2(id) { const t = _hmemTiers.get(id); if (!t) throw new Error(`hmem tier ${id} not found`); if (_hmemTierTerminal.has(t.status)) throw new Error(`cannot touch terminal hmem tier ${id}`); const now = Date.now(); t.lastTouchedAt = now; t.updatedAt = now; return { ...t, metadata: { ...t.metadata } }; }
1143
+ export function getHmemTierV2(id) { const t = _hmemTiers.get(id); if (!t) return null; return { ...t, metadata: { ...t.metadata } }; }
1144
+ export function listHmemTiersV2() { return [..._hmemTiers.values()].map((t) => ({ ...t, metadata: { ...t.metadata } })); }
1145
+
1146
+ function _hmemCountPending(tid) { let n = 0; for (const p of _hmemPromos.values()) if (p.tierId === tid && (p.status === HMEM_PROMOTION_LIFECYCLE_V2.QUEUED || p.status === HMEM_PROMOTION_LIFECYCLE_V2.PROMOTING)) n++; return n; }
1147
+
1148
+ export function createHmemPromotionV2({ id, tierId, itemKey, metadata } = {}) {
1149
+ if (!id || typeof id !== "string") throw new Error("id is required");
1150
+ if (!tierId || typeof tierId !== "string") throw new Error("tierId is required");
1151
+ if (_hmemPromos.has(id)) throw new Error(`hmem promotion ${id} already exists`);
1152
+ if (!_hmemTiers.has(tierId)) throw new Error(`hmem tier ${tierId} not found`);
1153
+ const pending = _hmemCountPending(tierId);
1154
+ if (pending >= _hmemMaxPendingPerTier) throw new Error(`max pending hmem promotions per tier (${_hmemMaxPendingPerTier}) reached for ${tierId}`);
1155
+ const now = Date.now();
1156
+ const p = { id, tierId, itemKey: itemKey || "", status: HMEM_PROMOTION_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
1157
+ _hmemPromos.set(id, p);
1158
+ return { ...p, metadata: { ...p.metadata } };
1159
+ }
1160
+ function _hmemCheckP(from, to) { const a = _hmemPromoTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid hmem promotion transition ${from} → ${to}`); }
1161
+ export function startHmemPromotionV2(id) { const p = _hmemPromos.get(id); if (!p) throw new Error(`hmem promotion ${id} not found`); _hmemCheckP(p.status, HMEM_PROMOTION_LIFECYCLE_V2.PROMOTING); const now = Date.now(); p.status = HMEM_PROMOTION_LIFECYCLE_V2.PROMOTING; p.updatedAt = now; if (!p.startedAt) p.startedAt = now; return { ...p, metadata: { ...p.metadata } }; }
1162
+ export function completeHmemPromotionV2(id) { const p = _hmemPromos.get(id); if (!p) throw new Error(`hmem promotion ${id} not found`); _hmemCheckP(p.status, HMEM_PROMOTION_LIFECYCLE_V2.PROMOTED); const now = Date.now(); p.status = HMEM_PROMOTION_LIFECYCLE_V2.PROMOTED; p.updatedAt = now; if (!p.settledAt) p.settledAt = now; return { ...p, metadata: { ...p.metadata } }; }
1163
+ export function failHmemPromotionV2(id, reason) { const p = _hmemPromos.get(id); if (!p) throw new Error(`hmem promotion ${id} not found`); _hmemCheckP(p.status, HMEM_PROMOTION_LIFECYCLE_V2.FAILED); const now = Date.now(); p.status = HMEM_PROMOTION_LIFECYCLE_V2.FAILED; p.updatedAt = now; if (!p.settledAt) p.settledAt = now; if (reason) p.metadata.failReason = String(reason); return { ...p, metadata: { ...p.metadata } }; }
1164
+ export function cancelHmemPromotionV2(id, reason) { const p = _hmemPromos.get(id); if (!p) throw new Error(`hmem promotion ${id} not found`); _hmemCheckP(p.status, HMEM_PROMOTION_LIFECYCLE_V2.CANCELLED); const now = Date.now(); p.status = HMEM_PROMOTION_LIFECYCLE_V2.CANCELLED; p.updatedAt = now; if (!p.settledAt) p.settledAt = now; if (reason) p.metadata.cancelReason = String(reason); return { ...p, metadata: { ...p.metadata } }; }
1165
+ export function getHmemPromotionV2(id) { const p = _hmemPromos.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
1166
+ export function listHmemPromotionsV2() { return [..._hmemPromos.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
1167
+
1168
+ export function autoDormantIdleHmemTiersV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const tier of _hmemTiers.values()) if (tier.status === HMEM_TIER_MATURITY_V2.ACTIVE && (t - tier.lastTouchedAt) >= _hmemTierIdleMs) { tier.status = HMEM_TIER_MATURITY_V2.DORMANT; tier.updatedAt = t; flipped.push(tier.id); } return { flipped, count: flipped.length }; }
1169
+ export function autoFailStuckHmemPromotionsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _hmemPromos.values()) if (p.status === HMEM_PROMOTION_LIFECYCLE_V2.PROMOTING && p.startedAt != null && (t - p.startedAt) >= _hmemPromoStuckMs) { p.status = HMEM_PROMOTION_LIFECYCLE_V2.FAILED; p.updatedAt = t; if (!p.settledAt) p.settledAt = t; p.metadata.failReason = "auto-fail-stuck"; flipped.push(p.id); } return { flipped, count: flipped.length }; }
1170
+
1171
+ export function getHierarchicalMemoryGovStatsV2() {
1172
+ const tiersByStatus = {}; for (const s of Object.values(HMEM_TIER_MATURITY_V2)) tiersByStatus[s] = 0; for (const t of _hmemTiers.values()) tiersByStatus[t.status]++;
1173
+ const promotionsByStatus = {}; for (const s of Object.values(HMEM_PROMOTION_LIFECYCLE_V2)) promotionsByStatus[s] = 0; for (const p of _hmemPromos.values()) promotionsByStatus[p.status]++;
1174
+ return { totalTiersV2: _hmemTiers.size, totalPromotionsV2: _hmemPromos.size, maxActiveHmemTiersPerOwner: _hmemMaxActivePerOwner, maxPendingHmemPromotionsPerTier: _hmemMaxPendingPerTier, hmemTierIdleMs: _hmemTierIdleMs, hmemPromotionStuckMs: _hmemPromoStuckMs, tiersByStatus, promotionsByStatus };
1175
+ }