feat(ipuaro): add session configuration

- Add SessionConfigSchema with persistIndefinitely, maxHistoryMessages, saveInputHistory
- Implement Session.truncateHistory() method for limiting message history
- Update HandleMessage to support history truncation and input history toggle
- Add config flow through useSession and App components
- Add 19 unit tests for SessionConfigSchema
- Update CHANGELOG.md and ROADMAP.md for v0.22.2
This commit is contained in:
imfozilbek
2025-12-02 01:34:04 +05:00
parent 7f0ec49c90
commit a7669f8947
20 changed files with 336 additions and 72 deletions

View File

@@ -68,6 +68,8 @@ export interface HandleMessageEvents {
export interface HandleMessageOptions {
autoApply?: boolean
maxToolCalls?: number
maxHistoryMessages?: number
saveInputHistory?: boolean
}
const DEFAULT_MAX_TOOL_CALLS = 20
@@ -135,6 +137,15 @@ export class HandleMessage {
this.llm.abort()
}
/**
* Truncate session history if maxHistoryMessages is set.
*/
private truncateHistoryIfNeeded(session: Session): void {
if (this.options.maxHistoryMessages !== undefined) {
session.truncateHistory(this.options.maxHistoryMessages)
}
}
/**
* Execute the message handling flow.
*/
@@ -145,7 +156,12 @@ export class HandleMessage {
if (message.trim()) {
const userMessage = createUserMessage(message)
session.addMessage(userMessage)
session.addInputToHistory(message)
this.truncateHistoryIfNeeded(session)
if (this.options.saveInputHistory !== false) {
session.addInputToHistory(message)
}
this.emitMessage(userMessage)
}
@@ -183,6 +199,7 @@ export class HandleMessage {
toolCalls: 0,
})
session.addMessage(assistantMessage)
this.truncateHistoryIfNeeded(session)
this.emitMessage(assistantMessage)
this.contextManager.addTokens(response.tokens)
this.contextManager.updateSession(session)
@@ -197,6 +214,7 @@ export class HandleMessage {
toolCalls: parsed.toolCalls.length,
})
session.addMessage(assistantMessage)
this.truncateHistoryIfNeeded(session)
this.emitMessage(assistantMessage)
toolCallCount += parsed.toolCalls.length
@@ -204,6 +222,7 @@ export class HandleMessage {
const errorMsg = `Maximum tool calls (${String(maxToolCalls)}) exceeded`
const errorMessage = createSystemMessage(errorMsg)
session.addMessage(errorMessage)
this.truncateHistoryIfNeeded(session)
this.emitMessage(errorMessage)
this.emitStatus("ready")
return
@@ -227,6 +246,7 @@ export class HandleMessage {
const toolMessage = createToolMessage(results)
session.addMessage(toolMessage)
this.truncateHistoryIfNeeded(session)
this.contextManager.addTokens(response.tokens)
@@ -306,6 +326,7 @@ export class HandleMessage {
const errorMessage = createSystemMessage(`Error: ${ipuaroError.message}`)
session.addMessage(errorMessage)
this.truncateHistoryIfNeeded(session)
this.emitMessage(errorMessage)
this.emitStatus("ready")

View File

@@ -94,6 +94,12 @@ export class Session {
}
}
truncateHistory(maxMessages: number): void {
if (this.history.length > maxMessages) {
this.history = this.history.slice(-maxMessages)
}
}
clearHistory(): void {
this.history = []
this.context = {

View File

@@ -97,6 +97,15 @@ export const DisplayConfigSchema = z.object({
progressBar: z.boolean().default(true),
})
/**
* Session configuration schema.
*/
export const SessionConfigSchema = z.object({
persistIndefinitely: z.boolean().default(true),
maxHistoryMessages: z.number().int().positive().default(100),
saveInputHistory: z.boolean().default(true),
})
/**
* Full configuration schema.
*/
@@ -109,6 +118,7 @@ export const ConfigSchema = z.object({
edit: EditConfigSchema.default({}),
input: InputConfigSchema.default({}),
display: DisplayConfigSchema.default({}),
session: SessionConfigSchema.default({}),
})
/**
@@ -123,6 +133,7 @@ export type UndoConfig = z.infer<typeof UndoConfigSchema>
export type EditConfig = z.infer<typeof EditConfigSchema>
export type InputConfig = z.infer<typeof InputConfigSchema>
export type DisplayConfig = z.infer<typeof DisplayConfigSchema>
export type SessionConfig = z.infer<typeof SessionConfigSchema>
/**
* Default configuration.

View File

@@ -10,6 +10,7 @@ import type { ISessionStorage } from "../domain/services/ISessionStorage.js"
import type { IStorage } from "../domain/services/IStorage.js"
import type { DiffInfo } from "../domain/services/ITool.js"
import type { ErrorOption } from "../shared/errors/IpuaroError.js"
import type { Config } from "../shared/constants/config.js"
import type { IToolRegistry } from "../application/interfaces/IToolRegistry.js"
import type { ConfirmationResult } from "../application/use-cases/ExecuteTool.js"
import type { ProjectStructure } from "../infrastructure/llm/prompts.js"
@@ -25,6 +26,7 @@ export interface AppDependencies {
llm: ILLMClient
tools: IToolRegistry
projectStructure?: ProjectStructure
config?: Config
}
export interface ExtendedAppProps extends AppProps {
@@ -129,6 +131,7 @@ export function App({
projectRoot: projectPath,
projectName,
projectStructure: deps.projectStructure,
config: deps.config,
},
{
autoApply,

View File

@@ -11,6 +11,7 @@ import type { IStorage } from "../../domain/services/IStorage.js"
import type { DiffInfo } from "../../domain/services/ITool.js"
import type { ChatMessage } from "../../domain/value-objects/ChatMessage.js"
import type { ErrorOption } from "../../shared/errors/IpuaroError.js"
import type { Config } from "../../shared/constants/config.js"
import type { IToolRegistry } from "../../application/interfaces/IToolRegistry.js"
import {
HandleMessage,
@@ -30,6 +31,7 @@ export interface UseSessionDependencies {
projectRoot: string
projectName: string
projectStructure?: ProjectStructure
config?: Config
}
export interface UseSessionOptions {
@@ -111,7 +113,11 @@ async function initializeSession(
if (deps.projectStructure) {
handleMessage.setProjectStructure(deps.projectStructure)
}
handleMessage.setOptions({ autoApply: options.autoApply })
handleMessage.setOptions({
autoApply: options.autoApply,
maxHistoryMessages: deps.config?.session.maxHistoryMessages,
saveInputHistory: deps.config?.session.saveInputHistory,
})
handleMessage.setEvents(createEventHandlers(setters, options))
refs.current.handleMessage = handleMessage
refs.current.undoChange = new UndoChange(deps.sessionStorage, deps.storage)