scalix 1.5.1 → 1.6.0
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/.env.example +5 -3
- package/CHANGELOG.md +40 -0
- package/LICENSE +0 -1
- package/README.md +91 -3
- package/dist/agent-presets.js +33 -0
- package/dist/agent.js +372 -43
- package/dist/api-client.js +64 -5
- package/dist/board-context.js +81 -0
- package/dist/browser-access.js +0 -0
- package/dist/cli.js +397 -45
- package/dist/cloud.js +1 -1
- package/dist/command-sandbox.js +146 -0
- package/dist/commands.js +18 -6
- package/dist/config.js +44 -5
- package/dist/constants.js +90 -11
- package/dist/context-builder.js +9 -1
- package/dist/cost-tracker.js +1 -1
- package/dist/feedback.js +51 -0
- package/dist/headless.js +43 -9
- package/dist/ipc-server.js +371 -110
- package/dist/keychain.js +34 -9
- package/dist/marketplace.js +400 -0
- package/dist/mcp.js +498 -29
- package/dist/openrouter-oauth.js +149 -0
- package/dist/project-index.js +46 -23
- package/dist/providers/index.js +21 -4
- package/dist/sandbox.js +115 -27
- package/dist/secure-fetch.js +117 -0
- package/dist/sessions.js +12 -0
- package/dist/settings.js +193 -44
- package/dist/slash-commands.js +8 -0
- package/dist/sub-agent.js +6 -2
- package/dist/tools-browser.js +95 -18
- package/dist/tools-extra.js +101 -7
- package/dist/tools-memory.js +74 -0
- package/dist/tools-web.js +92 -28
- package/dist/tools.js +99 -23
- package/dist/workspace-trust.js +11 -1
- package/dist/worktree.js +24 -5
- package/package.json +5 -3
package/dist/headless.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import readline from 'node:readline';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
|
-
import { createAgent } from './agent.js';
|
|
4
|
+
import { createAgent, REPORTED_ERROR } from './agent.js';
|
|
5
5
|
import { defaultTools } from './tools.js';
|
|
6
6
|
import { extraTools } from './tools-extra.js';
|
|
7
|
+
import { memoryTools } from './tools-memory.js';
|
|
7
8
|
import { webTools } from './tools-web.js';
|
|
8
9
|
import { advancedTools } from './tools-advanced.js';
|
|
9
10
|
import { visionTools } from './tools-vision.js';
|
|
10
11
|
import { voiceTools } from './tools-voice.js';
|
|
11
12
|
import { browserTools, closeBrowser } from './tools-browser.js';
|
|
12
|
-
import {
|
|
13
|
+
import { startMcp } from './mcp.js';
|
|
13
14
|
import { costTracker } from './cost-tracker.js';
|
|
14
15
|
import { saveSession, loadSession, listSessions, generateSessionId } from './sessions.js';
|
|
15
16
|
import { logger } from './logger.js';
|
|
@@ -163,7 +164,6 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
163
164
|
console.error('Error: No models configured.\nSet SCALIX_MODEL env var or run interactive setup: scalix');
|
|
164
165
|
process.exit(1);
|
|
165
166
|
}
|
|
166
|
-
const mcpTools = loadMcpServices();
|
|
167
167
|
// Only discover/load project-local plugins in a trusted workspace — plugins
|
|
168
168
|
// dynamically import and execute code from the project directory.
|
|
169
169
|
const plugins = isWorkspaceTrusted() ? await discoverPlugins(process.cwd()) : [];
|
|
@@ -171,7 +171,7 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
171
171
|
process.stderr.write('Project plugins skipped (untrusted workspace).\n');
|
|
172
172
|
}
|
|
173
173
|
const pluginTools = getPluginTools(plugins);
|
|
174
|
-
const allTools = [...defaultTools, ...extraTools, ...webTools, ...advancedTools, ...visionTools, ...voiceTools, ...browserTools, ...
|
|
174
|
+
const allTools = [...defaultTools, ...extraTools, ...memoryTools, ...webTools, ...advancedTools, ...visionTools, ...voiceTools, ...browserTools, ...pluginTools];
|
|
175
175
|
const instructions = await buildEnhancedInstructions();
|
|
176
176
|
let outputSchema;
|
|
177
177
|
if (outputSchemaPath) {
|
|
@@ -214,6 +214,12 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
214
214
|
groundingCheck: true,
|
|
215
215
|
contextRetrieval: true,
|
|
216
216
|
});
|
|
217
|
+
if (agent.getSkippedHookCount() > 0) {
|
|
218
|
+
process.stderr.write(`Project hooks skipped (untrusted workspace): ${agent.getSkippedHookCount()}\n`);
|
|
219
|
+
}
|
|
220
|
+
// Connect configured MCP servers and register their tools. Warnings only:
|
|
221
|
+
// a server that will not start must not take the whole run down with it.
|
|
222
|
+
const mcpHub = await startMcp(agent, (m) => process.stderr.write(`\x1b[33m⚠ ${m}\x1b[0m\n`));
|
|
217
223
|
// Register the account's Scalix Cloud platform tools (one attempt; failure
|
|
218
224
|
// is stated once and the run continues with local tools only).
|
|
219
225
|
const cloudStatus = await agent.refreshCloudTools();
|
|
@@ -235,7 +241,9 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
235
241
|
fullResponse = await agent.send(prompt);
|
|
236
242
|
}
|
|
237
243
|
catch (err) {
|
|
238
|
-
|
|
244
|
+
// The agent's 'error' event already printed this — don't print twice.
|
|
245
|
+
if (!err?.[REPORTED_ERROR])
|
|
246
|
+
console.error(`Error: ${err.message}`);
|
|
239
247
|
if (beforeExit)
|
|
240
248
|
await beforeExit();
|
|
241
249
|
process.exit(1);
|
|
@@ -271,6 +279,7 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
271
279
|
process.stdout.write(formatted + '\n');
|
|
272
280
|
}
|
|
273
281
|
await closeBrowser();
|
|
282
|
+
await mcpHub.close(); // stdio servers are child processes; don't orphan them
|
|
274
283
|
if (beforeExit)
|
|
275
284
|
await beforeExit();
|
|
276
285
|
process.exit(0);
|
|
@@ -329,7 +338,6 @@ async function runInteractive() {
|
|
|
329
338
|
if (selectedModel.length === 0) {
|
|
330
339
|
throw new Error('No models configured. Set SCALIX_MODEL env var or run interactive setup.');
|
|
331
340
|
}
|
|
332
|
-
const mcpTools = loadMcpServices();
|
|
333
341
|
// Only discover/load project-local plugins in a trusted workspace — plugins
|
|
334
342
|
// dynamically import and execute code from the project directory.
|
|
335
343
|
const plugins = isWorkspaceTrusted() ? await discoverPlugins(process.cwd()) : [];
|
|
@@ -337,7 +345,7 @@ async function runInteractive() {
|
|
|
337
345
|
process.stderr.write('Project plugins skipped (untrusted workspace).\n');
|
|
338
346
|
}
|
|
339
347
|
const pluginTools = getPluginTools(plugins);
|
|
340
|
-
const allTools = [...defaultTools, ...extraTools, ...webTools, ...advancedTools, ...visionTools, ...voiceTools, ...browserTools, ...
|
|
348
|
+
const allTools = [...defaultTools, ...extraTools, ...memoryTools, ...webTools, ...advancedTools, ...visionTools, ...voiceTools, ...browserTools, ...pluginTools];
|
|
341
349
|
const instructions = await buildEnhancedInstructions();
|
|
342
350
|
const rl = readline.createInterface({
|
|
343
351
|
input: process.stdin,
|
|
@@ -353,7 +361,11 @@ async function runInteractive() {
|
|
|
353
361
|
return Promise.resolve('/exit');
|
|
354
362
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
355
363
|
}
|
|
356
|
-
|
|
364
|
+
// Command templates can carry !`cmd` inline-bash — trust-gate like plugins.
|
|
365
|
+
const customCmds = isWorkspaceTrusted() ? await loadCustomCommands(process.cwd()) : [];
|
|
366
|
+
if (!isWorkspaceTrusted()) {
|
|
367
|
+
process.stderr.write('Project custom commands skipped (untrusted workspace).\n');
|
|
368
|
+
}
|
|
357
369
|
const agent = createAgent({
|
|
358
370
|
apiKey,
|
|
359
371
|
baseUrl,
|
|
@@ -383,6 +395,9 @@ async function runInteractive() {
|
|
|
383
395
|
return answer.trim().toLowerCase() === 'y';
|
|
384
396
|
},
|
|
385
397
|
});
|
|
398
|
+
if (agent.getSkippedHookCount() > 0) {
|
|
399
|
+
process.stderr.write(`Project hooks skipped (untrusted workspace): ${agent.getSkippedHookCount()}\n`);
|
|
400
|
+
}
|
|
386
401
|
let sessionId = generateSessionId();
|
|
387
402
|
agent.on('thinking:start', () => process.stdout.write('\n\x1b[33mThinking...\x1b[0m'));
|
|
388
403
|
agent.on('tool:call', (name, args) => {
|
|
@@ -413,6 +428,9 @@ async function runInteractive() {
|
|
|
413
428
|
console.log(`\x1b[33m\u26A0 Context usage: ${info.percentage}% (${info.tokens}/${info.maxTokens} tokens)\x1b[0m`);
|
|
414
429
|
}
|
|
415
430
|
});
|
|
431
|
+
// Connect configured MCP servers and register their tools. Warnings only:
|
|
432
|
+
// a server that will not start must not take the whole run down with it.
|
|
433
|
+
const mcpHub = await startMcp(agent, (m) => process.stderr.write(`\x1b[33m⚠ ${m}\x1b[0m\n`));
|
|
416
434
|
// Register the account's Scalix Cloud platform tools (one attempt; failure
|
|
417
435
|
// is stated once and the session continues with local tools only).
|
|
418
436
|
const cloudStatus = await agent.refreshCloudTools();
|
|
@@ -445,6 +463,7 @@ async function runInteractive() {
|
|
|
445
463
|
console.log('Goodbye!');
|
|
446
464
|
rl.close();
|
|
447
465
|
await closeBrowser().catch(() => { });
|
|
466
|
+
await mcpHub.close().catch(() => { }); // stdio servers are our children
|
|
448
467
|
process.exit(0);
|
|
449
468
|
}
|
|
450
469
|
process.on('SIGINT', () => { shutdown().catch(() => process.exit(1)); });
|
|
@@ -610,6 +629,10 @@ async function runInteractive() {
|
|
|
610
629
|
return true;
|
|
611
630
|
}
|
|
612
631
|
if (cmd === '/hooks') {
|
|
632
|
+
const skipped = agent.getSkippedHookCount();
|
|
633
|
+
if (skipped > 0) {
|
|
634
|
+
console.log(`\n${skipped} project hook(s) skipped — untrusted workspace.`);
|
|
635
|
+
}
|
|
613
636
|
console.log('\n' + hooksSummary(agent.getHooks()) + '\n');
|
|
614
637
|
return true;
|
|
615
638
|
}
|
|
@@ -626,7 +649,18 @@ async function runInteractive() {
|
|
|
626
649
|
const cheapModel = pickCheapestModel(agent.getModels());
|
|
627
650
|
console.log(`\nDelegating to sub-agent (${shortModel(cheapModel)})...`);
|
|
628
651
|
try {
|
|
629
|
-
const result = await spawnSubAgent({
|
|
652
|
+
const result = await spawnSubAgent({
|
|
653
|
+
apiKey: apiKey,
|
|
654
|
+
baseUrl,
|
|
655
|
+
// Inherit the parent's provider + cloud credentials so a BYOK
|
|
656
|
+
// session delegates through the same endpoint dialect it runs on;
|
|
657
|
+
// without `provider` the sub-agent defaults to the Scalix client
|
|
658
|
+
// and 404s on every request against a third-party base URL.
|
|
659
|
+
provider: active.provider,
|
|
660
|
+
cloudApiKey: active.cloudApiKey,
|
|
661
|
+
cloudBaseUrl: active.cloudBaseUrl,
|
|
662
|
+
models: agent.getModels(),
|
|
663
|
+
}, prompt, { model: cheapModel, tools: 'read-only', maxSteps: 10 });
|
|
630
664
|
console.log(`\n${result.response}\n`);
|
|
631
665
|
console.log(`\x1b[36mSub-agent: ${result.steps} steps, ~${result.tokenEstimate} tokens\x1b[0m\n`);
|
|
632
666
|
}
|