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
|
@@ -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
|