samagotchi 0.78.0 → 0.79.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 +15 -1
- data/docs/configuration.md +4 -2
- data/docs/internals/anthropic.md +9 -4
- data/docs/internals/engine.md +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/llm/adapter_common.rb +4 -2
- data/lib/samagotchi/llm/anthropic_binding_recovery.rb +195 -0
- data/lib/samagotchi/llm/anthropic_messages.rb +37 -177
- data/lib/samagotchi/llm/anthropic_wire.rb +7 -1
- data/lib/samagotchi/llm/chat_loop.rb +21 -12
- data/lib/samagotchi/llm/model_listing.rb +29 -1
- data/lib/samagotchi/llm/refusal_memo.rb +3 -2
- data/lib/samagotchi/question_desk.rb +10 -3
- data/lib/samagotchi/reasoning_history.rb +2 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/public/app.js +32 -222
- data/lib/samagotchi/web/public/composer_grip.js +231 -0
- data/lib/samagotchi/web/public/index.html +2 -2
- data/lib/samagotchi/web/public/model_picker.js +4 -5
- data/lib/samagotchi/web/public/stage_view.js +3 -2
- data/lib/samagotchi/web/public/storage.js +23 -0
- data/lib/samagotchi/web/public/toasts.js +5 -4
- metadata +4 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { GRIP_OPEN_PX, gripFloor, gripFolds } from "./composer_size.js";
|
|
2
|
+
import { COMPOSER_FOLD_KEY, composerFolded, foldHandle, opensOnKey } from "./composer_fold.js";
|
|
3
|
+
import { readStored, removeStored, writeStored } from "./storage.js";
|
|
4
|
+
|
|
5
|
+
// The composer's grip and fold (used by app.js): the grip's drag, tap and keys, the
|
|
6
|
+
// fold's state per visit and its page-wide listeners. app.js keeps the box's
|
|
7
|
+
// height (fitPrompt) and the grip's floor (+floor+ / +setFloor+), which
|
|
8
|
+
// fitPrompt reads; the floor is saved under GRIP_KEY.
|
|
9
|
+
export const GRIP_KEY = "chi_composer_floor";
|
|
10
|
+
|
|
11
|
+
// +session+: the open session's id, or null (the start page). +draft+,
|
|
12
|
+
// +waiting+, +turnRunning+, +allViewOpen+, +lightboxOpen+: what the page
|
|
13
|
+
// shows now. +imageFiles+ / +addImageFiles+: a drop's images and the chips.
|
|
14
|
+
export function createComposerGrip({
|
|
15
|
+
composeGrip, promptEl, infoCancelBtn, infoBarEl,
|
|
16
|
+
session, fitPrompt, promptMinPx, floor, setFloor,
|
|
17
|
+
draft, waiting, turnRunning, allViewOpen, lightboxOpen,
|
|
18
|
+
imageFiles, addImageFiles,
|
|
19
|
+
doc = globalThis.document, win = globalThis.window,
|
|
20
|
+
}) {
|
|
21
|
+
// The composer's fold (composer_fold.js): the grip, which is the handle
|
|
22
|
+
// while folded, and the info bar's Stop turn while folded (the composer's
|
|
23
|
+
// #cancelBtn is folded away then). +gripWillFold+: the drag in progress is
|
|
24
|
+
// past the fold threshold.
|
|
25
|
+
const gripStartTitle = composeGrip.title;
|
|
26
|
+
let gripWillFold = false;
|
|
27
|
+
// This visit's state (a visit is one select() call): opened by the page
|
|
28
|
+
// (a focus, a restored line), archived (decided once, from the visit's
|
|
29
|
+
// first getSession answer).
|
|
30
|
+
const newFoldVisit = () => ({ open: false, archived: false, decided: false });
|
|
31
|
+
let foldVisit = newFoldVisit();
|
|
32
|
+
|
|
33
|
+
// A drag down past the box folds the composer on release (a session only: the
|
|
34
|
+
// start page's grip only resizes); from folded, a pull up opens it and the box
|
|
35
|
+
// follows the pointer in the same gesture, a tap opens it and focuses the box.
|
|
36
|
+
// No click listener: a click still fires after pointerdown's preventDefault
|
|
37
|
+
// and would toggle twice.
|
|
38
|
+
composeGrip.addEventListener("pointerdown", (e) => {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
composeGrip.setPointerCapture(e.pointerId);
|
|
41
|
+
composeGrip.classList.add("dragging");
|
|
42
|
+
const tapY = e.clientY;
|
|
43
|
+
let startY = tapY;
|
|
44
|
+
let startH = promptEl.offsetHeight;
|
|
45
|
+
let opened = false;
|
|
46
|
+
const move = (ev) => {
|
|
47
|
+
if (session() && isComposerFolded()) {
|
|
48
|
+
if (startY - ev.clientY < GRIP_OPEN_PX) return;
|
|
49
|
+
setComposerFolded(false);
|
|
50
|
+
opened = true;
|
|
51
|
+
// Re-based after the unfold (its fitPrompt sized the box: the minimum
|
|
52
|
+
// when empty, the text's height with a draft).
|
|
53
|
+
startH = promptEl.offsetHeight;
|
|
54
|
+
startY = ev.clientY;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const requested = startH + (startY - ev.clientY);
|
|
58
|
+
setFloor(gripFloor(requested, win.innerHeight, promptMinPx()));
|
|
59
|
+
fitPrompt();
|
|
60
|
+
const willFold = !!session() && gripFolds({ requestedPx: requested, shownPx: promptEl.offsetHeight });
|
|
61
|
+
if (willFold !== gripWillFold) {
|
|
62
|
+
gripWillFold = willFold;
|
|
63
|
+
composeGrip.classList.toggle("will-fold", willFold);
|
|
64
|
+
drawFoldControls();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const end = () => {
|
|
68
|
+
composeGrip.classList.remove("dragging");
|
|
69
|
+
composeGrip.removeEventListener("pointermove", move);
|
|
70
|
+
composeGrip.removeEventListener("pointerup", up);
|
|
71
|
+
composeGrip.removeEventListener("pointercancel", cancel);
|
|
72
|
+
const folds = gripWillFold;
|
|
73
|
+
gripWillFold = false;
|
|
74
|
+
composeGrip.classList.remove("will-fold");
|
|
75
|
+
return folds;
|
|
76
|
+
};
|
|
77
|
+
const up = (ev) => {
|
|
78
|
+
const folds = end();
|
|
79
|
+
if (folds) {
|
|
80
|
+
// The next opening starts at the minimum (or the text's height).
|
|
81
|
+
setFloor(null);
|
|
82
|
+
removeStored(GRIP_KEY);
|
|
83
|
+
setComposerFolded(true);
|
|
84
|
+
} else if (session() && isComposerFolded()) {
|
|
85
|
+
if (Math.abs(ev.clientY - tapY) < GRIP_OPEN_PX) {
|
|
86
|
+
setComposerFolded(false);
|
|
87
|
+
promptEl.focus();
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
writeStored(GRIP_KEY, String(floor() || ""));
|
|
91
|
+
// A pull that opened the box leaves the focus in it, as a tap does.
|
|
92
|
+
if (opened) promptEl.focus();
|
|
93
|
+
}
|
|
94
|
+
drawFoldControls();
|
|
95
|
+
};
|
|
96
|
+
const cancel = () => {
|
|
97
|
+
end();
|
|
98
|
+
drawFoldControls();
|
|
99
|
+
};
|
|
100
|
+
composeGrip.addEventListener("pointermove", move);
|
|
101
|
+
composeGrip.addEventListener("pointerup", up);
|
|
102
|
+
composeGrip.addEventListener("pointercancel", cancel);
|
|
103
|
+
});
|
|
104
|
+
composeGrip.addEventListener("dblclick", () => {
|
|
105
|
+
if (session() && isComposerFolded()) return;
|
|
106
|
+
setFloor(null);
|
|
107
|
+
removeStored(GRIP_KEY);
|
|
108
|
+
fitPrompt();
|
|
109
|
+
});
|
|
110
|
+
// Enter or Space on the focused handle folds, or opens and focuses the box
|
|
111
|
+
// (a session only; preventDefault: Space would scroll the page).
|
|
112
|
+
composeGrip.addEventListener("keydown", (e) => {
|
|
113
|
+
if (!session() || (e.key !== "Enter" && e.key !== " ")) return;
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
if (isComposerFolded()) {
|
|
116
|
+
setComposerFolded(false);
|
|
117
|
+
promptEl.focus();
|
|
118
|
+
} else {
|
|
119
|
+
setComposerFolded(true);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ── Composer fold (composer_fold.js) ───────────────────────────────────────
|
|
124
|
+
// Dragging the composer's grip down folds the session's composer away; the
|
|
125
|
+
// grip, now the handle on the info bar's top edge, pulled up or clicked, or
|
|
126
|
+
// typing on the page, brings it back. The user's own choice is saved per
|
|
127
|
+
// browser; an archived session opens folded for its visit, and expanding
|
|
128
|
+
// there saves nothing. The page's own focus or a restored line opens it
|
|
129
|
+
// for the visit only. The start page never folds.
|
|
130
|
+
function composerFoldSaved() {
|
|
131
|
+
return readStored(COMPOSER_FOLD_KEY) === "1";
|
|
132
|
+
}
|
|
133
|
+
function isComposerFolded() {
|
|
134
|
+
return !!session() && composerFolded({ saved: composerFoldSaved(), archivedVisit: foldVisit.archived, visitOpen: foldVisit.open });
|
|
135
|
+
}
|
|
136
|
+
function applyComposerFolded() {
|
|
137
|
+
const folded = isComposerFolded();
|
|
138
|
+
const was = doc.body.classList.contains("composer-folded");
|
|
139
|
+
doc.body.classList.toggle("composer-folded", folded);
|
|
140
|
+
// Its height was measured while hidden.
|
|
141
|
+
if (was && !folded) fitPrompt();
|
|
142
|
+
drawFoldControls();
|
|
143
|
+
}
|
|
144
|
+
// The user's own fold or unfold (the grip, typing): saved, unless this
|
|
145
|
+
// is an archived visit (both are for the visit then).
|
|
146
|
+
function setComposerFolded(folded) {
|
|
147
|
+
if (foldVisit.archived) {
|
|
148
|
+
foldVisit.open = !folded;
|
|
149
|
+
} else {
|
|
150
|
+
writeStored(COMPOSER_FOLD_KEY, folded ? "1" : "0");
|
|
151
|
+
foldVisit.open = false;
|
|
152
|
+
}
|
|
153
|
+
applyComposerFolded();
|
|
154
|
+
}
|
|
155
|
+
// The page opens it for this visit: nothing saved.
|
|
156
|
+
function openForVisit() {
|
|
157
|
+
if (!isComposerFolded()) return;
|
|
158
|
+
foldVisit.open = true;
|
|
159
|
+
applyComposerFolded();
|
|
160
|
+
}
|
|
161
|
+
// Every focus of the message box: in a session it opens a folded composer
|
|
162
|
+
// for the visit first (+open+ false: only when it shows already).
|
|
163
|
+
function focusPrompt({ open = true } = {}) {
|
|
164
|
+
if (session() && isComposerFolded()) {
|
|
165
|
+
if (!open) return;
|
|
166
|
+
openForVisit();
|
|
167
|
+
}
|
|
168
|
+
promptEl.focus();
|
|
169
|
+
}
|
|
170
|
+
// The grip as the fold's handle (its title, marks, aria-expanded) and Stop turn
|
|
171
|
+
// while folded. The start page's grip only resizes: no role, no fold state.
|
|
172
|
+
function drawFoldControls() {
|
|
173
|
+
const folded = isComposerFolded();
|
|
174
|
+
infoCancelBtn.style.display = folded && turnRunning() ? "" : "none";
|
|
175
|
+
if (!session()) {
|
|
176
|
+
composeGrip.removeAttribute("role");
|
|
177
|
+
composeGrip.removeAttribute("aria-expanded");
|
|
178
|
+
composeGrip.title = gripStartTitle;
|
|
179
|
+
for (const c of ["folded", "draft", "waiting"]) composeGrip.classList.remove(c);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const h = foldHandle({ folded, draft: draft(), waiting: folded && waiting() });
|
|
183
|
+
composeGrip.title = gripWillFold ? "Release to hide the message box" : h.title;
|
|
184
|
+
composeGrip.setAttribute("role", "button");
|
|
185
|
+
composeGrip.setAttribute("aria-expanded", folded ? "false" : "true");
|
|
186
|
+
composeGrip.classList.toggle("folded", h.folded);
|
|
187
|
+
composeGrip.classList.toggle("draft", h.draft);
|
|
188
|
+
composeGrip.classList.toggle("waiting", h.waiting);
|
|
189
|
+
}
|
|
190
|
+
promptEl.addEventListener("input", () => drawFoldControls());
|
|
191
|
+
// Typing on the page opens a folded composer: the key lands in the box
|
|
192
|
+
// (the class goes before the focus, so the browser types it there).
|
|
193
|
+
doc.addEventListener("keydown", (e) => {
|
|
194
|
+
if (!isComposerFolded()) return;
|
|
195
|
+
if (!opensOnKey(e, { active: doc.activeElement, allView: allViewOpen(), lightbox: lightboxOpen() })) return;
|
|
196
|
+
setComposerFolded(false);
|
|
197
|
+
promptEl.focus();
|
|
198
|
+
});
|
|
199
|
+
// A file dropped on the folded info bar: open (for the visit), then add it.
|
|
200
|
+
infoBarEl.addEventListener("dragover", (e) => {
|
|
201
|
+
if (!isComposerFolded() || ![...(e.dataTransfer?.types || [])].includes("Files")) return;
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
});
|
|
204
|
+
infoBarEl.addEventListener("drop", (e) => {
|
|
205
|
+
if (!isComposerFolded()) return;
|
|
206
|
+
const files = imageFiles(e.dataTransfer);
|
|
207
|
+
if (!files.length) return;
|
|
208
|
+
e.preventDefault();
|
|
209
|
+
openForVisit();
|
|
210
|
+
addImageFiles(files);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
isComposerFolded,
|
|
215
|
+
openForVisit,
|
|
216
|
+
focusPrompt,
|
|
217
|
+
drawFoldControls,
|
|
218
|
+
// A new visit (select(), the start page): nothing opened, not decided.
|
|
219
|
+
newVisit() {
|
|
220
|
+
foldVisit = newFoldVisit();
|
|
221
|
+
applyComposerFolded();
|
|
222
|
+
},
|
|
223
|
+
// The visit's first getSession answer: an archived session opens folded.
|
|
224
|
+
decideVisit(archived) {
|
|
225
|
+
if (foldVisit.decided) return;
|
|
226
|
+
foldVisit.decided = true;
|
|
227
|
+
foldVisit.archived = !!archived;
|
|
228
|
+
applyComposerFolded();
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
@@ -594,7 +594,7 @@ body.has-session #composer{flex-wrap:wrap;row-gap:6px}
|
|
|
594
594
|
#composeGrip::after{content:"";position:absolute;left:50%;top:3px;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--border-strong);opacity:0;transition:opacity .12s ease}
|
|
595
595
|
#composeGrip:hover::after,#composeGrip:has(+ #composer:hover)::after,#composeGrip.dragging::after{opacity:1}
|
|
596
596
|
#composeGrip:hover::after,#composeGrip.dragging::after{background:var(--accent)}
|
|
597
|
-
/* Folded (
|
|
597
|
+
/* Folded (composer_grip.js, composer_fold.js): the grip is the handle on the info bar's top edge, drawn always, wider so it reads as a control;
|
|
598
598
|
draft = the accent, waiting = the attention colour; .will-fold = a drag past the fold threshold (released it hides the box). */
|
|
599
599
|
body.has-session.composer-folded #composeGrip::after{opacity:1;width:48px;margin-left:-24px}
|
|
600
600
|
#composeGrip.draft::after{background:var(--accent)}
|
|
@@ -866,7 +866,7 @@ body.stage-view .turn-stage{position:absolute;left:0;right:0;bottom:100%;margin-
|
|
|
866
866
|
#infoBar #infoArchiveBtn:hover{color:var(--text);background:var(--bg-hover)}
|
|
867
867
|
#infoBar #infoDeleteBtn{background:transparent;color:var(--text-muted);border:1px solid transparent}
|
|
868
868
|
#infoBar #infoDeleteBtn:hover{color:var(--on-solid);background:var(--danger-bg);border-color:var(--danger-bg)}
|
|
869
|
-
/* The composer folded away (
|
|
869
|
+
/* The composer folded away (composer_grip.js: the grip dragged down, Enter/Space on it): the info bar is the page's bottom edge.
|
|
870
870
|
Stop turn comes into the bar while folded and a turn runs. */
|
|
871
871
|
body.has-session.composer-folded #composer,body.has-session.composer-folded #initRow{display:none}
|
|
872
872
|
body.has-session.composer-folded #infoBar{border-top-color:transparent}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// ↑/↓ PageUp/PageDown move, ⏎ picks, Esc and Tab close without a change.
|
|
11
11
|
|
|
12
12
|
import { moveSelection } from "./command_complete.js";
|
|
13
|
+
import { readStored, writeStored } from "./storage.js";
|
|
13
14
|
import {
|
|
14
15
|
MODEL_KEY, RECENT_KEY, groupRows, matchModels, modelRows, pickModel, pushRecent, recentRows, rowNote, rowTitle,
|
|
15
16
|
} from "./model_pick.js";
|
|
@@ -39,7 +40,7 @@ function headTemplate(label, note = "") {
|
|
|
39
40
|
|
|
40
41
|
function readRecent() {
|
|
41
42
|
try {
|
|
42
|
-
const list = JSON.parse(
|
|
43
|
+
const list = JSON.parse(readStored(RECENT_KEY) || "[]");
|
|
43
44
|
return Array.isArray(list) ? list : [];
|
|
44
45
|
} catch (_) {
|
|
45
46
|
return [];
|
|
@@ -163,10 +164,8 @@ export function createModelPicker({ button, panel, input, list, composer, center
|
|
|
163
164
|
if (!row) return;
|
|
164
165
|
setChosen(row.name);
|
|
165
166
|
if (remember()) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
localStorage.setItem(RECENT_KEY, JSON.stringify(pushRecent(readRecent(), row.name)));
|
|
169
|
-
} catch (_) {}
|
|
167
|
+
writeStored(MODEL_KEY, row.name);
|
|
168
|
+
writeStored(RECENT_KEY, JSON.stringify(pushRecent(readRecent(), row.name)));
|
|
170
169
|
}
|
|
171
170
|
close();
|
|
172
171
|
onPick?.(row.name);
|
|
@@ -22,6 +22,7 @@ import { createStageFrame, reducedMotion } from "./stage_frame.js";
|
|
|
22
22
|
import { createInUseWatch } from "./stage_watch.js";
|
|
23
23
|
import { formatDuration } from "./timing.js";
|
|
24
24
|
import { easeScrollTo } from "./scroll.js";
|
|
25
|
+
import { readStored, writeStored } from "./storage.js";
|
|
25
26
|
|
|
26
27
|
const COLLAPSE_KEY = "chi_stage_collapsed";
|
|
27
28
|
// The turn-end read (markdown, answer_display) settles within this, or the
|
|
@@ -41,7 +42,7 @@ const PENDING = ".bubble.question:not(.answered):not(.cancelled), .bubble.contin
|
|
|
41
42
|
const ASKS = ".plugin-card[data-in-turn] .card-action";
|
|
42
43
|
|
|
43
44
|
function readCollapsed() {
|
|
44
|
-
|
|
45
|
+
return readStored(COLLAPSE_KEY) === "1";
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// +historyNearBottom+: the history's own follow check (the hand-off scrolls
|
|
@@ -289,7 +290,7 @@ export function createStageView({
|
|
|
289
290
|
function setCollapsed(value) {
|
|
290
291
|
collapsed = value;
|
|
291
292
|
revealed = false;
|
|
292
|
-
|
|
293
|
+
writeStored(COLLAPSE_KEY, value ? "1" : "0");
|
|
293
294
|
applyCollapsed();
|
|
294
295
|
}
|
|
295
296
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The page's per-browser choices in localStorage, guarded: storage can be
|
|
2
|
+
// missing or throw (a private window, blocked site data, a full quota), and
|
|
3
|
+
// a choice that can't be read or written is just not remembered. Reads
|
|
4
|
+
// globalThis.localStorage at each call, never at import (the specs install
|
|
5
|
+
// their own).
|
|
6
|
+
|
|
7
|
+
// The stored string, or +fallback+ when there is none or it can't be read.
|
|
8
|
+
export function readStored(key, fallback = null) {
|
|
9
|
+
try {
|
|
10
|
+
const value = globalThis.localStorage.getItem(key);
|
|
11
|
+
return value === null ? fallback : value;
|
|
12
|
+
} catch (_) {
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function writeStored(key, value) {
|
|
18
|
+
try { globalThis.localStorage.setItem(key, value); } catch (_) {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function removeStored(key) {
|
|
22
|
+
try { globalThis.localStorage.removeItem(key); } catch (_) {}
|
|
23
|
+
}
|
|
@@ -37,7 +37,7 @@ export function createToasts({ el, onChange = () => {}, doc = globalThis.documen
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function show(text, action = null, { sticky = false } = {}) {
|
|
40
|
-
const candidate = { el: doc.createElement("div"), timer: null, sticky, key: null };
|
|
40
|
+
const candidate = { el: doc.createElement("div"), timer: null, sticky, action, key: null };
|
|
41
41
|
candidate.el.className = "toast-item";
|
|
42
42
|
draw(candidate, text, action, sticky);
|
|
43
43
|
candidate.key = candidate.el.querySelector(".toast-message").textContent;
|
|
@@ -46,12 +46,13 @@ export function createToasts({ el, onChange = () => {}, doc = globalThis.documen
|
|
|
46
46
|
if (duplicate) {
|
|
47
47
|
if (duplicate.timer !== null) clearTimeout(duplicate.timer);
|
|
48
48
|
duplicate.timer = null;
|
|
49
|
-
duplicate.sticky = sticky;
|
|
49
|
+
duplicate.sticky = sticky || duplicate.sticky;
|
|
50
|
+
duplicate.action = action || duplicate.action;
|
|
50
51
|
duplicate.key = candidate.key;
|
|
51
|
-
draw(duplicate, text, action, sticky);
|
|
52
|
+
draw(duplicate, text, duplicate.action, duplicate.sticky);
|
|
52
53
|
entries.push(...entries.splice(entries.indexOf(duplicate), 1));
|
|
53
54
|
el.appendChild(duplicate.el);
|
|
54
|
-
if (!sticky) duplicate.timer = setTimeout(() => remove(duplicate), TOAST_MS);
|
|
55
|
+
if (!duplicate.sticky) duplicate.timer = setTimeout(() => remove(duplicate), TOAST_MS);
|
|
55
56
|
sync();
|
|
56
57
|
return;
|
|
57
58
|
}
|
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.79.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -337,6 +337,7 @@ files:
|
|
|
337
337
|
- lib/samagotchi/llm/adapter_common.rb
|
|
338
338
|
- lib/samagotchi/llm/adapters.rb
|
|
339
339
|
- lib/samagotchi/llm/anthropic_assembly.rb
|
|
340
|
+
- lib/samagotchi/llm/anthropic_binding_recovery.rb
|
|
340
341
|
- lib/samagotchi/llm/anthropic_messages.rb
|
|
341
342
|
- lib/samagotchi/llm/anthropic_wire.rb
|
|
342
343
|
- lib/samagotchi/llm/api_key.rb
|
|
@@ -595,6 +596,7 @@ files:
|
|
|
595
596
|
- lib/samagotchi/web/public/commands.js
|
|
596
597
|
- lib/samagotchi/web/public/composer.js
|
|
597
598
|
- lib/samagotchi/web/public/composer_fold.js
|
|
599
|
+
- lib/samagotchi/web/public/composer_grip.js
|
|
598
600
|
- lib/samagotchi/web/public/composer_size.js
|
|
599
601
|
- lib/samagotchi/web/public/context_chips.js
|
|
600
602
|
- lib/samagotchi/web/public/copy.js
|
|
@@ -647,6 +649,7 @@ files:
|
|
|
647
649
|
- lib/samagotchi/web/public/stage_slots.js
|
|
648
650
|
- lib/samagotchi/web/public/stage_view.js
|
|
649
651
|
- lib/samagotchi/web/public/stage_watch.js
|
|
652
|
+
- lib/samagotchi/web/public/storage.js
|
|
650
653
|
- lib/samagotchi/web/public/stream_handlers.js
|
|
651
654
|
- lib/samagotchi/web/public/strip.js
|
|
652
655
|
- lib/samagotchi/web/public/tally.js
|