openvibers/openvibe.live Commits
openvibe-sdk v0.62.0 (was v0.61.0) (#320)
Changes
@@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL<!-- versions:start -->- openvibe-contracts: v0.209.0--- openvibe-sdk: v0.61.0++- openvibe-sdk: v0.62.0- openvibe-shared: v4.9.0<!-- versions:end -->@@@ -6,7 +6,7 @@"port": 3000,"domain": "openvibe.live (www, ingest, whip)","contracts": "openvibe-contracts v0.209.0",-- "sdk": "openvibe-sdk v0.61.0",++ "sdk": "openvibe-sdk v0.62.0","shared": "openvibe-shared v4.9.0","capabilities": ["live.chat_context.read",@@@ -24,7 +24,7 @@"node-media-server": "^2.7.1","onnxruntime-node": "^1.20.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0","pg": "^8.23.1","sanitize-html": "^2.17.7",@@@ -2660,9 +2660,9 @@}},"node_modules/openvibe-sdk": {-- "version": "0.61.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",-- "integrity": "sha512-rhQKwM+yzVjhgUPTeqMOkL9N38fbSy8JYiEbJ8SwZk8LGcusde6LoQNhQ8UUxMJIwyY7xR1Rp5NVd6XRaVgTQQ==",++ "version": "0.62.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",++ "integrity": "sha512-s2KejVS38zK6tKBZUUWfah6RAfddUuHs3EmzrPxar2YhHUQtiZZp19v0eTQ7NKwKGDaTQY+t8Pf3k+ao11m+7g==","license": "MIT","bin": {"openvibe-download": "bin/openvibe-download.js"@@@ -43,7 +43,7 @@"node-media-server": "^2.7.1","onnxruntime-node": "^1.20.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0","pg": "^8.23.1","sanitize-html": "^2.17.7",@@@ -650,7 +650,7 @@ function _extractDescFromMaybeJson(text) {// written, so the scan only needs to run again when the scrub rules change: this hash of their source says when.let _looksRulesVersion = null;// What the repair covers is part of its version: a change in scope runs the scan again even when the rules did not change.--const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own (2026-10-22)';++const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own; pronouns (2026-10-23)';function looksRulesVersion() {if (!_looksRulesVersion) _looksRulesVersion = require('crypto').createHash('sha1').update([_LOOKS_REPAIR_SCOPE, _LOOKS_RULES].join('|')).digest('hex').slice(0, 12);return _looksRulesVersion;@@@ -698,7 +698,7 @@ async function cleanupMalformedAiText({ force = false } = {}) {// prompts forbade it): the person becomes "the streamer" and the card line follows.let looksDone = false;try { looksDone = !force && (await getState('ai_looks_repair_rules')) === looksRulesVersion(); } catch { looksDone = false; }-- const looks = "~* '(shirtless|topless|bare-chested|young|teen|little|bearded|bald|blond|haired|tattooed|muscular|heavyset|older|elderly|middle-aged|skinny|slim|chubby|attractive|pretty|handsome|cute|boy|girl|kid|child|youth)|\\m(wear|dressed|sporting|shirt|t-shirt|tee|tank top|hoodie|sweat|jacket|coat|vest|beanie|cap|hat|helmet|headset|headphones|sunglasses|glasses|outfit|dress|jersey|suit|robe|bandana|scar|mask|attire|apparel|beard|mustache|moustache|goatee|stubble|tattoo|hair|ponytail|braids|dreadlocks|piercing|freckles|makeup|[a-z-]+-wearing)|\\m(man|woman|men|women|guy|guys|gal|lady|ladies|gentleman|gentlemen|male|female)\\M'";++ const looks = "~* '(shirtless|topless|bare-chested|young|teen|little|bearded|bald|blond|haired|tattooed|muscular|heavyset|older|elderly|middle-aged|skinny|slim|chubby|attractive|pretty|handsome|cute|boy|girl|kid|child|youth)|\\m(wear|dressed|sporting|shirt|t-shirt|tee|tank top|hoodie|sweat|jacket|coat|vest|beanie|cap|hat|helmet|headset|headphones|sunglasses|glasses|outfit|dress|jersey|suit|robe|bandana|scar|mask|attire|apparel|beard|mustache|moustache|goatee|stubble|tattoo|hair|ponytail|braids|dreadlocks|piercing|freckles|makeup|[a-z-]+-wearing)|\\m(man|woman|men|women|guy|guys|gal|lady|ladies|gentleman|gentlemen|male|female|he|she|him|his|her|hers|himself|herself)\\M'";for (const [table, key, col, short] of [['stream_memories', 'id', 'description', null], ['streams', 'id', 'ai_overview', 'ai_overview_short'],['vod_ai_state', 'vod_id', 'ai_overview', 'ai_overview_short'], ['clip_ai_state', 'clip_id', 'ai_overview', 'ai_overview_short'],['streamer_overviews', 'user_id', 'overview', 'overview_short']]) {@@@ -36,7 +36,7 @@ const db = require('../server/db/database');['Panelists discuss while wearing headsets and microphones, with a trophy nearby.', 'Panelists discuss, with a trophy nearby.'],['A man wearing sunglasses on his head looks down at the camera.', 'A person looks down at the camera.'],['A collage of a man in a white shirt and tie wearing dark sunglasses in the left panel.', 'A collage of a person in the left panel.'],-- ['Behind him a green mat; he wears a blue jacket and is leaning to the left.', 'Behind him a green mat; he is leaning to the left.'],++ ['Behind him a green mat; he wears a blue jacket and is leaning to the left.', 'Behind them a green mat; the person is leaning to the left.'],['Two men lift weights, one shirtless and sweating.', 'Two people lift weights, one sweating.'],['Two men stand in an alley, one and drinking from a bottle while the other wears a dusty t-shirt; both look tired.', 'Two people stand in an alley, one drinking from a bottle; both look tired.'],['A man wearing a green bucket hat with frog-like eyes sits indoors.', 'A person sits indoors.'],@@@ -98,6 +98,11 @@ const db = require('../server/db/database');await db.setState('ai_looks_repair_rules', 'an older rule set');await db.cleanupMalformedAiText();assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9240')).description, 'A person cooks pasta.');++ // A pronoun is a guessed gender too (SDK 0.62.0): a row whose only gendered word is "he" or "his" is repaired.++ await db.run('INSERT INTO stream_memories (stream_id, user_id, offset_seconds, description) VALUES (?, ?, ?, ?)', [sid, uid, 9300, 'The streamer says he will check his settings.']);++ await db.setState('ai_looks_repair_rules', 'an older rule set');++ await db.cleanupMalformedAiText();++ assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9300')).description, 'The streamer says the person will check their settings.');// A card line stored without its full text (or derived from an older one) is repaired on its own.await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990001, 'The video shows a man wearing a headset in a kitchen.']);await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990002, 'A person adjusts their hair or clothing near the counter.']);@@@ -105,7 +110,7 @@ const db = require('../server/db/database');await db.cleanupMalformedAiText();assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990001')).ai_overview_short, 'The video shows a person in a kitchen.');assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990002')).ai_overview_short, ' ', 'a line that is all looks becomes the nothing-to-say sentinel');-- assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests his chin on his hand.');++ assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests their chin on their hand.');assert.deepStrictEqual(Object.values(await db.get('SELECT ai_overview, ai_overview_short FROM streams WHERE id = ?', [sid])), ['The streamer plays Fortnite.', 'The streamer plays Fortnite.']);assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9060')).description, 'An older version of the game loads.', 'looks words about things stay');
@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
<!-- versions:start -->
<!-- versions:start -->
- openvibe-contracts: v0.209.0
- openvibe-contracts: v0.209.0
-- openvibe-sdk: v0.61.0
+- openvibe-sdk: v0.62.0
- openvibe-shared: v4.9.0
- openvibe-shared: v4.9.0
<!-- versions:end -->
<!-- versions:end -->
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@
"port": 3000,
"port": 3000,
"domain": "openvibe.live (www, ingest, whip)",
"domain": "openvibe.live (www, ingest, whip)",
"contracts": "openvibe-contracts v0.209.0",
"contracts": "openvibe-contracts v0.209.0",
- "sdk": "openvibe-sdk v0.61.0",
+ "sdk": "openvibe-sdk v0.62.0",
"shared": "openvibe-shared v4.9.0",
"shared": "openvibe-shared v4.9.0",
"capabilities": [
"capabilities": [
"live.chat_context.read",
"live.chat_context.read",
@@ -24,7 +24,7 @@
@@ -24,7 +24,7 @@
"node-media-server": "^2.7.1",
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0",
"pg": "^8.23.1",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sanitize-html": "^2.17.7",
@@ -2660,9 +2660,9 @@
@@ -2660,9 +2660,9 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.61.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",
- "integrity": "sha512-rhQKwM+yzVjhgUPTeqMOkL9N38fbSy8JYiEbJ8SwZk8LGcusde6LoQNhQ8UUxMJIwyY7xR1Rp5NVd6XRaVgTQQ==",
+ "version": "0.62.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",
+ "integrity": "sha512-s2KejVS38zK6tKBZUUWfah6RAfddUuHs3EmzrPxar2YhHUQtiZZp19v0eTQ7NKwKGDaTQY+t8Pf3k+ao11m+7g==",
"license": "MIT",
"license": "MIT",
"bin": {
"bin": {
"openvibe-download": "bin/openvibe-download.js"
"openvibe-download": "bin/openvibe-download.js"
@@ -43,7 +43,7 @@
@@ -43,7 +43,7 @@
"node-media-server": "^2.7.1",
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0",
"pg": "^8.23.1",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sanitize-html": "^2.17.7",
@@ -650,7 +650,7 @@ function _extractDescFromMaybeJson(text) {
@@ -650,7 +650,7 @@ function _extractDescFromMaybeJson(text) {
// written, so the scan only needs to run again when the scrub rules change: this hash of their source says when.
// written, so the scan only needs to run again when the scrub rules change: this hash of their source says when.
let _looksRulesVersion = null;
let _looksRulesVersion = null;
// What the repair covers is part of its version: a change in scope runs the scan again even when the rules did not change.
// What the repair covers is part of its version: a change in scope runs the scan again even when the rules did not change.
-const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own (2026-10-22)';
+const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own; pronouns (2026-10-23)';
function looksRulesVersion() {
function looksRulesVersion() {
if (!_looksRulesVersion) _looksRulesVersion = require('crypto').createHash('sha1').update([_LOOKS_REPAIR_SCOPE, _LOOKS_RULES].join('|')).digest('hex').slice(0, 12);
if (!_looksRulesVersion) _looksRulesVersion = require('crypto').createHash('sha1').update([_LOOKS_REPAIR_SCOPE, _LOOKS_RULES].join('|')).digest('hex').slice(0, 12);
return _looksRulesVersion;
return _looksRulesVersion;
@@ -698,7 +698,7 @@ async function cleanupMalformedAiText({ force = false } = {}) {
@@ -698,7 +698,7 @@ async function cleanupMalformedAiText({ force = false } = {}) {
// prompts forbade it): the person becomes "the streamer" and the card line follows.
// prompts forbade it): the person becomes "the streamer" and the card line follows.
let looksDone = false;
let looksDone = false;
try { looksDone = !force && (await getState('ai_looks_repair_rules')) === looksRulesVersion(); } catch { looksDone = false; }
try { looksDone = !force && (await getState('ai_looks_repair_rules')) === looksRulesVersion(); } catch { looksDone = false; }
- const looks = "~* '(shirtless|topless|bare-chested|young|teen|little|bearded|bald|blond|haired|tattooed|muscular|heavyset|older|elderly|middle-aged|skinny|slim|chubby|attractive|pretty|handsome|cute|boy|girl|kid|child|youth)|\\m(wear|dressed|sporting|shirt|t-shirt|tee|tank top|hoodie|sweat|jacket|coat|vest|beanie|cap|hat|helmet|headset|headphones|sunglasses|glasses|outfit|dress|jersey|suit|robe|bandana|scar|mask|attire|apparel|beard|mustache|moustache|goatee|stubble|tattoo|hair|ponytail|braids|dreadlocks|piercing|freckles|makeup|[a-z-]+-wearing)|\\m(man|woman|men|women|guy|guys|gal|lady|ladies|gentleman|gentlemen|male|female)\\M'";
+ const looks = "~* '(shirtless|topless|bare-chested|young|teen|little|bearded|bald|blond|haired|tattooed|muscular|heavyset|older|elderly|middle-aged|skinny|slim|chubby|attractive|pretty|handsome|cute|boy|girl|kid|child|youth)|\\m(wear|dressed|sporting|shirt|t-shirt|tee|tank top|hoodie|sweat|jacket|coat|vest|beanie|cap|hat|helmet|headset|headphones|sunglasses|glasses|outfit|dress|jersey|suit|robe|bandana|scar|mask|attire|apparel|beard|mustache|moustache|goatee|stubble|tattoo|hair|ponytail|braids|dreadlocks|piercing|freckles|makeup|[a-z-]+-wearing)|\\m(man|woman|men|women|guy|guys|gal|lady|ladies|gentleman|gentlemen|male|female|he|she|him|his|her|hers|himself|herself)\\M'";
for (const [table, key, col, short] of [['stream_memories', 'id', 'description', null], ['streams', 'id', 'ai_overview', 'ai_overview_short'],
for (const [table, key, col, short] of [['stream_memories', 'id', 'description', null], ['streams', 'id', 'ai_overview', 'ai_overview_short'],
['vod_ai_state', 'vod_id', 'ai_overview', 'ai_overview_short'], ['clip_ai_state', 'clip_id', 'ai_overview', 'ai_overview_short'],
['vod_ai_state', 'vod_id', 'ai_overview', 'ai_overview_short'], ['clip_ai_state', 'clip_id', 'ai_overview', 'ai_overview_short'],
['streamer_overviews', 'user_id', 'overview', 'overview_short']]) {
['streamer_overviews', 'user_id', 'overview', 'overview_short']]) {
@@ -36,7 +36,7 @@ const db = require('../server/db/database');
@@ -36,7 +36,7 @@ const db = require('../server/db/database');
['Panelists discuss while wearing headsets and microphones, with a trophy nearby.', 'Panelists discuss, with a trophy nearby.'],
['Panelists discuss while wearing headsets and microphones, with a trophy nearby.', 'Panelists discuss, with a trophy nearby.'],
['A man wearing sunglasses on his head looks down at the camera.', 'A person looks down at the camera.'],
['A man wearing sunglasses on his head looks down at the camera.', 'A person looks down at the camera.'],
['A collage of a man in a white shirt and tie wearing dark sunglasses in the left panel.', 'A collage of a person in the left panel.'],
['A collage of a man in a white shirt and tie wearing dark sunglasses in the left panel.', 'A collage of a person in the left panel.'],
- ['Behind him a green mat; he wears a blue jacket and is leaning to the left.', 'Behind him a green mat; he is leaning to the left.'],
+ ['Behind him a green mat; he wears a blue jacket and is leaning to the left.', 'Behind them a green mat; the person is leaning to the left.'],
['Two men lift weights, one shirtless and sweating.', 'Two people lift weights, one sweating.'],
['Two men lift weights, one shirtless and sweating.', 'Two people lift weights, one sweating.'],
['Two men stand in an alley, one and drinking from a bottle while the other wears a dusty t-shirt; both look tired.', 'Two people stand in an alley, one drinking from a bottle; both look tired.'],
['Two men stand in an alley, one and drinking from a bottle while the other wears a dusty t-shirt; both look tired.', 'Two people stand in an alley, one drinking from a bottle; both look tired.'],
['A man wearing a green bucket hat with frog-like eyes sits indoors.', 'A person sits indoors.'],
['A man wearing a green bucket hat with frog-like eyes sits indoors.', 'A person sits indoors.'],
@@ -98,6 +98,11 @@ const db = require('../server/db/database');
@@ -98,6 +98,11 @@ const db = require('../server/db/database');
await db.setState('ai_looks_repair_rules', 'an older rule set');
await db.setState('ai_looks_repair_rules', 'an older rule set');
await db.cleanupMalformedAiText();
await db.cleanupMalformedAiText();
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9240')).description, 'A person cooks pasta.');
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9240')).description, 'A person cooks pasta.');
+ // A pronoun is a guessed gender too (SDK 0.62.0): a row whose only gendered word is "he" or "his" is repaired.
+ await db.run('INSERT INTO stream_memories (stream_id, user_id, offset_seconds, description) VALUES (?, ?, ?, ?)', [sid, uid, 9300, 'The streamer says he will check his settings.']);
+ await db.setState('ai_looks_repair_rules', 'an older rule set');
+ await db.cleanupMalformedAiText();
+ assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9300')).description, 'The streamer says the person will check their settings.');
// A card line stored without its full text (or derived from an older one) is repaired on its own.
// A card line stored without its full text (or derived from an older one) is repaired on its own.
await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990001, 'The video shows a man wearing a headset in a kitchen.']);
await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990001, 'The video shows a man wearing a headset in a kitchen.']);
await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990002, 'A person adjusts their hair or clothing near the counter.']);
await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990002, 'A person adjusts their hair or clothing near the counter.']);
@@ -105,7 +110,7 @@ const db = require('../server/db/database');
@@ -105,7 +110,7 @@ const db = require('../server/db/database');
await db.cleanupMalformedAiText();
await db.cleanupMalformedAiText();
assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990001')).ai_overview_short, 'The video shows a person in a kitchen.');
assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990001')).ai_overview_short, 'The video shows a person in a kitchen.');
assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990002')).ai_overview_short, ' ', 'a line that is all looks becomes the nothing-to-say sentinel');
assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990002')).ai_overview_short, ' ', 'a line that is all looks becomes the nothing-to-say sentinel');
- assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests his chin on his hand.');
+ assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests their chin on their hand.');
assert.deepStrictEqual(Object.values(await db.get('SELECT ai_overview, ai_overview_short FROM streams WHERE id = ?', [sid])), ['The streamer plays Fortnite.', 'The streamer plays Fortnite.']);
assert.deepStrictEqual(Object.values(await db.get('SELECT ai_overview, ai_overview_short FROM streams WHERE id = ?', [sid])), ['The streamer plays Fortnite.', 'The streamer plays Fortnite.']);
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9060')).description, 'An older version of the game loads.', 'looks words about things stay');
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9060')).description, 'An older version of the game loads.', 'looks words about things stay');