Class: Rubino::Tools::MemoryTool

Inherits:
Base
  • Object
show all
Defined in:
lib/rubino/tools/memory_tool.rb

Overview

Agent-callable interface to the memory store.

The agent uses this to record durable facts about the user or project across sessions. The schema is deliberately tiny — three actions, two targets — because every additional knob is another surface a prompt-injection attempt can probe. Threat scanning and the char-budget run inside Memory::Store; this tool only handles the action/target mapping and translates Store exceptions into tool-protocol error strings.

Constant Summary collapse

VALID_ACTIONS =
%w[add replace remove].freeze
VALID_TARGETS =
%w[memory user].freeze
TARGET_TO_KIND =

target → memory kind. “user” is the user_profile slot; “memory” is the catch-all “fact” kind. Other kinds (preference, technical_decision, …) are reserved for the auto-extractor — the agent does not get to write to them directly through this tool.

{ "memory" => "fact", "user" => "user_profile" }.freeze

Instance Attribute Summary

Attributes inherited from Base

#cancel_token, #read_tracker, #stream_chunk

Instance Method Summary collapse

Methods inherited from Base

#cancellation_requested?, #config_key, #emit_chunk, #risky?, #to_tool_definition, workspace_root, workspace_roots

Constructor Details

#initialize(backend: nil) ⇒ MemoryTool

Returns a new instance of MemoryTool.



24
25
26
# File 'lib/rubino/tools/memory_tool.rb', line 24

def initialize(backend: nil)
  @backend = backend
end

Instance Method Details

#call(arguments) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rubino/tools/memory_tool.rb', line 86

def call(arguments)
  args = symbolize(arguments)
  action = args[:action].to_s
  target = args[:target].to_s

  return error("invalid action '#{action}'; expected one of #{VALID_ACTIONS.join(", ")}") \
    unless VALID_ACTIONS.include?(action)
  return error("invalid target '#{target}'; expected one of #{VALID_TARGETS.join(", ")}") \
    unless VALID_TARGETS.include?(target)

  kind = TARGET_TO_KIND.fetch(target)

  case action
  when "add"     then do_add(kind, args[:content])
  when "replace" then do_replace(kind, args[:old_text], args[:content])
  when "remove"  then do_remove(kind, args[:old_text])
  end
end

#descriptionObject



32
33
34
35
36
37
38
39
40
41
# File 'lib/rubino/tools/memory_tool.rb', line 32

def description
  "Persist facts across sessions. Use action=add to record a new fact, " \
    "replace to update an existing fact (substring match on old_text), " \
    "or remove to delete one. target=user writes to the user profile; " \
    "target=memory writes to general memory. " \
    "Store ONE atomic fact per call — make separate calls for separate " \
    "facts so each can be superseded or forgotten independently. " \
    "Content is scanned for prompt-injection / exfiltration patterns and " \
    "subject to a character budget — refusals are reported in the output."
end

#input_schemaObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/rubino/tools/memory_tool.rb', line 43

def input_schema
  {
    type: "object",
    properties: {
      action: {
        type: "string",
        enum: VALID_ACTIONS,
        description: "add, replace, or remove"
      },
      target: {
        type: "string",
        enum: VALID_TARGETS,
        description: "memory (general) or user (user profile)"
      },
      content: {
        type: "string",
        description: "New content (required for add and replace)"
      },
      old_text: {
        type: "string",
        description: "Substring of existing memory to match " \
                     "(required for replace and remove)"
      }
    },
    required: %w[action target]
  }
end

#nameObject



28
29
30
# File 'lib/rubino/tools/memory_tool.rb', line 28

def name
  "memory"
end

#risk_levelObject



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/rubino/tools/memory_tool.rb', line 71

def risk_level
  # Memory store/retrieve/update is an internal, low-risk operation:
  # an autonomous "scratchpad" the agent maintains, not an external
  # side-effect like editing the user's files or running a shell
  # command. It must not trip the approval gate. Every write is
  # already threat-scanned and char-budgeted inside Memory::Store,
  # and the only destructive action (remove) deletes a SINGLE entry
  # by substring match — there is no full-wipe op exposed here — so
  # there is nothing left for an approval prompt to guard.
  # :low keeps it autonomous even under approvals.mode: manual
  # (Base#risky? only flags :medium/:high), matching how todo_tool
  # and other internal state-mutating tools stay unprompted.
  :low
end