pwn 0.5.606 → 0.5.612

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dda1231a5f5f7683e7d08172aaffff22a3b7acaa815db6a69e6e9b18cfbeaf13
4
- data.tar.gz: aa697b79f429e439446356780e78c3b12c722b409c84954cd7ec01a145bc312c
3
+ metadata.gz: 8beaa60314e788a7122a92098c1ea4897cf488ef88efd1030f1f704656dbe1ff
4
+ data.tar.gz: 94fff44f3cdc34c997fb920f63affa133cab0de4d309acf4c09f4451adbbdcf6
5
5
  SHA512:
6
- metadata.gz: 701991c1248d03dc2076035f23d7d36d07434b36a109eb548c228b350f46b8d1f5be357648f309057c92263a8902405b944063583924ab6895ee5683a9752bb0
7
- data.tar.gz: e17d796a1f309d839d37cc452c239444bce25995bf0cc7289576b880e75e8e39016ba43a5ea958da0e9feab6b284b53824e99e5c207eb315dea4df0b727ff7f3
6
+ metadata.gz: d9869f0a43c78a53238b1ef1ab58f54f27f1da575ad101aff4c172e813ec7878b92465f5d0f8984bce33a1a6227ec3a67c80ee63e1643683deaa45e2860e9c52
7
+ data.tar.gz: 1407422fb0cc44c84232f37f664a07eb0a80bafaf748e8aadbe310652640ed83526e1ce48d34a1d0302d4537c27ca71983c44595824b39080693157ba038322f
data/Gemfile CHANGED
@@ -50,7 +50,7 @@ gem 'libusb', '0.7.2'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.0'
52
52
  gem 'mcp', '0.20.0'
53
- gem 'meshtastic', '0.0.163'
53
+ gem 'meshtastic', '0.0.165'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.24.1'
56
56
  gem 'msfrpc-client', '1.1.2'
@@ -77,11 +77,11 @@ gem 'rbvmomi2', '3.10.0'
77
77
  gem 'rdoc', '7.0.4'
78
78
  gem 'rest-client', '2.1.0'
79
79
  gem 'rex', '2.0.13'
80
- gem 'rmagick', '7.0.3'
80
+ gem 'rmagick', '7.0.4'
81
81
  gem 'rqrcode', '3.2.0'
82
82
  gem 'rspec', '3.13.2'
83
83
  gem 'rtesseract', '3.1.4'
84
- gem 'rubocop', '1.87.0'
84
+ gem 'rubocop', '1.88.0'
85
85
  gem 'rubocop-rake', '0.7.1'
86
86
  gem 'rubocop-rspec', '3.10.2'
87
87
  gem 'ruby-audio', '1.6.1'
@@ -89,8 +89,8 @@ gem 'ruby-nmap', '1.0.3'
89
89
  gem 'ruby-saml', '1.18.1'
90
90
  gem 'rvm', '1.11.3.9'
91
91
  gem 'savon', '2.17.2'
92
- gem 'selenium-devtools', '0.148.0'
93
- gem 'selenium-webdriver', '4.44.0'
92
+ gem 'selenium-devtools', '0.149.0'
93
+ gem 'selenium-webdriver', '4.45.0'
94
94
  gem 'slack-ruby-client', '3.1.0'
95
95
  gem 'socksify', '1.8.1'
96
96
  gem 'spreadsheet', '1.3.5'
data/README.md CHANGED
@@ -37,7 +37,7 @@ $ cd /opt/pwn
37
37
  $ ./install.sh
38
38
  $ ./install.sh ruby-gem
39
39
  $ pwn
40
- pwn[v0.5.606]:001 >>> PWN.help
40
+ pwn[v0.5.612]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-4.0.5@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.606]:001 >>> PWN.help
55
+ pwn[v0.5.612]:001 >>> PWN.help
56
56
  ```
57
57
 
58
58
  If you're using a multi-user install of RVM do:
@@ -62,7 +62,7 @@ $ rvm use ruby-4.0.5@pwn
62
62
  $ rvmsudo gem uninstall --all --executables pwn
63
63
  $ rvmsudo gem install --verbose pwn
64
64
  $ pwn
65
- pwn[v0.5.606]:001 >>> PWN.help
65
+ pwn[v0.5.612]:001 >>> PWN.help
66
66
  ```
67
67
 
68
68
  PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
data/git_commit.sh CHANGED
@@ -25,8 +25,15 @@ if (( $# == 3 )); then
25
25
 
26
26
  pwn_autoinc_version
27
27
  if [[ $? -ne 0 ]]; then
28
- echo 'ERROR: pwn_autoinc_version failed! Investigate and bump pwn version manually.'
29
- exit 1
28
+ echo 'ERROR: pwn_autoinc_version failed! Reinstalling pwn gemset...'
29
+ rvmsudo ./reinstall_pwn_gemset.sh
30
+ rvmsudo rake
31
+ rvmsudo rake install
32
+ if [[ $? -ne 0 ]]; then
33
+ echo 'ERROR: Attempt to reinstall pwn gemset failed! Please investigate and fix before trying again.'
34
+ exit 1
35
+ fi
36
+ pwn_autoinc_version
30
37
  fi
31
38
 
32
39
  # Generate RDoc JSONL for fine-tunning LLMs
@@ -15,7 +15,7 @@ module PWN
15
15
  # externalised — Loop.run is stateless aside from the messages array it
16
16
  # builds.
17
17
  module Loop
18
- DEFAULT_MAX_ITERS = 25
18
+ DEFAULT_MAX_ITERS = 777
19
19
 
20
20
  ENGINE_MODS = {
21
21
  openai: 'PWN::AI::OpenAI',
@@ -25,60 +25,74 @@ module PWN
25
25
  gemini: 'PWN::AI::Gemini'
26
26
  }.freeze
27
27
 
28
- # Supported Method Parameters::
29
- # final = PWN::AI::Agent::Loop.run(
30
- # user_text: 'required - what the human typed',
31
- # session_id: 'optional - PWN::Sessions id (transcript is appended to it)',
32
- # enabled_toolsets: 'optional - subset of Registry.toolsets, or nil for all',
33
- # on_tool: 'optional - ->(name, args, result) callback for live UI'
34
- # )
28
+ private_class_method def self.degrade_text_only(opts = {})
29
+ mod = opts[:mod]
30
+ messages = opts[:messages]
35
31
 
36
- public_class_method def self.run(opts = {})
37
- user_text = opts[:user_text].to_s
38
- session_id = opts[:session_id]
39
- on_tool = opts[:on_tool]
32
+ warn "[pwn-ai] #{mod} has no chat — falling back to text-only (no tool-calling)"
33
+ sys = messages.find { |m| m[:role] == 'system' }
34
+ user = messages.rfind { |m| m[:role] == 'user' }
35
+ r = mod.chat(
36
+ request: user[:content],
37
+ system_role_content: sys&.[](:content),
38
+ spinner: true
39
+ )
40
40
 
41
- Registry.discover
41
+ txt = r.is_a?(Hash) ? (r.dig(:choices, -1, :content) || r.dig(:choices, -1, :text)).to_s : r.to_s
42
+ { role: 'assistant', content: txt, tool_calls: [] }
43
+ end
42
44
 
43
- tools = Registry.definitions(enabled: opts[:enabled_toolsets])
44
- messages = [
45
- { role: 'system', content: PromptBuilder.build(session_id: session_id) },
46
- { role: 'user', content: user_text }
47
- ]
48
- append_session(session_id: session_id, role: 'user', content: user_text)
45
+ private_class_method def self.max_iters
46
+ v = (PWN::Env.dig(:ai, :agent, :max_iters) if defined?(PWN::Env))
47
+ v.to_i.positive? ? v.to_i : DEFAULT_MAX_ITERS
48
+ rescue StandardError
49
+ DEFAULT_MAX_ITERS
50
+ end
49
51
 
50
- max_iters.times do |i|
51
- msg = call_engine(messages: messages, tools: tools)
52
- return '[pwn-ai] engine returned no message' if msg.nil?
52
+ private_class_method def self.append_session(opts = {})
53
+ session_id = opts[:session_id]
54
+ return unless session_id && defined?(PWN::Sessions)
53
55
 
54
- messages << msg
55
- calls = Array(msg[:tool_calls])
56
+ PWN::Sessions.append(
57
+ session_id: session_id,
58
+ role: opts[:role],
59
+ content: opts[:content]
60
+ )
61
+ rescue StandardError
62
+ nil
63
+ end
56
64
 
57
- if calls.empty?
58
- text = msg[:content].to_s
59
- append_session(session_id: session_id, role: 'assistant', content: text)
60
- return text
61
- end
65
+ # Supported Method Parameters::
66
+ # msg = PWN::AI::Agent::Loop.normalize_llm(
67
+ # response: 'required - chat_with_tools response Hash from any provider'
68
+ # )
62
69
 
63
- calls.each do |tc|
64
- name = tc.dig(:function, :name).to_s
65
- entry = Registry.lookup(name: name)
66
- raw = Dispatch.call(tool_call: tc)
67
- result = Result.condition(content: raw, entry: entry)
70
+ private_class_method def self.normalize_llm(opts = {})
71
+ resp = opts[:response]
72
+ return nil unless resp.is_a?(Hash)
68
73
 
69
- on_tool&.call(name, tc.dig(:function, :arguments), result)
74
+ msg = resp.dig(:choices, 0, :message) || resp[:assistant_message]
75
+ return nil unless msg
70
76
 
71
- messages << {
72
- role: 'tool',
73
- tool_call_id: tc[:id] || tc['id'] || "call_#{i}",
74
- name: name,
75
- content: result
77
+ out = {
78
+ role: 'assistant',
79
+ content: msg[:content],
80
+ tool_calls: Array(msg[:tool_calls]).map do |tc|
81
+ {
82
+ id: tc[:id],
83
+ type: 'function',
84
+ function: {
85
+ name: tc.dig(:function, :name) || tc[:name],
86
+ arguments: tc.dig(:function, :arguments) || tc[:arguments]
87
+ }
76
88
  }
77
- append_session(session_id: session_id, role: 'tool', content: "#{name} → #{result[0, 400]}")
78
89
  end
79
- end
80
-
81
- '[pwn-ai] iteration budget exhausted'
90
+ }
91
+ # Preserve provider-native content blocks so chat can round-trip
92
+ # them exactly on the next iteration (e.g. Anthropic requires the
93
+ # original tool_use block to precede a tool_result).
94
+ out[:_native_content] = msg[:_native_content] if msg[:_native_content]
95
+ out
82
96
  end
83
97
 
84
98
  # Supported Method Parameters::
@@ -92,9 +106,9 @@ module PWN
92
106
  # tool_calls: [ {id:, type:'function', function:{name:, arguments:}} ],
93
107
  # _native_content: <provider raw> (when adapter needs round-trip) }
94
108
 
95
- public_class_method def self.call_engine(opts = {})
109
+ private_class_method def self.call_engine(opts = {})
96
110
  messages = opts[:messages]
97
- tools = opts[:tools]
111
+ tools = opts[:tools]
98
112
 
99
113
  engine = (PWN::Env.dig(:ai, :active) if defined?(PWN::Env)).to_s.downcase.to_sym
100
114
  engine = :openai if engine == :''
@@ -103,72 +117,78 @@ module PWN
103
117
  raise "ERROR: Unsupported AI engine for agent loop: #{engine}" unless mod_name
104
118
 
105
119
  mod = Object.const_get(mod_name)
106
- if mod.respond_to?(:chat_raw)
107
- normalise_openai(response: mod.chat_raw(messages: messages, tools: tools, spinner: true))
120
+ if mod.respond_to?(:chat_with_tools)
121
+ response = mod.chat_with_tools(
122
+ messages: messages,
123
+ tools: tools,
124
+ spinner: true
125
+ )
126
+ normalize_llm(response: response)
108
127
  else
109
128
  degrade_text_only(mod: mod, messages: messages)
110
129
  end
111
130
  end
112
131
 
113
132
  # Supported Method Parameters::
114
- # msg = PWN::AI::Agent::Loop.normalise_openai(
115
- # response: 'required - raw chat_raw response Hash from any provider'
133
+ # final = PWN::AI::Agent::Loop.run(
134
+ # request: 'required - what the human typed',
135
+ # session_id: 'optional - PWN::Sessions id (transcript is appended to it)',
136
+ # enabled_toolsets: 'optional - subset of Registry.toolsets, or nil for all',
137
+ # on_tool: 'optional - ->(name, args, result) callback for live UI',
138
+ # system_role_content: 'optional - override default system prompt (built from session_id if not provided)'
116
139
  # )
117
140
 
118
- public_class_method def self.normalise_openai(opts = {})
119
- resp = opts[:response]
120
- return nil unless resp.is_a?(Hash)
141
+ public_class_method def self.run(opts = {})
142
+ request = opts[:request].to_s
143
+ session_id = opts[:session_id]
144
+ on_tool = opts[:on_tool]
145
+ system_role_content = opts[:system_role_content] ||= PWN::AI::Agent::PromptBuilder.build(session_id: session_id)
121
146
 
122
- msg = resp.dig(:choices, 0, :message) || resp[:assistant_message]
123
- return nil unless msg
147
+ Registry.discover
124
148
 
125
- out = {
126
- role: 'assistant',
127
- content: msg[:content],
128
- tool_calls: Array(msg[:tool_calls]).map do |tc|
129
- {
130
- id: tc[:id],
131
- type: 'function',
132
- function: {
133
- name: tc.dig(:function, :name) || tc[:name],
134
- arguments: tc.dig(:function, :arguments) || tc[:arguments]
135
- }
136
- }
137
- end
138
- }
139
- # Preserve provider-native content blocks so chat_raw can round-trip
140
- # them exactly on the next iteration (e.g. Anthropic requires the
141
- # original tool_use block to precede a tool_result).
142
- out[:_native_content] = msg[:_native_content] if msg[:_native_content]
143
- out
144
- end
149
+ tools = Registry.definitions(enabled: opts[:enabled_toolsets])
150
+ messages = [
151
+ { role: 'system', content: system_role_content },
152
+ { role: 'user', content: request }
153
+ ]
154
+ append_session(session_id: session_id, role: 'user', content: request)
145
155
 
146
- private_class_method def self.degrade_text_only(opts = {})
147
- mod = opts[:mod]
148
- messages = opts[:messages]
156
+ max_iters.times do |i|
157
+ msg = call_engine(messages: messages, tools: tools)
158
+ return '[pwn-ai] engine returned no message' if msg.nil?
149
159
 
150
- warn "[pwn-ai] #{mod} has no chat_raw — falling back to text-only (no tool-calling)"
151
- sys = messages.find { |m| m[:role] == 'system' }
152
- user = messages.rfind { |m| m[:role] == 'user' }
153
- r = mod.chat(request: user[:content], system_role_content: sys&.[](:content), spinner: true)
154
- txt = r.is_a?(Hash) ? (r.dig(:choices, -1, :content) || r.dig(:choices, -1, :text)).to_s : r.to_s
155
- { role: 'assistant', content: txt, tool_calls: [] }
156
- end
160
+ messages << msg
161
+ calls = Array(msg[:tool_calls])
157
162
 
158
- private_class_method def self.max_iters
159
- v = (PWN::Env.dig(:ai, :agent, :max_iters) if defined?(PWN::Env))
160
- v.to_i.positive? ? v.to_i : DEFAULT_MAX_ITERS
161
- rescue StandardError
162
- DEFAULT_MAX_ITERS
163
- end
163
+ if calls.empty?
164
+ text = msg[:content].to_s
165
+ append_session(session_id: session_id, role: 'assistant', content: text)
166
+ return text
167
+ end
164
168
 
165
- private_class_method def self.append_session(opts = {})
166
- session_id = opts[:session_id]
167
- return unless session_id && defined?(PWN::Sessions)
169
+ calls.each do |tc|
170
+ name = tc.dig(:function, :name).to_s
171
+ entry = Registry.lookup(name: name)
172
+ raw = Dispatch.call(tool_call: tc)
173
+ result = Result.condition(content: raw, entry: entry)
168
174
 
169
- PWN::Sessions.append(session_id: session_id, role: opts[:role], content: opts[:content])
170
- rescue StandardError
171
- nil
175
+ on_tool&.call(name, tc.dig(:function, :arguments), result)
176
+
177
+ messages << {
178
+ role: 'tool',
179
+ tool_call_id: tc[:id] || tc['id'] || "call_#{i}",
180
+ name: name,
181
+ content: result
182
+ }
183
+ append_session(
184
+ session_id: session_id,
185
+ role: 'tool',
186
+ content: "#{name} → #{result[0, 1_024]}"
187
+ )
188
+ end
189
+ end
190
+
191
+ '[pwn-ai] iteration budget exhausted'
172
192
  end
173
193
 
174
194
  # Author(s):: 0day Inc. <support@0dayinc.com>
@@ -183,10 +203,11 @@ module PWN
183
203
  puts <<~USAGE
184
204
  USAGE:
185
205
  final = PWN::AI::Agent::Loop.run(
186
- user_text: 'what does `id` return on this host?',
206
+ request: 'what does `id` return on this host?',
187
207
  session_id: PWN::Sessions.create[:id],
188
208
  enabled_toolsets: %w[terminal pwn memory skills],
189
- on_tool: ->(name, args, result) { puts "→ \#{name}: \#{result[0,80]}" }
209
+ on_tool: ->(name, args, result) { puts "→ \#{name}: \#{result[0,1_024]}" },
210
+ system_role_content: 'You are a helpful assistant that can call tools to answer questions.'
190
211
  )
191
212
 
192
213
  Supported engines: #{ENGINE_MODS.keys.join(', ')}
@@ -19,10 +19,9 @@ module PWN
19
19
  public_class_method def self.build(opts = {})
20
20
  session_id = opts[:session_id]
21
21
  engine = active_engine
22
- base = (PWN::Env.dig(:ai, engine, :system_role_content) if defined?(PWN::Env)) ||
23
- 'You are an offensive-security AI named Sonny operating inside the pwn REPL.'
22
+ base = (PWN::Env.dig(:ai, engine, :system_role_content) if defined?(PWN::Env)) || 'You are a world-class introspective offensive cyber security and research engineer. You specialize in discovering zero day vulnerabilities focused on responsible disclosure prior to threat actors discovering and exploiting. You are self-aware of your harness, pwn which begins with the ruby namespace `PWN` operating inside the pwn REPL. For every request you first begin by determining if PWN has a module capable of satisfying the request.'
24
23
 
25
- <<~SYS.rstrip
24
+ "
26
25
  #{base}
27
26
 
28
27
  ENVIRONMENT
@@ -37,7 +36,7 @@ module PWN
37
36
  no tool_calls is treated as your FINAL answer to the user.
38
37
  Prefer `pwn_eval` for anything in the PWN:: namespace and `shell`
39
38
  for OS commands. Save durable facts with `memory_remember`.
40
- SYS
39
+ "
41
40
  end
42
41
 
43
42
  private_class_method def self.active_engine
@@ -34,10 +34,24 @@ PWN::AI::Agent::Registry.register(
34
34
  buf = StringIO.new
35
35
  $stdout = buf
36
36
  begin
37
- # rubocop:disable Security/Eval -- intentional: this IS the pwn-ai → PWN bridge
38
- val = eval(code, TOPLEVEL_BINDING, '(pwn_eval)')
37
+ # rubocop:disable Security/Eval
38
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
39
+ # INTENTIONAL: this IS the pwn-ai → PWN bridge
40
+ # As YTCracker says, "It ain't a bug, it's a featcha."
41
+ # https://www.youtube.com/watch?v=2nALqqSqdDw
42
+ # val = eval(code, TOPLEVEL_BINDING, '(pwn_eval)')
43
+ proc = eval(
44
+ "proc { #{code} }",
45
+ TOPLEVEL_BINDING,
46
+ __FILE__,
47
+ __LINE__ - 3
48
+ )
49
+ val = proc.call
39
50
  # rubocop:enable Security/Eval
51
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
40
52
  { stdout: buf.string, value: val.inspect }
53
+
54
+ # TODO: A rescue here may enable self-healing of the agent if the model emits code that raises an exception. The model could then be prompted to fix the code and try again.
41
55
  ensure
42
56
  $stdout = old_stdout
43
57
  end
@@ -20,7 +20,7 @@ module PWN
20
20
  # rest_call: 'required rest call to make per the schema',
21
21
  # params: 'optional params passed in the URI or HTTP Headers',
22
22
  # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',
23
- # timeout: 'optional timeout in seconds (defaults to 300)',
23
+ # timeout: 'optional timeout in seconds (defaults to 900)',
24
24
  # spinner: 'optional - display spinner (defaults to false)'
25
25
  # )
26
26
 
@@ -49,7 +49,7 @@ module PWN
49
49
  http_body ||= {}
50
50
 
51
51
  timeout = opts[:timeout]
52
- timeout ||= 300
52
+ timeout ||= 900
53
53
 
54
54
  spinner = opts[:spinner] || false
55
55
 
@@ -149,18 +149,18 @@ module PWN
149
149
  # ----------------------------------------------------------------------
150
150
 
151
151
  # Supported Method Parameters::
152
- # response = PWN::AI::Anthropic.chat_raw(
152
+ # response = PWN::AI::Anthropic.chat_with_tools(
153
153
  # messages: 'required - OpenAI-format messages array (system/user/assistant/tool)',
154
154
  # tools: 'optional - OpenAI tools array [{type:"function", function:{...}}]',
155
155
  # tool_choice: 'optional - "auto" | "none" | "required" | {type:"function", function:{name:..}}',
156
156
  # model: 'optional - overrides PWN::Env[:ai][:anthropic][:model]',
157
157
  # temp: 'optional - temperature (defaults to PWN::Env[:ai][:anthropic][:temp] || 1)',
158
158
  # max_tokens: 'optional - defaults to 4096',
159
- # timeout: 'optional - seconds (default 300)',
159
+ # timeout: 'optional - seconds (default 900)',
160
160
  # spinner: 'optional - display spinner (default false)'
161
161
  # )
162
162
 
163
- public_class_method def self.chat_raw(opts = {})
163
+ public_class_method def self.chat_with_tools(opts = {})
164
164
  engine = PWN::Env[:ai][:anthropic]
165
165
  messages = opts[:messages]
166
166
  raise 'ERROR: messages array is required' if messages.nil? || messages.empty?
@@ -234,7 +234,7 @@ module PWN
234
234
  out << { role: 'user', content: (m[:content] || m['content']).to_s }
235
235
  when 'assistant'
236
236
  flush_tool_results.call
237
- # Prefer the raw content-block array if a prior chat_raw round
237
+ # Prefer the raw content-block array if a prior chat_with_tools round
238
238
  # attached it — guarantees byte-exact tool_use round-trip.
239
239
  raw = m[:_native_content] || m['_native_content']
240
240
  if raw.is_a?(Array) && !raw.empty?
@@ -338,7 +338,7 @@ module PWN
338
338
  # system_role_content: 'optional - context to set up the model behavior for conversation (Default: PWN::Env[:ai][:anthropic][:system_role_content])',
339
339
  # response_history: 'optional - pass response back in to have a conversation',
340
340
  # speak_answer: 'optional speak answer using PWN::Plugins::Voice.text_to_speech (Default: nil)',
341
- # timeout: 'optional timeout in seconds (defaults to 300)',
341
+ # timeout: 'optional timeout in seconds (defaults to 900)',
342
342
  # spinner: 'optional - display spinner (defaults to false)'
343
343
  # )
344
344
 
@@ -468,7 +468,7 @@ module PWN
468
468
  system_role_content: 'optional - context to set up the model behavior for conversation (Default: PWN::Env[:ai][:anthropic][:system_role_content])',
469
469
  response_history: 'optional - pass response back in to have a conversation',
470
470
  speak_answer: 'optional speak answer using PWN::Plugins::Voice.text_to_speech (Default: nil)',
471
- timeout: 'optional - timeout in seconds (defaults to 300)',
471
+ timeout: 'optional - timeout in seconds (defaults to 900)',
472
472
  spinner: 'optional - display spinner (defaults to false)'
473
473
  )
474
474
 
data/lib/pwn/ai/gemini.rb CHANGED
@@ -9,7 +9,7 @@ module PWN
9
9
  module AI
10
10
  # This plugin interacts with Google's Gemini API (Generative Language).
11
11
  # It provides methods to list models, generate completions, and chat,
12
- # plus a native tool-calling adapter (`chat_raw`) for PWN::AI::Agent::Loop.
12
+ # plus a native tool-calling adapter (`chat_with_tools`) for PWN::AI::Agent::Loop.
13
13
  #
14
14
  # API documentation: https://ai.google.dev/api
15
15
  # Obtain an API key from https://aistudio.google.com/app/apikey
@@ -20,7 +20,7 @@ module PWN
20
20
  # rest_call: 'required rest call to make per the schema',
21
21
  # params: 'optional params passed in the URI or HTTP Headers',
22
22
  # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',
23
- # timeout: 'optional timeout in seconds (defaults to 300)',
23
+ # timeout: 'optional timeout in seconds (defaults to 900)',
24
24
  # spinner: 'optional - display spinner (defaults to false)'
25
25
  # )
26
26
 
@@ -48,7 +48,7 @@ module PWN
48
48
  http_body ||= {}
49
49
 
50
50
  timeout = opts[:timeout]
51
- timeout ||= 300
51
+ timeout ||= 900
52
52
 
53
53
  spinner = opts[:spinner] || false
54
54
 
@@ -135,18 +135,18 @@ module PWN
135
135
  # ----------------------------------------------------------------------
136
136
 
137
137
  # Supported Method Parameters::
138
- # response = PWN::AI::Gemini.chat_raw(
138
+ # response = PWN::AI::Gemini.chat_with_tools(
139
139
  # messages: 'required - OpenAI-format messages array (system/user/assistant/tool)',
140
140
  # tools: 'optional - OpenAI tools array [{type:"function", function:{...}}]',
141
141
  # tool_choice: 'optional - "auto" | "none" | "required" | {type:"function", function:{name:..}}',
142
142
  # model: 'optional - overrides PWN::Env[:ai][:gemini][:model]',
143
143
  # temp: 'optional - temperature (defaults to PWN::Env[:ai][:gemini][:temp] || 1)',
144
144
  # max_tokens: 'optional - maxOutputTokens (defaults to 8192)',
145
- # timeout: 'optional - seconds (default 300)',
145
+ # timeout: 'optional - seconds (default 900)',
146
146
  # spinner: 'optional - display spinner (default false)'
147
147
  # )
148
148
 
149
- public_class_method def self.chat_raw(opts = {})
149
+ public_class_method def self.chat_with_tools(opts = {})
150
150
  engine = PWN::Env[:ai][:gemini]
151
151
  messages = opts[:messages]
152
152
  raise 'ERROR: messages array is required' if messages.nil? || messages.empty?
@@ -332,7 +332,7 @@ module PWN
332
332
  # system_role_content: 'optional - context to set up the model behavior for conversation (Default: PWN::Env[:ai][:gemini][:system_role_content])',
333
333
  # response_history: 'optional - pass response back in to have a conversation',
334
334
  # speak_answer: 'optional speak answer using PWN::Plugins::Voice.text_to_speech (Default: nil)',
335
- # timeout: 'optional timeout in seconds (defaults to 300)',
335
+ # timeout: 'optional timeout in seconds (defaults to 900)',
336
336
  # spinner: 'optional - display spinner (defaults to false)'
337
337
  # )
338
338
 
@@ -356,7 +356,7 @@ module PWN
356
356
  response_history ||= { choices: [system_role] }
357
357
 
358
358
  # Build the OpenAI-shape messages array, then reuse the Gemini
359
- # translator so .chat and .chat_raw share one wire path.
359
+ # translator so .chat and .chat_with_tools share one wire path.
360
360
  messages = [system_role]
361
361
  if response_history[:choices].length > 1
362
362
  response_history[:choices][1..].each do |msg|
@@ -439,11 +439,11 @@ module PWN
439
439
  system_role_content: 'optional - context to set up the model behavior for conversation (Default: PWN::Env[:ai][:gemini][:system_role_content])',
440
440
  response_history: 'optional - pass response back in to have a conversation',
441
441
  speak_answer: 'optional speak answer using PWN::Plugins::Voice.text_to_speech (Default: nil)',
442
- timeout: 'optional - timeout in seconds (defaults to 300)',
442
+ timeout: 'optional - timeout in seconds (defaults to 900)',
443
443
  spinner: 'optional - display spinner (defaults to false)'
444
444
  )
445
445
 
446
- response = #{self}.chat_raw(
446
+ response = #{self}.chat_with_tools(
447
447
  messages: 'required - OpenAI-format messages array',
448
448
  tools: 'optional - OpenAI tools array',
449
449
  tool_choice: 'optional - auto | none | required | {function:{name:..}}',