Merge branch 'codex/fix-desktop-provider-catalog'
This commit is contained in:
commit
e2f2b4ddc1
@ -3,8 +3,6 @@ package acp
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"xworkmate/go_core/internal/shared"
|
||||
)
|
||||
|
||||
type syncedProvider struct {
|
||||
@ -66,9 +64,6 @@ func (s *Server) syncedProviderByID(providerID string) (syncedProvider, bool) {
|
||||
|
||||
func (s *Server) availableProviders() []string {
|
||||
providers := make(map[string]struct{})
|
||||
for _, provider := range shared.DetectACPProviders() {
|
||||
providers[provider] = struct{}{}
|
||||
}
|
||||
s.mu.Lock()
|
||||
for _, provider := range s.providerCatalog {
|
||||
if !provider.Enabled || strings.TrimSpace(provider.Endpoint) == "" {
|
||||
|
||||
@ -4,11 +4,34 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"xworkmate/go_core/internal/shared"
|
||||
)
|
||||
|
||||
func TestCapabilitiesIgnoreLocalProviderAutodetectUntilSync(t *testing.T) {
|
||||
fakeProvider := t.TempDir() + "/fake-claude"
|
||||
if err := os.WriteFile(fakeProvider, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake provider: %v", err)
|
||||
}
|
||||
t.Setenv("ACP_CLAUDE_BIN", fakeProvider)
|
||||
|
||||
server := NewServer()
|
||||
result, rpcErr := server.handleRequest(shared.RPCRequest{
|
||||
Method: "acp.capabilities",
|
||||
Params: map[string]any{},
|
||||
}, func(map[string]any) {})
|
||||
if rpcErr != nil {
|
||||
t.Fatalf("expected capabilities success, got %v", rpcErr)
|
||||
}
|
||||
|
||||
providers, _ := result["providers"].([]string)
|
||||
if len(providers) != 0 {
|
||||
t.Fatalf("expected no providers before sync, got %#v", providers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersSyncUpdatesCapabilities(t *testing.T) {
|
||||
server := NewServer()
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package acp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -11,6 +12,36 @@ import (
|
||||
"xworkmate/go_core/internal/shared"
|
||||
)
|
||||
|
||||
func newExternalSingleAgentProvider(
|
||||
t *testing.T,
|
||||
providerID string,
|
||||
output string,
|
||||
) *httptest.Server {
|
||||
t.Helper()
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/acp/rpc" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
var request map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
t.Fatalf("decode request: %v", err)
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": request["id"],
|
||||
"result": map[string]any{
|
||||
"success": true,
|
||||
"output": output,
|
||||
"turnId": "turn-" + providerID,
|
||||
"provider": providerID,
|
||||
"mode": "single-agent",
|
||||
},
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
func TestHandleRoutingResolveCoversNineScenarioBuckets(t *testing.T) {
|
||||
localAvailableSkills := []map[string]any{
|
||||
{"id": "pptx", "label": "PPTX", "description": "slides", "installed": true},
|
||||
@ -139,19 +170,17 @@ func TestExecuteSessionTaskAutoRoutingRecordsProjectMemory(t *testing.T) {
|
||||
t.Fatalf("create workspace: %v", err)
|
||||
}
|
||||
|
||||
fakeProvider := filepath.Join(t.TempDir(), "fake-claude.sh")
|
||||
if err := os.WriteFile(
|
||||
fakeProvider,
|
||||
[]byte("#!/bin/sh\nprintf 'done'\n"),
|
||||
0o755,
|
||||
); err != nil {
|
||||
t.Fatalf("write fake provider: %v", err)
|
||||
}
|
||||
|
||||
t.Setenv("HOME", homeDir)
|
||||
t.Setenv("ACP_CLAUDE_BIN", fakeProvider)
|
||||
|
||||
server := NewServer()
|
||||
providerServer := newExternalSingleAgentProvider(t, "claude", "done")
|
||||
defer providerServer.Close()
|
||||
server.syncProviders([]syncedProvider{{
|
||||
ProviderID: "claude",
|
||||
Label: "Claude",
|
||||
Endpoint: providerServer.URL,
|
||||
Enabled: true,
|
||||
}})
|
||||
response, rpcErr := server.executeSessionTask(task{
|
||||
req: shared.RPCRequest{
|
||||
Params: map[string]any{
|
||||
@ -212,19 +241,17 @@ func TestExecuteSessionTaskExplicitRoutingDoesNotRecordProjectMemory(t *testing.
|
||||
t.Fatalf("create workspace: %v", err)
|
||||
}
|
||||
|
||||
fakeProvider := filepath.Join(t.TempDir(), "fake-claude.sh")
|
||||
if err := os.WriteFile(
|
||||
fakeProvider,
|
||||
[]byte("#!/bin/sh\nprintf 'done'\n"),
|
||||
0o755,
|
||||
); err != nil {
|
||||
t.Fatalf("write fake provider: %v", err)
|
||||
}
|
||||
|
||||
t.Setenv("HOME", homeDir)
|
||||
t.Setenv("ACP_CLAUDE_BIN", fakeProvider)
|
||||
|
||||
server := NewServer()
|
||||
providerServer := newExternalSingleAgentProvider(t, "claude", "done")
|
||||
defer providerServer.Close()
|
||||
server.syncProviders([]syncedProvider{{
|
||||
ProviderID: "claude",
|
||||
Label: "Claude",
|
||||
Endpoint: providerServer.URL,
|
||||
Enabled: true,
|
||||
}})
|
||||
response, rpcErr := server.executeSessionTask(task{
|
||||
req: shared.RPCRequest{
|
||||
Params: map[string]any{
|
||||
@ -270,6 +297,34 @@ func TestExecuteSessionTaskExplicitRoutingDoesNotRecordProjectMemory(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteSessionTaskExplicitProviderRequiresSyncedCatalog(t *testing.T) {
|
||||
server := NewServer()
|
||||
response, rpcErr := server.executeSessionTask(task{
|
||||
req: shared.RPCRequest{
|
||||
Method: "session.start",
|
||||
Params: map[string]any{
|
||||
"sessionId": "session-explicit-provider",
|
||||
"threadId": "thread-explicit-provider",
|
||||
"taskPrompt": "create a powerpoint deck for launch",
|
||||
"routing": map[string]any{
|
||||
"routingMode": "explicit",
|
||||
"explicitExecutionTarget": "singleAgent",
|
||||
"explicitProviderId": "claude",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if rpcErr != nil {
|
||||
t.Fatalf("expected structured unavailable response, got rpc error: %v", rpcErr)
|
||||
}
|
||||
if got := response["unavailable"]; got != true {
|
||||
t.Fatalf("expected unavailable response, got %#v", response)
|
||||
}
|
||||
if got := response["unavailableCode"]; got != "PROVIDER_UNAVAILABLE" {
|
||||
t.Fatalf("expected PROVIDER_UNAVAILABLE, got %#v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteSessionTaskRequiresRouting(t *testing.T) {
|
||||
server := NewServer()
|
||||
_, rpcErr := server.executeSessionTask(task{
|
||||
|
||||
@ -78,8 +78,8 @@ func (r Resolver) Resolve(req Request) Result {
|
||||
availableProviders := normalizeProviders(req.AvailableProviders)
|
||||
|
||||
result := Result{
|
||||
ResolvedModel: strings.TrimSpace(req.ExplicitModel),
|
||||
MemorySources: mem.Sources,
|
||||
ResolvedModel: strings.TrimSpace(req.ExplicitModel),
|
||||
MemorySources: mem.Sources,
|
||||
}
|
||||
|
||||
result.ResolvedExecutionTarget, result.ResolvedEndpointTarget = r.resolveExecution(req, mem.Preferences)
|
||||
@ -226,9 +226,6 @@ func resolveProvider(
|
||||
) (string, bool, string, string) {
|
||||
explicitProviderID := normalize(strings.TrimSpace(req.ExplicitProviderID))
|
||||
if explicitProviderID != "" {
|
||||
if len(availableProviders) == 0 {
|
||||
return explicitProviderID, false, "", ""
|
||||
}
|
||||
if containsProvider(availableProviders, explicitProviderID) {
|
||||
return explicitProviderID, false, "", ""
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ func TestResolveExplicitTargetOverridesAuto(t *testing.T) {
|
||||
ExplicitExecutionTarget: "singleAgent",
|
||||
ExplicitProviderID: "codex",
|
||||
ExplicitModel: "gpt-5.4",
|
||||
AvailableProviders: []string{"codex"},
|
||||
})
|
||||
|
||||
if result.ResolvedExecutionTarget != ExecutionTargetSingleAgent {
|
||||
@ -39,6 +40,28 @@ func TestResolveExplicitTargetOverridesAuto(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveExplicitProviderRequiresAvailability(t *testing.T) {
|
||||
resolver := Resolver{
|
||||
SkillFinder: skills.StaticFinder{},
|
||||
SkillInstaller: nil,
|
||||
MemoryService: memory.Service{},
|
||||
}
|
||||
|
||||
result := resolver.Resolve(Request{
|
||||
Prompt: "search the web and summarize results",
|
||||
RoutingMode: RoutingModeExplicit,
|
||||
ExplicitExecutionTarget: "singleAgent",
|
||||
ExplicitProviderID: "codex",
|
||||
})
|
||||
|
||||
if !result.Unavailable {
|
||||
t.Fatalf("expected explicit provider to be unavailable without synced catalog, got %#v", result)
|
||||
}
|
||||
if result.UnavailableCode != "PROVIDER_UNAVAILABLE" {
|
||||
t.Fatalf("expected PROVIDER_UNAVAILABLE, got %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAutoLocalTaskToSingleAgent(t *testing.T) {
|
||||
resolver := Resolver{
|
||||
SkillFinder: skills.StaticFinder{},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user