

























@@ -0,0 +1,220 @@
1+/**
2+ * helpers.system-prompt-resume.test.ts
3+ *
4+ * Unit-level regression tests for issue #80374 — three of the four code paths
5+ * fixed in `fix(anthropic): pass system prompt on every turn for claude-cli
6+ * backend`. These tests assert the argv-level behavior directly, which is the
7+ * authoritative before/after proof (the matching live test in
8+ * `src/gateway/gateway-cli-backend.system-prompt-resume.live.test.ts` is a
9+ * smoke test only — model context retention can mask the bug at the response
10+ * level).
11+ *
12+ * Two scenarios are exercised for each function:
13+ *
14+ * Legacy "first" behavior — verifies a backend explicitly configured with
15+ * `systemPromptWhen: "first"` still gets the documented old behavior
16+ * (system prompt dropped on resumed turns). Confirms the fix is
17+ * backward-compatible for third-party backends that opt in to that mode.
18+ *
19+ * New "always" behavior — verifies the fix: backends configured with
20+ * `systemPromptWhen: "always"` (which is now the bundled claude-cli
21+ * default) re-emit the system-prompt arg on every resumed turn.
22+ *
23+ * Path coverage:
24+ * Path 1 (extensions/anthropic/cli-backend.ts) — covered by
25+ * `extensions/anthropic/cli-shared.test.ts` and `src/agents/cli-backends.test.ts`.
26+ * Path 2 (src/agents/cli-runner/execute.ts) — implicit: same gate as Path 3,
27+ * and gated by `resolveSystemPromptUsage` which is tested below.
28+ * Path 3 (src/agents/cli-runner/helpers.ts — buildCliArgs) — covered here.
29+ * Path 4 (src/agents/cli-runner/claude-live-session.ts — stripLiveProcessArgs
30+ * via buildClaudeLiveArgs) — covered here.
31+ */
32+import { describe, expect, it } from "vitest";
33+import type { CliBackendConfig } from "../../config/types.js";
34+import { buildClaudeLiveArgs } from "./claude-live-session.js";
35+import { buildCliArgs, resolveSystemPromptUsage } from "./helpers.js";
36+37+// Minimal backend config matching the Anthropic claude-cli backend shape.
38+const CLAUDE_BACKEND_BASE: Pick<
39+CliBackendConfig,
40+| "systemPromptFileArg"
41+| "systemPromptArg"
42+| "systemPromptFileConfigKey"
43+| "systemPromptWhen"
44+| "sessionArg"
45+| "modelArg"
46+| "input"
47+| "output"
48+| "liveSession"
49+> = {
50+systemPromptFileArg: "--append-system-prompt-file",
51+systemPromptArg: undefined,
52+systemPromptFileConfigKey: undefined,
53+systemPromptWhen: "always",
54+sessionArg: "--session-id",
55+modelArg: "--model",
56+input: "stdin",
57+output: "jsonl",
58+liveSession: "claude-stdio",
59+};
60+61+// Backend configured with the legacy systemPromptWhen: "first" mode.
62+// Verifies the fix is backward-compatible for backends that opt in to this.
63+const BACKEND_FIRST: typeof CLAUDE_BACKEND_BASE = {
64+ ...CLAUDE_BACKEND_BASE,
65+systemPromptWhen: "first",
66+};
67+68+// Backend configured with the new systemPromptWhen: "always" default (the
69+// bundled claude-cli default after the fix).
70+const BACKEND_ALWAYS: typeof CLAUDE_BACKEND_BASE = {
71+ ...CLAUDE_BACKEND_BASE,
72+systemPromptWhen: "always",
73+};
74+75+const SYSTEM_PROMPT = "You are a test assistant. Append SYSTEM-PROOF-ACTIVE after every reply.";
76+const PROMPT_FILE = "/tmp/test-system-prompt.txt";
77+78+// ─── resolveSystemPromptUsage ────────────────────────────────────────────────
79+80+describe("resolveSystemPromptUsage — issue #80374", () => {
81+it("legacy 'first': returns null on resumed session (prompt dropped)", () => {
82+const result = resolveSystemPromptUsage({
83+backend: BACKEND_FIRST as CliBackendConfig,
84+isNewSession: false, // resumed
85+systemPrompt: SYSTEM_PROMPT,
86+});
87+expect(result).toBeNull();
88+});
89+90+it("new 'always': returns the prompt on resumed session (issue #80374)", () => {
91+const result = resolveSystemPromptUsage({
92+backend: BACKEND_ALWAYS as CliBackendConfig,
93+isNewSession: false, // resumed
94+systemPrompt: SYSTEM_PROMPT,
95+});
96+expect(result).toBe(SYSTEM_PROMPT);
97+});
98+99+it("returns the prompt on fresh session for both 'first' and 'always'", () => {
100+for (const backend of [BACKEND_FIRST, BACKEND_ALWAYS]) {
101+const result = resolveSystemPromptUsage({
102+backend: backend as CliBackendConfig,
103+isNewSession: true, // fresh
104+systemPrompt: SYSTEM_PROMPT,
105+});
106+expect(result, `systemPromptWhen=${backend.systemPromptWhen}`).toBe(SYSTEM_PROMPT);
107+}
108+});
109+110+it("returns null when systemPromptWhen='never' regardless of session state", () => {
111+for (const isNew of [true, false]) {
112+const result = resolveSystemPromptUsage({
113+backend: { ...BACKEND_ALWAYS, systemPromptWhen: "never" } as CliBackendConfig,
114+isNewSession: isNew,
115+systemPrompt: SYSTEM_PROMPT,
116+});
117+expect(result, `isNew=${String(isNew)}`).toBeNull();
118+}
119+});
120+});
121+122+// ─── buildCliArgs ────────────────────────────────────────────────────────────
123+124+describe("buildCliArgs — issue #80374", () => {
125+const BASE_ARGS = ["-p", "--output-format", "stream-json"];
126+127+it("legacy 'first': omits --append-system-prompt-file on resume", () => {
128+const args = buildCliArgs({
129+backend: BACKEND_FIRST as CliBackendConfig,
130+baseArgs: BASE_ARGS,
131+modelId: "claude-haiku-4-5",
132+sessionId: "test-session-id",
133+systemPrompt: SYSTEM_PROMPT,
134+systemPromptFilePath: PROMPT_FILE,
135+useResume: true,
136+});
137+expect(args).not.toContain("--append-system-prompt-file");
138+expect(args).not.toContain(PROMPT_FILE);
139+});
140+141+it("new 'always': includes --append-system-prompt-file on resume (issue #80374)", () => {
142+const args = buildCliArgs({
143+backend: BACKEND_ALWAYS as CliBackendConfig,
144+baseArgs: BASE_ARGS,
145+modelId: "claude-haiku-4-5",
146+sessionId: "test-session-id",
147+systemPrompt: SYSTEM_PROMPT,
148+systemPromptFilePath: PROMPT_FILE,
149+useResume: true,
150+});
151+expect(args).toContain("--append-system-prompt-file");
152+expect(args).toContain(PROMPT_FILE);
153+});
154+155+it("includes --append-system-prompt-file on fresh session for both 'first' and 'always'", () => {
156+for (const backend of [BACKEND_FIRST, BACKEND_ALWAYS]) {
157+const args = buildCliArgs({
158+backend: backend as CliBackendConfig,
159+baseArgs: BASE_ARGS,
160+modelId: "claude-haiku-4-5",
161+systemPrompt: SYSTEM_PROMPT,
162+systemPromptFilePath: PROMPT_FILE,
163+useResume: false,
164+});
165+expect(
166+args,
167+`systemPromptWhen=${backend.systemPromptWhen} should include flag on fresh session`,
168+).toContain("--append-system-prompt-file");
169+}
170+});
171+});
172+173+// ─── buildClaudeLiveArgs (Path 4: live-stdio strip guard) ───────────────────
174+175+describe("buildClaudeLiveArgs — issue #80374 (live-stdio path)", () => {
176+const ARGS_WITH_SP = [
177+"-p",
178+"--output-format",
179+"stream-json",
180+"--append-system-prompt-file",
181+PROMPT_FILE,
182+];
183+184+it("legacy 'first': strips --append-system-prompt-file on resume", () => {
185+const liveArgs = buildClaudeLiveArgs({
186+args: ARGS_WITH_SP,
187+backend: BACKEND_FIRST as CliBackendConfig,
188+systemPrompt: SYSTEM_PROMPT,
189+useResume: true,
190+});
191+expect(liveArgs).not.toContain("--append-system-prompt-file");
192+expect(liveArgs).not.toContain(PROMPT_FILE);
193+});
194+195+it("new 'always': keeps --append-system-prompt-file on resume (issue #80374)", () => {
196+const liveArgs = buildClaudeLiveArgs({
197+args: ARGS_WITH_SP,
198+backend: BACKEND_ALWAYS as CliBackendConfig,
199+systemPrompt: SYSTEM_PROMPT,
200+useResume: true,
201+});
202+expect(liveArgs).toContain("--append-system-prompt-file");
203+expect(liveArgs).toContain(PROMPT_FILE);
204+});
205+206+it("keeps --append-system-prompt-file when useResume=false (both 'first' and 'always')", () => {
207+for (const backend of [BACKEND_FIRST, BACKEND_ALWAYS]) {
208+const liveArgs = buildClaudeLiveArgs({
209+args: ARGS_WITH_SP,
210+backend: backend as CliBackendConfig,
211+systemPrompt: SYSTEM_PROMPT,
212+useResume: false,
213+});
214+expect(
215+liveArgs,
216+`systemPromptWhen=${backend.systemPromptWhen} fresh session should keep flag`,
217+).toContain("--append-system-prompt-file");
218+}
219+});
220+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。