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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/persona.md +9 -0
  3. data/.claude/commands/task.md +46 -1
  4. data/.rubocop.yml +13 -0
  5. data/.rubocop_custom/use_ux_helper.rb +44 -0
  6. data/CHANGELOG.md +8 -0
  7. data/Gemfile.lock +12 -1
  8. data/Makefile +26 -7
  9. data/Makefile.common +50 -0
  10. data/bin/aictl +8 -1
  11. data/components/agent/Gemfile +1 -1
  12. data/components/agent/bin/langop-agent +7 -0
  13. data/docs/README.md +58 -0
  14. data/docs/{dsl/best-practices.md → best-practices.md} +4 -4
  15. data/docs/cli-reference.md +274 -0
  16. data/docs/{dsl/constraints.md → constraints.md} +5 -5
  17. data/docs/how-agents-work.md +156 -0
  18. data/docs/installation.md +218 -0
  19. data/docs/quickstart.md +299 -0
  20. data/docs/understanding-generated-code.md +265 -0
  21. data/docs/using-tools.md +457 -0
  22. data/docs/webhooks.md +509 -0
  23. data/examples/ux_helpers_demo.rb +296 -0
  24. data/lib/language_operator/agent/base.rb +11 -1
  25. data/lib/language_operator/agent/executor.rb +23 -6
  26. data/lib/language_operator/agent/safety/safe_executor.rb +41 -39
  27. data/lib/language_operator/agent/task_executor.rb +346 -63
  28. data/lib/language_operator/agent/web_server.rb +110 -14
  29. data/lib/language_operator/agent/webhook_authenticator.rb +39 -5
  30. data/lib/language_operator/agent.rb +88 -2
  31. data/lib/language_operator/cli/base_command.rb +17 -11
  32. data/lib/language_operator/cli/command_loader.rb +72 -0
  33. data/lib/language_operator/cli/commands/agent/base.rb +837 -0
  34. data/lib/language_operator/cli/commands/agent/code_operations.rb +102 -0
  35. data/lib/language_operator/cli/commands/agent/helpers/cluster_llm_client.rb +116 -0
  36. data/lib/language_operator/cli/commands/agent/helpers/code_parser.rb +115 -0
  37. data/lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb +96 -0
  38. data/lib/language_operator/cli/commands/agent/learning.rb +289 -0
  39. data/lib/language_operator/cli/commands/agent/lifecycle.rb +102 -0
  40. data/lib/language_operator/cli/commands/agent/logs.rb +125 -0
  41. data/lib/language_operator/cli/commands/agent/workspace.rb +327 -0
  42. data/lib/language_operator/cli/commands/cluster.rb +129 -84
  43. data/lib/language_operator/cli/commands/install.rb +1 -1
  44. data/lib/language_operator/cli/commands/model/base.rb +215 -0
  45. data/lib/language_operator/cli/commands/model/test.rb +165 -0
  46. data/lib/language_operator/cli/commands/persona.rb +16 -34
  47. data/lib/language_operator/cli/commands/quickstart.rb +3 -2
  48. data/lib/language_operator/cli/commands/status.rb +40 -67
  49. data/lib/language_operator/cli/commands/system/base.rb +44 -0
  50. data/lib/language_operator/cli/commands/system/exec.rb +147 -0
  51. data/lib/language_operator/cli/commands/system/helpers/llm_synthesis.rb +183 -0
  52. data/lib/language_operator/cli/commands/system/helpers/pod_manager.rb +212 -0
  53. data/lib/language_operator/cli/commands/system/helpers/template_loader.rb +57 -0
  54. data/lib/language_operator/cli/commands/system/helpers/template_validator.rb +174 -0
  55. data/lib/language_operator/cli/commands/system/schema.rb +92 -0
  56. data/lib/language_operator/cli/commands/system/synthesis_template.rb +151 -0
  57. data/lib/language_operator/cli/commands/system/synthesize.rb +224 -0
  58. data/lib/language_operator/cli/commands/system/validate_template.rb +130 -0
  59. data/lib/language_operator/cli/commands/tool/base.rb +271 -0
  60. data/lib/language_operator/cli/commands/tool/install.rb +255 -0
  61. data/lib/language_operator/cli/commands/tool/search.rb +69 -0
  62. data/lib/language_operator/cli/commands/tool/test.rb +115 -0
  63. data/lib/language_operator/cli/commands/use.rb +29 -6
  64. data/lib/language_operator/cli/errors/handler.rb +20 -17
  65. data/lib/language_operator/cli/errors/suggestions.rb +3 -5
  66. data/lib/language_operator/cli/errors/thor_errors.rb +55 -0
  67. data/lib/language_operator/cli/formatters/code_formatter.rb +4 -11
  68. data/lib/language_operator/cli/formatters/log_formatter.rb +8 -15
  69. data/lib/language_operator/cli/formatters/progress_formatter.rb +6 -8
  70. data/lib/language_operator/cli/formatters/status_formatter.rb +26 -7
  71. data/lib/language_operator/cli/formatters/table_formatter.rb +47 -36
  72. data/lib/language_operator/cli/formatters/value_formatter.rb +75 -0
  73. data/lib/language_operator/cli/helpers/cluster_context.rb +5 -3
  74. data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +2 -1
  75. data/lib/language_operator/cli/helpers/label_utils.rb +97 -0
  76. data/lib/language_operator/{ux/concerns/provider_helpers.rb → cli/helpers/provider_helper.rb} +10 -29
  77. data/lib/language_operator/cli/helpers/schedule_builder.rb +21 -1
  78. data/lib/language_operator/cli/helpers/user_prompts.rb +19 -11
  79. data/lib/language_operator/cli/helpers/ux_helper.rb +538 -0
  80. data/lib/language_operator/{ux/concerns/input_validation.rb → cli/helpers/validation_helper.rb} +13 -66
  81. data/lib/language_operator/cli/main.rb +50 -40
  82. data/lib/language_operator/cli/templates/tools/generic.yaml +3 -0
  83. data/lib/language_operator/cli/wizards/agent_wizard.rb +12 -20
  84. data/lib/language_operator/cli/wizards/model_wizard.rb +271 -0
  85. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +8 -34
  86. data/lib/language_operator/client/base.rb +28 -0
  87. data/lib/language_operator/client/config.rb +4 -1
  88. data/lib/language_operator/client/mcp_connector.rb +1 -1
  89. data/lib/language_operator/config/cluster_config.rb +3 -2
  90. data/lib/language_operator/config.rb +38 -11
  91. data/lib/language_operator/constants/kubernetes_labels.rb +80 -0
  92. data/lib/language_operator/constants.rb +13 -0
  93. data/lib/language_operator/dsl/http.rb +127 -10
  94. data/lib/language_operator/dsl.rb +153 -6
  95. data/lib/language_operator/errors.rb +50 -0
  96. data/lib/language_operator/kubernetes/client.rb +11 -6
  97. data/lib/language_operator/kubernetes/resource_builder.rb +58 -84
  98. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
  99. data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
  100. data/lib/language_operator/type_coercion.rb +118 -34
  101. data/lib/language_operator/utils/secure_path.rb +74 -0
  102. data/lib/language_operator/utils.rb +7 -0
  103. data/lib/language_operator/validators.rb +54 -2
  104. data/lib/language_operator/version.rb +1 -1
  105. data/synth/001/Makefile +10 -2
  106. data/synth/001/agent.rb +16 -15
  107. data/synth/001/output.log +27 -10
  108. data/synth/002/Makefile +10 -2
  109. data/synth/003/Makefile +1 -1
  110. data/synth/003/README.md +205 -133
  111. data/synth/003/agent.optimized.rb +66 -0
  112. data/synth/003/agent.synthesized.rb +41 -0
  113. metadata +111 -35
  114. data/docs/dsl/agent-reference.md +0 -604
  115. data/docs/dsl/mcp-integration.md +0 -1177
  116. data/docs/dsl/webhooks.md +0 -932
  117. data/docs/dsl/workflows.md +0 -744
  118. data/lib/language_operator/cli/commands/agent.rb +0 -1712
  119. data/lib/language_operator/cli/commands/model.rb +0 -366
  120. data/lib/language_operator/cli/commands/system.rb +0 -1259
  121. data/lib/language_operator/cli/commands/tool.rb +0 -654
  122. data/lib/language_operator/cli/formatters/optimization_formatter.rb +0 -226
  123. data/lib/language_operator/cli/helpers/pastel_helper.rb +0 -24
  124. data/lib/language_operator/learning/adapters/base_adapter.rb +0 -149
  125. data/lib/language_operator/learning/adapters/jaeger_adapter.rb +0 -221
  126. data/lib/language_operator/learning/adapters/signoz_adapter.rb +0 -435
  127. data/lib/language_operator/learning/adapters/tempo_adapter.rb +0 -239
  128. data/lib/language_operator/learning/optimizer.rb +0 -319
  129. data/lib/language_operator/learning/pattern_detector.rb +0 -260
  130. data/lib/language_operator/learning/task_synthesizer.rb +0 -288
  131. data/lib/language_operator/learning/trace_analyzer.rb +0 -285
  132. data/lib/language_operator/templates/task_synthesis.tmpl +0 -98
  133. data/lib/language_operator/ux/base.rb +0 -81
  134. data/lib/language_operator/ux/concerns/README.md +0 -155
  135. data/lib/language_operator/ux/concerns/headings.rb +0 -90
  136. data/lib/language_operator/ux/create_agent.rb +0 -255
  137. data/lib/language_operator/ux/create_model.rb +0 -267
  138. data/lib/language_operator/ux/quickstart.rb +0 -594
  139. data/synth/003/agent.rb +0 -41
  140. data/synth/003/output.log +0 -68
  141. /data/docs/{architecture/agent-runtime.md → agent-internals.md} +0 -0
  142. /data/docs/{dsl/chat-endpoints.md → chat-endpoints.md} +0 -0
  143. /data/docs/{dsl/SCHEMA_VERSION.md → schema-versioning.md} +0 -0
@@ -0,0 +1,327 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'shellwords'
5
+ require_relative '../../helpers/label_utils'
6
+ require_relative '../../../constants/kubernetes_labels'
7
+
8
+ module LanguageOperator
9
+ module CLI
10
+ module Commands
11
+ module Agent
12
+ # Workspace management for agents
13
+ module Workspace
14
+ def self.included(base)
15
+ base.class_eval do
16
+ desc 'workspace NAME', 'Browse agent workspace files'
17
+ long_desc <<-DESC
18
+ Browse and manage the workspace files for an agent.
19
+
20
+ Workspaces provide persistent storage for agents to maintain state,
21
+ cache data, and remember information across executions.
22
+
23
+ Examples:
24
+ aictl agent workspace my-agent # List all files
25
+ aictl agent workspace my-agent --path /workspace/state.json # View specific file
26
+ aictl agent workspace my-agent --clean # Clear workspace
27
+ DESC
28
+ option :cluster, type: :string, desc: 'Override current cluster context'
29
+ option :path, type: :string, desc: 'View specific file contents'
30
+ option :clean, type: :boolean, desc: 'Clear workspace (with confirmation)'
31
+ def workspace(name)
32
+ handle_command_error('access workspace') do
33
+ ctx = CLI::Helpers::ClusterContext.from_options(options)
34
+
35
+ # Get agent to verify it exists
36
+ agent = get_resource_or_exit(LanguageOperator::Constants::RESOURCE_AGENT, name)
37
+
38
+ # Check if workspace is enabled
39
+ workspace_enabled = agent.dig('spec', 'workspace', 'enabled')
40
+ unless workspace_enabled
41
+ Formatters::ProgressFormatter.warn("Workspace is not enabled for agent '#{name}'")
42
+ puts
43
+ puts 'Enable workspace in agent configuration:'
44
+ puts ' spec:'
45
+ puts ' workspace:'
46
+ puts ' enabled: true'
47
+ puts ' size: 10Gi'
48
+ exit 1
49
+ end
50
+
51
+ if options[:path]
52
+ view_workspace_file(ctx, name, options[:path])
53
+ elsif options[:clean]
54
+ clean_workspace(ctx, name)
55
+ else
56
+ list_workspace_files(ctx, name)
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def get_agent_pod(ctx, agent_name)
64
+ # Validate agent name for label compatibility
65
+ unless CLI::Helpers::LabelUtils.valid_label_value?(agent_name)
66
+ Formatters::ProgressFormatter.error("Agent name '#{agent_name}' is not valid for Kubernetes labels")
67
+ puts
68
+ puts 'Agent names must:'
69
+ puts ' - Be 63 characters or less'
70
+ puts ' - Contain only lowercase letters, numbers, hyphens, and dots'
71
+ puts ' - Start and end with alphanumeric characters'
72
+ exit 1
73
+ end
74
+
75
+ # Find pod for this agent using normalized label selector
76
+ label_selector = CLI::Helpers::LabelUtils.agent_pod_selector(agent_name)
77
+ pods = ctx.client.list_resources('Pod', namespace: ctx.namespace, label_selector: label_selector)
78
+
79
+ if pods.empty?
80
+ debug_info = CLI::Helpers::LabelUtils.debug_pod_search(ctx, agent_name)
81
+
82
+ Formatters::ProgressFormatter.error("No running pods found for agent '#{agent_name}'")
83
+ puts
84
+ puts 'Possible reasons:'
85
+ puts ' - Agent pod has not started yet'
86
+ puts ' - Agent is paused or stopped'
87
+ puts ' - Agent failed to deploy'
88
+ puts ' - Label mismatch (debugging info below)'
89
+ puts
90
+ puts 'Debug information:'
91
+ puts " Agent name: #{debug_info[:agent_name]}"
92
+ puts " Normalized: #{debug_info[:normalized_name]}"
93
+ puts " Label selector: #{debug_info[:label_selector]}"
94
+ puts " Namespace: #{debug_info[:namespace]}"
95
+ puts
96
+ puts 'Check agent status with:'
97
+ puts " aictl agent inspect #{agent_name}"
98
+ puts
99
+ puts 'List all pods in namespace:'
100
+ puts " kubectl get pods -n #{ctx.namespace} -l #{Constants::KubernetesLabels::COMPONENT}=#{Constants::KubernetesLabels::COMPONENT_AGENT}"
101
+ exit 1
102
+ end
103
+
104
+ # Find a running pod
105
+ running_pod = pods.find do |pod|
106
+ pod.dig('status', 'phase') == 'Running'
107
+ end
108
+
109
+ unless running_pod
110
+ Formatters::ProgressFormatter.error('Agent pod exists but is not running')
111
+ puts
112
+ puts "Current pod status: #{pods.first.dig('status', 'phase')}"
113
+ puts
114
+ puts 'Check pod logs with:'
115
+ puts " aictl agent logs #{agent_name}"
116
+ exit 1
117
+ end
118
+
119
+ running_pod.dig('metadata', 'name')
120
+ end
121
+
122
+ def exec_in_pod(ctx, pod_name, command)
123
+ # Build command as array to prevent shell injection
124
+ kubectl_prefix_array = Shellwords.shellsplit(ctx.kubectl_prefix)
125
+ cmd_array = kubectl_prefix_array + ['exec', pod_name, '--']
126
+
127
+ # Add command arguments
128
+ cmd_array += if command.is_a?(Array)
129
+ command
130
+ else
131
+ [command]
132
+ end
133
+
134
+ # Execute with array to avoid shell interpolation
135
+ stdout, stderr, status = Open3.capture3(*cmd_array)
136
+
137
+ raise "Command failed: #{stderr}" unless status.success?
138
+
139
+ stdout
140
+ end
141
+
142
+ def list_workspace_files(ctx, agent_name)
143
+ pod_name = get_agent_pod(ctx, agent_name)
144
+
145
+ # Check if workspace directory exists
146
+ begin
147
+ exec_in_pod(ctx, pod_name, 'test -d /workspace')
148
+ rescue StandardError
149
+ Formatters::ProgressFormatter.error('Workspace directory not found in agent pod')
150
+ puts
151
+ puts 'The /workspace directory does not exist in the agent pod.'
152
+ puts 'This agent may not have workspace support enabled.'
153
+ exit 1
154
+ end
155
+
156
+ # Get workspace usage
157
+ usage_output = exec_in_pod(
158
+ ctx,
159
+ pod_name,
160
+ 'du -sh /workspace 2>/dev/null || echo "0\t/workspace"'
161
+ )
162
+ workspace_size = usage_output.split("\t").first.strip
163
+
164
+ # List files with details
165
+ file_list = exec_in_pod(
166
+ ctx,
167
+ pod_name,
168
+ 'find /workspace -ls 2>/dev/null | tail -n +2'
169
+ )
170
+
171
+ puts
172
+ puts pastel.cyan("Workspace for agent '#{agent_name}' (#{workspace_size})")
173
+ puts '=' * 60
174
+ puts
175
+
176
+ if file_list.strip.empty?
177
+ puts pastel.dim('Workspace is empty')
178
+ puts
179
+ puts 'The agent will create files here as it runs.'
180
+ puts
181
+ return
182
+ end
183
+
184
+ # Parse and display file list
185
+ file_list.each_line do |line|
186
+ parts = line.strip.split(/\s+/, 11)
187
+ next if parts.length < 11
188
+
189
+ # Extract relevant parts
190
+ # Format: inode blocks perms links user group size month day time path
191
+ perms = parts[2]
192
+ size = parts[6]
193
+ month = parts[7]
194
+ day = parts[8]
195
+ time_or_year = parts[9]
196
+ path = parts[10]
197
+
198
+ # Skip the /workspace directory itself
199
+ next if path == '/workspace'
200
+
201
+ # Determine file type and icon
202
+ icon = if perms.start_with?('d')
203
+ pastel.blue('📁')
204
+ else
205
+ pastel.white('📄')
206
+ end
207
+
208
+ # Format path relative to workspace
209
+ relative_path = path.sub('/workspace/', '')
210
+ indent = ' ' * relative_path.count('/')
211
+
212
+ # Format size
213
+ formatted_size = format_file_size(size.to_i).rjust(8)
214
+
215
+ # Format time
216
+ formatted_time = "#{month} #{day.rjust(2)} #{time_or_year}"
217
+
218
+ puts "#{indent}#{icon} #{File.basename(relative_path).ljust(30)} #{pastel.dim(formatted_size)} #{pastel.dim(formatted_time)}"
219
+ end
220
+
221
+ puts
222
+ puts pastel.dim('Commands:')
223
+ puts pastel.dim(" aictl agent workspace #{agent_name} --path /workspace/<file> # View file")
224
+ puts pastel.dim(" aictl agent workspace #{agent_name} --clean # Clear workspace")
225
+ puts
226
+ end
227
+
228
+ def view_workspace_file(ctx, agent_name, file_path)
229
+ pod_name = get_agent_pod(ctx, agent_name)
230
+
231
+ # Check if file exists
232
+ begin
233
+ exec_in_pod(ctx, pod_name, "test -f #{file_path}")
234
+ rescue StandardError
235
+ Formatters::ProgressFormatter.error("File not found: #{file_path}")
236
+ puts
237
+ puts 'List available files with:'
238
+ puts " aictl agent workspace #{agent_name}"
239
+ exit 1
240
+ end
241
+
242
+ # Get file metadata
243
+ stat_output = exec_in_pod(
244
+ ctx,
245
+ pod_name,
246
+ "stat -c '%s %Y' #{file_path}"
247
+ )
248
+ size, mtime = stat_output.strip.split
249
+
250
+ # Get file contents
251
+ contents = exec_in_pod(
252
+ ctx,
253
+ pod_name,
254
+ "cat #{file_path}"
255
+ )
256
+
257
+ # Display file
258
+ puts
259
+ puts pastel.cyan("File: #{file_path}")
260
+ puts "Size: #{format_file_size(size.to_i)}"
261
+ puts "Modified: #{format_timestamp(Time.at(mtime.to_i))}"
262
+ puts '=' * 60
263
+ puts
264
+ puts contents
265
+ puts
266
+ end
267
+
268
+ def clean_workspace(ctx, agent_name)
269
+ pod_name = get_agent_pod(ctx, agent_name)
270
+
271
+ # Get current workspace usage
272
+ usage_output = exec_in_pod(
273
+ ctx,
274
+ pod_name,
275
+ 'du -sh /workspace 2>/dev/null || echo "0\t/workspace"'
276
+ )
277
+ workspace_size = usage_output.split("\t").first.strip
278
+
279
+ # Count files
280
+ file_count = exec_in_pod(
281
+ ctx,
282
+ pod_name,
283
+ 'find /workspace -type f | wc -l'
284
+ ).strip.to_i
285
+
286
+ puts
287
+ puts pastel.yellow("This will delete ALL files in the workspace for '#{agent_name}'")
288
+ puts
289
+ puts 'The agent will lose:'
290
+ puts ' • Execution history'
291
+ puts ' • Cached data'
292
+ puts ' • State information'
293
+ puts
294
+ puts "Current workspace: #{file_count} files, #{workspace_size}"
295
+ puts
296
+
297
+ # Use UserPrompts helper
298
+ return unless CLI::Helpers::UserPrompts.confirm('Are you sure?')
299
+
300
+ # Delete all files in workspace
301
+ Formatters::ProgressFormatter.with_spinner('Cleaning workspace') do
302
+ exec_in_pod(
303
+ ctx,
304
+ pod_name,
305
+ 'find /workspace -mindepth 1 -delete'
306
+ )
307
+ end
308
+
309
+ Formatters::ProgressFormatter.success("Workspace cleared (freed #{workspace_size})")
310
+ puts
311
+ puts 'The agent will start fresh on its next execution.'
312
+ end
313
+
314
+ def format_file_size(bytes)
315
+ Formatters::ValueFormatter.file_size(bytes)
316
+ end
317
+
318
+ def format_timestamp(time)
319
+ Formatters::ValueFormatter.timestamp(time)
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end