mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 19:28:46 +01:00
fix(runner): retain Grok accounting during cancellation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
a3e6d637fb
commit
17c9f7e091
2 files changed
+40
-1
No files matched your search
@@ -952,6 +952,45 @@ describe("Codex ACPX runtime adapter", () => {
|
||||
} finally { await port.close({ reason: "fixture done" }); }
|
||||
});
|
||||
|
||||
it.each(["cancel", "closeStream"] as const)("retains the bound Grok usage response during %s until the prompt settles", async (stop) => {
|
||||
const runtime = fakeRuntime();
|
||||
const durableRecord = { acpxRecordId: "record-1", acpSessionId: "backend-1", agentSessionId: "agent-1", lastRequestId: "previous" };
|
||||
const durableStore: AcpSessionStore = { load: vi.fn(async () => structuredClone(durableRecord) as never), save: vi.fn() };
|
||||
let created!: AcpRuntimeOptions;
|
||||
let settle!: (value: { status: "completed" }) => void;
|
||||
const result = new Promise<{ status: "completed" }>(resolve => { settle = resolve; });
|
||||
const options = openOptions(fakeCommand());
|
||||
options.profile = { ...options.profile, agent: "grok" };
|
||||
const port = await openCodexAcpxRuntime(options, { createRegistry: () => registry(), createStore: () => durableStore,
|
||||
createRuntime: value => { created = value; return runtime; } });
|
||||
vi.mocked(runtime.startTurn).mockImplementation(input => {
|
||||
durableRecord.lastRequestId = input.requestId;
|
||||
created.onAcpMessage?.("outbound", { id: 1, method: "session/prompt", params: { sessionId: "backend-1" } });
|
||||
return { requestId: input.requestId, promptStarted: Promise.resolve(), events: (async function* () {})(), result,
|
||||
cancel: async () => undefined, closeStream: async () => undefined };
|
||||
});
|
||||
try {
|
||||
const before = await port.getStatus();
|
||||
const turn = port.startTurn({ requestId: "runner-cancelled", text: "fixture" });
|
||||
await turn[stop]({ reason: "fixture stopped" });
|
||||
created.onAcpMessage?.("outbound", { id: 2, method: "session/prompt", params: { sessionId: "backend-1" } });
|
||||
created.onAcpMessage?.("inbound", { id: 1, result: { _meta: {
|
||||
sessionId: "backend-1", promptId: "prompt-1", requestId: "prompt-1",
|
||||
usage: { inputTokens: 50, outputTokens: 5, totalTokens: 55, cachedReadTokens: 20, cacheCreationTokens: 0,
|
||||
reasoningTokens: 2, modelCalls: 1, costUsdTicks: 10000000 },
|
||||
} } });
|
||||
settle({ status: "completed" });
|
||||
await turn.result;
|
||||
const after = await port.getStatus();
|
||||
expect(persistedAcpxTurnUsage(before, after, "runner-cancelled")).toMatchObject({
|
||||
cost: { amount: 0.001, currency: "USD", basis: "turn" },
|
||||
breakdown: { inputTokens: 30, outputTokens: 5, cachedReadTokens: 20, thoughtTokens: 0 },
|
||||
});
|
||||
created.onAcpMessage?.("inbound", { id: 1, error: { message: "late conflicting response" } });
|
||||
expect(await port.getStatus()).toEqual(after);
|
||||
} finally { settle({ status: "completed" }); await port.close({ reason: "fixture done" }); }
|
||||
});
|
||||
|
||||
it("reads verified status from durable state without draining live updates", async () => {
|
||||
const runtime = fakeRuntime();
|
||||
vi.mocked(runtime.getStatus!).mockReturnValue(new Promise(() => {}));
|
||||
|
||||
@@ -470,7 +470,7 @@ export async function openQualifiedAcpxRuntime(
|
||||
onSessionNotification: acceptGoalNotification,
|
||||
onAcpMessage: (direction, message) => {
|
||||
const active = extensionBoundary.active;
|
||||
if (active && !active.signal.aborted) promptUsage?.observe(direction, message);
|
||||
if (active && (direction === "inbound" || !active.signal.aborted)) promptUsage?.observe(direction, message);
|
||||
},
|
||||
spawnEnvironment: () => ({
|
||||
...definedEnvironment(options.launchEnvironment),
|
||||
|
||||
Reference in new issue
Block a user