From b75d9c7566a43c71bc22a12ef1cabf25e84a7f9a Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 18:59:47 +0000 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=A4=96=20fix:=20Handle=20emojis=20wit?= =?UTF-8?q?h=20variation=20selectors=20in=20status=5Fset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use Intl.Segmenter to count grapheme clusters instead of code points - Properly handles variation selectors (like ✏️), skin tones, and complex emojis - Remove text-sm class from error message in StatusSetToolCall - Add tests for emojis with variation selectors and skin tone modifiers --- src/components/tools/StatusSetToolCall.tsx | 2 +- src/services/tools/status_set.test.ts | 29 ++++++++++++++++++++++ src/services/tools/status_set.ts | 18 +++++++++----- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/components/tools/StatusSetToolCall.tsx b/src/components/tools/StatusSetToolCall.tsx index 803362a58..fc9942d46 100644 --- a/src/components/tools/StatusSetToolCall.tsx +++ b/src/components/tools/StatusSetToolCall.tsx @@ -31,7 +31,7 @@ export const StatusSetToolCall: React.FC = ({ status_set {args.message} - {errorMessage && ({errorMessage})} + {errorMessage && ({errorMessage})} {statusDisplay} diff --git a/src/services/tools/status_set.test.ts b/src/services/tools/status_set.test.ts index 5625c7b13..36e6d7daf 100644 --- a/src/services/tools/status_set.test.ts +++ b/src/services/tools/status_set.test.ts @@ -31,6 +31,35 @@ describe("status_set tool validation", () => { } }); + it("should accept emojis with variation selectors", async () => { + const tool = createStatusSetTool(mockConfig); + + // Emojis with variation selectors (U+FE0F) + const emojis = ["✏️", "✅", "➡️", "☀️"]; + for (const emoji of emojis) { + const result = (await tool.execute!({ emoji, message: "Test" }, mockToolCallOptions)) as { + success: boolean; + emoji: string; + message: string; + }; + expect(result).toEqual({ success: true, emoji, message: "Test" }); + } + }); + + it("should accept emojis with skin tone modifiers", async () => { + const tool = createStatusSetTool(mockConfig); + + const emojis = ["👋🏻", "👋🏽", "👋🏿"]; + for (const emoji of emojis) { + const result = (await tool.execute!({ emoji, message: "Test" }, mockToolCallOptions)) as { + success: boolean; + emoji: string; + message: string; + }; + expect(result).toEqual({ success: true, emoji, message: "Test" }); + } + }); + it("should reject multiple emojis", async () => { const tool = createStatusSetTool(mockConfig); diff --git a/src/services/tools/status_set.ts b/src/services/tools/status_set.ts index ee73d90d9..04f13fc9f 100644 --- a/src/services/tools/status_set.ts +++ b/src/services/tools/status_set.ts @@ -18,18 +18,24 @@ export type StatusSetToolResult = /** * Validates that a string is a single emoji character - * Uses Unicode property escapes to match emoji characters + * Uses Intl.Segmenter to count grapheme clusters (handles variation selectors, skin tones, etc.) */ function isValidEmoji(str: string): boolean { - // Check if string contains exactly one character (handles multi-byte emojis) - const chars = [...str]; - if (chars.length !== 1) { + if (!str) return false; + + // Use Intl.Segmenter to count grapheme clusters (what users perceive as single characters) + // This properly handles emojis with variation selectors (like ✏️), skin tones, flags, etc. + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + const segments = [...segmenter.segment(str)]; + + // Must be exactly one grapheme cluster + if (segments.length !== 1) { return false; } // Check if it's an emoji using Unicode properties - const emojiRegex = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]$/u; - return emojiRegex.test(str); + const emojiRegex = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]/u; + return emojiRegex.test(segments[0].segment); } /** From 31f8e7902f62f8cbe7758c7b17e3cbd2c9d7fd29 Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 19:03:52 +0000 Subject: [PATCH 2/7] Add ES2022.Intl lib for Intl.Segmenter type support --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index b887b01f6..33d44c08e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "lib": ["ES2023", "DOM"], + "lib": ["ES2023", "DOM", "ES2022.Intl"], "module": "ESNext", "moduleResolution": "node", "jsx": "react-jsx", From b4eb794934296b2aff5cd3b216c5df22118a747e Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 19:04:39 +0000 Subject: [PATCH 3/7] Add ES2022.Intl lib to Jest TypeScript config --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 3502aa5d7..74eda6695 100644 --- a/jest.config.js +++ b/jest.config.js @@ -19,7 +19,7 @@ module.exports = { { tsconfig: { target: "ES2020", - lib: ["ES2020", "DOM"], + lib: ["ES2020", "DOM", "ES2022.Intl"], esModuleInterop: true, allowSyntheticDefaultImports: true, }, From a528519e6f8641b8c9c8ac34a41a6c6082e94a50 Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 19:07:39 +0000 Subject: [PATCH 4/7] Remove specific emoji examples from status_set description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give agents more freedom in choosing emojis by removing prescriptive examples like '🔍 Analyzing code'. Keep outcome examples (Success/Failure/Warning) but remove their emoji prefixes. --- src/utils/tools/toolDefinitions.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/tools/toolDefinitions.ts b/src/utils/tools/toolDefinitions.ts index 24304c2fa..99e987bed 100644 --- a/src/utils/tools/toolDefinitions.ts +++ b/src/utils/tools/toolDefinitions.ts @@ -187,11 +187,10 @@ export const TOOL_DEFINITIONS = { "The emoji appears left of the streaming indicator, and the message shows on hover. " + "IMPORTANT: Always set a status at the start of each response and update it as your work progresses. " + "Set a final status before finishing your response that reflects the outcome: " + - "'✅ PR checks pass and ready to merge' (success), " + - "'❌ CreateWorkspace Tests failed' (failure), " + - "'⚠️ Encountered serious issue with design' (warning/blocker). " + - "The status is cleared at the start of each new response, so you must set it again. " + - "Use this to communicate ongoing activities (e.g., '🔍 Analyzing code', '📝 Writing tests', '🔧 Refactoring logic').", + "Success: 'PR checks pass and ready to merge', " + + "Failure: 'CreateWorkspace Tests failed', " + + "Warning: 'Encountered serious issue with design'. " + + "The status is cleared at the start of each new response, so you must set it again.", schema: z .object({ emoji: z.string().describe("A single emoji character representing the current activity"), From 8a35c675b09b156ebf38371c9ef234d56f366829 Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 19:10:48 +0000 Subject: [PATCH 5/7] Make status_set description more generic Remove prescriptive emoji and message examples to give agents more freedom. Keep guidance about setting final status but without specific format examples. Clarify that status clears on new user message, not stream start. --- src/utils/tools/toolDefinitions.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/utils/tools/toolDefinitions.ts b/src/utils/tools/toolDefinitions.ts index 99e987bed..0a191b464 100644 --- a/src/utils/tools/toolDefinitions.ts +++ b/src/utils/tools/toolDefinitions.ts @@ -186,11 +186,8 @@ export const TOOL_DEFINITIONS = { "Set a status indicator to show what the agent is currently doing. " + "The emoji appears left of the streaming indicator, and the message shows on hover. " + "IMPORTANT: Always set a status at the start of each response and update it as your work progresses. " + - "Set a final status before finishing your response that reflects the outcome: " + - "Success: 'PR checks pass and ready to merge', " + - "Failure: 'CreateWorkspace Tests failed', " + - "Warning: 'Encountered serious issue with design'. " + - "The status is cleared at the start of each new response, so you must set it again.", + "The status is cleared when a new user message comes in, so you must set it again for each response. " + + "Use this to communicate ongoing activities and set a final status before completing that reflects the outcome.", schema: z .object({ emoji: z.string().describe("A single emoji character representing the current activity"), From 250586a3d5320c1f900eb975b599427e03aaddfe Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 19:14:08 +0000 Subject: [PATCH 6/7] Clarify that status_set displays immediately - Add to tool description that status is set IMMEDIATELY - Add code comment explaining this prevents premature success status (e.g., saying 'PR checks passed' when checks are still running) --- src/services/tools/status_set.ts | 6 ++++++ src/utils/tools/toolDefinitions.ts | 1 + 2 files changed, 7 insertions(+) diff --git a/src/services/tools/status_set.ts b/src/services/tools/status_set.ts index 04f13fc9f..14a499402 100644 --- a/src/services/tools/status_set.ts +++ b/src/services/tools/status_set.ts @@ -41,6 +41,12 @@ function isValidEmoji(str: string): boolean { /** * Status set tool factory for AI assistant * Creates a tool that allows the AI to set status indicator showing current activity + * + * The status is displayed IMMEDIATELY when this tool is called, even before other + * tool calls complete. This prevents agents from prematurely declaring success + * (e.g., "PR checks passed") when operations are still pending. Agents should only + * set success status after confirming the outcome of long-running operations. + * * @param config Required configuration (not used for this tool, but required by interface) */ export const createStatusSetTool: ToolFactory = () => { diff --git a/src/utils/tools/toolDefinitions.ts b/src/utils/tools/toolDefinitions.ts index 0a191b464..58fb11f81 100644 --- a/src/utils/tools/toolDefinitions.ts +++ b/src/utils/tools/toolDefinitions.ts @@ -185,6 +185,7 @@ export const TOOL_DEFINITIONS = { description: "Set a status indicator to show what the agent is currently doing. " + "The emoji appears left of the streaming indicator, and the message shows on hover. " + + "The status is set IMMEDIATELY when this tool is called, even before other tool calls complete. " + "IMPORTANT: Always set a status at the start of each response and update it as your work progresses. " + "The status is cleared when a new user message comes in, so you must set it again for each response. " + "Use this to communicate ongoing activities and set a final status before completing that reflects the outcome.", From ba14cf74ae9bf0bf4b0f3963f09945ab20562be1 Mon Sep 17 00:00:00 2001 From: Ammar Date: Tue, 28 Oct 2025 19:19:02 +0000 Subject: [PATCH 7/7] Retry CI