@@ -62,36 +62,41 @@ type Model struct {
64type sessionAgent struct {
64type sessionAgent struct {
65− largeModel Model
65+ largeModel Model
66− smallModel Model
66+ smallModel Model
67− systemPrompt string
67+ systemPrompt string
68− tools []ai.AgentTool
68+ tools []ai.AgentTool
69− sessions session.Service
69+ sessions session.Service
70− messages message.Service
70+ messages message.Service
71+ disableAutoSummarize bool
72 messageQueue *csync.Map[string, []SessionAgentCall]
73 messageQueue *csync.Map[string, []SessionAgentCall]
73 activeRequests *csync.Map[string, context.CancelFunc]
74 activeRequests *csync.Map[string, context.CancelFunc]
76−type SessionAgentOption func(*sessionAgent)
77+type SessionAgentOptions struct {
81+ DisableAutoSummarize bool
82+ Sessions session.Service
83+ Messages message.Service
78func NewSessionAgent(
87func NewSessionAgent(
79− largeModel Model,
88+ opts SessionAgentOptions,
82− sessions session.Service,
83− messages message.Service,
84− tools ...ai.AgentTool,
85) SessionAgent {
89) SessionAgent {
86 return &sessionAgent{
90 return &sessionAgent{
87− largeModel: largeModel,
91+ largeModel: opts.LargeModel,
88− smallModel: smallModel,
92+ smallModel: opts.SmallModel,
89− systemPrompt: systemPrompt,
93+ systemPrompt: opts.SystemPrompt,
90− sessions: sessions,
94+ sessions: opts.Sessions,
91− messages: messages,
95+ messages: opts.Messages,
96+ disableAutoSummarize: opts.DisableAutoSummarize,
92− tools: tools,
97+ tools: opts.Tools,
93− messageQueue: csync.NewMap[string, []SessionAgentCall](),
98+ messageQueue: csync.NewMap[string, []SessionAgentCall](),
94− activeRequests: csync.NewMap[string, context.CancelFunc](),
99+ activeRequests: csync.NewMap[string, context.CancelFunc](),
@@ -164,6 +169,7 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*ai.Agen
164 history, files := a.preparePrompt(msgs, call.Attachments...)
169 history, files := a.preparePrompt(msgs, call.Attachments...)
166 var currentAssistant *message.Message
171 var currentAssistant *message.Message
172+ var shouldSummarize bool
167 result, err := agent.Stream(genCtx, ai.AgentStreamCall{
173 result, err := agent.Stream(genCtx, ai.AgentStreamCall{
168 Prompt: call.Prompt,
174 Prompt: call.Prompt,
169 Files: files,
175 Files: files,
@@ -292,16 +298,16 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*ai.Agen
292 IsError: isError,
298 IsError: isError,
293 Metadata: result.ClientMetadata,
299 Metadata: result.ClientMetadata,
295− _, err := a.messages.Create(context.Background(), currentAssistant.SessionID, message.CreateMessageParams{
301+ _, createMsgErr := a.messages.Create(genCtx, currentAssistant.SessionID, message.CreateMessageParams{
296 Role: message.Tool,
302 Role: message.Tool,
297 Parts: []message.ContentPart{
303 Parts: []message.ContentPart{
298 toolResult,
304 toolResult,
301− if err != nil {
307+ if createMsgErr != nil {
302− return err
308+ return createMsgErr
304− return a.messages.Update(genCtx, *currentAssistant)
310+ return nil
306 OnStepFinish: func(stepResult ai.StepResult) error {
312 OnStepFinish: func(stepResult ai.StepResult) error {
307 finishReason := message.FinishReasonUnknown
313 finishReason := message.FinishReasonUnknown
@@ -323,6 +329,18 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*ai.Agen
324 return a.messages.Update(genCtx, *currentAssistant)
330 return a.messages.Update(genCtx, *currentAssistant)
332+ StopWhen: []ai.StopCondition{
333+ func(_ []ai.StepResult) bool {
334+ contextWindow := a.largeModel.CatwalkCfg.ContextWindow
335+ tokens := currentSession.CompletionTokens + currentSession.PromptTokens
336+ percentage := (float64(tokens) / float64(contextWindow)) * 100
337+ if (percentage > 80) && !a.disableAutoSummarize {
338+ shouldSummarize = true
327 if err != nil {
345 if err != nil {
328 isCancelErr := errors.Is(err, context.Canceled)
346 isCancelErr := errors.Is(err, context.Canceled)
@@ -358,28 +376,29 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*ai.Agen
361− if !found {
379+ if found {
362− content := "There was an error while executing the tool"
382+ content := "There was an error while executing the tool"
363− if isCancelErr {
383+ if isCancelErr {
364− content = "Tool execution canceled by user"
384+ content = "Tool execution canceled by user"
365− } else if isPermissionErr {
385+ } else if isPermissionErr {
366− content = "Permission denied"
386+ content = "Permission denied"
368− toolResult := message.ToolResult{
388+ toolResult := message.ToolResult{
369− ToolCallID: tc.ID,
389+ ToolCallID: tc.ID,
370− Name: tc.Name,
390+ Name: tc.Name,
371− Content: content,
391+ Content: content,
372− IsError: true,
392+ IsError: true,
374− _, createErr = a.messages.Create(context.Background(), currentAssistant.SessionID, message.CreateMessageParams{
394+ _, createErr = a.messages.Create(context.Background(), currentAssistant.SessionID, message.CreateMessageParams{
375− Role: message.Tool,
395+ Role: message.Tool,
376− Parts: []message.ContentPart{
396+ Parts: []message.ContentPart{
377− toolResult,
397+ toolResult,
380− if createErr != nil {
400+ if createErr != nil {
381− return nil, createErr
401+ return nil, createErr
385 if isCancelErr {
404 if isCancelErr {
@@ -398,6 +417,13 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*ai.Agen
399 wg.Wait()
418 wg.Wait()
420+ if shouldSummarize {
421+ a.activeRequests.Del(call.SessionID)
422+ if summarizeErr := a.Summarize(genCtx, call.SessionID); summarizeErr != nil {
423+ return nil, summarizeErr
401 queuedMessages, ok := a.messageQueue.Get(call.SessionID)
427 queuedMessages, ok := a.messageQueue.Get(call.SessionID)
402 if !ok || len(queuedMessages) == 0 {
428 if !ok || len(queuedMessages) == 0 {
403 return result, err
429 return result, err
@@ -437,20 +463,21 @@ func (a *sessionAgent) Summarize(ctx context.Context, sessionID string) error {
437 ai.WithSystemPrompt(string(summaryPrompt)),
463 ai.WithSystemPrompt(string(summaryPrompt)),
439 summaryMessage, err := a.messages.Create(ctx, sessionID, message.CreateMessageParams{
465 summaryMessage, err := a.messages.Create(ctx, sessionID, message.CreateMessageParams{
440− Role: message.Assistant,
466+ Role: message.Assistant,
441− Model: a.largeModel.Model.Model(),
467+ Model: a.largeModel.Model.Model(),
442− Provider: a.largeModel.Model.Provider(),
468+ Provider: a.largeModel.Model.Provider(),
469+ IsSummaryMessage: true,
444 if err != nil {
471 if err != nil {
445 return err
472 return err
448− resp, err := agent.Stream(ctx, ai.AgentStreamCall{
475+ resp, err := agent.Stream(genCtx, ai.AgentStreamCall{
449 Prompt: "Provide a detailed summary of our conversation above.",
476 Prompt: "Provide a detailed summary of our conversation above.",
450 Messages: aiMsgs,
477 Messages: aiMsgs,
451 OnReasoningDelta: func(id string, text string) error {
478 OnReasoningDelta: func(id string, text string) error {
452 summaryMessage.AppendReasoningContent(text)
479 summaryMessage.AppendReasoningContent(text)
453− return a.messages.Update(ctx, summaryMessage)
480+ return a.messages.Update(genCtx, summaryMessage)
455 OnReasoningEnd: func(id string, reasoning ai.ReasoningContent) error {
482 OnReasoningEnd: func(id string, reasoning ai.ReasoningContent) error {
456 // handle anthropic signature
483 // handle anthropic signature
@@ -460,14 +487,20 @@ func (a *sessionAgent) Summarize(ctx context.Context, sessionID string) error {
462 summaryMessage.FinishThinking()
489 summaryMessage.FinishThinking()
463− return a.messages.Update(ctx, summaryMessage)
490+ return a.messages.Update(genCtx, summaryMessage)
465 OnTextDelta: func(id, text string) error {
492 OnTextDelta: func(id, text string) error {
466 summaryMessage.AppendContent(text)
493 summaryMessage.AppendContent(text)
467− return a.messages.Update(ctx, summaryMessage)
494+ return a.messages.Update(genCtx, summaryMessage)
470 if err != nil {
497 if err != nil {
498+ isCancelErr := errors.Is(err, context.Canceled)
500+ // User cancelled summarize we need to remove the summary message
501+ deleteErr := a.messages.Delete(ctx, summaryMessage.ID)
471 return err
504 return err