diff --git a/kubechain/.gitignore b/kubechain/.gitignore index ada68ff..0f47bba 100644 --- a/kubechain/.gitignore +++ b/kubechain/.gitignore @@ -7,6 +7,9 @@ bin/* Dockerfile.cross +# Developer playground samples +config/samples/*.play.yaml + # Test binary, built with `go test -c` *.test diff --git a/kubechain/config/manager/kustomization.yaml b/kubechain/config/manager/kustomization.yaml index f0e9801..97ba6e4 100644 --- a/kubechain/config/manager/kustomization.yaml +++ b/kubechain/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: ghcr.io/humanlayer/smallchain - newTag: v0.1.13 + newName: controller + newTag: "202504012152" diff --git a/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller.go b/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller.go index c51a06c..d73b878 100644 --- a/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller.go +++ b/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller.go @@ -703,7 +703,7 @@ func (r *TaskRunToolCallReconciler) setStatusError(ctx context.Context, trtcStat return ctrl.Result{}, nil, true } -func (r *TaskRunToolCallReconciler) updateTRTCStatus(ctx context.Context, trtc *kubechainv1alpha1.TaskRunToolCall, trtcStatusType kubechainv1alpha1.TaskRunToolCallStatusType, trtcStatusPhase kubechainv1alpha1.TaskRunToolCallPhase, statusDetail string) (ctrl.Result, error, bool) { +func (r *TaskRunToolCallReconciler) updateTRTCStatus(ctx context.Context, trtc *kubechainv1alpha1.TaskRunToolCall, trtcStatusType kubechainv1alpha1.TaskRunToolCallStatusType, trtcStatusPhase kubechainv1alpha1.TaskRunToolCallPhase, statusDetail string, result string) (ctrl.Result, error, bool) { logger := log.FromContext(ctx) trtcDeepCopy := trtc.DeepCopy() @@ -712,6 +712,10 @@ func (r *TaskRunToolCallReconciler) updateTRTCStatus(ctx context.Context, trtc * trtcDeepCopy.Status.StatusDetail = statusDetail trtcDeepCopy.Status.Phase = trtcStatusPhase + if trtcStatusType == kubechainv1alpha1.TaskRunToolCallStatusTypeToolCallRejected { + trtcDeepCopy.Status.Result = result + } + if err := r.Status().Update(ctx, trtcDeepCopy); err != nil { logger.Error(err, "Failed to update status") return ctrl.Result{}, err, true @@ -789,12 +793,12 @@ func (r *TaskRunToolCallReconciler) handlePendingApproval(ctx context.Context, t return r.updateTRTCStatus(ctx, trtc, kubechainv1alpha1.TaskRunToolCallStatusTypeReadyToExecuteApprovedTool, kubechainv1alpha1.TaskRunToolCallPhasePending, - "Ready to execute approved tool") + "Ready to execute approved tool", "") } else { return r.updateTRTCStatus(ctx, trtc, kubechainv1alpha1.TaskRunToolCallStatusTypeToolCallRejected, kubechainv1alpha1.TaskRunToolCallPhaseFailed, - "Tool execution rejected") + "Tool execution rejected", status.GetComment()) } } diff --git a/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller_test.go b/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller_test.go index f2e89f2..ae6cf78 100644 --- a/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller_test.go +++ b/kubechain/internal/controller/taskruntoolcall/taskruntoolcall_controller_test.go @@ -222,7 +222,7 @@ var _ = Describe("TaskRunToolCall Controller", func() { }) }) - // Tests for approval workflow + // Tests for MCP tools with approval requirement Context("Pending -> AwaitingHumanApproval (MCP Tool, Slack Contact Channel)", func() { It("transitions to AwaitingHumanApproval when MCPServer has approval channel", func() { // Note setupTestApprovalResources sets up the MCP server, MCP tool, and TaskRunToolCall @@ -406,11 +406,14 @@ var _ = Describe("TaskRunToolCall Controller", func() { NeedsApproval: true, } + rejectionComment := "You know what, I strongly disagree with this tool call and feel it should not be be given permission to execute. I, by the powers granted to me by The System, hereby reject it. If you too feel strongly, you can try again. I will reject it a second time, but with greater vigor." + reconciler.HLClientFactory = &humanlayer.MockHumanLayerClientFactory{ ShouldFail: false, StatusCode: 200, ReturnError: nil, ShouldReturnRejection: true, + StatusComment: rejectionComment, } result, err := reconciler.Reconcile(ctx, reconcile.Request{ @@ -434,6 +437,7 @@ var _ = Describe("TaskRunToolCall Controller", func() { Expect(updatedTRTC.Status.Phase).To(Equal(kubechainv1alpha1.TaskRunToolCallPhaseFailed)) Expect(updatedTRTC.Status.Status).To(Equal(kubechainv1alpha1.TaskRunToolCallStatusTypeToolCallRejected)) Expect(updatedTRTC.Status.StatusDetail).To(ContainSubstring("Tool execution rejected")) + Expect(updatedTRTC.Status.Result).To(Equal(rejectionComment)) }) }) diff --git a/kubechain/internal/humanlayer/mock_hlclient.go b/kubechain/internal/humanlayer/mock_hlclient.go index 66841c2..7dc3142 100644 --- a/kubechain/internal/humanlayer/mock_hlclient.go +++ b/kubechain/internal/humanlayer/mock_hlclient.go @@ -20,6 +20,7 @@ type MockHumanLayerClientFactory struct { LastRunID string LastFunction string LastArguments map[string]interface{} + StatusComment string } // MockHumanLayerClientWrapper implements HumanLayerClientWrapper for testing @@ -104,6 +105,7 @@ func (m *MockHumanLayerClientWrapper) GetFunctionCallStatus(ctx context.Context) RequestedAt: *humanlayerapi.NewNullableTime(&now), RespondedAt: *humanlayerapi.NewNullableTime(&now), Approved: *humanlayerapi.NewNullableBool(&approved), + Comment: *humanlayerapi.NewNullableString(&m.parent.StatusComment), }) return &humanlayerapi.FunctionCallOutput{ Status: *status,