pwn 0.5.779 → 0.5.780
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/Gemfile +1 -1
- data/lib/pwn/ai/agent/loop.rb +6 -3
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/loop_spec.rb +39 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3207a135c108d0af20cc351965b217a3235abb0eaf67a31d244e3779ed6b72e5
|
|
4
|
+
data.tar.gz: 750df1371f8eb0fbab32b66e88b7568dbac4a49de7dc4020fc018a9957053acc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b10627e1329fa50ac482a49b1013ab455f52eac1a943d92ef7f9144bb93625e742935e0ad2caa1294f3d10a3c46d758b0de50695463e138af76ed7f61871438b
|
|
7
|
+
data.tar.gz: 3f55e834e447b8d51b8a28bd0c71d97498e577d49d7dadfea2a5ceda5e5bfa3876225d27f0c238b02dce690ea82d48612d29fd8a45130cea8213fc4a5650ce0a
|
data/Gemfile
CHANGED
data/lib/pwn/ai/agent/loop.rb
CHANGED
|
@@ -2195,7 +2195,8 @@ module PWN
|
|
|
2195
2195
|
pairs.each do |pair|
|
|
2196
2196
|
tool = pair.find { |m| m[:role].to_s == 'tool' }
|
|
2197
2197
|
prev = collapsed.last&.find { |m| m[:role].to_s == 'tool' }
|
|
2198
|
-
|
|
2198
|
+
# Compare raw bytes: existing history may contain malformed tool output.
|
|
2199
|
+
next if tool && prev && tool[:content].to_s.b.strip == prev[:content].to_s.b.strip
|
|
2199
2200
|
|
|
2200
2201
|
collapsed << pair
|
|
2201
2202
|
end
|
|
@@ -2241,8 +2242,10 @@ module PWN
|
|
|
2241
2242
|
FileUtils.mkdir_p(dir)
|
|
2242
2243
|
path = File.join(dir, "#{digest[0, 12]}.txt")
|
|
2243
2244
|
File.binwrite(path, text) unless File.file?(path)
|
|
2244
|
-
|
|
2245
|
-
|
|
2245
|
+
# Keep the artifact/digest byte-exact; only the displayed excerpt is text.
|
|
2246
|
+
# Byte slicing can bisect a multibyte character at either boundary.
|
|
2247
|
+
head = text.byteslice(0, 2_048).to_s.dup.force_encoding(Encoding::UTF_8).scrub
|
|
2248
|
+
tail = text.bytesize > 3_072 ? text.byteslice(-1_024, 1_024).to_s.dup.force_encoding(Encoding::UTF_8).scrub : ''
|
|
2246
2249
|
mid = tail.empty? ? '' : "\n...\n#{tail}"
|
|
2247
2250
|
"#{head}#{mid}\n[compacted path=#{path} sha256=#{digest} bytes=#{text.bytesize} ref=#{path}]"
|
|
2248
2251
|
rescue StandardError
|
data/lib/pwn/version.rb
CHANGED
|
@@ -1304,6 +1304,45 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
|
|
|
1304
1304
|
expect(src).not_to match(/compact_history!\(messages: messages\) if local/)
|
|
1305
1305
|
end
|
|
1306
1306
|
|
|
1307
|
+
it 'keeps UTF-8 previews valid across repeated history compaction' do
|
|
1308
|
+
Dir.mktmpdir('pwn-history-encoding') do |dir|
|
|
1309
|
+
allow(Dir).to receive(:home).and_return(dir)
|
|
1310
|
+
messages = [{ role: 'system', content: 'sys' }, { role: 'user', content: 'goal' }]
|
|
1311
|
+
8.times do |n|
|
|
1312
|
+
messages << { role: 'assistant', content: '', tool_calls: [{ id: "c#{n}" }] }
|
|
1313
|
+
messages << { role: 'tool', tool_call_id: "c#{n}", content: "a#{'é' * 3000}#{n}" }
|
|
1314
|
+
end
|
|
1315
|
+
2.times do
|
|
1316
|
+
expect { described_class.send(:compact_history!, messages: messages) }.not_to output(/compact_history swallowed/).to_stderr
|
|
1317
|
+
expect(messages.all? { |msg| msg[:content].valid_encoding? }).to be(true)
|
|
1318
|
+
end
|
|
1319
|
+
end
|
|
1320
|
+
end
|
|
1321
|
+
|
|
1322
|
+
it 'preserves raw spilled bytes while rendering malformed and binary-tagged text safely' do
|
|
1323
|
+
Dir.mktmpdir('pwn-history-binary') do |dir|
|
|
1324
|
+
allow(Dir).to receive(:home).and_return(dir)
|
|
1325
|
+
["a#{'é' * 3000}".b, "a\xff#{'é' * 3000}"].each do |text|
|
|
1326
|
+
preview = described_class.send(:spill_tool_body, text: text)
|
|
1327
|
+
expect(preview.encoding).to eq(Encoding::UTF_8)
|
|
1328
|
+
expect(preview.valid_encoding?).to be(true)
|
|
1329
|
+
expect(preview).to include('é', '[compacted path=')
|
|
1330
|
+
path = preview[/\[compacted path=(.*?) sha256=/, 1]
|
|
1331
|
+
expect(File.binread(path)).to eq(text.b)
|
|
1332
|
+
end
|
|
1333
|
+
end
|
|
1334
|
+
end
|
|
1335
|
+
|
|
1336
|
+
it 'compacts existing malformed tool history without swallowing encoding errors' do
|
|
1337
|
+
messages = [{ role: 'system', content: 'sys' }, { role: 'user', content: 'goal' }]
|
|
1338
|
+
8.times do |n|
|
|
1339
|
+
messages << { role: 'assistant', tool_calls: [{ id: "c#{n}" }] }
|
|
1340
|
+
messages << { role: 'tool', tool_call_id: "c#{n}", content: "invalid\xff#{n}" }
|
|
1341
|
+
end
|
|
1342
|
+
expect { described_class.send(:compact_history!, messages: messages) }.not_to output(/compact_history swallowed/).to_stderr
|
|
1343
|
+
expect(messages.count { |msg| msg[:role] == 'tool' }).to eq(6)
|
|
1344
|
+
end
|
|
1345
|
+
|
|
1307
1346
|
it 'shrinks tool bodies when history is fat' do
|
|
1308
1347
|
msgs = [
|
|
1309
1348
|
{ role: 'system', content: 'sys' },
|
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.
|
|
4
|
+
version: 0.5.780
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
@@ -505,14 +505,14 @@ dependencies:
|
|
|
505
505
|
requirements:
|
|
506
506
|
- - '='
|
|
507
507
|
- !ruby/object:Gem::Version
|
|
508
|
-
version: 1.8.
|
|
508
|
+
version: 1.8.1
|
|
509
509
|
type: :runtime
|
|
510
510
|
prerelease: false
|
|
511
511
|
version_requirements: !ruby/object:Gem::Requirement
|
|
512
512
|
requirements:
|
|
513
513
|
- - '='
|
|
514
514
|
- !ruby/object:Gem::Version
|
|
515
|
-
version: 1.8.
|
|
515
|
+
version: 1.8.1
|
|
516
516
|
- !ruby/object:Gem::Dependency
|
|
517
517
|
name: meshtastic
|
|
518
518
|
requirement: !ruby/object:Gem::Requirement
|