samagotchi 0.155.0 → 0.157.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 +57 -1
- data/bin/chi +2 -1
- data/docs/architecture.md +22 -13
- data/docs/bundles.md +3 -1
- data/docs/cli.md +9 -8
- data/docs/configuration.md +36 -4
- data/docs/guardrails.md +9 -5
- data/docs/hooks.md +11 -5
- data/docs/internals/anthropic.md +1 -1
- data/docs/internals/attached-context.md +3 -2
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/bridge.md +83 -6
- data/docs/internals/bundles.md +28 -18
- data/docs/internals/context-telemetry.md +4 -3
- data/docs/internals/engine.md +641 -937
- data/docs/internals/llm-context-forget.md +8 -3
- data/docs/internals/objectives.md +2 -2
- data/docs/internals/participants.md +3 -2
- data/docs/internals/prompt-caching.md +4 -1
- data/docs/internals/remote-sessions.md +8 -3
- data/docs/internals/web-frontend.md +3 -2
- data/docs/internals/working-set.md +2 -2
- data/docs/memory.md +13 -2
- data/docs/plugins.md +35 -2
- data/docs/sessions.md +11 -11
- data/docs/sub-agent.md +17 -2
- data/docs/system-one.md +3 -2
- data/docs/testing.md +53 -2
- data/lib/samagotchi/answer_command.rb +2 -1
- data/lib/samagotchi/archive_store.rb +4 -3
- data/lib/samagotchi/bootstrap/config_writer.rb +1 -1
- data/lib/samagotchi/bootstrap_command.rb +3 -2
- data/lib/samagotchi/bridge/card_places.rb +10 -4
- data/lib/samagotchi/bridge/card_store.rb +3 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +109 -9
- data/lib/samagotchi/bridge/turn_drawings.rb +265 -0
- data/lib/samagotchi/bridge.rb +31 -6
- data/lib/samagotchi/broadcast/recipients.rb +8 -8
- data/lib/samagotchi/broadcast/triage_model.rb +1 -1
- data/lib/samagotchi/broadcast_command.rb +3 -3
- data/lib/samagotchi/bundle_command/check.rb +18 -0
- data/lib/samagotchi/bundle_command/try/sandbox.rb +2 -2
- data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +5 -2
- data/lib/samagotchi/bundles/known-names/manifest.yml +4 -7
- data/lib/samagotchi/bundles/known-names/{hooks/known_names.rb → plugin.rb} +17 -0
- data/lib/samagotchi/bundles/source-links/manifest.yml +4 -7
- data/lib/samagotchi/bundles/source-links/{hooks/source_links.rb → plugin.rb} +23 -4
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/child_move.rb +13 -57
- data/lib/samagotchi/child_reports.rb +2 -2
- data/lib/samagotchi/children_status.rb +15 -21
- data/lib/samagotchi/cli/parent_wait.rb +9 -23
- data/lib/samagotchi/config.rb +20 -112
- data/lib/samagotchi/context_command.rb +1 -2
- data/lib/samagotchi/context_sources.rb +5 -4
- data/lib/samagotchi/context_window.rb +9 -12
- data/lib/samagotchi/desktop/macos.rb +1 -1
- data/lib/samagotchi/effective_model.rb +57 -22
- data/lib/samagotchi/engine/context_judge.rb +19 -11
- data/lib/samagotchi/engine/messages.rb +1 -1
- data/lib/samagotchi/engine/models.rb +5 -2
- data/lib/samagotchi/engine/objectives.rb +29 -11
- data/lib/samagotchi/engine/plugins.rb +1 -1
- data/lib/samagotchi/engine/recap.rb +8 -4
- data/lib/samagotchi/engine/side_requests.rb +21 -11
- data/lib/samagotchi/engine/toolkit.rb +10 -9
- data/lib/samagotchi/engine/turn_end.rb +1 -1
- data/lib/samagotchi/engine/turn_helpers.rb +7 -7
- data/lib/samagotchi/engine/verify.rb +1 -1
- data/lib/samagotchi/engine.rb +43 -12
- data/lib/samagotchi/events.rb +3 -2
- data/lib/samagotchi/extension_load.rb +6 -3
- data/lib/samagotchi/guardrails/targets.rb +24 -9
- data/lib/samagotchi/guardrails_command.rb +1 -1
- data/lib/samagotchi/host_address.rb +52 -0
- data/lib/samagotchi/host_config.rb +72 -1
- data/lib/samagotchi/host_model.rb +2 -6
- data/lib/samagotchi/host_registry.rb +58 -46
- data/lib/samagotchi/kernel_loop.rb +53 -124
- data/lib/samagotchi/llm/anthropic_wire.rb +2 -2
- data/lib/samagotchi/llm/chat_loop.rb +40 -21
- data/lib/samagotchi/llm/native_backend.rb +1 -1
- data/lib/samagotchi/llm_context_apply.rb +54 -1
- data/lib/samagotchi/llm_context_notice.rb +1 -1
- data/lib/samagotchi/llm_context_strategy.rb +29 -67
- data/lib/samagotchi/llm_context_view.rb +9 -0
- data/lib/samagotchi/memory_bundle/asset_installer.rb +71 -76
- data/lib/samagotchi/memory_bundle/builder.rb +2 -1
- data/lib/samagotchi/memory_bundle/file_actions.rb +193 -0
- data/lib/samagotchi/memory_bundle/file_digest.rb +13 -0
- data/lib/samagotchi/memory_bundle/file_state.rb +152 -0
- data/lib/samagotchi/memory_bundle/installed_copy.rb +2 -1
- data/lib/samagotchi/memory_bundle/installed_manifest.rb +8 -1
- data/lib/samagotchi/memory_bundle/installer.rb +185 -169
- data/lib/samagotchi/memory_bundle/manifest.rb +14 -4
- data/lib/samagotchi/memory_bundle/merger.rb +69 -65
- data/lib/samagotchi/memory_bundle/plugin_ref.rb +6 -5
- data/lib/samagotchi/memory_bundle/profile.rb +5 -5
- data/lib/samagotchi/memory_bundle/provenance.rb +15 -1
- data/lib/samagotchi/memory_bundle/remerge.rb +25 -12
- data/lib/samagotchi/memory_bundle/shipped_update.rb +51 -6
- data/lib/samagotchi/memory_bundle/status.rb +14 -20
- data/lib/samagotchi/memory_bundle/system_bundle.rb +24 -6
- data/lib/samagotchi/memory_bundle/trash.rb +23 -8
- data/lib/samagotchi/memory_bundle/uninstaller.rb +16 -11
- data/lib/samagotchi/memory_usage.rb +1 -1
- data/lib/samagotchi/model_lookup.rb +75 -0
- data/lib/samagotchi/model_price.rb +4 -10
- data/lib/samagotchi/model_profile.rb +7 -6
- data/lib/samagotchi/model_settings.rb +37 -2
- data/lib/samagotchi/models_command.rb +1 -1
- data/lib/samagotchi/note_command.rb +2 -3
- data/lib/samagotchi/objectives_audit.rb +4 -3
- data/lib/samagotchi/participant_mode.rb +2 -1
- data/lib/samagotchi/participants.rb +2 -2
- data/lib/samagotchi/plugin/api.rb +41 -6
- data/lib/samagotchi/plugin/context.rb +1 -1
- data/lib/samagotchi/plugin/loader.rb +33 -10
- data/lib/samagotchi/plugin/sessions.rb +3 -6
- data/lib/samagotchi/plugin/tool_spec.rb +5 -3
- data/lib/samagotchi/project_env.rb +1 -1
- data/lib/samagotchi/reasoning_history.rb +14 -20
- data/lib/samagotchi/recap_place.rb +27 -0
- data/lib/samagotchi/reminder_carry.rb +1 -1
- data/lib/samagotchi/reply_wait/liveness.rb +38 -0
- data/lib/samagotchi/reply_wait/poll.rb +44 -15
- data/lib/samagotchi/reply_wait/target.rb +113 -0
- data/lib/samagotchi/reply_wait.rb +22 -2
- data/lib/samagotchi/reprefill_meter.rb +45 -0
- data/lib/samagotchi/review_command.rb +1 -1
- data/lib/samagotchi/sampling_settings.rb +9 -16
- data/lib/samagotchi/self_report.rb +36 -37
- data/lib/samagotchi/send_command.rb +17 -23
- data/lib/samagotchi/session.rb +370 -445
- data/lib/samagotchi/session_access/local.rb +12 -11
- data/lib/samagotchi/session_access/remote.rb +22 -6
- data/lib/samagotchi/session_archive_command.rb +2 -2
- data/lib/samagotchi/session_chain.rb +7 -6
- data/lib/samagotchi/session_continue.rb +6 -5
- data/lib/samagotchi/session_delete_command.rb +1 -1
- data/lib/samagotchi/session_field.rb +133 -0
- data/lib/samagotchi/session_inbox.rb +49 -1
- data/lib/samagotchi/session_manager/delivery.rb +254 -0
- data/lib/samagotchi/session_manager/emptiness.rb +139 -0
- data/lib/samagotchi/session_manager/lifecycle.rb +274 -0
- data/lib/samagotchi/session_manager/listing.rb +118 -0
- data/lib/samagotchi/session_manager/removal.rb +190 -0
- data/lib/samagotchi/session_manager/spawn.rb +242 -0
- data/lib/samagotchi/session_manager/worker_command.rb +157 -0
- data/lib/samagotchi/session_manager.rb +28 -1541
- data/lib/samagotchi/session_metrics.rb +8 -5
- data/lib/samagotchi/session_paths.rb +57 -0
- data/lib/samagotchi/session_retention.rb +2 -2
- data/lib/samagotchi/session_row.rb +200 -0
- data/lib/samagotchi/session_setup.rb +15 -12
- data/lib/samagotchi/sessions_command.rb +11 -129
- data/lib/samagotchi/share_command.rb +1 -1
- data/lib/samagotchi/starting_marker.rb +65 -0
- data/lib/samagotchi/steer_cut_after.rb +7 -14
- data/lib/samagotchi/system_one/recorder.rb +49 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +6 -26
- data/lib/samagotchi/terminal_ui/exit_lines.rb +1 -1
- data/lib/samagotchi/terminal_ui/join_replay.rb +86 -8
- data/lib/samagotchi/terminal_ui/local_link.rb +2 -2
- data/lib/samagotchi/terminal_ui.rb +5 -7
- data/lib/samagotchi/testing/install.rb +2 -1
- data/lib/samagotchi/testing/sessions.rb +1 -1
- data/lib/samagotchi/thinking.rb +16 -21
- data/lib/samagotchi/token_usage.rb +1 -1
- data/lib/samagotchi/tool_activity.rb +10 -4
- data/lib/samagotchi/tool_declarations.rb +2 -2
- data/lib/samagotchi/tool_output_cut.rb +17 -0
- data/lib/samagotchi/tool_response.rb +5 -3
- data/lib/samagotchi/tool_runner.rb +25 -5
- data/lib/samagotchi/toolkit_command.rb +1 -1
- data/lib/samagotchi/tools/delegate.rb +55 -45
- data/lib/samagotchi/tools/delegate_relay.rb +5 -5
- data/lib/samagotchi/tools/delegate_result.rb +5 -5
- data/lib/samagotchi/tools/delegate_wait.rb +41 -17
- data/lib/samagotchi/tools/index_hint.rb +14 -0
- data/lib/samagotchi/tools/list_sessions.rb +7 -7
- data/lib/samagotchi/tools/registry.rb +28 -11
- data/lib/samagotchi/tools/send_note.rb +4 -6
- data/lib/samagotchi/tools/tool_index.rb +52 -51
- data/lib/samagotchi/tools/tool_surface.rb +22 -8
- data/lib/samagotchi/turn_context.rb +2 -1
- data/lib/samagotchi/turn_stages/context_judge.rb +38 -36
- data/lib/samagotchi/turn_stages/objectives.rb +56 -52
- data/lib/samagotchi/turn_stages/toolkit_suggestion.rb +13 -17
- data/lib/samagotchi/turn_stages/verify_anchors.rb +9 -10
- data/lib/samagotchi/turn_stages/warmup.rb +14 -15
- data/lib/samagotchi/update_command.rb +11 -7
- data/lib/samagotchi/verify_fresh.rb +7 -11
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +11 -14
- data/lib/samagotchi/web/app.rb +45 -28
- data/lib/samagotchi/web/message_display.rb +57 -7
- data/lib/samagotchi/web/participant_access.rb +3 -3
- data/lib/samagotchi/web/public/activity.js +16 -7
- data/lib/samagotchi/web/public/app.js +29 -8
- data/lib/samagotchi/web/public/bubbles.js +8 -3
- data/lib/samagotchi/web/public/cards_place.js +15 -0
- data/lib/samagotchi/web/public/commands.js +5 -1
- data/lib/samagotchi/web/public/data.js +4 -2
- data/lib/samagotchi/web/public/history_shape.js +220 -0
- data/lib/samagotchi/web/public/recap_stamp.js +17 -0
- data/lib/samagotchi/web/public/reload_check.js +71 -0
- data/lib/samagotchi/web/public/stage_model.js +7 -5
- data/lib/samagotchi/web/public/stage_view.js +7 -3
- data/lib/samagotchi/web/public/stream_handlers.js +8 -2
- data/lib/samagotchi/web/public/timing.js +43 -2
- data/lib/samagotchi/web/public/turn_block.js +215 -30
- data/lib/samagotchi/web/public/turn_drawing.js +43 -0
- data/lib/samagotchi/web/public/turn_events.js +10 -1
- data/lib/samagotchi/web/public/turn_pieces.js +29 -0
- data/lib/samagotchi/web/reload_check_route.rb +95 -0
- data/lib/samagotchi/web/server.rb +4 -2
- data/lib/samagotchi/web/session_access_routes.rb +6 -6
- data/lib/samagotchi/web/session_control.rb +4 -4
- data/lib/samagotchi/web/session_create.rb +3 -3
- data/lib/samagotchi/web/session_hub.rb +9 -17
- data/lib/samagotchi/web/session_show.rb +67 -5
- data/lib/samagotchi/web/session_summary.rb +39 -28
- data/lib/samagotchi/worker.rb +83 -32
- data/lib/samagotchi/worker_boot.rb +184 -0
- data/lib/samagotchi/worker_link.rb +3 -0
- data/lib/samagotchi/worker_signals.rb +1 -1
- data/lib/samagotchi/worker_task_wakes.rb +2 -2
- metadata +32 -4
- data/lib/samagotchi/send_command/wait_target.rb +0 -85
|
@@ -14,78 +14,62 @@ module Samagotchi
|
|
|
14
14
|
# audit of what comes in at a turn's start and what goes out at its end
|
|
15
15
|
# (ObjectivesAudit), the view announced when it changed, and the model's
|
|
16
16
|
# proposals kept as cards until the turn is over. Its state is those
|
|
17
|
-
# proposals under their mutex
|
|
17
|
+
# proposals under their mutex and the last view announced (under the
|
|
18
|
+
# event lock); the audit is built per call.
|
|
18
19
|
class Objectives
|
|
19
20
|
# @param side_judgments [SideJudgments] the Engine's side asks (the audit's kinds)
|
|
20
21
|
# @param host_registry [HostRegistry] System 1's client resolves its host here
|
|
21
|
-
|
|
22
|
-
# @param recording [#call] (client, judgment, session, mode:) → the client saving its runs
|
|
23
|
-
# @param runs [#call] the Engine's SystemOne::Runs
|
|
24
|
-
# @param synchronize [#call] (&block) runs the block under the event lock (Engine#synchronize_events)
|
|
25
|
-
# @param announce [#call] (event) tells the observers (Engine#announce)
|
|
26
|
-
# @param running [#call] the running turn's conversation, nil between turns (Engine#live_conversation)
|
|
27
|
-
# @param show_card [#call] (**card) shows a card (Engine#show_card)
|
|
28
|
-
def initialize(side_judgments:, host_registry:, state_dir:, recording:, runs:, synchronize:, announce:, running:, show_card:)
|
|
22
|
+
def initialize(side_judgments:, host_registry:)
|
|
29
23
|
@side_judgments = side_judgments
|
|
30
|
-
@
|
|
31
|
-
@synchronize = synchronize
|
|
32
|
-
@announce = announce
|
|
33
|
-
@running = running
|
|
34
|
-
@show_card = show_card
|
|
24
|
+
@host_registry = host_registry
|
|
35
25
|
# The last view announced (#announce), under the event lock.
|
|
36
26
|
@announced = nil
|
|
37
27
|
# The model's proposals until the turn is over (#finally).
|
|
38
28
|
@proposals = []
|
|
39
29
|
@proposals_mutex = Mutex.new
|
|
40
|
-
# With saving off nothing would come of an ask, so none is made.
|
|
41
|
-
@audit = ObjectivesAudit.new(
|
|
42
|
-
side: side_judgments, state_dir: state_dir,
|
|
43
|
-
client_for: lambda do |judgment, session|
|
|
44
|
-
next nil unless runs.call.on?
|
|
45
|
-
|
|
46
|
-
recording.call(SystemOne::Client.from_config(host_registry: host_registry, mode: ObjectivesAudit::MODE, tag: :system_one),
|
|
47
|
-
judgment, session, mode: nil)
|
|
48
|
-
end
|
|
49
|
-
)
|
|
50
30
|
end
|
|
51
31
|
|
|
52
32
|
# System 1 on the objectives, in audit (ObjectivesAudit), at a turn's
|
|
53
33
|
# start: the owner's turns only, and for a message a person's: a
|
|
54
34
|
# reminder's or a delegate's prompt isn't one.
|
|
55
|
-
|
|
35
|
+
# @param recorder [SystemOne::Recorder] the audit's runs (built for this call)
|
|
36
|
+
def incoming(turn, recorder:)
|
|
56
37
|
return unless turn.prompt.is_a?(String) && turn.origin.human? && OwnerPrompt.turn?(session: turn.session, author: turn.origin.author)
|
|
57
38
|
|
|
58
|
-
|
|
39
|
+
audit(turn.context.state_dir, recorder).incoming(turn.session, turn.prompt)
|
|
59
40
|
end
|
|
60
41
|
|
|
61
42
|
# The turn's answer, after the end event and before :after_turn (the
|
|
62
43
|
# caller skips a canceled or exhausted turn): the owner's turns only.
|
|
63
|
-
|
|
44
|
+
# @param recorder [SystemOne::Recorder] the audit's runs (built for this
|
|
45
|
+
# call: after a before_turn hook's model switch, the new model)
|
|
46
|
+
def outgoing(turn, result, recorder:)
|
|
64
47
|
return unless OwnerPrompt.turn?(session: turn.session, author: turn.origin.author)
|
|
65
48
|
|
|
66
|
-
|
|
49
|
+
audit(turn.context.state_dir, recorder).outgoing(turn.session, result.text)
|
|
67
50
|
end
|
|
68
51
|
|
|
69
52
|
# A note added between turns (Engine#add_context_note): incoming too.
|
|
70
|
-
|
|
53
|
+
# @param recorder [SystemOne::Recorder] the audit's runs (built for this call)
|
|
54
|
+
# @param state_dir [String] the sessions dir
|
|
55
|
+
def note(session, note, recorder:, state_dir:) = audit(state_dir, recorder).note(session, note)
|
|
71
56
|
|
|
72
57
|
# The session's objectives as the page draws them (Objectives.view), when
|
|
73
58
|
# that differs from the last announced: after an edit (its file written,
|
|
74
59
|
# pending), once a list note is in, at a turn's end (its moves) and after
|
|
75
|
-
# a rollback. A session with no list announces nothing.
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Log.warn(:turn, "objectives_announce_failed", error: e.class.name, msg: e.message)
|
|
60
|
+
# a rollback. A session with no list announces nothing. The caller
|
|
61
|
+
# (Engine#announce_objectives) holds the event lock, the one guard of
|
|
62
|
+
# the last view, announces the event and logs a failure: it only
|
|
63
|
+
# informs, never fails a turn.
|
|
64
|
+
# @param state_dir [String] the sessions dir
|
|
65
|
+
# @param running [Array<Hash>, nil] the running turn's conversation (Engine#live_conversation), nil between turns
|
|
66
|
+
# @return [Hash, nil] the :objectives_updated event to announce, nil when the view is the same
|
|
67
|
+
def announce(session, state_dir:, running:)
|
|
68
|
+
view = Samagotchi::Objectives.view(session.id, session.messages, state_dir: state_dir, running: running&.dup)
|
|
69
|
+
return if view.nil? || view == @announced
|
|
70
|
+
|
|
71
|
+
@announced = view
|
|
72
|
+
{ type: :objectives_updated, session_id: session.id, **view }
|
|
89
73
|
end
|
|
90
74
|
|
|
91
75
|
# A change the model proposes (the objectives tool), kept until the turn
|
|
@@ -108,9 +92,15 @@ module Samagotchi
|
|
|
108
92
|
# failed or rolled-back turn keeps none, and its proposals are dropped
|
|
109
93
|
# here so the next turn does not show them either: their Apply card
|
|
110
94
|
# would sit after a turn whose `objectives propose` call is not in the
|
|
111
|
-
# history (a rollback after Ctrl-C drops it).
|
|
95
|
+
# history (a rollback after Ctrl-C drops it). The caller
|
|
96
|
+
# (Engine#show_objective_proposals) shows the cards.
|
|
97
|
+
# @return [Array<Hash>] the completed turn's cards (Engine#show_card's
|
|
98
|
+
# arguments); none for any other ending
|
|
112
99
|
def finally(turn)
|
|
113
|
-
turn.completed?
|
|
100
|
+
return proposal_cards if turn.completed?
|
|
101
|
+
|
|
102
|
+
discard_proposals(turn)
|
|
103
|
+
[]
|
|
114
104
|
end
|
|
115
105
|
|
|
116
106
|
# For specs: the audit's asks' thread for +kind+ (ObjectivesAudit's
|
|
@@ -122,15 +112,13 @@ module Samagotchi
|
|
|
122
112
|
|
|
123
113
|
private
|
|
124
114
|
|
|
125
|
-
def
|
|
126
|
-
drain_proposals.
|
|
115
|
+
def proposal_cards
|
|
116
|
+
drain_proposals.map do |proposal|
|
|
127
117
|
id = "objectives-#{SecureRandom.hex(4)}"
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
{ source: Samagotchi::Objectives::SOURCE, id: id, title: proposal.title, body: proposal.body,
|
|
119
|
+
actions: [{ label: "Apply", command: proposal.action_line(id) },
|
|
120
|
+
{ label: "Dismiss", command: "/objectives --card #{id} dismiss" }] }
|
|
131
121
|
end
|
|
132
|
-
rescue StandardError => e
|
|
133
|
-
Log.warn(:turn, "objectives_proposal_failed", error: e.class.name, msg: e.message)
|
|
134
122
|
end
|
|
135
123
|
|
|
136
124
|
def discard_proposals(turn)
|
|
@@ -141,6 +129,22 @@ module Samagotchi
|
|
|
141
129
|
def drain_proposals
|
|
142
130
|
@proposals_mutex.synchronize { @proposals.slice!(0..) }
|
|
143
131
|
end
|
|
132
|
+
|
|
133
|
+
# The audit for one call, over the values it is given: nothing of it
|
|
134
|
+
# is kept. Its client is asked for only once an active objective is
|
|
135
|
+
# there (ObjectivesAudit#ask), and with saving off nothing would come
|
|
136
|
+
# of an ask, so none is made.
|
|
137
|
+
def audit(state_dir, recorder)
|
|
138
|
+
ObjectivesAudit.new(
|
|
139
|
+
side: @side_judgments, state_dir: state_dir,
|
|
140
|
+
client_for: lambda do |judgment, session|
|
|
141
|
+
next nil unless recorder.saving?
|
|
142
|
+
|
|
143
|
+
recorder.wrap(SystemOne::Client.from_config(host_registry: @host_registry, mode: ObjectivesAudit::MODE, tag: :system_one),
|
|
144
|
+
judgment, session, mode: nil)
|
|
145
|
+
end
|
|
146
|
+
)
|
|
147
|
+
end
|
|
144
148
|
end
|
|
145
149
|
end
|
|
146
150
|
end
|
|
@@ -23,17 +23,11 @@ module Samagotchi
|
|
|
23
23
|
# @param host_registry [HostRegistry] System 1's client resolves its host here
|
|
24
24
|
# @param toolkit_binding [ToolkitBinding] the kits, #suggest and #fell_back
|
|
25
25
|
# @param tools [Tools::Registry] the registered tools, for the snapshot
|
|
26
|
-
|
|
27
|
-
# @param recording [#call] (client, judgment, session, mode:) → the client saving its runs
|
|
28
|
-
# @param runs [#call] the Engine's SystemOne::Runs
|
|
29
|
-
def initialize(side_judgments:, host_registry:, toolkit_binding:, tools:, state_dir:, recording:, runs:)
|
|
26
|
+
def initialize(side_judgments:, host_registry:, toolkit_binding:, tools:)
|
|
30
27
|
@side_judgments = side_judgments
|
|
31
28
|
@host_registry = host_registry
|
|
32
29
|
@toolkit_binding = toolkit_binding
|
|
33
30
|
@tools = tools
|
|
34
|
-
@state_dir = state_dir
|
|
35
|
-
@recording = recording
|
|
36
|
-
@runs = runs
|
|
37
31
|
@pending = nil
|
|
38
32
|
@pending_turn = nil
|
|
39
33
|
@warmup_thread = nil
|
|
@@ -52,12 +46,14 @@ module Samagotchi
|
|
|
52
46
|
# snapshot. Audit never blocks: the thread logs its line. On mode
|
|
53
47
|
# keeps the ask's Pending for +turn+, which #settle joins before
|
|
54
48
|
# apply_toolkit!, with the turn's controller as the request's cancel
|
|
55
|
-
# controller.
|
|
49
|
+
# controller. The suggestion log is in the turn's sessions dir
|
|
50
|
+
# (TurnContext#state_dir).
|
|
51
|
+
# @param recorder [SystemOne::Recorder] the ask's runs (built for this call)
|
|
56
52
|
# @return [ToolkitSuggest::Pending, nil] nil unless the turn joins an ask
|
|
57
|
-
def start(turn)
|
|
53
|
+
def start(turn, recorder:)
|
|
58
54
|
@pending = nil
|
|
59
55
|
@pending_turn = turn
|
|
60
|
-
@pending = ask(turn)
|
|
56
|
+
@pending = ask(turn, recorder)
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
# On mode, before apply_toolkit!: wait for +turn+'s ask (#start) up to
|
|
@@ -154,14 +150,14 @@ module Samagotchi
|
|
|
154
150
|
# never reached #start) is never touched.
|
|
155
151
|
def pending_for(turn) = (@pending if @pending_turn.equal?(turn))
|
|
156
152
|
|
|
157
|
-
def ask(turn)
|
|
153
|
+
def ask(turn, recorder)
|
|
158
154
|
mode = Config.get("toolkit.suggest.mode").to_s
|
|
159
155
|
return if mode == "off"
|
|
160
156
|
|
|
161
157
|
joined = mode == "on"
|
|
162
158
|
session = turn.session
|
|
163
159
|
default = Toolkit.of(session.toolkit)
|
|
164
|
-
log = suggestion_log
|
|
160
|
+
log = suggestion_log(turn.context.state_dir)
|
|
165
161
|
# The predicate checks the session first; the kits (memory kits are
|
|
166
162
|
# read from disk) and the log (read whole) only when it gets there.
|
|
167
163
|
kits = nil
|
|
@@ -185,8 +181,8 @@ module Samagotchi
|
|
|
185
181
|
return
|
|
186
182
|
end
|
|
187
183
|
|
|
188
|
-
client =
|
|
189
|
-
|
|
184
|
+
client = recorder.wrap(SystemOne::Client.from_config(host_registry: @host_registry, mode: "on", tag: :toolkit),
|
|
185
|
+
ToolkitSuggest::JUDGMENT, session, mode: Config.get("toolkit.suggest.mode").to_s)
|
|
190
186
|
return unless client
|
|
191
187
|
|
|
192
188
|
# The warm-up's only value is the cache: the real ask never queues
|
|
@@ -215,7 +211,7 @@ module Samagotchi
|
|
|
215
211
|
state: { project: state[:project].dup.freeze, prompt: state[:prompt].dup.freeze }.freeze
|
|
216
212
|
)
|
|
217
213
|
threshold = Config.get("toolkit.suggest.threshold")
|
|
218
|
-
runs = session.scratch ? nil :
|
|
214
|
+
runs = session.scratch ? nil : recorder.runs
|
|
219
215
|
pending = ToolkitSuggest::Pending.new(log: log, joined: joined,
|
|
220
216
|
acted: runs && ->(judged, did) { runs.acted(judged.fields[:run_id], did) },
|
|
221
217
|
timeout_seconds: SystemOne::Client.configured_timeout_ms / 1000.0)
|
|
@@ -253,8 +249,8 @@ module Samagotchi
|
|
|
253
249
|
end
|
|
254
250
|
end
|
|
255
251
|
|
|
256
|
-
def suggestion_log
|
|
257
|
-
SideJudgments.log(ToolkitSuggest.log_path(
|
|
252
|
+
def suggestion_log(state_dir)
|
|
253
|
+
SideJudgments.log(ToolkitSuggest.log_path(state_dir), tag: :toolkit, event: "toolkit_suggest_log_failed")
|
|
258
254
|
end
|
|
259
255
|
end
|
|
260
256
|
end
|
|
@@ -17,21 +17,20 @@ module Samagotchi
|
|
|
17
17
|
# against its source session as the turn begins; at its end (inside
|
|
18
18
|
# Engine#end_turn's event step) the opening's stamp, a Revise turn's
|
|
19
19
|
# draft checked and a Verify turn's verdict recorded. No state of its
|
|
20
|
-
# own: it reads the turn, and the sessions it loads from the state dir
|
|
20
|
+
# own: it reads the turn, and the sessions it loads from the state dir
|
|
21
|
+
# it is given.
|
|
21
22
|
class VerifyAnchors
|
|
22
|
-
# @param state_dir [#call] the sessions dir (a fresh Verify child's parent is loaded from it)
|
|
23
|
-
def initialize(state_dir:)
|
|
24
|
-
@state_dir = state_dir
|
|
25
|
-
end
|
|
26
|
-
|
|
27
23
|
# A Verify turn's claim (Engine#begin_turn), when its quote is in the
|
|
28
24
|
# source session's answer for that turn; nil (logged) otherwise.
|
|
29
25
|
# @param anchor [VerifyAnchor, nil]
|
|
26
|
+
# @param state_dir [String] the sessions dir (a fresh Verify child's
|
|
27
|
+
# parent is loaded from it): the Engine's at the call, as no
|
|
28
|
+
# TurnContext holds it yet
|
|
30
29
|
# @return [VerifyAnchor, nil]
|
|
31
|
-
def verified(session, anchor)
|
|
30
|
+
def verified(session, anchor, state_dir:)
|
|
32
31
|
return unless anchor
|
|
33
32
|
|
|
34
|
-
source = source_session(session, anchor)
|
|
33
|
+
source = source_session(session, anchor, state_dir)
|
|
35
34
|
return unless source
|
|
36
35
|
|
|
37
36
|
turn = AnswerTail.turn_slice(Array(source.messages), anchor.turn_id)
|
|
@@ -133,14 +132,14 @@ module Samagotchi
|
|
|
133
132
|
comparison_text(Redact.text(answer, viewer)).include?(quote_text)
|
|
134
133
|
end
|
|
135
134
|
|
|
136
|
-
def source_session(session, anchor)
|
|
135
|
+
def source_session(session, anchor, state_dir)
|
|
137
136
|
return session unless session.verify_fresh
|
|
138
137
|
|
|
139
138
|
fresh = session.verify_fresh
|
|
140
139
|
return reject_verify(anchor, :fresh_anchor_mismatch) unless fresh.anchor == anchor
|
|
141
140
|
return reject_verify(anchor, :fresh_parent_missing) unless session.parent_id
|
|
142
141
|
|
|
143
|
-
Session.load(session.parent_id, state_dir:
|
|
142
|
+
Session.load(session.parent_id, state_dir: state_dir)
|
|
144
143
|
rescue ArgumentError, SystemCallError, IOError => e
|
|
145
144
|
Log.warn(:turn, "verify_anchor_rejected", turn_id: anchor.turn_id, reason: :source_session_unavailable,
|
|
146
145
|
error: e.class.name)
|
|
@@ -17,22 +17,17 @@ module Samagotchi
|
|
|
17
17
|
# @param prompt_warmup [PromptWarmup] the Engine's warm-up (the kernel asks it for a slot pin)
|
|
18
18
|
# @param kernel [KernelLoop] the warm-up prompt, the last slot, warmed_up!
|
|
19
19
|
# @param turn_state [TurnState] whether steers are carried to the next turn
|
|
20
|
-
# @param model_settings [SessionModelSettings] the next turn's LLM context strategy
|
|
21
|
-
# @param
|
|
22
|
-
# @param system_prompt [#call] (model) the system prompt (Engine#system_prompt)
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
# (Engine#next_turn_waiting, called)
|
|
26
|
-
def initialize(prompt_warmup:, kernel:, turn_state:, model_settings:, model:, system_prompt:, reminders_due:,
|
|
27
|
-
next_turn_waiting:)
|
|
20
|
+
# @param model_settings [SessionModelSettings] the effective model now, the next turn's LLM context strategy
|
|
21
|
+
# @param reminder_queue [ReminderQueue] whether a reminder is due
|
|
22
|
+
# @param system_prompt [#call] (model) the system prompt (Engine#system_prompt): called last, only
|
|
23
|
+
# once every check passed, as building it has side effects (the memory index metric, the prompt notes)
|
|
24
|
+
def initialize(prompt_warmup:, kernel:, turn_state:, model_settings:, reminder_queue:, system_prompt:)
|
|
28
25
|
@prompt_warmup = prompt_warmup
|
|
29
26
|
@kernel = kernel
|
|
30
27
|
@turn_state = turn_state
|
|
31
28
|
@model_settings = model_settings
|
|
32
|
-
@
|
|
29
|
+
@reminder_queue = reminder_queue
|
|
33
30
|
@system_prompt = system_prompt
|
|
34
|
-
@reminders_due = reminders_due
|
|
35
|
-
@next_turn_waiting = next_turn_waiting
|
|
36
31
|
end
|
|
37
32
|
|
|
38
33
|
# After a completed turn, prefill the next turn's prompt up to its user
|
|
@@ -40,14 +35,18 @@ module Samagotchi
|
|
|
40
35
|
# local llama.cpp host on the native loop, and not when the next turn's
|
|
41
36
|
# start differs anyway or starts at once: a reminder due, a continue
|
|
42
37
|
# offer (the turn ran out of steps), a carried steer or queued input.
|
|
43
|
-
# Never fails the turn.
|
|
44
|
-
|
|
38
|
+
# Never fails the turn. Each read comes only once the checks before it
|
|
39
|
+
# passed: the model resolved after the turn's own outcome, the probe
|
|
40
|
+
# and the prompt only on a target.
|
|
41
|
+
# @param next_turn_waiting [#call, nil] the host's probe: true when the next turn starts at once or none
|
|
42
|
+
# follows (Engine#next_turn_waiting); nil: neither
|
|
43
|
+
def after(turn, result, next_turn_waiting:)
|
|
45
44
|
return if result.canceled? || result.resumable?
|
|
46
45
|
|
|
47
|
-
model = @
|
|
46
|
+
model = @model_settings.effective_model
|
|
48
47
|
client = model.client
|
|
49
48
|
return unless target?(model, client)
|
|
50
|
-
return if @
|
|
49
|
+
return if @reminder_queue.due? || @turn_state.carried_steers? || next_turn_waiting&.call
|
|
51
50
|
|
|
52
51
|
messages = ContextNote.with_system_head(turn.session.messages.dup, { role: "system", content: @system_prompt.call(model) })
|
|
53
52
|
# Under the next turn's strategy (a /model switch may have changed it);
|
|
@@ -22,8 +22,8 @@ require "json"
|
|
|
22
22
|
module Samagotchi
|
|
23
23
|
# `chi update`: brings an installed chi up to date in one go: the system
|
|
24
24
|
# bundle, the shipped bundles the user installed, and the macOS helper.
|
|
25
|
-
# Unedited things update; edited ones merge or stay behind (.md) or are replaced
|
|
26
|
-
# and reported (hooks, plugin, rules,
|
|
25
|
+
# Unedited things update; edited ones merge or stay behind (.md) or are replaced,
|
|
26
|
+
# copied to the trash first, and reported (hooks, plugin, rules, scripts). Live
|
|
27
27
|
# workers and a running chi web are reported, never restarted. The bundles
|
|
28
28
|
# chi didn't ship are only checked, under the chi that will run them
|
|
29
29
|
# (BundleCommand::InstalledCheck): what it finds is rows that fail nothing.
|
|
@@ -297,10 +297,14 @@ module Samagotchi
|
|
|
297
297
|
earlier = [] if %i[installed skipped failed].include?(result.status)
|
|
298
298
|
row = Row.new(component: "system bundle", from: result.from, to: result.to)
|
|
299
299
|
notes = file_notes(row, system::BUNDLE_NAME, result.to, result.merged, result.behind, result.trash_copy, earlier: earlier)
|
|
300
|
+
# What the run warned of (a Checksum mismatch, a file it skipped) goes
|
|
301
|
+
# on its row, as ensure! echoes it.
|
|
302
|
+
warnings = Array(result.warnings)
|
|
303
|
+
warned = [*notes, *warnings]
|
|
300
304
|
case result.status
|
|
301
|
-
when :installed then done(row, :install)
|
|
302
|
-
when :updated then done(row, :update,
|
|
303
|
-
when :restored then done(row, :restore,
|
|
305
|
+
when :installed then done(row, :install, warnings.empty? ? nil : warnings.join("; "))
|
|
306
|
+
when :updated then done(row, :update, warned.empty? ? nil : warned.join("; "))
|
|
307
|
+
when :restored then done(row, :restore, warned.empty? ? nil : warned.join("; ")).tap { |r| r.to = nil }
|
|
304
308
|
when :merged, :up_to_date then same_version(row, notes + result.warnings, changed: result.merged.any?)
|
|
305
309
|
when :newer_installed then skipped(row, ["newer than this chi's #{result.to}: left", *notes].join("; ")).tap { |r| r.to = nil }
|
|
306
310
|
when :skipped then skipped(row, result.error)
|
|
@@ -352,7 +356,7 @@ module Samagotchi
|
|
|
352
356
|
notes = file_notes(row, update.name, update.to || update.from, update.merged, update.behind, update.trash_copy, earlier: earlier)
|
|
353
357
|
case update.status
|
|
354
358
|
when :would_update, :updated
|
|
355
|
-
notes = [adds(update.adds), *notes, replaced(update.replaced), *update.kept_own].compact
|
|
359
|
+
notes = [adds(update.adds), *notes, replaced(update.replaced), *update.kept_own, update.note].compact
|
|
356
360
|
done(row, :update, notes.empty? ? nil : notes.join("; "))
|
|
357
361
|
when :would_merge, :merged, :up_to_date then same_version(row, [*notes, update.note, update.manifest].compact, changed: Array(update.merged).any?)
|
|
358
362
|
when :skipped then skipped(row, [update.note, *notes].join("; "))
|
|
@@ -526,7 +530,7 @@ module Samagotchi
|
|
|
526
530
|
def replaced(files)
|
|
527
531
|
return nil if files.empty?
|
|
528
532
|
|
|
529
|
-
"#{@dry_run ? "would replace" : "replaced"} edited #{files.join(", ")} (
|
|
533
|
+
"#{@dry_run ? "would replace" : "replaced"} edited #{files.join(", ")} (a copy goes to the trash first)"
|
|
530
534
|
end
|
|
531
535
|
|
|
532
536
|
def print_table(rows)
|
|
@@ -7,6 +7,8 @@ require_relative "atomic_file"
|
|
|
7
7
|
require_relative "log"
|
|
8
8
|
require_relative "session"
|
|
9
9
|
require_relative "session_metrics"
|
|
10
|
+
require_relative "session_row"
|
|
11
|
+
require_relative "worker_link"
|
|
10
12
|
require_relative "steer"
|
|
11
13
|
require_relative "verify_anchor"
|
|
12
14
|
require_relative "verify_fresh_origin"
|
|
@@ -37,10 +39,6 @@ module Samagotchi
|
|
|
37
39
|
# The child's first message, after the claim.
|
|
38
40
|
ASK = "Verify this claim against the files here. You have none of the original session's context. " \
|
|
39
41
|
"End with a line `Verdict: confirmed|refuted|unclear — <one-line reason>`."
|
|
40
|
-
# How long a child whose worker hasn't come up yet counts as running
|
|
41
|
-
# (Tools::Delegate::STARTING_GRACE_SECONDS).
|
|
42
|
-
STARTING_GRACE_SECONDS = 15
|
|
43
|
-
|
|
44
42
|
# A start refused: the HTTP +status+, the error +code+ and its words.
|
|
45
43
|
class Refused < StandardError
|
|
46
44
|
attr_reader :status, :code
|
|
@@ -257,15 +255,13 @@ module Samagotchi
|
|
|
257
255
|
private_class_method :locked
|
|
258
256
|
|
|
259
257
|
# A child runs while its worker runs a turn, or while it is marked
|
|
260
|
-
# running and its worker is on its way (
|
|
258
|
+
# running and its worker is on its way (SessionRow.running_or_starting?,
|
|
259
|
+
# the delegates' rule).
|
|
261
260
|
def child_running?(child_id, state_dir)
|
|
262
261
|
child = Session.summary_from_file(Session.session_file(child_id, state_dir: state_dir)) or return false
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
Time.now - Time.iso8601(child.updated_at.to_s) < STARTING_GRACE_SECONDS
|
|
267
|
-
rescue ArgumentError
|
|
268
|
-
false
|
|
262
|
+
live = WorkerLink.resolve(Session.session_dir(child_id, state_dir: state_dir), unlink: false).worker_owned?
|
|
263
|
+
SessionRow.running_or_starting?(saved_status: child.status, live: live,
|
|
264
|
+
busy: live && child.status == Session::STATUS_RUNNING, updated_at: child.updated_at)
|
|
269
265
|
end
|
|
270
266
|
private_class_method :child_running?
|
|
271
267
|
|
data/lib/samagotchi/version.rb
CHANGED
|
@@ -21,15 +21,15 @@ module Samagotchi
|
|
|
21
21
|
def no? = value == false
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# @param
|
|
24
|
+
# @param lookup [ModelLookup] the model's layers (EffectiveModel#lookup);
|
|
25
|
+
# its target is the one the probes ask
|
|
25
26
|
# @param profile [ModelProfile, nil] the model's prompt profile (native)
|
|
26
27
|
# @param adapter [LLM::OpenAIChat, nil] the chat host's adapter
|
|
27
|
-
# @param models [Hash{String => ModelSettings}, nil] ConfigFile.model_settings (specs)
|
|
28
|
-
# @param names [Array<String>] the model's lookup names (HostRegistry#lookup_names)
|
|
29
28
|
# @return [Answer]
|
|
30
|
-
def self.for(
|
|
29
|
+
def self.for(lookup, profile: nil, adapter: nil)
|
|
30
|
+
target = lookup.target
|
|
31
31
|
entry = target.entry
|
|
32
|
-
configured = configured(
|
|
32
|
+
configured = configured(lookup)
|
|
33
33
|
return configured if configured&.no?
|
|
34
34
|
|
|
35
35
|
if entry.chat?
|
|
@@ -45,18 +45,15 @@ module Samagotchi
|
|
|
45
45
|
marker.is_a?(String) && !marker.empty? ? marker : nil
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def self.configured(
|
|
49
|
-
|
|
50
|
-
ConfigFile.model_settings
|
|
51
|
-
rescue StandardError
|
|
52
|
-
{}
|
|
53
|
-
end
|
|
54
|
-
if (setting = ConfigFile.model_setting(names, :vision, models: models))
|
|
48
|
+
def self.configured(lookup)
|
|
49
|
+
if (setting = lookup.model(:vision))
|
|
55
50
|
return Answer.new(value: setting.value, reason: setting.value ? nil : "models: #{setting.key} sets vision: false")
|
|
56
51
|
end
|
|
57
|
-
return nil if entry.vision.nil?
|
|
58
52
|
|
|
59
|
-
|
|
53
|
+
vision = lookup.host(:vision)
|
|
54
|
+
return nil if vision.nil?
|
|
55
|
+
|
|
56
|
+
Answer.new(value: vision, reason: vision ? nil : "hosts.#{lookup.host_name} sets vision: false")
|
|
60
57
|
end
|
|
61
58
|
|
|
62
59
|
def self.native(target, _entry, profile, trust_config:)
|