samagotchi 0.42.0 → 0.44.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 +47 -1
- data/docs/configuration.md +26 -0
- data/docs/context.md +46 -0
- data/docs/internals/context-telemetry.md +4 -1
- data/docs/internals/engine.md +1 -0
- data/docs/plugins.md +27 -5
- data/docs/sessions.md +1 -1
- data/lib/samagotchi/bridge/card_store.rb +20 -9
- data/lib/samagotchi/bridge/turn_notice.rb +3 -1
- data/lib/samagotchi/bundles/github-pr/manifest.yml +4 -4
- data/lib/samagotchi/bundles/github-pr/plugin.rb +177 -1
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/context_sources.rb +40 -0
- data/lib/samagotchi/context_status.rb +18 -5
- data/lib/samagotchi/kernel_loop.rb +24 -9
- data/lib/samagotchi/llm/chat_loop.rb +4 -3
- data/lib/samagotchi/llm_context_forget.rb +6 -2
- data/lib/samagotchi/llm_context_notice.rb +96 -0
- data/lib/samagotchi/plugin/attached_context.rb +75 -11
- data/lib/samagotchi/terminal_ui/attached_loop.rb +2 -1
- data/lib/samagotchi/terminal_ui/event_renderer.rb +3 -0
- data/lib/samagotchi/terminal_ui/formatting.rb +7 -0
- data/lib/samagotchi/terminal_ui/one_shot_sink.rb +1 -0
- data/lib/samagotchi/tool_declarations.rb +2 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +47 -7
- data/lib/samagotchi/web/message_parts.rb +90 -11
- data/lib/samagotchi/web/public/app.js +80 -13
- data/lib/samagotchi/web/public/ctx.js +27 -2
- data/lib/samagotchi/web/public/data.js +6 -3
- data/lib/samagotchi/web/public/index.html +12 -0
- data/lib/samagotchi/web/public/llm_context_chip.js +151 -21
- data/lib/samagotchi/web/public/model_pick.js +3 -1
- data/lib/samagotchi/web/public/model_picker.js +2 -0
- data/lib/samagotchi/web/public/session_state.js +2 -0
- data/lib/samagotchi/web/public/turn_events.js +34 -0
- data/lib/samagotchi/web/public/turn_html.js +33 -2
- data/lib/samagotchi/web/public/turn_view.js +4 -2
- metadata +2 -1
|
@@ -156,6 +156,39 @@ export function steerCutLine({ source } = {}) {
|
|
|
156
156
|
return `↪ cut in for ${STEER_CUT_FOR[source] || "your message"}`;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
// The ✂ row of a batch of LLM context edits the server applied
|
|
160
|
+
// (llm_context_edited, LLMContextNotice): its line as the server built
|
|
161
|
+
// it ("✂ forgot 2 outputs, stubbed 1 stale read · frees ~4.1k tokens"),
|
|
162
|
+
// so the web and the TUI say the same.
|
|
163
|
+
export function llmContextLine({ text } = {}) {
|
|
164
|
+
return text || "✂ LLM context edited";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// The ✂ row's hover: what each group did, one line per output (its id,
|
|
168
|
+
// tool and title), and what the batch costs ("~": chars/4).
|
|
169
|
+
export function llmContextHover({ groups = [], freed_tokens: freed, tail_tokens: tail, staged, moment } = {}) {
|
|
170
|
+
const lines = [];
|
|
171
|
+
const output = (item) => [item.id, item.tool, item.title].filter(Boolean).join(" ");
|
|
172
|
+
for (const group of Array.isArray(groups) ? groups : []) {
|
|
173
|
+
const items = Array.isArray(group.items) ? group.items : [];
|
|
174
|
+
if (group.kind === "stale") {
|
|
175
|
+
lines.push("Stubbed as stale:");
|
|
176
|
+
for (const item of items) lines.push(` ${output(item)}${item.note ? ` — ${item.note}` : ""}`);
|
|
177
|
+
} else if (group.kind === "forget") {
|
|
178
|
+
lines.push(`Forgotten by the ${group.by || "model"}: ${group.note || ""}`);
|
|
179
|
+
for (const item of items) lines.push(` ${output(item)}${item.kept ? ` · lines ${item.kept} kept` : ""}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const cost = [];
|
|
183
|
+
if (Number(freed) > 0) cost.push(`frees ~${freed} tokens`);
|
|
184
|
+
if (Number(tail) > 0) cost.push(`the server reads ~${tail} tokens again`);
|
|
185
|
+
if (cost.length) lines.push(cost.join("; "));
|
|
186
|
+
if (Number(staged) > 0) lines.push(`${staged} more staged until the turn ends`);
|
|
187
|
+
lines.push(moment === "turn_end" ? "Sent from the next turn on." : "Sent from the next request on.");
|
|
188
|
+
lines.push("The session keeps the originals.");
|
|
189
|
+
return lines.join("\n");
|
|
190
|
+
}
|
|
191
|
+
|
|
159
192
|
// The one muted line for a turn that ended with no answer (turn_completed's
|
|
160
193
|
// turn_summary.empty_answer, or a reloaded turn's marker): the TUI words
|
|
161
194
|
// it the same (TurnNote.empty_answer_line; spec/shared/labels_matrix.json).
|
|
@@ -170,6 +203,7 @@ export function emptyAnswerLine({ retries = 0 } = {}) {
|
|
|
170
203
|
export function noticeLine(entry = {}) {
|
|
171
204
|
if (entry.type === "empty_answer_retry") return emptyRetryLine(entry);
|
|
172
205
|
if (entry.type === "steer_cut") return steerCutLine(entry);
|
|
206
|
+
if (entry.type === "llm_context_edited") return llmContextLine(entry);
|
|
173
207
|
return `${hookNoticeLabel(entry.hook)}: ${entry.text || ""}`;
|
|
174
208
|
}
|
|
175
209
|
|
|
@@ -33,12 +33,43 @@ function statusWordHtml({ cls, word }) {
|
|
|
33
33
|
return word ? `<span class="activity-state ${cls}">${word}</span>` : "";
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// A reloaded row's ✂ mark: what an LLM context edit saved on its output
|
|
37
|
+
// sends the model instead (MessageParts.edit_marks): a stale stub and
|
|
38
|
+
// what it frees, a forget's note (or the output whose row carries it) and
|
|
39
|
+
// its kept lines, a forget still staged. The live rows get none (a reload
|
|
40
|
+
// shows them).
|
|
41
|
+
export function llmEditLine(edit) {
|
|
42
|
+
if (!edit || typeof edit !== "object") return "";
|
|
43
|
+
const kept = edit.kept ? ` · lines ${edit.kept} kept` : "";
|
|
44
|
+
if (edit.kind === "forget") {
|
|
45
|
+
if (edit.staged) return `✂ forget staged${kept}`;
|
|
46
|
+
return `${edit.with ? `✂ forgotten with ${edit.with}` : `✂ forgotten: ${edit.note || ""}`}${kept}`;
|
|
47
|
+
}
|
|
48
|
+
if (edit.kind === "stale") {
|
|
49
|
+
const tokens = Number(edit.tokens) > 0 ? ` · ~${compactTokens(Number(edit.tokens))}` : "";
|
|
50
|
+
return `✂ stubbed: ${edit.note || "stale"}${tokens}`;
|
|
51
|
+
}
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function compactTokens(n) {
|
|
56
|
+
return n >= 1000 ? `${(n / 1000).toFixed(1)}k tokens` : `${n} tokens`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function llmEditHtml(row) {
|
|
60
|
+
const line = llmEditLine(row.edit);
|
|
61
|
+
if (!line) return "";
|
|
62
|
+
const hover = `${row.tool_id ? `${row.tool_id}: ` : ""}${line}\nThe model is sent this instead of the output; the session keeps it.`;
|
|
63
|
+
return `<div class="activity-edit" title="${escapeHtml(hover)}">${escapeHtml(line)}</div>`;
|
|
64
|
+
}
|
|
65
|
+
|
|
36
66
|
// A tool row: its status mark, tool, title (else params; the full command,
|
|
37
67
|
// else the full params line, on hover), the word an exception shows, the
|
|
38
68
|
// call's duration, the full command (an execute/task_create's view, also
|
|
39
69
|
// while it runs), the output once done (its "[tool]" tag dropped, 300 chars,
|
|
40
70
|
// the rest on hover), its images (+thumbs+(images) draws them) and an
|
|
41
|
-
// edit's collapsed diff
|
|
71
|
+
// edit's collapsed diff, and a reloaded row's ✂ mark above its output. A
|
|
72
|
+
// part with nothing to say is left out.
|
|
42
73
|
export function toolRowHtml(row, { thumbs = () => "" } = {}) {
|
|
43
74
|
return `<div class="activity-row" data-key="${escapeHtml(row.key)}">${toolRowInnerHtml(row, { thumbs })}</div>`;
|
|
44
75
|
}
|
|
@@ -55,7 +86,7 @@ export function toolRowInnerHtml(row, { thumbs = () => "" } = {}) {
|
|
|
55
86
|
const status = statusParts(row.status);
|
|
56
87
|
return `${statusMarkHtml(status)}<span class="activity-tool">${escapeHtml(toolName(row))}</span>${params}${statusWordHtml(status)}` +
|
|
57
88
|
`${duration ? `<span class="activity-duration">${escapeHtml(duration)}</span>` : ""}` +
|
|
58
|
-
`${commandBlockHtml(row.view, row.title)}${output}` +
|
|
89
|
+
`${commandBlockHtml(row.view, row.title)}${llmEditHtml(row)}${output}` +
|
|
59
90
|
`${row.images?.length ? thumbs(row.images) : ""}${row.diff ? activityDiffHtml(row.diff) : ""}`;
|
|
60
91
|
}
|
|
61
92
|
|
|
@@ -422,15 +422,17 @@ export function createTurnView({
|
|
|
422
422
|
// A hook's line (hook_notice) as a row of the current step, above the row
|
|
423
423
|
// of the call it judged; false with no running turn (app.js shows the
|
|
424
424
|
// bubble then). data.line is a whole row of chi's own (an empty-answer
|
|
425
|
-
// retry), shown as is.
|
|
425
|
+
// retry), shown as is; data.cls a class of its own and data.title its
|
|
426
|
+
// hover (a ✂ row's).
|
|
426
427
|
function notice(data) {
|
|
427
428
|
const r = turn && model.notice(turn, data);
|
|
428
429
|
if (!r) return false;
|
|
429
430
|
const entry = genEls.get(r.gen);
|
|
430
431
|
if (!r.gen.text) entry.text.textContent = "";
|
|
431
432
|
const el = document.createElement("div");
|
|
432
|
-
el.className = "hook-notice" + (data.level === "warn" ? " warn" : "");
|
|
433
|
+
el.className = "hook-notice" + (data.level === "warn" ? " warn" : "") + (data.cls ? ` ${data.cls}` : "");
|
|
433
434
|
el.textContent = data.line || `${hookNoticeLabel(data.hook)}: ${data.text || ""}`;
|
|
435
|
+
if (data.title) el.title = data.title;
|
|
434
436
|
const stick = isNearBottom();
|
|
435
437
|
activityEl(r.gen).appendChild(el);
|
|
436
438
|
if (stick) follow();
|
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.44.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -315,6 +315,7 @@ files:
|
|
|
315
315
|
- lib/samagotchi/llm_context_command.rb
|
|
316
316
|
- lib/samagotchi/llm_context_edit.rb
|
|
317
317
|
- lib/samagotchi/llm_context_forget.rb
|
|
318
|
+
- lib/samagotchi/llm_context_notice.rb
|
|
318
319
|
- lib/samagotchi/llm_context_override.rb
|
|
319
320
|
- lib/samagotchi/llm_context_stale.rb
|
|
320
321
|
- lib/samagotchi/llm_context_strategy.rb
|