omp-fabric 1.13.1 → 1.15.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 (48) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/chunks/{chunk-R6QAXATY.js → chunk-3A72BUEE.js} +2 -2
  3. package/dist/chunks/{chunk-IIX4W4N4.js → chunk-45XM53NX.js} +3 -3
  4. package/dist/chunks/{chunk-U5QWDSIL.js → chunk-7AEYAPTZ.js} +3 -3
  5. package/dist/chunks/{chunk-KJKO6KGW.js → chunk-D2ET33NE.js} +13 -403
  6. package/dist/chunks/chunk-D2ET33NE.js.map +7 -0
  7. package/dist/chunks/chunk-E4HNDTLM.js +469 -0
  8. package/dist/chunks/chunk-E4HNDTLM.js.map +7 -0
  9. package/dist/chunks/{chunk-ANAXFBUE.js → chunk-GIBZGL4G.js} +3 -1
  10. package/dist/chunks/chunk-GIBZGL4G.js.map +7 -0
  11. package/dist/chunks/{chunk-DTXCVXXF.js → chunk-MGTKAZYH.js} +7 -5
  12. package/dist/chunks/{chunk-DTXCVXXF.js.map → chunk-MGTKAZYH.js.map} +1 -1
  13. package/dist/chunks/{chunk-2NTQAH6X.js → chunk-QHBWGAYF.js} +11 -6
  14. package/dist/chunks/chunk-QHBWGAYF.js.map +7 -0
  15. package/dist/compaction/hook.d.ts.map +1 -1
  16. package/dist/compaction/hook.js +2 -2
  17. package/dist/compaction/lcm-maintenance.d.ts +19 -2
  18. package/dist/compaction/lcm-maintenance.d.ts.map +1 -1
  19. package/dist/compaction/lcm-model.d.ts +1 -1
  20. package/dist/compaction/lcm-model.d.ts.map +1 -1
  21. package/dist/compaction/lcm-runtime.d.ts +2 -1
  22. package/dist/compaction/lcm-runtime.d.ts.map +1 -1
  23. package/dist/compaction/lcm-runtime.js +212 -81
  24. package/dist/compaction/lcm-runtime.js.map +2 -2
  25. package/dist/config.d.ts +1 -0
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/fabric-runtime-state.js +12 -11
  28. package/dist/fabric-runtime-state.js.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +7 -6
  31. package/dist/index.js.map +2 -2
  32. package/dist/residency/host.js +7 -7
  33. package/dist/residency/omp-entry.js +7 -7
  34. package/dist/storage/lcm-ledger.d.ts.map +1 -1
  35. package/dist/ui/settings.d.ts.map +1 -1
  36. package/dist/ui/settings.js +10 -1
  37. package/dist/ui/settings.js.map +2 -2
  38. package/docs/compaction.md +3 -2
  39. package/docs/configuration.md +1 -1
  40. package/package.json +1 -1
  41. package/dist/chunks/chunk-2NTQAH6X.js.map +0 -7
  42. package/dist/chunks/chunk-ANAXFBUE.js.map +0 -7
  43. package/dist/chunks/chunk-KJKO6KGW.js.map +0 -7
  44. package/dist/chunks/chunk-NIWOSO22.js +0 -74
  45. package/dist/chunks/chunk-NIWOSO22.js.map +0 -7
  46. /package/dist/chunks/{chunk-R6QAXATY.js.map → chunk-3A72BUEE.js.map} +0 -0
  47. /package/dist/chunks/{chunk-IIX4W4N4.js.map → chunk-45XM53NX.js.map} +0 -0
  48. /package/dist/chunks/{chunk-U5QWDSIL.js.map → chunk-7AEYAPTZ.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.15.0
4
+
5
+ ### Added
6
+
7
+ - Support for compaction custom instructions and preserve items in LCM: `renderAddressedFrontier` and `emergencyReduce` now render a `[Compaction Request]` block when instructions or preserve entries are supplied via `compact.request`, matching the deterministic compactor's policy contract.
8
+ - SQLite indexes on `summary_nodes(project_key, created_at, node_id)`, `maintenance_jobs(project_key, status)`, and `summary_edges(child_id, parent_id)` to eliminate unindexed table scans during node listing, job scheduling, and ancestor edge traversal.
9
+ - Warning logging and UI notifications on compaction hook errors before cancellation, preventing silent context blowups.
10
+
11
+ ### Fixed
12
+
13
+ - Transaction batching during session readback and compaction: `readback()` and `compact()` now wrap raw entry persistence in an explicit ledger transaction, collapsing hundreds of sequential synchronous SQLite fsync operations into a single atomic commit.
14
+ - Dirty flag enforcement in `LcmRuntime`: `syncAndSchedule()` now checks `this.dirty` and active session binding to skip redundant full-branch readbacks on idle turns.
15
+ - Memory allocation in `clipUtf8End`: trailing text slices are now bounded to `maxBytes * 2` before codepoint conversion, eliminating multi-megabyte array allocations when producing emergency excerpts.
16
+
17
+ ## 1.14.0
18
+
19
+ ### Added
20
+
21
+ - `compaction.lcmMaintenanceConcurrency` (default 3, clamped to 1-8, editable under **Compaction → Maintenance concurrency**) is how many summary jobs one maintenance run holds a lease on at once. Maintenance ran one job at a time before this, so the default multiplies the spend rate of a run by three over the same wall time; `lcmMaxDailyModelCalls`, `lcmMaxSessionModelCalls` and `lcmMaxDailyModelSeconds` stay the ceiling. Measured against an 8-job session backlog at 15 seconds per call, a run cleared 4 jobs per turn at 1 and all 8 in one turn at 2 and at 3.
22
+
23
+ ### Fixed
24
+
25
+ - A lapsed lease no longer drops out of the concurrency count and the budget reservation while its call is still running. The claim counted a lease as live only while `leaseUntil` was in the future, and the sweep returned the slot 60 seconds later, so a job in that gap was reserved for by nothing. Measured with a fake clock at cap 3 against a 4-call budget: three claims, a correct refusal of the fourth, then 31 seconds of clock advance admitted two more claims for five concurrent calls at a cap of 3, and the budget check reported room with three unrecorded calls outstanding. A claim, a steal, and the claimable-job query now all treat a lease as held until 60 seconds after it lapses, the moment the sweep hands the slot back.
26
+ - A claim refused for contention no longer spends one of a job's three attempts. `dispatch` sized its worker pool once while `claim` re-read the live setting, so lowering the concurrency mid-run left surplus workers refused with `project lease held`, and the catch could not tell a refusal from a model failure. Measured through the runtime with 8 jobs at concurrency 3 lowered to 1, 4 of the 8 carried an attempt they never earned; three such runs retire a job to `failed`, where the sweep and the scheduler both skip it and its range falls back to the emergency excerpt for the life of the project. A refusal now carries its reason, a surplus worker retires before it takes a job, and a refused claim leaves the job pending with its attempt count and the runtime's health untouched. The single-job cap of 1.13.1 and earlier threw the same refusal at a second process working the same project, so a ledger written before this release can hold jobs retired that way. One maintenance pass returns such a job to pending with its attempts cleared and marks it, so no later pass can take it a second time.
27
+ - Recovering a job an older ledger retired for contention no longer masks a real fault. The recovery matched any of the five refusal reasons under any `Word: ` prefix, so it also took a job retired on a genuine `budget exhausted` failure, the exact text completion throws when a real cap is crossed, and one retired on a provider `AbortError: job not eligible`. Once matched a job never stayed retired: five rounds of three real failures produced five recoveries with attempts cleared each time, so a permanently broken job cycled and its fault never surfaced. This release cannot write a refusal into a job's error at all, since `run` rethrows a fence, the dispatcher filters refusals before it records a failure, and `fail` is lease fenced. The rule is what it always was, a migration for ledgers written before 1.13.2, and it now reads as one: it takes only the two exact strings the older code wrote for a contention refusal, `LcmRejection: job lease held` and `LcmRejection: project lease held`, it consumes that error when it acts, and it marks the job so a second recovery needs a second ledger.
28
+ - A hard-threshold compaction is no longer cancelled by a lease its own last-resort claim could not take. `claimEmergency` inherited the 60-second reclaim grace from the ordinary claim, so a lease left by a dead worker refused it for 90 seconds: measured, refused at 30 seconds and one millisecond, admitted only at 90 seconds. The compaction hook turns a throw from `compact` into a cancellation with no summary and no notification, so that window silently cancelled compactions for three times as long. The emergency claim ignores the concurrency cap and the budget already, and the reclaim grace exists to hold a slot for the reservation those two read, so it now takes a lapsed lease at 30 seconds. A refusal it can still meet, a lease a live worker holds, returns the deterministic excerpt it had already built and leaves the node to the lease holder.
29
+ - The lease sweep no longer carries a window a caller can drop. Its grace was a parameter defaulting to `0`, and a sweep at grace `0` returns a running job to pending while its call is live, which drops it out of the live-lease query and reopens the reservation hole. The grace is now the constant every other member of that predicate reads, with no way to pass another.
30
+ - A completion fenced by a stalled lease no longer retires the job. Every lease of a fan-out batch lapses together, so one stall charged an attempt to all of them at once. The work may well have succeeded and only the bookkeeping was refused, so a fence is now reported as a fence: nothing is recorded, the node is left for whoever holds the lease next, and the attempt count stays where it was.
31
+ - The token, cost, and wall-time budgets reserve for a call before it is made. Those three dimensions were tested against recorded usage with nothing reserved, so with 350 input tokens of headroom at 100 tokens per call, 9 jobs were admitted; every call past the third was paid for and refused at completion, which left the recorded total below the cap and admitted the next job on the same reasoning. A claim now reserves the average consumption per call recorded today for itself and for every live sibling lease. Two cases still pay for a call that will be refused: the first calls of a UTC day, where nothing is recorded and the average reserves zero, and a call heavier than today's recorded mean, which the average understates. In both the call is made, completion refuses its result against the exact cap, and the range falls back to the deterministic excerpt with no usage recorded.
32
+
33
+ ### Changed
34
+
35
+ - The documented concurrency bound is the bound the code holds. `docs/compaction.md` carried both "One project model job runs at a time" and the new concurrency sentence, and it generalized the call reservation to every budget dimension. It now states what is enforced: leases at once never pass the setting, calls and per-dimension averages are reserved per live lease, a lease is reserved for until 60 seconds after it lapses, and a process stalled through the full 90 seconds of lease and reclaim window can push concurrent calls past the cap with its own result refused. It also names the two cases where a reserved-average claim pays for a refused call, the once-per-job bound on recovering an older ledger's contention retirements, and the 30-second window the compaction's own last-resort claim uses.
36
+
3
37
  ## 1.13.1
4
38
 
5
39
  ### Fixed
@@ -12,7 +12,7 @@ import {
12
12
  buildThinkingDigest,
13
13
  snapshotHandoffSession,
14
14
  thinkingTransferPolicy
15
- } from "./chunk-DTXCVXXF.js";
15
+ } from "./chunk-MGTKAZYH.js";
16
16
  import {
17
17
  isFabricThinking
18
18
  } from "./chunk-SKYNDRW5.js";
@@ -2803,4 +2803,4 @@ export {
2803
2803
  resolveFabricIdentity,
2804
2804
  MainAgentController
2805
2805
  };
2806
- //# sourceMappingURL=chunk-R6QAXATY.js.map
2806
+ //# sourceMappingURL=chunk-3A72BUEE.js.map
@@ -21,13 +21,13 @@ import {
21
21
  import {
22
22
  peerLabelPrefix,
23
23
  writeHandoffSession
24
- } from "./chunk-DTXCVXXF.js";
24
+ } from "./chunk-MGTKAZYH.js";
25
25
  import {
26
26
  DEFAULT_FABRIC_CONFIG,
27
27
  MAX_AGENT_TIMEOUT_MS,
28
28
  MIN_AGENT_TIMEOUT_MS,
29
29
  resolveAgentDir
30
- } from "./chunk-ANAXFBUE.js";
30
+ } from "./chunk-GIBZGL4G.js";
31
31
  import {
32
32
  FABRIC_ACTOR_HOST_EVENTS,
33
33
  readJsonlPage
@@ -6635,4 +6635,4 @@ export {
6635
6635
  agentParticipantRecords,
6636
6636
  actorParticipantRecord
6637
6637
  };
6638
- //# sourceMappingURL=chunk-IIX4W4N4.js.map
6638
+ //# sourceMappingURL=chunk-45XM53NX.js.map
@@ -10,11 +10,11 @@ import {
10
10
  lifecycleSourceIdentity,
11
11
  loadModelUsage,
12
12
  useBudgetLedger
13
- } from "./chunk-IIX4W4N4.js";
13
+ } from "./chunk-45XM53NX.js";
14
14
  import {
15
15
  normalizeModelAliases,
16
16
  resolveAvailableOmpModel
17
- } from "./chunk-ANAXFBUE.js";
17
+ } from "./chunk-GIBZGL4G.js";
18
18
  import {
19
19
  parseFabricOwnedModelGuidance,
20
20
  resolveFabricModelGuidance
@@ -685,4 +685,4 @@ if (isMain) {
685
685
  export {
686
686
  runResidentHostFromConfigPath
687
687
  };
688
- //# sourceMappingURL=chunk-U5QWDSIL.js.map
688
+ //# sourceMappingURL=chunk-7AEYAPTZ.js.map
@@ -2,11 +2,11 @@ import {
2
2
  fabricExecTitleHint
3
3
  } from "./chunk-3J7BKUNG.js";
4
4
  import {
5
+ decodeCompactionInstructions,
5
6
  renderSummary
6
- } from "./chunk-NIWOSO22.js";
7
+ } from "./chunk-E4HNDTLM.js";
7
8
  import {
8
9
  MAX_SUMMARY_BYTES,
9
- canonicalizeText,
10
10
  clipUtf8,
11
11
  omissionLine,
12
12
  sampleAddressed,
@@ -133,394 +133,6 @@ var runEnrichers = (enrichers, events, sections) => {
133
133
  }
134
134
  };
135
135
 
136
- // src/compaction/instructions.ts
137
- var FABRIC_COMPACTION_REQUEST_PREFIX = "__omp_fabric_compact_request_v1__:";
138
- var MAX_COMPACTION_INSTRUCTIONS_CHARS = 8 * 1024;
139
- var MAX_COMPACTION_INSTRUCTIONS_BYTES = 8 * 1024;
140
- var MAX_PRESERVE_ITEMS = 16;
141
- var MAX_PRESERVE_ITEM_CHARS = 2 * 1024;
142
- var MAX_PRESERVE_ITEM_BYTES = 2 * 1024;
143
- var MAX_TYPED_COMPACTION_SOURCE_BYTES = 16 * 1024;
144
- var rejection = (code, message, sourceBytes) => ({
145
- ok: false,
146
- requestLines: [],
147
- error: { code, message, sourceBytes }
148
- });
149
- var isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
150
- var hasPairedSurrogates = (value) => {
151
- for (let index = 0; index < value.length; index++) {
152
- const unit = value.charCodeAt(index);
153
- if (unit >= 55296 && unit <= 56319) {
154
- const next = value.charCodeAt(index + 1);
155
- if (!(next >= 56320 && next <= 57343)) return false;
156
- index += 1;
157
- } else if (unit >= 56320 && unit <= 57343) {
158
- return false;
159
- }
160
- }
161
- return true;
162
- };
163
- var compactionRequestBoundsError = (request) => {
164
- if (request.instructions !== void 0) {
165
- if (!hasPairedSurrogates(request.instructions)) {
166
- return {
167
- code: "invalid-unicode",
168
- message: "typed compaction instructions contain an unpaired UTF-16 surrogate"
169
- };
170
- }
171
- if (request.instructions.length > MAX_COMPACTION_INSTRUCTIONS_CHARS || utf8Bytes(request.instructions) > MAX_COMPACTION_INSTRUCTIONS_BYTES) {
172
- return {
173
- code: "instructions-too-large",
174
- message: `typed compaction instructions exceed ${MAX_COMPACTION_INSTRUCTIONS_CHARS} characters or ${MAX_COMPACTION_INSTRUCTIONS_BYTES} UTF-8 bytes`
175
- };
176
- }
177
- }
178
- if (request.preserve !== void 0) {
179
- if (request.preserve.length > MAX_PRESERVE_ITEMS) {
180
- return {
181
- code: "preserve-too-many",
182
- message: `typed compaction preserve exceeds ${MAX_PRESERVE_ITEMS} items`
183
- };
184
- }
185
- for (const item of request.preserve) {
186
- if (!hasPairedSurrogates(item)) {
187
- return {
188
- code: "invalid-unicode",
189
- message: "typed compaction preserve item contains an unpaired UTF-16 surrogate"
190
- };
191
- }
192
- if (item.length > MAX_PRESERVE_ITEM_CHARS || utf8Bytes(item) > MAX_PRESERVE_ITEM_BYTES) {
193
- return {
194
- code: "preserve-item-too-large",
195
- message: `typed compaction preserve item exceeds ${MAX_PRESERVE_ITEM_CHARS} characters or ${MAX_PRESERVE_ITEM_BYTES} UTF-8 bytes`
196
- };
197
- }
198
- }
199
- }
200
- return void 0;
201
- };
202
- var encodeCompactionRequest = (request) => {
203
- const boundsError = compactionRequestBoundsError(request);
204
- if (boundsError) throw new Error(boundsError.message);
205
- const payload = {
206
- version: 1,
207
- ...request.instructions !== void 0 ? { instructions: request.instructions } : {},
208
- ...request.preserve !== void 0 ? { preserve: request.preserve } : {}
209
- };
210
- const encoded = `${FABRIC_COMPACTION_REQUEST_PREFIX}${JSON.stringify(payload)}`;
211
- if (utf8Bytes(encoded) > MAX_TYPED_COMPACTION_SOURCE_BYTES) {
212
- throw new Error(`typed compaction request exceeds ${MAX_TYPED_COMPACTION_SOURCE_BYTES} encoded UTF-8 bytes`);
213
- }
214
- return encoded;
215
- };
216
- var plainInstructions = (source) => {
217
- const canonical = canonicalizeText(source);
218
- return {
219
- ok: true,
220
- requestLines: canonical.text ? [canonical.text] : [],
221
- policy: {
222
- mode: "plain",
223
- canonicalized: canonical.text !== source,
224
- sourceBytes: canonical.sourceBytes,
225
- truncated: canonical.truncated,
226
- preserveCount: 0,
227
- omittedPreserveCount: 0
228
- }
229
- };
230
- };
231
- var StrictJsonError = class extends Error {
232
- constructor(code, message) {
233
- super(message);
234
- this.code = code;
235
- }
236
- };
237
- var MAX_TYPED_JSON_DEPTH = 32;
238
- var MAX_TYPED_JSON_NODES = 4096;
239
- var StrictJsonParser = class {
240
- constructor(source) {
241
- this.source = source;
242
- }
243
- index = 0;
244
- nodes = 0;
245
- parse() {
246
- this.skipWhitespace();
247
- const value = this.parseValue(0);
248
- this.skipWhitespace();
249
- if (this.index !== this.source.length) this.malformed();
250
- return value;
251
- }
252
- parseValue(depth) {
253
- if (depth > MAX_TYPED_JSON_DEPTH) {
254
- throw new StrictJsonError("structure-too-complex", "typed compaction JSON exceeds the structural depth limit");
255
- }
256
- this.nodes += 1;
257
- if (this.nodes > MAX_TYPED_JSON_NODES) {
258
- throw new StrictJsonError("structure-too-complex", "typed compaction JSON exceeds the structural node limit");
259
- }
260
- const character = this.source[this.index];
261
- if (character === "{") return this.parseObject(depth + 1);
262
- if (character === "[") return this.parseArray(depth + 1);
263
- if (character === '"') return this.parseString();
264
- if (character === "t") return this.parseLiteral("true", true);
265
- if (character === "f") return this.parseLiteral("false", false);
266
- if (character === "n") return this.parseLiteral("null", null);
267
- if (character === "-" || character !== void 0 && character >= "0" && character <= "9") {
268
- return this.parseNumber();
269
- }
270
- return this.malformed();
271
- }
272
- parseObject(depth) {
273
- this.index += 1;
274
- this.skipWhitespace();
275
- const output = /* @__PURE__ */ Object.create(null);
276
- const keys = /* @__PURE__ */ new Set();
277
- if (this.source[this.index] === "}") {
278
- this.index += 1;
279
- return output;
280
- }
281
- while (this.index < this.source.length) {
282
- if (this.source[this.index] !== '"') this.malformed();
283
- const key = this.parseString();
284
- if (keys.has(key)) {
285
- throw new StrictJsonError("duplicate-field", `typed compaction request contains duplicate field ${JSON.stringify(key)}`);
286
- }
287
- keys.add(key);
288
- this.skipWhitespace();
289
- if (this.source[this.index] !== ":") this.malformed();
290
- this.index += 1;
291
- this.skipWhitespace();
292
- output[key] = this.parseValue(depth);
293
- this.skipWhitespace();
294
- const delimiter = this.source[this.index];
295
- if (delimiter === "}") {
296
- this.index += 1;
297
- return output;
298
- }
299
- if (delimiter !== ",") this.malformed();
300
- this.index += 1;
301
- this.skipWhitespace();
302
- }
303
- return this.malformed();
304
- }
305
- parseArray(depth) {
306
- this.index += 1;
307
- this.skipWhitespace();
308
- const output = [];
309
- if (this.source[this.index] === "]") {
310
- this.index += 1;
311
- return output;
312
- }
313
- while (this.index < this.source.length) {
314
- output.push(this.parseValue(depth));
315
- this.skipWhitespace();
316
- const delimiter = this.source[this.index];
317
- if (delimiter === "]") {
318
- this.index += 1;
319
- return output;
320
- }
321
- if (delimiter !== ",") this.malformed();
322
- this.index += 1;
323
- this.skipWhitespace();
324
- }
325
- return this.malformed();
326
- }
327
- parseString() {
328
- this.index += 1;
329
- let output = "";
330
- while (this.index < this.source.length) {
331
- const unit = this.source.charCodeAt(this.index);
332
- if (unit === 34) {
333
- this.index += 1;
334
- if (!hasPairedSurrogates(output)) {
335
- throw new StrictJsonError("invalid-unicode", "typed compaction JSON string contains an unpaired UTF-16 surrogate");
336
- }
337
- return output;
338
- }
339
- if (unit <= 31) this.malformed();
340
- if (unit !== 92) {
341
- output += this.source[this.index];
342
- this.index += 1;
343
- continue;
344
- }
345
- this.index += 1;
346
- const escape = this.source[this.index];
347
- this.index += 1;
348
- if (escape === '"' || escape === "\\" || escape === "/") output += escape;
349
- else if (escape === "b") output += "\b";
350
- else if (escape === "f") output += "\f";
351
- else if (escape === "n") output += "\n";
352
- else if (escape === "r") output += "\r";
353
- else if (escape === "t") output += " ";
354
- else if (escape === "u") {
355
- let value = 0;
356
- for (let offset = 0; offset < 4; offset++) {
357
- const digit = this.hexValue(this.source.charCodeAt(this.index + offset));
358
- if (digit < 0) this.malformed();
359
- value = value * 16 + digit;
360
- }
361
- this.index += 4;
362
- output += String.fromCharCode(value);
363
- } else {
364
- this.malformed();
365
- }
366
- }
367
- return this.malformed();
368
- }
369
- parseNumber() {
370
- const start = this.index;
371
- if (this.source[this.index] === "-") this.index += 1;
372
- if (this.source[this.index] === "0") {
373
- this.index += 1;
374
- if (this.isDigit(this.source.charCodeAt(this.index))) this.malformed();
375
- } else {
376
- if (!this.isDigitOneToNine(this.source.charCodeAt(this.index))) this.malformed();
377
- while (this.isDigit(this.source.charCodeAt(this.index))) this.index += 1;
378
- }
379
- if (this.source[this.index] === ".") {
380
- this.index += 1;
381
- if (!this.isDigit(this.source.charCodeAt(this.index))) this.malformed();
382
- while (this.isDigit(this.source.charCodeAt(this.index))) this.index += 1;
383
- }
384
- const exponent = this.source[this.index];
385
- if (exponent === "e" || exponent === "E") {
386
- this.index += 1;
387
- const sign = this.source[this.index];
388
- if (sign === "+" || sign === "-") this.index += 1;
389
- if (!this.isDigit(this.source.charCodeAt(this.index))) this.malformed();
390
- while (this.isDigit(this.source.charCodeAt(this.index))) this.index += 1;
391
- }
392
- const value = Number(this.source.slice(start, this.index));
393
- if (!Number.isFinite(value)) this.malformed();
394
- return value;
395
- }
396
- parseLiteral(literal, value) {
397
- if (!this.source.startsWith(literal, this.index)) this.malformed();
398
- this.index += literal.length;
399
- return value;
400
- }
401
- skipWhitespace() {
402
- while (this.index < this.source.length) {
403
- const unit = this.source.charCodeAt(this.index);
404
- if (unit !== 32 && unit !== 9 && unit !== 10 && unit !== 13) return;
405
- this.index += 1;
406
- }
407
- }
408
- hexValue(unit) {
409
- if (unit >= 48 && unit <= 57) return unit - 48;
410
- if (unit >= 65 && unit <= 70) return unit - 65 + 10;
411
- if (unit >= 97 && unit <= 102) return unit - 97 + 10;
412
- return -1;
413
- }
414
- isDigit(unit) {
415
- return unit >= 48 && unit <= 57;
416
- }
417
- isDigitOneToNine(unit) {
418
- return unit >= 49 && unit <= 57;
419
- }
420
- malformed() {
421
- throw new StrictJsonError("malformed-json", "typed compaction request contains malformed JSON");
422
- }
423
- };
424
- var decodeCompactionInstructions = (source) => {
425
- if (source === void 0 || source === "") {
426
- return {
427
- ok: true,
428
- requestLines: [],
429
- policy: {
430
- mode: "none",
431
- canonicalized: false,
432
- sourceBytes: 0,
433
- truncated: false,
434
- preserveCount: 0,
435
- omittedPreserveCount: 0
436
- }
437
- };
438
- }
439
- if (!source.startsWith(FABRIC_COMPACTION_REQUEST_PREFIX)) return plainInstructions(source);
440
- const encodedSourceBytes = utf8Bytes(source);
441
- if (encodedSourceBytes > MAX_TYPED_COMPACTION_SOURCE_BYTES) {
442
- return rejection(
443
- "encoded-source-too-large",
444
- `typed compaction request exceeds ${MAX_TYPED_COMPACTION_SOURCE_BYTES} encoded UTF-8 bytes`,
445
- encodedSourceBytes
446
- );
447
- }
448
- let parsed;
449
- try {
450
- parsed = new StrictJsonParser(source.slice(FABRIC_COMPACTION_REQUEST_PREFIX.length)).parse();
451
- } catch (error) {
452
- if (error instanceof StrictJsonError) {
453
- return rejection(error.code, error.message, encodedSourceBytes);
454
- }
455
- return rejection("malformed-json", "typed compaction request contains malformed JSON", encodedSourceBytes);
456
- }
457
- if (!isRecord(parsed)) {
458
- return rejection("invalid-object", "typed compaction request must be a JSON object", encodedSourceBytes);
459
- }
460
- const keys = Object.keys(parsed);
461
- const unknownField = keys.find((key) => key !== "version" && key !== "instructions" && key !== "preserve");
462
- if (unknownField !== void 0) {
463
- return rejection("unknown-field", `typed compaction request contains unknown field ${JSON.stringify(unknownField)}`, encodedSourceBytes);
464
- }
465
- if (parsed.version !== 1) {
466
- return rejection("unsupported-version", "typed compaction request version must be 1", encodedSourceBytes);
467
- }
468
- if (parsed.instructions !== void 0 && typeof parsed.instructions !== "string") {
469
- return rejection("invalid-type", "typed compaction request instructions must be a string", encodedSourceBytes);
470
- }
471
- if (parsed.preserve !== void 0 && !Array.isArray(parsed.preserve)) {
472
- return rejection("invalid-type", "typed compaction request preserve must be an array", encodedSourceBytes);
473
- }
474
- const preserve = parsed.preserve;
475
- if (preserve !== void 0 && preserve.length > MAX_PRESERVE_ITEMS) {
476
- return rejection(
477
- "preserve-too-many",
478
- `typed compaction preserve exceeds ${MAX_PRESERVE_ITEMS} items`,
479
- encodedSourceBytes
480
- );
481
- }
482
- if (preserve !== void 0 && !preserve.every((item) => typeof item === "string")) {
483
- return rejection("invalid-type", "typed compaction preserve items must be strings", encodedSourceBytes);
484
- }
485
- const request = {
486
- ...parsed.instructions !== void 0 ? { instructions: parsed.instructions } : {},
487
- ...preserve !== void 0 ? { preserve } : {}
488
- };
489
- const boundsError = compactionRequestBoundsError(request);
490
- if (boundsError) return rejection(boundsError.code, boundsError.message, encodedSourceBytes);
491
- const requestLines = [];
492
- let valueSourceBytes = 0;
493
- let truncated = false;
494
- let canonicalized = false;
495
- if (request.instructions !== void 0) {
496
- const instructions = canonicalizeText(request.instructions, MAX_COMPACTION_INSTRUCTIONS_BYTES);
497
- valueSourceBytes += instructions.sourceBytes;
498
- truncated ||= instructions.truncated;
499
- canonicalized ||= instructions.text !== request.instructions;
500
- if (instructions.text) requestLines.push(instructions.text);
501
- }
502
- for (let index = 0; index < (request.preserve?.length ?? 0); index++) {
503
- const sourceItem = request.preserve[index];
504
- const item = canonicalizeText(sourceItem, MAX_PRESERVE_ITEM_BYTES);
505
- valueSourceBytes += item.sourceBytes;
506
- truncated ||= item.truncated;
507
- canonicalized ||= item.text !== sourceItem;
508
- if (item.text) requestLines.push(`- ${item.text} [preserve:${index}]`);
509
- }
510
- return {
511
- ok: true,
512
- requestLines,
513
- policy: {
514
- mode: "typed-v1",
515
- canonicalized,
516
- sourceBytes: valueSourceBytes,
517
- truncated,
518
- preserveCount: request.preserve?.length ?? 0,
519
- omittedPreserveCount: 0
520
- }
521
- };
522
- };
523
-
524
136
  // src/run-display.ts
525
137
  var recordDisplay = (record) => {
526
138
  const display = {};
@@ -1855,13 +1467,13 @@ var computeCut = (branchEntries, options) => {
1855
1467
  live[closed].entry.id
1856
1468
  );
1857
1469
  };
1858
- var isRecord2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1859
- var isEntryRange = (value) => isRecord2(value) && typeof value.first === "string" && typeof value.last === "string";
1470
+ var isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1471
+ var isEntryRange = (value) => isRecord(value) && typeof value.first === "string" && typeof value.last === "string";
1860
1472
  var isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
1861
1473
  var isFabricV1Details = (value) => isStringArray(value.sections) && isEntryRange(value.summarizedEntryRange) && typeof value.sourceEntryCount === "number" && Number.isFinite(value.sourceEntryCount) && typeof value.firstKeptEntryId === "string" && typeof value.timestamp === "string";
1862
1474
  var hasFiniteNumbers = (value, keys) => keys.every((key) => typeof value[key] === "number" && Number.isFinite(value[key]));
1863
1475
  var isCompactionBudgetDetails = (value) => {
1864
- if (!isRecord2(value) || value.strategy !== "adaptive" && value.strategy !== "continuity") {
1476
+ if (!isRecord(value) || value.strategy !== "adaptive" && value.strategy !== "continuity") {
1865
1477
  return false;
1866
1478
  }
1867
1479
  if (!hasFiniteNumbers(value, [
@@ -1888,8 +1500,8 @@ var isCompactionBudgetDetails = (value) => {
1888
1500
  ]) && (value.bindingConstraint === "continuity" || value.bindingConstraint === "occupancy" || value.bindingConstraint === "reserve" || value.bindingConstraint === "reduction");
1889
1501
  };
1890
1502
  var isFabricV2Details = (value) => {
1891
- if (!isStringArray(value.sections) || !isRecord2(value.coverage) || !isRecord2(value.counts)) return false;
1892
- if (!isRecord2(value.omittedCounts) || !isRecord2(value.instructionPolicy) || !isRecord2(value.stableAddresses)) {
1503
+ if (!isStringArray(value.sections) || !isRecord(value.coverage) || !isRecord(value.counts)) return false;
1504
+ if (!isRecord(value.omittedCounts) || !isRecord(value.instructionPolicy) || !isRecord(value.stableAddresses)) {
1893
1505
  return false;
1894
1506
  }
1895
1507
  const instructionModes = /* @__PURE__ */ new Set(["none", "plain", "typed-v1", "malformed-typed-prefix"]);
@@ -1914,7 +1526,7 @@ var isFabricV2Details = (value) => {
1914
1526
  ]) && typeof value.stableAddresses.firstKeptEntryId === "string" && isEntryRange(value.stableAddresses.cumulativeSourceRange) && value.stableAddresses.recall === "session-entry-id-range" && (value.budget === void 0 || isCompactionBudgetDetails(value.budget)) && typeof value.timestamp === "string";
1915
1527
  };
1916
1528
  var fabricCompactionVersion = (details) => {
1917
- if (!isRecord2(details) || details.compactor !== "fabric") return void 0;
1529
+ if (!isRecord(details) || details.compactor !== "fabric") return void 0;
1918
1530
  if (details.version === 1 && isFabricV1Details(details)) return 1;
1919
1531
  if (details.version === 2 && isFabricV2Details(details)) return 2;
1920
1532
  return void 0;
@@ -2065,7 +1677,10 @@ var registerCompactionHook = (omp, options) => {
2065
1677
  tokensBefore: event.preparation.tokensBefore,
2066
1678
  ...event.customInstructions === void 0 ? {} : { customInstructions: event.customInstructions }
2067
1679
  });
2068
- } catch {
1680
+ } catch (error) {
1681
+ const message = error instanceof Error ? error.message : String(error);
1682
+ console.warn(`[omp-fabric] LCM compaction failed: ${message}`);
1683
+ if (context?.hasUI) context.ui.notify(`LCM compaction failed: ${message}`, "error");
2069
1684
  return { cancel: true };
2070
1685
  }
2071
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)) {
@@ -2101,11 +1716,6 @@ var registerCompactionHook = (omp, options) => {
2101
1716
  export {
2102
1717
  estimateTokens,
2103
1718
  compactAtConfiguredThreshold,
2104
- MAX_COMPACTION_INSTRUCTIONS_CHARS,
2105
- MAX_PRESERVE_ITEMS,
2106
- MAX_PRESERVE_ITEM_CHARS,
2107
- compactionRequestBoundsError,
2108
- encodeCompactionRequest,
2109
1719
  normalizeRunDisplay,
2110
1720
  fabricExecTitleHintCached,
2111
1721
  rawContextTokens,
@@ -2114,4 +1724,4 @@ export {
2114
1724
  compileFabricSummary,
2115
1725
  registerCompactionHook
2116
1726
  };
2117
- //# sourceMappingURL=chunk-KJKO6KGW.js.map
1727
+ //# sourceMappingURL=chunk-D2ET33NE.js.map