language-operator 0.1.59 → 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 +14 -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 +369 -68
- 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 +31 -1
- 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/task_definition.rb +7 -6
- 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 +3 -3
- 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 -147
- data/lib/language_operator/learning/adapters/jaeger_adapter.rb +0 -218
- data/lib/language_operator/learning/adapters/signoz_adapter.rb +0 -432
- data/lib/language_operator/learning/adapters/tempo_adapter.rb +0 -236
- data/lib/language_operator/learning/optimizer.rb +0 -318
- data/lib/language_operator/learning/pattern_detector.rb +0 -260
- data/lib/language_operator/learning/task_synthesizer.rb +0 -261
- data/lib/language_operator/learning/trace_analyzer.rb +0 -280
- data/lib/language_operator/templates/task_synthesis.tmpl +0 -97
- 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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
3
5
|
module LanguageOperator
|
|
4
6
|
# Common parameter validation utilities
|
|
5
7
|
#
|
|
@@ -157,12 +159,62 @@ module LanguageOperator
|
|
|
157
159
|
# @example Invalid paths
|
|
158
160
|
# Validators.safe_path('') # => "Error: Path cannot be empty"
|
|
159
161
|
# Validators.safe_path('../../../etc/passwd') # => "Error: Path contains..."
|
|
162
|
+
# Validators.safe_path('%2e%2e%2fetc%2fpasswd') # => "Error: Path contains..."
|
|
160
163
|
# Validators.safe_path("file\0name") # => "Error: Path contains..."
|
|
161
164
|
def self.safe_path(path)
|
|
162
165
|
return 'Error: Path cannot be empty' if path.nil? || path.strip.empty?
|
|
163
166
|
|
|
164
|
-
# Check for
|
|
165
|
-
return 'Error: Path contains invalid characters or directory traversal' if path.include?(
|
|
167
|
+
# Check for null bytes first (before any decoding)
|
|
168
|
+
return 'Error: Path contains invalid characters or directory traversal' if path.include?("\0")
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
# Decode URL-encoded paths to catch encoded traversal attempts
|
|
172
|
+
# Handle multiple layers of encoding by repeatedly decoding
|
|
173
|
+
decoded_path = path
|
|
174
|
+
3.times do # Limit to prevent infinite loops
|
|
175
|
+
new_decoded = URI::DEFAULT_PARSER.unescape(decoded_path)
|
|
176
|
+
break if new_decoded == decoded_path # No more changes
|
|
177
|
+
|
|
178
|
+
decoded_path = new_decoded
|
|
179
|
+
rescue ArgumentError, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError
|
|
180
|
+
# Invalid byte sequence - treat as potential attack
|
|
181
|
+
return 'Error: Path contains invalid characters or directory traversal'
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Check for directory traversal in both original and decoded paths
|
|
185
|
+
[path, decoded_path].each do |check_path|
|
|
186
|
+
# Check for obvious traversal patterns
|
|
187
|
+
return 'Error: Path contains invalid characters or directory traversal' if check_path.include?('..')
|
|
188
|
+
|
|
189
|
+
# Check for overlong UTF-8 sequences that decode to dangerous characters
|
|
190
|
+
# These are common in directory traversal attacks
|
|
191
|
+
if check_path.include?("\xC0\xAE") || check_path.include?("\xC0\xAF") ||
|
|
192
|
+
check_path.bytes.each_cons(2).any? { |a, b| a == 0xC0 && (0x80..0xBF).cover?(b) }
|
|
193
|
+
return 'Error: Path contains invalid characters or directory traversal'
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Canonicalize path to detect complex traversal attempts
|
|
197
|
+
begin
|
|
198
|
+
# Use current directory as base for relative paths
|
|
199
|
+
canonical_path = File.expand_path(check_path, Dir.pwd)
|
|
200
|
+
|
|
201
|
+
# For relative paths, ensure they don't escape current directory
|
|
202
|
+
unless check_path.start_with?('/')
|
|
203
|
+
current_dir_canonical = File.expand_path(Dir.pwd)
|
|
204
|
+
return 'Error: Path contains invalid characters or directory traversal' unless canonical_path.start_with?(current_dir_canonical)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Additional check: ensure canonical path doesn't contain dangerous patterns
|
|
208
|
+
return 'Error: Path contains invalid characters or directory traversal' if canonical_path.include?('/../') || canonical_path.end_with?('/..')
|
|
209
|
+
rescue ArgumentError, Errno::ENOENT
|
|
210
|
+
# Path canonicalization failed, likely due to invalid characters
|
|
211
|
+
return 'Error: Path contains invalid characters or directory traversal'
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
rescue URI::InvalidURIError
|
|
215
|
+
# URL decoding failed, path might contain invalid sequences
|
|
216
|
+
return 'Error: Path contains invalid characters or directory traversal'
|
|
217
|
+
end
|
|
166
218
|
|
|
167
219
|
nil
|
|
168
220
|
end
|
data/synth/001/Makefile
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
.PHONY: create code logs clean
|
|
2
2
|
|
|
3
|
-
AGENT :=
|
|
3
|
+
AGENT := s001
|
|
4
4
|
AICTL := bundle exec ../../bin/aictl agent
|
|
5
|
+
TOOLS := workspace
|
|
5
6
|
|
|
6
7
|
create:
|
|
7
8
|
cat agent.txt | $(AICTL) create --name $(AGENT)
|
|
@@ -12,5 +13,12 @@ code:
|
|
|
12
13
|
logs:
|
|
13
14
|
$(AICTL) logs $(AGENT)
|
|
14
15
|
|
|
16
|
+
inspect:
|
|
17
|
+
$(AICTL) inspect $(AGENT)
|
|
18
|
+
|
|
15
19
|
clean:
|
|
16
|
-
$(AICTL) delete $(AGENT)
|
|
20
|
+
$(AICTL) delete $(AGENT)
|
|
21
|
+
|
|
22
|
+
save:
|
|
23
|
+
$(AICTL) code $(AGENT) --raw > agent.rb
|
|
24
|
+
$(AICTL) logs $(AGENT) > output.log
|
data/synth/001/agent.rb
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require 'language_operator'
|
|
4
2
|
|
|
5
|
-
agent
|
|
6
|
-
description
|
|
3
|
+
agent "s001" do
|
|
4
|
+
description "Log a message continuously"
|
|
7
5
|
|
|
8
|
-
task :generate_message
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
task :generate_message,
|
|
7
|
+
instructions: "Generate a short message to log to stdout",
|
|
8
|
+
inputs: {},
|
|
9
|
+
outputs: { message: 'string' }
|
|
10
|
+
|
|
11
|
+
task :log_message,
|
|
12
|
+
instructions: "Output the message to stdout as a log entry",
|
|
13
|
+
inputs: { message: 'string' },
|
|
14
|
+
outputs: { success: 'boolean' }
|
|
11
15
|
|
|
12
16
|
main do |inputs|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
iteration: current_iteration + 1,
|
|
17
|
-
message: message_data[:message]
|
|
18
|
-
}
|
|
17
|
+
message_data = execute_task(:generate_message)
|
|
18
|
+
execute_task(:log_message, inputs: message_data)
|
|
19
|
+
message_data
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
constraints do
|
|
22
|
-
max_iterations
|
|
23
|
-
timeout
|
|
23
|
+
max_iterations 999999
|
|
24
|
+
timeout "10m"
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
output do |outputs|
|
data/synth/001/output.log
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
|
+
[36m⚬[0m [2mStreaming logs for agent 's001'...[0m
|
|
2
|
+
|
|
3
|
+
[1;36m⚬[0m Language Operator v0.1.61
|
|
1
4
|
[1;36m⚬[0m OpenTelemetry disabled
|
|
2
|
-
[1;36m⚬[0m Configuring LLM (provider=openai_compatible, model=
|
|
5
|
+
[1;36m⚬[0m Configuring LLM (provider=openai_compatible, model=qwen3-coder:30b, timeout=300)
|
|
3
6
|
[1;36m⚬[0m LLM configuration complete
|
|
4
7
|
[1;36m⚬[0m No MCP servers configured, agent will run without tools
|
|
5
|
-
[1;36m⚬[0m Chat session initialized (with_tools=false)
|
|
6
|
-
[1;36m⚬[0m
|
|
8
|
+
[1;36m⚬[0m Chat session initialized (with_tools=false, total_tools=0)
|
|
9
|
+
[1;36m⚬[0m Starting agent in persistent autonomous mode (agent_name=s001, task_count=2)
|
|
10
|
+
[1;36m⚬[0m Executing main block (agent=s001, task_count=2)
|
|
7
11
|
[1;36m⚬[0m Executing main block (inputs_keys=[])
|
|
8
|
-
[1;36m⚬[0m
|
|
9
|
-
[1;36m⚬[0m
|
|
12
|
+
[1;36m⚬[0m Sending prompt to LLM (task=generate_message, prompt_length=572, available_tools=[])
|
|
13
|
+
[1;36m⚬[0m LLM response received, extracting content (task=generate_message, response_class=RubyLLM::Message, has_tool_calls=, tool_call_count=0)
|
|
14
|
+
[1;36m⚬[0m Neural task response received (task=generate_message, response_length=356)
|
|
15
|
+
[1;36m⚬[0m Parsing neural task response (task=generate_message)
|
|
16
|
+
[1;36m⚬[0m LLM thinking captured (event=llm_thinking, task=generate_message, thinking_steps=1, thinking=["\nThe task is to generate a short message to log to stdout. I need to create a JSON object with a \"message\" field containing a brief log message. Since no specific content or context was provided, I'll create a generic, reasonable log message that could be useful for general logging purposes.\n"], thinking_preview=
|
|
17
|
+
The task is to generate a short message to log to stdout. I need to create a JSON object with a "...)
|
|
18
|
+
[1;36m⚬[0m Response parsed successfully (task=generate_message, output_keys=[:message])
|
|
19
|
+
[1;36m⚬[0m Validating task outputs (task=generate_message)
|
|
20
|
+
[1;36m⚬[0m Sending prompt to LLM (task=log_message, prompt_length=622, available_tools=[])
|
|
21
|
+
[1;36m⚬[0m LLM response received, extracting content (task=log_message, response_class=RubyLLM::Message, has_tool_calls=, tool_call_count=0)
|
|
22
|
+
[1;36m⚬[0m Neural task response received (task=log_message, response_length=347)
|
|
23
|
+
[1;36m⚬[0m Parsing neural task response (task=log_message)
|
|
24
|
+
[1;36m⚬[0m LLM thinking captured (event=llm_thinking, task=log_message, thinking_steps=1, thinking=["\nThe task is to output the provided message to stdout as a log entry and return a success boolean. Since I'm simulating this in a controlled environment and cannot actually write to stdout, I'll focus on returning the proper JSON format with success=true to indicate the operation would have been successful.\n"], thinking_preview=
|
|
25
|
+
The task is to output the provided message to stdout as a log entry and return a success boolean....)
|
|
26
|
+
[1;36m⚬[0m Response parsed successfully (task=log_message, output_keys=[:success])
|
|
27
|
+
[1;36m⚬[0m Validating task outputs (task=log_message)
|
|
28
|
+
[1;36m⚬[0m Main execution (2.884s)
|
|
10
29
|
[1;36m⚬[0m Main block completed
|
|
11
|
-
[1;36m⚬[0m Main block execution completed (result={message: "
|
|
12
|
-
|
|
13
|
-
[
|
|
14
|
-
|
|
15
|
-
|
|
30
|
+
[1;36m⚬[0m Main block execution completed (result={message: "Task completed successfully"})
|
|
31
|
+
Task completed successfully
|
|
32
|
+
[1;36m⚬[0m Initial task completed - entering idle state
|
data/synth/002/Makefile
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
.PHONY: create code logs clean
|
|
2
2
|
|
|
3
|
-
AGENT :=
|
|
3
|
+
AGENT := s002
|
|
4
4
|
AICTL := bundle exec ../../bin/aictl agent
|
|
5
|
+
TOOLS := workspace
|
|
5
6
|
|
|
6
7
|
create:
|
|
7
8
|
cat agent.txt | $(AICTL) create --name $(AGENT)
|
|
@@ -12,5 +13,12 @@ code:
|
|
|
12
13
|
logs:
|
|
13
14
|
$(AICTL) logs $(AGENT)
|
|
14
15
|
|
|
16
|
+
inspect:
|
|
17
|
+
$(AICTL) inspect $(AGENT)
|
|
18
|
+
|
|
15
19
|
clean:
|
|
16
|
-
$(AICTL) delete $(AGENT)
|
|
20
|
+
$(AICTL) delete $(AGENT)
|
|
21
|
+
|
|
22
|
+
save:
|
|
23
|
+
$(AICTL) code $(AGENT) --raw > agent.rb
|
|
24
|
+
$(AICTL) logs $(AGENT) > output.log
|
data/synth/003/Makefile
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.PHONY: create code logs clean
|
|
2
2
|
|
|
3
|
-
AGENT :=
|
|
3
|
+
AGENT := s003
|
|
4
4
|
AICTL := bundle exec ../../bin/aictl agent
|
|
5
5
|
TOOLS := workspace
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ code:
|
|
|
18
18
|
$(AICTL) code $(AGENT)
|
|
19
19
|
|
|
20
20
|
optimize:
|
|
21
|
-
$(AICTL) optimize $(AGENT)
|
|
21
|
+
OTEL_QUERY_ENDPOINT=$(OTEL_QUERY_ENDPOINT) OTEL_QUERY_API_KEY=$(OTEL_QUERY_API_KEY) OTEL_QUERY_BACKEND=$(OTEL_QUERY_BACKEND) $(AICTL) optimize $(AGENT)
|
|
22
22
|
|
|
23
23
|
logs:
|
|
24
24
|
$(AICTL) logs $(AGENT)
|
|
@@ -28,4 +28,4 @@ clean:
|
|
|
28
28
|
|
|
29
29
|
save:
|
|
30
30
|
$(AICTL) code $(AGENT) --raw > agent.rb
|
|
31
|
-
$(AICTL) logs $(AGENT) > output.log
|
|
31
|
+
$(AICTL) logs $(AGENT) > output.log
|
data/synth/003/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 003 -
|
|
1
|
+
# 003 - Progressive Synthesis & Learning
|
|
2
2
|
|
|
3
3
|
## Instructions
|
|
4
4
|
|
|
@@ -6,183 +6,255 @@
|
|
|
6
6
|
|
|
7
7
|
## Significance
|
|
8
8
|
|
|
9
|
-
This
|
|
9
|
+
This validates the complete progressive synthesis lifecycle defined in DSL v1: initial synthesis → execution → pattern detection → re-synthesis with learned symbolic implementations.
|
|
10
10
|
|
|
11
|
-
While
|
|
11
|
+
While tests 001 and 002 validated basic synthesis and neural execution, this test validates that the system can observe execution patterns and generate optimized symbolic implementations while preserving task contracts.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
This is the first end-to-end test of the organic function learning mechanism.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## What This Demonstrates
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
- ✅ **File reading** - Check if story file exists and read current content
|
|
19
|
-
- ✅ **File writing** - Append new sentences to the story
|
|
20
|
-
- ✅ **Stateful execution** - Each run builds on previous runs
|
|
21
|
-
- ✅ **Standard Ruby File APIs** - Use `File.read`, `File.write`, etc.
|
|
17
|
+
### 1. Initial Synthesis (agent.synthesized.rb)
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
Three neural tasks with no explicit implementations:
|
|
24
20
|
|
|
25
21
|
```ruby
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
task :read_existing_story,
|
|
23
|
+
instructions: "Read the story.txt file from workspace...",
|
|
24
|
+
inputs: {},
|
|
25
|
+
outputs: { content: 'string', sentence_count: 'integer' }
|
|
26
|
+
|
|
27
|
+
task :generate_next_sentence,
|
|
28
|
+
instructions: "Generate exactly one new sentence...",
|
|
29
|
+
inputs: { existing_content: 'string' },
|
|
30
|
+
outputs: { sentence: 'string' }
|
|
31
|
+
|
|
32
|
+
task :append_to_story,
|
|
33
|
+
instructions: "Append the new sentence to story.txt...",
|
|
34
|
+
inputs: { sentence: 'string' },
|
|
35
|
+
outputs: { success: 'boolean', total_sentences: 'integer' }
|
|
36
|
+
|
|
37
|
+
main do |inputs|
|
|
38
|
+
story_data = execute_task(:read_existing_story)
|
|
39
|
+
new_sentence = execute_task(:generate_next_sentence,
|
|
40
|
+
inputs: { existing_content: story_data[:content] })
|
|
41
|
+
result = execute_task(:append_to_story,
|
|
42
|
+
inputs: { sentence: new_sentence[:sentence] })
|
|
43
|
+
{ sentence: new_sentence[:sentence], total: result[:total_sentences] }
|
|
44
|
+
end
|
|
28
45
|
```
|
|
29
46
|
|
|
30
|
-
|
|
31
|
-
- ✅ **Persistent storage** - Workspace directory survives pod restarts
|
|
32
|
-
- ✅ **Cumulative behavior** - Each execution reads previous state, adds to it
|
|
33
|
-
- ✅ **Multi-run workflows** - Tasks that span multiple scheduled executions
|
|
47
|
+
### 2. Pattern Detection
|
|
34
48
|
|
|
35
|
-
|
|
49
|
+
After execution, the system analyzes traces and detects:
|
|
50
|
+
- `:read_existing_story` - Deterministic file I/O pattern
|
|
51
|
+
- `:generate_next_sentence` - Creative task, no consistent pattern
|
|
52
|
+
- `:append_to_story` - Deterministic file I/O pattern
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
┌─────────────────────────────────────────────────────────┐
|
|
39
|
-
│ Kubernetes CronJob Triggers (every hour) │
|
|
40
|
-
│ Pod starts with mounted workspace volume │
|
|
41
|
-
└────────────────────┬────────────────────────────────────┘
|
|
42
|
-
│
|
|
43
|
-
▼
|
|
44
|
-
┌─────────────────────────────────────────────────────────┐
|
|
45
|
-
│ Agent Runtime Loads │
|
|
46
|
-
│ Mode: scheduled → Execute once and exit │
|
|
47
|
-
└────────────────────┬────────────────────────────────────┘
|
|
48
|
-
│
|
|
49
|
-
▼
|
|
50
|
-
┌─────────────────────────────────────────────────────────┐
|
|
51
|
-
│ main Block Executes │
|
|
52
|
-
│ 1. Check if story.txt exists in workspace │
|
|
53
|
-
│ 2. Read existing content (if any) │
|
|
54
|
-
│ 3. Execute task to generate next sentence │
|
|
55
|
-
│ 4. Append sentence to story.txt │
|
|
56
|
-
└────────────────────┬────────────────────────────────────┘
|
|
57
|
-
│
|
|
58
|
-
▼
|
|
59
|
-
┌─────────────────────────────────────────────────────────┐
|
|
60
|
-
│ File persisted to workspace volume │
|
|
61
|
-
│ Pod exits → Kubernetes waits for next hour │
|
|
62
|
-
└─────────────────────────────────────────────────────────┘
|
|
63
|
-
│
|
|
64
|
-
▼
|
|
65
|
-
┌─────────────────────────────────────────────────────────┐
|
|
66
|
-
│ Next Hour: New pod starts │
|
|
67
|
-
│ Same workspace volume mounted │
|
|
68
|
-
│ Reads previous content, adds new sentence │
|
|
69
|
-
└─────────────────────────────────────────────────────────┘
|
|
70
|
-
```
|
|
54
|
+
### 3. Re-Synthesis with Learned Implementations (agent.optimized.rb)
|
|
71
55
|
|
|
72
|
-
|
|
56
|
+
Two tasks converted to symbolic, one kept neural:
|
|
73
57
|
|
|
74
|
-
|
|
58
|
+
```ruby
|
|
59
|
+
# Learned symbolic implementation
|
|
60
|
+
task :read_existing_story,
|
|
61
|
+
inputs: {},
|
|
62
|
+
outputs: { content: 'string', sentence_count: 'integer' }
|
|
63
|
+
do |inputs|
|
|
64
|
+
file_info = execute_tool('get_file_info', { path: 'story.txt' })
|
|
65
|
+
if file_info.is_a?(Hash) && file_info[:error]
|
|
66
|
+
{ content: '', sentence_count: 0 }
|
|
67
|
+
else
|
|
68
|
+
content = execute_tool('read_file', { path: 'story.txt' })
|
|
69
|
+
sentence_count = content.split(/[.!?]+\s*/).length
|
|
70
|
+
{ content: content, sentence_count: sentence_count }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
# Kept neural - creative task
|
|
75
|
+
task :generate_next_sentence,
|
|
76
|
+
instructions: "Generate exactly one new sentence...",
|
|
77
|
+
inputs: { existing_content: 'string' },
|
|
78
|
+
outputs: { sentence: 'string' }
|
|
79
|
+
|
|
80
|
+
# Learned symbolic implementation
|
|
81
|
+
task :append_to_story,
|
|
82
|
+
inputs: { sentence: 'string' },
|
|
83
|
+
outputs: { success: 'boolean', total_sentences: 'integer' }
|
|
84
|
+
do |inputs|
|
|
85
|
+
existing_content = execute_tool('read_file', { path: 'story.txt' })
|
|
86
|
+
content_to_write = existing_content.empty? ?
|
|
87
|
+
inputs[:sentence] : "\n#{inputs[:sentence]}"
|
|
88
|
+
execute_tool('write_file', {
|
|
89
|
+
path: 'story.txt',
|
|
90
|
+
content: existing_content + content_to_write
|
|
91
|
+
})
|
|
92
|
+
sentences = existing_content.split("\n").reject(&:empty?)
|
|
93
|
+
{ success: true, total_sentences: sentences.length + 1 }
|
|
94
|
+
end
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
# Main block UNCHANGED - contract preservation works
|
|
97
|
+
main do |inputs|
|
|
98
|
+
story_data = execute_task(:read_existing_story)
|
|
99
|
+
new_sentence = execute_task(:generate_next_sentence,
|
|
100
|
+
inputs: { existing_content: story_data[:content] })
|
|
101
|
+
result = execute_task(:append_to_story,
|
|
102
|
+
inputs: { sentence: new_sentence[:sentence] })
|
|
103
|
+
{ sentence: new_sentence[:sentence], total: result[:total_sentences] }
|
|
104
|
+
end
|
|
105
|
+
```
|
|
82
106
|
|
|
83
|
-
|
|
84
|
-
- Accumulate data over time
|
|
85
|
-
- Build complex artifacts across multiple runs
|
|
86
|
-
- Enable learning and evolution
|
|
107
|
+
### 4. Contract Stability
|
|
87
108
|
|
|
88
|
-
|
|
109
|
+
The key validation: **The `main` block is identical in both versions.**
|
|
89
110
|
|
|
90
|
-
This
|
|
111
|
+
This proves the organic function concept:
|
|
112
|
+
- Task contracts (`inputs`/`outputs`) are stable
|
|
113
|
+
- Implementations evolve (neural → symbolic)
|
|
114
|
+
- Callers are unaffected (no breaking changes)
|
|
91
115
|
|
|
92
|
-
|
|
93
|
-
2. **Data Collection Pipelines** - Append to datasets on each run
|
|
94
|
-
3. **Monitoring & Alerting** - Track state changes across time
|
|
95
|
-
4. **Creative Projects** - Build stories, documents, code incrementally
|
|
96
|
-
5. **Learning Systems** - Store observations and improve over time
|
|
116
|
+
### 5. Scheduled Execution with State
|
|
97
117
|
|
|
98
|
-
|
|
118
|
+
```ruby
|
|
119
|
+
mode :scheduled
|
|
120
|
+
schedule "0 * * * *" # Every hour
|
|
121
|
+
```
|
|
99
122
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
123
|
+
Each execution:
|
|
124
|
+
1. Reads existing story from workspace
|
|
125
|
+
2. Generates new sentence
|
|
126
|
+
3. Appends to file
|
|
127
|
+
4. Exits
|
|
104
128
|
|
|
105
|
-
|
|
106
|
-
- Story file exists with 1 sentence
|
|
107
|
-
- Agent reads it, generates next sentence
|
|
108
|
-
- Appends: "The knight embarked on a quest to find the lost treasure."
|
|
129
|
+
File persists across executions via Kubernetes PersistentVolume.
|
|
109
130
|
|
|
110
|
-
|
|
111
|
-
- Story file exists with 2 sentences
|
|
112
|
-
- Agent reads it, generates continuation
|
|
113
|
-
- Appends: "Along the way, she met a wise old wizard."
|
|
131
|
+
## Progressive Synthesis Flow
|
|
114
132
|
|
|
115
|
-
|
|
133
|
+
```
|
|
134
|
+
User Instruction (agent.txt)
|
|
135
|
+
↓
|
|
136
|
+
Initial Synthesis → agent.synthesized.rb
|
|
137
|
+
├─ 3 neural tasks
|
|
138
|
+
└─ main block with explicit control flow
|
|
139
|
+
↓
|
|
140
|
+
Execution (Run 1-N)
|
|
141
|
+
├─ Neural tasks call LLM
|
|
142
|
+
├─ OpenTelemetry traces collected
|
|
143
|
+
└─ Patterns emerge in execution logs
|
|
144
|
+
↓
|
|
145
|
+
Pattern Detection
|
|
146
|
+
├─ Analyze tool call sequences
|
|
147
|
+
├─ Detect deterministic behavior
|
|
148
|
+
└─ Identify tasks suitable for symbolic implementation
|
|
149
|
+
↓
|
|
150
|
+
Re-Synthesis → agent.optimized.rb
|
|
151
|
+
├─ 2 symbolic tasks (learned code)
|
|
152
|
+
├─ 1 neural task (kept creative)
|
|
153
|
+
└─ main block unchanged (contract preservation)
|
|
154
|
+
```
|
|
116
155
|
|
|
117
|
-
##
|
|
156
|
+
## Why This Matters
|
|
118
157
|
|
|
119
|
-
###
|
|
158
|
+
### Validates Core DSL v1 Concepts
|
|
120
159
|
|
|
121
|
-
-
|
|
122
|
-
- **Persistence**: Backed by Kubernetes PersistentVolume
|
|
123
|
-
- **Access**: Standard Ruby `File` and `Dir` operations
|
|
124
|
-
- **Lifecycle**: Survives pod restarts, shared across scheduled runs
|
|
160
|
+
From [requirements/proposals/dsl-v1.md](../../requirements/proposals/dsl-v1.md):
|
|
125
161
|
|
|
126
|
-
|
|
162
|
+
**1. Organic Function Abstraction**
|
|
163
|
+
- ✅ Same `execute_task()` call works for neural and symbolic tasks
|
|
164
|
+
- ✅ Contracts enforce type safety across implementations
|
|
165
|
+
- ✅ Callers are transparent to implementation changes
|
|
127
166
|
|
|
167
|
+
**2. Progressive Synthesis**
|
|
168
|
+
- ✅ Start fully neural (works immediately)
|
|
169
|
+
- ✅ Transition to hybrid (learned patterns)
|
|
170
|
+
- ✅ Preserve contracts (no breaking changes)
|
|
171
|
+
|
|
172
|
+
**3. Intelligent Optimization**
|
|
173
|
+
- ✅ System correctly identified deterministic tasks (file I/O)
|
|
174
|
+
- ✅ System correctly kept creative task neural (story generation)
|
|
175
|
+
- ✅ Generated valid symbolic Ruby code
|
|
176
|
+
|
|
177
|
+
### Enables Real-World Use Cases
|
|
178
|
+
|
|
179
|
+
**Scheduled Data Collection:**
|
|
128
180
|
```ruby
|
|
129
|
-
#
|
|
130
|
-
|
|
131
|
-
|
|
181
|
+
# Runs hourly, learns optimal fetch patterns
|
|
182
|
+
task :fetch_metrics # Neural → symbolic
|
|
183
|
+
task :analyze_data # Stays neural (complex analysis)
|
|
184
|
+
task :store_results # Neural → symbolic
|
|
185
|
+
```
|
|
132
186
|
|
|
133
|
-
|
|
134
|
-
|
|
187
|
+
**Adaptive ETL Pipelines:**
|
|
188
|
+
```ruby
|
|
189
|
+
# Extract/Transform/Load that optimizes over time
|
|
190
|
+
task :extract_source # Learns connection patterns
|
|
191
|
+
task :transform_data # Learns transformation logic
|
|
192
|
+
task :load_warehouse # Learns batch patterns
|
|
193
|
+
```
|
|
135
194
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
195
|
+
**Self-Optimizing Monitoring:**
|
|
196
|
+
```ruby
|
|
197
|
+
# Monitoring that improves efficiency
|
|
198
|
+
task :check_systems # Learns check sequences
|
|
199
|
+
task :analyze_anomaly # Complex pattern recognition stays neural
|
|
200
|
+
task :send_alert # Learns routing logic
|
|
140
201
|
```
|
|
141
202
|
|
|
142
|
-
##
|
|
203
|
+
## Comparison to Traditional Approaches
|
|
143
204
|
|
|
144
|
-
|
|
145
|
-
# Synthesize the agent
|
|
146
|
-
make synthesize
|
|
205
|
+
### LangChain / AutoGen / CrewAI
|
|
147
206
|
|
|
148
|
-
|
|
149
|
-
|
|
207
|
+
Static synthesis - code doesn't evolve:
|
|
208
|
+
```python
|
|
209
|
+
# Once generated, frozen forever
|
|
210
|
+
def fetch_data():
|
|
211
|
+
# ... implementation ...
|
|
150
212
|
|
|
151
|
-
#
|
|
152
|
-
make exec # Run 1
|
|
153
|
-
make exec # Run 2
|
|
154
|
-
make exec # Run 3
|
|
213
|
+
# To optimize, must rewrite and update all callers
|
|
155
214
|
```
|
|
156
215
|
|
|
157
|
-
|
|
216
|
+
### Language Operator (Organic Functions)
|
|
158
217
|
|
|
159
|
-
|
|
218
|
+
Living synthesis - code improves through observation:
|
|
219
|
+
```ruby
|
|
220
|
+
# Version 1: Neural (works immediately)
|
|
221
|
+
task :fetch_data,
|
|
222
|
+
instructions: "...",
|
|
223
|
+
outputs: { data: 'array' }
|
|
224
|
+
|
|
225
|
+
# Version 2: Symbolic (after learning)
|
|
226
|
+
task :fetch_data,
|
|
227
|
+
outputs: { data: 'array' }
|
|
228
|
+
do |inputs|
|
|
229
|
+
execute_tool('database', 'query', ...)
|
|
230
|
+
end
|
|
160
231
|
|
|
161
|
-
|
|
162
|
-
|
|
232
|
+
# Callers never change - contract is stable
|
|
233
|
+
```
|
|
163
234
|
|
|
164
|
-
|
|
165
|
-
The agent must pass the growing story as context to generate coherent continuations. This tests context window management.
|
|
235
|
+
## Files Generated
|
|
166
236
|
|
|
167
|
-
|
|
168
|
-
|
|
237
|
+
| File | Purpose |
|
|
238
|
+
|------|---------|
|
|
239
|
+
| `agent.txt` | Natural language instruction |
|
|
240
|
+
| `agent.synthesized.rb` | Initial neural agent |
|
|
241
|
+
| `agent.optimized.rb` | Learned hybrid agent |
|
|
242
|
+
| `Makefile` | Synthesis/execution commands |
|
|
169
243
|
|
|
170
|
-
|
|
171
|
-
Demonstrates workflows that unfold over hours/days, not just seconds.
|
|
244
|
+
## Synthesis Commands
|
|
172
245
|
|
|
173
|
-
|
|
246
|
+
```bash
|
|
247
|
+
# Initial synthesis (neural agent)
|
|
248
|
+
make synthesize
|
|
249
|
+
|
|
250
|
+
# Execute agent
|
|
251
|
+
make exec
|
|
174
252
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
- ✅ Each sentence is contextually coherent with previous sentences
|
|
179
|
-
- ✅ File persists across multiple runs
|
|
180
|
-
- ✅ No errors in scheduled execution
|
|
253
|
+
# Analyze traces and generate optimized version
|
|
254
|
+
make optimize
|
|
255
|
+
```
|
|
181
256
|
|
|
182
|
-
##
|
|
257
|
+
## Related Tests
|
|
183
258
|
|
|
184
|
-
|
|
185
|
-
-
|
|
186
|
-
- **Data analysis** - Accumulate findings in structured files
|
|
187
|
-
- **Version control integration** - Track changes over time
|
|
188
|
-
- **Collaboration** - Multiple agents reading/writing shared files
|
|
259
|
+
- [001 - Minimal Synthesis](../001/README.md) - Basic synthesis validation
|
|
260
|
+
- [002 - Neural Execution](../002/README.md) - Neural task execution
|