kibi-runtime 2.13.3 → 2.14.1
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/dist/engine-daemon.js +157 -35
- package/dist/index.js +22640 -21673
- package/dist/skills/kibi-usage/SKILL.md +44 -1
- package/dist/skills/kibi-usage/resources/fact-lanes.md +4 -2
- package/package.json +3 -3
package/dist/engine-daemon.js
CHANGED
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
openSync as openSync2,
|
|
20
20
|
readFileSync as readFileSync5,
|
|
21
21
|
readdirSync as readdirSync2,
|
|
22
|
-
realpathSync as
|
|
22
|
+
realpathSync as realpathSync4,
|
|
23
23
|
renameSync,
|
|
24
24
|
rmSync as rmSync2,
|
|
25
|
-
statSync as
|
|
25
|
+
statSync as statSync5,
|
|
26
26
|
unlinkSync,
|
|
27
27
|
writeFileSync as writeFileSync3
|
|
28
28
|
} from "node:fs";
|
|
@@ -102,7 +102,7 @@ function manifestVersion(manifestPath, expectedName) {
|
|
|
102
102
|
}
|
|
103
103
|
function cliVersion() {
|
|
104
104
|
if (true)
|
|
105
|
-
return "2.
|
|
105
|
+
return "2.20.0";
|
|
106
106
|
return manifestVersion(path.join(moduleDirectory, "..", "package.json"), "kibi-cli") ?? "unknown";
|
|
107
107
|
}
|
|
108
108
|
function coreVersion() {
|
|
@@ -1754,9 +1754,15 @@ writeln('${INTERACTIVE_QUERY_FRAME_END}').
|
|
|
1754
1754
|
}
|
|
1755
1755
|
|
|
1756
1756
|
// ../cli/src/prolog/store-lock.ts
|
|
1757
|
-
import {
|
|
1757
|
+
import {
|
|
1758
|
+
existsSync as existsSync4,
|
|
1759
|
+
realpathSync as realpathSync2,
|
|
1760
|
+
rmSync,
|
|
1761
|
+
statSync as statSync3,
|
|
1762
|
+
writeFileSync
|
|
1763
|
+
} from "node:fs";
|
|
1758
1764
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
1759
|
-
import { dirname, join } from "node:path";
|
|
1765
|
+
import { basename, dirname, join } from "node:path";
|
|
1760
1766
|
var cachedBootId;
|
|
1761
1767
|
function currentBootId() {
|
|
1762
1768
|
if (cachedBootId !== undefined)
|
|
@@ -1798,6 +1804,33 @@ function classifyStoreLockHolder(owner) {
|
|
|
1798
1804
|
}
|
|
1799
1805
|
return "alive";
|
|
1800
1806
|
}
|
|
1807
|
+
function readStoreLockOwner(storePath) {
|
|
1808
|
+
const candidates = [
|
|
1809
|
+
join(storePath, ".kibi-lock-owner.json"),
|
|
1810
|
+
join(storePath, "rdf", ".kibi-lock-owner.json")
|
|
1811
|
+
];
|
|
1812
|
+
for (const journalPath of candidates) {
|
|
1813
|
+
if (!existsSync4(journalPath))
|
|
1814
|
+
continue;
|
|
1815
|
+
let parsed;
|
|
1816
|
+
try {
|
|
1817
|
+
parsed = JSON.parse(readFileSync3(journalPath, "utf8"));
|
|
1818
|
+
} catch {
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
if (parsed === null || typeof parsed !== "object")
|
|
1822
|
+
continue;
|
|
1823
|
+
const record = parsed;
|
|
1824
|
+
const owner = {
|
|
1825
|
+
...typeof record.pid === "number" ? { pid: record.pid } : {},
|
|
1826
|
+
...typeof record.workspaceRoot === "string" ? { workspaceRoot: record.workspaceRoot } : {},
|
|
1827
|
+
...typeof record.bootId === "string" ? { bootId: record.bootId } : {},
|
|
1828
|
+
...typeof record.startedAt === "string" ? { startedAt: record.startedAt } : {}
|
|
1829
|
+
};
|
|
1830
|
+
return { owner, journalPath };
|
|
1831
|
+
}
|
|
1832
|
+
return null;
|
|
1833
|
+
}
|
|
1801
1834
|
function breakStoreLock(rdfDirectory) {
|
|
1802
1835
|
const candidates = [
|
|
1803
1836
|
join(rdfDirectory, "lock"),
|
|
@@ -1818,14 +1851,97 @@ function recordStoreLockTakeover(storePath, brokenPid, reason, at = new Date) {
|
|
|
1818
1851
|
`, { mode: 384 });
|
|
1819
1852
|
} catch {}
|
|
1820
1853
|
}
|
|
1821
|
-
function
|
|
1854
|
+
function readRdfLockPid(lockPath) {
|
|
1855
|
+
try {
|
|
1856
|
+
const match = /pid\((\d+)\)/.exec(readFileSync3(lockPath, "utf8"));
|
|
1857
|
+
const pid = Number.parseInt(match?.[1] ?? "", 10);
|
|
1858
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
1859
|
+
} catch {
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
function noteDeadHolderBeforeAttach(kbPath) {
|
|
1864
|
+
const lockPath = join(kbPath, "rdf", "lock");
|
|
1865
|
+
if (!existsSync4(lockPath))
|
|
1866
|
+
return () => {
|
|
1867
|
+
return;
|
|
1868
|
+
};
|
|
1869
|
+
const lockPid = readRdfLockPid(lockPath);
|
|
1870
|
+
const journal = readStoreLockOwner(kbPath)?.owner ?? null;
|
|
1871
|
+
const owner = journal !== null && (lockPid === null || journal.pid === lockPid) ? journal : lockPid !== null ? { pid: lockPid } : null;
|
|
1872
|
+
const decision = decideStoreLockTakeover(owner, { storePath: kbPath });
|
|
1873
|
+
if (decision.action !== "break")
|
|
1874
|
+
return () => {
|
|
1875
|
+
return;
|
|
1876
|
+
};
|
|
1877
|
+
const brokenPid = owner?.pid ?? null;
|
|
1878
|
+
return () => recordStoreLockTakeover(kbPath, brokenPid, decision.reason);
|
|
1879
|
+
}
|
|
1880
|
+
var FOREIGN_STORE_LOCK_REASON_PREFIX = "journal belongs to another workspace";
|
|
1881
|
+
function existingRealPath(pathname) {
|
|
1882
|
+
try {
|
|
1883
|
+
return realpathSync2(pathname);
|
|
1884
|
+
} catch {
|
|
1885
|
+
return null;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
function sameDirectory(left, right) {
|
|
1889
|
+
try {
|
|
1890
|
+
const a = statSync3(left);
|
|
1891
|
+
const b = statSync3(right);
|
|
1892
|
+
return a.dev === b.dev && a.ino === b.ino;
|
|
1893
|
+
} catch {
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
function owningWorkspaceRoot(storePath) {
|
|
1898
|
+
let current = storePath;
|
|
1899
|
+
for (;; ) {
|
|
1900
|
+
const parent = dirname(current);
|
|
1901
|
+
if (parent === current)
|
|
1902
|
+
return null;
|
|
1903
|
+
if (basename(parent) === "branches" && basename(dirname(parent)) === ".kb")
|
|
1904
|
+
return dirname(dirname(parent));
|
|
1905
|
+
current = parent;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
function foreignStoreLockJournalRoot(owner, storePath) {
|
|
1909
|
+
const recorded = owner?.workspaceRoot;
|
|
1910
|
+
if (recorded === undefined || recorded === "")
|
|
1911
|
+
return null;
|
|
1912
|
+
const store = existingRealPath(storePath);
|
|
1913
|
+
if (store === null)
|
|
1914
|
+
return null;
|
|
1915
|
+
const ownWorkspace = owningWorkspaceRoot(store);
|
|
1916
|
+
if (ownWorkspace === null)
|
|
1917
|
+
return null;
|
|
1918
|
+
const lockPid = readRdfLockPid(join(storePath, "rdf", "lock"));
|
|
1919
|
+
if (lockPid !== null && lockPid !== owner?.pid)
|
|
1920
|
+
return null;
|
|
1921
|
+
const recordedPath = existingRealPath(recorded);
|
|
1922
|
+
if (recordedPath === null || recordedPath === store)
|
|
1923
|
+
return null;
|
|
1924
|
+
if (sameDirectory(recordedPath, store))
|
|
1925
|
+
return null;
|
|
1926
|
+
const recordedWorkspace = owningWorkspaceRoot(recordedPath) ?? recordedPath;
|
|
1927
|
+
return recordedWorkspace === ownWorkspace ? null : recorded;
|
|
1928
|
+
}
|
|
1929
|
+
function decideStoreLockTakeover(owner, options = {}) {
|
|
1822
1930
|
const state = classifyStoreLockHolder(owner);
|
|
1931
|
+
if (state === "dead") {
|
|
1932
|
+
return {
|
|
1933
|
+
action: "break",
|
|
1934
|
+
reason: `recorded holder pid ${owner?.pid ?? "?"} is no longer running`
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
const foreignRoot = options.storePath === undefined ? null : foreignStoreLockJournalRoot(owner, options.storePath);
|
|
1938
|
+
if (foreignRoot !== null) {
|
|
1939
|
+
return {
|
|
1940
|
+
action: "break",
|
|
1941
|
+
reason: `${FOREIGN_STORE_LOCK_REASON_PREFIX} ${foreignRoot}`
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1823
1944
|
switch (state) {
|
|
1824
|
-
case "dead":
|
|
1825
|
-
return {
|
|
1826
|
-
action: "break",
|
|
1827
|
-
reason: `recorded holder pid ${owner?.pid ?? "?"} is no longer running`
|
|
1828
|
-
};
|
|
1829
1945
|
case "unknown":
|
|
1830
1946
|
return {
|
|
1831
1947
|
action: "surface",
|
|
@@ -1844,7 +1960,9 @@ async function retryAttachAfterBreakingStaleLock(prolog, kbPath, failed) {
|
|
|
1844
1960
|
const storeLocked = record?.storeLocked;
|
|
1845
1961
|
if (storeLocked === undefined)
|
|
1846
1962
|
return failed;
|
|
1847
|
-
const decision = decideStoreLockTakeover(storeLocked.owner
|
|
1963
|
+
const decision = decideStoreLockTakeover(storeLocked.owner, {
|
|
1964
|
+
storePath: kbPath
|
|
1965
|
+
});
|
|
1848
1966
|
if (decision.action !== "break") {
|
|
1849
1967
|
const holderSummary = storeLocked.owner === null ? decision.reason : `pid ${storeLocked.owner.pid ?? "?"} (${decision.reason})`;
|
|
1850
1968
|
return {
|
|
@@ -1877,7 +1995,7 @@ import {
|
|
|
1877
1995
|
mkdirSync,
|
|
1878
1996
|
readFileSync as readFileSync4,
|
|
1879
1997
|
readdirSync,
|
|
1880
|
-
statSync as
|
|
1998
|
+
statSync as statSync4,
|
|
1881
1999
|
writeFileSync as writeFileSync2
|
|
1882
2000
|
} from "node:fs";
|
|
1883
2001
|
import path5 from "node:path";
|
|
@@ -1989,14 +2107,14 @@ function isValidBranchName(name) {
|
|
|
1989
2107
|
}
|
|
1990
2108
|
|
|
1991
2109
|
// ../cli/src/utils/canonical-path.ts
|
|
1992
|
-
import { lstatSync as lstatSync2, realpathSync as
|
|
2110
|
+
import { lstatSync as lstatSync2, realpathSync as realpathSync3 } from "node:fs";
|
|
1993
2111
|
import path7 from "node:path";
|
|
1994
2112
|
function canonicalFilesystemPath(candidate) {
|
|
1995
2113
|
let existing = path7.resolve(candidate);
|
|
1996
2114
|
const missing = [];
|
|
1997
2115
|
for (;; ) {
|
|
1998
2116
|
try {
|
|
1999
|
-
return path7.resolve(
|
|
2117
|
+
return path7.resolve(realpathSync3.native(existing), ...missing);
|
|
2000
2118
|
} catch (error) {
|
|
2001
2119
|
if (error.code !== "ENOENT")
|
|
2002
2120
|
throw error;
|
|
@@ -2054,10 +2172,10 @@ function readJournalGeneration(branchPath) {
|
|
|
2054
2172
|
function readEngineAttachmentIdentity(branchPath) {
|
|
2055
2173
|
if (!existsSync6(branchPath))
|
|
2056
2174
|
return null;
|
|
2057
|
-
const stats =
|
|
2175
|
+
const stats = statSync5(branchPath);
|
|
2058
2176
|
let resolved = path8.resolve(branchPath);
|
|
2059
2177
|
try {
|
|
2060
|
-
resolved =
|
|
2178
|
+
resolved = realpathSync4(branchPath);
|
|
2061
2179
|
} catch {}
|
|
2062
2180
|
return {
|
|
2063
2181
|
path: resolved,
|
|
@@ -2302,26 +2420,27 @@ function recoverStaleJournalLock(branchPath) {
|
|
|
2302
2420
|
const lockPath = path8.join(branchPath, "rdf", "lock");
|
|
2303
2421
|
if (!existsSync6(lockPath))
|
|
2304
2422
|
return;
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
contents = readFileSync5(lockPath, "utf8");
|
|
2308
|
-
} catch {
|
|
2423
|
+
const pid = readRdfLockPid(lockPath);
|
|
2424
|
+
if (pid === null || pid === process.pid)
|
|
2309
2425
|
return;
|
|
2426
|
+
let reason;
|
|
2427
|
+
if (!isProcessAlive(pid)) {
|
|
2428
|
+
reason = decideStoreLockTakeover({ pid }).reason;
|
|
2429
|
+
} else {
|
|
2430
|
+
const journal = readStoreLockOwner(branchPath)?.owner ?? null;
|
|
2431
|
+
if (journal === null || journal.pid !== pid)
|
|
2432
|
+
return;
|
|
2433
|
+
const decision = decideStoreLockTakeover(journal, {
|
|
2434
|
+
storePath: branchPath
|
|
2435
|
+
});
|
|
2436
|
+
if (decision.action !== "break")
|
|
2437
|
+
return;
|
|
2438
|
+
reason = decision.reason;
|
|
2310
2439
|
}
|
|
2311
|
-
|
|
2312
|
-
if (!match)
|
|
2313
|
-
return;
|
|
2314
|
-
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
2315
|
-
if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid)
|
|
2316
|
-
return;
|
|
2440
|
+
recordStoreLockTakeover(branchPath, pid, reason);
|
|
2317
2441
|
try {
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
} catch {
|
|
2321
|
-
try {
|
|
2322
|
-
rmSync2(lockPath, { force: true });
|
|
2323
|
-
} catch {}
|
|
2324
|
-
}
|
|
2442
|
+
rmSync2(lockPath, { force: true });
|
|
2443
|
+
} catch {}
|
|
2325
2444
|
}
|
|
2326
2445
|
function connectSocket(socketPath, timeoutMs) {
|
|
2327
2446
|
return new Promise((resolve, reject) => {
|
|
@@ -2541,8 +2660,11 @@ class EngineSession {
|
|
|
2541
2660
|
}
|
|
2542
2661
|
async boot() {
|
|
2543
2662
|
await this.prolog.start();
|
|
2663
|
+
const recordTakeover = noteDeadHolderBeforeAttach(this.branchPath);
|
|
2544
2664
|
const attached = await this.prolog.query(`kb_attach('${quoteProlog(this.branchPath)}')`);
|
|
2545
|
-
if (
|
|
2665
|
+
if (attached.success)
|
|
2666
|
+
recordTakeover();
|
|
2667
|
+
else {
|
|
2546
2668
|
const recovered = await retryAttachAfterBreakingStaleLock(this.prolog, this.branchPath, attached);
|
|
2547
2669
|
if (!recovered.success)
|
|
2548
2670
|
throw new Error(recovered.error ?? "Failed to attach branch KB");
|