@render-harness/cap-memory-pg 0.4.1 → 0.5.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.
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { definePack } from '@render-harness/registry';
7
7
 
8
8
  // package.json
9
9
  var package_default = {
10
- version: "0.4.1"};
10
+ version: "0.5.0"};
11
11
 
12
12
  // src/index.ts
13
13
  var HERE = dirname(fileURLToPath(import.meta.url));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["{\n \"name\": \"@render-harness/cap-memory-pg\",\n \"version\": \"0.4.1\",\n \"description\": \"Postgres-backed long-term memory for the Render agent harness.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"keywords\": [\n \"render-harness-cap\",\n \"render-harness\",\n \"memory\",\n \"postgres\"\n ],\n \"renderHarness\": {\n \"gallery\": {\n \"label\": \"Postgres long-term memory\",\n \"envHint\": \"DATABASE_URL\"\n }\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --passWithNoTests\"\n },\n \"dependencies\": {\n \"@render-harness/core\": \"workspace:*\",\n \"@render-harness/registry\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.6.2\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/render-lab/render-agent-harness.git\",\n \"directory\": \"packages/capabilities/cap-memory-pg\"\n }\n}\n","/**\n * cap-memory-pg — long-term memory backed by Postgres.\n *\n * Adds two LocalToolHandlers an agent can use to write durable notes\n * across runs and search them with fuzzy text matching.\n *\n * - memory.write { key, value, tags? } → stores a note.\n * - memory.search { query, limit?, tags? } → returns ranked matches.\n *\n * The pack uses Postgres `pg_trgm` for fuzzy similarity. We don't pull\n * in pgvector for v1 — keeps the dependency surface tiny and avoids\n * extension issues on Render's managed Postgres.\n *\n * Usage in render-harness.yaml:\n *\n * capabilities:\n * - pack: \"@render-harness/cap-memory-pg\"\n * config:\n * namespace: \"support\" # optional; default: entry.name\n *\n * The Postgres table is bootstrapped lazily on first tool call:\n * `CREATE TABLE IF NOT EXISTS` and `CREATE EXTENSION IF NOT EXISTS pg_trgm`.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getPool, type LocalToolHandler, type SkillMetadata } from \"@render-harness/core\";\nimport { definePack, type PackContext } from \"@render-harness/registry\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\nconst SKILLS_DIR = join(HERE, \"..\", \"skills\");\n\ninterface MemoryConfig {\n namespace?: string;\n}\n\nconst SCHEMA_BOOTSTRAP_SQL = `\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\n\nCREATE TABLE IF NOT EXISTS agent_memory (\n id BIGSERIAL PRIMARY KEY,\n namespace TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n tags TEXT[] NOT NULL DEFAULT '{}',\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CONSTRAINT agent_memory_ns_key UNIQUE (namespace, key)\n);\nCREATE INDEX IF NOT EXISTS agent_memory_ns_idx ON agent_memory (namespace);\nCREATE INDEX IF NOT EXISTS agent_memory_value_trgm\n ON agent_memory USING gin (value gin_trgm_ops);\nCREATE INDEX IF NOT EXISTS agent_memory_tags_idx ON agent_memory USING gin (tags);\n`;\n\nlet bootstrapped = false;\nasync function ensureSchema(): Promise<void> {\n if (bootstrapped) return;\n const pool = getPool();\n await pool.query(SCHEMA_BOOTSTRAP_SQL);\n bootstrapped = true;\n}\n\nconst pack = definePack({\n name: \"cap-memory-pg\",\n version: pkg.version,\n localTools(ctx: PackContext): LocalToolHandler[] {\n const cfg = ctx.config as MemoryConfig;\n const namespace = cfg.namespace ?? ctx.entryName;\n\n const writeMemory: LocalToolHandler = {\n definition: {\n name: \"write\",\n description:\n \"Store a durable note in long-term memory. Notes survive across runs of this agent. Use for facts the user wants you to remember (preferences, names, decisions). The (key) is unique per namespace; calling write twice with the same key updates the value.\",\n source: \"pack:cap-memory-pg\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n key: {\n type: \"string\",\n description: \"Stable identifier. Reuse the same key to update.\",\n minLength: 1,\n maxLength: 256,\n },\n value: {\n type: \"string\",\n description: \"The note body.\",\n minLength: 1,\n maxLength: 10_000,\n },\n tags: {\n type: \"array\",\n items: { type: \"string\", minLength: 1, maxLength: 64 },\n description: \"Optional category tags for filtering on search.\",\n maxItems: 16,\n },\n },\n required: [\"key\", \"value\"],\n },\n },\n async handler({ input }) {\n const args = (input ?? {}) as { key?: string; value?: string; tags?: string[] };\n if (!args.key || !args.value) {\n return { content: \"memory.write: key and value are required\", isError: true };\n }\n await ensureSchema();\n const pool = getPool();\n const result = await pool.query<{ id: string; updated: boolean }>(\n `INSERT INTO agent_memory (namespace, key, value, tags)\n VALUES ($1, $2, $3, COALESCE($4::text[], '{}'::text[]))\n ON CONFLICT (namespace, key) DO UPDATE SET\n value = EXCLUDED.value,\n tags = EXCLUDED.tags,\n updated_at = now()\n RETURNING id, (xmax::text::int > 0) AS updated`,\n [namespace, args.key, args.value, args.tags ?? null],\n );\n const row = result.rows[0];\n return {\n content: row\n ? `memory.write: ${row.updated ? \"updated\" : \"created\"} id=${row.id} key=\"${args.key}\"`\n : \"memory.write: stored\",\n };\n },\n };\n\n const searchMemory: LocalToolHandler = {\n definition: {\n name: \"search\",\n description:\n \"Search long-term memory using fuzzy text similarity. Returns the top matches ranked by trigram similarity. Use this before answering when the user asks about something they may have told you before.\",\n source: \"pack:cap-memory-pg\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n query: { type: \"string\", description: \"Free-form search text.\", minLength: 1 },\n limit: {\n type: \"integer\",\n description: \"Max rows to return. Default 10, max 50.\",\n minimum: 1,\n maximum: 50,\n },\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Optional tag filter; row must have at least one of these.\",\n },\n },\n required: [\"query\"],\n },\n },\n async handler({ input }) {\n const args = (input ?? {}) as { query?: string; limit?: number; tags?: string[] };\n if (!args.query) {\n return { content: \"memory.search: query is required\", isError: true };\n }\n await ensureSchema();\n const pool = getPool();\n const limit = Math.min(args.limit ?? 10, 50);\n const rows = await pool.query<{\n key: string;\n value: string;\n tags: string[];\n score: number;\n updated_at: Date;\n }>(\n `SELECT key, value, tags, similarity(value, $2) AS score, updated_at\n FROM agent_memory\n WHERE namespace = $1\n AND ($3::text[] IS NULL OR tags && $3::text[])\n AND value ILIKE '%' || $2 || '%' OR similarity(value, $2) > 0.1\n ORDER BY score DESC, updated_at DESC\n LIMIT $4`,\n [namespace, args.query, args.tags ?? null, limit],\n );\n if (rows.rows.length === 0) {\n return {\n content: `memory.search: no matches for \"${args.query}\" in namespace \"${namespace}\"`,\n };\n }\n const lines = rows.rows.map(\n (r, i) =>\n `${i + 1}. [${r.key}] (score=${r.score.toFixed(2)}, tags=${r.tags.join(\",\") || \"—\"})\\n ${r.value.slice(0, 600)}`,\n );\n return { content: lines.join(\"\\n\\n\") };\n },\n };\n\n return [writeMemory, searchMemory];\n },\n skills(_ctx: PackContext): SkillMetadata[] {\n return [\n {\n name: \"memory\",\n description: \"Use long-term memory to remember facts across runs.\",\n whenToUse:\n \"When the user asks you to remember something, OR when they ask about something they may have told you in a prior run.\",\n contentPath: join(SKILLS_DIR, \"memory.md\"),\n },\n ];\n },\n});\n\nexport default pack;\n"]}
1
+ {"version":3,"sources":["../package.json","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["{\n \"name\": \"@render-harness/cap-memory-pg\",\n \"version\": \"0.5.0\",\n \"description\": \"Postgres-backed long-term memory for the Render agent harness.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"keywords\": [\n \"render-harness-cap\",\n \"render-harness\",\n \"memory\",\n \"postgres\"\n ],\n \"renderHarness\": {\n \"gallery\": {\n \"label\": \"Postgres long-term memory\",\n \"envHint\": \"DATABASE_URL\"\n }\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --passWithNoTests\"\n },\n \"dependencies\": {\n \"@render-harness/core\": \"workspace:*\",\n \"@render-harness/registry\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.6.2\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/render-lab/render-agent-harness.git\",\n \"directory\": \"packages/capabilities/cap-memory-pg\"\n }\n}\n","/**\n * cap-memory-pg — long-term memory backed by Postgres.\n *\n * Adds two LocalToolHandlers an agent can use to write durable notes\n * across runs and search them with fuzzy text matching.\n *\n * - memory.write { key, value, tags? } → stores a note.\n * - memory.search { query, limit?, tags? } → returns ranked matches.\n *\n * The pack uses Postgres `pg_trgm` for fuzzy similarity. We don't pull\n * in pgvector for v1 — keeps the dependency surface tiny and avoids\n * extension issues on Render's managed Postgres.\n *\n * Usage in render-harness.yaml:\n *\n * capabilities:\n * - pack: \"@render-harness/cap-memory-pg\"\n * config:\n * namespace: \"support\" # optional; default: entry.name\n *\n * The Postgres table is bootstrapped lazily on first tool call:\n * `CREATE TABLE IF NOT EXISTS` and `CREATE EXTENSION IF NOT EXISTS pg_trgm`.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getPool, type LocalToolHandler, type SkillMetadata } from \"@render-harness/core\";\nimport { definePack, type PackContext } from \"@render-harness/registry\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\nconst SKILLS_DIR = join(HERE, \"..\", \"skills\");\n\ninterface MemoryConfig {\n namespace?: string;\n}\n\nconst SCHEMA_BOOTSTRAP_SQL = `\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\n\nCREATE TABLE IF NOT EXISTS agent_memory (\n id BIGSERIAL PRIMARY KEY,\n namespace TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n tags TEXT[] NOT NULL DEFAULT '{}',\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CONSTRAINT agent_memory_ns_key UNIQUE (namespace, key)\n);\nCREATE INDEX IF NOT EXISTS agent_memory_ns_idx ON agent_memory (namespace);\nCREATE INDEX IF NOT EXISTS agent_memory_value_trgm\n ON agent_memory USING gin (value gin_trgm_ops);\nCREATE INDEX IF NOT EXISTS agent_memory_tags_idx ON agent_memory USING gin (tags);\n`;\n\nlet bootstrapped = false;\nasync function ensureSchema(): Promise<void> {\n if (bootstrapped) return;\n const pool = getPool();\n await pool.query(SCHEMA_BOOTSTRAP_SQL);\n bootstrapped = true;\n}\n\nconst pack = definePack({\n name: \"cap-memory-pg\",\n version: pkg.version,\n localTools(ctx: PackContext): LocalToolHandler[] {\n const cfg = ctx.config as MemoryConfig;\n const namespace = cfg.namespace ?? ctx.entryName;\n\n const writeMemory: LocalToolHandler = {\n definition: {\n name: \"write\",\n description:\n \"Store a durable note in long-term memory. Notes survive across runs of this agent. Use for facts the user wants you to remember (preferences, names, decisions). The (key) is unique per namespace; calling write twice with the same key updates the value.\",\n source: \"pack:cap-memory-pg\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n key: {\n type: \"string\",\n description: \"Stable identifier. Reuse the same key to update.\",\n minLength: 1,\n maxLength: 256,\n },\n value: {\n type: \"string\",\n description: \"The note body.\",\n minLength: 1,\n maxLength: 10_000,\n },\n tags: {\n type: \"array\",\n items: { type: \"string\", minLength: 1, maxLength: 64 },\n description: \"Optional category tags for filtering on search.\",\n maxItems: 16,\n },\n },\n required: [\"key\", \"value\"],\n },\n },\n async handler({ input }) {\n const args = (input ?? {}) as { key?: string; value?: string; tags?: string[] };\n if (!args.key || !args.value) {\n return { content: \"memory.write: key and value are required\", isError: true };\n }\n await ensureSchema();\n const pool = getPool();\n const result = await pool.query<{ id: string; updated: boolean }>(\n `INSERT INTO agent_memory (namespace, key, value, tags)\n VALUES ($1, $2, $3, COALESCE($4::text[], '{}'::text[]))\n ON CONFLICT (namespace, key) DO UPDATE SET\n value = EXCLUDED.value,\n tags = EXCLUDED.tags,\n updated_at = now()\n RETURNING id, (xmax::text::int > 0) AS updated`,\n [namespace, args.key, args.value, args.tags ?? null],\n );\n const row = result.rows[0];\n return {\n content: row\n ? `memory.write: ${row.updated ? \"updated\" : \"created\"} id=${row.id} key=\"${args.key}\"`\n : \"memory.write: stored\",\n };\n },\n };\n\n const searchMemory: LocalToolHandler = {\n definition: {\n name: \"search\",\n description:\n \"Search long-term memory using fuzzy text similarity. Returns the top matches ranked by trigram similarity. Use this before answering when the user asks about something they may have told you before.\",\n source: \"pack:cap-memory-pg\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n query: { type: \"string\", description: \"Free-form search text.\", minLength: 1 },\n limit: {\n type: \"integer\",\n description: \"Max rows to return. Default 10, max 50.\",\n minimum: 1,\n maximum: 50,\n },\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Optional tag filter; row must have at least one of these.\",\n },\n },\n required: [\"query\"],\n },\n },\n async handler({ input }) {\n const args = (input ?? {}) as { query?: string; limit?: number; tags?: string[] };\n if (!args.query) {\n return { content: \"memory.search: query is required\", isError: true };\n }\n await ensureSchema();\n const pool = getPool();\n const limit = Math.min(args.limit ?? 10, 50);\n const rows = await pool.query<{\n key: string;\n value: string;\n tags: string[];\n score: number;\n updated_at: Date;\n }>(\n `SELECT key, value, tags, similarity(value, $2) AS score, updated_at\n FROM agent_memory\n WHERE namespace = $1\n AND ($3::text[] IS NULL OR tags && $3::text[])\n AND value ILIKE '%' || $2 || '%' OR similarity(value, $2) > 0.1\n ORDER BY score DESC, updated_at DESC\n LIMIT $4`,\n [namespace, args.query, args.tags ?? null, limit],\n );\n if (rows.rows.length === 0) {\n return {\n content: `memory.search: no matches for \"${args.query}\" in namespace \"${namespace}\"`,\n };\n }\n const lines = rows.rows.map(\n (r, i) =>\n `${i + 1}. [${r.key}] (score=${r.score.toFixed(2)}, tags=${r.tags.join(\",\") || \"—\"})\\n ${r.value.slice(0, 600)}`,\n );\n return { content: lines.join(\"\\n\\n\") };\n },\n };\n\n return [writeMemory, searchMemory];\n },\n skills(_ctx: PackContext): SkillMetadata[] {\n return [\n {\n name: \"memory\",\n description: \"Use long-term memory to remember facts across runs.\",\n whenToUse:\n \"When the user asks you to remember something, OR when they ask about something they may have told you in a prior run.\",\n contentPath: join(SKILLS_DIR, \"memory.md\"),\n },\n ];\n },\n});\n\nexport default pack;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@render-harness/cap-memory-pg",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Postgres-backed long-term memory for the Render agent harness.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,8 +30,8 @@
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
- "@render-harness/core": "0.4.1",
34
- "@render-harness/registry": "0.4.1"
33
+ "@render-harness/core": "0.5.0",
34
+ "@render-harness/registry": "0.5.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.6.2",