@grackle-ai/plugin-knowledge 0.119.0 → 0.121.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 (42) hide show
  1. package/dist/entity-sync.d.ts +22 -0
  2. package/dist/entity-sync.d.ts.map +1 -0
  3. package/dist/entity-sync.js +116 -0
  4. package/dist/entity-sync.js.map +1 -0
  5. package/dist/knowledge-init.d.ts.map +1 -1
  6. package/dist/knowledge-init.js +12 -1
  7. package/dist/knowledge-init.js.map +1 -1
  8. package/dist/knowledge-plugin.d.ts.map +1 -1
  9. package/dist/knowledge-plugin.js +9 -2
  10. package/dist/knowledge-plugin.js.map +1 -1
  11. package/dist/knowledge-projection-phase.d.ts +39 -0
  12. package/dist/knowledge-projection-phase.d.ts.map +1 -0
  13. package/dist/knowledge-projection-phase.js +154 -0
  14. package/dist/knowledge-projection-phase.js.map +1 -0
  15. package/dist/projection/derive-text.d.ts +21 -0
  16. package/dist/projection/derive-text.d.ts.map +1 -0
  17. package/dist/projection/derive-text.js +40 -0
  18. package/dist/projection/derive-text.js.map +1 -0
  19. package/dist/projection/edge-mappers.d.ts +44 -0
  20. package/dist/projection/edge-mappers.d.ts.map +1 -0
  21. package/dist/projection/edge-mappers.js +95 -0
  22. package/dist/projection/edge-mappers.js.map +1 -0
  23. package/dist/projection/node-mappers.d.ts +42 -0
  24. package/dist/projection/node-mappers.d.ts.map +1 -0
  25. package/dist/projection/node-mappers.js +106 -0
  26. package/dist/projection/node-mappers.js.map +1 -0
  27. package/dist/projection/project-entity.d.ts +54 -0
  28. package/dist/projection/project-entity.d.ts.map +1 -0
  29. package/dist/projection/project-entity.js +164 -0
  30. package/dist/projection/project-entity.js.map +1 -0
  31. package/dist/projection/project-transcript.d.ts +29 -0
  32. package/dist/projection/project-transcript.d.ts.map +1 -0
  33. package/dist/projection/project-transcript.js +90 -0
  34. package/dist/projection/project-transcript.js.map +1 -0
  35. package/dist/projection/rebuild.d.ts +29 -0
  36. package/dist/projection/rebuild.d.ts.map +1 -0
  37. package/dist/projection/rebuild.js +104 -0
  38. package/dist/projection/rebuild.js.map +1 -0
  39. package/dist/proto-converters.d.ts.map +1 -1
  40. package/dist/proto-converters.js +3 -1
  41. package/dist/proto-converters.js.map +1 -1
  42. package/package.json +7 -7
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Incremental entity projection via the domain event bus (#1258).
3
+ *
4
+ * Subscribes to entity mutation events and projects/unprojects the affected
5
+ * node low-latency, as an optimization on top of the reconciliation phase
6
+ * (which remains the correctness backbone). Only events with a precise entity
7
+ * id in their payload are handled here; coarse events (e.g. `environment.changed`
8
+ * with an empty payload) and sessions/transcripts are left to the phase.
9
+ *
10
+ * Fire-and-forget: handler errors are logged, never propagated — a missed
11
+ * projection is reconciled on the next scan.
12
+ *
13
+ * @module
14
+ */
15
+ import type { PluginContext, Disposable } from "@grackle-ai/plugin-sdk";
16
+ /**
17
+ * Subscribe to entity mutation events and incrementally project them.
18
+ *
19
+ * @returns A {@link Disposable} that unsubscribes from the event bus.
20
+ */
21
+ export declare function createEntitySyncSubscriber(ctx: PluginContext): Disposable;
22
+ //# sourceMappingURL=entity-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-sync.d.ts","sourceRoot":"","sources":["../src/entity-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAgB,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAoGtF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,aAAa,GAAG,UAAU,CAOzE"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Incremental entity projection via the domain event bus (#1258).
3
+ *
4
+ * Subscribes to entity mutation events and projects/unprojects the affected
5
+ * node low-latency, as an optimization on top of the reconciliation phase
6
+ * (which remains the correctness backbone). Only events with a precise entity
7
+ * id in their payload are handled here; coarse events (e.g. `environment.changed`
8
+ * with an empty payload) and sessions/transcripts are left to the phase.
9
+ *
10
+ * Fire-and-forget: handler errors are logged, never propagated — a missed
11
+ * projection is reconciled on the next scan.
12
+ *
13
+ * @module
14
+ */
15
+ import { taskStore, workspaceStore, personaStore } from "@grackle-ai/database";
16
+ import { logger } from "./logger.js";
17
+ import { getKnowledgeEmbedder } from "./knowledge-init.js";
18
+ import { isNeo4jHealthy } from "./knowledge-health.js";
19
+ import { projectTask, unprojectTask, projectWorkspace, projectPersona, unprojectPersona, unprojectEnvironment, } from "./projection/project-entity.js";
20
+ /** Read a non-empty string field from an event payload, or `undefined`. */
21
+ function stringField(payload, key) {
22
+ const value = payload[key];
23
+ return typeof value === "string" && value.length > 0 ? value : undefined;
24
+ }
25
+ /** Dispatch one entity mutation event to the projection. */
26
+ async function handleEvent(event) {
27
+ // Only project when knowledge is initialized and Neo4j is reachable; otherwise
28
+ // the reconciliation phase will converge the graph once it recovers.
29
+ if (!getKnowledgeEmbedder() || !isNeo4jHealthy()) {
30
+ return;
31
+ }
32
+ const { payload } = event;
33
+ switch (event.type) {
34
+ case "task.created":
35
+ case "task.updated":
36
+ case "task.started":
37
+ case "task.completed":
38
+ case "task.reparented": {
39
+ const taskId = stringField(payload, "taskId");
40
+ if (!taskId) {
41
+ return;
42
+ }
43
+ const task = taskStore.getTask(taskId);
44
+ if (task) {
45
+ await projectTask(task);
46
+ }
47
+ break;
48
+ }
49
+ case "task.deleted": {
50
+ const taskId = stringField(payload, "taskId");
51
+ if (taskId) {
52
+ await unprojectTask(taskId);
53
+ }
54
+ break;
55
+ }
56
+ case "workspace.created":
57
+ case "workspace.updated":
58
+ case "workspace.archived": {
59
+ // Archive is not a delete — the row remains (status="archived") and the
60
+ // node is re-projected with the updated status.
61
+ const workspaceId = stringField(payload, "workspaceId");
62
+ if (!workspaceId) {
63
+ return;
64
+ }
65
+ const workspace = workspaceStore.getWorkspace(workspaceId);
66
+ if (workspace) {
67
+ await projectWorkspace(workspace);
68
+ }
69
+ break;
70
+ }
71
+ case "persona.created":
72
+ case "persona.updated": {
73
+ const personaId = stringField(payload, "personaId");
74
+ if (!personaId) {
75
+ return;
76
+ }
77
+ const persona = personaStore.getPersona(personaId);
78
+ if (persona) {
79
+ await projectPersona(persona);
80
+ }
81
+ break;
82
+ }
83
+ case "persona.deleted": {
84
+ const personaId = stringField(payload, "personaId");
85
+ if (personaId) {
86
+ await unprojectPersona(personaId);
87
+ }
88
+ break;
89
+ }
90
+ case "environment.removed": {
91
+ const environmentId = stringField(payload, "environmentId");
92
+ if (environmentId) {
93
+ await unprojectEnvironment(environmentId);
94
+ }
95
+ break;
96
+ }
97
+ default:
98
+ // sessions, transcript chunks, environment.changed (empty payload),
99
+ // and non-entity events are handled by the reconciliation phase.
100
+ break;
101
+ }
102
+ }
103
+ /**
104
+ * Subscribe to entity mutation events and incrementally project them.
105
+ *
106
+ * @returns A {@link Disposable} that unsubscribes from the event bus.
107
+ */
108
+ export function createEntitySyncSubscriber(ctx) {
109
+ const unsubscribe = ctx.subscribe((event) => {
110
+ handleEvent(event).catch((err) => {
111
+ logger.error({ err, eventType: event.type }, "Knowledge entity sync failed");
112
+ });
113
+ });
114
+ return { dispose: unsubscribe };
115
+ }
116
+ //# sourceMappingURL=entity-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-sync.js","sourceRoot":"","sources":["../src/entity-sync.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge-init.d.ts","sourceRoot":"","sources":["../src/knowledge-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,WAAW,IAAI,gBAAgB,EAE/B,KAAK,QAAQ,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAK5B,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,QAAQ,GAAG,SAAS,CAE3D;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA0BpF"}
1
+ {"version":3,"file":"knowledge-init.d.ts","sourceRoot":"","sources":["../src/knowledge-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,WAAW,IAAI,gBAAgB,EAG/B,KAAK,QAAQ,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG5D,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAK5B,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,QAAQ,GAAG,SAAS,CAE3D;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAqCpF"}
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * @module
10
10
  */
11
- import { openNeo4j, initSchema, closeNeo4j, healthCheck as neo4jHealthCheck, createLocalEmbedder, } from "@grackle-ai/knowledge";
11
+ import { openNeo4j, initSchema, closeNeo4j, healthCheck as neo4jHealthCheck, createLocalEmbedder, listRecentNodes, } from "@grackle-ai/knowledge";
12
+ import { rebuild } from "./projection/rebuild.js";
12
13
  /** Re-export Neo4j health check for use by the reconciliation health phase. */
13
14
  export { neo4jHealthCheck };
14
15
  /** Module-level embedder, available after initKnowledge() completes. */
@@ -36,6 +37,16 @@ export async function initKnowledge(ctx) {
36
37
  try {
37
38
  await initSchema(embedder.dimensions);
38
39
  knowledgeEmbedder = embedder;
40
+ // Startup-if-empty: a fresh or wiped graph re-projects itself from SQL +
41
+ // session logs (recovery = re-project, never replay). Runs in the background
42
+ // so a large rebuild (CPU-bound embeddings) doesn't block plugin init.
43
+ const recent = await listRecentNodes(1);
44
+ if (recent.nodes.length === 0) {
45
+ ctx.logger.info("Knowledge graph is empty — starting background rebuild from SQL + logs");
46
+ void rebuild(embedder).catch((err) => {
47
+ ctx.logger.error({ err }, "Knowledge graph rebuild failed");
48
+ });
49
+ }
39
50
  ctx.logger.info("Knowledge graph subsystem ready");
40
51
  return async () => {
41
52
  ctx.logger.info("Shutting down knowledge graph subsystem");
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge-init.js","sourceRoot":"","sources":["../src/knowledge-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,WAAW,IAAI,gBAAgB,EAC/B,mBAAmB,GAEpB,MAAM,uBAAuB,CAAC;AAG/B,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,wEAAwE;AACxE,IAAI,iBAAuC,CAAC;AAE5C,qFAAqF;AACrF,MAAM,UAAU,oBAAoB;IAClC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAkB;IACpD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAE1D,MAAM,QAAQ,GAAa,mBAAmB,EAAE,CAAC;IAEjD,MAAM,SAAS,EAAE,CAAC;IAElB,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAEtC,iBAAiB,GAAG,QAAQ,CAAC;QAE7B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAEnD,OAAO,KAAK,IAAmB,EAAE;YAC/B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YAC3D,iBAAiB,GAAG,SAAS,CAAC;YAC9B,MAAM,UAAU,EAAE,CAAC;YACnB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACvD,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,uCAAuC;QACvC,iBAAiB,GAAG,SAAS,CAAC;QAC9B,MAAM,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"knowledge-init.js","sourceRoot":"","sources":["../src/knowledge-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,WAAW,IAAI,gBAAgB,EAC/B,mBAAmB,EACnB,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,wEAAwE;AACxE,IAAI,iBAAuC,CAAC;AAE5C,qFAAqF;AACrF,MAAM,UAAU,oBAAoB;IAClC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAkB;IACpD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAE1D,MAAM,QAAQ,GAAa,mBAAmB,EAAE,CAAC;IAEjD,MAAM,SAAS,EAAE,CAAC;IAElB,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAEtC,iBAAiB,GAAG,QAAQ,CAAC;QAE7B,yEAAyE;QACzE,6EAA6E;QAC7E,uEAAuE;QACvE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,wEAAwE,CAAC,CAAC;YAC1F,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBAC5C,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,gCAAgC,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACL,CAAC;QAED,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAEnD,OAAO,KAAK,IAAmB,EAAE;YAC/B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YAC3D,iBAAiB,GAAG,SAAS,CAAC;YAC9B,MAAM,UAAU,EAAE,CAAC;YACnB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACvD,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,uCAAuC;QACvC,iBAAiB,GAAG,SAAS,CAAC;QAC9B,MAAM,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge-plugin.d.ts","sourceRoot":"","sources":["../src/knowledge-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,wBAAwB,CAAC;AAgB3E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,IAAI,aAAa,CAuCrD"}
1
+ {"version":3,"file":"knowledge-plugin.d.ts","sourceRoot":"","sources":["../src/knowledge-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,wBAAwB,CAAC;AAuB3E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,IAAI,aAAa,CA6CrD"}
@@ -8,10 +8,12 @@
8
8
  */
9
9
  import { grackle } from "@grackle-ai/common";
10
10
  import { logger } from "./logger.js";
11
- import { initKnowledge, neo4jHealthCheck, } from "./knowledge-init.js";
12
- import { createKnowledgeHealthPhase, markKnowledgeInitFailed } from "./knowledge-health.js";
11
+ import { initKnowledge, neo4jHealthCheck, getKnowledgeEmbedder, } from "./knowledge-init.js";
12
+ import { createKnowledgeHealthPhase, markKnowledgeInitFailed, isNeo4jHealthy, } from "./knowledge-health.js";
13
+ import { createKnowledgeProjectionPhase } from "./knowledge-projection-phase.js";
13
14
  import { searchKnowledge, getKnowledgeNode, expandKnowledgeNode, listRecentKnowledgeNodes, } from "./knowledge-handlers.js";
14
15
  import { knowledgeMcpTools } from "./mcp-tools.js";
16
+ import { createEntitySyncSubscriber } from "./entity-sync.js";
15
17
  /**
16
18
  * Create the knowledge plugin that contributes Neo4j-backed knowledge graph
17
19
  * read capabilities to the Grackle server. The graph is populated by the
@@ -57,7 +59,12 @@ export function createKnowledgePlugin() {
57
59
  }],
58
60
  reconciliationPhases: () => [
59
61
  createKnowledgeHealthPhase({ healthCheck: neo4jHealthCheck }),
62
+ createKnowledgeProjectionPhase({
63
+ getEmbedder: getKnowledgeEmbedder,
64
+ isHealthy: isNeo4jHealthy,
65
+ }),
60
66
  ],
67
+ eventSubscribers: (ctx) => [createEntitySyncSubscriber(ctx)],
61
68
  mcpTools: () => knowledgeMcpTools,
62
69
  };
63
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge-plugin.js","sourceRoot":"","sources":["../src/knowledge-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,aAAa,EACb,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAA0C,CAAC;IAE/C,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,YAAY,EAAE,CAAC,MAAM,CAAC;QAEtB,UAAU,EAAE,KAAK,EAAE,GAAkB,EAAiB,EAAE;YACtD,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,gEAAgE;gBAChE,yEAAyE;gBACzE,uBAAuB,EAAE,CAAC;gBAC1B,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,2DAA2D,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;QAED,QAAQ,EAAE,KAAK,IAAmB,EAAE;YAClC,MAAM,OAAO,EAAE,EAAE,CAAC;YAClB,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC;QAED,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;gBACnB,OAAO,EAAE,OAAO,CAAC,gBAAgB;gBACjC,QAAQ,EAAE;oBACR,eAAe;oBACf,gBAAgB;oBAChB,mBAAmB;oBACnB,wBAAwB;iBACzB;aACF,CAAC;QAEF,oBAAoB,EAAE,GAAG,EAAE,CAAC;YAC1B,0BAA0B,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;SAC9D;QAED,QAAQ,EAAE,GAAG,EAAE,CAAC,iBAAiB;KAClC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"knowledge-plugin.js","sourceRoot":"","sources":["../src/knowledge-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAA0C,CAAC;IAE/C,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,YAAY,EAAE,CAAC,MAAM,CAAC;QAEtB,UAAU,EAAE,KAAK,EAAE,GAAkB,EAAiB,EAAE;YACtD,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,gEAAgE;gBAChE,yEAAyE;gBACzE,uBAAuB,EAAE,CAAC;gBAC1B,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,2DAA2D,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;QAED,QAAQ,EAAE,KAAK,IAAmB,EAAE;YAClC,MAAM,OAAO,EAAE,EAAE,CAAC;YAClB,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC;QAED,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;gBACnB,OAAO,EAAE,OAAO,CAAC,gBAAgB;gBACjC,QAAQ,EAAE;oBACR,eAAe;oBACf,gBAAgB;oBAChB,mBAAmB;oBACnB,wBAAwB;iBACzB;aACF,CAAC;QAEF,oBAAoB,EAAE,GAAG,EAAE,CAAC;YAC1B,0BAA0B,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;YAC7D,8BAA8B,CAAC;gBAC7B,WAAW,EAAE,oBAAoB;gBACjC,SAAS,EAAE,cAAc;aAC1B,CAAC;SACH;QAED,gBAAgB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,0BAA0B,CAAC,GAAG,CAAC,CAAC;QAE5D,QAAQ,EAAE,GAAG,EAAE,CAAC,iBAAiB;KAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Reconciliation phase that keeps the derived KG mirror converged (#1258).
3
+ *
4
+ * The correctness backbone of the projection. Each tick (gated on Neo4j health
5
+ * + an available embedder):
6
+ * 1. **Entity sync** — re-project changed task/workspace/persona/environment
7
+ * rows (hash-gated; unchanged rows are skipped) + bulk-prune vanished ones.
8
+ * 2. **Session sync** — the same for sessions, plus incremental transcript
9
+ * chunking (the per-session byte cursor reads at most one bounded window of
10
+ * new log content per pass).
11
+ * 3. **Embed backfill** — embed a bounded batch (`EMBED_BACKFILL_BATCH`) of
12
+ * nodes upserted structurally with no embedding (keeps embedding off the
13
+ * write path).
14
+ *
15
+ * Entity nodes are also projected low-latency by the event subscriber; this
16
+ * phase guarantees eventual convergence for all (e.g. events missed while Neo4j
17
+ * was down).
18
+ *
19
+ * Bounding: the expensive work is bounded per tick — transcript reads are
20
+ * byte-capped and embedding is batch-limited. Entity/session reconciliation does
21
+ * a hash-gated scan of all rows each tick (cheap reads; only changed rows write),
22
+ * which is acceptable for current entity volumes.
23
+ *
24
+ * @module
25
+ */
26
+ import type { ReconciliationPhase } from "@grackle-ai/plugin-sdk";
27
+ import { type Embedder } from "@grackle-ai/knowledge";
28
+ /** Dependencies for the projection phase (injected for testability). */
29
+ export interface KnowledgeProjectionPhaseDeps {
30
+ /** Returns the embedder, or undefined when knowledge is not initialized. */
31
+ getEmbedder: () => Embedder | undefined;
32
+ /** Returns whether Neo4j is currently reachable. */
33
+ isHealthy: () => boolean;
34
+ }
35
+ /**
36
+ * Create the `knowledge-projection` reconciliation phase.
37
+ */
38
+ export declare function createKnowledgeProjectionPhase(deps: KnowledgeProjectionPhaseDeps): ReconciliationPhase;
39
+ //# sourceMappingURL=knowledge-projection-phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge-projection-phase.d.ts","sourceRoot":"","sources":["../src/knowledge-projection-phase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AASlE,OAAO,EAOL,KAAK,QAAQ,EAId,MAAM,uBAAuB,CAAC;AA4B/B,wEAAwE;AACxE,MAAM,WAAW,4BAA4B;IAC3C,4EAA4E;IAC5E,WAAW,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAC;IACxC,oDAAoD;IACpD,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AA2GD;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,4BAA4B,GACjC,mBAAmB,CAuCrB"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Reconciliation phase that keeps the derived KG mirror converged (#1258).
3
+ *
4
+ * The correctness backbone of the projection. Each tick (gated on Neo4j health
5
+ * + an available embedder):
6
+ * 1. **Entity sync** — re-project changed task/workspace/persona/environment
7
+ * rows (hash-gated; unchanged rows are skipped) + bulk-prune vanished ones.
8
+ * 2. **Session sync** — the same for sessions, plus incremental transcript
9
+ * chunking (the per-session byte cursor reads at most one bounded window of
10
+ * new log content per pass).
11
+ * 3. **Embed backfill** — embed a bounded batch (`EMBED_BACKFILL_BATCH`) of
12
+ * nodes upserted structurally with no embedding (keeps embedding off the
13
+ * write path).
14
+ *
15
+ * Entity nodes are also projected low-latency by the event subscriber; this
16
+ * phase guarantees eventual convergence for all (e.g. events missed while Neo4j
17
+ * was down).
18
+ *
19
+ * Bounding: the expensive work is bounded per tick — transcript reads are
20
+ * byte-capped and embedding is batch-limited. Entity/session reconciliation does
21
+ * a hash-gated scan of all rows each tick (cheap reads; only changed rows write),
22
+ * which is acceptable for current entity volumes.
23
+ *
24
+ * @module
25
+ */
26
+ import { sessionStore, taskStore, workspaceStore, personaStore, envRegistry, workspaceEnvironmentLinkStore, } from "@grackle-ai/database";
27
+ import { getReferenceNodeProps, listReferenceSourceIds, listNodesMissingEmbedding, updateNode, pruneReferenceNodesNotIn, REFERENCE_SOURCE, } from "@grackle-ai/knowledge";
28
+ import { logger } from "./logger.js";
29
+ import { sessionToNodeInput, environmentToNodeInput, personaToNodeInput, workspaceToNodeInput, taskToNodeInput, } from "./projection/node-mappers.js";
30
+ import { projectSession, linkSessionSpawn, projectEnvironment, projectPersona, projectWorkspace, projectTask, reconcileTaskEdges, reconcileWorkspaceEdges, reconcileSessionEdges, } from "./projection/project-entity.js";
31
+ import { projectSessionTranscript, unprojectSessionTranscript, } from "./projection/project-transcript.js";
32
+ /** Max nodes embedded per tick so CPU-bound embedding never starves the loop. */
33
+ const EMBED_BACKFILL_BATCH = 32;
34
+ /** Text to embed for a node: a reference node's content (chunk) or label, else a native node's content. */
35
+ function embedText(node) {
36
+ if (node.kind === "reference") {
37
+ return node.content ?? node.label;
38
+ }
39
+ return node.content;
40
+ }
41
+ /** Resolve a session's workspace scope via its task (empty when no task). */
42
+ function resolveSessionWorkspaceId(taskId) {
43
+ if (!taskId) {
44
+ return "";
45
+ }
46
+ return taskStore.getTask(taskId)?.workspaceId ?? "";
47
+ }
48
+ async function syncSessions(embedder) {
49
+ const sessions = sessionStore.listSessions();
50
+ const liveSessionIds = new Set();
51
+ for (const session of sessions) {
52
+ liveSessionIds.add(session.id);
53
+ const workspaceId = resolveSessionWorkspaceId(session.taskId);
54
+ // Hash-gate the node write, but always reconcile edges: on change re-project
55
+ // (clears stale + re-applies); when unchanged, re-apply additively so an edge
56
+ // dropped on an earlier pass (endpoint missing, transient failure) still heals.
57
+ const desired = sessionToNodeInput(session, workspaceId);
58
+ const existing = await getReferenceNodeProps(REFERENCE_SOURCE.SESSION, session.id);
59
+ if (existing?.projectionHash !== desired.extraProps?.projectionHash) {
60
+ await projectSession(session, workspaceId);
61
+ }
62
+ else {
63
+ await reconcileSessionEdges(session);
64
+ }
65
+ // Incremental transcript chunking (cursor-based; cheap when no new content).
66
+ await projectSessionTranscript(session, embedder);
67
+ }
68
+ // SPAWNED edges in a second pass — all session nodes now exist, so a child
69
+ // projected before its parent still gets its edge (order-independent).
70
+ for (const session of sessions) {
71
+ await linkSessionSpawn(session);
72
+ }
73
+ // Prune sessions whose row no longer exists, plus their transcript chunks.
74
+ const existingSessionIds = await listReferenceSourceIds(REFERENCE_SOURCE.SESSION);
75
+ const vanished = existingSessionIds.filter((id) => !liveSessionIds.has(id));
76
+ if (vanished.length > 0) {
77
+ await pruneReferenceNodesNotIn(REFERENCE_SOURCE.SESSION, [...liveSessionIds]);
78
+ for (const sessionId of vanished) {
79
+ await unprojectSessionTranscript(sessionId);
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Reconcile one reference-node entity type: hash-gated re-project of changed
85
+ * rows + prune of nodes whose source row no longer exists. This is the
86
+ * correctness backbone for entities — it converges the mirror even if the
87
+ * low-latency event subscriber missed an event (e.g. while Neo4j was down).
88
+ *
89
+ * @param reconcileEdges - When a row's hash is unchanged (node already current),
90
+ * this re-applies its outgoing edges additively (no node write, no stale-clear),
91
+ * so an edge dropped on an earlier pass still heals. Omit for edgeless entities.
92
+ */
93
+ async function syncEntity(sourceType, rows, getId, toInput, project, reconcileEdges) {
94
+ const liveIds = new Set();
95
+ for (const row of rows) {
96
+ const id = getId(row);
97
+ liveIds.add(id);
98
+ const existing = await getReferenceNodeProps(sourceType, id);
99
+ if (existing?.projectionHash !== toInput(row).extraProps?.projectionHash) {
100
+ await project(row);
101
+ }
102
+ else if (reconcileEdges) {
103
+ await reconcileEdges(row);
104
+ }
105
+ }
106
+ await pruneReferenceNodesNotIn(sourceType, [...liveIds]);
107
+ }
108
+ async function backfillEmbeddings(embedder) {
109
+ const pending = await listNodesMissingEmbedding(EMBED_BACKFILL_BATCH);
110
+ for (const node of pending) {
111
+ const text = embedText(node);
112
+ if (!text) {
113
+ continue;
114
+ }
115
+ try {
116
+ const { vector } = await embedder.embed(text);
117
+ await updateNode(node.id, { embedding: vector });
118
+ }
119
+ catch (err) {
120
+ // Non-fatal: leave the node unembedded; it retries on the next tick.
121
+ logger.debug({ err, nodeId: node.id }, "Embedding backfill failed; will retry");
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * Create the `knowledge-projection` reconciliation phase.
127
+ */
128
+ export function createKnowledgeProjectionPhase(deps) {
129
+ return {
130
+ name: "knowledge-projection",
131
+ execute: async () => {
132
+ const embedder = deps.getEmbedder();
133
+ if (!embedder || !deps.isHealthy()) {
134
+ return;
135
+ }
136
+ // Entity backbone (hash-gated): converges the mirror even if the
137
+ // low-latency event subscriber missed events (e.g. while Neo4j was down).
138
+ // Endpoints (env/persona/workspace) before tasks/sessions so edges resolve.
139
+ // Endpointless entities first (env/persona), then workspaces, then tasks/
140
+ // sessions — so an edge's endpoint node already exists when it is applied.
141
+ // The `reconcileEdges` arg re-applies edges additively for unchanged rows
142
+ // each tick, so a transiently-dropped edge eventually heals.
143
+ await syncEntity(REFERENCE_SOURCE.ENVIRONMENT, envRegistry.listEnvironments(), (row) => row.id, environmentToNodeInput, projectEnvironment);
144
+ await syncEntity(REFERENCE_SOURCE.PERSONA, personaStore.listPersonas(), (row) => row.id, personaToNodeInput, projectPersona);
145
+ // Workspace hash folds in the linked-env set so a link/unlink re-projects
146
+ // (keeps LINKED_TO converged even if the change's event was missed).
147
+ await syncEntity(REFERENCE_SOURCE.WORKSPACE, workspaceStore.listWorkspaces(), (row) => row.id, (row) => workspaceToNodeInput(row, workspaceEnvironmentLinkStore.getLinkedEnvironmentIds(row.id)), projectWorkspace, reconcileWorkspaceEdges);
148
+ await syncEntity(REFERENCE_SOURCE.TASK, taskStore.listTasks(), (row) => row.id, taskToNodeInput, projectTask, reconcileTaskEdges);
149
+ await syncSessions(embedder);
150
+ await backfillEmbeddings(embedder);
151
+ },
152
+ };
153
+ }
154
+ //# sourceMappingURL=knowledge-projection-phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge-projection-phase.js","sourceRoot":"","sources":["../src/knowledge-projection-phase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pure text derivers that turn a Grackle entity row into an embeddable string
3
+ * for its knowledge-graph node (#1258).
4
+ *
5
+ * Each mirrors the `[Entity] ...` shape of the original task deriver so the
6
+ * embedding captures the entity's human-meaningful content.
7
+ *
8
+ * @module
9
+ */
10
+ import type { TaskRow, WorkspaceRow, SessionRow, PersonaRow, EnvironmentRow } from "@grackle-ai/database";
11
+ /** Embeddable text for a Task node. */
12
+ export declare function deriveTaskText(task: TaskRow): string;
13
+ /** Embeddable text for a Workspace node. */
14
+ export declare function deriveWorkspaceText(workspace: WorkspaceRow): string;
15
+ /** Embeddable text for a Session node (its prompt is the meaningful content). */
16
+ export declare function deriveSessionText(session: SessionRow): string;
17
+ /** Embeddable text for a Persona node. */
18
+ export declare function derivePersonaText(persona: PersonaRow): string;
19
+ /** Embeddable text for an Environment node. */
20
+ export declare function deriveEnvironmentText(environment: EnvironmentRow): string;
21
+ //# sourceMappingURL=derive-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-text.d.ts","sourceRoot":"","sources":["../../src/projection/derive-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACf,MAAM,sBAAsB,CAAC;AAO9B,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEpD;AAED,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAEnE;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAK7D;AAED,0CAA0C;AAC1C,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAE7D;AAED,+CAA+C;AAC/C,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,cAAc,GAAG,MAAM,CAKzE"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Pure text derivers that turn a Grackle entity row into an embeddable string
3
+ * for its knowledge-graph node (#1258).
4
+ *
5
+ * Each mirrors the `[Entity] ...` shape of the original task deriver so the
6
+ * embedding captures the entity's human-meaningful content.
7
+ *
8
+ * @module
9
+ */
10
+ /** Join non-empty parts with " - ". */
11
+ function joinParts(parts) {
12
+ return parts.filter((part) => Boolean(part && part.trim())).join(" - ");
13
+ }
14
+ /** Embeddable text for a Task node. */
15
+ export function deriveTaskText(task) {
16
+ return joinParts([`[Task] ${task.title}`, task.description]);
17
+ }
18
+ /** Embeddable text for a Workspace node. */
19
+ export function deriveWorkspaceText(workspace) {
20
+ return joinParts([`[Workspace] ${workspace.name}`, workspace.description]);
21
+ }
22
+ /** Embeddable text for a Session node (its prompt is the meaningful content). */
23
+ export function deriveSessionText(session) {
24
+ return joinParts([
25
+ `[Session] ${session.prompt}`,
26
+ session.model ? `model:${session.model}` : undefined,
27
+ ]);
28
+ }
29
+ /** Embeddable text for a Persona node. */
30
+ export function derivePersonaText(persona) {
31
+ return joinParts([`[Persona] ${persona.name}`, persona.description]);
32
+ }
33
+ /** Embeddable text for an Environment node. */
34
+ export function deriveEnvironmentText(environment) {
35
+ return joinParts([
36
+ `[Environment] ${environment.displayName}`,
37
+ `adapter:${environment.adapterType}`,
38
+ ]);
39
+ }
40
+ //# sourceMappingURL=derive-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-text.js","sourceRoot":"","sources":["../../src/projection/derive-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,uCAAuC;AACvC,SAAS,SAAS,CAAC,KAAgC;IACjD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC1F,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,OAAO,SAAS,CAAC,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,mBAAmB,CAAC,SAAuB;IACzD,OAAO,SAAS,CAAC,CAAC,eAAe,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,OAAmB;IACnD,OAAO,SAAS,CAAC;QACf,aAAa,OAAO,CAAC,MAAM,EAAE;QAC7B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC,CAAC;AACL,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,iBAAiB,CAAC,OAAmB;IACnD,OAAO,SAAS,CAAC,CAAC,aAAa,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,qBAAqB,CAAC,WAA2B;IAC/D,OAAO,SAAS,CAAC;QACf,iBAAiB,WAAW,CAAC,WAAW,EAAE;QAC1C,WAAW,WAAW,CAAC,WAAW,EAAE;KACrC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Pure mappers from Grackle SQL rows to structural knowledge-graph edge specs
3
+ * (#1258). An {@link EdgeSpec} identifies endpoints by `(sourceType, sourceId)`;
4
+ * the projector resolves those to node IDs at write time.
5
+ *
6
+ * Edge rule: an empty/`""` soft foreign-key reference produces NO edge.
7
+ *
8
+ * @module
9
+ */
10
+ import { type EdgeType, type ReferenceSource } from "@grackle-ai/knowledge";
11
+ import type { TaskRow, SessionRow } from "@grackle-ai/database";
12
+ /** Identifies a reference node by its source identity. */
13
+ export interface SourceKey {
14
+ sourceType: ReferenceSource;
15
+ sourceId: string;
16
+ }
17
+ /** A structural edge to project, with endpoints identified by source key. */
18
+ export interface EdgeSpec {
19
+ from: SourceKey;
20
+ to: SourceKey;
21
+ type: EdgeType;
22
+ }
23
+ /** Outgoing structural edge types reconciled on a Task node. */
24
+ export declare const TASK_EDGE_TYPES: EdgeType[];
25
+ /** Outgoing structural edge types reconciled on a Session node. */
26
+ export declare const SESSION_EDGE_TYPES: EdgeType[];
27
+ /** Outgoing structural edge types reconciled on a Workspace node. */
28
+ export declare const WORKSPACE_EDGE_TYPES: EdgeType[];
29
+ /** Defensively parse the `tasks.dependsOn` JSON-array column. */
30
+ export declare function parseDependsOn(raw: string): string[];
31
+ /** Structural edges originating from a Task: IN_WORKSPACE, PART_OF, DEPENDS_ON. */
32
+ export declare function taskEdges(task: TaskRow): EdgeSpec[];
33
+ /** Outgoing structural edges from a Session: ATTEMPT_OF, RAN_IN, USED_PERSONA. */
34
+ export declare function sessionEdges(session: SessionRow): EdgeSpec[];
35
+ /**
36
+ * The SPAWNED edge (parent Session → this Session). Originates from the parent,
37
+ * so it is reconciled with the child (parent_session_id is immutable); returned
38
+ * separately from {@link sessionEdges} and upserted (never bulk-removed) so a
39
+ * parent's spawn edges aren't cleared when the parent re-projects.
40
+ */
41
+ export declare function sessionSpawnEdge(session: SessionRow): EdgeSpec | undefined;
42
+ /** The LINKED_TO edge for a workspace↔environment link. */
43
+ export declare function workspaceLinkEdge(workspaceId: string, environmentId: string): EdgeSpec;
44
+ //# sourceMappingURL=edge-mappers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-mappers.d.ts","sourceRoot":"","sources":["../../src/projection/edge-mappers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACX,MAAM,sBAAsB,CAAC;AAE9B,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,eAAe,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;CAChB;AAOD,gEAAgE;AAChE,eAAO,MAAM,eAAe,EAAE,QAAQ,EAIrC,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,EAAE,QAAQ,EAIxC,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,EAAE,QAAQ,EAA0B,CAAC;AAEtE,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CASpD;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,EAAE,CAanD;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,EAAE,CAa5D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS,CAS1E;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,QAAQ,CAMtF"}