This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

niri / src / runner / loop.test.ts
4.7 kB 165 lines
1import assert from "node:assert/strict" 2import test from "node:test" 3import { __loopTest } from "./loop" 4import type { Message } from "../types" 5import type { LoopState } from "./types" 6 7function makeState(): LoopState { 8 return { 9 conversation: [], 10 pendingInputs: [], 11 tokenCount: 0, 12 contextSize: 0, 13 toolInFlight: false, 14 memoryRecallCooldowns: {}, 15 memoryRecallTurn: 0, 16 memoryRecallPending: false, 17 } 18} 19 20test("applyDiscordSendNudge appends a follow-up user nudge for unsent Discord replies", () => { 21 const state = makeState() 22 const turnMessages: Message[] = [ 23 { 24 role: "user", 25 content: "[user/discord] [discord/dm] hey are you there", 26 }, 27 { 28 role: "assistant", 29 content: "yeah, i'm here", 30 }, 31 ] 32 33 const nudged = __loopTest.applyDiscordSendNudge(state, turnMessages) 34 35 assert.equal(nudged, true) 36 assert.equal(state.conversation.length, 1) 37 assert.match(String(state.conversation[0]?.content), /did not call discord_send/i) 38}) 39 40test("applyDiscordSendNudge fires for [discord batch] envelopes (space, not slash)", () => { 41 const state = makeState() 42 const turnMessages: Message[] = [ 43 { 44 role: "user", 45 content: "[user/discord] [discord batch] 2026-05-01T03:10:50.162Z -> 2026-05-01T03:11:22.198Z\n@meowskullz: hi\n@meowskullz: u there", 46 }, 47 { 48 role: "assistant", 49 content: "yeah hi", 50 }, 51 ] 52 53 const nudged = __loopTest.applyDiscordSendNudge(state, turnMessages) 54 55 assert.equal(nudged, true) 56 assert.equal(state.conversation.length, 1) 57 assert.match(String(state.conversation[0]?.content), /did not call discord_send/i) 58}) 59 60test("applyDiscordSendNudge fires after a harness restart when the discord event is pre-turn context", () => { 61 const state = makeState() 62 state.conversation.push( 63 { 64 role: "user", 65 content: "[harness restarted — discord @ 2026-05-01T04:30:00.000Z]\n\n[discord/dm] hi starfish", 66 }, 67 { 68 role: "assistant", 69 content: "i keep getting bounced by harness restarts sorry ^^ still here though! what's up?", 70 }, 71 ) 72 73 const turnStart = 1 74 const turnMessages: Message[] = [state.conversation[1]!] 75 76 const nudged = __loopTest.applyDiscordSendNudge(state, turnMessages, turnStart) 77 78 assert.equal(nudged, true) 79 assert.equal(state.conversation.length, 3) 80 assert.match(String(state.conversation[2]?.content), /did not call discord_send/i) 81}) 82 83test("applyDiscordSendNudge does not fire when discord_send was already called", () => { 84 const state = makeState() 85 const turnMessages: Message[] = [ 86 { 87 role: "user", 88 content: "[user/discord] [discord/channel] can you reply", 89 }, 90 { 91 role: "assistant", 92 content: "sending now", 93 tool_calls: [ 94 { 95 id: "call_123", 96 type: "function", 97 function: { 98 name: "discord_send", 99 arguments: "{\"channel_id\":\"1\",\"content\":\"sending now\"}", 100 }, 101 }, 102 ], 103 }, 104 ] 105 106 const nudged = __loopTest.applyDiscordSendNudge(state, turnMessages) 107 108 assert.equal(nudged, false) 109 assert.equal(state.conversation.length, 0) 110}) 111 112test("applyLoopGuardNudge appends an in-band user nudge and saves", async () => { 113 const state = makeState() 114 let saved = false 115 116 await __loopTest.applyLoopGuardNudge( 117 state, 118 { 119 waitForEvent: async () => { 120 throw new Error("unexpected wait") 121 }, 122 waitForEventWithTimeout: async () => null, 123 injectIncomingEvent: () => {}, 124 flushDeferredEvents: () => {}, 125 clearSession: async () => {}, 126 saveSession: async () => { 127 saved = true 128 }, 129 }, 130 "loop guard tripped after 120 turns", 131 ) 132 133 assert.equal(saved, true) 134 assert.equal(state.conversation.length, 1) 135 assert.equal(state.conversation[0]?.role, "user") 136 assert.match(String(state.conversation[0]?.content), /loop guard tripped after 120 turns/i) 137}) 138 139test("waitForNextEvent waits for and injects the next external event", async () => { 140 const calls: string[] = [] 141 const event = { 142 source: "chat" as const, 143 triggeredAt: "2026-05-01T04:40:00.000Z", 144 content: "still here", 145 raw: {}, 146 } 147 148 await __loopTest.waitForNextEvent(42, { 149 waitForEvent: async () => { 150 calls.push("wait") 151 return event 152 }, 153 waitForEventWithTimeout: async () => null, 154 injectIncomingEvent: (_convId, incoming) => { 155 calls.push(`inject:${incoming.content}`) 156 assert.equal(_convId, 42) 157 assert.equal(incoming, event) 158 }, 159 flushDeferredEvents: () => {}, 160 clearSession: async () => {}, 161 saveSession: async () => {}, 162 }) 163 164 assert.deepEqual(calls, ["wait", "inject:still here"]) 165})