feat: add OPENCLAUDE_DISABLE_TOOL_REMINDERS env var to suppress hidden tool-output reminders (#837)
Gates three injection sites behind OPENCLAUDE_DISABLE_TOOL_REMINDERS: - FileReadTool cyber-risk mitigation reminder (appended to every Read result when the model is not in MITIGATION_EXEMPT_MODELS) - todo_reminder attachment for TodoWrite usage - task_reminder attachment for TaskCreate/TaskUpdate usage All three reminders are model-only side-channel instructions the user cannot see today. Users who want full transparency over what the model receives can now opt out without patching dist/cli.mjs on every upgrade. Default behavior is unchanged when the flag is unset. Closes #809
This commit is contained in:
@@ -272,6 +272,11 @@ ANTHROPIC_API_KEY=sk-ant-your-key-here
|
|||||||
# trigger "Extra required key ... supplied" errors from OpenAI-compatible endpoints
|
# trigger "Extra required key ... supplied" errors from OpenAI-compatible endpoints
|
||||||
# OPENCLAUDE_DISABLE_STRICT_TOOLS=1
|
# OPENCLAUDE_DISABLE_STRICT_TOOLS=1
|
||||||
|
|
||||||
|
# Disable hidden <system-reminder> messages injected into tool output
|
||||||
|
# Suppresses the file-read cyber-risk reminder and the todo/task tool nudges
|
||||||
|
# Useful for users who want full transparency over what the model sees
|
||||||
|
# OPENCLAUDE_DISABLE_TOOL_REMINDERS=1
|
||||||
|
|
||||||
# Custom timeout for API requests in milliseconds (default: varies)
|
# Custom timeout for API requests in milliseconds (default: varies)
|
||||||
# API_TIMEOUT_MS=60000
|
# API_TIMEOUT_MS=60000
|
||||||
|
|
||||||
|
|||||||
@@ -733,6 +733,9 @@ export const CYBER_RISK_MITIGATION_REMINDER =
|
|||||||
const MITIGATION_EXEMPT_MODELS = new Set(['claude-opus-4-6'])
|
const MITIGATION_EXEMPT_MODELS = new Set(['claude-opus-4-6'])
|
||||||
|
|
||||||
function shouldIncludeFileReadMitigation(): boolean {
|
function shouldIncludeFileReadMitigation(): boolean {
|
||||||
|
if (isEnvTruthy(process.env.OPENCLAUDE_DISABLE_TOOL_REMINDERS)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
const shortName = getCanonicalName(getMainLoopModel())
|
const shortName = getCanonicalName(getMainLoopModel())
|
||||||
return !MITIGATION_EXEMPT_MODELS.has(shortName)
|
return !MITIGATION_EXEMPT_MODELS.has(shortName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import type {
|
|||||||
import { isAdvisorBlock } from './advisor.js'
|
import { isAdvisorBlock } from './advisor.js'
|
||||||
import { isAgentSwarmsEnabled } from './agentSwarmsEnabled.js'
|
import { isAgentSwarmsEnabled } from './agentSwarmsEnabled.js'
|
||||||
import { count } from './array.js'
|
import { count } from './array.js'
|
||||||
|
import { isEnvTruthy } from './envUtils.js'
|
||||||
import {
|
import {
|
||||||
type Attachment,
|
type Attachment,
|
||||||
type HookAttachment,
|
type HookAttachment,
|
||||||
@@ -3666,6 +3667,9 @@ Read the team config to discover your teammates' names. Check the task list peri
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
case 'todo_reminder': {
|
case 'todo_reminder': {
|
||||||
|
if (isEnvTruthy(process.env.OPENCLAUDE_DISABLE_TOOL_REMINDERS)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
const todoItems = attachment.content
|
const todoItems = attachment.content
|
||||||
.map((todo, index) => `${index + 1}. [${todo.status}] ${todo.content}`)
|
.map((todo, index) => `${index + 1}. [${todo.status}] ${todo.content}`)
|
||||||
.join('\n')
|
.join('\n')
|
||||||
@@ -3686,6 +3690,9 @@ Read the team config to discover your teammates' names. Check the task list peri
|
|||||||
if (!isTodoV2Enabled()) {
|
if (!isTodoV2Enabled()) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
if (isEnvTruthy(process.env.OPENCLAUDE_DISABLE_TOOL_REMINDERS)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
const taskItems = attachment.content
|
const taskItems = attachment.content
|
||||||
.map(task => `#${task.id}. [${task.status}] ${task.subject}`)
|
.map(task => `#${task.id}. [${task.status}] ${task.subject}`)
|
||||||
.join('\n')
|
.join('\n')
|
||||||
|
|||||||
Reference in New Issue
Block a user