Module: PWN::Memory

Defined in:
lib/pwn/memory.rb

Overview

PWN::Memory provides persistent cross-session memory for the pwn-ai agent (equivalent to Hermes agent memory providers). Facts, user preferences, environment details, lessons learned, and task state are stored in ~/.pwn/memory.json and survive across REPL restarts / pwn-ai sessions.

The pwn-ai agent (in agent mode) automatically receives relevant memory injected into its system prompt. The agent can also call remember/recall via ruby code blocks during execution loops.

Constant Summary collapse

MEMORY_FILE =
File.join(Dir.home, '.pwn', 'memory.json')

Class Method Summary collapse

Class Method Details

.authorsObject

Author(s)

0day Inc. <support@0dayinc.com>



117
118
119
# File 'lib/pwn/memory.rb', line 117

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <support@0dayinc.com>\n"
end

.clearObject

Supported Method Parameters

PWN::Memory.clear



95
96
97
98
# File 'lib/pwn/memory.rb', line 95

public_class_method def self.clear
  FileUtils.rm_f(MEMORY_FILE)
  {}
end

.forget(key) ⇒ Object

rubocop:disable Naming/PredicateMethod



86
87
88
89
90
91
# File 'lib/pwn/memory.rb', line 86

public_class_method def self.forget(key) # rubocop:disable Naming/PredicateMethod
  mem = load
  mem.delete(key.to_sym)
  save(mem)
  true
end

.helpObject

Display Usage for this Module



122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/pwn/memory.rb', line 122

public_class_method def self.help
  puts <<~USAGE
    USAGE:
      mem = PWN::Memory.load
      PWN::Memory.remember(key: :user_prefers_ruby, value: 'Always prefer pure Ruby + RestClient patterns', category: :preference)
      facts = PWN::Memory.recall(query: 'recon', category: :fact, limit: 10)
      PWN::Memory.forget(:some_key)
      PWN::Memory.clear
      context_str = PWN::Memory.to_context

      #{self}.authors
  USAGE
end

.loadObject

Supported Method Parameters

memory = PWN::Memory.load



21
22
23
24
25
26
27
28
# File 'lib/pwn/memory.rb', line 21

public_class_method def self.load
  FileUtils.mkdir_p(File.dirname(MEMORY_FILE))
  return {} unless File.exist?(MEMORY_FILE)

  JSON.parse(File.read(MEMORY_FILE), symbolize_names: true)
rescue StandardError
  {}
end

.recall(opts = {}) ⇒ Object

Supported Method Parameters

results = PWN::Memory.recall(

query: 'optional - string to search keys/values/categories (simple match)',
category: 'optional - filter by category',
limit: 'optional - max results (default 50)'

)



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pwn/memory.rb', line 68

public_class_method def self.recall(opts = {})
  query = opts[:query].to_s.downcase
  category = opts[:category]
  limit = opts[:limit] || 50

  mem = load
  results = mem.select do |k, v|
    match = true
    match &&= k.to_s.downcase.include?(query) || v[:value].to_s.downcase.include?(query) || v[:category].to_s.downcase.include?(query) if query && !query.empty?
    match &&= (v[:category] == category.to_sym) if category
    match
  end

  results.to_a.first(limit).to_h
end

.remember(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Memory.remember(

key: 'required - Symbol or String key for the memory fact',
value: 'required - The value (any JSON serializable)',
category: 'optional - e.g. :fact, :preference, :lesson, :env (default: :fact)'

)



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/pwn/memory.rb', line 44

public_class_method def self.remember(opts = {})
  key = opts[:key]
  value = opts[:value]
  category = opts[:category] || :fact

  raise 'ERROR: key and value are required' if key.nil? || value.nil?

  mem = load
  mem[key.to_sym] = {
    value: value,
    category: category.to_sym,
    timestamp: Time.now.utc.iso8601,
    source: 'pwn-ai'
  }
  save(mem)
  mem[key.to_sym]
end

.save(mem = {}) ⇒ Object

Supported Method Parameters

PWN::Memory.save(memory_hash)



32
33
34
35
36
# File 'lib/pwn/memory.rb', line 32

public_class_method def self.save(mem = {})
  FileUtils.mkdir_p(File.dirname(MEMORY_FILE))
  File.write(MEMORY_FILE, JSON.pretty_generate(mem))
  mem
end

.to_context(opts = {}) ⇒ Object

Supported Method Parameters

context = PWN::Memory.to_context(limit: 20) (used internally by pwn-ai hook to inject into system prompt)



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/pwn/memory.rb', line 103

public_class_method def self.to_context(opts = {})
  limit = opts[:limit] || 20
  mem = recall(limit: limit)
  return '' if mem.empty?

  ctx = "\n\nPERSISTENT MEMORY (cross-session facts, prefs, lessons - use PWN::Memory.remember to store new ones):\n"
  mem.each do |k, v|
    ctx += "- #{k} [#{v[:category]} @ #{v[:timestamp]}]: #{v[:value].to_s[0, 300]}\n"
  end
  ctx
end