open-think 0.3.3 → 0.3.5
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/{chunk-ZKUJ5M2W.js → chunk-BBCWF24H.js} +1 -1
- package/dist/{chunk-DCTG6IK4.js → chunk-HUBRLTY3.js} +1 -0
- package/dist/{chunk-OFGWR45G.js → chunk-LN2TIS5R.js} +1 -1
- package/dist/{git-TG6OJFBT.js → git-CMDUX3KB.js} +2 -2
- package/dist/index.js +577 -239
- package/dist/{memory-queries-N4VT5G2E.js → memory-queries-QKGOKRFR.js} +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
setLongtermSummary,
|
|
15
15
|
setSyncCursor,
|
|
16
16
|
tombstoneMemory
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-LN2TIS5R.js";
|
|
18
18
|
import {
|
|
19
19
|
appendAndCommit,
|
|
20
20
|
countBranchFileLines,
|
|
@@ -28,18 +28,19 @@ import {
|
|
|
28
28
|
migrateToBuckets,
|
|
29
29
|
readFileFromBranch,
|
|
30
30
|
saveConfig
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-BBCWF24H.js";
|
|
32
32
|
import {
|
|
33
33
|
ensureThinkDirs,
|
|
34
34
|
getCuratorMdPath,
|
|
35
35
|
getEngramsDir,
|
|
36
36
|
getLongtermPath,
|
|
37
|
-
getThinkDataDir
|
|
38
|
-
|
|
37
|
+
getThinkDataDir,
|
|
38
|
+
getThinkDir
|
|
39
|
+
} from "./chunk-HUBRLTY3.js";
|
|
39
40
|
|
|
40
41
|
// src/index.ts
|
|
41
|
-
import
|
|
42
|
-
import
|
|
42
|
+
import fs13 from "fs";
|
|
43
|
+
import path7 from "path";
|
|
43
44
|
import { Command as Command20 } from "commander";
|
|
44
45
|
|
|
45
46
|
// src/commands/log.ts
|
|
@@ -1010,7 +1011,7 @@ ${shown.length} events` + (entries.length > limit ? ` (showing last ${limit} of
|
|
|
1010
1011
|
});
|
|
1011
1012
|
|
|
1012
1013
|
// src/commands/cortex.ts
|
|
1013
|
-
import
|
|
1014
|
+
import fs9 from "fs";
|
|
1014
1015
|
import { Command as Command9 } from "commander";
|
|
1015
1016
|
import chalk9 from "chalk";
|
|
1016
1017
|
import readline2 from "readline";
|
|
@@ -1555,6 +1556,146 @@ function getSyncAdapter() {
|
|
|
1555
1556
|
return null;
|
|
1556
1557
|
}
|
|
1557
1558
|
|
|
1559
|
+
// src/lib/auto-curate.ts
|
|
1560
|
+
import fs8 from "fs";
|
|
1561
|
+
import path5 from "path";
|
|
1562
|
+
import crypto2 from "crypto";
|
|
1563
|
+
import { execFileSync } from "child_process";
|
|
1564
|
+
var DEFAULT_INTERVAL_SECONDS = 300;
|
|
1565
|
+
function getHome() {
|
|
1566
|
+
const home = process.env.HOME;
|
|
1567
|
+
if (!home) throw new Error("HOME environment variable is not set");
|
|
1568
|
+
return home;
|
|
1569
|
+
}
|
|
1570
|
+
function getLaunchAgentsDir() {
|
|
1571
|
+
return path5.join(getHome(), "Library", "LaunchAgents");
|
|
1572
|
+
}
|
|
1573
|
+
function getAgentLabel() {
|
|
1574
|
+
const thinkHome = process.env.THINK_HOME;
|
|
1575
|
+
if (!thinkHome) return "ai.openthink.curate.default";
|
|
1576
|
+
const hash = crypto2.createHash("sha1").update(thinkHome).digest("hex").slice(0, 8);
|
|
1577
|
+
return `ai.openthink.curate.${hash}`;
|
|
1578
|
+
}
|
|
1579
|
+
function getPlistPath(label = getAgentLabel()) {
|
|
1580
|
+
return path5.join(getLaunchAgentsDir(), `${label}.plist`);
|
|
1581
|
+
}
|
|
1582
|
+
function getLogPath() {
|
|
1583
|
+
return path5.join(getThinkDir(), "auto-curate.log");
|
|
1584
|
+
}
|
|
1585
|
+
function resolveThinkBinary() {
|
|
1586
|
+
const arg1 = process.argv[1];
|
|
1587
|
+
if (arg1 && fs8.existsSync(arg1)) return arg1;
|
|
1588
|
+
throw new Error("Could not resolve think binary path");
|
|
1589
|
+
}
|
|
1590
|
+
function resolveNodeBinary() {
|
|
1591
|
+
return process.execPath;
|
|
1592
|
+
}
|
|
1593
|
+
function renderPlist(opts) {
|
|
1594
|
+
const envBlock = opts.thinkHome ? ` <key>EnvironmentVariables</key>
|
|
1595
|
+
<dict>
|
|
1596
|
+
<key>THINK_HOME</key>
|
|
1597
|
+
<string>${escapeXml(opts.thinkHome)}</string>
|
|
1598
|
+
</dict>
|
|
1599
|
+
` : "";
|
|
1600
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1601
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1602
|
+
<plist version="1.0">
|
|
1603
|
+
<dict>
|
|
1604
|
+
<key>Label</key>
|
|
1605
|
+
<string>${escapeXml(opts.label)}</string>
|
|
1606
|
+
<key>ProgramArguments</key>
|
|
1607
|
+
<array>
|
|
1608
|
+
<string>${escapeXml(opts.nodePath)}</string>
|
|
1609
|
+
<string>${escapeXml(opts.thinkPath)}</string>
|
|
1610
|
+
<string>curate</string>
|
|
1611
|
+
<string>--if-idle</string>
|
|
1612
|
+
</array>
|
|
1613
|
+
<key>StartInterval</key>
|
|
1614
|
+
<integer>${opts.intervalSeconds}</integer>
|
|
1615
|
+
<key>RunAtLoad</key>
|
|
1616
|
+
<false/>
|
|
1617
|
+
${envBlock} <key>StandardOutPath</key>
|
|
1618
|
+
<string>${escapeXml(opts.logPath)}</string>
|
|
1619
|
+
<key>StandardErrorPath</key>
|
|
1620
|
+
<string>${escapeXml(opts.logPath)}</string>
|
|
1621
|
+
</dict>
|
|
1622
|
+
</plist>
|
|
1623
|
+
`;
|
|
1624
|
+
}
|
|
1625
|
+
function escapeXml(s) {
|
|
1626
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1627
|
+
}
|
|
1628
|
+
function installAgent(opts = {}) {
|
|
1629
|
+
if (process.platform !== "darwin") {
|
|
1630
|
+
throw new Error("auto-curate install currently supports macOS only. For Linux, run `think curate --if-idle` from cron or systemd.");
|
|
1631
|
+
}
|
|
1632
|
+
const label = getAgentLabel();
|
|
1633
|
+
const plistPath = getPlistPath(label);
|
|
1634
|
+
const agentsDir = getLaunchAgentsDir();
|
|
1635
|
+
fs8.mkdirSync(agentsDir, { recursive: true });
|
|
1636
|
+
fs8.mkdirSync(getThinkDir(), { recursive: true });
|
|
1637
|
+
const plist = renderPlist({
|
|
1638
|
+
label,
|
|
1639
|
+
nodePath: resolveNodeBinary(),
|
|
1640
|
+
thinkPath: resolveThinkBinary(),
|
|
1641
|
+
thinkHome: process.env.THINK_HOME,
|
|
1642
|
+
intervalSeconds: opts.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS,
|
|
1643
|
+
logPath: getLogPath()
|
|
1644
|
+
});
|
|
1645
|
+
fs8.writeFileSync(plistPath, plist, { mode: 420 });
|
|
1646
|
+
try {
|
|
1647
|
+
execFileSync("launchctl", ["unload", plistPath], { stdio: "ignore" });
|
|
1648
|
+
} catch {
|
|
1649
|
+
}
|
|
1650
|
+
execFileSync("launchctl", ["load", plistPath], { stdio: "ignore" });
|
|
1651
|
+
return { label, plistPath };
|
|
1652
|
+
}
|
|
1653
|
+
function uninstallAgent() {
|
|
1654
|
+
const plistPath = getPlistPath();
|
|
1655
|
+
if (!fs8.existsSync(plistPath)) {
|
|
1656
|
+
return { removed: false, plistPath };
|
|
1657
|
+
}
|
|
1658
|
+
if (process.platform === "darwin") {
|
|
1659
|
+
try {
|
|
1660
|
+
execFileSync("launchctl", ["unload", plistPath], { stdio: "ignore" });
|
|
1661
|
+
} catch {
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
fs8.unlinkSync(plistPath);
|
|
1665
|
+
return { removed: true, plistPath };
|
|
1666
|
+
}
|
|
1667
|
+
function getAgentStatus() {
|
|
1668
|
+
const label = getAgentLabel();
|
|
1669
|
+
const plistPath = getPlistPath(label);
|
|
1670
|
+
const installed = fs8.existsSync(plistPath);
|
|
1671
|
+
let loaded = false;
|
|
1672
|
+
let intervalSeconds = null;
|
|
1673
|
+
if (installed && process.platform === "darwin") {
|
|
1674
|
+
try {
|
|
1675
|
+
const out = execFileSync("launchctl", ["list", label], { stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
1676
|
+
loaded = out.trim().length > 0;
|
|
1677
|
+
} catch {
|
|
1678
|
+
loaded = false;
|
|
1679
|
+
}
|
|
1680
|
+
try {
|
|
1681
|
+
const plist = fs8.readFileSync(plistPath, "utf-8");
|
|
1682
|
+
const match = plist.match(/<key>StartInterval<\/key>\s*<integer>(\d+)<\/integer>/);
|
|
1683
|
+
if (match) intervalSeconds = parseInt(match[1], 10);
|
|
1684
|
+
} catch {
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
let lastRunAt = null;
|
|
1688
|
+
const logPath = getLogPath();
|
|
1689
|
+
if (fs8.existsSync(logPath)) {
|
|
1690
|
+
try {
|
|
1691
|
+
const stat = fs8.statSync(logPath);
|
|
1692
|
+
lastRunAt = stat.mtime;
|
|
1693
|
+
} catch {
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
return { installed, label, plistPath, loaded, lastRunAt, intervalSeconds };
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1558
1699
|
// src/commands/cortex.ts
|
|
1559
1700
|
function prompt2(question, defaultValue) {
|
|
1560
1701
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -1595,7 +1736,7 @@ cortexCommand.addCommand(new Command9("setup").description("Configure a sync bac
|
|
|
1595
1736
|
const adapter = getSyncAdapter();
|
|
1596
1737
|
if (adapter) {
|
|
1597
1738
|
try {
|
|
1598
|
-
const { ensureRepoCloned: ensureRepoCloned2 } = await import("./git-
|
|
1739
|
+
const { ensureRepoCloned: ensureRepoCloned2 } = await import("./git-CMDUX3KB.js");
|
|
1599
1740
|
ensureRepoCloned2();
|
|
1600
1741
|
console.log(chalk9.green("\u2713") + " Repo cloned");
|
|
1601
1742
|
} catch (err) {
|
|
@@ -1636,8 +1777,8 @@ cortexCommand.addCommand(new Command9("list").description("Show all cortexes").a
|
|
|
1636
1777
|
const config = getConfig();
|
|
1637
1778
|
const engramsDir = getEngramsDir();
|
|
1638
1779
|
const localCortexes = [];
|
|
1639
|
-
if (
|
|
1640
|
-
for (const file of
|
|
1780
|
+
if (fs9.existsSync(engramsDir)) {
|
|
1781
|
+
for (const file of fs9.readdirSync(engramsDir)) {
|
|
1641
1782
|
if (file.endsWith(".db") && !file.endsWith("-shm") && !file.endsWith("-wal")) {
|
|
1642
1783
|
localCortexes.push(file.replace(".db", ""));
|
|
1643
1784
|
}
|
|
@@ -1678,7 +1819,7 @@ cortexCommand.addCommand(new Command9("switch").argument("<name>", "Cortex name"
|
|
|
1678
1819
|
}
|
|
1679
1820
|
const engramsDir = getEngramsDir();
|
|
1680
1821
|
const dbPath = `${engramsDir}/${name}.db`;
|
|
1681
|
-
if (!
|
|
1822
|
+
if (!fs9.existsSync(dbPath)) {
|
|
1682
1823
|
const adapter = getSyncAdapter();
|
|
1683
1824
|
if (adapter?.isAvailable()) {
|
|
1684
1825
|
try {
|
|
@@ -1792,272 +1933,467 @@ cortexCommand.addCommand(new Command9("status").description("Show sync status fo
|
|
|
1792
1933
|
}
|
|
1793
1934
|
closeCortexDb(cortex);
|
|
1794
1935
|
}));
|
|
1936
|
+
var autoCurateCommand = new Command9("auto-curate").description("Manage scheduled background curation (macOS LaunchAgent)");
|
|
1937
|
+
autoCurateCommand.addCommand(new Command9("enable").description("Install a LaunchAgent that runs `think curate --if-idle` every 5 minutes").option("--interval <seconds>", "Scheduler cadence in seconds (default 300)", (v) => parseInt(v, 10)).action((opts) => {
|
|
1938
|
+
try {
|
|
1939
|
+
const { label, plistPath } = installAgent({ intervalSeconds: opts.interval });
|
|
1940
|
+
console.log(chalk9.green("\u2713") + ` Auto-curation enabled`);
|
|
1941
|
+
console.log(chalk9.dim(` Label: ${label}`));
|
|
1942
|
+
console.log(chalk9.dim(` Plist: ${plistPath}`));
|
|
1943
|
+
if (process.env.THINK_HOME) {
|
|
1944
|
+
console.log(chalk9.dim(` THINK_HOME: ${process.env.THINK_HOME}`));
|
|
1945
|
+
}
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
console.error(chalk9.red(err instanceof Error ? err.message : String(err)));
|
|
1948
|
+
process.exit(1);
|
|
1949
|
+
}
|
|
1950
|
+
}));
|
|
1951
|
+
autoCurateCommand.addCommand(new Command9("disable").description("Remove the auto-curation LaunchAgent for this workspace").action(() => {
|
|
1952
|
+
const { removed, plistPath } = uninstallAgent();
|
|
1953
|
+
if (removed) {
|
|
1954
|
+
console.log(chalk9.green("\u2713") + ` Auto-curation disabled (${plistPath})`);
|
|
1955
|
+
} else {
|
|
1956
|
+
console.log(chalk9.dim(`No auto-curation agent installed (${plistPath})`));
|
|
1957
|
+
}
|
|
1958
|
+
}));
|
|
1959
|
+
autoCurateCommand.addCommand(new Command9("status").description("Show auto-curation scheduler status").action(() => {
|
|
1960
|
+
const s = getAgentStatus();
|
|
1961
|
+
console.log(`Label: ${chalk9.cyan(s.label)}`);
|
|
1962
|
+
console.log(`Installed: ${s.installed ? chalk9.green("yes") : chalk9.dim("no")}`);
|
|
1963
|
+
console.log(`Loaded: ${s.loaded ? chalk9.green("yes") : chalk9.dim("no")}`);
|
|
1964
|
+
if (s.intervalSeconds) {
|
|
1965
|
+
console.log(`Interval: ${s.intervalSeconds}s`);
|
|
1966
|
+
}
|
|
1967
|
+
console.log(`Plist: ${s.plistPath}`);
|
|
1968
|
+
if (s.lastRunAt) {
|
|
1969
|
+
console.log(`Last log: ${s.lastRunAt.toISOString()}`);
|
|
1970
|
+
} else {
|
|
1971
|
+
console.log(`Last log: ${chalk9.dim("(no log file yet)")}`);
|
|
1972
|
+
}
|
|
1973
|
+
}));
|
|
1974
|
+
cortexCommand.addCommand(autoCurateCommand);
|
|
1795
1975
|
|
|
1796
1976
|
// src/commands/curate.ts
|
|
1797
1977
|
import { Command as Command10 } from "commander";
|
|
1798
1978
|
import readline3 from "readline";
|
|
1799
1979
|
import chalk10 from "chalk";
|
|
1800
|
-
|
|
1980
|
+
|
|
1981
|
+
// src/lib/curate-lock.ts
|
|
1982
|
+
import fs10 from "fs";
|
|
1983
|
+
import path6 from "path";
|
|
1984
|
+
function getLockPath(cortex) {
|
|
1985
|
+
return path6.join(getThinkDir(), `curate-${cortex}.lock`);
|
|
1986
|
+
}
|
|
1987
|
+
function isProcessAlive(pid) {
|
|
1988
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
1989
|
+
try {
|
|
1990
|
+
process.kill(pid, 0);
|
|
1991
|
+
return true;
|
|
1992
|
+
} catch (err) {
|
|
1993
|
+
const code = err.code;
|
|
1994
|
+
return code === "EPERM";
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
function acquireCurateLock(cortex) {
|
|
1998
|
+
const lockPath = getLockPath(cortex);
|
|
1999
|
+
fs10.mkdirSync(path6.dirname(lockPath), { recursive: true });
|
|
2000
|
+
try {
|
|
2001
|
+
fs10.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
2002
|
+
return makeAcquired(lockPath);
|
|
2003
|
+
} catch (err) {
|
|
2004
|
+
if (err.code !== "EEXIST") throw err;
|
|
2005
|
+
}
|
|
2006
|
+
let heldByPid = null;
|
|
2007
|
+
try {
|
|
2008
|
+
const raw = fs10.readFileSync(lockPath, "utf-8").trim();
|
|
2009
|
+
const parsed = parseInt(raw, 10);
|
|
2010
|
+
if (Number.isFinite(parsed) && parsed > 0) heldByPid = parsed;
|
|
2011
|
+
} catch {
|
|
2012
|
+
}
|
|
2013
|
+
if (heldByPid && isProcessAlive(heldByPid)) {
|
|
2014
|
+
return { acquired: false, heldByPid };
|
|
2015
|
+
}
|
|
2016
|
+
try {
|
|
2017
|
+
fs10.unlinkSync(lockPath);
|
|
2018
|
+
} catch {
|
|
2019
|
+
}
|
|
2020
|
+
try {
|
|
2021
|
+
fs10.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
2022
|
+
return makeAcquired(lockPath);
|
|
2023
|
+
} catch (err) {
|
|
2024
|
+
if (err.code === "EEXIST") {
|
|
2025
|
+
let nowHeldBy = null;
|
|
2026
|
+
try {
|
|
2027
|
+
const raw = fs10.readFileSync(lockPath, "utf-8").trim();
|
|
2028
|
+
const parsed = parseInt(raw, 10);
|
|
2029
|
+
if (Number.isFinite(parsed) && parsed > 0) nowHeldBy = parsed;
|
|
2030
|
+
} catch {
|
|
2031
|
+
}
|
|
2032
|
+
return { acquired: false, heldByPid: nowHeldBy };
|
|
2033
|
+
}
|
|
2034
|
+
throw err;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
function makeAcquired(lockPath) {
|
|
2038
|
+
let released = false;
|
|
2039
|
+
const unlinkIfHeld = () => {
|
|
2040
|
+
if (released) return;
|
|
2041
|
+
released = true;
|
|
2042
|
+
try {
|
|
2043
|
+
fs10.unlinkSync(lockPath);
|
|
2044
|
+
} catch {
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
const exitHandler = () => {
|
|
2048
|
+
unlinkIfHeld();
|
|
2049
|
+
};
|
|
2050
|
+
const sigintHandler = () => {
|
|
2051
|
+
unlinkIfHeld();
|
|
2052
|
+
process.exit(130);
|
|
2053
|
+
};
|
|
2054
|
+
const sigtermHandler = () => {
|
|
2055
|
+
unlinkIfHeld();
|
|
2056
|
+
process.exit(143);
|
|
2057
|
+
};
|
|
2058
|
+
process.on("exit", exitHandler);
|
|
2059
|
+
process.on("SIGINT", sigintHandler);
|
|
2060
|
+
process.on("SIGTERM", sigtermHandler);
|
|
2061
|
+
const release = () => {
|
|
2062
|
+
if (released) {
|
|
2063
|
+
process.removeListener("exit", exitHandler);
|
|
2064
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
2065
|
+
process.removeListener("SIGTERM", sigtermHandler);
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
2068
|
+
unlinkIfHeld();
|
|
2069
|
+
process.removeListener("exit", exitHandler);
|
|
2070
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
2071
|
+
process.removeListener("SIGTERM", sigtermHandler);
|
|
2072
|
+
};
|
|
2073
|
+
return { acquired: true, release };
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
// src/commands/curate.ts
|
|
2077
|
+
var curateCommand = new Command10("curate").description("Run curation: evaluate pending engrams and promote to memories").option("--dry-run", "Preview what would be committed without saving").option("--consolidate", "Run long-term memory consolidation only (no curation)").option("--episode <key>", "Curate a specific episode into a narrative memory").option("--if-idle", "Only curate if the user appears idle (used by auto-curation scheduler)").action(async (opts) => {
|
|
1801
2078
|
const config = getConfig();
|
|
1802
2079
|
const cortex = config.cortex?.active;
|
|
1803
2080
|
if (!cortex) {
|
|
2081
|
+
if (opts.ifIdle) {
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
1804
2084
|
console.error(chalk10.red("No active cortex. Run: think cortex switch <name>"));
|
|
1805
2085
|
process.exit(1);
|
|
1806
2086
|
}
|
|
1807
2087
|
const author = config.cortex.author;
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
2088
|
+
let releaseLock = () => {
|
|
2089
|
+
};
|
|
2090
|
+
if (!opts.dryRun) {
|
|
2091
|
+
const lock = acquireCurateLock(cortex);
|
|
2092
|
+
if (!lock.acquired) {
|
|
2093
|
+
if (opts.ifIdle) {
|
|
2094
|
+
if (process.env.THINK_IDLE_DEBUG) {
|
|
2095
|
+
console.log(chalk10.dim(`[auto-curate] skipped: another curation is running (pid ${lock.heldByPid ?? "?"})`));
|
|
2096
|
+
}
|
|
2097
|
+
} else {
|
|
2098
|
+
console.log(chalk10.yellow(`Another curation is already running (pid ${lock.heldByPid ?? "?"}). Skipping.`));
|
|
1814
2099
|
}
|
|
1815
|
-
} catch {
|
|
1816
|
-
console.log(chalk10.dim(" Sync pull skipped (remote unavailable)"));
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
if (opts.episode) {
|
|
1820
|
-
const episodeEngrams = getPendingEpisodeEngrams(cortex, opts.episode);
|
|
1821
|
-
if (episodeEngrams.length === 0) {
|
|
1822
|
-
console.log(chalk10.dim(`No pending engrams for episode: ${opts.episode}`));
|
|
1823
2100
|
closeCortexDb(cortex);
|
|
1824
2101
|
return;
|
|
1825
2102
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
if (opts.dryRun) {
|
|
1841
|
-
console.log();
|
|
1842
|
-
console.log(chalk10.cyan("Episode prompt would be sent to LLM:"));
|
|
1843
|
-
console.log(chalk10.dim(` ${episodeEngrams.length} engrams, ${existingMemory ? "updating" : "creating"} narrative`));
|
|
1844
|
-
for (const e of episodeEngrams) {
|
|
1845
|
-
const ts = e.created_at.slice(0, 16).replace("T", " ");
|
|
1846
|
-
console.log(chalk10.dim(` ${ts}: ${e.content.slice(0, 100)}${e.content.length > 100 ? "..." : ""}`));
|
|
2103
|
+
releaseLock = lock.release;
|
|
2104
|
+
}
|
|
2105
|
+
try {
|
|
2106
|
+
if (opts.ifIdle && !opts.episode && !opts.consolidate) {
|
|
2107
|
+
const shouldRun = shouldRunIdleCuration(cortex, config.cortex);
|
|
2108
|
+
if (!shouldRun.run) {
|
|
2109
|
+
if (process.env.THINK_IDLE_DEBUG) {
|
|
2110
|
+
console.log(chalk10.dim(`[auto-curate] skipped: ${shouldRun.reason}`));
|
|
2111
|
+
}
|
|
2112
|
+
closeCortexDb(cortex);
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
if (process.env.THINK_IDLE_DEBUG) {
|
|
2116
|
+
console.log(chalk10.dim(`[auto-curate] running: ${shouldRun.reason}`));
|
|
1847
2117
|
}
|
|
1848
|
-
closeCortexDb(cortex);
|
|
1849
|
-
return;
|
|
1850
|
-
}
|
|
1851
|
-
let narrative;
|
|
1852
|
-
try {
|
|
1853
|
-
narrative = await runEpisodeCuration(prompt3);
|
|
1854
|
-
} catch (err) {
|
|
1855
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1856
|
-
console.error(chalk10.red(`Episode curation failed: ${message}`));
|
|
1857
|
-
closeCortexDb(cortex);
|
|
1858
|
-
process.exit(1);
|
|
1859
|
-
}
|
|
1860
|
-
if (existingMemoryRow) {
|
|
1861
|
-
tombstoneMemory(cortex, existingMemoryRow.id);
|
|
1862
2118
|
}
|
|
1863
|
-
const
|
|
1864
|
-
...existingMemory?.source_ids ?? [],
|
|
1865
|
-
...episodeEngrams.map((e) => e.id)
|
|
1866
|
-
];
|
|
1867
|
-
insertMemory(cortex, {
|
|
1868
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1869
|
-
author,
|
|
1870
|
-
content: narrative,
|
|
1871
|
-
source_ids: allSourceIds,
|
|
1872
|
-
episode_key: opts.episode
|
|
1873
|
-
});
|
|
1874
|
-
markPromoted(cortex, episodeEngrams.map((e) => e.id));
|
|
2119
|
+
const adapter = getSyncAdapter();
|
|
1875
2120
|
if (adapter?.isAvailable()) {
|
|
1876
2121
|
try {
|
|
1877
|
-
const
|
|
1878
|
-
if (
|
|
1879
|
-
console.log(chalk10.dim(`
|
|
2122
|
+
const pullResult = await adapter.pull(cortex);
|
|
2123
|
+
if (pullResult.pulled > 0) {
|
|
2124
|
+
console.log(chalk10.dim(` Pulled ${pullResult.pulled} memories from ${adapter.name}`));
|
|
1880
2125
|
}
|
|
1881
2126
|
} catch {
|
|
1882
|
-
console.log(chalk10.dim(" Sync
|
|
2127
|
+
console.log(chalk10.dim(" Sync pull skipped (remote unavailable)"));
|
|
1883
2128
|
}
|
|
1884
2129
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
try {
|
|
1907
|
-
const newSummary = await runConsolidation(longtermSummary, older);
|
|
2130
|
+
if (opts.episode) {
|
|
2131
|
+
const episodeEngrams = getPendingEpisodeEngrams(cortex, opts.episode);
|
|
2132
|
+
if (episodeEngrams.length === 0) {
|
|
2133
|
+
console.log(chalk10.dim(`No pending engrams for episode: ${opts.episode}`));
|
|
2134
|
+
closeCortexDb(cortex);
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
const existingMemoryRow = getMemoryByEpisodeKey(cortex, opts.episode);
|
|
2138
|
+
const existingMemory = existingMemoryRow ? {
|
|
2139
|
+
ts: existingMemoryRow.ts,
|
|
2140
|
+
author: existingMemoryRow.author,
|
|
2141
|
+
content: existingMemoryRow.content,
|
|
2142
|
+
source_ids: JSON.parse(existingMemoryRow.source_ids)
|
|
2143
|
+
} : null;
|
|
2144
|
+
console.log(chalk10.cyan(`Curating episode: ${opts.episode} (${episodeEngrams.length} engrams${existingMemory ? ", updating existing narrative" : ""})...`));
|
|
2145
|
+
const prompt3 = assembleEpisodeCurationPrompt({
|
|
2146
|
+
episodeKey: opts.episode,
|
|
2147
|
+
pendingEngrams: episodeEngrams,
|
|
2148
|
+
existingMemory,
|
|
2149
|
+
author
|
|
2150
|
+
});
|
|
1908
2151
|
if (opts.dryRun) {
|
|
1909
2152
|
console.log();
|
|
1910
|
-
console.log(chalk10.cyan("
|
|
1911
|
-
console.log(
|
|
2153
|
+
console.log(chalk10.cyan("Episode prompt would be sent to LLM:"));
|
|
2154
|
+
console.log(chalk10.dim(` ${episodeEngrams.length} engrams, ${existingMemory ? "updating" : "creating"} narrative`));
|
|
2155
|
+
for (const e of episodeEngrams) {
|
|
2156
|
+
const ts = e.created_at.slice(0, 16).replace("T", " ");
|
|
2157
|
+
console.log(chalk10.dim(` ${ts}: ${e.content.slice(0, 100)}${e.content.length > 100 ? "..." : ""}`));
|
|
2158
|
+
}
|
|
2159
|
+
closeCortexDb(cortex);
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
let narrative;
|
|
2163
|
+
try {
|
|
2164
|
+
narrative = await runEpisodeCuration(prompt3);
|
|
2165
|
+
} catch (err) {
|
|
2166
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2167
|
+
console.error(chalk10.red(`Episode curation failed: ${message}`));
|
|
2168
|
+
closeCortexDb(cortex);
|
|
2169
|
+
process.exit(1);
|
|
2170
|
+
}
|
|
2171
|
+
if (existingMemoryRow) {
|
|
2172
|
+
tombstoneMemory(cortex, existingMemoryRow.id);
|
|
2173
|
+
}
|
|
2174
|
+
const allSourceIds = [
|
|
2175
|
+
...existingMemory?.source_ids ?? [],
|
|
2176
|
+
...episodeEngrams.map((e) => e.id)
|
|
2177
|
+
];
|
|
2178
|
+
insertMemory(cortex, {
|
|
2179
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2180
|
+
author,
|
|
2181
|
+
content: narrative,
|
|
2182
|
+
source_ids: allSourceIds,
|
|
2183
|
+
episode_key: opts.episode
|
|
2184
|
+
});
|
|
2185
|
+
markPromoted(cortex, episodeEngrams.map((e) => e.id));
|
|
2186
|
+
if (adapter?.isAvailable()) {
|
|
2187
|
+
try {
|
|
2188
|
+
const pushResult = await adapter.push(cortex);
|
|
2189
|
+
if (pushResult.pushed > 0) {
|
|
2190
|
+
console.log(chalk10.dim(` Pushed ${pushResult.pushed} memories to ${adapter.name}`));
|
|
2191
|
+
}
|
|
2192
|
+
} catch {
|
|
2193
|
+
console.log(chalk10.dim(" Sync push skipped (remote unavailable)"));
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
console.log();
|
|
2197
|
+
console.log(`${chalk10.green("\u2713")} Episode curated: ${opts.episode}`);
|
|
2198
|
+
console.log(` ${episodeEngrams.length} engrams synthesized into narrative`);
|
|
2199
|
+
closeCortexDb(cortex);
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
const allMemories = getMemories(cortex);
|
|
2203
|
+
const memoryEntries = allMemories.map((m) => ({
|
|
2204
|
+
ts: m.ts,
|
|
2205
|
+
author: m.author,
|
|
2206
|
+
content: m.content,
|
|
2207
|
+
source_ids: JSON.parse(m.source_ids)
|
|
2208
|
+
}));
|
|
2209
|
+
const { recent, older } = filterRecentMemories(memoryEntries);
|
|
2210
|
+
const longtermSummary = getLongtermSummary(cortex);
|
|
2211
|
+
if (opts.consolidate) {
|
|
2212
|
+
if (older.length === 0) {
|
|
2213
|
+
console.log(chalk10.dim("No memories older than 2 weeks to consolidate."));
|
|
1912
2214
|
return;
|
|
1913
2215
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
2216
|
+
console.log(chalk10.cyan(`Consolidating ${older.length} older memories into long-term summary...`));
|
|
2217
|
+
try {
|
|
2218
|
+
const newSummary = await runConsolidation(longtermSummary, older);
|
|
2219
|
+
if (opts.dryRun) {
|
|
2220
|
+
console.log();
|
|
2221
|
+
console.log(chalk10.cyan("Proposed long-term summary:"));
|
|
2222
|
+
console.log(newSummary);
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
setLongtermSummary(cortex, newSummary);
|
|
2226
|
+
console.log(chalk10.green("\u2713") + ` Long-term summary updated (${older.length} memories consolidated)`);
|
|
2227
|
+
} catch (err) {
|
|
2228
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2229
|
+
console.error(chalk10.red(`Consolidation failed: ${message}`));
|
|
2230
|
+
process.exit(1);
|
|
2231
|
+
}
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const pending = getPendingEngrams(cortex);
|
|
2235
|
+
if (pending.length === 0) {
|
|
2236
|
+
console.log(chalk10.dim("No pending engrams to evaluate."));
|
|
2237
|
+
closeCortexDb(cortex);
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
console.log(chalk10.cyan(`Evaluating ${pending.length} engrams (${recent.length} recent memories, long-term summary ${longtermSummary ? "loaded" : "absent"})...`));
|
|
2241
|
+
const curatorMd = readCuratorMd();
|
|
2242
|
+
const curationPrompt = assembleCurationPrompt({
|
|
2243
|
+
recentMemories: recent,
|
|
2244
|
+
longtermSummary,
|
|
2245
|
+
curatorMd,
|
|
2246
|
+
pendingEngrams: pending,
|
|
2247
|
+
author,
|
|
2248
|
+
selectivity: config.cortex?.selectivity,
|
|
2249
|
+
granularity: config.cortex?.granularity,
|
|
2250
|
+
maxMemoriesPerRun: config.cortex?.maxMemoriesPerRun
|
|
2251
|
+
});
|
|
2252
|
+
let curationResult;
|
|
2253
|
+
try {
|
|
2254
|
+
curationResult = await runCuration(curationPrompt);
|
|
1916
2255
|
} catch (err) {
|
|
1917
2256
|
const message = err instanceof Error ? err.message : String(err);
|
|
1918
|
-
console.error(chalk10.red(`
|
|
2257
|
+
console.error(chalk10.red(`Curation failed: ${message}`));
|
|
2258
|
+
closeCortexDb(cortex);
|
|
1919
2259
|
process.exit(1);
|
|
1920
2260
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
console.log(chalk10.dim("No pending engrams to evaluate."));
|
|
1926
|
-
closeCortexDb(cortex);
|
|
1927
|
-
return;
|
|
1928
|
-
}
|
|
1929
|
-
console.log(chalk10.cyan(`Evaluating ${pending.length} engrams (${recent.length} recent memories, long-term summary ${longtermSummary ? "loaded" : "absent"})...`));
|
|
1930
|
-
const curatorMd = readCuratorMd();
|
|
1931
|
-
const curationPrompt = assembleCurationPrompt({
|
|
1932
|
-
recentMemories: recent,
|
|
1933
|
-
longtermSummary,
|
|
1934
|
-
curatorMd,
|
|
1935
|
-
pendingEngrams: pending,
|
|
1936
|
-
author,
|
|
1937
|
-
selectivity: config.cortex?.selectivity,
|
|
1938
|
-
granularity: config.cortex?.granularity,
|
|
1939
|
-
maxMemoriesPerRun: config.cortex?.maxMemoriesPerRun
|
|
1940
|
-
});
|
|
1941
|
-
let curationResult;
|
|
1942
|
-
try {
|
|
1943
|
-
curationResult = await runCuration(curationPrompt);
|
|
1944
|
-
} catch (err) {
|
|
1945
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1946
|
-
console.error(chalk10.red(`Curation failed: ${message}`));
|
|
1947
|
-
closeCortexDb(cortex);
|
|
1948
|
-
process.exit(1);
|
|
1949
|
-
}
|
|
1950
|
-
const newEntries = curationResult.memories;
|
|
1951
|
-
for (const entry of newEntries) {
|
|
1952
|
-
entry.author = author;
|
|
1953
|
-
if (!entry.ts) entry.ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1954
|
-
}
|
|
1955
|
-
const promotedIds = /* @__PURE__ */ new Set();
|
|
1956
|
-
for (const entry of newEntries) {
|
|
1957
|
-
for (const id of entry.source_ids) {
|
|
1958
|
-
promotedIds.add(id);
|
|
2261
|
+
const newEntries = curationResult.memories;
|
|
2262
|
+
for (const entry of newEntries) {
|
|
2263
|
+
entry.author = author;
|
|
2264
|
+
if (!entry.ts) entry.ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1959
2265
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
if (opts.dryRun) {
|
|
1965
|
-
console.log();
|
|
1966
|
-
if (newEntries.length === 0) {
|
|
1967
|
-
console.log(chalk10.dim("Curator would produce no new memories."));
|
|
1968
|
-
} else {
|
|
1969
|
-
console.log(chalk10.cyan("Would append:"));
|
|
1970
|
-
for (const entry of newEntries) {
|
|
1971
|
-
console.log(chalk10.green(` + `) + `[${entry.ts}] ${entry.content}`);
|
|
2266
|
+
const promotedIds = /* @__PURE__ */ new Set();
|
|
2267
|
+
for (const entry of newEntries) {
|
|
2268
|
+
for (const id of entry.source_ids) {
|
|
2269
|
+
promotedIds.add(id);
|
|
1972
2270
|
}
|
|
1973
2271
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
rl.close();
|
|
1990
|
-
resolve(ans.trim().toLowerCase());
|
|
1991
|
-
});
|
|
1992
|
-
});
|
|
1993
|
-
if (answer === "n" || answer === "no") {
|
|
1994
|
-
console.log(chalk10.dim(" Aborted. Engrams left as pending."));
|
|
2272
|
+
const pendingIdSet = new Set(pending.map((e) => e.id));
|
|
2273
|
+
const purgedIds = curationResult.purgeIds.filter((id) => pendingIdSet.has(id) && !promotedIds.has(id));
|
|
2274
|
+
const heldCount = pending.length - promotedIds.size - purgedIds.length;
|
|
2275
|
+
if (opts.dryRun) {
|
|
2276
|
+
console.log();
|
|
2277
|
+
if (newEntries.length === 0) {
|
|
2278
|
+
console.log(chalk10.dim("Curator would produce no new memories."));
|
|
2279
|
+
} else {
|
|
2280
|
+
console.log(chalk10.cyan("Would append:"));
|
|
2281
|
+
for (const entry of newEntries) {
|
|
2282
|
+
console.log(chalk10.green(` + `) + `[${entry.ts}] ${entry.content}`);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
console.log();
|
|
2286
|
+
console.log(`${pending.length} evaluated, ${newEntries.length} would promote, ${purgedIds.length} would purge, ${heldCount} would stay pending`);
|
|
1995
2287
|
closeCortexDb(cortex);
|
|
1996
2288
|
return;
|
|
1997
2289
|
}
|
|
1998
|
-
if (
|
|
2290
|
+
if (config.cortex?.confirmBeforeCommit && newEntries.length > 0) {
|
|
2291
|
+
console.log();
|
|
2292
|
+
console.log(chalk10.cyan("Proposed memories:"));
|
|
1999
2293
|
for (let i = 0; i < newEntries.length; i++) {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2294
|
+
console.log(chalk10.green(` ${i + 1}. `) + newEntries[i].content);
|
|
2295
|
+
}
|
|
2296
|
+
console.log();
|
|
2297
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
2298
|
+
const answer = await new Promise((resolve) => {
|
|
2299
|
+
rl.question(" Save these memories? [Y/n/edit] ", (ans) => {
|
|
2300
|
+
rl.close();
|
|
2301
|
+
resolve(ans.trim().toLowerCase());
|
|
2302
|
+
});
|
|
2303
|
+
});
|
|
2304
|
+
if (answer === "n" || answer === "no") {
|
|
2305
|
+
console.log(chalk10.dim(" Aborted. Engrams left as pending."));
|
|
2306
|
+
closeCortexDb(cortex);
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
if (answer === "e" || answer === "edit") {
|
|
2310
|
+
for (let i = 0; i < newEntries.length; i++) {
|
|
2311
|
+
const editRl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
2312
|
+
const edited = await new Promise((resolve) => {
|
|
2313
|
+
editRl.question(` ${i + 1}. ${chalk10.dim("(enter to keep, or type replacement)")}
|
|
2003
2314
|
${newEntries[i].content}
|
|
2004
2315
|
> `, (ans) => {
|
|
2005
|
-
|
|
2006
|
-
|
|
2316
|
+
editRl.close();
|
|
2317
|
+
resolve(ans.trim());
|
|
2318
|
+
});
|
|
2007
2319
|
});
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2320
|
+
if (edited) {
|
|
2321
|
+
newEntries[i].content = edited;
|
|
2322
|
+
}
|
|
2011
2323
|
}
|
|
2012
2324
|
}
|
|
2013
2325
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2326
|
+
if (newEntries.length > 0) {
|
|
2327
|
+
for (const entry of newEntries) {
|
|
2328
|
+
insertMemory(cortex, {
|
|
2329
|
+
ts: entry.ts,
|
|
2330
|
+
author: entry.author,
|
|
2331
|
+
content: entry.content,
|
|
2332
|
+
source_ids: entry.source_ids,
|
|
2333
|
+
decisions: entry.decisions
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2024
2336
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
markPromoted(cortex, [...promotedIds]);
|
|
2028
|
-
}
|
|
2029
|
-
if (purgedIds.length > 0) {
|
|
2030
|
-
markPurged(cortex, purgedIds);
|
|
2031
|
-
}
|
|
2032
|
-
const pruned = pruneExpiredEngrams(cortex);
|
|
2033
|
-
if (older.length > 0 && !longtermSummary) {
|
|
2034
|
-
console.log(chalk10.dim(` Consolidating ${older.length} older memories into long-term summary...`));
|
|
2035
|
-
try {
|
|
2036
|
-
const newSummary = await runConsolidation(null, older);
|
|
2037
|
-
setLongtermSummary(cortex, newSummary);
|
|
2038
|
-
console.log(chalk10.dim(` Long-term summary created`));
|
|
2039
|
-
} catch {
|
|
2040
|
-
console.log(chalk10.dim(` Long-term consolidation skipped (will retry next run)`));
|
|
2337
|
+
if (promotedIds.size > 0) {
|
|
2338
|
+
markPromoted(cortex, [...promotedIds]);
|
|
2041
2339
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2340
|
+
if (purgedIds.length > 0) {
|
|
2341
|
+
markPurged(cortex, purgedIds);
|
|
2342
|
+
}
|
|
2343
|
+
const pruned = pruneExpiredEngrams(cortex);
|
|
2344
|
+
if (older.length > 0 && !longtermSummary) {
|
|
2345
|
+
console.log(chalk10.dim(` Consolidating ${older.length} older memories into long-term summary...`));
|
|
2346
|
+
try {
|
|
2347
|
+
const newSummary = await runConsolidation(null, older);
|
|
2348
|
+
setLongtermSummary(cortex, newSummary);
|
|
2349
|
+
console.log(chalk10.dim(` Long-term summary created`));
|
|
2350
|
+
} catch {
|
|
2351
|
+
console.log(chalk10.dim(` Long-term consolidation skipped (will retry next run)`));
|
|
2048
2352
|
}
|
|
2049
|
-
} catch {
|
|
2050
|
-
console.log(chalk10.dim(" Sync push skipped (remote unavailable) \u2014 will push on next sync"));
|
|
2051
2353
|
}
|
|
2354
|
+
if (adapter?.isAvailable() && newEntries.length > 0) {
|
|
2355
|
+
try {
|
|
2356
|
+
const pushResult = await adapter.push(cortex);
|
|
2357
|
+
if (pushResult.pushed > 0) {
|
|
2358
|
+
console.log(chalk10.dim(` Pushed ${pushResult.pushed} memories to ${adapter.name}`));
|
|
2359
|
+
}
|
|
2360
|
+
} catch {
|
|
2361
|
+
console.log(chalk10.dim(" Sync push skipped (remote unavailable) \u2014 will push on next sync"));
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
console.log();
|
|
2365
|
+
console.log(`${chalk10.green("\u2713")} Curation complete`);
|
|
2366
|
+
console.log(` ${pending.length} evaluated, ${newEntries.length} promoted, ${purgedIds.length} purged, ${heldCount} still pending`);
|
|
2367
|
+
if (pruned > 0) {
|
|
2368
|
+
console.log(` ${pruned} expired engrams pruned`);
|
|
2369
|
+
}
|
|
2370
|
+
closeCortexDb(cortex);
|
|
2371
|
+
} finally {
|
|
2372
|
+
releaseLock();
|
|
2052
2373
|
}
|
|
2053
|
-
console.log();
|
|
2054
|
-
console.log(`${chalk10.green("\u2713")} Curation complete`);
|
|
2055
|
-
console.log(` ${pending.length} evaluated, ${newEntries.length} promoted, ${purgedIds.length} purged, ${heldCount} still pending`);
|
|
2056
|
-
if (pruned > 0) {
|
|
2057
|
-
console.log(` ${pruned} expired engrams pruned`);
|
|
2058
|
-
}
|
|
2059
|
-
closeCortexDb(cortex);
|
|
2060
2374
|
});
|
|
2375
|
+
var DEFAULT_IDLE_WINDOW_MINUTES = 3;
|
|
2376
|
+
var DEFAULT_STALE_WINDOW_MINUTES = 60;
|
|
2377
|
+
function shouldRunIdleCuration(cortex, cortexConfig) {
|
|
2378
|
+
const pending = getPendingEngrams(cortex);
|
|
2379
|
+
if (pending.length === 0) {
|
|
2380
|
+
return { run: false, reason: "no pending engrams" };
|
|
2381
|
+
}
|
|
2382
|
+
const idleMinutes = cortexConfig?.idleWindowMinutes ?? DEFAULT_IDLE_WINDOW_MINUTES;
|
|
2383
|
+
const staleMinutes = cortexConfig?.staleWindowMinutes ?? DEFAULT_STALE_WINDOW_MINUTES;
|
|
2384
|
+
const now = Date.now();
|
|
2385
|
+
const oldest = pending[0];
|
|
2386
|
+
const newest = pending[pending.length - 1];
|
|
2387
|
+
const oldestAgeMin = (now - new Date(oldest.created_at).getTime()) / 6e4;
|
|
2388
|
+
const newestAgeMin = (now - new Date(newest.created_at).getTime()) / 6e4;
|
|
2389
|
+
if (oldestAgeMin >= staleMinutes) {
|
|
2390
|
+
return { run: true, reason: `staleness cap hit (oldest pending ${oldestAgeMin.toFixed(1)}min old)` };
|
|
2391
|
+
}
|
|
2392
|
+
if (newestAgeMin < idleMinutes) {
|
|
2393
|
+
return { run: false, reason: `still active (newest engram ${newestAgeMin.toFixed(1)}min old, idle threshold ${idleMinutes}min)` };
|
|
2394
|
+
}
|
|
2395
|
+
return { run: true, reason: `idle (${pending.length} pending, newest ${newestAgeMin.toFixed(1)}min old)` };
|
|
2396
|
+
}
|
|
2061
2397
|
|
|
2062
2398
|
// src/commands/monitor.ts
|
|
2063
2399
|
import { Command as Command11 } from "commander";
|
|
@@ -2122,7 +2458,7 @@ var recallCommand = new Command12("recall").argument("<query>", "What to recall"
|
|
|
2122
2458
|
}
|
|
2123
2459
|
const limit = parseInt(opts.limit, 10);
|
|
2124
2460
|
if (opts.all) {
|
|
2125
|
-
const { getMemories: getMemories2 } = await import("./memory-queries-
|
|
2461
|
+
const { getMemories: getMemories2 } = await import("./memory-queries-QKGOKRFR.js");
|
|
2126
2462
|
const days = parseInt(opts.days, 10);
|
|
2127
2463
|
const cutoff = new Date(Date.now() - days * 864e5).toISOString();
|
|
2128
2464
|
const recentMemories = getMemories2(cortex, { since: cutoff });
|
|
@@ -2278,7 +2614,7 @@ memoryCommand.addCommand(addCommand);
|
|
|
2278
2614
|
// src/commands/curator-cmd.ts
|
|
2279
2615
|
import { Command as Command14 } from "commander";
|
|
2280
2616
|
import { spawnSync } from "child_process";
|
|
2281
|
-
import
|
|
2617
|
+
import fs11 from "fs";
|
|
2282
2618
|
import chalk14 from "chalk";
|
|
2283
2619
|
var CURATOR_TEMPLATE = `# Curator Guidance
|
|
2284
2620
|
|
|
@@ -2297,8 +2633,8 @@ var curatorCommand = new Command14("curator").description("Manage personal curat
|
|
|
2297
2633
|
curatorCommand.addCommand(new Command14("edit").description("Edit your curator guidance in $EDITOR").action(() => {
|
|
2298
2634
|
ensureThinkDirs();
|
|
2299
2635
|
const mdPath = getCuratorMdPath();
|
|
2300
|
-
if (!
|
|
2301
|
-
|
|
2636
|
+
if (!fs11.existsSync(mdPath)) {
|
|
2637
|
+
fs11.writeFileSync(mdPath, CURATOR_TEMPLATE, "utf-8");
|
|
2302
2638
|
}
|
|
2303
2639
|
const editor = process.env.EDITOR || "vi";
|
|
2304
2640
|
const result = spawnSync(editor, [mdPath], { stdio: "inherit" });
|
|
@@ -2310,8 +2646,8 @@ curatorCommand.addCommand(new Command14("edit").description("Edit your curator g
|
|
|
2310
2646
|
}));
|
|
2311
2647
|
curatorCommand.addCommand(new Command14("show").description("Print your current curator guidance").action(() => {
|
|
2312
2648
|
const mdPath = getCuratorMdPath();
|
|
2313
|
-
if (
|
|
2314
|
-
console.log(
|
|
2649
|
+
if (fs11.existsSync(mdPath)) {
|
|
2650
|
+
console.log(fs11.readFileSync(mdPath, "utf-8"));
|
|
2315
2651
|
} else {
|
|
2316
2652
|
console.log(chalk14.dim("No curator guidance configured. Run: think curator edit"));
|
|
2317
2653
|
}
|
|
@@ -2375,6 +2711,8 @@ var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
2375
2711
|
"cortex.repo",
|
|
2376
2712
|
"cortex.active",
|
|
2377
2713
|
"cortex.engramTTLDays",
|
|
2714
|
+
"cortex.idleWindowMinutes",
|
|
2715
|
+
"cortex.staleWindowMinutes",
|
|
2378
2716
|
"paused"
|
|
2379
2717
|
]);
|
|
2380
2718
|
var configCommand = new Command17("config").description("View or update think configuration");
|
|
@@ -2408,12 +2746,12 @@ configCommand.addCommand(new Command17("set").argument("<key>", "Config key (e.g
|
|
|
2408
2746
|
|
|
2409
2747
|
// src/commands/update.ts
|
|
2410
2748
|
import { Command as Command18 } from "commander";
|
|
2411
|
-
import { execFileSync } from "child_process";
|
|
2749
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2412
2750
|
import chalk18 from "chalk";
|
|
2413
2751
|
var updateCommand = new Command18("update").description("Update think to the latest version").action(() => {
|
|
2414
2752
|
console.log(chalk18.cyan("Checking for updates..."));
|
|
2415
2753
|
try {
|
|
2416
|
-
const result =
|
|
2754
|
+
const result = execFileSync2("npm", ["install", "-g", "open-think@latest"], {
|
|
2417
2755
|
encoding: "utf-8",
|
|
2418
2756
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2419
2757
|
});
|
|
@@ -2431,7 +2769,7 @@ var updateCommand = new Command18("update").description("Update think to the lat
|
|
|
2431
2769
|
|
|
2432
2770
|
// src/commands/migrate-data.ts
|
|
2433
2771
|
import { Command as Command19 } from "commander";
|
|
2434
|
-
import
|
|
2772
|
+
import fs12 from "fs";
|
|
2435
2773
|
import chalk19 from "chalk";
|
|
2436
2774
|
var migrateDataCommand = new Command19("migrate-data").description("Import existing memories from git into local SQLite (one-time migration)").action(async () => {
|
|
2437
2775
|
const config = getConfig();
|
|
@@ -2469,8 +2807,8 @@ var migrateDataCommand = new Command19("migrate-data").description("Import exist
|
|
|
2469
2807
|
if (wasInserted) inserted++;
|
|
2470
2808
|
}
|
|
2471
2809
|
const ltPath = getLongtermPath(cortex);
|
|
2472
|
-
if (
|
|
2473
|
-
const ltContent =
|
|
2810
|
+
if (fs12.existsSync(ltPath)) {
|
|
2811
|
+
const ltContent = fs12.readFileSync(ltPath, "utf-8").trim();
|
|
2474
2812
|
if (ltContent) {
|
|
2475
2813
|
setLongtermSummary(cortex, ltContent);
|
|
2476
2814
|
console.log(chalk19.green(" \u2713") + " Long-term summary migrated");
|
|
@@ -2489,8 +2827,8 @@ var migrateDataCommand = new Command19("migrate-data").description("Import exist
|
|
|
2489
2827
|
// src/index.ts
|
|
2490
2828
|
function readPackageVersion() {
|
|
2491
2829
|
try {
|
|
2492
|
-
const pkgPath =
|
|
2493
|
-
return JSON.parse(
|
|
2830
|
+
const pkgPath = path7.join(import.meta.dirname, "..", "package.json");
|
|
2831
|
+
return JSON.parse(fs13.readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
|
|
2494
2832
|
} catch {
|
|
2495
2833
|
return "0.0.0";
|
|
2496
2834
|
}
|