samagotchi 0.4.0 → 0.5.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -1
  3. data/README.md +13 -2
  4. data/bin/chi +29 -39
  5. data/docs/cli.md +135 -73
  6. data/docs/configuration.md +15 -20
  7. data/docs/hooks.md +1 -1
  8. data/docs/memory.md +40 -0
  9. data/docs/plugins.md +50 -0
  10. data/docs/releasing.md +9 -6
  11. data/docs/sessions.md +3 -3
  12. data/lib/samagotchi/bootstrap/config_writer.rb +1 -2
  13. data/lib/samagotchi/bridge/sse_writer.rb +0 -3
  14. data/lib/samagotchi/bridge/turn_accumulator.rb +1 -0
  15. data/lib/samagotchi/bridge.rb +16 -11
  16. data/lib/samagotchi/bundles/skills/manifest.yml +10 -0
  17. data/lib/samagotchi/bundles/skills/plugin.rb +419 -0
  18. data/lib/samagotchi/bundles/system/config_modification_protocol.md +7 -8
  19. data/lib/samagotchi/bundles/system/identity.md +5 -0
  20. data/lib/samagotchi/bundles/system/manifest.yml +5 -5
  21. data/lib/samagotchi/bundles/system/memory_guide.md +26 -0
  22. data/lib/samagotchi/bundles/system/self_map.md +2 -1
  23. data/lib/samagotchi/client.rb +16 -20
  24. data/lib/samagotchi/config.rb +40 -100
  25. data/lib/samagotchi/engine.rb +50 -354
  26. data/lib/samagotchi/kernel_loop.rb +33 -44
  27. data/lib/samagotchi/live_versions.rb +7 -1
  28. data/lib/samagotchi/llm/errors.rb +17 -0
  29. data/lib/samagotchi/llm/http.rb +4 -18
  30. data/lib/samagotchi/llm/openai_chat.rb +17 -0
  31. data/lib/samagotchi/model_profile.rb +4 -10
  32. data/lib/samagotchi/note_command.rb +2 -1
  33. data/lib/samagotchi/reply_wait.rb +48 -4
  34. data/lib/samagotchi/self_report.rb +20 -2
  35. data/lib/samagotchi/send_command.rb +84 -6
  36. data/lib/samagotchi/session.rb +4 -2
  37. data/lib/samagotchi/session_manager.rb +18 -37
  38. data/lib/samagotchi/system_prompt.rb +403 -0
  39. data/lib/samagotchi/terminal_ui/attach_launcher.rb +5 -3
  40. data/lib/samagotchi/terminal_ui/attached_loop.rb +120 -83
  41. data/lib/samagotchi/terminal_ui/attached_view.rb +27 -12
  42. data/lib/samagotchi/terminal_ui/event_renderer.rb +23 -7
  43. data/lib/samagotchi/terminal_ui/formatting.rb +32 -22
  44. data/lib/samagotchi/terminal_ui/input_support.rb +3 -4
  45. data/lib/samagotchi/terminal_ui/plain_surface.rb +13 -7
  46. data/lib/samagotchi/terminal_ui/status_row.rb +81 -0
  47. data/lib/samagotchi/terminal_ui/surface.rb +1 -1
  48. data/lib/samagotchi/terminal_ui.rb +95 -690
  49. data/lib/samagotchi/thinking.rb +11 -0
  50. data/lib/samagotchi/tool_activity.rb +52 -2
  51. data/lib/samagotchi/tool_runner.rb +3 -0
  52. data/lib/samagotchi/tools/execute.rb +3 -3
  53. data/lib/samagotchi/tools/output_guardrails.rb +8 -7
  54. data/lib/samagotchi/tools/read.rb +4 -4
  55. data/lib/samagotchi/update_command.rb +2 -1
  56. data/lib/samagotchi/version.rb +1 -1
  57. data/lib/samagotchi/web/app.rb +170 -35
  58. data/lib/samagotchi/web/lan.rb +99 -0
  59. data/lib/samagotchi/web/message_parts.rb +15 -11
  60. data/lib/samagotchi/web/public/activity.js +7 -0
  61. data/lib/samagotchi/web/public/app.js +99 -54
  62. data/lib/samagotchi/web/public/chat_view.js +5 -1
  63. data/lib/samagotchi/web/public/index.html +163 -17
  64. data/lib/samagotchi/web/public/model_pick.js +136 -0
  65. data/lib/samagotchi/web/public/model_picker.js +224 -0
  66. data/lib/samagotchi/web/public/notify.js +10 -0
  67. data/lib/samagotchi/web/public/stage_model.js +110 -0
  68. data/lib/samagotchi/web/public/stage_view.js +580 -0
  69. data/lib/samagotchi/web/public/timing.js +6 -2
  70. data/lib/samagotchi/web/public/turn_events.js +9 -5
  71. data/lib/samagotchi/web/public/turn_model.js +11 -3
  72. data/lib/samagotchi/web/public/turn_view.js +74 -19
  73. data/lib/samagotchi/web/qr.rb +40 -0
  74. data/lib/samagotchi/web/server.rb +101 -11
  75. data/lib/samagotchi/web/token.rb +97 -0
  76. metadata +27 -3
  77. data/lib/samagotchi/terminal_ui/legacy_surface.rb +0 -111
@@ -6,17 +6,19 @@ require_relative "surface"
6
6
 
7
7
  module Samagotchi
8
8
  class TerminalUI
9
- # Attached mode's Surface when a live region can't be drawn (output or
9
+ # Both TUIs' Surface when a live region can't be drawn (output or
10
10
  # input not a terminal, TERM=dumb, or a Reline the seam doesn't support):
11
11
  # append-only output. Committed text and the notes, status and hints rows
12
12
  # print as lines. The activity slot is dropped, because redrawing it in
13
13
  # place needs cursor movement (the turn's lines still print as they end).
14
- # The prompt is Reline's (or a plain read's) own, so output may cross it.
14
+ # The prompt is Reline's own, so output may cross it; the editor slot
15
+ # prints the prompt of a plain read (the REPL's ? off a terminal).
15
16
  class PlainSurface
16
17
  include Surface
17
18
 
18
- # @param out [IO]
19
- def initialize(out:)
19
+ # @param out [IO, nil] nil writes to $stdout as it is at each write
20
+ # (specs swap it after the UI is built)
21
+ def initialize(out: nil)
20
22
  @out = out
21
23
  @lock = Monitor.new
22
24
  end
@@ -27,15 +29,15 @@ module Samagotchi
27
29
  def columns = IO.console&.winsize&.last || 80
28
30
 
29
31
  def commit(text)
30
- synchronize { @out.puts(text) }
32
+ synchronize { out.puts(text) }
31
33
  end
32
34
 
33
35
  def set_slot(name, rows)
34
36
  check_slot!(name)
35
- raise ArgumentError, "Reline draws the editor slot" if name == :editor
36
37
  return if name == :activity
38
+ return synchronize { out.print(Array(rows).join("\n")) && out.flush } if name == :editor
37
39
 
38
- synchronize { lay_out(rows, width: columns).each { |row| @out.puts(row) } }
40
+ synchronize { lay_out(rows, width: columns).each { |row| out.puts(row) } }
39
41
  end
40
42
 
41
43
  # Nothing printed can be taken back.
@@ -46,6 +48,10 @@ module Samagotchi
46
48
  end
47
49
 
48
50
  def close; end
51
+
52
+ private
53
+
54
+ def out = @out || $stdout
49
55
  end
50
56
  end
51
57
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "formatting"
4
+
5
+ module Samagotchi
6
+ class TerminalUI
7
+ # The status row under the prompt, the same for both TUIs:
8
+ # `status> model=… | ↳ parent | ctx=12.3% (under20) | mem: … | muted: …`.
9
+ # A UI feeds it what it learns (attached mode from the Bridge's events,
10
+ # the REPL from its Engine's) through #update, and the row is redrawn in
11
+ # the surface's status slot only when its text changes.
12
+ class StatusRow
13
+ include Formatting
14
+
15
+ # Memory names shown before ", +N".
16
+ MEMORY_LIMIT = 8
17
+ # What the row shows:
18
+ # model, default_model the model in use, and the config's default
19
+ # (named beside another model)
20
+ # served [served, asked]: what the server said it
21
+ # served for the name asked (ServedModel)
22
+ # parent_id the session that delegated this one
23
+ # context the kernel's estimate {est_pct:, bucket:}
24
+ # used_memories the memories the session read
25
+ # preloaded its --memory list (shown until a turn
26
+ # records them as used)
27
+ # muted its --mute list
28
+ FIELDS = %i[model default_model served parent_id context used_memories preloaded muted].freeze
29
+
30
+ # @param surface [Surface] with #columns
31
+ def initialize(surface)
32
+ @surface = surface
33
+ @values = { used_memories: [], preloaded: [], muted: [] }
34
+ @shown = nil
35
+ end
36
+
37
+ # The surface it draws on (the REPL swaps in a live region): the row
38
+ # is drawn there at the next change.
39
+ def surface=(surface)
40
+ @surface = surface
41
+ @shown = nil
42
+ end
43
+
44
+ def [](field) = @values[field]
45
+
46
+ # Change some fields; the row is redrawn when its text changed.
47
+ def update(**fields)
48
+ unknown = fields.keys - FIELDS
49
+ raise ArgumentError, "unknown status field(s): #{unknown.join(", ")}" if unknown.any?
50
+
51
+ @values.merge!(fields)
52
+ refresh
53
+ end
54
+
55
+ # Draw the row unless it shows that already (status.line: off draws
56
+ # nothing).
57
+ def refresh
58
+ return unless status_line_enabled?
59
+
60
+ rows = rows(@surface.columns - 1)
61
+ return if rows == @shown
62
+
63
+ @shown = rows
64
+ rows.empty? ? @surface.clear_slot(:status) : @surface.set_slot(:status, rows)
65
+ end
66
+
67
+ # @return [Array<String>] the row cut to +width+ (none with nothing to say)
68
+ def rows(width)
69
+ served, served_for = @values[:served]
70
+ model = @values[:model]
71
+ parent = @values[:parent_id]
72
+ segments = [model ? status_model_text(model, @values[:default_model], served: served, served_for: served_for) : "",
73
+ parent ? "↳ #{parent.to_s[0, 8]}" : "",
74
+ status_context_text(estimate: @values[:context]),
75
+ status_memory_text(Array(@values[:used_memories]) | Array(@values[:preloaded]), MEMORY_LIMIT),
76
+ status_memory_text(@values[:muted], MEMORY_LIMIT, label: "muted")].reject(&:empty?)
77
+ status_rows(segments, width)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -15,7 +15,7 @@ module Samagotchi
15
15
  #
16
16
  # The slots, top down around the prompt: +activity+ (spinner, thinking
17
17
  # preview), +editor+ (the prompt), +status+, +notes+ (question choices),
18
- # +hints+. An implementation without a live region (LegacySurface) may
18
+ # +hints+. An implementation without a live region (PlainSurface) may
19
19
  # print a slot's rows as plain output instead, so the rows stay on screen.
20
20
  #
21
21
  # Instead of rows, a slot may hold content that lays itself out: anything