IDE-интеграция (ACP)
Creo работает как AI-ассистент внутри IDE через Agent Client Protocol (ACP) — открытый стандарт для интеграции AI-агентов с редакторами кода. ACP — альтернативный фронтенд к ядру агента (наряду с TUI), не содержащий собственной агентской логики.
Архитектура
graph TB
IDE["IDE-клиент<br/>(редактор)"]
ACP["ACP-сервер<br/>creo acp (stdio)"]
AgentImpl["AgentImpl<br/>ACP ↔ agent bridge"]
Agent["agent.Agent<br/>LLM цикл"]
Bus[("Bus<br/>in-process pub/sub")]
Manager["Session Manager"]
DB[("SQLite")]
AcpTools["ACP-proxied tools<br/>read, write, terminal"]
IDE <-->|"stdin/stdout<br/>JSON-RPC"| ACP
ACP --> AgentImpl
AgentImpl -->|"ensureAgent → Run"| Agent
Agent -->|"events"| Bus
AgentImpl -->|"subscribeSession"| Bus
Bus -->|"StreamingToken<br/>ToolCall/Status<br/>TurnEnd"| AgentImpl
AgentImpl -->|"SendText/SendThought<br/>StartToolCall/CompleteToolCall"| IDE
Agent --> AcpTools
AcpTools -->|"fs/read_text_file<br/>fs/write_text_file<br/>terminal/create"| IDE
AgentImpl --> Manager
Manager --> DB
Запуск
Команда creo acp запускает ACP-сервер на stdin/stdout:
1// cmd/creo — упрощённо
2agentImpl := NewAgent(deps)
3conn := acp.NewAgentSideConnection(agentImpl, os.Stdin, os.Stdout)
4agentImpl.SetClient(conn.Client())
5conn.Start(ctx)
IDE подключается через stdin/stdout. Диагностика пишется в stderr.
Deps — разделяемые зависимости
Deps передаётся при создании AgentImpl и живёт всё время жизни ACP-сервера:
1type Deps struct {
2 Bus bus.Bus
3 Cfg *config.Config
4 LLM llm.Provider
5 Provider string
6 ModelID string
7 Sessions *session.Manager
8 Repo *storage.SessionRepo
9 CPS func(cwd string) *checkpoint.Store
10}
Эти зависимости разделяются между TUI и ACP — один и тот же агентский движок.
Управление сессиями
ACP-сессии создаются клиентом (session/new) и лениво разрешаются в Creo-сессии
внутри агента.
| Метод ACP | Что делает |
|---|---|
NewSession |
Создаёт ACP-сессию сгенерированным ID, запоминает cwd |
LoadSession |
Открывает существующую Creo-сессию, replay истории через SendUserMessage |
ListSessions |
Список сессий, опционально отфильтрованный по cwd |
Cancel |
Отмена текущего хода: AbortRequestEvent в шину + context.Cancel |
Lazy agent creation
ensureAgent вызывается при первом Prompt для сессии:
Sessions.Open(sessionID)— попытка открыть существующую сессию- Если не существует —
Sessions.CreateWithID(cwd, sessionID) - Создание tool registry через
BuildACPRegistry - Генерация system message
- Создание
agent.AgentсWithAutoApprove()иWithCheckpoints
1func (a *AgentImpl) ensureAgent(sessionID, cwd string) (*agent.Agent, error) {
2 // ... open or create session ...
3 cps := a.deps.CPS(cwd)
4 registry := BuildACPRegistry(cwd, sess, ...)
5 agent.GenerateSystemMessage(sess, registry, cwd)
6 ag := agent.New(...).WithAutoApprove().WithCheckpoints(cps, cwd)
7 a.agents[sessionID] = ag
8 return ag, nil
9}
Делегирование файловых операций
В ACP-транспорте файловые операции делегируются IDE-клиенту через ACP RPC.
Вместо локальных read, write, terminal инструментов регистрируются
ACP-proxied аналоги:
| Локальный инструмент | ACP-аналог | ACP RPC |
|---|---|---|
read |
AcpReadTool |
fs/read_text_file |
write |
AcpWriteTool |
fs/write_text_file |
terminal |
AcpTerminalTool |
terminal/create |
BuildACPRegistry
1func BuildACPRegistry(...) *tool.Registry {
2 r := agent.BuildBaseRegistry(...) // calculate, lua, memory, agent, mcp, skills, ...
3 r.Register(acp.NewAcpRead(cwd)) // замена read
4 r.Register(acp.NewAcpWrite(cwd)) // замена write
5 r.Register(acp.NewAcpTerminal(cwd)) // замена terminal
6 return r
7}
Базовые инструменты (не работающие с файлами) остаются неизменными:
calculate, lua, memory, agent, agent_manage, agent_abort,
ask_user, mcp_register, skill, skillhub, checkpoint_*, search_sessions,
sessions, schedule, todo, kv, update_config.
AcpClient в контексте
ACP-клиент передаётся через context для proxied tools:
1toolCtx := tool.WithAcpClient(promptCtx, a.client, params.SessionID)
requireAcpClient извлекает клиент из контекста. Если клиент отсутствует —
инструмент возвращает ошибку (не ACP-транспорт).
Ограничения ACP-инструментов
| Параметр | TUI read |
ACP read |
|---|---|---|
path |
✅ | ✅ |
offset |
✅ | ❌ — IDE возвращает полный файл |
limit |
✅ | ❌ |
pages (PDF) |
✅ | ❌ |
Streaming — события агента в IDE
relayEvents подписывается на session-scoped bus events и транслирует их
в ACP session/update уведомления через SessionStream:
| Bus event | ACP stream method |
|---|---|
StreamingTokenEvent (kind="text") |
SendText |
StreamingTokenEvent (kind="thinking") |
SendThought |
ToolCallEvent |
StartToolCall |
ToolStatusEvent (done) |
CompleteToolCall |
ToolStatusEvent (error) |
FailToolCall |
AskUserEvent |
SendText (текст с вариантами) |
ErrorEvent |
сохранение ошибки |
TurnEndEvent |
завершение цикла |
1func (a *AgentImpl) relayEvents(ctx context.Context, done <-chan struct{}, sessionID string, stream *acp.SessionStream) error {
2 events := agent.SubscribeSession(ctx, a.deps.Bus, sessionID)
3 for {
4 select {
5 case ev := <-events:
6 switch ev := ev.(type) {
7 case bus.StreamingTokenEvent:
8 if ev.Kind == "thinking" {
9 stream.SendThought(ctx, ev.TextDelta)
10 } else {
11 stream.SendText(ctx, ev.TextDelta)
12 }
13 case bus.ToolCallEvent:
14 stream.StartToolCall(ctx, acp.ToolCallID(ev.ToolCallID), ev.Display, acp.ToolKindOther)
15 // ...
16 case bus.TurnEndEvent:
17 break consume
18 }
19 case <-done:
20 break consume
21 case <-ctx.Done():
22 break consume
23 }
24 }
25}
Replay истории (LoadSession)
При LoadSession история сессии replay'ится через stream:
UserMessage→SendUserMessageSkillMessage/CommandMessage(если естьDisplay) →SendUserMessageAssistantMessage→SendText(text content) +SendThought(thinking content)
SendUserMessage используется только при replay, никогда при live-промптах.
Недоступные инструменты через ACP
Следующие инструменты недоступны через ACP, так как файловые операции делегируются IDE, а остальные требуют локального git:
| Инструмент | Причина |
|---|---|
edit |
Файловые операции делегируются IDE (только read + write) |
glob |
Локальный поиск по файловой системе |
grep |
Локальный поиск по содержимому |
git_* (все) |
Требуют локальный .git репозиторий |
Ограничения по сравнению с TUI
- Терминал синхронный — без живого стриминга вывода команд.
- TUI-специфичные фичи недоступны — слэш-команды, мини-режим, темы, сайдбар,
интерактивный
ask_user-пикер. ask_userработает, но как текстовый вывод с вариантами (не интерактивный пикер).- AutoApprove — ACP-агент всегда работает в режиме авто-аппрува (
WithAutoApprove()), так как IDE-клиент управляет подтверждениями.
Файлы
| Файл | Назначение |
|---|---|
internal/acp/run.go |
Run — запуск ACP-сервера на stdin/stdout |
internal/acp/agent.go |
AgentImpl, Deps, NewAgent, SetClient, ACP capabilities |
internal/acp/agent_handlers.go |
Prompt, Cancel, NewSession, LoadSession, ListSessions, ensureAgent |
internal/acp/agent_relay.go |
relayEvents — трансляция bus events → ACP stream, replaySession |
internal/acp/registry.go |
BuildACPRegistry — tool registry для ACP-транспорта |
internal/tool/acp/read.go |
AcpReadTool — read через fs/read_text_file |
internal/tool/acp/write.go |
AcpWriteTool — write через fs/write_text_file |
internal/tool/acp/terminal.go |
AcpTerminalTool — terminal через terminal/create |
internal/tool/acp/shared.go |
requireAcpClient — извлечение ACP-клиента из context |