From be38728ed4805368804889ec59cff5c3fe5fad6d Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:49:24 +0000 Subject: [PATCH] fix(discord): preserve full persisted session errors Remove the remaining 300-character slice from the watchdog database query so text attachments receive the complete persisted stack trace. Add a SQLite-backed regression test. Co-authored-by: Enrico Polanski <16064771+enricopolanski@users.noreply.github.com> Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- apps/discord-bot/src/features/Alerts.test.ts | 39 ++++++++++++++++++++ apps/discord-bot/src/features/Alerts.ts | 4 +- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/apps/discord-bot/src/features/Alerts.test.ts b/apps/discord-bot/src/features/Alerts.test.ts index ee1acf4959f..363a720fd0a 100644 --- a/apps/discord-bot/src/features/Alerts.test.ts +++ b/apps/discord-bot/src/features/Alerts.test.ts @@ -1,3 +1,9 @@ +// @effect-diagnostics nodeBuiltinImport:off +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; + import * as Cause from "effect/Cause"; import { describe, expect, it, vi } from "vite-plus/test"; @@ -12,6 +18,7 @@ import { fatalAlertDelivery, formatAlertCause, isExpectedSessionLastError, + listSessionErrors, selectSessionErrorsForAlert, sessionErrorAlertDelivery, sessionErrorAlertKey, @@ -82,6 +89,38 @@ describe("formatAlertCause", () => { }); describe("Discord alert content", () => { + it("reads the complete persisted session error before attaching it", () => { + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-alert-trace-")); + const dbPath = NodePath.join(tempDir, "state.sqlite"); + const trace = `Error: Invalid params\n${" at decodeFrame (file:///long/path.js:1:1)\n".repeat(80)}`; + + try { + NodeChildProcess.execFileSync( + "python3", + [ + "-c", + [ + "import sqlite3, sys", + "db = sqlite3.connect(sys.argv[1])", + "db.execute('CREATE TABLE projection_thread_sessions (thread_id TEXT, last_error TEXT, status TEXT, updated_at TEXT)')", + "db.execute('INSERT INTO projection_thread_sessions VALUES (?, ?, ?, ?)', ('thread-1', sys.argv[2], 'error', '2026-07-28T00:00:00Z'))", + "db.commit()", + ].join("\n"), + dbPath, + trace, + ], + { encoding: "utf8" }, + ); + + expect(trace.length).toBeGreaterThan(300); + expect(listSessionErrors(dbPath)).toEqual([ + { threadId: "thread-1", lastError: trace, status: "error" }, + ]); + } finally { + NodeFS.rmSync(tempDir, { recursive: true, force: true }); + } + }); + it("attaches the complete T3 session stack as one text file", () => { const trace = [ "Error: Invalid params", diff --git a/apps/discord-bot/src/features/Alerts.ts b/apps/discord-bot/src/features/Alerts.ts index 1b9986c5b69..798d634e0b8 100644 --- a/apps/discord-bot/src/features/Alerts.ts +++ b/apps/discord-bot/src/features/Alerts.ts @@ -555,7 +555,7 @@ print(json.dumps(out)) return (parsed as Array<{ threadId: string; turnId: string; ageMin: number }>) ?? []; } -function listSessionErrors(dbPath: string): ReadonlyArray<{ +export function listSessionErrors(dbPath: string): ReadonlyArray<{ threadId: string; lastError: string; status: string | null; @@ -569,7 +569,7 @@ cur = db.execute( "ORDER BY updated_at DESC LIMIT 40" ) print(json.dumps([ - {"threadId": r[0], "lastError": (r[1] or "")[:300], "status": r[2]} + {"threadId": r[0], "lastError": r[1] or "", "status": r[2]} for r in cur ])) `,