From 5c8a944e9f7f0881e7c047bac9f5c0af8f986d1a Mon Sep 17 00:00:00 2001 From: Mark Turansky Date: Wed, 29 Apr 2026 15:51:08 +0000 Subject: [PATCH 1/2] fix(google-oauth): set correct redirect URI for workspace-mcp in runner pods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit workspace-mcp defaults to http://localhost:8000/oauth2callback when GOOGLE_OAUTH_REDIRECT_URI is not set. This causes Google OAuth to fail with "redirect_uri_mismatch" because localhost:8000 is not a registered redirect URI in the Google Cloud Console app. The fix threads the backend's public URL through the operator so that runner pods receive the correct redirect URI pointing to the Ambient backend's /oauth2callback endpoint (which is already registered and handled by handlers/oauth.go). Changes: - operator/config.go: add BackendPublicURL field, read from BACKEND_PUBLIC_URL env - operator/handlers/sessions.go: inject GOOGLE_OAUTH_REDIRECT_URI into runner pod env when BackendPublicURL is configured - manifests/base/core/operator-deployment.yaml: source BACKEND_PUBLIC_URL from google-workflow-app-secret (key: BACKEND_URL, optional) — reuses the same secret already configured by platform admins - runners/ambient-runner/.mcp.json: forward GOOGLE_OAUTH_REDIRECT_URI into the workspace-mcp process environment Co-Authored-By: Claude Sonnet 4.6 --- .../manifests/base/core/operator-deployment.yaml | 8 ++++++++ components/operator/internal/config/config.go | 4 ++++ components/operator/internal/handlers/sessions.go | 9 +++++++++ components/runners/ambient-runner/.mcp.json | 12 +++++++++--- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/components/manifests/base/core/operator-deployment.yaml b/components/manifests/base/core/operator-deployment.yaml index f6f543684..915547e01 100644 --- a/components/manifests/base/core/operator-deployment.yaml +++ b/components/manifests/base/core/operator-deployment.yaml @@ -125,6 +125,14 @@ spec: name: google-workflow-app-secret key: GOOGLE_OAUTH_CLIENT_SECRET optional: true + # Public backend URL used to build the OAuth redirect URI injected into runner pods. + # Must match the redirect URI registered in the Google Cloud Console OAuth app. + - name: BACKEND_PUBLIC_URL + valueFrom: + secretKeyRef: + name: google-workflow-app-secret + key: BACKEND_URL + optional: true # S3 state sync configuration (defaults - can be overridden per-project in settings) - name: STATE_SYNC_IMAGE value: "quay.io/ambient_code/vteam_state_sync:latest" diff --git a/components/operator/internal/config/config.go b/components/operator/internal/config/config.go index 5f305b48a..600c22cf1 100644 --- a/components/operator/internal/config/config.go +++ b/components/operator/internal/config/config.go @@ -23,6 +23,7 @@ var ( type Config struct { Namespace string BackendNamespace string + BackendPublicURL string AmbientCodeRunnerImage string StateSyncImage string ImagePullPolicy corev1.PullPolicy @@ -121,9 +122,12 @@ func LoadConfig() *Config { } } + backendPublicURL := os.Getenv("BACKEND_PUBLIC_URL") + return &Config{ Namespace: namespace, BackendNamespace: backendNamespace, + BackendPublicURL: backendPublicURL, AmbientCodeRunnerImage: ambientCodeRunnerImage, StateSyncImage: stateSyncImage, ImagePullPolicy: imagePullPolicy, diff --git a/components/operator/internal/handlers/sessions.go b/components/operator/internal/handlers/sessions.go index 1e03a15af..bfe44b31e 100755 --- a/components/operator/internal/handlers/sessions.go +++ b/components/operator/internal/handlers/sessions.go @@ -1078,6 +1078,15 @@ func handleAgenticSessionEvent(obj *unstructured.Unstructured) error { {Name: "GOOGLE_OAUTH_CLIENT_SECRET", Value: os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET")}, } + // Set the OAuth redirect URI for workspace-mcp so it uses the Ambient backend + // callback endpoint instead of defaulting to http://localhost:8000/oauth2callback. + if appConfig.BackendPublicURL != "" { + base = append(base, corev1.EnvVar{ + Name: "GOOGLE_OAUTH_REDIRECT_URI", + Value: fmt.Sprintf("%s/oauth2callback", appConfig.BackendPublicURL), + }) + } + // For e2e: use minimal MCP config (webfetch only, no credentials needed) if mcpConfigFile := os.Getenv("MCP_CONFIG_FILE"); strings.TrimSpace(mcpConfigFile) != "" { base = append(base, corev1.EnvVar{Name: "MCP_CONFIG_FILE", Value: mcpConfigFile}) diff --git a/components/runners/ambient-runner/.mcp.json b/components/runners/ambient-runner/.mcp.json index 399975c9e..fa9569581 100644 --- a/components/runners/ambient-runner/.mcp.json +++ b/components/runners/ambient-runner/.mcp.json @@ -10,11 +10,15 @@ }, "webfetch": { "command": "uvx", - "args": ["mcp-server-fetch"] + "args": [ + "mcp-server-fetch" + ] }, "mcp-atlassian": { "command": "uvx", - "args": ["mcp-atlassian"], + "args": [ + "mcp-atlassian" + ], "env": { "JIRA_URL": "${JIRA_URL}", "JIRA_USERNAME": "${JIRA_EMAIL}", @@ -27,7 +31,8 @@ "command": "uvx", "args": [ "kubernetes-mcp-server@latest", - "--kubeconfig", "/tmp/.ambient_kubeconfig", + "--kubeconfig", + "/tmp/.ambient_kubeconfig", "--disable-multi-cluster" ] }, @@ -44,6 +49,7 @@ "MCP_SINGLE_USER_MODE": "1", "GOOGLE_OAUTH_CLIENT_ID": "${GOOGLE_OAUTH_CLIENT_ID}", "GOOGLE_OAUTH_CLIENT_SECRET": "${GOOGLE_OAUTH_CLIENT_SECRET}", + "GOOGLE_OAUTH_REDIRECT_URI": "${GOOGLE_OAUTH_REDIRECT_URI}", "USER_GOOGLE_EMAIL": "${USER_GOOGLE_EMAIL:-user@example.com}" } } From 1c2c067b2ce2fd9816d53ebaf8a7b2b1a71f23cb Mon Sep 17 00:00:00 2001 From: Mark Turansky Date: Wed, 29 Apr 2026 16:45:09 +0000 Subject: [PATCH 2/2] fix(api-server): add missing ScheduledSession openapi model types The scheduledSessions plugin and ambient-sdk go-sdk referenced openapi.ScheduledSession, openapi.ScheduledSessionList, openapi.ScheduledSessionPatchRequest, and types.ScheduledSession / types.ScheduledSessionPatch / types.ScheduledSessionList which were never generated. This caused ambient-api-server and ambient-control-plane Docker builds to fail with "undefined" compile errors. Co-Authored-By: Claude Sonnet 4.6 --- .../api/openapi/model_scheduled_session.go | 141 +++++++++++++++++ .../openapi/model_scheduled_session_list.go | 136 ++++++++++++++++ .../model_scheduled_session_patch_request.go | 108 +++++++++++++ .../go-sdk/types/scheduled_session.go | 147 ++++++++++++++++++ 4 files changed, 532 insertions(+) create mode 100644 components/ambient-api-server/pkg/api/openapi/model_scheduled_session.go create mode 100644 components/ambient-api-server/pkg/api/openapi/model_scheduled_session_list.go create mode 100644 components/ambient-api-server/pkg/api/openapi/model_scheduled_session_patch_request.go create mode 100644 components/ambient-sdk/go-sdk/types/scheduled_session.go diff --git a/components/ambient-api-server/pkg/api/openapi/model_scheduled_session.go b/components/ambient-api-server/pkg/api/openapi/model_scheduled_session.go new file mode 100644 index 000000000..72de48d37 --- /dev/null +++ b/components/ambient-api-server/pkg/api/openapi/model_scheduled_session.go @@ -0,0 +1,141 @@ +/* +Ambient API Server + +Ambient API Server + +API version: 1.0.0 +Contact: ambient-code@redhat.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" + "time" +) + +// checks if the ScheduledSession type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &ScheduledSession{} + +// ScheduledSession struct for ScheduledSession +type ScheduledSession struct { + Id *string `json:"id,omitempty"` + Kind *string `json:"kind,omitempty"` + Href *string `json:"href,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + ProjectId string `json:"project_id"` + AgentId string `json:"agent_id"` + Schedule string `json:"schedule"` + Timezone *string `json:"timezone,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + SessionPrompt *string `json:"session_prompt,omitempty"` + LastRunAt *time.Time `json:"last_run_at,omitempty"` + NextRunAt *time.Time `json:"next_run_at,omitempty"` +} + +// NewScheduledSession instantiates a new ScheduledSession object +func NewScheduledSession(name string, projectId string, agentId string, schedule string) *ScheduledSession { + this := ScheduledSession{} + this.Name = name + this.ProjectId = projectId + this.AgentId = agentId + this.Schedule = schedule + return &this +} + +// NewScheduledSessionWithDefaults instantiates a new ScheduledSession object with defaults +func NewScheduledSessionWithDefaults() *ScheduledSession { + this := ScheduledSession{} + return &this +} + +func (o ScheduledSession) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o ScheduledSession) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Id) { + toSerialize["id"] = o.Id + } + if !IsNil(o.Kind) { + toSerialize["kind"] = o.Kind + } + if !IsNil(o.Href) { + toSerialize["href"] = o.Href + } + if !IsNil(o.CreatedAt) { + toSerialize["created_at"] = o.CreatedAt + } + if !IsNil(o.UpdatedAt) { + toSerialize["updated_at"] = o.UpdatedAt + } + toSerialize["name"] = o.Name + if !IsNil(o.Description) { + toSerialize["description"] = o.Description + } + toSerialize["project_id"] = o.ProjectId + toSerialize["agent_id"] = o.AgentId + toSerialize["schedule"] = o.Schedule + if !IsNil(o.Timezone) { + toSerialize["timezone"] = o.Timezone + } + if !IsNil(o.Enabled) { + toSerialize["enabled"] = o.Enabled + } + if !IsNil(o.SessionPrompt) { + toSerialize["session_prompt"] = o.SessionPrompt + } + if !IsNil(o.LastRunAt) { + toSerialize["last_run_at"] = o.LastRunAt + } + if !IsNil(o.NextRunAt) { + toSerialize["next_run_at"] = o.NextRunAt + } + return toSerialize, nil +} + +type NullableScheduledSession struct { + value *ScheduledSession + isSet bool +} + +func (v NullableScheduledSession) Get() *ScheduledSession { + return v.value +} + +func (v *NullableScheduledSession) Set(val *ScheduledSession) { + v.value = val + v.isSet = true +} + +func (v NullableScheduledSession) IsSet() bool { + return v.isSet +} + +func (v *NullableScheduledSession) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableScheduledSession(val *ScheduledSession) *NullableScheduledSession { + return &NullableScheduledSession{value: val, isSet: true} +} + +func (v NullableScheduledSession) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableScheduledSession) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/components/ambient-api-server/pkg/api/openapi/model_scheduled_session_list.go b/components/ambient-api-server/pkg/api/openapi/model_scheduled_session_list.go new file mode 100644 index 000000000..830c7c5ba --- /dev/null +++ b/components/ambient-api-server/pkg/api/openapi/model_scheduled_session_list.go @@ -0,0 +1,136 @@ +/* +Ambient API Server + +Ambient API Server + +API version: 1.0.0 +Contact: ambient-code@redhat.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "bytes" + "encoding/json" + "fmt" +) + +// checks if the ScheduledSessionList type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &ScheduledSessionList{} + +// ScheduledSessionList struct for ScheduledSessionList +type ScheduledSessionList struct { + Kind string `json:"kind"` + Page int32 `json:"page"` + Size int32 `json:"size"` + Total int32 `json:"total"` + Items []ScheduledSession `json:"items"` +} + +type _ScheduledSessionList ScheduledSessionList + +// NewScheduledSessionList instantiates a new ScheduledSessionList object +func NewScheduledSessionList(kind string, page int32, size int32, total int32, items []ScheduledSession) *ScheduledSessionList { + this := ScheduledSessionList{} + this.Kind = kind + this.Page = page + this.Size = size + this.Total = total + this.Items = items + return &this +} + +// NewScheduledSessionListWithDefaults instantiates a new ScheduledSessionList object with defaults +func NewScheduledSessionListWithDefaults() *ScheduledSessionList { + this := ScheduledSessionList{} + return &this +} + +func (o ScheduledSessionList) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o ScheduledSessionList) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["kind"] = o.Kind + toSerialize["page"] = o.Page + toSerialize["size"] = o.Size + toSerialize["total"] = o.Total + toSerialize["items"] = o.Items + return toSerialize, nil +} + +func (o *ScheduledSessionList) UnmarshalJSON(data []byte) (err error) { + requiredProperties := []string{ + "kind", + "page", + "size", + "total", + "items", + } + + allProperties := make(map[string]interface{}) + err = json.Unmarshal(data, &allProperties) + if err != nil { + return err + } + + for _, requiredProperty := range requiredProperties { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varScheduledSessionList := _ScheduledSessionList{} + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varScheduledSessionList) + if err != nil { + return err + } + + *o = ScheduledSessionList(varScheduledSessionList) + return err +} + +type NullableScheduledSessionList struct { + value *ScheduledSessionList + isSet bool +} + +func (v NullableScheduledSessionList) Get() *ScheduledSessionList { + return v.value +} + +func (v *NullableScheduledSessionList) Set(val *ScheduledSessionList) { + v.value = val + v.isSet = true +} + +func (v NullableScheduledSessionList) IsSet() bool { + return v.isSet +} + +func (v *NullableScheduledSessionList) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableScheduledSessionList(val *ScheduledSessionList) *NullableScheduledSessionList { + return &NullableScheduledSessionList{value: val, isSet: true} +} + +func (v NullableScheduledSessionList) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableScheduledSessionList) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/components/ambient-api-server/pkg/api/openapi/model_scheduled_session_patch_request.go b/components/ambient-api-server/pkg/api/openapi/model_scheduled_session_patch_request.go new file mode 100644 index 000000000..efa990616 --- /dev/null +++ b/components/ambient-api-server/pkg/api/openapi/model_scheduled_session_patch_request.go @@ -0,0 +1,108 @@ +/* +Ambient API Server + +Ambient API Server + +API version: 1.0.0 +Contact: ambient-code@redhat.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// checks if the ScheduledSessionPatchRequest type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &ScheduledSessionPatchRequest{} + +// ScheduledSessionPatchRequest struct for ScheduledSessionPatchRequest +type ScheduledSessionPatchRequest struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Schedule *string `json:"schedule,omitempty"` + Timezone *string `json:"timezone,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + SessionPrompt *string `json:"session_prompt,omitempty"` +} + +// NewScheduledSessionPatchRequest instantiates a new ScheduledSessionPatchRequest object +func NewScheduledSessionPatchRequest() *ScheduledSessionPatchRequest { + this := ScheduledSessionPatchRequest{} + return &this +} + +// NewScheduledSessionPatchRequestWithDefaults instantiates a new ScheduledSessionPatchRequest object with defaults +func NewScheduledSessionPatchRequestWithDefaults() *ScheduledSessionPatchRequest { + this := ScheduledSessionPatchRequest{} + return &this +} + +func (o ScheduledSessionPatchRequest) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o ScheduledSessionPatchRequest) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Name) { + toSerialize["name"] = o.Name + } + if !IsNil(o.Description) { + toSerialize["description"] = o.Description + } + if !IsNil(o.Schedule) { + toSerialize["schedule"] = o.Schedule + } + if !IsNil(o.Timezone) { + toSerialize["timezone"] = o.Timezone + } + if !IsNil(o.Enabled) { + toSerialize["enabled"] = o.Enabled + } + if !IsNil(o.SessionPrompt) { + toSerialize["session_prompt"] = o.SessionPrompt + } + return toSerialize, nil +} + +type NullableScheduledSessionPatchRequest struct { + value *ScheduledSessionPatchRequest + isSet bool +} + +func (v NullableScheduledSessionPatchRequest) Get() *ScheduledSessionPatchRequest { + return v.value +} + +func (v *NullableScheduledSessionPatchRequest) Set(val *ScheduledSessionPatchRequest) { + v.value = val + v.isSet = true +} + +func (v NullableScheduledSessionPatchRequest) IsSet() bool { + return v.isSet +} + +func (v *NullableScheduledSessionPatchRequest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableScheduledSessionPatchRequest(val *ScheduledSessionPatchRequest) *NullableScheduledSessionPatchRequest { + return &NullableScheduledSessionPatchRequest{value: val, isSet: true} +} + +func (v NullableScheduledSessionPatchRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableScheduledSessionPatchRequest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/components/ambient-sdk/go-sdk/types/scheduled_session.go b/components/ambient-sdk/go-sdk/types/scheduled_session.go new file mode 100644 index 000000000..60eab9625 --- /dev/null +++ b/components/ambient-sdk/go-sdk/types/scheduled_session.go @@ -0,0 +1,147 @@ +package types + +import ( + "errors" + "fmt" + "time" +) + +type ScheduledSession struct { + ObjectReference + + AgentID string `json:"agent_id,omitempty"` + Description string `json:"description,omitempty"` + Enabled bool `json:"enabled,omitempty"` + LastRunAt *time.Time `json:"last_run_at,omitempty"` + Name string `json:"name"` + NextRunAt *time.Time `json:"next_run_at,omitempty"` + ProjectID string `json:"project_id,omitempty"` + Schedule string `json:"schedule,omitempty"` + SessionPrompt string `json:"session_prompt,omitempty"` + Timezone string `json:"timezone,omitempty"` +} + +type ScheduledSessionList struct { + ListMeta + Items []ScheduledSession `json:"items"` +} + +func (l *ScheduledSessionList) GetItems() []ScheduledSession { return l.Items } +func (l *ScheduledSessionList) GetTotal() int { return l.Total } +func (l *ScheduledSessionList) GetPage() int { return l.Page } +func (l *ScheduledSessionList) GetSize() int { return l.Size } + +// ScheduledSessionPatch is the request body for a PATCH operation. +// Only set fields that should be changed; omitted (nil) fields are left unchanged. +type ScheduledSessionPatch struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Schedule *string `json:"schedule,omitempty"` + Timezone *string `json:"timezone,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + SessionPrompt *string `json:"session_prompt,omitempty"` +} + +// ScheduledSessionBuilder builds a ScheduledSession for creation. +type ScheduledSessionBuilder struct { + resource ScheduledSession + errs []error +} + +func NewScheduledSessionBuilder() *ScheduledSessionBuilder { + return &ScheduledSessionBuilder{} +} + +func (b *ScheduledSessionBuilder) Name(v string) *ScheduledSessionBuilder { + b.resource.Name = v + return b +} + +func (b *ScheduledSessionBuilder) ProjectID(v string) *ScheduledSessionBuilder { + b.resource.ProjectID = v + return b +} + +func (b *ScheduledSessionBuilder) AgentID(v string) *ScheduledSessionBuilder { + b.resource.AgentID = v + return b +} + +func (b *ScheduledSessionBuilder) Schedule(v string) *ScheduledSessionBuilder { + b.resource.Schedule = v + return b +} + +func (b *ScheduledSessionBuilder) Timezone(v string) *ScheduledSessionBuilder { + b.resource.Timezone = v + return b +} + +func (b *ScheduledSessionBuilder) SessionPrompt(v string) *ScheduledSessionBuilder { + b.resource.SessionPrompt = v + return b +} + +func (b *ScheduledSessionBuilder) Description(v string) *ScheduledSessionBuilder { + b.resource.Description = v + return b +} + +func (b *ScheduledSessionBuilder) Build() (*ScheduledSession, error) { + if b.resource.Name == "" { + b.errs = append(b.errs, fmt.Errorf("name is required")) + } + if b.resource.AgentID == "" { + b.errs = append(b.errs, fmt.Errorf("agent_id is required")) + } + if b.resource.Schedule == "" { + b.errs = append(b.errs, fmt.Errorf("schedule is required")) + } + if len(b.errs) > 0 { + return nil, fmt.Errorf("validation failed: %w", errors.Join(b.errs...)) + } + return &b.resource, nil +} + +// ScheduledSessionPatchBuilder builds a ScheduledSessionPatch for update operations. +type ScheduledSessionPatchBuilder struct { + patch ScheduledSessionPatch +} + +func NewScheduledSessionPatchBuilder() *ScheduledSessionPatchBuilder { + return &ScheduledSessionPatchBuilder{} +} + +func (b *ScheduledSessionPatchBuilder) Name(v string) *ScheduledSessionPatchBuilder { + b.patch.Name = &v + return b +} + +func (b *ScheduledSessionPatchBuilder) Description(v string) *ScheduledSessionPatchBuilder { + b.patch.Description = &v + return b +} + +func (b *ScheduledSessionPatchBuilder) Schedule(v string) *ScheduledSessionPatchBuilder { + b.patch.Schedule = &v + return b +} + +func (b *ScheduledSessionPatchBuilder) Timezone(v string) *ScheduledSessionPatchBuilder { + b.patch.Timezone = &v + return b +} + +func (b *ScheduledSessionPatchBuilder) Enabled(v bool) *ScheduledSessionPatchBuilder { + b.patch.Enabled = &v + return b +} + +func (b *ScheduledSessionPatchBuilder) SessionPrompt(v string) *ScheduledSessionPatchBuilder { + b.patch.SessionPrompt = &v + return b +} + +func (b *ScheduledSessionPatchBuilder) Build() *ScheduledSessionPatch { + return &b.patch +}