samagotchi 0.156.0 → 0.158.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -1
  3. data/README.md +1 -1
  4. data/bin/chi +4 -1
  5. data/docs/architecture.md +18 -12
  6. data/docs/bundle-authoring.md +5 -5
  7. data/docs/bundles.md +4 -1
  8. data/docs/cli.md +29 -24
  9. data/docs/configuration.md +43 -9
  10. data/docs/context.md +5 -3
  11. data/docs/guardrails.md +12 -7
  12. data/docs/hooks.md +53 -83
  13. data/docs/internals/bridge.md +84 -7
  14. data/docs/internals/bundles.md +69 -46
  15. data/docs/internals/context-telemetry.md +4 -3
  16. data/docs/internals/engine.md +23 -7
  17. data/docs/internals/participants.md +3 -2
  18. data/docs/internals/remote-sessions.md +5 -2
  19. data/docs/internals/web-frontend.md +3 -2
  20. data/docs/internals/working-set.md +1 -1
  21. data/docs/memory.md +51 -10
  22. data/docs/plugins.md +48 -11
  23. data/docs/releasing.md +6 -5
  24. data/docs/sessions.md +13 -13
  25. data/docs/sub-agent.md +17 -2
  26. data/docs/testing.md +56 -5
  27. data/lib/samagotchi/answer_command.rb +2 -1
  28. data/lib/samagotchi/archive_store.rb +4 -3
  29. data/lib/samagotchi/bootstrap_command.rb +3 -2
  30. data/lib/samagotchi/bridge/card_places.rb +5 -7
  31. data/lib/samagotchi/bridge/card_store.rb +3 -0
  32. data/lib/samagotchi/bridge/turn_accumulator.rb +113 -10
  33. data/lib/samagotchi/bridge/turn_drawings.rb +271 -0
  34. data/lib/samagotchi/bridge.rb +31 -6
  35. data/lib/samagotchi/broadcast/recipients.rb +8 -8
  36. data/lib/samagotchi/broadcast/triage_model.rb +1 -1
  37. data/lib/samagotchi/bundle_command/check.rb +41 -10
  38. data/lib/samagotchi/bundle_command/installed_check.rb +13 -2
  39. data/lib/samagotchi/bundle_command/try/run.rb +4 -1
  40. data/lib/samagotchi/bundle_command/try/sandbox.rb +1 -1
  41. data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
  42. data/lib/samagotchi/bundle_command.rb +20 -45
  43. data/lib/samagotchi/bundles/known-names/manifest.yml +4 -7
  44. data/lib/samagotchi/bundles/known-names/{hooks/known_names.rb → plugin.rb} +17 -0
  45. data/lib/samagotchi/bundles/source-links/manifest.yml +4 -7
  46. data/lib/samagotchi/bundles/source-links/{hooks/source_links.rb → plugin.rb} +23 -4
  47. data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
  48. data/lib/samagotchi/bundles/system/manifest.yml +4 -4
  49. data/lib/samagotchi/bundles/system/memory_guide.md +7 -7
  50. data/lib/samagotchi/bundles/system/self_map.md +1 -1
  51. data/lib/samagotchi/child_move.rb +6 -6
  52. data/lib/samagotchi/child_reports.rb +2 -2
  53. data/lib/samagotchi/children_status.rb +15 -21
  54. data/lib/samagotchi/cli/parent_wait.rb +8 -22
  55. data/lib/samagotchi/config.rb +3 -0
  56. data/lib/samagotchi/context_command.rb +7 -5
  57. data/lib/samagotchi/context_sources.rb +9 -6
  58. data/lib/samagotchi/context_window.rb +9 -12
  59. data/lib/samagotchi/effective_model.rb +57 -22
  60. data/lib/samagotchi/engine/plugins.rb +1 -1
  61. data/lib/samagotchi/engine/recap.rb +8 -4
  62. data/lib/samagotchi/engine.rb +30 -9
  63. data/lib/samagotchi/events.rb +3 -2
  64. data/lib/samagotchi/extension_load.rb +26 -44
  65. data/lib/samagotchi/guardrails/load_failures.rb +3 -2
  66. data/lib/samagotchi/guardrails/path_resolve.rb +11 -0
  67. data/lib/samagotchi/guardrails/review_paths.rb +1 -5
  68. data/lib/samagotchi/guardrails_command.rb +1 -1
  69. data/lib/samagotchi/hook_routes.rb +2 -2
  70. data/lib/samagotchi/hooks.rb +2 -3
  71. data/lib/samagotchi/host_address.rb +52 -0
  72. data/lib/samagotchi/host_registry.rb +57 -45
  73. data/lib/samagotchi/kernel_loop.rb +7 -3
  74. data/lib/samagotchi/llm/chat_loop.rb +6 -3
  75. data/lib/samagotchi/llm_context_strategy.rb +27 -63
  76. data/lib/samagotchi/memory_bundle/asset_installer.rb +92 -177
  77. data/lib/samagotchi/memory_bundle/builder.rb +12 -35
  78. data/lib/samagotchi/memory_bundle/bundle_hook.rb +6 -31
  79. data/lib/samagotchi/memory_bundle/file_actions.rb +193 -0
  80. data/lib/samagotchi/memory_bundle/file_digest.rb +13 -0
  81. data/lib/samagotchi/memory_bundle/file_state.rb +152 -0
  82. data/lib/samagotchi/memory_bundle/installed_bundle.rb +1 -1
  83. data/lib/samagotchi/memory_bundle/installed_copy.rb +13 -19
  84. data/lib/samagotchi/memory_bundle/installed_manifest.rb +10 -17
  85. data/lib/samagotchi/memory_bundle/installer.rb +212 -178
  86. data/lib/samagotchi/memory_bundle/manifest.rb +51 -58
  87. data/lib/samagotchi/memory_bundle/merger.rb +69 -65
  88. data/lib/samagotchi/memory_bundle/plugin_ref.rb +6 -5
  89. data/lib/samagotchi/memory_bundle/profile.rb +5 -5
  90. data/lib/samagotchi/memory_bundle/provenance.rb +26 -52
  91. data/lib/samagotchi/memory_bundle/remerge.rb +31 -14
  92. data/lib/samagotchi/memory_bundle/shas.rb +5 -8
  93. data/lib/samagotchi/memory_bundle/shipped_update.rb +51 -11
  94. data/lib/samagotchi/memory_bundle/status.rb +26 -30
  95. data/lib/samagotchi/memory_bundle/system_bundle.rb +39 -10
  96. data/lib/samagotchi/memory_bundle/trash.rb +28 -18
  97. data/lib/samagotchi/memory_bundle/uninstaller.rb +16 -11
  98. data/lib/samagotchi/memory_orphans.rb +4 -4
  99. data/lib/samagotchi/memory_paths.rb +102 -13
  100. data/lib/samagotchi/memory_usage.rb +1 -1
  101. data/lib/samagotchi/model_lookup.rb +75 -0
  102. data/lib/samagotchi/model_profile.rb +1 -0
  103. data/lib/samagotchi/models_command.rb +1 -1
  104. data/lib/samagotchi/note_command.rb +1 -1
  105. data/lib/samagotchi/participant_mode.rb +2 -1
  106. data/lib/samagotchi/plugin/api.rb +42 -10
  107. data/lib/samagotchi/plugin/loader.rb +75 -15
  108. data/lib/samagotchi/plugin/sessions.rb +1 -1
  109. data/lib/samagotchi/project_command.rb +167 -0
  110. data/lib/samagotchi/project_init.rb +69 -0
  111. data/lib/samagotchi/project_scope.rb +6 -4
  112. data/lib/samagotchi/prompt_files.rb +10 -7
  113. data/lib/samagotchi/reasoning_history.rb +14 -20
  114. data/lib/samagotchi/recap_place.rb +26 -0
  115. data/lib/samagotchi/reply_wait/liveness.rb +38 -0
  116. data/lib/samagotchi/reply_wait/poll.rb +44 -15
  117. data/lib/samagotchi/reply_wait/target.rb +113 -0
  118. data/lib/samagotchi/reply_wait.rb +22 -2
  119. data/lib/samagotchi/run_options.rb +1 -0
  120. data/lib/samagotchi/sampling_settings.rb +9 -16
  121. data/lib/samagotchi/self_report.rb +34 -35
  122. data/lib/samagotchi/send_command.rb +17 -23
  123. data/lib/samagotchi/session.rb +349 -441
  124. data/lib/samagotchi/session_access/contract.rb +1 -1
  125. data/lib/samagotchi/session_access/local.rb +5 -6
  126. data/lib/samagotchi/session_access/remote.rb +5 -3
  127. data/lib/samagotchi/session_field.rb +134 -0
  128. data/lib/samagotchi/session_inbox.rb +49 -1
  129. data/lib/samagotchi/session_manager/emptiness.rb +2 -1
  130. data/lib/samagotchi/session_manager/listing.rb +29 -100
  131. data/lib/samagotchi/session_manager.rb +2 -1
  132. data/lib/samagotchi/session_metrics.rb +7 -4
  133. data/lib/samagotchi/session_paths.rb +57 -0
  134. data/lib/samagotchi/session_retention.rb +2 -2
  135. data/lib/samagotchi/session_row.rb +200 -0
  136. data/lib/samagotchi/session_setup.rb +15 -12
  137. data/lib/samagotchi/sessions_command.rb +11 -129
  138. data/lib/samagotchi/steer.rb +10 -0
  139. data/lib/samagotchi/steer_cut_after.rb +7 -14
  140. data/lib/samagotchi/system_prompt.rb +20 -11
  141. data/lib/samagotchi/terminal_ui/attached_loop.rb +6 -26
  142. data/lib/samagotchi/terminal_ui/join_replay.rb +91 -8
  143. data/lib/samagotchi/terminal_ui/local_link.rb +2 -2
  144. data/lib/samagotchi/terminal_ui.rb +3 -1
  145. data/lib/samagotchi/testing/bundle.rb +5 -12
  146. data/lib/samagotchi/testing/install.rb +5 -4
  147. data/lib/samagotchi/testing/recorder.rb +1 -1
  148. data/lib/samagotchi/testing.rb +1 -1
  149. data/lib/samagotchi/thinking.rb +16 -21
  150. data/lib/samagotchi/tool_declarations.rb +1 -1
  151. data/lib/samagotchi/tool_response.rb +5 -3
  152. data/lib/samagotchi/tool_runner.rb +18 -1
  153. data/lib/samagotchi/toolkit_binding.rb +1 -4
  154. data/lib/samagotchi/toolkit_command.rb +1 -4
  155. data/lib/samagotchi/tools/delegate.rb +60 -51
  156. data/lib/samagotchi/tools/delegate_relay.rb +5 -5
  157. data/lib/samagotchi/tools/delegate_result.rb +1 -1
  158. data/lib/samagotchi/tools/delegate_wait.rb +41 -17
  159. data/lib/samagotchi/tools/list_sessions.rb +7 -7
  160. data/lib/samagotchi/update_command.rb +11 -7
  161. data/lib/samagotchi/verify_fresh.rb +7 -11
  162. data/lib/samagotchi/version.rb +1 -1
  163. data/lib/samagotchi/vision_support.rb +11 -14
  164. data/lib/samagotchi/web/app.rb +66 -21
  165. data/lib/samagotchi/web/message_display.rb +57 -7
  166. data/lib/samagotchi/web/participant_access.rb +1 -1
  167. data/lib/samagotchi/web/project_list.rb +3 -2
  168. data/lib/samagotchi/web/public/activity.js +16 -7
  169. data/lib/samagotchi/web/public/app.js +29 -8
  170. data/lib/samagotchi/web/public/bubbles.js +8 -3
  171. data/lib/samagotchi/web/public/cards_place.js +15 -0
  172. data/lib/samagotchi/web/public/commands.js +5 -1
  173. data/lib/samagotchi/web/public/data.js +4 -2
  174. data/lib/samagotchi/web/public/history_shape.js +220 -0
  175. data/lib/samagotchi/web/public/index.html +6 -1
  176. data/lib/samagotchi/web/public/project_picker.js +86 -4
  177. data/lib/samagotchi/web/public/recap_stamp.js +17 -0
  178. data/lib/samagotchi/web/public/reload_check.js +71 -0
  179. data/lib/samagotchi/web/public/stage_model.js +7 -5
  180. data/lib/samagotchi/web/public/stage_view.js +7 -3
  181. data/lib/samagotchi/web/public/stream_handlers.js +8 -2
  182. data/lib/samagotchi/web/public/timing.js +43 -2
  183. data/lib/samagotchi/web/public/turn_block.js +215 -30
  184. data/lib/samagotchi/web/public/turn_drawing.js +43 -0
  185. data/lib/samagotchi/web/public/turn_events.js +10 -1
  186. data/lib/samagotchi/web/public/turn_pieces.js +29 -0
  187. data/lib/samagotchi/web/reload_check_route.rb +95 -0
  188. data/lib/samagotchi/web/server.rb +4 -2
  189. data/lib/samagotchi/web/session_access_routes.rb +3 -3
  190. data/lib/samagotchi/web/session_control.rb +1 -1
  191. data/lib/samagotchi/web/session_hub.rb +9 -17
  192. data/lib/samagotchi/web/session_show.rb +64 -2
  193. data/lib/samagotchi/web/session_summary.rb +36 -24
  194. data/lib/samagotchi/worker.rb +28 -5
  195. data/lib/samagotchi/worker_link.rb +3 -0
  196. metadata +22 -5
  197. data/lib/samagotchi/hooks/bundle_loader.rb +0 -191
  198. data/lib/samagotchi/send_command/wait_target.rb +0 -85
@@ -343,8 +343,8 @@ module Samagotchi
343
343
  return if dir_stat.mtime == @dir_mtime
344
344
 
345
345
  @dir_mtime = dir_stat.mtime
346
- on_disk = Dir.glob(File.join(@state_dir, "*#{Session::FILE_EXT}")).to_h do |path|
347
- [File.basename(path, Session::FILE_EXT), path]
346
+ on_disk = Dir.glob(File.join(@state_dir, "*#{SessionPaths::FILE_EXT}")).to_h do |path|
347
+ [File.basename(path, SessionPaths::FILE_EXT), path]
348
348
  end.select { |id, _| Session.valid_id?(id) }
349
349
  (@sessions.keys - on_disk.keys).each { |id| drop(id) }
350
350
  on_disk.each_key { |id| @sessions[id] ||= Entry.new }
@@ -404,21 +404,13 @@ module Samagotchi
404
404
  emit("session", session: summary)
405
405
  end
406
406
 
407
- # One HostRegistry kept across scans: every session's ctx_pct may
408
- # need its llm_context budget resolved, and the registry's config read
409
- # is what would otherwise repeat per scan and per session. Built on
410
- # the first summary that needs it, and built again when config.yml's
411
- # hosts: changed (compared as read, like App#host_registry: the YAML
412
- # read is mtime-cached), so a host added or a window changed while
413
- # chi web runs is picked up on the next refresh of a card.
414
- def hub_registry
415
- hosts = HostConfig.all
416
- if @hub_registry.nil? || hosts != @hub_registry_hosts
417
- @hub_registry = HostRegistry.new(hosts_config: hosts)
418
- @hub_registry_hosts = hosts
419
- end
420
- @hub_registry
421
- end
407
+ # The registry kept across scans (HostRegistry.current, built again
408
+ # when config.yml's hosts: changed): every session's ctx_pct may need
409
+ # its llm_context budget resolved, and the registry's config read is
410
+ # what would otherwise repeat per scan and per session; a host added
411
+ # or a window changed while chi web runs is picked up on the next
412
+ # refresh of a card.
413
+ def hub_registry = HostRegistry.current
422
414
 
423
415
  def drop(id)
424
416
  entry = @sessions.delete(id)
@@ -4,6 +4,8 @@ require "time" # Time.iso8601
4
4
  require "uri"
5
5
 
6
6
  require_relative "../analytics_file"
7
+ require_relative "../bridge/turn_accumulator"
8
+ require_relative "../bridge/turn_drawings"
7
9
  require_relative "../answer_refs"
8
10
  require_relative "../answer_tail"
9
11
  require_relative "../objectives"
@@ -67,13 +69,27 @@ module Samagotchi
67
69
  session_json = shown_session_json(session, snapshot, owner, persisted)
68
70
  raw_messages = live_or_saved(turn_snapshot, "messages") { session.messages }
69
71
  timing = timing_payload(id, live_metrics: snapshot && snapshot["metrics"], persisted: persisted)
72
+ parts = req.params["parts"] == "1"
73
+ # A page that draws finished turns from their drawings (drawings=1,
74
+ # with parts=1): the valid lines stamp their items, which lose their
75
+ # parts. An older page (no drawings=1) reads what it always did.
76
+ # A live worker from before turn drawings lists no of_turn on its
77
+ # cards: its turns all draw as before.
78
+ drawings = parts && req.params["drawings"] == "1" && (!live || worker_draws?(id))
79
+ drawn = drawings ? drawn_lines(id, raw_messages) : []
80
+ messages = messages_for_display(raw_messages, parts: parts, cwd: session.working_directory,
81
+ drawings: drawn, strip_drawn: true)
70
82
  json_response(200, {
71
83
  session: session_json,
72
84
  history: read_history(id),
73
- messages: messages_for_display(raw_messages, parts: req.params["parts"] == "1",
74
- cwd: session.working_directory),
85
+ messages: messages,
86
+ **turn_drawings_field(drawn, messages),
75
87
  failed_turn: failed_turn_for(session, raw_messages),
76
88
  current_turn: current_turn,
89
+ # The page's join: the turn in progress and the queue behind it as
90
+ # the live events that draw them (TurnAccumulator.join_events, one
91
+ # unfold, here: any worker's snapshot works).
92
+ replay: replay_events_for(turn_snapshot, timing),
77
93
  queued: snapshot_list(turn_snapshot, "queued"),
78
94
  # Commands waiting for the running turn's end (their bubbles).
79
95
  queued_commands: snapshot_list(turn_snapshot, "queued_commands"),
@@ -110,6 +126,52 @@ module Samagotchi
110
126
  view ? { objectives: view } : {}
111
127
  end
112
128
 
129
+ # The live events a joining page replays (`replay`): the worker's
130
+ # +turn_snapshot+'s turn, queued prompts and commands, the turn's start
131
+ # from +timing+'s active_turn; [] without a worker or with nothing.
132
+ def replay_events_for(turn_snapshot, timing)
133
+ return [] unless turn_snapshot
134
+
135
+ wire = Bridge::TurnAccumulator.from_wire(turn_snapshot.slice("current_turn", "queued", "queued_commands"))
136
+ active = timing[:active_turn]
137
+ Bridge::TurnAccumulator.join_events(current_turn: wire[:current_turn], queued: wire[:queued],
138
+ queued_commands: wire[:queued_commands],
139
+ started_at: active.is_a?(Hash) ? active["started_at"] : nil)
140
+ end
141
+
142
+ # Whether the session's live worker names turn drawings in its
143
+ # sidecar's features (Bridge::TurnDrawings::FEATURE); false when its
144
+ # link can't be read as live.
145
+ def worker_draws?(id)
146
+ link = worker_link(id)
147
+ link.live? && Array(link.sidecar.features).include?(Bridge::TurnDrawings::FEATURE)
148
+ end
149
+
150
+ # The session's turn drawings that hold against +messages+ (the live
151
+ # snapshot's, else the file's): Bridge::TurnDrawings, cached by the
152
+ # file's size and mtime; the newest SHOWN_MAX of them.
153
+ # A drawing that can't be read draws its turn as before.
154
+ def drawn_lines(id, messages)
155
+ Degrade.call(:web, "turn_drawings_unreadable", fallback: []) do
156
+ dir = @session_class.session_dir(id, state_dir: default_state_dir)
157
+ Bridge::TurnDrawings.valid(Bridge::TurnDrawings.read(dir), messages).last(Bridge::TurnDrawings::SHOWN_MAX)
158
+ end
159
+ end
160
+
161
+ # {turn_drawings: {turn_id => {events:, end:}}} for the stamped turns
162
+ # (chi web's one unfold of a drawing, its cards too); nothing without
163
+ # one.
164
+ def turn_drawings_field(lines, messages)
165
+ stamped = messages.filter_map { |item| item[:drawn] }.uniq
166
+ shown = lines.select { |line| stamped.include?(line.turn_id) }
167
+ return {} if shown.empty?
168
+
169
+ { turn_drawings: shown.to_h do |line|
170
+ turn = Bridge::TurnAccumulator.from_wire(line.drawing)
171
+ [line.turn_id, { events: Bridge::TurnAccumulator.replay_events(turn, cards: true), end: line.end }]
172
+ end }
173
+ end
174
+
113
175
  # A list in the worker's turn +snapshot+ (queued prompts, commands,
114
176
  # init tasks); [] without a worker.
115
177
  def snapshot_list(snapshot, key) = Array(snapshot&.[](key))
@@ -7,10 +7,11 @@ require_relative "../verify_fresh"
7
7
  require_relative "../recap_store"
8
8
  require_relative "../archive_store"
9
9
  require_relative "../session_metrics"
10
- require_relative "../llm_context_strategy"
10
+ require_relative "../effective_model"
11
11
  require_relative "../host_registry"
12
12
  require_relative "../bridge_client"
13
13
  require_relative "../worker_link"
14
+ require_relative "../session_row"
14
15
  require_relative "../bridge/pending_card"
15
16
 
16
17
  module Samagotchi
@@ -20,6 +21,14 @@ module Samagotchi
20
21
  # session is answered (GET /api/sessions, the session view, the session
21
22
  # hub's events), so every path agrees on status, owner and bridge_up.
22
23
  module SessionSummary
24
+ # The page's keys, in the order it has always had them (a hub event
25
+ # compares with ==, a 0.156 page reads them by name).
26
+ WEB_KEYS = %i[id status mode model_name working_directory created_at updated_at last_prompt short_id test_run
27
+ used_memory_names preloaded_memory_names muted_memory_names prompt_notes parent_id delegate
28
+ continues review first_preview title owner recap auto_title ctx_pct tokens memory_index side
29
+ archived project_root bridge_up worker_version worker_features pending_question pending_card
30
+ last_turn verdicts verify_fresh last_verdict].freeze
31
+
23
32
  module_function
24
33
 
25
34
  # @param session [Session] a saved session (messages may be empty)
@@ -39,64 +48,66 @@ module Samagotchi
39
48
  # @return [Hash] symbol keys
40
49
  def build(session, owner:, session_dir:, status: displayed_status(session, owner: owner), root_cache: nil,
41
50
  registry: nil)
42
- used = Array(session.used_memory_names)
43
51
  link = WorkerLink.resolve(session_dir, unlink: false, owner: owner)
44
52
  up = link.live?
45
53
  sidecar = up ? link.sidecar : nil
46
54
  saved = SessionMetrics.saved_summary(session_dir, budget_tokens: budget_resolver(session, registry))
47
- recap_card = RecapStore.card(session_dir)
48
- {
49
- id: session.id,
55
+ # The facts every list shares (SessionRow), from this one link,
56
+ # analytics and recap read.
57
+ row = SessionRow.build(session, session_dir: session_dir, owner: owner, link: link, roots: root_cache || {},
58
+ saved: saved, card: RecapStore.card(session_dir))
59
+ values = {
60
+ id: row.id,
50
61
  status: status,
51
62
  mode: session.mode,
52
63
  model_name: session.model_name,
53
- working_directory: session.working_directory,
64
+ working_directory: row.cwd,
54
65
  created_at: session.created_at,
55
- updated_at: session.updated_at,
66
+ updated_at: row.updated_at,
56
67
  last_prompt: session.last_prompt,
57
- short_id: session.id.to_s[0, 8],
58
- test_run: !!session.test_run,
59
- used_memory_names: used,
68
+ short_id: row.short_id,
69
+ test_run: !!row.test_run,
70
+ used_memory_names: Array(session.used_memory_names),
60
71
  # The session's --memory and --mute lists (info-bar tooltip).
61
72
  preloaded_memory_names: Array(session.preloaded_memory_names),
62
73
  muted_memory_names: Array(session.muted_memory_names),
63
74
  # The model notes its prompt carried (the info bar's notes chip).
64
75
  prompt_notes: Array(session.prompt_notes).map(&:to_h),
65
76
  # The session that delegated this one (the `delegate` tool), else nil.
66
- parent_id: session.parent_id,
77
+ parent_id: row.parent_id,
67
78
  # Started by the delegate tool (Session#delegate?); a fork has a
68
79
  # parent_id too but isn't one. The parent's children chip counts
69
80
  # delegates only, as the TUI's segment and /children do.
70
- delegate: session.delegate?,
81
+ delegate: row.delegate,
71
82
  # The session this one continues (the previous link of its
72
83
  # chain), else nil: the lists draw chains from it.
73
- continues: session.continues,
84
+ continues: row.continues,
74
85
  # A review session's {subjects, from} (SessionReview), else nil:
75
86
  # the session bar offers Start review on the others.
76
87
  review: session.review&.to_file,
77
88
  first_preview: first_preview_for(session),
78
89
  # The session's own name (/title), else nil: the pages show it in
79
90
  # place of the first message.
80
- title: session.title,
81
- owner: owner&.kind,
91
+ title: row.title,
92
+ owner: row.owner,
82
93
  # The saved recap's first sentence, for the session card, and the
83
94
  # auto title its reply led with (RecapTitle), else nil: an untitled
84
95
  # session's name on the pages and its pushes (never in a model
85
96
  # request).
86
- recap: recap_card.preview,
87
- auto_title: recap_card.title,
97
+ recap: row.recap,
98
+ auto_title: row.auto_title,
88
99
  # How full the context was after the last turn (%), or nil, and
89
100
  # the session's token sums and cost (the card's ctx tooltip), from
90
101
  # one read of analytics.json.
91
- ctx_pct: saved&.ctx_pct&.round(1),
102
+ ctx_pct: row.ctx_pct,
92
103
  tokens: saved&.tokens,
93
104
  # The memory indexes its prompt held (the card's ctx tooltip).
94
105
  memory_index: saved&.memory_index,
95
106
  # Its side requests, apart from the tokens (the tooltip's own line).
96
107
  side: saved&.side,
97
108
  # Hidden from the strip and the list unless the all view's "archived" toggle is on.
98
- archived: ArchiveStore.archived?(session_dir),
99
- project_root: session.project_root(cache: root_cache),
109
+ archived: row.archived,
110
+ project_root: row.project,
100
111
  # A worker is reachable: WorkerLink answers Live (the lock held by
101
112
  # a worker whose sidecar names its pid). A sidecar a dead worker
102
113
  # left is not up.
@@ -108,8 +119,8 @@ module Samagotchi
108
119
  # For the tab's notifications (notify.js): an open question, the
109
120
  # running turn's open card with actions, and how the last turn
110
121
  # ended. A question saved by a worker that died is not open, nor a
111
- # one-turn chi run's (only a worker shares it).
112
- pending_question: session.waiting_question(live: !!owner&.worker?),
122
+ # one-turn chi run's (only a worker shares it): the row's live.
123
+ pending_question: session.waiting_question(live: row.live),
113
124
  pending_card: up ? Bridge::PendingCard.read(session_dir) : nil,
114
125
  last_turn: session.last_turn&.to_file,
115
126
  # Its Verify verdicts, the fresh ones its worker hasn't taken yet
@@ -117,9 +128,10 @@ module Samagotchi
117
128
  # ({of, child_id}: the card's "verifying fresh" and its link).
118
129
  verdicts: VerifyFresh.verdicts_of(session, session_dir),
119
130
  verify_fresh: VerifyFresh.running(session.id, state_dir: File.dirname(session_dir))
120
- .map { |row| { of: row[:of], child_id: row[:child_id] } },
131
+ .map { |entry| { of: entry[:of], child_id: entry[:child_id] } },
121
132
  last_verdict: last_verdict(session, session_dir)
122
133
  }
134
+ WEB_KEYS.to_h { |key| [key, values.fetch(key)] }
123
135
  end
124
136
 
125
137
  def last_verdict(session, session_dir)
@@ -141,7 +153,7 @@ module Samagotchi
141
153
  # no network) is resolved only for a session that has a saved context
142
154
  # to count. One registry per caller (the hub) or one built here.
143
155
  def budget_resolver(session, registry)
144
- -> { LLMContextStrategy.session_budget(session, registry: registry ||= HostRegistry.new) }
156
+ -> { EffectiveModel.session_budget(session, registry: registry ||= HostRegistry.current) }
145
157
  end
146
158
  private_class_method :budget_resolver
147
159
 
@@ -676,13 +676,14 @@ module Samagotchi
676
676
  save_or_log(:interrupted_turn) { save_session }
677
677
  end
678
678
 
679
- # The per-input tombstones (removed, settled) older than
679
+ # The per-input tombstones (removed, settled, merged) older than
680
680
  # SessionInbox::TOMBSTONE_MAX_AGE, once per worker start: they only go
681
681
  # with the session dir otherwise.
682
682
  def prune_tombstones
683
683
  removed = SessionInbox.prune_removed(@session_dir)
684
684
  settled = TurnCheckpoint.prune_settled(@session_dir)
685
- Log.info(:worker, "tombstones_pruned", removed: removed, settled: settled) if (removed + settled).positive?
685
+ merged = SessionInbox.prune_merged(@session_dir)
686
+ Log.info(:worker, "tombstones_pruned", removed: removed, settled: settled, merged: merged) if (removed + settled + merged).positive?
686
687
  end
687
688
 
688
689
  # A saved parent_answers that isn't a level reads as hard on every
@@ -910,14 +911,31 @@ module Samagotchi
910
911
  # A failure has no prompt to hand back (the Engine announced
911
912
  # :turn_failed). The offer went before the turn
912
913
  # (ContinueOffer#drop); either way the rollback window closes.
913
- run_engine_turn(nil, continue: true, origin: REMINDER_ORIGIN,
914
+ run_engine_turn(nil, continue: true, origin: wake_origin(REMINDER_ORIGIN),
914
915
  max_iterations: IterationLimit.for) { @turn_flow.after_reminder_turn }
915
916
  true
916
917
  end
917
918
 
918
- # A reminder turn's origin: chi's own (system:reminder).
919
+ # A reminder turn's origin: chi's own (system:reminder). Its run gets
920
+ # an id of its own (#wake_origin); this constant has none.
919
921
  REMINDER_ORIGIN = TurnOrigin.new(client_id: ClientId::REMINDER)
920
922
 
923
+ # A turn nobody typed (a reminder, a wake) with an enqueued_id of its
924
+ # own, minted here as it runs, so its checkpoint mark, last_turn, reply
925
+ # file and settle name it (a wait with no message binds it:
926
+ # ReplyWait.own_turn). Never on the origin constants nor on
927
+ # ChildReports::Report#origin: a report merged into a turn keeps an
928
+ # id-less origin, which the web page reads as a report (turn_events.js
929
+ # promptOps), not as an unmatched steer.
930
+ # @return [TurnOrigin] +origin+ with an id ("wake-<16 hex>")
931
+ def wake_origin(origin)
932
+ origin.enqueued_id ? origin : origin.with(enqueued_id: "#{WAKE_ID_PREFIX}#{SecureRandom.hex(8)}")
933
+ end
934
+
935
+ # A wake turn's minted enqueued_id starts with this
936
+ # (SessionInbox::ENQUEUED_ID_NAME).
937
+ WAKE_ID_PREFIX = "wake-"
938
+
921
939
  # :due when an idle parent should run a turn for its delegate
922
940
  # children's reports now (session.delegate_reports: wake): rings wait,
923
941
  # no continue offer does (they merge into its turn), wakes aren't paused
@@ -976,7 +994,7 @@ module Samagotchi
976
994
  def run_wake(origin:, failed:, kept: nil, mark: nil)
977
995
  @turn_flow.before_prompt_turn
978
996
  restores = @turn_flow.restores
979
- run_engine_turn(nil, continue: true, origin: origin, id: mark&.turn_id,
997
+ run_engine_turn(nil, continue: true, origin: wake_origin(origin), id: mark&.turn_id,
980
998
  max_iterations: IterationLimit.for) do |result, error|
981
999
  if error
982
1000
  steps_kept = @turn_flow.prompt_turn_failed(error: error, wake: failed, wake_kept: kept) == :kept
@@ -1331,6 +1349,8 @@ module Samagotchi
1331
1349
  origin = fresh_origin(turn_args[:origin])
1332
1350
  # Settled here: the checkpoint's prompt carries it from the start.
1333
1351
  turn_args = turn_args.merge(origin: origin, id: turn_args[:id] || SecureRandom.uuid)
1352
+ # The id its drain's merge records name (SessionInbox.mark_merged).
1353
+ @turn_enqueued_id = origin.enqueued_id
1334
1354
  verdicts_before = @session.verdicts.size
1335
1355
  @reporting_turn = reports_to_parent?(origin)
1336
1356
  # A participant's review turn merges no input (#pending_input_drain).
@@ -1540,6 +1560,9 @@ module Samagotchi
1540
1560
  begin
1541
1561
  input = SessionInbox.read_input(claimed_file)
1542
1562
  prompt = input.prompt.to_s.strip
1563
+ # Which turn took it, on disk before its file goes (a wait for
1564
+ # it reads this, not an inference: ReplyWait::Poll#follow_merge).
1565
+ SessionInbox.mark_merged(@session_dir, input.origin.enqueued_id, into: @turn_enqueued_id) unless prompt.empty?
1543
1566
  prompt.empty? ? nil : [prompt, input.origin]
1544
1567
  ensure
1545
1568
  FileUtils.rm_f(claimed_file)
@@ -113,6 +113,9 @@ module Samagotchi
113
113
 
114
114
  def not_live(session_id) = "no live worker for session #{session_id}"
115
115
  def unanswered(session_id) = "the worker for session #{session_id} didn't answer"
116
+ # A worker chi started that never came up within its wait (chi send
117
+ # --new's, a delegate child's).
118
+ def did_not_start = "its worker did not start"
116
119
 
117
120
  # @param session_dir [String]
118
121
  # @param unlink [Boolean] remove a Stale sidecar (the clients that would
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samagotchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.156.0
4
+ version: 0.158.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -204,6 +204,7 @@ files:
204
204
  - lib/samagotchi/bridge/ring_buffer.rb
205
205
  - lib/samagotchi/bridge/sse_writer.rb
206
206
  - lib/samagotchi/bridge/turn_accumulator.rb
207
+ - lib/samagotchi/bridge/turn_drawings.rb
207
208
  - lib/samagotchi/bridge/turn_notice.rb
208
209
  - lib/samagotchi/bridge_client.rb
209
210
  - lib/samagotchi/bridge_client/event_stream.rb
@@ -267,9 +268,9 @@ files:
267
268
  - lib/samagotchi/bundles/guardrails/manifest.yml
268
269
  - lib/samagotchi/bundles/inline-widgets/manifest.yml
269
270
  - lib/samagotchi/bundles/inline-widgets/plugin.rb
270
- - lib/samagotchi/bundles/known-names/hooks/known_names.rb
271
271
  - lib/samagotchi/bundles/known-names/known_names.md
272
272
  - lib/samagotchi/bundles/known-names/manifest.yml
273
+ - lib/samagotchi/bundles/known-names/plugin.rb
273
274
  - lib/samagotchi/bundles/loop-guard/manifest.yml
274
275
  - lib/samagotchi/bundles/loop-guard/plugin.rb
275
276
  - lib/samagotchi/bundles/mcp/manifest.yml
@@ -284,8 +285,8 @@ files:
284
285
  - lib/samagotchi/bundles/skills/plugin.rb
285
286
  - lib/samagotchi/bundles/slow-commands/manifest.yml
286
287
  - lib/samagotchi/bundles/slow-commands/plugin.rb
287
- - lib/samagotchi/bundles/source-links/hooks/source_links.rb
288
288
  - lib/samagotchi/bundles/source-links/manifest.yml
289
+ - lib/samagotchi/bundles/source-links/plugin.rb
289
290
  - lib/samagotchi/bundles/source-links/source_links.md
290
291
  - lib/samagotchi/bundles/system/config_modification_protocol.md
291
292
  - lib/samagotchi/bundles/system/delegated.md
@@ -434,10 +435,10 @@ files:
434
435
  - lib/samagotchi/hook_question.rb
435
436
  - lib/samagotchi/hook_routes.rb
436
437
  - lib/samagotchi/hooks.rb
437
- - lib/samagotchi/hooks/bundle_loader.rb
438
438
  - lib/samagotchi/hooks/loader.rb
439
439
  - lib/samagotchi/hooks/registry.rb
440
440
  - lib/samagotchi/hooks/stream_watch.rb
441
+ - lib/samagotchi/host_address.rb
441
442
  - lib/samagotchi/host_config.rb
442
443
  - lib/samagotchi/host_keys.rb
443
444
  - lib/samagotchi/host_model.rb
@@ -501,8 +502,10 @@ files:
501
502
  - lib/samagotchi/memory_bundle/builder.rb
502
503
  - lib/samagotchi/memory_bundle/bundle_hook.rb
503
504
  - lib/samagotchi/memory_bundle/entry_size.rb
505
+ - lib/samagotchi/memory_bundle/file_actions.rb
504
506
  - lib/samagotchi/memory_bundle/file_digest.rb
505
507
  - lib/samagotchi/memory_bundle/file_standing.rb
508
+ - lib/samagotchi/memory_bundle/file_state.rb
506
509
  - lib/samagotchi/memory_bundle/index_size.rb
507
510
  - lib/samagotchi/memory_bundle/index_sync.rb
508
511
  - lib/samagotchi/memory_bundle/index_updater.rb
@@ -538,6 +541,7 @@ files:
538
541
  - lib/samagotchi/model_binding.rb
539
542
  - lib/samagotchi/model_catalog.rb
540
543
  - lib/samagotchi/model_list_store.rb
544
+ - lib/samagotchi/model_lookup.rb
541
545
  - lib/samagotchi/model_match.rb
542
546
  - lib/samagotchi/model_notes.rb
543
547
  - lib/samagotchi/model_overlay.rb
@@ -587,7 +591,9 @@ files:
587
591
  - lib/samagotchi/plugin_tasks.rb
588
592
  - lib/samagotchi/private_files.rb
589
593
  - lib/samagotchi/process_group.rb
594
+ - lib/samagotchi/project_command.rb
590
595
  - lib/samagotchi/project_env.rb
596
+ - lib/samagotchi/project_init.rb
591
597
  - lib/samagotchi/project_scope.rb
592
598
  - lib/samagotchi/prompt.rb
593
599
  - lib/samagotchi/prompt_files.rb
@@ -604,6 +610,7 @@ files:
604
610
  - lib/samagotchi/reasoning_history.rb
605
611
  - lib/samagotchi/reasoning_history_command.rb
606
612
  - lib/samagotchi/recap_command.rb
613
+ - lib/samagotchi/recap_place.rb
607
614
  - lib/samagotchi/recap_setup.rb
608
615
  - lib/samagotchi/recap_store.rb
609
616
  - lib/samagotchi/recap_title.rb
@@ -621,7 +628,9 @@ files:
621
628
  - lib/samagotchi/remote_command.rb
622
629
  - lib/samagotchi/remotes.rb
623
630
  - lib/samagotchi/reply_wait.rb
631
+ - lib/samagotchi/reply_wait/liveness.rb
624
632
  - lib/samagotchi/reply_wait/poll.rb
633
+ - lib/samagotchi/reply_wait/target.rb
625
634
  - lib/samagotchi/reprefill_meter.rb
626
635
  - lib/samagotchi/review_apply.rb
627
636
  - lib/samagotchi/review_command.rb
@@ -635,7 +644,6 @@ files:
635
644
  - lib/samagotchi/send_command.rb
636
645
  - lib/samagotchi/send_command/busy.rb
637
646
  - lib/samagotchi/send_command/parent_gate.rb
638
- - lib/samagotchi/send_command/wait_target.rb
639
647
  - lib/samagotchi/served_model.rb
640
648
  - lib/samagotchi/session.rb
641
649
  - lib/samagotchi/session_access.rb
@@ -651,6 +659,7 @@ files:
651
659
  - lib/samagotchi/session_config.rb
652
660
  - lib/samagotchi/session_continue.rb
653
661
  - lib/samagotchi/session_delete_command.rb
662
+ - lib/samagotchi/session_field.rb
654
663
  - lib/samagotchi/session_inbox.rb
655
664
  - lib/samagotchi/session_manager.rb
656
665
  - lib/samagotchi/session_manager/delivery.rb
@@ -669,8 +678,10 @@ files:
669
678
  - lib/samagotchi/session_model_settings.rb
670
679
  - lib/samagotchi/session_observer.rb
671
680
  - lib/samagotchi/session_participants.rb
681
+ - lib/samagotchi/session_paths.rb
672
682
  - lib/samagotchi/session_retention.rb
673
683
  - lib/samagotchi/session_review.rb
684
+ - lib/samagotchi/session_row.rb
674
685
  - lib/samagotchi/session_setting.rb
675
686
  - lib/samagotchi/session_setup.rb
676
687
  - lib/samagotchi/sessions_command.rb
@@ -922,6 +933,7 @@ files:
922
933
  - lib/samagotchi/web/public/forms.css
923
934
  - lib/samagotchi/web/public/hash_intent.js
924
935
  - lib/samagotchi/web/public/history_reveal.js
936
+ - lib/samagotchi/web/public/history_shape.js
925
937
  - lib/samagotchi/web/public/icons/chi-180.png
926
938
  - lib/samagotchi/web/public/icons/chi-192.png
927
939
  - lib/samagotchi/web/public/icons/chi-512.png
@@ -973,9 +985,11 @@ files:
973
985
  - lib/samagotchi/web/public/question_cards.js
974
986
  - lib/samagotchi/web/public/read_view.js
975
987
  - lib/samagotchi/web/public/reasoning_history_chip.js
988
+ - lib/samagotchi/web/public/recap_stamp.js
976
989
  - lib/samagotchi/web/public/ref_menu.js
977
990
  - lib/samagotchi/web/public/ref_scan.js
978
991
  - lib/samagotchi/web/public/refs.js
992
+ - lib/samagotchi/web/public/reload_check.js
979
993
  - lib/samagotchi/web/public/restore.js
980
994
  - lib/samagotchi/web/public/review_apply.js
981
995
  - lib/samagotchi/web/public/review_history.js
@@ -1030,9 +1044,11 @@ files:
1030
1044
  - lib/samagotchi/web/public/toolkit_chip.js
1031
1045
  - lib/samagotchi/web/public/tools_overview.js
1032
1046
  - lib/samagotchi/web/public/turn_block.js
1047
+ - lib/samagotchi/web/public/turn_drawing.js
1033
1048
  - lib/samagotchi/web/public/turn_events.js
1034
1049
  - lib/samagotchi/web/public/turn_html.js
1035
1050
  - lib/samagotchi/web/public/turn_model.js
1051
+ - lib/samagotchi/web/public/turn_pieces.js
1036
1052
  - lib/samagotchi/web/public/turn_range.js
1037
1053
  - lib/samagotchi/web/public/unqueue.js
1038
1054
  - lib/samagotchi/web/public/update_notice.js
@@ -1065,6 +1081,7 @@ files:
1065
1081
  - lib/samagotchi/web/push/store.rb
1066
1082
  - lib/samagotchi/web/push_routes.rb
1067
1083
  - lib/samagotchi/web/qr.rb
1084
+ - lib/samagotchi/web/reload_check_route.rb
1068
1085
  - lib/samagotchi/web/running.rb
1069
1086
  - lib/samagotchi/web/server.rb
1070
1087
  - lib/samagotchi/web/session_access_routes.rb