Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/src/wasm/ask.rs

4.7 KiB, 1 run

created by r2519314175:973, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! The question card's edge — a thin binding to the JS driver `window.DaimondAsk`.
2//!
3//! WHY THIS MODULE EXISTS, WHICH IS NOT WHAT IT DOES. Of the seven things the owner types at a
4//! session every day, six could be typed at Daimond and one could not: a model had no way to put
5//! a decision to him with options he could tap. It could write a question in prose and stop,
6//! which is a question answered by TYPING — and a decision answered by typing is a decision put
7//! off. [`crate::tools::Tool::FileShow`]'s rule again, arriving at the most-used interaction in
8//! a working session rather than at a file: a thing a person does every day that the model has no
9//! way to reach is a thing the model will eventually deny is possible.
10//!
11//! **THE DRIVER IS HANDED A QUESTION AND HANDS BACK NOTHING BUT WHETHER IT DREW ONE.** It does
12//! not wait for the answer and there is no promise on this side holding a turn open. That is the
13//! whole difference between this and `parkConsent`, which parks a worker's consent question on the
14//! Pending panel and holds the `resolve` of a promise in memory: a promise does not survive a
15//! reload, so those tiles are marked expired when the page comes back and the tick is disabled.
16//! A question here is drawn from the tool call's own arguments, exactly as `renderSaid` draws a
17//! stored fold, so a reload redraws it with working buttons — because answering is only sending a
18//! message, and a message needs nothing left over from the turn that asked.
19//!
20//! What comes back is therefore one fact: was a card drawn. A page with no driver in it must
21//! fail loudly rather than let the model report a question nobody can see.
22
23use crate::llm::extract_json_bool;
24use crate::wasm::js_str;
25
26use oxedyne_fe2o3_core::prelude::*;
27
28use wasm_bindgen::prelude::wasm_bindgen;
29use wasm_bindgen::{JsCast, JsValue};
30use wasm_bindgen_futures::JsFuture;
31
32
33#[wasm_bindgen]
34extern "C" {
35
36 /// The driver object `www/js/daimond.js` installs at `window.DaimondAsk`.
37 #[wasm_bindgen(js_name = DaimondAsk)]
38 type Asker;
39
40 /// Draw one question in the conversation, and answer whether it was drawn.
41 #[wasm_bindgen(method)]
42 fn put(this: &Asker, payload: &str) -> js_sys::Promise;
43}
44
45
46/// Reach the driver object on `window`, or refuse in the model's language.
47fn asker() -> Outcome<Asker> {
48 let win = res!(web_sys::window()
49 .ok_or_else(|| err!("Putting a question needs a browser window."; System, Missing)));
50 let obj = res!(js_sys::Reflect::get(&win, &JsValue::from_str("DaimondAsk"))
51 .map_err(|e| err!("Reading window.DaimondAsk failed: {}.", js_str(&e); System, Missing)));
52 if obj.is_undefined() || obj.is_null() {
53 return Err(err!(
54 "This page cannot draw a question card, so nobody would see one. Ask in prose \
55 instead, giving the options, your recommendation and the reason for it.";
56 System, Missing));
57 }
58 Ok(obj.unchecked_into::<Asker>())
59}
60
61/// The `message` of a rejected JS `Error`, verbatim. A refusal from the driver is written for
62/// the model to read and act on, so nothing here rewords it.
63fn refusal(e: &JsValue) -> String {
64 match js_sys::Reflect::get(e, &JsValue::from_str("message")) {
65 Ok(m) => m.as_string().unwrap_or_else(|| js_str(e)),
66 Err(_) => js_str(e),
67 }
68}
69
70/// Put one question on the screen, and say nothing about the answer.
71///
72/// # Arguments
73/// * `payload` - The checked question as `ask_step` composed it, as JSON.
74pub async fn put(payload: &str) -> Outcome<()> {
75 let a = res!(asker());
76 let v = match JsFuture::from(a.put(payload)).await {
77 Ok(v) => v,
78 Err(e) => return Err(err!("{}", refusal(&e); IO, Invalid)),
79 };
80 let json = match v.as_string() {
81 Some(s) => s,
82 None => match js_sys::JSON::stringify(&v) {
83 Ok(s) => String::from(s),
84 Err(_) => return Err(err!(
85 "The page answered with something that cannot be read, so whether the user can \
86 see the question is unknown. Do not tell them you have asked."; Invalid, Data)),
87 },
88 };
89 // Absent means NOT drawn, which is the opposite of `doc::show`'s reading of a missing field
90 // and deliberately so: a show that predates its own `shown` field really did show something,
91 // while a page that says nothing here has said nothing about a card the model is about to
92 // claim is on screen.
93 if !extract_json_bool(&json, "drawn").unwrap_or(false) {
94 return Err(err!(
95 "The page did not draw the question, so there is nothing on screen to tap. Ask in \
96 prose instead, giving the options, your recommendation and the reason for it.";
97 IO, Invalid));
98 }
99 Ok(())
100}