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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +70 -1
- data/README.md +1 -1
- data/bin/chi +4 -1
- data/docs/architecture.md +18 -12
- data/docs/bundle-authoring.md +5 -5
- data/docs/bundles.md +4 -1
- data/docs/cli.md +29 -24
- data/docs/configuration.md +43 -9
- data/docs/context.md +5 -3
- data/docs/guardrails.md +12 -7
- data/docs/hooks.md +53 -83
- data/docs/internals/bridge.md +84 -7
- data/docs/internals/bundles.md +69 -46
- data/docs/internals/context-telemetry.md +4 -3
- data/docs/internals/engine.md +23 -7
- data/docs/internals/participants.md +3 -2
- data/docs/internals/remote-sessions.md +5 -2
- data/docs/internals/web-frontend.md +3 -2
- data/docs/internals/working-set.md +1 -1
- data/docs/memory.md +51 -10
- data/docs/plugins.md +48 -11
- data/docs/releasing.md +6 -5
- data/docs/sessions.md +13 -13
- data/docs/sub-agent.md +17 -2
- data/docs/testing.md +56 -5
- data/lib/samagotchi/answer_command.rb +2 -1
- data/lib/samagotchi/archive_store.rb +4 -3
- data/lib/samagotchi/bootstrap_command.rb +3 -2
- data/lib/samagotchi/bridge/card_places.rb +5 -7
- data/lib/samagotchi/bridge/card_store.rb +3 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +113 -10
- data/lib/samagotchi/bridge/turn_drawings.rb +271 -0
- data/lib/samagotchi/bridge.rb +31 -6
- data/lib/samagotchi/broadcast/recipients.rb +8 -8
- data/lib/samagotchi/broadcast/triage_model.rb +1 -1
- data/lib/samagotchi/bundle_command/check.rb +41 -10
- data/lib/samagotchi/bundle_command/installed_check.rb +13 -2
- data/lib/samagotchi/bundle_command/try/run.rb +4 -1
- data/lib/samagotchi/bundle_command/try/sandbox.rb +1 -1
- data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +20 -45
- data/lib/samagotchi/bundles/known-names/manifest.yml +4 -7
- data/lib/samagotchi/bundles/known-names/{hooks/known_names.rb → plugin.rb} +17 -0
- data/lib/samagotchi/bundles/source-links/manifest.yml +4 -7
- data/lib/samagotchi/bundles/source-links/{hooks/source_links.rb → plugin.rb} +23 -4
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +4 -4
- data/lib/samagotchi/bundles/system/memory_guide.md +7 -7
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/child_move.rb +6 -6
- data/lib/samagotchi/child_reports.rb +2 -2
- data/lib/samagotchi/children_status.rb +15 -21
- data/lib/samagotchi/cli/parent_wait.rb +8 -22
- data/lib/samagotchi/config.rb +3 -0
- data/lib/samagotchi/context_command.rb +7 -5
- data/lib/samagotchi/context_sources.rb +9 -6
- data/lib/samagotchi/context_window.rb +9 -12
- data/lib/samagotchi/effective_model.rb +57 -22
- data/lib/samagotchi/engine/plugins.rb +1 -1
- data/lib/samagotchi/engine/recap.rb +8 -4
- data/lib/samagotchi/engine.rb +30 -9
- data/lib/samagotchi/events.rb +3 -2
- data/lib/samagotchi/extension_load.rb +26 -44
- data/lib/samagotchi/guardrails/load_failures.rb +3 -2
- data/lib/samagotchi/guardrails/path_resolve.rb +11 -0
- data/lib/samagotchi/guardrails/review_paths.rb +1 -5
- data/lib/samagotchi/guardrails_command.rb +1 -1
- data/lib/samagotchi/hook_routes.rb +2 -2
- data/lib/samagotchi/hooks.rb +2 -3
- data/lib/samagotchi/host_address.rb +52 -0
- data/lib/samagotchi/host_registry.rb +57 -45
- data/lib/samagotchi/kernel_loop.rb +7 -3
- data/lib/samagotchi/llm/chat_loop.rb +6 -3
- data/lib/samagotchi/llm_context_strategy.rb +27 -63
- data/lib/samagotchi/memory_bundle/asset_installer.rb +92 -177
- data/lib/samagotchi/memory_bundle/builder.rb +12 -35
- data/lib/samagotchi/memory_bundle/bundle_hook.rb +6 -31
- data/lib/samagotchi/memory_bundle/file_actions.rb +193 -0
- data/lib/samagotchi/memory_bundle/file_digest.rb +13 -0
- data/lib/samagotchi/memory_bundle/file_state.rb +152 -0
- data/lib/samagotchi/memory_bundle/installed_bundle.rb +1 -1
- data/lib/samagotchi/memory_bundle/installed_copy.rb +13 -19
- data/lib/samagotchi/memory_bundle/installed_manifest.rb +10 -17
- data/lib/samagotchi/memory_bundle/installer.rb +212 -178
- data/lib/samagotchi/memory_bundle/manifest.rb +51 -58
- data/lib/samagotchi/memory_bundle/merger.rb +69 -65
- data/lib/samagotchi/memory_bundle/plugin_ref.rb +6 -5
- data/lib/samagotchi/memory_bundle/profile.rb +5 -5
- data/lib/samagotchi/memory_bundle/provenance.rb +26 -52
- data/lib/samagotchi/memory_bundle/remerge.rb +31 -14
- data/lib/samagotchi/memory_bundle/shas.rb +5 -8
- data/lib/samagotchi/memory_bundle/shipped_update.rb +51 -11
- data/lib/samagotchi/memory_bundle/status.rb +26 -30
- data/lib/samagotchi/memory_bundle/system_bundle.rb +39 -10
- data/lib/samagotchi/memory_bundle/trash.rb +28 -18
- data/lib/samagotchi/memory_bundle/uninstaller.rb +16 -11
- data/lib/samagotchi/memory_orphans.rb +4 -4
- data/lib/samagotchi/memory_paths.rb +102 -13
- data/lib/samagotchi/memory_usage.rb +1 -1
- data/lib/samagotchi/model_lookup.rb +75 -0
- data/lib/samagotchi/model_profile.rb +1 -0
- data/lib/samagotchi/models_command.rb +1 -1
- data/lib/samagotchi/note_command.rb +1 -1
- data/lib/samagotchi/participant_mode.rb +2 -1
- data/lib/samagotchi/plugin/api.rb +42 -10
- data/lib/samagotchi/plugin/loader.rb +75 -15
- data/lib/samagotchi/plugin/sessions.rb +1 -1
- data/lib/samagotchi/project_command.rb +167 -0
- data/lib/samagotchi/project_init.rb +69 -0
- data/lib/samagotchi/project_scope.rb +6 -4
- data/lib/samagotchi/prompt_files.rb +10 -7
- data/lib/samagotchi/reasoning_history.rb +14 -20
- data/lib/samagotchi/recap_place.rb +26 -0
- data/lib/samagotchi/reply_wait/liveness.rb +38 -0
- data/lib/samagotchi/reply_wait/poll.rb +44 -15
- data/lib/samagotchi/reply_wait/target.rb +113 -0
- data/lib/samagotchi/reply_wait.rb +22 -2
- data/lib/samagotchi/run_options.rb +1 -0
- data/lib/samagotchi/sampling_settings.rb +9 -16
- data/lib/samagotchi/self_report.rb +34 -35
- data/lib/samagotchi/send_command.rb +17 -23
- data/lib/samagotchi/session.rb +349 -441
- data/lib/samagotchi/session_access/contract.rb +1 -1
- data/lib/samagotchi/session_access/local.rb +5 -6
- data/lib/samagotchi/session_access/remote.rb +5 -3
- data/lib/samagotchi/session_field.rb +134 -0
- data/lib/samagotchi/session_inbox.rb +49 -1
- data/lib/samagotchi/session_manager/emptiness.rb +2 -1
- data/lib/samagotchi/session_manager/listing.rb +29 -100
- data/lib/samagotchi/session_manager.rb +2 -1
- data/lib/samagotchi/session_metrics.rb +7 -4
- data/lib/samagotchi/session_paths.rb +57 -0
- data/lib/samagotchi/session_retention.rb +2 -2
- data/lib/samagotchi/session_row.rb +200 -0
- data/lib/samagotchi/session_setup.rb +15 -12
- data/lib/samagotchi/sessions_command.rb +11 -129
- data/lib/samagotchi/steer.rb +10 -0
- data/lib/samagotchi/steer_cut_after.rb +7 -14
- data/lib/samagotchi/system_prompt.rb +20 -11
- data/lib/samagotchi/terminal_ui/attached_loop.rb +6 -26
- data/lib/samagotchi/terminal_ui/join_replay.rb +91 -8
- data/lib/samagotchi/terminal_ui/local_link.rb +2 -2
- data/lib/samagotchi/terminal_ui.rb +3 -1
- data/lib/samagotchi/testing/bundle.rb +5 -12
- data/lib/samagotchi/testing/install.rb +5 -4
- data/lib/samagotchi/testing/recorder.rb +1 -1
- data/lib/samagotchi/testing.rb +1 -1
- data/lib/samagotchi/thinking.rb +16 -21
- data/lib/samagotchi/tool_declarations.rb +1 -1
- data/lib/samagotchi/tool_response.rb +5 -3
- data/lib/samagotchi/tool_runner.rb +18 -1
- data/lib/samagotchi/toolkit_binding.rb +1 -4
- data/lib/samagotchi/toolkit_command.rb +1 -4
- data/lib/samagotchi/tools/delegate.rb +60 -51
- data/lib/samagotchi/tools/delegate_relay.rb +5 -5
- data/lib/samagotchi/tools/delegate_result.rb +1 -1
- data/lib/samagotchi/tools/delegate_wait.rb +41 -17
- data/lib/samagotchi/tools/list_sessions.rb +7 -7
- data/lib/samagotchi/update_command.rb +11 -7
- data/lib/samagotchi/verify_fresh.rb +7 -11
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +11 -14
- data/lib/samagotchi/web/app.rb +66 -21
- data/lib/samagotchi/web/message_display.rb +57 -7
- data/lib/samagotchi/web/participant_access.rb +1 -1
- data/lib/samagotchi/web/project_list.rb +3 -2
- data/lib/samagotchi/web/public/activity.js +16 -7
- data/lib/samagotchi/web/public/app.js +29 -8
- data/lib/samagotchi/web/public/bubbles.js +8 -3
- data/lib/samagotchi/web/public/cards_place.js +15 -0
- data/lib/samagotchi/web/public/commands.js +5 -1
- data/lib/samagotchi/web/public/data.js +4 -2
- data/lib/samagotchi/web/public/history_shape.js +220 -0
- data/lib/samagotchi/web/public/index.html +6 -1
- data/lib/samagotchi/web/public/project_picker.js +86 -4
- data/lib/samagotchi/web/public/recap_stamp.js +17 -0
- data/lib/samagotchi/web/public/reload_check.js +71 -0
- data/lib/samagotchi/web/public/stage_model.js +7 -5
- data/lib/samagotchi/web/public/stage_view.js +7 -3
- data/lib/samagotchi/web/public/stream_handlers.js +8 -2
- data/lib/samagotchi/web/public/timing.js +43 -2
- data/lib/samagotchi/web/public/turn_block.js +215 -30
- data/lib/samagotchi/web/public/turn_drawing.js +43 -0
- data/lib/samagotchi/web/public/turn_events.js +10 -1
- data/lib/samagotchi/web/public/turn_pieces.js +29 -0
- data/lib/samagotchi/web/reload_check_route.rb +95 -0
- data/lib/samagotchi/web/server.rb +4 -2
- data/lib/samagotchi/web/session_access_routes.rb +3 -3
- data/lib/samagotchi/web/session_control.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +9 -17
- data/lib/samagotchi/web/session_show.rb +64 -2
- data/lib/samagotchi/web/session_summary.rb +36 -24
- data/lib/samagotchi/worker.rb +28 -5
- data/lib/samagotchi/worker_link.rb +3 -0
- metadata +22 -5
- data/lib/samagotchi/hooks/bundle_loader.rb +0 -191
- data/lib/samagotchi/send_command/wait_target.rb +0 -85
|
@@ -5,7 +5,7 @@ require_relative "../session_inbox"
|
|
|
5
5
|
require_relative "../turn_checkpoint"
|
|
6
6
|
require_relative "../continue_offer"
|
|
7
7
|
require_relative "../steer"
|
|
8
|
-
require_relative "
|
|
8
|
+
require_relative "liveness"
|
|
9
9
|
|
|
10
10
|
module Samagotchi
|
|
11
11
|
module ReplyWait
|
|
@@ -72,13 +72,17 @@ module Samagotchi
|
|
|
72
72
|
# @return [Result, nil]
|
|
73
73
|
def look
|
|
74
74
|
look_at_input
|
|
75
|
+
# Before the load: the turn it names began before what the load sees.
|
|
76
|
+
merge = merge_record
|
|
75
77
|
session = Session.load(@id, state_dir: @state_dir)
|
|
78
|
+
# Who runs it, read once for this look's checks.
|
|
79
|
+
@live = Liveness.new(@id, state_dir: @state_dir)
|
|
76
80
|
if (result = removed(session))
|
|
77
81
|
return result
|
|
78
82
|
end
|
|
79
83
|
|
|
80
84
|
note_own_running(session)
|
|
81
|
-
follow_merge(session)
|
|
85
|
+
follow_merge(session, merge)
|
|
82
86
|
reply(session) || settled_turn(session) || status_end(session) || question(session) || held(session) || canceled ||
|
|
83
87
|
ended_before_reply(session) || ended_before_turn(session) || turn_end(session) || owner_gone(session) ||
|
|
84
88
|
message_waiting || timed_out
|
|
@@ -106,8 +110,11 @@ module Samagotchi
|
|
|
106
110
|
Result.new(status: :no_reply, outcome: "removed", session: session)
|
|
107
111
|
end
|
|
108
112
|
|
|
113
|
+
# Its turn ran, or a drain merged it (its merge record): a dequeue's
|
|
114
|
+
# tombstone that lost the claim to the drain is a stray too.
|
|
109
115
|
def turn_ran?(session)
|
|
110
|
-
session.last_turn&.enqueued_id == @turn || !SessionInbox.reply_for(@session_dir, @turn).nil?
|
|
116
|
+
session.last_turn&.enqueued_id == @turn || !SessionInbox.reply_for(@session_dir, @turn).nil? ||
|
|
117
|
+
!SessionInbox.merged_into(@session_dir, @turn).nil?
|
|
111
118
|
end
|
|
112
119
|
|
|
113
120
|
def note_own_running(session)
|
|
@@ -119,14 +126,37 @@ module Samagotchi
|
|
|
119
126
|
|
|
120
127
|
# Taken but never run as a turn of its own: a running turn's drain
|
|
121
128
|
# merged it as a steer, so that turn's reply (or question) is its
|
|
122
|
-
# answer.
|
|
123
|
-
# turn
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
# answer. With the drain's merge record (SessionInbox.mark_merged)
|
|
130
|
+
# the wait is that turn's from here on, as if it had been bound to it
|
|
131
|
+
# (#follow_into). Without one (a 0.156 worker, a turn with no id, a
|
|
132
|
+
# merge from before the update) the merge is inferred
|
|
133
|
+
# (ReplyWait.merged_elsewhere?) and waited for as the next reply past
|
|
134
|
+
# the cursor, as without turn:, never as a turn that will not come.
|
|
135
|
+
def follow_merge(session, merge)
|
|
136
|
+
return unless may_follow?
|
|
137
|
+
|
|
138
|
+
if merge
|
|
139
|
+
follow_into(session, merge)
|
|
140
|
+
elsif ReplyWait.merged_elsewhere?(session, @turn, @baseline, @session_dir)
|
|
141
|
+
@turn = nil
|
|
142
|
+
@seen_running = running?(session)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def may_follow? = @turn && @claimed && !@seen_own && !@own_turn_input
|
|
147
|
+
|
|
148
|
+
def merge_record = may_follow? ? SessionInbox.merged_into(@session_dir, @turn) : nil
|
|
149
|
+
|
|
150
|
+
# Bound to the turn that took it, once (its own input ran as its own
|
|
151
|
+
# turn). Not running in this look, which loaded after the record:
|
|
152
|
+
# that turn is over (its end, its reply, its settle say how), and a
|
|
153
|
+
# turn that ended after the merge with last_turn not its is past it
|
|
154
|
+
# (#own_turn_end).
|
|
155
|
+
def follow_into(session, merge)
|
|
156
|
+
@turn = merge.into
|
|
157
|
+
@own_turn_input = true
|
|
158
|
+
note_own_running(session)
|
|
159
|
+
@merged_at = merge.at unless @seen_own
|
|
130
160
|
end
|
|
131
161
|
|
|
132
162
|
def reply(session)
|
|
@@ -172,9 +202,7 @@ module Samagotchi
|
|
|
172
202
|
end
|
|
173
203
|
|
|
174
204
|
# A worker holds the session's lock (a one-turn chi run's question is its own).
|
|
175
|
-
def worker_owned?
|
|
176
|
-
WorkerLink.resolve(Session.session_dir(@id, state_dir: @state_dir), unlink: false).worker_owned?
|
|
177
|
-
end
|
|
205
|
+
def worker_owned? = @live.worker_owned?
|
|
178
206
|
|
|
179
207
|
# A participant's turn its worker holds while the owner's Continue
|
|
180
208
|
# offer stands (Worker#run_input_files): it waits for the owner's
|
|
@@ -273,6 +301,7 @@ module Samagotchi
|
|
|
273
301
|
# died), or it ended with no reply and a next turn ran and ended
|
|
274
302
|
# between two looks. Waiting on would sit through those turns.
|
|
275
303
|
return Result.new(status: :no_reply, session: session) if @seen_own && ReplyWait.past_own?(session, @turn, @own_base)
|
|
304
|
+
return Result.new(status: :no_reply, session: session) if @merged_at && ReplyWait.ended_after?(session, @merged_at)
|
|
276
305
|
|
|
277
306
|
nil
|
|
278
307
|
end
|
|
@@ -307,7 +336,7 @@ module Samagotchi
|
|
|
307
336
|
def owner_gone(session)
|
|
308
337
|
return nil unless @owner_grace
|
|
309
338
|
|
|
310
|
-
if
|
|
339
|
+
if @live.owner?
|
|
311
340
|
@gone_since = nil
|
|
312
341
|
elsif ReplyWait.monotonic - (@gone_since ||= ReplyWait.monotonic) > @owner_grace
|
|
313
342
|
return Result.new(status: :worker_gone, session: session)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../delivery"
|
|
4
|
+
require_relative "../session"
|
|
5
|
+
|
|
6
|
+
module Samagotchi
|
|
7
|
+
module ReplyWait
|
|
8
|
+
# Which turn a reply wait waits for, and how: the one binding every
|
|
9
|
+
# caller builds (chi send --wait, chi answer, a participant's wait, the
|
|
10
|
+
# delegate tools and a delegate report), each with its constructor.
|
|
11
|
+
# #to_h is ReplyWait.call's kwargs (+cursor+, +baseline+,
|
|
12
|
+
# +owner_grace+, +turn+, +queued+, +ended_before+: ReplyWait.call's
|
|
13
|
+
# parameters say what each means); +report_turn_id+ (the turn_id of
|
|
14
|
+
# --format json's line, ParentWait's) is read on its own.
|
|
15
|
+
Target = Data.define(:turn, :report_turn_id, :queued, :ended_before, :cursor, :baseline, :owner_grace) do
|
|
16
|
+
def initialize(turn: nil, report_turn_id: nil, queued: false, ended_before: false, cursor: nil, baseline: nil,
|
|
17
|
+
owner_grace: nil)
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# A --new session's first message: its own turn, the ack's id (a
|
|
22
|
+
# file-delivered one's also names the outcome of a session command
|
|
23
|
+
# that runs no turn), so a worker that dies in it and the next one's
|
|
24
|
+
# settle end the wait (TurnCheckpoint.settled_turn). A message merged
|
|
25
|
+
# into a turn starting then falls back to that turn's reply
|
|
26
|
+
# (ReplyWait's turn: without queued:); no id (an older worker), the
|
|
27
|
+
# next reply. A prompt the session was created with has no delivery:
|
|
28
|
+
# its turn is the id the create gave it (+direct_turn+,
|
|
29
|
+
# Session#first_turn_id), and nothing can merge into it, so the wait
|
|
30
|
+
# is for that turn alone (ReplyWait's queued:).
|
|
31
|
+
# @param start [SessionAccess::WaitStart] prepare_wait's (its cursor
|
|
32
|
+
# and baseline)
|
|
33
|
+
# @param delivered [SessionManager::DeliverResult, nil] nil when the
|
|
34
|
+
# message started the session itself (no delivery)
|
|
35
|
+
# @param direct_turn [String, nil] that first turn's id, for a direct
|
|
36
|
+
# start
|
|
37
|
+
def self.first_message(start, delivered, direct_turn: nil)
|
|
38
|
+
reads = { cursor: start&.cursor, baseline: start&.baseline, owner_grace: ReplyWait::GONE_AFTER }
|
|
39
|
+
return new(**reads) unless delivered || !direct_turn.to_s.strip.empty?
|
|
40
|
+
|
|
41
|
+
return new(turn: direct_turn, report_turn_id: direct_turn, queued: true, **reads) if delivered.nil?
|
|
42
|
+
|
|
43
|
+
new(turn: delivered.enqueued_id, report_turn_id: delivered.enqueued_id, **reads)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A message to an existing session: its own turn, the ack's id. With
|
|
47
|
+
# --queue it runs as that turn, also when the turn running at the
|
|
48
|
+
# check ended before it went in (an idle worker's ack says next_step,
|
|
49
|
+
# and the file still runs as that turn). A plain one may run as its
|
|
50
|
+
# own turn or merge into a running one (a session left running by a
|
|
51
|
+
# dead worker, the busy check's owner gone; or a turn starting then):
|
|
52
|
+
# that turn's end is its answer (ReplyWait's follow_merge). A plain
|
|
53
|
+
# message the worker queued (its ack says queue: a participant's
|
|
54
|
+
# review turn was running, which takes no steer) is a --queue's. An
|
|
55
|
+
# ack with no id (an older worker): the next reply, reported as the
|
|
56
|
+
# turn that was running when it went in (start's own_turn).
|
|
57
|
+
# @param start [SessionAccess::WaitStart] read before it went in (its
|
|
58
|
+
# running says the session was running then)
|
|
59
|
+
def self.message(start, delivered, queue:)
|
|
60
|
+
queue ||= delivered.delivery == Delivery::QUEUE
|
|
61
|
+
turn = delivered.enqueued_id
|
|
62
|
+
new(turn: turn, report_turn_id: turn || (start.own_turn if start.running && !queue), queued: queue && !turn.nil?,
|
|
63
|
+
cursor: start.cursor, baseline: start.baseline, owner_grace: ReplyWait::GONE_AFTER)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# --wait with no message: --turn's (+requested+), or the turn running
|
|
67
|
+
# now or whose continue offer stands (start's own_turn,
|
|
68
|
+
# ReplyWait.own_turn). Settled (idle, a turn ended, no question
|
|
69
|
+
# standing: a continue offer waits for its answer), its last turn's
|
|
70
|
+
# end is the answer (ended_before); with --turn, when that turn is the
|
|
71
|
+
# last one. A session no worker runs waits for whatever wakes one (no
|
|
72
|
+
# owner_grace).
|
|
73
|
+
# @param start [SessionAccess::WaitStart]
|
|
74
|
+
def self.wait_only(start, requested:)
|
|
75
|
+
turn = requested || start.own_turn
|
|
76
|
+
ended_before = requested ? start.last_turn_id == requested : start.settled
|
|
77
|
+
new(turn: turn, report_turn_id: turn || (start.last_turn_id if start.settled), ended_before: ended_before,
|
|
78
|
+
cursor: start.cursor, baseline: start.baseline, owner_grace: start.live ? ReplyWait::GONE_AFTER : nil)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# chi answer: the turn the question is about (start's own_turn: the
|
|
82
|
+
# one running, or the one whose continue offer stands), else the next
|
|
83
|
+
# reply.
|
|
84
|
+
def self.answer(start)
|
|
85
|
+
new(turn: start.own_turn, report_turn_id: start.own_turn, cursor: start.cursor, baseline: start.baseline,
|
|
86
|
+
owner_grace: ReplyWait::GONE_AFTER)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# A participant's --wait -m: their review turn just queued (+turn+),
|
|
90
|
+
# as --wait --turn T (their wait routes take only a turn of theirs).
|
|
91
|
+
def self.participant(start, turn:) = wait_only(start, requested: turn)
|
|
92
|
+
|
|
93
|
+
# A delegate's wait (DelegateWait, a delegate report) from the
|
|
94
|
+
# parent's DelegateCursor: the newest reply it was given, the child as
|
|
95
|
+
# it was then, and the turn a follow-up joined. The child's first turn
|
|
96
|
+
# (Session#first_turn_id: the prompt it was spawned with) runs as its
|
|
97
|
+
# own and nothing merges into it, so a wait bound to it is queued.
|
|
98
|
+
# @param cursor [Tools::DelegateCursor]
|
|
99
|
+
# @param child [Session, nil] the child as read now (its first_turn_id)
|
|
100
|
+
# @param owner_grace [Numeric, nil] nil: no gone check (a report's
|
|
101
|
+
# look)
|
|
102
|
+
def self.from_cursor(cursor, child:, owner_grace: nil)
|
|
103
|
+
first = child&.first_turn_id
|
|
104
|
+
new(turn: cursor.turn, cursor: cursor.reply_file, baseline: cursor.baseline, owner_grace: owner_grace,
|
|
105
|
+
queued: !cursor.turn.to_s.empty? && cursor.turn == first)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# ReplyWait.call's kwargs (report_turn_id is ParentWait's, not one).
|
|
109
|
+
def to_h = { cursor: cursor, baseline: baseline, owner_grace: owner_grace, turn: turn, queued: queued,
|
|
110
|
+
ended_before: ended_before }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -6,6 +6,7 @@ require_relative "session_inbox"
|
|
|
6
6
|
require_relative "turn_checkpoint"
|
|
7
7
|
require_relative "worker_link"
|
|
8
8
|
require_relative "reply_wait/poll"
|
|
9
|
+
require_relative "reply_wait/target"
|
|
9
10
|
|
|
10
11
|
module Samagotchi
|
|
11
12
|
# Loaded on first use: this autoload keeps SessionManager and its ~80
|
|
@@ -21,6 +22,12 @@ module Samagotchi
|
|
|
21
22
|
# The delegate tools and `chi send --wait` wrap it in their own words.
|
|
22
23
|
module ReplyWait
|
|
23
24
|
POLL_INTERVAL = 0.5
|
|
25
|
+
# The one dead-worker rule: no owner of any kind this long while
|
|
26
|
+
# waiting (a Target's owner_grace) and the worker is gone (it died
|
|
27
|
+
# before it could mark the session). A caller that just spawned or woke
|
|
28
|
+
# a worker awaits it first (Tools::Delegate.await_worker,
|
|
29
|
+
# SendCommand#deliver_new), so a booting worker isn't read as gone.
|
|
30
|
+
GONE_AFTER = 5
|
|
24
31
|
# A worker's sidecar feature (Bridge::FEATURES): its running turn's
|
|
25
32
|
# checkpoint mark (InterruptedTurn#enqueued_id), a continue turn and a
|
|
26
33
|
# Stop's last_turn carry the turn's enqueued_id (#own_turn).
|
|
@@ -209,7 +216,8 @@ module Samagotchi
|
|
|
209
216
|
end
|
|
210
217
|
|
|
211
218
|
# Whether +turn+ is a turn the caller can wait for: running now, queued
|
|
212
|
-
# as its own turn,
|
|
219
|
+
# as its own turn, the last one to have ended, or an input a turn's
|
|
220
|
+
# drain merged (its merge record: the wait follows it to that turn).
|
|
213
221
|
def known_turn?(session, session_dir:, turn:)
|
|
214
222
|
id = turn.to_s
|
|
215
223
|
return false if id.empty?
|
|
@@ -217,7 +225,7 @@ module Samagotchi
|
|
|
217
225
|
running = session.status == Session::STATUS_RUNNING && session.interrupted_turn&.enqueued_id == id
|
|
218
226
|
queued = SessionInbox.waiting_input(session_dir, id) == :turn_end
|
|
219
227
|
ended = session.last_turn&.enqueued_id == id || !TurnCheckpoint.settled_turn(session_dir, id).nil?
|
|
220
|
-
running || queued || ended
|
|
228
|
+
running || queued || ended || !SessionInbox.merged_into(session_dir, id).nil?
|
|
221
229
|
end
|
|
222
230
|
|
|
223
231
|
# Whether the session's live worker names TURN_IDS_FEATURE.
|
|
@@ -287,6 +295,18 @@ module Samagotchi
|
|
|
287
295
|
!ended.nil? && ended != own_base
|
|
288
296
|
end
|
|
289
297
|
|
|
298
|
+
# Whether the session's last turn ended after +at+ (a Time): a turn
|
|
299
|
+
# that isn't own_turn_end's (its last_turn is checked first) ended
|
|
300
|
+
# after the turn a merge record named began, so that turn is over.
|
|
301
|
+
# Clock skew can't fire it: +at+ and every ended_at come from the
|
|
302
|
+
# same machine's clock (its workers'; a remote wait's Poll runs there).
|
|
303
|
+
def ended_after?(session, at)
|
|
304
|
+
ended = session.last_turn&.ended_at
|
|
305
|
+
!ended.to_s.empty? && Time.iso8601(ended.to_s) > at
|
|
306
|
+
rescue ArgumentError
|
|
307
|
+
false
|
|
308
|
+
end
|
|
309
|
+
|
|
290
310
|
# @return [Result, nil] :done with the reply of the turn that ran the
|
|
291
311
|
# input +turn+ (its enqueued_id)
|
|
292
312
|
def reply_for_turn(id, state_dir:, turn:)
|
|
@@ -213,6 +213,7 @@ module Samagotchi
|
|
|
213
213
|
chi desktop <install|upgrade|uninstall|status> macOS "Send to chi" helper
|
|
214
214
|
chi models [--format json] [TEXT] the models --model takes, from every host
|
|
215
215
|
chi home [set [--ruby PATH] | doctor] the Ruby every chi launch boots
|
|
216
|
+
chi project [init [DIR]] this folder's project; init makes a folder in no git repository one
|
|
216
217
|
chi remote <add|ls|rm> other machines' chi web, for chi --remote NAME
|
|
217
218
|
chi --remote NAME <send|answer|note|context|sessions|share|review> … those commands on that machine (or CHI_REMOTE=NAME)
|
|
218
219
|
chi --remote NAME --attach ID | --resume ID | --dir DIR [-p TEXT] a session on that machine, in this terminal
|
|
@@ -11,18 +11,16 @@ module Samagotchi
|
|
|
11
11
|
module SamplingSettings
|
|
12
12
|
EMPTY = {}.freeze
|
|
13
13
|
|
|
14
|
-
# @param
|
|
15
|
-
# @param names [Array<String>] the model's lookup names (HostRegistry#lookup_names)
|
|
16
|
-
# @param models [Hash{String => ModelSettings}, nil] ModelSettings.all (specs)
|
|
14
|
+
# @param lookup [ModelLookup] the model's layers (EffectiveModel#lookup)
|
|
17
15
|
# @return [Hash] frozen, symbol keys; may be empty
|
|
18
|
-
def self.for(
|
|
19
|
-
resolve(
|
|
16
|
+
def self.for(lookup)
|
|
17
|
+
resolve(lookup).first
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
# "temperature=0.6 presence_penalty=1.5 (hosts.work, models: qwen)" for
|
|
23
21
|
# /model, or nil when nothing is configured.
|
|
24
|
-
def self.summary(
|
|
25
|
-
params, sources = resolve(
|
|
22
|
+
def self.summary(lookup)
|
|
23
|
+
params, sources = resolve(lookup)
|
|
26
24
|
return nil if params.empty?
|
|
27
25
|
|
|
28
26
|
pairs = params.map { |key, value| "#{key}=#{value.nil? ? "(not sent)" : value.inspect}" }
|
|
@@ -37,16 +35,11 @@ module Samagotchi
|
|
|
37
35
|
fields.map { |key, value| "#{key}=#{value.is_a?(String) ? value : JSON.generate(value)}" }.join(" ")
|
|
38
36
|
end
|
|
39
37
|
|
|
40
|
-
def self.resolve(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
rescue StandardError
|
|
44
|
-
{}
|
|
45
|
-
end
|
|
46
|
-
host = target.entry.sampling || EMPTY
|
|
47
|
-
model = ModelSettings.find(names, :sampling, models: models)
|
|
38
|
+
def self.resolve(lookup)
|
|
39
|
+
host = lookup.host(:sampling) || EMPTY
|
|
40
|
+
model = lookup.model(:sampling)
|
|
48
41
|
sources = []
|
|
49
|
-
sources << "hosts.#{
|
|
42
|
+
sources << "hosts.#{lookup.host_name}" unless host.empty?
|
|
50
43
|
sources << "models: #{model.key}" if model
|
|
51
44
|
[host.merge(model&.value || EMPTY).freeze, sources]
|
|
52
45
|
end
|
|
@@ -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
|
-
|
|
185
|
-
|
|
186
|
-
names = registry.lookup_names(model, target: target)
|
|
187
|
-
models = ModelSettings.all(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.#{
|
|
188
|
+
return [window.tokens, "hosts.#{effective.host_name}"] if window.source == :host_setting
|
|
191
189
|
|
|
192
|
-
[window.tokens, "models: #{
|
|
190
|
+
[window.tokens, "models: #{effective.lookup.model(:window_tokens).key}"]
|
|
193
191
|
end
|
|
194
192
|
end
|
|
195
193
|
|
|
@@ -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(
|
|
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 =
|
|
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(
|
|
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
|
|
@@ -311,7 +309,7 @@ module Samagotchi
|
|
|
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 =
|
|
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
|
-
|
|
331
|
-
target =
|
|
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 =
|
|
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
|
-
|
|
378
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
models: ModelSettings.all(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(
|
|
404
|
-
models: ModelSettings.all(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
|
-
|
|
415
|
-
|
|
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
|
-
|
|
420
|
-
models: ModelSettings.all(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 =
|
|
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 =
|
|
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.
|
|
197
|
+
registry ||= HostRegistry.current
|
|
197
198
|
name = ModelProfile.required_model_name(model_name)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
525
|
-
|
|
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 =
|
|
605
|
-
|
|
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
|
-
|
|
626
|
-
|
|
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 =
|
|
652
|
-
wait_for_reply(id,
|
|
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:
|
|
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?
|