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
|
@@ -39,7 +39,7 @@ module Samagotchi
|
|
|
39
39
|
List installed bundles and the ones shipped with chi that aren't installed.
|
|
40
40
|
|
|
41
41
|
build [--scope system|project] [--name NAME] [--version VER] [--description DESC] [--out PATH] [FILES...]
|
|
42
|
-
Build local memories and installed
|
|
42
|
+
Build local memories and an installed plugin, rules and scripts into a shareable bundle (dir or zip).
|
|
43
43
|
--scope selects source dir (default: system). --out inferred from extension; default <name>.zip.
|
|
44
44
|
FILES... optional allowlist of *.md basenames to include (default: all but installed bundles' ones);
|
|
45
45
|
a named memory brings its model overlays (<name>.<model-key>.md).
|
|
@@ -75,7 +75,7 @@ module Samagotchi
|
|
|
75
75
|
It also installs a bundle whose requires_chi this chi doesn't meet: without --force that install is
|
|
76
76
|
refused, and the bundle is not written anywhere.
|
|
77
77
|
install --dev PATH
|
|
78
|
-
Install the bundle in the local directory PATH as a link to it: its plugin,
|
|
78
|
+
Install the bundle in the local directory PATH as a link to it: its plugin, rules and scripts
|
|
79
79
|
are PATH's own files (an edit applies at the next worker or chi web restart, with no re-install) and
|
|
80
80
|
their sha256 isn't checked, for this bundle only. Its memories are copied; re-install with --force to
|
|
81
81
|
pick up memory edits. Upgrade refuses it; uninstall removes the link, never PATH's files.
|
|
@@ -90,7 +90,7 @@ module Samagotchi
|
|
|
90
90
|
Usage: chi bundle build [--scope system|project] [--name NAME] [--version VER] [--description DESC] [--out PATH] [FILES...]
|
|
91
91
|
|
|
92
92
|
build [--scope system|project] [--name NAME] [--version VER] [--description DESC] [--out PATH] [FILES...]
|
|
93
|
-
Build local memories and installed
|
|
93
|
+
Build local memories and an installed plugin, rules and scripts into a shareable bundle (dir or zip).
|
|
94
94
|
--scope selects source dir (default: system). --out inferred from extension; default <name>.zip.
|
|
95
95
|
FILES... optional allowlist of *.md basenames to include (default: all but installed bundles' ones);
|
|
96
96
|
a named memory brings its model overlays (<name>.<model-key>.md).
|
|
@@ -200,7 +200,7 @@ module Samagotchi
|
|
|
200
200
|
|
|
201
201
|
installer = installer_for(expanded_source, bundle_name, prepared: prepared, scope: scope, force: force, dev: dev)
|
|
202
202
|
run_installer(installer, failed: "Install") do |manifest|
|
|
203
|
-
|
|
203
|
+
print_plugin(manifest)
|
|
204
204
|
print_provenance(bundle_name, manifest)
|
|
205
205
|
end
|
|
206
206
|
end
|
|
@@ -236,7 +236,7 @@ module Samagotchi
|
|
|
236
236
|
|
|
237
237
|
unless Samagotchi::MemoryBundle::Provenance.new(name: bundle_name).installed?
|
|
238
238
|
@stderr.puts "Bundle '#{bundle_name}' not installed — falling back to install"
|
|
239
|
-
# (no
|
|
239
|
+
# (no Plugin line here)
|
|
240
240
|
installer = installer_for(expanded_source, bundle_name, prepared: prepared, scope: scope, force: force, dry_run: dry_run)
|
|
241
241
|
return run_installer(installer, failed: "Install") do |manifest|
|
|
242
242
|
next dry_run_done if dry_run
|
|
@@ -246,7 +246,7 @@ module Samagotchi
|
|
|
246
246
|
end
|
|
247
247
|
installer = installer_for(expanded_source, bundle_name, prepared: prepared, scope: scope, force: force, upgrade: true, dry_run: dry_run)
|
|
248
248
|
run_installer(installer, failed: "Upgrade") do |manifest|
|
|
249
|
-
|
|
249
|
+
print_plugin(manifest, plugin: !dry_run)
|
|
250
250
|
next dry_run_done if dry_run
|
|
251
251
|
|
|
252
252
|
# Its summary said so, to someone reading.
|
|
@@ -304,7 +304,7 @@ module Samagotchi
|
|
|
304
304
|
# Runs +installer+, prints its summary, then yields the manifest.
|
|
305
305
|
# Ensures the extracted source dir is cleaned up even if an exception
|
|
306
306
|
# occurs between installer.run and the block (e.g. summary,
|
|
307
|
-
#
|
|
307
|
+
# print_plugin). The block (upgrade_conflicts) still sees
|
|
308
308
|
# the files because cleanup happens after the block returns or raises.
|
|
309
309
|
# @return [Integer] the block's exit status, or 1 after "<failed> failed: …"
|
|
310
310
|
def run_installer(installer, failed:)
|
|
@@ -318,8 +318,7 @@ module Samagotchi
|
|
|
318
318
|
installer.cleanup_source!
|
|
319
319
|
end
|
|
320
320
|
|
|
321
|
-
def
|
|
322
|
-
@stdout.puts "Hooks: #{manifest.hooks.size} hook(s) (#{manifest.hooks.keys.join(", ")})" if manifest&.hooks&.any?
|
|
321
|
+
def print_plugin(manifest, plugin: true)
|
|
323
322
|
return unless plugin && manifest&.plugin
|
|
324
323
|
|
|
325
324
|
# Installed with --force past its requires_chi: the Engine won't load it.
|
|
@@ -425,8 +424,6 @@ module Samagotchi
|
|
|
425
424
|
puts_trashed(uninstaller.trashed_files, uninstaller.trash_dir)
|
|
426
425
|
@stdout.puts uninstaller.trash_copy_line if uninstaller.trash_copy_line
|
|
427
426
|
@stdout.puts "Removed: #{uninstaller.removed_files.join(", ")}" unless uninstaller.removed_files.empty?
|
|
428
|
-
hook_removed = uninstaller.removed_files.count { |f| f.start_with?("hooks/") }
|
|
429
|
-
@stdout.puts "Hooks removed: #{hook_removed}" if hook_removed > 0
|
|
430
427
|
uninstaller.warnings.each { |w| @stdout.puts w }
|
|
431
428
|
0
|
|
432
429
|
rescue Samagotchi::MemoryBundle::Uninstaller::UninstallError => e
|
|
@@ -519,22 +516,14 @@ module Samagotchi
|
|
|
519
516
|
@stdout.puts " #{k}: #{label}"
|
|
520
517
|
end
|
|
521
518
|
@stdout.puts " #{manifest_line(st.provenance)}"
|
|
522
|
-
# Hooks
|
|
523
519
|
manifest = st.provenance.manifest
|
|
524
|
-
hooks = manifest.hooks
|
|
525
520
|
trust = manifest.trust_level || "experimental"
|
|
526
521
|
commit = st.provenance.source_commit
|
|
527
|
-
@stdout.puts " trust_level: #{trust}"
|
|
522
|
+
@stdout.puts " trust_level: #{trust}"
|
|
528
523
|
@stdout.puts " source_commit: #{commit}" if commit
|
|
529
|
-
|
|
530
|
-
@stdout.puts " Hooks (#{
|
|
531
|
-
|
|
532
|
-
hook_path = File.join(Samagotchi::MemoryBundle::Provenance.new(name: name).hooks_dir, hk)
|
|
533
|
-
exists = File.exist?(hook_path) ? "ok" : "missing"
|
|
534
|
-
@stdout.puts " #{hk}: #{hook.events_field} on_error=#{hook.on_error} priority=#{hook.priority} [#{exists}]"
|
|
535
|
-
end
|
|
536
|
-
@stdout.puts " requires_chi: #{manifest.requires_chi}" if st.hooks_requires_failure
|
|
537
|
-
@stdout.puts " not loaded: #{st.hooks_requires_failure}" if st.hooks_requires_failure
|
|
524
|
+
unless st.retired_hooks.empty?
|
|
525
|
+
@stdout.puts " Hooks (#{st.retired_hooks.size}): not loaded: #{Samagotchi::MemoryBundle::Manifest::HooksRetired::WORDS}"
|
|
526
|
+
st.retired_hooks.each { |file, state| @stdout.puts " #{file} [#{state}]" }
|
|
538
527
|
end
|
|
539
528
|
if (plugin = st.plugin)
|
|
540
529
|
note = plugin[:state] == "modified" ? " (edited after install: not loaded; reinstall the bundle)" : ""
|
|
@@ -555,11 +544,10 @@ module Samagotchi
|
|
|
555
544
|
|
|
556
545
|
st = Samagotchi::MemoryBundle::Status.bundle_status(bname)
|
|
557
546
|
mods = st.files.values.count { |v| v[:behind] || v[:missing] || v[:unreadable] }
|
|
558
|
-
|
|
559
|
-
hook_info = hooks_count > 0 ? " hooks=#{hooks_count}" : ""
|
|
547
|
+
hook_info = st.retired_hooks.empty? ? "" : " hooks=#{st.retired_hooks.size}"
|
|
560
548
|
plugin = st.plugin
|
|
561
549
|
mods += 1 if plugin && (plugin[:state] != "ok" || plugin[:requires_failure])
|
|
562
|
-
mods += 1
|
|
550
|
+
mods += 1 unless st.retired_hooks.empty?
|
|
563
551
|
mods += 1 if st.scope_error
|
|
564
552
|
plugin_info = plugin ? " plugin=#{plugin[:file]}" : ""
|
|
565
553
|
includes = st.provenance.includes
|
|
@@ -606,15 +594,12 @@ module Samagotchi
|
|
|
606
594
|
rescue ArgumentError => e
|
|
607
595
|
@stderr.puts "Bundle '#{bname}': #{e.message}"; return 1
|
|
608
596
|
end
|
|
609
|
-
hooks = record.manifest.hooks
|
|
610
597
|
plugin_path = prov.plugin_path(record)
|
|
611
598
|
plugin_file = plugin_path && File.basename(plugin_path)
|
|
612
599
|
if file_arg
|
|
613
|
-
#
|
|
600
|
+
# A memory first, then the plugin (an older chi's hook is no file of the bundle's)
|
|
614
601
|
if record.owns?(file_arg)
|
|
615
602
|
diff_memory(prov, target_dir, file_arg)
|
|
616
|
-
elsif hooks.key?(file_arg)
|
|
617
|
-
diff_hook(prov, file_arg, hooks[file_arg])
|
|
618
603
|
elsif plugin_file && [plugin_file, "plugin/#{plugin_file}"].include?(file_arg)
|
|
619
604
|
diff_plugin(prov, record, plugin_path)
|
|
620
605
|
else
|
|
@@ -627,10 +612,6 @@ module Samagotchi
|
|
|
627
612
|
diff_memory(prov, target_dir, key)
|
|
628
613
|
@stdout.puts ""
|
|
629
614
|
end
|
|
630
|
-
hooks.each do |key, hook|
|
|
631
|
-
diff_hook(prov, key, hook)
|
|
632
|
-
@stdout.puts ""
|
|
633
|
-
end
|
|
634
615
|
if plugin_file
|
|
635
616
|
diff_plugin(prov, record, plugin_path)
|
|
636
617
|
@stdout.puts ""
|
|
@@ -639,9 +620,12 @@ module Samagotchi
|
|
|
639
620
|
0
|
|
640
621
|
end
|
|
641
622
|
|
|
623
|
+
# The base and the file; a marked one also the bundle's text it stays
|
|
624
|
+
# behind (its source-less FileState says which).
|
|
642
625
|
def diff_memory(prov, target_dir, key)
|
|
643
|
-
|
|
644
|
-
|
|
626
|
+
state = Samagotchi::MemoryBundle::FileState.of(key, record: prov.record, provenance: prov, target_dir: target_dir, source_dir: nil)
|
|
627
|
+
print_base_and_current(key, state.paths[:base], state.paths[:current])
|
|
628
|
+
print_incoming(prov, key) if state.marked?
|
|
645
629
|
end
|
|
646
630
|
|
|
647
631
|
# The bundle's text a file stays behind (Provenance#incoming_path), so the
|
|
@@ -663,11 +647,6 @@ module Samagotchi
|
|
|
663
647
|
end
|
|
664
648
|
end
|
|
665
649
|
|
|
666
|
-
def diff_hook(prov, key, hook)
|
|
667
|
-
print_base_and_current("hooks/#{key}", prov.base_path(key), File.join(prov.hooks_dir, key))
|
|
668
|
-
@stdout.puts hook_metadata_line(hook)
|
|
669
|
-
end
|
|
670
|
-
|
|
671
650
|
def diff_plugin(prov, record, plugin_path)
|
|
672
651
|
file = File.basename(plugin_path)
|
|
673
652
|
print_base_and_current("plugin/#{file}", prov.plugin_base_path(file), plugin_path)
|
|
@@ -730,10 +709,6 @@ module Samagotchi
|
|
|
730
709
|
"includes=#{here.join(",")}#{" left out=#{gone.join(",")}" unless gone.empty?}"
|
|
731
710
|
end
|
|
732
711
|
|
|
733
|
-
def hook_metadata_line(hook)
|
|
734
|
-
"--- metadata: #{hook.events_field} on_error=#{hook.on_error} priority=#{hook.priority} sha256=#{hook.sha256}"
|
|
735
|
-
end
|
|
736
|
-
|
|
737
712
|
def build(rest)
|
|
738
713
|
parsed = parse_flags(BUILD_FLAGS, rest, "build", help: BUILD_HELP)
|
|
739
714
|
return parsed if parsed.is_a?(Integer)
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: known-names
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.2.0
|
|
4
4
|
requires_chi: ">= 0.150.0"
|
|
5
5
|
scope: system
|
|
6
6
|
description: Catches a near-miss of a protected name (home folder, login, git name, repo name, names from config) in a tool call and rejects it with the right spelling; mode ask / correct optional
|
|
7
7
|
trust_level: reviewed
|
|
8
8
|
files:
|
|
9
9
|
known_names.md: sha256:d5348b95b6c655017b541910b36e384220da280818160201f6ec9a50e1e4287c
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
event: before_tool_call
|
|
14
|
-
on_error: log
|
|
15
|
-
priority: 50
|
|
10
|
+
plugin:
|
|
11
|
+
file: plugin.rb
|
|
12
|
+
sha256: sha256:d140612e6c34a9d2d205bf818a2a0797ad1e133829481f354aaa9e56698017e3
|
|
16
13
|
# config.yml bundles: known-names: (another key warns; an older chi ignores this)
|
|
17
14
|
settings: [names, ignore, mode, derive, min_length, max_distance]
|
|
@@ -243,3 +243,20 @@ class KnownNames
|
|
|
243
243
|
Array(value).map { |name| name.to_s.strip }.reject(&:empty?)
|
|
244
244
|
end
|
|
245
245
|
end
|
|
246
|
+
|
|
247
|
+
# What chi loads: one KnownNames, built once from config.yml
|
|
248
|
+
# `bundles: known-names:` when the plugin loads, on before_tool_call at
|
|
249
|
+
# priority 50. A raise in the handler is logged and the call goes on
|
|
250
|
+
# (chi.on's log policy); KnownNames#call turns its own errors into a
|
|
251
|
+
# notice first.
|
|
252
|
+
class Plugin
|
|
253
|
+
PRIORITY = 50
|
|
254
|
+
|
|
255
|
+
def initialize(settings = {})
|
|
256
|
+
@names = KnownNames.new(settings)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def register(chi)
|
|
260
|
+
chi.on(:before_tool_call, priority: PRIORITY) { |event, _ctx| @names.call(event) }
|
|
261
|
+
end
|
|
262
|
+
end
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: source-links
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.7.0
|
|
4
4
|
scope: system
|
|
5
5
|
description: Links source refs (JIRA tickets, GitHub issues, …) in the model's answer — inline links in the web, refs in a waiting question's reply, and a one-line note after the turn (note false turns it off); sources are configured patterns
|
|
6
6
|
trust_level: reviewed
|
|
7
7
|
requires_chi: ">= 0.150.0"
|
|
8
8
|
files:
|
|
9
9
|
source_links.md: sha256:ace01a0f7c308a0ae54ece714c31c0e6d7a49c372d98111030e6d268320bed3a
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
events: [after_turn, question_asked]
|
|
14
|
-
on_error: log
|
|
15
|
-
priority: 90
|
|
10
|
+
plugin:
|
|
11
|
+
file: plugin.rb
|
|
12
|
+
sha256: sha256:b4a777d8b6135f80e248dd21b5370c91dfcf2aea17057734e77241eb9cc4afe4
|
|
16
13
|
# config.yml bundles: source-links: (another key warns; an older chi ignores this)
|
|
17
14
|
settings: [sources, max, note]
|
|
@@ -59,10 +59,10 @@ require "samagotchi/ref"
|
|
|
59
59
|
# action, "Open in <name>", the ref's URL. A URL that isn't http(s) stays a
|
|
60
60
|
# markdown link only. The web's ref menu shows the action.
|
|
61
61
|
#
|
|
62
|
-
# The hook is on :question_asked too (
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
62
|
+
# The hook is on :question_asked too (Plugin below hooks both events): while
|
|
63
|
+
# a question of the model's (or an approval of its call) waits, the refs in
|
|
64
|
+
# the reply that asks it, and in the question's text and options, are refs
|
|
65
|
+
# on that reply, the same as an answer's. The web pairs
|
|
66
66
|
# them in the stage's asking slot, on the question's card and on that step
|
|
67
67
|
# in the history (a ref only in the question's text pairs there alone). No
|
|
68
68
|
# links in the text and no note line for it: the turn's end has those.
|
|
@@ -657,3 +657,22 @@ class SourceLinks
|
|
|
657
657
|
value.to_s.gsub(/[^A-Za-z0-9\-._~]/) { |c| c.bytes.map { |b| format("%%%02X", b) }.join }
|
|
658
658
|
end
|
|
659
659
|
end
|
|
660
|
+
|
|
661
|
+
# What chi loads: one SourceLinks, built once from config.yml
|
|
662
|
+
# `bundles: source-links:` when the plugin loads, on after_turn and
|
|
663
|
+
# question_asked at priority 90 (its state, the remotes it asked git for,
|
|
664
|
+
# is shared). A raise in the handler is logged and the turn goes on
|
|
665
|
+
# (chi.on's log policy).
|
|
666
|
+
class Plugin
|
|
667
|
+
PRIORITY = 90
|
|
668
|
+
|
|
669
|
+
def initialize(settings = {})
|
|
670
|
+
@links = SourceLinks.new(settings)
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
def register(chi)
|
|
674
|
+
%i[after_turn question_asked].each do |event|
|
|
675
|
+
chi.on(event, priority: PRIORITY) { |payload, _ctx| @links.call(payload) }
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
end
|
|
@@ -76,7 +76,7 @@ guardrails:
|
|
|
76
76
|
disable: [guardrails:git-rebase]
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
- `bundles:` map of installed bundle name → its settings (one Hash, string keys, handed to the bundle's
|
|
79
|
+
- `bundles:` map of installed bundle name → its settings (one Hash, string keys, handed to the bundle's plugin `initialize(settings)`). Read when a session's Engine starts: after a change restart the session's worker (`chi sessions stop <id>`). Unknown keys are ignored by the bundle, not validated. `chi bundle list` names the bundles; each bundle's keys are in `docs/bundles.md` / `docs/guardrails.md`.
|
|
80
80
|
|
|
81
81
|
```yaml
|
|
82
82
|
bundles:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: samagotchi-system
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.158.0
|
|
4
4
|
scope: system
|
|
5
5
|
description: Default system memories — identity, self map, config modification protocol, memory guide and the delegated-session rules
|
|
6
6
|
files:
|
|
7
7
|
identity.md: sha256:29c2f7df9f6c5acbc5ec17000a4490741e55e426af15ca0d81c8706edd9bd027
|
|
8
|
-
self_map.md: sha256:
|
|
9
|
-
config_modification_protocol.md: sha256:
|
|
10
|
-
memory_guide.md: sha256:
|
|
8
|
+
self_map.md: sha256:750822b3488b302c6e32638e6224f270d8e06b50ea85cd6e0bb5a6ff12eed1d8
|
|
9
|
+
config_modification_protocol.md: sha256:44cf7bdac8e526805bff36b76b54fefecd41db30ca525c2ed661d78fe9155dd8
|
|
10
|
+
memory_guide.md: sha256:91e8fd248a155afad6b825097d9ee0a0215ba0b79092c8ab25501c0fc19dedfc
|
|
11
11
|
delegated.md: sha256:a0d6ddcd4a8be06526d964898611775fd251cfd9d145a9d16afaccaa503597bd
|
|
@@ -15,7 +15,7 @@ This memory teaches you (the agent) how to use Samagotchi memories — persisten
|
|
|
15
15
|
| `system` | `$XDG_CONFIG_HOME/samagotchi/memories/`, default `~/.config/samagotchi/memories/` (`MemoryPaths.system_dir`) | User-wide preferences, identity, cross-project knowledge |
|
|
16
16
|
| `project` | `<system dir>/projects/<basename>_<hash>/` (`MemoryPaths.project_dir`: `basename(root)` + 8-char `MD5(root)`, root = `MemoryPaths.project_root`) | Repo-specific conventions, workflow, stack decisions |
|
|
17
17
|
|
|
18
|
-
- The project root is the git repository: its common git dir, which every linked worktree shares. All worktrees and subdirectories of one repository share one project folder; the system prompt shows that folder (not the root: in a linked worktree, work stays in the worktree). Outside a repository
|
|
18
|
+
- The project root is the git repository: its common git dir, which every linked worktree shares. All worktrees and subdirectories of one repository share one project folder; the system prompt shows that folder (not the root: in a linked worktree, work stays in the worktree). A separate clone is a different project. Outside a repository, a folder marked by `chi project init` (its `.chi/project.yml`) is the project for it and every subfolder; outside both, the root is the working directory.
|
|
19
19
|
- `index.md` lives in each scope dir and contains auto-managed lines like `- **name** · scope · date · bytes — description`; a memory a bundle installed adds `· from <bundle>` before the description. Your free-form sections in `index.md` are preserved but managed lines are owned by `memory_write` (and refreshed when `write`/`edit` change a memory's file).
|
|
20
20
|
|
|
21
21
|
## Tools you have
|
|
@@ -101,11 +101,11 @@ Writing or updating a toolkit memory is free like writing a skill; say so in the
|
|
|
101
101
|
|
|
102
102
|
## Memory Bundles — shareable packs
|
|
103
103
|
|
|
104
|
-
Bundles are versioned directories/zips/tar.gz/git URLs with a `manifest.yml` and any of: memories (`*.md`), `
|
|
104
|
+
Bundles are versioned directories/zips/tar.gz/git URLs with a `manifest.yml` and any of: memories (`*.md`), `guardrails/*.yml` (rules, `docs/guardrails.md`), a `plugin.rb` (commands, tools, hooks, services; `docs/plugins.md`; class-file `hooks/*.rb` are retired: chi refuses a source with them, `docs/hooks.md`). They are shareable and installable.
|
|
105
105
|
|
|
106
|
-
- Manifest: `files:` (memory → `sha256:`), `
|
|
107
|
-
- Integrity: each file's sha256 is recorded at install; a
|
|
108
|
-
- Installing = trusting its Ruby code (
|
|
106
|
+
- Manifest: `files:` (memory → `sha256:`), `plugin: {file: plugin.rb, sha256: sha256:…}`, `requires_chi: ">= 0.1.30"` (gem-style), `needs:` (outside commands looked up on `PATH`, advisory; `docs/memory.md` "Bundles that need outside commands").
|
|
107
|
+
- Integrity: each file's sha256 is recorded at install; a plugin/rule file changed afterwards is not loaded (rules: every call denied) until reinstalled. It is an integrity check, not proof of authorship.
|
|
108
|
+
- Installing = trusting its Ruby code (the plugin), like a gem. Install only copies; the code runs at the next session start (`Engine.new`), so a running worker needs a restart to pick it up.
|
|
109
109
|
- A bundle without `*.md` (btw, mcp, loop-guard) adds no line to the prompt's memory index.
|
|
110
110
|
- Profiles are shipped bundles with only `includes:` (bundle names): `core` (loop-guard, check-in, guardrails, slow-commands; `chi bootstrap` installs it) and `dev` (known-names, mcp, btw, skills, source-links, github-pr, coordinator, claude-peer, codex-peer). `chi bundle install core` installs the ones not installed and records them; a bundle the user uninstalled stays out on a later install, upgrade or `chi update`, which install only bundles new to the profile. `chi bundle uninstall core` removes its recorded bundles, then core (`docs/memory.md` "Bundle profiles").
|
|
111
111
|
|
|
@@ -114,12 +114,12 @@ Bundles are versioned directories/zips/tar.gz/git URLs with a `manifest.yml` and
|
|
|
114
114
|
| Command | Purpose |
|
|
115
115
|
|---------|---------|
|
|
116
116
|
| `install <source> [--scope system\|project] [--force]` | Install from dir/zip/tar.gz/git URL. `--force` overwrites existing entries, otherwise skips them; a skipped file stays the user's (not recorded, never upgraded or removed by the bundle). It also installs a bundle whose `requires_chi` this chi doesn't meet: without `--force` that install is refused (`NAME can't be installed: it requires chi …`). Writes provenance to `.bundles/<name>/` (base snapshots + `manifest.json`) and updates `index.md`. Warns on double-brace placeholders and checksum mismatches (strict mode). |
|
|
117
|
-
| `upgrade <source\|name> [--force] [--dry-run] [--agent]` | A bare `name` is an installed bundle: chi upgrades it from the source its record keeps (its git URL with the `#ref` it recorded, its folder, or the archive it came from) — a dev install is refused, and a record whose source is gone says so and asks for the path or URL. Otherwise 3-way merge upgrade (base vs current vs incoming) per `Merger::Upgrade#
|
|
117
|
+
| `upgrade <source\|name> [--force] [--dry-run] [--agent]` | A bare `name` is an installed bundle: chi upgrades it from the source its record keeps (its git URL with the `#ref` it recorded, its folder, or the archive it came from) — a dev install is refused, and a record whose source is gone says so and asks for the path or URL. Otherwise 3-way merge upgrade (base vs current vs incoming) per `Merger::Upgrade#decision` (the file's `FileState` and its `FileActions` row, then a text merge where both sides changed): `install` (new file), `noop` (current==incoming), `fast_forward` (current==base, not edited → auto-update), `keep` (incoming==base → preserve local edits), `merged` (both edited, a text merge with `git` comes out clean → the file takes it, the old one goes to the bundle trash, and entries both sides added under a `Changelog` heading are kept), `conflict` (both edited, a real conflict, or no `git` or base → the file stays untouched and marked, reported as `X stays behind 0.8.0: …` with why and what it missed, needs a retry, `--force`, which copies it to the trash first, or interactive `memory_write` resolution). A marked file is retried at the same version: the system bundle's at each chi start and on `chi update`, another shipped bundle's on `chi update`, any other bundle's on `chi bundle upgrade`. A file marked by an older chi merges normally when its recorded base is a version chi shipped (an installed samagotchi gem still has it); otherwise it merges only if the bundle takes out or rewords no line it still holds, and if it does, it stays behind until resolved by hand, with `--agent` or with `--force`, and the retry can't fix that. Pruned files (removed from new bundle) are kept if locally edited, otherwise warned. |
|
|
118
118
|
| `uninstall <bundle> [--force]` | Moves the bundle's memory files to `.bundles/.trash/<bundle>-<time>/` (refuses locally edited ones unless `--force`; keeps a file another installed bundle also lists), removes their `index.md` lines, deletes the provenance dir. |
|
|
119
119
|
| `status [<bundle>]` | Provenance + per-file `ok|modified|missing|unreadable|no-index` vs stored checksum and base snapshot, and what stays behind or merged at a chi start; `issues=` counts files behind, missing or unreadable, not edits; a model overlay (`<name>.<key>.md`) has no index line and reads `ok (model overlay)`. |
|
|
120
120
|
| `diff <bundle> [file]` | Prints `base` (provenance snapshot) vs `current` (on-disk) for each file. |
|
|
121
121
|
| `list` | Lists installed bundles (`name v<version> scope files installed_at`, plus the shipped version when newer) and the bundles shipped with chi that are not installed (`install <name>` installs one), grouped under their profile; an installed profile shows `includes=` and `left out=`. |
|
|
122
|
-
| `build [--scope system\|project] [--name NAME] [--version VER] [--description DESC] [--out PATH] [FILES...]` | **Inverse of install** — builds a shareable bundle from local memories and installed
|
|
122
|
+
| `build [--scope system\|project] [--name NAME] [--version VER] [--description DESC] [--out PATH] [FILES...]` | **Inverse of install** — builds a shareable bundle from local memories and an installed bundle's plugin, rules and scripts. Infers `zip` vs `dir`/`tar.gz` from `--out` extension; default `chi_system_memories.zip` (system) or `chi_<repo>_memories.zip` (project, named after the project root) v`1.0.0` in `Dir.pwd`. `FILES...` is an optional allowlist of memory basenames (`identity` or `identity.md`); if omitted, all `*.md` except `index.md`/hidden/non-md and the memories other installed bundles own (each named in a `Left out` line) are included. Computes `sha256:` checksums and writes `manifest.yml` via `Manifest.write`. No provenance write. |
|
|
123
123
|
|
|
124
124
|
**Scope resolution for install/build:**
|
|
125
125
|
- CLI `--scope` wins over `manifest.yml` `scope`. Default is `system` if none given (`Installer#run`). For `project`, target is the project folder `<system dir>/projects/<basename>_<hash>` of the project root (`MemoryPaths.scope_dir`).
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
normal session: it shows in `chi sessions list` with `↳ <parent>`, and the user can steer it
|
|
52
52
|
with `chi --attach <id>`. While I wait for a child, its tool approvals go to my user as my own
|
|
53
53
|
approvals (I see only how each was answered); see `docs/sessions.md` "Delegating".
|
|
54
|
-
- Bundles extend me: memories, `
|
|
54
|
+
- Bundles extend me: memories, `guardrails/*.yml`, and a `plugin.rb` that adds slash
|
|
55
55
|
commands (`anytime:` ones run mid-turn), model tools (they can return images I see), hooks,
|
|
56
56
|
cards, side answers (`ask_model`), System 1 judgments (`chi.judgment`, `ctx.judge`), HTTP calls (`ctx.http`),
|
|
57
57
|
child sessions and services (e.g. MCP server processes). Shipped: `btw` (`/btw` side question),
|
|
@@ -60,23 +60,23 @@ module Samagotchi
|
|
|
60
60
|
move = []
|
|
61
61
|
refuse = []
|
|
62
62
|
SessionManager.children_of(prev_id, state_dir: state_dir).each do |row|
|
|
63
|
-
subtree = [row
|
|
63
|
+
subtree = [row.id, *SessionManager.descendant_ids(row.id, state_dir)]
|
|
64
64
|
next if (subtree & open_ids).empty?
|
|
65
65
|
|
|
66
|
-
if row
|
|
66
|
+
if row.archived
|
|
67
67
|
# An open delegate below an archived one: the archive's cascade
|
|
68
68
|
# would take it, the move doesn't (only unarchived ones move).
|
|
69
69
|
refuse.concat(open.select { |c| subtree.include?(c.id) }.map do |c|
|
|
70
|
-
c.with(why: "under archived #{row
|
|
70
|
+
c.with(why: "under archived #{row.short_id}; unarchive that one or archive this one")
|
|
71
71
|
end)
|
|
72
72
|
next
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
why = unmovable(row
|
|
75
|
+
why = unmovable(row.id, state_dir: state_dir)
|
|
76
76
|
if why
|
|
77
|
-
refuse << SessionContinue::OpenChild.new(id: row
|
|
77
|
+
refuse << SessionContinue::OpenChild.new(id: row.id, short_id: row.short_id, why: why)
|
|
78
78
|
else
|
|
79
|
-
move << row
|
|
79
|
+
move << row.id
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
Plan.new(move: move, refuse: refuse)
|
|
@@ -335,8 +335,8 @@ module Samagotchi
|
|
|
335
335
|
before = kept ? kept.after : current
|
|
336
336
|
# A follow-up's turn (DelegateCursor#turn) is reported by its own
|
|
337
337
|
# reply, as delegate_result takes it, not a newer queued turn's.
|
|
338
|
-
wait = ReplyWait.call(child_id, state_dir: @state_dir,
|
|
339
|
-
timeout: 0
|
|
338
|
+
wait = ReplyWait.call(child_id, state_dir: @state_dir, **ReplyWait::Target.from_cursor(before, child: child).to_h,
|
|
339
|
+
timeout: 0)
|
|
340
340
|
return nil unless reportable?(wait)
|
|
341
341
|
|
|
342
342
|
claim_id = before.report_claim&.id || SecureRandom.hex(16)
|
|
@@ -5,7 +5,8 @@ require_relative "session"
|
|
|
5
5
|
require_relative "archive_store"
|
|
6
6
|
require_relative "git_head"
|
|
7
7
|
require_relative "reply_wait"
|
|
8
|
-
require_relative "
|
|
8
|
+
require_relative "owner_lock"
|
|
9
|
+
require_relative "session_row"
|
|
9
10
|
require_relative "tools/delegate"
|
|
10
11
|
require_relative "tools/delegate_cursor"
|
|
11
12
|
|
|
@@ -50,44 +51,47 @@ module Samagotchi
|
|
|
50
51
|
def of(parent_id, state_dir:, include_archived: false)
|
|
51
52
|
cursors = Tools::DelegateCursors.read(parent_id, state_dir: state_dir)
|
|
52
53
|
SessionManager.children_of(parent_id, state_dir: state_dir).filter_map do |row|
|
|
53
|
-
next if row
|
|
54
|
+
next if row.archived && !include_archived
|
|
54
55
|
|
|
55
|
-
session = load(row
|
|
56
|
+
session = load(row.id, state_dir: state_dir)
|
|
56
57
|
session && child(session, row, cursors, state_dir: state_dir)
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
# The cheap path for the glance views: only the children in the
|
|
61
|
-
# parent's delegates.json (one Session.load and
|
|
62
|
+
# parent's delegates.json (one Session.load and a SessionRow each, no
|
|
62
63
|
# scan). Its keys can also name a fork the model passed to delegate
|
|
63
64
|
# session: or a deleted child: only live delegates of this parent count,
|
|
64
65
|
# archived ones don't.
|
|
65
66
|
# @return [Counts]
|
|
66
67
|
def counts(parent_id, state_dir:)
|
|
67
68
|
cursors = Tools::DelegateCursors.read(parent_id, state_dir: state_dir)
|
|
69
|
+
budgets = SessionRow.budgets
|
|
68
70
|
children = cursors.keys.filter_map do |id|
|
|
69
71
|
session = load(id, state_dir: state_dir)
|
|
70
72
|
next unless session&.delegate? && session.parent_id == parent_id
|
|
71
73
|
next if ArchiveStore.archived?(Session.session_dir(id, state_dir: state_dir))
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
dir = Session.session_dir(id, state_dir: state_dir)
|
|
76
|
+
child(session, SessionRow.build(session, session_dir: dir, owner: OwnerLock.owner(dir), budgets: budgets), cursors,
|
|
77
|
+
state_dir: state_dir)
|
|
74
78
|
end
|
|
75
79
|
Counts.new(running: children.count { |c| c.state == "running" },
|
|
76
80
|
waiting: children.count { |c| c.state == "waiting" },
|
|
77
81
|
unreported: children.count { |c| c.last_reply && !c.reported })
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
# @param row [
|
|
84
|
+
# @param row [SessionRow] the child's row
|
|
81
85
|
# @param cursors [Hash] the parent's delegates.json
|
|
82
86
|
def child(session, row, cursors, state_dir:)
|
|
83
87
|
reply = ReplyWait.newest_reply(session.id, state_dir: state_dir)
|
|
84
88
|
cursor = Tools::DelegateCursors.get_from(cursors, session.id)
|
|
85
|
-
Child.new(id: session.id, short_id: session.id[0, 8], title: (session.title || row
|
|
86
|
-
state: state_of(session, row, reply), waiting: row
|
|
89
|
+
Child.new(id: session.id, short_id: session.id[0, 8], title: (session.title || row.preview).to_s,
|
|
90
|
+
state: state_of(session, row, reply), waiting: row.waiting, live: !!row.live,
|
|
87
91
|
delegate: session.delegate?, cwd: session.working_directory, branch: GitHead.branch(session.working_directory),
|
|
88
92
|
last_reply: reply && first_line(session.id, reply, state_dir: state_dir),
|
|
89
93
|
last_reply_at: reply && reply_time(reply), reported: !reply.nil? && reported?(session, cursor, reply),
|
|
90
|
-
updated_at: session.updated_at, archived: !!row
|
|
94
|
+
updated_at: session.updated_at, archived: !!row.archived)
|
|
91
95
|
end
|
|
92
96
|
private_class_method :child
|
|
93
97
|
|
|
@@ -97,8 +101,8 @@ module Samagotchi
|
|
|
97
101
|
private_class_method :reported?
|
|
98
102
|
|
|
99
103
|
def state_of(session, row, reply)
|
|
100
|
-
return "waiting" if row
|
|
101
|
-
return "running" if
|
|
104
|
+
return "waiting" if row.waiting
|
|
105
|
+
return "running" if row.running_or_starting?
|
|
102
106
|
return "stopped" if session.status == Session::STATUS_STOPPED
|
|
103
107
|
|
|
104
108
|
outcome = session.last_turn&.outcome
|
|
@@ -109,16 +113,6 @@ module Samagotchi
|
|
|
109
113
|
end
|
|
110
114
|
private_class_method :state_of
|
|
111
115
|
|
|
112
|
-
# The keys of a children_of row that state_of and child read, for a
|
|
113
|
-
# session counts loaded itself.
|
|
114
|
-
def summary_of(session, state_dir:)
|
|
115
|
-
live = WorkerLink.resolve(Session.session_dir(session.id, state_dir: state_dir), unlink: false).worker_owned?
|
|
116
|
-
{ live: live, busy: live && session.status == Session::STATUS_RUNNING, saved_status: session.status,
|
|
117
|
-
updated_at: session.updated_at, waiting: session.waiting_question(live: live)&.dig(:kind),
|
|
118
|
-
preview: session.last_prompt.to_s }
|
|
119
|
-
end
|
|
120
|
-
private_class_method :summary_of
|
|
121
|
-
|
|
122
116
|
def first_line(id, file, state_dir:)
|
|
123
117
|
text = File.read(File.join(ReplyWait.reply_dir(id, state_dir: state_dir), file), 4096)
|
|
124
118
|
text.each_line.map(&:strip).find { |line| !line.empty? }.to_s
|
|
@@ -17,9 +17,6 @@ module Samagotchi
|
|
|
17
17
|
# override the constants (specs stub them on the class).
|
|
18
18
|
module ParentWait
|
|
19
19
|
POLL_INTERVAL = ReplyWait::POLL_INTERVAL
|
|
20
|
-
# No live worker this long while waiting: it died before it could mark
|
|
21
|
-
# the session (a worker takes well under a second to start).
|
|
22
|
-
WORKER_GONE_AFTER = 5
|
|
23
20
|
FORMATS = %w[text json].freeze
|
|
24
21
|
|
|
25
22
|
# @return [Integer] the exit status
|
|
@@ -59,26 +56,15 @@ module Samagotchi
|
|
|
59
56
|
nil
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
# Wait for
|
|
63
|
-
#
|
|
64
|
-
# @param
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# (ReplyWait's turn:): a queued message's, or the turn an answer or
|
|
68
|
-
# a --wait with no message is about (ReplyWait.own_turn)
|
|
69
|
-
# @param queued [Boolean] +turn+ is a queued message's (ReplyWait's
|
|
70
|
-
# queued:)
|
|
71
|
-
# @param ended_before [Boolean] a last_turn that ended before the
|
|
72
|
-
# baseline is the answer (ReplyWait's ended_before:) — the
|
|
73
|
-
# --wait-with-no-message's settled session
|
|
74
|
-
# @param report_turn_id [String, nil] the turn id to include in the
|
|
75
|
-
# report, even when ReplyWait uses the next-reply path
|
|
59
|
+
# Wait for what +target+ binds (ReplyWait) and report it. Ctrl-C
|
|
60
|
+
# leaves the turn running (130).
|
|
61
|
+
# @param target [ReplyWait::Target] which turn, from the cursor and
|
|
62
|
+
# baseline prepare_wait read; its report_turn_id is the turn id the
|
|
63
|
+
# report names, even when ReplyWait takes the next reply
|
|
76
64
|
# @return [Integer] the exit status
|
|
77
|
-
def wait_for_reply(id,
|
|
78
|
-
|
|
79
|
-
result = @access.wait_reply(id,
|
|
80
|
-
poll_interval: self.class::POLL_INTERVAL, turn: turn, queued: queued,
|
|
81
|
-
ended_before: ended_before)
|
|
65
|
+
def wait_for_reply(id, target:, timeout:)
|
|
66
|
+
report_turn_id = target.report_turn_id
|
|
67
|
+
result = @access.wait_reply(id, **target.to_h, timeout: timeout, poll_interval: self.class::POLL_INTERVAL)
|
|
82
68
|
result.turn_id = report_turn_id if report_turn_id && !report_turn_id.to_s.empty?
|
|
83
69
|
result.text = result.text&.dup&.force_encoding(Encoding::UTF_8)&.scrub
|
|
84
70
|
@reported = true
|
data/lib/samagotchi/config.rb
CHANGED
|
@@ -373,6 +373,9 @@ module Samagotchi
|
|
|
373
373
|
# The stage view's working set (files, memories and URLs the turn brought into context): a rail in the stage, a
|
|
374
374
|
# footprint under each finished turn and a "set N" chip in place of the mem chip; ?working_set=1|0 overrides it per page load.
|
|
375
375
|
Entry.new(key: "web.working_set", yaml_path: %w[web working_set], type: :bool, default: true, expose: %i[env config cli], lifecycle: :web_start),
|
|
376
|
+
# Opt-in: the page checks each finished turn against a reload of it (one full read per turn end) and chi web logs
|
|
377
|
+
# reload_checked, with reload_mismatch and the kinds that differed (counts only, no content).
|
|
378
|
+
Entry.new(key: "web.reload_check", yaml_path: %w[web reload_check], type: :bool, default: false, expose: %i[env config cli], lifecycle: :web_start),
|
|
376
379
|
# Quick replies next to Annotate in the page's selection bubble, "|"-separated (a YAML list works too); "" leaves only Annotate.
|
|
377
380
|
Entry.new(key: "web.annotate_presets", yaml_path: %w[web annotate_presets], type: :string, default: "Agreed|Could you please elaborate?", expose: %i[env config cli], lifecycle: :web_start),
|
|
378
381
|
# What a file ref (a tool row's file, a diff line) opens on this machine: a preset (vscode, vscode-insiders, cursor, zed, none) or a URL template with {path} and an optional {line} (Web::Editor).
|
|
@@ -409,17 +409,19 @@ module Samagotchi
|
|
|
409
409
|
end
|
|
410
410
|
|
|
411
411
|
# A project's sources: local only (SessionAccess::Contract), this
|
|
412
|
-
# machine's checkout.
|
|
412
|
+
# machine's checkout or marked folder.
|
|
413
413
|
def project_target
|
|
414
|
-
|
|
415
|
-
|
|
414
|
+
found = MemoryPaths.lookup(@cwd)
|
|
415
|
+
unless found.project?
|
|
416
|
+
error_line("#{command_name}: --project needs a project (a git repository, or a folder marked with " \
|
|
417
|
+
"`chi project init`), and #{@cwd} is in none")
|
|
416
418
|
return nil
|
|
417
419
|
end
|
|
418
420
|
|
|
419
|
-
root =
|
|
421
|
+
root = found.root
|
|
420
422
|
# Its sources run in the root, or in this checkout when the root is a
|
|
421
423
|
# git dir (ContextSources.project_cwd).
|
|
422
|
-
Target.new(session_id: nil, project_root: root, cwd:
|
|
424
|
+
Target.new(session_id: nil, project_root: root, cwd: found.top || @cwd,
|
|
423
425
|
label: "project #{File.basename(root)}")
|
|
424
426
|
end
|
|
425
427
|
|