Improve Tool Mapping - #72
Conversation
…e for unknowns Expands TOOL_TO_MODULE with explicit entries for sessions_*, memory_*, agent_*, and update_plan so these tools resolve to meaningful module/method pairs instead of falling through to the Unknown default. Adds deriveModule() and deriveMethod() for any unmapped tool names, splitting on underscores and camelCase to produce a reasonable module and method rather than logging Unknown everywhere.
Greptile SummaryThis PR expands
Confidence Score: 3/5The derive fallbacks feed directly into policy-enforcement calls; a capitalization bug in deriveMethod can cause rule mismatches for any unmapped camelCase tool name. The static TOOL_TO_MODULE additions are correct and safe. The concern is in deriveMethod: splitting a camelCase name like memoryRead yields the method as Read instead of read. If the policy engine or scoreIrreversibility does case-sensitive matching, the wrong rule fires for any unmapped camelCase tool. A one-character fix resolves it, but as written the security-critical evaluation path has a latent case-mismatch defect. src/plugin/tool-interceptor.ts — specifically the deriveMethod helper and the update_plan mapping. Important Files Changed
Reviews (1): Last reviewed commit: "feat: add tool mappings for session/memo..." | Re-trigger Greptile |
| return parts | ||
| .slice(1) | ||
| .map((p, i) => (i === 0 ? p : p.charAt(0).toUpperCase() + p.slice(1))) | ||
| .join(''); |
There was a problem hiding this comment.
deriveMethod emits a capital-first-letter method name for camelCase inputs. The regex /(?=[A-Z])/ splits before each uppercase letter but does not lowercase it, so the first element returned by .slice(1) is handed to the i === 0 branch unchanged. deriveMethod("memoryRead") → ["memory","Read"] → slice(1) = ["Read"] → returns "Read" instead of "read". This method name is passed directly to interceptor.evaluate(moduleName, methodName, ...) for policy enforcement; a case-sensitive policy engine would treat "Read" as an unknown method and apply the wrong (or no) rule.
| return parts | |
| .slice(1) | |
| .map((p, i) => (i === 0 ? p : p.charAt(0).toUpperCase() + p.slice(1))) | |
| .join(''); | |
| return parts | |
| .slice(1) | |
| .map((p, i) => (i === 0 ? p.charAt(0).toLowerCase() + p.slice(1) : p.charAt(0).toUpperCase() + p.slice(1))) | |
| .join(''); |
| */ | ||
| function deriveModule(toolName: string): string { | ||
| const first = toolName.split(/[_\-.]|(?=[A-Z])/)[0]; | ||
| const base = first.replace(/s$/, ''); // plurals: "sessions" → "session" |
There was a problem hiding this comment.
deriveModule over-strips any trailing s, not just genuine plurals. The regex .replace(/s$/, '') fires on words like status → "Statu", access → "Acces", process → "Proces". Any future unmapped tool whose first segment ends in a non-plural s would be routed under a mangled module name, silently producing wrong irreversibility scores and incorrect interceptor.evaluate calls.
| const base = first.replace(/s$/, ''); // plurals: "sessions" → "session" | |
| const base = first.replace(/(?<=.)s$/, ''); // plurals: "sessions" → "session" (avoid "status"→"statu") |
| memory_list: { module: 'Memory', method: 'list' }, | ||
| memory_update: { module: 'Memory', method: 'update' }, | ||
| memory_delete: { module: 'Memory', method: 'delete' }, | ||
| update_plan: { module: 'Memory', method: 'update' }, |
There was a problem hiding this comment.
update_plan routed to Memory.update may trigger unintended memory-risk policies. MemoryRiskForecaster.assess() evaluates the (moduleName, methodName) pair to decide whether to pause and request human approval. Classifying a plan-update as Memory.update means it will be scored against memory-drift and salami-index heuristics designed for raw memory writes, potentially raising false-positive intervention prompts — or, conversely, missing plan-specific risk signals if those are ever added under a Plan module. Is it intentional to map update_plan to Memory.update, or should it have its own Plan module entry to keep memory-risk heuristics scoped correctly?
Expands TOOL_TO_MODULE with explicit entries for sessions_, memory_, agent_*, and update_plan so these tools resolve to meaningful module/method pairs instead of falling through to the Unknown default.
Adds deriveModule() and deriveMethod() for any unmapped tool names, splitting on underscores and camelCase to produce a reasonable module and method rather than logging Unknown everywhere.