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:
@@ -1,2 +1,2 @@
|
||||
export { SessionStore } from './store.js';
|
||||
export { SessionStore, parseDuration } from './store.js';
|
||||
export { SessionManager, ManagedSession, type Session } from './manager.js';
|
||||
|
||||
@@ -98,4 +98,11 @@ export class SessionManager {
|
||||
const id = this.makeSessionId(frontend, userId);
|
||||
this.sessions.delete(id);
|
||||
}
|
||||
|
||||
/** Remove sessions from the in-memory cache by their IDs. */
|
||||
evictSessions(sessionIds: string[]): void {
|
||||
for (const id of sessionIds) {
|
||||
this.sessions.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import type { Message } from '../models/types.js';
|
||||
|
||||
/** Parse a duration string like '30d', '7d', '12h' to milliseconds. Returns null if invalid or '0'. */
|
||||
export function parseDuration(s: string): number | null {
|
||||
if (s === '0' || s === 'false') return null;
|
||||
const match = s.match(/^(\d+)(h|d)$/);
|
||||
if (!match) return null;
|
||||
const [, n, unit] = match;
|
||||
return unit === 'h' ? Number(n) * 3600_000 : Number(n) * 86_400_000;
|
||||
}
|
||||
|
||||
export class SessionStore {
|
||||
private db: Database.Database;
|
||||
|
||||
@@ -71,6 +80,27 @@ export class SessionStore {
|
||||
return rows.map(row => row.session_id);
|
||||
}
|
||||
|
||||
/** Delete all messages for sessions with no activity since the given timestamp. Returns pruned session IDs. */
|
||||
pruneStale(beforeTimestamp: number): string[] {
|
||||
const stale = this.db.prepare(`
|
||||
SELECT session_id FROM messages
|
||||
GROUP BY session_id
|
||||
HAVING MAX(created_at) < ?
|
||||
`).all(beforeTimestamp) as Array<{ session_id: string }>;
|
||||
|
||||
if (stale.length === 0) return [];
|
||||
|
||||
const deleteStmt = this.db.prepare('DELETE FROM messages WHERE session_id = ?');
|
||||
const transaction = this.db.transaction(() => {
|
||||
for (const { session_id } of stale) {
|
||||
deleteStmt.run(session_id);
|
||||
}
|
||||
});
|
||||
transaction();
|
||||
|
||||
return stale.map(r => r.session_id);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.db.close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user