Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions packages/types/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
66 changes: 66 additions & 0 deletions src/api/providers/fetchers/__tests__/roo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
4 changes: 4 additions & 0 deletions src/api/providers/fetchers/roo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
52 changes: 52 additions & 0 deletions src/core/prompts/__tests__/sections.spec.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down Expand Up @@ -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")
})
})
17 changes: 16 additions & 1 deletion src/core/prompts/sections/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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() : ""}`
}
2 changes: 2 additions & 0 deletions src/core/prompts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/core/webview/generateSystemPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
.getConfiguration(Package.name)
.get<boolean>("newTaskRequireTodos", false),
toolProtocol,
isStealthModel: modelInfo?.isStealthModel,
},
)

Expand Down
Loading