Class: Pikuri::Tasks::List
- Inherits:
-
Object
- Object
- Pikuri::Tasks::List
- Defined in:
- lib/pikuri/tasks/list.rb
Overview
An in-memory ordered list of Items, scoped to a single Agent. Held inside Extension and captured by closure into the execute block of each of the four task tool classes, so every mutation hits the same instance.
Concurrency
The agent loop is single-threaded with respect to tool calls (ruby_llm dispatches them sequentially), so no locking. A future parallel-tool-execution feature would need a Mutex here.
Persistence
None. The list is dropped when the Agent is garbage-collected. That matches the gem’s stated scope: “in-memory only, no session-state-on-disk.”
Instance Method Summary collapse
-
#add(content) ⇒ Item
Append a new item with status
pending. -
#delete(content) ⇒ Item
Remove the item whose
contentmatches. - #empty? ⇒ Boolean
- #initialize ⇒ List constructor
-
#items ⇒ Array<Item>
A frozen snapshot of the current items, in insertion order.
-
#render ⇒ String
The canonical rendering returned as the observation by every task tool, so the LLM sees the latest full state on each call without needing a separate read tool.
-
#set_status(content:, status:) ⇒ Item
Update the status of the item whose
contentmatches. - #size ⇒ Integer
Constructor Details
#initialize ⇒ List
49 50 51 |
# File 'lib/pikuri/tasks/list.rb', line 49 def initialize @items = [] end |
Instance Method Details
#add(content) ⇒ Item
Append a new item with status pending. Content matching is exact (no case- or whitespace-folding) since the tools quote the content back to the LLM as the identifier.
79 80 81 82 83 84 85 |
# File 'lib/pikuri/tasks/list.rb', line 79 def add(content) raise DuplicateItem, content if find(content) item = Item.new(content: content, status: 'pending') @items << item item end |
#delete(content) ⇒ Item
Remove the item whose content matches.
112 113 114 115 116 117 |
# File 'lib/pikuri/tasks/list.rb', line 112 def delete(content) idx = @items.index { |i| i.content == content } raise ItemNotFound, content if idx.nil? @items.delete_at(idx) end |
#empty? ⇒ Boolean
66 67 68 |
# File 'lib/pikuri/tasks/list.rb', line 66 def empty? @items.empty? end |
#items ⇒ Array<Item>
Returns a frozen snapshot of the current items, in insertion order. Callers cannot mutate the internal storage through this accessor.
56 57 58 |
# File 'lib/pikuri/tasks/list.rb', line 56 def items @items.dup.freeze end |
#render ⇒ String
The canonical rendering returned as the observation by every task tool, so the LLM sees the latest full state on each call without needing a separate read tool. Format:
<tasks>
- [pending] Add dark mode toggle
- [in_progress] Write unit tests
- [completed] Update README
</tasks>
Empty list renders as <tasks>(empty)</tasks> so the LLM gets an unambiguous “yes, the call worked and the list is now empty” signal rather than an ambiguous blank block.
134 135 136 137 138 139 |
# File 'lib/pikuri/tasks/list.rb', line 134 def render return '<tasks>(empty)</tasks>' if @items.empty? lines = @items.map { |i| "- [#{i.status}] #{i.content}" } "<tasks>\n#{lines.join("\n")}\n</tasks>" end |
#set_status(content:, status:) ⇒ Item
Update the status of the item whose content matches.
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/pikuri/tasks/list.rb', line 95 def set_status(content:, status:) unless STATUSES.include?(status) raise ArgumentError, "invalid status: #{status.inspect} (allowed: #{STATUSES.join(', ')})" end idx = @items.index { |i| i.content == content } raise ItemNotFound, content if idx.nil? @items[idx] = Item.new(content: content, status: status) @items[idx] end |
#size ⇒ Integer
61 62 63 |
# File 'lib/pikuri/tasks/list.rb', line 61 def size @items.size end |