@vellumai/assistant 0.12.2-staging.1 → 0.12.2-staging.3
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/docs/desktop-browser-cli.md +33 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +50 -4
- package/package.json +1 -1
- package/scripts/smoke-desktop-browser-cli.ts +279 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +5 -5
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/config-sounds-sync.test.ts +23 -0
- package/src/__tests__/conversation-error.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +33 -3
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- package/src/__tests__/credential-routes.test.ts +38 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/cu-unified-flow.test.ts +6 -2
- package/src/__tests__/external-plugin-loader.test.ts +52 -0
- package/src/__tests__/headless-browser-mode.test.ts +48 -0
- package/src/__tests__/host-cu-proxy.test.ts +185 -30
- package/src/__tests__/list-all-apps.test.ts +40 -8
- package/src/__tests__/mcp-auth-routes.test.ts +99 -0
- package/src/__tests__/mcp-client-auth.test.ts +54 -0
- package/src/__tests__/mcp-health-check.test.ts +32 -32
- package/src/__tests__/mcp-list-plugin-servers.test.ts +77 -32
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +4 -2
- package/src/__tests__/oauth-commands-routes.test.ts +55 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- package/src/__tests__/skills.test.ts +10 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +51 -0
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/user-plugin-loader.test.ts +21 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -0
- package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
- package/src/acp/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- package/src/acp/claude-token-refresh.ts +150 -0
- package/src/acp/prepare-agent-env.ts +21 -6
- package/src/apps/app-store.ts +6 -5
- package/src/browser/operations.ts +2 -1
- package/src/browser/types.ts +7 -0
- package/src/browser/virtual-desktop-target.ts +34 -0
- package/src/calls/__tests__/progress-narration.test.ts +23 -0
- package/src/calls/__tests__/voice-control-protocol.test.ts +69 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +81 -0
- package/src/calls/progress-narration.ts +15 -2
- package/src/calls/voice-control-protocol.ts +132 -0
- package/src/calls/voice-session-bridge.ts +56 -26
- package/src/calls/voice-triage-escalate.ts +3 -3
- package/src/cli/commands/__tests__/browser.test.ts +84 -3
- package/src/cli/commands/browser.help.ts +43 -0
- package/src/cli/commands/browser.ts +52 -8
- package/src/cli/commands/oauth/status.ts +5 -0
- package/src/cli/commands/plugins.ts +16 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +111 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +77 -2
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +45 -3
- package/src/cli/lib/__tests__/plugin-details.test.ts +26 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +122 -5
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/install-from-github.ts +17 -18
- package/src/cli/lib/install-from-platform.ts +14 -9
- package/src/cli/lib/list-installed-plugins.ts +21 -35
- package/src/cli/lib/plugin-details.ts +21 -8
- package/src/cli/lib/uninstall-plugin.ts +59 -1
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +21 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +43 -4
- package/src/config/bundled-skills/computer-use/TOOLS.json +3 -3
- package/src/config/bundled-skills/screen-annotation/SKILL.md +2 -2
- package/src/config/feature-flag-registry.json +11 -3
- package/src/config/loader.ts +1 -0
- package/src/config/schemas/__tests__/voice.test.ts +2 -2
- package/src/config/schemas/mcp.ts +9 -7
- package/src/config/schemas/monitoring.ts +9 -0
- package/src/config/schemas/services.ts +10 -0
- package/src/config/schemas/voice.ts +2 -2
- package/src/config/skills.ts +13 -21
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +47 -0
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -0
- package/src/daemon/conversation-client-surface.ts +26 -0
- package/src/daemon/conversation-runtime-assembly.ts +19 -7
- package/src/daemon/conversation-surfaces.ts +6 -1
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/host-cu-proxy.ts +94 -31
- package/src/daemon/host-proxy-preactivation.ts +2 -0
- package/src/daemon/mcp-reload-service.ts +7 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/providers-setup.ts +4 -0
- package/src/desktop/__tests__/fake-desktop.ts +1 -0
- package/src/desktop/desktop-automation-lease.test.ts +208 -0
- package/src/desktop/desktop-automation-lease.ts +270 -0
- package/src/desktop/desktop-browser-client.test.ts +413 -0
- package/src/desktop/desktop-browser-client.ts +430 -0
- package/src/desktop/desktop-browser-cursor.ts +40 -0
- package/src/desktop/desktop-browser-endpoint.test.ts +78 -0
- package/src/desktop/desktop-browser-endpoint.ts +145 -0
- package/src/desktop/desktop-browser-operations.ts +130 -0
- package/src/desktop/desktop-chrome-session.test.ts +48 -0
- package/src/desktop/desktop-chrome-session.ts +58 -8
- package/src/desktop/desktop-dependencies.test.ts +29 -0
- package/src/desktop/desktop-dependencies.ts +26 -5
- package/src/desktop/desktop-display.ts +9 -0
- package/src/desktop/desktop-panel-config.ts +14 -1
- package/src/desktop/desktop-session-manager.test.ts +86 -3
- package/src/desktop/desktop-session-manager.ts +144 -24
- package/src/desktop/desktop-stream-bridge.ts +1 -1
- package/src/desktop/virtual-desktop-feature.ts +18 -0
- package/src/desktop/virtual-desktop-platform.test.ts +61 -0
- package/src/i18n/__tests__/i18n.test.ts +9 -5
- package/src/i18n/messages.ts +13 -4
- package/src/ipc/__tests__/browser-ipc.test.ts +230 -2
- package/src/ipc/__tests__/cancel-on-disconnect.test.ts +54 -0
- package/src/ipc/assistant-server.ts +15 -1
- package/src/ipc/cli-client.ts +13 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +143 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +9 -7
- package/src/live-voice/__tests__/live-voice-progress.test.ts +79 -0
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +19 -0
- package/src/live-voice/__tests__/protocol.test.ts +47 -0
- package/src/live-voice/__tests__/session-controls.test.ts +113 -0
- package/src/live-voice/live-voice-metrics.ts +9 -0
- package/src/live-voice/live-voice-session.ts +88 -7
- package/src/live-voice/protocol.ts +69 -0
- package/src/live-voice/session-controls.ts +139 -0
- package/src/mcp/__tests__/credential-target.test.ts +59 -0
- package/src/mcp/__tests__/effective-config.test.ts +11 -0
- package/src/mcp/__tests__/manager-state.test.ts +128 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +20 -6
- package/src/mcp/__tests__/mcp-auth-state-target.test.ts +49 -0
- package/src/mcp/__tests__/plugin-mcp-oauth-cleanup.test.ts +107 -0
- package/src/mcp/__tests__/plugin-mcp-oauth-provider-isolation.test.ts +89 -0
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +63 -11
- package/src/mcp/__tests__/reload-signal-emission.test.ts +39 -2
- package/src/mcp/client.ts +64 -16
- package/src/mcp/credential-target.ts +105 -0
- package/src/mcp/effective-config.ts +42 -15
- package/src/mcp/manager.ts +61 -8
- package/src/mcp/mcp-auth-orchestrator.ts +17 -4
- package/src/mcp/mcp-auth-state.ts +38 -6
- package/src/mcp/mcp-oauth-provider.ts +101 -33
- package/src/monitoring/__tests__/mount-watch.test.ts +236 -0
- package/src/monitoring/mount-watch.ts +348 -0
- package/src/monitoring/worker.ts +9 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +95 -0
- package/src/oauth/connection-resolver.test.ts +27 -0
- package/src/oauth/connection-resolver.ts +25 -1
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/seed-providers.ts +121 -2
- package/src/onboarding/checkin-event.ts +2 -1
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +14 -1
- package/src/plugins/__tests__/mcp-servers.test.ts +38 -8
- package/src/plugins/external-plugin-loader.ts +49 -86
- package/src/plugins/installed-plugin-dirs.ts +6 -4
- package/src/plugins/mcp-servers.ts +9 -15
- package/src/plugins/mtime-cache.ts +2 -1
- package/src/plugins/user-loader.ts +2 -1
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +45 -0
- package/src/providers/inference/endpoint-probe.ts +27 -12
- package/src/providers/openai/responses-provider.ts +9 -2
- package/src/providers/opencode/client.test.ts +167 -0
- package/src/providers/opencode/client.ts +73 -4
- package/src/runtime/AGENTS.md +4 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +45 -2
- package/src/runtime/http-server.ts +6 -6
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +74 -4
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +25 -3
- package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
- package/src/runtime/routes/app-management-routes.ts +17 -2
- package/src/runtime/routes/browser-context.ts +39 -0
- package/src/runtime/routes/browser-routes.ts +16 -47
- package/src/runtime/routes/browser-tabs-routes.ts +72 -29
- package/src/runtime/routes/credential-routes.ts +1 -4
- package/src/runtime/routes/desktop-setup-routes.test.ts +16 -1
- package/src/runtime/routes/desktop-setup-routes.ts +7 -5
- package/src/runtime/routes/mcp-auth-routes.ts +106 -94
- package/src/runtime/routes/oauth-commands-routes.ts +14 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/plugins-routes.ts +14 -1
- package/src/runtime/sync/resource-sync-events.ts +4 -0
- package/src/tools/browser/browser-execution.ts +44 -13
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +69 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +5 -0
- package/src/tools/computer-use/definitions.ts +3 -4
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- package/src/util/abort-reasons.ts +12 -1
- package/src/util/plugin-manifest.ts +203 -0
- package/src/watch/watch-retro.ts +6 -8
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
|
@@ -17,7 +17,12 @@ import { z } from "zod";
|
|
|
17
17
|
import type { McpServerConfig } from "../../config/schemas/mcp.js";
|
|
18
18
|
import { estimateToolDefinitionTokens } from "../../context/token-estimator.js";
|
|
19
19
|
import { reloadMcpServers } from "../../daemon/mcp-reload-service.js";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
mcpOAuthCredentialKey,
|
|
22
|
+
resolveMcpOAuthCredentialTarget,
|
|
23
|
+
workspaceMcpOAuthCredentialTarget,
|
|
24
|
+
} from "../../mcp/credential-target.js";
|
|
25
|
+
import { readEffectiveMcpConfig } from "../../mcp/effective-config.js";
|
|
21
26
|
import { getMcpServerManager } from "../../mcp/manager.js";
|
|
22
27
|
import { orchestrateMcpOAuthConnect } from "../../mcp/mcp-auth-orchestrator.js";
|
|
23
28
|
import { getMcpAuthState } from "../../mcp/mcp-auth-state.js";
|
|
@@ -74,9 +79,7 @@ function persistWorkspaceMcpConfig(config: {
|
|
|
74
79
|
try {
|
|
75
80
|
saveWorkspaceMcpConfig(config);
|
|
76
81
|
} catch (err) {
|
|
77
|
-
throw new InternalError(
|
|
78
|
-
err instanceof Error ? err.message : String(err),
|
|
79
|
-
);
|
|
82
|
+
throw new InternalError(err instanceof Error ? err.message : String(err));
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
|
|
@@ -91,8 +94,9 @@ async function handleMcpAuthStart({
|
|
|
91
94
|
}> {
|
|
92
95
|
const { serverId } = parseBody(McpServerIdParams, body);
|
|
93
96
|
|
|
94
|
-
const
|
|
95
|
-
|
|
97
|
+
const serverConfig = readEffectiveMcpConfig(loadWorkspaceMcpConfig()).servers[
|
|
98
|
+
serverId
|
|
99
|
+
];
|
|
96
100
|
|
|
97
101
|
if (!serverConfig) {
|
|
98
102
|
throw new BadRequestError(`MCP server "${serverId}" not configured`);
|
|
@@ -105,10 +109,29 @@ async function handleMcpAuthStart({
|
|
|
105
109
|
);
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
const credentialTarget = resolveMcpOAuthCredentialTarget(
|
|
113
|
+
serverId,
|
|
114
|
+
serverConfig,
|
|
115
|
+
);
|
|
116
|
+
if (!credentialTarget) {
|
|
117
|
+
throw new BadRequestError(
|
|
118
|
+
`OAuth only supported for sse/streamable-http transports (server "${serverId}" uses ${transport.type})`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
mcpOAuthCredentialKey(credentialTarget, "tokens");
|
|
124
|
+
} catch {
|
|
125
|
+
throw new BadRequestError(
|
|
126
|
+
`MCP server "${serverId}" has an invalid transport URL`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
108
130
|
let result: { auth_url: string; already_authenticated?: boolean };
|
|
109
131
|
try {
|
|
110
132
|
result = await orchestrateMcpOAuthConnect({
|
|
111
133
|
serverId,
|
|
134
|
+
credentialTarget,
|
|
112
135
|
transport: {
|
|
113
136
|
url: transport.url,
|
|
114
137
|
type: transport.type,
|
|
@@ -135,7 +158,19 @@ function handleMcpAuthStatus({
|
|
|
135
158
|
| { status: "complete" }
|
|
136
159
|
| { status: "error"; error: string } {
|
|
137
160
|
const { serverId } = pathParams as { serverId: string };
|
|
138
|
-
const
|
|
161
|
+
const serverConfig = readEffectiveMcpConfig(loadWorkspaceMcpConfig()).servers[
|
|
162
|
+
serverId
|
|
163
|
+
];
|
|
164
|
+
const target = serverConfig
|
|
165
|
+
? resolveMcpOAuthCredentialTarget(serverId, serverConfig)
|
|
166
|
+
: null;
|
|
167
|
+
let targetKey: string | undefined;
|
|
168
|
+
try {
|
|
169
|
+
targetKey = target ? mcpOAuthCredentialKey(target, "tokens") : undefined;
|
|
170
|
+
} catch {
|
|
171
|
+
targetKey = undefined;
|
|
172
|
+
}
|
|
173
|
+
const state = targetKey ? getMcpAuthState(serverId, targetKey) : null;
|
|
139
174
|
|
|
140
175
|
if (state === null) {
|
|
141
176
|
throw new NotFoundError(`No active OAuth flow for server "${serverId}"`);
|
|
@@ -170,53 +205,6 @@ function handleMcpReload(_args: { body?: Record<string, unknown> }): {
|
|
|
170
205
|
return { ok: true };
|
|
171
206
|
}
|
|
172
207
|
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
174
|
-
// Health check helper
|
|
175
|
-
// ---------------------------------------------------------------------------
|
|
176
|
-
|
|
177
|
-
const HEALTH_CHECK_TIMEOUT_MS = 10_000;
|
|
178
|
-
|
|
179
|
-
async function checkMachineReadableHealth(
|
|
180
|
-
serverId: string,
|
|
181
|
-
config: McpServerConfig,
|
|
182
|
-
timeoutMs = HEALTH_CHECK_TIMEOUT_MS,
|
|
183
|
-
): Promise<string> {
|
|
184
|
-
const client = new McpClient(serverId);
|
|
185
|
-
try {
|
|
186
|
-
await Promise.race([
|
|
187
|
-
client.connect(config.transport),
|
|
188
|
-
new Promise<never>((_, reject) => {
|
|
189
|
-
const t = setTimeout(() => reject(new Error("timeout")), timeoutMs);
|
|
190
|
-
if (typeof t === "object" && "unref" in t) {
|
|
191
|
-
t.unref();
|
|
192
|
-
}
|
|
193
|
-
}),
|
|
194
|
-
]);
|
|
195
|
-
|
|
196
|
-
if (client.isConnected) {
|
|
197
|
-
await client.disconnect();
|
|
198
|
-
return "connected";
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const err = client.lastError;
|
|
202
|
-
if (err) {
|
|
203
|
-
if (err.message.includes("timeout")) {
|
|
204
|
-
return "error";
|
|
205
|
-
}
|
|
206
|
-
return "error";
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return "needs-auth";
|
|
210
|
-
} catch {
|
|
211
|
-
try {
|
|
212
|
-
await client.disconnect();
|
|
213
|
-
} catch {
|
|
214
|
-
/* ignore */
|
|
215
|
-
}
|
|
216
|
-
return "error";
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
208
|
// ---------------------------------------------------------------------------
|
|
221
209
|
// List
|
|
222
210
|
// ---------------------------------------------------------------------------
|
|
@@ -271,12 +259,19 @@ async function handleMcpList(_args: {
|
|
|
271
259
|
const configEntries = (
|
|
272
260
|
Object.entries(servers) as [string, McpServerConfig][]
|
|
273
261
|
).filter(([, config]) => config && typeof config === "object");
|
|
262
|
+
const manager = getMcpServerManager();
|
|
274
263
|
|
|
275
264
|
const workspaceEntries: McpServerEntry[] = await Promise.all(
|
|
276
265
|
configEntries.map(async ([id, config]) => {
|
|
277
|
-
const
|
|
266
|
+
const runtimeState = manager.getServerState(id, "workspace");
|
|
267
|
+
const status =
|
|
268
|
+
runtimeState === "connected" || runtimeState === "needs-auth"
|
|
269
|
+
? runtimeState
|
|
270
|
+
: "error";
|
|
278
271
|
const hasOAuth =
|
|
279
|
-
config.transport.type !== "stdio"
|
|
272
|
+
config.transport.type !== "stdio"
|
|
273
|
+
? await hasMcpOAuthTokens(workspaceMcpOAuthCredentialTarget(id))
|
|
274
|
+
: false;
|
|
280
275
|
|
|
281
276
|
// Check credential store for stored static auth headers
|
|
282
277
|
const storedHeaders = await getMcpHeaders(id);
|
|
@@ -316,21 +311,23 @@ async function handleMcpList(_args: {
|
|
|
316
311
|
);
|
|
317
312
|
|
|
318
313
|
return {
|
|
319
|
-
servers: [
|
|
314
|
+
servers: [
|
|
315
|
+
...workspaceEntries,
|
|
316
|
+
...(await listPluginServerEntries(configEntries)),
|
|
317
|
+
],
|
|
320
318
|
};
|
|
321
319
|
}
|
|
322
320
|
|
|
323
321
|
/**
|
|
324
322
|
* Project plugin-declared servers onto listing entries.
|
|
325
323
|
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* from the manager's in-memory client
|
|
329
|
-
* {@link PLUGIN_SERVER_STATUS}).
|
|
324
|
+
* OAuth presence is read from the plugin's scoped credential identity. Static
|
|
325
|
+
* workspace headers are never consulted, no network probe runs, and status
|
|
326
|
+
* comes from the manager's in-memory client (see {@link PLUGIN_SERVER_STATUS}).
|
|
330
327
|
*/
|
|
331
|
-
function listPluginServerEntries(
|
|
328
|
+
async function listPluginServerEntries(
|
|
332
329
|
configEntries: [string, McpServerConfig][],
|
|
333
|
-
): McpServerEntry[] {
|
|
330
|
+
): Promise<McpServerEntry[]> {
|
|
334
331
|
const { servers, issues } = readPluginMcpServers();
|
|
335
332
|
for (const issue of issues) {
|
|
336
333
|
log.warn(
|
|
@@ -349,36 +346,51 @@ function listPluginServerEntries(
|
|
|
349
346
|
const workspaceIds = new Set(configEntries.map(([id]) => id));
|
|
350
347
|
const manager = getMcpServerManager();
|
|
351
348
|
|
|
352
|
-
return
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
349
|
+
return Promise.all(
|
|
350
|
+
servers
|
|
351
|
+
.filter((server) => {
|
|
352
|
+
if (!workspaceIds.has(server.id)) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
log.warn(
|
|
356
|
+
{ plugin: server.pluginName, serverId: server.id },
|
|
357
|
+
"Plugin MCP server shadowed by a workspace server of the same id; skipping",
|
|
358
|
+
);
|
|
359
|
+
return false;
|
|
360
|
+
})
|
|
361
|
+
.map(async (server) => {
|
|
362
|
+
const { headers: _stripped, ...safeTransport } = server.config
|
|
363
|
+
.transport as Record<string, unknown>;
|
|
364
|
+
let hasOAuth = false;
|
|
365
|
+
const credentialTarget = resolveMcpOAuthCredentialTarget(
|
|
366
|
+
server.id,
|
|
367
|
+
server.config,
|
|
368
|
+
);
|
|
369
|
+
if (credentialTarget) {
|
|
370
|
+
try {
|
|
371
|
+
hasOAuth = await hasMcpOAuthTokens(credentialTarget);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
log.warn(
|
|
374
|
+
{ err, plugin: server.pluginName, serverId: server.id },
|
|
375
|
+
"Plugin MCP OAuth credential identity could not be resolved",
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
id: server.id,
|
|
381
|
+
status:
|
|
382
|
+
manager.getServerState(server.id, "plugin") === "connected"
|
|
383
|
+
? "connected"
|
|
384
|
+
: PLUGIN_SERVER_STATUS,
|
|
385
|
+
transport: safeTransport as McpServerEntry["transport"],
|
|
386
|
+
hasOAuth,
|
|
387
|
+
hasStaticAuth: false,
|
|
388
|
+
authType: "none" as const,
|
|
389
|
+
source: "plugin" as const,
|
|
390
|
+
pluginName: server.pluginName,
|
|
391
|
+
};
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
382
394
|
}
|
|
383
395
|
|
|
384
396
|
// ---------------------------------------------------------------------------
|
|
@@ -687,7 +699,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
687
699
|
},
|
|
688
700
|
summary: "List MCP servers with health status",
|
|
689
701
|
description:
|
|
690
|
-
"Returns configured MCP servers with
|
|
702
|
+
"Returns configured MCP servers with recorded runtime status without opening new connections.",
|
|
691
703
|
tags: ["internal"],
|
|
692
704
|
responseBody: z.object({
|
|
693
705
|
servers: z.array(
|
|
@@ -66,6 +66,7 @@ interface PlatformConnectionEntry {
|
|
|
66
66
|
id: string;
|
|
67
67
|
account_label?: string;
|
|
68
68
|
scopes_granted?: string[];
|
|
69
|
+
provider_params?: Record<string, string> | null;
|
|
69
70
|
status?: string;
|
|
70
71
|
}
|
|
71
72
|
|
|
@@ -574,6 +575,9 @@ async function handleStatus({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
574
575
|
account: c.account_label ?? null,
|
|
575
576
|
grantedScopes: c.scopes_granted ?? [],
|
|
576
577
|
status: c.status ?? "ACTIVE",
|
|
578
|
+
// Values the provider scopes the connection by (QuickBooks' realm id),
|
|
579
|
+
// so a caller can address resources the proxy's base URL does not.
|
|
580
|
+
providerParams: c.provider_params ?? {},
|
|
577
581
|
}));
|
|
578
582
|
|
|
579
583
|
return {
|
|
@@ -1060,6 +1064,8 @@ async function handleManagedConnect({ body = {} }: RouteHandlerArgs) {
|
|
|
1060
1064
|
provider: string;
|
|
1061
1065
|
scopes?: string[];
|
|
1062
1066
|
redirect_after_connect?: string;
|
|
1067
|
+
/** Per-tenant providers (Shopify): the customer's own host. */
|
|
1068
|
+
tenant_host?: string;
|
|
1063
1069
|
};
|
|
1064
1070
|
|
|
1065
1071
|
if (!b.provider) {
|
|
@@ -1076,6 +1082,13 @@ async function handleManagedConnect({ body = {} }: RouteHandlerArgs) {
|
|
|
1076
1082
|
}
|
|
1077
1083
|
reqBody.redirect_after_connect =
|
|
1078
1084
|
b.redirect_after_connect ?? "/account/oauth/complete";
|
|
1085
|
+
// Only forwarded when present: the platform validates it against the
|
|
1086
|
+
// provider's pattern and rejects per-tenant providers that omit it.
|
|
1087
|
+
const tenantHost =
|
|
1088
|
+
typeof b.tenant_host === "string" ? b.tenant_host.trim() : "";
|
|
1089
|
+
if (tenantHost) {
|
|
1090
|
+
reqBody.tenant_host = tenantHost;
|
|
1091
|
+
}
|
|
1079
1092
|
|
|
1080
1093
|
const response = await client.fetch(startPath, {
|
|
1081
1094
|
method: "POST",
|
|
@@ -1122,6 +1135,7 @@ async function handleManagedConnectPoll({
|
|
|
1122
1135
|
id: e.id,
|
|
1123
1136
|
account_label: e.account_label ?? null,
|
|
1124
1137
|
scopes_granted: e.scopes_granted ?? [],
|
|
1138
|
+
provider_params: e.provider_params ?? {},
|
|
1125
1139
|
})),
|
|
1126
1140
|
};
|
|
1127
1141
|
}
|
|
@@ -397,6 +397,13 @@ export const oauthProviderSummarySchema = z.object({
|
|
|
397
397
|
supports_managed_mode: z.boolean(),
|
|
398
398
|
managed_service_is_paid: z.boolean(),
|
|
399
399
|
feature_flag: z.string().nullable(),
|
|
400
|
+
tenant_host: z
|
|
401
|
+
.object({
|
|
402
|
+
pattern: z.string(),
|
|
403
|
+
label: z.string(),
|
|
404
|
+
placeholder: z.string(),
|
|
405
|
+
})
|
|
406
|
+
.nullable(),
|
|
400
407
|
acts_as: z.enum(["user", "assistant"]),
|
|
401
408
|
});
|
|
402
409
|
|
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
PluginDirectoryNotFoundError,
|
|
81
81
|
} from "../../cli/lib/toggle-plugin.js";
|
|
82
82
|
import {
|
|
83
|
+
PLUGIN_UNINSTALL_WARNING_KEYS,
|
|
83
84
|
PluginNotInstalledError,
|
|
84
85
|
uninstallPlugin,
|
|
85
86
|
} from "../../cli/lib/uninstall-plugin.js";
|
|
@@ -255,6 +256,14 @@ const pluginUninstallResponseSchema = z.object({
|
|
|
255
256
|
.describe(
|
|
256
257
|
"Absolute path that was removed on the assistant host. Useful for audit logs and confirmation toasts.",
|
|
257
258
|
),
|
|
259
|
+
warnings: z
|
|
260
|
+
.array(
|
|
261
|
+
z.literal(PLUGIN_UNINSTALL_WARNING_KEYS.MCP_OAUTH_CREDENTIALS_UNCHECKED),
|
|
262
|
+
)
|
|
263
|
+
.optional()
|
|
264
|
+
.describe(
|
|
265
|
+
"Stable keys for non-fatal cleanup limitations that should be localized at the presentation edge.",
|
|
266
|
+
),
|
|
258
267
|
});
|
|
259
268
|
|
|
260
269
|
const pluginDetailsResponseSchema = z.object({
|
|
@@ -1134,7 +1143,11 @@ async function handleUninstallPlugin({
|
|
|
1134
1143
|
const result = await uninstallPlugin({ name: rawName });
|
|
1135
1144
|
await reconcilePluginSourcesNow();
|
|
1136
1145
|
publishPluginsChanged(getOriginClientId(headers));
|
|
1137
|
-
return {
|
|
1146
|
+
return {
|
|
1147
|
+
name: result.name,
|
|
1148
|
+
target: result.target,
|
|
1149
|
+
...(result.warnings && { warnings: result.warnings }),
|
|
1150
|
+
};
|
|
1138
1151
|
} catch (err) {
|
|
1139
1152
|
if (err instanceof InvalidPluginNameError) {
|
|
1140
1153
|
throw new BadRequestError(err.message);
|
|
@@ -152,6 +152,10 @@ export function publishPluginsChanged(originClientId?: string): void {
|
|
|
152
152
|
void publishSyncInvalidation([SYNC_TAGS.pluginsList], originClientId);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
export function publishMcpChanged(): void {
|
|
156
|
+
void publishSyncInvalidation([SYNC_TAGS.mcpList]);
|
|
157
|
+
}
|
|
158
|
+
|
|
155
159
|
/**
|
|
156
160
|
* Invalidate the activation-checklist progress resource on every client.
|
|
157
161
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { optimizeImageForTransport } from "../../agent/image-optimize.js";
|
|
2
|
+
import type { BrowserOperationContext as ToolContext } from "../../browser/types.js";
|
|
2
3
|
import { getConfig } from "../../config/loader.js";
|
|
3
4
|
import { HostBrowserProxy } from "../../daemon/host-browser-proxy.js";
|
|
4
5
|
import type { ImageContent } from "../../providers/types.js";
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
sanitizeUrlForOutput,
|
|
22
23
|
sanitizeUrlStringForOutput,
|
|
23
24
|
} from "../network/url-safety.js";
|
|
24
|
-
import type {
|
|
25
|
+
import type { ToolExecutionResult } from "../types.js";
|
|
25
26
|
import {
|
|
26
27
|
type AuthChallenge,
|
|
27
28
|
detectAuthChallenge,
|
|
@@ -515,6 +516,9 @@ async function acquireCdpClientWithMode(
|
|
|
515
516
|
}
|
|
516
517
|
| { cdp?: never; browserMode?: never; errorResult: ToolExecutionResult }
|
|
517
518
|
> {
|
|
519
|
+
if (context.cdpClient) {
|
|
520
|
+
return { cdp: context.cdpClient, browserMode: "cdp-inspect" };
|
|
521
|
+
}
|
|
518
522
|
const modeResult = parseBrowserMode(input);
|
|
519
523
|
if (!modeResult.ok) {
|
|
520
524
|
return {
|
|
@@ -746,6 +750,17 @@ export type ResolvedElement =
|
|
|
746
750
|
| { kind: "backend"; backendNodeId: number; eid: string }
|
|
747
751
|
| { kind: "selector"; selector: string };
|
|
748
752
|
|
|
753
|
+
function snapshotConversationId(context: ToolContext): string {
|
|
754
|
+
return context.cdpClient?.conversationId ?? context.conversationId;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function clearBrowserSessionState(context: ToolContext): void {
|
|
758
|
+
browserManager.clearSnapshotBackendNodeMap(snapshotConversationId(context));
|
|
759
|
+
if (!context.cdpClient) {
|
|
760
|
+
browserManager.clearPreferredBackendKind(context.conversationId);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
749
764
|
/**
|
|
750
765
|
* Resolve an element reference (either `element_id` from a prior
|
|
751
766
|
* snapshot or a raw `selector`) for CDP-native tools. Returns a
|
|
@@ -872,6 +887,9 @@ export async function executeBrowserNavigate(
|
|
|
872
887
|
typeof input.target_client_id === "string" && input.target_client_id !== ""
|
|
873
888
|
? input.target_client_id
|
|
874
889
|
: undefined;
|
|
890
|
+
if (context.cdpClient && forceNewTab) {
|
|
891
|
+
await cdp.send("Vellum.createTab", {}, context.signal);
|
|
892
|
+
}
|
|
875
893
|
if (cdp.kind === "extension" && useActiveTab) {
|
|
876
894
|
// Explicit opt-out: target the currently-active tab. Clear any
|
|
877
895
|
// conversation pin and reset the live session so this navigate is
|
|
@@ -1188,7 +1206,7 @@ export async function executeBrowserNavigate(
|
|
|
1188
1206
|
// Navigation changed the page content, so clear stale snapshot
|
|
1189
1207
|
// mappings regardless of backend. The backendNodeId map is shared
|
|
1190
1208
|
// per-conversation state that needs to be invalidated on any nav.
|
|
1191
|
-
browserManager.clearSnapshotBackendNodeMap(context
|
|
1209
|
+
browserManager.clearSnapshotBackendNodeMap(snapshotConversationId(context));
|
|
1192
1210
|
|
|
1193
1211
|
// Auto-dismiss common blocker modals (regulatory notices, cookie
|
|
1194
1212
|
// banners) that aren't exposed in the accessibility tree. Runs
|
|
@@ -1398,7 +1416,7 @@ export async function executeBrowserSnapshot(
|
|
|
1398
1416
|
const { elements, selectorMap: backendNodeMap } = transformAxTree(rawTree);
|
|
1399
1417
|
|
|
1400
1418
|
browserManager.storeSnapshotBackendNodeMap(
|
|
1401
|
-
context
|
|
1419
|
+
snapshotConversationId(context),
|
|
1402
1420
|
backendNodeMap,
|
|
1403
1421
|
);
|
|
1404
1422
|
|
|
@@ -1600,8 +1618,7 @@ export async function executeBrowserDetach(
|
|
|
1600
1618
|
// Vellum.detach round-trip failed (target gone, transport dropped).
|
|
1601
1619
|
// browser_detach is the user's recovery path — leaving a stale
|
|
1602
1620
|
// sticky backend or snapshot map behind would defeat its purpose.
|
|
1603
|
-
|
|
1604
|
-
browserManager.clearPreferredBackendKind(context.conversationId);
|
|
1621
|
+
clearBrowserSessionState(context);
|
|
1605
1622
|
cdp.dispose();
|
|
1606
1623
|
}
|
|
1607
1624
|
}
|
|
@@ -1653,8 +1670,7 @@ export async function executeBrowserClose(
|
|
|
1653
1670
|
// Tolerate detach failures (already detached, tab closed, etc.)
|
|
1654
1671
|
}
|
|
1655
1672
|
}
|
|
1656
|
-
|
|
1657
|
-
browserManager.clearPreferredBackendKind(context.conversationId);
|
|
1673
|
+
clearBrowserSessionState(context);
|
|
1658
1674
|
return {
|
|
1659
1675
|
content:
|
|
1660
1676
|
"Browser session cleared. (Your Chrome tab was not closed — close it yourself if desired.)",
|
|
@@ -1683,7 +1699,10 @@ export async function executeBrowserClick(
|
|
|
1683
1699
|
input: Record<string, unknown>,
|
|
1684
1700
|
context: ToolContext,
|
|
1685
1701
|
): Promise<ToolExecutionResult> {
|
|
1686
|
-
const { resolved, error } = resolveElement(
|
|
1702
|
+
const { resolved, error } = resolveElement(
|
|
1703
|
+
snapshotConversationId(context),
|
|
1704
|
+
input,
|
|
1705
|
+
);
|
|
1687
1706
|
if (error) {
|
|
1688
1707
|
return { content: error, isError: true };
|
|
1689
1708
|
}
|
|
@@ -1796,7 +1815,10 @@ export async function executeBrowserType(
|
|
|
1796
1815
|
input: Record<string, unknown>,
|
|
1797
1816
|
context: ToolContext,
|
|
1798
1817
|
): Promise<ToolExecutionResult> {
|
|
1799
|
-
const { resolved, error } = resolveElement(
|
|
1818
|
+
const { resolved, error } = resolveElement(
|
|
1819
|
+
snapshotConversationId(context),
|
|
1820
|
+
input,
|
|
1821
|
+
);
|
|
1800
1822
|
if (error) {
|
|
1801
1823
|
return { content: error, isError: true };
|
|
1802
1824
|
}
|
|
@@ -1887,7 +1909,7 @@ export async function executeBrowserPressKey(
|
|
|
1887
1909
|
let targetDescription: string | null = null;
|
|
1888
1910
|
let resolved: ResolvedElement | null = null;
|
|
1889
1911
|
if (hasTarget) {
|
|
1890
|
-
const res = resolveElement(context
|
|
1912
|
+
const res = resolveElement(snapshotConversationId(context), input);
|
|
1891
1913
|
if (res.error) {
|
|
1892
1914
|
return { content: res.error, isError: true };
|
|
1893
1915
|
}
|
|
@@ -2025,7 +2047,10 @@ export async function executeBrowserSelectOption(
|
|
|
2025
2047
|
input: Record<string, unknown>,
|
|
2026
2048
|
context: ToolContext,
|
|
2027
2049
|
): Promise<ToolExecutionResult> {
|
|
2028
|
-
const { resolved, error } = resolveElement(
|
|
2050
|
+
const { resolved, error } = resolveElement(
|
|
2051
|
+
snapshotConversationId(context),
|
|
2052
|
+
input,
|
|
2053
|
+
);
|
|
2029
2054
|
if (error) {
|
|
2030
2055
|
return { content: error, isError: true };
|
|
2031
2056
|
}
|
|
@@ -2162,7 +2187,10 @@ export async function executeBrowserHover(
|
|
|
2162
2187
|
input: Record<string, unknown>,
|
|
2163
2188
|
context: ToolContext,
|
|
2164
2189
|
): Promise<ToolExecutionResult> {
|
|
2165
|
-
const { resolved, error } = resolveElement(
|
|
2190
|
+
const { resolved, error } = resolveElement(
|
|
2191
|
+
snapshotConversationId(context),
|
|
2192
|
+
input,
|
|
2193
|
+
);
|
|
2166
2194
|
if (error) {
|
|
2167
2195
|
return { content: error, isError: true };
|
|
2168
2196
|
}
|
|
@@ -2410,7 +2438,10 @@ export async function executeBrowserFillCredential(
|
|
|
2410
2438
|
return { content: "Error: field is required.", isError: true };
|
|
2411
2439
|
}
|
|
2412
2440
|
|
|
2413
|
-
const { resolved, error } = resolveElement(
|
|
2441
|
+
const { resolved, error } = resolveElement(
|
|
2442
|
+
snapshotConversationId(context),
|
|
2443
|
+
input,
|
|
2444
|
+
);
|
|
2414
2445
|
if (error) {
|
|
2415
2446
|
return { content: error, isError: true };
|
|
2416
2447
|
}
|
|
@@ -2804,3 +2804,72 @@ describe("macOS host-browser proxy without extension registry", () => {
|
|
|
2804
2804
|
expect(result).toEqual({ ok: true, via: "extension" });
|
|
2805
2805
|
});
|
|
2806
2806
|
});
|
|
2807
|
+
|
|
2808
|
+
describe("native renderer browser compatibility", () => {
|
|
2809
|
+
beforeEach(() => {
|
|
2810
|
+
setCdpInspectEnabled(false);
|
|
2811
|
+
setDesktopAutoConfig({ enabled: true, cooldownMs: 30_000 });
|
|
2812
|
+
_resetDesktopAutoCooldown();
|
|
2813
|
+
_resetHostBridgeCooldown();
|
|
2814
|
+
mockSingletonProxy = null;
|
|
2815
|
+
});
|
|
2816
|
+
test.each(["macos", "windows"] as const)(
|
|
2817
|
+
"%s web renderer sends CDP to the connected host bridge",
|
|
2818
|
+
async (clientOs) => {
|
|
2819
|
+
mockSingletonProxy = makeMacosBridgeOnlyProxy();
|
|
2820
|
+
const client = getCdpClient(
|
|
2821
|
+
makeContext({
|
|
2822
|
+
conversationId: "conv-native",
|
|
2823
|
+
transportInterface: "web",
|
|
2824
|
+
clientOs,
|
|
2825
|
+
}),
|
|
2826
|
+
);
|
|
2827
|
+
try {
|
|
2828
|
+
expect(await client.send("Page.getFrameTree", {})).toMatchObject({
|
|
2829
|
+
via: "host-bridge",
|
|
2830
|
+
});
|
|
2831
|
+
} finally {
|
|
2832
|
+
client.dispose();
|
|
2833
|
+
}
|
|
2834
|
+
},
|
|
2835
|
+
);
|
|
2836
|
+
|
|
2837
|
+
test.each(["macos", "windows"] as const)(
|
|
2838
|
+
"%s web renderer keeps the existing Playwright fallback without a bridge",
|
|
2839
|
+
async (clientOs) => {
|
|
2840
|
+
const client = getCdpClient(
|
|
2841
|
+
makeContext({
|
|
2842
|
+
conversationId: "conv-native",
|
|
2843
|
+
transportInterface: "web",
|
|
2844
|
+
clientOs,
|
|
2845
|
+
}),
|
|
2846
|
+
);
|
|
2847
|
+
try {
|
|
2848
|
+
expect(await client.send("Page.getFrameTree", {})).toMatchObject({
|
|
2849
|
+
via: "local",
|
|
2850
|
+
});
|
|
2851
|
+
} finally {
|
|
2852
|
+
client.dispose();
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
);
|
|
2856
|
+
|
|
2857
|
+
test("explicit Playwright choice overrides the native browser default", async () => {
|
|
2858
|
+
mockSingletonProxy = makeMacosBridgeOnlyProxy();
|
|
2859
|
+
const client = getCdpClient(
|
|
2860
|
+
makeContext({
|
|
2861
|
+
conversationId: "conv-native",
|
|
2862
|
+
transportInterface: "web",
|
|
2863
|
+
clientOs: "windows",
|
|
2864
|
+
}),
|
|
2865
|
+
{ mode: "local" },
|
|
2866
|
+
);
|
|
2867
|
+
try {
|
|
2868
|
+
expect(await client.send("Page.getFrameTree", {})).toMatchObject({
|
|
2869
|
+
via: "local",
|
|
2870
|
+
});
|
|
2871
|
+
} finally {
|
|
2872
|
+
client.dispose();
|
|
2873
|
+
}
|
|
2874
|
+
});
|
|
2875
|
+
});
|
|
@@ -417,6 +417,7 @@ describe("connectCdpWsTransport", () => {
|
|
|
417
417
|
}
|
|
418
418
|
expect(caught).toBeInstanceOf(CdpWsTransportError);
|
|
419
419
|
expect((caught as CdpWsTransportError).code).toBe("closed");
|
|
420
|
+
expect(transport.closed).toBe(true);
|
|
420
421
|
|
|
421
422
|
// After close, subsequent sends also reject with closed.
|
|
422
423
|
let caught2: unknown;
|
|
@@ -99,6 +99,8 @@ export interface CdpTransportEvent {
|
|
|
99
99
|
* CDP error mapping to the shared `CdpError` taxonomy.
|
|
100
100
|
*/
|
|
101
101
|
export interface CdpWsTransport {
|
|
102
|
+
readonly closed?: boolean;
|
|
103
|
+
|
|
102
104
|
/**
|
|
103
105
|
* Send a CDP method call over the socket and await its response.
|
|
104
106
|
*
|
|
@@ -464,6 +466,9 @@ function createTransport(ws: WsLike): CdpWsTransport {
|
|
|
464
466
|
ws.addEventListener("error", handleError);
|
|
465
467
|
|
|
466
468
|
const transport: CdpWsTransport = {
|
|
469
|
+
get closed() {
|
|
470
|
+
return disposed || closed;
|
|
471
|
+
},
|
|
467
472
|
send<T = unknown>(
|
|
468
473
|
method: string,
|
|
469
474
|
params?: Record<string, unknown>,
|
|
@@ -352,7 +352,7 @@ export const computerUseOpenAppTool = {
|
|
|
352
352
|
export const computerUseRunAppleScriptTool = {
|
|
353
353
|
name: "computer_use_run_applescript",
|
|
354
354
|
description:
|
|
355
|
-
"Run an AppleScript
|
|
355
|
+
"Run an AppleScript on the Mac. Try the target app's own scripting dictionary first, asking it for the state you want rather than for the clicks that would produce it; fall back to System Events menu clicking for apps with no dictionary entry for what you need. Prefer this over click and type when the app supports it; it does not move the cursor. The result is the script's return value, and the accessibility tree may not change even when the script worked. Never use 'do shell script' inside AppleScript (blocked for security).",
|
|
356
356
|
category: "computer-use",
|
|
357
357
|
defaultRiskLevel: RiskLevel.Low,
|
|
358
358
|
executionTarget: "host",
|
|
@@ -367,8 +367,7 @@ export const computerUseRunAppleScriptTool = {
|
|
|
367
367
|
},
|
|
368
368
|
reasoning: {
|
|
369
369
|
type: "string",
|
|
370
|
-
description:
|
|
371
|
-
"Explanation of what this script does and why AppleScript is better than UI interaction for this step",
|
|
370
|
+
description: "What this script does",
|
|
372
371
|
},
|
|
373
372
|
target_client_id: {
|
|
374
373
|
type: "string",
|
|
@@ -569,7 +568,7 @@ export const computerUseObserveTool = {
|
|
|
569
568
|
include_screenshot: {
|
|
570
569
|
type: "boolean",
|
|
571
570
|
description:
|
|
572
|
-
"Force a screenshot with this observation. The accessibility tree is returned every step; ask for pixels whenever the tree is not enough to act on, such as a canvas, a game, a custom-drawn view, a window with few or unlabeled controls, or a question about layout.",
|
|
571
|
+
"Force a screenshot with this observation. The accessibility tree is returned every step and actions always return a screenshot of their result; ask for pixels on an observation whenever the tree is not enough to act on, such as a canvas, a game, a custom-drawn view, a window with few or unlabeled controls, or a question about layout.",
|
|
573
572
|
},
|
|
574
573
|
full_tree: {
|
|
575
574
|
type: "boolean",
|