samagotchi 0.151.0 → 0.152.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/docs/bundle-authoring.md +4 -4
  4. data/docs/cli.md +32 -7
  5. data/docs/guardrails.md +4 -0
  6. data/docs/internals/block-renderers.md +3 -1
  7. data/docs/internals/bundles.md +48 -14
  8. data/docs/internals/objectives.md +6 -4
  9. data/docs/memory.md +6 -1
  10. data/docs/plugins.md +51 -14
  11. data/docs/releasing.md +7 -2
  12. data/docs/sessions.md +1 -1
  13. data/lib/samagotchi/block_renderers.rb +9 -8
  14. data/lib/samagotchi/bundle_command/check.rb +43 -3
  15. data/lib/samagotchi/bundle_command/installed_check.rb +6 -2
  16. data/lib/samagotchi/bundle_command/try/sandbox.rb +11 -3
  17. data/lib/samagotchi/bundle_command.rb +88 -10
  18. data/lib/samagotchi/bundle_needs.rb +4 -6
  19. data/lib/samagotchi/bundle_settings_check.rb +14 -13
  20. data/lib/samagotchi/bundles/agents-md/manifest.yml +2 -2
  21. data/lib/samagotchi/bundles/btw/manifest.yml +2 -2
  22. data/lib/samagotchi/bundles/loop-guard/manifest.yml +2 -2
  23. data/lib/samagotchi/bundles/mcp/manifest.yml +2 -2
  24. data/lib/samagotchi/bundles/skills/manifest.yml +2 -2
  25. data/lib/samagotchi/bundles/slow-commands/manifest.yml +2 -2
  26. data/lib/samagotchi/bundles/system/manifest.yml +2 -2
  27. data/lib/samagotchi/bundles/system/memory_guide.md +3 -3
  28. data/lib/samagotchi/child_env.rb +15 -0
  29. data/lib/samagotchi/context_providers.rb +42 -10
  30. data/lib/samagotchi/engine.rb +27 -4
  31. data/lib/samagotchi/extension_load.rb +4 -3
  32. data/lib/samagotchi/guardrail_wiring.rb +28 -19
  33. data/lib/samagotchi/guardrails/approval.rb +2 -2
  34. data/lib/samagotchi/guardrails/rules.rb +1 -0
  35. data/lib/samagotchi/guardrails/verdict.rb +20 -0
  36. data/lib/samagotchi/hooks/bundle_loader.rb +5 -7
  37. data/lib/samagotchi/memory_bundle/asset_installer.rb +11 -8
  38. data/lib/samagotchi/memory_bundle/builder.rb +36 -30
  39. data/lib/samagotchi/memory_bundle/bundle_hook.rb +3 -2
  40. data/lib/samagotchi/memory_bundle/file_digest.rb +31 -0
  41. data/lib/samagotchi/memory_bundle/file_standing.rb +2 -2
  42. data/lib/samagotchi/memory_bundle/installed_bundle.rb +55 -46
  43. data/lib/samagotchi/memory_bundle/installed_copy.rb +38 -15
  44. data/lib/samagotchi/memory_bundle/installed_manifest.rb +224 -0
  45. data/lib/samagotchi/memory_bundle/installer.rb +90 -16
  46. data/lib/samagotchi/memory_bundle/manifest.rb +146 -6
  47. data/lib/samagotchi/memory_bundle/manifest_standing.rb +171 -0
  48. data/lib/samagotchi/memory_bundle/merger.rb +7 -8
  49. data/lib/samagotchi/memory_bundle/profile.rb +3 -2
  50. data/lib/samagotchi/memory_bundle/provenance.rb +217 -18
  51. data/lib/samagotchi/memory_bundle/shas.rb +4 -3
  52. data/lib/samagotchi/memory_bundle/shipped_update.rb +52 -24
  53. data/lib/samagotchi/memory_bundle/status.rb +11 -10
  54. data/lib/samagotchi/memory_bundle/system_bundle.rb +13 -2
  55. data/lib/samagotchi/plugin/loader.rb +1 -1
  56. data/lib/samagotchi/session_config.rb +5 -4
  57. data/lib/samagotchi/system_one/record.rb +10 -2
  58. data/lib/samagotchi/tools/delegate.rb +20 -2
  59. data/lib/samagotchi/update_command.rb +13 -2
  60. data/lib/samagotchi/version.rb +1 -1
  61. data/lib/samagotchi/web/app.rb +2 -1
  62. data/lib/samagotchi/web/session_access_routes.rb +2 -1
  63. data/lib/samagotchi/web/session_hub.rb +6 -0
  64. metadata +4 -1
@@ -159,7 +159,8 @@ module Samagotchi
159
159
  # repository (a worktree the parent meant to pass as cwd:), which makes
160
160
  # every write there an ask for the user (child-boundary). One warning
161
161
  # line, however many are named; nil when the task names none, or names
162
- # only the checkout holding the child's folder (or a subfolder of it).
162
+ # only the checkout holding the child's folder (or a subfolder of it),
163
+ # or main while the child itself runs in a worktree of it.
163
164
  # Never raises: a started child must not come back as an Error:.
164
165
  # @return [String, nil]
165
166
  def self.other_checkout(task, folder, parent:, child_id:, no_cwd:)
@@ -180,6 +181,9 @@ module Samagotchi
180
181
  # not the one holding +folder+: the first path token in the task that
181
182
  # belongs to one (the deepest containing it), else nil. Reads files
182
183
  # only, no git process, so a spec stubbing Process.spawn is unaffected.
184
+ # A child started in a linked worktree is never pointed at main: its
185
+ # work belongs in the worktree, so naming a main-checkout file - mostly
186
+ # to read it - is not a cwd: the parent forgot.
183
187
  # @return [String, nil] an absolute checkout folder
184
188
  def self.foreign_checkout(task, folder)
185
189
  common = MemoryPaths.common_git_dir(folder)
@@ -187,17 +191,31 @@ module Samagotchi
187
191
 
188
192
  checkouts = Guardrails::GitInfo.checkouts(common).map { |checkout| File.realpath(checkout) }
189
193
  own = deepest_checkout(File.realpath(folder), checkouts)
194
+ main = main_checkout(common)
190
195
  task.to_s.scan(PATH_TOKEN) do |token|
191
196
  path = existing_ancestor(File.expand_path(token.sub(/[.:!?]+\z/, ""), folder))
192
197
  next if path.nil?
193
198
 
194
199
  found = deepest_checkout(path, checkouts)
195
- return found if found && found != own
200
+ next if found.nil? || found == own || found == main
201
+
202
+ return found
196
203
  end
197
204
  nil
198
205
  end
199
206
  private_class_method :foreign_checkout
200
207
 
208
+ # The main checkout of a common git dir (the folder holding it), nil for
209
+ # a bare or separate-git-dir layout, which has none.
210
+ def self.main_checkout(common)
211
+ return nil unless File.basename(common) == ".git"
212
+
213
+ File.realpath(File.dirname(common))
214
+ rescue StandardError
215
+ nil
216
+ end
217
+ private_class_method :main_checkout
218
+
201
219
  # The checkout containing +path+, matched by whole components (so
202
220
  # /x/app is not found in /x/app-fix): the deepest, so a nested worktree's
203
221
  # path belongs to that worktree, not to main; nil when none does.
@@ -10,6 +10,7 @@ require_relative "gem_update"
10
10
  require_relative "installed_gem"
11
11
  require_relative "live_versions"
12
12
  require_relative "memory_bundle/file_standing"
13
+ require_relative "memory_bundle/manifest_standing"
13
14
  require_relative "memory_bundle/shipped_update"
14
15
  require_relative "memory_bundle/system_bundle"
15
16
  require_relative "session"
@@ -331,10 +332,20 @@ module Samagotchi
331
332
 
332
333
  require_relative "bundle_command/installed_check"
333
334
  bundles.flat_map do |bundle|
334
- BundleCommand::InstalledCheck.run(bundle).map { |found| Row.new(component: bundle.name, status: found.text, level: found.level) }
335
+ kept = MemoryBundle::ManifestStanding.keep(bundle, dry_run: @dry_run)
336
+ [manifest_row(kept)].compact +
337
+ BundleCommand::InstalledCheck.run(kept.bundle).map { |found| Row.new(component: bundle.name, status: found.text, level: found.level) }
335
338
  end
336
339
  end
337
340
 
341
+ # What an external bundle's manifest.yml came to (ManifestStanding.keep):
342
+ # kept (or would be) is a change, anything else to say a warning.
343
+ def manifest_row(kept)
344
+ return nil unless kept.line
345
+
346
+ Row.new(component: kept.bundle.name, status: kept.line, level: kept.kept ? nil : "warn")
347
+ end
348
+
338
349
  def bundle_row(update)
339
350
  row = Row.new(component: update.name, from: update.from, to: update.to)
340
351
  earlier = update.status == :failed ? [] : Array(update.earlier)
@@ -343,7 +354,7 @@ module Samagotchi
343
354
  when :would_update, :updated
344
355
  notes = [adds(update.adds), *notes, replaced(update.replaced), *update.kept_own].compact
345
356
  done(row, :update, notes.empty? ? nil : notes.join("; "))
346
- when :would_merge, :merged, :up_to_date then same_version(row, [*notes, update.note].compact, changed: Array(update.merged).any?)
357
+ when :would_merge, :merged, :up_to_date then same_version(row, [*notes, update.note, update.manifest].compact, changed: Array(update.merged).any?)
347
358
  when :skipped then skipped(row, [update.note, *notes].join("; "))
348
359
  else failed(row, [update.note, *notes].compact.join("; "))
349
360
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Samagotchi
4
- VERSION = "0.151.0"
4
+ VERSION = "0.152.0"
5
5
  # Sent as the User-Agent of every HTTP request chi makes.
6
6
  USER_AGENT = "chi/#{VERSION}"
7
7
  end
@@ -62,6 +62,7 @@ require_relative "editor"
62
62
  require_relative "../message_parts"
63
63
  require_relative "../launch_mode"
64
64
  require_relative "session_summary"
65
+ require_relative "session_hub"
65
66
  require_relative "project_list"
66
67
  require_relative "session_access_routes"
67
68
  require_relative "principal"
@@ -1025,7 +1026,7 @@ module Samagotchi
1025
1026
  def handle_info(req)
1026
1027
  return participant_info unless principal(req).owner?
1027
1028
 
1028
- chi_state = @hub&.chi_state || { version: Samagotchi::VERSION, installed: nil, available: nil, update_blocked: nil, auto: "off", restart: "idle", auto_offers: nil }
1029
+ chi_state = @hub&.chi_state || SessionHub::HUBLESS_STATE
1029
1030
  json_response(200, { app: "chi-web", **chi_state, pid: Process.pid,
1030
1031
  cwd: Dir.pwd, start_dir: start_dir, features: INFO_FEATURES, lan: @lan && @lan[:ip],
1031
1032
  proxy: @public_url && { identity_header: @public_url.identity_header } })
@@ -14,6 +14,7 @@ require_relative "../client_id"
14
14
  require_relative "../children_status"
15
15
  require_relative "../session_metrics"
16
16
  require_relative "../version"
17
+ require_relative "session_hub"
17
18
 
18
19
  module Samagotchi
19
20
  module Web
@@ -274,7 +275,7 @@ module Samagotchi
274
275
  model_name: session_access.show(id).model_name,
275
276
  default_model: ModelProfile.default_model_name,
276
277
  parent_strategy: strategy_json(session_access.parent_strategy(id)),
277
- **(@hub&.chi_state || { version: VERSION, installed: nil, available: nil, update_blocked: nil, auto: "off", restart: "idle", auto_offers: nil }) })
278
+ **(@hub&.chi_state || SessionHub::HUBLESS_STATE) })
278
279
  rescue SessionManager::OwnedByTUI => e
279
280
  owned_by_tui(e)
280
281
  rescue SessionAccess::Refused => e
@@ -32,6 +32,12 @@ module Samagotchi
32
32
  # step under it, so no stale upsert can follow a fresh snapshot.
33
33
  class SessionHub
34
34
  SCAN_INTERVAL = 1.0
35
+ # What an info route says about updates when there is no hub at all:
36
+ # this chi's version, and nothing known of the rest. The one place this
37
+ # is written (Web::App#handle_info and the remote info route).
38
+ HUBLESS_STATE = { version: Samagotchi::VERSION, installed: nil, available: nil, update_blocked: nil,
39
+ auto: "off", restart: "idle", auto_offers: nil }.freeze
40
+
35
41
  # The flock leaves no file trace when its holder dies, so owners are
36
42
  # probed: every tick for the sessions the projection believes owned
37
43
  # (a handful; OwnerLock.owner is open + LOCK_SH|LOCK_NB + close), and
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samagotchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.151.0
4
+ version: 0.152.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -472,15 +472,18 @@ files:
472
472
  - lib/samagotchi/memory_bundle/builder.rb
473
473
  - lib/samagotchi/memory_bundle/bundle_hook.rb
474
474
  - lib/samagotchi/memory_bundle/entry_size.rb
475
+ - lib/samagotchi/memory_bundle/file_digest.rb
475
476
  - lib/samagotchi/memory_bundle/file_standing.rb
476
477
  - lib/samagotchi/memory_bundle/index_size.rb
477
478
  - lib/samagotchi/memory_bundle/index_sync.rb
478
479
  - lib/samagotchi/memory_bundle/index_updater.rb
479
480
  - lib/samagotchi/memory_bundle/installed_bundle.rb
480
481
  - lib/samagotchi/memory_bundle/installed_copy.rb
482
+ - lib/samagotchi/memory_bundle/installed_manifest.rb
481
483
  - lib/samagotchi/memory_bundle/installer.rb
482
484
  - lib/samagotchi/memory_bundle/listing.rb
483
485
  - lib/samagotchi/memory_bundle/manifest.rb
486
+ - lib/samagotchi/memory_bundle/manifest_standing.rb
484
487
  - lib/samagotchi/memory_bundle/memory_files.rb
485
488
  - lib/samagotchi/memory_bundle/merger.rb
486
489
  - lib/samagotchi/memory_bundle/placeholder.rb