Commit e9ac050

mo khan <mo@mokhan.ca>
2026-09-12 05:22:21
wip: elelem-driven fix for prompt/answer correlation (broken, see review)
Dogfooding checkpoint. elelem (llama provider, qwen3-coder-30b) attempted the prompt/id correlation fix for the /answer injection bug. Server-side change is directionally correct; JS was never wired to pass the real server-issued id through, so answers never match and every ask() would hang. Kept as a commit to diff against the follow-up fix.
1 parent 4ab731c
Changed files (2)
lib/elelem/server/web_terminal.rb
@@ -6,7 +6,8 @@ module Elelem
       def initialize
         @subscribers = []
         @mutex = Mutex.new
-        @answers = Queue.new
+        @prompts = {}
+        @prompt_counter = 0
       end
 
       def subscribe
@@ -22,8 +23,10 @@ module Elelem
       end
 
       def ask(prompt)
-        emit("prompt", prompt)
-        @answers.pop
+        id = @prompt_counter += 1
+        emit("prompt", prompt, id: id)
+        @prompts[id] = Queue.new
+        @prompts[id].pop
       end
 
       def thinking(text)
@@ -55,8 +58,11 @@ module Elelem
         say(fallback || path)
       end
 
-      def answer(text)
-        @answers << text
+      def answer(id, text)
+        if @prompts.key?(id)
+          @prompts[id] << text
+          @prompts.delete(id)
+        end
       end
 
       def done
@@ -65,11 +71,11 @@ module Elelem
 
       private
 
-      def emit(type, text)
+      def emit(type, text, id: nil)
         plain = text.to_s.gsub(/\e\[[0-9;]*[a-zA-Z]/, "")
         return if plain.strip.empty?
 
-        broadcast(type: type, text: plain)
+        broadcast(type: type, text: plain, id: id)
       end
 
       def broadcast(event)
lib/elelem/server.rb
@@ -80,7 +80,8 @@ module Elelem
     end
 
     def answer(req, res)
-      @terminal.answer(JSON.parse(req.body)["text"].to_s)
+      body = JSON.parse(req.body)
+      @terminal.answer(body["id"].to_i, body["text"].to_s)
       res.status = 204
     end
 
@@ -181,7 +182,7 @@ module Elelem
         const respond = (answer) => {
           el.querySelectorAll("button").forEach((b) => b.remove());
           el.insertAdjacentHTML("beforeend", "<div><em>" + answer + "</em></div>");
-          fetch("/answer", { method: "POST", body: JSON.stringify({ text: answer }) });
+          fetch("/answer", { method: "POST", body: JSON.stringify({ id: el.dataset.id, text: answer }) });
         };
         const allow = document.createElement("button");
         allow.className = "allow";
@@ -194,6 +195,8 @@ module Elelem
         el.append(allow, deny);
         log.appendChild(el);
         scroll(was);
+        // Store the ID for this prompt
+        el.dataset.id = Date.now() + Math.random();
       }
 
       new EventSource("/events").onmessage = (e) => {