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
29 changes: 7 additions & 22 deletions pkg/cli/remote_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,12 +631,7 @@ func extractDispatchWorkflowNames(content string) []string {
return nil
}

safeOutputs, exists := result.Frontmatter["safe-outputs"]
if !exists {
return nil
}

safeOutputsMap, ok := safeOutputs.(map[string]any)
safeOutputsMap, ok := result.Frontmatter["safe-outputs"].(map[string]any)
if !ok {
return nil
}
Expand All @@ -652,32 +647,22 @@ func extractDispatchWorkflowNames(content string) []string {
case []any:
// Array format: dispatch-workflow: [name1, name2]
for _, item := range v {
if name, ok := item.(string); ok {
if name, ok := item.(string); ok && !strings.Contains(name, "${{") {
workflowNames = append(workflowNames, name)
}
}
case map[string]any:
// Map format: dispatch-workflow: {workflows: [name1, name2]}
if workflows, exists := v["workflows"]; exists {
if workflowsArray, ok := workflows.([]any); ok {
for _, item := range workflowsArray {
if name, ok := item.(string); ok {
workflowNames = append(workflowNames, name)
}
if workflowsArray, ok := v["workflows"].([]any); ok {
for _, item := range workflowsArray {
if name, ok := item.(string); ok && !strings.Contains(name, "${{") {
workflowNames = append(workflowNames, name)
}
}
}
}

// Filter out GitHub Actions expression syntax (e.g. "${{ vars.WORKFLOW }}")
filtered := make([]string, 0, len(workflowNames))
for _, name := range workflowNames {
if !strings.Contains(name, "${{") {
filtered = append(filtered, name)
}
}

return filtered
return workflowNames
}

// fetchAndSaveRemoteDispatchWorkflows fetches and saves the workflow files referenced in the
Expand Down
99 changes: 39 additions & 60 deletions pkg/workflow/safe_outputs_config_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,87 +10,66 @@ import (
func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) map[string]any {
safeOutputsConfigLog.Printf("Generating tool definition for custom job: %s", jobName)

// Build the tool definition
tool := map[string]any{
"name": jobName,
description := jobConfig.Description
if description == "" {
description = fmt.Sprintf("Execute the %s custom job", jobName)
}

// Add description if present
if jobConfig.Description != "" {
tool["description"] = jobConfig.Description
} else {
// Provide a default description if none is specified
tool["description"] = fmt.Sprintf("Execute the %s custom job", jobName)
}

// Build the input schema
inputSchema := map[string]any{
"type": "object",
"properties": make(map[string]any),
"type": "object",
"properties": make(map[string]any),
"additionalProperties": false,
}

// Track required fields
var requiredFields []string
properties := inputSchema["properties"].(map[string]any)

// Add each input to the schema
if len(jobConfig.Inputs) > 0 {
properties := inputSchema["properties"].(map[string]any)
for inputName, inputDef := range jobConfig.Inputs {
property := map[string]any{}

for inputName, inputDef := range jobConfig.Inputs {
property := map[string]any{}

// Add description
if inputDef.Description != "" {
property["description"] = inputDef.Description
}

// Convert type to JSON Schema type
switch inputDef.Type {
case "choice":
// Choice inputs are strings with enum constraints
property["type"] = "string"
if len(inputDef.Options) > 0 {
property["enum"] = inputDef.Options
}
case "boolean":
property["type"] = "boolean"
case "number":
property["type"] = "number"
case "string", "":
// Default to string if type is not specified
property["type"] = "string"
default:
// For any unknown type, default to string
property["type"] = "string"
}
if inputDef.Description != "" {
property["description"] = inputDef.Description
}

// Add default value if present
if inputDef.Default != nil {
property["default"] = inputDef.Default
// Convert type to JSON Schema type
switch inputDef.Type {
case "choice":
// Choice inputs are strings with enum constraints
property["type"] = "string"
if len(inputDef.Options) > 0 {
property["enum"] = inputDef.Options
}
case "boolean":
property["type"] = "boolean"
case "number":
property["type"] = "number"
default:
// "string", empty string, or any unknown type defaults to string
property["type"] = "string"
}

// Track required fields
if inputDef.Required {
requiredFields = append(requiredFields, inputName)
}
if inputDef.Default != nil {
property["default"] = inputDef.Default
}

properties[inputName] = property
if inputDef.Required {
requiredFields = append(requiredFields, inputName)
}

properties[inputName] = property
}

// Add required fields array if any inputs are required
if len(requiredFields) > 0 {
sort.Strings(requiredFields)
inputSchema["required"] = requiredFields
}

// Prevent additional properties to maintain schema strictness
inputSchema["additionalProperties"] = false

tool["inputSchema"] = inputSchema

safeOutputsConfigLog.Printf("Generated tool definition for %s with %d inputs, %d required",
jobName, len(jobConfig.Inputs), len(requiredFields))

return tool
return map[string]any{
"name": jobName,
"description": description,
"inputSchema": inputSchema,
}
}