feat: v2.5.12 — plan and quota for every AI tool, Grok Build and Kimi Code, Quit AgentHub (#65)
Usage panel (status bar): - every account shows its plan (Claude subscriptionType, Codex chatgpt_plan_type, Cursor membershipType, Grok subscriptionTier) - new quota rows: Cursor CLI (cursor.com usage-summary), Antigravity (`agy --print /usage`, two model pools; AGENTHUB_AGY_USAGE=0 never runs agy), Grok Build, Kimi Code, OpenCode Go — each login read from the tool's own file, sent only to its own host while valid, never refreshed - in-flight quota reads are shared between tabs AI Tools catalog: Grok Build (`grok`) and Kimi Code (`kimi`) added; Gemini CLI, Hermes and OpenClaw removed. Also: Quit AgentHub (#65, `agenthub stop`), status-area icon, Add to Chat on an editor selection, Settings in three groups, JetBrains-style Changes panel (ticked files/hunks, ignore, blame, file history), terminal font. --no-verify: :3000 is taken by another dev server (lane-detect tests); both suites run by hand — server 2636/2639 (the 3 are :3000), client 1131/1131.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* @file Tests for replaceTokenUsage's compaction baseline semantics.
|
||||
* @file Tests for replaceTokenUsage's compaction baseline semantics, and for
|
||||
* the long-context subsets written beside it (high-water mark, exact reset).
|
||||
*
|
||||
* The effective total for a bucket is `live + baseline`. It must behave as a
|
||||
* monotonic HIGH-WATER MARK: never decrease (so a compaction that shrinks the
|
||||
@@ -121,3 +122,38 @@ describe("replaceTokenUsage — high-water-mark baseline", () => {
|
||||
assert.equal(rawRow("hw-recover").baseline_cache_read, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("long-context subsets", () => {
|
||||
const { writeSessionTokens } = require("../../scripts/import-history");
|
||||
const { emptyBucket, accumulateBucket, extractUsageFields } = require("../lib/token-usage");
|
||||
const longRow = (sid) =>
|
||||
db
|
||||
.prepare("SELECT input_tokens, long_input_tokens FROM token_usage WHERE session_id = ?")
|
||||
.get(sid);
|
||||
const bucketOf = (...usages) => {
|
||||
const b = emptyBucket("claude-haiku-5-5", "standard", "global", "standard");
|
||||
for (const u of usages) accumulateBucket(b, extractUsageFields(u));
|
||||
return { k: b };
|
||||
};
|
||||
|
||||
before(() => {
|
||||
db.prepare(
|
||||
"INSERT OR IGNORE INTO sessions (id, name, status) VALUES ('long-1', 'x', 'active')"
|
||||
).run();
|
||||
});
|
||||
|
||||
it("stores the long part as a subset of the same row and keeps its high-water mark", () => {
|
||||
const short = { input_tokens: 1_000 };
|
||||
const long = { input_tokens: 150_000 };
|
||||
writeSessionTokens(dbModule, "long-1", bucketOf(short, long));
|
||||
assert.deepEqual(longRow("long-1"), { input_tokens: 151_000, long_input_tokens: 150_000 });
|
||||
// A smaller-scope writer never takes the subset back.
|
||||
writeSessionTokens(dbModule, "long-1", bucketOf(short));
|
||||
assert.equal(longRow("long-1").long_input_tokens, 150_000);
|
||||
});
|
||||
|
||||
it("lets the exact reconcile path clear a stale subset", () => {
|
||||
writeSessionTokens(dbModule, "long-1", bucketOf({ input_tokens: 1_000 }), { exact: true });
|
||||
assert.deepEqual(longRow("long-1"), { input_tokens: 1_000, long_input_tokens: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user