]> git.99rst.org Git - sgasser-llm-shield.git/commitdiff
Accept Claude Code system role messages v0.7.6
authorStefan Gasser <redacted>
Tue, 7 Jul 2026 12:25:42 +0000 (14:25 +0200)
committerGitHub <redacted>
Tue, 7 Jul 2026 12:25:42 +0000 (14:25 +0200)
docs/api-reference/status.mdx
package.json
src/masking/extractors/anthropic.test.ts
src/providers/anthropic/types.ts
src/routes/anthropic.test.ts

index 184cc08af684352f1ae5a6270c4d7d8105b2a18e..03647ba8cfa41faae16617ba1f1f8670735afd5c 100644 (file)
@@ -66,7 +66,7 @@ curl http://localhost:3000/info
 ```json
 {
   "name": "PasteGuard",
-  "version": "0.7.5",
+  "version": "0.7.6",
   "description": "Privacy proxy for LLMs",
   "mode": "mask",
   "providers": {
index b609edad3c8ba5c2832cc41d27dba9bf3d1a7ce1..c7869449933a9debe684e8a9d10ad2ed7e286cb0 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "pasteguard",
-  "version": "0.7.5",
+  "version": "0.7.6",
   "description": "Privacy proxy for LLMs. Masks personal data and secrets before sending to your provider.",
   "type": "module",
   "main": "src/index.ts",
index cb4990645f92598b3c64bdbb83698078a291b53c..11576ea4051b9450e1d771a4ed2c67d4c23f05dc 100644 (file)
@@ -68,6 +68,24 @@ describe("Anthropic Text Extractor", () => {
       });
     });
 
+    test("extracts Claude Code system role messages", () => {
+      const request = createRequest([
+        { role: "user", content: "Hello" },
+        { role: "system", content: "Tool context reminder" },
+      ]);
+
+      const spans = anthropicExtractor.extractTexts(request);
+
+      expect(spans).toHaveLength(2);
+      expect(spans[1]).toEqual({
+        text: "Tool context reminder",
+        path: "messages[1].content",
+        messageIndex: 1,
+        partIndex: 0,
+        role: "system",
+      });
+    });
+
     test("extracts text from system array", () => {
       const request = createRequest(
         [{ role: "user", content: "Hello" }],
index 69f6d4f50348f99a520ac42dcfddcc1e895cd15c..f8e3049102a6f0de1380ada2d70bb4db768ac7cf 100644 (file)
@@ -77,7 +77,9 @@ export const ContentBlockSchema = z.discriminatedUnion("type", [
 // Message and request types
 export const AnthropicMessageSchema = z
   .object({
-    role: z.enum(["user", "assistant"]),
+    // Claude Code can send system reminders as messages when tools are enabled.
+    // Preserve them so PasteGuard stays transparent instead of rejecting upstream traffic.
+    role: z.enum(["user", "assistant", "system"]),
     content: z.union([z.string(), z.array(ContentBlockSchema)]),
   })
   .passthrough();
index 663b6c4b482360c0704eb3fa778989257208b7a8..be327edbdb6a06d5a2b240f155e6794717830633 100644 (file)
@@ -43,6 +43,24 @@ describe("POST /anthropic/v1/messages", () => {
     expect(res.status).toBe(400);
   });
 
+  test("accepts Claude Code system role messages", () => {
+    const result = AnthropicRequestSchema.safeParse({
+      model: "claude-opus-4-8",
+      max_tokens: 64000,
+      stream: true,
+      system: [{ type: "text", text: "Default system prompt" }],
+      messages: [
+        { role: "user", content: [{ type: "text", text: "Hello" }] },
+        { role: "system", content: "Tool context reminder" },
+      ],
+    });
+
+    expect(result.success).toBe(true);
+    if (result.success) {
+      expect(result.data.messages[1].role).toBe("system");
+    }
+  });
+
   test("returns 400 for missing model", async () => {
     const res = await app.request("/anthropic/v1/messages", {
       method: "POST",
git clone https://git.99rst.org/PROJECT