language-operator 0.1.65 → 0.1.67

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.
@@ -4,42 +4,57 @@ module LanguageOperator
4
4
  module CLI
5
5
  module Commands
6
6
  module Agent
7
- # Code viewing and editing for agents
7
+ # Code viewing and editing for agents using LanguageAgentVersion CRD
8
8
  module CodeOperations
9
9
  def self.included(base)
10
10
  base.class_eval do
11
- desc 'code NAME', 'Display synthesized agent code'
11
+ desc 'code NAME', 'Display agent code from LanguageAgentVersion'
12
12
  option :cluster, type: :string, desc: 'Override current cluster context'
13
13
  option :raw, type: :boolean, default: false, desc: 'Output raw code without formatting'
14
+ option :version, type: :string, desc: 'Display specific version (e.g., --version=2)'
14
15
  def code(name)
15
16
  handle_command_error('get code') do
16
17
  require_relative '../../formatters/code_formatter'
17
18
 
18
19
  ctx = CLI::Helpers::ClusterContext.from_options(options)
19
20
 
20
- # Get the code ConfigMap for this agent
21
- configmap_name = "#{name}-code"
22
- begin
23
- configmap = ctx.client.get_resource('ConfigMap', configmap_name, ctx.namespace)
24
- rescue K8s::Error::NotFound
25
- Formatters::ProgressFormatter.error("Synthesized code not found for agent '#{name}'")
21
+ # Get the LanguageAgentVersion resource
22
+ version_resource = get_agent_version_resource(ctx, name, options[:version])
23
+
24
+ unless version_resource
25
+ Formatters::ProgressFormatter.error("No code versions found for agent '#{name}'")
26
26
  puts
27
- puts 'Possible reasons:'
28
- puts ' - Agent synthesis not yet complete'
27
+ puts 'This may indicate:'
28
+ puts ' - Agent has not been synthesized yet'
29
29
  puts ' - Agent synthesis failed'
30
30
  puts
31
- puts 'Check synthesis status with:'
31
+ puts 'Check agent status with:'
32
32
  puts " aictl agent inspect #{name}"
33
33
  exit 1
34
34
  end
35
35
 
36
- # Get the agent.rb code from the ConfigMap
37
- code_content = configmap.dig('data', 'agent.rb')
36
+ # Get code from LanguageAgentVersion spec
37
+ code_content = version_resource.dig('spec', 'code')
38
38
  unless code_content
39
- Formatters::ProgressFormatter.error('Code content not found in ConfigMap')
39
+ Formatters::ProgressFormatter.error('Code content not found in LanguageAgentVersion')
40
40
  exit 1
41
41
  end
42
42
 
43
+ # Determine version info for title
44
+ version_num = version_resource.dig('spec', 'version')
45
+ source_type = version_resource.dig('spec', 'sourceType') || 'manual'
46
+
47
+ title = if options[:version]
48
+ "Code for Agent: #{name} (Version #{version_num})"
49
+ else
50
+ active_version = get_active_version(ctx, name)
51
+ if version_num.to_s == active_version
52
+ "Current Code for Agent: #{name} (Version #{version_num} - #{source_type})"
53
+ else
54
+ "Code for Agent: #{name} (Version #{version_num} - #{source_type})"
55
+ end
56
+ end
57
+
43
58
  # Raw output mode - just print the code
44
59
  if options[:raw]
45
60
  puts code_content
@@ -49,11 +64,137 @@ module LanguageOperator
49
64
  # Display with syntax highlighting
50
65
  Formatters::CodeFormatter.display_ruby_code(
51
66
  code_content,
52
- title: "Synthesized Code for Agent: #{name}"
67
+ title: title
53
68
  )
54
69
  end
55
70
  end
56
71
 
72
+ desc 'versions NAME', 'List available LanguageAgentVersion resources'
73
+ option :cluster, type: :string, desc: 'Override current cluster context'
74
+ def versions(name)
75
+ handle_command_error('list versions') do
76
+ ctx = CLI::Helpers::ClusterContext.from_options(options)
77
+
78
+ # Get agent to verify it exists
79
+ get_resource_or_exit(Constants::RESOURCE_AGENT, name)
80
+
81
+ # Find all LanguageAgentVersion resources for this agent
82
+ versions = ctx.client.list_resources(Constants::RESOURCE_AGENT_VERSION, namespace: ctx.namespace)
83
+ .select { |v| v.dig('spec', 'agentRef', 'name') == name }
84
+ .sort_by { |v| v.dig('spec', 'version').to_i }
85
+
86
+ if versions.empty?
87
+ puts
88
+ puts "No LanguageAgentVersion resources found for agent: #{pastel.bold(name)}"
89
+ puts
90
+ puts pastel.yellow('No versions have been created yet.')
91
+ puts
92
+ puts 'Versions are created automatically during agent synthesis.'
93
+ puts "Check agent status: #{pastel.dim("aictl agent inspect #{name}")}"
94
+ return
95
+ end
96
+
97
+ # Get current active version
98
+ active_version = get_active_version(ctx, name)
99
+
100
+ # Build table data
101
+ table_data = versions.map do |version_resource|
102
+ version_num = version_resource.dig('spec', 'version').to_s
103
+ status = version_num == active_version ? 'current' : 'available'
104
+ source_type = version_resource.dig('spec', 'sourceType') || 'manual'
105
+ created = version_resource.dig('metadata', 'creationTimestamp')
106
+ created = created ? Time.parse(created).strftime('%Y-%m-%d %H:%M') : 'Unknown'
107
+
108
+ {
109
+ version: "v#{version_num}",
110
+ status: status,
111
+ type: source_type.capitalize,
112
+ created: created
113
+ }
114
+ end
115
+
116
+ # Display table
117
+ headers = %w[VERSION STATUS TYPE CREATED]
118
+ rows = table_data.map do |row|
119
+ status_indicator = row[:status] == 'current' ? pastel.green('●') : pastel.dim('○')
120
+ status_text = row[:status] == 'current' ? pastel.green('current') : 'available'
121
+
122
+ [
123
+ row[:version],
124
+ "#{status_indicator} #{status_text}",
125
+ row[:type],
126
+ row[:created]
127
+ ]
128
+ end
129
+
130
+ puts
131
+ puts "Code versions for agent: #{pastel.bold(name)}"
132
+ puts
133
+ puts table(headers, rows)
134
+ puts
135
+ puts 'Usage:'
136
+ puts " #{pastel.dim("aictl agent code #{name}")}"
137
+ puts " #{pastel.dim("aictl agent code #{name} --version=X")}"
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def get_active_version(ctx, agent_name)
144
+ begin
145
+ agent = ctx.client.get_resource(Constants::RESOURCE_AGENT, agent_name, ctx.namespace)
146
+ version_ref = agent.dig('spec', 'agentVersionRef', 'name')
147
+ return nil unless version_ref
148
+
149
+ # Extract version number from "agent-name-vX" format
150
+ version_ref.split('-v').last
151
+ rescue K8s::Error::NotFound
152
+ nil
153
+ end
154
+ end
155
+
156
+ def get_agent_version_resource(ctx, agent_name, requested_version = nil)
157
+ # Query LanguageAgentVersion resources for this agent
158
+ versions = ctx.client.list_resources(Constants::RESOURCE_AGENT_VERSION, namespace: ctx.namespace)
159
+ .select { |v| v.dig('spec', 'agentRef', 'name') == agent_name }
160
+
161
+ if versions.empty?
162
+ return nil
163
+ end
164
+
165
+ if requested_version
166
+ # Find specific version
167
+ target_version = versions.find { |v| v.dig('spec', 'version').to_s == requested_version }
168
+
169
+ unless target_version
170
+ Formatters::ProgressFormatter.error("Version #{requested_version} not found for agent '#{agent_name}'")
171
+ puts
172
+ puts 'Available versions:'
173
+ versions.each do |v|
174
+ version_num = v.dig('spec', 'version')
175
+ puts " v#{version_num}"
176
+ end
177
+ puts
178
+ puts "Use 'aictl agent versions #{agent_name}' to see all available versions"
179
+ exit 1
180
+ end
181
+
182
+ return target_version
183
+ else
184
+ # Get currently active version
185
+ active_version = get_active_version(ctx, agent_name)
186
+
187
+ if active_version
188
+ # Find the currently active version resource
189
+ active_version_resource = versions.find { |v| v.dig('spec', 'version').to_s == active_version }
190
+ return active_version_resource if active_version_resource
191
+ end
192
+
193
+ # Fall back to latest version if no active version or active version not found
194
+ return versions.max_by { |v| v.dig('spec', 'version').to_i }
195
+ end
196
+ end
197
+
57
198
  desc 'edit NAME', 'Edit agent instructions'
58
199
  option :cluster, type: :string, desc: 'Override current cluster context'
59
200
  def edit(name)
@@ -99,4 +240,4 @@ module LanguageOperator
99
240
  end
100
241
  end
101
242
  end
102
- end
243
+ end
@@ -51,7 +51,7 @@ module LanguageOperator
51
51
  def agent_not_found_suggestions(context)
52
52
  suggestions = []
53
53
  suggestions << "List all agents: #{pastel.dim('aictl agent list')}"
54
- suggestions << "Create a new agent: #{pastel.dim('aictl agent create \"description\"')}"
54
+ suggestions << "Create a new agent: #{pastel.dim('aictl agent create "description"')}"
55
55
  suggestions << "Use the wizard: #{pastel.dim('aictl agent create --wizard')}" if context[:suggest_wizard]
56
56
  suggestions
57
57
  end
@@ -60,6 +60,7 @@ module LanguageOperator
60
60
  # Kubernetes Custom Resource Definitions (CRD) kinds
61
61
  # These replace magic strings scattered across CLI commands
62
62
  RESOURCE_AGENT = 'LanguageAgent'
63
+ RESOURCE_AGENT_VERSION = 'LanguageAgentVersion'
63
64
  RESOURCE_MODEL = 'LanguageModel'
64
65
  RESOURCE_TOOL = 'LanguageTool'
65
66
  RESOURCE_PERSONA = 'LanguagePersona'
@@ -385,7 +385,7 @@ module LanguageOperator
385
385
 
386
386
  def default_api_version(kind)
387
387
  case kind.downcase
388
- when 'languagecluster', 'languageagent', 'languagetool', 'languagemodel', 'languageclient', 'languagepersona'
388
+ when 'languagecluster', 'languageagent', 'languageagentversion', 'languagetool', 'languagemodel', 'languageclient', 'languagepersona'
389
389
  'langop.io/v1alpha1'
390
390
  when 'namespace', 'configmap', 'secret', 'service'
391
391
  'v1'
@@ -2,7 +2,7 @@
2
2
  :openapi: 3.0.3
3
3
  :info:
4
4
  :title: Language Operator Agent API
5
- :version: 0.1.65
5
+ :version: 0.1.67
6
6
  :description: HTTP API endpoints exposed by Language Operator reactive agents
7
7
  :contact:
8
8
  :name: Language Operator
@@ -3,7 +3,7 @@
3
3
  "$id": "https://github.com/language-operator/language-operator-gem/schema/agent-dsl.json",
4
4
  "title": "Language Operator Agent DSL",
5
5
  "description": "Schema for defining autonomous AI agents using the Language Operator DSL",
6
- "version": "0.1.65",
6
+ "version": "0.1.67",
7
7
  "type": "object",
8
8
  "properties": {
9
9
  "name": {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LanguageOperator
4
- VERSION = '0.1.65'
4
+ VERSION = '0.1.67'
5
5
  end
data/synth/003/Makefile CHANGED
@@ -10,13 +10,17 @@ create:
10
10
  run:
11
11
  @JOB_NAME=$(AGENT)-$(shell date +%s); \
12
12
  kubectl create job --from=cronjob/$(AGENT) $$JOB_NAME && \
13
- trap "kubectl delete job $$JOB_NAME" EXIT; \
14
13
  kubectl wait --for=condition=ready pod -l job-name=$$JOB_NAME --timeout=60s && \
15
- kubectl logs -f job/$$JOB_NAME
14
+ kubectl logs -f job/$$JOB_NAME && \
15
+ sleep 15 && \
16
+ kubectl delete job $$JOB_NAME
16
17
 
17
18
  code:
18
19
  $(AICTL) code $(AGENT)
19
20
 
21
+ versions:
22
+ $(AICTL) versions $(AGENT)
23
+
20
24
  inspect:
21
25
  $(AICTL) inspect $(AGENT)
22
26
 
data/synth/003/agent.txt CHANGED
@@ -1 +1 @@
1
- Write a story one sentence at a time, with one new sentence every hour.
1
+ Write a story one sentence at a time, with one new sentence every 10 minutes.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: language-operator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.65
4
+ version: 0.1.67
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Ryan
@@ -502,6 +502,7 @@ files:
502
502
  - docs/constraints.md
503
503
  - docs/how-agents-work.md
504
504
  - docs/installation.md
505
+ - docs/observability.md
505
506
  - docs/quickstart.md
506
507
  - docs/schema-versioning.md
507
508
  - docs/understanding-generated-code.md
@@ -533,7 +534,6 @@ files:
533
534
  - lib/language_operator/cli/commands/agent/helpers/cluster_llm_client.rb
534
535
  - lib/language_operator/cli/commands/agent/helpers/code_parser.rb
535
536
  - lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb
536
- - lib/language_operator/cli/commands/agent/learning.rb
537
537
  - lib/language_operator/cli/commands/agent/lifecycle.rb
538
538
  - lib/language_operator/cli/commands/agent/logs.rb
539
539
  - lib/language_operator/cli/commands/agent/workspace.rb
@@ -649,8 +649,6 @@ files:
649
649
  - synth/002/output.log
650
650
  - synth/003/Makefile
651
651
  - synth/003/README.md
652
- - synth/003/agent.optimized.rb
653
- - synth/003/agent.synthesized.rb
654
652
  - synth/003/agent.txt
655
653
  - synth/004/Makefile
656
654
  - synth/004/README.md