




















@@ -239,66 +239,12 @@ describe("runReplyAgent auto-compaction token update", () => {
239239return { typing, sessionCtx, resolvedQueue, followupRun };
240240}
241241242-it("updates totalTokens from lastCallUsage even without compaction", async () => {
243-const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-usage-last-"));
244-const storePath = path.join(tmp, "sessions.json");
245-const sessionKey = "main";
246-const sessionEntry = {
247-sessionId: "session",
248-updatedAt: Date.now(),
249-totalTokens: 50_000,
250-};
251-252-await seedSessionStore({ storePath, sessionKey, entry: sessionEntry });
253-254-runEmbeddedPiAgentMock.mockResolvedValue({
255-payloads: [{ text: "ok" }],
256-meta: {
257-agentMeta: {
258-// Tool-use loop: accumulated input is higher than last call's input
259-usage: { input: 75_000, output: 5_000, total: 80_000 },
260-lastCallUsage: { input: 55_000, output: 2_000, total: 57_000 },
261-},
262-},
263-});
264-265-const { typing, sessionCtx, resolvedQueue, followupRun } = createBaseRun({
266- storePath,
267- sessionEntry,
268-});
269-270-await runReplyAgent({
271-commandBody: "hello",
272- followupRun,
273-queueKey: "main",
274- resolvedQueue,
275-shouldSteer: false,
276-shouldFollowup: false,
277-isActive: false,
278-isStreaming: false,
279- typing,
280- sessionCtx,
281- sessionEntry,
282-sessionStore: { [sessionKey]: sessionEntry },
283- sessionKey,
284- storePath,
285-defaultModel: "anthropic/claude-opus-4-6",
286-agentCfgContextTokens: 200_000,
287-resolvedVerboseLevel: "off",
288-isNewSession: false,
289-blockStreamingEnabled: false,
290-resolvedBlockStreamingBreak: "message_end",
291-shouldInjectGroupIntro: false,
292-typingMode: "instant",
293-});
294-295-const stored = JSON.parse(await fs.readFile(storePath, "utf-8"));
296-// totalTokens should use lastCallUsage (55k), not accumulated (75k)
297-expect(stored[sessionKey].totalTokens).toBe(55_000);
298-});
299-300-it("reports live diagnostic context from promptTokens, not provider usage totals", async () => {
301-const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-usage-diagnostic-"));
242+async function runBaseReplyWithAgentMeta(params: {
243+agentMeta: Record<string, unknown>;
244+collectDiagnostics?: boolean;
245+tmpPrefix: string;
246+}) {
247+const tmp = await fs.mkdtemp(path.join(os.tmpdir(), params.tmpPrefix));
302248const storePath = path.join(tmp, "sessions.json");
303249const sessionKey = "main";
304250const sessionEntry = {
@@ -312,18 +258,16 @@ describe("runReplyAgent auto-compaction token update", () => {
312258runEmbeddedPiAgentMock.mockResolvedValue({
313259payloads: [{ text: "ok" }],
314260meta: {
315-agentMeta: {
316-usage: { input: 75_000, output: 5_000, cacheRead: 25_000, total: 105_000 },
317-lastCallUsage: { input: 55_000, output: 2_000, cacheRead: 25_000, total: 82_000 },
318-promptTokens: 44_000,
319-},
261+agentMeta: params.agentMeta,
320262},
321263});
322264323265const diagnostics: DiagnosticEventPayload[] = [];
324-const unsubscribe = onInternalDiagnosticEvent((event) => {
325-diagnostics.push(event);
326-});
266+const unsubscribe = params.collectDiagnostics
267+ ? onInternalDiagnosticEvent((event) => {
268+diagnostics.push(event);
269+})
270+ : undefined;
327271const { typing, sessionCtx, resolvedQueue, followupRun } = createBaseRun({
328272 storePath,
329273 sessionEntry,
@@ -355,10 +299,39 @@ describe("runReplyAgent auto-compaction token update", () => {
355299typingMode: "instant",
356300});
357301} finally {
358-unsubscribe();
302+unsubscribe?.();
359303}
360304305+const stored = JSON.parse(await fs.readFile(storePath, "utf-8"));
361306const usageEvent = diagnostics.find((event) => event.type === "model.usage");
307+return { sessionKey, stored, usageEvent };
308+}
309+310+it("updates totalTokens from lastCallUsage even without compaction", async () => {
311+const { sessionKey, stored } = await runBaseReplyWithAgentMeta({
312+tmpPrefix: "openclaw-usage-last-",
313+agentMeta: {
314+// Tool-use loop: accumulated input is higher than last call's input
315+usage: { input: 75_000, output: 5_000, total: 80_000 },
316+lastCallUsage: { input: 55_000, output: 2_000, total: 57_000 },
317+},
318+});
319+320+// totalTokens should use lastCallUsage (55k), not accumulated (75k)
321+expect(stored[sessionKey].totalTokens).toBe(55_000);
322+});
323+324+it("reports live diagnostic context from promptTokens, not provider usage totals", async () => {
325+const { usageEvent } = await runBaseReplyWithAgentMeta({
326+tmpPrefix: "openclaw-usage-diagnostic-",
327+collectDiagnostics: true,
328+agentMeta: {
329+usage: { input: 75_000, output: 5_000, cacheRead: 25_000, total: 105_000 },
330+lastCallUsage: { input: 55_000, output: 2_000, cacheRead: 25_000, total: 82_000 },
331+promptTokens: 44_000,
332+},
333+});
334+362335expect(usageEvent).toMatchObject({
363336type: "model.usage",
364337usage: {
@@ -376,72 +349,21 @@ describe("runReplyAgent auto-compaction token update", () => {
376349});
377350378351it("falls back to last-call prompt usage for live diagnostic context", async () => {
379-const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-usage-diagnostic-last-"));
380-const storePath = path.join(tmp, "sessions.json");
381-const sessionKey = "main";
382-const sessionEntry = {
383-sessionId: "session",
384-updatedAt: Date.now(),
385-totalTokens: 50_000,
386-};
387-388-await seedSessionStore({ storePath, sessionKey, entry: sessionEntry });
389-390-runEmbeddedPiAgentMock.mockResolvedValue({
391-payloads: [{ text: "ok" }],
392-meta: {
393-agentMeta: {
394-usage: { input: 75_000, output: 5_000, cacheRead: 25_000, total: 105_000 },
395-lastCallUsage: {
396-input: 55_000,
397-output: 2_000,
398-cacheRead: 25_000,
399-cacheWrite: 1_000,
400-total: 83_000,
401-},
352+const { usageEvent } = await runBaseReplyWithAgentMeta({
353+tmpPrefix: "openclaw-usage-diagnostic-last-",
354+collectDiagnostics: true,
355+agentMeta: {
356+usage: { input: 75_000, output: 5_000, cacheRead: 25_000, total: 105_000 },
357+lastCallUsage: {
358+input: 55_000,
359+output: 2_000,
360+cacheRead: 25_000,
361+cacheWrite: 1_000,
362+total: 83_000,
402363},
403364},
404365});
405366406-const diagnostics: DiagnosticEventPayload[] = [];
407-const unsubscribe = onInternalDiagnosticEvent((event) => {
408-diagnostics.push(event);
409-});
410-const { typing, sessionCtx, resolvedQueue, followupRun } = createBaseRun({
411- storePath,
412- sessionEntry,
413-});
414-415-try {
416-await runReplyAgent({
417-commandBody: "hello",
418- followupRun,
419-queueKey: "main",
420- resolvedQueue,
421-shouldSteer: false,
422-shouldFollowup: false,
423-isActive: false,
424-isStreaming: false,
425- typing,
426- sessionCtx,
427- sessionEntry,
428-sessionStore: { [sessionKey]: sessionEntry },
429- sessionKey,
430- storePath,
431-defaultModel: "anthropic/claude-opus-4-6",
432-agentCfgContextTokens: 200_000,
433-resolvedVerboseLevel: "off",
434-isNewSession: false,
435-blockStreamingEnabled: false,
436-resolvedBlockStreamingBreak: "message_end",
437-shouldInjectGroupIntro: false,
438-typingMode: "instant",
439-});
440-} finally {
441-unsubscribe();
442-}
443-444-const usageEvent = diagnostics.find((event) => event.type === "model.usage");
445367expect(usageEvent).toMatchObject({
446368type: "model.usage",
447369usage: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。