samagotchi 0.63.0 → 0.65.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 +86 -1
- data/docs/architecture.md +32 -5
- data/docs/cli.md +41 -9
- data/docs/configuration.md +143 -11
- data/docs/internals/engine.md +25 -6
- data/docs/plugins.md +7 -0
- data/docs/sessions.md +14 -10
- data/docs/sub-agent.md +21 -4
- data/docs/testing.md +6 -0
- data/lib/samagotchi/answer_command.rb +5 -1
- data/lib/samagotchi/bridge/turn_accumulator.rb +1 -0
- data/lib/samagotchi/bridge.rb +29 -5
- data/lib/samagotchi/bundles/slow-commands/manifest.yml +2 -2
- data/lib/samagotchi/bundles/slow-commands/plugin.rb +5 -3
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/child_reports.rb +7 -2
- data/lib/samagotchi/cli/parent_wait.rb +9 -2
- data/lib/samagotchi/client.rb +4 -2
- data/lib/samagotchi/composer_route.rb +90 -0
- data/lib/samagotchi/config.rb +11 -7
- data/lib/samagotchi/continue_offer.rb +17 -3
- data/lib/samagotchi/delivery.rb +13 -8
- data/lib/samagotchi/engine.rb +41 -7
- data/lib/samagotchi/generation_phase.rb +17 -0
- data/lib/samagotchi/host_config.rb +33 -12
- data/lib/samagotchi/host_model.rb +2 -2
- data/lib/samagotchi/host_registry.rb +34 -11
- data/lib/samagotchi/idle_client.rb +62 -25
- data/lib/samagotchi/idle_recap.rb +5 -2
- data/lib/samagotchi/idle_target.rb +30 -7
- data/lib/samagotchi/interrupted_turn.rb +7 -3
- data/lib/samagotchi/last_turn.rb +5 -2
- data/lib/samagotchi/llm/adapters.rb +41 -0
- data/lib/samagotchi/llm/anthropic_assembly.rb +210 -0
- data/lib/samagotchi/llm/anthropic_messages.rb +416 -0
- data/lib/samagotchi/llm/anthropic_wire.rb +238 -0
- data/lib/samagotchi/llm/api_key.rb +26 -9
- data/lib/samagotchi/llm/chat_loop.rb +113 -14
- data/lib/samagotchi/llm/dialect.rb +57 -0
- data/lib/samagotchi/llm/errors.rb +81 -12
- data/lib/samagotchi/llm/model_listing.rb +111 -0
- data/lib/samagotchi/llm/model_result.rb +9 -2
- data/lib/samagotchi/llm/openai_chat.rb +46 -93
- data/lib/samagotchi/model_profile.rb +1 -1
- data/lib/samagotchi/pending_input_queue.rb +10 -3
- data/lib/samagotchi/plugin/sessions.rb +27 -5
- data/lib/samagotchi/reasoning_history.rb +14 -4
- data/lib/samagotchi/recap_setup.rb +2 -1
- data/lib/samagotchi/reply_wait.rb +171 -6
- data/lib/samagotchi/self_report.rb +18 -28
- data/lib/samagotchi/send_command.rb +63 -19
- data/lib/samagotchi/session.rb +3 -2
- data/lib/samagotchi/session_commands.rb +1 -1
- data/lib/samagotchi/session_inbox.rb +63 -7
- data/lib/samagotchi/session_manager.rb +49 -20
- data/lib/samagotchi/session_metrics.rb +6 -11
- data/lib/samagotchi/steer_cut.rb +4 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +55 -28
- data/lib/samagotchi/terminal_ui/event_renderer.rb +2 -1
- data/lib/samagotchi/terminal_ui/formatting.rb +7 -22
- data/lib/samagotchi/terminal_ui/status_row.rb +24 -3
- data/lib/samagotchi/terminal_ui.rb +72 -57
- data/lib/samagotchi/tool_activity.rb +5 -5
- data/lib/samagotchi/tools/delegate.rb +28 -1
- data/lib/samagotchi/tools/delegate_cursor.rb +9 -4
- data/lib/samagotchi/tools/delegate_wait.rb +17 -5
- data/lib/samagotchi/tools/execute.rb +11 -4
- data/lib/samagotchi/turn_checkpoint.rb +9 -3
- data/lib/samagotchi/turn_note.rb +22 -5
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +2 -1
- data/lib/samagotchi/web/public/activity.js +3 -0
- data/lib/samagotchi/web/public/app.js +96 -13
- data/lib/samagotchi/web/public/commands.js +8 -8
- data/lib/samagotchi/web/public/composer.js +3 -3
- data/lib/samagotchi/web/public/delivery_line.js +228 -0
- data/lib/samagotchi/web/public/index.html +29 -0
- data/lib/samagotchi/web/public/live_turn.js +8 -0
- data/lib/samagotchi/web/public/stage_model.js +27 -7
- data/lib/samagotchi/web/public/stage_view.js +32 -10
- data/lib/samagotchi/web/public/stream_handlers.js +8 -2
- data/lib/samagotchi/web/public/timing.js +2 -1
- data/lib/samagotchi/web/public/turn_events.js +22 -8
- data/lib/samagotchi/web/public/turn_html.js +20 -4
- data/lib/samagotchi/web/public/turn_model.js +29 -12
- data/lib/samagotchi/web/public/turn_view.js +6 -4
- data/lib/samagotchi/worker.rb +7 -5
- metadata +9 -1
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// Rules:
|
|
8
8
|
// - turn_started opens the first gen (no iteration yet).
|
|
9
9
|
// - generation_started(n) opens a new gen unless the current one is still
|
|
10
|
-
// empty (then it takes n)
|
|
10
|
+
// empty (then it takes n); either way the gen's startedAt is the page's
|
|
11
|
+
// clock at it (null for a replay).
|
|
11
12
|
// - a chunk goes to the gen of its iteration (opening one when none has it);
|
|
12
13
|
// a chunk with no iteration goes to the current gen, unless that one is
|
|
13
14
|
// textDone or already has tool rows (an older server's replay), which
|
|
@@ -45,7 +46,7 @@ function isEmpty(gen) {
|
|
|
45
46
|
|
|
46
47
|
function openGen(turn, iteration) {
|
|
47
48
|
const closed = closeCurrent(turn);
|
|
48
|
-
const gen = { iteration: iteration ?? null, thinking: "", text: "", activity: newActivity(), textDone: false, closed: false, notices: [] };
|
|
49
|
+
const gen = { iteration: iteration ?? null, thinking: "", text: "", activity: newActivity(), textDone: false, closed: false, notices: [], startedAt: null, thinkingAt: null };
|
|
49
50
|
gen.tools = gen.activity.rows;
|
|
50
51
|
turn.gens.push(gen);
|
|
51
52
|
return { gen, opened: true, closed };
|
|
@@ -81,16 +82,21 @@ export function turnStarted(turn) {
|
|
|
81
82
|
return openGen(turn, null);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
|
|
85
|
+
// +now+ (ms): when the page saw the generation start, its startedAt (the
|
|
86
|
+
// composer's delivery line clocks the step from it); null for a replayed
|
|
87
|
+
// start (a join: the snapshot's generation age stands in).
|
|
88
|
+
export function generationStarted(turn, iteration = null, now = null) {
|
|
85
89
|
const current = currentGen(turn);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return openGen(turn, iteration);
|
|
90
|
+
const r = current && isEmpty(current) ? { gen: current, opened: false, closed: null } : openGen(turn, iteration);
|
|
91
|
+
if (!r.opened) r.gen.iteration = iteration ?? r.gen.iteration;
|
|
92
|
+
r.gen.startedAt = now;
|
|
93
|
+
return r;
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
// +now+ (ms): when the page got the chunk (null for a replay): a gen's
|
|
97
|
+
// first thinking delta stamps its thinkingAt, the clock the worker's cut
|
|
98
|
+
// counts from too (GenerationPhase).
|
|
99
|
+
export function chunk(turn, { text = "", thinking = "", iteration = null, now = null } = {}) {
|
|
94
100
|
let r;
|
|
95
101
|
if (iteration == null) {
|
|
96
102
|
const current = currentGen(turn);
|
|
@@ -102,6 +108,7 @@ export function chunk(turn, { text = "", thinking = "", iteration = null } = {})
|
|
|
102
108
|
// Leading whitespace (the "\n" after <think>, or after a closed
|
|
103
109
|
// think/tool_call block) would render as an empty first line; trim it at
|
|
104
110
|
// the start of a gen's thinking and text only.
|
|
111
|
+
if (thinking && !gen.thinking && now != null) gen.thinkingAt = now;
|
|
105
112
|
if (thinking) gen.thinking += leadTrimmed(gen.thinking, thinking);
|
|
106
113
|
if (text) gen.text += leadTrimmed(gen.text, text);
|
|
107
114
|
return r;
|
|
@@ -222,13 +229,23 @@ export function genLabelParts(gen) {
|
|
|
222
229
|
if (line) return { head: cut(line), calls, repeats: true };
|
|
223
230
|
if (first?.title) return { head: cut(`${toolName(first)} ${first.title}`), calls, repeats: true };
|
|
224
231
|
if (gen.tools.length) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
// A plugin label that is a verb ("echoing", the default "calling
|
|
233
|
+
// tool") is the head itself when every call has one; among other
|
|
234
|
+
// tools it gives way to its tool's name ("working with read, echo").
|
|
235
|
+
const verbs = gen.tools.every(isVerbLabel);
|
|
236
|
+
const names = [...new Set(gen.tools.map((r) => (verbs || !isVerbLabel(r) ? toolName(r) : r.tool || "tool")))];
|
|
237
|
+
const list = `${names.slice(0, LABEL_TOOLS).join(", ")}${names.length > LABEL_TOOLS ? ", …" : ""}`;
|
|
238
|
+
return { head: verbs ? list : `working with ${list}`, calls, repeats: false };
|
|
228
239
|
}
|
|
229
240
|
return { head: "thinking", calls: "", repeats: false };
|
|
230
241
|
}
|
|
231
242
|
|
|
243
|
+
// A plugin tool's label that reads as a verb (its first word ends in
|
|
244
|
+
// "ing": "echoing", "calling tool"), not as a name ("chrome: screenshot").
|
|
245
|
+
function isVerbLabel(row) {
|
|
246
|
+
return /^[a-z]+ing\b/i.test(String(row?.label || ""));
|
|
247
|
+
}
|
|
248
|
+
|
|
232
249
|
// A step that said something (its text is not the turn's answer): a row of
|
|
233
250
|
// its own in a finished block's outline.
|
|
234
251
|
export function isNarrated(gen) {
|
|
@@ -700,14 +700,16 @@ export function createTurnView({
|
|
|
700
700
|
draw(entry);
|
|
701
701
|
}
|
|
702
702
|
},
|
|
703
|
-
|
|
703
|
+
// +now+: the page's clock at a live start (stream_handlers.js), null
|
|
704
|
+
// for a replayed one.
|
|
705
|
+
generationStarted(data, now = null) {
|
|
704
706
|
if (!turn) this.turnStarted({});
|
|
705
|
-
apply(model.generationStarted(turn, data.iteration ?? null));
|
|
707
|
+
apply(model.generationStarted(turn, data.iteration ?? null, now));
|
|
706
708
|
},
|
|
707
|
-
chunk({ text, thinking, iteration }) {
|
|
709
|
+
chunk({ text, thinking, iteration, now = null }) {
|
|
708
710
|
if (!text && !thinking) return;
|
|
709
711
|
if (!turn) this.turnStarted({});
|
|
710
|
-
const r = model.chunk(turn, { text, thinking, iteration });
|
|
712
|
+
const r = model.chunk(turn, { text, thinking, iteration, now });
|
|
711
713
|
apply(r);
|
|
712
714
|
scheduleFlush(r.gen);
|
|
713
715
|
},
|
data/lib/samagotchi/worker.rb
CHANGED
|
@@ -900,7 +900,7 @@ module Samagotchi
|
|
|
900
900
|
@reporting_turn = reports_to_parent?(turn_args[:origin])
|
|
901
901
|
restores = @turn_flow.restores
|
|
902
902
|
begin
|
|
903
|
-
@checkpoint.begin_turn(prompt: prompt)
|
|
903
|
+
@checkpoint.begin_turn(prompt: prompt, enqueued_id: turn_args[:origin]&.dig(:enqueued_id))
|
|
904
904
|
@session.status = Session::STATUS_RUNNING
|
|
905
905
|
# A continue that moved this session to the chain's new link wrote
|
|
906
906
|
# the new parent beside it (Session::PARENT_FILE): this turn's
|
|
@@ -929,7 +929,7 @@ module Samagotchi
|
|
|
929
929
|
# no reply: its text is what came before its last tool calls, and a
|
|
930
930
|
# wait (chi send --wait) gets the question instead.
|
|
931
931
|
unless response.nil? || response.strip.empty? || @continue_offer.awaiting?
|
|
932
|
-
SessionInbox.write_output(@session_dir, response)
|
|
932
|
+
SessionInbox.write_output(@session_dir, response, enqueued_id: turn_args[:origin]&.dig(:enqueued_id))
|
|
933
933
|
end
|
|
934
934
|
save_or_log(:turn) { save_session }
|
|
935
935
|
# The reports the turn merged were read unless its end took the turn
|
|
@@ -1045,10 +1045,12 @@ module Samagotchi
|
|
|
1045
1045
|
@pending_input_drain ||= lambda do
|
|
1046
1046
|
# An iteration boundary: tell whoever sent a command now that it waits.
|
|
1047
1047
|
refuse_queued_commands(mid_turn: true)
|
|
1048
|
-
# A line with images runs as its own
|
|
1049
|
-
#
|
|
1048
|
+
# A queued line or one with images runs as its own turn after this
|
|
1049
|
+
# one (steering merges text only): skipped here, not a barrier, so a
|
|
1050
|
+
# line sent after it still goes in at this boundary. Only the files
|
|
1051
|
+
# claimed below reach @merged_this_turn and the checkpoint.
|
|
1050
1052
|
files = SessionInbox.find_new_input_files(@session_dir).sort
|
|
1051
|
-
.
|
|
1053
|
+
.reject { |input_file| SessionInbox.waits_for_turn_end?(input_file) }
|
|
1052
1054
|
merged = files.filter_map do |input_file|
|
|
1053
1055
|
claimed_file = SessionInbox.claim_input_file(input_file)
|
|
1054
1056
|
next unless claimed_file
|
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.65.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -235,6 +235,7 @@ files:
|
|
|
235
235
|
- lib/samagotchi/client_id.rb
|
|
236
236
|
- lib/samagotchi/command_steps.rb
|
|
237
237
|
- lib/samagotchi/commands/registry.rb
|
|
238
|
+
- lib/samagotchi/composer_route.rb
|
|
238
239
|
- lib/samagotchi/config.rb
|
|
239
240
|
- lib/samagotchi/config_text_edit.rb
|
|
240
241
|
- lib/samagotchi/context_absorber.rb
|
|
@@ -320,11 +321,17 @@ files:
|
|
|
320
321
|
- lib/samagotchi/last_turn.rb
|
|
321
322
|
- lib/samagotchi/launch_mode.rb
|
|
322
323
|
- lib/samagotchi/live_versions.rb
|
|
324
|
+
- lib/samagotchi/llm/adapters.rb
|
|
325
|
+
- lib/samagotchi/llm/anthropic_assembly.rb
|
|
326
|
+
- lib/samagotchi/llm/anthropic_messages.rb
|
|
327
|
+
- lib/samagotchi/llm/anthropic_wire.rb
|
|
323
328
|
- lib/samagotchi/llm/api_key.rb
|
|
324
329
|
- lib/samagotchi/llm/backend.rb
|
|
325
330
|
- lib/samagotchi/llm/chat_loop.rb
|
|
331
|
+
- lib/samagotchi/llm/dialect.rb
|
|
326
332
|
- lib/samagotchi/llm/errors.rb
|
|
327
333
|
- lib/samagotchi/llm/http.rb
|
|
334
|
+
- lib/samagotchi/llm/model_listing.rb
|
|
328
335
|
- lib/samagotchi/llm/model_result.rb
|
|
329
336
|
- lib/samagotchi/llm/native_backend.rb
|
|
330
337
|
- lib/samagotchi/llm/native_tool_normalizer.rb
|
|
@@ -554,6 +561,7 @@ files:
|
|
|
554
561
|
- lib/samagotchi/web/public/copy.js
|
|
555
562
|
- lib/samagotchi/web/public/ctx.js
|
|
556
563
|
- lib/samagotchi/web/public/data.js
|
|
564
|
+
- lib/samagotchi/web/public/delivery_line.js
|
|
557
565
|
- lib/samagotchi/web/public/diff_view.js
|
|
558
566
|
- lib/samagotchi/web/public/families_view.js
|
|
559
567
|
- lib/samagotchi/web/public/favicon.svg
|