diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index ea0ce2dad48..54c550cc33b 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -1452,6 +1452,10 @@ as they are available, so later setup failures still clean up acquired resources Busy-account deferral returns explicitly before session/workspace preparation. Public-path tests are in `server/src/__tests__/heartbeat-execution-configuration.test.ts`. +Browser Use skill assignment first checks for an enabled, active Browser Use +connection in the run's company. With none, it skips full tool discovery. +With one, the existing task, tool-policy and credential-grant checks still apply. + Session preparation is in `server/src/services/heartbeat/session-preparation.ts`. It selects fresh or resumable sessions from effective config and managed-account identity, then resolves adapter session identity and compaction after workspace diff --git a/server/src/__tests__/browser-use-connection.test.ts b/server/src/__tests__/browser-use-connection.test.ts index f9282b9f082..90a227c55ad 100644 --- a/server/src/__tests__/browser-use-connection.test.ts +++ b/server/src/__tests__/browser-use-connection.test.ts @@ -407,6 +407,18 @@ const actor = { actorType: "user" as const, actorId: "browser-reviewer" }; expect(revoked.paperclipConnectorSkillDigest).toBeNull(); expect((await prepareConnectorSkillDelivery(revoked, "cursor_local")).instructions).toBe(""); }); + it.each([false, true])("skips tool discovery without an active company Browser Use connection (foreign: %s)", async foreign => { + const f = await fixture(); + await db.update(toolConnections).set({ enabled: false }).where(eq(toolConnections.id, f.connection.connectionId)); + if (foreign) await fixture(); + f.request.mockClear(); + const discovery = vi.spyOn(db, "transaction"); + try { + expect(await f.gateway.browserUseResources(f.binding)).toEqual([]); + expect(discovery).not.toHaveBeenCalled(); + expect(f.request).not.toHaveBeenCalled(); + } finally { discovery.mockRestore(); } + }); it("withholds Cloud instructions when connection tools are disabled", async () => { const f = await fixture(); await db.update(toolConnections).set({ enabled: false }).where(eq(toolConnections.id, f.connection.connectionId)); diff --git a/server/src/services/tool-gateway.ts b/server/src/services/tool-gateway.ts index f2eb2727b9f..f7fe7f1617b 100644 --- a/server/src/services/tool-gateway.ts +++ b/server/src/services/tool-gateway.ts @@ -9128,6 +9128,12 @@ export function createToolGatewayService( async browserUseResources(binding: { companyId: string; agentId: string; runId?: string; issueId?: string }) { if (!binding.runId || !binding.issueId) return []; + const [connection] = await db.select({ id: toolConnections.id }).from(toolConnections).where(and( + eq(toolConnections.companyId, binding.companyId), eq(toolConnections.transport, "rest_api"), + eq(toolConnections.enabled, true), eq(toolConnections.status, "active"), + sql`${toolConnections.config}->>'sourceTemplateKey' = 'browser-use-cloud'`, + )).limit(1); + if (!connection) return []; const session: ToolGatewaySession = { ...binding, runId: binding.runId, issueId: binding.issueId, id: randomUUID(), token: "", projectId: null, createdAt: new Date(), expiresAt: new Date(Date.now() + 60000) }; const tools = (await listToolsForContext(session)).filter(t => t.providerType === "provider_rest"); const resources: Array<{ id: string; label: string; connectionId: string }> = [];