From 57496d7404453246b61301345117068c5bc11d37 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Thu, 2 Jul 2026 08:07:47 -0500 Subject: [PATCH] feat(agent): surface the run's artist_account_id in the agent system prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headless runs (customer-prompt-task -> /api/chat/runs) receive artistId on every scheduled run, but it stopped at session provisioning — 0 occurrences past provisionRunSession — so agents roster-guess and nondeterministically land on the org-scoped artist list ("Rostrum Pacific" trap): tasks with implicit artists delivered 1/4 across two benchmark rounds while the one id-embedding task went 2/2 (recoupable/chat#1837). buildAgentSystemPrompt now emits an IMPORTANT CONTEXT VALUES block (artist_account_id + account_id, mirroring the interactive chat's getSystemPrompt) whenever the run has them; threaded via buildRunAgentInput -> RunAgentWorkflowInput -> runAgentStep. handleStartChatRun passes the validated artistId; handleChatWorkflowStream passes session.artist_id. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/lib/workflows/runAgentStep.ts | 6 ++++++ app/lib/workflows/runAgentWorkflow.ts | 2 ++ .../__tests__/buildAgentSystemPrompt.test.ts | 13 ++++++++++++ lib/chat/buildAgentSystemPrompt.ts | 20 +++++++++++++++++++ lib/chat/buildRunAgentInput.ts | 4 ++++ lib/chat/handleChatWorkflowStream.ts | 1 + lib/chat/runs/handleStartChatRun.ts | 1 + 7 files changed, 47 insertions(+) diff --git a/app/lib/workflows/runAgentStep.ts b/app/lib/workflows/runAgentStep.ts index 33db6714..730b4e40 100644 --- a/app/lib/workflows/runAgentStep.ts +++ b/app/lib/workflows/runAgentStep.ts @@ -36,6 +36,10 @@ export type RunAgentStepInput = { * is added to `experimental_context` right before each model call. */ agentContext: DurableAgentContext; + /** Active artist for the run — surfaced in the agent's system prompt (chat#1837). */ + artistId?: string; + /** Owning account id — surfaced alongside the artist in the system prompt. */ + accountId?: string; /** * Whether a user is present to answer `ask_user_question`. Interactive chat * sets true (default); headless runs (`/api/chat/runs`, `customer-prompt-task`) @@ -135,6 +139,8 @@ export async function runAgentStep(input: RunAgentStepInput): Promise { expect(prompt).toMatch(/never fabricate/i); expect(prompt).toMatch(/sample|estimate|industry average/i); }); + it("emits IMPORTANT CONTEXT VALUES when artistId/accountId are provided", () => { + const prompt = buildAgentSystemPrompt({ + artistId: "ebae4bb9-e38f-4763-b3c8-ff30e99f5d01", + accountId: "fb678396-a68f-4294-ae50-b8cacf9ce77b", + }); + expect(prompt).toMatch(/IMPORTANT CONTEXT VALUES/); + expect(prompt).toContain("artist_account_id: ebae4bb9-e38f-4763-b3c8-ff30e99f5d01"); + expect(prompt).toContain("account_id: fb678396-a68f-4294-ae50-b8cacf9ce77b"); + }); + + it("omits the context section when neither artistId nor accountId is provided", () => { + expect(buildAgentSystemPrompt({})).not.toMatch(/IMPORTANT CONTEXT VALUES/); + }); }); diff --git a/lib/chat/buildAgentSystemPrompt.ts b/lib/chat/buildAgentSystemPrompt.ts index c458b301..46f1e1a4 100644 --- a/lib/chat/buildAgentSystemPrompt.ts +++ b/lib/chat/buildAgentSystemPrompt.ts @@ -26,6 +26,10 @@ export type BuildAgentSystemPromptOptions = { * link prompt + recoup-api skill prompt). */ customInstructions?: string; + /** Active artist for this run — injected so the agent never roster-guesses. */ + artistId?: string; + /** Owning account id (the run's auth identity). */ + accountId?: string; }; /** @@ -55,6 +59,22 @@ export function buildAgentSystemPrompt(options: BuildAgentSystemPromptOptions): parts.push(ENVIRONMENT_SECTION); } + if (options.artistId || options.accountId) { + // Mirrors the interactive chat's getSystemPrompt context block — without + // this, headless agents roster-guess and can land on the org-scoped + // artist list instead of the run's actual artist (recoupable/chat#1837). + const lines = ["# IMPORTANT CONTEXT VALUES (use these exact values in tools)"]; + if (options.artistId) { + lines.push( + `- artist_account_id: ${options.artistId} (the active artist for this run — use for /api/artists/{id}/* and roster operations; do NOT search the roster for it)`, + ); + } + if (options.accountId) { + lines.push(`- account_id: ${options.accountId}`); + } + parts.push(lines.join("\n")); + } + if (options.customInstructions) { parts.push(options.customInstructions); } diff --git a/lib/chat/buildRunAgentInput.ts b/lib/chat/buildRunAgentInput.ts index cc487ddd..c644d400 100644 --- a/lib/chat/buildRunAgentInput.ts +++ b/lib/chat/buildRunAgentInput.ts @@ -11,6 +11,8 @@ export type BuildRunAgentInputParams = { sessionId: string; accountId: string; modelId: string; + /** Active artist for the run — surfaced in the agent's system prompt (chat#1837). */ + artistId?: string; sessionTitle?: string; /** `session.clone_url` — the single source for repo ids + recoup org id. */ cloneUrl: string | null; @@ -44,6 +46,7 @@ export function buildRunAgentInput({ sessionId, accountId, modelId, + artistId, sessionTitle, cloneUrl, sandboxState, @@ -62,6 +65,7 @@ export function buildRunAgentInput({ sessionId, accountId, modelId, + artistId, sessionTitle, interactive, repoOwner: repoIds?.owner, diff --git a/lib/chat/handleChatWorkflowStream.ts b/lib/chat/handleChatWorkflowStream.ts index fab562e1..568e546b 100644 --- a/lib/chat/handleChatWorkflowStream.ts +++ b/lib/chat/handleChatWorkflowStream.ts @@ -128,6 +128,7 @@ export async function handleChatWorkflowStream(request: NextRequest): Promise