@agentmemory/agentmemory 0.7.9 → 0.8.1

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.mjs CHANGED
@@ -40,10 +40,16 @@ function loadEnvFile() {
40
40
  }
41
41
  function detectProvider(env) {
42
42
  const maxTokens = parseInt(env["MAX_TOKENS"] || "4096", 10);
43
+ if (env["MINIMAX_API_KEY"]) return {
44
+ provider: "minimax",
45
+ model: env["MINIMAX_MODEL"] || "MiniMax-M2.7",
46
+ maxTokens
47
+ };
43
48
  if (env["ANTHROPIC_API_KEY"]) return {
44
49
  provider: "anthropic",
45
50
  model: env["ANTHROPIC_MODEL"] || "claude-sonnet-4-20250514",
46
- maxTokens
51
+ maxTokens,
52
+ baseURL: env["ANTHROPIC_BASE_URL"]
47
53
  };
48
54
  if (env["GEMINI_API_KEY"]) return {
49
55
  provider: "gemini",
@@ -157,7 +163,8 @@ const VALID_PROVIDERS = new Set([
157
163
  "anthropic",
158
164
  "gemini",
159
165
  "openrouter",
160
- "agent-sdk"
166
+ "agent-sdk",
167
+ "minimax"
161
168
  ]);
162
169
  function loadFallbackConfig() {
163
170
  return { providers: (getMergedEnv()["FALLBACK_PROVIDERS"] || "").split(",").map((p) => p.trim()).filter((p) => Boolean(p) && VALID_PROVIDERS.has(p)) };
@@ -196,8 +203,11 @@ var AnthropicProvider = class {
196
203
  client;
197
204
  model;
198
205
  maxTokens;
199
- constructor(apiKey, model, maxTokens) {
200
- this.client = new Anthropic({ apiKey });
206
+ constructor(apiKey, model, maxTokens, baseURL) {
207
+ this.client = new Anthropic({
208
+ apiKey,
209
+ ...baseURL ? { baseURL } : {}
210
+ });
201
211
  this.model = model;
202
212
  this.maxTokens = maxTokens;
203
213
  }
@@ -220,6 +230,67 @@ var AnthropicProvider = class {
220
230
  }
221
231
  };
222
232
 
233
+ //#endregion
234
+ //#region src/providers/minimax.ts
235
+ /**
236
+ * MiniMax provider using raw fetch to call MiniMax's Anthropic-compatible API.
237
+ *
238
+ * The Anthropic SDK automatically injects `x-stainless-*` headers that MiniMax
239
+ * rejects with 403. This provider bypasses the SDK and calls the API directly.
240
+ *
241
+ * Required env vars:
242
+ * MINIMAX_API_KEY — your MiniMax API key
243
+ * MINIMAX_MODEL — model name (default: MiniMax-M2.7)
244
+ * MAX_TOKENS — max output tokens (default: 800; MiniMax-M2.7 needs ≤800)
245
+ *
246
+ * Optional:
247
+ * MINIMAX_BASE_URL — base URL without path (default: https://api.minimaxi.com/anthropic)
248
+ */
249
+ var MinimaxProvider = class {
250
+ name = "minimax";
251
+ apiKey;
252
+ model;
253
+ maxTokens;
254
+ baseUrl;
255
+ constructor(apiKey, model, maxTokens) {
256
+ this.apiKey = apiKey;
257
+ this.model = model;
258
+ this.maxTokens = maxTokens;
259
+ this.baseUrl = process.env["MINIMAX_BASE_URL"] || "https://api.minimaxi.com/anthropic";
260
+ }
261
+ async compress(systemPrompt, userPrompt) {
262
+ return this.call(systemPrompt, userPrompt);
263
+ }
264
+ async summarize(systemPrompt, userPrompt) {
265
+ return this.call(systemPrompt, userPrompt);
266
+ }
267
+ async call(systemPrompt, userPrompt) {
268
+ const url = `${this.baseUrl}/v1/messages`;
269
+ const response = await fetch(url, {
270
+ method: "POST",
271
+ headers: {
272
+ "Content-Type": "application/json",
273
+ "x-api-key": this.apiKey,
274
+ "anthropic-version": "2023-06-01"
275
+ },
276
+ body: JSON.stringify({
277
+ model: this.model,
278
+ max_tokens: this.maxTokens,
279
+ system: systemPrompt,
280
+ messages: [{
281
+ role: "user",
282
+ content: userPrompt
283
+ }]
284
+ })
285
+ });
286
+ if (!response.ok) {
287
+ const text = await response.text();
288
+ throw new Error(`MiniMax API error ${response.status}: ${text}`);
289
+ }
290
+ return ((await response.json()).content?.find((b) => b.type === "text"))?.text ?? "";
291
+ }
292
+ };
293
+
223
294
  //#endregion
224
295
  //#region src/providers/openrouter.ts
225
296
  var OpenRouterProvider = class {
@@ -647,7 +718,8 @@ function createFallbackProvider(config, fallbackConfig) {
647
718
  }
648
719
  function createBaseProvider(config) {
649
720
  switch (config.provider) {
650
- case "anthropic": return new AnthropicProvider(requireEnvVar("ANTHROPIC_API_KEY"), config.model, config.maxTokens);
721
+ case "minimax": return new MinimaxProvider(requireEnvVar("MINIMAX_API_KEY"), config.model, config.maxTokens);
722
+ case "anthropic": return new AnthropicProvider(requireEnvVar("ANTHROPIC_API_KEY"), config.model, config.maxTokens, config.baseURL);
651
723
  case "gemini": return new OpenRouterProvider(requireEnvVar("GEMINI_API_KEY"), config.model, config.maxTokens, "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions");
652
724
  case "openrouter": return new OpenRouterProvider(requireEnvVar("OPENROUTER_API_KEY"), config.model, config.maxTokens, "https://openrouter.ai/api/v1/chat/completions");
653
725
  default: return new AgentSDKProvider();
@@ -2123,23 +2195,56 @@ function registerSearchFunction(sdk, kv) {
2123
2195
  }, async (data) => {
2124
2196
  const ctx = getContext();
2125
2197
  const idx = getSearchIndex();
2198
+ if (typeof data?.query !== "string" || !data.query.trim()) throw new Error("mem::search: query must be a non-empty string");
2199
+ const query = data.query.trim();
2200
+ const MAX_LIMIT = 100;
2201
+ let effectiveLimit = 20;
2202
+ if (data.limit !== void 0) {
2203
+ if (!Number.isInteger(data.limit) || data.limit < 1) throw new Error("mem::search: limit must be a positive integer");
2204
+ effectiveLimit = Math.min(data.limit, MAX_LIMIT);
2205
+ }
2206
+ const projectFilter = typeof data.project === "string" && data.project.length > 0 ? data.project : void 0;
2207
+ const cwdFilter = typeof data.cwd === "string" && data.cwd.length > 0 ? data.cwd : void 0;
2126
2208
  if (idx.size === 0) {
2127
2209
  const count = await rebuildIndex(kv);
2128
2210
  ctx.logger.info("Search index rebuilt", { entries: count });
2129
2211
  }
2130
- const results = idx.search(data.query, data.limit || 20);
2131
- const enriched = [];
2212
+ const filtering = !!(projectFilter || cwdFilter);
2213
+ const fetchLimit = filtering ? Math.max(effectiveLimit * 10, 100) : effectiveLimit;
2214
+ const results = idx.search(query, fetchLimit);
2215
+ const sessionCache = /* @__PURE__ */ new Map();
2216
+ const loadSession = async (sessionId) => {
2217
+ if (sessionCache.has(sessionId)) return sessionCache.get(sessionId);
2218
+ const s = await kv.get(KV.sessions, sessionId);
2219
+ sessionCache.set(sessionId, s ?? null);
2220
+ return s ?? null;
2221
+ };
2222
+ const candidates = [];
2132
2223
  for (const r of results) {
2133
- const obs = await kv.get(KV.observations(r.sessionId), r.obsId);
2224
+ if (candidates.length >= effectiveLimit) break;
2225
+ if (filtering) {
2226
+ const s = await loadSession(r.sessionId);
2227
+ if (!s) continue;
2228
+ if (projectFilter && s.project !== projectFilter) continue;
2229
+ if (cwdFilter && s.cwd !== cwdFilter) continue;
2230
+ }
2231
+ candidates.push(r);
2232
+ }
2233
+ const obsResults = await Promise.all(candidates.map((r) => kv.get(KV.observations(r.sessionId), r.obsId)));
2234
+ const enriched = [];
2235
+ for (let i = 0; i < candidates.length; i++) {
2236
+ const obs = obsResults[i];
2134
2237
  if (obs) enriched.push({
2135
2238
  observation: obs,
2136
- score: r.score,
2137
- sessionId: r.sessionId
2239
+ score: candidates[i].score,
2240
+ sessionId: candidates[i].sessionId
2138
2241
  });
2139
2242
  }
2140
2243
  ctx.logger.info("Search completed", {
2141
- query: data.query,
2142
- results: enriched.length
2244
+ query,
2245
+ results: enriched.length,
2246
+ hasProjectFilter: !!projectFilter,
2247
+ hasCwdFilter: !!cwdFilter
2143
2248
  });
2144
2249
  return { results: enriched };
2145
2250
  });
@@ -3805,7 +3910,7 @@ function registerAutoForgetFunction(sdk, kv) {
3805
3910
 
3806
3911
  //#endregion
3807
3912
  //#region src/version.ts
3808
- const VERSION = "0.7.9";
3913
+ const VERSION = "0.8.1";
3809
3914
 
3810
3915
  //#endregion
3811
3916
  //#region src/functions/export-import.ts
@@ -3910,7 +4015,9 @@ function registerExportImportFunction(sdk, kv) {
3910
4015
  "0.7.5",
3911
4016
  "0.7.6",
3912
4017
  "0.7.7",
3913
- "0.7.9"
4018
+ "0.7.9",
4019
+ "0.8.0",
4020
+ "0.8.1"
3914
4021
  ]).has(importData.version)) return {
3915
4022
  success: false,
3916
4023
  error: `Unsupported export version: ${importData.version}`
@@ -10033,9 +10140,32 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
10033
10140
  sdk.registerFunction({ id: "api::search" }, async (req) => {
10034
10141
  const authErr = checkAuth$1(req, secret);
10035
10142
  if (authErr) return authErr;
10143
+ const body = req.body ?? {};
10144
+ if (typeof body.query !== "string" || !body.query.trim()) return {
10145
+ status_code: 400,
10146
+ body: { error: "query is required and must be a non-empty string" }
10147
+ };
10148
+ if (body.limit !== void 0 && (!Number.isInteger(body.limit) || body.limit < 1)) return {
10149
+ status_code: 400,
10150
+ body: { error: "limit must be a positive integer" }
10151
+ };
10152
+ if (body.project !== void 0 && typeof body.project !== "string") return {
10153
+ status_code: 400,
10154
+ body: { error: "project must be a string" }
10155
+ };
10156
+ if (body.cwd !== void 0 && typeof body.cwd !== "string") return {
10157
+ status_code: 400,
10158
+ body: { error: "cwd must be a string" }
10159
+ };
10160
+ const payload = {
10161
+ query: body.query.trim(),
10162
+ limit: body.limit,
10163
+ project: body.project,
10164
+ cwd: body.cwd
10165
+ };
10036
10166
  return {
10037
10167
  status_code: 200,
10038
- body: await sdk.trigger("mem::search", req.body)
10168
+ body: await sdk.trigger("mem::search", payload)
10039
10169
  };
10040
10170
  });
10041
10171
  sdk.registerTrigger({
@@ -14336,9 +14466,11 @@ function startViewerServer(port, _kv, _sdk, secret, restPort) {
14336
14466
  if (method === "GET" && (pathname === "/" || pathname === "/viewer" || pathname === "/agentmemory/viewer")) {
14337
14467
  const base = dirname(fileURLToPath(import.meta.url));
14338
14468
  const candidates = [
14339
- join(base, "..", "src", "viewer", "index.html"),
14469
+ join(base, "index.html"),
14470
+ join(base, "viewer", "index.html"),
14340
14471
  join(base, "..", "viewer", "index.html"),
14341
- join(base, "viewer", "index.html")
14472
+ join(base, "..", "src", "viewer", "index.html"),
14473
+ join(base, "..", "dist", "viewer", "index.html")
14342
14474
  ];
14343
14475
  for (const p of candidates) try {
14344
14476
  const html = readFileSync(p, "utf-8");