pikuri-core 0.0.7 → 0.1.0
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/README.md +1 -1
- data/lib/pikuri/agent/chat_transport.rb +73 -93
- data/lib/pikuri/agent/configurator.rb +46 -106
- data/lib/pikuri/agent/context_window_detector.rb +44 -85
- data/lib/pikuri/agent/control/cancellable.rb +87 -66
- data/lib/pikuri/agent/control/interloper.rb +127 -105
- data/lib/pikuri/agent/control/step_limit.rb +25 -41
- data/lib/pikuri/agent/control.rb +14 -34
- data/lib/pikuri/agent/event.rb +123 -188
- data/lib/pikuri/agent/extension.rb +118 -94
- data/lib/pikuri/agent/extension_context.rb +50 -77
- data/lib/pikuri/agent/history.rb +653 -0
- data/lib/pikuri/agent/listener/rate_limited.rb +40 -66
- data/lib/pikuri/agent/listener/terminal.rb +143 -117
- data/lib/pikuri/agent/listener/token_log.rb +101 -140
- data/lib/pikuri/agent/listener.rb +23 -43
- data/lib/pikuri/agent/listener_list.rb +26 -47
- data/lib/pikuri/agent/synthesizer.rb +45 -87
- data/lib/pikuri/agent.rb +816 -474
- data/lib/pikuri/bundler_env.rb +68 -0
- data/lib/pikuri/extractor/html.rb +63 -110
- data/lib/pikuri/extractor/passthrough.rb +20 -30
- data/lib/pikuri/extractor.rb +93 -154
- data/lib/pikuri/file_type.rb +63 -135
- data/lib/pikuri/finalizers.rb +32 -47
- data/lib/pikuri/paths.rb +104 -13
- data/lib/pikuri/ruby_llm_patches.rb +106 -0
- data/lib/pikuri/sanitizer.rb +45 -67
- data/lib/pikuri/subprocess.rb +75 -119
- data/lib/pikuri/testing.rb +296 -0
- data/lib/pikuri/tool/calculator.rb +56 -66
- data/lib/pikuri/tool/execute_context.rb +42 -0
- data/lib/pikuri/tool/fetch.rb +51 -77
- data/lib/pikuri/tool/parameters.rb +21 -29
- data/lib/pikuri/tool/scraper.rb +55 -97
- data/lib/pikuri/tool/search/brave.rb +52 -80
- data/lib/pikuri/tool/search/duckduckgo.rb +59 -91
- data/lib/pikuri/tool/search/engines.rb +230 -97
- data/lib/pikuri/tool/search/exa.rb +56 -90
- data/lib/pikuri/tool/search/rate_limiter.rb +61 -38
- data/lib/pikuri/tool/search/result.rb +10 -15
- data/lib/pikuri/tool/trifecta_legs.rb +217 -0
- data/lib/pikuri/tool/web_scrape.rb +38 -54
- data/lib/pikuri/tool/web_search.rb +100 -24
- data/lib/pikuri/tool.rb +140 -65
- data/lib/pikuri/trifecta/contribution.rb +43 -0
- data/lib/pikuri/trifecta/node.rb +47 -0
- data/lib/pikuri/trifecta/report.rb +230 -0
- data/lib/pikuri/trifecta.rb +127 -0
- data/lib/pikuri/url_cache.rb +33 -49
- data/lib/pikuri/version.rb +1 -1
- data/lib/pikuri-core.rb +72 -88
- data/prompts/agent-loop.txt +5 -0
- data/prompts/pikuri-chat.txt +3 -12
- metadata +14 -3
|
@@ -3,48 +3,37 @@
|
|
|
3
3
|
module Pikuri
|
|
4
4
|
class Agent
|
|
5
5
|
# Capability facade handed to {Extension#bind} and
|
|
6
|
-
# {Extension#on_user_message} — the runtime counterpart of
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# agent
|
|
11
|
-
# stream, registering raw per-agent tools, and deriving sub-agent
|
|
12
|
-
# listener lists.
|
|
6
|
+
# {Extension#on_user_message} — the runtime counterpart of {Configurator}.
|
|
7
|
+
# Where the Configurator collects *build-time* declarations, this grants
|
|
8
|
+
# the *runtime* capabilities an extension needs once the agent is wired:
|
|
9
|
+
# emitting domain events, registering raw per-agent tools, deriving
|
|
10
|
+
# sub-agent listener lists.
|
|
13
11
|
#
|
|
14
12
|
# == Why a handed object, not a getter on Agent
|
|
15
13
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# extension's +bind+ received). Capabilities flow by explicit
|
|
26
|
-
# handoff, never by fetching from a globally reachable object.
|
|
27
|
-
#
|
|
28
|
-
# The usual Ruby caveat applies: nothing here is mechanically
|
|
29
|
-
# sealed (+instance_variable_get+ exists). The boundary is the
|
|
30
|
-
# API contract — same as every seam in CLAUDE.md.
|
|
14
|
+
# {Agent} exposes NO public path to these capabilities (no +listeners+ /
|
|
15
|
+
# +chat+ reader, no emit method): holding an agent grants read access to
|
|
16
|
+
# its config and nothing more. The only way to obtain this object is to be
|
|
17
|
+
# an {Extension} receiving +bind+ / +on_user_message+ — or to be handed it
|
|
18
|
+
# onward by one ({Pikuri::SubAgent::SubAgentTool} and
|
|
19
|
+
# {Pikuri::Mcp::Servers::Connect} both capture the context their +bind+
|
|
20
|
+
# received). Capabilities flow by explicit handoff, never by fetching from
|
|
21
|
+
# a globally reachable object. The boundary is the API contract, not a
|
|
22
|
+
# mechanical seal — same as every seam in CLAUDE.md.
|
|
31
23
|
#
|
|
32
24
|
# == Boundary rule
|
|
33
25
|
#
|
|
34
|
-
# Operations that *act on* the live agent's wiring live here
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# add capabilities to Agent.
|
|
26
|
+
# Operations that *act on* the live agent's wiring live here; passive
|
|
27
|
+
# readers of constructor-given config (+transport+, +id+, +tools+, ...)
|
|
28
|
+
# stay on {Agent}, reachable via {#agent}. Don't move readers in; don't add
|
|
29
|
+
# capabilities to Agent.
|
|
39
30
|
#
|
|
40
31
|
# == Audit
|
|
41
32
|
#
|
|
42
|
-
# One context per agent,
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# +grep -rn 'emit_event\|add_raw_tool\|sub_agent_listeners'
|
|
47
|
-
# pikuri-*/lib/+.
|
|
33
|
+
# One context per agent, built by {Agent#initialize} before the +bind+
|
|
34
|
+
# sweep. {ListenerList#emit} has exactly two callers: {Agent} (loop
|
|
35
|
+
# narration) and this class (domain events). Capability-user roster:
|
|
36
|
+
# +grep -rn 'emit_event\|add_raw_tool\|sub_agent_listeners' pikuri-*/lib/+.
|
|
48
37
|
class ExtensionContext
|
|
49
38
|
# @param agent [Agent] the live, fully wired agent.
|
|
50
39
|
# @param chat [RubyLLM::Chat] the agent's underlying chat —
|
|
@@ -65,44 +54,33 @@ module Pikuri
|
|
|
65
54
|
# configuration (tools, transport, id, streaming, ...).
|
|
66
55
|
attr_reader :agent
|
|
67
56
|
|
|
68
|
-
# Emit a domain event onto the agent's listener stream.
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
# emit them here. Listeners must no-op on variants they don't
|
|
74
|
-
# recognize — {Listener::Base#on_event}'s default and
|
|
75
|
-
# +case+-fallthrough give that for free.
|
|
57
|
+
# Emit a domain event onto the agent's listener stream. Core {Event}
|
|
58
|
+
# variants narrate the loop ({Agent}-emitted); gems define their own
|
|
59
|
+
# (e.g. +Pikuri::Tasks::ListChanged+) and emit them here. Listeners must
|
|
60
|
+
# no-op on variants they don't recognize (the {Listener::Base#on_event}
|
|
61
|
+
# default gives that for free).
|
|
76
62
|
#
|
|
77
|
-
# Called on the agent's thread (typically
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
# cross-thread handoff snapshot/serialize inside +on_event+.
|
|
63
|
+
# Called on the agent's thread (typically inside a tool's +execute+,
|
|
64
|
+
# landing between {Event::ToolCall} and {Event::ToolResult}); listeners
|
|
65
|
+
# doing cross-thread handoff snapshot inside +on_event+.
|
|
81
66
|
#
|
|
82
|
-
# @param event [Object] an immutable event value (by
|
|
83
|
-
# convention a +Data+ instance).
|
|
67
|
+
# @param event [Object] an immutable event value (by convention a +Data+)
|
|
84
68
|
# @return [void]
|
|
85
69
|
def emit_event(event)
|
|
86
70
|
@listeners.emit(event)
|
|
87
71
|
nil
|
|
88
72
|
end
|
|
89
73
|
|
|
90
|
-
# Register a raw +RubyLLM::Tool+ subclass on the agent's
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# Intended callers: {Pikuri::Mcp::Servers} (MCP tools
|
|
97
|
-
# deliberately bypass — see IDEAS.md §"MCP tools bypass
|
|
98
|
-
# +Pikuri::Tool+ entirely") and
|
|
99
|
-
# {Pikuri::SubAgent::Extension} (the +agent+ tool must
|
|
100
|
-
# register after the parent's tool list is final).
|
|
74
|
+
# Register a raw +RubyLLM::Tool+ subclass on the agent's chat, bypassing
|
|
75
|
+
# the {Pikuri::Tool} strict-validation seam — hence "raw" (native tools
|
|
76
|
+
# go through {Configurator#add_tool} for {Tool::Parameters} validation).
|
|
77
|
+
# Callers: {Pikuri::Mcp::Servers} (MCP tools deliberately bypass) and
|
|
78
|
+
# {Pikuri::SubAgent::Extension} (the +agent+ tool registers after the
|
|
79
|
+
# parent's tool list is final).
|
|
101
80
|
#
|
|
102
|
-
# The
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# IDEAS.md §"Per-agent activation, no propagation".
|
|
81
|
+
# The tool does NOT enter {Agent#tools}, only the chat's list — so
|
|
82
|
+
# sub-agents can't snapshot it, which is the point: activation is strictly
|
|
83
|
+
# per-agent.
|
|
106
84
|
#
|
|
107
85
|
# @param ruby_llm_tool [Class] subclass of +RubyLLM::Tool+
|
|
108
86
|
# @return [void]
|
|
@@ -112,27 +90,22 @@ module Pikuri
|
|
|
112
90
|
end
|
|
113
91
|
|
|
114
92
|
# Derive a listener list for a spawned sub-agent via
|
|
115
|
-
# {ListenerList#for_sub_agent}. Sole
|
|
116
|
-
# {Pikuri::SubAgent::SubAgentTool}, once per spawn.
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
# *instances* where a listener opts to share by reference
|
|
120
|
-
# (stateful sinks like {Listener::InMemoryEventList}) — see
|
|
121
|
-
# {ListenerList#for_sub_agent} for the per-listener semantics.
|
|
93
|
+
# {ListenerList#for_sub_agent}. Sole caller:
|
|
94
|
+
# {Pikuri::SubAgent::SubAgentTool}, once per spawn. The derived list
|
|
95
|
+
# aliases the parent's listener *instances* where a listener opts to
|
|
96
|
+
# share by reference — see {ListenerList#for_sub_agent}.
|
|
122
97
|
#
|
|
123
|
-
# @param params [Hash{Symbol => Object}] forwarded to each
|
|
124
|
-
#
|
|
98
|
+
# @param params [Hash{Symbol => Object}] forwarded to each listener's
|
|
99
|
+
# +for_sub_agent+ hook (currently +id:+).
|
|
125
100
|
# @return [ListenerList]
|
|
126
101
|
def sub_agent_listeners(**params)
|
|
127
102
|
@listeners.for_sub_agent(**params)
|
|
128
103
|
end
|
|
129
104
|
|
|
130
|
-
# Register a handler called by {Agent#close}
|
|
131
|
-
# {Configurator#on_close}
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
# to this specific agent (e.g. +Pikuri::Memory::Extension+
|
|
135
|
-
# arms its recorder's bounded flush here).
|
|
105
|
+
# Register a handler called by {Agent#close} — symmetric to
|
|
106
|
+
# {Configurator#on_close} (LIFO, per-handler rescue, idempotent) but
|
|
107
|
+
# available post-construction, so an {Extension}'s +bind+ can install
|
|
108
|
+
# per-agent cleanup (Memory arms its recorder's bounded flush here).
|
|
136
109
|
#
|
|
137
110
|
# @yield called with no arguments at close time
|
|
138
111
|
# @return [void]
|