on main help sign in
refactor: update client methods to return proto types
commit72251dd90681476e158a931069d5b078261c0323 authorAyman Bagabas <ayman.bagabas@gmail.com> dateTue 17 Mar 2026, 15:29 (7 months ago) parenteb7ed4f
3 files changed+98−106
internal/client/proto.go+31−94 internal/cmd/run.go+3−3 internal/workspace/client_workspace.go+64−9
internal/client/proto.go+31−94
@@ -14,11 +14,9 @@ import (
1414 "time"
1515
1616 "github.com/charmbracelet/crush/internal/config"
17− "github.com/charmbracelet/crush/internal/history"
1817 "github.com/charmbracelet/crush/internal/message"
1918 "github.com/charmbracelet/crush/internal/proto"
2019 "github.com/charmbracelet/crush/internal/pubsub"
21− "github.com/charmbracelet/crush/internal/session"
2220 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
2321)
2422
@@ -404,8 +402,8 @@ func (c *Client) InitiateAgentProcessing(ctx context.Context, id string) error {
404402 return nil
405403}
406404
407−// ListMessages retrieves all messages for a session.
408−func (c *Client) ListMessages(ctx context.Context, id string, sessionID string) ([]message.Message, error) {
405+// ListMessages retrieves all messages for a session as proto types.
406+func (c *Client) ListMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error) {
409407 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/messages", id, sessionID), nil, nil)
410408 if err != nil {
411409 return nil, fmt.Errorf("failed to get messages: %w", err)
@@ -414,15 +412,15 @@ func (c *Client) ListMessages(ctx context.Context, id string, sessionID string)
414412 if rsp.StatusCode != http.StatusOK {
415413 return nil, fmt.Errorf("failed to get messages: status code %d", rsp.StatusCode)
416414 }
417− var protoMsgs []proto.Message
418− if err := json.NewDecoder(rsp.Body).Decode(&protoMsgs); err != nil && !errors.Is(err, io.EOF) {
415+ var msgs []proto.Message
416+ if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
419417 return nil, fmt.Errorf("failed to decode messages: %w", err)
420418 }
421− return protoToMessages(protoMsgs), nil
419+ return msgs, nil
422420}
423421
424−// GetSession retrieves a specific session.
425−func (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*session.Session, error) {
422+// GetSession retrieves a specific session as a proto type.
423+func (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*proto.Session, error) {
426424 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sessionID), nil, nil)
427425 if err != nil {
428426 return nil, fmt.Errorf("failed to get session: %w", err)
@@ -431,15 +429,15 @@ func (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*
431429 if rsp.StatusCode != http.StatusOK {
432430 return nil, fmt.Errorf("failed to get session: status code %d", rsp.StatusCode)
433431 }
434− var sess session.Session
432+ var sess proto.Session
435433 if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
436434 return nil, fmt.Errorf("failed to decode session: %w", err)
437435 }
438436 return &sess, nil
439437}
440438
441−// ListSessionHistoryFiles retrieves history files for a session.
442−func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]history.File, error) {
439+// ListSessionHistoryFiles retrieves history files for a session as proto types.
440+func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]proto.File, error) {
443441 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/history", id, sessionID), nil, nil)
444442 if err != nil {
445443 return nil, fmt.Errorf("failed to get session history files: %w", err)
@@ -448,16 +446,16 @@ func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, session
448446 if rsp.StatusCode != http.StatusOK {
449447 return nil, fmt.Errorf("failed to get session history files: status code %d", rsp.StatusCode)
450448 }
451− var files []history.File
449+ var files []proto.File
452450 if err := json.NewDecoder(rsp.Body).Decode(&files); err != nil {
453451 return nil, fmt.Errorf("failed to decode session history files: %w", err)
454452 }
455453 return files, nil
456454}
457455
458−// CreateSession creates a new session in a workspace.
459−func (c *Client) CreateSession(ctx context.Context, id string, title string) (*session.Session, error) {
460− rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/sessions", id), nil, jsonBody(session.Session{Title: title}), http.Header{"Content-Type": []string{"application/json"}})
456+// CreateSession creates a new session in a workspace as a proto type.
457+func (c *Client) CreateSession(ctx context.Context, id string, title string) (*proto.Session, error) {
458+ rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/sessions", id), nil, jsonBody(proto.Session{Title: title}), http.Header{"Content-Type": []string{"application/json"}})
461459 if err != nil {
462460 return nil, fmt.Errorf("failed to create session: %w", err)
463461 }
@@ -465,15 +463,15 @@ func (c *Client) CreateSession(ctx context.Context, id string, title string) (*s
465463 if rsp.StatusCode != http.StatusOK {
466464 return nil, fmt.Errorf("failed to create session: status code %d", rsp.StatusCode)
467465 }
468− var sess session.Session
466+ var sess proto.Session
469467 if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
470468 return nil, fmt.Errorf("failed to decode session: %w", err)
471469 }
472470 return &sess, nil
473471}
474472
475−// ListSessions lists all sessions in a workspace.
476−func (c *Client) ListSessions(ctx context.Context, id string) ([]session.Session, error) {
473+// ListSessions lists all sessions in a workspace as proto types.
474+func (c *Client) ListSessions(ctx context.Context, id string) ([]proto.Session, error) {
477475 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions", id), nil, nil)
478476 if err != nil {
479477 return nil, fmt.Errorf("failed to get sessions: %w", err)
@@ -482,7 +480,7 @@ func (c *Client) ListSessions(ctx context.Context, id string) ([]session.Session
482480 if rsp.StatusCode != http.StatusOK {
483481 return nil, fmt.Errorf("failed to get sessions: status code %d", rsp.StatusCode)
484482 }
485− var sessions []session.Session
483+ var sessions []proto.Session
486484 if err := json.NewDecoder(rsp.Body).Decode(&sessions); err != nil {
487485 return nil, fmt.Errorf("failed to decode sessions: %w", err)
488486 }
@@ -556,8 +554,8 @@ func jsonBody(v any) *bytes.Buffer {
556554 return b
557555}
558556
559−// SaveSession updates a session in a workspace.
560−func (c *Client) SaveSession(ctx context.Context, id string, sess session.Session) (*session.Session, error) {
557+// SaveSession updates a session in a workspace, returning a proto type.
558+func (c *Client) SaveSession(ctx context.Context, id string, sess proto.Session) (*proto.Session, error) {
561559 rsp, err := c.put(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sess.ID), nil, jsonBody(sess), http.Header{"Content-Type": []string{"application/json"}})
562560 if err != nil {
563561 return nil, fmt.Errorf("failed to save session: %w", err)
@@ -566,7 +564,7 @@ func (c *Client) SaveSession(ctx context.Context, id string, sess session.Sessio
566564 if rsp.StatusCode != http.StatusOK {
567565 return nil, fmt.Errorf("failed to save session: status code %d", rsp.StatusCode)
568566 }
569− var saved session.Session
567+ var saved proto.Session
570568 if err := json.NewDecoder(rsp.Body).Decode(&saved); err != nil {
571569 return nil, fmt.Errorf("failed to decode session: %w", err)
572570 }
@@ -586,8 +584,8 @@ func (c *Client) DeleteSession(ctx context.Context, id string, sessionID string)
586584 return nil
587585}
588586
589−// ListUserMessages retrieves user-role messages for a session.
590−func (c *Client) ListUserMessages(ctx context.Context, id string, sessionID string) ([]message.Message, error) {
587+// ListUserMessages retrieves user-role messages for a session as proto types.
588+func (c *Client) ListUserMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error) {
591589 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/messages/user", id, sessionID), nil, nil)
592590 if err != nil {
593591 return nil, fmt.Errorf("failed to get user messages: %w", err)
@@ -596,15 +594,15 @@ func (c *Client) ListUserMessages(ctx context.Context, id string, sessionID stri
596594 if rsp.StatusCode != http.StatusOK {
597595 return nil, fmt.Errorf("failed to get user messages: status code %d", rsp.StatusCode)
598596 }
599− var protoMsgs []proto.Message
600− if err := json.NewDecoder(rsp.Body).Decode(&protoMsgs); err != nil && !errors.Is(err, io.EOF) {
597+ var msgs []proto.Message
598+ if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
601599 return nil, fmt.Errorf("failed to decode user messages: %w", err)
602600 }
603− return protoToMessages(protoMsgs), nil
601+ return msgs, nil
604602}
605603
606−// ListAllUserMessages retrieves all user-role messages across sessions.
607−func (c *Client) ListAllUserMessages(ctx context.Context, id string) ([]message.Message, error) {
604+// ListAllUserMessages retrieves all user-role messages across sessions as proto types.
605+func (c *Client) ListAllUserMessages(ctx context.Context, id string) ([]proto.Message, error) {
608606 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/messages/user", id), nil, nil)
609607 if err != nil {
610608 return nil, fmt.Errorf("failed to get all user messages: %w", err)
@@ -613,11 +611,11 @@ func (c *Client) ListAllUserMessages(ctx context.Context, id string) ([]message.
613611 if rsp.StatusCode != http.StatusOK {
614612 return nil, fmt.Errorf("failed to get all user messages: status code %d", rsp.StatusCode)
615613 }
616− var protoMsgs []proto.Message
617− if err := json.NewDecoder(rsp.Body).Decode(&protoMsgs); err != nil && !errors.Is(err, io.EOF) {
614+ var msgs []proto.Message
615+ if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
618616 return nil, fmt.Errorf("failed to decode all user messages: %w", err)
619617 }
620− return protoToMessages(protoMsgs), nil
618+ return msgs, nil
621619}
622620
623621// CancelAgentSession cancels an ongoing agent operation for a session.
@@ -749,64 +747,3 @@ func (c *Client) LSPStopAll(ctx context.Context, id string) error {
749747 }
750748 return nil
751749}
752−
753−func protoToMessages(msgs []proto.Message) []message.Message {
754− out := make([]message.Message, len(msgs))
755− for i, m := range msgs {
756− out[i] = protoToMessage(m)
757− }
758− return out
759−}
760−
761−func protoToMessage(m proto.Message) message.Message {
762− msg := message.Message{
763− ID: m.ID,
764− SessionID: m.SessionID,
765− Role: message.MessageRole(m.Role),
766− Model: m.Model,
767− Provider: m.Provider,
768− CreatedAt: m.CreatedAt,
769− UpdatedAt: m.UpdatedAt,
770− }
771−
772− for _, p := range m.Parts {
773− switch v := p.(type) {
774− case proto.TextContent:
775− msg.Parts = append(msg.Parts, message.TextContent{Text: v.Text})
776− case proto.ReasoningContent:
777− msg.Parts = append(msg.Parts, message.ReasoningContent{
778− Thinking: v.Thinking,
779− Signature: v.Signature,
780− StartedAt: v.StartedAt,
781− FinishedAt: v.FinishedAt,
782− })
783− case proto.ToolCall:
784− msg.Parts = append(msg.Parts, message.ToolCall{
785− ID: v.ID,
786− Name: v.Name,
787− Input: v.Input,
788− Finished: v.Finished,
789− })
790− case proto.ToolResult:
791− msg.Parts = append(msg.Parts, message.ToolResult{
792− ToolCallID: v.ToolCallID,
793− Name: v.Name,
794− Content: v.Content,
795− IsError: v.IsError,
796− })
797− case proto.Finish:
798− msg.Parts = append(msg.Parts, message.Finish{
799− Reason: message.FinishReason(v.Reason),
800− Time: v.Time,
801− Message: v.Message,
802− Details: v.Details,
803− })
804− case proto.ImageURLContent:
805− msg.Parts = append(msg.Parts, message.ImageURLContent{URL: v.URL, Detail: v.Detail})
806− case proto.BinaryContent:
807− msg.Parts = append(msg.Parts, message.BinaryContent{Path: v.Path, MIMEType: v.MIMEType, Data: v.Data})
808− }
809− }
810−
811− return msg
812−}
internal/cmd/run.go+3−3
@@ -448,7 +448,7 @@ func validateModelMatches(matches []modelMatch, modelID, label string) (modelMat
448448// If continueSessionID is set it fetches that session; if useLast is set it
449449// returns the most recently updated top-level session; otherwise it creates a
450450// new one.
451−func resolveSession(ctx context.Context, c *client.Client, wsID, continueSessionID string, useLast bool) (*session.Session, error) {
451+func resolveSession(ctx context.Context, c *client.Client, wsID, continueSessionID string, useLast bool) (*proto.Session, error) {
452452 switch {
453453 case continueSessionID != "":
454454 sess, err := c.GetSession(ctx, wsID, continueSessionID)
@@ -480,7 +480,7 @@ func resolveSession(ctx context.Context, c *client.Client, wsID, continueSession
480480
481481// resolveSessionByID resolves a session ID that may be a full UUID or a hash
482482// prefix returned by crush session list.
483−func resolveSessionByID(ctx context.Context, c *client.Client, wsID, id string) (*session.Session, error) {
483+func resolveSessionByID(ctx context.Context, c *client.Client, wsID, id string) (*proto.Session, error) {
484484 if sess, err := c.GetSession(ctx, wsID, id); err == nil {
485485 return sess, nil
486486 }
@@ -490,7 +490,7 @@ func resolveSessionByID(ctx context.Context, c *client.Client, wsID, id string)
490490 return nil, err
491491 }
492492
493− var matches []session.Session
493+ var matches []proto.Session
494494 for _, s := range sessions {
495495 hash := session.HashID(s.ID)
496496 if hash == id || strings.HasPrefix(hash, id) {
internal/workspace/client_workspace.go+64−9
@@ -83,7 +83,7 @@ func (w *ClientWorkspace) CreateSession(ctx context.Context, title string) (sess
8383 if err != nil {
8484 return session.Session{}, err
8585 }
86− return *sess, nil
86+ return protoToSession(*sess), nil
8787}
8888
8989func (w *ClientWorkspace) GetSession(ctx context.Context, sessionID string) (session.Session, error) {
@@ -91,19 +91,27 @@ func (w *ClientWorkspace) GetSession(ctx context.Context, sessionID string) (ses
9191 if err != nil {
9292 return session.Session{}, err
9393 }
94− return *sess, nil
94+ return protoToSession(*sess), nil
9595}
9696
9797func (w *ClientWorkspace) ListSessions(ctx context.Context) ([]session.Session, error) {
98− return w.client.ListSessions(ctx, w.workspaceID())
98+ protoSessions, err := w.client.ListSessions(ctx, w.workspaceID())
99+ if err != nil {
100+ return nil, err
101+ }
102+ sessions := make([]session.Session, len(protoSessions))
103+ for i, s := range protoSessions {
104+ sessions[i] = protoToSession(s)
105+ }
106+ return sessions, nil
99107}
100108
101109func (w *ClientWorkspace) SaveSession(ctx context.Context, sess session.Session) (session.Session, error) {
102− saved, err := w.client.SaveSession(ctx, w.workspaceID(), sess)
110+ saved, err := w.client.SaveSession(ctx, w.workspaceID(), sessionToProto(sess))
103111 if err != nil {
104112 return session.Session{}, err
105113 }
106− return *saved, nil
114+ return protoToSession(*saved), nil
107115}
108116
109117func (w *ClientWorkspace) DeleteSession(ctx context.Context, sessionID string) error {
@@ -125,15 +133,27 @@ func (w *ClientWorkspace) ParseAgentToolSessionID(sessionID string) (string, str
125133// -- Messages --
126134
127135func (w *ClientWorkspace) ListMessages(ctx context.Context, sessionID string) ([]message.Message, error) {
128− return w.client.ListMessages(ctx, w.workspaceID(), sessionID)
136+ msgs, err := w.client.ListMessages(ctx, w.workspaceID(), sessionID)
137+ if err != nil {
138+ return nil, err
139+ }
140+ return protoToMessages(msgs), nil
129141}
130142
131143func (w *ClientWorkspace) ListUserMessages(ctx context.Context, sessionID string) ([]message.Message, error) {
132− return w.client.ListUserMessages(ctx, w.workspaceID(), sessionID)
144+ msgs, err := w.client.ListUserMessages(ctx, w.workspaceID(), sessionID)
145+ if err != nil {
146+ return nil, err
147+ }
148+ return protoToMessages(msgs), nil
133149}
134150
135151func (w *ClientWorkspace) ListAllUserMessages(ctx context.Context) ([]message.Message, error) {
136− return w.client.ListAllUserMessages(ctx, w.workspaceID())
152+ msgs, err := w.client.ListAllUserMessages(ctx, w.workspaceID())
153+ if err != nil {
154+ return nil, err
155+ }
156+ return protoToMessages(msgs), nil
137157}
138158
139159// -- Agent --
@@ -304,7 +324,11 @@ func (w *ClientWorkspace) FileTrackerListReadFiles(ctx context.Context, sessionI
304324// -- History --
305325
306326func (w *ClientWorkspace) ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error) {
307− return w.client.ListSessionHistoryFiles(ctx, w.workspaceID(), sessionID)
327+ files, err := w.client.ListSessionHistoryFiles(ctx, w.workspaceID(), sessionID)
328+ if err != nil {
329+ return nil, err
330+ }
331+ return protoToFiles(files), nil
308332}
309333
310334// -- LSP --
@@ -688,3 +712,34 @@ func protoToMessage(m proto.Message) message.Message {
688712
689713 return msg
690714}
715+
716+func protoToMessages(msgs []proto.Message) []message.Message {
717+ out := make([]message.Message, len(msgs))
718+ for i, m := range msgs {
719+ out[i] = protoToMessage(m)
720+ }
721+ return out
722+}
723+
724+func protoToFiles(files []proto.File) []history.File {
725+ out := make([]history.File, len(files))
726+ for i, f := range files {
727+ out[i] = protoToFile(f)
728+ }
729+ return out
730+}
731+
732+func sessionToProto(s session.Session) proto.Session {
733+ return proto.Session{
734+ ID: s.ID,
735+ ParentSessionID: s.ParentSessionID,
736+ Title: s.Title,
737+ SummaryMessageID: s.SummaryMessageID,
738+ MessageCount: s.MessageCount,
739+ PromptTokens: s.PromptTokens,
740+ CompletionTokens: s.CompletionTokens,
741+ Cost: s.Cost,
742+ CreatedAt: s.CreatedAt,
743+ UpdatedAt: s.UpdatedAt,
744+ }
745+}