feat(opencode): переход на v2 HTTP API opencode (хардпин модели, поллинг вердикта)
- client.go: эндпоинты /api/* (create+model, prompt-admit, message, active, interrupt) - runner.go: неблокирующий prompt + поллинг новых assistant-сообщений; завершение = сессия ушла из активных дренажей + стабильное финальное сообщение - config.go: чтение top-level model из opencode.jsonc (JSONC-стрип) + хардпин в сессию - server.go: healthcheck /api/health, MinVersion=1.18.18, понятная ошибка для старого бинаря - класс O5 WARN: устойчивость к v1-конфигу провайдера (npm/options игнорируются v2) - README: раздел интеграции, минимальная версия opencode, предупреждения - .serena: актуализация памяти (core, tech_stack)
This commit is contained in:
@@ -2,6 +2,7 @@ package opencode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
|
||||
// fakePool создаёт Pool, в котором уже «живёт» сервер для каталога (без spawn):
|
||||
// Server{URL: fake.URL}, поэтому Runner ходит по HTTP на фейк-API.
|
||||
// XDG_CONFIG_HOME уводится во временный каталог, чтобы ReadModelRef не читал
|
||||
// реальный пользовательский конфиг opencode (детерминизм тестов).
|
||||
func fakePool(t *testing.T, f *fakeAPIServer, dir string) (*Pool, *Client) {
|
||||
t.Helper()
|
||||
ts := httptestURL(t, f)
|
||||
@@ -28,13 +31,12 @@ func httptestURL(t *testing.T, f *fakeAPIServer) string {
|
||||
}
|
||||
|
||||
func TestRun_Success(t *testing.T) {
|
||||
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
||||
dir := t.TempDir()
|
||||
f := &fakeAPIServer{
|
||||
verdictParts: []part{{Type: "text", Text: "done"}},
|
||||
}
|
||||
f := &fakeAPIServer{verdictText: "done"}
|
||||
p, _ := fakePool(t, f, dir)
|
||||
|
||||
r := &Runner{Pool: p, PollInterval: 5 * time.Millisecond}
|
||||
r := &Runner{Pool: p, PollInterval: 5 * time.Millisecond, Stdout: io.Discard}
|
||||
res, err := r.Run(context.Background(), "task", dir, "dev", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Run err: %v", err)
|
||||
@@ -51,13 +53,14 @@ func TestRun_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRun_IdleTimeout(t *testing.T) {
|
||||
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
||||
dir := t.TempDir()
|
||||
// prompt блокируется (агент «завис»), прогресс не растёт → idle abort
|
||||
// агент «завис»: active=true, прогресс не растёт → idle abort
|
||||
f := &fakeAPIServer{blockPrompt: true}
|
||||
p, _ := fakePool(t, f, dir)
|
||||
|
||||
r := &Runner{Pool: p, IdleTimeout: 30 * time.Millisecond,
|
||||
PollInterval: 5 * time.Millisecond}
|
||||
PollInterval: 5 * time.Millisecond, Stdout: io.Discard}
|
||||
res, err := r.Run(context.Background(), "task", dir, "dev", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Run err: %v", err)
|
||||
@@ -68,13 +71,14 @@ func TestRun_IdleTimeout(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRun_ContextCancel(t *testing.T) {
|
||||
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
||||
dir := t.TempDir()
|
||||
f := &fakeAPIServer{blockPrompt: true}
|
||||
p, _ := fakePool(t, f, dir)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
r := &Runner{Pool: p, IdleTimeout: time.Minute, HardTimeout: time.Minute,
|
||||
PollInterval: 5 * time.Millisecond}
|
||||
PollInterval: 5 * time.Millisecond, Stdout: io.Discard}
|
||||
done := make(chan *Result, 1)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
|
||||
Reference in New Issue
Block a user