console_agent 0.3.0 → 0.4.0
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/lib/console_agent/console_methods.rb +10 -3
- data/lib/console_agent/repl.rb +84 -17
- data/lib/console_agent/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28a64e6be4f7d39d73e55b67d6e95404324355420293651acf44fd439394eb56
|
|
4
|
+
data.tar.gz: 8350d0a49d94733c54530debc4d8e184bd0c2774b04f82c68be24b71d32a7457
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 874af072ffd5222c5ef7c782e1b5db18a148ecaf013a6ba8f2e33d863123895235c1d93106551ad5197eb35c17ad2bf65b076a79e74ef091ac76690c59fe4ba8
|
|
7
|
+
data.tar.gz: c2ba1ece779bf4e12b30c9bc0ed533dc9acabdaa22d6749e8f3f75d0d18fea1cb3895c669c313ec005f1e79a3a72ab7e2d16a2fefdcbc7df8509eac35afaee92
|
|
@@ -84,7 +84,7 @@ module ConsoleAgent
|
|
|
84
84
|
nil
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
def ai_resume(identifier)
|
|
87
|
+
def ai_resume(identifier = nil)
|
|
88
88
|
__ensure_console_agent_user
|
|
89
89
|
|
|
90
90
|
require 'console_agent/context_builder'
|
|
@@ -93,9 +93,16 @@ module ConsoleAgent
|
|
|
93
93
|
require 'console_agent/repl'
|
|
94
94
|
require 'console_agent/session_logger'
|
|
95
95
|
|
|
96
|
-
session =
|
|
96
|
+
session = if identifier
|
|
97
|
+
__find_session(identifier)
|
|
98
|
+
else
|
|
99
|
+
session_class = Object.const_get('ConsoleAgent::Session')
|
|
100
|
+
session_class.where(mode: 'interactive', user_name: ConsoleAgent.current_user).recent.first
|
|
101
|
+
end
|
|
102
|
+
|
|
97
103
|
unless session
|
|
98
|
-
|
|
104
|
+
msg = identifier ? "Session not found: #{identifier}" : "No interactive sessions found."
|
|
105
|
+
$stderr.puts "\e[31m#{msg}\e[0m"
|
|
99
106
|
return nil
|
|
100
107
|
end
|
|
101
108
|
|
data/lib/console_agent/repl.rb
CHANGED
|
@@ -106,17 +106,17 @@ module ConsoleAgent
|
|
|
106
106
|
@total_input_tokens = session.input_tokens || 0
|
|
107
107
|
@total_output_tokens = session.output_tokens || 0
|
|
108
108
|
|
|
109
|
-
#
|
|
109
|
+
# Seed the capture buffer with previous output so it's preserved on save
|
|
110
|
+
@interactive_console_capture.write(session.console_output.to_s)
|
|
111
|
+
|
|
112
|
+
# Replay to the user via the real stdout (bypass TeeIO to avoid double-capture)
|
|
110
113
|
if session.console_output && !session.console_output.strip.empty?
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
@interactive_old_stdout.puts "\e[2m--- Replaying previous session output ---\e[0m"
|
|
115
|
+
@interactive_old_stdout.puts session.console_output
|
|
116
|
+
@interactive_old_stdout.puts "\e[2m--- End of previous output ---\e[0m"
|
|
117
|
+
@interactive_old_stdout.puts
|
|
115
118
|
end
|
|
116
119
|
|
|
117
|
-
# Copy replayed output into the capture buffer so it's preserved on save
|
|
118
|
-
@interactive_console_capture.write(session.console_output.to_s)
|
|
119
|
-
|
|
120
120
|
interactive_loop
|
|
121
121
|
end
|
|
122
122
|
|
|
@@ -144,8 +144,9 @@ module ConsoleAgent
|
|
|
144
144
|
def interactive_loop
|
|
145
145
|
auto = ConsoleAgent.configuration.auto_execute
|
|
146
146
|
name_display = @interactive_session_name ? " (#{@interactive_session_name})" : ""
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
# Write banner to real stdout (bypass TeeIO) so it doesn't accumulate on resume
|
|
148
|
+
@interactive_old_stdout.puts "\e[36mConsoleAgent interactive mode#{name_display}. Type 'exit' or 'quit' to leave.\e[0m"
|
|
149
|
+
@interactive_old_stdout.puts "\e[2m Auto-execute: #{auto ? 'ON' : 'OFF'} (Shift-Tab or /auto to toggle) | /usage | /name <label>\e[0m"
|
|
149
150
|
|
|
150
151
|
# Bind Shift-Tab to insert /auto command and submit
|
|
151
152
|
if Readline.respond_to?(:parse_and_bind)
|
|
@@ -153,7 +154,7 @@ module ConsoleAgent
|
|
|
153
154
|
end
|
|
154
155
|
|
|
155
156
|
loop do
|
|
156
|
-
input = Readline.readline("\e[
|
|
157
|
+
input = Readline.readline("\001\e[33m\002ai> \001\e[0m\002", false)
|
|
157
158
|
break if input.nil? # Ctrl-D
|
|
158
159
|
|
|
159
160
|
input = input.strip
|
|
@@ -163,7 +164,7 @@ module ConsoleAgent
|
|
|
163
164
|
if input == '/auto'
|
|
164
165
|
ConsoleAgent.configuration.auto_execute = !ConsoleAgent.configuration.auto_execute
|
|
165
166
|
mode = ConsoleAgent.configuration.auto_execute ? 'ON' : 'OFF'
|
|
166
|
-
|
|
167
|
+
@interactive_old_stdout.puts "\e[36m Auto-execute: #{mode}\e[0m"
|
|
167
168
|
next
|
|
168
169
|
end
|
|
169
170
|
|
|
@@ -175,7 +176,7 @@ module ConsoleAgent
|
|
|
175
176
|
if input == '/debug'
|
|
176
177
|
ConsoleAgent.configuration.debug = !ConsoleAgent.configuration.debug
|
|
177
178
|
mode = ConsoleAgent.configuration.debug ? 'ON' : 'OFF'
|
|
178
|
-
|
|
179
|
+
@interactive_old_stdout.puts "\e[36m Debug: #{mode}\e[0m"
|
|
179
180
|
next
|
|
180
181
|
end
|
|
181
182
|
|
|
@@ -183,9 +184,9 @@ module ConsoleAgent
|
|
|
183
184
|
name = input.sub('/name', '').strip
|
|
184
185
|
if name.empty?
|
|
185
186
|
if @interactive_session_name
|
|
186
|
-
|
|
187
|
+
@interactive_old_stdout.puts "\e[36m Session name: #{@interactive_session_name}\e[0m"
|
|
187
188
|
else
|
|
188
|
-
|
|
189
|
+
@interactive_old_stdout.puts "\e[33m Usage: /name <label> (e.g. /name salesforce_user_123)\e[0m"
|
|
189
190
|
end
|
|
190
191
|
else
|
|
191
192
|
@interactive_session_name = name
|
|
@@ -193,7 +194,7 @@ module ConsoleAgent
|
|
|
193
194
|
require 'console_agent/session_logger'
|
|
194
195
|
SessionLogger.update(@interactive_session_id, name: name)
|
|
195
196
|
end
|
|
196
|
-
|
|
197
|
+
@interactive_old_stdout.puts "\e[36m Session named: #{name}\e[0m"
|
|
197
198
|
end
|
|
198
199
|
next
|
|
199
200
|
end
|
|
@@ -328,7 +329,20 @@ module ConsoleAgent
|
|
|
328
329
|
$stdout.puts "\e[2m Thinking...\e[0m"
|
|
329
330
|
end
|
|
330
331
|
|
|
331
|
-
|
|
332
|
+
begin
|
|
333
|
+
result = with_escape_monitoring do
|
|
334
|
+
provider.chat_with_tools(messages, tools: tools, system_prompt: context)
|
|
335
|
+
end
|
|
336
|
+
rescue Interrupt
|
|
337
|
+
redirect = prompt_for_redirect
|
|
338
|
+
if redirect
|
|
339
|
+
messages << { role: :user, content: redirect }
|
|
340
|
+
new_messages << messages.last
|
|
341
|
+
next
|
|
342
|
+
else
|
|
343
|
+
raise
|
|
344
|
+
end
|
|
345
|
+
end
|
|
332
346
|
total_input += result.input_tokens || 0
|
|
333
347
|
total_output += result.output_tokens || 0
|
|
334
348
|
|
|
@@ -390,6 +404,59 @@ module ConsoleAgent
|
|
|
390
404
|
[final_result, new_messages]
|
|
391
405
|
end
|
|
392
406
|
|
|
407
|
+
# Monitors stdin for Escape (or Ctrl+C, since raw mode disables signals)
|
|
408
|
+
# and raises Interrupt in the main thread when detected.
|
|
409
|
+
def with_escape_monitoring
|
|
410
|
+
require 'io/console'
|
|
411
|
+
return yield unless $stdin.respond_to?(:raw)
|
|
412
|
+
|
|
413
|
+
monitor = Thread.new do
|
|
414
|
+
Thread.current.report_on_exception = false
|
|
415
|
+
$stdin.raw do |io|
|
|
416
|
+
loop do
|
|
417
|
+
break if Thread.current[:stop]
|
|
418
|
+
ready = IO.select([io], nil, nil, 0.2)
|
|
419
|
+
next unless ready
|
|
420
|
+
|
|
421
|
+
char = io.read_nonblock(1) rescue nil
|
|
422
|
+
next unless char
|
|
423
|
+
|
|
424
|
+
if char == "\x03" # Ctrl+C (raw mode eats the signal)
|
|
425
|
+
Thread.main.raise(Interrupt)
|
|
426
|
+
break
|
|
427
|
+
elsif char == "\e"
|
|
428
|
+
# Distinguish standalone Escape from escape sequences (arrow keys, etc.)
|
|
429
|
+
seq = IO.select([io], nil, nil, 0.05)
|
|
430
|
+
if seq
|
|
431
|
+
io.read_nonblock(10) rescue nil # consume the sequence
|
|
432
|
+
else
|
|
433
|
+
Thread.main.raise(Interrupt)
|
|
434
|
+
break
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
rescue IOError, Errno::EIO, Errno::ENODEV, Errno::ENOTTY
|
|
440
|
+
# stdin is not a TTY (e.g. in tests or piped input) — silently skip
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
begin
|
|
444
|
+
yield
|
|
445
|
+
ensure
|
|
446
|
+
monitor[:stop] = true
|
|
447
|
+
monitor.join(1) rescue nil
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def prompt_for_redirect
|
|
452
|
+
$stdout.puts "\n\e[33m Interrupted. What should the AI do differently?\e[0m"
|
|
453
|
+
$stdout.puts "\e[2m (Press Enter with no input to abort entirely)\e[0m"
|
|
454
|
+
$stdout.print "\e[33m redirect> \e[0m"
|
|
455
|
+
input = $stdin.gets
|
|
456
|
+
return nil if input.nil? || input.strip.empty?
|
|
457
|
+
input.strip
|
|
458
|
+
end
|
|
459
|
+
|
|
393
460
|
def format_tool_args(name, args)
|
|
394
461
|
return '' if args.nil? || args.empty?
|
|
395
462
|
|