@tekmidian/pai 0.7.3 → 0.7.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/cli/index.mjs +1 -1
- package/dist/daemon/index.mjs +1 -1
- package/dist/{daemon-D8ZxcFhU.mjs → daemon-D8qqODkf.mjs} +36 -22
- package/dist/daemon-D8qqODkf.mjs.map +1 -0
- package/dist/hooks/load-project-context.mjs +41 -24
- package/dist/hooks/load-project-context.mjs.map +2 -2
- package/package.json +1 -1
- package/src/hooks/ts/session-start/load-project-context.ts +63 -29
- package/dist/daemon-D8ZxcFhU.mjs.map +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -3793,7 +3793,7 @@ function cmdLogs(opts) {
|
|
|
3793
3793
|
}
|
|
3794
3794
|
function registerDaemonCommands(daemonCmd) {
|
|
3795
3795
|
daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
|
|
3796
|
-
const { serve } = await import("../daemon-
|
|
3796
|
+
const { serve } = await import("../daemon-D8qqODkf.mjs").then((n) => n.t);
|
|
3797
3797
|
const { loadConfig: lc, ensureConfigDir } = await import("../config-BuhHWyOK.mjs").then((n) => n.r);
|
|
3798
3798
|
ensureConfigDir();
|
|
3799
3799
|
await serve(lc());
|
package/dist/daemon/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import "../indexer-D53l5d1U.mjs";
|
|
|
8
8
|
import { t as PaiClient } from "../ipc-client-CoyUHPod.mjs";
|
|
9
9
|
import { i as ensureConfigDir, o as loadConfig } from "../config-BuhHWyOK.mjs";
|
|
10
10
|
import "../factory-Ygqe_bVZ.mjs";
|
|
11
|
-
import { n as serve } from "../daemon-
|
|
11
|
+
import { n as serve } from "../daemon-D8qqODkf.mjs";
|
|
12
12
|
import "../state-C6_vqz7w.mjs";
|
|
13
13
|
import "../tools-DcaJlYDN.mjs";
|
|
14
14
|
import "../detector-jGBuYQJM.mjs";
|
|
@@ -1480,7 +1480,7 @@ ${filesSection ? `\nFILES MODIFIED:\n${filesSection}` : ""}`;
|
|
|
1480
1480
|
* 1. Finding the current session's JSONL transcript
|
|
1481
1481
|
* 2. Extracting user messages and assistant context
|
|
1482
1482
|
* 3. Gathering git commits from the session period
|
|
1483
|
-
* 4. Spawning
|
|
1483
|
+
* 4. Spawning Claude (sonnet for compaction, opus for session end) to generate a structured summary
|
|
1484
1484
|
* 5. Writing the summary to the project's session note
|
|
1485
1485
|
*
|
|
1486
1486
|
* Designed to run inside the daemon's work queue worker. All errors are
|
|
@@ -1488,12 +1488,21 @@ ${filesSection ? `\nFILES MODIFIED:\n${filesSection}` : ""}`;
|
|
|
1488
1488
|
*/
|
|
1489
1489
|
/** Minimum interval between summaries for the same project (ms). */
|
|
1490
1490
|
const SUMMARY_COOLDOWN_MS = 1800 * 1e3;
|
|
1491
|
-
/** Maximum JSONL content to feed to
|
|
1492
|
-
|
|
1491
|
+
/** Maximum JSONL content to feed to the summarizer (characters). */
|
|
1492
|
+
/** Max JSONL chars per model. Opus/Sonnet can handle much more than Haiku. */
|
|
1493
|
+
const MAX_JSONL_CHARS = {
|
|
1494
|
+
haiku: 5e4,
|
|
1495
|
+
sonnet: 2e5,
|
|
1496
|
+
opus: 5e5
|
|
1497
|
+
};
|
|
1493
1498
|
/** Maximum user messages to include in the prompt. */
|
|
1494
1499
|
const MAX_USER_MESSAGES = 30;
|
|
1495
1500
|
/** Timeout for the claude CLI process (ms). */
|
|
1496
|
-
const CLAUDE_TIMEOUT_MS =
|
|
1501
|
+
const CLAUDE_TIMEOUT_MS = {
|
|
1502
|
+
haiku: 6e4,
|
|
1503
|
+
sonnet: 12e4,
|
|
1504
|
+
opus: 3e5
|
|
1505
|
+
};
|
|
1497
1506
|
/** File tracking last summary timestamps per project. */
|
|
1498
1507
|
const COOLDOWN_FILE = join(homedir(), ".config", "pai", "summary-cooldowns.json");
|
|
1499
1508
|
/** Claude Code projects directory. */
|
|
@@ -1578,9 +1587,9 @@ function findLatestJsonl(cwd) {
|
|
|
1578
1587
|
}
|
|
1579
1588
|
/**
|
|
1580
1589
|
* Parse a JSONL transcript and extract relevant content.
|
|
1581
|
-
* Filters noise, truncates to
|
|
1590
|
+
* Filters noise, truncates to model-appropriate size from the end of the file.
|
|
1582
1591
|
*/
|
|
1583
|
-
function extractFromJsonl(jsonlPath) {
|
|
1592
|
+
function extractFromJsonl(jsonlPath, model = "sonnet") {
|
|
1584
1593
|
const result = {
|
|
1585
1594
|
userMessages: [],
|
|
1586
1595
|
filesModified: [],
|
|
@@ -1592,8 +1601,9 @@ function extractFromJsonl(jsonlPath) {
|
|
|
1592
1601
|
} catch (e) {
|
|
1593
1602
|
throw new Error(`Could not read JSONL at ${jsonlPath}: ${e}`);
|
|
1594
1603
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1604
|
+
const maxChars = MAX_JSONL_CHARS[model] ?? 2e5;
|
|
1605
|
+
if (raw.length > maxChars) {
|
|
1606
|
+
const truncPoint = raw.indexOf("\n", raw.length - maxChars);
|
|
1597
1607
|
raw = truncPoint >= 0 ? raw.slice(truncPoint + 1) : raw.slice(-MAX_JSONL_CHARS);
|
|
1598
1608
|
}
|
|
1599
1609
|
const lines = raw.trim().split("\n");
|
|
@@ -1707,11 +1717,14 @@ function findClaudeBinary() {
|
|
|
1707
1717
|
return null;
|
|
1708
1718
|
}
|
|
1709
1719
|
/**
|
|
1710
|
-
* Spawn
|
|
1711
|
-
* Pipes the prompt via stdin
|
|
1720
|
+
* Spawn a Claude model via the CLI to generate a session summary.
|
|
1721
|
+
* Pipes the prompt via stdin. Model selection:
|
|
1722
|
+
* - opus: session end (best quality for final summary, runs once)
|
|
1723
|
+
* - sonnet: auto-compaction (good quality for incremental checkpoints, runs often)
|
|
1724
|
+
* - haiku: fallback / budget mode
|
|
1712
1725
|
* Returns the generated text, or null if spawning fails.
|
|
1713
1726
|
*/
|
|
1714
|
-
async function
|
|
1727
|
+
async function spawnSummarizer(prompt, model = "sonnet") {
|
|
1715
1728
|
const claudeBin = findClaudeBinary();
|
|
1716
1729
|
if (!claudeBin) {
|
|
1717
1730
|
process.stderr.write("[session-summary] Claude CLI not found in PATH or common locations.\n");
|
|
@@ -1722,7 +1735,7 @@ async function spawnHaikuSummarizer(prompt) {
|
|
|
1722
1735
|
let timer = null;
|
|
1723
1736
|
const child = spawn(claudeBin, [
|
|
1724
1737
|
"--model",
|
|
1725
|
-
|
|
1738
|
+
model,
|
|
1726
1739
|
"-p",
|
|
1727
1740
|
"--no-session-persistence"
|
|
1728
1741
|
], {
|
|
@@ -1746,7 +1759,7 @@ async function spawnHaikuSummarizer(prompt) {
|
|
|
1746
1759
|
clearTimeout(timer);
|
|
1747
1760
|
timer = null;
|
|
1748
1761
|
}
|
|
1749
|
-
process.stderr.write(`[session-summary]
|
|
1762
|
+
process.stderr.write(`[session-summary] ${model} spawn error: ${err.message}\n`);
|
|
1750
1763
|
resolve(null);
|
|
1751
1764
|
});
|
|
1752
1765
|
child.on("close", (code) => {
|
|
@@ -1755,15 +1768,15 @@ async function spawnHaikuSummarizer(prompt) {
|
|
|
1755
1768
|
timer = null;
|
|
1756
1769
|
}
|
|
1757
1770
|
if (code !== 0) {
|
|
1758
|
-
process.stderr.write(`[session-summary]
|
|
1771
|
+
process.stderr.write(`[session-summary] ${model} exited with code ${code}: ${stderr.slice(0, 300)}\n`);
|
|
1759
1772
|
resolve(null);
|
|
1760
1773
|
} else resolve(stdout.trim() || null);
|
|
1761
1774
|
});
|
|
1762
1775
|
timer = setTimeout(() => {
|
|
1763
|
-
process.stderr.write(
|
|
1776
|
+
process.stderr.write(`[session-summary] ${model} timed out — killing process.\n`);
|
|
1764
1777
|
child.kill("SIGTERM");
|
|
1765
1778
|
resolve(null);
|
|
1766
|
-
}, CLAUDE_TIMEOUT_MS);
|
|
1779
|
+
}, CLAUDE_TIMEOUT_MS[model] ?? 12e4);
|
|
1767
1780
|
child.stdin.write(prompt);
|
|
1768
1781
|
child.stdin.end();
|
|
1769
1782
|
});
|
|
@@ -1896,7 +1909,8 @@ async function handleSessionSummary(payload) {
|
|
|
1896
1909
|
return;
|
|
1897
1910
|
}
|
|
1898
1911
|
process.stderr.write(`[session-summary] Using transcript: ${jsonlPath}\n`);
|
|
1899
|
-
const
|
|
1912
|
+
const selectedModel = payload.model ?? (force ? "opus" : "sonnet");
|
|
1913
|
+
const extracted = extractFromJsonl(jsonlPath, selectedModel);
|
|
1900
1914
|
if (extracted.userMessages.length === 0) {
|
|
1901
1915
|
process.stderr.write("[session-summary] No user messages found in transcript — skipping.\n");
|
|
1902
1916
|
return;
|
|
@@ -1921,14 +1935,14 @@ async function handleSessionSummary(payload) {
|
|
|
1921
1935
|
filesModified: extracted.filesModified,
|
|
1922
1936
|
existingNote
|
|
1923
1937
|
});
|
|
1924
|
-
process.stderr.write(`[session-summary] Sending ${prompt.length} char prompt to
|
|
1925
|
-
const summaryText = await
|
|
1938
|
+
process.stderr.write(`[session-summary] Sending ${prompt.length} char prompt to ${selectedModel}...\n`);
|
|
1939
|
+
const summaryText = await spawnSummarizer(prompt, selectedModel);
|
|
1926
1940
|
if (!summaryText) {
|
|
1927
|
-
process.stderr.write(
|
|
1941
|
+
process.stderr.write(`[session-summary] ${selectedModel} did not produce output — falling back to mechanical checkpoint.\n`);
|
|
1928
1942
|
markCooldown(cwd);
|
|
1929
1943
|
return;
|
|
1930
1944
|
}
|
|
1931
|
-
process.stderr.write(`[session-summary]
|
|
1945
|
+
process.stderr.write(`[session-summary] ${selectedModel} produced ${summaryText.length} char summary.\n`);
|
|
1932
1946
|
const notePath = writeSessionNote(cwd, summaryText, extracted.filesModified);
|
|
1933
1947
|
if (notePath) process.stderr.write(`[session-summary] Session note written: ${basename(notePath)}\n`);
|
|
1934
1948
|
markCooldown(cwd);
|
|
@@ -2755,4 +2769,4 @@ var daemon_exports = /* @__PURE__ */ __exportAll({ serve: () => serve });
|
|
|
2755
2769
|
|
|
2756
2770
|
//#endregion
|
|
2757
2771
|
export { serve as n, daemon_exports as t };
|
|
2758
|
-
//# sourceMappingURL=daemon-
|
|
2772
|
+
//# sourceMappingURL=daemon-D8qqODkf.mjs.map
|