on main help sign in
refactor: update client methods to return proto types
72251ddAyman Bagabas
internal/client/proto.go+31−94
@@ -14,11 +14,9 @@ import (
14 "time"
14 "time"
15
15
16 "github.com/charmbracelet/crush/internal/config"
16 "github.com/charmbracelet/crush/internal/config"
17− "github.com/charmbracelet/crush/internal/history"
18 "github.com/charmbracelet/crush/internal/message"
17 "github.com/charmbracelet/crush/internal/message"
19 "github.com/charmbracelet/crush/internal/proto"
18 "github.com/charmbracelet/crush/internal/proto"
20 "github.com/charmbracelet/crush/internal/pubsub"
19 "github.com/charmbracelet/crush/internal/pubsub"
21− "github.com/charmbracelet/crush/internal/session"
22 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
20 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
23)
21)
24
22
@@ -404,8 +402,8 @@ func (c *Client) InitiateAgentProcessing(ctx context.Context, id string) error {
404 return nil
402 return nil
405}
403}
406
404
407−// ListMessages retrieves all messages for a session.
405+// ListMessages retrieves all messages for a session as proto types.
408−func (c *Client) ListMessages(ctx context.Context, id string, sessionID string) ([]message.Message, error) {
406+func (c *Client) ListMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error) {
409 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/messages", id, sessionID), nil, nil)
407 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/messages", id, sessionID), nil, nil)
410 if err != nil {
408 if err != nil {
411 return nil, fmt.Errorf("failed to get messages: %w", err)
409 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)
414 if rsp.StatusCode != http.StatusOK {
412 if rsp.StatusCode != http.StatusOK {
415 return nil, fmt.Errorf("failed to get messages: status code %d", rsp.StatusCode)
413 return nil, fmt.Errorf("failed to get messages: status code %d", rsp.StatusCode)
416 }
414 }
417− var protoMsgs []proto.Message
415+ var msgs []proto.Message
418− if err := json.NewDecoder(rsp.Body).Decode(&protoMsgs); err != nil && !errors.Is(err, io.EOF) {
416+ if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
419 return nil, fmt.Errorf("failed to decode messages: %w", err)
417 return nil, fmt.Errorf("failed to decode messages: %w", err)
420 }
418 }
421− return protoToMessages(protoMsgs), nil
419+ return msgs, nil
422}
420}
423
421
424−// GetSession retrieves a specific session.
422+// GetSession retrieves a specific session as a proto type.
425−func (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*session.Session, error) {
423+func (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*proto.Session, error) {
426 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sessionID), nil, nil)
424 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sessionID), nil, nil)
427 if err != nil {
425 if err != nil {
428 return nil, fmt.Errorf("failed to get session: %w", err)
426 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) (*
431 if rsp.StatusCode != http.StatusOK {
429 if rsp.StatusCode != http.StatusOK {
432 return nil, fmt.Errorf("failed to get session: status code %d", rsp.StatusCode)
430 return nil, fmt.Errorf("failed to get session: status code %d", rsp.StatusCode)
433 }
431 }
434− var sess session.Session
432+ var sess proto.Session
435 if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
433 if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
436 return nil, fmt.Errorf("failed to decode session: %w", err)
434 return nil, fmt.Errorf("failed to decode session: %w", err)
437 }
435 }
438 return &sess, nil
436 return &sess, nil
439}
437}
440
438
441−// ListSessionHistoryFiles retrieves history files for a session.
439+// ListSessionHistoryFiles retrieves history files for a session as proto types.
442−func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]history.File, error) {
440+func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]proto.File, error) {
443 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/history", id, sessionID), nil, nil)
441 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/history", id, sessionID), nil, nil)
444 if err != nil {
442 if err != nil {
445 return nil, fmt.Errorf("failed to get session history files: %w", err)
443 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
448 if rsp.StatusCode != http.StatusOK {
446 if rsp.StatusCode != http.StatusOK {
449 return nil, fmt.Errorf("failed to get session history files: status code %d", rsp.StatusCode)
447 return nil, fmt.Errorf("failed to get session history files: status code %d", rsp.StatusCode)
450 }
448 }
451− var files []history.File
449+ var files []proto.File
452 if err := json.NewDecoder(rsp.Body).Decode(&files); err != nil {
450 if err := json.NewDecoder(rsp.Body).Decode(&files); err != nil {
453 return nil, fmt.Errorf("failed to decode session history files: %w", err)
451 return nil, fmt.Errorf("failed to decode session history files: %w", err)
454 }
452 }
455 return files, nil
453 return files, nil
456}
454}
457
455
458−// CreateSession creates a new session in a workspace.
456+// CreateSession creates a new session in a workspace as a proto type.
459−func (c *Client) CreateSession(ctx context.Context, id string, title string) (*session.Session, error) {
457+func (c *Client) CreateSession(ctx context.Context, id string, title string) (*proto.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"}})
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"}})
461 if err != nil {
459 if err != nil {
462 return nil, fmt.Errorf("failed to create session: %w", err)
460 return nil, fmt.Errorf("failed to create session: %w", err)
463 }
461 }
@@ -465,15 +463,15 @@ func (c *Client) CreateSession(ctx context.Context, id string, title string) (*s
465 if rsp.StatusCode != http.StatusOK {
463 if rsp.StatusCode != http.StatusOK {
466 return nil, fmt.Errorf("failed to create session: status code %d", rsp.StatusCode)
464 return nil, fmt.Errorf("failed to create session: status code %d", rsp.StatusCode)
467 }
465 }
468− var sess session.Session
466+ var sess proto.Session
469 if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
467 if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
470 return nil, fmt.Errorf("failed to decode session: %w", err)
468 return nil, fmt.Errorf("failed to decode session: %w", err)
471 }
469 }
472 return &sess, nil
470 return &sess, nil
473}
471}
474
472
475−// ListSessions lists all sessions in a workspace.
473+// ListSessions lists all sessions in a workspace as proto types.
476−func (c *Client) ListSessions(ctx context.Context, id string) ([]session.Session, error) {
474+func (c *Client) ListSessions(ctx context.Context, id string) ([]proto.Session, error) {
477 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions", id), nil, nil)
475 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions", id), nil, nil)
478 if err != nil {
476 if err != nil {
479 return nil, fmt.Errorf("failed to get sessions: %w", err)
477 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
482 if rsp.StatusCode != http.StatusOK {
480 if rsp.StatusCode != http.StatusOK {
483 return nil, fmt.Errorf("failed to get sessions: status code %d", rsp.StatusCode)
481 return nil, fmt.Errorf("failed to get sessions: status code %d", rsp.StatusCode)
484 }
482 }
485− var sessions []session.Session
483+ var sessions []proto.Session
486 if err := json.NewDecoder(rsp.Body).Decode(&sessions); err != nil {
484 if err := json.NewDecoder(rsp.Body).Decode(&sessions); err != nil {
487 return nil, fmt.Errorf("failed to decode sessions: %w", err)
485 return nil, fmt.Errorf("failed to decode sessions: %w", err)
488 }
486 }
@@ -556,8 +554,8 @@ func jsonBody(v any) *bytes.Buffer {
556 return b
554 return b
557}
555}
558
556
559−// SaveSession updates a session in a workspace.
557+// SaveSession updates a session in a workspace, returning a proto type.
560−func (c *Client) SaveSession(ctx context.Context, id string, sess session.Session) (*session.Session, error) {
558+func (c *Client) SaveSession(ctx context.Context, id string, sess proto.Session) (*proto.Session, error) {
561 rsp, err := c.put(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sess.ID), nil, jsonBody(sess), http.Header{"Content-Type": []string{"application/json"}})
559 rsp, err := c.put(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sess.ID), nil, jsonBody(sess), http.Header{"Content-Type": []string{"application/json"}})
562 if err != nil {
560 if err != nil {
563 return nil, fmt.Errorf("failed to save session: %w", err)
561 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
566 if rsp.StatusCode != http.StatusOK {
564 if rsp.StatusCode != http.StatusOK {
567 return nil, fmt.Errorf("failed to save session: status code %d", rsp.StatusCode)
565 return nil, fmt.Errorf("failed to save session: status code %d", rsp.StatusCode)
568 }
566 }
569− var saved session.Session
567+ var saved proto.Session
570 if err := json.NewDecoder(rsp.Body).Decode(&saved); err != nil {
568 if err := json.NewDecoder(rsp.Body).Decode(&saved); err != nil {
571 return nil, fmt.Errorf("failed to decode session: %w", err)
569 return nil, fmt.Errorf("failed to decode session: %w", err)
572 }
570 }
@@ -586,8 +584,8 @@ func (c *Client) DeleteSession(ctx context.Context, id string, sessionID string)
586 return nil
584 return nil
587}
585}
588
586
589−// ListUserMessages retrieves user-role messages for a session.
587+// ListUserMessages retrieves user-role messages for a session as proto types.
590−func (c *Client) ListUserMessages(ctx context.Context, id string, sessionID string) ([]message.Message, error) {
588+func (c *Client) ListUserMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error) {
591 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/messages/user", id, sessionID), nil, nil)
589 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/messages/user", id, sessionID), nil, nil)
592 if err != nil {
590 if err != nil {
593 return nil, fmt.Errorf("failed to get user messages: %w", err)
591 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
596 if rsp.StatusCode != http.StatusOK {
594 if rsp.StatusCode != http.StatusOK {
597 return nil, fmt.Errorf("failed to get user messages: status code %d", rsp.StatusCode)
595 return nil, fmt.Errorf("failed to get user messages: status code %d", rsp.StatusCode)
598 }
596 }
599− var protoMsgs []proto.Message
597+ var msgs []proto.Message
600− if err := json.NewDecoder(rsp.Body).Decode(&protoMsgs); err != nil && !errors.Is(err, io.EOF) {
598+ if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
601 return nil, fmt.Errorf("failed to decode user messages: %w", err)
599 return nil, fmt.Errorf("failed to decode user messages: %w", err)
602 }
600 }
603− return protoToMessages(protoMsgs), nil
601+ return msgs, nil
604}
602}
605
603
606−// ListAllUserMessages retrieves all user-role messages across sessions.
604+// ListAllUserMessages retrieves all user-role messages across sessions as proto types.
607−func (c *Client) ListAllUserMessages(ctx context.Context, id string) ([]message.Message, error) {
605+func (c *Client) ListAllUserMessages(ctx context.Context, id string) ([]proto.Message, error) {
608 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/messages/user", id), nil, nil)
606 rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/messages/user", id), nil, nil)
609 if err != nil {
607 if err != nil {
610 return nil, fmt.Errorf("failed to get all user messages: %w", err)
608 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.
613 if rsp.StatusCode != http.StatusOK {
611 if rsp.StatusCode != http.StatusOK {
614 return nil, fmt.Errorf("failed to get all user messages: status code %d", rsp.StatusCode)
612 return nil, fmt.Errorf("failed to get all user messages: status code %d", rsp.StatusCode)
615 }
613 }
616− var protoMsgs []proto.Message
614+ var msgs []proto.Message
617− if err := json.NewDecoder(rsp.Body).Decode(&protoMsgs); err != nil && !errors.Is(err, io.EOF) {
615+ if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
618 return nil, fmt.Errorf("failed to decode all user messages: %w", err)
616 return nil, fmt.Errorf("failed to decode all user messages: %w", err)
619 }
617 }
620− return protoToMessages(protoMsgs), nil
618+ return msgs, nil
621}
619}
622
620
623// CancelAgentSession cancels an ongoing agent operation for a session.
621// CancelAgentSession cancels an ongoing agent operation for a session.
@@ -749,64 +747,3 @@ func (c *Client) LSPStopAll(ctx context.Context, id string) error {
749 }
747 }
750 return nil
748 return nil
751}
749}
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
448// If continueSessionID is set it fetches that session; if useLast is set it
448// If continueSessionID is set it fetches that session; if useLast is set it
449// returns the most recently updated top-level session; otherwise it creates a
449// returns the most recently updated top-level session; otherwise it creates a
450// new one.
450// 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) {
452 switch {
452 switch {
453 case continueSessionID != "":
453 case continueSessionID != "":
454 sess, err := c.GetSession(ctx, wsID, continueSessionID)
454 sess, err := c.GetSession(ctx, wsID, continueSessionID)
@@ -480,7 +480,7 @@ func resolveSession(ctx context.Context, c *client.Client, wsID, continueSession
480
480
481// resolveSessionByID resolves a session ID that may be a full UUID or a hash
481// resolveSessionByID resolves a session ID that may be a full UUID or a hash
482// prefix returned by crush session list.
482// 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) {
484 if sess, err := c.GetSession(ctx, wsID, id); err == nil {
484 if sess, err := c.GetSession(ctx, wsID, id); err == nil {
485 return sess, nil
485 return sess, nil
486 }
486 }
@@ -490,7 +490,7 @@ func resolveSessionByID(ctx context.Context, c *client.Client, wsID, id string)
490 return nil, err
490 return nil, err
491 }
491 }
492
492
493− var matches []session.Session
493+ var matches []proto.Session
494 for _, s := range sessions {
494 for _, s := range sessions {
495 hash := session.HashID(s.ID)
495 hash := session.HashID(s.ID)
496 if hash == id || strings.HasPrefix(hash, id) {
496 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
83 if err != nil {
83 if err != nil {
84 return session.Session{}, err
84 return session.Session{}, err
85 }
85 }
86− return *sess, nil
86+ return protoToSession(*sess), nil
87}
87}
88
88
89func (w *ClientWorkspace) GetSession(ctx context.Context, sessionID string) (session.Session, error) {
89func (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
91 if err != nil {
91 if err != nil {
92 return session.Session{}, err
92 return session.Session{}, err
93 }
93 }
94− return *sess, nil
94+ return protoToSession(*sess), nil
95}
95}
96
96
97func (w *ClientWorkspace) ListSessions(ctx context.Context) ([]session.Session, error) {
97func (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
99}
107}
100
108
101func (w *ClientWorkspace) SaveSession(ctx context.Context, sess session.Session) (session.Session, error) {
109func (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))
103 if err != nil {
111 if err != nil {
104 return session.Session{}, err
112 return session.Session{}, err
105 }
113 }
106− return *saved, nil
114+ return protoToSession(*saved), nil
107}
115}
108
116
109func (w *ClientWorkspace) DeleteSession(ctx context.Context, sessionID string) error {
117func (w *ClientWorkspace) DeleteSession(ctx context.Context, sessionID string) error {
@@ -125,15 +133,27 @@ func (w *ClientWorkspace) ParseAgentToolSessionID(sessionID string) (string, str
125// -- Messages --
133// -- Messages --
126
134
127func (w *ClientWorkspace) ListMessages(ctx context.Context, sessionID string) ([]message.Message, error) {
135func (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
129}
141}
130
142
131func (w *ClientWorkspace) ListUserMessages(ctx context.Context, sessionID string) ([]message.Message, error) {
143func (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
133}
149}
134
150
135func (w *ClientWorkspace) ListAllUserMessages(ctx context.Context) ([]message.Message, error) {
151func (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
137}
157}
138
158
139// -- Agent --
159// -- Agent --
@@ -304,7 +324,11 @@ func (w *ClientWorkspace) FileTrackerListReadFiles(ctx context.Context, sessionI
304// -- History --
324// -- History --
305
325
306func (w *ClientWorkspace) ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error) {
326func (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
308}
332}
309
333
310// -- LSP --
334// -- LSP --
@@ -688,3 +712,34 @@ func protoToMessage(m proto.Message) message.Message {
688
712
689 return msg
713 return msg
690}
714}
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+}