samagotchi 0.66.0 → 0.67.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -1
  3. data/docs/architecture.md +50 -28
  4. data/docs/cli.md +3 -1
  5. data/docs/configuration.md +50 -9
  6. data/docs/context.md +35 -1
  7. data/docs/desktop.md +90 -22
  8. data/docs/guardrails.md +8 -5
  9. data/docs/hooks.md +2 -2
  10. data/docs/internals/anthropic.md +156 -0
  11. data/docs/internals/background-tasks.md +1 -1
  12. data/docs/internals/bundles.md +2 -0
  13. data/docs/internals/engine.md +71 -29
  14. data/docs/internals/gemma4-contract.md +1 -1
  15. data/docs/internals/llm-context-bench.md +5 -4
  16. data/docs/internals/prompt-caching.md +26 -11
  17. data/docs/plugins.md +126 -23
  18. data/docs/sessions.md +6 -6
  19. data/docs/sub-agent.md +4 -1
  20. data/docs/testing.md +17 -3
  21. data/lib/samagotchi/bootstrap/probe.rb +4 -3
  22. data/lib/samagotchi/bootstrap_command.rb +13 -3
  23. data/lib/samagotchi/bridge/ring_buffer.rb +0 -5
  24. data/lib/samagotchi/bridge.rb +6 -3
  25. data/lib/samagotchi/bundles/claude-peer/manifest.yml +7 -6
  26. data/lib/samagotchi/bundles/claude-peer/plugin.rb +34 -313
  27. data/lib/samagotchi/bundles/claude-peer/scripts/chi-peer-SKILL.md +8 -3
  28. data/lib/samagotchi/bundles/claude-peer/skill_claude_peer.md +14 -6
  29. data/lib/samagotchi/bundles/codex-peer/manifest.yml +7 -6
  30. data/lib/samagotchi/bundles/codex-peer/plugin.rb +37 -312
  31. data/lib/samagotchi/bundles/codex-peer/scripts/chi-peer-SKILL.md +8 -3
  32. data/lib/samagotchi/bundles/codex-peer/skill_codex_peer.md +14 -6
  33. data/lib/samagotchi/bundles/mcp/manifest.yml +5 -5
  34. data/lib/samagotchi/bundles/mcp/plugin.rb +82 -294
  35. data/lib/samagotchi/bundles/system/config_modification_protocol.md +7 -3
  36. data/lib/samagotchi/bundles/system/manifest.yml +3 -3
  37. data/lib/samagotchi/bundles/system/self_map.md +1 -1
  38. data/lib/samagotchi/config.rb +15 -7
  39. data/lib/samagotchi/context_absorber.rb +66 -13
  40. data/lib/samagotchi/desktop/macos/AgentTerminal.swift +260 -0
  41. data/lib/samagotchi/desktop/macos/App.swift +3 -3
  42. data/lib/samagotchi/desktop/macos/ChiRunner.swift +5 -2
  43. data/lib/samagotchi/desktop/macos/Images.swift +2 -2
  44. data/lib/samagotchi/desktop/macos/Kitty.swift +33 -197
  45. data/lib/samagotchi/desktop/macos/Panel.swift +118 -82
  46. data/lib/samagotchi/desktop/macos/ProcessRunner.swift +1 -1
  47. data/lib/samagotchi/desktop/macos/Tmux.swift +239 -0
  48. data/lib/samagotchi/desktop/macos.rb +84 -13
  49. data/lib/samagotchi/desktop_command.rb +14 -3
  50. data/lib/samagotchi/engine.rb +96 -191
  51. data/lib/samagotchi/engine_peers.rb +3 -0
  52. data/lib/samagotchi/guardrail_wiring.rb +1 -1
  53. data/lib/samagotchi/guardrails/approval.rb +1 -1
  54. data/lib/samagotchi/guardrails/approvals.rb +1 -1
  55. data/lib/samagotchi/guardrails/rules.rb +1 -1
  56. data/lib/samagotchi/guardrails/targets.rb +1 -1
  57. data/lib/samagotchi/hook_routes.rb +151 -0
  58. data/lib/samagotchi/hooks/registry.rb +2 -2
  59. data/lib/samagotchi/host_keys.rb +147 -0
  60. data/lib/samagotchi/llm/adapter_common.rb +138 -0
  61. data/lib/samagotchi/llm/anthropic_messages.rb +28 -105
  62. data/lib/samagotchi/llm/api_key.rb +23 -7
  63. data/lib/samagotchi/llm/chat_loop.rb +10 -6
  64. data/lib/samagotchi/llm/openai_chat.rb +9 -92
  65. data/lib/samagotchi/llm/refusal_memo.rb +29 -0
  66. data/lib/samagotchi/llm_context_override.rb +1 -0
  67. data/lib/samagotchi/peer_sources.rb +58 -0
  68. data/lib/samagotchi/plugin/api.rb +64 -155
  69. data/lib/samagotchi/plugin/loader.rb +4 -2
  70. data/lib/samagotchi/plugin/peer.rb +394 -0
  71. data/lib/samagotchi/plugin/tool_spec.rb +166 -0
  72. data/lib/samagotchi/plugin_tasks.rb +2 -2
  73. data/lib/samagotchi/row_refs.rb +1 -1
  74. data/lib/samagotchi/self_report.rb +5 -3
  75. data/lib/samagotchi/send_command.rb +21 -11
  76. data/lib/samagotchi/session_continue.rb +58 -15
  77. data/lib/samagotchi/session_inbox.rb +0 -13
  78. data/lib/samagotchi/tool_call_parser.rb +1 -1
  79. data/lib/samagotchi/toolkit_binding.rb +138 -0
  80. data/lib/samagotchi/tools/context_read.rb +14 -0
  81. data/lib/samagotchi/tools/execute.rb +12 -12
  82. data/lib/samagotchi/tools/registry.rb +1 -1
  83. data/lib/samagotchi/tools/task_create.rb +6 -6
  84. data/lib/samagotchi/tools/task_get.rb +13 -13
  85. data/lib/samagotchi/tools/task_list.rb +6 -6
  86. data/lib/samagotchi/tools/task_record.rb +52 -0
  87. data/lib/samagotchi/tools/task_runtime.rb +55 -91
  88. data/lib/samagotchi/tools/task_stop.rb +6 -6
  89. data/lib/samagotchi/tools/task_wait.rb +12 -13
  90. data/lib/samagotchi/tools/tool_index.rb +454 -0
  91. data/lib/samagotchi/update_command.rb +9 -6
  92. data/lib/samagotchi/version.rb +1 -1
  93. data/lib/samagotchi/web/app.rb +16 -9
  94. data/lib/samagotchi/web/capabilities.rb +1 -1
  95. data/lib/samagotchi/web/public/activity.js +2 -2
  96. data/lib/samagotchi/web/public/annotate_actions.js +1 -1
  97. data/lib/samagotchi/web/public/app.js +31 -36
  98. data/lib/samagotchi/web/public/chains.js +1 -1
  99. data/lib/samagotchi/web/public/commands.js +2 -2
  100. data/lib/samagotchi/web/public/composer_size.js +2 -2
  101. data/lib/samagotchi/web/public/copy.js +1 -1
  102. data/lib/samagotchi/web/public/ctx.js +1 -1
  103. data/lib/samagotchi/web/public/delivery_line.js +4 -4
  104. data/lib/samagotchi/web/public/families_view.js +1 -1
  105. data/lib/samagotchi/web/public/format.js +11 -7
  106. data/lib/samagotchi/web/public/images.js +25 -2
  107. data/lib/samagotchi/web/public/llm_context_chip.js +2 -2
  108. data/lib/samagotchi/web/public/notify.js +6 -4
  109. data/lib/samagotchi/web/public/question_card.js +1 -1
  110. data/lib/samagotchi/web/public/ref_scan.js +1 -1
  111. data/lib/samagotchi/web/public/session_state.js +14 -0
  112. data/lib/samagotchi/web/public/session_title.js +2 -3
  113. data/lib/samagotchi/web/public/sessions_list.js +0 -8
  114. data/lib/samagotchi/web/public/stage_model.js +2 -2
  115. data/lib/samagotchi/web/public/strip.js +3 -3
  116. data/lib/samagotchi/web/public/task_stop.js +3 -2
  117. data/lib/samagotchi/web/public/turn_events.js +1 -1
  118. data/lib/samagotchi/web/public/turn_html.js +2 -2
  119. data/lib/samagotchi/web/public/turn_model.js +2 -2
  120. data/lib/samagotchi/web/public/turn_view.js +1 -1
  121. data/lib/samagotchi/web/server.rb +0 -4
  122. data/lib/samagotchi/worker.rb +11 -8
  123. data/lib/samagotchi/worker_inbound.rb +36 -4
  124. data/lib/samagotchi/worker_wakes.rb +67 -7
  125. metadata +14 -1
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
3
  require_relative "../toolkit"
5
4
  require_relative "../log"
6
5
  require_relative "../hooks/registry"
7
- require_relative "../tools/args"
8
6
  require_relative "../commands/registry"
9
7
  require_relative "service"
10
8
  require_relative "tool_result"
9
+ require_relative "tool_spec"
11
10
 
12
11
  module Samagotchi
13
12
  module Plugin
@@ -16,8 +15,6 @@ module Samagotchi
16
15
  # #register returns, so a plugin that raises halfway adds nothing.
17
16
  class Api
18
17
  COMMAND_NAME = %r{\A/[a-z][a-z0-9_-]{0,31}\z}
19
- TOOL_NAME = /\A[a-z][a-z0-9_]{0,47}\z/
20
- PARAM_NAME = /\A[a-z_][a-z0-9_]*\z/i
21
18
  ANNOTATION_LABEL_MAX = 40
22
19
  ANNOTATIONS_MAX = 5
23
20
 
@@ -38,6 +35,8 @@ module Samagotchi
38
35
  @services = []
39
36
  @inits = []
40
37
  @toolkits = []
38
+ @peer_sources = []
39
+ @tool_source = nil
41
40
  @committed = false
42
41
  end
43
42
 
@@ -118,11 +117,11 @@ module Samagotchi
118
117
  # a list of {kind:, target:, label:, actions:} (RowRefs, Ref). Run
119
118
  # once per call on the tool path, so it must be cheap (no I/O)
120
119
  def tool(name, description, **, &)
121
- spec = Api.tool_spec(name, description, **, &)
122
- if (taken = @registries.tools[spec[:name]])
123
- raise ArgumentError, "tool #{spec[:name]} is already registered (#{taken.source})"
120
+ spec = ToolSpec.build(name, description, **, &)
121
+ if (taken = @registries.tools[spec.name])
122
+ raise ArgumentError, "tool #{spec.name} is already registered (#{taken.source})"
124
123
  end
125
- raise ArgumentError, "tool #{spec[:name]} is registered twice" if @tools.any? { |tool| tool[:name] == spec[:name] }
124
+ raise ArgumentError, "tool #{spec.name} is registered twice" if @tools.any? { |tool| tool.name == spec.name }
126
125
 
127
126
  @tools << spec
128
127
  nil
@@ -146,7 +145,7 @@ module Samagotchi
146
145
  yield set
147
146
  if (stage = @registries.stage_tools)
148
147
  stage.call(@bundle, set.specs, @context)
149
- elsif Api.apply_tools(@registries.tools, @bundle, set.specs, @context)[:changed]
148
+ elsif ToolSpec.apply(@registries.tools, @bundle, set.specs, @context)[:changed]
150
149
  tools_changed!
151
150
  end
152
151
  nil
@@ -154,7 +153,7 @@ module Samagotchi
154
153
 
155
154
  # What #replace_tools' block declares tools on.
156
155
  class ToolSet
157
- # @return [Array<Hash>]
156
+ # @return [Array<ToolSpec>]
158
157
  attr_reader :specs
159
158
 
160
159
  def initialize
@@ -163,8 +162,8 @@ module Samagotchi
163
162
 
164
163
  # As Api#tool.
165
164
  def tool(name, description, **, &)
166
- spec = Api.tool_spec(name, description, **, &)
167
- raise ArgumentError, "tool #{spec[:name]} is registered twice" if @specs.any? { |s| s[:name] == spec[:name] }
165
+ spec = ToolSpec.build(name, description, **, &)
166
+ raise ArgumentError, "tool #{spec.name} is registered twice" if @specs.any? { |s| s.name == spec.name }
168
167
 
169
168
  @specs << spec
170
169
  nil
@@ -250,6 +249,32 @@ module Samagotchi
250
249
  nil
251
250
  end
252
251
 
252
+ # Publish tools into the session's tool index instead of declaring
253
+ # them one by one (docs/plugins.md, "Publishing tools to the index"):
254
+ # the model searches them with find_tools and calls one with
255
+ # call_tool (<group>/<tool>), which chi declares once, after the
256
+ # plugins load, when a source registered. Returns the source: its
257
+ # #publish(groups) (ToolIndex::Group values, each with its
258
+ # ToolIndex::Tool values) swaps in the plugin's whole list, from any
259
+ # thread, at any time, and changes only what searches answer.
260
+ # @param label [String] the call tool's row label (default: the bundle)
261
+ # @param call [#call] (tool, args, ctx) → the call's result, as a
262
+ # tool's block returns it; +tool+ a ToolIndex::Tool the index looked
263
+ # up and checked, +args+ typed by its schema
264
+ # @param acts_as [#call] (group, tool) → the name guardrail rules
265
+ # match for a tool from its names alone (one not published yet)
266
+ # @param wait [#call, nil] (group) waits for a :starting group's start
267
+ # @param prefix [String, nil] a bare name with it is taken as an
268
+ # acts_as as given
269
+ # @return [Tools::ToolIndex::Source]
270
+ def tool_source(call:, acts_as:, label: @bundle, wait: nil, prefix: nil)
271
+ raise ArgumentError, "this chi has no tool index (plugins: false)" unless @registries.tool_index
272
+ raise ArgumentError, "tool_source is declared twice" if @tool_source
273
+
274
+ @tool_source = @registries.tool_index.source(label: label, call: call, acts_as: acts_as, wait: wait,
275
+ prefix: prefix, context: @context)
276
+ end
277
+
253
278
  TOOLKIT_NAME = /\A[a-z][a-z0-9_-]{0,31}\z/
254
279
 
255
280
  # A named toolkit sessions can be put on (/toolkit NAME, chi --toolkit,
@@ -273,11 +298,29 @@ module Samagotchi
273
298
  nil
274
299
  end
275
300
 
301
+ # A context source that holds a peer agent's answers (the peer
302
+ # bundles' claude-review, codex-review): +label+ is the peer's name
303
+ # ("Codex", PeerSources::LABEL), said in chi's own lines. Such a
304
+ # source, when it is a pushed one of the session's own, wakes on
305
+ # context.peer_turns rather than the 10-minute window, with the peer
306
+ # line (docs/context.md, Waking). Declared here, in-process, never by
307
+ # a source file; gone with the bundle.
308
+ def peer_source(name, label:)
309
+ raise ArgumentError, "this chi has no peer sources (plugins: false)" unless @registries.peer_sources
310
+
311
+ name = @registries.peer_sources.check!(name, label: label, bundle: @bundle)
312
+ raise ArgumentError, "peer source #{name} is declared twice" if @peer_sources.any? { |item| item[:name] == name }
313
+
314
+ @peer_sources << { name: name, label: label.to_s }
315
+ nil
316
+ end
317
+
276
318
  # Register what was staged. Called by Loader after #register. What
277
319
  # can only be checked now (an annotation's command) is checked before
278
320
  # anything is registered: a raise here leaves the registries as they were.
279
321
  def commit!
280
322
  check_annotations!
323
+ @peer_sources.each { |item| @registries.peer_sources.check!(item[:name], label: item[:label], bundle: @bundle) }
281
324
  context = @context
282
325
  @commands.each do |cmd|
283
326
  block = cmd[:block]
@@ -287,7 +330,7 @@ module Samagotchi
287
330
  block.call(args, context)
288
331
  end
289
332
  end
290
- @tools.each { |tool| @registries.tools.register(tool[:name], source: @bundle, **Api.entry_fields(tool, context)) }
333
+ @tools.each { |tool| @registries.tools.register(tool.name, source: @bundle, **tool.entry_fields(context)) }
291
334
  @hooks.each do |hook|
292
335
  block = hook[:block]
293
336
  # Logged only (echo: false): a turn's live region is on screen.
@@ -307,12 +350,15 @@ module Samagotchi
307
350
  end
308
351
  end
309
352
  @registries.toolkits&.concat(@toolkits)
353
+ @registries.tool_index.add(@tool_source) if @tool_source
354
+ @peer_sources.each { |item| @registries.peer_sources.declare(item[:name], label: item[:label], bundle: @bundle) }
310
355
  @committed = true
311
356
  end
312
357
 
313
358
  # @return [Hash] how many of each it registered (for the log)
314
359
  def counts = { commands: @commands.size, annotations: @annotations.size, tools: @tools.size, hooks: @hooks.size,
315
- services: @services.size, inits: @inits.size, toolkits: @toolkits.size }
360
+ services: @services.size, inits: @inits.size, toolkits: @toolkits.size,
361
+ tool_sources: @tool_source ? 1 : 0, peer_sources: @peer_sources.size }
316
362
 
317
363
  # Each annotation's command is one #command staged.
318
364
  def check_annotations!
@@ -325,147 +371,10 @@ module Samagotchi
325
371
  end
326
372
  private :check_annotations!
327
373
 
328
- # A checked tool declaration (#tool's arguments: Api#tool and
329
- # ToolSet#tool pass theirs on, so a new keyword is added here).
330
- # @return [Hash] {name:, schema:, label:, preview:, targets:, refs:, block:}
331
- def self.tool_spec(name, description, params: {}, schema: nil, label: nil, preview: nil, targets: nil, refs: nil,
332
- &block)
333
- raise ArgumentError, "tool #{name.inspect} needs a block" unless block
334
-
335
- name = name.to_s
336
- raise ArgumentError, "tool name #{name.inspect} must be a-z, 0-9 and _ (at most 48)" unless name.match?(TOOL_NAME)
337
- raise ArgumentError, "tool #{name}: preview must respond to #call" if preview && !preview.respond_to?(:call)
338
- raise ArgumentError, "tool #{name}: targets must respond to #call" if targets && !targets.respond_to?(:call)
339
- raise ArgumentError, "tool #{name}: refs must respond to #call" if refs && !refs.respond_to?(:call)
340
-
341
- parameters = schema ? schema_parameters(name, schema) : params_schema(name, params)
342
- { name: name, schema: { name: name, description: description.to_s, parameters: parameters },
343
- label: label&.to_s, preview: preview, targets: targets, refs: refs, block: block }
344
- end
345
-
346
- # Make +bundle+'s tools in +registry+ the +specs+: its tools not in
347
- # them go; a new one, or one whose schema or label changed, is
348
- # registered (a changed one again, in its place: Tools::Registry
349
- # orders bundle tools by bundle and name); an unchanged one is kept
350
- # as it is, but for its block, preview:, targets: and refs:, swapped
351
- # in place: none of them reaches the prompt, so not a change (that
352
- # would build the prompts again). A name
353
- # another source has is left out. Called on the turn thread
354
- # (Engine#apply_staged_tools!).
355
- # @return [Hash] {changed: Boolean, skipped: [String] (why)}
356
- def self.apply_tools(registry, bundle, specs, context)
357
- wanted = specs.to_h { |spec| [spec[:name], spec] }
358
- changed = false
359
- registry.by_source(bundle).each do |entry|
360
- if (spec = wanted[entry.name]) && spec[:schema] == entry.schema && spec[:label] == entry.label
361
- entry_fields(spec, context).slice(:handler, :preview, :targets, :refs).each { |key, value| entry[key] = value }
362
- next
363
- end
364
-
365
- registry.unregister(entry.name)
366
- changed = true
367
- end
368
- skipped = []
369
- specs.each do |spec|
370
- if (taken = registry[spec[:name]])
371
- skipped << "tool #{spec[:name]} is already registered (#{taken.source})" unless taken.source == bundle
372
- next
373
- end
374
-
375
- registry.register(spec[:name], source: bundle, **entry_fields(spec, context))
376
- changed = true
377
- end
378
- { changed: changed, skipped: skipped }
379
- end
380
-
381
- # Registry#register's keywords for a tool declaration: its block
382
- # wrapped as a handler (typed args, the Context), preview, targets, refs.
383
- def self.entry_fields(spec, context)
384
- block = spec[:block]
385
- parameters = spec[:schema][:parameters]
386
- preview = per_call(spec[:preview], parameters)
387
- {
388
- schema: spec[:schema], label: spec[:label],
389
- handler: lambda { |call, _kctx|
390
- result = block.call(Api.args_of(call, parameters), context)
391
- Api.result_text(result)
392
- },
393
- preview: preview && ->(call) { preview.call(call)&.to_s },
394
- targets: per_call(spec[:targets], parameters),
395
- refs: per_call(spec[:refs], parameters)
396
- }
397
- end
398
-
399
- # A plugin's args → value lambda (+fn+) as call → value: it gets the
400
- # call's typed args (Api.args_of), as the tool's block does. nil
401
- # without one.
402
- def self.per_call(fn, parameters)
403
- fn && ->(call) { fn.call(Api.args_of(call, parameters)) }
404
- end
405
-
406
- # A tool's result as the model reads it: a String (a ToolResult too,
407
- # with its images) as it is, a Hash or Array as JSON (its #to_s is
408
- # Hash#inspect, which changes with the Ruby version), else #to_s.
409
- def self.result_text(result)
410
- case result
411
- when String then result
412
- when Hash, Array then JSON.generate(result)
413
- else result.to_s
414
- end
415
- rescue JSON::GeneratorError
416
- result.to_s
417
- end
418
-
419
- # A tool call's arguments as a plugin sees them: the parsers' args:
420
- # (a call built without one, in a spec say: the call without its
421
- # name), string keys, typed by +parameters+, frozen.
422
- def self.args_of(call, parameters = nil)
423
- given = call[:args].is_a?(Hash) ? call[:args] : call.reject { |key, _| %i[name args].include?(key) }
424
- Tools::Args.coerce(given, parameters).freeze
425
- end
426
-
427
- # A JSON Schema with symbol keys (property names included), as
428
- # ToolDeclarations::TOOL_SCHEMAS has them.
429
- def self.symbolize(value)
430
- case value
431
- when Hash then value.to_h { |key, item| [key.to_sym, symbolize(item)] }
432
- when Array then value.map { |item| symbolize(item) }
433
- else value
434
- end
435
- end
436
-
437
- # The parameters object from params: (name => property spec).
438
- def self.params_schema(name, params)
439
- raise ArgumentError, "tool #{name}: params must be a Hash" unless params.is_a?(Hash)
440
-
441
- required = []
442
- properties = params.each_with_object({}) do |(param, spec), acc|
443
- param = param.to_s
444
- raise ArgumentError, "tool #{name}: parameter name #{param.inspect} is not a plain word" unless param.match?(PARAM_NAME)
445
- raise ArgumentError, "tool #{name}: parameter #{param} must be a Hash {type:, description:}" unless spec.is_a?(Hash)
446
-
447
- spec = Api.symbolize(spec)
448
- required << param if spec.delete(:required) == true
449
- acc[param.to_sym] = { type: (spec.delete(:type) || "string").to_s, description: spec.delete(:description).to_s }.merge(spec)
450
- end
451
- { type: "object", properties: properties, required: required }
452
- end
453
- private_class_method :params_schema
454
-
455
- # The parameters object from schema: (a JSON Schema object).
456
- def self.schema_parameters(name, schema)
457
- raise ArgumentError, "tool #{name}: schema must be a Hash" unless schema.is_a?(Hash)
458
-
459
- schema = Api.symbolize(schema)
460
- raise ArgumentError, "tool #{name}: schema must be {type: \"object\", properties: {…}}" unless schema.fetch(:type, "object").to_s == "object"
461
-
462
- properties = schema[:properties] || {}
463
- raise ArgumentError, "tool #{name}: schema properties must be a Hash" unless properties.is_a?(Hash)
464
-
465
- { type: "object", properties: properties, required: Array(schema[:required]).map(&:to_s) }
466
- .merge(schema.except(:type, :properties, :required))
467
- end
468
- private_class_method :schema_parameters
374
+ # A checked tool declaration (ToolSpec.build), for bundles: the mcp
375
+ # bundle's token estimate calls it, installed ones included.
376
+ # @return [ToolSpec]
377
+ def self.tool_spec(...) = ToolSpec.build(...)
469
378
  end
470
379
  end
471
380
  end
@@ -17,9 +17,11 @@ module Samagotchi
17
17
  # Engine#shutdown stops; +stage_tools+ is (bundle, specs, context),
18
18
  # chi.replace_tools' set, which the Engine applies on its turn thread;
19
19
  # +init+ adds a chi.init task (Engine#add_init_task); +toolkits+ is the
20
- # Engine's list of the plugins' toolkits (chi.toolkit: Toolkit::Kit).
20
+ # Engine's list of the plugins' toolkits (chi.toolkit: Toolkit::Kit);
21
+ # +tool_index+ the Engine's Tools::ToolIndex (chi.tool_source);
22
+ # +peer_sources+ the Engine's PeerSources (chi.peer_source).
21
23
  Registries = Struct.new(:commands, :tools, :hooks, :context_for, :tools_changed, :services, :stage_tools, :init,
22
- :toolkits, keyword_init: true)
24
+ :toolkits, :tool_index, :peer_sources, keyword_init: true)
23
25
 
24
26
  # Loads installed bundles' plugins (manifest plugin: {file:, sha256:})
25
27
  # into an Engine's registries (docs/plugins.md).