opencode-graphiti 0.1.9 → 0.1.10
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/README.md +7 -1
- package/esm/_dnt.polyfills.d.ts +50 -0
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +37 -0
- package/esm/mod.d.ts +1 -0
- package/esm/mod.d.ts.map +1 -1
- package/esm/mod.js +1 -0
- package/esm/src/config.d.ts +7 -1
- package/esm/src/config.d.ts.map +1 -1
- package/esm/src/config.js +17 -5
- package/esm/src/handlers/chat.d.ts.map +1 -1
- package/esm/src/handlers/chat.js +51 -42
- package/esm/src/handlers/event.d.ts +1 -1
- package/esm/src/handlers/event.d.ts.map +1 -1
- package/esm/src/handlers/event.js +42 -46
- package/esm/src/handlers/messages.d.ts.map +1 -1
- package/esm/src/handlers/messages.js +2 -3
- package/esm/src/index.js +2 -2
- package/esm/src/services/client.d.ts +7 -0
- package/esm/src/services/client.d.ts.map +1 -1
- package/esm/src/services/client.js +22 -24
- package/esm/src/services/compaction.d.ts.map +1 -1
- package/esm/src/services/compaction.js +24 -33
- package/esm/src/services/constants.d.ts +7 -0
- package/esm/src/services/constants.d.ts.map +1 -0
- package/esm/src/services/constants.js +6 -0
- package/esm/src/services/context-limit.d.ts +1 -1
- package/esm/src/services/context-limit.d.ts.map +1 -1
- package/esm/src/services/context-limit.js +12 -14
- package/esm/src/services/context.d.ts +27 -7
- package/esm/src/services/context.d.ts.map +1 -1
- package/esm/src/services/context.js +34 -2
- package/esm/src/services/logger.js +2 -4
- package/esm/src/services/sdk-normalize.d.ts +55 -0
- package/esm/src/services/sdk-normalize.d.ts.map +1 -0
- package/esm/src/services/sdk-normalize.js +61 -0
- package/esm/src/session.d.ts +4 -2
- package/esm/src/session.d.ts.map +1 -1
- package/esm/src/session.js +38 -34
- package/esm/src/types/index.d.ts +13 -14
- package/esm/src/types/index.d.ts.map +1 -1
- package/esm/src/utils.d.ts +6 -0
- package/esm/src/utils.d.ts.map +1 -1
- package/esm/src/utils.js +16 -2
- package/package.json +1 -1
- package/script/_dnt.polyfills.d.ts +50 -0
- package/script/_dnt.polyfills.d.ts.map +1 -0
- package/script/_dnt.polyfills.js +38 -0
- package/script/mod.d.ts +1 -0
- package/script/mod.d.ts.map +1 -1
- package/script/mod.js +1 -0
- package/script/src/config.d.ts +7 -1
- package/script/src/config.d.ts.map +1 -1
- package/script/src/config.js +20 -5
- package/script/src/handlers/chat.d.ts.map +1 -1
- package/script/src/handlers/chat.js +49 -40
- package/script/src/handlers/event.d.ts +1 -1
- package/script/src/handlers/event.d.ts.map +1 -1
- package/script/src/handlers/event.js +41 -45
- package/script/src/handlers/messages.d.ts.map +1 -1
- package/script/src/handlers/messages.js +2 -3
- package/script/src/index.js +2 -2
- package/script/src/services/client.d.ts +7 -0
- package/script/src/services/client.d.ts.map +1 -1
- package/script/src/services/client.js +22 -24
- package/script/src/services/compaction.d.ts.map +1 -1
- package/script/src/services/compaction.js +24 -33
- package/script/src/services/constants.d.ts +7 -0
- package/script/src/services/constants.d.ts.map +1 -0
- package/script/src/services/constants.js +9 -0
- package/script/src/services/context-limit.d.ts +1 -1
- package/script/src/services/context-limit.d.ts.map +1 -1
- package/script/src/services/context-limit.js +13 -15
- package/script/src/services/context.d.ts +27 -7
- package/script/src/services/context.d.ts.map +1 -1
- package/script/src/services/context.js +36 -4
- package/script/src/services/logger.js +2 -4
- package/script/src/services/sdk-normalize.d.ts +55 -0
- package/script/src/services/sdk-normalize.d.ts.map +1 -0
- package/script/src/services/sdk-normalize.js +67 -0
- package/script/src/session.d.ts +4 -2
- package/script/src/session.d.ts.map +1 -1
- package/script/src/session.js +38 -34
- package/script/src/types/index.d.ts +13 -14
- package/script/src/types/index.d.ts.map +1 -1
- package/script/src/utils.d.ts +6 -0
- package/script/src/utils.d.ts.map +1 -1
- package/script/src/utils.js +18 -3
|
@@ -7,20 +7,17 @@ const logger_js_1 = require("../services/logger.js");
|
|
|
7
7
|
const utils_js_1 = require("../utils.js");
|
|
8
8
|
/** Creates the `event` hook handler. */
|
|
9
9
|
function createEventHandler(deps) {
|
|
10
|
-
const { sessionManager, client, defaultGroupId, sdkClient, directory,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const lastSnapshotBody = new Map();
|
|
10
|
+
const { sessionManager, client, defaultGroupId, defaultUserGroupId, sdkClient, directory, } = deps;
|
|
11
|
+
/** Per-handler context-limit cache — no cross-instance sharing. */
|
|
12
|
+
const contextLimitCache = new Map();
|
|
14
13
|
const buildSessionSnapshot = (sessionId, messages) => {
|
|
15
14
|
const recentMessages = messages.slice(-12);
|
|
16
|
-
const recentAssistant =
|
|
17
|
-
.
|
|
18
|
-
.find((message) => message.startsWith("Assistant:"))
|
|
15
|
+
const recentAssistant = recentMessages
|
|
16
|
+
.findLast((message) => message.startsWith("Assistant:"))
|
|
19
17
|
?.replace(/^Assistant:\s*/, "")
|
|
20
18
|
.trim();
|
|
21
|
-
const recentUser =
|
|
22
|
-
.
|
|
23
|
-
.find((message) => message.startsWith("User:"))
|
|
19
|
+
const recentUser = recentMessages
|
|
20
|
+
.findLast((message) => message.startsWith("User:"))
|
|
24
21
|
?.replace(/^User:\s*/, "")
|
|
25
22
|
.trim();
|
|
26
23
|
const questionRegex = /[^\n\r?]{3,200}\?/g;
|
|
@@ -60,19 +57,7 @@ function createEventHandler(deps) {
|
|
|
60
57
|
parentID: info.parentID,
|
|
61
58
|
});
|
|
62
59
|
if (isMain) {
|
|
63
|
-
sessionManager.setState(sessionId,
|
|
64
|
-
groupId: defaultGroupId,
|
|
65
|
-
userGroupId: defaultUserGroupId,
|
|
66
|
-
injectedMemories: false,
|
|
67
|
-
lastInjectionFactUuids: [],
|
|
68
|
-
cachedMemoryContext: undefined,
|
|
69
|
-
cachedFactUuids: undefined,
|
|
70
|
-
visibleFactUuids: [],
|
|
71
|
-
messageCount: 0,
|
|
72
|
-
pendingMessages: [],
|
|
73
|
-
contextLimit: 200_000,
|
|
74
|
-
isMain,
|
|
75
|
-
});
|
|
60
|
+
sessionManager.setState(sessionId, sessionManager.createDefaultState(defaultGroupId, defaultUserGroupId));
|
|
76
61
|
}
|
|
77
62
|
else {
|
|
78
63
|
logger_js_1.logger.debug("Ignoring subagent session:", sessionId);
|
|
@@ -115,25 +100,32 @@ function createEventHandler(deps) {
|
|
|
115
100
|
return;
|
|
116
101
|
}
|
|
117
102
|
try {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
103
|
+
if (state.pendingMessages.length > 0) {
|
|
104
|
+
const snapshotContent = buildSessionSnapshot(sessionId, state.pendingMessages);
|
|
105
|
+
if (snapshotContent.trim()) {
|
|
106
|
+
if (state.lastSnapshotBody === snapshotContent) {
|
|
107
|
+
logger_js_1.logger.debug("Skipping duplicate session snapshot", {
|
|
108
|
+
sessionId,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await client.addEpisode({
|
|
113
|
+
name: `Snapshot: ${sessionId}`,
|
|
114
|
+
episodeBody: snapshotContent,
|
|
115
|
+
groupId: state.groupId,
|
|
116
|
+
source: "text",
|
|
117
|
+
sourceDescription: "session-snapshot",
|
|
118
|
+
});
|
|
119
|
+
state.lastSnapshotBody = snapshotContent;
|
|
120
|
+
logger_js_1.logger.info("Saved session snapshot", { sessionId });
|
|
121
|
+
}
|
|
135
122
|
}
|
|
136
123
|
}
|
|
124
|
+
else {
|
|
125
|
+
logger_js_1.logger.debug("Skipping idle snapshot: no pending messages", {
|
|
126
|
+
sessionId,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
137
129
|
}
|
|
138
130
|
catch (err) {
|
|
139
131
|
logger_js_1.logger.error("Failed to save session snapshot", { sessionId, err });
|
|
@@ -175,11 +167,15 @@ function createEventHandler(deps) {
|
|
|
175
167
|
return;
|
|
176
168
|
sessionManager.finalizeAssistantMessage(state, sessionId, info.id, "message.updated");
|
|
177
169
|
if (info.tokens && info.providerID && info.modelID) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
170
|
+
// Fire-and-forget: update contextLimit asynchronously without
|
|
171
|
+
// blocking event responsiveness. The state update is eventually
|
|
172
|
+
// consistent — a missed update only affects injection budget sizing,
|
|
173
|
+
// not correctness. We snapshot `state` here; if the session is
|
|
174
|
+
// deleted before the promise resolves the write is a harmless no-op.
|
|
175
|
+
const capturedState = state;
|
|
176
|
+
(0, context_limit_js_1.resolveContextLimit)(info.providerID, info.modelID, sdkClient, directory, contextLimitCache).then((limit) => {
|
|
177
|
+
capturedState.contextLimit = limit;
|
|
178
|
+
}).catch((err) => logger_js_1.logger.debug("Failed to resolve context limit", err));
|
|
183
179
|
}
|
|
184
180
|
return;
|
|
185
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,KAAK,qBAAqB,GAAG,WAAW,CACtC,KAAK,CAAC,sCAAsC,CAAC,CAC9C,CAAC;AACF,KAAK,sBAAsB,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,KAAK,uBAAuB,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,cAAc,CAAC;CAChC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,IAK3D,QAAQ,sBAAsB,EAC9B,QAAQ,uBAAuB,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,KAAK,qBAAqB,GAAG,WAAW,CACtC,KAAK,CAAC,sCAAsC,CAAC,CAC9C,CAAC;AACF,KAAK,sBAAsB,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,KAAK,uBAAuB,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,cAAc,CAAC;CAChC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,IAK3D,QAAQ,sBAAsB,EAC9B,QAAQ,uBAAuB,mBAiFlC"}
|
|
@@ -7,9 +7,8 @@ function createMessagesHandler(deps) {
|
|
|
7
7
|
const { sessionManager } = deps;
|
|
8
8
|
// deno-lint-ignore require-await
|
|
9
9
|
return async (_input, output) => {
|
|
10
|
-
const lastUserEntry =
|
|
11
|
-
.
|
|
12
|
-
.find((message) => message.info.role === "user");
|
|
10
|
+
const lastUserEntry = output.messages
|
|
11
|
+
.findLast((message) => message.info.role === "user");
|
|
13
12
|
if (!lastUserEntry)
|
|
14
13
|
return;
|
|
15
14
|
const sessionID = lastUserEntry.info.sessionID;
|
package/script/src/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const utils_js_1 = require("./utils.js");
|
|
|
14
14
|
* OpenCode plugin entry point for Graphiti memory integration.
|
|
15
15
|
*/
|
|
16
16
|
const graphiti = async (input) => {
|
|
17
|
-
const config = (0, config_js_1.loadConfig)();
|
|
17
|
+
const config = (0, config_js_1.loadConfig)(input.directory);
|
|
18
18
|
const client = new client_js_1.GraphitiClient(config.endpoint);
|
|
19
19
|
const sdkClient = input.client;
|
|
20
20
|
const connected = await client.connect();
|
|
@@ -31,9 +31,9 @@ const graphiti = async (input) => {
|
|
|
31
31
|
sessionManager,
|
|
32
32
|
client,
|
|
33
33
|
defaultGroupId,
|
|
34
|
+
defaultUserGroupId,
|
|
34
35
|
sdkClient,
|
|
35
36
|
directory: input.directory,
|
|
36
|
-
groupIdPrefix: config.groupIdPrefix,
|
|
37
37
|
}),
|
|
38
38
|
"chat.message": (0, chat_js_1.createChatHandler)({
|
|
39
39
|
sessionManager,
|
|
@@ -41,6 +41,13 @@ export declare class GraphitiClient {
|
|
|
41
41
|
source?: "text" | "json" | "message";
|
|
42
42
|
sourceDescription?: string;
|
|
43
43
|
}): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Extract an array from a tool result that may be a bare array or a
|
|
46
|
+
* wrapped-array response object (`{ [key]: T[] }`).
|
|
47
|
+
* Returns the array when found, otherwise `null`.
|
|
48
|
+
* Public for testing.
|
|
49
|
+
*/
|
|
50
|
+
parseWrappedArray<T>(result: unknown, wrappedKey: string): T[] | null;
|
|
44
51
|
/**
|
|
45
52
|
* Search Graphiti facts matching the provided query.
|
|
46
53
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/src/services/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/src/services/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,YAAY,EACb,MAAM,mBAAmB,CAAC;AAI3B;;;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;;;;;OAKG;IACH,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI;IAYrE;;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;IAc3B;;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;IAc3B;;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;IAe9B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
|
|
@@ -8,6 +8,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
|
8
8
|
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
9
9
|
const deno_js_1 = __importDefault(require("../../deno.js"));
|
|
10
10
|
const logger_js_1 = require("./logger.js");
|
|
11
|
+
const sdk_normalize_js_1 = require("./sdk-normalize.js");
|
|
11
12
|
/**
|
|
12
13
|
* Graphiti MCP client wrapper for connecting, querying,
|
|
13
14
|
* and persisting episodes with basic reconnection handling.
|
|
@@ -173,6 +174,22 @@ class GraphitiClient {
|
|
|
173
174
|
});
|
|
174
175
|
logger_js_1.logger.debug("Added episode:", params.name);
|
|
175
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Extract an array from a tool result that may be a bare array or a
|
|
179
|
+
* wrapped-array response object (`{ [key]: T[] }`).
|
|
180
|
+
* Returns the array when found, otherwise `null`.
|
|
181
|
+
* Public for testing.
|
|
182
|
+
*/
|
|
183
|
+
parseWrappedArray(result, wrappedKey) {
|
|
184
|
+
if (Array.isArray(result))
|
|
185
|
+
return result;
|
|
186
|
+
if (result &&
|
|
187
|
+
typeof result === "object" &&
|
|
188
|
+
Array.isArray(result[wrappedKey])) {
|
|
189
|
+
return result[wrappedKey];
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
176
193
|
/**
|
|
177
194
|
* Search Graphiti facts matching the provided query.
|
|
178
195
|
*/
|
|
@@ -183,14 +200,7 @@ class GraphitiClient {
|
|
|
183
200
|
group_ids: params.groupIds,
|
|
184
201
|
max_facts: params.maxFacts || 10,
|
|
185
202
|
});
|
|
186
|
-
|
|
187
|
-
return result;
|
|
188
|
-
if (result &&
|
|
189
|
-
typeof result === "object" &&
|
|
190
|
-
Array.isArray(result.facts)) {
|
|
191
|
-
return result.facts;
|
|
192
|
-
}
|
|
193
|
-
return [];
|
|
203
|
+
return this.parseWrappedArray(result, "facts") ?? [];
|
|
194
204
|
}
|
|
195
205
|
catch (err) {
|
|
196
206
|
logger_js_1.logger.error("searchFacts error:", err);
|
|
@@ -207,14 +217,7 @@ class GraphitiClient {
|
|
|
207
217
|
group_ids: params.groupIds,
|
|
208
218
|
max_nodes: params.maxNodes || 10,
|
|
209
219
|
});
|
|
210
|
-
|
|
211
|
-
return result;
|
|
212
|
-
if (result &&
|
|
213
|
-
typeof result === "object" &&
|
|
214
|
-
Array.isArray(result.nodes)) {
|
|
215
|
-
return result.nodes;
|
|
216
|
-
}
|
|
217
|
-
return [];
|
|
220
|
+
return this.parseWrappedArray(result, "nodes") ?? [];
|
|
218
221
|
}
|
|
219
222
|
catch (err) {
|
|
220
223
|
logger_js_1.logger.error("searchNodes error:", err);
|
|
@@ -230,14 +233,9 @@ class GraphitiClient {
|
|
|
230
233
|
group_id: params.groupId,
|
|
231
234
|
last_n: params.lastN,
|
|
232
235
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
typeof result === "object" &&
|
|
237
|
-
Array.isArray(result.episodes)) {
|
|
238
|
-
return result.episodes;
|
|
239
|
-
}
|
|
240
|
-
return [];
|
|
236
|
+
const raw = this.parseWrappedArray(result, "episodes") ??
|
|
237
|
+
[];
|
|
238
|
+
return raw.map(sdk_normalize_js_1.normalizeEpisode);
|
|
241
239
|
}
|
|
242
240
|
catch (err) {
|
|
243
241
|
logger_js_1.logger.error("getEpisodes error:", err);
|
|
@@ -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;AAgCpE,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;CAuB5B,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,CA4KpB"}
|
|
@@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.takeFactsWithinBudget = exports.classifyFacts = void 0;
|
|
4
4
|
exports.handleCompaction = handleCompaction;
|
|
5
5
|
exports.getCompactionContext = getCompactionContext;
|
|
6
|
+
const utils_js_1 = require("../utils.js");
|
|
6
7
|
const context_js_1 = require("./context.js");
|
|
8
|
+
const constants_js_1 = require("./constants.js");
|
|
7
9
|
const logger_js_1 = require("./logger.js");
|
|
8
|
-
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
9
10
|
const DECISION_KEYWORDS = [
|
|
10
11
|
"decided",
|
|
11
12
|
"must",
|
|
@@ -22,19 +23,17 @@ const DECISION_KEYWORDS = [
|
|
|
22
23
|
"design",
|
|
23
24
|
"selected",
|
|
24
25
|
];
|
|
26
|
+
// Precompile keyword regex once, outside the fact-classification loop.
|
|
27
|
+
const DECISION_KEYWORD_REGEX = new RegExp(DECISION_KEYWORDS.map((kw) => `\\b${kw}\\b`).join("|"), "i");
|
|
25
28
|
const classifyFacts = (facts, now) => {
|
|
26
29
|
const decisions = [];
|
|
27
30
|
const active = [];
|
|
28
31
|
const background = [];
|
|
29
|
-
const cutoff = now.getTime() - 7 * DAY_MS;
|
|
32
|
+
const cutoff = now.getTime() - 7 * constants_js_1.DAY_MS;
|
|
30
33
|
for (const fact of facts) {
|
|
31
|
-
const text = fact.fact
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
35
|
-
return regex.test(text);
|
|
36
|
-
});
|
|
37
|
-
if (hasDecisionKeyword) {
|
|
34
|
+
const text = fact.fact;
|
|
35
|
+
// Task 7: use precompiled regex instead of building one per keyword per fact.
|
|
36
|
+
if (DECISION_KEYWORD_REGEX.test(text)) {
|
|
38
37
|
decisions.push(fact);
|
|
39
38
|
continue;
|
|
40
39
|
}
|
|
@@ -106,7 +105,7 @@ async function getCompactionContext(params) {
|
|
|
106
105
|
const projectFactsPromise = client.searchFacts({
|
|
107
106
|
query: queryText,
|
|
108
107
|
groupIds: [groupIds.project],
|
|
109
|
-
maxFacts:
|
|
108
|
+
maxFacts: constants_js_1.PROJECT_MAX_FACTS,
|
|
110
109
|
});
|
|
111
110
|
const projectNodesPromise = client.searchNodes({
|
|
112
111
|
query: queryText,
|
|
@@ -128,26 +127,19 @@ async function getCompactionContext(params) {
|
|
|
128
127
|
maxNodes: 10,
|
|
129
128
|
})
|
|
130
129
|
: Promise.resolve([]);
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
]);
|
|
130
|
+
const { projectContext, userContext, projectFacts, projectNodes, userFacts, userNodes, } = await (0, context_js_1.resolveProjectUserContext)({
|
|
131
|
+
projectFacts: projectFactsPromise,
|
|
132
|
+
projectNodes: projectNodesPromise,
|
|
133
|
+
userFacts: userFactsPromise,
|
|
134
|
+
userNodes: userNodesPromise,
|
|
135
|
+
});
|
|
138
136
|
if (projectFacts.length === 0 && projectNodes.length === 0 &&
|
|
139
137
|
userFacts.length === 0 && userNodes.length === 0) {
|
|
140
138
|
return [];
|
|
141
139
|
}
|
|
142
140
|
const formatOptions = { factStaleDays, now };
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
nodes: projectNodes,
|
|
146
|
-
});
|
|
147
|
-
const userContext = (0, context_js_1.deduplicateContext)({
|
|
148
|
-
facts: userFacts,
|
|
149
|
-
nodes: userNodes,
|
|
150
|
-
});
|
|
141
|
+
// Task 1: build section line-by-line; truncate at a line boundary to avoid
|
|
142
|
+
// cutting mid-tag or mid-line while still respecting the per-section budget.
|
|
151
143
|
const buildSection = (header, facts, nodes, budget) => {
|
|
152
144
|
const lines = [];
|
|
153
145
|
lines.push(header);
|
|
@@ -179,7 +171,7 @@ async function getCompactionContext(params) {
|
|
|
179
171
|
lines.push(...(0, context_js_1.formatNodeLines)(nodes));
|
|
180
172
|
lines.push("</nodes>");
|
|
181
173
|
}
|
|
182
|
-
return lines.join("\n");
|
|
174
|
+
return (0, utils_js_1.truncateAtLineBoundary)(lines.join("\n"), budget);
|
|
183
175
|
};
|
|
184
176
|
const headerLines = [
|
|
185
177
|
"<summary>",
|
|
@@ -204,20 +196,19 @@ async function getCompactionContext(params) {
|
|
|
204
196
|
const userBudget = remainingBudget - projectBudget;
|
|
205
197
|
const projectSection = buildSection('<memory source="project">', projectContext.facts, projectContext.nodes, projectBudget);
|
|
206
198
|
const userSection = buildSection('<memory source="user">', userContext.facts, userContext.nodes, userBudget);
|
|
207
|
-
const truncatedProject = projectSection.slice(0, projectBudget);
|
|
208
|
-
const truncatedUser = userSection.slice(0, userBudget);
|
|
209
199
|
const sections = [header];
|
|
210
|
-
if (
|
|
211
|
-
sections.push(
|
|
200
|
+
if (projectSection.trim()) {
|
|
201
|
+
sections.push(projectSection);
|
|
212
202
|
sections.push("</memory>");
|
|
213
203
|
}
|
|
214
|
-
if (
|
|
215
|
-
sections.push(
|
|
204
|
+
if (userSection.trim()) {
|
|
205
|
+
sections.push(userSection);
|
|
216
206
|
sections.push("</memory>");
|
|
217
207
|
}
|
|
218
208
|
sections.push("</persistent_memory>");
|
|
219
209
|
sections.push("</summary>");
|
|
220
|
-
|
|
210
|
+
// Final overall truncation at a line boundary.
|
|
211
|
+
const content = (0, utils_js_1.truncateAtLineBoundary)(sections.join("\n"), characterBudget);
|
|
221
212
|
return [content];
|
|
222
213
|
}
|
|
223
214
|
catch (err) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Milliseconds in one day. */
|
|
2
|
+
export declare const DAY_MS: number;
|
|
3
|
+
/** Default token context limit when the provider does not report one. */
|
|
4
|
+
export declare const DEFAULT_CONTEXT_LIMIT = 200000;
|
|
5
|
+
/** Maximum project-scope facts fetched per search query. */
|
|
6
|
+
export declare const PROJECT_MAX_FACTS = 50;
|
|
7
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/src/services/constants.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,eAAO,MAAM,MAAM,QAAsB,CAAC;AAE1C,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,SAAU,CAAC;AAE7C,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,KAAK,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROJECT_MAX_FACTS = exports.DEFAULT_CONTEXT_LIMIT = exports.DAY_MS = void 0;
|
|
4
|
+
/** Milliseconds in one day. */
|
|
5
|
+
exports.DAY_MS = 24 * 60 * 60 * 1000;
|
|
6
|
+
/** Default token context limit when the provider does not report one. */
|
|
7
|
+
exports.DEFAULT_CONTEXT_LIMIT = 200_000;
|
|
8
|
+
/** Maximum project-scope facts fetched per search query. */
|
|
9
|
+
exports.PROJECT_MAX_FACTS = 50;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OpencodeClient } from "@opencode-ai/sdk";
|
|
2
|
-
export declare function resolveContextLimit(providerID: string, modelID: string, client: OpencodeClient, directory: string): Promise<number>;
|
|
2
|
+
export declare function resolveContextLimit(providerID: string, modelID: string, client: OpencodeClient, directory: string, cache: Map<string, number>): Promise<number>;
|
|
3
3
|
/**
|
|
4
4
|
* Calculate the character budget for memory injection
|
|
5
5
|
* (5% of context limit * 4 chars/token).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-limit.d.ts","sourceRoot":"","sources":["../../../src/src/services/context-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"context-limit.d.ts","sourceRoot":"","sources":["../../../src/src/services/context-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAKvD,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
|
@@ -2,31 +2,29 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveContextLimit = resolveContextLimit;
|
|
4
4
|
exports.calculateInjectionBudget = calculateInjectionBudget;
|
|
5
|
+
const constants_js_1 = require("./constants.js");
|
|
5
6
|
const logger_js_1 = require("./logger.js");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
async function resolveContextLimit(providerID, modelID, client, directory) {
|
|
7
|
+
const sdk_normalize_js_1 = require("./sdk-normalize.js");
|
|
8
|
+
async function resolveContextLimit(providerID, modelID, client, directory, cache) {
|
|
9
9
|
const modelKey = `${providerID}/${modelID}`;
|
|
10
|
-
const cached =
|
|
10
|
+
const cached = cache.get(modelKey);
|
|
11
11
|
if (cached)
|
|
12
12
|
return cached;
|
|
13
13
|
try {
|
|
14
|
-
const
|
|
14
|
+
const response = await client.provider.list({
|
|
15
15
|
query: { directory },
|
|
16
16
|
});
|
|
17
|
-
const list =
|
|
17
|
+
const list = (0, sdk_normalize_js_1.extractSdkProviders)(response);
|
|
18
18
|
for (const provider of list) {
|
|
19
|
-
|
|
20
|
-
if (providerInfo.id !== providerID)
|
|
19
|
+
if (provider.id !== providerID)
|
|
21
20
|
continue;
|
|
22
|
-
const models =
|
|
21
|
+
const models = provider.models ?? [];
|
|
23
22
|
for (const model of models) {
|
|
24
|
-
|
|
25
|
-
if (modelInfo.id !== modelID)
|
|
23
|
+
if (model.id !== modelID)
|
|
26
24
|
continue;
|
|
27
|
-
const contextLimit =
|
|
25
|
+
const contextLimit = model.limit?.context;
|
|
28
26
|
if (typeof contextLimit === "number" && contextLimit > 0) {
|
|
29
|
-
|
|
27
|
+
cache.set(modelKey, contextLimit);
|
|
30
28
|
return contextLimit;
|
|
31
29
|
}
|
|
32
30
|
}
|
|
@@ -35,8 +33,8 @@ async function resolveContextLimit(providerID, modelID, client, directory) {
|
|
|
35
33
|
catch (err) {
|
|
36
34
|
logger_js_1.logger.warn("Failed to fetch provider context limit", err);
|
|
37
35
|
}
|
|
38
|
-
|
|
39
|
-
return DEFAULT_CONTEXT_LIMIT;
|
|
36
|
+
cache.set(modelKey, constants_js_1.DEFAULT_CONTEXT_LIMIT);
|
|
37
|
+
return constants_js_1.DEFAULT_CONTEXT_LIMIT;
|
|
40
38
|
}
|
|
41
39
|
/**
|
|
42
40
|
* Calculate the character budget for memory injection
|
|
@@ -16,13 +16,33 @@ export declare const formatNodeLines: (nodes: GraphitiNode[]) => string[];
|
|
|
16
16
|
export declare const deduplicateFactsByUuid: (facts: GraphitiFact[]) => GraphitiFact[];
|
|
17
17
|
export declare const deduplicateNodesByUuid: (nodes: GraphitiNode[]) => GraphitiNode[];
|
|
18
18
|
export declare const removeNodesReferencedByFacts: (facts: GraphitiFact[], nodes: GraphitiNode[]) => GraphitiNode[];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Await four parallel fact/node promises, deduplicate each side, and return
|
|
21
|
+
* the resolved project and user contexts.
|
|
22
|
+
*
|
|
23
|
+
* Callers construct the promises themselves — this lets chat.ts seed the
|
|
24
|
+
* project-facts promise from an earlier drift-check fetch without issuing a
|
|
25
|
+
* duplicate network request.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveProjectUserContext(promises: {
|
|
28
|
+
projectFacts: Promise<GraphitiFact[]>;
|
|
29
|
+
projectNodes: Promise<GraphitiNode[]>;
|
|
30
|
+
userFacts: Promise<GraphitiFact[]>;
|
|
31
|
+
userNodes: Promise<GraphitiNode[]>;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
projectContext: {
|
|
34
|
+
facts: GraphitiFact[];
|
|
35
|
+
nodes: GraphitiNode[];
|
|
36
|
+
};
|
|
37
|
+
userContext: {
|
|
38
|
+
facts: GraphitiFact[];
|
|
39
|
+
nodes: GraphitiNode[];
|
|
40
|
+
};
|
|
41
|
+
projectFacts: GraphitiFact[];
|
|
42
|
+
projectNodes: GraphitiNode[];
|
|
43
|
+
userFacts: GraphitiFact[];
|
|
44
|
+
userNodes: GraphitiNode[];
|
|
45
|
+
}>;
|
|
26
46
|
/**
|
|
27
47
|
* Format Graphiti facts and nodes into a user-facing context block.
|
|
28
48
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/src/services/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/src/services/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGpE,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,KAAG,IAAI,GAAG,IAKjD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM,YAAY,EAAE,KAAK,IAAI,KAAG,OAQ7D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,MAAM,YAAY,EAClB,KAAK,IAAI,EACT,eAAe,MAAM,KACpB,YAUF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,OAAO,YAAY,EAAE,KAAG,YAAY,EAWtE,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,OAAO,YAAY,EAAE,EACrB,UAAU;IACR,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,KACA,YAAY,EAMd,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,YAAY,KAAG,MAMnD,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,YAAY,EAAE,EACrB,UAAU;IACR,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,KACA,MAAM,EAGR,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,YAAY,EAAE,KAAG,MAAM,EAK1D,CAAC;AAEL,eAAO,MAAM,sBAAsB,GACjC,OAAO,YAAY,EAAE,KACpB,YAAY,EASd,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,OAAO,YAAY,EAAE,KACpB,YAAY,EASd,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,OAAO,YAAY,EAAE,EACrB,OAAO,YAAY,EAAE,KACpB,YAAY,EAOd,CAAC;AAeF;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE;IACxD,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACtC,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACtC,SAAS,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CACpC,GAAG,OAAO,CAAC;IACV,cAAc,EAAE;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IACjE,WAAW,EAAE;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAC9D,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B,CAAC,CAyBD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,EAAE,EACrB,KAAK,EAAE,YAAY,EAAE,EACrB,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GACA,MAAM,CA6BR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAW1D"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.removeNodesReferencedByFacts = exports.deduplicateNodesByUuid = exports.deduplicateFactsByUuid = exports.formatNodeLines = exports.formatFactLines = exports.formatFactLine = exports.filterAndAnnotateFacts = exports.sortFactsByRecency = exports.annotateStaleFact = exports.isFactInvalid = exports.parseDate = void 0;
|
|
4
|
+
exports.resolveProjectUserContext = resolveProjectUserContext;
|
|
4
5
|
exports.formatMemoryContext = formatMemoryContext;
|
|
5
6
|
exports.extractVisibleUuids = extractVisibleUuids;
|
|
6
|
-
const
|
|
7
|
+
const constants_js_1 = require("./constants.js");
|
|
7
8
|
const parseDate = (value) => {
|
|
8
9
|
if (!value)
|
|
9
10
|
return null;
|
|
@@ -27,7 +28,7 @@ const annotateStaleFact = (fact, now, factStaleDays) => {
|
|
|
27
28
|
const validAt = (0, exports.parseDate)(fact.valid_at);
|
|
28
29
|
if (!validAt)
|
|
29
30
|
return fact;
|
|
30
|
-
const ageDays = Math.floor((now.getTime() - validAt.getTime()) / DAY_MS);
|
|
31
|
+
const ageDays = Math.floor((now.getTime() - validAt.getTime()) / constants_js_1.DAY_MS);
|
|
31
32
|
if (ageDays < 0)
|
|
32
33
|
return fact;
|
|
33
34
|
if (ageDays < factStaleDays)
|
|
@@ -122,7 +123,38 @@ const deduplicateContext = (params) => {
|
|
|
122
123
|
const filteredNodes = (0, exports.removeNodesReferencedByFacts)(dedupedFacts, dedupedNodes);
|
|
123
124
|
return { facts: dedupedFacts, nodes: filteredNodes };
|
|
124
125
|
};
|
|
125
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Await four parallel fact/node promises, deduplicate each side, and return
|
|
128
|
+
* the resolved project and user contexts.
|
|
129
|
+
*
|
|
130
|
+
* Callers construct the promises themselves — this lets chat.ts seed the
|
|
131
|
+
* project-facts promise from an earlier drift-check fetch without issuing a
|
|
132
|
+
* duplicate network request.
|
|
133
|
+
*/
|
|
134
|
+
async function resolveProjectUserContext(promises) {
|
|
135
|
+
const [projectFacts, projectNodes, userFacts, userNodes] = await Promise.all([
|
|
136
|
+
promises.projectFacts,
|
|
137
|
+
promises.projectNodes,
|
|
138
|
+
promises.userFacts,
|
|
139
|
+
promises.userNodes,
|
|
140
|
+
]);
|
|
141
|
+
const projectContext = deduplicateContext({
|
|
142
|
+
facts: projectFacts,
|
|
143
|
+
nodes: projectNodes,
|
|
144
|
+
});
|
|
145
|
+
const userContext = deduplicateContext({
|
|
146
|
+
facts: userFacts,
|
|
147
|
+
nodes: userNodes,
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
projectContext,
|
|
151
|
+
userContext,
|
|
152
|
+
projectFacts,
|
|
153
|
+
projectNodes,
|
|
154
|
+
userFacts,
|
|
155
|
+
userNodes,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
126
158
|
/**
|
|
127
159
|
* Format Graphiti facts and nodes into a user-facing context block.
|
|
128
160
|
*/
|
|
@@ -13,12 +13,10 @@ exports.logger = {
|
|
|
13
13
|
console.log(PREFIX, ...args);
|
|
14
14
|
},
|
|
15
15
|
warn: (...args) => {
|
|
16
|
-
|
|
17
|
-
console.warn(PREFIX, ...args);
|
|
16
|
+
console.warn(PREFIX, ...args);
|
|
18
17
|
},
|
|
19
18
|
error: (...args) => {
|
|
20
|
-
|
|
21
|
-
console.error(PREFIX, ...args);
|
|
19
|
+
console.error(PREFIX, ...args);
|
|
22
20
|
},
|
|
23
21
|
debug: (...args) => {
|
|
24
22
|
if (node_process_1.default.env.GRAPHITI_DEBUG)
|