my harness for niri
1import test from "node:test"
2import assert from "node:assert/strict"
3import type { Message } from "./types.js"
4import { __memoryTest } from "./memory.js"
5
6test("latestMemoryRecallQuery falls back past scheduled heartbeat", () => {
7 const conversation: Message[] = [
8 { role: "user", content: "what did we talk about when lisya was buying monero" },
9 { role: "assistant", content: "..." },
10 { role: "user", content: "Scheduled heartbeat." },
11 ]
12
13 assert.equal(
14 __memoryTest.latestMemoryRecallQuery(conversation),
15 "what did we talk about when lisya was buying monero",
16 )
17})
18
19test("memoryQueryForUserMessage extracts structured parts from discord batch", () => {
20 const batch = `[user/discord] [discord batch] 2026-05-01T03:10:50.162Z -> 2026-05-01T03:11:22.198Z
21new_messages=1 channels=1 pending_inbox=0 scope=configured+dm
22auto_seen_timeout=10m auto_demoted=0
23channel_flag_repairs=0
24channel messages are context, not direct requests. replying is optional; use judgment.
25
26recent messages:
27- [channel/staying up till 1 billion oclock/#niri] [2026-05-01 03:11:14.553Z] @meowskullz: awa
28
29pending preview:
30- 1499679672404021248 [mention] [channel/staying up till 1 billion oclock/#niri] [2026-05-01 03:11:14.553Z] @meowskullz: awa`
31
32 assert.deepEqual(__memoryTest.memoryQueryForUserMessage(batch), {
33 sender: "meowskullz",
34 source: "channel/staying up till 1 billion oclock/#niri",
35 body: "awa",
36 })
37})
38
39test("latestMemoryRecallQuery skips ambient discord batch without pending items", () => {
40 const batch = `[user/discord] [discord batch] 2026-05-01T03:10:50.162Z -> 2026-05-01T03:11:22.198Z
41new_messages=1 channels=1 pending_inbox=0 scope=configured+dm
42
43recent messages:
44- [channel/meowskullz's server/#ai-sister-yap] [2026-05-01 08:01:35.639Z] @rose: foxie emoji
45
46pending preview:
47- (none)`
48
49 const conversation: Message[] = [
50 { role: "user", content: "what did we talk about when lisya was buying monero" },
51 { role: "assistant", content: "..." },
52 { role: "user", content: batch },
53 ]
54
55 assert.equal(
56 __memoryTest.latestMemoryRecallQuery(conversation),
57 "what did we talk about when lisya was buying monero",
58 )
59})
60
61test("memoryQueryForUserMessage ignores ambient discord batch recent messages", () => {
62 const batch = `[user/discord] [discord batch] 2026-05-01T03:10:50.162Z -> 2026-05-01T03:11:22.198Z
63new_messages=1 channels=1 pending_inbox=0 scope=configured+dm
64
65recent messages:
66- [channel/meowskullz's server/#ai-sister-yap] [2026-05-01 08:01:35.639Z] @rose: foxie emoji
67
68pending preview:
69- (none)`
70
71 assert.deepEqual(__memoryTest.memoryQueryForUserMessage(batch), {
72 sender: null,
73 source: null,
74 body: batch,
75 })
76})
77
78test("memoryQueryForUserMessage parses discord DM envelope into parts", () => {
79 const dm = `[discord/dm] @meowskullz
80context: DM 1234567890
81message_id: 9999
82timestamp: 2026-05-01T00:00:00Z
83action: This is a direct message. Reply if it needs a response.
84
85thanks`
86
87 const parts = __memoryTest.memoryQueryForUserMessage(dm)
88 assert.equal(parts.sender, "meowskullz")
89 assert.equal(parts.source, "DM")
90 assert.equal(parts.body, "thanks")
91})
92
93test("buildSearchProfile uses sender as primary signal and drops source", async () => {
94 const profile = await __memoryTest.buildSearchProfile({
95 sender: "meowskullz",
96 source: "DM",
97 body: "thanks",
98 })
99
100 assert.equal(profile.sender, "meowskullz")
101 assert.equal(profile.personQuery, true)
102 assert.deepEqual(profile.bodyTokens, ["thanks"])
103 assert.ok(profile.tokens.includes("meowskullz"))
104 assert.ok(profile.tokens.includes("thanks"))
105 assert.ok(!profile.tokens.includes("dm"), "source label should not become a search token")
106})
107
108test("resolveAliases follows transitive mappings without cycles", () => {
109 const map = {
110 meowskullz: ["ana"],
111 ana: ["ana_canonical"],
112 foo: ["meowskullz"],
113 }
114 assert.deepEqual(__memoryTest.resolveAliases("meowskullz", map), ["ana", "ana_canonical"])
115 assert.deepEqual(__memoryTest.resolveAliases("foo", map), ["meowskullz", "ana", "ana_canonical"])
116 assert.deepEqual(__memoryTest.resolveAliases(null, map), [])
117})
118
119test("buildSearchProfile expands sender via alias map", async (t) => {
120 const fs = await import("node:fs/promises")
121 const path = await import("node:path")
122 const url = await import("node:url")
123 const memoriesDir = path.resolve(url.fileURLToPath(import.meta.url), "../../home/memories")
124 const aliasFile = path.join(memoriesDir, "aliases.json")
125 const had = await fs.readFile(aliasFile, "utf-8").catch(() => null)
126
127 await fs.mkdir(memoriesDir, { recursive: true })
128 await fs.writeFile(aliasFile, JSON.stringify({ meowskullz: ["ana"] }), "utf-8")
129
130 t.after(async () => {
131 if (had !== null) await fs.writeFile(aliasFile, had, "utf-8")
132 else await fs.rm(aliasFile, { force: true })
133 })
134
135 const profile = await __memoryTest.buildSearchProfile({
136 sender: "meowskullz",
137 source: "DM",
138 body: "thanks",
139 })
140
141 assert.deepEqual(profile.senderAliases, ["ana"])
142 assert.ok(profile.tokens.includes("ana"))
143})
144
145test("buildSearchProfile body informativeness short-circuits on body-people", async () => {
146 const withPerson = await __memoryTest.buildSearchProfile({
147 sender: "meowskullz",
148 source: "DM",
149 body: "yayy, who is rea",
150 })
151 assert.equal(withPerson.bodyInformative, true)
152
153 const empty = await __memoryTest.buildSearchProfile({ sender: "meowskullz", source: "DM", body: "" })
154 assert.equal(empty.bodyInformative, false)
155})
156
157test("buildSearchProfile detects people mentioned in body", async () => {
158 const profile = await __memoryTest.buildSearchProfile({
159 sender: "meowskullz",
160 source: "DM",
161 body: "patpat, who is rea",
162 })
163
164 assert.ok(profile.bodyPeople.includes("rea"), `expected rea in bodyPeople, got ${JSON.stringify(profile.bodyPeople)}`)
165 assert.ok(profile.tokens.includes("rea"))
166 assert.equal(profile.personQuery, true)
167})
168
169test("searchTokens keeps meaningful body terms", () => {
170 assert.deepEqual(__memoryTest.searchTokens("staying up till 1 billion oclock awa"), [
171 "staying",
172 "till",
173 "billion",
174 "oclock",
175 "awa",
176 ])
177})