samagotchi 0.51.0 → 0.52.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 +46 -1
- data/README.md +1 -1
- data/bin/chi +11 -3
- data/docs/architecture.md +1 -1
- data/docs/cli.md +23 -8
- data/docs/configuration.md +59 -19
- data/docs/memory.md +11 -0
- data/docs/plugins.md +2 -1
- data/docs/sessions.md +5 -1
- data/docs/testing.md +6 -1
- data/lib/samagotchi/bootstrap/config_writer.rb +6 -37
- data/lib/samagotchi/bridge/ring_buffer.rb +0 -5
- data/lib/samagotchi/bridge.rb +10 -13
- data/lib/samagotchi/broadcast/recipients.rb +1 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/children_status.rb +1 -1
- data/lib/samagotchi/commands/registry.rb +2 -1
- data/lib/samagotchi/config.rb +50 -182
- data/lib/samagotchi/config_text_edit.rb +8 -32
- data/lib/samagotchi/context_sources.rb +0 -7
- data/lib/samagotchi/context_window.rb +2 -2
- data/lib/samagotchi/continue_offer.rb +5 -4
- data/lib/samagotchi/cut_policy.rb +0 -1
- data/lib/samagotchi/delivery.rb +23 -0
- data/lib/samagotchi/engine.rb +48 -17
- data/lib/samagotchi/engine_peers.rb +1 -0
- data/lib/samagotchi/host_config.rb +183 -0
- data/lib/samagotchi/host_registry.rb +5 -10
- data/lib/samagotchi/last_turn.rb +48 -0
- data/lib/samagotchi/live_versions.rb +0 -6
- data/lib/samagotchi/llm/native_tool_normalizer.rb +0 -5
- data/lib/samagotchi/llm_context_override.rb +3 -22
- data/lib/samagotchi/llm_context_strategy.rb +50 -16
- data/lib/samagotchi/memory_bundle/placeholder.rb +0 -5
- data/lib/samagotchi/model_profile.rb +4 -4
- data/lib/samagotchi/model_settings.rb +46 -0
- data/lib/samagotchi/plugin/context.rb +3 -3
- data/lib/samagotchi/plugin/sessions.rb +2 -2
- data/lib/samagotchi/queued_command.rb +35 -0
- data/lib/samagotchi/reminder_store.rb +0 -27
- data/lib/samagotchi/reply_wait.rb +11 -13
- data/lib/samagotchi/sampling_settings.rb +4 -4
- data/lib/samagotchi/self_report.rb +8 -5
- data/lib/samagotchi/send_command.rb +37 -19
- data/lib/samagotchi/session.rb +45 -24
- data/lib/samagotchi/session_commands.rb +36 -7
- data/lib/samagotchi/session_inbox.rb +18 -9
- data/lib/samagotchi/session_manager.rb +8 -7
- data/lib/samagotchi/session_setup.rb +30 -0
- data/lib/samagotchi/steer_cut.rb +9 -2
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +10 -4
- data/lib/samagotchi/terminal_ui/attached_loop.rb +1 -1
- data/lib/samagotchi/terminal_ui/formatting.rb +21 -3
- data/lib/samagotchi/terminal_ui.rb +8 -3
- data/lib/samagotchi/thinking.rb +67 -7
- data/lib/samagotchi/thinking_command.rb +72 -0
- data/lib/samagotchi/tools/builtins.rb +7 -2
- data/lib/samagotchi/tools/peers.rb +3 -2
- data/lib/samagotchi/tools/task_runtime.rb +1 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +4 -3
- data/lib/samagotchi/web/app.rb +54 -6
- data/lib/samagotchi/web/public/app.js +133 -20
- data/lib/samagotchi/web/public/data.js +8 -4
- data/lib/samagotchi/web/public/format.js +8 -4
- data/lib/samagotchi/web/public/index.html +8 -5
- data/lib/samagotchi/web/public/kept_commands.js +52 -0
- data/lib/samagotchi/web/public/model_pick.js +3 -1
- data/lib/samagotchi/web/public/thinking_chip.js +169 -0
- data/lib/samagotchi/web/public/timing.js +15 -0
- data/lib/samagotchi/web/public/turn_events.js +23 -0
- data/lib/samagotchi/web/public/turn_view.js +328 -310
- data/lib/samagotchi/web/public/vendor/lit-html.js +8 -0
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +43 -42
- data/lib/samagotchi/worker_inbound.rb +5 -5
- data/lib/samagotchi/worker_wakes.rb +0 -2
- data/lib/samagotchi/yaml_lines.rb +72 -0
- metadata +11 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// lit-html 3.3.3 (npm tarball lit-html-3.3.3.tgz, lit-html.js: the minified production
|
|
2
|
+
// build, unmodified but for this line and the dropped sourceMappingURL). BSD-3-Clause.
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2017 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
*/
|
|
8
|
+
const t=globalThis,i=t=>t,s=t.trustedTypes,e=s?s.createPolicy("lit-html",{createHTML:t=>t}):void 0,h="$lit$",o=`lit$${Math.random().toFixed(9).slice(2)}$`,n="?"+o,r=`<${n}>`,l=document,c=()=>l.createComment(""),a=t=>null===t||"object"!=typeof t&&"function"!=typeof t,u=Array.isArray,d=t=>u(t)||"function"==typeof t?.[Symbol.iterator],f="[ \t\n\f\r]",v=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${f}(?:([^\\s"'>=/]+)(${f}*=${f}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),g=/'/g,$=/"/g,y=/^(?:script|style|textarea|title)$/i,x=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),b=x(1),w=x(2),T=x(3),E=Symbol.for("lit-noChange"),A=Symbol.for("lit-nothing"),C=new WeakMap,P=l.createTreeWalker(l,129);function V(t,i){if(!u(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==e?e.createHTML(i):i}const N=(t,i)=>{const s=t.length-1,e=[];let n,l=2===i?"<svg>":3===i?"<math>":"",c=v;for(let i=0;i<s;i++){const s=t[i];let a,u,d=-1,f=0;for(;f<s.length&&(c.lastIndex=f,u=c.exec(s),null!==u);)f=c.lastIndex,c===v?"!--"===u[1]?c=_:void 0!==u[1]?c=m:void 0!==u[2]?(y.test(u[2])&&(n=RegExp("</"+u[2],"g")),c=p):void 0!==u[3]&&(c=p):c===p?">"===u[0]?(c=n??v,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?p:'"'===u[3]?$:g):c===$||c===g?c=p:c===_||c===m?c=v:(c=p,n=void 0);const x=c===p&&t[i+1].startsWith("/>")?" ":"";l+=c===v?s+r:d>=0?(e.push(a),s.slice(0,d)+h+s.slice(d)+o+x):s+o+(-2===d?i:x)}return[V(t,l+(t[s]||"<?>")+(2===i?"</svg>":3===i?"</math>":"")),e]};class S{constructor({strings:t,_$litType$:i},e){let r;this.parts=[];let l=0,a=0;const u=t.length-1,d=this.parts,[f,v]=N(t,i);if(this.el=S.createElement(f,e),P.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(r=P.nextNode())&&d.length<u;){if(1===r.nodeType){if(r.hasAttributes())for(const t of r.getAttributeNames())if(t.endsWith(h)){const i=v[a++],s=r.getAttribute(t).split(o),e=/([.?@])?(.*)/.exec(i);d.push({type:1,index:l,name:e[2],strings:s,ctor:"."===e[1]?I:"?"===e[1]?L:"@"===e[1]?z:H}),r.removeAttribute(t)}else t.startsWith(o)&&(d.push({type:6,index:l}),r.removeAttribute(t));if(y.test(r.tagName)){const t=r.textContent.split(o),i=t.length-1;if(i>0){r.textContent=s?s.emptyScript:"";for(let s=0;s<i;s++)r.append(t[s],c()),P.nextNode(),d.push({type:2,index:++l});r.append(t[i],c())}}}else if(8===r.nodeType)if(r.data===n)d.push({type:2,index:l});else{let t=-1;for(;-1!==(t=r.data.indexOf(o,t+1));)d.push({type:7,index:l}),t+=o.length-1}l++}}static createElement(t,i){const s=l.createElement("template");return s.innerHTML=t,s}}function M(t,i,s=t,e){if(i===E)return i;let h=void 0!==e?s._$Co?.[e]:s._$Cl;const o=a(i)?void 0:i._$litDirective$;return h?.constructor!==o&&(h?._$AO?.(!1),void 0===o?h=void 0:(h=new o(t),h._$AT(t,s,e)),void 0!==e?(s._$Co??=[])[e]=h:s._$Cl=h),void 0!==h&&(i=M(t,h._$AS(t,i.values),h,e)),i}class R{constructor(t,i){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=i}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){const{el:{content:i},parts:s}=this._$AD,e=(t?.creationScope??l).importNode(i,!0);P.currentNode=e;let h=P.nextNode(),o=0,n=0,r=s[0];for(;void 0!==r;){if(o===r.index){let i;2===r.type?i=new k(h,h.nextSibling,this,t):1===r.type?i=new r.ctor(h,r.name,r.strings,this,t):6===r.type&&(i=new Z(h,this,t)),this._$AV.push(i),r=s[++n]}o!==r?.index&&(h=P.nextNode(),o++)}return P.currentNode=l,e}p(t){let i=0;for(const s of this._$AV)void 0!==s&&(void 0!==s.strings?(s._$AI(t,s,i),i+=s.strings.length-2):s._$AI(t[i])),i++}}class k{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(t,i,s,e){this.type=2,this._$AH=A,this._$AN=void 0,this._$AA=t,this._$AB=i,this._$AM=s,this.options=e,this._$Cv=e?.isConnected??!0}get parentNode(){let t=this._$AA.parentNode;const i=this._$AM;return void 0!==i&&11===t?.nodeType&&(t=i.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,i=this){t=M(this,t,i),a(t)?t===A||null==t||""===t?(this._$AH!==A&&this._$AR(),this._$AH=A):t!==this._$AH&&t!==E&&this._(t):void 0!==t._$litType$?this.$(t):void 0!==t.nodeType?this.T(t):d(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==A&&a(this._$AH)?this._$AA.nextSibling.data=t:this.T(l.createTextNode(t)),this._$AH=t}$(t){const{values:i,_$litType$:s}=t,e="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=S.createElement(V(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===e)this._$AH.p(i);else{const t=new R(e,this),s=t.u(this.options);t.p(i),this.T(s),this._$AH=t}}_$AC(t){let i=C.get(t.strings);return void 0===i&&C.set(t.strings,i=new S(t)),i}k(t){u(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,e=0;for(const h of t)e===i.length?i.push(s=new k(this.O(c()),this.O(c()),this,this.options)):s=i[e],s._$AI(h),e++;e<i.length&&(this._$AR(s&&s._$AB.nextSibling,e),i.length=e)}_$AR(t=this._$AA.nextSibling,s){for(this._$AP?.(!1,!0,s);t!==this._$AB;){const s=i(t).nextSibling;i(t).remove(),t=s}}setConnected(t){void 0===this._$AM&&(this._$Cv=t,this._$AP?.(t))}}class H{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(t,i,s,e,h){this.type=1,this._$AH=A,this._$AN=void 0,this.element=t,this.name=i,this._$AM=e,this.options=h,s.length>2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A}_$AI(t,i=this,s,e){const h=this.strings;let o=!1;if(void 0===h)t=M(this,t,i,0),o=!a(t)||t!==this._$AH&&t!==E,o&&(this._$AH=t);else{const e=t;let n,r;for(t=h[0],n=0;n<h.length-1;n++)r=M(this,e[s+n],i,n),r===E&&(r=this._$AH[n]),o||=!a(r)||r!==this._$AH[n],r===A?t=A:t!==A&&(t+=(r??"")+h[n+1]),this._$AH[n]=r}o&&!e&&this.j(t)}j(t){t===A?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"")}}class I extends H{constructor(){super(...arguments),this.type=3}j(t){this.element[this.name]=t===A?void 0:t}}class L extends H{constructor(){super(...arguments),this.type=4}j(t){this.element.toggleAttribute(this.name,!!t&&t!==A)}}class z extends H{constructor(t,i,s,e,h){super(t,i,s,e,h),this.type=5}_$AI(t,i=this){if((t=M(this,t,i,0)??A)===E)return;const s=this._$AH,e=t===A&&s!==A||t.capture!==s.capture||t.once!==s.once||t.passive!==s.passive,h=t!==A&&(s===A||e);e&&this.element.removeEventListener(this.name,this,s),h&&this.element.addEventListener(this.name,this,t),this._$AH=t}handleEvent(t){"function"==typeof this._$AH?this._$AH.call(this.options?.host??this.element,t):this._$AH.handleEvent(t)}}class Z{constructor(t,i,s){this.element=t,this.type=6,this._$AN=void 0,this._$AM=i,this.options=s}get _$AU(){return this._$AM._$AU}_$AI(t){M(this,t)}}const j={M:h,P:o,A:n,C:1,L:N,R,D:d,V:M,I:k,H,N:L,U:z,B:I,F:Z},B=t.litHtmlPolyfillSupport;B?.(S,k),(t.litHtmlVersions??=[]).push("3.3.3");const D=(t,i,s)=>{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new k(i.insertBefore(c(),t),t,void 0,s??{})}return h._$AI(t),h};export{j as _$LH,b as html,T as mathml,E as noChange,A as nothing,D as render,w as svg};
|
|
@@ -93,7 +93,7 @@ module Samagotchi
|
|
|
93
93
|
# chi REPL's (SessionManager.worker_live?'s rule: only a worker shares it).
|
|
94
94
|
pending_question: session.waiting_question(live: !!owner&.worker?),
|
|
95
95
|
pending_card: up ? Bridge::PendingCard.read(session_dir) : nil,
|
|
96
|
-
last_turn: session.last_turn
|
|
96
|
+
last_turn: session.last_turn&.to_file
|
|
97
97
|
}
|
|
98
98
|
end
|
|
99
99
|
|
data/lib/samagotchi/worker.rb
CHANGED
|
@@ -20,6 +20,7 @@ require_relative "llm/errors"
|
|
|
20
20
|
require_relative "continue_offer"
|
|
21
21
|
require_relative "iteration_limit"
|
|
22
22
|
require_relative "session_commands"
|
|
23
|
+
require_relative "queued_command"
|
|
23
24
|
require_relative "model_profile"
|
|
24
25
|
require_relative "child_reports"
|
|
25
26
|
require_relative "child_move"
|
|
@@ -175,9 +176,12 @@ module Samagotchi
|
|
|
175
176
|
# stream gets the session's history and status in its snapshot, not
|
|
176
177
|
# an empty session until the first turn.
|
|
177
178
|
@engine.session = @session
|
|
178
|
-
# Input already waiting in the inbox starts the next turn at once
|
|
179
|
-
# turn
|
|
180
|
-
|
|
179
|
+
# Input already waiting in the inbox starts the next turn at once, and
|
|
180
|
+
# a command queued mid-turn (/thinking, /model, /llm-context) changes
|
|
181
|
+
# what it starts with: no turn-end warm-up then.
|
|
182
|
+
@engine.next_turn_waiting = lambda {
|
|
183
|
+
!@command_queue.empty? || SessionInbox.find_new_input_files(@session_dir).any? || wake_due?
|
|
184
|
+
}
|
|
181
185
|
@turn_flow = TurnFlow.new(engine: @engine)
|
|
182
186
|
# Its question's answer is queued as the matching /continue (the
|
|
183
187
|
# Bridge exists by the time anyone answers).
|
|
@@ -592,9 +596,9 @@ module Samagotchi
|
|
|
592
596
|
end
|
|
593
597
|
|
|
594
598
|
def ready_command?(command)
|
|
595
|
-
return true if command
|
|
599
|
+
return true if command.after_file.nil?
|
|
596
600
|
|
|
597
|
-
SessionInbox.find_new_input_files(@session_dir).none? { |path| File.basename(path) <= command
|
|
601
|
+
SessionInbox.find_new_input_files(@session_dir).none? { |path| File.basename(path) <= command.after_file }
|
|
598
602
|
end
|
|
599
603
|
|
|
600
604
|
def drain_commands
|
|
@@ -619,11 +623,11 @@ module Samagotchi
|
|
|
619
623
|
|
|
620
624
|
@engine.synchronize_events do
|
|
621
625
|
drain_commands.each do |command|
|
|
622
|
-
during = mid_turn || command
|
|
626
|
+
during = mid_turn || command.after_seq.to_i < @turn_end_seq
|
|
623
627
|
case (during ? turn_answer(command, ended: !mid_turn) : :keep)
|
|
624
628
|
when :refuse then announce_command(command, status: "busy", output: busy_output(command), changed: [])
|
|
625
629
|
when :drop then drop_command(waiting_for_turn_end(command), "turn #{@turn_end_type == :turn_failed ? "failed" : "canceled"}: " \
|
|
626
|
-
"#{command
|
|
630
|
+
"#{command.line} not run; send it again")
|
|
627
631
|
else @command_queue << (during ? waiting_for_turn_end(command) : command)
|
|
628
632
|
end
|
|
629
633
|
end
|
|
@@ -634,8 +638,8 @@ module Samagotchi
|
|
|
634
638
|
# (D4, once it +ended+) or :keep. One queued idle just as the turn
|
|
635
639
|
# began (:loop) goes by its line's policy (a show form waits too).
|
|
636
640
|
def turn_answer(command, ended:)
|
|
637
|
-
policy = command
|
|
638
|
-
policy = @engine.command_registry.mid_turn(command
|
|
641
|
+
policy = command.mid_turn
|
|
642
|
+
policy = @engine.command_registry.mid_turn(command.line) if policy == :loop
|
|
639
643
|
return :refuse if policy == :refuse
|
|
640
644
|
return :drop if ended && policy == :queue && shell_command?(command) &&
|
|
641
645
|
%i[turn_canceled turn_failed].include?(@turn_end_type)
|
|
@@ -648,18 +652,16 @@ module Samagotchi
|
|
|
648
652
|
# marked as one queued mid-turn: its command_queued (waits) tells the
|
|
649
653
|
# UIs now, and its command_ran says queued.
|
|
650
654
|
def waiting_for_turn_end(command)
|
|
651
|
-
return command unless command
|
|
655
|
+
return command unless command.mid_turn == :loop && @engine.command_registry.mid_turn(command.line) == :queue
|
|
652
656
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
@engine.announce(event)
|
|
656
|
-
command.merge(mid_turn: :queue)
|
|
657
|
+
@engine.announce(type: :command_queued, **command.event_fields, waits: "turn_end")
|
|
658
|
+
command.with(mid_turn: :queue)
|
|
657
659
|
end
|
|
658
660
|
|
|
659
|
-
def shell_command?(command) = @engine.command_registry.lookup(command
|
|
661
|
+
def shell_command?(command) = @engine.command_registry.lookup(command.line)&.id == :shell
|
|
660
662
|
|
|
661
663
|
def busy_output(command)
|
|
662
|
-
@engine.command_registry.lookup(command
|
|
664
|
+
@engine.command_registry.lookup(command.line)&.id == :rollback ? ROLLBACK_BUSY_OUTPUT : BUSY_OUTPUT
|
|
663
665
|
end
|
|
664
666
|
|
|
665
667
|
# +command+ won't run: its sender (a UI's queued bubble) is told.
|
|
@@ -695,7 +697,7 @@ module Samagotchi
|
|
|
695
697
|
shown.each { |event| @engine.announce(event) }
|
|
696
698
|
end
|
|
697
699
|
save_or_log(:command) { save_session } unless Array(result.changed).empty?
|
|
698
|
-
user_input(command
|
|
700
|
+
user_input(command.client_id) if resolved
|
|
699
701
|
@continue_offer.after_command(result, resolved: resolved)
|
|
700
702
|
@continue_offer.run_continue_turn(command) if result.resume
|
|
701
703
|
end
|
|
@@ -704,12 +706,16 @@ module Samagotchi
|
|
|
704
706
|
# the cards and notices it showed, held: they follow its command_ran,
|
|
705
707
|
# as its output
|
|
706
708
|
def run_command_line(command)
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
709
|
+
@engine.holding_announcements { command_result(command) { @commands.run(command.line) } }
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
# The block's Result for +command+; one that raised, or found no
|
|
713
|
+
# command for the line, is an error Result saying so.
|
|
714
|
+
# @return [SessionCommands::Result]
|
|
715
|
+
def command_result(command)
|
|
716
|
+
yield || SessionCommands::Result.new(status: :error, output: "not a session command", changed: [])
|
|
717
|
+
rescue StandardError => e
|
|
718
|
+
SessionCommands::Result.new(status: :error, output: "#{command.line.split.first}: #{e.message}", changed: [])
|
|
713
719
|
end
|
|
714
720
|
|
|
715
721
|
# A session command from the Bridge, called with the event log held:
|
|
@@ -731,8 +737,8 @@ module Samagotchi
|
|
|
731
737
|
# :refuse while a turn runs, :loop idle
|
|
732
738
|
def on_command(command, after_file: nil)
|
|
733
739
|
registry = @engine.command_registry
|
|
734
|
-
policy = if @engine.turn_running? then registry.mid_turn(command
|
|
735
|
-
else registry.anytime?(command
|
|
740
|
+
policy = if @engine.turn_running? then registry.mid_turn(command.line)
|
|
741
|
+
else registry.anytime?(command.line) ? :anytime : :loop
|
|
736
742
|
end
|
|
737
743
|
if policy == :anytime
|
|
738
744
|
start_anytime_command(command)
|
|
@@ -741,7 +747,7 @@ module Samagotchi
|
|
|
741
747
|
|
|
742
748
|
after_file ||= newest_input_file if policy == :queue
|
|
743
749
|
# The count says which turn ends came before it.
|
|
744
|
-
@command_queue << command.
|
|
750
|
+
@command_queue << command.with(after_seq: @engine.event_count, mid_turn: policy, after_file: after_file)
|
|
745
751
|
@waker.wake
|
|
746
752
|
policy
|
|
747
753
|
end
|
|
@@ -761,16 +767,11 @@ module Samagotchi
|
|
|
761
767
|
# saved.
|
|
762
768
|
def start_anytime_command(command)
|
|
763
769
|
@engine.spawn_anytime do
|
|
764
|
-
result =
|
|
765
|
-
@engine.running_anytime { @commands.run(command[:line]) }
|
|
766
|
-
rescue StandardError => e
|
|
767
|
-
SessionCommands::Result.new(status: :error, output: "#{command[:line].split.first}: #{e.message}", changed: [])
|
|
768
|
-
end
|
|
769
|
-
result ||= SessionCommands::Result.new(status: :error, output: "not a session command", changed: [])
|
|
770
|
+
result = command_result(command) { @engine.running_anytime { @commands.run(command.line) } }
|
|
770
771
|
announce_command(command, status: result.status.to_s, output: result.output, changed: Array(result.changed),
|
|
771
772
|
anytime: true)
|
|
772
773
|
rescue StandardError => e
|
|
773
|
-
Log.warn(:worker, "anytime_command_failed", line: command
|
|
774
|
+
Log.warn(:worker, "anytime_command_failed", line: command.line, error: e.class.name, msg: e.message)
|
|
774
775
|
end
|
|
775
776
|
end
|
|
776
777
|
|
|
@@ -778,15 +779,15 @@ module Samagotchi
|
|
|
778
779
|
# its command_queued already
|
|
779
780
|
def announce_command(command, status:, output:, changed:, anytime: false)
|
|
780
781
|
text = output.to_s
|
|
781
|
-
event = { type: :command_ran,
|
|
782
|
-
|
|
783
|
-
model_name: @engine.effective_model_name }
|
|
782
|
+
event = { type: :command_ran, **command.event_fields, status: status, output: text[0, COMMAND_OUTPUT_LIMIT],
|
|
783
|
+
changed: changed.map(&:to_s), model_name: @engine.effective_model_name }
|
|
784
784
|
event[:anytime] = true if anytime
|
|
785
785
|
# Its line was shown at its command_queued (waits: turn_end).
|
|
786
|
-
event[:queued] = true if command
|
|
787
|
-
event[:card] = true if command[:card]
|
|
786
|
+
event[:queued] = true if command.waits_for_turn_end?
|
|
788
787
|
# /llm-context or /model: what the next turn runs under now (the web's chip).
|
|
789
788
|
event[:llm_context] = @engine.llm_context_summary if changed.intersect?(%i[llm_context model])
|
|
789
|
+
# /thinking or /model: the level the next turn runs at (the web's chip).
|
|
790
|
+
event[:thinking] = @engine.thinking_explained&.summary if changed.intersect?(%i[thinking model])
|
|
790
791
|
event[:output_truncated] = true if text.length > COMMAND_OUTPUT_LIMIT
|
|
791
792
|
@engine.announce(event)
|
|
792
793
|
end
|
|
@@ -867,7 +868,7 @@ module Samagotchi
|
|
|
867
868
|
def merges_lost?(error)
|
|
868
869
|
return false if error.nil? || error.is_a?(LLM::FailedTurn)
|
|
869
870
|
|
|
870
|
-
!KEPT_TURN_OUTCOMES.include?(@session.last_turn&.
|
|
871
|
+
!KEPT_TURN_OUTCOMES.include?(@session.last_turn&.outcome)
|
|
871
872
|
end
|
|
872
873
|
|
|
873
874
|
# The Engine's turn endings that keep the turn's conversation (#merges_lost?).
|
|
@@ -959,9 +960,9 @@ module Samagotchi
|
|
|
959
960
|
next unless claimed_file
|
|
960
961
|
|
|
961
962
|
begin
|
|
962
|
-
|
|
963
|
-
prompt = prompt.to_s.strip
|
|
964
|
-
prompt.empty? ? nil : [prompt, origin]
|
|
963
|
+
input = SessionInbox.read_input(claimed_file)
|
|
964
|
+
prompt = input.prompt.to_s.strip
|
|
965
|
+
prompt.empty? ? nil : [prompt, input.origin]
|
|
965
966
|
ensure
|
|
966
967
|
FileUtils.rm_f(claimed_file)
|
|
967
968
|
end
|
|
@@ -183,12 +183,12 @@ module Samagotchi
|
|
|
183
183
|
return unless claimed_file
|
|
184
184
|
|
|
185
185
|
begin
|
|
186
|
-
|
|
187
|
-
return if
|
|
188
|
-
return if
|
|
186
|
+
input = SessionInbox.read_input(claimed_file)
|
|
187
|
+
return if input.prompt.to_s.strip.empty?
|
|
188
|
+
return if input.images.empty? && queue_as_command(input.prompt, input.origin, File.basename(input_file))
|
|
189
189
|
|
|
190
|
-
yield Prompt.new(text:
|
|
191
|
-
delivery: delivery)
|
|
190
|
+
yield Prompt.new(text: input.prompt, origin: input.origin, no_interrupt: input.no_interrupt, images: input.images,
|
|
191
|
+
delivery: input.delivery)
|
|
192
192
|
ensure
|
|
193
193
|
FileUtils.rm_f(claimed_file)
|
|
194
194
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "psych"
|
|
5
|
+
|
|
6
|
+
module Samagotchi
|
|
7
|
+
# Line helpers for editing a YAML file's text in place, so the user's
|
|
8
|
+
# comments and layout stay (YAML.dump would drop them): where a top-level
|
|
9
|
+
# block ends, where a new line goes in it, and whether the text holds
|
|
10
|
+
# anything a line edit can't handle (anchors and aliases). Shared by
|
|
11
|
+
# ConfigTextEdit (one section.key) and Bootstrap::ConfigWriter (a hosts:
|
|
12
|
+
# entry).
|
|
13
|
+
module YAMLLines
|
|
14
|
+
DEFAULT_INDENT = 2
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# +text+ as its lines (each keeping its line end, the last one given
|
|
19
|
+
# one) and the line end it uses.
|
|
20
|
+
# @return [Array(Array<String>, String)]
|
|
21
|
+
def lines_of(text)
|
|
22
|
+
eol = text.include?("\r\n") ? "\r\n" : "\n"
|
|
23
|
+
lines = text.split(/(?<=\n)/)
|
|
24
|
+
lines[-1] = "#{lines[-1]}#{eol}" if lines.any? && !lines[-1].end_with?("\n")
|
|
25
|
+
[lines, eol]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The index after the block whose header is lines[+at+]: its indented
|
|
29
|
+
# lines, blank lines and comments.
|
|
30
|
+
def block_stop(lines, at)
|
|
31
|
+
stop = at + 1
|
|
32
|
+
stop += 1 while stop < lines.length && inside_block?(lines[stop])
|
|
33
|
+
stop
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Where a new child line of the block at +at+ (ending at +stop+) goes:
|
|
37
|
+
# its end, before trailing blank lines and comments shallower than a
|
|
38
|
+
# +child+ line.
|
|
39
|
+
def insert_at(lines, at, stop, child)
|
|
40
|
+
stop -= 1 while stop > at + 1 && (lines[stop - 1].strip.empty? || (comment?(lines[stop - 1]) && indent(lines[stop - 1]) < child))
|
|
41
|
+
stop
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def inside_block?(line)
|
|
45
|
+
return true if line.strip.empty?
|
|
46
|
+
return false if line.start_with?("---", "...")
|
|
47
|
+
|
|
48
|
+
line.start_with?(" ", "\t") || comment?(line)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def blank_or_comment?(line) = line.strip.empty? || comment?(line)
|
|
52
|
+
def comment?(line) = line.lstrip.start_with?("#")
|
|
53
|
+
def indent(line) = line[/\A */].length
|
|
54
|
+
|
|
55
|
+
def parse(text) = YAML.safe_load(text, permitted_classes: [], aliases: false)
|
|
56
|
+
|
|
57
|
+
# Anchors and aliases: chi's reader refuses aliases, and an edit can't
|
|
58
|
+
# know what an anchor shares. Text that doesn't parse counts too.
|
|
59
|
+
def anchors?(text)
|
|
60
|
+
stack = [Psych.parse_stream(text)]
|
|
61
|
+
until stack.empty?
|
|
62
|
+
node = stack.pop
|
|
63
|
+
return true if node.is_a?(Psych::Nodes::Alias) || (node.respond_to?(:anchor) && node.anchor)
|
|
64
|
+
|
|
65
|
+
stack.concat(Array(node.children)) if node.respond_to?(:children)
|
|
66
|
+
end
|
|
67
|
+
false
|
|
68
|
+
rescue Psych::Exception
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
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.
|
|
4
|
+
version: 0.52.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -288,6 +288,7 @@ files:
|
|
|
288
288
|
- lib/samagotchi/hooks/loader.rb
|
|
289
289
|
- lib/samagotchi/hooks/registry.rb
|
|
290
290
|
- lib/samagotchi/hooks/stream_watch.rb
|
|
291
|
+
- lib/samagotchi/host_config.rb
|
|
291
292
|
- lib/samagotchi/host_model.rb
|
|
292
293
|
- lib/samagotchi/host_registry.rb
|
|
293
294
|
- lib/samagotchi/idle_client.rb
|
|
@@ -300,6 +301,7 @@ files:
|
|
|
300
301
|
- lib/samagotchi/installed_versions.rb
|
|
301
302
|
- lib/samagotchi/iteration_limit.rb
|
|
302
303
|
- lib/samagotchi/kernel_loop.rb
|
|
304
|
+
- lib/samagotchi/last_turn.rb
|
|
303
305
|
- lib/samagotchi/launch_mode.rb
|
|
304
306
|
- lib/samagotchi/live_versions.rb
|
|
305
307
|
- lib/samagotchi/llm/api_key.rb
|
|
@@ -355,6 +357,7 @@ files:
|
|
|
355
357
|
- lib/samagotchi/model_price.rb
|
|
356
358
|
- lib/samagotchi/model_profile.rb
|
|
357
359
|
- lib/samagotchi/model_ref.rb
|
|
360
|
+
- lib/samagotchi/model_settings.rb
|
|
358
361
|
- lib/samagotchi/models_command.rb
|
|
359
362
|
- lib/samagotchi/muted_memories.rb
|
|
360
363
|
- lib/samagotchi/note_command.rb
|
|
@@ -382,6 +385,7 @@ files:
|
|
|
382
385
|
- lib/samagotchi/prompt_note.rb
|
|
383
386
|
- lib/samagotchi/prompt_warmup.rb
|
|
384
387
|
- lib/samagotchi/question_desk.rb
|
|
388
|
+
- lib/samagotchi/queued_command.rb
|
|
385
389
|
- lib/samagotchi/recap_setup.rb
|
|
386
390
|
- lib/samagotchi/recap_store.rb
|
|
387
391
|
- lib/samagotchi/relay.rb
|
|
@@ -405,6 +409,7 @@ files:
|
|
|
405
409
|
- lib/samagotchi/session_metrics.rb
|
|
406
410
|
- lib/samagotchi/session_observer.rb
|
|
407
411
|
- lib/samagotchi/session_retention.rb
|
|
412
|
+
- lib/samagotchi/session_setup.rb
|
|
408
413
|
- lib/samagotchi/sessions_command.rb
|
|
409
414
|
- lib/samagotchi/steer.rb
|
|
410
415
|
- lib/samagotchi/steer_cut.rb
|
|
@@ -434,6 +439,7 @@ files:
|
|
|
434
439
|
- lib/samagotchi/terminal_ui/version_lines.rb
|
|
435
440
|
- lib/samagotchi/text_diff.rb
|
|
436
441
|
- lib/samagotchi/thinking.rb
|
|
442
|
+
- lib/samagotchi/thinking_command.rb
|
|
437
443
|
- lib/samagotchi/thinking_tails.rb
|
|
438
444
|
- lib/samagotchi/thinking_ticker.rb
|
|
439
445
|
- lib/samagotchi/thought_stream_splitter.rb
|
|
@@ -519,6 +525,7 @@ files:
|
|
|
519
525
|
- lib/samagotchi/web/public/images.js
|
|
520
526
|
- lib/samagotchi/web/public/index.html
|
|
521
527
|
- lib/samagotchi/web/public/init_row.js
|
|
528
|
+
- lib/samagotchi/web/public/kept_commands.js
|
|
522
529
|
- lib/samagotchi/web/public/live_timing.js
|
|
523
530
|
- lib/samagotchi/web/public/live_turn.js
|
|
524
531
|
- lib/samagotchi/web/public/llm_context_chip.js
|
|
@@ -543,6 +550,7 @@ files:
|
|
|
543
550
|
- lib/samagotchi/web/public/strip.js
|
|
544
551
|
- lib/samagotchi/web/public/tally.js
|
|
545
552
|
- lib/samagotchi/web/public/task_stop.js
|
|
553
|
+
- lib/samagotchi/web/public/thinking_chip.js
|
|
546
554
|
- lib/samagotchi/web/public/thinking_ticker.js
|
|
547
555
|
- lib/samagotchi/web/public/timing.js
|
|
548
556
|
- lib/samagotchi/web/public/turn_events.js
|
|
@@ -550,6 +558,7 @@ files:
|
|
|
550
558
|
- lib/samagotchi/web/public/turn_model.js
|
|
551
559
|
- lib/samagotchi/web/public/turn_view.js
|
|
552
560
|
- lib/samagotchi/web/public/update_notice.js
|
|
561
|
+
- lib/samagotchi/web/public/vendor/lit-html.js
|
|
553
562
|
- lib/samagotchi/web/qr.rb
|
|
554
563
|
- lib/samagotchi/web/server.rb
|
|
555
564
|
- lib/samagotchi/web/session_hub.rb
|
|
@@ -560,6 +569,7 @@ files:
|
|
|
560
569
|
- lib/samagotchi/worker_inbound.rb
|
|
561
570
|
- lib/samagotchi/worker_sidecar.rb
|
|
562
571
|
- lib/samagotchi/worker_wakes.rb
|
|
572
|
+
- lib/samagotchi/yaml_lines.rb
|
|
563
573
|
homepage: https://github.com/dm1try/samagotchi
|
|
564
574
|
licenses:
|
|
565
575
|
- MIT
|