opencode-graphiti 0.1.0

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/esm/_dnt.shims.d.ts +6 -0
  4. package/esm/_dnt.shims.d.ts.map +1 -0
  5. package/esm/_dnt.shims.js +61 -0
  6. package/esm/mod.d.ts +2 -0
  7. package/esm/mod.d.ts.map +1 -0
  8. package/esm/mod.js +1 -0
  9. package/esm/package.json +3 -0
  10. package/esm/src/config.d.ts +3 -0
  11. package/esm/src/config.d.ts.map +1 -0
  12. package/esm/src/config.js +43 -0
  13. package/esm/src/index.d.ts +4 -0
  14. package/esm/src/index.d.ts.map +1 -0
  15. package/esm/src/index.js +436 -0
  16. package/esm/src/services/client.d.ts +37 -0
  17. package/esm/src/services/client.d.ts.map +1 -0
  18. package/esm/src/services/client.js +201 -0
  19. package/esm/src/services/compaction.d.ts +93 -0
  20. package/esm/src/services/compaction.d.ts.map +1 -0
  21. package/esm/src/services/compaction.js +144 -0
  22. package/esm/src/services/context.d.ts +3 -0
  23. package/esm/src/services/context.d.ts.map +1 -0
  24. package/esm/src/services/context.js +36 -0
  25. package/esm/src/services/logger.d.ts +7 -0
  26. package/esm/src/services/logger.d.ts.map +1 -0
  27. package/esm/src/services/logger.js +22 -0
  28. package/esm/src/types/index.d.ts +48 -0
  29. package/esm/src/types/index.d.ts.map +1 -0
  30. package/esm/src/types/index.js +1 -0
  31. package/package.json +34 -0
  32. package/script/_dnt.shims.d.ts +6 -0
  33. package/script/_dnt.shims.d.ts.map +1 -0
  34. package/script/_dnt.shims.js +65 -0
  35. package/script/mod.d.ts +2 -0
  36. package/script/mod.d.ts.map +1 -0
  37. package/script/mod.js +17 -0
  38. package/script/package.json +3 -0
  39. package/script/src/config.d.ts +3 -0
  40. package/script/src/config.d.ts.map +1 -0
  41. package/script/src/config.js +79 -0
  42. package/script/src/index.d.ts +4 -0
  43. package/script/src/index.d.ts.map +1 -0
  44. package/script/src/index.js +441 -0
  45. package/script/src/services/client.d.ts +37 -0
  46. package/script/src/services/client.d.ts.map +1 -0
  47. package/script/src/services/client.js +238 -0
  48. package/script/src/services/compaction.d.ts +93 -0
  49. package/script/src/services/compaction.d.ts.map +1 -0
  50. package/script/src/services/compaction.js +149 -0
  51. package/script/src/services/context.d.ts +3 -0
  52. package/script/src/services/context.d.ts.map +1 -0
  53. package/script/src/services/context.js +39 -0
  54. package/script/src/services/logger.d.ts +7 -0
  55. package/script/src/services/logger.d.ts.map +1 -0
  56. package/script/src/services/logger.js +61 -0
  57. package/script/src/types/index.d.ts +48 -0
  58. package/script/src/types/index.d.ts.map +1 -0
  59. package/script/src/types/index.js +2 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vicary
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # opencode-graphiti
2
+
3
+ OpenCode plugin that provides persistent memory via a
4
+ [Graphiti](https://github.com/getzep/graphiti) knowledge graph.
5
+
6
+ ## Motivation
7
+
8
+ Long-running AI coding sessions depend on persistent memory to stay on track.
9
+ Graphiti's MCP server is the intended backbone for this, but in practice it is
10
+ unreliable — connections drop, queries time out, and ingestion silently fails.
11
+ When the context window fills up and OpenCode triggers compaction, the
12
+ summarizer discards details that were never persisted. The result is **context
13
+ rot**: the agent loses track of recent decisions, re-explores solved problems,
14
+ and drifts away from the original goal.
15
+
16
+ This plugin exists to close that gap. It captures chat histories and project
17
+ facts into Graphiti when the server is healthy, then **re-injects them at the
18
+ start of every session and before every compaction** so the agent is always
19
+ reminded of recent project context — regardless of what survived the summary.
20
+
21
+ ## Overview
22
+
23
+ This plugin connects to a Graphiti MCP server and:
24
+
25
+ - Injects relevant memories into the first user message of each session
26
+ - Detects user-triggered memory saves ("remember this", "keep in mind", etc.)
27
+ - Preserves key facts during context compaction
28
+ - Scopes memories per project using directory-based group IDs
29
+
30
+ ## Prerequisites
31
+
32
+ A running
33
+ [Graphiti MCP server](https://github.com/getzep/graphiti/tree/main/mcp_server)
34
+ accessible over HTTP. The easiest way to set one up:
35
+
36
+ ```bash
37
+ # Clone and start with Docker Compose
38
+ git clone https://github.com/getzep/graphiti.git
39
+ cd graphiti/mcp_server
40
+ docker compose up -d
41
+ ```
42
+
43
+ This starts the MCP server at `http://localhost:8000/mcp` with a FalkorDB
44
+ backend.
45
+
46
+ ## Installation
47
+
48
+ ### Option A: npm package (recommended)
49
+
50
+ Add the plugin to your `opencode.json` (or `opencode.jsonc`):
51
+
52
+ ```jsonc
53
+ {
54
+ "plugin": ["opencode-graphiti"]
55
+ }
56
+ ```
57
+
58
+ ### Option B: Local build
59
+
60
+ Clone and build, then reference the built file:
61
+
62
+ ```bash
63
+ git clone https://github.com/vicary/opencode-graphiti.git
64
+ cd opencode-graphiti
65
+ deno task build
66
+ ```
67
+
68
+ Then add to your `opencode.json`:
69
+
70
+ ```jsonc
71
+ {
72
+ "plugin": ["file:///absolute/path/to/opencode-graphiti/dist/index.js"]
73
+ }
74
+ ```
75
+
76
+ ### Option C: Plugin directory
77
+
78
+ Copy the built plugin into OpenCode's auto-loaded plugin directory:
79
+
80
+ ```bash
81
+ # Global (all projects)
82
+ cp dist/index.js ~/.config/opencode/plugins/opencode-graphiti.js
83
+
84
+ # Or project-level
85
+ mkdir -p .opencode/plugins
86
+ cp dist/index.js .opencode/plugins/opencode-graphiti.js
87
+ ```
88
+
89
+ No config entry needed — OpenCode loads plugins from these directories
90
+ automatically.
91
+
92
+ ## Configuration
93
+
94
+ Create a config file at `~/.config/opencode/graphiti.jsonc`:
95
+
96
+ ```jsonc
97
+ {
98
+ // Graphiti MCP server endpoint
99
+ "endpoint": "http://localhost:8000/mcp",
100
+
101
+ // Prefix for project group IDs (e.g. "opencode_my-project")
102
+ "groupIdPrefix": "opencode",
103
+
104
+ // Maximum results to retrieve
105
+ "maxFacts": 10,
106
+ "maxNodes": 5,
107
+ "maxEpisodes": 5,
108
+
109
+ // Feature toggles
110
+ "injectOnFirstMessage": true,
111
+ "enableTriggerDetection": true,
112
+ "enableCompactionSave": true
113
+ }
114
+ ```
115
+
116
+ All fields are optional — defaults are used for any missing values.
117
+
118
+ ## How It Works
119
+
120
+ ### Memory Injection (`chat.message`)
121
+
122
+ On the first user message in a session, the plugin searches Graphiti for facts
123
+ and entities relevant to the message content. Matching results are formatted and
124
+ prepended to the conversation as a synthetic context block.
125
+
126
+ ### Trigger Detection (`chat.message`)
127
+
128
+ User messages are scanned for phrases like:
129
+
130
+ - "remember this", "memorize that"
131
+ - "save this in memory", "keep this in mind"
132
+ - "don't forget", "note this"
133
+ - "for future reference"
134
+
135
+ When detected, the message content is saved as an episode in Graphiti.
136
+
137
+ ### Compaction Preservation (`event` + `experimental.session.compacting`)
138
+
139
+ When OpenCode compacts the context window:
140
+
141
+ 1. **Before compaction**: The plugin injects known facts into the compaction
142
+ context, so the summarizer preserves important knowledge.
143
+ 2. **After compaction**: If a summary is produced, it is saved as an episode to
144
+ Graphiti, ensuring knowledge survives across compaction boundaries.
145
+
146
+ ### Project Scoping
147
+
148
+ Each project gets a unique `group_id` derived from its directory name (e.g.
149
+ `opencode_my-project`). Group IDs only allow letters, numbers, dashes, and
150
+ underscores (colons are not allowed). This ensures memories from different
151
+ projects stay isolated.
152
+
153
+ ## Development
154
+
155
+ ```bash
156
+ # Format
157
+ deno fmt
158
+
159
+ # Lint
160
+ deno lint
161
+
162
+ # Type check
163
+ deno check src/index.ts
164
+
165
+ # Build
166
+ deno task build
167
+ ```
168
+
169
+ ## License
170
+
171
+ MIT
172
+
173
+ ## Acknowledgement
174
+
175
+ This project is inspired by
176
+ [opencode-openmemory](https://github.com/happycastle114/opencode-openmemory)
@@ -0,0 +1,6 @@
1
+ import { Deno } from "@deno/shim-deno";
2
+ export { Deno } from "@deno/shim-deno";
3
+ export declare const dntGlobalThis: Omit<typeof globalThis, "Deno"> & {
4
+ Deno: typeof Deno;
5
+ };
6
+ //# sourceMappingURL=_dnt.shims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAKvC,eAAO,MAAM,aAAa;;CAA2C,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { Deno } from "@deno/shim-deno";
2
+ export { Deno } from "@deno/shim-deno";
3
+ const dntGlobals = {
4
+ Deno,
5
+ };
6
+ export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
7
+ function createMergeProxy(baseObj, extObj) {
8
+ return new Proxy(baseObj, {
9
+ get(_target, prop, _receiver) {
10
+ if (prop in extObj) {
11
+ return extObj[prop];
12
+ }
13
+ else {
14
+ return baseObj[prop];
15
+ }
16
+ },
17
+ set(_target, prop, value) {
18
+ if (prop in extObj) {
19
+ delete extObj[prop];
20
+ }
21
+ baseObj[prop] = value;
22
+ return true;
23
+ },
24
+ deleteProperty(_target, prop) {
25
+ let success = false;
26
+ if (prop in extObj) {
27
+ delete extObj[prop];
28
+ success = true;
29
+ }
30
+ if (prop in baseObj) {
31
+ delete baseObj[prop];
32
+ success = true;
33
+ }
34
+ return success;
35
+ },
36
+ ownKeys(_target) {
37
+ const baseKeys = Reflect.ownKeys(baseObj);
38
+ const extKeys = Reflect.ownKeys(extObj);
39
+ const extKeysSet = new Set(extKeys);
40
+ return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
41
+ },
42
+ defineProperty(_target, prop, desc) {
43
+ if (prop in extObj) {
44
+ delete extObj[prop];
45
+ }
46
+ Reflect.defineProperty(baseObj, prop, desc);
47
+ return true;
48
+ },
49
+ getOwnPropertyDescriptor(_target, prop) {
50
+ if (prop in extObj) {
51
+ return Reflect.getOwnPropertyDescriptor(extObj, prop);
52
+ }
53
+ else {
54
+ return Reflect.getOwnPropertyDescriptor(baseObj, prop);
55
+ }
56
+ },
57
+ has(_target, prop) {
58
+ return prop in extObj || prop in baseObj;
59
+ },
60
+ });
61
+ }
package/esm/mod.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./src/index.js";
2
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
package/esm/mod.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index.js";
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,3 @@
1
+ import type { GraphitiConfig } from "./types/index.js";
2
+ export declare function loadConfig(): GraphitiConfig;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA0BvD,wBAAgB,UAAU,IAAI,cAAc,CAe3C"}
@@ -0,0 +1,43 @@
1
+ import * as dntShim from "../_dnt.shims.js";
2
+ import { cosmiconfigSync } from "cosmiconfig";
3
+ import { readFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ const DEFAULT_CONFIG = {
7
+ endpoint: "http://localhost:8000/mcp",
8
+ groupIdPrefix: "opencode",
9
+ maxFacts: 10,
10
+ maxNodes: 5,
11
+ maxEpisodes: 5,
12
+ injectOnFirstMessage: true,
13
+ enableCompactionSave: true,
14
+ compactionThreshold: 0.8,
15
+ minTokensForCompaction: 50000,
16
+ compactionCooldownMs: 30000,
17
+ autoResumeAfterCompaction: true,
18
+ };
19
+ function parseJsonc(text) {
20
+ let stripped = text.replace(/(^|\s)\/\/.*$/gm, "$1");
21
+ stripped = stripped.replace(/\/\*[\s\S]*?\*\//g, "");
22
+ return JSON.parse(stripped);
23
+ }
24
+ const createExplorer = (cwd) => {
25
+ return cosmiconfigSync("graphiti", { stopDir: cwd });
26
+ };
27
+ export function loadConfig() {
28
+ const cwd = dntShim.Deno.cwd();
29
+ const explorer = createExplorer(cwd);
30
+ const result = explorer.search(cwd);
31
+ let config = result?.config;
32
+ if (!config) {
33
+ const configPath = join(homedir(), ".config", "opencode", "graphiti.jsonc");
34
+ try {
35
+ const raw = readFileSync(configPath, "utf-8");
36
+ config = parseJsonc(raw);
37
+ }
38
+ catch {
39
+ config = null;
40
+ }
41
+ }
42
+ return { ...DEFAULT_CONFIG, ...(config ?? {}) };
43
+ }
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const makeGroupId: (prefix: string, directory?: string) => string;
3
+ export declare const graphiti: Plugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}