omp-fabric 1.13.1 → 1.14.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.
@@ -867,6 +867,20 @@ function migrateSessions(options) {
867
867
  import crypto3 from "node:crypto";
868
868
  var DEFAULT_LCM_BUDGET = { calls: Number.POSITIVE_INFINITY, inputTokens: Number.POSITIVE_INFINITY, outputTokens: Number.POSITIVE_INFINITY, cost: Number.POSITIVE_INFINITY, wallMs: Number.POSITIVE_INFINITY, sessionCalls: Number.POSITIVE_INFINITY };
869
869
  var DEFAULT_LEAF_ENTRIES = 32;
870
+ var DEFAULT_MAINTENANCE_CONCURRENCY = 3;
871
+ var LEASE_MS = 3e4;
872
+ var LEASE_SWEEP_GRACE_MS = 6e4;
873
+ var LcmRejection = class extends Error {
874
+ reason;
875
+ constructor(reason) {
876
+ super(reason);
877
+ this.name = "LcmRejection";
878
+ this.reason = reason;
879
+ }
880
+ };
881
+ var isLcmRejection = (error) => error instanceof LcmRejection;
882
+ var isLcmCapacityRejection = (error) => isLcmRejection(error) && (error.reason === "project lease held" || error.reason === "budget exhausted");
883
+ var LEGACY_CONTENTION_RETIREMENTS = /* @__PURE__ */ new Set(["job lease held", "project lease held", "LcmRejection: job lease held", "LcmRejection: project lease held"]);
870
884
  var hash3 = (v) => hashLcmPayload(v);
871
885
  var parse = (v) => JSON.parse(String(v));
872
886
  var rawHash = (e) => e.payloadHash;
@@ -881,9 +895,14 @@ AND CAST(json_extract(j.payload,'$.nextRetryAt') AS INTEGER)<=?
881
895
  AND (? IS NULL OR json_extract(n.payload,'$.sessionId')=?)
882
896
  ORDER BY CAST(json_extract(j.payload,'$.priority') AS INTEGER) DESC, j.created_at, j.job_id
883
897
  LIMIT ?`;
898
+ var LIVE_LEASES_SQL = `SELECT coalesce(json_extract(n.payload,'$.sessionId'),'') sessionId
899
+ FROM maintenance_jobs j
900
+ LEFT JOIN summary_nodes n ON n.node_id=json_extract(j.payload,'$.nodeId') AND n.project_key=j.project_key
901
+ WHERE j.project_key=? AND j.status='running' AND j.job_id<>? AND CAST(coalesce(json_extract(j.payload,'$.leaseUntil'),0) AS INTEGER)>?`;
884
902
  var LcmMaintenance = class {
885
903
  constructor(ledger, options = {}) {
886
904
  this.ledger = ledger;
905
+ this.maxConcurrentJobs = options.maxConcurrentJobs ?? DEFAULT_MAINTENANCE_CONCURRENCY;
887
906
  this.projectKey = ledger.project.key;
888
907
  this.now = options.now ?? Date.now;
889
908
  this.ownerId = options.ownerId ?? crypto3.randomUUID();
@@ -905,9 +924,27 @@ var LcmMaintenance = class {
905
924
  maxOutputChars;
906
925
  budget;
907
926
  modelTimeoutMs;
927
+ maxConcurrentJobs;
908
928
  budgetPolicy() {
909
929
  return { ...this.budget };
910
930
  }
931
+ get concurrencyLimit() {
932
+ const raw = typeof this.maxConcurrentJobs === "function" ? this.maxConcurrentJobs() : this.maxConcurrentJobs;
933
+ return Number.isFinite(raw) ? Math.max(1, Math.floor(raw)) : DEFAULT_MAINTENANCE_CONCURRENCY;
934
+ }
935
+ liveLeases(db, at, excludeJobId) {
936
+ return db.prepare(LIVE_LEASES_SQL).all(this.projectKey, excludeJobId ?? "", at - LEASE_SWEEP_GRACE_MS);
937
+ }
938
+ admits(db, at, sessionId, leases) {
939
+ const day = this.day(at);
940
+ const project = this.usage(db, day);
941
+ const session = this.usage(db, day, sessionId);
942
+ let sessionReserved = 0;
943
+ for (const lease of leases) if (lease.sessionId === sessionId) sessionReserved += 1;
944
+ const reserved = leases.length + 1;
945
+ const fits = (used, cap) => used < cap && used + (project.calls > 0 ? used / project.calls * reserved : 0) <= cap;
946
+ return project.calls + leases.length < this.budget.calls && fits(project.inputTokens, this.budget.inputTokens) && fits(project.outputTokens, this.budget.outputTokens) && fits(project.cost, this.budget.cost) && fits(project.wallMs, this.budget.wallMs) && session.calls + sessionReserved < this.budget.sessionCalls;
947
+ }
911
948
  recordRevision(db, node) {
912
949
  if (!node.text) return;
913
950
  const row = db.prepare("SELECT coalesce(max(revision),0) n FROM summary_node_revisions WHERE node_id=?").get(node.nodeId);
@@ -990,7 +1027,7 @@ var LcmMaintenance = class {
990
1027
  claimableJobs(sessionId, limit = 64) {
991
1028
  const t = this.now();
992
1029
  const scope = sessionId ?? null;
993
- return this.ledger.readOnly((db) => db.prepare(CLAIMABLE_JOBS_SQL).all(this.projectKey, t, t, t, scope, scope, limit).map((r) => {
1030
+ return this.ledger.readOnly((db) => db.prepare(CLAIMABLE_JOBS_SQL).all(this.projectKey, t - LEASE_SWEEP_GRACE_MS, t, t, scope, scope, limit).map((r) => {
994
1031
  const job = parse(r.payload);
995
1032
  if (job.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
996
1033
  return job;
@@ -1001,25 +1038,43 @@ var LcmMaintenance = class {
1001
1038
  }
1002
1039
  isClaimable(job) {
1003
1040
  const t = this.now();
1004
- return (job.state === "pending" || job.state === "running" && (job.leaseUntil ?? 0) <= t) && job.eligibleAt <= t && job.nextRetryAt <= t;
1041
+ return (job.state === "pending" || job.state === "running" && (job.leaseUntil ?? 0) + LEASE_SWEEP_GRACE_MS <= t) && job.eligibleAt <= t && job.nextRetryAt <= t;
1005
1042
  }
1006
- sweepExpiredLeases(graceMs = 0) {
1043
+ sweepExpiredLeases() {
1007
1044
  return this.ledger.transaction((db) => {
1008
- const t = this.now();
1045
+ const t = this.now() - LEASE_SWEEP_GRACE_MS;
1009
1046
  const rows = db.prepare("SELECT payload FROM maintenance_jobs WHERE project_key=? AND status=?").all(this.projectKey, "running");
1010
1047
  const swept = [];
1011
1048
  for (const row of rows) {
1012
1049
  const old = parse(row.payload);
1013
1050
  if (old.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
1014
- if ((old.leaseUntil ?? 0) + graceMs > t) continue;
1051
+ if ((old.leaseUntil ?? 0) > t) continue;
1052
+ const now = this.now();
1015
1053
  const { ownerId: _ownerId, leaseToken: _leaseToken, leaseUntil: _leaseUntil, ...base } = old;
1016
- const job = { ...base, state: "pending", error: "lease expired", eligibleAt: t, nextRetryAt: t, updatedAt: t };
1054
+ const job = { ...base, state: "pending", error: "lease expired", eligibleAt: now, nextRetryAt: now, updatedAt: now };
1017
1055
  db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(job.state, JSON.stringify(job), job.updatedAt, job.jobId, this.projectKey);
1018
1056
  swept.push(job);
1019
1057
  }
1020
1058
  return swept;
1021
1059
  });
1022
1060
  }
1061
+ recoverLegacyContentionRetirements() {
1062
+ return this.ledger.transaction((db) => {
1063
+ const rows = db.prepare("SELECT payload FROM maintenance_jobs WHERE project_key=? AND status=?").all(this.projectKey, "failed");
1064
+ const recovered = [];
1065
+ for (const row of rows) {
1066
+ const old = parse(row.payload);
1067
+ if (old.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
1068
+ if (old.legacyRecovery || !LEGACY_CONTENTION_RETIREMENTS.has(old.error ?? "")) continue;
1069
+ const t = this.now();
1070
+ const { error: _error, ...base } = old;
1071
+ const job = { ...base, state: "pending", attempts: 0, legacyRecovery: true, eligibleAt: t, nextRetryAt: t, updatedAt: t };
1072
+ db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(job.state, JSON.stringify(job), job.updatedAt, job.jobId, this.projectKey);
1073
+ recovered.push(job);
1074
+ }
1075
+ return recovered;
1076
+ });
1077
+ }
1023
1078
  recordFailure(job, error) {
1024
1079
  return this.ledger.transaction((db) => {
1025
1080
  const row = db.prepare("SELECT payload FROM maintenance_jobs WHERE job_id=? AND project_key=?").get(job.jobId, this.projectKey);
@@ -1132,18 +1187,15 @@ var LcmMaintenance = class {
1132
1187
  const old = parse(row.payload);
1133
1188
  if (old.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
1134
1189
  const t = this.now();
1135
- const expired = old.state === "running" && (old.leaseUntil ?? 0) <= t;
1136
- if (old.state === "running" && !expired) throw new Error("job lease held");
1137
- if (old.state !== "pending" && !expired || old.eligibleAt > t || old.nextRetryAt > t) throw new Error("job not eligible");
1138
- const active = db.prepare("SELECT payload FROM maintenance_jobs WHERE project_key=? AND status=?").all(this.projectKey, "running").map((r) => parse(r.payload)).filter((j) => j.jobId !== old.jobId && (j.leaseUntil ?? 0) > t);
1139
- if (active.length) throw new Error("project lease held");
1190
+ const expired = old.state === "running" && (old.leaseUntil ?? 0) + LEASE_SWEEP_GRACE_MS <= t;
1191
+ if (old.state === "running" && !expired) throw new LcmRejection("job lease held");
1192
+ if (old.state !== "pending" && !expired || old.eligibleAt > t || old.nextRetryAt > t) throw new LcmRejection("job not eligible");
1193
+ const leases = this.liveLeases(db, t, old.jobId);
1194
+ if (leases.length >= this.concurrencyLimit) throw new LcmRejection("project lease held");
1140
1195
  const nodeRow = db.prepare("SELECT payload FROM summary_nodes WHERE node_id=? AND project_key=?").get(old.nodeId, this.projectKey);
1141
1196
  const sessionId = nodeRow?.payload ? parse(nodeRow.payload).sessionId : "";
1142
- const day = this.day(t);
1143
- const projectUsage = this.usage(db, day);
1144
- const sessionUsage = this.usage(db, day, sessionId);
1145
- if (projectUsage.calls >= this.budget.calls || projectUsage.inputTokens >= this.budget.inputTokens || projectUsage.outputTokens >= this.budget.outputTokens || projectUsage.cost >= this.budget.cost || projectUsage.wallMs >= this.budget.wallMs || sessionUsage.calls >= this.budget.sessionCalls) throw new Error("budget exhausted");
1146
- const out = { ...old, state: "running", ownerId, leaseToken: token(), leaseUntil: t + 3e4, updatedAt: t };
1197
+ if (!this.admits(db, t, sessionId, leases)) throw new LcmRejection("budget exhausted");
1198
+ const out = { ...old, state: "running", ownerId, leaseToken: token(), leaseUntil: t + LEASE_MS, updatedAt: t };
1147
1199
  db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(out.state, JSON.stringify(out), t, jobId, this.projectKey);
1148
1200
  return out;
1149
1201
  });
@@ -1156,15 +1208,15 @@ var LcmMaintenance = class {
1156
1208
  if (old.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
1157
1209
  const t = this.now();
1158
1210
  const expired = old.state === "running" && (old.leaseUntil ?? 0) <= t;
1159
- if (old.state === "running" && !expired) throw new Error("job lease held");
1211
+ if (old.state === "running" && !expired) throw new LcmRejection("job lease held");
1160
1212
  if (old.state === "completed") throw new Error("job already completed");
1161
- const out = { ...old, state: "running", ownerId, leaseToken: token(), leaseUntil: t + 3e4, updatedAt: t };
1213
+ const out = { ...old, state: "running", ownerId, leaseToken: token(), leaseUntil: t + LEASE_MS, updatedAt: t };
1162
1214
  db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(out.state, JSON.stringify(out), t, jobId, this.projectKey);
1163
1215
  return out;
1164
1216
  });
1165
1217
  }
1166
1218
  renew(job) {
1167
- return this.fenced(job, (old) => ({ ...old, leaseUntil: this.now() + 3e4, updatedAt: this.now() }));
1219
+ return this.fenced(job, (old) => ({ ...old, leaseUntil: this.now() + LEASE_MS, updatedAt: this.now() }));
1168
1220
  }
1169
1221
  validateText(text) {
1170
1222
  if (typeof text !== "string" || text.length === 0) throw new Error("invalid summary text");
@@ -1178,12 +1230,10 @@ var LcmMaintenance = class {
1178
1230
  if (!Number.isSafeInteger(inputBytes) || inputBytes < 0) throw new Error("invalid input bound");
1179
1231
  if (utf8Bytes(text) >= inputBytes) throw new Error("summary does not shrink its input");
1180
1232
  }
1181
- withinBudget(sessionId) {
1233
+ withinBudget(sessionId, excludeJobId) {
1182
1234
  return this.ledger.readOnly((db) => {
1183
- const day = this.day(this.now());
1184
- const project = this.usage(db, day);
1185
- const session = this.usage(db, day, sessionId);
1186
- return project.calls < this.budget.calls && project.inputTokens < this.budget.inputTokens && project.outputTokens < this.budget.outputTokens && project.cost < this.budget.cost && project.wallMs < this.budget.wallMs && session.calls < this.budget.sessionCalls;
1235
+ const at = this.now();
1236
+ return this.admits(db, at, sessionId, this.liveLeases(db, at, excludeJobId));
1187
1237
  });
1188
1238
  }
1189
1239
  accountRejected(sessionId, result) {
@@ -1277,7 +1327,7 @@ var LcmMaintenance = class {
1277
1327
  let lastError = new Error("LCM summarization did not converge");
1278
1328
  for (const [index, level] of LCM_MODEL_LEVELS.entries()) {
1279
1329
  if (controller.signal.aborted) break;
1280
- if (index > 0 && !this.withinBudget(sessionId)) break;
1330
+ if (index > 0 && !this.withinBudget(sessionId, job.jobId)) break;
1281
1331
  const target = Math.max(1, Math.min(Math.floor(this.maxOutputChars * level.share), inputBytes - 1));
1282
1332
  let result;
1283
1333
  try {
@@ -1302,7 +1352,8 @@ var LcmMaintenance = class {
1302
1352
  const fallback = emergencyReduce(input, this.maxOutputChars, sources);
1303
1353
  try {
1304
1354
  return this.completeEmergency(claimed, fallback);
1305
- } catch {
1355
+ } catch (fenced) {
1356
+ if (isLcmRejection(fenced) && fenced.reason === "lease fenced") throw fenced;
1306
1357
  try {
1307
1358
  this.fail(claimed, String(error));
1308
1359
  } catch {
@@ -1335,7 +1386,7 @@ var LcmMaintenance = class {
1335
1386
  return job;
1336
1387
  }
1337
1388
  assertLease(current, expected) {
1338
- if (current.ownerId !== expected.ownerId || current.leaseToken !== expected.leaseToken || current.state !== "running" || (current.leaseUntil ?? 0) < this.now()) throw new Error("lease fenced");
1389
+ if (current.ownerId !== expected.ownerId || current.leaseToken !== expected.leaseToken || current.state !== "running" || (current.leaseUntil ?? 0) < this.now()) throw new LcmRejection("lease fenced");
1339
1390
  }
1340
1391
  fenced(job, update) {
1341
1392
  return this.ledger.transaction((db) => {
@@ -1349,7 +1400,6 @@ var LcmMaintenance = class {
1349
1400
  };
1350
1401
 
1351
1402
  // src/compaction/lcm-runtime.ts
1352
- var LEASE_SWEEP_GRACE_MS = 6e4;
1353
1403
  var MAX_RECONCILE_RECOVERIES = 3;
1354
1404
  var CLAIMABLE_JOB_LIMIT = 256;
1355
1405
  var FAILED_JOB_WINDOW_MS = 24 * 60 * 60 * 1e3;
@@ -1445,6 +1495,7 @@ var LcmRuntime = class {
1445
1495
  ...initial.lcmMaxInputChars === void 0 ? {} : { maxInputChars: initial.lcmMaxInputChars },
1446
1496
  ...initial.lcmMaxOutputChars === void 0 ? {} : { maxOutputChars: initial.lcmMaxOutputChars },
1447
1497
  ...initial.modelTimeoutSeconds === void 0 ? {} : { modelTimeoutMs: initial.modelTimeoutSeconds * 1e3 },
1498
+ maxConcurrentJobs: () => this.resolveOptions().maintenanceConcurrency ?? DEFAULT_MAINTENANCE_CONCURRENCY,
1448
1499
  budget: {
1449
1500
  ...initial.maxDailyModelCalls ? { calls: initial.maxDailyModelCalls } : {},
1450
1501
  ...initial.maxSessionModelCalls ? { sessionCalls: initial.maxSessionModelCalls } : {},
@@ -1587,7 +1638,7 @@ var LcmRuntime = class {
1587
1638
  reclaimExpiredLeases() {
1588
1639
  if (this.closed) return 0;
1589
1640
  try {
1590
- return this.maintenance.sweepExpiredLeases(LEASE_SWEEP_GRACE_MS).length;
1641
+ return this.maintenance.sweepExpiredLeases().length + this.maintenance.recoverLegacyContentionRetirements().length;
1591
1642
  } catch (error) {
1592
1643
  this.degradedError = error;
1593
1644
  return 0;
@@ -1680,24 +1731,49 @@ var LcmRuntime = class {
1680
1731
  try {
1681
1732
  const input = inputFor(node);
1682
1733
  await this.maintenance.run(job, model, input, this.signal);
1734
+ return "done";
1683
1735
  } catch (error) {
1736
+ if (isLcmCapacityRejection(error)) return "capacity";
1737
+ if (isLcmRejection(error)) return "rejected";
1684
1738
  this.degradedError = error;
1685
1739
  try {
1686
1740
  this.maintenance.recordFailure(job, error);
1687
1741
  } catch {
1688
1742
  }
1743
+ return "done";
1689
1744
  }
1690
1745
  };
1746
+ const dispatch = async (items) => {
1747
+ let next = 0;
1748
+ const worker = async (slot) => {
1749
+ while (!this.closed && Date.now() < runDeadline && slot < this.maintenance.concurrencyLimit) {
1750
+ const item = items[next];
1751
+ if (!item) return;
1752
+ next += 1;
1753
+ if (await runJob(item.job, item.node) === "capacity") return;
1754
+ }
1755
+ };
1756
+ await Promise.all(Array.from({ length: Math.min(this.maintenance.concurrencyLimit, items.length) }, (_unused, slot) => worker(slot)));
1757
+ };
1691
1758
  this.reclaimExpiredLeases();
1692
1759
  for (let pass = 0; pass < passes && !this.closed && Date.now() < runDeadline; pass += 1) {
1693
1760
  const leafCandidate = this.maintenance.createLeaf(this.maintenance.selectLeaf(raw, this.context.sessionManager.getLeafId()));
1694
1761
  const leaf = leafCandidate ? this.maintenance.getNode(leafCandidate.nodeId) : void 0;
1762
+ const batch = [];
1763
+ const queued = /* @__PURE__ */ new Set();
1695
1764
  if (leaf?.state === "pending") {
1696
1765
  const job = this.maintenance.jobForNode(leaf.nodeId);
1697
- if (job && this.maintenance.isClaimable(job)) await runJob(job, leaf);
1766
+ if (job && this.maintenance.isClaimable(job)) {
1767
+ batch.push({ job, node: leaf });
1768
+ queued.add(job.jobId);
1769
+ }
1698
1770
  }
1699
1771
  const pending = this.actionableJobs(sessionId, this.context.sessionManager.getLeafId(), passes);
1700
- for (const item of pending) await runJob(item.job, item.node);
1772
+ for (const item of pending) if (!queued.has(item.job.jobId)) {
1773
+ queued.add(item.job.jobId);
1774
+ batch.push(item);
1775
+ }
1776
+ await dispatch(batch);
1701
1777
  const children = this.maintenance.selectCondensation(sessionId, this.activeSources, this.context.sessionManager.getLeafId());
1702
1778
  if (children.length >= fanIn) {
1703
1779
  const node = this.maintenance.createCondensed(children);
@@ -1766,8 +1842,13 @@ var LcmRuntime = class {
1766
1842
  }
1767
1843
  const job = this.maintenance.jobForNode(leaf.nodeId);
1768
1844
  if (!job) throw new Error("LCM emergency fallback job was not created");
1769
- const completed = this.maintenance.completeEmergency(this.maintenance.claimEmergency(job.jobId), fallback);
1770
- return { summary: completed.text ?? fallback, firstKeptEntryId: input.firstKeptEntryId, tokensBefore: input.tokensBefore, source: "emergency", branch: input.branch };
1845
+ try {
1846
+ const completed = this.maintenance.completeEmergency(this.maintenance.claimEmergency(job.jobId), fallback);
1847
+ return { summary: completed.text ?? fallback, firstKeptEntryId: input.firstKeptEntryId, tokensBefore: input.tokensBefore, source: "emergency", branch: input.branch };
1848
+ } catch (error) {
1849
+ if (!isLcmRejection(error)) throw error;
1850
+ return { summary: fallback, firstKeptEntryId: input.firstKeptEntryId, tokensBefore: input.tokensBefore, source: "emergency", branch: input.branch };
1851
+ }
1771
1852
  }
1772
1853
  report() {
1773
1854
  const day = new Date(Date.now()).toISOString().slice(0, 10);