diff --git a/server/src/__tests__/built-in-agents.test.ts b/server/src/__tests__/built-in-agents.test.ts index 2537f12949e..6f0676661fd 100644 --- a/server/src/__tests__/built-in-agents.test.ts +++ b/server/src/__tests__/built-in-agents.test.ts @@ -511,6 +511,11 @@ describeEmbeddedPostgres("built-in agents", () => { it("preserves new-agent approval gates during automatic Reflection Coach provisioning", async () => { const companyId = await seedCompany({ requireApproval: true }); + const mutationPolicy = { + requiresDisplayedDiff: true, + requiresAcceptedTaskInteraction: true, + applyInSeparateFollowUpRun: true, + }; const result = await reconcileBuiltInAgentsOnStartup(db); @@ -526,6 +531,9 @@ describeEmbeddedPostgres("built-in agents", () => { name: "Reflection Coach", status: "pending_approval", budgetMonthlyCents: 0, + permissions: { + builtInMutationPolicy: mutationPolicy, + }, }, }); expect(state.resources.map((resource) => resource.stockStatus)).toEqual(["missing", "missing", "missing"]); @@ -538,6 +546,18 @@ describeEmbeddedPostgres("built-in agents", () => { agentId: state.agentId, sourceBuiltInAgentKey: "reflection-coach", featureKeys: ["reflection-coach"], + permissions: expect.objectContaining({ + builtInMutationPolicy: mutationPolicy, + }), + }, + }); + + await approvalService(db).approve(approval.id, "board-user", "Approved Reflection Coach"); + await expect(builtInAgentService(db).get(companyId, "reflection-coach")).resolves.toMatchObject({ + agent: { + permissions: { + builtInMutationPolicy: mutationPolicy, + }, }, }); diff --git a/server/src/services/agents.ts b/server/src/services/agents.ts index 72683199141..51546b911cc 100644 --- a/server/src/services/agents.ts +++ b/server/src/services/agents.ts @@ -176,6 +176,9 @@ function configPatchFromApprovalPayload(payload: Record) { if (Object.prototype.hasOwnProperty.call(payload, "metadata")) { patch.metadata = isPlainRecord(payload.metadata) ? payload.metadata : null; } + if (isPlainRecord(payload.permissions)) { + patch.permissions = payload.permissions; + } return patch; } @@ -783,6 +786,12 @@ export function agentService(db: Db) { { adapterType: (patch.adapterType ?? existing.adapterType) as string }, ); } + if (patch.permissions !== undefined) { + patch.permissions = normalizeAgentPermissions( + patch.permissions, + (patch.role ?? existing.role) as string, + ); + } const updated = await tx .update(agents) .set({ ...patch, status: "idle", updatedAt: new Date() }) diff --git a/server/src/services/built-in-agents.ts b/server/src/services/built-in-agents.ts index c0502c526f9..8bd0b905ef2 100644 --- a/server/src/services/built-in-agents.ts +++ b/server/src/services/built-in-agents.ts @@ -1416,7 +1416,7 @@ export function builtInAgentService(db: Db) { status: "pending_approval", metadata: builtInMetadata(definition), runtimeConfig: {}, - permissions: {}, + permissions: definition.defaultPermissions ?? {}, spentMonthlyCents: 0, lastHeartbeatAt: null, }, { allowBuiltInAgentMetadata: true }) as Agent; @@ -1436,6 +1436,7 @@ export function builtInAgentService(db: Db) { adapterType: pending.adapterType, adapterConfig: pending.adapterConfig, runtimeConfig: pending.runtimeConfig, + permissions: pending.permissions, budgetMonthlyCents: pending.budgetMonthlyCents, metadata: pending.metadata, agentId: pending.id,