Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/lib/workflows/runAgentStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new account_id prompt propagation is currently type-optional at the runAgentStep boundary, which weakens the compile-time guarantee that this context is always present. Since upstream workflow input already requires accountId, making it optional here can let future call sites accidentally omit it and silently remove account_id from the system prompt again. Keeping this field required in RunAgentStepInput would preserve the end-to-end contract.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/lib/workflows/runAgentStep.ts, line 42:

<comment>The new `account_id` prompt propagation is currently type-optional at the `runAgentStep` boundary, which weakens the compile-time guarantee that this context is always present. Since upstream workflow input already requires `accountId`, making it optional here can let future call sites accidentally omit it and silently remove `account_id` from the system prompt again. Keeping this field required in `RunAgentStepInput` would preserve the end-to-end contract.</comment>

<file context>
@@ -36,6 +36,10 @@ export type RunAgentStepInput = {
+  /** 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
</file context>

/**
* Whether a user is present to answer `ask_user_question`. Interactive chat
* sets true (default); headless runs (`/api/chat/runs`, `customer-prompt-task`)
Expand Down Expand Up @@ -135,6 +139,8 @@ export async function runAgentStep(input: RunAgentStepInput): Promise<RunAgentSt
const systemPrompt = buildAgentSystemPrompt({
cwd: input.agentContext.sandbox.workingDirectory,
customInstructions: agentCustomInstructions,
artistId: input.artistId,
accountId: input.accountId,
});
const result = streamText({
model: callModel,
Expand Down
2 changes: 2 additions & 0 deletions app/lib/workflows/runAgentWorkflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export type RunAgentWorkflowInput = {
*/
accountId: string;
modelId: string;
/** Active artist for the run — surfaced in the agent's system prompt (chat#1837). */
artistId?: string;
/** Whether an interactive user is present (chat UI) to answer ask_user_question. */
interactive?: boolean;
/**
Expand Down
13 changes: 13 additions & 0 deletions lib/chat/__tests__/buildAgentSystemPrompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,17 @@ describe("buildAgentSystemPrompt", () => {
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");
});
Comment on lines +34 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new prompt logic has separate artistId and accountId branches, but the added tests only verify "both present" and "neither present." That leaves the one-ID paths unprotected, which is where this feature can silently regress in future edits. Adding explicit tests for artistId-only and accountId-only inputs would make the new behavior contract complete.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/chat/__tests__/buildAgentSystemPrompt.test.ts, line 34:

<comment>The new prompt logic has separate `artistId` and `accountId` branches, but the added tests only verify "both present" and "neither present." That leaves the one-ID paths unprotected, which is where this feature can silently regress in future edits. Adding explicit tests for `artistId`-only and `accountId`-only inputs would make the new behavior contract complete.</comment>

<file context>
@@ -31,4 +31,17 @@ describe("buildAgentSystemPrompt", () => {
     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",
</file context>
Suggested change
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.each([
[{ artistId: "ebae4bb9-e38f-4763-b3c8-ff30e99f5d01" }, ["artist_account_id: ebae4bb9-e38f-4763-b3c8-ff30e99f5d01"], ["account_id:"]],
[{ accountId: "fb678396-a68f-4294-ae50-b8cacf9ce77b" }, ["account_id: fb678396-a68f-4294-ae50-b8cacf9ce77b"], ["artist_account_id:"]],
[
{
artistId: "ebae4bb9-e38f-4763-b3c8-ff30e99f5d01",
accountId: "fb678396-a68f-4294-ae50-b8cacf9ce77b",
},
[
"artist_account_id: ebae4bb9-e38f-4763-b3c8-ff30e99f5d01",
"account_id: fb678396-a68f-4294-ae50-b8cacf9ce77b",
],
[],
],
])("emits IMPORTANT CONTEXT VALUES for %o", (options, expected, unexpected) => {
const prompt = buildAgentSystemPrompt(options);
expect(prompt).toMatch(/IMPORTANT CONTEXT VALUES/);
for (const value of expected) expect(prompt).toContain(value);
for (const value of unexpected) expect(prompt).not.toContain(value);
});


it("omits the context section when neither artistId nor accountId is provided", () => {
expect(buildAgentSystemPrompt({})).not.toMatch(/IMPORTANT CONTEXT VALUES/);
});
});
20 changes: 20 additions & 0 deletions lib/chat/buildAgentSystemPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 4 additions & 0 deletions lib/chat/buildRunAgentInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -44,6 +46,7 @@ export function buildRunAgentInput({
sessionId,
accountId,
modelId,
artistId,
sessionTitle,
cloneUrl,
sandboxState,
Expand All @@ -62,6 +65,7 @@ export function buildRunAgentInput({
sessionId,
accountId,
modelId,
artistId,
sessionTitle,
interactive,
repoOwner: repoIds?.owner,
Expand Down
1 change: 1 addition & 0 deletions lib/chat/handleChatWorkflowStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export async function handleChatWorkflowStream(request: NextRequest): Promise<Re
sessionId: validated.sessionId,
accountId: validated.accountId,
modelId,
artistId: session.artist_id ?? undefined,
sessionTitle: session.title ?? undefined,
cloneUrl: session.clone_url,
sandboxState: session.sandbox_state as VercelState,
Expand Down
1 change: 1 addition & 0 deletions lib/chat/runs/handleStartChatRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export async function handleStartChatRun(request: NextRequest): Promise<Response
sessionId: provisioned.session.id,
accountId,
modelId,
artistId,
sessionTitle: provisioned.session.title ?? undefined,
cloneUrl: provisioned.session.clone_url,
sandboxState: provisioned.sandboxState,
Expand Down
Loading