fix: also prefix next system block with Claude Code banner
This commit is contained in:
@@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -134,6 +135,8 @@ func TestSystemIncludesClaudeCodePrompt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInjectClaudeCodePrompt(t *testing.T) {
|
func TestInjectClaudeCodePrompt(t *testing.T) {
|
||||||
|
claudePrefix := strings.TrimSpace(claudeCodeSystemPrompt)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
body string
|
body string
|
||||||
@@ -162,7 +165,7 @@ func TestInjectClaudeCodePrompt(t *testing.T) {
|
|||||||
system: "Custom prompt",
|
system: "Custom prompt",
|
||||||
wantSystemLen: 2,
|
wantSystemLen: 2,
|
||||||
wantFirstText: claudeCodeSystemPrompt,
|
wantFirstText: claudeCodeSystemPrompt,
|
||||||
wantSecondText: "Custom prompt",
|
wantSecondText: claudePrefix + "\n\nCustom prompt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "string system equals Claude Code prompt",
|
name: "string system equals Claude Code prompt",
|
||||||
@@ -178,7 +181,7 @@ func TestInjectClaudeCodePrompt(t *testing.T) {
|
|||||||
// Claude Code + Custom = 2
|
// Claude Code + Custom = 2
|
||||||
wantSystemLen: 2,
|
wantSystemLen: 2,
|
||||||
wantFirstText: claudeCodeSystemPrompt,
|
wantFirstText: claudeCodeSystemPrompt,
|
||||||
wantSecondText: "Custom",
|
wantSecondText: claudePrefix + "\n\nCustom",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "array system with existing Claude Code prompt (should dedupe)",
|
name: "array system with existing Claude Code prompt (should dedupe)",
|
||||||
@@ -190,7 +193,7 @@ func TestInjectClaudeCodePrompt(t *testing.T) {
|
|||||||
// Claude Code at start + Other = 2 (deduped)
|
// Claude Code at start + Other = 2 (deduped)
|
||||||
wantSystemLen: 2,
|
wantSystemLen: 2,
|
||||||
wantFirstText: claudeCodeSystemPrompt,
|
wantFirstText: claudeCodeSystemPrompt,
|
||||||
wantSecondText: "Other",
|
wantSecondText: claudePrefix + "\n\nOther",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty array",
|
name: "empty array",
|
||||||
|
|||||||
@@ -2479,6 +2479,10 @@ func injectClaudeCodePrompt(body []byte, system any) []byte {
|
|||||||
"text": claudeCodeSystemPrompt,
|
"text": claudeCodeSystemPrompt,
|
||||||
"cache_control": map[string]string{"type": "ephemeral"},
|
"cache_control": map[string]string{"type": "ephemeral"},
|
||||||
}
|
}
|
||||||
|
// Opencode plugin applies an extra safeguard: it not only prepends the Claude Code
|
||||||
|
// banner, it also prefixes the next system instruction with the same banner plus
|
||||||
|
// a blank line. This helps when upstream concatenates system instructions.
|
||||||
|
claudeCodePrefix := strings.TrimSpace(claudeCodeSystemPrompt)
|
||||||
|
|
||||||
var newSystem []any
|
var newSystem []any
|
||||||
|
|
||||||
@@ -2490,16 +2494,32 @@ func injectClaudeCodePrompt(body []byte, system any) []byte {
|
|||||||
if strings.TrimSpace(v) == "" || strings.TrimSpace(v) == strings.TrimSpace(claudeCodeSystemPrompt) {
|
if strings.TrimSpace(v) == "" || strings.TrimSpace(v) == strings.TrimSpace(claudeCodeSystemPrompt) {
|
||||||
newSystem = []any{claudeCodeBlock}
|
newSystem = []any{claudeCodeBlock}
|
||||||
} else {
|
} else {
|
||||||
newSystem = []any{claudeCodeBlock, map[string]any{"type": "text", "text": v}}
|
// Mirror opencode behavior: keep the banner as a separate system entry,
|
||||||
|
// but also prefix the next system text with the banner.
|
||||||
|
merged := v
|
||||||
|
if !strings.HasPrefix(v, claudeCodePrefix) {
|
||||||
|
merged = claudeCodePrefix + "\n\n" + v
|
||||||
|
}
|
||||||
|
newSystem = []any{claudeCodeBlock, map[string]any{"type": "text", "text": merged}}
|
||||||
}
|
}
|
||||||
case []any:
|
case []any:
|
||||||
newSystem = make([]any, 0, len(v)+1)
|
newSystem = make([]any, 0, len(v)+1)
|
||||||
newSystem = append(newSystem, claudeCodeBlock)
|
newSystem = append(newSystem, claudeCodeBlock)
|
||||||
|
prefixedNext := false
|
||||||
for _, item := range v {
|
for _, item := range v {
|
||||||
if m, ok := item.(map[string]any); ok {
|
if m, ok := item.(map[string]any); ok {
|
||||||
if text, ok := m["text"].(string); ok && strings.TrimSpace(text) == strings.TrimSpace(claudeCodeSystemPrompt) {
|
if text, ok := m["text"].(string); ok && strings.TrimSpace(text) == strings.TrimSpace(claudeCodeSystemPrompt) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Prefix the first subsequent text system block once.
|
||||||
|
if !prefixedNext {
|
||||||
|
if blockType, _ := m["type"].(string); blockType == "text" {
|
||||||
|
if text, ok := m["text"].(string); ok && strings.TrimSpace(text) != "" && !strings.HasPrefix(text, claudeCodePrefix) {
|
||||||
|
m["text"] = claudeCodePrefix + "\n\n" + text
|
||||||
|
prefixedNext = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
newSystem = append(newSystem, item)
|
newSystem = append(newSystem, item)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user