






















@@ -723,7 +723,7 @@ describe("SessionManager.open", () => {
723723).toEqual(["changed 1"]);
724724});
725725726-it("invalidates assistant tool-call hook writes before advancing the cache", async () => {
726+it("detects tool-result detail hooks before advancing the cache", async () => {
727727const dir = await makeTempDir();
728728const sessionFile = path.join(dir, "session.jsonl");
729729const assistantEntry = {
@@ -749,32 +749,136 @@ describe("SessionManager.open", () => {
749749withSessionWriteLock: async (run) => await run(),
750750},
751751async () => {
752-sessionManager.appendMessage(
753-{
754- ...buildAssistantMessage("unused"),
755-content: [
756-{
757-type: "toolCall",
758-id: "call-1",
759-name: "custom",
760-arguments: {
761-value: {
762-toJSON() {
763-writeFileSync(
764-sessionFile,
765-`${headerLine}\n${JSON.stringify(replacementEntry)}\n`,
766-"utf8",
767-);
768-return "persisted";
769-},
752+sessionManager.appendMessage({
753+role: "toolResult",
754+toolCallId: "call-1",
755+toolName: "custom",
756+content: [{ type: "text", text: "unused" }],
757+details: {
758+value: {
759+toJSON() {
760+writeFileSync(
761+sessionFile,
762+`${headerLine}\n${JSON.stringify(replacementEntry)}\n`,
763+"utf8",
764+);
765+return "persisted";
766+},
767+},
768+},
769+isError: false,
770+timestamp: Date.now(),
771+});
772+},
773+);
774+775+const reopenedPrefix = SessionManager.open(sessionFile, dir, dir)
776+.getEntries()
777+.find((entry) => entry.id === "assistant-1");
778+expect(reopenedPrefix ? readMessageContent(reopenedPrefix) : undefined).toBe("changed 1");
779+});
780+781+it("does not warm-cache tool-result detail appends", async () => {
782+const dir = await makeTempDir();
783+const sessionFile = path.join(dir, "session.jsonl");
784+const assistantEntry = {
785+type: "message",
786+id: "assistant-1",
787+parentId: null,
788+timestamp: "2026-06-04T00:00:01.000Z",
789+message: buildAssistantMessage("message 1"),
790+};
791+await fs.writeFile(
792+sessionFile,
793+`${JSON.stringify(buildSessionHeader(dir))}\n${JSON.stringify(assistantEntry)}\n`,
794+"utf8",
795+);
796+797+const sessionManager = SessionManager.open(sessionFile, dir, dir);
798+await withOwnedSessionTranscriptWrites(
799+{
800+ sessionFile,
801+canAdvanceSessionEntryCache: () => true,
802+publishSessionFileSnapshot: () => true,
803+withSessionWriteLock: async (run) => await run(),
804+},
805+async () => {
806+sessionManager.appendMessage({
807+role: "toolResult",
808+toolCallId: "call-1",
809+toolName: "custom",
810+content: [{ type: "text", text: "unused" }],
811+details: { source: "extension" },
812+isError: false,
813+timestamp: Date.now(),
814+});
815+},
816+);
817+818+const originalParse = JSON.parse;
819+let parseCount = 0;
820+JSON.parse = function countedParse(...args: Parameters<typeof JSON.parse>) {
821+parseCount += 1;
822+return originalParse.apply(originalParse, args);
823+} as typeof JSON.parse;
824+825+try {
826+expect(SessionManager.open(sessionFile, dir, dir).getEntries()).toHaveLength(2);
827+expect(parseCount).toBeGreaterThan(0);
828+} finally {
829+JSON.parse = originalParse;
830+}
831+});
832+833+it("detects assistant tool-call hook writes before advancing the cache", async () => {
834+const dir = await makeTempDir();
835+const sessionFile = path.join(dir, "session.jsonl");
836+const assistantEntry = {
837+type: "message",
838+id: "assistant-1",
839+parentId: null,
840+timestamp: "2026-06-04T00:00:01.000Z",
841+message: buildAssistantMessage("message 1"),
842+};
843+const replacementEntry = {
844+ ...assistantEntry,
845+message: buildAssistantMessage("changed 1"),
846+};
847+const headerLine = JSON.stringify(buildSessionHeader(dir));
848+await fs.writeFile(sessionFile, `${headerLine}\n${JSON.stringify(assistantEntry)}\n`, "utf8");
849+850+const sessionManager = SessionManager.open(sessionFile, dir, dir);
851+await withOwnedSessionTranscriptWrites(
852+{
853+ sessionFile,
854+canAdvanceSessionEntryCache: () => true,
855+publishSessionFileSnapshot: () => true,
856+withSessionWriteLock: async (run) => await run(),
857+},
858+async () => {
859+sessionManager.appendMessage({
860+ ...buildAssistantMessage("unused"),
861+content: [
862+{
863+type: "toolCall",
864+id: "call-1",
865+name: "custom",
866+arguments: {
867+value: {
868+toJSON() {
869+writeFileSync(
870+sessionFile,
871+`${headerLine}\n${JSON.stringify(replacementEntry)}\n`,
872+"utf8",
873+);
874+return "persisted";
770875},
771876},
772877},
773-],
774-stopReason: "toolUse",
775-},
776-{ invalidateSerializedPrefixCache: true },
777-);
878+},
879+],
880+stopReason: "toolUse",
881+});
778882},
779883);
780884此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。