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
@@ -926,3 +926,108 @@ export function getStats(db) {
926
926
  deploysByStrategy: deployByStrategy,
927
927
  };
928
928
  }
929
+
930
+
931
+ // ===== V2 Surface: Pipeline Orchestrator governance overlay (CLI v0.137.0) =====
932
+ export const PIPELINE_MATURITY_V2 = Object.freeze({
933
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived",
934
+ });
935
+ export const PIPELINE_RUN_LIFECYCLE_V2 = Object.freeze({
936
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
937
+ });
938
+
939
+ const _pipTrans = new Map([
940
+ [PIPELINE_MATURITY_V2.PENDING, new Set([PIPELINE_MATURITY_V2.ACTIVE, PIPELINE_MATURITY_V2.ARCHIVED])],
941
+ [PIPELINE_MATURITY_V2.ACTIVE, new Set([PIPELINE_MATURITY_V2.PAUSED, PIPELINE_MATURITY_V2.ARCHIVED])],
942
+ [PIPELINE_MATURITY_V2.PAUSED, new Set([PIPELINE_MATURITY_V2.ACTIVE, PIPELINE_MATURITY_V2.ARCHIVED])],
943
+ [PIPELINE_MATURITY_V2.ARCHIVED, new Set()],
944
+ ]);
945
+ const _pipTerminal = new Set([PIPELINE_MATURITY_V2.ARCHIVED]);
946
+ const _pipRunTrans = new Map([
947
+ [PIPELINE_RUN_LIFECYCLE_V2.QUEUED, new Set([PIPELINE_RUN_LIFECYCLE_V2.RUNNING, PIPELINE_RUN_LIFECYCLE_V2.CANCELLED])],
948
+ [PIPELINE_RUN_LIFECYCLE_V2.RUNNING, new Set([PIPELINE_RUN_LIFECYCLE_V2.COMPLETED, PIPELINE_RUN_LIFECYCLE_V2.FAILED, PIPELINE_RUN_LIFECYCLE_V2.CANCELLED])],
949
+ [PIPELINE_RUN_LIFECYCLE_V2.COMPLETED, new Set()],
950
+ [PIPELINE_RUN_LIFECYCLE_V2.FAILED, new Set()],
951
+ [PIPELINE_RUN_LIFECYCLE_V2.CANCELLED, new Set()],
952
+ ]);
953
+
954
+ const _pips = new Map();
955
+ const _pipRuns = new Map();
956
+ let _pipMaxActivePerOwner = 10;
957
+ let _pipMaxPendingPerPip = 20;
958
+ let _pipIdleMs = 3 * 24 * 60 * 60 * 1000;
959
+ let _pipRunStuckMs = 10 * 60 * 1000;
960
+
961
+ function _pipPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
962
+
963
+ export function setMaxActivePipelinesPerOwnerV2(n) { _pipMaxActivePerOwner = _pipPos(n, "maxActivePipelinesPerOwner"); }
964
+ export function getMaxActivePipelinesPerOwnerV2() { return _pipMaxActivePerOwner; }
965
+ export function setMaxPendingPipelineRunsPerPipelineV2(n) { _pipMaxPendingPerPip = _pipPos(n, "maxPendingPipelineRunsPerPipeline"); }
966
+ export function getMaxPendingPipelineRunsPerPipelineV2() { return _pipMaxPendingPerPip; }
967
+ export function setPipelineIdleMsV2(n) { _pipIdleMs = _pipPos(n, "pipelineIdleMs"); }
968
+ export function getPipelineIdleMsV2() { return _pipIdleMs; }
969
+ export function setPipelineRunStuckMsV2(n) { _pipRunStuckMs = _pipPos(n, "pipelineRunStuckMs"); }
970
+ export function getPipelineRunStuckMsV2() { return _pipRunStuckMs; }
971
+
972
+ export function _resetStatePipelineOrchestratorV2() {
973
+ _pips.clear(); _pipRuns.clear();
974
+ _pipMaxActivePerOwner = 10; _pipMaxPendingPerPip = 20;
975
+ _pipIdleMs = 3 * 24 * 60 * 60 * 1000; _pipRunStuckMs = 10 * 60 * 1000;
976
+ }
977
+
978
+ export function registerPipelineV2({ id, owner, name, metadata } = {}) {
979
+ if (!id || typeof id !== "string") throw new Error("id is required");
980
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
981
+ if (_pips.has(id)) throw new Error(`pipeline ${id} already registered`);
982
+ const now = Date.now();
983
+ const p = { id, owner, name: name || id, status: PIPELINE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
984
+ _pips.set(id, p);
985
+ return { ...p, metadata: { ...p.metadata } };
986
+ }
987
+ function _pipCheckP(from, to) { const a = _pipTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid pipeline transition ${from} → ${to}`); }
988
+ function _pipCountActive(owner) { let n = 0; for (const p of _pips.values()) if (p.owner === owner && p.status === PIPELINE_MATURITY_V2.ACTIVE) n++; return n; }
989
+
990
+ export function activatePipelineV2(id) {
991
+ const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`);
992
+ _pipCheckP(p.status, PIPELINE_MATURITY_V2.ACTIVE);
993
+ const recovery = p.status === PIPELINE_MATURITY_V2.PAUSED;
994
+ if (!recovery) { const c = _pipCountActive(p.owner); if (c >= _pipMaxActivePerOwner) throw new Error(`max active pipelines per owner (${_pipMaxActivePerOwner}) reached for ${p.owner}`); }
995
+ const now = Date.now(); p.status = PIPELINE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
996
+ return { ...p, metadata: { ...p.metadata } };
997
+ }
998
+ export function pausePipelineV2(id) { const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`); _pipCheckP(p.status, PIPELINE_MATURITY_V2.PAUSED); p.status = PIPELINE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
999
+ export function archivePipelineV2(id) { const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`); _pipCheckP(p.status, PIPELINE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = PIPELINE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
1000
+ export function touchPipelineV2(id) { const p = _pips.get(id); if (!p) throw new Error(`pipeline ${id} not found`); if (_pipTerminal.has(p.status)) throw new Error(`cannot touch terminal pipeline ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
1001
+ export function getPipelineV2(id) { const p = _pips.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
1002
+ export function listPipelinesV2() { return [..._pips.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
1003
+
1004
+ function _pipCountPendingRuns(pid) { let n = 0; for (const r of _pipRuns.values()) if (r.pipelineId === pid && (r.status === PIPELINE_RUN_LIFECYCLE_V2.QUEUED || r.status === PIPELINE_RUN_LIFECYCLE_V2.RUNNING)) n++; return n; }
1005
+
1006
+ export function createPipelineRunV2({ id, pipelineId, trigger, metadata } = {}) {
1007
+ if (!id || typeof id !== "string") throw new Error("id is required");
1008
+ if (!pipelineId || typeof pipelineId !== "string") throw new Error("pipelineId is required");
1009
+ if (_pipRuns.has(id)) throw new Error(`pipeline run ${id} already exists`);
1010
+ if (!_pips.has(pipelineId)) throw new Error(`pipeline ${pipelineId} not found`);
1011
+ const pending = _pipCountPendingRuns(pipelineId);
1012
+ if (pending >= _pipMaxPendingPerPip) throw new Error(`max pending pipeline runs per pipeline (${_pipMaxPendingPerPip}) reached for ${pipelineId}`);
1013
+ const now = Date.now();
1014
+ const r = { id, pipelineId, trigger: trigger || "manual", status: PIPELINE_RUN_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
1015
+ _pipRuns.set(id, r);
1016
+ return { ...r, metadata: { ...r.metadata } };
1017
+ }
1018
+ function _pipCheckR(from, to) { const a = _pipRunTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid pipeline run transition ${from} → ${to}`); }
1019
+ export function startPipelineRunV2(id) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.RUNNING); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.RUNNING; r.updatedAt = now; if (!r.startedAt) r.startedAt = now; return { ...r, metadata: { ...r.metadata } }; }
1020
+ export function completePipelineRunV2(id) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.COMPLETED); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.COMPLETED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; return { ...r, metadata: { ...r.metadata } }; }
1021
+ export function failPipelineRunV2(id, reason) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.FAILED); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.FAILED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.failReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
1022
+ export function cancelPipelineRunV2(id, reason) { const r = _pipRuns.get(id); if (!r) throw new Error(`pipeline run ${id} not found`); _pipCheckR(r.status, PIPELINE_RUN_LIFECYCLE_V2.CANCELLED); const now = Date.now(); r.status = PIPELINE_RUN_LIFECYCLE_V2.CANCELLED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.cancelReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
1023
+ export function getPipelineRunV2(id) { const r = _pipRuns.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
1024
+ export function listPipelineRunsV2() { return [..._pipRuns.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
1025
+
1026
+ export function autoPauseIdlePipelinesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _pips.values()) if (p.status === PIPELINE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _pipIdleMs) { p.status = PIPELINE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
1027
+ export function autoFailStuckPipelineRunsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _pipRuns.values()) if (r.status === PIPELINE_RUN_LIFECYCLE_V2.RUNNING && r.startedAt != null && (t - r.startedAt) >= _pipRunStuckMs) { r.status = PIPELINE_RUN_LIFECYCLE_V2.FAILED; r.updatedAt = t; if (!r.settledAt) r.settledAt = t; r.metadata.failReason = "auto-fail-stuck"; flipped.push(r.id); } return { flipped, count: flipped.length }; }
1028
+
1029
+ export function getPipelineOrchestratorGovStatsV2() {
1030
+ const pipelinesByStatus = {}; for (const s of Object.values(PIPELINE_MATURITY_V2)) pipelinesByStatus[s] = 0; for (const p of _pips.values()) pipelinesByStatus[p.status]++;
1031
+ const runsByStatus = {}; for (const s of Object.values(PIPELINE_RUN_LIFECYCLE_V2)) runsByStatus[s] = 0; for (const r of _pipRuns.values()) runsByStatus[r.status]++;
1032
+ return { totalPipelinesV2: _pips.size, totalRunsV2: _pipRuns.size, maxActivePipelinesPerOwner: _pipMaxActivePerOwner, maxPendingPipelineRunsPerPipeline: _pipMaxPendingPerPip, pipelineIdleMs: _pipIdleMs, pipelineRunStuckMs: _pipRunStuckMs, pipelinesByStatus, runsByStatus };
1033
+ }
@@ -544,3 +544,84 @@ export function destroyPlanModeManager() {
544
544
  _instance = null;
545
545
  }
546
546
  }
547
+
548
+ // ===== V2 Surface: Plan Mode governance overlay (CLI v0.141.0) =====
549
+ export const PLAN_PROFILE_MATURITY_V2 = Object.freeze({
550
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived",
551
+ });
552
+ export const PLAN_STEP_LIFECYCLE_V2 = Object.freeze({
553
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
554
+ });
555
+ const _planPTrans = new Map([
556
+ [PLAN_PROFILE_MATURITY_V2.PENDING, new Set([PLAN_PROFILE_MATURITY_V2.ACTIVE, PLAN_PROFILE_MATURITY_V2.ARCHIVED])],
557
+ [PLAN_PROFILE_MATURITY_V2.ACTIVE, new Set([PLAN_PROFILE_MATURITY_V2.PAUSED, PLAN_PROFILE_MATURITY_V2.ARCHIVED])],
558
+ [PLAN_PROFILE_MATURITY_V2.PAUSED, new Set([PLAN_PROFILE_MATURITY_V2.ACTIVE, PLAN_PROFILE_MATURITY_V2.ARCHIVED])],
559
+ [PLAN_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
560
+ ]);
561
+ const _planPTerminal = new Set([PLAN_PROFILE_MATURITY_V2.ARCHIVED]);
562
+ const _planSTrans = new Map([
563
+ [PLAN_STEP_LIFECYCLE_V2.QUEUED, new Set([PLAN_STEP_LIFECYCLE_V2.RUNNING, PLAN_STEP_LIFECYCLE_V2.CANCELLED])],
564
+ [PLAN_STEP_LIFECYCLE_V2.RUNNING, new Set([PLAN_STEP_LIFECYCLE_V2.COMPLETED, PLAN_STEP_LIFECYCLE_V2.FAILED, PLAN_STEP_LIFECYCLE_V2.CANCELLED])],
565
+ [PLAN_STEP_LIFECYCLE_V2.COMPLETED, new Set()],
566
+ [PLAN_STEP_LIFECYCLE_V2.FAILED, new Set()],
567
+ [PLAN_STEP_LIFECYCLE_V2.CANCELLED, new Set()],
568
+ ]);
569
+ const _planPsV2 = new Map();
570
+ const _planSsV2 = new Map();
571
+ let _planMaxActivePerOwner = 6, _planMaxPendingStepsPerProfile = 15, _planIdleMs = 7 * 24 * 60 * 60 * 1000, _planStuckMs = 30 * 60 * 1000;
572
+ function _planPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
573
+ function _planCheckP(from, to) { const a = _planPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid plan profile transition ${from} → ${to}`); }
574
+ function _planCheckS(from, to) { const a = _planSTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid plan step transition ${from} → ${to}`); }
575
+ export function setMaxActivePlanProfilesPerOwnerV2(n) { _planMaxActivePerOwner = _planPos(n, "maxActivePlanProfilesPerOwner"); }
576
+ export function getMaxActivePlanProfilesPerOwnerV2() { return _planMaxActivePerOwner; }
577
+ export function setMaxPendingPlanStepsPerProfileV2(n) { _planMaxPendingStepsPerProfile = _planPos(n, "maxPendingPlanStepsPerProfile"); }
578
+ export function getMaxPendingPlanStepsPerProfileV2() { return _planMaxPendingStepsPerProfile; }
579
+ export function setPlanProfileIdleMsV2(n) { _planIdleMs = _planPos(n, "planProfileIdleMs"); }
580
+ export function getPlanProfileIdleMsV2() { return _planIdleMs; }
581
+ export function setPlanStepStuckMsV2(n) { _planStuckMs = _planPos(n, "planStepStuckMs"); }
582
+ export function getPlanStepStuckMsV2() { return _planStuckMs; }
583
+ export function _resetStatePlanModeV2() { _planPsV2.clear(); _planSsV2.clear(); _planMaxActivePerOwner = 6; _planMaxPendingStepsPerProfile = 15; _planIdleMs = 7 * 24 * 60 * 60 * 1000; _planStuckMs = 30 * 60 * 1000; }
584
+ export function registerPlanProfileV2({ id, owner, goal, metadata } = {}) {
585
+ if (!id) throw new Error("plan profile id required"); if (!owner) throw new Error("plan profile owner required");
586
+ if (_planPsV2.has(id)) throw new Error(`plan profile ${id} already registered`);
587
+ const now = Date.now();
588
+ const p = { id, owner, goal: goal || "", status: PLAN_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
589
+ _planPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
590
+ }
591
+ function _planCountActive(owner) { let n = 0; for (const p of _planPsV2.values()) if (p.owner === owner && p.status === PLAN_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
592
+ export function activatePlanProfileV2(id) {
593
+ const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`);
594
+ _planCheckP(p.status, PLAN_PROFILE_MATURITY_V2.ACTIVE);
595
+ const recovery = p.status === PLAN_PROFILE_MATURITY_V2.PAUSED;
596
+ if (!recovery && _planCountActive(p.owner) >= _planMaxActivePerOwner) throw new Error(`max active plan profiles for owner ${p.owner} reached`);
597
+ const now = Date.now(); p.status = PLAN_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
598
+ return { ...p, metadata: { ...p.metadata } };
599
+ }
600
+ export function pausePlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`); _planCheckP(p.status, PLAN_PROFILE_MATURITY_V2.PAUSED); p.status = PLAN_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
601
+ export function archivePlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`); _planCheckP(p.status, PLAN_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = PLAN_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
602
+ export function touchPlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) throw new Error(`plan profile ${id} not found`); if (_planPTerminal.has(p.status)) throw new Error(`cannot touch terminal plan profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
603
+ export function getPlanProfileV2(id) { const p = _planPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
604
+ export function listPlanProfilesV2() { return [..._planPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
605
+ function _planCountPending(profileId) { let n = 0; for (const s of _planSsV2.values()) if (s.profileId === profileId && (s.status === PLAN_STEP_LIFECYCLE_V2.QUEUED || s.status === PLAN_STEP_LIFECYCLE_V2.RUNNING)) n++; return n; }
606
+ export function createPlanStepV2({ id, profileId, action, metadata } = {}) {
607
+ if (!id) throw new Error("plan step id required"); if (!profileId) throw new Error("plan step profileId required");
608
+ if (_planSsV2.has(id)) throw new Error(`plan step ${id} already exists`);
609
+ if (!_planPsV2.has(profileId)) throw new Error(`plan profile ${profileId} not found`);
610
+ if (_planCountPending(profileId) >= _planMaxPendingStepsPerProfile) throw new Error(`max pending plan steps for profile ${profileId} reached`);
611
+ const now = Date.now();
612
+ const s = { id, profileId, action: action || "", status: PLAN_STEP_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
613
+ _planSsV2.set(id, s); return { ...s, metadata: { ...s.metadata } };
614
+ }
615
+ export function startPlanStepV2(id) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.RUNNING); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.RUNNING; s.updatedAt = now; if (!s.startedAt) s.startedAt = now; return { ...s, metadata: { ...s.metadata } }; }
616
+ export function completePlanStepV2(id) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.COMPLETED); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.COMPLETED; s.updatedAt = now; if (!s.settledAt) s.settledAt = now; return { ...s, metadata: { ...s.metadata } }; }
617
+ export function failPlanStepV2(id, reason) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.FAILED); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.FAILED; s.updatedAt = now; if (!s.settledAt) s.settledAt = now; if (reason) s.metadata.failReason = String(reason); return { ...s, metadata: { ...s.metadata } }; }
618
+ export function cancelPlanStepV2(id, reason) { const s = _planSsV2.get(id); if (!s) throw new Error(`plan step ${id} not found`); _planCheckS(s.status, PLAN_STEP_LIFECYCLE_V2.CANCELLED); const now = Date.now(); s.status = PLAN_STEP_LIFECYCLE_V2.CANCELLED; s.updatedAt = now; if (!s.settledAt) s.settledAt = now; if (reason) s.metadata.cancelReason = String(reason); return { ...s, metadata: { ...s.metadata } }; }
619
+ export function getPlanStepV2(id) { const s = _planSsV2.get(id); if (!s) return null; return { ...s, metadata: { ...s.metadata } }; }
620
+ export function listPlanStepsV2() { return [..._planSsV2.values()].map((s) => ({ ...s, metadata: { ...s.metadata } })); }
621
+ export function autoPauseIdlePlanProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _planPsV2.values()) if (p.status === PLAN_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _planIdleMs) { p.status = PLAN_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
622
+ export function autoFailStuckPlanStepsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const s of _planSsV2.values()) if (s.status === PLAN_STEP_LIFECYCLE_V2.RUNNING && s.startedAt != null && (t - s.startedAt) >= _planStuckMs) { s.status = PLAN_STEP_LIFECYCLE_V2.FAILED; s.updatedAt = t; if (!s.settledAt) s.settledAt = t; s.metadata.failReason = "auto-fail-stuck"; flipped.push(s.id); } return { flipped, count: flipped.length }; }
623
+ export function getPlanModeGovStatsV2() {
624
+ const profilesByStatus = {}; for (const v of Object.values(PLAN_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _planPsV2.values()) profilesByStatus[p.status]++;
625
+ const stepsByStatus = {}; for (const v of Object.values(PLAN_STEP_LIFECYCLE_V2)) stepsByStatus[v] = 0; for (const s of _planSsV2.values()) stepsByStatus[s.status]++;
626
+ return { totalPlanProfilesV2: _planPsV2.size, totalPlanStepsV2: _planSsV2.size, maxActivePlanProfilesPerOwner: _planMaxActivePerOwner, maxPendingPlanStepsPerProfile: _planMaxPendingStepsPerProfile, planProfileIdleMs: _planIdleMs, planStepStuckMs: _planStuckMs, profilesByStatus, stepsByStatus };
627
+ }
@@ -9,13 +9,4 @@
9
9
  * in new code.
10
10
  */
11
11
 
12
- export {
13
- estimateTokens,
14
- estimateMessagesTokens,
15
- CONTEXT_WINDOWS,
16
- getContextWindow,
17
- COMPRESSION_VARIANTS,
18
- getCompressionVariant,
19
- adaptiveThresholds,
20
- PromptCompressor,
21
- } from "../harness/prompt-compressor.js";
12
+ export * from "../harness/prompt-compressor.js";
@@ -181,3 +181,84 @@ export class ServiceContainer {
181
181
  export function createServiceContainer() {
182
182
  return new ServiceContainer();
183
183
  }
184
+
185
+ // ===== V2 Surface: Service Container governance overlay (CLI v0.141.0) =====
186
+ export const SVC_CONTAINER_MATURITY_V2 = Object.freeze({
187
+ PENDING: "pending", ACTIVE: "active", DEGRADED: "degraded", DECOMMISSIONED: "decommissioned",
188
+ });
189
+ export const SVC_RESOLUTION_LIFECYCLE_V2 = Object.freeze({
190
+ QUEUED: "queued", RESOLVING: "resolving", RESOLVED: "resolved", FAILED: "failed", CANCELLED: "cancelled",
191
+ });
192
+ const _svcCTrans = new Map([
193
+ [SVC_CONTAINER_MATURITY_V2.PENDING, new Set([SVC_CONTAINER_MATURITY_V2.ACTIVE, SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED])],
194
+ [SVC_CONTAINER_MATURITY_V2.ACTIVE, new Set([SVC_CONTAINER_MATURITY_V2.DEGRADED, SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED])],
195
+ [SVC_CONTAINER_MATURITY_V2.DEGRADED, new Set([SVC_CONTAINER_MATURITY_V2.ACTIVE, SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED])],
196
+ [SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED, new Set()],
197
+ ]);
198
+ const _svcCTerminal = new Set([SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED]);
199
+ const _svcRTrans = new Map([
200
+ [SVC_RESOLUTION_LIFECYCLE_V2.QUEUED, new Set([SVC_RESOLUTION_LIFECYCLE_V2.RESOLVING, SVC_RESOLUTION_LIFECYCLE_V2.CANCELLED])],
201
+ [SVC_RESOLUTION_LIFECYCLE_V2.RESOLVING, new Set([SVC_RESOLUTION_LIFECYCLE_V2.RESOLVED, SVC_RESOLUTION_LIFECYCLE_V2.FAILED, SVC_RESOLUTION_LIFECYCLE_V2.CANCELLED])],
202
+ [SVC_RESOLUTION_LIFECYCLE_V2.RESOLVED, new Set()],
203
+ [SVC_RESOLUTION_LIFECYCLE_V2.FAILED, new Set()],
204
+ [SVC_RESOLUTION_LIFECYCLE_V2.CANCELLED, new Set()],
205
+ ]);
206
+ const _svcCsV2 = new Map();
207
+ const _svcRsV2 = new Map();
208
+ let _svcMaxActivePerOwner = 8, _svcMaxPendingResPerContainer = 25, _svcIdleMs = 60 * 60 * 1000, _svcStuckMs = 30 * 1000;
209
+ function _svcPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
210
+ function _svcCheckC(from, to) { const a = _svcCTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid svc container transition ${from} → ${to}`); }
211
+ function _svcCheckR(from, to) { const a = _svcRTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid svc resolution transition ${from} → ${to}`); }
212
+ export function setMaxActiveSvcContainersPerOwnerV2(n) { _svcMaxActivePerOwner = _svcPos(n, "maxActiveSvcContainersPerOwner"); }
213
+ export function getMaxActiveSvcContainersPerOwnerV2() { return _svcMaxActivePerOwner; }
214
+ export function setMaxPendingSvcResolutionsPerContainerV2(n) { _svcMaxPendingResPerContainer = _svcPos(n, "maxPendingSvcResolutionsPerContainer"); }
215
+ export function getMaxPendingSvcResolutionsPerContainerV2() { return _svcMaxPendingResPerContainer; }
216
+ export function setSvcContainerIdleMsV2(n) { _svcIdleMs = _svcPos(n, "svcContainerIdleMs"); }
217
+ export function getSvcContainerIdleMsV2() { return _svcIdleMs; }
218
+ export function setSvcResolutionStuckMsV2(n) { _svcStuckMs = _svcPos(n, "svcResolutionStuckMs"); }
219
+ export function getSvcResolutionStuckMsV2() { return _svcStuckMs; }
220
+ export function _resetStateServiceContainerV2() { _svcCsV2.clear(); _svcRsV2.clear(); _svcMaxActivePerOwner = 8; _svcMaxPendingResPerContainer = 25; _svcIdleMs = 60 * 60 * 1000; _svcStuckMs = 30 * 1000; }
221
+ export function registerSvcContainerV2({ id, owner, scope, metadata } = {}) {
222
+ if (!id) throw new Error("svc container id required"); if (!owner) throw new Error("svc container owner required");
223
+ if (_svcCsV2.has(id)) throw new Error(`svc container ${id} already registered`);
224
+ const now = Date.now();
225
+ const c = { id, owner, scope: scope || "default", status: SVC_CONTAINER_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, decommissionedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
226
+ _svcCsV2.set(id, c); return { ...c, metadata: { ...c.metadata } };
227
+ }
228
+ function _svcCountActive(owner) { let n = 0; for (const c of _svcCsV2.values()) if (c.owner === owner && c.status === SVC_CONTAINER_MATURITY_V2.ACTIVE) n++; return n; }
229
+ export function activateSvcContainerV2(id) {
230
+ const c = _svcCsV2.get(id); if (!c) throw new Error(`svc container ${id} not found`);
231
+ _svcCheckC(c.status, SVC_CONTAINER_MATURITY_V2.ACTIVE);
232
+ const recovery = c.status === SVC_CONTAINER_MATURITY_V2.DEGRADED;
233
+ if (!recovery && _svcCountActive(c.owner) >= _svcMaxActivePerOwner) throw new Error(`max active svc containers for owner ${c.owner} reached`);
234
+ const now = Date.now(); c.status = SVC_CONTAINER_MATURITY_V2.ACTIVE; c.updatedAt = now; c.lastTouchedAt = now; if (!c.activatedAt) c.activatedAt = now;
235
+ return { ...c, metadata: { ...c.metadata } };
236
+ }
237
+ export function degradeSvcContainerV2(id) { const c = _svcCsV2.get(id); if (!c) throw new Error(`svc container ${id} not found`); _svcCheckC(c.status, SVC_CONTAINER_MATURITY_V2.DEGRADED); c.status = SVC_CONTAINER_MATURITY_V2.DEGRADED; c.updatedAt = Date.now(); return { ...c, metadata: { ...c.metadata } }; }
238
+ export function decommissionSvcContainerV2(id) { const c = _svcCsV2.get(id); if (!c) throw new Error(`svc container ${id} not found`); _svcCheckC(c.status, SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED); const now = Date.now(); c.status = SVC_CONTAINER_MATURITY_V2.DECOMMISSIONED; c.updatedAt = now; if (!c.decommissionedAt) c.decommissionedAt = now; return { ...c, metadata: { ...c.metadata } }; }
239
+ export function touchSvcContainerV2(id) { const c = _svcCsV2.get(id); if (!c) throw new Error(`svc container ${id} not found`); if (_svcCTerminal.has(c.status)) throw new Error(`cannot touch terminal svc container ${id}`); const now = Date.now(); c.lastTouchedAt = now; c.updatedAt = now; return { ...c, metadata: { ...c.metadata } }; }
240
+ export function getSvcContainerV2(id) { const c = _svcCsV2.get(id); if (!c) return null; return { ...c, metadata: { ...c.metadata } }; }
241
+ export function listSvcContainersV2() { return [..._svcCsV2.values()].map((c) => ({ ...c, metadata: { ...c.metadata } })); }
242
+ function _svcCountPending(containerId) { let n = 0; for (const r of _svcRsV2.values()) if (r.containerId === containerId && (r.status === SVC_RESOLUTION_LIFECYCLE_V2.QUEUED || r.status === SVC_RESOLUTION_LIFECYCLE_V2.RESOLVING)) n++; return n; }
243
+ export function createSvcResolutionV2({ id, containerId, token, metadata } = {}) {
244
+ if (!id) throw new Error("svc resolution id required"); if (!containerId) throw new Error("svc resolution containerId required");
245
+ if (_svcRsV2.has(id)) throw new Error(`svc resolution ${id} already exists`);
246
+ if (!_svcCsV2.has(containerId)) throw new Error(`svc container ${containerId} not found`);
247
+ if (_svcCountPending(containerId) >= _svcMaxPendingResPerContainer) throw new Error(`max pending svc resolutions for container ${containerId} reached`);
248
+ const now = Date.now();
249
+ const r = { id, containerId, token: token || "", status: SVC_RESOLUTION_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
250
+ _svcRsV2.set(id, r); return { ...r, metadata: { ...r.metadata } };
251
+ }
252
+ export function resolvingSvcResolutionV2(id) { const r = _svcRsV2.get(id); if (!r) throw new Error(`svc resolution ${id} not found`); _svcCheckR(r.status, SVC_RESOLUTION_LIFECYCLE_V2.RESOLVING); const now = Date.now(); r.status = SVC_RESOLUTION_LIFECYCLE_V2.RESOLVING; r.updatedAt = now; if (!r.startedAt) r.startedAt = now; return { ...r, metadata: { ...r.metadata } }; }
253
+ export function resolveSvcResolutionV2(id) { const r = _svcRsV2.get(id); if (!r) throw new Error(`svc resolution ${id} not found`); _svcCheckR(r.status, SVC_RESOLUTION_LIFECYCLE_V2.RESOLVED); const now = Date.now(); r.status = SVC_RESOLUTION_LIFECYCLE_V2.RESOLVED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; return { ...r, metadata: { ...r.metadata } }; }
254
+ export function failSvcResolutionV2(id, reason) { const r = _svcRsV2.get(id); if (!r) throw new Error(`svc resolution ${id} not found`); _svcCheckR(r.status, SVC_RESOLUTION_LIFECYCLE_V2.FAILED); const now = Date.now(); r.status = SVC_RESOLUTION_LIFECYCLE_V2.FAILED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.failReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
255
+ export function cancelSvcResolutionV2(id, reason) { const r = _svcRsV2.get(id); if (!r) throw new Error(`svc resolution ${id} not found`); _svcCheckR(r.status, SVC_RESOLUTION_LIFECYCLE_V2.CANCELLED); const now = Date.now(); r.status = SVC_RESOLUTION_LIFECYCLE_V2.CANCELLED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.cancelReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
256
+ export function getSvcResolutionV2(id) { const r = _svcRsV2.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
257
+ export function listSvcResolutionsV2() { return [..._svcRsV2.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
258
+ export function autoDegradeIdleSvcContainersV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const c of _svcCsV2.values()) if (c.status === SVC_CONTAINER_MATURITY_V2.ACTIVE && (t - c.lastTouchedAt) >= _svcIdleMs) { c.status = SVC_CONTAINER_MATURITY_V2.DEGRADED; c.updatedAt = t; flipped.push(c.id); } return { flipped, count: flipped.length }; }
259
+ export function autoFailStuckSvcResolutionsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _svcRsV2.values()) if (r.status === SVC_RESOLUTION_LIFECYCLE_V2.RESOLVING && r.startedAt != null && (t - r.startedAt) >= _svcStuckMs) { r.status = SVC_RESOLUTION_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 }; }
260
+ export function getServiceContainerGovStatsV2() {
261
+ const containersByStatus = {}; for (const v of Object.values(SVC_CONTAINER_MATURITY_V2)) containersByStatus[v] = 0; for (const c of _svcCsV2.values()) containersByStatus[c.status]++;
262
+ const resolutionsByStatus = {}; for (const v of Object.values(SVC_RESOLUTION_LIFECYCLE_V2)) resolutionsByStatus[v] = 0; for (const r of _svcRsV2.values()) resolutionsByStatus[r.status]++;
263
+ return { totalSvcContainersV2: _svcCsV2.size, totalSvcResolutionsV2: _svcRsV2.size, maxActiveSvcContainersPerOwner: _svcMaxActivePerOwner, maxPendingSvcResolutionsPerContainer: _svcMaxPendingResPerContainer, svcContainerIdleMs: _svcIdleMs, svcResolutionStuckMs: _svcStuckMs, containersByStatus, resolutionsByStatus };
264
+ }
@@ -123,3 +123,108 @@ export async function consolidateJsonlSession(sessionId, options = {}) {
123
123
  },
124
124
  );
125
125
  }
126
+
127
+
128
+ // ===== V2 Surface: Session Consolidator governance overlay (CLI v0.134.0) =====
129
+ export const CONSOL_PROFILE_MATURITY_V2 = Object.freeze({
130
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", ARCHIVED: "archived",
131
+ });
132
+ export const CONSOL_JOB_LIFECYCLE_V2 = Object.freeze({
133
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
134
+ });
135
+
136
+ const _scProfTrans = new Map([
137
+ [CONSOL_PROFILE_MATURITY_V2.PENDING, new Set([CONSOL_PROFILE_MATURITY_V2.ACTIVE, CONSOL_PROFILE_MATURITY_V2.ARCHIVED])],
138
+ [CONSOL_PROFILE_MATURITY_V2.ACTIVE, new Set([CONSOL_PROFILE_MATURITY_V2.PAUSED, CONSOL_PROFILE_MATURITY_V2.ARCHIVED])],
139
+ [CONSOL_PROFILE_MATURITY_V2.PAUSED, new Set([CONSOL_PROFILE_MATURITY_V2.ACTIVE, CONSOL_PROFILE_MATURITY_V2.ARCHIVED])],
140
+ [CONSOL_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
141
+ ]);
142
+ const _scProfTerminal = new Set([CONSOL_PROFILE_MATURITY_V2.ARCHIVED]);
143
+ const _scJobTrans = new Map([
144
+ [CONSOL_JOB_LIFECYCLE_V2.QUEUED, new Set([CONSOL_JOB_LIFECYCLE_V2.RUNNING, CONSOL_JOB_LIFECYCLE_V2.CANCELLED])],
145
+ [CONSOL_JOB_LIFECYCLE_V2.RUNNING, new Set([CONSOL_JOB_LIFECYCLE_V2.COMPLETED, CONSOL_JOB_LIFECYCLE_V2.FAILED, CONSOL_JOB_LIFECYCLE_V2.CANCELLED])],
146
+ [CONSOL_JOB_LIFECYCLE_V2.COMPLETED, new Set()],
147
+ [CONSOL_JOB_LIFECYCLE_V2.FAILED, new Set()],
148
+ [CONSOL_JOB_LIFECYCLE_V2.CANCELLED, new Set()],
149
+ ]);
150
+
151
+ const _scProfiles = new Map();
152
+ const _scJobs = new Map();
153
+ let _scMaxActivePerOwner = 8;
154
+ let _scMaxPendingPerProfile = 12;
155
+ let _scProfileIdleMs = 7 * 24 * 60 * 60 * 1000;
156
+ let _scJobStuckMs = 10 * 60 * 1000;
157
+
158
+ function _scPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
159
+
160
+ export function setMaxActiveConsolProfilesPerOwnerV2(n) { _scMaxActivePerOwner = _scPos(n, "maxActiveConsolProfilesPerOwner"); }
161
+ export function getMaxActiveConsolProfilesPerOwnerV2() { return _scMaxActivePerOwner; }
162
+ export function setMaxPendingConsolJobsPerProfileV2(n) { _scMaxPendingPerProfile = _scPos(n, "maxPendingConsolJobsPerProfile"); }
163
+ export function getMaxPendingConsolJobsPerProfileV2() { return _scMaxPendingPerProfile; }
164
+ export function setConsolProfileIdleMsV2(n) { _scProfileIdleMs = _scPos(n, "consolProfileIdleMs"); }
165
+ export function getConsolProfileIdleMsV2() { return _scProfileIdleMs; }
166
+ export function setConsolJobStuckMsV2(n) { _scJobStuckMs = _scPos(n, "consolJobStuckMs"); }
167
+ export function getConsolJobStuckMsV2() { return _scJobStuckMs; }
168
+
169
+ export function _resetStateSessionConsolidatorV2() {
170
+ _scProfiles.clear(); _scJobs.clear();
171
+ _scMaxActivePerOwner = 8; _scMaxPendingPerProfile = 12;
172
+ _scProfileIdleMs = 7 * 24 * 60 * 60 * 1000; _scJobStuckMs = 10 * 60 * 1000;
173
+ }
174
+
175
+ export function registerConsolProfileV2({ id, owner, scope, metadata } = {}) {
176
+ if (!id || typeof id !== "string") throw new Error("id is required");
177
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
178
+ if (_scProfiles.has(id)) throw new Error(`consol profile ${id} already registered`);
179
+ const now = Date.now();
180
+ const p = { id, owner, scope: scope || "agent", status: CONSOL_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
181
+ _scProfiles.set(id, p);
182
+ return { ...p, metadata: { ...p.metadata } };
183
+ }
184
+ function _scCheckP(from, to) { const a = _scProfTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid consol profile transition ${from} → ${to}`); }
185
+ function _scCountActive(owner) { let n = 0; for (const p of _scProfiles.values()) if (p.owner === owner && p.status === CONSOL_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
186
+
187
+ export function activateConsolProfileV2(id) {
188
+ const p = _scProfiles.get(id); if (!p) throw new Error(`consol profile ${id} not found`);
189
+ _scCheckP(p.status, CONSOL_PROFILE_MATURITY_V2.ACTIVE);
190
+ const recovery = p.status === CONSOL_PROFILE_MATURITY_V2.PAUSED;
191
+ if (!recovery) { const a = _scCountActive(p.owner); if (a >= _scMaxActivePerOwner) throw new Error(`max active consol profiles per owner (${_scMaxActivePerOwner}) reached for ${p.owner}`); }
192
+ const now = Date.now(); p.status = CONSOL_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
193
+ return { ...p, metadata: { ...p.metadata } };
194
+ }
195
+ export function pauseConsolProfileV2(id) { const p = _scProfiles.get(id); if (!p) throw new Error(`consol profile ${id} not found`); _scCheckP(p.status, CONSOL_PROFILE_MATURITY_V2.PAUSED); p.status = CONSOL_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
196
+ export function archiveConsolProfileV2(id) { const p = _scProfiles.get(id); if (!p) throw new Error(`consol profile ${id} not found`); _scCheckP(p.status, CONSOL_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = CONSOL_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
197
+ export function touchConsolProfileV2(id) { const p = _scProfiles.get(id); if (!p) throw new Error(`consol profile ${id} not found`); if (_scProfTerminal.has(p.status)) throw new Error(`cannot touch terminal consol profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
198
+ export function getConsolProfileV2(id) { const p = _scProfiles.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
199
+ export function listConsolProfilesV2() { return [..._scProfiles.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
200
+
201
+ function _scCountPending(pid) { let n = 0; for (const j of _scJobs.values()) if (j.profileId === pid && (j.status === CONSOL_JOB_LIFECYCLE_V2.QUEUED || j.status === CONSOL_JOB_LIFECYCLE_V2.RUNNING)) n++; return n; }
202
+
203
+ export function createConsolJobV2({ id, profileId, sessionId, metadata } = {}) {
204
+ if (!id || typeof id !== "string") throw new Error("id is required");
205
+ if (!profileId || typeof profileId !== "string") throw new Error("profileId is required");
206
+ if (_scJobs.has(id)) throw new Error(`consol job ${id} already exists`);
207
+ if (!_scProfiles.has(profileId)) throw new Error(`consol profile ${profileId} not found`);
208
+ const pending = _scCountPending(profileId);
209
+ if (pending >= _scMaxPendingPerProfile) throw new Error(`max pending consol jobs per profile (${_scMaxPendingPerProfile}) reached for ${profileId}`);
210
+ const now = Date.now();
211
+ const j = { id, profileId, sessionId: sessionId || null, status: CONSOL_JOB_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
212
+ _scJobs.set(id, j);
213
+ return { ...j, metadata: { ...j.metadata } };
214
+ }
215
+ function _scCheckJ(from, to) { const a = _scJobTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid consol job transition ${from} → ${to}`); }
216
+ export function startConsolJobV2(id) { const j = _scJobs.get(id); if (!j) throw new Error(`consol job ${id} not found`); _scCheckJ(j.status, CONSOL_JOB_LIFECYCLE_V2.RUNNING); const now = Date.now(); j.status = CONSOL_JOB_LIFECYCLE_V2.RUNNING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
217
+ export function completeConsolJobV2(id) { const j = _scJobs.get(id); if (!j) throw new Error(`consol job ${id} not found`); _scCheckJ(j.status, CONSOL_JOB_LIFECYCLE_V2.COMPLETED); const now = Date.now(); j.status = CONSOL_JOB_LIFECYCLE_V2.COMPLETED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
218
+ export function failConsolJobV2(id, reason) { const j = _scJobs.get(id); if (!j) throw new Error(`consol job ${id} not found`); _scCheckJ(j.status, CONSOL_JOB_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = CONSOL_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 } }; }
219
+ export function cancelConsolJobV2(id, reason) { const j = _scJobs.get(id); if (!j) throw new Error(`consol job ${id} not found`); _scCheckJ(j.status, CONSOL_JOB_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = CONSOL_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 } }; }
220
+ export function getConsolJobV2(id) { const j = _scJobs.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
221
+ export function listConsolJobsV2() { return [..._scJobs.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
222
+
223
+ export function autoPauseIdleConsolProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _scProfiles.values()) if (p.status === CONSOL_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _scProfileIdleMs) { p.status = CONSOL_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
224
+ export function autoFailStuckConsolJobsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _scJobs.values()) if (j.status === CONSOL_JOB_LIFECYCLE_V2.RUNNING && j.startedAt != null && (t - j.startedAt) >= _scJobStuckMs) { j.status = CONSOL_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 }; }
225
+
226
+ export function getSessionConsolidatorStatsV2() {
227
+ const profilesByStatus = {}; for (const s of Object.values(CONSOL_PROFILE_MATURITY_V2)) profilesByStatus[s] = 0; for (const p of _scProfiles.values()) profilesByStatus[p.status]++;
228
+ const jobsByStatus = {}; for (const s of Object.values(CONSOL_JOB_LIFECYCLE_V2)) jobsByStatus[s] = 0; for (const j of _scJobs.values()) jobsByStatus[j.status]++;
229
+ return { totalProfilesV2: _scProfiles.size, totalJobsV2: _scJobs.size, maxActiveConsolProfilesPerOwner: _scMaxActivePerOwner, maxPendingConsolJobsPerProfile: _scMaxPendingPerProfile, consolProfileIdleMs: _scProfileIdleMs, consolJobStuckMs: _scJobStuckMs, profilesByStatus, jobsByStatus };
230
+ }
@@ -229,3 +229,84 @@ function withTimeout(promise, ms, label) {
229
229
  });
230
230
  return Promise.race([promise, timeout]).finally(() => clearTimeout(t));
231
231
  }
232
+
233
+ // ===== V2 Surface: Session Hooks governance overlay (CLI v0.142.0) =====
234
+ export const SHOK_PROFILE_MATURITY_V2 = Object.freeze({
235
+ PENDING: "pending", ACTIVE: "active", DISABLED: "disabled", RETIRED: "retired",
236
+ });
237
+ export const SHOK_INVOCATION_LIFECYCLE_V2 = Object.freeze({
238
+ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
239
+ });
240
+ const _shokPTrans = new Map([
241
+ [SHOK_PROFILE_MATURITY_V2.PENDING, new Set([SHOK_PROFILE_MATURITY_V2.ACTIVE, SHOK_PROFILE_MATURITY_V2.RETIRED])],
242
+ [SHOK_PROFILE_MATURITY_V2.ACTIVE, new Set([SHOK_PROFILE_MATURITY_V2.DISABLED, SHOK_PROFILE_MATURITY_V2.RETIRED])],
243
+ [SHOK_PROFILE_MATURITY_V2.DISABLED, new Set([SHOK_PROFILE_MATURITY_V2.ACTIVE, SHOK_PROFILE_MATURITY_V2.RETIRED])],
244
+ [SHOK_PROFILE_MATURITY_V2.RETIRED, new Set()],
245
+ ]);
246
+ const _shokPTerminal = new Set([SHOK_PROFILE_MATURITY_V2.RETIRED]);
247
+ const _shokITrans = new Map([
248
+ [SHOK_INVOCATION_LIFECYCLE_V2.QUEUED, new Set([SHOK_INVOCATION_LIFECYCLE_V2.RUNNING, SHOK_INVOCATION_LIFECYCLE_V2.CANCELLED])],
249
+ [SHOK_INVOCATION_LIFECYCLE_V2.RUNNING, new Set([SHOK_INVOCATION_LIFECYCLE_V2.COMPLETED, SHOK_INVOCATION_LIFECYCLE_V2.FAILED, SHOK_INVOCATION_LIFECYCLE_V2.CANCELLED])],
250
+ [SHOK_INVOCATION_LIFECYCLE_V2.COMPLETED, new Set()],
251
+ [SHOK_INVOCATION_LIFECYCLE_V2.FAILED, new Set()],
252
+ [SHOK_INVOCATION_LIFECYCLE_V2.CANCELLED, new Set()],
253
+ ]);
254
+ const _shokPsV2 = new Map();
255
+ const _shokIsV2 = new Map();
256
+ let _shokMaxActive = 12, _shokMaxPending = 25, _shokIdleMs = 30 * 24 * 60 * 60 * 1000, _shokStuckMs = 30 * 1000;
257
+ function _shokPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
258
+ function _shokCheckP(from, to) { const a = _shokPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid shok profile transition ${from} → ${to}`); }
259
+ function _shokCheckI(from, to) { const a = _shokITrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid shok invocation transition ${from} → ${to}`); }
260
+ export function setMaxActiveShokProfilesPerOwnerV2(n) { _shokMaxActive = _shokPos(n, "maxActiveShokProfilesPerOwner"); }
261
+ export function getMaxActiveShokProfilesPerOwnerV2() { return _shokMaxActive; }
262
+ export function setMaxPendingShokInvocationsPerProfileV2(n) { _shokMaxPending = _shokPos(n, "maxPendingShokInvocationsPerProfile"); }
263
+ export function getMaxPendingShokInvocationsPerProfileV2() { return _shokMaxPending; }
264
+ export function setShokProfileIdleMsV2(n) { _shokIdleMs = _shokPos(n, "shokProfileIdleMs"); }
265
+ export function getShokProfileIdleMsV2() { return _shokIdleMs; }
266
+ export function setShokInvocationStuckMsV2(n) { _shokStuckMs = _shokPos(n, "shokInvocationStuckMs"); }
267
+ export function getShokInvocationStuckMsV2() { return _shokStuckMs; }
268
+ export function _resetStateSessionHooksV2() { _shokPsV2.clear(); _shokIsV2.clear(); _shokMaxActive = 12; _shokMaxPending = 25; _shokIdleMs = 30 * 24 * 60 * 60 * 1000; _shokStuckMs = 30 * 1000; }
269
+ export function registerShokProfileV2({ id, owner, event, metadata } = {}) {
270
+ if (!id) throw new Error("shok profile id required"); if (!owner) throw new Error("shok profile owner required");
271
+ if (_shokPsV2.has(id)) throw new Error(`shok profile ${id} already registered`);
272
+ const now = Date.now();
273
+ const p = { id, owner, event: event || "preTurn", status: SHOK_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
274
+ _shokPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
275
+ }
276
+ function _shokCountActive(owner) { let n = 0; for (const p of _shokPsV2.values()) if (p.owner === owner && p.status === SHOK_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
277
+ export function activateShokProfileV2(id) {
278
+ const p = _shokPsV2.get(id); if (!p) throw new Error(`shok profile ${id} not found`);
279
+ _shokCheckP(p.status, SHOK_PROFILE_MATURITY_V2.ACTIVE);
280
+ const recovery = p.status === SHOK_PROFILE_MATURITY_V2.DISABLED;
281
+ if (!recovery && _shokCountActive(p.owner) >= _shokMaxActive) throw new Error(`max active shok profiles for owner ${p.owner} reached`);
282
+ const now = Date.now(); p.status = SHOK_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
283
+ return { ...p, metadata: { ...p.metadata } };
284
+ }
285
+ export function disableShokProfileV2(id) { const p = _shokPsV2.get(id); if (!p) throw new Error(`shok profile ${id} not found`); _shokCheckP(p.status, SHOK_PROFILE_MATURITY_V2.DISABLED); p.status = SHOK_PROFILE_MATURITY_V2.DISABLED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
286
+ export function retireShokProfileV2(id) { const p = _shokPsV2.get(id); if (!p) throw new Error(`shok profile ${id} not found`); _shokCheckP(p.status, SHOK_PROFILE_MATURITY_V2.RETIRED); const now = Date.now(); p.status = SHOK_PROFILE_MATURITY_V2.RETIRED; p.updatedAt = now; if (!p.retiredAt) p.retiredAt = now; return { ...p, metadata: { ...p.metadata } }; }
287
+ export function touchShokProfileV2(id) { const p = _shokPsV2.get(id); if (!p) throw new Error(`shok profile ${id} not found`); if (_shokPTerminal.has(p.status)) throw new Error(`cannot touch terminal shok profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
288
+ export function getShokProfileV2(id) { const p = _shokPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
289
+ export function listShokProfilesV2() { return [..._shokPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
290
+ function _shokCountPending(profileId) { let n = 0; for (const i of _shokIsV2.values()) if (i.profileId === profileId && (i.status === SHOK_INVOCATION_LIFECYCLE_V2.QUEUED || i.status === SHOK_INVOCATION_LIFECYCLE_V2.RUNNING)) n++; return n; }
291
+ export function createShokInvocationV2({ id, profileId, payload, metadata } = {}) {
292
+ if (!id) throw new Error("shok invocation id required"); if (!profileId) throw new Error("shok invocation profileId required");
293
+ if (_shokIsV2.has(id)) throw new Error(`shok invocation ${id} already exists`);
294
+ if (!_shokPsV2.has(profileId)) throw new Error(`shok profile ${profileId} not found`);
295
+ if (_shokCountPending(profileId) >= _shokMaxPending) throw new Error(`max pending shok invocations for profile ${profileId} reached`);
296
+ const now = Date.now();
297
+ const i = { id, profileId, payload: payload || "", status: SHOK_INVOCATION_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
298
+ _shokIsV2.set(id, i); return { ...i, metadata: { ...i.metadata } };
299
+ }
300
+ export function runningShokInvocationV2(id) { const i = _shokIsV2.get(id); if (!i) throw new Error(`shok invocation ${id} not found`); _shokCheckI(i.status, SHOK_INVOCATION_LIFECYCLE_V2.RUNNING); const now = Date.now(); i.status = SHOK_INVOCATION_LIFECYCLE_V2.RUNNING; i.updatedAt = now; if (!i.startedAt) i.startedAt = now; return { ...i, metadata: { ...i.metadata } }; }
301
+ export function completeShokInvocationV2(id) { const i = _shokIsV2.get(id); if (!i) throw new Error(`shok invocation ${id} not found`); _shokCheckI(i.status, SHOK_INVOCATION_LIFECYCLE_V2.COMPLETED); const now = Date.now(); i.status = SHOK_INVOCATION_LIFECYCLE_V2.COMPLETED; i.updatedAt = now; if (!i.settledAt) i.settledAt = now; return { ...i, metadata: { ...i.metadata } }; }
302
+ export function failShokInvocationV2(id, reason) { const i = _shokIsV2.get(id); if (!i) throw new Error(`shok invocation ${id} not found`); _shokCheckI(i.status, SHOK_INVOCATION_LIFECYCLE_V2.FAILED); const now = Date.now(); i.status = SHOK_INVOCATION_LIFECYCLE_V2.FAILED; i.updatedAt = now; if (!i.settledAt) i.settledAt = now; if (reason) i.metadata.failReason = String(reason); return { ...i, metadata: { ...i.metadata } }; }
303
+ export function cancelShokInvocationV2(id, reason) { const i = _shokIsV2.get(id); if (!i) throw new Error(`shok invocation ${id} not found`); _shokCheckI(i.status, SHOK_INVOCATION_LIFECYCLE_V2.CANCELLED); const now = Date.now(); i.status = SHOK_INVOCATION_LIFECYCLE_V2.CANCELLED; i.updatedAt = now; if (!i.settledAt) i.settledAt = now; if (reason) i.metadata.cancelReason = String(reason); return { ...i, metadata: { ...i.metadata } }; }
304
+ export function getShokInvocationV2(id) { const i = _shokIsV2.get(id); if (!i) return null; return { ...i, metadata: { ...i.metadata } }; }
305
+ export function listShokInvocationsV2() { return [..._shokIsV2.values()].map((i) => ({ ...i, metadata: { ...i.metadata } })); }
306
+ export function autoDisableIdleShokProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _shokPsV2.values()) if (p.status === SHOK_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _shokIdleMs) { p.status = SHOK_PROFILE_MATURITY_V2.DISABLED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
307
+ export function autoFailStuckShokInvocationsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const i of _shokIsV2.values()) if (i.status === SHOK_INVOCATION_LIFECYCLE_V2.RUNNING && i.startedAt != null && (t - i.startedAt) >= _shokStuckMs) { i.status = SHOK_INVOCATION_LIFECYCLE_V2.FAILED; i.updatedAt = t; if (!i.settledAt) i.settledAt = t; i.metadata.failReason = "auto-fail-stuck"; flipped.push(i.id); } return { flipped, count: flipped.length }; }
308
+ export function getSessionHooksGovStatsV2() {
309
+ const profilesByStatus = {}; for (const v of Object.values(SHOK_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _shokPsV2.values()) profilesByStatus[p.status]++;
310
+ const invocationsByStatus = {}; for (const v of Object.values(SHOK_INVOCATION_LIFECYCLE_V2)) invocationsByStatus[v] = 0; for (const i of _shokIsV2.values()) invocationsByStatus[i.status]++;
311
+ return { totalShokProfilesV2: _shokPsV2.size, totalShokInvocationsV2: _shokIsV2.size, maxActiveShokProfilesPerOwner: _shokMaxActive, maxPendingShokInvocationsPerProfile: _shokMaxPending, shokProfileIdleMs: _shokIdleMs, shokInvocationStuckMs: _shokStuckMs, profilesByStatus, invocationsByStatus };
312
+ }
@@ -191,3 +191,84 @@ export class SessionSearchIndex {
191
191
  }
192
192
  }
193
193
  }
194
+
195
+ // ===== V2 Surface: Session Search governance overlay (CLI v0.142.0) =====
196
+ export const SSCH_PROFILE_MATURITY_V2 = Object.freeze({
197
+ PENDING: "pending", ACTIVE: "active", STALE: "stale", ARCHIVED: "archived",
198
+ });
199
+ export const SSCH_QUERY_LIFECYCLE_V2 = Object.freeze({
200
+ QUEUED: "queued", SEARCHING: "searching", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
201
+ });
202
+ const _sschPTrans = new Map([
203
+ [SSCH_PROFILE_MATURITY_V2.PENDING, new Set([SSCH_PROFILE_MATURITY_V2.ACTIVE, SSCH_PROFILE_MATURITY_V2.ARCHIVED])],
204
+ [SSCH_PROFILE_MATURITY_V2.ACTIVE, new Set([SSCH_PROFILE_MATURITY_V2.STALE, SSCH_PROFILE_MATURITY_V2.ARCHIVED])],
205
+ [SSCH_PROFILE_MATURITY_V2.STALE, new Set([SSCH_PROFILE_MATURITY_V2.ACTIVE, SSCH_PROFILE_MATURITY_V2.ARCHIVED])],
206
+ [SSCH_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
207
+ ]);
208
+ const _sschPTerminal = new Set([SSCH_PROFILE_MATURITY_V2.ARCHIVED]);
209
+ const _sschQTrans = new Map([
210
+ [SSCH_QUERY_LIFECYCLE_V2.QUEUED, new Set([SSCH_QUERY_LIFECYCLE_V2.SEARCHING, SSCH_QUERY_LIFECYCLE_V2.CANCELLED])],
211
+ [SSCH_QUERY_LIFECYCLE_V2.SEARCHING, new Set([SSCH_QUERY_LIFECYCLE_V2.COMPLETED, SSCH_QUERY_LIFECYCLE_V2.FAILED, SSCH_QUERY_LIFECYCLE_V2.CANCELLED])],
212
+ [SSCH_QUERY_LIFECYCLE_V2.COMPLETED, new Set()],
213
+ [SSCH_QUERY_LIFECYCLE_V2.FAILED, new Set()],
214
+ [SSCH_QUERY_LIFECYCLE_V2.CANCELLED, new Set()],
215
+ ]);
216
+ const _sschPsV2 = new Map();
217
+ const _sschQsV2 = new Map();
218
+ let _sschMaxActive = 8, _sschMaxPending = 20, _sschIdleMs = 30 * 24 * 60 * 60 * 1000, _sschStuckMs = 30 * 1000;
219
+ function _sschPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
220
+ function _sschCheckP(from, to) { const a = _sschPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid ssch profile transition ${from} → ${to}`); }
221
+ function _sschCheckQ(from, to) { const a = _sschQTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid ssch query transition ${from} → ${to}`); }
222
+ export function setMaxActiveSschProfilesPerOwnerV2(n) { _sschMaxActive = _sschPos(n, "maxActiveSschProfilesPerOwner"); }
223
+ export function getMaxActiveSschProfilesPerOwnerV2() { return _sschMaxActive; }
224
+ export function setMaxPendingSschQueriesPerProfileV2(n) { _sschMaxPending = _sschPos(n, "maxPendingSschQueriesPerProfile"); }
225
+ export function getMaxPendingSschQueriesPerProfileV2() { return _sschMaxPending; }
226
+ export function setSschProfileIdleMsV2(n) { _sschIdleMs = _sschPos(n, "sschProfileIdleMs"); }
227
+ export function getSschProfileIdleMsV2() { return _sschIdleMs; }
228
+ export function setSschQueryStuckMsV2(n) { _sschStuckMs = _sschPos(n, "sschQueryStuckMs"); }
229
+ export function getSschQueryStuckMsV2() { return _sschStuckMs; }
230
+ export function _resetStateSessionSearchV2() { _sschPsV2.clear(); _sschQsV2.clear(); _sschMaxActive = 8; _sschMaxPending = 20; _sschIdleMs = 30 * 24 * 60 * 60 * 1000; _sschStuckMs = 30 * 1000; }
231
+ export function registerSschProfileV2({ id, owner, scope, metadata } = {}) {
232
+ if (!id) throw new Error("ssch profile id required"); if (!owner) throw new Error("ssch profile owner required");
233
+ if (_sschPsV2.has(id)) throw new Error(`ssch profile ${id} already registered`);
234
+ const now = Date.now();
235
+ const p = { id, owner, scope: scope || "all", status: SSCH_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, archivedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
236
+ _sschPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
237
+ }
238
+ function _sschCountActive(owner) { let n = 0; for (const p of _sschPsV2.values()) if (p.owner === owner && p.status === SSCH_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
239
+ export function activateSschProfileV2(id) {
240
+ const p = _sschPsV2.get(id); if (!p) throw new Error(`ssch profile ${id} not found`);
241
+ _sschCheckP(p.status, SSCH_PROFILE_MATURITY_V2.ACTIVE);
242
+ const recovery = p.status === SSCH_PROFILE_MATURITY_V2.STALE;
243
+ if (!recovery && _sschCountActive(p.owner) >= _sschMaxActive) throw new Error(`max active ssch profiles for owner ${p.owner} reached`);
244
+ const now = Date.now(); p.status = SSCH_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
245
+ return { ...p, metadata: { ...p.metadata } };
246
+ }
247
+ export function staleSschProfileV2(id) { const p = _sschPsV2.get(id); if (!p) throw new Error(`ssch profile ${id} not found`); _sschCheckP(p.status, SSCH_PROFILE_MATURITY_V2.STALE); p.status = SSCH_PROFILE_MATURITY_V2.STALE; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
248
+ export function archiveSschProfileV2(id) { const p = _sschPsV2.get(id); if (!p) throw new Error(`ssch profile ${id} not found`); _sschCheckP(p.status, SSCH_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = SSCH_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
249
+ export function touchSschProfileV2(id) { const p = _sschPsV2.get(id); if (!p) throw new Error(`ssch profile ${id} not found`); if (_sschPTerminal.has(p.status)) throw new Error(`cannot touch terminal ssch profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
250
+ export function getSschProfileV2(id) { const p = _sschPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
251
+ export function listSschProfilesV2() { return [..._sschPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
252
+ function _sschCountPending(profileId) { let n = 0; for (const q of _sschQsV2.values()) if (q.profileId === profileId && (q.status === SSCH_QUERY_LIFECYCLE_V2.QUEUED || q.status === SSCH_QUERY_LIFECYCLE_V2.SEARCHING)) n++; return n; }
253
+ export function createSschQueryV2({ id, profileId, q, metadata } = {}) {
254
+ if (!id) throw new Error("ssch query id required"); if (!profileId) throw new Error("ssch query profileId required");
255
+ if (_sschQsV2.has(id)) throw new Error(`ssch query ${id} already exists`);
256
+ if (!_sschPsV2.has(profileId)) throw new Error(`ssch profile ${profileId} not found`);
257
+ if (_sschCountPending(profileId) >= _sschMaxPending) throw new Error(`max pending ssch queries for profile ${profileId} reached`);
258
+ const now = Date.now();
259
+ const r = { id, profileId, q: q || "", status: SSCH_QUERY_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
260
+ _sschQsV2.set(id, r); return { ...r, metadata: { ...r.metadata } };
261
+ }
262
+ export function searchingSschQueryV2(id) { const r = _sschQsV2.get(id); if (!r) throw new Error(`ssch query ${id} not found`); _sschCheckQ(r.status, SSCH_QUERY_LIFECYCLE_V2.SEARCHING); const now = Date.now(); r.status = SSCH_QUERY_LIFECYCLE_V2.SEARCHING; r.updatedAt = now; if (!r.startedAt) r.startedAt = now; return { ...r, metadata: { ...r.metadata } }; }
263
+ export function completeSschQueryV2(id) { const r = _sschQsV2.get(id); if (!r) throw new Error(`ssch query ${id} not found`); _sschCheckQ(r.status, SSCH_QUERY_LIFECYCLE_V2.COMPLETED); const now = Date.now(); r.status = SSCH_QUERY_LIFECYCLE_V2.COMPLETED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; return { ...r, metadata: { ...r.metadata } }; }
264
+ export function failSschQueryV2(id, reason) { const r = _sschQsV2.get(id); if (!r) throw new Error(`ssch query ${id} not found`); _sschCheckQ(r.status, SSCH_QUERY_LIFECYCLE_V2.FAILED); const now = Date.now(); r.status = SSCH_QUERY_LIFECYCLE_V2.FAILED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.failReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
265
+ export function cancelSschQueryV2(id, reason) { const r = _sschQsV2.get(id); if (!r) throw new Error(`ssch query ${id} not found`); _sschCheckQ(r.status, SSCH_QUERY_LIFECYCLE_V2.CANCELLED); const now = Date.now(); r.status = SSCH_QUERY_LIFECYCLE_V2.CANCELLED; r.updatedAt = now; if (!r.settledAt) r.settledAt = now; if (reason) r.metadata.cancelReason = String(reason); return { ...r, metadata: { ...r.metadata } }; }
266
+ export function getSschQueryV2(id) { const r = _sschQsV2.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
267
+ export function listSschQueriesV2() { return [..._sschQsV2.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
268
+ export function autoStaleIdleSschProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _sschPsV2.values()) if (p.status === SSCH_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _sschIdleMs) { p.status = SSCH_PROFILE_MATURITY_V2.STALE; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
269
+ export function autoFailStuckSschQueriesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _sschQsV2.values()) if (r.status === SSCH_QUERY_LIFECYCLE_V2.SEARCHING && r.startedAt != null && (t - r.startedAt) >= _sschStuckMs) { r.status = SSCH_QUERY_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 }; }
270
+ export function getSessionSearchGovStatsV2() {
271
+ const profilesByStatus = {}; for (const v of Object.values(SSCH_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _sschPsV2.values()) profilesByStatus[p.status]++;
272
+ const queriesByStatus = {}; for (const v of Object.values(SSCH_QUERY_LIFECYCLE_V2)) queriesByStatus[v] = 0; for (const r of _sschQsV2.values()) queriesByStatus[r.status]++;
273
+ return { totalSschProfilesV2: _sschPsV2.size, totalSschQueriesV2: _sschQsV2.size, maxActiveSschProfilesPerOwner: _sschMaxActive, maxPendingSschQueriesPerProfile: _sschMaxPending, sschProfileIdleMs: _sschIdleMs, sschQueryStuckMs: _sschStuckMs, profilesByStatus, queriesByStatus };
274
+ }