opencode-graphiti 0.1.6 → 0.1.8
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 +39 -18
- package/esm/src/handlers/chat.d.ts.map +1 -1
- package/esm/src/handlers/chat.js +55 -21
- package/esm/src/handlers/event.d.ts.map +1 -1
- package/esm/src/handlers/event.js +4 -1
- package/esm/src/handlers/messages.d.ts +11 -0
- package/esm/src/handlers/messages.d.ts.map +1 -0
- package/esm/src/handlers/messages.js +75 -0
- package/esm/src/index.js +2 -2
- package/esm/src/services/context.d.ts +4 -0
- package/esm/src/services/context.d.ts.map +1 -1
- package/esm/src/services/context.js +15 -0
- package/esm/src/session.d.ts +6 -2
- package/esm/src/session.d.ts.map +1 -1
- package/esm/src/session.js +3 -1
- package/package.json +3 -2
- package/script/src/handlers/chat.d.ts.map +1 -1
- package/script/src/handlers/chat.js +55 -21
- package/script/src/handlers/event.d.ts.map +1 -1
- package/script/src/handlers/event.js +4 -1
- package/script/src/handlers/messages.d.ts +11 -0
- package/script/src/handlers/messages.d.ts.map +1 -0
- package/script/src/handlers/messages.js +78 -0
- package/script/src/index.js +2 -2
- package/script/src/services/context.d.ts +4 -0
- package/script/src/services/context.d.ts.map +1 -1
- package/script/src/services/context.js +16 -0
- package/script/src/session.d.ts +6 -2
- package/script/src/session.d.ts.map +1 -1
- package/script/src/session.js +3 -1
- package/esm/src/handlers/system.d.ts +0 -11
- package/esm/src/handlers/system.d.ts.map +0 -1
- package/esm/src/handlers/system.js +0 -18
- package/script/src/handlers/system.d.ts +0 -11
- package/script/src/handlers/system.d.ts.map +0 -1
- package/script/src/handlers/system.js +0 -21
package/README.md
CHANGED
|
@@ -22,12 +22,17 @@ reminded of recent project context — regardless of what survived the summary.
|
|
|
22
22
|
|
|
23
23
|
This plugin connects to a Graphiti MCP server and:
|
|
24
24
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
25
|
+
- Searches Graphiti for relevant facts and entities on each user message
|
|
26
|
+
- Injects memories into the last user message as a `<memory>` block via
|
|
27
|
+
`experimental.chat.messages.transform`, keeping the system prompt static for
|
|
28
|
+
prefix caching
|
|
29
|
+
- Detects context drift using Jaccard similarity and re-injects when the
|
|
30
|
+
conversation topic shifts
|
|
27
31
|
- Buffers user and assistant messages, flushing them to Graphiti on idle or
|
|
28
32
|
before compaction
|
|
29
33
|
- Preserves key facts during context compaction
|
|
30
34
|
- Saves compaction summaries as episodes so knowledge survives across boundaries
|
|
35
|
+
- Annotates stale facts and filters expired ones automatically
|
|
31
36
|
- Scopes memories per project (and per user) using directory-based group IDs
|
|
32
37
|
|
|
33
38
|
## Prerequisites
|
|
@@ -104,8 +109,12 @@ Create a config file at `~/.config/opencode/graphiti.jsonc`:
|
|
|
104
109
|
// Prefix for project group IDs (e.g. "opencode-my-project")
|
|
105
110
|
"groupIdPrefix": "opencode",
|
|
106
111
|
|
|
107
|
-
//
|
|
108
|
-
|
|
112
|
+
// Jaccard similarity threshold (0–1) below which memory is re-injected
|
|
113
|
+
// Lower values mean the topic must drift further before re-injection
|
|
114
|
+
"driftThreshold": 0.5,
|
|
115
|
+
|
|
116
|
+
// Number of days after which facts are annotated as stale
|
|
117
|
+
"factStaleDays": 30
|
|
109
118
|
}
|
|
110
119
|
```
|
|
111
120
|
|
|
@@ -114,27 +123,39 @@ values.
|
|
|
114
123
|
|
|
115
124
|
## How It Works
|
|
116
125
|
|
|
117
|
-
### Memory
|
|
126
|
+
### Memory Search and Caching (`chat.message`)
|
|
118
127
|
|
|
119
|
-
On
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
On each user message the plugin searches Graphiti for facts and entities
|
|
129
|
+
relevant to the message content. Results are split into project and user scopes
|
|
130
|
+
(70% / 30% budget), deduplicated, filtered for validity, annotated with
|
|
131
|
+
staleness if older than `factStaleDays`, and formatted as Markdown. The
|
|
132
|
+
formatted context is cached on the session state for the messages transform hook
|
|
133
|
+
to pick up.
|
|
123
134
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
field is unavailable, the plugin falls back to prepending synthetic parts.
|
|
135
|
+
On the very first message of a session, the plugin also loads the most recent
|
|
136
|
+
session snapshot episode to prime the conversation with prior context.
|
|
127
137
|
|
|
128
138
|
The injection budget is calculated dynamically: 5% of the model's context limit
|
|
129
139
|
(resolved from the provider list) multiplied by 4 characters per token.
|
|
130
140
|
|
|
131
|
-
###
|
|
141
|
+
### User Message Injection (`experimental.chat.messages.transform`)
|
|
142
|
+
|
|
143
|
+
A separate hook reads the cached memory context and prepends it to the last user
|
|
144
|
+
message as a `<memory data-uuids="...">` block. The `data-uuids` attribute lists
|
|
145
|
+
the fact UUIDs included in the injection, which are tracked in
|
|
146
|
+
`visibleFactUuids` so subsequent searches can filter out already-visible facts.
|
|
147
|
+
This approach keeps the system prompt static, enabling provider-side prefix
|
|
148
|
+
caching, and avoids influencing session titles. The cache is cleared after
|
|
149
|
+
injection so stale context is not re-injected on subsequent LLM calls within the
|
|
150
|
+
same turn.
|
|
151
|
+
|
|
152
|
+
### Drift-Based Re-injection (`chat.message`)
|
|
132
153
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
project
|
|
154
|
+
After the first injection, the plugin monitors for context drift on every user
|
|
155
|
+
message. It searches Graphiti for the current message and compares the returned
|
|
156
|
+
fact UUIDs against the previously injected set using Jaccard similarity. When
|
|
157
|
+
similarity drops below `driftThreshold` (default 0.5), the memory cache is
|
|
158
|
+
refreshed with project-scoped results only (no user scope).
|
|
138
159
|
|
|
139
160
|
### Message Buffering (`event`)
|
|
140
161
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAO5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,KAAK,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;AAC1D,KAAK,gBAAgB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,KAAK,iBAAiB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAO5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,KAAK,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;AAC1D,KAAK,gBAAgB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,KAAK,iBAAiB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,IAkLvC,eAAe,gBAAgB,EAAE,QAAQ,iBAAiB,mBA6EzE"}
|
package/esm/src/handlers/chat.js
CHANGED
|
@@ -46,6 +46,28 @@ export function createChatHandler(deps) {
|
|
|
46
46
|
facts: userFacts,
|
|
47
47
|
nodes: userNodes,
|
|
48
48
|
});
|
|
49
|
+
const visibleSet = new Set(state.visibleFactUuids ?? []);
|
|
50
|
+
const beforeProjectFacts = projectContext.facts.length;
|
|
51
|
+
const beforeUserFacts = userContext.facts.length;
|
|
52
|
+
projectContext.facts = projectContext.facts.filter((fact) => !visibleSet.has(fact.uuid));
|
|
53
|
+
userContext.facts = userContext.facts.filter((fact) => !visibleSet.has(fact.uuid));
|
|
54
|
+
logger.debug("Filtered visible facts from injection", {
|
|
55
|
+
visibleCount: visibleSet.size,
|
|
56
|
+
filteredProjectFacts: beforeProjectFacts - projectContext.facts.length,
|
|
57
|
+
filteredUserFacts: beforeUserFacts - userContext.facts.length,
|
|
58
|
+
remainingProjectFacts: projectContext.facts.length,
|
|
59
|
+
remainingUserFacts: userContext.facts.length,
|
|
60
|
+
});
|
|
61
|
+
if (projectContext.facts.length === 0 &&
|
|
62
|
+
userContext.facts.length === 0 &&
|
|
63
|
+
projectContext.nodes.length === 0 &&
|
|
64
|
+
userContext.nodes.length === 0) {
|
|
65
|
+
logger.debug("All facts filtered; skipping context cache", {
|
|
66
|
+
groupId: state.groupId,
|
|
67
|
+
userGroupId: state.userGroupId,
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
49
71
|
const projectContextString = formatMemoryContext(projectContext.facts, projectContext.nodes, { factStaleDays });
|
|
50
72
|
const userContextString = formatMemoryContext(userContext.facts, userContext.nodes, { factStaleDays });
|
|
51
73
|
if (!projectContextString && !userContextString)
|
|
@@ -96,24 +118,27 @@ export function createChatHandler(deps) {
|
|
|
96
118
|
.slice(0, characterBudget);
|
|
97
119
|
if (!memoryContext)
|
|
98
120
|
return;
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
const allFactUuids = [
|
|
122
|
+
...projectContext.facts.map((fact) => fact.uuid),
|
|
123
|
+
...userContext.facts.map((fact) => fact.uuid),
|
|
124
|
+
];
|
|
125
|
+
const factUuids = seedFactUuids ?? Array.from(new Set(allFactUuids));
|
|
104
126
|
state.cachedMemoryContext = memoryContext;
|
|
105
|
-
|
|
127
|
+
state.cachedFactUuids = factUuids;
|
|
128
|
+
logger.info(`Cached ${projectFacts.length + userFacts.length} facts and ${projectNodes.length + userNodes.length} nodes for user message injection`);
|
|
106
129
|
state.lastInjectionFactUuids = factUuids;
|
|
107
130
|
};
|
|
108
131
|
const computeJaccardSimilarity = (left, right) => {
|
|
109
|
-
if (left.
|
|
132
|
+
if (left.length === 0 && right.length === 0)
|
|
110
133
|
return 1;
|
|
134
|
+
const leftSet = new Set(left);
|
|
135
|
+
const rightSet = new Set(right);
|
|
111
136
|
let intersection = 0;
|
|
112
|
-
for (const value of
|
|
113
|
-
if (
|
|
137
|
+
for (const value of leftSet) {
|
|
138
|
+
if (rightSet.has(value))
|
|
114
139
|
intersection += 1;
|
|
115
140
|
}
|
|
116
|
-
const union =
|
|
141
|
+
const union = leftSet.size + rightSet.size - intersection;
|
|
117
142
|
return union === 0 ? 1 : intersection / union;
|
|
118
143
|
};
|
|
119
144
|
return async ({ sessionID }, output) => {
|
|
@@ -144,18 +169,27 @@ export function createChatHandler(deps) {
|
|
|
144
169
|
let shouldReinject = false;
|
|
145
170
|
let currentFactUuids = null;
|
|
146
171
|
if (!shouldInjectOnFirst) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
172
|
+
try {
|
|
173
|
+
const driftFacts = await client.searchFacts({
|
|
174
|
+
query: messageText,
|
|
175
|
+
groupIds: [state.groupId],
|
|
176
|
+
maxFacts: 20,
|
|
177
|
+
});
|
|
178
|
+
currentFactUuids = driftFacts.map((fact) => fact.uuid);
|
|
179
|
+
const similarity = computeJaccardSimilarity(currentFactUuids, state.lastInjectionFactUuids);
|
|
180
|
+
shouldReinject = similarity < driftThreshold;
|
|
181
|
+
if (!shouldReinject) {
|
|
182
|
+
logger.debug("Skipping reinjection; similarity above threshold", {
|
|
183
|
+
sessionID,
|
|
184
|
+
similarity,
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
logger.error("Failed to check topic drift, skipping reinjection", {
|
|
157
191
|
sessionID,
|
|
158
|
-
|
|
192
|
+
err,
|
|
159
193
|
});
|
|
160
194
|
return;
|
|
161
195
|
}
|
|
@@ -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,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,
|
|
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,mBAqLpC"}
|
|
@@ -59,7 +59,10 @@ export function createEventHandler(deps) {
|
|
|
59
59
|
groupId: defaultGroupId,
|
|
60
60
|
userGroupId: defaultUserGroupId,
|
|
61
61
|
injectedMemories: false,
|
|
62
|
-
lastInjectionFactUuids:
|
|
62
|
+
lastInjectionFactUuids: [],
|
|
63
|
+
cachedMemoryContext: undefined,
|
|
64
|
+
cachedFactUuids: undefined,
|
|
65
|
+
visibleFactUuids: [],
|
|
63
66
|
messageCount: 0,
|
|
64
67
|
pendingMessages: [],
|
|
65
68
|
contextLimit: 200_000,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
import type { SessionManager } from "../session.js";
|
|
3
|
+
type MessagesTransformHook = NonNullable<Hooks["experimental.chat.messages.transform"]>;
|
|
4
|
+
type MessagesTransformInput = Parameters<MessagesTransformHook>[0];
|
|
5
|
+
type MessagesTransformOutput = Parameters<MessagesTransformHook>[1];
|
|
6
|
+
export interface MessagesHandlerDeps {
|
|
7
|
+
sessionManager: SessionManager;
|
|
8
|
+
}
|
|
9
|
+
export declare function createMessagesHandler(deps: MessagesHandlerDeps): (_input: MessagesTransformInput, output: MessagesTransformOutput) => Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +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,mBAkFlC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { extractVisibleUuids } from "../services/context.js";
|
|
2
|
+
import { logger } from "../services/logger.js";
|
|
3
|
+
export function createMessagesHandler(deps) {
|
|
4
|
+
const { sessionManager } = deps;
|
|
5
|
+
// deno-lint-ignore require-await
|
|
6
|
+
return async (_input, output) => {
|
|
7
|
+
const lastUserEntry = [...output.messages]
|
|
8
|
+
.reverse()
|
|
9
|
+
.find((message) => message.info.role === "user");
|
|
10
|
+
if (!lastUserEntry)
|
|
11
|
+
return;
|
|
12
|
+
const sessionID = lastUserEntry.info.sessionID;
|
|
13
|
+
const state = sessionManager.getState(sessionID);
|
|
14
|
+
if (!state?.isMain) {
|
|
15
|
+
logger.debug("Skipping memory injection; not main session", {
|
|
16
|
+
sessionID,
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const allVisibleUuids = [];
|
|
21
|
+
for (const entry of output.messages) {
|
|
22
|
+
for (const part of entry.parts) {
|
|
23
|
+
if (part.type === "text" && "text" in part) {
|
|
24
|
+
const uuids = extractVisibleUuids(part.text);
|
|
25
|
+
if (uuids.length > 0) {
|
|
26
|
+
logger.debug("Found <memory> block UUIDs", {
|
|
27
|
+
sessionID,
|
|
28
|
+
uuids,
|
|
29
|
+
messageID: entry.info.id,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
allVisibleUuids.push(...uuids);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
state.visibleFactUuids = [...new Set(allVisibleUuids)];
|
|
37
|
+
logger.debug("Updated visibleFactUuids from message scan", {
|
|
38
|
+
sessionID,
|
|
39
|
+
visibleCount: state.visibleFactUuids.length,
|
|
40
|
+
});
|
|
41
|
+
if (!state.cachedMemoryContext) {
|
|
42
|
+
logger.debug("Skipping memory injection; no cached context", {
|
|
43
|
+
sessionID,
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const textPart = lastUserEntry.parts.find((part) => part.type === "text" && "text" in part);
|
|
48
|
+
if (!textPart) {
|
|
49
|
+
logger.debug("Skipping memory injection; no text part", {
|
|
50
|
+
sessionID,
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (textPart.text.includes("<memory")) {
|
|
55
|
+
logger.debug("Skipping memory injection; already injected", {
|
|
56
|
+
sessionID,
|
|
57
|
+
});
|
|
58
|
+
state.cachedMemoryContext = undefined;
|
|
59
|
+
state.cachedFactUuids = undefined;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const uuids = state.cachedFactUuids ?? [];
|
|
63
|
+
const uuidAttr = uuids.length > 0 ? ` data-uuids="${uuids.join(",")}"` : "";
|
|
64
|
+
const memoryBlock = `<memory${uuidAttr}>\n${state.cachedMemoryContext}\n</memory>`;
|
|
65
|
+
textPart.text = `${memoryBlock}\n\n${textPart.text}`;
|
|
66
|
+
logger.info("Injected memory context into last user message", {
|
|
67
|
+
sessionID,
|
|
68
|
+
factCount: uuids.length,
|
|
69
|
+
blockLength: memoryBlock.length,
|
|
70
|
+
preview: state.cachedMemoryContext.slice(0, 100),
|
|
71
|
+
});
|
|
72
|
+
state.cachedMemoryContext = undefined;
|
|
73
|
+
state.cachedFactUuids = undefined;
|
|
74
|
+
};
|
|
75
|
+
}
|
package/esm/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { loadConfig } from "./config.js";
|
|
|
2
2
|
import { createChatHandler } from "./handlers/chat.js";
|
|
3
3
|
import { createCompactingHandler } from "./handlers/compacting.js";
|
|
4
4
|
import { createEventHandler } from "./handlers/event.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createMessagesHandler } from "./handlers/messages.js";
|
|
6
6
|
import { GraphitiClient } from "./services/client.js";
|
|
7
7
|
import { logger } from "./services/logger.js";
|
|
8
8
|
import { SessionManager } from "./session.js";
|
|
@@ -44,7 +44,7 @@ export const graphiti = async (input) => {
|
|
|
44
44
|
defaultGroupId,
|
|
45
45
|
factStaleDays: config.factStaleDays,
|
|
46
46
|
}),
|
|
47
|
-
"experimental.chat.
|
|
47
|
+
"experimental.chat.messages.transform": createMessagesHandler({
|
|
48
48
|
sessionManager,
|
|
49
49
|
}),
|
|
50
50
|
};
|
|
@@ -30,4 +30,8 @@ export declare function formatMemoryContext(facts: GraphitiFact[], nodes: Graphi
|
|
|
30
30
|
factStaleDays?: number;
|
|
31
31
|
now?: Date;
|
|
32
32
|
}): string;
|
|
33
|
+
/**
|
|
34
|
+
* Extract fact UUIDs from all <memory data-uuids="..."> blocks in a text string.
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractVisibleUuids(text: string): string[];
|
|
33
37
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -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;AAIpE,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,eAAO,MAAM,kBAAkB,GAAI,QAAQ;IACzC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,KAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAQjD,CAAC;AAEF;;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"}
|
|
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;AAIpE,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,eAAO,MAAM,kBAAkB,GAAI,QAAQ;IACzC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,KAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAQjD,CAAC;AAEF;;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"}
|
|
@@ -129,3 +129,18 @@ export function formatMemoryContext(facts, nodes, options) {
|
|
|
129
129
|
}
|
|
130
130
|
return sections.join("\n");
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Extract fact UUIDs from all <memory data-uuids="..."> blocks in a text string.
|
|
134
|
+
*/
|
|
135
|
+
export function extractVisibleUuids(text) {
|
|
136
|
+
const uuids = [];
|
|
137
|
+
const regex = /<memory[^>]*\bdata-uuids="([^"]*)"[^>]*>/g;
|
|
138
|
+
let match;
|
|
139
|
+
while ((match = regex.exec(text)) !== null) {
|
|
140
|
+
const raw = match[1];
|
|
141
|
+
if (raw) {
|
|
142
|
+
uuids.push(...raw.split(",").filter(Boolean));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return uuids;
|
|
146
|
+
}
|
package/esm/src/session.d.ts
CHANGED
|
@@ -11,9 +11,13 @@ export type SessionState = {
|
|
|
11
11
|
/** Whether memories have been injected into this session yet. */
|
|
12
12
|
injectedMemories: boolean;
|
|
13
13
|
/** Fact UUIDs included in the last memory injection. */
|
|
14
|
-
lastInjectionFactUuids:
|
|
15
|
-
/** Cached formatted memory context for
|
|
14
|
+
lastInjectionFactUuids: string[];
|
|
15
|
+
/** Cached formatted memory context for user message injection. */
|
|
16
16
|
cachedMemoryContext?: string;
|
|
17
|
+
/** Fact UUIDs from cached context, for embedding in <memory> tag. */
|
|
18
|
+
cachedFactUuids?: string[];
|
|
19
|
+
/** Fact UUIDs currently visible in <memory> blocks across all messages. */
|
|
20
|
+
visibleFactUuids: string[];
|
|
17
21
|
/** Count of messages observed in this session. */
|
|
18
22
|
messageCount: number;
|
|
19
23
|
/** Buffered message strings awaiting flush. */
|
package/esm/src/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,wDAAwD;IACxD,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,wDAAwD;IACxD,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,kEAAkE;IAClE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,qBAAa,cAAc;IAUvB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAZjC,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,wBAAwB,CAG5B;IACJ,OAAO,CAAC,2BAA2B,CAAqB;gBAGrC,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,cAAc,EACzB,cAAc,EAAE,cAAc;IAGjD,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIrD,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAItD,gDAAgD;IAChD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI7D,qDAAqD;IAC/C,eAAe,CACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAsBrC,yDAAyD;IACnD,mBAAmB,CACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IA4B7D,yDAAyD;IACnD,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK5D,6DAA6D;IAC7D,mBAAmB,CACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,IAAI;IAKP,gEAAgE;IAChE,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAKlE;;OAEG;IACH,wBAAwB,CACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,IAAI;IAwCP,+EAA+E;IACzE,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAgFhB,iDAAiD;IACjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAKlE,uCAAuC;IACvC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;YAexB,2BAA2B;CAiC1C"}
|
package/esm/src/session.js
CHANGED
|
@@ -106,8 +106,10 @@ export class SessionManager {
|
|
|
106
106
|
groupId: this.defaultGroupId,
|
|
107
107
|
userGroupId: this.defaultUserGroupId,
|
|
108
108
|
injectedMemories: false,
|
|
109
|
-
lastInjectionFactUuids:
|
|
109
|
+
lastInjectionFactUuids: [],
|
|
110
110
|
cachedMemoryContext: undefined,
|
|
111
|
+
cachedFactUuids: undefined,
|
|
112
|
+
visibleFactUuids: [],
|
|
111
113
|
messageCount: 0,
|
|
112
114
|
pendingMessages: [],
|
|
113
115
|
contextLimit: 200_000,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-graphiti",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "OpenCode plugin for persistent memory via Graphiti knowledge graph",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"hooks": [
|
|
41
41
|
"chat.message",
|
|
42
42
|
"event",
|
|
43
|
-
"experimental.session.compacting"
|
|
43
|
+
"experimental.session.compacting",
|
|
44
|
+
"experimental.chat.messages.transform"
|
|
44
45
|
]
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAO5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,KAAK,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;AAC1D,KAAK,gBAAgB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,KAAK,iBAAiB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAO5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,KAAK,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;AAC1D,KAAK,gBAAgB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,KAAK,iBAAiB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,IAkLvC,eAAe,gBAAgB,EAAE,QAAQ,iBAAiB,mBA6EzE"}
|
|
@@ -49,6 +49,28 @@ function createChatHandler(deps) {
|
|
|
49
49
|
facts: userFacts,
|
|
50
50
|
nodes: userNodes,
|
|
51
51
|
});
|
|
52
|
+
const visibleSet = new Set(state.visibleFactUuids ?? []);
|
|
53
|
+
const beforeProjectFacts = projectContext.facts.length;
|
|
54
|
+
const beforeUserFacts = userContext.facts.length;
|
|
55
|
+
projectContext.facts = projectContext.facts.filter((fact) => !visibleSet.has(fact.uuid));
|
|
56
|
+
userContext.facts = userContext.facts.filter((fact) => !visibleSet.has(fact.uuid));
|
|
57
|
+
logger_js_1.logger.debug("Filtered visible facts from injection", {
|
|
58
|
+
visibleCount: visibleSet.size,
|
|
59
|
+
filteredProjectFacts: beforeProjectFacts - projectContext.facts.length,
|
|
60
|
+
filteredUserFacts: beforeUserFacts - userContext.facts.length,
|
|
61
|
+
remainingProjectFacts: projectContext.facts.length,
|
|
62
|
+
remainingUserFacts: userContext.facts.length,
|
|
63
|
+
});
|
|
64
|
+
if (projectContext.facts.length === 0 &&
|
|
65
|
+
userContext.facts.length === 0 &&
|
|
66
|
+
projectContext.nodes.length === 0 &&
|
|
67
|
+
userContext.nodes.length === 0) {
|
|
68
|
+
logger_js_1.logger.debug("All facts filtered; skipping context cache", {
|
|
69
|
+
groupId: state.groupId,
|
|
70
|
+
userGroupId: state.userGroupId,
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
52
74
|
const projectContextString = (0, context_js_1.formatMemoryContext)(projectContext.facts, projectContext.nodes, { factStaleDays });
|
|
53
75
|
const userContextString = (0, context_js_1.formatMemoryContext)(userContext.facts, userContext.nodes, { factStaleDays });
|
|
54
76
|
if (!projectContextString && !userContextString)
|
|
@@ -99,24 +121,27 @@ function createChatHandler(deps) {
|
|
|
99
121
|
.slice(0, characterBudget);
|
|
100
122
|
if (!memoryContext)
|
|
101
123
|
return;
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
const allFactUuids = [
|
|
125
|
+
...projectContext.facts.map((fact) => fact.uuid),
|
|
126
|
+
...userContext.facts.map((fact) => fact.uuid),
|
|
127
|
+
];
|
|
128
|
+
const factUuids = seedFactUuids ?? Array.from(new Set(allFactUuids));
|
|
107
129
|
state.cachedMemoryContext = memoryContext;
|
|
108
|
-
|
|
130
|
+
state.cachedFactUuids = factUuids;
|
|
131
|
+
logger_js_1.logger.info(`Cached ${projectFacts.length + userFacts.length} facts and ${projectNodes.length + userNodes.length} nodes for user message injection`);
|
|
109
132
|
state.lastInjectionFactUuids = factUuids;
|
|
110
133
|
};
|
|
111
134
|
const computeJaccardSimilarity = (left, right) => {
|
|
112
|
-
if (left.
|
|
135
|
+
if (left.length === 0 && right.length === 0)
|
|
113
136
|
return 1;
|
|
137
|
+
const leftSet = new Set(left);
|
|
138
|
+
const rightSet = new Set(right);
|
|
114
139
|
let intersection = 0;
|
|
115
|
-
for (const value of
|
|
116
|
-
if (
|
|
140
|
+
for (const value of leftSet) {
|
|
141
|
+
if (rightSet.has(value))
|
|
117
142
|
intersection += 1;
|
|
118
143
|
}
|
|
119
|
-
const union =
|
|
144
|
+
const union = leftSet.size + rightSet.size - intersection;
|
|
120
145
|
return union === 0 ? 1 : intersection / union;
|
|
121
146
|
};
|
|
122
147
|
return async ({ sessionID }, output) => {
|
|
@@ -147,18 +172,27 @@ function createChatHandler(deps) {
|
|
|
147
172
|
let shouldReinject = false;
|
|
148
173
|
let currentFactUuids = null;
|
|
149
174
|
if (!shouldInjectOnFirst) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
try {
|
|
176
|
+
const driftFacts = await client.searchFacts({
|
|
177
|
+
query: messageText,
|
|
178
|
+
groupIds: [state.groupId],
|
|
179
|
+
maxFacts: 20,
|
|
180
|
+
});
|
|
181
|
+
currentFactUuids = driftFacts.map((fact) => fact.uuid);
|
|
182
|
+
const similarity = computeJaccardSimilarity(currentFactUuids, state.lastInjectionFactUuids);
|
|
183
|
+
shouldReinject = similarity < driftThreshold;
|
|
184
|
+
if (!shouldReinject) {
|
|
185
|
+
logger_js_1.logger.debug("Skipping reinjection; similarity above threshold", {
|
|
186
|
+
sessionID,
|
|
187
|
+
similarity,
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
logger_js_1.logger.error("Failed to check topic drift, skipping reinjection", {
|
|
160
194
|
sessionID,
|
|
161
|
-
|
|
195
|
+
err,
|
|
162
196
|
});
|
|
163
197
|
return;
|
|
164
198
|
}
|
|
@@ -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,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,
|
|
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,mBAqLpC"}
|
|
@@ -62,7 +62,10 @@ function createEventHandler(deps) {
|
|
|
62
62
|
groupId: defaultGroupId,
|
|
63
63
|
userGroupId: defaultUserGroupId,
|
|
64
64
|
injectedMemories: false,
|
|
65
|
-
lastInjectionFactUuids:
|
|
65
|
+
lastInjectionFactUuids: [],
|
|
66
|
+
cachedMemoryContext: undefined,
|
|
67
|
+
cachedFactUuids: undefined,
|
|
68
|
+
visibleFactUuids: [],
|
|
66
69
|
messageCount: 0,
|
|
67
70
|
pendingMessages: [],
|
|
68
71
|
contextLimit: 200_000,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
import type { SessionManager } from "../session.js";
|
|
3
|
+
type MessagesTransformHook = NonNullable<Hooks["experimental.chat.messages.transform"]>;
|
|
4
|
+
type MessagesTransformInput = Parameters<MessagesTransformHook>[0];
|
|
5
|
+
type MessagesTransformOutput = Parameters<MessagesTransformHook>[1];
|
|
6
|
+
export interface MessagesHandlerDeps {
|
|
7
|
+
sessionManager: SessionManager;
|
|
8
|
+
}
|
|
9
|
+
export declare function createMessagesHandler(deps: MessagesHandlerDeps): (_input: MessagesTransformInput, output: MessagesTransformOutput) => Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +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,mBAkFlC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMessagesHandler = createMessagesHandler;
|
|
4
|
+
const context_js_1 = require("../services/context.js");
|
|
5
|
+
const logger_js_1 = require("../services/logger.js");
|
|
6
|
+
function createMessagesHandler(deps) {
|
|
7
|
+
const { sessionManager } = deps;
|
|
8
|
+
// deno-lint-ignore require-await
|
|
9
|
+
return async (_input, output) => {
|
|
10
|
+
const lastUserEntry = [...output.messages]
|
|
11
|
+
.reverse()
|
|
12
|
+
.find((message) => message.info.role === "user");
|
|
13
|
+
if (!lastUserEntry)
|
|
14
|
+
return;
|
|
15
|
+
const sessionID = lastUserEntry.info.sessionID;
|
|
16
|
+
const state = sessionManager.getState(sessionID);
|
|
17
|
+
if (!state?.isMain) {
|
|
18
|
+
logger_js_1.logger.debug("Skipping memory injection; not main session", {
|
|
19
|
+
sessionID,
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const allVisibleUuids = [];
|
|
24
|
+
for (const entry of output.messages) {
|
|
25
|
+
for (const part of entry.parts) {
|
|
26
|
+
if (part.type === "text" && "text" in part) {
|
|
27
|
+
const uuids = (0, context_js_1.extractVisibleUuids)(part.text);
|
|
28
|
+
if (uuids.length > 0) {
|
|
29
|
+
logger_js_1.logger.debug("Found <memory> block UUIDs", {
|
|
30
|
+
sessionID,
|
|
31
|
+
uuids,
|
|
32
|
+
messageID: entry.info.id,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
allVisibleUuids.push(...uuids);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
state.visibleFactUuids = [...new Set(allVisibleUuids)];
|
|
40
|
+
logger_js_1.logger.debug("Updated visibleFactUuids from message scan", {
|
|
41
|
+
sessionID,
|
|
42
|
+
visibleCount: state.visibleFactUuids.length,
|
|
43
|
+
});
|
|
44
|
+
if (!state.cachedMemoryContext) {
|
|
45
|
+
logger_js_1.logger.debug("Skipping memory injection; no cached context", {
|
|
46
|
+
sessionID,
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const textPart = lastUserEntry.parts.find((part) => part.type === "text" && "text" in part);
|
|
51
|
+
if (!textPart) {
|
|
52
|
+
logger_js_1.logger.debug("Skipping memory injection; no text part", {
|
|
53
|
+
sessionID,
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (textPart.text.includes("<memory")) {
|
|
58
|
+
logger_js_1.logger.debug("Skipping memory injection; already injected", {
|
|
59
|
+
sessionID,
|
|
60
|
+
});
|
|
61
|
+
state.cachedMemoryContext = undefined;
|
|
62
|
+
state.cachedFactUuids = undefined;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const uuids = state.cachedFactUuids ?? [];
|
|
66
|
+
const uuidAttr = uuids.length > 0 ? ` data-uuids="${uuids.join(",")}"` : "";
|
|
67
|
+
const memoryBlock = `<memory${uuidAttr}>\n${state.cachedMemoryContext}\n</memory>`;
|
|
68
|
+
textPart.text = `${memoryBlock}\n\n${textPart.text}`;
|
|
69
|
+
logger_js_1.logger.info("Injected memory context into last user message", {
|
|
70
|
+
sessionID,
|
|
71
|
+
factCount: uuids.length,
|
|
72
|
+
blockLength: memoryBlock.length,
|
|
73
|
+
preview: state.cachedMemoryContext.slice(0, 100),
|
|
74
|
+
});
|
|
75
|
+
state.cachedMemoryContext = undefined;
|
|
76
|
+
state.cachedFactUuids = undefined;
|
|
77
|
+
};
|
|
78
|
+
}
|
package/script/src/index.js
CHANGED
|
@@ -5,7 +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
|
|
8
|
+
const messages_js_1 = require("./handlers/messages.js");
|
|
9
9
|
const client_js_1 = require("./services/client.js");
|
|
10
10
|
const logger_js_1 = require("./services/logger.js");
|
|
11
11
|
const session_js_1 = require("./session.js");
|
|
@@ -47,7 +47,7 @@ const graphiti = async (input) => {
|
|
|
47
47
|
defaultGroupId,
|
|
48
48
|
factStaleDays: config.factStaleDays,
|
|
49
49
|
}),
|
|
50
|
-
"experimental.chat.
|
|
50
|
+
"experimental.chat.messages.transform": (0, messages_js_1.createMessagesHandler)({
|
|
51
51
|
sessionManager,
|
|
52
52
|
}),
|
|
53
53
|
};
|
|
@@ -30,4 +30,8 @@ export declare function formatMemoryContext(facts: GraphitiFact[], nodes: Graphi
|
|
|
30
30
|
factStaleDays?: number;
|
|
31
31
|
now?: Date;
|
|
32
32
|
}): string;
|
|
33
|
+
/**
|
|
34
|
+
* Extract fact UUIDs from all <memory data-uuids="..."> blocks in a text string.
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractVisibleUuids(text: string): string[];
|
|
33
37
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -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;AAIpE,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,eAAO,MAAM,kBAAkB,GAAI,QAAQ;IACzC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,KAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAQjD,CAAC;AAEF;;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"}
|
|
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;AAIpE,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,eAAO,MAAM,kBAAkB,GAAI,QAAQ;IACzC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,KAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAQjD,CAAC;AAEF;;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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deduplicateContext = 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
4
|
exports.formatMemoryContext = formatMemoryContext;
|
|
5
|
+
exports.extractVisibleUuids = extractVisibleUuids;
|
|
5
6
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
6
7
|
const parseDate = (value) => {
|
|
7
8
|
if (!value)
|
|
@@ -145,3 +146,18 @@ function formatMemoryContext(facts, nodes, options) {
|
|
|
145
146
|
}
|
|
146
147
|
return sections.join("\n");
|
|
147
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Extract fact UUIDs from all <memory data-uuids="..."> blocks in a text string.
|
|
151
|
+
*/
|
|
152
|
+
function extractVisibleUuids(text) {
|
|
153
|
+
const uuids = [];
|
|
154
|
+
const regex = /<memory[^>]*\bdata-uuids="([^"]*)"[^>]*>/g;
|
|
155
|
+
let match;
|
|
156
|
+
while ((match = regex.exec(text)) !== null) {
|
|
157
|
+
const raw = match[1];
|
|
158
|
+
if (raw) {
|
|
159
|
+
uuids.push(...raw.split(",").filter(Boolean));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return uuids;
|
|
163
|
+
}
|
package/script/src/session.d.ts
CHANGED
|
@@ -11,9 +11,13 @@ export type SessionState = {
|
|
|
11
11
|
/** Whether memories have been injected into this session yet. */
|
|
12
12
|
injectedMemories: boolean;
|
|
13
13
|
/** Fact UUIDs included in the last memory injection. */
|
|
14
|
-
lastInjectionFactUuids:
|
|
15
|
-
/** Cached formatted memory context for
|
|
14
|
+
lastInjectionFactUuids: string[];
|
|
15
|
+
/** Cached formatted memory context for user message injection. */
|
|
16
16
|
cachedMemoryContext?: string;
|
|
17
|
+
/** Fact UUIDs from cached context, for embedding in <memory> tag. */
|
|
18
|
+
cachedFactUuids?: string[];
|
|
19
|
+
/** Fact UUIDs currently visible in <memory> blocks across all messages. */
|
|
20
|
+
visibleFactUuids: string[];
|
|
17
21
|
/** Count of messages observed in this session. */
|
|
18
22
|
messageCount: number;
|
|
19
23
|
/** Buffered message strings awaiting flush. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,wDAAwD;IACxD,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/src/session.ts"],"names":[],"mappings":"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"}
|
package/script/src/session.js
CHANGED
|
@@ -109,8 +109,10 @@ class SessionManager {
|
|
|
109
109
|
groupId: this.defaultGroupId,
|
|
110
110
|
userGroupId: this.defaultUserGroupId,
|
|
111
111
|
injectedMemories: false,
|
|
112
|
-
lastInjectionFactUuids:
|
|
112
|
+
lastInjectionFactUuids: [],
|
|
113
113
|
cachedMemoryContext: undefined,
|
|
114
|
+
cachedFactUuids: undefined,
|
|
115
|
+
visibleFactUuids: [],
|
|
114
116
|
messageCount: 0,
|
|
115
117
|
pendingMessages: [],
|
|
116
118
|
contextLimit: 200_000,
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { logger } from "../services/logger.js";
|
|
2
|
-
export function createSystemHandler(deps) {
|
|
3
|
-
const { sessionManager } = deps;
|
|
4
|
-
// Assumes chat.message hook completes before system.transform fires for the same turn.
|
|
5
|
-
// deno-lint-ignore require-await
|
|
6
|
-
return async ({ sessionID }, output) => {
|
|
7
|
-
if (!sessionID)
|
|
8
|
-
return;
|
|
9
|
-
const state = sessionManager.getState(sessionID);
|
|
10
|
-
if (!state?.isMain)
|
|
11
|
-
return;
|
|
12
|
-
if (!state.cachedMemoryContext)
|
|
13
|
-
return;
|
|
14
|
-
output.system.push(state.cachedMemoryContext);
|
|
15
|
-
state.cachedMemoryContext = undefined;
|
|
16
|
-
logger.info("Injected memory context into system prompt");
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
}
|