-
Notifications
You must be signed in to change notification settings - Fork 2
Cursor/multiple model prompt lanes bb23 #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
57eba48
9c47f25
da59df4
9eb8c73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -101,6 +101,7 @@ import type { | |
| AgentChatSurface, | ||
| AgentChatSteerArgs, | ||
| AgentChatSendArgs, | ||
| AgentChatSuggestLaneNameArgs, | ||
| AgentChatCursorConfigOption, | ||
| AgentChatCursorConfigValue, | ||
| AgentChatCursorModeSnapshot, | ||
|
|
@@ -837,6 +838,13 @@ Return only the title text. | |
| - No quotes. | ||
| - No emoji. | ||
| - No trailing punctuation.`; | ||
|
|
||
| const LANE_NAME_FROM_PROMPT_SYSTEM_PROMPT = `You name git worktree lanes for a software project. | ||
| Return only the base name text (no model suffixes). | ||
| - Use 2 to 5 words, lowercase except proper nouns if needed. | ||
| - Slug-friendly: letters, numbers, spaces, and hyphens only (no slashes). | ||
| - Describe the task or feature from the user's message. | ||
| - No quotes, no emoji, no trailing punctuation.`; | ||
| const CODEX_REASONING_EFFORTS: Array<{ effort: string; description: string }> = [ | ||
| { effort: "low", description: "Fastest turn-around with shallow reasoning." }, | ||
| { effort: "medium", description: "Balanced reasoning depth and speed." }, | ||
|
|
@@ -4403,6 +4411,80 @@ export function createAgentChatService(args: { | |
| }; | ||
| }; | ||
|
|
||
| const suggestLaneNameFromPrompt = async (args: AgentChatSuggestLaneNameArgs): Promise<string> => { | ||
| const prompt = String(args.prompt ?? "").trim(); | ||
| const requestedModelId = String(args.modelId ?? "").trim(); | ||
| const sourceLaneId = String(args.laneId ?? "").trim(); | ||
| const fallback = (): string => { | ||
| const collapsed = prompt.replace(/\s+/g, " ").trim(); | ||
| if (!collapsed.length) return "parallel-task"; | ||
| const words = collapsed.split(/\s+/).filter(Boolean).slice(0, 4); | ||
| const slug = words.join("-").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, ""); | ||
| return slug.length ? slug.slice(0, 48) : "parallel-task"; | ||
| }; | ||
|
|
||
| if (!prompt.length || !requestedModelId.length || !sourceLaneId.length) { | ||
| return fallback(); | ||
| } | ||
|
|
||
| let cwd = projectRoot; | ||
| try { | ||
| ({ laneWorktreePath: cwd } = resolveLaneLaunchContext({ | ||
| laneService, | ||
| laneId: sourceLaneId, | ||
| purpose: "name a lane from prompt", | ||
| })); | ||
| } catch { | ||
| cwd = projectRoot; | ||
| } | ||
|
|
||
| try { | ||
| const auth = await detectAuth(); | ||
| const availableModels = getRegistryModels(auth).filter((descriptor) => !descriptor.deprecated); | ||
| if (!availableModels.length) return fallback(); | ||
|
|
||
| const config = resolveChatConfig(); | ||
| const preferredModelId = | ||
| [ | ||
| requestedModelId, | ||
| config.autoTitleModelId, | ||
| DEFAULT_AUTO_TITLE_MODEL_ID, | ||
| "anthropic/claude-haiku-4-5", | ||
| "openai/gpt-5.4-mini", | ||
| "openai/gpt-5.2", | ||
| "openai/gpt-5.4", | ||
| availableModels[0]?.id, | ||
| ].find((candidate) => { | ||
| const modelId = typeof candidate === "string" ? candidate.trim() : ""; | ||
| return modelId.length > 0 && availableModels.some((descriptor) => descriptor.id === modelId); | ||
| }) ?? null; | ||
|
|
||
| if (!preferredModelId) return fallback(); | ||
|
|
||
| const descriptor = getModelById(preferredModelId); | ||
| if (!descriptor) return fallback(); | ||
|
|
||
| const resolvedModel = await providerResolver.resolveModel(descriptor.id, auth, { | ||
| cwd, | ||
| middleware: false, | ||
| }); | ||
| const result = await generateText({ | ||
| model: resolvedModel, | ||
| system: LANE_NAME_FROM_PROMPT_SYSTEM_PROMPT, | ||
| prompt: `User message to parallelize across models:\n${prompt.slice(0, 2000)}`, | ||
| }); | ||
| const sanitized = sanitizeAutoTitle(result.text.trim(), 56); | ||
| if (!sanitized) return fallback(); | ||
| return sanitized; | ||
| } catch (error) { | ||
|
Comment on lines
+4476
to
+4479
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce lane-safe normalization on model output before returning.
💡 Suggested fix+ const normalizeLaneBase = (value: string): string | null => {
+ const normalized = value
+ .toLowerCase()
+ .replace(/[^a-z0-9 -]+/g, " ")
+ .replace(/\s+/g, " ")
+ .trim();
+ if (!normalized.length) return null;
+ const slug = normalized.replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
+ return slug.length ? slug.slice(0, 56) : null;
+ };
+
const sanitized = sanitizeAutoTitle(result.text.trim(), 56);
- if (!sanitized) return fallback();
- return sanitized;
+ const laneBase = sanitized ? normalizeLaneBase(sanitized) : null;
+ if (!laneBase) return fallback();
+ return laneBase;🤖 Prompt for AI Agents |
||
| logger.warn("agent_chat.suggest_lane_name_failed", { | ||
| modelId: requestedModelId, | ||
| error: error instanceof Error ? error.message : String(error), | ||
| }); | ||
| return fallback(); | ||
| } | ||
| }; | ||
|
|
||
| const computeHeadShaBestEffort = async (laneId: string): Promise<string | null> => { | ||
| let cwd: string; | ||
| try { | ||
|
|
@@ -13458,6 +13540,7 @@ export function createAgentChatService(args: { | |
|
|
||
| return { | ||
| createSession, | ||
| suggestLaneNameFromPrompt, | ||
| handoffSession, | ||
| sendMessage, | ||
| runSessionTurn, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -177,6 +177,7 @@ import type { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatRespondToInputArgs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatResumeArgs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatSendArgs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatSuggestLaneNameArgs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatSession, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatSessionSummary, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentChatSubagentSnapshot, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3796,6 +3797,11 @@ export function registerIpc({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await ctx.agentChatService.createSession(arg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcMain.handle(IPC.agentChatSuggestLaneName, async (_event, arg: AgentChatSuggestLaneNameArgs): Promise<string> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ctx = getCtx(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await ctx.agentChatService.suggestLaneNameFromPrompt(arg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3800
to
+3803
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# 1) Confirm the new IPC handler currently forwards payload directly.
rg -n -C3 'IPC\.agentChatSuggestLaneName|suggestLaneNameFromPrompt\('
# 2) Inspect whether service-level validation exists for this arg type.
fd -i 'agentChatService.ts' --exec rg -n -C5 'suggestLaneNameFromPrompt|AgentChatSuggestLaneNameArgs' {}Repository: arul28/ADE Length of output: 2780 🏁 Script executed: # Find AgentChatSuggestLaneNameArgs type definition
rg -n 'AgentChatSuggestLaneNameArgs\s*=' --type ts --type tsxRepository: arul28/ADE Length of output: 81 🏁 Script executed: # Check if validation patterns exist at IPC boundary by looking at other handlers
rg -n 'ipcMain\.handle' apps/desktop/src/main/services/ipc/registerIpc.ts | head -20Repository: arul28/ADE Length of output: 1730 🏁 Script executed: # Look for requireRecord or parseRecord patterns to see if validation is a standard pattern
rg -n 'requireRecord|parseRecord' apps/desktop/src/main/services/ipc/registerIpc.ts | head -10Repository: arul28/ADE Length of output: 677 🏁 Script executed: # Check the service implementation more thoroughly to understand validation approach
sed -n '4414,4450p' apps/desktop/src/main/services/ipc/registerIpc.tsRepository: arul28/ADE Length of output: 1465 🏁 Script executed: # View requireRecord implementation
sed -n '3494,3510p' apps/desktop/src/main/services/ipc/registerIpc.tsRepository: arul28/ADE Length of output: 690 🏁 Script executed: # View full suggestLaneNameFromPrompt service method
sed -n '4414,4470p' apps/desktop/src/main/services/ipc/registerIpc.tsRepository: arul28/ADE Length of output: 2163 🏁 Script executed: # Find AgentChatSuggestLaneNameArgs type definition
rg -n 'AgentChatSuggestLaneNameArgs' -A 3 --type tsRepository: arul28/ADE Length of output: 3567 🏁 Script executed: # Get the full AgentChatSuggestLaneNameArgs type definition
sed -n '615,625p' apps/desktop/src/shared/types/chat.tsRepository: arul28/ADE Length of output: 479 🏁 Script executed: # Check if there are similar agent chat IPC handlers and how they're validated
rg -n 'ipcMain\.handle\(IPC\.agentChat' apps/desktop/src/main/services/ipc/registerIpc.ts -A 3 | head -40Repository: arul28/ADE Length of output: 2037 🏁 Script executed: # Look at patterns: how many IPC handlers use requireRecord vs direct typing
rg -n 'const parse.*Args.*=|ipcMain\.handle' apps/desktop/src/main/services/ipc/registerIpc.ts | grep -A1 'parse.*Args' | head -20Repository: arul28/ADE Length of output: 427 Add runtime validation for The handler accepts Suggested fix+ const parseAgentChatSuggestLaneNameArgs = (value: unknown): AgentChatSuggestLaneNameArgs => {
+ const record = requireRecord(value, "Agent chat suggest lane name request");
+ if (typeof record.prompt !== "string" || !record.prompt.trim()) {
+ throw new Error("Agent chat suggest lane name prompt must be a non-empty string");
+ }
+ if (typeof record.modelId !== "string" || !record.modelId.trim()) {
+ throw new Error("Agent chat suggest lane name model ID must be a non-empty string");
+ }
+ if (typeof record.laneId !== "string" || !record.laneId.trim()) {
+ throw new Error("Agent chat suggest lane name lane ID must be a non-empty string");
+ }
+ return {
+ prompt: record.prompt.trim(),
+ modelId: record.modelId.trim(),
+ laneId: record.laneId.trim(),
+ } as AgentChatSuggestLaneNameArgs;
+ };
+
- ipcMain.handle(IPC.agentChatSuggestLaneName, async (_event, arg: AgentChatSuggestLaneNameArgs): Promise<string> => {
+ ipcMain.handle(IPC.agentChatSuggestLaneName, async (_event, arg: unknown): Promise<string> => {
const ctx = getCtx();
- return await ctx.agentChatService.suggestLaneNameFromPrompt(arg);
+ return await ctx.agentChatService.suggestLaneNameFromPrompt(parseAgentChatSuggestLaneNameArgs(arg));
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcMain.handle(IPC.agentChatHandoff, async (_event, arg: AgentChatHandoffArgs): Promise<AgentChatHandoffResult> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ctx = getCtx(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await ctx.agentChatService.handoffSession(arg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -4779,6 +4785,9 @@ export function registerIpc({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| const reasoning = typeof arg?.reasoning === "string" && arg.reasoning.trim().length > 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? arg.reasoning.trim() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const additionalInstructions = typeof arg?.additionalInstructions === "string" && arg.additionalInstructions.trim().length > 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? arg.additionalInstructions.trim() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let runId = ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!model) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -4835,6 +4844,7 @@ export function registerIpc({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| model, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reasoningEffort: reasoning, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permissionMode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| additionalInstructions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originSurface: context.sourceTab === "integration" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? "integration" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : context.sourceTab === "rebase" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the hard requirement on
modelId/laneIdbefore AI suggestion.Line 4426 currently forces fallback unless
prompt,modelId, andlaneIdare all present. That bypasses your own model fallback chain andcwdfallback path, so AI suggestion can be unintentionally disabled.💡 Suggested fix
🤖 Prompt for AI Agents