omp-fabric 1.15.0 → 1.17.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 (78) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -0
  3. package/dist/chunks/{chunk-7AEYAPTZ.js → chunk-7OAFYAJC.js} +2 -2
  4. package/dist/chunks/{chunk-MGTKAZYH.js → chunk-C5JMLDIM.js} +4 -4
  5. package/dist/chunks/{chunk-GZQVYRM3.js → chunk-NX2G7BRO.js} +6 -2
  6. package/dist/chunks/{chunk-GZQVYRM3.js.map → chunk-NX2G7BRO.js.map} +2 -2
  7. package/dist/chunks/{chunk-M4DQ47WO.js → chunk-QD2B5FUE.js} +1 -2
  8. package/dist/chunks/chunk-QZJB7QTM.js +248 -0
  9. package/dist/chunks/chunk-QZJB7QTM.js.map +7 -0
  10. package/dist/chunks/chunk-SMNOQLKU.js +53 -0
  11. package/dist/chunks/chunk-SMNOQLKU.js.map +7 -0
  12. package/dist/chunks/{chunk-45XM53NX.js → chunk-VYB7ROEH.js} +2 -2
  13. package/dist/chunks/{chunk-QHBWGAYF.js → chunk-WCV26AOT.js} +2 -2
  14. package/dist/chunks/{chunk-D2ET33NE.js → chunk-WMB6QCWX.js} +2 -5
  15. package/dist/chunks/{chunk-D2ET33NE.js.map → chunk-WMB6QCWX.js.map} +2 -2
  16. package/dist/chunks/{chunk-AOXYDBXC.js → chunk-YD4MAZMT.js} +3 -5
  17. package/dist/chunks/{chunk-AOXYDBXC.js.map → chunk-YD4MAZMT.js.map} +2 -2
  18. package/dist/chunks/{chunk-3A72BUEE.js → chunk-Z7N4PVTM.js} +2 -2
  19. package/dist/commands/fabric.d.ts.map +1 -1
  20. package/dist/compaction/hook.d.ts +0 -2
  21. package/dist/compaction/hook.d.ts.map +1 -1
  22. package/dist/compaction/hook.js +1 -1
  23. package/dist/compaction/lcm-doctor.d.ts +60 -0
  24. package/dist/compaction/lcm-doctor.d.ts.map +1 -0
  25. package/dist/compaction/lcm-maintenance.d.ts +10 -7
  26. package/dist/compaction/lcm-maintenance.d.ts.map +1 -1
  27. package/dist/compaction/lcm-runtime.d.ts +29 -9
  28. package/dist/compaction/lcm-runtime.d.ts.map +1 -1
  29. package/dist/compaction/lcm-runtime.js +480 -121
  30. package/dist/compaction/lcm-runtime.js.map +4 -4
  31. package/dist/fabric-runtime-state.d.ts +13 -1
  32. package/dist/fabric-runtime-state.d.ts.map +1 -1
  33. package/dist/fabric-runtime-state.js +7 -7
  34. package/dist/fabric-runtime-state.js.map +2 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +64 -16
  37. package/dist/index.js.map +2 -2
  38. package/dist/memory/lcm-adapter.d.ts +0 -3
  39. package/dist/memory/lcm-adapter.d.ts.map +1 -1
  40. package/dist/providers/memory-provider.js +2 -2
  41. package/dist/residency/host.js +6 -6
  42. package/dist/residency/omp-entry.js +6 -6
  43. package/dist/storage/lcm-directory.d.ts +16 -0
  44. package/dist/storage/lcm-directory.d.ts.map +1 -0
  45. package/dist/storage/lcm-identity.d.ts +1 -0
  46. package/dist/storage/lcm-identity.d.ts.map +1 -1
  47. package/dist/storage/lcm-ledger.d.ts +11 -0
  48. package/dist/storage/lcm-ledger.d.ts.map +1 -1
  49. package/dist/storage/lcm-migration.d.ts +1 -0
  50. package/dist/storage/lcm-migration.d.ts.map +1 -1
  51. package/dist/ui/controller.d.ts +1 -0
  52. package/dist/ui/controller.d.ts.map +1 -1
  53. package/dist/ui/conversation.js +5 -5
  54. package/dist/ui/dashboard-model.d.ts +1 -1
  55. package/dist/ui/dashboard-model.d.ts.map +1 -1
  56. package/dist/ui/dashboard.d.ts +0 -18
  57. package/dist/ui/dashboard.d.ts.map +1 -1
  58. package/dist/ui/dashboard.js +68 -504
  59. package/dist/ui/dashboard.js.map +4 -4
  60. package/dist/ui/frame.d.ts +7 -0
  61. package/dist/ui/frame.d.ts.map +1 -0
  62. package/dist/ui/lcm-dashboard.d.ts +43 -0
  63. package/dist/ui/lcm-dashboard.d.ts.map +1 -0
  64. package/dist/ui/lcm-dashboard.js +395 -0
  65. package/dist/ui/lcm-dashboard.js.map +7 -0
  66. package/dist/ui/lcm-panel.d.ts +0 -1
  67. package/dist/ui/lcm-panel.d.ts.map +1 -1
  68. package/dist/ui/lcm-panel.js +23 -0
  69. package/dist/ui/lcm-panel.js.map +7 -0
  70. package/docs/compaction.md +28 -1
  71. package/docs/interface.md +1 -0
  72. package/package.json +1 -1
  73. /package/dist/chunks/{chunk-7AEYAPTZ.js.map → chunk-7OAFYAJC.js.map} +0 -0
  74. /package/dist/chunks/{chunk-MGTKAZYH.js.map → chunk-C5JMLDIM.js.map} +0 -0
  75. /package/dist/chunks/{chunk-M4DQ47WO.js.map → chunk-QD2B5FUE.js.map} +0 -0
  76. /package/dist/chunks/{chunk-45XM53NX.js.map → chunk-VYB7ROEH.js.map} +0 -0
  77. /package/dist/chunks/{chunk-QHBWGAYF.js.map → chunk-WCV26AOT.js.map} +0 -0
  78. /package/dist/chunks/{chunk-3A72BUEE.js.map → chunk-Z7N4PVTM.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.17.0
4
+
5
+ ### Fixed
6
+
7
+ - Summaries stopped leaving the frontier one message after they were built. LCM stamped each stored entry with the host's leaf id, which is the id of the newest entry, and then filtered the frontier on it, so a node matched only until the next message arrived. The label is gone from the model: `LcmNode` no longer carries it, leaf and condensed identity no longer hash it, and the four selectors no longer filter on it. A production ledger of 5,400 entries held 106 of 133 nodes behind 36 dead leaf values and served 2 of 400 sources; the same branch now serves 160 of 400. Existing ledgers keep the labels in a column nothing reads, so nothing migrates.
8
+ - Branch isolation now rests where it was already exact: a summary is served only when every source it covers is on the live branch. `selectLeaf` applies that same predicate when it decides which entries are already summarised, so an entry covered only by a node the live branch cannot serve is summarised again instead of being skipped forever.
9
+ - `getFrontier` drops a node whose sources another selected node fully contains, so a rewind that remints a covered prefix can no longer put the same range into a compacted prompt twice.
10
+
11
+ ### Changed
12
+
13
+ - `branch` was removed from `LcmCompactionInput`, `LcmCompactionOutput`, `LcmSummaryNode`, `LcmDashboardNode` and the memory branch binding. `createLeaf` and `createCondensed` now require their arguments rather than defaulting to an unscoped selection.
14
+
15
+ ## 1.16.0
16
+
17
+ ### Added
18
+
19
+ - `/fabric lcm` opens a dashboard of its own for the LCM ledger, with four tabs: `health` runs a doctor over seven checks and offers a one-key repair for the ones that carry one, `graph` pages the summary nodes and opens the raw entry behind any source, `coverage` draws every stored branch entry as one aggregated cell above the text a compaction would serve, and `jobs` lists maintenance work failure-first with `r` to requeue and `l` to release stuck leases.
20
+ - An automatic repair ladder persisted in the ledger (`repair_ladder`): one repair per turn boundary, a failing check preferred over a warning one, five attempts per fault class with 30-second backoff doubling to 8 minutes, parked and reported once the budget is spent, and cleared after its check has been quiet for half an hour.
21
+ - A ledger directory (`projects.json`) that remembers the key each project path was first filed under, so a project whose directory changes inode keeps its history, plus a daily sweep that removes a ledger only when its project directory is gone and nothing has written it for 30 days.
22
+
23
+ ### Fixed
24
+
25
+ - A session file the host has not written yet is reported as absent rather than as a reconciliation error, so a fresh session no longer opens `degraded` with a fault it cannot act on. Reconciliation re-runs once the file appears.
26
+ - Readback hashes each branch entry and appends only the ones whose payload is not stored yet, which lets it run on every message instead of once a turn; an entry amended in place still gains a revision.
27
+ - The frontier cache is invalidated whenever readback, reconciliation, maintenance, compaction, or a repair changes what it summarises, so coverage can no longer report a stale figure.
28
+ - The jobs page orders failures first in SQL, so a recent failure is visible on a ledger holding more jobs than one page.
29
+ - `BACKUP_MANIFEST_VERSION` moves to 3 because the ledger snapshot now digests the repair table; an older backup is refused with the versioned message instead of a raw SQLite error.
30
+
31
+ ### Changed
32
+
33
+ - The LCM view left the shared `/fabric dashboard`; key `3` is gone and the help screen points at `/fabric lcm`.
34
+
3
35
  ## 1.15.0
4
36
 
5
37
  ### Added
package/README.md CHANGED
@@ -122,6 +122,7 @@ Fabric includes a live activity surface in OMP:
122
122
 
123
123
  - A compact widget above the chat (like `pi-supervisor`) whose header follows the current phase while its rows show active/completed agents, active actors, and their recent nested tool or code-change activity.
124
124
  - `/fabric` (or `/fabric dashboard`): opens the **Activity** and **Topology** views. The user-facing OMP session appears as **Main**. You can queue or steer participants and inspect the project topology.
125
+ - `/fabric lcm`: opens the LCM ledger dashboard with health checks and one-key repairs, the summary node graph, branch coverage, and maintenance jobs.
125
126
  - `/fabric settings`: mirrors OMP's `/settings` and writes changes to `fabric.json`. TUI hosts get the searchable settings component; RPC hosts get the same nested sections, value/input/model pickers, list editors, and project/global save scopes through native dialog primitives.
126
127
  - `Tool display` (`compact` by default, or `full`) is configured under `/fabric settings` → **UI**; compact elevates the declared display intent, hides the outer TypeScript, and applies to the current transcript immediately. OMP's tool-expand keybinding (`ctrl+o` by default) expands a compact card to the full transcript.
127
128
 
@@ -10,7 +10,7 @@ import {
10
10
  lifecycleSourceIdentity,
11
11
  loadModelUsage,
12
12
  useBudgetLedger
13
- } from "./chunk-45XM53NX.js";
13
+ } from "./chunk-VYB7ROEH.js";
14
14
  import {
15
15
  normalizeModelAliases,
16
16
  resolveAvailableOmpModel
@@ -685,4 +685,4 @@ if (isMain) {
685
685
  export {
686
686
  runResidentHostFromConfigPath
687
687
  };
688
- //# sourceMappingURL=chunk-7AEYAPTZ.js.map
688
+ //# sourceMappingURL=chunk-7OAFYAJC.js.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  emergencyReduce
3
- } from "./chunk-QHBWGAYF.js";
3
+ } from "./chunk-WCV26AOT.js";
4
4
  import {
5
5
  canonicalLcmPayload
6
- } from "./chunk-GZQVYRM3.js";
6
+ } from "./chunk-NX2G7BRO.js";
7
7
  import {
8
8
  rawContextTokens
9
- } from "./chunk-D2ET33NE.js";
9
+ } from "./chunk-WMB6QCWX.js";
10
10
  import {
11
11
  compactionRequestBoundsError,
12
12
  encodeCompactionRequest
@@ -463,4 +463,4 @@ export {
463
463
  buildPeerCards,
464
464
  awaitPeerSettle
465
465
  };
466
- //# sourceMappingURL=chunk-MGTKAZYH.js.map
466
+ //# sourceMappingURL=chunk-C5JMLDIM.js.map
@@ -41,7 +41,10 @@ function canonicalProjectIdentity(input) {
41
41
  const key = ids.device !== void 0 && ids.inode !== void 0 ? `v1:devino:${ids.device}:${ids.inode}` : `v1:path:${canonicalPath}`;
42
42
  return { version: 1, key, canonicalPath, ...ids, aliases: [normalized, canonicalPath] };
43
43
  }
44
- function defaultLedgerPath(rootDir = path.join(process.env.XDG_STATE_HOME || path.join(process.env.HOME || ".", ".local", "state"), "omp-fabric", "lcm"), projectKey = "default") {
44
+ function defaultLedgerRoot() {
45
+ return path.join(process.env.XDG_STATE_HOME || path.join(process.env.HOME || ".", ".local", "state"), "omp-fabric", "lcm");
46
+ }
47
+ function defaultLedgerPath(rootDir = defaultLedgerRoot(), projectKey = "default") {
45
48
  return path.join(rootDir, `${hash(projectKey).slice(0, 24)}.sqlite`);
46
49
  }
47
50
 
@@ -74,10 +77,11 @@ export {
74
77
  canonicalLcmPayload,
75
78
  hashLcmPayload,
76
79
  canonicalProjectIdentity,
80
+ defaultLedgerRoot,
77
81
  defaultLedgerPath,
78
82
  lcmRawAddress,
79
83
  lcmSummaryAddress,
80
84
  renderLcmSourceAddresses,
81
85
  renderLcmChildAddresses
82
86
  };
83
- //# sourceMappingURL=chunk-GZQVYRM3.js.map
87
+ //# sourceMappingURL=chunk-NX2G7BRO.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/storage/lcm-identity.ts", "../../src/compaction/lcm-addresses.ts"],
4
- "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\n\nexport interface ProjectIdentityInput { recordedCwd?: string; liveCwd?: string }\nexport interface ProjectIdentity { version: 1; key: string; canonicalPath?: string; device?: number; inode?: number; aliases: string[] }\nexport interface SessionEntry { projectKey: string; sessionId: string; entryId: string; revision?: number; role: string; content: string; payloadJson: string; parentEntryId?: string | null; branch?: string | null; recordedCwd?: string; createdAt?: number }\nexport interface RawEntry extends SessionEntry { revision: number; contentHash: string; payloadHash: string; createdAt: number }\nexport interface DeleteConfirmationToken { readonly projectKey: string; readonly value: string; readonly __brand: \"DeleteConfirmationToken\" }\n\nexport const hash = (value: string): string => crypto.createHash(\"sha256\").update(value).digest(\"hex\");\nconst normalize = (target: string): string => path.normalize(path.resolve(target));\nconst statIdentity = (target: string) => { try { const stats = fs.statSync(target); return { device: Number(stats.dev), inode: Number(stats.ino) }; } catch { return {}; } };\n\nexport function canonicalLcmPayload(value: unknown): string {\n const normalized: unknown = JSON.parse(JSON.stringify(value));\n const encode = (item: unknown): string => {\n if (Array.isArray(item)) return `[${item.map(encode).join(\",\")}]`;\n if (item !== null && typeof item === \"object\") return `{${Object.keys(item).sort().map(key => `${JSON.stringify(key)}:${encode((item as Record<string, unknown>)[key])}`).join(\",\")}}`;\n return JSON.stringify(item);\n };\n return encode(normalized);\n}\n\nexport function hashLcmPayload(value: unknown): string { return crypto.createHash(\"sha256\").update(canonicalLcmPayload(value), \"utf8\").digest(\"hex\"); }\n\nexport function createDeleteConfirmationToken(projectKey: string): DeleteConfirmationToken { return { projectKey, value: hash(`delete:${projectKey}`), __brand: \"DeleteConfirmationToken\" }; }\n\nexport function canonicalProjectIdentity(input: ProjectIdentityInput): ProjectIdentity {\n const source = input.recordedCwd?.trim() || input.liveCwd?.trim();\n if (!source) throw new Error(\"project cwd is required\");\n const normalized = normalize(source);\n let canonicalPath = normalized;\n try { canonicalPath = fs.realpathSync.native(normalized); } catch {}\n const ids = statIdentity(canonicalPath);\n const key = ids.device !== undefined && ids.inode !== undefined ? `v1:devino:${ids.device}:${ids.inode}` : `v1:path:${canonicalPath}`;\n return { version: 1, key, canonicalPath, ...ids, aliases: [normalized, canonicalPath] };\n}\n\nexport function defaultLedgerPath(rootDir = path.join(process.env.XDG_STATE_HOME || path.join(process.env.HOME || \".\", \".local\", \"state\"), \"omp-fabric\", \"lcm\"), projectKey = \"default\"): string {\n return path.join(rootDir, `${hash(projectKey).slice(0, 24)}.sqlite`);\n}\n", "import { utf8Bytes } from \"./bounds.js\";\n\nexport interface LcmAddressSource {\n sessionId: string;\n entryId: string;\n revision: number;\n}\n\nconst SOURCES_LABEL = \"sources: \";\nconst CHILDREN_LABEL = \"children: \";\n\nconst renderAddressList = (label: string, addresses: readonly string[], budget: number): string => {\n if (budget <= 0 || addresses.length === 0) return \"\";\n const markerReserve = utf8Bytes(`, +${addresses.length} more`);\n const kept: string[] = [];\n let used = utf8Bytes(label);\n for (const address of addresses) {\n const cost = utf8Bytes(kept.length === 0 ? address : `, ${address}`);\n const reserve = kept.length + 1 < addresses.length ? markerReserve : 0;\n if (used + cost + reserve > budget) break;\n used += cost;\n kept.push(address);\n }\n if (kept.length === 0) return \"\";\n const omitted = addresses.length - kept.length;\n return `${label}${kept.join(\", \")}${omitted > 0 ? `, +${omitted} more` : \"\"}`;\n};\n\nexport const lcmRawAddress = (source: LcmAddressSource): string =>\n `lcm.raw:${source.sessionId}:${source.entryId}:${source.revision}`;\n\nexport const lcmSummaryAddress = (nodeId: string): string => `lcm.summary:${nodeId}`;\n\nexport const renderLcmSourceAddresses = (\n sources: readonly LcmAddressSource[],\n budget: number,\n): string => renderAddressList(SOURCES_LABEL, sources.map(lcmRawAddress), budget);\n\nexport const renderLcmChildAddresses = (\n nodeIds: readonly string[],\n budget: number,\n): string => renderAddressList(CHILDREN_LABEL, nodeIds.map(lcmSummaryAddress), budget);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\n\nexport interface ProjectIdentityInput { recordedCwd?: string; liveCwd?: string }\nexport interface ProjectIdentity { version: 1; key: string; canonicalPath?: string; device?: number; inode?: number; aliases: string[] }\nexport interface SessionEntry { projectKey: string; sessionId: string; entryId: string; revision?: number; role: string; content: string; payloadJson: string; parentEntryId?: string | null; branch?: string | null; recordedCwd?: string; createdAt?: number }\nexport interface RawEntry extends SessionEntry { revision: number; contentHash: string; payloadHash: string; createdAt: number }\nexport interface DeleteConfirmationToken { readonly projectKey: string; readonly value: string; readonly __brand: \"DeleteConfirmationToken\" }\n\nexport const hash = (value: string): string => crypto.createHash(\"sha256\").update(value).digest(\"hex\");\nconst normalize = (target: string): string => path.normalize(path.resolve(target));\nconst statIdentity = (target: string) => { try { const stats = fs.statSync(target); return { device: Number(stats.dev), inode: Number(stats.ino) }; } catch { return {}; } };\n\nexport function canonicalLcmPayload(value: unknown): string {\n const normalized: unknown = JSON.parse(JSON.stringify(value));\n const encode = (item: unknown): string => {\n if (Array.isArray(item)) return `[${item.map(encode).join(\",\")}]`;\n if (item !== null && typeof item === \"object\") return `{${Object.keys(item).sort().map(key => `${JSON.stringify(key)}:${encode((item as Record<string, unknown>)[key])}`).join(\",\")}}`;\n return JSON.stringify(item);\n };\n return encode(normalized);\n}\n\nexport function hashLcmPayload(value: unknown): string { return crypto.createHash(\"sha256\").update(canonicalLcmPayload(value), \"utf8\").digest(\"hex\"); }\n\nexport function createDeleteConfirmationToken(projectKey: string): DeleteConfirmationToken { return { projectKey, value: hash(`delete:${projectKey}`), __brand: \"DeleteConfirmationToken\" }; }\n\nexport function canonicalProjectIdentity(input: ProjectIdentityInput): ProjectIdentity {\n const source = input.recordedCwd?.trim() || input.liveCwd?.trim();\n if (!source) throw new Error(\"project cwd is required\");\n const normalized = normalize(source);\n let canonicalPath = normalized;\n try { canonicalPath = fs.realpathSync.native(normalized); } catch {}\n const ids = statIdentity(canonicalPath);\n const key = ids.device !== undefined && ids.inode !== undefined ? `v1:devino:${ids.device}:${ids.inode}` : `v1:path:${canonicalPath}`;\n return { version: 1, key, canonicalPath, ...ids, aliases: [normalized, canonicalPath] };\n}\n\nexport function defaultLedgerRoot(): string {\n return path.join(process.env.XDG_STATE_HOME || path.join(process.env.HOME || \".\", \".local\", \"state\"), \"omp-fabric\", \"lcm\");\n}\n\nexport function defaultLedgerPath(rootDir = defaultLedgerRoot(), projectKey = \"default\"): string {\n return path.join(rootDir, `${hash(projectKey).slice(0, 24)}.sqlite`);\n}\n", "import { utf8Bytes } from \"./bounds.js\";\n\nexport interface LcmAddressSource {\n sessionId: string;\n entryId: string;\n revision: number;\n}\n\nconst SOURCES_LABEL = \"sources: \";\nconst CHILDREN_LABEL = \"children: \";\n\nconst renderAddressList = (label: string, addresses: readonly string[], budget: number): string => {\n if (budget <= 0 || addresses.length === 0) return \"\";\n const markerReserve = utf8Bytes(`, +${addresses.length} more`);\n const kept: string[] = [];\n let used = utf8Bytes(label);\n for (const address of addresses) {\n const cost = utf8Bytes(kept.length === 0 ? address : `, ${address}`);\n const reserve = kept.length + 1 < addresses.length ? markerReserve : 0;\n if (used + cost + reserve > budget) break;\n used += cost;\n kept.push(address);\n }\n if (kept.length === 0) return \"\";\n const omitted = addresses.length - kept.length;\n return `${label}${kept.join(\", \")}${omitted > 0 ? `, +${omitted} more` : \"\"}`;\n};\n\nexport const lcmRawAddress = (source: LcmAddressSource): string =>\n `lcm.raw:${source.sessionId}:${source.entryId}:${source.revision}`;\n\nexport const lcmSummaryAddress = (nodeId: string): string => `lcm.summary:${nodeId}`;\n\nexport const renderLcmSourceAddresses = (\n sources: readonly LcmAddressSource[],\n budget: number,\n): string => renderAddressList(SOURCES_LABEL, sources.map(lcmRawAddress), budget);\n\nexport const renderLcmChildAddresses = (\n nodeIds: readonly string[],\n budget: number,\n): string => renderAddressList(CHILDREN_LABEL, nodeIds.map(lcmSummaryAddress), budget);\n"],
5
+ "mappings": ";;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,YAAY;AAQZ,IAAM,OAAO,CAAC,UAA0B,OAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACrG,IAAM,YAAY,CAAC,WAA2B,KAAK,UAAU,KAAK,QAAQ,MAAM,CAAC;AACjF,IAAM,eAAe,CAAC,WAAmB;AAAE,MAAI;AAAE,UAAM,QAAQ,GAAG,SAAS,MAAM;AAAG,WAAO,EAAE,QAAQ,OAAO,MAAM,GAAG,GAAG,OAAO,OAAO,MAAM,GAAG,EAAE;AAAA,EAAG,QAAQ;AAAE,WAAO,CAAC;AAAA,EAAG;AAAE;AAEpK,SAAS,oBAAoB,OAAwB;AAC1D,QAAM,aAAsB,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAC5D,QAAM,SAAS,CAAC,SAA0B;AACxC,QAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,IAAI,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAC9D,QAAI,SAAS,QAAQ,OAAO,SAAS,SAAU,QAAO,IAAI,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,IAAI,SAAO,GAAG,KAAK,UAAU,GAAG,CAAC,IAAI,OAAQ,KAAiC,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AACnL,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B;AACA,SAAO,OAAO,UAAU;AAC1B;AAEO,SAAS,eAAe,OAAwB;AAAE,SAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,oBAAoB,KAAK,GAAG,MAAM,EAAE,OAAO,KAAK;AAAG;AAI/I,SAAS,yBAAyB,OAA8C;AACrF,QAAM,SAAS,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS,KAAK;AAChE,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,yBAAyB;AACtD,QAAM,aAAa,UAAU,MAAM;AACnC,MAAI,gBAAgB;AACpB,MAAI;AAAE,oBAAgB,GAAG,aAAa,OAAO,UAAU;AAAA,EAAG,QAAQ;AAAA,EAAC;AACnE,QAAM,MAAM,aAAa,aAAa;AACtC,QAAM,MAAM,IAAI,WAAW,UAAa,IAAI,UAAU,SAAY,aAAa,IAAI,MAAM,IAAI,IAAI,KAAK,KAAK,WAAW,aAAa;AACnI,SAAO,EAAE,SAAS,GAAG,KAAK,eAAe,GAAG,KAAK,SAAS,CAAC,YAAY,aAAa,EAAE;AACxF;AAEO,SAAS,oBAA4B;AAC1C,SAAO,KAAK,KAAK,QAAQ,IAAI,kBAAkB,KAAK,KAAK,QAAQ,IAAI,QAAQ,KAAK,UAAU,OAAO,GAAG,cAAc,KAAK;AAC3H;AAEO,SAAS,kBAAkB,UAAU,kBAAkB,GAAG,aAAa,WAAmB;AAC/F,SAAO,KAAK,KAAK,SAAS,GAAG,KAAK,UAAU,EAAE,MAAM,GAAG,EAAE,CAAC,SAAS;AACrE;;;ACrCA,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB,CAAC,OAAe,WAA8B,WAA2B;AACjG,MAAI,UAAU,KAAK,UAAU,WAAW,EAAG,QAAO;AAClD,QAAM,gBAAgB,UAAU,MAAM,UAAU,MAAM,OAAO;AAC7D,QAAM,OAAiB,CAAC;AACxB,MAAI,OAAO,UAAU,KAAK;AAC1B,aAAW,WAAW,WAAW;AAC/B,UAAM,OAAO,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,OAAO,EAAE;AACnE,UAAM,UAAU,KAAK,SAAS,IAAI,UAAU,SAAS,gBAAgB;AACrE,QAAI,OAAO,OAAO,UAAU,OAAQ;AACpC,YAAQ;AACR,SAAK,KAAK,OAAO;AAAA,EACnB;AACA,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,UAAU,UAAU,SAAS,KAAK;AACxC,SAAO,GAAG,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC,GAAG,UAAU,IAAI,MAAM,OAAO,UAAU,EAAE;AAC7E;AAEO,IAAM,gBAAgB,CAAC,WAC5B,WAAW,OAAO,SAAS,IAAI,OAAO,OAAO,IAAI,OAAO,QAAQ;AAE3D,IAAM,oBAAoB,CAAC,WAA2B,eAAe,MAAM;AAE3E,IAAM,2BAA2B,CACtC,SACA,WACW,kBAAkB,eAAe,QAAQ,IAAI,aAAa,GAAG,MAAM;AAEzE,IAAM,0BAA0B,CACrC,SACA,WACW,kBAAkB,gBAAgB,QAAQ,IAAI,iBAAiB,GAAG,MAAM;",
6
6
  "names": []
7
7
  }
@@ -10,7 +10,6 @@ var lcmStatusLine = (report) => {
10
10
 
11
11
  export {
12
12
  reconcileLcmState,
13
- lcmStatusLabel,
14
13
  lcmStatusLine
15
14
  };
16
- //# sourceMappingURL=chunk-M4DQ47WO.js.map
15
+ //# sourceMappingURL=chunk-QD2B5FUE.js.map
@@ -0,0 +1,248 @@
1
+ import {
2
+ formatClock,
3
+ padToWidth,
4
+ safeText,
5
+ wrapPlainText
6
+ } from "./chunk-KIH3KLZU.js";
7
+
8
+ // src/ui/lcm-panel.ts
9
+ import { truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
10
+ var LCM_NODE_PAGE = 200;
11
+ var COVERAGE_COMPLETE_RATIO = 0.95;
12
+ var EMERGENCY_MODEL_HASH = "emergency";
13
+ var readLcmSnapshot = (source, offset) => {
14
+ const nodes = source.nodes(LCM_NODE_PAGE, offset);
15
+ return {
16
+ report: source.report(),
17
+ coverage: source.coverage(),
18
+ preview: source.preview(),
19
+ coverageMap: source.coverageMap(),
20
+ nodes,
21
+ offset,
22
+ hasMore: nodes.length === LCM_NODE_PAGE
23
+ };
24
+ };
25
+ var lcmNodeDetail = (source, nodeId) => {
26
+ const detail = source.node(nodeId);
27
+ if (!detail) return void 0;
28
+ return {
29
+ node: detail.node,
30
+ revisions: [...detail.revisions].sort((left, right) => left.revision - right.revision),
31
+ ancestors: detail.ancestors
32
+ };
33
+ };
34
+ var formatBytes = (bytes) => {
35
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
36
+ if (bytes < 1024) return `${Math.round(bytes)} B`;
37
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} kB`;
38
+ return `${(bytes / 1048576).toFixed(1)} MB`;
39
+ };
40
+ var formatPercent = (value) => value > 0 && value < 1 ? `${value.toFixed(value < 0.1 ? 2 : 1)}%` : `${Math.round(value)}%`;
41
+ var isEmergency = (modelHash) => modelHash === EMERGENCY_MODEL_HASH;
42
+ var originLabel = (modelHash) => isEmergency(modelHash) ? "excerpt" : "model";
43
+ var shortId = (value) => value.length > 10 ? `${value.slice(0, 10)}\u2026` : value;
44
+ var textBytes = (text) => text ? Buffer.byteLength(text, "utf8") : 0;
45
+ var lcmUnavailableLine = () => "LCM ledger unavailable \xB7 set compaction.engine to lcm and start a session to populate it";
46
+ var bandRun = (theme, char, length) => {
47
+ const color = char === "\u2588" ? "success" : char === "\u2593" ? "warning" : "dim";
48
+ return theme.fg(color, char.repeat(length));
49
+ };
50
+ var coverageBand = (theme, map, cells) => {
51
+ if (cells <= 0) return "";
52
+ if (map.length === 0) return theme.fg("dim", "\xB7".repeat(cells));
53
+ let band = "";
54
+ let runChar = "";
55
+ let runLength = 0;
56
+ for (let cell = 0; cell < cells; cell++) {
57
+ const start = Math.min(map.length - 1, Math.floor(cell * map.length / cells));
58
+ const end = Math.min(map.length, Math.max(start + 1, Math.floor((cell + 1) * map.length / cells)));
59
+ let covered = 0;
60
+ for (let index = start; index < end; index++) {
61
+ if (map[index].covered) covered++;
62
+ }
63
+ const char = covered === 0 ? "\u2591" : covered === end - start ? "\u2588" : "\u2593";
64
+ if (char === runChar) {
65
+ runLength++;
66
+ continue;
67
+ }
68
+ if (runLength > 0) band += bandRun(theme, runChar, runLength);
69
+ runChar = char;
70
+ runLength = 1;
71
+ }
72
+ if (runLength > 0) band += bandRun(theme, runChar, runLength);
73
+ return band;
74
+ };
75
+ var renderLcmCoverageLine = (theme, snapshot, width) => {
76
+ const { active, covered } = snapshot.coverage;
77
+ const label = `${covered}/${active} covered ${formatPercent(active > 0 ? covered / active * 100 : 0)}`;
78
+ const cells = Math.max(0, width - visibleWidth(label) - 1);
79
+ const band = coverageBand(theme, snapshot.coverageMap, cells);
80
+ return truncateToWidth(`${band} ${theme.fg("muted", label)}`, width, "");
81
+ };
82
+ var nodeRow = (node) => {
83
+ const indent = " ".repeat(Math.min(node.depth, 8));
84
+ const glyph = node.kind === "condensed" ? "\u25C6" : "\u25C7";
85
+ return safeText(
86
+ `${indent}${glyph} ${node.kind} ${originLabel(node.modelHash)} \xB7 ${node.sources.length} src \xB7 ${node.children.length} ch \xB7 ${formatBytes(textBytes(node.text))} \xB7 ${shortId(node.nodeId)}`
87
+ );
88
+ };
89
+ var renderLcmNodeLines = (theme, snapshot, width, height, selectedIndex) => {
90
+ const available = Math.max(1, height);
91
+ const total = snapshot.nodes.length;
92
+ const heading = `nodes ${total === 0 ? 0 : snapshot.offset + 1}-${snapshot.offset + total}${snapshot.hasMore ? "+" : ""}`;
93
+ const lines = [theme.fg("accent", truncateToWidth(heading, width, ""))];
94
+ if (total === 0) {
95
+ lines.push(theme.fg("dim", " (no summary nodes on the active branch yet)"));
96
+ while (lines.length < available) lines.push("");
97
+ return lines.slice(0, available);
98
+ }
99
+ const rows = Math.max(1, available - 1);
100
+ const start = Math.max(
101
+ 0,
102
+ Math.min(selectedIndex - Math.floor(rows / 2), Math.max(0, total - rows))
103
+ );
104
+ for (let index = start; index < Math.min(total, start + rows); index++) {
105
+ const node = snapshot.nodes[index];
106
+ const emergency = isEmergency(node.modelHash);
107
+ let line = theme.fg(emergency ? "warning" : "muted", nodeRow(node));
108
+ if (index === selectedIndex) line = theme.bg("selectedBg", padToWidth(line, width));
109
+ lines.push(truncateToWidth(line, width, ""));
110
+ }
111
+ while (lines.length < available) lines.push("");
112
+ return lines.slice(0, available);
113
+ };
114
+ var renderLcmPreviewLines = (theme, snapshot, width, height) => {
115
+ const available = Math.max(1, height);
116
+ const preview = snapshot.preview;
117
+ const complete = preview.activeSources > 0 && preview.coveredSources / preview.activeSources >= COVERAGE_COMPLETE_RATIO;
118
+ const ratio = complete && preview.sourceBytes > 0 ? ` \xB7 assembled text is ${formatPercent(preview.summaryBytes / preview.sourceBytes * 100)} of the session payload it replaces` : "";
119
+ const headline = `preview \xB7 frontier holds ${preview.coveredSources}/${preview.activeSources} sources \xB7 ${preview.nodes} nodes assemble to ${formatBytes(preview.summaryBytes)} \xB7 session stored payload ${formatBytes(preview.sourceBytes)}${ratio}`;
120
+ const lines = wrapPlainText(headline, width, 2).map((line) => theme.fg("accent", line));
121
+ const body = wrapPlainText(preview.text, width, Math.max(1, available - lines.length));
122
+ for (const line of body) lines.push(theme.fg("dim", line));
123
+ if (body.length === 0) lines.push(theme.fg("dim", "(the frontier assembles no text yet)"));
124
+ while (lines.length < available) lines.push("");
125
+ return lines.slice(0, available);
126
+ };
127
+ var revisionColumns = (theme, detail, width, rows) => {
128
+ const current = detail.revisions[detail.revisions.length - 1];
129
+ const previous = detail.revisions[detail.revisions.length - 2];
130
+ if (!current) return [];
131
+ if (!previous) {
132
+ const lines2 = [theme.fg("accent", `text \xB7 current revision ${current.revision} \xB7 ${originLabel(current.modelHash)}`)];
133
+ for (const line of wrapPlainText(current.text, width, Math.max(1, rows - 1))) {
134
+ lines2.push(theme.fg("dim", line));
135
+ }
136
+ return lines2;
137
+ }
138
+ const label = (revision, name) => `${name} revision ${revision.revision} \xB7 ${originLabel(revision.modelHash)}`;
139
+ if (width < 60) {
140
+ const lines2 = [theme.fg("accent", label(current, "current"))];
141
+ for (const line of wrapPlainText(current.text, width, Math.max(1, Math.floor((rows - 2) / 2)))) {
142
+ lines2.push(theme.fg("dim", line));
143
+ }
144
+ lines2.push(theme.fg("accent", label(previous, "previous")));
145
+ for (const line of wrapPlainText(previous.text, width, Math.max(1, Math.floor((rows - 2) / 2)))) {
146
+ lines2.push(theme.fg("muted", line));
147
+ }
148
+ return lines2;
149
+ }
150
+ const columnWidth = Math.floor((width - 1) / 2);
151
+ const body = Math.max(1, rows - 1);
152
+ const previousBody = wrapPlainText(previous.text, columnWidth, body);
153
+ const currentBody = wrapPlainText(current.text, columnWidth, body);
154
+ const lines = [
155
+ `${padToWidth(theme.fg("accent", label(previous, "previous")), columnWidth)}${theme.fg("borderMuted", "\u2502")}${theme.fg("accent", label(current, "current"))}`
156
+ ];
157
+ for (let index = 0; index < body; index++) {
158
+ const left = previousBody[index];
159
+ const right = currentBody[index];
160
+ if (left === void 0 && right === void 0) break;
161
+ lines.push(
162
+ `${padToWidth(theme.fg("muted", left ?? ""), columnWidth)}${theme.fg("borderMuted", "\u2502")}${theme.fg("dim", right ?? "")}`
163
+ );
164
+ }
165
+ return lines;
166
+ };
167
+ var renderLcmDetailLines = (theme, detail, sourceIndex, entry, width, height) => {
168
+ const available = Math.max(1, height);
169
+ const node = detail.node;
170
+ const lines = [
171
+ theme.fg(
172
+ isEmergency(node.modelHash) ? "warning" : "accent",
173
+ truncateToWidth(
174
+ safeText(
175
+ `${node.kind} \xB7 ${originLabel(node.modelHash)} \xB7 depth ${node.depth} \xB7 ${node.state} \xB7 ${formatClock(node.createdAt)} \xB7 ${node.nodeId}`
176
+ ),
177
+ width,
178
+ ""
179
+ )
180
+ ),
181
+ theme.fg(
182
+ "dim",
183
+ truncateToWidth(safeText(`model hash ${node.modelHash}`), width, "")
184
+ ),
185
+ theme.fg(
186
+ "dim",
187
+ truncateToWidth(
188
+ safeText(`ancestors ${detail.ancestors.length > 0 ? detail.ancestors.join(" \u203A ") : "none"}`),
189
+ width,
190
+ ""
191
+ )
192
+ ),
193
+ theme.fg(
194
+ "dim",
195
+ truncateToWidth(
196
+ safeText(
197
+ `children (${node.children.length}) ${node.children.length > 0 ? node.children.map(shortId).join(" ") : "none"}`
198
+ ),
199
+ width,
200
+ ""
201
+ )
202
+ ),
203
+ theme.fg("accent", truncateToWidth(`sources (${node.sources.length})`, width, ""))
204
+ ];
205
+ const sourceRows = Math.min(node.sources.length, Math.max(1, Math.floor(available / 4)));
206
+ const sourceStart = Math.max(
207
+ 0,
208
+ Math.min(sourceIndex - Math.floor(sourceRows / 2), Math.max(0, node.sources.length - sourceRows))
209
+ );
210
+ for (let index = sourceStart; index < Math.min(node.sources.length, sourceStart + sourceRows); index++) {
211
+ const source = node.sources[index];
212
+ const text = ` ${source.sessionId}/${source.entryId}@${source.revision}`;
213
+ const line = index === sourceIndex ? theme.bg("selectedBg", padToWidth(theme.fg("muted", text), width)) : theme.fg("muted", text);
214
+ lines.push(truncateToWidth(line, width, ""));
215
+ }
216
+ if (node.sources.length === 0) lines.push(theme.fg("dim", " (no sources)"));
217
+ if (entry) {
218
+ lines.push(
219
+ theme.fg(
220
+ "accent",
221
+ truncateToWidth(
222
+ safeText(`raw entry \xB7 ${entry.role} \xB7 ${formatClock(entry.createdAt)} \xB7 ${formatBytes(Buffer.byteLength(entry.payloadJson, "utf8"))} stored`),
223
+ width,
224
+ ""
225
+ )
226
+ )
227
+ );
228
+ for (const line of wrapPlainText(entry.content, width, Math.max(1, Math.floor(available / 4)))) {
229
+ lines.push(theme.fg("dim", line));
230
+ }
231
+ }
232
+ const remaining = Math.max(1, available - lines.length);
233
+ for (const line of revisionColumns(theme, detail, width, remaining)) lines.push(line);
234
+ while (lines.length < available) lines.push("");
235
+ return lines.slice(0, available);
236
+ };
237
+
238
+ export {
239
+ LCM_NODE_PAGE,
240
+ readLcmSnapshot,
241
+ lcmNodeDetail,
242
+ lcmUnavailableLine,
243
+ renderLcmCoverageLine,
244
+ renderLcmNodeLines,
245
+ renderLcmPreviewLines,
246
+ renderLcmDetailLines
247
+ };
248
+ //# sourceMappingURL=chunk-QZJB7QTM.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/ui/lcm-panel.ts"],
4
+ "sourcesContent": ["import type { Theme } from \"@oh-my-pi/pi-coding-agent\";\nimport { truncateToWidth, visibleWidth } from \"@oh-my-pi/pi-tui\";\nimport type { LcmDashboardNode, LcmStatusSource } from \"../fabric-runtime-state.js\";\nimport { lcmStatusLabel } from \"../compaction/lcm-status.js\";\nimport { formatClock, formatCost, padToWidth, safeText, wrapPlainText } from \"./format.js\";\n\nexport const LCM_NODE_PAGE = 200;\n\nconst COVERAGE_COMPLETE_RATIO = 0.95;\nconst EMERGENCY_MODEL_HASH = \"emergency\";\n\nexport interface LcmViewSnapshot {\n report: ReturnType<LcmStatusSource[\"report\"]>;\n coverage: ReturnType<LcmStatusSource[\"coverage\"]>;\n preview: ReturnType<LcmStatusSource[\"preview\"]>;\n coverageMap: ReturnType<LcmStatusSource[\"coverageMap\"]>;\n nodes: LcmDashboardNode[];\n offset: number;\n hasMore: boolean;\n}\n\nexport interface LcmNodeDetail {\n node: LcmDashboardNode;\n revisions: Array<{ revision: number; text: string; modelHash: string; createdAt: number }>;\n ancestors: string[];\n}\n\nexport interface LcmSourceEntry {\n payloadJson: string;\n content: string;\n role: string;\n createdAt: number;\n}\n\n/** Reads every ledger projection the LCM view needs in one pass so rendering never walks the ledger. */\nexport const readLcmSnapshot = (source: LcmStatusSource, offset: number): LcmViewSnapshot => {\n const nodes = source.nodes(LCM_NODE_PAGE, offset);\n return {\n report: source.report(),\n coverage: source.coverage(),\n preview: source.preview(),\n coverageMap: source.coverageMap(),\n nodes,\n offset,\n hasMore: nodes.length === LCM_NODE_PAGE,\n };\n};\n\nexport const lcmNodeDetail = (\n source: LcmStatusSource,\n nodeId: string,\n): LcmNodeDetail | undefined => {\n const detail = source.node(nodeId);\n if (!detail) return undefined;\n return {\n node: detail.node,\n revisions: [...detail.revisions].sort((left, right) => left.revision - right.revision),\n ancestors: detail.ancestors,\n };\n};\n\nconst formatBytes = (bytes: number): string => {\n if (!Number.isFinite(bytes) || bytes <= 0) return \"0 B\";\n if (bytes < 1_024) return `${Math.round(bytes)} B`;\n if (bytes < 1_048_576) return `${(bytes / 1_024).toFixed(1)} kB`;\n return `${(bytes / 1_048_576).toFixed(1)} MB`;\n};\n\nconst formatPercent = (value: number): string =>\n value > 0 && value < 1 ? `${value.toFixed(value < 0.1 ? 2 : 1)}%` : `${Math.round(value)}%`;\n\nconst isEmergency = (modelHash: string): boolean => modelHash === EMERGENCY_MODEL_HASH;\n\nconst originLabel = (modelHash: string): string => (isEmergency(modelHash) ? \"excerpt\" : \"model\");\n\nconst shortId = (value: string): string => (value.length > 10 ? `${value.slice(0, 10)}\u2026` : value);\n\nconst textBytes = (text: string | undefined): number =>\n text ? Buffer.byteLength(text, \"utf8\") : 0;\n\nexport const lcmUnavailableLine = (): string =>\n \"LCM ledger unavailable \u00B7 set compaction.engine to lcm and start a session to populate it\";\n\nconst bandRun = (theme: Theme, char: string, length: number): string => {\n const color = char === \"\u2588\" ? \"success\" : char === \"\u2593\" ? \"warning\" : \"dim\";\n return theme.fg(color, char.repeat(length));\n};\n\nconst coverageBand = (\n theme: Theme,\n map: LcmViewSnapshot[\"coverageMap\"],\n cells: number,\n): string => {\n if (cells <= 0) return \"\";\n if (map.length === 0) return theme.fg(\"dim\", \"\u00B7\".repeat(cells));\n let band = \"\";\n let runChar = \"\";\n let runLength = 0;\n for (let cell = 0; cell < cells; cell++) {\n const start = Math.min(map.length - 1, Math.floor((cell * map.length) / cells));\n const end = Math.min(map.length, Math.max(start + 1, Math.floor(((cell + 1) * map.length) / cells)));\n let covered = 0;\n for (let index = start; index < end; index++) {\n if (map[index]!.covered) covered++;\n }\n const char = covered === 0 ? \"\u2591\" : covered === end - start ? \"\u2588\" : \"\u2593\";\n if (char === runChar) {\n runLength++;\n continue;\n }\n if (runLength > 0) band += bandRun(theme, runChar, runLength);\n runChar = char;\n runLength = 1;\n }\n if (runLength > 0) band += bandRun(theme, runChar, runLength);\n return band;\n};\n\nexport const renderLcmCoverageLine = (\n theme: Theme,\n snapshot: LcmViewSnapshot,\n width: number,\n): string => {\n const { active, covered } = snapshot.coverage;\n const label = `${covered}/${active} covered ${formatPercent(active > 0 ? (covered / active) * 100 : 0)}`;\n const cells = Math.max(0, width - visibleWidth(label) - 1);\n const band = coverageBand(theme, snapshot.coverageMap, cells);\n return truncateToWidth(`${band} ${theme.fg(\"muted\", label)}`, width, \"\");\n};\n\nconst nodeRow = (node: LcmDashboardNode): string => {\n const indent = \" \".repeat(Math.min(node.depth, 8));\n const glyph = node.kind === \"condensed\" ? \"\u25C6\" : \"\u25C7\";\n return safeText(\n `${indent}${glyph} ${node.kind} ${originLabel(node.modelHash)} \u00B7 ${node.sources.length} src \u00B7 ${node.children.length} ch \u00B7 ${formatBytes(textBytes(node.text))} \u00B7 ${shortId(node.nodeId)}`,\n );\n};\n\nexport const renderLcmNodeLines = (\n theme: Theme,\n snapshot: LcmViewSnapshot,\n width: number,\n height: number,\n selectedIndex: number,\n): string[] => {\n const available = Math.max(1, height);\n const total = snapshot.nodes.length;\n const heading = `nodes ${total === 0 ? 0 : snapshot.offset + 1}-${snapshot.offset + total}${snapshot.hasMore ? \"+\" : \"\"}`;\n const lines = [theme.fg(\"accent\", truncateToWidth(heading, width, \"\"))];\n if (total === 0) {\n lines.push(theme.fg(\"dim\", \" (no summary nodes on the active branch yet)\"));\n while (lines.length < available) lines.push(\"\");\n return lines.slice(0, available);\n }\n const rows = Math.max(1, available - 1);\n const start = Math.max(\n 0,\n Math.min(selectedIndex - Math.floor(rows / 2), Math.max(0, total - rows)),\n );\n for (let index = start; index < Math.min(total, start + rows); index++) {\n const node = snapshot.nodes[index]!;\n const emergency = isEmergency(node.modelHash);\n let line = theme.fg(emergency ? \"warning\" : \"muted\", nodeRow(node));\n if (index === selectedIndex) line = theme.bg(\"selectedBg\", padToWidth(line, width));\n lines.push(truncateToWidth(line, width, \"\"));\n }\n while (lines.length < available) lines.push(\"\");\n return lines.slice(0, available);\n};\n\nexport const renderLcmPreviewLines = (\n theme: Theme,\n snapshot: LcmViewSnapshot,\n width: number,\n height: number,\n): string[] => {\n const available = Math.max(1, height);\n const preview = snapshot.preview;\n const complete =\n preview.activeSources > 0 &&\n preview.coveredSources / preview.activeSources >= COVERAGE_COMPLETE_RATIO;\n const ratio =\n complete && preview.sourceBytes > 0\n ? ` \u00B7 assembled text is ${formatPercent((preview.summaryBytes / preview.sourceBytes) * 100)} of the session payload it replaces`\n : \"\";\n const headline = `preview \u00B7 frontier holds ${preview.coveredSources}/${preview.activeSources} sources \u00B7 ${preview.nodes} nodes assemble to ${formatBytes(preview.summaryBytes)} \u00B7 session stored payload ${formatBytes(preview.sourceBytes)}${ratio}`;\n const lines = wrapPlainText(headline, width, 2).map((line) => theme.fg(\"accent\", line));\n const body = wrapPlainText(preview.text, width, Math.max(1, available - lines.length));\n for (const line of body) lines.push(theme.fg(\"dim\", line));\n if (body.length === 0) lines.push(theme.fg(\"dim\", \"(the frontier assembles no text yet)\"));\n while (lines.length < available) lines.push(\"\");\n return lines.slice(0, available);\n};\n\nconst revisionColumns = (\n theme: Theme,\n detail: LcmNodeDetail,\n width: number,\n rows: number,\n): string[] => {\n const current = detail.revisions[detail.revisions.length - 1];\n const previous = detail.revisions[detail.revisions.length - 2];\n if (!current) return [];\n if (!previous) {\n const lines = [theme.fg(\"accent\", `text \u00B7 current revision ${current.revision} \u00B7 ${originLabel(current.modelHash)}`)];\n for (const line of wrapPlainText(current.text, width, Math.max(1, rows - 1))) {\n lines.push(theme.fg(\"dim\", line));\n }\n return lines;\n }\n const label = (revision: { revision: number; modelHash: string }, name: string): string =>\n `${name} revision ${revision.revision} \u00B7 ${originLabel(revision.modelHash)}`;\n if (width < 60) {\n const lines = [theme.fg(\"accent\", label(current, \"current\"))];\n for (const line of wrapPlainText(current.text, width, Math.max(1, Math.floor((rows - 2) / 2)))) {\n lines.push(theme.fg(\"dim\", line));\n }\n lines.push(theme.fg(\"accent\", label(previous, \"previous\")));\n for (const line of wrapPlainText(previous.text, width, Math.max(1, Math.floor((rows - 2) / 2)))) {\n lines.push(theme.fg(\"muted\", line));\n }\n return lines;\n }\n const columnWidth = Math.floor((width - 1) / 2);\n const body = Math.max(1, rows - 1);\n const previousBody = wrapPlainText(previous.text, columnWidth, body);\n const currentBody = wrapPlainText(current.text, columnWidth, body);\n const lines = [\n `${padToWidth(theme.fg(\"accent\", label(previous, \"previous\")), columnWidth)}${theme.fg(\"borderMuted\", \"\u2502\")}${theme.fg(\"accent\", label(current, \"current\"))}`,\n ];\n for (let index = 0; index < body; index++) {\n const left = previousBody[index];\n const right = currentBody[index];\n if (left === undefined && right === undefined) break;\n lines.push(\n `${padToWidth(theme.fg(\"muted\", left ?? \"\"), columnWidth)}${theme.fg(\"borderMuted\", \"\u2502\")}${theme.fg(\"dim\", right ?? \"\")}`,\n );\n }\n return lines;\n};\n\nexport const renderLcmDetailLines = (\n theme: Theme,\n detail: LcmNodeDetail,\n sourceIndex: number,\n entry: LcmSourceEntry | undefined,\n width: number,\n height: number,\n): string[] => {\n const available = Math.max(1, height);\n const node = detail.node;\n const lines: string[] = [\n theme.fg(\n isEmergency(node.modelHash) ? \"warning\" : \"accent\",\n truncateToWidth(\n safeText(\n `${node.kind} \u00B7 ${originLabel(node.modelHash)} \u00B7 depth ${node.depth} \u00B7 ${node.state} \u00B7 ${formatClock(node.createdAt)} \u00B7 ${node.nodeId}`,\n ),\n width,\n \"\",\n ),\n ),\n theme.fg(\n \"dim\",\n truncateToWidth(safeText(`model hash ${node.modelHash}`), width, \"\"),\n ),\n theme.fg(\n \"dim\",\n truncateToWidth(\n safeText(`ancestors ${detail.ancestors.length > 0 ? detail.ancestors.join(\" \u203A \") : \"none\"}`),\n width,\n \"\",\n ),\n ),\n theme.fg(\n \"dim\",\n truncateToWidth(\n safeText(\n `children (${node.children.length}) ${node.children.length > 0 ? node.children.map(shortId).join(\" \") : \"none\"}`,\n ),\n width,\n \"\",\n ),\n ),\n theme.fg(\"accent\", truncateToWidth(`sources (${node.sources.length})`, width, \"\")),\n ];\n const sourceRows = Math.min(node.sources.length, Math.max(1, Math.floor(available / 4)));\n const sourceStart = Math.max(\n 0,\n Math.min(sourceIndex - Math.floor(sourceRows / 2), Math.max(0, node.sources.length - sourceRows)),\n );\n for (let index = sourceStart; index < Math.min(node.sources.length, sourceStart + sourceRows); index++) {\n const source = node.sources[index]!;\n const text = ` ${source.sessionId}/${source.entryId}@${source.revision}`;\n const line =\n index === sourceIndex\n ? theme.bg(\"selectedBg\", padToWidth(theme.fg(\"muted\", text), width))\n : theme.fg(\"muted\", text);\n lines.push(truncateToWidth(line, width, \"\"));\n }\n if (node.sources.length === 0) lines.push(theme.fg(\"dim\", \" (no sources)\"));\n if (entry) {\n lines.push(\n theme.fg(\n \"accent\",\n truncateToWidth(\n safeText(`raw entry \u00B7 ${entry.role} \u00B7 ${formatClock(entry.createdAt)} \u00B7 ${formatBytes(Buffer.byteLength(entry.payloadJson, \"utf8\"))} stored`),\n width,\n \"\",\n ),\n ),\n );\n for (const line of wrapPlainText(entry.content, width, Math.max(1, Math.floor(available / 4)))) {\n lines.push(theme.fg(\"dim\", line));\n }\n }\n const remaining = Math.max(1, available - lines.length);\n for (const line of revisionColumns(theme, detail, width, remaining)) lines.push(line);\n while (lines.length < available) lines.push(\"\");\n return lines.slice(0, available);\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["lines"]
7
+ }
@@ -0,0 +1,53 @@
1
+ // src/compaction/lcm-doctor.ts
2
+ var SEVERITY_ORDER = { ok: 0, info: 1, warn: 2, fail: 3 };
3
+ var worstSeverity = (checks) => checks.reduce(
4
+ (worst, check) => SEVERITY_ORDER[check.severity] > SEVERITY_ORDER[worst] ? check.severity : worst,
5
+ "ok"
6
+ );
7
+ var REPAIR_LABELS = {
8
+ reconcile: "re-read the session file into the ledger",
9
+ readback: "import the live branch into the ledger",
10
+ leases: "release expired maintenance leases",
11
+ jobs: "requeue failed maintenance jobs"
12
+ };
13
+ var megabytes = (bytes) => `${(bytes / 1024 ** 2).toFixed(1)} MB`;
14
+ var percent = (part, whole) => whole <= 0 ? "0%" : `${Math.round(part / whole * 100)}%`;
15
+ var lcmChecks = (diagnostics) => {
16
+ const checks = [];
17
+ const size = `${megabytes(diagnostics.ledgerBytes)} \xB7 ${diagnostics.ledgerEntries} entries`;
18
+ checks.push(
19
+ diagnostics.ledgerState === "degraded" ? { id: "ledger", title: "ledger", severity: "fail", detail: `unusable \xB7 ${diagnostics.ledgerPath}` } : diagnostics.ledgerState !== "healthy" ? { id: "ledger", title: "ledger", severity: "warn", detail: `${diagnostics.ledgerState} \xB7 ${size}` } : { id: "ledger", title: "ledger", severity: "ok", detail: `healthy \xB7 ${size}` }
20
+ );
21
+ if (diagnostics.runtimeError) {
22
+ checks.push({ id: "runtime", title: "runtime", severity: "fail", detail: diagnostics.runtimeError, repair: "readback" });
23
+ }
24
+ checks.push(
25
+ diagnostics.sessionId === void 0 ? { id: "session", title: "session", severity: "info", detail: "no session selected yet" } : diagnostics.sessionFile === void 0 ? { id: "session", title: "session", severity: "info", detail: `${diagnostics.sessionId} \xB7 this session is not persisted to disk` } : diagnostics.sessionFilePresent ? { id: "session", title: "session", severity: "ok", detail: `${diagnostics.sessionId} \xB7 file on disk` } : { id: "session", title: "session", severity: "info", detail: `${diagnostics.sessionId} \xB7 file not written yet; the branch is still in memory` }
26
+ );
27
+ const behind = diagnostics.liveBranchEntries - diagnostics.ledgerSessionEntries;
28
+ checks.push(
29
+ diagnostics.liveBranchEntries === 0 ? { id: "readback", title: "readback", severity: "info", detail: "the live branch is empty" } : behind > 0 ? { id: "readback", title: "readback", severity: "info", detail: `${behind} live entr${behind === 1 ? "y" : "ies"} land at the next turn boundary (${diagnostics.ledgerSessionEntries}/${diagnostics.liveBranchEntries} stored)`, repair: "readback" } : { id: "readback", title: "readback", severity: "ok", detail: `all ${diagnostics.liveBranchEntries} branch entries stored \xB7 ${diagnostics.ledgerSessionEntries} kept for this session` }
30
+ );
31
+ const reconciliation = diagnostics.reconciliation;
32
+ checks.push(
33
+ reconciliation === void 0 ? { id: "reconcile", title: "reconcile", severity: "info", detail: "not run in this session yet" } : reconciliation.errors > 0 ? { id: "reconcile", title: "reconcile", severity: "fail", detail: `${reconciliation.errors} unreadable session file(s)`, repair: "reconcile" } : reconciliation.reasons.length > 0 ? { id: "reconcile", title: "reconcile", severity: "warn", detail: reconciliation.reasons.join("; "), repair: "reconcile" } : reconciliation.absent > 0 ? { id: "reconcile", title: "reconcile", severity: "info", detail: "session file is not on disk yet; nothing to re-read" } : { id: "reconcile", title: "reconcile", severity: "ok", detail: reconciliation.raced > 0 ? `clean after ${reconciliation.raced} raced read(s)` : "clean" }
34
+ );
35
+ const uncovered = diagnostics.activeSources - diagnostics.coveredSources;
36
+ checks.push(
37
+ diagnostics.activeSources === 0 ? { id: "coverage", title: "coverage", severity: "info", detail: "no active sources to cover yet" } : uncovered === 0 ? { id: "coverage", title: "coverage", severity: "ok", detail: `${diagnostics.coveredSources}/${diagnostics.activeSources} sources held by ready nodes` } : diagnostics.pendingJobs + diagnostics.runningJobs > 0 ? { id: "coverage", title: "coverage", severity: "info", detail: `${uncovered} source(s) uncovered \xB7 ${diagnostics.pendingJobs + diagnostics.runningJobs} job(s) already queued` } : uncovered >= diagnostics.backlogThreshold ? { id: "coverage", title: "coverage", severity: "warn", detail: `${uncovered} source(s) past the ${diagnostics.backlogThreshold}-entry backlog with no job queued (${percent(diagnostics.coveredSources, diagnostics.activeSources)} covered)` } : { id: "coverage", title: "coverage", severity: "info", detail: `${uncovered} source(s) uncovered \xB7 below the ${diagnostics.backlogThreshold}-entry backlog that starts a summary` }
38
+ );
39
+ checks.push(
40
+ diagnostics.failedJobs > 0 ? { id: "jobs", title: "jobs", severity: "warn", detail: `${diagnostics.failedJobs} failed \xB7 ${diagnostics.pendingJobs} pending \xB7 ${diagnostics.runningJobs} running`, repair: "jobs" } : diagnostics.expiredLeases > 0 ? { id: "jobs", title: "jobs", severity: "warn", detail: `${diagnostics.expiredLeases} expired lease(s) still marked running`, repair: "leases" } : { id: "jobs", title: "jobs", severity: "ok", detail: `${diagnostics.pendingJobs} pending \xB7 ${diagnostics.runningJobs} running \xB7 ${diagnostics.pendingNodes} node(s) awaiting text` }
41
+ );
42
+ checks.push(
43
+ !diagnostics.modelSummaries ? { id: "model", title: "model", severity: "info", detail: "model summaries are off; compaction serves deterministic excerpts" } : !diagnostics.withinBudget ? { id: "model", title: "model", severity: "warn", detail: `budget exhausted \xB7 ${diagnostics.usedCalls}/${Number.isFinite(diagnostics.budgetCalls) ? diagnostics.budgetCalls : "no limit"} calls today` } : { id: "model", title: "model", severity: "ok", detail: `${diagnostics.summaryModel || "inherit"} \xB7 ${diagnostics.usedCalls} call(s) today` }
44
+ );
45
+ return checks;
46
+ };
47
+
48
+ export {
49
+ worstSeverity,
50
+ REPAIR_LABELS,
51
+ lcmChecks
52
+ };
53
+ //# sourceMappingURL=chunk-SMNOQLKU.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/compaction/lcm-doctor.ts"],
4
+ "sourcesContent": ["export type LcmRepairId = \"reconcile\" | \"readback\" | \"leases\" | \"jobs\";\n\nexport type LcmCheckSeverity = \"ok\" | \"info\" | \"warn\" | \"fail\";\n\nexport interface LcmDiagnostics {\n projectKey: string;\n ledgerPath: string;\n ledgerBytes: number;\n ledgerState: string;\n runtimeError: string | undefined;\n sessionId: string | undefined;\n sessionFile: string | undefined;\n sessionFilePresent: boolean;\n liveBranchEntries: number;\n ledgerSessionEntries: number;\n ledgerEntries: number;\n activeSources: number;\n coveredSources: number;\n backlogThreshold: number;\n pendingNodes: number;\n pendingJobs: number;\n runningJobs: number;\n failedJobs: number;\n expiredLeases: number;\n modelSummaries: boolean;\n summaryModel: string | undefined;\n budgetCalls: number;\n usedCalls: number;\n withinBudget: boolean;\n reconciliation: {\n errors: number;\n raced: number;\n absent: number;\n reasons: readonly string[];\n } | undefined;\n repairs: readonly LcmRepairOutcome[];\n autoRepairs: readonly LcmAutoRepair[];\n}\n\nexport interface LcmAutoRepair {\n fault: string;\n attempts: number;\n nextAt: number;\n detail: string;\n}\n\nexport interface LcmRepairOutcome {\n id: LcmRepairId;\n changed: boolean;\n detail: string;\n at: number;\n}\n\nexport interface LcmCheck {\n id: string;\n title: string;\n severity: LcmCheckSeverity;\n detail: string;\n repair?: LcmRepairId;\n}\n\nconst SEVERITY_ORDER: Record<LcmCheckSeverity, number> = { ok: 0, info: 1, warn: 2, fail: 3 };\n\nexport const worstSeverity = (checks: readonly LcmCheck[]): LcmCheckSeverity =>\n checks.reduce<LcmCheckSeverity>(\n (worst, check) => (SEVERITY_ORDER[check.severity] > SEVERITY_ORDER[worst] ? check.severity : worst),\n \"ok\",\n );\n\nexport const REPAIR_LABELS: Record<LcmRepairId, string> = {\n reconcile: \"re-read the session file into the ledger\",\n readback: \"import the live branch into the ledger\",\n leases: \"release expired maintenance leases\",\n jobs: \"requeue failed maintenance jobs\",\n};\n\nconst megabytes = (bytes: number): string => `${(bytes / 1024 ** 2).toFixed(1)} MB`;\n\nconst percent = (part: number, whole: number): string =>\n whole <= 0 ? \"0%\" : `${Math.round((part / whole) * 100)}%`;\n\n/** Every check names the evidence it read, so a verdict is never a bare adjective. */\nexport const lcmChecks = (diagnostics: LcmDiagnostics): LcmCheck[] => {\n const checks: LcmCheck[] = [];\n\n const size = `${megabytes(diagnostics.ledgerBytes)} \u00B7 ${diagnostics.ledgerEntries} entries`;\n checks.push(\n diagnostics.ledgerState === \"degraded\"\n ? { id: \"ledger\", title: \"ledger\", severity: \"fail\", detail: `unusable \u00B7 ${diagnostics.ledgerPath}` }\n : diagnostics.ledgerState !== \"healthy\"\n ? { id: \"ledger\", title: \"ledger\", severity: \"warn\", detail: `${diagnostics.ledgerState} \u00B7 ${size}` }\n : { id: \"ledger\", title: \"ledger\", severity: \"ok\", detail: `healthy \u00B7 ${size}` },\n );\n\n if (diagnostics.runtimeError) {\n checks.push({ id: \"runtime\", title: \"runtime\", severity: \"fail\", detail: diagnostics.runtimeError, repair: \"readback\" });\n }\n\n checks.push(\n diagnostics.sessionId === undefined\n ? { id: \"session\", title: \"session\", severity: \"info\", detail: \"no session selected yet\" }\n : diagnostics.sessionFile === undefined\n ? { id: \"session\", title: \"session\", severity: \"info\", detail: `${diagnostics.sessionId} \u00B7 this session is not persisted to disk` }\n : diagnostics.sessionFilePresent\n ? { id: \"session\", title: \"session\", severity: \"ok\", detail: `${diagnostics.sessionId} \u00B7 file on disk` }\n : { id: \"session\", title: \"session\", severity: \"info\", detail: `${diagnostics.sessionId} \u00B7 file not written yet; the branch is still in memory` },\n );\n\n const behind = diagnostics.liveBranchEntries - diagnostics.ledgerSessionEntries;\n checks.push(\n diagnostics.liveBranchEntries === 0\n ? { id: \"readback\", title: \"readback\", severity: \"info\", detail: \"the live branch is empty\" }\n : behind > 0\n ? { id: \"readback\", title: \"readback\", severity: \"info\", detail: `${behind} live entr${behind === 1 ? \"y\" : \"ies\"} land at the next turn boundary (${diagnostics.ledgerSessionEntries}/${diagnostics.liveBranchEntries} stored)`, repair: \"readback\" }\n : { id: \"readback\", title: \"readback\", severity: \"ok\", detail: `all ${diagnostics.liveBranchEntries} branch entries stored \u00B7 ${diagnostics.ledgerSessionEntries} kept for this session` },\n );\n\n const reconciliation = diagnostics.reconciliation;\n checks.push(\n reconciliation === undefined\n ? { id: \"reconcile\", title: \"reconcile\", severity: \"info\", detail: \"not run in this session yet\" }\n : reconciliation.errors > 0\n ? { id: \"reconcile\", title: \"reconcile\", severity: \"fail\", detail: `${reconciliation.errors} unreadable session file(s)`, repair: \"reconcile\" }\n : reconciliation.reasons.length > 0\n ? { id: \"reconcile\", title: \"reconcile\", severity: \"warn\", detail: reconciliation.reasons.join(\"; \"), repair: \"reconcile\" }\n : reconciliation.absent > 0\n ? { id: \"reconcile\", title: \"reconcile\", severity: \"info\", detail: \"session file is not on disk yet; nothing to re-read\" }\n : { id: \"reconcile\", title: \"reconcile\", severity: \"ok\", detail: reconciliation.raced > 0 ? `clean after ${reconciliation.raced} raced read(s)` : \"clean\" },\n );\n\n const uncovered = diagnostics.activeSources - diagnostics.coveredSources;\n checks.push(\n diagnostics.activeSources === 0\n ? { id: \"coverage\", title: \"coverage\", severity: \"info\", detail: \"no active sources to cover yet\" }\n : uncovered === 0\n ? { id: \"coverage\", title: \"coverage\", severity: \"ok\", detail: `${diagnostics.coveredSources}/${diagnostics.activeSources} sources held by ready nodes` }\n : diagnostics.pendingJobs + diagnostics.runningJobs > 0\n ? { id: \"coverage\", title: \"coverage\", severity: \"info\", detail: `${uncovered} source(s) uncovered \u00B7 ${diagnostics.pendingJobs + diagnostics.runningJobs} job(s) already queued` }\n : uncovered >= diagnostics.backlogThreshold\n ? { id: \"coverage\", title: \"coverage\", severity: \"warn\", detail: `${uncovered} source(s) past the ${diagnostics.backlogThreshold}-entry backlog with no job queued (${percent(diagnostics.coveredSources, diagnostics.activeSources)} covered)` }\n : { id: \"coverage\", title: \"coverage\", severity: \"info\", detail: `${uncovered} source(s) uncovered \u00B7 below the ${diagnostics.backlogThreshold}-entry backlog that starts a summary` },\n );\n\n checks.push(\n diagnostics.failedJobs > 0\n ? { id: \"jobs\", title: \"jobs\", severity: \"warn\", detail: `${diagnostics.failedJobs} failed \u00B7 ${diagnostics.pendingJobs} pending \u00B7 ${diagnostics.runningJobs} running`, repair: \"jobs\" }\n : diagnostics.expiredLeases > 0\n ? { id: \"jobs\", title: \"jobs\", severity: \"warn\", detail: `${diagnostics.expiredLeases} expired lease(s) still marked running`, repair: \"leases\" }\n : { id: \"jobs\", title: \"jobs\", severity: \"ok\", detail: `${diagnostics.pendingJobs} pending \u00B7 ${diagnostics.runningJobs} running \u00B7 ${diagnostics.pendingNodes} node(s) awaiting text` },\n );\n\n checks.push(\n !diagnostics.modelSummaries\n ? { id: \"model\", title: \"model\", severity: \"info\", detail: \"model summaries are off; compaction serves deterministic excerpts\" }\n : !diagnostics.withinBudget\n ? { id: \"model\", title: \"model\", severity: \"warn\", detail: `budget exhausted \u00B7 ${diagnostics.usedCalls}/${Number.isFinite(diagnostics.budgetCalls) ? diagnostics.budgetCalls : \"no limit\"} calls today` }\n : { id: \"model\", title: \"model\", severity: \"ok\", detail: `${diagnostics.summaryModel || \"inherit\"} \u00B7 ${diagnostics.usedCalls} call(s) today` },\n );\n\n return checks;\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  peerLabelPrefix,
23
23
  writeHandoffSession
24
- } from "./chunk-MGTKAZYH.js";
24
+ } from "./chunk-C5JMLDIM.js";
25
25
  import {
26
26
  DEFAULT_FABRIC_CONFIG,
27
27
  MAX_AGENT_TIMEOUT_MS,
@@ -6635,4 +6635,4 @@ export {
6635
6635
  agentParticipantRecords,
6636
6636
  actorParticipantRecord
6637
6637
  };
6638
- //# sourceMappingURL=chunk-45XM53NX.js.map
6638
+ //# sourceMappingURL=chunk-VYB7ROEH.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  hashLcmPayload,
3
3
  renderLcmSourceAddresses
4
- } from "./chunk-GZQVYRM3.js";
4
+ } from "./chunk-NX2G7BRO.js";
5
5
  import {
6
6
  LCM_RECOVERY_POINTER
7
7
  } from "./chunk-E4HNDTLM.js";
@@ -180,4 +180,4 @@ export {
180
180
  emergencyReduce,
181
181
  LcmModelAdapter
182
182
  };
183
- //# sourceMappingURL=chunk-QHBWGAYF.js.map
183
+ //# sourceMappingURL=chunk-WCV26AOT.js.map
@@ -1666,13 +1666,11 @@ var registerCompactionHook = (omp, options) => {
1666
1666
  const branchEntries = event.branchEntries ?? [];
1667
1667
  const sessionManager = context?.sessionManager;
1668
1668
  const sessionId = sessionManager?.getSessionId?.() ?? "";
1669
- const branch = sessionManager?.getLeafId?.() ?? null;
1670
1669
  let output;
1671
1670
  try {
1672
1671
  output = options.lcm.compact({
1673
1672
  branchEntries,
1674
1673
  sessionId,
1675
- branch,
1676
1674
  firstKeptEntryId: event.preparation.firstKeptEntryId,
1677
1675
  tokensBefore: event.preparation.tokensBefore,
1678
1676
  ...event.customInstructions === void 0 ? {} : { customInstructions: event.customInstructions }
@@ -1683,7 +1681,7 @@ var registerCompactionHook = (omp, options) => {
1683
1681
  if (context?.hasUI) context.ui.notify(`LCM compaction failed: ${message}`, "error");
1684
1682
  return { cancel: true };
1685
1683
  }
1686
- if (!output || typeof output.summary !== "string" || output.summary.length === 0 || output.source !== "ready-frontier" && output.source !== "emergency" || output.tokensBefore !== event.preparation.tokensBefore || output.branch !== branch || typeof output.firstKeptEntryId !== "string" || branchEntries.length === 0 || output.firstKeptEntryId.length > 0 && !branchEntries.some((entry) => entry.id === output.firstKeptEntryId)) {
1684
+ if (!output || typeof output.summary !== "string" || output.summary.length === 0 || output.source !== "ready-frontier" && output.source !== "emergency" || output.tokensBefore !== event.preparation.tokensBefore || typeof output.firstKeptEntryId !== "string" || branchEntries.length === 0 || output.firstKeptEntryId.length > 0 && !branchEntries.some((entry) => entry.id === output.firstKeptEntryId)) {
1687
1685
  return { cancel: true };
1688
1686
  }
1689
1687
  return {
@@ -1694,7 +1692,6 @@ var registerCompactionHook = (omp, options) => {
1694
1692
  details: {
1695
1693
  compactor: "lcm",
1696
1694
  source: output.source,
1697
- branch,
1698
1695
  ...output.details && typeof output.details === "object" ? output.details : {}
1699
1696
  }
1700
1697
  }
@@ -1724,4 +1721,4 @@ export {
1724
1721
  compileFabricSummary,
1725
1722
  registerCompactionHook
1726
1723
  };
1727
- //# sourceMappingURL=chunk-D2ET33NE.js.map
1724
+ //# sourceMappingURL=chunk-WMB6QCWX.js.map