Commit 8af28af

mo khan <mo@mokhan.ca>
2026-09-07 13:13:36
refactor: adapt WebTerminal to core's Output/Input split, fix stale Config API
WebTerminal now includes Elelem::Output and drops quiet?/gap/newline/ markdown, matching the split shipped in elelem core 0.11: think -> thinking, do -> doing, say(text, markdown:) -> say(text, as:). One WebTerminal instance serves as both output: and input: for Agent.new, since it's a single bidirectional SSE/HTTP channel. Also fixed Server.serve, which was still calling Providers.build / Plugins.setup! and Agent.new(terminal:) - APIs that predate this gem's already-declared elelem ~> 0.11 dependency. Now uses Config.build / Config.apply. Gemfile.lock refreshed to match (was still resolving 0.10). Claude-Session: https://claude.ai/code/session_01DUuj4amvRrPxDnHPHBkvz5
1 parent d7ba7b4
Changed files (4)
lib/elelem/server/web_terminal.rb
@@ -3,6 +3,8 @@
 module Elelem
   class Server
     class WebTerminal
+      include Output
+
       def initialize
         @subscribers = []
         @mutex = Mutex.new
@@ -21,26 +23,20 @@ module Elelem
         true
       end
 
-      def quiet?
-        false
-      end
-
       def ask(prompt)
         emit("prompt", prompt)
         @answers.pop
       end
 
-      def think(text)
+      def thinking(text)
         emit("thinking", text)
-        nil
       end
 
-      def say(text, markdown: false)
+      def say(text, as: nil)
         emit("content", text)
       end
 
-      def do(header)
-        gap
+      def doing(header)
         say header
       end
 
@@ -53,10 +49,6 @@ module Elelem
         emit("content", text)
       end
 
-      def markdown(text)
-        text
-      end
-
       def display_file(path, fallback: nil)
         content = File.read(path)
         raise ArgumentError unless content.valid_encoding?
@@ -74,12 +66,6 @@ module Elelem
         broadcast(type: "done")
       end
 
-      def waiting; end
-
-      def gap; end
-
-      def newline(n: 1); end
-
       private
 
       def emit(type, text)
lib/elelem/server.rb
@@ -10,9 +10,10 @@ require_relative "server/web_terminal"
 module Elelem
   class Server
     def self.serve(provider: "stub", port: 4567, toolbox: Toolbox.new)
-      client = Providers.build(provider)
-      agent = Agent.new(client, toolbox: toolbox, terminal: WebTerminal.new)
-      Plugins.setup!(agent)
+      client = Config.build(provider)
+      web_terminal = WebTerminal.new
+      agent = Agent.new(client, toolbox: toolbox, output: web_terminal, input: web_terminal)
+      Config.apply(agent)
       new(agent, port: port).start
     end
 
@@ -20,7 +21,7 @@ module Elelem
       @agent = agent
       @port = port
       @address = address
-      @terminal = agent.terminal
+      @terminal = agent.output
     end
 
     def start
spec/elelem/server/web_terminal_spec.rb
@@ -15,10 +15,6 @@ RSpec.describe Elelem::Server::WebTerminal do
     it { expect(subject).to be_interactive }
   end
 
-  describe "#quiet?" do
-    it { expect(subject).not_to be_quiet }
-  end
-
   describe "#say" do
     it "emits content" do
       subject.say("hello")
@@ -36,26 +32,19 @@ RSpec.describe Elelem::Server::WebTerminal do
       expect(drain).to eq([{ type: "content", text: "+ execute(...)" }])
     end
 
-    it "accepts the markdown keyword like Terminal#say" do
-      subject.say("hello", markdown: true)
+    it "accepts the as: keyword like Terminal#say" do
+      subject.say("hello", as: :markdown)
       expect(drain).to eq([{ type: "content", text: "hello" }])
     end
   end
 
-  describe "#think" do
-    it "emits thinking and returns nil so #print stays a no-op" do
-      expect(subject.think("pondering")).to be_nil
+  describe "#thinking" do
+    it "emits a thinking event" do
+      subject.thinking("pondering")
       expect(drain).to eq([{ type: "thinking", text: "pondering" }])
     end
   end
 
-  describe "#markdown" do
-    it "passes text through for client-side rendering" do
-      expect(subject.markdown("# hi")).to eq("# hi")
-      expect(drain).to be_empty
-    end
-  end
-
   describe "#display_file" do
     it "emits the file's contents" do
       Tempfile.create do |file|
@@ -89,11 +78,9 @@ RSpec.describe Elelem::Server::WebTerminal do
     end
   end
 
-  describe "terminal-only concerns" do
-    it "are no-ops" do
+  describe "#waiting" do
+    it "is a no-op" do
       subject.waiting
-      subject.gap
-      subject.newline
       expect(drain).to be_empty
     end
   end
Gemfile.lock
@@ -2,37 +2,29 @@ PATH
   remote: .
   specs:
     elelem-server (0.1.0)
-      elelem (~> 0.10)
+      elelem (~> 0.11)
       json (~> 2.21)
       webrick (~> 1.9)
 
 GEM
   remote: https://rubygems.org/
   specs:
-    base64 (0.3.0)
     bigdecimal (4.1.2)
-    date (3.5.1)
-    digest (3.2.1)
-    elelem (0.10.0)
-      base64 (~> 0.1)
-      date (~> 3.0)
-      digest (~> 3.0)
+    elelem (0.11.0)
       erb (~> 6.0)
-      fileutils (~> 1.0)
-      json (~> 2.0)
-      json_schemer (~> 2.0)
-      logger (~> 1.0)
-      net-hippie (~> 1.0)
-      open3 (~> 0.1)
-      optparse (~> 0.1)
-      pathname (~> 0.1)
-      reline (~> 0.6)
-      securerandom (~> 0.1)
+      fileutils (~> 1.8)
+      io-console (~> 0.9)
+      json (~> 2.21)
+      json_schemer (~> 2.5)
+      logger (~> 1.7)
+      open3 (~> 0.2)
+      optparse (~> 0.8)
+      pathname (~> 0.5)
+      reline (~> 0.7)
       shellwords (~> 0.2)
-      stringio (~> 3.0)
+      stringio (~> 3.2)
       tempfile (~> 0.3)
       uri (~> 1.0)
-      webrick (~> 1.9)
     erb (6.0.7)
     fileutils (1.8.0)
     hana (1.3.7)
@@ -44,33 +36,17 @@ GEM
       regexp_parser (~> 2.0)
       simpleidn (~> 0.2)
     logger (1.7.0)
-    monitor (0.2.0)
-    net-hippie (1.5.1)
-      base64 (~> 0.1)
-      json (~> 2.0)
-      logger (~> 1.0)
-      monitor (~> 0.1)
-      net-http (~> 0.1)
-      openssl (~> 4.0)
-      resolv (~> 0.1)
-      timeout (~> 0.1)
-    net-http (0.9.1)
-      uri (>= 0.11.1)
     open3 (0.2.1)
-    openssl (4.0.2)
     optparse (0.8.1)
     pathname (0.5.0)
     rake (13.4.2)
     regexp_parser (2.12.0)
     reline (0.7.0)
       io-console (~> 0.5)
-    resolv (0.7.2)
-    securerandom (0.4.1)
     shellwords (0.2.2)
     simpleidn (0.3.0)
     stringio (3.2.0)
     tempfile (0.3.1)
-    timeout (0.6.1)
     uri (1.1.1)
     webrick (1.9.2)
 
@@ -83,12 +59,9 @@ DEPENDENCIES
   rake (~> 13.0)
 
 CHECKSUMS
-  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
   bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
   bundler (4.0.20) sha256=7978a8ac648767f5e635bc522445b79e80a52b907a39a36c2d8085ed6bc762ae
-  date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
-  digest (3.2.1) sha256=ab3312b4e272d7d5dc41c564c86a25861a1f34ac5153374199a0b74861395947
-  elelem (0.10.0) sha256=de67f3a28351640da471e6e80e7a1d79e83e293cbc502a9e98c3c609cad2e237
+  elelem (0.11.0)
   elelem-server (0.1.0)
   erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
   fileutils (1.8.0) sha256=8c6b1df54e2540bdb2f39258f08af78853aa70bad52b4d394bbc6424593c6e02
@@ -97,23 +70,16 @@ CHECKSUMS
   json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
   json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
   logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
-  monitor (0.2.0) sha256=18698584f161ca6611d73130663990ca7dc439d9d88095226e683035d66f1f0f
-  net-hippie (1.5.1) sha256=76ecfc7d8df7866c3ed4f2e25fe963d87537451a1fb0d318b58eca161127fbcc
-  net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
   open3 (0.2.1) sha256=8e2d7d2113526351201438c1aa35c8139f0141c9e8913baa007c898973bf3952
-  openssl (4.0.2) sha256=1037ad2868ae58df9ad917891c0c0f9815a1172f6846d4bcdd508e4c2ee747c2
   optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a
   pathname (0.5.0) sha256=d5a331784f6e1f2fefb31c2ff0b8855aabfb661d807284ead9fa47b883d81623
   rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
   regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
   reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
-  resolv (0.7.2) sha256=626d044d975ab2daac759bf898416f1b51e2cb8dcd6727c2b5b5b28b97ead2e1
-  securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
   shellwords (0.2.2) sha256=b8695a791de2f71472de5abdc3f4332f6535a4177f55d8f99e7e44266cd32f94
   simpleidn (0.3.0) sha256=12ca730bed2f3db04d11e9bfd1bca3e11fb37f55b21eb2e9793fb5814bf54d03
   stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
   tempfile (0.3.1) sha256=0bb53ab646744e505eb3102147e22ae130a626a15563e882428c1ec973fed76a
-  timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
   uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
   webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131