samagotchi 0.3.0 → 0.4.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -1
  3. data/README.md +16 -0
  4. data/bin/chi +32 -31
  5. data/docs/cli.md +77 -5
  6. data/docs/configuration.md +104 -2
  7. data/docs/desktop.md +39 -4
  8. data/docs/guardrails.md +11 -0
  9. data/docs/hooks.md +88 -6
  10. data/docs/releasing.md +6 -6
  11. data/docs/sessions.md +19 -17
  12. data/lib/samagotchi/bridge/turn_accumulator.rb +1 -0
  13. data/lib/samagotchi/bridge.rb +4 -1
  14. data/lib/samagotchi/bundles/source-links/hooks/source_links.rb +178 -5
  15. data/lib/samagotchi/bundles/source-links/manifest.yml +3 -3
  16. data/lib/samagotchi/bundles/source-links/source_links.md +1 -1
  17. data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
  18. data/lib/samagotchi/bundles/system/delegated.md +6 -7
  19. data/lib/samagotchi/bundles/system/manifest.yml +3 -3
  20. data/lib/samagotchi/client.rb +9 -6
  21. data/lib/samagotchi/commands/registry.rb +8 -0
  22. data/lib/samagotchi/config.rb +64 -20
  23. data/lib/samagotchi/desktop/macos/App.swift +12 -8
  24. data/lib/samagotchi/desktop/macos/ChiRunner.swift +4 -2
  25. data/lib/samagotchi/desktop/macos/Images.swift +113 -0
  26. data/lib/samagotchi/desktop/macos/Info.plist.erb +6 -0
  27. data/lib/samagotchi/desktop/macos/Panel.swift +112 -9
  28. data/lib/samagotchi/desktop/macos.rb +59 -8
  29. data/lib/samagotchi/desktop_command.rb +6 -3
  30. data/lib/samagotchi/edit_preview.rb +82 -0
  31. data/lib/samagotchi/engine.rb +201 -104
  32. data/lib/samagotchi/gem_update.rb +89 -0
  33. data/lib/samagotchi/guardrails/approval.rb +26 -4
  34. data/lib/samagotchi/guardrails/load_failures.rb +9 -3
  35. data/lib/samagotchi/host_registry.rb +8 -12
  36. data/lib/samagotchi/idle_client.rb +24 -15
  37. data/lib/samagotchi/idle_reminders.rb +2 -2
  38. data/lib/samagotchi/image_store.rb +10 -6
  39. data/lib/samagotchi/kernel_loop.rb +26 -79
  40. data/lib/samagotchi/live_versions.rb +59 -0
  41. data/lib/samagotchi/llm/api_key.rb +41 -0
  42. data/lib/samagotchi/llm/chat_loop.rb +77 -13
  43. data/lib/samagotchi/llm/errors.rb +21 -7
  44. data/lib/samagotchi/llm/http.rb +15 -4
  45. data/lib/samagotchi/llm/openai_chat.rb +5 -26
  46. data/lib/samagotchi/memory_bundle/installer.rb +65 -63
  47. data/lib/samagotchi/memory_bundle/provenance.rb +51 -12
  48. data/lib/samagotchi/memory_bundle/shipped_update.rb +157 -0
  49. data/lib/samagotchi/memory_bundle/status.rb +4 -1
  50. data/lib/samagotchi/memory_bundle/system_bundle.rb +81 -53
  51. data/lib/samagotchi/model_profile.rb +23 -0
  52. data/lib/samagotchi/prompt.rb +4 -2
  53. data/lib/samagotchi/reminder_store.rb +1 -9
  54. data/lib/samagotchi/self_report.rb +17 -3
  55. data/lib/samagotchi/send_command.rb +107 -12
  56. data/lib/samagotchi/session_commands.rb +38 -8
  57. data/lib/samagotchi/session_manager.rb +1 -16
  58. data/lib/samagotchi/terminal_ui/attached_loop.rb +21 -25
  59. data/lib/samagotchi/terminal_ui/event_renderer.rb +8 -3
  60. data/lib/samagotchi/terminal_ui/formatting.rb +9 -0
  61. data/lib/samagotchi/terminal_ui/input_support.rb +4 -19
  62. data/lib/samagotchi/terminal_ui/question_prompt.rb +35 -0
  63. data/lib/samagotchi/terminal_ui.rb +62 -248
  64. data/lib/samagotchi/text_diff.rb +181 -0
  65. data/lib/samagotchi/thinking.rb +115 -0
  66. data/lib/samagotchi/tool_runner.rb +34 -1
  67. data/lib/samagotchi/tools/ask_user_question.rb +41 -33
  68. data/lib/samagotchi/tools/edit.rb +23 -9
  69. data/lib/samagotchi/tools/write.rb +4 -0
  70. data/lib/samagotchi/turn_flow.rb +12 -2
  71. data/lib/samagotchi/update_command.rb +308 -0
  72. data/lib/samagotchi/update_hint.rb +59 -0
  73. data/lib/samagotchi/version.rb +1 -1
  74. data/lib/samagotchi/vision_support.rb +6 -4
  75. data/lib/samagotchi/web/app.rb +3 -3
  76. data/lib/samagotchi/web/message_parts.rb +8 -3
  77. data/lib/samagotchi/web/public/activity.js +3 -0
  78. data/lib/samagotchi/web/public/app.js +36 -24
  79. data/lib/samagotchi/web/public/chat_view.js +3 -0
  80. data/lib/samagotchi/web/public/data.js +2 -0
  81. data/lib/samagotchi/web/public/diff_view.js +58 -0
  82. data/lib/samagotchi/web/public/index.html +22 -1
  83. data/lib/samagotchi/web/public/question_card.js +3 -1
  84. data/lib/samagotchi/web/public/turn_events.js +29 -5
  85. data/lib/samagotchi/web/public/turn_view.js +2 -1
  86. data/lib/samagotchi/worker.rb +5 -4
  87. metadata +12 -1
@@ -0,0 +1,58 @@
1
+ // What chi's edit/write tools change, as HTML, apart from the DOM. One
2
+ // renderer for both places: the approval card (the dry run, which can also
3
+ // say the edit would fail, or that the file isn't shown) and a tool row's
4
+ // collapsed "diff +3 −1" (what the call really changed). A diff is the
5
+ // server's {text, added, removed, truncated, new_file} (EditPreview).
6
+
7
+ import { escapeHtml } from "./format.js";
8
+
9
+ // More lines than this fold behind a "show all N lines" toggle.
10
+ export const DIFF_FOLD_LINES = 20;
11
+
12
+ const MINUS = "−";
13
+
14
+ // "+3 −1"
15
+ export function diffCounts(diff) {
16
+ return `+${Number(diff?.added) || 0} ${MINUS}${Number(diff?.removed) || 0}`;
17
+ }
18
+
19
+ // A row's summary line: "diff +3 −1", "new file +12".
20
+ export function diffSummary(diff) {
21
+ return diff?.new_file ? `new file +${Number(diff.added) || 0}` : `diff ${diffCounts(diff)}`;
22
+ }
23
+
24
+ export function diffLineClass(line) {
25
+ if (line.startsWith("@@")) return "hunk";
26
+ if (line.startsWith("+")) return "add";
27
+ if (line.startsWith("-")) return "del";
28
+ if (line.startsWith("\\") || line.startsWith("… ")) return "meta";
29
+ return "ctx";
30
+ }
31
+
32
+ function linesHtml(lines) {
33
+ return lines.map((line) => `<span class="diff-line diff-${diffLineClass(line)}">${escapeHtml(line) || " "}</span>`).join("");
34
+ }
35
+
36
+ // @param diff an EditPreview: a diff, or {error} / {skipped}
37
+ // @return {string} HTML; "" for none
38
+ export function diffHtml(diff, { fold = DIFF_FOLD_LINES } = {}) {
39
+ if (!diff || typeof diff !== "object") return "";
40
+ if (diff.error) return `<div class="diff-note warn">this edit would fail: ${escapeHtml(diff.error)}</div>`;
41
+ if (diff.skipped) return `<div class="diff-note">diff not shown: ${escapeHtml(diff.skipped)}</div>`;
42
+ const text = String(diff.text ?? "");
43
+ if (!text) return `<div class="diff-note">no change</div>`;
44
+ const lines = text.split("\n");
45
+ const head = diff.new_file ? `<div class="diff-note">new file</div>` : "";
46
+ const shown = `<pre class="diff"><code>${linesHtml(lines.slice(0, fold))}</code></pre>`;
47
+ if (lines.length <= fold) return head + shown;
48
+ // The rest opens under the first lines; the toggle hides once open (CSS).
49
+ const rest = `<details class="diff-more"><summary>show all ${lines.length} lines</summary>` +
50
+ `<pre class="diff"><code>${linesHtml(lines.slice(fold))}</code></pre></details>`;
51
+ return head + shown + rest;
52
+ }
53
+
54
+ // A tool row's collapsed diff (closed by default).
55
+ export function activityDiffHtml(diff) {
56
+ if (!diff || typeof diff !== "object" || diff.error || diff.skipped) return "";
57
+ return `<details class="activity-diff"><summary>${escapeHtml(diffSummary(diff))}</summary>${diffHtml(diff)}</details>`;
58
+ }
@@ -6,7 +6,8 @@
6
6
  <meta name="viewport" content="width=device-width,initial-scale=1">
7
7
  <style>
8
8
  :root{--bg:#0b0d11;--bg-panel:#151821;--bg-inset:#0f1115;--bg-raised:#22262f;--bg-hover:#1b1f27;--border:#1f232b;--border-strong:#2d333f;--text:#e6e8eb;--text-bright:#f0f3f6;--text-2:#c9d1d9;--text-3:#9aa4b2;--text-muted:#8b949e;--text-faint:#6e7681;--accent:#7aa5ff;--accent-strong:#1f6feb;--accent-bg:#1b2336;--accent-ring:rgba(122,165,255,.18);--primary:#2a68d1;--ok:#3fb950;--ok-text:#7ee787;--ok-bg:#1a2e1a;--warn:#d29922;--warn-text:#e3b341;--warn-bright:#f2cc60;--warn-bg:#2b2108;--warn-border:#6b5416;--danger:#f85149;--danger-bg:#da3633;--danger-text:#ff7b72;--danger-bg-dim:#3d1f1f;--think:#8957e5;--think-text:#d2a8ff;--accent-border:#223150;--accent-tint:color-mix(in srgb,var(--accent) 7%,transparent);
9
- --glass-bg:color-mix(in srgb,var(--bg-panel) 70%,transparent);--glass-border:rgba(255,255,255,.08);--glass-highlight:rgba(255,255,255,.05);--glass-shadow:0 8px 30px rgba(0,0,0,.35)}
9
+ --glass-bg:color-mix(in srgb,var(--bg-panel) 70%,transparent);--glass-border:rgba(255,255,255,.08);--glass-highlight:rgba(255,255,255,.05);--glass-shadow:0 8px 30px rgba(0,0,0,.35);
10
+ --diff-add-bg:rgba(63,185,80,.14);--diff-add-text:#aff5b4;--diff-del-bg:rgba(248,81,73,.14);--diff-del-text:#ffc1bc}
10
11
  *{box-sizing:border-box;margin:0;padding:0}
11
12
  body{font-family: ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:radial-gradient(1100px 700px at 8% 0%,color-mix(in srgb,var(--accent) 13%,transparent),transparent 70%),radial-gradient(900px 700px at 100% 100%,color-mix(in srgb,var(--think) 9%,transparent),transparent 70%),var(--bg);background-attachment:fixed;color:var(--text);height:100vh;display:flex;flex-direction:column}
12
13
  /* Glass: the composer card, the strip cards, the all-sessions cards and the question cards (by selector, so app.js is untouched). */
@@ -130,9 +131,15 @@ main{flex:1;display:flex;flex-direction:column;gap:16px;padding:16px;overflow:au
130
131
  .activity-status,.activity-tool{flex-shrink:0;white-space:nowrap}
131
132
  .activity-params{min-width:0}
132
133
  .activity-output{flex:1 1 100%;min-width:0}
134
+ /* An edit/write's change: a closed "diff +3 −1" under the output, lined up with it. */
135
+ .activity-diff{flex:1 1 100%;min-width:0;margin:2px 0 2px 60px}
136
+ .activity-diff>summary{cursor:pointer;font-size:12px;color:var(--text-muted);width:max-content;font-variant-numeric:tabular-nums}
137
+ .activity-diff>summary:hover{color:var(--text-2)}
138
+ .activity-diff[open]>summary{margin-bottom:4px}
133
139
  @media(max-width:600px){
134
140
  .activity-params{flex:1 1 0}
135
141
  .activity-output{margin-left:0}
142
+ .activity-diff{margin-left:0}
136
143
  }
137
144
  .bubble.thinking{border-left:3px solid var(--think);background:color-mix(in srgb,var(--think) 6%,var(--bg-panel));padding:8px 12px}
138
145
  .bubble.thinking:not([open]),.bubble.activity:not([open]){padding:3px 10px;background:transparent;border-color:transparent;align-self:flex-start;border-radius:6px;transition:background .12s ease}
@@ -253,6 +260,20 @@ main{flex:1;display:flex;flex-direction:column;gap:16px;padding:16px;overflow:au
253
260
  .approval-where,.approval-why{font-size:12px;color:var(--text-muted);word-break:break-all}
254
261
  .approval-why{color:var(--warn-text)}
255
262
  .approval-label{color:var(--text-faint)}
263
+ /* A diff (diff_view.js): the approval card's dry run and a tool row's change. */
264
+ .approval-diff{margin:0 0 6px}
265
+ .diff{margin:0;padding:4px 0;background:var(--bg-inset);border:1px solid var(--border);border-radius:6px;font-size:12px;line-height:1.45;overflow:auto;max-height:420px}
266
+ .diff code{display:block;min-width:max-content}
267
+ .diff-line{display:block;padding:0 10px;white-space:pre;color:var(--text-2)}
268
+ .diff-add{background:var(--diff-add-bg);color:var(--diff-add-text)}
269
+ .diff-del{background:var(--diff-del-bg);color:var(--diff-del-text)}
270
+ .diff-hunk{color:var(--text-faint)}
271
+ .diff-meta{color:var(--text-faint);font-style:italic}
272
+ .diff-note{font-size:12px;color:var(--text-muted);margin:0 0 4px}
273
+ .diff-note.warn{color:var(--warn-text)}
274
+ .diff-more{margin-top:4px}
275
+ .diff-more>summary{cursor:pointer;font-size:12px;color:var(--accent);width:max-content}
276
+ .diff-more[open]>summary{display:none}
256
277
  .bubble.guardrail-warning{border-left:3px solid var(--danger);background:color-mix(in srgb,var(--danger) 8%,var(--bg-panel));color:var(--danger-text);font-size:13px;padding:8px 12px;white-space:pre-wrap}
257
278
  .bubble.hook-notice{border-left:3px solid var(--text-faint);background:var(--bg-panel);color:var(--text-3);font-size:13px;padding:8px 12px;white-space:pre-wrap}
258
279
  .bubble.hook-notice.warn{border-left-color:var(--warn);color:var(--warn-text)}
@@ -8,7 +8,8 @@ export function isApproval(pq) {
8
8
  return !!pq && pq.kind === "approval";
9
9
  }
10
10
 
11
- // @return {null | {tool, what, isCommand, where, why}}
11
+ // @return {null | {tool, what, isCommand, where, why, preview?}}; preview is
12
+ // an edit/write's dry-run diff (diff_view.js draws it)
12
13
  export function approvalView(pq) {
13
14
  if (!isApproval(pq)) return null;
14
15
  const a = pq.approval || {};
@@ -31,6 +32,7 @@ export function approvalView(pq) {
31
32
  isCommand: !!command,
32
33
  where,
33
34
  why: `${reason} (${who})`,
35
+ ...(a.preview && typeof a.preview === "object" ? { preview: a.preview } : {}),
34
36
  };
35
37
  }
36
38
 
@@ -45,10 +45,14 @@ export function reminderText(event) {
45
45
  }
46
46
 
47
47
  // Who a hook's notice is from: the bundle's name for a bundle hook
48
- // ("known_names.rb (bundle known-names)"), else "hook".
48
+ // ("known_names.rb (bundle known-names)"), chi's own notice by its word
49
+ // (thinking), else "hook".
49
50
  export function hookNoticeLabel(hook) {
50
- const m = /\(bundle (.+)\)$/.exec(String(hook || ""));
51
- return m ? m[1] : "hook";
51
+ const name = String(hook || "");
52
+ const m = /\(bundle (.+)\)$/.exec(name);
53
+ if (m) return m[1];
54
+ // chi's own notices name themselves with a bare word (thinking).
55
+ return /^[a-z][a-z0-9-]*$/.test(name) ? name : "hook";
52
56
  }
53
57
 
54
58
  // The row a step gets when the loop asks again after an empty answer.
@@ -56,6 +60,23 @@ export function emptyRetryLine({ attempt, of } = {}) {
56
60
  return `↻ empty answer, asking again (${attempt}/${of})`;
57
61
  }
58
62
 
63
+ // The live turn's status while the provider is asked again after an error
64
+ // (generation_retrying): "↻ retrying (503) in 4 s, 2/5" (retry 2 of 5).
65
+ export function retryStatusLine({ attempt, max_retries, next_delay, status, error_class } = {}) {
66
+ const why = status || String(error_class || "").split("::").pop();
67
+ const delay = Number(next_delay);
68
+ const when = Number.isFinite(delay) ? ` in ${delay < 1 ? delay.toFixed(1) : Math.round(delay)} s` : "";
69
+ const count = attempt ? `, ${attempt}${max_retries ? `/${max_retries}` : ""}` : "";
70
+ return `↻ retrying${why ? ` (${why})` : ""}${when}${count}`;
71
+ }
72
+
73
+ // The live turn's status while it waits for plugins' slow setup before its
74
+ // first request (plugin_init_wait).
75
+ export function initWaitLine({ tasks } = {}) {
76
+ const labels = (tasks || []).map((t) => [t?.bundle, t?.label].filter(Boolean).join(": ")).filter(Boolean);
77
+ return `waiting for ${labels.length ? labels.join(" · ") : "plugins"}…`;
78
+ }
79
+
59
80
  // A composer line for the worker's command route rather than a prompt.
60
81
  export function isCommandLine(text) {
61
82
  return /^[/!]/.test(String(text || "").trim());
@@ -67,6 +88,7 @@ export function isCommandLine(text) {
67
88
  const WEB_LOCAL_REPLIES = {
68
89
  "/archive": "/archive: use the archive button in the session bar",
69
90
  "/exit": "/exit: close the tab to leave; stop in the session bar stops the worker",
91
+ "/quit": "/quit: close the tab to leave; stop in the session bar stops the worker",
70
92
  };
71
93
 
72
94
  export function webLocalReply(text) {
@@ -213,10 +235,12 @@ export function snapshotEvents({ current_turn: turn = null, queued = [], started
213
235
  if (part.label) call.label = part.label;
214
236
  events.push({ type: "tool_call_started", ...call, params: part.params });
215
237
  if (part.status !== "running") {
216
- events.push(withImages({
238
+ const completed = {
217
239
  type: "tool_call_completed", ...call, output: part.output, output_truncated: !!part.output_truncated,
218
240
  activity: { status: part.status, params: part.params },
219
- }, part.images));
241
+ };
242
+ if (part.diff) completed.diff = part.diff;
243
+ events.push(withImages(completed, part.images));
220
244
  }
221
245
  break;
222
246
  }
@@ -13,6 +13,7 @@ import { toolName } from "./activity.js";
13
13
  import { leavesBlock } from "./card.js";
14
14
  import { activityStatusClass, activityStatusLabel, fillActivityRow } from "./chat_view.js";
15
15
  import { copySourceAttr } from "./copy.js";
16
+ import { activityDiffHtml } from "./diff_view.js";
16
17
  import { escapeHtml, messageBodyHtml, noteHtml, normalize, steerRowHtml } from "./format.js";
17
18
  import { shouldFollowScroll } from "./scroll.js";
18
19
  import { hookNoticeLabel } from "./turn_events.js";
@@ -562,7 +563,7 @@ function reloadRowHtml(r, thumbs) {
562
563
  : "";
563
564
  return `<div class="activity-row" data-key="${escapeHtml(r.key)}"><span class="activity-status ${activityStatusClass(r.status)}">${activityStatusLabel(r.status)}</span>` +
564
565
  `<span class="activity-tool">${escapeHtml(toolName(r))}</span>${params}${duration ? `<span class="activity-duration">${escapeHtml(duration)}</span>` : ""}${output}` +
565
- `${r.images?.length ? thumbs(r.images) : ""}</div>`;
566
+ `${r.images?.length ? thumbs(r.images) : ""}${r.diff ? activityDiffHtml(r.diff) : ""}</div>`;
566
567
  }
567
568
 
568
569
  // A reloaded history in the turn view (renderHistory's object path): per
@@ -318,6 +318,7 @@ module Samagotchi
318
318
  @engine.clear_due_reminder_names!
319
319
  return false unless @engine.reminders_due?
320
320
 
321
+ drop_continue_offer({ client_id: SessionManager::REMINDER_CLIENT_ID })
321
322
  @session.status = Session::STATUS_RUNNING
322
323
  @session.save(state_dir: @state_dir)
323
324
  begin
@@ -331,8 +332,8 @@ module Samagotchi
331
332
  ensure
332
333
  refuse_queued_commands
333
334
  end
334
- # A pending continue offer stays (the REPL rule); otherwise the
335
- # rollback window closes.
335
+ # The offer went before the turn (drop_continue_offer); the rollback
336
+ # window closes.
336
337
  @turn_flow.after_reminder_turn
337
338
  @session.save(state_dir: @state_dir) unless stopped_on_disk?
338
339
  true
@@ -476,8 +477,8 @@ module Samagotchi
476
477
  @engine.announce(type: :continue_offered, context: offer[:context], no_interrupt: offer[:no_interrupt])
477
478
  end
478
479
 
479
- # A prompt taken while a continue is offered replaces the answer (D2):
480
- # the offer goes, the partial turn stays.
480
+ # A prompt (or a reminder turn) taken while a continue is offered
481
+ # replaces the answer (D2): the offer goes, the partial turn stays.
481
482
  def drop_continue_offer(origin)
482
483
  return unless @turn_flow.awaiting_continue?
483
484
 
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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -177,11 +177,14 @@ files:
177
177
  - lib/samagotchi/desktop/macos/App.swift
178
178
  - lib/samagotchi/desktop/macos/ChiRunner.swift
179
179
  - lib/samagotchi/desktop/macos/Hotkey.swift
180
+ - lib/samagotchi/desktop/macos/Images.swift
180
181
  - lib/samagotchi/desktop/macos/Info.plist.erb
181
182
  - lib/samagotchi/desktop/macos/Panel.swift
182
183
  - lib/samagotchi/desktop_command.rb
184
+ - lib/samagotchi/edit_preview.rb
183
185
  - lib/samagotchi/empty_answer_retry.rb
184
186
  - lib/samagotchi/engine.rb
187
+ - lib/samagotchi/gem_update.rb
185
188
  - lib/samagotchi/guardrails.rb
186
189
  - lib/samagotchi/guardrails/approval.rb
187
190
  - lib/samagotchi/guardrails/approvals.rb
@@ -206,6 +209,8 @@ files:
206
209
  - lib/samagotchi/installed_gem.rb
207
210
  - lib/samagotchi/kernel_loop.rb
208
211
  - lib/samagotchi/launch_mode.rb
212
+ - lib/samagotchi/live_versions.rb
213
+ - lib/samagotchi/llm/api_key.rb
209
214
  - lib/samagotchi/llm/backend.rb
210
215
  - lib/samagotchi/llm/chat_loop.rb
211
216
  - lib/samagotchi/llm/errors.rb
@@ -228,6 +233,7 @@ files:
228
233
  - lib/samagotchi/memory_bundle/merger.rb
229
234
  - lib/samagotchi/memory_bundle/placeholder.rb
230
235
  - lib/samagotchi/memory_bundle/provenance.rb
236
+ - lib/samagotchi/memory_bundle/shipped_update.rb
231
237
  - lib/samagotchi/memory_bundle/source.rb
232
238
  - lib/samagotchi/memory_bundle/status.rb
233
239
  - lib/samagotchi/memory_bundle/system_bundle.rb
@@ -284,6 +290,8 @@ files:
284
290
  - lib/samagotchi/terminal_ui/screen.rb
285
291
  - lib/samagotchi/terminal_ui/surface.rb
286
292
  - lib/samagotchi/terminal_ui/thinking_line.rb
293
+ - lib/samagotchi/text_diff.rb
294
+ - lib/samagotchi/thinking.rb
287
295
  - lib/samagotchi/thinking_ticker.rb
288
296
  - lib/samagotchi/thought_stream_splitter.rb
289
297
  - lib/samagotchi/token_usage.rb
@@ -321,6 +329,8 @@ files:
321
329
  - lib/samagotchi/turn_flow.rb
322
330
  - lib/samagotchi/turn_note.rb
323
331
  - lib/samagotchi/turn_tally.rb
332
+ - lib/samagotchi/update_command.rb
333
+ - lib/samagotchi/update_hint.rb
324
334
  - lib/samagotchi/version.rb
325
335
  - lib/samagotchi/vision_context.rb
326
336
  - lib/samagotchi/vision_support.rb
@@ -339,6 +349,7 @@ files:
339
349
  - lib/samagotchi/web/public/copy.js
340
350
  - lib/samagotchi/web/public/ctx.js
341
351
  - lib/samagotchi/web/public/data.js
352
+ - lib/samagotchi/web/public/diff_view.js
342
353
  - lib/samagotchi/web/public/format.js
343
354
  - lib/samagotchi/web/public/hold.js
344
355
  - lib/samagotchi/web/public/images.js