samagotchi 0.154.1 → 0.156.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.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/bin/chi +1 -1
  4. data/docs/architecture.md +12 -7
  5. data/docs/bundles.md +21 -0
  6. data/docs/cli.md +1 -1
  7. data/docs/guardrails.md +2 -2
  8. data/docs/hooks.md +1 -1
  9. data/docs/internals/anthropic.md +5 -5
  10. data/docs/internals/attached-context.md +9 -6
  11. data/docs/internals/background-tasks.md +1 -1
  12. data/docs/internals/block-renderers.md +8 -1
  13. data/docs/internals/engine.md +642 -831
  14. data/docs/internals/inline-widgets.md +136 -0
  15. data/docs/internals/llm-context-forget.md +8 -3
  16. data/docs/internals/objectives.md +8 -6
  17. data/docs/internals/participants.md +5 -5
  18. data/docs/internals/prompt-caching.md +13 -6
  19. data/docs/internals/remote-sessions.md +3 -1
  20. data/docs/internals/web-frontend.md +1 -1
  21. data/docs/internals/working-set.md +2 -1
  22. data/docs/plugins.md +2 -0
  23. data/docs/releasing.md +1 -1
  24. data/docs/sessions.md +4 -4
  25. data/docs/system-one.md +3 -2
  26. data/docs/testing.md +1 -1
  27. data/lib/samagotchi/block_renderers.rb +5 -0
  28. data/lib/samagotchi/bootstrap/config_writer.rb +1 -1
  29. data/lib/samagotchi/broadcast_command.rb +3 -3
  30. data/lib/samagotchi/bundle_command/check.rb +1 -1
  31. data/lib/samagotchi/bundle_command/try/sandbox.rb +1 -1
  32. data/lib/samagotchi/bundles/dev/manifest.yml +3 -2
  33. data/lib/samagotchi/bundles/inline-widgets/manifest.yml +11 -0
  34. data/lib/samagotchi/bundles/inline-widgets/plugin.rb +76 -0
  35. data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
  36. data/lib/samagotchi/bundles/system/manifest.yml +2 -2
  37. data/lib/samagotchi/child_move.rb +7 -51
  38. data/lib/samagotchi/children_status.rb +1 -1
  39. data/lib/samagotchi/cli/parent_wait.rb +1 -1
  40. data/lib/samagotchi/config.rb +17 -112
  41. data/lib/samagotchi/context_command.rb +1 -2
  42. data/lib/samagotchi/context_window.rb +3 -3
  43. data/lib/samagotchi/desktop/macos.rb +1 -1
  44. data/lib/samagotchi/engine/context_judge.rb +21 -119
  45. data/lib/samagotchi/engine/guardrails.rb +4 -2
  46. data/lib/samagotchi/engine/messages.rb +9 -6
  47. data/lib/samagotchi/engine/models.rb +5 -2
  48. data/lib/samagotchi/engine/objectives.rb +30 -80
  49. data/lib/samagotchi/engine/participants.rb +15 -10
  50. data/lib/samagotchi/engine/plugins.rb +3 -1
  51. data/lib/samagotchi/engine/questions.rb +1 -1
  52. data/lib/samagotchi/engine/side_requests.rb +26 -13
  53. data/lib/samagotchi/engine/toolkit.rb +35 -191
  54. data/lib/samagotchi/engine/turn_end.rb +1 -1
  55. data/lib/samagotchi/engine/turn_helpers.rb +16 -39
  56. data/lib/samagotchi/engine/verify.rb +7 -143
  57. data/lib/samagotchi/engine.rb +166 -98
  58. data/lib/samagotchi/engine_peers.rb +1 -1
  59. data/lib/samagotchi/event_fanout.rb +18 -12
  60. data/lib/samagotchi/guardrail_wiring.rb +83 -82
  61. data/lib/samagotchi/guardrails/gate.rb +41 -40
  62. data/lib/samagotchi/guardrails/pair_boundary.rb +2 -1
  63. data/lib/samagotchi/guardrails/review_boundary.rb +2 -1
  64. data/lib/samagotchi/guardrails/targets.rb +28 -11
  65. data/lib/samagotchi/guardrails_command.rb +1 -1
  66. data/lib/samagotchi/host_config.rb +72 -1
  67. data/lib/samagotchi/host_model.rb +2 -6
  68. data/lib/samagotchi/host_registry.rb +1 -1
  69. data/lib/samagotchi/inline_widgets.rb +325 -0
  70. data/lib/samagotchi/kernel_loop.rb +215 -313
  71. data/lib/samagotchi/llm/anthropic_wire.rb +2 -2
  72. data/lib/samagotchi/llm/backend.rb +3 -1
  73. data/lib/samagotchi/llm/chat_loop.rb +87 -49
  74. data/lib/samagotchi/llm/native_backend.rb +3 -2
  75. data/lib/samagotchi/llm_context_apply.rb +54 -1
  76. data/lib/samagotchi/llm_context_forget.rb +5 -3
  77. data/lib/samagotchi/llm_context_notice.rb +1 -1
  78. data/lib/samagotchi/llm_context_strategy.rb +7 -9
  79. data/lib/samagotchi/llm_context_view.rb +9 -0
  80. data/lib/samagotchi/model_binding.rb +3 -3
  81. data/lib/samagotchi/model_price.rb +4 -10
  82. data/lib/samagotchi/model_profile.rb +6 -6
  83. data/lib/samagotchi/model_settings.rb +37 -2
  84. data/lib/samagotchi/note_command.rb +1 -2
  85. data/lib/samagotchi/objectives_audit.rb +4 -3
  86. data/lib/samagotchi/participants.rb +2 -2
  87. data/lib/samagotchi/plugin/context.rb +1 -1
  88. data/lib/samagotchi/plugin/prompt_sections.rb +7 -0
  89. data/lib/samagotchi/plugin/sessions.rb +2 -5
  90. data/lib/samagotchi/plugin/tool_spec.rb +5 -3
  91. data/lib/samagotchi/project_env.rb +1 -1
  92. data/lib/samagotchi/question_desk.rb +1 -1
  93. data/lib/samagotchi/reasoning_history.rb +3 -3
  94. data/lib/samagotchi/reminder_carry.rb +1 -1
  95. data/lib/samagotchi/reprefill_meter.rb +45 -0
  96. data/lib/samagotchi/review_command.rb +1 -1
  97. data/lib/samagotchi/revise_anchor.rb +1 -1
  98. data/lib/samagotchi/sampling_settings.rb +3 -3
  99. data/lib/samagotchi/self_report.rb +7 -7
  100. data/lib/samagotchi/session.rb +50 -7
  101. data/lib/samagotchi/session_access/local.rb +7 -5
  102. data/lib/samagotchi/session_access/remote.rb +17 -3
  103. data/lib/samagotchi/session_archive_command.rb +2 -2
  104. data/lib/samagotchi/session_chain.rb +7 -6
  105. data/lib/samagotchi/session_continue.rb +6 -5
  106. data/lib/samagotchi/session_delete_command.rb +1 -1
  107. data/lib/samagotchi/session_manager/delivery.rb +254 -0
  108. data/lib/samagotchi/session_manager/emptiness.rb +138 -0
  109. data/lib/samagotchi/session_manager/lifecycle.rb +274 -0
  110. data/lib/samagotchi/session_manager/listing.rb +189 -0
  111. data/lib/samagotchi/session_manager/removal.rb +190 -0
  112. data/lib/samagotchi/session_manager/spawn.rb +242 -0
  113. data/lib/samagotchi/session_manager/worker_command.rb +157 -0
  114. data/lib/samagotchi/session_manager.rb +26 -1540
  115. data/lib/samagotchi/session_metrics.rb +1 -1
  116. data/lib/samagotchi/sessions_command.rb +1 -1
  117. data/lib/samagotchi/share_command.rb +1 -1
  118. data/lib/samagotchi/starting_marker.rb +65 -0
  119. data/lib/samagotchi/steer_cut_after.rb +3 -3
  120. data/lib/samagotchi/system_one/recorder.rb +49 -0
  121. data/lib/samagotchi/system_prompt.rb +37 -30
  122. data/lib/samagotchi/terminal_ui/exit_lines.rb +1 -1
  123. data/lib/samagotchi/terminal_ui.rb +2 -6
  124. data/lib/samagotchi/testing/boundary.rb +7 -0
  125. data/lib/samagotchi/testing/bundle.rb +7 -5
  126. data/lib/samagotchi/testing/install.rb +3 -2
  127. data/lib/samagotchi/testing/sessions.rb +1 -1
  128. data/lib/samagotchi/thinking.rb +3 -3
  129. data/lib/samagotchi/token_usage.rb +1 -1
  130. data/lib/samagotchi/tool_activity.rb +15 -7
  131. data/lib/samagotchi/tool_declarations.rb +6 -6
  132. data/lib/samagotchi/tool_output_cut.rb +17 -0
  133. data/lib/samagotchi/tool_response.rb +4 -3
  134. data/lib/samagotchi/tool_runner.rb +96 -102
  135. data/lib/samagotchi/toolkit_binding.rb +40 -14
  136. data/lib/samagotchi/toolkit_command.rb +1 -1
  137. data/lib/samagotchi/tools/deferred_tools.rb +17 -21
  138. data/lib/samagotchi/tools/delegate.rb +10 -5
  139. data/lib/samagotchi/tools/delegate_result.rb +4 -4
  140. data/lib/samagotchi/tools/index_hint.rb +14 -0
  141. data/lib/samagotchi/tools/registry.rb +64 -44
  142. data/lib/samagotchi/tools/send_note.rb +4 -6
  143. data/lib/samagotchi/tools/tool_index.rb +150 -102
  144. data/lib/samagotchi/tools/tool_surface.rb +187 -0
  145. data/lib/samagotchi/turn_context.rb +68 -0
  146. data/lib/samagotchi/turn_stages/context_judge.rb +172 -0
  147. data/lib/samagotchi/turn_stages/objectives.rb +150 -0
  148. data/lib/samagotchi/turn_stages/toolkit_suggestion.rb +257 -0
  149. data/lib/samagotchi/turn_stages/verify_anchors.rb +168 -0
  150. data/lib/samagotchi/turn_stages/warmup.rb +82 -0
  151. data/lib/samagotchi/turn_state.rb +46 -4
  152. data/lib/samagotchi/version.rb +1 -1
  153. data/lib/samagotchi/vision_support.rb +3 -3
  154. data/lib/samagotchi/web/app.rb +15 -8
  155. data/lib/samagotchi/web/markdown_renderer.rb +39 -14
  156. data/lib/samagotchi/web/message_display.rb +1 -1
  157. data/lib/samagotchi/web/participant_access.rb +2 -2
  158. data/lib/samagotchi/web/public/app.js +5 -1
  159. data/lib/samagotchi/web/public/block_host.js +74 -199
  160. data/lib/samagotchi/web/public/block_renderers.css +2 -0
  161. data/lib/samagotchi/web/public/block_source.js +7 -4
  162. data/lib/samagotchi/web/public/block_watch.js +256 -0
  163. data/lib/samagotchi/web/public/composer_fold.js +3 -0
  164. data/lib/samagotchi/web/public/index.html +28 -9
  165. data/lib/samagotchi/web/public/inline_widgets.js +146 -0
  166. data/lib/samagotchi/web/public/owner_verify.js +1 -1
  167. data/lib/samagotchi/web/public/participant.js +1 -1
  168. data/lib/samagotchi/web/public/revise_stack.js +1 -1
  169. data/lib/samagotchi/web/public/stage_watch.js +6 -2
  170. data/lib/samagotchi/web/public/text_roots.js +1 -1
  171. data/lib/samagotchi/web/public/verify_target.js +1 -1
  172. data/lib/samagotchi/web/public/widget_picks.js +130 -0
  173. data/lib/samagotchi/web/public/widget_walk.js +230 -0
  174. data/lib/samagotchi/web/public/widgets.css +70 -0
  175. data/lib/samagotchi/web/session_access_routes.rb +3 -3
  176. data/lib/samagotchi/web/session_control.rb +3 -3
  177. data/lib/samagotchi/web/session_create.rb +3 -3
  178. data/lib/samagotchi/web/session_hub.rb +1 -1
  179. data/lib/samagotchi/web/session_show.rb +3 -3
  180. data/lib/samagotchi/web/session_summary.rb +3 -4
  181. data/lib/samagotchi/worker.rb +55 -27
  182. data/lib/samagotchi/worker_boot.rb +184 -0
  183. data/lib/samagotchi/worker_signals.rb +1 -1
  184. data/lib/samagotchi/worker_task_wakes.rb +2 -2
  185. metadata +29 -1
@@ -314,11 +314,13 @@ module Samagotchi
314
314
 
315
315
  # @return [String] the child's id, or an Error: line
316
316
  def self.follow_up(task, session:, parent:, state_dir:)
317
- id = Session.resolve_id(session, state_dir: state_dir)
317
+ id = Session.resolve!(session, state_dir: state_dir)
318
318
  child = begin
319
319
  Session.load(id, state_dir: state_dir)
320
- rescue ArgumentError
321
- return "Error: no session #{session} (list_sessions shows them)"
320
+ rescue Session::NotFound
321
+ raise # gone since resolve!: the outer rescue adds the list_sessions hint
322
+ rescue ArgumentError => e # a file that won't load
323
+ return "Error: #{e.message}"
322
324
  end
323
325
  unless child.parent_id == parent.id
324
326
  return "Error: #{id[0, 8]} is not a delegate of this session; send_note reaches any session"
@@ -340,6 +342,8 @@ module Samagotchi
340
342
  "Error: session #{id[0, 8]}: #{e.message}"
341
343
  rescue SessionManager::OwnedByTUI
342
344
  "Error: session #{id[0, 8]} is #{OwnerLock::TUI_BUSY}, which can't take a delegated message"
345
+ rescue Session::NotFound => e
346
+ "Error: #{e.message} (list_sessions shows them)"
343
347
  rescue Session::AmbiguousId => e
344
348
  "Error: #{e.message}"
345
349
  end
@@ -392,10 +396,11 @@ module Samagotchi
392
396
  # busy (a worker runs its turn), or running with a worker on its way
393
397
  # (ChildrenStatus asks it too).
394
398
  # @param summary [Hash] a children_of row, or the same keys: busy,
395
- # status, live, updated_at
399
+ # saved_status (the file's: the displayed status shows a worker on
400
+ # its way as idle), live, updated_at
396
401
  def self.running?(summary)
397
402
  return true if summary[:busy]
398
- return false unless summary[:status] == Session::STATUS_RUNNING && !summary[:live]
403
+ return false unless summary[:saved_status] == Session::STATUS_RUNNING && !summary[:live]
399
404
 
400
405
  Time.now - Time.iso8601(summary[:updated_at].to_s) < STARTING_GRACE_SECONDS
401
406
  rescue ArgumentError
@@ -29,7 +29,7 @@ module Samagotchi
29
29
  newest_running_child(peers.session_id, state_dir: sd) ||
30
30
  (return "No running delegate; list_sessions shows finished ones.")
31
31
  else
32
- id = Session.resolve_id(session.to_s.strip, state_dir: sd)
32
+ id = Session.resolve!(session.to_s.strip, state_dir: sd)
33
33
  child = Session.load(id, state_dir: sd)
34
34
  unless child.parent_id == peers.session_id
35
35
  return "Error: #{id[0, 8]} is not a delegate of this session (list_sessions marks them child)"
@@ -38,10 +38,10 @@ module Samagotchi
38
38
  id
39
39
  end
40
40
  DelegateWait.call(child_id, peers: peers, timeout: Delegate.parse_timeout(timeout))
41
- rescue Session::AmbiguousId => e
41
+ rescue Session::NotFound => e
42
+ "Error: #{e.message} (list_sessions shows them)"
43
+ rescue ArgumentError => e
42
44
  "Error: #{e.message}"
43
- rescue ArgumentError
44
- "Error: no session #{session.to_s.strip} (list_sessions shows them)"
45
45
  end
46
46
 
47
47
  def self.newest_running_child(parent_id, state_dir:)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Samagotchi
4
+ module Tools
5
+ # find_tools' answer text that the wire reads too, kept a leaf (no
6
+ # requires): ToolIndex writes CALL_HINT as a keyword answer's first
7
+ # line, and LLM::AnthropicWire strips it from a result whose tools it
8
+ # loads (they are called directly then).
9
+ module IndexHint
10
+ # A keyword answer's first line; call_tool is BuiltinCalls::VIA.
11
+ CALL_HINT = "Call one with call_tool(tool: \"<group>/<tool>\", args: {…})."
12
+ end
13
+ end
14
+ end
@@ -2,14 +2,23 @@
2
2
 
3
3
  module Samagotchi
4
4
  module Tools
5
+ # A reader of the turn's tools (Tools::ToolSurface) given none: a
6
+ # context or call that should carry the turn's carries nothing. Never
7
+ # taken as a failing preview's or targets' (ToolActivity, Guardrails::
8
+ # Targets re-raise it): a rule's acts_as must not silently go missing.
9
+ class MissingSurface < StandardError
10
+ def initialize(msg = "no ToolSurface: the turn's tools are missing (TurnContext#surface)") = super
11
+ end
12
+
5
13
  # The tools a session offers the model, in declaration order: the
6
14
  # built-ins (Tools::Builtins) as registered, then the ones bundles add,
7
15
  # by bundle and name. That order doesn't depend on which plugin
8
16
  # finished its init first or re-registered a changed tool, so the
9
17
  # tool list (part of the prompt's cached prefix) is the same in every
10
- # session with the same tools. The
11
- # native prompts and the chat path's tools: are rendered from #schemas,
12
- # and KernelLoop#dispatch runs a call through its entry's handler.
18
+ # session with the same tools. Its readers (the prompt, a turn's
19
+ # loops, the tool index, /toolkit) read the Tools::ToolSurface built
20
+ # from its lists (#entries, #reachable_names, #left_out_names,
21
+ # #removed_names, #stranded_names) under a toolkit and layers.
13
22
  class Registry
14
23
  # @!attribute name [String]
15
24
  # @!attribute schema [Hash] {name:, description:, parameters:}, as in
@@ -18,22 +27,45 @@ module Samagotchi
18
27
  # "Error: …" marks a failure); kctx is the KernelLoop's ToolContext
19
28
  # @!attribute label [String, nil] the activity line's action ("calling
20
29
  # tool" when nil); ToolActivity knows the built-ins' own
21
- # @!attribute preview [#call, nil] call → the activity line's params
22
- # @!attribute targets [#call, nil] call → what guardrail rules match
30
+ # @!attribute preview [#call, nil] (call, surface) → the activity line's
31
+ # params; +surface+ the turn's Tools::ToolSurface (nil: none)
32
+ # @!attribute targets [#call, nil] (call, surface) → what guardrail
33
+ # rules match. #register takes a one-argument preview or targets
34
+ # (call only, a plugin's) and gives it this form (.per_call)
23
35
  # @!attribute refs [#call, nil] call → the row's refs (a plugin's
24
36
  # refs:, RowRefs checks what it gives)
25
37
  # @!attribute source [String] "core", or the bundle that added it
26
38
  # @!attribute layer [Symbol, nil] the LLM context layer the tool
27
39
  # belongs to (LLMContextStrategy): offered only in a turn running
28
40
  # under it (forget_outputs, :forget); nil: always
29
- # @!attribute unwrap [#call, nil] (call, layers) → the call it stands
30
- # for, run in its place before the guardrail gate (call_tool on one
31
- # of chi's own tools: Tools::ToolIndex#unwrap), or nil to run it as
32
- # it is; nil on every other tool
41
+ # @!attribute unwrap [#call, nil] (call, surface) → the call it stands
42
+ # for under the turn's Tools::ToolSurface, run in its place before
43
+ # the guardrail gate (call_tool on one of chi's own tools:
44
+ # Tools::ToolIndex#unwrap), or nil to run it as it is; nil on every
45
+ # other tool
33
46
  Entry = Struct.new(:name, :schema, :handler, :label, :preview, :targets, :refs, :source, :layer, :unwrap,
34
47
  keyword_init: true) do
35
48
  def core? = source == "core"
36
49
 
50
+ # Its preview on +call+ (the row's params), nil without one.
51
+ # +surface+, the turn's Tools::ToolSurface (nil: none), is what
52
+ # call_tool's reads (what the turn reaches: it raises MissingSurface
53
+ # without one); a plugin's ignores it.
54
+ def preview_of(call, surface = nil) = preview&.call(call, surface)
55
+
56
+ # Its targets on +call+ (what guardrail rules match), nil without
57
+ # any; +surface+ as #preview_of.
58
+ def targets_of(call, surface = nil) = targets&.call(call, surface)
59
+
60
+ # +callable+ as (call, surface) → value, the one form an entry's
61
+ # preview and targets take: a one-argument one (call only) is given
62
+ # the call alone. The arity is read here, once, at registration.
63
+ def self.per_call(callable)
64
+ return callable if callable.nil? || callable.arity != 1
65
+
66
+ ->(call, _surface = nil) { callable.call(call) }
67
+ end
68
+
37
69
  # Offered in a turn under +layers+ (the turn's LLM context layers).
38
70
  def offered?(layers) = layer.nil? || Array(layers).include?(layer)
39
71
  end
@@ -43,17 +75,19 @@ module Samagotchi
43
75
  # name, and +groups+ the source groups registered when the plugins
44
76
  # loaded; +loaded+ the entries registered then (nil: whatever is
45
77
  # registered now), which alone decide whether the index's tools are
46
- # needed (Registry#index_needed?). +deferrable+ (→ the sources'
47
- # tools a host that loads tools may declare deferred) and +wrap+
48
- # (call → the call_tool call a direct call of one of them runs as, or
49
- # nil) are nil when nothing reaches them.
78
+ # needed (Registry#index_needed?). +deferrable+ (surface → the
79
+ # sources' tools a host that loads tools may declare deferred),
80
+ # +wrap+ ((call, surface) → the call_tool call a direct call of one
81
+ # of them runs as, or nil) and +unreached+ ((name, surface) → a
82
+ # source tool the toolkit doesn't reach) are nil when nothing reaches
83
+ # them; each is given the turn's Tools::ToolSurface.
50
84
  Index = Data.define(:source, :call_name, :groups, :deferrable, :wrap, :unreached, :loaded) do
51
85
  def initialize(source:, call_name:, groups:, deferrable: nil, wrap: nil, unreached: nil, loaded: nil)
52
86
  super(source: source, call_name: call_name, groups: Array(groups).map(&:to_s).uniq.freeze,
53
87
  deferrable: deferrable, wrap: wrap, unreached: unreached, loaded: loaded&.dup&.freeze)
54
88
  end
55
89
 
56
- def unreached_tool(name) = unreached&.call(name)
90
+ def unreached_tool(name, surface) = unreached&.call(name, surface)
57
91
  end
58
92
 
59
93
  def initialize
@@ -66,7 +100,7 @@ module Samagotchi
66
100
  attr_accessor :index
67
101
 
68
102
  # The session's resolved toolkit (Toolkit::Resolved), which the lists
69
- # below and #offered filter by; nil declares every tool (full). The
103
+ # below filter by; nil declares every tool (full). The
70
104
  # Engine sets it at a turn's start, before the prompt is built, and
71
105
  # only there: a request's declared tools never change mid-turn.
72
106
  attr_accessor :toolkit
@@ -81,8 +115,9 @@ module Samagotchi
81
115
  unwrap: nil)
82
116
  raise ArgumentError, "tool #{name} is already registered" if @entries.key?(name)
83
117
 
84
- @entries[name] = Entry.new(name: name, schema: schema, handler: handler, label: label, preview: preview,
85
- targets: targets, refs: refs, source: source, layer: layer, unwrap: unwrap)
118
+ @entries[name] = Entry.new(name: name, schema: schema, handler: handler, label: label,
119
+ preview: Entry.per_call(preview), targets: Entry.per_call(targets), refs: refs,
120
+ source: source, layer: layer, unwrap: unwrap)
86
121
  end
87
122
 
88
123
  # Remove a tool (a plugin's tool set changed after load).
@@ -94,22 +129,6 @@ module Samagotchi
94
129
 
95
130
  def key?(name) = @entries.key?(name.to_s)
96
131
 
97
- # The entry +name+ names when a turn under +layers+ offers it (the
98
- # one a call dispatches to).
99
- # @return [Entry, nil]
100
- def offered(name, layers: [])
101
- entry = self[name]
102
- entry if entry&.offered?(layers) && declared?(entry)
103
- end
104
-
105
- # The entry +name+ names when the session's toolkit leaves it out (a
106
- # real tool the model can't call here, by its name), nil otherwise.
107
- # @return [Entry, nil]
108
- def left_out(name, layers: [])
109
- entry = self[name]
110
- entry if entry && left_out?(entry, layers, @toolkit)
111
- end
112
-
113
132
  # The tools +toolkit+ (the session's by default) leaves out of a turn
114
133
  # under +layers+. The tool index's own aren't, unless it removes them:
115
134
  # they are declared when there is something to reach.
@@ -118,14 +137,6 @@ module Samagotchi
118
137
  ordered(@entries.values.select { |entry| left_out?(entry, layers, toolkit) }).map(&:name)
119
138
  end
120
139
 
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).
123
- # @return [Entry, nil]
124
- def indexed(name, layers: [], toolkit: @toolkit)
125
- entry = self[name]
126
- entry if entry && call_offered?(toolkit) && soft?(entry, layers, toolkit)
127
- end
128
-
129
140
  # The tools +toolkit+ leaves out of a turn under +layers+ that the tool
130
141
  # index reaches (call_tool), built-ins and plugin tools alike: not in
131
142
  # its kit's list, but not removed either (remove:, --no-tool, /toolkit
@@ -184,14 +195,23 @@ module Samagotchi
184
195
  ordered(@entries.values.select { |entry| entry.offered?(layers) && declared?(entry, toolkit) })
185
196
  end
186
197
 
187
- # @return [Array<Hash>] the schemas, in declaration order
188
- def schemas(layers: []) = entries(layers: layers).map(&:schema)
189
-
190
198
  def freeze
191
199
  @entries.freeze
192
200
  super
193
201
  end
194
202
 
203
+ # A frozen copy of its entry set, with its toolkit, reaching_kit and
204
+ # index as they are now (ToolkitBinding#publish!, on the turn thread):
205
+ # registering or unregistering here leaves it as it was. Shallow: the
206
+ # Entry Structs are this Registry's own, their fields set in place on
207
+ # the turn thread (Plugin::ToolSpec.apply, ToolIndex#refresh!).
208
+ # @return [Registry]
209
+ def snapshot
210
+ copy = dup
211
+ copy.instance_variable_set(:@entries, @entries.dup)
212
+ copy.freeze
213
+ end
214
+
195
215
  private
196
216
 
197
217
  # Declared by +toolkit+ (nil: every tool, full). The tool index's
@@ -22,7 +22,7 @@ module Samagotchi
22
22
  return "Error: this session's id is not known here" unless peers&.session_id
23
23
  return "Error: give the target session's id (list_sessions shows them)" if session.to_s.strip.empty?
24
24
 
25
- id = Session.resolve_id(session.to_s.strip, state_dir: peers.state_dir)
25
+ id = Session.resolve!(session.to_s.strip, state_dir: peers.state_dir)
26
26
  Session.load(id, state_dir: peers.state_dir)
27
27
  return "Error: #{id[0, 8]} is this session; send_note is for other sessions" if id == peers.session_id
28
28
 
@@ -33,12 +33,10 @@ module Samagotchi
33
33
 
34
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"
35
35
  "Queued a note for session #{id[0, 8]}: #{where}. It does not start a turn there."
36
- rescue Session::AmbiguousId => e
36
+ rescue Session::NotFound => e
37
+ "Error: #{e.message} (list_sessions shows them)"
38
+ rescue ArgumentError => e # AmbiguousId, SessionInbox::NoteRejected, a file that won't load
37
39
  "Error: #{e.message}"
38
- rescue SessionInbox::NoteRejected => e
39
- "Error: #{e.message}"
40
- rescue ArgumentError
41
- "Error: no session #{session.to_s.strip} (list_sessions shows them)"
42
40
  end
43
41
  end
44
42
  end