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
@@ -214,3 +214,108 @@ export async function takeScreenshot(url, outputPath, options = {}) {
214
214
  throw err;
215
215
  }
216
216
  }
217
+
218
+
219
+ // ===== V2 Surface: Browser Automation governance overlay (CLI v0.134.0) =====
220
+ export const BROWSE_TARGET_MATURITY_V2 = Object.freeze({
221
+ PENDING: "pending", ACTIVE: "active", DEGRADED: "degraded", RETIRED: "retired",
222
+ });
223
+ export const BROWSE_ACTION_LIFECYCLE_V2 = Object.freeze({
224
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
225
+ });
226
+
227
+ const _brTargTrans = new Map([
228
+ [BROWSE_TARGET_MATURITY_V2.PENDING, new Set([BROWSE_TARGET_MATURITY_V2.ACTIVE, BROWSE_TARGET_MATURITY_V2.RETIRED])],
229
+ [BROWSE_TARGET_MATURITY_V2.ACTIVE, new Set([BROWSE_TARGET_MATURITY_V2.DEGRADED, BROWSE_TARGET_MATURITY_V2.RETIRED])],
230
+ [BROWSE_TARGET_MATURITY_V2.DEGRADED, new Set([BROWSE_TARGET_MATURITY_V2.ACTIVE, BROWSE_TARGET_MATURITY_V2.RETIRED])],
231
+ [BROWSE_TARGET_MATURITY_V2.RETIRED, new Set()],
232
+ ]);
233
+ const _brTargTerminal = new Set([BROWSE_TARGET_MATURITY_V2.RETIRED]);
234
+ const _brActTrans = new Map([
235
+ [BROWSE_ACTION_LIFECYCLE_V2.QUEUED, new Set([BROWSE_ACTION_LIFECYCLE_V2.RUNNING, BROWSE_ACTION_LIFECYCLE_V2.CANCELLED])],
236
+ [BROWSE_ACTION_LIFECYCLE_V2.RUNNING, new Set([BROWSE_ACTION_LIFECYCLE_V2.COMPLETED, BROWSE_ACTION_LIFECYCLE_V2.FAILED, BROWSE_ACTION_LIFECYCLE_V2.CANCELLED])],
237
+ [BROWSE_ACTION_LIFECYCLE_V2.COMPLETED, new Set()],
238
+ [BROWSE_ACTION_LIFECYCLE_V2.FAILED, new Set()],
239
+ [BROWSE_ACTION_LIFECYCLE_V2.CANCELLED, new Set()],
240
+ ]);
241
+
242
+ const _brTargets = new Map();
243
+ const _brActions = new Map();
244
+ let _brMaxActivePerOwner = 8;
245
+ let _brMaxPendingPerTarget = 20;
246
+ let _brTargetIdleMs = 12 * 60 * 60 * 1000;
247
+ let _brActionStuckMs = 3 * 60 * 1000;
248
+
249
+ function _brPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
250
+
251
+ export function setMaxActiveBrowseTargetsPerOwnerV2(n) { _brMaxActivePerOwner = _brPos(n, "maxActiveBrowseTargetsPerOwner"); }
252
+ export function getMaxActiveBrowseTargetsPerOwnerV2() { return _brMaxActivePerOwner; }
253
+ export function setMaxPendingBrowseActionsPerTargetV2(n) { _brMaxPendingPerTarget = _brPos(n, "maxPendingBrowseActionsPerTarget"); }
254
+ export function getMaxPendingBrowseActionsPerTargetV2() { return _brMaxPendingPerTarget; }
255
+ export function setBrowseTargetIdleMsV2(n) { _brTargetIdleMs = _brPos(n, "browseTargetIdleMs"); }
256
+ export function getBrowseTargetIdleMsV2() { return _brTargetIdleMs; }
257
+ export function setBrowseActionStuckMsV2(n) { _brActionStuckMs = _brPos(n, "browseActionStuckMs"); }
258
+ export function getBrowseActionStuckMsV2() { return _brActionStuckMs; }
259
+
260
+ export function _resetStateBrowserAutomationV2() {
261
+ _brTargets.clear(); _brActions.clear();
262
+ _brMaxActivePerOwner = 8; _brMaxPendingPerTarget = 20;
263
+ _brTargetIdleMs = 12 * 60 * 60 * 1000; _brActionStuckMs = 3 * 60 * 1000;
264
+ }
265
+
266
+ export function registerBrowseTargetV2({ id, owner, url, metadata } = {}) {
267
+ if (!id || typeof id !== "string") throw new Error("id is required");
268
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
269
+ if (_brTargets.has(id)) throw new Error(`browse target ${id} already registered`);
270
+ const now = Date.now();
271
+ const t = { id, owner, url: url || "", status: BROWSE_TARGET_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
272
+ _brTargets.set(id, t);
273
+ return { ...t, metadata: { ...t.metadata } };
274
+ }
275
+ function _brCheckT(from, to) { const a = _brTargTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid browse target transition ${from} → ${to}`); }
276
+ function _brCountActive(owner) { let n = 0; for (const t of _brTargets.values()) if (t.owner === owner && t.status === BROWSE_TARGET_MATURITY_V2.ACTIVE) n++; return n; }
277
+
278
+ export function activateBrowseTargetV2(id) {
279
+ const t = _brTargets.get(id); if (!t) throw new Error(`browse target ${id} not found`);
280
+ _brCheckT(t.status, BROWSE_TARGET_MATURITY_V2.ACTIVE);
281
+ const recovery = t.status === BROWSE_TARGET_MATURITY_V2.DEGRADED;
282
+ if (!recovery) { const a = _brCountActive(t.owner); if (a >= _brMaxActivePerOwner) throw new Error(`max active browse targets per owner (${_brMaxActivePerOwner}) reached for ${t.owner}`); }
283
+ const now = Date.now(); t.status = BROWSE_TARGET_MATURITY_V2.ACTIVE; t.updatedAt = now; t.lastTouchedAt = now; if (!t.activatedAt) t.activatedAt = now;
284
+ return { ...t, metadata: { ...t.metadata } };
285
+ }
286
+ export function degradeBrowseTargetV2(id) { const t = _brTargets.get(id); if (!t) throw new Error(`browse target ${id} not found`); _brCheckT(t.status, BROWSE_TARGET_MATURITY_V2.DEGRADED); t.status = BROWSE_TARGET_MATURITY_V2.DEGRADED; t.updatedAt = Date.now(); return { ...t, metadata: { ...t.metadata } }; }
287
+ export function retireBrowseTargetV2(id) { const t = _brTargets.get(id); if (!t) throw new Error(`browse target ${id} not found`); _brCheckT(t.status, BROWSE_TARGET_MATURITY_V2.RETIRED); const now = Date.now(); t.status = BROWSE_TARGET_MATURITY_V2.RETIRED; t.updatedAt = now; if (!t.retiredAt) t.retiredAt = now; return { ...t, metadata: { ...t.metadata } }; }
288
+ export function touchBrowseTargetV2(id) { const t = _brTargets.get(id); if (!t) throw new Error(`browse target ${id} not found`); if (_brTargTerminal.has(t.status)) throw new Error(`cannot touch terminal browse target ${id}`); const now = Date.now(); t.lastTouchedAt = now; t.updatedAt = now; return { ...t, metadata: { ...t.metadata } }; }
289
+ export function getBrowseTargetV2(id) { const t = _brTargets.get(id); if (!t) return null; return { ...t, metadata: { ...t.metadata } }; }
290
+ export function listBrowseTargetsV2() { return [..._brTargets.values()].map((t) => ({ ...t, metadata: { ...t.metadata } })); }
291
+
292
+ function _brCountPending(tid) { let n = 0; for (const a of _brActions.values()) if (a.targetId === tid && (a.status === BROWSE_ACTION_LIFECYCLE_V2.QUEUED || a.status === BROWSE_ACTION_LIFECYCLE_V2.RUNNING)) n++; return n; }
293
+
294
+ export function createBrowseActionV2({ id, targetId, kind, metadata } = {}) {
295
+ if (!id || typeof id !== "string") throw new Error("id is required");
296
+ if (!targetId || typeof targetId !== "string") throw new Error("targetId is required");
297
+ if (_brActions.has(id)) throw new Error(`browse action ${id} already exists`);
298
+ if (!_brTargets.has(targetId)) throw new Error(`browse target ${targetId} not found`);
299
+ const pending = _brCountPending(targetId);
300
+ if (pending >= _brMaxPendingPerTarget) throw new Error(`max pending browse actions per target (${_brMaxPendingPerTarget}) reached for ${targetId}`);
301
+ const now = Date.now();
302
+ const a = { id, targetId, kind: kind || "fetch", status: BROWSE_ACTION_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
303
+ _brActions.set(id, a);
304
+ return { ...a, metadata: { ...a.metadata } };
305
+ }
306
+ function _brCheckA(from, to) { const al = _brActTrans.get(from); if (!al || !al.has(to)) throw new Error(`invalid browse action transition ${from} → ${to}`); }
307
+ export function startBrowseActionV2(id) { const a = _brActions.get(id); if (!a) throw new Error(`browse action ${id} not found`); _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.RUNNING); const now = Date.now(); a.status = BROWSE_ACTION_LIFECYCLE_V2.RUNNING; a.updatedAt = now; if (!a.startedAt) a.startedAt = now; return { ...a, metadata: { ...a.metadata } }; }
308
+ export function completeBrowseActionV2(id) { const a = _brActions.get(id); if (!a) throw new Error(`browse action ${id} not found`); _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.COMPLETED); const now = Date.now(); a.status = BROWSE_ACTION_LIFECYCLE_V2.COMPLETED; a.updatedAt = now; if (!a.settledAt) a.settledAt = now; return { ...a, metadata: { ...a.metadata } }; }
309
+ export function failBrowseActionV2(id, reason) { const a = _brActions.get(id); if (!a) throw new Error(`browse action ${id} not found`); _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.FAILED); const now = Date.now(); a.status = BROWSE_ACTION_LIFECYCLE_V2.FAILED; a.updatedAt = now; if (!a.settledAt) a.settledAt = now; if (reason) a.metadata.failReason = String(reason); return { ...a, metadata: { ...a.metadata } }; }
310
+ export function cancelBrowseActionV2(id, reason) { const a = _brActions.get(id); if (!a) throw new Error(`browse action ${id} not found`); _brCheckA(a.status, BROWSE_ACTION_LIFECYCLE_V2.CANCELLED); const now = Date.now(); a.status = BROWSE_ACTION_LIFECYCLE_V2.CANCELLED; a.updatedAt = now; if (!a.settledAt) a.settledAt = now; if (reason) a.metadata.cancelReason = String(reason); return { ...a, metadata: { ...a.metadata } }; }
311
+ export function getBrowseActionV2(id) { const a = _brActions.get(id); if (!a) return null; return { ...a, metadata: { ...a.metadata } }; }
312
+ export function listBrowseActionsV2() { return [..._brActions.values()].map((a) => ({ ...a, metadata: { ...a.metadata } })); }
313
+
314
+ export function autoDegradeIdleBrowseTargetsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const tg of _brTargets.values()) if (tg.status === BROWSE_TARGET_MATURITY_V2.ACTIVE && (t - tg.lastTouchedAt) >= _brTargetIdleMs) { tg.status = BROWSE_TARGET_MATURITY_V2.DEGRADED; tg.updatedAt = t; flipped.push(tg.id); } return { flipped, count: flipped.length }; }
315
+ export function autoFailStuckBrowseActionsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const a of _brActions.values()) if (a.status === BROWSE_ACTION_LIFECYCLE_V2.RUNNING && a.startedAt != null && (t - a.startedAt) >= _brActionStuckMs) { a.status = BROWSE_ACTION_LIFECYCLE_V2.FAILED; a.updatedAt = t; if (!a.settledAt) a.settledAt = t; a.metadata.failReason = "auto-fail-stuck"; flipped.push(a.id); } return { flipped, count: flipped.length }; }
316
+
317
+ export function getBrowserAutomationStatsV2() {
318
+ const targetsByStatus = {}; for (const s of Object.values(BROWSE_TARGET_MATURITY_V2)) targetsByStatus[s] = 0; for (const t of _brTargets.values()) targetsByStatus[t.status]++;
319
+ const actionsByStatus = {}; for (const s of Object.values(BROWSE_ACTION_LIFECYCLE_V2)) actionsByStatus[s] = 0; for (const a of _brActions.values()) actionsByStatus[a.status]++;
320
+ return { totalTargetsV2: _brTargets.size, totalActionsV2: _brActions.size, maxActiveBrowseTargetsPerOwner: _brMaxActivePerOwner, maxPendingBrowseActionsPerTarget: _brMaxPendingPerTarget, browseTargetIdleMs: _brTargetIdleMs, browseActionStuckMs: _brActionStuckMs, targetsByStatus, actionsByStatus };
321
+ }
@@ -598,3 +598,108 @@ export function generateFrameworkReport(frameworkId, opts = {}) {
598
598
  export function _resetState() {
599
599
  _customTemplates.clear();
600
600
  }
601
+
602
+
603
+ // ===== V2 Surface: Compliance Framework Reporter governance overlay (CLI v0.138.0) =====
604
+ export const COMPLIANCE_FW_MATURITY_V2 = Object.freeze({
605
+ PENDING: "pending", ACTIVE: "active", DEPRECATED: "deprecated", ARCHIVED: "archived",
606
+ });
607
+ export const COMPLIANCE_FW_REPORT_LIFECYCLE_V2 = Object.freeze({
608
+ QUEUED: "queued", GENERATING: "generating", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
609
+ });
610
+
611
+ const _cfwTrans = new Map([
612
+ [COMPLIANCE_FW_MATURITY_V2.PENDING, new Set([COMPLIANCE_FW_MATURITY_V2.ACTIVE, COMPLIANCE_FW_MATURITY_V2.ARCHIVED])],
613
+ [COMPLIANCE_FW_MATURITY_V2.ACTIVE, new Set([COMPLIANCE_FW_MATURITY_V2.DEPRECATED, COMPLIANCE_FW_MATURITY_V2.ARCHIVED])],
614
+ [COMPLIANCE_FW_MATURITY_V2.DEPRECATED, new Set([COMPLIANCE_FW_MATURITY_V2.ACTIVE, COMPLIANCE_FW_MATURITY_V2.ARCHIVED])],
615
+ [COMPLIANCE_FW_MATURITY_V2.ARCHIVED, new Set()],
616
+ ]);
617
+ const _cfwTerminal = new Set([COMPLIANCE_FW_MATURITY_V2.ARCHIVED]);
618
+ const _cfwRepTrans = new Map([
619
+ [COMPLIANCE_FW_REPORT_LIFECYCLE_V2.QUEUED, new Set([COMPLIANCE_FW_REPORT_LIFECYCLE_V2.GENERATING, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.CANCELLED])],
620
+ [COMPLIANCE_FW_REPORT_LIFECYCLE_V2.GENERATING, new Set([COMPLIANCE_FW_REPORT_LIFECYCLE_V2.COMPLETED, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.FAILED, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.CANCELLED])],
621
+ [COMPLIANCE_FW_REPORT_LIFECYCLE_V2.COMPLETED, new Set()],
622
+ [COMPLIANCE_FW_REPORT_LIFECYCLE_V2.FAILED, new Set()],
623
+ [COMPLIANCE_FW_REPORT_LIFECYCLE_V2.CANCELLED, new Set()],
624
+ ]);
625
+
626
+ const _cfwsV2 = new Map();
627
+ const _cfwReports = new Map();
628
+ let _cfwMaxActivePerOwner = 8;
629
+ let _cfwMaxPendingReportsPerFw = 15;
630
+ let _cfwIdleMs = 90 * 24 * 60 * 60 * 1000;
631
+ let _cfwReportStuckMs = 10 * 60 * 1000;
632
+
633
+ function _cfwPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
634
+
635
+ export function setMaxActiveComplianceFwsPerOwnerV2(n) { _cfwMaxActivePerOwner = _cfwPos(n, "maxActiveComplianceFwsPerOwner"); }
636
+ export function getMaxActiveComplianceFwsPerOwnerV2() { return _cfwMaxActivePerOwner; }
637
+ export function setMaxPendingComplianceFwReportsPerFwV2(n) { _cfwMaxPendingReportsPerFw = _cfwPos(n, "maxPendingComplianceFwReportsPerFw"); }
638
+ export function getMaxPendingComplianceFwReportsPerFwV2() { return _cfwMaxPendingReportsPerFw; }
639
+ export function setComplianceFwIdleMsV2(n) { _cfwIdleMs = _cfwPos(n, "complianceFwIdleMs"); }
640
+ export function getComplianceFwIdleMsV2() { return _cfwIdleMs; }
641
+ export function setComplianceFwReportStuckMsV2(n) { _cfwReportStuckMs = _cfwPos(n, "complianceFwReportStuckMs"); }
642
+ export function getComplianceFwReportStuckMsV2() { return _cfwReportStuckMs; }
643
+
644
+ export function _resetStateComplianceFwReporterV2() {
645
+ _cfwsV2.clear(); _cfwReports.clear();
646
+ _cfwMaxActivePerOwner = 8; _cfwMaxPendingReportsPerFw = 15;
647
+ _cfwIdleMs = 90 * 24 * 60 * 60 * 1000; _cfwReportStuckMs = 10 * 60 * 1000;
648
+ }
649
+
650
+ export function registerComplianceFwV2({ id, owner, name, metadata } = {}) {
651
+ if (!id || typeof id !== "string") throw new Error("id is required");
652
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
653
+ if (_cfwsV2.has(id)) throw new Error(`compliance framework ${id} already registered`);
654
+ const now = Date.now();
655
+ const f = { id, owner, name: name || id, status: COMPLIANCE_FW_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
656
+ _cfwsV2.set(id, f);
657
+ return { ...f, metadata: { ...f.metadata } };
658
+ }
659
+ function _cfwCheckF(from, to) { const a = _cfwTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid compliance framework transition ${from} → ${to}`); }
660
+ function _cfwCountActive(owner) { let n = 0; for (const f of _cfwsV2.values()) if (f.owner === owner && f.status === COMPLIANCE_FW_MATURITY_V2.ACTIVE) n++; return n; }
661
+
662
+ export function activateComplianceFwV2(id) {
663
+ const f = _cfwsV2.get(id); if (!f) throw new Error(`compliance framework ${id} not found`);
664
+ _cfwCheckF(f.status, COMPLIANCE_FW_MATURITY_V2.ACTIVE);
665
+ const recovery = f.status === COMPLIANCE_FW_MATURITY_V2.DEPRECATED;
666
+ if (!recovery) { const c = _cfwCountActive(f.owner); if (c >= _cfwMaxActivePerOwner) throw new Error(`max active compliance frameworks per owner (${_cfwMaxActivePerOwner}) reached for ${f.owner}`); }
667
+ const now = Date.now(); f.status = COMPLIANCE_FW_MATURITY_V2.ACTIVE; f.updatedAt = now; f.lastTouchedAt = now; if (!f.activatedAt) f.activatedAt = now;
668
+ return { ...f, metadata: { ...f.metadata } };
669
+ }
670
+ export function deprecateComplianceFwV2(id) { const f = _cfwsV2.get(id); if (!f) throw new Error(`compliance framework ${id} not found`); _cfwCheckF(f.status, COMPLIANCE_FW_MATURITY_V2.DEPRECATED); f.status = COMPLIANCE_FW_MATURITY_V2.DEPRECATED; f.updatedAt = Date.now(); return { ...f, metadata: { ...f.metadata } }; }
671
+ export function archiveComplianceFwV2(id) { const f = _cfwsV2.get(id); if (!f) throw new Error(`compliance framework ${id} not found`); _cfwCheckF(f.status, COMPLIANCE_FW_MATURITY_V2.ARCHIVED); const now = Date.now(); f.status = COMPLIANCE_FW_MATURITY_V2.ARCHIVED; f.updatedAt = now; if (!f.archivedAt) f.archivedAt = now; return { ...f, metadata: { ...f.metadata } }; }
672
+ export function touchComplianceFwV2(id) { const f = _cfwsV2.get(id); if (!f) throw new Error(`compliance framework ${id} not found`); if (_cfwTerminal.has(f.status)) throw new Error(`cannot touch terminal compliance framework ${id}`); const now = Date.now(); f.lastTouchedAt = now; f.updatedAt = now; return { ...f, metadata: { ...f.metadata } }; }
673
+ export function getComplianceFwV2(id) { const f = _cfwsV2.get(id); if (!f) return null; return { ...f, metadata: { ...f.metadata } }; }
674
+ export function listComplianceFwsV2() { return [..._cfwsV2.values()].map((f) => ({ ...f, metadata: { ...f.metadata } })); }
675
+
676
+ function _cfwCountPendingReports(frameworkId) { let n = 0; for (const r of _cfwReports.values()) if (r.frameworkId === frameworkId && (r.status === COMPLIANCE_FW_REPORT_LIFECYCLE_V2.QUEUED || r.status === COMPLIANCE_FW_REPORT_LIFECYCLE_V2.GENERATING)) n++; return n; }
677
+
678
+ export function createComplianceFwReportV2({ id, frameworkId, format, metadata } = {}) {
679
+ if (!id || typeof id !== "string") throw new Error("id is required");
680
+ if (!frameworkId || typeof frameworkId !== "string") throw new Error("frameworkId is required");
681
+ if (_cfwReports.has(id)) throw new Error(`compliance framework report ${id} already exists`);
682
+ if (!_cfwsV2.has(frameworkId)) throw new Error(`compliance framework ${frameworkId} not found`);
683
+ const pending = _cfwCountPendingReports(frameworkId);
684
+ if (pending >= _cfwMaxPendingReportsPerFw) throw new Error(`max pending compliance framework reports per framework (${_cfwMaxPendingReportsPerFw}) reached for ${frameworkId}`);
685
+ const now = Date.now();
686
+ const r = { id, frameworkId, format: format || "markdown", status: COMPLIANCE_FW_REPORT_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
687
+ _cfwReports.set(id, r);
688
+ return { ...r, metadata: { ...r.metadata } };
689
+ }
690
+ function _cfwCheckR(from, to) { const a = _cfwRepTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid compliance framework report transition ${from} → ${to}`); }
691
+ export function startComplianceFwReportV2(id) { const r = _cfwReports.get(id); if (!r) throw new Error(`compliance framework report ${id} not found`); _cfwCheckR(r.status, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.GENERATING); const now = Date.now(); r.status = COMPLIANCE_FW_REPORT_LIFECYCLE_V2.GENERATING; r.updatedAt = now; if (!r.startedAt) r.startedAt = now; return { ...r, metadata: { ...r.metadata } }; }
692
+ export function completeComplianceFwReportV2(id) { const r = _cfwReports.get(id); if (!r) throw new Error(`compliance framework report ${id} not found`); _cfwCheckR(r.status, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.COMPLETED); const now = Date.now(); r.status = COMPLIANCE_FW_REPORT_LIFECYCLE_V2.COMPLETED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; return { ...r, metadata: { ...r.metadata } }; }
693
+ export function failComplianceFwReportV2(id, reason) { const r = _cfwReports.get(id); if (!r) throw new Error(`compliance framework report ${id} not found`); _cfwCheckR(r.status, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.FAILED); const now = Date.now(); r.status = COMPLIANCE_FW_REPORT_LIFECYCLE_V2.FAILED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.failReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
694
+ export function cancelComplianceFwReportV2(id, reason) { const r = _cfwReports.get(id); if (!r) throw new Error(`compliance framework report ${id} not found`); _cfwCheckR(r.status, COMPLIANCE_FW_REPORT_LIFECYCLE_V2.CANCELLED); const now = Date.now(); r.status = COMPLIANCE_FW_REPORT_LIFECYCLE_V2.CANCELLED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.cancelReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
695
+ export function getComplianceFwReportV2(id) { const r = _cfwReports.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
696
+ export function listComplianceFwReportsV2() { return [..._cfwReports.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
697
+
698
+ export function autoDeprecateIdleComplianceFwsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const f of _cfwsV2.values()) if (f.status === COMPLIANCE_FW_MATURITY_V2.ACTIVE && (t - f.lastTouchedAt) >= _cfwIdleMs) { f.status = COMPLIANCE_FW_MATURITY_V2.DEPRECATED; f.updatedAt = t; flipped.push(f.id); } return { flipped, count: flipped.length }; }
699
+ export function autoFailStuckComplianceFwReportsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _cfwReports.values()) if (r.status === COMPLIANCE_FW_REPORT_LIFECYCLE_V2.GENERATING && r.startedAt != null && (t - r.startedAt) >= _cfwReportStuckMs) { r.status = COMPLIANCE_FW_REPORT_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 }; }
700
+
701
+ export function getComplianceFwReporterGovStatsV2() {
702
+ const fwsByStatus = {}; for (const s of Object.values(COMPLIANCE_FW_MATURITY_V2)) fwsByStatus[s] = 0; for (const f of _cfwsV2.values()) fwsByStatus[f.status]++;
703
+ const reportsByStatus = {}; for (const s of Object.values(COMPLIANCE_FW_REPORT_LIFECYCLE_V2)) reportsByStatus[s] = 0; for (const r of _cfwReports.values()) reportsByStatus[r.status]++;
704
+ return { totalComplianceFwsV2: _cfwsV2.size, totalComplianceFwReportsV2: _cfwReports.size, maxActiveComplianceFwsPerOwner: _cfwMaxActivePerOwner, maxPendingComplianceFwReportsPerFw: _cfwMaxPendingReportsPerFw, complianceFwIdleMs: _cfwIdleMs, complianceFwReportStuckMs: _cfwReportStuckMs, fwsByStatus, reportsByStatus };
705
+ }
@@ -3,3 +3,84 @@ export {
3
3
  getCompressionTelemetrySummary,
4
4
  resetCompressionTelemetry,
5
5
  } from "../harness/compression-telemetry.js";
6
+
7
+
8
+ // =====================================================================
9
+ // Compression Telemetry V2 governance overlay
10
+ // =====================================================================
11
+ export const COMPT_PROFILE_MATURITY_V2 = Object.freeze({ PENDING: "pending", ACTIVE: "active", STALE: "stale", ARCHIVED: "archived" });
12
+ export const COMPT_SAMPLE_LIFECYCLE_V2 = Object.freeze({ QUEUED: "queued", RECORDING: "recording", RECORDED: "recorded", FAILED: "failed", CANCELLED: "cancelled" });
13
+ const _comptPTrans = new Map([
14
+ [COMPT_PROFILE_MATURITY_V2.PENDING, new Set([COMPT_PROFILE_MATURITY_V2.ACTIVE, COMPT_PROFILE_MATURITY_V2.ARCHIVED])],
15
+ [COMPT_PROFILE_MATURITY_V2.ACTIVE, new Set([COMPT_PROFILE_MATURITY_V2.STALE, COMPT_PROFILE_MATURITY_V2.ARCHIVED])],
16
+ [COMPT_PROFILE_MATURITY_V2.STALE, new Set([COMPT_PROFILE_MATURITY_V2.ACTIVE, COMPT_PROFILE_MATURITY_V2.ARCHIVED])],
17
+ [COMPT_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
18
+ ]);
19
+ const _comptPTerminal = new Set([COMPT_PROFILE_MATURITY_V2.ARCHIVED]);
20
+ const _comptJTrans = new Map([
21
+ [COMPT_SAMPLE_LIFECYCLE_V2.QUEUED, new Set([COMPT_SAMPLE_LIFECYCLE_V2.RECORDING, COMPT_SAMPLE_LIFECYCLE_V2.CANCELLED])],
22
+ [COMPT_SAMPLE_LIFECYCLE_V2.RECORDING, new Set([COMPT_SAMPLE_LIFECYCLE_V2.RECORDED, COMPT_SAMPLE_LIFECYCLE_V2.FAILED, COMPT_SAMPLE_LIFECYCLE_V2.CANCELLED])],
23
+ [COMPT_SAMPLE_LIFECYCLE_V2.RECORDED, new Set()],
24
+ [COMPT_SAMPLE_LIFECYCLE_V2.FAILED, new Set()],
25
+ [COMPT_SAMPLE_LIFECYCLE_V2.CANCELLED, new Set()],
26
+ ]);
27
+ const _comptPsV2 = new Map();
28
+ const _comptJsV2 = new Map();
29
+ let _comptMaxActive = 10, _comptMaxPending = 30, _comptIdleMs = 30 * 24 * 60 * 60 * 1000, _comptStuckMs = 30 * 1000;
30
+ function _comptPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
31
+ function _comptCheckP(from, to) { const a = _comptPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid compt profile transition ${from} → ${to}`); }
32
+ function _comptCheckJ(from, to) { const a = _comptJTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid compt sample transition ${from} → ${to}`); }
33
+ function _comptCountActive(owner) { let c = 0; for (const p of _comptPsV2.values()) if (p.owner === owner && p.status === COMPT_PROFILE_MATURITY_V2.ACTIVE) c++; return c; }
34
+ function _comptCountPending(profileId) { let c = 0; for (const j of _comptJsV2.values()) if (j.profileId === profileId && (j.status === COMPT_SAMPLE_LIFECYCLE_V2.QUEUED || j.status === COMPT_SAMPLE_LIFECYCLE_V2.RECORDING)) c++; return c; }
35
+ export function setMaxActiveComptProfilesPerOwnerV2(n) { _comptMaxActive = _comptPos(n, "maxActiveComptProfilesPerOwner"); }
36
+ export function getMaxActiveComptProfilesPerOwnerV2() { return _comptMaxActive; }
37
+ export function setMaxPendingComptSamplesPerProfileV2(n) { _comptMaxPending = _comptPos(n, "maxPendingComptSamplesPerProfile"); }
38
+ export function getMaxPendingComptSamplesPerProfileV2() { return _comptMaxPending; }
39
+ export function setComptProfileIdleMsV2(n) { _comptIdleMs = _comptPos(n, "comptProfileIdleMs"); }
40
+ export function getComptProfileIdleMsV2() { return _comptIdleMs; }
41
+ export function setComptSampleStuckMsV2(n) { _comptStuckMs = _comptPos(n, "comptSampleStuckMs"); }
42
+ export function getComptSampleStuckMsV2() { return _comptStuckMs; }
43
+ export function _resetStateCompressionTelemetryV2() { _comptPsV2.clear(); _comptJsV2.clear(); _comptMaxActive = 10; _comptMaxPending = 30; _comptIdleMs = 30 * 24 * 60 * 60 * 1000; _comptStuckMs = 30 * 1000; }
44
+ export function registerComptProfileV2({ id, owner, kind, metadata } = {}) {
45
+ if (!id || !owner) throw new Error("id and owner required");
46
+ if (_comptPsV2.has(id)) throw new Error(`compt profile ${id} already exists`);
47
+ const now = Date.now();
48
+ const p = { id, owner, kind: kind || "default", status: COMPT_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, lastTouchedAt: now, activatedAt: null, archivedAt: null, metadata: { ...(metadata || {}) } };
49
+ _comptPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
50
+ }
51
+ export function activateComptProfileV2(id) {
52
+ const p = _comptPsV2.get(id); if (!p) throw new Error(`compt profile ${id} not found`);
53
+ const isInitial = p.status === COMPT_PROFILE_MATURITY_V2.PENDING;
54
+ _comptCheckP(p.status, COMPT_PROFILE_MATURITY_V2.ACTIVE);
55
+ if (isInitial && _comptCountActive(p.owner) >= _comptMaxActive) throw new Error(`max active compt profiles for owner ${p.owner} reached`);
56
+ const now = Date.now(); p.status = COMPT_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now;
57
+ if (!p.activatedAt) p.activatedAt = now;
58
+ return { ...p, metadata: { ...p.metadata } };
59
+ }
60
+ export function staleComptProfileV2(id) { const p = _comptPsV2.get(id); if (!p) throw new Error(`compt profile ${id} not found`); _comptCheckP(p.status, COMPT_PROFILE_MATURITY_V2.STALE); p.status = COMPT_PROFILE_MATURITY_V2.STALE; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
61
+ export function archiveComptProfileV2(id) { const p = _comptPsV2.get(id); if (!p) throw new Error(`compt profile ${id} not found`); _comptCheckP(p.status, COMPT_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = COMPT_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
62
+ export function touchComptProfileV2(id) { const p = _comptPsV2.get(id); if (!p) throw new Error(`compt profile ${id} not found`); if (_comptPTerminal.has(p.status)) throw new Error(`cannot touch terminal compt profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
63
+ export function getComptProfileV2(id) { const p = _comptPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
64
+ export function listComptProfilesV2() { return [..._comptPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
65
+ export function createComptSampleV2({ id, profileId, metric, metadata } = {}) {
66
+ if (!id || !profileId) throw new Error("id and profileId required");
67
+ if (_comptJsV2.has(id)) throw new Error(`compt sample ${id} already exists`);
68
+ if (!_comptPsV2.has(profileId)) throw new Error(`compt profile ${profileId} not found`);
69
+ if (_comptCountPending(profileId) >= _comptMaxPending) throw new Error(`max pending compt samples for profile ${profileId} reached`);
70
+ const now = Date.now();
71
+ const j = { id, profileId, metric: metric || "", status: COMPT_SAMPLE_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
72
+ _comptJsV2.set(id, j); return { ...j, metadata: { ...j.metadata } };
73
+ }
74
+ export function recordingComptSampleV2(id) { const j = _comptJsV2.get(id); if (!j) throw new Error(`compt sample ${id} not found`); _comptCheckJ(j.status, COMPT_SAMPLE_LIFECYCLE_V2.RECORDING); const now = Date.now(); j.status = COMPT_SAMPLE_LIFECYCLE_V2.RECORDING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
75
+ export function recordComptSampleV2(id) { const j = _comptJsV2.get(id); if (!j) throw new Error(`compt sample ${id} not found`); _comptCheckJ(j.status, COMPT_SAMPLE_LIFECYCLE_V2.RECORDED); const now = Date.now(); j.status = COMPT_SAMPLE_LIFECYCLE_V2.RECORDED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
76
+ export function failComptSampleV2(id, reason) { const j = _comptJsV2.get(id); if (!j) throw new Error(`compt sample ${id} not found`); _comptCheckJ(j.status, COMPT_SAMPLE_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = COMPT_SAMPLE_LIFECYCLE_V2.FAILED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.failReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
77
+ export function cancelComptSampleV2(id, reason) { const j = _comptJsV2.get(id); if (!j) throw new Error(`compt sample ${id} not found`); _comptCheckJ(j.status, COMPT_SAMPLE_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = COMPT_SAMPLE_LIFECYCLE_V2.CANCELLED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.cancelReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
78
+ export function getComptSampleV2(id) { const j = _comptJsV2.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
79
+ export function listComptSamplesV2() { return [..._comptJsV2.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
80
+ export function autoStaleIdleComptProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _comptPsV2.values()) if (p.status === COMPT_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _comptIdleMs) { p.status = COMPT_PROFILE_MATURITY_V2.STALE; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
81
+ export function autoFailStuckComptSamplesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _comptJsV2.values()) if (j.status === COMPT_SAMPLE_LIFECYCLE_V2.RECORDING && j.startedAt != null && (t - j.startedAt) >= _comptStuckMs) { j.status = COMPT_SAMPLE_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 }; }
82
+ export function getCompressionTelemetryGovStatsV2() {
83
+ const profilesByStatus = {}; for (const v of Object.values(COMPT_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _comptPsV2.values()) profilesByStatus[p.status]++;
84
+ const samplesByStatus = {}; for (const v of Object.values(COMPT_SAMPLE_LIFECYCLE_V2)) samplesByStatus[v] = 0; for (const j of _comptJsV2.values()) samplesByStatus[j.status]++;
85
+ return { totalComptProfilesV2: _comptPsV2.size, totalComptSamplesV2: _comptJsV2.size, maxActiveComptProfilesPerOwner: _comptMaxActive, maxPendingComptSamplesPerProfile: _comptMaxPending, comptProfileIdleMs: _comptIdleMs, comptSampleStuckMs: _comptStuckMs, profilesByStatus, samplesByStatus };
86
+ }
@@ -203,3 +203,108 @@ export class CLIContentRecommender {
203
203
  return matrix;
204
204
  }
205
205
  }
206
+
207
+
208
+ // ===== V2 Surface: Content Recommender governance overlay (CLI v0.139.0) =====
209
+ export const RECOMMENDER_PROFILE_MATURITY_V2 = Object.freeze({
210
+ PENDING: "pending", ACTIVE: "active", STALE: "stale", ARCHIVED: "archived",
211
+ });
212
+ export const RECOMMENDATION_JOB_LIFECYCLE_V2 = Object.freeze({
213
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
214
+ });
215
+
216
+ const _crpTrans = new Map([
217
+ [RECOMMENDER_PROFILE_MATURITY_V2.PENDING, new Set([RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE, RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED])],
218
+ [RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE, new Set([RECOMMENDER_PROFILE_MATURITY_V2.STALE, RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED])],
219
+ [RECOMMENDER_PROFILE_MATURITY_V2.STALE, new Set([RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE, RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED])],
220
+ [RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
221
+ ]);
222
+ const _crpTerminal = new Set([RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED]);
223
+ const _crjTrans = new Map([
224
+ [RECOMMENDATION_JOB_LIFECYCLE_V2.QUEUED, new Set([RECOMMENDATION_JOB_LIFECYCLE_V2.RUNNING, RECOMMENDATION_JOB_LIFECYCLE_V2.CANCELLED])],
225
+ [RECOMMENDATION_JOB_LIFECYCLE_V2.RUNNING, new Set([RECOMMENDATION_JOB_LIFECYCLE_V2.COMPLETED, RECOMMENDATION_JOB_LIFECYCLE_V2.FAILED, RECOMMENDATION_JOB_LIFECYCLE_V2.CANCELLED])],
226
+ [RECOMMENDATION_JOB_LIFECYCLE_V2.COMPLETED, new Set()],
227
+ [RECOMMENDATION_JOB_LIFECYCLE_V2.FAILED, new Set()],
228
+ [RECOMMENDATION_JOB_LIFECYCLE_V2.CANCELLED, new Set()],
229
+ ]);
230
+
231
+ const _crpsV2 = new Map();
232
+ const _crjsV2 = new Map();
233
+ let _crpMaxActivePerOwner = 8;
234
+ let _crpMaxPendingJobsPerProfile = 10;
235
+ let _crpIdleMs = 7 * 24 * 60 * 60 * 1000;
236
+ let _crjStuckMs = 5 * 60 * 1000;
237
+
238
+ function _crpPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
239
+
240
+ export function setMaxActiveRecommenderProfilesPerOwnerV2(n) { _crpMaxActivePerOwner = _crpPos(n, "maxActiveRecommenderProfilesPerOwner"); }
241
+ export function getMaxActiveRecommenderProfilesPerOwnerV2() { return _crpMaxActivePerOwner; }
242
+ export function setMaxPendingRecommendationJobsPerProfileV2(n) { _crpMaxPendingJobsPerProfile = _crpPos(n, "maxPendingRecommendationJobsPerProfile"); }
243
+ export function getMaxPendingRecommendationJobsPerProfileV2() { return _crpMaxPendingJobsPerProfile; }
244
+ export function setRecommenderProfileIdleMsV2(n) { _crpIdleMs = _crpPos(n, "recommenderProfileIdleMs"); }
245
+ export function getRecommenderProfileIdleMsV2() { return _crpIdleMs; }
246
+ export function setRecommendationJobStuckMsV2(n) { _crjStuckMs = _crpPos(n, "recommendationJobStuckMs"); }
247
+ export function getRecommendationJobStuckMsV2() { return _crjStuckMs; }
248
+
249
+ export function _resetStateContentRecommenderV2() {
250
+ _crpsV2.clear(); _crjsV2.clear();
251
+ _crpMaxActivePerOwner = 8; _crpMaxPendingJobsPerProfile = 10;
252
+ _crpIdleMs = 7 * 24 * 60 * 60 * 1000; _crjStuckMs = 5 * 60 * 1000;
253
+ }
254
+
255
+ export function registerRecommenderProfileV2({ id, owner, strategy, metadata } = {}) {
256
+ if (!id || typeof id !== "string") throw new Error("id is required");
257
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
258
+ if (_crpsV2.has(id)) throw new Error(`recommender profile ${id} already registered`);
259
+ const now = Date.now();
260
+ const p = { id, owner, strategy: strategy || "tfidf", status: RECOMMENDER_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
261
+ _crpsV2.set(id, p);
262
+ return { ...p, metadata: { ...p.metadata } };
263
+ }
264
+ function _crpCheckP(from, to) { const a = _crpTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid recommender profile transition ${from} → ${to}`); }
265
+ function _crpCountActive(owner) { let n = 0; for (const p of _crpsV2.values()) if (p.owner === owner && p.status === RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
266
+
267
+ export function activateRecommenderProfileV2(id) {
268
+ const p = _crpsV2.get(id); if (!p) throw new Error(`recommender profile ${id} not found`);
269
+ _crpCheckP(p.status, RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE);
270
+ const recovery = p.status === RECOMMENDER_PROFILE_MATURITY_V2.STALE;
271
+ if (!recovery) { const c = _crpCountActive(p.owner); if (c >= _crpMaxActivePerOwner) throw new Error(`max active recommender profiles per owner (${_crpMaxActivePerOwner}) reached for ${p.owner}`); }
272
+ const now = Date.now(); p.status = RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
273
+ return { ...p, metadata: { ...p.metadata } };
274
+ }
275
+ export function staleRecommenderProfileV2(id) { const p = _crpsV2.get(id); if (!p) throw new Error(`recommender profile ${id} not found`); _crpCheckP(p.status, RECOMMENDER_PROFILE_MATURITY_V2.STALE); p.status = RECOMMENDER_PROFILE_MATURITY_V2.STALE; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
276
+ export function archiveRecommenderProfileV2(id) { const p = _crpsV2.get(id); if (!p) throw new Error(`recommender profile ${id} not found`); _crpCheckP(p.status, RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = RECOMMENDER_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
277
+ export function touchRecommenderProfileV2(id) { const p = _crpsV2.get(id); if (!p) throw new Error(`recommender profile ${id} not found`); if (_crpTerminal.has(p.status)) throw new Error(`cannot touch terminal recommender profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
278
+ export function getRecommenderProfileV2(id) { const p = _crpsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
279
+ export function listRecommenderProfilesV2() { return [..._crpsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
280
+
281
+ function _crjCountPending(profileId) { let n = 0; for (const j of _crjsV2.values()) if (j.profileId === profileId && (j.status === RECOMMENDATION_JOB_LIFECYCLE_V2.QUEUED || j.status === RECOMMENDATION_JOB_LIFECYCLE_V2.RUNNING)) n++; return n; }
282
+
283
+ export function createRecommendationJobV2({ id, profileId, query, metadata } = {}) {
284
+ if (!id || typeof id !== "string") throw new Error("id is required");
285
+ if (!profileId || typeof profileId !== "string") throw new Error("profileId is required");
286
+ if (_crjsV2.has(id)) throw new Error(`recommendation job ${id} already exists`);
287
+ if (!_crpsV2.has(profileId)) throw new Error(`recommender profile ${profileId} not found`);
288
+ const pending = _crjCountPending(profileId);
289
+ if (pending >= _crpMaxPendingJobsPerProfile) throw new Error(`max pending recommendation jobs per profile (${_crpMaxPendingJobsPerProfile}) reached for ${profileId}`);
290
+ const now = Date.now();
291
+ const j = { id, profileId, query: query || "", status: RECOMMENDATION_JOB_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
292
+ _crjsV2.set(id, j);
293
+ return { ...j, metadata: { ...j.metadata } };
294
+ }
295
+ function _crjCheckJ(from, to) { const a = _crjTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid recommendation job transition ${from} → ${to}`); }
296
+ export function startRecommendationJobV2(id) { const j = _crjsV2.get(id); if (!j) throw new Error(`recommendation job ${id} not found`); _crjCheckJ(j.status, RECOMMENDATION_JOB_LIFECYCLE_V2.RUNNING); const now = Date.now(); j.status = RECOMMENDATION_JOB_LIFECYCLE_V2.RUNNING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
297
+ export function completeRecommendationJobV2(id) { const j = _crjsV2.get(id); if (!j) throw new Error(`recommendation job ${id} not found`); _crjCheckJ(j.status, RECOMMENDATION_JOB_LIFECYCLE_V2.COMPLETED); const now = Date.now(); j.status = RECOMMENDATION_JOB_LIFECYCLE_V2.COMPLETED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
298
+ export function failRecommendationJobV2(id, reason) { const j = _crjsV2.get(id); if (!j) throw new Error(`recommendation job ${id} not found`); _crjCheckJ(j.status, RECOMMENDATION_JOB_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = RECOMMENDATION_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 } }; }
299
+ export function cancelRecommendationJobV2(id, reason) { const j = _crjsV2.get(id); if (!j) throw new Error(`recommendation job ${id} not found`); _crjCheckJ(j.status, RECOMMENDATION_JOB_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = RECOMMENDATION_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 } }; }
300
+ export function getRecommendationJobV2(id) { const j = _crjsV2.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
301
+ export function listRecommendationJobsV2() { return [..._crjsV2.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
302
+
303
+ export function autoStaleIdleRecommenderProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _crpsV2.values()) if (p.status === RECOMMENDER_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _crpIdleMs) { p.status = RECOMMENDER_PROFILE_MATURITY_V2.STALE; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
304
+ export function autoFailStuckRecommendationJobsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _crjsV2.values()) if (j.status === RECOMMENDATION_JOB_LIFECYCLE_V2.RUNNING && j.startedAt != null && (t - j.startedAt) >= _crjStuckMs) { j.status = RECOMMENDATION_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 }; }
305
+
306
+ export function getContentRecommenderGovStatsV2() {
307
+ const profilesByStatus = {}; for (const s of Object.values(RECOMMENDER_PROFILE_MATURITY_V2)) profilesByStatus[s] = 0; for (const p of _crpsV2.values()) profilesByStatus[p.status]++;
308
+ const jobsByStatus = {}; for (const s of Object.values(RECOMMENDATION_JOB_LIFECYCLE_V2)) jobsByStatus[s] = 0; for (const j of _crjsV2.values()) jobsByStatus[j.status]++;
309
+ return { totalRecommenderProfilesV2: _crpsV2.size, totalRecommendationJobsV2: _crjsV2.size, maxActiveRecommenderProfilesPerOwner: _crpMaxActivePerOwner, maxPendingRecommendationJobsPerProfile: _crpMaxPendingJobsPerProfile, recommenderProfileIdleMs: _crpIdleMs, recommendationJobStuckMs: _crjStuckMs, profilesByStatus, jobsByStatus };
310
+ }
@@ -472,3 +472,84 @@ function _minuteKey(date) {
472
472
  function _secondKey(date) {
473
473
  return `${_minuteKey(date)}-${date.getSeconds()}`;
474
474
  }
475
+
476
+
477
+ // =====================================================================
478
+ // Cowork Cron V2 governance overlay
479
+ // =====================================================================
480
+ export const CCRON_PROFILE_MATURITY_V2 = Object.freeze({ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived" });
481
+ export const CCRON_TICK_LIFECYCLE_V2 = Object.freeze({ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled" });
482
+ const _ccronPTrans = new Map([
483
+ [CCRON_PROFILE_MATURITY_V2.PENDING, new Set([CCRON_PROFILE_MATURITY_V2.ACTIVE, CCRON_PROFILE_MATURITY_V2.ARCHIVED])],
484
+ [CCRON_PROFILE_MATURITY_V2.ACTIVE, new Set([CCRON_PROFILE_MATURITY_V2.PAUSED, CCRON_PROFILE_MATURITY_V2.ARCHIVED])],
485
+ [CCRON_PROFILE_MATURITY_V2.PAUSED, new Set([CCRON_PROFILE_MATURITY_V2.ACTIVE, CCRON_PROFILE_MATURITY_V2.ARCHIVED])],
486
+ [CCRON_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
487
+ ]);
488
+ const _ccronPTerminal = new Set([CCRON_PROFILE_MATURITY_V2.ARCHIVED]);
489
+ const _ccronTTrans = new Map([
490
+ [CCRON_TICK_LIFECYCLE_V2.QUEUED, new Set([CCRON_TICK_LIFECYCLE_V2.RUNNING, CCRON_TICK_LIFECYCLE_V2.CANCELLED])],
491
+ [CCRON_TICK_LIFECYCLE_V2.RUNNING, new Set([CCRON_TICK_LIFECYCLE_V2.COMPLETED, CCRON_TICK_LIFECYCLE_V2.FAILED, CCRON_TICK_LIFECYCLE_V2.CANCELLED])],
492
+ [CCRON_TICK_LIFECYCLE_V2.COMPLETED, new Set()],
493
+ [CCRON_TICK_LIFECYCLE_V2.FAILED, new Set()],
494
+ [CCRON_TICK_LIFECYCLE_V2.CANCELLED, new Set()],
495
+ ]);
496
+ const _ccronPsV2 = new Map();
497
+ const _ccronTsV2 = new Map();
498
+ let _ccronMaxActive = 6, _ccronMaxPending = 15, _ccronIdleMs = 30 * 24 * 60 * 60 * 1000, _ccronStuckMs = 60 * 1000;
499
+ function _ccronPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
500
+ function _ccronCheckP(from, to) { const a = _ccronPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid ccron profile transition ${from} → ${to}`); }
501
+ function _ccronCheckT(from, to) { const a = _ccronTTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid ccron tick transition ${from} → ${to}`); }
502
+ function _ccronCountActive(owner) { let c = 0; for (const p of _ccronPsV2.values()) if (p.owner === owner && p.status === CCRON_PROFILE_MATURITY_V2.ACTIVE) c++; return c; }
503
+ function _ccronCountPending(profileId) { let c = 0; for (const t of _ccronTsV2.values()) if (t.profileId === profileId && (t.status === CCRON_TICK_LIFECYCLE_V2.QUEUED || t.status === CCRON_TICK_LIFECYCLE_V2.RUNNING)) c++; return c; }
504
+ export function setMaxActiveCcronProfilesPerOwnerV2(n) { _ccronMaxActive = _ccronPos(n, "maxActiveCcronProfilesPerOwner"); }
505
+ export function getMaxActiveCcronProfilesPerOwnerV2() { return _ccronMaxActive; }
506
+ export function setMaxPendingCcronTicksPerProfileV2(n) { _ccronMaxPending = _ccronPos(n, "maxPendingCcronTicksPerProfile"); }
507
+ export function getMaxPendingCcronTicksPerProfileV2() { return _ccronMaxPending; }
508
+ export function setCcronProfileIdleMsV2(n) { _ccronIdleMs = _ccronPos(n, "ccronProfileIdleMs"); }
509
+ export function getCcronProfileIdleMsV2() { return _ccronIdleMs; }
510
+ export function setCcronTickStuckMsV2(n) { _ccronStuckMs = _ccronPos(n, "ccronTickStuckMs"); }
511
+ export function getCcronTickStuckMsV2() { return _ccronStuckMs; }
512
+ export function _resetStateCoworkCronV2() { _ccronPsV2.clear(); _ccronTsV2.clear(); _ccronMaxActive = 6; _ccronMaxPending = 15; _ccronIdleMs = 30 * 24 * 60 * 60 * 1000; _ccronStuckMs = 60 * 1000; }
513
+ export function registerCcronProfileV2({ id, owner, expr, metadata } = {}) {
514
+ if (!id || !owner) throw new Error("id and owner required");
515
+ if (_ccronPsV2.has(id)) throw new Error(`ccron profile ${id} already exists`);
516
+ const now = Date.now();
517
+ const p = { id, owner, expr: expr || "0 0 * * *", status: CCRON_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, lastTouchedAt: now, activatedAt: null, archivedAt: null, metadata: { ...(metadata || {}) } };
518
+ _ccronPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
519
+ }
520
+ export function activateCcronProfileV2(id) {
521
+ const p = _ccronPsV2.get(id); if (!p) throw new Error(`ccron profile ${id} not found`);
522
+ const isInitial = p.status === CCRON_PROFILE_MATURITY_V2.PENDING;
523
+ _ccronCheckP(p.status, CCRON_PROFILE_MATURITY_V2.ACTIVE);
524
+ if (isInitial && _ccronCountActive(p.owner) >= _ccronMaxActive) throw new Error(`max active ccron profiles for owner ${p.owner} reached`);
525
+ const now = Date.now(); p.status = CCRON_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now;
526
+ if (!p.activatedAt) p.activatedAt = now;
527
+ return { ...p, metadata: { ...p.metadata } };
528
+ }
529
+ export function pauseCcronProfileV2(id) { const p = _ccronPsV2.get(id); if (!p) throw new Error(`ccron profile ${id} not found`); _ccronCheckP(p.status, CCRON_PROFILE_MATURITY_V2.PAUSED); p.status = CCRON_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
530
+ export function archiveCcronProfileV2(id) { const p = _ccronPsV2.get(id); if (!p) throw new Error(`ccron profile ${id} not found`); _ccronCheckP(p.status, CCRON_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = CCRON_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
531
+ export function touchCcronProfileV2(id) { const p = _ccronPsV2.get(id); if (!p) throw new Error(`ccron profile ${id} not found`); if (_ccronPTerminal.has(p.status)) throw new Error(`cannot touch terminal ccron profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
532
+ export function getCcronProfileV2(id) { const p = _ccronPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
533
+ export function listCcronProfilesV2() { return [..._ccronPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
534
+ export function createCcronTickV2({ id, profileId, tickAt, metadata } = {}) {
535
+ if (!id || !profileId) throw new Error("id and profileId required");
536
+ if (_ccronTsV2.has(id)) throw new Error(`ccron tick ${id} already exists`);
537
+ if (!_ccronPsV2.has(profileId)) throw new Error(`ccron profile ${profileId} not found`);
538
+ if (_ccronCountPending(profileId) >= _ccronMaxPending) throw new Error(`max pending ccron ticks for profile ${profileId} reached`);
539
+ const now = Date.now();
540
+ const t = { id, profileId, tickAt: tickAt || now, status: CCRON_TICK_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
541
+ _ccronTsV2.set(id, t); return { ...t, metadata: { ...t.metadata } };
542
+ }
543
+ export function runningCcronTickV2(id) { const t = _ccronTsV2.get(id); if (!t) throw new Error(`ccron tick ${id} not found`); _ccronCheckT(t.status, CCRON_TICK_LIFECYCLE_V2.RUNNING); const now = Date.now(); t.status = CCRON_TICK_LIFECYCLE_V2.RUNNING; t.updatedAt = now; if (!t.startedAt) t.startedAt = now; return { ...t, metadata: { ...t.metadata } }; }
544
+ export function completeCcronTickV2(id) { const t = _ccronTsV2.get(id); if (!t) throw new Error(`ccron tick ${id} not found`); _ccronCheckT(t.status, CCRON_TICK_LIFECYCLE_V2.COMPLETED); const now = Date.now(); t.status = CCRON_TICK_LIFECYCLE_V2.COMPLETED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; return { ...t, metadata: { ...t.metadata } }; }
545
+ export function failCcronTickV2(id, reason) { const t = _ccronTsV2.get(id); if (!t) throw new Error(`ccron tick ${id} not found`); _ccronCheckT(t.status, CCRON_TICK_LIFECYCLE_V2.FAILED); const now = Date.now(); t.status = CCRON_TICK_LIFECYCLE_V2.FAILED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; if (reason) t.metadata.failReason = String(reason); return { ...t, metadata: { ...t.metadata } }; }
546
+ export function cancelCcronTickV2(id, reason) { const t = _ccronTsV2.get(id); if (!t) throw new Error(`ccron tick ${id} not found`); _ccronCheckT(t.status, CCRON_TICK_LIFECYCLE_V2.CANCELLED); const now = Date.now(); t.status = CCRON_TICK_LIFECYCLE_V2.CANCELLED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; if (reason) t.metadata.cancelReason = String(reason); return { ...t, metadata: { ...t.metadata } }; }
547
+ export function getCcronTickV2(id) { const t = _ccronTsV2.get(id); if (!t) return null; return { ...t, metadata: { ...t.metadata } }; }
548
+ export function listCcronTicksV2() { return [..._ccronTsV2.values()].map((t) => ({ ...t, metadata: { ...t.metadata } })); }
549
+ export function autoPauseIdleCcronProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _ccronPsV2.values()) if (p.status === CCRON_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _ccronIdleMs) { p.status = CCRON_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
550
+ export function autoFailStuckCcronTicksV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const x of _ccronTsV2.values()) if (x.status === CCRON_TICK_LIFECYCLE_V2.RUNNING && x.startedAt != null && (t - x.startedAt) >= _ccronStuckMs) { x.status = CCRON_TICK_LIFECYCLE_V2.FAILED; x.updatedAt = t; if (!x.settledAt) x.settledAt = t; x.metadata.failReason = "auto-fail-stuck"; flipped.push(x.id); } return { flipped, count: flipped.length }; }
551
+ export function getCoworkCronGovStatsV2() {
552
+ const profilesByStatus = {}; for (const v of Object.values(CCRON_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _ccronPsV2.values()) profilesByStatus[p.status]++;
553
+ const ticksByStatus = {}; for (const v of Object.values(CCRON_TICK_LIFECYCLE_V2)) ticksByStatus[v] = 0; for (const t of _ccronTsV2.values()) ticksByStatus[t.status]++;
554
+ return { totalCcronProfilesV2: _ccronPsV2.size, totalCcronTicksV2: _ccronTsV2.size, maxActiveCcronProfilesPerOwner: _ccronMaxActive, maxPendingCcronTicksPerProfile: _ccronMaxPending, ccronProfileIdleMs: _ccronIdleMs, ccronTickStuckMs: _ccronStuckMs, profilesByStatus, ticksByStatus };
555
+ }