@shodh/memory-mcp 0.1.90 → 0.2.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 +372 -43
- package/package.json +1 -1
- package/scripts/postinstall.cjs +3 -3
package/dist/index.js
CHANGED
|
@@ -4909,10 +4909,41 @@ function nextReconnectDelay(currentDelayMs, maxDelayMs) {
|
|
|
4909
4909
|
return Math.min(safeCurrent * 2, maxDelayMs);
|
|
4910
4910
|
}
|
|
4911
4911
|
|
|
4912
|
+
// string-utils.ts
|
|
4913
|
+
function stripSystemNoise(text) {
|
|
4914
|
+
let result = text;
|
|
4915
|
+
const tagPatterns = [
|
|
4916
|
+
/<task-notification>[\s\S]*?<\/task-notification>/g,
|
|
4917
|
+
/<system-reminder>[\s\S]*?<\/system-reminder>/g,
|
|
4918
|
+
/<shodh-context[\s\S]*?<\/shodh-context>/g,
|
|
4919
|
+
/<shodh-memory[\s\S]*?<\/shodh-memory>/g,
|
|
4920
|
+
/<command-name>[\s\S]*?<\/command-name>/g
|
|
4921
|
+
];
|
|
4922
|
+
for (const pattern of tagPatterns) {
|
|
4923
|
+
result = result.replace(pattern, "");
|
|
4924
|
+
}
|
|
4925
|
+
result = result.replace(/\s{3,}/g, " ").trim();
|
|
4926
|
+
return result;
|
|
4927
|
+
}
|
|
4928
|
+
|
|
4912
4929
|
// index.ts
|
|
4913
4930
|
var __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath(import.meta.url) : "";
|
|
4914
4931
|
var __dirname2 = __filename2 ? path.dirname(__filename2) : process.cwd();
|
|
4915
|
-
|
|
4932
|
+
function resolveApiUrl() {
|
|
4933
|
+
if (process.env.SHODH_API_URL)
|
|
4934
|
+
return process.env.SHODH_API_URL;
|
|
4935
|
+
const host = process.env.SHODH_HOST;
|
|
4936
|
+
const port = process.env.SHODH_PORT;
|
|
4937
|
+
if (host) {
|
|
4938
|
+
const scheme = port === "443" ? "https" : "http";
|
|
4939
|
+
const portSuffix = port && port !== "443" && port !== "80" ? `:${port}` : "";
|
|
4940
|
+
return `${scheme}://${host}${portSuffix}`;
|
|
4941
|
+
}
|
|
4942
|
+
if (port)
|
|
4943
|
+
return `http://127.0.0.1:${port}`;
|
|
4944
|
+
return "http://127.0.0.1:3030";
|
|
4945
|
+
}
|
|
4946
|
+
var API_URL = resolveApiUrl();
|
|
4916
4947
|
var WS_URL = API_URL.replace(/^http/, "ws") + "/api/stream";
|
|
4917
4948
|
var USER_ID = process.env.SHODH_USER_ID || "claude-code";
|
|
4918
4949
|
function isLocalServer() {
|
|
@@ -4925,10 +4956,25 @@ function isLocalServer() {
|
|
|
4925
4956
|
}
|
|
4926
4957
|
}
|
|
4927
4958
|
var SANDBOX_MODE = process.env.SMITHERY_SANDBOX === "true";
|
|
4928
|
-
var API_KEY =
|
|
4959
|
+
var API_KEY = "";
|
|
4960
|
+
var apiKeySource = "";
|
|
4961
|
+
if (process.env.SHODH_API_KEY) {
|
|
4962
|
+
API_KEY = process.env.SHODH_API_KEY;
|
|
4963
|
+
apiKeySource = "SHODH_API_KEY";
|
|
4964
|
+
} else if (process.env.SHODH_DEV_API_KEY) {
|
|
4965
|
+
API_KEY = process.env.SHODH_DEV_API_KEY;
|
|
4966
|
+
apiKeySource = "SHODH_DEV_API_KEY";
|
|
4967
|
+
} else if (process.env.SHODH_API_KEYS?.split(",")[0]?.trim()) {
|
|
4968
|
+
API_KEY = process.env.SHODH_API_KEYS.split(",")[0].trim();
|
|
4969
|
+
apiKeySource = "SHODH_API_KEYS";
|
|
4970
|
+
} else if (SANDBOX_MODE) {
|
|
4971
|
+
API_KEY = "sandbox";
|
|
4972
|
+
apiKeySource = "sandbox";
|
|
4973
|
+
}
|
|
4929
4974
|
if (!API_KEY) {
|
|
4930
4975
|
if (isLocalServer()) {
|
|
4931
4976
|
API_KEY = crypto.randomBytes(32).toString("hex");
|
|
4977
|
+
apiKeySource = "auto-generated";
|
|
4932
4978
|
console.error("[shodh-memory] No API key set — auto-generated for local server.");
|
|
4933
4979
|
} else {
|
|
4934
4980
|
console.error("ERROR: SHODH_API_KEY is required for remote servers.");
|
|
@@ -4941,6 +4987,11 @@ if (!API_KEY) {
|
|
|
4941
4987
|
process.exit(1);
|
|
4942
4988
|
}
|
|
4943
4989
|
}
|
|
4990
|
+
if (apiKeySource === "SHODH_DEV_API_KEY") {
|
|
4991
|
+
console.error("[shodh-memory] WARNING: API key loaded from SHODH_DEV_API_KEY — this is a development key. Use SHODH_API_KEY for production.");
|
|
4992
|
+
} else if (apiKeySource && apiKeySource !== "auto-generated" && apiKeySource !== "sandbox") {
|
|
4993
|
+
console.error(`[shodh-memory] API key loaded from ${apiKeySource}.`);
|
|
4994
|
+
}
|
|
4944
4995
|
var RETRY_ATTEMPTS = 3;
|
|
4945
4996
|
var RETRY_DELAY_MS = 1000;
|
|
4946
4997
|
var REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -4977,22 +5028,9 @@ var STREAM_MIN_CONTENT_LENGTH = 50;
|
|
|
4977
5028
|
var PROACTIVE_SURFACING = process.env.SHODH_PROACTIVE !== "false";
|
|
4978
5029
|
var PROACTIVE_MIN_CONTEXT_LENGTH = 30;
|
|
4979
5030
|
var MAX_CONTEXT_LENGTH = 4000;
|
|
4980
|
-
function stripSystemNoise(text) {
|
|
4981
|
-
let result = text;
|
|
4982
|
-
const tagPatterns = [
|
|
4983
|
-
/<task-notification>[\s\S]*?<\/task-notification>/g,
|
|
4984
|
-
/<system-reminder>[\s\S]*?<\/system-reminder>/g,
|
|
4985
|
-
/<shodh-context[\s\S]*?<\/shodh-context>/g,
|
|
4986
|
-
/<shodh-memory[\s\S]*?<\/shodh-memory>/g,
|
|
4987
|
-
/<command-name>[\s\S]*?<\/command-name>/g
|
|
4988
|
-
];
|
|
4989
|
-
for (const pattern of tagPatterns) {
|
|
4990
|
-
result = result.replace(pattern, "");
|
|
4991
|
-
}
|
|
4992
|
-
result = result.replace(/\s{3,}/g, " ").trim();
|
|
4993
|
-
return result;
|
|
4994
|
-
}
|
|
4995
5031
|
var lastProactiveResponse = "";
|
|
5032
|
+
var lastUserContext = "";
|
|
5033
|
+
var proactiveCallInFlight = false;
|
|
4996
5034
|
var streamSocket = null;
|
|
4997
5035
|
var streamConnecting = false;
|
|
4998
5036
|
var streamReconnectTimer = null;
|
|
@@ -5135,10 +5173,12 @@ async function surfaceRelevant(context, maxResults = 3) {
|
|
|
5135
5173
|
}),
|
|
5136
5174
|
signal: controller.signal
|
|
5137
5175
|
});
|
|
5138
|
-
|
|
5139
|
-
|
|
5176
|
+
if (!response.ok) {
|
|
5177
|
+
clearTimeout(timeoutId);
|
|
5140
5178
|
return null;
|
|
5179
|
+
}
|
|
5141
5180
|
const result = await response.json();
|
|
5181
|
+
clearTimeout(timeoutId);
|
|
5142
5182
|
return result.memories || null;
|
|
5143
5183
|
} catch (e) {
|
|
5144
5184
|
console.error("[Proactive] Failed to surface memories:", e);
|
|
@@ -5192,10 +5232,15 @@ async function apiCall(endpoint, method = "GET", body) {
|
|
|
5192
5232
|
const errorText = await response.text().catch(() => "Unknown error");
|
|
5193
5233
|
throw new Error(`API error ${response.status}: ${errorText}`);
|
|
5194
5234
|
}
|
|
5195
|
-
|
|
5235
|
+
try {
|
|
5236
|
+
return await response.json();
|
|
5237
|
+
} catch {
|
|
5238
|
+
throw new Error(`API returned invalid JSON from ${endpoint}`);
|
|
5239
|
+
}
|
|
5196
5240
|
} catch (error) {
|
|
5197
5241
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
5198
|
-
|
|
5242
|
+
const statusMatch = lastError.message.match(/API error (\d+)/);
|
|
5243
|
+
if (statusMatch && parseInt(statusMatch[1], 10) >= 400 && parseInt(statusMatch[1], 10) < 500) {
|
|
5199
5244
|
throw lastError;
|
|
5200
5245
|
}
|
|
5201
5246
|
if (attempt < RETRY_ATTEMPTS) {
|
|
@@ -5297,6 +5342,57 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
5297
5342
|
parent_id: {
|
|
5298
5343
|
type: "string",
|
|
5299
5344
|
description: "Parent memory ID for hierarchical organization. Creates memory trees (e.g., '71-research' -> 'algebraic' -> '21×27≡-1')"
|
|
5345
|
+
},
|
|
5346
|
+
importance: {
|
|
5347
|
+
type: "number",
|
|
5348
|
+
description: "Optional importance override (0.0-1.0). Bypasses auto-calculation. Use for memories where importance is known: Decision=0.8, Learning=0.7, Error=0.7, Discovery=0.6, Observation=0.3"
|
|
5349
|
+
},
|
|
5350
|
+
robot_id: {
|
|
5351
|
+
type: "string",
|
|
5352
|
+
description: "Robot/drone identifier for multi-robot systems"
|
|
5353
|
+
},
|
|
5354
|
+
mission_id: {
|
|
5355
|
+
type: "string",
|
|
5356
|
+
description: "Mission identifier for grouping experiences"
|
|
5357
|
+
},
|
|
5358
|
+
geo_location: {
|
|
5359
|
+
type: "array",
|
|
5360
|
+
items: { type: "number" },
|
|
5361
|
+
minItems: 3,
|
|
5362
|
+
maxItems: 3,
|
|
5363
|
+
description: "GPS coordinates [latitude, longitude, altitude] in WGS84"
|
|
5364
|
+
},
|
|
5365
|
+
local_position: {
|
|
5366
|
+
type: "array",
|
|
5367
|
+
items: { type: "number" },
|
|
5368
|
+
minItems: 3,
|
|
5369
|
+
maxItems: 3,
|
|
5370
|
+
description: "Local position [x, y, z] in meters (robot-local frame)"
|
|
5371
|
+
},
|
|
5372
|
+
heading: {
|
|
5373
|
+
type: "number",
|
|
5374
|
+
description: "Heading in degrees (0-360)"
|
|
5375
|
+
},
|
|
5376
|
+
action_type: {
|
|
5377
|
+
type: "string",
|
|
5378
|
+
description: "Action type name (e.g., 'navigate', 'grasp', 'dock')"
|
|
5379
|
+
},
|
|
5380
|
+
reward: {
|
|
5381
|
+
type: "number",
|
|
5382
|
+
description: "Reinforcement learning reward signal (-1.0 to 1.0)"
|
|
5383
|
+
},
|
|
5384
|
+
sensor_data: {
|
|
5385
|
+
type: "object",
|
|
5386
|
+
additionalProperties: { type: "number" },
|
|
5387
|
+
description: "Raw sensor readings (e.g., {battery: 72.5, temperature: 23.1})"
|
|
5388
|
+
},
|
|
5389
|
+
outcome_type: {
|
|
5390
|
+
type: "string",
|
|
5391
|
+
description: "Outcome type: success, failure, partial, aborted, timeout"
|
|
5392
|
+
},
|
|
5393
|
+
terrain_type: {
|
|
5394
|
+
type: "string",
|
|
5395
|
+
description: "Terrain type: indoor, outdoor, urban, rural, water, aerial"
|
|
5300
5396
|
}
|
|
5301
5397
|
},
|
|
5302
5398
|
required: ["content"]
|
|
@@ -5304,7 +5400,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
5304
5400
|
},
|
|
5305
5401
|
{
|
|
5306
5402
|
name: "recall",
|
|
5307
|
-
description: "Search memories AND todos using semantic similarity. Returns both relevant memories and matching todos. Use this to find past experiences, decisions, context, or pending work. Modes: 'semantic' (vector similarity), 'associative' (graph traversal), 'hybrid' (combined).",
|
|
5403
|
+
description: "Search memories AND todos using semantic similarity. Returns both relevant memories and matching todos. Use this to find past experiences, decisions, context, or pending work. Modes: 'semantic' (vector similarity), 'associative' (graph traversal), 'temporal' (time-based retrieval), 'hybrid' (combined), 'spatial' (geo-location based), 'mission' (mission context), 'action_outcome' (reward-based learning).",
|
|
5308
5404
|
inputSchema: {
|
|
5309
5405
|
type: "object",
|
|
5310
5406
|
properties: {
|
|
@@ -5319,14 +5415,87 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
5319
5415
|
},
|
|
5320
5416
|
mode: {
|
|
5321
5417
|
type: "string",
|
|
5322
|
-
enum: ["semantic", "associative", "hybrid"],
|
|
5323
|
-
description: "Retrieval mode: 'semantic' for pure vector similarity, 'associative' for graph-based traversal (follows learned connections), 'hybrid' for density-dependent combination (default)",
|
|
5418
|
+
enum: ["semantic", "associative", "temporal", "hybrid", "spatial", "mission", "action_outcome"],
|
|
5419
|
+
description: "Retrieval mode: 'semantic' for pure vector similarity, 'associative' for graph-based traversal (follows learned connections), 'temporal' for time-based retrieval, 'hybrid' for density-dependent combination (default), 'spatial' for geo-location based, 'mission' for mission context, 'action_outcome' for reward-based learning",
|
|
5324
5420
|
default: "hybrid"
|
|
5421
|
+
},
|
|
5422
|
+
session_id: {
|
|
5423
|
+
type: "string",
|
|
5424
|
+
description: "Session ID for session-scoped retrieval. When provided, retrieves memories from that session's time window. Forces temporal mode."
|
|
5425
|
+
},
|
|
5426
|
+
robot_id: {
|
|
5427
|
+
type: "string",
|
|
5428
|
+
description: "Filter by robot/drone identifier (for multi-robot systems)"
|
|
5429
|
+
},
|
|
5430
|
+
mission_id: {
|
|
5431
|
+
type: "string",
|
|
5432
|
+
description: "Filter by mission identifier"
|
|
5433
|
+
},
|
|
5434
|
+
geo_lat: {
|
|
5435
|
+
type: "number",
|
|
5436
|
+
description: "Spatial filter: center latitude (-90 to 90). Requires geo_lon and geo_radius_meters."
|
|
5437
|
+
},
|
|
5438
|
+
geo_lon: {
|
|
5439
|
+
type: "number",
|
|
5440
|
+
description: "Spatial filter: center longitude (-180 to 180). Requires geo_lat and geo_radius_meters."
|
|
5441
|
+
},
|
|
5442
|
+
geo_radius_meters: {
|
|
5443
|
+
type: "number",
|
|
5444
|
+
description: "Spatial filter: search radius in meters. Requires geo_lat and geo_lon."
|
|
5445
|
+
},
|
|
5446
|
+
action_type: {
|
|
5447
|
+
type: "string",
|
|
5448
|
+
description: "Filter by action type (e.g., 'navigate', 'grasp', 'dock')"
|
|
5449
|
+
},
|
|
5450
|
+
reward_min: {
|
|
5451
|
+
type: "number",
|
|
5452
|
+
description: "Filter by minimum reward value (-1.0 to 1.0)"
|
|
5453
|
+
},
|
|
5454
|
+
reward_max: {
|
|
5455
|
+
type: "number",
|
|
5456
|
+
description: "Filter by maximum reward value (-1.0 to 1.0)"
|
|
5457
|
+
},
|
|
5458
|
+
outcome_type: {
|
|
5459
|
+
type: "string",
|
|
5460
|
+
description: "Filter by outcome type: success, failure, partial, aborted, timeout"
|
|
5461
|
+
},
|
|
5462
|
+
failures_only: {
|
|
5463
|
+
type: "boolean",
|
|
5464
|
+
description: "If true, only return failure/error experiences"
|
|
5465
|
+
},
|
|
5466
|
+
terrain_type: {
|
|
5467
|
+
type: "string",
|
|
5468
|
+
description: "Filter by terrain type: indoor, outdoor, urban, rural, water, aerial"
|
|
5469
|
+
},
|
|
5470
|
+
tags: {
|
|
5471
|
+
type: "array",
|
|
5472
|
+
items: { type: "string" },
|
|
5473
|
+
description: "Filter by tags (any match)"
|
|
5325
5474
|
}
|
|
5326
5475
|
},
|
|
5327
5476
|
required: ["query"]
|
|
5328
5477
|
}
|
|
5329
5478
|
},
|
|
5479
|
+
{
|
|
5480
|
+
name: "recall_by_tags",
|
|
5481
|
+
description: "Find memories by tags. Returns memories matching ANY of the provided tags. Useful for finding memories by category (e.g., 'tool:Edit', 'file:src/main.rs', 'source:hook', 'error', 'session-summary').",
|
|
5482
|
+
inputSchema: {
|
|
5483
|
+
type: "object",
|
|
5484
|
+
properties: {
|
|
5485
|
+
tags: {
|
|
5486
|
+
type: "array",
|
|
5487
|
+
items: { type: "string" },
|
|
5488
|
+
description: "Tags to search for (returns memories matching ANY of these tags)"
|
|
5489
|
+
},
|
|
5490
|
+
limit: {
|
|
5491
|
+
type: "number",
|
|
5492
|
+
description: "Maximum number of results (default: 50)",
|
|
5493
|
+
default: 50
|
|
5494
|
+
}
|
|
5495
|
+
},
|
|
5496
|
+
required: ["tags"]
|
|
5497
|
+
}
|
|
5498
|
+
},
|
|
5330
5499
|
{
|
|
5331
5500
|
name: "context_summary",
|
|
5332
5501
|
description: "Get a condensed summary of recent learnings, decisions, and context. Use this at the start of a session to quickly understand what you've learned before.",
|
|
@@ -5522,6 +5691,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
5522
5691
|
type: "boolean",
|
|
5523
5692
|
description: "Automatically store the context as a Conversation memory (default: true). Set to false to only surface memories without storing.",
|
|
5524
5693
|
default: true
|
|
5694
|
+
},
|
|
5695
|
+
tool_actions: {
|
|
5696
|
+
type: "array",
|
|
5697
|
+
items: {
|
|
5698
|
+
type: "object",
|
|
5699
|
+
properties: {
|
|
5700
|
+
tool_name: { type: "string", description: "Tool or actuator name (e.g., 'Edit', 'Bash', 'navigate', 'grasp')" },
|
|
5701
|
+
inputs: { type: "object", additionalProperties: { type: "string" }, description: "Key-value input parameters" },
|
|
5702
|
+
success: { type: "boolean", description: "Whether the action succeeded" },
|
|
5703
|
+
output_snippet: { type: "string", description: "First 200 chars of output" },
|
|
5704
|
+
reward: { type: "number", description: "Reward signal for robotics (-1.0 to 1.0)" }
|
|
5705
|
+
},
|
|
5706
|
+
required: ["tool_name", "success"]
|
|
5707
|
+
},
|
|
5708
|
+
description: "Tool/actuator actions performed since last proactive_context call. Used for causal feedback attribution."
|
|
5525
5709
|
}
|
|
5526
5710
|
},
|
|
5527
5711
|
required: ["context"]
|
|
@@ -6059,7 +6243,18 @@ To start: cd shodh-memory && cargo run`
|
|
|
6059
6243
|
episode_id,
|
|
6060
6244
|
sequence_number,
|
|
6061
6245
|
preceding_memory_id,
|
|
6062
|
-
parent_id
|
|
6246
|
+
parent_id,
|
|
6247
|
+
importance,
|
|
6248
|
+
robot_id,
|
|
6249
|
+
mission_id,
|
|
6250
|
+
geo_location,
|
|
6251
|
+
local_position,
|
|
6252
|
+
heading,
|
|
6253
|
+
action_type,
|
|
6254
|
+
reward,
|
|
6255
|
+
sensor_data,
|
|
6256
|
+
outcome_type,
|
|
6257
|
+
terrain_type
|
|
6063
6258
|
} = args;
|
|
6064
6259
|
if (!content || content.length === 0) {
|
|
6065
6260
|
return { content: [{ type: "text", text: "Error: 'content' is required and cannot be empty" }], isError: true };
|
|
@@ -6081,7 +6276,18 @@ To start: cd shodh-memory && cargo run`
|
|
|
6081
6276
|
...episode_id && { episode_id },
|
|
6082
6277
|
...sequence_number !== undefined && { sequence_number },
|
|
6083
6278
|
...preceding_memory_id && { preceding_memory_id },
|
|
6084
|
-
...parent_id && { parent_id }
|
|
6279
|
+
...parent_id && { parent_id },
|
|
6280
|
+
...importance !== undefined && { importance },
|
|
6281
|
+
...robot_id && { robot_id },
|
|
6282
|
+
...mission_id && { mission_id },
|
|
6283
|
+
...geo_location && geo_location.length === 3 && { geo_location },
|
|
6284
|
+
...local_position && local_position.length === 3 && { local_position },
|
|
6285
|
+
...heading !== undefined && { heading },
|
|
6286
|
+
...action_type && { action_type },
|
|
6287
|
+
...reward !== undefined && { reward },
|
|
6288
|
+
...sensor_data && Object.keys(sensor_data).length > 0 && { sensor_data },
|
|
6289
|
+
...outcome_type && { outcome_type },
|
|
6290
|
+
...terrain_type && { terrain_type }
|
|
6085
6291
|
});
|
|
6086
6292
|
let response = `\uD83D\uDC18 Memory Stored
|
|
6087
6293
|
`;
|
|
@@ -6102,14 +6308,31 @@ ID: ${result.id}`;
|
|
|
6102
6308
|
};
|
|
6103
6309
|
}
|
|
6104
6310
|
case "recall": {
|
|
6105
|
-
const {
|
|
6311
|
+
const {
|
|
6312
|
+
query,
|
|
6313
|
+
limit: rawLimit = 5,
|
|
6314
|
+
mode = "hybrid",
|
|
6315
|
+
session_id,
|
|
6316
|
+
robot_id,
|
|
6317
|
+
mission_id,
|
|
6318
|
+
geo_lat,
|
|
6319
|
+
geo_lon,
|
|
6320
|
+
geo_radius_meters,
|
|
6321
|
+
action_type,
|
|
6322
|
+
reward_min,
|
|
6323
|
+
reward_max,
|
|
6324
|
+
outcome_type,
|
|
6325
|
+
failures_only,
|
|
6326
|
+
terrain_type,
|
|
6327
|
+
tags
|
|
6328
|
+
} = args;
|
|
6106
6329
|
if (!query || query.length === 0) {
|
|
6107
6330
|
return { content: [{ type: "text", text: "Error: 'query' is required and cannot be empty" }], isError: true };
|
|
6108
6331
|
}
|
|
6109
6332
|
if (query.length > MAX_QUERY_LENGTH) {
|
|
6110
6333
|
return { content: [{ type: "text", text: `Error: 'query' exceeds maximum length of ${MAX_QUERY_LENGTH} characters` }], isError: true };
|
|
6111
6334
|
}
|
|
6112
|
-
const validModes = ["semantic", "associative", "hybrid"];
|
|
6335
|
+
const validModes = ["semantic", "associative", "temporal", "hybrid", "spatial", "mission", "action_outcome"];
|
|
6113
6336
|
if (!validModes.includes(mode)) {
|
|
6114
6337
|
return { content: [{ type: "text", text: `Error: 'mode' must be one of: ${validModes.join(", ")}` }], isError: true };
|
|
6115
6338
|
}
|
|
@@ -6118,7 +6341,20 @@ ID: ${result.id}`;
|
|
|
6118
6341
|
user_id: USER_ID,
|
|
6119
6342
|
query,
|
|
6120
6343
|
limit,
|
|
6121
|
-
mode
|
|
6344
|
+
mode,
|
|
6345
|
+
...session_id ? { session_id } : {},
|
|
6346
|
+
...robot_id ? { robot_id } : {},
|
|
6347
|
+
...mission_id ? { mission_id } : {},
|
|
6348
|
+
...geo_lat !== undefined ? { geo_lat } : {},
|
|
6349
|
+
...geo_lon !== undefined ? { geo_lon } : {},
|
|
6350
|
+
...geo_radius_meters !== undefined ? { geo_radius_meters } : {},
|
|
6351
|
+
...action_type ? { action_type } : {},
|
|
6352
|
+
...reward_min !== undefined ? { reward_min } : {},
|
|
6353
|
+
...reward_max !== undefined ? { reward_max } : {},
|
|
6354
|
+
...outcome_type ? { outcome_type } : {},
|
|
6355
|
+
...failures_only !== undefined ? { failures_only } : {},
|
|
6356
|
+
...terrain_type ? { terrain_type } : {},
|
|
6357
|
+
...tags && tags.length > 0 ? { tags } : {}
|
|
6122
6358
|
});
|
|
6123
6359
|
const memories = result.memories || [];
|
|
6124
6360
|
const todos = result.todos || [];
|
|
@@ -6166,14 +6402,17 @@ ID: ${result.id}`;
|
|
|
6166
6402
|
return d.toLocaleDateString([], { month: "short", day: "numeric" });
|
|
6167
6403
|
}
|
|
6168
6404
|
};
|
|
6405
|
+
const memoryDisplayScores = memories.map((m) => m.score || 0);
|
|
6406
|
+
const todoDisplayScores = todos.map((t) => t.score || 0);
|
|
6169
6407
|
if (memories.length > 0) {
|
|
6170
6408
|
response += `\uD83D\uDCDD MEMORIES
|
|
6171
6409
|
`;
|
|
6172
6410
|
for (let i = 0;i < memories.length; i++) {
|
|
6173
6411
|
const m = memories[i];
|
|
6174
6412
|
const content = getContent(m);
|
|
6175
|
-
const
|
|
6176
|
-
const
|
|
6413
|
+
const displayScore = memoryDisplayScores[i];
|
|
6414
|
+
const score = (displayScore * 100).toFixed(0);
|
|
6415
|
+
const filled = Math.max(0, Math.min(10, Math.round(displayScore * 10)));
|
|
6177
6416
|
const matchBar = "█".repeat(filled) + "░".repeat(10 - filled);
|
|
6178
6417
|
const timeStr = formatTime(m.created_at);
|
|
6179
6418
|
response += `• ${matchBar} ${score}% │ ${timeStr}
|
|
@@ -6195,8 +6434,9 @@ ID: ${result.id}`;
|
|
|
6195
6434
|
`;
|
|
6196
6435
|
for (let i = 0;i < todos.length; i++) {
|
|
6197
6436
|
const t = todos[i];
|
|
6198
|
-
const
|
|
6199
|
-
const
|
|
6437
|
+
const displayScore = todoDisplayScores[i];
|
|
6438
|
+
const score = (displayScore * 100).toFixed(0);
|
|
6439
|
+
const filled = Math.max(0, Math.min(10, Math.round(displayScore * 10)));
|
|
6200
6440
|
const matchBar = "█".repeat(filled) + "░".repeat(10 - filled);
|
|
6201
6441
|
const statusIcon = t.status === "done" ? "✓" : t.status === "in_progress" ? "▶" : t.status === "blocked" ? "⊗" : "○";
|
|
6202
6442
|
const timeStr = formatTime(t.created_at);
|
|
@@ -6254,6 +6494,47 @@ ID: ${result.id}`;
|
|
|
6254
6494
|
content: [{ type: "text", text: response }]
|
|
6255
6495
|
};
|
|
6256
6496
|
}
|
|
6497
|
+
case "recall_by_tags": {
|
|
6498
|
+
const { tags, limit: rawTagLimit = 50 } = args;
|
|
6499
|
+
if (!tags || tags.length === 0) {
|
|
6500
|
+
return {
|
|
6501
|
+
content: [{ type: "text", text: "Error: 'tags' is required and must contain at least one tag" }],
|
|
6502
|
+
isError: true
|
|
6503
|
+
};
|
|
6504
|
+
}
|
|
6505
|
+
const tagLimit = Math.max(1, Math.min(Math.floor(rawTagLimit), MAX_LIMIT));
|
|
6506
|
+
const tagResult = await apiCall("/api/recall/tags", "POST", {
|
|
6507
|
+
user_id: USER_ID,
|
|
6508
|
+
tags,
|
|
6509
|
+
limit: tagLimit
|
|
6510
|
+
});
|
|
6511
|
+
const tagMemories = tagResult.memories || [];
|
|
6512
|
+
if (tagMemories.length === 0) {
|
|
6513
|
+
return {
|
|
6514
|
+
content: [{ type: "text", text: `No memories found matching tags: ${tags.join(", ")}` }]
|
|
6515
|
+
};
|
|
6516
|
+
}
|
|
6517
|
+
let tagResponse = `\uD83C\uDFF7️ Recall by Tags: ${tags.join(", ")}
|
|
6518
|
+
`;
|
|
6519
|
+
tagResponse += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
6520
|
+
`;
|
|
6521
|
+
tagResponse += `Found ${tagMemories.length} memories
|
|
6522
|
+
|
|
6523
|
+
`;
|
|
6524
|
+
for (let i = 0;i < tagMemories.length; i++) {
|
|
6525
|
+
const m = tagMemories[i];
|
|
6526
|
+
const content = getContent(m);
|
|
6527
|
+
const memTags = (m.experience?.tags || []).join(", ");
|
|
6528
|
+
tagResponse += `${String(i + 1).padStart(2)}. ${content.slice(0, 150)}${content.length > 150 ? "..." : ""}
|
|
6529
|
+
`;
|
|
6530
|
+
tagResponse += ` ┗━ ${getType(m)} │ tags: [${memTags}] │ ${m.id}
|
|
6531
|
+
|
|
6532
|
+
`;
|
|
6533
|
+
}
|
|
6534
|
+
return {
|
|
6535
|
+
content: [{ type: "text", text: tagResponse.trimEnd() }]
|
|
6536
|
+
};
|
|
6537
|
+
}
|
|
6257
6538
|
case "context_summary": {
|
|
6258
6539
|
const {
|
|
6259
6540
|
include_decisions = true,
|
|
@@ -6698,7 +6979,8 @@ By Type:
|
|
|
6698
6979
|
recency_weight = 0.2,
|
|
6699
6980
|
max_results = 5,
|
|
6700
6981
|
memory_types = [],
|
|
6701
|
-
auto_ingest = true
|
|
6982
|
+
auto_ingest = true,
|
|
6983
|
+
tool_actions = []
|
|
6702
6984
|
} = args;
|
|
6703
6985
|
const cleanedContext = stripSystemNoise(context).slice(0, MAX_CONTEXT_LENGTH);
|
|
6704
6986
|
if (cleanedContext.length < PROACTIVE_MIN_CONTEXT_LENGTH) {
|
|
@@ -6708,6 +6990,10 @@ By Type:
|
|
|
6708
6990
|
[Latency: 0.0ms]` }]
|
|
6709
6991
|
};
|
|
6710
6992
|
}
|
|
6993
|
+
const skipFeedback = proactiveCallInFlight;
|
|
6994
|
+
proactiveCallInFlight = true;
|
|
6995
|
+
const previousUserContext = skipFeedback ? "" : lastUserContext;
|
|
6996
|
+
lastUserContext = cleanedContext;
|
|
6711
6997
|
const result = await apiCall("/api/proactive_context", "POST", {
|
|
6712
6998
|
user_id: USER_ID,
|
|
6713
6999
|
context: cleanedContext,
|
|
@@ -6717,8 +7003,9 @@ By Type:
|
|
|
6717
7003
|
recency_weight,
|
|
6718
7004
|
memory_types,
|
|
6719
7005
|
auto_ingest,
|
|
6720
|
-
previous_response: lastProactiveResponse || undefined,
|
|
6721
|
-
user_followup: lastProactiveResponse ?
|
|
7006
|
+
previous_response: skipFeedback ? undefined : lastProactiveResponse || undefined,
|
|
7007
|
+
user_followup: skipFeedback || !lastProactiveResponse ? undefined : previousUserContext || undefined,
|
|
7008
|
+
...tool_actions.length > 0 ? { tool_actions } : {}
|
|
6722
7009
|
});
|
|
6723
7010
|
const memories = result.memories || [];
|
|
6724
7011
|
const entities = result.detected_entities || [];
|
|
@@ -6729,10 +7016,13 @@ By Type:
|
|
|
6729
7016
|
Detected entities: ${entities.map((e) => `"${e.name}" (${e.entity_type})`).join(", ")}` : "";
|
|
6730
7017
|
const feedbackNote2 = result.feedback_processed ? `
|
|
6731
7018
|
[Feedback: ${result.feedback_processed.memories_evaluated} evaluated, ${result.feedback_processed.reinforced.length} reinforced, ${result.feedback_processed.weakened.length} weakened]` : "";
|
|
6732
|
-
const
|
|
7019
|
+
const temporalNote2 = result.temporal_credits_applied ? `
|
|
7020
|
+
[Temporal credits: ${result.temporal_credits_applied} multi-turn signals applied]` : "";
|
|
7021
|
+
const emptyText = `No relevant memories surfaced for this context.${entityList}${feedbackNote2}${temporalNote2}
|
|
6733
7022
|
|
|
6734
7023
|
[Latency: ${(result.latency_ms ?? 0).toFixed(1)}ms]`;
|
|
6735
7024
|
lastProactiveResponse = emptyText;
|
|
7025
|
+
proactiveCallInFlight = false;
|
|
6736
7026
|
return {
|
|
6737
7027
|
content: [{ type: "text", text: emptyText }]
|
|
6738
7028
|
};
|
|
@@ -6852,6 +7142,8 @@ Detected entities: ${entities.map((e) => `"${e.name}" (${e.entity_type})`).join(
|
|
|
6852
7142
|
`);
|
|
6853
7143
|
const feedbackNote = result.feedback_processed ? `
|
|
6854
7144
|
[Feedback loop: ${result.feedback_processed.memories_evaluated} evaluated, ${result.feedback_processed.reinforced.length} reinforced, ${result.feedback_processed.weakened.length} weakened]` : "";
|
|
7145
|
+
const temporalNote = result.temporal_credits_applied ? `
|
|
7146
|
+
[Temporal credits: ${result.temporal_credits_applied} multi-turn signals applied]` : "";
|
|
6855
7147
|
const ingestNote = result.ingested_memory_id ? `
|
|
6856
7148
|
[Context ingested: ${result.ingested_memory_id}]` : "";
|
|
6857
7149
|
const summaryParts = [];
|
|
@@ -6866,10 +7158,13 @@ Detected entities: ${entities.map((e) => `"${e.name}" (${e.entity_type})`).join(
|
|
|
6866
7158
|
const summary = summaryParts.length > 0 ? `Surfaced ${summaryParts.join(", ")}` : "No relevant context found";
|
|
6867
7159
|
const responseText = `${temporalHeader}${summary}:
|
|
6868
7160
|
|
|
6869
|
-
${formattedWithTime}${entitySummary}${factsBlock}${reminderBlock}${todoBlock}${feedbackNote}${ingestNote}
|
|
7161
|
+
${formattedWithTime}${entitySummary}${factsBlock}${reminderBlock}${todoBlock}${feedbackNote}${temporalNote}${ingestNote}
|
|
6870
7162
|
|
|
6871
7163
|
[Latency: ${(result.latency_ms ?? 0).toFixed(1)}ms | Threshold: ${(semantic_threshold * 100).toFixed(0)}%]`;
|
|
6872
|
-
|
|
7164
|
+
const cleanContent = memories.map((m) => m.content || "").filter((c) => c.length > 0).join(`
|
|
7165
|
+
`);
|
|
7166
|
+
lastProactiveResponse = cleanContent || responseText;
|
|
7167
|
+
proactiveCallInFlight = false;
|
|
6873
7168
|
return {
|
|
6874
7169
|
content: [{ type: "text", text: responseText }]
|
|
6875
7170
|
};
|
|
@@ -7420,13 +7715,13 @@ ${formattedWithTime}${entitySummary}${factsBlock}${reminderBlock}${todoBlock}${f
|
|
|
7420
7715
|
const context = contextParts.join(" ").slice(0, 1000);
|
|
7421
7716
|
if (context.length >= PROACTIVE_MIN_CONTEXT_LENGTH) {
|
|
7422
7717
|
const surfaced = await surfaceRelevant(context, 3);
|
|
7423
|
-
if (surfaced && surfaced.length > 0) {
|
|
7718
|
+
if (surfaced && surfaced.length > 0 && result.content.length > 0) {
|
|
7424
7719
|
const surfacedText = formatSurfacedMemories(surfaced);
|
|
7425
7720
|
result.content[result.content.length - 1].text += surfacedText;
|
|
7426
7721
|
}
|
|
7427
7722
|
}
|
|
7428
7723
|
}
|
|
7429
|
-
if (tokenStatus.alert) {
|
|
7724
|
+
if (tokenStatus.alert && result.content.length > 0) {
|
|
7430
7725
|
const percentUsed = Math.round(tokenStatus.percent * 100);
|
|
7431
7726
|
const warning = `⚠️ CONTEXT ALERT: ${percentUsed}% of token budget used (${tokenStatus.tokens.toLocaleString()}/${tokenStatus.budget.toLocaleString()}). Consider starting a new session or running consolidation.
|
|
7432
7727
|
|
|
@@ -8172,16 +8467,31 @@ async function ensureServerRunning() {
|
|
|
8172
8467
|
}
|
|
8173
8468
|
}
|
|
8174
8469
|
function cleanupServer() {
|
|
8470
|
+
if (streamReconnectTimer) {
|
|
8471
|
+
clearTimeout(streamReconnectTimer);
|
|
8472
|
+
streamReconnectTimer = null;
|
|
8473
|
+
}
|
|
8474
|
+
STREAM_ENABLED = false;
|
|
8475
|
+
if (streamSocket) {
|
|
8476
|
+
try {
|
|
8477
|
+
streamSocket.close();
|
|
8478
|
+
} catch (_) {}
|
|
8479
|
+
streamSocket = null;
|
|
8480
|
+
}
|
|
8175
8481
|
if (serverProcess && !serverProcess.killed) {
|
|
8176
8482
|
if (process.platform !== "win32" && serverProcess.pid) {
|
|
8177
8483
|
try {
|
|
8178
8484
|
process.kill(-serverProcess.pid, "SIGTERM");
|
|
8179
8485
|
} catch (e) {
|
|
8180
8486
|
console.error("[Cleanup] Process group kill failed, falling back to direct kill:", e);
|
|
8181
|
-
|
|
8487
|
+
try {
|
|
8488
|
+
serverProcess.kill("SIGTERM");
|
|
8489
|
+
} catch (_) {}
|
|
8182
8490
|
}
|
|
8183
8491
|
} else {
|
|
8184
|
-
|
|
8492
|
+
try {
|
|
8493
|
+
serverProcess.kill();
|
|
8494
|
+
} catch (_) {}
|
|
8185
8495
|
}
|
|
8186
8496
|
}
|
|
8187
8497
|
}
|
|
@@ -8196,6 +8506,25 @@ process.on("SIGTERM", () => {
|
|
|
8196
8506
|
cleanupServer();
|
|
8197
8507
|
process.exit(0);
|
|
8198
8508
|
});
|
|
8509
|
+
var shuttingDown = false;
|
|
8510
|
+
function gracefulShutdown(reason, code = 0) {
|
|
8511
|
+
if (shuttingDown)
|
|
8512
|
+
return;
|
|
8513
|
+
shuttingDown = true;
|
|
8514
|
+
console.error(`[shodh-memory] ${reason}`);
|
|
8515
|
+
cleanupServer();
|
|
8516
|
+
setTimeout(() => process.exit(code), 100);
|
|
8517
|
+
}
|
|
8518
|
+
process.stdin.on("end", () => gracefulShutdown("stdin closed (MCP session ended), shutting down..."));
|
|
8519
|
+
process.stdin.on("close", () => gracefulShutdown("stdin pipe closed, shutting down..."));
|
|
8520
|
+
process.on("uncaughtException", (err) => {
|
|
8521
|
+
console.error("[shodh-memory] Uncaught exception:", err);
|
|
8522
|
+
gracefulShutdown("Shutting down after uncaught exception", 1);
|
|
8523
|
+
});
|
|
8524
|
+
process.on("unhandledRejection", (reason) => {
|
|
8525
|
+
console.error("[shodh-memory] Unhandled rejection:", reason);
|
|
8526
|
+
gracefulShutdown("Shutting down after unhandled rejection", 1);
|
|
8527
|
+
});
|
|
8199
8528
|
function createSandboxServer() {
|
|
8200
8529
|
process.env.SMITHERY_SANDBOX = "true";
|
|
8201
8530
|
return server;
|
package/package.json
CHANGED
package/scripts/postinstall.cjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const https = require('https');
|
|
12
|
-
const {
|
|
12
|
+
const { execFileSync } = require('child_process');
|
|
13
13
|
|
|
14
14
|
const VERSION = require('../package.json').version;
|
|
15
15
|
const REPO = 'varun29ankuS/shodh-memory';
|
|
@@ -71,10 +71,10 @@ function download(url, dest) {
|
|
|
71
71
|
// Extract archive
|
|
72
72
|
function extract(archive, dest, platformInfo) {
|
|
73
73
|
if (platformInfo.ext === '.tar.gz') {
|
|
74
|
-
|
|
74
|
+
execFileSync('tar', ['-xzf', archive, '-C', dest], { stdio: 'inherit' });
|
|
75
75
|
} else if (platformInfo.ext === '.zip') {
|
|
76
76
|
// Use PowerShell on Windows
|
|
77
|
-
|
|
77
|
+
execFileSync('powershell', ['-Command', `Expand-Archive -Path '${archive}' -DestinationPath '${dest}' -Force`], { stdio: 'inherit' });
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|