Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions cli/__tests__/adapters.qwen.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ test('public launch isolates HOME and config, strips shadows, and never serializ
const executable = join(realpathSync(dirname(ctx._binaryPath)), basename(ctx._binaryPath));
expect(cmd).toBe(executable);
expect(args).toEqual(expect.arrayContaining(['--bare', '--safe-mode', '--auth-type', 'openai', '-o', 'stream-json']));
expect(args).not.toContain('--allowed-tools');
expect(args[args.indexOf('--exclude-tools') + 1].split(','))
.toEqual(expect.arrayContaining([
'agent', 'workflow', 'advisor', 'replace', 'task', 'search_file_content',
Expand Down Expand Up @@ -241,6 +242,47 @@ test('an explicit MCP server reaches --mcp-config alongside --safe-mode', async
});
});

test('private Qwen approves only declared Commonly MCP tools in the non-interactive run', async () => {
await withSeat(async (ctx) => {
let observed;
await qwen.spawn('hello', {
...ctx,
environment: {
...ctx.environment,
sandbox: undefined,
mcp: [
{ name: 'commonly', transport: 'stdio', command: ['/usr/bin/true'] },
{ name: 'other', transport: 'stdio', command: ['/usr/bin/true'] },
],
},
_spawnImpl: (_cmd, args) => {
observed = {
args,
config: JSON.parse(readFileSync(args[args.indexOf('--mcp-config') + 1], 'utf8')),
};
return fakeChild();
},
});
expect(Object.keys(observed.config.mcpServers)).toEqual(['commonly', 'other']);
expect(observed.args[observed.args.indexOf('--allowed-tools') + 1]).toBe('mcp__commonly__*');
expect(observed.args).toContain('--safe-mode');
expect(observed.args).not.toContain('--approval-mode');
expect(observed.args).not.toContain('--yolo');
});
});

test('private Qwen does not approve Commonly tools when that server was not configured', async () => {
await withSeat(async (ctx) => {
let args;
await qwen.spawn('hello', {
...ctx,
environment: { ...ctx.environment, sandbox: undefined },
_spawnImpl: (_cmd, argv) => { args = argv; return fakeChild(); },
});
expect(args).not.toContain('--allowed-tools');
});
});

// TASK-273: a public seat whose MCP did not start used to run with a notice,
// and the model narrated the failure to a stranger (2026-10-15). It now
// refuses the turn before Qwen runs.
Expand Down Expand Up @@ -286,17 +328,20 @@ test('a warm public Qwen spawn grants only the selected MCP version directory',
try {
let profileOptions;
let config;
let launchArgs;
await qwen.spawn('hello', {
...ctx,
environment: { ...ctx.environment, mcp: [{
name: 'commonly', transport: 'stdio', command: ['npx', '-y', '@commonlyai/mcp@latest'],
}] },
_wrapArgvWithSeatbelt: (argv, options) => { profileOptions = options; return argv; },
_spawnImpl: (_cmd, args) => {
launchArgs = args;
config = JSON.parse(readFileSync(args[args.indexOf('--mcp-config') + 1], 'utf8'));
return fakeChild();
},
});
expect(launchArgs).not.toContain('--allowed-tools');
expect(config.mcpServers.commonly).toMatchObject({
command: 'node', args: [join(packageDir, 'src', 'index.js')],
});
Expand Down
4 changes: 2 additions & 2 deletions cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@commonlyai/cli",
"version": "0.1.138",
"version": "0.1.139",
"license": "Apache-2.0",
"description": "The Commonly CLI — connect agents, manage pods, iterate fast",
"type": "module",
Expand Down
5 changes: 5 additions & 0 deletions cli/src/lib/adapters/qwen.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,11 @@ export default {
'--mcp-config', mcpPath, '--exclude-tools', [
...EXCLUDED_TOOLS, ...(isPublic ? PUBLIC_READ_TOOLS : []),
].join(','),
// Qwen ignores settings permissions in bare/safe mode, but still
// honours CLI allow rules. A private seat needs its declared Commonly
// tools approved before non-interactive execution begins.
...(!isPublic && Object.hasOwn(mcp.mcpServers, 'commonly')
? ['--allowed-tools', 'mcp__commonly__*'] : []),
'-o', 'stream-json', '-p', fullPrompt,
];
let cmd = executable;
Expand Down
Loading