pwn 0.5.755 → 0.5.756

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: '088caeab6962a895018847c637332f41ca7033f69c7e8a547862d219c58b162c'
4
- data.tar.gz: b0562e89e6cd08079728c7d5f0b2f640b975f482ed028c1cbe6b27a363361172
3
+ metadata.gz: 7fac5f5a9f5b01e9c4ce830db72a7e454f1a2dce37b44e57f98e290c154cfbbf
4
+ data.tar.gz: 1c508a671f0eb88f704bd38e58b7f3ad7154eca3e0e8d6b4d62852c7bf217a1b
5
5
  SHA512:
6
- metadata.gz: 2730afce4cd70424a7fb927983bb417c1bb755846cd33113ee522f3b4d1a2e3a227251b7f3e5079b9bf3198224f8d36d5f31bed362acc37b6022fb0da03c417d
7
- data.tar.gz: 9ac89ddaeb41583bc8b0db2a98faf9696ae78aae7f8ecc2689f4f8519071e1c284d172245aeb586de4a33c4ad7a81e4dc7ee16a7b849c23f4429e4ee9456e8ca
6
+ metadata.gz: 311ad0755488c66ad2c92c10751c52c5b86b4b725aae976fa697b0f206fb3c88afc954371d8ec8628fcc9431449fe58b71948f4ec2e3ad73b189e88165bcf3b4
7
+ data.tar.gz: e0055f29efafc74e550e84fd504debb71400acbfaf768eb1be82caa1d57cc1d79d49addfb78bb31608fdd0fd2716a4f68277b0d88272960e6b4dffb990b829f9
@@ -578,7 +578,7 @@ module PWN
578
578
  input_schema: anthropic_input_schema(schema: fn[:parameters] || fn['parameters'])
579
579
  }
580
580
  end
581
- http_body[:tool_choice] = anth_tool_choice(choice: opts[:tool_choice]) if opts[:tool_choice]
581
+ http_body[:tool_choice] = anth_tool_choice(choice: opts[:tool_choice], model: model) if opts[:tool_choice]
582
582
  end
583
583
 
584
584
  response = anthropic_rest_call(
@@ -794,15 +794,28 @@ module PWN
794
794
 
795
795
  private_class_method def self.anth_tool_choice(opts = {})
796
796
  choice = opts[:choice]
797
- case choice
798
- when 'none', :none then { type: 'none' }
799
- when 'required', :required, 'any', :any then { type: 'any' }
800
- when Hash
801
- fn = choice[:function] || choice['function'] || choice
802
- { type: 'tool', name: fn[:name] || fn['name'] }
803
- else # 'auto', :auto, nil, anything else
804
- { type: 'auto' }
805
- end
797
+ mapped = case choice
798
+ when 'none', :none then { type: 'none' }
799
+ when 'required', :required, 'any', :any then { type: 'any' }
800
+ when Hash
801
+ fn = choice[:function] || choice['function'] || choice
802
+ { type: 'tool', name: fn[:name] || fn['name'] }
803
+ else
804
+ { type: 'auto' }
805
+ end
806
+ return { type: 'auto' } if !forced_tool_choice?(model: opts[:model]) && %w[any tool].include?(mapped[:type].to_s)
807
+
808
+ mapped
809
+ end
810
+
811
+ # Claude Mythos 5.1, Fable 5.1, Opus 5.5, and Sonnet 5.5 reject forced tool use.
812
+ # Mythos 5 still accepts type any/tool, so match the documented ids, not every Mythos model.
813
+ private_class_method def self.forced_tool_choice?(opts = {})
814
+ model = opts[:model].to_s
815
+ return false if model.match?(/claude-(?:mythos|fable)-5-1(?:-|\z)/i)
816
+ return false if model.match?(/claude-(?:opus|sonnet)-5-5(?:-|\z)/i)
817
+
818
+ true
806
819
  end
807
820
 
808
821
  # Supported Method Parameters::
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.755'
4
+ VERSION = '0.5.756'
5
5
  end
@@ -106,4 +106,31 @@ describe PWN::AI::Anthropic do
106
106
  expect(flat[:properties].keys.map(&:to_s)).to include('handle', 'path')
107
107
  expect(flat[:description]).to include('handle', 'path')
108
108
  end
109
+
110
+ it 'does not force tool use on Anthropic models that reject tool_choice any and tool' do
111
+ allow(PWN::Env).to receive(:[]).with(:ai).and_return(
112
+ anthropic: { key: 'test-key', model: 'claude-mythos-5', temp: 1, max_tokens: 100 }
113
+ )
114
+ allow(PWN::AI::Agent::PromptCache).to receive(:enabled?).and_return(false) if defined?(PWN::AI::Agent::PromptCache)
115
+ captured = nil
116
+ allow(described_class).to receive(:anthropic_rest_call) do |opts|
117
+ captured = opts[:http_body]
118
+ '{"id":"msg_choice","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"stop_reason":"end_turn"}'
119
+ end
120
+ tools = [{ type: 'function', function: { name: 'memory_recall', description: 'recall', parameters: { type: 'object', properties: {} } } }]
121
+ messages = [{ role: 'user', content: 'hello' }]
122
+
123
+ described_class.chat_with_tools(messages: messages, tools: tools, model: 'claude-mythos-5', tool_choice: 'required')
124
+ expect(captured[:tool_choice]).to eq(type: 'any')
125
+
126
+ %w[claude-mythos-5-1 claude-fable-5-1 claude-opus-5-5 claude-sonnet-5-5].each do |model|
127
+ described_class.chat_with_tools(messages: messages, tools: tools, model: model, tool_choice: 'required')
128
+ expect(captured[:tool_choice]).to eq(type: 'auto'), model
129
+ described_class.chat_with_tools(messages: messages, tools: tools, model: model, tool_choice: { type: 'function', function: { name: 'memory_recall' } })
130
+ expect(captured[:tool_choice]).to eq(type: 'auto'), model
131
+ end
132
+
133
+ described_class.chat_with_tools(messages: messages, tools: tools, model: 'claude-mythos-5-1', tool_choice: 'none')
134
+ expect(captured[:tool_choice]).to eq(type: 'none')
135
+ end
109
136
  end
@@ -91,13 +91,22 @@ describe PWN::Plugins::BinaryParser do
91
91
  end
92
92
 
93
93
  it 'elf_resolve points system at the .plt.sec stub, not the next slot' do
94
+ x86_64 = RbConfig::CONFIG['host_cpu'].to_s.match?(/amd64|x86_64/)
94
95
  Dir.mktmpdir('pwn-elf-ibt-') do |dir|
95
96
  src = File.join(dir, 't.c')
96
97
  path = File.join(dir, 't')
97
98
  File.write(src, "#include <stdlib.h>\nint main(void) { return system(\"x\"); }\n")
98
- out, status = Open3.capture2e('cc', '-O0', '-fno-pie', '-no-pie', '-Wl,-z,ibt', '-o', path, src)
99
+ flags = ['-O0', '-fno-pie', '-no-pie']
100
+ flags << '-Wl,-z,ibt' if x86_64
101
+ out, status = Open3.capture2e('cc', *flags, '-o', path, src)
99
102
  expect(status.success?).to eq(true), out
100
- expect(File.binread(path)).to include('.plt.sec')
103
+ binary = File.binread(path)
104
+ unless x86_64
105
+ expect(binary).not_to include('.plt.sec')
106
+ next
107
+ end
108
+
109
+ expect(binary).to include('.plt.sec')
101
110
  row = described_class.elf_resolve(path: path)
102
111
  dump, dump_status = Open3.capture2('objdump', '-d', path)
103
112
  expect(dump_status.success?).to eq(true), dump
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.755
4
+ version: 0.5.756
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.