Skip to content

feat: implement acp handler and communication layer with the sidecar#8294

Open
Nabil-Salah wants to merge 11 commits intojaegertracing:mainfrom
Nabil-Salah:develop_acp_handler
Open

feat: implement acp handler and communication layer with the sidecar#8294
Nabil-Salah wants to merge 11 commits intojaegertracing:mainfrom
Nabil-Salah:develop_acp_handler

Conversation

@Nabil-Salah
Copy link
Copy Markdown
Contributor

@Nabil-Salah Nabil-Salah commented Apr 5, 2026

Which problem is this PR solving?

Description of the changes

  • Added AI proxy configuration for Jaeger Query via extensions.jaeger_query.ai.agent_url (default ws://localhost:9000) and wired it into query options.

  • Registered a new chat endpoint POST /api/ai/chat, enabled only when ai.agent_url is configured and non-empty.

  • Introduced a new jaegerai communication layer that:

    • connects to the sidecar over WebSocket,
    • establishes ACP client connection (Initialize, NewSession, Prompt),
    • streams text responses back to HTTP clients,
    • handles end-of-turn/timeout and error paths.
  • Added required dependencies for ACP/WebSocket support (github.com/coder/acp-go-sdk, github.com/gorilla/websocket).

  • continue of work to this pr (feat: Add sidecar example for jaeger agent #8276)

How was this change tested?

  • Added/updated unit tests covering:
    • default AI config values,
    • route registration behavior (enabled/disabled + base path),
    • ACP handshake/prompt flow in the chat handler,
    • handler error paths (bad method/body, dial/init/session/prompt failures),
    • streaming client behavior and websocket adapter behavior.
  • Result: all tests in that scope passed.

Checklist

AI Usage in this PR (choose one)

See AI Usage Policy.

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

…agent

Signed-off-by: Nabil-Salah <nabil.salah203@gmail.com>
@Nabil-Salah Nabil-Salah requested a review from a team as a code owner April 5, 2026 19:20
Copilot AI review requested due to automatic review settings April 5, 2026 19:20
@dosubot dosubot bot added the enhancement label Apr 5, 2026
@Nabil-Salah Nabil-Salah changed the title feat: implement acp handler and communication layer with the sidecar … feat: implement acp handler and communication layer with the sidecar Apr 5, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an AI gateway capability to the Jaeger Query service by introducing an /api/ai/chat endpoint that proxies chat prompts to an external ACP-capable sidecar over WebSockets, streaming the response back to the HTTP client.

Changes:

  • Added extensions.jaeger_query.ai.agent_url configuration and wired it into Jaeger Query options and routing.
  • Implemented a jaegerai communication layer (WebSocket adapter, ACP client connection, streaming response handling).
  • Added unit tests covering config defaults, route registration, ACP handshake/prompt flows, and error/streaming behaviors.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
go.mod Adds ACP SDK + gorilla/websocket dependencies needed for sidecar communication.
go.sum Adds checksums for new dependencies.
cmd/jaeger/internal/extension/jaegerquery/internal/server.go Registers /api/ai/chat route when AI config is enabled.
cmd/jaeger/internal/extension/jaegerquery/internal/server_test.go Tests route registration behavior with/without base path and enabled/disabled config.
cmd/jaeger/internal/extension/jaegerquery/internal/flags.go Introduces AI config types and defaults in Query options.
cmd/jaeger/internal/extension/jaegerquery/internal/flags_test.go Tests default AI config values.
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/handler.go Implements the HTTP chat handler and ACP handshake/session/prompt flow over WebSocket.
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/handler_test.go Tests handler success path and error paths (dial/init/session/prompt failures).
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/streaming_client.go Implements ACP client callbacks and streaming text writes to HTTP response.
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/streaming_client_test.go Tests streaming client write/flush behavior and ACP callback handling.
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/ws_adapter.go Adds io.ReadWriteCloser adapter around gorilla/websocket for ACP SDK.
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/ws_adapter_test.go Tests adapter behavior for round-trip, EOF handling, and close/write errors.
cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/package_test.go Adds leak-checking TestMain for the new package.
cmd/jaeger/config.yaml Adds example AI configuration stanza under extensions.jaeger_query.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +88 to +98
func (*streamingClient) RequestPermission(_ context.Context, p acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
if len(p.Options) == 0 {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},
},
}, nil
}
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Selected: &acp.RequestPermissionOutcomeSelected{OptionId: p.Options[0].OptionId},
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestPermission automatically selects the first option whenever options are provided. This effectively grants permissions without any user/admin consent and could let a sidecar perform actions it should not be allowed to. Safer default is to always return a cancelled/denied outcome (or gate auto-approval behind an explicit config option).

Suggested change
func (*streamingClient) RequestPermission(_ context.Context, p acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
if len(p.Options) == 0 {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},
},
}, nil
}
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Selected: &acp.RequestPermissionOutcomeSelected{OptionId: p.Options[0].OptionId},
func (*streamingClient) RequestPermission(_ context.Context, _ acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},

Copilot uses AI. Check for mistakes.
Comment on lines +130 to +145
// This blocks until the sidecar completes the ACP prompt turn.
_, err = acpConn.Prompt(acpCtx, acp.PromptRequest{
SessionId: sess.SessionId,
Prompt: []acp.ContentBlock{acp.TextBlock(req.Prompt)},
})
if err != nil {
w.WriteHeader(http.StatusBadGateway)
if _, writeErr := fmt.Fprintf(w, "Error starting prompt: %v\n", err); writeErr != nil {
h.Logger.Warn("Failed to write prompt error response", zap.Error(writeErr))
}
return
}

// Wait for explicit end-of-turn marker from the sidecar, with a 3 mins timeout fallback.
clientImpl.waitForTurnCompletion(acpCtx, 180*time.Second)
}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After Prompt returns successfully, the handler waits for an out-of-band __END_OF_TURN__ marker to close doneCh. This marker is not part of ACP itself and requires sidecars to implement a Jaeger-specific convention; if they don’t, requests will hang until the 180s timeout. Consider treating a successful Prompt response (e.g., StopReasonEndTurn) as completion and signaling doneCh immediately, keeping the timeout only as a safeguard for the RPC itself.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch - why do we need an explicit __END_OF_TURN__ signal instead of relying on the ACP protocol semantics?

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.67%. Comparing base (1737376) to head (c324a67).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8294      +/-   ##
==========================================
+ Coverage   95.61%   95.67%   +0.05%     
==========================================
  Files         314      317       +3     
  Lines       16505    16705     +200     
==========================================
+ Hits        15782    15982     +200     
  Misses        570      570              
  Partials      153      153              
Flag Coverage Δ
badger_direct 9.31% <ø> (ø)
badger_e2e 1.07% <ø> (ø)
cassandra-4.x-direct-manual 13.63% <ø> (ø)
cassandra-4.x-e2e-auto 1.06% <ø> (ø)
cassandra-4.x-e2e-manual 1.06% <ø> (ø)
cassandra-5.x-direct-manual 13.63% <ø> (ø)
cassandra-5.x-e2e-auto 1.06% <ø> (ø)
cassandra-5.x-e2e-manual 1.06% <ø> (ø)
clickhouse 1.20% <ø> (ø)
elasticsearch-6.x-direct 17.49% <ø> (ø)
elasticsearch-7.x-direct 17.53% <ø> (ø)
elasticsearch-8.x-direct 17.68% <ø> (ø)
elasticsearch-8.x-e2e 1.07% <ø> (ø)
elasticsearch-9.x-e2e 1.07% <ø> (ø)
grpc_direct 8.09% <ø> (ø)
grpc_e2e 1.07% <ø> (ø)
kafka-3.x-v2 1.07% <ø> (ø)
memory_v2 1.07% <ø> (ø)
opensearch-1.x-direct 17.57% <ø> (ø)
opensearch-2.x-direct 17.57% <ø> (ø)
opensearch-2.x-e2e 1.07% <ø> (ø)
opensearch-3.x-e2e 1.07% <ø> (ø)
query 1.07% <ø> (ø)
tailsampling-processor 0.54% <ø> (ø)
unittests 94.33% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

}

// Wait for explicit end-of-turn marker from the sidecar, with a 3 mins timeout fallback.
clientImpl.waitForTurnCompletion(acpCtx, 180*time.Second)
Copy link
Copy Markdown
Member

@yurishkuro yurishkuro Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

180*time.Second - move to configurable param wait_for_turn_timeout

}
}
if u.ToolCall != nil {
c.writeAndFlush(fmt.Sprintf("\n[tool_call] %s\n", u.ToolCall.Title))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the significance of the string formatting [tool_call] ...? Is something meant to be parsing it? Is it part of some protocol? Please document with comments

return string(*v)
}

func (*streamingClient) WriteTextFile(_ context.Context, _ acp.WriteTextFileRequest) (acp.WriteTextFileResponse, error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of these functions are rather odd - who is expected to call them? Shouldn't they all be returning "not implemented" error if we don't expect anyone to call them?

c.flusher.Flush()
}

func (c *streamingClient) waitForTurnCompletion(ctx context.Context, maxWait time.Duration) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing seems to be calling this function

func DefaultQueryOptions() QueryOptions {
return QueryOptions{
MaxClockSkewAdjust: 0, // disabled by default
AI: configoptional.Some(AIConfig{AgentURL: "ws://localhost:9000"}),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be configoptional.Default(...)

Copilot AI review requested due to automatic review settings April 6, 2026 20:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +90 to +100
func (*streamingClient) RequestPermission(_ context.Context, p acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
if len(p.Options) == 0 {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},
},
}, nil
}
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Selected: &acp.RequestPermissionOutcomeSelected{OptionId: p.Options[0].OptionId},
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestPermission auto-selects the first permission option when any are provided. This effectively grants whatever the agent asks for without user consent/policy, which is risky for an AI gateway. Safer default is to always cancel/deny, or gate approvals behind explicit config/policy and restrict to a known-safe subset of permission kinds.

Suggested change
func (*streamingClient) RequestPermission(_ context.Context, p acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
if len(p.Options) == 0 {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},
},
}, nil
}
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Selected: &acp.RequestPermissionOutcomeSelected{OptionId: p.Options[0].OptionId},
func (*streamingClient) RequestPermission(_ context.Context, _ acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},

Copilot uses AI. Check for mistakes.

type AIConfig struct {
// AgentURL is the WebSocket endpoint of an agent that supports ACP.
// See https://agentclientprotocol.com/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// See https://agentclientprotocol.com/
// For example, ws://localhost:9000
// See https://agentclientprotocol.com/

defer cancel()

clientVersion := version.Get().GitVersion
if clientVersion == "" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can skip it -- #8310

// Build an ACP client-side connection over the websocket adapter.
acpConn := acp.NewClientSideConnection(clientImpl, adapter, adapter)

acpCtx, cancel := context.WithCancel(ctx)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not do this at L79?

w.Header().Set("Connection", "keep-alive")

ctx := r.Context()
dialer := websocket.Dialer{HandshakeTimeout: 5 * time.Second}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this initialization should be moved into NewWsAdapter(), since it's part of websocket handling and the conn variable created in this scope is never used again. Instead you would call defer adapter.Close()


func (h *ChatHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
http.Error(w, "Only POST method is supported", http.StatusMethodNotAllowed)

)

const (
endOfTurnMarker = "__END_OF_TURN__"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's your take on this observation?

The acp.Client interface has two types of methods:

Request/Response methods (agent calls client, blocks waiting for response):

  • ReadTextFile, WriteTextFile - file operations
  • RequestPermission - permission dialogs
  • CreateTerminal, TerminalOutput, etc. - terminal operations

Notification method (one-way, fire-and-forget):

  • SessionUpdate - streams real-time progress and results during prompt processing

SessionUpdate carries only informational content for UI streaming:

  • AgentMessageChunk - streamed text from the agent
  • AgentThoughtChunk - agent's internal reasoning
  • ToolCall / ToolCallUpdate - notifications that the agent has initiated/completed a tool call (for UI progress display, not execution requests)
  • Plan - execution plan for complex tasks

In Jaeger's architecture, the sidecar executes MCP tools by calling Jaeger's MCP
server directly over HTTP. The SessionUpdate(ToolCall) notifications merely
inform the UI that a tool is running - they do not ask the client to execute
anything.

End-of-Turn Handling

The agent will not return PromptResponse until all actual work (including tool
execution via MCP) is complete. By the time Prompt() returns:

  1. All tool calls have finished (via MCP)
  2. All SessionUpdate notifications have been sent

The current implementation uses an __END_OF_TURN__ marker as a SessionUpdate
to signal completion. This works around a minor race condition in acp-go-sdk
where notifications are processed in goroutines (go c.handleInbound(&msg) in
connection.go:97), meaning SessionUpdate callbacks might still be running
when PromptResponse arrives and Prompt() returns.

Since SessionUpdate handlers just write to the HTTP response (fast operation),
this race is minimal. The marker may be over-engineering - a potential
simplification would be to remove the marker and return immediately after
Prompt() completes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but the marker costs almost nothing, so why not keep on it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it costs the complexity of implementation and it's not part of the protocol we're trying to implement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay will update my code

…nd Return errNotSupported

Signed-off-by: Nabil-Salah <nabil.salah203@gmail.com>
Signed-off-by: Nabil-Salah <nabil.salah203@gmail.com>
Copilot AI review requested due to automatic review settings April 9, 2026 20:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +84 to +86
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler sets a Connection: keep-alive response header. This is a hop-by-hop header that net/http manages automatically and can be problematic with proxies and is not applicable to HTTP/2. Consider removing this header and relying on the server’s connection handling (keeping Cache-Control: no-cache etc. for streaming).

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +89
func (c *streamingClient) waitForTurnCompletion(ctx context.Context, maxWait time.Duration) {
if maxWait <= 0 {
return
}

maxTimer := time.NewTimer(maxWait)
defer maxTimer.Stop()

select {
case <-ctx.Done():
return
case <-maxTimer.C:
return
case <-c.doneCh:
return
}
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waitForTurnCompletion can only return early via c.doneCh, but doneCh is never signaled on the normal success path (it’s only closed on request cancel, write error, or panic). As a result, successful requests will always wait the full maxWait duration, adding fixed latency without actually confirming that all SessionUpdate goroutines have drained. Consider either (a) simplifying this to a plain sleep with a clarifying name/comment, or (b) implementing an actual “idle/drained” signal (e.g., track last-write time and wait until no writes occur for N ms, or close doneCh via an explicit end-of-turn notification if ACP provides one).

Copilot uses AI. Check for mistakes.
Signed-off-by: Nabil-Salah <nabil.salah203@gmail.com>
Signed-off-by: Nabil-Salah <nabil.salah203@gmail.com>
Copilot AI review requested due to automatic review settings April 9, 2026 22:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +47 to +55
// Limit the size of the request body to prevent memory/CPU abuse.
r.Body = http.MaxBytesReader(w, r.Body, h.maxRequestBodySize)
defer r.Body.Close()

var req ChatRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request body is wrapped with http.MaxBytesReader, but JSON decode errors are always returned as 400. When the body exceeds maxRequestBodySize, Decode will fail with *http.MaxBytesError and should return 413 (Request Entity Too Large) so clients can distinguish oversized payloads from malformed JSON.

Copilot uses AI. Check for mistakes.
Signed-off-by: Nabil-Salah <nabil.salah203@gmail.com>
Copilot AI review requested due to automatic review settings April 9, 2026 22:46
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +56
var req ChatRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler accepts an empty/missing "prompt" (it will forward an empty string to the sidecar). If "prompt" is required for this API, add validation after decoding (e.g., strings.TrimSpace(req.Prompt) != "") and return 400 to fail fast and avoid creating ACP sessions for invalid requests.

Copilot uses AI. Check for mistakes.
Comment on lines +97 to +110
func (*streamingClient) RequestPermission(_ context.Context, p acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
if len(p.Options) == 0 {
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Cancelled: &acp.RequestPermissionOutcomeCancelled{},
},
}, nil
}
return acp.RequestPermissionResponse{
Outcome: acp.RequestPermissionOutcome{
Selected: &acp.RequestPermissionOutcomeSelected{OptionId: p.Options[0].OptionId},
},
}, nil
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestPermission currently auto-selects the first option whenever any options are provided. That can unintentionally approve sensitive actions if an agent presents an "allow" option first. Consider defaulting to a safe outcome (Cancelled/Denied) unless the gateway has explicit user consent/config for auto-approval, or select only an explicitly safe option kind.

Copilot uses AI. Check for mistakes.
@Nabil-Salah Nabil-Salah requested a review from yurishkuro April 9, 2026 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants