@pcircle/memesh 4.8.3 → 4.8.5
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +8 -3
- package/README.de.md +2 -1
- package/README.md +2 -1
- package/README.zh-TW.md +2 -1
- package/dashboard/dist/index.html +3 -3
- package/dist/core/agent-message-inbox.d.ts +2 -1
- package/dist/core/agent-message-inbox.d.ts.map +1 -1
- package/dist/core/agent-message-inbox.js +23 -4
- package/dist/core/agent-message-inbox.js.map +1 -1
- package/dist/core/agent-messaging.d.ts.map +1 -1
- package/dist/core/agent-messaging.js +23 -15
- package/dist/core/agent-messaging.js.map +1 -1
- package/dist/core/agent-scope-id.d.ts +11 -0
- package/dist/core/agent-scope-id.d.ts.map +1 -0
- package/dist/core/agent-scope-id.js +40 -0
- package/dist/core/agent-scope-id.js.map +1 -0
- package/dist/core/briefing.d.ts.map +1 -1
- package/dist/core/briefing.js +8 -2
- package/dist/core/briefing.js.map +1 -1
- package/dist/core/doctor.d.ts +1 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +154 -16
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +3 -1
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +4 -1
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +8 -5
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +3 -1
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/ollama-host.d.ts +1 -1
- package/dist/core/ollama-host.d.ts.map +1 -1
- package/dist/core/ollama-host.js +46 -14
- package/dist/core/ollama-host.js.map +1 -1
- package/dist/core/operations.d.ts +1 -1
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +1 -1
- package/dist/core/operations.js.map +1 -1
- package/dist/core/project-tags.d.ts +2 -0
- package/dist/core/project-tags.d.ts.map +1 -1
- package/dist/core/project-tags.js +29 -1
- package/dist/core/project-tags.js.map +1 -1
- package/dist/core/schema-export.js +3 -3
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/version-check.d.ts +1 -0
- package/dist/core/version-check.d.ts.map +1 -1
- package/dist/core/version-check.js +35 -0
- package/dist/core/version-check.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +5 -2
- package/dist/db.js.map +1 -1
- package/dist/host-runtime/acp.d.ts.map +1 -1
- package/dist/host-runtime/acp.js +2 -1
- package/dist/host-runtime/acp.js.map +1 -1
- package/dist/host-runtime/claude.d.ts.map +1 -1
- package/dist/host-runtime/claude.js +6 -8
- package/dist/host-runtime/claude.js.map +1 -1
- package/dist/host-runtime/codex.d.ts.map +1 -1
- package/dist/host-runtime/codex.js +2 -1
- package/dist/host-runtime/codex.js.map +1 -1
- package/dist/host-runtime/entry.d.ts +5 -0
- package/dist/host-runtime/entry.d.ts.map +1 -0
- package/dist/host-runtime/entry.js +11 -0
- package/dist/host-runtime/entry.js.map +1 -0
- package/dist/knowledge-graph.d.ts +0 -1
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +27 -19
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/skills-manifest.json +29 -24
- package/dist/storage/entity-index.d.ts +4 -0
- package/dist/storage/entity-index.d.ts.map +1 -0
- package/dist/storage/entity-index.js +15 -0
- package/dist/storage/entity-index.js.map +1 -0
- package/dist/storage/fts-index.d.ts.map +1 -1
- package/dist/storage/fts-index.js +5 -0
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/storage/graph-repairs.d.ts +11 -1
- package/dist/storage/graph-repairs.d.ts.map +1 -1
- package/dist/storage/graph-repairs.js +172 -9
- package/dist/storage/graph-repairs.js.map +1 -1
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +23 -12
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +3 -3
- package/dist/transports/mcp/handlers.js +3 -3
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +19 -19
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +18 -11
- package/dist/transports/schemas.js.map +1 -1
- package/docs/platforms/README.md +1 -1
- package/docs/platforms/agent-messaging.md +157 -0
- package/hooks/hooks.json +10 -0
- package/package.json +9 -5
- package/scripts/hooks/_generated/agent-message-inbox.js +23 -4
- package/scripts/hooks/_generated/fts-index.js +5 -0
- package/scripts/hooks/_shared.js +51 -3
- package/scripts/hooks/decision-nudge.js +152 -0
- package/scripts/hooks/post-commit.js +11 -0
- package/scripts/hooks/pre-compact.js +12 -4
- package/scripts/hooks/session-start.js +24 -3
- package/scripts/upgrade-plugin.sh +71 -2
- package/skills/memesh/SKILL.md +5 -1
- /package/{.mcp.json → .claude-plugin/mcp.json} +0 -0
package/dist/core/doctor.js
CHANGED
|
@@ -28,6 +28,7 @@ import { autoCaptureDecision } from './capture-flag.js';
|
|
|
28
28
|
import { guardFromMetadata } from './guards.js';
|
|
29
29
|
import { getAgentMessageStorageReport } from './agent-message-storage.js';
|
|
30
30
|
import { readHostConfigFile } from '../host-runtime/config.js';
|
|
31
|
+
import { summariseTelemetry } from './llm-telemetry.js';
|
|
31
32
|
const EMBEDDING_PROBE_TIMEOUT_MS = 15000;
|
|
32
33
|
const EXPECTED_HOOK_TYPES = ['PreToolUse', 'SessionStart', 'PostToolUse', 'Stop', 'PreCompact'];
|
|
33
34
|
const AGENT_MESSAGE_STORAGE_QUOTA_ENV = 'MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES';
|
|
@@ -216,28 +217,49 @@ function inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl,
|
|
|
216
217
|
return createCheck('config', 'Config', 'fail', `${configPath} could not be read or parsed (${msg}). Every setting in it — LLM provider, fallbacks, embedder — is being silently ignored right now.`, `Fix the JSON or remove the file to fall back to defaults: mv ${configPath} ${configPath}.bak`, { code: 'config-parse.unreadable', params: { path: configPath, detail: msg } });
|
|
217
218
|
}
|
|
218
219
|
}
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
const MCP_PLACEHOLDER = '${CLAUDE_PLUGIN_ROOT}';
|
|
221
|
+
function declaredMcpManifest(packageRoot, readFileSyncImpl) {
|
|
222
|
+
const parsed = parseJsonFile(path.join(packageRoot, '.claude-plugin', 'plugin.json'), readFileSyncImpl);
|
|
223
|
+
if (!parsed.ok)
|
|
224
|
+
return null;
|
|
225
|
+
const declared = parsed.value.mcpServers;
|
|
226
|
+
if (typeof declared !== 'string' || !declared.startsWith('./'))
|
|
227
|
+
return null;
|
|
228
|
+
return declared.slice(2);
|
|
229
|
+
}
|
|
230
|
+
function inspectMcpConfig(packageRoot, installChannel, existsSyncImpl, readFileSyncImpl, env) {
|
|
231
|
+
const relativeManifest = declaredMcpManifest(packageRoot, readFileSyncImpl);
|
|
232
|
+
if (relativeManifest === null) {
|
|
233
|
+
return createCheck('mcp-config', 'MCP config', 'fail', '.claude-plugin/plugin.json declares no `mcpServers` path, so Claude Code has no MeMesh MCP server to start.', 'Reinstall MeMesh so the plugin manifest and the MCP manifest it names are both restored.', { code: 'mcp-config.missing' });
|
|
234
|
+
}
|
|
235
|
+
const label = relativeManifest;
|
|
236
|
+
const mcpPath = path.join(packageRoot, relativeManifest);
|
|
221
237
|
if (!existsSyncImpl(mcpPath)) {
|
|
222
|
-
return createCheck('mcp-config', 'MCP config', 'fail',
|
|
238
|
+
return createCheck('mcp-config', 'MCP config', 'fail', `${label} is missing.`, 'Reinstall MeMesh so the plugin manifest and the MCP manifest it names are both restored.', { code: 'mcp-config.missing' });
|
|
223
239
|
}
|
|
224
240
|
const parsed = parseJsonFile(mcpPath, readFileSyncImpl);
|
|
225
241
|
if (!parsed.ok) {
|
|
226
|
-
return createCheck('mcp-config', 'MCP config', 'fail',
|
|
242
|
+
return createCheck('mcp-config', 'MCP config', 'fail', `${label} is not valid JSON.`, `Fix ${mcpPath} so Claude Code can read the MCP server definition.`, { code: 'mcp-config.invalid-json', params: { path: mcpPath } });
|
|
227
243
|
}
|
|
228
244
|
const server = parsed.value.mcpServers?.memesh;
|
|
229
245
|
if (!server || typeof server.command !== 'string') {
|
|
230
|
-
return createCheck('mcp-config', 'MCP config', 'fail',
|
|
246
|
+
return createCheck('mcp-config', 'MCP config', 'fail', `${label} does not define a usable \`memesh\` MCP server entry.`, `Reinstall MeMesh or restore the \`mcpServers.memesh\` entry in \`${label}\`.`, { code: 'mcp-config.no-entry' });
|
|
231
247
|
}
|
|
232
248
|
const args = Array.isArray(server.args) ? server.args : [];
|
|
233
249
|
const entry = typeof args[0] === 'string' ? args[0] : null;
|
|
234
250
|
if (entry) {
|
|
235
|
-
const
|
|
251
|
+
const pluginRoot = installChannel === 'plugin-marketplace' ? packageRoot : (env.CLAUDE_PLUGIN_ROOT || null);
|
|
252
|
+
if (entry.includes(MCP_PLACEHOLDER) && pluginRoot === null) {
|
|
253
|
+
return createCheck('mcp-config', 'MCP config', 'warn', `${label} starts \`${entry}\`. NOT VERIFIED: \`${MCP_PLACEHOLDER}\` is substituted by the Claude Code plugin runtime, and this is a ${installChannel} install with CLAUDE_PLUGIN_ROOT unset — so the file it names was not checked.`, `Verify it the way the plugin runtime would: CLAUDE_PLUGIN_ROOT=${packageRoot} memesh doctor`, { code: 'mcp-config.placeholder-unresolved', params: { entry, channel: installChannel } });
|
|
254
|
+
}
|
|
255
|
+
const resolved = pluginRoot === null
|
|
256
|
+
? path.resolve(packageRoot, entry)
|
|
257
|
+
: path.resolve(entry.replaceAll(MCP_PLACEHOLDER, pluginRoot));
|
|
236
258
|
if (!existsSyncImpl(resolved)) {
|
|
237
|
-
return createCheck('mcp-config', 'MCP config', 'fail',
|
|
259
|
+
return createCheck('mcp-config', 'MCP config', 'fail', `${label} starts \`${entry}\`, and that file is not in this install — so every memesh MCP tool fails to start.`, `Reinstall MeMesh; if you edited \`${label}\` by hand, point it back at \`${MCP_PLACEHOLDER}/dist/mcp/server.js\`.`, { code: 'mcp-config.entry-missing', params: { entry, resolved } });
|
|
238
260
|
}
|
|
239
261
|
}
|
|
240
|
-
return createCheck('mcp-config', 'MCP config', 'pass',
|
|
262
|
+
return createCheck('mcp-config', 'MCP config', 'pass', `${label} is present, defines the memesh MCP server, and the script it starts exists.`);
|
|
241
263
|
}
|
|
242
264
|
function extractHookScriptPaths(hooksConfig, packageRoot) {
|
|
243
265
|
const hooks = hooksConfig.hooks;
|
|
@@ -619,7 +641,32 @@ function inspectNativeBinding(packageRoot, _existsSyncImpl, probeImpl = defaultN
|
|
|
619
641
|
}
|
|
620
642
|
return createCheck('native-binding', 'SQLite and vector search', 'warn', `sqlite-vec could not be loaded: ${result.message}. Memories are still saved and found by keyword; only search by meaning is off.`, `Run: cd "${packageRoot}" && npm install --omit=dev`, { code: 'native-binding.load-failed', params: { detail: result.message, root: packageRoot } });
|
|
621
643
|
}
|
|
622
|
-
function
|
|
644
|
+
function readVersionFromInstalledBinary(binaryPath, existsSyncImpl, readFileSyncImpl, realpathSyncImpl = fs.realpathSync) {
|
|
645
|
+
let resolved;
|
|
646
|
+
try {
|
|
647
|
+
resolved = realpathSyncImpl(binaryPath);
|
|
648
|
+
}
|
|
649
|
+
catch {
|
|
650
|
+
resolved = binaryPath;
|
|
651
|
+
}
|
|
652
|
+
let dir = path.dirname(resolved);
|
|
653
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
654
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
655
|
+
if (existsSyncImpl(pkgPath)) {
|
|
656
|
+
const parsed = parseJsonFile(pkgPath, readFileSyncImpl);
|
|
657
|
+
if (!parsed.ok)
|
|
658
|
+
return null;
|
|
659
|
+
const { name, version } = parsed.value;
|
|
660
|
+
return name === '@pcircle/memesh' && typeof version === 'string' ? version : null;
|
|
661
|
+
}
|
|
662
|
+
const parent = path.dirname(dir);
|
|
663
|
+
if (parent === dir)
|
|
664
|
+
return null;
|
|
665
|
+
dir = parent;
|
|
666
|
+
}
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
function inspectShellCli(installChannel, packageRoot, packageVersion, resolveShellMemeshImpl, existsSyncImpl, readFileSyncImpl) {
|
|
623
670
|
const shellPath = resolveShellMemeshImpl();
|
|
624
671
|
const isSameAsCurrent = shellPath ? path.resolve(shellPath).startsWith(path.resolve(packageRoot)) : false;
|
|
625
672
|
const hasDistinctShellCli = !!shellPath && !isSameAsCurrent;
|
|
@@ -629,7 +676,25 @@ function inspectShellCli(installChannel, packageRoot, resolveShellMemeshImpl) {
|
|
|
629
676
|
: 'Running from npm-global install — shell access available in this terminal.');
|
|
630
677
|
}
|
|
631
678
|
if (hasDistinctShellCli) {
|
|
632
|
-
|
|
679
|
+
const shellVersion = readVersionFromInstalledBinary(shellPath, existsSyncImpl, readFileSyncImpl);
|
|
680
|
+
const shellIsBehind = shellVersion ? classifyBump(shellVersion, packageVersion) : null;
|
|
681
|
+
const thisIsBehind = shellVersion ? classifyBump(packageVersion, shellVersion) : null;
|
|
682
|
+
if (shellIsBehind) {
|
|
683
|
+
return createCheck('shell-cli', 'Shell CLI on PATH', 'warn', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}), and it is running ${shellVersion} — behind this install's ${packageVersion}. Both share the same DB, but an agent using this install and a human typing \`memesh\` in a terminal are running different code.`, 'Run `npm install -g @pcircle/memesh@latest` to bring the shell CLI up to date — a separate global install is never updated automatically by the plugin marketplace.');
|
|
684
|
+
}
|
|
685
|
+
if (thisIsBehind) {
|
|
686
|
+
const pluginHost = installChannel === 'plugin-marketplace' ? detectPluginHost(packageRoot) : null;
|
|
687
|
+
const fix = installChannel !== 'plugin-marketplace'
|
|
688
|
+
? `Update this install (a ${installChannel}) to ${shellVersion} or newer via its own channel — see \`memesh status\`.`
|
|
689
|
+
: pluginHost
|
|
690
|
+
? `Run \`${PLUGIN_REFRESH_COMMANDS[pluginHost]}\` to bring this plugin copy to ${shellVersion} (or newer).`
|
|
691
|
+
: `Bring this plugin copy to ${shellVersion} (or newer) with your host's refresh command — `
|
|
692
|
+
+ `Claude Code: \`${PLUGIN_REFRESH_COMMANDS['claude-code']}\`; `
|
|
693
|
+
+ `Codex: \`${PLUGIN_REFRESH_COMMANDS.codex}\`.`;
|
|
694
|
+
return createCheck('shell-cli', 'Shell CLI on PATH', 'warn', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}), and it is running ${shellVersion} — ahead of this install's ${packageVersion}.`, fix);
|
|
695
|
+
}
|
|
696
|
+
return createCheck('shell-cli', 'Shell CLI on PATH', 'pass', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}). Both paths coexist and share the same DB`
|
|
697
|
+
+ (shellVersion ? `, both on ${packageVersion}.` : ' — could not read the shell copy\'s own version to compare.'));
|
|
633
698
|
}
|
|
634
699
|
if (installChannel === 'plugin-marketplace') {
|
|
635
700
|
const host = detectPluginHost(packageRoot) === 'codex' ? 'Codex CLI' : 'Claude Code';
|
|
@@ -827,6 +892,33 @@ function inspectPluginCacheCurrency(installChannel, pluginHost, packageRoot, ins
|
|
|
827
892
|
}
|
|
828
893
|
return createCheck('plugin-cache', `Plugin cache source record is current (${hostLabel})`, 'warn', `The plugin cache records commit ${installedSha.slice(0, 8)}, but the marketplace has moved to ${marketplaceSha.slice(0, 8)} under the same version — ${hostLabel} does not normally refresh a cache whose version did not change, so refresh the cache before relying on the newer marketplace code.`, `Run \`${command}\` to refresh the cache in place, then restart ${hostLabel}.`, { code: 'plugin-cache.stale', params: { installed: installedSha.slice(0, 8), marketplace: marketplaceSha.slice(0, 8), host: hostLabel, command } });
|
|
829
894
|
}
|
|
895
|
+
function annotateNpmGlobalPluginCacheVersion(check, discoveredPackageRoot, hostLabel, runningVersion) {
|
|
896
|
+
const cacheRoot = path.dirname(discoveredPackageRoot);
|
|
897
|
+
const discoveredVersion = path.basename(discoveredPackageRoot);
|
|
898
|
+
let amended = check;
|
|
899
|
+
if (discoveredVersion !== runningVersion && classifyBump(runningVersion, discoveredVersion)) {
|
|
900
|
+
const skewNote = `This npm-global install is on ${runningVersion}; the ${hostLabel} plugin cache is on ${discoveredVersion}. `
|
|
901
|
+
+ 'The plugin marketplace\'s own auto-updater only ever refreshes its plugin copy — it cannot and will not update this separate npm-global install.';
|
|
902
|
+
const skewFix = `Run \`memesh update\` to bring this npm-global install to ${discoveredVersion} (or newer) — it does not update itself automatically.`;
|
|
903
|
+
amended = {
|
|
904
|
+
...amended,
|
|
905
|
+
status: amended.status === 'pass' ? 'warn' : amended.status,
|
|
906
|
+
summary: `${amended.summary} ${skewNote}`,
|
|
907
|
+
fix: amended.fix ? `${amended.fix} Also: ${skewFix}` : skewFix,
|
|
908
|
+
code: undefined,
|
|
909
|
+
params: undefined,
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
const cachedVersions = versionedPluginCacheRoots(cacheRoot);
|
|
913
|
+
if (cachedVersions.length > 2) {
|
|
914
|
+
amended = {
|
|
915
|
+
...amended,
|
|
916
|
+
summary: `${amended.summary} ${cachedVersions.length} versioned copies of the ${hostLabel} plugin are cached under ${cacheRoot}; old ones are never removed automatically. `
|
|
917
|
+
+ `Delete ones you no longer need once no ${hostLabel} process is using them, e.g. \`rm -rf "${cachedVersions[0]}"\`.`,
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
return amended;
|
|
921
|
+
}
|
|
830
922
|
function inspectClaudeChannelRegistration(existsSyncImpl, readFileSyncImpl) {
|
|
831
923
|
const configPath = path.join(homeDir(), '.claude.json');
|
|
832
924
|
let parsed = null;
|
|
@@ -977,7 +1069,13 @@ async function inspectUpdateStatus(packageVersion, getUpdateCheckImpl, installSu
|
|
|
977
1069
|
return createCheck('update-status', 'Update status', 'pass', `Running pre-release version (${packageVersion}), npm latest is ${update.latestVersion}`);
|
|
978
1070
|
}
|
|
979
1071
|
}
|
|
980
|
-
|
|
1072
|
+
const checkedHoursAgo = update.lastSuccessfulCheckAt === null
|
|
1073
|
+
? null
|
|
1074
|
+
: (Date.now() - Date.parse(update.lastSuccessfulCheckAt)) / 3600_000;
|
|
1075
|
+
const checkedAgo = formatHoursAgo(checkedHoursAgo);
|
|
1076
|
+
return createCheck('update-status', 'Update status', update.freshness === 'stale' ? 'warn' : 'pass', update.freshness === 'stale'
|
|
1077
|
+
? `As of the last check (${checkedAgo}), ${packageVersion} was the latest version — that check is more than 24h old, so a newer release may exist since. Doctor never makes a live registry call itself.`
|
|
1078
|
+
: `As of the last check (${checkedAgo}), ${packageVersion} was the latest version.`, update.freshness === 'stale'
|
|
981
1079
|
? 'Run `memesh status` while online to refresh cached update metadata.'
|
|
982
1080
|
: undefined, update.freshness === 'stale'
|
|
983
1081
|
? { code: 'update-status.stale', params: { version: packageVersion } }
|
|
@@ -1175,6 +1273,33 @@ async function inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextI
|
|
|
1175
1273
|
clearTimeout(timer);
|
|
1176
1274
|
}
|
|
1177
1275
|
}
|
|
1276
|
+
const LLM_TELEMETRY_HEALTH_WINDOW_DAYS = 7;
|
|
1277
|
+
const LLM_TELEMETRY_HEALTH_MIN_CALLS = 3;
|
|
1278
|
+
function inspectLlmTelemetryHealth(db, windowDays = LLM_TELEMETRY_HEALTH_WINDOW_DAYS, minCalls = LLM_TELEMETRY_HEALTH_MIN_CALLS) {
|
|
1279
|
+
let summaries;
|
|
1280
|
+
try {
|
|
1281
|
+
summaries = summariseTelemetry(windowDays, db);
|
|
1282
|
+
}
|
|
1283
|
+
catch {
|
|
1284
|
+
return undefined;
|
|
1285
|
+
}
|
|
1286
|
+
if (summaries.length === 0)
|
|
1287
|
+
return undefined;
|
|
1288
|
+
const broken = summaries
|
|
1289
|
+
.filter((s) => s.total_calls >= minCalls && s.successes === 0)
|
|
1290
|
+
.sort((a, b) => b.total_calls - a.total_calls);
|
|
1291
|
+
if (broken.length > 0) {
|
|
1292
|
+
const detail = broken.map((s) => `${s.flow} (${s.total_calls} call${s.total_calls === 1 ? '' : 's'}, 0 succeeded)`).join(', ');
|
|
1293
|
+
return createCheck('llm_telemetry_health', 'AI feature health', 'warn', `${broken.length} AI-backed feature${broken.length === 1 ? '' : 's'} failed every call in the last `
|
|
1294
|
+
+ `${windowDays} days: ${detail}. Those features are silently doing nothing.`, 'Run `memesh telemetry` for the full per-flow detail, then check the model/provider configured for '
|
|
1295
|
+
+ 'the failing flow. `memesh doctor --probe` confirms whether it answers a live call.', { code: 'llm-telemetry.silent-failure', params: { count: broken.length, detail, windowDays } });
|
|
1296
|
+
}
|
|
1297
|
+
const totalCalls = summaries.reduce((n, s) => n + s.total_calls, 0);
|
|
1298
|
+
const totalSuccesses = summaries.reduce((n, s) => n + s.successes, 0);
|
|
1299
|
+
const rate = totalCalls > 0 ? Math.round((totalSuccesses / totalCalls) * 100) : 100;
|
|
1300
|
+
return createInfo('llm_telemetry_health', 'AI feature health', `${summaries.length} AI-backed flow${summaries.length === 1 ? '' : 's'} made ${totalCalls} call(s) in the `
|
|
1301
|
+
+ `last ${windowDays} days; ${rate}% succeeded.`);
|
|
1302
|
+
}
|
|
1178
1303
|
async function inspectLlmProbe(capabilities, probeCapabilities, probeProviderImpl) {
|
|
1179
1304
|
const llm = capabilities.llm;
|
|
1180
1305
|
if (!llm) {
|
|
@@ -1204,7 +1329,7 @@ function summarizeOverallStatus(checks) {
|
|
|
1204
1329
|
return 'PASS';
|
|
1205
1330
|
}
|
|
1206
1331
|
export async function runDoctor(options) {
|
|
1207
|
-
const { packageRoot, packageVersion, probeHttp = false, probeCapabilities = false, embedTextImpl = embedText, probeProviderImpl = probeProvider, httpBaseUrl = 'http://127.0.0.1:3737', platform = process.platform, openDatabaseImpl = openDatabase, closeDatabaseImpl = closeDatabase, isDatabaseOpenImpl = isDatabaseOpen, detectCapabilitiesImpl = detectCapabilities, getConfigPathImpl = getConfigPath, getUpdateCheckImpl = getUpdateCheck, getCurrentInstallChannelImpl = getCurrentInstallChannel, installedPluginsPathImpl, marketplaceHeadShaImpl = defaultMarketplaceHeadSha, pluginCacheDiscoveryImpl, getInstallChannelSupportImpl = getInstallChannelSupport, existsSyncImpl = fs.existsSync, readFileSyncImpl = fs.readFileSync, statSyncImpl = fs.statSync, fetchImpl = fetch, agentMessageStoragePolicy, nativeBindingProbeImpl, resolveShellMemeshImpl = defaultResolveShellMemesh, probeMessageCapability = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY === '1', messageCapabilityProbeImpl = probeInstalledMessageCapability, probeMessageRouterStatus = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_ROUTER === '1', messageRouterStatusProbeImpl = defaultMessageRouterStatusProbe, } = options;
|
|
1332
|
+
const { packageRoot, packageVersion, probeHttp = false, probeCapabilities = false, embedTextImpl = embedText, probeProviderImpl = probeProvider, httpBaseUrl = 'http://127.0.0.1:3737', platform = process.platform, envImpl = process.env, openDatabaseImpl = openDatabase, closeDatabaseImpl = closeDatabase, isDatabaseOpenImpl = isDatabaseOpen, detectCapabilitiesImpl = detectCapabilities, getConfigPathImpl = getConfigPath, getUpdateCheckImpl = getUpdateCheck, getCurrentInstallChannelImpl = getCurrentInstallChannel, installedPluginsPathImpl, marketplaceHeadShaImpl = defaultMarketplaceHeadSha, pluginCacheDiscoveryImpl, getInstallChannelSupportImpl = getInstallChannelSupport, existsSyncImpl = fs.existsSync, readFileSyncImpl = fs.readFileSync, statSyncImpl = fs.statSync, fetchImpl = fetch, agentMessageStoragePolicy, nativeBindingProbeImpl, resolveShellMemeshImpl = defaultResolveShellMemesh, probeMessageCapability = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY === '1', messageCapabilityProbeImpl = probeInstalledMessageCapability, probeMessageRouterStatus = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_ROUTER === '1', messageRouterStatusProbeImpl = defaultMessageRouterStatusProbe, } = options;
|
|
1208
1333
|
const wasDbOpenBeforeUs = isDatabaseOpenImpl();
|
|
1209
1334
|
const safeCloseDatabaseImpl = wasDbOpenBeforeUs
|
|
1210
1335
|
? () => undefined
|
|
@@ -1259,7 +1384,14 @@ export async function runDoctor(options) {
|
|
|
1259
1384
|
? 'The vector index could not be read, so how much of your memory semantic recall can see is unknown'
|
|
1260
1385
|
: `${missingVectors} memor${missingVectors === 1 ? 'y has' : 'ies have'} no search vector, `
|
|
1261
1386
|
+ 'so semantic recall cannot find them (keyword search still works)';
|
|
1262
|
-
|
|
1387
|
+
const noEmbedderConfigured = detectCapabilitiesImpl().embeddings === 'tfidf';
|
|
1388
|
+
const vectorIndexFix = noEmbedderConfigured
|
|
1389
|
+
? `No embedder is configured, so reindex has nothing to embed with — run 'memesh config set embedder.provider ollama' (or 'openai') first, then 'memesh reindex'.`
|
|
1390
|
+
: `Run 'memesh reindex' to fix. This will restore full search functionality.`;
|
|
1391
|
+
dbChecks.push(createCheck('vector_index', 'Vector Index', 'warn', owed, vectorIndexFix, {
|
|
1392
|
+
code: noEmbedderConfigured ? 'vector-index.stale-no-embedder' : 'vector-index.stale',
|
|
1393
|
+
params: { missing: missingVectors ?? -1 },
|
|
1394
|
+
}));
|
|
1263
1395
|
}
|
|
1264
1396
|
const generation = readVectorGeneration();
|
|
1265
1397
|
if (generation.state !== 'none') {
|
|
@@ -1340,6 +1472,9 @@ export async function runDoctor(options) {
|
|
|
1340
1472
|
dbChecks.push(createInfo('citation_compliance', 'Memory citation rate', `${cited} of ${citationTotal} session(s) with injected memories cited at least one (${rate}%).`));
|
|
1341
1473
|
}
|
|
1342
1474
|
}
|
|
1475
|
+
const llmTelemetryHealth = inspectLlmTelemetryHealth(db);
|
|
1476
|
+
if (llmTelemetryHealth)
|
|
1477
|
+
dbChecks.push(llmTelemetryHealth);
|
|
1343
1478
|
}
|
|
1344
1479
|
catch (err) {
|
|
1345
1480
|
const message = err instanceof Error ? err.message : 'unknown database error';
|
|
@@ -1407,7 +1542,7 @@ export async function runDoctor(options) {
|
|
|
1407
1542
|
}
|
|
1408
1543
|
}
|
|
1409
1544
|
checks.push(inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl, detectCapabilitiesImpl().llm));
|
|
1410
|
-
checks.push(inspectMcpConfig(packageRoot, existsSyncImpl, readFileSyncImpl));
|
|
1545
|
+
checks.push(inspectMcpConfig(packageRoot, install, existsSyncImpl, readFileSyncImpl, envImpl));
|
|
1411
1546
|
checks.push(...inspectHooksConfig(packageRoot, platform, existsSyncImpl, readFileSyncImpl, statSyncImpl));
|
|
1412
1547
|
const pluginHost = detectPluginHost(packageRoot);
|
|
1413
1548
|
let codexPluginCacheDetected = pluginHost === 'codex';
|
|
@@ -1426,9 +1561,12 @@ export async function runDoctor(options) {
|
|
|
1426
1561
|
codexPluginCacheDetected = true;
|
|
1427
1562
|
if (discovered.host === 'claude-code')
|
|
1428
1563
|
claudePluginCacheDetected = true;
|
|
1429
|
-
|
|
1564
|
+
let check = discovered.unverifiableReason
|
|
1430
1565
|
? pluginCacheUnverifiable(discovered.host, discovered.unverifiableReason)
|
|
1431
1566
|
: inspectPluginCacheCurrency('plugin-marketplace', discovered.host, discovered.packageRoot, discovered.installedPluginsPath, readFileSyncImpl, existsSyncImpl, marketplaceHeadShaImpl);
|
|
1567
|
+
if (check && !discovered.unverifiableReason) {
|
|
1568
|
+
check = annotateNpmGlobalPluginCacheVersion(check, discovered.packageRoot, discovered.host === 'codex' ? 'Codex' : 'Claude Code', packageVersion);
|
|
1569
|
+
}
|
|
1432
1570
|
if (check) {
|
|
1433
1571
|
const hostName = discovered.host;
|
|
1434
1572
|
const index = (discoveredCounts.get(discovered.host) ?? 0) + 1;
|
|
@@ -1450,7 +1588,7 @@ export async function runDoctor(options) {
|
|
|
1450
1588
|
checks.push(inspectDashboardArtifact(packageRoot, existsSyncImpl));
|
|
1451
1589
|
checks.push(inspectNodeRuntime(packageRoot, existsSyncImpl, readFileSyncImpl));
|
|
1452
1590
|
checks.push(inspectNativeBinding(packageRoot, existsSyncImpl, nativeBindingProbeImpl));
|
|
1453
|
-
checks.push(inspectShellCli(install, packageRoot, resolveShellMemeshImpl));
|
|
1591
|
+
checks.push(inspectShellCli(install, packageRoot, packageVersion, resolveShellMemeshImpl, existsSyncImpl, readFileSyncImpl));
|
|
1454
1592
|
checks.push(verifySkillsManifest(packageRoot, existsSyncImpl, readFileSyncImpl, installSupport));
|
|
1455
1593
|
checks.push(inspectMessageCapability(packageRoot, probeMessageCapability, messageCapabilityProbeImpl));
|
|
1456
1594
|
checks.push(await inspectMessageRouterStatus(probeMessageRouterStatus, messageRouterStatusProbeImpl));
|