amalgm 0.1.91 → 0.1.92
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/scripts/amalgm-mcp/browser/attach.js +238 -0
- package/runtime/scripts/amalgm-mcp/browser/auth-tools.js +26 -40
- package/runtime/scripts/amalgm-mcp/browser/backend.js +100 -0
- package/runtime/scripts/amalgm-mcp/browser/capture.js +167 -0
- package/runtime/scripts/amalgm-mcp/browser/cdp.js +177 -0
- package/runtime/scripts/amalgm-mcp/browser/engine.js +34 -349
- package/runtime/scripts/amalgm-mcp/browser/index.js +28 -0
- package/runtime/scripts/amalgm-mcp/browser/recorder-tools.js +4 -4
- package/runtime/scripts/amalgm-mcp/browser/recorder.js +60 -72
- package/runtime/scripts/amalgm-mcp/browser/sessions.js +64 -0
- package/runtime/scripts/amalgm-mcp/browser/tools.js +35 -31
- package/runtime/scripts/amalgm-mcp/server/core-tools.js +4 -14
- package/runtime/scripts/amalgm-mcp/server/mcp.js +24 -1
- package/runtime/scripts/amalgm-mcp/tests/browser-capture.test.js +87 -0
- package/runtime/scripts/amalgm-mcp/tests/core-tools.test.js +59 -29
- package/runtime/scripts/amalgm-mcp/tests/platform-context.test.js +46 -0
- package/runtime/scripts/amalgm-mcp/tests/system-catalog.test.js +4 -4
- package/runtime/scripts/amalgm-mcp/tests/toolbox-service.test.js +2 -2
- package/runtime/scripts/amalgm-mcp/toolbox/loadout-context.js +33 -10
- package/runtime/scripts/chat-core/tool-shape.js +27 -10
- package/runtime/scripts/platform-context.txt +21 -20
|
@@ -4,6 +4,7 @@ const assert = require('node:assert/strict');
|
|
|
4
4
|
const test = require('node:test');
|
|
5
5
|
|
|
6
6
|
const { CORE_TOOL_GROUPS, CORE_TOOLS } = require('../server/core-tools');
|
|
7
|
+
const { toolboxMcpToolName } = require('../toolbox/names');
|
|
7
8
|
|
|
8
9
|
test('core tools are grouped into first-party toolbox-style defaults', () => {
|
|
9
10
|
assert.deepEqual(CORE_TOOL_GROUPS.map((group) => group.id), [
|
|
@@ -23,19 +24,19 @@ test('core tool grouping preserves handler definitions for MCP surface wrapping'
|
|
|
23
24
|
|
|
24
25
|
assert.equal(CORE_TOOLS.length, groupedTools.length);
|
|
25
26
|
assert.deepEqual(CORE_TOOLS.map((tool) => tool.name), groupedTools.map((tool) => tool.name));
|
|
26
|
-
assert.equal(CORE_TOOLS.some((tool) => tool.name === '
|
|
27
|
+
assert.equal(CORE_TOOLS.some((tool) => tool.name === 'open' && tool.toolboxToolId === 'browser'), true);
|
|
27
28
|
assert.equal(CORE_TOOLS.some((tool) => tool.name === 'notify_user'), true);
|
|
28
29
|
assert.equal(CORE_TOOLS.some((tool) => tool.name === 'append_change_log'), true);
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
test('core tool actions carry stable toolbox ids', () => {
|
|
32
|
-
const browserOpen = CORE_TOOLS.find((tool) => tool.name === '
|
|
33
|
+
const browserOpen = CORE_TOOLS.find((tool) => tool.name === 'open');
|
|
33
34
|
const notifyUser = CORE_TOOLS.find((tool) => tool.name === 'notify_user');
|
|
34
35
|
const cuaClick = CORE_TOOLS.find((tool) => tool.name === 'cua_click');
|
|
35
36
|
const recordStart = CORE_TOOLS.find((tool) => tool.name === 'record_start');
|
|
36
37
|
|
|
37
38
|
assert.equal(browserOpen.toolboxToolId, 'browser');
|
|
38
|
-
assert.equal(browserOpen.toolboxActionId, 'browser.
|
|
39
|
+
assert.equal(browserOpen.toolboxActionId, 'browser.open');
|
|
39
40
|
assert.equal(cuaClick.toolboxToolId, 'browser');
|
|
40
41
|
assert.equal(cuaClick.toolboxActionId, 'browser.cua_click');
|
|
41
42
|
assert.equal(recordStart.toolboxActionId, 'browser.record_start');
|
|
@@ -47,16 +48,16 @@ test('browser is a single toolbox entry holding every browser action', () => {
|
|
|
47
48
|
const browser = CORE_TOOL_GROUPS.find((group) => group.id === 'browser');
|
|
48
49
|
assert.deepEqual(browser.tools.map((tool) => tool.name), [
|
|
49
50
|
// core verbs
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
'
|
|
51
|
+
'open',
|
|
52
|
+
'snapshot',
|
|
53
|
+
'screenshot',
|
|
54
|
+
'click',
|
|
55
|
+
'fill',
|
|
56
|
+
'press',
|
|
57
|
+
'eval',
|
|
58
|
+
'wait',
|
|
59
|
+
'cli',
|
|
60
|
+
'close',
|
|
60
61
|
// computer use
|
|
61
62
|
'cua_screenshot',
|
|
62
63
|
'cua_click',
|
|
@@ -71,12 +72,10 @@ test('browser is a single toolbox entry holding every browser action', () => {
|
|
|
71
72
|
'record_stop',
|
|
72
73
|
'record_list',
|
|
73
74
|
// auth / profiles
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'browser_auth_save',
|
|
79
|
-
'browser_auth_load',
|
|
75
|
+
'auth_list',
|
|
76
|
+
'auth_link_create',
|
|
77
|
+
'auth_save',
|
|
78
|
+
'auth_load',
|
|
80
79
|
]);
|
|
81
80
|
|
|
82
81
|
// No other browser-ish groups exist.
|
|
@@ -86,6 +85,21 @@ test('browser is a single toolbox entry holding every browser action', () => {
|
|
|
86
85
|
);
|
|
87
86
|
});
|
|
88
87
|
|
|
88
|
+
test('browser MCP names never stutter', () => {
|
|
89
|
+
// Regression for the 2026-06-10 trap: action ids like browser.browser_open
|
|
90
|
+
// rendered as toolbox__browser_browser_open, and agents reasonably guessed
|
|
91
|
+
// toolbox__browser_open — which did not exist. Short action names make the
|
|
92
|
+
// guessable name the real one.
|
|
93
|
+
const browser = CORE_TOOL_GROUPS.find((group) => group.id === 'browser');
|
|
94
|
+
for (const tool of browser.tools) {
|
|
95
|
+
const mcpName = toolboxMcpToolName({ id: tool.toolboxActionId });
|
|
96
|
+
assert.equal(mcpName.startsWith('toolbox__browser_'), true);
|
|
97
|
+
assert.equal(mcpName.includes('browser_browser'), false, `${mcpName} stutters`);
|
|
98
|
+
}
|
|
99
|
+
assert.equal(toolboxMcpToolName({ id: 'browser.open' }), 'toolbox__browser_open');
|
|
100
|
+
assert.equal(toolboxMcpToolName({ id: 'browser.record_start' }), 'toolbox__browser_record_start');
|
|
101
|
+
});
|
|
102
|
+
|
|
89
103
|
test('browser actions carry capability tags so UIs can section one card', () => {
|
|
90
104
|
const browser = CORE_TOOL_GROUPS.find((group) => group.id === 'browser');
|
|
91
105
|
const byCapability = (capability) => browser.tools
|
|
@@ -95,31 +109,47 @@ test('browser actions carry capability tags so UIs can section one card', () =>
|
|
|
95
109
|
assert.equal(byCapability('core').length, 10);
|
|
96
110
|
assert.equal(byCapability('computer-use').every((name) => name.startsWith('cua_')), true);
|
|
97
111
|
assert.deepEqual(byCapability('recording'), ['record_start', 'record_stop', 'record_list']);
|
|
98
|
-
assert.
|
|
112
|
+
assert.deepEqual(byCapability('auth'), ['auth_list', 'auth_link_create', 'auth_save', 'auth_load']);
|
|
99
113
|
assert.equal(browser.tools.every((tool) => Boolean(tool.capability)), true);
|
|
100
114
|
});
|
|
101
115
|
|
|
102
|
-
test('
|
|
116
|
+
test('loadouts from every browser era still grant the current actions', () => {
|
|
103
117
|
const { loadoutContainsToolOrAction } = require('../toolbox/loadout-context');
|
|
104
118
|
|
|
105
|
-
//
|
|
119
|
+
// Era-1 group id grants exactly that group's actions.
|
|
106
120
|
const cuaLoadout = new Set(['browser_cua']);
|
|
107
121
|
assert.equal(loadoutContainsToolOrAction(cuaLoadout, 'browser', 'browser.cua_click'), true);
|
|
108
122
|
assert.equal(loadoutContainsToolOrAction(cuaLoadout, 'browser', 'browser.record_start'), false);
|
|
109
|
-
assert.equal(loadoutContainsToolOrAction(cuaLoadout, 'browser', 'browser.
|
|
123
|
+
assert.equal(loadoutContainsToolOrAction(cuaLoadout, 'browser', 'browser.open'), false);
|
|
110
124
|
|
|
111
|
-
//
|
|
125
|
+
// Era-1 fine-grained action ids map 1:1.
|
|
112
126
|
const actionLoadout = new Set(['browser_recording.record_start']);
|
|
113
127
|
assert.equal(loadoutContainsToolOrAction(actionLoadout, 'browser', 'browser.record_start'), true);
|
|
114
128
|
assert.equal(loadoutContainsToolOrAction(actionLoadout, 'browser', 'browser.record_stop'), false);
|
|
115
129
|
|
|
116
|
-
//
|
|
130
|
+
// Era-1 auth group covers the merged auth_list (and its old spellings).
|
|
131
|
+
const authLoadout = new Set(['browser_auth']);
|
|
132
|
+
assert.equal(loadoutContainsToolOrAction(authLoadout, 'browser', 'browser.auth_list'), true);
|
|
133
|
+
assert.equal(loadoutContainsToolOrAction(authLoadout, 'browser', 'browser.auth_save'), true);
|
|
134
|
+
assert.equal(loadoutContainsToolOrAction(authLoadout, 'browser', 'browser.open'), false);
|
|
135
|
+
const authActionLoadout = new Set(['browser_auth.browser_profiles_list']);
|
|
136
|
+
assert.equal(loadoutContainsToolOrAction(authActionLoadout, 'browser', 'browser.auth_list'), true);
|
|
137
|
+
|
|
138
|
+
// Era-2 fine-grained grants (browser.browser_open) cover the renamed verbs.
|
|
139
|
+
const renamedLoadout = new Set(['browser.browser_open', 'browser.browser_eval']);
|
|
140
|
+
assert.equal(loadoutContainsToolOrAction(renamedLoadout, 'browser', 'browser.open'), true);
|
|
141
|
+
assert.equal(loadoutContainsToolOrAction(renamedLoadout, 'browser', 'browser.eval'), true);
|
|
142
|
+
assert.equal(loadoutContainsToolOrAction(renamedLoadout, 'browser', 'browser.click'), false);
|
|
143
|
+
const renamedAuthLoadout = new Set(['browser.browser_auth_bundles_list']);
|
|
144
|
+
assert.equal(loadoutContainsToolOrAction(renamedAuthLoadout, 'browser', 'browser.auth_list'), true);
|
|
145
|
+
|
|
146
|
+
// The single entry grants everything.
|
|
117
147
|
const browserLoadout = new Set(['browser']);
|
|
118
|
-
assert.equal(loadoutContainsToolOrAction(browserLoadout, 'browser', 'browser.
|
|
148
|
+
assert.equal(loadoutContainsToolOrAction(browserLoadout, 'browser', 'browser.open'), true);
|
|
119
149
|
assert.equal(loadoutContainsToolOrAction(browserLoadout, 'browser', 'browser.cua_click'), true);
|
|
120
|
-
assert.equal(loadoutContainsToolOrAction(browserLoadout, 'browser', 'browser.
|
|
150
|
+
assert.equal(loadoutContainsToolOrAction(browserLoadout, 'browser', 'browser.auth_save'), true);
|
|
121
151
|
|
|
122
|
-
//
|
|
152
|
+
// The ancient amalgm.browser aggregate keeps granting browser actions.
|
|
123
153
|
const legacyAggregate = new Set(['amalgm.browser']);
|
|
124
|
-
assert.equal(loadoutContainsToolOrAction(legacyAggregate, 'browser', 'browser.
|
|
154
|
+
assert.equal(loadoutContainsToolOrAction(legacyAggregate, 'browser', 'browser.open'), true);
|
|
125
155
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
// The platform prompt is what every agent reads about the browser before its
|
|
9
|
+
// first call. It documented a deleted tool surface once (browser_navigate et
|
|
10
|
+
// al.) and agents spent whole sessions calling tools that did not exist —
|
|
11
|
+
// keep it pinned to the real action names.
|
|
12
|
+
|
|
13
|
+
const CONTEXT_PATH = path.join(__dirname, '..', '..', 'platform-context.txt');
|
|
14
|
+
|
|
15
|
+
test('platform context documents the live browser surface, not a deleted one', () => {
|
|
16
|
+
const text = fs.readFileSync(CONTEXT_PATH, 'utf8');
|
|
17
|
+
|
|
18
|
+
for (const ghost of [
|
|
19
|
+
'browser_navigate',
|
|
20
|
+
'browser_type',
|
|
21
|
+
'browser_get_text',
|
|
22
|
+
'browser_evaluate',
|
|
23
|
+
'browser_start_video',
|
|
24
|
+
'browser_stop_video',
|
|
25
|
+
]) {
|
|
26
|
+
assert.equal(text.includes(ghost), false, `platform context still documents deleted tool ${ghost}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
assert.equal(text.includes('toolbox__browser_open'), true, 'platform context should show the real MCP name shape');
|
|
30
|
+
for (const action of ['`open`', '`snapshot`', '`record_start`', '`auth_link_create`']) {
|
|
31
|
+
assert.equal(text.includes(action), true, `platform context should document ${action}`);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('platform context browser names match the registered actions', () => {
|
|
36
|
+
const text = fs.readFileSync(CONTEXT_PATH, 'utf8');
|
|
37
|
+
const { CORE_TOOL_GROUPS } = require('../server/core-tools');
|
|
38
|
+
const browser = CORE_TOOL_GROUPS.find((group) => group.id === 'browser');
|
|
39
|
+
// Every core verb the prompt teaches must exist under that exact name.
|
|
40
|
+
const documented = ['open', 'snapshot', 'click', 'fill', 'press', 'eval', 'wait', 'screenshot', 'cli', 'close'];
|
|
41
|
+
const registered = new Set(browser.tools.map((tool) => tool.name));
|
|
42
|
+
for (const name of documented) {
|
|
43
|
+
assert.equal(registered.has(name), true, `prompt documents "${name}" but the toolbox does not register it`);
|
|
44
|
+
assert.equal(text.includes(`\`${name}\``), true, `prompt should mention ${name}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -17,12 +17,12 @@ test('system toolbox catalog exposes first-party default tools', () => {
|
|
|
17
17
|
assert.equal(catalog.tools.browser.origin, 'system');
|
|
18
18
|
assert.equal(catalog.tools.browser.policy.firstParty, true);
|
|
19
19
|
assert.equal(
|
|
20
|
-
catalog.toolActions['browser.
|
|
21
|
-
'
|
|
20
|
+
catalog.toolActions['browser.open'].sourceAction.mcpToolName,
|
|
21
|
+
'toolbox__browser_open',
|
|
22
22
|
);
|
|
23
23
|
assert.equal(
|
|
24
|
-
catalog.toolActions['browser.
|
|
25
|
-
'
|
|
24
|
+
catalog.toolActions['browser.open'].sourceAction.handlerName,
|
|
25
|
+
'open',
|
|
26
26
|
);
|
|
27
27
|
assert.equal(
|
|
28
28
|
catalog.toolActions['notifications.notify_user'].sourceAction.mcpToolName,
|
|
@@ -161,7 +161,7 @@ test('toolbox service overlays first-party system catalog entries', () => {
|
|
|
161
161
|
const service = createToolboxService(createRepository());
|
|
162
162
|
const catalog = service.readCatalog();
|
|
163
163
|
const browser = service.getTool('browser', { includeActions: false });
|
|
164
|
-
const navigate = service.getAction('browser.
|
|
164
|
+
const navigate = service.getAction('browser.open');
|
|
165
165
|
|
|
166
166
|
assert.equal(catalog.tools.amalgm, undefined);
|
|
167
167
|
assert.equal(catalog.toolActions['amalgm.notify_user'], undefined);
|
|
@@ -170,7 +170,7 @@ test('toolbox service overlays first-party system catalog entries', () => {
|
|
|
170
170
|
assert.equal(catalog.tools.browser.owner, 'amalgm');
|
|
171
171
|
assert.equal(browser.tool.name, 'Browser');
|
|
172
172
|
assert.equal(browser.actions, undefined);
|
|
173
|
-
assert.equal(navigate.action.sourceAction.mcpToolName, '
|
|
173
|
+
assert.equal(navigate.action.sourceAction.mcpToolName, 'toolbox__browser_open');
|
|
174
174
|
assert.equal(service.listTools({ origin: 'system' }).tools.some((tool) => tool.id === 'browser'), true);
|
|
175
175
|
});
|
|
176
176
|
|
|
@@ -11,31 +11,54 @@ const FIRST_PARTY_TOOL_IDS = new Set([
|
|
|
11
11
|
'browser',
|
|
12
12
|
]);
|
|
13
13
|
|
|
14
|
-
// Browser
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
14
|
+
// Browser action names through history — loadouts saved in any era keep
|
|
15
|
+
// granting the same capability. Frozen maps; these never grow.
|
|
16
|
+
// era 1: four toolbox entries (browser + browser_cua/_recording/_auth)
|
|
17
|
+
// era 2: one entry, prefixed action names (browser.browser_open)
|
|
18
|
+
// era 3 (now): one entry, short verbs (browser.open); the three auth list
|
|
19
|
+
// actions merged into auth_list.
|
|
20
|
+
const OLD_BROWSER_ACTION_NAMES = {
|
|
21
|
+
open: ['browser_open'],
|
|
22
|
+
snapshot: ['browser_snapshot'],
|
|
23
|
+
screenshot: ['browser_screenshot'],
|
|
24
|
+
click: ['browser_click'],
|
|
25
|
+
fill: ['browser_fill'],
|
|
26
|
+
press: ['browser_press'],
|
|
27
|
+
eval: ['browser_eval'],
|
|
28
|
+
wait: ['browser_wait'],
|
|
29
|
+
cli: ['browser_cli'],
|
|
30
|
+
close: ['browser_close'],
|
|
31
|
+
auth_list: ['browser_profiles_list', 'browser_auth_bundles_list', 'browser_login_sessions_list'],
|
|
32
|
+
auth_link_create: ['browser_auth_link_create'],
|
|
33
|
+
auth_save: ['browser_auth_save'],
|
|
34
|
+
auth_load: ['browser_auth_load'],
|
|
35
|
+
};
|
|
36
|
+
|
|
18
37
|
const LEGACY_BROWSER_GROUP_ACTIONS = {
|
|
19
38
|
browser_cua: new Set([
|
|
20
39
|
'cua_screenshot', 'cua_click', 'cua_double_click', 'cua_move',
|
|
21
40
|
'cua_scroll', 'cua_type', 'cua_keypress', 'cua_drag',
|
|
22
41
|
]),
|
|
23
42
|
browser_recording: new Set(['record_start', 'record_stop', 'record_list']),
|
|
24
|
-
browser_auth: new Set([
|
|
25
|
-
'browser_profiles_list', 'browser_auth_bundles_list',
|
|
26
|
-
'browser_login_sessions_list', 'browser_auth_link_create',
|
|
27
|
-
'browser_auth_save', 'browser_auth_load',
|
|
28
|
-
]),
|
|
43
|
+
browser_auth: new Set(['auth_list', 'auth_link_create', 'auth_save', 'auth_load']),
|
|
29
44
|
};
|
|
30
45
|
|
|
31
46
|
function legacyBrowserLoadoutHas(loadoutToolIds, actionId) {
|
|
32
47
|
if (!actionId.startsWith('browser.')) return false;
|
|
33
48
|
const action = actionId.slice('browser.'.length);
|
|
49
|
+
const oldNames = OLD_BROWSER_ACTION_NAMES[action] || [];
|
|
50
|
+
// Era-2 fine-grained grants: browser.browser_open ⇒ browser.open.
|
|
51
|
+
for (const oldName of oldNames) {
|
|
52
|
+
if (loadoutToolIds.has(`browser.${oldName}`)) return true;
|
|
53
|
+
}
|
|
54
|
+
// Era-1 group grants, including their fine-grained spellings.
|
|
34
55
|
for (const [legacyId, actions] of Object.entries(LEGACY_BROWSER_GROUP_ACTIONS)) {
|
|
35
56
|
if (!actions.has(action)) continue;
|
|
36
57
|
if (loadoutToolIds.has(legacyId)) return true;
|
|
37
|
-
if (loadoutToolIds.has(`${legacyId}.${action}`)) return true;
|
|
38
58
|
if (loadoutToolIds.has(`amalgm.${legacyId}`)) return true;
|
|
59
|
+
for (const name of [action, ...oldNames]) {
|
|
60
|
+
if (loadoutToolIds.has(`${legacyId}.${name}`)) return true;
|
|
61
|
+
}
|
|
39
62
|
}
|
|
40
63
|
return false;
|
|
41
64
|
}
|
|
@@ -75,18 +75,35 @@ function toolKind(name = '', input = {}, metadata = {}) {
|
|
|
75
75
|
return 'unknown';
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Browser action out of a tool name, across naming eras: current
|
|
80
|
+
* toolbox__browser_open, era-2 toolbox__browser_browser_open / browser_open,
|
|
81
|
+
* and bare record_start/cua_click. Null for non-browser tools.
|
|
82
|
+
*/
|
|
83
|
+
function browserAction(name) {
|
|
84
|
+
const match = String(name || '').match(/^(?:toolbox__browser_|browser_)(.+)$/);
|
|
85
|
+
if (match) return match[1].replace(/^browser_/, '');
|
|
86
|
+
if (/^(?:record_(?:start|stop|list)|cua_[a-z_]+)$/.test(name)) return name;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
78
90
|
function titleFromMcp(tool, input = {}) {
|
|
79
91
|
const name = String(tool || '');
|
|
80
|
-
const action = name
|
|
81
|
-
if (
|
|
82
|
-
if (
|
|
83
|
-
if (
|
|
84
|
-
if (
|
|
85
|
-
if (
|
|
86
|
-
if (
|
|
87
|
-
if (
|
|
88
|
-
if (
|
|
89
|
-
if (
|
|
92
|
+
const action = browserAction(name);
|
|
93
|
+
if (action === 'open') return input.url ? `Opening ${hostLabel(input.url)}` : 'Opening browser page';
|
|
94
|
+
if (action === 'snapshot') return 'Reading browser page';
|
|
95
|
+
if (action === 'screenshot' || action === 'cua_screenshot') return 'Taking browser screenshot';
|
|
96
|
+
if (action === 'fill') return input.text ? `Typing "${compactText(input.text, 60)}"` : 'Typing in browser';
|
|
97
|
+
if (action === 'click') return 'Clicking in browser';
|
|
98
|
+
if (action === 'press') return input.key ? `Pressing ${compactText(input.key, 20)}` : 'Pressing key in browser';
|
|
99
|
+
if (action === 'eval') return 'Running JavaScript in browser';
|
|
100
|
+
if (action === 'wait') return 'Waiting for page';
|
|
101
|
+
if (action === 'cli') return Array.isArray(input.args) ? `Browser: ${compactText(input.args.join(' '), 60)}` : 'Running browser command';
|
|
102
|
+
if (action === 'close') return 'Closing browser session';
|
|
103
|
+
if (action === 'record_start') return 'Starting browser recording';
|
|
104
|
+
if (action === 'record_stop') return 'Saving browser recording';
|
|
105
|
+
if (action === 'record_list') return 'Listing browser recordings';
|
|
106
|
+
if (action === 'auth_list') return 'Listing browser logins';
|
|
90
107
|
if (name === 'apps_register') return input.name ? `Registering app ${compactText(input.name, 60)}` : 'Registering app';
|
|
91
108
|
if (name === 'apps_start') return 'Starting app';
|
|
92
109
|
if (name === 'apps_stop') return 'Stopping app';
|
|
@@ -115,26 +115,27 @@ Discover and talk to other agents running on the platform.
|
|
|
115
115
|
|
|
116
116
|
Agent communication is session-based. Pass the `session_id` on subsequent calls to continue the same conversation — the other agent retains full context. Use this for delegation, second opinions, or cross-model collaboration.
|
|
117
117
|
|
|
118
|
-
## Browser
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
129
|
-
- `
|
|
130
|
-
- `
|
|
131
|
-
- `
|
|
132
|
-
- `
|
|
133
|
-
- `
|
|
134
|
-
- `
|
|
135
|
-
- `
|
|
136
|
-
|
|
137
|
-
|
|
118
|
+
## Browser
|
|
119
|
+
|
|
120
|
+
One persistent browser per chat session. When the Amalgm desktop app is open, the user watches it live in a split view; everywhere else it runs headless in the background. Same actions either way — you never pick a mode.
|
|
121
|
+
|
|
122
|
+
Browser actions are MCP tools named `toolbox__browser_<action>` (your harness may add its own prefix, e.g. `mcp__amalgm__toolbox__browser_open`).
|
|
123
|
+
|
|
124
|
+
The loop: `open` a URL → `snapshot` to see interactive elements as @refs → `click`/`fill`/`press` by @ref → re-`snapshot` after the page changes.
|
|
125
|
+
|
|
126
|
+
- `open` — go to a URL (the session persists across calls; navigate once, then interact)
|
|
127
|
+
- `snapshot` — read the page: every interactive element gets a stable @ref
|
|
128
|
+
- `click` / `fill` / `press` — act on @refs, CSS selectors, or `text=Label` targets
|
|
129
|
+
- `eval` — run JavaScript in the page and get the result
|
|
130
|
+
- `wait` — wait for a selector, URL, or delay
|
|
131
|
+
- `screenshot` — PNG when you need pixels (snapshot is cheaper for driving)
|
|
132
|
+
- `cli` — the full agent-browser command surface: scroll, hover, back, tabs, cookies, storage, network, traces, find-by-role, and more
|
|
133
|
+
- `close` — end the session
|
|
134
|
+
- `cua_screenshot` / `cua_click` / `cua_type` / … — coordinate-based control for vision loops
|
|
135
|
+
- `record_start` / `record_stop` / `record_list` — capture the session to a local WebM video (QA evidence)
|
|
136
|
+
- `auth_list` / `auth_link_create` / `auth_save` / `auth_load` — reuse logins: mint a link for the user to sign in, then save/load encrypted auth bundles
|
|
137
|
+
|
|
138
|
+
Recordings and auth bundles stay on this computer. Use the browser to test apps, scrape data, or automate web interactions.
|
|
138
139
|
|
|
139
140
|
---
|
|
140
141
|
|