Skip to content

Commit f41b7bb

Browse files
committed
chore: refactoring status updates
1 parent e35ea2d commit f41b7bb

24 files changed

Lines changed: 366 additions & 264 deletions

File tree

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ func setupSubscriptions(app *app.App, parentCtx context.Context) (chan tea.Msg,
229229
setupSubscriber(ctx, &wg, "sessions", app.Sessions.Subscribe, ch)
230230
setupSubscriber(ctx, &wg, "messages", app.Messages.Subscribe, ch)
231231
setupSubscriber(ctx, &wg, "permissions", app.Permissions.Subscribe, ch)
232+
setupSubscriber(ctx, &wg, "status", app.Status.Subscribe, ch)
232233

233234
cleanupFunc := func() {
234235
logging.Info("Cancelling all subscriptions")

internal/app/app.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/opencode-ai/opencode/internal/message"
1717
"github.com/opencode-ai/opencode/internal/permission"
1818
"github.com/opencode-ai/opencode/internal/session"
19+
"github.com/opencode-ai/opencode/internal/status"
1920
"github.com/opencode-ai/opencode/internal/tui/theme"
2021
)
2122

@@ -24,6 +25,7 @@ type App struct {
2425
Messages message.Service
2526
History history.Service
2627
Permissions permission.Service
28+
Status status.Service
2729

2830
CoderAgent agent.Service
2931

@@ -38,18 +40,24 @@ type App struct {
3840

3941
func New(ctx context.Context, conn *sql.DB) (*App, error) {
4042
q := db.New(conn)
41-
sessions := session.NewService(q)
42-
messages := message.NewService(q)
43-
files := history.NewService(q, conn)
43+
sessionService := session.NewService(q)
44+
messageService := message.NewService(q)
45+
historyService := history.NewService(q, conn)
46+
permissionService := permission.NewPermissionService()
47+
statusService := status.NewService()
4448

4549
// Initialize session manager
46-
session.InitManager(sessions)
50+
session.InitManager(sessionService)
51+
52+
// Initialize status service
53+
status.InitManager(statusService)
4754

4855
app := &App{
49-
Sessions: sessions,
50-
Messages: messages,
51-
History: files,
52-
Permissions: permission.NewPermissionService(),
56+
Sessions: sessionService,
57+
Messages: messageService,
58+
History: historyService,
59+
Permissions: permissionService,
60+
Status: statusService,
5361
LSPClients: make(map[string]*lsp.Client),
5462
}
5563

internal/llm/agent/agent.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/opencode-ai/opencode/internal/message"
1818
"github.com/opencode-ai/opencode/internal/permission"
1919
"github.com/opencode-ai/opencode/internal/session"
20+
"github.com/opencode-ai/opencode/internal/status"
2021
)
2122

2223
// Common errors
@@ -96,7 +97,7 @@ func NewAgent(
9697
func (a *agent) Cancel(sessionID string) {
9798
if cancelFunc, exists := a.activeRequests.LoadAndDelete(sessionID); exists {
9899
if cancel, ok := cancelFunc.(context.CancelFunc); ok {
99-
logging.InfoPersist(fmt.Sprintf("Request cancellation initiated for session: %s", sessionID))
100+
status.Info(fmt.Sprintf("Request cancellation initiated for session: %s", sessionID))
100101
cancel()
101102
}
102103
}
@@ -186,7 +187,7 @@ func (a *agent) Run(ctx context.Context, sessionID string, content string, attac
186187
}
187188
result := a.processGeneration(genCtx, sessionID, content, attachmentParts)
188189
if result.Err() != nil && !errors.Is(result.Err(), ErrRequestCancelled) && !errors.Is(result.Err(), context.Canceled) {
189-
logging.ErrorPersist(result.Err().Error())
190+
status.Error(result.Err().Error())
190191
}
191192
logging.Debug("Request completed", "sessionID", sessionID)
192193
a.activeRequests.Delete(sessionID)
@@ -224,11 +225,11 @@ func (a *agent) processGeneration(ctx context.Context, sessionID, content string
224225
if len(sessionMessages) == 0 && currentSession.Summary == "" {
225226
go func() {
226227
defer logging.RecoverPanic("agent.Run", func() {
227-
logging.ErrorPersist("panic while generating title")
228+
status.Error("panic while generating title")
228229
})
229230
titleErr := a.generateTitle(context.Background(), sessionID, content)
230231
if titleErr != nil {
231-
logging.ErrorPersist(fmt.Sprintf("failed to generate title: %v", titleErr))
232+
status.Error(fmt.Sprintf("failed to generate title: %v", titleErr))
232233
}
233234
}()
234235
}
@@ -308,11 +309,11 @@ func (a *agent) streamAndHandleEvents(ctx context.Context, sessionID string, msg
308309

309310
// If we're approaching the context window limit, trigger auto-compaction
310311
if false && (*usage+maxTokens) >= threshold {
311-
logging.InfoPersist(fmt.Sprintf("Auto-compaction triggered for session %s. Estimated tokens: %d, Threshold: %d", sessionID, usage, threshold))
312+
status.Info(fmt.Sprintf("Auto-compaction triggered for session %s. Estimated tokens: %d, Threshold: %d", sessionID, usage, threshold))
312313

313314
// Perform compaction with pause/resume to ensure safety
314315
if err := a.CompactSession(ctx, sessionID); err != nil {
315-
logging.ErrorPersist(fmt.Sprintf("Auto-compaction failed: %v", err))
316+
status.Error(fmt.Sprintf("Auto-compaction failed: %v", err))
316317
// Continue with the request even if compaction fails
317318
} else {
318319
// Re-fetch session details after compaction
@@ -495,10 +496,10 @@ func (a *agent) processEvent(ctx context.Context, sessionID string, assistantMsg
495496
return a.messages.Update(ctx, *assistantMsg)
496497
case provider.EventError:
497498
if errors.Is(event.Error, context.Canceled) {
498-
logging.InfoPersist(fmt.Sprintf("Event processing canceled for session: %s", sessionID))
499+
status.Info(fmt.Sprintf("Event processing canceled for session: %s", sessionID))
499500
return context.Canceled
500501
}
501-
logging.ErrorPersist(event.Error.Error())
502+
status.Error(event.Error.Error())
502503
return event.Error
503504
case provider.EventComplete:
504505
assistantMsg.SetToolCalls(event.Response.ToolCalls)
@@ -570,15 +571,15 @@ func (a *agent) PauseSession(sessionID string) error {
570571
return nil // Session is not active, no need to pause
571572
}
572573

573-
logging.InfoPersist(fmt.Sprintf("Pausing session: %s", sessionID))
574+
status.Info(fmt.Sprintf("Pausing session: %s", sessionID))
574575
a.pauseLock.Lock() // Acquire write lock to block new operations
575576
return nil
576577
}
577578

578579
// ResumeSession resumes message processing for a session
579580
// This should be called after completing operations that required exclusive access
580581
func (a *agent) ResumeSession(sessionID string) error {
581-
logging.InfoPersist(fmt.Sprintf("Resuming session: %s", sessionID))
582+
status.Info(fmt.Sprintf("Resuming session: %s", sessionID))
582583
a.pauseLock.Unlock() // Release write lock to allow operations to continue
583584
return nil
584585
}
@@ -592,7 +593,7 @@ func (a *agent) CompactSession(ctx context.Context, sessionID string) error {
592593
}
593594
// Make sure to resume the session when we're done
594595
defer a.ResumeSession(sessionID)
595-
logging.InfoPersist(fmt.Sprintf("Session %s paused for compaction", sessionID))
596+
status.Info(fmt.Sprintf("Session %s paused for compaction", sessionID))
596597
}
597598

598599
// Create a cancellable context

internal/llm/provider/anthropic.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/opencode-ai/opencode/internal/llm/tools"
1818
"github.com/opencode-ai/opencode/internal/logging"
1919
"github.com/opencode-ai/opencode/internal/message"
20+
"github.com/opencode-ai/opencode/internal/status"
2021
)
2122

2223
type anthropicOptions struct {
@@ -227,7 +228,7 @@ func (a *anthropicClient) send(ctx context.Context, messages []message.Message,
227228
return nil, retryErr
228229
}
229230
if retry {
230-
logging.WarnPersist(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries), logging.PersistTimeArg, time.Millisecond*time.Duration(after+100))
231+
status.Warn(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries))
231232
select {
232233
case <-ctx.Done():
233234
return nil, ctx.Err()
@@ -365,7 +366,7 @@ func (a *anthropicClient) stream(ctx context.Context, messages []message.Message
365366
return
366367
}
367368
if retry {
368-
logging.WarnPersist(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries), logging.PersistTimeArg, time.Millisecond*time.Duration(after+100))
369+
status.Warn(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries))
369370
select {
370371
case <-ctx.Done():
371372
// context cancelled

internal/llm/provider/gemini.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/opencode-ai/opencode/internal/llm/tools"
1616
"github.com/opencode-ai/opencode/internal/logging"
1717
"github.com/opencode-ai/opencode/internal/message"
18+
"github.com/opencode-ai/opencode/internal/status"
1819
"google.golang.org/api/iterator"
1920
"google.golang.org/api/option"
2021
)
@@ -195,7 +196,7 @@ func (g *geminiClient) send(ctx context.Context, messages []message.Message, too
195196
return nil, retryErr
196197
}
197198
if retry {
198-
logging.WarnPersist(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries), logging.PersistTimeArg, time.Millisecond*time.Duration(after+100))
199+
status.Warn(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries))
199200
select {
200201
case <-ctx.Done():
201202
return nil, ctx.Err()
@@ -297,7 +298,7 @@ func (g *geminiClient) stream(ctx context.Context, messages []message.Message, t
297298
return
298299
}
299300
if retry {
300-
logging.WarnPersist(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries), logging.PersistTimeArg, time.Millisecond*time.Duration(after+100))
301+
status.Warn(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries))
301302
select {
302303
case <-ctx.Done():
303304
if ctx.Err() != nil {

internal/llm/provider/openai.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/opencode-ai/opencode/internal/llm/tools"
1717
"github.com/opencode-ai/opencode/internal/logging"
1818
"github.com/opencode-ai/opencode/internal/message"
19+
"github.com/opencode-ai/opencode/internal/status"
1920
)
2021

2122
type openaiOptions struct {
@@ -214,7 +215,7 @@ func (o *openaiClient) send(ctx context.Context, messages []message.Message, too
214215
return nil, retryErr
215216
}
216217
if retry {
217-
logging.WarnPersist(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries), logging.PersistTimeArg, time.Millisecond*time.Duration(after+100))
218+
status.Warn(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries))
218219
select {
219220
case <-ctx.Done():
220221
return nil, ctx.Err()
@@ -320,7 +321,7 @@ func (o *openaiClient) stream(ctx context.Context, messages []message.Message, t
320321
return
321322
}
322323
if retry {
323-
logging.WarnPersist(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries), logging.PersistTimeArg, time.Millisecond*time.Duration(after+100))
324+
status.Warn(fmt.Sprintf("Retrying due to rate limit... attempt %d of %d", attempts, maxRetries))
324325
select {
325326
case <-ctx.Done():
326327
// context cancelled

internal/llm/tools/shell/shell.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"syscall"
1313
"time"
1414

15-
"github.com/opencode-ai/opencode/internal/logging"
15+
"github.com/opencode-ai/opencode/internal/status"
1616
)
1717

1818
type PersistentShell struct {
@@ -101,7 +101,7 @@ func newPersistentShell(cwd string) *PersistentShell {
101101
go func() {
102102
err := cmd.Wait()
103103
if err != nil {
104-
logging.ErrorPersist(fmt.Sprintf("Shell process exited with error: %v", err))
104+
status.Error(fmt.Sprintf("Shell process exited with error: %v", err))
105105
}
106106
shell.isAlive = false
107107
close(shell.commandQueue)

internal/logging/logger.go

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"runtime/debug"
88
"time"
9+
10+
"github.com/opencode-ai/opencode/internal/status"
911
)
1012

1113
func Info(msg string, args ...any) {
@@ -24,41 +26,25 @@ func Error(msg string, args ...any) {
2426
slog.Error(msg, args...)
2527
}
2628

27-
func InfoPersist(msg string, args ...any) {
28-
args = append(args, persistKeyArg, true)
29-
slog.Info(msg, args...)
30-
}
31-
32-
func DebugPersist(msg string, args ...any) {
33-
args = append(args, persistKeyArg, true)
34-
slog.Debug(msg, args...)
35-
}
36-
37-
func WarnPersist(msg string, args ...any) {
38-
args = append(args, persistKeyArg, true)
39-
slog.Warn(msg, args...)
40-
}
41-
42-
func ErrorPersist(msg string, args ...any) {
43-
args = append(args, persistKeyArg, true)
44-
slog.Error(msg, args...)
45-
}
46-
4729
// RecoverPanic is a common function to handle panics gracefully.
4830
// It logs the error, creates a panic log file with stack trace,
4931
// and executes an optional cleanup function before returning.
5032
func RecoverPanic(name string, cleanup func()) {
5133
if r := recover(); r != nil {
5234
// Log the panic
53-
ErrorPersist(fmt.Sprintf("Panic in %s: %v", name, r))
35+
errorMsg := fmt.Sprintf("Panic in %s: %v", name, r)
36+
Error(errorMsg)
37+
status.Error(errorMsg)
5438

5539
// Create a timestamped panic log file
5640
timestamp := time.Now().Format("20060102-150405")
5741
filename := fmt.Sprintf("opencode-panic-%s-%s.log", name, timestamp)
5842

5943
file, err := os.Create(filename)
6044
if err != nil {
61-
ErrorPersist(fmt.Sprintf("Failed to create panic log: %v", err))
45+
errMsg := fmt.Sprintf("Failed to create panic log: %v", err)
46+
Error(errMsg)
47+
status.Error(errMsg)
6248
} else {
6349
defer file.Close()
6450

@@ -67,7 +53,9 @@ func RecoverPanic(name string, cleanup func()) {
6753
fmt.Fprintf(file, "Time: %s\n\n", time.Now().Format(time.RFC3339))
6854
fmt.Fprintf(file, "Stack Trace:\n%s\n", debug.Stack())
6955

70-
InfoPersist(fmt.Sprintf("Panic details written to %s", filename))
56+
infoMsg := fmt.Sprintf("Panic details written to %s", filename)
57+
Info(infoMsg)
58+
status.Info(infoMsg)
7159
}
7260

7361
// Execute cleanup function if provided

internal/logging/message.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ import (
66

77
// LogMessage is the event payload for a log message
88
type LogMessage struct {
9-
ID string
10-
Time time.Time
11-
Level string
12-
Persist bool // used when we want to show the mesage in the status bar
13-
PersistTime time.Duration // used when we want to show the mesage in the status bar
14-
Message string `json:"msg"`
15-
Attributes []Attr
9+
ID string
10+
Time time.Time
11+
Level string
12+
Message string `json:"msg"`
13+
Attributes []Attr
1614
}
1715

1816
type Attr struct {

0 commit comments

Comments
 (0)