memorix 1.8.0 → 1.8.2

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 (45) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cli/index.js +407 -130
  3. package/dist/dashboard/static/app.js +37 -23
  4. package/dist/dashboard/static/index.html +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +633 -391
  7. package/dist/maintenance-runner.js +31 -25
  8. package/dist/media-audio-runner.js +31 -25
  9. package/dist/media-video-runner.js +31 -25
  10. package/dist/memcode-runtime/CHANGELOG.md +29 -0
  11. package/dist/sdk.js +625 -390
  12. package/dist/vector-backfill-runner.js +31 -25
  13. package/docs/1.2.0-PROVIDER-QUALITY.md +15 -6
  14. package/docs/1.3.3-MULTIMODAL-MEDIA-AND-MCP-COMPAT.md +7 -6
  15. package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +10 -8
  16. package/docs/API_REFERENCE.md +12 -8
  17. package/docs/ARCHITECTURE.md +3 -1
  18. package/docs/CONFIGURATION.md +2 -2
  19. package/docs/DASHBOARD.md +6 -0
  20. package/docs/DEVELOPMENT.md +2 -2
  21. package/docs/README.md +14 -8
  22. package/package.json +1 -1
  23. package/plugins/antigravity/memorix/plugin.json +2 -1
  24. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  25. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  26. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  27. package/plugins/copilot/memorix/plugin.json +1 -1
  28. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  29. package/plugins/hermes/memorix/plugin.yaml +1 -1
  30. package/plugins/omp/memorix/package.json +1 -1
  31. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  32. package/plugins/pi/memorix/package.json +1 -1
  33. package/server.json +3 -3
  34. package/src/cli/commands/dashboard.ts +1 -1
  35. package/src/cli/commands/serve-http.ts +38 -3
  36. package/src/codegraph/external-provider.ts +299 -19
  37. package/src/config/dotenv-loader.ts +9 -4
  38. package/src/config.ts +16 -16
  39. package/src/dashboard/maintenance.ts +4 -1
  40. package/src/dashboard/server.ts +85 -86
  41. package/src/dashboard/static/index.html +1 -1
  42. package/src/llm/provider.ts +17 -4
  43. package/src/server/mcp-compat.ts +82 -0
  44. package/src/server/mcp-diagnostics.ts +13 -4
  45. package/src/server.ts +9 -6
package/dist/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.8.0" : pkg.version;
3235
+ return true ? "1.8.2" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -21630,8 +21630,10 @@ function loadEnvFile(filePath) {
21630
21630
  loadedEnvFiles.push(filePath);
21631
21631
  }
21632
21632
  function loadDotenv(projectRoot, options = {}) {
21633
- if (dotenvLoaded && dotenvProjectRoot === (projectRoot ?? null)) return;
21634
- if (dotenvLoaded && dotenvProjectRoot !== (projectRoot ?? null)) {
21633
+ const userHomeDir = options.userHomeDir ?? homedir4();
21634
+ const normalizedProjectRoot = projectRoot ?? null;
21635
+ if (dotenvLoaded && dotenvProjectRoot === normalizedProjectRoot && dotenvHomeDir === userHomeDir) return;
21636
+ if (dotenvLoaded && (dotenvProjectRoot !== normalizedProjectRoot || dotenvHomeDir !== userHomeDir)) {
21635
21637
  resetDotenv();
21636
21638
  }
21637
21639
  loadedEnvFiles.length = 0;
@@ -21640,9 +21642,10 @@ function loadDotenv(projectRoot, options = {}) {
21640
21642
  if (projectRoot) {
21641
21643
  loadEnvFile(getProjectDotenvPath(projectRoot));
21642
21644
  }
21643
- loadEnvFile(getGlobalDotenvPath(options.userHomeDir ?? homedir4()));
21645
+ loadEnvFile(getGlobalDotenvPath(userHomeDir));
21644
21646
  dotenvLoaded = true;
21645
- dotenvProjectRoot = projectRoot ?? null;
21647
+ dotenvProjectRoot = normalizedProjectRoot;
21648
+ dotenvHomeDir = userHomeDir;
21646
21649
  }
21647
21650
  function resetDotenv() {
21648
21651
  for (const key of injectedKeys) {
@@ -21651,6 +21654,7 @@ function resetDotenv() {
21651
21654
  injectedKeys.clear();
21652
21655
  dotenvLoaded = false;
21653
21656
  dotenvProjectRoot = null;
21657
+ dotenvHomeDir = null;
21654
21658
  loadedEnvFiles.length = 0;
21655
21659
  loadedEnvKeys.clear();
21656
21660
  }
@@ -21663,7 +21667,7 @@ function getLoadedEnvKeys() {
21663
21667
  function getLoadedEnvValue(key) {
21664
21668
  return loadedEnvValues.get(key);
21665
21669
  }
21666
- var import_dotenv, dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
21670
+ var import_dotenv, dotenvLoaded, dotenvProjectRoot, dotenvHomeDir, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
21667
21671
  var init_dotenv_loader = __esm({
21668
21672
  "src/config/dotenv-loader.ts"() {
21669
21673
  "use strict";
@@ -21672,6 +21676,7 @@ var init_dotenv_loader = __esm({
21672
21676
  init_config_paths();
21673
21677
  dotenvLoaded = false;
21674
21678
  dotenvProjectRoot = null;
21679
+ dotenvHomeDir = null;
21675
21680
  loadedEnvFiles = [];
21676
21681
  loadedEnvKeys = /* @__PURE__ */ new Set();
21677
21682
  loadedEnvValues = /* @__PURE__ */ new Map();
@@ -22023,33 +22028,33 @@ __export(config_exports, {
22023
22028
  resetConfigCache: () => resetConfigCache,
22024
22029
  resetDotenv: () => resetDotenv
22025
22030
  });
22026
- function getLLMApiKey() {
22027
- return getResolvedMemoryLane().llm.apiKey;
22031
+ function getLLMApiKey(options = {}) {
22032
+ return getResolvedMemoryLane(options).llm.apiKey;
22028
22033
  }
22029
- function getLLMProvider() {
22030
- const provider2 = getResolvedMemoryLane().llm.provider;
22034
+ function getLLMProvider(options = {}) {
22035
+ const provider2 = getResolvedMemoryLane(options).llm.provider;
22031
22036
  if (provider2) return provider2;
22032
22037
  if (process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) return "anthropic";
22033
22038
  if (process.env.OPENROUTER_API_KEY && !process.env.OPENAI_API_KEY) return "openrouter";
22034
22039
  return "openai";
22035
22040
  }
22036
- function getLLMModel(providerDefault) {
22037
- return getResolvedMemoryLane().llm.model || providerDefault;
22041
+ function getLLMModel(providerDefault, options = {}) {
22042
+ return getResolvedMemoryLane(options).llm.model || providerDefault;
22038
22043
  }
22039
- function getLLMBaseUrl(providerDefault) {
22040
- return getResolvedMemoryLane().llm.baseUrl || providerDefault;
22044
+ function getLLMBaseUrl(providerDefault, options = {}) {
22045
+ return getResolvedMemoryLane(options).llm.baseUrl || providerDefault;
22041
22046
  }
22042
- function getAgentLLMApiKey() {
22043
- return getResolvedAgentLane().apiKey;
22047
+ function getAgentLLMApiKey(options = {}) {
22048
+ return getResolvedAgentLane(options).apiKey;
22044
22049
  }
22045
- function getAgentLLMProvider() {
22046
- return getResolvedAgentLane().provider || getLLMProvider();
22050
+ function getAgentLLMProvider(options = {}) {
22051
+ return getResolvedAgentLane(options).provider || getLLMProvider(options);
22047
22052
  }
22048
- function getAgentLLMModel(providerDefault) {
22049
- return getResolvedAgentLane().model || getLLMModel(providerDefault);
22053
+ function getAgentLLMModel(providerDefault, options = {}) {
22054
+ return getResolvedAgentLane(options).model || getLLMModel(providerDefault, options);
22050
22055
  }
22051
- function getAgentLLMBaseUrl(providerDefault) {
22052
- return getResolvedAgentLane().baseUrl || getLLMBaseUrl(providerDefault);
22056
+ function getAgentLLMBaseUrl(providerDefault, options = {}) {
22057
+ return getResolvedAgentLane(options).baseUrl || getLLMBaseUrl(providerDefault, options);
22053
22058
  }
22054
22059
  function getEmbeddingMode() {
22055
22060
  const provider2 = getResolvedEmbeddingLane().provider?.toLowerCase()?.trim();
@@ -24308,18 +24313,19 @@ async function* callLLMWithToolsStream(messages, tools) {
24308
24313
  }
24309
24314
  function initLLM(options = {}) {
24310
24315
  const scope = options.scope ?? "memory";
24311
- const apiKey = scope === "agent" ? getAgentLLMApiKey() : getLLMApiKey();
24316
+ const configOptions = { projectRoot: options.projectRoot, homeDir: options.homeDir };
24317
+ const apiKey = scope === "agent" ? getAgentLLMApiKey(configOptions) : getLLMApiKey(configOptions);
24312
24318
  if (!apiKey) {
24313
24319
  currentConfig = null;
24314
24320
  return null;
24315
24321
  }
24316
- const provider2 = scope === "agent" ? getAgentLLMProvider() : getLLMProvider();
24322
+ const provider2 = scope === "agent" ? getAgentLLMProvider(configOptions) : getLLMProvider(configOptions);
24317
24323
  const defaults2 = PROVIDER_DEFAULTS[provider2] ?? PROVIDER_DEFAULTS.openai;
24318
24324
  currentConfig = {
24319
24325
  provider: provider2,
24320
24326
  apiKey,
24321
- model: scope === "agent" ? getAgentLLMModel(defaults2.model) : getLLMModel(defaults2.model),
24322
- baseUrl: scope === "agent" ? getAgentLLMBaseUrl(defaults2.baseUrl) : getLLMBaseUrl(defaults2.baseUrl)
24327
+ model: scope === "agent" ? getAgentLLMModel(defaults2.model, configOptions) : getLLMModel(defaults2.model, configOptions),
24328
+ baseUrl: scope === "agent" ? getAgentLLMBaseUrl(defaults2.baseUrl, configOptions) : getLLMBaseUrl(defaults2.baseUrl, configOptions)
24323
24329
  };
24324
24330
  return currentConfig;
24325
24331
  }
@@ -197170,6 +197176,27 @@ function unavailableHealth(result) {
197170
197176
  if (result.outputLimited) return { state: "invalid", reason: "A local CodeGraph response exceeded the safety limit." };
197171
197177
  return { state: "unavailable", reason: "A local CodeGraph command did not complete." };
197172
197178
  }
197179
+ function notAttemptedDiagnostic(command) {
197180
+ return { command, attempted: false, outcome: "not-attempted" };
197181
+ }
197182
+ function commandDiagnostic(command, result, outcome, reason) {
197183
+ return {
197184
+ command,
197185
+ attempted: true,
197186
+ outcome,
197187
+ ...typeof result.exitCode === "number" ? { exitCode: result.exitCode } : {},
197188
+ ...reason ? { reason: sanitizeCredentials(reason).slice(0, 240) } : {}
197189
+ };
197190
+ }
197191
+ function failedCommandDiagnostic(command, result) {
197192
+ const outcome = result.timedOut ? "timed-out" : result.outputLimited ? "output-limited" : "failed";
197193
+ return commandDiagnostic(
197194
+ command,
197195
+ result,
197196
+ outcome,
197197
+ result.error || result.stderr || "CodeGraph command did not complete."
197198
+ );
197199
+ }
197173
197200
  async function runSafely(runner, input) {
197174
197201
  try {
197175
197202
  return await runner.run(input);
@@ -197319,6 +197346,7 @@ function parseOutline(value, projectRoot, exclude) {
197319
197346
  if (nodes.some((node) => !node)) return void 0;
197320
197347
  const byId = /* @__PURE__ */ new Map();
197321
197348
  for (const node of nodes) {
197349
+ if (byId.has(node.id)) return void 0;
197322
197350
  byId.set(node.id, externalSymbol(node));
197323
197351
  }
197324
197352
  const entryPoints = value.entryPoints.map((item) => parseNode(item, projectRoot, exclude)).filter((node) => Boolean(node)).map(externalSymbol).filter((node, index, items) => items.findIndex((item) => item.id === node.id) === index).slice(0, 5);
@@ -197332,7 +197360,7 @@ function parseOutline(value, projectRoot, exclude) {
197332
197360
  if (!source || !target || !kind || rawEdge.line !== void 0 && (!line || line < 1)) return void 0;
197333
197361
  const from = byId.get(source);
197334
197362
  const to2 = byId.get(target);
197335
- if (!from || !to2) continue;
197363
+ if (!from || !to2) return void 0;
197336
197364
  relations.push({ from, to: to2, kind, ...line ? { line } : {} });
197337
197365
  }
197338
197366
  const relatedFiles = value.relatedFiles.map((item) => safeRelativePath(projectRoot, item, exclude));
@@ -197350,45 +197378,234 @@ function parseOutline(value, projectRoot, exclude) {
197350
197378
  stats: { nodes: nodeCount, edges: edgeCount, files: fileCount }
197351
197379
  };
197352
197380
  }
197381
+ function parseTextLocation(value) {
197382
+ const match2 = value.trim().match(/^(.*?)(?::([1-9]\d*))?$/);
197383
+ if (!match2?.[1]) return void 0;
197384
+ return {
197385
+ filePath: match2[1].trim(),
197386
+ ...match2[2] ? { line: Number(match2[2]) } : {}
197387
+ };
197388
+ }
197389
+ function parseExploreText(value, projectRoot, exclude) {
197390
+ if (!value.trim() || Buffer.byteLength(value, "utf8") > MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES) return void 0;
197391
+ const entryPoints = [];
197392
+ const nodes = [];
197393
+ const relatedFiles = /* @__PURE__ */ new Set();
197394
+ const byName = /* @__PURE__ */ new Map();
197395
+ let section2 = null;
197396
+ let relationKind = "references";
197397
+ const addNode = (node, entry) => {
197398
+ const key = `${node.filePath}\0${node.name}`;
197399
+ const existing = byName.get(key);
197400
+ if (existing) return;
197401
+ byName.set(key, node);
197402
+ relatedFiles.add(node.filePath);
197403
+ (entry ? entryPoints : nodes).push(node);
197404
+ };
197405
+ for (const rawLine of value.split(/\r?\n/)) {
197406
+ const line = rawLine.trim();
197407
+ const fileHeader = line.match(/^\*\*`([^`]+)`\*\*\s+(?:—|-)\s+(.+)$/);
197408
+ if (fileHeader) {
197409
+ const filePath = safeRelativePath(projectRoot, fileHeader[1], exclude);
197410
+ if (!filePath) return void 0;
197411
+ const symbolText = fileHeader[2].split(/\s+·\s+/, 1)[0] ?? "";
197412
+ const symbols = [...symbolText.matchAll(/([^,]+?)\(([^()]+)\)/g)];
197413
+ if (symbols.length === 0) return void 0;
197414
+ const isFirstFile = entryPoints.length === 0 && nodes.length === 0;
197415
+ for (const symbol2 of symbols.slice(0, MAX_EXTERNAL_NODE_COUNT)) {
197416
+ const name = symbol2[1]?.trim();
197417
+ const kind = symbol2[2]?.trim();
197418
+ if (!name || !kind) return void 0;
197419
+ addNode({
197420
+ id: `explore:${filePath}:${name}`,
197421
+ kind,
197422
+ name,
197423
+ filePath
197424
+ }, isFirstFile);
197425
+ }
197426
+ section2 = null;
197427
+ continue;
197428
+ }
197429
+ if (/^#{2,3}\s+Entry Points?\b/i.test(line)) {
197430
+ section2 = "entry";
197431
+ continue;
197432
+ }
197433
+ if (/^#{2,3}\s+Related Symbols?\b/i.test(line)) {
197434
+ section2 = "related";
197435
+ continue;
197436
+ }
197437
+ if (/^\*\*Relationships\*\*$/i.test(line)) {
197438
+ section2 = "relationships";
197439
+ continue;
197440
+ }
197441
+ if (/^\*\*(?:Source Code|Code)\*\*$/i.test(line) || /^#{2,3}\s+/i.test(line)) {
197442
+ section2 = null;
197443
+ continue;
197444
+ }
197445
+ if (section2 === "entry") {
197446
+ const match2 = line.match(/^-\s+\*\*(.+?)\*\*\s+\(([^)]+)\)\s+-\s+(.+)$/);
197447
+ if (!match2) continue;
197448
+ const location = parseTextLocation(match2[3]);
197449
+ if (!location) return void 0;
197450
+ const filePath = safeRelativePath(projectRoot, location.filePath, exclude);
197451
+ if (!filePath) return void 0;
197452
+ addNode({
197453
+ id: `explore:${filePath}:${match2[1]}`,
197454
+ kind: match2[2],
197455
+ name: match2[1],
197456
+ filePath,
197457
+ ...location.line ? { startLine: location.line, endLine: location.line } : {}
197458
+ }, true);
197459
+ continue;
197460
+ }
197461
+ if (section2 === "related") {
197462
+ const match2 = line.match(/^-\s+([^:]+):\s+(.+)$/);
197463
+ if (!match2) continue;
197464
+ const filePath = safeRelativePath(projectRoot, match2[1], exclude);
197465
+ if (!filePath) return void 0;
197466
+ relatedFiles.add(filePath);
197467
+ for (const rawSymbol of match2[2].split(",")) {
197468
+ const symbol2 = rawSymbol.trim().match(/^(.+?)(?::([1-9]\d*))?$/);
197469
+ if (!symbol2?.[1]) return void 0;
197470
+ addNode({
197471
+ id: `explore:${filePath}:${symbol2[1].trim()}`,
197472
+ kind: "unknown",
197473
+ name: symbol2[1].trim(),
197474
+ filePath,
197475
+ ...symbol2[2] ? { startLine: Number(symbol2[2]), endLine: Number(symbol2[2]) } : {}
197476
+ }, false);
197477
+ }
197478
+ continue;
197479
+ }
197480
+ if (section2 === "relationships") {
197481
+ const kind = line.match(/^\*\*([^*]+):\*\*$/);
197482
+ if (kind?.[1]) {
197483
+ relationKind = kind[1].trim();
197484
+ continue;
197485
+ }
197486
+ }
197487
+ }
197488
+ const bySymbolName = /* @__PURE__ */ new Map();
197489
+ for (const node of [...entryPoints, ...nodes]) {
197490
+ if (!bySymbolName.has(node.name)) bySymbolName.set(node.name, node);
197491
+ }
197492
+ const relations = [];
197493
+ if (section2 === "relationships") {
197494
+ for (const rawLine of value.split(/\r?\n/)) {
197495
+ const line = rawLine.trim();
197496
+ const match2 = line.match(/^-\s+(.+?)\s+(?:\u2192|->)\s+(.+)$/);
197497
+ if (!match2) continue;
197498
+ const from = bySymbolName.get(match2[1].trim());
197499
+ const to2 = bySymbolName.get(match2[2].trim());
197500
+ if (!from || !to2) continue;
197501
+ relations.push({
197502
+ from: externalSymbol(from),
197503
+ to: externalSymbol(to2),
197504
+ kind: relationKind
197505
+ });
197506
+ }
197507
+ } else {
197508
+ const relationshipStart = value.split(/\r?\n/).findIndex((line) => /^\s*\*\*Relationships\*\*\s*$/i.test(line));
197509
+ if (relationshipStart >= 0) {
197510
+ const relationshipLines = value.split(/\r?\n/).slice(relationshipStart + 1);
197511
+ for (const rawLine of relationshipLines) {
197512
+ const line = rawLine.trim();
197513
+ if (/^\*\*(?:Source Code|Code)\*\*$/i.test(line) || /^#{2,3}\s+/i.test(line)) break;
197514
+ const kind = line.match(/^\*\*([^*]+):\*\*$/);
197515
+ if (kind?.[1]) {
197516
+ relationKind = kind[1].trim();
197517
+ continue;
197518
+ }
197519
+ const match2 = line.match(/^-\s+(.+?)\s+(?:\u2192|->)\s+(.+)$/);
197520
+ if (!match2) continue;
197521
+ const from = bySymbolName.get(match2[1].trim());
197522
+ const to2 = bySymbolName.get(match2[2].trim());
197523
+ if (from && to2) relations.push({ from: externalSymbol(from), to: externalSymbol(to2), kind: relationKind });
197524
+ }
197525
+ }
197526
+ }
197527
+ const allNodes = [...entryPoints, ...nodes];
197528
+ if (allNodes.length === 0) return void 0;
197529
+ return {
197530
+ provider: "external",
197531
+ entryPoints: entryPoints.map(externalSymbol).slice(0, 5),
197532
+ relations: relations.slice(0, 6),
197533
+ relatedFiles: [...relatedFiles].slice(0, 5),
197534
+ stats: {
197535
+ nodes: allNodes.length,
197536
+ edges: relations.length,
197537
+ files: relatedFiles.size
197538
+ }
197539
+ };
197540
+ }
197541
+ function parseExplorePayload(value, projectRoot, exclude) {
197542
+ if (typeof value === "string") return parseExploreText(value, projectRoot, exclude);
197543
+ if (isRecord3(value) && isRecord3(value.result)) value = value.result;
197544
+ if (isRecord3(value) && Array.isArray(value.content)) {
197545
+ const text2 = value.content.filter((item) => isRecord3(item) && item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n");
197546
+ return parseExploreText(text2, projectRoot, exclude);
197547
+ }
197548
+ return parseOutline(value, projectRoot, exclude);
197549
+ }
197353
197550
  function fallbackCaution(health) {
197354
197551
  if (health.state === "disabled" || health.state === "not-detected" || health.state === "not-initialized" || health.state === "ready") return void 0;
197355
197552
  return "External semantic CodeGraph is " + health.state + "; using Lite structural evidence for this brief.";
197356
197553
  }
197554
+ function queryFallbackCaution(diagnostics) {
197555
+ return `External semantic CodeGraph context was unavailable (context: ${diagnostics.context.outcome}; explore: ${diagnostics.explore.outcome}); using Lite structural evidence.`;
197556
+ }
197357
197557
  async function getExternalCodeGraphContext(input) {
197358
197558
  const inspected = await inspectExternalCodeGraph(input);
197359
197559
  if (inspected.health.state !== "ready") {
197360
197560
  return { ...inspected, caution: fallbackCaution(inspected.health) };
197361
197561
  }
197362
197562
  const mode = input.mode ?? "auto";
197363
- const result = await runSafely(input.runner ?? defaultRunner, {
197563
+ const runner = input.runner ?? defaultRunner;
197564
+ const contextResult = await runSafely(runner, {
197364
197565
  command: input.command,
197365
197566
  args: ["context", "--path", input.projectRoot, "--format", "json", "--max-nodes", "8", "--no-code", input.task],
197366
197567
  cwd: input.projectRoot,
197367
197568
  timeoutMs: boundedTimeout(input.timeoutMs),
197368
197569
  maxOutputBytes: MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES
197369
197570
  });
197370
- if (!result.ok) {
197371
- const health = unavailableHealth(result);
197571
+ const diagnostics = {
197572
+ context: contextResult.ok ? commandDiagnostic("context", contextResult, "success") : failedCommandDiagnostic("context", contextResult),
197573
+ explore: notAttemptedDiagnostic("explore")
197574
+ };
197575
+ const contextOutline = contextResult.ok ? parseOutline(parseJson2(contextResult.stdout), input.projectRoot, input.exclude) : void 0;
197576
+ if (contextOutline && (contextOutline.entryPoints.length > 0 || contextOutline.relations.length > 0 || contextOutline.relatedFiles.length > 0)) {
197372
197577
  return {
197373
- health,
197374
- quality: providerQuality({ mode, health }),
197375
- caution: fallbackCaution(health)
197578
+ ...inspected,
197579
+ outline: contextOutline,
197580
+ diagnostics,
197581
+ quality: providerQuality({ mode, health: inspected.health, selected: "external" })
197376
197582
  };
197377
197583
  }
197378
- const outline = parseOutline(parseJson2(result.stdout), input.projectRoot, input.exclude);
197379
- if (!outline) {
197380
- const health = { state: "invalid", reason: "The local CodeGraph context payload failed validation." };
197584
+ if (contextResult.ok) diagnostics.context = commandDiagnostic("context", contextResult, "invalid-output", "The context payload failed validation or contained no safe outline.");
197585
+ const exploreResult = await runSafely(runner, {
197586
+ command: input.command,
197587
+ args: ["explore", "--path", input.projectRoot, "--max-files", "4", input.task],
197588
+ cwd: input.projectRoot,
197589
+ timeoutMs: boundedTimeout(input.timeoutMs),
197590
+ maxOutputBytes: MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES
197591
+ });
197592
+ diagnostics.explore = exploreResult.ok ? commandDiagnostic("explore", exploreResult, "success") : failedCommandDiagnostic("explore", exploreResult);
197593
+ const exploreOutline = exploreResult.ok ? parseExplorePayload(parseJson2(exploreResult.stdout) ?? exploreResult.stdout, input.projectRoot, input.exclude) : void 0;
197594
+ if (exploreOutline && (exploreOutline.entryPoints.length > 0 || exploreOutline.relations.length > 0 || exploreOutline.relatedFiles.length > 0)) {
197381
197595
  return {
197382
- health,
197383
- quality: providerQuality({ mode, health }),
197384
- caution: fallbackCaution(health)
197596
+ ...inspected,
197597
+ outline: exploreOutline,
197598
+ diagnostics,
197599
+ quality: providerQuality({ mode, health: inspected.health, selected: "external" })
197385
197600
  };
197386
197601
  }
197387
- const contributes = outline.entryPoints.length > 0 || outline.relations.length > 0 || outline.relatedFiles.length > 0;
197602
+ if (exploreResult.ok) diagnostics.explore = commandDiagnostic("explore", exploreResult, "invalid-output", "The explore payload failed validation or contained no safe outline.");
197603
+ const health = exploreResult.ok ? { state: "invalid", reason: "The local CodeGraph context and explore payloads failed validation." } : unavailableHealth(exploreResult);
197388
197604
  return {
197389
- ...inspected,
197390
- ...contributes ? { outline } : {},
197391
- quality: providerQuality({ mode, health: inspected.health, ...contributes ? { selected: "external" } : {} })
197605
+ health,
197606
+ quality: providerQuality({ mode, health }),
197607
+ diagnostics,
197608
+ caution: queryFallbackCaution(diagnostics)
197392
197609
  };
197393
197610
  }
197394
197611
  var DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS, EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES, MAX_EXTERNAL_NODE_COUNT, MAX_EXTERNAL_EDGE_COUNT, MAX_EXTERNAL_FILE_COUNT, MAX_EXTERNAL_TEXT_LENGTH, LITE_SUPPORTED_LANGUAGES, defaultRunner;
@@ -197399,7 +197616,7 @@ var init_external_provider = __esm({
197399
197616
  init_secret_filter();
197400
197617
  init_exclude3();
197401
197618
  init_ids();
197402
- DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 1200;
197619
+ DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 3e3;
197403
197620
  MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 5e3;
197404
197621
  EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS = 3e4;
197405
197622
  MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES = 256 * 1024;
@@ -218992,6 +219209,51 @@ var init_request_context = __esm({
218992
219209
  }
218993
219210
  });
218994
219211
 
219212
+ // src/server/mcp-compat.ts
219213
+ function buildMcpDiscoverResult(server, serverInfo) {
219214
+ const getCapabilities = server.server.getCapabilities.bind(server.server);
219215
+ return {
219216
+ resultType: "complete",
219217
+ supportedVersions: [
219218
+ MCP_MODERN_PROTOCOL_VERSION,
219219
+ MCP_LEGACY_PROTOCOL_VERSION,
219220
+ MCP_OLDEST_LEGACY_PROTOCOL_VERSION
219221
+ ],
219222
+ capabilities: getCapabilities(),
219223
+ _meta: {
219224
+ [MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
219225
+ },
219226
+ // v1 has no response-cache negotiation. A zero private TTL is the
219227
+ // conservative result and prevents clients from inventing a shared cache.
219228
+ ttlMs: 0,
219229
+ cacheScope: "private"
219230
+ };
219231
+ }
219232
+ function registerMcpDiscovery(server, serverInfo) {
219233
+ if (registeredServers.has(server)) return;
219234
+ server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => buildMcpDiscoverResult(server, serverInfo));
219235
+ registeredServers.add(server);
219236
+ }
219237
+ var MCP_MODERN_PROTOCOL_VERSION, MCP_LEGACY_PROTOCOL_VERSION, MCP_OLDEST_LEGACY_PROTOCOL_VERSION, MCP_SERVER_DISCOVER_METHOD, MCP_SERVER_INFO_META_KEY, ServerDiscoverRequestSchema, registeredServers;
219238
+ var init_mcp_compat = __esm({
219239
+ "src/server/mcp-compat.ts"() {
219240
+ "use strict";
219241
+ init_esm_shims();
219242
+ init_types8();
219243
+ init_v4();
219244
+ MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
219245
+ MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
219246
+ MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
219247
+ MCP_SERVER_DISCOVER_METHOD = "server/discover";
219248
+ MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
219249
+ ServerDiscoverRequestSchema = RequestSchema.extend({
219250
+ method: external_exports.literal(MCP_SERVER_DISCOVER_METHOD),
219251
+ params: external_exports.looseObject({}).optional()
219252
+ });
219253
+ registeredServers = /* @__PURE__ */ new WeakSet();
219254
+ }
219255
+ });
219256
+
218995
219257
  // src/config/behavior.ts
218996
219258
  var behavior_exports = {};
218997
219259
  __export(behavior_exports, {
@@ -219278,6 +219540,7 @@ async function executeCleanup(context, payload, token) {
219278
219540
  return { action: "cleanup", projectId: context.projectId, ...result };
219279
219541
  }
219280
219542
  async function previewDeduplicate(context) {
219543
+ initLLM({ scope: "memory", projectRoot: context.projectRoot });
219281
219544
  if (!isLLMEnabled()) {
219282
219545
  return {
219283
219546
  action: "deduplicate",
@@ -220770,6 +221033,37 @@ function normalizeDashboardTask(task) {
220770
221033
  preferredRole: task.preferred_role ?? task.preferredRole ?? null
220771
221034
  };
220772
221035
  }
221036
+ function emptyTeamSnapshot(mode, scope, status) {
221037
+ return {
221038
+ status,
221039
+ mode,
221040
+ readOnly: true,
221041
+ scope,
221042
+ agents: [],
221043
+ activeCount: 0,
221044
+ recentCount: 0,
221045
+ historicalCount: 0,
221046
+ totalAgents: 0,
221047
+ recentWindowDays: 7,
221048
+ locks: [],
221049
+ tasks: [],
221050
+ availableTasks: 0,
221051
+ sessions: 0,
221052
+ roles: [],
221053
+ roleOccupancy: [],
221054
+ handoffs: [],
221055
+ openTasks: 0,
221056
+ openHandoffs: 0,
221057
+ totalUnread: 0,
221058
+ activeSessions: 0,
221059
+ error: status === "error" ? "Coordination state unavailable" : "Coordination state is incomplete",
221060
+ errorCode: status === "error" ? "COORDINATION_STATE_UNAVAILABLE" : "COORDINATION_STATE_DEGRADED"
221061
+ };
221062
+ }
221063
+ function logTeamSnapshotFailure(scope, status) {
221064
+ const safeScope = scope === "global" ? "global" : "project";
221065
+ console.error("[dashboard] coordination snapshot unavailable", { status, scope: safeScope });
221066
+ }
220773
221067
  async function buildTeamSnapshot(dataDir, projectId, scope, mode) {
220774
221068
  try {
220775
221069
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
@@ -220796,8 +221090,9 @@ async function buildTeamSnapshot(dataDir, projectId, scope, mode) {
220796
221090
  const roleOccupancy = effectiveProjectId ? teamStore.getRoleOccupancy(effectiveProjectId) : [];
220797
221091
  const handoffs = effectiveProjectId ? teamStore.listHandoffs(effectiveProjectId) : [];
220798
221092
  return {
221093
+ status: "ok",
220799
221094
  mode,
220800
- readOnly: mode === "standalone",
221095
+ readOnly: true,
220801
221096
  scope,
220802
221097
  agents: withTier,
220803
221098
  activeCount,
@@ -220818,28 +221113,8 @@ async function buildTeamSnapshot(dataDir, projectId, scope, mode) {
220818
221113
  activeSessions: activeCount
220819
221114
  };
220820
221115
  } catch {
220821
- return {
220822
- mode,
220823
- readOnly: mode === "standalone",
220824
- scope,
220825
- agents: [],
220826
- activeCount: 0,
220827
- recentCount: 0,
220828
- historicalCount: 0,
220829
- totalAgents: 0,
220830
- recentWindowDays: 7,
220831
- locks: [],
220832
- tasks: [],
220833
- availableTasks: 0,
220834
- sessions: 0,
220835
- roles: [],
220836
- roleOccupancy: [],
220837
- handoffs: [],
220838
- openTasks: 0,
220839
- openHandoffs: 0,
220840
- totalUnread: 0,
220841
- activeSessions: 0
220842
- };
221116
+ logTeamSnapshotFailure(scope, "error");
221117
+ return emptyTeamSnapshot(mode, scope, "error");
220843
221118
  }
220844
221119
  }
220845
221120
  function readBody(req) {
@@ -220858,13 +221133,12 @@ function readBody(req) {
220858
221133
  req.on("error", reject);
220859
221134
  });
220860
221135
  }
220861
- async function startDashboard(dataDir, port, staticDir, projectId, projectName, autoOpen = true, teamInstances, projectRoot = null, projectResolved = true) {
221136
+ async function startDashboard(dataDir, port, staticDir, projectId, projectName, autoOpen = true, projectRoot = null, projectResolved = true) {
220862
221137
  await initObservationStore(dataDir);
220863
221138
  await initSessionStore(dataDir);
220864
221139
  const resolvedStaticDir = staticDir;
220865
221140
  const baseDir = getBaseDataDir();
220866
- const isControlPlane = !!teamInstances;
220867
- const state = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: isControlPlane ? "control-plane" : "standalone", port };
221141
+ const state = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: "standalone", port };
220868
221142
  const server = createServer(async (req, res) => {
220869
221143
  const url2 = req.url || "/";
220870
221144
  if (url2.startsWith("/api/set-current-project") && req.method === "POST") {
@@ -220886,54 +221160,20 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
220886
221160
  }
220887
221161
  return;
220888
221162
  }
220889
- if (url2.startsWith("/api/team")) {
220890
- if (!teamInstances) {
220891
- const parsedUrl = new URL(url2, `http://127.0.0.1:${port}`);
220892
- const scope = parsedUrl.searchParams.get("scope") || "project";
220893
- sendJson(res, await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode));
220894
- return;
220895
- }
220896
- try {
220897
- teamInstances.fileLocks.cleanExpired();
220898
- const agents = teamInstances.registry.listAgents();
220899
- const locks = teamInstances.fileLocks.listLocks();
220900
- const tasks = teamInstances.taskManager.list();
220901
- const available = teamInstances.taskManager.getAvailable();
220902
- let roles = [];
220903
- let roleOccupancy = [];
220904
- let handoffs = [];
220905
- try {
220906
- const { getTeamStore: getTeamStore2, isTeamStoreInitialized: isTeamStoreInitialized2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
220907
- if (isTeamStoreInitialized2()) {
220908
- const teamStore = getTeamStore2();
220909
- const projectId2 = state.projectId;
220910
- roles = teamStore.listRoles(projectId2);
220911
- roleOccupancy = teamStore.getRoleOccupancy(projectId2);
220912
- handoffs = teamStore.listHandoffs(projectId2);
220913
- }
220914
- } catch {
220915
- }
221163
+ const parsedUrl = new URL(url2, `http://127.0.0.1:${port}`);
221164
+ if (parsedUrl.pathname === "/api/team") {
221165
+ if (req.method !== "GET") {
220916
221166
  sendJson(res, {
220917
- agents: agents.map((a4) => ({
220918
- ...a4,
220919
- unread: teamInstances.messageBus.getUnreadCount(a4.id)
220920
- })),
220921
- activeCount: teamInstances.registry.getActiveCount(),
220922
- locks,
220923
- tasks,
220924
- availableTasks: available.length,
220925
- roles,
220926
- roleOccupancy,
220927
- handoffs,
220928
- // Resume data for "Continue this project" area
220929
- openTasks: tasks.filter((t4) => t4.status === "pending" || t4.status === "in_progress").length,
220930
- openHandoffs: handoffs.filter((h5) => h5.handoff_status === "open" || h5.handoffStatus === "open").length,
220931
- totalUnread: agents.reduce((sum, a4) => sum + teamInstances.messageBus.getUnreadCount(a4.id), 0),
220932
- activeSessions: agents.filter((a4) => a4.status === "active").length
220933
- });
220934
- } catch {
220935
- sendJson(res, { agents: [], activeCount: 0, locks: [], tasks: [], availableTasks: 0, roles: [], roleOccupancy: [], handoffs: [] });
221167
+ status: "error",
221168
+ readOnly: true,
221169
+ error: "Coordination status is read-only",
221170
+ errorCode: "COORDINATION_STATUS_READ_ONLY"
221171
+ }, 405);
221172
+ return;
220936
221173
  }
221174
+ const scope = parsedUrl.searchParams.get("scope") || "project";
221175
+ const snapshot = await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode);
221176
+ sendJson(res, snapshot, snapshot.status === "error" ? 503 : 200);
220937
221177
  return;
220938
221178
  }
220939
221179
  if (url2.startsWith("/api/")) {
@@ -220954,13 +221194,12 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
220954
221194
  server.listen(port, "127.0.0.1", () => {
220955
221195
  const url2 = `http://127.0.0.1:${port}`;
220956
221196
  const resolvedLabel = projectResolved ? "resolved" : "unresolved";
220957
- const modeLabel = isControlPlane ? "Control Plane" : "Standalone";
221197
+ const modeLabel = "Standalone";
220958
221198
  console.error(` Memorix Dashboard [${modeLabel}]`);
220959
221199
  console.error(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
220960
221200
  console.error(` Mode: ${modeLabel}`);
220961
221201
  console.error(` Project: ${projectName} (${projectId}) [${resolvedLabel}]`);
220962
221202
  console.error(` Local: ${url2}`);
220963
- if (isControlPlane) console.error(` MCP: ${url2}/mcp`);
220964
221203
  console.error(` Data dir: ${dataDir}`);
220965
221204
  console.error(`
220966
221205
  Press Ctrl+C to stop
@@ -261394,10 +261633,12 @@ The path should point to a directory containing a .git folder.`
261394
261633
  isError: true
261395
261634
  };
261396
261635
  };
261397
- const server = existingServer ?? new McpServer({
261636
+ const serverInfo = {
261398
261637
  name: "memorix",
261399
- version: true ? "1.8.0" : "1.0.1"
261400
- });
261638
+ version: true ? "1.8.2" : "1.0.1"
261639
+ };
261640
+ const server = existingServer ?? new McpServer(serverInfo);
261641
+ registerMcpDiscovery(server, serverInfo);
261401
261642
  const originalRegisterTool = server.registerTool.bind(server);
261402
261643
  server.registerTool = ((name, ...args) => {
261403
261644
  if (!isToolInProfile(name, toolProfile)) {
@@ -264549,7 +264790,7 @@ ${json4}
264549
264790
  }
264550
264791
  }
264551
264792
  console.error(`[memorix] Dashboard staticDir: ${staticDir}`);
264552
- startDashboard2(projectDir2, portNum, staticDir, project.id, project.name, false, void 0, project.rootPath, projectResolved).then(() => {
264793
+ startDashboard2(projectDir2, portNum, staticDir, project.id, project.name, false, project.rootPath, projectResolved).then(() => {
264553
264794
  dashboardRunning = true;
264554
264795
  }).catch((err) => {
264555
264796
  console.error("[memorix] Dashboard error:", err);
@@ -264891,7 +265132,7 @@ ${lines.join("\n")}` }] };
264891
265132
  server.registerTool(
264892
265133
  "team_task",
264893
265134
  {
264894
- title: "Task Board",
265135
+ title: "Task Status",
264895
265136
  description: 'Create, claim, complete, or list tasks in the team task board. Supports dependencies. Action "create": create a task. Action "claim": assign to yourself (atomic, race-safe). Action "complete": mark done with result. Action "list": show tasks.',
264896
265137
  inputSchema: {
264897
265138
  action: external_exports2.enum(["create", "claim", "complete", "list"]).describe("Operation to perform"),
@@ -265775,6 +266016,7 @@ var init_server4 = __esm({
265775
266016
  init_secret_filter();
265776
266017
  init_surfaced_registry();
265777
266018
  init_request_context();
266019
+ init_mcp_compat();
265778
266020
  FORMATION_TIMEOUT_MS = parseFormationTimeoutMs(process.env.MEMORIX_FORMATION_TIMEOUT_MS);
265779
266021
  COMPACT_ON_WRITE_TIMEOUT_MS = 12e3;
265780
266022
  COMPRESSION_TIMEOUT_MS = 5e3;
@@ -266256,13 +266498,17 @@ function getMcpDiagnostics() {
266256
266498
  protocol: {
266257
266499
  current: CURRENT_MCP_PROTOCOL_VERSION,
266258
266500
  legacy: LEGACY_MCP_PROTOCOL_VERSION,
266259
- statelessRequests: "supported",
266501
+ discovery: "supported",
266502
+ versionlessRequests: "legacy-compatible",
266503
+ modernResultMetadata: "discovery-only",
266504
+ statelessRequests: "compatibility-only",
266260
266505
  statefulStreamableHttp: "supported"
266261
266506
  },
266262
266507
  capabilities: {
266263
266508
  tasks: "unsupported",
266264
266509
  polling: "unsupported",
266265
- listCache: "supported",
266510
+ subscriptions: "unsupported",
266511
+ listCache: "unsupported",
266266
266512
  explicitProjectHandle: "supported"
266267
266513
  }
266268
266514
  };
@@ -266273,7 +266519,7 @@ var init_mcp_diagnostics = __esm({
266273
266519
  "use strict";
266274
266520
  init_esm_shims();
266275
266521
  CURRENT_MCP_PROTOCOL_VERSION = "2026-07-28";
266276
- LEGACY_MCP_PROTOCOL_VERSION = "2024-11-05";
266522
+ LEGACY_MCP_PROTOCOL_VERSION = "2025-11-25";
266277
266523
  MCP_SERVER_NAME = "io.github.AVIDS2/memorix";
266278
266524
  }
266279
266525
  });
@@ -268517,9 +268763,13 @@ __export(serve_http_exports, {
268517
268763
  MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
268518
268764
  _testing: () => _testing2,
268519
268765
  default: () => serve_http_default,
268766
+ isServerDiscoverRequest: () => isServerDiscoverRequest,
268520
268767
  parseSessionTimeoutMs: () => parseSessionTimeoutMs,
268521
268768
  resolveServeHttpDefaults: () => resolveServeHttpDefaults
268522
268769
  });
268770
+ function isServerDiscoverRequest(body) {
268771
+ return Boolean(body && typeof body === "object" && !Array.isArray(body) && body.method === "server/discover");
268772
+ }
268523
268773
  function parseSessionTimeoutMs(raw) {
268524
268774
  const value = raw?.trim();
268525
268775
  if (!value) return DEFAULT_SESSION_TIMEOUT_MS;
@@ -268800,7 +269050,7 @@ var init_serve_http = __esm({
268800
269050
  }
268801
269051
  const requestedRoot = typeof req.headers["mcp-project-root"] === "string" ? req.headers["mcp-project-root"] : void 0;
268802
269052
  const root = persisted?.projectRoot ?? requestedRoot ?? projectRoot;
268803
- if (!persisted && !isInitializeRequest2(body)) {
269053
+ if (!persisted && !isInitializeRequest2(body) && !isServerDiscoverRequest(body)) {
268804
269054
  res.writeHead(400, { "Content-Type": "application/json" });
268805
269055
  res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32e3, message: "Stateless MCP requests require Mcp-Project-Handle; initialize first." }, id: null }));
268806
269056
  return;
@@ -268864,7 +269114,7 @@ var init_serve_http = __esm({
268864
269114
  const sessionId = req.headers["mcp-session-id"];
268865
269115
  const body = await parseBody(req);
268866
269116
  const requestedProtocol = req.headers["mcp-protocol-version"];
268867
- const statelessRequested = req.headers["mcp-stateless"] === "true" || requestedProtocol === CURRENT_MCP_PROTOCOL_VERSION;
269117
+ const statelessRequested = req.headers["mcp-stateless"] === "true" || requestedProtocol === CURRENT_MCP_PROTOCOL_VERSION || typeof req.headers["mcp-project-handle"] === "string" || isServerDiscoverRequest(body);
268868
269118
  if (!sessionId && statelessRequested) {
268869
269119
  await handleStatelessPost(req, res, body);
268870
269120
  return;
@@ -269160,6 +269410,15 @@ var init_serve_http = __esm({
269160
269410
  return;
269161
269411
  }
269162
269412
  if (apiPath === "/team") {
269413
+ if (req.method !== "GET") {
269414
+ sendJson2({
269415
+ status: "error",
269416
+ readOnly: true,
269417
+ error: "Coordination status is read-only",
269418
+ errorCode: "COORDINATION_STATUS_READ_ONLY"
269419
+ }, 405);
269420
+ return;
269421
+ }
269163
269422
  const { projectId: teamProjectId, dataDir: teamDataDir } = await resolveRequestProject(url2);
269164
269423
  const ts2 = await getTeamStore2(teamDataDir);
269165
269424
  const scope = url2.searchParams.get("scope") || "project";
@@ -269224,8 +269483,9 @@ var init_serve_http = __esm({
269224
269483
  const handoffs = effectiveTeamProjectId ? ts2.listHandoffs(effectiveTeamProjectId) : [];
269225
269484
  const openHandoffs = handoffs.filter((handoff) => handoff.status !== "completed").length;
269226
269485
  sendJson2({
269486
+ status: "ok",
269227
269487
  mode: "control-plane",
269228
- readOnly: false,
269488
+ readOnly: true,
269229
269489
  scope,
269230
269490
  agents: withTier,
269231
269491
  // Primary headline number
@@ -269798,6 +270058,23 @@ var init_serve_http = __esm({
269798
270058
  }
269799
270059
  sendJson2({ error: "Not found" }, 404);
269800
270060
  } catch (err) {
270061
+ if (apiPath === "/team") {
270062
+ const scope = url2.searchParams.get("scope") === "global" ? "global" : "project";
270063
+ console.error("[memorix] coordination snapshot unavailable", { status: "error", scope });
270064
+ sendJson2({
270065
+ status: "error",
270066
+ mode: "control-plane",
270067
+ readOnly: true,
270068
+ scope,
270069
+ agents: [],
270070
+ locks: [],
270071
+ tasks: [],
270072
+ handoffs: [],
270073
+ error: "Coordination state unavailable",
270074
+ errorCode: "COORDINATION_STATE_UNAVAILABLE"
270075
+ }, 503);
270076
+ return;
270077
+ }
269801
270078
  sendJson2({ error: err instanceof Error ? err.message : "Unknown error" }, 500);
269802
270079
  }
269803
270080
  }
@@ -276684,7 +276961,7 @@ var init_dashboard = __esm({
276684
276961
  }
276685
276962
  const cliDir = path45.dirname(fileURLToPath9(import.meta.url));
276686
276963
  const staticDir = path45.join(cliDir, "..", "dashboard", "static");
276687
- await startDashboard2(dataDir, port, staticDir, project.id, project.name, true, void 0, project.rootPath, true);
276964
+ await startDashboard2(dataDir, port, staticDir, project.id, project.name, true, project.rootPath, true);
276688
276965
  await new Promise(() => {
276689
276966
  });
276690
276967
  }