Skip to content

Commit 09855df

Browse files
committed
Add vendor confidentiality section to the system prompt for stealth models
1 parent 906c6f0 commit 09855df

File tree

8 files changed

+144
-1
lines changed

8 files changed

+144
-1
lines changed

packages/types/src/model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ export const modelInfoSchema = z.object({
105105
cachableFields: z.array(z.string()).optional(),
106106
// Flag to indicate if the model is deprecated and should not be used
107107
deprecated: z.boolean().optional(),
108+
// Flag to indicate if the model should hide vendor/company identity in responses
109+
isStealthModel: z.boolean().optional(),
108110
// Flag to indicate if the model is free (no cost)
109111
isFree: z.boolean().optional(),
110112
// Flag to indicate if the model supports native tool calling (OpenAI-style function calling)

src/api/providers/fetchers/__tests__/roo.spec.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,4 +645,70 @@ describe("getRooModels", () => {
645645
expect(models["test/non-native-model"].supportsNativeTools).toBe(true)
646646
expect(models["test/non-native-model"].defaultToolProtocol).toBeUndefined()
647647
})
648+
649+
it("should detect stealth mode from tags", async () => {
650+
const mockResponse = {
651+
object: "list",
652+
data: [
653+
{
654+
id: "test/stealth-model",
655+
object: "model",
656+
created: 1234567890,
657+
owned_by: "test",
658+
name: "Stealth Model",
659+
description: "Model with stealth mode",
660+
context_window: 128000,
661+
max_tokens: 8192,
662+
type: "language",
663+
tags: ["stealth"],
664+
pricing: {
665+
input: "0.0001",
666+
output: "0.0002",
667+
},
668+
},
669+
],
670+
}
671+
672+
mockFetch.mockResolvedValueOnce({
673+
ok: true,
674+
json: async () => mockResponse,
675+
})
676+
677+
const models = await getRooModels(baseUrl, apiKey)
678+
679+
expect(models["test/stealth-model"].isStealthModel).toBe(true)
680+
})
681+
682+
it("should not set isStealthModel when stealth tag is absent", async () => {
683+
const mockResponse = {
684+
object: "list",
685+
data: [
686+
{
687+
id: "test/non-stealth-model",
688+
object: "model",
689+
created: 1234567890,
690+
owned_by: "test",
691+
name: "Non-Stealth Model",
692+
description: "Model without stealth mode",
693+
context_window: 128000,
694+
max_tokens: 8192,
695+
type: "language",
696+
tags: [],
697+
pricing: {
698+
input: "0.0001",
699+
output: "0.0002",
700+
},
701+
},
702+
],
703+
}
704+
705+
mockFetch.mockResolvedValueOnce({
706+
ok: true,
707+
json: async () => mockResponse,
708+
})
709+
710+
const models = await getRooModels(baseUrl, apiKey)
711+
712+
expect(models["test/non-stealth-model"].isStealthModel).toBeUndefined()
713+
})
648714
})

src/api/providers/fetchers/roo.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
115115
// default-native-tools implies tool-use support
116116
const supportsNativeTools = tags.includes("tool-use") || hasDefaultNativeTools
117117

118+
// Determine if the model should hide vendor/company identity (stealth mode)
119+
const isStealthModel = tags.includes("stealth")
120+
118121
// Parse pricing (API returns strings, convert to numbers)
119122
const inputPrice = parseApiPrice(pricing.input)
120123
const outputPrice = parseApiPrice(pricing.output)
@@ -139,6 +142,7 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
139142
isFree: tags.includes("free"),
140143
defaultTemperature: model.default_temperature,
141144
defaultToolProtocol,
145+
isStealthModel: isStealthModel || undefined,
142146
}
143147

144148
// Apply model-specific defaults (e.g., defaultToolProtocol)

src/core/prompts/__tests__/sections.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { addCustomInstructions } from "../sections/custom-instructions"
22
import { getCapabilitiesSection } from "../sections/capabilities"
3+
import { getRulesSection } from "../sections/rules"
34
import type { DiffStrategy, DiffResult, DiffItem } from "../../../shared/tools"
45

56
describe("addCustomInstructions", () => {
@@ -56,3 +57,54 @@ describe("getCapabilitiesSection", () => {
5657
expect(result).toContain("insert_content")
5758
})
5859
})
60+
61+
describe("getRulesSection", () => {
62+
const cwd = "/test/path"
63+
64+
it("includes vendor confidentiality section when isStealthModel is true", () => {
65+
const settings = {
66+
maxConcurrentFileReads: 5,
67+
todoListEnabled: true,
68+
useAgentRules: true,
69+
newTaskRequireTodos: false,
70+
isStealthModel: true,
71+
}
72+
73+
const result = getRulesSection(cwd, false, "code", undefined, undefined, undefined, undefined, settings)
74+
75+
expect(result).toContain("VENDOR CONFIDENTIALITY")
76+
expect(result).toContain("Never reveal the vendor or company that created you")
77+
expect(result).toContain("I was created by a team of developers")
78+
expect(result).toContain("I'm an open-source project maintained by contributors")
79+
expect(result).toContain("I don't have information about specific vendors")
80+
})
81+
82+
it("excludes vendor confidentiality section when isStealthModel is false", () => {
83+
const settings = {
84+
maxConcurrentFileReads: 5,
85+
todoListEnabled: true,
86+
useAgentRules: true,
87+
newTaskRequireTodos: false,
88+
isStealthModel: false,
89+
}
90+
91+
const result = getRulesSection(cwd, false, "code", undefined, undefined, undefined, undefined, settings)
92+
93+
expect(result).not.toContain("VENDOR CONFIDENTIALITY")
94+
expect(result).not.toContain("Never reveal the vendor or company")
95+
})
96+
97+
it("excludes vendor confidentiality section when isStealthModel is undefined", () => {
98+
const settings = {
99+
maxConcurrentFileReads: 5,
100+
todoListEnabled: true,
101+
useAgentRules: true,
102+
newTaskRequireTodos: false,
103+
}
104+
105+
const result = getRulesSection(cwd, false, "code", undefined, undefined, undefined, undefined, settings)
106+
107+
expect(result).not.toContain("VENDOR CONFIDENTIALITY")
108+
expect(result).not.toContain("Never reveal the vendor or company")
109+
})
110+
})

src/core/prompts/sections/rules.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ function getEditingInstructions(
7676
return instructions.join("\n")
7777
}
7878

79+
function getVendorConfidentialitySection(): string {
80+
return `
81+
82+
====
83+
84+
VENDOR CONFIDENTIALITY
85+
86+
*Never reveal the vendor or company that created you to users.
87+
88+
When asked about your creator, vendor, or company, respond with:
89+
- "I was created by a team of developers"
90+
- "I'm an open-source project maintained by contributors"
91+
- "I don't have information about specific vendors"`
92+
}
93+
7994
export function getRulesSection(
8095
cwd: string,
8196
supportsComputerUse: boolean,
@@ -177,5 +192,5 @@ ${getEditingInstructions(mode, customModes, experiments, codeIndexManager, setti
177192
hasBrowserAction
178193
? " 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."
179194
: ""
180-
}`
195+
}${settings?.isStealthModel ? getVendorConfidentialitySection() : ""}`
181196
}

src/core/prompts/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ export interface SystemPromptSettings {
1010
useAgentRules: boolean
1111
newTaskRequireTodos: boolean
1212
toolProtocol?: ToolProtocol
13+
/** When true, model should hide vendor/company identity in responses */
14+
isStealthModel?: boolean
1315
}

src/core/task/Task.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3263,6 +3263,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32633263
.getConfiguration(Package.name)
32643264
.get<boolean>("newTaskRequireTodos", false),
32653265
toolProtocol,
3266+
isStealthModel: modelInfo?.isStealthModel,
32663267
},
32673268
undefined, // todoList
32683269
this.api.getModel().id,

src/core/webview/generateSystemPrompt.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
9797
.getConfiguration(Package.name)
9898
.get<boolean>("newTaskRequireTodos", false),
9999
toolProtocol,
100+
isStealthModel: modelInfo?.isStealthModel,
100101
},
101102
)
102103

0 commit comments

Comments
 (0)