refactor(integrations): shared agent plugin runtimes and native adapters (#7203)

This commit is contained in:
Kartik
2026-09-08 23:32:25 +05:30
committed by GitHub
parent dae67f74f5
commit 73e7b8763a
369 changed files with 31056 additions and 20464 deletions
@@ -1,43 +1,7 @@
interface MemoryLike {
id: string;
memory?: string;
categories?: string[];
createdAt?: Date | string;
}
export function formatAge(date: Date | string): string {
const d = typeof date === "string" ? new Date(date) : date;
const ms = Date.now() - d.getTime();
const minutes = Math.floor(ms / 60_000);
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h ago`;
const days = Math.floor(hours / 24);
return `${days}d ago`;
}
export function formatMemoryCompact(mem: MemoryLike): string {
const cat = mem.categories?.[0] ?? "uncategorized";
const age = mem.createdAt ? ` (${formatAge(mem.createdAt)})` : "";
return `[${cat}] ${mem.memory ?? "(empty)"}${age} [mem0:${mem.id}]`;
}
export function formatMemoryList(memories: MemoryLike[]): string {
if (memories.length === 0) return "No memories found.";
return memories
.map((m, i) => `${i + 1}. ${formatMemoryCompact(m)}`)
.join("\n");
}
export function groupByCategory(
memories: MemoryLike[],
): Map<string, MemoryLike[]> {
const groups = new Map<string, MemoryLike[]>();
for (const m of memories) {
const cat = m.categories?.[0] ?? "uncategorized";
const list = groups.get(cat) ?? [];
list.push(m);
groups.set(cat, list);
}
return groups;
}
export {
formatAge,
formatMemoryCompact,
formatMemoryList,
groupByCategory,
} from "../../../agent-plugin-core/typescript/src/formatting.ts";
export type { MemoryLike } from "../../../agent-plugin-core/typescript/src/formatting.ts";
@@ -63,8 +63,8 @@ describe("resolveSearchFilters", () => {
expect(resolveSearchFilters("session", ctx)).toEqual({ user_id: "u1", app_id: "a1", run_id: "r1" });
});
it("uses wildcard app_id for global scope", () => {
expect(resolveSearchFilters("global", ctx)).toEqual({ user_id: "u1", app_id: "*" });
it("limits global scope to the configured user", () => {
expect(resolveSearchFilters("global", ctx)).toEqual({ user_id: "u1" });
});
});
@@ -2,6 +2,10 @@ import * as path from "node:path";
import * as crypto from "node:crypto";
import { execFileSync } from "node:child_process";
import type { Scope, ScopeContext } from "../types.ts";
import {
scopeAddParams,
scopeSearchFilters,
} from "../../../agent-plugin-core/typescript/src/scoping.ts";
export function detectAppId(cwd: string): string {
try {
@@ -26,26 +30,12 @@ export function resolveSearchFilters(
scope: Scope,
ctx: ScopeContext,
): Record<string, string> {
switch (scope) {
case "project":
return { user_id: ctx.userId, app_id: ctx.appId };
case "session":
return { user_id: ctx.userId, app_id: ctx.appId, run_id: ctx.runId };
case "global":
return { user_id: ctx.userId, app_id: "*" };
}
return scopeSearchFilters(scope, ctx);
}
export function resolveAddParams(
scope: Scope,
ctx: ScopeContext,
): Record<string, string> {
switch (scope) {
case "project":
return { userId: ctx.userId, appId: ctx.appId };
case "session":
return { userId: ctx.userId, appId: ctx.appId, runId: ctx.runId };
case "global":
return { userId: ctx.userId };
}
return scopeAddParams(scope, ctx);
}
@@ -1,3 +1,4 @@
import { resolveToolScope } from "../../../agent-plugin-core/typescript/src/scoping.ts";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
import { StringEnum } from "@earendil-works/pi-ai";
@@ -17,6 +18,10 @@ interface MemoryResult {
const MAX_OUTPUT_LINES = 200;
const MAX_OUTPUT_BYTES = 50_000;
function normalizeMemoryId(id: string): string {
return id.replace(/^\[?mem0:([0-9a-f-]{36})\]?$/i, "$1");
}
function truncateOutput(text: string): string {
const lines = text.split("\n");
if (lines.length <= MAX_OUTPUT_LINES && text.length <= MAX_OUTPUT_BYTES) {
@@ -50,7 +55,7 @@ export function buildToolExecute(
defaultScope: Scope,
) {
return async (params: ToolParams, signal?: AbortSignal) => {
const scope = params.scope ?? defaultScope;
const scope = resolveToolScope(params.scope, defaultScope);
switch (params.action) {
case "search": {
@@ -96,18 +101,19 @@ export function buildToolExecute(
if (signal?.aborted) throw new Error("Cancelled");
if (!params.memory_id) throw new Error("memory_id is required for update");
if (!params.content) throw new Error("content is required for update");
const updateResult = await mem0.update(params.memory_id, { text: params.content });
const memoryId = normalizeMemoryId(params.memory_id);
const updateResult = await mem0.update(memoryId, { text: params.content });
const res = updateResult as MemoryResult;
return {
content: [{ type: "text" as const, text: res.status ?? "Memory updated." }],
details: { memoryId: params.memory_id },
details: { memoryId },
};
}
case "delete": {
if (signal?.aborted) throw new Error("Cancelled");
if (!params.memory_id) throw new Error("memory_id is required for delete");
const result = await mem0.delete(params.memory_id);
const result = await mem0.delete(normalizeMemoryId(params.memory_id));
return {
content: [{ type: "text" as const, text: result.message ?? "Memory deleted." }],
details: {},
@@ -145,7 +151,7 @@ export function registerMemoryTool(
'For multi-part or comparative questions, run several searches with different phrasings and combine the results before answering -- one search is rarely enough',
'Use mem0_memory with action "add" to save important facts, preferences, goals, decisions, or lessons the user shares',
'Use mem0_memory with action "update" to modify an existing memory — requires memory_id and content. Preserves the memory ID',
"Always use the default project scope unless the user EXPLICITLY asks to search across all projects — only then use scope \"global\"",
"Always use the default project scope unless the user EXPLICITLY asks to search across all projects — only after the user selects /mem0-scope global use scope \"global\"",
"Do NOT pass scope at all for normal queries — omitting it uses the project default automatically",
],
parameters: Type.Object({