Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions src/node/services/agentSession.admissionGates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ describe("AgentSession.sendMessage (admission gates)", () => {
it("refuses at the pre-persist gate before any row lands when the epoch is stale", async () => {
const workspaceId = "ws-epoch-prepersist";
const { session, historyService, streamMessage } = await createSessionHarness(workspaceId);
const appendMany = spyOn(historyService, "appendManyToHistory");
const publication = spyOn(historyService, "acceptCompactionReplacement");
let acceptedCalls = 0;

const result = await session.sendMessage(
"family trigger",
{ model: TEST_MODEL, agentId: "exec" },
{
acceptanceOrigin: "automatic",
synthetic: true,
preTurnMessages: [
createMuxMessage("family-payload-stale", "assistant", "untrusted payload", {
Expand All @@ -94,7 +95,7 @@ describe("AgentSession.sendMessage (admission gates)", () => {
});
// Pre-acceptance refusal: nothing persisted, nothing accepted, no stream.
expect(acceptedCalls).toBe(0);
expect(appendMany).not.toHaveBeenCalled();
expect(publication).not.toHaveBeenCalled();
expect(streamMessage).not.toHaveBeenCalled();
const history = await historyService.getHistoryFromLatestBoundary(workspaceId);
expect(history.success ? history.data : ["unexpected"]).toHaveLength(0);
Expand All @@ -103,13 +104,25 @@ describe("AgentSession.sendMessage (admission gates)", () => {
it("invokes the cancellation hook when the caller probe goes stale before acceptance", async () => {
const workspaceId = "ws-caller-stale-cancel";
const { session, historyService, streamMessage } = await createSessionHarness(workspaceId);
const appendMany = spyOn(historyService, "appendManyToHistory");
let published = false;
const publish = historyService.acceptCompactionReplacement.bind(historyService);
spyOn(historyService, "acceptCompactionReplacement").mockImplementationOnce(
(id, capture, operation, observer) =>
publish(id, capture, operation, {
...observer,
onCommitted: (receipt) => {
observer.onCommitted(receipt);
published = true;
},
})
);
const canceled: string[] = [];

const result = await session.sendMessage(
"peer trigger",
{ model: TEST_MODEL, agentId: "exec" },
{
acceptanceOrigin: "automatic",
synthetic: true,
preTurnMessages: [
createMuxMessage("peer-payload-stale", "assistant", "untrusted payload", {
Expand All @@ -121,7 +134,7 @@ describe("AgentSession.sendMessage (admission gates)", () => {
// which must roll the rows back AND surface the refusal through the cancellation hook —
// a queued peer send's caller already returned success and this hook carries its budget
// refund; without it the reservation would leak.
admissionStale: () => appendMany.mock.calls.length > 0,
admissionStale: () => published,
onCanceled: (reason: string) => {
canceled.push(reason);
},
Expand All @@ -138,7 +151,18 @@ describe("AgentSession.sendMessage (admission gates)", () => {
it("keeps the charge when a stale send's rollback did not commit", async () => {
const workspaceId = "ws-caller-stale-rollback-failed";
const { session, historyService, streamMessage } = await createSessionHarness(workspaceId);
const appendMany = spyOn(historyService, "appendManyToHistory");
let published = false;
const publish = historyService.acceptCompactionReplacement.bind(historyService);
spyOn(historyService, "acceptCompactionReplacement").mockImplementationOnce(
(id, capture, operation, observer) =>
publish(id, capture, operation, {
...observer,
onCommitted: (receipt) => {
observer.onCommitted(receipt);
published = true;
},
})
);
// Rollback deletion fails and the rows verifiably REMAIN: the cancellation hook must not
// fire — a refunded reservation with durable rows would let the payload enter provider
// context after a resume while no longer counting against the sender's budget.
Expand All @@ -152,14 +176,15 @@ describe("AgentSession.sendMessage (admission gates)", () => {
"peer trigger",
{ model: TEST_MODEL, agentId: "exec" },
{
acceptanceOrigin: "automatic",
synthetic: true,
preTurnMessages: [
createMuxMessage("peer-payload-stuck", "assistant", "untrusted payload", {
timestamp: 1,
synthetic: true,
}),
],
admissionStale: () => appendMany.mock.calls.length > 0,
admissionStale: () => published,
onCanceled: (reason: string) => {
canceled.push(reason);
},
Expand Down Expand Up @@ -196,6 +221,7 @@ describe("AgentSession.sendMessage (admission gates)", () => {
"hello",
{ model: TEST_MODEL, agentId: "exec" },
{
acceptanceOrigin: "automatic",
synthetic: true,
onAccepted: () => {
acceptedCalls += 1;
Expand Down
Loading
Loading