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.
Files changed (234) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -1
  3. data/bin/chi +2 -1
  4. data/docs/architecture.md +22 -13
  5. data/docs/bundles.md +3 -1
  6. data/docs/cli.md +9 -8
  7. data/docs/configuration.md +36 -4
  8. data/docs/guardrails.md +9 -5
  9. data/docs/hooks.md +11 -5
  10. data/docs/internals/anthropic.md +1 -1
  11. data/docs/internals/attached-context.md +3 -2
  12. data/docs/internals/background-tasks.md +1 -1
  13. data/docs/internals/bridge.md +83 -6
  14. data/docs/internals/bundles.md +28 -18
  15. data/docs/internals/context-telemetry.md +4 -3
  16. data/docs/internals/engine.md +641 -937
  17. data/docs/internals/llm-context-forget.md +8 -3
  18. data/docs/internals/objectives.md +2 -2
  19. data/docs/internals/participants.md +3 -2
  20. data/docs/internals/prompt-caching.md +4 -1
  21. data/docs/internals/remote-sessions.md +8 -3
  22. data/docs/internals/web-frontend.md +3 -2
  23. data/docs/internals/working-set.md +2 -2
  24. data/docs/memory.md +13 -2
  25. data/docs/plugins.md +35 -2
  26. data/docs/sessions.md +11 -11
  27. data/docs/sub-agent.md +17 -2
  28. data/docs/system-one.md +3 -2
  29. data/docs/testing.md +53 -2
  30. data/lib/samagotchi/answer_command.rb +2 -1
  31. data/lib/samagotchi/archive_store.rb +4 -3
  32. data/lib/samagotchi/bootstrap/config_writer.rb +1 -1
  33. data/lib/samagotchi/bootstrap_command.rb +3 -2
  34. data/lib/samagotchi/bridge/card_places.rb +10 -4
  35. data/lib/samagotchi/bridge/card_store.rb +3 -0
  36. data/lib/samagotchi/bridge/turn_accumulator.rb +109 -9
  37. data/lib/samagotchi/bridge/turn_drawings.rb +265 -0
  38. data/lib/samagotchi/bridge.rb +31 -6
  39. data/lib/samagotchi/broadcast/recipients.rb +8 -8
  40. data/lib/samagotchi/broadcast/triage_model.rb +1 -1
  41. data/lib/samagotchi/broadcast_command.rb +3 -3
  42. data/lib/samagotchi/bundle_command/check.rb +18 -0
  43. data/lib/samagotchi/bundle_command/try/sandbox.rb +2 -2
  44. data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
  45. data/lib/samagotchi/bundle_command.rb +5 -2
  46. data/lib/samagotchi/bundles/known-names/manifest.yml +4 -7
  47. data/lib/samagotchi/bundles/known-names/{hooks/known_names.rb → plugin.rb} +17 -0
  48. data/lib/samagotchi/bundles/source-links/manifest.yml +4 -7
  49. data/lib/samagotchi/bundles/source-links/{hooks/source_links.rb → plugin.rb} +23 -4
  50. data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
  51. data/lib/samagotchi/bundles/system/manifest.yml +3 -3
  52. data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
  53. data/lib/samagotchi/child_move.rb +13 -57
  54. data/lib/samagotchi/child_reports.rb +2 -2
  55. data/lib/samagotchi/children_status.rb +15 -21
  56. data/lib/samagotchi/cli/parent_wait.rb +9 -23
  57. data/lib/samagotchi/config.rb +20 -112
  58. data/lib/samagotchi/context_command.rb +1 -2
  59. data/lib/samagotchi/context_sources.rb +5 -4
  60. data/lib/samagotchi/context_window.rb +9 -12
  61. data/lib/samagotchi/desktop/macos.rb +1 -1
  62. data/lib/samagotchi/effective_model.rb +57 -22
  63. data/lib/samagotchi/engine/context_judge.rb +19 -11
  64. data/lib/samagotchi/engine/messages.rb +1 -1
  65. data/lib/samagotchi/engine/models.rb +5 -2
  66. data/lib/samagotchi/engine/objectives.rb +29 -11
  67. data/lib/samagotchi/engine/plugins.rb +1 -1
  68. data/lib/samagotchi/engine/recap.rb +8 -4
  69. data/lib/samagotchi/engine/side_requests.rb +21 -11
  70. data/lib/samagotchi/engine/toolkit.rb +10 -9
  71. data/lib/samagotchi/engine/turn_end.rb +1 -1
  72. data/lib/samagotchi/engine/turn_helpers.rb +7 -7
  73. data/lib/samagotchi/engine/verify.rb +1 -1
  74. data/lib/samagotchi/engine.rb +43 -12
  75. data/lib/samagotchi/events.rb +3 -2
  76. data/lib/samagotchi/extension_load.rb +6 -3
  77. data/lib/samagotchi/guardrails/targets.rb +24 -9
  78. data/lib/samagotchi/guardrails_command.rb +1 -1
  79. data/lib/samagotchi/host_address.rb +52 -0
  80. data/lib/samagotchi/host_config.rb +72 -1
  81. data/lib/samagotchi/host_model.rb +2 -6
  82. data/lib/samagotchi/host_registry.rb +58 -46
  83. data/lib/samagotchi/kernel_loop.rb +53 -124
  84. data/lib/samagotchi/llm/anthropic_wire.rb +2 -2
  85. data/lib/samagotchi/llm/chat_loop.rb +40 -21
  86. data/lib/samagotchi/llm/native_backend.rb +1 -1
  87. data/lib/samagotchi/llm_context_apply.rb +54 -1
  88. data/lib/samagotchi/llm_context_notice.rb +1 -1
  89. data/lib/samagotchi/llm_context_strategy.rb +29 -67
  90. data/lib/samagotchi/llm_context_view.rb +9 -0
  91. data/lib/samagotchi/memory_bundle/asset_installer.rb +71 -76
  92. data/lib/samagotchi/memory_bundle/builder.rb +2 -1
  93. data/lib/samagotchi/memory_bundle/file_actions.rb +193 -0
  94. data/lib/samagotchi/memory_bundle/file_digest.rb +13 -0
  95. data/lib/samagotchi/memory_bundle/file_state.rb +152 -0
  96. data/lib/samagotchi/memory_bundle/installed_copy.rb +2 -1
  97. data/lib/samagotchi/memory_bundle/installed_manifest.rb +8 -1
  98. data/lib/samagotchi/memory_bundle/installer.rb +185 -169
  99. data/lib/samagotchi/memory_bundle/manifest.rb +14 -4
  100. data/lib/samagotchi/memory_bundle/merger.rb +69 -65
  101. data/lib/samagotchi/memory_bundle/plugin_ref.rb +6 -5
  102. data/lib/samagotchi/memory_bundle/profile.rb +5 -5
  103. data/lib/samagotchi/memory_bundle/provenance.rb +15 -1
  104. data/lib/samagotchi/memory_bundle/remerge.rb +25 -12
  105. data/lib/samagotchi/memory_bundle/shipped_update.rb +51 -6
  106. data/lib/samagotchi/memory_bundle/status.rb +14 -20
  107. data/lib/samagotchi/memory_bundle/system_bundle.rb +24 -6
  108. data/lib/samagotchi/memory_bundle/trash.rb +23 -8
  109. data/lib/samagotchi/memory_bundle/uninstaller.rb +16 -11
  110. data/lib/samagotchi/memory_usage.rb +1 -1
  111. data/lib/samagotchi/model_lookup.rb +75 -0
  112. data/lib/samagotchi/model_price.rb +4 -10
  113. data/lib/samagotchi/model_profile.rb +7 -6
  114. data/lib/samagotchi/model_settings.rb +37 -2
  115. data/lib/samagotchi/models_command.rb +1 -1
  116. data/lib/samagotchi/note_command.rb +2 -3
  117. data/lib/samagotchi/objectives_audit.rb +4 -3
  118. data/lib/samagotchi/participant_mode.rb +2 -1
  119. data/lib/samagotchi/participants.rb +2 -2
  120. data/lib/samagotchi/plugin/api.rb +41 -6
  121. data/lib/samagotchi/plugin/context.rb +1 -1
  122. data/lib/samagotchi/plugin/loader.rb +33 -10
  123. data/lib/samagotchi/plugin/sessions.rb +3 -6
  124. data/lib/samagotchi/plugin/tool_spec.rb +5 -3
  125. data/lib/samagotchi/project_env.rb +1 -1
  126. data/lib/samagotchi/reasoning_history.rb +14 -20
  127. data/lib/samagotchi/recap_place.rb +27 -0
  128. data/lib/samagotchi/reminder_carry.rb +1 -1
  129. data/lib/samagotchi/reply_wait/liveness.rb +38 -0
  130. data/lib/samagotchi/reply_wait/poll.rb +44 -15
  131. data/lib/samagotchi/reply_wait/target.rb +113 -0
  132. data/lib/samagotchi/reply_wait.rb +22 -2
  133. data/lib/samagotchi/reprefill_meter.rb +45 -0
  134. data/lib/samagotchi/review_command.rb +1 -1
  135. data/lib/samagotchi/sampling_settings.rb +9 -16
  136. data/lib/samagotchi/self_report.rb +36 -37
  137. data/lib/samagotchi/send_command.rb +17 -23
  138. data/lib/samagotchi/session.rb +370 -445
  139. data/lib/samagotchi/session_access/local.rb +12 -11
  140. data/lib/samagotchi/session_access/remote.rb +22 -6
  141. data/lib/samagotchi/session_archive_command.rb +2 -2
  142. data/lib/samagotchi/session_chain.rb +7 -6
  143. data/lib/samagotchi/session_continue.rb +6 -5
  144. data/lib/samagotchi/session_delete_command.rb +1 -1
  145. data/lib/samagotchi/session_field.rb +133 -0
  146. data/lib/samagotchi/session_inbox.rb +49 -1
  147. data/lib/samagotchi/session_manager/delivery.rb +254 -0
  148. data/lib/samagotchi/session_manager/emptiness.rb +139 -0
  149. data/lib/samagotchi/session_manager/lifecycle.rb +274 -0
  150. data/lib/samagotchi/session_manager/listing.rb +118 -0
  151. data/lib/samagotchi/session_manager/removal.rb +190 -0
  152. data/lib/samagotchi/session_manager/spawn.rb +242 -0
  153. data/lib/samagotchi/session_manager/worker_command.rb +157 -0
  154. data/lib/samagotchi/session_manager.rb +28 -1541
  155. data/lib/samagotchi/session_metrics.rb +8 -5
  156. data/lib/samagotchi/session_paths.rb +57 -0
  157. data/lib/samagotchi/session_retention.rb +2 -2
  158. data/lib/samagotchi/session_row.rb +200 -0
  159. data/lib/samagotchi/session_setup.rb +15 -12
  160. data/lib/samagotchi/sessions_command.rb +11 -129
  161. data/lib/samagotchi/share_command.rb +1 -1
  162. data/lib/samagotchi/starting_marker.rb +65 -0
  163. data/lib/samagotchi/steer_cut_after.rb +7 -14
  164. data/lib/samagotchi/system_one/recorder.rb +49 -0
  165. data/lib/samagotchi/terminal_ui/attached_loop.rb +6 -26
  166. data/lib/samagotchi/terminal_ui/exit_lines.rb +1 -1
  167. data/lib/samagotchi/terminal_ui/join_replay.rb +86 -8
  168. data/lib/samagotchi/terminal_ui/local_link.rb +2 -2
  169. data/lib/samagotchi/terminal_ui.rb +5 -7
  170. data/lib/samagotchi/testing/install.rb +2 -1
  171. data/lib/samagotchi/testing/sessions.rb +1 -1
  172. data/lib/samagotchi/thinking.rb +16 -21
  173. data/lib/samagotchi/token_usage.rb +1 -1
  174. data/lib/samagotchi/tool_activity.rb +10 -4
  175. data/lib/samagotchi/tool_declarations.rb +2 -2
  176. data/lib/samagotchi/tool_output_cut.rb +17 -0
  177. data/lib/samagotchi/tool_response.rb +5 -3
  178. data/lib/samagotchi/tool_runner.rb +25 -5
  179. data/lib/samagotchi/toolkit_command.rb +1 -1
  180. data/lib/samagotchi/tools/delegate.rb +55 -45
  181. data/lib/samagotchi/tools/delegate_relay.rb +5 -5
  182. data/lib/samagotchi/tools/delegate_result.rb +5 -5
  183. data/lib/samagotchi/tools/delegate_wait.rb +41 -17
  184. data/lib/samagotchi/tools/index_hint.rb +14 -0
  185. data/lib/samagotchi/tools/list_sessions.rb +7 -7
  186. data/lib/samagotchi/tools/registry.rb +28 -11
  187. data/lib/samagotchi/tools/send_note.rb +4 -6
  188. data/lib/samagotchi/tools/tool_index.rb +52 -51
  189. data/lib/samagotchi/tools/tool_surface.rb +22 -8
  190. data/lib/samagotchi/turn_context.rb +2 -1
  191. data/lib/samagotchi/turn_stages/context_judge.rb +38 -36
  192. data/lib/samagotchi/turn_stages/objectives.rb +56 -52
  193. data/lib/samagotchi/turn_stages/toolkit_suggestion.rb +13 -17
  194. data/lib/samagotchi/turn_stages/verify_anchors.rb +9 -10
  195. data/lib/samagotchi/turn_stages/warmup.rb +14 -15
  196. data/lib/samagotchi/update_command.rb +11 -7
  197. data/lib/samagotchi/verify_fresh.rb +7 -11
  198. data/lib/samagotchi/version.rb +1 -1
  199. data/lib/samagotchi/vision_support.rb +11 -14
  200. data/lib/samagotchi/web/app.rb +45 -28
  201. data/lib/samagotchi/web/message_display.rb +57 -7
  202. data/lib/samagotchi/web/participant_access.rb +3 -3
  203. data/lib/samagotchi/web/public/activity.js +16 -7
  204. data/lib/samagotchi/web/public/app.js +29 -8
  205. data/lib/samagotchi/web/public/bubbles.js +8 -3
  206. data/lib/samagotchi/web/public/cards_place.js +15 -0
  207. data/lib/samagotchi/web/public/commands.js +5 -1
  208. data/lib/samagotchi/web/public/data.js +4 -2
  209. data/lib/samagotchi/web/public/history_shape.js +220 -0
  210. data/lib/samagotchi/web/public/recap_stamp.js +17 -0
  211. data/lib/samagotchi/web/public/reload_check.js +71 -0
  212. data/lib/samagotchi/web/public/stage_model.js +7 -5
  213. data/lib/samagotchi/web/public/stage_view.js +7 -3
  214. data/lib/samagotchi/web/public/stream_handlers.js +8 -2
  215. data/lib/samagotchi/web/public/timing.js +43 -2
  216. data/lib/samagotchi/web/public/turn_block.js +215 -30
  217. data/lib/samagotchi/web/public/turn_drawing.js +43 -0
  218. data/lib/samagotchi/web/public/turn_events.js +10 -1
  219. data/lib/samagotchi/web/public/turn_pieces.js +29 -0
  220. data/lib/samagotchi/web/reload_check_route.rb +95 -0
  221. data/lib/samagotchi/web/server.rb +4 -2
  222. data/lib/samagotchi/web/session_access_routes.rb +6 -6
  223. data/lib/samagotchi/web/session_control.rb +4 -4
  224. data/lib/samagotchi/web/session_create.rb +3 -3
  225. data/lib/samagotchi/web/session_hub.rb +9 -17
  226. data/lib/samagotchi/web/session_show.rb +67 -5
  227. data/lib/samagotchi/web/session_summary.rb +39 -28
  228. data/lib/samagotchi/worker.rb +83 -32
  229. data/lib/samagotchi/worker_boot.rb +184 -0
  230. data/lib/samagotchi/worker_link.rb +3 -0
  231. data/lib/samagotchi/worker_signals.rb +1 -1
  232. data/lib/samagotchi/worker_task_wakes.rb +2 -2
  233. metadata +32 -4
  234. data/lib/samagotchi/send_command/wait_target.rb +0 -85
@@ -4,6 +4,7 @@ require_relative "version"
4
4
  require_relative "config"
5
5
  require_relative "context_window"
6
6
  require_relative "degrade"
7
+ require_relative "effective_model"
7
8
  require_relative "session"
8
9
  require_relative "log_path"
9
10
  require_relative "model_profile"
@@ -181,15 +182,12 @@ module Samagotchi
181
182
  Degrade.call(:config, "model_window_unreadable") do
182
183
  return nil unless model
183
184
 
184
- registry = HostRegistry.new(env: env)
185
- target = registry.resolve(model)
186
- names = registry.lookup_names(model, target: target)
187
- models = ConfigFile.model_settings(env: env)
188
- window = ContextWindow.setting(target, names: names, models: models)
185
+ effective = effective_model(model, env)
186
+ window = effective.window_setting
189
187
  return nil unless window
190
- return [window.tokens, "hosts.#{target.entry.name}"] if window.source == :host_setting
188
+ return [window.tokens, "hosts.#{effective.host_name}"] if window.source == :host_setting
191
189
 
192
- [window.tokens, "models: #{ConfigFile.model_setting(names, :window_tokens, models: models).key}"]
190
+ [window.tokens, "models: #{effective.lookup.model(:window_tokens).key}"]
193
191
  end
194
192
  end
195
193
 
@@ -224,7 +222,7 @@ module Samagotchi
224
222
  Degrade.call(:config, "model_ref_unresolved", fallback: false) do
225
223
  return true if one == other
226
224
 
227
- ConfigFile.model_ref(one, env: env).ref == ConfigFile.model_ref(other, env: env).ref
225
+ HostConfig.model_ref(one, env: env).ref == HostConfig.model_ref(other, env: env).ref
228
226
  end
229
227
  end
230
228
 
@@ -232,7 +230,7 @@ module Samagotchi
232
230
  # engine keys memory_write current_model_only): which `<name>.<key>.md`
233
231
  # overlays are this model's.
234
232
  def model_key_for(model, env)
235
- ModelOverlay.key_for(HostRegistry.new(env: env).bare_name(model)) || "-"
233
+ ModelOverlay.key_for(registry(env).bare_name(model)) || "-"
236
234
  rescue StandardError
237
235
  ModelOverlay.key_for(model) || "-"
238
236
  end
@@ -243,7 +241,7 @@ module Samagotchi
243
241
  # without any. Read from the memories dirs now: a session's prompt
244
242
  # takes them at its next build (/stats names the ones it carries).
245
243
  def model_notes_for(model, env)
246
- bare = HostRegistry.new(env: env).bare_name(model)
244
+ bare = registry(env).bare_name(model)
247
245
  key = ModelOverlay.key_for(bare)
248
246
  notes = ModelNotes.for(name: bare, key: key, fallback_key: fallback_model_key(model, key, env),
249
247
  muted: session_muted(env), env: env, warn: false)
@@ -260,7 +258,7 @@ module Samagotchi
260
258
  def fallback_model_key(model, key, env)
261
259
  Degrade.call(:model, "fallback_key_unresolved") do
262
260
  typed = session_typed_model(env) || model
263
- typed_key = ModelOverlay.key_for(HostRegistry.new(env: env).parse_qualified_model(typed).last)
261
+ typed_key = ModelOverlay.key_for(registry(env).parse_qualified_model(typed).last)
264
262
  typed_key == key ? nil : typed_key
265
263
  end
266
264
  end
@@ -305,13 +303,13 @@ module Samagotchi
305
303
  # it resolves to (an alias applied, ModelRef), as `chi models` names it.
306
304
  def model_ref_name(env: ENV)
307
305
  name = model_name
308
- name && ConfigFile.model_ref(name, env: env).ref
306
+ name && HostConfig.model_ref(name, env: env).ref
309
307
  end
310
308
 
311
309
  # Routed without model discovery, so an unqualified name may land on
312
310
  # another host at runtime after /models.
313
311
  def loop_for(model, env)
314
- entry = HostRegistry.new(env: env).resolve(model).entry
312
+ entry = registry(env).resolve(model).entry
315
313
  return "-" unless entry
316
314
 
317
315
  return "native (raw prompt)" unless entry.chat?
@@ -327,15 +325,15 @@ module Samagotchi
327
325
  # chat host (api: openai or anthropic) has no /props, so it gets its model list
328
326
  # with the same timeouts: up/down and the ids it serves.
329
327
  def served_model_for(model, env)
330
- registry = HostRegistry.new(env: env)
331
- target = registry.resolve(model)
328
+ hosts = registry(env)
329
+ target = hosts.resolve(model)
332
330
  entry = target.entry
333
331
  bare = target.bare_model
334
332
  return "-" unless entry
335
333
  return "reported per turn (remote host)" if entry.remote?
336
334
  return chat_served_model_for(entry, bare, env) if entry.chat?
337
335
 
338
- client = registry.client_for(entry)
336
+ client = hosts.client_for(entry)
339
337
  props = client.server_props(model: bare)
340
338
  return NO_PROPS if props.nil? || props.status == :http_error
341
339
  return "unknown (the server didn't answer; is it running?)" if props.status == :network_error
@@ -374,16 +372,12 @@ module Samagotchi
374
372
  # Offline, so no /props probe: where nothing is configured, a native
375
373
  # llama.cpp host's chat template decides at runtime.
376
374
  def profile_for(model, env)
377
- registry = HostRegistry.new(env: env)
378
- target = registry.resolve(model)
379
- entry = target.entry
380
- bare = target.bare_model
381
- return "name-based (chat API: only strips thoughts)" if entry&.chat?
375
+ effective = effective_model(model, env)
376
+ return "name-based (chat API: only strips thoughts)" if effective.entry&.chat?
382
377
 
383
- names = registry.lookup_names(model)
384
- override = Config.resolve_with_origin("model.profile", env: env, cli_overrides: Config.cli_overrides)
385
- result = ModelProfile.resolve(names: names, entry: entry, client: nil, bare_model: bare, override: override,
386
- models: ConfigFile.model_settings(env: env))
378
+ lookup = effective.lookup
379
+ result = ModelProfile.resolve(names: effective.names, entry: effective.entry, client: nil, bare_model: effective.bare,
380
+ override: lookup.global("model.profile"), models: lookup.models)
387
381
  name = result.profile.name
388
382
  case result.source
389
383
  when :config then "#{name} (config #{result.detail})"
@@ -397,11 +391,8 @@ module Samagotchi
397
391
  # The model's thinking level and where it came from (Thinking.resolve);
398
392
  # in a session's commands its own level first (SAMAGOTCHI_SESSION_THINKING).
399
393
  def thinking_for(model, env)
400
- registry = HostRegistry.new(env: env)
401
- target = registry.resolve(model)
402
394
  session = session_model(env) ? Thinking.session_level(env[SESSION_THINKING_ENV]) : nil
403
- Thinking.explain(target, names: registry.lookup_names(model, target: target),
404
- models: ConfigFile.model_settings(env: env), session: session).label
395
+ Thinking.explain(effective_model(model, env).lookup, session: session).label
405
396
  rescue StandardError => e
406
397
  "(unknown: #{e.message})"
407
398
  end
@@ -411,20 +402,28 @@ module Samagotchi
411
402
  # first (SAMAGOTCHI_SESSION_REASONING_HISTORY); a native host keeps the
412
403
  # running turn's on its own.
413
404
  def reasoning_history_for(model, env)
414
- registry = HostRegistry.new(env: env)
415
- target = registry.resolve(model)
416
- return "native loop: the running turn's, always" unless target.entry&.chat?
405
+ effective = effective_model(model, env)
406
+ return "native loop: the running turn's, always" unless effective.entry&.chat?
417
407
 
418
408
  session = session_model(env) ? ReasoningHistory.session_level(env[SESSION_REASONING_HISTORY_ENV]) : nil
419
- level, source = ReasoningHistory.resolve(target, names: registry.lookup_names(model, target: target),
420
- models: ConfigFile.model_settings(env: env), session: session)
421
- ReasoningHistory.label(level, source)
409
+ ReasoningHistory.label(*ReasoningHistory.resolve(effective.lookup, session: session))
422
410
  rescue StandardError => e
423
411
  "(unknown: #{e.message})"
424
412
  end
425
413
 
414
+ # The hosts chi self reads (HostRegistry.current: the process's shared
415
+ # one for ENV, a fresh one for another env).
416
+ def registry(env) = HostRegistry.current(env: env)
417
+
418
+ # +model+ as chi self reads its settings (EffectiveModel: every layer
419
+ # through its ModelLookup, with +env+). Raises as HostRegistry#resolve does.
420
+ # @return [EffectiveModel]
421
+ def effective_model(model, env)
422
+ EffectiveModel.of(registry(env), ModelChoice.new(name: model, typed: model))
423
+ end
424
+
426
425
  def host_for(model, env)
427
- target = HostRegistry.new(env: env).resolve(model)
426
+ target = registry(env).resolve(model)
428
427
  entry = target.entry
429
428
  bare = target.bare_model
430
429
  return "(no hosts configured)" unless entry
@@ -437,7 +436,7 @@ module Samagotchi
437
436
  # The variable the host's API key comes from and whether it is set, or
438
437
  # comes from the Keychain (HostKeys); the key itself is never shown.
439
438
  def api_key_for(model, env)
440
- name = HostRegistry.new(env: env).resolve(model).entry&.api_key_env
439
+ name = registry(env).resolve(model).entry&.api_key_env
441
440
  return "-" unless name
442
441
 
443
442
  state = { env: "set", keychain: "keychain" }.fetch(HostKeys.source(name, env: env), "unset")
@@ -4,6 +4,7 @@ require_relative "client_id"
4
4
  require_relative "delivery"
5
5
  require_relative "terminal_ui/formatting"
6
6
  require_relative "session"
7
+ require_relative "worker_link"
7
8
  require_relative "session_commands"
8
9
  require_relative "parent_answers_command"
9
10
  require_relative "session_inbox"
@@ -17,6 +18,7 @@ require_relative "turn_flow"
17
18
  require_relative "guardrails/parent_approvals"
18
19
  require_relative "guardrails/parent_policy"
19
20
  require_relative "image_store"
21
+ require_relative "effective_model"
20
22
  require_relative "host_registry"
21
23
  require_relative "model_profile"
22
24
  require_relative "vision_support"
@@ -24,7 +26,6 @@ require_relative "cli/command"
24
26
  require_relative "cli/flags"
25
27
  require_relative "cli/parent_wait"
26
28
  require_relative "send_command/parent_gate"
27
- require_relative "send_command/wait_target"
28
29
  require_relative "send_command/busy"
29
30
  require_relative "session_access/local"
30
31
 
@@ -193,16 +194,13 @@ module Samagotchi
193
194
  # @param typed [String, nil] the name the model was typed as (a session's model_typed)
194
195
  # @return [VisionSupport::Answer] value nil when unknown
195
196
  def self.vision_answer(model_name, registry: nil, typed: nil)
196
- registry ||= HostRegistry.new
197
+ registry ||= HostRegistry.current
197
198
  name = ModelProfile.required_model_name(model_name)
198
- target = registry.resolve(name)
199
- names = registry.lookup_names(typed.to_s.strip.empty? ? name : typed, resolved: name, target: target)
200
- entry = target.entry
201
- return VisionSupport.for(target, adapter: registry.adapter_for(entry), names: names) if entry.chat?
202
-
203
- profile = ModelProfile.resolve(names: names, entry: entry, client: target.client, bare_model: target.bare_model).profile
204
- answer = VisionSupport.for(target, profile: profile, names: names)
205
- props = target.client.server_props(model: target.bare_model)
199
+ model = EffectiveModel.of(registry, ModelChoice.new(name: name, typed: typed.to_s.strip.empty? ? name : typed))
200
+ return model.vision_support(nil) if model.chat?
201
+
202
+ answer = model.vision_support(model.profile_resolution.profile)
203
+ props = model.client.server_props(model: model.bare)
206
204
  return UNKNOWN_VISION if answer.no? && !props&.answered? && !configured_no?(answer)
207
205
 
208
206
  answer
@@ -521,9 +519,8 @@ module Samagotchi
521
519
  return report_command(session.id) if @sent_command
522
520
 
523
521
  start = @access.prepare_wait(session.id, session: session, created: true)
524
- wait_for_reply(session.id, cursor: start.cursor, baseline: start.baseline,
525
- timeout: options[:timeout],
526
- **WaitTarget.first_message(@delivered, direct_turn: session.first_turn_id).to_h)
522
+ target = ReplyWait::Target.first_message(start, @delivered, direct_turn: session.first_turn_id)
523
+ wait_for_reply(session.id, target: target, timeout: options[:timeout])
527
524
  end
528
525
 
529
526
  # What chi send --new asks to create: through the worker (images, a
@@ -601,9 +598,8 @@ module Samagotchi
601
598
  return 1 unless deliver(id, prompt)
602
599
  return report_command(id) if @sent_command
603
600
 
604
- target = WaitTarget.message(@delivered, queue: @queue, running: start.running,
605
- running_turn: (start.own_turn if start.running))
606
- wait_for_reply(id, cursor: start.cursor, baseline: start.baseline, timeout: options[:timeout], **target.to_h)
601
+ target = ReplyWait::Target.message(start, @delivered, queue: @queue)
602
+ wait_for_reply(id, target: target, timeout: options[:timeout])
607
603
  end
608
604
 
609
605
  # A participant's --wait -m (a review turn through chi --remote with
@@ -622,9 +618,8 @@ module Samagotchi
622
618
  return 1
623
619
  end
624
620
  start = @access.prepare_wait(id, turn: turn)
625
- wait_for_reply(id, cursor: start.cursor, baseline: start.baseline, timeout: options[:timeout],
626
- owner_grace: start.live ? self.class::WORKER_GONE_AFTER : nil,
627
- **WaitTarget.after(start, requested: turn).to_h)
621
+ target = ReplyWait::Target.participant(start, turn: turn)
622
+ wait_for_reply(id, target: target, timeout: options[:timeout])
628
623
  end
629
624
 
630
625
  # --wait with no message: the session's next reply, sending nothing,
@@ -648,9 +643,8 @@ module Samagotchi
648
643
  error_line("chi send: turn #{requested_turn} is not a running, queued, or last ended turn")
649
644
  return 1
650
645
  end
651
- target = WaitTarget.after(start, requested: requested_turn)
652
- wait_for_reply(id, cursor: start.cursor, baseline: start.baseline,
653
- timeout: options[:timeout], owner_grace: start.live ? self.class::WORKER_GONE_AFTER : nil, **target.to_h)
646
+ target = ReplyWait::Target.wait_only(start, requested: requested_turn)
647
+ wait_for_reply(id, target: target, timeout: options[:timeout])
654
648
  end
655
649
 
656
650
  # A new idle session's first turn, with the images. Its worker's Bridge
@@ -675,7 +669,7 @@ module Samagotchi
675
669
  # its preview; the user can attach and send it again.
676
670
  unless result
677
671
  attach = @access.invocation.attach(session.id)
678
- @info.puts("#{session.id} failed: its worker did not start; the session is kept (#{attach})")
672
+ @info.puts("#{session.id} failed: #{WorkerLink.did_not_start}; the session is kept (#{attach})")
679
673
  return false
680
674
  end
681
675
  unless result.accepted?