Skip to content

Commit da9d7c6

Browse files
Merge pull request #1558 from stanislavHamara/always-permissions-for-tools-commands
Always permissions for tools commands
2 parents 05b4697 + a267087 commit da9d7c6

File tree

14 files changed

+151
-40
lines changed

14 files changed

+151
-40
lines changed

gen/cagent/v1/cagent.pb.go

Lines changed: 13 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/proto/cagent/v1/cagent.pb.go

Lines changed: 13 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ type UpdateSessionPermissionsRequest struct {
146146
// ResumeSessionRequest represents a request to resume a session
147147
type ResumeSessionRequest struct {
148148
Confirmation string `json:"confirmation"`
149-
Reason string `json:"reason,omitempty"` // e.g reason for tool call rejection
149+
Reason string `json:"reason,omitempty"` // e.g reason for tool call rejection
150+
ToolName string `json:"tool_name,omitempty"` // tool name for approve-tool confirmation
150151
}
151152

152153
// DesktopTokenResponse represents the response from getting a desktop token

pkg/connectrpc/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ func (s *Server) DeleteSession(ctx context.Context, req *connect.Request[cagentv
225225

226226
// ResumeSession resumes a paused session.
227227
func (s *Server) ResumeSession(ctx context.Context, req *connect.Request[cagentv1.ResumeSessionRequest]) (*connect.Response[cagentv1.ResumeSessionResponse], error) {
228-
if err := s.sm.ResumeSession(ctx, req.Msg.Id, req.Msg.Confirmation, req.Msg.Reason); err != nil {
228+
if err := s.sm.ResumeSession(ctx, req.Msg.Id, req.Msg.Confirmation, req.Msg.Reason, req.Msg.ToolName); err != nil {
229229
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to resume session: %w", err))
230230
}
231231
return connect.NewResponse(&cagentv1.ResumeSessionResponse{}), nil

pkg/runtime/client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,9 @@ func (c *Client) CreateSession(ctx context.Context, sessTemplate *session.Sessio
251251
return &sess, err
252252
}
253253

254-
// ResumeSession resumes a session by ID with an optional rejection reason
255-
func (c *Client) ResumeSession(ctx context.Context, id, confirmation, reason string) error {
256-
req := api.ResumeSessionRequest{Confirmation: confirmation, Reason: reason}
254+
// ResumeSession resumes a session by ID with optional rejection reason or tool name
255+
func (c *Client) ResumeSession(ctx context.Context, id, confirmation, reason, toolName string) error {
256+
req := api.ResumeSessionRequest{Confirmation: confirmation, Reason: reason, ToolName: toolName}
257257
return c.doRequest(ctx, http.MethodPost, "/api/sessions/"+id+"/resume", req, nil)
258258
}
259259

pkg/runtime/connectrpc_client.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,13 @@ func (c *ConnectRPCClient) DeleteSession(ctx context.Context, id string) error {
154154
return err
155155
}
156156

157-
// ResumeSession resumes a session by ID with an optional rejection reason
158-
func (c *ConnectRPCClient) ResumeSession(ctx context.Context, id, confirmation, reason string) error {
157+
// ResumeSession resumes a session by ID with optional rejection reason or tool name
158+
func (c *ConnectRPCClient) ResumeSession(ctx context.Context, id, confirmation, reason, toolName string) error {
159159
_, err := c.client.ResumeSession(ctx, connect.NewRequest(&cagentv1.ResumeSessionRequest{
160160
Id: id,
161161
Confirmation: confirmation,
162162
Reason: reason,
163+
ToolName: toolName,
163164
}))
164165
return err
165166
}

pkg/runtime/remote_client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ type RemoteClient interface {
1818
// CreateSession creates a new session
1919
CreateSession(ctx context.Context, sessTemplate *session.Session) (*session.Session, error)
2020

21-
// ResumeSession resumes a paused session with an optional rejection reason
22-
ResumeSession(ctx context.Context, id, confirmation, reason string) error
21+
// ResumeSession resumes a paused session with optional rejection reason or tool name
22+
ResumeSession(ctx context.Context, id, confirmation, reason, toolName string) error
2323

2424
// ResumeElicitation sends an elicitation response
2525
ResumeElicitation(ctx context.Context, sessionID string, action tools.ElicitationAction, content map[string]any) error

pkg/runtime/remote_runtime.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,14 @@ func (r *RemoteRuntime) Run(ctx context.Context, sess *session.Session) ([]sessi
198198

199199
// Resume allows resuming execution after user confirmation
200200
func (r *RemoteRuntime) Resume(ctx context.Context, req ResumeRequest) {
201-
slog.Debug("Resuming remote runtime", "agent", r.currentAgent, "type", req.Type, "reason", req.Reason, "session_id", r.sessionID)
201+
slog.Debug("Resuming remote runtime", "agent", r.currentAgent, "type", req.Type, "reason", req.Reason, "tool_name", req.ToolName, "session_id", r.sessionID)
202202

203203
if r.sessionID == "" {
204204
slog.Error("Cannot resume: no session ID available")
205205
return
206206
}
207207

208-
if err := r.client.ResumeSession(ctx, r.sessionID, string(req.Type), req.Reason); err != nil {
208+
if err := r.client.ResumeSession(ctx, r.sessionID, string(req.Type), req.Reason, req.ToolName); err != nil {
209209
slog.Error("Failed to resume remote session", "error", err, "session_id", r.sessionID)
210210
}
211211
}

pkg/runtime/resume_validation.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ func IsValidResumeType(t ResumeType) bool {
55
switch t {
66
case ResumeTypeApprove,
77
ResumeTypeApproveSession,
8+
ResumeTypeApproveTool,
89
ResumeTypeReject:
910
return true
1011
default:
@@ -17,6 +18,7 @@ func ValidResumeTypes() []ResumeType {
1718
return []ResumeType{
1819
ResumeTypeApprove,
1920
ResumeTypeApproveSession,
21+
ResumeTypeApproveTool,
2022
ResumeTypeReject,
2123
}
2224
}

pkg/runtime/runtime.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@ func (e *ElicitationError) Error() string {
5959
const (
6060
ResumeTypeApprove ResumeType = "approve"
6161
ResumeTypeApproveSession ResumeType = "approve-session"
62+
ResumeTypeApproveTool ResumeType = "approve-tool"
6263
ResumeTypeReject ResumeType = "reject"
6364
)
6465

6566
// ResumeRequest carries the user's confirmation decision along with an optional
6667
// reason (used when rejecting a tool call to help the model understand why).
6768
type ResumeRequest struct {
68-
Type ResumeType
69-
Reason string // Optional; primarily used with ResumeTypeReject
69+
Type ResumeType
70+
Reason string // Optional; primarily used with ResumeTypeReject
71+
ToolName string // Optional; used with ResumeTypeApproveTool to specify which tool to always allow
7072
}
7173

7274
// ResumeApprove creates a ResumeRequest to approve a single tool call.
@@ -79,6 +81,11 @@ func ResumeApproveSession() ResumeRequest {
7981
return ResumeRequest{Type: ResumeTypeApproveSession}
8082
}
8183

84+
// ResumeApproveTool creates a ResumeRequest to always approve a specific tool for the session.
85+
func ResumeApproveTool(toolName string) ResumeRequest {
86+
return ResumeRequest{Type: ResumeTypeApproveTool, ToolName: toolName}
87+
}
88+
8289
// ResumeReject creates a ResumeRequest to reject a tool call with an optional reason.
8390
func ResumeReject(reason string) ResumeRequest {
8491
return ResumeRequest{Type: ResumeTypeReject, Reason: reason}
@@ -1432,6 +1439,20 @@ func (r *LocalRuntime) executeWithApproval(
14321439
slog.Debug("Resume signal received, approving session", "tool", toolCall.Function.Name, "session_id", sess.ID)
14331440
sess.ToolsApproved = true
14341441
runTool()
1442+
case ResumeTypeApproveTool:
1443+
// Add the tool to session's allow list for future auto-approval
1444+
approvedTool := req.ToolName
1445+
if approvedTool == "" {
1446+
approvedTool = toolName
1447+
}
1448+
if sess.Permissions == nil {
1449+
sess.Permissions = &session.PermissionsConfig{}
1450+
}
1451+
if !slices.Contains(sess.Permissions.Allow, approvedTool) {
1452+
sess.Permissions.Allow = append(sess.Permissions.Allow, approvedTool)
1453+
}
1454+
slog.Debug("Resume signal received, approving tool permanently", "tool", approvedTool, "session_id", sess.ID)
1455+
runTool()
14351456
case ResumeTypeReject:
14361457
slog.Debug("Resume signal received, rejecting tool", "tool", toolCall.Function.Name, "session_id", sess.ID, "reason", req.Reason)
14371458
rejectMsg := "The user rejected the tool call."

0 commit comments

Comments
 (0)