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
@@ -1,191 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "registry"
4
- require "digest"
5
- require_relative "../log"
6
- require_relative "../guardrails/load_failures"
7
- require_relative "../memory_bundle/bundle_hook"
8
- require_relative "../memory_bundle/provenance"
9
-
10
- module Samagotchi
11
- # Namespace that wraps hook plugin classes per-bundle. Each bundle's hooks are
12
- # evaluated inside `Samagotchi::Bundles::<BundleName>` so that two bundles
13
- # shipping a file with the same basename (e.g. `guardrails.rb`) cannot collide
14
- # on a top-level constant.
15
- module Bundles; end
16
-
17
- module Hooks
18
- # Loads bundle-owned hook plugins into a Registry.
19
- #
20
- # Unlike +Hooks::Loader+ (which `require`s a file and resolves a top-level
21
- # constant), this loader `module_eval`s each plugin file inside a
22
- # per-bundle namespace module. This gives:
23
- # - zero class-name collisions across bundles
24
- # - a clean uninstall path (`Registry#unregister_bundle`)
25
- #
26
- # Each plugin must define a class whose PascalCase name matches the file
27
- # basename and whose instances respond to +call(event)+.
28
- #
29
- # A "fail_closed" before_tool_call hook is required: if it is missing,
30
- # fails to load, or its file's sha256 differs from the one recorded at
31
- # install, the failure is reported and the gate denies every tool call.
32
- # Any hook whose sha256 differs is not loaded (reinstall the bundle
33
- # after editing a hook by hand), nor any of a bundle whose requires_chi
34
- # this chi doesn't meet.
35
- #
36
- # The +on_error+ policy is applied per hook at fire time (Hooks.wrap):
37
- # - "fail_closed" (on :before_tool_call): a raising guardrail denies
38
- # the tool call (fail-closed).
39
- # - "log": warn to stderr and continue.
40
- # - "skip" (default): silently continue.
41
- # Whatever the policy, a hook file that raises is told to the user once
42
- # per hook (its +hook_failed+ callback, the Engine's
43
- # HookRoutes#plugin_hook_failed): :skip would otherwise leave no trace.
44
- module BundleLoader
45
- class << self
46
- # Load all hook plugins for a bundle into the given registry.
47
- #
48
- # @param bundle_name [String]
49
- # @param hooks_dir [String] directory containing the bundle's .rb hook files
50
- # @param metadata [Hash] basename => { event:, on_error:, priority:,
51
- # sha256: } (MemoryBundle::BundleHook.parse reads it)
52
- # @param registry [Samagotchi::Hooks::Registry]
53
- # @param failures [Guardrails::LoadFailures, nil] collects hooks that
54
- # failed to load
55
- # @param settings [Hash] the bundle's section of config.yml
56
- # `bundles:` (string keys); a hook class whose initialize takes an
57
- # argument gets it
58
- # @param manifest [MemoryBundle::InstalledManifest, nil] the installed
59
- # bundle's manifest; when this chi doesn't meet its requires_chi no
60
- # hook loads (each is reported, as the plugin loader does)
61
- # @param hook_failed [#call, nil] (label, "Class: message", effect:)
62
- # when a hook file raises at fire time — the Engine's HookRoutes;
63
- # nil when there is no UI to tell (the test kit)
64
- # @return [Integer] number of hooks successfully registered
65
- def load(bundle_name:, hooks_dir:, metadata:, registry:, failures: nil, settings: {}, manifest: nil, hook_failed: nil)
66
- return 0 unless metadata.is_a?(Hash)
67
-
68
- too_old = manifest&.requires_chi_failure
69
- loaded = 0
70
- metadata.each do |raw_basename, raw_meta|
71
- basename = raw_basename.to_s
72
- next if basename.empty?
73
-
74
- hook = MemoryBundle::BundleHook.parse(raw_meta)
75
- # A hook with no declared event cannot be auto-registered.
76
- next if hook.events.empty?
77
-
78
- fail_closed = hook.events.any? { |event| hook.fail_closed?(event) }
79
- what = "hook #{basename} (bundle #{bundle_name})"
80
- file = hooks_dir && File.join(hooks_dir, basename)
81
- unless file && File.exist?(file)
82
- failures&.add(what, "the file is missing", required: true) if fail_closed
83
- next
84
- end
85
- if (mismatch = too_old || sha_mismatch(file, hook.sha256, required: fail_closed))
86
- Log.warn(:hooks, "bundle_hook_mismatch", echo: "[samagotchi:hooks] bundle '#{bundle_name}' hook '#{basename}' not loaded: #{mismatch}", bundle: bundle_name, hook: basename.to_s)
87
- failures&.add(what, mismatch, required: fail_closed)
88
- next
89
- end
90
-
91
- begin
92
- load_hook_file(bundle_name, basename, file, hook, registry, settings: settings, hook_failed: hook_failed)
93
- loaded += 1
94
- rescue Exception => e # rubocop:disable Lint/RescueException -- a hook's SyntaxError or exit must not stop chi
95
- raise if e.is_a?(SignalException) # Ctrl-C and kill signals are the user's
96
-
97
- Log.error(:hooks, "bundle_hook_load_failed", echo: "[samagotchi:hooks] bundle '#{bundle_name}' hook '#{basename}' failed to load: #{e.class}: #{e.message}", bundle: bundle_name, hook: basename.to_s, error: e.class.name)
98
- failures&.add(what, "#{e.class}: #{e.message}", required: fail_closed,
99
- location: Guardrails::LoadFailures.location(e, File.dirname(hooks_dir)))
100
- end
101
- end
102
- loaded
103
- end
104
-
105
- # Build one hook file and register it in +registry+ under each of
106
- # its events (one instance for all: its state is shared), with its
107
- # priority and error policy (a fail_closed before_tool_call one
108
- # denies when it raises). No missing-file, sha or requires_chi
109
- # check: #load makes them; the test kit loads a bundle's source
110
- # this way.
111
- # @param hook [MemoryBundle::BundleHook] its manifest entry
112
- # @param namespace [Module, nil] the module the file is evaluated
113
- # in (default: the bundle's, #namespace_for)
114
- # @param hook_failed [#call, nil] (label, "Class: message", effect:)
115
- # on every raise at fire time, whatever the policy; nil gives no
116
- # notice (the log still has a :log hook's failures)
117
- # @raise [Exception] what loading or building the file raised;
118
- # nothing is registered then
119
- def load_hook_file(bundle_name, basename, file, hook, registry, settings: {}, namespace: nil, hook_failed: nil)
120
- plugin = instantiate(bundle_name, basename, file, settings: settings, namespace: namespace)
121
- hook.events.each do |event_name|
122
- event_sym = event_name.to_sym
123
- fail_closed = hook.fail_closed?(event_sym)
124
- policy = if fail_closed then :deny
125
- elsif hook.on_error == "log" then :log
126
- else :skip
127
- end
128
- label = "#{"guardrail " if fail_closed}#{basename} (bundle #{bundle_name})"
129
- # Every policy tells, once per label: a hook file under the
130
- # default :skip would raise with no trace anywhere. The label
131
- # names the file, the bundle and the event, so one label per
132
- # hook and a stream hook can't nag.
133
- on_failure = hook_failed && lambda { |hook_label, failed|
134
- hook_failed.call("#{hook_label} #{event_sym} hook", failed, effect: policy)
135
- }
136
- handler = Hooks.wrap(label: label, event: event_sym, policy: policy, log: [:hooks, "bundle_hook_failed"],
137
- fields: { bundle: bundle_name }, on_failure: on_failure) { |event| plugin.call(event) }
138
- registry.register_bundle(bundle_name, event_sym, hook_name: basename, priority: hook.priority, &handler)
139
- end
140
- nil
141
- end
142
-
143
- # Why the file doesn't match the sha256 recorded at install, or nil.
144
- # With none recorded, only a required hook counts as a mismatch.
145
- def sha_mismatch(file, recorded, required:)
146
- return nil if MemoryBundle::Provenance.unchecked?(recorded)
147
-
148
- expected = MemoryBundle::Provenance.recorded_sha(recorded)
149
- return (required ? "no sha256 recorded for it (reinstall the bundle)" : nil) if expected.empty?
150
-
151
- actual = MemoryBundle::Provenance.file_sha(file)
152
- return nil if actual == expected
153
-
154
- "its sha256 #{actual[0, 12]}… differs from the installed #{expected[0, 12]}… (edited after install? reinstall the bundle)"
155
- end
156
-
157
- # Evaluate a plugin file inside the bundle's namespace module (or
158
- # +namespace+) and
159
- # return an instance that responds to #call. A class whose
160
- # initialize takes an argument gets the settings (one Hash, string
161
- # keys); one that takes none is built bare.
162
- def instantiate(bundle_name, basename, file, settings: {}, namespace: nil)
163
- ns = namespace || namespace_for(bundle_name)
164
- content = File.read(file)
165
- ns.module_eval(content, file, 1)
166
- klass = Hooks.class_for(basename, ns)
167
- instance = Hooks.build_plugin(klass, settings)
168
- raise ArgumentError, "plugin #{klass.name.split("::").last} does not respond to #call" unless instance.respond_to?(:call)
169
-
170
- instance
171
- end
172
-
173
- # The (lazily created) namespace module for a bundle.
174
- # Sanitization is made injective by appending a short digest so that
175
- # `my-bundle` and `my_bundle` do not collide.
176
- def namespace_for(bundle_name)
177
- raw = bundle_name.to_s
178
- base = raw.gsub(/[^A-Za-z0-9_]/, "_")
179
- base = "B_#{base}" unless base.match?(/\A[A-Z]/)
180
- digest = Digest::MD5.hexdigest(raw)[0, 4]
181
- ns_name = "#{base}_#{digest}"
182
- if Samagotchi::Bundles.const_defined?(ns_name.to_sym, false)
183
- Samagotchi::Bundles.const_get(ns_name.to_sym)
184
- else
185
- Samagotchi::Bundles.const_set(ns_name.to_sym, Module.new)
186
- end
187
- end
188
- end
189
- end
190
- end
191
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../delivery"
4
- require_relative "../reply_wait"
5
- require_relative "../session"
6
-
7
- module Samagotchi
8
- class SendCommand
9
- # Which turn chi send --wait waits for and names in its report:
10
- # ReplyWait's +turn+ (an enqueued_id; nil: the next reply), +queued+
11
- # and +ended_before+, and ParentWait's +report_turn_id+ (the turn_id
12
- # of the --format json line). Each run path builds it its way.
13
- WaitTarget = Data.define(:turn, :report_turn_id, :queued, :ended_before) do
14
- def initialize(turn: nil, report_turn_id: nil, queued: false, ended_before: false) = super
15
-
16
- # A --new session's first message: its own turn, the ack's id (a
17
- # file-delivered one's also names the outcome of a session command
18
- # that runs no turn), so a worker that dies in it and the next one's
19
- # settle end the wait (TurnCheckpoint.settled_turn). A message merged
20
- # into a turn starting then falls back to that turn's reply
21
- # (ReplyWait's turn: without queued:); no id (an older worker), the
22
- # next reply. A prompt the session was created with has no delivery:
23
- # its turn is the id the create gave it (+direct_turn+,
24
- # Session#first_turn_id), and nothing can merge into it, so the wait
25
- # is for that turn alone (ReplyWait's queued:).
26
- # @param delivered [SessionManager::DeliverResult, nil] nil when the
27
- # message started the session itself (no delivery)
28
- # @param direct_turn [String, nil] that first turn's id, for a direct
29
- # start
30
- def self.first_message(delivered, direct_turn: nil)
31
- return new unless delivered || !direct_turn.to_s.strip.empty?
32
-
33
- return new(turn: direct_turn, report_turn_id: direct_turn, queued: true) if delivered.nil?
34
-
35
- new(turn: delivered.enqueued_id, report_turn_id: delivered.enqueued_id)
36
- end
37
-
38
- # A message to an existing session. --queue: its own turn, the ack's
39
- # id, also when the turn running at the check ended before it went in
40
- # (an idle worker's ack says next_step, and the file still runs as
41
- # that turn). A file fallback's id names its turn too, and so does a
42
- # plain message to an idle session, as #first_message's. Otherwise
43
- # (a session left running by a dead worker, the busy check's owner
44
- # gone) the next reply, reported as the turn that was running when it
45
- # went in (+running_turn+, ReplyWait.own_turn). A plain message the
46
- # worker queued (its ack says queue: a participant's review turn was
47
- # running, which takes no steer) is a --queue's.
48
- # @param running [Boolean] the session was running when it went in
49
- def self.message(delivered, queue:, running:, running_turn:)
50
- queue ||= delivered.delivery == Delivery::QUEUE
51
- turn = delivered.enqueued_id if queue || delivered.file? || !running
52
- report = if queue then turn
53
- elsif running then running_turn
54
- else delivered.enqueued_id
55
- end
56
- new(turn: turn, report_turn_id: report, queued: queue && !turn.nil?)
57
- end
58
-
59
- # --wait with no message: --turn's (+requested+), or the turn running
60
- # now or whose continue offer stands (ReplyWait.own_turn). Settled
61
- # (idle, a turn ended, no question standing: a continue offer waits
62
- # for its answer), its last turn's end is the answer (ended_before);
63
- # with --turn, when that turn is the last one.
64
- def self.wait_only(session, session_dir:, requested:)
65
- settled = session.status == Session::STATUS_IDLE && !session.last_turn.nil? && session.pending_question.nil?
66
- own = requested ? nil : ReplyWait.own_turn(session, session_dir: session_dir)
67
- wait_only_of(own_turn: own, settled: settled, last_turn_id: session.last_turn&.enqueued_id, requested: requested)
68
- end
69
-
70
- # #wait_only from what SessionAccess#prepare_wait read
71
- # (SessionAccess::WaitStart): its own_turn, settled and last_turn_id.
72
- def self.after(start, requested:)
73
- wait_only_of(own_turn: start.own_turn, settled: start.settled, last_turn_id: start.last_turn_id,
74
- requested: requested)
75
- end
76
-
77
- def self.wait_only_of(own_turn:, settled:, last_turn_id:, requested:)
78
- turn = requested || own_turn
79
- ended_before = requested ? last_turn_id == requested : settled
80
- new(turn: turn, report_turn_id: turn || (last_turn_id if settled), ended_before: ended_before)
81
- end
82
- private_class_method :wait_only_of
83
- end
84
- end
85
- end