Adds a minimal but real ACP stack for the opencode process: - pkg/config: process.args default ["acp"] (opencodeCommand still "opencode") - internal/process: NewManager(command, args) — exec.Command uses args - internal/acp (new): NDJSON transport + JSON-RPC client over the existing process stdio. Implements initialize / session/new / session/prompt / session/cancel. Serves fs/read_text_file and fs/write_text_file from the workspace's fs.FileSystem. terminal/* requests get MethodNotFound. - internal/service/acp_service: per-workspace Client + mutex; starts the process on first prompt; transparently re-init on restart. - internal/api/acp_handler: GET /acp/status, GET /acp/history, POST /acp/prompt, POST /acp/cancel. - internal/model/acp: API DTOs. - internal/acp/client_test: NDJSON split-lines, request/response correlation, notification dispatch, agent-initiated request handling (fs + terminal). Existing process WS endpoint and Shell subsystem are unchanged. Conversation: 019f354c-a51b-7ec3-83ad-c647e9b50b19
70 lines
1.4 KiB
Go
70 lines
1.4 KiB
Go
package acp
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Message is one entry in the conversation history.
|
|
type Message struct {
|
|
Role string `json:"role"`
|
|
Text string `json:"text"`
|
|
Time time.Time `json:"time"`
|
|
}
|
|
|
|
// History accumulates prompt/response messages in memory for a session.
|
|
type History struct {
|
|
mu sync.RWMutex
|
|
messages []Message
|
|
}
|
|
|
|
// NewHistory creates an empty History.
|
|
func NewHistory() *History {
|
|
return &History{}
|
|
}
|
|
|
|
// Add appends a message with the current time.
|
|
func (h *History) Add(role, text string) {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
h.messages = append(h.messages, Message{Role: role, Text: text, Time: time.Now()})
|
|
}
|
|
|
|
// List returns a copy of all messages, oldest first.
|
|
func (h *History) List() []Message {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
out := make([]Message, len(h.messages))
|
|
copy(out, h.messages)
|
|
return out
|
|
}
|
|
|
|
// Len returns the number of stored messages.
|
|
func (h *History) Len() int {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
return len(h.messages)
|
|
}
|
|
|
|
// Since returns messages starting at index idx.
|
|
func (h *History) Since(idx int) []Message {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
if idx < 0 {
|
|
idx = 0
|
|
}
|
|
if idx > len(h.messages) {
|
|
idx = len(h.messages)
|
|
}
|
|
out := make([]Message, len(h.messages)-idx)
|
|
copy(out, h.messages[idx:])
|
|
return out
|
|
}
|
|
|
|
// Clear removes all stored messages.
|
|
func (h *History) Clear() {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
h.messages = h.messages[:0]
|
|
}
|