samagotchi 0.153.0 → 0.154.1
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 +42 -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/bundles.md +9 -12
- data/docs/cli.md +82 -96
- data/docs/configuration.md +27 -26
- data/docs/context.md +3 -3
- data/docs/desktop.md +1 -1
- data/docs/guardrails.md +8 -10
- data/docs/hooks.md +9 -9
- data/docs/internals/attached-context.md +2 -2
- data/docs/internals/background-tasks.md +3 -4
- data/docs/internals/block-renderers.md +4 -0
- data/docs/internals/bridge.md +21 -8
- data/docs/internals/context-telemetry.md +1 -1
- data/docs/internals/engine.md +33 -7
- data/docs/internals/objectives.md +6 -5
- data/docs/internals/participants.md +7 -7
- data/docs/internals/prompt-caching.md +4 -2
- data/docs/internals/remote-sessions.md +7 -7
- data/docs/internals/web-frontend.md +2 -2
- data/docs/internals/web-push.md +1 -1
- data/docs/memory.md +2 -2
- data/docs/plugins.md +23 -28
- data/docs/sessions.md +24 -24
- data/docs/sub-agent.md +2 -2
- data/docs/system-one.md +1 -1
- data/docs/testing.md +31 -6
- data/lib/samagotchi/answer_text.rb +33 -0
- data/lib/samagotchi/bridge/card_places.rb +56 -7
- data/lib/samagotchi/bridge/card_store.rb +24 -9
- data/lib/samagotchi/bridge.rb +52 -32
- data/lib/samagotchi/bridge_client.rb +19 -14
- 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/try/trace.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +2 -2
- data/lib/samagotchi/bundles/btw/manifest.yml +2 -2
- data/lib/samagotchi/bundles/btw/plugin.rb +1 -1
- data/lib/samagotchi/bundles/coordinator/manifest.yml +2 -2
- data/lib/samagotchi/bundles/coordinator/plugin.rb +3 -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/github-pr/manifest.yml +2 -2
- data/lib/samagotchi/bundles/github-pr/plugin.rb +3 -3
- data/lib/samagotchi/bundles/skills/manifest.yml +2 -2
- data/lib/samagotchi/bundles/skills/plugin.rb +3 -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/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 +3 -3
- 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 +62 -2646
- data/lib/samagotchi/engine_snapshots.rb +9 -8
- 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/hook_routes.rb +3 -3
- data/lib/samagotchi/idle_recap.rb +7 -6
- data/lib/samagotchi/idle_reminders.rb +3 -4
- data/lib/samagotchi/iteration_limit.rb +2 -2
- data/lib/samagotchi/kernel_loop.rb +13 -13
- data/lib/samagotchi/launch_mode.rb +13 -32
- data/lib/samagotchi/level_setting_command.rb +3 -2
- data/lib/samagotchi/llm/chat_loop.rb +3 -1
- data/lib/samagotchi/llm_context_command.rb +3 -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/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/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 +67 -49
- 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 +27 -7
- data/lib/samagotchi/sessions_command.rb +4 -4
- data/lib/samagotchi/share_command.rb +10 -10
- data/lib/samagotchi/steer.rb +13 -2
- data/lib/samagotchi/stream_event.rb +22 -0
- data/lib/samagotchi/system_prompt.rb +154 -145
- 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 +1 -11
- 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 +17 -22
- data/lib/samagotchi/terminal_ui/join_replay.rb +16 -12
- 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 +2 -12
- data/lib/samagotchi/terminal_ui/status_row.rb +6 -14
- data/lib/samagotchi/terminal_ui.rb +142 -1387
- data/lib/samagotchi/title_command.rb +2 -2
- data/lib/samagotchi/tool_runner.rb +15 -16
- data/lib/samagotchi/toolkit_command.rb +2 -1
- 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/send_note.rb +2 -1
- data/lib/samagotchi/turn_endings.rb +2 -2
- data/lib/samagotchi/turn_flow.rb +11 -7
- data/lib/samagotchi/update_hint.rb +2 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +42 -51
- data/lib/samagotchi/web/message_display.rb +7 -0
- 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/block_host.js +35 -8
- data/lib/samagotchi/web/public/block_renderers.css +9 -5
- data/lib/samagotchi/web/public/card_stamps.js +27 -0
- data/lib/samagotchi/web/public/cards_place.js +5 -3
- 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 +1 -1
- data/lib/samagotchi/web/public/format.js +4 -3
- data/lib/samagotchi/web/public/turn_html.js +8 -4
- data/lib/samagotchi/web/push/store.rb +1 -1
- data/lib/samagotchi/web/session_access_routes.rb +13 -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 +6 -2
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +103 -9
- data/lib/samagotchi/worker_link.rb +3 -3
- data/lib/samagotchi/working_set_command.rb +3 -2
- data/lib/samagotchi.rb +0 -1
- metadata +31 -7
- data/lib/samagotchi/pending_input_queue.rb +0 -55
- 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
|
@@ -22,7 +22,7 @@ module Samagotchi
|
|
|
22
22
|
# chi's own settings, keys, proxies or dirs (LLM calls, HostKeys, Config,
|
|
23
23
|
# Paths, hooks, plugins). A pair turn's tools don't get it either. A
|
|
24
24
|
# mise.toml edit reaches a session at its next worker (`chi sessions
|
|
25
|
-
# restart`).
|
|
25
|
+
# restart`). A --non-interactive one-shot runs no worker and keeps the shell's env.
|
|
26
26
|
# docs/sessions.md (Project env).
|
|
27
27
|
#
|
|
28
28
|
# And `chi` inside those tools stays the chi home's (ChiShim): the project's
|
|
@@ -75,7 +75,7 @@ module Samagotchi
|
|
|
75
75
|
Overlay = Data.define(:env, :applied, :reason, :ruby) do
|
|
76
76
|
def marker = applied ? "mise:#{ruby || "no ruby"}" : "none:#{reason}"
|
|
77
77
|
end
|
|
78
|
-
# No project env: specs,
|
|
78
|
+
# No project env: specs, a one-shot, a worker before #install!.
|
|
79
79
|
NONE = Overlay.new(env: {}.freeze, applied: false, reason: "none", ruby: nil)
|
|
80
80
|
|
|
81
81
|
class Failed < StandardError; end
|
|
@@ -17,7 +17,8 @@ module Samagotchi
|
|
|
17
17
|
# An Engine's question flow: the one open question (ask_user_question, a
|
|
18
18
|
# hook's ask_user, a guardrail approval), its answer and its cancel, shared
|
|
19
19
|
# by every UI. The turn thread blocks in #open_question; the UIs answer or
|
|
20
|
-
# cancel from theirs (a
|
|
20
|
+
# cancel from theirs (a --non-interactive run dismisses it inline through
|
|
21
|
+
# the sync handler).
|
|
21
22
|
#
|
|
22
23
|
# A standing question (#post: a worker's step-limit question, kind
|
|
23
24
|
# "continue") blocks nobody: it waits between turns, its answer goes to
|
|
@@ -52,7 +53,7 @@ module Samagotchi
|
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
# The line for the parent (ParentPolicy.message).
|
|
55
|
-
# @param typed [Boolean] typed at a ? prompt
|
|
56
|
+
# @param typed [Boolean] typed at a terminal's ? prompt
|
|
56
57
|
def detail(typed: false)
|
|
57
58
|
Guardrails::ParentPolicy.message(reason, pending: @pending, strategy: @strategy, typed: typed)
|
|
58
59
|
end
|
|
@@ -212,8 +213,8 @@ module Samagotchi
|
|
|
212
213
|
id = pending.id
|
|
213
214
|
|
|
214
215
|
# If a synchronous UI handler is registered (TUI), invoke it inline on the
|
|
215
|
-
# SAME thread that called request_question (
|
|
216
|
-
# turn thread — no second thread exists to answer). This avoids deadlock.
|
|
216
|
+
# SAME thread that called request_question (a --non-interactive run's
|
|
217
|
+
# TerminalUI thread is the turn thread — no second thread exists to answer). This avoids deadlock.
|
|
217
218
|
# This path is TUI-specific but the surrounding emit/clear is generic, so
|
|
218
219
|
# any future UI that registers a sync handler gets the same guarantee.
|
|
219
220
|
if @sync_handler
|
|
@@ -369,7 +370,7 @@ module Samagotchi
|
|
|
369
370
|
|
|
370
371
|
attr_writer :sync_handler
|
|
371
372
|
|
|
372
|
-
# @return [Boolean] a
|
|
373
|
+
# @return [Boolean] a sync handler answers inline (a --non-interactive run: no wait loop)
|
|
373
374
|
def sync_handler? = !@sync_handler.nil?
|
|
374
375
|
|
|
375
376
|
# Cancel the pending question (e.g. /cancel, a dismiss). Announces which
|
|
@@ -402,8 +403,8 @@ module Samagotchi
|
|
|
402
403
|
true
|
|
403
404
|
end
|
|
404
405
|
|
|
405
|
-
# A question through the question flow (
|
|
406
|
-
# web), single-select, kind "hook". A --non-interactive run has no one
|
|
406
|
+
# A question through the question flow (the one-shot's sync handler,
|
|
407
|
+
# attached TUI, web), single-select, kind "hook". A --non-interactive run has no one
|
|
407
408
|
# to ask: nil at once. Anything but an answer (a sync handler's text, no
|
|
408
409
|
# answer, cancelled) is nil too.
|
|
409
410
|
# @param asked [HookQuestion] its effects (label => effect for a parent
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "config"
|
|
4
|
+
require_relative "session"
|
|
4
5
|
|
|
5
6
|
module Samagotchi
|
|
6
7
|
# /recap on|off|default, the setting form of /recap (the bare /recap,
|
|
@@ -27,7 +28,7 @@ module Samagotchi
|
|
|
27
28
|
def run(args)
|
|
28
29
|
word = args.to_s.strip.downcase
|
|
29
30
|
return [USAGE, false, :error] unless CHOICES.key?(word)
|
|
30
|
-
return [
|
|
31
|
+
return [Session::NONE_YET, false, :error] unless @engine.session
|
|
31
32
|
|
|
32
33
|
value = CHOICES.fetch(word)
|
|
33
34
|
changed = @engine.recap_override != value
|
|
@@ -42,7 +42,7 @@ module Samagotchi
|
|
|
42
42
|
if session.key?("recap.enabled")
|
|
43
43
|
return nil unless session["recap.enabled"]
|
|
44
44
|
else
|
|
45
|
-
#
|
|
45
|
+
# A worker passes nothing: the config is read here alone
|
|
46
46
|
# (a scalar `recap: false` is only seen this way).
|
|
47
47
|
return nil if recap.nil? && ConfigFile.recap_config == false
|
|
48
48
|
return nil if Samagotchi::Config.get("recap.enabled") == false
|
|
@@ -12,7 +12,7 @@ module Samagotchi
|
|
|
12
12
|
# (title: the auto title the recap reply led with, RecapTitle; absent in
|
|
13
13
|
# a recap saved before it, or when no reply gave one).
|
|
14
14
|
# A separate file, so it never races the session-file save at the end of a
|
|
15
|
-
# turn. Follows the Engine's current session
|
|
15
|
+
# turn. Follows the Engine's current session.
|
|
16
16
|
class RecapStore
|
|
17
17
|
FILE = "recap.json"
|
|
18
18
|
# A session card's or the picker's recap line.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Samagotchi
|
|
4
|
+
# Secret shapes and host paths out of what a participant gets (the
|
|
5
|
+
# session's folder as relative paths, home as ~). Defence in depth: the
|
|
6
|
+
# allowlist (Web::ParticipantView) is the control; this catches a key the
|
|
7
|
+
# model quoted in a reply, or one in a prompt. Also how the Engine reads a
|
|
8
|
+
# Verify quote and ObjectivesCheck a text: as a participant's page shows it.
|
|
9
|
+
# Pure, stdlib only: core files use it without loading chi web.
|
|
10
|
+
module Redact
|
|
11
|
+
# Who is looking: +author+ the participant's ("p:<id>"; nil for the
|
|
12
|
+
# owner through the participant view: nothing is theirs), +cwd+ the
|
|
13
|
+
# session's folder (tool paths are shown relative to it, only inside
|
|
14
|
+
# it), +home+ the folder shown as ~, +secrets+ literal strings never
|
|
15
|
+
# shown (the owner's token).
|
|
16
|
+
Viewer = Data.define(:author, :cwd, :home, :secrets) do
|
|
17
|
+
def initialize(author: nil, cwd: nil, home: Dir.home, secrets: [])
|
|
18
|
+
super(author: author, cwd: cwd, home: home, secrets: Array(secrets).map(&:to_s).reject(&:empty?))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def own?(author) = !self.author.nil? && author.to_s == self.author
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
MARK = "[redacted]"
|
|
25
|
+
PRIVATE_KEY = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----.*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|\z)/m
|
|
26
|
+
PATTERNS = [
|
|
27
|
+
PRIVATE_KEY,
|
|
28
|
+
/\bsk-(?:ant-|proj-)?[A-Za-z0-9_-]{16,}/,
|
|
29
|
+
/\bgh[pousr]_[A-Za-z0-9]{20,}/,
|
|
30
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/,
|
|
31
|
+
/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
|
|
32
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/,
|
|
33
|
+
/\bchp_[A-Za-z0-9_-]{20,}/,
|
|
34
|
+
# A one-time link code (LinkCodes::CODE_RE): a bearer credential
|
|
35
|
+
# until its grant expires (an owner's, 10 minutes).
|
|
36
|
+
/\bchc_[A-Za-z0-9_-]{43}/,
|
|
37
|
+
/\bAIza[0-9A-Za-z_-]{30,}/,
|
|
38
|
+
/\b[rs]k_(?:live|test)_[A-Za-z0-9]{16,}/,
|
|
39
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/,
|
|
40
|
+
%r{https://hooks\.slack\.com/services/[A-Za-z0-9/_-]+}
|
|
41
|
+
].freeze
|
|
42
|
+
# The value after an Authorization (or a Bridge's secret,
|
|
43
|
+
# BridgeSecret::HEADER) header or a token=/key= pair.
|
|
44
|
+
VALUES = [
|
|
45
|
+
/(\b(?:Authorization|X-Chi-Bridge):\s*(?:Bearer\s+|Basic\s+|Token\s+)?)[^\s"'`]+/i,
|
|
46
|
+
/((?<![A-Za-z0-9])[A-Za-z0-9_-]*?(?:token|api[_-]?key|access[_-]?key|secret|password|passwd)["']?\s*[=:]\s*["']?)[^\s"'&`,;}]+/i,
|
|
47
|
+
# A URL's password: scheme://user:PASSWORD@host.
|
|
48
|
+
%r{(\b[a-z][a-z0-9+.-]*://[^/\s:@]+:)[^@\s/]+(?=@)}i
|
|
49
|
+
].freeze
|
|
50
|
+
# A text that ends where one of VALUES' values (or a private key's
|
|
51
|
+
# BEGIN line) would go on: a stream cut there would send the value
|
|
52
|
+
# on its own, unredacted (ParticipantStreamFilter never cuts there).
|
|
53
|
+
OPEN_VALUE = /(?:\b(?:Authorization|X-Chi-Bridge):\s*(?:(?:Bearer|Basic|Token)\s*)?|[A-Za-z0-9_-]*?(?:token|api[_-]?key|access[_-]?key|secret|password|passwd)["']?\s*(?:[=:]\s*["']?)?|-----BEGIN [A-Z0-9 ]*)\z/i
|
|
54
|
+
|
|
55
|
+
module_function
|
|
56
|
+
|
|
57
|
+
# +value+ with every string in it redacted (hash values, array items).
|
|
58
|
+
def deep(value, viewer)
|
|
59
|
+
case value
|
|
60
|
+
when String then text(value, viewer)
|
|
61
|
+
when Hash then value.transform_values { |item| deep(item, viewer) }
|
|
62
|
+
when Array then value.map { |item| deep(item, viewer) }
|
|
63
|
+
else value
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def text(value, viewer)
|
|
68
|
+
out = value.dup
|
|
69
|
+
viewer.secrets.each { |secret| out = out.gsub(secret, MARK) }
|
|
70
|
+
PATTERNS.each { |pattern| out = out.gsub(pattern, MARK) }
|
|
71
|
+
VALUES.each { |pattern| out = out.gsub(pattern) { "#{Regexp.last_match(1)}#{MARK}" } }
|
|
72
|
+
out = within(out, viewer.cwd.to_s)
|
|
73
|
+
home = viewer.home.to_s
|
|
74
|
+
out = out.gsub(%r{#{Regexp.escape(home)}(?=/|\b|\z)}, "~") if home.length > 1
|
|
75
|
+
out
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# +text+ with +dir+ (the session's folder) shown relative: "dir/x"
|
|
79
|
+
# as "x", "dir" alone as ".".
|
|
80
|
+
def within(text, dir)
|
|
81
|
+
return text if dir.length < 2
|
|
82
|
+
|
|
83
|
+
text.gsub(%r{#{Regexp.escape(dir)}(/|(?=\b|\z))}) { Regexp.last_match(1) == "/" ? "" : "." }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -8,8 +8,8 @@ module Samagotchi
|
|
|
8
8
|
#
|
|
9
9
|
# The ReminderStore is the truth (a reminder is due once its interval
|
|
10
10
|
# passed). This adds the names the idle tick found due and handed to the
|
|
11
|
-
# UI's callback (#note_pending): the worker
|
|
12
|
-
#
|
|
11
|
+
# UI's callback (#note_pending): the worker runs a reminder turn while
|
|
12
|
+
# they are pending. A turn injects whatever is due then
|
|
13
13
|
# (#inject!), which marks it fired and clears the pending names.
|
|
14
14
|
class ReminderQueue
|
|
15
15
|
# The reminders message's first line at +time+ (a Time in the zone to
|
|
@@ -15,7 +15,7 @@ module Samagotchi
|
|
|
15
15
|
# attached TUI on a session of another machine, through its chi web
|
|
16
16
|
# (bin/chi branches here after RunOptions.parse, before its own checks,
|
|
17
17
|
# which are this machine's). The run flags come parsed; the ones that
|
|
18
|
-
# run this machine's
|
|
18
|
+
# run this machine's Engine or read its files were refused already
|
|
19
19
|
# (SessionAccess::Contract::RUN). The setup flags go as typed: that
|
|
20
20
|
# machine checks them against its own config and bundles.
|
|
21
21
|
class RemoteAttachCommand
|
|
@@ -160,7 +160,7 @@ module Samagotchi
|
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
# Only a question a live worker holds waits for an answer (the lists'
|
|
163
|
-
# rule): one a dead worker left waits for no one, and a chi
|
|
163
|
+
# rule): one a dead worker left waits for no one, and a one-turn chi run's is
|
|
164
164
|
# answered there.
|
|
165
165
|
def question(session)
|
|
166
166
|
pending = session.pending_question
|
|
@@ -171,7 +171,7 @@ module Samagotchi
|
|
|
171
171
|
Result.new(status: :waiting_for_answer, question: pending, session: session)
|
|
172
172
|
end
|
|
173
173
|
|
|
174
|
-
# A worker holds the session's lock (a chi
|
|
174
|
+
# A worker holds the session's lock (a one-turn chi run's question is its own).
|
|
175
175
|
def worker_owned?
|
|
176
176
|
WorkerLink.resolve(Session.session_dir(@id, state_dir: @state_dir), unlink: false).worker_owned?
|
|
177
177
|
end
|
|
@@ -143,7 +143,7 @@ module Samagotchi
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
# What inviting a participant to +session+ now means: the grant's mode
|
|
146
|
-
# (
|
|
146
|
+
# (Grants::PAIR_MODE only when the owner is shown PAIR_WARNING: a
|
|
147
147
|
# working session whose live worker runs pair turns; nil otherwise, a
|
|
148
148
|
# review participant), the mode their turns get (:pair or :review) and
|
|
149
149
|
# the warning shown for it (nil for a review session). One read of the
|
|
@@ -14,7 +14,7 @@ module Samagotchi
|
|
|
14
14
|
module RunOptions
|
|
15
15
|
# The flags an underscored spelling is refused for (--recap_base_url),
|
|
16
16
|
# with the config registry's.
|
|
17
|
-
KNOWN = %w[--prompt --resume --attach --
|
|
17
|
+
KNOWN = %w[--prompt --resume --attach --model --thinking --toolkit --tool --no-tool --set --parent-answers --memory --mute --append-system-prompt --dir --llm-context --llm-context-apply --llm-context-budget --no-interrupt --non-interactive --verbose --no-default-input --port --open --new-token --tunnel --start-dir].freeze
|
|
18
18
|
|
|
19
19
|
# An OptionParser that also records each switch it took, as the
|
|
20
20
|
# normalized [flag] or [flag, value] (a --[no-]x as --x or --no-x; an
|
|
@@ -221,12 +221,11 @@ module Samagotchi
|
|
|
221
221
|
(each subcommand takes --help)
|
|
222
222
|
|
|
223
223
|
BANNER
|
|
224
|
-
opts.on("-p", "--prompt PROMPT", "Run PROMPT as the first turn, then stay in the session (attached;
|
|
224
|
+
opts.on("-p", "--prompt PROMPT", "Run PROMPT as the first turn, then stay in the session (attached; one turn and out with --non-interactive)") { |p| options[:prompt] = p }
|
|
225
225
|
opts.on("--resume SESSION_ID", "Resume a previous session by its ID (or a unique prefix of it)") { |s| options[:resume] = s }
|
|
226
226
|
opts.on("--attach SESSION_ID", "Attach to a session's worker, waking one if it has exited; ID or a unique prefix") { |s| options[:attach] = s }
|
|
227
227
|
opts.on("--dir DIR", "With --remote: a new session's folder on that machine, its absolute path (required there; " \
|
|
228
228
|
"here cd instead)") { |d| options[:dir] = d }
|
|
229
|
-
opts.on("--[no-]shared", "Run the session in a background worker and attach to it, so `chi web` can share it (the default, config session.shared; with --resume: that session); --no-shared runs a plain in-process REPL") { |v| options[:shared] = v }
|
|
230
229
|
opts.on("--model NAME", "Model for this run, not saved (overrides --resume's; config default.model) [env SAMAGOTCHI_DEFAULT_MODEL]") { |m| options[:model] = m; cli_overrides["default.model"] = m }
|
|
231
230
|
opts.on("--profile NAME", Samagotchi::ModelProfile::NAMES,
|
|
232
231
|
"Prompt profile for every model in this run (#{Samagotchi::ModelProfile::NAMES.join("|")}) [alias for --model-profile]") { |p| cli_overrides["model.profile"] = p }
|
|
@@ -260,9 +259,10 @@ module Samagotchi
|
|
|
260
259
|
opts.on("--llm-context-apply RULE", "This session's own apply rule for it: payoff|next_request|turn_end") { |v| (options[:llm_context] ||= {})[:apply] = v }
|
|
261
260
|
opts.on("--llm-context-budget TOKENS", "This session's own context budget: tokens (64000, 64k) or off") { |v| (options[:llm_context] ||= {})[:budget_tokens] = v }
|
|
262
261
|
opts.on("--no-interrupt", "Raise the tool call limit to 1000 iterations (useful for long tasks)") { options[:no_interrupt] = true; cli_overrides["no_interrupt"] = true }
|
|
263
|
-
opts.on("--non-interactive", "Run
|
|
264
|
-
opts.on("-v", "--verbose", "
|
|
265
|
-
|
|
262
|
+
opts.on("--non-interactive", "Run the -p prompt in this process, one turn, and exit (implies --no-interrupt)") { options[:non_interactive] = true }
|
|
263
|
+
opts.on("-v", "--verbose", "Show the session's debug records (raw LLM responses, tool calls/results) as they come; " \
|
|
264
|
+
"a worker this chi starts logs at debug (with --non-interactive: on stderr)") { options[:verbose] = true }
|
|
265
|
+
opts.on("--no-default-input", "Skip the default input typed into a new session's first read (SAMAGOTCHI_DEFAULT_INPUT)") { options[:no_default_input] = true; cli_overrides["no_default_input"] = true }
|
|
266
266
|
opts.on("--port PORT", "Port for `chi web` (default: 4567, env SAMAGOTCHI_WEB_PORT)") { |p| options[:web_port] = p.to_i; cli_overrides["web.port"] = p.to_s }
|
|
267
267
|
opts.on("--open", "Open browser for `chi web`") { options[:web_open] = true }
|
|
268
268
|
opts.on("--new-token", "`chi web`: replace the LAN access token (web.host: lan); links and QR codes made with the old one stop working") { options[:new_token] = true }
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "version"
|
|
4
4
|
require_relative "config"
|
|
5
5
|
require_relative "context_window"
|
|
6
|
+
require_relative "degrade"
|
|
6
7
|
require_relative "session"
|
|
7
8
|
require_relative "log_path"
|
|
8
9
|
require_relative "model_profile"
|
|
@@ -177,19 +178,19 @@ module Samagotchi
|
|
|
177
178
|
|
|
178
179
|
# [tokens, "models: KEY" | "hosts.NAME"], or nil when neither is set.
|
|
179
180
|
def model_window(env, model)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
181
|
+
Degrade.call(:config, "model_window_unreadable") do
|
|
182
|
+
return nil unless model
|
|
183
|
+
|
|
184
|
+
registry = HostRegistry.new(env: env)
|
|
185
|
+
target = registry.resolve(model)
|
|
186
|
+
names = registry.lookup_names(model, target: target)
|
|
187
|
+
models = ConfigFile.model_settings(env: env)
|
|
188
|
+
window = ContextWindow.setting(target, names: names, models: models)
|
|
189
|
+
return nil unless window
|
|
190
|
+
return [window.tokens, "hosts.#{target.entry.name}"] if window.source == :host_setting
|
|
191
|
+
|
|
192
|
+
[window.tokens, "models: #{ConfigFile.model_setting(names, :window_tokens, models: models).key}"]
|
|
193
|
+
end
|
|
193
194
|
end
|
|
194
195
|
|
|
195
196
|
SESSION_MODEL_ENV = "SAMAGOTCHI_SESSION_MODEL"
|
|
@@ -220,11 +221,11 @@ module Samagotchi
|
|
|
220
221
|
end
|
|
221
222
|
|
|
222
223
|
def same_model?(one, other, env)
|
|
223
|
-
|
|
224
|
+
Degrade.call(:config, "model_ref_unresolved", fallback: false) do
|
|
225
|
+
return true if one == other
|
|
224
226
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
false
|
|
227
|
+
ConfigFile.model_ref(one, env: env).ref == ConfigFile.model_ref(other, env: env).ref
|
|
228
|
+
end
|
|
228
229
|
end
|
|
229
230
|
|
|
230
231
|
# The memory overlay key (ModelOverlay.key_for the bare id, as the
|
|
@@ -257,41 +258,41 @@ module Samagotchi
|
|
|
257
258
|
# session chi self runs in has one (an alias), else of the name chi self
|
|
258
259
|
# reports. nil when that is the key itself. Read-only, never raising.
|
|
259
260
|
def fallback_model_key(model, key, env)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
Degrade.call(:model, "fallback_key_unresolved") do
|
|
262
|
+
typed = session_typed_model(env) || model
|
|
263
|
+
typed_key = ModelOverlay.key_for(HostRegistry.new(env: env).parse_qualified_model(typed).last)
|
|
264
|
+
typed_key == key ? nil : typed_key
|
|
265
|
+
end
|
|
265
266
|
end
|
|
266
267
|
|
|
267
268
|
# The model name the session chi self runs in was typed as (an alias),
|
|
268
269
|
# its model_typed, else nil.
|
|
269
270
|
def session_typed_model(env)
|
|
270
|
-
|
|
271
|
-
|
|
271
|
+
Degrade.call(:config, "session_typed_model_unreadable") do
|
|
272
|
+
id = env[PARENT_SESSION_ENV].to_s
|
|
273
|
+
return nil if id.empty? || !Session.valid_id?(id)
|
|
272
274
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
+
state_dir = Session.default_state_dir(env: env)
|
|
276
|
+
return nil unless Session.exist?(id, state_dir: state_dir)
|
|
275
277
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
nil
|
|
278
|
+
session = Session.load(id, state_dir: state_dir)
|
|
279
|
+
typed = session.model_typed.to_s.strip
|
|
280
|
+
typed.empty? ? nil : typed
|
|
281
|
+
end
|
|
281
282
|
end
|
|
282
283
|
|
|
283
284
|
# The --mute list of the session chi self runs in (its execute exports
|
|
284
285
|
# SAMAGOTCHI_PARENT_SESSION); [] outside one.
|
|
285
286
|
def session_muted(env)
|
|
286
|
-
|
|
287
|
-
|
|
287
|
+
Degrade.call(:config, "session_mutes_unreadable", fallback: []) do
|
|
288
|
+
id = env[PARENT_SESSION_ENV].to_s
|
|
289
|
+
return [] if id.empty? || !Session.valid_id?(id)
|
|
288
290
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
state_dir = Session.default_state_dir(env: env)
|
|
292
|
+
return [] unless Session.exist?(id, state_dir: state_dir)
|
|
291
293
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
[]
|
|
294
|
+
MutedMemories.normalize_list(Session.load(id, state_dir: state_dir).muted_memory_names)
|
|
295
|
+
end
|
|
295
296
|
end
|
|
296
297
|
|
|
297
298
|
def model_name
|
|
@@ -489,7 +490,9 @@ module Samagotchi
|
|
|
489
490
|
|
|
490
491
|
# "samagotchi-system 0.1.5 (shipped 0.1.5), other 1.0.0"
|
|
491
492
|
def bundles_summary
|
|
492
|
-
shipped =
|
|
493
|
+
shipped = Degrade.call(:memory, "shipped_bundle_unreadable") do
|
|
494
|
+
MemoryBundle::Manifest.read(dir: MemoryBundle::SystemBundle::GEM_BUNDLE_DIR).version
|
|
495
|
+
end
|
|
493
496
|
installed = MemoryBundle::Provenance.each_installed.map do |name, bundle|
|
|
494
497
|
version = bundle.version || "?"
|
|
495
498
|
label = "#{name} #{version}"
|
|
@@ -23,7 +23,7 @@ module Samagotchi
|
|
|
23
23
|
# parent_answers
|
|
24
24
|
Busy = Data.define(:error, :session_id, :question, :strategy) do
|
|
25
25
|
# @param session [Session] the session as just read
|
|
26
|
-
# @param owner [OwnerLock::Owner, nil] the worker or
|
|
26
|
+
# @param owner [OwnerLock::Owner, nil] the worker or one-shot that owns it
|
|
27
27
|
# @param queue [Boolean] chi send --queue
|
|
28
28
|
# @return [Busy, nil] nil when the message may go in
|
|
29
29
|
def self.for(session, owner:, queue:)
|
|
@@ -799,8 +799,11 @@ module Samagotchi
|
|
|
799
799
|
end
|
|
800
800
|
@info.puts("#{short} sent#{with_images}#{note}")
|
|
801
801
|
true
|
|
802
|
+
rescue SessionManager::ScratchSession => e
|
|
803
|
+
@info.puts("#{short} refused: #{e.message}")
|
|
804
|
+
false
|
|
802
805
|
rescue SessionManager::OwnedByTUI
|
|
803
|
-
@info.puts("#{short} refused: it is
|
|
806
|
+
@info.puts("#{short} refused: it is #{OwnerLock::TUI_BUSY}; send once it ends")
|
|
804
807
|
false
|
|
805
808
|
rescue StandardError => e
|
|
806
809
|
@info.puts("#{short} failed: #{e.message}")
|
data/lib/samagotchi/session.rb
CHANGED
|
@@ -38,6 +38,11 @@ module Samagotchi
|
|
|
38
38
|
STATUS_ERROR = "error"
|
|
39
39
|
STATUS_STOPPED = "stopped"
|
|
40
40
|
TOOLKIT_ORIGINS = %w[chosen default inherited suggested].freeze
|
|
41
|
+
# Why anything but the terminal that started a `chi scratch` session is
|
|
42
|
+
# refused it (chi --attach/--resume, chi send, chi web's wake, turn and
|
|
43
|
+
# command routes): one wording for a live one and a leftover one.
|
|
44
|
+
SCRATCH_REFUSED = "that's a chi scratch session: only the terminal that started it uses it " \
|
|
45
|
+
"(a leftover one is deleted at the next sweep: chi sessions clean)"
|
|
41
46
|
|
|
42
47
|
# A stopped session's marker, <session dir>/stopped: a stop (chi
|
|
43
48
|
# sessions stop, from another process) creates it instead of rewriting
|
|
@@ -54,6 +59,9 @@ module Samagotchi
|
|
|
54
59
|
# The memory a delegate child starts with (Tools::Delegate::CHILD_MEMORIES).
|
|
55
60
|
DELEGATE_MEMORY = "system/delegated"
|
|
56
61
|
|
|
62
|
+
# What a command answers when there is no session to act on yet.
|
|
63
|
+
NONE_YET = "no session yet"
|
|
64
|
+
|
|
57
65
|
SORT_KEYS = %w[created_at updated_at].freeze
|
|
58
66
|
SORT_ORDERS = %w[asc desc].freeze
|
|
59
67
|
|
|
@@ -116,8 +124,9 @@ module Samagotchi
|
|
|
116
124
|
# nil. Not a parent: archiving one link leaves the next alone. The chain
|
|
117
125
|
# itself is derived (SessionChain), never stored.
|
|
118
126
|
attr_accessor :continues
|
|
119
|
-
# A `chi scratch` session: deleted when its
|
|
120
|
-
#
|
|
127
|
+
# A `chi scratch` session: deleted when its terminal leaves (its worker
|
|
128
|
+
# deletes it too once its last client is gone), and by the next sweep
|
|
129
|
+
# (or `chi sessions clean`) when both died first.
|
|
121
130
|
attr_accessor :scratch
|
|
122
131
|
# The question this session waits on (PendingQuestion), saved as
|
|
123
132
|
# session.json's "pending_question"; nil with none. A Hash (a saved
|
|
@@ -149,14 +149,14 @@ module Samagotchi
|
|
|
149
149
|
|
|
150
150
|
# bin/chi's run flags under a remote (chi --remote NAME --attach ID,
|
|
151
151
|
# RemoteAttachCommand): what a remote attach takes, and what runs
|
|
152
|
-
# this machine's
|
|
152
|
+
# this machine's Engine or reads this machine's files (P4-D2..D4). A
|
|
153
153
|
# config registry flag (--recap-base-url …) is this machine's
|
|
154
154
|
# config: LOCAL_ONLY without being listed.
|
|
155
155
|
RUN = {
|
|
156
|
-
SUPPORTED => %w[--attach --resume -p --prompt --
|
|
156
|
+
SUPPORTED => %w[--attach --resume -p --prompt --model --thinking --llm-context --llm-context-apply
|
|
157
157
|
--llm-context-budget --toolkit --tool --no-tool --set --parent-answers --no-interrupt
|
|
158
158
|
--no-default-input --dir --version -h --help],
|
|
159
|
-
LOCAL_ONLY => %w[--
|
|
159
|
+
LOCAL_ONLY => %w[--non-interactive -v --verbose --append-system-prompt --memory --mute --profile
|
|
160
160
|
--port --open --new-token --tunnel --start-dir --scope --web-markdown --no-web-markdown]
|
|
161
161
|
}.freeze
|
|
162
162
|
# A short run flag's name in a refusal.
|
|
@@ -23,7 +23,7 @@ require_relative "../launch_mode"
|
|
|
23
23
|
require_relative "../participants"
|
|
24
24
|
require_relative "../config"
|
|
25
25
|
require_relative "../live_versions"
|
|
26
|
-
require_relative "../
|
|
26
|
+
require_relative "../grants"
|
|
27
27
|
require_relative "../web/info_probe"
|
|
28
28
|
require_relative "../web/public_url"
|
|
29
29
|
require_relative "../web/link_codes"
|
|
@@ -76,7 +76,7 @@ module Samagotchi
|
|
|
76
76
|
# @raise [ArgumentError] no such session
|
|
77
77
|
def show(id) = Session.load(id, **dir)
|
|
78
78
|
|
|
79
|
-
# @return [OwnerLock::Owner, nil] the worker or chi
|
|
79
|
+
# @return [OwnerLock::Owner, nil] the worker or one-turn chi run that owns it
|
|
80
80
|
def owner(id) = SessionManager.session_owner(id, **dir)
|
|
81
81
|
|
|
82
82
|
# SessionManager.list_sessions: Session objects, for chi sessions list.
|
|
@@ -94,11 +94,11 @@ module Samagotchi
|
|
|
94
94
|
# a margin), so the resume finds the session free.
|
|
95
95
|
# @return [Woken] its sidecar nil when the Bridge didn't come in time
|
|
96
96
|
# @raise [ArgumentError] no such session
|
|
97
|
-
# @raise [Refused] code scratch: a
|
|
98
|
-
# @raise [SessionManager::OwnedByTUI] a chi
|
|
97
|
+
# @raise [Refused] code scratch: a chi scratch session (its terminal's alone)
|
|
98
|
+
# @raise [SessionManager::OwnedByTUI] a one-turn chi run has it
|
|
99
99
|
def wake(id, wait:)
|
|
100
100
|
session = show(id)
|
|
101
|
-
raise Refused.new(
|
|
101
|
+
raise Refused.new(Session::SCRATCH_REFUSED, code: "scratch") if session.scratch
|
|
102
102
|
|
|
103
103
|
link = WorkerLink.resolve(session_dir(id), unlink: true)
|
|
104
104
|
case link
|
|
@@ -177,7 +177,7 @@ module Samagotchi
|
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
# @return [SessionManager::DeliverResult]
|
|
180
|
-
# @raise [SessionManager::OwnedByTUI] a chi
|
|
180
|
+
# @raise [SessionManager::OwnedByTUI] a one-turn chi run owns it
|
|
181
181
|
def deliver_turn(id, prompt:, client_id:, images:, delivery: nil)
|
|
182
182
|
SessionManager.deliver_turn(id, prompt: prompt, client_id: client_id, images: images, delivery: delivery, **dir)
|
|
183
183
|
end
|
|
@@ -422,7 +422,7 @@ module Samagotchi
|
|
|
422
422
|
Invitation.new(participant: ParticipantView.from_h(issued.grant.public_h(grants.now)), token: issued.token,
|
|
423
423
|
link: link, lan: !public_url.nil? || !web.ip.nil?,
|
|
424
424
|
warning: terms.warning)
|
|
425
|
-
rescue
|
|
425
|
+
rescue Grants::Invalid => e
|
|
426
426
|
raise BadRequest.new(e.message, code: "invalid_participant")
|
|
427
427
|
end
|
|
428
428
|
|
|
@@ -446,13 +446,13 @@ module Samagotchi
|
|
|
446
446
|
raise BadRequest.new(Participants.no_participant(participant, id), code: "invalid_participant") unless grant
|
|
447
447
|
|
|
448
448
|
ParticipantView.from_h(grant.public_h(grants.now))
|
|
449
|
-
rescue
|
|
449
|
+
rescue Grants::Invalid => e
|
|
450
450
|
raise BadRequest.new(e.message, code: "invalid_participant")
|
|
451
451
|
end
|
|
452
452
|
|
|
453
453
|
private
|
|
454
454
|
|
|
455
|
-
def grants = @grants ||=
|
|
455
|
+
def grants = @grants ||= Grants.new(Grants.path(state_dir: state_dir!))
|
|
456
456
|
|
|
457
457
|
LOCAL_PROBE_TIMEOUT = 1.0
|
|
458
458
|
|
|
@@ -12,7 +12,7 @@ require_relative "../bridge_client"
|
|
|
12
12
|
require_relative "../session_continue"
|
|
13
13
|
require_relative "../guardrails/parent_policy"
|
|
14
14
|
require_relative "../model_profile"
|
|
15
|
-
require_relative "../
|
|
15
|
+
require_relative "../grants"
|
|
16
16
|
require_relative "http_transport"
|
|
17
17
|
require_relative "../chi_invocation"
|
|
18
18
|
|
|
@@ -59,7 +59,7 @@ module Samagotchi
|
|
|
59
59
|
def remote? = true
|
|
60
60
|
def name = @entry.name
|
|
61
61
|
|
|
62
|
-
#
|
|
62
|
+
# Grants::PREFIX, without loading chi web.
|
|
63
63
|
PARTICIPANT_PREFIX = "chp_"
|
|
64
64
|
|
|
65
65
|
# Whether the token is a participant's (chi share's link), not the
|
|
@@ -229,7 +229,12 @@ module Samagotchi
|
|
|
229
229
|
SessionManager::DeliverResult.new(status: :accepted, via: via, ack: ack.except("via"))
|
|
230
230
|
when 504 then SessionManager::DeliverResult.new(status: :timeout, ack: { "detail" => ack["detail"] })
|
|
231
231
|
when 409
|
|
232
|
-
|
|
232
|
+
if ack["error"] == "owned_by_tui"
|
|
233
|
+
# A chi scratch session (scratch: true; an older chi web sends none).
|
|
234
|
+
raise SessionManager::ScratchSession, ack["session_id"] || id if ack["scratch"] == true
|
|
235
|
+
|
|
236
|
+
raise SessionManager::OwnedByTUI, ack["session_id"] || id
|
|
237
|
+
end
|
|
233
238
|
|
|
234
239
|
SessionManager::DeliverResult.new(status: :refused, code: 409, ack: ack)
|
|
235
240
|
when 404 then raise_refusal(reply)
|
|
@@ -316,7 +321,9 @@ module Samagotchi
|
|
|
316
321
|
|
|
317
322
|
NoteDelivery::Result.new(id: id, status: json["status"].to_sym, queued: json["queued"].to_i)
|
|
318
323
|
when 409
|
|
319
|
-
|
|
324
|
+
if json["error"] == "owned_by_tui"
|
|
325
|
+
return NoteDelivery::Result.new(id: id, status: :refused, queued: 0, scratch: json["scratch"] == true)
|
|
326
|
+
end
|
|
320
327
|
|
|
321
328
|
raise_refusal(reply)
|
|
322
329
|
when 400
|
|
@@ -584,7 +591,7 @@ module Samagotchi
|
|
|
584
591
|
# when none runs, waited for up to +wait+ seconds there (a leaving
|
|
585
592
|
# worker first: +read_timeout+ covers that wait too).
|
|
586
593
|
# @return [WakeReply]
|
|
587
|
-
# @raise [SessionManager::OwnedByTUI] a chi
|
|
594
|
+
# @raise [SessionManager::OwnedByTUI] a one-turn chi run has it there
|
|
588
595
|
# @raise [Refused] a leftover scratch session (code scratch), or its
|
|
589
596
|
# Bridge didn't come in time (code worker_timeout): chi web's words,
|
|
590
597
|
# LaunchMode's constants
|
|
@@ -709,7 +716,10 @@ module Samagotchi
|
|
|
709
716
|
case error
|
|
710
717
|
when "not_found" then raise ArgumentError, message.to_s
|
|
711
718
|
when "ambiguous_id" then raise Session::AmbiguousId.new(message, ids: Array(json["ids"]))
|
|
712
|
-
when "owned_by_tui"
|
|
719
|
+
when "owned_by_tui"
|
|
720
|
+
raise SessionManager::ScratchSession, sid if json["scratch"] == true
|
|
721
|
+
|
|
722
|
+
raise SessionManager::OwnedByTUI, sid
|
|
713
723
|
when "worker_running", "still_stopping" then raise SessionManager::DeleteRefused.new(sid, reason)
|
|
714
724
|
when "busy", "scratch"
|
|
715
725
|
raise SessionManager::ArchiveRefused.new(sid, reason, busy_id: json["busy_id"]) if reason
|
|
@@ -728,7 +738,7 @@ module Samagotchi
|
|
|
728
738
|
# A toolkit whose config kit's groups: is invalid there: the same.
|
|
729
739
|
when "toolkit_refused" then raise Toolkit::Refused, message.to_s
|
|
730
740
|
# The grants store doesn't read there: chi share says what it says here.
|
|
731
|
-
when "grants_unwritable" then raise
|
|
741
|
+
when "grants_unwritable" then raise Grants::Unwritable, message.to_s
|
|
732
742
|
when /\Ainvalid_/, "missing_fields", "bad_images"
|
|
733
743
|
# invalid_participant: chi share's name, kind or ttl, an archived session
|
|
734
744
|
raise BadRequest.new(message.to_s, code: error)
|