-
Notifications
You must be signed in to change notification settings - Fork 213
Expand file tree
/
Copy pathsession-tail-sections.ts
More file actions
139 lines (131 loc) · 4.28 KB
/
Copy pathsession-tail-sections.ts
File metadata and controls
139 lines (131 loc) · 4.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import type { SessionState } from "../session/session-state.js";
import { truncateToTokens } from "./token-budget.js";
export interface SessionSectionParts {
/** Body only (no `###` header) for the tail, after the shared cap. */
loaded: string | null;
/** Body only (no `###` header) for the tail, after the shared cap. */
facts: string | null;
/** String passed to `checkBudget` for `loadedSkills` (no header, matches tail body). */
budgetLoaded: string;
/** String passed to `checkBudget` for `sessionFacts` (no header). */
budgetFacts: string;
truncationLoaded: boolean;
truncationFacts: boolean;
}
/**
* Renders and applies the shared `limits.session` cap. Order for truncation:
* `### session-facts` first, `### loaded-skills` second (legacy: facts
* before skills) so the tail of the combined blob is cut from skills first.
*/
export function buildSessionSectionParts(
session: SessionState,
sessionTokenLimit: number,
): SessionSectionParts {
const empty: SessionSectionParts = {
loaded: null,
facts: null,
budgetLoaded: "",
budgetFacts: "",
truncationLoaded: false,
truncationFacts: false,
};
const bodyLoaded = renderLoadedSkillsBody(session);
const bodyFacts = renderSessionFactsBody(session);
if (bodyLoaded.length === 0 && bodyFacts.length === 0) {
return empty;
}
const withFactsHeader = bodyFacts.length
? `### session-facts\n${bodyFacts}`
: "";
const withLoadedHeader = bodyLoaded.length
? `### loaded-skills\n${bodyLoaded}`
: "";
if (!withFactsHeader) {
const t = truncateToTokens(withLoadedHeader, sessionTokenLimit);
const loadedBody = t.startsWith("### loaded-skills\n")
? t.slice("### loaded-skills\n".length)
: t;
return {
loaded: loadedBody,
facts: null,
budgetLoaded: loadedBody,
budgetFacts: "",
truncationLoaded: t !== withLoadedHeader,
truncationFacts: false,
};
}
if (!withLoadedHeader) {
const t = truncateToTokens(withFactsHeader, sessionTokenLimit);
const factsOut = t.startsWith("### session-facts\n")
? t.slice("### session-facts\n".length)
: t;
return {
loaded: null,
facts: factsOut,
budgetLoaded: "",
budgetFacts: factsOut,
truncationLoaded: false,
truncationFacts: t !== withFactsHeader,
};
}
const combined = `${withFactsHeader}\n\n${withLoadedHeader}`;
const truncated = truncateToTokens(combined, sessionTokenLimit);
const marker = "\n\n### loaded-skills\n";
const at = truncated.indexOf(marker);
if (at < 0) {
if (truncated.startsWith("### session-facts\n")) {
const factsOut = truncated.slice("### session-facts\n".length);
return {
loaded: null,
facts: factsOut,
budgetLoaded: "",
budgetFacts: factsOut,
truncationLoaded: bodyLoaded.length > 0,
truncationFacts: factsOut !== bodyFacts,
};
}
if (truncated.startsWith("### loaded-skills\n")) {
const lOut = truncated.slice("### loaded-skills\n".length);
return {
loaded: lOut,
facts: null,
budgetLoaded: lOut,
budgetFacts: "",
truncationLoaded: lOut !== bodyLoaded,
truncationFacts: false,
};
}
return {
loaded: null,
facts: null,
budgetLoaded: "",
budgetFacts: "",
truncationLoaded: bodyLoaded.length > 0,
truncationFacts: bodyFacts.length > 0,
};
}
const fBlock = truncated.slice(0, at);
const lBlock = truncated.slice(at + marker.length);
const factsStripped = fBlock.startsWith("### session-facts\n")
? fBlock.slice("### session-facts\n".length)
: fBlock;
return {
loaded: lBlock,
facts: factsStripped,
budgetLoaded: lBlock,
budgetFacts: factsStripped,
truncationLoaded: lBlock !== bodyLoaded,
truncationFacts: factsStripped !== bodyFacts,
};
}
function renderLoadedSkillsBody(session: SessionState): string {
if (session.loadedSkills.length === 0) return "";
return session.loadedSkills
.map((s) => `--- skill:${s.name} v${s.version} ---\n${s.body}`)
.join("\n\n");
}
function renderSessionFactsBody(session: SessionState): string {
const recent = session.knownFacts.slice(-8);
if (recent.length === 0) return "";
return `known facts:\n${recent.map((f) => `- ${f.text}`).join("\n")}`;
}