feat: implement tier 1 quick wins (tool groups, typing, pruning, verbose, think)
Five additive features with no breaking changes: - Tool groups: group:fs, group:runtime, group:web, group:memory syntactic sugar for allow/deny lists in tool policy config - Typing indicators: Discord sendTyping() and WhatsApp sendStateTyping() on message receipt for better UX feedback - Session pruning: TTL-based auto-cleanup via sessions.ttl config with hourly daemon timer and SQLite GROUP BY pruning - /verbose command: TUI command parser toggle for raw streaming display - !!think prefix: per-message extended thinking mode wired through Anthropic (budget_tokens), OpenAI/GitHub (reasoning_effort), and Gemini (thinkingConfig) providers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+32
-13
@@ -45,6 +45,21 @@ const PROFILE_TOOLS: Record<ToolProfile, Set<string>> = {
|
||||
full: new Set(), // Special: matches everything
|
||||
};
|
||||
|
||||
// ── Tool groups ─────────────────────────────────────────────────────
|
||||
|
||||
/** Named groups for use in allow/deny lists (e.g. 'group:fs'). */
|
||||
export const TOOL_GROUPS: Record<string, string[]> = {
|
||||
'group:fs': ['file.read', 'file.write', 'file.edit', 'file.list'],
|
||||
'group:runtime': ['shell.exec', 'process.start', 'process.output', 'process.status', 'process.kill', 'process.list'],
|
||||
'group:web': ['web.fetch', 'web.search', 'browser.navigate', 'browser.screenshot', 'browser.click', 'browser.type', 'browser.content', 'browser.eval'],
|
||||
'group:memory': ['memory.read', 'memory.write', 'memory.search'],
|
||||
};
|
||||
|
||||
/** Expand group references in a list of tool names/patterns. */
|
||||
function expandGroups(names: string[]): string[] {
|
||||
return names.flatMap(n => TOOL_GROUPS[n] ?? [n]);
|
||||
}
|
||||
|
||||
// ── Glob matching ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -122,19 +137,21 @@ export class ToolPolicy {
|
||||
// Step 1: Start from global profile
|
||||
let allowed = this.applyProfile(this.config.profile, allToolNames);
|
||||
|
||||
// Step 2: Apply global allow (adds tools)
|
||||
if (this.config.allow.length > 0) {
|
||||
// Step 2: Apply global allow (adds tools) — expand groups first
|
||||
const globalAllow = expandGroups(this.config.allow);
|
||||
if (globalAllow.length > 0) {
|
||||
for (const name of allToolNames) {
|
||||
if (matchesAnyPattern(name, this.config.allow)) {
|
||||
if (matchesAnyPattern(name, globalAllow)) {
|
||||
allowed.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Apply global deny (removes tools)
|
||||
if (this.config.deny.length > 0) {
|
||||
// Step 3: Apply global deny (removes tools) — expand groups first
|
||||
const globalDeny = expandGroups(this.config.deny);
|
||||
if (globalDeny.length > 0) {
|
||||
allowed = new Set(
|
||||
[...allowed].filter(name => !matchesAnyPattern(name, this.config.deny)),
|
||||
[...allowed].filter(name => !matchesAnyPattern(name, globalDeny)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -197,19 +214,21 @@ export class ToolPolicy {
|
||||
const baseProfile = override.profile ?? this.config.profile;
|
||||
let allowed = this.applyProfile(baseProfile, allToolNames);
|
||||
|
||||
// Apply override allow
|
||||
if (override.allow.length > 0) {
|
||||
// Apply override allow — expand groups first
|
||||
const overrideAllow = expandGroups(override.allow);
|
||||
if (overrideAllow.length > 0) {
|
||||
for (const name of allToolNames) {
|
||||
if (matchesAnyPattern(name, override.allow)) {
|
||||
if (matchesAnyPattern(name, overrideAllow)) {
|
||||
allowed.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply override deny (deny always wins)
|
||||
if (override.deny.length > 0) {
|
||||
// Apply override deny (deny always wins) — expand groups first
|
||||
const overrideDeny = expandGroups(override.deny);
|
||||
if (overrideDeny.length > 0) {
|
||||
allowed = new Set(
|
||||
[...allowed].filter(name => !matchesAnyPattern(name, override.deny)),
|
||||
[...allowed].filter(name => !matchesAnyPattern(name, overrideDeny)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -232,4 +251,4 @@ function intersect(a: Set<string>, b: Set<string>): Set<string> {
|
||||
/**
|
||||
* Exported for testing and for use in HookEngine (DRY).
|
||||
*/
|
||||
export { patternToRegex, matchesAnyPattern, PROFILE_TOOLS };
|
||||
export { patternToRegex, matchesAnyPattern, PROFILE_TOOLS, expandGroups };
|
||||
|
||||
Reference in New Issue
Block a user