opencode-graphiti 0.1.5 → 0.1.6
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/esm/src/config.d.ts.map +1 -1
- package/esm/src/config.js +4 -2
- package/esm/src/handlers/chat.d.ts +2 -1
- package/esm/src/handlers/chat.d.ts.map +1 -1
- package/esm/src/handlers/chat.js +89 -49
- package/esm/src/handlers/compacting.d.ts +1 -0
- package/esm/src/handlers/compacting.d.ts.map +1 -1
- package/esm/src/handlers/compacting.js +2 -1
- package/esm/src/handlers/event.d.ts +2 -2
- package/esm/src/handlers/event.d.ts.map +1 -1
- package/esm/src/handlers/event.js +52 -1
- package/esm/src/handlers/system.d.ts +11 -0
- package/esm/src/handlers/system.d.ts.map +1 -0
- package/esm/src/handlers/system.js +18 -0
- package/esm/src/index.d.ts.map +1 -1
- package/esm/src/index.js +8 -2
- package/esm/src/services/client.d.ts +8 -1
- package/esm/src/services/client.d.ts.map +1 -1
- package/esm/src/services/client.js +23 -0
- package/esm/src/services/compaction.d.ts +10 -0
- package/esm/src/services/compaction.d.ts.map +1 -1
- package/esm/src/services/compaction.js +106 -18
- package/esm/src/services/context-limit.d.ts +2 -8
- package/esm/src/services/context-limit.d.ts.map +1 -1
- package/esm/src/services/context-limit.js +3 -1
- package/esm/src/services/context.d.ts +27 -2
- package/esm/src/services/context.d.ts.map +1 -1
- package/esm/src/services/context.js +107 -13
- package/esm/src/services/logger.d.ts.map +1 -1
- package/esm/src/services/logger.js +1 -2
- package/esm/src/session.d.ts +6 -25
- package/esm/src/session.d.ts.map +1 -1
- package/esm/src/session.js +4 -7
- package/esm/src/types/index.d.ts +8 -2
- package/esm/src/types/index.d.ts.map +1 -1
- package/package.json +2 -3
- package/script/src/config.d.ts.map +1 -1
- package/script/src/config.js +4 -2
- package/script/src/handlers/chat.d.ts +2 -1
- package/script/src/handlers/chat.d.ts.map +1 -1
- package/script/src/handlers/chat.js +88 -48
- package/script/src/handlers/compacting.d.ts +1 -0
- package/script/src/handlers/compacting.d.ts.map +1 -1
- package/script/src/handlers/compacting.js +2 -1
- package/script/src/handlers/event.d.ts +2 -2
- package/script/src/handlers/event.d.ts.map +1 -1
- package/script/src/handlers/event.js +52 -1
- package/script/src/handlers/system.d.ts +11 -0
- package/script/src/handlers/system.d.ts.map +1 -0
- package/script/src/handlers/system.js +21 -0
- package/script/src/index.d.ts.map +1 -1
- package/script/src/index.js +8 -2
- package/script/src/services/client.d.ts +8 -1
- package/script/src/services/client.d.ts.map +1 -1
- package/script/src/services/client.js +23 -0
- package/script/src/services/compaction.d.ts +10 -0
- package/script/src/services/compaction.d.ts.map +1 -1
- package/script/src/services/compaction.js +108 -17
- package/script/src/services/context-limit.d.ts +2 -8
- package/script/src/services/context-limit.d.ts.map +1 -1
- package/script/src/services/context-limit.js +3 -1
- package/script/src/services/context.d.ts +27 -2
- package/script/src/services/context.d.ts.map +1 -1
- package/script/src/services/context.js +118 -14
- package/script/src/services/logger.d.ts.map +1 -1
- package/script/src/services/logger.js +1 -35
- package/script/src/session.d.ts +6 -25
- package/script/src/session.d.ts.map +1 -1
- package/script/src/session.js +4 -7
- package/script/src/types/index.d.ts +8 -2
- package/script/src/types/index.d.ts.map +1 -1
- package/esm/_dnt.shims.d.ts +0 -6
- package/esm/_dnt.shims.d.ts.map +0 -1
- package/esm/_dnt.shims.js +0 -61
- package/script/_dnt.shims.d.ts +0 -6
- package/script/_dnt.shims.d.ts.map +0 -1
- package/script/_dnt.shims.js +0 -65
|
@@ -7,17 +7,8 @@ const logger_js_1 = require("../services/logger.js");
|
|
|
7
7
|
const utils_js_1 = require("../utils.js");
|
|
8
8
|
/** Creates the `chat.message` hook handler. */
|
|
9
9
|
function createChatHandler(deps) {
|
|
10
|
-
const { sessionManager,
|
|
11
|
-
const
|
|
12
|
-
if (part.type !== "text")
|
|
13
|
-
return true;
|
|
14
|
-
if (part.id?.startsWith("graphiti-memory-"))
|
|
15
|
-
return false;
|
|
16
|
-
if (part.id?.startsWith("graphiti-refresh-"))
|
|
17
|
-
return false;
|
|
18
|
-
return true;
|
|
19
|
-
});
|
|
20
|
-
const injectMemoryContext = async (state, messageText, output, prefix, useUserScope, characterBudget, shouldReinject) => {
|
|
10
|
+
const { sessionManager, driftThreshold, factStaleDays, client } = deps;
|
|
11
|
+
const searchAndCacheMemoryContext = async (state, messageText, useUserScope, characterBudget, seedFactUuids) => {
|
|
21
12
|
const userGroupId = state.userGroupId;
|
|
22
13
|
const projectFactsPromise = client.searchFacts({
|
|
23
14
|
query: messageText,
|
|
@@ -50,45 +41,83 @@ function createChatHandler(deps) {
|
|
|
50
41
|
userFactsPromise,
|
|
51
42
|
userNodesPromise,
|
|
52
43
|
]);
|
|
53
|
-
const projectContext = (0, context_js_1.
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
const projectContext = (0, context_js_1.deduplicateContext)({
|
|
45
|
+
facts: projectFacts,
|
|
46
|
+
nodes: projectNodes,
|
|
47
|
+
});
|
|
48
|
+
const userContext = (0, context_js_1.deduplicateContext)({
|
|
49
|
+
facts: userFacts,
|
|
50
|
+
nodes: userNodes,
|
|
51
|
+
});
|
|
52
|
+
const projectContextString = (0, context_js_1.formatMemoryContext)(projectContext.facts, projectContext.nodes, { factStaleDays });
|
|
53
|
+
const userContextString = (0, context_js_1.formatMemoryContext)(userContext.facts, userContext.nodes, { factStaleDays });
|
|
54
|
+
if (!projectContextString && !userContextString)
|
|
56
55
|
return;
|
|
56
|
+
let snapshotPrimer = "";
|
|
57
|
+
if (useUserScope && characterBudget > 0) {
|
|
58
|
+
try {
|
|
59
|
+
const episodes = await client.getEpisodes({
|
|
60
|
+
groupId: state.groupId,
|
|
61
|
+
lastN: 10,
|
|
62
|
+
});
|
|
63
|
+
const snapshot = episodes
|
|
64
|
+
.filter((episode) => {
|
|
65
|
+
const description = episode.sourceDescription ??
|
|
66
|
+
episode.source_description ?? "";
|
|
67
|
+
return description === "session-snapshot";
|
|
68
|
+
})
|
|
69
|
+
.sort((a, b) => {
|
|
70
|
+
const aTime = a.created_at ? Date.parse(a.created_at) : 0;
|
|
71
|
+
const bTime = b.created_at ? Date.parse(b.created_at) : 0;
|
|
72
|
+
return bTime - aTime;
|
|
73
|
+
})[0];
|
|
74
|
+
if (snapshot?.content) {
|
|
75
|
+
const snapshotBudget = Math.min(characterBudget, 1200);
|
|
76
|
+
snapshotPrimer = [
|
|
77
|
+
"## Session Snapshot",
|
|
78
|
+
"> Most recent session snapshot; use to restore active strategy and open questions.",
|
|
79
|
+
"",
|
|
80
|
+
snapshot.content.slice(0, snapshotBudget),
|
|
81
|
+
].join("\n");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger_js_1.logger.error("Failed to load session snapshot", { err });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
57
88
|
const projectBudget = useUserScope
|
|
58
89
|
? Math.floor(characterBudget * 0.7)
|
|
59
90
|
: characterBudget;
|
|
60
91
|
const userBudget = characterBudget - projectBudget;
|
|
61
|
-
const truncatedProject =
|
|
62
|
-
const truncatedUser = useUserScope
|
|
63
|
-
|
|
92
|
+
const truncatedProject = projectContextString.slice(0, projectBudget);
|
|
93
|
+
const truncatedUser = useUserScope
|
|
94
|
+
? userContextString.slice(0, userBudget)
|
|
95
|
+
: "";
|
|
96
|
+
const memoryContext = [snapshotPrimer, truncatedProject, truncatedUser]
|
|
64
97
|
.filter((section) => section.trim().length > 0)
|
|
65
98
|
.join("\n\n")
|
|
66
99
|
.slice(0, characterBudget);
|
|
67
100
|
if (!memoryContext)
|
|
68
101
|
return;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
id: `${prefix}${Date.now()}`,
|
|
86
|
-
sessionID: output.message.sessionID,
|
|
87
|
-
messageID: output.message.id,
|
|
88
|
-
synthetic: true,
|
|
89
|
-
});
|
|
102
|
+
const factUuids = seedFactUuids ??
|
|
103
|
+
new Set([
|
|
104
|
+
...projectContext.facts.map((fact) => fact.uuid),
|
|
105
|
+
...userContext.facts.map((fact) => fact.uuid),
|
|
106
|
+
]);
|
|
107
|
+
state.cachedMemoryContext = memoryContext;
|
|
108
|
+
logger_js_1.logger.info(`Cached ${projectFacts.length + userFacts.length} facts and ${projectNodes.length + userNodes.length} nodes for system prompt injection`);
|
|
109
|
+
state.lastInjectionFactUuids = factUuids;
|
|
110
|
+
};
|
|
111
|
+
const computeJaccardSimilarity = (left, right) => {
|
|
112
|
+
if (left.size === 0 && right.size === 0)
|
|
113
|
+
return 1;
|
|
114
|
+
let intersection = 0;
|
|
115
|
+
for (const value of left) {
|
|
116
|
+
if (right.has(value))
|
|
117
|
+
intersection += 1;
|
|
90
118
|
}
|
|
91
|
-
|
|
119
|
+
const union = left.size + right.size - intersection;
|
|
120
|
+
return union === 0 ? 1 : intersection / union;
|
|
92
121
|
};
|
|
93
122
|
return async ({ sessionID }, output) => {
|
|
94
123
|
if (await sessionManager.isSubagentSession(sessionID)) {
|
|
@@ -115,19 +144,30 @@ function createChatHandler(deps) {
|
|
|
115
144
|
messageLength: messageText.length,
|
|
116
145
|
});
|
|
117
146
|
const shouldInjectOnFirst = !state.injectedMemories;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
147
|
+
let shouldReinject = false;
|
|
148
|
+
let currentFactUuids = null;
|
|
149
|
+
if (!shouldInjectOnFirst) {
|
|
150
|
+
const driftFacts = await client.searchFacts({
|
|
151
|
+
query: messageText,
|
|
152
|
+
groupIds: [state.groupId],
|
|
153
|
+
maxFacts: 20,
|
|
154
|
+
});
|
|
155
|
+
currentFactUuids = new Set(driftFacts.map((fact) => fact.uuid));
|
|
156
|
+
const similarity = computeJaccardSimilarity(currentFactUuids, state.lastInjectionFactUuids);
|
|
157
|
+
shouldReinject = similarity < driftThreshold;
|
|
158
|
+
if (!shouldReinject) {
|
|
159
|
+
logger_js_1.logger.debug("Skipping reinjection; drift above threshold", {
|
|
160
|
+
sessionID,
|
|
161
|
+
similarity,
|
|
162
|
+
});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
124
166
|
try {
|
|
125
|
-
const prefix = shouldReinject ? "graphiti-refresh-" : "graphiti-memory-";
|
|
126
167
|
const useUserScope = shouldInjectOnFirst;
|
|
127
168
|
const characterBudget = (0, context_limit_js_1.calculateInjectionBudget)(state.contextLimit);
|
|
128
|
-
await
|
|
169
|
+
await searchAndCacheMemoryContext(state, messageText, useUserScope, characterBudget, currentFactUuids);
|
|
129
170
|
state.injectedMemories = true;
|
|
130
|
-
state.lastInjectionMessageCount = state.messageCount;
|
|
131
171
|
}
|
|
132
172
|
catch (err) {
|
|
133
173
|
logger_js_1.logger.error("Failed to inject memories:", err);
|
|
@@ -9,6 +9,7 @@ export interface CompactingHandlerDeps {
|
|
|
9
9
|
sessionManager: SessionManager;
|
|
10
10
|
client: GraphitiClient;
|
|
11
11
|
defaultGroupId: string;
|
|
12
|
+
factStaleDays: number;
|
|
12
13
|
}
|
|
13
14
|
/** Creates the `experimental.session.compacting` hook handler. */
|
|
14
15
|
export declare function createCompactingHandler(deps: CompactingHandlerDeps): ({ sessionID }: CompactingInput, output: CompactingOutput) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compacting.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/compacting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,KAAK,cAAc,GAAG,WAAW,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC;AAC5E,KAAK,eAAe,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"compacting.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/compacting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,KAAK,cAAc,GAAG,WAAW,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC;AAC5E,KAAK,eAAe,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,qBAAqB,IAI/D,eAAe,eAAe,EAC9B,QAAQ,gBAAgB,mBA0B3B"}
|
|
@@ -6,7 +6,7 @@ const context_limit_js_1 = require("../services/context-limit.js");
|
|
|
6
6
|
const logger_js_1 = require("../services/logger.js");
|
|
7
7
|
/** Creates the `experimental.session.compacting` hook handler. */
|
|
8
8
|
function createCompactingHandler(deps) {
|
|
9
|
-
const { sessionManager, client, defaultGroupId } = deps;
|
|
9
|
+
const { sessionManager, client, defaultGroupId, factStaleDays } = deps;
|
|
10
10
|
return async ({ sessionID }, output) => {
|
|
11
11
|
const state = sessionManager.getState(sessionID);
|
|
12
12
|
if (!state?.isMain) {
|
|
@@ -23,6 +23,7 @@ function createCompactingHandler(deps) {
|
|
|
23
23
|
user: state.userGroupId,
|
|
24
24
|
},
|
|
25
25
|
contextStrings: output.context,
|
|
26
|
+
factStaleDays,
|
|
26
27
|
});
|
|
27
28
|
if (additionalContext.length > 0) {
|
|
28
29
|
output.context.push(...additionalContext);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
import type { OpencodeClient } from "@opencode-ai/sdk";
|
|
2
3
|
import type { GraphitiClient } from "../services/client.js";
|
|
3
|
-
import type { ProviderListClient } from "../services/context-limit.js";
|
|
4
4
|
import type { SessionManager } from "../session.js";
|
|
5
5
|
type EventHook = NonNullable<Hooks["event"]>;
|
|
6
6
|
type EventInput = Parameters<EventHook>[0];
|
|
@@ -9,7 +9,7 @@ export interface EventHandlerDeps {
|
|
|
9
9
|
sessionManager: SessionManager;
|
|
10
10
|
client: GraphitiClient;
|
|
11
11
|
defaultGroupId: string;
|
|
12
|
-
sdkClient:
|
|
12
|
+
sdkClient: OpencodeClient;
|
|
13
13
|
directory: string;
|
|
14
14
|
groupIdPrefix: string;
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,KAAK,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7C,KAAK,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3C,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wCAAwC;AACxC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,IAkDzC,WAAW,UAAU,mBAkLpC"}
|
|
@@ -9,6 +9,41 @@ const utils_js_1 = require("../utils.js");
|
|
|
9
9
|
function createEventHandler(deps) {
|
|
10
10
|
const { sessionManager, client, defaultGroupId, sdkClient, directory, groupIdPrefix, } = deps;
|
|
11
11
|
const defaultUserGroupId = (0, utils_js_1.makeUserGroupId)(groupIdPrefix);
|
|
12
|
+
const buildSessionSnapshot = (sessionId, messages) => {
|
|
13
|
+
const recentMessages = messages.slice(-12);
|
|
14
|
+
const recentAssistant = [...recentMessages]
|
|
15
|
+
.reverse()
|
|
16
|
+
.find((message) => message.startsWith("Assistant:"))
|
|
17
|
+
?.replace(/^Assistant:\s*/, "")
|
|
18
|
+
.trim();
|
|
19
|
+
const recentUser = [...recentMessages]
|
|
20
|
+
.reverse()
|
|
21
|
+
.find((message) => message.startsWith("User:"))
|
|
22
|
+
?.replace(/^User:\s*/, "")
|
|
23
|
+
.trim();
|
|
24
|
+
const questionRegex = /[^\n\r?]{3,200}\?/g;
|
|
25
|
+
const questions = recentMessages
|
|
26
|
+
.flatMap((message) => {
|
|
27
|
+
const text = message.replace(/^(User|Assistant):\s*/, "");
|
|
28
|
+
return text.match(questionRegex) ?? [];
|
|
29
|
+
})
|
|
30
|
+
.map((question) => question.trim());
|
|
31
|
+
const uniqueQuestions = Array.from(new Set(questions)).slice(0, 6);
|
|
32
|
+
const lines = [];
|
|
33
|
+
lines.push(`Session ${sessionId} working snapshot`);
|
|
34
|
+
if (recentUser)
|
|
35
|
+
lines.push(`Recent user focus: ${recentUser}`);
|
|
36
|
+
if (recentAssistant) {
|
|
37
|
+
lines.push(`Recent assistant focus: ${recentAssistant}`);
|
|
38
|
+
}
|
|
39
|
+
if (uniqueQuestions.length > 0) {
|
|
40
|
+
lines.push("Open questions:");
|
|
41
|
+
for (const question of uniqueQuestions) {
|
|
42
|
+
lines.push(`- ${question}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
};
|
|
12
47
|
return async ({ event }) => {
|
|
13
48
|
try {
|
|
14
49
|
if (event.type === "session.created") {
|
|
@@ -27,7 +62,7 @@ function createEventHandler(deps) {
|
|
|
27
62
|
groupId: defaultGroupId,
|
|
28
63
|
userGroupId: defaultUserGroupId,
|
|
29
64
|
injectedMemories: false,
|
|
30
|
-
|
|
65
|
+
lastInjectionFactUuids: new Set(),
|
|
31
66
|
messageCount: 0,
|
|
32
67
|
pendingMessages: [],
|
|
33
68
|
contextLimit: 200_000,
|
|
@@ -74,6 +109,22 @@ function createEventHandler(deps) {
|
|
|
74
109
|
logger_js_1.logger.debug("Ignoring non-main idle session:", sessionId);
|
|
75
110
|
return;
|
|
76
111
|
}
|
|
112
|
+
try {
|
|
113
|
+
const snapshotContent = buildSessionSnapshot(sessionId, state.pendingMessages);
|
|
114
|
+
if (snapshotContent.trim()) {
|
|
115
|
+
await client.addEpisode({
|
|
116
|
+
name: `Snapshot: ${sessionId}`,
|
|
117
|
+
episodeBody: snapshotContent,
|
|
118
|
+
groupId: state.groupId,
|
|
119
|
+
source: "text",
|
|
120
|
+
sourceDescription: "session-snapshot",
|
|
121
|
+
});
|
|
122
|
+
logger_js_1.logger.info("Saved session snapshot", { sessionId });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
logger_js_1.logger.error("Failed to save session snapshot", { sessionId, err });
|
|
127
|
+
}
|
|
77
128
|
await sessionManager.flushPendingMessages(sessionId, "Buffered messages from OpenCode session", 50);
|
|
78
129
|
return;
|
|
79
130
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
import type { SessionManager } from "../session.js";
|
|
3
|
+
type SystemTransformHook = NonNullable<Hooks["experimental.chat.system.transform"]>;
|
|
4
|
+
type SystemTransformInput = Parameters<SystemTransformHook>[0];
|
|
5
|
+
type SystemTransformOutput = Parameters<SystemTransformHook>[1];
|
|
6
|
+
export interface SystemHandlerDeps {
|
|
7
|
+
sessionManager: SessionManager;
|
|
8
|
+
}
|
|
9
|
+
export declare function createSystemHandler(deps: SystemHandlerDeps): ({ sessionID }: SystemTransformInput, output: SystemTransformOutput) => Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=system.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/system.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,KAAK,mBAAmB,GAAG,WAAW,CACpC,KAAK,CAAC,oCAAoC,CAAC,CAC5C,CAAC;AACF,KAAK,oBAAoB,GAAG,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,KAAK,qBAAqB,GAAG,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,cAAc,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,IAMvD,eAAe,oBAAoB,EACnC,QAAQ,qBAAqB,mBAYhC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSystemHandler = createSystemHandler;
|
|
4
|
+
const logger_js_1 = require("../services/logger.js");
|
|
5
|
+
function createSystemHandler(deps) {
|
|
6
|
+
const { sessionManager } = deps;
|
|
7
|
+
// Assumes chat.message hook completes before system.transform fires for the same turn.
|
|
8
|
+
// deno-lint-ignore require-await
|
|
9
|
+
return async ({ sessionID }, output) => {
|
|
10
|
+
if (!sessionID)
|
|
11
|
+
return;
|
|
12
|
+
const state = sessionManager.getState(sessionID);
|
|
13
|
+
if (!state?.isMain)
|
|
14
|
+
return;
|
|
15
|
+
if (!state.cachedMemoryContext)
|
|
16
|
+
return;
|
|
17
|
+
output.system.push(state.cachedMemoryContext);
|
|
18
|
+
state.cachedMemoryContext = undefined;
|
|
19
|
+
logger_js_1.logger.info("Injected memory context into system prompt");
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAW/D;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAW/D;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,MAuDtB,CAAC"}
|
package/script/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ const config_js_1 = require("./config.js");
|
|
|
5
5
|
const chat_js_1 = require("./handlers/chat.js");
|
|
6
6
|
const compacting_js_1 = require("./handlers/compacting.js");
|
|
7
7
|
const event_js_1 = require("./handlers/event.js");
|
|
8
|
+
const system_js_1 = require("./handlers/system.js");
|
|
8
9
|
const client_js_1 = require("./services/client.js");
|
|
9
10
|
const logger_js_1 = require("./services/logger.js");
|
|
10
11
|
const session_js_1 = require("./session.js");
|
|
@@ -30,19 +31,24 @@ const graphiti = async (input) => {
|
|
|
30
31
|
sessionManager,
|
|
31
32
|
client,
|
|
32
33
|
defaultGroupId,
|
|
33
|
-
sdkClient
|
|
34
|
+
sdkClient,
|
|
34
35
|
directory: input.directory,
|
|
35
36
|
groupIdPrefix: config.groupIdPrefix,
|
|
36
37
|
}),
|
|
37
38
|
"chat.message": (0, chat_js_1.createChatHandler)({
|
|
38
39
|
sessionManager,
|
|
39
|
-
|
|
40
|
+
driftThreshold: config.driftThreshold,
|
|
41
|
+
factStaleDays: config.factStaleDays,
|
|
40
42
|
client,
|
|
41
43
|
}),
|
|
42
44
|
"experimental.session.compacting": (0, compacting_js_1.createCompactingHandler)({
|
|
43
45
|
sessionManager,
|
|
44
46
|
client,
|
|
45
47
|
defaultGroupId,
|
|
48
|
+
factStaleDays: config.factStaleDays,
|
|
49
|
+
}),
|
|
50
|
+
"experimental.chat.system.transform": (0, system_js_1.createSystemHandler)({
|
|
51
|
+
sessionManager,
|
|
46
52
|
}),
|
|
47
53
|
};
|
|
48
54
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GraphitiFact, GraphitiNode } from "../types/index.js";
|
|
1
|
+
import type { GraphitiEpisode, GraphitiFact, GraphitiNode } from "../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Graphiti MCP client wrapper for connecting, querying,
|
|
4
4
|
* and persisting episodes with basic reconnection handling.
|
|
@@ -57,6 +57,13 @@ export declare class GraphitiClient {
|
|
|
57
57
|
groupIds?: string[];
|
|
58
58
|
maxNodes?: number;
|
|
59
59
|
}): Promise<GraphitiNode[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Retrieve recent episodes for a group.
|
|
62
|
+
*/
|
|
63
|
+
getEpisodes(params: {
|
|
64
|
+
groupId?: string;
|
|
65
|
+
lastN?: number;
|
|
66
|
+
}): Promise<GraphitiEpisode[]>;
|
|
60
67
|
/**
|
|
61
68
|
* Check whether the Graphiti MCP server is reachable.
|
|
62
69
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/src/services/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EAEZ,YAAY,EAEb,MAAM,mBAAmB,CAAC;AAG3B;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAS;IAEzB;;OAEG;gBACS,QAAQ,EAAE,MAAM;IAS5B,oDAAoD;IACpD,OAAO,CAAC,wBAAwB;IAUhC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBjC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAOnB,QAAQ;IAkCtB,OAAO,CAAC,gBAAgB;YASV,SAAS;IAavB;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO;IAyBzC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;QACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAsB3B;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAsB3B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/src/services/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAEZ,YAAY,EAEb,MAAM,mBAAmB,CAAC;AAG3B;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAS;IAEzB;;OAEG;gBACS,QAAQ,EAAE,MAAM;IAS5B,oDAAoD;IACpD,OAAO,CAAC,wBAAwB;IAUhC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBjC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAOnB,QAAQ;IAkCtB,OAAO,CAAC,gBAAgB;YASV,SAAS;IAavB;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO;IAyBzC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;QACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAsB3B;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAsB3B;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqB9B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
|
|
@@ -221,6 +221,29 @@ class GraphitiClient {
|
|
|
221
221
|
return [];
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Retrieve recent episodes for a group.
|
|
226
|
+
*/
|
|
227
|
+
async getEpisodes(params) {
|
|
228
|
+
try {
|
|
229
|
+
const result = await this.callTool("get_episodes", {
|
|
230
|
+
group_id: params.groupId,
|
|
231
|
+
last_n: params.lastN,
|
|
232
|
+
});
|
|
233
|
+
if (Array.isArray(result))
|
|
234
|
+
return result;
|
|
235
|
+
if (result &&
|
|
236
|
+
typeof result === "object" &&
|
|
237
|
+
Array.isArray(result.episodes)) {
|
|
238
|
+
return result.episodes;
|
|
239
|
+
}
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
logger_js_1.logger.error("getEpisodes error:", err);
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
224
247
|
/**
|
|
225
248
|
* Check whether the Graphiti MCP server is reachable.
|
|
226
249
|
*/
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { GraphitiFact, GraphitiNode } from "../types/index.js";
|
|
2
|
+
export declare const classifyFacts: (facts: GraphitiFact[], now: Date) => {
|
|
3
|
+
decisions: GraphitiFact[];
|
|
4
|
+
active: GraphitiFact[];
|
|
5
|
+
background: GraphitiFact[];
|
|
6
|
+
};
|
|
7
|
+
export declare const takeFactsWithinBudget: (facts: GraphitiFact[], budget: number, formatOptions: {
|
|
8
|
+
factStaleDays: number;
|
|
9
|
+
now: Date;
|
|
10
|
+
}) => GraphitiFact[];
|
|
2
11
|
/**
|
|
3
12
|
* Persist a compaction summary episode when enabled.
|
|
4
13
|
*/
|
|
@@ -38,5 +47,6 @@ export declare function getCompactionContext(params: {
|
|
|
38
47
|
user?: string;
|
|
39
48
|
};
|
|
40
49
|
contextStrings: string[];
|
|
50
|
+
factStaleDays?: number;
|
|
41
51
|
}): Promise<string[]>;
|
|
42
52
|
//# sourceMappingURL=compaction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../../src/src/services/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../../src/src/services/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA0BpE,eAAO,MAAM,aAAa,GACxB,OAAO,YAAY,EAAE,EACrB,KAAK,IAAI,KACR;IACD,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,UAAU,EAAE,YAAY,EAAE,CAAC;CA4B5B,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,OAAO,YAAY,EAAE,EACrB,QAAQ,MAAM,EACd,eAAe;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,KAClD,YAAY,EAoBd,CAAC;AAEF;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE;QACN,UAAU,EAAE,CAAC,IAAI,EAAE;YACjB,IAAI,EAAE,MAAM,CAAC;YACb,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;YACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC;SAC5B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACrB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,MAAM,EAAE;QACN,WAAW,EAAE,CAAC,IAAI,EAAE;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;YACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;QAC9B,WAAW,EAAE,CAAC,IAAI,EAAE;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;YACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;KAC/B,CAAC;IACF,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA0KpB"}
|
|
@@ -1,9 +1,76 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.takeFactsWithinBudget = exports.classifyFacts = void 0;
|
|
3
4
|
exports.handleCompaction = handleCompaction;
|
|
4
5
|
exports.getCompactionContext = getCompactionContext;
|
|
5
6
|
const context_js_1 = require("./context.js");
|
|
6
7
|
const logger_js_1 = require("./logger.js");
|
|
8
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
const DECISION_KEYWORDS = [
|
|
10
|
+
"decided",
|
|
11
|
+
"must",
|
|
12
|
+
"should",
|
|
13
|
+
"prefer",
|
|
14
|
+
"constraint",
|
|
15
|
+
"require",
|
|
16
|
+
"chose",
|
|
17
|
+
"always",
|
|
18
|
+
"never",
|
|
19
|
+
"schema",
|
|
20
|
+
"architecture",
|
|
21
|
+
"agreed",
|
|
22
|
+
"design",
|
|
23
|
+
"selected",
|
|
24
|
+
];
|
|
25
|
+
const classifyFacts = (facts, now) => {
|
|
26
|
+
const decisions = [];
|
|
27
|
+
const active = [];
|
|
28
|
+
const background = [];
|
|
29
|
+
const cutoff = now.getTime() - 7 * DAY_MS;
|
|
30
|
+
for (const fact of facts) {
|
|
31
|
+
const text = fact.fact.toLowerCase();
|
|
32
|
+
// Use word boundary regex to match whole words only
|
|
33
|
+
const hasDecisionKeyword = DECISION_KEYWORDS.some((keyword) => {
|
|
34
|
+
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
35
|
+
return regex.test(text);
|
|
36
|
+
});
|
|
37
|
+
if (hasDecisionKeyword) {
|
|
38
|
+
decisions.push(fact);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const validAt = fact.valid_at ? Date.parse(fact.valid_at) : NaN;
|
|
42
|
+
if (!Number.isNaN(validAt) && validAt >= cutoff) {
|
|
43
|
+
active.push(fact);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
background.push(fact);
|
|
47
|
+
}
|
|
48
|
+
return { decisions, active, background };
|
|
49
|
+
};
|
|
50
|
+
exports.classifyFacts = classifyFacts;
|
|
51
|
+
const takeFactsWithinBudget = (facts, budget, formatOptions) => {
|
|
52
|
+
if (budget <= 0 || facts.length === 0)
|
|
53
|
+
return [];
|
|
54
|
+
const classified = (0, exports.classifyFacts)(facts, formatOptions.now);
|
|
55
|
+
const prioritized = [
|
|
56
|
+
...classified.decisions,
|
|
57
|
+
...classified.active,
|
|
58
|
+
...classified.background,
|
|
59
|
+
];
|
|
60
|
+
const lines = (0, context_js_1.formatFactLines)(prioritized, formatOptions);
|
|
61
|
+
const selected = [];
|
|
62
|
+
let remaining = budget;
|
|
63
|
+
for (let i = 0; i < lines.length; i++) {
|
|
64
|
+
const line = lines[i];
|
|
65
|
+
const length = line.length + 1;
|
|
66
|
+
if (remaining - length < 0)
|
|
67
|
+
continue;
|
|
68
|
+
selected.push(prioritized[i]);
|
|
69
|
+
remaining -= length;
|
|
70
|
+
}
|
|
71
|
+
return selected;
|
|
72
|
+
};
|
|
73
|
+
exports.takeFactsWithinBudget = takeFactsWithinBudget;
|
|
7
74
|
/**
|
|
8
75
|
* Persist a compaction summary episode when enabled.
|
|
9
76
|
*/
|
|
@@ -30,6 +97,8 @@ async function handleCompaction(params) {
|
|
|
30
97
|
*/
|
|
31
98
|
async function getCompactionContext(params) {
|
|
32
99
|
const { client, characterBudget, groupIds, contextStrings } = params;
|
|
100
|
+
const now = new Date();
|
|
101
|
+
const factStaleDays = params.factStaleDays ?? 30;
|
|
33
102
|
try {
|
|
34
103
|
const queryText = contextStrings.slice(0, 3).join(" ").slice(0, 500);
|
|
35
104
|
if (!queryText.trim())
|
|
@@ -70,14 +139,40 @@ async function getCompactionContext(params) {
|
|
|
70
139
|
userFacts.length === 0 && userNodes.length === 0) {
|
|
71
140
|
return [];
|
|
72
141
|
}
|
|
73
|
-
const
|
|
142
|
+
const formatOptions = { factStaleDays, now };
|
|
143
|
+
const projectContext = (0, context_js_1.deduplicateContext)({
|
|
144
|
+
facts: projectFacts,
|
|
145
|
+
nodes: projectNodes,
|
|
146
|
+
});
|
|
147
|
+
const userContext = (0, context_js_1.deduplicateContext)({
|
|
148
|
+
facts: userFacts,
|
|
149
|
+
nodes: userNodes,
|
|
150
|
+
});
|
|
151
|
+
const buildSection = (header, facts, nodes, budget) => {
|
|
74
152
|
const lines = [];
|
|
75
153
|
lines.push(header);
|
|
76
154
|
lines.push("<instruction>Background context only; do not reference in titles, summaries, or opening responses unless directly relevant.</instruction>");
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
155
|
+
const classified = (0, exports.classifyFacts)(facts, now);
|
|
156
|
+
const decisionBudget = Math.floor(budget * 0.4);
|
|
157
|
+
const activeBudget = Math.floor(budget * 0.35);
|
|
158
|
+
const backgroundBudget = budget - decisionBudget - activeBudget;
|
|
159
|
+
const selectedDecisions = (0, exports.takeFactsWithinBudget)(classified.decisions, decisionBudget, formatOptions);
|
|
160
|
+
const selectedActive = (0, exports.takeFactsWithinBudget)(classified.active, activeBudget, formatOptions);
|
|
161
|
+
const selectedBackground = (0, exports.takeFactsWithinBudget)(classified.background, backgroundBudget, formatOptions);
|
|
162
|
+
if (selectedDecisions.length > 0) {
|
|
163
|
+
lines.push("<decisions>");
|
|
164
|
+
lines.push(...(0, context_js_1.formatFactLines)(selectedDecisions, formatOptions));
|
|
165
|
+
lines.push("</decisions>");
|
|
166
|
+
}
|
|
167
|
+
if (selectedActive.length > 0) {
|
|
168
|
+
lines.push("<active_context>");
|
|
169
|
+
lines.push(...(0, context_js_1.formatFactLines)(selectedActive, formatOptions));
|
|
170
|
+
lines.push("</active_context>");
|
|
171
|
+
}
|
|
172
|
+
if (selectedBackground.length > 0) {
|
|
173
|
+
lines.push("<background>");
|
|
174
|
+
lines.push(...(0, context_js_1.formatFactLines)(selectedBackground, formatOptions));
|
|
175
|
+
lines.push("</background>");
|
|
81
176
|
}
|
|
82
177
|
if (nodes.length > 0) {
|
|
83
178
|
lines.push("<nodes>");
|
|
@@ -86,25 +181,19 @@ async function getCompactionContext(params) {
|
|
|
86
181
|
}
|
|
87
182
|
return lines.join("\n");
|
|
88
183
|
};
|
|
89
|
-
const projectSection = buildSection('<memory source="project">', projectFacts, projectNodes);
|
|
90
|
-
const userSection = buildSection('<memory source="user">', userFacts, userNodes);
|
|
91
184
|
const headerLines = [
|
|
92
185
|
"<summary>",
|
|
93
|
-
"<
|
|
94
|
-
"- ",
|
|
95
|
-
"</current_goal>",
|
|
96
|
-
"",
|
|
97
|
-
"<work_completed>",
|
|
186
|
+
"<decisions>",
|
|
98
187
|
"- ",
|
|
99
|
-
"</
|
|
188
|
+
"</decisions>",
|
|
100
189
|
"",
|
|
101
|
-
"<
|
|
190
|
+
"<active_context>",
|
|
102
191
|
"- ",
|
|
103
|
-
"</
|
|
192
|
+
"</active_context>",
|
|
104
193
|
"",
|
|
105
|
-
"<
|
|
194
|
+
"<background>",
|
|
106
195
|
"- ",
|
|
107
|
-
"</
|
|
196
|
+
"</background>",
|
|
108
197
|
"",
|
|
109
198
|
"<persistent_memory>",
|
|
110
199
|
];
|
|
@@ -113,6 +202,8 @@ async function getCompactionContext(params) {
|
|
|
113
202
|
const remainingBudget = Math.max(characterBudget - base.length, 0);
|
|
114
203
|
const projectBudget = Math.floor(remainingBudget * 0.7);
|
|
115
204
|
const userBudget = remainingBudget - projectBudget;
|
|
205
|
+
const projectSection = buildSection('<memory source="project">', projectContext.facts, projectContext.nodes, projectBudget);
|
|
206
|
+
const userSection = buildSection('<memory source="user">', userContext.facts, userContext.nodes, userBudget);
|
|
116
207
|
const truncatedProject = projectSection.slice(0, projectBudget);
|
|
117
208
|
const truncatedUser = userSection.slice(0, userBudget);
|
|
118
209
|
const sections = [header];
|