From 702e44f16ab12021e48b1c2d04c0e975e6308e94 Mon Sep 17 00:00:00 2001 From: isamu Date: Tue, 20 Jan 2026 12:36:12 +0900 Subject: [PATCH] Refactor to use context.app API instead of global state - Remove roles.ts and DEFAULT_ROLES (app should own roles) - Get roles from context.app.getRoles() at execute time - Call context.app.switchRole() instead of globalThis.switchRole - Add createToolDefinition(roles) for dynamic enum generation - Keep fallback TOOL_DEFINITION for backward compatibility Co-Authored-By: Claude Opus 4.5 --- package.json | 2 +- src/core/definition.ts | 31 +++++++++++++++++----- src/core/index.ts | 3 --- src/core/plugin.ts | 59 ++++++++++++++++++++++++++++++------------ src/core/roles.ts | 32 ----------------------- src/vue/index.ts | 4 --- yarn.lock | 8 +++--- 7 files changed, 71 insertions(+), 68 deletions(-) delete mode 100644 src/core/roles.ts diff --git a/package.json b/package.json index d2d87d9..607ea6f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "eslint": "^9.39.2", "eslint-plugin-vue": "^10.6.2", "globals": "^17.0.0", - "gui-chat-protocol": "^0.0.1", + "gui-chat-protocol": "^0.0.2", "tailwindcss": "^4.1.18", "typescript": "~5.9.3", "vite": "^7.3.1", diff --git a/src/core/definition.ts b/src/core/definition.ts index 3ad5adb..1c4111c 100644 --- a/src/core/definition.ts +++ b/src/core/definition.ts @@ -3,16 +3,15 @@ */ import type { ToolDefinition } from "gui-chat-protocol"; -import { getRoles } from "./roles"; +import type { Role } from "./types"; export const TOOL_NAME = "switchRole"; /** - * Create tool definition with current roles - * This needs to be called dynamically since roles can be configured + * Create tool definition with specific roles + * @param roles - Array of available roles */ -export function createToolDefinition(): ToolDefinition { - const roles = getRoles(); +export function createToolDefinition(roles: Role[]): ToolDefinition { const roleIds = roles.map((r) => r.id); const roleOptionsDescription = roles .map((r) => `'${r.id}' (${r.name})`) @@ -36,7 +35,25 @@ export function createToolDefinition(): ToolDefinition { }; } -// Default tool definition using default roles -export const TOOL_DEFINITION: ToolDefinition = createToolDefinition(); +/** + * Default tool definition (used when roles are not provided) + * Apps should use createToolDefinition() with their roles for better LLM guidance + */ +export const TOOL_DEFINITION: ToolDefinition = { + type: "function", + name: TOOL_NAME, + description: + "Switch the system prompt role and reconnect to the LLM. This changes the AI's personality and behavior.", + parameters: { + type: "object", + properties: { + role: { + type: "string", + description: "The role ID to switch to", + }, + }, + required: ["role"], + }, +}; export const SYSTEM_PROMPT = `When users ask to change the role, personality, or behavior of the AI (e.g., 'switch to tutor role', 'change to listener role', 'be a teacher'), use the ${TOOL_NAME} function. Note that switching roles will disconnect and reconnect the conversation.`; diff --git a/src/core/index.ts b/src/core/index.ts index d47e35a..429761c 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -5,9 +5,6 @@ // Export plugin-specific types export type { Role, SwitchRoleArgs, SwitchRoleJsonData } from "./types"; -// Export role configuration -export { DEFAULT_ROLES, setRoles, getRoles, getRoleById } from "./roles"; - // Export plugin utilities export { TOOL_NAME, diff --git a/src/core/plugin.ts b/src/core/plugin.ts index 4b32fc6..c9e437d 100644 --- a/src/core/plugin.ts +++ b/src/core/plugin.ts @@ -3,29 +3,61 @@ */ import type { ToolPluginCore, ToolContext, ToolResult } from "gui-chat-protocol"; -import type { SwitchRoleArgs, SwitchRoleJsonData } from "./types"; +import type { Role, SwitchRoleArgs, SwitchRoleJsonData } from "./types"; import { TOOL_DEFINITION, SYSTEM_PROMPT } from "./definition"; -import { getRoles, getRoleById } from "./roles"; // Re-export for convenience export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, createToolDefinition } from "./definition"; -export { DEFAULT_ROLES, setRoles, getRoles, getRoleById } from "./roles"; + +/** + * Extended ToolContext with role-related app functions + */ +interface SwitchRoleToolContext extends ToolContext { + app?: ToolContext["app"] & { + getRoles?: () => Role[]; + switchRole?: (roleId: string) => void; + }; +} + +/** + * Get roles from context.app (provided by the host app) + */ +function getRolesFromContext(context: SwitchRoleToolContext): Role[] { + if (typeof context.app?.getRoles === "function") { + return context.app.getRoles(); + } + console.warn( + "switchRole: context.app.getRoles() not available, returning empty roles", + ); + return []; +} + +/** + * Get role by ID from context + */ +function getRoleByIdFromContext( + context: SwitchRoleToolContext, + id: string, +): Role | undefined { + const roles = getRolesFromContext(context); + return roles.find((role) => role.id === id); +} /** * Execute the switchRole function * Triggers a role switch via the app layer */ export const executeSwitchRole = async ( - _context: ToolContext, + context: SwitchRoleToolContext, args: SwitchRoleArgs, ): Promise> => { const { role } = args; - const roles = getRoles(); + const roles = getRolesFromContext(context); const availableRolesSummary = roles.map((r) => `${r.id} (${r.name})`).join(", "); try { // Validate role - const validRole = getRoleById(role); + const validRole = getRoleByIdFromContext(context, role); if (!validRole) { return { message: `Invalid role: ${role}`, @@ -38,21 +70,14 @@ export const executeSwitchRole = async ( }; } - // Call switchRole asynchronously (don't await) - const globalObject = globalThis as typeof globalThis & { - switchRole?: (selectedRole: string) => void; - }; - - if ( - typeof window !== "undefined" && - typeof globalObject.switchRole === "function" - ) { + // Call switchRole via context.app (provided by the host app) + if (typeof context.app?.switchRole === "function") { // Fire and forget - this will disconnect and reconnect setTimeout(() => { - globalObject.switchRole?.(role); + context.app?.switchRole?.(role); }, 0); } else { - console.error("switchRole function not found on window object"); + console.error("switchRole: context.app.switchRole() not available"); return { message: "Failed to switch role: switchRole API not available", jsonData: { diff --git a/src/core/roles.ts b/src/core/roles.ts deleted file mode 100644 index ae3adaf..0000000 --- a/src/core/roles.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Default roles configuration - * These can be overridden by the app layer - */ - -import type { Role } from "./types"; - -export const DEFAULT_ROLES: Role[] = [ - { id: "general", name: "General" }, - { id: "tutor", name: "Tutor" }, - { id: "listener", name: "Listener" }, - { id: "receptionist", name: "Receptionist" }, - { id: "tourPlanner", name: "Trip Planner" }, - { id: "recipeGuide", name: "Recipe Guide" }, - { id: "game", name: "Game" }, - { id: "office", name: "Office" }, -]; - -// Allow roles to be configured at runtime -let configuredRoles: Role[] = DEFAULT_ROLES; - -export function setRoles(roles: Role[]): void { - configuredRoles = roles; -} - -export function getRoles(): Role[] { - return configuredRoles; -} - -export function getRoleById(id: string): Role | undefined { - return configuredRoles.find((role) => role.id === id); -} diff --git a/src/vue/index.ts b/src/vue/index.ts index 872b8f2..95a643a 100644 --- a/src/vue/index.ts +++ b/src/vue/index.ts @@ -26,10 +26,6 @@ export { createToolDefinition, executeSwitchRole, pluginCore, - DEFAULT_ROLES, - setRoles, - getRoles, - getRoleById, } from "../core/plugin"; export { Preview }; diff --git a/yarn.lock b/yarn.lock index ac7548e..1b22b0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1145,10 +1145,10 @@ graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -gui-chat-protocol@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/gui-chat-protocol/-/gui-chat-protocol-0.0.1.tgz#d0770d03a6669b075473a0d8753ca9506f718dcd" - integrity sha512-FStQbzQ7gvjDugWAmf4IOsvhqxe8/DUdA9VK9PgXwi44J2lZyEd1ww1bhThoHXpXT4djFpayQ2uMO1YMtQhCqA== +gui-chat-protocol@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/gui-chat-protocol/-/gui-chat-protocol-0.0.2.tgz#96ce2b565dcc3908a8cb0aa024be842ee6513391" + integrity sha512-Hn/+iwbvbYF+HL8siDKCRxUYZBYT6FyhiMa/kL4EF0BCoF55e65yAtpZdAxUBebfT5VXZvgp+/DCIC3i0RPuLw== has-flag@^4.0.0: version "4.0.0"