@phi-code-admin/phi-code 0.63.2 → 0.63.3
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.
- package/extensions/phi/memory.ts +128 -0
- package/package.json +1 -1
package/extensions/phi/memory.ts
CHANGED
|
@@ -211,6 +211,134 @@ export default function memoryExtension(pi: ExtensionAPI) {
|
|
|
211
211
|
},
|
|
212
212
|
});
|
|
213
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Ontology tool - Add entities and relations to the knowledge graph
|
|
216
|
+
*/
|
|
217
|
+
pi.registerTool({
|
|
218
|
+
name: "ontology_add",
|
|
219
|
+
label: "Ontology Add",
|
|
220
|
+
description: "Add an entity or relation to the project knowledge graph. Entities represent things (projects, files, services, people). Relations connect them.",
|
|
221
|
+
promptGuidelines: [
|
|
222
|
+
"When discovering project architecture (services, databases, APIs), add entities and relations to the ontology.",
|
|
223
|
+
"When learning about how components connect, add relations (e.g. 'api-server' → 'uses' → 'postgres-db').",
|
|
224
|
+
],
|
|
225
|
+
parameters: Type.Object({
|
|
226
|
+
type: Type.Union([Type.Literal("entity"), Type.Literal("relation")], { description: "What to add: 'entity' or 'relation'" }),
|
|
227
|
+
// Entity fields
|
|
228
|
+
entityType: Type.Optional(Type.String({ description: "Entity type (e.g. Project, Service, Database, File, Person, Tool)" })),
|
|
229
|
+
name: Type.Optional(Type.String({ description: "Entity name (e.g. 'my-api', 'postgres-db')" })),
|
|
230
|
+
properties: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Key-value properties (e.g. {language: 'TypeScript', port: '3000'})" })),
|
|
231
|
+
// Relation fields
|
|
232
|
+
from: Type.Optional(Type.String({ description: "Source entity ID" })),
|
|
233
|
+
to: Type.Optional(Type.String({ description: "Target entity ID" })),
|
|
234
|
+
relationType: Type.Optional(Type.String({ description: "Relation type (e.g. 'uses', 'depends-on', 'deployed-on', 'created-by')" })),
|
|
235
|
+
}),
|
|
236
|
+
|
|
237
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
238
|
+
const p = params as any;
|
|
239
|
+
try {
|
|
240
|
+
if (p.type === "entity") {
|
|
241
|
+
if (!p.entityType || !p.name) {
|
|
242
|
+
return { content: [{ type: "text", text: "Entity requires 'entityType' and 'name'" }], isError: true };
|
|
243
|
+
}
|
|
244
|
+
const id = sigmaMemory.ontology.addEntity({
|
|
245
|
+
type: p.entityType,
|
|
246
|
+
name: p.name,
|
|
247
|
+
properties: p.properties || {},
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
content: [{ type: "text", text: `Entity added: **${p.name}** (${p.entityType}) — ID: \`${id}\`` }],
|
|
251
|
+
details: { id, type: p.entityType, name: p.name },
|
|
252
|
+
};
|
|
253
|
+
} else if (p.type === "relation") {
|
|
254
|
+
if (!p.from || !p.to || !p.relationType) {
|
|
255
|
+
return { content: [{ type: "text", text: "Relation requires 'from', 'to', and 'relationType'" }], isError: true };
|
|
256
|
+
}
|
|
257
|
+
const id = sigmaMemory.ontology.addRelation({
|
|
258
|
+
from: p.from,
|
|
259
|
+
to: p.to,
|
|
260
|
+
type: p.relationType,
|
|
261
|
+
properties: p.properties || {},
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
content: [{ type: "text", text: `Relation added: \`${p.from}\` → **${p.relationType}** → \`${p.to}\` — ID: \`${id}\`` }],
|
|
265
|
+
details: { id, from: p.from, to: p.to, type: p.relationType },
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return { content: [{ type: "text", text: "Type must be 'entity' or 'relation'" }], isError: true };
|
|
269
|
+
} catch (error) {
|
|
270
|
+
return { content: [{ type: "text", text: `Ontology error: ${error}` }], isError: true };
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Ontology query tool - Query the knowledge graph
|
|
277
|
+
*/
|
|
278
|
+
pi.registerTool({
|
|
279
|
+
name: "ontology_query",
|
|
280
|
+
label: "Ontology Query",
|
|
281
|
+
description: "Query the project knowledge graph. Find entities by type/name, get relations, find paths between entities, or get stats.",
|
|
282
|
+
parameters: Type.Object({
|
|
283
|
+
action: Type.Union([
|
|
284
|
+
Type.Literal("find"),
|
|
285
|
+
Type.Literal("relations"),
|
|
286
|
+
Type.Literal("path"),
|
|
287
|
+
Type.Literal("stats"),
|
|
288
|
+
Type.Literal("graph"),
|
|
289
|
+
], { description: "Query action: find (entities), relations (of entity), path (between entities), stats, graph (full export)" }),
|
|
290
|
+
entityType: Type.Optional(Type.String({ description: "Filter by entity type (for 'find' action)" })),
|
|
291
|
+
name: Type.Optional(Type.String({ description: "Filter by name (partial match, for 'find' action)" })),
|
|
292
|
+
entityId: Type.Optional(Type.String({ description: "Entity ID (for 'relations' action)" })),
|
|
293
|
+
fromId: Type.Optional(Type.String({ description: "Source entity ID (for 'path' action)" })),
|
|
294
|
+
toId: Type.Optional(Type.String({ description: "Target entity ID (for 'path' action)" })),
|
|
295
|
+
}),
|
|
296
|
+
|
|
297
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
298
|
+
const p = params as any;
|
|
299
|
+
try {
|
|
300
|
+
switch (p.action) {
|
|
301
|
+
case "find": {
|
|
302
|
+
const results = sigmaMemory.ontology.findEntity({ type: p.entityType, name: p.name });
|
|
303
|
+
if (results.length === 0) return { content: [{ type: "text", text: "No entities found." }] };
|
|
304
|
+
const text = results.map(e => `- **${e.name}** (${e.type}) ID:\`${e.id}\` ${JSON.stringify(e.properties)}`).join("\n");
|
|
305
|
+
return { content: [{ type: "text", text: `Found ${results.length} entities:\n${text}` }] };
|
|
306
|
+
}
|
|
307
|
+
case "relations": {
|
|
308
|
+
if (!p.entityId) return { content: [{ type: "text", text: "'entityId' required" }], isError: true };
|
|
309
|
+
const rels = sigmaMemory.ontology.findRelations(p.entityId);
|
|
310
|
+
if (rels.length === 0) return { content: [{ type: "text", text: "No relations found." }] };
|
|
311
|
+
const text = rels.map(r => `- \`${r.from}\` → **${r.type}** → \`${r.to}\``).join("\n");
|
|
312
|
+
return { content: [{ type: "text", text: `Found ${rels.length} relations:\n${text}` }] };
|
|
313
|
+
}
|
|
314
|
+
case "path": {
|
|
315
|
+
if (!p.fromId || !p.toId) return { content: [{ type: "text", text: "'fromId' and 'toId' required" }], isError: true };
|
|
316
|
+
const path = sigmaMemory.ontology.queryPath(p.fromId, p.toId);
|
|
317
|
+
if (!path) return { content: [{ type: "text", text: "No path found between these entities." }] };
|
|
318
|
+
const text = path.map(s => `${s.entity.name}${s.relation ? ` → [${s.relation.type}]` : ""}`).join(" → ");
|
|
319
|
+
return { content: [{ type: "text", text: `Path: ${text}` }] };
|
|
320
|
+
}
|
|
321
|
+
case "stats": {
|
|
322
|
+
const stats = sigmaMemory.ontology.stats();
|
|
323
|
+
const graph = sigmaMemory.ontology.getGraph();
|
|
324
|
+
let text = `**Ontology Stats:**\n- Entities: ${graph.entities.length}\n- Relations: ${graph.relations.length}\n`;
|
|
325
|
+
text += `\nBy type:\n`;
|
|
326
|
+
for (const [type, count] of Object.entries(stats.entitiesByType)) text += ` - ${type}: ${count}\n`;
|
|
327
|
+
return { content: [{ type: "text", text }] };
|
|
328
|
+
}
|
|
329
|
+
case "graph": {
|
|
330
|
+
const graph = sigmaMemory.ontology.export();
|
|
331
|
+
return { content: [{ type: "text", text: JSON.stringify(graph, null, 2) }] };
|
|
332
|
+
}
|
|
333
|
+
default:
|
|
334
|
+
return { content: [{ type: "text", text: "Action must be: find, relations, path, stats, graph" }], isError: true };
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
return { content: [{ type: "text", text: `Ontology query error: ${error}` }], isError: true };
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
214
342
|
/**
|
|
215
343
|
* Memory status tool - Get status of all memory subsystems
|
|
216
344
|
*/
|