opencode-graphiti 0.1.0 → 0.1.2

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 (72) hide show
  1. package/README.md +44 -27
  2. package/esm/src/config.d.ts +3 -0
  3. package/esm/src/config.d.ts.map +1 -1
  4. package/esm/src/config.js +21 -35
  5. package/esm/src/handlers/chat.d.ts +21 -0
  6. package/esm/src/handlers/chat.d.ts.map +1 -0
  7. package/esm/src/handlers/chat.js +127 -0
  8. package/esm/src/handlers/compacting.d.ts +15 -0
  9. package/esm/src/handlers/compacting.d.ts.map +1 -0
  10. package/esm/src/handlers/compacting.js +29 -0
  11. package/esm/src/handlers/event.d.ts +18 -0
  12. package/esm/src/handlers/event.d.ts.map +1 -0
  13. package/esm/src/handlers/event.js +132 -0
  14. package/esm/src/index.d.ts +3 -1
  15. package/esm/src/index.d.ts.map +1 -1
  16. package/esm/src/index.js +28 -419
  17. package/esm/src/services/client.d.ts +33 -5
  18. package/esm/src/services/client.d.ts.map +1 -1
  19. package/esm/src/services/client.js +42 -20
  20. package/esm/src/services/compaction.d.ts +18 -69
  21. package/esm/src/services/compaction.d.ts.map +1 -1
  22. package/esm/src/services/compaction.js +86 -112
  23. package/esm/src/services/context-limit.d.ts +14 -0
  24. package/esm/src/services/context-limit.d.ts.map +1 -0
  25. package/esm/src/services/context-limit.js +41 -0
  26. package/esm/src/services/context.d.ts +5 -0
  27. package/esm/src/services/context.d.ts.map +1 -1
  28. package/esm/src/services/context.js +19 -16
  29. package/esm/src/session.d.ts +90 -0
  30. package/esm/src/session.d.ts.map +1 -0
  31. package/esm/src/session.js +304 -0
  32. package/esm/src/types/index.d.ts +28 -9
  33. package/esm/src/types/index.d.ts.map +1 -1
  34. package/esm/src/utils.d.ts +21 -0
  35. package/esm/src/utils.d.ts.map +1 -0
  36. package/esm/src/utils.js +34 -0
  37. package/package.json +3 -2
  38. package/script/src/config.d.ts +3 -0
  39. package/script/src/config.d.ts.map +1 -1
  40. package/script/src/config.js +21 -35
  41. package/script/src/handlers/chat.d.ts +21 -0
  42. package/script/src/handlers/chat.d.ts.map +1 -0
  43. package/script/src/handlers/chat.js +130 -0
  44. package/script/src/handlers/compacting.d.ts +15 -0
  45. package/script/src/handlers/compacting.d.ts.map +1 -0
  46. package/script/src/handlers/compacting.js +32 -0
  47. package/script/src/handlers/event.d.ts +18 -0
  48. package/script/src/handlers/event.d.ts.map +1 -0
  49. package/script/src/handlers/event.js +135 -0
  50. package/script/src/index.d.ts +3 -1
  51. package/script/src/index.d.ts.map +1 -1
  52. package/script/src/index.js +30 -422
  53. package/script/src/services/client.d.ts +33 -5
  54. package/script/src/services/client.d.ts.map +1 -1
  55. package/script/src/services/client.js +42 -53
  56. package/script/src/services/compaction.d.ts +18 -69
  57. package/script/src/services/compaction.d.ts.map +1 -1
  58. package/script/src/services/compaction.js +86 -113
  59. package/script/src/services/context-limit.d.ts +14 -0
  60. package/script/src/services/context-limit.d.ts.map +1 -0
  61. package/script/src/services/context-limit.js +45 -0
  62. package/script/src/services/context.d.ts +5 -0
  63. package/script/src/services/context.d.ts.map +1 -1
  64. package/script/src/services/context.js +22 -16
  65. package/script/src/session.d.ts +90 -0
  66. package/script/src/session.d.ts.map +1 -0
  67. package/script/src/session.js +308 -0
  68. package/script/src/types/index.d.ts +28 -9
  69. package/script/src/types/index.d.ts.map +1 -1
  70. package/script/src/utils.d.ts +21 -0
  71. package/script/src/utils.d.ts.map +1 -0
  72. package/script/src/utils.js +44 -0
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createChatHandler = createChatHandler;
4
+ const context_js_1 = require("../services/context.js");
5
+ const context_limit_js_1 = require("../services/context-limit.js");
6
+ const logger_js_1 = require("../services/logger.js");
7
+ const utils_js_1 = require("../utils.js");
8
+ /** Creates the `chat.message` hook handler. */
9
+ function createChatHandler(deps) {
10
+ const { sessionManager, injectionInterval, client } = deps;
11
+ const removeSyntheticMemoryParts = (parts) => parts.filter((part) => {
12
+ const synthetic = part.synthetic;
13
+ const id = typeof part.id === "string"
14
+ ? String(part.id)
15
+ : "";
16
+ if (!synthetic)
17
+ return true;
18
+ if (id.startsWith("graphiti-memory-"))
19
+ return false;
20
+ if (id.startsWith("graphiti-refresh-"))
21
+ return false;
22
+ return true;
23
+ });
24
+ const injectMemoryContext = async (state, messageText, output, prefix, useUserScope, characterBudget) => {
25
+ const userGroupId = state.userGroupId;
26
+ const projectFactsPromise = client.searchFacts({
27
+ query: messageText,
28
+ groupIds: [state.groupId],
29
+ maxFacts: 50,
30
+ });
31
+ const projectNodesPromise = client.searchNodes({
32
+ query: messageText,
33
+ groupIds: [state.groupId],
34
+ maxNodes: 30,
35
+ });
36
+ const userFactsPromise = useUserScope && userGroupId
37
+ ? client.searchFacts({
38
+ query: messageText,
39
+ groupIds: [userGroupId],
40
+ maxFacts: 20,
41
+ })
42
+ : Promise.resolve([]);
43
+ const userNodesPromise = useUserScope && userGroupId
44
+ ? client.searchNodes({
45
+ query: messageText,
46
+ groupIds: [userGroupId],
47
+ maxNodes: 10,
48
+ })
49
+ : Promise.resolve([]);
50
+ const [projectFacts, projectNodes, userFacts, userNodes] = await Promise
51
+ .all([
52
+ projectFactsPromise,
53
+ projectNodesPromise,
54
+ userFactsPromise,
55
+ userNodesPromise,
56
+ ]);
57
+ const projectContext = (0, context_js_1.formatMemoryContext)(projectFacts, projectNodes);
58
+ const userContext = (0, context_js_1.formatMemoryContext)(userFacts, userNodes);
59
+ if (!projectContext && !userContext)
60
+ return;
61
+ const projectBudget = useUserScope
62
+ ? Math.floor(characterBudget * 0.7)
63
+ : characterBudget;
64
+ const userBudget = characterBudget - projectBudget;
65
+ const truncatedProject = projectContext.slice(0, projectBudget);
66
+ const truncatedUser = useUserScope ? userContext.slice(0, userBudget) : "";
67
+ const memoryContext = [truncatedProject, truncatedUser]
68
+ .filter((section) => section.trim().length > 0)
69
+ .join("\n\n")
70
+ .slice(0, characterBudget);
71
+ if (!memoryContext)
72
+ return;
73
+ output.parts.unshift({
74
+ type: "text",
75
+ text: memoryContext,
76
+ id: `${prefix}${Date.now()}`,
77
+ sessionID: output.message.sessionID,
78
+ messageID: output.message.id,
79
+ synthetic: true,
80
+ });
81
+ logger_js_1.logger.info(`Injected ${projectFacts.length + userFacts.length} facts and ${projectNodes.length + userNodes.length} nodes`);
82
+ };
83
+ return async ({ sessionID }, output) => {
84
+ if (await sessionManager.isSubagentSession(sessionID)) {
85
+ logger_js_1.logger.debug("Ignoring subagent chat message:", sessionID);
86
+ return;
87
+ }
88
+ const { state, resolved } = await sessionManager.resolveSessionState(sessionID);
89
+ if (!resolved) {
90
+ output.allow_buffering = true;
91
+ logger_js_1.logger.debug("Unable to resolve session for message:", { sessionID });
92
+ return;
93
+ }
94
+ if (!state?.isMain) {
95
+ logger_js_1.logger.debug("Ignoring subagent chat message:", sessionID);
96
+ return;
97
+ }
98
+ state.messageCount++;
99
+ const messageText = (0, utils_js_1.extractTextFromParts)(output.parts);
100
+ if (!messageText)
101
+ return;
102
+ state.pendingMessages.push(`User: ${messageText}`);
103
+ logger_js_1.logger.info("Buffered user message", {
104
+ hook: "chat.message",
105
+ sessionID,
106
+ messageLength: messageText.length,
107
+ });
108
+ const shouldInjectOnFirst = !state.injectedMemories;
109
+ const shouldReinject = !shouldInjectOnFirst &&
110
+ injectionInterval > 0 &&
111
+ (state.messageCount - state.lastInjectionMessageCount) >=
112
+ injectionInterval;
113
+ if (!shouldInjectOnFirst && !shouldReinject)
114
+ return;
115
+ if (shouldReinject) {
116
+ output.parts = removeSyntheticMemoryParts(output.parts);
117
+ }
118
+ try {
119
+ const prefix = shouldReinject ? "graphiti-refresh-" : "graphiti-memory-";
120
+ const useUserScope = shouldInjectOnFirst;
121
+ const characterBudget = (0, context_limit_js_1.calculateInjectionBudget)(state.contextLimit);
122
+ await injectMemoryContext(state, messageText, output, prefix, useUserScope, characterBudget);
123
+ state.injectedMemories = true;
124
+ state.lastInjectionMessageCount = state.messageCount;
125
+ }
126
+ catch (err) {
127
+ logger_js_1.logger.error("Failed to inject memories:", err);
128
+ }
129
+ };
130
+ }
@@ -0,0 +1,15 @@
1
+ import type { GraphitiClient } from "../services/client.js";
2
+ import type { SessionManager } from "../session.js";
3
+ /** Dependencies for the compacting handler. */
4
+ export interface CompactingHandlerDeps {
5
+ sessionManager: SessionManager;
6
+ client: GraphitiClient;
7
+ defaultGroupId: string;
8
+ }
9
+ /** Creates the `experimental.session.compacting` hook handler. */
10
+ export declare function createCompactingHandler(deps: CompactingHandlerDeps): ({ sessionID }: {
11
+ sessionID: string;
12
+ }, output: {
13
+ context: string[];
14
+ }) => Promise<void>;
15
+ //# sourceMappingURL=compacting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compacting.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/compacting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,+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;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EACpC,QAAQ;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,mBAyBhC"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCompactingHandler = createCompactingHandler;
4
+ const compaction_js_1 = require("../services/compaction.js");
5
+ const context_limit_js_1 = require("../services/context-limit.js");
6
+ const logger_js_1 = require("../services/logger.js");
7
+ /** Creates the `experimental.session.compacting` hook handler. */
8
+ function createCompactingHandler(deps) {
9
+ const { sessionManager, client, defaultGroupId } = deps;
10
+ return async ({ sessionID }, output) => {
11
+ const state = sessionManager.getState(sessionID);
12
+ if (!state?.isMain) {
13
+ logger_js_1.logger.debug("Ignoring non-main compaction context:", sessionID);
14
+ return;
15
+ }
16
+ const groupId = state.groupId || defaultGroupId;
17
+ const characterBudget = (0, context_limit_js_1.calculateInjectionBudget)(state.contextLimit);
18
+ const additionalContext = await (0, compaction_js_1.getCompactionContext)({
19
+ client,
20
+ characterBudget,
21
+ groupIds: {
22
+ project: groupId,
23
+ user: state.userGroupId,
24
+ },
25
+ contextStrings: output.context,
26
+ });
27
+ if (additionalContext.length > 0) {
28
+ output.context.push(...additionalContext);
29
+ logger_js_1.logger.info("Injected persistent knowledge into compaction context");
30
+ }
31
+ };
32
+ }
@@ -0,0 +1,18 @@
1
+ import type { Event } from "@opencode-ai/sdk";
2
+ import type { GraphitiClient } from "../services/client.js";
3
+ import type { ProviderListClient } from "../services/context-limit.js";
4
+ import type { SessionManager } from "../session.js";
5
+ /** Dependencies for the event handler. */
6
+ export interface EventHandlerDeps {
7
+ sessionManager: SessionManager;
8
+ client: GraphitiClient;
9
+ defaultGroupId: string;
10
+ sdkClient: ProviderListClient;
11
+ directory: string;
12
+ groupIdPrefix: string;
13
+ }
14
+ /** Creates the `event` hook handler. */
15
+ export declare function createEventHandler(deps: EventHandlerDeps): ({ event }: {
16
+ event: Event;
17
+ }) => Promise<void>;
18
+ //# sourceMappingURL=event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../../src/src/handlers/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,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,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;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,mBA+J1C"}
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEventHandler = createEventHandler;
4
+ const compaction_js_1 = require("../services/compaction.js");
5
+ const context_limit_js_1 = require("../services/context-limit.js");
6
+ const logger_js_1 = require("../services/logger.js");
7
+ const utils_js_1 = require("../utils.js");
8
+ /** Creates the `event` hook handler. */
9
+ function createEventHandler(deps) {
10
+ const { sessionManager, client, defaultGroupId, sdkClient, directory, groupIdPrefix, } = deps;
11
+ const defaultUserGroupId = (0, utils_js_1.makeUserGroupId)(groupIdPrefix);
12
+ return async ({ event }) => {
13
+ try {
14
+ if (event.type === "session.created") {
15
+ const info = event.properties.info;
16
+ const sessionId = info.id;
17
+ const parentId = info.parentID ?? null;
18
+ const isMain = !parentId;
19
+ sessionManager.setParentId(sessionId, parentId);
20
+ logger_js_1.logger.info("Session created:", {
21
+ sessionId,
22
+ isMain,
23
+ parentID: info.parentID,
24
+ });
25
+ if (isMain) {
26
+ sessionManager.setState(sessionId, {
27
+ groupId: defaultGroupId,
28
+ userGroupId: defaultUserGroupId,
29
+ injectedMemories: false,
30
+ lastInjectionMessageCount: 0,
31
+ messageCount: 0,
32
+ pendingMessages: [],
33
+ contextLimit: 200_000,
34
+ isMain,
35
+ });
36
+ }
37
+ else {
38
+ logger_js_1.logger.debug("Ignoring subagent session:", sessionId);
39
+ }
40
+ return;
41
+ }
42
+ if (event.type === "session.compacted") {
43
+ const sessionId = event.properties.sessionID;
44
+ const { state, resolved } = await sessionManager.resolveSessionState(sessionId);
45
+ if (!resolved) {
46
+ logger_js_1.logger.debug("Unable to resolve session compaction:", sessionId);
47
+ return;
48
+ }
49
+ if (!state?.isMain) {
50
+ logger_js_1.logger.debug("Ignoring non-main compaction:", sessionId);
51
+ return;
52
+ }
53
+ const summary = event.properties.summary ||
54
+ "";
55
+ await sessionManager.flushPendingMessages(sessionId, "Buffered messages flushed before compaction", 0);
56
+ if (summary) {
57
+ await (0, compaction_js_1.handleCompaction)({
58
+ client,
59
+ groupId: state.groupId,
60
+ summary,
61
+ sessionId,
62
+ });
63
+ }
64
+ return;
65
+ }
66
+ if (event.type === "session.idle") {
67
+ const sessionId = event.properties.sessionID;
68
+ const { state, resolved } = await sessionManager.resolveSessionState(sessionId);
69
+ if (!resolved) {
70
+ logger_js_1.logger.debug("Unable to resolve idle session:", sessionId);
71
+ return;
72
+ }
73
+ if (!state?.isMain) {
74
+ logger_js_1.logger.debug("Ignoring non-main idle session:", sessionId);
75
+ return;
76
+ }
77
+ await sessionManager.flushPendingMessages(sessionId, "Buffered messages from OpenCode session", 50);
78
+ return;
79
+ }
80
+ if (event.type === "message.updated") {
81
+ const info = event.properties.info;
82
+ const sessionId = info.sessionID;
83
+ logger_js_1.logger.info("Message event fired", {
84
+ hook: "message.updated",
85
+ eventType: "message.updated",
86
+ sessionId,
87
+ role: info.role,
88
+ messageID: info.id,
89
+ });
90
+ const { state, resolved } = await sessionManager.resolveSessionState(sessionId);
91
+ if (!resolved) {
92
+ logger_js_1.logger.debug("Unable to resolve session for message update:", {
93
+ sessionId,
94
+ messageID: info.id,
95
+ role: info.role,
96
+ });
97
+ return;
98
+ }
99
+ if (!state?.isMain) {
100
+ logger_js_1.logger.debug("Ignoring non-main message update:", sessionId);
101
+ return;
102
+ }
103
+ if (info.role !== "assistant") {
104
+ sessionManager.deletePendingAssistant(sessionId, info.id);
105
+ return;
106
+ }
107
+ const time = info.time;
108
+ if (!time?.completed)
109
+ return;
110
+ if (sessionManager.isAssistantBuffered(sessionId, info.id))
111
+ return;
112
+ sessionManager.finalizeAssistantMessage(state, sessionId, info.id, "message.updated");
113
+ if (info.tokens && info.providerID && info.modelID) {
114
+ (0, context_limit_js_1.resolveContextLimit)(info.providerID, info.modelID, sdkClient, directory)
115
+ .then((limit) => {
116
+ state.contextLimit = limit;
117
+ })
118
+ .catch((err) => logger_js_1.logger.debug("Failed to resolve context limit", err));
119
+ }
120
+ return;
121
+ }
122
+ if (event.type === "message.part.updated") {
123
+ const part = event.properties.part;
124
+ if (!(0, utils_js_1.isTextPart)(part))
125
+ return;
126
+ const sessionId = part.sessionID;
127
+ const messageId = part.messageID;
128
+ sessionManager.bufferAssistantPart(sessionId, messageId, part.text);
129
+ }
130
+ }
131
+ catch (err) {
132
+ logger_js_1.logger.error("Event handler error", { type: event.type, err });
133
+ }
134
+ };
135
+ }
@@ -1,4 +1,6 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
- export declare const makeGroupId: (prefix: string, directory?: string) => string;
2
+ /**
3
+ * OpenCode plugin entry point for Graphiti memory integration.
4
+ */
3
5
  export declare const graphiti: Plugin;
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAqB/D,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAG,MAKhE,CAAC;AAeF,eAAO,MAAM,QAAQ,EAAE,MA+ftB,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,MA4CtB,CAAC"}