language-operator 0.1.31 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -8
  3. data/CHANGELOG.md +14 -0
  4. data/CI_STATUS.md +56 -0
  5. data/Gemfile.lock +2 -2
  6. data/Makefile +22 -6
  7. data/lib/language_operator/agent/base.rb +10 -6
  8. data/lib/language_operator/agent/executor.rb +19 -97
  9. data/lib/language_operator/agent/safety/ast_validator.rb +62 -43
  10. data/lib/language_operator/agent/safety/safe_executor.rb +27 -2
  11. data/lib/language_operator/agent/scheduler.rb +60 -0
  12. data/lib/language_operator/agent/task_executor.rb +548 -0
  13. data/lib/language_operator/agent.rb +90 -27
  14. data/lib/language_operator/cli/base_command.rb +117 -0
  15. data/lib/language_operator/cli/commands/agent.rb +339 -407
  16. data/lib/language_operator/cli/commands/cluster.rb +274 -290
  17. data/lib/language_operator/cli/commands/install.rb +110 -119
  18. data/lib/language_operator/cli/commands/model.rb +284 -184
  19. data/lib/language_operator/cli/commands/persona.rb +218 -284
  20. data/lib/language_operator/cli/commands/quickstart.rb +4 -5
  21. data/lib/language_operator/cli/commands/status.rb +31 -35
  22. data/lib/language_operator/cli/commands/system.rb +221 -233
  23. data/lib/language_operator/cli/commands/tool.rb +356 -422
  24. data/lib/language_operator/cli/commands/use.rb +19 -22
  25. data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
  26. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -1
  27. data/lib/language_operator/client/config.rb +20 -21
  28. data/lib/language_operator/config.rb +115 -3
  29. data/lib/language_operator/constants.rb +54 -0
  30. data/lib/language_operator/dsl/agent_context.rb +7 -7
  31. data/lib/language_operator/dsl/agent_definition.rb +111 -26
  32. data/lib/language_operator/dsl/config.rb +30 -66
  33. data/lib/language_operator/dsl/main_definition.rb +114 -0
  34. data/lib/language_operator/dsl/schema.rb +84 -43
  35. data/lib/language_operator/dsl/task_definition.rb +315 -0
  36. data/lib/language_operator/dsl.rb +0 -1
  37. data/lib/language_operator/instrumentation/task_tracer.rb +285 -0
  38. data/lib/language_operator/logger.rb +4 -4
  39. data/lib/language_operator/synthesis_test_harness.rb +324 -0
  40. data/lib/language_operator/templates/examples/agent_synthesis.tmpl +26 -8
  41. data/lib/language_operator/templates/schema/CHANGELOG.md +26 -0
  42. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
  43. data/lib/language_operator/templates/schema/agent_dsl_schema.json +84 -42
  44. data/lib/language_operator/type_coercion.rb +250 -0
  45. data/lib/language_operator/ux/base.rb +81 -0
  46. data/lib/language_operator/ux/concerns/README.md +155 -0
  47. data/lib/language_operator/ux/concerns/headings.rb +90 -0
  48. data/lib/language_operator/ux/concerns/input_validation.rb +146 -0
  49. data/lib/language_operator/ux/concerns/provider_helpers.rb +167 -0
  50. data/lib/language_operator/ux/create_agent.rb +252 -0
  51. data/lib/language_operator/ux/create_model.rb +267 -0
  52. data/lib/language_operator/ux/quickstart.rb +594 -0
  53. data/lib/language_operator/version.rb +1 -1
  54. data/lib/language_operator.rb +2 -0
  55. data/requirements/ARCHITECTURE.md +1 -0
  56. data/requirements/SCRATCH.md +153 -0
  57. data/requirements/dsl.md +0 -0
  58. data/requirements/features +1 -0
  59. data/requirements/personas +1 -0
  60. data/requirements/proposals +1 -0
  61. data/requirements/tasks/iterate.md +14 -15
  62. data/requirements/tasks/optimize.md +13 -4
  63. data/synth/001/Makefile +90 -0
  64. data/synth/001/agent.rb +26 -0
  65. data/synth/001/agent.yaml +7 -0
  66. data/synth/001/output.log +44 -0
  67. data/synth/Makefile +39 -0
  68. data/synth/README.md +342 -0
  69. metadata +37 -10
  70. data/lib/language_operator/dsl/workflow_definition.rb +0 -259
  71. data/test_agent_dsl.rb +0 -108
@@ -4,8 +4,11 @@ module LanguageOperator
4
4
  module Dsl
5
5
  # Configuration helper for managing environment variables
6
6
  #
7
- # Provides utilities for reading and managing environment variables with fallback support,
8
- # type conversion, and validation. All methods are class methods.
7
+ # This class delegates to LanguageOperator::Config for all functionality.
8
+ # It exists for backwards compatibility with existing code that uses
9
+ # Dsl::Config.
10
+ #
11
+ # @deprecated Use LanguageOperator::Config directly instead
9
12
  #
10
13
  # @example Basic usage
11
14
  # Config.get('SMTP_HOST', 'MAIL_HOST', default: 'localhost')
@@ -13,92 +16,45 @@ module LanguageOperator
13
16
  # Config.get_bool('USE_TLS', default: true)
14
17
  class Config
15
18
  # Get environment variable with multiple fallback keys
16
- #
17
- # @param keys [Array<String>] Environment variable names to try
18
- # @param default [Object, nil] Default value if none found
19
- # @return [String, nil] The first non-nil value or default
19
+ # Delegates to LanguageOperator::Config.get
20
20
  def self.get(*keys, default: nil)
21
- keys.each do |key|
22
- value = ENV.fetch(key.to_s, nil)
23
- return value if value
24
- end
25
- default
21
+ LanguageOperator::Config.get(*keys, default: default)
26
22
  end
27
23
 
28
24
  # Get required environment variable with fallback keys
29
- #
30
- # @param keys [Array<String>] Environment variable names to try
31
- # @return [String] The first non-nil value
32
- # @raise [ArgumentError] If none of the keys are set
25
+ # Delegates to LanguageOperator::Config.require
33
26
  def self.require(*keys)
34
- value = get(*keys)
35
- raise ArgumentError, "Missing required configuration: #{keys.join(' or ')}" unless value
36
-
37
- value
27
+ LanguageOperator::Config.require(*keys)
38
28
  end
39
29
 
40
30
  # Get environment variable as integer
41
- #
42
- # @param keys [Array<String>] Environment variable names to try
43
- # @param default [Integer, nil] Default value if none found
44
- # @return [Integer, nil] The value converted to integer, or default
31
+ # Delegates to LanguageOperator::Config.get_int
45
32
  def self.get_int(*keys, default: nil)
46
- value = get(*keys)
47
- return default if value.nil?
48
-
49
- value.to_i
33
+ LanguageOperator::Config.get_int(*keys, default: default)
50
34
  end
51
35
 
52
36
  # Get environment variable as boolean
53
- #
54
- # Treats 'true', '1', 'yes', 'on' as true (case insensitive).
55
- #
56
- # @param keys [Array<String>] Environment variable names to try
57
- # @param default [Boolean] Default value if none found
58
- # @return [Boolean] The value as boolean
37
+ # Delegates to LanguageOperator::Config.get_bool
59
38
  def self.get_bool(*keys, default: false)
60
- value = get(*keys)
61
- return default if value.nil?
62
-
63
- value.to_s.downcase.match?(/^(true|1|yes|on)$/)
39
+ LanguageOperator::Config.get_bool(*keys, default: default)
64
40
  end
65
41
 
66
- # Get environment variable as array (split by separator)
67
- #
68
- # @param keys [Array<String>] Environment variable names to try
69
- # @param default [Array] Default value if none found
70
- # @param separator [String] Character to split on (default: ',')
71
- # @return [Array<String>] The value split into array
42
+ # Get environment variable as array
43
+ # Delegates to LanguageOperator::Config.get_array
72
44
  def self.get_array(*keys, default: [], separator: ',')
73
- value = get(*keys)
74
- return default if value.nil? || value.empty?
75
-
76
- value.split(separator).map(&:strip).reject(&:empty?)
45
+ LanguageOperator::Config.get_array(*keys, default: default, separator: separator)
77
46
  end
78
47
 
79
- # Check if all required keys are present
80
- #
81
- # @param keys [Array<String>] Environment variable names to check
82
- # @return [Array<String>] Array of missing keys (empty if all present)
83
- def self.check_required(*keys)
84
- keys.reject { |key| ENV.fetch(key.to_s, nil) }
85
- end
86
-
87
- # Check if environment variable is set (even if empty string)
88
- #
89
- # @param keys [Array<String>] Environment variable names to check
90
- # @return [Boolean] True if any key is set
48
+ # Check if environment variable is set
49
+ # Delegates to LanguageOperator::Config.set?
91
50
  def self.set?(*keys)
92
- keys.any? { |key| ENV.key?(key.to_s) }
51
+ LanguageOperator::Config.set?(*keys)
93
52
  end
94
53
 
95
54
  # Get all environment variables matching a prefix
96
- #
97
- # @param prefix [String] Prefix to match
98
- # @return [Hash<String, String>] Hash with prefix removed from keys
55
+ # Delegates to LanguageOperator::Config.with_prefix
99
56
  def self.with_prefix(prefix)
100
- ENV.select { |key, _| key.start_with?(prefix) }
101
- .transform_keys { |key| key.sub(prefix, '') }
57
+ LanguageOperator::Config.with_prefix(prefix)
102
58
  end
103
59
 
104
60
  # Build a configuration hash from environment variables
@@ -109,11 +65,19 @@ module LanguageOperator
109
65
  config = {}
110
66
  mappings.each do |config_key, env_keys|
111
67
  env_keys = [env_keys] unless env_keys.is_a?(Array)
112
- value = get(*env_keys)
68
+ value = LanguageOperator::Config.get(*env_keys)
113
69
  config[config_key] = value if value
114
70
  end
115
71
  config
116
72
  end
73
+
74
+ # Check if all required keys are present
75
+ #
76
+ # @param keys [Array<String>] Environment variable names to check
77
+ # @return [Array<String>] Array of missing keys (empty if all present)
78
+ def self.check_required(*keys)
79
+ keys.reject { |key| ENV.fetch(key.to_s, nil) }
80
+ end
117
81
  end
118
82
  end
119
83
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../loggable'
4
+
5
+ module LanguageOperator
6
+ module Dsl
7
+ # Main execution block for agents (DSL v1)
8
+ #
9
+ # Defines the imperative entry point for agent execution. The main block receives
10
+ # agent inputs and returns agent outputs. Within the block, agents can call tasks
11
+ # using execute_task(), use standard Ruby control flow (if/else, loops), and handle
12
+ # errors with standard Ruby exceptions.
13
+ #
14
+ # This replaces the declarative workflow/step model with an imperative programming
15
+ # model centered on organic functions (tasks).
16
+ #
17
+ # @example Simple main block
18
+ # main do |inputs|
19
+ # result = execute_task(:fetch_data, inputs: inputs)
20
+ # execute_task(:process_data, inputs: result)
21
+ # end
22
+ #
23
+ # @example Main block with control flow
24
+ # main do |inputs|
25
+ # data = execute_task(:fetch_data, inputs: inputs)
26
+ #
27
+ # if data[:count] > 100
28
+ # execute_task(:send_alert, inputs: { count: data[:count] })
29
+ # end
30
+ #
31
+ # execute_task(:save_results, inputs: data)
32
+ # end
33
+ #
34
+ # @example Main block with error handling
35
+ # main do |inputs|
36
+ # begin
37
+ # result = execute_task(:risky_operation, inputs: inputs)
38
+ # { success: true, result: result }
39
+ # rescue => e
40
+ # logger.error("Operation failed: #{e.message}")
41
+ # { success: false, error: e.message }
42
+ # end
43
+ # end
44
+ class MainDefinition
45
+ include LanguageOperator::Loggable
46
+
47
+ attr_reader :execute_block
48
+
49
+ # Initialize a new main definition
50
+ def initialize
51
+ @execute_block = nil
52
+ end
53
+
54
+ # Define the main execution block
55
+ #
56
+ # @yield [inputs] Block that receives agent inputs and returns agent outputs
57
+ # @yieldparam inputs [Hash] Agent input parameters
58
+ # @yieldreturn [Object] Agent output (typically a Hash)
59
+ # @return [void]
60
+ # @example
61
+ # execute do |inputs|
62
+ # result = execute_task(:my_task, inputs: inputs)
63
+ # result
64
+ # end
65
+ def execute(&block)
66
+ raise ArgumentError, 'Main block is required' unless block
67
+
68
+ @execute_block = block
69
+ logger.debug('Main block defined', arity: block.arity)
70
+ end
71
+
72
+ # Execute the main block with given inputs
73
+ #
74
+ # @param inputs [Hash] Agent input parameters
75
+ # @param context [Object] Execution context that provides execute_task method
76
+ # @return [Object] Result from main block
77
+ # @raise [RuntimeError] If main block is not defined
78
+ # @raise [ArgumentError] If inputs is not a Hash
79
+ def call(inputs, context)
80
+ raise 'Main block not defined. Use execute { |inputs| ... } to define it.' unless @execute_block
81
+ raise ArgumentError, "inputs must be a Hash, got #{inputs.class}" unless inputs.is_a?(Hash)
82
+
83
+ logger.info('Executing main block', inputs_keys: inputs.keys)
84
+
85
+ result = logger.timed('Main execution') do
86
+ # Execute block in context to provide access to execute_task
87
+ context.instance_exec(inputs, &@execute_block)
88
+ end
89
+
90
+ logger.info('Main block completed')
91
+ result
92
+ rescue StandardError => e
93
+ logger.error('Main block execution failed',
94
+ error: e.class.name,
95
+ message: e.message,
96
+ backtrace: e.backtrace&.first(5))
97
+ raise
98
+ end
99
+
100
+ # Check if main block is defined
101
+ #
102
+ # @return [Boolean] True if execute block is set
103
+ def defined?
104
+ !@execute_block.nil?
105
+ end
106
+
107
+ private
108
+
109
+ def logger_component
110
+ 'Main'
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../version'
4
+ require_relative '../constants'
4
5
 
5
6
  module LanguageOperator
6
7
  module Dsl
@@ -164,7 +165,7 @@ module LanguageOperator
164
165
  mode: {
165
166
  type: 'string',
166
167
  description: 'Execution mode for the agent',
167
- enum: %w[autonomous scheduled reactive]
168
+ enum: Constants::PRIMARY_MODES
168
169
  },
169
170
  objectives: {
170
171
  type: 'array',
@@ -174,9 +175,19 @@ module LanguageOperator
174
175
  },
175
176
  minItems: 0
176
177
  },
177
- workflow: {
178
- '$ref': '#/definitions/WorkflowDefinition'
178
+ # DSL v1 (task/main model)
179
+ tasks: {
180
+ type: 'array',
181
+ description: 'Task definitions (organic functions with stable contracts)',
182
+ items: {
183
+ '$ref': '#/definitions/TaskDefinition'
184
+ }
179
185
  },
186
+ main: {
187
+ '$ref': '#/definitions/MainDefinition',
188
+ description: 'Main execution block (imperative entry point)'
189
+ },
190
+ # Common properties
180
191
  constraints: {
181
192
  '$ref': '#/definitions/ConstraintsDefinition'
182
193
  },
@@ -204,8 +215,11 @@ module LanguageOperator
204
215
  # @return [Hash] Schema definitions for nested types
205
216
  def self.all_definitions
206
217
  {
207
- WorkflowDefinition: workflow_definition_schema,
208
- StepDefinition: step_definition_schema,
218
+ # DSL v1 (task/main model)
219
+ TaskDefinition: task_definition_schema,
220
+ MainDefinition: main_definition_schema,
221
+ TypeSchema: type_schema_definition,
222
+ # Common definitions
209
223
  ConstraintsDefinition: constraints_definition_schema,
210
224
  OutputDefinition: output_definition_schema,
211
225
  WebhookDefinition: webhook_definition_schema,
@@ -217,62 +231,89 @@ module LanguageOperator
217
231
  }
218
232
  end
219
233
 
220
- # Workflow definition schema
234
+ # Task definition schema (DSL v1)
221
235
  #
222
- # @return [Hash] Schema for workflow definitions
223
- def self.workflow_definition_schema
236
+ # @return [Hash] Schema for task definitions (organic functions)
237
+ def self.task_definition_schema
224
238
  {
225
239
  type: 'object',
226
- description: 'Multi-step workflow with dependencies',
240
+ description: 'Organic function with stable contract (inputs/outputs) and evolving implementation',
227
241
  properties: {
228
- steps: {
229
- type: 'array',
230
- description: 'Ordered list of workflow steps',
231
- items: {
232
- '$ref': '#/definitions/StepDefinition'
233
- }
242
+ name: {
243
+ type: 'string',
244
+ description: 'Task identifier (symbol)',
245
+ pattern: '^[a-z_][a-z0-9_]*$'
246
+ },
247
+ inputs: {
248
+ '$ref': '#/definitions/TypeSchema',
249
+ description: 'Input contract (parameter types)'
250
+ },
251
+ outputs: {
252
+ '$ref': '#/definitions/TypeSchema',
253
+ description: 'Output contract (return value types)'
254
+ },
255
+ instructions: {
256
+ type: 'string',
257
+ description: 'Natural language instructions for neural implementation (optional)'
258
+ },
259
+ implementation_type: {
260
+ type: 'string',
261
+ description: 'Implementation approach',
262
+ enum: %w[neural symbolic hybrid undefined]
234
263
  }
235
- }
264
+ },
265
+ required: %w[name inputs outputs]
236
266
  }
237
267
  end
238
268
 
239
- # Step definition schema
269
+ # Main definition schema (DSL v1)
240
270
  #
241
- # @return [Hash] Schema for workflow steps
242
- def self.step_definition_schema
271
+ # @return [Hash] Schema for main execution block
272
+ def self.main_definition_schema
243
273
  {
244
274
  type: 'object',
245
- description: 'Individual workflow step',
275
+ description: 'Imperative entry point for agent execution',
246
276
  properties: {
247
- name: {
277
+ type: {
248
278
  type: 'string',
249
- description: 'Step identifier (symbol or string)'
279
+ description: 'Block type',
280
+ enum: ['main']
250
281
  },
251
- tool: {
282
+ description: {
252
283
  type: 'string',
253
- description: 'Tool name to execute in this step'
254
- },
255
- params: {
256
- type: 'object',
257
- description: 'Parameters to pass to the tool',
258
- additionalProperties: true
259
- },
260
- depends_on: {
261
- oneOf: [
262
- { type: 'string' },
263
- {
264
- type: 'array',
265
- items: { type: 'string' }
266
- }
267
- ],
268
- description: 'Step dependencies (must complete before this step)'
269
- },
270
- prompt: {
284
+ description: 'Main block executes tasks using execute_task() with Ruby control flow'
285
+ }
286
+ },
287
+ additionalProperties: false
288
+ }
289
+ end
290
+
291
+ # Type schema definition (DSL v1)
292
+ #
293
+ # @return [Hash] Schema for type schemas used in task inputs/outputs
294
+ def self.type_schema_definition
295
+ {
296
+ type: 'object',
297
+ description: 'Type schema for task contract validation',
298
+ patternProperties: {
299
+ '^[a-z_][a-z0-9_]*$': {
271
300
  type: 'string',
272
- description: 'LLM prompt template for this step'
301
+ description: 'Parameter type',
302
+ enum: %w[string integer number boolean array hash any]
273
303
  }
274
304
  },
275
- required: %w[name]
305
+ additionalProperties: false,
306
+ examples: [
307
+ {
308
+ user_id: 'integer',
309
+ name: 'string',
310
+ active: 'boolean'
311
+ },
312
+ {
313
+ data: 'array',
314
+ metadata: 'hash'
315
+ }
316
+ ]
276
317
  }
277
318
  end
278
319