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
|
@@ -30,7 +30,7 @@ module Samagotchi
|
|
|
30
30
|
|
|
31
31
|
def run(prompt:, options:, timeout:)
|
|
32
32
|
command = [RbConfig.ruby, "-I", Sandbox::LIB_DIR, Sandbox::CHI,
|
|
33
|
-
"--
|
|
33
|
+
"--non-interactive", "-p", prompt, *child_options(options)]
|
|
34
34
|
trap_state, old_traps = trap_forwarding
|
|
35
35
|
pid = nil
|
|
36
36
|
streams = []
|
|
@@ -124,7 +124,7 @@ module Samagotchi
|
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
# The listing's placement fields: nothing to place in a one-turn run.
|
|
127
|
-
LISTING_FIELDS = %w[turns_since current earlier after_failed label].freeze
|
|
127
|
+
LISTING_FIELDS = %w[turns_since opening_id current earlier after_failed label].freeze
|
|
128
128
|
|
|
129
129
|
# The saved cards as the web reads them (CardStore.saved), with
|
|
130
130
|
# string keys and values, as the JSON trace prints them.
|
|
@@ -367,8 +367,8 @@ module Samagotchi
|
|
|
367
367
|
prompt = build_conflict_prompt(bundle_name, installer.conflicts, expanded_source)
|
|
368
368
|
before = installer.conflicts.to_h { |key, info| [key, Samagotchi::MemoryBundle::Merger.digest(info[:current])] }
|
|
369
369
|
@stdout.puts "Launching an interactive session to merge them… (/exit when done)"
|
|
370
|
-
require "samagotchi/terminal_ui"
|
|
371
|
-
Samagotchi::TerminalUI.
|
|
370
|
+
require "samagotchi/terminal_ui/attach_launcher"
|
|
371
|
+
Samagotchi::TerminalUI::AttachLauncher.run(prompt: prompt)
|
|
372
372
|
settle_conflicts(installer.conflicts, before, bundle_name)
|
|
373
373
|
ensure
|
|
374
374
|
# An owned source (git/zip/tar) was kept for the incoming files above;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: btw
|
|
3
|
-
version: 0.1.
|
|
3
|
+
version: 0.1.5
|
|
4
4
|
scope: system
|
|
5
5
|
description: "/btw <question>: a side answer about the conversation, as a card in every UI, even mid-turn; Keep as session forks it into a child session"
|
|
6
6
|
trust_level: reviewed
|
|
7
7
|
plugin:
|
|
8
8
|
file: plugin.rb
|
|
9
|
-
sha256: sha256:
|
|
9
|
+
sha256: sha256:d4c0d6309308270a1430d64f9713e46c15d71ace1b068fefc22916aa8cc2d043
|
|
10
10
|
requires_chi: ">= 0.143.0"
|
|
11
11
|
# config.yml bundles: btw: (another key warns; an older chi ignores this)
|
|
12
12
|
settings: [max_tokens, timeout]
|
|
@@ -15,7 +15,7 @@ class Plugin
|
|
|
15
15
|
SYSTEM = "Answer the user's question about the conversation below, briefly. The user asks it on the side: " \
|
|
16
16
|
"don't continue the conversation's task, and don't say what you would do next."
|
|
17
17
|
# How many answers /btw keep can still fork: the last ones, for the
|
|
18
|
-
# worker's
|
|
18
|
+
# worker's life.
|
|
19
19
|
KEPT = 10
|
|
20
20
|
TITLE_CHARS = 72
|
|
21
21
|
EARLIER = "(About the conversation before the running turn.)"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: coordinator
|
|
3
|
-
version: 0.8.
|
|
3
|
+
version: 0.8.1
|
|
4
4
|
scope: system
|
|
5
5
|
description: "Coordinate parallel work: the coordinator skill (split the work, one worktree and child session per task, check each report, merges under a guardrail approval (the git-merge rule) or a question, a handoff memory to resume from), /coordinate <goal>, /coordinate resume, /coordinate end, /coordinate review (experimental: a sprint review session to share) and /children; the coordinator toolkit (lean + delegate, delegate_result, list_sessions, objectives) and the coding toolkit for build children (child + be concise)"
|
|
6
6
|
trust_level: reviewed
|
|
@@ -10,7 +10,7 @@ files:
|
|
|
10
10
|
description: "Coordinate parallel work: split it, delegate each task wait:false in its own worktree (cwd:), check each report, merge under the git-merge approval or ask the user; resume from a handoff_* memory"
|
|
11
11
|
plugin:
|
|
12
12
|
file: plugin.rb
|
|
13
|
-
sha256: sha256:
|
|
13
|
+
sha256: sha256:540dd6369342ea4dcbc6ba5ee17fb20d980b880df614a475668c70115e7ec6b8
|
|
14
14
|
# the coordinator toolkit needs chi.toolkit and ctx.use_toolkit (0.66.0); the
|
|
15
15
|
# coding toolkit (chi.toolkit prompt:) and the skill's delegate toolkit: need
|
|
16
16
|
# the next chi.
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
# (ctx.sessions.stop, by session id) and shows the card again.
|
|
17
17
|
#
|
|
18
18
|
# /coordinate <goal> asks the model, in this session, to follow the skill
|
|
19
|
-
# for the goal. A
|
|
20
|
-
# is shown for the user
|
|
19
|
+
# for the goal. A session a one-turn run holds (-p --non-interactive, or an
|
|
20
|
+
# older chi's terminal) takes no messages: the request is shown for the user
|
|
21
|
+
# to send.
|
|
21
22
|
#
|
|
22
23
|
# /coordinate resume picks up a coordinator handoff: the open handoff_*
|
|
23
24
|
# memories of this project (an index line whose description doesn't start
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: decision-card
|
|
3
|
-
version: 0.2.
|
|
3
|
+
version: 0.2.1
|
|
4
4
|
scope: system
|
|
5
5
|
description: "In a session with objectives, a long answer ending in a question gets a card: a short summary and the question, answered in the composer"
|
|
6
6
|
trust_level: reviewed
|
|
7
7
|
plugin:
|
|
8
8
|
file: plugin.rb
|
|
9
|
-
sha256: sha256:
|
|
10
|
-
# ctx.
|
|
11
|
-
requires_chi: ">= 0.
|
|
9
|
+
sha256: sha256:b85fd2162e41d1ea059ff6ca89ce6bb543e4f283d18337e9d4ac6936477d3770
|
|
10
|
+
# ctx.last_answer is new after 0.153.0 (ctx.objectives, ctx.turn_running? after 0.148.0)
|
|
11
|
+
requires_chi: ">= 0.154.0"
|
|
12
12
|
# config.yml bundles: decision-card: (another key warns; an older chi ignores this)
|
|
13
13
|
settings: [min_chars, tail_chars, model, timeout, max_tokens]
|
|
@@ -135,7 +135,7 @@ class Plugin
|
|
|
135
135
|
return SHORT_BUSY if ctx.turn_running?
|
|
136
136
|
|
|
137
137
|
messages = ctx.messages
|
|
138
|
-
answer = last_answer
|
|
138
|
+
answer = ctx.last_answer or return "no answer to shorten yet"
|
|
139
139
|
|
|
140
140
|
id = ctx.card(title: SHORT_TITLE, body: "…")
|
|
141
141
|
@mutex.synchronize { @shorts[id] = ctx }
|
|
@@ -158,14 +158,6 @@ class Plugin
|
|
|
158
158
|
ctx.card(id: id, title: SHORT_TITLE, body: body)
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
-
# @return [String, nil] the newest non-empty answer's text
|
|
162
|
-
def last_answer(messages)
|
|
163
|
-
found = Array(messages).reverse_each.find do |message|
|
|
164
|
-
%w[model assistant].include?(field(message, :role).to_s) && !field(message, :content).to_s.strip.empty?
|
|
165
|
-
end
|
|
166
|
-
found && field(found, :content).to_s
|
|
167
|
-
end
|
|
168
|
-
|
|
169
161
|
def turn_ended(event, ctx)
|
|
170
162
|
return unless event[:status].to_s == "completed"
|
|
171
163
|
# A participant's question is for the participant, on the owner's model; a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: github-pr
|
|
3
|
-
version: 0.6.
|
|
3
|
+
version: 0.6.4
|
|
4
4
|
scope: system
|
|
5
5
|
description: Proposes the branch's open GitHub pull request to a session, attached once you mention it (#N, the branch or its URL), and attaches any PR URL with chi context add or the web's "+ URL"; the agent gets a note when reviews, comments, checks or its state change; in the web, a path:line in an answer links to that line of the PR; the auto_attach setting can attach the PR at once instead, or do nothing
|
|
6
6
|
trust_level: reviewed
|
|
@@ -11,7 +11,7 @@ needs:
|
|
|
11
11
|
hint: brew install gh, then gh auth login
|
|
12
12
|
plugin:
|
|
13
13
|
file: plugin.rb
|
|
14
|
-
sha256: sha256:
|
|
14
|
+
sha256: sha256:87678c39565a3d586b462d0449a27ee3da4a96f53ef5b352dea959256f6c1eda
|
|
15
15
|
scripts:
|
|
16
16
|
pr_context.rb: sha256:a883d2780e89c68c0d7b6d5e0fcb22c273f17bb167e79064e0e99db6bd2ee6f0
|
|
17
17
|
context_providers:
|
|
@@ -29,11 +29,11 @@ require "samagotchi/git_env"
|
|
|
29
29
|
# diff" (or "File at head") action the web's ref menu offers. The
|
|
30
30
|
# after_turn hook links from a per-PR cache and never runs gh (the web holds the answer until the hooks are
|
|
31
31
|
# done); before_turn, before_generation and the init task fill the cache
|
|
32
|
-
# off the turn's thread (`gh api`), in a session worker only:
|
|
33
|
-
#
|
|
32
|
+
# off the turn's thread (`gh api`), in a session worker only: -p
|
|
33
|
+
# --non-interactive shows no web page while it runs (ctx.frontend; a chi
|
|
34
34
|
# without it counts as a worker), and a scratch session never fetches.
|
|
35
35
|
# `bundles: github-pr: line_links:` false turns it off, "always" fetches in
|
|
36
|
-
#
|
|
36
|
+
# -p --non-interactive too (still not for a scratch session).
|
|
37
37
|
#
|
|
38
38
|
# `bundles: github-pr: auto_attach:` says what the init task does with the
|
|
39
39
|
# branch's open PR: `propose` (the default, above), `attach` (attached at
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skills
|
|
3
|
-
version: 0.1.
|
|
3
|
+
version: 0.1.10
|
|
4
4
|
scope: system
|
|
5
5
|
description: "Skills (skill_<name> memories, the steps of a task done together): /skill save, list, show and diff; older versions kept with a short diff line on every update; a nudge when a followed skill's step fails"
|
|
6
6
|
trust_level: reviewed
|
|
7
7
|
plugin:
|
|
8
8
|
file: plugin.rb
|
|
9
|
-
sha256: sha256:
|
|
9
|
+
sha256: sha256:8d72d29286d0807c18d2c37225455ca06d45f4f974f9335b76cc77916043838c
|
|
10
10
|
requires_chi: ">= 0.143.0"
|
|
11
11
|
# config.yml bundles: skills: (another key warns; an older chi ignores this)
|
|
12
12
|
settings: [history_keep, nudge]
|
|
@@ -242,8 +242,9 @@ class Plugin
|
|
|
242
242
|
# what was just done (the model has seen the commands; a side answer
|
|
243
243
|
# wouldn't). The request runs as a turn; sent while a turn runs it joins
|
|
244
244
|
# that turn at its next step, and the request says to finish the task
|
|
245
|
-
# first. A
|
|
246
|
-
# shown for the user to
|
|
245
|
+
# first. A session a one-turn run holds (-p --non-interactive, or an older
|
|
246
|
+
# chi's terminal) takes no messages: the request is shown for the user to
|
|
247
|
+
# send.
|
|
247
248
|
def save(rest, ctx)
|
|
248
249
|
words = rest.split
|
|
249
250
|
scope = words.delete("--system") ? "system" : "project"
|
|
@@ -18,7 +18,7 @@ Single registry `Samagotchi::Config` (`Config::ENTRIES` in `lib/samagotchi/confi
|
|
|
18
18
|
|
|
19
19
|
Sections forbid `_`/`-` (`SECTION_RE` `/\A[a-z0-9]+\z/`), since an env or CLI name is derived from them; a config-only section (`system_one`: `expose: [:config]`, no derived name) may carry `_`. Leaves keep `snake_case` in YAML (`base_url`) and become kebab in CLI (`base-url`) via registry derivation — no generic string split, registry lookup avoids flat vs nested collision.
|
|
20
20
|
|
|
21
|
-
**Universal entries** (`expose: [:env,:config,:cli]`): `default.model`, `server.host/port/open_timeout/read_timeout`, `server.first_token_timeout` (env and config only), `recap.model/base_url/host_ref/inactivity/timeout/min_user_turns/sentences`, `session.retention_days/max_count/keep_status/sweep_interval_hours/idle_exit_minutes`, `session.
|
|
21
|
+
**Universal entries** (`expose: [:env,:config,:cli]`): `default.model`, `server.host/port/open_timeout/read_timeout`, `server.first_token_timeout` (env and config only), `recap.model/base_url/host_ref/inactivity/timeout/min_user_turns/sentences`, `session.retention_days/max_count/keep_status/sweep_interval_hours/idle_exit_minutes`, `session.keep_empty/max_children` (env and config only), `log.file/disable`, `status.line`, `context.status/window_tokens/chars_per_token/status_thresholds`, `memory.index_warn_tokens/entry_warn_tokens/read_max_chars`, `default.max_tokens`, `max_tool_output_chars`, `retry.max/base_delay/max_delay`, `read.*`, `execute.*`, `web.port/host`, `no_interrupt`, `no_default_input` etc. (`Config::ENTRIES`; `expose` says which of env/config/cli each takes). Precedence is `CLI > ENV > file > default`.
|
|
22
22
|
|
|
23
23
|
Example `config.yml` (new nested form, preferred):
|
|
24
24
|
|
|
@@ -40,10 +40,9 @@ recap:
|
|
|
40
40
|
session:
|
|
41
41
|
retention_days: 14
|
|
42
42
|
max_count: 500
|
|
43
|
-
keep_status: "" # the default: no status protects a session from pruning; a live worker or
|
|
43
|
+
keep_status: "" # the default: no status protects a session from pruning; a live worker or a one-turn run always does
|
|
44
44
|
sweep_interval_hours: 24
|
|
45
45
|
idle_exit_minutes: 30 # a background worker nobody uses exits; 0 = never
|
|
46
|
-
shared: true # the default: plain `chi` runs its session in a background worker and attaches (as `chi --shared`); false keeps the in-process REPL (env SAMAGOTCHI_SESSION_SHARED; no CLI flag, `--no-shared` opts out per run)
|
|
47
46
|
keep_empty: false # the default: a session nothing happened in (no prompt, default model, no note/image) is deleted when left; true keeps it (env SAMAGOTCHI_SESSION_KEEP_EMPTY)
|
|
48
47
|
max_children: 4 # running child sessions one session may have delegated at a time (the delegate tool; env SAMAGOTCHI_SESSION_MAX_CHILDREN)
|
|
49
48
|
log:
|
|
@@ -77,7 +76,7 @@ guardrails:
|
|
|
77
76
|
disable: [guardrails:git-rebase]
|
|
78
77
|
```
|
|
79
78
|
|
|
80
|
-
- `bundles:` map of installed bundle name → its settings (one Hash, string keys, handed to the bundle's hook/plugin `initialize(settings)`). Read when a session's Engine starts: after a change restart the session's worker (`chi sessions stop <id>`)
|
|
79
|
+
- `bundles:` map of installed bundle name → its settings (one Hash, string keys, handed to the bundle's hook/plugin `initialize(settings)`). Read when a session's Engine starts: after a change restart the session's worker (`chi sessions stop <id>`). Unknown keys are ignored by the bundle, not validated. `chi bundle list` names the bundles; each bundle's keys are in `docs/bundles.md` / `docs/guardrails.md`.
|
|
81
80
|
|
|
82
81
|
```yaml
|
|
83
82
|
bundles:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: samagotchi-system
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.154.1
|
|
4
4
|
scope: system
|
|
5
5
|
description: Default system memories — identity, self map, config modification protocol, memory guide and the delegated-session rules
|
|
6
6
|
files:
|
|
7
7
|
identity.md: sha256:29c2f7df9f6c5acbc5ec17000a4490741e55e426af15ca0d81c8706edd9bd027
|
|
8
|
-
self_map.md: sha256:
|
|
9
|
-
config_modification_protocol.md: sha256:
|
|
8
|
+
self_map.md: sha256:709bd52710bf6c50334a89a10742ea1554c5985cc2d7c4e7571e526cc4479526
|
|
9
|
+
config_modification_protocol.md: sha256:f3f6545c93d59b624a2bf1766e26059520ffc7b236470dd9f1b51ab77312376d
|
|
10
10
|
memory_guide.md: sha256:8b7727b939b9c04486ca59859561023dcc319a73e0f5b47b3fcf5741edaa69e2
|
|
11
11
|
delegated.md: sha256:a0d6ddcd4a8be06526d964898611775fd251cfd9d145a9d16afaccaa503597bd
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
`docs/bundle-authoring.md` in my source dir first.
|
|
68
68
|
|
|
69
69
|
## Where things live (relative to the source dir)
|
|
70
|
-
- `docs/`: `cli.md` (flags, subcommands,
|
|
70
|
+
- `docs/`: `cli.md` (flags, subcommands, the terminal's commands), `configuration.md` (config.yml keys,
|
|
71
71
|
hosts, the server api), `hooks.md` (hook events table), `plugins.md` (plugin API), `bundles.md` (each shipped bundle: btw, mcp, cost-cap…),
|
|
72
72
|
`guardrails.md`, `memory.md`, `sessions.md`. `README.md` is only the quickstart.
|
|
73
73
|
- `lib/samagotchi/tools/<tool>.rb`: each tool's real limits and defaults (e.g. `execute.rb`,
|
|
@@ -47,7 +47,7 @@ module Samagotchi
|
|
|
47
47
|
# What a continue does with the previous link's children: +move+ the
|
|
48
48
|
# direct children whose subtree has an open node (they go to the new
|
|
49
49
|
# link, their subtrees with them), +refuse+ the open ones it can't move
|
|
50
|
-
# (SessionContinue::OpenChild, why: an older chi worker or a chi
|
|
50
|
+
# (SessionContinue::OpenChild, why: an older chi worker or a one-turn chi run).
|
|
51
51
|
Plan = Data.define(:move, :refuse)
|
|
52
52
|
# A move under way: from the session, the children moved, and those
|
|
53
53
|
# already sent their note (so a move finished again notes no one twice).
|
|
@@ -87,14 +87,14 @@ module Samagotchi
|
|
|
87
87
|
Plan.new(move: move, refuse: refuse)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# Why a child's live owner keeps it from moving: a chi
|
|
90
|
+
# Why a child's live owner keeps it from moving: a one-turn chi run holds its
|
|
91
91
|
# own Session and never reads the override; a worker on an older chi
|
|
92
92
|
# neither. A worker with no sidecar yet is starting, on the newest chi
|
|
93
93
|
# installed (SessionManager.worker_command): it moves.
|
|
94
94
|
# @return [String, nil]
|
|
95
95
|
def unmovable(child_id, state_dir:)
|
|
96
96
|
link = WorkerLink.resolve(Session.session_dir(child_id, state_dir: state_dir), unlink: false)
|
|
97
|
-
return "
|
|
97
|
+
return "#{OwnerLock::TUI_BUSY}; let it finish" if link.owner&.tui?
|
|
98
98
|
return nil if !link.live? || link.features.include?(FEATURE)
|
|
99
99
|
|
|
100
100
|
"older chi worker#{" #{link.version}" if link.version}; restart it"
|
|
@@ -29,9 +29,9 @@ module Samagotchi
|
|
|
29
29
|
# @!attribute mid_turn [Symbol, #call] what a line of it does while a
|
|
30
30
|
# turn runs (MID_TURN): a symbol, or a lambda line (stripped) → one
|
|
31
31
|
# @!attribute local [Boolean] the UI runs it; completion and help only
|
|
32
|
-
# @!attribute uis [Array<Symbol>, nil] a local entry's UIs (:
|
|
33
|
-
# :
|
|
34
|
-
#
|
|
32
|
+
# @!attribute uis [Array<Symbol>, nil] a local entry's UIs (:attached,
|
|
33
|
+
# :web) for completion and /help; nil means every UI (for a local
|
|
34
|
+
# entry, the terminal)
|
|
35
35
|
# @!attribute match [#call] line (stripped) → whether it is this command
|
|
36
36
|
# @!attribute handler [Proc, nil] runs it; SessionCommands instance_execs
|
|
37
37
|
# a built-in's with the line, and calls a bundle's with the text
|
|
@@ -165,8 +165,8 @@ module Samagotchi
|
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
# @return [Entry, nil] the first entry the UI runs itself (local) that
|
|
168
|
-
# matches +line+, whatever its uis (a UI answers the others' too
|
|
169
|
-
#
|
|
168
|
+
# matches +line+, whatever its uis (a UI answers the others' too,
|
|
169
|
+
# with a note)
|
|
170
170
|
def lookup_local(line)
|
|
171
171
|
text = line.to_s.strip
|
|
172
172
|
@entries.find { |entry| entry.local && entry.match?(text) }
|
|
@@ -190,7 +190,7 @@ module Samagotchi
|
|
|
190
190
|
end
|
|
191
191
|
end
|
|
192
192
|
|
|
193
|
-
# @param ui [Symbol] :
|
|
193
|
+
# @param ui [Symbol] :attached
|
|
194
194
|
# @return [Array<String>] the /names Tab offers in +ui+, sorted
|
|
195
195
|
def completions(ui)
|
|
196
196
|
@entries.select { |entry| entry.slash? && entry.in_ui?(ui) }.map(&:name).uniq.sort
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
require_relative "delivery"
|
|
4
4
|
|
|
5
5
|
module Samagotchi
|
|
6
|
-
# Where a line typed at a terminal's prompt goes
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# attached TUI's continue offer and question). The web's composer.js
|
|
6
|
+
# Where a line typed at a terminal's prompt goes (AttachedLoop#submit; a
|
|
7
|
+
# -p prompt's /cut or /queue: TerminalUI#run). It only classifies; each
|
|
8
|
+
# UI keeps what it does with the answer (the attached TUI's continue
|
|
9
|
+
# offer and question). The web's composer.js
|
|
11
10
|
# routeComposerLine decides the same; spec/fixtures/composer_routes.json
|
|
12
11
|
# holds the cases both answer (docs/testing.md).
|
|
13
12
|
#
|
data/lib/samagotchi/config.rb
CHANGED
|
@@ -119,7 +119,7 @@ module Samagotchi
|
|
|
119
119
|
check: ->(value) { "N or N-M (1-#{IdleRecap::RecapPrompt::MAX_SENTENCES})" unless IdleRecap::RecapPrompt.sentences_range(value) }),
|
|
120
120
|
|
|
121
121
|
# chi broadcast (Broadcast::Recipients, Broadcast::Tags): a session that ended no turn in this many hours (and
|
|
122
|
-
# no worker
|
|
122
|
+
# no worker runs) gets no broadcast; a ticket id in the note and in a session's branch or prompts
|
|
123
123
|
# delivers it there without triage.
|
|
124
124
|
Entry.new(key: "broadcast.active_hours", yaml_path: %w[broadcast active_hours], type: :float, default: 8.0, expose: %i[env config], lifecycle: :next_broadcast),
|
|
125
125
|
Entry.new(key: "broadcast.ticket_pattern", yaml_path: %w[broadcast ticket_pattern], type: :string, default: '\b[A-Z][A-Z0-9]+-\d+\b', expose: %i[env config], lifecycle: :next_broadcast),
|
|
@@ -140,8 +140,6 @@ module Samagotchi
|
|
|
140
140
|
Entry.new(key: "session.idle_exit_minutes", yaml_path: %w[session idle_exit_minutes], type: :float, default: 30.0, expose: %i[env config cli], lifecycle: :worker_start),
|
|
141
141
|
# Tools follow the project: a worker applies its folder's `mise env` at start (ProjectEnv); off keeps the spawner's env.
|
|
142
142
|
Entry.new(key: "session.project_env", yaml_path: %w[session project_env], type: :enum, default: "mise", expose: %i[env config], enum_values: %w[mise off], lifecycle: :worker_start),
|
|
143
|
-
# Plain `chi` runs like `chi --shared` (bin/chi, LaunchMode); false, or --no-shared per run, keeps the plain REPL. No CLI flag: that would duplicate --shared.
|
|
144
|
-
Entry.new(key: "session.shared", yaml_path: %w[session shared], type: :bool, default: true, expose: %i[env config], lifecycle: :next_invocation),
|
|
145
143
|
# false: a continue's carry note holds the previous link's recap only (SessionChain.note_text), not its last answer too.
|
|
146
144
|
Entry.new(key: "session.carry_last_answer", yaml_path: %w[session carry_last_answer], type: :bool, default: true, expose: %i[env config], lifecycle: :next_lifecycle_decision),
|
|
147
145
|
# false: a session nothing happened in is deleted when it is left (SessionManager.discardable?).
|
|
@@ -698,7 +696,7 @@ module Samagotchi
|
|
|
698
696
|
# Live resolve so ENV changes (as in specs) are reflected without explicit reload
|
|
699
697
|
# Use current ENV and file (via ConfigFile's cached reader), plus any CLI
|
|
700
698
|
# overrides captured via reload!
|
|
701
|
-
path = Samagotchi::ConfigFile.global_path rescue nil
|
|
699
|
+
path = Samagotchi::ConfigFile.global_path rescue nil # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
702
700
|
file_data = Samagotchi::ConfigFile.read_yaml(path: path) if path
|
|
703
701
|
resolve_with_origin(entry.key, file_data: file_data, env: ENV, cli_overrides: cli_overrides, session: session)
|
|
704
702
|
end
|
|
@@ -817,7 +815,7 @@ module Samagotchi
|
|
|
817
815
|
end
|
|
818
816
|
|
|
819
817
|
# Keys replaced by another, and what the warning adds: it names the new
|
|
820
|
-
# one (no alias).
|
|
818
|
+
# one (no alias). A key removed with nothing in its place has nil.
|
|
821
819
|
PARENT_ANSWERS_HINT = "hard, standard or soft; see docs/guardrails.md"
|
|
822
820
|
SYSTEM_ONE_MOVED_HINT = "it has moved: until all of host, model and timeout_ms have, nothing is judged or " \
|
|
823
821
|
"auto-allowed, and every ask goes to you; see docs/configuration.md"
|
|
@@ -826,14 +824,18 @@ module Samagotchi
|
|
|
826
824
|
"turn.parent_continue" => ["guardrails.parent_answers", PARENT_ANSWERS_HINT],
|
|
827
825
|
"guardrails.system_one.host" => ["system_one.host", SYSTEM_ONE_MOVED_HINT],
|
|
828
826
|
"guardrails.system_one.model" => ["system_one.model", SYSTEM_ONE_MOVED_HINT],
|
|
829
|
-
"guardrails.system_one.timeout_ms" => ["system_one.timeout_ms", SYSTEM_ONE_MOVED_HINT]
|
|
827
|
+
"guardrails.system_one.timeout_ms" => ["system_one.timeout_ms", SYSTEM_ONE_MOVED_HINT],
|
|
828
|
+
"session.shared" => [nil, "chi always runs attached"]
|
|
830
829
|
}.freeze
|
|
831
830
|
|
|
832
831
|
# nil when +dotted+ is a key the file may hold.
|
|
833
832
|
def key_problem(dotted)
|
|
834
833
|
return nil if known_config_keys.include?(dotted)
|
|
835
834
|
return "config: #{ConfigFile.removed_transport_problem("'#{dotted}'")}" if dotted == "server.transport"
|
|
835
|
+
|
|
836
836
|
if (replaced = REPLACED_KEYS[dotted])
|
|
837
|
+
return "config: unknown key '#{dotted}': removed: #{replaced.last}" unless replaced.first
|
|
838
|
+
|
|
837
839
|
return "config: unknown key '#{dotted}': replaced by '#{replaced.first}' (#{replaced.last})"
|
|
838
840
|
end
|
|
839
841
|
|
|
@@ -1231,7 +1233,7 @@ module Samagotchi
|
|
|
1231
1233
|
return [] unless raw.is_a?(Hash)
|
|
1232
1234
|
|
|
1233
1235
|
raw.filter_map { |name, cfg| name.to_s.strip.downcase if cfg.is_a?(Hash) && HostConfig.disabled?(cfg) }
|
|
1234
|
-
rescue StandardError
|
|
1236
|
+
rescue StandardError # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
1235
1237
|
[]
|
|
1236
1238
|
end
|
|
1237
1239
|
|
|
@@ -1277,7 +1279,7 @@ module Samagotchi
|
|
|
1277
1279
|
|
|
1278
1280
|
items = raw.is_a?(Array) ? raw : [raw]
|
|
1279
1281
|
items.flat_map { |v| v.to_s.split(",") }.map(&:strip).reject(&:empty?)
|
|
1280
|
-
rescue StandardError
|
|
1282
|
+
rescue StandardError # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
1281
1283
|
[]
|
|
1282
1284
|
end
|
|
1283
1285
|
|
|
@@ -1296,7 +1298,7 @@ module Samagotchi
|
|
|
1296
1298
|
else []
|
|
1297
1299
|
end
|
|
1298
1300
|
items.select { |v| v.is_a?(String) }.map(&:strip).reject(&:empty?).filter_map { |v| expand_appended_entry(v) }
|
|
1299
|
-
rescue StandardError
|
|
1301
|
+
rescue StandardError # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
1300
1302
|
[]
|
|
1301
1303
|
end
|
|
1302
1304
|
|
|
@@ -1355,7 +1357,7 @@ module Samagotchi
|
|
|
1355
1357
|
end
|
|
1356
1358
|
|
|
1357
1359
|
raw.each_with_object({}) { |(name, value), acc| acc[name.to_s] = value.is_a?(Hash) ? value : {} }
|
|
1358
|
-
rescue StandardError
|
|
1360
|
+
rescue StandardError # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
1359
1361
|
{}
|
|
1360
1362
|
end
|
|
1361
1363
|
|
|
@@ -1414,7 +1416,7 @@ module Samagotchi
|
|
|
1414
1416
|
|
|
1415
1417
|
result[key.downcase] = val
|
|
1416
1418
|
end
|
|
1417
|
-
rescue StandardError
|
|
1419
|
+
rescue StandardError # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
1418
1420
|
{}
|
|
1419
1421
|
end
|
|
1420
1422
|
|
|
@@ -1432,7 +1434,7 @@ module Samagotchi
|
|
|
1432
1434
|
settings = ModelSettings.parse(key, v)
|
|
1433
1435
|
result[key] = settings if settings
|
|
1434
1436
|
end
|
|
1435
|
-
rescue StandardError
|
|
1437
|
+
rescue StandardError # degrade-ok: Config↔Log: Log resolves its file and level through Config
|
|
1436
1438
|
{}
|
|
1437
1439
|
end
|
|
1438
1440
|
|
|
@@ -117,7 +117,7 @@ module Samagotchi
|
|
|
117
117
|
# @param show_card [#call] (**card) → replaces the card
|
|
118
118
|
# @return [Array(Symbol, String)] :ok or :error, and the answer
|
|
119
119
|
def choose(action, name, session:, state_dir:, show_card:)
|
|
120
|
-
return [:error,
|
|
120
|
+
return [:error, Session::NONE_YET] unless session&.id
|
|
121
121
|
return [:error, "a review session takes no context candidates"] if session.review?
|
|
122
122
|
return [:error, USAGE] if name.to_s.empty?
|
|
123
123
|
|
|
@@ -176,7 +176,7 @@ module Samagotchi
|
|
|
176
176
|
.filter_map { |candidate, state| candidate if state == "proposed" }
|
|
177
177
|
end
|
|
178
178
|
|
|
179
|
-
# Turn start (Worker#run_prompt before its absorb;
|
|
179
|
+
# Turn start (Worker#run_prompt before its absorb; a one-shot's turn):
|
|
180
180
|
# the proposed candidates +prompt+ (or an earlier owner prompt: a late
|
|
181
181
|
# proposal) mentions are attached, then each gets one first fetch
|
|
182
182
|
# (together within +seconds+), so the note reaches this turn. Only an
|
|
@@ -192,7 +192,7 @@ module Samagotchi
|
|
|
192
192
|
return [] if own.candidates.empty? || !OwnerPrompt.turn?(session: session, author: author)
|
|
193
193
|
|
|
194
194
|
# The earlier owner prompts too: a proposal that came while one ran
|
|
195
|
-
# (
|
|
195
|
+
# (a one-shot's plugins never see a running turn's prompt).
|
|
196
196
|
texts = owner_texts(session.messages) + (prompt.is_a?(String) ? [prompt] : [])
|
|
197
197
|
found = hits(texts, proposed(session.id, project_root: session.project_root, state_dir: state_dir))
|
|
198
198
|
names = found.filter_map { |hit| attach(hit, own: own, state_dir: state_dir) }
|
|
@@ -203,7 +203,8 @@ module Samagotchi
|
|
|
203
203
|
[]
|
|
204
204
|
end
|
|
205
205
|
|
|
206
|
-
#
|
|
206
|
+
# A --non-interactive one-shot's turn start (it has no ContextAbsorber
|
|
207
|
+
# running, TerminalUI#attach_mentioned_context): the notes
|
|
207
208
|
# of the sources attached from candidates, as the worker's absorb gives
|
|
208
209
|
# them, added to +engine+'s session and marked delivered.
|
|
209
210
|
# @return [Integer] how many notes were added
|
|
@@ -134,7 +134,7 @@ module Samagotchi
|
|
|
134
134
|
# The continue offer was answered yes: resume the conversation without a
|
|
135
135
|
# user message, with the iteration limit the offer's turn had, from the
|
|
136
136
|
# run that turn started (TurnFlow#resume_from: its reasoning stays). A failure
|
|
137
|
-
# keeps the offer
|
|
137
|
+
# keeps the offer ("continue prompt preserved"), and
|
|
138
138
|
# asks again, one before the turn began too (logged, no :turn_failed).
|
|
139
139
|
# The answer's text is carried into the turn as it begins; a turn that
|
|
140
140
|
# never began leaves it to no other.
|
data/lib/samagotchi/debug_log.rb
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "log"
|
|
4
|
+
|
|
5
|
+
module Samagotchi
|
|
6
|
+
# A logged degradation: a piece of work whose failure has a fallback (a
|
|
7
|
+
# prompt section left out, an empty list, nil), with a record in the debug
|
|
8
|
+
# log instead of silence.
|
|
9
|
+
#
|
|
10
|
+
# Degrade.call(:prompt, "model_notes_unreadable", fallback: nil, model: name) { read_notes }
|
|
11
|
+
# Degrade.call(:bridge, "state_unreadable", fallback: {}, rescue: [SystemCallError, JSON::ParserError]) { … }
|
|
12
|
+
#
|
|
13
|
+
# Runs the block and returns its value. When it raises one of +rescue:+
|
|
14
|
+
# (StandardError by default; never Exception), returns +fallback+ and logs
|
|
15
|
+
# Log.warn(area, event, error:, msg:, **fields) the first time this
|
|
16
|
+
# process sees [area, event], Log.debug after: a hot path that fails on
|
|
17
|
+
# every call writes one WARN, and the happy path takes no lock. Nothing
|
|
18
|
+
# goes to stderr (a worker has none; a terminal line would be noise).
|
|
19
|
+
#
|
|
20
|
+
# A `return` in the block returns from the enclosing method as usual; a
|
|
21
|
+
# `next` or `break` would change what the site does, so a site that needs
|
|
22
|
+
# one logs by hand (Degrade.note) instead.
|
|
23
|
+
#
|
|
24
|
+
# Written Degrade.call (Style/LambdaCall turns Degrade.() into it).
|
|
25
|
+
#
|
|
26
|
+
# +area+ is a Log tag (LogLine::TAGS), +event+ a slug saying what was
|
|
27
|
+
# lost; with Log's strict mode on (the specs) both are checked on every
|
|
28
|
+
# call, the happy path's included.
|
|
29
|
+
module Degrade
|
|
30
|
+
MSG_CHARS = 500
|
|
31
|
+
|
|
32
|
+
@seen = Set.new
|
|
33
|
+
@mutex = Mutex.new
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
# @param area [Symbol] a Log tag
|
|
37
|
+
# @param event [String, Symbol] what was lost (agents_md_unreadable)
|
|
38
|
+
# @param fallback [Object] the value when the block raises
|
|
39
|
+
# @param fields [Hash] extra record fields; +rescue:+ (a class or an
|
|
40
|
+
# array of them, default StandardError) is the one taken out
|
|
41
|
+
# @return the block's value, or +fallback+
|
|
42
|
+
def call(area, event, fallback: nil, **fields)
|
|
43
|
+
classes = Array(fields.delete(:rescue) || StandardError)
|
|
44
|
+
check!(area, event) if Log.strict
|
|
45
|
+
begin
|
|
46
|
+
yield
|
|
47
|
+
rescue *classes => e
|
|
48
|
+
note(area, event, e, **fields)
|
|
49
|
+
fallback
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Log a degradation the caller has already handled (its rescue keeps
|
|
54
|
+
# a next/break or a fallback of its own): the same once-per-process
|
|
55
|
+
# WARN, DEBUG after.
|
|
56
|
+
# @return nil
|
|
57
|
+
def note(area, event, error, **fields)
|
|
58
|
+
first = @mutex.synchronize { @seen.add?([area.to_sym, event.to_s]) }
|
|
59
|
+
record = { error: error.class.name, msg: error.message.to_s[0, MSG_CHARS], **fields }
|
|
60
|
+
first ? Log.warn(area, event.to_s, **record) : Log.debug(area, event.to_s, **record)
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Forget what was logged (specs, with Log.reset!).
|
|
65
|
+
def reset! = @mutex.synchronize { @seen.clear }
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def check!(area, event)
|
|
70
|
+
raise ArgumentError, "unknown log tag #{area}" unless LogLine::TAGS.include?(area.to_s)
|
|
71
|
+
raise ArgumentError, "bad log event #{event}" unless event.to_s.match?(LogLine::SLUG)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -182,8 +182,8 @@ final class ChiRunner {
|
|
|
182
182
|
/// The live sessions, oldest started first, then up to +recentCount+
|
|
183
183
|
/// stopped ones, newest first. The live order is by creation so ⌘1…⌘9
|
|
184
184
|
/// stay on their sessions as turns update them; a new one comes last.
|
|
185
|
-
/// A session a chi
|
|
186
|
-
/// messages. A failed second call just leaves "recent" empty. Calls
|
|
185
|
+
/// A session a one-turn chi --non-interactive run holds is in neither: it
|
|
186
|
+
/// takes no notes or messages. A failed second call just leaves "recent" empty. Calls
|
|
187
187
|
/// back on +queue+ (a headless seam waits on another one).
|
|
188
188
|
func sessions(recentCount: Int = 3, queue: DispatchQueue = .main,
|
|
189
189
|
completion: @escaping (Result<[LiveSession], ChiError>) -> Void) {
|
|
@@ -199,7 +199,7 @@ final class ChiRunner {
|
|
|
199
199
|
private static let recentFlags = ["--limit", "20"]
|
|
200
200
|
|
|
201
201
|
/// The live rows, then up to +recentCount+ of the recent list that are
|
|
202
|
-
/// neither live nor a
|
|
202
|
+
/// neither live nor held by an owner (a one-turn run); a failed recent list adds none.
|
|
203
203
|
private static func rows(live: [LiveSession], recent: Result<[LiveSession], ChiError>, recentCount: Int) -> [LiveSession] {
|
|
204
204
|
let liveIds = Set(live.map(\.id))
|
|
205
205
|
let stopped = ((try? recent.get()) ?? [])
|