scalix 1.5.0 → 1.5.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.
- package/.env.example +5 -3
- package/CHANGELOG.md +24 -0
- package/LICENSE +0 -1
- package/README.md +47 -2
- package/dist/agent-presets.js +33 -0
- package/dist/agent.js +358 -42
- package/dist/api-client.js +12 -4
- package/dist/board-context.js +81 -0
- package/dist/browser-access.js +0 -0
- package/dist/cli.js +435 -94
- package/dist/cloud.js +95 -6
- package/dist/config.js +44 -5
- package/dist/constants.js +121 -19
- package/dist/context-builder.js +9 -1
- package/dist/feedback.js +51 -0
- package/dist/headless.js +44 -13
- package/dist/ipc-server.js +362 -99
- package/dist/keychain.js +34 -9
- package/dist/mcp.js +470 -28
- package/dist/openrouter-oauth.js +149 -0
- package/dist/project-index.js +46 -23
- package/dist/sandbox.js +115 -27
- package/dist/sessions.js +12 -0
- package/dist/settings.js +33 -2
- package/dist/slash-commands.js +6 -0
- package/dist/sub-agent.js +3 -2
- package/dist/tools-browser.js +87 -16
- package/dist/tools-extra.js +15 -3
- package/dist/tools-memory.js +74 -0
- package/dist/tools-web.js +67 -6
- package/dist/tools.js +15 -14
- package/dist/workspace-trust.js +7 -1
- package/package.json +3 -1
package/dist/headless.js
CHANGED
|
@@ -4,17 +4,18 @@ import { execSync } from 'node:child_process';
|
|
|
4
4
|
import { createAgent } 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';
|
|
16
|
-
import { DEFAULT_MODELS, DEFAULT_INSTRUCTIONS, ENV_MODEL, HEADLESS_DEFAULTS, fetchModelSpecs } from './constants.js';
|
|
17
|
-
import { loadConfig, resolveActiveProvider, DEFAULT_LIMITS } from './settings.js';
|
|
17
|
+
import { DEFAULT_MODELS, DEFAULT_INSTRUCTIONS, ENV_MODEL, HEADLESS_DEFAULTS, fetchModelSpecs, normalizeModelId } from './constants.js';
|
|
18
|
+
import { loadConfig, loadGlobalConfig, saveConfig, resolveActiveProvider, DEFAULT_LIMITS } from './settings.js';
|
|
18
19
|
import { checkForUpdate, getUpdateBanner } from './update-checker.js';
|
|
19
20
|
import { getModeDescription, PERMISSION_MODES, setSandboxRoot } from './sandbox.js';
|
|
20
21
|
import { collectGitInfo, gitInfoToContext } from './git-context.js';
|
|
@@ -155,7 +156,7 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
155
156
|
}).catch(() => { });
|
|
156
157
|
}
|
|
157
158
|
const envModels = process.env[ENV_MODEL]
|
|
158
|
-
? process.env[ENV_MODEL].split(',').map((m) => m.trim()).filter(Boolean)
|
|
159
|
+
? process.env[ENV_MODEL].split(',').map((m) => m.trim()).filter(Boolean).map(normalizeModelId)
|
|
159
160
|
: [];
|
|
160
161
|
const selectedModel = envModels.length > 0 ? envModels
|
|
161
162
|
: config.models.length > 0 ? config.models : DEFAULT_MODELS;
|
|
@@ -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) {
|
|
@@ -194,6 +194,8 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
194
194
|
apiKey,
|
|
195
195
|
baseUrl,
|
|
196
196
|
provider: active.provider,
|
|
197
|
+
cloudApiKey: active.cloudApiKey,
|
|
198
|
+
cloudBaseUrl: active.cloudBaseUrl,
|
|
197
199
|
model: selectedModel,
|
|
198
200
|
instructions,
|
|
199
201
|
tools: allTools,
|
|
@@ -212,11 +214,18 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
212
214
|
groundingCheck: true,
|
|
213
215
|
contextRetrieval: true,
|
|
214
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`));
|
|
215
223
|
// Register the account's Scalix Cloud platform tools (one attempt; failure
|
|
216
224
|
// is stated once and the run continues with local tools only).
|
|
217
225
|
const cloudStatus = await agent.refreshCloudTools();
|
|
218
226
|
if (cloudStatus.state === 'unavailable') {
|
|
219
|
-
|
|
227
|
+
const remedy = cloudStatus.remedy ? ` ${cloudStatus.remedy}` : '';
|
|
228
|
+
process.stderr.write(`\x1b[33m☁ Scalix Cloud unavailable — running with local tools only. ${cloudStatus.error ?? ''}${remedy}\x1b[0m\n`);
|
|
220
229
|
}
|
|
221
230
|
let fullResponse = '';
|
|
222
231
|
agent.on('tool:call', (name) => process.stderr.write(`\x1b[33m▶ ${name}\x1b[0m\n`));
|
|
@@ -268,6 +277,7 @@ async function runSinglePrompt(prompt, output, format, outputSchemaPath, beforeE
|
|
|
268
277
|
process.stdout.write(formatted + '\n');
|
|
269
278
|
}
|
|
270
279
|
await closeBrowser();
|
|
280
|
+
await mcpHub.close(); // stdio servers are child processes; don't orphan them
|
|
271
281
|
if (beforeExit)
|
|
272
282
|
await beforeExit();
|
|
273
283
|
process.exit(0);
|
|
@@ -319,14 +329,13 @@ async function runInteractive() {
|
|
|
319
329
|
if (active.provider === 'scalix')
|
|
320
330
|
fetchModelSpecs(baseUrl, apiKey).catch(() => { });
|
|
321
331
|
const envModels = process.env[ENV_MODEL]
|
|
322
|
-
? process.env[ENV_MODEL].split(',').map((m) => m.trim()).filter(Boolean)
|
|
332
|
+
? process.env[ENV_MODEL].split(',').map((m) => m.trim()).filter(Boolean).map(normalizeModelId)
|
|
323
333
|
: [];
|
|
324
334
|
const selectedModel = envModels.length > 0 ? envModels
|
|
325
335
|
: config.models.length > 0 ? config.models : DEFAULT_MODELS;
|
|
326
336
|
if (selectedModel.length === 0) {
|
|
327
337
|
throw new Error('No models configured. Set SCALIX_MODEL env var or run interactive setup.');
|
|
328
338
|
}
|
|
329
|
-
const mcpTools = loadMcpServices();
|
|
330
339
|
// Only discover/load project-local plugins in a trusted workspace — plugins
|
|
331
340
|
// dynamically import and execute code from the project directory.
|
|
332
341
|
const plugins = isWorkspaceTrusted() ? await discoverPlugins(process.cwd()) : [];
|
|
@@ -334,7 +343,7 @@ async function runInteractive() {
|
|
|
334
343
|
process.stderr.write('Project plugins skipped (untrusted workspace).\n');
|
|
335
344
|
}
|
|
336
345
|
const pluginTools = getPluginTools(plugins);
|
|
337
|
-
const allTools = [...defaultTools, ...extraTools, ...webTools, ...advancedTools, ...visionTools, ...voiceTools, ...browserTools, ...
|
|
346
|
+
const allTools = [...defaultTools, ...extraTools, ...memoryTools, ...webTools, ...advancedTools, ...visionTools, ...voiceTools, ...browserTools, ...pluginTools];
|
|
338
347
|
const instructions = await buildEnhancedInstructions();
|
|
339
348
|
const rl = readline.createInterface({
|
|
340
349
|
input: process.stdin,
|
|
@@ -355,6 +364,8 @@ async function runInteractive() {
|
|
|
355
364
|
apiKey,
|
|
356
365
|
baseUrl,
|
|
357
366
|
provider: active.provider,
|
|
367
|
+
cloudApiKey: active.cloudApiKey,
|
|
368
|
+
cloudBaseUrl: active.cloudBaseUrl,
|
|
358
369
|
model: selectedModel,
|
|
359
370
|
instructions,
|
|
360
371
|
tools: allTools,
|
|
@@ -378,6 +389,9 @@ async function runInteractive() {
|
|
|
378
389
|
return answer.trim().toLowerCase() === 'y';
|
|
379
390
|
},
|
|
380
391
|
});
|
|
392
|
+
if (agent.getSkippedHookCount() > 0) {
|
|
393
|
+
process.stderr.write(`Project hooks skipped (untrusted workspace): ${agent.getSkippedHookCount()}\n`);
|
|
394
|
+
}
|
|
381
395
|
let sessionId = generateSessionId();
|
|
382
396
|
agent.on('thinking:start', () => process.stdout.write('\n\x1b[33mThinking...\x1b[0m'));
|
|
383
397
|
agent.on('tool:call', (name, args) => {
|
|
@@ -408,11 +422,15 @@ async function runInteractive() {
|
|
|
408
422
|
console.log(`\x1b[33m\u26A0 Context usage: ${info.percentage}% (${info.tokens}/${info.maxTokens} tokens)\x1b[0m`);
|
|
409
423
|
}
|
|
410
424
|
});
|
|
425
|
+
// Connect configured MCP servers and register their tools. Warnings only:
|
|
426
|
+
// a server that will not start must not take the whole run down with it.
|
|
427
|
+
const mcpHub = await startMcp(agent, (m) => process.stderr.write(`\x1b[33m⚠ ${m}\x1b[0m\n`));
|
|
411
428
|
// Register the account's Scalix Cloud platform tools (one attempt; failure
|
|
412
429
|
// is stated once and the session continues with local tools only).
|
|
413
430
|
const cloudStatus = await agent.refreshCloudTools();
|
|
414
431
|
if (cloudStatus.state === 'unavailable') {
|
|
415
|
-
|
|
432
|
+
const remedy = cloudStatus.remedy ? ` ${cloudStatus.remedy}` : '';
|
|
433
|
+
console.log(`\x1b[33m☁ Scalix Cloud unavailable — running with local tools only. ${cloudStatus.error ?? ''}${remedy}\x1b[0m`);
|
|
416
434
|
}
|
|
417
435
|
const toolCount = allTools.length + cloudStatus.toolCount;
|
|
418
436
|
const modelDisplay = selectedModel.map(shortModel).join(' | ');
|
|
@@ -439,6 +457,7 @@ async function runInteractive() {
|
|
|
439
457
|
console.log('Goodbye!');
|
|
440
458
|
rl.close();
|
|
441
459
|
await closeBrowser().catch(() => { });
|
|
460
|
+
await mcpHub.close().catch(() => { }); // stdio servers are our children
|
|
442
461
|
process.exit(0);
|
|
443
462
|
}
|
|
444
463
|
process.on('SIGINT', () => { shutdown().catch(() => process.exit(1)); });
|
|
@@ -502,8 +521,16 @@ async function runInteractive() {
|
|
|
502
521
|
console.log(`\nInvalid model name: "${name}"\n`);
|
|
503
522
|
return true;
|
|
504
523
|
}
|
|
505
|
-
|
|
506
|
-
|
|
524
|
+
const chosen = normalizeModelId(name);
|
|
525
|
+
agent.setModel(chosen);
|
|
526
|
+
// Persist like the TUI does — an in-memory-only switch silently
|
|
527
|
+
// reverted on the next launch. Global config only (no project-override
|
|
528
|
+
// laundering), chain-preserving (chosen model to the front).
|
|
529
|
+
loadGlobalConfig().then((cfg) => {
|
|
530
|
+
cfg.models = [chosen, ...cfg.models.filter((m) => m !== chosen)];
|
|
531
|
+
return saveConfig(cfg);
|
|
532
|
+
}).catch(() => { });
|
|
533
|
+
console.log(`\nSwitched to ${shortModel(chosen)}${process.env[ENV_MODEL] ? '\n(note: SCALIX_MODEL is set and will override this on next launch)' : ''}\n`);
|
|
507
534
|
return true;
|
|
508
535
|
}
|
|
509
536
|
if (cmd === '/compact') {
|
|
@@ -596,6 +623,10 @@ async function runInteractive() {
|
|
|
596
623
|
return true;
|
|
597
624
|
}
|
|
598
625
|
if (cmd === '/hooks') {
|
|
626
|
+
const skipped = agent.getSkippedHookCount();
|
|
627
|
+
if (skipped > 0) {
|
|
628
|
+
console.log(`\n${skipped} project hook(s) skipped — untrusted workspace.`);
|
|
629
|
+
}
|
|
599
630
|
console.log('\n' + hooksSummary(agent.getHooks()) + '\n');
|
|
600
631
|
return true;
|
|
601
632
|
}
|