samagotchi 0.152.0 → 0.154.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 +45 -1
- data/README.md +2 -3
- data/bin/chi +23 -24
- data/docs/architecture.md +30 -33
- data/docs/broadcast.md +5 -5
- data/docs/bundle-authoring.md +1 -1
- data/docs/bundles.md +93 -12
- data/docs/cli.md +83 -99
- data/docs/configuration.md +46 -40
- data/docs/context.md +3 -3
- data/docs/desktop.md +1 -1
- data/docs/guardrails.md +15 -15
- data/docs/hooks.md +13 -12
- data/docs/internals/anthropic.md +3 -3
- data/docs/internals/attached-context.md +5 -2
- data/docs/internals/background-tasks.md +3 -4
- data/docs/internals/block-renderers.md +4 -0
- data/docs/internals/bridge.md +3 -3
- data/docs/internals/context-telemetry.md +1 -1
- data/docs/internals/engine.md +42 -12
- data/docs/internals/objectives.md +6 -5
- data/docs/internals/participants.md +8 -8
- data/docs/internals/prompt-caching.md +8 -4
- data/docs/internals/remote-sessions.md +7 -7
- data/docs/internals/web-frontend.md +20 -17
- data/docs/internals/web-push.md +1 -1
- data/docs/memory.md +2 -2
- data/docs/plugins.md +52 -43
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +25 -25
- data/docs/sub-agent.md +2 -2
- data/docs/system-one.md +1 -1
- data/docs/testing.md +18 -8
- data/lib/samagotchi/answer_text.rb +34 -0
- data/lib/samagotchi/bridge.rb +43 -32
- data/lib/samagotchi/bridge_client.rb +10 -9
- data/lib/samagotchi/broadcast/recipients.rb +2 -2
- data/lib/samagotchi/broadcast/triage.rb +1 -1
- data/lib/samagotchi/broadcast_command.rb +5 -5
- data/lib/samagotchi/bundle_command/try/command.rb +1 -1
- data/lib/samagotchi/bundle_command/try/run.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +2 -2
- data/lib/samagotchi/bundles/decision-card/manifest.yml +4 -4
- data/lib/samagotchi/bundles/decision-card/plugin.rb +1 -9
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +3 -4
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/bundles/web-search/manifest.yml +13 -0
- data/lib/samagotchi/bundles/web-search/plugin.rb +174 -0
- data/lib/samagotchi/child_move.rb +3 -3
- data/lib/samagotchi/commands/registry.rb +6 -6
- data/lib/samagotchi/composer_route.rb +4 -5
- data/lib/samagotchi/config.rb +14 -12
- data/lib/samagotchi/context_candidate_choice.rb +1 -1
- data/lib/samagotchi/context_candidates.rb +4 -3
- data/lib/samagotchi/continue_offer.rb +1 -1
- data/lib/samagotchi/debug_log.rb +1 -1
- data/lib/samagotchi/degrade.rb +75 -0
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +2 -2
- data/lib/samagotchi/engine/cards.rb +87 -0
- data/lib/samagotchi/engine/context_judge.rb +138 -0
- data/lib/samagotchi/engine/events.rb +131 -0
- data/lib/samagotchi/engine/guardrails.rb +87 -0
- data/lib/samagotchi/engine/hooks.rb +119 -0
- data/lib/samagotchi/engine/messages.rb +124 -0
- data/lib/samagotchi/engine/model_settings.rb +127 -0
- data/lib/samagotchi/engine/models.rb +261 -0
- data/lib/samagotchi/engine/objectives.rb +116 -0
- data/lib/samagotchi/engine/participants.rb +86 -0
- data/lib/samagotchi/engine/plugins.rb +208 -0
- data/lib/samagotchi/engine/questions.rb +98 -0
- data/lib/samagotchi/engine/recap.rb +130 -0
- data/lib/samagotchi/engine/session.rb +131 -0
- data/lib/samagotchi/engine/side_requests.rb +135 -0
- data/lib/samagotchi/engine/steering.rb +134 -0
- data/lib/samagotchi/engine/toolkit.rb +269 -0
- data/lib/samagotchi/engine/turn_end.rb +180 -0
- data/lib/samagotchi/engine/turn_helpers.rb +160 -0
- data/lib/samagotchi/engine/verify.rb +153 -0
- data/lib/samagotchi/engine.rb +63 -2642
- data/lib/samagotchi/grants.rb +575 -0
- data/lib/samagotchi/guardrail_wiring.rb +2 -2
- data/lib/samagotchi/guardrails/context.rb +2 -3
- data/lib/samagotchi/guardrails/parent_approvals.rb +2 -2
- data/lib/samagotchi/guardrails/parent_policy.rb +2 -2
- data/lib/samagotchi/guardrails/review_boundary.rb +4 -3
- data/lib/samagotchi/hook_routes.rb +3 -3
- data/lib/samagotchi/idle_recap.rb +4 -5
- data/lib/samagotchi/idle_reminders.rb +3 -4
- data/lib/samagotchi/iteration_limit.rb +2 -2
- data/lib/samagotchi/kernel_loop.rb +10 -11
- data/lib/samagotchi/launch_mode.rb +13 -32
- data/lib/samagotchi/level_setting_command.rb +3 -2
- data/lib/samagotchi/llm_context_command.rb +3 -2
- data/lib/samagotchi/llm_context_stale.rb +2 -2
- data/lib/samagotchi/log.rb +7 -7
- data/lib/samagotchi/log_line.rb +1 -1
- data/lib/samagotchi/log_path.rb +1 -1
- data/lib/samagotchi/message_parts.rb +3 -2
- data/lib/samagotchi/note_command.rb +1 -1
- data/lib/samagotchi/note_delivery.rb +18 -8
- data/lib/samagotchi/objectives.rb +24 -3
- data/lib/samagotchi/objectives_check.rb +8 -27
- data/lib/samagotchi/objectives_command.rb +4 -69
- data/lib/samagotchi/objectives_edit.rb +87 -0
- data/lib/samagotchi/output_formatter.rb +7 -1
- data/lib/samagotchi/owner_lock.rb +7 -1
- data/lib/samagotchi/parent_answers_command.rb +5 -4
- data/lib/samagotchi/participant_exposure.rb +42 -0
- data/lib/samagotchi/participant_mode.rb +1 -1
- data/lib/samagotchi/participant_name.rb +1 -1
- data/lib/samagotchi/participants.rb +17 -17
- data/lib/samagotchi/participants_watch.rb +1 -1
- data/lib/samagotchi/pending_input_queue.rb +2 -2
- data/lib/samagotchi/plugin/api.rb +3 -2
- data/lib/samagotchi/plugin/context.rb +21 -7
- data/lib/samagotchi/plugin/peer.rb +2 -1
- data/lib/samagotchi/plugin/sessions.rb +6 -4
- data/lib/samagotchi/plugin_tasks.rb +1 -2
- data/lib/samagotchi/private_files.rb +29 -0
- data/lib/samagotchi/project_env.rb +2 -2
- data/lib/samagotchi/question_desk.rb +8 -7
- data/lib/samagotchi/recap_command.rb +2 -1
- data/lib/samagotchi/recap_setup.rb +1 -1
- data/lib/samagotchi/recap_store.rb +1 -1
- data/lib/samagotchi/redact.rb +86 -0
- data/lib/samagotchi/reminder_queue.rb +2 -2
- data/lib/samagotchi/remote_attach_command.rb +1 -1
- data/lib/samagotchi/reply_wait/poll.rb +2 -2
- data/lib/samagotchi/review_start.rb +1 -1
- data/lib/samagotchi/run_options.rb +6 -6
- data/lib/samagotchi/self_report.rb +42 -39
- data/lib/samagotchi/send_command/busy.rb +1 -1
- data/lib/samagotchi/send_command.rb +4 -1
- data/lib/samagotchi/session.rb +11 -2
- data/lib/samagotchi/session_access/contract.rb +3 -3
- data/lib/samagotchi/session_access/local.rb +9 -9
- data/lib/samagotchi/session_access/remote.rb +17 -7
- data/lib/samagotchi/session_access.rb +4 -4
- data/lib/samagotchi/session_archive_command.rb +2 -2
- data/lib/samagotchi/session_chain.rb +8 -6
- data/lib/samagotchi/session_commands.rb +45 -48
- data/lib/samagotchi/session_continue.rb +1 -1
- data/lib/samagotchi/session_delete_command.rb +4 -4
- data/lib/samagotchi/session_manager.rb +59 -36
- data/lib/samagotchi/session_metrics.rb +2 -2
- data/lib/samagotchi/session_model_settings.rb +3 -2
- data/lib/samagotchi/session_participants.rb +1 -1
- data/lib/samagotchi/session_retention.rb +3 -3
- data/lib/samagotchi/sessions_command.rb +4 -4
- data/lib/samagotchi/share_command.rb +10 -10
- data/lib/samagotchi/steer.rb +1 -1
- data/lib/samagotchi/system_prompt.rb +102 -109
- data/lib/samagotchi/task_watch.rb +1 -1
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +72 -10
- data/lib/samagotchi/terminal_ui/attached_loop/composer.rb +12 -9
- data/lib/samagotchi/terminal_ui/attached_loop/exit_commands.rb +16 -0
- data/lib/samagotchi/terminal_ui/attached_loop/questions.rb +2 -3
- data/lib/samagotchi/terminal_ui/attached_loop.rb +17 -8
- data/lib/samagotchi/terminal_ui/attached_view.rb +2 -3
- data/lib/samagotchi/terminal_ui/event_renderer.rb +3 -5
- data/lib/samagotchi/terminal_ui/formatting.rb +7 -7
- data/lib/samagotchi/terminal_ui/input_support.rb +1 -7
- data/lib/samagotchi/terminal_ui/line_reader.rb +2 -2
- data/lib/samagotchi/terminal_ui/live_region.rb +2 -2
- data/lib/samagotchi/terminal_ui/log_tail.rb +135 -0
- data/lib/samagotchi/terminal_ui/one_shot_sink.rb +2 -2
- data/lib/samagotchi/terminal_ui/plain_surface.rb +1 -1
- data/lib/samagotchi/terminal_ui/question_prompt.rb +4 -5
- data/lib/samagotchi/terminal_ui/reline_seam.rb +3 -3
- data/lib/samagotchi/terminal_ui/status_row.rb +7 -8
- data/lib/samagotchi/terminal_ui.rb +142 -1387
- data/lib/samagotchi/title_command.rb +2 -2
- data/lib/samagotchi/tool_runner.rb +14 -16
- data/lib/samagotchi/toolkit.rb +8 -5
- data/lib/samagotchi/toolkit_binding.rb +30 -5
- data/lib/samagotchi/toolkit_command.rb +2 -1
- data/lib/samagotchi/toolkit_report_command.rb +9 -5
- data/lib/samagotchi/toolkit_suggest.rb +12 -6
- data/lib/samagotchi/tools/builtin_calls.rb +14 -10
- data/lib/samagotchi/tools/deferred_tools.rb +2 -2
- data/lib/samagotchi/tools/delegate.rb +3 -1
- data/lib/samagotchi/tools/delegate_cursor.rb +3 -3
- data/lib/samagotchi/tools/delegate_wait.rb +1 -1
- data/lib/samagotchi/tools/objectives_tool.rb +2 -2
- data/lib/samagotchi/tools/registry.rb +44 -20
- data/lib/samagotchi/tools/send_note.rb +2 -1
- data/lib/samagotchi/tools/tool_index.rb +27 -21
- data/lib/samagotchi/turn_endings.rb +2 -2
- data/lib/samagotchi/turn_flow.rb +4 -5
- data/lib/samagotchi/update_hint.rb +2 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +39 -50
- data/lib/samagotchi/web/participant_access.rb +2 -2
- data/lib/samagotchi/web/participant_review.rb +5 -5
- data/lib/samagotchi/web/participant_stream_filter.rb +8 -8
- data/lib/samagotchi/web/participant_view.rb +2 -82
- data/lib/samagotchi/web/participants_admin.rb +1 -1
- data/lib/samagotchi/web/public/app.js +61 -357
- data/lib/samagotchi/web/public/block_host.js +35 -8
- data/lib/samagotchi/web/public/block_renderers.css +9 -5
- data/lib/samagotchi/web/public/chains.js +2 -2
- data/lib/samagotchi/web/public/claim_link.js +4 -4
- data/lib/samagotchi/web/public/composer_mode.js +110 -0
- data/lib/samagotchi/web/public/format.js +3 -3
- data/lib/samagotchi/web/public/owner_claim.js +81 -0
- data/lib/samagotchi/web/public/owner_verify.js +112 -0
- data/lib/samagotchi/web/public/session_fields.js +3 -3
- data/lib/samagotchi/web/public/strip_grip.js +3 -3
- data/lib/samagotchi/web/public/strip_hidden.js +65 -0
- data/lib/samagotchi/web/public/version_notices.js +132 -0
- data/lib/samagotchi/web/push/store.rb +1 -1
- data/lib/samagotchi/web/session_access_routes.rb +5 -4
- data/lib/samagotchi/web/session_control.rb +12 -8
- data/lib/samagotchi/web/session_create.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +2 -2
- data/lib/samagotchi/web/session_show.rb +2 -2
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +106 -9
- data/lib/samagotchi/worker_link.rb +3 -3
- data/lib/samagotchi/working_set_command.rb +3 -2
- metadata +36 -6
- data/lib/samagotchi/terminal_ui/between_turns.rb +0 -138
- data/lib/samagotchi/terminal_ui/repl_input.rb +0 -152
- data/lib/samagotchi/web/grants.rb +0 -575
- data/lib/samagotchi/web/participant_exposure.rb +0 -44
- data/lib/samagotchi/web/private_files.rb +0 -31
|
@@ -25,8 +25,8 @@ module Samagotchi
|
|
|
25
25
|
# SessionMetrics is a persistent, error-isolated collector of per-session
|
|
26
26
|
# analytics. It implements the same #call(event) sink contract as
|
|
27
27
|
# SessionObserver subscribers, so Engine#run_turn feeds it (it forwards every
|
|
28
|
-
# event to its SessionObserver):
|
|
29
|
-
#
|
|
28
|
+
# event to its SessionObserver): a --non-interactive one-shot and the
|
|
29
|
+
# session workers.
|
|
30
30
|
#
|
|
31
31
|
# Each finished turn leaves a record (status, timings, model, prompt and
|
|
32
32
|
# completion tokens, tool calls, iterations, retries, cuts); session totals are
|
|
@@ -7,6 +7,7 @@ require_relative "session_setting"
|
|
|
7
7
|
require_relative "steer_cut"
|
|
8
8
|
require_relative "steer_cut_after"
|
|
9
9
|
require_relative "thinking"
|
|
10
|
+
require_relative "session"
|
|
10
11
|
|
|
11
12
|
module Samagotchi
|
|
12
13
|
# The session's settings on its effective model, as the Engine reads them:
|
|
@@ -163,7 +164,7 @@ module Samagotchi
|
|
|
163
164
|
# @raise [ArgumentError] with no session yet
|
|
164
165
|
def reasoning_history_override=(level)
|
|
165
166
|
session = @session.call
|
|
166
|
-
raise ArgumentError,
|
|
167
|
+
raise ArgumentError, Session::NONE_YET unless session
|
|
167
168
|
|
|
168
169
|
SessionSetting::REASONING_HISTORY.set(session, level)
|
|
169
170
|
end
|
|
@@ -178,7 +179,7 @@ module Samagotchi
|
|
|
178
179
|
# @raise [ArgumentError] with no session yet
|
|
179
180
|
def thinking_override=(level)
|
|
180
181
|
session = @session.call
|
|
181
|
-
raise ArgumentError,
|
|
182
|
+
raise ArgumentError, Session::NONE_YET unless session
|
|
182
183
|
|
|
183
184
|
SessionSetting::THINKING.set(session, level)
|
|
184
185
|
end
|
|
@@ -4,7 +4,7 @@ require_relative "steer"
|
|
|
4
4
|
require_relative "participant_mode"
|
|
5
5
|
|
|
6
6
|
module Samagotchi
|
|
7
|
-
# Loaded only when a session is read: it brings
|
|
7
|
+
# Loaded only when a session is read: it brings Grants.
|
|
8
8
|
autoload :Participants, File.expand_path("participants", __dir__)
|
|
9
9
|
|
|
10
10
|
# What a session's Engine knows of its participants: participants.json
|
|
@@ -30,7 +30,7 @@ module Samagotchi
|
|
|
30
30
|
DEFAULT_KEEP_STATUS = [].freeze
|
|
31
31
|
|
|
32
32
|
# How long a session may sit empty before the sweep takes it: its
|
|
33
|
-
# worker
|
|
33
|
+
# worker deletes it as it leaves, so the sweep only catches
|
|
34
34
|
# those killed first (a reboot, kill -9).
|
|
35
35
|
EMPTY_GRACE_SECONDS = 3600
|
|
36
36
|
|
|
@@ -69,7 +69,7 @@ module Samagotchi
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# Directories with no session file and nothing but the skeleton, nobody
|
|
72
|
-
# owning them: a
|
|
72
|
+
# owning them: a one-shot killed before its first save, or a worker woken
|
|
73
73
|
# just as its session was discarded.
|
|
74
74
|
# @return [Array<String>] their ids
|
|
75
75
|
private_class_method def self.prune_orphan_dirs(state_dir, dry_run:)
|
|
@@ -161,7 +161,7 @@ module Samagotchi
|
|
|
161
161
|
# +any_age+ makes every session eligible, whatever its age and the
|
|
162
162
|
# count (`chi sessions clean` with no --days: test runs are throwaway).
|
|
163
163
|
#
|
|
164
|
-
# A scratch session nobody owns (its
|
|
164
|
+
# A scratch session nobody owns (its terminal and worker were killed) goes whatever its
|
|
165
165
|
# age, its status and the count; +test_only+ takes it too.
|
|
166
166
|
#
|
|
167
167
|
# +max_count+ counts the sessions that stay: one deleted whatever its
|
|
@@ -21,7 +21,7 @@ module Samagotchi
|
|
|
21
21
|
# `chi sessions`: list, stop, restart, archive/unarchive, delete, prune,
|
|
22
22
|
# clean and stats sessions from the shell (bin/chi dispatches here before
|
|
23
23
|
# OptionParser; the subcommands have their own flags). Not SessionCommands,
|
|
24
|
-
# the
|
|
24
|
+
# the slash commands a session's worker runs (session_commands.rb).
|
|
25
25
|
class SessionsCommand
|
|
26
26
|
include CLI::Command
|
|
27
27
|
|
|
@@ -207,15 +207,15 @@ module Samagotchi
|
|
|
207
207
|
)
|
|
208
208
|
case format
|
|
209
209
|
when "json"
|
|
210
|
-
# owner: "worker", "tui" (a chi
|
|
210
|
+
# owner: "worker", "tui" (a one-turn chi run: it takes no notes or messages)
|
|
211
211
|
# or nil; recap: the saved recap's first sentence, or nil; project:
|
|
212
212
|
# its git project's root, or nil
|
|
213
213
|
# parent_id: the session that delegated it (the delegate tool), or nil
|
|
214
214
|
# delegate: started by the delegate tool (a fork has a parent_id too)
|
|
215
215
|
# continues: the session it continues (its chain's previous link), or nil
|
|
216
216
|
# archived: hidden from the lists (only with --archived can it be true)
|
|
217
|
-
# scratch: a `chi scratch` session (deleted when its
|
|
218
|
-
# leftover one at the next sweep)
|
|
217
|
+
# scratch: a `chi scratch` session (deleted when its terminal leaves,
|
|
218
|
+
# a leftover one at the next sweep)
|
|
219
219
|
# ctx_pct: how full the context was after the last turn, or nil
|
|
220
220
|
# title: the session's own name (/title; desc shows it too), or nil;
|
|
221
221
|
# auto_title: the one its recap gave it (RecapTitle), or nil
|
|
@@ -8,12 +8,12 @@ require_relative "cli/flags"
|
|
|
8
8
|
require_relative "session"
|
|
9
9
|
require_relative "session_access"
|
|
10
10
|
require_relative "session_access/local"
|
|
11
|
-
require_relative "
|
|
11
|
+
require_relative "grants"
|
|
12
12
|
require_relative "web/qr"
|
|
13
13
|
|
|
14
14
|
module Samagotchi
|
|
15
15
|
# `chi share`: invite a participant to review a session (the
|
|
16
|
-
# shared-sessions plan: a
|
|
16
|
+
# shared-sessions plan: a Grants grant, one session, one person or
|
|
17
17
|
# agent, 7 days unless --expires says otherwise), list the participants,
|
|
18
18
|
# revoke them. Local or through chi --remote NAME (SessionAccess). The
|
|
19
19
|
# token is printed once, in the link; chi keeps only its digest. See
|
|
@@ -95,9 +95,9 @@ module Samagotchi
|
|
|
95
95
|
return parsed if parsed.is_a?(Integer)
|
|
96
96
|
|
|
97
97
|
send(:"run_#{@sub || "invite"}", parsed.options, parsed.args)
|
|
98
|
-
rescue SessionAccess::BadRequest,
|
|
98
|
+
rescue SessionAccess::BadRequest, Grants::BadIdentity => e
|
|
99
99
|
usage_error(e.message)
|
|
100
|
-
rescue
|
|
100
|
+
rescue Grants::Unwritable => e
|
|
101
101
|
error_line("#{command_name}: #{e.message}")
|
|
102
102
|
CLI::Exit::FAILED
|
|
103
103
|
rescue ArgumentError => e
|
|
@@ -114,13 +114,13 @@ module Samagotchi
|
|
|
114
114
|
return usage_error("give one session ID") unless args.size == 1
|
|
115
115
|
return usage_error("--name NAME: who the link is for") if options[:name].to_s.strip.empty?
|
|
116
116
|
|
|
117
|
-
ttl =
|
|
117
|
+
ttl = Grants.parse_ttl(options[:expires])
|
|
118
118
|
return usage_error("--expires takes Nh or Nd, 1h to 30d (e.g. 3d)") if ttl == false
|
|
119
119
|
|
|
120
120
|
cap = options[:cap] && dollars(options[:cap])
|
|
121
121
|
return usage_error(CAP_USAGE) if cap == false
|
|
122
122
|
|
|
123
|
-
identity = options[:identity] &&
|
|
123
|
+
identity = options[:identity] && Grants.clean_identity(options[:identity])
|
|
124
124
|
|
|
125
125
|
id = @access.resolve(args.first)
|
|
126
126
|
invitation = @access.invite(id, name: options[:name], kind: options[:agent] ? "agent" : "human", ttl: ttl, cap: cap,
|
|
@@ -149,13 +149,13 @@ module Samagotchi
|
|
|
149
149
|
0
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
CAP_USAGE = "a cap is dollars, $0 to $#{
|
|
152
|
+
CAP_USAGE = "a cap is dollars, $0 to $#{Grants::MAX_CAP.to_i} (e.g. --cap 5)".freeze
|
|
153
153
|
|
|
154
154
|
def run_cap(_options, args)
|
|
155
155
|
return usage_error("give a session ID, a participant and dollars (chi share cap ID p_… 5)") unless args.size == 3
|
|
156
156
|
|
|
157
157
|
participant = args[1]
|
|
158
|
-
return usage_error("not a participant: #{participant} (chi share ls lists them)") unless
|
|
158
|
+
return usage_error("not a participant: #{participant} (chi share ls lists them)") unless Grants::ID_RE.match?(participant)
|
|
159
159
|
|
|
160
160
|
cap = dollars(args[2])
|
|
161
161
|
return usage_error(CAP_USAGE) if cap == false
|
|
@@ -172,7 +172,7 @@ module Samagotchi
|
|
|
172
172
|
return false unless text.match?(/\A\d+(?:\.\d+)?\z/)
|
|
173
173
|
|
|
174
174
|
value = text.to_f
|
|
175
|
-
|
|
175
|
+
Grants.cap?(value) ? value : false
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
def money(value) = value.nil? ? "-" : format("$%.2f", value)
|
|
@@ -184,7 +184,7 @@ module Samagotchi
|
|
|
184
184
|
return usage_error("give a session ID (and a participant), or --all")
|
|
185
185
|
end
|
|
186
186
|
participant = args[1]
|
|
187
|
-
return usage_error("not a participant: #{participant} (chi share ls lists them)") if participant && !
|
|
187
|
+
return usage_error("not a participant: #{participant} (chi share ls lists them)") if participant && !Grants::ID_RE.match?(participant)
|
|
188
188
|
|
|
189
189
|
id = args.first && @access.resolve(args.first)
|
|
190
190
|
revoked = @access.revoke_participants(id, participant: participant)
|
data/lib/samagotchi/steer.rb
CHANGED
|
@@ -136,7 +136,7 @@ module Samagotchi
|
|
|
136
136
|
PARTICIPANT_SOURCE = "participant"
|
|
137
137
|
# The owner's author, when one is written out; absent means the same.
|
|
138
138
|
OWNER_AUTHOR = "owner"
|
|
139
|
-
# A participant's author: this prefix, then its id (
|
|
139
|
+
# A participant's author: this prefix, then its id (Grants'
|
|
140
140
|
# grant id), "p:<id>" (#author_for, #participant_id).
|
|
141
141
|
PARTICIPANT_PREFIX = "p:"
|
|
142
142
|
# A participant's message's keys, which every message copy keeps.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "config"
|
|
4
4
|
require_relative "log"
|
|
5
|
+
require_relative "degrade"
|
|
5
6
|
require_relative "log_path"
|
|
6
7
|
require_relative "memory_paths"
|
|
7
8
|
require_relative "paths"
|
|
@@ -74,11 +75,7 @@ module Samagotchi
|
|
|
74
75
|
@requested_memories = effective_preload_list(preload_memory_list(memories))
|
|
75
76
|
# Read once, as the memories: list is: a config edit is picked up by
|
|
76
77
|
# a new process. The files' text is read at each build.
|
|
77
|
-
@config_appended =
|
|
78
|
-
ConfigFile.appended_prompt_files
|
|
79
|
-
rescue StandardError
|
|
80
|
-
[]
|
|
81
|
-
end
|
|
78
|
+
@config_appended = Degrade.call(:prompt, "appended_list_unreadable", fallback: []) { ConfigFile.appended_prompt_files }
|
|
82
79
|
@cli_appended = Array(appended_files).map { |path| File.expand_path(path.to_s) }
|
|
83
80
|
end
|
|
84
81
|
|
|
@@ -386,11 +383,11 @@ module Samagotchi
|
|
|
386
383
|
# (Plugin::PromptSections). nil with none: the prompt is what it was
|
|
387
384
|
# before prompt sections.
|
|
388
385
|
def plugin_prompt_section
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
386
|
+
Degrade.call(:prompt, "plugin_section_failed") do
|
|
387
|
+
built = Array(@sections_lookup.call)
|
|
388
|
+
@section_plugins = built
|
|
389
|
+
Plugin::PromptSections.text(built)
|
|
390
|
+
end
|
|
394
391
|
end
|
|
395
392
|
|
|
396
393
|
def with_tail_declarations(prompt, chat:, layers:)
|
|
@@ -405,35 +402,35 @@ module Samagotchi
|
|
|
405
402
|
# Identity is not tracked as an "activated" memory for the sticky status line
|
|
406
403
|
# to avoid always showing `mem: identity`.
|
|
407
404
|
def system_identity_section
|
|
408
|
-
|
|
409
|
-
|
|
405
|
+
Degrade.call(:prompt, "identity_unreadable") do
|
|
406
|
+
DEFAULT_SYSTEM_MEMORIES.each do |name|
|
|
407
|
+
next if memory_muted?(name)
|
|
410
408
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
409
|
+
body = Tools::MemoryRead.call(name, scope: "system", **overlay_keys)
|
|
410
|
+
next if body.start_with?("Error:")
|
|
411
|
+
next if body.strip.empty?
|
|
414
412
|
|
|
415
|
-
|
|
413
|
+
return "System identity (auto-loaded, scope=system):\n#{body}"
|
|
414
|
+
end
|
|
415
|
+
nil
|
|
416
416
|
end
|
|
417
|
-
nil
|
|
418
|
-
rescue StandardError
|
|
419
|
-
nil
|
|
420
417
|
end
|
|
421
418
|
|
|
422
419
|
# The model notes for the session's model (ModelNotes), right after
|
|
423
420
|
# identity: a heading per scope, each note under its name, the models:
|
|
424
421
|
# line left out. nil without any, so the prompt stays as it was.
|
|
425
422
|
def model_notes_section
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
423
|
+
Degrade.call(:prompt, "model_notes_failed") do
|
|
424
|
+
@section_notes = []
|
|
425
|
+
notes = Array(@model_notes_lookup.call)
|
|
426
|
+
return nil if notes.empty?
|
|
427
|
+
|
|
428
|
+
ref = @model_lookup.call&.ref
|
|
429
|
+
notes.group_by(&:scope).map do |scope, list|
|
|
430
|
+
heading = ref.to_s.empty? ? "Model notes (scope=#{scope}):" : "Model notes (for #{ref}, scope=#{scope}):"
|
|
431
|
+
[heading, *list.map { |note| "## #{note.name}\n#{note.body}" }].join("\n\n")
|
|
432
|
+
end.join("\n\n").tap { @section_notes = notes }
|
|
433
|
+
end
|
|
437
434
|
end
|
|
438
435
|
|
|
439
436
|
# ── Memory helpers ─────────────────────────────────────────────────────────
|
|
@@ -441,23 +438,23 @@ module Samagotchi
|
|
|
441
438
|
# The session model's overlay keys, so the prompt's own memory bodies
|
|
442
439
|
# (identity, preloads) get `<name>.<key>.md` as memory_read gives it.
|
|
443
440
|
def overlay_keys
|
|
444
|
-
|
|
445
|
-
|
|
441
|
+
Degrade.call(:prompt, "overlay_keys_failed", fallback: {}) do
|
|
442
|
+
model = @model_lookup.call
|
|
443
|
+
return {} unless model&.key
|
|
446
444
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
{}
|
|
445
|
+
{ model_key: model.key, fallback_model_key: model.fallback_key }
|
|
446
|
+
end
|
|
450
447
|
end
|
|
451
448
|
|
|
452
449
|
# { system: { tokens:, lines: }, project: { … } } of the index texts
|
|
453
450
|
# the prompt holds; nil when they can't be measured.
|
|
454
451
|
def measure_indexes(indexes)
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
[
|
|
452
|
+
Degrade.call(:prompt, "index_size_failed") do
|
|
453
|
+
chars_per_token = MemoryBundle::IndexSize.chars_per_token
|
|
454
|
+
%i[system project].to_h do |scope|
|
|
455
|
+
[scope, MemoryBundle::IndexSize.of_text(scope, indexes[scope], chars_per_token: chars_per_token).figures]
|
|
456
|
+
end
|
|
458
457
|
end
|
|
459
|
-
rescue StandardError
|
|
460
|
-
nil
|
|
461
458
|
end
|
|
462
459
|
|
|
463
460
|
# The scope's index text without the muted memories' lines, nor the
|
|
@@ -473,11 +470,7 @@ module Samagotchi
|
|
|
473
470
|
# comma-split and appended without duplicates (same ref shape as --memory:
|
|
474
471
|
# bare name or scope/name).
|
|
475
472
|
def preload_memory_list(cli_memories)
|
|
476
|
-
baseline =
|
|
477
|
-
ConfigFile.preloaded_memories
|
|
478
|
-
rescue StandardError
|
|
479
|
-
[]
|
|
480
|
-
end
|
|
473
|
+
baseline = Degrade.call(:prompt, "preload_list_unreadable", fallback: []) { ConfigFile.preloaded_memories }
|
|
481
474
|
|
|
482
475
|
merged = Array(baseline).dup
|
|
483
476
|
# For the warning when one can't be loaded: it names where it came from.
|
|
@@ -555,15 +548,15 @@ module Samagotchi
|
|
|
555
548
|
# The AGENTS.md chain from the top of the git work tree down to the cwd
|
|
556
549
|
# (PromptFiles.agents_sections), one section per file with text.
|
|
557
550
|
def project_specific_description
|
|
558
|
-
|
|
551
|
+
Degrade.call(:prompt, "agents_md_unreadable") do
|
|
552
|
+
return nil if skip_agent_description?
|
|
559
553
|
|
|
560
|
-
|
|
561
|
-
|
|
554
|
+
sections = PromptFiles.agents_sections(Dir.pwd)
|
|
555
|
+
return nil if sections.empty?
|
|
562
556
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
nil
|
|
557
|
+
@section_files&.concat(sections.map(&:path))
|
|
558
|
+
sections.map(&:text).join("\n\n")
|
|
559
|
+
end
|
|
567
560
|
end
|
|
568
561
|
|
|
569
562
|
# The appended files' text, right after AGENTS.md (the stable, cached
|
|
@@ -572,26 +565,26 @@ module Samagotchi
|
|
|
572
565
|
# then --append-system-prompt's. A file the AGENTS.md chain holds, a
|
|
573
566
|
# repeat and an empty or vanished file are left out.
|
|
574
567
|
def appended_section
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
568
|
+
Degrade.call(:prompt, "appended_files_failed") do
|
|
569
|
+
cwd = Dir.pwd
|
|
570
|
+
top = MemoryPaths.work_tree_root(cwd)
|
|
571
|
+
seen = Set.new((skip_agent_description? ? [] : PromptFiles.agents_paths(cwd)).map { |path| canonical_path(path) })
|
|
572
|
+
sections = []
|
|
573
|
+
appended_candidates(cwd).each do |path, source|
|
|
574
|
+
next unless seen.add?(canonical_path(path))
|
|
575
|
+
|
|
576
|
+
unless File.file?(path)
|
|
577
|
+
Log.debug(:config, "appended_prompt_file_missing", path: path, source: source)
|
|
578
|
+
next
|
|
579
|
+
end
|
|
580
|
+
text = PromptFiles.section(path, "Appended from #{PromptFiles.display_path(path, top: top)}:")
|
|
581
|
+
next unless text
|
|
588
582
|
|
|
589
|
-
|
|
590
|
-
|
|
583
|
+
@section_files&.push(path)
|
|
584
|
+
sections << text
|
|
585
|
+
end
|
|
586
|
+
sections.empty? ? nil : sections.join("\n\n")
|
|
591
587
|
end
|
|
592
|
-
sections.empty? ? nil : sections.join("\n\n")
|
|
593
|
-
rescue StandardError
|
|
594
|
-
nil
|
|
595
588
|
end
|
|
596
589
|
|
|
597
590
|
# [absolute path, "config" | "cli"], in prompt order.
|
|
@@ -617,27 +610,27 @@ module Samagotchi
|
|
|
617
610
|
# The home directory is spelled out once so the model copies the right
|
|
618
611
|
# sequence, with the advice to write it as ~ or $HOME instead.
|
|
619
612
|
def project_location
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
613
|
+
Degrade.call(:prompt, "location_failed") do
|
|
614
|
+
cwd = Dir.pwd
|
|
615
|
+
top = MemoryPaths.work_tree_root(cwd)
|
|
616
|
+
lines = ["Current working directory:", cwd]
|
|
617
|
+
if top && top != cwd
|
|
618
|
+
lines << "Top of this checkout (where the project's commands run from; stay inside it):"
|
|
619
|
+
lines << top
|
|
620
|
+
elsif top
|
|
621
|
+
lines << "This checkout (the project's files; its commands run here):"
|
|
622
|
+
lines << top
|
|
623
|
+
end
|
|
624
|
+
if top && MemoryPaths.project_root(cwd) != top
|
|
625
|
+
lines << "This checkout is a linked git worktree: read, edit, run and commit only in it, never in the " \
|
|
626
|
+
"repository's other checkouts."
|
|
627
|
+
end
|
|
628
|
+
home = Dir.home
|
|
629
|
+
lines << "Home directory: #{home} (write it as ~ or $HOME in commands and paths)" unless home.to_s.empty?
|
|
630
|
+
lines << "Project memories folder (your notes about the project, not its files; never work or run commands there):"
|
|
631
|
+
lines << Paths.tilde(Tools::MemoryRead.memories_dir("project"))
|
|
632
|
+
lines.join("\n")
|
|
633
633
|
end
|
|
634
|
-
home = Dir.home
|
|
635
|
-
lines << "Home directory: #{home} (write it as ~ or $HOME in commands and paths)" unless home.to_s.empty?
|
|
636
|
-
lines << "Project memories folder (your notes about the project, not its files; never work or run commands there):"
|
|
637
|
-
lines << Paths.tilde(Tools::MemoryRead.memories_dir("project"))
|
|
638
|
-
lines.join("\n")
|
|
639
|
-
rescue StandardError
|
|
640
|
-
nil
|
|
641
634
|
end
|
|
642
635
|
|
|
643
636
|
# Only while the session has participants (people its owner invited to
|
|
@@ -662,12 +655,12 @@ module Samagotchi
|
|
|
662
655
|
# once a participant invited to act is in a working session, the review
|
|
663
656
|
# line for review participants; nothing with none (nil, false).
|
|
664
657
|
def participants_section
|
|
665
|
-
|
|
666
|
-
|
|
658
|
+
Degrade.call(:prompt, "participants_line_failed") do
|
|
659
|
+
mode = @participants_lookup.call
|
|
660
|
+
return nil unless mode
|
|
667
661
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
nil
|
|
662
|
+
mode == ParticipantMode::PAIR ? PAIR_PARTICIPANTS_LINE : PARTICIPANTS_LINE
|
|
663
|
+
end
|
|
671
664
|
end
|
|
672
665
|
|
|
673
666
|
# The model this session runs on. A model guesses its name from
|
|
@@ -675,19 +668,19 @@ module Samagotchi
|
|
|
675
668
|
# says to answer from here. It depends only on the effective model, and
|
|
676
669
|
# the prompt is rebuilt only on a switch (#reset!): no per-turn churn.
|
|
677
670
|
def current_model
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
"
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
671
|
+
Degrade.call(:prompt, "model_line_failed") do
|
|
672
|
+
model = @model_lookup.call
|
|
673
|
+
return nil unless model && !model.ref.to_s.empty?
|
|
674
|
+
|
|
675
|
+
details = [model.host, model.key && "model key #{model.key}", model.served && "the server says it serves #{model.served}"]
|
|
676
|
+
details = details.compact.reject(&:empty?)
|
|
677
|
+
where = details.empty? ? "" : " (#{details.join("; ")})"
|
|
678
|
+
"Model: this session runs on #{model.ref}#{where}.\n" \
|
|
679
|
+
"Asked which model you are, answer with this line, not from training: a fine-tuned model often knows only " \
|
|
680
|
+
"its base model's name, but this session runs what is named here; it changes only with /model. " \
|
|
681
|
+
"Guidance for this model only goes in a model note: a memory named model_notes_<name> whose first line is " \
|
|
682
|
+
"`models: <glob on this model's id or key>`."
|
|
683
|
+
end
|
|
691
684
|
end
|
|
692
685
|
|
|
693
686
|
# Fixed for the session's lifetime, so it doesn't churn the prompt cache.
|
|
@@ -701,7 +694,7 @@ module Samagotchi
|
|
|
701
694
|
line = "Current session id: #{id} (resume later with `chi --resume #{id}`)"
|
|
702
695
|
# The log path too: asked what went wrong, a model that has to look
|
|
703
696
|
# it up guesses ~/.local/state first (the self-awareness probes).
|
|
704
|
-
log =
|
|
697
|
+
log = Degrade.call(:prompt, "log_path_failed") { LogPath.resolve }
|
|
705
698
|
line = "#{line}\nMy debug log: #{log} (one record per line; this session's carry sid=#{id[0, Log::SID_LENGTH]})" if log
|
|
706
699
|
parent = session.parent_id.to_s
|
|
707
700
|
return line if parent.empty?
|
|
@@ -9,7 +9,7 @@ require_relative "tools/task_runtime"
|
|
|
9
9
|
module Samagotchi
|
|
10
10
|
# The background tasks a worker's session started that the model hasn't
|
|
11
11
|
# seen finish (docs/internals/background-tasks.md, Finish wake). Held by
|
|
12
|
-
# the Worker (
|
|
12
|
+
# the Worker (a --non-interactive one-shot has none) and fed by the Engine's raw
|
|
13
13
|
# :tool_call_completed events (#observe), the input Bridge#own_task_ids
|
|
14
14
|
# reads too; the idle loop takes the finished ones (#take_finished) for a
|
|
15
15
|
# wake turn or a plain note. In memory only: a crash loses them, and they
|