samagotchi 0.155.0 → 0.157.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 +57 -1
- data/bin/chi +2 -1
- data/docs/architecture.md +22 -13
- data/docs/bundles.md +3 -1
- data/docs/cli.md +9 -8
- data/docs/configuration.md +36 -4
- data/docs/guardrails.md +9 -5
- data/docs/hooks.md +11 -5
- data/docs/internals/anthropic.md +1 -1
- data/docs/internals/attached-context.md +3 -2
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/bridge.md +83 -6
- data/docs/internals/bundles.md +28 -18
- data/docs/internals/context-telemetry.md +4 -3
- data/docs/internals/engine.md +641 -937
- data/docs/internals/llm-context-forget.md +8 -3
- data/docs/internals/objectives.md +2 -2
- data/docs/internals/participants.md +3 -2
- data/docs/internals/prompt-caching.md +4 -1
- data/docs/internals/remote-sessions.md +8 -3
- data/docs/internals/web-frontend.md +3 -2
- data/docs/internals/working-set.md +2 -2
- data/docs/memory.md +13 -2
- data/docs/plugins.md +35 -2
- data/docs/sessions.md +11 -11
- data/docs/sub-agent.md +17 -2
- data/docs/system-one.md +3 -2
- data/docs/testing.md +53 -2
- data/lib/samagotchi/answer_command.rb +2 -1
- data/lib/samagotchi/archive_store.rb +4 -3
- data/lib/samagotchi/bootstrap/config_writer.rb +1 -1
- data/lib/samagotchi/bootstrap_command.rb +3 -2
- data/lib/samagotchi/bridge/card_places.rb +10 -4
- data/lib/samagotchi/bridge/card_store.rb +3 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +109 -9
- data/lib/samagotchi/bridge/turn_drawings.rb +265 -0
- data/lib/samagotchi/bridge.rb +31 -6
- data/lib/samagotchi/broadcast/recipients.rb +8 -8
- data/lib/samagotchi/broadcast/triage_model.rb +1 -1
- data/lib/samagotchi/broadcast_command.rb +3 -3
- data/lib/samagotchi/bundle_command/check.rb +18 -0
- data/lib/samagotchi/bundle_command/try/sandbox.rb +2 -2
- data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +5 -2
- data/lib/samagotchi/bundles/known-names/manifest.yml +4 -7
- data/lib/samagotchi/bundles/known-names/{hooks/known_names.rb → plugin.rb} +17 -0
- data/lib/samagotchi/bundles/source-links/manifest.yml +4 -7
- data/lib/samagotchi/bundles/source-links/{hooks/source_links.rb → plugin.rb} +23 -4
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/child_move.rb +13 -57
- data/lib/samagotchi/child_reports.rb +2 -2
- data/lib/samagotchi/children_status.rb +15 -21
- data/lib/samagotchi/cli/parent_wait.rb +9 -23
- data/lib/samagotchi/config.rb +20 -112
- data/lib/samagotchi/context_command.rb +1 -2
- data/lib/samagotchi/context_sources.rb +5 -4
- data/lib/samagotchi/context_window.rb +9 -12
- data/lib/samagotchi/desktop/macos.rb +1 -1
- data/lib/samagotchi/effective_model.rb +57 -22
- data/lib/samagotchi/engine/context_judge.rb +19 -11
- data/lib/samagotchi/engine/messages.rb +1 -1
- data/lib/samagotchi/engine/models.rb +5 -2
- data/lib/samagotchi/engine/objectives.rb +29 -11
- data/lib/samagotchi/engine/plugins.rb +1 -1
- data/lib/samagotchi/engine/recap.rb +8 -4
- data/lib/samagotchi/engine/side_requests.rb +21 -11
- data/lib/samagotchi/engine/toolkit.rb +10 -9
- data/lib/samagotchi/engine/turn_end.rb +1 -1
- data/lib/samagotchi/engine/turn_helpers.rb +7 -7
- data/lib/samagotchi/engine/verify.rb +1 -1
- data/lib/samagotchi/engine.rb +43 -12
- data/lib/samagotchi/events.rb +3 -2
- data/lib/samagotchi/extension_load.rb +6 -3
- data/lib/samagotchi/guardrails/targets.rb +24 -9
- data/lib/samagotchi/guardrails_command.rb +1 -1
- data/lib/samagotchi/host_address.rb +52 -0
- data/lib/samagotchi/host_config.rb +72 -1
- data/lib/samagotchi/host_model.rb +2 -6
- data/lib/samagotchi/host_registry.rb +58 -46
- data/lib/samagotchi/kernel_loop.rb +53 -124
- data/lib/samagotchi/llm/anthropic_wire.rb +2 -2
- data/lib/samagotchi/llm/chat_loop.rb +40 -21
- data/lib/samagotchi/llm/native_backend.rb +1 -1
- data/lib/samagotchi/llm_context_apply.rb +54 -1
- data/lib/samagotchi/llm_context_notice.rb +1 -1
- data/lib/samagotchi/llm_context_strategy.rb +29 -67
- data/lib/samagotchi/llm_context_view.rb +9 -0
- data/lib/samagotchi/memory_bundle/asset_installer.rb +71 -76
- data/lib/samagotchi/memory_bundle/builder.rb +2 -1
- data/lib/samagotchi/memory_bundle/file_actions.rb +193 -0
- data/lib/samagotchi/memory_bundle/file_digest.rb +13 -0
- data/lib/samagotchi/memory_bundle/file_state.rb +152 -0
- data/lib/samagotchi/memory_bundle/installed_copy.rb +2 -1
- data/lib/samagotchi/memory_bundle/installed_manifest.rb +8 -1
- data/lib/samagotchi/memory_bundle/installer.rb +185 -169
- data/lib/samagotchi/memory_bundle/manifest.rb +14 -4
- data/lib/samagotchi/memory_bundle/merger.rb +69 -65
- data/lib/samagotchi/memory_bundle/plugin_ref.rb +6 -5
- data/lib/samagotchi/memory_bundle/profile.rb +5 -5
- data/lib/samagotchi/memory_bundle/provenance.rb +15 -1
- data/lib/samagotchi/memory_bundle/remerge.rb +25 -12
- data/lib/samagotchi/memory_bundle/shipped_update.rb +51 -6
- data/lib/samagotchi/memory_bundle/status.rb +14 -20
- data/lib/samagotchi/memory_bundle/system_bundle.rb +24 -6
- data/lib/samagotchi/memory_bundle/trash.rb +23 -8
- data/lib/samagotchi/memory_bundle/uninstaller.rb +16 -11
- data/lib/samagotchi/memory_usage.rb +1 -1
- data/lib/samagotchi/model_lookup.rb +75 -0
- data/lib/samagotchi/model_price.rb +4 -10
- data/lib/samagotchi/model_profile.rb +7 -6
- data/lib/samagotchi/model_settings.rb +37 -2
- data/lib/samagotchi/models_command.rb +1 -1
- data/lib/samagotchi/note_command.rb +2 -3
- data/lib/samagotchi/objectives_audit.rb +4 -3
- data/lib/samagotchi/participant_mode.rb +2 -1
- data/lib/samagotchi/participants.rb +2 -2
- data/lib/samagotchi/plugin/api.rb +41 -6
- data/lib/samagotchi/plugin/context.rb +1 -1
- data/lib/samagotchi/plugin/loader.rb +33 -10
- data/lib/samagotchi/plugin/sessions.rb +3 -6
- data/lib/samagotchi/plugin/tool_spec.rb +5 -3
- data/lib/samagotchi/project_env.rb +1 -1
- data/lib/samagotchi/reasoning_history.rb +14 -20
- data/lib/samagotchi/recap_place.rb +27 -0
- data/lib/samagotchi/reminder_carry.rb +1 -1
- data/lib/samagotchi/reply_wait/liveness.rb +38 -0
- data/lib/samagotchi/reply_wait/poll.rb +44 -15
- data/lib/samagotchi/reply_wait/target.rb +113 -0
- data/lib/samagotchi/reply_wait.rb +22 -2
- data/lib/samagotchi/reprefill_meter.rb +45 -0
- data/lib/samagotchi/review_command.rb +1 -1
- data/lib/samagotchi/sampling_settings.rb +9 -16
- data/lib/samagotchi/self_report.rb +36 -37
- data/lib/samagotchi/send_command.rb +17 -23
- data/lib/samagotchi/session.rb +370 -445
- data/lib/samagotchi/session_access/local.rb +12 -11
- data/lib/samagotchi/session_access/remote.rb +22 -6
- data/lib/samagotchi/session_archive_command.rb +2 -2
- data/lib/samagotchi/session_chain.rb +7 -6
- data/lib/samagotchi/session_continue.rb +6 -5
- data/lib/samagotchi/session_delete_command.rb +1 -1
- data/lib/samagotchi/session_field.rb +133 -0
- data/lib/samagotchi/session_inbox.rb +49 -1
- data/lib/samagotchi/session_manager/delivery.rb +254 -0
- data/lib/samagotchi/session_manager/emptiness.rb +139 -0
- data/lib/samagotchi/session_manager/lifecycle.rb +274 -0
- data/lib/samagotchi/session_manager/listing.rb +118 -0
- data/lib/samagotchi/session_manager/removal.rb +190 -0
- data/lib/samagotchi/session_manager/spawn.rb +242 -0
- data/lib/samagotchi/session_manager/worker_command.rb +157 -0
- data/lib/samagotchi/session_manager.rb +28 -1541
- data/lib/samagotchi/session_metrics.rb +8 -5
- data/lib/samagotchi/session_paths.rb +57 -0
- data/lib/samagotchi/session_retention.rb +2 -2
- data/lib/samagotchi/session_row.rb +200 -0
- data/lib/samagotchi/session_setup.rb +15 -12
- data/lib/samagotchi/sessions_command.rb +11 -129
- data/lib/samagotchi/share_command.rb +1 -1
- data/lib/samagotchi/starting_marker.rb +65 -0
- data/lib/samagotchi/steer_cut_after.rb +7 -14
- data/lib/samagotchi/system_one/recorder.rb +49 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +6 -26
- data/lib/samagotchi/terminal_ui/exit_lines.rb +1 -1
- data/lib/samagotchi/terminal_ui/join_replay.rb +86 -8
- data/lib/samagotchi/terminal_ui/local_link.rb +2 -2
- data/lib/samagotchi/terminal_ui.rb +5 -7
- data/lib/samagotchi/testing/install.rb +2 -1
- data/lib/samagotchi/testing/sessions.rb +1 -1
- data/lib/samagotchi/thinking.rb +16 -21
- data/lib/samagotchi/token_usage.rb +1 -1
- data/lib/samagotchi/tool_activity.rb +10 -4
- data/lib/samagotchi/tool_declarations.rb +2 -2
- data/lib/samagotchi/tool_output_cut.rb +17 -0
- data/lib/samagotchi/tool_response.rb +5 -3
- data/lib/samagotchi/tool_runner.rb +25 -5
- data/lib/samagotchi/toolkit_command.rb +1 -1
- data/lib/samagotchi/tools/delegate.rb +55 -45
- data/lib/samagotchi/tools/delegate_relay.rb +5 -5
- data/lib/samagotchi/tools/delegate_result.rb +5 -5
- data/lib/samagotchi/tools/delegate_wait.rb +41 -17
- data/lib/samagotchi/tools/index_hint.rb +14 -0
- data/lib/samagotchi/tools/list_sessions.rb +7 -7
- data/lib/samagotchi/tools/registry.rb +28 -11
- data/lib/samagotchi/tools/send_note.rb +4 -6
- data/lib/samagotchi/tools/tool_index.rb +52 -51
- data/lib/samagotchi/tools/tool_surface.rb +22 -8
- data/lib/samagotchi/turn_context.rb +2 -1
- data/lib/samagotchi/turn_stages/context_judge.rb +38 -36
- data/lib/samagotchi/turn_stages/objectives.rb +56 -52
- data/lib/samagotchi/turn_stages/toolkit_suggestion.rb +13 -17
- data/lib/samagotchi/turn_stages/verify_anchors.rb +9 -10
- data/lib/samagotchi/turn_stages/warmup.rb +14 -15
- data/lib/samagotchi/update_command.rb +11 -7
- data/lib/samagotchi/verify_fresh.rb +7 -11
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +11 -14
- data/lib/samagotchi/web/app.rb +45 -28
- data/lib/samagotchi/web/message_display.rb +57 -7
- data/lib/samagotchi/web/participant_access.rb +3 -3
- data/lib/samagotchi/web/public/activity.js +16 -7
- data/lib/samagotchi/web/public/app.js +29 -8
- data/lib/samagotchi/web/public/bubbles.js +8 -3
- data/lib/samagotchi/web/public/cards_place.js +15 -0
- data/lib/samagotchi/web/public/commands.js +5 -1
- data/lib/samagotchi/web/public/data.js +4 -2
- data/lib/samagotchi/web/public/history_shape.js +220 -0
- data/lib/samagotchi/web/public/recap_stamp.js +17 -0
- data/lib/samagotchi/web/public/reload_check.js +71 -0
- data/lib/samagotchi/web/public/stage_model.js +7 -5
- data/lib/samagotchi/web/public/stage_view.js +7 -3
- data/lib/samagotchi/web/public/stream_handlers.js +8 -2
- data/lib/samagotchi/web/public/timing.js +43 -2
- data/lib/samagotchi/web/public/turn_block.js +215 -30
- data/lib/samagotchi/web/public/turn_drawing.js +43 -0
- data/lib/samagotchi/web/public/turn_events.js +10 -1
- data/lib/samagotchi/web/public/turn_pieces.js +29 -0
- data/lib/samagotchi/web/reload_check_route.rb +95 -0
- data/lib/samagotchi/web/server.rb +4 -2
- data/lib/samagotchi/web/session_access_routes.rb +6 -6
- data/lib/samagotchi/web/session_control.rb +4 -4
- data/lib/samagotchi/web/session_create.rb +3 -3
- data/lib/samagotchi/web/session_hub.rb +9 -17
- data/lib/samagotchi/web/session_show.rb +67 -5
- data/lib/samagotchi/web/session_summary.rb +39 -28
- data/lib/samagotchi/worker.rb +83 -32
- data/lib/samagotchi/worker_boot.rb +184 -0
- data/lib/samagotchi/worker_link.rb +3 -0
- data/lib/samagotchi/worker_signals.rb +1 -1
- data/lib/samagotchi/worker_task_wakes.rb +2 -2
- metadata +32 -4
- data/lib/samagotchi/send_command/wait_target.rb +0 -85
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require_relative "args"
|
|
5
5
|
require_relative "builtin_calls"
|
|
6
|
+
require_relative "index_hint"
|
|
6
7
|
require_relative "registry"
|
|
7
8
|
require_relative "../log"
|
|
8
9
|
require_relative "../token_usage"
|
|
@@ -11,7 +12,7 @@ require_relative "../plugin/tool_spec"
|
|
|
11
12
|
|
|
12
13
|
module Samagotchi
|
|
13
14
|
module Tools
|
|
14
|
-
# Read only when a
|
|
15
|
+
# Read only when a surface is built (#declare, #refresh!): the bundle
|
|
15
16
|
# test kit loads this file for ToolIndex.new and never builds one.
|
|
16
17
|
autoload :ToolSurface, File.expand_path("tool_surface", __dir__)
|
|
17
18
|
|
|
@@ -27,12 +28,14 @@ module Samagotchi
|
|
|
27
28
|
# guardrail gate). The index searches the
|
|
28
29
|
# groups, looks a call's tool up and checks it.
|
|
29
30
|
#
|
|
30
|
-
# What it reads of the session's tools is a ToolSurface
|
|
31
|
-
# (TurnContext#surface)
|
|
32
|
-
# handlers, through their ToolContext;
|
|
33
|
-
#
|
|
34
|
-
# the
|
|
35
|
-
#
|
|
31
|
+
# What it reads of the session's tools is a ToolSurface, always given:
|
|
32
|
+
# the turn's (TurnContext#surface) on the turn's path (the two tools'
|
|
33
|
+
# handlers, through their ToolContext; call_tool's row and targets,
|
|
34
|
+
# from the row and the gate; #unwrap, #wrap, #deferrable and
|
|
35
|
+
# #unreached_tool, from the runner and the deferred list); one built
|
|
36
|
+
# from the Registry #declare registered in at load and at #refresh!;
|
|
37
|
+
# ToolkitBinding#published's for ctx.load_tools (#deferred_name). None
|
|
38
|
+
# where one is needed raises MissingSurface.
|
|
36
39
|
#
|
|
37
40
|
# The two tools are registered once, after the plugins load, and
|
|
38
41
|
# declared per toolkit (Registry#declared?): when a source registered
|
|
@@ -69,8 +72,6 @@ module Samagotchi
|
|
|
69
72
|
group: { type: "string", description: "search only this group" }
|
|
70
73
|
}.freeze
|
|
71
74
|
CALL_DESCRIPTION = "Call a tool #{FIND_NAME} found.".freeze
|
|
72
|
-
# A keyword answer's first line.
|
|
73
|
-
CALL_HINT = "Call one with #{CALL_NAME}(tool: \"<group>/<tool>\", args: {…}).".freeze
|
|
74
75
|
CALL_PARAMS = {
|
|
75
76
|
tool: { type: "string", required: true, description: "the tool as <group>/<tool>" },
|
|
76
77
|
args: { type: "object", description: "the arguments, per the inputSchema #{FIND_NAME} gave" }
|
|
@@ -264,7 +265,7 @@ module Samagotchi
|
|
|
264
265
|
# +surface+), then the sources'. A source group with a reserved name,
|
|
265
266
|
# or one an earlier group has, is left out of search, lookup and the
|
|
266
267
|
# description (said once per name: a source publishes after load too).
|
|
267
|
-
def all_groups(surface
|
|
268
|
+
def all_groups(surface)
|
|
268
269
|
own = registry_groups(surface)
|
|
269
270
|
taken = own.map(&:name) | RESERVED_GROUPS
|
|
270
271
|
kept = @sources.flat_map(&:groups).select do |group|
|
|
@@ -280,7 +281,7 @@ module Samagotchi
|
|
|
280
281
|
|
|
281
282
|
# The groups +surface+'s toolkit reaches (#all_groups' own, and the
|
|
282
283
|
# source groups #declare found that it reaches).
|
|
283
|
-
def reached_groups(surface
|
|
284
|
+
def reached_groups(surface)
|
|
284
285
|
groups = all_groups(surface)
|
|
285
286
|
toolkit = surface.toolkit
|
|
286
287
|
return groups if toolkit.nil? || toolkit.groups == :all
|
|
@@ -299,10 +300,11 @@ module Samagotchi
|
|
|
299
300
|
# @return [Array<String>] the names registered
|
|
300
301
|
def declare(registry, failures: nil)
|
|
301
302
|
@registry = registry
|
|
302
|
-
|
|
303
|
+
at_load = ToolSurface.for(registry)
|
|
304
|
+
@described = description_lines(all_groups(at_load).reject { |group| own?(group) }).freeze
|
|
303
305
|
@declared_group_names = @described.keys.freeze
|
|
304
306
|
loaded = registry.registered
|
|
305
|
-
specs = [find_spec, call_spec]
|
|
307
|
+
specs = [find_spec(at_load), call_spec]
|
|
306
308
|
declared = specs.filter_map do |spec|
|
|
307
309
|
if (taken = registry[spec.name])
|
|
308
310
|
failures&.add("tool #{spec.name}", "already registered (#{taken.source}); the tool index's is left out",
|
|
@@ -320,7 +322,7 @@ module Samagotchi
|
|
|
320
322
|
wrap: ->(call, surface) { wrap(call, surface) },
|
|
321
323
|
unreached: ->(name, surface) { unreached_tool(name, surface) },
|
|
322
324
|
loaded: loaded)
|
|
323
|
-
declared!(declared)
|
|
325
|
+
declared!(declared, ToolSurface.for(registry))
|
|
324
326
|
declared.map(&:name)
|
|
325
327
|
end
|
|
326
328
|
|
|
@@ -331,8 +333,9 @@ module Samagotchi
|
|
|
331
333
|
entry = @registry && @registry[FIND_NAME]
|
|
332
334
|
return unless entry&.source == SOURCE
|
|
333
335
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
+
surface = ToolSurface.for(@registry)
|
|
337
|
+
entry.schema = entry.schema.merge(description: find_description(surface))
|
|
338
|
+
declared!([find_spec(surface), call_spec].select { |spec| @registry[spec.name]&.source == SOURCE }, surface)
|
|
336
339
|
end
|
|
337
340
|
|
|
338
341
|
# A checked tool declaration's (Plugin::ToolSpec) definition as
|
|
@@ -357,7 +360,7 @@ module Samagotchi
|
|
|
357
360
|
# server's: its name, and its summary if it has one). Never every
|
|
358
361
|
# tool: knowing the names, a model searches less and guesses
|
|
359
362
|
# arguments.
|
|
360
|
-
def find_description(surface
|
|
363
|
+
def find_description(surface)
|
|
361
364
|
"Search tools this session doesn't declare, by keywords. It answers up to #{FIND_RESULTS} tools, each with " \
|
|
362
365
|
"its name (<group>/<tool>) and inputSchema; call one with #{CALL_NAME}. An empty query lists every tool's " \
|
|
363
366
|
"name.\nGroups:\n#{(description_lines(registry_groups(surface)).values + described_groups(surface).values).join("\n")}"
|
|
@@ -370,7 +373,7 @@ module Samagotchi
|
|
|
370
373
|
# name before it was +group+: a model that still gives it is told
|
|
371
374
|
# every group was searched. +surface+: the turn's (the handler's).
|
|
372
375
|
# @return [FoundText]
|
|
373
|
-
def find(query, only = nil, server: nil
|
|
376
|
+
def find(query, only = nil, surface:, server: nil)
|
|
374
377
|
answer = find_in(query, only, surface)
|
|
375
378
|
return answer unless (only.nil? || only.strip.empty?) && !server.to_s.strip.empty?
|
|
376
379
|
|
|
@@ -384,7 +387,7 @@ module Samagotchi
|
|
|
384
387
|
# guardrails were given (#claimed) is refused, and so is one of the
|
|
385
388
|
# index's own (a chi tool runs as itself, #unwrap, or not at all).
|
|
386
389
|
# +surface+: the turn's (the handler's).
|
|
387
|
-
def call(name, args, surface:
|
|
390
|
+
def call(name, args, surface:)
|
|
388
391
|
tool = lookup(name, surface)
|
|
389
392
|
return tool if tool.is_a?(String)
|
|
390
393
|
|
|
@@ -420,7 +423,7 @@ module Samagotchi
|
|
|
420
423
|
# turn's +surface+ doesn't declare call_tool, for an MCP tool, a miss
|
|
421
424
|
# or arguments that aren't an object.
|
|
422
425
|
# @return [Hash, nil]
|
|
423
|
-
def unwrap(call, surface
|
|
426
|
+
def unwrap(call, surface)
|
|
424
427
|
return nil unless surface.declared_entry(CALL_NAME)
|
|
425
428
|
|
|
426
429
|
args = call[:args].is_a?(Hash) ? call[:args] : {}
|
|
@@ -439,7 +442,7 @@ module Samagotchi
|
|
|
439
442
|
# registered tool's. Sorted by acts_as, so the same index gives the
|
|
440
443
|
# same list in any session and after a respawn.
|
|
441
444
|
# @return [Array<Tool>]
|
|
442
|
-
def deferrable(surface
|
|
445
|
+
def deferrable(surface)
|
|
443
446
|
all = all_groups(surface).reject { |group| own?(group) }.flat_map(&:tools)
|
|
444
447
|
reached = reached_groups(surface).reject { |group| own?(group) }.flat_map(&:tools)
|
|
445
448
|
counts = all.map(&:acts_as).tally
|
|
@@ -455,7 +458,7 @@ module Samagotchi
|
|
|
455
458
|
# <group>/<tool>). Nil for any other: declared, removed, unknown, or a
|
|
456
459
|
# source tool that can't be deferred. Never waits for a start.
|
|
457
460
|
# @return [String, nil]
|
|
458
|
-
def deferred_name(name, surface
|
|
461
|
+
def deferred_name(name, surface)
|
|
459
462
|
name = name.to_s.strip
|
|
460
463
|
bare = name.delete_prefix("#{CHI_GROUP}/")
|
|
461
464
|
return bare if surface.indexed_entry(bare)
|
|
@@ -469,7 +472,7 @@ module Samagotchi
|
|
|
469
472
|
# Finds a source tool regardless of the active toolkit, without
|
|
470
473
|
# waiting for a starting group. Used only to refuse a call or explain
|
|
471
474
|
# a deferred stub for a group the session cannot reach.
|
|
472
|
-
def unreached_tool(name, surface
|
|
475
|
+
def unreached_tool(name, surface)
|
|
473
476
|
name = name.to_s.strip
|
|
474
477
|
return nil if name.empty?
|
|
475
478
|
|
|
@@ -491,7 +494,7 @@ module Samagotchi
|
|
|
491
494
|
# loads tools): the call tool's row, rules and approvals. Nil for any
|
|
492
495
|
# other call.
|
|
493
496
|
# @return [Hash, nil]
|
|
494
|
-
def wrap(call, surface
|
|
497
|
+
def wrap(call, surface)
|
|
495
498
|
name = call[:name].to_s
|
|
496
499
|
return nil if name.empty? || surface.registered?(name)
|
|
497
500
|
|
|
@@ -502,7 +505,7 @@ module Samagotchi
|
|
|
502
505
|
|
|
503
506
|
# The call tool's activity row: <group>/<tool> and its arguments, as
|
|
504
507
|
# +surface+ (the turn's) reaches it.
|
|
505
|
-
def call_preview(args, surface
|
|
508
|
+
def call_preview(args, surface)
|
|
506
509
|
tool = lookup_quietly(args["tool"].to_s, surface)
|
|
507
510
|
name = tool ? tool.full_name : args["tool"].to_s.strip
|
|
508
511
|
inner = tool && inner_args(args["args"], tool)
|
|
@@ -515,7 +518,7 @@ module Samagotchi
|
|
|
515
518
|
# model gave (#claimed), so a rule on it fires before the call waits
|
|
516
519
|
# for the server. Nothing for a name that can't be a tool (the call
|
|
517
520
|
# refuses it). +surface+: the turn's (the gate's).
|
|
518
|
-
def call_targets(args, surface
|
|
521
|
+
def call_targets(args, surface)
|
|
519
522
|
name = args["tool"].to_s.strip
|
|
520
523
|
tool = lookup_quietly(name, surface)
|
|
521
524
|
acts_as, label = tool ? [tool.acts_as, "#{tool.group}: #{tool.name}"] : claimed(name, surface)
|
|
@@ -526,44 +529,41 @@ module Samagotchi
|
|
|
526
529
|
|
|
527
530
|
private
|
|
528
531
|
|
|
529
|
-
# The Registry #declare registered in, read now as a ToolSurface (its
|
|
530
|
-
# toolkit, every layer's tools left aside: the index's own group never
|
|
531
|
-
# holds one): what a caller without a turn's surface sees.
|
|
532
|
-
# @return [ToolSurface]
|
|
533
|
-
def live_surface = ToolSurface.for(@registry)
|
|
534
|
-
|
|
535
532
|
# The fields +spec+'s tool registers with beside its entry_fields: a
|
|
536
533
|
# handler whose block is given the call's typed args and the turn's
|
|
537
|
-
# surface (
|
|
538
|
-
#
|
|
539
|
-
#
|
|
540
|
-
#
|
|
534
|
+
# surface (its ToolContext's), and call_tool's preview and targets,
|
|
535
|
+
# given the turn's surface by the row and the gate
|
|
536
|
+
# (Registry::Entry#preview_of / #targets_of). Without a surface each
|
|
537
|
+
# raises MissingSurface: none reads the Registry as it is now.
|
|
541
538
|
def own_fields(spec)
|
|
542
539
|
parameters = spec.schema[:parameters]
|
|
543
540
|
args = ->(call) { Plugin::ToolSpec.args_of(call, parameters) }
|
|
544
541
|
fields = { handler: lambda { |call, kctx|
|
|
545
|
-
Plugin::ToolSpec.result_text(spec.block.call(args.call(call), kctx&.surface ||
|
|
542
|
+
Plugin::ToolSpec.result_text(spec.block.call(args.call(call), kctx&.surface || raise(MissingSurface)))
|
|
546
543
|
} }
|
|
547
544
|
return fields unless spec.name == CALL_NAME
|
|
548
545
|
|
|
549
|
-
fields.merge(preview: ->(call, surface = nil) { call_preview(args.call(call), surface ||
|
|
550
|
-
targets: ->(call, surface = nil) { call_targets(args.call(call), surface ||
|
|
546
|
+
fields.merge(preview: ->(call, surface = nil) { call_preview(args.call(call), surface || raise(MissingSurface)) },
|
|
547
|
+
targets: ->(call, surface = nil) { call_targets(args.call(call), surface || raise(MissingSurface)) })
|
|
551
548
|
end
|
|
552
549
|
|
|
553
|
-
|
|
554
|
-
|
|
550
|
+
# The search tool's declaration, its description read from +surface+
|
|
551
|
+
# (the Registry's now: #declare, #refresh!); its block searches the
|
|
552
|
+
# turn's (own_fields' handler gives it).
|
|
553
|
+
def find_spec(surface)
|
|
554
|
+
Plugin::ToolSpec.build(FIND_NAME, find_description(surface), params: FIND_PARAMS,
|
|
555
555
|
preview: lambda { |args|
|
|
556
556
|
[args["query"].to_s.strip, args["group"] && "in #{args["group"]}"].compact.join(" ")
|
|
557
|
-
}) do |args,
|
|
558
|
-
find(args["query"].to_s, args["group"], server: args["server"], surface:
|
|
557
|
+
}) do |args, turn|
|
|
558
|
+
find(args["query"].to_s, args["group"], server: args["server"], surface: turn)
|
|
559
559
|
end
|
|
560
560
|
end
|
|
561
561
|
|
|
562
|
+
# The call tool's declaration; its preview and targets are own_fields'.
|
|
562
563
|
def call_spec
|
|
563
|
-
Plugin::ToolSpec.build(CALL_NAME, CALL_DESCRIPTION, params: CALL_PARAMS,
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
call(args["tool"].to_s, args["args"], surface: surface)
|
|
564
|
+
Plugin::ToolSpec.build(CALL_NAME, CALL_DESCRIPTION, params: CALL_PARAMS,
|
|
565
|
+
label: @sources.first&.label) do |args, turn|
|
|
566
|
+
call(args["tool"].to_s, args["args"], surface: turn)
|
|
567
567
|
end
|
|
568
568
|
end
|
|
569
569
|
|
|
@@ -590,7 +590,7 @@ module Samagotchi
|
|
|
590
590
|
blocks = matches.map { |tool| match_text(tool, all) }
|
|
591
591
|
unsearched = searched.filter_map { |group| "#{group.name} #{group.note}" if group.tools.empty? && group.note }
|
|
592
592
|
blocks << "Not searched: #{unsearched.join("; ")}." unless unsearched.empty?
|
|
593
|
-
FoundText.new("#{CALL_HINT}\n\n#{blocks.join("\n\n")}",
|
|
593
|
+
FoundText.new("#{IndexHint::CALL_HINT}\n\n#{blocks.join("\n\n")}",
|
|
594
594
|
loadable(matches, surface))
|
|
595
595
|
end
|
|
596
596
|
|
|
@@ -608,11 +608,12 @@ module Samagotchi
|
|
|
608
608
|
end
|
|
609
609
|
end
|
|
610
610
|
|
|
611
|
-
# What the sources hear of the declared tools (/mcp's last line)
|
|
612
|
-
|
|
611
|
+
# What the sources hear of the declared tools (/mcp's last line), the
|
|
612
|
+
# groups +surface+ (the Registry's now) reaches.
|
|
613
|
+
def declared!(specs, surface)
|
|
613
614
|
names = specs.map(&:name)
|
|
614
615
|
tokens = specs.sum { |spec| ToolIndex.spec_tokens(spec) }
|
|
615
|
-
groups = reached_groups.reject { |group| own?(group) }.map(&:name)
|
|
616
|
+
groups = reached_groups(surface).reject { |group| own?(group) }.map(&:name)
|
|
616
617
|
@sources.each { |source| source.declared!(names, tokens, groups) }
|
|
617
618
|
end
|
|
618
619
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "builtin_calls"
|
|
4
4
|
require_relative "deferred_tools"
|
|
5
|
+
require_relative "registry"
|
|
5
6
|
|
|
6
7
|
module Samagotchi
|
|
7
8
|
module Tools
|
|
@@ -14,8 +15,10 @@ module Samagotchi
|
|
|
14
15
|
# tool_definitions/deferred_tools and ToolRunner's left-out, unwrap
|
|
15
16
|
# and refusal checks read it. A change made while the turn runs (a
|
|
16
17
|
# /toolkit change, chi.replace_tools: staged anyway) shapes the next
|
|
17
|
-
# turn's surface only. Outside a turn
|
|
18
|
-
#
|
|
18
|
+
# turn's surface only. Outside a turn a caller builds one (.for): a bare
|
|
19
|
+
# kernel or loop at its own context (KernelLoop#own_context), the tool
|
|
20
|
+
# index at load and at #refresh!, the readers off the turn thread from
|
|
21
|
+
# ToolkitBinding#published.
|
|
19
22
|
#
|
|
20
23
|
# +declared+ the entries the request declares, in prompt order (Registry#entries)
|
|
21
24
|
# +index_only+ the entries the toolkit leaves out that call_tool reaches (Registry#reachable_names)
|
|
@@ -67,14 +70,25 @@ module Samagotchi
|
|
|
67
70
|
layers: frozen_copy(Array(layers)), **)
|
|
68
71
|
end
|
|
69
72
|
|
|
70
|
-
# The turn's surface (+context+'s
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
+
# The turn's surface (+context+'s: an Engine turn's, or a bare kernel's
|
|
74
|
+
# or loop's own, KernelLoop#own_context); a context without one raises
|
|
75
|
+
# MissingSurface (no reader falls back to the Registry as it is now).
|
|
73
76
|
# @param context [TurnContext]
|
|
74
77
|
# @return [ToolSurface]
|
|
75
|
-
def self.of(context
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
def self.of(context) = context.surface || raise(MissingSurface)
|
|
79
|
+
|
|
80
|
+
# A bare kernel's or loop's own +context+ (no Engine turn: a spec, a
|
|
81
|
+
# bench) with a surface: its own, else one built now from +registry+
|
|
82
|
+
# under its layers and loads_tools. Only their own default context
|
|
83
|
+
# goes through here (KernelLoop#own_context, ChatLoop#turn_context);
|
|
84
|
+
# a context handed down is read as it is (.of).
|
|
85
|
+
# @param context [TurnContext]
|
|
86
|
+
# @return [TurnContext]
|
|
87
|
+
def self.bare(context, registry)
|
|
88
|
+
return context if context.surface
|
|
89
|
+
|
|
90
|
+
context.with(surface: self.for(registry, layers: context.llm_context_layers,
|
|
91
|
+
loads_tools: context.settings&.loads_tools == true))
|
|
78
92
|
end
|
|
79
93
|
|
|
80
94
|
def self.empty(layers, loads_tools)
|
|
@@ -32,7 +32,8 @@ module Samagotchi
|
|
|
32
32
|
# rules' models:), +model_name+ the bare model name the gate's rules match: frozen at
|
|
33
33
|
# Engine#generate with the settings (a before_turn hook may switch the model)
|
|
34
34
|
# +surface+ the turn's Tools::ToolSurface (what its loops declare, defer and dispatch):
|
|
35
|
-
# nil until Engine#generate builds it; a reader
|
|
35
|
+
# nil until Engine#generate builds it; a reader given none raises Tools::MissingSurface
|
|
36
|
+
# (ToolSurface.of), a bare kernel's or loop's own context builds one (ToolSurface.bare)
|
|
36
37
|
#
|
|
37
38
|
# Only the turn thread replaces it (TurnState#replace_context), one frozen
|
|
38
39
|
# value per write; other threads read the published one (Engine#turn_context,
|
|
@@ -19,24 +19,22 @@ module Samagotchi
|
|
|
19
19
|
# its pass at an owner turn's start and for a late candidate, the asks it
|
|
20
20
|
# starts, and the cards an answer brought while a turn ran, shown once it
|
|
21
21
|
# has ended. Its state is those held cards, under their mutex.
|
|
22
|
+
#
|
|
23
|
+
# Its two callables are read on the asks' thread, after the call that
|
|
24
|
+
# started the asks returned (an answer comes when it comes): which
|
|
25
|
+
# session the Engine is on then, and the card shown where cards go then.
|
|
22
26
|
class ContextJudge
|
|
23
27
|
# @param side_judgments [SideJudgments] the Engine's side asks (:context kind)
|
|
24
28
|
# @param host_registry [HostRegistry] System 1's client resolves its host here
|
|
25
|
-
# @param
|
|
26
|
-
# @param
|
|
27
|
-
# @param
|
|
28
|
-
|
|
29
|
-
# @param turn_running [#call] whether a turn runs now
|
|
30
|
-
# @param show_card [#call] (**card) shows a card (Engine#show_card)
|
|
31
|
-
def initialize(side_judgments:, host_registry:, state_dir:, recording:, session:, messages:, turn_running:, show_card:)
|
|
29
|
+
# @param turn_state [TurnState] whether a turn runs as an answer comes (#running?)
|
|
30
|
+
# @param show_card [#call] (**card) shows a card (Engine#show_card: EventRoutes, on the caller's thread)
|
|
31
|
+
# @param current_session [#call] the session the Engine is on as an answer comes (nil: none)
|
|
32
|
+
def initialize(side_judgments:, host_registry:, turn_state:, show_card:, current_session:)
|
|
32
33
|
@side_judgments = side_judgments
|
|
33
34
|
@host_registry = host_registry
|
|
34
|
-
@
|
|
35
|
-
@recording = recording
|
|
36
|
-
@session = session
|
|
37
|
-
@messages = messages
|
|
38
|
-
@turn_running = turn_running
|
|
35
|
+
@turn_state = turn_state
|
|
39
36
|
@show_card = show_card
|
|
37
|
+
@current_session = current_session
|
|
40
38
|
# On mode's cards an answer brought while a turn ran: shown once it
|
|
41
39
|
# has ended (#show_cards_after_turn).
|
|
42
40
|
@cards_after_turn = []
|
|
@@ -51,23 +49,28 @@ module Samagotchi
|
|
|
51
49
|
# (ContextCandidatesLog#look) and, unless context.candidates.mode is
|
|
52
50
|
# off, the proposed candidates due an ask are asked about the owner's
|
|
53
51
|
# prompts so far, this one included. Never a participant's turn, a
|
|
54
|
-
# review session or a Continue (no new prompt).
|
|
55
|
-
|
|
52
|
+
# review session or a Continue (no new prompt). The sessions dir is
|
|
53
|
+
# the turn's (TurnContext#state_dir).
|
|
54
|
+
# @param recorder [SystemOne::Recorder] the asks' runs (built for this call)
|
|
55
|
+
def start(turn, recorder:)
|
|
56
56
|
session = turn.session
|
|
57
57
|
return unless turn.prompt.is_a?(String) && OwnerPrompt.turn?(session: session, author: turn.origin.author)
|
|
58
58
|
|
|
59
|
-
pass(session, ContextCandidates.owner_texts(session.messages) + [turn.prompt], phase: "turn"
|
|
59
|
+
pass(session, ContextCandidates.owner_texts(session.messages) + [turn.prompt], phase: "turn",
|
|
60
|
+
state_dir: turn.context.state_dir, recorder: recorder)
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# A late proposal no owner prompt mentions (Plugin::Host
|
|
63
64
|
# #candidate_proposed: github-pr's init task runs beside the first
|
|
64
65
|
# turn): judged on the owner's prompts so far, the running turn's too.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
# The caller (Engine#judge_late_candidates) gives the session the
|
|
67
|
+
# Engine is on, never nil nor a review session.
|
|
68
|
+
# @param messages [Array<Hash>] the owner's conversation so far (Engine#plugin_messages)
|
|
69
|
+
# @param recorder [SystemOne::Recorder] the asks' runs (built for this call)
|
|
70
|
+
# @param state_dir [String] the sessions dir
|
|
71
|
+
def late_candidates(session, messages:, recorder:, state_dir:)
|
|
72
|
+
texts = ContextCandidates.owner_texts(messages)
|
|
73
|
+
pass(session, texts, phase: "late", state_dir: state_dir, recorder: recorder) unless texts.empty?
|
|
71
74
|
end
|
|
72
75
|
|
|
73
76
|
# The cards #choice held while a turn ran (Engine#run_turn's ensure,
|
|
@@ -83,27 +86,27 @@ module Samagotchi
|
|
|
83
86
|
|
|
84
87
|
# One look at +session+'s candidates; the asks go to SideJudgments'
|
|
85
88
|
# :context kind, one thread asking each due candidate in turn.
|
|
86
|
-
def pass(session, texts, phase:)
|
|
89
|
+
def pass(session, texts, phase:, state_dir:, recorder:)
|
|
87
90
|
return if session.id.nil? || session.scratch
|
|
88
91
|
|
|
89
|
-
rows = ContextCandidates.with_status(session.id, project_root: session.project_root, state_dir:
|
|
92
|
+
rows = ContextCandidates.with_status(session.id, project_root: session.project_root, state_dir: state_dir)
|
|
90
93
|
return if rows.empty?
|
|
91
94
|
|
|
92
|
-
log = ContextCandidatesLog.log_for(
|
|
95
|
+
log = ContextCandidatesLog.log_for(state_dir)
|
|
93
96
|
records = ContextCandidatesLog.records_for(log, session.id)
|
|
94
97
|
ContextCandidatesLog.look(log, session.id, rows, records) if phase == "turn"
|
|
95
98
|
settings = Samagotchi::ContextJudge::Settings.from_config
|
|
96
99
|
return if settings.mode == "off"
|
|
97
100
|
|
|
98
101
|
jobs = Samagotchi::ContextJudge.jobs(rows, records, session_id: session.id, texts: texts, settings: settings,
|
|
99
|
-
phase: phase, parent_attached: parent_attached(session))
|
|
102
|
+
phase: phase, parent_attached: parent_attached(session, state_dir))
|
|
100
103
|
return if jobs.empty?
|
|
101
104
|
|
|
102
|
-
client =
|
|
103
|
-
|
|
105
|
+
client = recorder.wrap(SystemOne::Client.from_config(host_registry: @host_registry, mode: "on", tag: :context),
|
|
106
|
+
Samagotchi::ContextJudge::JUDGMENT, session, mode: settings.mode)
|
|
104
107
|
return unless client
|
|
105
108
|
|
|
106
|
-
start_asks(client, jobs, log, settings, choice(session, log, settings))
|
|
109
|
+
start_asks(client, jobs, log, settings, choice(session, log, settings, state_dir))
|
|
107
110
|
rescue StandardError => e
|
|
108
111
|
Samagotchi::ContextJudge.warn(e)
|
|
109
112
|
end
|
|
@@ -128,16 +131,15 @@ module Samagotchi
|
|
|
128
131
|
# and not in a running turn (it isn't the turn's: no "waiting for you"
|
|
129
132
|
# on it), but once it has ended.
|
|
130
133
|
# @return [#call] (job, fields)
|
|
131
|
-
def choice(session, log, settings)
|
|
134
|
+
def choice(session, log, settings, state_dir)
|
|
132
135
|
session_id = session.id
|
|
133
136
|
project_root = session.project_root
|
|
134
|
-
state_dir = @state_dir.call
|
|
135
137
|
show = lambda do |**card|
|
|
136
|
-
current = @
|
|
138
|
+
current = @current_session.call
|
|
137
139
|
next nil unless current.nil? || current.id == session_id
|
|
138
140
|
|
|
139
141
|
@cards_mutex.synchronize do
|
|
140
|
-
next @cards_after_turn << card if @
|
|
142
|
+
next @cards_after_turn << card if @turn_state.running?
|
|
141
143
|
|
|
142
144
|
@show_card.call(**card)
|
|
143
145
|
end
|
|
@@ -151,15 +153,15 @@ module Samagotchi
|
|
|
151
153
|
# A delegate child's parent's sources, by title (else name): the
|
|
152
154
|
# judge's started_from. nil for any other session (a chi send --new
|
|
153
155
|
# session has no parent; a fork isn't a delegate).
|
|
154
|
-
def parent_attached(session)
|
|
156
|
+
def parent_attached(session, state_dir)
|
|
155
157
|
return nil unless session.delegate? && session.parent_id
|
|
156
158
|
|
|
157
|
-
parent_root = if Session.exist?(session.parent_id, state_dir:
|
|
158
|
-
Session.load(session.parent_id, state_dir:
|
|
159
|
+
parent_root = if Session.exist?(session.parent_id, state_dir: state_dir)
|
|
160
|
+
Session.load(session.parent_id, state_dir: state_dir).project_root
|
|
159
161
|
else
|
|
160
162
|
session.project_root
|
|
161
163
|
end
|
|
162
|
-
ContextSources.attached(session.parent_id, project_root: parent_root, state_dir:
|
|
164
|
+
ContextSources.attached(session.parent_id, project_root: parent_root, state_dir: state_dir)
|
|
163
165
|
.map { |attached| attached.source.title || attached.name }
|
|
164
166
|
rescue StandardError => e
|
|
165
167
|
Log.warn(:context, "context_parent_attached_failed", error: e.class.name, msg: e.message.to_s[0, 200])
|