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,90 @@
1
+ import type { GraphitiClient } from "./services/client.js";
2
+ /**
3
+ * Per-session state tracked by the plugin.
4
+ */
5
+ export type SessionState = {
6
+ /** Graphiti group ID for this session. */
7
+ groupId: string;
8
+ /** Graphiti group ID for user-scoped memories. */
9
+ userGroupId: string;
10
+ /** Whether memories have been injected into this session yet. */
11
+ injectedMemories: boolean;
12
+ /** Message count at last memory injection. */
13
+ lastInjectionMessageCount: number;
14
+ /** Count of messages observed in this session. */
15
+ messageCount: number;
16
+ /** Buffered message strings awaiting flush. */
17
+ pendingMessages: string[];
18
+ /** Context window limit in tokens. */
19
+ contextLimit: number;
20
+ /** True when this session is the primary (non-subagent) session. */
21
+ isMain: boolean;
22
+ };
23
+ /**
24
+ * Minimal SDK client interface needed for session operations.
25
+ */
26
+ export interface SdkSessionClient {
27
+ session: {
28
+ /** Retrieve session metadata by ID. */
29
+ get: (args: {
30
+ path: {
31
+ id: string;
32
+ };
33
+ }) => Promise<unknown>;
34
+ /** List recent messages for a session. */
35
+ messages: (args: {
36
+ sessionID: string;
37
+ limit?: number;
38
+ }) => Promise<unknown>;
39
+ };
40
+ }
41
+ /**
42
+ * Manages session lifecycle, parent ID resolution, message buffering,
43
+ * and flushing of pending messages to Graphiti.
44
+ */
45
+ /**
46
+ * Tracks per-session state, parent resolution, message buffering,
47
+ * and flushing pending messages to Graphiti.
48
+ */
49
+ export declare class SessionManager {
50
+ private readonly defaultGroupId;
51
+ private readonly defaultUserGroupId;
52
+ private readonly sdkClient;
53
+ private readonly graphitiClient;
54
+ private sessions;
55
+ private parentIdCache;
56
+ private pendingAssistantMessages;
57
+ private bufferedAssistantMessageIds;
58
+ constructor(defaultGroupId: string, defaultUserGroupId: string, sdkClient: SdkSessionClient, graphitiClient: GraphitiClient);
59
+ /** Get the current session state, if present. */
60
+ getState(sessionId: string): SessionState | undefined;
61
+ /** Persist session state for the given session ID. */
62
+ setState(sessionId: string, state: SessionState): void;
63
+ /** Cache a resolved parent ID for a session. */
64
+ setParentId(sessionId: string, parentId: string | null): void;
65
+ /** Resolve and cache the parent ID for a session. */
66
+ resolveParentId(sessionId: string): Promise<string | null | undefined>;
67
+ /** Resolve the session state, initializing if needed. */
68
+ resolveSessionState(sessionId: string): Promise<{
69
+ state: SessionState | null;
70
+ resolved: boolean;
71
+ }>;
72
+ /** Determine whether a session is a subagent session. */
73
+ isSubagentSession(sessionId: string): Promise<boolean>;
74
+ /** Buffer partial assistant text for a streaming message. */
75
+ bufferAssistantPart(sessionId: string, messageId: string, text: string): void;
76
+ /** Check if an assistant message has already been finalized. */
77
+ isAssistantBuffered(sessionId: string, messageId: string): boolean;
78
+ /**
79
+ * Finalize a buffered assistant message and append it to pending messages.
80
+ */
81
+ finalizeAssistantMessage(state: SessionState, sessionId: string, messageId: string, source: string): void;
82
+ /** Flush pending buffered messages to Graphiti when size thresholds permit. */
83
+ flushPendingMessages(sessionId: string, sourceDescription: string, minBytes: number): Promise<void>;
84
+ /** Remove a pending assistant message by key. */
85
+ deletePendingAssistant(sessionId: string, messageId: string): void;
86
+ /** Clear cached data for a session. */
87
+ deleteSession(sessionId: string): void;
88
+ private fetchLatestAssistantMessage;
89
+ }
90
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,308 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const logger_js_1 = require("./services/logger.js");
5
+ const utils_js_1 = require("./utils.js");
6
+ /**
7
+ * Manages session lifecycle, parent ID resolution, message buffering,
8
+ * and flushing of pending messages to Graphiti.
9
+ */
10
+ /**
11
+ * Tracks per-session state, parent resolution, message buffering,
12
+ * and flushing pending messages to Graphiti.
13
+ */
14
+ class SessionManager {
15
+ constructor(defaultGroupId, defaultUserGroupId, sdkClient, graphitiClient) {
16
+ Object.defineProperty(this, "defaultGroupId", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: defaultGroupId
21
+ });
22
+ Object.defineProperty(this, "defaultUserGroupId", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: defaultUserGroupId
27
+ });
28
+ Object.defineProperty(this, "sdkClient", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: sdkClient
33
+ });
34
+ Object.defineProperty(this, "graphitiClient", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: graphitiClient
39
+ });
40
+ Object.defineProperty(this, "sessions", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: new Map()
45
+ });
46
+ Object.defineProperty(this, "parentIdCache", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: new Map()
51
+ });
52
+ Object.defineProperty(this, "pendingAssistantMessages", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: new Map()
57
+ });
58
+ Object.defineProperty(this, "bufferedAssistantMessageIds", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: new Set()
63
+ });
64
+ }
65
+ /** Get the current session state, if present. */
66
+ getState(sessionId) {
67
+ return this.sessions.get(sessionId);
68
+ }
69
+ /** Persist session state for the given session ID. */
70
+ setState(sessionId, state) {
71
+ this.sessions.set(sessionId, state);
72
+ }
73
+ /** Cache a resolved parent ID for a session. */
74
+ setParentId(sessionId, parentId) {
75
+ this.parentIdCache.set(sessionId, parentId);
76
+ }
77
+ /** Resolve and cache the parent ID for a session. */
78
+ async resolveParentId(sessionId) {
79
+ if (this.parentIdCache.has(sessionId)) {
80
+ return this.parentIdCache.get(sessionId) ?? null;
81
+ }
82
+ try {
83
+ const response = await this.sdkClient.session.get({
84
+ path: { id: sessionId },
85
+ });
86
+ const sessionInfo = typeof response === "object" && response !== null &&
87
+ "data" in response
88
+ ? response.data
89
+ : response;
90
+ if (!sessionInfo)
91
+ return undefined;
92
+ const parentId = sessionInfo.parentID ?? null;
93
+ this.parentIdCache.set(sessionId, parentId);
94
+ return parentId;
95
+ }
96
+ catch (err) {
97
+ logger_js_1.logger.debug("Failed to resolve session parentID", { sessionId, err });
98
+ return undefined;
99
+ }
100
+ }
101
+ /** Resolve the session state, initializing if needed. */
102
+ async resolveSessionState(sessionId) {
103
+ const parentId = await this.resolveParentId(sessionId);
104
+ if (parentId === undefined)
105
+ return { state: null, resolved: false };
106
+ if (parentId) {
107
+ this.sessions.delete(sessionId);
108
+ return { state: null, resolved: true };
109
+ }
110
+ let state = this.sessions.get(sessionId);
111
+ if (!state) {
112
+ state = {
113
+ groupId: this.defaultGroupId,
114
+ userGroupId: this.defaultUserGroupId,
115
+ injectedMemories: false,
116
+ lastInjectionMessageCount: 0,
117
+ messageCount: 0,
118
+ pendingMessages: [],
119
+ contextLimit: 200_000,
120
+ isMain: true,
121
+ };
122
+ this.sessions.set(sessionId, state);
123
+ }
124
+ return { state, resolved: true };
125
+ }
126
+ /** Determine whether a session is a subagent session. */
127
+ async isSubagentSession(sessionId) {
128
+ const parentId = await this.resolveParentId(sessionId);
129
+ return !!parentId;
130
+ }
131
+ /** Buffer partial assistant text for a streaming message. */
132
+ bufferAssistantPart(sessionId, messageId, text) {
133
+ const key = `${sessionId}:${messageId}`;
134
+ this.pendingAssistantMessages.set(key, { sessionId, text });
135
+ }
136
+ /** Check if an assistant message has already been finalized. */
137
+ isAssistantBuffered(sessionId, messageId) {
138
+ const key = `${sessionId}:${messageId}`;
139
+ return this.bufferedAssistantMessageIds.has(key);
140
+ }
141
+ /**
142
+ * Finalize a buffered assistant message and append it to pending messages.
143
+ */
144
+ finalizeAssistantMessage(state, sessionId, messageId, source) {
145
+ const key = `${sessionId}:${messageId}`;
146
+ if (this.bufferedAssistantMessageIds.has(key))
147
+ return;
148
+ const buffered = this.pendingAssistantMessages.get(key);
149
+ this.pendingAssistantMessages.delete(key);
150
+ this.bufferedAssistantMessageIds.add(key);
151
+ const messageText = buffered?.text?.trim() ?? "";
152
+ const messagePreview = messageText.slice(0, 120);
153
+ logger_js_1.logger.info("Assistant message completed", {
154
+ hook: source,
155
+ sessionId,
156
+ messageID: messageId,
157
+ source,
158
+ messageLength: messageText.length,
159
+ preview: messagePreview,
160
+ });
161
+ if (!messageText) {
162
+ logger_js_1.logger.debug("Assistant message completed without buffered text", {
163
+ hook: source,
164
+ sessionId,
165
+ messageID: messageId,
166
+ source,
167
+ });
168
+ return;
169
+ }
170
+ state.pendingMessages.push(`Assistant: ${messageText}`);
171
+ logger_js_1.logger.info("Buffered assistant reply", {
172
+ hook: source,
173
+ sessionId,
174
+ messageID: messageId,
175
+ source,
176
+ messageLength: messageText.length,
177
+ preview: messagePreview,
178
+ });
179
+ }
180
+ /** Flush pending buffered messages to Graphiti when size thresholds permit. */
181
+ async flushPendingMessages(sessionId, sourceDescription, minBytes) {
182
+ const state = this.sessions.get(sessionId);
183
+ if (!state || state.pendingMessages.length === 0)
184
+ return;
185
+ const lastMessage = state.pendingMessages.at(-1);
186
+ if (lastMessage) {
187
+ const separatorIndex = lastMessage.indexOf(":");
188
+ const role = separatorIndex === -1
189
+ ? lastMessage.trim().toLowerCase()
190
+ : lastMessage.slice(0, separatorIndex).trim().toLowerCase();
191
+ if (role === "user") {
192
+ const fallback = await this.fetchLatestAssistantMessage(sessionId);
193
+ if (fallback?.text) {
194
+ const fallbackKey = fallback.id
195
+ ? `${sessionId}:${fallback.id}`
196
+ : undefined;
197
+ const alreadyBuffered = fallbackKey
198
+ ? this.bufferedAssistantMessageIds.has(fallbackKey)
199
+ : state.pendingMessages.some((message) => message.startsWith("Assistant:") &&
200
+ message.includes(fallback.text));
201
+ if (!alreadyBuffered) {
202
+ state.pendingMessages.push(`Assistant: ${fallback.text}`);
203
+ if (fallbackKey) {
204
+ this.bufferedAssistantMessageIds.add(fallbackKey);
205
+ }
206
+ logger_js_1.logger.info("Fallback assistant fetch used", {
207
+ sessionId,
208
+ messageID: fallback.id,
209
+ messageLength: fallback.text.length,
210
+ });
211
+ }
212
+ }
213
+ }
214
+ }
215
+ const combined = state.pendingMessages.join("\n\n");
216
+ if (combined.length < minBytes)
217
+ return;
218
+ const messagesToFlush = [...state.pendingMessages];
219
+ state.pendingMessages = [];
220
+ const messageLines = messagesToFlush.map((message) => {
221
+ const separatorIndex = message.indexOf(":");
222
+ const role = separatorIndex === -1
223
+ ? "Unknown"
224
+ : message.slice(0, separatorIndex).trim();
225
+ const text = separatorIndex === -1
226
+ ? message
227
+ : message.slice(separatorIndex + 1).trim();
228
+ return `${role}: ${text}`;
229
+ });
230
+ try {
231
+ const name = combined.slice(0, 80).replace(/\n/g, " ");
232
+ logger_js_1.logger.info(`Flushing ${messagesToFlush.length} buffered message(s).`);
233
+ logger_js_1.logger.info(`Buffered message contents:\n${messageLines.join("\n")}`, { sessionId });
234
+ await this.graphitiClient.addEpisode({
235
+ name: `Buffered messages: ${name}`,
236
+ episodeBody: combined,
237
+ groupId: state.groupId,
238
+ source: "text",
239
+ sourceDescription,
240
+ });
241
+ logger_js_1.logger.info("Flushed buffered messages to Graphiti");
242
+ }
243
+ catch (err) {
244
+ logger_js_1.logger.error(`Failed to flush messages for ${sessionId}:`, err);
245
+ const currentState = this.sessions.get(sessionId);
246
+ if (currentState) {
247
+ currentState.pendingMessages = [
248
+ ...messagesToFlush,
249
+ ...currentState.pendingMessages,
250
+ ];
251
+ }
252
+ }
253
+ }
254
+ /** Remove a pending assistant message by key. */
255
+ deletePendingAssistant(sessionId, messageId) {
256
+ const key = `${sessionId}:${messageId}`;
257
+ this.pendingAssistantMessages.delete(key);
258
+ }
259
+ /** Clear cached data for a session. */
260
+ deleteSession(sessionId) {
261
+ this.sessions.delete(sessionId);
262
+ this.parentIdCache.delete(sessionId);
263
+ for (const key of this.pendingAssistantMessages.keys()) {
264
+ if (key.startsWith(`${sessionId}:`)) {
265
+ this.pendingAssistantMessages.delete(key);
266
+ }
267
+ }
268
+ for (const key of this.bufferedAssistantMessageIds) {
269
+ if (key.startsWith(`${sessionId}:`)) {
270
+ this.bufferedAssistantMessageIds.delete(key);
271
+ }
272
+ }
273
+ }
274
+ async fetchLatestAssistantMessage(sessionId) {
275
+ try {
276
+ const response = await this.sdkClient.session.messages({
277
+ sessionID: sessionId,
278
+ limit: 20,
279
+ });
280
+ const payload = response && typeof response === "object" &&
281
+ "data" in response
282
+ ? response.data
283
+ : response;
284
+ const messages = Array.isArray(payload)
285
+ ? payload
286
+ : [];
287
+ if (messages.length === 0)
288
+ return null;
289
+ const lastAssistant = [...messages]
290
+ .reverse()
291
+ .find((message) => message.info?.role === "assistant");
292
+ if (!lastAssistant)
293
+ return null;
294
+ const text = (0, utils_js_1.extractTextFromParts)(lastAssistant.parts);
295
+ if (!text)
296
+ return null;
297
+ return { id: lastAssistant.info?.id, text };
298
+ }
299
+ catch (err) {
300
+ logger_js_1.logger.debug("Failed to list session messages for fallback", {
301
+ sessionId,
302
+ err,
303
+ });
304
+ return null;
305
+ }
306
+ }
307
+ }
308
+ exports.SessionManager = SessionManager;
@@ -1,48 +1,67 @@
1
+ /** Plugin configuration for Graphiti memory integration. */
1
2
  export interface GraphitiConfig {
3
+ /** URL of the Graphiti MCP server endpoint. */
2
4
  endpoint: string;
5
+ /** Prefix for group IDs to namespace project memories. */
3
6
  groupIdPrefix: string;
4
- maxFacts: number;
5
- maxNodes: number;
6
- maxEpisodes: number;
7
- injectOnFirstMessage: boolean;
8
- enableCompactionSave: boolean;
9
- compactionThreshold?: number;
10
- minTokensForCompaction?: number;
11
- compactionCooldownMs?: number;
12
- autoResumeAfterCompaction?: boolean;
7
+ /** Number of user messages between reinjections (0 disables). */
8
+ injectionInterval: number;
13
9
  }
10
+ /** A fact retrieved from the Graphiti knowledge graph. */
14
11
  export interface GraphitiFact {
12
+ /** Unique identifier for the fact. */
15
13
  uuid: string;
14
+ /** Human-readable fact content. */
16
15
  fact: string;
16
+ /** Timestamp when the fact becomes valid. */
17
17
  valid_at?: string;
18
+ /** Timestamp when the fact becomes invalid. */
18
19
  invalid_at?: string;
20
+ /** Source entity for the fact edge. */
19
21
  source_node?: {
20
22
  name: string;
21
23
  uuid: string;
22
24
  };
25
+ /** Target entity for the fact edge. */
23
26
  target_node?: {
24
27
  name: string;
25
28
  uuid: string;
26
29
  };
27
30
  }
31
+ /** Response payload containing Graphiti facts. */
28
32
  export interface GraphitiFactsResponse {
33
+ /** List of facts from Graphiti. */
29
34
  facts: GraphitiFact[];
30
35
  }
36
+ /** A node retrieved from the Graphiti knowledge graph. */
31
37
  export interface GraphitiNode {
38
+ /** Unique identifier for the node. */
32
39
  uuid: string;
40
+ /** Display name of the node. */
33
41
  name: string;
42
+ /** Optional summary describing the node. */
34
43
  summary?: string;
44
+ /** Optional labels associated with the node. */
35
45
  labels?: string[];
36
46
  }
47
+ /** Response payload containing Graphiti nodes. */
37
48
  export interface GraphitiNodesResponse {
49
+ /** List of nodes from Graphiti. */
38
50
  nodes: GraphitiNode[];
39
51
  }
52
+ /** An episode retrieved from Graphiti memory. */
40
53
  export interface GraphitiEpisode {
54
+ /** Unique identifier for the episode. */
41
55
  uuid: string;
56
+ /** Episode title or name. */
42
57
  name: string;
58
+ /** Episode content body. */
43
59
  content: string;
60
+ /** Optional episode source type. */
44
61
  source?: string;
62
+ /** Optional episode creation timestamp. */
45
63
  created_at?: string;
64
+ /** Optional labels associated with the episode. */
46
65
  labels?: string[];
47
66
  }
48
67
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,oBAAoB,EAAE,OAAO,CAAC;IAE9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/types/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,uCAAuC;IACvC,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAED,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB"}
@@ -0,0 +1,21 @@
1
+ import type { Part } from "@opencode-ai/sdk";
2
+ /**
3
+ * Build a sanitized Graphiti group ID from a prefix and project directory.
4
+ */
5
+ export declare const makeGroupId: (prefix: string, directory?: string) => string;
6
+ /**
7
+ * Build a sanitized Graphiti group ID from a prefix and user home directory.
8
+ */
9
+ export declare const makeUserGroupId: (prefix: string) => string;
10
+ /**
11
+ * Narrow an OpenCode Part to a non-synthetic text part.
12
+ */
13
+ export declare const isTextPart: (value: unknown) => value is Part & {
14
+ type: "text";
15
+ text: string;
16
+ };
17
+ /**
18
+ * Extract and join text from OpenCode message parts.
19
+ */
20
+ export declare const extractTextFromParts: (parts: Part[]) => string;
21
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAG,MAKhE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAMhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,IAAI,GAAG;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAMd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,IAAI,EAAE,KAAG,MACe,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractTextFromParts = exports.isTextPart = exports.makeUserGroupId = exports.makeGroupId = void 0;
7
+ const node_os_1 = __importDefault(require("node:os"));
8
+ /**
9
+ * Build a sanitized Graphiti group ID from a prefix and project directory.
10
+ */
11
+ const makeGroupId = (prefix, directory) => {
12
+ const parts = directory?.split("/").filter(Boolean) ?? [];
13
+ const projectName = parts[parts.length - 1] || "default";
14
+ const rawGroupId = `${prefix}_${projectName}`;
15
+ return rawGroupId.replace(/[^A-Za-z0-9_-]/g, "_");
16
+ };
17
+ exports.makeGroupId = makeGroupId;
18
+ /**
19
+ * Build a sanitized Graphiti group ID from a prefix and user home directory.
20
+ */
21
+ const makeUserGroupId = (prefix) => {
22
+ const homeDir = node_os_1.default.homedir() || "user";
23
+ const parts = homeDir.split("/").filter(Boolean);
24
+ const userName = parts[parts.length - 1] || "user";
25
+ const rawGroupId = `${prefix}_user_${userName}`;
26
+ return rawGroupId.replace(/[^A-Za-z0-9_-]/g, "_");
27
+ };
28
+ exports.makeUserGroupId = makeUserGroupId;
29
+ /**
30
+ * Narrow an OpenCode Part to a non-synthetic text part.
31
+ */
32
+ const isTextPart = (value) => {
33
+ if (!value || typeof value !== "object")
34
+ return false;
35
+ const part = value;
36
+ return part.type === "text" && typeof part.text === "string" &&
37
+ !part.synthetic;
38
+ };
39
+ exports.isTextPart = isTextPart;
40
+ /**
41
+ * Extract and join text from OpenCode message parts.
42
+ */
43
+ const extractTextFromParts = (parts) => parts.filter(exports.isTextPart).map((part) => part.text).join(" ").trim();
44
+ exports.extractTextFromParts = extractTextFromParts;