samagotchi 0.67.0 → 0.69.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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -1
  3. data/docs/architecture.md +7 -4
  4. data/docs/cli.md +6 -1
  5. data/docs/configuration.md +28 -6
  6. data/docs/guardrails.md +23 -1
  7. data/docs/hooks.md +4 -0
  8. data/docs/internals/bundles.md +1 -1
  9. data/docs/internals/engine.md +73 -17
  10. data/docs/internals/prompt-caching.md +9 -2
  11. data/docs/plugins.md +42 -19
  12. data/docs/releasing.md +14 -6
  13. data/docs/sessions.md +3 -3
  14. data/docs/testing.md +8 -2
  15. data/lib/samagotchi/bootstrap/probe.rb +1 -3
  16. data/lib/samagotchi/bundles/system/manifest.yml +2 -2
  17. data/lib/samagotchi/bundles/system/self_map.md +1 -1
  18. data/lib/samagotchi/config.rb +0 -19
  19. data/lib/samagotchi/effective_model.rb +283 -0
  20. data/lib/samagotchi/engine.rb +216 -360
  21. data/lib/samagotchi/guardrails/targets.rb +9 -2
  22. data/lib/samagotchi/host_config.rb +1 -29
  23. data/lib/samagotchi/host_model.rb +0 -7
  24. data/lib/samagotchi/kernel_loop.rb +24 -7
  25. data/lib/samagotchi/level_setting_command.rb +99 -0
  26. data/lib/samagotchi/llm/errors.rb +47 -1
  27. data/lib/samagotchi/llm/http.rb +20 -2
  28. data/lib/samagotchi/llm_context_stale.rb +6 -0
  29. data/lib/samagotchi/llm_context_strategy.rb +5 -10
  30. data/lib/samagotchi/memory_bundle/manifest.rb +3 -0
  31. data/lib/samagotchi/model_price.rb +2 -4
  32. data/lib/samagotchi/reasoning_history_command.rb +29 -90
  33. data/lib/samagotchi/session.rb +22 -8
  34. data/lib/samagotchi/session_metrics.rb +4 -1
  35. data/lib/samagotchi/session_setting.rb +86 -0
  36. data/lib/samagotchi/system_prompt.rb +19 -3
  37. data/lib/samagotchi/terminal_ui/formatting.rb +5 -2
  38. data/lib/samagotchi/thinking_command.rb +24 -76
  39. data/lib/samagotchi/thinking_notices.rb +169 -0
  40. data/lib/samagotchi/tool_activity.rb +3 -0
  41. data/lib/samagotchi/tool_row_fields.rb +4 -2
  42. data/lib/samagotchi/tool_runner.rb +24 -3
  43. data/lib/samagotchi/toolkit.rb +27 -14
  44. data/lib/samagotchi/toolkit_binding.rb +8 -2
  45. data/lib/samagotchi/toolkit_command.rb +20 -4
  46. data/lib/samagotchi/tools/builtin_calls.rb +45 -1
  47. data/lib/samagotchi/tools/registry.rb +95 -8
  48. data/lib/samagotchi/tools/tool_index.rb +223 -72
  49. data/lib/samagotchi/version.rb +1 -1
  50. data/lib/samagotchi/web/app.rb +69 -93
  51. data/lib/samagotchi/web/message_parts.rb +5 -2
  52. data/lib/samagotchi/web/public/activity.js +3 -1
  53. data/lib/samagotchi/web/public/app.js +196 -526
  54. data/lib/samagotchi/web/public/bubbles.js +291 -0
  55. data/lib/samagotchi/web/public/cards_place.js +184 -0
  56. data/lib/samagotchi/web/public/chains.js +19 -2
  57. data/lib/samagotchi/web/public/chip_popover.js +111 -0
  58. data/lib/samagotchi/web/public/command_list.js +12 -10
  59. data/lib/samagotchi/web/public/families_view.js +15 -0
  60. data/lib/samagotchi/web/public/index.html +21 -11
  61. data/lib/samagotchi/web/public/info_bar.js +3 -3
  62. data/lib/samagotchi/web/public/info_bar_model.js +110 -0
  63. data/lib/samagotchi/web/public/init_row.js +21 -10
  64. data/lib/samagotchi/web/public/level_chip.js +29 -80
  65. data/lib/samagotchi/web/public/lists_view.js +64 -9
  66. data/lib/samagotchi/web/public/llm_context_chip.js +36 -105
  67. data/lib/samagotchi/web/public/model_picker.js +47 -28
  68. data/lib/samagotchi/web/public/ref_scan.js +1 -7
  69. data/lib/samagotchi/web/public/refs.js +13 -0
  70. data/lib/samagotchi/web/public/session_state.js +5 -0
  71. data/lib/samagotchi/web/public/stage_frame.js +179 -0
  72. data/lib/samagotchi/web/public/stage_model.js +33 -0
  73. data/lib/samagotchi/web/public/stage_slots.js +130 -0
  74. data/lib/samagotchi/web/public/stage_view.js +56 -335
  75. data/lib/samagotchi/web/public/stage_watch.js +65 -0
  76. data/lib/samagotchi/web/public/stream_handlers.js +5 -2
  77. data/lib/samagotchi/web/public/timing.js +3 -1
  78. data/lib/samagotchi/web/public/toast_place.js +16 -0
  79. data/lib/samagotchi/web/public/toolkit_chip.js +11 -4
  80. data/lib/samagotchi/web/public/turn_html.js +4 -2
  81. data/lib/samagotchi/web/public/vendor/directive-helpers.js +8 -0
  82. data/lib/samagotchi/web/public/vendor/directives/repeat.js +9 -0
  83. data/lib/samagotchi/worker.rb +11 -16
  84. metadata +15 -1
@@ -0,0 +1,65 @@
1
+ // The stage view's in-use watch (stage_view.js): the stage is in use while
2
+ // the pointer is over it (tracked on the stage itself: :hover goes stale
3
+ // when children change), after a touch or wheel in the last 4 s (phones
4
+ // have no hover), with keyboard focus or a selection in it. While an ended
5
+ // turn waits for its hand-off it ticks every WATCH_MS: the stage wears
6
+ // `in-use`, and the stage's +onTick+ (stage_model.js handOffDue) says when
7
+ // the hand-off is due, with the quiet clock this watch keeps.
8
+ import { inUseFrom } from "./stage_model.js";
9
+
10
+ const WATCH_MS = 200;
11
+
12
+ // +watching()+: the stage still holds an ended turn (else a tick stops the
13
+ // watch). +onTick({ inUse, quietSinceMs, nowMs })+ returns { due,
14
+ // quietSinceMs }; +onDue()+ hands off.
15
+ export function createInUseWatch({ stage, watching, onTick, onDue }) {
16
+ let watchTimer = null;
17
+ let quietSince = null;
18
+ let pointerIn = false;
19
+ let lastTouch = 0;
20
+
21
+ stage.addEventListener("pointerenter", (e) => { if (e.pointerType === "mouse") pointerIn = true; });
22
+ stage.addEventListener("pointermove", (e) => { if (e.pointerType === "mouse") pointerIn = true; });
23
+ stage.addEventListener("pointerleave", () => { pointerIn = false; });
24
+ // Scrolling needs the pointer over it, a touch or keyboard focus; the
25
+ // stage's own scrolls (follow, the answer's top) are no use.
26
+ for (const type of ["touchstart", "touchmove", "wheel"]) {
27
+ stage.addEventListener(type, () => { lastTouch = Date.now(); }, { passive: true, capture: true });
28
+ }
29
+
30
+ function inUse(nowMs) {
31
+ const active = document.activeElement;
32
+ const sel = window.getSelection();
33
+ return inUseFrom({
34
+ pointerIn,
35
+ lastTouchMs: lastTouch,
36
+ // A clicked button keeps the focus in Chrome: only keyboard focus and
37
+ // a field being typed in count.
38
+ focusInside: !!active && stage.contains(active) && active.matches(":focus-visible"),
39
+ selectionInside: !!sel && !sel.isCollapsed && stage.contains(sel.anchorNode),
40
+ nowMs,
41
+ });
42
+ }
43
+
44
+ function stopWatch() {
45
+ clearInterval(watchTimer);
46
+ watchTimer = null;
47
+ quietSince = null;
48
+ stage.classList.remove("in-use");
49
+ }
50
+
51
+ function startWatch() {
52
+ stopWatch();
53
+ watchTimer = setInterval(() => {
54
+ if (!watching()) return stopWatch();
55
+ const nowMs = Date.now();
56
+ const busy = inUse(nowMs);
57
+ stage.classList.toggle("in-use", busy);
58
+ const r = onTick({ inUse: busy, quietSinceMs: quietSince, nowMs });
59
+ quietSince = r.quietSinceMs;
60
+ if (r.due) onDue();
61
+ }, WATCH_MS);
62
+ }
63
+
64
+ return { start: startWatch, stop: stopWatch };
65
+ }
@@ -31,7 +31,7 @@
31
31
  // addCommandBubble(view), addNoteBubble(note), findInTurnRoots(selector),
32
32
  // setUsedMemories(names), showRecap(text), showGuardrailWarning(text, label),
33
33
  // showHookNotice(data), onInitEvent(type, data), upsertCard(card),
34
- // refreshCardBodies() the page's hooks
34
+ // refreshCardBodies(), syncModelPick() the page's hooks
35
35
 
36
36
  import { routeChunk } from "./chunk_router.js";
37
37
  import { extractCtxPct, generationTokens } from "./ctx.js";
@@ -55,7 +55,7 @@ export function createStreamHandlers(deps) {
55
55
  resync, updateInfoBar, applyPromptOps, showSteers, markRecapStale, setTurnRunning, setLiveStatus, endTurnView,
56
56
  appendChunk, readTurnEnd, applyFinalMarkdown, refreshTimingFromSession, makeUserBubble, addEndBubble,
57
57
  commandBubble, addCommandBubble, addNoteBubble, findInTurnRoots, setUsedMemories, showRecap,
58
- showGuardrailWarning, showHookNotice, onInitEvent, upsertCard, refreshCardBodies,
58
+ showGuardrailWarning, showHookNotice, onInitEvent, upsertCard, refreshCardBodies, syncModelPick,
59
59
  } = deps;
60
60
 
61
61
  return {
@@ -210,6 +210,9 @@ export function createStreamHandlers(deps) {
210
210
  state().served = null;
211
211
  updateInfoBar();
212
212
  }
213
+ // A fresh link's picker shows the model the worker kept, a refused
214
+ // /model too (its pick set the button before the answer).
215
+ if (view.modelName) syncModelPick?.();
213
216
  // /llm-context changed what the next turn runs under.
214
217
  if (data.llm_context) {
215
218
  state().llmContext = data.llm_context;
@@ -258,12 +258,14 @@ function canceledSteps(g, record, tools) {
258
258
  }
259
259
 
260
260
  // A call's row from its tool record alone (no saved parts): the record's
261
- // called_as too, so "(called as bash)" shows after a reload, as live, and
261
+ // called_as and via too, so "(called as bash)" and "(via call_tool)" show
262
+ // after a reload, as live, and
262
263
  // its no_match ("no match" on a grep that found nothing).
263
264
  function recordRows(tools, iteration) {
264
265
  return tools.filter((r) => Number(r.iteration) === iteration).map((r) => ({
265
266
  key: `${iteration}:${r.call_index ?? 0}`, tool: r.tool, status: r.status, duration_ms: r.duration_ms,
266
267
  ...(r.called_as ? { called_as: r.called_as } : {}),
268
+ ...(r.via ? { via: r.via } : {}),
267
269
  ...noMatch(r),
268
270
  }));
269
271
  }
@@ -0,0 +1,16 @@
1
+ // Where the toast sits (app.js placeToast measures, this decides): in a
2
+ // session, +gap+ above the highest of the composer card and what opens
3
+ // over it (the stage while it shows, a fresh link's open model panel), so
4
+ // it covers none of them; null elsewhere (the CSS's 24 px from the bottom).
5
+ // When that leaves the toast no room on screen it stays +gap+ from the top
6
+ // (covering what's under it rather than going off screen).
7
+ // @param tops the tops (px from the viewport's top) of what shows over the
8
+ // card; null for one that doesn't
9
+ // @return {number|null} the toast's bottom offset in px
10
+ export function toastBottom({ inSession, viewportHeight, dockTop, tops = [], toastHeight = 0, gap = 12 }) {
11
+ if (!inSession) return null;
12
+ const top = Math.min(dockTop, ...tops.filter((t) => Number.isFinite(t)));
13
+ const bottom = viewportHeight - top + gap;
14
+ const cap = viewportHeight - toastHeight - gap;
15
+ return Math.round(Math.max(0, Math.min(bottom, cap)));
16
+ }
@@ -1,9 +1,11 @@
1
1
  // The session bar's tools chip: the session's toolkit when it isn't full
2
2
  // ("tools lean"), with the tools it leaves out and how to change them in
3
3
  // its tooltip. The summary is the session's toolkit ({name, label,
4
- // left_out, fallback}: Toolkit::Explained#summary; the worker's snapshot or
5
- // a command_ran; the server's own reading of the session file has no
6
- // left_out). Changing it is /toolkit (no popover yet).
4
+ // left_out, reachable, removed, fallback}: Toolkit::Explained#summary; the
5
+ // worker's snapshot or a command_ran; the server's own reading of the
6
+ // session file has no lists). Left out is reachable (find_tools and
7
+ // call_tool reach them) and removed (only /toolkit add brings one back).
8
+ // Changing it is /toolkit (no popover yet).
7
9
 
8
10
  import { html, nothing } from "./vendor/lit-html.js";
9
11
 
@@ -13,7 +15,12 @@ export function chipModel(summary) {
13
15
  const label = summary.label || summary.name;
14
16
  const parts = [`Toolkit: ${label}, the tools this session declares.`];
15
17
  if (summary.fallback) parts.push(`${summary.fallback}: every tool is declared (full) until it resolves.`);
16
- if (Array.isArray(summary.left_out)) {
18
+ const reachable = Array.isArray(summary.reachable) ? summary.reachable : [];
19
+ const removed = Array.isArray(summary.removed) ? summary.removed : [];
20
+ if (reachable.length) {
21
+ parts.push(`Reachable with find_tools: ${reachable.join(", ")}.`);
22
+ if (removed.length) parts.push(`Removed: ${removed.join(", ")}.`);
23
+ } else if (Array.isArray(summary.left_out)) {
17
24
  parts.push(summary.left_out.length ? `Not loaded: ${summary.left_out.join(", ")}.` : "Nothing left out.");
18
25
  }
19
26
  parts.push("/toolkit lists them; /toolkit NAME, add TOOL or remove TOOL changes them from the next turn.");
@@ -100,8 +100,10 @@ export function toolRowInnerTemplate(row, { thumbs = () => nothing, stop = nothi
100
100
  const params = shown
101
101
  ? html`<span class=${`activity-params${ref?.kind ? " ref" : ""}`} title=${hover || nothing} data-ref-kind=${attr(ref?.kind)} data-ref-path=${attr(ref?.kind && ref.path)} data-ref-line=${attr(ref?.kind && ref.line)} data-ref-end-line=${attr(ref?.kind && ref.end_line)}>${shown}</span>`
102
102
  : nothing;
103
- // The name the model called the tool by when it was an alias's, dim.
104
- const alias = row.called_as ? html`<span class="activity-alias">(called as ${row.called_as})</span>` : nothing;
103
+ // The name the model called the tool by when it was an alias's, or the
104
+ // tool it was reached through (call_tool), dim.
105
+ const alias = row.called_as ? html`<span class="activity-alias">(called as ${row.called_as})</span>`
106
+ : row.via ? html`<span class="activity-alias">(via ${row.via})</span>` : nothing;
105
107
  const duration = formatDuration(row.duration_ms);
106
108
  const full = row.status === "running" ? "" : String(row.output || "").replace(/^\[[^\]]*\]\s*/, "");
107
109
  const output = full
@@ -0,0 +1,8 @@
1
+ // lit-html 3.3.3 (npm tarball lit-html-3.3.3.tgz, directive-helpers.js: the minified production build,
2
+ // unmodified but for this line and the dropped sourceMappingURL). BSD-3-Clause.
3
+ import{_$LH as o}from"./lit-html.js";
4
+ /**
5
+ * @license
6
+ * Copyright 2020 Google LLC
7
+ * SPDX-License-Identifier: BSD-3-Clause
8
+ */const{I:t}=o,i=o=>o,n=o=>null===o||"object"!=typeof o&&"function"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(""),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};
@@ -0,0 +1,9 @@
1
+ // lit-html 3.3.3 (npm tarball lit-html-3.3.3.tgz, directives/repeat.js: the minified production build,
2
+ // unmodified but for this line and the dropped sourceMappingURL). BSD-3-Clause.
3
+ import{noChange as e}from"../lit-html.js";import{directive as s,Directive as t,PartType as r}from"../directive.js";import{getCommittedValue as l,setChildPartValue as o,insertPart as i,removePart as n,setCommittedValue as f}from"../directive-helpers.js";
4
+ /**
5
+ * @license
6
+ * Copyright 2017 Google LLC
7
+ * SPDX-License-Identifier: BSD-3-Clause
8
+ */
9
+ const u=(e,s,t)=>{const r=new Map;for(let l=s;l<=t;l++)r.set(e[l],l);return r},c=s(class extends t{constructor(e){if(super(e),e.type!==r.CHILD)throw Error("repeat() can only be used in text expressions")}dt(e,s,t){let r;void 0===t?t=s:void 0!==s&&(r=s);const l=[],o=[];let i=0;for(const s of e)l[i]=r?r(s,i):i,o[i]=t(s,i),i++;return{values:o,keys:l}}render(e,s,t){return this.dt(e,s,t).values}update(s,[t,r,c]){const d=l(s),{values:p,keys:a}=this.dt(t,r,c);if(!Array.isArray(d))return this.ut=a,p;const h=this.ut??=[],v=[];let m,y,x=0,j=d.length-1,k=0,w=p.length-1;for(;x<=j&&k<=w;)if(null===d[x])x++;else if(null===d[j])j--;else if(h[x]===a[k])v[k]=o(d[x],p[k]),x++,k++;else if(h[j]===a[w])v[w]=o(d[j],p[w]),j--,w--;else if(h[x]===a[w])v[w]=o(d[x],p[w]),i(s,v[w+1],d[x]),x++,w--;else if(h[j]===a[k])v[k]=o(d[j],p[k]),i(s,d[x],d[j]),j--,k++;else if(void 0===m&&(m=u(a,k,w),y=u(h,x,j)),m.has(h[x]))if(m.has(h[j])){const e=y.get(a[k]),t=void 0!==e?d[e]:null;if(null===t){const e=i(s,d[x]);o(e,p[k]),v[k]=e}else v[k]=o(t,p[k]),i(s,d[x],t),d[e]=null;k++}else n(d[j]),j--;else n(d[x]),x++;for(;k<=w;){const e=i(s,v[w+1]);o(e,p[k]),v[k++]=e}for(;x<=j;){const e=d[x++];null!==e&&n(e)}return this.ut=a,f(s,v),e}});export{c as repeat};
@@ -22,6 +22,7 @@ require_relative "llm/errors"
22
22
  require_relative "continue_offer"
23
23
  require_relative "iteration_limit"
24
24
  require_relative "session_commands"
25
+ require_relative "session_setting"
25
26
  require_relative "queued_command"
26
27
  require_relative "model_profile"
27
28
  require_relative "child_reports"
@@ -153,9 +154,6 @@ module Samagotchi
153
154
  # The running turn has ended (its end event) and its after_turn hooks
154
155
  # run: a signal's grace waits for them (#signalled).
155
156
  @turn_ending = false
156
- # The worker's own saves and a signal's (#signal_grace_expired), one
157
- # at a time.
158
- @save_mutex = Mutex.new
159
157
  end
160
158
 
161
159
  # Whether the session was empty as the worker left it, so the caller
@@ -460,7 +458,7 @@ module Samagotchi
460
458
  return unless mark
461
459
 
462
460
  Log.info(:worker, "interrupted_turn_settled", turn_id: mark.id, iteration: mark.iteration, at: mark.at)
463
- save_or_log(:interrupted_turn) { @session.save(state_dir: @state_dir) }
461
+ save_or_log(:interrupted_turn) { save_session }
464
462
  end
465
463
 
466
464
  # A question saved by a worker that died while it waited (an
@@ -473,7 +471,7 @@ module Samagotchi
473
471
  return unless id
474
472
 
475
473
  Log.info(:worker, "dead_question_dropped", id: id)
476
- save_or_log(:dead_question) { @session.save(state_dir: @state_dir) }
474
+ save_or_log(:dead_question) { save_session }
477
475
  end
478
476
 
479
477
  # The attached context's notes (WorkerInbound#absorb_context), and the
@@ -873,15 +871,10 @@ module Samagotchi
873
871
  event[:anytime] = true if anytime
874
872
  # Its line was shown at its command_queued (waits: turn_end).
875
873
  event[:queued] = true if command.waits_for_turn_end?
876
- # /llm-context or /model: what the next turn runs under now (the web's chip).
877
- event[:llm_context] = @engine.llm_context_summary if changed.intersect?(%i[llm_context model])
878
- # /thinking or /model: the level the next turn runs at (the web's chip).
879
- event[:thinking] = @engine.thinking_explained&.summary if changed.intersect?(%i[thinking model])
880
- # /reasoning-history or /model: the level the next turn runs under
881
- # (the web's chip); nil on a native host, which ignores it.
882
- event[:reasoning_history] = @engine.reasoning_history_explained&.summary if changed.intersect?(%i[reasoning_history model])
883
- # /toolkit: what the next turn declares (the web's chip; nil on full).
884
- event[:toolkit] = @engine.toolkit_summary if changed.include?(:toolkit)
874
+ # The chips the command moved (SessionSetting#changed_by): what the next
875
+ # turn runs under now (/llm-context, /thinking, /reasoning-history,
876
+ # /toolkit, and /model for the model's).
877
+ SessionSetting::ALL.each { |setting| event[setting.key] = setting.summary(@engine) if setting.changed_by?(changed) }
885
878
  event[:confirm] = confirm.to_h if confirm
886
879
  event[:output_truncated] = true if text.length > COMMAND_OUTPUT_LIMIT
887
880
  @engine.announce(event)
@@ -910,7 +903,7 @@ module Samagotchi
910
903
  # the new parent beside it (Session::PARENT_FILE): this turn's
911
904
  # status, its rings and every save from here on say so.
912
905
  @session.parent_id = Session.parent_override(@session_id, state_dir: @state_dir) || @session.parent_id
913
- @session.save(state_dir: @state_dir)
906
+ save_session
914
907
  result = @engine.run_turn(@session, prompt, pending_input: pending_input_drain, **turn_args)
915
908
  rescue StandardError => e
916
909
  error = e
@@ -1238,8 +1231,10 @@ module Samagotchi
1238
1231
  # Save the session, a stopped one too: a stop is the marker file
1239
1232
  # (Session.mark_stopped), which every load reads over the saved status,
1240
1233
  # so this save can't hide it, and the turn the stop canceled is kept.
1234
+ # Session#save serializes it with every other save of @session (Bridge
1235
+ # threads, the turn's checkpoints, a signal's thread).
1241
1236
  def save_session
1242
- @save_mutex.synchronize { @session.save(state_dir: @state_dir) }
1237
+ @session.save(state_dir: @state_dir)
1243
1238
  end
1244
1239
 
1245
1240
  def signalled? = !@signals.signal.nil?
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.67.0
4
+ version: 0.69.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -275,6 +275,7 @@ files:
275
275
  - lib/samagotchi/desktop/macos/Tmux.swift
276
276
  - lib/samagotchi/desktop_command.rb
277
277
  - lib/samagotchi/edit_preview.rb
278
+ - lib/samagotchi/effective_model.rb
278
279
  - lib/samagotchi/empty_answer_retry.rb
279
280
  - lib/samagotchi/engine.rb
280
281
  - lib/samagotchi/engine_peers.rb
@@ -330,6 +331,7 @@ files:
330
331
  - lib/samagotchi/kernel_loop.rb
331
332
  - lib/samagotchi/last_turn.rb
332
333
  - lib/samagotchi/launch_mode.rb
334
+ - lib/samagotchi/level_setting_command.rb
333
335
  - lib/samagotchi/live_versions.rb
334
336
  - lib/samagotchi/llm/adapter_common.rb
335
337
  - lib/samagotchi/llm/adapters.rb
@@ -459,6 +461,7 @@ files:
459
461
  - lib/samagotchi/session_metrics.rb
460
462
  - lib/samagotchi/session_observer.rb
461
463
  - lib/samagotchi/session_retention.rb
464
+ - lib/samagotchi/session_setting.rb
462
465
  - lib/samagotchi/session_setup.rb
463
466
  - lib/samagotchi/sessions_command.rb
464
467
  - lib/samagotchi/setting_explained.rb
@@ -492,6 +495,7 @@ files:
492
495
  - lib/samagotchi/text_diff.rb
493
496
  - lib/samagotchi/thinking.rb
494
497
  - lib/samagotchi/thinking_command.rb
498
+ - lib/samagotchi/thinking_notices.rb
495
499
  - lib/samagotchi/thinking_tails.rb
496
500
  - lib/samagotchi/thinking_ticker.rb
497
501
  - lib/samagotchi/thought_stream_splitter.rb
@@ -569,8 +573,11 @@ files:
569
573
  - lib/samagotchi/web/public/annotations.js
570
574
  - lib/samagotchi/web/public/answer_refs.js
571
575
  - lib/samagotchi/web/public/app.js
576
+ - lib/samagotchi/web/public/bubbles.js
572
577
  - lib/samagotchi/web/public/card.js
578
+ - lib/samagotchi/web/public/cards_place.js
573
579
  - lib/samagotchi/web/public/chains.js
580
+ - lib/samagotchi/web/public/chip_popover.js
574
581
  - lib/samagotchi/web/public/chunk_router.js
575
582
  - lib/samagotchi/web/public/command_complete.js
576
583
  - lib/samagotchi/web/public/command_list.js
@@ -591,6 +598,7 @@ files:
591
598
  - lib/samagotchi/web/public/images.js
592
599
  - lib/samagotchi/web/public/index.html
593
600
  - lib/samagotchi/web/public/info_bar.js
601
+ - lib/samagotchi/web/public/info_bar_model.js
594
602
  - lib/samagotchi/web/public/init_row.js
595
603
  - lib/samagotchi/web/public/keep_focus.js
596
604
  - lib/samagotchi/web/public/kept_commands.js
@@ -620,8 +628,11 @@ files:
620
628
  - lib/samagotchi/web/public/session_title.js
621
629
  - lib/samagotchi/web/public/sessions_list.js
622
630
  - lib/samagotchi/web/public/shown_texts.js
631
+ - lib/samagotchi/web/public/stage_frame.js
623
632
  - lib/samagotchi/web/public/stage_model.js
633
+ - lib/samagotchi/web/public/stage_slots.js
624
634
  - lib/samagotchi/web/public/stage_view.js
635
+ - lib/samagotchi/web/public/stage_watch.js
625
636
  - lib/samagotchi/web/public/stream_handlers.js
626
637
  - lib/samagotchi/web/public/strip.js
627
638
  - lib/samagotchi/web/public/tally.js
@@ -629,13 +640,16 @@ files:
629
640
  - lib/samagotchi/web/public/thinking_chip.js
630
641
  - lib/samagotchi/web/public/thinking_ticker.js
631
642
  - lib/samagotchi/web/public/timing.js
643
+ - lib/samagotchi/web/public/toast_place.js
632
644
  - lib/samagotchi/web/public/toolkit_chip.js
633
645
  - lib/samagotchi/web/public/turn_events.js
634
646
  - lib/samagotchi/web/public/turn_html.js
635
647
  - lib/samagotchi/web/public/turn_model.js
636
648
  - lib/samagotchi/web/public/turn_view.js
637
649
  - lib/samagotchi/web/public/update_notice.js
650
+ - lib/samagotchi/web/public/vendor/directive-helpers.js
638
651
  - lib/samagotchi/web/public/vendor/directive.js
652
+ - lib/samagotchi/web/public/vendor/directives/repeat.js
639
653
  - lib/samagotchi/web/public/vendor/directives/unsafe-html.js
640
654
  - lib/samagotchi/web/public/vendor/lit-html.js
641
655
  - lib/samagotchi/web/qr.rb