Skip to content

Commit 037db03

Browse files
committed
fix: widen handoff detection context window beyond single-line sentence extraction
Remove \n from sentence boundary regexes in extractSentence so markdown newlines no longer truncate the context window. This fixes handoff chip detection when specialist names and intent phrases span multiple lines (e.g., markdown headings followed by body text). Caps extracted sentences at 500 chars to prevent runaway matching.
1 parent ee313b0 commit 037db03

2 files changed

Lines changed: 50 additions & 4 deletions

File tree

apps/desktop/src/features/chat/lib/handoff-detector.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,28 @@ function generateDeterministicId(specialistId: string, reason: string): string {
6565
}
6666

6767
function extractSentence(text: string, matchIndex: number): string {
68-
// Find sentence boundaries around the match position
68+
// Find sentence boundaries around the match position.
69+
// Use only . ! ? as sentence terminators — NOT \n, which is a
70+
// formatting character in markdown and would break cross-line detection.
6971
const beforeText = text.slice(0, matchIndex);
7072
const afterText = text.slice(matchIndex);
7173

7274
// Find sentence start (look backwards for . ! ? or start of text)
73-
const sentenceStartMatch = beforeText.match(/(?:.*[.!?\n]\s*)([^.!?\n]*)$/);
75+
const sentenceStartMatch = beforeText.match(/(?:.*[.!?]\s*)([^.!?]*)$/);
7476
const sentenceStart = sentenceStartMatch
7577
? matchIndex - sentenceStartMatch[1].length
7678
: 0;
7779

7880
// Find sentence end (look forwards for . ! ? or end of text)
79-
const sentenceEndMatch = afterText.match(/^[^.!?\n]*[.!?\n]?/);
81+
const sentenceEndMatch = afterText.match(/^[^.!?]*[.!?]?/);
8082
const sentenceEnd = sentenceEndMatch
8183
? matchIndex + sentenceEndMatch[0].length
8284
: text.length;
8385

84-
return text.slice(sentenceStart, sentenceEnd).trim();
86+
// Cap at 500 characters to avoid matching the entire message
87+
const MAX_SENTENCE_LEN = 500;
88+
const raw = text.slice(sentenceStart, sentenceEnd).trim();
89+
return raw.length > MAX_SENTENCE_LEN ? raw.slice(0, MAX_SENTENCE_LEN) : raw;
8590
}
8691

8792
function hasHandoffIntent(sentence: string): boolean {

test/features/chat/lib/handoff-detector.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,47 @@ describe("detectHandoffSuggestions", () => {
159159
});
160160
});
161161

162+
describe("markdown-formatted text with cross-line detection", () => {
163+
it("detects handoff when specialist name is on a heading line and intent phrase on next line", () => {
164+
const text =
165+
"## For the Positioning Agent\n**The Positioning Agent could help sharpen messaging based on these gaps.**";
166+
const result = detectHandoffSuggestions(text);
167+
expect(result.length).toBeGreaterThanOrEqual(1);
168+
expect(result[0].specialistId).toBe("positioning");
169+
expect(result[0].reason).toContain("could help");
170+
});
171+
172+
it("detects handoff across newlines for Content Agent", () => {
173+
const text =
174+
"## For the Content Agent\n**The Content Agent could help turn these angles into blog outlines.**";
175+
const result = detectHandoffSuggestions(text);
176+
expect(result.length).toBeGreaterThanOrEqual(1);
177+
expect(result[0].specialistId).toBe("content");
178+
});
179+
180+
it("detects multiple handoffs in markdown-formatted response spanning lines", () => {
181+
const text =
182+
"## Next Steps\n\n" +
183+
"### For the Positioning Agent\n" +
184+
"**The Positioning Agent could help sharpen messaging based on these gaps.**\n\n" +
185+
"### For the Content Agent\n" +
186+
"**The Content Agent could help turn these angles into blog posts.**";
187+
const result = detectHandoffSuggestions(text);
188+
expect(result.length).toBe(2);
189+
const ids = result.map((s) => s.specialistId);
190+
expect(ids).toContain("positioning");
191+
expect(ids).toContain("content");
192+
});
193+
194+
it("still detects single-line sentences after fix", () => {
195+
const result = detectHandoffSuggestions(
196+
"The Positioning Agent could help with messaging.",
197+
);
198+
expect(result.length).toBeGreaterThanOrEqual(1);
199+
expect(result[0].specialistId).toBe("positioning");
200+
});
201+
});
202+
162203
describe("case insensitivity", () => {
163204
it("handles varied capitalization of specialist names", () => {
164205
const result = detectHandoffSuggestions(

0 commit comments

Comments
 (0)