samagotchi 0.152.0 → 0.154.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/CHANGELOG.md +45 -1
- data/README.md +2 -3
- data/bin/chi +23 -24
- data/docs/architecture.md +30 -33
- data/docs/broadcast.md +5 -5
- data/docs/bundle-authoring.md +1 -1
- data/docs/bundles.md +93 -12
- data/docs/cli.md +83 -99
- data/docs/configuration.md +46 -40
- data/docs/context.md +3 -3
- data/docs/desktop.md +1 -1
- data/docs/guardrails.md +15 -15
- data/docs/hooks.md +13 -12
- data/docs/internals/anthropic.md +3 -3
- data/docs/internals/attached-context.md +5 -2
- data/docs/internals/background-tasks.md +3 -4
- data/docs/internals/block-renderers.md +4 -0
- data/docs/internals/bridge.md +3 -3
- data/docs/internals/context-telemetry.md +1 -1
- data/docs/internals/engine.md +42 -12
- data/docs/internals/objectives.md +6 -5
- data/docs/internals/participants.md +8 -8
- data/docs/internals/prompt-caching.md +8 -4
- data/docs/internals/remote-sessions.md +7 -7
- data/docs/internals/web-frontend.md +20 -17
- data/docs/internals/web-push.md +1 -1
- data/docs/memory.md +2 -2
- data/docs/plugins.md +52 -43
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +25 -25
- data/docs/sub-agent.md +2 -2
- data/docs/system-one.md +1 -1
- data/docs/testing.md +18 -8
- data/lib/samagotchi/answer_text.rb +34 -0
- data/lib/samagotchi/bridge.rb +43 -32
- data/lib/samagotchi/bridge_client.rb +10 -9
- data/lib/samagotchi/broadcast/recipients.rb +2 -2
- data/lib/samagotchi/broadcast/triage.rb +1 -1
- data/lib/samagotchi/broadcast_command.rb +5 -5
- data/lib/samagotchi/bundle_command/try/command.rb +1 -1
- data/lib/samagotchi/bundle_command/try/run.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +2 -2
- data/lib/samagotchi/bundles/decision-card/manifest.yml +4 -4
- data/lib/samagotchi/bundles/decision-card/plugin.rb +1 -9
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +3 -4
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/bundles/web-search/manifest.yml +13 -0
- data/lib/samagotchi/bundles/web-search/plugin.rb +174 -0
- data/lib/samagotchi/child_move.rb +3 -3
- data/lib/samagotchi/commands/registry.rb +6 -6
- data/lib/samagotchi/composer_route.rb +4 -5
- data/lib/samagotchi/config.rb +14 -12
- data/lib/samagotchi/context_candidate_choice.rb +1 -1
- data/lib/samagotchi/context_candidates.rb +4 -3
- data/lib/samagotchi/continue_offer.rb +1 -1
- data/lib/samagotchi/debug_log.rb +1 -1
- data/lib/samagotchi/degrade.rb +75 -0
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +2 -2
- data/lib/samagotchi/engine/cards.rb +87 -0
- data/lib/samagotchi/engine/context_judge.rb +138 -0
- data/lib/samagotchi/engine/events.rb +131 -0
- data/lib/samagotchi/engine/guardrails.rb +87 -0
- data/lib/samagotchi/engine/hooks.rb +119 -0
- data/lib/samagotchi/engine/messages.rb +124 -0
- data/lib/samagotchi/engine/model_settings.rb +127 -0
- data/lib/samagotchi/engine/models.rb +261 -0
- data/lib/samagotchi/engine/objectives.rb +116 -0
- data/lib/samagotchi/engine/participants.rb +86 -0
- data/lib/samagotchi/engine/plugins.rb +208 -0
- data/lib/samagotchi/engine/questions.rb +98 -0
- data/lib/samagotchi/engine/recap.rb +130 -0
- data/lib/samagotchi/engine/session.rb +131 -0
- data/lib/samagotchi/engine/side_requests.rb +135 -0
- data/lib/samagotchi/engine/steering.rb +134 -0
- data/lib/samagotchi/engine/toolkit.rb +269 -0
- data/lib/samagotchi/engine/turn_end.rb +180 -0
- data/lib/samagotchi/engine/turn_helpers.rb +160 -0
- data/lib/samagotchi/engine/verify.rb +153 -0
- data/lib/samagotchi/engine.rb +63 -2642
- data/lib/samagotchi/grants.rb +575 -0
- data/lib/samagotchi/guardrail_wiring.rb +2 -2
- data/lib/samagotchi/guardrails/context.rb +2 -3
- data/lib/samagotchi/guardrails/parent_approvals.rb +2 -2
- data/lib/samagotchi/guardrails/parent_policy.rb +2 -2
- data/lib/samagotchi/guardrails/review_boundary.rb +4 -3
- data/lib/samagotchi/hook_routes.rb +3 -3
- data/lib/samagotchi/idle_recap.rb +4 -5
- data/lib/samagotchi/idle_reminders.rb +3 -4
- data/lib/samagotchi/iteration_limit.rb +2 -2
- data/lib/samagotchi/kernel_loop.rb +10 -11
- data/lib/samagotchi/launch_mode.rb +13 -32
- data/lib/samagotchi/level_setting_command.rb +3 -2
- data/lib/samagotchi/llm_context_command.rb +3 -2
- data/lib/samagotchi/llm_context_stale.rb +2 -2
- data/lib/samagotchi/log.rb +7 -7
- data/lib/samagotchi/log_line.rb +1 -1
- data/lib/samagotchi/log_path.rb +1 -1
- data/lib/samagotchi/message_parts.rb +3 -2
- data/lib/samagotchi/note_command.rb +1 -1
- data/lib/samagotchi/note_delivery.rb +18 -8
- data/lib/samagotchi/objectives.rb +24 -3
- data/lib/samagotchi/objectives_check.rb +8 -27
- data/lib/samagotchi/objectives_command.rb +4 -69
- data/lib/samagotchi/objectives_edit.rb +87 -0
- data/lib/samagotchi/output_formatter.rb +7 -1
- data/lib/samagotchi/owner_lock.rb +7 -1
- data/lib/samagotchi/parent_answers_command.rb +5 -4
- data/lib/samagotchi/participant_exposure.rb +42 -0
- data/lib/samagotchi/participant_mode.rb +1 -1
- data/lib/samagotchi/participant_name.rb +1 -1
- data/lib/samagotchi/participants.rb +17 -17
- data/lib/samagotchi/participants_watch.rb +1 -1
- data/lib/samagotchi/pending_input_queue.rb +2 -2
- data/lib/samagotchi/plugin/api.rb +3 -2
- data/lib/samagotchi/plugin/context.rb +21 -7
- data/lib/samagotchi/plugin/peer.rb +2 -1
- data/lib/samagotchi/plugin/sessions.rb +6 -4
- data/lib/samagotchi/plugin_tasks.rb +1 -2
- data/lib/samagotchi/private_files.rb +29 -0
- data/lib/samagotchi/project_env.rb +2 -2
- data/lib/samagotchi/question_desk.rb +8 -7
- data/lib/samagotchi/recap_command.rb +2 -1
- data/lib/samagotchi/recap_setup.rb +1 -1
- data/lib/samagotchi/recap_store.rb +1 -1
- data/lib/samagotchi/redact.rb +86 -0
- data/lib/samagotchi/reminder_queue.rb +2 -2
- data/lib/samagotchi/remote_attach_command.rb +1 -1
- data/lib/samagotchi/reply_wait/poll.rb +2 -2
- data/lib/samagotchi/review_start.rb +1 -1
- data/lib/samagotchi/run_options.rb +6 -6
- data/lib/samagotchi/self_report.rb +42 -39
- data/lib/samagotchi/send_command/busy.rb +1 -1
- data/lib/samagotchi/send_command.rb +4 -1
- data/lib/samagotchi/session.rb +11 -2
- data/lib/samagotchi/session_access/contract.rb +3 -3
- data/lib/samagotchi/session_access/local.rb +9 -9
- data/lib/samagotchi/session_access/remote.rb +17 -7
- data/lib/samagotchi/session_access.rb +4 -4
- data/lib/samagotchi/session_archive_command.rb +2 -2
- data/lib/samagotchi/session_chain.rb +8 -6
- data/lib/samagotchi/session_commands.rb +45 -48
- data/lib/samagotchi/session_continue.rb +1 -1
- data/lib/samagotchi/session_delete_command.rb +4 -4
- data/lib/samagotchi/session_manager.rb +59 -36
- data/lib/samagotchi/session_metrics.rb +2 -2
- data/lib/samagotchi/session_model_settings.rb +3 -2
- data/lib/samagotchi/session_participants.rb +1 -1
- data/lib/samagotchi/session_retention.rb +3 -3
- data/lib/samagotchi/sessions_command.rb +4 -4
- data/lib/samagotchi/share_command.rb +10 -10
- data/lib/samagotchi/steer.rb +1 -1
- data/lib/samagotchi/system_prompt.rb +102 -109
- data/lib/samagotchi/task_watch.rb +1 -1
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +72 -10
- data/lib/samagotchi/terminal_ui/attached_loop/composer.rb +12 -9
- data/lib/samagotchi/terminal_ui/attached_loop/exit_commands.rb +16 -0
- data/lib/samagotchi/terminal_ui/attached_loop/questions.rb +2 -3
- data/lib/samagotchi/terminal_ui/attached_loop.rb +17 -8
- data/lib/samagotchi/terminal_ui/attached_view.rb +2 -3
- data/lib/samagotchi/terminal_ui/event_renderer.rb +3 -5
- data/lib/samagotchi/terminal_ui/formatting.rb +7 -7
- data/lib/samagotchi/terminal_ui/input_support.rb +1 -7
- data/lib/samagotchi/terminal_ui/line_reader.rb +2 -2
- data/lib/samagotchi/terminal_ui/live_region.rb +2 -2
- data/lib/samagotchi/terminal_ui/log_tail.rb +135 -0
- data/lib/samagotchi/terminal_ui/one_shot_sink.rb +2 -2
- data/lib/samagotchi/terminal_ui/plain_surface.rb +1 -1
- data/lib/samagotchi/terminal_ui/question_prompt.rb +4 -5
- data/lib/samagotchi/terminal_ui/reline_seam.rb +3 -3
- data/lib/samagotchi/terminal_ui/status_row.rb +7 -8
- data/lib/samagotchi/terminal_ui.rb +142 -1387
- data/lib/samagotchi/title_command.rb +2 -2
- data/lib/samagotchi/tool_runner.rb +14 -16
- data/lib/samagotchi/toolkit.rb +8 -5
- data/lib/samagotchi/toolkit_binding.rb +30 -5
- data/lib/samagotchi/toolkit_command.rb +2 -1
- data/lib/samagotchi/toolkit_report_command.rb +9 -5
- data/lib/samagotchi/toolkit_suggest.rb +12 -6
- data/lib/samagotchi/tools/builtin_calls.rb +14 -10
- data/lib/samagotchi/tools/deferred_tools.rb +2 -2
- data/lib/samagotchi/tools/delegate.rb +3 -1
- data/lib/samagotchi/tools/delegate_cursor.rb +3 -3
- data/lib/samagotchi/tools/delegate_wait.rb +1 -1
- data/lib/samagotchi/tools/objectives_tool.rb +2 -2
- data/lib/samagotchi/tools/registry.rb +44 -20
- data/lib/samagotchi/tools/send_note.rb +2 -1
- data/lib/samagotchi/tools/tool_index.rb +27 -21
- data/lib/samagotchi/turn_endings.rb +2 -2
- data/lib/samagotchi/turn_flow.rb +4 -5
- data/lib/samagotchi/update_hint.rb +2 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +39 -50
- data/lib/samagotchi/web/participant_access.rb +2 -2
- data/lib/samagotchi/web/participant_review.rb +5 -5
- data/lib/samagotchi/web/participant_stream_filter.rb +8 -8
- data/lib/samagotchi/web/participant_view.rb +2 -82
- data/lib/samagotchi/web/participants_admin.rb +1 -1
- data/lib/samagotchi/web/public/app.js +61 -357
- data/lib/samagotchi/web/public/block_host.js +35 -8
- data/lib/samagotchi/web/public/block_renderers.css +9 -5
- data/lib/samagotchi/web/public/chains.js +2 -2
- data/lib/samagotchi/web/public/claim_link.js +4 -4
- data/lib/samagotchi/web/public/composer_mode.js +110 -0
- data/lib/samagotchi/web/public/format.js +3 -3
- data/lib/samagotchi/web/public/owner_claim.js +81 -0
- data/lib/samagotchi/web/public/owner_verify.js +112 -0
- data/lib/samagotchi/web/public/session_fields.js +3 -3
- data/lib/samagotchi/web/public/strip_grip.js +3 -3
- data/lib/samagotchi/web/public/strip_hidden.js +65 -0
- data/lib/samagotchi/web/public/version_notices.js +132 -0
- data/lib/samagotchi/web/push/store.rb +1 -1
- data/lib/samagotchi/web/session_access_routes.rb +5 -4
- data/lib/samagotchi/web/session_control.rb +12 -8
- data/lib/samagotchi/web/session_create.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +2 -2
- data/lib/samagotchi/web/session_show.rb +2 -2
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +106 -9
- data/lib/samagotchi/worker_link.rb +3 -3
- data/lib/samagotchi/working_set_command.rb +3 -2
- metadata +36 -6
- data/lib/samagotchi/terminal_ui/between_turns.rb +0 -138
- data/lib/samagotchi/terminal_ui/repl_input.rb +0 -152
- data/lib/samagotchi/web/grants.rb +0 -575
- data/lib/samagotchi/web/participant_exposure.rb +0 -44
- data/lib/samagotchi/web/private_files.rb +0 -31
|
@@ -9,7 +9,7 @@ module Samagotchi
|
|
|
9
9
|
# `/title --clear` drops it, and the lists show the first message again.
|
|
10
10
|
# Anyone who may send the session a command may name it (a parent agent
|
|
11
11
|
# its delegates too). It doesn't save: its host saves a changed result at
|
|
12
|
-
# once (Worker
|
|
12
|
+
# once (Worker). It runs mid-turn as well (anytime): the
|
|
13
13
|
# worker's save takes the session's lock and writes a running turn's
|
|
14
14
|
# checkpoint, as a turn's own saves do.
|
|
15
15
|
class TitleCommand
|
|
@@ -28,7 +28,7 @@ module Samagotchi
|
|
|
28
28
|
# @return [Array(String, Boolean, Symbol)] the reply, whether the
|
|
29
29
|
# title changed, and :ok or :error
|
|
30
30
|
def run(args)
|
|
31
|
-
session = @engine.session or return [
|
|
31
|
+
session = @engine.session or return [Session::NONE_YET, false, :error]
|
|
32
32
|
text = args.to_s.gsub(/\s+/, " ").strip
|
|
33
33
|
return [listing(session), false, :ok] if text.empty?
|
|
34
34
|
# "--clear and more" is a slip, not a title.
|
|
@@ -6,6 +6,7 @@ require_relative "row_refs"
|
|
|
6
6
|
require_relative "file_ref"
|
|
7
7
|
require_relative "guardrails"
|
|
8
8
|
require_relative "vision_context"
|
|
9
|
+
require_relative "degrade"
|
|
9
10
|
require_relative "log"
|
|
10
11
|
require_relative "edit_preview"
|
|
11
12
|
require_relative "tool_output_cut"
|
|
@@ -234,12 +235,12 @@ module Samagotchi
|
|
|
234
235
|
# default isn't a window) or can't be read. It checks this result, not the
|
|
235
236
|
# context already used.
|
|
236
237
|
def window_guard
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
Degrade.call(:memory, "read_window_unknown") do
|
|
239
|
+
window = @window ? @window.call : (@kernel.tool_output_window if @kernel.respond_to?(:tool_output_window))
|
|
240
|
+
return nil unless window&.tokens&.positive? && window.source != :default
|
|
239
241
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
nil
|
|
242
|
+
(window.tokens * MemoryBundle::IndexSize.chars_per_token / 2).floor
|
|
243
|
+
end
|
|
243
244
|
end
|
|
244
245
|
|
|
245
246
|
# memory_read's cut line: the rest is in the memory's file, in the dir(s)
|
|
@@ -307,12 +308,12 @@ module Samagotchi
|
|
|
307
308
|
# (MemoryBundle::IndexSize), nil for any other path, or when
|
|
308
309
|
# memory.index_warn_tokens is off: other writes cost nothing.
|
|
309
310
|
def memory_index_size(call)
|
|
310
|
-
|
|
311
|
-
|
|
311
|
+
Degrade.call(:memory, "index_size_unreadable") do
|
|
312
|
+
scope = MemoryBundle::IndexSync.memory_scope(Tools::ToolPath.normalize(call[:path]))
|
|
313
|
+
return nil unless scope && MemoryBundle::IndexSize.warn_limit.positive?
|
|
312
314
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
nil
|
|
315
|
+
MemoryBundle::IndexSize.measure(scope)
|
|
316
|
+
end
|
|
316
317
|
end
|
|
317
318
|
|
|
318
319
|
# +result+ with the note memory_write gives when the refreshed index
|
|
@@ -556,15 +557,12 @@ module Samagotchi
|
|
|
556
557
|
|
|
557
558
|
# A failing hook must not break the turn.
|
|
558
559
|
def fire(name, event)
|
|
559
|
-
@kernel.hooks&.fire(name, event)
|
|
560
|
-
rescue StandardError
|
|
561
|
-
nil
|
|
560
|
+
Degrade.call(:hooks, "tool_hook_fire_failed", hook: name) { @kernel.hooks&.fire(name, event) }
|
|
562
561
|
end
|
|
563
562
|
|
|
563
|
+
# A failing stream callback (a UI gone) must not break the call either.
|
|
564
564
|
def emit(callback, event)
|
|
565
|
-
callback&.call(event)
|
|
566
|
-
rescue StandardError
|
|
567
|
-
nil
|
|
565
|
+
Degrade.call(:turn, "tool_event_callback_failed", type: (event[:type] if event.is_a?(Hash))) { callback&.call(event) }
|
|
568
566
|
end
|
|
569
567
|
end
|
|
570
568
|
end
|
data/lib/samagotchi/toolkit.rb
CHANGED
|
@@ -249,10 +249,13 @@ module Samagotchi
|
|
|
249
249
|
Kit.new(name: REVIEW, tools: REVIEW_TOOLS, groups: [])
|
|
250
250
|
].to_h { |kit| [kit.name, kit] }.freeze
|
|
251
251
|
|
|
252
|
-
# What a toolkit declares, the Registry's filter: every
|
|
253
|
-
#
|
|
254
|
-
# an LLM context layer gates
|
|
255
|
-
# +added+ declares any registered
|
|
252
|
+
# What a toolkit declares, the Registry's filter: every registered tool,
|
|
253
|
+
# built-ins and plugin tools (chi.tool) alike (+all_core+, full), or the
|
|
254
|
+
# built-ins in +core+, and every tool an LLM context layer gates
|
|
255
|
+
# (forget_outputs), but for +removed+; +added+ declares any registered
|
|
256
|
+
# tool. A tool it leaves out without removing is reachable through the
|
|
257
|
+
# tool index (Registry#reachable_names). The index's own tools
|
|
258
|
+
# (find_tools, call_tool) are the Registry's to declare. +fallback+: why the session's
|
|
256
259
|
# own toolkit couldn't be resolved (this is FULL then), nil when it was.
|
|
257
260
|
# +prompt+: the kit's prompt text (its own or its base's), nil without.
|
|
258
261
|
# +groups+: :all or the sorted source group names/globs it reaches.
|
|
@@ -272,7 +275,7 @@ module Samagotchi
|
|
|
272
275
|
# @param entry [Tools::Registry::Entry]
|
|
273
276
|
def declares?(entry)
|
|
274
277
|
return false if removed.include?(entry.name)
|
|
275
|
-
return true if added.include?(entry.name) || !entry.
|
|
278
|
+
return true if added.include?(entry.name) || !entry.layer.nil?
|
|
276
279
|
|
|
277
280
|
all_core || core.include?(entry.name)
|
|
278
281
|
end
|
|
@@ -38,6 +38,8 @@ module Samagotchi
|
|
|
38
38
|
# config ones (#kits); Plugin::Registries appends to it.
|
|
39
39
|
@plugin_kits = []
|
|
40
40
|
@warned = []
|
|
41
|
+
# The tools #warn_stranded said once.
|
|
42
|
+
@stranded = []
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
# @return [Array<Toolkit::Kit>] the plugins' kits (Plugin::Registries' toolkits:)
|
|
@@ -192,13 +194,36 @@ module Samagotchi
|
|
|
192
194
|
resolved = nil if resolved.everything?
|
|
193
195
|
tools = @tools.call
|
|
194
196
|
tools.reaching_kit = ->(group) { reaching_kit(group) }
|
|
195
|
-
|
|
197
|
+
changed = resolved != tools.toolkit
|
|
198
|
+
if changed
|
|
199
|
+
tools.toolkit = resolved
|
|
200
|
+
@refresh_index.call
|
|
201
|
+
@reset_prompt.call
|
|
202
|
+
end
|
|
203
|
+
warn_stranded(tools)
|
|
204
|
+
changed
|
|
205
|
+
end
|
|
196
206
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
207
|
+
# "web_x isn't declared on toolkit lean, and call_tool can't reach it
|
|
208
|
+
# this session: it arrived after the session's tools were set ·
|
|
209
|
+
# /toolkit add web_x declares it"
|
|
210
|
+
def self.stranded_notice(tool, toolkit)
|
|
211
|
+
"#{tool} isn't declared on toolkit #{toolkit}, and call_tool can't reach it this session: it arrived after " \
|
|
212
|
+
"the session's tools were set · /toolkit add #{tool} declares it"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# A notice (by its bundle, once per tool and worker) for each plugin
|
|
216
|
+
# tool that arrived mid-session on a kit that leaves it out without
|
|
217
|
+
# the tool index to reach it (Tools::Registry#stranded_names).
|
|
218
|
+
def warn_stranded(tools)
|
|
219
|
+
return unless tools.respond_to?(:stranded_names)
|
|
220
|
+
|
|
221
|
+
(tools.stranded_names - @stranded).each do |name|
|
|
222
|
+
@stranded << name
|
|
223
|
+
@notify.call(self.class.stranded_notice(name, tools.toolkit.name), :warn, tools[name]&.source.to_s, extra: {})
|
|
224
|
+
end
|
|
201
225
|
end
|
|
226
|
+
private :warn_stranded
|
|
202
227
|
|
|
203
228
|
# The first kit that resolves and reaches source group +group+, the
|
|
204
229
|
# user's own (config's, the plugins') before the built-ins, so a hint
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require_relative "cache_confirm"
|
|
5
5
|
require_relative "toolkit"
|
|
6
|
+
require_relative "session"
|
|
6
7
|
|
|
7
8
|
module Samagotchi
|
|
8
9
|
# /toolkit, the session's toolkit (Session#toolkit): with no argument,
|
|
@@ -40,7 +41,7 @@ module Samagotchi
|
|
|
40
41
|
words, yes = CacheConfirm.split(args, downcase: false)
|
|
41
42
|
return [listing(@engine.toolkit_explained), false, nil] if words.empty?
|
|
42
43
|
|
|
43
|
-
session = @engine.session or raise ArgumentError,
|
|
44
|
+
session = @engine.session or raise ArgumentError, Session::NONE_YET
|
|
44
45
|
before = @engine.toolkit_explained
|
|
45
46
|
after = @engine.toolkit_explained(changed(before.toolkit, words.split))
|
|
46
47
|
# Nothing the prompt carries changes (full +delegate on full): only
|
|
@@ -241,7 +241,9 @@ module Samagotchi
|
|
|
241
241
|
def grade(entry, kit, uses)
|
|
242
242
|
kits = entry["kits"].is_a?(Hash) ? entry["kits"] : {}
|
|
243
243
|
rules = kits[kit]
|
|
244
|
-
|
|
244
|
+
# The tools a kit's rules declare: the built-ins and the plugin tools
|
|
245
|
+
# (chi.tool) logged when the ask started (an older line has none).
|
|
246
|
+
core_tools = Array(entry["core_tools"]) | Array(entry["plugin_tools"])
|
|
245
247
|
return ["ungradable", " (no rules logged for #{kit})"] unless rules.is_a?(Hash)
|
|
246
248
|
return ["ungradable", " (tool calls not readable)"] if uses == :ungradable
|
|
247
249
|
|
|
@@ -259,8 +261,10 @@ module Samagotchi
|
|
|
259
261
|
end
|
|
260
262
|
|
|
261
263
|
# Whether a kit's rules declare +use+ (Toolkit::Resolved#declares?): a
|
|
262
|
-
# tool-index group by the kit's groups; a built-in
|
|
263
|
-
#
|
|
264
|
+
# tool-index group by the kit's groups; a logged built-in or plugin
|
|
265
|
+
# tool (+core_tools+) by the kit's list (all of them on full) and its
|
|
266
|
+
# adds; any other (the index's own, a layer's, one that registered
|
|
267
|
+
# after the ask) unless the kit removes it.
|
|
264
268
|
def covers?(rules, core_tools, use)
|
|
265
269
|
return group_reached?(rules["groups"], use.group) if use.group && use.group != "chi"
|
|
266
270
|
|
|
@@ -276,8 +280,8 @@ module Samagotchi
|
|
|
276
280
|
(declared & core_tools) - Array(rules["removed"])
|
|
277
281
|
end
|
|
278
282
|
|
|
279
|
-
# +other+ declares no more built-ins and
|
|
280
|
-
# +rules+, and less of one of them.
|
|
283
|
+
# +other+ declares no more logged tools (built-ins and plugin tools)
|
|
284
|
+
# and reaches no more groups than +rules+, and less of one of them.
|
|
281
285
|
def narrower?(other, rules, core_tools)
|
|
282
286
|
mine = core_set(rules, core_tools)
|
|
283
287
|
theirs = core_set(other, core_tools)
|
|
@@ -31,8 +31,13 @@ module Samagotchi
|
|
|
31
31
|
# A kit as the ask saw it: its name, classifier description and #rules.
|
|
32
32
|
Candidate = Data.define(:name, :description, :rules)
|
|
33
33
|
# +default+ is a Candidate (no description needed); +core_tools+ the
|
|
34
|
-
# built-ins registered when the ask started
|
|
35
|
-
|
|
34
|
+
# built-ins registered when the ask started, +plugin_tools+ the plugin
|
|
35
|
+
# tools (chi.tool) registered then: a kit declares both by its rules.
|
|
36
|
+
Snapshot = Data.define(:session_id, :turn_number, :default, :candidates, :core_tools, :plugin_tools, :state) do
|
|
37
|
+
def initialize(session_id:, turn_number:, default:, candidates:, core_tools:, state:, plugin_tools: [])
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
end
|
|
36
41
|
Suggestion = Data.define(:kit, :raw, :confidence, :decided)
|
|
37
42
|
# One ask's outcome: its Suggestion, what on mode would do with it
|
|
38
43
|
# (suggested, default, defer, below_threshold, invalid, no_judgment)
|
|
@@ -164,6 +169,7 @@ module Samagotchi
|
|
|
164
169
|
decided: !!decided,
|
|
165
170
|
would: would,
|
|
166
171
|
core_tools: snapshot.core_tools,
|
|
172
|
+
plugin_tools: snapshot.plugin_tools,
|
|
167
173
|
kits: [snapshot.default, *snapshot.candidates].to_h { |kit| [kit.name, kit.rules] }
|
|
168
174
|
}
|
|
169
175
|
Judged.new(suggestion: Suggestion.new(kit: selected, raw: raw, confidence: confidence, decided: !!decided),
|
|
@@ -319,10 +325,10 @@ module Samagotchi
|
|
|
319
325
|
end
|
|
320
326
|
|
|
321
327
|
# A resolved kit (Toolkit::Resolved) as frozen, JSON-ready rules, not
|
|
322
|
-
# a tool list: plugin and MCP tools may still be
|
|
323
|
-
# starts,
|
|
324
|
-
#
|
|
325
|
-
#
|
|
328
|
+
# a tool list: a plugin's replaced tools and MCP tools may still be
|
|
329
|
+
# loading when the ask starts, so the report expands these against
|
|
330
|
+
# the tools a turn really called (ToolkitReportCommand), the logged
|
|
331
|
+
# core_tools and plugin_tools by the same rule (Resolved#declares?).
|
|
326
332
|
# @return [Hash{String => Object}]
|
|
327
333
|
def self.rules(resolved)
|
|
328
334
|
{
|
|
@@ -149,20 +149,23 @@ module Samagotchi
|
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
# A saved call as it ran (a reload, the stale-read pairing): a call_tool
|
|
152
|
-
# of a built-in
|
|
153
|
-
#
|
|
154
|
-
# result) shows it ran as
|
|
155
|
-
# no lead, an error that names
|
|
156
|
-
#
|
|
157
|
-
#
|
|
152
|
+
# of a chi tool, a built-in or a plugin's ("chi/delegate",
|
|
153
|
+
# "chi/web_search", or a bare "delegate") is that tool's call with
|
|
154
|
+
# via: "call_tool" when +output+ (its saved result) shows it ran as
|
|
155
|
+
# that tool: its "[name]" lead, or, with no lead, an error that names
|
|
156
|
+
# it (refused by its name before the gate; a plugin tool's only by
|
|
157
|
+
# its chi/ name, the one no MCP tool has); any other call as it is (a
|
|
158
|
+
# call_tool that ran, refused or not, leads with "[call_tool]"). No
|
|
159
|
+
# registry is asked: a reload without the plugin reads the same.
|
|
158
160
|
# @param call [Hash] as #build gives it
|
|
159
161
|
# @param output [String, nil]
|
|
160
162
|
# @return [Hash]
|
|
161
163
|
def unwrap_saved(call, output)
|
|
162
164
|
return call unless call[:name] == VIA && call[:args].is_a?(Hash)
|
|
163
165
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
named = call[:args]["tool"].to_s.strip
|
|
167
|
+
name = named.delete_prefix(VIA_GROUP)
|
|
168
|
+
return call if name.empty? || name == VIA || !ran_as?(output.to_s, name, error: row(name) || named != name)
|
|
166
169
|
|
|
167
170
|
build(name, saved_args(call[:args]["args"])).merge(via: VIA)
|
|
168
171
|
end
|
|
@@ -183,9 +186,10 @@ module Samagotchi
|
|
|
183
186
|
build(VIA, { "tool" => name, "args" => call[:args] })
|
|
184
187
|
end
|
|
185
188
|
|
|
186
|
-
|
|
189
|
+
# +error+: whether a lead-less error that names the tool counts.
|
|
190
|
+
def ran_as?(output, name, error: true)
|
|
187
191
|
lead = output[SAVED_LEAD, 1]
|
|
188
|
-
lead ? lead == name : output.start_with?("Error: #{name} ")
|
|
192
|
+
lead ? lead == name : error && output.start_with?("Error: #{name} ")
|
|
189
193
|
end
|
|
190
194
|
|
|
191
195
|
# call_tool's inner args as saved: an object, or its JSON text.
|
|
@@ -9,8 +9,8 @@ module Samagotchi
|
|
|
9
9
|
# that loads tools (LLM::AnthropicMessages#loads_tools?): every tool the
|
|
10
10
|
# session reaches but doesn't declare, so a find_tools hit can load it
|
|
11
11
|
# and the model calls it by its own name. In order:
|
|
12
|
-
# 1. the built-ins the toolkit leaves out
|
|
13
|
-
# (Registry#reachable_names), in registry order;
|
|
12
|
+
# 1. the chi tools (built-ins and plugin tools) the toolkit leaves out
|
|
13
|
+
# but doesn't remove (Registry#reachable_names), in registry order;
|
|
14
14
|
# 2. the tool index's source tools (an MCP server's, by acts_as), only
|
|
15
15
|
# while call_tool is declared (ToolIndex#deferrable: sorted by name);
|
|
16
16
|
# 3. every other name the conversation loaded (a find result's
|
|
@@ -336,8 +336,10 @@ module Samagotchi
|
|
|
336
336
|
when :timeout then "Error: the worker of session #{id[0, 8]} did not answer in time, so the message was not sent"
|
|
337
337
|
else "Error: the message to session #{id[0, 8]} could not be written"
|
|
338
338
|
end
|
|
339
|
+
rescue SessionManager::ScratchSession => e
|
|
340
|
+
"Error: session #{id[0, 8]}: #{e.message}"
|
|
339
341
|
rescue SessionManager::OwnedByTUI
|
|
340
|
-
"Error: session #{id[0, 8]} is
|
|
342
|
+
"Error: session #{id[0, 8]} is #{OwnerLock::TUI_BUSY}, which can't take a delegated message"
|
|
341
343
|
rescue Session::AmbiguousId => e
|
|
342
344
|
"Error: #{e.message}"
|
|
343
345
|
end
|
|
@@ -67,9 +67,9 @@ module Samagotchi
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# The parent's cursors, one per child, in <parent dir>/delegates.json, so
|
|
70
|
-
# a respawned worker (or a
|
|
71
|
-
# model a reply it already had. Written by whoever owns the parent
|
|
72
|
-
# worker, or the
|
|
70
|
+
# a respawned worker (or a one-shot resuming the session) doesn't hand
|
|
71
|
+
# the model a reply it already had. Written by whoever owns the parent
|
|
72
|
+
# (its worker, or the one-shot its kernel runs in), and by a continue moving
|
|
73
73
|
# delegates to the chain's new link (ChildMove, from another process):
|
|
74
74
|
# a Mutex for the owner's turn thread vs its loop thread, and a flock on
|
|
75
75
|
# LOCK_FILE between processes; the file is replaced whole.
|
|
@@ -143,7 +143,7 @@ module Samagotchi
|
|
|
143
143
|
# "wake" (a delegate report, a turn of its own when idle), "queue" (a
|
|
144
144
|
# report at its next step or turn, no wake) or "off" (only
|
|
145
145
|
# delegate_result). Only a worker's session reads its children's rings
|
|
146
|
-
# (its Engine gives a relay); a
|
|
146
|
+
# (its Engine gives a relay); a -p --non-interactive run or a scratch session
|
|
147
147
|
# never gets a report.
|
|
148
148
|
# @param peers [Peers]
|
|
149
149
|
# @return [String] "wake", "queue" or "off"
|
|
@@ -64,8 +64,8 @@ module Samagotchi
|
|
|
64
64
|
|
|
65
65
|
def self.refusal(list, id, call)
|
|
66
66
|
item = list.find(id)
|
|
67
|
-
return "Error:
|
|
68
|
-
return "Error: #{id}
|
|
67
|
+
return "Error: #{Objectives.unknown(id, list)}. Nothing was recorded." unless item
|
|
68
|
+
return "Error: #{Objectives.dropped(id)}. Nothing was recorded." if item.status == Objectives::DROPPED
|
|
69
69
|
|
|
70
70
|
"Error: a move needs a note: what moved, in a line." if Objectives.clean(call[:note]).empty?
|
|
71
71
|
end
|
|
@@ -41,14 +41,16 @@ module Samagotchi
|
|
|
41
41
|
# What Tools::ToolIndex#declare registered: the entries of +source+
|
|
42
42
|
# are the index's search and call tools, +call_name+ the call tool's
|
|
43
43
|
# name, and +groups+ the source groups registered when the plugins
|
|
44
|
-
# loaded
|
|
44
|
+
# loaded; +loaded+ the entries registered then (nil: whatever is
|
|
45
|
+
# registered now), which alone decide whether the index's tools are
|
|
46
|
+
# needed (Registry#index_needed?). +deferrable+ (→ the sources'
|
|
45
47
|
# tools a host that loads tools may declare deferred) and +wrap+
|
|
46
48
|
# (call → the call_tool call a direct call of one of them runs as, or
|
|
47
49
|
# nil) are nil when nothing reaches them.
|
|
48
|
-
Index = Data.define(:source, :call_name, :groups, :deferrable, :wrap, :unreached) do
|
|
49
|
-
def initialize(source:, call_name:, groups:, deferrable: nil, wrap: nil, unreached: nil)
|
|
50
|
+
Index = Data.define(:source, :call_name, :groups, :deferrable, :wrap, :unreached, :loaded) do
|
|
51
|
+
def initialize(source:, call_name:, groups:, deferrable: nil, wrap: nil, unreached: nil, loaded: nil)
|
|
50
52
|
super(source: source, call_name: call_name, groups: Array(groups).map(&:to_s).uniq.freeze,
|
|
51
|
-
deferrable: deferrable, wrap: wrap, unreached: unreached)
|
|
53
|
+
deferrable: deferrable, wrap: wrap, unreached: unreached, loaded: loaded&.dup&.freeze)
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def unreached_tool(name) = unreached&.call(name)
|
|
@@ -116,18 +118,18 @@ module Samagotchi
|
|
|
116
118
|
ordered(@entries.values.select { |entry| left_out?(entry, layers, toolkit) }).map(&:name)
|
|
117
119
|
end
|
|
118
120
|
|
|
119
|
-
# The
|
|
120
|
-
# but the tool index reaches it (#reachable_names).
|
|
121
|
+
# The tool +name+ names (a built-in or a plugin's) when the session's
|
|
122
|
+
# toolkit leaves it out but the tool index reaches it (#reachable_names).
|
|
121
123
|
# @return [Entry, nil]
|
|
122
124
|
def indexed(name, layers: [], toolkit: @toolkit)
|
|
123
125
|
entry = self[name]
|
|
124
126
|
entry if entry && call_offered?(toolkit) && soft?(entry, layers, toolkit)
|
|
125
127
|
end
|
|
126
128
|
|
|
127
|
-
# The
|
|
128
|
-
#
|
|
129
|
-
# removed either (remove:, --no-tool, /toolkit
|
|
130
|
-
# itself declared.
|
|
129
|
+
# The tools +toolkit+ leaves out of a turn under +layers+ that the tool
|
|
130
|
+
# index reaches (call_tool), built-ins and plugin tools alike: not in
|
|
131
|
+
# its kit's list, but not removed either (remove:, --no-tool, /toolkit
|
|
132
|
+
# remove), with call_tool itself declared. Never the index's own.
|
|
131
133
|
# @return [Array<String>] in declaration order
|
|
132
134
|
def reachable_names(layers: [], toolkit: @toolkit)
|
|
133
135
|
return [] unless call_offered?(toolkit)
|
|
@@ -145,6 +147,25 @@ module Samagotchi
|
|
|
145
147
|
.map(&:name)
|
|
146
148
|
end
|
|
147
149
|
|
|
150
|
+
# The tools +toolkit+ leaves out of a turn under +layers+ that a
|
|
151
|
+
# plugin registered after the index was declared (chi.replace_tools)
|
|
152
|
+
# and that nothing reaches: call_tool isn't declared (the index's
|
|
153
|
+
# need is the load-time one, so the declared tools don't grow
|
|
154
|
+
# mid-session), nor removed by the user. /toolkit add declares one.
|
|
155
|
+
# @return [Array<String>] in declaration order
|
|
156
|
+
def stranded_names(layers: [], toolkit: @toolkit)
|
|
157
|
+
return [] if @index&.loaded.nil? || toolkit.nil? || toolkit.removed.include?(@index.call_name)
|
|
158
|
+
return [] if call_offered?(toolkit)
|
|
159
|
+
|
|
160
|
+
loaded = @index.loaded.map(&:name)
|
|
161
|
+
ordered(@entries.values.select { |entry| !loaded.include?(entry.name) && soft?(entry, layers, toolkit) })
|
|
162
|
+
.map(&:name)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Every entry registered now, declared or not, in registration order.
|
|
166
|
+
# @return [Array<Entry>]
|
|
167
|
+
def registered = @entries.values
|
|
168
|
+
|
|
148
169
|
# Every tool +source+ registered, declared or not (a plugin's tool set
|
|
149
170
|
# changing: Plugin::ToolSpec.apply).
|
|
150
171
|
# @return [Array<Entry>]
|
|
@@ -175,7 +196,7 @@ module Samagotchi
|
|
|
175
196
|
|
|
176
197
|
# Declared by +toolkit+ (nil: every tool, full). The tool index's
|
|
177
198
|
# tools are declared when they reach something, whatever the toolkit:
|
|
178
|
-
# a source registered at load, or a
|
|
199
|
+
# a source registered at load, or a tool it leaves out but
|
|
179
200
|
# doesn't remove (#index_needed?); unless it removes them.
|
|
180
201
|
def declared?(entry, toolkit = @toolkit)
|
|
181
202
|
if index_entry?(entry)
|
|
@@ -187,8 +208,10 @@ module Samagotchi
|
|
|
187
208
|
|
|
188
209
|
def index_entry?(entry) = !@index.nil? && entry.source == @index.source
|
|
189
210
|
|
|
190
|
-
# From what the plugins registered at load and the
|
|
191
|
-
# the declared tools stay the same for a session's
|
|
211
|
+
# From what the plugins registered at load (Index#loaded) and the
|
|
212
|
+
# toolkit alone, so the declared tools stay the same for a session's
|
|
213
|
+
# life: a plugin tool registered later on a kit that leaves it out
|
|
214
|
+
# doesn't bring find_tools and call_tool in (#stranded_names). A tool
|
|
192
215
|
# left out counts only while call_tool can reach it (not removed).
|
|
193
216
|
def index_needed?(toolkit)
|
|
194
217
|
return true if reached_groups?(toolkit)
|
|
@@ -198,10 +221,10 @@ module Samagotchi
|
|
|
198
221
|
end
|
|
199
222
|
|
|
200
223
|
# Whether the index's tools would reach something under +toolkit+,
|
|
201
|
-
# removed or not: a source registered at load, or a built-in
|
|
202
|
-
# toolkit leaves out without removing it.
|
|
224
|
+
# removed or not: a source registered at load, or a tool (a built-in
|
|
225
|
+
# or a plugin's) the toolkit leaves out without removing it.
|
|
203
226
|
def index_reaches?(toolkit)
|
|
204
|
-
reached_groups?(toolkit) || @entries.values.any? { |entry| soft?(entry, [], toolkit) }
|
|
227
|
+
reached_groups?(toolkit) || (@index.loaded || @entries.values).any? { |entry| soft?(entry, [], toolkit) }
|
|
205
228
|
end
|
|
206
229
|
|
|
207
230
|
def reached_groups?(toolkit)
|
|
@@ -218,11 +241,12 @@ module Samagotchi
|
|
|
218
241
|
toolkit&.removed&.include?(entry.name) == true && index_reaches?(toolkit)
|
|
219
242
|
end
|
|
220
243
|
|
|
221
|
-
# A
|
|
222
|
-
#
|
|
244
|
+
# A tool +toolkit+ leaves out of a turn under +layers+ without removing
|
|
245
|
+
# it: a built-in or a plugin's, never one of the index's own (they are
|
|
246
|
+
# declared when they reach something) or a layer's (always declared).
|
|
223
247
|
def soft?(entry, layers, toolkit)
|
|
224
|
-
!toolkit.nil? && entry
|
|
225
|
-
!toolkit.declares?(entry)
|
|
248
|
+
!toolkit.nil? && !index_entry?(entry) && entry.layer.nil? && entry.offered?(layers) &&
|
|
249
|
+
!toolkit.removed.include?(entry.name) && !toolkit.declares?(entry)
|
|
226
250
|
end
|
|
227
251
|
|
|
228
252
|
# Whether +toolkit+ declares the index's call tool.
|
|
@@ -28,7 +28,8 @@ module Samagotchi
|
|
|
28
28
|
|
|
29
29
|
result = NoteDelivery.deliver(id, text: content, source: "session", from_session: peers.session_id,
|
|
30
30
|
from_cwd: peers.cwd, state_dir: peers.state_dir)
|
|
31
|
-
return "Error: session #{id[0, 8]}
|
|
31
|
+
return "Error: session #{id[0, 8]}: #{result.refusal}" if result.scratch
|
|
32
|
+
return "Error: session #{id[0, 8]} is #{OwnerLock::TUI_BUSY}, which can't take notes" unless result.delivered?
|
|
32
33
|
|
|
33
34
|
where = result.status == :queued ? "its worker adds it before its next turn" : "it has no worker now, so it waits for its next start"
|
|
34
35
|
"Queued a note for session #{id[0, 8]}: #{where}. It does not start a turn there."
|
|
@@ -17,14 +17,15 @@ module Samagotchi
|
|
|
17
17
|
# inputSchema, and call_tool, which calls one as <group>/<tool>.
|
|
18
18
|
# A source (the mcp bundle, chi.tool_source) publishes its groups (an
|
|
19
19
|
# MCP server each) and runs the calls; the index's own group, chi, is
|
|
20
|
-
# the
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# the tools the session's toolkit leaves out but doesn't remove, chi's
|
|
21
|
+
# built-ins and its bundles' (chi.tool) alike (Registry#reachable_names),
|
|
22
|
+
# and a call_tool of one runs as that tool (#unwrap, before the
|
|
23
|
+
# guardrail gate). The index searches the
|
|
23
24
|
# groups, looks a call's tool up and checks it.
|
|
24
25
|
#
|
|
25
26
|
# The two tools are registered once, after the plugins load, and
|
|
26
27
|
# declared per toolkit (Registry#declared?): when a source registered
|
|
27
|
-
# then, or the toolkit leaves a
|
|
28
|
+
# then, or the toolkit leaves a tool reachable. The search tool's
|
|
28
29
|
# description has the source groups' lines as they were at load and a
|
|
29
30
|
# chi line per toolkit (#refresh!): nothing a source publishes later
|
|
30
31
|
# changes the request's tools, so the prompt cache keeps.
|
|
@@ -44,9 +45,10 @@ module Samagotchi
|
|
|
44
45
|
# times one in its description.
|
|
45
46
|
NAME_WEIGHT = 3
|
|
46
47
|
PREVIEW_CHARS = 60
|
|
47
|
-
# The index's own group: chi's built-ins
|
|
48
|
+
# The index's own group: chi's tools (built-ins and bundles' chi.tool)
|
|
49
|
+
# a toolkit leaves out.
|
|
48
50
|
CHI_GROUP = "chi"
|
|
49
|
-
CHI_SUMMARY = "chi's
|
|
51
|
+
CHI_SUMMARY = "chi's and its bundles' tools not declared in this session"
|
|
50
52
|
# Group names a source can't publish: the index's own groups'.
|
|
51
53
|
RESERVED_GROUPS = [CHI_GROUP].freeze
|
|
52
54
|
|
|
@@ -276,7 +278,7 @@ module Samagotchi
|
|
|
276
278
|
end
|
|
277
279
|
|
|
278
280
|
# Register the search and call tools in +registry+, once, after the
|
|
279
|
-
# plugins load (or without them: the
|
|
281
|
+
# plugins load (or without them: the tools a toolkit leaves out
|
|
280
282
|
# are reachable either way); +registry+ declares them per toolkit
|
|
281
283
|
# (Registry::Index). A name another tool has is left out (a line in
|
|
282
284
|
# +failures+).
|
|
@@ -285,6 +287,7 @@ module Samagotchi
|
|
|
285
287
|
@registry = registry
|
|
286
288
|
@described = description_lines(all_groups.reject { |group| own?(group) }).freeze
|
|
287
289
|
@declared_group_names = @described.keys.freeze
|
|
290
|
+
loaded = registry.registered
|
|
288
291
|
specs = [find_spec, call_spec]
|
|
289
292
|
declared = specs.filter_map do |spec|
|
|
290
293
|
if (taken = registry[spec.name])
|
|
@@ -299,7 +302,7 @@ module Samagotchi
|
|
|
299
302
|
end
|
|
300
303
|
registry.index = Registry::Index.new(source: SOURCE, call_name: CALL_NAME, groups: @declared_group_names,
|
|
301
304
|
deferrable: -> { deferrable }, wrap: ->(call) { wrap(call) },
|
|
302
|
-
unreached: ->(name) { unreached_tool(name) })
|
|
305
|
+
unreached: ->(name) { unreached_tool(name) }, loaded: loaded)
|
|
303
306
|
declared!(declared)
|
|
304
307
|
declared.map(&:name)
|
|
305
308
|
end
|
|
@@ -362,7 +365,7 @@ module Samagotchi
|
|
|
362
365
|
# A call of the tool +name+ names, with +args+ typed by its schema,
|
|
363
366
|
# run by its source. It fails closed: a tool that isn't the one
|
|
364
367
|
# guardrails were given (#claimed) is refused, and so is one of the
|
|
365
|
-
# index's own (a
|
|
368
|
+
# index's own (a chi tool runs as itself, #unwrap, or not at all).
|
|
366
369
|
def call(name, args)
|
|
367
370
|
tool = lookup(name)
|
|
368
371
|
return tool if tool.is_a?(String)
|
|
@@ -388,12 +391,12 @@ module Samagotchi
|
|
|
388
391
|
source.run(tool, inner)
|
|
389
392
|
end
|
|
390
393
|
|
|
391
|
-
# The
|
|
394
|
+
# The chi tool a call_tool +call+ names, as the call that runs in its
|
|
392
395
|
# place (ToolRunner#run's first step, before the guardrail gate):
|
|
393
396
|
# built from its arguments as a direct call would be
|
|
394
397
|
# (BuiltinCalls.build), with via: "call_tool". The name is a chi tool
|
|
395
|
-
# (a bare one only while the toolkit reaches it), or chi/<
|
|
396
|
-
#
|
|
398
|
+
# (a bare one only while the toolkit reaches it), or chi/<name> for
|
|
399
|
+
# any registered built-in or plugin tool: one the toolkit removes or declares
|
|
397
400
|
# is then refused by its name before the gate (ToolRunner#left_out?),
|
|
398
401
|
# with no hook or card. Nil, so the call runs as call_tool, when the
|
|
399
402
|
# turn under +layers+ doesn't offer call_tool, for an MCP tool, a miss
|
|
@@ -429,8 +432,8 @@ module Samagotchi
|
|
|
429
432
|
end
|
|
430
433
|
|
|
431
434
|
# The name a request declares +name+'s tool by when it goes out
|
|
432
|
-
# deferred (Tools::DeferredTools): a reachable built-in
|
|
433
|
-
# name or chi/<name>), a deferrable source tool's acts_as (by it or
|
|
435
|
+
# deferred (Tools::DeferredTools): a reachable chi tool's, built-in or
|
|
436
|
+
# plugin (by its name or chi/<name>), a deferrable source tool's acts_as (by it or
|
|
434
437
|
# <group>/<tool>). Nil for any other: declared, removed, unknown, or a
|
|
435
438
|
# source tool that can't be deferred. Never waits for a start.
|
|
436
439
|
# @return [String, nil]
|
|
@@ -549,7 +552,7 @@ module Samagotchi
|
|
|
549
552
|
end
|
|
550
553
|
|
|
551
554
|
# The wire names of +matches+ a request can declare deferred: a
|
|
552
|
-
#
|
|
555
|
+
# chi tool of the index's own groups by its name, a source's tool by
|
|
553
556
|
# its acts_as when it is #deferrable.
|
|
554
557
|
def loadable(matches)
|
|
555
558
|
sourced = nil
|
|
@@ -578,16 +581,17 @@ module Samagotchi
|
|
|
578
581
|
|
|
579
582
|
def own?(group) = RESERVED_GROUPS.include?(group.name)
|
|
580
583
|
|
|
581
|
-
# The registered
|
|
584
|
+
# The registered chi tool chi/<name> names (a built-in or a plugin's,
|
|
585
|
+
# never the index's own), reachable or not.
|
|
582
586
|
def builtin_named(name)
|
|
583
587
|
return nil unless name.start_with?("#{CHI_GROUP}/")
|
|
584
588
|
|
|
585
589
|
entry = @registry[name.delete_prefix("#{CHI_GROUP}/")]
|
|
586
|
-
entry
|
|
590
|
+
entry unless entry.nil? || entry.source == SOURCE
|
|
587
591
|
end
|
|
588
592
|
|
|
589
|
-
# The index's own groups: chi, the built-ins
|
|
590
|
-
# leaves out but reaches (none: no group). Each tool's schema is its
|
|
593
|
+
# The index's own groups: chi, the tools (built-ins and plugin tools)
|
|
594
|
+
# the Registry's toolkit leaves out but reaches (none: no group). Each tool's schema is its
|
|
591
595
|
# registered entry's (a config's execute description included), and
|
|
592
596
|
# it acts as itself.
|
|
593
597
|
# @return [Array<Group>]
|
|
@@ -698,8 +702,10 @@ module Samagotchi
|
|
|
698
702
|
return same.first if same.size == 1
|
|
699
703
|
return "Error: #{name} names #{same.map(&:full_name).join(" and ")}; call one as <group>/<tool>" if same.size > 1
|
|
700
704
|
|
|
701
|
-
# A
|
|
702
|
-
|
|
705
|
+
# A chi tool the session declares (a built-in or a plugin's) is no
|
|
706
|
+
# starting server's tool.
|
|
707
|
+
declared = @registry&.offered(name)
|
|
708
|
+
return "Error: #{name} is declared; call it directly" if declared && declared.source != SOURCE
|
|
703
709
|
|
|
704
710
|
# A name with a group's <group>/ waits for that group only; any
|
|
705
711
|
# other name (an acts_as) for every one starting.
|