natureco-cli 5.14.7 → 5.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.14.7",
3
+ "version": "5.15.0",
4
4
  "description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
5
5
  "bin": {
6
6
  "natureco": "bin/natureco.js"
@@ -0,0 +1,138 @@
1
+ /**
2
+ * file-memory.js — File-based memory provider (default)
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const { MemoryProvider, registerProvider } = require('../utils/memory-provider');
9
+
10
+ const BASE_DIR = path.join(os.homedir(), '.natureco', 'memory');
11
+
12
+ class FileMemoryProvider extends MemoryProvider {
13
+ constructor(config = {}) {
14
+ super(config);
15
+ this.name = 'file';
16
+ }
17
+
18
+ _ensureDir() {
19
+ if (!fs.existsSync(BASE_DIR)) fs.mkdirSync(BASE_DIR, { recursive: true });
20
+ }
21
+
22
+ _fileFor(userId) {
23
+ return path.join(BASE_DIR, `${(userId || 'default').toLowerCase()}.json`);
24
+ }
25
+
26
+ _load(userId) {
27
+ this._ensureDir();
28
+ const file = this._fileFor(userId);
29
+ try {
30
+ if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8'));
31
+ } catch {}
32
+ return { name: userId || 'User', nickname: null, botName: null, facts: [], preferences: [] };
33
+ }
34
+
35
+ _save(userId, data) {
36
+ this._ensureDir();
37
+ data.lastUpdated = new Date().toISOString();
38
+ fs.writeFileSync(this._fileFor(userId), JSON.stringify(data, null, 2));
39
+ }
40
+
41
+ async add(userId, content, metadata = {}) {
42
+ const uid = userId || 'default';
43
+ const mem = this._load(uid);
44
+ const now = new Date().toISOString();
45
+ const existing = mem.facts.find(f => (f.value || '').toLowerCase() === content.toLowerCase());
46
+ if (existing) {
47
+ existing.score = Math.min(10, (existing.score || 5) + 2);
48
+ existing.updatedAt = now;
49
+ } else {
50
+ mem.facts.push({
51
+ value: content,
52
+ score: metadata.score || 5,
53
+ category: metadata.category || 'general',
54
+ createdAt: now,
55
+ updatedAt: now,
56
+ });
57
+ }
58
+ if (metadata.name) mem.name = metadata.name;
59
+ if (metadata.botName) mem.botName = metadata.botName;
60
+ if (metadata.nickname !== undefined) mem.nickname = metadata.nickname;
61
+ this._save(uid, mem);
62
+ return { success: true, id: content, message: 'Memory added', totalFacts: mem.facts.length };
63
+ }
64
+
65
+ async search(query, options = {}) {
66
+ const uid = options.userId;
67
+ const results = [];
68
+ if (uid) {
69
+ const mem = this._load(uid);
70
+ for (const f of mem.facts || []) {
71
+ const v = f.value || '';
72
+ if (v.toLowerCase().includes(query.toLowerCase())) {
73
+ results.push({ id: v, content: v, score: f.score || 5, metadata: { category: f.category } });
74
+ }
75
+ }
76
+ } else {
77
+ this._ensureDir();
78
+ const files = fs.readdirSync(BASE_DIR).filter(f => f.endsWith('.json'));
79
+ for (const file of files) {
80
+ try {
81
+ const mem = JSON.parse(fs.readFileSync(path.join(BASE_DIR, file), 'utf8'));
82
+ for (const f of mem.facts || []) {
83
+ const v = f.value || '';
84
+ if (v.toLowerCase().includes(query.toLowerCase())) {
85
+ results.push({ id: v, content: v, score: f.score || 5, metadata: { userId: file.replace('.json', ''), category: f.category } });
86
+ }
87
+ }
88
+ } catch {}
89
+ }
90
+ }
91
+ results.sort((a, b) => (b.score || 0) - (a.score || 0));
92
+ const limit = options.limit || 20;
93
+ return { success: true, results: results.slice(0, limit) };
94
+ }
95
+
96
+ async list(userId) {
97
+ const uid = userId || 'default';
98
+ const mem = this._load(uid);
99
+ return {
100
+ success: true,
101
+ memories: (mem.facts || []).map(f => ({
102
+ id: f.value,
103
+ content: f.value,
104
+ score: f.score,
105
+ metadata: { category: f.category, createdAt: f.createdAt, updatedAt: f.updatedAt },
106
+ })),
107
+ name: mem.name,
108
+ nickname: mem.nickname,
109
+ botName: mem.botName,
110
+ };
111
+ }
112
+
113
+ async remove(id) {
114
+ const files = fs.readdirSync(BASE_DIR).filter(f => f.endsWith('.json'));
115
+ for (const file of files) {
116
+ try {
117
+ const mem = JSON.parse(fs.readFileSync(path.join(BASE_DIR, file), 'utf8'));
118
+ const before = mem.facts.length;
119
+ mem.facts = mem.facts.filter(f => (f.value || '').toLowerCase() !== id.toLowerCase());
120
+ if (mem.facts.length !== before) {
121
+ this._save(file.replace('.json', ''), mem);
122
+ return { success: true, message: 'Memory removed' };
123
+ }
124
+ } catch {}
125
+ }
126
+ return { success: false, message: 'Memory not found' };
127
+ }
128
+
129
+ async clear(userId) {
130
+ const uid = userId || 'default';
131
+ const file = this._fileFor(uid);
132
+ if (fs.existsSync(file)) fs.unlinkSync(file);
133
+ return { success: true, message: `Memory cleared for ${uid}` };
134
+ }
135
+ }
136
+
137
+ registerProvider('file', FileMemoryProvider);
138
+ module.exports = FileMemoryProvider;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * mem0-memory.js — Mem0 memory provider
3
+ *
4
+ * Uses the mem0ai npm package (platform API) or local OSS Memory class.
5
+ * Requires MEM0_API_KEY env var or config.mem0.apiKey.
6
+ * npm install mem0ai
7
+ */
8
+
9
+ const { MemoryProvider, registerProvider } = require('../utils/memory-provider');
10
+
11
+ class Mem0MemoryProvider extends MemoryProvider {
12
+ constructor(config = {}) {
13
+ super(config);
14
+ this.name = 'mem0';
15
+ this._client = null;
16
+ this._mode = 'platform'; // or 'oss'
17
+ }
18
+
19
+ _getConfig() {
20
+ return this.config.mem0 || this.config.Mem0 || {};
21
+ }
22
+
23
+ _apiKey() {
24
+ return process.env.MEM0_API_KEY || this._getConfig().apiKey || '';
25
+ }
26
+
27
+ async _ensureClient() {
28
+ if (this._client) return;
29
+ try {
30
+ const apiKey = this._apiKey();
31
+ if (apiKey) {
32
+ const { MemoryClient } = await import('mem0ai');
33
+ this._client = new MemoryClient({ apiKey });
34
+ this._mode = 'platform';
35
+ } else {
36
+ const { Memory } = await import('mem0ai/oss');
37
+ this._client = new Memory(this._getConfig().oss || {});
38
+ this._mode = 'oss';
39
+ }
40
+ } catch (e) {
41
+ throw new Error('mem0ai paketi yuklu degil. npm install mem0ai');
42
+ }
43
+ }
44
+
45
+ async add(userId, content, metadata = {}) {
46
+ await this._ensureClient();
47
+ const uid = userId || 'default';
48
+ if (this._mode === 'platform') {
49
+ const messages = [{ role: 'user', content }];
50
+ await this._client.add(messages, { userId: uid, metadata });
51
+ } else {
52
+ const messages = [
53
+ { role: 'user', content },
54
+ { role: 'assistant', content: 'Bilgi kaydedildi.' },
55
+ ];
56
+ await this._client.add(messages, { userId: uid, metadata });
57
+ }
58
+ return { success: true, id: content, message: 'Mem0 memory added', provider: 'mem0' };
59
+ }
60
+
61
+ async search(query, options = {}) {
62
+ await this._ensureClient();
63
+ const uid = options.userId;
64
+ const filters = uid ? { userId: uid } : {};
65
+ if (options.limit) filters.limit = options.limit;
66
+ try {
67
+ const results = await this._client.search(query, { filters });
68
+ return { success: true, results: (results.results || results || []).map(r => ({
69
+ id: r.id || r.memory,
70
+ content: r.memory || r.content || '',
71
+ score: r.score || 0,
72
+ metadata: r.metadata || {},
73
+ })) };
74
+ } catch (e) {
75
+ return { success: false, error: e.message, results: [] };
76
+ }
77
+ }
78
+
79
+ async list(userId) {
80
+ await this._ensureClient();
81
+ const uid = userId || 'default';
82
+ try {
83
+ const memories = await this._client.getAll({ userId: uid });
84
+ return {
85
+ success: true,
86
+ memories: (memories || []).map(r => ({
87
+ id: r.id || r.memory,
88
+ content: r.memory || r.content || '',
89
+ score: r.score || 0,
90
+ metadata: r.metadata || {},
91
+ })),
92
+ };
93
+ } catch (e) {
94
+ return { success: false, error: e.message, memories: [] };
95
+ }
96
+ }
97
+
98
+ async remove(id) {
99
+ await this._ensureClient();
100
+ try {
101
+ await this._client.delete(id);
102
+ return { success: true, message: 'Mem0 memory removed' };
103
+ } catch (e) {
104
+ return { success: false, error: e.message };
105
+ }
106
+ }
107
+
108
+ async clear(userId) {
109
+ await this._ensureClient();
110
+ const uid = userId || 'default';
111
+ try {
112
+ await this._client.deleteAll({ userId: uid });
113
+ return { success: true, message: `Mem0 memory cleared for ${uid}` };
114
+ } catch (e) {
115
+ return { success: false, error: e.message };
116
+ }
117
+ }
118
+ }
119
+
120
+ registerProvider('mem0', Mem0MemoryProvider);
121
+ module.exports = Mem0MemoryProvider;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * rest-memory.js — Generic REST API memory provider
3
+ *
4
+ * Configure via config:
5
+ * memoryProvider: "holographic" | "hindsight" | "honcho" | "openviking" | "retaindb" | "byterover"
6
+ * <providerName>: { baseUrl: "...", apiKey: "...", ... }
7
+ *
8
+ * Expects REST endpoints:
9
+ * POST /memories — add { userId, content, metadata }
10
+ * POST /memories/search — search { query, userId, limit }
11
+ * GET /memories/:userId — list
12
+ * DELETE /memories/:id — remove
13
+ * DELETE /memories/:userId/clear — clear
14
+ */
15
+
16
+ const https = require('https');
17
+ const http = require('http');
18
+ const { MemoryProvider, registerProvider } = require('../utils/memory-provider');
19
+
20
+ const PROVIDERS = ['holographic', 'hindsight', 'honcho', 'openviking', 'retaindb', 'byterover'];
21
+
22
+ class RestMemoryProvider extends MemoryProvider {
23
+ constructor(config = {}) {
24
+ super(config);
25
+ this.name = config._providerName || 'rest';
26
+ this._providerConfig = config[this.name] || {};
27
+ }
28
+
29
+ _baseUrl() {
30
+ return this._providerConfig.baseUrl || process.env[`${this.name.toUpperCase()}_URL`] || `http://localhost:3000`;
31
+ }
32
+
33
+ _apiKey() {
34
+ return this._providerConfig.apiKey || process.env[`${this.name.toUpperCase()}_API_KEY`] || '';
35
+ }
36
+
37
+ _headers() {
38
+ const h = { 'Content-Type': 'application/json' };
39
+ const key = this._apiKey();
40
+ if (key) h['Authorization'] = 'Bearer ' + key;
41
+ return h;
42
+ }
43
+
44
+ _request(method, path, body) {
45
+ return new Promise((resolve, reject) => {
46
+ const base = this._baseUrl().replace(/\/+$/, '');
47
+ const url = new URL(base + path);
48
+ const mod = url.protocol === 'https:' ? https : http;
49
+ const opts = {
50
+ method,
51
+ hostname: url.hostname,
52
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
53
+ path: url.pathname + url.search,
54
+ headers: this._headers(),
55
+ timeout: 15000,
56
+ };
57
+ const req = mod.request(opts, (res) => {
58
+ let data = '';
59
+ res.on('data', c => data += c);
60
+ res.on('end', () => {
61
+ try { resolve(JSON.parse(data)); } catch { resolve(data); }
62
+ });
63
+ });
64
+ req.on('error', reject);
65
+ req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
66
+ if (body) req.write(JSON.stringify(body));
67
+ req.end();
68
+ });
69
+ }
70
+
71
+ async add(userId, content, metadata = {}) {
72
+ try {
73
+ const res = await this._request('POST', '/memories', { userId: userId || 'default', content, metadata });
74
+ return { success: true, id: res.id || content, message: `${this.name} memory added`, provider: this.name };
75
+ } catch (e) {
76
+ return { success: false, error: e.message };
77
+ }
78
+ }
79
+
80
+ async search(query, options = {}) {
81
+ try {
82
+ const res = await this._request('POST', '/memories/search', { query, userId: options.userId, limit: options.limit || 10 });
83
+ const results = (res.results || res || []).map(r => ({
84
+ id: r.id,
85
+ content: r.content || r.memory || '',
86
+ score: r.score || 0,
87
+ metadata: r.metadata || {},
88
+ }));
89
+ return { success: true, results };
90
+ } catch (e) {
91
+ return { success: false, error: e.message, results: [] };
92
+ }
93
+ }
94
+
95
+ async list(userId) {
96
+ try {
97
+ const uid = userId || 'default';
98
+ const res = await this._request('GET', `/memories/${encodeURIComponent(uid)}`);
99
+ const memories = (res.memories || res.results || res || []).map(r => ({
100
+ id: r.id,
101
+ content: r.content || r.memory || '',
102
+ score: r.score || 0,
103
+ metadata: r.metadata || {},
104
+ }));
105
+ return { success: true, memories };
106
+ } catch (e) {
107
+ return { success: false, error: e.message, memories: [] };
108
+ }
109
+ }
110
+
111
+ async remove(id) {
112
+ try {
113
+ await this._request('DELETE', `/memories/${encodeURIComponent(id)}`);
114
+ return { success: true, message: `${this.name} memory removed` };
115
+ } catch (e) {
116
+ return { success: false, error: e.message };
117
+ }
118
+ }
119
+
120
+ async clear(userId) {
121
+ try {
122
+ const uid = userId || 'default';
123
+ await this._request('DELETE', `/memories/${encodeURIComponent(uid)}/clear`);
124
+ return { success: true, message: `${this.name} memory cleared for ${uid}` };
125
+ } catch (e) {
126
+ return { success: false, error: e.message };
127
+ }
128
+ }
129
+ }
130
+
131
+ // Register all 6 provider names with the same RestMemoryProvider class
132
+ for (const name of PROVIDERS) {
133
+ const factory = class extends RestMemoryProvider {
134
+ constructor(config) {
135
+ super({ ...config, _providerName: name });
136
+ }
137
+ };
138
+ registerProvider(name, factory);
139
+ }
140
+
141
+ module.exports = RestMemoryProvider;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * supermemory-memory.js — Supermemory memory provider
3
+ *
4
+ * Uses the supermemory npm package.
5
+ * Requires SUPERMEMORY_API_KEY env var or config.supermemory.apiKey.
6
+ * npm install supermemory
7
+ */
8
+
9
+ const { MemoryProvider, registerProvider } = require('../utils/memory-provider');
10
+
11
+ class SupermemoryMemoryProvider extends MemoryProvider {
12
+ constructor(config = {}) {
13
+ super(config);
14
+ this.name = 'supermemory';
15
+ this._client = null;
16
+ }
17
+
18
+ _getConfig() {
19
+ return this.config.supermemory || this.config.Supermemory || {};
20
+ }
21
+
22
+ _apiKey() {
23
+ return process.env.SUPERMEMORY_API_KEY || this._getConfig().apiKey || '';
24
+ }
25
+
26
+ async _ensureClient() {
27
+ if (this._client) return;
28
+ try {
29
+ const Supermemory = (await import('supermemory')).default;
30
+ this._client = new Supermemory({
31
+ apiKey: this._apiKey() || undefined,
32
+ ...this._getConfig().options,
33
+ });
34
+ } catch (e) {
35
+ throw new Error('supermemory paketi yuklu degil. npm install supermemory');
36
+ }
37
+ }
38
+
39
+ async add(userId, content, metadata = {}) {
40
+ await this._ensureClient();
41
+ const tag = `user_${(userId || 'default').toLowerCase()}`;
42
+ await this._client.add({
43
+ content,
44
+ containerTags: [tag],
45
+ ...metadata,
46
+ });
47
+ return { success: true, id: content, message: 'Supermemory added', provider: 'supermemory' };
48
+ }
49
+
50
+ async search(query, options = {}) {
51
+ await this._ensureClient();
52
+ const tag = options.userId ? `user_${options.userId.toLowerCase()}` : undefined;
53
+ try {
54
+ const response = await this._client.search.documents({
55
+ q: query,
56
+ ...(tag ? { containerTags: [tag] } : {}),
57
+ limit: options.limit || 10,
58
+ });
59
+ return {
60
+ success: true,
61
+ results: (response.results || []).map(r => ({
62
+ id: r.id,
63
+ content: r.content || '',
64
+ score: r.score || 0,
65
+ metadata: r.metadata || {},
66
+ })),
67
+ };
68
+ } catch (e) {
69
+ return { success: false, error: e.message, results: [] };
70
+ }
71
+ }
72
+
73
+ async list(userId) {
74
+ await this._ensureClient();
75
+ const tag = `user_${(userId || 'default').toLowerCase()}`;
76
+ try {
77
+ const docs = await this._client.documents.list({ containerTags: [tag] });
78
+ return {
79
+ success: true,
80
+ memories: (docs.results || docs || []).map(r => ({
81
+ id: r.id,
82
+ content: r.content || '',
83
+ score: 0,
84
+ metadata: r.metadata || {},
85
+ })),
86
+ };
87
+ } catch (e) {
88
+ return { success: false, error: e.message, memories: [] };
89
+ }
90
+ }
91
+
92
+ async remove(id) {
93
+ await this._ensureClient();
94
+ try {
95
+ await this._client.documents.delete({ docId: id });
96
+ return { success: true, message: 'Supermemory removed' };
97
+ } catch (e) {
98
+ return { success: false, error: e.message };
99
+ }
100
+ }
101
+
102
+ async clear(userId) {
103
+ const tag = `user_${(userId || 'default').toLowerCase()}`;
104
+ try {
105
+ const listed = await this.list(userId);
106
+ for (const mem of listed.memories || []) {
107
+ try { await this.remove(mem.id); } catch {}
108
+ }
109
+ return { success: true, message: `Supermemory cleared for ${tag}` };
110
+ } catch (e) {
111
+ return { success: false, error: e.message };
112
+ }
113
+ }
114
+ }
115
+
116
+ registerProvider('supermemory', SupermemoryMemoryProvider);
117
+ module.exports = SupermemoryMemoryProvider;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * memory_provider — Switchable memory backend tool
3
+ *
4
+ * Uses the memory provider abstraction. Default: file (same as memory_write).
5
+ * Switch provider via config: memoryProvider: "mem0" | "supermemory" | ...
6
+ * Or env: NATURECO_MEMORY_PROVIDER=mem0
7
+ *
8
+ * Actions:
9
+ * add — save a memory
10
+ * search — semantic/keyword search
11
+ * list — list all memories for a user
12
+ * remove — delete a memory
13
+ * clear — clear all memories for a user
14
+ * status — show active provider info
15
+ */
16
+
17
+ const { getActiveProvider, getProviderNames } = require('../utils/memory-provider');
18
+ const loadConfig = () => {
19
+ try { return JSON.parse(require('fs').readFileSync(require('path').join(require('os').homedir(), '.natureco', 'config.json'), 'utf8')); } catch { return {}; }
20
+ };
21
+
22
+ const name = 'memory_provider';
23
+ const description = 'Unified memory backend with pluggable providers. add/search/list/remove/clear/status. Default is file-based. Switch via NATURECO_MEMORY_PROVIDER env or config.memoryProvider.';
24
+ const parameters = {
25
+ type: 'object',
26
+ properties: {
27
+ action: {
28
+ type: 'string',
29
+ enum: ['add', 'search', 'list', 'remove', 'clear', 'status'],
30
+ description: 'Operation: add, search, list, remove, clear, or status',
31
+ },
32
+ content: { type: 'string', description: 'Memory content to add, or query for search' },
33
+ userId: { type: 'string', description: 'User identifier' },
34
+ metadata: { type: 'object', description: 'Additional metadata (category, score, etc.)' },
35
+ limit: { type: 'number', description: 'Max results for search/list' },
36
+ provider: { type: 'string', description: 'Override provider name (default: config or file)' },
37
+ },
38
+ required: ['action'],
39
+ };
40
+
41
+ async function execute(params) {
42
+ const cfg = loadConfig();
43
+ const providerName = params.provider || process.env.NATURECO_MEMORY_PROVIDER || cfg.memoryProvider;
44
+ let Provider;
45
+ if (providerName) {
46
+ const { getProvider } = require('../utils/memory-provider');
47
+ Provider = getProvider(providerName);
48
+ }
49
+ if (!Provider) {
50
+ // Load default (file)
51
+ Provider = getActiveProvider(cfg);
52
+ }
53
+ const provider = new Provider(cfg);
54
+ const userId = params.userId || cfg.userName || 'default';
55
+
56
+ switch (params.action) {
57
+ case 'add':
58
+ if (!params.content) return JSON.stringify({ success: false, error: 'content required for add' });
59
+ return provider.add(userId, params.content, params.metadata || {});
60
+
61
+ case 'search':
62
+ if (!params.content) return JSON.stringify({ success: false, error: 'content (query) required for search' });
63
+ return provider.search(params.content, { userId, limit: params.limit || 10 });
64
+
65
+ case 'list':
66
+ return provider.list(userId);
67
+
68
+ case 'remove':
69
+ if (!params.content) return JSON.stringify({ success: false, error: 'content (id) required for remove' });
70
+ return provider.remove(params.content);
71
+
72
+ case 'clear':
73
+ return provider.clear(userId);
74
+
75
+ case 'status': {
76
+ const avail = getProviderNames().sort();
77
+ return {
78
+ active: provider.name,
79
+ available: avail,
80
+ userId,
81
+ config: cfg.memoryProvider || '(not set, using default)',
82
+ env: process.env.NATURECO_MEMORY_PROVIDER || '(not set)',
83
+ };
84
+ }
85
+
86
+ default:
87
+ return JSON.stringify({ success: false, error: `Unknown action: ${params.action}` });
88
+ }
89
+ }
90
+
91
+ module.exports = { name, description, parameters, execute };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * memory-provider.js — Abstract memory backend interface + registry
3
+ *
4
+ * All memory providers extend the MemoryProvider base class.
5
+ * Register a provider via registerProvider(name, class) and
6
+ * switch with memory config or environment variable.
7
+ */
8
+
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ const PROVIDER_CONFIG_KEY = 'memoryProvider';
13
+ const DEFAULT_PROVIDER = 'file';
14
+
15
+ // Registry: name -> class
16
+ const _registry = new Map();
17
+
18
+ function registerProvider(name, providerClass) {
19
+ _registry.set(name, providerClass);
20
+ }
21
+
22
+ function getProviderNames() {
23
+ return Array.from(_registry.keys());
24
+ }
25
+
26
+ function getProvider(name) {
27
+ const Cls = _registry.get(name);
28
+ if (!Cls) return null;
29
+ return Cls;
30
+ }
31
+
32
+ /**
33
+ * Resolve active provider from config + env + defaults.
34
+ * Priority: env NATURECO_MEMORY_PROVIDER > config.memoryProvider > 'file'
35
+ */
36
+ function resolveProviderConfig(cfg) {
37
+ const env = process.env.NATURECO_MEMORY_PROVIDER || '';
38
+ if (env && _registry.has(env)) return env;
39
+ if (cfg && cfg[PROVIDER_CONFIG_KEY] && _registry.has(cfg[PROVIDER_CONFIG_KEY])) {
40
+ return cfg[PROVIDER_CONFIG_KEY];
41
+ }
42
+ return DEFAULT_PROVIDER;
43
+ }
44
+
45
+ /**
46
+ * Get an instance of the active memory provider.
47
+ * Provider classes should be singletons — call this once, cache the result.
48
+ */
49
+ function getActiveProvider(cfg) {
50
+ const name = resolveProviderConfig(cfg);
51
+ const Cls = getProvider(name);
52
+ if (!Cls) return getProvider(DEFAULT_PROVIDER);
53
+ return Cls;
54
+ }
55
+
56
+ /**
57
+ * MemoryProvider base class.
58
+ *
59
+ * Subclasses must implement:
60
+ * add(userId, content, metadata) → { success, id, message }
61
+ * search(query, options) → { success, results: [{id, content, score, metadata}] }
62
+ * list(userId) → { success, memories: [{id, content, metadata}] }
63
+ * remove(id) → { success, message }
64
+ * clear(userId) → { success, message }
65
+ */
66
+ class MemoryProvider {
67
+ constructor(config = {}) {
68
+ this.config = config;
69
+ this.name = 'base';
70
+ }
71
+
72
+ async add(userId, content, metadata = {}) {
73
+ throw new Error('Not implemented: add');
74
+ }
75
+
76
+ async search(query, options = {}) {
77
+ throw new Error('Not implemented: search');
78
+ }
79
+
80
+ async list(userId) {
81
+ throw new Error('Not implemented: list');
82
+ }
83
+
84
+ async remove(id) {
85
+ throw new Error('Not implemented: remove');
86
+ }
87
+
88
+ async clear(userId) {
89
+ throw new Error('Not implemented: clear');
90
+ }
91
+
92
+ /**
93
+ * Extract a user ID from args or config
94
+ */
95
+ _userId(args) {
96
+ return args?.userId || args?.username || args?.user || this.config?.defaultUserId || 'default';
97
+ }
98
+ }
99
+
100
+ module.exports = {
101
+ MemoryProvider,
102
+ registerProvider,
103
+ getProviderNames,
104
+ getProvider,
105
+ resolveProviderConfig,
106
+ getActiveProvider,
107
+ PROVIDER_CONFIG_KEY,
108
+ DEFAULT_PROVIDER,
109
+ };