diff --git a/README.md b/README.md index aedd0fe5d..410efca01 100644 --- a/README.md +++ b/README.md @@ -474,22 +474,23 @@ Core contributors: Thanks to all clawtributors:

- steipete joaohlisboa mneves75 rahthakor joshp123 mukhtharcm maxsumrall xadenryan Tobias Bischoff hsrvc - magimetal meaningfool NicholasSpisak abhisekbasu1 claude jamesgroat Hyaxia dantelex daveonkels vrknetha - radek-paclt mteam88 Eng. Juan Combetto dbhurley Mariano Belinky julianengel benithors sreekaransrinath gupsammy cristip73 - nachoiacovino Vasanth Rao Naik Sabavat lc0rp scald andranik-sahakyan nachx639 davidguttman sleontenko sircrumpet peschee - rafaelreis-r ratulsarna lutr0 thewilloftheshadow emanuelst KristijanJovanovski CashWilliams osolmaz kiranjd sebslight - sheeek onutc manuelhettich minghinmatthewlam myfunc buddyh mcinteerj timkrase obviyus azade-c - danielz1z Josh Phillips bjesuiter roshanasingh4 superman32432432 Yurii Chukhlib antons austinm911 blacksmith-sh[bot] grp06 - HeimdallStrategy imfing jarvis-medmatic mahmoudashraf93 petter-b pkrmf RandyVentures dan-dr erikpr1994 jalehman - jonasjancarik Keith the Silly Goose L36 Server mitschabaude-bot neist chrisrodz Friederike Seiler gabriel-trigo iamadig Kit - koala73 manmal ngutman ogulcancelik pasogott petradonka VACInc zats Chris Taylor Django Navarro - pcty-nextgen-service-account rubyrunsstuff Syhids Aaron Konyer erik-agens evalexpr fcatuhe gumadeiras henrino3 jayhickey - jeffersonwarrior jeffersonwarrior Jonathan D. Rhyne (DJ-D) juanpablodlc jverdi mickahouan mjrussell oswalpalash p6l-richard philipp-spiess - robaxelsen Sash Catanzarite VAC zknicker adam91holt alejandro maza andrewting19 Asleep123 bolismauro cash-echo-bot - Clawd conhecendocontato Drake Thomsen gtsifrikas HazAT hrdwdmrbl hugobarauna Jarvis Jefferson Nunn kitze - kkarimi levifig Lloyd loukotal Marc martinpucik Miles mrdbstn MSch Mustafa Tag Eldeen - ndraiman nexty5870 prathamdby reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha siraht snopoke - The Admiral wes-davis wstock YuriNachos Zach Knickerbocker Azade carlulsoe cpojer ddyo Erik - latitudeki5223 longmaba Manuel Maly Mourad Boustani pcty-nextgen-ios-builder Quentin Randy Torres ronak-guliani thesash William Stock + steipete bohdanpodvirnyi joaohlisboa mneves75 rahthakor vrknetha joshp123 mukhtharcm maxsumrall xadenryan + Tobias Bischoff hsrvc magimetal meaningfool NicholasSpisak abhisekbasu1 claude jamesgroat Hyaxia dantelex + daveonkels radek-paclt mteam88 Eng. Juan Combetto dbhurley Mariano Belinky julianengel benithors sreekaransrinath gupsammy + cristip73 nachoiacovino Vasanth Rao Naik Sabavat lc0rp scald andranik-sahakyan nachx639 davidguttman sleontenko sircrumpet + peschee rafaelreis-r ratulsarna lutr0 thewilloftheshadow emanuelst KristijanJovanovski CashWilliams rdev osolmaz + kiranjd sebslight sheeek onutc manuelhettich minghinmatthewlam myfunc buddyh mcinteerj timkrase + obviyus azade-c bjesuiter danielz1z Josh Phillips roshanasingh4 superman32432432 Yurii Chukhlib antons austinm911 + blacksmith-sh[bot] grp06 HeimdallStrategy imfing jarvis-medmatic mahmoudashraf93 petter-b pkrmf RandyVentures dan-dr + erikpr1994 jalehman jonasjancarik Keith the Silly Goose L36 Server mitschabaude-bot neist chrisrodz Friederike Seiler gabriel-trigo + iamadig Kit koala73 manmal ngutman ogulcancelik pasogott petradonka VACInc zats + Chris Taylor Django Navarro pcty-nextgen-service-account rubyrunsstuff Syhids Aaron Konyer erik-agens evalexpr fcatuhe gumadeiras + henrino3 jayhickey jeffersonwarrior jeffersonwarrior Jonathan D. Rhyne (DJ-D) juanpablodlc jverdi mickahouan mjrussell oswalpalash + p6l-richard philipp-spiess robaxelsen Sash Catanzarite VAC zknicker adam91holt alejandro maza andrewting19 Asleep123 + bolismauro cash-echo-bot Clawd conhecendocontato Drake Thomsen gtsifrikas HazAT hrdwdmrbl hugobarauna Jarvis + Jefferson Nunn kitze kkarimi levifig Lloyd loukotal Marc martinpucik Miles mrdbstn + MSch Mustafa Tag Eldeen ndraiman nexty5870 prathamdby reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha + siraht snopoke The Admiral wes-davis wstock YuriNachos Zach Knickerbocker Azade carlulsoe cpojer + ddyo Erik latitudeki5223 longmaba Manuel Maly Mourad Boustani pcty-nextgen-ios-builder Quentin Randy Torres ronak-guliani + thesash William Stock tyler6204

diff --git a/src/agents/clawdbot-tools.session-status.test.ts b/src/agents/clawdbot-tools.session-status.test.ts index 1a173339d..f3a07b7e2 100644 --- a/src/agents/clawdbot-tools.session-status.test.ts +++ b/src/agents/clawdbot-tools.session-status.test.ts @@ -1,15 +1,22 @@ import { describe, expect, it, vi } from "vitest"; const loadSessionStoreMock = vi.fn(); -const saveSessionStoreMock = vi.fn(); +const updateSessionStoreMock = vi.fn(); vi.mock("../config/sessions.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, loadSessionStore: (storePath: string) => loadSessionStoreMock(storePath), - saveSessionStore: (storePath: string, store: Record) => - saveSessionStoreMock(storePath, store), + updateSessionStore: async ( + storePath: string, + mutator: (store: Record) => Promise | void, + ) => { + const store = loadSessionStoreMock(storePath) as Record; + await mutator(store); + updateSessionStoreMock(storePath, store); + return store; + }, resolveStorePath: () => "/tmp/sessions.json", }; }); @@ -73,7 +80,7 @@ import { createClawdbotTools } from "./clawdbot-tools.js"; describe("session_status tool", () => { it("returns a status card for the current session", async () => { loadSessionStoreMock.mockReset(); - saveSessionStoreMock.mockReset(); + updateSessionStoreMock.mockReset(); loadSessionStoreMock.mockReturnValue({ main: { sessionId: "s1", @@ -96,7 +103,7 @@ describe("session_status tool", () => { it("errors for unknown session keys", async () => { loadSessionStoreMock.mockReset(); - saveSessionStoreMock.mockReset(); + updateSessionStoreMock.mockReset(); loadSessionStoreMock.mockReturnValue({ main: { sessionId: "s1", updatedAt: 10 }, }); @@ -110,12 +117,12 @@ describe("session_status tool", () => { await expect(tool.execute("call2", { sessionKey: "nope" })).rejects.toThrow( "Unknown sessionKey", ); - expect(saveSessionStoreMock).not.toHaveBeenCalled(); + expect(updateSessionStoreMock).not.toHaveBeenCalled(); }); it("resets per-session model override via model=default", async () => { loadSessionStoreMock.mockReset(); - saveSessionStoreMock.mockReset(); + updateSessionStoreMock.mockReset(); loadSessionStoreMock.mockReturnValue({ main: { sessionId: "s1", @@ -133,8 +140,8 @@ describe("session_status tool", () => { if (!tool) throw new Error("missing session_status tool"); await tool.execute("call3", { model: "default" }); - expect(saveSessionStoreMock).toHaveBeenCalled(); - const [, savedStore] = saveSessionStoreMock.mock.calls.at(-1) as [ + expect(updateSessionStoreMock).toHaveBeenCalled(); + const [, savedStore] = updateSessionStoreMock.mock.calls.at(-1) as [ string, Record, ]; diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index f9129566a..dc46ee7bc 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -400,6 +400,9 @@ export async function runAgentTurnWithFallback(params: { } } + // Keep the in-memory snapshot consistent with the on-disk store reset. + delete params.activeSessionStore[sessionKey]; + // Remove session entry from store using a fresh, locked snapshot. await updateSessionStore(params.storePath, (store) => { delete store[sessionKey]; diff --git a/src/commands/agent.ts b/src/commands/agent.ts index 1f3ad44c6..e7d3680dc 100644 --- a/src/commands/agent.ts +++ b/src/commands/agent.ts @@ -243,6 +243,7 @@ export async function agentCommand( } if (sessionEntry && sessionStore && sessionKey && hasStoredOverride) { + const entry = sessionEntry; const overrideProvider = sessionEntry.providerOverride?.trim() || defaultProvider; const overrideModel = sessionEntry.modelOverride?.trim(); if (overrideModel) { @@ -252,12 +253,12 @@ export async function agentCommand( allowedModelKeys.size > 0 && !allowedModelKeys.has(key) ) { - delete sessionEntry.providerOverride; - delete sessionEntry.modelOverride; - sessionEntry.updatedAt = Date.now(); - sessionStore[sessionKey] = sessionEntry; + delete entry.providerOverride; + delete entry.modelOverride; + entry.updatedAt = Date.now(); + sessionStore[sessionKey] = entry; await updateSessionStore(storePath, (store) => { - store[sessionKey] = sessionEntry; + store[sessionKey] = entry; }); } } @@ -277,17 +278,21 @@ export async function agentCommand( model = storedModelOverride; } } - if (sessionEntry?.authProfileOverride) { - const store = ensureAuthProfileStore(); - const profile = store.profiles[sessionEntry.authProfileOverride]; - if (!profile || profile.provider !== provider) { - delete sessionEntry.authProfileOverride; - sessionEntry.updatedAt = Date.now(); - if (sessionStore && sessionKey) { - sessionStore[sessionKey] = sessionEntry; - await updateSessionStore(storePath, (store) => { - store[sessionKey] = sessionEntry; - }); + if (sessionEntry) { + const authProfileId = sessionEntry.authProfileOverride; + if (authProfileId) { + const entry = sessionEntry; + const store = ensureAuthProfileStore(); + const profile = store.profiles[authProfileId]; + if (!profile || profile.provider !== provider) { + delete entry.authProfileOverride; + entry.updatedAt = Date.now(); + if (sessionStore && sessionKey) { + sessionStore[sessionKey] = entry; + await updateSessionStore(storePath, (store) => { + store[sessionKey] = entry; + }); + } } } } @@ -312,11 +317,12 @@ export async function agentCommand( } resolvedThinkLevel = "high"; if (sessionEntry && sessionStore && sessionKey && sessionEntry.thinkingLevel === "xhigh") { - sessionEntry.thinkingLevel = "high"; - sessionEntry.updatedAt = Date.now(); - sessionStore[sessionKey] = sessionEntry; + const entry = sessionEntry; + entry.thinkingLevel = "high"; + entry.updatedAt = Date.now(); + sessionStore[sessionKey] = entry; await updateSessionStore(storePath, (store) => { - store[sessionKey] = sessionEntry; + store[sessionKey] = entry; }); } } diff --git a/src/discord/monitor/provider.ts b/src/discord/monitor/provider.ts index facfa9595..f2c3e8997 100644 --- a/src/discord/monitor/provider.ts +++ b/src/discord/monitor/provider.ts @@ -136,11 +136,6 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) { publicKey: "a", token, autoDeploy: nativeEnabled, - eventQueue: { - // Auto-threading (create thread + generate reply + post) can exceed the default - // 30s listener timeout in some environments. - listenerTimeout: 120_000, - }, }, { commands,