samagotchi 0.112.0 → 0.114.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -1
  3. data/bin/chi +8 -0
  4. data/docs/cli.md +24 -6
  5. data/docs/configuration.md +22 -10
  6. data/docs/internals/prompt-caching.md +2 -1
  7. data/docs/internals/web-push.md +28 -4
  8. data/docs/sessions.md +3 -3
  9. data/lib/samagotchi/bootstrap_command.rb +70 -5
  10. data/lib/samagotchi/bundles/system/manifest.yml +1 -1
  11. data/lib/samagotchi/config.rb +24 -0
  12. data/lib/samagotchi/config_text_edit.rb +10 -3
  13. data/lib/samagotchi/gem_update.rb +5 -1
  14. data/lib/samagotchi/idle_recap.rb +38 -10
  15. data/lib/samagotchi/push_command.rb +188 -0
  16. data/lib/samagotchi/recap_store.rb +20 -3
  17. data/lib/samagotchi/recap_title.rb +78 -0
  18. data/lib/samagotchi/run_options.rb +1 -0
  19. data/lib/samagotchi/session_manager.rb +10 -3
  20. data/lib/samagotchi/sessions_command.rb +14 -11
  21. data/lib/samagotchi/title_command.rb +6 -1
  22. data/lib/samagotchi/version.rb +1 -1
  23. data/lib/samagotchi/web/app.rb +46 -3
  24. data/lib/samagotchi/web/markdown_renderer.rb +20 -0
  25. data/lib/samagotchi/web/public/app.js +6 -0
  26. data/lib/samagotchi/web/public/index.html +43 -4
  27. data/lib/samagotchi/web/public/markdown_tip.js +33 -0
  28. data/lib/samagotchi/web/public/notifications.js +22 -8
  29. data/lib/samagotchi/web/public/push_devices.js +187 -0
  30. data/lib/samagotchi/web/push/attention.rb +2 -1
  31. data/lib/samagotchi/web/push/store.rb +14 -0
  32. data/lib/samagotchi/web/push.rb +5 -0
  33. data/lib/samagotchi/web/push_routes.rb +17 -3
  34. data/lib/samagotchi/web/server.rb +46 -14
  35. data/lib/samagotchi/web/session_show.rb +2 -0
  36. data/lib/samagotchi/web/session_summary.rb +7 -2
  37. metadata +5 -1
@@ -130,7 +130,9 @@ module Samagotchi
130
130
  annotate_presets: annotate_presets, editor: editor, lan: lan, working_set: working_set,
131
131
  public_url: public_url }
132
132
  started = if plan
133
- with_tunnel(provider, plan) { start(**options, public_notes: provider.start_lines(plan)) }
133
+ with_tunnel(provider, plan) do |up|
134
+ start(**options, public_notes: provider.start_lines(plan), on_bound: up)
135
+ end
134
136
  else
135
137
  start(**options)
136
138
  end
@@ -147,25 +149,43 @@ module Samagotchi
147
149
  end
148
150
 
149
151
  # The tunnel on while the block runs (the server), off after it
150
- # whatever ends it (Ctrl-C, SIGTERM, SIGHUP, a port taken), and only
151
- # once #up succeeded: a refusal before it, or a failed #up, turns off
152
- # nothing (a live tunnel of another chi web stays).
152
+ # whatever ends it (Ctrl-C, SIGTERM, SIGHUP), once #up may have taken
153
+ # effect: a refusal before it turns off nothing (a live tunnel of
154
+ # another chi web stays). The block gets #up to call once the port is
155
+ # bound (start's on_bound), so Serve never forwards the tailnet to a
156
+ # listener that took the port first. A signal during #up waits for it
157
+ # (its command runs in its own process group, and Serve keeps what it
158
+ # set even if chi stopped waiting), so it can't land between the serve
159
+ # going on and the teardown being due; #up is bounded by its timeout.
153
160
  def self.with_tunnel(provider, plan)
154
- provider.up(plan)
155
- upped = true
156
- yield
161
+ attempted = false
162
+ yield(lambda do
163
+ Thread.handle_interrupt(SignalException => :never) do
164
+ attempted = true
165
+ provider.up(plan)
166
+ end
167
+ end)
157
168
  ensure
158
- tunnel_down(provider, plan) if upped
169
+ tunnel_down(provider, plan) if attempted
159
170
  end
160
171
 
161
172
  # The tunnel's teardown, before any line (a hung-up terminal can't
162
173
  # write): a failure is one warning line, never a crash or another exit
163
- # status, and a second Ctrl-C during it only cuts it short.
174
+ # status. A second Ctrl-C (SIGTERM, SIGHUP) during it waits for it
175
+ # (bounded by its commands' timeouts) and is then dropped: the run
176
+ # already ends the way the first signal ends it.
164
177
  def self.tunnel_down(provider, plan)
165
- line = begin
166
- provider.down_line(plan, provider.down(plan))
167
- rescue Tunnel::Refused, SignalException => e
168
- provider.down_warning(plan, e)
178
+ line = nil
179
+ begin
180
+ Thread.handle_interrupt(SignalException => :never) do
181
+ line = begin
182
+ provider.down_line(plan, provider.down(plan))
183
+ rescue Tunnel::Refused => e
184
+ provider.down_warning(plan, e)
185
+ end
186
+ end
187
+ rescue SignalException
188
+ nil
169
189
  end
170
190
  say(line) if line
171
191
  end
@@ -187,11 +207,14 @@ module Samagotchi
187
207
  # (web.public_url, or --tunnel's): every request needs the access token
188
208
  # @param public_notes [Array<String>] lines after the proxied link's
189
209
  # (the tunnel's)
210
+ # @param on_bound [#call, nil] run once the port is bound, before any
211
+ # line (the tunnel's #up); what it raises ends the run
190
212
  # @return [Boolean] false when the port was taken (said so on stderr)
191
213
  def self.start(port: nil, host: nil, url: nil, open_browser: false, state_dir: nil, manager: nil, markdown: false,
192
214
  view: Config::BY_KEY["web.view"].default, annotate_presets: Config::BY_KEY["web.annotate_presets"].default, hub: nil, lan: nil,
193
215
  editor: Config::BY_KEY["web.editor"].default, token_path: Token.path,
194
- working_set: Config::BY_KEY["web.working_set"].default, public_url: nil, public_notes: [])
216
+ working_set: Config::BY_KEY["web.working_set"].default, public_url: nil, public_notes: [],
217
+ on_bound: nil)
195
218
  config_restart_unsubscribe = ConfigFile.watch_restart_settings!(process: :web) do |message|
196
219
  Samagotchi::Log.warn(:config, "restart_required", echo: "Warning: config: #{message}")
197
220
  end
@@ -227,7 +250,11 @@ module Samagotchi
227
250
  # Said once the port is bound: a second chi web racing for it gets
228
251
  # the in-use line instead.
229
252
  running_path = Running.path(state_dir: state_dir)
253
+ webrick = nil
254
+ callback_unfinished = false
230
255
  started = lambda do
256
+ callback_unfinished = true
257
+ on_bound&.call
231
258
  record_running(running_path, port, public_url)
232
259
  puts "Chi Web on #{url} (public: #{File.expand_path("public", __dir__)})"
233
260
  puts lan_lines(ip: lan.ip, port: port, token: Token.read(token_path), others: lan.others, public: lan.public) if lan
@@ -244,12 +271,14 @@ module Samagotchi
244
271
  open_url(open_target)
245
272
  end
246
273
  end
274
+ callback_unfinished = false
247
275
  end
248
276
 
249
277
  # The event loops (GET /api/events) end once WEBrick leaves
250
278
  # :Running: it joins every request thread before run returns.
251
279
  Rackup::Handler::WEBrick.run(app, Host: host, Port: port, AccessLog: [], Logger: Log.new($stderr, WEBrick::Log::WARN),
252
280
  StartCallback: started) do |server|
281
+ webrick = server
253
282
  app.server_running = -> { server.status == :Running }
254
283
  listen_lan(server, lan, port) if lan
255
284
  end
@@ -261,6 +290,9 @@ module Samagotchi
261
290
  warn "Error: can't listen on #{lan.ip}:#{port} (#{e.message}); did the address change? Run chi web again"
262
291
  false
263
292
  ensure
293
+ # A start callback that raised (on_bound's refusal, a Ctrl-C) leaves
294
+ # WEBrick :Running with its sockets open.
295
+ webrick&.listeners&.each { |socket| socket.close unless socket.closed? } if callback_unfinished
264
296
  Running.clear(path: running_path) if running_path
265
297
  config_restart_unsubscribe&.call
266
298
  notifier&.stop
@@ -81,6 +81,7 @@ module Samagotchi
81
81
  # names its plugins' too.
82
82
  commands: turn_snapshot&.fetch("commands", nil) || SessionCommands.builtin_registry.listing,
83
83
  markdown_warning: @markdown_renderer.warning,
84
+ markdown_tip: @markdown_renderer.tip,
84
85
  **viewer_capabilities(req),
85
86
  pending_question: pending,
86
87
  last_event_seq: last_event_seq,
@@ -208,6 +209,7 @@ module Samagotchi
208
209
  session: session_json,
209
210
  messages: messages,
210
211
  markdown_warning: @markdown_renderer.warning,
212
+ markdown_tip: @markdown_renderer.tip,
211
213
  **viewer_capabilities(req),
212
214
  timing: timing
213
215
  })
@@ -40,6 +40,7 @@ module Samagotchi
40
40
  up = bridge_up?(session_dir, owner)
41
41
  sidecar = up ? WorkerSidecar.read(session_dir) : nil
42
42
  saved = SessionMetrics.saved_summary(session_dir, budget_tokens: budget_resolver(session, registry))
43
+ recap_card = RecapStore.card(session_dir)
43
44
  {
44
45
  id: session.id,
45
46
  status: status,
@@ -74,8 +75,12 @@ module Samagotchi
74
75
  # place of the first message.
75
76
  title: session.title,
76
77
  owner: owner&.kind,
77
- # The saved recap's first sentence, for the session card.
78
- recap: RecapStore.preview(session_dir),
78
+ # The saved recap's first sentence, for the session card, and the
79
+ # auto title its reply led with (RecapTitle), else nil: an untitled
80
+ # session's name on the pages and its pushes (never in a model
81
+ # request).
82
+ recap: recap_card.preview,
83
+ auto_title: recap_card.title,
79
84
  # How full the context was after the last turn (%), or nil, and
80
85
  # the session's token sums and cost (the card's ctx tooltip), from
81
86
  # one read of analytics.json.
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.112.0
4
+ version: 0.114.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -503,6 +503,7 @@ files:
503
503
  - lib/samagotchi/prompt_note.rb
504
504
  - lib/samagotchi/prompt_warmup.rb
505
505
  - lib/samagotchi/proxy_trace.rb
506
+ - lib/samagotchi/push_command.rb
506
507
  - lib/samagotchi/question_answer.rb
507
508
  - lib/samagotchi/question_asked.rb
508
509
  - lib/samagotchi/question_desk.rb
@@ -512,6 +513,7 @@ files:
512
513
  - lib/samagotchi/recap_command.rb
513
514
  - lib/samagotchi/recap_setup.rb
514
515
  - lib/samagotchi/recap_store.rb
516
+ - lib/samagotchi/recap_title.rb
515
517
  - lib/samagotchi/redacted_inspect.rb
516
518
  - lib/samagotchi/ref.rb
517
519
  - lib/samagotchi/relay.rb
@@ -767,6 +769,7 @@ files:
767
769
  - lib/samagotchi/web/public/llm_context_chip.js
768
770
  - lib/samagotchi/web/public/manifest.webmanifest
769
771
  - lib/samagotchi/web/public/markdown_place.js
772
+ - lib/samagotchi/web/public/markdown_tip.js
770
773
  - lib/samagotchi/web/public/model_pick.js
771
774
  - lib/samagotchi/web/public/model_picker.js
772
775
  - lib/samagotchi/web/public/notice_links.js
@@ -780,6 +783,7 @@ files:
780
783
  - lib/samagotchi/web/public/plain_line.js
781
784
  - lib/samagotchi/web/public/prompt_history.js
782
785
  - lib/samagotchi/web/public/push.js
786
+ - lib/samagotchi/web/public/push_devices.js
783
787
  - lib/samagotchi/web/public/question_card.js
784
788
  - lib/samagotchi/web/public/question_cards.js
785
789
  - lib/samagotchi/web/public/read_view.js