opencode-graphiti 0.1.5 → 0.1.7

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