@askexenow/exe-os 0.9.300 → 0.9.301

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 (244) hide show
  1. package/deploy/compose/erp-nginx/nginx.conf +6 -3
  2. package/dist/active-agent-UNJO6AJ2.js +27 -0
  3. package/dist/active-agent-Y5LSIMVC.js +28 -0
  4. package/dist/agentic-ontology-QEV7GI3T.js +25 -0
  5. package/dist/backfill-metadata-RQZ4BN7G.js +600 -0
  6. package/dist/behaviors-A3L5CWMK.js +46 -0
  7. package/dist/bin/agentic-ontology-backfill.js +5 -5
  8. package/dist/bin/agentic-reflection-backfill.js +6 -6
  9. package/dist/bin/agentic-semantic-label.js +5 -5
  10. package/dist/bin/backfill-conversations.js +4 -4
  11. package/dist/bin/backfill-responses.js +4 -4
  12. package/dist/bin/backfill-vectors.js +5 -5
  13. package/dist/bin/bulk-sync-postgres.js +7 -7
  14. package/dist/bin/cc-doctor.js +4 -4
  15. package/dist/bin/cleanup-stale-review-tasks.js +9 -9
  16. package/dist/bin/cli.js +15 -15
  17. package/dist/bin/exe-agent-config.js +2 -2
  18. package/dist/bin/exe-agent.js +4 -4
  19. package/dist/bin/exe-assign.js +5 -5
  20. package/dist/bin/exe-boot.js +23 -18
  21. package/dist/bin/exe-call.js +4 -4
  22. package/dist/bin/exe-cloud.js +7 -7
  23. package/dist/bin/exe-dispatch.js +9 -9
  24. package/dist/bin/exe-doctor.js +2 -2
  25. package/dist/bin/exe-export-behaviors.js +7 -7
  26. package/dist/bin/exe-forget.js +6 -6
  27. package/dist/bin/exe-gateway.js +7 -7
  28. package/dist/bin/exe-healthcheck.js +4 -4
  29. package/dist/bin/exe-heartbeat.js +9 -9
  30. package/dist/bin/exe-kill.js +12 -12
  31. package/dist/bin/exe-launch-agent.js +16 -16
  32. package/dist/bin/exe-new-employee.js +6 -6
  33. package/dist/bin/exe-pending-messages.js +10 -10
  34. package/dist/bin/exe-pending-notifications.js +9 -9
  35. package/dist/bin/exe-pending-reviews.js +9 -9
  36. package/dist/bin/exe-rename.js +4 -4
  37. package/dist/bin/exe-review.js +11 -11
  38. package/dist/bin/exe-search.js +5 -5
  39. package/dist/bin/exe-session-cleanup.js +14 -14
  40. package/dist/bin/exe-settings.js +7 -7
  41. package/dist/bin/exe-start-codex.js +11 -11
  42. package/dist/bin/exe-start-opencode.js +8 -8
  43. package/dist/bin/exe-status.js +10 -10
  44. package/dist/bin/exe-team.js +3 -3
  45. package/dist/bin/exe-watchdog.js +3 -3
  46. package/dist/bin/git-sweep.js +10 -10
  47. package/dist/bin/graph-backfill.js +4 -4
  48. package/dist/bin/graph-export.js +5 -5
  49. package/dist/bin/import-history.js +7 -7
  50. package/dist/bin/install.js +6 -6
  51. package/dist/bin/intercom-check.js +4 -4
  52. package/dist/bin/mcp-sessions.js +2 -2
  53. package/dist/bin/orchestration-metrics.js +4 -4
  54. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  55. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  56. package/dist/bin/scan-tasks.js +9 -9
  57. package/dist/bin/setup.js +1 -1
  58. package/dist/bin/shard-migrate.js +4 -4
  59. package/dist/capability-cards-VTGDTOZ4.js +89 -0
  60. package/dist/capacity-monitor-G2MAJPRP.js +51 -0
  61. package/dist/catchup-brief-VBGEJQRS.js +175 -0
  62. package/dist/chunk-24JVDLQJ.js +345 -0
  63. package/dist/chunk-2T3OYZMR.js +1158 -0
  64. package/dist/chunk-3ABY3QDX.js +199 -0
  65. package/dist/chunk-3G3ECFB5.js +668 -0
  66. package/dist/chunk-3LHOFGHT.js +280 -0
  67. package/dist/chunk-3RA62PNQ.js +58 -0
  68. package/dist/chunk-3XRS5AVV.js +567 -0
  69. package/dist/chunk-3YK7X5FD.js +1186 -0
  70. package/dist/chunk-4L6PVYFE.js +54 -0
  71. package/dist/chunk-4O67LBMK.js +377 -0
  72. package/dist/chunk-4OIU3N6U.js +167 -0
  73. package/dist/chunk-5SYYMNPE.js +30 -0
  74. package/dist/chunk-5TO5PH7O.js +304 -0
  75. package/dist/chunk-5XD2Y463.js +402 -0
  76. package/dist/chunk-63AENHJC.js +123 -0
  77. package/dist/chunk-673IFJYB.js +731 -0
  78. package/dist/chunk-7AWH47AR.js +448 -0
  79. package/dist/chunk-7P4B6AEP.js +227 -0
  80. package/dist/chunk-7VHOALNC.js +244 -0
  81. package/dist/chunk-ASJHCAVL.js +38 -0
  82. package/dist/chunk-DICIFTCS.js +150 -0
  83. package/dist/chunk-DIFI5JDC.js +76 -0
  84. package/dist/chunk-DPOIJ5SM.js +284 -0
  85. package/dist/chunk-FAZNXNA5.js +33 -0
  86. package/dist/chunk-FCHG5RC4.js +197 -0
  87. package/dist/chunk-FFKSPZO2.js +157 -0
  88. package/dist/chunk-FNHYH5U6.js +331 -0
  89. package/dist/chunk-FWPDAQ6Q.js +1350 -0
  90. package/dist/chunk-FZB73QOI.js +210 -0
  91. package/dist/chunk-GJBR6QLD.js +630 -0
  92. package/dist/chunk-GRSYAHKI.js +535 -0
  93. package/dist/chunk-J5HFRVNW.js +362 -0
  94. package/dist/chunk-J6SD7LT2.js +171 -0
  95. package/dist/chunk-JURL2S27.js +128 -0
  96. package/dist/chunk-KPUYYOFS.js +122 -0
  97. package/dist/chunk-LAFARYU5.js +456 -0
  98. package/dist/chunk-LSDXEHKL.js +381 -0
  99. package/dist/chunk-MS2EOZJQ.js +290 -0
  100. package/dist/chunk-MUIMJGSQ.js +128 -0
  101. package/dist/chunk-NJMPNYBS.js +427 -0
  102. package/dist/chunk-OJACH2JF.js +128 -0
  103. package/dist/chunk-OO4IFABD.js +382 -0
  104. package/dist/chunk-Q65NCNL4.js +1352 -0
  105. package/dist/chunk-QIGS2LRT.js +735 -0
  106. package/dist/chunk-S73ZAJ2S.js +262 -0
  107. package/dist/chunk-SJ4UF7YK.js +1094 -0
  108. package/dist/chunk-SOZ7D77I.js +204 -0
  109. package/dist/chunk-TDX2LK2M.js +240 -0
  110. package/dist/chunk-U5RKGLV6.js +50 -0
  111. package/dist/chunk-UFGTHBHP.js +127 -0
  112. package/dist/chunk-ULCYWCPI.js +1079 -0
  113. package/dist/chunk-V6LOEOXG.js +3372 -0
  114. package/dist/chunk-VAZOVAW4.js +2162 -0
  115. package/dist/chunk-VEUQVKKT.js +185 -0
  116. package/dist/chunk-VIDDJ5RF.js +214 -0
  117. package/dist/chunk-VNB4ROYG.js +348 -0
  118. package/dist/chunk-VWUQFZFB.js +395 -0
  119. package/dist/chunk-W77GRCNA.js +85 -0
  120. package/dist/chunk-WVMG4ZRH.js +14597 -0
  121. package/dist/chunk-WYVOTRRZ.js +129 -0
  122. package/dist/chunk-XQQ7D4I4.js +85 -0
  123. package/dist/chunk-YHJPTIPR.js +836 -0
  124. package/dist/chunk-YLKS7KKC.js +2145 -0
  125. package/dist/chunk-YU3KEVCO.js +333 -0
  126. package/dist/chunk-ZDPU3JTF.js +221 -0
  127. package/dist/chunk-ZDY4LYAJ.js +81 -0
  128. package/dist/chunk-ZG33AACD.js +70 -0
  129. package/dist/chunk-ZKHPZ6KN.js +181 -0
  130. package/dist/chunk-ZO2TM5N5.js +97 -0
  131. package/dist/chunk-ZSUACDQC.js +4388 -0
  132. package/dist/co-activation-XM25BLZM.js +74 -0
  133. package/dist/co-occurrence-CKEMDPWO.js +95 -0
  134. package/dist/core-memory-ZDA76EU3.js +110 -0
  135. package/dist/crdt-sync-BJKZB6T6.js +33 -0
  136. package/dist/crm-webhook-E5PAFAUN.js +10 -0
  137. package/dist/cto-delegation-gate-EMY6ZZ2F.js +280 -0
  138. package/dist/daemon-orchestration-Y5Y6YNE3.js +143 -0
  139. package/dist/db-backup-HFJ53IBU.js +43 -0
  140. package/dist/doc-graph-extractor-ID45AQ2P.js +133 -0
  141. package/dist/dreaming-N6B7KBIE.js +34 -0
  142. package/dist/exe-drift-CPUEAPIU.js +70 -0
  143. package/dist/exe-export-4RTGDV53.js +77 -0
  144. package/dist/exe-import-AZMIF34Z.js +80 -0
  145. package/dist/exe-key-LJV23AJI.js +673 -0
  146. package/dist/exe-snapshot-ZOZBW7V6.js +338 -0
  147. package/dist/fast-db-init-ATRZGHOL.js +7 -0
  148. package/dist/gateway/index.js +8 -8
  149. package/dist/git-staleness-XNOKI4D3.js +112 -0
  150. package/dist/git-task-sweep-3MO4OVND.js +42 -0
  151. package/dist/global-procedures-EBAPPWGZ.js +22 -0
  152. package/dist/graph-auto-extract-VJOUQBPK.js +183 -0
  153. package/dist/hooks/bug-report-worker.js +11 -11
  154. package/dist/hooks/codex-stop-task-finalizer.js +11 -11
  155. package/dist/hooks/commit-complete.js +11 -11
  156. package/dist/hooks/error-recall.js +6 -6
  157. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  158. package/dist/hooks/ingest.js +6 -6
  159. package/dist/hooks/instructions-loaded.js +4 -4
  160. package/dist/hooks/manifest.json +19 -19
  161. package/dist/hooks/notification.js +4 -4
  162. package/dist/hooks/post-compact.js +11 -11
  163. package/dist/hooks/post-tool-combined.js +5 -5
  164. package/dist/hooks/pre-compact.js +12 -12
  165. package/dist/hooks/pre-tool-use.js +14 -14
  166. package/dist/hooks/prompt-submit.js +26 -24
  167. package/dist/hooks/session-end.js +15 -15
  168. package/dist/hooks/session-start.js +12 -12
  169. package/dist/hooks/stop.js +14 -14
  170. package/dist/hooks/subagent-stop.js +10 -10
  171. package/dist/hooks/summary-worker.js +14 -14
  172. package/dist/index.js +17 -17
  173. package/dist/installer-37KFNAWE.js +344 -0
  174. package/dist/installer-F55NR4E2.js +298 -0
  175. package/dist/installer-KOYBUS4J.js +40 -0
  176. package/dist/lib/cloud-sync.js +7 -7
  177. package/dist/lib/consolidation.js +5 -5
  178. package/dist/lib/database.js +2 -2
  179. package/dist/lib/db.js +2 -2
  180. package/dist/lib/employee-templates.js +4 -4
  181. package/dist/lib/employees.js +2 -2
  182. package/dist/lib/exe-daemon.js +40 -40
  183. package/dist/lib/hybrid-search.js +5 -5
  184. package/dist/lib/identity.js +2 -2
  185. package/dist/lib/messaging.js +9 -9
  186. package/dist/lib/reminders.js +3 -3
  187. package/dist/lib/schedules.js +5 -5
  188. package/dist/lib/session-registry.js +4 -4
  189. package/dist/lib/skill-learning.js +6 -6
  190. package/dist/lib/store.js +4 -4
  191. package/dist/lib/task-router.js +3 -3
  192. package/dist/lib/tasks.js +10 -10
  193. package/dist/lib/tmux-routing.js +8 -8
  194. package/dist/lib/token-spend.js +3 -3
  195. package/dist/mcp/register-tools.js +54 -54
  196. package/dist/mcp/server.js +55 -55
  197. package/dist/mcp/tools/complete-reminder.js +4 -4
  198. package/dist/mcp/tools/create-reminder.js +4 -4
  199. package/dist/mcp/tools/create-task.js +12 -12
  200. package/dist/mcp/tools/deactivate-behavior.js +7 -7
  201. package/dist/mcp/tools/list-reminders.js +4 -4
  202. package/dist/mcp/tools/list-tasks.js +12 -12
  203. package/dist/mcp/tools/send-message.js +11 -11
  204. package/dist/mcp/tools/update-task.js +11 -11
  205. package/dist/mcp-http-config-7KJZI7UD.js +31 -0
  206. package/dist/memory-cards-ZIT7ZKL5.js +180 -0
  207. package/dist/memory-graph-extractor-LY2VORZT.js +22 -0
  208. package/dist/memory-poisoning-defense-SEM25TY5.js +224 -0
  209. package/dist/memory-reflection-J2W7CJ7C.js +244 -0
  210. package/dist/notifications-RLMSI4YE.js +47 -0
  211. package/dist/orchestration-events-TGQYA72K.js +27 -0
  212. package/dist/orchestrator-JQD5G3CW.js +35 -0
  213. package/dist/pipeline-router-3Q3YBYSM.js +15 -0
  214. package/dist/plan-limits-YGXTYCW4.js +28 -0
  215. package/dist/project-boot-TDOZKKDR.js +299 -0
  216. package/dist/projection-worker-TMKUSVGD.js +1084 -0
  217. package/dist/prospective-memory-CNJDBNWF.js +232 -0
  218. package/dist/reranker-YQIRNGDM.js +19 -0
  219. package/dist/retrieval-health-M5BVB7EV.js +12 -0
  220. package/dist/review-polling-ZMB3OBPC.js +126 -0
  221. package/dist/runtime/index.js +11 -11
  222. package/dist/session-events-UTMCKDIN.js +38 -0
  223. package/dist/session-kill-telemetry-FRQA5MVD.js +31 -0
  224. package/dist/session-scope-2BD6QLNI.js +88 -0
  225. package/dist/setup-wizard-W64I6SHC.js +12 -0
  226. package/dist/skill-refinement-FXCXTUS2.js +159 -0
  227. package/dist/steward-gate-JDR3SLH3.js +15 -0
  228. package/dist/task-enforcement-WCEA4FZI.js +506 -0
  229. package/dist/task-scope-L5GDL2AV.js +37 -0
  230. package/dist/tasks-crud-S36AFYYM.js +79 -0
  231. package/dist/tasks-notify-W2W2BJRB.js +40 -0
  232. package/dist/tasks-review-34WV7BX2.js +49 -0
  233. package/dist/telemetry-upload-3CSVO3J7.js +741 -0
  234. package/dist/token-budget-ZG2MQ5GD.js +86 -0
  235. package/dist/tool-telemetry-ODL4F2CW.js +17 -0
  236. package/dist/tui/App.js +16 -16
  237. package/dist/tui-data-Z5UF7KEI.js +260 -0
  238. package/dist/wiki-acl-SZFHCEC4.js +111 -0
  239. package/dist/worker-gate-OOO6BWZ6.js +21 -0
  240. package/dist/workflow-engine-QY3IFFR2.js +28 -0
  241. package/dist/worktree-CNOQZBNT.js +28 -0
  242. package/dist/worktree-sweep-SE7ITXC4.js +21 -0
  243. package/package.json +1 -1
  244. package/release-notes.json +88 -88
@@ -0,0 +1,199 @@
1
+ import {
2
+ resolveAnthropicAuth
3
+ } from "./chunk-EJIF4FNT.js";
4
+
5
+ // src/lib/retrieval-health.ts
6
+ async function getRetrievalHealth() {
7
+ let embedDaemon = "unavailable";
8
+ try {
9
+ const { isDaemonAlive } = await import("./mcp-health-WDOB6XUB.js");
10
+ const { alive } = isDaemonAlive();
11
+ if (alive) {
12
+ try {
13
+ const { pingDaemon } = await import("./lib/exe-daemon-client.js");
14
+ const health = await pingDaemon();
15
+ if (health) {
16
+ const worker = health.embedWorker;
17
+ const embeddingField = health.embedding;
18
+ if (worker?.disabledByDesign || embeddingField === "disabled") {
19
+ embedDaemon = "disabled";
20
+ } else if (worker) {
21
+ embedDaemon = worker.forked && worker.ready && worker.pid != null ? "running" : "stopped";
22
+ } else if (embeddingField === "ok") {
23
+ embedDaemon = "running";
24
+ } else {
25
+ embedDaemon = "stopped";
26
+ }
27
+ } else {
28
+ embedDaemon = "stopped";
29
+ }
30
+ } catch {
31
+ embedDaemon = "stopped";
32
+ }
33
+ } else {
34
+ embedDaemon = "unavailable";
35
+ }
36
+ } catch {
37
+ embedDaemon = "unavailable";
38
+ }
39
+ let configuredSearchMode = "hybrid";
40
+ let selfQueryRouterEnabled = true;
41
+ try {
42
+ const { loadConfigSync } = await import("./lib/config.js");
43
+ const cfg = loadConfigSync();
44
+ configuredSearchMode = cfg.searchMode ?? "hybrid";
45
+ selfQueryRouterEnabled = cfg.selfQueryRouter ?? true;
46
+ } catch {
47
+ }
48
+ let actualSearchMode;
49
+ if (configuredSearchMode === "fts") {
50
+ actualSearchMode = "fts+graph";
51
+ } else if (embedDaemon === "running") {
52
+ actualSearchMode = "hybrid (vector+fts+graph)";
53
+ } else {
54
+ actualSearchMode = "fts+graph (degraded from hybrid)";
55
+ }
56
+ let queryRouter;
57
+ if (!selfQueryRouterEnabled) {
58
+ queryRouter = "disabled";
59
+ } else if (embedDaemon === "running" || embedDaemon === "disabled") {
60
+ queryRouter = resolveAnthropicAuth() ? "active" : "fallback";
61
+ } else {
62
+ queryRouter = "fallback";
63
+ }
64
+ let totalMemories = 0;
65
+ let memoriesWithVectors = 0;
66
+ let entities = 0;
67
+ let relationships = 0;
68
+ try {
69
+ const { getClient } = await import("./lib/database.js");
70
+ const client = getClient();
71
+ const [totalRes, vectorRes] = await Promise.all([
72
+ client.execute("SELECT COUNT(*) as cnt FROM memories WHERE status IS NULL OR status != 'deleted'"),
73
+ client.execute("SELECT COUNT(*) as cnt FROM memories WHERE vector IS NOT NULL AND (status IS NULL OR status != 'deleted')")
74
+ ]);
75
+ totalMemories = Number(totalRes.rows[0]?.cnt ?? 0);
76
+ memoriesWithVectors = Number(vectorRes.rows[0]?.cnt ?? 0);
77
+ try {
78
+ const { getGraphStats } = await import("./graph-query-AP5R6ZHO.js");
79
+ const stats = await getGraphStats(client);
80
+ entities = stats.entities;
81
+ relationships = stats.relationships;
82
+ } catch {
83
+ }
84
+ } catch {
85
+ }
86
+ const staleCount = totalMemories - memoriesWithVectors;
87
+ const coveragePct = totalMemories > 0 ? Math.round(memoriesWithVectors / totalMemories * 100) : null;
88
+ let dbRestore = { hasEvents: false, count: 0, last: null, quarantineFiles: 0 };
89
+ try {
90
+ const { getDbRestoreHealth } = await import("./db-restore-events-GNZS42YO.js");
91
+ dbRestore = getDbRestoreHealth();
92
+ } catch {
93
+ }
94
+ const summary = {
95
+ embedDaemon,
96
+ searchMode: {
97
+ configured: configuredSearchMode,
98
+ actual: actualSearchMode
99
+ },
100
+ queryRouter,
101
+ vectorBackfill: {
102
+ totalMemories,
103
+ memoriesWithVectors,
104
+ staleCount,
105
+ coveragePct
106
+ },
107
+ graph: {
108
+ entities,
109
+ relationships
110
+ },
111
+ dbRestore
112
+ };
113
+ return {
114
+ ...summary,
115
+ degradations: computeDegradations(summary)
116
+ };
117
+ }
118
+ function computeDegradations(h) {
119
+ const out = [];
120
+ const isRuntimeDowngrade = h.searchMode.actual.includes("degraded");
121
+ if (isRuntimeDowngrade) {
122
+ out.push({
123
+ code: "retrieval_downgraded",
124
+ severity: "warn",
125
+ message: `Retrieval running as "${h.searchMode.actual}" (configured: "${h.searchMode.configured}")`,
126
+ remediation: h.embedDaemon === "disabled" ? "Embeddings are disabled. Enable them in settings if you want vector retrieval." : "Embed daemon is not running. Start the daemon / embed worker to restore hybrid retrieval."
127
+ });
128
+ } else if (h.searchMode.configured === "fts") {
129
+ out.push({
130
+ code: "embeddings_off",
131
+ severity: "info",
132
+ message: "Search mode is FTS-only by configuration (keyword + graph; no vector retrieval)",
133
+ remediation: 'Switch searchMode to "hybrid" in settings to enable vector retrieval.'
134
+ });
135
+ }
136
+ if (h.queryRouter === "fallback") {
137
+ out.push({
138
+ code: "query_router_fallback",
139
+ severity: "info",
140
+ message: "Self-query-router using rule-based fallback (LLM routing is an optional enrichment; keyword + graph retrieval unaffected)",
141
+ remediation: "To enable LLM-based query routing, provide an Anthropic credential: set ANTHROPIC_API_KEY, or ANTHROPIC_AUTH_TOKEN if you use Claude Code / OpenCode / a subscription-OAuth host."
142
+ });
143
+ }
144
+ if (h.dbRestore.hasEvents) {
145
+ const last = h.dbRestore.last;
146
+ if (last && (last.kind === "restore-failed" || last.kind === "zeroed-no-wal")) {
147
+ out.push({
148
+ code: "db_corruption_unrecovered",
149
+ severity: "critical",
150
+ message: `DB corruption could not be auto-recovered: ${last.detail ?? last.kind} (${last.at})`,
151
+ remediation: "Restore from a known-good backup (`exe-os cloud reupload` or restore a backup); preserved corrupt DB is in ~/.exe-os/."
152
+ });
153
+ } else if (last && (last.kind === "auto-restore" || last.kind === "wal-recovery")) {
154
+ out.push({
155
+ code: "db_auto_restored",
156
+ severity: "warn",
157
+ message: last.kind === "auto-restore" ? `DB was auto-restored from backup${last.restoredFrom ? ` (${last.restoredFrom})` : ""}${typeof last.hoursLost === "number" ? `, ~${last.hoursLost}h data-loss window` : ""} at ${last.at}` : `DB was reconstructed from WAL after a 0-byte corruption at ${last.at}`,
158
+ remediation: "Recent memories may be missing. Run a cloud sync to reconcile and verify recent data."
159
+ });
160
+ } else if (h.dbRestore.quarantineFiles > 0) {
161
+ out.push({
162
+ code: "db_corruption_quarantine",
163
+ severity: "warn",
164
+ message: `${h.dbRestore.quarantineFiles} quarantined corrupt DB file(s) present in ~/.exe-os/ (prior corruption event)`,
165
+ remediation: "Verify data integrity; remove quarantined .corrupt-*/.zeroed-* files once you've confirmed no data is needed."
166
+ });
167
+ }
168
+ }
169
+ return out;
170
+ }
171
+ var SEVERITY_ICON = {
172
+ info: "\u2139\uFE0F",
173
+ // ℹ️
174
+ warn: "\u{1F7E0}",
175
+ // 🟠
176
+ critical: "\u{1F534}"
177
+ // 🔴
178
+ };
179
+ function formatDegradations(degradations, heading = "Degradations") {
180
+ const lines = [];
181
+ lines.push(`### ${heading}`);
182
+ if (degradations.length === 0) {
183
+ lines.push("\u{1F7E2} None \u2014 all retrieval and storage subsystems nominal.");
184
+ return lines.join("\n");
185
+ }
186
+ const order = { critical: 0, warn: 1, info: 2 };
187
+ const sorted = [...degradations].sort((a, b) => order[a.severity] - order[b.severity]);
188
+ for (const d of sorted) {
189
+ lines.push(`${SEVERITY_ICON[d.severity]} ${d.message}`);
190
+ if (d.remediation) lines.push(` \u2192 ${d.remediation}`);
191
+ }
192
+ return lines.join("\n");
193
+ }
194
+
195
+ export {
196
+ getRetrievalHealth,
197
+ computeDegradations,
198
+ formatDegradations
199
+ };