From 6cdcc9544a6a5cfb7f368a86501808d71c1d7a35 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 9 Jul 2026 19:35:55 +0000 Subject: [PATCH] Skip pending built-in reconciliation mutations Co-Authored-By: Paperclip --- server/src/__tests__/built-in-agents.test.ts | 16 ++++++++++++++++ server/src/services/built-in-agents.ts | 14 +++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/server/src/__tests__/built-in-agents.test.ts b/server/src/__tests__/built-in-agents.test.ts index b206d5037bf..2baf41c9f22 100644 --- a/server/src/__tests__/built-in-agents.test.ts +++ b/server/src/__tests__/built-in-agents.test.ts @@ -597,6 +597,22 @@ describeEmbeddedPostgres("built-in agents", () => { }, }); + const pendingReconcile = await reconcileBuiltInAgentsOnStartup(db); + expect(pendingReconcile.pendingApprovals).toBe(1); + const stillPending = await builtInAgentService(db).get(companyId, "reflection-coach"); + expect(stillPending).toMatchObject({ + status: "pending_approval", + agent: { + adapterConfig: {}, + status: "pending_approval", + }, + }); + expect(stillPending.resources.map((resource) => resource.stockStatus)).toEqual([ + "missing", + "missing", + "missing", + ]); + await approvalService(db).approve(approval.id, "board-user", "Approved Reflection Coach"); const approvedState = await builtInAgentService(db).get(companyId, "reflection-coach"); expect(approvedState).toMatchObject({ diff --git a/server/src/services/built-in-agents.ts b/server/src/services/built-in-agents.ts index 258d5f5aa65..243db39ff0e 100644 --- a/server/src/services/built-in-agents.ts +++ b/server/src/services/built-in-agents.ts @@ -1308,26 +1308,31 @@ export function builtInAgentService(db: Db) { const definition = requireBuiltInAgentDefinition(key); await ensureCompany(companyId); const existing = await findSingleAgent(companyId, definition); + const existingPendingApproval = existing?.status === "pending_approval"; const preserveExistingAdapter = Boolean( existing + && !existingPendingApproval && input.adapterType === undefined && input.adapterConfig === undefined && hasCompleteAdapterConfig(existing.adapterType, existing.adapterConfig), ); - const resolvedInput = preserveExistingAdapter + const resolvedInput = existingPendingApproval || preserveExistingAdapter ? input : await defaultProvisionInput(companyId, definition, input); if (existing) { const patch: Partial = { metadata: builtInMetadata(definition, existing.metadata), }; - if (resolvedInput.adapterType !== undefined || resolvedInput.adapterConfig !== undefined) { + if ( + !existingPendingApproval + && (resolvedInput.adapterType !== undefined || resolvedInput.adapterConfig !== undefined) + ) { const adapterType = resolvedInput.adapterType ?? existing.adapterType; assertAdapterAllowed(definition, adapterType); patch.adapterType = adapterType; patch.adapterConfig = resolvedInput.adapterConfig ?? existing.adapterConfig; } - if (resolvedInput.budgetMonthlyCents !== undefined) { + if (!existingPendingApproval && resolvedInput.budgetMonthlyCents !== undefined) { patch.budgetMonthlyCents = resolvedInput.budgetMonthlyCents; } const updated = await agentSvc.update(existing.id, patch, { @@ -1335,6 +1340,9 @@ export function builtInAgentService(db: Db) { recordRevision: { source: "built-in-agent:ensure" }, }); if (!updated) throw notFound("Built-in agent not found"); + if (existingPendingApproval) { + return state(definition, updated as Agent); + } await ensureBuiltInAgentDefaultGrants(updated as Agent, definition); const resources = await reconcileBundleResources(updated as Agent, definition, "reconcile"); return state(definition, await agentSvc.getById(existing.id) as Agent, resources);