Files
memind/capabilities.test.mjs
T
john dcf83e9b6f fix(memory-v2): improve pgvector recall and WeChat memory injection
Add keyword fallback and dedupe for pgvector resolve, score personal vs episodic memories by relevance, record WeChat recall product events, and disable broken chatrecall on Postgres session storage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 22:26:47 +08:00

685 lines
24 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
buildAgentExtensionPolicy,
buildPageEditAgentPolicy,
CAPABILITY_CATALOG,
clampUserCapabilities,
DEFAULT_USER_CAPABILITIES,
normalizeCapabilityPatch,
resolveMindSearchMcpEndpoint,
resolveExcelMcpServerPath,
resolveMindSearchMcpServerPath,
resolveSandboxMcpNodeExecPath,
resolveSandboxMcpServerPath,
resolveSandboxMcpUserDataPgUrl,
resolveSandboxMcpControlDbEnv,
sandboxDeveloperTools,
sandboxMcpTools,
isChatRecallExtensionSupported,
withoutSessionImageRead,
} from './capabilities.mjs';
import {
verifyMindSpaceMcpScopedToken,
} from './mindspace-mcp-scoped-token.mjs';
import { applyPoliciesToCapabilities } from './policies.mjs';
test('resolveSandboxMcpControlDbEnv rewrites loopback MySQL endpoints for container MCP', () => {
const resolved = resolveSandboxMcpControlDbEnv(
{
DATABASE_URL: 'mysql://portal:secret@127.0.0.1:3306/memind',
MYSQL_HOST: 'localhost',
MYSQL_PORT: '3306',
MYSQL_DATABASE: 'memind',
},
{
containerized: true,
hostGateway: 'host.docker.internal',
},
);
assert.equal(
resolved.DATABASE_URL,
'mysql://portal:secret@host.docker.internal:3306/memind',
);
assert.equal(resolved.MYSQL_HOST, 'host.docker.internal');
assert.equal(resolved.MYSQL_PORT, '3306');
assert.equal(resolved.MYSQL_DATABASE, 'memind');
});
test('resolveSandboxMcpControlDbEnv preserves native-process database endpoints', () => {
assert.deepEqual(
resolveSandboxMcpControlDbEnv(
{
DATABASE_URL: 'mysql://portal:secret@127.0.0.1:3306/memind',
MYSQL_HOST: '127.0.0.1',
},
{ containerized: false },
),
{
DATABASE_URL: 'mysql://portal:secret@127.0.0.1:3306/memind',
MYSQL_HOST: '127.0.0.1',
},
);
});
test('resolveSandboxMcpServerPath resolves to an existing MCP entry file', () => {
const serverPath = resolveSandboxMcpServerPath();
assert.match(serverPath, /mindspace-sandbox-mcp\.mjs$/);
assert.ok(fs.existsSync(serverPath));
});
test('resolveSandboxMcpServerPath honors container-path override without host fs checks', () => {
assert.equal(
resolveSandboxMcpServerPath('/opt/portal/mindspace-sandbox-mcp.mjs'),
'/opt/portal/mindspace-sandbox-mcp.mjs',
);
});
test('bundled MCP paths use the persistent Portal runtime root in split mode', () => {
const runtimeRoot = '/srv/memind-persistent';
assert.equal(
resolveSandboxMcpServerPath('', runtimeRoot),
'/srv/memind-persistent/mindspace-sandbox-mcp.mjs',
);
assert.equal(
resolveMindSearchMcpServerPath('', runtimeRoot),
'/srv/memind-persistent/tkmind-search-mcp.mjs',
);
assert.equal(
resolveExcelMcpServerPath('', runtimeRoot),
'/srv/memind-persistent/tkmind-excel-mcp.mjs',
);
});
test('resolveSandboxMcpUserDataPgUrl rewrites a portal loopback URL for container MCP access', () => {
const resolved = resolveSandboxMcpUserDataPgUrl({
portalUrl: 'postgresql://mindspace:secret@127.0.0.1:5433/mindspace_userdata_prod',
containerized: true,
});
const parsed = new URL(resolved);
assert.equal(parsed.hostname, 'host.docker.internal');
assert.equal(parsed.port, '5433');
assert.equal(parsed.pathname, '/mindspace_userdata_prod');
});
test('resolveSandboxMcpUserDataPgUrl rewrites a host Unix socket DSN for container MCP access', () => {
const resolved = resolveSandboxMcpUserDataPgUrl({
portalUrl: 'postgresql://john:secret@/mindspace_userdata_dev?host=%2Ftmp&port=5433',
containerized: true,
});
const parsed = new URL(resolved);
assert.equal(parsed.hostname, 'host.docker.internal');
assert.equal(parsed.port, '5433');
assert.equal(parsed.pathname, '/mindspace_userdata_dev');
assert.equal(parsed.password, 'secret');
assert.equal(parsed.searchParams.has('host'), false);
assert.equal(parsed.searchParams.has('port'), false);
});
test('resolveSandboxMcpUserDataPgUrl fails fast for passwordless socket DSN in a container', () => {
assert.throws(
() =>
resolveSandboxMcpUserDataPgUrl({
portalUrl:
'postgresql://john@/mindspace_userdata_dev?host=%2Ftmp&port=5433',
containerized: true,
}),
(error) =>
error?.code ===
'MINDSPACE_USERDATA_MCP_PG_URL_REQUIRED' &&
/MINDSPACE_USERDATA_MCP_PG_URL/.test(error.message),
);
});
test('resolveSandboxMcpUserDataPgUrl preserves native URLs and honors an explicit MCP URL', () => {
const portalUrl = 'postgresql://mindspace:secret@127.0.0.1:5433/mindspace_userdata_prod';
assert.equal(resolveSandboxMcpUserDataPgUrl({ portalUrl }), portalUrl);
assert.equal(
resolveSandboxMcpUserDataPgUrl({
portalUrl,
mcpUrl: 'postgresql://mindspace:secret@pg-proxy.internal:6432/mindspace_userdata_prod',
containerized: true,
}),
'postgresql://mindspace:secret@pg-proxy.internal:6432/mindspace_userdata_prod',
);
});
test('resolveMindSearchMcpEndpoint rewrites host loopback for goosed containers', () => {
assert.equal(
resolveMindSearchMcpEndpoint('http://127.0.0.1:20080/search'),
'http://host.docker.internal:20080/search',
);
assert.equal(
resolveMindSearchMcpEndpoint('https://search.internal/query'),
'https://search.internal/query',
);
});
test('resolveSandboxMcpNodeExecPath honors container-path override without host fs checks', () => {
assert.equal(resolveSandboxMcpNodeExecPath('/usr/local/bin/node'), '/usr/local/bin/node');
assert.equal(resolveSandboxMcpNodeExecPath(''), process.execPath);
});
test('web capability mounts platform web extension with search tools', () => {
const caps = applyPoliciesToCapabilities(
{ ...DEFAULT_USER_CAPABILITIES, web: true },
{ network_egress: 'allow' },
);
const policy = buildAgentExtensionPolicy(caps, {
policies: { network_egress: 'allow', goose_mode: 'auto' },
});
const web = policy.extensionOverrides.find((ext) => ext.name === 'web');
assert.ok(web);
assert.equal(web.type, 'platform');
assert.deepEqual(web.available_tools, ['web_search', 'fetch_url']);
});
test('web capability is stripped when network egress is denied', () => {
const caps = applyPoliciesToCapabilities(
{ ...DEFAULT_USER_CAPABILITIES, web: true },
{ network_egress: 'deny' },
);
assert.equal(caps.web, false);
const policy = buildAgentExtensionPolicy(caps, {
policies: { network_egress: 'deny', goose_mode: 'auto' },
});
assert.equal(policy.extensionOverrides.some((ext) => ext.name === 'web'), false);
});
test('default user policy blocks dangerous capabilities', () => {
assert.equal(DEFAULT_USER_CAPABILITIES.shell, false);
assert.equal(DEFAULT_USER_CAPABILITIES.filesystem, false);
assert.equal(DEFAULT_USER_CAPABILITIES.extension_admin, false);
assert.equal(DEFAULT_USER_CAPABILITIES.static_publish, false);
assert.equal(DEFAULT_USER_CAPABILITIES.private_data_space, true);
assert.equal(DEFAULT_USER_CAPABILITIES.code_browse, false);
assert.equal(DEFAULT_USER_CAPABILITIES.memory_store, true);
assert.equal(DEFAULT_USER_CAPABILITIES.skills, true);
assert.equal(DEFAULT_USER_CAPABILITIES.chat_recall, true);
assert.equal(DEFAULT_USER_CAPABILITIES.web, true);
assert.equal(DEFAULT_USER_CAPABILITIES.aider, false);
assert.equal(DEFAULT_USER_CAPABILITIES.openhands, false);
});
test('isChatRecallExtensionSupported disables chatrecall on postgres session storage', () => {
assert.equal(isChatRecallExtensionSupported({}), true);
assert.equal(
isChatRecallExtensionSupported({ GOOSE_SESSION_DB_URL: 'postgresql://john@127.0.0.1:5432/memind_sessions' }),
false,
);
});
test('buildAgentExtensionPolicy omits chatrecall when postgres session storage is configured', () => {
const previous = process.env.GOOSE_SESSION_DB_URL;
process.env.GOOSE_SESSION_DB_URL = 'postgresql://john@127.0.0.1:5432/memind_sessions';
try {
const policy = buildAgentExtensionPolicy(DEFAULT_USER_CAPABILITIES);
assert.equal(policy.extensionOverrides.some((ext) => ext.name === 'chatrecall'), false);
} finally {
if (previous == null) delete process.env.GOOSE_SESSION_DB_URL;
else process.env.GOOSE_SESSION_DB_URL = previous;
}
});
test('buildAgentExtensionPolicy returns null overrides and auto mode for unrestricted users', () => {
const policy = buildAgentExtensionPolicy({}, { unrestricted: true });
assert.equal(policy.extensionOverrides, null);
assert.equal(policy.gooseMode, 'auto');
});
test('static_publish enables sandbox developer tools without shell by default', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
assert.deepEqual(sandboxDeveloperTools(caps), ['write', 'edit', 'read_image']);
const policy = buildAgentExtensionPolicy(caps);
const developer = policy.extensionOverrides.find((ext) => ext.name === 'developer');
assert.deepEqual(developer?.available_tools, ['write', 'edit', 'read_image']);
assert.equal(developer?.available_tools.includes('shell'), false);
assert.equal(developer?.available_tools.includes('tree'), false);
const summon = policy.extensionOverrides.find((ext) => ext.name === 'summon');
assert.deepEqual(summon?.available_tools, ['load_skill']);
assert.ok(policy.extensionOverrides.some((ext) => ext.name === 'projectmemory'));
assert.equal(policy.enableContextMemory, true);
});
test('static_publish with shell survives network deny and includes tree', () => {
const caps = applyPoliciesToCapabilities(
{ ...DEFAULT_USER_CAPABILITIES, static_publish: true, shell: true },
{ network_egress: 'deny' },
);
assert.equal(caps.shell, true);
const policy = buildAgentExtensionPolicy(caps, {
policies: { network_egress: 'deny', goose_mode: 'chat' },
});
const developer = policy.extensionOverrides.find((ext) => ext.name === 'developer');
assert.deepEqual(developer?.available_tools, ['write', 'edit', 'shell', 'tree', 'read_image']);
});
test('buildAgentExtensionPolicy filters developer tools', () => {
const policy = buildAgentExtensionPolicy({
...DEFAULT_USER_CAPABILITIES,
shell: true,
filesystem: false,
code_browse: true,
});
const developer = policy.extensionOverrides.find((ext) => ext.name === 'developer');
assert.ok(developer);
assert.deepEqual(developer.available_tools, ['shell', 'tree', 'read_image']);
assert.equal(
policy.extensionOverrides.some((ext) => ext.name === 'extensionmanager'),
false,
);
});
test('buildAgentExtensionPolicy keeps aider and openhands out of chat mode when granted', () => {
const policy = buildAgentExtensionPolicy({
...DEFAULT_USER_CAPABILITIES,
aider: true,
openhands: true,
});
assert.equal(policy.extensionOverrides.some((ext) => ext.name === 'aider'), false);
assert.equal(policy.extensionOverrides.some((ext) => ext.name === 'openhands'), false);
});
test('buildAgentExtensionPolicy includes aider and openhands only for code tool mode', () => {
const policy = buildAgentExtensionPolicy({
...DEFAULT_USER_CAPABILITIES,
aider: true,
openhands: true,
}, { toolMode: 'code' });
assert.ok(policy.extensionOverrides.some((ext) => ext.name === 'aider'));
assert.ok(policy.extensionOverrides.some((ext) => ext.name === 'openhands'));
const aider = policy.extensionOverrides.find((ext) => ext.name === 'aider');
const openhands = policy.extensionOverrides.find((ext) => ext.name === 'openhands');
assert.equal(aider?.metadata?.runtime_scope, 'code_tool_task');
assert.equal(openhands?.metadata?.runtime_scope, 'code_tool_task');
assert.equal(typeof aider?.timeout_ms, 'number');
assert.equal(typeof openhands?.timeout_ms, 'number');
});
test('normalizeCapabilityPatch ignores unknown keys', () => {
assert.deepEqual(normalizeCapabilityPatch({ shell: true, unknown: true }), { shell: true });
});
test('clampUserCapabilities blocks extension_admin even when stored as true', () => {
const clamped = clampUserCapabilities({
...DEFAULT_USER_CAPABILITIES,
extension_admin: true,
shell: true,
});
assert.equal(clamped.extension_admin, false);
assert.equal(clamped.shell, true);
});
test('clampUserCapabilities blocks Apps for regular H5 users even when granted by role', () => {
const clamped = clampUserCapabilities({
...DEFAULT_USER_CAPABILITIES,
apps: true,
});
assert.equal(clamped.apps, false);
const policy = buildAgentExtensionPolicy(clamped);
assert.equal(policy.extensionOverrides.some((ext) => ext.name === 'apps'), false);
});
test('catalog keys are unique', () => {
const keys = CAPABILITY_CATALOG.map((item) => item.key);
assert.equal(keys.length, new Set(keys).size);
});
test('buildPageEditAgentPolicy narrows tools to shell when available', () => {
const base = buildAgentExtensionPolicy({
...DEFAULT_USER_CAPABILITIES,
shell: true,
filesystem: true,
skills: true,
});
const narrowed = buildPageEditAgentPolicy(base);
assert.equal(narrowed.enableContextMemory, false);
assert.equal(narrowed.gooseMode, 'auto');
assert.deepEqual(narrowed.extensionOverrides, [
{
type: 'platform',
name: 'developer',
description: '',
display_name: 'developer',
bundled: true,
available_tools: ['shell'],
},
]);
});
test('buildPageEditAgentPolicy without shell keeps empty developer tools', () => {
const base = buildAgentExtensionPolicy(DEFAULT_USER_CAPABILITIES);
const narrowed = buildPageEditAgentPolicy(base);
assert.deepEqual(narrowed.extensionOverrides, []);
});
test('buildPageEditAgentPolicy grants shell for static_publish sandbox users with shell capability', () => {
const base = {
...buildAgentExtensionPolicy(
{ ...DEFAULT_USER_CAPABILITIES, static_publish: true, shell: true },
{
sandboxMcp: {
serverPath: '/tmp/mindspace-sandbox-mcp.mjs',
sandboxRoot: '/tmp/sandbox',
},
},
),
capabilities: { ...DEFAULT_USER_CAPABILITIES, static_publish: true, shell: true },
};
const narrowed = buildPageEditAgentPolicy(base);
assert.deepEqual(narrowed.extensionOverrides?.[0]?.available_tools, ['shell']);
});
test('sandboxMcpTools returns correct tool list based on capabilities', () => {
const base = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
assert.deepEqual(sandboxMcpTools(base), [
'read_file',
'write_file',
'edit_file',
'create_dir',
'publish_page',
'generate_image',
'generate_docx',
'generate_long_image',
'private_data_info',
'private_data_schema',
'private_data_query',
'private_data_execute',
'private_data_register_dataset',
'private_data_set_page_policy',
'private_data_close_page_dataset',
'private_data_bind_workspace_page',
'schedule_create_item',
'schedule_create_reminder',
'schedule_list_items',
'scheduled_task_create',
'scheduled_task_list',
'scheduled_task_cancel',
]);
const withBrowse = { ...base, code_browse: true };
assert.ok(sandboxMcpTools(withBrowse).includes('list_dir'));
const withShell = { ...base, shell: true };
assert.ok(sandboxMcpTools(withShell).includes('list_dir'));
});
test('private_data_space alone exposes private data tools through sandbox MCP', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: false, private_data_space: true };
assert.deepEqual(sandboxMcpTools(caps), [
'private_data_info',
'private_data_schema',
'private_data_query',
'private_data_execute',
'private_data_register_dataset',
'private_data_set_page_policy',
'private_data_close_page_dataset',
'private_data_bind_workspace_page',
'schedule_create_item',
'schedule_create_reminder',
'schedule_list_items',
'scheduled_task_create',
'scheduled_task_list',
'scheduled_task_cancel',
]);
const policy = buildAgentExtensionPolicy(caps, {
sandboxMcp: {
serverPath: '/opt/h5/mindspace-sandbox-mcp.mjs',
sandboxRoot: '/opt/h5/MindSpace/user-1',
userId: 'user-1',
containerized: true,
userDataBackend: 'postgres',
userDataPgUrl: 'postgresql://mindspace:secret@127.0.0.1:5433/mindspace_userdata_prod',
userDataAutoProvision: '1',
},
});
const sandboxExt = policy.extensionOverrides.find((ext) => ext.name === 'sandbox-fs');
assert.ok(sandboxExt);
assert.equal(sandboxExt.envs.PRIVATE_DATA_USER_ID, 'user-1');
assert.equal(new URL(sandboxExt.envs.MINDSPACE_USERDATA_PG_URL).hostname, 'host.docker.internal');
assert.equal(sandboxExt.envs.MINDSPACE_USERDATA_BACKEND, 'postgres');
assert.equal(sandboxExt.envs.MINDSPACE_USERDATA_AUTO_PROVISION, '1');
assert.deepEqual(sandboxExt.available_tools, [
'private_data_info',
'private_data_schema',
'private_data_query',
'private_data_execute',
'private_data_register_dataset',
'private_data_set_page_policy',
'private_data_close_page_dataset',
'private_data_bind_workspace_page',
'schedule_create_item',
'schedule_create_reminder',
'schedule_list_items',
'scheduled_task_create',
'scheduled_task_list',
'scheduled_task_cancel',
]);
});
test('static_publish with sandboxMcp uses stdio sandbox-fs extension instead of developer', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
const sandboxMcp = {
serverPath: '/opt/h5/mindspace-sandbox-mcp.mjs',
sandboxRoot: '/opt/h5/MindSpace/abc123',
agentApiBaseUrl: 'http://host.docker.internal:8081/api',
internalAgentSecret: 'internal-secret',
publicBaseUrl: 'http://127.0.0.1:18131',
portalBaseUrl: 'http://127.0.0.1:18131',
portalPort: '18131',
pageDataDeliveryBaseUrl: 'http://127.0.0.1:18131',
};
const policy = buildAgentExtensionPolicy(caps, { sandboxMcp });
const sandboxExt = policy.extensionOverrides.find((ext) => ext.name === 'sandbox-fs');
assert.ok(sandboxExt, 'sandbox-fs extension should be present');
assert.equal(sandboxExt.type, 'stdio');
assert.equal(sandboxExt.cmd, process.execPath);
assert.equal(sandboxExt.envs.SANDBOX_ROOT, '/opt/h5/MindSpace/abc123');
assert.equal(sandboxExt.args[1], '/opt/h5/MindSpace/abc123'); // also passed as argv[2]
assert.equal(sandboxExt.envs.MINDSPACE_AGENT_API_BASE_URL, 'http://host.docker.internal:8081/api');
assert.equal(sandboxExt.envs.MINDSPACE_INTERNAL_AGENT_SECRET, 'internal-secret');
assert.equal(sandboxExt.envs.H5_PUBLIC_BASE_URL, 'http://127.0.0.1:18131');
assert.equal(sandboxExt.envs.H5_PORTAL_BASE_URL, 'http://127.0.0.1:18131');
assert.equal(sandboxExt.envs.H5_PORT, '18131');
assert.equal(
sandboxExt.envs.MEMIND_PAGE_DATA_DELIVERY_BASE_URL,
'http://127.0.0.1:18131',
);
assert.ok(sandboxExt.available_tools.includes('write_file'));
assert.ok(sandboxExt.available_tools.includes('read_file'));
assert.ok(sandboxExt.available_tools.includes('generate_docx'));
assert.ok(sandboxExt.available_tools.includes('generate_long_image'));
assert.ok(sandboxExt.available_tools.includes('generate_image'));
// built-in developer extension should only remain for read_image (image_read: true by default)
const developer = policy.extensionOverrides.find((ext) => ext.name === 'developer');
assert.ok(developer, 'developer should remain for read_image');
assert.deepEqual(developer.available_tools, ['read_image']);
// no full developer write/edit/shell in extensions
const allTools = policy.extensionOverrides.flatMap((e) => e.available_tools ?? []);
assert.ok(!allTools.includes('write'), 'built-in write should not be exposed');
assert.ok(!allTools.includes('shell'), 'shell should not be exposed');
});
test('sandboxMcp emits a scoped logical workspace token only for a bound session', () => {
const secret =
'mindspace-mcp-policy-secret-1234';
const caps = {
...DEFAULT_USER_CAPABILITIES,
static_publish: true,
};
const sandboxMcp = {
serverPath:
'/opt/h5/mindspace-sandbox-mcp.mjs',
sandboxRoot:
'/opt/h5/MindSpace/user-1',
workspaceRoot:
'/srv/h5/MindSpace/user-1',
workspaceRef:
'mindspace://users/user-1/workspace',
userId: 'user-1',
sessionId: 'session-1',
packageId: 'cp_session-1',
mcpBaseUrl:
'http://mindspace.internal:8082/',
mcpTokenSecret: secret,
};
const policy =
buildAgentExtensionPolicy(caps, {
sandboxMcp,
});
const sandboxExt =
policy.extensionOverrides.find(
(extension) =>
extension.name === 'sandbox-fs',
);
const claims =
verifyMindSpaceMcpScopedToken({
token:
sandboxExt.envs
.MINDSPACE_MCP_SCOPED_TOKEN,
secret,
tool: 'publish_page',
});
assert.equal(
sandboxExt.envs.MINDSPACE_MCP_BASE_URL,
'http://mindspace.internal:8082',
);
assert.equal(
sandboxExt.envs.MINDSPACE_SESSION_ID,
'session-1',
);
assert.equal(
sandboxExt.envs.MINDSPACE_PACKAGE_ID,
'cp_session-1',
);
assert.equal(claims.userId, 'user-1');
assert.equal(
claims.workspaceRef,
sandboxMcp.workspaceRef,
);
assert.ok(
claims.tools.includes('write_file'),
);
assert.ok(
claims.tools.includes(
'generate_long_image',
),
);
assert.ok(
claims.tools.includes(
'write_binary_file',
),
);
assert.equal(
Object.values(sandboxExt.envs).includes(
secret,
),
false,
);
const unboundPolicy =
buildAgentExtensionPolicy(caps, {
sandboxMcp: {
...sandboxMcp,
sessionId: null,
packageId: null,
},
});
const unboundSandbox =
unboundPolicy.extensionOverrides.find(
(extension) =>
extension.name === 'sandbox-fs',
);
assert.equal(
unboundSandbox.envs
.MINDSPACE_MCP_SCOPED_TOKEN,
undefined,
);
});
test('withoutSessionImageRead removes only the optional visual inspection tool', () => {
const policy = {
gooseMode: 'chat',
extensionOverrides: [
{ name: 'memory', available_tools: [] },
{ name: 'developer', available_tools: ['write', 'read_image'] },
],
};
const next = withoutSessionImageRead(policy);
assert.deepEqual(next.extensionOverrides, [
{ name: 'memory', available_tools: [] },
{ name: 'developer', available_tools: ['write'] },
]);
assert.deepEqual(policy.extensionOverrides[1].available_tools, ['write', 'read_image']);
assert.deepEqual(
withoutSessionImageRead({
extensionOverrides: [
{ name: 'memory', available_tools: [] },
{ name: 'developer', available_tools: ['read_image'] },
],
}).extensionOverrides,
[{ name: 'memory', available_tools: [] }],
);
});
test('sandboxMcp honors container node executable override', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
const policy = buildAgentExtensionPolicy(caps, {
sandboxMcp: {
serverPath: '/opt/h5/mindspace-sandbox-mcp.mjs',
sandboxRoot: '/opt/h5/MindSpace/abc123',
nodeExecPath: '/usr/local/bin/node',
},
});
const sandboxExt = policy.extensionOverrides.find((ext) => ext.name === 'sandbox-fs');
assert.equal(sandboxExt?.cmd, '/usr/local/bin/node');
});
test('sandboxMcp can use workspaceRoot as the local runtime root compatibility field', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
const policy = buildAgentExtensionPolicy(caps, {
sandboxMcp: {
serverPath: '/opt/h5/mindspace-sandbox-mcp.mjs',
workspaceRoot: '/opt/h5/MindSpace/abc123',
workspaceRef: 'mindspace://users/abc123/workspace',
},
});
const sandboxExt = policy.extensionOverrides.find((ext) => ext.name === 'sandbox-fs');
assert.ok(sandboxExt);
assert.equal(sandboxExt.args[1], '/opt/h5/MindSpace/abc123');
assert.equal(sandboxExt.envs.SANDBOX_ROOT, '/opt/h5/MindSpace/abc123');
assert.equal(sandboxExt.envs.MINDSPACE_WORKSPACE_ROOT, '/opt/h5/MindSpace/abc123');
assert.equal(sandboxExt.envs.MINDSPACE_WORKSPACE_REF, 'mindspace://users/abc123/workspace');
});
test('Excel analyst uses the container-visible bundled MCP directory', () => {
const policy = buildAgentExtensionPolicy(
{
...DEFAULT_USER_CAPABILITIES,
excel_analysis: true,
},
{
sandboxMcp: {
containerized: true,
serverPath: '/opt/portal/mindspace-sandbox-mcp.mjs',
sandboxRoot: '/opt/portal/MindSpace/user-1',
},
},
);
const extension = policy.extensionOverrides.find((ext) => ext.name === 'tkmind-excel');
assert.equal(extension.args[0], '/opt/portal/tkmind-excel-mcp.mjs');
});