backpack-ontology 0.2.25 → 0.3.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 (105) hide show
  1. package/README.md +153 -3
  2. package/dist/bin/backpack-benchmark.d.ts +3 -0
  3. package/dist/bin/backpack-benchmark.d.ts.map +1 -0
  4. package/dist/bin/backpack-benchmark.js +213 -0
  5. package/dist/bin/backpack-benchmark.js.map +1 -0
  6. package/dist/bin/backpack.js +3 -3
  7. package/dist/bin/backpack.js.map +1 -1
  8. package/dist/bin/init.js +8 -11
  9. package/dist/bin/init.js.map +1 -1
  10. package/dist/core/backpack.d.ts +69 -2
  11. package/dist/core/backpack.d.ts.map +1 -1
  12. package/dist/core/backpack.js +205 -3
  13. package/dist/core/backpack.js.map +1 -1
  14. package/dist/core/draft.d.ts +42 -0
  15. package/dist/core/draft.d.ts.map +1 -0
  16. package/dist/core/draft.js +232 -0
  17. package/dist/core/draft.js.map +1 -0
  18. package/dist/core/events.d.ts +114 -0
  19. package/dist/core/events.d.ts.map +1 -0
  20. package/dist/core/events.js +375 -0
  21. package/dist/core/events.js.map +1 -0
  22. package/dist/core/graph.d.ts +3 -1
  23. package/dist/core/graph.d.ts.map +1 -1
  24. package/dist/core/graph.js +50 -0
  25. package/dist/core/graph.js.map +1 -1
  26. package/dist/core/hooks.d.ts +11 -3
  27. package/dist/core/hooks.d.ts.map +1 -1
  28. package/dist/core/hooks.js +53 -47
  29. package/dist/core/hooks.js.map +1 -1
  30. package/dist/core/normalize.d.ts +28 -0
  31. package/dist/core/normalize.d.ts.map +1 -0
  32. package/dist/core/normalize.js +133 -0
  33. package/dist/core/normalize.js.map +1 -0
  34. package/dist/core/remote-fetch.d.ts +50 -0
  35. package/dist/core/remote-fetch.d.ts.map +1 -0
  36. package/dist/core/remote-fetch.js +338 -0
  37. package/dist/core/remote-fetch.js.map +1 -0
  38. package/dist/core/remote-registry.d.ts +95 -0
  39. package/dist/core/remote-registry.d.ts.map +1 -0
  40. package/dist/core/remote-registry.js +296 -0
  41. package/dist/core/remote-registry.js.map +1 -0
  42. package/dist/core/remote-schema.d.ts +31 -0
  43. package/dist/core/remote-schema.d.ts.map +1 -0
  44. package/dist/core/remote-schema.js +252 -0
  45. package/dist/core/remote-schema.js.map +1 -0
  46. package/dist/core/role-audit.d.ts +20 -0
  47. package/dist/core/role-audit.d.ts.map +1 -0
  48. package/dist/core/role-audit.js +197 -0
  49. package/dist/core/role-audit.js.map +1 -0
  50. package/dist/core/telemetry.d.ts +2 -0
  51. package/dist/core/telemetry.d.ts.map +1 -1
  52. package/dist/core/telemetry.js +16 -0
  53. package/dist/core/telemetry.js.map +1 -1
  54. package/dist/core/token-estimate.d.ts +16 -0
  55. package/dist/core/token-estimate.d.ts.map +1 -0
  56. package/dist/core/token-estimate.js +29 -0
  57. package/dist/core/token-estimate.js.map +1 -0
  58. package/dist/core/types.d.ts +30 -1
  59. package/dist/core/types.d.ts.map +1 -1
  60. package/dist/index.d.ts +9 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +8 -1
  63. package/dist/index.js.map +1 -1
  64. package/dist/mcp/server.d.ts.map +1 -1
  65. package/dist/mcp/server.js +9 -0
  66. package/dist/mcp/server.js.map +1 -1
  67. package/dist/mcp/tools/bulk-tools.d.ts.map +1 -1
  68. package/dist/mcp/tools/bulk-tools.js +80 -12
  69. package/dist/mcp/tools/bulk-tools.js.map +1 -1
  70. package/dist/mcp/tools/edge-tools.d.ts.map +1 -1
  71. package/dist/mcp/tools/edge-tools.js +14 -18
  72. package/dist/mcp/tools/edge-tools.js.map +1 -1
  73. package/dist/mcp/tools/error-helpers.d.ts +16 -0
  74. package/dist/mcp/tools/error-helpers.d.ts.map +1 -0
  75. package/dist/mcp/tools/error-helpers.js +34 -0
  76. package/dist/mcp/tools/error-helpers.js.map +1 -0
  77. package/dist/mcp/tools/intelligence-tools.d.ts.map +1 -1
  78. package/dist/mcp/tools/intelligence-tools.js +28 -18
  79. package/dist/mcp/tools/intelligence-tools.js.map +1 -1
  80. package/dist/mcp/tools/node-tools.d.ts.map +1 -1
  81. package/dist/mcp/tools/node-tools.js +43 -40
  82. package/dist/mcp/tools/node-tools.js.map +1 -1
  83. package/dist/mcp/tools/ontology-tools.d.ts.map +1 -1
  84. package/dist/mcp/tools/ontology-tools.js +237 -12
  85. package/dist/mcp/tools/ontology-tools.js.map +1 -1
  86. package/dist/mcp/tools/remote-tools.d.ts +5 -0
  87. package/dist/mcp/tools/remote-tools.d.ts.map +1 -0
  88. package/dist/mcp/tools/remote-tools.js +295 -0
  89. package/dist/mcp/tools/remote-tools.js.map +1 -0
  90. package/dist/storage/backpack-app-backend.d.ts +1 -1
  91. package/dist/storage/backpack-app-backend.d.ts.map +1 -1
  92. package/dist/storage/backpack-app-backend.js +3 -1
  93. package/dist/storage/backpack-app-backend.js.map +1 -1
  94. package/dist/storage/event-sourced-backend.d.ts +174 -0
  95. package/dist/storage/event-sourced-backend.d.ts.map +1 -0
  96. package/dist/storage/event-sourced-backend.js +840 -0
  97. package/dist/storage/event-sourced-backend.js.map +1 -0
  98. package/dist/storage/json-file-backend.d.ts +1 -72
  99. package/dist/storage/json-file-backend.d.ts.map +1 -1
  100. package/dist/storage/json-file-backend.js +11 -549
  101. package/dist/storage/json-file-backend.js.map +1 -1
  102. package/package.json +3 -3
  103. package/hooks/auto-capture-prompt.md +0 -40
  104. package/hooks/hooks.json +0 -16
  105. package/hooks/suggest-viewer.sh +0 -4
@@ -0,0 +1,114 @@
1
+ import type { Node, Edge, LearningGraphData, LearningGraphMetadata } from "./types.js";
2
+ export declare const EVENT_SCHEMA_VERSION = 1;
3
+ export type EventOp = "node.add" | "node.update" | "node.retype" | "node.remove" | "edge.add" | "edge.remove" | "edge.retype" | "metadata.update" | "snapshot.label";
4
+ export interface BaseEvent {
5
+ /** Schema version for forward-compat checks. */
6
+ v: number;
7
+ /** ISO timestamp the event was created. */
8
+ ts: string;
9
+ /** Optional author identifier (e.g. machine ID, user email). */
10
+ author?: string;
11
+ /** Operation discriminator. */
12
+ op: EventOp;
13
+ }
14
+ export interface NodeAddEvent extends BaseEvent {
15
+ op: "node.add";
16
+ node: Node;
17
+ }
18
+ export interface NodeUpdateEvent extends BaseEvent {
19
+ op: "node.update";
20
+ id: string;
21
+ /** Properties to merge into the existing node. Pass `null` for a key to delete it. */
22
+ properties: Record<string, unknown>;
23
+ }
24
+ export interface NodeRetypeEvent extends BaseEvent {
25
+ op: "node.retype";
26
+ id: string;
27
+ /** New type for the node. The ID and properties are unchanged. */
28
+ type: string;
29
+ }
30
+ export interface NodeRemoveEvent extends BaseEvent {
31
+ op: "node.remove";
32
+ id: string;
33
+ }
34
+ export interface EdgeAddEvent extends BaseEvent {
35
+ op: "edge.add";
36
+ edge: Edge;
37
+ }
38
+ export interface EdgeRemoveEvent extends BaseEvent {
39
+ op: "edge.remove";
40
+ id: string;
41
+ }
42
+ export interface EdgeRetypeEvent extends BaseEvent {
43
+ op: "edge.retype";
44
+ id: string;
45
+ /** New type for the edge. The ID, endpoints, and properties are unchanged. */
46
+ type: string;
47
+ }
48
+ export interface MetadataUpdateEvent extends BaseEvent {
49
+ op: "metadata.update";
50
+ patch: Partial<Pick<LearningGraphMetadata, "name" | "description">>;
51
+ }
52
+ export interface SnapshotLabelEvent extends BaseEvent {
53
+ op: "snapshot.label";
54
+ /** Optional human-readable label. */
55
+ label?: string;
56
+ }
57
+ export type GraphEvent = NodeAddEvent | NodeUpdateEvent | NodeRetypeEvent | NodeRemoveEvent | EdgeAddEvent | EdgeRemoveEvent | EdgeRetypeEvent | MetadataUpdateEvent | SnapshotLabelEvent;
58
+ export declare class EventReplayError extends Error {
59
+ readonly eventIndex: number;
60
+ readonly event: GraphEvent | null;
61
+ constructor(message: string, eventIndex: number, event: GraphEvent | null);
62
+ }
63
+ export declare function makeNodeAddEvent(node: Node, author?: string): NodeAddEvent;
64
+ export declare function makeNodeUpdateEvent(id: string, properties: Record<string, unknown>, author?: string): NodeUpdateEvent;
65
+ export declare function makeNodeRetypeEvent(id: string, type: string, author?: string): NodeRetypeEvent;
66
+ export declare function makeNodeRemoveEvent(id: string, author?: string): NodeRemoveEvent;
67
+ export declare function makeEdgeAddEvent(edge: Edge, author?: string): EdgeAddEvent;
68
+ export declare function makeEdgeRemoveEvent(id: string, author?: string): EdgeRemoveEvent;
69
+ export declare function makeEdgeRetypeEvent(id: string, type: string, author?: string): EdgeRetypeEvent;
70
+ export declare function makeMetadataUpdateEvent(patch: Partial<Pick<LearningGraphMetadata, "name" | "description">>, author?: string): MetadataUpdateEvent;
71
+ export declare function makeSnapshotLabelEvent(label?: string, author?: string): SnapshotLabelEvent;
72
+ /**
73
+ * Apply a sequence of events on top of an existing state. Pure function:
74
+ * does not mutate inputs. Use this when you already have a materialized
75
+ * state and want to extend it with newly-appended events.
76
+ */
77
+ export declare function applyEvents(state: LearningGraphData, events: GraphEvent[]): LearningGraphData;
78
+ /**
79
+ * Replay a sequence of events into a LearningGraphData state, starting
80
+ * from an empty state with the given metadata. Pure function: no IO, no
81
+ * mutation of inputs.
82
+ *
83
+ * Throws EventReplayError on the first invalid event. The error
84
+ * carries the event index so the caller can locate the problem in
85
+ * the source log.
86
+ */
87
+ export declare function replay(events: GraphEvent[], initialMetadata: LearningGraphMetadata): LearningGraphData;
88
+ /**
89
+ * Compute the minimal sequence of events that would transform `before`
90
+ * into `after`. Used by the storage backend's `saveOntology` to support
91
+ * coarse-grained "save the whole graph" callers transparently.
92
+ *
93
+ * Detection rules:
94
+ * - Nodes present in `after` but not `before`: node.add
95
+ * - Nodes present in `before` but not `after`: node.remove (cascades to edges)
96
+ * - Nodes present in both with different properties: node.update
97
+ * - Edges present in `after` but not `before`: edge.add
98
+ * - Edges present in `before` but not `after`: edge.remove
99
+ * - Metadata name/description differences: metadata.update
100
+ */
101
+ export declare function diffToEvents(before: LearningGraphData, after: LearningGraphData, author?: string): GraphEvent[];
102
+ /**
103
+ * Serialize an event to a single JSONL line (no trailing newline).
104
+ */
105
+ export declare function serializeEvent(event: GraphEvent): string;
106
+ /**
107
+ * Parse a single JSONL line into an event. Throws on malformed input.
108
+ */
109
+ export declare function parseEvent(line: string): GraphEvent;
110
+ /**
111
+ * Parse a full JSONL document into an array of events. Skips blank lines.
112
+ */
113
+ export declare function parseEventLog(text: string): GraphEvent[];
114
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,375 @@
1
+ // ============================================================
2
+ // Event types and replay logic for event-sourced learning graphs.
3
+ //
4
+ // A graph branch is an append-only sequence of events. The current
5
+ // state of the branch is the result of replaying all events from the
6
+ // beginning. Snapshots are events with a `label` field. Rollback is
7
+ // truncating the event log at a snapshot's position.
8
+ //
9
+ // This module is pure: events in, state out, no IO. The storage
10
+ // backend handles persistence; this module handles semantics.
11
+ // ============================================================
12
+ // --- Event schema version ---
13
+ //
14
+ // Bumped when the event format changes incompatibly. The replay function
15
+ // rejects events with a version it doesn't know.
16
+ export const EVENT_SCHEMA_VERSION = 1;
17
+ // --- Errors ---
18
+ export class EventReplayError extends Error {
19
+ eventIndex;
20
+ event;
21
+ constructor(message, eventIndex, event) {
22
+ super(`event ${eventIndex}: ${message}`);
23
+ this.eventIndex = eventIndex;
24
+ this.event = event;
25
+ this.name = "EventReplayError";
26
+ }
27
+ }
28
+ // --- Helpers ---
29
+ function nowISO() {
30
+ return new Date().toISOString();
31
+ }
32
+ export function makeNodeAddEvent(node, author) {
33
+ return { v: EVENT_SCHEMA_VERSION, ts: nowISO(), author, op: "node.add", node };
34
+ }
35
+ export function makeNodeUpdateEvent(id, properties, author) {
36
+ return {
37
+ v: EVENT_SCHEMA_VERSION,
38
+ ts: nowISO(),
39
+ author,
40
+ op: "node.update",
41
+ id,
42
+ properties,
43
+ };
44
+ }
45
+ export function makeNodeRetypeEvent(id, type, author) {
46
+ return {
47
+ v: EVENT_SCHEMA_VERSION,
48
+ ts: nowISO(),
49
+ author,
50
+ op: "node.retype",
51
+ id,
52
+ type,
53
+ };
54
+ }
55
+ export function makeNodeRemoveEvent(id, author) {
56
+ return { v: EVENT_SCHEMA_VERSION, ts: nowISO(), author, op: "node.remove", id };
57
+ }
58
+ export function makeEdgeAddEvent(edge, author) {
59
+ return { v: EVENT_SCHEMA_VERSION, ts: nowISO(), author, op: "edge.add", edge };
60
+ }
61
+ export function makeEdgeRemoveEvent(id, author) {
62
+ return { v: EVENT_SCHEMA_VERSION, ts: nowISO(), author, op: "edge.remove", id };
63
+ }
64
+ export function makeEdgeRetypeEvent(id, type, author) {
65
+ return {
66
+ v: EVENT_SCHEMA_VERSION,
67
+ ts: nowISO(),
68
+ author,
69
+ op: "edge.retype",
70
+ id,
71
+ type,
72
+ };
73
+ }
74
+ export function makeMetadataUpdateEvent(patch, author) {
75
+ return {
76
+ v: EVENT_SCHEMA_VERSION,
77
+ ts: nowISO(),
78
+ author,
79
+ op: "metadata.update",
80
+ patch,
81
+ };
82
+ }
83
+ export function makeSnapshotLabelEvent(label, author) {
84
+ return { v: EVENT_SCHEMA_VERSION, ts: nowISO(), author, op: "snapshot.label", label };
85
+ }
86
+ // --- Replay / apply ---
87
+ /**
88
+ * Apply a sequence of events on top of an existing state. Pure function:
89
+ * does not mutate inputs. Use this when you already have a materialized
90
+ * state and want to extend it with newly-appended events.
91
+ */
92
+ export function applyEvents(state, events) {
93
+ return doReplay(events, state.metadata, state);
94
+ }
95
+ /**
96
+ * Replay a sequence of events into a LearningGraphData state, starting
97
+ * from an empty state with the given metadata. Pure function: no IO, no
98
+ * mutation of inputs.
99
+ *
100
+ * Throws EventReplayError on the first invalid event. The error
101
+ * carries the event index so the caller can locate the problem in
102
+ * the source log.
103
+ */
104
+ export function replay(events, initialMetadata) {
105
+ return doReplay(events, initialMetadata, null);
106
+ }
107
+ function doReplay(events, initialMetadata, startingState) {
108
+ // Mutable accumulators (released as the immutable result on return)
109
+ const nodes = new Map();
110
+ const edges = new Map();
111
+ let metadata = { ...initialMetadata };
112
+ if (startingState) {
113
+ for (const node of startingState.nodes) {
114
+ nodes.set(node.id, { ...node });
115
+ }
116
+ for (const edge of startingState.edges) {
117
+ edges.set(edge.id, { ...edge });
118
+ }
119
+ metadata = { ...startingState.metadata };
120
+ }
121
+ for (let i = 0; i < events.length; i++) {
122
+ const event = events[i];
123
+ if (!event || typeof event !== "object") {
124
+ throw new EventReplayError("event is not an object", i, null);
125
+ }
126
+ if (event.v !== EVENT_SCHEMA_VERSION) {
127
+ throw new EventReplayError(`unknown event schema version ${event.v}`, i, event);
128
+ }
129
+ switch (event.op) {
130
+ case "node.add": {
131
+ if (nodes.has(event.node.id)) {
132
+ throw new EventReplayError(`node ${event.node.id} already exists`, i, event);
133
+ }
134
+ nodes.set(event.node.id, { ...event.node });
135
+ break;
136
+ }
137
+ case "node.update": {
138
+ const existing = nodes.get(event.id);
139
+ if (!existing) {
140
+ throw new EventReplayError(`node ${event.id} does not exist`, i, event);
141
+ }
142
+ const merged = { ...existing.properties };
143
+ for (const [key, value] of Object.entries(event.properties)) {
144
+ if (value === null) {
145
+ delete merged[key];
146
+ }
147
+ else {
148
+ merged[key] = value;
149
+ }
150
+ }
151
+ nodes.set(event.id, {
152
+ ...existing,
153
+ properties: merged,
154
+ updatedAt: event.ts,
155
+ });
156
+ break;
157
+ }
158
+ case "node.retype": {
159
+ const existing = nodes.get(event.id);
160
+ if (!existing) {
161
+ throw new EventReplayError(`node ${event.id} does not exist`, i, event);
162
+ }
163
+ nodes.set(event.id, {
164
+ ...existing,
165
+ type: event.type,
166
+ updatedAt: event.ts,
167
+ });
168
+ break;
169
+ }
170
+ case "node.remove": {
171
+ if (!nodes.has(event.id)) {
172
+ throw new EventReplayError(`node ${event.id} does not exist`, i, event);
173
+ }
174
+ nodes.delete(event.id);
175
+ // Cascade delete edges that reference this node
176
+ for (const [edgeId, edge] of edges) {
177
+ if (edge.sourceId === event.id || edge.targetId === event.id) {
178
+ edges.delete(edgeId);
179
+ }
180
+ }
181
+ break;
182
+ }
183
+ case "edge.add": {
184
+ if (edges.has(event.edge.id)) {
185
+ throw new EventReplayError(`edge ${event.edge.id} already exists`, i, event);
186
+ }
187
+ // Both endpoints must exist at the time of replay
188
+ if (!nodes.has(event.edge.sourceId)) {
189
+ throw new EventReplayError(`edge ${event.edge.id} sourceId ${event.edge.sourceId} not in graph`, i, event);
190
+ }
191
+ if (!nodes.has(event.edge.targetId)) {
192
+ throw new EventReplayError(`edge ${event.edge.id} targetId ${event.edge.targetId} not in graph`, i, event);
193
+ }
194
+ edges.set(event.edge.id, { ...event.edge });
195
+ break;
196
+ }
197
+ case "edge.remove": {
198
+ if (!edges.has(event.id)) {
199
+ throw new EventReplayError(`edge ${event.id} does not exist`, i, event);
200
+ }
201
+ edges.delete(event.id);
202
+ break;
203
+ }
204
+ case "edge.retype": {
205
+ const existing = edges.get(event.id);
206
+ if (!existing) {
207
+ throw new EventReplayError(`edge ${event.id} does not exist`, i, event);
208
+ }
209
+ edges.set(event.id, {
210
+ ...existing,
211
+ type: event.type,
212
+ updatedAt: event.ts,
213
+ });
214
+ break;
215
+ }
216
+ case "metadata.update": {
217
+ metadata = {
218
+ ...metadata,
219
+ ...event.patch,
220
+ updatedAt: event.ts,
221
+ };
222
+ break;
223
+ }
224
+ case "snapshot.label": {
225
+ // Snapshots are markers in the log. They don't change state.
226
+ break;
227
+ }
228
+ default: {
229
+ const exhaustive = event;
230
+ throw new EventReplayError(`unknown event op: ${exhaustive.op}`, i, exhaustive);
231
+ }
232
+ }
233
+ }
234
+ return {
235
+ metadata,
236
+ nodes: Array.from(nodes.values()),
237
+ edges: Array.from(edges.values()),
238
+ };
239
+ }
240
+ // --- Diff ---
241
+ /**
242
+ * Compute the minimal sequence of events that would transform `before`
243
+ * into `after`. Used by the storage backend's `saveOntology` to support
244
+ * coarse-grained "save the whole graph" callers transparently.
245
+ *
246
+ * Detection rules:
247
+ * - Nodes present in `after` but not `before`: node.add
248
+ * - Nodes present in `before` but not `after`: node.remove (cascades to edges)
249
+ * - Nodes present in both with different properties: node.update
250
+ * - Edges present in `after` but not `before`: edge.add
251
+ * - Edges present in `before` but not `after`: edge.remove
252
+ * - Metadata name/description differences: metadata.update
253
+ */
254
+ export function diffToEvents(before, after, author) {
255
+ const events = [];
256
+ // Metadata
257
+ const metaPatch = {};
258
+ if (before.metadata.name !== after.metadata.name) {
259
+ metaPatch.name = after.metadata.name;
260
+ }
261
+ if (before.metadata.description !== after.metadata.description) {
262
+ metaPatch.description = after.metadata.description;
263
+ }
264
+ if (Object.keys(metaPatch).length > 0) {
265
+ events.push(makeMetadataUpdateEvent(metaPatch, author));
266
+ }
267
+ const beforeNodes = new Map(before.nodes.map((n) => [n.id, n]));
268
+ const afterNodes = new Map(after.nodes.map((n) => [n.id, n]));
269
+ const beforeEdges = new Map(before.edges.map((e) => [e.id, e]));
270
+ const afterEdges = new Map(after.edges.map((e) => [e.id, e]));
271
+ // Removed edges first (so cascade-delete from removed nodes doesn't double-emit)
272
+ // Removed nodes will cascade their own edges in replay; we only need explicit
273
+ // edge removes for edges whose endpoints are NOT being removed.
274
+ const removedNodeIds = new Set();
275
+ for (const id of beforeNodes.keys()) {
276
+ if (!afterNodes.has(id))
277
+ removedNodeIds.add(id);
278
+ }
279
+ for (const [id, edge] of beforeEdges) {
280
+ if (afterEdges.has(id))
281
+ continue;
282
+ // Skip edges whose endpoints are being removed — the node.remove cascade handles them
283
+ if (removedNodeIds.has(edge.sourceId) || removedNodeIds.has(edge.targetId)) {
284
+ continue;
285
+ }
286
+ events.push(makeEdgeRemoveEvent(id, author));
287
+ }
288
+ // Removed nodes
289
+ for (const id of removedNodeIds) {
290
+ events.push(makeNodeRemoveEvent(id, author));
291
+ }
292
+ // Added + updated nodes
293
+ for (const [id, node] of afterNodes) {
294
+ const prev = beforeNodes.get(id);
295
+ if (!prev) {
296
+ events.push(makeNodeAddEvent(node, author));
297
+ continue;
298
+ }
299
+ // Type change: emit retype (preserves ID + edges)
300
+ if (prev.type !== node.type) {
301
+ events.push(makeNodeRetypeEvent(id, node.type, author));
302
+ }
303
+ // Property change: emit update with the property delta
304
+ if (JSON.stringify(prev.properties) !== JSON.stringify(node.properties)) {
305
+ const patch = {};
306
+ for (const [k, v] of Object.entries(node.properties)) {
307
+ if (!Object.prototype.hasOwnProperty.call(prev.properties, k)) {
308
+ patch[k] = v;
309
+ }
310
+ else if (JSON.stringify(prev.properties[k]) !== JSON.stringify(v)) {
311
+ patch[k] = v;
312
+ }
313
+ }
314
+ for (const k of Object.keys(prev.properties)) {
315
+ if (!Object.prototype.hasOwnProperty.call(node.properties, k)) {
316
+ patch[k] = null;
317
+ }
318
+ }
319
+ events.push(makeNodeUpdateEvent(id, patch, author));
320
+ }
321
+ }
322
+ // Added edges + edge type changes
323
+ for (const [id, edge] of afterEdges) {
324
+ const prev = beforeEdges.get(id);
325
+ if (!prev) {
326
+ events.push(makeEdgeAddEvent(edge, author));
327
+ continue;
328
+ }
329
+ if (prev.type !== edge.type) {
330
+ events.push(makeEdgeRetypeEvent(id, edge.type, author));
331
+ }
332
+ }
333
+ return events;
334
+ }
335
+ // --- Serialization ---
336
+ /**
337
+ * Serialize an event to a single JSONL line (no trailing newline).
338
+ */
339
+ export function serializeEvent(event) {
340
+ return JSON.stringify(event);
341
+ }
342
+ /**
343
+ * Parse a single JSONL line into an event. Throws on malformed input.
344
+ */
345
+ export function parseEvent(line) {
346
+ const trimmed = line.trim();
347
+ if (trimmed.length === 0) {
348
+ throw new Error("empty event line");
349
+ }
350
+ const parsed = JSON.parse(trimmed);
351
+ if (typeof parsed !== "object" || parsed === null) {
352
+ throw new Error("event must be an object");
353
+ }
354
+ return parsed;
355
+ }
356
+ /**
357
+ * Parse a full JSONL document into an array of events. Skips blank lines.
358
+ */
359
+ export function parseEventLog(text) {
360
+ const events = [];
361
+ const lines = text.split("\n");
362
+ for (let i = 0; i < lines.length; i++) {
363
+ const line = lines[i];
364
+ if (line.trim().length === 0)
365
+ continue;
366
+ try {
367
+ events.push(parseEvent(line));
368
+ }
369
+ catch (err) {
370
+ throw new Error(`parse error on line ${i + 1}: ${err.message}`);
371
+ }
372
+ }
373
+ return events;
374
+ }
375
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Node, Edge, LearningGraphData, NodeSummary, NodeTypeInfo, EdgeTypeInfo, ListNodesResult, GetNodeResult, NeighborResult, GraphStats, GraphAudit } from "./types.js";
1
+ import type { Node, Edge, LearningGraphData, NodeSummary, NodeTypeInfo, EdgeTypeInfo, ListNodesResult, GetNodeResult, NeighborResult, GraphStats, GraphAudit, GraphDegreeTable } from "./types.js";
2
2
  /**
3
3
  * In-memory graph operations on an LearningGraphData object.
4
4
  * Pure logic — no I/O, no MCP, fully testable.
@@ -49,6 +49,8 @@ export declare class Graph {
49
49
  getEdgeTypes(): EdgeTypeInfo[];
50
50
  /** Compute graph statistics for diagnostics and improvement planning. */
51
51
  getStats(): GraphStats;
52
+ /** Full node degree table grouped by type, sorted by connectivity ascending. */
53
+ getDegreeTable(): GraphDegreeTable;
52
54
  /** Audit the graph and produce a structured improvement report. */
53
55
  audit(): GraphAudit;
54
56
  /** Bulk-import edges only (all nodes must already exist). Single atomic operation. */
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/core/graph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,IAAI,EACJ,IAAI,EACJ,iBAAiB,EACjB,WAAW,EAEX,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,aAAa,EAEb,cAAc,EACd,UAAU,EACV,UAAU,EAGX,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,qBAAa,KAAK;IACG,IAAI,EAAE,iBAAiB;gBAAvB,IAAI,EAAE,iBAAiB;IAI1C,OAAO,CAAC,GAAG;IAIX,+EAA+E;IAC/E,OAAO,CAAC,SAAS;IAOjB,wEAAwE;IACxE,OAAO,CAAC,aAAa;IAQrB,6CAA6C;IAC7C,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAchE;;;;OAIG;IACH,mBAAmB,CACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,EACnE,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,CAAC,GACD;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IA2C3C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIrC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAWjE,uFAAuF;IACvF,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAiB9B,6DAA6D;IAC7D,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,SAAK,EAAE,MAAM,SAAI,GAAG,eAAe;IAgBjE,sEAAsE;IACtE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE;IA2BxD,qEAAqE;IACrE,YAAY,IAAI,YAAY,EAAE;IAU9B,qEAAqE;IACrE,YAAY,IAAI,YAAY,EAAE;IAU9B,yEAAyE;IACzE,QAAQ,IAAI,UAAU;IAkDtB,mEAAmE;IACnE,KAAK,IAAI,UAAU;IAyGnB,sFAAsF;IACtF,WAAW,CACT,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,GACvG,MAAM,EAAE;IAiBX,OAAO,CACL,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACvC,IAAI;IAoBP,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIrC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAO5B,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa;IAW3C;;;OAGG;IACH,YAAY,CACV,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,GAAE,UAAU,GAAG,UAAU,GAAG,MAAe,EACpD,KAAK,SAAI,GACR,cAAc;CA8ClB"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/core/graph.ts"],"names":[],"mappings":"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"}
@@ -226,6 +226,56 @@ export class Graph {
226
226
  .sort((a, b) => b.count - a.count),
227
227
  };
228
228
  }
229
+ /** Full node degree table grouped by type, sorted by connectivity ascending. */
230
+ getDegreeTable() {
231
+ const incoming = new Map();
232
+ const outgoing = new Map();
233
+ for (const edge of this.data.edges) {
234
+ outgoing.set(edge.sourceId, (outgoing.get(edge.sourceId) ?? 0) + 1);
235
+ incoming.set(edge.targetId, (incoming.get(edge.targetId) ?? 0) + 1);
236
+ }
237
+ const byType = new Map();
238
+ for (const node of this.data.nodes) {
239
+ const inc = incoming.get(node.id) ?? 0;
240
+ const out = outgoing.get(node.id) ?? 0;
241
+ const detail = {
242
+ id: node.id,
243
+ label: this.nodeLabel(node),
244
+ type: node.type,
245
+ incoming: inc,
246
+ outgoing: out,
247
+ total: inc + out,
248
+ propertyCount: Object.keys(node.properties).length,
249
+ };
250
+ const list = byType.get(node.type) ?? [];
251
+ list.push(detail);
252
+ byType.set(node.type, list);
253
+ }
254
+ const types = [];
255
+ for (const [type, nodes] of byType) {
256
+ nodes.sort((a, b) => a.total - b.total);
257
+ const totalConns = nodes.reduce((s, n) => s + n.total, 0);
258
+ const totalProps = nodes.reduce((s, n) => s + n.propertyCount, 0);
259
+ types.push({
260
+ type,
261
+ count: nodes.length,
262
+ avgConnections: Math.round((totalConns / nodes.length) * 10) / 10,
263
+ avgProperties: Math.round((totalProps / nodes.length) * 10) / 10,
264
+ nodes,
265
+ });
266
+ }
267
+ types.sort((a, b) => a.avgConnections - b.avgConnections);
268
+ const totalPossible = this.data.nodes.length * (this.data.nodes.length - 1) / 2;
269
+ const totalConns = [...incoming.values()].reduce((a, b) => a + b, 0)
270
+ + [...outgoing.values()].reduce((a, b) => a + b, 0);
271
+ return {
272
+ nodeCount: this.data.nodes.length,
273
+ edgeCount: this.data.edges.length,
274
+ density: totalPossible > 0 ? Math.round((this.data.edges.length / totalPossible) * 1000) / 1000 : 0,
275
+ avgConnections: this.data.nodes.length > 0 ? Math.round((totalConns / this.data.nodes.length) * 10) / 10 : 0,
276
+ types,
277
+ };
278
+ }
229
279
  /** Audit the graph and produce a structured improvement report. */
230
280
  audit() {
231
281
  const stats = this.getStats();