A picture she showed Whiskers: one photo, or one frame of a video.
Where a saved picture lives, so the parents' log can point at it.
24impl PictureId {
A bare file name of the kind pictures are given (0000000000000000-0000.jpg): no path,
no odd characters, a picture extension. Names arrive from other devices, so they are checked.
27 pub fn is_safe(&self) -> bool { 28 let n = &self.0; 29 n.len() <= 64 30 && n.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.')) 31 && !n.starts_with('.') 32 && !n.contains("..") 33 && n.rsplit('.').next().is_some_and(|e| matches!(e, "jpg" | "png" | "webp" | "gif" | "img")) 34 } 35}
What she gave Whiskers in one go: her words and anything she showed it.
Only the turn being answered carries pictures; history keeps words.
Which way a message is travelling past the guard.
Why a message was stopped, as far as what happens next is concerned.
Not for a child this young; the cat steers somewhere else.
81 OffLimits,
She may be hurt, scared or unsafe. The cat sends her to a grown-up and the parents' view puts it first.
How a turn ended.
Anything else ends in a fixed, pre-written line, never in silence and never in error text.
The guard refused what the child said; the model never saw it.
99 ChildRefused,
She said something that means she needs a grown-up now; the model never saw it and the parents' view shows it first.
102 NeedsAGrownUp,
The guard refused what the model wrote; she never heard it.
The allowance for thinking is used up for now: not a fault, and said as a rest.
108 Resting,
The log (or the place pictures are kept) could not be written, so nothing was sent anywhere.
114impl Fallback {
Every way a turn can end in a fixed line. Adding a variant fails to compile in line, and
the journey's tests hold this list to the variants.
The line the cat says: fixed and pre-written, never model output and never error text.
128 pub fn line(self) -> &'static str { 129 match self { 130 Fallback::NeedsAGrownUp => "That sounds important. Let's go and find a grown-up you trust right now.", 131 Fallback::ChildRefused | Fallback::AnswerRefused => { 132 "Hmm, that is one to ask a grown-up about. Want to tell me about your toys instead?" 133 } 134 // Say what is wrong in words a child can follow: the cloud, not the cat, is the problem. 135 Fallback::GuardUnavailable | Fallback::ModelUnavailable => { 136 "Oh no, I can't reach my thinking cloud right now. Let's try again in a little bit!" 137 } 138 // Not a fault: Whiskers has used up the thinking it is allowed for now. 139 Fallback::Resting => "I've done lots of thinking and my brain needs a little rest. Let's talk again in a while!", 140 Fallback::LogUnavailable => "My whiskers are all tangled right now. Let's try again in a little bit!", 141 } 142 } 143}
One line of the parents' log. Everything is recorded, including text the guard kept from her: the parents see what the model wrote, not only what she heard.
What a separate look at her pictures found, written before the cat saw them.
The fixed hello. Its own event: a Said would close whatever turn a crash left open.
168 Greeted { text: String },
Something Whiskers will remember about her.
170 Remembered { fact: String },
A parent removed something Whiskers remembered.
172 Forgot { fact: String },
She put a fact away: Whiskers stops using it, the parents can restore it.
174 PutAway { fact: String },
A parent restored a fact she had put away.
176 Restored { fact: String },
A fact the model proposed and the guard (or the limit) kept out.
178 NotRemembered { fact: String, why: String },
What Whiskers called to mind before answering, so the parents can see what shaped a reply.
180 Recalled { facts: Vec<String> },
The oldest part of the chat was folded into the running summary.
182 Compressed { turns: usize },
The background memory work failed; nothing she said was lost, only not yet filed.
What sort of thing a memory is about. Used to group the parents' journal.
200impl Kind {
The word the model is asked to use, and parsed back.
A memory to be filed: what Whiskers has learned, before it has an id.
Names involved: a toy's name, a friend's.
233 pub who: Vec<String>,
Where, as she said it ("at Nana's").
235 pub place: Option<String>,
When, as she said it ("yesterday", "on my birthday").
237 pub when: Option<String>,
Something Whiskers remembers, such as the name of a toy: the child's journal.
243#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 244pub struct Fact { 245 pub id: u64, 246 pub text: String, 247 pub learned_at_ms: u64, 248 #[serde(default)] 249 pub kind: Kind, 250 #[serde(default)] 251 pub who: Vec<String>, 252 #[serde(default)] 253 pub place: Option<String>, 254 #[serde(default)] 255 pub when: Option<String>, 256 #[serde(default)] 257 pub pictures: Vec<PictureId>,
The text's embedding, for searching by meaning. Empty until it has been computed.
Identifies this fact on every device (id is only this device's numbering), so what
one phone learned can be recognised, and forgotten, on another. Required: a fact without
one does not parse, so a memory file that lacks it is read as damaged.
264 pub gid: String,
Whether she has put it away. Hidden is not forgotten: see [Visibility]. Left out of the stored
and wire forms while it is the default, so an untouched fact is what it always was.
The picture the service chose for it from the admitted set, if one has been chosen. Absent until
then, and the card shows a generic star. A name this build does not admit reads as absent (see
[lenient_icon]) rather than damaging the whole memory file.
An icon name that is not admitted (a newer build's, or an excluded one) reads as no icon: losing a picture must never make the memory file "damaged", which starts the app with nothing.
278fn lenient_icon<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Option<IconId>, D::Error> { 279 let name = Option::<String>::deserialize(d)?; 280 Ok(name.and_then(|n| { 281 let icon = IconId::new(&n).ok(); 282 if icon.is_none() { 283 ::log::warn!("a fact names an icon that is not admitted; reading it as none"); 284 } 285 icon 286 })) 287}
289impl Fact {
Whether Whiskers may use this fact (recall, prompts, search). A fact she put away is not used until a parent restores it.
Everything a device knows about what Whiskers remembers, to be merged with another's: the facts, and the identities of those the parents asked it to forget. Merging two of these in either order, any number of times, gives the same memory.