Documentation
¶
Overview ¶
Package socketclient provides a client library for connecting to the scriptschnell Unix socket server.
The client library enables frontends (TUI, CLI, Web, and custom clients) to communicate with a running scriptschnell instance over Unix domain sockets, supporting concurrent sessions, workspace isolation, and full protocol features.
Architecture ¶
The client follows a straightforward architecture:
- SocketClient: Main client struct managing connection, message I/O, and protocol handling
- Callback-based API: Receive messages via registered callbacks for different message types
- Reconnection: Automatic reconnection with configurable retry logic
- Request-Response Pattern: Send requests with request IDs and wait for responses
Basic Usage
// Create new client
client, err := socketclient.NewClient("~/.scriptschnell.sock")
if err != nil {
log.Fatal(err)
}
// Set up message callbacks
client.SetChatMessageCallback(func(msg socketclient.ChatMessage) {
fmt.Printf("[%s]: %s\n", msg.Role, msg.Content)
})
// Connect to server
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
log.Fatal(err)
}
defer client.Disconnect()
// Create session
session, err := client.CreateSession(ctx, "my-workspace", "")
if err != nil {
log.Fatal(err)
}
// Send chat message
err = client.SendChat(ctx, "Hello, how can you help me?")
if err != nil {
log.Fatal(err)
}
Connection Management ¶
The client provides automatic reconnection with configurable parameters:
// Configure reconnection
client.SetReconnectEnabled(true)
client.SetMaxReconnectAttempts(10)
client.SetReconnectDelay(2 * time.Second)
// Monitor connection state
client.SetStateChangedCallback(func(state socketclient.ConnectionState, err error) {
fmt.Printf("State changed: %v\n", state)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
})
Session Management ¶
The client provides convenient methods for session operations:
// List available sessions
sessions, err := client.ListSessions(ctx, "")
if err != nil {
log.Fatal(err)
}
for _, sess := range sessions {
fmt.Printf("%s: %s (%d messages)\n", sess.ID, sess.Title, sess.MessageCount)
}
// Attach to existing session
err = client.AttachSession(ctx, "existing-session-id")
if err != nil {
log.Fatal(err)
}
// Save current session
err = client.SaveSession(ctx, "My Session")
if err != nil {
log.Fatal(err)
}
Workspace Management ¶
Workspaces can be listed and switched:
// List workspaces
workspaces, err := client.ListWorkspaces(ctx)
if err != nil {
log.Fatal(err)
}
// Set active workspace
err = client.SetWorkspace(ctx, "/path/to/workspace")
if err != nil {
log.Fatal(err)
}
Message Protocol ¶
The client implements the full message protocol defined in docs/unix-socket-protocol.md:
- Authentication (auth_request, auth_response)
- Session management (session_create, session_attach, session_list, etc.)
- Chat and generation (chat_send, chat_stop, chat_message)
- Tool interactions (tool_call, tool_result, tool_compact)
- Authorization (authorization_request, authorization_response)
- Question dialogs (question_request, question_response)
- Progress updates (progress)
- Configuration (config_get, config_set)
- Workspace management (workspace_list, workspace_set)
- Session persistence (session_save, session_load)
- Connection lifecycle (ping, pong, close, closed)
Authorization Flow ¶
Authorization requests are handled via callbacks:
// Set authorization callback
client.SetAuthorizationCallback(func(req socketclient.AuthorizationRequest) (approved bool, err error) {
fmt.Printf("Authorization required for tool: %s\n", req.ToolName)
fmt.Printf("Reason: %s\n", req.Reason)
// Ask user for approval (in TUI/CLI/Web)
return true, nil // or false, nil
})
Question Dialogs ¶
Planning agent questions are handled via callbacks:
// Set question callback
client.SetQuestionCallback(func(req socketclient.QuestionRequest) (answers map[string]string, err error) {
if req.MultiMode {
answers = make(map[string]string)
for id, question := range req.Questions {
fmt.Printf("Q: %s\n", question)
answers[id] = getUserInput() // Get user input
}
return answers, nil
}
return map[string]string{"answer": getUserInput()}, nil
})
Progress Updates ¶
Progress updates are streamed via callbacks:
// Set progress callback
client.SetProgressCallback(func(progress socketclient.ProgressData) {
fmt.Printf("Progress: %s\n", progress.Message)
if progress.IsCompact {
fmt.Printf("\033[2K\r%s", progress.Message) // Update in place
}
})
Error Handling ¶
The client provides detailed error information via the error interface:
err := client.SendChat(ctx, "test")
if err != nil {
var socketErr *socketclient.SocketError
if errors.As(err, &socketErr) {
fmt.Printf("Socket error: %s (code: %s)\n", socketErr.Message, socketErr.Code)
if socketErr.Details != "" {
fmt.Printf("Details: %s\n", socketErr.Details)
}
}
}
Thread Safety ¶
SocketClient is thread-safe for concurrent use from multiple goroutines. All operations are protected by mutexes. Callbacks may be invoked concurrently from different goroutines, so implementations must be thread-safe if they access shared state.
Context Support ¶
Most methods accept a context.Context for cancellation and timeout:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
session, err := client.CreateSession(ctx, "workspace", "")
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("Request timed out")
}
return
}
Reconnection Behavior ¶
When enabled, automatic reconnection attempts to reconnect on connection loss:
- Waits with exponential backoff (configurable)
- Attempts up to max retry attempts (default: 10)
- Notifies state changes via callback
- Restores session attachment if previously attached
- Re-subscribes to message callbacks
Graceful Shutdown ¶
Always disconnect the client when done:
client.Disconnect()
Or use Close for immediate shutdown without sending close message:
client.Close()
Index ¶
- type AuthorizationAck
- type AuthorizationRequest
- type AuthorizationResponse
- type ChatMessage
- type Client
- func (c *Client) AttachSession(ctx context.Context, sessionID string) error
- func (c *Client) ClearChat(ctx context.Context) error
- func (c *Client) Close() error
- func (c *Client) CloseSession(ctx context.Context, reason string, preserveSession bool) error
- func (c *Client) Connect(ctx context.Context) error
- func (c *Client) CreateSession(ctx context.Context, workspace, sessionID string, workingDir string) (string, error)
- func (c *Client) CreateWorkspace(ctx context.Context, baseWorkspace, name string) (workspaceID, path string, err error)
- func (c *Client) DeleteSession(ctx context.Context, sessionID, workspace string) error
- func (c *Client) DetachSession(ctx context.Context) error
- func (c *Client) Disconnect() error
- func (c *Client) GetConfig(ctx context.Context, keys []string) (map[string]ConfigValue, error)
- func (c *Client) GetCurrentSessionID() string
- func (c *Client) GetCurrentWorkspace() string
- func (c *Client) GetReconnectAttempts() int
- func (c *Client) GetSessionInfo(ctx context.Context) (*SessionInfo, error)
- func (c *Client) GetState() ConnectionState
- func (c *Client) IsConnected() bool
- func (c *Client) ListSessions(ctx context.Context, workspace string) ([]SessionInfo, error)
- func (c *Client) ListWorkspaces(ctx context.Context) ([]WorkspaceInfo, error)
- func (c *Client) LoadSession(ctx context.Context, sessionID, workspace string) error
- func (c *Client) Ping() error
- func (c *Client) SaveSession(ctx context.Context, name string) error
- func (c *Client) SendAuthorizationAck(authID string) error
- func (c *Client) SendAuthorizationResponse(authID string, approved bool) error
- func (c *Client) SendChat(ctx context.Context, content string, options map[string]interface{}) error
- func (c *Client) SendMessage(msg *Message) error
- func (c *Client) SendQuestionResponse(questionID string, answer string, answers map[string]string) error
- func (c *Client) SendRequest(msg *Message) (*Message, error)
- func (c *Client) SetAuthorizationCallback(fn func(AuthorizationRequest) (bool, error))
- func (c *Client) SetChatMessageCallback(fn func(ChatMessage))
- func (c *Client) SetCompletionCallback(fn func(requestID string, success bool, errorMsg string))
- func (c *Client) SetConfig(ctx context.Context, values map[string]interface{}) error
- func (c *Client) SetConnectionLostCallback(fn func(error))
- func (c *Client) SetMaxReconnectAttempts(attempts int)
- func (c *Client) SetProgressCallback(fn func(ProgressData))
- func (c *Client) SetQuestionCallback(fn func(QuestionRequest) (map[string]string, error))
- func (c *Client) SetReconnectDelay(delay time.Duration)
- func (c *Client) SetReconnectEnabled(enabled bool)
- func (c *Client) SetReconnectMaxDelay(delay time.Duration)
- func (c *Client) SetReconnectingCallback(fn func(attempt int, maxAttempts int))
- func (c *Client) SetRequestTimeout(timeout time.Duration)
- func (c *Client) SetStateChangedCallback(fn func(ConnectionState, error))
- func (c *Client) SetToolCallCallback(fn func(ToolCall))
- func (c *Client) SetToolResultCallback(fn func(ToolResult))
- func (c *Client) SetWorkspace(ctx context.Context, workspace string) error
- func (c *Client) StopChat(ctx context.Context) error
- func (c *Client) StreamChat(ctx context.Context, content string, options map[string]interface{}, ...) error
- func (c *Client) WaitForCompletion(ctx context.Context, timeout time.Duration) error
- type Config
- type ConfigValue
- type ConnectionState
- type ErrorInfo
- type Message
- type MessageHistory
- type ProgressData
- type QuestionRequest
- type QuestionResponse
- type SessionInfo
- type SocketError
- type ToolCall
- type ToolResult
- type WorkspaceInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuthorizationAck ¶
type AuthorizationAck struct {
AuthID string `json:"auth_id"`
}
AuthorizationAck represents an authorization acknowledgment
type AuthorizationRequest ¶
type AuthorizationRequest struct {
SessionID string `json:"session_id,omitempty"`
AuthID string `json:"auth_id"`
ToolName string `json:"tool_name"`
Parameters map[string]interface{} `json:"parameters"`
Reason string `json:"reason"`
}
AuthorizationRequest represents an authorization request
type AuthorizationResponse ¶
type AuthorizationResponse struct {
AuthID string `json:"auth_id"`
Approved bool `json:"approved"`
}
AuthorizationResponse represents an authorization response
type ChatMessage ¶
type ChatMessage struct {
SessionID string `json:"session_id,omitempty"`
Role string `json:"role"`
Content string `json:"content"`
StreamID string `json:"stream_id,omitempty"`
ChunkIndex int `json:"chunk_index,omitempty"`
IsFinal bool `json:"is_final,omitempty"`
Reasoning string `json:"reasoning,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
ChatMessage represents a chat message
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client represents a socket client
func NewClientWithConfig ¶
NewClientWithConfig creates a new socket client with custom configuration
func (*Client) AttachSession ¶
AttachSession attaches to an existing session
func (*Client) CloseSession ¶
CloseSession closes the current session gracefully
func (*Client) CreateSession ¶
func (c *Client) CreateSession(ctx context.Context, workspace, sessionID string, workingDir string) (string, error)
CreateSession creates a new session
func (*Client) CreateWorkspace ¶
func (c *Client) CreateWorkspace(ctx context.Context, baseWorkspace, name string) (workspaceID, path string, err error)
CreateWorkspace creates a new workspace (e.g., git worktree)
func (*Client) DeleteSession ¶
DeleteSession deletes a session
func (*Client) DetachSession ¶
DetachSession detaches from the current session
func (*Client) Disconnect ¶
Disconnect disconnects from the socket server gracefully
func (*Client) GetCurrentSessionID ¶
GetCurrentSessionID returns the current session ID
func (*Client) GetCurrentWorkspace ¶
GetCurrentWorkspace returns the current workspace
func (*Client) GetReconnectAttempts ¶
GetReconnectAttempts returns the current number of reconnection attempts
func (*Client) GetSessionInfo ¶
func (c *Client) GetSessionInfo(ctx context.Context) (*SessionInfo, error)
GetSessionInfo gets information about the current session
func (*Client) GetState ¶
func (c *Client) GetState() ConnectionState
GetState returns the current connection state
func (*Client) IsConnected ¶
IsConnected returns true if the client is connected
func (*Client) ListSessions ¶
ListSessions lists sessions for a workspace
func (*Client) ListWorkspaces ¶
func (c *Client) ListWorkspaces(ctx context.Context) ([]WorkspaceInfo, error)
ListWorkspaces lists all workspaces
func (*Client) LoadSession ¶
LoadSession loads a saved session
func (*Client) SaveSession ¶
SaveSession saves the current session
func (*Client) SendAuthorizationAck ¶
SendAuthorizationAck sends an authorization acknowledgment
func (*Client) SendAuthorizationResponse ¶
SendAuthorizationResponse sends an authorization response
func (*Client) SendChat ¶
func (c *Client) SendChat(ctx context.Context, content string, options map[string]interface{}) error
SendChat sends a chat message to the server
func (*Client) SendMessage ¶
SendMessage sends a message without waiting for a response
func (*Client) SendQuestionResponse ¶
func (c *Client) SendQuestionResponse(questionID string, answer string, answers map[string]string) error
SendQuestionResponse sends a question response
func (*Client) SendRequest ¶
SendRequest sends a request and waits for a response
func (*Client) SetAuthorizationCallback ¶
func (c *Client) SetAuthorizationCallback(fn func(AuthorizationRequest) (bool, error))
SetAuthorizationCallback sets the callback for authorization requests
func (*Client) SetChatMessageCallback ¶
func (c *Client) SetChatMessageCallback(fn func(ChatMessage))
SetChatMessageCallback sets the callback for chat messages
func (*Client) SetCompletionCallback ¶
SetCompletionCallback sets the callback for completion notifications
func (*Client) SetConnectionLostCallback ¶
SetConnectionLostCallback sets the callback for connection loss events
func (*Client) SetMaxReconnectAttempts ¶
SetMaxReconnectAttempts sets the maximum number of reconnection attempts
func (*Client) SetProgressCallback ¶
func (c *Client) SetProgressCallback(fn func(ProgressData))
SetProgressCallback sets the callback for progress updates
func (*Client) SetQuestionCallback ¶
func (c *Client) SetQuestionCallback(fn func(QuestionRequest) (map[string]string, error))
SetQuestionCallback sets the callback for question requests
func (*Client) SetReconnectDelay ¶
SetReconnectDelay sets the initial delay between reconnection attempts
func (*Client) SetReconnectEnabled ¶
SetReconnectEnabled enables or disables automatic reconnection
func (*Client) SetReconnectMaxDelay ¶
SetReconnectMaxDelay sets the maximum delay between reconnection attempts
func (*Client) SetReconnectingCallback ¶
SetReconnectingCallback sets the callback for reconnection attempts
func (*Client) SetRequestTimeout ¶
SetRequestTimeout sets the default timeout for requests
func (*Client) SetStateChangedCallback ¶
func (c *Client) SetStateChangedCallback(fn func(ConnectionState, error))
SetStateChangedCallback sets the callback for connection state changes
func (*Client) SetToolCallCallback ¶
SetToolCallCallback sets the callback for tool calls
func (*Client) SetToolResultCallback ¶
func (c *Client) SetToolResultCallback(fn func(ToolResult))
SetToolResultCallback sets the callback for tool results
func (*Client) SetWorkspace ¶
SetWorkspace sets the active workspace
func (*Client) StreamChat ¶
func (c *Client) StreamChat(ctx context.Context, content string, options map[string]interface{}, callback func(ChatMessage)) error
StreamChat sends a chat message and streams responses via callback
type Config ¶
type Config struct {
// SocketPath is the path to the Unix socket
SocketPath string
// ClientType identifies the type of client (e.g., "tui", "cli", "web")
ClientType string
// ClientVersion is the version of the client
ClientVersion string
// Capabilities is a list of client capabilities
Capabilities []string
// AuthToken is an optional authentication token
AuthToken string
// ConnectTimeout is the timeout for initial connection
ConnectTimeout time.Duration
// ReconnectEnabled enables automatic reconnection
ReconnectEnabled bool
// MaxReconnectAttempts is the maximum number of reconnection attempts
MaxReconnectAttempts int
// ReconnectDelay is the initial delay between reconnection attempts
ReconnectDelay time.Duration
// ReconnectMaxDelay is the maximum delay between reconnection attempts
ReconnectMaxDelay time.Duration
// RequestTimeout is the default timeout for requests
RequestTimeout time.Duration
// ReadTimeout is the timeout for reading messages
ReadTimeout time.Duration
// WriteTimeout is the timeout for writing messages
WriteTimeout time.Duration
// PingInterval is the interval for sending ping messages
PingInterval time.Duration
}
Config holds client configuration
type ConfigValue ¶
type ConfigValue struct {
Value interface{} `json:"value"`
Type string `json:"type"`
}
ConfigValue represents a configuration value
type ConnectionState ¶
type ConnectionState int
ConnectionState represents the current state of the socket connection
const ( // StateDisconnected indicates the client is not connected StateDisconnected ConnectionState = iota // StateConnecting indicates the client is attempting to connect StateConnecting // StateConnected indicates the client is connected and authenticated StateConnected // StateReconnecting indicates the client is attempting to reconnect StateReconnecting // StateClosed indicates the client has been closed StateClosed )
func (ConnectionState) String ¶
func (s ConnectionState) String() string
type ErrorInfo ¶
type ErrorInfo struct {
Code string `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
ErrorInfo contains error details
type Message ¶
type Message struct {
Type string `json:"type"`
RequestID string `json:"request_id,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
Error *ErrorInfo `json:"error,omitempty"`
}
Message represents a protocol message
func NewMessage ¶
NewMessage creates a new message
func NewMessageWithRequestID ¶
NewMessageWithRequestID creates a new message with a specific request ID
func ParseMessage ¶
ParseMessage parses a message from JSON bytes
func (*Message) GetRequestID ¶
GetRequestID returns the request ID
type MessageHistory ¶
type MessageHistory struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
Reasoning string `json:"reasoning,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
MessageHistory represents a message in session history
type ProgressData ¶
type ProgressData struct {
SessionID string `json:"session_id,omitempty"`
Message string `json:"message"`
Status string `json:"status,omitempty"`
ContextUsage int `json:"context_usage,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
VerificationAgent bool `json:"verification_agent,omitempty"`
IsCompact bool `json:"is_compact,omitempty"`
Reasoning string `json:"reasoning,omitempty"`
Mode string `json:"mode,omitempty"`
}
ProgressData represents a progress update
type QuestionRequest ¶
type QuestionRequest struct {
SessionID string `json:"session_id,omitempty"`
QuestionID string `json:"question_id"`
Question string `json:"question"`
MultiMode bool `json:"multi_mode"`
Questions map[string]string `json:"questions,omitempty"`
}
QuestionRequest represents a question request
type QuestionResponse ¶
type QuestionResponse struct {
QuestionID string `json:"question_id"`
Answer string `json:"answer,omitempty"`
Answers map[string]string `json:"answers,omitempty"`
}
QuestionResponse represents a question response
type SessionInfo ¶
type SessionInfo struct {
SessionID string `json:"session_id"`
Workspace string `json:"workspace"`
Title string `json:"title"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
MessageCount int `json:"message_count"`
Status string `json:"status"`
TotalTokens int64 `json:"total_tokens,omitempty"`
CachedTokens int64 `json:"cached_tokens,omitempty"`
TotalCost float64 `json:"total_cost,omitempty"`
MessageHistory []MessageHistory `json:"message_history,omitempty"`
}
SessionInfo represents session information
type SocketError ¶
SocketError represents an error from the socket server
func NewSocketError ¶
func NewSocketError(code, message, details string) *SocketError
NewSocketError creates a new SocketError
func (*SocketError) Error ¶
func (e *SocketError) Error() string
type ToolCall ¶
type ToolCall struct {
SessionID string `json:"session_id,omitempty"`
ToolName string `json:"tool_name"`
ToolID string `json:"tool_id"`
Parameters map[string]interface{} `json:"parameters"`
Description string `json:"description,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
ToolCall represents a tool call notification
type ToolResult ¶
type ToolResult struct {
SessionID string `json:"session_id,omitempty"`
ToolID string `json:"tool_id"`
Result *string `json:"result,omitempty"`
Error *string `json:"error,omitempty"`
Status string `json:"status,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
ToolResult represents a tool execution result
type WorkspaceInfo ¶
type WorkspaceInfo struct {
ID string `json:"id"`
Path string `json:"path"`
Name string `json:"name"`
RepositoryRoot string `json:"repository_root"`
CurrentBranch string `json:"current_branch"`
IsWorktree bool `json:"is_worktree"`
WorktreeName string `json:"worktree_name"`
SessionCount int `json:"session_count"`
LastAccessed string `json:"last_accessed"`
CreatedAt string `json:"created_at"`
ContextDirs []string `json:"context_dirs"`
LandlockRead []string `json:"landlock_read"`
LandlockWrite []string `json:"landlock_write"`
DomainsApproved map[string]bool `json:"domains_approved"`
CommandsApproved map[string]bool `json:"commands_approved"`
}
WorkspaceInfo represents workspace information