Эх сурвалжийг харах

fix(tui): route casual chat away from status fallback

Ma 7 сар өмнө
parent
commit
098ea4a2c2

+ 2 - 0
packages/cli/src/__tests__/tui-input-chrome.test.ts

@@ -16,6 +16,8 @@ describe("tui input chrome", () => {
     expect(promptPrefix).toContain("│");
     expect(promptPrefix).toContain("│");
     expect(promptPrefix).toContain("❯");
     expect(promptPrefix).toContain("❯");
     expect(chrome.outerGapLines).toBeGreaterThan(0);
     expect(chrome.outerGapLines).toBeGreaterThan(0);
+    expect(chrome.promptLiftRows).toBeGreaterThan(0);
+    expect(chrome.settleRowsAfterSubmit).toBeGreaterThan(0);
   });
   });
 
 
   it("clamps the shell width instead of spanning edge-to-edge", () => {
   it("clamps the shell width instead of spanning edge-to-edge", () => {

+ 1 - 0
packages/core/src/__tests__/interaction-models.test.ts

@@ -20,6 +20,7 @@ describe("interaction models", () => {
 
 
   it("parses supported interaction intents", () => {
   it("parses supported interaction intents", () => {
     expect(InteractionIntentTypeSchema.parse("create_book")).toBe("create_book");
     expect(InteractionIntentTypeSchema.parse("create_book")).toBe("create_book");
+    expect(InteractionIntentTypeSchema.parse("chat")).toBe("chat");
     expect(InteractionIntentTypeSchema.parse("write_next")).toBe("write_next");
     expect(InteractionIntentTypeSchema.parse("write_next")).toBe("write_next");
     expect(InteractionIntentTypeSchema.parse("rewrite_chapter")).toBe("rewrite_chapter");
     expect(InteractionIntentTypeSchema.parse("rewrite_chapter")).toBe("rewrite_chapter");
     expect(InteractionIntentTypeSchema.parse("explain_failure")).toBe("explain_failure");
     expect(InteractionIntentTypeSchema.parse("explain_failure")).toBe("explain_failure");

+ 21 - 0
packages/core/src/__tests__/interaction-nl-router.test.ts

@@ -58,6 +58,19 @@ describe("interaction natural-language router", () => {
     });
     });
   });
   });
 
 
+  it("maps greetings to chat instead of status explanation", () => {
+    expect(routeNaturalLanguageIntent("hi", { activeBookId: "harbor" })).toEqual({
+      intent: "chat",
+      bookId: "harbor",
+      instruction: "hi",
+    });
+    expect(routeNaturalLanguageIntent("你好", { activeBookId: "harbor" })).toEqual({
+      intent: "chat",
+      bookId: "harbor",
+      instruction: "你好",
+    });
+  });
+
   it("maps mode switch requests to switch_mode", () => {
   it("maps mode switch requests to switch_mode", () => {
     expect(routeNaturalLanguageIntent("切换到全自动", { activeBookId: "harbor" })).toEqual({
     expect(routeNaturalLanguageIntent("切换到全自动", { activeBookId: "harbor" })).toEqual({
       intent: "switch_mode",
       intent: "switch_mode",
@@ -138,4 +151,12 @@ describe("interaction natural-language router", () => {
       newValue: "Lin Yan",
       newValue: "Lin Yan",
     });
     });
   });
   });
+
+  it("falls back to chat for unmatched freeform input", () => {
+    expect(routeNaturalLanguageIntent("没有动效没有回答", { activeBookId: "harbor" })).toEqual({
+      intent: "chat",
+      bookId: "harbor",
+      instruction: "没有动效没有回答",
+    });
+  });
 });
 });

+ 26 - 0
packages/core/src/__tests__/interaction-runtime.test.ts

@@ -192,6 +192,32 @@ describe("interaction runtime", () => {
     );
     );
   });
   });
 
 
+  it("answers chat-style requests without forcing a status summary", async () => {
+    const result = await runInteractionRequest({
+      session: InteractionSessionSchema.parse({
+        sessionId: "session-chat",
+        projectRoot: "/tmp/project",
+        activeBookId: "harbor",
+        automationMode: "semi",
+        messages: [],
+        events: [],
+      }),
+      request: {
+        intent: "chat",
+        bookId: "harbor",
+        instruction: "hi",
+      },
+      tools: makeTools(),
+    });
+
+    expect(result.responseText).toContain("harbor");
+    expect(result.responseText).not.toContain("Current status");
+    expect(result.session.events.map((event) => event.kind)).toEqual([
+      "task.started",
+      "task.completed",
+    ]);
+  });
+
   it("routes edit_truth to the truth-file updater", async () => {
   it("routes edit_truth to the truth-file updater", async () => {
     const writeTruthFile = vi.fn(async () => ({ ok: true }));
     const writeTruthFile = vi.fn(async () => ({ ok: true }));
 
 

+ 1 - 0
packages/core/src/interaction/intents.ts

@@ -16,6 +16,7 @@ export const InteractionIntentTypeSchema = z.enum([
   "rename_entity",
   "rename_entity",
   "update_focus",
   "update_focus",
   "update_author_intent",
   "update_author_intent",
+  "chat",
   "explain_status",
   "explain_status",
   "explain_failure",
   "explain_failure",
   "export_book",
   "export_book",

+ 10 - 2
packages/core/src/interaction/nl-router.ts

@@ -12,6 +12,14 @@ export function routeNaturalLanguageIntent(
   const lower = trimmed.toLowerCase();
   const lower = trimmed.toLowerCase();
   const bookId = context.activeBookId;
   const bookId = context.activeBookId;
 
 
+  if (/^(hi|hello|hey|你好|嗨|哈喽)$/i.test(trimmed)) {
+    return {
+      intent: "chat",
+      ...(bookId ? { bookId } : {}),
+      instruction: trimmed,
+    };
+  }
+
   if (/^(continue|继续|继续写|写下一章|write next)$/i.test(trimmed)) {
   if (/^(continue|继续|继续写|写下一章|write next)$/i.test(trimmed)) {
     return {
     return {
       intent: "write_next",
       intent: "write_next",
@@ -190,8 +198,8 @@ export function routeNaturalLanguageIntent(
   }
   }
 
 
   return {
   return {
-    intent: "explain_status",
+    intent: "chat",
     ...(bookId ? { bookId } : {}),
     ...(bookId ? { bookId } : {}),
-    instruction: lower,
+    instruction: trimmed,
   };
   };
 }
 }

+ 16 - 0
packages/core/src/interaction/runtime.ts

@@ -542,6 +542,22 @@ export async function runInteractionRequest(params: {
         responseText: `Resumed ${bookId ?? "current book"}.`,
         responseText: `Resumed ${bookId ?? "current book"}.`,
       };
       };
     }
     }
+    case "chat": {
+      const bookId = request.bookId ?? session.activeBookId;
+      const prompt = request.instruction?.trim().toLowerCase() ?? "";
+      const responseText = /^(hi|hello|hey|你好|嗨|哈喽)$/i.test(prompt)
+        ? (bookId
+            ? `Hi. Active book is ${bookId}. Ask me to continue, revise a chapter, or explain what is blocked.`
+            : "Hi. No active book yet. Open a book, list books, or tell me what you want to write.")
+        : (bookId
+            ? `I’m here. Active book is ${bookId}. You can ask me to continue, revise a chapter, rewrite, change focus, or inspect why the pipeline stopped.`
+            : "I’m here. No active book is bound yet. Open a book, list books, or describe what you want to write.");
+      const completed = markCompleted(session);
+      return {
+        session: addEvent(completed, "task.completed", "completed", responseText),
+        responseText,
+      };
+    }
     case "explain_status":
     case "explain_status":
     case "explain_failure": {
     case "explain_failure": {
       const bookId = request.bookId ?? session.activeBookId;
       const bookId = request.bookId ?? session.activeBookId;