samagotchi 0.14.0 → 0.16.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 +77 -1
- data/docs/architecture.md +10 -0
- data/docs/cli.md +3 -2
- data/docs/configuration.md +3 -1
- data/docs/guardrails.md +16 -2
- data/docs/plugins.md +3 -3
- data/docs/sessions.md +7 -2
- data/docs/sub-agent.md +15 -3
- data/lib/samagotchi/answer_display.rb +6 -2
- data/lib/samagotchi/bridge/card_store.rb +16 -1
- data/lib/samagotchi/bridge/turn_accumulator.rb +6 -0
- data/lib/samagotchi/bridge.rb +84 -0
- data/lib/samagotchi/bridge_client.rb +33 -6
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/self_map.md +2 -1
- data/lib/samagotchi/empty_answer_retry.rb +2 -1
- data/lib/samagotchi/engine.rb +58 -17
- data/lib/samagotchi/kernel_loop.rb +16 -5
- data/lib/samagotchi/llm/chat_loop.rb +17 -9
- data/lib/samagotchi/llm/model_result.rb +12 -7
- data/lib/samagotchi/log_subscriber.rb +1 -0
- data/lib/samagotchi/parent_report.rb +8 -0
- data/lib/samagotchi/plugin_session_state.rb +30 -0
- data/lib/samagotchi/question_desk.rb +97 -18
- data/lib/samagotchi/relay.rb +88 -0
- data/lib/samagotchi/relay_desk.rb +73 -0
- data/lib/samagotchi/relay_verifier.rb +113 -0
- data/lib/samagotchi/relay_watcher.rb +46 -0
- data/lib/samagotchi/reply_wait.rb +10 -1
- data/lib/samagotchi/self_report.rb +24 -4
- data/lib/samagotchi/session.rb +15 -2
- data/lib/samagotchi/session_manager.rb +5 -2
- data/lib/samagotchi/session_metrics.rb +18 -1
- data/lib/samagotchi/session_retention.rb +6 -1
- data/lib/samagotchi/sessions_command.rb +10 -4
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +11 -2
- data/lib/samagotchi/terminal_ui/attached_loop.rb +88 -12
- data/lib/samagotchi/terminal_ui/event_renderer.rb +6 -1
- data/lib/samagotchi/terminal_ui/formatting.rb +7 -0
- data/lib/samagotchi/terminal_ui/question_prompt.rb +48 -2
- data/lib/samagotchi/terminal_ui/question_slot.rb +5 -1
- data/lib/samagotchi/terminal_ui.rb +11 -0
- data/lib/samagotchi/tool_declarations.rb +2 -2
- data/lib/samagotchi/tools/delegate_relay.rb +216 -0
- data/lib/samagotchi/tools/delegate_wait.rb +72 -6
- data/lib/samagotchi/tools/peers.rb +6 -4
- data/lib/samagotchi/turn_note.rb +30 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +24 -0
- data/lib/samagotchi/web/public/app.js +32 -11
- data/lib/samagotchi/web/public/index.html +9 -0
- data/lib/samagotchi/web/public/notify.js +18 -5
- data/lib/samagotchi/web/public/question_card.js +45 -3
- data/lib/samagotchi/web/public/question_cards.js +49 -1
- data/lib/samagotchi/web/public/sessions_list.js +16 -1
- data/lib/samagotchi/web/public/stage_model.js +8 -3
- data/lib/samagotchi/web/public/stage_view.js +7 -2
- data/lib/samagotchi/web/public/strip.js +8 -1
- data/lib/samagotchi/web/public/timing.js +22 -5
- data/lib/samagotchi/web/public/turn_events.js +9 -0
- data/lib/samagotchi/web/public/turn_view.js +38 -13
- metadata +7 -1
|
@@ -16,7 +16,7 @@ import { activityDiffHtml } from "./diff_view.js";
|
|
|
16
16
|
import { escapeHtml, messageBodyHtml, noteHtml, normalize, steerRowHtml } from "./format.js";
|
|
17
17
|
import { thumbsHtml } from "./images.js";
|
|
18
18
|
import { shouldFollowScroll } from "./scroll.js";
|
|
19
|
-
import { hookNoticeLabel } from "./turn_events.js";
|
|
19
|
+
import { emptyAnswerLine, hookNoticeLabel } from "./turn_events.js";
|
|
20
20
|
import { cancelLineHtml, formatDuration, turnGroups, turnTimingText } from "./timing.js";
|
|
21
21
|
import { createSentenceFeed } from "./sentences.js";
|
|
22
22
|
import { createTicker } from "./thinking_ticker.js";
|
|
@@ -465,11 +465,15 @@ export function createTurnView({
|
|
|
465
465
|
// +heldHeight+ (the live box's height, measured before the step closed) the
|
|
466
466
|
// bubble is held at that height, scrolled to its tail, until answerReady()
|
|
467
467
|
// (the rendered markdown landed) or 1.5 s reveals it.
|
|
468
|
-
|
|
468
|
+
// +endNote+: the notice of a turn that ended with no answer (app.js
|
|
469
|
+
// builds it), placed where the answer bubble would go.
|
|
470
|
+
function promote(gen, { heldHeight = 0, endNote = null } = {}) {
|
|
469
471
|
const entry = genEls.get(gen);
|
|
470
472
|
const bubble = entry.text;
|
|
471
|
-
|
|
472
|
-
|
|
473
|
+
// No text (an empty answer): no bubble, not even an empty one left in
|
|
474
|
+
// the step.
|
|
475
|
+
if (gen.text) bubble.className = "bubble output";
|
|
476
|
+
else bubble.remove();
|
|
473
477
|
// A hidden tab gets no pop: nobody sees it, and its timers are throttled.
|
|
474
478
|
const holding = useHold && heldHeight > 0 && !!gen.text && !document.hidden;
|
|
475
479
|
let holdHeight = 0;
|
|
@@ -512,11 +516,12 @@ export function createTurnView({
|
|
|
512
516
|
}
|
|
513
517
|
pieces.push(...cards);
|
|
514
518
|
if (gen.text) pieces.push(bubble);
|
|
519
|
+
else if (endNote) pieces.push(endNote);
|
|
515
520
|
placeAnswer(pieces, { plain: true, blockEl });
|
|
516
521
|
blockEl = null;
|
|
517
522
|
blockSummaryEl = null;
|
|
518
523
|
} else {
|
|
519
|
-
placeAnswer(gen.text ? [...cards, bubble] : cards, { plain: false, blockEl });
|
|
524
|
+
placeAnswer(gen.text ? [...cards, bubble] : [...cards, ...(endNote ? [endNote] : [])], { plain: false, blockEl });
|
|
520
525
|
// The last step, left with its thinking only, reads "thinking" once.
|
|
521
526
|
if (!gen.tools.length && gen.thinking) unwrapThinking(entry);
|
|
522
527
|
}
|
|
@@ -524,8 +529,10 @@ export function createTurnView({
|
|
|
524
529
|
}
|
|
525
530
|
|
|
526
531
|
// Returns the cards that left the block (card.js leavesBlock), detached:
|
|
527
|
-
// app.js puts them after the turn's end line.
|
|
528
|
-
|
|
532
|
+
// app.js puts them after the turn's end line. +endNote+ (a completed turn
|
|
533
|
+
// with no answer): its notice, in the answer's place; left unplaced
|
|
534
|
+
// with no turn to end (app.js then shows it itself).
|
|
535
|
+
function turnEnded({ kind, endNote = null }) {
|
|
529
536
|
// Once per turn: a second end (a re-dispatched event) would promote again.
|
|
530
537
|
if (!turn || turn.ended) return [];
|
|
531
538
|
const last = model.currentGen(turn);
|
|
@@ -541,7 +548,8 @@ export function createTurnView({
|
|
|
541
548
|
const heldHeight = box ? box.getBoundingClientRect().height : 0;
|
|
542
549
|
if (pendingSteers.length) flushSteers();
|
|
543
550
|
apply(model.turnEnded(turn));
|
|
544
|
-
if (promoting) promote(last, { heldHeight });
|
|
551
|
+
if (promoting) promote(last, { heldHeight, endNote });
|
|
552
|
+
else if (endNote) placeAnswer([endNote], { plain: false, blockEl });
|
|
545
553
|
const kept = [];
|
|
546
554
|
if (blockEl) {
|
|
547
555
|
updateBlockSummary();
|
|
@@ -678,6 +686,13 @@ function reloadRowHtml(r, thumbs) {
|
|
|
678
686
|
`${r.images?.length ? thumbs(r.images) : ""}${r.diff ? activityDiffHtml(r.diff) : ""}</div>`;
|
|
679
687
|
}
|
|
680
688
|
|
|
689
|
+
// The notice of a turn that ended with no answer: a muted row like the
|
|
690
|
+
// loop's retry row, where the answer would be (live: promote()).
|
|
691
|
+
export const EMPTY_ANSWER_CLASS = "bubble hook-notice empty-answer";
|
|
692
|
+
function emptyAnswerHtml(item) {
|
|
693
|
+
return `<div class="${EMPTY_ANSWER_CLASS}">${escapeHtml(emptyAnswerLine(item))}</div>`;
|
|
694
|
+
}
|
|
695
|
+
|
|
681
696
|
// A reloaded history in the turn view (renderHistory's object path): per
|
|
682
697
|
// turn the prompt, a collapsed block of its steps when it had any (texts,
|
|
683
698
|
// and with the server's parts the thinking, params and output, from the
|
|
@@ -687,8 +702,11 @@ function reloadRowHtml(r, thumbs) {
|
|
|
687
702
|
export function turnHistoryHtml(items, timing, { thumbs }) {
|
|
688
703
|
const timingHtml = (group) => {
|
|
689
704
|
const record = group.record;
|
|
705
|
+
// Numbered by its place in the turn records, as live (a failed turn
|
|
706
|
+
// whose prompt went back still counts).
|
|
707
|
+
const at = (timing?.turnRecords || []).indexOf(record);
|
|
690
708
|
return record && formatDuration(record.duration_ms)
|
|
691
|
-
? `<div class="turn-timing">${escapeHtml(turnTimingText(group.turnIndex + 1, record.duration_ms, { canceled: record.status === "canceled" }))}</div>`
|
|
709
|
+
? `<div class="turn-timing">${escapeHtml(turnTimingText(at >= 0 ? at + 1 : group.turnIndex + 1, record.duration_ms, { canceled: record.status === "canceled" }))}</div>`
|
|
692
710
|
: "";
|
|
693
711
|
};
|
|
694
712
|
// The timing line after the bubble, not in it: where the live view
|
|
@@ -705,9 +723,13 @@ export function turnHistoryHtml(items, timing, { thumbs }) {
|
|
|
705
723
|
const prompt = items[group.user];
|
|
706
724
|
const parts = [`<div class="bubble user"${copySourceAttr(prompt, false)}><div class="user-message">${messageBodyHtml(prompt).body}${thumbs(prompt.images)}</div></div>`];
|
|
707
725
|
const [only] = group.steps;
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
726
|
+
const ended = group.answer !== null || group.emptyAnswer != null;
|
|
727
|
+
const textless = only && (only.i === null || !String(items[only.i].content ?? ""));
|
|
728
|
+
// A lone thinking step before an answer (or the notice of a turn with
|
|
729
|
+
// none): no block, as live.
|
|
730
|
+
const plainThinking = group.steps.length === 1 && ended && textless && !only.tools.length && only.thinking;
|
|
731
|
+
const noBlock = !group.steps.length || plainThinking;
|
|
732
|
+
if (plainThinking) {
|
|
711
733
|
// A plain answer with thinking: the collapsed thinking block, as live.
|
|
712
734
|
parts.push(`<details class="bubble thinking"><summary>thinking</summary><div class="thinking-body">${escapeHtml(only.thinking)}</div></details>`);
|
|
713
735
|
} else if (group.steps.length) {
|
|
@@ -733,9 +755,12 @@ export function turnHistoryHtml(items, timing, { thumbs }) {
|
|
|
733
755
|
// after its steps and before its answer.
|
|
734
756
|
parts.push((group.merged || []).map((i) =>
|
|
735
757
|
`<div class="bubble user steered" data-user-state="steered"${items[i].step ? ` data-step="${Number(items[i].step)}"` : ""}${copySourceAttr(items[i], false)}><div class="user-message">${messageBodyHtml(items[i]).body}${thumbs(items[i].images)}</div><span class="state-badge">steered</span></div>`).join(""));
|
|
758
|
+
// A turn that ended with no answer: chi's muted notice where the answer
|
|
759
|
+
// would be, before the timing, as live.
|
|
760
|
+
if (group.emptyAnswer != null) parts.push(`${emptyAnswerHtml(items[group.emptyAnswer])}${timingHtml(group)}`);
|
|
736
761
|
// A turn with no answer (canceled, or running: no record yet) shows its
|
|
737
762
|
// timing after its prompt and steps, where the live view leaves it.
|
|
738
|
-
if (group.answer === null) parts.push(timingHtml(group));
|
|
763
|
+
else if (group.answer === null) parts.push(timingHtml(group));
|
|
739
764
|
else parts.push(answerHtml(items[group.answer], timingHtml(group)));
|
|
740
765
|
// A canceled turn ends with its cancel line, as the live view left it.
|
|
741
766
|
parts.push(cancelLineHtml(group.record, escapeHtml));
|
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.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -301,6 +301,7 @@ files:
|
|
|
301
301
|
- lib/samagotchi/plugin/sessions.rb
|
|
302
302
|
- lib/samagotchi/plugin/side_question.rb
|
|
303
303
|
- lib/samagotchi/plugin/tool_result.rb
|
|
304
|
+
- lib/samagotchi/plugin_session_state.rb
|
|
304
305
|
- lib/samagotchi/plugin_tasks.rb
|
|
305
306
|
- lib/samagotchi/project_scope.rb
|
|
306
307
|
- lib/samagotchi/prompt.rb
|
|
@@ -309,6 +310,10 @@ files:
|
|
|
309
310
|
- lib/samagotchi/question_desk.rb
|
|
310
311
|
- lib/samagotchi/recap_setup.rb
|
|
311
312
|
- lib/samagotchi/recap_store.rb
|
|
313
|
+
- lib/samagotchi/relay.rb
|
|
314
|
+
- lib/samagotchi/relay_desk.rb
|
|
315
|
+
- lib/samagotchi/relay_verifier.rb
|
|
316
|
+
- lib/samagotchi/relay_watcher.rb
|
|
312
317
|
- lib/samagotchi/reminder_queue.rb
|
|
313
318
|
- lib/samagotchi/reminder_store.rb
|
|
314
319
|
- lib/samagotchi/reply_wait.rb
|
|
@@ -365,6 +370,7 @@ files:
|
|
|
365
370
|
- lib/samagotchi/tools/builtins.rb
|
|
366
371
|
- lib/samagotchi/tools/cancel_reminder.rb
|
|
367
372
|
- lib/samagotchi/tools/delegate.rb
|
|
373
|
+
- lib/samagotchi/tools/delegate_relay.rb
|
|
368
374
|
- lib/samagotchi/tools/delegate_result.rb
|
|
369
375
|
- lib/samagotchi/tools/delegate_wait.rb
|
|
370
376
|
- lib/samagotchi/tools/edit.rb
|