opencode-graphiti 0.0.0-development

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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +358 -0
  3. package/esm/_dnt.polyfills.d.ts +166 -0
  4. package/esm/_dnt.polyfills.d.ts.map +1 -0
  5. package/esm/_dnt.polyfills.js +177 -0
  6. package/esm/_dnt.shims.d.ts +6 -0
  7. package/esm/_dnt.shims.d.ts.map +1 -0
  8. package/esm/_dnt.shims.js +61 -0
  9. package/esm/deno.d.ts +45 -0
  10. package/esm/deno.d.ts.map +1 -0
  11. package/esm/deno.js +39 -0
  12. package/esm/mod.d.ts +3 -0
  13. package/esm/mod.d.ts.map +1 -0
  14. package/esm/mod.js +2 -0
  15. package/esm/package.json +3 -0
  16. package/esm/src/config.d.ts +20 -0
  17. package/esm/src/config.d.ts.map +1 -0
  18. package/esm/src/config.js +246 -0
  19. package/esm/src/handlers/chat.d.ts +14 -0
  20. package/esm/src/handlers/chat.d.ts.map +1 -0
  21. package/esm/src/handlers/chat.js +60 -0
  22. package/esm/src/handlers/compacting.d.ts +9 -0
  23. package/esm/src/handlers/compacting.d.ts.map +1 -0
  24. package/esm/src/handlers/compacting.js +30 -0
  25. package/esm/src/handlers/event.d.ts +22 -0
  26. package/esm/src/handlers/event.d.ts.map +1 -0
  27. package/esm/src/handlers/event.js +287 -0
  28. package/esm/src/handlers/messages.d.ts +9 -0
  29. package/esm/src/handlers/messages.d.ts.map +1 -0
  30. package/esm/src/handlers/messages.js +93 -0
  31. package/esm/src/index.d.ts +5 -0
  32. package/esm/src/index.d.ts.map +1 -0
  33. package/esm/src/index.js +153 -0
  34. package/esm/src/services/batch-drain.d.ts +23 -0
  35. package/esm/src/services/batch-drain.d.ts.map +1 -0
  36. package/esm/src/services/batch-drain.js +217 -0
  37. package/esm/src/services/connection-manager.d.ts +104 -0
  38. package/esm/src/services/connection-manager.d.ts.map +1 -0
  39. package/esm/src/services/connection-manager.js +621 -0
  40. package/esm/src/services/constants.d.ts +7 -0
  41. package/esm/src/services/constants.d.ts.map +1 -0
  42. package/esm/src/services/constants.js +6 -0
  43. package/esm/src/services/context-limit.d.ts +3 -0
  44. package/esm/src/services/context-limit.d.ts.map +1 -0
  45. package/esm/src/services/context-limit.js +44 -0
  46. package/esm/src/services/event-extractor.d.ts +29 -0
  47. package/esm/src/services/event-extractor.d.ts.map +1 -0
  48. package/esm/src/services/event-extractor.js +659 -0
  49. package/esm/src/services/graphiti-async.d.ts +22 -0
  50. package/esm/src/services/graphiti-async.d.ts.map +1 -0
  51. package/esm/src/services/graphiti-async.js +219 -0
  52. package/esm/src/services/graphiti-mcp.d.ts +57 -0
  53. package/esm/src/services/graphiti-mcp.d.ts.map +1 -0
  54. package/esm/src/services/graphiti-mcp.js +194 -0
  55. package/esm/src/services/logger.d.ts +9 -0
  56. package/esm/src/services/logger.d.ts.map +1 -0
  57. package/esm/src/services/logger.js +104 -0
  58. package/esm/src/services/opencode-warning.d.ts +8 -0
  59. package/esm/src/services/opencode-warning.d.ts.map +1 -0
  60. package/esm/src/services/opencode-warning.js +104 -0
  61. package/esm/src/services/redis-cache.d.ts +27 -0
  62. package/esm/src/services/redis-cache.d.ts.map +1 -0
  63. package/esm/src/services/redis-cache.js +215 -0
  64. package/esm/src/services/redis-client.d.ts +89 -0
  65. package/esm/src/services/redis-client.d.ts.map +1 -0
  66. package/esm/src/services/redis-client.js +906 -0
  67. package/esm/src/services/redis-events.d.ts +46 -0
  68. package/esm/src/services/redis-events.d.ts.map +1 -0
  69. package/esm/src/services/redis-events.js +517 -0
  70. package/esm/src/services/redis-snapshot.d.ts +16 -0
  71. package/esm/src/services/redis-snapshot.d.ts.map +1 -0
  72. package/esm/src/services/redis-snapshot.js +184 -0
  73. package/esm/src/services/render-utils.d.ts +23 -0
  74. package/esm/src/services/render-utils.d.ts.map +1 -0
  75. package/esm/src/services/render-utils.js +149 -0
  76. package/esm/src/services/runtime-teardown.d.ts +23 -0
  77. package/esm/src/services/runtime-teardown.d.ts.map +1 -0
  78. package/esm/src/services/runtime-teardown.js +119 -0
  79. package/esm/src/services/sdk-normalize.d.ts +55 -0
  80. package/esm/src/services/sdk-normalize.d.ts.map +1 -0
  81. package/esm/src/services/sdk-normalize.js +61 -0
  82. package/esm/src/session.d.ts +74 -0
  83. package/esm/src/session.d.ts.map +1 -0
  84. package/esm/src/session.js +694 -0
  85. package/esm/src/types/index.d.ts +120 -0
  86. package/esm/src/types/index.d.ts.map +1 -0
  87. package/esm/src/types/index.js +28 -0
  88. package/esm/src/utils.d.ts +27 -0
  89. package/esm/src/utils.d.ts.map +1 -0
  90. package/esm/src/utils.js +76 -0
  91. package/package.json +59 -0
  92. package/script/_dnt.polyfills.d.ts +166 -0
  93. package/script/_dnt.polyfills.d.ts.map +1 -0
  94. package/script/_dnt.polyfills.js +180 -0
  95. package/script/_dnt.shims.d.ts +6 -0
  96. package/script/_dnt.shims.d.ts.map +1 -0
  97. package/script/_dnt.shims.js +65 -0
  98. package/script/deno.d.ts +45 -0
  99. package/script/deno.d.ts.map +1 -0
  100. package/script/deno.js +41 -0
  101. package/script/mod.d.ts +3 -0
  102. package/script/mod.d.ts.map +1 -0
  103. package/script/mod.js +6 -0
  104. package/script/package.json +3 -0
  105. package/script/src/config.d.ts +20 -0
  106. package/script/src/config.d.ts.map +1 -0
  107. package/script/src/config.js +256 -0
  108. package/script/src/handlers/chat.d.ts +14 -0
  109. package/script/src/handlers/chat.d.ts.map +1 -0
  110. package/script/src/handlers/chat.js +63 -0
  111. package/script/src/handlers/compacting.d.ts +9 -0
  112. package/script/src/handlers/compacting.d.ts.map +1 -0
  113. package/script/src/handlers/compacting.js +33 -0
  114. package/script/src/handlers/event.d.ts +22 -0
  115. package/script/src/handlers/event.d.ts.map +1 -0
  116. package/script/src/handlers/event.js +290 -0
  117. package/script/src/handlers/messages.d.ts +9 -0
  118. package/script/src/handlers/messages.d.ts.map +1 -0
  119. package/script/src/handlers/messages.js +96 -0
  120. package/script/src/index.d.ts +5 -0
  121. package/script/src/index.d.ts.map +1 -0
  122. package/script/src/index.js +159 -0
  123. package/script/src/services/batch-drain.d.ts +23 -0
  124. package/script/src/services/batch-drain.d.ts.map +1 -0
  125. package/script/src/services/batch-drain.js +221 -0
  126. package/script/src/services/connection-manager.d.ts +104 -0
  127. package/script/src/services/connection-manager.d.ts.map +1 -0
  128. package/script/src/services/connection-manager.js +635 -0
  129. package/script/src/services/constants.d.ts +7 -0
  130. package/script/src/services/constants.d.ts.map +1 -0
  131. package/script/src/services/constants.js +9 -0
  132. package/script/src/services/context-limit.d.ts +3 -0
  133. package/script/src/services/context-limit.d.ts.map +1 -0
  134. package/script/src/services/context-limit.js +47 -0
  135. package/script/src/services/event-extractor.d.ts +29 -0
  136. package/script/src/services/event-extractor.d.ts.map +1 -0
  137. package/script/src/services/event-extractor.js +669 -0
  138. package/script/src/services/graphiti-async.d.ts +22 -0
  139. package/script/src/services/graphiti-async.d.ts.map +1 -0
  140. package/script/src/services/graphiti-async.js +223 -0
  141. package/script/src/services/graphiti-mcp.d.ts +57 -0
  142. package/script/src/services/graphiti-mcp.d.ts.map +1 -0
  143. package/script/src/services/graphiti-mcp.js +198 -0
  144. package/script/src/services/logger.d.ts +9 -0
  145. package/script/src/services/logger.d.ts.map +1 -0
  146. package/script/src/services/logger.js +142 -0
  147. package/script/src/services/opencode-warning.d.ts +8 -0
  148. package/script/src/services/opencode-warning.d.ts.map +1 -0
  149. package/script/src/services/opencode-warning.js +114 -0
  150. package/script/src/services/redis-cache.d.ts +27 -0
  151. package/script/src/services/redis-cache.d.ts.map +1 -0
  152. package/script/src/services/redis-cache.js +219 -0
  153. package/script/src/services/redis-client.d.ts +89 -0
  154. package/script/src/services/redis-client.d.ts.map +1 -0
  155. package/script/src/services/redis-client.js +943 -0
  156. package/script/src/services/redis-events.d.ts +46 -0
  157. package/script/src/services/redis-events.d.ts.map +1 -0
  158. package/script/src/services/redis-events.js +535 -0
  159. package/script/src/services/redis-snapshot.d.ts +16 -0
  160. package/script/src/services/redis-snapshot.d.ts.map +1 -0
  161. package/script/src/services/redis-snapshot.js +189 -0
  162. package/script/src/services/render-utils.d.ts +23 -0
  163. package/script/src/services/render-utils.d.ts.map +1 -0
  164. package/script/src/services/render-utils.js +165 -0
  165. package/script/src/services/runtime-teardown.d.ts +23 -0
  166. package/script/src/services/runtime-teardown.d.ts.map +1 -0
  167. package/script/src/services/runtime-teardown.js +155 -0
  168. package/script/src/services/sdk-normalize.d.ts +55 -0
  169. package/script/src/services/sdk-normalize.d.ts.map +1 -0
  170. package/script/src/services/sdk-normalize.js +67 -0
  171. package/script/src/session.d.ts +74 -0
  172. package/script/src/session.d.ts.map +1 -0
  173. package/script/src/session.js +698 -0
  174. package/script/src/types/index.d.ts +120 -0
  175. package/script/src/types/index.d.ts.map +1 -0
  176. package/script/src/types/index.js +33 -0
  177. package/script/src/utils.d.ts +27 -0
  178. package/script/src/utils.d.ts.map +1 -0
  179. package/script/src/utils.js +87 -0
@@ -0,0 +1,29 @@
1
+ import type { EventCategory, SessionEvent, SessionEventSourceKind } from "../types/index.js";
2
+ type EventRole = SessionEvent["role"];
3
+ type EventContext = {
4
+ summary: string;
5
+ body?: string;
6
+ detail?: string;
7
+ continuityText?: string;
8
+ keywords?: string[];
9
+ sourceKind?: SessionEventSourceKind;
10
+ refs?: string[];
11
+ metadata?: Record<string, unknown>;
12
+ };
13
+ type ExtractedEventInput = {
14
+ eventType: string;
15
+ properties?: Record<string, unknown>;
16
+ sessionId?: string;
17
+ messageText?: string;
18
+ messageCount?: number;
19
+ role?: EventRole;
20
+ };
21
+ export declare const createSessionEvent: (category: EventCategory, role: EventRole, context: EventContext) => SessionEvent;
22
+ export declare const extractUserMessageEvent: (text: string, messageCount: number) => SessionEvent;
23
+ export declare const extractAssistantMessageEvent: (text: string) => SessionEvent;
24
+ export declare const extractSessionCreatedEvent: (sessionId?: string) => SessionEvent;
25
+ export declare const extractCompactionEvent: (summary: string) => SessionEvent;
26
+ export declare const extractStructuredEvents: (input: ExtractedEventInput) => SessionEvent[];
27
+ export declare const estimateEventSize: (event: SessionEvent) => number;
28
+ export {};
29
+ //# sourceMappingURL=event-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-extractor.d.ts","sourceRoot":"","sources":["../../../src/src/services/event-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAqC3B,KAAK,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAEtC,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,CAAC;AA2aF,eAAO,MAAM,kBAAkB,GAC7B,UAAU,aAAa,EACvB,MAAM,SAAS,EACf,SAAS,YAAY,KACpB,YAAoD,CAAC;AAExD,eAAO,MAAM,uBAAuB,GAClC,MAAM,MAAM,EACZ,cAAc,MAAM,KACnB,YAQC,CAAC;AAEL,eAAO,MAAM,4BAA4B,GAAI,MAAM,MAAM,KAAG,YAOxD,CAAC;AAEL,eAAO,MAAM,0BAA0B,GAAI,YAAY,MAAM,KAAG,YAa5D,CAAC;AAEL,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,KAAG,YAWrD,CAAC;AAqEL,eAAO,MAAM,uBAAuB,GAClC,OAAO,mBAAmB,KACzB,YAAY,EAwWd,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,YAAY,KAAG,MACN,CAAC"}
@@ -0,0 +1,659 @@
1
+ import { isHighValueMemoryText, looksLikeOperationalChatter, looksLikeToolTranscript, looksTranscriptHeavy, sanitizeMemoryInput, } from "./render-utils.js";
2
+ const MAX_SUMMARY = 200;
3
+ const MAX_BODY = 4096;
4
+ const priorityByCategory = {
5
+ decision: 0,
6
+ preference: 0,
7
+ "rule.load": 0,
8
+ "task.create": 0,
9
+ "task.update": 1,
10
+ "task.complete": 1,
11
+ "file.read": 1,
12
+ "file.write": 1,
13
+ "file.edit": 1,
14
+ "file.search": 2,
15
+ "cwd.change": 2,
16
+ "env.change": 2,
17
+ error: 2,
18
+ "git.activity": 3,
19
+ "subagent.start": 1,
20
+ "subagent.finish": 3,
21
+ "integration.call": 3,
22
+ intent: 0,
23
+ "data.import": 4,
24
+ discovery: 4,
25
+ message: 4,
26
+ "session.meta": 3,
27
+ };
28
+ const textEncoder = new TextEncoder();
29
+ const eventRoles = new Set(["user", "assistant", "tool", "system"]);
30
+ const normalizeWhitespace = (text) => text.replace(/\s+/g, " ").trim();
31
+ const normalizeMemoryWhitespace = (text) => normalizeWhitespace(sanitizeMemoryInput(text));
32
+ const summarize = (text) => normalizeWhitespace(text).slice(0, MAX_SUMMARY);
33
+ const truncateBody = (text) => text.slice(0, MAX_BODY);
34
+ const truncateDetail = (text) => text.slice(0, 600);
35
+ const truncateContinuity = (text) => text.slice(0, 800);
36
+ const USER_DECISION_PATTERN = /\b(?:must|should|keep|prefer|never|always|do not|don't|avoid|require|only)\b/i;
37
+ const USER_TASK_PATTERN = /\b(?:implement|update|fix|continue|finish|complete|add|remove|refactor|investigate|revisit|clean(?:up)?|align|strip|prevent|enforce|make|keep)\b/i;
38
+ const ASSISTANT_META_PATTERN = /\b(?:plan per target|i(?:'m| am| will| can| should| need to)|reading|checking|inspecting|updating|running|prepared|schedule(?:d)?|inject(?:ed|ion)|hot-tier|continuity)/i;
39
+ const makeId = () => crypto.randomUUID?.() ??
40
+ `${Date.now()}-${Math.random().toString(16).slice(2)}`;
41
+ const asRecord = (value) => value && typeof value === "object" && !Array.isArray(value)
42
+ ? value
43
+ : undefined;
44
+ const asString = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
45
+ const asEventRole = (value) => {
46
+ const role = asString(value);
47
+ return role && eventRoles.has(role)
48
+ ? role
49
+ : undefined;
50
+ };
51
+ const asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
52
+ const toText = (value) => {
53
+ if (typeof value === "string") {
54
+ const normalized = normalizeWhitespace(value);
55
+ return normalized || undefined;
56
+ }
57
+ if (typeof value === "number" || typeof value === "boolean") {
58
+ return String(value);
59
+ }
60
+ if (Array.isArray(value)) {
61
+ const joined = value.map((item) => toText(item)).filter(Boolean).join(" ");
62
+ return joined || undefined;
63
+ }
64
+ const record = asRecord(value);
65
+ if (!record)
66
+ return undefined;
67
+ for (const key of [
68
+ "text",
69
+ "summary",
70
+ "message",
71
+ "content",
72
+ "body",
73
+ "description",
74
+ "prompt",
75
+ "query",
76
+ "title",
77
+ "name",
78
+ "value",
79
+ "reason",
80
+ "goal",
81
+ "status",
82
+ "intent",
83
+ ]) {
84
+ const result = toText(record[key]);
85
+ if (result)
86
+ return result;
87
+ }
88
+ return undefined;
89
+ };
90
+ const sanitizeExtractedText = (value) => value ? normalizeMemoryWhitespace(value) : "";
91
+ const sanitizeRefs = (refs) => refs.map((ref) => sanitizeMemoryInput(ref)).filter(Boolean);
92
+ const shouldRejectUserText = (text) => !text || looksLikeToolTranscript(text) || looksTranscriptHeavy(text);
93
+ const shouldPromoteUserDecision = (text) => USER_DECISION_PATTERN.test(text) && !looksLikeOperationalChatter(text);
94
+ const shouldPromoteUserTask = (text) => USER_TASK_PATTERN.test(text) && !looksLikeOperationalChatter(text);
95
+ const shouldPromoteAssistantSignal = (text) => !looksLikeOperationalChatter(text) && !ASSISTANT_META_PATTERN.test(text) &&
96
+ !looksTranscriptHeavy(text) && isHighValueMemoryText(text);
97
+ const shouldCaptureToolError = (tool, text) => {
98
+ const lowerTool = tool.toLowerCase();
99
+ const lowerText = text.toLowerCase();
100
+ return !hasLowerKeyword(lowerTool, "read", "open", "grep", "search", "glob") &&
101
+ hasLowerKeyword(lowerText, "error", "failed", "exception", "unable", "exit");
102
+ };
103
+ const sourceKindForRole = (role) => role === "assistant"
104
+ ? "assistant-response"
105
+ : role === "user"
106
+ ? "user-request"
107
+ : role === "tool"
108
+ ? "tool-activity"
109
+ : "system-state";
110
+ const pickStrings = (values, limit = 8) => {
111
+ const seen = new Set();
112
+ const result = [];
113
+ for (const value of values) {
114
+ const text = toText(value);
115
+ if (!text || seen.has(text))
116
+ continue;
117
+ seen.add(text);
118
+ result.push(text);
119
+ if (result.length >= limit)
120
+ break;
121
+ }
122
+ return result;
123
+ };
124
+ const pickKeywords = (values, limit = 8) => pickStrings(values, limit).map((value) => summarize(value));
125
+ const collectInlinePathRefs = (text) => {
126
+ const refs = new Set();
127
+ for (const match of text.matchAll(/(?:[A-Za-z0-9._-]+\/)+[A-Za-z0-9._-]+(?:\.[A-Za-z0-9]{1,8})?/g)) {
128
+ const value = match[0]?.trim();
129
+ if (value)
130
+ refs.add(value);
131
+ }
132
+ return [...refs];
133
+ };
134
+ const collectPathRefs = (value, refs = new Set()) => {
135
+ if (!value)
136
+ return [...refs];
137
+ if (typeof value === "string") {
138
+ const trimmed = value.trim();
139
+ if (trimmed &&
140
+ (trimmed.includes("/") || trimmed.includes("\\") ||
141
+ /\.[A-Za-z0-9]{1,8}$/.test(trimmed))) {
142
+ refs.add(trimmed);
143
+ }
144
+ return [...refs];
145
+ }
146
+ if (Array.isArray(value)) {
147
+ for (const item of value)
148
+ collectPathRefs(item, refs);
149
+ return [...refs];
150
+ }
151
+ const record = asRecord(value);
152
+ if (!record)
153
+ return [...refs];
154
+ const nestedCall = asRecord(record.call);
155
+ if (nestedCall?.tool !== undefined) {
156
+ collectPathRefs(nestedCall, refs);
157
+ collectPathRefs(nestedCall.tool, refs);
158
+ }
159
+ for (const [key, item] of Object.entries(record)) {
160
+ if (/(path|paths|file|files|ref|refs|cwd|directory)/i.test(key)) {
161
+ collectPathRefs(item, refs);
162
+ }
163
+ }
164
+ return [...refs];
165
+ };
166
+ const hasLowerKeyword = (haystack, ...needles) => {
167
+ if (!haystack)
168
+ return false;
169
+ return needles.some((needle) => haystack.includes(needle));
170
+ };
171
+ const hasKeyword = (haystack, ...needles) => hasLowerKeyword(haystack?.toLowerCase(), ...needles);
172
+ const compactParts = (...parts) => {
173
+ const fragments = [];
174
+ for (const part of parts) {
175
+ const value = part ? normalizeWhitespace(part) : "";
176
+ if (!value)
177
+ continue;
178
+ const normalized = value.toLowerCase();
179
+ if (fragments.some((fragment) => fragment.toLowerCase() === normalized)) {
180
+ continue;
181
+ }
182
+ fragments.push(value);
183
+ }
184
+ const compact = fragments.join(" — ");
185
+ return compact || undefined;
186
+ };
187
+ const collectMetadataKeywords = (props) => pickKeywords([
188
+ props.tool,
189
+ props.name,
190
+ props.integration,
191
+ props.status,
192
+ props.result,
193
+ props.reason,
194
+ props.cwd,
195
+ ]);
196
+ const compactToolMetadata = (props, extra = {}) => {
197
+ const metadata = {};
198
+ for (const [key, value] of Object.entries({
199
+ tool: props.tool,
200
+ name: props.name,
201
+ integration: props.integration,
202
+ status: props.status,
203
+ result: props.result,
204
+ exitCode: props.exitCode,
205
+ cwd: props.cwd,
206
+ blocking: props.blocking,
207
+ resolved: props.resolved,
208
+ ...extra,
209
+ })) {
210
+ if (typeof value === "string" || typeof value === "number" ||
211
+ typeof value === "boolean") {
212
+ metadata[key] = value;
213
+ }
214
+ }
215
+ return metadata;
216
+ };
217
+ const buildContinuityText = (summary, detail, refs, keywords) => {
218
+ const fragments = [];
219
+ for (const candidate of [summary, detail, refs?.join(" "), keywords?.join(" ")]) {
220
+ const value = candidate ? normalizeWhitespace(candidate) : "";
221
+ if (!value)
222
+ continue;
223
+ const normalized = value.toLowerCase();
224
+ let replaced = false;
225
+ for (let index = fragments.length - 1; index >= 0; index -= 1) {
226
+ const existing = fragments[index];
227
+ const existingNormalized = existing.toLowerCase();
228
+ if (existingNormalized === normalized ||
229
+ existingNormalized.includes(normalized)) {
230
+ replaced = true;
231
+ break;
232
+ }
233
+ if (normalized.includes(existingNormalized)) {
234
+ fragments.splice(index, 1);
235
+ }
236
+ }
237
+ if (!replaced)
238
+ fragments.push(value);
239
+ }
240
+ const continuity = fragments.join(" ");
241
+ return continuity ? truncateContinuity(continuity) : undefined;
242
+ };
243
+ const compactMessageBody = (text) => {
244
+ const normalized = normalizeWhitespace(text);
245
+ if (!normalized)
246
+ return undefined;
247
+ if (looksTranscriptHeavy(normalized) || looksLikeToolTranscript(normalized)) {
248
+ return undefined;
249
+ }
250
+ return truncateBody(normalized.slice(0, 480));
251
+ };
252
+ const buildToolActivityContext = (tool, text, refs, props, options = {}) => {
253
+ const normalizedText = looksTranscriptHeavy(text) || looksLikeToolTranscript(text)
254
+ ? ""
255
+ : sanitizeExtractedText(text);
256
+ const cleanRefs = sanitizeRefs(refs);
257
+ const refSummary = cleanRefs.slice(0, 3).join(", ");
258
+ const statusSummary = compactParts(asString(props.status), asString(props.result), typeof props.exitCode === "number" ? `exit ${props.exitCode}` : undefined);
259
+ const summary = compactParts(options.summaryPrefix ?? tool, refSummary, statusSummary) ?? `${tool} activity`;
260
+ const detail = compactParts(summarize(normalizedText), statusSummary, cleanRefs.length > 0
261
+ ? `refs ${cleanRefs.slice(0, 4).join(", ")}`
262
+ : undefined);
263
+ const keywords = pickKeywords([
264
+ tool,
265
+ ...cleanRefs,
266
+ ...collectMetadataKeywords(props),
267
+ ...(options.extraKeywords ?? []),
268
+ ]);
269
+ return {
270
+ summary,
271
+ body: options.preserveBody ? compactMessageBody(normalizedText) : undefined,
272
+ detail,
273
+ continuityText: buildContinuityText(summary, detail, cleanRefs, keywords),
274
+ keywords,
275
+ sourceKind: options.sourceKind ?? "tool-activity",
276
+ refs: cleanRefs,
277
+ metadata: compactToolMetadata(props, options.extraMetadata),
278
+ };
279
+ };
280
+ const normalizeInput = (input) => {
281
+ const props = input.properties ?? {};
282
+ const text = sanitizeExtractedText(input.messageText ?? toText(props) ?? "");
283
+ const refs = [
284
+ ...new Set([...collectPathRefs(props), ...collectInlinePathRefs(text)]),
285
+ ];
286
+ return {
287
+ eventType: input.eventType,
288
+ props,
289
+ sessionId: input.sessionId,
290
+ text,
291
+ refs,
292
+ role: input.role ?? "system",
293
+ messageCount: asNumber(input.messageCount) ?? 1,
294
+ };
295
+ };
296
+ const createEvent = (category, role, context) => ({
297
+ id: makeId(),
298
+ ts: Date.now(),
299
+ category,
300
+ priority: priorityByCategory[category],
301
+ role,
302
+ summary: summarize(context.summary),
303
+ body: context.body ? truncateBody(context.body) : undefined,
304
+ detail: context.detail ? truncateDetail(context.detail) : undefined,
305
+ continuityText: context.continuityText
306
+ ? truncateContinuity(context.continuityText)
307
+ : undefined,
308
+ keywords: context.keywords?.filter(Boolean).slice(0, 8),
309
+ sourceKind: context.sourceKind,
310
+ refs: context.refs?.filter(Boolean),
311
+ metadata: context.metadata,
312
+ });
313
+ export const createSessionEvent = (category, role, context) => createEvent(category, role, context);
314
+ export const extractUserMessageEvent = (text, messageCount) => createEvent(messageCount <= 1 ? "intent" : "message", "user", {
315
+ summary: text,
316
+ body: compactMessageBody(text),
317
+ detail: summarize(text),
318
+ continuityText: buildContinuityText(text, summarize(text)),
319
+ keywords: pickKeywords([text]),
320
+ sourceKind: "user-request",
321
+ });
322
+ export const extractAssistantMessageEvent = (text) => createEvent("message", "assistant", {
323
+ summary: summarize(text),
324
+ detail: compactParts("Assistant response", summarize(text)),
325
+ continuityText: buildContinuityText(summarize(text), summarize(text)),
326
+ keywords: pickKeywords([text]),
327
+ sourceKind: "assistant-response",
328
+ });
329
+ export const extractSessionCreatedEvent = (sessionId) => createEvent("session.meta", "system", {
330
+ summary: `Session created${sessionId ? `: ${sessionId}` : ""}`,
331
+ detail: sessionId
332
+ ? `Session ${sessionId} initialized`
333
+ : "Session initialized",
334
+ continuityText: sessionId
335
+ ? `session created ${sessionId}`
336
+ : "session created",
337
+ keywords: pickKeywords([sessionId, "session", "created"]),
338
+ sourceKind: "system-state",
339
+ refs: sessionId ? [sessionId] : undefined,
340
+ metadata: sessionId ? { sessionId } : undefined,
341
+ });
342
+ export const extractCompactionEvent = (summary) => createEvent("task.update", "system", {
343
+ summary: `Session compacted: ${summary}`,
344
+ detail: summarize(summary),
345
+ continuityText: buildContinuityText(`Session compacted: ${summary}`, summary),
346
+ keywords: pickKeywords([summary, "compacted"]),
347
+ sourceKind: "system-state",
348
+ metadata: { compacted: true },
349
+ });
350
+ const inferTaskCategory = (text) => {
351
+ if (hasKeyword(text, "complete", "completed", "done", "finished", "resolved", "fixed")) {
352
+ return "task.complete";
353
+ }
354
+ if (hasKeyword(text, "start", "create", "begin", "plan", "goal", "implement")) {
355
+ return "task.create";
356
+ }
357
+ return "task.update";
358
+ };
359
+ const extractFromHookPayload = (input) => {
360
+ const normalized = normalizeInput(input);
361
+ const { eventType, props, sessionId, text, refs, role, messageCount } = normalized;
362
+ if (eventType === "session.created") {
363
+ return [
364
+ extractSessionCreatedEvent(sessionId ?? asString(asRecord(props.info)?.id)),
365
+ ];
366
+ }
367
+ if (eventType === "session.compacted" && text) {
368
+ return [extractCompactionEvent(text)];
369
+ }
370
+ if (eventType === "message.updated" && role === "assistant" && text) {
371
+ return [extractAssistantMessageEvent(text)];
372
+ }
373
+ if (eventType === "chat.message" && text) {
374
+ return [extractUserMessageEvent(text, messageCount)];
375
+ }
376
+ const genericSummary = text || eventType;
377
+ return [createEvent("session.meta", role, {
378
+ summary: genericSummary,
379
+ detail: summarize(text),
380
+ continuityText: buildContinuityText(genericSummary, summarize(text), refs),
381
+ keywords: pickKeywords([eventType, text, ...refs]),
382
+ sourceKind: role === "tool"
383
+ ? "tool-activity"
384
+ : role === "assistant"
385
+ ? "assistant-response"
386
+ : role === "user"
387
+ ? "user-request"
388
+ : "system-state",
389
+ refs,
390
+ metadata: { eventType },
391
+ })];
392
+ };
393
+ export const extractStructuredEvents = (input) => {
394
+ const normalized = normalizeInput(input);
395
+ const { eventType, props, text, refs, role, messageCount } = normalized;
396
+ if (eventType === "chat.message") {
397
+ if (shouldRejectUserText(text))
398
+ return [];
399
+ const events = [extractUserMessageEvent(text, messageCount)];
400
+ const lower = text.toLowerCase();
401
+ if (shouldPromoteUserDecision(text) &&
402
+ hasLowerKeyword(lower, "prefer", "please", "always", "never")) {
403
+ events.push(createEvent("preference", "user", {
404
+ summary: text,
405
+ detail: summarize(text),
406
+ continuityText: buildContinuityText(text, summarize(text)),
407
+ keywords: pickKeywords([text, "preference"]),
408
+ sourceKind: "user-request",
409
+ }));
410
+ }
411
+ if (shouldPromoteUserDecision(text) &&
412
+ hasLowerKeyword(lower, "decide", "decision", "must", "should", "keep ")) {
413
+ events.push(createEvent("decision", "user", {
414
+ summary: text,
415
+ detail: summarize(text),
416
+ continuityText: buildContinuityText(text, summarize(text)),
417
+ keywords: pickKeywords([text, "decision"]),
418
+ sourceKind: "user-request",
419
+ }));
420
+ }
421
+ if (hasLowerKeyword(lower, "import", "paste", "uploaded", "dataset", "csv", "json")) {
422
+ events.push(createEvent("data.import", "user", {
423
+ summary: text,
424
+ detail: compactParts("Imported or referenced data", summarize(text)),
425
+ continuityText: buildContinuityText(text, summarize(text), refs),
426
+ keywords: pickKeywords([text, ...refs, "data"]),
427
+ sourceKind: "user-request",
428
+ refs,
429
+ }));
430
+ }
431
+ if (shouldPromoteUserTask(text) && messageCount > 1) {
432
+ events.push(createEvent(inferTaskCategory(text), "user", {
433
+ summary: text,
434
+ detail: compactParts("User task", summarize(text)),
435
+ continuityText: buildContinuityText(text, summarize(text), refs),
436
+ keywords: pickKeywords([text, ...refs, "task"]),
437
+ sourceKind: "user-request",
438
+ refs,
439
+ }));
440
+ }
441
+ return events;
442
+ }
443
+ if (eventType === "message.updated") {
444
+ const resolvedRole = input.role ?? asEventRole(asRecord(props.info)?.role);
445
+ if (resolvedRole === "assistant" && text) {
446
+ if (!shouldPromoteAssistantSignal(text)) {
447
+ return [];
448
+ }
449
+ const events = [extractAssistantMessageEvent(text)];
450
+ if (hasKeyword(text, "discovered", "found", "identified", "confirmed")) {
451
+ events.push(createEvent("discovery", "assistant", {
452
+ summary: text,
453
+ detail: summarize(text),
454
+ continuityText: buildContinuityText(text, summarize(text), refs),
455
+ keywords: pickKeywords([text, ...refs, "discovery"]),
456
+ sourceKind: "assistant-response",
457
+ refs,
458
+ }));
459
+ }
460
+ return events;
461
+ }
462
+ }
463
+ if (eventType === "task.updated") {
464
+ const task = asRecord(props.task) ?? props;
465
+ const summary = toText(task) ?? "Task updated";
466
+ return [createEvent(inferTaskCategory(summary), "system", {
467
+ summary,
468
+ detail: compactParts("Task update", summarize(summary)),
469
+ continuityText: buildContinuityText(summary, summarize(summary), refs),
470
+ keywords: pickKeywords([summary, task.id, task.path, ...refs]),
471
+ sourceKind: "system-state",
472
+ refs: pickStrings([task.id, task.path, ...refs]),
473
+ metadata: compactToolMetadata(task),
474
+ })];
475
+ }
476
+ if (eventType === "rules.loaded") {
477
+ const summary = pickStrings([props.name, props.path, props.source, text]).join(" — ") ||
478
+ "Rules loaded";
479
+ return [createEvent("rule.load", "system", {
480
+ summary,
481
+ detail: compactParts("Rules loaded", text || summary),
482
+ continuityText: buildContinuityText(summary, text || summary, refs),
483
+ keywords: pickKeywords([summary, ...refs, "rules"]),
484
+ sourceKind: "system-state",
485
+ refs,
486
+ metadata: compactToolMetadata(props),
487
+ })];
488
+ }
489
+ if (eventType === "tool.called" || eventType === "tool.completed") {
490
+ const tool = asString(props.tool) ?? asString(props.name) ??
491
+ toText(asRecord(props.call)?.tool) ?? "tool";
492
+ const summaryText = text || `${tool} activity`;
493
+ const lowerTool = tool.toLowerCase();
494
+ const lowerText = summaryText.toLowerCase();
495
+ if (hasLowerKeyword(lowerTool, "read", "open") ||
496
+ hasLowerKeyword(lowerText, "read file", "opened")) {
497
+ return [
498
+ createEvent("file.read", "tool", buildToolActivityContext(tool, summaryText, refs, props, {
499
+ summaryPrefix: "Read",
500
+ extraKeywords: ["file", "read"],
501
+ })),
502
+ ];
503
+ }
504
+ if (hasLowerKeyword(lowerTool, "write", "create") ||
505
+ hasLowerKeyword(lowerText, "wrote", "created file")) {
506
+ return [
507
+ createEvent("file.write", "tool", buildToolActivityContext(tool, summaryText, refs, props, {
508
+ summaryPrefix: "Wrote",
509
+ extraKeywords: ["file", "write"],
510
+ })),
511
+ ];
512
+ }
513
+ if (hasLowerKeyword(lowerTool, "edit", "patch", "replace") ||
514
+ hasLowerKeyword(lowerText, "updated file", "edited")) {
515
+ return [
516
+ createEvent("file.edit", "tool", buildToolActivityContext(tool, summaryText, refs, props, {
517
+ summaryPrefix: "Edited",
518
+ extraKeywords: ["file", "edit"],
519
+ })),
520
+ ];
521
+ }
522
+ if (hasLowerKeyword(lowerTool, "grep", "search", "glob") ||
523
+ hasLowerKeyword(lowerText, "searched", "query")) {
524
+ return [
525
+ createEvent("file.search", "tool", buildToolActivityContext(tool, summaryText, refs, props, {
526
+ summaryPrefix: "Searched",
527
+ extraKeywords: ["search"],
528
+ })),
529
+ ];
530
+ }
531
+ if (hasLowerKeyword(lowerTool, "git") ||
532
+ hasLowerKeyword(lowerText, "branch", "commit", "merge", "rebase", "push", "stash")) {
533
+ return [
534
+ createEvent("git.activity", "tool", buildToolActivityContext(tool, summaryText, refs, props, {
535
+ summaryPrefix: "Git",
536
+ extraKeywords: ["git"],
537
+ preserveBody: true,
538
+ })),
539
+ ];
540
+ }
541
+ const isIntegrationActivity = hasLowerKeyword(lowerTool, "graphiti", "mcp", "redis", "http") ||
542
+ asString(props.integration);
543
+ const isUnresolvedToolFailure = props.resolved === false;
544
+ if (shouldCaptureToolError(tool, summaryText) &&
545
+ (!isIntegrationActivity || isUnresolvedToolFailure)) {
546
+ return [createEvent("error", "tool", {
547
+ ...buildToolActivityContext(tool, summaryText, refs, props, {
548
+ summaryPrefix: "Tool error",
549
+ preserveBody: true,
550
+ extraKeywords: ["error", "failed"],
551
+ extraMetadata: isUnresolvedToolFailure ? { resolved: false } : {},
552
+ }),
553
+ })];
554
+ }
555
+ if (isIntegrationActivity) {
556
+ return [
557
+ createEvent("integration.call", "tool", buildToolActivityContext(tool, summaryText, refs, props, {
558
+ summaryPrefix: "Integration",
559
+ extraKeywords: ["integration"],
560
+ })),
561
+ ];
562
+ }
563
+ }
564
+ if (eventType === "environment.updated") {
565
+ const summary = text || "Environment updated";
566
+ const entries = [];
567
+ if (hasKeyword(summary, "cwd", "directory", "working directory")) {
568
+ entries.push(createEvent("cwd.change", "system", {
569
+ summary,
570
+ detail: compactParts("Working directory updated", text),
571
+ continuityText: buildContinuityText(summary, text, refs),
572
+ keywords: pickKeywords([summary, ...refs, "cwd"]),
573
+ sourceKind: "system-state",
574
+ refs,
575
+ metadata: compactToolMetadata(props),
576
+ }));
577
+ }
578
+ entries.push(createEvent("env.change", "system", {
579
+ summary,
580
+ detail: compactParts("Environment updated", text),
581
+ continuityText: buildContinuityText(summary, text, refs),
582
+ keywords: pickKeywords([summary, ...refs, "environment"]),
583
+ sourceKind: "system-state",
584
+ refs,
585
+ metadata: compactToolMetadata(props),
586
+ }));
587
+ return entries;
588
+ }
589
+ if (eventType === "subagent.started" || eventType === "subagent.finished") {
590
+ return [
591
+ createEvent(eventType === "subagent.started" ? "subagent.start" : "subagent.finish", "system", {
592
+ summary: text || eventType,
593
+ detail: compactParts(eventType === "subagent.started"
594
+ ? "Subagent started"
595
+ : "Subagent finished", text),
596
+ continuityText: buildContinuityText(text || eventType, text, refs),
597
+ keywords: pickKeywords([
598
+ text,
599
+ props.agentId,
600
+ props.sessionId,
601
+ ...refs,
602
+ ]),
603
+ sourceKind: "system-state",
604
+ refs: pickStrings([props.agentId, props.sessionId, ...refs]),
605
+ metadata: compactToolMetadata(props),
606
+ }),
607
+ ];
608
+ }
609
+ if (eventType === "session.idle") {
610
+ return [createEvent("session.meta", "system", {
611
+ summary: text || "Session idle",
612
+ detail: compactParts("Session idle", text),
613
+ continuityText: buildContinuityText(text || "Session idle", text, refs),
614
+ keywords: pickKeywords([text, eventType, ...refs]),
615
+ sourceKind: "system-state",
616
+ refs,
617
+ metadata: { ...props, eventType },
618
+ })];
619
+ }
620
+ if (text) {
621
+ const lower = text.toLowerCase();
622
+ if (role !== "assistant" &&
623
+ hasLowerKeyword(lower, "error", "failed", "exception", "blocker")) {
624
+ return [createEvent("error", role, {
625
+ summary: text,
626
+ detail: summarize(text),
627
+ continuityText: buildContinuityText(text, summarize(text), refs),
628
+ keywords: pickKeywords([text, ...refs, "error"]),
629
+ sourceKind: sourceKindForRole(role),
630
+ refs,
631
+ metadata: { ...props, resolved: false, eventType },
632
+ })];
633
+ }
634
+ if (role !== "assistant" &&
635
+ hasLowerKeyword(lower, "discover", "found", "inspect", "observed")) {
636
+ return [createEvent("discovery", role, {
637
+ summary: text,
638
+ detail: summarize(text),
639
+ continuityText: buildContinuityText(text, summarize(text), refs),
640
+ keywords: pickKeywords([text, ...refs, "discovery"]),
641
+ sourceKind: sourceKindForRole(role),
642
+ refs,
643
+ metadata: { ...props, eventType },
644
+ })];
645
+ }
646
+ return [createEvent("message", role, {
647
+ summary: text,
648
+ body: role === "user" ? compactMessageBody(text) : undefined,
649
+ detail: summarize(text),
650
+ continuityText: buildContinuityText(text, summarize(text), refs),
651
+ keywords: pickKeywords([text, ...refs]),
652
+ sourceKind: sourceKindForRole(role),
653
+ refs,
654
+ metadata: { ...props, eventType },
655
+ })];
656
+ }
657
+ return extractFromHookPayload(input);
658
+ };
659
+ export const estimateEventSize = (event) => textEncoder.encode(JSON.stringify(event)).length;