samagotchi 0.66.0 → 0.68.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 +80 -1
- data/docs/architecture.md +55 -30
- data/docs/cli.md +3 -1
- data/docs/configuration.md +50 -9
- data/docs/context.md +35 -1
- data/docs/desktop.md +90 -22
- data/docs/guardrails.md +8 -5
- data/docs/hooks.md +2 -2
- data/docs/internals/anthropic.md +156 -0
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/bundles.md +2 -0
- data/docs/internals/engine.md +129 -38
- data/docs/internals/gemma4-contract.md +1 -1
- data/docs/internals/llm-context-bench.md +5 -4
- data/docs/internals/prompt-caching.md +26 -11
- data/docs/plugins.md +126 -23
- data/docs/sessions.md +6 -6
- data/docs/sub-agent.md +4 -1
- data/docs/testing.md +18 -4
- data/lib/samagotchi/bootstrap/probe.rb +4 -3
- data/lib/samagotchi/bootstrap_command.rb +13 -3
- data/lib/samagotchi/bridge/ring_buffer.rb +0 -5
- data/lib/samagotchi/bridge.rb +6 -3
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +7 -6
- data/lib/samagotchi/bundles/claude-peer/plugin.rb +34 -313
- data/lib/samagotchi/bundles/claude-peer/scripts/chi-peer-SKILL.md +8 -3
- data/lib/samagotchi/bundles/claude-peer/skill_claude_peer.md +14 -6
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +7 -6
- data/lib/samagotchi/bundles/codex-peer/plugin.rb +37 -312
- data/lib/samagotchi/bundles/codex-peer/scripts/chi-peer-SKILL.md +8 -3
- data/lib/samagotchi/bundles/codex-peer/skill_codex_peer.md +14 -6
- data/lib/samagotchi/bundles/mcp/manifest.yml +5 -5
- data/lib/samagotchi/bundles/mcp/plugin.rb +82 -294
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +7 -3
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/config.rb +15 -7
- data/lib/samagotchi/context_absorber.rb +66 -13
- data/lib/samagotchi/desktop/macos/AgentTerminal.swift +260 -0
- data/lib/samagotchi/desktop/macos/App.swift +3 -3
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +5 -2
- data/lib/samagotchi/desktop/macos/Images.swift +2 -2
- data/lib/samagotchi/desktop/macos/Kitty.swift +33 -197
- data/lib/samagotchi/desktop/macos/Panel.swift +118 -82
- data/lib/samagotchi/desktop/macos/ProcessRunner.swift +1 -1
- data/lib/samagotchi/desktop/macos/Tmux.swift +239 -0
- data/lib/samagotchi/desktop/macos.rb +84 -13
- data/lib/samagotchi/desktop_command.rb +14 -3
- data/lib/samagotchi/effective_model.rb +283 -0
- data/lib/samagotchi/engine.rb +307 -549
- data/lib/samagotchi/engine_peers.rb +3 -0
- data/lib/samagotchi/guardrail_wiring.rb +1 -1
- data/lib/samagotchi/guardrails/approval.rb +1 -1
- data/lib/samagotchi/guardrails/approvals.rb +1 -1
- data/lib/samagotchi/guardrails/rules.rb +1 -1
- data/lib/samagotchi/guardrails/targets.rb +1 -1
- data/lib/samagotchi/hook_routes.rb +151 -0
- data/lib/samagotchi/hooks/registry.rb +2 -2
- data/lib/samagotchi/host_keys.rb +147 -0
- data/lib/samagotchi/level_setting_command.rb +99 -0
- data/lib/samagotchi/llm/adapter_common.rb +138 -0
- data/lib/samagotchi/llm/anthropic_messages.rb +28 -105
- data/lib/samagotchi/llm/api_key.rb +23 -7
- data/lib/samagotchi/llm/chat_loop.rb +10 -6
- data/lib/samagotchi/llm/openai_chat.rb +9 -92
- data/lib/samagotchi/llm/refusal_memo.rb +29 -0
- data/lib/samagotchi/llm_context_override.rb +1 -0
- data/lib/samagotchi/llm_context_strategy.rb +5 -10
- data/lib/samagotchi/peer_sources.rb +58 -0
- data/lib/samagotchi/plugin/api.rb +64 -155
- data/lib/samagotchi/plugin/loader.rb +4 -2
- data/lib/samagotchi/plugin/peer.rb +394 -0
- data/lib/samagotchi/plugin/tool_spec.rb +166 -0
- data/lib/samagotchi/plugin_tasks.rb +2 -2
- data/lib/samagotchi/reasoning_history_command.rb +29 -90
- data/lib/samagotchi/row_refs.rb +1 -1
- data/lib/samagotchi/self_report.rb +5 -3
- data/lib/samagotchi/send_command.rb +21 -11
- data/lib/samagotchi/session.rb +22 -8
- data/lib/samagotchi/session_continue.rb +58 -15
- data/lib/samagotchi/session_inbox.rb +0 -13
- data/lib/samagotchi/session_setting.rb +86 -0
- data/lib/samagotchi/thinking_command.rb +24 -76
- data/lib/samagotchi/thinking_notices.rb +169 -0
- data/lib/samagotchi/tool_call_parser.rb +1 -1
- data/lib/samagotchi/toolkit_binding.rb +138 -0
- data/lib/samagotchi/tools/context_read.rb +14 -0
- data/lib/samagotchi/tools/execute.rb +12 -12
- data/lib/samagotchi/tools/registry.rb +1 -1
- data/lib/samagotchi/tools/task_create.rb +6 -6
- data/lib/samagotchi/tools/task_get.rb +13 -13
- data/lib/samagotchi/tools/task_list.rb +6 -6
- data/lib/samagotchi/tools/task_record.rb +52 -0
- data/lib/samagotchi/tools/task_runtime.rb +55 -91
- data/lib/samagotchi/tools/task_stop.rb +6 -6
- data/lib/samagotchi/tools/task_wait.rb +12 -13
- data/lib/samagotchi/tools/tool_index.rb +454 -0
- data/lib/samagotchi/update_command.rb +9 -6
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +71 -93
- data/lib/samagotchi/web/capabilities.rb +1 -1
- data/lib/samagotchi/web/public/activity.js +2 -2
- data/lib/samagotchi/web/public/annotate_actions.js +1 -1
- data/lib/samagotchi/web/public/app.js +103 -539
- data/lib/samagotchi/web/public/bubbles.js +291 -0
- data/lib/samagotchi/web/public/cards_place.js +184 -0
- data/lib/samagotchi/web/public/chains.js +1 -1
- data/lib/samagotchi/web/public/chip_popover.js +111 -0
- data/lib/samagotchi/web/public/command_list.js +12 -10
- data/lib/samagotchi/web/public/commands.js +2 -2
- data/lib/samagotchi/web/public/composer_size.js +2 -2
- data/lib/samagotchi/web/public/copy.js +1 -1
- data/lib/samagotchi/web/public/ctx.js +1 -1
- data/lib/samagotchi/web/public/delivery_line.js +4 -4
- data/lib/samagotchi/web/public/families_view.js +1 -1
- data/lib/samagotchi/web/public/format.js +11 -7
- data/lib/samagotchi/web/public/images.js +25 -2
- data/lib/samagotchi/web/public/index.html +15 -7
- data/lib/samagotchi/web/public/info_bar.js +3 -3
- data/lib/samagotchi/web/public/info_bar_model.js +110 -0
- data/lib/samagotchi/web/public/init_row.js +21 -10
- data/lib/samagotchi/web/public/level_chip.js +29 -80
- data/lib/samagotchi/web/public/lists_view.js +64 -9
- data/lib/samagotchi/web/public/llm_context_chip.js +38 -107
- data/lib/samagotchi/web/public/model_picker.js +26 -22
- data/lib/samagotchi/web/public/notify.js +6 -4
- data/lib/samagotchi/web/public/question_card.js +1 -1
- data/lib/samagotchi/web/public/ref_scan.js +2 -8
- data/lib/samagotchi/web/public/refs.js +13 -0
- data/lib/samagotchi/web/public/session_state.js +14 -0
- data/lib/samagotchi/web/public/session_title.js +2 -3
- data/lib/samagotchi/web/public/sessions_list.js +0 -8
- data/lib/samagotchi/web/public/stage_frame.js +179 -0
- data/lib/samagotchi/web/public/stage_model.js +35 -2
- data/lib/samagotchi/web/public/stage_slots.js +130 -0
- data/lib/samagotchi/web/public/stage_view.js +56 -335
- data/lib/samagotchi/web/public/stage_watch.js +65 -0
- data/lib/samagotchi/web/public/strip.js +3 -3
- data/lib/samagotchi/web/public/task_stop.js +3 -2
- data/lib/samagotchi/web/public/turn_events.js +1 -1
- data/lib/samagotchi/web/public/turn_html.js +2 -2
- data/lib/samagotchi/web/public/turn_model.js +2 -2
- data/lib/samagotchi/web/public/turn_view.js +1 -1
- data/lib/samagotchi/web/public/vendor/directive-helpers.js +8 -0
- data/lib/samagotchi/web/public/vendor/directives/repeat.js +9 -0
- data/lib/samagotchi/web/server.rb +0 -4
- data/lib/samagotchi/worker.rb +22 -24
- data/lib/samagotchi/worker_inbound.rb +36 -4
- data/lib/samagotchi/worker_wakes.rb +67 -7
- metadata +27 -1
data/lib/samagotchi/web/app.rb
CHANGED
|
@@ -26,8 +26,8 @@ require_relative "../host_registry"
|
|
|
26
26
|
require_relative "../llm_context_override"
|
|
27
27
|
require_relative "../model_catalog"
|
|
28
28
|
require_relative "../model_profile"
|
|
29
|
+
require_relative "../session_setting"
|
|
29
30
|
require_relative "../thinking"
|
|
30
|
-
require_relative "../reasoning_history"
|
|
31
31
|
require_relative "../sampling_settings"
|
|
32
32
|
require_relative "../served_model"
|
|
33
33
|
require_relative "../project_scope"
|
|
@@ -557,52 +557,6 @@ module Samagotchi
|
|
|
557
557
|
nil
|
|
558
558
|
end
|
|
559
559
|
|
|
560
|
-
# A session's LLM context strategy, apply rule and budget, each with
|
|
561
|
-
# where it came from (LLMContextStrategy::Explained#summary), as its
|
|
562
|
-
# worker would resolve them; nil when they can't be.
|
|
563
|
-
# (LLMContextStrategy.for_session, shared with the ctx % the session
|
|
564
|
-
# lists count.)
|
|
565
|
-
def llm_context_for(session)
|
|
566
|
-
LLMContextStrategy.for_session(session, registry: host_registry)
|
|
567
|
-
end
|
|
568
|
-
|
|
569
|
-
# A session's toolkit as its file names it ({name:, label:}), nil on
|
|
570
|
-
# full: without a worker nothing resolves it (a bundle's kits load in
|
|
571
|
-
# the worker), so the tools it leaves out aren't known here.
|
|
572
|
-
def toolkit_for(session)
|
|
573
|
-
toolkit = session.toolkit
|
|
574
|
-
toolkit && !toolkit.full? ? { name: toolkit.name, label: toolkit.label } : nil
|
|
575
|
-
end
|
|
576
|
-
|
|
577
|
-
# A session's thinking level with where it came from and its own
|
|
578
|
-
# (SettingExplained#summary), as its worker would resolve it; nil
|
|
579
|
-
# when it can't be. Config and the registry only: no network.
|
|
580
|
-
def thinking_for(session)
|
|
581
|
-
registry = host_registry
|
|
582
|
-
name = session.model_name.to_s.strip.empty? ? ModelProfile.required_model_name(nil) : session.model_name
|
|
583
|
-
target = registry.resolve(name)
|
|
584
|
-
names = registry.lookup_names(session.model_typed || name, resolved: name, target: target)
|
|
585
|
-
Thinking.explain(target, names: names, session: session.thinking).summary
|
|
586
|
-
rescue StandardError
|
|
587
|
-
nil
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
# A session's reasoning_history level with where it came from and its
|
|
591
|
-
# own (SettingExplained#summary), as its worker would resolve it; nil
|
|
592
|
-
# on a native host (it ignores the level) or when it can't be. Config
|
|
593
|
-
# and the registry only: no network.
|
|
594
|
-
def reasoning_history_for(session)
|
|
595
|
-
registry = host_registry
|
|
596
|
-
name = session.model_name.to_s.strip.empty? ? ModelProfile.required_model_name(nil) : session.model_name
|
|
597
|
-
target = registry.resolve(name)
|
|
598
|
-
return nil unless target.entry&.chat?
|
|
599
|
-
|
|
600
|
-
names = registry.lookup_names(session.model_typed || name, resolved: name, target: target)
|
|
601
|
-
ReasoningHistory.explain(target, names: names, session: session.reasoning_history).summary
|
|
602
|
-
rescue StandardError
|
|
603
|
-
nil
|
|
604
|
-
end
|
|
605
|
-
|
|
606
560
|
# Built again when config.yml's hosts: changed (compared as read: the
|
|
607
561
|
# YAML read is mtime-cached), so the model picker lists a host added
|
|
608
562
|
# while chi web runs. An injected registry (specs) stays.
|
|
@@ -719,9 +673,13 @@ module Samagotchi
|
|
|
719
673
|
|
|
720
674
|
# POST /api/sessions with continues: <id, prefix or last:id>: the next
|
|
721
675
|
# link of that session's chain (SessionManager.continue_session), in its
|
|
722
|
-
# folder
|
|
723
|
-
#
|
|
724
|
-
#
|
|
676
|
+
# folder and with its own setup (SessionSetup: llm_context, thinking
|
|
677
|
+
# and reasoning_history levels), so dir, llm_context and thinking are
|
|
678
|
+
# refused beside it (a create takes no reasoning_history). It runs on
|
|
679
|
+
# model (or model_name) when given, else on the previous link's: one
|
|
680
|
+
# whose host is gone falls back to the default, and the 201's
|
|
681
|
+
# model_warning (null when none) says so. A model it can't start on is
|
|
682
|
+
# 400 invalid_model, nothing archived.
|
|
725
683
|
# 409 continued (with next_id, the link that continues it already: the
|
|
726
684
|
# page opens that one), open_children (ids), folder_gone, busy, scratch
|
|
727
685
|
# or owned_by_tui.
|
|
@@ -730,14 +688,16 @@ module Samagotchi
|
|
|
730
688
|
unless given.is_a?(String) && !given.strip.empty?
|
|
731
689
|
return error_response(400, "invalid_continues", "continues is a session id")
|
|
732
690
|
end
|
|
733
|
-
if (fields = %w[dir
|
|
691
|
+
if (fields = %w[dir llm_context thinking].select { |key| body.key?(key) }).any?
|
|
734
692
|
return error_response(400, "invalid_continues",
|
|
735
|
-
"continues takes the previous session's folder,
|
|
693
|
+
"continues takes the previous session's folder, llm_context and thinking; " \
|
|
736
694
|
"leave out #{fields.join(", ")}")
|
|
737
695
|
end
|
|
738
696
|
|
|
739
697
|
preview = idle ? body["preview"].to_s.strip : ""
|
|
740
|
-
|
|
698
|
+
model = (body["model"] || body["model_name"]).to_s.strip
|
|
699
|
+
session, error = continue_from(given, prompt: idle ? nil : prompt.to_s, title: preview.empty? ? nil : preview,
|
|
700
|
+
model: model.empty? ? nil : model)
|
|
741
701
|
return error if error
|
|
742
702
|
|
|
743
703
|
port = await_bridge_port(session.id)
|
|
@@ -745,14 +705,15 @@ module Samagotchi
|
|
|
745
705
|
# The moved delegates' cards fold under the new link now.
|
|
746
706
|
[session.continues, session.id, *moved].each { |sid| @hub&.touch(sid) }
|
|
747
707
|
record_history(body, prompt) if !idle && PromptHistory.shell_line?(prompt.to_s.strip)
|
|
748
|
-
json_response(201, session_to_json(session).merge(bridge_port: port, moved: moved
|
|
708
|
+
json_response(201, session_to_json(session).merge(bridge_port: port, moved: moved,
|
|
709
|
+
model_warning: session.model_warning))
|
|
749
710
|
end
|
|
750
711
|
|
|
751
712
|
# SessionManager.continue_session, its refusals as responses: [session,
|
|
752
713
|
# nil] or [nil, the response]. Only its own errors: what fails after
|
|
753
714
|
# the new link started is not a refusal of it.
|
|
754
|
-
def continue_from(given, prompt:, title:)
|
|
755
|
-
[@manager.continue_session(given, prompt: prompt, title: title, state_dir: @state_dir), nil]
|
|
715
|
+
def continue_from(given, prompt:, title:, model:)
|
|
716
|
+
[@manager.continue_session(given, prompt: prompt, title: title, model: model, state_dir: @state_dir), nil]
|
|
756
717
|
rescue SessionContinue::Refused => e
|
|
757
718
|
extra = e.reason == :continued ? { next_id: e.ids.first } : { ids: e.ids }
|
|
758
719
|
[nil, json_response(409, { error: e.reason.to_s, detail: e.message, **extra })]
|
|
@@ -821,46 +782,15 @@ module Samagotchi
|
|
|
821
782
|
live = bridge_get_json(id, "snapshot")
|
|
822
783
|
turn_snapshot = live && live["snapshot"]
|
|
823
784
|
snapshot = live && live["session_state_snapshot"]
|
|
824
|
-
last_event_seq = snapshot
|
|
785
|
+
last_event_seq = live_or_saved(snapshot, "event_seq") { bridge_event_seq(id) }
|
|
825
786
|
current_turn = turn_snapshot && turn_snapshot["current_turn"]
|
|
826
787
|
owner = session_owner(id)
|
|
827
|
-
|
|
828
|
-
# dead worker saved can't be answered (its turn is gone), nor a chi
|
|
829
|
-
# REPL's (it shares nothing with the web).
|
|
830
|
-
# A question between turns (the step-limit one) is the snapshot's own.
|
|
831
|
-
pending = if turn_snapshot
|
|
832
|
-
current_turn ? current_turn["pending_question"] : turn_snapshot["pending_question"]
|
|
833
|
-
elsif owner&.worker?
|
|
834
|
-
session.pending_question
|
|
835
|
-
end
|
|
836
|
-
# A /model in the worker changes it before the file catches up.
|
|
837
|
-
session.model_name = snapshot["model_name"] if snapshot && !snapshot["model_name"].to_s.empty?
|
|
838
|
-
session_json = session_to_json(session, status: displayed_status(session, snapshot, owner: owner), owner: owner)
|
|
788
|
+
pending = shown_pending_question(session, turn_snapshot, owner)
|
|
839
789
|
# What the worker's server said it served for that model (after a turn);
|
|
840
790
|
# without a worker, what the session's analytics.json last saved.
|
|
841
791
|
persisted = snapshot ? nil : read_analytics(id)
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
served_expected_by: snapshot["served_expected_by"])
|
|
845
|
-
# The model notes its prompt carries now (a /model there changes them).
|
|
846
|
-
session_json = session_json.merge(prompt_notes: snapshot["prompt_notes"]) if snapshot["prompt_notes"].is_a?(Array)
|
|
847
|
-
else
|
|
848
|
-
session_json = session_json.merge(saved_served_model(session, persisted))
|
|
849
|
-
end
|
|
850
|
-
# The LLM context strategy the next turn runs under (the info bar's
|
|
851
|
-
# chip): the worker's, else worked out here from the session file.
|
|
852
|
-
session_json = session_json.merge(llm_context: (snapshot && snapshot["llm_context"]) || llm_context_for(session))
|
|
853
|
-
# The thinking level the next turn runs at (the think chip), the same way.
|
|
854
|
-
session_json = session_json.merge(thinking: (snapshot && snapshot["thinking"]) || thinking_for(session))
|
|
855
|
-
# Its reasoning history (the reasoning chip): the worker's (nil on a
|
|
856
|
-
# native host), else the server's reading (an older worker, none).
|
|
857
|
-
history = snapshot&.key?("reasoning_history") ? snapshot["reasoning_history"] : reasoning_history_for(session)
|
|
858
|
-
session_json = session_json.merge(reasoning_history: history)
|
|
859
|
-
# Its toolkit when it isn't full (the tools chip): the worker's, which
|
|
860
|
-
# resolved it, else the name saved in the session file.
|
|
861
|
-
toolkit = snapshot&.key?("toolkit") ? snapshot["toolkit"] : toolkit_for(session)
|
|
862
|
-
session_json = session_json.merge(toolkit: toolkit)
|
|
863
|
-
raw_messages = turn_snapshot ? turn_snapshot["messages"] : session.messages
|
|
792
|
+
session_json = shown_session_json(session, snapshot, owner, persisted)
|
|
793
|
+
raw_messages = live_or_saved(turn_snapshot, "messages") { session.messages }
|
|
864
794
|
timing = timing_payload(id, live_metrics: snapshot && snapshot["metrics"], persisted: persisted)
|
|
865
795
|
json_response(200, {
|
|
866
796
|
session: session_json,
|
|
@@ -879,7 +809,7 @@ module Samagotchi
|
|
|
879
809
|
plugin_warning: turn_snapshot && turn_snapshot["plugin_warning"],
|
|
880
810
|
# Plugins' slow setup still running (chi.init): the page's init row.
|
|
881
811
|
init_tasks: turn_snapshot ? Array(turn_snapshot["init_tasks"]) : [],
|
|
882
|
-
cards: cards_for_display(turn_snapshot
|
|
812
|
+
cards: cards_for_display(live_or_saved(turn_snapshot, "cards") { saved_cards(id) }),
|
|
883
813
|
# The composer's / autocomplete; the built-ins until a worker
|
|
884
814
|
# names its plugins' too.
|
|
885
815
|
commands: turn_snapshot&.fetch("commands", nil) || SessionCommands.builtin_registry.listing,
|
|
@@ -896,6 +826,54 @@ module Samagotchi
|
|
|
896
826
|
error_response(404, "not_found", e.message)
|
|
897
827
|
end
|
|
898
828
|
|
|
829
|
+
# A live worker's +key+ as its reading (+live+: a snapshot, a tail)
|
|
830
|
+
# says it, nil included; without a worker (+live+ nil) the block's,
|
|
831
|
+
# the server's own reading of the saved session. A nil value from a
|
|
832
|
+
# worker is not a missing one (contrast SessionSetting#snapshot_value's
|
|
833
|
+
# :or).
|
|
834
|
+
def live_or_saved(live, key)
|
|
835
|
+
live ? live[key] : yield
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# The file's question only while a worker owns the session: one a
|
|
839
|
+
# dead worker saved can't be answered (its turn is gone), nor a chi
|
|
840
|
+
# REPL's (it shares nothing with the web).
|
|
841
|
+
# A question between turns (the step-limit one) is the snapshot's own.
|
|
842
|
+
def shown_pending_question(session, turn_snapshot, owner)
|
|
843
|
+
if turn_snapshot
|
|
844
|
+
current_turn = turn_snapshot["current_turn"]
|
|
845
|
+
current_turn ? current_turn["pending_question"] : turn_snapshot["pending_question"]
|
|
846
|
+
elsif owner&.worker?
|
|
847
|
+
session.pending_question
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
# The full show's session part: the file's, with what a live worker's
|
|
852
|
+
# state +snapshot+ knows first (its model, status, served model,
|
|
853
|
+
# prompt notes and chips); without one, the served model from
|
|
854
|
+
# +persisted+ (analytics.json). Sets a worker's model on +session+:
|
|
855
|
+
# a /model in the worker changes it before the file catches up.
|
|
856
|
+
def shown_session_json(session, snapshot, owner, persisted)
|
|
857
|
+
session.model_name = snapshot["model_name"] if snapshot && !snapshot["model_name"].to_s.empty?
|
|
858
|
+
session_json = session_to_json(session, status: displayed_status(session, snapshot, owner: owner), owner: owner)
|
|
859
|
+
if snapshot
|
|
860
|
+
session_json = session_json.merge(served_model: snapshot["served_model"], served_model_for: snapshot["served_model_for"],
|
|
861
|
+
served_expected_by: snapshot["served_expected_by"])
|
|
862
|
+
# The model notes its prompt carries now (a /model there changes them).
|
|
863
|
+
session_json = session_json.merge(prompt_notes: snapshot["prompt_notes"]) if snapshot["prompt_notes"].is_a?(Array)
|
|
864
|
+
else
|
|
865
|
+
session_json = session_json.merge(saved_served_model(session, persisted))
|
|
866
|
+
end
|
|
867
|
+
# The per-session settings' chips (the info bar's llm ctx, think,
|
|
868
|
+
# reasoning and tools): the worker's, else worked out here from the
|
|
869
|
+
# session file (SessionSetting#snapshot_value, #saved_summary).
|
|
870
|
+
SessionSetting::ALL.each do |setting|
|
|
871
|
+
chip = setting.snapshot_value(snapshot) { setting.saved_summary(session, registry: host_registry) }
|
|
872
|
+
session_json = session_json.merge(setting.key => chip)
|
|
873
|
+
end
|
|
874
|
+
session_json
|
|
875
|
+
end
|
|
876
|
+
|
|
899
877
|
# ?cards=1: the page's re-read when a card arrives, for its rendered
|
|
900
878
|
# body (the stream carries the card's plain text only). The worker's
|
|
901
879
|
# cards from its light GET tail, else the saved ones; the session file
|
|
@@ -904,7 +882,7 @@ module Samagotchi
|
|
|
904
882
|
return error_response(404, "not_found", "Session not found: #{id}") unless session_exists?(id)
|
|
905
883
|
|
|
906
884
|
tail = live_tail(id)
|
|
907
|
-
json_response(200, { cards: cards_for_display(tail
|
|
885
|
+
json_response(200, { cards: cards_for_display(live_or_saved(tail, "cards") { saved_cards(id) }) })
|
|
908
886
|
end
|
|
909
887
|
|
|
910
888
|
# ?tail=1: the page's re-read at the end of a turn wants the final
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Samagotchi
|
|
4
4
|
module Web
|
|
5
5
|
# What this web page renders that a notice may stand in for
|
|
6
|
-
# (
|
|
6
|
+
# (HookRoutes#notify's fallback_for:), and what this viewer can do. The
|
|
7
7
|
# session and tail JSON carry it as +capabilities+; the page leaves out
|
|
8
8
|
# a notice whose fallback_for names a capability that is true here.
|
|
9
9
|
# +display+: the answer's display is rendered as markdown, so its links
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// state from :tool_call_started / :tool_call_completed events. Keeping the
|
|
7
7
|
// state pure makes it unit-testable under `node --test` like data.js/format.js.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const ROW_STATUS = Object.freeze({
|
|
10
10
|
RUNNING: "running",
|
|
11
11
|
OK: "ok",
|
|
12
12
|
ERROR: "error",
|
|
@@ -161,7 +161,7 @@ function callDuration(row, event, now) {
|
|
|
161
161
|
|
|
162
162
|
// A completed call's row status from the server's: error, stopped,
|
|
163
163
|
// blocked, else ok.
|
|
164
|
-
|
|
164
|
+
function completedStatus(status) {
|
|
165
165
|
if (status === "error") return ROW_STATUS.ERROR;
|
|
166
166
|
if (status === "stopped") return ROW_STATUS.STOPPED;
|
|
167
167
|
if (status === "blocked") return ROW_STATUS.BLOCKED;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { quoteBlock } from "./annotations.js";
|
|
16
16
|
|
|
17
17
|
// How many plugin pills the bar shows; the rest go behind "⋯".
|
|
18
|
-
|
|
18
|
+
const INLINE_ACTIONS = 3;
|
|
19
19
|
|
|
20
20
|
// The session's commands' annotations, flattened in listing order: {label,
|
|
21
21
|
// line, template, send, command, anytime, source}. anytime: the command
|