feat: route desktop control to authorized devices
This commit is contained in:
@@ -308,6 +308,62 @@ export function classifyMasterAgentControlIntent(
|
||||
|
||||
export const classifyMasterAgentControlIntentForTesting = classifyMasterAgentControlIntent;
|
||||
|
||||
type ControlTargetDeviceInput = {
|
||||
replyProjectId: string;
|
||||
intentCategory: "browser_control" | "desktop_control";
|
||||
preferredDeviceId?: string;
|
||||
authorizedDeviceIds: string[];
|
||||
devices: Array<{
|
||||
id: string;
|
||||
status?: string;
|
||||
capabilities?: {
|
||||
browserAutomation?: { connected?: boolean };
|
||||
computerUse?: { connected?: boolean };
|
||||
};
|
||||
}>;
|
||||
projects: Array<{
|
||||
id: string;
|
||||
deviceIds?: string[];
|
||||
}>;
|
||||
};
|
||||
|
||||
function isControlDeviceCapable(
|
||||
device: ControlTargetDeviceInput["devices"][number] | undefined,
|
||||
intentCategory: ControlTargetDeviceInput["intentCategory"],
|
||||
) {
|
||||
if (!device || device.status !== "online") return false;
|
||||
if (intentCategory === "browser_control") {
|
||||
return device.capabilities?.browserAutomation?.connected === true;
|
||||
}
|
||||
return device.capabilities?.computerUse?.connected === true;
|
||||
}
|
||||
|
||||
function resolveMasterAgentControlTargetDeviceId(input: ControlTargetDeviceInput) {
|
||||
const authorized = new Set(input.authorizedDeviceIds);
|
||||
const deviceById = new Map(input.devices.map((device) => [device.id, device]));
|
||||
const project = input.projects.find((item) => item.id === input.replyProjectId);
|
||||
const projectDevice = project?.deviceIds
|
||||
?.find((deviceId) => authorized.has(deviceId) && isControlDeviceCapable(deviceById.get(deviceId), input.intentCategory));
|
||||
if (projectDevice) return projectDevice;
|
||||
|
||||
const authorizedCapableDevices = input.authorizedDeviceIds.filter((deviceId) =>
|
||||
isControlDeviceCapable(deviceById.get(deviceId), input.intentCategory),
|
||||
);
|
||||
if (authorizedCapableDevices.length === 1) return authorizedCapableDevices[0];
|
||||
|
||||
if (
|
||||
input.preferredDeviceId &&
|
||||
authorized.has(input.preferredDeviceId) &&
|
||||
isControlDeviceCapable(deviceById.get(input.preferredDeviceId), input.intentCategory)
|
||||
) {
|
||||
return input.preferredDeviceId;
|
||||
}
|
||||
|
||||
return authorizedCapableDevices[0] ?? input.preferredDeviceId ?? input.authorizedDeviceIds[0] ?? "mac-studio";
|
||||
}
|
||||
|
||||
export const resolveMasterAgentControlTargetDeviceIdForTesting = resolveMasterAgentControlTargetDeviceId;
|
||||
|
||||
const GENERIC_COMPATIBLE_MODEL_OPTIONS = ["gpt-5.4-mini", "gpt-5.4", "gpt-5.1", "gpt-4.1"];
|
||||
|
||||
type QueuedMasterAgentReplyEnvelope = {
|
||||
@@ -3709,7 +3765,14 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
if (
|
||||
controlIntent.intentCategory === "browser_control" || controlIntent.intentCategory === "desktop_control"
|
||||
) {
|
||||
const deviceId = runtime.account.nodeId || state.user.boundDeviceId || "mac-studio";
|
||||
const deviceId = resolveMasterAgentControlTargetDeviceId({
|
||||
replyProjectId,
|
||||
intentCategory: controlIntent.intentCategory,
|
||||
preferredDeviceId: runtime.account.nodeId || state.user.boundDeviceId || "mac-studio",
|
||||
authorizedDeviceIds: authorizedScope.authorizedDeviceIds,
|
||||
devices: state.devices,
|
||||
projects: state.projects,
|
||||
});
|
||||
const taskType = controlIntent.intentCategory;
|
||||
const task = await queueMasterAgentTask({
|
||||
projectId: replyProjectId,
|
||||
@@ -3803,6 +3866,14 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
? "browser-automation-runtime"
|
||||
: "computer-use-runtime";
|
||||
const taskType = controlIntent.intentCategory;
|
||||
const controlDeviceId = resolveMasterAgentControlTargetDeviceId({
|
||||
replyProjectId,
|
||||
intentCategory: controlIntent.intentCategory,
|
||||
preferredDeviceId: deviceId,
|
||||
authorizedDeviceIds: authorizedScope.authorizedDeviceIds,
|
||||
devices: state.devices,
|
||||
projects: state.projects,
|
||||
});
|
||||
const task = await queueMasterAgentTask({
|
||||
projectId: replyProjectId,
|
||||
taskType,
|
||||
@@ -3811,7 +3882,7 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
executionPrompt: masterExecutionPrompt,
|
||||
requestedBy: params.requestedBy,
|
||||
requestedByAccount: params.requestedByAccount,
|
||||
deviceId,
|
||||
deviceId: controlDeviceId,
|
||||
accountId: selectedMasterAccount.accountId,
|
||||
accountLabel: selectedMasterAccount.label || runtime.summary.roleLabel,
|
||||
...masterTaskAuthorization(["master_agent.ask", "computer.control"]),
|
||||
@@ -3820,7 +3891,7 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
riskLevel: controlIntent.riskLevel,
|
||||
confirmationPolicy: controlIntent.riskLevel === "high" ? "strong_confirm" : "light_confirm",
|
||||
requiresUserConfirmation: false,
|
||||
confirmationScopeKey: `${deviceId}:${replyProjectId}`,
|
||||
confirmationScopeKey: `${controlDeviceId}:${replyProjectId}`,
|
||||
externalReplyTarget: params.externalReplyTarget,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user