samagotchi 0.44.0 → 0.46.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 +120 -1
- data/docs/architecture.md +2 -1
- data/docs/cli.md +43 -12
- data/docs/configuration.md +102 -5
- data/docs/context.md +2 -1
- data/docs/internals/background-tasks.md +1 -0
- data/docs/internals/engine.md +1 -1
- data/docs/internals/llm-context-live.md +111 -0
- data/docs/plugins.md +4 -2
- data/docs/sessions.md +9 -5
- data/lib/samagotchi/bridge/turn_accumulator.rb +19 -0
- data/lib/samagotchi/bridge.rb +22 -10
- data/lib/samagotchi/bundles/github-pr/manifest.yml +3 -3
- data/lib/samagotchi/bundles/github-pr/plugin.rb +22 -4
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/self_map.md +2 -1
- data/lib/samagotchi/commands/registry.rb +54 -9
- data/lib/samagotchi/config.rb +57 -4
- data/lib/samagotchi/desktop/macos/Panel.swift +7 -3
- data/lib/samagotchi/engine.rb +47 -9
- data/lib/samagotchi/host_model.rb +116 -0
- data/lib/samagotchi/host_registry.rb +43 -12
- data/lib/samagotchi/kernel_loop.rb +2 -1
- data/lib/samagotchi/llm/chat_loop.rb +89 -7
- data/lib/samagotchi/llm/turn_settings.rb +5 -3
- data/lib/samagotchi/llm_context_strategy.rb +37 -0
- data/lib/samagotchi/model_catalog.rb +17 -12
- data/lib/samagotchi/model_notes.rb +8 -6
- data/lib/samagotchi/model_overlay.rb +3 -2
- data/lib/samagotchi/model_price.rb +54 -0
- data/lib/samagotchi/model_profile.rb +74 -3
- data/lib/samagotchi/plugin/context.rb +19 -7
- data/lib/samagotchi/process_group.rb +56 -9
- data/lib/samagotchi/recap_setup.rb +1 -1
- data/lib/samagotchi/self_report.rb +33 -3
- data/lib/samagotchi/session_commands.rb +47 -21
- data/lib/samagotchi/session_manager.rb +19 -1
- data/lib/samagotchi/session_metrics.rb +79 -11
- data/lib/samagotchi/sessions_command.rb +15 -5
- data/lib/samagotchi/terminal_ui/attached_loop.rb +85 -3
- data/lib/samagotchi/terminal_ui/attached_view.rb +2 -0
- data/lib/samagotchi/terminal_ui/formatting.rb +45 -10
- data/lib/samagotchi/terminal_ui/status_row.rb +12 -6
- data/lib/samagotchi/terminal_ui.rb +97 -12
- data/lib/samagotchi/tools/delegate_wait.rb +5 -4
- data/lib/samagotchi/tools/task_runtime.rb +11 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +12 -11
- data/lib/samagotchi/web/public/app.js +30 -18
- data/lib/samagotchi/web/public/command_complete.js +3 -1
- data/lib/samagotchi/web/public/ctx.js +62 -22
- data/lib/samagotchi/web/public/format.js +7 -2
- data/lib/samagotchi/web/public/index.html +3 -1
- data/lib/samagotchi/web/public/llm_context_chip.js +2 -4
- data/lib/samagotchi/web/public/model_pick.js +23 -3
- data/lib/samagotchi/web/public/model_picker.js +5 -3
- data/lib/samagotchi/web/public/stage_model.js +18 -4
- data/lib/samagotchi/web/public/stage_view.js +4 -3
- data/lib/samagotchi/web/public/turn_events.js +19 -6
- data/lib/samagotchi/web/public/turn_model.js +16 -0
- data/lib/samagotchi/web/public/turn_view.js +19 -0
- data/lib/samagotchi/web/session_hub.rb +9 -1
- data/lib/samagotchi/web/session_summary.rb +19 -2
- data/lib/samagotchi/worker.rb +172 -21
- data/lib/samagotchi/worker_inbound.rb +6 -5
- metadata +4 -1
data/lib/samagotchi/worker.rb
CHANGED
|
@@ -16,6 +16,7 @@ require_relative "session_manager"
|
|
|
16
16
|
require_relative "archive_store"
|
|
17
17
|
require_relative "log"
|
|
18
18
|
require_relative "turn_flow"
|
|
19
|
+
require_relative "llm/errors"
|
|
19
20
|
require_relative "continue_offer"
|
|
20
21
|
require_relative "iteration_limit"
|
|
21
22
|
require_relative "session_commands"
|
|
@@ -59,6 +60,8 @@ module Samagotchi
|
|
|
59
60
|
# How much of a command's output goes into its :command_ran.
|
|
60
61
|
COMMAND_OUTPUT_LIMIT = 4096
|
|
61
62
|
BUSY_OUTPUT = "busy: wait for the turn to end"
|
|
63
|
+
# !rollback mid-turn would mean "the turn I'm in".
|
|
64
|
+
ROLLBACK_BUSY_OUTPUT = "busy: Ctrl-C the turn first, then !rollback"
|
|
62
65
|
|
|
63
66
|
# Wakes the worker loop. Whoever queues work writes it first and wakes
|
|
64
67
|
# after, and #wait drains every wake before the loop looks for work: a
|
|
@@ -184,7 +187,10 @@ module Samagotchi
|
|
|
184
187
|
# queued while that turn ran.
|
|
185
188
|
@turn_end_seq = 0
|
|
186
189
|
@engine.subscribe(observer: lambda { |event|
|
|
187
|
-
|
|
190
|
+
if Events::TURN_END.include?(event[:type])
|
|
191
|
+
@turn_end_seq = event[:event_seq]
|
|
192
|
+
@turn_end_type = event[:type]
|
|
193
|
+
end
|
|
188
194
|
@turn_id = event[:turn_id] if event[:type] == :turn_started
|
|
189
195
|
ring_question(event) if event[:type] == :question_requested
|
|
190
196
|
})
|
|
@@ -198,7 +204,9 @@ module Samagotchi
|
|
|
198
204
|
context_absorber: @context_absorber, wakes: @wakes,
|
|
199
205
|
awaiting_continue: -> { @turn_flow.awaiting_continue? },
|
|
200
206
|
stopped: -> { stopped_on_disk? },
|
|
201
|
-
queue_command:
|
|
207
|
+
queue_command: lambda { |line, client_id, after_file|
|
|
208
|
+
@bridge.queue_command(line, client_id: client_id, after_file: after_file)
|
|
209
|
+
})
|
|
202
210
|
# /model's default is the config's, as in the REPL; the Engine started
|
|
203
211
|
# on the session's model.
|
|
204
212
|
@default_model = ModelProfile.required_model_name(nil)
|
|
@@ -215,14 +223,7 @@ module Samagotchi
|
|
|
215
223
|
def serve
|
|
216
224
|
@bridge = SessionManager.start_bridge(engine: @engine, state_dir: @state_dir, session_id: @session_id,
|
|
217
225
|
on_input: -> { @waker.wake },
|
|
218
|
-
on_command:
|
|
219
|
-
next start_anytime_command(command) if anytime_command?(command[:line])
|
|
220
|
-
|
|
221
|
-
# Called with the event log held: the
|
|
222
|
-
# count says which turn ends came before it.
|
|
223
|
-
@command_queue << command.merge(after_seq: @engine.event_count)
|
|
224
|
-
@waker.wake
|
|
225
|
-
},
|
|
226
|
+
on_command: method(:on_command),
|
|
226
227
|
on_exit_request: method(:exit_request),
|
|
227
228
|
# Decided again as it leaves (a note may still come in).
|
|
228
229
|
exit_discards: method(:empty_session?))
|
|
@@ -307,6 +308,9 @@ module Samagotchi
|
|
|
307
308
|
# A stop between two queued turns leaves the rest queued.
|
|
308
309
|
break if stopped_on_disk?
|
|
309
310
|
|
|
311
|
+
# Commands that came before this file (D1: a queued /model X sent
|
|
312
|
+
# before this prompt, a command an earlier file carried) run first.
|
|
313
|
+
run_queued_commands
|
|
310
314
|
@inbound.absorb_notes
|
|
311
315
|
run_input_file(input_file)
|
|
312
316
|
end
|
|
@@ -334,6 +338,7 @@ module Samagotchi
|
|
|
334
338
|
# group) before the Engine does.
|
|
335
339
|
def teardown
|
|
336
340
|
@context_poller&.stop
|
|
341
|
+
drop_queued_commands
|
|
337
342
|
@engine&.shutdown
|
|
338
343
|
@bridge&.stop
|
|
339
344
|
# The process exits next: a parent wake it rang for (a crash's
|
|
@@ -549,29 +554,122 @@ module Samagotchi
|
|
|
549
554
|
|
|
550
555
|
def max_iterations(no_interrupt) = IterationLimit.for(no_interrupt: no_interrupt)
|
|
551
556
|
|
|
557
|
+
# The queued commands that may run now, in order, up to one whose
|
|
558
|
+
# after_file (D1) still waits in the inbox: it and those behind it stay
|
|
559
|
+
# queued.
|
|
552
560
|
# @return [Boolean] whether any command ran
|
|
553
561
|
def run_queued_commands
|
|
554
562
|
ran = false
|
|
555
|
-
while (command =
|
|
563
|
+
while (command = next_ready_command)
|
|
556
564
|
run_command(command)
|
|
557
565
|
ran = true
|
|
558
566
|
end
|
|
559
567
|
ran
|
|
560
568
|
end
|
|
561
569
|
|
|
562
|
-
#
|
|
570
|
+
# Pops the first queued command if it may run now (its after_file is
|
|
571
|
+
# taken), else none: one behind it waits too, in arrival order (an idle
|
|
572
|
+
# /model Y that came just after the turn ended stays behind a /model X
|
|
573
|
+
# still waiting for a prompt sent before it). With the event log held,
|
|
574
|
+
# as the Bridge queues.
|
|
575
|
+
def next_ready_command
|
|
576
|
+
return nil if @command_queue.empty?
|
|
577
|
+
|
|
578
|
+
@engine.synchronize_events do
|
|
579
|
+
commands = drain_commands
|
|
580
|
+
first = commands.first
|
|
581
|
+
ready = first && ready_command?(first) ? commands.shift : nil
|
|
582
|
+
commands.each { |command| @command_queue << command }
|
|
583
|
+
ready
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def ready_command?(command)
|
|
588
|
+
return true if command[:after_file].nil?
|
|
589
|
+
|
|
590
|
+
SessionInbox.find_new_input_files(@session_dir).none? { |path| File.basename(path) <= command[:after_file] }
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def drain_commands
|
|
594
|
+
commands = []
|
|
595
|
+
while (command = next_command)
|
|
596
|
+
commands << command
|
|
597
|
+
end
|
|
598
|
+
commands
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# A command a turn refuses (:refuse while it ran, S1) is answered busy:
|
|
563
602
|
# at the turn's iteration boundaries (mid_turn: all of them), and when
|
|
564
603
|
# it ends (the ones queued before its end event; later ones run next).
|
|
604
|
+
# A queued one (:queue) stays for after the turn, but a !cmd after a
|
|
605
|
+
# canceled or failed turn (D4): running it would close the rollback
|
|
606
|
+
# window the turn left, so it is dropped. With the event log held, so
|
|
607
|
+
# a command the Bridge queues meanwhile stays behind the ones kept, in
|
|
608
|
+
# arrival order. One that comes after the empty look is the next
|
|
609
|
+
# boundary's (or end's), as it would be.
|
|
565
610
|
def refuse_queued_commands(mid_turn: false)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
611
|
+
return if @command_queue.empty?
|
|
612
|
+
|
|
613
|
+
@engine.synchronize_events do
|
|
614
|
+
drain_commands.each do |command|
|
|
615
|
+
during = mid_turn || command[:after_seq].to_i < @turn_end_seq
|
|
616
|
+
case (during ? turn_answer(command, ended: !mid_turn) : :keep)
|
|
617
|
+
when :refuse then announce_command(command, status: "busy", output: busy_output(command), changed: [])
|
|
618
|
+
when :drop then drop_command(waiting_for_turn_end(command), "turn #{@turn_end_type == :turn_failed ? "failed" : "canceled"}: " \
|
|
619
|
+
"#{command[:line]} not run; send it again")
|
|
620
|
+
else @command_queue << (during ? waiting_for_turn_end(command) : command)
|
|
621
|
+
end
|
|
572
622
|
end
|
|
573
623
|
end
|
|
574
|
-
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# What a turn does with +command+, queued while it ran: :refuse, :drop
|
|
627
|
+
# (D4, once it +ended+) or :keep. One queued idle just as the turn
|
|
628
|
+
# began (:loop) goes by its line's policy (a show form waits too).
|
|
629
|
+
def turn_answer(command, ended:)
|
|
630
|
+
policy = command[:mid_turn]
|
|
631
|
+
policy = @engine.command_registry.mid_turn(command[:line]) if policy == :loop
|
|
632
|
+
return :refuse if policy == :refuse
|
|
633
|
+
return :drop if ended && policy == :queue && shell_command?(command) &&
|
|
634
|
+
%i[turn_canceled turn_failed].include?(@turn_end_type)
|
|
635
|
+
|
|
636
|
+
:keep
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
# +command+, kept for after the turn. One queued idle just as the turn
|
|
640
|
+
# began (:loop) whose line waits for a turn's end (/model X, !cmd) is
|
|
641
|
+
# marked as one queued mid-turn: its command_queued (waits) tells the
|
|
642
|
+
# UIs now, and its command_ran says queued.
|
|
643
|
+
def waiting_for_turn_end(command)
|
|
644
|
+
return command unless command[:mid_turn] == :loop && @engine.command_registry.mid_turn(command[:line]) == :queue
|
|
645
|
+
|
|
646
|
+
event = { type: :command_queued, **command.slice(:command_id, :client_id, :line), waits: "turn_end" }
|
|
647
|
+
event[:card] = true if command[:card]
|
|
648
|
+
@engine.announce(event)
|
|
649
|
+
command.merge(mid_turn: :queue)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def shell_command?(command) = @engine.command_registry.lookup(command[:line])&.id == :shell
|
|
653
|
+
|
|
654
|
+
def busy_output(command)
|
|
655
|
+
@engine.command_registry.lookup(command[:line])&.id == :rollback ? ROLLBACK_BUSY_OUTPUT : BUSY_OUTPUT
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
# +command+ won't run: its sender (a UI's queued bubble) is told.
|
|
659
|
+
def drop_command(command, output)
|
|
660
|
+
announce_command(command, status: "dropped", output: output, changed: [])
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# The worker leaves with commands still queued (a stop, a crash): each
|
|
664
|
+
# one's sender is told it won't run, so no UI shows it waiting for good.
|
|
665
|
+
def drop_queued_commands
|
|
666
|
+
return if @command_queue.nil? || @command_queue.empty? || @engine.nil?
|
|
667
|
+
|
|
668
|
+
@engine.synchronize_events do
|
|
669
|
+
drain_commands.each { |command| drop_command(command, "dropped: the session's worker stopped before it ran") }
|
|
670
|
+
end
|
|
671
|
+
rescue StandardError => e
|
|
672
|
+
Log.warn(:worker, "drop_commands_failed", error: e.class.name, msg: e.message)
|
|
575
673
|
end
|
|
576
674
|
|
|
577
675
|
def next_command
|
|
@@ -607,7 +705,44 @@ module Samagotchi
|
|
|
607
705
|
[result || SessionCommands::Result.new(status: :error, output: "not a session command", changed: []), shown]
|
|
608
706
|
end
|
|
609
707
|
|
|
610
|
-
|
|
708
|
+
# A session command from the Bridge, called with the event log held:
|
|
709
|
+
# an anytime one starts now, any other is queued for the loop (and
|
|
710
|
+
# refused if a turn runs: #refuse_queued_commands). While a turn runs
|
|
711
|
+
# the line's policy decides (a show form like /model runs now); idle,
|
|
712
|
+
# only an always-anytime command (/help) runs beside the loop, so an
|
|
713
|
+
# idle /model X then /model run in order. A turn starting or ending
|
|
714
|
+
# right now may see the other side: the line is only shown early or
|
|
715
|
+
# run in order.
|
|
716
|
+
#
|
|
717
|
+
# A queued one (:queue) keeps arrival order with the prompts (D1): it
|
|
718
|
+
# runs once every input file written before it is taken (after_file,
|
|
719
|
+
# the newest one now; names compare, not clocks), so a prompt sent
|
|
720
|
+
# before it runs first and one sent after it runs after. One that came
|
|
721
|
+
# in an input file runs once that file is taken (+after_file+).
|
|
722
|
+
# @return [Symbol] the policy it applied (the Bridge marks an anytime
|
|
723
|
+
# one's command_queued, and a queued one's): :anytime, :queue or
|
|
724
|
+
# :refuse while a turn runs, :loop idle
|
|
725
|
+
def on_command(command, after_file: nil)
|
|
726
|
+
registry = @engine.command_registry
|
|
727
|
+
policy = if @engine.turn_running? then registry.mid_turn(command[:line])
|
|
728
|
+
else registry.anytime?(command[:line]) ? :anytime : :loop
|
|
729
|
+
end
|
|
730
|
+
if policy == :anytime
|
|
731
|
+
start_anytime_command(command)
|
|
732
|
+
return policy
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
after_file ||= newest_input_file if policy == :queue
|
|
736
|
+
# The count says which turn ends came before it.
|
|
737
|
+
@command_queue << command.merge(after_seq: @engine.event_count, mid_turn: policy, after_file: after_file).compact
|
|
738
|
+
@waker.wake
|
|
739
|
+
policy
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
# @return [String, nil] the newest input file waiting (its basename)
|
|
743
|
+
def newest_input_file
|
|
744
|
+
SessionInbox.find_new_input_files(@session_dir).map { |path| File.basename(path) }.max
|
|
745
|
+
end
|
|
611
746
|
|
|
612
747
|
# An anytime command (/help, a plugin's /btw; D8) runs now, on its own
|
|
613
748
|
# thread, never queued behind a turn: it is never busy. The Bridge calls
|
|
@@ -640,6 +775,8 @@ module Samagotchi
|
|
|
640
775
|
status: status, output: text[0, COMMAND_OUTPUT_LIMIT], changed: changed.map(&:to_s),
|
|
641
776
|
model_name: @engine.effective_model_name }
|
|
642
777
|
event[:anytime] = true if anytime
|
|
778
|
+
# Its line was shown at its command_queued (waits: turn_end).
|
|
779
|
+
event[:queued] = true if command[:mid_turn] == :queue
|
|
643
780
|
event[:card] = true if command[:card]
|
|
644
781
|
# /llm-context or /model: what the next turn runs under now (the web's chip).
|
|
645
782
|
event[:llm_context] = @engine.llm_context_summary if changed.intersect?(%i[llm_context model])
|
|
@@ -694,7 +831,7 @@ module Samagotchi
|
|
|
694
831
|
# failed prompt or wake turn that got nowhere, a canceled continue
|
|
695
832
|
# turn). A failed turn whose work stayed, and a reminder or continue
|
|
696
833
|
# turn that failed (kept with its failed note), read them.
|
|
697
|
-
settle_child_reports(rolled_back: @turn_flow.restores != restores)
|
|
834
|
+
settle_child_reports(rolled_back: @turn_flow.restores != restores || merges_lost?(error))
|
|
698
835
|
ring_parent_after_turn
|
|
699
836
|
end
|
|
700
837
|
|
|
@@ -707,6 +844,20 @@ module Samagotchi
|
|
|
707
844
|
rolled_back ? @child_reports.release : @child_reports.commit
|
|
708
845
|
end
|
|
709
846
|
|
|
847
|
+
# Whether +error+ ended the turn without its loop's conversation: a
|
|
848
|
+
# failure the loops didn't mark (LLM::FailedTurn), one past their end
|
|
849
|
+
# (the Engine's own end of the turn, say). The Engine kept the turn's
|
|
850
|
+
# start only, so the reports its loop merged are not in the
|
|
851
|
+
# conversation: their rings stay for the next turn, even when the turn
|
|
852
|
+
# itself stays (a reminder or continue turn). Not one raised after the
|
|
853
|
+
# turn ended (the Engine's post-turn work): the turn was kept as
|
|
854
|
+
# completed, the reports in it.
|
|
855
|
+
def merges_lost?(error)
|
|
856
|
+
return false if error.nil? || error.is_a?(LLM::FailedTurn)
|
|
857
|
+
|
|
858
|
+
@session.last_turn&.dig("outcome") != "completed"
|
|
859
|
+
end
|
|
860
|
+
|
|
710
861
|
# A delegate child's turn that was its parent's (#reports_to_parent?)
|
|
711
862
|
# rings the parent, after the save: the parent reads a settled child.
|
|
712
863
|
# A question it rang about that still waits makes the next turn ring
|
|
@@ -61,8 +61,9 @@ module Samagotchi
|
|
|
61
61
|
# @param wakes [WorkerWakes] the wake budget a context wake asks
|
|
62
62
|
# @param awaiting_continue [#call] whether a continue offer waits
|
|
63
63
|
# @param stopped [#call] whether the session was stopped on disk
|
|
64
|
-
# @param queue_command [#call] (line, client_id) queues a
|
|
65
|
-
# command for the loop's next pass
|
|
64
|
+
# @param queue_command [#call] (line, client_id, after_file) queues a
|
|
65
|
+
# session command for the loop's next pass; after_file is the input
|
|
66
|
+
# file it came in, or nil
|
|
66
67
|
def initialize(session:, state_dir:, session_dir:, engine:, context_absorber:, wakes:, awaiting_continue:,
|
|
67
68
|
stopped:, queue_command:)
|
|
68
69
|
@session = session
|
|
@@ -182,7 +183,7 @@ module Samagotchi
|
|
|
182
183
|
begin
|
|
183
184
|
message, origin, no_interrupt, images = SessionInbox.read_input(claimed_file)
|
|
184
185
|
return if message.to_s.strip.empty?
|
|
185
|
-
return if Array(images).empty? && queue_as_command(message, origin)
|
|
186
|
+
return if Array(images).empty? && queue_as_command(message, origin, File.basename(input_file))
|
|
186
187
|
|
|
187
188
|
yield Prompt.new(text: message, origin: origin, no_interrupt: !!no_interrupt, images: images || [])
|
|
188
189
|
ensure
|
|
@@ -211,10 +212,10 @@ module Samagotchi
|
|
|
211
212
|
# POST /turn; an unknown /word stays a prompt. Queued: the loop's next
|
|
212
213
|
# pass runs it.
|
|
213
214
|
# @return [Boolean] whether +text+ was one
|
|
214
|
-
def queue_as_command(text, origin)
|
|
215
|
+
def queue_as_command(text, origin, after_file = nil)
|
|
215
216
|
return false unless @engine.command_registry.command?(text.to_s)
|
|
216
217
|
|
|
217
|
-
@queue_command.call(text, origin&.dig(:client_id))
|
|
218
|
+
@queue_command.call(text, origin&.dig(:client_id), after_file)
|
|
218
219
|
true
|
|
219
220
|
end
|
|
220
221
|
|
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.
|
|
4
|
+
version: 0.46.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -136,6 +136,7 @@ files:
|
|
|
136
136
|
- docs/internals/gemma4-contract.md
|
|
137
137
|
- docs/internals/llm-context-bench.md
|
|
138
138
|
- docs/internals/llm-context-forget.md
|
|
139
|
+
- docs/internals/llm-context-live.md
|
|
139
140
|
- docs/internals/prompt-caching.md
|
|
140
141
|
- docs/internals/tool-guardrails.md
|
|
141
142
|
- docs/memory.md
|
|
@@ -285,6 +286,7 @@ files:
|
|
|
285
286
|
- lib/samagotchi/hooks/loader.rb
|
|
286
287
|
- lib/samagotchi/hooks/registry.rb
|
|
287
288
|
- lib/samagotchi/hooks/stream_watch.rb
|
|
289
|
+
- lib/samagotchi/host_model.rb
|
|
288
290
|
- lib/samagotchi/host_registry.rb
|
|
289
291
|
- lib/samagotchi/idle_client.rb
|
|
290
292
|
- lib/samagotchi/idle_recap.rb
|
|
@@ -348,6 +350,7 @@ files:
|
|
|
348
350
|
- lib/samagotchi/model_match.rb
|
|
349
351
|
- lib/samagotchi/model_notes.rb
|
|
350
352
|
- lib/samagotchi/model_overlay.rb
|
|
353
|
+
- lib/samagotchi/model_price.rb
|
|
351
354
|
- lib/samagotchi/model_profile.rb
|
|
352
355
|
- lib/samagotchi/model_ref.rb
|
|
353
356
|
- lib/samagotchi/models_command.rb
|