samagotchi 0.154.1 → 0.155.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 +16 -1
- data/docs/architecture.md +3 -2
- data/docs/bundles.md +21 -0
- data/docs/cli.md +1 -1
- data/docs/guardrails.md +2 -2
- data/docs/hooks.md +1 -1
- data/docs/internals/anthropic.md +4 -4
- data/docs/internals/attached-context.md +8 -6
- data/docs/internals/block-renderers.md +8 -1
- data/docs/internals/engine.md +220 -99
- data/docs/internals/inline-widgets.md +136 -0
- data/docs/internals/objectives.md +8 -6
- data/docs/internals/participants.md +5 -5
- data/docs/internals/prompt-caching.md +9 -5
- data/docs/internals/web-frontend.md +1 -1
- data/docs/internals/working-set.md +2 -1
- data/docs/plugins.md +2 -0
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +2 -2
- data/docs/testing.md +1 -1
- data/lib/samagotchi/block_renderers.rb +5 -0
- data/lib/samagotchi/bundle_command/check.rb +1 -1
- data/lib/samagotchi/bundles/dev/manifest.yml +3 -2
- data/lib/samagotchi/bundles/inline-widgets/manifest.yml +11 -0
- data/lib/samagotchi/bundles/inline-widgets/plugin.rb +76 -0
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/engine/context_judge.rb +19 -125
- data/lib/samagotchi/engine/guardrails.rb +4 -2
- data/lib/samagotchi/engine/messages.rb +9 -6
- data/lib/samagotchi/engine/objectives.rb +22 -90
- data/lib/samagotchi/engine/participants.rb +15 -10
- data/lib/samagotchi/engine/plugins.rb +3 -1
- data/lib/samagotchi/engine/questions.rb +1 -1
- data/lib/samagotchi/engine/side_requests.rb +5 -2
- data/lib/samagotchi/engine/toolkit.rb +33 -190
- data/lib/samagotchi/engine/turn_end.rb +1 -1
- data/lib/samagotchi/engine/turn_helpers.rb +16 -39
- data/lib/samagotchi/engine/verify.rb +7 -143
- data/lib/samagotchi/engine.rb +162 -99
- data/lib/samagotchi/engine_peers.rb +1 -1
- data/lib/samagotchi/event_fanout.rb +18 -12
- data/lib/samagotchi/guardrail_wiring.rb +83 -82
- data/lib/samagotchi/guardrails/gate.rb +41 -40
- data/lib/samagotchi/guardrails/pair_boundary.rb +2 -1
- data/lib/samagotchi/guardrails/review_boundary.rb +2 -1
- data/lib/samagotchi/guardrails/targets.rb +6 -4
- data/lib/samagotchi/guardrails_command.rb +1 -1
- data/lib/samagotchi/inline_widgets.rb +325 -0
- data/lib/samagotchi/kernel_loop.rb +190 -213
- data/lib/samagotchi/llm/backend.rb +3 -1
- data/lib/samagotchi/llm/chat_loop.rb +63 -41
- data/lib/samagotchi/llm/native_backend.rb +3 -2
- data/lib/samagotchi/llm_context_forget.rb +5 -3
- data/lib/samagotchi/model_binding.rb +3 -3
- data/lib/samagotchi/plugin/prompt_sections.rb +7 -0
- data/lib/samagotchi/question_desk.rb +1 -1
- data/lib/samagotchi/revise_anchor.rb +1 -1
- data/lib/samagotchi/system_prompt.rb +37 -30
- data/lib/samagotchi/testing/boundary.rb +7 -0
- data/lib/samagotchi/testing/bundle.rb +7 -5
- data/lib/samagotchi/testing/install.rb +3 -2
- data/lib/samagotchi/tool_activity.rb +8 -6
- data/lib/samagotchi/tool_declarations.rb +4 -4
- data/lib/samagotchi/tool_response.rb +4 -3
- data/lib/samagotchi/tool_runner.rb +91 -100
- data/lib/samagotchi/toolkit_binding.rb +40 -14
- data/lib/samagotchi/tools/deferred_tools.rb +17 -21
- data/lib/samagotchi/tools/registry.rb +43 -40
- data/lib/samagotchi/tools/tool_index.rb +135 -88
- data/lib/samagotchi/tools/tool_surface.rb +173 -0
- data/lib/samagotchi/turn_context.rb +67 -0
- data/lib/samagotchi/turn_stages/context_judge.rb +170 -0
- data/lib/samagotchi/turn_stages/objectives.rb +146 -0
- data/lib/samagotchi/turn_stages/toolkit_suggestion.rb +261 -0
- data/lib/samagotchi/turn_stages/verify_anchors.rb +169 -0
- data/lib/samagotchi/turn_stages/warmup.rb +83 -0
- data/lib/samagotchi/turn_state.rb +46 -4
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/markdown_renderer.rb +39 -14
- data/lib/samagotchi/web/message_display.rb +1 -1
- data/lib/samagotchi/web/public/app.js +5 -1
- data/lib/samagotchi/web/public/block_host.js +74 -199
- data/lib/samagotchi/web/public/block_renderers.css +2 -0
- data/lib/samagotchi/web/public/block_source.js +7 -4
- data/lib/samagotchi/web/public/block_watch.js +256 -0
- data/lib/samagotchi/web/public/composer_fold.js +3 -0
- data/lib/samagotchi/web/public/index.html +28 -9
- data/lib/samagotchi/web/public/inline_widgets.js +146 -0
- data/lib/samagotchi/web/public/owner_verify.js +1 -1
- data/lib/samagotchi/web/public/participant.js +1 -1
- data/lib/samagotchi/web/public/revise_stack.js +1 -1
- data/lib/samagotchi/web/public/stage_watch.js +6 -2
- data/lib/samagotchi/web/public/text_roots.js +1 -1
- data/lib/samagotchi/web/public/verify_target.js +1 -1
- data/lib/samagotchi/web/public/widget_picks.js +130 -0
- data/lib/samagotchi/web/public/widget_walk.js +230 -0
- data/lib/samagotchi/web/public/widgets.css +70 -0
- metadata +17 -1
|
@@ -2,137 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
module Samagotchi
|
|
4
4
|
class Engine
|
|
5
|
-
# The attached-context candidate judge (ContextJudge
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
5
|
+
# The attached-context candidate judge (TurnStages::ContextJudge,
|
|
6
|
+
# forwarded): prepare_turn starts its pass, run_turn's ensure shows the
|
|
7
|
+
# cards it held, and a plugin's late candidate asks it. Reads
|
|
8
|
+
# @context_judge, @side_judgments, @host_registry.
|
|
9
9
|
module ContextJudgePart
|
|
10
10
|
private
|
|
11
11
|
|
|
12
|
-
# The
|
|
13
|
-
|
|
14
|
-
# are logged (ContextCandidatesLog#look) and, unless
|
|
15
|
-
# context.candidates.mode is off, the proposed candidates due an ask
|
|
16
|
-
# are asked about the owner's prompts so far, this one included. Never
|
|
17
|
-
# a participant's turn, a review session or a Continue (no new prompt).
|
|
18
|
-
def start_context_judge(turn)
|
|
19
|
-
session = turn.session
|
|
20
|
-
return unless turn.prompt.is_a?(String) && OwnerPrompt.turn?(session: session, author: turn.origin.author)
|
|
21
|
-
|
|
22
|
-
context_judge_pass(session, ContextCandidates.owner_texts(session.messages) + [turn.prompt], phase: "turn")
|
|
23
|
-
end
|
|
24
|
-
private :start_context_judge
|
|
12
|
+
# The judge's stage (TurnStages::ContextJudge).
|
|
13
|
+
attr_reader :context_judge
|
|
25
14
|
|
|
26
15
|
# A late proposal no owner prompt mentions (Plugin::Host
|
|
27
|
-
# #candidate_proposed:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def context_judge_pass(session, texts, phase:)
|
|
41
|
-
return if session.id.nil? || session.scratch
|
|
42
|
-
|
|
43
|
-
rows = ContextCandidates.with_status(session.id, project_root: session.project_root, state_dir: session_state_dir)
|
|
44
|
-
return if rows.empty?
|
|
45
|
-
|
|
46
|
-
log = ContextCandidatesLog.log_for(session_state_dir)
|
|
47
|
-
records = ContextCandidatesLog.records_for(log, session.id)
|
|
48
|
-
ContextCandidatesLog.look(log, session.id, rows, records) if phase == "turn"
|
|
49
|
-
settings = ContextJudge::Settings.from_config
|
|
50
|
-
return if settings.mode == "off"
|
|
51
|
-
|
|
52
|
-
jobs = ContextJudge.jobs(rows, records, session_id: session.id, texts: texts, settings: settings, phase: phase,
|
|
53
|
-
parent_attached: context_parent_attached(session))
|
|
54
|
-
return if jobs.empty?
|
|
55
|
-
|
|
56
|
-
client = system_one_recording(SystemOne::Client.from_config(host_registry: @host_registry, mode: "on", tag: :context),
|
|
57
|
-
ContextJudge::JUDGMENT, session, mode: settings.mode)
|
|
58
|
-
return unless client
|
|
59
|
-
|
|
60
|
-
start_context_asks(client, jobs, log, settings, context_choice(session, log, settings))
|
|
61
|
-
rescue StandardError => e
|
|
62
|
-
ContextJudge.warn(e)
|
|
63
|
-
end
|
|
64
|
-
private :context_judge_pass
|
|
65
|
-
|
|
66
|
-
# @return [Thread, nil] the asks' thread, nil when none started
|
|
67
|
-
def start_context_asks(client, jobs, log, settings, choice)
|
|
68
|
-
if @side_judgments.in_flight?(:context)
|
|
69
|
-
Log.info(:context, "context_judge_in_flight_skipped", session_id: jobs.first.session_id)
|
|
70
|
-
return nil
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
@side_judgments.start(:context, warn: ContextJudge.method(:warn)) do
|
|
74
|
-
jobs.each { |job| choice.call(job, ContextJudge.ask(client: client, job: job, log: log, settings: settings)) }
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
private :start_context_asks
|
|
78
|
-
|
|
79
|
-
# What an answer does on the asks' thread (ContextCandidateChoice
|
|
80
|
-
# #after_judged: on mode attaches or cards; audit nothing). Taken on
|
|
81
|
-
# the caller's thread. The attach only writes the session's folder: its
|
|
82
|
-
# note comes with the next between-turn delivery, never into a running
|
|
83
|
-
# turn. A card shows only while this Engine is still on that session,
|
|
84
|
-
# and not in a running turn (it isn't the turn's: no "waiting for you"
|
|
85
|
-
# on it), but once it has ended.
|
|
86
|
-
# @return [#call] (job, fields)
|
|
87
|
-
def context_choice(session, log, settings)
|
|
88
|
-
session_id = session.id
|
|
89
|
-
project_root = session.project_root
|
|
90
|
-
state_dir = session_state_dir
|
|
91
|
-
show = lambda do |**card|
|
|
92
|
-
current = @session
|
|
93
|
-
next nil unless current.nil? || current.id == session_id
|
|
94
|
-
|
|
95
|
-
@context_cards_mutex.synchronize do
|
|
96
|
-
next @context_cards_after_turn << card if turn_running?
|
|
97
|
-
|
|
98
|
-
show_card(**card)
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
lambda do |job, fields|
|
|
102
|
-
ContextCandidateChoice.after_judged(job: job, fields: fields, settings: settings, project_root: project_root,
|
|
103
|
-
state_dir: state_dir, log: log, show_card: show)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
private :context_choice
|
|
107
|
-
|
|
108
|
-
# The cards #context_choice held while a turn ran (#run_turn's ensure,
|
|
109
|
-
# after the turn flag is released).
|
|
110
|
-
def show_context_cards_after_turn
|
|
111
|
-
cards = @context_cards_mutex.synchronize { @context_cards_after_turn.slice!(0..) }
|
|
112
|
-
cards.each { |card| show_card(**card) }
|
|
113
|
-
rescue StandardError => e
|
|
114
|
-
ContextJudge.warn(e)
|
|
115
|
-
end
|
|
116
|
-
private :show_context_cards_after_turn
|
|
117
|
-
|
|
118
|
-
# A delegate child's parent's sources, by title (else name): the
|
|
119
|
-
# judge's started_from. nil for any other session (a chi send --new
|
|
120
|
-
# session has no parent; a fork isn't a delegate).
|
|
121
|
-
def context_parent_attached(session)
|
|
122
|
-
return nil unless session.delegate? && session.parent_id
|
|
123
|
-
|
|
124
|
-
parent_root = if Session.exist?(session.parent_id, state_dir: session_state_dir)
|
|
125
|
-
Session.load(session.parent_id, state_dir: session_state_dir).project_root
|
|
126
|
-
else
|
|
127
|
-
session.project_root
|
|
128
|
-
end
|
|
129
|
-
ContextSources.attached(session.parent_id, project_root: parent_root, state_dir: session_state_dir)
|
|
130
|
-
.map { |attached| attached.source.title || attached.name }
|
|
131
|
-
rescue StandardError => e
|
|
132
|
-
Log.warn(:context, "context_parent_attached_failed", error: e.class.name, msg: e.message.to_s[0, 200])
|
|
133
|
-
[]
|
|
16
|
+
# #candidate_proposed): TurnStages::ContextJudge#late_candidates.
|
|
17
|
+
def judge_late_candidates = @context_judge.late_candidates
|
|
18
|
+
|
|
19
|
+
# The stage over this Engine's side asks and its session (built in
|
|
20
|
+
# #build_tools_and_plugins, before the plugins load: one may propose a
|
|
21
|
+
# candidate as it loads).
|
|
22
|
+
def build_context_judge
|
|
23
|
+
TurnStages::ContextJudge.new(
|
|
24
|
+
side_judgments: @side_judgments, host_registry: @host_registry, state_dir: -> { session_state_dir },
|
|
25
|
+
recording: ->(client, judgment, session, mode:) { system_one_recording(client, judgment, session, mode: mode) },
|
|
26
|
+
session: -> { session }, messages: -> { plugin_messages }, turn_running: -> { turn_running? },
|
|
27
|
+
show_card: ->(**card) { show_card(**card) }
|
|
28
|
+
)
|
|
134
29
|
end
|
|
135
|
-
private :context_parent_attached
|
|
136
30
|
end
|
|
137
31
|
end
|
|
138
32
|
end
|
|
@@ -76,11 +76,13 @@ module Samagotchi
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
# Ask the user to approve a call the gate voted `ask` on
|
|
79
|
-
# (GuardrailWiring#request_approval)
|
|
79
|
+
# (GuardrailWiring#request_approval), for the running turn (its
|
|
80
|
+
# author), else the last one. The gate's own asks come with their
|
|
81
|
+
# call's TurnContext instead.
|
|
80
82
|
# @param verdict [Guardrails::Verdict]
|
|
81
83
|
# @return [Guardrails::Verdict]
|
|
82
84
|
def request_approval(verdict)
|
|
83
|
-
@guardrail_wiring.request_approval(verdict)
|
|
85
|
+
@guardrail_wiring.request_approval(verdict, turn_context || last_turn_context)
|
|
84
86
|
end
|
|
85
87
|
end
|
|
86
88
|
end
|
|
@@ -46,7 +46,7 @@ module Samagotchi
|
|
|
46
46
|
from_cwd: note[:from_cwd], text: note[:text], context_source: note[:context_source],
|
|
47
47
|
created_at: note[:created_at], objectives: entry }.compact)
|
|
48
48
|
announce_objectives(session) if entry
|
|
49
|
-
|
|
49
|
+
@objectives_stage.note(session, note)
|
|
50
50
|
message
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -76,14 +76,17 @@ module Samagotchi
|
|
|
76
76
|
# register it on the kernel's llm_context_turn): the same array the loop
|
|
77
77
|
# appends to, not a copy, so read it on the turn's thread (the pending
|
|
78
78
|
# input drain, at an iteration boundary: TurnCheckpoint), or from another
|
|
79
|
-
# only as a dup, read only (/objectives). nil when no
|
|
80
|
-
#
|
|
81
|
-
# last turn's
|
|
79
|
+
# only as a dup, read only (/objectives). nil when no turn runs, and
|
|
80
|
+
# while the running turn's loop has yet to register its own
|
|
81
|
+
# (llm_context_turn is never cleared, and still holds the last turn's
|
|
82
|
+
# then: its turn_id tells them apart).
|
|
82
83
|
# @return [Array<Hash>, nil]
|
|
83
84
|
def live_conversation
|
|
84
|
-
|
|
85
|
+
running = turn_context
|
|
86
|
+
return nil unless running && @kernel.respond_to?(:llm_context_turn)
|
|
85
87
|
|
|
86
|
-
@kernel.llm_context_turn
|
|
88
|
+
forget = @kernel.llm_context_turn
|
|
89
|
+
forget&.turn_id == running.id ? forget.conversation : nil
|
|
87
90
|
end
|
|
88
91
|
|
|
89
92
|
# Append messages to the current session's conversation.
|
|
@@ -2,30 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Samagotchi
|
|
4
4
|
class Engine
|
|
5
|
-
# The session's objectives:
|
|
6
|
-
#
|
|
7
|
-
# is over, System 1's audit of them (ObjectivesAudit)
|
|
8
|
-
# @
|
|
9
|
-
#
|
|
5
|
+
# The session's objectives (TurnStages::Objectives, forwarded): the view
|
|
6
|
+
# announced when it changed, the model's proposals kept as cards until
|
|
7
|
+
# the turn is over, System 1's audit of them (ObjectivesAudit); and
|
|
8
|
+
# Check this's side request. Reads @objectives_stage, @side_judgments,
|
|
9
|
+
# @host_registry, @session.
|
|
10
10
|
module ObjectivesPart
|
|
11
|
-
# The session's objectives as the page draws them
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
# a rollback. A session with no list announces nothing. It only informs:
|
|
15
|
-
# a failure is logged, never the turn's.
|
|
16
|
-
def announce_objectives(session = @session)
|
|
17
|
-
return unless session
|
|
18
|
-
|
|
19
|
-
synchronize_events do
|
|
20
|
-
view = Objectives.view(session.id, session.messages, state_dir: session_state_dir, running: live_conversation&.dup)
|
|
21
|
-
next if view.nil? || view == @objectives_announced
|
|
22
|
-
|
|
23
|
-
@objectives_announced = view
|
|
24
|
-
announce({ type: :objectives_updated, session_id: session.id, **view })
|
|
25
|
-
end
|
|
26
|
-
rescue StandardError => e
|
|
27
|
-
Log.warn(:turn, "objectives_announce_failed", error: e.class.name, msg: e.message)
|
|
28
|
-
end
|
|
11
|
+
# The session's objectives as the page draws them, when that differs
|
|
12
|
+
# from the last announced (TurnStages::Objectives#announce).
|
|
13
|
+
def announce_objectives(session = @session) = @objectives_stage.announce(session)
|
|
29
14
|
|
|
30
15
|
# Check this (ObjectivesCheck)'s one side request. Its usage is in the
|
|
31
16
|
# session's side line. Raises as ctx.ask_model's request does.
|
|
@@ -38,79 +23,26 @@ module Samagotchi
|
|
|
38
23
|
end
|
|
39
24
|
|
|
40
25
|
# A change the model proposes (the objectives tool), kept until the turn
|
|
41
|
-
# is over
|
|
42
|
-
# over a restart (CardStore), not a running turn's that says it waits on the
|
|
43
|
-
# user. The same change twice in a turn is one card.
|
|
26
|
+
# is over (TurnStages::Objectives#propose).
|
|
44
27
|
# @return [Boolean] false past Objectives::PROPOSALS_PER_TURN
|
|
45
|
-
def propose_objective(proposal)
|
|
46
|
-
@objective_proposals_mutex.synchronize do
|
|
47
|
-
next true if @objective_proposals.any? { |p| p.line == proposal.line }
|
|
48
|
-
next false if @objective_proposals.size >= Objectives::PROPOSALS_PER_TURN
|
|
49
|
-
|
|
50
|
-
@objective_proposals << proposal
|
|
51
|
-
true
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def show_objective_proposals
|
|
56
|
-
drain_objective_proposals.each do |proposal|
|
|
57
|
-
id = "objectives-#{SecureRandom.hex(4)}"
|
|
58
|
-
show_card(source: Objectives::SOURCE, id: id, title: proposal.title, body: proposal.body,
|
|
59
|
-
actions: [{ label: "Apply", command: proposal.action_line(id) },
|
|
60
|
-
{ label: "Dismiss", command: "/objectives --card #{id} dismiss" }])
|
|
61
|
-
end
|
|
62
|
-
rescue StandardError => e
|
|
63
|
-
Log.warn(:turn, "objectives_proposal_failed", error: e.class.name, msg: e.message)
|
|
64
|
-
end
|
|
65
|
-
private :show_objective_proposals
|
|
66
|
-
|
|
67
|
-
# A turn that did not complete (canceled, failed) takes its proposals
|
|
68
|
-
# off the buffer without showing them: their Apply card would sit after a
|
|
69
|
-
# turn whose `objectives propose` call is not in the history (a rollback
|
|
70
|
-
# after Ctrl-C drops it), and leaving them in the buffer would show them
|
|
71
|
-
# at the end of the next turn.
|
|
72
|
-
def discard_objective_proposals(turn)
|
|
73
|
-
dropped = drain_objective_proposals
|
|
74
|
-
Log.info(:turn, "objectives_proposals_dropped", count: dropped.size, outcome: turn.outcome.to_s) unless dropped.empty?
|
|
75
|
-
end
|
|
76
|
-
private :discard_objective_proposals
|
|
77
|
-
|
|
78
|
-
def drain_objective_proposals
|
|
79
|
-
@objective_proposals_mutex.synchronize { @objective_proposals.slice!(0..) }
|
|
80
|
-
end
|
|
81
|
-
private :drain_objective_proposals
|
|
28
|
+
def propose_objective(proposal) = @objectives_stage.propose(proposal)
|
|
82
29
|
|
|
83
30
|
private
|
|
84
31
|
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
objectives_audit.outgoing(turn.session, result.text)
|
|
98
|
-
end
|
|
99
|
-
private :audit_objectives_outgoing
|
|
100
|
-
|
|
101
|
-
# With saving off nothing would come of an ask, so none is made.
|
|
102
|
-
def objectives_audit
|
|
103
|
-
@objectives_audit ||= ObjectivesAudit.new(
|
|
104
|
-
side: @side_judgments, state_dir: -> { session_state_dir },
|
|
105
|
-
client_for: lambda do |judgment, session|
|
|
106
|
-
next nil unless system_one_runs.on?
|
|
107
|
-
|
|
108
|
-
system_one_recording(SystemOne::Client.from_config(host_registry: @host_registry, mode: ObjectivesAudit::MODE, tag: :system_one),
|
|
109
|
-
judgment, session)
|
|
110
|
-
end
|
|
32
|
+
# The objectives' stage (TurnStages::Objectives).
|
|
33
|
+
attr_reader :objectives_stage
|
|
34
|
+
|
|
35
|
+
# The stage over this Engine's side asks and its event lock (built in
|
|
36
|
+
# #initialize, once the host registry is).
|
|
37
|
+
def build_objectives_stage
|
|
38
|
+
TurnStages::Objectives.new(
|
|
39
|
+
side_judgments: @side_judgments, host_registry: @host_registry, state_dir: -> { session_state_dir },
|
|
40
|
+
recording: ->(client, judgment, session, mode:) { system_one_recording(client, judgment, session, mode: mode) },
|
|
41
|
+
runs: -> { system_one_runs }, synchronize: ->(&block) { synchronize_events(&block) },
|
|
42
|
+
announce: ->(event) { announce(event) }, running: -> { live_conversation },
|
|
43
|
+
show_card: ->(**card) { show_card(**card) }
|
|
111
44
|
)
|
|
112
45
|
end
|
|
113
|
-
private :objectives_audit
|
|
114
46
|
end
|
|
115
47
|
end
|
|
116
48
|
end
|
|
@@ -5,13 +5,16 @@ module Samagotchi
|
|
|
5
5
|
# The session's participants: the running turn's author, a pair turn's
|
|
6
6
|
# stop check, who came or went, the system prompt's participants line and
|
|
7
7
|
# a participant's author fields. Writes @participant_stop,
|
|
8
|
-
# @participants_line; reads @participants, @participants_watch
|
|
9
|
-
#
|
|
8
|
+
# @participants_line; reads @participants, @participants_watch and the
|
|
9
|
+
# turn's TurnContext.
|
|
10
10
|
module ParticipantsPart
|
|
11
11
|
# The running turn's author when a participant wrote it, else nil
|
|
12
|
-
# (ctx.turn_author, the hooks' author
|
|
12
|
+
# (ctx.turn_author, the hooks' author, the judgments' participant:,
|
|
13
|
+
# ObjectivesCheck): one read of the published TurnContext, so any
|
|
14
|
+
# thread sees the running turn's author from its first moment, never
|
|
15
|
+
# the last turn's.
|
|
13
16
|
# @return [String, nil]
|
|
14
|
-
def turn_author =
|
|
17
|
+
def turn_author = turn_context&.author
|
|
15
18
|
|
|
16
19
|
# Whether a participant's pair turn must stop, read before each of its
|
|
17
20
|
# generations (#guard_participant_spend): author ("p:<id>") → why (their
|
|
@@ -20,7 +23,7 @@ module Samagotchi
|
|
|
20
23
|
# nothing is checked mid-turn.
|
|
21
24
|
attr_writer :participant_stop
|
|
22
25
|
|
|
23
|
-
# Before each generation (KernelLoop#generation_guard): a pair turn whose
|
|
26
|
+
# Before each generation (KernelLoop::Services#generation_guard): a pair turn whose
|
|
24
27
|
# participant's link ended, or whose spend (Engine#metrics: their
|
|
25
28
|
# finished turns, this one's finished generations, their side requests)
|
|
26
29
|
# passed their cap, stops on the plugins' stop path (cost-cap's,
|
|
@@ -28,10 +31,11 @@ module Samagotchi
|
|
|
28
31
|
# once it ends, so a turn can pass the cap by its last generation. The
|
|
29
32
|
# turn-start check is the Worker's (Worker#participant_refusal); a
|
|
30
33
|
# review turn only reads, and keeps to it.
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
# @param context [TurnContext] the running turn's (the loop's)
|
|
35
|
+
def guard_participant_spend(context)
|
|
36
|
+
author = context&.author
|
|
33
37
|
return if author.nil? || @participant_stop.nil?
|
|
34
|
-
return unless
|
|
38
|
+
return unless context.participant_mode == ParticipantMode::PAIR
|
|
35
39
|
|
|
36
40
|
reason = @participant_stop.call(author) or return
|
|
37
41
|
hook_stop_turn(reason, PARTICIPANTS_HOOK)
|
|
@@ -77,8 +81,9 @@ module Samagotchi
|
|
|
77
81
|
# A participant's opening message keeps the rules it was delivered under
|
|
78
82
|
# (ParticipantMode), stamped before any request carries it, so its header
|
|
79
83
|
# never changes when the session's mode does.
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
# @param context [TurnContext] the turn's
|
|
85
|
+
def participant_mode_fields(context)
|
|
86
|
+
mode = context.participant? && context.participant_mode
|
|
82
87
|
mode ? { participant_mode: mode.to_s } : {}
|
|
83
88
|
end
|
|
84
89
|
end
|
|
@@ -101,11 +101,13 @@ module Samagotchi
|
|
|
101
101
|
# Apply the staged tool sets, on the turn thread, before the turn's
|
|
102
102
|
# system prompt is built (PluginTasks#apply_staged_tools!). A changed
|
|
103
103
|
# set may change the chi group the search tool's description counts
|
|
104
|
-
# (a plugin tool the toolkit leaves out).
|
|
104
|
+
# (a plugin tool the toolkit leaves out). Publishes the Registry it
|
|
105
|
+
# leaves (#publish_tools!).
|
|
105
106
|
# @return [Boolean] whether the tools changed
|
|
106
107
|
def apply_staged_tools!
|
|
107
108
|
changed = @plugin_tasks.apply_staged_tools!
|
|
108
109
|
@tool_index.refresh! if changed
|
|
110
|
+
publish_tools!
|
|
109
111
|
changed
|
|
110
112
|
end
|
|
111
113
|
public :apply_staged_tools!
|
|
@@ -15,7 +15,7 @@ module Samagotchi
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Ask the model's question (ask_user_question): the kernel's
|
|
18
|
-
# question_handler, called on the turn thread with the payload
|
|
18
|
+
# Services#question_handler, called on the turn thread with the payload
|
|
19
19
|
# Tools::AskUserQuestion.validate made (QuestionDesk#request), after
|
|
20
20
|
# the :question_asked hooks (QuestionAsked) gave the step its refs; a
|
|
21
21
|
# --non-interactive run has no one to ask, so they don't fire there.
|
|
@@ -42,9 +42,12 @@ module Samagotchi
|
|
|
42
42
|
def side_author(kind)
|
|
43
43
|
# Check this is only ever the owner's, whichever turn runs as it ends.
|
|
44
44
|
return nil if kind.to_s == ObjectivesCheck::SOURCE
|
|
45
|
-
return nil unless turn_running? || kind.to_s == RECAP_SIDE_KIND
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
# One read each of the published contexts (TurnState): any thread.
|
|
47
|
+
running = turn_context
|
|
48
|
+
return running.author if running
|
|
49
|
+
|
|
50
|
+
kind.to_s == RECAP_SIDE_KIND ? last_turn_context&.author : nil
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
private
|