language-operator 0.1.61 → 0.1.62
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.
- checksums.yaml +4 -4
- data/.claude/commands/persona.md +9 -0
- data/.claude/commands/task.md +46 -1
- data/.rubocop.yml +13 -0
- data/.rubocop_custom/use_ux_helper.rb +44 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +12 -1
- data/Makefile +26 -7
- data/Makefile.common +50 -0
- data/bin/aictl +8 -1
- data/components/agent/Gemfile +1 -1
- data/components/agent/bin/langop-agent +7 -0
- data/docs/README.md +58 -0
- data/docs/{dsl/best-practices.md → best-practices.md} +4 -4
- data/docs/cli-reference.md +274 -0
- data/docs/{dsl/constraints.md → constraints.md} +5 -5
- data/docs/how-agents-work.md +156 -0
- data/docs/installation.md +218 -0
- data/docs/quickstart.md +299 -0
- data/docs/understanding-generated-code.md +265 -0
- data/docs/using-tools.md +457 -0
- data/docs/webhooks.md +509 -0
- data/examples/ux_helpers_demo.rb +296 -0
- data/lib/language_operator/agent/base.rb +11 -1
- data/lib/language_operator/agent/executor.rb +23 -6
- data/lib/language_operator/agent/safety/safe_executor.rb +41 -39
- data/lib/language_operator/agent/task_executor.rb +346 -63
- data/lib/language_operator/agent/web_server.rb +110 -14
- data/lib/language_operator/agent/webhook_authenticator.rb +39 -5
- data/lib/language_operator/agent.rb +88 -2
- data/lib/language_operator/cli/base_command.rb +17 -11
- data/lib/language_operator/cli/command_loader.rb +72 -0
- data/lib/language_operator/cli/commands/agent/base.rb +837 -0
- data/lib/language_operator/cli/commands/agent/code_operations.rb +102 -0
- data/lib/language_operator/cli/commands/agent/helpers/cluster_llm_client.rb +116 -0
- data/lib/language_operator/cli/commands/agent/helpers/code_parser.rb +115 -0
- data/lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb +96 -0
- data/lib/language_operator/cli/commands/agent/learning.rb +289 -0
- data/lib/language_operator/cli/commands/agent/lifecycle.rb +102 -0
- data/lib/language_operator/cli/commands/agent/logs.rb +125 -0
- data/lib/language_operator/cli/commands/agent/workspace.rb +327 -0
- data/lib/language_operator/cli/commands/cluster.rb +129 -84
- data/lib/language_operator/cli/commands/install.rb +1 -1
- data/lib/language_operator/cli/commands/model/base.rb +215 -0
- data/lib/language_operator/cli/commands/model/test.rb +165 -0
- data/lib/language_operator/cli/commands/persona.rb +16 -34
- data/lib/language_operator/cli/commands/quickstart.rb +3 -2
- data/lib/language_operator/cli/commands/status.rb +40 -67
- data/lib/language_operator/cli/commands/system/base.rb +44 -0
- data/lib/language_operator/cli/commands/system/exec.rb +147 -0
- data/lib/language_operator/cli/commands/system/helpers/llm_synthesis.rb +183 -0
- data/lib/language_operator/cli/commands/system/helpers/pod_manager.rb +212 -0
- data/lib/language_operator/cli/commands/system/helpers/template_loader.rb +57 -0
- data/lib/language_operator/cli/commands/system/helpers/template_validator.rb +174 -0
- data/lib/language_operator/cli/commands/system/schema.rb +92 -0
- data/lib/language_operator/cli/commands/system/synthesis_template.rb +151 -0
- data/lib/language_operator/cli/commands/system/synthesize.rb +224 -0
- data/lib/language_operator/cli/commands/system/validate_template.rb +130 -0
- data/lib/language_operator/cli/commands/tool/base.rb +271 -0
- data/lib/language_operator/cli/commands/tool/install.rb +255 -0
- data/lib/language_operator/cli/commands/tool/search.rb +69 -0
- data/lib/language_operator/cli/commands/tool/test.rb +115 -0
- data/lib/language_operator/cli/commands/use.rb +29 -6
- data/lib/language_operator/cli/errors/handler.rb +20 -17
- data/lib/language_operator/cli/errors/suggestions.rb +3 -5
- data/lib/language_operator/cli/errors/thor_errors.rb +55 -0
- data/lib/language_operator/cli/formatters/code_formatter.rb +4 -11
- data/lib/language_operator/cli/formatters/log_formatter.rb +8 -15
- data/lib/language_operator/cli/formatters/progress_formatter.rb +6 -8
- data/lib/language_operator/cli/formatters/status_formatter.rb +26 -7
- data/lib/language_operator/cli/formatters/table_formatter.rb +47 -36
- data/lib/language_operator/cli/formatters/value_formatter.rb +75 -0
- data/lib/language_operator/cli/helpers/cluster_context.rb +5 -3
- data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +2 -1
- data/lib/language_operator/cli/helpers/label_utils.rb +97 -0
- data/lib/language_operator/{ux/concerns/provider_helpers.rb → cli/helpers/provider_helper.rb} +10 -29
- data/lib/language_operator/cli/helpers/schedule_builder.rb +21 -1
- data/lib/language_operator/cli/helpers/user_prompts.rb +19 -11
- data/lib/language_operator/cli/helpers/ux_helper.rb +538 -0
- data/lib/language_operator/{ux/concerns/input_validation.rb → cli/helpers/validation_helper.rb} +13 -66
- data/lib/language_operator/cli/main.rb +50 -40
- data/lib/language_operator/cli/templates/tools/generic.yaml +3 -0
- data/lib/language_operator/cli/wizards/agent_wizard.rb +12 -20
- data/lib/language_operator/cli/wizards/model_wizard.rb +271 -0
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +8 -34
- data/lib/language_operator/client/base.rb +28 -0
- data/lib/language_operator/client/config.rb +4 -1
- data/lib/language_operator/client/mcp_connector.rb +1 -1
- data/lib/language_operator/config/cluster_config.rb +3 -2
- data/lib/language_operator/config.rb +38 -11
- data/lib/language_operator/constants/kubernetes_labels.rb +80 -0
- data/lib/language_operator/constants.rb +13 -0
- data/lib/language_operator/dsl/http.rb +127 -10
- data/lib/language_operator/dsl.rb +153 -6
- data/lib/language_operator/errors.rb +50 -0
- data/lib/language_operator/kubernetes/client.rb +11 -6
- data/lib/language_operator/kubernetes/resource_builder.rb +58 -84
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
- data/lib/language_operator/type_coercion.rb +118 -34
- data/lib/language_operator/utils/secure_path.rb +74 -0
- data/lib/language_operator/utils.rb +7 -0
- data/lib/language_operator/validators.rb +54 -2
- data/lib/language_operator/version.rb +1 -1
- data/synth/001/Makefile +10 -2
- data/synth/001/agent.rb +16 -15
- data/synth/001/output.log +27 -10
- data/synth/002/Makefile +10 -2
- data/synth/003/Makefile +1 -1
- data/synth/003/README.md +205 -133
- data/synth/003/agent.optimized.rb +66 -0
- data/synth/003/agent.synthesized.rb +41 -0
- metadata +111 -35
- data/docs/dsl/agent-reference.md +0 -604
- data/docs/dsl/mcp-integration.md +0 -1177
- data/docs/dsl/webhooks.md +0 -932
- data/docs/dsl/workflows.md +0 -744
- data/lib/language_operator/cli/commands/agent.rb +0 -1712
- data/lib/language_operator/cli/commands/model.rb +0 -366
- data/lib/language_operator/cli/commands/system.rb +0 -1259
- data/lib/language_operator/cli/commands/tool.rb +0 -654
- data/lib/language_operator/cli/formatters/optimization_formatter.rb +0 -226
- data/lib/language_operator/cli/helpers/pastel_helper.rb +0 -24
- data/lib/language_operator/learning/adapters/base_adapter.rb +0 -149
- data/lib/language_operator/learning/adapters/jaeger_adapter.rb +0 -221
- data/lib/language_operator/learning/adapters/signoz_adapter.rb +0 -435
- data/lib/language_operator/learning/adapters/tempo_adapter.rb +0 -239
- data/lib/language_operator/learning/optimizer.rb +0 -319
- data/lib/language_operator/learning/pattern_detector.rb +0 -260
- data/lib/language_operator/learning/task_synthesizer.rb +0 -288
- data/lib/language_operator/learning/trace_analyzer.rb +0 -285
- data/lib/language_operator/templates/task_synthesis.tmpl +0 -98
- data/lib/language_operator/ux/base.rb +0 -81
- data/lib/language_operator/ux/concerns/README.md +0 -155
- data/lib/language_operator/ux/concerns/headings.rb +0 -90
- data/lib/language_operator/ux/create_agent.rb +0 -255
- data/lib/language_operator/ux/create_model.rb +0 -267
- data/lib/language_operator/ux/quickstart.rb +0 -594
- data/synth/003/agent.rb +0 -41
- data/synth/003/output.log +0 -68
- /data/docs/{architecture/agent-runtime.md → agent-internals.md} +0 -0
- /data/docs/{dsl/chat-endpoints.md → chat-endpoints.md} +0 -0
- /data/docs/{dsl/SCHEMA_VERSION.md → schema-versioning.md} +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Demo script showcasing all UxHelper components
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ruby examples/ux_helpers_demo.rb
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
10
|
+
|
|
11
|
+
require 'language_operator/cli/helpers/ux_helper'
|
|
12
|
+
|
|
13
|
+
# Demo class showing all UX helper features
|
|
14
|
+
class UxDemo
|
|
15
|
+
include LanguageOperator::CLI::Helpers::UxHelper
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
show_header
|
|
19
|
+
demo_colors
|
|
20
|
+
demo_spinner
|
|
21
|
+
demo_table
|
|
22
|
+
demo_box
|
|
23
|
+
demo_highlighted_box
|
|
24
|
+
demo_list_box
|
|
25
|
+
demo_prompt if ARGV.include?('--interactive')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def show_header
|
|
31
|
+
puts "\n"
|
|
32
|
+
puts pastel.bold.cyan('=' * 60)
|
|
33
|
+
puts pastel.bold.cyan(' UxHelper Demo - All Available Components')
|
|
34
|
+
puts pastel.bold.cyan('=' * 60)
|
|
35
|
+
puts "\n"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def demo_colors
|
|
39
|
+
puts pastel.bold('1. Colors & Styles')
|
|
40
|
+
puts pastel.dim('-' * 40)
|
|
41
|
+
puts " #{pastel.green('✓')} Success message"
|
|
42
|
+
puts " #{pastel.yellow('⚠')} Warning message"
|
|
43
|
+
puts " #{pastel.red('✗')} Error message"
|
|
44
|
+
puts " #{pastel.cyan('ℹ')} Info message"
|
|
45
|
+
puts " #{pastel.bold('Bold text')}"
|
|
46
|
+
puts " #{pastel.dim('Dimmed text')}"
|
|
47
|
+
puts " #{pastel.red.bold('Combined: red + bold')}"
|
|
48
|
+
puts "\n"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def demo_spinner
|
|
52
|
+
puts pastel.bold('2. Spinners')
|
|
53
|
+
puts pastel.dim('-' * 40)
|
|
54
|
+
|
|
55
|
+
# Example 1: Success
|
|
56
|
+
spin = spinner('Loading configuration...')
|
|
57
|
+
spin.auto_spin
|
|
58
|
+
sleep 1.5
|
|
59
|
+
spin.success('Config loaded!')
|
|
60
|
+
|
|
61
|
+
# Example 2: Processing
|
|
62
|
+
spin = spinner('Processing data...')
|
|
63
|
+
spin.auto_spin
|
|
64
|
+
sleep 1
|
|
65
|
+
spin.success('Data processed!')
|
|
66
|
+
|
|
67
|
+
# Example 3: Error handling
|
|
68
|
+
spin = spinner('Connecting to cluster...')
|
|
69
|
+
spin.auto_spin
|
|
70
|
+
sleep 1
|
|
71
|
+
if rand > 0.5
|
|
72
|
+
spin.success('Connected!')
|
|
73
|
+
else
|
|
74
|
+
spin.error('Connection failed!')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
puts "\n"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def demo_table
|
|
81
|
+
puts pastel.bold('3. Tables')
|
|
82
|
+
puts pastel.dim('-' * 40)
|
|
83
|
+
|
|
84
|
+
# Agent status table
|
|
85
|
+
agents = [
|
|
86
|
+
['agent-web', pastel.green('running'), '2h 15m', '42.3 MB'],
|
|
87
|
+
['agent-data', pastel.green('running'), '5h 02m', '128.7 MB'],
|
|
88
|
+
['agent-sync', pastel.yellow('pending'), '0m', '-'],
|
|
89
|
+
['agent-test', pastel.red('stopped'), '12m', '8.1 MB']
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
puts table(%w[Name Status Uptime Memory], agents)
|
|
93
|
+
puts "\n"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def demo_box
|
|
97
|
+
puts pastel.bold('4. Boxes')
|
|
98
|
+
puts pastel.dim('-' * 40)
|
|
99
|
+
puts "\n"
|
|
100
|
+
|
|
101
|
+
# Simple box
|
|
102
|
+
puts box('Simple framed message')
|
|
103
|
+
puts "\n"
|
|
104
|
+
|
|
105
|
+
# Box with title
|
|
106
|
+
puts box(
|
|
107
|
+
'Agent deployed successfully to cluster!',
|
|
108
|
+
title: 'Success'
|
|
109
|
+
)
|
|
110
|
+
puts "\n"
|
|
111
|
+
|
|
112
|
+
# Warning box
|
|
113
|
+
puts box(
|
|
114
|
+
'This action cannot be undone.',
|
|
115
|
+
title: 'Warning',
|
|
116
|
+
border: :thick
|
|
117
|
+
)
|
|
118
|
+
puts "\n"
|
|
119
|
+
|
|
120
|
+
# Multi-line box
|
|
121
|
+
puts box(<<~MSG, title: 'Next Steps', padding: 2)
|
|
122
|
+
1. Monitor logs: aictl agent logs my-agent
|
|
123
|
+
2. Check status: aictl agent inspect my-agent
|
|
124
|
+
3. View metrics: aictl agent metrics my-agent
|
|
125
|
+
4. Scale replicas: aictl agent scale my-agent --replicas=3
|
|
126
|
+
MSG
|
|
127
|
+
puts "\n"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def demo_highlighted_box
|
|
131
|
+
puts pastel.bold('5. Highlighted Boxes')
|
|
132
|
+
puts pastel.dim('-' * 40)
|
|
133
|
+
puts "\n"
|
|
134
|
+
|
|
135
|
+
# Model details
|
|
136
|
+
highlighted_box(
|
|
137
|
+
title: 'LanguageModel Details',
|
|
138
|
+
rows: {
|
|
139
|
+
'Name' => 'gpt-4-turbo',
|
|
140
|
+
'Provider' => 'OpenAI',
|
|
141
|
+
'Model' => 'gpt-4-turbo-preview',
|
|
142
|
+
'Cluster' => 'production'
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
puts "\n"
|
|
146
|
+
|
|
147
|
+
# Agent configuration with nil values (skipped)
|
|
148
|
+
highlighted_box(
|
|
149
|
+
title: 'Agent Configuration',
|
|
150
|
+
rows: {
|
|
151
|
+
'Name' => 'web-scraper',
|
|
152
|
+
'Mode' => 'scheduled',
|
|
153
|
+
'Schedule' => '0 */6 * * *',
|
|
154
|
+
'Endpoint' => nil, # This will be skipped
|
|
155
|
+
'Replicas' => '3',
|
|
156
|
+
'Status' => pastel.green('Running')
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
puts "\n"
|
|
160
|
+
|
|
161
|
+
# Resource summary with custom character
|
|
162
|
+
highlighted_box(
|
|
163
|
+
title: 'Resource Summary',
|
|
164
|
+
rows: {
|
|
165
|
+
'Created' => '5 agents',
|
|
166
|
+
'Updated' => '2 models',
|
|
167
|
+
'Deleted' => '0 personas'
|
|
168
|
+
},
|
|
169
|
+
title_char: '▶'
|
|
170
|
+
)
|
|
171
|
+
puts "\n"
|
|
172
|
+
|
|
173
|
+
# Deployment status
|
|
174
|
+
highlighted_box(
|
|
175
|
+
title: 'Deployment Status',
|
|
176
|
+
rows: {
|
|
177
|
+
'Cluster' => 'production-us-west',
|
|
178
|
+
'Namespace' => 'language-operator',
|
|
179
|
+
'Available' => pastel.green('12/12'),
|
|
180
|
+
'Ready' => pastel.green('12/12'),
|
|
181
|
+
'Up-to-date' => pastel.green('12/12')
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
puts "\n"
|
|
185
|
+
|
|
186
|
+
# Custom colors
|
|
187
|
+
highlighted_box(
|
|
188
|
+
title: 'Success',
|
|
189
|
+
rows: {
|
|
190
|
+
'Operation' => 'Model created',
|
|
191
|
+
'Status' => 'Completed'
|
|
192
|
+
},
|
|
193
|
+
color: :green
|
|
194
|
+
)
|
|
195
|
+
puts "\n"
|
|
196
|
+
|
|
197
|
+
highlighted_box(
|
|
198
|
+
title: 'Error',
|
|
199
|
+
rows: {
|
|
200
|
+
'Code' => '500',
|
|
201
|
+
'Message' => 'Connection failed'
|
|
202
|
+
},
|
|
203
|
+
color: :red
|
|
204
|
+
)
|
|
205
|
+
puts "\n"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def demo_list_box
|
|
209
|
+
puts pastel.bold('6. List Boxes')
|
|
210
|
+
puts pastel.dim('-' * 40)
|
|
211
|
+
puts "\n"
|
|
212
|
+
|
|
213
|
+
# Simple list
|
|
214
|
+
list_box(
|
|
215
|
+
title: 'Models',
|
|
216
|
+
items: ['gpt-4-turbo', 'claude-3-opus', 'llama-3-70b']
|
|
217
|
+
)
|
|
218
|
+
puts "\n"
|
|
219
|
+
|
|
220
|
+
# Detailed list
|
|
221
|
+
list_box(
|
|
222
|
+
title: 'Agents',
|
|
223
|
+
items: [
|
|
224
|
+
{ name: 'bash-agent', status: pastel.green('Running') },
|
|
225
|
+
{ name: 'web-scraper', status: pastel.yellow('Pending') },
|
|
226
|
+
{ name: 'data-processor', status: pastel.red('Stopped') }
|
|
227
|
+
],
|
|
228
|
+
style: :detailed
|
|
229
|
+
)
|
|
230
|
+
puts "\n"
|
|
231
|
+
|
|
232
|
+
# Conditions style
|
|
233
|
+
list_box(
|
|
234
|
+
title: 'Conditions',
|
|
235
|
+
items: [
|
|
236
|
+
{ type: 'Ready', status: 'True', message: 'Agent is ready' },
|
|
237
|
+
{ type: 'Synthesized', status: 'True', message: 'Code synthesized successfully' },
|
|
238
|
+
{ type: 'Validated', status: 'False', message: 'Validation pending' }
|
|
239
|
+
],
|
|
240
|
+
style: :conditions
|
|
241
|
+
)
|
|
242
|
+
puts "\n"
|
|
243
|
+
|
|
244
|
+
# Key-value pairs
|
|
245
|
+
list_box(
|
|
246
|
+
title: 'Labels',
|
|
247
|
+
items: {
|
|
248
|
+
'app' => 'language-operator',
|
|
249
|
+
'env' => 'production',
|
|
250
|
+
'version' => 'v1.0.0'
|
|
251
|
+
},
|
|
252
|
+
style: :key_value
|
|
253
|
+
)
|
|
254
|
+
puts "\n"
|
|
255
|
+
|
|
256
|
+
# Empty list
|
|
257
|
+
list_box(
|
|
258
|
+
title: 'Personas',
|
|
259
|
+
items: [],
|
|
260
|
+
empty_message: 'No personas configured'
|
|
261
|
+
)
|
|
262
|
+
puts "\n"
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def demo_prompt
|
|
266
|
+
puts pastel.bold('7. Interactive Prompts')
|
|
267
|
+
puts pastel.dim('-' * 40)
|
|
268
|
+
|
|
269
|
+
name = prompt.ask('What is your name?')
|
|
270
|
+
puts " Hello, #{pastel.cyan(name)}!"
|
|
271
|
+
|
|
272
|
+
if prompt.yes?('Do you like the new UX helpers?')
|
|
273
|
+
puts " #{pastel.green('Great!')} We're glad you like them."
|
|
274
|
+
else
|
|
275
|
+
puts " #{pastel.yellow('Thanks for the feedback!')} We'll keep improving."
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
choice = prompt.select(
|
|
279
|
+
'Which helper is your favorite?',
|
|
280
|
+
%w[pastel prompt spinner table box highlighted_box list_box]
|
|
281
|
+
)
|
|
282
|
+
puts " You selected: #{pastel.bold(choice)}"
|
|
283
|
+
|
|
284
|
+
puts "\n"
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Run the demo
|
|
289
|
+
if __FILE__ == $PROGRAM_NAME
|
|
290
|
+
demo = UxDemo.new
|
|
291
|
+
demo.run
|
|
292
|
+
|
|
293
|
+
puts demo.pastel.bold.cyan('Demo complete!')
|
|
294
|
+
puts demo.pastel.dim("Run with --interactive for prompt examples: ruby #{__FILE__} --interactive")
|
|
295
|
+
puts "\n"
|
|
296
|
+
end
|
|
@@ -38,7 +38,7 @@ module LanguageOperator
|
|
|
38
38
|
logger.info "OpenTelemetry #{otel_enabled ? 'enabled' : 'disabled'}"
|
|
39
39
|
|
|
40
40
|
@workspace_path = ENV.fetch('WORKSPACE_PATH', '/workspace')
|
|
41
|
-
@mode =
|
|
41
|
+
@mode = agent_mode_with_default
|
|
42
42
|
@executor = nil
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -134,6 +134,16 @@ module LanguageOperator
|
|
|
134
134
|
rescue StandardError => e
|
|
135
135
|
logger.warn("Failed to flush telemetry: #{e.message}")
|
|
136
136
|
end
|
|
137
|
+
|
|
138
|
+
# Get AGENT_MODE with fallback to default, handling empty/whitespace values
|
|
139
|
+
#
|
|
140
|
+
# @return [String] The agent mode to use
|
|
141
|
+
def agent_mode_with_default
|
|
142
|
+
mode = ENV.fetch('AGENT_MODE', nil)
|
|
143
|
+
return 'autonomous' if mode.nil? || mode.strip.empty?
|
|
144
|
+
|
|
145
|
+
mode
|
|
146
|
+
end
|
|
137
147
|
end
|
|
138
148
|
end
|
|
139
149
|
end
|
|
@@ -56,6 +56,16 @@ module LanguageOperator
|
|
|
56
56
|
execute(enriched_instruction)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Cleanup executor resources including MCP connections
|
|
60
|
+
#
|
|
61
|
+
# This method delegates to the agent's connection cleanup to prevent
|
|
62
|
+
# resource leaks when executors are no longer needed.
|
|
63
|
+
#
|
|
64
|
+
# @return [void]
|
|
65
|
+
def cleanup_connections
|
|
66
|
+
@agent.cleanup_connections if @agent.respond_to?(:cleanup_connections)
|
|
67
|
+
end
|
|
68
|
+
|
|
59
69
|
# Execute a single task
|
|
60
70
|
#
|
|
61
71
|
# @param task [String] The task to execute
|
|
@@ -282,23 +292,30 @@ module LanguageOperator
|
|
|
282
292
|
|
|
283
293
|
def parse_float_env(key)
|
|
284
294
|
val = ENV.fetch(key, nil)
|
|
285
|
-
return nil unless val
|
|
295
|
+
return nil unless val && !val.strip.empty?
|
|
286
296
|
|
|
287
|
-
val.
|
|
297
|
+
Float(val.strip)
|
|
298
|
+
rescue ArgumentError
|
|
299
|
+
logger.warn("Invalid float value for #{key}: #{val}. Ignoring.")
|
|
300
|
+
nil
|
|
288
301
|
end
|
|
289
302
|
|
|
290
303
|
def parse_int_env(key)
|
|
291
304
|
val = ENV.fetch(key, nil)
|
|
292
|
-
return nil unless val
|
|
305
|
+
return nil unless val && !val.strip.empty?
|
|
293
306
|
|
|
294
|
-
val.
|
|
307
|
+
Integer(val.strip)
|
|
308
|
+
rescue ArgumentError
|
|
309
|
+
logger.warn("Invalid integer value for #{key}: #{val}. Ignoring.")
|
|
310
|
+
nil
|
|
295
311
|
end
|
|
296
312
|
|
|
297
313
|
def parse_array_env(key)
|
|
298
314
|
val = ENV.fetch(key, nil)
|
|
299
|
-
return nil unless val
|
|
315
|
+
return nil unless val && !val.strip.empty?
|
|
300
316
|
|
|
301
|
-
val.split(',').map(&:strip)
|
|
317
|
+
result = val.split(',').map(&:strip).reject(&:empty?)
|
|
318
|
+
result.empty? ? nil : result
|
|
302
319
|
end
|
|
303
320
|
|
|
304
321
|
def estimate_tokens(text)
|
|
@@ -34,32 +34,26 @@ module LanguageOperator
|
|
|
34
34
|
# Step 2: Execute in sandboxed context
|
|
35
35
|
sandbox = SandboxProxy.new(@context, self)
|
|
36
36
|
|
|
37
|
-
# Step 3:
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# sandbox.instance_eval("Numeric = ::Numeric\nInteger = ::Integer\nFloat = ::Float\n
|
|
58
|
-
# String = ::String\nArray = ::Array\nHash = ::Hash\nTrueClass = ::TrueClass\n
|
|
59
|
-
# FalseClass = ::FalseClass\nTime = ::Time\nDate = ::Date\n<user code>", __FILE__, __LINE__)
|
|
60
|
-
# rubocop:disable Style/DocumentDynamicEvalDefinition
|
|
61
|
-
sandbox.instance_eval("#{safe_constants_code}\n#{code}", __FILE__, __LINE__)
|
|
62
|
-
# rubocop:enable Style/DocumentDynamicEvalDefinition
|
|
37
|
+
# Step 3: Execute using instance_eval with smart constant injection
|
|
38
|
+
# Only inject constants that won't conflict with user-defined ones
|
|
39
|
+
safe_constants = %w[Numeric Integer Float String Array Hash TrueClass FalseClass Time Date]
|
|
40
|
+
|
|
41
|
+
# Find which constants user code defines to avoid redefinition warnings
|
|
42
|
+
user_defined_constants = safe_constants.select { |const| code.include?("#{const} =") }
|
|
43
|
+
|
|
44
|
+
# Only inject constants that user code doesn't define
|
|
45
|
+
constants_to_inject = safe_constants - user_defined_constants
|
|
46
|
+
|
|
47
|
+
if constants_to_inject.any?
|
|
48
|
+
# Inject only safe constants that won't conflict
|
|
49
|
+
safe_setup = constants_to_inject.map { |name| "#{name} = ::#{name}" }.join("\n")
|
|
50
|
+
# rubocop:disable Style/DocumentDynamicEvalDefinition
|
|
51
|
+
sandbox.instance_eval("#{safe_setup}\n#{code}", __FILE__, __LINE__)
|
|
52
|
+
# rubocop:enable Style/DocumentDynamicEvalDefinition
|
|
53
|
+
else
|
|
54
|
+
# User defines all constants, just run their code
|
|
55
|
+
sandbox.instance_eval(code, file_path, 1)
|
|
56
|
+
end
|
|
63
57
|
rescue ASTValidator::SecurityError => e
|
|
64
58
|
# Re-raise validation errors as executor errors for clarity
|
|
65
59
|
raise SecurityError, "Code validation failed: #{e.message}"
|
|
@@ -119,20 +113,28 @@ module LanguageOperator
|
|
|
119
113
|
|
|
120
114
|
# Provide access to safe constants from the context
|
|
121
115
|
def const_missing(name)
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
# Define allowed constants explicitly (security-by-default)
|
|
117
|
+
# This allowlist must be kept in sync with ASTValidator safe constants
|
|
118
|
+
case name
|
|
119
|
+
when :HTTP
|
|
120
|
+
::LanguageOperator::Dsl::HTTP
|
|
121
|
+
when :Shell
|
|
122
|
+
::LanguageOperator::Dsl::Shell
|
|
123
|
+
when :String, :Array, :Hash, :Integer, :Float, :Numeric, :Symbol
|
|
124
|
+
# Allow safe Ruby built-in types
|
|
125
|
+
::Object.const_get(name)
|
|
126
|
+
when :Time, :Date
|
|
127
|
+
# Allow safe time/date types
|
|
128
|
+
::Object.const_get(name)
|
|
129
|
+
when :TrueClass, :FalseClass, :NilClass
|
|
130
|
+
# Allow boolean and nil types
|
|
131
|
+
::Object.const_get(name)
|
|
132
|
+
else
|
|
133
|
+
# Security-by-default: explicitly deny access to any other constants
|
|
134
|
+
# This prevents sandbox bypass through const_missing fallback
|
|
135
|
+
::Kernel.raise ::LanguageOperator::Agent::Safety::SafeExecutor::SecurityError,
|
|
136
|
+
"Access to constant '#{name}' is not allowed in sandbox (security restriction)"
|
|
127
137
|
end
|
|
128
|
-
|
|
129
|
-
# Ruby type constants are now injected at eval time (see SafeExecutor#eval)
|
|
130
|
-
# but keep this as fallback for dynamic constant access
|
|
131
|
-
|
|
132
|
-
# Otherwise delegate to the context's module
|
|
133
|
-
@__context__.class.const_get(name)
|
|
134
|
-
rescue ::NameError
|
|
135
|
-
::Kernel.raise ::NameError, "uninitialized constant #{name}"
|
|
136
138
|
end
|
|
137
139
|
|
|
138
140
|
private
|