samagotchi 0.102.0 → 0.103.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 +40 -1
- data/bin/chi +23 -191
- data/docs/architecture.md +54 -0
- data/docs/cli.md +114 -7
- data/docs/configuration.md +7 -1
- data/docs/guardrails.md +2 -1
- data/docs/internals/engine.md +1 -1
- data/docs/internals/llm-context-forget.md +11 -1
- data/docs/internals/remote-sessions.md +303 -0
- data/docs/internals/working-set.md +95 -11
- data/docs/plugins.md +26 -2
- data/docs/sessions.md +12 -5
- data/docs/sub-agent.md +8 -0
- data/lib/samagotchi/answer_command.rb +27 -25
- data/lib/samagotchi/bridge.rb +24 -4
- data/lib/samagotchi/bridge_client/event_stream.rb +81 -12
- data/lib/samagotchi/bridge_client/stream_request.rb +77 -0
- data/lib/samagotchi/bridge_client.rb +22 -38
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/claude-peer/scripts/pair +30 -1
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/codex-peer/scripts/pair +49 -4
- data/lib/samagotchi/bundles/guardrails/guardrails/rules.yml +7 -4
- data/lib/samagotchi/bundles/guardrails/manifest.yml +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/chi_invocation.rb +43 -0
- data/lib/samagotchi/cli/command.rb +3 -2
- data/lib/samagotchi/cli/flags.rb +5 -0
- data/lib/samagotchi/cli/parent_wait.rb +22 -13
- data/lib/samagotchi/context_command.rb +80 -73
- data/lib/samagotchi/engine.rb +12 -8
- data/lib/samagotchi/idle_recap.rb +7 -1
- data/lib/samagotchi/kernel_loop.rb +13 -1
- data/lib/samagotchi/launch_mode.rb +22 -0
- data/lib/samagotchi/llm_context_forget.rb +13 -6
- data/lib/samagotchi/note_command.rb +8 -7
- data/lib/samagotchi/parent_report.rb +55 -43
- data/lib/samagotchi/remote_command.rb +170 -0
- data/lib/samagotchi/remotes.rb +130 -0
- data/lib/samagotchi/run_options.rb +233 -0
- data/lib/samagotchi/send_command/busy.rb +58 -0
- data/lib/samagotchi/send_command/parent_gate.rb +23 -10
- data/lib/samagotchi/send_command/wait_target.rb +16 -3
- data/lib/samagotchi/send_command.rb +173 -108
- data/lib/samagotchi/session.rb +30 -5
- data/lib/samagotchi/session_access/contract.rb +129 -0
- data/lib/samagotchi/session_access/dispatch.rb +133 -0
- data/lib/samagotchi/session_access/http_transport.rb +109 -0
- data/lib/samagotchi/session_access/local.rb +349 -0
- data/lib/samagotchi/session_access/remote.rb +638 -0
- data/lib/samagotchi/session_access/web_bridge_client.rb +181 -0
- data/lib/samagotchi/session_access.rb +141 -0
- data/lib/samagotchi/session_archive_command.rb +9 -5
- data/lib/samagotchi/session_commands.rb +20 -15
- data/lib/samagotchi/session_delete_command.rb +8 -4
- data/lib/samagotchi/session_manager.rb +7 -2
- data/lib/samagotchi/session_setup.rb +11 -6
- data/lib/samagotchi/sessions_command.rb +36 -14
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +51 -15
- data/lib/samagotchi/terminal_ui/attach_link.rb +104 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +80 -75
- data/lib/samagotchi/terminal_ui/local_link.rb +130 -0
- data/lib/samagotchi/terminal_ui/version_lines.rb +5 -3
- data/lib/samagotchi/terminal_ui.rb +2 -2
- data/lib/samagotchi/title_command.rb +9 -11
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +360 -69
- data/lib/samagotchi/web/public/data.js +3 -2
- data/lib/samagotchi/web/public/index.html +20 -4
- data/lib/samagotchi/web/public/stage_frame.js +7 -2
- data/lib/samagotchi/web/public/stage_view.js +3 -2
- data/lib/samagotchi/web/public/stream_handlers.js +3 -1
- data/lib/samagotchi/web/public/working_set.js +159 -28
- data/lib/samagotchi/web/public/working_set_rail.js +39 -12
- data/lib/samagotchi/web/public/working_set_view.js +91 -33
- data/lib/samagotchi/web/session_access_routes.rb +343 -0
- data/lib/samagotchi/worker_sidecar.rb +12 -5
- data/lib/samagotchi/working_set.rb +88 -14
- data/lib/samagotchi/working_set_command.rb +133 -30
- data/lib/samagotchi/working_set_pin.rb +34 -0
- data/lib/samagotchi/working_set_steer.rb +191 -27
- metadata +19 -1
|
@@ -6,11 +6,14 @@ require_relative "llm_context_view"
|
|
|
6
6
|
require_relative "llm_context_apply"
|
|
7
7
|
require_relative "token_usage"
|
|
8
8
|
require_relative "working_set"
|
|
9
|
+
require_relative "working_set_pin"
|
|
9
10
|
|
|
10
11
|
module Samagotchi
|
|
11
12
|
# The user steering the working set (/working-set, the web's popover):
|
|
12
13
|
# drop an item (every content output of it still sent goes out of the
|
|
13
|
-
# prompt as a stub) and undrop it
|
|
14
|
+
# prompt as a stub) and undrop it; pin one (the model's forget_outputs
|
|
15
|
+
# refuses its outputs, LLMContextForget) and unpin it. Pure, on a
|
|
16
|
+
# conversation the caller
|
|
14
17
|
# owns (Engine#clone_messages' copies: an op stores new edits Hashes,
|
|
15
18
|
# LLMContextEdit.store) and runs between turns, so the next turn's first
|
|
16
19
|
# request is the first with the change (prompt caching: one break where
|
|
@@ -24,6 +27,20 @@ module Samagotchi
|
|
|
24
27
|
# puts it back. The outputs are WorkingSet.outputs_of's, the projection's
|
|
25
28
|
# own pairing, so the set and the op never disagree.
|
|
26
29
|
#
|
|
30
|
+
# A pin is the session's (Session#working_set_pins, a WorkingSetPin per
|
|
31
|
+
# item), not an edit: it sends nothing different, so it breaks no cache
|
|
32
|
+
# and survives a strategy switch. Pin and drop exclude each other: a
|
|
33
|
+
# drop asks for an unpin first, and a pin takes a pending drop off (one
|
|
34
|
+
# never sent: free), else asks for an undrop first.
|
|
35
|
+
#
|
|
36
|
+
# An add is a request: the turn-start note asks the model to read the
|
|
37
|
+
# item (read, memory_read, web_fetch) before the task, so its output is
|
|
38
|
+
# a real tool run (stamps, ids, forget, the projection), on both loops.
|
|
39
|
+
# Nothing sent changes until then; it costs the model one step. An item
|
|
40
|
+
# the model has whole already is refused; a stale or forgotten one is
|
|
41
|
+
# its refresh. Pending until the next turn sends the note
|
|
42
|
+
# (WorkingSet.pending_adds); an unadd takes it back.
|
|
43
|
+
#
|
|
27
44
|
# Every op since the last turn is listed in one tail system message
|
|
28
45
|
# (kind "working_set", #record!), so the model learns what changed and
|
|
29
46
|
# doesn't read a dropped file again by reflex. An inverse op before the
|
|
@@ -31,14 +48,18 @@ module Samagotchi
|
|
|
31
48
|
# nothing noted).
|
|
32
49
|
module WorkingSetSteer
|
|
33
50
|
BY = "user"
|
|
34
|
-
NOTE_KIND =
|
|
51
|
+
NOTE_KIND = WorkingSet::NOTE_KIND
|
|
35
52
|
# The tail note's ops: [{"op", "kind", "key", "label"}], until the turn
|
|
36
53
|
# that sends it (the chat loop's kept entries leave the key out).
|
|
37
|
-
OPS =
|
|
54
|
+
OPS = WorkingSet::NOTE_OPS
|
|
38
55
|
NOTE_OPEN = "[WORKING SET: "
|
|
39
56
|
DROP = "drop"
|
|
40
57
|
UNDROP = "undrop"
|
|
41
|
-
|
|
58
|
+
PIN = "pin"
|
|
59
|
+
UNPIN = "unpin"
|
|
60
|
+
ADD = WorkingSet::ADD
|
|
61
|
+
UNADD = "unadd"
|
|
62
|
+
INVERSE = { DROP => UNDROP, UNDROP => DROP, PIN => UNPIN, UNPIN => PIN, ADD => UNADD, UNADD => ADD }.freeze
|
|
42
63
|
MEMORY_PREFIX = "memory:"
|
|
43
64
|
URL = %r{\A[a-z][a-z0-9+.-]*://}i
|
|
44
65
|
|
|
@@ -47,12 +68,25 @@ module Samagotchi
|
|
|
47
68
|
class Refused < StandardError
|
|
48
69
|
MESSAGES = {
|
|
49
70
|
dropped: "dropped by the user",
|
|
71
|
+
pinned_by_user: "pinned by the user: it stays in your context",
|
|
72
|
+
unpin_first: "pinned by the user: unpin it first",
|
|
73
|
+
undrop_first: "dropped by the user: undrop it first",
|
|
74
|
+
already_pinned: "already pinned",
|
|
75
|
+
not_pinned: "not pinned",
|
|
50
76
|
not_in_set: "not in the working set",
|
|
51
77
|
only_edited: "nothing to drop: only edited or written (no read of it is sent)",
|
|
52
78
|
derived: "nothing to drop: an older session's output that chi can't name",
|
|
53
79
|
already_dropped: "already dropped",
|
|
54
80
|
not_sent: "nothing to drop: none of it is sent now (forgotten or stubbed)",
|
|
55
|
-
not_dropped: "not dropped"
|
|
81
|
+
not_dropped: "not dropped",
|
|
82
|
+
no_file: "no such file",
|
|
83
|
+
directory: "a directory: add its files",
|
|
84
|
+
no_memory: "no such memory",
|
|
85
|
+
bad_memory: "not a memory name (no /, \\ or ..)",
|
|
86
|
+
not_http: "not an http(s) URL: web_fetch can't fetch it",
|
|
87
|
+
already_sent: "already in the model's context (sent whole): nothing to add",
|
|
88
|
+
already_added: "already added: the next turn reads it",
|
|
89
|
+
not_added: "not added (no add of it is pending)"
|
|
56
90
|
}.freeze
|
|
57
91
|
|
|
58
92
|
# @return [Symbol] a key of MESSAGES
|
|
@@ -73,11 +107,44 @@ module Samagotchi
|
|
|
73
107
|
|
|
74
108
|
# What an op did: the ids it changed, and the chars it frees (a drop)
|
|
75
109
|
# and the tail the server reads again from its earliest entry on.
|
|
76
|
-
# +free+: an undrop that took back a drop never sent (nothing changes)
|
|
77
|
-
|
|
110
|
+
# +free+: an undrop that took back a drop never sent (nothing changes),
|
|
111
|
+
# a pin or an unpin (nothing sent changes). +undone+: the pending drop
|
|
112
|
+
# a pin took off (a Done), else nil. +shared_with+: the other memories a
|
|
113
|
+
# memory_read of several names holds in an output the op acted on (the
|
|
114
|
+
# op acts on that output whole, so on them too).
|
|
115
|
+
Done = Data.define(:target, :op, :ids, :freed_chars, :tail_chars, :free, :undone, :shared_with) do
|
|
116
|
+
def initialize(undone: nil, shared_with: [], **fields) = super
|
|
117
|
+
end
|
|
78
118
|
|
|
79
119
|
module_function
|
|
80
120
|
|
|
121
|
+
# A word quoted ("…" or '…'): runs to its closing quote, spaces and
|
|
122
|
+
# all; a backslash escapes the quote or itself. Else a bare word.
|
|
123
|
+
WORD = /\G\s*(?:"((?:[^"\\]|\\.)*)"?|'((?:[^'\\]|\\.)*)'?|(\S+))/m
|
|
124
|
+
# What a quoted word unescapes (\" \' \\); any other backslash stays.
|
|
125
|
+
ESCAPE = /\\(["'\\])/
|
|
126
|
+
# A word that needs quoting on a line: whitespace, or a leading quote.
|
|
127
|
+
QUOTE = /\s|\A["']/
|
|
128
|
+
|
|
129
|
+
# The KEY words of a /working-set line (what follows the verb):
|
|
130
|
+
# whitespace-separated, a quoted word keeps its spaces ("my dir/a.rb").
|
|
131
|
+
# An unclosed quote runs to the end. The web's working_set.js
|
|
132
|
+
# (steerWords) reads lines the same way.
|
|
133
|
+
# @return [Array<String>]
|
|
134
|
+
def words(text)
|
|
135
|
+
text.to_s.scan(WORD).filter_map do |double, single, bare|
|
|
136
|
+
quoted = double || single
|
|
137
|
+
quoted ? quoted.gsub(ESCAPE, '\\1') : bare
|
|
138
|
+
end.reject(&:empty?)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# +word+ as a line names it: quoted when it has whitespace or starts
|
|
142
|
+
# with a quote (#words reads it back), else as it is.
|
|
143
|
+
def quote(word)
|
|
144
|
+
word = word.to_s
|
|
145
|
+
word.match?(QUOTE) ? %("#{word.gsub(/["\\]/) { |char| "\\#{char}" }}") : word
|
|
146
|
+
end
|
|
147
|
+
|
|
81
148
|
# +word+ (a path, absolute or the session's folder's; memory:NAME; a
|
|
82
149
|
# URL) as a Target.
|
|
83
150
|
def target(word, cwd:)
|
|
@@ -103,9 +170,12 @@ module Samagotchi
|
|
|
103
170
|
# Drops +target+'s content outputs that are still sent under
|
|
104
171
|
# +strategy+ (the next turn's: LLMContextView's form), saving an applied
|
|
105
172
|
# :drop on each.
|
|
173
|
+
# +pins+: the session's (a pinned item asks for an unpin first).
|
|
106
174
|
# @return [Done]
|
|
107
175
|
# @raise [Refused]
|
|
108
|
-
def drop(conversation, target, strategy:, cwd:, now: Time.now.utc.iso8601(3))
|
|
176
|
+
def drop(conversation, target, strategy:, cwd:, pins: [], now: Time.now.utc.iso8601(3))
|
|
177
|
+
raise Refused, :unpin_first if pinned?(pins, target)
|
|
178
|
+
|
|
109
179
|
outputs = WorkingSet.outputs_of(conversation, kind: target.kind, key: target.key, strategy: strategy, cwd: cwd)
|
|
110
180
|
raise Refused, (known?(conversation, target, strategy, cwd) ? :only_edited : :not_in_set) if outputs.empty?
|
|
111
181
|
|
|
@@ -124,7 +194,7 @@ module Samagotchi
|
|
|
124
194
|
freed, tail = LLMContextApply.weigh(conversation, layers: layers(strategy), edits: edits)
|
|
125
195
|
edits.each { |index, edit| LLMContextEdit.store(conversation[index], edit) }
|
|
126
196
|
Done.new(target: target, op: DROP, ids: edits.map { |_, edit| edit.id }, freed_chars: freed, tail_chars: tail,
|
|
127
|
-
free: false)
|
|
197
|
+
free: false, shared_with: shared_with(sent))
|
|
128
198
|
end
|
|
129
199
|
|
|
130
200
|
# Takes +target+'s drops off, putting back what each output held before
|
|
@@ -149,7 +219,85 @@ module Samagotchi
|
|
|
149
219
|
view = LLMContextView.new(strategy: strategy)
|
|
150
220
|
tail = sent ? LLMContextView.chars(view.messages(conversation)[first..]) : 0
|
|
151
221
|
Done.new(target: target, op: UNDROP, ids: dropped.map { |output, _| output.id }, freed_chars: 0, tail_chars: tail,
|
|
152
|
-
free: !sent)
|
|
222
|
+
free: !sent, shared_with: shared_with(dropped.map(&:first)))
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Pins +target+ (an item the set holds; an edit-only one too: its later
|
|
226
|
+
# reads are covered). A drop of it still pending (made since the last
|
|
227
|
+
# turn) is taken off first, free, as Done#undone; a sent one asks for
|
|
228
|
+
# an undrop first. The caller adds the pin (#pinning).
|
|
229
|
+
# @param pins [Array<WorkingSetPin>] the session's
|
|
230
|
+
# @return [Done] +ids+: the outputs the pin holds now
|
|
231
|
+
# @raise [Refused]
|
|
232
|
+
def pin(conversation, pins, target, strategy:, cwd:)
|
|
233
|
+
outputs = WorkingSet.outputs_of(conversation, kind: target.kind, key: target.key, strategy: strategy, cwd: cwd)
|
|
234
|
+
raise Refused, :not_in_set if outputs.empty? && !known?(conversation, target, strategy, cwd)
|
|
235
|
+
raise Refused, :already_pinned if pinned?(pins, target)
|
|
236
|
+
|
|
237
|
+
undone = nil
|
|
238
|
+
if outputs.any? { |output| drop_of(conversation, output) }
|
|
239
|
+
raise Refused, :undrop_first unless pending?(conversation, DROP, target)
|
|
240
|
+
|
|
241
|
+
undone = undrop(conversation, target, strategy: strategy, cwd: cwd, sent: false)
|
|
242
|
+
end
|
|
243
|
+
named = outputs.reject(&:derived)
|
|
244
|
+
Done.new(target: target, op: PIN, ids: named.map(&:id), freed_chars: 0, tail_chars: 0, free: true, undone: undone,
|
|
245
|
+
shared_with: shared_with(named))
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Unpins +target+: the model may forget it again. Only the pins are
|
|
249
|
+
# asked (a rollback may have taken the item out of the set).
|
|
250
|
+
# @return [Done]
|
|
251
|
+
# @raise [Refused]
|
|
252
|
+
def unpin(pins, target)
|
|
253
|
+
raise Refused, :not_pinned unless pinned?(pins, target)
|
|
254
|
+
|
|
255
|
+
Done.new(target: target, op: UNPIN, ids: [], freed_chars: 0, tail_chars: 0, free: true)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Adds +target+: the next turn's note asks the model to read it. +found+
|
|
259
|
+
# answers whether the item exists (a file, a memory; the caller's
|
|
260
|
+
# look). Refused for an item the model is sent whole now (live, a read
|
|
261
|
+
# of it sent), a dropped one (undrop it), and one added already.
|
|
262
|
+
# @param found [#call] target → nil when it exists, else a Refused reason
|
|
263
|
+
# @return [Done]
|
|
264
|
+
# @raise [Refused]
|
|
265
|
+
def add(conversation, target, strategy:, cwd:, found: ->(_) {})
|
|
266
|
+
raise Refused, :already_added if pending?(conversation, ADD, target)
|
|
267
|
+
|
|
268
|
+
reason = target.kind == WorkingSet::URL && !target.key.match?(%r{\Ahttps?://}i) ? :not_http : found.call(target)
|
|
269
|
+
raise Refused, reason if reason
|
|
270
|
+
|
|
271
|
+
outputs = WorkingSet.outputs_of(conversation, kind: target.kind, key: target.key, strategy: strategy, cwd: cwd)
|
|
272
|
+
raise Refused, :undrop_first if outputs.any? { |output| drop_of(conversation, output) }
|
|
273
|
+
raise Refused, :already_sent if outputs.any?(&:sent) && live?(conversation, target, strategy, cwd)
|
|
274
|
+
|
|
275
|
+
Done.new(target: target, op: ADD, ids: [], freed_chars: 0, tail_chars: 0, free: true)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Takes back +target+'s pending add (never sent: free).
|
|
279
|
+
# @return [Done]
|
|
280
|
+
# @raise [Refused]
|
|
281
|
+
def unadd(conversation, target)
|
|
282
|
+
raise Refused, :not_added unless pending?(conversation, ADD, target)
|
|
283
|
+
|
|
284
|
+
Done.new(target: target, op: UNADD, ids: [], freed_chars: 0, tail_chars: 0, free: true)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# +pins+ after +done+ (a pin or an unpin). A new frozen Array.
|
|
288
|
+
# @return [Array<WorkingSetPin>]
|
|
289
|
+
def pinning(pins, done)
|
|
290
|
+
pin = WorkingSetPin.new(kind: done.target.kind, key: done.target.key)
|
|
291
|
+
rest = Array(pins).reject { |held| held == pin }
|
|
292
|
+
(done.op == PIN ? rest + [pin] : rest).freeze
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# The other memory names +outputs+ hold (WorkingSet::Output#shared_with).
|
|
296
|
+
def shared_with(outputs) = outputs.flat_map(&:shared_with).uniq
|
|
297
|
+
|
|
298
|
+
# Whether +pins+ hold +target+.
|
|
299
|
+
def pinned?(pins, target)
|
|
300
|
+
Array(pins).any? { |pin| pin.kind == target.kind && pin.key == target.key }
|
|
153
301
|
end
|
|
154
302
|
|
|
155
303
|
# +output+'s drop (an LLMContextEdit), or nil.
|
|
@@ -165,26 +313,23 @@ module Samagotchi
|
|
|
165
313
|
end
|
|
166
314
|
end
|
|
167
315
|
|
|
316
|
+
# Whether the set holds +target+ live (not stale, forgotten, dropped).
|
|
317
|
+
def live?(conversation, target, strategy, cwd)
|
|
318
|
+
WorkingSet.build(conversation, cwd: cwd, strategy: strategy).items.any? do |item|
|
|
319
|
+
item.kind == target.kind && item.key == target.key && item.state == WorkingSet::LIVE
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
168
323
|
def layers(strategy) = LLMContextView.new(strategy: strategy).layers
|
|
169
324
|
|
|
170
325
|
# ── the tail note ───────────────────────────────────────────────────
|
|
171
326
|
|
|
172
|
-
# The index of the note the next turn will send
|
|
173
|
-
#
|
|
174
|
-
def pending_note_index(messages)
|
|
175
|
-
(messages.size - 1).downto(0) do |index|
|
|
176
|
-
message = messages[index]
|
|
177
|
-
return nil unless message[:role].to_s == "system"
|
|
178
|
-
return index if message[:kind].to_s == NOTE_KIND
|
|
179
|
-
end
|
|
180
|
-
nil
|
|
181
|
-
end
|
|
327
|
+
# The index of the note the next turn will send, or nil
|
|
328
|
+
# (WorkingSet.pending_note_index).
|
|
329
|
+
def pending_note_index(messages) = WorkingSet.pending_note_index(messages)
|
|
182
330
|
|
|
183
331
|
# The ops the pending note lists ([{"op", "kind", "key", "label"}]).
|
|
184
|
-
def pending_ops(messages)
|
|
185
|
-
index = pending_note_index(messages)
|
|
186
|
-
index ? Array(messages[index][OPS]).select { |op| op.is_a?(Hash) } : []
|
|
187
|
-
end
|
|
332
|
+
def pending_ops(messages) = WorkingSet.pending_ops(messages)
|
|
188
333
|
|
|
189
334
|
# Whether +target+'s +op+ is pending (made since the last turn).
|
|
190
335
|
def pending?(messages, op, target)
|
|
@@ -224,8 +369,8 @@ module Samagotchi
|
|
|
224
369
|
# The note listing +ops+: a tail system message, escaped like a context
|
|
225
370
|
# note (Prompt::ESCAPED_KINDS: it carries paths and URLs).
|
|
226
371
|
def note(ops)
|
|
227
|
-
drops = ops
|
|
228
|
-
undrops = ops
|
|
372
|
+
drops = labels(ops, DROP)
|
|
373
|
+
undrops = labels(ops, UNDROP)
|
|
229
374
|
parts = []
|
|
230
375
|
unless drops.empty?
|
|
231
376
|
parts << "the user dropped #{drops.join(", ")} from your context: you are sent a stub instead of " \
|
|
@@ -236,15 +381,34 @@ module Samagotchi
|
|
|
236
381
|
parts << "the user brought back #{undrops.join(", ")}: #{undrops.size == 1 ? "its outputs are" : "their outputs are"} " \
|
|
237
382
|
"sent whole again"
|
|
238
383
|
end
|
|
384
|
+
pins = labels(ops, PIN)
|
|
385
|
+
parts << "the user pinned #{pins.join(", ")}: forget_outputs won't forget #{pins.size == 1 ? "it" : "them"}" unless pins.empty?
|
|
386
|
+
unpins = labels(ops, UNPIN)
|
|
387
|
+
parts << "the user unpinned #{unpins.join(", ")}: you may forget #{unpins.size == 1 ? "it" : "them"} again" unless unpins.empty?
|
|
388
|
+
adds = ops.select { |entry| entry["op"] == ADD }
|
|
389
|
+
unless adds.empty?
|
|
390
|
+
parts << "the user added #{adds.map { |entry| entry["label"] }.join(", ")} to your context: before anything " \
|
|
391
|
+
"else, load #{adds.size == 1 ? "it" : "them"} with #{adds.map { |entry| call_of(entry) }.join(", ")}"
|
|
392
|
+
end
|
|
239
393
|
{ role: "system", kind: NOTE_KIND, content: "#{NOTE_OPEN}since your last turn #{parts.join("; ")}.]", OPS => ops }
|
|
240
394
|
end
|
|
241
395
|
|
|
396
|
+
def labels(ops, op) = ops.select { |entry| entry["op"] == op }.map { |entry| entry["label"] }
|
|
397
|
+
|
|
398
|
+
# The tool call that loads an added item, as the note asks for it: a
|
|
399
|
+
# file by its absolute path (a read resolves a relative one against
|
|
400
|
+
# the process's folder, which may not be the session's).
|
|
401
|
+
def call_of(entry)
|
|
402
|
+
tool = { WorkingSet::MEMORY => "memory_read", WorkingSet::URL => "web_fetch" }.fetch(entry["kind"], "read")
|
|
403
|
+
%(#{tool} "#{entry["key"]}")
|
|
404
|
+
end
|
|
405
|
+
|
|
242
406
|
# "12.4k tokens", chars/4.
|
|
243
407
|
def k(chars)
|
|
244
408
|
tokens = (chars / TokenUsage::CHARS_PER_TOKEN).ceil
|
|
245
409
|
tokens >= 1000 ? format("%.1fk tokens", tokens / 1000.0) : "#{tokens} tokens"
|
|
246
410
|
end
|
|
247
411
|
|
|
248
|
-
private_class_method :relative, :drop_of, :known?, :layers, :same
|
|
412
|
+
private_class_method :relative, :shared_with, :drop_of, :known?, :live?, :layers, :same?, :labels, :call_of
|
|
249
413
|
end
|
|
250
414
|
end
|
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.103.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -161,6 +161,7 @@ files:
|
|
|
161
161
|
- docs/internals/llm-context-forget.md
|
|
162
162
|
- docs/internals/llm-context-live.md
|
|
163
163
|
- docs/internals/prompt-caching.md
|
|
164
|
+
- docs/internals/remote-sessions.md
|
|
164
165
|
- docs/internals/tool-guardrails.md
|
|
165
166
|
- docs/internals/working-set.md
|
|
166
167
|
- docs/memory.md
|
|
@@ -194,6 +195,7 @@ files:
|
|
|
194
195
|
- lib/samagotchi/bridge_client.rb
|
|
195
196
|
- lib/samagotchi/bridge_client/event_stream.rb
|
|
196
197
|
- lib/samagotchi/bridge_client/sse_parser.rb
|
|
198
|
+
- lib/samagotchi/bridge_client/stream_request.rb
|
|
197
199
|
- lib/samagotchi/broadcast/recipients.rb
|
|
198
200
|
- lib/samagotchi/broadcast/scope_card.rb
|
|
199
201
|
- lib/samagotchi/broadcast/tags.rb
|
|
@@ -270,6 +272,7 @@ files:
|
|
|
270
272
|
- lib/samagotchi/bundles/work-cap/plugin.rb
|
|
271
273
|
- lib/samagotchi/cache_confirm.rb
|
|
272
274
|
- lib/samagotchi/cancellation_controller.rb
|
|
275
|
+
- lib/samagotchi/chi_invocation.rb
|
|
273
276
|
- lib/samagotchi/child_env.rb
|
|
274
277
|
- lib/samagotchi/child_move.rb
|
|
275
278
|
- lib/samagotchi/child_reports.rb
|
|
@@ -503,17 +506,28 @@ files:
|
|
|
503
506
|
- lib/samagotchi/relay_watcher.rb
|
|
504
507
|
- lib/samagotchi/reminder_queue.rb
|
|
505
508
|
- lib/samagotchi/reminder_store.rb
|
|
509
|
+
- lib/samagotchi/remote_command.rb
|
|
510
|
+
- lib/samagotchi/remotes.rb
|
|
506
511
|
- lib/samagotchi/reply_wait.rb
|
|
507
512
|
- lib/samagotchi/reply_wait/poll.rb
|
|
508
513
|
- lib/samagotchi/row_refs.rb
|
|
514
|
+
- lib/samagotchi/run_options.rb
|
|
509
515
|
- lib/samagotchi/sampling_settings.rb
|
|
510
516
|
- lib/samagotchi/sandbox_layout.rb
|
|
511
517
|
- lib/samagotchi/self_report.rb
|
|
512
518
|
- lib/samagotchi/send_command.rb
|
|
519
|
+
- lib/samagotchi/send_command/busy.rb
|
|
513
520
|
- lib/samagotchi/send_command/parent_gate.rb
|
|
514
521
|
- lib/samagotchi/send_command/wait_target.rb
|
|
515
522
|
- lib/samagotchi/served_model.rb
|
|
516
523
|
- lib/samagotchi/session.rb
|
|
524
|
+
- lib/samagotchi/session_access.rb
|
|
525
|
+
- lib/samagotchi/session_access/contract.rb
|
|
526
|
+
- lib/samagotchi/session_access/dispatch.rb
|
|
527
|
+
- lib/samagotchi/session_access/http_transport.rb
|
|
528
|
+
- lib/samagotchi/session_access/local.rb
|
|
529
|
+
- lib/samagotchi/session_access/remote.rb
|
|
530
|
+
- lib/samagotchi/session_access/web_bridge_client.rb
|
|
517
531
|
- lib/samagotchi/session_archive_command.rb
|
|
518
532
|
- lib/samagotchi/session_chain.rb
|
|
519
533
|
- lib/samagotchi/session_commands.rb
|
|
@@ -544,6 +558,7 @@ files:
|
|
|
544
558
|
- lib/samagotchi/task_watch.rb
|
|
545
559
|
- lib/samagotchi/terminal_ui.rb
|
|
546
560
|
- lib/samagotchi/terminal_ui/attach_launcher.rb
|
|
561
|
+
- lib/samagotchi/terminal_ui/attach_link.rb
|
|
547
562
|
- lib/samagotchi/terminal_ui/attached_loop.rb
|
|
548
563
|
- lib/samagotchi/terminal_ui/attached_view.rb
|
|
549
564
|
- lib/samagotchi/terminal_ui/between_turns.rb
|
|
@@ -553,6 +568,7 @@ files:
|
|
|
553
568
|
- lib/samagotchi/terminal_ui/input_support.rb
|
|
554
569
|
- lib/samagotchi/terminal_ui/line_reader.rb
|
|
555
570
|
- lib/samagotchi/terminal_ui/live_region.rb
|
|
571
|
+
- lib/samagotchi/terminal_ui/local_link.rb
|
|
556
572
|
- lib/samagotchi/terminal_ui/model_segments_text.rb
|
|
557
573
|
- lib/samagotchi/terminal_ui/one_shot_sink.rb
|
|
558
574
|
- lib/samagotchi/terminal_ui/plain_surface.rb
|
|
@@ -767,6 +783,7 @@ files:
|
|
|
767
783
|
- lib/samagotchi/web/public/working_set_view.js
|
|
768
784
|
- lib/samagotchi/web/qr.rb
|
|
769
785
|
- lib/samagotchi/web/server.rb
|
|
786
|
+
- lib/samagotchi/web/session_access_routes.rb
|
|
770
787
|
- lib/samagotchi/web/session_hub.rb
|
|
771
788
|
- lib/samagotchi/web/session_summary.rb
|
|
772
789
|
- lib/samagotchi/web/token.rb
|
|
@@ -779,6 +796,7 @@ files:
|
|
|
779
796
|
- lib/samagotchi/worker_wakes.rb
|
|
780
797
|
- lib/samagotchi/working_set.rb
|
|
781
798
|
- lib/samagotchi/working_set_command.rb
|
|
799
|
+
- lib/samagotchi/working_set_pin.rb
|
|
782
800
|
- lib/samagotchi/working_set_steer.rb
|
|
783
801
|
- lib/samagotchi/yaml_lines.rb
|
|
784
802
|
homepage: https://github.com/dm1try/samagotchi
|