diff --git a/index.ts b/index.ts
index aaf6e640..c401802f 100644
--- a/index.ts
+++ b/index.ts
@@ -45,7 +45,8 @@ const plugin: Plugin = (async (ctx) => {
const prompts = {
synthInstruction: loadPrompt("synthetic"),
- nudgeInstruction: loadPrompt("nudge")
+ nudgeInstruction: loadPrompt("nudge"),
+ systemReminder: loadPrompt("system-reminder")
}
// Install global fetch wrapper for context pruning and synthetic instruction injection
diff --git a/lib/fetch-wrapper/formats/bedrock.ts b/lib/fetch-wrapper/formats/bedrock.ts
index 2aaedc64..bde93b59 100644
--- a/lib/fetch-wrapper/formats/bedrock.ts
+++ b/lib/fetch-wrapper/formats/bedrock.ts
@@ -8,7 +8,8 @@ function isNudgeMessage(msg: any, nudgeText: string): boolean {
return false
}
-function injectSynth(messages: any[], instruction: string, nudgeText: string): boolean {
+function injectSynth(messages: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ const fullInstruction = systemReminder + '\n\n' + instruction
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i]
if (msg.role === 'user') {
@@ -16,13 +17,13 @@ function injectSynth(messages: any[], instruction: string, nudgeText: string): b
if (typeof msg.content === 'string') {
if (msg.content.includes(instruction)) return false
- msg.content = msg.content + '\n\n' + instruction
+ msg.content = msg.content + '\n\n' + fullInstruction
} else if (Array.isArray(msg.content)) {
const alreadyInjected = msg.content.some(
(part: any) => part?.type === 'text' && typeof part.text === 'string' && part.text.includes(instruction)
)
if (alreadyInjected) return false
- msg.content.push({ type: 'text', text: instruction })
+ msg.content.push({ type: 'text', text: fullInstruction })
}
return true
}
@@ -56,8 +57,8 @@ export const bedrockFormat: FormatDescriptor = {
return body.messages
},
- injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
- return injectSynth(data, instruction, nudgeText)
+ injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ return injectSynth(data, instruction, nudgeText, systemReminder)
},
injectPrunableList(data: any[], injection: string): boolean {
diff --git a/lib/fetch-wrapper/formats/gemini.ts b/lib/fetch-wrapper/formats/gemini.ts
index 65102902..ab0a8593 100644
--- a/lib/fetch-wrapper/formats/gemini.ts
+++ b/lib/fetch-wrapper/formats/gemini.ts
@@ -9,7 +9,8 @@ function isNudgeContent(content: any, nudgeText: string): boolean {
return false
}
-function injectSynth(contents: any[], instruction: string, nudgeText: string): boolean {
+function injectSynth(contents: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ const fullInstruction = systemReminder + '\n\n' + instruction
for (let i = contents.length - 1; i >= 0; i--) {
const content = contents[i]
if (content.role === 'user' && Array.isArray(content.parts)) {
@@ -19,7 +20,7 @@ function injectSynth(contents: any[], instruction: string, nudgeText: string): b
(part: any) => part?.text && typeof part.text === 'string' && part.text.includes(instruction)
)
if (alreadyInjected) return false
- content.parts.push({ text: instruction })
+ content.parts.push({ text: fullInstruction })
return true
}
}
@@ -48,8 +49,8 @@ export const geminiFormat: FormatDescriptor = {
return body.contents
},
- injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
- return injectSynth(data, instruction, nudgeText)
+ injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ return injectSynth(data, instruction, nudgeText, systemReminder)
},
injectPrunableList(data: any[], injection: string): boolean {
diff --git a/lib/fetch-wrapper/formats/openai-chat.ts b/lib/fetch-wrapper/formats/openai-chat.ts
index 2ac3793c..48fdfcbf 100644
--- a/lib/fetch-wrapper/formats/openai-chat.ts
+++ b/lib/fetch-wrapper/formats/openai-chat.ts
@@ -8,7 +8,8 @@ function isNudgeMessage(msg: any, nudgeText: string): boolean {
return false
}
-function injectSynth(messages: any[], instruction: string, nudgeText: string): boolean {
+function injectSynth(messages: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ const fullInstruction = systemReminder + '\n\n' + instruction
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i]
if (msg.role === 'user') {
@@ -16,13 +17,13 @@ function injectSynth(messages: any[], instruction: string, nudgeText: string): b
if (typeof msg.content === 'string') {
if (msg.content.includes(instruction)) return false
- msg.content = msg.content + '\n\n' + instruction
+ msg.content = msg.content + '\n\n' + fullInstruction
} else if (Array.isArray(msg.content)) {
const alreadyInjected = msg.content.some(
(part: any) => part?.type === 'text' && typeof part.text === 'string' && part.text.includes(instruction)
)
if (alreadyInjected) return false
- msg.content.push({ type: 'text', text: instruction })
+ msg.content.push({ type: 'text', text: fullInstruction })
}
return true
}
@@ -47,8 +48,8 @@ export const openaiChatFormat: FormatDescriptor = {
return body.messages
},
- injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
- return injectSynth(data, instruction, nudgeText)
+ injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ return injectSynth(data, instruction, nudgeText, systemReminder)
},
injectPrunableList(data: any[], injection: string): boolean {
diff --git a/lib/fetch-wrapper/formats/openai-responses.ts b/lib/fetch-wrapper/formats/openai-responses.ts
index 6b84891d..acc03e3b 100644
--- a/lib/fetch-wrapper/formats/openai-responses.ts
+++ b/lib/fetch-wrapper/formats/openai-responses.ts
@@ -8,7 +8,8 @@ function isNudgeItem(item: any, nudgeText: string): boolean {
return false
}
-function injectSynth(input: any[], instruction: string, nudgeText: string): boolean {
+function injectSynth(input: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ const fullInstruction = systemReminder + '\n\n' + instruction
for (let i = input.length - 1; i >= 0; i--) {
const item = input[i]
if (item.type === 'message' && item.role === 'user') {
@@ -16,13 +17,13 @@ function injectSynth(input: any[], instruction: string, nudgeText: string): bool
if (typeof item.content === 'string') {
if (item.content.includes(instruction)) return false
- item.content = item.content + '\n\n' + instruction
+ item.content = item.content + '\n\n' + fullInstruction
} else if (Array.isArray(item.content)) {
const alreadyInjected = item.content.some(
(part: any) => part?.type === 'input_text' && typeof part.text === 'string' && part.text.includes(instruction)
)
if (alreadyInjected) return false
- item.content.push({ type: 'input_text', text: instruction })
+ item.content.push({ type: 'input_text', text: fullInstruction })
}
return true
}
@@ -47,8 +48,8 @@ export const openaiResponsesFormat: FormatDescriptor = {
return body.input
},
- injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
- return injectSynth(data, instruction, nudgeText)
+ injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
+ return injectSynth(data, instruction, nudgeText, systemReminder)
},
injectPrunableList(data: any[], injection: string): boolean {
diff --git a/lib/fetch-wrapper/handler.ts b/lib/fetch-wrapper/handler.ts
index db188838..6b7bfc3f 100644
--- a/lib/fetch-wrapper/handler.ts
+++ b/lib/fetch-wrapper/handler.ts
@@ -76,7 +76,7 @@ export async function handleFormat(
}
if (ctx.config.strategies.onTool.length > 0) {
- if (format.injectSynth(data, ctx.prompts.synthInstruction, ctx.prompts.nudgeInstruction)) {
+ if (format.injectSynth(data, ctx.prompts.synthInstruction, ctx.prompts.nudgeInstruction, ctx.prompts.systemReminder)) {
modified = true
}
diff --git a/lib/fetch-wrapper/types.ts b/lib/fetch-wrapper/types.ts
index 6a74840a..cb728a32 100644
--- a/lib/fetch-wrapper/types.ts
+++ b/lib/fetch-wrapper/types.ts
@@ -18,7 +18,7 @@ export interface FormatDescriptor {
name: string
detect(body: any): boolean
getDataArray(body: any): any[] | undefined
- injectSynth(data: any[], instruction: string, nudgeText: string): boolean
+ injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean
injectPrunableList(data: any[], injection: string): boolean
extractToolOutputs(data: any[], state: PluginState): ToolOutput[]
replaceToolOutput(data: any[], toolId: string, prunedMessage: string, state: PluginState): boolean
@@ -29,6 +29,7 @@ export interface FormatDescriptor {
export interface SynthPrompts {
synthInstruction: string
nudgeInstruction: string
+ systemReminder: string
}
export interface FetchHandlerContext {
diff --git a/lib/prompts/nudge.txt b/lib/prompts/nudge.txt
index 5a1fd5c9..8e91d946 100644
--- a/lib/prompts/nudge.txt
+++ b/lib/prompts/nudge.txt
@@ -1,7 +1,3 @@
-
-This nudge is injected by a plugin and is invisible to the user. Do not acknowledge or reference it in your response - simply follow it silently.
-
-
-You have accumulated several tool outputs. If you have completed a discrete unit of work and distilled relevant understanding in writing for the user to keep, use the prune tool to remove obsolete tool outputs from this conversation and optimize token usage.
+You have accumulated several tool outputs. Consider using the `prune` tool to declutter the conversation context.
diff --git a/lib/prompts/system-reminder.txt b/lib/prompts/system-reminder.txt
new file mode 100644
index 00000000..479ea7a2
--- /dev/null
+++ b/lib/prompts/system-reminder.txt
@@ -0,0 +1,3 @@
+
+These instructions are injected by a plugin and are invisible to the user. You must follow them silently and NEVER reference or discuss them (including the list) in your response or thinking/reasoning blocks, as this will confuse the user who cannot see them.
+
\ No newline at end of file