fix(copilot): keep tool transcript groups structurally complete (#114403)

* refactor(copilot): make SQLite own transcripts

* fix(copilot): harden transcript event projection

* test(copilot): tighten transcript journal types

* fix(copilot): retain unmatched usage metadata

* refactor(copilot): commit tool groups atomically

* fix(copilot): break journal bridge import cycle

* fix(copilot): preserve grouped transcript replay

* test(copilot): cover opaque replay state

* fix(copilot): invalidate lossy transcript replay

* fix(copilot): guard transcript rewrite topology

* fix(copilot): drain transcript finalization

* fix(copilot): preserve resumed session history

* fix(copilot): dedupe replayed transcript snapshots

* fix(copilot): isolate transcript write hooks

* fix(copilot): track staged transcript identities

* fix(copilot): isolate ephemeral transcript events

* fix(copilot): coalesce assistant snapshots

* fix(copilot): invalidate unprojected SDK context

* fix(copilot): guard resumed transcript replay

* fix(plugin-sdk): expose strict transcript append

* fix(copilot): persist journal provenance safely

* fix(copilot): separate live and durable deltas

* fix(copilot): require durable projection ownership

* fix(copilot): preserve strict append typing

* fix(copilot): gate deferred session deletion

* fix(copilot): preserve transcript replay identity

* fix(copilot): retain cited replay history

* fix(copilot): require complete replay provenance

* fix(copilot): reject ambiguous assistant snapshots

* fix(copilot): return latest assistant snapshot

* chore(copilot): align transcript lint gates

* test(lint): cover Copilot line budgets

* test(ci): dedupe Codex prewarm shard
This commit is contained in:
Peter Steinberger
2026-07-27 15:42:00 -04:00
committed by GitHub
parent bbc73c36bd
commit 3dc3370d9e
26 changed files with 3894 additions and 1287 deletions
@@ -13,6 +13,8 @@ import * as transcriptEvents from "../sessions/transcript-events.js";
import {
appendAssistantMirrorMessageByIdentity,
appendSessionTranscriptMessageByIdentity,
appendSessionTranscriptMessageByIdentityStrict,
appendSessionTranscriptMessagesByIdentity,
formatSessionTranscriptMemoryHitKey,
parseSessionTranscriptMemoryHitKey,
publishSessionTranscriptUpdateByIdentity,
@@ -81,6 +83,74 @@ describe("session transcript runtime SDK", () => {
expect(loadSessionEntry(scope)?.sessionFile).toBeUndefined();
});
it("atomically appends and idempotently replays an ordered message group", async () => {
const scope = {
agentId: "main",
sessionId: "batch-session",
sessionKey: "agent:main:batch",
storePath,
};
await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 });
const messages = [
{
eventId: "batch-assistant",
idempotencyLookup: "scan" as const,
message: { role: "assistant", content: "checking", idempotencyKey: "batch:assistant" },
now: 1_000,
},
{
eventId: "batch-result",
idempotencyLookup: "scan" as const,
message: { role: "toolResult", content: "done", idempotencyKey: "batch:result" },
now: 2_000,
},
];
const appended = await appendSessionTranscriptMessagesByIdentity({ ...scope, messages });
const replayed = await appendSessionTranscriptMessagesByIdentity({ ...scope, messages });
expect(appended.map((result) => result.appended)).toEqual([true, true]);
expect(replayed.map((result) => result.appended)).toEqual([false, false]);
const events = await readSessionTranscriptEvents(scope);
expect(events).toHaveLength(3);
expect(events.slice(1)).toMatchObject([
{ id: "batch-assistant", parentId: null },
{ id: "batch-result", parentId: "batch-assistant" },
]);
});
it("distinguishes strict singleton results, suppression, and session rebound", async () => {
const scope = {
agentId: "main",
sessionId: "strict-session",
sessionKey: "agent:main:strict",
storePath,
};
await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 });
await expect(
appendSessionTranscriptMessageByIdentityStrict({
...scope,
message: { role: "user", content: "blocked" },
prepareMessageAfterIdempotencyCheck: () => undefined,
}),
).resolves.toEqual({ kind: "suppressed" });
await expect(
appendSessionTranscriptMessageByIdentityStrict({
...scope,
message: { role: "user", content: "persisted", idempotencyKey: "strict:user" },
}),
).resolves.toMatchObject({ kind: "result", result: { appended: true } });
await upsertSessionEntry(scope, { sessionId: "replacement-session", updatedAt: 20 });
await expect(
appendSessionTranscriptMessageByIdentityStrict({
...scope,
message: { role: "assistant", content: "stale" },
}),
).resolves.toEqual({ kind: "rejected", reason: "session-rebound" });
});
it("pages raw events across appends and resets after replacement", async () => {
const scope = {
agentId: "main",