omp-fabric 1.4.4 → 1.5.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.
- package/CHANGELOG.md +26 -0
- package/dist/chunks/{chunk-TR3LQHO7.js → chunk-2ZEMWIHZ.js} +2 -2
- package/dist/chunks/chunk-3XPR2AZL.js +74 -0
- package/dist/chunks/chunk-3XPR2AZL.js.map +7 -0
- package/dist/chunks/{chunk-SZWF6C4V.js → chunk-4MVTUMKO.js} +3 -3
- package/dist/chunks/{chunk-SG4GVSWX.js → chunk-EQSL47QL.js} +10 -66
- package/dist/chunks/chunk-EQSL47QL.js.map +7 -0
- package/dist/chunks/{chunk-OW2LSF5R.js → chunk-IGNMZZQH.js} +3 -3
- package/dist/chunks/{chunk-LB2EC7FZ.js → chunk-JMYHQUJQ.js} +9 -105
- package/dist/chunks/chunk-JMYHQUJQ.js.map +7 -0
- package/dist/chunks/{chunk-6JPXLXDS.js → chunk-KHQJKGBC.js} +2 -2
- package/dist/chunks/{chunk-6JPXLXDS.js.map → chunk-KHQJKGBC.js.map} +1 -1
- package/dist/chunks/chunk-KTB4R3E3.js +51 -0
- package/dist/chunks/chunk-KTB4R3E3.js.map +7 -0
- package/dist/chunks/chunk-TQQPXV3I.js +58 -0
- package/dist/chunks/chunk-TQQPXV3I.js.map +7 -0
- package/dist/chunks/{chunk-2J2GOB4A.js → chunk-VDLZAORG.js} +5 -3
- package/dist/chunks/chunk-VDLZAORG.js.map +7 -0
- package/dist/chunks/chunk-ZDWBTQB6.js +107 -0
- package/dist/chunks/chunk-ZDWBTQB6.js.map +7 -0
- package/dist/compaction/hook.js +2 -1
- package/dist/compaction/lcm-loader.d.ts +12 -0
- package/dist/compaction/lcm-loader.d.ts.map +1 -0
- package/dist/compaction/lcm-maintenance.d.ts +2 -1
- package/dist/compaction/lcm-maintenance.d.ts.map +1 -1
- package/dist/compaction/lcm-runtime.d.ts +1 -0
- package/dist/compaction/lcm-runtime.d.ts.map +1 -1
- package/dist/compaction/lcm-runtime.js +1169 -0
- package/dist/compaction/lcm-runtime.js.map +7 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/fabric-runtime-state.js +9 -7
- package/dist/fabric-runtime-state.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +135 -989
- package/dist/index.js.map +4 -4
- package/dist/memory/lcm-adapter.d.ts +1 -1
- package/dist/memory/lcm-adapter.d.ts.map +1 -1
- package/dist/providers/memory-provider.js +2 -2
- package/dist/residency/host.js +8 -6
- package/dist/residency/omp-entry.js +8 -6
- package/dist/residency/omp-entry.js.map +1 -1
- package/dist/storage/lcm-identity.d.ts +43 -0
- package/dist/storage/lcm-identity.d.ts.map +1 -0
- package/dist/storage/lcm-ledger.d.ts +7 -46
- package/dist/storage/lcm-ledger.d.ts.map +1 -1
- package/dist/storage/sqlite.d.ts +19 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/ui/dashboard.js +3 -3
- package/dist/ui/settings.js +4 -4
- package/docs/compaction.md +8 -1
- package/package.json +1 -1
- package/dist/chunks/chunk-2J2GOB4A.js.map +0 -7
- package/dist/chunks/chunk-LB2EC7FZ.js.map +0 -7
- package/dist/chunks/chunk-MBOXJKF6.js +0 -311
- package/dist/chunks/chunk-MBOXJKF6.js.map +0 -7
- package/dist/chunks/chunk-SG4GVSWX.js.map +0 -7
- /package/dist/chunks/{chunk-TR3LQHO7.js.map → chunk-2ZEMWIHZ.js.map} +0 -0
- /package/dist/chunks/{chunk-SZWF6C4V.js.map → chunk-4MVTUMKO.js.map} +0 -0
- /package/dist/chunks/{chunk-OW2LSF5R.js.map → chunk-IGNMZZQH.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -46,6 +46,9 @@ import {
|
|
|
46
46
|
formatTokens,
|
|
47
47
|
safeText
|
|
48
48
|
} from "./chunks/chunk-KIH3KLZU.js";
|
|
49
|
+
import {
|
|
50
|
+
loadSqliteDriver
|
|
51
|
+
} from "./chunks/chunk-TQQPXV3I.js";
|
|
49
52
|
import {
|
|
50
53
|
ApprovalController,
|
|
51
54
|
CapturedToolCatalog,
|
|
@@ -67,7 +70,7 @@ import {
|
|
|
67
70
|
resolveFabricIdentity,
|
|
68
71
|
settleInPlacePrewalk,
|
|
69
72
|
streamedToolCallName
|
|
70
|
-
} from "./chunks/chunk-
|
|
73
|
+
} from "./chunks/chunk-2ZEMWIHZ.js";
|
|
71
74
|
import "./chunks/chunk-7VIQNUPS.js";
|
|
72
75
|
import {
|
|
73
76
|
AgentTranscriptReader
|
|
@@ -168,10 +171,7 @@ import {
|
|
|
168
171
|
replaySuccessfulCallsAsync,
|
|
169
172
|
schemaDigest
|
|
170
173
|
} from "./chunks/chunk-4A4MVZQW.js";
|
|
171
|
-
import
|
|
172
|
-
sessionDirNamesForCwd,
|
|
173
|
-
sessionsDirRoot
|
|
174
|
-
} from "./chunks/chunk-FX3XLQYI.js";
|
|
174
|
+
import "./chunks/chunk-FX3XLQYI.js";
|
|
175
175
|
import "./chunks/chunk-6QBPSZJ5.js";
|
|
176
176
|
import {
|
|
177
177
|
FABRIC_COMPONENT_DISCOVER_EVENT,
|
|
@@ -189,12 +189,9 @@ import {
|
|
|
189
189
|
readFabricToolResultProxyDetailsV1
|
|
190
190
|
} from "./chunks/chunk-RBA54WC7.js";
|
|
191
191
|
import {
|
|
192
|
-
LcmModelAdapter,
|
|
193
192
|
awaitPeerSettle,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
emergencyReduce
|
|
197
|
-
} from "./chunks/chunk-LB2EC7FZ.js";
|
|
193
|
+
buildPeerCards
|
|
194
|
+
} from "./chunks/chunk-JMYHQUJQ.js";
|
|
198
195
|
import {
|
|
199
196
|
DEFAULT_FABRIC_CONFIG,
|
|
200
197
|
effectiveToolCaptureConfig,
|
|
@@ -202,20 +199,17 @@ import {
|
|
|
202
199
|
loadFabricConfigForScope,
|
|
203
200
|
resolveAgentDir,
|
|
204
201
|
saveFabricConfig
|
|
205
|
-
} from "./chunks/chunk-
|
|
202
|
+
} from "./chunks/chunk-VDLZAORG.js";
|
|
206
203
|
import {
|
|
207
204
|
FABRIC_ACTOR_OMP_HOST_EVENTS
|
|
208
205
|
} from "./chunks/chunk-3LI7YO4M.js";
|
|
209
206
|
import {
|
|
210
207
|
defaultCodePreviewSettings
|
|
211
208
|
} from "./chunks/chunk-JSBTB3P6.js";
|
|
209
|
+
import "./chunks/chunk-ZDWBTQB6.js";
|
|
212
210
|
import {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
canonicalProjectIdentity,
|
|
216
|
-
defaultLedgerPath,
|
|
217
|
-
hashLcmPayload
|
|
218
|
-
} from "./chunks/chunk-MBOXJKF6.js";
|
|
211
|
+
canonicalProjectIdentity
|
|
212
|
+
} from "./chunks/chunk-KTB4R3E3.js";
|
|
219
213
|
import {
|
|
220
214
|
FABRIC_EXECUTION_GUIDANCE_SLOT,
|
|
221
215
|
MAX_FABRIC_MODEL_GUIDANCE_CONTENT_CHARS,
|
|
@@ -234,10 +228,11 @@ import {
|
|
|
234
228
|
fabricExecTitleHintCached,
|
|
235
229
|
normalizeRunDisplay,
|
|
236
230
|
registerCompactionHook
|
|
237
|
-
} from "./chunks/chunk-
|
|
231
|
+
} from "./chunks/chunk-EQSL47QL.js";
|
|
238
232
|
import {
|
|
239
233
|
fabricWriteBindings
|
|
240
234
|
} from "./chunks/chunk-3J7BKUNG.js";
|
|
235
|
+
import "./chunks/chunk-3XPR2AZL.js";
|
|
241
236
|
import "./chunks/chunk-4HQDSTHN.js";
|
|
242
237
|
import {
|
|
243
238
|
FABRIC_EXECUTION_TRACE_KIND,
|
|
@@ -276,13 +271,13 @@ var clearTimingInterval = (state) => {
|
|
|
276
271
|
};
|
|
277
272
|
var ensureTimingInterval = (state, invalidate) => {
|
|
278
273
|
state.codePreviewTimingInterval ??= setInterval(() => {
|
|
279
|
-
const
|
|
280
|
-
state.codePreviewTimingOnlyRenderToken =
|
|
274
|
+
const token = (state.codePreviewTimingOnlyRenderToken ?? 0) + 1;
|
|
275
|
+
state.codePreviewTimingOnlyRenderToken = token;
|
|
281
276
|
try {
|
|
282
277
|
invalidate();
|
|
283
278
|
} finally {
|
|
284
279
|
queueMicrotask(() => {
|
|
285
|
-
if (state.codePreviewTimingOnlyRenderToken ===
|
|
280
|
+
if (state.codePreviewTimingOnlyRenderToken === token) {
|
|
286
281
|
delete state.codePreviewTimingOnlyRenderToken;
|
|
287
282
|
}
|
|
288
283
|
});
|
|
@@ -818,10 +813,10 @@ var extractContentText = (content) => {
|
|
|
818
813
|
};
|
|
819
814
|
var summarizeLogLine = (entry) => {
|
|
820
815
|
if (typeof entry !== "object" || entry === null) return truncateMiddle(String(entry), 200);
|
|
821
|
-
const
|
|
822
|
-
const type = typeof
|
|
823
|
-
const tool = typeof
|
|
824
|
-
const msg =
|
|
816
|
+
const record = entry;
|
|
817
|
+
const type = typeof record.type === "string" ? record.type : void 0;
|
|
818
|
+
const tool = typeof record.toolName === "string" ? record.toolName : void 0;
|
|
819
|
+
const msg = record.message;
|
|
825
820
|
if (typeof msg === "object" && msg !== null && !Array.isArray(msg)) {
|
|
826
821
|
const m = msg;
|
|
827
822
|
const role = typeof m.role === "string" ? m.role : "message";
|
|
@@ -833,13 +828,13 @@ var summarizeLogLine = (entry) => {
|
|
|
833
828
|
if (type) {
|
|
834
829
|
const bits = [type];
|
|
835
830
|
if (tool) bits.push(tool);
|
|
836
|
-
const model = typeof
|
|
837
|
-
const provider = typeof
|
|
831
|
+
const model = typeof record.modelId === "string" ? record.modelId : void 0;
|
|
832
|
+
const provider = typeof record.provider === "string" && !model ? record.provider : void 0;
|
|
838
833
|
if (provider) bits.push(provider);
|
|
839
834
|
if (model) bits.push(model);
|
|
840
835
|
return bits.join(" ");
|
|
841
836
|
}
|
|
842
|
-
return truncateMiddle(JSON.stringify(
|
|
837
|
+
return truncateMiddle(JSON.stringify(record), 160);
|
|
843
838
|
};
|
|
844
839
|
var resolvePrewalkModel = async (state, context) => {
|
|
845
840
|
const configured = state.config.prewalk.model?.trim();
|
|
@@ -1856,885 +1851,33 @@ var FabricSpeculationWarmup = class {
|
|
|
1856
1851
|
}
|
|
1857
1852
|
};
|
|
1858
1853
|
|
|
1859
|
-
// src/
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
const at = Date.parse(value);
|
|
1870
|
-
const normalized = value.replace("+00:00", "Z").replace(/Z$/, "");
|
|
1871
|
-
if (!Number.isFinite(at) || new Date(at).toISOString().slice(0, 19) !== normalized.slice(0, 19)) throw new Error("invalid UTC timestamp");
|
|
1872
|
-
return at;
|
|
1873
|
-
};
|
|
1874
|
-
function migrationWindow(options) {
|
|
1875
|
-
const now = options.now ?? Date.now();
|
|
1876
|
-
const since = options.since === void 0 ? now - 72 * 60 * 60 * 1e3 : utc(options.since);
|
|
1877
|
-
const until = options.until === void 0 ? now : utc(options.until);
|
|
1878
|
-
if (!Number.isFinite(since) || !Number.isFinite(until) || since > until) throw new Error("invalid migration window");
|
|
1879
|
-
return { since, until };
|
|
1880
|
-
}
|
|
1881
|
-
var positive = (value, fallback) => {
|
|
1882
|
-
const result = value ?? fallback;
|
|
1883
|
-
if (!Number.isSafeInteger(result) || result < 1) throw new Error("scan limits must be positive safe integers");
|
|
1884
|
-
return result;
|
|
1885
|
-
};
|
|
1886
|
-
function discoverMigrationSessions(options) {
|
|
1887
|
-
const maxFiles = positive(options.maxFiles, 1e4);
|
|
1888
|
-
const maxEntries = positive(options.maxDiscoveryEntries, 1e5);
|
|
1889
|
-
const files = /* @__PURE__ */ new Set();
|
|
1890
|
-
let incomplete = 0;
|
|
1891
|
-
let entries = 0;
|
|
1892
|
-
const add = (file) => {
|
|
1893
|
-
if (files.size >= maxFiles && !files.has(path3.resolve(file))) {
|
|
1894
|
-
incomplete++;
|
|
1895
|
-
return;
|
|
1896
|
-
}
|
|
1897
|
-
files.add(path3.resolve(file));
|
|
1898
|
-
};
|
|
1899
|
-
if (options.files) {
|
|
1900
|
-
for (const file of options.files.slice(0, maxFiles)) add(file);
|
|
1901
|
-
if (options.files.length > maxFiles) incomplete++;
|
|
1902
|
-
return { files: [...files].sort(), incomplete };
|
|
1903
|
-
}
|
|
1904
|
-
const list = (dir, visit) => {
|
|
1905
|
-
let handle;
|
|
1906
|
-
try {
|
|
1907
|
-
handle = fs2.opendirSync(dir);
|
|
1908
|
-
let entry;
|
|
1909
|
-
while ((entry = handle.readSync()) !== null) {
|
|
1910
|
-
if (++entries > maxEntries) {
|
|
1911
|
-
incomplete++;
|
|
1912
|
-
break;
|
|
1913
|
-
}
|
|
1914
|
-
visit(entry);
|
|
1915
|
-
}
|
|
1916
|
-
} catch {
|
|
1917
|
-
incomplete++;
|
|
1918
|
-
} finally {
|
|
1919
|
-
handle?.closeSync();
|
|
1920
|
-
}
|
|
1921
|
-
};
|
|
1922
|
-
const scan = (dir) => list(dir, (entry) => {
|
|
1923
|
-
if (entry.isFile() && entry.name.endsWith(".jsonl")) add(path3.join(dir, entry.name));
|
|
1924
|
-
else if (entry.isSymbolicLink() && entry.name.endsWith(".jsonl")) incomplete++;
|
|
1925
|
-
});
|
|
1926
|
-
if (options.candidateDirs) {
|
|
1927
|
-
for (const dir of options.candidateDirs) {
|
|
1928
|
-
if (entries >= maxEntries) {
|
|
1929
|
-
incomplete++;
|
|
1930
|
-
break;
|
|
1931
|
-
}
|
|
1932
|
-
scan(dir);
|
|
1933
|
-
}
|
|
1934
|
-
} else if (options.projectCwd) {
|
|
1935
|
-
const names = sessionDirNamesForCwd(options.projectCwd);
|
|
1936
|
-
let found = false;
|
|
1937
|
-
for (const name of [names.canonical, ...names.legacy]) {
|
|
1938
|
-
const dir = path3.join(sessionsDirRoot(options.agentDir), name);
|
|
1854
|
+
// src/compaction/lcm-loader.ts
|
|
1855
|
+
var LCM_SQLITE_NOTICE = "omp-fabric LCM compaction needs a SQLite driver, which this runtime does not provide";
|
|
1856
|
+
var createLcmRuntimeLoader = (importRuntime = () => import("./compaction/lcm-runtime.js"), loadSqlite = loadSqliteDriver) => {
|
|
1857
|
+
let unavailable = false;
|
|
1858
|
+
return {
|
|
1859
|
+
get unavailable() {
|
|
1860
|
+
return unavailable;
|
|
1861
|
+
},
|
|
1862
|
+
async load(context) {
|
|
1863
|
+
if (unavailable) return void 0;
|
|
1939
1864
|
try {
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
scan(dir);
|
|
1943
|
-
}
|
|
1865
|
+
await loadSqlite();
|
|
1866
|
+
return (await importRuntime()).LcmRuntime;
|
|
1944
1867
|
} catch (error) {
|
|
1945
|
-
|
|
1868
|
+
unavailable = true;
|
|
1869
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1870
|
+
const notice = `${LCM_SQLITE_NOTICE} (${detail}). Compaction falls back to OMP core.`;
|
|
1871
|
+
console.warn(`[omp-fabric] ${notice}`);
|
|
1872
|
+
if (context.hasUI) context.ui.notify(notice, "warning");
|
|
1873
|
+
return void 0;
|
|
1946
1874
|
}
|
|
1947
1875
|
}
|
|
1948
|
-
if (!found) incomplete++;
|
|
1949
|
-
} else {
|
|
1950
|
-
const root = sessionsDirRoot(options.agentDir);
|
|
1951
|
-
list(root, (entry) => {
|
|
1952
|
-
if (entry.isDirectory()) scan(path3.join(root, entry.name));
|
|
1953
|
-
else if (entry.isFile() && entry.name.endsWith(".jsonl")) add(path3.join(root, entry.name));
|
|
1954
|
-
else if (entry.isSymbolicLink()) incomplete++;
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
return { files: [...files].sort(), incomplete };
|
|
1958
|
-
}
|
|
1959
|
-
var CHECKPOINT_SCHEMA = "CREATE TABLE IF NOT EXISTS migration_checkpoints (project_key TEXT NOT NULL, session_path TEXT NOT NULL, source_hash TEXT NOT NULL, line_ordinal INTEGER NOT NULL, entry_id TEXT NOT NULL, content_hash TEXT NOT NULL, PRIMARY KEY(project_key,session_path,source_hash,line_ordinal,entry_id,content_hash))";
|
|
1960
|
-
function reconcileSession(options) {
|
|
1961
|
-
return migrateSessions({ ...options, onDemand: true });
|
|
1962
|
-
}
|
|
1963
|
-
function migrateSessions(options) {
|
|
1964
|
-
const window = migrationWindow(options);
|
|
1965
|
-
const maxFileBytes = positive(options.maxFileBytes, 64 * 1024 ** 2);
|
|
1966
|
-
const maxLineBytes = positive(options.maxLineBytes, 8 * 1024 ** 2);
|
|
1967
|
-
const maxTotalBytes = positive(options.maxTotalBytes, 1024 ** 3);
|
|
1968
|
-
const discovery = discoverMigrationSessions(options);
|
|
1969
|
-
const result = {
|
|
1970
|
-
mode: options.apply ? "apply" : "dry-run",
|
|
1971
|
-
since: new Date(window.since).toISOString(),
|
|
1972
|
-
until: new Date(window.until).toISOString(),
|
|
1973
|
-
counts: { eligible: 0, imported: 0, skippedDuplicate: 0, skippedOutOfWindow: 0, malformed: 0, oversized: 0, incompleteDiscovery: discovery.incomplete, errors: 0 },
|
|
1974
|
-
filesScanned: 0,
|
|
1975
|
-
bytesScanned: 0,
|
|
1976
|
-
generations: [],
|
|
1977
|
-
exitCode: 0
|
|
1978
1876
|
};
|
|
1979
|
-
const counts = result.counts;
|
|
1980
|
-
const drySeen = /* @__PURE__ */ new Set();
|
|
1981
|
-
for (const file of discovery.files) {
|
|
1982
|
-
let data;
|
|
1983
|
-
let fd;
|
|
1984
|
-
try {
|
|
1985
|
-
fd = fs2.openSync(file, "r");
|
|
1986
|
-
const before = fs2.fstatSync(fd);
|
|
1987
|
-
if (!before.isFile()) {
|
|
1988
|
-
counts.incompleteDiscovery++;
|
|
1989
|
-
continue;
|
|
1990
|
-
}
|
|
1991
|
-
if (before.size > maxFileBytes) {
|
|
1992
|
-
counts.oversized++;
|
|
1993
|
-
continue;
|
|
1994
|
-
}
|
|
1995
|
-
if (result.bytesScanned + before.size > maxTotalBytes) {
|
|
1996
|
-
counts.incompleteDiscovery++;
|
|
1997
|
-
break;
|
|
1998
|
-
}
|
|
1999
|
-
data = Buffer.allocUnsafe(before.size);
|
|
2000
|
-
let offset = 0;
|
|
2001
|
-
while (offset < data.length) {
|
|
2002
|
-
const size = fs2.readSync(fd, data, offset, data.length - offset, offset);
|
|
2003
|
-
if (size === 0) throw new Error("source shortened during read");
|
|
2004
|
-
offset += size;
|
|
2005
|
-
}
|
|
2006
|
-
const after = fs2.fstatSync(fd);
|
|
2007
|
-
if (after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) throw new Error("source changed during read");
|
|
2008
|
-
} catch {
|
|
2009
|
-
counts.errors++;
|
|
2010
|
-
counts.incompleteDiscovery++;
|
|
2011
|
-
continue;
|
|
2012
|
-
} finally {
|
|
2013
|
-
if (fd !== void 0) fs2.closeSync(fd);
|
|
2014
|
-
}
|
|
2015
|
-
result.filesScanned++;
|
|
2016
|
-
result.bytesScanned += data.length;
|
|
2017
|
-
const sourceHash = hash(data);
|
|
2018
|
-
result.generations.push({ sessionPath: file, sourceHash });
|
|
2019
|
-
let header;
|
|
2020
|
-
let ledger;
|
|
2021
|
-
let database;
|
|
2022
|
-
let sessionId = "";
|
|
2023
|
-
let projectKey = "";
|
|
2024
|
-
let cwd;
|
|
2025
|
-
let ordinal = 0;
|
|
2026
|
-
try {
|
|
2027
|
-
for (let start = 0; start < data.length; ) {
|
|
2028
|
-
ordinal++;
|
|
2029
|
-
const newline = data.indexOf(10, start);
|
|
2030
|
-
const end = newline < 0 ? data.length : newline;
|
|
2031
|
-
const bytes = data.subarray(start, end);
|
|
2032
|
-
start = newline < 0 ? data.length : end + 1;
|
|
2033
|
-
if (bytes.length > maxLineBytes) {
|
|
2034
|
-
counts.oversized++;
|
|
2035
|
-
continue;
|
|
2036
|
-
}
|
|
2037
|
-
const content = bytes.toString("utf8").replace(/\r$/, "");
|
|
2038
|
-
if (!content.trim()) continue;
|
|
2039
|
-
let row;
|
|
2040
|
-
try {
|
|
2041
|
-
row = record(JSON.parse(content));
|
|
2042
|
-
} catch {
|
|
2043
|
-
counts.malformed++;
|
|
2044
|
-
continue;
|
|
2045
|
-
}
|
|
2046
|
-
if (!row) {
|
|
2047
|
-
counts.malformed++;
|
|
2048
|
-
continue;
|
|
2049
|
-
}
|
|
2050
|
-
if (row.type === "message_end") continue;
|
|
2051
|
-
if (!header) {
|
|
2052
|
-
if (row.type !== "session") {
|
|
2053
|
-
if (record(row.message)) counts.malformed++;
|
|
2054
|
-
continue;
|
|
2055
|
-
}
|
|
2056
|
-
if (!nonblank(row.id)) {
|
|
2057
|
-
counts.malformed++;
|
|
2058
|
-
break;
|
|
2059
|
-
}
|
|
2060
|
-
header = row;
|
|
2061
|
-
sessionId = row.id;
|
|
2062
|
-
cwd = nonblank(row.cwd) ? row.cwd : options.liveCwd;
|
|
2063
|
-
if (!cwd) {
|
|
2064
|
-
counts.malformed++;
|
|
2065
|
-
break;
|
|
2066
|
-
}
|
|
2067
|
-
const projectInput = options.liveCwd ? { recordedCwd: cwd, liveCwd: options.liveCwd } : { recordedCwd: cwd };
|
|
2068
|
-
projectKey = canonicalProjectIdentity(projectInput).key;
|
|
2069
|
-
if (options.ledger && options.ledger.project.key !== projectKey) {
|
|
2070
|
-
counts.errors++;
|
|
2071
|
-
break;
|
|
2072
|
-
}
|
|
2073
|
-
if (options.apply) {
|
|
2074
|
-
if (options.ledger) ledger = options.ledger;
|
|
2075
|
-
else if (options.ledgerRoot) ledger = new LcmLedger({ rootDir: options.ledgerRoot, project: { recordedCwd: cwd } });
|
|
2076
|
-
else ledger = new LcmLedger({ project: { recordedCwd: cwd } });
|
|
2077
|
-
database = ledger.db;
|
|
2078
|
-
database.exec(CHECKPOINT_SCHEMA);
|
|
2079
|
-
} else if (options.ledger) database = options.ledger.db;
|
|
2080
|
-
else {
|
|
2081
|
-
const dbPath = defaultLedgerPath(options.ledgerRoot, projectKey);
|
|
2082
|
-
if (fs2.existsSync(dbPath)) database = new DatabaseSync(dbPath, { readOnly: true });
|
|
2083
|
-
}
|
|
2084
|
-
continue;
|
|
2085
|
-
}
|
|
2086
|
-
if (row.type === "session" || !nonblank(row.type) || !nonblank(row.id) || row.parentId !== null && row.parentId !== void 0 && typeof row.parentId !== "string") {
|
|
2087
|
-
counts.malformed++;
|
|
2088
|
-
continue;
|
|
2089
|
-
}
|
|
2090
|
-
const message = record(row.message);
|
|
2091
|
-
if (row.type === "message" && (!message || !nonblank(message.role))) {
|
|
2092
|
-
counts.malformed++;
|
|
2093
|
-
continue;
|
|
2094
|
-
}
|
|
2095
|
-
let at;
|
|
2096
|
-
try {
|
|
2097
|
-
at = typeof row.timestamp === "string" ? utc(row.timestamp) : NaN;
|
|
2098
|
-
} catch {
|
|
2099
|
-
at = NaN;
|
|
2100
|
-
}
|
|
2101
|
-
if (!Number.isFinite(at)) {
|
|
2102
|
-
counts.malformed++;
|
|
2103
|
-
continue;
|
|
2104
|
-
}
|
|
2105
|
-
if (!options.onDemand && (at < window.since || at > window.until)) {
|
|
2106
|
-
counts.skippedOutOfWindow++;
|
|
2107
|
-
continue;
|
|
2108
|
-
}
|
|
2109
|
-
counts.eligible++;
|
|
2110
|
-
const payloadJson = canonicalLcmPayload(row);
|
|
2111
|
-
const contentHash = hashLcmPayload(row);
|
|
2112
|
-
const identity = JSON.stringify([projectKey, sessionId, row.id, contentHash]);
|
|
2113
|
-
const progress = { projectKey, sessionPath: file, sourceHash, lineOrdinal: ordinal, entryId: row.id, contentHash };
|
|
2114
|
-
const duplicate = drySeen.has(identity) || database?.prepare("SELECT 1 FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND content_hash=?").get(projectKey, sessionId, row.id, contentHash) !== void 0;
|
|
2115
|
-
if (duplicate) counts.skippedDuplicate++;
|
|
2116
|
-
else if (ledger) {
|
|
2117
|
-
ledger.appendRaw({
|
|
2118
|
-
projectKey,
|
|
2119
|
-
sessionId,
|
|
2120
|
-
entryId: row.id,
|
|
2121
|
-
role: message && typeof message.role === "string" ? message.role : row.type,
|
|
2122
|
-
content,
|
|
2123
|
-
payloadJson,
|
|
2124
|
-
parentEntryId: typeof row.parentId === "string" ? row.parentId : null,
|
|
2125
|
-
branch: typeof row.branch === "string" ? row.branch : typeof row.branchId === "string" ? row.branchId : null,
|
|
2126
|
-
...cwd ? { recordedCwd: cwd } : {},
|
|
2127
|
-
createdAt: at
|
|
2128
|
-
});
|
|
2129
|
-
counts.imported++;
|
|
2130
|
-
} else if (!duplicate) counts.imported++;
|
|
2131
|
-
drySeen.add(identity);
|
|
2132
|
-
if (ledger) database.prepare("INSERT OR IGNORE INTO migration_checkpoints(project_key,session_path,source_hash,line_ordinal,entry_id,content_hash) VALUES(?,?,?,?,?,?)").run(projectKey, file, sourceHash, ordinal, row.id, contentHash);
|
|
2133
|
-
options.onProgress?.(progress);
|
|
2134
|
-
}
|
|
2135
|
-
if (!header) counts.malformed++;
|
|
2136
|
-
} catch (error) {
|
|
2137
|
-
counts.errors++;
|
|
2138
|
-
if (options.onProgress) throw error;
|
|
2139
|
-
} finally {
|
|
2140
|
-
if (ledger && ledger !== options.ledger) ledger.close();
|
|
2141
|
-
else if (!ledger && database && database !== options.ledger?.db) database.close();
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
result.exitCode = counts.errors || counts.malformed || counts.oversized || counts.incompleteDiscovery && !options.allowIncompleteDiscovery ? 1 : 0;
|
|
2145
|
-
return result;
|
|
2146
|
-
}
|
|
2147
|
-
|
|
2148
|
-
// src/compaction/lcm-maintenance.ts
|
|
2149
|
-
import crypto2 from "node:crypto";
|
|
2150
|
-
var DEFAULT_LCM_BUDGET = { calls: 32, inputTokens: 2e6, outputTokens: 128e3, cost: Number.POSITIVE_INFINITY, wallMs: 6e4, sessionCalls: 16 };
|
|
2151
|
-
var hash2 = (v) => hashLcmPayload(v);
|
|
2152
|
-
var parse = (v) => JSON.parse(String(v));
|
|
2153
|
-
var rawHash = (e) => e.payloadHash;
|
|
2154
|
-
var token = () => crypto2.randomUUID();
|
|
2155
|
-
var LcmMaintenance = class {
|
|
2156
|
-
constructor(ledger, options = {}) {
|
|
2157
|
-
this.ledger = ledger;
|
|
2158
|
-
this.projectKey = ledger.project.key;
|
|
2159
|
-
this.now = options.now ?? Date.now;
|
|
2160
|
-
this.ownerId = options.ownerId ?? crypto2.randomUUID();
|
|
2161
|
-
this.policyHash = options.policyHash ?? hash2("lcm-policy-v1");
|
|
2162
|
-
this.maxLeaf = options.maxLeafEntries ?? 8;
|
|
2163
|
-
this.maxChildren = options.maxCondenseChildren ?? 4;
|
|
2164
|
-
this.maxInputChars = options.maxInputChars ?? 48e3;
|
|
2165
|
-
this.maxOutputChars = options.maxOutputChars ?? 4096;
|
|
2166
|
-
this.budget = { ...DEFAULT_LCM_BUDGET, ...options.budget };
|
|
2167
|
-
}
|
|
2168
|
-
projectKey;
|
|
2169
|
-
now;
|
|
2170
|
-
ownerId;
|
|
2171
|
-
policyHash;
|
|
2172
|
-
maxLeaf;
|
|
2173
|
-
maxChildren;
|
|
2174
|
-
maxInputChars;
|
|
2175
|
-
maxOutputChars;
|
|
2176
|
-
budget;
|
|
2177
|
-
listNodes(limit = 100) {
|
|
2178
|
-
return this.ledger.readOnly((db) => db.prepare("SELECT payload FROM summary_nodes WHERE project_key=? ORDER BY created_at,node_id LIMIT ?").all(this.projectKey, limit).map((r) => {
|
|
2179
|
-
const node = parse(r.payload);
|
|
2180
|
-
if (node.projectKey !== this.projectKey) throw new Error("node project does not match ledger project");
|
|
2181
|
-
return node;
|
|
2182
|
-
}));
|
|
2183
|
-
}
|
|
2184
|
-
getNode(nodeId) {
|
|
2185
|
-
return this.ledger.readOnly((db) => {
|
|
2186
|
-
const row = db.prepare("SELECT payload FROM summary_nodes WHERE node_id=? AND project_key=?").get(nodeId, this.projectKey);
|
|
2187
|
-
if (!row?.payload) return void 0;
|
|
2188
|
-
const node = parse(row.payload);
|
|
2189
|
-
if (node.projectKey !== this.projectKey) throw new Error("node project does not match ledger project");
|
|
2190
|
-
return node;
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
|
-
getFrontier(sessionId, branch, activeSources) {
|
|
2194
|
-
const nodes = this.listNodes(1e5).filter((n) => n.state === "ready" && n.policyHash === this.policyHash && (!sessionId || n.sessionId === sessionId) && (branch === void 0 || n.branch === branch || n.branch === null) && (!activeSources || n.sources.every((s) => activeSources.has(this.sourceKey(s)))));
|
|
2195
|
-
const covered = new Set(nodes.flatMap((n) => n.children));
|
|
2196
|
-
return nodes.filter((n) => !covered.has(n.nodeId));
|
|
2197
|
-
}
|
|
2198
|
-
listJobs(limit = 100) {
|
|
2199
|
-
return this.ledger.readOnly((db) => db.prepare("SELECT payload FROM maintenance_jobs WHERE project_key=? ORDER BY created_at,job_id LIMIT ?").all(this.projectKey, limit).map((r) => {
|
|
2200
|
-
const job = parse(r.payload);
|
|
2201
|
-
if (job.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
|
|
2202
|
-
return job;
|
|
2203
|
-
}));
|
|
2204
|
-
}
|
|
2205
|
-
selectLeaf(entries = this.ledger.readRaw(this.projectKey), branch) {
|
|
2206
|
-
const first = entries[0];
|
|
2207
|
-
if (!first) return [];
|
|
2208
|
-
const nodes = this.listNodes(1e5).filter((n) => n.state !== "failed" && n.policyHash === this.policyHash && n.sessionId === first.sessionId && (branch === void 0 || n.branch === branch || n.branch === null));
|
|
2209
|
-
const covered = new Set(nodes.flatMap((n) => n.sources.map((s) => this.sourceKey(s))));
|
|
2210
|
-
return entries.filter((e) => e.sessionId === first.sessionId && (branch === void 0 || e.branch === branch || e.branch === null) && !covered.has(this.sourceKey(e))).slice(0, this.maxLeaf);
|
|
2211
|
-
}
|
|
2212
|
-
selectCondensation(sessionId, activeSources, branch) {
|
|
2213
|
-
const nodes = this.listNodes(1e5).filter((n) => n.state === "ready" && n.policyHash === this.policyHash && (!sessionId || n.sessionId === sessionId) && (!activeSources || n.sources.every((s) => activeSources.has(this.sourceKey(s)))) && (branch === void 0 || n.branch === branch || n.branch === null));
|
|
2214
|
-
const eligible = new Set(nodes.map((n) => n.nodeId));
|
|
2215
|
-
const edges = this.ledger.readOnly((db) => db.prepare("SELECT e.parent_id,e.child_id FROM summary_edges e JOIN summary_nodes parent ON parent.node_id=e.parent_id AND parent.project_key=? JOIN summary_nodes child ON child.node_id=e.child_id AND child.project_key=parent.project_key WHERE parent.project_key=?").all(this.projectKey, this.projectKey));
|
|
2216
|
-
const consumed = new Set(edges.filter((e) => eligible.has(e.parent_id)).map((e) => e.child_id));
|
|
2217
|
-
const candidates = nodes.filter((n) => !consumed.has(n.nodeId));
|
|
2218
|
-
const first = candidates[0];
|
|
2219
|
-
if (!first) return [];
|
|
2220
|
-
const depth = Math.min(...candidates.map((n) => n.depth));
|
|
2221
|
-
const sameDepth = candidates.filter((n) => n.depth === depth);
|
|
2222
|
-
if (depth > 0 && sameDepth.length < 2) return [];
|
|
2223
|
-
return candidates.filter((n) => n.projectKey === first.projectKey && n.sessionId === first.sessionId && n.depth === depth).sort((a, b) => a.nodeId.localeCompare(b.nodeId)).slice(0, this.maxChildren);
|
|
2224
|
-
}
|
|
2225
|
-
createLeaf(entries = this.selectLeaf()) {
|
|
2226
|
-
if (!entries.length) return void 0;
|
|
2227
|
-
const branches = new Set(entries.map((entry) => entry.branch).filter((value) => typeof value === "string"));
|
|
2228
|
-
if (branches.size > 1) throw new Error("cross-branch ranges");
|
|
2229
|
-
const branch = [...branches][0] ?? null;
|
|
2230
|
-
const source = entries.map((e, index) => {
|
|
2231
|
-
if (e.projectKey !== this.projectKey) throw new Error("entry project does not match ledger project");
|
|
2232
|
-
const payload = parse(e.payloadJson);
|
|
2233
|
-
if (typeof payload.type !== "string" || !payload.type || payload.id !== e.entryId) throw new Error("invalid raw payload");
|
|
2234
|
-
if (hash2(payload) !== rawHash(e)) throw new Error("payload hash mismatch");
|
|
2235
|
-
return { sessionId: e.sessionId, entryId: e.entryId, revision: e.revision, payloadHash: rawHash(e) };
|
|
2236
|
-
});
|
|
2237
|
-
this.validateSources(source);
|
|
2238
|
-
const node = { nodeId: `leaf:${hash2({ projectKey: this.projectKey, source, branch, policyHash: this.policyHash })}`, projectKey: this.projectKey, sessionId: entries[0].sessionId, branch, kind: "leaf", sources: source, children: [], depth: 0, sourceHash: hash2(source.map((s) => s.payloadHash)), policyHash: this.policyHash, modelHash: "", state: "pending", createdAt: this.now() };
|
|
2239
|
-
this.publish(node);
|
|
2240
|
-
return node;
|
|
2241
|
-
}
|
|
2242
|
-
createCondensed(children = this.selectCondensation()) {
|
|
2243
|
-
if (!children.length) return void 0;
|
|
2244
|
-
if (children.some((c) => c.state !== "ready")) throw new Error("condensation requires ready children");
|
|
2245
|
-
if (new Set(children.map((c) => c.sessionId)).size !== 1 || new Set(children.map((c) => c.depth)).size !== 1) throw new Error("cross-branch ranges");
|
|
2246
|
-
const storedChildren = children.map((child) => this.getNode(child.nodeId));
|
|
2247
|
-
if (storedChildren.some((child) => !child || child.projectKey !== this.projectKey)) throw new Error("child project does not match ledger project");
|
|
2248
|
-
if (storedChildren.some((child, index) => JSON.stringify(child) !== JSON.stringify(children[index]))) throw new Error("child payload changed");
|
|
2249
|
-
const concreteBranches = new Set(storedChildren.map((c) => c.branch).filter((value) => typeof value === "string"));
|
|
2250
|
-
if (concreteBranches.size > 1) throw new Error("cross-branch ranges");
|
|
2251
|
-
const branch = [...concreteBranches][0] ?? null;
|
|
2252
|
-
const source = [...new Map(storedChildren.flatMap((c) => c.sources).map((value) => [this.sourceKey(value), value])).values()];
|
|
2253
|
-
this.validateSources(source);
|
|
2254
|
-
const node = { nodeId: `condensed:${hash2({ projectKey: this.projectKey, children: storedChildren.map((c) => c.nodeId), branch, policyHash: this.policyHash })}`, projectKey: this.projectKey, sessionId: children[0].sessionId, branch, kind: "condensed", sources: source, children: children.map((c) => c.nodeId), depth: Math.max(...children.map((c) => c.depth)) + 1, sourceHash: hash2(children.map((c) => c.sourceHash)), policyHash: this.policyHash, modelHash: "", state: "pending", createdAt: this.now() };
|
|
2255
|
-
this.publish(node);
|
|
2256
|
-
return node;
|
|
2257
|
-
}
|
|
2258
|
-
validateSources(source) {
|
|
2259
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2260
|
-
for (const s of source) {
|
|
2261
|
-
if (seen.has(this.sourceKey(s))) throw new Error("duplicate source identity");
|
|
2262
|
-
seen.add(this.sourceKey(s));
|
|
2263
|
-
}
|
|
2264
|
-
if (new Set(source.map((s) => s.sessionId)).size !== 1) throw new Error("cross-session ranges");
|
|
2265
|
-
}
|
|
2266
|
-
sourceKey(source) {
|
|
2267
|
-
return `${source.sessionId}:${source.entryId}:${source.revision}`;
|
|
2268
|
-
}
|
|
2269
|
-
publish(node) {
|
|
2270
|
-
this.ledger.transaction((db) => {
|
|
2271
|
-
if (node.children.includes(node.nodeId)) throw new Error("cycle");
|
|
2272
|
-
for (const child of node.children) if (!db.prepare("SELECT 1 FROM summary_nodes WHERE node_id=? AND project_key=?").get(child, this.projectKey)) throw new Error("missing child");
|
|
2273
|
-
db.prepare("INSERT OR IGNORE INTO summary_nodes(node_id,project_key,payload,created_at) VALUES(?,?,?,?)").run(node.nodeId, this.projectKey, JSON.stringify(node), node.createdAt);
|
|
2274
|
-
for (const child of node.children) db.prepare("INSERT OR IGNORE INTO summary_edges(parent_id,child_id) VALUES(?,?)").run(node.nodeId, child);
|
|
2275
|
-
const job = this.job(node);
|
|
2276
|
-
db.prepare("INSERT OR IGNORE INTO maintenance_jobs(job_id,project_key,status,payload,created_at,updated_at) VALUES(?,?,?,?,?,?)").run(job.jobId, this.projectKey, job.state, JSON.stringify(job), job.createdAt, job.updatedAt);
|
|
2277
|
-
});
|
|
2278
|
-
}
|
|
2279
|
-
job(node) {
|
|
2280
|
-
const t = this.now();
|
|
2281
|
-
return { jobId: `job:${node.nodeId}`, projectKey: this.projectKey, nodeId: node.nodeId, priority: node.kind === "condensed" ? 20 : 10, eligibleAt: t, state: "pending", attempts: 0, nextRetryAt: t, createdAt: t, updatedAt: t };
|
|
2282
|
-
}
|
|
2283
|
-
claim(jobId, ownerId = this.ownerId) {
|
|
2284
|
-
return this.ledger.transaction((db) => {
|
|
2285
|
-
const row = db.prepare("SELECT payload FROM maintenance_jobs WHERE job_id=? AND project_key=?").get(jobId, this.projectKey);
|
|
2286
|
-
if (!row?.payload) throw new Error("job not found");
|
|
2287
|
-
const old = parse(row.payload);
|
|
2288
|
-
if (old.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
|
|
2289
|
-
const t = this.now();
|
|
2290
|
-
const expired = old.state === "running" && (old.leaseUntil ?? 0) <= t;
|
|
2291
|
-
if (old.state === "running" && !expired) throw new Error("job lease held");
|
|
2292
|
-
if (old.state !== "pending" && !expired || old.eligibleAt > t || old.nextRetryAt > t) throw new Error("job not eligible");
|
|
2293
|
-
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);
|
|
2294
|
-
if (active.length) throw new Error("project lease held");
|
|
2295
|
-
const nodeRow = db.prepare("SELECT payload FROM summary_nodes WHERE node_id=? AND project_key=?").get(old.nodeId, this.projectKey);
|
|
2296
|
-
const sessionId = nodeRow?.payload ? parse(nodeRow.payload).sessionId : "";
|
|
2297
|
-
const day = this.day(t);
|
|
2298
|
-
const projectUsage = this.usage(db, day);
|
|
2299
|
-
const sessionUsage = this.usage(db, day, sessionId);
|
|
2300
|
-
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");
|
|
2301
|
-
const out = { ...old, state: "running", ownerId, leaseToken: token(), leaseUntil: t + 3e4, updatedAt: t };
|
|
2302
|
-
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);
|
|
2303
|
-
return out;
|
|
2304
|
-
});
|
|
2305
|
-
}
|
|
2306
|
-
claimEmergency(jobId, ownerId = this.ownerId) {
|
|
2307
|
-
return this.ledger.transaction((db) => {
|
|
2308
|
-
const row = db.prepare("SELECT payload FROM maintenance_jobs WHERE job_id=? AND project_key=?").get(jobId, this.projectKey);
|
|
2309
|
-
if (!row?.payload) throw new Error("job not found");
|
|
2310
|
-
const old = parse(row.payload);
|
|
2311
|
-
if (old.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
|
|
2312
|
-
const t = this.now();
|
|
2313
|
-
const expired = old.state === "running" && (old.leaseUntil ?? 0) <= t;
|
|
2314
|
-
if (old.state === "running" && !expired) throw new Error("job lease held");
|
|
2315
|
-
if (old.state === "completed") throw new Error("job already completed");
|
|
2316
|
-
const out = { ...old, state: "running", ownerId, leaseToken: token(), leaseUntil: t + 3e4, updatedAt: t };
|
|
2317
|
-
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);
|
|
2318
|
-
return out;
|
|
2319
|
-
});
|
|
2320
|
-
}
|
|
2321
|
-
renew(job) {
|
|
2322
|
-
return this.fenced(job, (old) => ({ ...old, leaseUntil: this.now() + 3e4, updatedAt: this.now() }));
|
|
2323
|
-
}
|
|
2324
|
-
validateText(text) {
|
|
2325
|
-
if (typeof text !== "string" || text.length === 0) throw new Error("invalid summary text");
|
|
2326
|
-
let length = 0;
|
|
2327
|
-
for (const _ of text) {
|
|
2328
|
-
length += 1;
|
|
2329
|
-
if (length > this.maxOutputChars) throw new Error("summary exceeds output bound");
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
complete(job, result) {
|
|
2333
|
-
this.validateText(result.text);
|
|
2334
|
-
return this.ledger.transaction((db) => {
|
|
2335
|
-
const current = this.readJob(db, job.jobId);
|
|
2336
|
-
this.assertLease(current, job);
|
|
2337
|
-
if (current.nodeId !== job.nodeId) throw new Error("job node mismatch");
|
|
2338
|
-
const row = db.prepare("SELECT payload FROM summary_nodes WHERE node_id=? AND project_key=?").get(job.nodeId, this.projectKey);
|
|
2339
|
-
if (!row?.payload) throw new Error("node not found");
|
|
2340
|
-
const node = parse(row.payload);
|
|
2341
|
-
if (node.projectKey !== this.projectKey || node.nodeId !== current.nodeId) throw new Error("node project does not match ledger project");
|
|
2342
|
-
for (const source of node.sources) {
|
|
2343
|
-
const raw = db.prepare("SELECT payload_json,session_id FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND revision=?").get(this.projectKey, source.sessionId, source.entryId, source.revision);
|
|
2344
|
-
if (!raw?.payload_json || hash2(parse(raw.payload_json)) !== source.payloadHash || raw.session_id !== source.sessionId) throw new Error("stale source");
|
|
2345
|
-
}
|
|
2346
|
-
if (![result.inputTokens, result.outputTokens, result.cost, result.wallMs].every((value) => Number.isFinite(value) && value >= 0)) throw new Error("invalid model usage");
|
|
2347
|
-
const projectUsage = this.usage(db, this.day(this.now()));
|
|
2348
|
-
const sessionUsage = this.usage(db, this.day(this.now()), node.sessionId);
|
|
2349
|
-
if (projectUsage.calls + 1 > this.budget.calls || projectUsage.inputTokens + result.inputTokens > this.budget.inputTokens || projectUsage.outputTokens + result.outputTokens > this.budget.outputTokens || projectUsage.cost + result.cost > this.budget.cost || projectUsage.wallMs + result.wallMs > this.budget.wallMs || sessionUsage.calls + 1 > this.budget.sessionCalls) throw new Error("budget exhausted");
|
|
2350
|
-
const out = { ...node, state: "ready", text: result.text, modelHash: result.modelHash };
|
|
2351
|
-
this.account(db, node.sessionId, result);
|
|
2352
|
-
db.prepare("UPDATE summary_nodes SET payload=? WHERE node_id=? AND project_key=?").run(JSON.stringify(out), node.nodeId, this.projectKey);
|
|
2353
|
-
const { ownerId: _ownerId, leaseToken: _leaseToken, leaseUntil: _leaseUntil, ...base } = current;
|
|
2354
|
-
const done = { ...base, state: "completed", updatedAt: this.now() };
|
|
2355
|
-
db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(done.state, JSON.stringify(done), done.updatedAt, done.jobId, this.projectKey);
|
|
2356
|
-
return out;
|
|
2357
|
-
});
|
|
2358
|
-
}
|
|
2359
|
-
completeEmergency(job, text) {
|
|
2360
|
-
this.validateText(text);
|
|
2361
|
-
return this.ledger.transaction((db) => {
|
|
2362
|
-
const current = this.readJob(db, job.jobId);
|
|
2363
|
-
this.assertLease(current, job);
|
|
2364
|
-
if (current.nodeId !== job.nodeId) throw new Error("job node mismatch");
|
|
2365
|
-
const row = db.prepare("SELECT payload FROM summary_nodes WHERE node_id=? AND project_key=?").get(job.nodeId, this.projectKey);
|
|
2366
|
-
if (!row?.payload) throw new Error("node not found");
|
|
2367
|
-
const node = parse(row.payload);
|
|
2368
|
-
if (node.projectKey !== this.projectKey || node.nodeId !== current.nodeId) throw new Error("node project does not match ledger project");
|
|
2369
|
-
for (const source of node.sources) {
|
|
2370
|
-
const raw = db.prepare("SELECT payload_json,session_id FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND revision=?").get(this.projectKey, source.sessionId, source.entryId, source.revision);
|
|
2371
|
-
if (!raw?.payload_json || hash2(parse(raw.payload_json)) !== source.payloadHash || raw.session_id !== source.sessionId) throw new Error("stale source");
|
|
2372
|
-
}
|
|
2373
|
-
const out = { ...node, state: "ready", text, modelHash: "emergency" };
|
|
2374
|
-
db.prepare("UPDATE summary_nodes SET payload=? WHERE node_id=? AND project_key=?").run(JSON.stringify(out), node.nodeId, this.projectKey);
|
|
2375
|
-
const { ownerId: _ownerId, leaseToken: _leaseToken, leaseUntil: _leaseUntil, ...base } = current;
|
|
2376
|
-
const done = { ...base, state: "completed", updatedAt: this.now() };
|
|
2377
|
-
db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(done.state, JSON.stringify(done), done.updatedAt, done.jobId, this.projectKey);
|
|
2378
|
-
return out;
|
|
2379
|
-
});
|
|
2380
|
-
}
|
|
2381
|
-
fail(job, error) {
|
|
2382
|
-
return this.fenced(job, (old) => {
|
|
2383
|
-
const attempts = old.attempts + 1;
|
|
2384
|
-
const delay = Math.min(9e5, 3e4 * 2 ** (attempts - 1));
|
|
2385
|
-
const { ownerId: _ownerId, leaseToken: _leaseToken, leaseUntil: _leaseUntil, ...base } = old;
|
|
2386
|
-
return { ...base, attempts, error, state: attempts >= 3 ? "failed" : "pending", eligibleAt: this.now() + delay, nextRetryAt: this.now() + delay, updatedAt: this.now() };
|
|
2387
|
-
});
|
|
2388
|
-
}
|
|
2389
|
-
async run(job, model, input, signal = new AbortController().signal) {
|
|
2390
|
-
let sources = [];
|
|
2391
|
-
let claimed;
|
|
2392
|
-
const controller = new AbortController();
|
|
2393
|
-
let renew;
|
|
2394
|
-
const timer = setTimeout(() => controller.abort(), 6e4);
|
|
2395
|
-
const abort = () => controller.abort();
|
|
2396
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
2397
|
-
try {
|
|
2398
|
-
claimed = this.claim(job.jobId, job.ownerId ?? this.ownerId);
|
|
2399
|
-
const node = this.getNode(claimed.nodeId);
|
|
2400
|
-
sources = node?.sources ?? [];
|
|
2401
|
-
renew = setInterval(() => {
|
|
2402
|
-
try {
|
|
2403
|
-
if (claimed) claimed = this.renew(claimed);
|
|
2404
|
-
} catch {
|
|
2405
|
-
}
|
|
2406
|
-
}, 1e4);
|
|
2407
|
-
const result = await Promise.race([model.generate({ prompt: buildLcmPrompt(node?.kind ?? "leaf", input, this.maxInputChars), sessionId: node?.sessionId ?? "", signal: controller.signal }), new Promise((_, reject) => controller.signal.addEventListener("abort", () => reject(new Error("LCM timeout")), { once: true }))]);
|
|
2408
|
-
return this.complete(claimed, result);
|
|
2409
|
-
} catch (error) {
|
|
2410
|
-
if (!claimed) throw error;
|
|
2411
|
-
const fallback = emergencyReduce(input, this.maxOutputChars, sources);
|
|
2412
|
-
try {
|
|
2413
|
-
return this.completeEmergency(claimed, fallback);
|
|
2414
|
-
} catch {
|
|
2415
|
-
try {
|
|
2416
|
-
this.fail(claimed, String(error));
|
|
2417
|
-
} catch {
|
|
2418
|
-
}
|
|
2419
|
-
throw error;
|
|
2420
|
-
}
|
|
2421
|
-
} finally {
|
|
2422
|
-
clearTimeout(timer);
|
|
2423
|
-
if (renew) clearInterval(renew);
|
|
2424
|
-
signal.removeEventListener("abort", abort);
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
day(ms) {
|
|
2428
|
-
return new Date(ms).toISOString().slice(0, 10);
|
|
2429
|
-
}
|
|
2430
|
-
usage(db, day, sessionId) {
|
|
2431
|
-
const q = sessionId === void 0 ? "SELECT COALESCE(SUM(calls),0) calls,COALESCE(SUM(input_tokens),0) inputTokens,COALESCE(SUM(output_tokens),0) outputTokens,COALESCE(SUM(cost),0) cost,COALESCE(SUM(wall_ms),0) wallMs FROM maintenance_usage WHERE project_key=? AND day=?" : "SELECT COALESCE(SUM(calls),0) calls,COALESCE(SUM(input_tokens),0) inputTokens,COALESCE(SUM(output_tokens),0) outputTokens,COALESCE(SUM(cost),0) cost,COALESCE(SUM(wall_ms),0) wallMs FROM maintenance_usage WHERE project_key=? AND day=? AND session_id=?";
|
|
2432
|
-
const r = sessionId === void 0 ? db.prepare(q).get(this.projectKey, day) : db.prepare(q).get(this.projectKey, day, sessionId);
|
|
2433
|
-
return r;
|
|
2434
|
-
}
|
|
2435
|
-
account(db, sessionId, result) {
|
|
2436
|
-
const d = this.day(this.now());
|
|
2437
|
-
db.prepare("INSERT INTO maintenance_usage(project_key,day,session_id,calls,input_tokens,output_tokens,cost,wall_ms) VALUES(?,?,?,?,?,?,?,?) ON CONFLICT(project_key,day,session_id) DO UPDATE SET calls=calls+excluded.calls,input_tokens=input_tokens+excluded.input_tokens,output_tokens=output_tokens+excluded.output_tokens,cost=cost+excluded.cost,wall_ms=wall_ms+excluded.wall_ms").run(this.projectKey, d, sessionId, 1, result.inputTokens, result.outputTokens, result.cost, result.wallMs);
|
|
2438
|
-
}
|
|
2439
|
-
readJob(db, id) {
|
|
2440
|
-
const row = db.prepare("SELECT payload FROM maintenance_jobs WHERE job_id=? AND project_key=?").get(id, this.projectKey);
|
|
2441
|
-
if (!row) throw new Error("job not found");
|
|
2442
|
-
const job = parse(row.payload);
|
|
2443
|
-
if (job.projectKey !== this.projectKey) throw new Error("job project does not match ledger project");
|
|
2444
|
-
return job;
|
|
2445
|
-
}
|
|
2446
|
-
assertLease(current, expected) {
|
|
2447
|
-
if (current.ownerId !== expected.ownerId || current.leaseToken !== expected.leaseToken || current.state !== "running" || (current.leaseUntil ?? 0) < this.now()) throw new Error("lease fenced");
|
|
2448
|
-
}
|
|
2449
|
-
fenced(job, update) {
|
|
2450
|
-
return this.ledger.transaction((db) => {
|
|
2451
|
-
const old = this.readJob(db, job.jobId);
|
|
2452
|
-
this.assertLease(old, job);
|
|
2453
|
-
const out = update(old);
|
|
2454
|
-
db.prepare("UPDATE maintenance_jobs SET status=?,payload=?,updated_at=? WHERE job_id=? AND project_key=?").run(out.state, JSON.stringify(out), out.updatedAt, out.jobId, this.projectKey);
|
|
2455
|
-
return out;
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
};
|
|
2459
|
-
|
|
2460
|
-
// src/compaction/lcm-runtime.ts
|
|
2461
|
-
var LcmRuntime = class {
|
|
2462
|
-
ledger;
|
|
2463
|
-
maintenance;
|
|
2464
|
-
context;
|
|
2465
|
-
resolveOptions;
|
|
2466
|
-
abort = new AbortController();
|
|
2467
|
-
writePending = Promise.resolve();
|
|
2468
|
-
maintenancePending = Promise.resolve();
|
|
2469
|
-
closed = false;
|
|
2470
|
-
dirty = false;
|
|
2471
|
-
degradedError;
|
|
2472
|
-
activeSources = /* @__PURE__ */ new Set();
|
|
2473
|
-
activeSessionId;
|
|
2474
|
-
constructor(context, options = {}) {
|
|
2475
|
-
this.context = context;
|
|
2476
|
-
this.resolveOptions = typeof options === "function" ? options : () => options;
|
|
2477
|
-
const initial = this.resolveOptions();
|
|
2478
|
-
const recorded = context.sessionManager.getRecordedCwd?.();
|
|
2479
|
-
const liveCwd = recorded || context.cwd;
|
|
2480
|
-
const project = canonicalProjectIdentity({ liveCwd });
|
|
2481
|
-
const ledgerOptions = initial.rootDir === void 0 ? { project: { liveCwd: project.canonicalPath ?? liveCwd } } : { rootDir: initial.rootDir, project: { liveCwd: project.canonicalPath ?? liveCwd } };
|
|
2482
|
-
this.ledger = new LcmLedger(ledgerOptions);
|
|
2483
|
-
this.maintenance = new LcmMaintenance(this.ledger, {
|
|
2484
|
-
...initial,
|
|
2485
|
-
...initial.lcmMaxInputChars === void 0 ? {} : { maxInputChars: initial.lcmMaxInputChars },
|
|
2486
|
-
...initial.lcmMaxOutputChars === void 0 ? {} : { maxOutputChars: initial.lcmMaxOutputChars }
|
|
2487
|
-
});
|
|
2488
|
-
}
|
|
2489
|
-
get options() {
|
|
2490
|
-
return this.resolveOptions();
|
|
2491
|
-
}
|
|
2492
|
-
get projectKey() {
|
|
2493
|
-
return this.ledger.project.key;
|
|
2494
|
-
}
|
|
2495
|
-
get signal() {
|
|
2496
|
-
return this.abort.signal;
|
|
2497
|
-
}
|
|
2498
|
-
get status() {
|
|
2499
|
-
return this.degradedError === void 0 ? "healthy" : "degraded";
|
|
2500
|
-
}
|
|
2501
|
-
get error() {
|
|
2502
|
-
return this.degradedError;
|
|
2503
|
-
}
|
|
2504
|
-
markDirty() {
|
|
2505
|
-
this.dirty = true;
|
|
2506
|
-
}
|
|
2507
|
-
enqueueWrite(operation) {
|
|
2508
|
-
const run = this.writePending.then(operation);
|
|
2509
|
-
this.writePending = run.catch((error) => {
|
|
2510
|
-
this.degradedError = error;
|
|
2511
|
-
});
|
|
2512
|
-
return run;
|
|
2513
|
-
}
|
|
2514
|
-
refreshActiveSources(sessionId, entries) {
|
|
2515
|
-
const ids = new Set(entries.map((entry) => entry.id));
|
|
2516
|
-
const latest = /* @__PURE__ */ new Map();
|
|
2517
|
-
for (const entry of this.ledger.readRaw(this.projectKey, sessionId)) {
|
|
2518
|
-
if (!ids.has(entry.entryId)) continue;
|
|
2519
|
-
const previous = latest.get(entry.entryId);
|
|
2520
|
-
if (!previous || previous.revision < entry.revision) latest.set(entry.entryId, entry);
|
|
2521
|
-
}
|
|
2522
|
-
this.activeSources = new Set(
|
|
2523
|
-
[...latest.values()].map((entry) => `${entry.sessionId}:${entry.entryId}:${entry.revision}`)
|
|
2524
|
-
);
|
|
2525
|
-
this.activeSessionId = sessionId;
|
|
2526
|
-
}
|
|
2527
|
-
reconcileSelectedSession() {
|
|
2528
|
-
if (this.closed) return Promise.resolve();
|
|
2529
|
-
return this.enqueueWrite(() => {
|
|
2530
|
-
const getSessionFile = this.context.sessionManager.getSessionFile;
|
|
2531
|
-
if (typeof getSessionFile !== "function") return;
|
|
2532
|
-
const sessionFile = getSessionFile.call(this.context.sessionManager);
|
|
2533
|
-
if (!sessionFile) return;
|
|
2534
|
-
const recorded = this.context.sessionManager.getRecordedCwd?.();
|
|
2535
|
-
const cwd = recorded || this.context.cwd;
|
|
2536
|
-
const result = reconcileSession({
|
|
2537
|
-
agentDir: this.options.rootDir ?? process.env.PI_CODING_AGENT_DIR ?? `${process.env.HOME ?? "."}/.omp/agent`,
|
|
2538
|
-
ledger: this.ledger,
|
|
2539
|
-
files: [sessionFile],
|
|
2540
|
-
liveCwd: cwd,
|
|
2541
|
-
projectCwd: cwd,
|
|
2542
|
-
apply: true
|
|
2543
|
-
});
|
|
2544
|
-
this.degradedError = result.counts.errors > 0 ? new Error(`LCM session reconciliation reported ${result.counts.errors} error(s)`) : void 0;
|
|
2545
|
-
const sessionId = this.context.sessionManager.getSessionId();
|
|
2546
|
-
this.refreshActiveSources(sessionId, this.context.sessionManager.getBranch());
|
|
2547
|
-
});
|
|
2548
|
-
}
|
|
2549
|
-
readback() {
|
|
2550
|
-
if (this.closed) return Promise.resolve();
|
|
2551
|
-
return this.enqueueWrite(() => {
|
|
2552
|
-
if (this.closed) return;
|
|
2553
|
-
const entries = this.context.sessionManager.getBranch();
|
|
2554
|
-
const sessionId = this.context.sessionManager.getSessionId();
|
|
2555
|
-
const branch = this.context.sessionManager.getLeafId();
|
|
2556
|
-
const recorded = this.context.sessionManager.getRecordedCwd?.();
|
|
2557
|
-
const active = /* @__PURE__ */ new Set();
|
|
2558
|
-
for (const entry of entries) {
|
|
2559
|
-
const payloadJson = canonicalLcmPayload(entry);
|
|
2560
|
-
const message = entry.type === "message" ? entry.message : void 0;
|
|
2561
|
-
const stored = this.ledger.appendRaw({
|
|
2562
|
-
projectKey: this.projectKey,
|
|
2563
|
-
sessionId,
|
|
2564
|
-
entryId: entry.id,
|
|
2565
|
-
role: message?.role ?? entry.type,
|
|
2566
|
-
content: message ? JSON.stringify(message.content ?? "") : payloadJson,
|
|
2567
|
-
payloadJson,
|
|
2568
|
-
parentEntryId: entry.parentId ?? null,
|
|
2569
|
-
branch,
|
|
2570
|
-
...recorded || this.context.cwd ? { recordedCwd: recorded || this.context.cwd } : {}
|
|
2571
|
-
});
|
|
2572
|
-
active.add(`${sessionId}:${stored.entryId}:${stored.revision}`);
|
|
2573
|
-
}
|
|
2574
|
-
this.activeSources = active;
|
|
2575
|
-
this.activeSessionId = sessionId;
|
|
2576
|
-
this.dirty = false;
|
|
2577
|
-
this.degradedError = void 0;
|
|
2578
|
-
});
|
|
2579
|
-
}
|
|
2580
|
-
async syncAndSchedule() {
|
|
2581
|
-
if (this.closed) return;
|
|
2582
|
-
await this.readback();
|
|
2583
|
-
this.scheduleMaintenance();
|
|
2584
|
-
}
|
|
2585
|
-
maintain() {
|
|
2586
|
-
return this.syncAndSchedule();
|
|
2587
|
-
}
|
|
2588
|
-
scheduleMaintenance() {
|
|
2589
|
-
if (this.closed) return;
|
|
2590
|
-
const run = this.maintenancePending.then(() => this.runMaintenance());
|
|
2591
|
-
this.maintenancePending = run.catch((error) => {
|
|
2592
|
-
this.degradedError = error;
|
|
2593
|
-
});
|
|
2594
|
-
}
|
|
2595
|
-
async runMaintenance() {
|
|
2596
|
-
if (this.closed || !this.activeSessionId || this.activeSources.size === 0) return;
|
|
2597
|
-
let model;
|
|
2598
|
-
try {
|
|
2599
|
-
model = new LcmModelAdapter(this.context, this.options.summaryModel, true, {
|
|
2600
|
-
...this.options.lcmMaxInputChars === void 0 ? {} : { maxInputChars: this.options.lcmMaxInputChars },
|
|
2601
|
-
...this.options.lcmMaxOutputTokens === void 0 ? {} : { maxOutputTokens: this.options.lcmMaxOutputTokens },
|
|
2602
|
-
...this.options.lcmMaxOutputChars === void 0 ? {} : { maxOutputChars: this.options.lcmMaxOutputChars }
|
|
2603
|
-
});
|
|
2604
|
-
} catch (error) {
|
|
2605
|
-
this.degradedError = error;
|
|
2606
|
-
model = { modelHash: "unavailable", generate: async () => {
|
|
2607
|
-
throw error instanceof Error ? error : new Error(String(error));
|
|
2608
|
-
} };
|
|
2609
|
-
}
|
|
2610
|
-
const budget = Math.max(1, this.options.maxCondenseChildren ?? 4);
|
|
2611
|
-
const sessionId = this.activeSessionId;
|
|
2612
|
-
const raw = this.ledger.readRaw(this.projectKey, sessionId).filter(
|
|
2613
|
-
(entry) => this.activeSources.has(`${entry.sessionId}:${entry.entryId}:${entry.revision}`)
|
|
2614
|
-
);
|
|
2615
|
-
const inputFor = (node) => {
|
|
2616
|
-
if (node.kind === "condensed") return node.children.map((id) => this.maintenance.getNode(id)?.text ?? "").filter(Boolean).join("\n\n");
|
|
2617
|
-
return node.sources.map((source) => {
|
|
2618
|
-
const entry = this.ledger.readRawEntry(this.projectKey, source.sessionId, source.entryId, source.revision);
|
|
2619
|
-
if (!entry || entry.payloadHash !== source.payloadHash) throw new Error("stale source");
|
|
2620
|
-
return entry.payloadJson;
|
|
2621
|
-
}).join("\n");
|
|
2622
|
-
};
|
|
2623
|
-
const runJob = async (job, node) => {
|
|
2624
|
-
try {
|
|
2625
|
-
await this.maintenance.run(job, model, inputFor(node), this.signal);
|
|
2626
|
-
} catch {
|
|
2627
|
-
}
|
|
2628
|
-
};
|
|
2629
|
-
for (let pass = 0; pass < budget && !this.closed; pass += 1) {
|
|
2630
|
-
const leafCandidate = this.maintenance.createLeaf(this.maintenance.selectLeaf(raw, this.context.sessionManager.getLeafId()));
|
|
2631
|
-
const leaf = leafCandidate ? this.maintenance.getNode(leafCandidate.nodeId) : void 0;
|
|
2632
|
-
if (leaf?.state === "pending") {
|
|
2633
|
-
const job = this.maintenance.listJobs().find((item) => item.nodeId === leaf.nodeId);
|
|
2634
|
-
if (job) await runJob(job, leaf);
|
|
2635
|
-
}
|
|
2636
|
-
const branch = this.context.sessionManager.getLeafId();
|
|
2637
|
-
const pending = this.maintenance.listJobs().filter((job) => job.state === "pending").flatMap((job) => {
|
|
2638
|
-
const node = this.maintenance.getNode(job.nodeId);
|
|
2639
|
-
if (!node || node.sessionId !== sessionId || branch !== null && node.branch !== branch && node.branch !== null || !node.sources.every((source) => this.activeSources.has(`${source.sessionId}:${source.entryId}:${source.revision}`))) return [];
|
|
2640
|
-
return [{ job, node }];
|
|
2641
|
-
}).slice(0, budget);
|
|
2642
|
-
for (const item of pending) await runJob(item.job, item.node);
|
|
2643
|
-
const children = this.maintenance.selectCondensation(sessionId, this.activeSources, this.context.sessionManager.getLeafId());
|
|
2644
|
-
if (children.length >= budget) {
|
|
2645
|
-
const node = this.maintenance.createCondensed(children);
|
|
2646
|
-
if (node) {
|
|
2647
|
-
const job = this.maintenance.listJobs().find((item) => item.nodeId === node.nodeId);
|
|
2648
|
-
if (job) await runJob(job, node);
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
if (!leaf && pending.length === 0 && children.length < budget) break;
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
compact(input) {
|
|
2655
|
-
const persistedEntries = /* @__PURE__ */ new Map();
|
|
2656
|
-
for (const entry of input.branchEntries) {
|
|
2657
|
-
const message = entry.type === "message" ? entry.message : void 0;
|
|
2658
|
-
const payloadJson = canonicalLcmPayload(entry);
|
|
2659
|
-
const stored = this.ledger.appendRaw({
|
|
2660
|
-
projectKey: this.projectKey,
|
|
2661
|
-
sessionId: input.sessionId,
|
|
2662
|
-
entryId: entry.id,
|
|
2663
|
-
role: message?.role ?? entry.type,
|
|
2664
|
-
content: message ? JSON.stringify(message.content ?? "") : payloadJson,
|
|
2665
|
-
payloadJson,
|
|
2666
|
-
parentEntryId: entry.parentId ?? null,
|
|
2667
|
-
branch: input.branch
|
|
2668
|
-
});
|
|
2669
|
-
persistedEntries.set(`${entry.id}:${stored.payloadHash}`, stored);
|
|
2670
|
-
}
|
|
2671
|
-
const firstKeptIndex = input.firstKeptEntryId ? input.branchEntries.findIndex((entry) => entry.id === input.firstKeptEntryId) : input.branchEntries.length;
|
|
2672
|
-
const sourceEntries = input.branchEntries.slice(0, firstKeptIndex < 0 ? input.branchEntries.length : firstKeptIndex);
|
|
2673
|
-
const selectedStored = sourceEntries.map((entry) => {
|
|
2674
|
-
const payloadHash = hashLcmPayload(JSON.parse(canonicalLcmPayload(entry)));
|
|
2675
|
-
const stored = persistedEntries.get(`${entry.id}:${payloadHash}`);
|
|
2676
|
-
if (!stored) throw new Error("compaction source was not persisted");
|
|
2677
|
-
return stored;
|
|
2678
|
-
});
|
|
2679
|
-
const activeSources = new Set(selectedStored.map((entry) => `${entry.sessionId}:${entry.entryId}:${entry.revision}`));
|
|
2680
|
-
const frontier = this.maintenance.getFrontier(input.sessionId, input.branch, activeSources);
|
|
2681
|
-
const coveredSources = new Set(frontier.flatMap((node) => node.sources.map((source) => `${source.sessionId}:${source.entryId}:${source.revision}`)));
|
|
2682
|
-
const summary = frontier.map((node) => node.text ?? "").filter(Boolean).join("\n\n");
|
|
2683
|
-
if (summary && selectedStored.every((entry) => coveredSources.has(`${entry.sessionId}:${entry.entryId}:${entry.revision}`))) return { summary, firstKeptEntryId: input.firstKeptEntryId, tokensBefore: input.tokensBefore, source: "ready-frontier", branch: input.branch };
|
|
2684
|
-
if (selectedStored.length === 0) throw new Error("LCM emergency fallback has no persisted sources");
|
|
2685
|
-
const payloads = sourceEntries.map((entry) => canonicalLcmPayload(entry)).join("\n");
|
|
2686
|
-
const sources = selectedStored.map((stored) => ({ sessionId: stored.sessionId, entryId: stored.entryId, revision: stored.revision, payloadHash: stored.payloadHash }));
|
|
2687
|
-
const fallback = emergencyReduce(payloads, this.options.lcmMaxOutputChars ?? 4096, sources);
|
|
2688
|
-
const leaf = this.maintenance.createLeaf(selectedStored);
|
|
2689
|
-
if (!leaf) throw new Error("LCM emergency fallback node was not created");
|
|
2690
|
-
const persisted = this.maintenance.getNode(leaf.nodeId);
|
|
2691
|
-
if (persisted?.state === "ready") {
|
|
2692
|
-
if (persisted.branch !== input.branch || JSON.stringify(persisted.sources) !== JSON.stringify(sources)) throw new Error("LCM emergency fallback provenance mismatch");
|
|
2693
|
-
if (!persisted.text) throw new Error("LCM emergency fallback text is missing");
|
|
2694
|
-
return { summary: persisted.text, firstKeptEntryId: input.firstKeptEntryId, tokensBefore: input.tokensBefore, source: "emergency", branch: input.branch };
|
|
2695
|
-
}
|
|
2696
|
-
const job = this.maintenance.listJobs().find((item) => item.nodeId === leaf.nodeId);
|
|
2697
|
-
if (!job) throw new Error("LCM emergency fallback job was not created");
|
|
2698
|
-
const completed = this.maintenance.completeEmergency(this.maintenance.claimEmergency(job.jobId), fallback);
|
|
2699
|
-
return { summary: completed.text ?? fallback, firstKeptEntryId: input.firstKeptEntryId, tokensBefore: input.tokensBefore, source: "emergency", branch: input.branch };
|
|
2700
|
-
}
|
|
2701
|
-
memoryContext() {
|
|
2702
|
-
return {
|
|
2703
|
-
ledger: {
|
|
2704
|
-
projectKey: this.projectKey,
|
|
2705
|
-
readRaw: (sessionId) => this.ledger.readRaw(this.projectKey, sessionId),
|
|
2706
|
-
readRawPage: (sessionId, offset, limit) => this.ledger.readRawPage(this.projectKey, sessionId, offset, limit),
|
|
2707
|
-
readRawEntry: (sessionId, entryId, revision) => this.ledger.readRawEntry(this.projectKey, sessionId, entryId, revision)
|
|
2708
|
-
},
|
|
2709
|
-
...this.activeSessionId === void 0 ? {} : { currentSessionId: this.activeSessionId },
|
|
2710
|
-
summaries: {
|
|
2711
|
-
listNodes: ({ sessionId, branch, limit }) => this.maintenance.getFrontier(sessionId, branch, sessionId === this.activeSessionId ? this.activeSources : void 0).slice(0, limit).map((node) => ({ ...node, text: node.text ?? "", sources: node.sources.map((source) => ({ entryId: source.entryId, revision: source.revision, contentHash: source.payloadHash })) })),
|
|
2712
|
-
getNode: (nodeId) => {
|
|
2713
|
-
const node = this.maintenance.getNode(nodeId);
|
|
2714
|
-
return node ? { ...node, text: node.text ?? "", sources: node.sources.map((source) => ({ entryId: source.entryId, revision: source.revision, contentHash: source.payloadHash })) } : void 0;
|
|
2715
|
-
}
|
|
2716
|
-
},
|
|
2717
|
-
branchForSession: (sessionId) => sessionId === this.activeSessionId ? { branch: this.context.sessionManager.getLeafId(), activeSourceKeys: [...this.activeSources], ready: this.status === "healthy" } : void 0
|
|
2718
|
-
};
|
|
2719
|
-
}
|
|
2720
|
-
frontier(sessionId, branch) {
|
|
2721
|
-
return this.maintenance.getFrontier(sessionId, branch, sessionId === this.activeSessionId ? this.activeSources : void 0);
|
|
2722
|
-
}
|
|
2723
|
-
raw(sessionId) {
|
|
2724
|
-
return this.ledger.readRaw(this.projectKey, sessionId);
|
|
2725
|
-
}
|
|
2726
|
-
async shutdown() {
|
|
2727
|
-
if (this.closed) return;
|
|
2728
|
-
this.closed = true;
|
|
2729
|
-
this.abort.abort();
|
|
2730
|
-
await this.writePending.catch(() => void 0);
|
|
2731
|
-
await this.maintenancePending.catch(() => void 0);
|
|
2732
|
-
this.ledger.close();
|
|
2733
|
-
}
|
|
2734
1877
|
};
|
|
2735
1878
|
|
|
2736
1879
|
// src/core/tool-ownership.ts
|
|
2737
|
-
import
|
|
1880
|
+
import path3 from "node:path";
|
|
2738
1881
|
|
|
2739
1882
|
// src/audit/details.ts
|
|
2740
1883
|
var FABRIC_EXECUTION_DETAILS_MAX_BYTES = 512 * 1024;
|
|
@@ -2836,7 +1979,7 @@ var readFabricExecutionRenderDetails = (value) => {
|
|
|
2836
1979
|
var FABRIC_TOOL_NAME = "fabric_exec";
|
|
2837
1980
|
var TOP_LEVEL_SCHEMA_REF_PREFIX = "schema.top_level_tool.";
|
|
2838
1981
|
var ownsFabricToolSource = (tools, extensionEntryPath) => tools.some(
|
|
2839
|
-
(tool) => tool.name === FABRIC_TOOL_NAME &&
|
|
1982
|
+
(tool) => tool.name === FABRIC_TOOL_NAME && path3.resolve(tool.sourceInfo.path) === path3.resolve(extensionEntryPath)
|
|
2840
1983
|
);
|
|
2841
1984
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2842
1985
|
var finalFabricDetailsFailed = (details) => {
|
|
@@ -2999,7 +2142,7 @@ ${sections.join("\n")}` : "";
|
|
|
2999
2142
|
|
|
3000
2143
|
// src/core/system-guidance.ts
|
|
3001
2144
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
3002
|
-
import * as
|
|
2145
|
+
import * as path4 from "node:path";
|
|
3003
2146
|
var fabricExecutionKernelGuidance = (fullCodeMode) => [
|
|
3004
2147
|
fullCodeMode ? "OMP Fabric full code mode: `fabric_exec` is the only way to call OMP core tools \u2014 use them as `omp.*` inside `code`." : "OMP Fabric is in orchestration-only mode. OMP core and registered extension tools stay on their native direct execution path; inside fabric_exec, `omp.*` and `extensions.*` are unavailable.",
|
|
3005
2148
|
// Files the model has not opened (images in particular) must be read before
|
|
@@ -3011,10 +2154,10 @@ var defaultFabricExecutionGuidance = (fullCodeMode) => fullCodeMode ? "Examples
|
|
|
3011
2154
|
var manifestNameCache = /* @__PURE__ */ new Map();
|
|
3012
2155
|
var packageNameFromManifest = (startPath) => {
|
|
3013
2156
|
if (!startPath) return void 0;
|
|
3014
|
-
let directory =
|
|
2157
|
+
let directory = path4.dirname(path4.resolve(startPath));
|
|
3015
2158
|
while (true) {
|
|
3016
2159
|
if (manifestNameCache.has(directory)) return manifestNameCache.get(directory);
|
|
3017
|
-
const manifestPath =
|
|
2160
|
+
const manifestPath = path4.join(directory, "package.json");
|
|
3018
2161
|
let name;
|
|
3019
2162
|
if (existsSync2(manifestPath)) {
|
|
3020
2163
|
try {
|
|
@@ -3025,7 +2168,7 @@ var packageNameFromManifest = (startPath) => {
|
|
|
3025
2168
|
}
|
|
3026
2169
|
manifestNameCache.set(directory, name);
|
|
3027
2170
|
if (name) return name;
|
|
3028
|
-
const parent =
|
|
2171
|
+
const parent = path4.dirname(directory);
|
|
3029
2172
|
if (parent === directory) return void 0;
|
|
3030
2173
|
directory = parent;
|
|
3031
2174
|
}
|
|
@@ -3293,8 +2436,8 @@ var formatFailureProgress = (trace) => {
|
|
|
3293
2436
|
);
|
|
3294
2437
|
if (completed.length === 0) return void 0;
|
|
3295
2438
|
const summaries = completed.slice(0, MAX_COMPLETED_CALLS).map((operation) => {
|
|
3296
|
-
const
|
|
3297
|
-
return typeof
|
|
2439
|
+
const path11 = operation.args.path;
|
|
2440
|
+
return typeof path11 === "string" ? `${operation.ref}(${compactPath(path11)})` : operation.ref;
|
|
3298
2441
|
});
|
|
3299
2442
|
const omitted = completed.length - summaries.length;
|
|
3300
2443
|
return [
|
|
@@ -3603,12 +2746,12 @@ var PartialResultObserver = class {
|
|
|
3603
2746
|
// src/output-budget.ts
|
|
3604
2747
|
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
3605
2748
|
import { tmpdir } from "node:os";
|
|
3606
|
-
import
|
|
2749
|
+
import path5 from "node:path";
|
|
3607
2750
|
var MAX_FAILURE_MODEL_OUTPUT_CHARS = 2e4;
|
|
3608
2751
|
var modelOutputBudget = (configuredMaxChars, success) => success ? configuredMaxChars : Math.min(configuredMaxChars, MAX_FAILURE_MODEL_OUTPUT_CHARS);
|
|
3609
2752
|
var writeOutputArtifact = async (content) => {
|
|
3610
|
-
const directory = await mkdtemp(
|
|
3611
|
-
const artifactPath =
|
|
2753
|
+
const directory = await mkdtemp(path5.join(tmpdir(), "omp-fabric-output-"));
|
|
2754
|
+
const artifactPath = path5.join(directory, "output.txt");
|
|
3612
2755
|
await writeFile(artifactPath, content, { encoding: "utf8", mode: 384 });
|
|
3613
2756
|
return artifactPath;
|
|
3614
2757
|
};
|
|
@@ -3644,20 +2787,20 @@ var normalizeFabricExecParams = (input) => {
|
|
|
3644
2787
|
if (typeof prepared !== "object" || prepared === null || Array.isArray(prepared)) {
|
|
3645
2788
|
throw new Error("fabric_exec arguments must be an object");
|
|
3646
2789
|
}
|
|
3647
|
-
const
|
|
3648
|
-
const code =
|
|
2790
|
+
const record = prepared;
|
|
2791
|
+
const code = record.code;
|
|
3649
2792
|
if (typeof code !== "string" && !(Array.isArray(code) && code.every((line) => typeof line === "string"))) {
|
|
3650
2793
|
throw new Error("fabric_exec code must be a string or string array");
|
|
3651
2794
|
}
|
|
3652
|
-
const resultFormat = RESULT_FORMATS.includes(
|
|
2795
|
+
const resultFormat = RESULT_FORMATS.includes(record.resultFormat) ? record.resultFormat : void 0;
|
|
3653
2796
|
return {
|
|
3654
2797
|
code,
|
|
3655
|
-
...
|
|
3656
|
-
...
|
|
2798
|
+
...record.payloads !== void 0 ? { payloads: record.payloads } : {},
|
|
2799
|
+
...record.display !== void 0 ? { display: record.display } : {},
|
|
3657
2800
|
...resultFormat !== void 0 ? { resultFormat } : {},
|
|
3658
|
-
...typeof
|
|
3659
|
-
...typeof
|
|
3660
|
-
...typeof
|
|
2801
|
+
...typeof record.tokenBudget === "number" ? { tokenBudget: record.tokenBudget } : {},
|
|
2802
|
+
...typeof record.agentBudget === "number" ? { agentBudget: record.agentBudget } : {},
|
|
2803
|
+
...typeof record.timeoutMs === "number" ? { timeoutMs: record.timeoutMs } : {}
|
|
3661
2804
|
};
|
|
3662
2805
|
};
|
|
3663
2806
|
var toolDisplayMode = (state) => state.bootstrapped ? state.config.ui.toolDisplay : "full";
|
|
@@ -4392,8 +3535,8 @@ ${recoveryHint}` : ""}`,
|
|
|
4392
3535
|
);
|
|
4393
3536
|
|
|
4394
3537
|
// src/fabric-state.ts
|
|
4395
|
-
import
|
|
4396
|
-
import
|
|
3538
|
+
import fs2 from "node:fs";
|
|
3539
|
+
import path6 from "node:path";
|
|
4397
3540
|
var FabricState = class {
|
|
4398
3541
|
constructor(omp, capturedTools, options = {}) {
|
|
4399
3542
|
this.omp = omp;
|
|
@@ -4532,15 +3675,15 @@ var FabricState = class {
|
|
|
4532
3675
|
const sessionId = context.sessionManager.getSessionId();
|
|
4533
3676
|
if (resolveFabricIdentity(sessionId).identity.kind !== "main") return false;
|
|
4534
3677
|
const projectRoot = process.env.OMP_FABRIC_PROJECT_ROOT ?? context.cwd;
|
|
4535
|
-
const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (this.config.mesh.root ?
|
|
3678
|
+
const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (this.config.mesh.root ? path6.resolve(projectRoot, this.config.mesh.root) : path6.join(projectRoot, ".omp", "fabric", "mesh"));
|
|
4536
3679
|
const fabricSessionId = process.env.OMP_FABRIC_SESSION_ID?.trim() || sessionId;
|
|
4537
3680
|
const actorRoots = [
|
|
4538
|
-
|
|
4539
|
-
|
|
3681
|
+
path6.join(meshRoot, "actors"),
|
|
3682
|
+
path6.join(meshRoot, "actors", fabricSessionId)
|
|
4540
3683
|
];
|
|
4541
3684
|
return actorRoots.some((actorRoot) => {
|
|
4542
3685
|
try {
|
|
4543
|
-
const registry = JSON.parse(
|
|
3686
|
+
const registry = JSON.parse(fs2.readFileSync(path6.join(actorRoot, "actors.json"), "utf8"));
|
|
4544
3687
|
if (typeof registry !== "object" || registry === null || Array.isArray(registry)) return false;
|
|
4545
3688
|
const actors = registry.actors;
|
|
4546
3689
|
return Array.isArray(actors) && actors.some((value) => {
|
|
@@ -4943,7 +4086,7 @@ var classifyToolResult = (input) => {
|
|
|
4943
4086
|
|
|
4944
4087
|
// src/host-compatibility.ts
|
|
4945
4088
|
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
4946
|
-
import
|
|
4089
|
+
import path7 from "node:path";
|
|
4947
4090
|
var MINIMUM_OMP_HOST_VERSION = "18.1.10";
|
|
4948
4091
|
var OMP_HOST_PACKAGE_NAMES = {
|
|
4949
4092
|
"@oh-my-pi/pi-coding-agent": true
|
|
@@ -4973,12 +4116,12 @@ var detectOmpHostVersion = (cliPath = process.argv[1]) => {
|
|
|
4973
4116
|
if (!cliPath) return void 0;
|
|
4974
4117
|
let directory;
|
|
4975
4118
|
try {
|
|
4976
|
-
directory =
|
|
4119
|
+
directory = path7.dirname(realpathSync2(cliPath));
|
|
4977
4120
|
} catch {
|
|
4978
4121
|
return void 0;
|
|
4979
4122
|
}
|
|
4980
4123
|
while (true) {
|
|
4981
|
-
const manifestPath =
|
|
4124
|
+
const manifestPath = path7.join(directory, "package.json");
|
|
4982
4125
|
if (existsSync3(manifestPath)) {
|
|
4983
4126
|
try {
|
|
4984
4127
|
const manifest = JSON.parse(readFileSync3(manifestPath, "utf8"));
|
|
@@ -4988,7 +4131,7 @@ var detectOmpHostVersion = (cliPath = process.argv[1]) => {
|
|
|
4988
4131
|
} catch {
|
|
4989
4132
|
}
|
|
4990
4133
|
}
|
|
4991
|
-
const parent =
|
|
4134
|
+
const parent = path7.dirname(directory);
|
|
4992
4135
|
if (parent === directory) return void 0;
|
|
4993
4136
|
directory = parent;
|
|
4994
4137
|
}
|
|
@@ -5001,8 +4144,8 @@ var ompHostCompatibilityWarning = (version = detectOmpHostVersion()) => {
|
|
|
5001
4144
|
};
|
|
5002
4145
|
|
|
5003
4146
|
// src/update/check.ts
|
|
5004
|
-
import
|
|
5005
|
-
import
|
|
4147
|
+
import fs3 from "node:fs";
|
|
4148
|
+
import path8 from "node:path";
|
|
5006
4149
|
import { fileURLToPath } from "node:url";
|
|
5007
4150
|
var PACKAGE_NAME = "omp-fabric";
|
|
5008
4151
|
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
@@ -5010,27 +4153,27 @@ var UPGRADE_COMMAND = `omp plugin install ${PACKAGE_NAME}`;
|
|
|
5010
4153
|
var REQUEST_TIMEOUT_MS = 4e3;
|
|
5011
4154
|
var UPDATE_STATUS_KEY = "fabric-update";
|
|
5012
4155
|
var UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5013
|
-
var updateCheckCachePath = (agentDir) =>
|
|
4156
|
+
var updateCheckCachePath = (agentDir) => path8.join(agentDir, "fabric", "update-check.json");
|
|
5014
4157
|
var installedVersionFromManifest = () => {
|
|
5015
|
-
let directory =
|
|
4158
|
+
let directory = path8.dirname(fileURLToPath(import.meta.url));
|
|
5016
4159
|
while (true) {
|
|
5017
4160
|
try {
|
|
5018
4161
|
const manifest = JSON.parse(
|
|
5019
|
-
|
|
4162
|
+
fs3.readFileSync(path8.join(directory, "package.json"), "utf8")
|
|
5020
4163
|
);
|
|
5021
4164
|
if (manifest.name === PACKAGE_NAME && typeof manifest.version === "string") {
|
|
5022
4165
|
return manifest.version;
|
|
5023
4166
|
}
|
|
5024
4167
|
} catch {
|
|
5025
4168
|
}
|
|
5026
|
-
const parent =
|
|
4169
|
+
const parent = path8.dirname(directory);
|
|
5027
4170
|
if (parent === directory) return void 0;
|
|
5028
4171
|
directory = parent;
|
|
5029
4172
|
}
|
|
5030
4173
|
};
|
|
5031
4174
|
var readCache = (cachePath) => {
|
|
5032
4175
|
try {
|
|
5033
|
-
const parsed = JSON.parse(
|
|
4176
|
+
const parsed = JSON.parse(fs3.readFileSync(cachePath, "utf8"));
|
|
5034
4177
|
if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt)) return void 0;
|
|
5035
4178
|
if (typeof parsed.latest !== "string" || parsed.latest.length === 0) return void 0;
|
|
5036
4179
|
return {
|
|
@@ -5077,32 +4220,32 @@ var checkFabricUpdate = async (options) => {
|
|
|
5077
4220
|
const cached = readCache(cachePath);
|
|
5078
4221
|
const age = cached === void 0 ? void 0 : now - cached.checkedAt;
|
|
5079
4222
|
const fresh = cached !== void 0 && age !== void 0 && age >= 0 && age < UPDATE_CHECK_TTL_MS;
|
|
5080
|
-
let
|
|
4223
|
+
let record;
|
|
5081
4224
|
if (fresh && cached !== void 0) {
|
|
5082
|
-
|
|
4225
|
+
record = cached;
|
|
5083
4226
|
} else {
|
|
5084
4227
|
const latest = await fetchLatestVersion(
|
|
5085
4228
|
options.fetchImpl ?? globalThis.fetch,
|
|
5086
4229
|
options.timeoutMs ?? REQUEST_TIMEOUT_MS
|
|
5087
4230
|
);
|
|
5088
4231
|
if (latest === void 0) return void 0;
|
|
5089
|
-
|
|
4232
|
+
record = {
|
|
5090
4233
|
format: 1,
|
|
5091
4234
|
checkedAt: now,
|
|
5092
4235
|
latest,
|
|
5093
4236
|
...cached?.notified === void 0 ? {} : { notified: cached.notified }
|
|
5094
4237
|
};
|
|
5095
4238
|
}
|
|
5096
|
-
const behind = compareVersions(
|
|
5097
|
-
const announce = behind &&
|
|
4239
|
+
const behind = compareVersions(record.latest, installed) === 1;
|
|
4240
|
+
const announce = behind && record.notified !== record.latest;
|
|
5098
4241
|
if (!fresh || announce) {
|
|
5099
|
-
writeCache(cachePath, announce ? { ...
|
|
4242
|
+
writeCache(cachePath, announce ? { ...record, notified: record.latest } : record);
|
|
5100
4243
|
}
|
|
5101
4244
|
if (!announce) return void 0;
|
|
5102
4245
|
return {
|
|
5103
4246
|
installed,
|
|
5104
|
-
latest:
|
|
5105
|
-
message: `${PACKAGE_NAME} ${installed} \u2192 ${
|
|
4247
|
+
latest: record.latest,
|
|
4248
|
+
message: `${PACKAGE_NAME} ${installed} \u2192 ${record.latest} \xB7 ${UPGRADE_COMMAND}`
|
|
5106
4249
|
};
|
|
5107
4250
|
} catch {
|
|
5108
4251
|
return void 0;
|
|
@@ -5131,7 +4274,7 @@ var scheduleFabricUpdateCheck = async (context, options) => {
|
|
|
5131
4274
|
};
|
|
5132
4275
|
|
|
5133
4276
|
// src/ui/controller.ts
|
|
5134
|
-
import
|
|
4277
|
+
import path9 from "node:path";
|
|
5135
4278
|
|
|
5136
4279
|
// src/ui/snapshot.ts
|
|
5137
4280
|
var MAX_UI_AGENTS = 240;
|
|
@@ -5188,57 +4331,57 @@ var createDashboardSnapshot = (state, events, context, activityRuns) => {
|
|
|
5188
4331
|
if (leftLaunch !== rightLaunch) return leftLaunch ? -1 : 1;
|
|
5189
4332
|
return left.call.startedAt - right.call.startedAt;
|
|
5190
4333
|
});
|
|
5191
|
-
const agentFromRecord = (
|
|
4334
|
+
const agentFromRecord = (record, nestingDepth, parentId, parent) => {
|
|
5192
4335
|
const linked = parentId ? void 0 : agentLinks.find(
|
|
5193
|
-
({ call }) => call.entityId && (
|
|
4336
|
+
({ call }) => call.entityId && (record.id.startsWith(call.entityId) || call.entityId.startsWith(record.id))
|
|
5194
4337
|
);
|
|
5195
4338
|
const base = {
|
|
5196
|
-
id:
|
|
5197
|
-
name:
|
|
5198
|
-
status:
|
|
5199
|
-
runner:
|
|
5200
|
-
transport:
|
|
5201
|
-
cwd:
|
|
5202
|
-
...!isRunRecord(
|
|
5203
|
-
...
|
|
5204
|
-
...
|
|
5205
|
-
...
|
|
5206
|
-
...isRunRecord(
|
|
5207
|
-
...
|
|
5208
|
-
...
|
|
5209
|
-
...
|
|
5210
|
-
...
|
|
4339
|
+
id: record.id,
|
|
4340
|
+
name: record.name,
|
|
4341
|
+
status: record.status,
|
|
4342
|
+
runner: record.runner,
|
|
4343
|
+
transport: record.transport,
|
|
4344
|
+
cwd: record.cwd,
|
|
4345
|
+
...!isRunRecord(record) && linked ? { startedAt: linked.call.startedAt } : {},
|
|
4346
|
+
...record.model ? { model: record.model } : {},
|
|
4347
|
+
...record.thinking ? { thinking: record.thinking } : {},
|
|
4348
|
+
...record.attachCommand ? { attachCommand: record.attachCommand } : {},
|
|
4349
|
+
...isRunRecord(record) && record.logFile ? { logFile: record.logFile } : {},
|
|
4350
|
+
...record.branch ? { branch: record.branch } : {},
|
|
4351
|
+
...record.worktree ? { worktree: record.worktree } : {},
|
|
4352
|
+
...record.actorId ? { actorId: record.actorId } : {},
|
|
4353
|
+
...record.actorName ? { actorName: record.actorName } : {},
|
|
5211
4354
|
...parentId ? { parentId } : {},
|
|
5212
4355
|
...nestingDepth > 0 ? { nestingDepth } : {},
|
|
5213
4356
|
...linked ? { runId: linked.runId } : parent?.runId ? { runId: parent.runId } : {},
|
|
5214
4357
|
...linked?.call.phaseId ? { phaseId: linked.call.phaseId } : parent?.phaseId ? { phaseId: parent.phaseId } : {}
|
|
5215
4358
|
};
|
|
5216
|
-
if (!isRunRecord(
|
|
4359
|
+
if (!isRunRecord(record)) return base;
|
|
5217
4360
|
return {
|
|
5218
4361
|
...base,
|
|
5219
|
-
task:
|
|
5220
|
-
startedAt:
|
|
5221
|
-
updatedAt:
|
|
5222
|
-
...
|
|
5223
|
-
...
|
|
5224
|
-
turns:
|
|
5225
|
-
toolCalls:
|
|
5226
|
-
usage: { ...
|
|
5227
|
-
...
|
|
5228
|
-
...
|
|
5229
|
-
...
|
|
4362
|
+
task: record.task,
|
|
4363
|
+
startedAt: record.startedAt,
|
|
4364
|
+
updatedAt: record.updatedAt,
|
|
4365
|
+
...record.finishedAt !== void 0 ? { finishedAt: record.finishedAt } : {},
|
|
4366
|
+
...record.currentTool ? { currentTool: record.currentTool } : {},
|
|
4367
|
+
turns: record.turns,
|
|
4368
|
+
toolCalls: record.toolCalls,
|
|
4369
|
+
usage: { ...record.usage },
|
|
4370
|
+
...record.text ? { text: record.text } : {},
|
|
4371
|
+
...record.value !== void 0 ? { value: structuredClone(record.value) } : {},
|
|
4372
|
+
...record.error ? { error: record.error } : {}
|
|
5230
4373
|
};
|
|
5231
4374
|
};
|
|
5232
4375
|
const allAgents = [];
|
|
5233
|
-
const appendAgent = (
|
|
5234
|
-
const agent = agentFromRecord(
|
|
4376
|
+
const appendAgent = (record, nestingDepth, parentId, parent) => {
|
|
4377
|
+
const agent = agentFromRecord(record, nestingDepth, parentId, parent);
|
|
5235
4378
|
allAgents.push(agent);
|
|
5236
|
-
if (!isRunRecord(
|
|
5237
|
-
for (const nested of
|
|
5238
|
-
appendAgent(nested, nestingDepth + 1,
|
|
4379
|
+
if (!isRunRecord(record)) return;
|
|
4380
|
+
for (const nested of record.nestedAgents ?? []) {
|
|
4381
|
+
appendAgent(nested, nestingDepth + 1, record.id, agent);
|
|
5239
4382
|
}
|
|
5240
4383
|
};
|
|
5241
|
-
for (const
|
|
4384
|
+
for (const record of agentRecords) appendAgent(record, 0);
|
|
5242
4385
|
const participants = typeof state.participantInfos === "function" ? state.participantInfos({ scope: "project" }) : [];
|
|
5243
4386
|
const participantById = new Map(participants.map((participant) => [participant.id, participant]));
|
|
5244
4387
|
const actors = state.actors.list().map((actor) => {
|
|
@@ -5988,7 +5131,7 @@ var FabricUiController = class {
|
|
|
5988
5131
|
logFile: actor.worker.logFile
|
|
5989
5132
|
};
|
|
5990
5133
|
}
|
|
5991
|
-
const retained = actor.lastRunId && actor.logDir ?
|
|
5134
|
+
const retained = actor.lastRunId && actor.logDir ? path9.join(actor.logDir, actor.lastRunId, "events.jsonl") : void 0;
|
|
5992
5135
|
if (retained) return { id: actor.id, status: actor.status, logFile: retained };
|
|
5993
5136
|
if (actor.sessionFile) {
|
|
5994
5137
|
return { id: actor.id, status: actor.status, logFile: actor.sessionFile };
|
|
@@ -6147,15 +5290,15 @@ var REFRESH_CARDS_PER_TICK = 3;
|
|
|
6147
5290
|
|
|
6148
5291
|
// src/index.ts
|
|
6149
5292
|
import { existsSync as existsSync4 } from "node:fs";
|
|
6150
|
-
import
|
|
5293
|
+
import path10 from "node:path";
|
|
6151
5294
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6152
|
-
var FABRIC_EXTENSION_ENTRY_PATH =
|
|
6153
|
-
var FABRIC_ENTRY_DIR =
|
|
5295
|
+
var FABRIC_EXTENSION_ENTRY_PATH = path10.resolve(fileURLToPath2(import.meta.url));
|
|
5296
|
+
var FABRIC_ENTRY_DIR = path10.dirname(FABRIC_EXTENSION_ENTRY_PATH);
|
|
6154
5297
|
var FABRIC_RUNTIME_PATHS = {
|
|
6155
5298
|
extension: FABRIC_EXTENSION_ENTRY_PATH,
|
|
6156
|
-
worker:
|
|
6157
|
-
residentHost:
|
|
6158
|
-
skills:
|
|
5299
|
+
worker: path10.join(FABRIC_ENTRY_DIR, "worker.js"),
|
|
5300
|
+
residentHost: path10.join(FABRIC_ENTRY_DIR, "residency", "launcher.js"),
|
|
5301
|
+
skills: path10.resolve(FABRIC_ENTRY_DIR, "..", "skills")
|
|
6159
5302
|
};
|
|
6160
5303
|
var FABRIC_SKILLS_DIR = FABRIC_RUNTIME_PATHS.skills;
|
|
6161
5304
|
var componentRegistrationFrom = (value) => {
|
|
@@ -6199,6 +5342,7 @@ async function ompFabric(omp) {
|
|
|
6199
5342
|
);
|
|
6200
5343
|
const pendingHandoffs = /* @__PURE__ */ new Map();
|
|
6201
5344
|
let lcmRuntime;
|
|
5345
|
+
const lcmLoader = createLcmRuntimeLoader();
|
|
6202
5346
|
let lcmCwd;
|
|
6203
5347
|
let lcmProjectKey;
|
|
6204
5348
|
const toolOwnership = new FabricToolOwnership(omp);
|
|
@@ -6474,16 +5618,18 @@ async function ompFabric(omp) {
|
|
|
6474
5618
|
lcmProjectKey = void 0;
|
|
6475
5619
|
}
|
|
6476
5620
|
if (state.config.compaction.engine === "lcm" && !lcmRuntime) {
|
|
6477
|
-
|
|
5621
|
+
const LcmRuntimeClass = await lcmLoader.load(context);
|
|
5622
|
+
if (LcmRuntimeClass) lcmRuntime = new LcmRuntimeClass(context, () => ({
|
|
6478
5623
|
...state.config.compaction.summaryModel ? { summaryModel: state.config.compaction.summaryModel } : {},
|
|
6479
5624
|
maxLeafEntries: state.config.compaction.lcmMaxLeafEntries,
|
|
6480
5625
|
maxCondenseChildren: state.config.compaction.lcmMaxCondenseChildren,
|
|
5626
|
+
maxMaintenancePasses: state.config.compaction.lcmMaintenancePasses,
|
|
6481
5627
|
lcmMaxInputChars: state.config.compaction.lcmMaxInputChars,
|
|
6482
5628
|
lcmMaxOutputTokens: state.config.compaction.lcmMaxOutputTokens,
|
|
6483
5629
|
lcmMaxOutputChars: state.config.compaction.lcmMaxOutputChars
|
|
6484
5630
|
}));
|
|
6485
|
-
lcmCwd = context.cwd;
|
|
6486
|
-
lcmProjectKey = lcmRuntime
|
|
5631
|
+
lcmCwd = lcmRuntime ? context.cwd : void 0;
|
|
5632
|
+
lcmProjectKey = lcmRuntime?.projectKey;
|
|
6487
5633
|
}
|
|
6488
5634
|
if (state.config.compaction.engine === "lcm" && lcmRuntime) await lcmRuntime.reconcileSelectedSession();
|
|
6489
5635
|
refreshCodePreviewSettings();
|
|
@@ -6633,7 +5779,7 @@ async function ompFabric(omp) {
|
|
|
6633
5779
|
await state.publishHostLifecycle("omp.session_compact", event);
|
|
6634
5780
|
});
|
|
6635
5781
|
registerCompactionHook(omp, {
|
|
6636
|
-
getEngine: () => state.cwd ? state.config.compaction.engine : DEFAULT_FABRIC_CONFIG.compaction.engine,
|
|
5782
|
+
getEngine: () => lcmLoader.unavailable ? "omp" : state.cwd ? state.config.compaction.engine : DEFAULT_FABRIC_CONFIG.compaction.engine,
|
|
6637
5783
|
getTargetContextRatio: () => state.cwd ? state.config.compaction.targetContextRatio : DEFAULT_FABRIC_CONFIG.compaction.targetContextRatio,
|
|
6638
5784
|
lcm: {
|
|
6639
5785
|
compact: (input) => lcmRuntime?.compact(input)
|