Handle mentioned apps via smart context (#1412)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> Send mentioned apps (names + files) to the engine through
dyad_options, omit inline other-apps prefix when engine is enabled, and
adjust e2e to snapshot request payload.
> 
> - **Engine/Backend**:
> - Pass mentioned apps to engine via
`providerOptions['dyad-engine'].dyadMentionedApps` and forward as
`dyad_options.mentioned_apps` in `llm_engine_provider`.
> - Gate inline other-apps context: only include `otherCodebasePrefix`
when `isEngineEnabled` is false.
> - Enhance `extractMentionedAppsCodebases` to return `files` alongside
`codebaseInfo`.
> - **Tests**:
> - e2e: change `mention app (with pro)` snapshot to
`snapshotServerDump("request")` and update snapshot to assert request
payload contents.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7ddddf6c16c53cd36b4c7e4ec6a57da0616d1bb0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Will Chen
2025-09-30 17:05:18 -07:00
committed by GitHub
parent 29d8421ce9
commit 75b4b7c299
5 changed files with 521 additions and 205 deletions

View File

@@ -17,5 +17,5 @@ test("mention app (with pro)", async ({ po }) => {
await po.goToAppsTab(); await po.goToAppsTab();
await po.sendPrompt("[dump] @app:minimal-with-ai-rules hi"); await po.sendPrompt("[dump] @app:minimal-with-ai-rules hi");
await po.snapshotServerDump("all-messages"); await po.snapshotServerDump("request");
}); });

File diff suppressed because one or more lines are too long

View File

@@ -630,7 +630,10 @@ This conversation includes one or more image attachments. When the user uploads
}, },
] as const); ] as const);
const otherCodebasePrefix = otherAppsCodebaseInfo // If engine is enabled, we will send the other apps codebase info to the engine
// and process it with smart context.
const otherCodebasePrefix =
otherAppsCodebaseInfo && !isEngineEnabled
? ([ ? ([
{ {
role: "user", role: "user",
@@ -720,6 +723,12 @@ This conversation includes one or more image attachments. When the user uploads
"dyad-engine": { "dyad-engine": {
dyadRequestId, dyadRequestId,
dyadDisableFiles, dyadDisableFiles,
dyadMentionedApps: mentionedAppsCodebases.map(
({ files, appName }) => ({
appName,
files,
}),
),
}, },
"dyad-gateway": getExtraProviderOptions( "dyad-gateway": getExtraProviderOptions(
modelClient.builtinProviderId, modelClient.builtinProviderId,

View File

@@ -142,6 +142,10 @@ export function createDyadEngine(
if ("dyadDisableFiles" in parsedBody) { if ("dyadDisableFiles" in parsedBody) {
delete parsedBody.dyadDisableFiles; delete parsedBody.dyadDisableFiles;
} }
const dyadMentionedApps = parsedBody.dyadMentionedApps;
if ("dyadMentionedApps" in parsedBody) {
delete parsedBody.dyadMentionedApps;
}
// Track and modify requestId with attempt number // Track and modify requestId with attempt number
let modifiedRequestId = requestId; let modifiedRequestId = requestId;
@@ -161,6 +165,9 @@ export function createDyadEngine(
smart_context_mode: options.dyadOptions.smartContextMode, smart_context_mode: options.dyadOptions.smartContextMode,
enable_web_search: options.dyadOptions.enableWebSearch, enable_web_search: options.dyadOptions.enableWebSearch,
}; };
if (dyadMentionedApps?.length) {
parsedBody.dyad_options.mentioned_apps = dyadMentionedApps;
}
} }
// Return modified request with files included and requestId in headers // Return modified request with files included and requestId in headers

View File

@@ -1,6 +1,6 @@
import { db } from "../../db"; import { db } from "../../db";
import { getDyadAppPath } from "../../paths/paths"; import { getDyadAppPath } from "../../paths/paths";
import { extractCodebase } from "../../utils/codebase"; import { CodebaseFile, extractCodebase } from "../../utils/codebase";
import { validateChatContext } from "../utils/context_paths_utils"; import { validateChatContext } from "../utils/context_paths_utils";
import log from "electron-log"; import log from "electron-log";
@@ -10,7 +10,7 @@ const logger = log.scope("mention_apps");
export async function extractMentionedAppsCodebases( export async function extractMentionedAppsCodebases(
mentionedAppNames: string[], mentionedAppNames: string[],
excludeCurrentAppId?: number, excludeCurrentAppId?: number,
): Promise<{ appName: string; codebaseInfo: string }[]> { ): Promise<{ appName: string; codebaseInfo: string; files: CodebaseFile[] }[]> {
if (mentionedAppNames.length === 0) { if (mentionedAppNames.length === 0) {
return []; return [];
} }
@@ -25,14 +25,18 @@ export async function extractMentionedAppsCodebases(
) && app.id !== excludeCurrentAppId, ) && app.id !== excludeCurrentAppId,
); );
const results: { appName: string; codebaseInfo: string }[] = []; const results: {
appName: string;
codebaseInfo: string;
files: CodebaseFile[];
}[] = [];
for (const app of mentionedApps) { for (const app of mentionedApps) {
try { try {
const appPath = getDyadAppPath(app.path); const appPath = getDyadAppPath(app.path);
const chatContext = validateChatContext(app.chatContext); const chatContext = validateChatContext(app.chatContext);
const { formattedOutput } = await extractCodebase({ const { formattedOutput, files } = await extractCodebase({
appPath, appPath,
chatContext, chatContext,
}); });
@@ -40,6 +44,7 @@ export async function extractMentionedAppsCodebases(
results.push({ results.push({
appName: app.name, appName: app.name,
codebaseInfo: formattedOutput, codebaseInfo: formattedOutput,
files,
}); });
logger.log(`Extracted codebase for mentioned app: ${app.name}`); logger.log(`Extracted codebase for mentioned app: ${app.name}`);