opencode-graphiti 0.1.9 → 0.1.11
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 +10 -1
- package/esm/src/config.d.ts.map +1 -1
- package/esm/src/config.js +30 -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 +8 -0
- package/esm/src/services/client.d.ts.map +1 -1
- package/esm/src/services/client.js +44 -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 +10 -1
- package/script/src/config.d.ts.map +1 -1
- package/script/src/config.js +33 -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 +8 -0
- package/script/src/services/client.d.ts.map +1 -1
- package/script/src/services/client.js +44 -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,
|
|
@@ -25,6 +25,7 @@ export declare class GraphitiClient {
|
|
|
25
25
|
disconnect(): Promise<void>;
|
|
26
26
|
private callTool;
|
|
27
27
|
private isSessionExpired;
|
|
28
|
+
private isRequestTimeout;
|
|
28
29
|
private reconnect;
|
|
29
30
|
/**
|
|
30
31
|
* Parse MCP tool results into JSON when possible.
|
|
@@ -41,6 +42,13 @@ export declare class GraphitiClient {
|
|
|
41
42
|
source?: "text" | "json" | "message";
|
|
42
43
|
sourceDescription?: string;
|
|
43
44
|
}): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Extract an array from a tool result that may be a bare array or a
|
|
47
|
+
* wrapped-array response object (`{ [key]: T[] }`).
|
|
48
|
+
* Returns the array when found, otherwise `null`.
|
|
49
|
+
* Public for testing.
|
|
50
|
+
*/
|
|
51
|
+
parseWrappedArray<T>(result: unknown, wrappedKey: string): T[] | null;
|
|
44
52
|
/**
|
|
45
53
|
* Search Graphiti facts matching the provided query.
|
|
46
54
|
*/
|
|
@@ -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;IASxB,OAAO,CAAC,gBAAgB;YAgBV,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;IAkB3B;;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;IAkB3B;;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;IAmB9B;;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.
|
|
@@ -120,6 +121,16 @@ class GraphitiClient {
|
|
|
120
121
|
"code" in err &&
|
|
121
122
|
err.code === 404);
|
|
122
123
|
}
|
|
124
|
+
isRequestTimeout(err) {
|
|
125
|
+
if (typeof err === "string") {
|
|
126
|
+
return /request timed out/i.test(err);
|
|
127
|
+
}
|
|
128
|
+
if (!err || typeof err !== "object")
|
|
129
|
+
return false;
|
|
130
|
+
const { code, message } = err;
|
|
131
|
+
return code === -32001 ||
|
|
132
|
+
(typeof message === "string" && /request timed out/i.test(message));
|
|
133
|
+
}
|
|
123
134
|
async reconnect() {
|
|
124
135
|
this.connected = false;
|
|
125
136
|
try {
|
|
@@ -173,6 +184,22 @@ class GraphitiClient {
|
|
|
173
184
|
});
|
|
174
185
|
logger_js_1.logger.debug("Added episode:", params.name);
|
|
175
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Extract an array from a tool result that may be a bare array or a
|
|
189
|
+
* wrapped-array response object (`{ [key]: T[] }`).
|
|
190
|
+
* Returns the array when found, otherwise `null`.
|
|
191
|
+
* Public for testing.
|
|
192
|
+
*/
|
|
193
|
+
parseWrappedArray(result, wrappedKey) {
|
|
194
|
+
if (Array.isArray(result))
|
|
195
|
+
return result;
|
|
196
|
+
if (result &&
|
|
197
|
+
typeof result === "object" &&
|
|
198
|
+
Array.isArray(result[wrappedKey])) {
|
|
199
|
+
return result[wrappedKey];
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
176
203
|
/**
|
|
177
204
|
* Search Graphiti facts matching the provided query.
|
|
178
205
|
*/
|
|
@@ -183,16 +210,13 @@ class GraphitiClient {
|
|
|
183
210
|
group_ids: params.groupIds,
|
|
184
211
|
max_facts: params.maxFacts || 10,
|
|
185
212
|
});
|
|
186
|
-
|
|
187
|
-
return result;
|
|
188
|
-
if (result &&
|
|
189
|
-
typeof result === "object" &&
|
|
190
|
-
Array.isArray(result.facts)) {
|
|
191
|
-
return result.facts;
|
|
192
|
-
}
|
|
193
|
-
return [];
|
|
213
|
+
return this.parseWrappedArray(result, "facts") ?? [];
|
|
194
214
|
}
|
|
195
215
|
catch (err) {
|
|
216
|
+
if (this.isRequestTimeout(err)) {
|
|
217
|
+
logger_js_1.logger.warn("searchFacts request timed out; returning no facts");
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
196
220
|
logger_js_1.logger.error("searchFacts error:", err);
|
|
197
221
|
return [];
|
|
198
222
|
}
|
|
@@ -207,16 +231,13 @@ class GraphitiClient {
|
|
|
207
231
|
group_ids: params.groupIds,
|
|
208
232
|
max_nodes: params.maxNodes || 10,
|
|
209
233
|
});
|
|
210
|
-
|
|
211
|
-
return result;
|
|
212
|
-
if (result &&
|
|
213
|
-
typeof result === "object" &&
|
|
214
|
-
Array.isArray(result.nodes)) {
|
|
215
|
-
return result.nodes;
|
|
216
|
-
}
|
|
217
|
-
return [];
|
|
234
|
+
return this.parseWrappedArray(result, "nodes") ?? [];
|
|
218
235
|
}
|
|
219
236
|
catch (err) {
|
|
237
|
+
if (this.isRequestTimeout(err)) {
|
|
238
|
+
logger_js_1.logger.warn("searchNodes request timed out; returning no nodes");
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
220
241
|
logger_js_1.logger.error("searchNodes error:", err);
|
|
221
242
|
return [];
|
|
222
243
|
}
|
|
@@ -230,16 +251,15 @@ class GraphitiClient {
|
|
|
230
251
|
group_id: params.groupId,
|
|
231
252
|
last_n: params.lastN,
|
|
232
253
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
typeof result === "object" &&
|
|
237
|
-
Array.isArray(result.episodes)) {
|
|
238
|
-
return result.episodes;
|
|
239
|
-
}
|
|
240
|
-
return [];
|
|
254
|
+
const raw = this.parseWrappedArray(result, "episodes") ??
|
|
255
|
+
[];
|
|
256
|
+
return raw.map(sdk_normalize_js_1.normalizeEpisode);
|
|
241
257
|
}
|
|
242
258
|
catch (err) {
|
|
259
|
+
if (this.isRequestTimeout(err)) {
|
|
260
|
+
logger_js_1.logger.warn("getEpisodes request timed out; returning no episodes");
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
243
263
|
logger_js_1.logger.error("getEpisodes error:", err);
|
|
244
264
|
return [];
|
|
245
265
|
}
|
|
@@ -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
|
*/
|