samagotchi 0.97.0 → 0.98.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 +30 -1
- data/docs/architecture.md +10 -2
- data/docs/bundle-authoring.md +5 -3
- data/docs/cli.md +8 -3
- data/docs/hooks.md +59 -0
- data/docs/internals/bundles.md +1 -1
- data/docs/internals/engine.md +18 -0
- data/docs/plugins.md +8 -1
- data/docs/sessions.md +1 -1
- data/lib/samagotchi/answer_refs.rb +16 -9
- data/lib/samagotchi/bundle_command.rb +2 -2
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/source-links/hooks/source_links.rb +39 -5
- data/lib/samagotchi/bundles/source-links/manifest.yml +5 -5
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/engine.rb +26 -2
- data/lib/samagotchi/hooks/bundle_loader.rb +18 -15
- data/lib/samagotchi/hooks.rb +4 -1
- data/lib/samagotchi/llm/errors.rb +51 -17
- data/lib/samagotchi/memory_bundle/bundle_hook.rb +28 -9
- data/lib/samagotchi/memory_bundle/manifest.rb +17 -0
- data/lib/samagotchi/output_formatter.rb +8 -2
- data/lib/samagotchi/pending_question.rb +7 -3
- data/lib/samagotchi/plugin/api.rb +5 -0
- data/lib/samagotchi/question_asked.rb +83 -0
- data/lib/samagotchi/question_desk.rb +3 -2
- data/lib/samagotchi/testing/bundle.rb +18 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +30 -9
- data/lib/samagotchi/web/public/app.js +44 -11
- data/lib/samagotchi/web/public/index.html +36 -13
- data/lib/samagotchi/web/public/info_bar_model.js +0 -2
- data/lib/samagotchi/web/public/lists_view.js +32 -8
- data/lib/samagotchi/web/public/markdown_place.js +25 -8
- data/lib/samagotchi/web/public/question_cards.js +23 -6
- data/lib/samagotchi/web/public/session_rename.js +88 -15
- data/lib/samagotchi/web/public/session_title.js +6 -0
- data/lib/samagotchi/web/public/stage_frame.js +24 -14
- data/lib/samagotchi/web/public/stage_model.js +4 -2
- data/lib/samagotchi/web/public/stage_slots.js +41 -9
- data/lib/samagotchi/web/public/stage_view.js +149 -8
- data/lib/samagotchi/web/public/stream_handlers.js +5 -1
- data/lib/samagotchi/web/public/turn_model.js +20 -0
- data/lib/samagotchi/web/public/turn_view.js +22 -9
- metadata +2 -1
|
@@ -174,6 +174,26 @@ export function askingText(turn) {
|
|
|
174
174
|
return gen.text.trim();
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
// A plugin's refs on the reply that asks (a question's refs, from its
|
|
178
|
+
// pending question): kept on the asking step's gen, the last one, while
|
|
179
|
+
// the turn goes on, for the stage's asking slot (askingRefs) and the
|
|
180
|
+
// step's closed text (turn_view.js). A non-array or an empty list keeps
|
|
181
|
+
// what the gen has. The gen, or null.
|
|
182
|
+
export function questionRefs(turn, refs) {
|
|
183
|
+
if (!turn || turn.ended || !Array.isArray(refs) || !refs.length) return null;
|
|
184
|
+
const gen = turn.gens[turn.gens.length - 1];
|
|
185
|
+
if (!gen) return null;
|
|
186
|
+
gen.refs = refs;
|
|
187
|
+
return gen;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// The refs on the asking text (questionRefs), while it shows
|
|
191
|
+
// (askingText's rule); else null.
|
|
192
|
+
export function askingRefs(turn) {
|
|
193
|
+
if (!askingText(turn)) return null;
|
|
194
|
+
return turn.gens[turn.gens.length - 1].refs || null;
|
|
195
|
+
}
|
|
196
|
+
|
|
177
197
|
// The turn is over (completed, canceled, failed, the worker gone).
|
|
178
198
|
export function turnEnded(turn) {
|
|
179
199
|
const closed = closeCurrent(turn);
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// chrome (CSS :has), so a plain answer streams like today's bubble, and is
|
|
22
22
|
// removed at the end: the answer bubble takes its place.
|
|
23
23
|
import { html, nothing, render } from "./vendor/lit-html.js";
|
|
24
|
-
import { setAnswerRefs } from "./answer_refs.js";
|
|
24
|
+
import { answerRefsOf, setAnswerRefs } from "./answer_refs.js";
|
|
25
25
|
import { bindRowRefs } from "./refs.js";
|
|
26
26
|
import { leavesBlock } from "./card.js";
|
|
27
27
|
import { failedTurnText, normalize, sourceLabel } from "./format.js";
|
|
@@ -70,13 +70,14 @@ function thinkingTemplate({ body, wrapped = true, line = null, cls = "thinking",
|
|
|
70
70
|
// the newest last), the live "…" until the first one, or a closed step's
|
|
71
71
|
// full text. None when there is nothing to say. A closed step's text is
|
|
72
72
|
// set whole (.textContent): its refs (ref_scan.js) split it into spans
|
|
73
|
-
// that a text part's update would leave behind.
|
|
74
|
-
|
|
73
|
+
// that a text part's update would leave behind. +at+: a reloaded step's
|
|
74
|
+
// message index, for its refs (data-answer-at, as an answer's).
|
|
75
|
+
function textTemplate(text, { live, sentences, placeholder, at = null }) {
|
|
75
76
|
if (live && sentences?.length) {
|
|
76
77
|
return html`<div class="gen-text streaming"><div class="narration">${sentences.map((s) => html`<div class="sentence">${s}</div>`)}</div></div>`;
|
|
77
78
|
}
|
|
78
79
|
if (live) return placeholder ? html`<div class="gen-text streaming">…</div>` : nothing;
|
|
79
|
-
return text ? html`<div class="gen-text" .textContent=${text}></div>` : nothing;
|
|
80
|
+
return text ? html`<div class="gen-text" data-answer-at=${at ?? nothing} .textContent=${text}></div>` : nothing;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
// One step, live or reloaded. +gen+: { text, thinking, tools, answer } (a
|
|
@@ -86,16 +87,17 @@ function textTemplate(text, { live, sentences, placeholder }) {
|
|
|
86
87
|
// answers, its +items+ (tool rows, notices, cards: nodes or templates; a
|
|
87
88
|
// reload's are its rows' nodes), the thinking +body+ node, the ticker's
|
|
88
89
|
// +line+, the narration's +sentences+ and "…" +placeholder+, +on+ (the
|
|
89
|
-
// click and toggle handlers).
|
|
90
|
+
// click and toggle handlers). A reload's +at+: the step's message index
|
|
91
|
+
// (textTemplate).
|
|
90
92
|
export function stepTemplate(gen, st = {}) {
|
|
91
93
|
const { live = false, index = 0, open = live, steers = [], items = [], body = gen.thinking, line = null,
|
|
92
|
-
sentences = null, placeholder = false, on = {} } = st;
|
|
94
|
+
sentences = null, placeholder = false, on = {}, at = null } = st;
|
|
93
95
|
const text = gen.answer ? "" : gen.text || "";
|
|
94
96
|
const label = live ? { repeats: false, content: `step ${index + 1}` } : closedLabel({ ...gen, text });
|
|
95
97
|
const failed = !live && model.failedCount(gen.tools) > 0;
|
|
96
98
|
// A step left with its thinking only reads "thinking" once.
|
|
97
99
|
const wrapped = live || !!text || gen.tools.length > 0;
|
|
98
|
-
return html`<details class=${`gen${live ? " live" : ""}${failed ? " has-failed" : ""}`} .open=${open}><summary class=${label.repeats ? "repeats" : nothing} @click=${on.stepClick ?? nothing}>${label.content}</summary>${steers}${gen.thinking ? thinkingTemplate({ body, wrapped, line, on }) : nothing}${textTemplate(text, { live, sentences, placeholder })}${items.length ? html`<div class="activity-body">${items}</div>` : nothing}</details>`;
|
|
100
|
+
return html`<details class=${`gen${live ? " live" : ""}${failed ? " has-failed" : ""}`} .open=${open}><summary class=${label.repeats ? "repeats" : nothing} @click=${on.stepClick ?? nothing}>${label.content}</summary>${steers}${gen.thinking ? thinkingTemplate({ body, wrapped, line, on }) : nothing}${textTemplate(text, { live, sentences, placeholder, at })}${items.length ? html`<div class="activity-body">${items}</div>` : nothing}</details>`;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
// A hook's line (hook_notice) as a step's row. data.line is a whole row of
|
|
@@ -254,6 +256,10 @@ export function createTurnView({
|
|
|
254
256
|
live: !entry.closed, index: entry.index, open: entry.open, steers: entry.steers, items: entry.items,
|
|
255
257
|
body: entry.thinkingText, line: entry.line, sentences: entry.sentences, placeholder: entry.placeholder, on: entry.on,
|
|
256
258
|
}), entry.frag, { renderBefore: entry.end });
|
|
259
|
+
// A closed step that asked a question knows its refs (model.questionRefs):
|
|
260
|
+
// the page pairs them with its text at the turn's end (linkTextRefs).
|
|
261
|
+
const text = entry.closed && entry.gen.refs ? entry.el?.querySelector(":scope > .gen-text") : null;
|
|
262
|
+
if (text && !answerRefsOf(text)) setAnswerRefs(text, entry.gen.refs);
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
// A step's nodes (lit's start marker, the <details>, the end marker), to
|
|
@@ -776,6 +782,10 @@ export function createTurnView({
|
|
|
776
782
|
// The running turn's asking reply (turn_model.js askingText): what a
|
|
777
783
|
// question or approval card keeps once answered.
|
|
778
784
|
askingText: () => model.askingText(turn),
|
|
785
|
+
// A question's refs (its pending question's) on the step that asks it.
|
|
786
|
+
questionRefs(refs) {
|
|
787
|
+
if (turn) model.questionRefs(turn, refs);
|
|
788
|
+
},
|
|
779
789
|
step(i) {
|
|
780
790
|
const entry = turn && genEls.get(turn.gens[i]);
|
|
781
791
|
return entry ? { el: entry.el, summary: entry.el.querySelector(":scope > summary"), thinkingWrap: thinkingWrap(entry) } : null;
|
|
@@ -880,6 +890,8 @@ export function turnHistoryNodes(items, timing, { thumbs }) {
|
|
|
880
890
|
// The steers the step answered first, where the live rows went.
|
|
881
891
|
steers: (group.steps[k].steers || []).map((i) => steerTemplate(i, false)),
|
|
882
892
|
items: gen.tools.length ? [rowNodes(gen.tools, thumbs)] : [],
|
|
893
|
+
// Its text knows its message's refs (a question's), as an answer does.
|
|
894
|
+
at: group.steps[k].i,
|
|
883
895
|
}));
|
|
884
896
|
parts.push(html`<details class="turn-work done"><summary>${model.blockSummary({ gens, ended: true })}</summary>${steps}</details>`);
|
|
885
897
|
outlines.push(gens);
|
|
@@ -910,8 +922,9 @@ export function turnHistoryNodes(items, timing, { thumbs }) {
|
|
|
910
922
|
const out = nodesOf(parts);
|
|
911
923
|
// A finished turn's block as the live end leaves it: its outline, open.
|
|
912
924
|
[...out.children].filter((el) => el.matches(".turn-work")).forEach((block, i) => outlineBlock(block, outlines[i] || []));
|
|
913
|
-
// Each answer bubble knows its message's refs
|
|
914
|
-
// page pairs them after the render), the index
|
|
925
|
+
// Each answer bubble and step text knows its message's refs
|
|
926
|
+
// (answer_refs.js; the page pairs them after the render), the index
|
|
927
|
+
// stays out of the DOM.
|
|
915
928
|
for (const bubble of out.querySelectorAll("[data-answer-at]")) {
|
|
916
929
|
setAnswerRefs(bubble, items[Number(bubble.dataset.answerAt)]?.display_refs);
|
|
917
930
|
bubble.removeAttribute("data-answer-at");
|
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.98.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -485,6 +485,7 @@ files:
|
|
|
485
485
|
- lib/samagotchi/prompt_note.rb
|
|
486
486
|
- lib/samagotchi/prompt_warmup.rb
|
|
487
487
|
- lib/samagotchi/question_answer.rb
|
|
488
|
+
- lib/samagotchi/question_asked.rb
|
|
488
489
|
- lib/samagotchi/question_desk.rb
|
|
489
490
|
- lib/samagotchi/queued_command.rb
|
|
490
491
|
- lib/samagotchi/reasoning_history.rb
|