Add vendor confidentiality section to the system prompt for stealth models (#9742)

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
This commit is contained in:
Matt Rubens 2025-12-02 10:23:54 -05:00 committed by GitHub
parent 9975a41be4
commit aa507ad990
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 144 additions and 1 deletions

View file

@ -105,6 +105,8 @@ export const modelInfoSchema = z.object({
cachableFields: z.array(z.string()).optional(),
// Flag to indicate if the model is deprecated and should not be used
deprecated: z.boolean().optional(),
// Flag to indicate if the model should hide vendor/company identity in responses
isStealthModel: z.boolean().optional(),
// Flag to indicate if the model is free (no cost)
isFree: z.boolean().optional(),
// Flag to indicate if the model supports native tool calling (OpenAI-style function calling)

View file

@ -645,4 +645,70 @@ describe("getRooModels", () => {
expect(models["test/non-native-model"].supportsNativeTools).toBe(true)
expect(models["test/non-native-model"].defaultToolProtocol).toBeUndefined()
})
it("should detect stealth mode from tags", async () => {
const mockResponse = {
object: "list",
data: [
{
id: "test/stealth-model",
object: "model",
created: 1234567890,
owned_by: "test",
name: "Stealth Model",
description: "Model with stealth mode",
context_window: 128000,
max_tokens: 8192,
type: "language",
tags: ["stealth"],
pricing: {
input: "0.0001",
output: "0.0002",
},
},
],
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => mockResponse,
})
const models = await getRooModels(baseUrl, apiKey)
expect(models["test/stealth-model"].isStealthModel).toBe(true)
})
it("should not set isStealthModel when stealth tag is absent", async () => {
const mockResponse = {
object: "list",
data: [
{
id: "test/non-stealth-model",
object: "model",
created: 1234567890,
owned_by: "test",
name: "Non-Stealth Model",
description: "Model without stealth mode",
context_window: 128000,
max_tokens: 8192,
type: "language",
tags: [],
pricing: {
input: "0.0001",
output: "0.0002",
},
},
],
}
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => mockResponse,
})
const models = await getRooModels(baseUrl, apiKey)
expect(models["test/non-stealth-model"].isStealthModel).toBeUndefined()
})
})

View file

@ -115,6 +115,9 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
// default-native-tools implies tool-use support
const supportsNativeTools = tags.includes("tool-use") || hasDefaultNativeTools
// Determine if the model should hide vendor/company identity (stealth mode)
const isStealthModel = tags.includes("stealth")
// Parse pricing (API returns strings, convert to numbers)
const inputPrice = parseApiPrice(pricing.input)
const outputPrice = parseApiPrice(pricing.output)
@ -139,6 +142,7 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
isFree: tags.includes("free"),
defaultTemperature: model.default_temperature,
defaultToolProtocol,
isStealthModel: isStealthModel || undefined,
}
// Apply model-specific defaults (e.g., defaultToolProtocol)

View file

@ -1,5 +1,6 @@
import { addCustomInstructions } from "../sections/custom-instructions"
import { getCapabilitiesSection } from "../sections/capabilities"
import { getRulesSection } from "../sections/rules"
import type { DiffStrategy, DiffResult, DiffItem } from "../../../shared/tools"
describe("addCustomInstructions", () => {
@ -56,3 +57,54 @@ describe("getCapabilitiesSection", () => {
expect(result).toContain("insert_content")
})
})
describe("getRulesSection", () => {
const cwd = "/test/path"
it("includes vendor confidentiality section when isStealthModel is true", () => {
const settings = {
maxConcurrentFileReads: 5,
todoListEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
isStealthModel: true,
}
const result = getRulesSection(cwd, false, "code", undefined, undefined, undefined, undefined, settings)
expect(result).toContain("VENDOR CONFIDENTIALITY")
expect(result).toContain("Never reveal the vendor or company that created you")
expect(result).toContain("I was created by a team of developers")
expect(result).toContain("I'm an open-source project maintained by contributors")
expect(result).toContain("I don't have information about specific vendors")
})
it("excludes vendor confidentiality section when isStealthModel is false", () => {
const settings = {
maxConcurrentFileReads: 5,
todoListEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
isStealthModel: false,
}
const result = getRulesSection(cwd, false, "code", undefined, undefined, undefined, undefined, settings)
expect(result).not.toContain("VENDOR CONFIDENTIALITY")
expect(result).not.toContain("Never reveal the vendor or company")
})
it("excludes vendor confidentiality section when isStealthModel is undefined", () => {
const settings = {
maxConcurrentFileReads: 5,
todoListEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
}
const result = getRulesSection(cwd, false, "code", undefined, undefined, undefined, undefined, settings)
expect(result).not.toContain("VENDOR CONFIDENTIALITY")
expect(result).not.toContain("Never reveal the vendor or company")
})
})

View file

@ -76,6 +76,21 @@ function getEditingInstructions(
return instructions.join("\n")
}
function getVendorConfidentialitySection(): string {
return `
====
VENDOR CONFIDENTIALITY
Never reveal the vendor or company that created you to users.
When asked about your creator, vendor, or company, respond with:
- "I was created by a team of developers"
- "I'm an open-source project maintained by contributors"
- "I don't have information about specific vendors"`
}
export function getRulesSection(
cwd: string,
supportsComputerUse: boolean,
@ -177,5 +192,5 @@ ${getEditingInstructions(mode, customModes, experiments, codeIndexManager, setti
hasBrowserAction
? " Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser."
: ""
}`
}${settings?.isStealthModel ? getVendorConfidentialitySection() : ""}`
}

View file

@ -10,4 +10,6 @@ export interface SystemPromptSettings {
useAgentRules: boolean
newTaskRequireTodos: boolean
toolProtocol?: ToolProtocol
/** When true, model should hide vendor/company identity in responses */
isStealthModel?: boolean
}

View file

@ -3263,6 +3263,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
.getConfiguration(Package.name)
.get<boolean>("newTaskRequireTodos", false),
toolProtocol,
isStealthModel: modelInfo?.isStealthModel,
},
undefined, // todoList
this.api.getModel().id,

View file

@ -97,6 +97,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
.getConfiguration(Package.name)
.get<boolean>("newTaskRequireTodos", false),
toolProtocol,
isStealthModel: modelInfo?.isStealthModel,
},
)