samagotchi 0.91.0 → 0.93.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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -1
  3. data/README.md +5 -2
  4. data/bin/chi +8 -8
  5. data/docs/architecture.md +20 -16
  6. data/docs/bundle-authoring.md +2 -1
  7. data/docs/cli.md +5 -5
  8. data/docs/configuration.md +3 -1
  9. data/docs/context.md +5 -3
  10. data/docs/guardrails.md +4 -5
  11. data/docs/hooks.md +3 -3
  12. data/docs/internals/background-tasks.md +2 -2
  13. data/docs/internals/bundles.md +4 -4
  14. data/docs/internals/engine.md +27 -8
  15. data/docs/internals/tool-guardrails.md +10 -2
  16. data/docs/memory.md +6 -0
  17. data/docs/plugins.md +30 -13
  18. data/docs/releasing.md +3 -0
  19. data/docs/sessions.md +4 -4
  20. data/docs/testing.md +2 -1
  21. data/lib/samagotchi/bootstrap/probe.rb +2 -2
  22. data/lib/samagotchi/bridge.rb +8 -9
  23. data/lib/samagotchi/bundle_command/check.rb +43 -12
  24. data/lib/samagotchi/bundle_command/scaffold.rb +12 -0
  25. data/lib/samagotchi/bundle_command/test_run.rb +12 -0
  26. data/lib/samagotchi/bundle_command/try/command.rb +164 -0
  27. data/lib/samagotchi/bundle_command/try/render.rb +80 -0
  28. data/lib/samagotchi/bundle_command/try/run.rb +35 -17
  29. data/lib/samagotchi/bundle_command/try/sandbox.rb +113 -34
  30. data/lib/samagotchi/bundle_command/try/trace.rb +20 -26
  31. data/lib/samagotchi/bundle_command.rb +11 -213
  32. data/lib/samagotchi/bundle_session_settings.rb +82 -45
  33. data/lib/samagotchi/bundles/core/manifest.yml +2 -2
  34. data/lib/samagotchi/bundles/cost-cap/manifest.yml +6 -5
  35. data/lib/samagotchi/bundles/cost-cap/plugin.rb +57 -12
  36. data/lib/samagotchi/bundles/system/manifest.yml +1 -1
  37. data/lib/samagotchi/child_env.rb +50 -0
  38. data/lib/samagotchi/child_reports.rb +12 -14
  39. data/lib/samagotchi/client_id.rb +18 -0
  40. data/lib/samagotchi/config/restart_watch.rb +112 -0
  41. data/lib/samagotchi/config.rb +18 -59
  42. data/lib/samagotchi/engine.rb +26 -26
  43. data/lib/samagotchi/guardrails/data_readers.rb +34 -30
  44. data/lib/samagotchi/guardrails/parent_levels.rb +31 -0
  45. data/lib/samagotchi/guardrails/parent_policy.rb +9 -20
  46. data/lib/samagotchi/guardrails/shell_lex.rb +28 -7
  47. data/lib/samagotchi/hook_question.rb +21 -0
  48. data/lib/samagotchi/hook_routes.rb +6 -6
  49. data/lib/samagotchi/hooks/registry.rb +7 -5
  50. data/lib/samagotchi/host_registry.rb +1 -1
  51. data/lib/samagotchi/kernel_loop.rb +14 -3
  52. data/lib/samagotchi/llm/anthropic_messages.rb +5 -0
  53. data/lib/samagotchi/llm/chat_loop.rb +3 -1
  54. data/lib/samagotchi/llm/errors.rb +4 -4
  55. data/lib/samagotchi/memory_bundle/builder.rb +3 -2
  56. data/lib/samagotchi/memory_bundle/installed_bundle.rb +7 -2
  57. data/lib/samagotchi/memory_bundle/installer.rb +1 -0
  58. data/lib/samagotchi/memory_bundle/manifest.rb +42 -8
  59. data/lib/samagotchi/memory_bundle/provenance.rb +2 -1
  60. data/lib/samagotchi/parent_answers_command.rb +6 -6
  61. data/lib/samagotchi/parent_report.rb +12 -0
  62. data/lib/samagotchi/plugin/context.rb +6 -5
  63. data/lib/samagotchi/plugin/http_transport.rb +14 -3
  64. data/lib/samagotchi/plugin/sessions.rb +2 -3
  65. data/lib/samagotchi/question_desk.rb +12 -9
  66. data/lib/samagotchi/reply_wait/poll.rb +267 -0
  67. data/lib/samagotchi/reply_wait.rb +51 -190
  68. data/lib/samagotchi/sandbox_layout.rb +66 -0
  69. data/lib/samagotchi/send_command/parent_gate.rb +83 -0
  70. data/lib/samagotchi/send_command/wait_target.rb +55 -0
  71. data/lib/samagotchi/send_command.rb +34 -103
  72. data/lib/samagotchi/session.rb +10 -9
  73. data/lib/samagotchi/session_inbox.rb +31 -8
  74. data/lib/samagotchi/session_manager.rb +60 -35
  75. data/lib/samagotchi/session_metrics.rb +22 -3
  76. data/lib/samagotchi/sessions_command.rb +4 -4
  77. data/lib/samagotchi/task_watch.rb +64 -21
  78. data/lib/samagotchi/terminal_ui/attached_loop.rb +3 -3
  79. data/lib/samagotchi/terminal_ui/formatting.rb +8 -5
  80. data/lib/samagotchi/testing/bundle.rb +33 -20
  81. data/lib/samagotchi/testing/host.rb +2 -8
  82. data/lib/samagotchi/testing/install.rb +26 -17
  83. data/lib/samagotchi/testing/recorder.rb +6 -9
  84. data/lib/samagotchi/testing/sandbox.rb +45 -37
  85. data/lib/samagotchi/testing/waiting.rb +5 -7
  86. data/lib/samagotchi/tool_response.rb +28 -6
  87. data/lib/samagotchi/tools/delegate.rb +10 -10
  88. data/lib/samagotchi/tools/delegate_cursor.rb +32 -8
  89. data/lib/samagotchi/tools/delegate_relay.rb +31 -16
  90. data/lib/samagotchi/tools/delegate_wait.rb +4 -4
  91. data/lib/samagotchi/tools/task_runtime.rb +2 -2
  92. data/lib/samagotchi/turn_checkpoint.rb +23 -10
  93. data/lib/samagotchi/version.rb +1 -1
  94. data/lib/samagotchi/web/app.rb +46 -63
  95. data/lib/samagotchi/web/public/app.js +130 -554
  96. data/lib/samagotchi/web/public/cards_place.js +4 -1
  97. data/lib/samagotchi/web/public/images.js +6 -0
  98. data/lib/samagotchi/web/public/index.html +1 -1
  99. data/lib/samagotchi/web/public/model_pick.js +10 -0
  100. data/lib/samagotchi/web/public/send_turn.js +173 -0
  101. data/lib/samagotchi/web/public/session_actions.js +203 -0
  102. data/lib/samagotchi/web/public/start_page.js +236 -0
  103. data/lib/samagotchi/web/public/toasts.js +17 -9
  104. data/lib/samagotchi/web/public/unqueue.js +20 -4
  105. data/lib/samagotchi/worker.rb +77 -185
  106. data/lib/samagotchi/worker_idle_exit.rb +20 -5
  107. data/lib/samagotchi/worker_inbound.rb +5 -5
  108. data/lib/samagotchi/worker_task_wakes.rb +160 -0
  109. data/lib/samagotchi/worker_wakes.rb +0 -3
  110. metadata +15 -1
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ require_relative "child_reports"
6
+ require_relative "kernel_loop"
7
+ require_relative "log"
8
+ require_relative "task_finish_note"
9
+ require_relative "task_watch"
10
+ require_relative "tools/task_runtime"
11
+ require_relative "worker_inbound"
12
+ require_relative "worker_wakes"
13
+
14
+ module Samagotchi
15
+ # A worker's finish wake (docs/internals/background-tasks.md, Finish
16
+ # wake): its TaskWatch, fed by the Engine's raw events (#observe), and
17
+ # the TASK FINISHED notes for the tasks that finished unseen. #take
18
+ # writes them and hands back the wake turn to run, which the Worker runs
19
+ # (Worker#run_wake); #absorb gives them to a prompt's turn as plain notes.
20
+ # On the loop's thread, like WorkerInbound.
21
+ class WorkerTaskWakes
22
+ # @return [TaskWatch]
23
+ attr_reader :watch
24
+
25
+ # @param wakes [WorkerWakes] the wake budget a task's wake asks
26
+ # @param awaiting_continue [#call] whether a continue offer waits
27
+ # @param idle [#call] nothing queued and no exit asked for: a finish
28
+ # may start a turn
29
+ # @param save [#call] saves the session (a failure is the callee's to log)
30
+ def initialize(engine:, session:, wakes:, awaiting_continue:, idle:, save:)
31
+ @engine = engine
32
+ @session = session
33
+ @wakes = wakes
34
+ @awaiting_continue = awaiting_continue
35
+ @idle = idle
36
+ @save = save
37
+ @watch = TaskWatch.new
38
+ end
39
+
40
+ # The Engine's raw events feed the task watch (session.task_wakes not
41
+ # off): a result that started a task or showed its end, a turn's start,
42
+ # a user's Stop of it (M5).
43
+ def observe(event)
44
+ case event[:type]
45
+ when :tool_call_completed then @watch.observe(event) unless TaskWatch.mode == "off"
46
+ when :turn_started then @watch.turn_started
47
+ when :turn_canceled then @watch.turn_canceled(event[:cancellation_reason])
48
+ end
49
+ end
50
+
51
+ # Whether a finished task would start a wake turn now: the Engine skips
52
+ # the turn-end warm-up then (next_turn_waiting).
53
+ def due?
54
+ TaskWatch.mode == "wake" && @watch.any? && @wakes.task_open?(awaiting_continue: @awaiting_continue.call) &&
55
+ @watch.finished?
56
+ end
57
+
58
+ # The background tasks this session started that finished unseen
59
+ # (TaskWatch#take_finished), each as a TASK FINISHED note
60
+ # (TaskFinishNote): the first one not note-only starts a wake turn from
61
+ # task:<id> when session.task_wakes is wake, nothing is queued, no exit
62
+ # was asked for and the wake budget is open (WorkerWakes#task_open?);
63
+ # the others, and every one otherwise, are plain notes for the next
64
+ # turn (M2). A spent budget is announced once.
65
+ # @return [WorkerInbound::TaskWake, nil] the wake turn to run now
66
+ def take
67
+ return nil if @wakes.in_grace? || !@watch.any?
68
+
69
+ finished = take_finished
70
+ return nil if finished.empty?
71
+
72
+ waking = finished.find { |entry, _| !entry.note_only } if wake_open?
73
+ task_wake = note_finished(finished, waking: waking)
74
+ return nil if task_wake == :failed
75
+
76
+ @save.call
77
+ return task_wake if task_wake
78
+
79
+ budget_notice(finished.count { |entry, _| !entry.note_only }) if TaskWatch.mode == "wake" && @wakes.spent?
80
+ nil
81
+ end
82
+
83
+ # Before a prompt's turn: the finished tasks join it as plain notes.
84
+ def absorb
85
+ return unless @watch.any?
86
+
87
+ finished = take_finished
88
+ return if finished.empty?
89
+ return if note_finished(finished) == :failed
90
+
91
+ @save.call
92
+ end
93
+
94
+ private
95
+
96
+ # Each finished task's note into the conversation: +waking+'s as the
97
+ # wake turn's start, the others plain. A note that can't be written (a
98
+ # log that can't be read, say) puts it and the ones after it back in
99
+ # the watch for the next look.
100
+ # @return [WorkerInbound::TaskWake, nil, :failed]
101
+ def note_finished(finished, waking: nil)
102
+ written = []
103
+ (finished - [waking]).each do |entry, record|
104
+ add_note(entry, record)
105
+ written << entry
106
+ end
107
+ waking && add_wake_note(*waking)
108
+ rescue StandardError => e
109
+ Log.warn(:worker, "task_note_failed", error: e.class.name, msg: e.message)
110
+ @watch.put_back(finished.map(&:first) - written)
111
+ :failed
112
+ end
113
+
114
+ # Only the tasks the conversation still holds (a !rollback, a restored
115
+ # turn may have dropped the result that started one). A failure to
116
+ # read them is logged and counted (TaskWatch#look_failed: past
117
+ # TaskWatch::MAX_FAILURES they no longer hold the worker up): they wait
118
+ # for the next look.
119
+ def take_finished
120
+ @watch.take_finished(keep: Tools::TaskRuntime.created_ids_in(@engine.messages_checkpoint))
121
+ rescue StandardError => e
122
+ Log.warn(:worker, "task_watch_failed", error: e.class.name, msg: e.message)
123
+ @watch.look_failed
124
+ []
125
+ end
126
+
127
+ def wake_open?
128
+ TaskWatch.mode == "wake" && @idle.call && @wakes.task_open?(awaiting_continue: @awaiting_continue.call)
129
+ end
130
+
131
+ def note(entry, record, wake:, turn_id: nil)
132
+ TaskFinishNote.note(record, limit_sec: entry.limit_sec, wake: wake, turn_id: turn_id,
133
+ cap: KernelLoop.resolve_output_char_cap(nil))
134
+ end
135
+
136
+ def add_note(entry, record)
137
+ @engine.add_context_note(@session, note(entry, record, wake: false))
138
+ end
139
+
140
+ # The wake note, in the conversation as the wake turn's first message
141
+ # (before TurnFlow#before_prompt_turn, so a restore keeps it).
142
+ # @return [WorkerInbound::TaskWake]
143
+ def add_wake_note(entry, record)
144
+ turn_id = SecureRandom.uuid
145
+ @engine.add_context_note(@session, note(entry, record, wake: true, turn_id: turn_id))
146
+ WorkerInbound::TaskWake.new(id: entry.id, turn_id: turn_id, plain_note: note(entry, record, wake: false))
147
+ end
148
+
149
+ # The wake budget is spent: the UIs hear once that finished tasks wait
150
+ # as notes (one notice per stretch, shared with the other kinds).
151
+ def budget_notice(count)
152
+ return unless count.positive? && @wakes.notice_budget!
153
+
154
+ @engine.announce(type: :hook_notice, hook: "task", level: :info, between_turns: true,
155
+ text: "#{count} finished background task#{"s" if count != 1} noted; the model reads " \
156
+ "#{count == 1 ? "it" : "them"} with your next message " \
157
+ "(#{@wakes.max} wake turn#{"s" if @wakes.max != 1} in a row, #{ChildRing::MAX_WAKES_KEY})")
158
+ end
159
+ end
160
+ end
@@ -117,9 +117,6 @@ module Samagotchi
117
117
  !@paused && !awaiting_continue && !in_grace? && @in_a_row < max
118
118
  end
119
119
 
120
- # Whether wakes are paused (a failed wake turn) until a human's input.
121
- def paused? = @paused
122
-
123
120
  # A wake turn starts; +peer+: for a peer's answer, which counts toward
124
121
  # both budgets. @return [Integer] the wake turns in a row now
125
122
  def count!(peer: false)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samagotchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.91.0
4
+ version: 0.93.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dedov
@@ -204,6 +204,8 @@ files:
204
204
  - lib/samagotchi/bundle_command/check.rb
205
205
  - lib/samagotchi/bundle_command/scaffold.rb
206
206
  - lib/samagotchi/bundle_command/test_run.rb
207
+ - lib/samagotchi/bundle_command/try/command.rb
208
+ - lib/samagotchi/bundle_command/try/render.rb
207
209
  - lib/samagotchi/bundle_command/try/run.rb
208
210
  - lib/samagotchi/bundle_command/try/sandbox.rb
209
211
  - lib/samagotchi/bundle_command/try/trace.rb
@@ -263,6 +265,7 @@ files:
263
265
  - lib/samagotchi/bundles/system/self_map.md
264
266
  - lib/samagotchi/cache_confirm.rb
265
267
  - lib/samagotchi/cancellation_controller.rb
268
+ - lib/samagotchi/child_env.rb
266
269
  - lib/samagotchi/child_move.rb
267
270
  - lib/samagotchi/child_reports.rb
268
271
  - lib/samagotchi/children_status.rb
@@ -276,6 +279,7 @@ files:
276
279
  - lib/samagotchi/commands/registry.rb
277
280
  - lib/samagotchi/composer_route.rb
278
281
  - lib/samagotchi/config.rb
282
+ - lib/samagotchi/config/restart_watch.rb
279
283
  - lib/samagotchi/config_text_edit.rb
280
284
  - lib/samagotchi/context_absorber.rb
281
285
  - lib/samagotchi/context_command.rb
@@ -331,6 +335,7 @@ files:
331
335
  - lib/samagotchi/guardrails/model_size.rb
332
336
  - lib/samagotchi/guardrails/outside.rb
333
337
  - lib/samagotchi/guardrails/parent_approvals.rb
338
+ - lib/samagotchi/guardrails/parent_levels.rb
334
339
  - lib/samagotchi/guardrails/parent_policy.rb
335
340
  - lib/samagotchi/guardrails/protected_paths.rb
336
341
  - lib/samagotchi/guardrails/read_only_shell.rb
@@ -343,6 +348,7 @@ files:
343
348
  - lib/samagotchi/guardrails/shell_paths.rb
344
349
  - lib/samagotchi/guardrails/targets.rb
345
350
  - lib/samagotchi/guardrails/verdict.rb
351
+ - lib/samagotchi/hook_question.rb
346
352
  - lib/samagotchi/hook_routes.rb
347
353
  - lib/samagotchi/hooks.rb
348
354
  - lib/samagotchi/hooks/bundle_loader.rb
@@ -490,10 +496,14 @@ files:
490
496
  - lib/samagotchi/reminder_queue.rb
491
497
  - lib/samagotchi/reminder_store.rb
492
498
  - lib/samagotchi/reply_wait.rb
499
+ - lib/samagotchi/reply_wait/poll.rb
493
500
  - lib/samagotchi/row_refs.rb
494
501
  - lib/samagotchi/sampling_settings.rb
502
+ - lib/samagotchi/sandbox_layout.rb
495
503
  - lib/samagotchi/self_report.rb
496
504
  - lib/samagotchi/send_command.rb
505
+ - lib/samagotchi/send_command/parent_gate.rb
506
+ - lib/samagotchi/send_command/wait_target.rb
497
507
  - lib/samagotchi/served_model.rb
498
508
  - lib/samagotchi/session.rb
499
509
  - lib/samagotchi/session_archive_command.rb
@@ -700,7 +710,9 @@ files:
700
710
  - lib/samagotchi/web/public/route.js
701
711
  - lib/samagotchi/web/public/scope.js
702
712
  - lib/samagotchi/web/public/scroll.js
713
+ - lib/samagotchi/web/public/send_turn.js
703
714
  - lib/samagotchi/web/public/sentences.js
715
+ - lib/samagotchi/web/public/session_actions.js
704
716
  - lib/samagotchi/web/public/session_state.js
705
717
  - lib/samagotchi/web/public/session_title.js
706
718
  - lib/samagotchi/web/public/sessions_list.js
@@ -710,6 +722,7 @@ files:
710
722
  - lib/samagotchi/web/public/stage_slots.js
711
723
  - lib/samagotchi/web/public/stage_view.js
712
724
  - lib/samagotchi/web/public/stage_watch.js
725
+ - lib/samagotchi/web/public/start_page.js
713
726
  - lib/samagotchi/web/public/storage.js
714
727
  - lib/samagotchi/web/public/stream_handlers.js
715
728
  - lib/samagotchi/web/public/strip.js
@@ -743,6 +756,7 @@ files:
743
756
  - lib/samagotchi/worker_inbound.rb
744
757
  - lib/samagotchi/worker_sidecar.rb
745
758
  - lib/samagotchi/worker_signals.rb
759
+ - lib/samagotchi/worker_task_wakes.rb
746
760
  - lib/samagotchi/worker_wakes.rb
747
761
  - lib/samagotchi/yaml_lines.rb
748
762
  homepage: https://github.com/dm1try/samagotchi