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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { html } from "./vendor/lit-html.js";
|
|
2
|
+
import { cantText } from "./format.js";
|
|
3
|
+
import { workerBadgeFor } from "./info_bar_model.js";
|
|
4
|
+
import { readStored, writeStored } from "./storage.js";
|
|
5
|
+
import { createUpdateSheet } from "./update_sheet.js";
|
|
6
|
+
import { mergeVersionState, restartConfirmText, updateFrameToast, versionNotice } from "./update_notice.js";
|
|
7
|
+
|
|
8
|
+
// An available release's notice is once per version per browser, not per
|
|
9
|
+
// tab: chi web says it in every snapshot until the owner updates.
|
|
10
|
+
export const AVAILABLE_SHOWN_KEY = "chi_update_available_shown";
|
|
11
|
+
|
|
12
|
+
// The owner page's chi versions (used by app.js): the versions carried by each
|
|
13
|
+
// event snapshot and `chi` frame, the toasts they bring (chi web upgraded, a
|
|
14
|
+
// newer one installed, an aged one available to update to), the update sheet
|
|
15
|
+
// (update_sheet.js) and its `chi_update` frames, and the open session's
|
|
16
|
+
// worker Restart.
|
|
17
|
+
//
|
|
18
|
+
// +store+: the page's session store (selected and current read; it writes no
|
|
19
|
+
// field, only touch("infoBar"): the worker badge and the Restart button read
|
|
20
|
+
// the versions and the restarting set). +toasts+: toasts.js's. +dialog+: the
|
|
21
|
+
// update sheet's <dialog>. +loadedVersion+: the chi this page was loaded
|
|
22
|
+
// from (body data-version). +api+: {updateChi, restartWorkers, restartWeb,
|
|
23
|
+
// restartSession} (data.js). +confirm+, +reload+, +clipboard()+: the
|
|
24
|
+
// browser's; +storage+: storage.js's guarded read and write.
|
|
25
|
+
export function createVersionNotices({
|
|
26
|
+
store, toasts, dialog, loadedVersion, api,
|
|
27
|
+
confirm = (text) => globalThis.confirm(text),
|
|
28
|
+
reload = () => globalThis.location.reload(),
|
|
29
|
+
clipboard = () => globalThis.navigator?.clipboard,
|
|
30
|
+
storage = { read: readStored, write: writeStored },
|
|
31
|
+
}) {
|
|
32
|
+
const toast = (text, action = null, options = {}) => { toasts.show(text, action, options); };
|
|
33
|
+
const sheet = createUpdateSheet({
|
|
34
|
+
dialog,
|
|
35
|
+
update: api.updateChi,
|
|
36
|
+
restartWorkers: api.restartWorkers,
|
|
37
|
+
restartWeb: api.restartWeb,
|
|
38
|
+
});
|
|
39
|
+
// Once per notice per tab: every reconnect's snapshot says it again.
|
|
40
|
+
const versions = { served: null, installed: null };
|
|
41
|
+
// The sessions whose Restart is under way (their button says so): one
|
|
42
|
+
// session's restart never blocks another's.
|
|
43
|
+
const restarting = new Set();
|
|
44
|
+
const shown = new Set();
|
|
45
|
+
const availableShown = storage.read(AVAILABLE_SHOWN_KEY);
|
|
46
|
+
if (availableShown) shown.add(`available:${availableShown}`);
|
|
47
|
+
let versionToast = null;
|
|
48
|
+
|
|
49
|
+
function notice(data) {
|
|
50
|
+
Object.assign(versions, mergeVersionState(versions, data));
|
|
51
|
+
const next = versionNotice({ loaded: loadedVersion, served: versions.served,
|
|
52
|
+
installed: versions.installed, available: versions.available,
|
|
53
|
+
updateBlocked: versions.updateBlocked, auto: versions.auto, restart: versions.restart,
|
|
54
|
+
autoOffers: versions.autoOffers, shown });
|
|
55
|
+
store.touch("infoBar"); // the worker badge compares with the newest installed
|
|
56
|
+
if (!next) return;
|
|
57
|
+
shown.add(next.key);
|
|
58
|
+
if (next.key.startsWith("available:")) storage.write(AVAILABLE_SHOWN_KEY, next.key.slice("available:".length));
|
|
59
|
+
const text = next.releaseUrl
|
|
60
|
+
? html`${next.text} <a href=${next.releaseUrl} target="_blank" rel="noopener noreferrer">RubyGems</a>`
|
|
61
|
+
: next.text;
|
|
62
|
+
const action = next.reload
|
|
63
|
+
? { label: "Reload", run: () => reload() }
|
|
64
|
+
: next.updateVersion
|
|
65
|
+
? { label: "Update", run: () => sheet.install(next.updateVersion) }
|
|
66
|
+
: next.actions
|
|
67
|
+
? next.actions.map((name) => ({
|
|
68
|
+
label: name === "restartWorkers" ? "Restart workers" : "Restart chi web",
|
|
69
|
+
run: () => (name === "restartWorkers" ? sheet.restartWorkers() : sheet.restartWeb()),
|
|
70
|
+
}))
|
|
71
|
+
: next.command
|
|
72
|
+
? { label: "Copy", run: () => copyUpdateCommand(next.command) }
|
|
73
|
+
: null;
|
|
74
|
+
// One version toast at a time: a newer notice (Reload after a restart)
|
|
75
|
+
// supersedes the one before it.
|
|
76
|
+
toasts.dismiss(versionToast);
|
|
77
|
+
versionToast = toasts.show(text, action, { sticky: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// A `chi_update` frame (+snapshot+: the one an event snapshot carries).
|
|
81
|
+
function frame(data, { snapshot = false } = {}) {
|
|
82
|
+
const wasOpen = sheet.isOpen();
|
|
83
|
+
if (!sheet.receive(data)) return;
|
|
84
|
+
if (data.operation === "install" && data.state === "done" && data.version) notice({ installed: data.version });
|
|
85
|
+
const text = updateFrameToast(data, { wasOpen, snapshot });
|
|
86
|
+
if (text) toast(text, { label: "View", run: () => sheet.view() }, { sticky: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// An event snapshot's version after Restart chi web: the sheet says done.
|
|
90
|
+
function reconnected(version) {
|
|
91
|
+
if (sheet.reconnected(version)) toast(`chi web reconnected on chi ${version}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function copyUpdateCommand(command) {
|
|
95
|
+
const board = clipboard();
|
|
96
|
+
if (!board?.writeText) {
|
|
97
|
+
toast(`Clipboard unavailable; run ${command}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
board.writeText(command).then(
|
|
101
|
+
() => toast(`Copied ${command}`),
|
|
102
|
+
() => toast(`Couldn't copy ${command}`),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The open session's worker badge (info_bar_model.js workerBadgeFor).
|
|
107
|
+
function badge() {
|
|
108
|
+
return workerBadgeFor(store.selected, store.current, versions);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Restart: the session moves to a new worker on the newest chi; the page
|
|
112
|
+
// follows it (its bridge_up), the composer's draft stays as it is.
|
|
113
|
+
async function restart() {
|
|
114
|
+
const current = badge();
|
|
115
|
+
if (!current || !current.restart || restarting.has(store.selected)) return;
|
|
116
|
+
if (!confirm(restartConfirmText(store.current.workerVersion, current.newest))) return;
|
|
117
|
+
const id = store.selected;
|
|
118
|
+
restarting.add(id);
|
|
119
|
+
store.touch("infoBar");
|
|
120
|
+
try {
|
|
121
|
+
const result = await api.restartSession(id);
|
|
122
|
+
toast(result.version ? `Restarted on chi ${result.version}` : "Restarting: the new worker is starting");
|
|
123
|
+
} catch (e) {
|
|
124
|
+
toast(cantText("restart", e), null, { sticky: true });
|
|
125
|
+
} finally {
|
|
126
|
+
restarting.delete(id);
|
|
127
|
+
store.touch("infoBar");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { notice, frame, reconnected, versions: () => versions, restarting, restart };
|
|
132
|
+
}
|
|
@@ -7,7 +7,7 @@ require "openssl"
|
|
|
7
7
|
require "time"
|
|
8
8
|
require "uri"
|
|
9
9
|
require_relative "crypto"
|
|
10
|
-
require_relative "
|
|
10
|
+
require_relative "../../private_files"
|
|
11
11
|
require_relative "../../log"
|
|
12
12
|
require_relative "../../atomic_file"
|
|
13
13
|
require_relative "../../session"
|
|
@@ -256,7 +256,7 @@ module Samagotchi
|
|
|
256
256
|
# status row), parent_strategy, installed (the newest chi here)}. A
|
|
257
257
|
# worker still leaving is waited for first (Local#wake: its recap's
|
|
258
258
|
# timeout and a margin, on top of +wait+); 409
|
|
259
|
-
# owned_by_tui (a chi
|
|
259
|
+
# owned_by_tui (a one-turn chi run has it), 409 scratch (a leftover scratch
|
|
260
260
|
# session, as chi --attach here says); 504 worker_timeout (its Bridge
|
|
261
261
|
# didn't come in time); 409 worker_newer (a worker whose protocol is newer
|
|
262
262
|
# than this chi's, not resumed); 404 not_found.
|
|
@@ -425,7 +425,7 @@ module Samagotchi
|
|
|
425
425
|
# session (NoteDelivery, as chi note; source: where it came from,
|
|
426
426
|
# "cli" by default, never "broadcast"). 200 {status: "queued" (a
|
|
427
427
|
# worker adds it) or "waits" (the next start does), session_id,
|
|
428
|
-
# queued}; 409 owned_by_tui (status refused: a chi
|
|
428
|
+
# queued}; 409 owned_by_tui (status refused: a one-turn chi run has it); 400
|
|
429
429
|
# note_rejected (empty, or over 16 KiB).
|
|
430
430
|
def handle_notes(req, id)
|
|
431
431
|
body = body_hash(req) or return error_response(400, "invalid_json", "invalid JSON body")
|
|
@@ -438,8 +438,9 @@ module Samagotchi
|
|
|
438
438
|
session_access.show(id)
|
|
439
439
|
result = session_access.note(id, text: text, source: source.strip)
|
|
440
440
|
if result.status == :refused
|
|
441
|
-
error = SessionManager::OwnedByTUI.new(id)
|
|
442
|
-
return refusal_response(409, "owned_by_tui", error, session_id: id, status: "refused", queued: 0
|
|
441
|
+
error = result.scratch ? SessionManager::ScratchSession.new(id) : SessionManager::OwnedByTUI.new(id)
|
|
442
|
+
return refusal_response(409, "owned_by_tui", error, session_id: id, status: "refused", queued: 0,
|
|
443
|
+
scratch: result.scratch || nil)
|
|
443
444
|
end
|
|
444
445
|
|
|
445
446
|
json_response(200, { status: result.status.to_s, session_id: id, queued: result.queued })
|
|
@@ -16,7 +16,7 @@ module Samagotchi
|
|
|
16
16
|
# a task, dequeue a queued turn, what it could stop and stop it
|
|
17
17
|
# (handle_session_holds, handle_stop_holds — the page's Stop asks about
|
|
18
18
|
# these first), stop, restart, archive, unarchive and delete
|
|
19
|
-
# (handle_question_answer … handle_delete), with the refusals they answer (
|
|
19
|
+
# (handle_question_answer … handle_delete), with the refusals they answer (owned_by_one_shot, owned_by_tui, archive_refused). Included in App:
|
|
20
20
|
# its ROUTES name the handlers, and its helpers (json_response,
|
|
21
21
|
# error_response, parse_json, request_body, relay, not_live,
|
|
22
22
|
# bridge_error, reply_detail, worker_timeout, bridge_client,
|
|
@@ -443,7 +443,7 @@ module Samagotchi
|
|
|
443
443
|
|
|
444
444
|
# POST /api/sessions/:id/archive: hide it (and its delegates) from the
|
|
445
445
|
# lists, keep it for good. A live idle worker is stopped first; a turn
|
|
446
|
-
# running in it or a delegate, a chi
|
|
446
|
+
# running in it or a delegate, a one-turn chi run's session and a scratch one
|
|
447
447
|
# are refused (409).
|
|
448
448
|
def handle_archive(_req, id)
|
|
449
449
|
result = @manager.archive_session(id, state_dir: @state_dir, wait: STOP_WAIT_SECONDS)
|
|
@@ -451,7 +451,7 @@ module Samagotchi
|
|
|
451
451
|
json_response(200, { status: "archived", session_id: result.id, archived: result.archived,
|
|
452
452
|
stopped: result.stopped, discarded: result.discarded })
|
|
453
453
|
rescue SessionManager::OwnedByTUI => e
|
|
454
|
-
|
|
454
|
+
owned_by_one_shot(id, e)
|
|
455
455
|
rescue SessionManager::ArchiveRefused => e
|
|
456
456
|
archive_refused(e)
|
|
457
457
|
end
|
|
@@ -466,7 +466,7 @@ module Samagotchi
|
|
|
466
466
|
# DELETE /api/sessions/:id?force=true: the session for good. With
|
|
467
467
|
# force a live worker is stopped first (the page sends it: its confirm
|
|
468
468
|
# says so); without it a live worker's session is refused, 409
|
|
469
|
-
# worker_running, as chi sessions delete without --force. A chi
|
|
469
|
+
# worker_running, as chi sessions delete without --force. A one-turn chi run's
|
|
470
470
|
# session is always refused.
|
|
471
471
|
def handle_delete(req, id)
|
|
472
472
|
result = @manager.delete_session(id, state_dir: @state_dir, stop: truthy_param?(req.params["force"]),
|
|
@@ -474,7 +474,7 @@ module Samagotchi
|
|
|
474
474
|
@hub&.touch(result[:id])
|
|
475
475
|
json_response(200, { status: "deleted", session_id: result[:id], stopped: result[:stopped] })
|
|
476
476
|
rescue SessionManager::OwnedByTUI => e
|
|
477
|
-
|
|
477
|
+
owned_by_one_shot(id, e)
|
|
478
478
|
rescue SessionManager::DeleteRefused => e
|
|
479
479
|
detail = e.reason == :still_stopping ? "#{e.message}; try again in a moment" : e.message
|
|
480
480
|
refusal_response(409, e.reason.to_s, e, detail: detail, session_id: e.session_id, reason: e.reason.to_s)
|
|
@@ -482,15 +482,19 @@ module Samagotchi
|
|
|
482
482
|
|
|
483
483
|
# 409 owned_by_tui in the words a refused archive, delete or continue
|
|
484
484
|
# shows (stop, restart, a turn and a command show OwnedByTUI's own).
|
|
485
|
-
def
|
|
485
|
+
def owned_by_one_shot(id, error = nil)
|
|
486
486
|
named = SessionManager::OwnedByTUI.new(id)
|
|
487
487
|
error ||= named
|
|
488
488
|
refusal_response(409, "owned_by_tui", error, detail: named.message, session_id: error.session_id)
|
|
489
489
|
end
|
|
490
490
|
|
|
491
491
|
# 409 owned_by_tui in OwnedByTUI's own words (stop, restart, a turn, a
|
|
492
|
-
# command)
|
|
493
|
-
|
|
492
|
+
# command); scratch: true for a chi scratch session
|
|
493
|
+
# (SessionManager::ScratchSession), which a remote client raises again.
|
|
494
|
+
def owned_by_tui(error)
|
|
495
|
+
scratch = error.is_a?(SessionManager::ScratchSession) || nil
|
|
496
|
+
refusal_response(409, "owned_by_tui", error, session_id: error.session_id, scratch: scratch)
|
|
497
|
+
end
|
|
494
498
|
|
|
495
499
|
# 409 scratch or busy (every other reason: a turn, a queued prompt, an
|
|
496
500
|
# open step-limit question, in it or a delegate, +busy_id+), with the
|
|
@@ -339,7 +339,7 @@ module Samagotchi
|
|
|
339
339
|
rescue SessionManager::ArchiveRefused => e
|
|
340
340
|
[nil, archive_refused(e)]
|
|
341
341
|
rescue SessionManager::OwnedByTUI => e
|
|
342
|
-
[nil,
|
|
342
|
+
[nil, owned_by_one_shot(given, e)]
|
|
343
343
|
rescue ModelProfile::MissingModel => e
|
|
344
344
|
[nil, error_response(400, "invalid_model", e.message)]
|
|
345
345
|
rescue Session::AmbiguousId => e
|
|
@@ -41,8 +41,8 @@ module Samagotchi
|
|
|
41
41
|
# The flock leaves no file trace when its holder dies, so owners are
|
|
42
42
|
# probed: every tick for the sessions the projection believes owned
|
|
43
43
|
# (a handful; OwnerLock.owner is open + LOCK_SH|LOCK_NB + close), and
|
|
44
|
-
# all sessions this often (a
|
|
45
|
-
# without saving it has no file signal until its first save).
|
|
44
|
+
# all sessions this often (a one-shot or an older chi's REPL that
|
|
45
|
+
# opened an old session without saving it has no file signal until its first save).
|
|
46
46
|
FULL_PROBE_INTERVAL = 10.0
|
|
47
47
|
|
|
48
48
|
# What a subscriber gets: `session` with {session: summary} for an
|
|
@@ -120,8 +120,8 @@ module Samagotchi
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
# The file's question only while a worker owns the session: one a
|
|
123
|
-
# dead worker saved can't be answered (its turn is gone), nor a
|
|
124
|
-
# REPL's (
|
|
123
|
+
# dead worker saved can't be answered (its turn is gone), nor a
|
|
124
|
+
# one-shot's or an older chi's REPL's (they share nothing with the web).
|
|
125
125
|
# A question between turns (the step-limit one) is the snapshot's own.
|
|
126
126
|
# Its refs (the asking reply's, QuestionAsked) are checked again.
|
|
127
127
|
def shown_pending_question(session, turn_snapshot, owner)
|
|
@@ -108,7 +108,7 @@ module Samagotchi
|
|
|
108
108
|
# For the tab's notifications (notify.js): an open question, the
|
|
109
109
|
# running turn's open card with actions, and how the last turn
|
|
110
110
|
# ended. A question saved by a worker that died is not open, nor a
|
|
111
|
-
# chi
|
|
111
|
+
# one-turn chi run's (only a worker shares it).
|
|
112
112
|
pending_question: session.waiting_question(live: !!owner&.worker?),
|
|
113
113
|
pending_card: up ? Bridge::PendingCard.read(session_dir) : nil,
|
|
114
114
|
last_turn: session.last_turn&.to_file,
|
data/lib/samagotchi/worker.rb
CHANGED
|
@@ -14,6 +14,7 @@ require_relative "turn_note"
|
|
|
14
14
|
require_relative "steer"
|
|
15
15
|
require_relative "turn_origin"
|
|
16
16
|
require_relative "worker_idle_exit"
|
|
17
|
+
require_relative "degrade"
|
|
17
18
|
require_relative "hold_stop"
|
|
18
19
|
require_relative "children_status"
|
|
19
20
|
require_relative "worker_wakes"
|
|
@@ -51,7 +52,7 @@ module Samagotchi
|
|
|
51
52
|
# It runs the session's Engine and Bridge, takes queued prompts from the
|
|
52
53
|
# input dir one turn at a time, and returns when nobody has used it for the
|
|
53
54
|
# idle-exit timeout. A failed turn is rolled back and its prompt handed back
|
|
54
|
-
# (:prompt_restored),
|
|
55
|
+
# (:prompt_restored), and the loop goes on. A stop marked on
|
|
55
56
|
# disk exits the process; an error outside a turn marks the session and
|
|
56
57
|
# exits with 1.
|
|
57
58
|
#
|
|
@@ -188,6 +189,10 @@ module Samagotchi
|
|
|
188
189
|
# checks again then).
|
|
189
190
|
def discard? = @discard
|
|
190
191
|
|
|
192
|
+
# Whether this worker runs a `chi scratch` session: deleted as it leaves
|
|
193
|
+
# (SessionManager.run_session_loop).
|
|
194
|
+
def scratch? = !!@session&.scratch
|
|
195
|
+
|
|
191
196
|
# Whether delegate reports wait that an idle parent runs a turn for
|
|
192
197
|
# (#wake_state): SessionManager.run_session_loop starts a new worker
|
|
193
198
|
# for them after this one leaves. Reports the budget or a failure held
|
|
@@ -201,8 +206,9 @@ module Samagotchi
|
|
|
201
206
|
# to exit (Bridge POST /exit), :restart when it asked for a restart
|
|
202
207
|
# (the caller starts a new worker), :stopped when the session was stopped
|
|
203
208
|
# (chi stop), :signal when a signal stopped it (WorkerSignals),
|
|
204
|
-
# :crashed when the loop raised (the session is marked errored)
|
|
205
|
-
#
|
|
209
|
+
# :crashed when the loop raised (the session is marked errored),
|
|
210
|
+
# :scratch_left when a scratch session's terminal is gone (the caller
|
|
211
|
+
# deletes it); SessionManager.run_session_loop turns it into the exit
|
|
206
212
|
def run
|
|
207
213
|
# The thread a signal's expired grace raises in (the main thread in a
|
|
208
214
|
# worker process).
|
|
@@ -248,6 +254,8 @@ module Samagotchi
|
|
|
248
254
|
# The session, its Engine and what runs its turns, before anyone can
|
|
249
255
|
# reach it.
|
|
250
256
|
def start
|
|
257
|
+
# A scratch worker's grace for its terminal's first stream counts from here.
|
|
258
|
+
@started_at = monotonic_now
|
|
251
259
|
# The wake turns' budget (delegate reports, attached context, peers), from now.
|
|
252
260
|
@wakes = WorkerWakes.new(grace: WAKE_START_GRACE)
|
|
253
261
|
# This worker holds the session now (a continue's starting marker held
|
|
@@ -340,7 +348,7 @@ module Samagotchi
|
|
|
340
348
|
@bridge.queue_command(line, client_id: client_id, after_file: after_file,
|
|
341
349
|
enqueued_id: enqueued_id, author: author)
|
|
342
350
|
})
|
|
343
|
-
# /model's default is the config's
|
|
351
|
+
# /model's default is the config's; the Engine started
|
|
344
352
|
# on the session's model. nil when there is no default.model.
|
|
345
353
|
@default_model = ModelProfile.default_model_name
|
|
346
354
|
@commands = SessionCommands.new(engine: @engine, turn_flow: @turn_flow,
|
|
@@ -416,6 +424,31 @@ module Samagotchi
|
|
|
416
424
|
@context_poller = ContextPoller.new(session_id: @session_id, state_dir: @state_dir,
|
|
417
425
|
project_root: @session.project_root, cwd: @session.working_directory,
|
|
418
426
|
on_change: -> { @waker.wake }).start
|
|
427
|
+
@scratch_watch = start_scratch_watch if @session.scratch
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# A scratch session's turn never runs on with no terminal: a turn
|
|
431
|
+
# waiting on a question or an approval would keep the worker up for
|
|
432
|
+
# nobody. Each tick while a turn runs and its terminal is gone
|
|
433
|
+
# (#scratch_unattended?), the question is dropped and the turn
|
|
434
|
+
# cancelled as a stop cancels it; the idle pass then leaves
|
|
435
|
+
# (:scratch_left) and the session is deleted.
|
|
436
|
+
def start_scratch_watch
|
|
437
|
+
Thread.new do
|
|
438
|
+
loop do
|
|
439
|
+
sleep(@poll_interval)
|
|
440
|
+
Degrade.call(:worker, "scratch_watch_failed") { cancel_unattended_turn }
|
|
441
|
+
end
|
|
442
|
+
end.tap { |thread| thread.report_on_exception = false }
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def cancel_unattended_turn
|
|
446
|
+
return unless @engine.turn_running? && scratch_unattended?
|
|
447
|
+
|
|
448
|
+
Log.info(:worker, "scratch_turn_cancelled", sid: @session_id)
|
|
449
|
+
@engine.cancel_question(SessionManager::STOP_CANCEL_REASON) if @engine.pending_question
|
|
450
|
+
@engine.cancel_current_turn!(SessionManager::STOP_CANCEL_REASON.to_sym)
|
|
451
|
+
@waker.wake
|
|
419
452
|
end
|
|
420
453
|
|
|
421
454
|
# One pass of the loop: the first work found runs (queued commands, a
|
|
@@ -468,6 +501,7 @@ module Samagotchi
|
|
|
468
501
|
return if run_wake_turn
|
|
469
502
|
return if run_task_wake
|
|
470
503
|
return if run_due_reminders
|
|
504
|
+
return left(:scratch_left) if scratch_unattended? && !@engine.turn_running? && leave_scratch
|
|
471
505
|
return left(:idle_exit) if @idle_exit.due? && leave_idle
|
|
472
506
|
|
|
473
507
|
leaving = @exit_requested && leave_on_request
|
|
@@ -553,6 +587,7 @@ module Samagotchi
|
|
|
553
587
|
TEARDOWN_LEAVE_WAIT = 10
|
|
554
588
|
|
|
555
589
|
def teardown
|
|
590
|
+
@scratch_watch&.kill
|
|
556
591
|
@config_restart_unsubscribe&.call
|
|
557
592
|
@context_poller&.stop
|
|
558
593
|
drop_queued_commands
|
|
@@ -571,6 +606,8 @@ module Samagotchi
|
|
|
571
606
|
engine = nil
|
|
572
607
|
engine = Samagotchi::Engine.new(
|
|
573
608
|
model_name: @session.model_name,
|
|
609
|
+
# `chi scratch`: no memory_write, no delegates, approvals in memory.
|
|
610
|
+
scratch: @session.scratch,
|
|
574
611
|
model_typed: @session.model_typed,
|
|
575
612
|
# The session's --memory and --mute lists: the same prompt on every
|
|
576
613
|
# (re)spawn.
|
|
@@ -783,7 +820,7 @@ module Samagotchi
|
|
|
783
820
|
end
|
|
784
821
|
|
|
785
822
|
# A participant's cap in dollars: their own, else the config's.
|
|
786
|
-
def participant_cap(entry) = entry&.cap.is_a?(Numeric) ? entry.cap.to_f :
|
|
823
|
+
def participant_cap(entry) = entry&.cap.is_a?(Numeric) ? entry.cap.to_f : Grants.default_cap
|
|
787
824
|
|
|
788
825
|
# A participant's cleaned name, else their id.
|
|
789
826
|
def participant_label(author, name)
|
|
@@ -830,7 +867,7 @@ module Samagotchi
|
|
|
830
867
|
nil
|
|
831
868
|
end
|
|
832
869
|
|
|
833
|
-
# A due reminder runs as a continue turn
|
|
870
|
+
# A due reminder runs as a continue turn: Engine#run_turn
|
|
834
871
|
# injects the reminders as a tail system message, and no user message is
|
|
835
872
|
# added. A prompt's turn may have injected them already (a stale latch):
|
|
836
873
|
# then there is nothing to run.
|
|
@@ -1414,7 +1451,7 @@ module Samagotchi
|
|
|
1414
1451
|
# WorkerInbound.save_or_log: a failed save between turns is logged.
|
|
1415
1452
|
def save_or_log(at, &) = WorkerInbound.save_or_log(at, &)
|
|
1416
1453
|
|
|
1417
|
-
# Back to the conversation before the failed turn
|
|
1454
|
+
# Back to the conversation before the failed turn (so
|
|
1418
1455
|
# failed prompts don't pile up as consecutive user messages), and each
|
|
1419
1456
|
# prompt it took (its own and any merged into it) goes back to its
|
|
1420
1457
|
# sender, who can send it again. The rollback and the announcements are
|
|
@@ -1559,6 +1596,57 @@ module Samagotchi
|
|
|
1559
1596
|
end
|
|
1560
1597
|
end
|
|
1561
1598
|
|
|
1599
|
+
# How long a scratch worker waits for its terminal's first stream
|
|
1600
|
+
# (AttachLauncher attaches within BRIDGE_WAIT of the spawn).
|
|
1601
|
+
SCRATCH_JOIN_GRACE = 30
|
|
1602
|
+
|
|
1603
|
+
# How long a scratch worker's terminal may have no stream open, after
|
|
1604
|
+
# it had one, before it counts as gone: twice the attached terminal's
|
|
1605
|
+
# whole reconnect ladder (TerminalUI::LocalLink#reconnect_delays), so
|
|
1606
|
+
# a reconnect gap (a dropped stream, a slow worker) never reads as a
|
|
1607
|
+
# terminal that left.
|
|
1608
|
+
SCRATCH_GONE_AFTER = 2 * BridgeClient::EventStream::REDISCOVER_DELAYS.sum
|
|
1609
|
+
|
|
1610
|
+
# Whether this is a `chi scratch` session's worker whose terminal is
|
|
1611
|
+
# gone: no stream open for SCRATCH_GONE_AFTER, after one was (a killed
|
|
1612
|
+
# TUI's closes as its writer reads the hang-up), or none came within
|
|
1613
|
+
# SCRATCH_JOIN_GRACE. Its terminal deletes it on every way out it sees;
|
|
1614
|
+
# this is for the ones it doesn't (kill -9, a closed terminal window).
|
|
1615
|
+
def scratch_unattended?
|
|
1616
|
+
return false unless @session&.scratch && @bridge
|
|
1617
|
+
|
|
1618
|
+
if @bridge.open_streams.positive?
|
|
1619
|
+
@unattended_since = nil
|
|
1620
|
+
return false
|
|
1621
|
+
end
|
|
1622
|
+
return monotonic_now - @started_at >= SCRATCH_JOIN_GRACE unless @bridge.streams_served.positive?
|
|
1623
|
+
|
|
1624
|
+
# Set by the first look (the scratch watch or the idle pass) that
|
|
1625
|
+
# finds no stream; a stream back resets it above.
|
|
1626
|
+
@unattended_since ||= monotonic_now
|
|
1627
|
+
monotonic_now - @unattended_since >= SCRATCH_GONE_AFTER
|
|
1628
|
+
end
|
|
1629
|
+
|
|
1630
|
+
def monotonic_now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1631
|
+
|
|
1632
|
+
# #leave_idle for a scratch worker its terminal left: checked again with
|
|
1633
|
+
# the event log held, then the intake closes. The session is deleted
|
|
1634
|
+
# once the lock is free (SessionManager.run_session_loop: :scratch_left).
|
|
1635
|
+
# @return [Boolean] false when a client came back since
|
|
1636
|
+
def leave_scratch
|
|
1637
|
+
stop_bridge_after do
|
|
1638
|
+
@engine.synchronize_events do
|
|
1639
|
+
# A turn still running is the scratch watch's to cancel first.
|
|
1640
|
+
next false unless scratch_unattended? && !@engine.turn_running?
|
|
1641
|
+
|
|
1642
|
+
@bridge&.close_intake
|
|
1643
|
+
@engine.stop_idle(join: false)
|
|
1644
|
+
Log.info(:worker, "scratch_left", served: @bridge.streams_served)
|
|
1645
|
+
true
|
|
1646
|
+
end
|
|
1647
|
+
end
|
|
1648
|
+
end
|
|
1649
|
+
|
|
1562
1650
|
# Check again with the event log held, which the Bridge holds while it
|
|
1563
1651
|
# queues a POST /turn, then close the Bridge's intake so no client can
|
|
1564
1652
|
# queue one after the check, and stop the idle jobs (reminder callback,
|
|
@@ -1694,6 +1782,9 @@ module Samagotchi
|
|
|
1694
1782
|
# @return [WorkerIdleExit::Hold, nil]
|
|
1695
1783
|
def exit_hold(client_id, kind:, stop:, ask:)
|
|
1696
1784
|
if kind == :restart
|
|
1785
|
+
# A scratch session's worker ends with its terminal: never restarted.
|
|
1786
|
+
return WorkerIdleExit::Hold.new(reason: :scratch) if @session.scratch
|
|
1787
|
+
|
|
1697
1788
|
hold = @idle_exit.hold_for_restart
|
|
1698
1789
|
return hold && WorkerIdleExit::Hold.new(reason: hold)
|
|
1699
1790
|
end
|
|
@@ -1792,14 +1883,20 @@ module Samagotchi
|
|
|
1792
1883
|
# A restart keeps even an empty session (a new worker takes it over at
|
|
1793
1884
|
# once) and leaves the recap to the conversation's next real pause.
|
|
1794
1885
|
def left(result)
|
|
1795
|
-
deleting = result == :exit_requested && @exit_deletes
|
|
1886
|
+
deleting = (result == :exit_requested && @exit_deletes) || result == :scratch_left
|
|
1887
|
+
# The attached context goes with this process: a change that landed after
|
|
1888
|
+
# the leaving pass looked at it has no later pass to note it, so note it
|
|
1889
|
+
# here (no wake turn: the worker is on its way out). Not into a session
|
|
1890
|
+
# /exit --delete deletes next: its save could land after the delete and
|
|
1891
|
+
# write the session back.
|
|
1892
|
+
absorb_context unless deleting
|
|
1796
1893
|
# The watch goes with this process: a task that finished since the
|
|
1797
1894
|
# last look (nothing held the exit for it: queue, a note-only task,
|
|
1798
1895
|
# /exit) is a plain note for the next turn, not lost. Not into a
|
|
1799
1896
|
# session /exit --delete deletes next: its save could land after the
|
|
1800
1897
|
# delete and write the session back.
|
|
1801
1898
|
@task_wakes.absorb unless deleting
|
|
1802
|
-
return result if result
|
|
1899
|
+
return result if %i[restart scratch_left].include?(result)
|
|
1803
1900
|
|
|
1804
1901
|
@discard = empty_session?
|
|
1805
1902
|
write_recap_on_leave unless @discard || deleting
|
|
@@ -23,9 +23,9 @@ module Samagotchi
|
|
|
23
23
|
# past START_LIMIT without one;
|
|
24
24
|
# - Leaving: the worker closed its Bridge and holds the lock for its last work;
|
|
25
25
|
# - Skew: a worker whose sidecar speaks a newer PROTOCOL than this chi;
|
|
26
|
-
# - Stale: a sidecar, but the lock is free (or a chi
|
|
26
|
+
# - Stale: a sidecar, but the lock is free (or a one-turn chi run holds it, which
|
|
27
27
|
# writes none): its worker died;
|
|
28
|
-
# - None: no worker and no sidecar, or a chi
|
|
28
|
+
# - None: no worker and no sidecar, or a one-turn chi run.
|
|
29
29
|
# A kernel releases a dead worker's lock, so a free lock is the proof of
|
|
30
30
|
# death a TCP probe never gave (a port can be reused by another session's
|
|
31
31
|
# Bridge).
|
|
@@ -80,7 +80,7 @@ module Samagotchi
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# No worker: no sidecar in the session folder, and no worker's lock (a
|
|
83
|
-
# chi
|
|
83
|
+
# one-turn chi run's, +owner+, is no worker's).
|
|
84
84
|
None = Data.define(:owner) do
|
|
85
85
|
def initialize(owner: nil) = super
|
|
86
86
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "working_set"
|
|
4
4
|
require_relative "working_set_steer"
|
|
5
5
|
require_relative "tools/memory"
|
|
6
|
+
require_relative "session"
|
|
6
7
|
|
|
7
8
|
module Samagotchi
|
|
8
9
|
# /working-set: with no arguments the session's working set (WorkingSet,
|
|
@@ -50,7 +51,7 @@ module Samagotchi
|
|
|
50
51
|
return [listing, [], :ok] if verb.nil?
|
|
51
52
|
raise ArgumentError, USAGE unless VERBS.include?(verb) && !words.empty?
|
|
52
53
|
|
|
53
|
-
session = @engine.session or raise ArgumentError,
|
|
54
|
+
session = @engine.session or raise ArgumentError, Session::NONE_YET
|
|
54
55
|
cwd = session.working_directory
|
|
55
56
|
strategy = @engine.working_set_strategy
|
|
56
57
|
messages = @engine.clone_messages(session.messages)
|
|
@@ -79,7 +80,7 @@ module Samagotchi
|
|
|
79
80
|
# The set under the strategy the next turn runs, and the pending ops.
|
|
80
81
|
def listing
|
|
81
82
|
session = @engine.session
|
|
82
|
-
return
|
|
83
|
+
return Session::NONE_YET unless session
|
|
83
84
|
|
|
84
85
|
messages = Array(session.messages)
|
|
85
86
|
items = WorkingSet.build(messages, cwd: session.working_directory, strategy: @engine.working_set_strategy,
|