Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion edge-server/internal/adapters/acp.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func acpEnvPassthrough(keys []string) []string {
// ParseStream runs one ACP turn via the coder/acp-go-sdk client runtime:
// initialize handshake → session/new → session/prompt → session/update
// stream → prompt response. See runACPSession in acp_client.go for the
// full flow and the TODO list (#1404: fs/terminal frames, live adapter
// full flow and the TODO list (#1743 (follow-up of #1404): fs/terminal frames, live adapter
// verification). session/request_permission is bridged to the Edge
// approval chain via the permission broker installed by
// SetPermissionBroker.
Expand Down
18 changes: 9 additions & 9 deletions edge-server/internal/adapters/acp_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const acpSDKVersion = "v0.13.5"
// a JSON-RPC error instead of a silent hang. session/request_permission no
// longer goes through this sentinel — it is bridged to the Edge approval
// chain via PermissionDecisionBroker.
var errACPEndpointNotWired = errors.New("acp: endpoint not wired (TODO #1404: frame design)")
var errACPEndpointNotWired = errors.New("acp: endpoint not wired (TODO #1743 (follow-up of #1404): frame design)")

// acpClientHandler is the coder/acp-go-sdk client side. The SDK dispatches
// inbound JSON-RPC to the acp.Client interface methods:
Expand Down Expand Up @@ -254,7 +254,7 @@ func firstPermissionOption(options []acp.PermissionOption, kinds ...acp.Permissi
// ── Unwired endpoint stubs (#1404) ────────────────────────────────────────
//
// STUB INVENTORY (single source of the "not wired" list — update both the
// methods below and TestUnwiredACPEndpointsFailClosed when #1404 lands):
// methods below and TestUnwiredACPEndpointsFailClosed when #1743 lands):
//
// fs/read_text_file → ReadTextFile (fs frame design + allowlist)
// fs/write_text_file → WriteTextFile (fs frame design + allowlist)
Expand All @@ -270,7 +270,7 @@ func firstPermissionOption(options []acp.PermissionOption, kinds ...acp.Permissi
// Every stub fails closed with errACPEndpointNotWired — a JSON-RPC error the
// agent can surface — never a silent hang, never a fake success. The
// capabilities are still advertised in initialize (runACPSession) so the
// agent can discover them; removing the advertisement is a #1404 follow-up,
// agent can discover them; removing the advertisement is a #1743 follow-up,
// not something a stub should silently do.
//
// ReadTextFile handles fs/read_text_file.
Expand All @@ -284,7 +284,7 @@ func (h *acpClientHandler) WriteTextFile(ctx context.Context, params acp.WriteTe
}

func fsEndpointError(method, path string) error {
return fmt.Errorf("acp: %s %q not wired (TODO #1404: Edge fs frame design + allowlist): %w",
return fmt.Errorf("acp: %s %q not wired (TODO #1743 (follow-up of #1404): Edge fs frame design + allowlist): %w",
method, path, errACPEndpointNotWired)
}

Expand Down Expand Up @@ -314,7 +314,7 @@ func (h *acpClientHandler) WaitForTerminalExit(ctx context.Context, params acp.W
}

func terminalEndpointError(method string) error {
return fmt.Errorf("acp: %s not wired (TODO #1404: Edge terminal frame design): %w", method, errACPEndpointNotWired)
return fmt.Errorf("acp: %s not wired (TODO #1743 (follow-up of #1404): Edge terminal frame design): %w", method, errACPEndpointNotWired)
}

// runACPSession runs one ACP turn with the SDK client runtime: initialize
Expand Down Expand Up @@ -352,7 +352,7 @@ func runACPSession(ctx context.Context, stdout io.Reader, stdin io.Writer, emitt

// 1. initialize handshake. Capabilities: fs read/write + terminal are
// advertised; the endpoints answer with errors until the fs/terminal
// frame design lands (#1404; see handler TODO comments). Tool permission
// frame design lands (#1743; see handler TODO comments). Tool permission
// gates use session/request_permission, which is bridged to the Edge
// approval chain.
initResp, err := conn.Initialize(ctx, acp.InitializeRequest{
Expand All @@ -375,7 +375,7 @@ func runACPSession(ctx context.Context, stdout io.Reader, stdin io.Writer, emitt
}

// 2. session/new. MCP server config from the run profile is not yet wired
// into the ACP session (frame design deferred, #1404). When a profile
// into the ACP session (frame design deferred (#1743, follow-up of #1404)). When a profile
// declares MCP servers, surface the gap as a visible status-change event
// + warning log so the user sees *why* MCP tools are unavailable instead
// of a silent absence.
Expand All @@ -390,7 +390,7 @@ func runACPSession(ctx context.Context, stdout io.Reader, stdin io.Writer, emitt
}
sessResp, err := conn.NewSession(ctx, acp.NewSessionRequest{
Cwd: rc.WorkDir,
McpServers: []acp.McpServer{}, // TODO(#1404): wire RunProcessContext.MCPConfig
McpServers: []acp.McpServer{}, // TODO(#1743): wire RunProcessContext.MCPConfig
Comment thread
DeliciousBuding marked this conversation as resolved.
})
if err != nil {
return NewNonRecoverableParseError(fmt.Errorf("acp: session/new failed: %w", err))
Expand All @@ -399,7 +399,7 @@ func runACPSession(ctx context.Context, stdout io.Reader, stdin io.Writer, emitt

// 3. session/prompt. During this call the SDK dispatches all inbound
// session/update notifications (→ SessionUpdate → run.agent.*) and
// blocking requests (→ handler TODOs, #1404) concurrently.
// blocking requests (→ handler #1743) concurrently.
slog.Debug("acp: sending session/prompt", "run_id", run.ID, "session_id", sessResp.SessionId)
promptResp, err := conn.Prompt(ctx, acp.PromptRequest{
SessionId: sessResp.SessionId,
Expand Down
2 changes: 1 addition & 1 deletion edge-server/internal/httpserver/server_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func debugAuthFunc(cfg Config) func(r *http.Request) bool {
if cfg.HubJWTSecret != "" {
return hubJWTDebugAuth(cfg.HubJWTSecret, cfg.EdgeDeviceID)
}
return nil
return func(r *http.Request) bool { return false }
}

// hubJWTDebugAuth returns an auth predicate that accepts a Bearer Hub JWT
Expand Down
18 changes: 12 additions & 6 deletions edge-server/internal/httpserver/server_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"testing"
"time"

"github.com/agenthub/edge-server/internal/jwtutil"
"github.com/golang-jwt/jwt/v5"
)

Expand Down Expand Up @@ -148,11 +147,18 @@ func TestDebugAuthFunc_HubJWTFallbackRejectsWrongDeviceID(t *testing.T) {
}
}

func TestDebugAuthFunc_NoAuthConfiguredReturnsNil(t *testing.T) {
// Dev false, both secrets empty: equivalent to local dev open mode.
func TestDebugAuthFunc_NoAuthConfiguredDeniesAll(t *testing.T) {
// Dev false, both secrets empty: deny-all so debug endpoints are not
// exposed without explicit auth configuration. Run() auto-generates a
// LocalAuthToken before calling debugAuthFunc, making this branch
// unreachable in production — but the gate itself must fail closed.
cfg := Config{}
if auth := debugAuthFunc(cfg); auth != nil {
t.Fatalf("expected nil auth when no auth configured, got non-nil")
auth := debugAuthFunc(cfg)
if auth == nil {
t.Fatal("expected non-nil deny-all auth when no auth configured, got nil")
}
req := httptest.NewRequest(http.MethodGet, "/debug/pprof/", nil)
if auth(req) {
t.Fatal("deny-all auth must reject all requests")
}
_ = jwtutil.ErrTokenInvalid // keep jwtutil import meaningful if assertions evolve
}
Loading