amalgm 0.1.79 → 0.1.80
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/package.json +1 -1
- package/runtime/lib/chatInput.js +0 -4
- package/runtime/scripts/amalgm-mcp/agent-config/compiler/instructions.js +70 -0
- package/runtime/scripts/amalgm-mcp/agent-config/loadout.js +65 -0
- package/runtime/scripts/amalgm-mcp/agent-config/renderers/instructions.js +9 -49
- package/runtime/scripts/amalgm-mcp/agent-config/rest.js +10 -10
- package/runtime/scripts/amalgm-mcp/agent-config/schema.js +15 -31
- package/runtime/scripts/amalgm-mcp/agent-config/store.js +21 -5
- package/runtime/scripts/amalgm-mcp/agents/rest.js +34 -34
- package/runtime/scripts/amalgm-mcp/agents/store.js +19 -28
- package/runtime/scripts/amalgm-mcp/agents/talk.js +10 -12
- package/runtime/scripts/amalgm-mcp/agents/tools.js +1 -1
- package/runtime/scripts/amalgm-mcp/automations/runner.js +1 -6
- package/runtime/scripts/amalgm-mcp/email/inbound.js +12 -8
- package/runtime/scripts/amalgm-mcp/mcp-connections/rest.js +0 -13
- package/runtime/scripts/amalgm-mcp/server/mcp-request-context.js +5 -0
- package/runtime/scripts/amalgm-mcp/slack/inbound.js +12 -8
- package/runtime/scripts/amalgm-mcp/tests/agent-config.test.js +1 -1
- package/runtime/scripts/amalgm-mcp/tests/agents-source-of-truth.test.js +3 -5
- package/runtime/scripts/amalgm-mcp/tests/chat-payloads.test.js +1 -1
- package/runtime/scripts/amalgm-mcp/tests/mcp-surface.test.js +2 -12
- package/runtime/scripts/amalgm-mcp/tests/toolbox-service.test.js +3 -4
- package/runtime/scripts/amalgm-mcp/toolbox/loadout-context.js +57 -0
- package/runtime/scripts/amalgm-mcp/toolbox/mcp-surface.js +8 -8
- package/runtime/scripts/amalgm-mcp/toolbox/service.js +17 -13
- package/runtime/scripts/amalgm-mcp/workflows/runner.js +1 -6
- package/runtime/scripts/chat-core/chat-payload.js +165 -0
- package/runtime/scripts/chat-core/server.js +4 -2
- package/runtime/scripts/chat-core/tests/chat-payload.test.js +78 -0
- package/runtime/scripts/amalgm-mcp/toolbox/selection.js +0 -42
|
@@ -32,6 +32,9 @@ const {
|
|
|
32
32
|
readUserPreferences,
|
|
33
33
|
} = require('../lib/prefs');
|
|
34
34
|
const credentialAdapter = require('../../credential-adapter');
|
|
35
|
+
const {
|
|
36
|
+
normalizeLoadout,
|
|
37
|
+
} = require('../agent-config/loadout');
|
|
35
38
|
|
|
36
39
|
const DEFAULT_AGENT_STATUS = 'unknown';
|
|
37
40
|
const CUSTOM_AGENT_STATUS = 'ready';
|
|
@@ -89,9 +92,8 @@ const BUILTIN_AGENTS = BUILTIN_AGENT_BLUEPRINTS.map((agent) => ({
|
|
|
89
92
|
files: [],
|
|
90
93
|
skills: [],
|
|
91
94
|
mcpAppIds: [],
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
mcp: { inheritAll: true, customServers: [], appIds: [], nativeMcps: [] },
|
|
95
|
+
loadout: { toolIds: [] },
|
|
96
|
+
mcp: { inheritAll: false, customServers: [], appIds: [] },
|
|
95
97
|
builtin: false,
|
|
96
98
|
deletable: true,
|
|
97
99
|
editable: true,
|
|
@@ -230,30 +232,23 @@ function defaultCapabilities(agent) {
|
|
|
230
232
|
streaming: true,
|
|
231
233
|
resume: true,
|
|
232
234
|
cancel: true,
|
|
233
|
-
tools:
|
|
235
|
+
tools: 'selected',
|
|
234
236
|
};
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
function normalizeMcpTransportConfig(
|
|
239
|
+
function normalizeMcpTransportConfig(loadout, mcpAppIds) {
|
|
238
240
|
return {
|
|
239
|
-
inheritAll:
|
|
241
|
+
inheritAll: false,
|
|
240
242
|
customServers: [],
|
|
241
243
|
appIds: normalizeStringList(mcpAppIds),
|
|
242
|
-
nativeMcps: normalizeStringList(nativeMcps),
|
|
243
244
|
};
|
|
244
245
|
}
|
|
245
246
|
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
?? raw?.selected
|
|
251
|
-
?? [],
|
|
247
|
+
function normalizeAgentLoadout(input, existing) {
|
|
248
|
+
return normalizeLoadout(
|
|
249
|
+
input?.loadout ?? input?.toolset ?? input?.tools,
|
|
250
|
+
existing?.loadout ?? existing?.tools,
|
|
252
251
|
);
|
|
253
|
-
return {
|
|
254
|
-
mode: raw?.mode === 'selected' ? 'selected' : 'all',
|
|
255
|
-
selectedToolIds,
|
|
256
|
-
};
|
|
257
252
|
}
|
|
258
253
|
|
|
259
254
|
function normalizeAgent(input, existing) {
|
|
@@ -262,8 +257,8 @@ function normalizeAgent(input, existing) {
|
|
|
262
257
|
const builtin = input.builtin === true;
|
|
263
258
|
const baseHarnessId = nonEmptyString(input.baseHarnessId || input.base_harness_id) || id;
|
|
264
259
|
const adapter = nonEmptyString(input.adapter) || baseHarnessId;
|
|
265
|
-
const
|
|
266
|
-
const mcpConfig = normalizeMcpTransportConfig(
|
|
260
|
+
const loadout = normalizeAgentLoadout(input, existing);
|
|
261
|
+
const mcpConfig = normalizeMcpTransportConfig(loadout, input.mcpAppIds);
|
|
267
262
|
const ownerComputerId = nonEmptyString(input.ownerComputerId || input.owner_computer_id)
|
|
268
263
|
|| nonEmptyString(existing?.ownerComputerId)
|
|
269
264
|
|| computerId();
|
|
@@ -286,11 +281,9 @@ function normalizeAgent(input, existing) {
|
|
|
286
281
|
files: normalizeStringList(input.files),
|
|
287
282
|
skills: normalizeStringList(input.skills),
|
|
288
283
|
mcpAppIds: normalizeStringList(mcpConfig.appIds),
|
|
289
|
-
|
|
290
|
-
tools: toolConfig,
|
|
284
|
+
loadout,
|
|
291
285
|
mcp: {
|
|
292
286
|
...mcpConfig,
|
|
293
|
-
inheritAll: toolConfig.mode !== 'selected',
|
|
294
287
|
},
|
|
295
288
|
authMethod: input.authMethod || existing?.authMethod || persistedAuthMethod(baseHarnessId),
|
|
296
289
|
location,
|
|
@@ -300,8 +293,8 @@ function normalizeAgent(input, existing) {
|
|
|
300
293
|
installStatus: input.installStatus || existing?.installStatus || (builtin ? 'unknown' : 'external'),
|
|
301
294
|
authStatus: input.authStatus || existing?.authStatus || 'unknown',
|
|
302
295
|
capabilities: isObject(input.capabilities)
|
|
303
|
-
? { ...defaultCapabilities({
|
|
304
|
-
: defaultCapabilities({
|
|
296
|
+
? { ...defaultCapabilities({ loadout }), ...input.capabilities }
|
|
297
|
+
: defaultCapabilities({ loadout }),
|
|
305
298
|
builtin,
|
|
306
299
|
deletable: input.deletable === false ? false : true,
|
|
307
300
|
editable: input.editable === false ? false : true,
|
|
@@ -469,13 +462,11 @@ function seedBuiltinAgents(options = {}) {
|
|
|
469
462
|
files: existing?.files || [],
|
|
470
463
|
skills: existing?.skills || [],
|
|
471
464
|
mcpAppIds: existing?.mcpAppIds || [],
|
|
472
|
-
|
|
473
|
-
tools: existing?.tools || { mode: 'all', selectedToolIds: [] },
|
|
465
|
+
loadout: existing?.loadout || existing?.tools || { toolIds: [] },
|
|
474
466
|
mcp: {
|
|
475
|
-
inheritAll:
|
|
467
|
+
inheritAll: false,
|
|
476
468
|
customServers: [],
|
|
477
469
|
appIds: existing?.mcpAppIds || [],
|
|
478
|
-
nativeMcps: [],
|
|
479
470
|
},
|
|
480
471
|
builtin: false,
|
|
481
472
|
deletable: true,
|
|
@@ -38,8 +38,9 @@ const {
|
|
|
38
38
|
getChatInputText,
|
|
39
39
|
normalizeChatInput,
|
|
40
40
|
} = require('../../../lib/chatInput');
|
|
41
|
-
const {
|
|
41
|
+
const { agentFieldsFromConfig, getAgentConfig } = require('../agent-config/store');
|
|
42
42
|
const { renderAgentInstructions } = require('../agent-config/renderers/instructions');
|
|
43
|
+
const { loadoutToolIds } = require('../agent-config/loadout');
|
|
43
44
|
const credentialAdapter = require('../../credential-adapter');
|
|
44
45
|
const { buildLocalMcpServerConfigs } = require('../lib/mcp-resolver');
|
|
45
46
|
const {
|
|
@@ -599,19 +600,17 @@ async function handleTalkToAgent(args, context = {}) {
|
|
|
599
600
|
agent.authMethod || preferredAuthMethod,
|
|
600
601
|
);
|
|
601
602
|
const agentConfig = getAgentConfig(agent);
|
|
602
|
-
const
|
|
603
|
+
const agentFields = agentFieldsFromConfig(agentConfig);
|
|
603
604
|
const agentFiles = normalizeStringList(agent.files);
|
|
604
|
-
const agentSkills = normalizeStringList(
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
agentTools.selectedToolIds,
|
|
609
|
-
);
|
|
605
|
+
const agentSkills = normalizeStringList(agentFields.skills);
|
|
606
|
+
const agentLoadout = agentConfig.loadout || agent.loadout || agent.tools || { toolIds: [] };
|
|
607
|
+
const agentLoadoutToolIds = loadoutToolIds(agentLoadout);
|
|
608
|
+
const agentMcpAppIds = toolboxService.externalMcpToolIdsForLoadout(agentLoadoutToolIds);
|
|
610
609
|
const baseAgentSystemPrompt = [
|
|
611
610
|
renderAgentInstructions(agentConfig, { includeAgentToAgentPrompt: true }),
|
|
612
611
|
buildAgentFilesBlock(agentFiles),
|
|
613
612
|
].filter(Boolean).join('\n\n') || buildAgentInstructions(
|
|
614
|
-
|
|
613
|
+
agentFields.systemPrompt || agent.systemPrompt,
|
|
615
614
|
agentFiles,
|
|
616
615
|
agentSkills,
|
|
617
616
|
);
|
|
@@ -641,8 +640,7 @@ async function handleTalkToAgent(args, context = {}) {
|
|
|
641
640
|
},
|
|
642
641
|
tools: {
|
|
643
642
|
...((isObject(chatInput) && isObject(chatInput.tools)) ? chatInput.tools : {}),
|
|
644
|
-
|
|
645
|
-
toolIds: normalizeStringList(agentTools.selectedToolIds),
|
|
643
|
+
toolIds: agentLoadoutToolIds,
|
|
646
644
|
mcpAppIds: agentMcpAppIds,
|
|
647
645
|
},
|
|
648
646
|
execution: {
|
|
@@ -783,7 +781,7 @@ async function handleTalkToAgent(args, context = {}) {
|
|
|
783
781
|
files: agentFiles,
|
|
784
782
|
skills: agentSkills,
|
|
785
783
|
mcpAppIds: agentMcpAppIds,
|
|
786
|
-
|
|
784
|
+
loadout: agentConfig.loadout,
|
|
787
785
|
agentConfig,
|
|
788
786
|
...(parentOrigin || {}),
|
|
789
787
|
},
|
|
@@ -64,7 +64,7 @@ module.exports = [
|
|
|
64
64
|
getSelectedModel(agent.baseHarnessId) ||
|
|
65
65
|
DEFAULT_SELECTED_MODELS[agent.baseHarnessId],
|
|
66
66
|
systemPrompt: agent.systemPrompt ? '(configured)' : '(none)',
|
|
67
|
-
|
|
67
|
+
loadout: agent.loadout || { toolIds: [] },
|
|
68
68
|
location: agent.location || null,
|
|
69
69
|
locationName: agent.locationName || null,
|
|
70
70
|
ownerComputerId: agent.ownerComputerId || null,
|
|
@@ -311,12 +311,7 @@ async function runToolCell(cell, args, allowlist) {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
const result = await callToolboxMcpTool(toolboxMcpToolName(resolved.action), args, {
|
|
314
|
-
|
|
315
|
-
tools: {
|
|
316
|
-
mode: 'selected',
|
|
317
|
-
selectedToolIds: [resolved.tool.id, resolved.action.id],
|
|
318
|
-
},
|
|
319
|
-
},
|
|
314
|
+
loadout: { toolIds: [resolved.tool.id, resolved.action.id] },
|
|
320
315
|
});
|
|
321
316
|
return mcpResultToOutput(result, actionKey);
|
|
322
317
|
}
|
|
@@ -25,6 +25,9 @@ const {
|
|
|
25
25
|
resolveModelSelection,
|
|
26
26
|
DEFAULT_SELECTED_MODELS,
|
|
27
27
|
} = require('../lib/prefs');
|
|
28
|
+
const { getAgentConfig } = require('../agent-config/store');
|
|
29
|
+
const { loadoutToolIds } = require('../agent-config/loadout');
|
|
30
|
+
const { defaultToolboxService: toolboxService } = require('../toolbox/service');
|
|
28
31
|
const { buildLocalMcpServerConfigs } = require('../lib/mcp-resolver');
|
|
29
32
|
const {
|
|
30
33
|
chatInputToLegacyFields,
|
|
@@ -50,12 +53,14 @@ function normalizeStringList(values) {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
function sessionToolConfig(metadata) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
const agentId = asString(metadata?.agentConfigId) || asString(metadata?.agentId);
|
|
57
|
+
const loadout = agentId
|
|
58
|
+
? getAgentConfig(agentId).loadout
|
|
59
|
+
: metadata?.loadout || metadata?.tools || {};
|
|
60
|
+
const toolIds = loadoutToolIds(loadout);
|
|
56
61
|
return {
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
toolIds,
|
|
63
|
+
mcpAppIds: toolboxService.externalMcpToolIdsForLoadout(toolIds),
|
|
59
64
|
};
|
|
60
65
|
}
|
|
61
66
|
|
|
@@ -154,8 +159,7 @@ async function handleEmailInbound(body, sendJson) {
|
|
|
154
159
|
systemPrompt: asString(metadata.systemPrompt),
|
|
155
160
|
},
|
|
156
161
|
tools: {
|
|
157
|
-
mcpAppIds:
|
|
158
|
-
mode: toolConfig.mode,
|
|
162
|
+
mcpAppIds: toolConfig.mcpAppIds,
|
|
159
163
|
toolIds: toolConfig.toolIds,
|
|
160
164
|
},
|
|
161
165
|
execution: {
|
|
@@ -170,7 +174,7 @@ async function handleEmailInbound(body, sendJson) {
|
|
|
170
174
|
cwd,
|
|
171
175
|
computerId,
|
|
172
176
|
systemPrompt: asString(metadata.systemPrompt),
|
|
173
|
-
mcpAppIds:
|
|
177
|
+
mcpAppIds: toolConfig.mcpAppIds,
|
|
174
178
|
});
|
|
175
179
|
|
|
176
180
|
const legacyChatFields = chatInputToLegacyFields(normalizedChatInput, {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
|
-
const os = require('os');
|
|
8
7
|
const path = require('path');
|
|
9
8
|
const { AMALGM_DIR } = require('../config');
|
|
10
9
|
const { appendStateEvent } = require('../state/events');
|
|
@@ -13,7 +12,6 @@ const {
|
|
|
13
12
|
} = require('../toolbox/service');
|
|
14
13
|
|
|
15
14
|
const CONNECTIONS_FILE = path.join(AMALGM_DIR, 'mcp-connections.json');
|
|
16
|
-
const CLAUDE_FILE = path.join(os.homedir(), '.claude.json');
|
|
17
15
|
let mcpRegistry = null;
|
|
18
16
|
let mcpRegistryLoadAttempted = false;
|
|
19
17
|
let warnedRegistryLoadFailure = false;
|
|
@@ -96,21 +94,10 @@ function sanitizeConnections(connections) {
|
|
|
96
94
|
return sanitized;
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
function readNativeMcps() {
|
|
100
|
-
try {
|
|
101
|
-
const raw = fs.readFileSync(CLAUDE_FILE, 'utf8');
|
|
102
|
-
const parsed = JSON.parse(raw);
|
|
103
|
-
return Array.isArray(parsed?.claudeAiMcpEverConnected) ? parsed.claudeAiMcpEverConnected : [];
|
|
104
|
-
} catch {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
97
|
function buildConnectionsSnapshot() {
|
|
110
98
|
const file = readConnectionsFile();
|
|
111
99
|
return {
|
|
112
100
|
connections: sanitizeConnections(file.connections),
|
|
113
|
-
nativeMcps: readNativeMcps(),
|
|
114
101
|
};
|
|
115
102
|
}
|
|
116
103
|
|
|
@@ -21,6 +21,11 @@ async function buildMcpRequestContext(extra) {
|
|
|
21
21
|
const parent = Array.isArray(rows) ? rows[0] : null;
|
|
22
22
|
if (parent) {
|
|
23
23
|
ctx.sessionMetadata = parent.metadata || {};
|
|
24
|
+
ctx.agentConfigId =
|
|
25
|
+
parent.metadata?.agentConfigId
|
|
26
|
+
|| parent.metadata?.agentId
|
|
27
|
+
|| parent.metadata?.customAgentId
|
|
28
|
+
|| undefined;
|
|
24
29
|
ctx.originName = parent.title || undefined;
|
|
25
30
|
ctx.originHarnessId = parent.harness || undefined;
|
|
26
31
|
ctx.originBaseHarnessId = parent.metadata?.baseHarnessId || undefined;
|
|
@@ -23,6 +23,9 @@ const {
|
|
|
23
23
|
resolveModelSelection,
|
|
24
24
|
DEFAULT_SELECTED_MODELS,
|
|
25
25
|
} = require('../lib/prefs');
|
|
26
|
+
const { getAgentConfig } = require('../agent-config/store');
|
|
27
|
+
const { loadoutToolIds } = require('../agent-config/loadout');
|
|
28
|
+
const { defaultToolboxService: toolboxService } = require('../toolbox/service');
|
|
26
29
|
const { buildLocalMcpServerConfigs } = require('../lib/mcp-resolver');
|
|
27
30
|
const {
|
|
28
31
|
chatInputToLegacyFields,
|
|
@@ -48,12 +51,14 @@ function normalizeStringList(values) {
|
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
function sessionToolConfig(metadata) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
const agentId = asString(metadata?.agentConfigId) || asString(metadata?.agentId);
|
|
55
|
+
const loadout = agentId
|
|
56
|
+
? getAgentConfig(agentId).loadout
|
|
57
|
+
: metadata?.loadout || metadata?.tools || {};
|
|
58
|
+
const toolIds = loadoutToolIds(loadout);
|
|
54
59
|
return {
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
toolIds,
|
|
61
|
+
mcpAppIds: toolboxService.externalMcpToolIdsForLoadout(toolIds),
|
|
57
62
|
};
|
|
58
63
|
}
|
|
59
64
|
|
|
@@ -147,8 +152,7 @@ async function handleSlackInbound(body, sendJson) {
|
|
|
147
152
|
systemPrompt: slackSystemPrompt || null,
|
|
148
153
|
},
|
|
149
154
|
tools: {
|
|
150
|
-
mcpAppIds:
|
|
151
|
-
mode: toolConfig.mode,
|
|
155
|
+
mcpAppIds: toolConfig.mcpAppIds,
|
|
152
156
|
toolIds: toolConfig.toolIds,
|
|
153
157
|
},
|
|
154
158
|
execution: {
|
|
@@ -163,7 +167,7 @@ async function handleSlackInbound(body, sendJson) {
|
|
|
163
167
|
cwd,
|
|
164
168
|
computerId,
|
|
165
169
|
systemPrompt: slackSystemPrompt || undefined,
|
|
166
|
-
mcpAppIds:
|
|
170
|
+
mcpAppIds: toolConfig.mcpAppIds,
|
|
167
171
|
});
|
|
168
172
|
|
|
169
173
|
const legacyChatFields = chatInputToLegacyFields(normalizedChatInput, {
|
|
@@ -65,7 +65,7 @@ test('codex native import ports command hooks without inheriting MCP or auth con
|
|
|
65
65
|
assert.equal(config.hooks[0].timeout, 90);
|
|
66
66
|
assert.equal(config.hooks[0].statusMessage, 'Searching memories...');
|
|
67
67
|
assert.equal(config.hooks[0].source.provider, 'codex');
|
|
68
|
-
assert.
|
|
68
|
+
assert.deepEqual(config.loadout.toolIds, []);
|
|
69
69
|
|
|
70
70
|
const serialized = JSON.stringify(config);
|
|
71
71
|
assert.equal(serialized.includes('mcp_servers'), false);
|
|
@@ -70,13 +70,11 @@ test('custom agent auth and model settings survive normalization', () => {
|
|
|
70
70
|
baseModelId: 'openai/gpt-5.5',
|
|
71
71
|
authMethod: 'provider_auth',
|
|
72
72
|
modelSettings: { effort: 'medium', fastMode: true },
|
|
73
|
-
|
|
73
|
+
loadout: { toolIds: ['tool.search'] },
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
assert.equal(agent.authMethod, 'provider_auth');
|
|
77
77
|
assert.deepEqual(agent.modelSettings, { effort: 'medium', fastMode: true });
|
|
78
|
-
assert.deepEqual(agent.
|
|
79
|
-
|
|
80
|
-
selectedToolIds: ['tool.search'],
|
|
81
|
-
});
|
|
78
|
+
assert.deepEqual(agent.loadout, { toolIds: ['tool.search'] });
|
|
79
|
+
assert.equal(Object.prototype.hasOwnProperty.call(agent, 'tools'), false);
|
|
82
80
|
});
|
|
@@ -34,7 +34,7 @@ test('chat payloads persist in local SQLite and snapshot by id', () => {
|
|
|
34
34
|
customAgentId: null,
|
|
35
35
|
systemPrompt: null,
|
|
36
36
|
mcpAppIds: ['github'],
|
|
37
|
-
|
|
37
|
+
loadout: { toolIds: ['tool.search'] },
|
|
38
38
|
pendingWorktree: false,
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -131,12 +131,7 @@ test('MCP surface filters static tools in selected-tool mode', async () => {
|
|
|
131
131
|
},
|
|
132
132
|
});
|
|
133
133
|
const context = {
|
|
134
|
-
|
|
135
|
-
tools: {
|
|
136
|
-
mode: 'selected',
|
|
137
|
-
selectedToolIds: ['amalgm.test'],
|
|
138
|
-
},
|
|
139
|
-
},
|
|
134
|
+
loadout: { toolIds: ['amalgm.test'] },
|
|
140
135
|
};
|
|
141
136
|
|
|
142
137
|
const listed = await surface.listTools(context);
|
|
@@ -167,12 +162,7 @@ test('MCP surface accepts legacy selected ids for first-party tools', async () =
|
|
|
167
162
|
});
|
|
168
163
|
|
|
169
164
|
const listed = await surface.listTools({
|
|
170
|
-
|
|
171
|
-
tools: {
|
|
172
|
-
mode: 'selected',
|
|
173
|
-
selectedToolIds: ['amalgm.browser'],
|
|
174
|
-
},
|
|
175
|
-
},
|
|
165
|
+
loadout: { toolIds: ['amalgm.browser'] },
|
|
176
166
|
});
|
|
177
167
|
|
|
178
168
|
assert.deepEqual(listed.map((tool) => tool.name), ['toolbox__browser_browser_navigate']);
|
|
@@ -186,7 +186,7 @@ test('toolbox service resolves only runnable CLI/API actions', () => {
|
|
|
186
186
|
assert.equal(service.resolveRunnableAction('amalgm', 'notify_user'), null);
|
|
187
187
|
|
|
188
188
|
const selected = service.listRunnableActions({
|
|
189
|
-
|
|
189
|
+
loadout: { toolIds: ['api.weather.lookup'] },
|
|
190
190
|
});
|
|
191
191
|
assert.deepEqual(selected.map(({ action }) => action.id), ['api.weather.lookup']);
|
|
192
192
|
});
|
|
@@ -194,9 +194,8 @@ test('toolbox service resolves only runnable CLI/API actions', () => {
|
|
|
194
194
|
test('toolbox service derives only external MCP ids from selected tools', () => {
|
|
195
195
|
const service = createToolboxService(createRepository());
|
|
196
196
|
|
|
197
|
-
assert.deepEqual(service.externalMcpToolIdsForMode('all'), ['remote.mcp']);
|
|
198
197
|
assert.deepEqual(
|
|
199
|
-
service.
|
|
198
|
+
service.externalMcpToolIdsForLoadout([
|
|
200
199
|
'remote.mcp',
|
|
201
200
|
'browser',
|
|
202
201
|
'api.weather',
|
|
@@ -209,7 +208,7 @@ test('toolbox service derives only external MCP ids from selected tools', () =>
|
|
|
209
208
|
test('toolbox service exposes portable MCP descriptors', () => {
|
|
210
209
|
const service = createToolboxService(createRepository());
|
|
211
210
|
const descriptors = service.listMcpTools({
|
|
212
|
-
|
|
211
|
+
loadout: { toolIds: ['api.weather.lookup'] },
|
|
213
212
|
});
|
|
214
213
|
|
|
215
214
|
assert.equal(descriptors.length, 1);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { loadoutToolIds } = require('../agent-config/loadout');
|
|
4
|
+
|
|
5
|
+
const FIRST_PARTY_TOOL_IDS = new Set([
|
|
6
|
+
'automations',
|
|
7
|
+
'notifications',
|
|
8
|
+
'agents',
|
|
9
|
+
'apps',
|
|
10
|
+
'browser',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
function agentConfigIdFromContext(context) {
|
|
14
|
+
const metadata = context?.sessionMetadata || {};
|
|
15
|
+
return (
|
|
16
|
+
context?.agentConfigId
|
|
17
|
+
|| metadata.agentConfigId
|
|
18
|
+
|| metadata.agentId
|
|
19
|
+
|| metadata.customAgentId
|
|
20
|
+
|| null
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadoutSetFromContext(context) {
|
|
25
|
+
const direct = context?.loadout || context?.toolset || null;
|
|
26
|
+
if (direct) return new Set(loadoutToolIds(direct));
|
|
27
|
+
|
|
28
|
+
const agentConfigId = agentConfigIdFromContext(context);
|
|
29
|
+
if (!agentConfigId) return null;
|
|
30
|
+
|
|
31
|
+
const { getAgentConfig } = require('../agent-config/store');
|
|
32
|
+
return new Set(loadoutToolIds(getAgentConfig(agentConfigId)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function loadoutHas(loadoutToolIds, id) {
|
|
36
|
+
if (!loadoutToolIds || !id) return false;
|
|
37
|
+
const value = String(id || '').trim();
|
|
38
|
+
if (loadoutToolIds.has(value)) return true;
|
|
39
|
+
const toolId = value.split('.')[0];
|
|
40
|
+
if (loadoutToolIds.has(toolId)) return true;
|
|
41
|
+
if (FIRST_PARTY_TOOL_IDS.has(toolId) && loadoutToolIds.has(`amalgm.${value}`)) return true;
|
|
42
|
+
if (FIRST_PARTY_TOOL_IDS.has(value) && loadoutToolIds.has(`amalgm.${value}`)) return true;
|
|
43
|
+
if (value === 'notifications.notify_user' && loadoutToolIds.has('amalgm.notify_user')) return true;
|
|
44
|
+
if (value === 'agents.talk_to_agent' && loadoutToolIds.has('amalgm.talk_to_agent')) return true;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function loadoutContainsToolOrAction(loadoutToolIds, toolId, actionId) {
|
|
49
|
+
if (!loadoutToolIds) return true;
|
|
50
|
+
return loadoutHas(loadoutToolIds, toolId) || loadoutHas(loadoutToolIds, actionId);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
agentConfigIdFromContext,
|
|
55
|
+
loadoutContainsToolOrAction,
|
|
56
|
+
loadoutSetFromContext,
|
|
57
|
+
};
|
|
@@ -13,9 +13,9 @@ const { CORE_TOOLS } = require('../server/core-tools');
|
|
|
13
13
|
const { toolboxMcpToolName } = require('./names');
|
|
14
14
|
const { callToolboxMcpTool } = require('./runner');
|
|
15
15
|
const {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} = require('./
|
|
16
|
+
loadoutContainsToolOrAction,
|
|
17
|
+
loadoutSetFromContext,
|
|
18
|
+
} = require('./loadout-context');
|
|
19
19
|
const {
|
|
20
20
|
defaultToolboxService: toolboxService,
|
|
21
21
|
} = require('./service');
|
|
@@ -43,9 +43,9 @@ function defaultListDynamicTools(context) {
|
|
|
43
43
|
return toolboxService.listMcpTools(context);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function
|
|
47
|
-
return
|
|
48
|
-
|
|
46
|
+
function isStaticToolInLoadout(tool, loadoutToolIds) {
|
|
47
|
+
return loadoutContainsToolOrAction(
|
|
48
|
+
loadoutToolIds,
|
|
49
49
|
tool.toolboxToolId,
|
|
50
50
|
tool.toolboxActionId,
|
|
51
51
|
);
|
|
@@ -59,8 +59,8 @@ function createToolboxMcpSurface(options = {}) {
|
|
|
59
59
|
const callDynamicTool = options.callDynamicTool || callToolboxMcpTool;
|
|
60
60
|
|
|
61
61
|
function staticToolsForContext(context) {
|
|
62
|
-
const
|
|
63
|
-
return staticTools.filter((tool) =>
|
|
62
|
+
const loadoutToolIds = loadoutSetFromContext(context);
|
|
63
|
+
return staticTools.filter((tool) => isStaticToolInLoadout(tool, loadoutToolIds));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function findStaticTool(toolName, context) {
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
const store = require('./store');
|
|
11
11
|
const { portableInputSchema, toolboxMcpToolName } = require('./names');
|
|
12
12
|
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} = require('./
|
|
13
|
+
loadoutContainsToolOrAction,
|
|
14
|
+
loadoutSetFromContext,
|
|
15
|
+
} = require('./loadout-context');
|
|
16
16
|
const { mergeSystemToolboxCatalog } = require('./system-catalog');
|
|
17
17
|
|
|
18
18
|
function actionRef(toolId, actionName) {
|
|
@@ -132,7 +132,15 @@ function createToolboxService(repository = store) {
|
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
function
|
|
135
|
+
function libraryToolIds() {
|
|
136
|
+
const toolbox = readCatalog();
|
|
137
|
+
return Object.values(toolbox.tools || {})
|
|
138
|
+
.filter((tool) => tool?.status === 'enabled')
|
|
139
|
+
.map((tool) => tool.id)
|
|
140
|
+
.filter(Boolean);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function externalMcpToolIdsForLoadout(toolIds = []) {
|
|
136
144
|
const selected = new Set(
|
|
137
145
|
(Array.isArray(toolIds) ? toolIds : [])
|
|
138
146
|
.map((value) => String(value || '').trim())
|
|
@@ -140,23 +148,19 @@ function createToolboxService(repository = store) {
|
|
|
140
148
|
);
|
|
141
149
|
const toolbox = readCatalog();
|
|
142
150
|
return Object.values(toolbox.tools || {})
|
|
143
|
-
.filter((tool) =>
|
|
151
|
+
.filter((tool) => selected.has(tool.id))
|
|
144
152
|
.filter(isExternalMcpTool)
|
|
145
153
|
.map((tool) => tool.id);
|
|
146
154
|
}
|
|
147
155
|
|
|
148
|
-
function externalMcpToolIdsForSelection(toolIds = []) {
|
|
149
|
-
return externalMcpToolIdsForMode('selected', toolIds);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
156
|
function listRunnableActions(context) {
|
|
153
157
|
const toolbox = readToolbox();
|
|
154
|
-
const
|
|
158
|
+
const loadoutToolIds = loadoutSetFromContext(context);
|
|
155
159
|
return Object.values(toolbox.toolActions || {})
|
|
156
160
|
.map((action) => ({ action, tool: toolbox.tools?.[action.toolId] }))
|
|
157
161
|
.filter(({ action, tool }) => {
|
|
158
162
|
if (!isRunnableToolboxTool(tool) || action.status !== 'enabled') return false;
|
|
159
|
-
return
|
|
163
|
+
return loadoutContainsToolOrAction(loadoutToolIds, tool.id, action.id);
|
|
160
164
|
});
|
|
161
165
|
}
|
|
162
166
|
|
|
@@ -196,11 +200,11 @@ function createToolboxService(repository = store) {
|
|
|
196
200
|
return {
|
|
197
201
|
deleteAction: repository.deleteToolAction,
|
|
198
202
|
deleteTool: repository.deleteTool,
|
|
199
|
-
|
|
200
|
-
externalMcpToolIdsForSelection,
|
|
203
|
+
externalMcpToolIdsForLoadout,
|
|
201
204
|
getAction,
|
|
202
205
|
getTool,
|
|
203
206
|
listMcpTools,
|
|
207
|
+
libraryToolIds,
|
|
204
208
|
listRunnableActions,
|
|
205
209
|
listTools,
|
|
206
210
|
readCatalog,
|
|
@@ -317,12 +317,7 @@ async function runToolCell(cell, args, allowlist) {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
const result = await callToolboxMcpTool(toolboxMcpToolName(resolved.action), args, {
|
|
320
|
-
|
|
321
|
-
tools: {
|
|
322
|
-
mode: 'selected',
|
|
323
|
-
selectedToolIds: [resolved.tool.id, resolved.action.id],
|
|
324
|
-
},
|
|
325
|
-
},
|
|
320
|
+
loadout: { toolIds: [resolved.tool.id, resolved.action.id] },
|
|
326
321
|
});
|
|
327
322
|
return mcpResultToOutput(result, actionKey);
|
|
328
323
|
}
|