samagotchi 0.9.0 → 0.10.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -1
  3. data/docs/architecture.md +24 -9
  4. data/docs/cli.md +4 -3
  5. data/docs/guardrails.md +3 -2
  6. data/docs/hooks.md +44 -7
  7. data/docs/internals/gemma4-contract.md +1 -0
  8. data/docs/plugins.md +34 -12
  9. data/docs/sessions.md +1 -1
  10. data/lib/samagotchi/bootstrap_command.rb +29 -29
  11. data/lib/samagotchi/bridge/card_store.rb +4 -4
  12. data/lib/samagotchi/bridge/pending_card.rb +2 -2
  13. data/lib/samagotchi/bridge/sse_writer.rb +22 -28
  14. data/lib/samagotchi/bridge/turn_accumulator.rb +92 -5
  15. data/lib/samagotchi/bridge.rb +59 -112
  16. data/lib/samagotchi/bridge_client.rb +11 -28
  17. data/lib/samagotchi/bundle_command.rb +158 -212
  18. data/lib/samagotchi/bundles/check-in/manifest.yml +2 -2
  19. data/lib/samagotchi/bundles/check-in/plugin.rb +3 -4
  20. data/lib/samagotchi/bundles/known-names/hooks/known_names.rb +7 -3
  21. data/lib/samagotchi/bundles/known-names/manifest.yml +2 -2
  22. data/lib/samagotchi/bundles/loop-guard/manifest.yml +2 -2
  23. data/lib/samagotchi/bundles/loop-guard/plugin.rb +10 -10
  24. data/lib/samagotchi/bundles/mcp/manifest.yml +3 -3
  25. data/lib/samagotchi/bundles/mcp/plugin.rb +2 -4
  26. data/lib/samagotchi/bundles/skills/manifest.yml +2 -2
  27. data/lib/samagotchi/bundles/skills/plugin.rb +4 -6
  28. data/lib/samagotchi/bundles/source-links/hooks/source_links.rb +0 -1
  29. data/lib/samagotchi/bundles/source-links/manifest.yml +2 -2
  30. data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
  31. data/lib/samagotchi/bundles/system/manifest.yml +2 -2
  32. data/lib/samagotchi/cli/command.rb +51 -0
  33. data/lib/samagotchi/cli/flags.rb +116 -0
  34. data/lib/samagotchi/config.rb +70 -55
  35. data/lib/samagotchi/config_text_edit.rb +107 -0
  36. data/lib/samagotchi/desktop_command.rb +24 -18
  37. data/lib/samagotchi/edit_preview.rb +2 -2
  38. data/lib/samagotchi/engine.rb +20 -75
  39. data/lib/samagotchi/events.rb +23 -0
  40. data/lib/samagotchi/guardrail_wiring.rb +2 -5
  41. data/lib/samagotchi/hooks/bundle_loader.rb +13 -30
  42. data/lib/samagotchi/hooks/loader.rb +23 -33
  43. data/lib/samagotchi/hooks/registry.rb +41 -0
  44. data/lib/samagotchi/hooks.rb +0 -3
  45. data/lib/samagotchi/host_registry.rb +15 -0
  46. data/lib/samagotchi/idle_reminders.rb +0 -7
  47. data/lib/samagotchi/image_store.rb +22 -0
  48. data/lib/samagotchi/kernel_loop.rb +12 -16
  49. data/lib/samagotchi/live_versions.rb +2 -13
  50. data/lib/samagotchi/llm/native_tool_normalizer.rb +10 -227
  51. data/lib/samagotchi/memory_bundle/builder.rb +1 -14
  52. data/lib/samagotchi/memory_bundle/index_updater.rb +2 -28
  53. data/lib/samagotchi/memory_bundle/installer.rb +1 -27
  54. data/lib/samagotchi/memory_bundle/provenance.rb +2 -6
  55. data/lib/samagotchi/memory_bundle/status.rb +1 -14
  56. data/lib/samagotchi/memory_bundle/system_bundle.rb +2 -2
  57. data/lib/samagotchi/memory_bundle/uninstaller.rb +1 -24
  58. data/lib/samagotchi/memory_paths.rb +10 -0
  59. data/lib/samagotchi/note_command.rb +23 -37
  60. data/lib/samagotchi/owner_lock.rb +14 -3
  61. data/lib/samagotchi/plugin/api.rb +11 -17
  62. data/lib/samagotchi/plugin/context.rb +70 -15
  63. data/lib/samagotchi/plugin/loader.rb +2 -3
  64. data/lib/samagotchi/sampling_settings.rb +3 -5
  65. data/lib/samagotchi/self_report.rb +6 -9
  66. data/lib/samagotchi/send_command.rb +33 -52
  67. data/lib/samagotchi/session.rb +88 -92
  68. data/lib/samagotchi/session_archive_command.rb +8 -14
  69. data/lib/samagotchi/session_commands.rb +3 -21
  70. data/lib/samagotchi/session_delete_command.rb +15 -29
  71. data/lib/samagotchi/session_manager.rb +27 -13
  72. data/lib/samagotchi/terminal_ui/attached_loop.rb +30 -80
  73. data/lib/samagotchi/terminal_ui/attached_view.rb +1 -10
  74. data/lib/samagotchi/terminal_ui/between_turns.rb +133 -0
  75. data/lib/samagotchi/terminal_ui/event_renderer.rb +3 -1
  76. data/lib/samagotchi/terminal_ui/formatting.rb +51 -1
  77. data/lib/samagotchi/terminal_ui/status_row.rb +32 -0
  78. data/lib/samagotchi/terminal_ui/thinking_line.rb +3 -3
  79. data/lib/samagotchi/terminal_ui.rb +156 -234
  80. data/lib/samagotchi/thinking.rb +2 -3
  81. data/lib/samagotchi/thought_stream_splitter.rb +10 -9
  82. data/lib/samagotchi/tool_call_parser.rb +56 -292
  83. data/lib/samagotchi/tools/builtin_calls.rb +146 -0
  84. data/lib/samagotchi/tools/builtins.rb +2 -1
  85. data/lib/samagotchi/tools/edit.rb +21 -29
  86. data/lib/samagotchi/tools/memory.rb +1 -11
  87. data/lib/samagotchi/tools/send_note.rb +1 -1
  88. data/lib/samagotchi/update_command.rb +32 -22
  89. data/lib/samagotchi/version.rb +1 -1
  90. data/lib/samagotchi/vision_support.rb +4 -5
  91. data/lib/samagotchi/web/app.rb +89 -151
  92. data/lib/samagotchi/web/public/activity.js +1 -11
  93. data/lib/samagotchi/web/public/annotate_bar.js +120 -0
  94. data/lib/samagotchi/web/public/app.js +158 -785
  95. data/lib/samagotchi/web/public/chunk_router.js +7 -6
  96. data/lib/samagotchi/web/public/command_list.js +99 -0
  97. data/lib/samagotchi/web/public/data.js +22 -66
  98. data/lib/samagotchi/web/public/format.js +0 -23
  99. data/lib/samagotchi/web/public/index.html +0 -2
  100. data/lib/samagotchi/web/public/model_pick.js +0 -7
  101. data/lib/samagotchi/web/public/notifications.js +169 -0
  102. data/lib/samagotchi/web/public/question_cards.js +352 -0
  103. data/lib/samagotchi/web/public/session_state.js +34 -0
  104. data/lib/samagotchi/web/public/timing.js +1 -1
  105. data/lib/samagotchi/web/public/turn_events.js +11 -2
  106. data/lib/samagotchi/web/public/turn_model.js +0 -14
  107. data/lib/samagotchi/web/session_hub.rb +6 -4
  108. data/lib/samagotchi/web/session_summary.rb +5 -3
  109. data/lib/samagotchi/worker.rb +59 -53
  110. data/lib/samagotchi/worker_sidecar.rb +27 -1
  111. metadata +12 -1
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "fileutils"
4
4
 
5
+ require_relative "events"
5
6
  require_relative "session"
6
7
  require_relative "session_inbox"
7
8
  require_relative "turn_note"
@@ -43,7 +44,6 @@ module Samagotchi
43
44
  # How much of a command's output goes into its :command_ran.
44
45
  COMMAND_OUTPUT_LIMIT = 4096
45
46
  BUSY_OUTPUT = "busy: wait for the turn to end"
46
- TURN_END_EVENTS = %i[turn_completed turn_canceled turn_failed].freeze
47
47
  DEFAULT_MAX_ITERATIONS = 100
48
48
  NO_INTERRUPT_MAX_ITERATIONS = 1000
49
49
 
@@ -95,8 +95,10 @@ module Samagotchi
95
95
  # What a new session starts on (and /model resets to); nil before #run.
96
96
  attr_reader :default_model
97
97
 
98
- # @return [Symbol] :idle_exit, or :exit_requested when a client asked it
99
- # to exit (Bridge POST /exit)
98
+ # @return [Symbol] :idle_exit, :exit_requested when a client asked it
99
+ # to exit (Bridge POST /exit), :stopped when the session was stopped
100
+ # (chi stop), :crashed when the loop raised (the session is marked
101
+ # errored); SessionManager.run_session_loop turns it into the exit
100
102
  def run
101
103
  @session = Session.load(@session_id, state_dir: @state_dir)
102
104
  drop_dead_question
@@ -113,7 +115,7 @@ module Samagotchi
113
115
  # queued while that turn ran.
114
116
  @turn_end_seq = 0
115
117
  @engine.subscribe(observer: lambda { |event|
116
- @turn_end_seq = event[:event_seq] if TURN_END_EVENTS.include?(event[:type])
118
+ @turn_end_seq = event[:event_seq] if Events::TURN_END.include?(event[:type])
117
119
  })
118
120
  # /model's default is the config's, as in the REPL; the Engine started
119
121
  # on the session's model.
@@ -151,14 +153,17 @@ module Samagotchi
151
153
  begin
152
154
  # A session stopped before this worker took the lock (e.g. a stop
153
155
  # right after create) must not run its initial prompt.
154
- exit(0) if stopped_on_disk?
156
+ return :stopped if stopped_on_disk?
155
157
  # Plugins' slow setup (chi.init: an MCP server's first start), in
156
158
  # the background, shown by the UIs; a turn waits only for the ones
157
159
  # that bring tools.
158
160
  @engine.start_init_tasks!
159
161
  loop do
160
- # Check if the session was externally marked as stopped
161
- exit(0) if Session.load(@session_id, state_dir: @state_dir).status == Session::STATUS_STOPPED
162
+ # Check if the session was externally marked as stopped. Not
163
+ # stopped_on_disk?, which reads a vanished file as "not stopped":
164
+ # a session file deleted under a running worker crashes it here
165
+ # instead of being saved back by its next turn.
166
+ return :stopped if Session.load(@session_id, state_dir: @state_dir).status == Session::STATUS_STOPPED
162
167
 
163
168
  # Commands queued before a prompt run first (a /continue sent
164
169
  # before a new prompt still answers the offer).
@@ -194,7 +199,7 @@ module Samagotchi
194
199
  # Its stderr is /dev/null: the log is the only trace of why.
195
200
  Log.exception(:worker, "crashed", e)
196
201
  Session.mark_error(@session_id, reason: e.message, state_dir: @state_dir)
197
- exit(1)
202
+ :crashed
198
203
  ensure
199
204
  # The anytime commands finish and the plugins' services stop (a
200
205
  # server process), whatever the way out; the Bridge last, so a
@@ -297,28 +302,19 @@ module Samagotchi
297
302
  # its continue turn
298
303
  # @param images [Array<Hash>] the prompt's image refs ({file:, name:})
299
304
  def run_prompt(prompt, origin, no_interrupt: false, images: [])
300
- # Show the turn as running to readers of the file (the web's session
301
- # list); the Engine resets it to idle when it ends.
302
- @session.status = Session::STATUS_RUNNING
303
- @session.save(state_dir: @state_dir)
304
305
  user_input(origin&.dig(:client_id))
305
306
  drop_continue_offer(origin)
306
307
  @turn_flow.before_prompt_turn
307
308
  @merged_this_turn = []
308
- begin
309
- result = @engine.run_turn(@session, prompt, pending_input: pending_input_drain, origin: origin,
310
- max_iterations: max_iterations(no_interrupt), images: images)
311
- rescue StandardError => e
312
- # The Engine announced :turn_failed (with the error's one line).
313
- restore_failed_turn([[prompt, origin, images], *@merged_this_turn], error: e)
314
- return
315
- ensure
316
- refuse_queued_commands
309
+ run_engine_turn(prompt, origin: origin, max_iterations: max_iterations(no_interrupt),
310
+ images: images) do |result, error|
311
+ if error
312
+ # The Engine announced :turn_failed (with the error's one line).
313
+ restore_failed_turn([[prompt, origin, images], *@merged_this_turn], error: error)
314
+ else
315
+ after_turn(result, no_interrupt: no_interrupt)
316
+ end
317
317
  end
318
- after_turn(result, no_interrupt: no_interrupt)
319
- response = result.respond_to?(:output) ? result.output : nil
320
- SessionInbox.write_output(@session_dir, response) unless response.nil? || response.strip.empty?
321
- @session.save(state_dir: @state_dir) unless stopped_on_disk?
322
318
  end
323
319
 
324
320
  # A due reminder runs as a continue turn, as in the REPL: Engine#run_turn
@@ -333,23 +329,11 @@ module Samagotchi
333
329
  return false unless @engine.reminders_due?
334
330
 
335
331
  drop_continue_offer({ client_id: SessionManager::REMINDER_CLIENT_ID })
336
- @session.status = Session::STATUS_RUNNING
337
- @session.save(state_dir: @state_dir)
338
- begin
339
- result = @engine.run_turn(@session, nil, continue: true, pending_input: pending_input_drain,
340
- origin: { client_id: SessionManager::REMINDER_CLIENT_ID },
341
- max_iterations: DEFAULT_MAX_ITERATIONS)
342
- response = result.respond_to?(:output) ? result.output : nil
343
- SessionInbox.write_output(@session_dir, response) unless response.nil? || response.strip.empty?
344
- rescue StandardError
345
- nil # the Engine announced :turn_failed; there is no prompt to hand back
346
- ensure
347
- refuse_queued_commands
348
- end
349
- # The offer went before the turn (drop_continue_offer); the rollback
350
- # window closes.
351
- @turn_flow.after_reminder_turn
352
- @session.save(state_dir: @state_dir) unless stopped_on_disk?
332
+ # A failure has no prompt to hand back (the Engine announced
333
+ # :turn_failed). The offer went before the turn
334
+ # (drop_continue_offer); either way the rollback window closes.
335
+ run_engine_turn(nil, continue: true, origin: { client_id: SessionManager::REMINDER_CLIENT_ID },
336
+ max_iterations: DEFAULT_MAX_ITERATIONS) { @turn_flow.after_reminder_turn }
353
337
  true
354
338
  end
355
339
 
@@ -397,7 +381,7 @@ module Samagotchi
397
381
  announce_command(command, status: result.status.to_s, output: result.output, changed: Array(result.changed))
398
382
  shown.each { |event| @engine.announce(event) }
399
383
  end
400
- @session.save(state_dir: @state_dir) unless Array(result.changed).empty? || stopped_on_disk?
384
+ save_session unless Array(result.changed).empty?
401
385
  user_input(command[:client_id]) if resolved
402
386
  # An offer answered without a turn ("no") is activity: the recap
403
387
  # written at the offer (it says the turn stopped) gets rewritten.
@@ -461,23 +445,38 @@ module Samagotchi
461
445
  def run_continue_turn(command)
462
446
  offer = @turn_flow.offer
463
447
  @turn_flow.before_continue_turn
448
+ run_engine_turn(nil, continue: true, origin: { client_id: command[:client_id] }.compact,
449
+ max_iterations: max_iterations(offer[:no_interrupt])) do |result, error|
450
+ if error
451
+ @engine.announce(type: :continue_offered, context: offer[:context], no_interrupt: offer[:no_interrupt])
452
+ else
453
+ after_turn(result, continue: true, no_interrupt: offer[:no_interrupt])
454
+ end
455
+ end
456
+ end
457
+
458
+ # One Engine turn, the same for a prompt, a reminder and a continue:
459
+ # the session shows as running to readers of the file (the web's
460
+ # session list; the Engine resets it to idle when it ends), commands
461
+ # queued while it ran are refused, then the caller's block takes the
462
+ # result (or the error the Engine announced as :turn_failed), the
463
+ # answer goes to the output file and the session is saved.
464
+ # @yieldparam result [Object, nil] Engine#run_turn's, nil on a failure
465
+ # @yieldparam error [StandardError, nil]
466
+ def run_engine_turn(prompt, **turn_args)
464
467
  @session.status = Session::STATUS_RUNNING
465
468
  @session.save(state_dir: @state_dir)
466
469
  begin
467
- result = @engine.run_turn(@session, nil, continue: true, pending_input: pending_input_drain,
468
- origin: { client_id: command[:client_id] }.compact,
469
- max_iterations: max_iterations(offer[:no_interrupt]))
470
- rescue StandardError
471
- @engine.announce(type: :continue_offered, context: offer[:context], no_interrupt: offer[:no_interrupt])
472
- @session.save(state_dir: @state_dir) unless stopped_on_disk?
473
- return
470
+ result = @engine.run_turn(@session, prompt, pending_input: pending_input_drain, **turn_args)
471
+ rescue StandardError => e
472
+ error = e
474
473
  ensure
475
474
  refuse_queued_commands
476
475
  end
477
- after_turn(result, continue: true, no_interrupt: offer[:no_interrupt])
476
+ yield result, error
478
477
  response = result.respond_to?(:output) ? result.output : nil
479
478
  SessionInbox.write_output(@session_dir, response) unless response.nil? || response.strip.empty?
480
- @session.save(state_dir: @state_dir) unless stopped_on_disk?
479
+ save_session
481
480
  end
482
481
 
483
482
  # TurnFlow keeps the checkpoint (a cancelled turn's, for !rollback) and
@@ -513,6 +512,7 @@ module Samagotchi
513
512
  # origin, images] (a web client gets its image chips back)
514
513
  # @param error [Exception, nil] what failed: its one line stays in the
515
514
  # conversation as a turn note
515
+ # Not saved here: run_engine_turn saves after its block.
516
516
  def restore_failed_turn(prompts, error: nil)
517
517
  note = error && TurnNote.failed(error.respond_to?(:summary) ? error.summary : error.message, restored: true)
518
518
  @engine.synchronize_events do
@@ -523,7 +523,6 @@ module Samagotchi
523
523
  @engine.announce(restored)
524
524
  end
525
525
  end
526
- @session.save(state_dir: @state_dir) unless stopped_on_disk?
527
526
  end
528
527
 
529
528
  # Shared mid-turn steering drain: claims any input files that arrive
@@ -657,5 +656,12 @@ module Samagotchi
657
656
  def stopped_on_disk?
658
657
  SessionManager.stopped_on_disk?(@session_id, state_dir: @state_dir)
659
658
  end
659
+
660
+ # Save the session unless it was stopped meanwhile: the stop (chi stop,
661
+ # from another process) saved its status, and this save would write
662
+ # the worker's over it.
663
+ def save_session
664
+ @session.save(state_dir: @state_dir) unless stopped_on_disk?
665
+ end
660
666
  end
661
667
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "json"
4
4
  require "fileutils"
5
+ require "socket"
5
6
  require_relative "atomic_file"
6
7
 
7
8
  module Samagotchi
@@ -10,9 +11,11 @@ module Samagotchi
10
11
  # "worker" owner), the chi it runs (chi update) and the input-file format
11
12
  # it reads (SessionInbox::INPUT_FORMAT). The Bridge writes it on start
12
13
  # and removes it on stop; one a dead worker left stays until a client's
13
- # probe finds its port closed (BridgeClient.sidecar_port).
14
+ # probe finds its port closed (.live with unlink: true).
14
15
  class WorkerSidecar < Data.define(:port, :bind, :session_id, :started_at, :version, :input_format)
15
16
  FILE = "bridge.json"
17
+ # Seconds a liveness probe waits for the port to take a connect.
18
+ PROBE_TIMEOUT = 0.2
16
19
 
17
20
  def self.path(session_dir) = File.join(session_dir, FILE)
18
21
 
@@ -29,6 +32,29 @@ module Samagotchi
29
32
  nil
30
33
  end
31
34
 
35
+ # The sidecar of a worker whose Bridge takes a connect on its port, or
36
+ # nil. One whose port refuses (its worker died) is stale: +unlink+
37
+ # removes it (the clients that would talk to it), else it stays (a
38
+ # read-only look, like `chi update`'s).
39
+ # @return [WorkerSidecar, nil]
40
+ def self.live(session_dir, unlink:, host: "127.0.0.1", timeout: PROBE_TIMEOUT)
41
+ sidecar = read(session_dir)
42
+ return nil unless sidecar&.port&.positive?
43
+
44
+ begin
45
+ Socket.tcp(host, sidecar.port, connect_timeout: timeout).close
46
+ sidecar
47
+ rescue StandardError
48
+ FileUtils.rm_f(path(session_dir)) if unlink
49
+ nil
50
+ end
51
+ rescue StandardError
52
+ nil
53
+ end
54
+
55
+ # @return [Integer, nil] .live's port
56
+ def self.live_port(session_dir, unlink:, host: "127.0.0.1") = live(session_dir, unlink: unlink, host: host)&.port
57
+
32
58
  def initialize(port:, bind: nil, session_id: nil, started_at: nil, version: nil, input_format: nil) = super
33
59
 
34
60
  # Written whole or not at all; input_format only when it names one.
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.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -187,9 +187,12 @@ files:
187
187
  - lib/samagotchi/bundles/system/memory_guide.md
188
188
  - lib/samagotchi/bundles/system/self_map.md
189
189
  - lib/samagotchi/cancellation_controller.rb
190
+ - lib/samagotchi/cli/command.rb
191
+ - lib/samagotchi/cli/flags.rb
190
192
  - lib/samagotchi/client.rb
191
193
  - lib/samagotchi/commands/registry.rb
192
194
  - lib/samagotchi/config.rb
195
+ - lib/samagotchi/config_text_edit.rb
193
196
  - lib/samagotchi/context_note.rb
194
197
  - lib/samagotchi/context_quote.rb
195
198
  - lib/samagotchi/context_status.rb
@@ -210,6 +213,7 @@ files:
210
213
  - lib/samagotchi/edit_preview.rb
211
214
  - lib/samagotchi/empty_answer_retry.rb
212
215
  - lib/samagotchi/engine.rb
216
+ - lib/samagotchi/events.rb
213
217
  - lib/samagotchi/gem_update.rb
214
218
  - lib/samagotchi/guardrail_wiring.rb
215
219
  - lib/samagotchi/guardrails.rb
@@ -315,6 +319,7 @@ files:
315
319
  - lib/samagotchi/terminal_ui/attach_launcher.rb
316
320
  - lib/samagotchi/terminal_ui/attached_loop.rb
317
321
  - lib/samagotchi/terminal_ui/attached_view.rb
322
+ - lib/samagotchi/terminal_ui/between_turns.rb
318
323
  - lib/samagotchi/terminal_ui/event_renderer.rb
319
324
  - lib/samagotchi/terminal_ui/formatting.rb
320
325
  - lib/samagotchi/terminal_ui/image_input.rb
@@ -341,6 +346,7 @@ files:
341
346
  - lib/samagotchi/tool_runner.rb
342
347
  - lib/samagotchi/tools/args.rb
343
348
  - lib/samagotchi/tools/ask_user_question.rb
349
+ - lib/samagotchi/tools/builtin_calls.rb
344
350
  - lib/samagotchi/tools/builtins.rb
345
351
  - lib/samagotchi/tools/cancel_reminder.rb
346
352
  - lib/samagotchi/tools/delegate.rb
@@ -380,12 +386,14 @@ files:
380
386
  - lib/samagotchi/web/markdown_renderer.rb
381
387
  - lib/samagotchi/web/message_parts.rb
382
388
  - lib/samagotchi/web/public/activity.js
389
+ - lib/samagotchi/web/public/annotate_bar.js
383
390
  - lib/samagotchi/web/public/annotate_presets.js
384
391
  - lib/samagotchi/web/public/annotations.js
385
392
  - lib/samagotchi/web/public/app.js
386
393
  - lib/samagotchi/web/public/card.js
387
394
  - lib/samagotchi/web/public/chunk_router.js
388
395
  - lib/samagotchi/web/public/command_complete.js
396
+ - lib/samagotchi/web/public/command_list.js
389
397
  - lib/samagotchi/web/public/composer_size.js
390
398
  - lib/samagotchi/web/public/copy.js
391
399
  - lib/samagotchi/web/public/ctx.js
@@ -398,12 +406,15 @@ files:
398
406
  - lib/samagotchi/web/public/init_row.js
399
407
  - lib/samagotchi/web/public/model_pick.js
400
408
  - lib/samagotchi/web/public/model_picker.js
409
+ - lib/samagotchi/web/public/notifications.js
401
410
  - lib/samagotchi/web/public/notify.js
402
411
  - lib/samagotchi/web/public/question_card.js
412
+ - lib/samagotchi/web/public/question_cards.js
403
413
  - lib/samagotchi/web/public/route.js
404
414
  - lib/samagotchi/web/public/scope.js
405
415
  - lib/samagotchi/web/public/scroll.js
406
416
  - lib/samagotchi/web/public/sentences.js
417
+ - lib/samagotchi/web/public/session_state.js
407
418
  - lib/samagotchi/web/public/sessions_list.js
408
419
  - lib/samagotchi/web/public/stage_model.js
409
420
  - lib/samagotchi/web/public/stage_view.js