diff --git a/core/promptFiles/parsePromptFile.ts b/core/promptFiles/parsePromptFile.ts
index 4b6cf6917b8..06eb0458c76 100644
--- a/core/promptFiles/parsePromptFile.ts
+++ b/core/promptFiles/parsePromptFile.ts
@@ -15,7 +15,9 @@ export function parsePromptFile(path: string, content: string) {
const version = preamble.version ?? 2;
let systemMessage: string | undefined = undefined;
- if (prompt.includes("")) {
+ // Require both tags: a `` with no matching `` would make
+ // `split("")[1]` undefined and crash on `.trim()`.
+ if (prompt.includes("") && prompt.includes("")) {
systemMessage = prompt.split("")[1].split("")[0].trim();
prompt = prompt.split("")[1].trim();
}
diff --git a/core/promptFiles/parsePromptFile.vitest.ts b/core/promptFiles/parsePromptFile.vitest.ts
new file mode 100644
index 00000000000..dd8f508d090
--- /dev/null
+++ b/core/promptFiles/parsePromptFile.vitest.ts
@@ -0,0 +1,32 @@
+import { describe, expect, it } from "vitest";
+
+import { parsePromptFile } from "./parsePromptFile.js";
+
+describe("parsePromptFile", () => {
+ it("extracts the system message when both tags are present", () => {
+ const result = parsePromptFile(
+ "greeting.prompt",
+ "You are helpful\nSummarize the file",
+ );
+
+ expect(result.systemMessage).toBe("You are helpful");
+ expect(result.prompt).toBe("Summarize the file");
+ });
+
+ it("does not throw when has no closing tag", () => {
+ const content = "You forgot to close the tag\nSummarize the file";
+
+ expect(() => parsePromptFile("broken.prompt", content)).not.toThrow();
+
+ const result = parsePromptFile("broken.prompt", content);
+ expect(result.systemMessage).toBeUndefined();
+ expect(result.prompt).toBe(content);
+ });
+
+ it("leaves the prompt untouched when there is no system block", () => {
+ const result = parsePromptFile("plain.prompt", "Just a prompt body");
+
+ expect(result.systemMessage).toBeUndefined();
+ expect(result.prompt).toBe("Just a prompt body");
+ });
+});