@memoraone/mcp 0.1.37 → 0.1.39

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.
Files changed (4) hide show
  1. package/dist/cli.cjs +2311 -515
  2. package/dist/daemon.cjs +526 -89
  3. package/dist/index.cjs +525 -88
  4. package/package.json +11 -11
package/dist/index.cjs CHANGED
@@ -78,7 +78,7 @@ var EnvSchema = import_v4.z.object({
78
78
  MEMORAONE_AGENT_NAME: import_v4.z.string().min(1).optional(),
79
79
  MEMORAONE_AGENT_TYPE: import_v4.z.string().min(1).optional(),
80
80
  MEMORAONE_SOURCE: import_v4.z.string().min(1).optional(),
81
- MEMORAONE_IDE_TYPE: import_v4.z.enum(["cursor", "copilot-vscode", "jetbrains"]).optional(),
81
+ MEMORAONE_IDE_TYPE: import_v4.z.enum(["cursor", "copilot-vscode", "jetbrains", "claude-code", "windsurf", "opencode", "codex"]).optional(),
82
82
  MEMORAONE_WORKLOG: import_v4.z.string().min(1).optional(),
83
83
  MEMORAONE_HEARTBEAT: import_v4.z.string().min(1).optional(),
84
84
  MEMORAONE_HEARTBEAT_INTERVAL_MS: import_v4.z.string().min(1).optional()
@@ -127,7 +127,9 @@ var config2 = {
127
127
  devMode: parseBooleanFlag(parsed.data.MEMORAONE_DEV_MODE, false),
128
128
  worklogEnabled: parseBooleanFlag(parsed.data.MEMORAONE_WORKLOG, true),
129
129
  heartbeatEnabled: parseBooleanFlag(parsed.data.MEMORAONE_HEARTBEAT, true),
130
- heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "30000", 10)
130
+ // Cadence is owned by LOCAL_MCP_HEARTBEAT_INTERVAL_MS in heartbeat.ts (1_000).
131
+ // Env override is accepted for forward-compat but the timer path ignores it.
132
+ heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "1000", 10)
131
133
  };
132
134
 
133
135
  // src/client/memoraClient.ts
@@ -1431,7 +1433,15 @@ var HASH_SOCKET_FILENAME_RE = new RegExp(
1431
1433
  `^mcp-[0-9a-f]{${BINDING_SOCKET_HASH_LENGTH}}\\.sock$`,
1432
1434
  "i"
1433
1435
  );
1434
- var IDE_TYPES = ["cursor", "copilot-vscode", "jetbrains"];
1436
+ var IDE_TYPES = [
1437
+ "cursor",
1438
+ "copilot-vscode",
1439
+ "jetbrains",
1440
+ "claude-code",
1441
+ "windsurf",
1442
+ "opencode",
1443
+ "codex"
1444
+ ];
1435
1445
  var IDE_TYPE_SET = new Set(IDE_TYPES);
1436
1446
  function parseIdeType(value) {
1437
1447
  if (value === void 0 || value.trim() === "" || !IDE_TYPE_SET.has(value)) {
@@ -1442,16 +1452,19 @@ function parseIdeType(value) {
1442
1452
  function resolveIdeTypeFromEnv(env2 = process.env) {
1443
1453
  return parseIdeType(env2.MEMORAONE_IDE_TYPE);
1444
1454
  }
1445
- function resolveBindingIdeType(env2 = process.env) {
1455
+ function resolveBindingIdeType(env2 = process.env, ideTypeOverride) {
1456
+ if (ideTypeOverride !== void 0) {
1457
+ return ideTypeOverride;
1458
+ }
1446
1459
  return resolveIdeTypeFromEnv(env2) ?? "";
1447
1460
  }
1448
- function getBindingSocketFilename(binding, env2 = process.env) {
1449
- const ideType = resolveBindingIdeType(env2);
1461
+ function getBindingSocketFilename(binding, env2 = process.env, ideTypeOverride) {
1462
+ const ideType = resolveBindingIdeType(env2, ideTypeOverride);
1450
1463
  const hash = hashBindingIdentity(binding.repositoryBindingId, binding.workspaceRoot, ideType);
1451
1464
  return `mcp-${hash}.sock`;
1452
1465
  }
1453
- function getBindingSocketPath(binding, env2 = process.env) {
1454
- return path10.join(BASE_DIR, getBindingSocketFilename(binding, env2));
1466
+ function getBindingSocketPath(binding, env2 = process.env, ideTypeOverride) {
1467
+ return path10.join(BASE_DIR, getBindingSocketFilename(binding, env2, ideTypeOverride));
1455
1468
  }
1456
1469
 
1457
1470
  // src/initializeBinding.ts
@@ -1770,8 +1783,79 @@ var logCommandShape = {
1770
1783
  // src/tools/bindingStatus.ts
1771
1784
  var bindingStatusShape = {};
1772
1785
 
1773
- // src/tools/handlers/postEvent.ts
1786
+ // src/tools/listTimeline.ts
1774
1787
  var import_v411 = require("zod/v4");
1788
+ var listTimelineDescription = "List timeline events for the project bound to this Local MCP installation.";
1789
+ var listTimelineInputSchema = import_v411.z.object({
1790
+ since: import_v411.z.string().optional(),
1791
+ concept: import_v411.z.string().optional(),
1792
+ kind: import_v411.z.union([import_v411.z.string(), import_v411.z.array(import_v411.z.string())]).optional(),
1793
+ sort: import_v411.z.enum(["newest", "oldest"]).optional(),
1794
+ limit: import_v411.z.number().int().min(1).max(200).optional(),
1795
+ cursor: import_v411.z.string().optional()
1796
+ }).strict();
1797
+
1798
+ // src/tools/listConcepts.ts
1799
+ var import_v412 = require("zod/v4");
1800
+ var listConceptsDescription = "List concepts for the project bound to this Local MCP installation.";
1801
+ var listConceptsInputSchema = import_v412.z.object({
1802
+ q: import_v412.z.string().min(1).optional(),
1803
+ tag: import_v412.z.string().min(1).optional(),
1804
+ parent_id: import_v412.z.string().min(1).optional(),
1805
+ limit: import_v412.z.number().int().min(1).max(200).optional(),
1806
+ cursor: import_v412.z.string().min(1).optional()
1807
+ }).strict();
1808
+
1809
+ // src/tools/getConcept.ts
1810
+ var import_v413 = require("zod/v4");
1811
+ var getConceptDescription = "Get one concept by ID from the project bound to this Local MCP installation.";
1812
+ var getConceptInputSchema = import_v413.z.object({
1813
+ id: import_v413.z.string().trim().min(1)
1814
+ }).strict();
1815
+
1816
+ // src/tools/createConceptVersion.ts
1817
+ var import_v414 = require("zod/v4");
1818
+ var createConceptVersionDescription = "Create a version of a concept in the project bound to this Local MCP installation.";
1819
+ var createConceptVersionInputSchema = import_v414.z.object({
1820
+ id: import_v414.z.string().trim().min(1),
1821
+ value: import_v414.z.unknown(),
1822
+ reason: import_v414.z.string().optional(),
1823
+ confidence: import_v414.z.number().finite().min(0).max(1).optional(),
1824
+ source_ref: import_v414.z.string().optional(),
1825
+ tags: import_v414.z.array(import_v414.z.string()).optional(),
1826
+ parent_id: import_v414.z.union([import_v414.z.string(), import_v414.z.null()]).optional()
1827
+ }).strict();
1828
+ function isJsonValue(value) {
1829
+ if (value === null) return true;
1830
+ if (typeof value === "boolean" || typeof value === "string") return true;
1831
+ if (typeof value === "number") return Number.isFinite(value);
1832
+ if (Array.isArray(value)) return value.every(isJsonValue);
1833
+ if (typeof value !== "object") return false;
1834
+ const prototype = Object.getPrototypeOf(value);
1835
+ if (prototype !== Object.prototype && prototype !== null) return false;
1836
+ return Object.values(value).every(isJsonValue);
1837
+ }
1838
+
1839
+ // src/tools/toolInventory.ts
1840
+ var LOCAL_MCP_TOOL_NAMES = [
1841
+ "memora_ask_with_memory",
1842
+ "memora_post_event",
1843
+ "memora_create_fact",
1844
+ "memora_add_personal_context",
1845
+ "memora_get_personal_context",
1846
+ "memora_log_intent",
1847
+ "memora_log_change_summary",
1848
+ "memora_log_tool_result",
1849
+ "memora_log_command",
1850
+ "memora_status",
1851
+ "memora_list_timeline",
1852
+ "memora_list_concepts",
1853
+ "memora_get_concept",
1854
+ "memora_create_concept_version"
1855
+ ];
1856
+
1857
+ // src/tools/handlers/postEvent.ts
1858
+ var import_v415 = require("zod/v4");
1775
1859
  var crypto4 = __toESM(require("crypto"), 1);
1776
1860
 
1777
1861
  // src/runContext.ts
@@ -1827,14 +1911,14 @@ function generateRunId() {
1827
1911
  }
1828
1912
 
1829
1913
  // src/tools/handlers/postEvent.ts
1830
- var postEventInputSchema = import_v411.z.object({
1831
- kind: import_v411.z.string().min(1),
1832
- actor: import_v411.z.object({
1833
- identifier: import_v411.z.string().min(1),
1834
- id: import_v411.z.string().min(1).optional()
1914
+ var postEventInputSchema = import_v415.z.object({
1915
+ kind: import_v415.z.string().min(1),
1916
+ actor: import_v415.z.object({
1917
+ identifier: import_v415.z.string().min(1),
1918
+ id: import_v415.z.string().min(1).optional()
1835
1919
  }),
1836
- content: import_v411.z.record(import_v411.z.string(), import_v411.z.any()),
1837
- metadata: import_v411.z.record(import_v411.z.string(), import_v411.z.any()).optional()
1920
+ content: import_v415.z.record(import_v415.z.string(), import_v415.z.any()),
1921
+ metadata: import_v415.z.record(import_v415.z.string(), import_v415.z.any()).optional()
1838
1922
  });
1839
1923
  function buildPostEventContentFields(content) {
1840
1924
  if (typeof content.message === "string") {
@@ -1906,10 +1990,10 @@ async function handlePostEvent(client, args) {
1906
1990
  }
1907
1991
 
1908
1992
  // src/tools/handlers/createFact.ts
1909
- var import_v412 = require("zod/v4");
1910
- var createFactInputSchema = import_v412.z.object({
1911
- content: import_v412.z.string().min(1),
1912
- metadata: import_v412.z.record(import_v412.z.string(), import_v412.z.any()).optional()
1993
+ var import_v416 = require("zod/v4");
1994
+ var createFactInputSchema = import_v416.z.object({
1995
+ content: import_v416.z.string().min(1),
1996
+ metadata: import_v416.z.record(import_v416.z.string(), import_v416.z.any()).optional()
1913
1997
  });
1914
1998
  async function handleCreateFact(client, args) {
1915
1999
  const parsed2 = createFactInputSchema.parse(args ?? {});
@@ -1954,13 +2038,13 @@ async function handleCreateFact(client, args) {
1954
2038
  }
1955
2039
 
1956
2040
  // src/tools/handlers/addPersonalContext.ts
1957
- var import_v413 = require("zod/v4");
1958
- var addPersonalContextInputSchema = import_v413.z.object({
1959
- content: import_v413.z.string().min(1),
1960
- category: import_v413.z.string().optional(),
1961
- tags: import_v413.z.array(import_v413.z.string()).optional(),
1962
- scope_type: import_v413.z.enum(["general", "project"]).optional(),
1963
- scope_id: import_v413.z.string().optional()
2041
+ var import_v417 = require("zod/v4");
2042
+ var addPersonalContextInputSchema = import_v417.z.object({
2043
+ content: import_v417.z.string().min(1),
2044
+ category: import_v417.z.string().optional(),
2045
+ tags: import_v417.z.array(import_v417.z.string()).optional(),
2046
+ scope_type: import_v417.z.enum(["general", "project"]).optional(),
2047
+ scope_id: import_v417.z.string().optional()
1964
2048
  });
1965
2049
  async function handleAddPersonalContext(client, args) {
1966
2050
  const parsed2 = addPersonalContextInputSchema.parse(args ?? {});
@@ -1996,12 +2080,12 @@ async function handleAddPersonalContext(client, args) {
1996
2080
  }
1997
2081
 
1998
2082
  // src/tools/handlers/getPersonalContext.ts
1999
- var import_v414 = require("zod/v4");
2000
- var getPersonalContextInputSchema = import_v414.z.object({
2001
- query: import_v414.z.string().optional(),
2002
- scope_type: import_v414.z.enum(["general", "project"]).optional(),
2003
- scope_id: import_v414.z.string().optional(),
2004
- limit: import_v414.z.number().int().positive().optional()
2083
+ var import_v418 = require("zod/v4");
2084
+ var getPersonalContextInputSchema = import_v418.z.object({
2085
+ query: import_v418.z.string().optional(),
2086
+ scope_type: import_v418.z.enum(["general", "project"]).optional(),
2087
+ scope_id: import_v418.z.string().optional(),
2088
+ limit: import_v418.z.number().int().positive().optional()
2005
2089
  });
2006
2090
  function buildPersonalContextPath(parsed2) {
2007
2091
  const params = new URLSearchParams();
@@ -2038,13 +2122,13 @@ async function handleGetPersonalContext(client, args) {
2038
2122
  }
2039
2123
 
2040
2124
  // src/tools/handlers/askWithMemory.ts
2041
- var import_v415 = require("zod/v4");
2042
- var askWithMemoryInputSchema = import_v415.z.object({
2043
- question: import_v415.z.string().min(1),
2044
- code_context: import_v415.z.object({
2045
- file_path: import_v415.z.string().optional(),
2046
- selected_text: import_v415.z.string().optional(),
2047
- language: import_v415.z.string().optional()
2125
+ var import_v419 = require("zod/v4");
2126
+ var askWithMemoryInputSchema = import_v419.z.object({
2127
+ question: import_v419.z.string().min(1),
2128
+ code_context: import_v419.z.object({
2129
+ file_path: import_v419.z.string().optional(),
2130
+ selected_text: import_v419.z.string().optional(),
2131
+ language: import_v419.z.string().optional()
2048
2132
  }).optional()
2049
2133
  });
2050
2134
  function isAskWithMemoryResponse(value) {
@@ -2080,13 +2164,13 @@ async function handleAskWithMemory(client, args) {
2080
2164
  }
2081
2165
 
2082
2166
  // src/tools/handlers/logIntent.ts
2083
- var import_v416 = require("zod/v4");
2084
- var logIntentInputSchema = import_v416.z.object({
2085
- intent: import_v416.z.enum(["task", "decision"]),
2086
- message: import_v416.z.string().min(1),
2087
- context: import_v416.z.record(import_v416.z.string(), import_v416.z.any()).optional(),
2088
- intent_source: import_v416.z.string().optional().default("cursor_chat"),
2089
- run_id: import_v416.z.string().min(1).optional()
2167
+ var import_v420 = require("zod/v4");
2168
+ var logIntentInputSchema = import_v420.z.object({
2169
+ intent: import_v420.z.enum(["task", "decision"]),
2170
+ message: import_v420.z.string().min(1),
2171
+ context: import_v420.z.record(import_v420.z.string(), import_v420.z.any()).optional(),
2172
+ intent_source: import_v420.z.string().optional().default("cursor_chat"),
2173
+ run_id: import_v420.z.string().min(1).optional()
2090
2174
  });
2091
2175
  async function handleLogIntent(client, args) {
2092
2176
  const parsed2 = logIntentInputSchema.parse(args ?? {});
@@ -2128,18 +2212,18 @@ async function handleLogIntent(client, args) {
2128
2212
  }
2129
2213
 
2130
2214
  // src/tools/handlers/logChangeSummary.ts
2131
- var import_v417 = require("zod/v4");
2132
- var logChangeSummaryInputSchema = import_v417.z.object({
2133
- summary: import_v417.z.string().min(1),
2134
- scope: import_v417.z.string().min(1).optional(),
2135
- files: import_v417.z.array(import_v417.z.string().min(1)).optional(),
2136
- stats: import_v417.z.object({
2137
- files: import_v417.z.number().int().nonnegative().optional(),
2138
- add: import_v417.z.number().int().nonnegative().optional(),
2139
- del: import_v417.z.number().int().nonnegative().optional()
2215
+ var import_v421 = require("zod/v4");
2216
+ var logChangeSummaryInputSchema = import_v421.z.object({
2217
+ summary: import_v421.z.string().min(1),
2218
+ scope: import_v421.z.string().min(1).optional(),
2219
+ files: import_v421.z.array(import_v421.z.string().min(1)).optional(),
2220
+ stats: import_v421.z.object({
2221
+ files: import_v421.z.number().int().nonnegative().optional(),
2222
+ add: import_v421.z.number().int().nonnegative().optional(),
2223
+ del: import_v421.z.number().int().nonnegative().optional()
2140
2224
  }).optional(),
2141
- commit: import_v417.z.string().min(1).optional(),
2142
- run_id: import_v417.z.string().min(1).optional()
2225
+ commit: import_v421.z.string().min(1).optional(),
2226
+ run_id: import_v421.z.string().min(1).optional()
2143
2227
  });
2144
2228
  async function handleLogChangeSummary(client, args) {
2145
2229
  const parsed2 = logChangeSummaryInputSchema.parse(args ?? {});
@@ -2174,17 +2258,17 @@ async function handleLogChangeSummary(client, args) {
2174
2258
  }
2175
2259
 
2176
2260
  // src/tools/handlers/logToolResult.ts
2177
- var import_v418 = require("zod/v4");
2178
- var logToolResultInputSchema = import_v418.z.object({
2179
- tool: import_v418.z.string().min(1),
2180
- status: import_v418.z.enum(["ok", "error", "partial"]),
2181
- summary: import_v418.z.string().min(1),
2182
- run_id: import_v418.z.string().min(1).optional(),
2183
- duration_ms: import_v418.z.number().int().nonnegative().optional(),
2184
- error_code: import_v418.z.string().min(1).optional(),
2185
- error_message: import_v418.z.string().min(1).optional(),
2186
- error_kind: import_v418.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
2187
- stats: import_v418.z.record(import_v418.z.string(), import_v418.z.any()).optional()
2261
+ var import_v422 = require("zod/v4");
2262
+ var logToolResultInputSchema = import_v422.z.object({
2263
+ tool: import_v422.z.string().min(1),
2264
+ status: import_v422.z.enum(["ok", "error", "partial"]),
2265
+ summary: import_v422.z.string().min(1),
2266
+ run_id: import_v422.z.string().min(1).optional(),
2267
+ duration_ms: import_v422.z.number().int().nonnegative().optional(),
2268
+ error_code: import_v422.z.string().min(1).optional(),
2269
+ error_message: import_v422.z.string().min(1).optional(),
2270
+ error_kind: import_v422.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
2271
+ stats: import_v422.z.record(import_v422.z.string(), import_v422.z.any()).optional()
2188
2272
  });
2189
2273
  async function handleLogToolResult(client, args) {
2190
2274
  const parsed2 = logToolResultInputSchema.parse(args ?? {});
@@ -2222,15 +2306,15 @@ async function handleLogToolResult(client, args) {
2222
2306
  }
2223
2307
 
2224
2308
  // src/tools/handlers/logCommand.ts
2225
- var import_v419 = require("zod/v4");
2226
- var logCommandInputSchema = import_v419.z.object({
2227
- cmd: import_v419.z.string().min(1),
2228
- summary: import_v419.z.string().min(1),
2229
- cwd: import_v419.z.string().min(1).optional(),
2230
- exit_code: import_v419.z.number().int().optional(),
2231
- duration_ms: import_v419.z.number().int().nonnegative().optional(),
2232
- run_id: import_v419.z.string().min(1).optional(),
2233
- stats: import_v419.z.record(import_v419.z.string(), import_v419.z.any()).optional()
2309
+ var import_v423 = require("zod/v4");
2310
+ var logCommandInputSchema = import_v423.z.object({
2311
+ cmd: import_v423.z.string().min(1),
2312
+ summary: import_v423.z.string().min(1),
2313
+ cwd: import_v423.z.string().min(1).optional(),
2314
+ exit_code: import_v423.z.number().int().optional(),
2315
+ duration_ms: import_v423.z.number().int().nonnegative().optional(),
2316
+ run_id: import_v423.z.string().min(1).optional(),
2317
+ stats: import_v423.z.record(import_v423.z.string(), import_v423.z.any()).optional()
2234
2318
  });
2235
2319
  async function handleLogCommand(client, args) {
2236
2320
  const parsed2 = logCommandInputSchema.parse(args ?? {});
@@ -2265,6 +2349,244 @@ async function handleLogCommand(client, args) {
2265
2349
  return { ok: true };
2266
2350
  }
2267
2351
 
2352
+ // src/tools/responseProjection.ts
2353
+ var timelineItemKeys = [
2354
+ "id",
2355
+ "ts",
2356
+ "kind",
2357
+ "concept",
2358
+ "old_value",
2359
+ "new_value",
2360
+ "reason",
2361
+ "confidence",
2362
+ "links",
2363
+ "source_ref",
2364
+ "redacted",
2365
+ "redaction_reason",
2366
+ "summary",
2367
+ "consolidated_of"
2368
+ ];
2369
+ var conceptVersionKeys = [
2370
+ "version",
2371
+ "ts",
2372
+ "reason",
2373
+ "confidence",
2374
+ "source_ref",
2375
+ "value"
2376
+ ];
2377
+ function invalidResponse() {
2378
+ throw new Error("Invalid Memora response");
2379
+ }
2380
+ function isRecord(value) {
2381
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2382
+ }
2383
+ function pickFields(raw, keys) {
2384
+ const result = {};
2385
+ for (const key of keys) {
2386
+ if (Object.prototype.hasOwnProperty.call(raw, key) && raw[key] !== void 0) {
2387
+ result[key] = raw[key];
2388
+ }
2389
+ }
2390
+ return result;
2391
+ }
2392
+ function pickConceptVersion(raw) {
2393
+ return pickFields(raw, conceptVersionKeys);
2394
+ }
2395
+ function pickConceptItem(raw) {
2396
+ if (typeof raw.id !== "string" || raw.id.trim() === "") invalidResponse();
2397
+ const item = { id: raw.id };
2398
+ if (Object.prototype.hasOwnProperty.call(raw, "tags")) {
2399
+ if (!Array.isArray(raw.tags) || !raw.tags.every((tag) => typeof tag === "string")) {
2400
+ invalidResponse();
2401
+ }
2402
+ item.tags = raw.tags;
2403
+ }
2404
+ if (Object.prototype.hasOwnProperty.call(raw, "parent_id")) {
2405
+ if (raw.parent_id !== null && typeof raw.parent_id !== "string") invalidResponse();
2406
+ item.parent_id = raw.parent_id;
2407
+ }
2408
+ if (Object.prototype.hasOwnProperty.call(raw, "latest")) {
2409
+ if (raw.latest === null) {
2410
+ item.latest = null;
2411
+ } else if (isRecord(raw.latest)) {
2412
+ item.latest = pickConceptVersion(raw.latest);
2413
+ } else {
2414
+ invalidResponse();
2415
+ }
2416
+ }
2417
+ return item;
2418
+ }
2419
+ function normalizeCursor(raw) {
2420
+ for (const key of ["next_cursor", "nextCursor"]) {
2421
+ if (!Object.prototype.hasOwnProperty.call(raw, key)) continue;
2422
+ const value = raw[key];
2423
+ if (value === null) return null;
2424
+ if (typeof value === "string") return value;
2425
+ invalidResponse();
2426
+ }
2427
+ return null;
2428
+ }
2429
+ function projectTimelineResponse(data) {
2430
+ if (!isRecord(data) || !Array.isArray(data.items) || !isRecord(data.page)) {
2431
+ invalidResponse();
2432
+ }
2433
+ if (typeof data.page.limit !== "number" || !Number.isFinite(data.page.limit) || data.page.sort !== "newest" && data.page.sort !== "oldest") {
2434
+ invalidResponse();
2435
+ }
2436
+ const items = data.items.map((entry) => {
2437
+ if (!isRecord(entry)) invalidResponse();
2438
+ return pickFields(entry, timelineItemKeys);
2439
+ });
2440
+ return {
2441
+ items,
2442
+ page: {
2443
+ limit: data.page.limit,
2444
+ sort: data.page.sort,
2445
+ next_cursor: normalizeCursor(data.page)
2446
+ }
2447
+ };
2448
+ }
2449
+ function projectConceptListResponse(data, requestedLimit) {
2450
+ if (!isRecord(data)) invalidResponse();
2451
+ let entries;
2452
+ if (Object.prototype.hasOwnProperty.call(data, "rows")) {
2453
+ entries = data.rows;
2454
+ } else if (Object.prototype.hasOwnProperty.call(data, "items")) {
2455
+ entries = data.items;
2456
+ } else {
2457
+ invalidResponse();
2458
+ }
2459
+ if (!Array.isArray(entries)) invalidResponse();
2460
+ let cursorSource = data;
2461
+ if (!Object.prototype.hasOwnProperty.call(data, "next_cursor") && !Object.prototype.hasOwnProperty.call(data, "nextCursor") && Object.prototype.hasOwnProperty.call(data, "page")) {
2462
+ if (!isRecord(data.page)) invalidResponse();
2463
+ cursorSource = data.page;
2464
+ }
2465
+ return {
2466
+ items: entries.map((entry) => {
2467
+ if (!isRecord(entry)) invalidResponse();
2468
+ return pickConceptItem(entry);
2469
+ }),
2470
+ page: {
2471
+ limit: requestedLimit ?? 50,
2472
+ next_cursor: normalizeCursor(cursorSource)
2473
+ }
2474
+ };
2475
+ }
2476
+ function projectConceptResponse(data) {
2477
+ if (!isRecord(data)) invalidResponse();
2478
+ const item = pickConceptItem(data);
2479
+ if (!Object.prototype.hasOwnProperty.call(data, "history")) {
2480
+ return { ...item, history: [] };
2481
+ }
2482
+ if (!Array.isArray(data.history)) invalidResponse();
2483
+ const history = data.history.map((entry) => {
2484
+ if (!isRecord(entry)) invalidResponse();
2485
+ return pickConceptVersion(entry);
2486
+ });
2487
+ return { ...item, history };
2488
+ }
2489
+ function projectCreatedConceptVersionResponse(data) {
2490
+ if (!isRecord(data)) invalidResponse();
2491
+ return pickConceptItem(data);
2492
+ }
2493
+
2494
+ // src/tools/handlers/toolRequestError.ts
2495
+ function rethrowToolRequestError(toolName, error) {
2496
+ if (error instanceof SyntaxError) {
2497
+ throw new Error("Invalid Memora response");
2498
+ }
2499
+ if (!(error instanceof MemoraOneHttpError)) throw error;
2500
+ let detail = "request failed";
2501
+ if (error.status === 401) detail = "authentication failed";
2502
+ else if (error.status === 403) detail = "connection unavailable";
2503
+ else if (error.status === 404) detail = "resource not found";
2504
+ else if (error.status === 409) detail = "conflict";
2505
+ else if (error.status >= 500) detail = "backend error";
2506
+ throw new Error(`${toolName} failed: ${error.status} ${detail}`);
2507
+ }
2508
+
2509
+ // src/tools/handlers/listTimeline.ts
2510
+ function buildQuery(input) {
2511
+ const parts = [];
2512
+ const append = (key, value) => {
2513
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
2514
+ };
2515
+ if (input.since !== void 0) append("since", input.since);
2516
+ if (input.concept !== void 0) append("concept", input.concept);
2517
+ if (input.kind !== void 0) {
2518
+ for (const kind of Array.isArray(input.kind) ? input.kind : [input.kind]) {
2519
+ append("kind", kind);
2520
+ }
2521
+ }
2522
+ if (input.sort !== void 0) append("sort", input.sort);
2523
+ if (input.limit !== void 0) append("limit", String(input.limit));
2524
+ if (input.cursor !== void 0) append("cursor", input.cursor);
2525
+ return parts.length === 0 ? "" : `?${parts.join("&")}`;
2526
+ }
2527
+ async function handleListTimeline(client, args) {
2528
+ const input = listTimelineInputSchema.parse(args ?? {});
2529
+ try {
2530
+ return projectTimelineResponse(await client.get(`/timeline${buildQuery(input)}`));
2531
+ } catch (error) {
2532
+ rethrowToolRequestError("memora_list_timeline", error);
2533
+ }
2534
+ }
2535
+
2536
+ // src/tools/handlers/listConcepts.ts
2537
+ function buildQuery2(input) {
2538
+ const parts = [];
2539
+ const append = (key, value) => {
2540
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
2541
+ };
2542
+ if (input.q !== void 0) append("q", input.q);
2543
+ if (input.tag !== void 0) append("tag", input.tag);
2544
+ if (input.parent_id !== void 0) append("parent_id", input.parent_id);
2545
+ if (input.limit !== void 0) append("limit", String(input.limit));
2546
+ if (input.cursor !== void 0) append("cursor", input.cursor);
2547
+ return parts.length === 0 ? "" : `?${parts.join("&")}`;
2548
+ }
2549
+ async function handleListConcepts(client, args) {
2550
+ const input = listConceptsInputSchema.parse(args ?? {});
2551
+ try {
2552
+ const data = await client.get(`/concepts${buildQuery2(input)}`);
2553
+ return projectConceptListResponse(data, input.limit);
2554
+ } catch (error) {
2555
+ rethrowToolRequestError("memora_list_concepts", error);
2556
+ }
2557
+ }
2558
+
2559
+ // src/tools/handlers/getConcept.ts
2560
+ async function handleGetConcept(client, args) {
2561
+ const input = getConceptInputSchema.parse(args ?? {});
2562
+ try {
2563
+ const data = await client.get(`/concepts/${encodeURIComponent(input.id)}`);
2564
+ return projectConceptResponse(data);
2565
+ } catch (error) {
2566
+ rethrowToolRequestError("memora_get_concept", error);
2567
+ }
2568
+ }
2569
+
2570
+ // src/tools/handlers/createConceptVersion.ts
2571
+ async function handleCreateConceptVersion(client, args) {
2572
+ const input = createConceptVersionInputSchema.parse(args ?? {});
2573
+ if (!isJsonValue(input.value)) {
2574
+ throw new Error("Invalid arguments: value must be a valid JSON value");
2575
+ }
2576
+ const body = { value: input.value };
2577
+ if (input.reason !== void 0) body.reason = input.reason;
2578
+ if (input.confidence !== void 0) body.confidence = input.confidence;
2579
+ if (input.source_ref !== void 0) body.source_ref = input.source_ref;
2580
+ if (input.tags !== void 0) body.tags = input.tags;
2581
+ if (input.parent_id !== void 0) body.parent_id = input.parent_id;
2582
+ try {
2583
+ const path14 = `/concepts/${encodeURIComponent(input.id)}/versions`;
2584
+ return projectCreatedConceptVersionResponse(await client.post(path14, body));
2585
+ } catch (error) {
2586
+ rethrowToolRequestError("memora_create_concept_version", error);
2587
+ }
2588
+ }
2589
+
2268
2590
  // src/tools/handlers/bindingStatus.ts
2269
2591
  function buildBindingStatus(binding, options = {}) {
2270
2592
  const status = {
@@ -2315,8 +2637,9 @@ function isHeartbeatDebugEnabled() {
2315
2637
  const value = String(process.env.MEMORAONE_DEBUG_HEARTBEAT ?? "").trim().toLowerCase();
2316
2638
  return ["1", "true", "yes", "on"].includes(value);
2317
2639
  }
2640
+ var LOCAL_MCP_HEARTBEAT_INTERVAL_MS = 1e3;
2318
2641
  function resolveHeartbeatIntervalMs() {
2319
- return Number.isFinite(config2.heartbeatIntervalMs) ? Math.max(1e3, config2.heartbeatIntervalMs) : 3e4;
2642
+ return LOCAL_MCP_HEARTBEAT_INTERVAL_MS;
2320
2643
  }
2321
2644
  function redactSensitiveText(text) {
2322
2645
  return text.replace(/mcs_[A-Za-z0-9_-]+/g, "mcs_[redacted]").replace(/mia_[A-Za-z0-9_-]+/g, "mia_[redacted]").replace(/mir_[A-Za-z0-9_-]+/g, "mir_[redacted]").replace(/mcc_[A-Za-z0-9_-]+/g, "mcc_[redacted]").replace(/Bearer\s+\S+/gi, "Bearer [redacted]");
@@ -2426,6 +2749,7 @@ async function sendProjectHeartbeat(client, ctx) {
2426
2749
  }
2427
2750
  function createDaemonHeartbeat(opts) {
2428
2751
  let interval = null;
2752
+ let runGeneration = 0;
2429
2753
  let client = null;
2430
2754
  let announced = false;
2431
2755
  let studioActive = null;
@@ -2494,21 +2818,24 @@ function createDaemonHeartbeat(opts) {
2494
2818
  studioActive = true;
2495
2819
  }
2496
2820
  };
2497
- const tick = async () => {
2498
- if (!client) return;
2821
+ const tick = async (generation) => {
2822
+ if (!client || generation !== runGeneration) return;
2499
2823
  const outcome = await sendProjectHeartbeat(client, ctx);
2824
+ if (generation !== runGeneration || !interval) return;
2500
2825
  applyHeartbeatOutcome(outcome);
2501
2826
  };
2502
2827
  const beginInterval = () => {
2503
2828
  if (interval) return;
2504
2829
  const intervalMs = resolveHeartbeatIntervalMs();
2830
+ const generation = runGeneration;
2505
2831
  log(
2506
2832
  `daemon owns heartbeat for binding=${opts.binding.repositoryBindingId} project=${opts.binding.projectId} ideType=${ctx.ideType ?? "unknown"} interval=${intervalMs}ms`
2507
2833
  );
2508
- void tick();
2509
2834
  interval = setInterval(() => {
2510
- void tick();
2835
+ void tick(generation);
2511
2836
  }, intervalMs);
2837
+ interval.unref?.();
2838
+ void tick(generation);
2512
2839
  };
2513
2840
  const start = async () => {
2514
2841
  if (!config2.heartbeatEnabled) {
@@ -2541,6 +2868,7 @@ function createDaemonHeartbeat(opts) {
2541
2868
  }
2542
2869
  };
2543
2870
  const stop = () => {
2871
+ runGeneration += 1;
2544
2872
  if (interval) {
2545
2873
  clearInterval(interval);
2546
2874
  interval = null;
@@ -2560,7 +2888,7 @@ function createDaemonHeartbeat(opts) {
2560
2888
  return;
2561
2889
  }
2562
2890
  if (client && interval) {
2563
- void tick();
2891
+ void tick(runGeneration);
2564
2892
  }
2565
2893
  };
2566
2894
  const getIdeType = () => ctx.ideType;
@@ -2580,12 +2908,52 @@ function createDaemonHeartbeat(opts) {
2580
2908
  }
2581
2909
 
2582
2910
  // src/ideType.ts
2911
+ function mapReliableClientInfoName(name) {
2912
+ if (typeof name !== "string") {
2913
+ return void 0;
2914
+ }
2915
+ const normalized = name.trim().toLowerCase();
2916
+ if (normalized === "") {
2917
+ return void 0;
2918
+ }
2919
+ if (normalized === "claude-code") {
2920
+ return "claude-code";
2921
+ }
2922
+ if (normalized === "devin") {
2923
+ return "windsurf";
2924
+ }
2925
+ if (normalized === "windsurf" || /^windsurf[\s_-].+$/.test(normalized)) {
2926
+ return "windsurf";
2927
+ }
2928
+ return void 0;
2929
+ }
2930
+ function mapReliableHostIdentity(env2) {
2931
+ if (env2.WINDSURF_IDE_TYPE === "windsurf") {
2932
+ return "windsurf";
2933
+ }
2934
+ if (env2.ACP_BACKEND === "windsurf") {
2935
+ return "windsurf";
2936
+ }
2937
+ if (env2.__CFBundleIdentifier === "com.exafunction.windsurf") {
2938
+ return "windsurf";
2939
+ }
2940
+ return void 0;
2941
+ }
2583
2942
  function inferIdeType(params, options = {}) {
2584
2943
  const env2 = options.env ?? process.env;
2585
2944
  const argv = (options.argv ?? process.argv).join(" ").toLowerCase();
2586
- const configIdeType = options.configIdeType ?? config2.ideType;
2587
- if (configIdeType) {
2588
- return configIdeType;
2945
+ const hasExplicitConfigOption = Object.prototype.hasOwnProperty.call(options, "configIdeType");
2946
+ const explicitHint = hasExplicitConfigOption ? options.configIdeType : resolveIdeTypeFromEnv(env2) ?? config2.ideType;
2947
+ const fromClientInfo = mapReliableClientInfoName(params?.clientInfo?.name);
2948
+ if (fromClientInfo) {
2949
+ return fromClientInfo;
2950
+ }
2951
+ const fromHost = mapReliableHostIdentity(env2);
2952
+ if (fromHost) {
2953
+ return fromHost;
2954
+ }
2955
+ if (explicitHint) {
2956
+ return explicitHint;
2589
2957
  }
2590
2958
  const clientInfoName = String(params?.clientInfo?.name ?? "").toLowerCase();
2591
2959
  const clientInfoVersion = String(params?.clientInfo?.version ?? "").toLowerCase();
@@ -2742,7 +3110,11 @@ async function main(opts = {}) {
2742
3110
  `[memoraone-mcp] refreshed stale cached binding ${reconciled.binding.repositoryBindingId}: project=${reconciled.binding.projectId}`
2743
3111
  );
2744
3112
  try {
2745
- const socketPath = getBindingSocketPath(opts.daemonBindingHint);
3113
+ const socketPath = getBindingSocketPath(
3114
+ opts.daemonBindingHint,
3115
+ process.env,
3116
+ runtime.ideType ?? ""
3117
+ );
2746
3118
  writeBindingSidecar(socketPath, reconciled.binding, runtime.ideType ?? "");
2747
3119
  } catch (err) {
2748
3120
  console.error(
@@ -2935,6 +3307,71 @@ async function main(opts = {}) {
2935
3307
  }
2936
3308
  );
2937
3309
  registeredToolNames.push("memora_log_command");
3310
+ server.registerTool(
3311
+ "memora_list_timeline",
3312
+ {
3313
+ description: listTimelineDescription,
3314
+ inputSchema: listTimelineInputSchema
3315
+ },
3316
+ async (args) => runWithSessionContext(sessionContext, async () => {
3317
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3318
+ const result = await handleListTimeline(runtime.client, args);
3319
+ return {
3320
+ content: [{ type: "text", text: JSON.stringify(result) }]
3321
+ };
3322
+ })
3323
+ );
3324
+ registeredToolNames.push("memora_list_timeline");
3325
+ server.registerTool(
3326
+ "memora_list_concepts",
3327
+ {
3328
+ description: listConceptsDescription,
3329
+ inputSchema: listConceptsInputSchema
3330
+ },
3331
+ async (args) => runWithSessionContext(sessionContext, async () => {
3332
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3333
+ const result = await handleListConcepts(runtime.client, args);
3334
+ return {
3335
+ content: [{ type: "text", text: JSON.stringify(result) }]
3336
+ };
3337
+ })
3338
+ );
3339
+ registeredToolNames.push("memora_list_concepts");
3340
+ server.registerTool(
3341
+ "memora_get_concept",
3342
+ {
3343
+ description: getConceptDescription,
3344
+ inputSchema: getConceptInputSchema
3345
+ },
3346
+ async (args) => runWithSessionContext(sessionContext, async () => {
3347
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3348
+ const result = await handleGetConcept(runtime.client, args);
3349
+ return {
3350
+ content: [{ type: "text", text: JSON.stringify(result) }]
3351
+ };
3352
+ })
3353
+ );
3354
+ registeredToolNames.push("memora_get_concept");
3355
+ server.registerTool(
3356
+ "memora_create_concept_version",
3357
+ {
3358
+ description: createConceptVersionDescription,
3359
+ inputSchema: createConceptVersionInputSchema
3360
+ },
3361
+ async (args) => runWithSessionContext(sessionContext, async () => {
3362
+ if (!runtime.client || !runtime.projectId) return notInitializedResult;
3363
+ const result = await handleCreateConceptVersion(runtime.client, args);
3364
+ return {
3365
+ content: [{ type: "text", text: JSON.stringify(result) }]
3366
+ };
3367
+ })
3368
+ );
3369
+ registeredToolNames.push("memora_create_concept_version");
3370
+ if (registeredToolNames.length !== LOCAL_MCP_TOOL_NAMES.length || registeredToolNames.some(
3371
+ (name) => !LOCAL_MCP_TOOL_NAMES.includes(name)
3372
+ )) {
3373
+ throw new Error("Local MCP tool registration inventory mismatch");
3374
+ }
2938
3375
  server.server.setRequestHandler(
2939
3376
  import_types.InitializeRequestSchema,
2940
3377
  async (request) => runWithSessionContext(sessionContext, async () => {