diff --git a/AGENTS.md b/AGENTS.md index 68e14a6..ef784dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,6 +82,10 @@ describe code behavior only, never a live deployment or interaction. - Tests for collaborative adapters must cover an unmentioned agent message in an established group DM, exact self-echo rejection, duplicate delivery, and rejection of an unestablished out-of-scope conversation. +- Collaboration/history tools must preserve complete provider-sized individual + messages. Never silently clip a handoff and make the receiver infer the + missing suffix. If an aggregate context budget is necessary, omit older whole + messages with an explicit notice rather than truncating a shown message. ## Voice-control contract diff --git a/src/tools/read-thread.ts b/src/tools/read-thread.ts index 45144bf..df73165 100644 --- a/src/tools/read-thread.ts +++ b/src/tools/read-thread.ts @@ -106,7 +106,9 @@ function channelNameForEntry(entry: LogEntry): string { return channel.startsWith("slack:#") ? channel.slice("slack:#".length) : entry.channelId || "unknown"; } -function normalizeText(text: unknown, maxLength = 1000): string { +const DEFAULT_MAX_MESSAGE_CHARACTERS = 40_000; + +function normalizeText(text: unknown, maxLength = DEFAULT_MAX_MESSAGE_CHARACTERS): string { if (typeof text !== "string") return ""; const normalized = text.replace(/\s+/g, " ").trim(); if (normalized.length <= maxLength) return normalized; diff --git a/test/read-thread.test.ts b/test/read-thread.test.ts index d02d1bf..c738f31 100644 --- a/test/read-thread.test.ts +++ b/test/read-thread.test.ts @@ -70,7 +70,7 @@ try { channel: "slack:#tinyfat", channelId: "C0123456789", userName: "agent", - text: "I will check deploy QA in this thread.", + text: `I will check deploy QA in this thread. ${"context ".repeat(180)}COMPLETE HANDOFF END`, isBot: true, }, { @@ -231,6 +231,8 @@ try { assert(!first?.messages.some((m) => m.text.includes("product feedback")), "first transcript excludes second thread nuance"); assert(first?.messages[0]?.isRoot === true, "root message is marked root"); assert(first?.messages[1]?.isBot === true, "bot reply is marked Agent context"); + assert(first?.messages[1]?.text.endsWith("COMPLETE HANDOFF END"), "read_thread preserves the end of provider-sized agent messages"); + assert((first?.messages[1]?.text.length || 0) > 1000, "read_thread no longer clips messages at the old 1,000-character boundary"); assert(second?.messages.length === 2, "second thread transcript includes second thread messages"); assert(second?.messages.some((m) => m.text.includes("thread replies less noisy")), "second transcript preserves follow-up nuance"); assert(missing?.messages.length === 0, "valid but unseen thread returns an empty transcript");