@memtensor/memos-cloud-openclaw-plugin 0.1.10-beta.4 → 0.1.11-beta.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/index.js CHANGED
@@ -5,6 +5,8 @@ import {
5
5
  extractResultData,
6
6
  extractText,
7
7
  formatRecallHookResult,
8
+ isAgentAllowed,
9
+ resolveAgentConfig,
8
10
  searchMemory,
9
11
  stripOpenClawInjectedPrefix,
10
12
  } from "./lib/memos-cloud-api.js";
@@ -53,6 +55,21 @@ function getEffectiveAgentId(cfg, ctx) {
53
55
  return agentId === "main" ? undefined : agentId;
54
56
  }
55
57
 
58
+ export function extractDirectSessionUserId(sessionKey) {
59
+ if (!sessionKey || typeof sessionKey !== "string") return "";
60
+ const parts = sessionKey.split(":");
61
+ const directIndex = parts.lastIndexOf("direct");
62
+ if (directIndex === -1) return "";
63
+ return parts[directIndex + 1] || "";
64
+ }
65
+
66
+ export function resolveMemosUserId(cfg, ctx) {
67
+ const fallback = cfg?.userId || "openclaw-user";
68
+ if (!cfg?.useDirectSessionUserId) return fallback;
69
+ const directUserId = extractDirectSessionUserId(ctx?.sessionKey);
70
+ return directUserId || fallback;
71
+ }
72
+
56
73
  function resolveConversationId(cfg, ctx) {
57
74
  if (cfg.conversationId) return cfg.conversationId;
58
75
  // TODO: consider binding conversation_id directly to OpenClaw sessionId (prefer ctx.sessionId).
@@ -65,7 +82,7 @@ function resolveConversationId(cfg, ctx) {
65
82
  return `${prefix}openclaw-${Date.now()}${dynamicSuffix}${suffix}`;
66
83
  }
67
84
 
68
- function buildSearchPayload(cfg, prompt, ctx) {
85
+ export function buildSearchPayload(cfg, prompt, ctx) {
69
86
  const cleanPrompt = stripOpenClawInjectedPrefix(prompt);
70
87
  const queryRaw = `${cfg.queryPrefix || ""}${cleanPrompt}`;
71
88
  const query =
@@ -74,7 +91,7 @@ function buildSearchPayload(cfg, prompt, ctx) {
74
91
  : queryRaw;
75
92
 
76
93
  const payload = {
77
- user_id: cfg.userId,
94
+ user_id: resolveMemosUserId(cfg, ctx),
78
95
  query,
79
96
  source: MEMOS_SOURCE,
80
97
  };
@@ -113,9 +130,9 @@ function buildSearchPayload(cfg, prompt, ctx) {
113
130
  return payload;
114
131
  }
115
132
 
116
- function buildAddMessagePayload(cfg, messages, ctx) {
133
+ export function buildAddMessagePayload(cfg, messages, ctx) {
117
134
  const payload = {
118
- user_id: cfg.userId,
135
+ user_id: resolveMemosUserId(cfg, ctx),
119
136
  conversation_id: resolveConversationId(cfg, ctx),
120
137
  messages,
121
138
  source: MEMOS_SOURCE,
@@ -326,20 +343,20 @@ async function callRecallFilterModel(cfg, userPrompt, candidatePayload) {
326
343
  };
327
344
 
328
345
  let lastError;
329
- const retries = Number.isFinite(cfg.recallFilterRetries) ? Math.max(0, cfg.recallFilterRetries) : 0;
330
- const timeoutMs = Number.isFinite(cfg.recallFilterTimeoutMs) ? Math.max(1000, cfg.recallFilterTimeoutMs) : 6000;
346
+ const retries = Number.isFinite(cfg.recallFilterRetries) ? Math.max(0, cfg.recallFilterRetries) : 1;
347
+ const timeoutMs = Number.isFinite(cfg.recallFilterTimeoutMs) ? Math.max(1000, cfg.recallFilterTimeoutMs) : 30000;
331
348
 
332
349
  for (let attempt = 0; attempt <= retries; attempt += 1) {
350
+ let timeoutId;
333
351
  try {
334
352
  const controller = new AbortController();
335
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
353
+ timeoutId = setTimeout(() => controller.abort(), timeoutMs);
336
354
  const res = await fetch(`${cfg.recallFilterBaseUrl}/chat/completions`, {
337
355
  method: "POST",
338
356
  headers,
339
357
  body: JSON.stringify(body),
340
358
  signal: controller.signal,
341
359
  });
342
- clearTimeout(timeoutId);
343
360
  if (!res.ok) {
344
361
  throw new Error(`HTTP ${res.status}`);
345
362
  }
@@ -351,10 +368,17 @@ async function callRecallFilterModel(cfg, userPrompt, candidatePayload) {
351
368
  }
352
369
  return parsed;
353
370
  } catch (err) {
354
- lastError = err;
371
+ const isAbort = err?.name === "AbortError" || /aborted/i.test(String(err?.message ?? err));
372
+ lastError = isAbort
373
+ ? new Error(
374
+ `timed out after ${timeoutMs}ms (raise recallFilterTimeoutMs; local LLMs often need 30s+ on cold start)`,
375
+ )
376
+ : err;
355
377
  if (attempt < retries) {
356
378
  await sleep(120 * (attempt + 1));
357
379
  }
380
+ } finally {
381
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
358
382
  }
359
383
  }
360
384
  throw lastError;
@@ -375,7 +399,13 @@ async function maybeFilterRecallData(cfg, data, userPrompt, log) {
375
399
 
376
400
  try {
377
401
  const decision = await callRecallFilterModel(cfg, userPrompt, lists.candidatePayload);
378
- return applyRecallDecision(data, decision, lists);
402
+ const filtered = applyRecallDecision(data, decision, lists);
403
+ log.info?.(
404
+ `[memos-cloud] recall filter applied: memory ${lists.memoryList.length}->${filtered.memory_detail_list?.length ?? 0}, ` +
405
+ `preference ${lists.preferenceList.length}->${filtered.preference_detail_list?.length ?? 0}, ` +
406
+ `tool_memory ${lists.toolList.length}->${filtered.tool_memory_detail_list?.length ?? 0}`,
407
+ );
408
+ return filtered;
379
409
  } catch (err) {
380
410
  log.warn?.(`[memos-cloud] recall filter failed: ${String(err)}`);
381
411
  return cfg.recallFilterFailOpen ? data : { ...data, memory_detail_list: [], preference_detail_list: [], tool_memory_detail_list: [] };
@@ -400,6 +430,15 @@ export default {
400
430
  log.warn?.(`[memos-cloud] No .env found in ${searchPaths}; falling back to process env or plugin config.`);
401
431
  }
402
432
 
433
+ if (cfg.multiAgentMode && cfg.allowedAgents?.length > 0) {
434
+ log.info?.(`[memos-cloud] Multi-agent mode enabled. Allowed agents: [${cfg.allowedAgents.join(", ")}]`);
435
+ }
436
+
437
+ const overrideAgentIds = Object.keys(cfg._agentOverrides || {});
438
+ if (overrideAgentIds.length > 0) {
439
+ log.info?.(`[memos-cloud] Per-agent overrides configured for: [${overrideAgentIds.join(", ")}]`);
440
+ }
441
+
403
442
  if (cfg.conversationSuffixMode === "counter" && cfg.resetOnNew) {
404
443
  if (api.config?.hooks?.internal?.enabled !== true) {
405
444
  log.warn?.("[memos-cloud] command:new hook requires hooks.internal.enabled = true");
@@ -419,24 +458,29 @@ export default {
419
458
  }
420
459
 
421
460
  api.on("before_agent_start", async (event, ctx) => {
422
- if (!cfg.recallEnabled) return;
461
+ if (!isAgentAllowed(cfg, ctx)) {
462
+ log.info?.(`[memos-cloud] recall skipped: agent "${ctx?.agentId}" not in allowedAgents [${cfg.allowedAgents?.join(", ")}]`);
463
+ return;
464
+ }
465
+ const agentCfg = resolveAgentConfig(cfg, ctx?.agentId);
466
+ if (!agentCfg.recallEnabled) return;
423
467
  const userPrompt = stripOpenClawInjectedPrefix(event?.prompt || "");
424
468
  if (!userPrompt || userPrompt.length < 3) return;
425
- if (!cfg.apiKey) {
469
+ if (!agentCfg.apiKey) {
426
470
  warnMissingApiKey(log, "recall");
427
471
  return;
428
472
  }
429
473
 
430
474
  try {
431
- const payload = buildSearchPayload(cfg, userPrompt, ctx);
432
- const result = await searchMemory(cfg, payload);
475
+ const payload = buildSearchPayload(agentCfg, userPrompt, ctx);
476
+ const result = await searchMemory(agentCfg, payload);
433
477
  const resultData = extractResultData(result);
434
478
  if (!resultData) return;
435
- const filteredData = await maybeFilterRecallData(cfg, resultData, userPrompt, log);
479
+ const filteredData = await maybeFilterRecallData(agentCfg, resultData, userPrompt, log);
436
480
  const hookResult = formatRecallHookResult({ data: filteredData }, {
437
481
  wrapTagBlocks: true,
438
482
  relativity: payload.relativity,
439
- maxItemChars: cfg.maxItemChars,
483
+ maxItemChars: agentCfg.maxItemChars,
440
484
  });
441
485
  if (!hookResult.appendSystemContext && !hookResult.prependContext) return;
442
486
 
@@ -447,29 +491,34 @@ export default {
447
491
  });
448
492
 
449
493
  api.on("agent_end", async (event, ctx) => {
450
- if (!cfg.addEnabled) return;
494
+ if (!isAgentAllowed(cfg, ctx)) {
495
+ log.info?.(`[memos-cloud] add skipped: agent "${ctx?.agentId}" not in allowedAgents [${cfg.allowedAgents?.join(", ")}]`);
496
+ return;
497
+ }
498
+ const agentCfg = resolveAgentConfig(cfg, ctx?.agentId);
499
+ if (!agentCfg.addEnabled) return;
451
500
  if (!event?.success || !event?.messages?.length) return;
452
- if (!cfg.apiKey) {
501
+ if (!agentCfg.apiKey) {
453
502
  warnMissingApiKey(log, "add");
454
503
  return;
455
504
  }
456
505
 
457
506
  const now = Date.now();
458
- if (cfg.throttleMs && now - lastCaptureTime < cfg.throttleMs) {
507
+ if (agentCfg.throttleMs && now - lastCaptureTime < agentCfg.throttleMs) {
459
508
  return;
460
509
  }
461
510
  lastCaptureTime = now;
462
511
 
463
512
  try {
464
513
  const messages =
465
- cfg.captureStrategy === "full_session"
466
- ? pickFullSessionMessages(event.messages, cfg)
467
- : pickLastTurnMessages(event.messages, cfg);
514
+ agentCfg.captureStrategy === "full_session"
515
+ ? pickFullSessionMessages(event.messages, agentCfg)
516
+ : pickLastTurnMessages(event.messages, agentCfg);
468
517
 
469
518
  if (!messages.length) return;
470
519
 
471
- const payload = buildAddMessagePayload(cfg, messages, ctx);
472
- await addMessage(cfg, payload);
520
+ const payload = buildAddMessagePayload(agentCfg, messages, ctx);
521
+ await addMessage(agentCfg, payload);
473
522
  } catch (err) {
474
523
  log.warn?.(`[memos-cloud] add failed: ${String(err)}`);
475
524
  }
@@ -157,6 +157,28 @@ function parseNumber(value, fallback) {
157
157
  return Number.isFinite(n) ? n : fallback;
158
158
  }
159
159
 
160
+ function parseStringArray(value) {
161
+ if (!value) return [];
162
+ if (Array.isArray(value)) return value.map((v) => String(v).trim()).filter(Boolean);
163
+ return String(value)
164
+ .split(",")
165
+ .map((s) => s.trim().replace(/^["']|["']$/g, ""))
166
+ .filter(Boolean);
167
+ }
168
+
169
+ function parseJsonObject(value) {
170
+ if (!value || typeof value !== "string") return null;
171
+ try {
172
+ const parsed = JSON.parse(value);
173
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
174
+ return parsed;
175
+ }
176
+ } catch {
177
+ // ignore parse error
178
+ }
179
+ return null;
180
+ }
181
+
160
182
  export function buildConfig(pluginConfig = {}) {
161
183
  const cfg = pluginConfig ?? {};
162
184
 
@@ -184,6 +206,10 @@ export function buildConfig(pluginConfig = {}) {
184
206
  parseBool(loadEnvVar("MEMOS_MULTI_AGENT_MODE"), false),
185
207
  );
186
208
 
209
+ const allowedAgents = parseStringArray(
210
+ cfg.allowedAgents ?? loadEnvVar("MEMOS_ALLOWED_AGENTS"),
211
+ );
212
+
187
213
  const recallFilterEnabled = parseBool(
188
214
  cfg.recallFilterEnabled,
189
215
  parseBool(loadEnvVar("MEMOS_RECALL_FILTER_ENABLED"), false),
@@ -200,6 +226,10 @@ export function buildConfig(pluginConfig = {}) {
200
226
  ? parseBool(loadEnvVar("MEMOS_INCLUDE_ASSISTANT"), true)
201
227
  : cfg.includeAssistant !== false;
202
228
  const maxMessageChars = cfg.maxMessageChars ?? parseNumber(loadEnvVar("MEMOS_MAX_MESSAGE_CHARS"), 20000);
229
+ const useDirectSessionUserId = parseBool(
230
+ cfg.useDirectSessionUserId,
231
+ parseBool(loadEnvVar("MEMOS_USE_DIRECT_SESSION_USER_ID"), false),
232
+ );
203
233
 
204
234
  return {
205
235
  baseUrl: baseUrl.replace(/\/+$/, ""),
@@ -209,6 +239,7 @@ export function buildConfig(pluginConfig = {}) {
209
239
  conversationIdPrefix,
210
240
  conversationIdSuffix,
211
241
  conversationSuffixMode,
242
+ useDirectSessionUserId,
212
243
  recallGlobal,
213
244
  resetOnNew,
214
245
  envFileStatus: getEnvFileStatus(),
@@ -230,15 +261,16 @@ export function buildConfig(pluginConfig = {}) {
230
261
  return v ? parseFloat(v) : 0.45;
231
262
  })()),
232
263
  filter: cfg.filter,
233
- knowledgebaseIds: cfg.knowledgebaseIds ?? [],
234
- tags: cfg.tags ?? ["openclaw"],
264
+ knowledgebaseIds: cfg.knowledgebaseIds ?? (loadEnvVar("MEMOS_KNOWLEDGEBASE_IDS") ? parseStringArray(loadEnvVar("MEMOS_KNOWLEDGEBASE_IDS")) : []),
265
+ tags: cfg.tags ?? (loadEnvVar("MEMOS_TAGS") ? parseStringArray(loadEnvVar("MEMOS_TAGS")) : ["openclaw"]),
235
266
  info: cfg.info ?? {},
236
267
  agentId: cfg.agentId,
237
268
  appId: cfg.appId,
238
269
  allowPublic: cfg.allowPublic ?? false,
239
- allowKnowledgebaseIds: cfg.allowKnowledgebaseIds ?? [],
270
+ allowKnowledgebaseIds: cfg.allowKnowledgebaseIds ?? (loadEnvVar("MEMOS_ALLOW_KNOWLEDGEBASE_IDS") ? parseStringArray(loadEnvVar("MEMOS_ALLOW_KNOWLEDGEBASE_IDS")) : []),
240
271
  asyncMode,
241
272
  multiAgentMode,
273
+ allowedAgents,
242
274
  recallFilterEnabled,
243
275
  recallFilterBaseUrl:
244
276
  (cfg.recallFilterBaseUrl ?? loadEnvVar("MEMOS_RECALL_FILTER_BASE_URL") ?? "").replace(/\/+$/, ""),
@@ -246,9 +278,9 @@ export function buildConfig(pluginConfig = {}) {
246
278
  recallFilterModel: cfg.recallFilterModel ?? loadEnvVar("MEMOS_RECALL_FILTER_MODEL") ?? "",
247
279
  recallFilterTimeoutMs: parseNumber(
248
280
  cfg.recallFilterTimeoutMs ?? loadEnvVar("MEMOS_RECALL_FILTER_TIMEOUT_MS"),
249
- 6000,
281
+ 30000,
250
282
  ),
251
- recallFilterRetries: parseNumber(cfg.recallFilterRetries ?? loadEnvVar("MEMOS_RECALL_FILTER_RETRIES"), 0),
283
+ recallFilterRetries: parseNumber(cfg.recallFilterRetries ?? loadEnvVar("MEMOS_RECALL_FILTER_RETRIES"), 1),
252
284
  recallFilterCandidateLimit:
253
285
  parseNumber(cfg.recallFilterCandidateLimit ?? loadEnvVar("MEMOS_RECALL_FILTER_CANDIDATE_LIMIT"), 30),
254
286
  recallFilterMaxItemChars:
@@ -257,9 +289,35 @@ export function buildConfig(pluginConfig = {}) {
257
289
  timeoutMs: cfg.timeoutMs ?? 5000,
258
290
  retries: cfg.retries ?? 1,
259
291
  throttleMs,
292
+ _agentOverrides: cfg.agentOverrides ?? parseJsonObject(loadEnvVar("MEMOS_AGENT_OVERRIDES")) ?? {},
260
293
  };
261
294
  }
262
295
 
296
+ const AGENT_OVERRIDABLE_KEYS = [
297
+ "knowledgebaseIds", "memoryLimitNumber", "preferenceLimitNumber",
298
+ "includePreference", "includeToolMemory", "toolMemoryLimitNumber",
299
+ "relativity",
300
+ "recallEnabled", "addEnabled", "captureStrategy", "queryPrefix",
301
+ "maxItemChars", "maxMessageChars", "includeAssistant",
302
+ "recallGlobal", "recallFilterEnabled", "recallFilterModel",
303
+ "recallFilterBaseUrl", "recallFilterApiKey",
304
+ "allowKnowledgebaseIds", "tags", "throttleMs",
305
+ ];
306
+
307
+ export function resolveAgentConfig(baseCfg, agentId) {
308
+ if (!agentId || !baseCfg._agentOverrides) return baseCfg;
309
+ const overrides = baseCfg._agentOverrides[agentId];
310
+ if (!overrides || typeof overrides !== "object") return baseCfg;
311
+
312
+ const merged = { ...baseCfg };
313
+ for (const key of AGENT_OVERRIDABLE_KEYS) {
314
+ if (key in overrides) {
315
+ merged[key] = overrides[key];
316
+ }
317
+ }
318
+ return merged;
319
+ }
320
+
263
321
  export async function callApi({ baseUrl, apiKey, timeoutMs = 5000, retries = 1 }, path, body) {
264
322
  if (!apiKey) {
265
323
  throw new Error("Missing MEMOS API key (Token auth)");
@@ -317,6 +375,13 @@ function sanitizeAddMessageEntry(entry) {
317
375
  return { ...entry, content };
318
376
  }
319
377
 
378
+ export function isAgentAllowed(cfg, ctx) {
379
+ if (!cfg.multiAgentMode) return true;
380
+ if (!cfg.allowedAgents || cfg.allowedAgents.length === 0) return true;
381
+ const agentId = ctx?.agentId || cfg.agentId || "main";
382
+ return cfg.allowedAgents.includes(agentId);
383
+ }
384
+
320
385
  export async function searchMemory(cfg, payload) {
321
386
  return callApi(cfg, "/search/memory", sanitizeSearchPayload(payload));
322
387
  }
@@ -518,12 +583,16 @@ function sanitizeInlineText(text) {
518
583
  return String(text).replace(/\r?\n+/g, " ").trim();
519
584
  }
520
585
 
586
+ function resolveDisplayTime(item) {
587
+ return item?.update_time ?? item?.create_time;
588
+ }
589
+
521
590
  function formatMemoryLine(item, text, options = {}) {
522
591
  const cleaned = sanitizeInlineText(text);
523
592
  if (!cleaned) return "";
524
593
  const maxChars = options.maxItemChars;
525
594
  const truncated = truncate(cleaned, maxChars);
526
- const time = formatTime(item?.create_time);
595
+ const time = formatTime(resolveDisplayTime(item));
527
596
  if (time) return ` -[${time}] ${truncated}`;
528
597
  return ` - ${truncated}`;
529
598
  }
@@ -533,7 +602,7 @@ function formatPreferenceLine(item, text, options = {}) {
533
602
  if (!cleaned) return "";
534
603
  const maxChars = options.maxItemChars;
535
604
  const truncated = truncate(cleaned, maxChars);
536
- const time = formatTime(item?.create_time);
605
+ const time = formatTime(resolveDisplayTime(item));
537
606
  const type = normalizePreferenceType(item?.preference_type);
538
607
  const typeLabel = type ? ` [${type}]` : "";
539
608
  if (time) return ` -[${time}]${typeLabel} ${truncated}`;
@@ -2,7 +2,7 @@
2
2
  "id": "memos-cloud-openclaw-plugin",
3
3
  "name": "MemOS Cloud OpenClaw Plugin",
4
4
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
5
- "version": "0.1.10-beta.0",
5
+ "version": "0.1.11-beta.0",
6
6
  "kind": "lifecycle",
7
7
  "main": "./index.js",
8
8
  "configSchema": {
@@ -41,6 +41,11 @@
41
41
  ],
42
42
  "default": "none"
43
43
  },
44
+ "useDirectSessionUserId": {
45
+ "type": "boolean",
46
+ "description": "When enabled, direct-session keys like agent:main:<provider>:direct:<id> use the direct id as MemOS user_id instead of the default configured userId.",
47
+ "default": false
48
+ },
44
49
  "resetOnNew": {
45
50
  "type": "boolean",
46
51
  "default": true
@@ -109,7 +114,47 @@
109
114
  },
110
115
  "filter": {
111
116
  "type": "object",
112
- "description": "MemOS search filter"
117
+ "description": "MemOS search filter",
118
+ "additionalProperties": true
119
+ },
120
+ "relativity": {
121
+ "type": "number",
122
+ "description": "Search relativity threshold",
123
+ "default": 0.45
124
+ },
125
+ "recallFilterEnabled": {
126
+ "type": "boolean",
127
+ "default": false
128
+ },
129
+ "recallFilterBaseUrl": {
130
+ "type": "string",
131
+ "description": "OpenAI-compatible API base URL for recall filtering"
132
+ },
133
+ "recallFilterApiKey": {
134
+ "type": "string"
135
+ },
136
+ "recallFilterModel": {
137
+ "type": "string"
138
+ },
139
+ "recallFilterTimeoutMs": {
140
+ "type": "integer",
141
+ "default": 30000
142
+ },
143
+ "recallFilterRetries": {
144
+ "type": "integer",
145
+ "default": 1
146
+ },
147
+ "recallFilterCandidateLimit": {
148
+ "type": "integer",
149
+ "default": 30
150
+ },
151
+ "recallFilterMaxItemChars": {
152
+ "type": "integer",
153
+ "default": 500
154
+ },
155
+ "recallFilterFailOpen": {
156
+ "type": "boolean",
157
+ "default": true
113
158
  },
114
159
  "knowledgebaseIds": {
115
160
  "type": "array",
@@ -134,6 +179,13 @@
134
179
  "type": "boolean",
135
180
  "default": false
136
181
  },
182
+ "allowedAgents": {
183
+ "type": "array",
184
+ "items": {
185
+ "type": "string"
186
+ },
187
+ "description": "When multiAgentMode is true, only these agent IDs will activate the memory plugin. Comma-separated in env var MEMOS_ALLOWED_AGENTS. Empty list means all agents are allowed."
188
+ },
137
189
  "appId": {
138
190
  "type": "string"
139
191
  },
@@ -162,6 +214,108 @@
162
214
  "throttleMs": {
163
215
  "type": "integer",
164
216
  "default": 0
217
+ },
218
+ "agentOverrides": {
219
+ "type": "object",
220
+ "description": "Per-agent config overrides. Keys are agent IDs, values override global defaults for that agent.",
221
+ "additionalProperties": {
222
+ "type": "object",
223
+ "properties": {
224
+ "knowledgebaseIds": {
225
+ "type": "array",
226
+ "items": {
227
+ "type": "string"
228
+ }
229
+ },
230
+ "memoryLimitNumber": {
231
+ "type": "integer"
232
+ },
233
+ "preferenceLimitNumber": {
234
+ "type": "integer"
235
+ },
236
+ "includePreference": {
237
+ "type": "boolean"
238
+ },
239
+ "includeToolMemory": {
240
+ "type": "boolean"
241
+ },
242
+ "toolMemoryLimitNumber": {
243
+ "type": "integer"
244
+ },
245
+ "includeSkill": {
246
+ "type": "boolean"
247
+ },
248
+ "skillLimitNumber": {
249
+ "type": "integer"
250
+ },
251
+ "relativity": {
252
+ "type": "number"
253
+ },
254
+ "filter": {
255
+ "type": "object",
256
+ "additionalProperties": true
257
+ },
258
+ "recallEnabled": {
259
+ "type": "boolean"
260
+ },
261
+ "addEnabled": {
262
+ "type": "boolean"
263
+ },
264
+ "captureStrategy": {
265
+ "type": "string",
266
+ "enum": [
267
+ "last_turn",
268
+ "full_session"
269
+ ]
270
+ },
271
+ "queryPrefix": {
272
+ "type": "string"
273
+ },
274
+ "maxQueryChars": {
275
+ "type": "integer"
276
+ },
277
+ "maxItemChars": {
278
+ "type": "integer"
279
+ },
280
+ "maxMessageChars": {
281
+ "type": "integer"
282
+ },
283
+ "includeAssistant": {
284
+ "type": "boolean"
285
+ },
286
+ "recallGlobal": {
287
+ "type": "boolean"
288
+ },
289
+ "recallFilterEnabled": {
290
+ "type": "boolean"
291
+ },
292
+ "recallFilterModel": {
293
+ "type": "string"
294
+ },
295
+ "recallFilterBaseUrl": {
296
+ "type": "string"
297
+ },
298
+ "recallFilterApiKey": {
299
+ "type": "string"
300
+ },
301
+ "allowKnowledgebaseIds": {
302
+ "type": "array",
303
+ "items": {
304
+ "type": "string"
305
+ }
306
+ },
307
+ "tags": {
308
+ "type": "array",
309
+ "items": {
310
+ "type": "string"
311
+ }
312
+ },
313
+ "throttleMs": {
314
+ "type": "integer"
315
+ }
316
+ },
317
+ "additionalProperties": false
318
+ }
165
319
  }
166
320
  },
167
321
  "additionalProperties": false