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:
@@ -51,6 +51,7 @@ export class NativeAgent {
|
||||
private _callCount: number = 0;
|
||||
private _toolPolicyContext?: ToolPolicyContext;
|
||||
private _attachmentCollector?: OutboundAttachmentCollector;
|
||||
private _thinking: boolean = false;
|
||||
|
||||
constructor(config: NativeAgentConfig) {
|
||||
this.modelClient = config.modelClient;
|
||||
@@ -69,6 +70,14 @@ export class NativeAgent {
|
||||
}
|
||||
|
||||
async process(userMessage: string, attachments?: Attachment[]): Promise<string> {
|
||||
// Detect and strip !!think prefix for per-message thinking mode
|
||||
if (userMessage.startsWith('!!think ') || userMessage === '!!think') {
|
||||
this._thinking = true;
|
||||
userMessage = userMessage.replace(/^!!think\s*/, '').trim() || 'Think about this.';
|
||||
} else {
|
||||
this._thinking = false;
|
||||
}
|
||||
|
||||
const userMsg = buildUserMessage(userMessage, attachments);
|
||||
|
||||
if (this.session) {
|
||||
@@ -89,6 +98,7 @@ export class NativeAgent {
|
||||
const request: ChatRequest = {
|
||||
messages: this.history,
|
||||
system: this.systemPrompt,
|
||||
...(this._thinking ? { thinking: true } : {}),
|
||||
};
|
||||
|
||||
const response = await this.chatWithRouter(request);
|
||||
@@ -101,10 +111,16 @@ export class NativeAgent {
|
||||
console.warn(`[Flynn] ${response.fallbackReason}`);
|
||||
}
|
||||
|
||||
// Prepend thinking content if present
|
||||
let finalContent = response.content;
|
||||
if (response.thinkingContent) {
|
||||
finalContent = `<thinking>\n${response.thinkingContent}\n</thinking>\n\n${response.content}`;
|
||||
}
|
||||
|
||||
const assistantMsg: Message = { role: 'assistant', content: response.content };
|
||||
this.addToHistory(assistantMsg);
|
||||
|
||||
return response.content;
|
||||
return finalContent;
|
||||
}
|
||||
|
||||
private async toolLoop(): Promise<string> {
|
||||
@@ -124,6 +140,7 @@ export class NativeAgent {
|
||||
messages: loopMessages as unknown as Message[],
|
||||
system: this.systemPrompt,
|
||||
tools,
|
||||
...(this._thinking ? { thinking: true } : {}),
|
||||
};
|
||||
|
||||
const response = await this.chatWithRouter(request);
|
||||
@@ -138,9 +155,13 @@ export class NativeAgent {
|
||||
|
||||
// If the model didn't request tool use, we're done
|
||||
if (response.stopReason !== 'tool_use' || !response.toolCalls?.length) {
|
||||
let finalContent = response.content;
|
||||
if (response.thinkingContent) {
|
||||
finalContent = `<thinking>\n${response.thinkingContent}\n</thinking>\n\n${response.content}`;
|
||||
}
|
||||
const assistantMsg: Message = { role: 'assistant', content: response.content };
|
||||
this.addToHistory(assistantMsg);
|
||||
return response.content;
|
||||
return finalContent;
|
||||
}
|
||||
|
||||
// Build the assistant message with tool_use content blocks
|
||||
|
||||
Reference in New Issue
Block a user