@inetafrica/open-claudia 3.0.5 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/core/runner.js +15 -0
- package/package.json +1 -1
- package/test-provider-language.js +5 -5
- package/test-usage-accounting.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v3.0.6 — cache-bust burn is now a one-line query
|
|
4
|
+
|
|
5
|
+
- **Usage records carry a `coldStart` flag.** The restart→cache-bust burn (each bot restart re-bills the whole accumulated window as cache *writes* instead of ~13×-cheaper cache *reads*) was invisible to every token-count analysis — same token counts, different price class — and took a forensic reconstruction of the 22-day usage history to quantify (~8% of all spend, ~$12/day at its worst). Now the first usage record a process writes for each session is tagged `coldStart: true`: a cold start mid-conversation is the restart fingerprint, so the burn is one jq filter away instead of a modelling exercise. Records already carried `cacheReadTokens`/`cacheCreationTokens`; this adds the missing "was the cache necessarily cold?" dimension.
|
|
6
|
+
- **Agent-space / OpenClaw:** no new deps, no new env, no schema change (additive JSONL field); Docker image builds identically. `usageSessionColdStart` covered by new assertions in `test-usage-accounting.js`.
|
|
7
|
+
|
|
3
8
|
## v3.0.5 — the conflict handler owns the poll
|
|
4
9
|
|
|
5
10
|
- **Hiccup heals no longer fight the 409 backoff.** During a 409 conflict streak the conflict handler pauses polling and retries on its own schedule — but a network hiccup (`ECONNRESET`) arriving mid-streak would schedule its own heal, which called `startPolling` straight back into the conflict and reset the streak clock. This was the live v2.15 burn mechanism: its own ghost long-poll after an ECONNRESET heal raised a 409, the old code exited on any 409, launchd respawned it (~1/hr, 208 lifetime startups), and every restart busted the prompt cache so the next turn re-billed the whole accumulated window uncached. Hiccup handling is now a no-op while a conflict streak is active — the conflict handler owns the poll lifecycle.
|
package/core/runner.js
CHANGED
|
@@ -183,6 +183,7 @@ function logTurnUsage(state, usage, costUsd, announce, opts = {}) {
|
|
|
183
183
|
billedContextTokens: parts.context,
|
|
184
184
|
costUsd: typeof costUsd === "number" ? costUsd : 0,
|
|
185
185
|
};
|
|
186
|
+
if (typeof opts.coldStart === "boolean") record.coldStart = opts.coldStart;
|
|
186
187
|
if (opts.rawUsage && opts.rawUsage !== usage) {
|
|
187
188
|
const raw = usageParts(opts.rawUsage, backend);
|
|
188
189
|
record.rawInputTokens = raw.input;
|
|
@@ -1425,6 +1426,18 @@ async function persistForegroundTranscript(result, runContext, state = currentSt
|
|
|
1425
1426
|
);
|
|
1426
1427
|
}
|
|
1427
1428
|
|
|
1429
|
+
// Sessions this PROCESS has already recorded usage for. The first record per
|
|
1430
|
+
// session since boot is a cold start: the prompt cache is necessarily cold, so
|
|
1431
|
+
// the whole accumulated window re-bills as cache writes (~13x the read rate).
|
|
1432
|
+
// Restart burn is therefore one query away: coldStart=true mid-conversation.
|
|
1433
|
+
const warmUsageSessions = new Set();
|
|
1434
|
+
function usageSessionColdStart(sessionId, warmSet = warmUsageSessions) {
|
|
1435
|
+
if (!sessionId) return true;
|
|
1436
|
+
if (warmSet.has(sessionId)) return false;
|
|
1437
|
+
warmSet.add(sessionId);
|
|
1438
|
+
return true;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1428
1441
|
async function persistForegroundUsage(result, runContext, state = currentState(), store = chatContext.getStore()) {
|
|
1429
1442
|
if (!result.usage) return;
|
|
1430
1443
|
const previousUsageBySession = structuredClone(state.usageBySession || {});
|
|
@@ -1468,6 +1481,7 @@ async function persistForegroundUsage(result, runContext, state = currentState()
|
|
|
1468
1481
|
usageScope,
|
|
1469
1482
|
liveContextTokens,
|
|
1470
1483
|
rawUsage,
|
|
1484
|
+
coldStart: usageSessionColdStart(usageSessionId),
|
|
1471
1485
|
strict: true,
|
|
1472
1486
|
},
|
|
1473
1487
|
);
|
|
@@ -2056,6 +2070,7 @@ module.exports = {
|
|
|
2056
2070
|
effectiveCompactThreshold,
|
|
2057
2071
|
usageParts,
|
|
2058
2072
|
peakContextTokens,
|
|
2073
|
+
usageSessionColdStart,
|
|
2059
2074
|
clampUsageDelta,
|
|
2060
2075
|
normalizeCodexUsage,
|
|
2061
2076
|
applyUsageToState,
|
package/package.json
CHANGED
|
@@ -32,11 +32,11 @@ assert.doesNotMatch(setup, /Your Claude Code bot is connected|Description=Claude
|
|
|
32
32
|
|
|
33
33
|
const pkg = JSON.parse(read("package.json"));
|
|
34
34
|
assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
35
|
-
// 3.0.
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
assert.strictEqual(pkg.version, "3.0.
|
|
35
|
+
// 3.0.6 approved by Sumeet 2026-07-12 ("Yeh you can do that too" — coldStart
|
|
36
|
+
// flag on usage records so restart cache-bust burn, measured at ~8% of all
|
|
37
|
+
// spend from the 22-day history, is a one-line query instead of a forensic
|
|
38
|
+
// reconstruction).
|
|
39
|
+
assert.strictEqual(pkg.version, "3.0.6", "release version must remain unchanged without explicit approval");
|
|
40
40
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
41
41
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
42
42
|
}
|
package/test-usage-accounting.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
applyUsageToState,
|
|
11
11
|
shouldAutoCompact,
|
|
12
12
|
peakContextTokens,
|
|
13
|
+
usageSessionColdStart,
|
|
13
14
|
} = require("./core/runner");
|
|
14
15
|
const stateApi = require("./core/state");
|
|
15
16
|
const { normalizeUsageRecord, loadUsageHistory, USAGE_HISTORY_FILE } = require("./core/usage-log");
|
|
@@ -176,5 +177,17 @@ assert.strictEqual(peakContextTokens([
|
|
|
176
177
|
{ type: "usage", scope: "provider_call", usage: { input_tokens: 70, cached_input_tokens: 30, output_tokens: 5 } },
|
|
177
178
|
], "codex"), 70, "codex context counts input only, matching usageParts");
|
|
178
179
|
|
|
180
|
+
// usageSessionColdStart: the first usage record per session since process boot
|
|
181
|
+
// is a cold start (prompt cache empty — the restart-burn fingerprint); every
|
|
182
|
+
// later record for that session in the same process is warm.
|
|
183
|
+
{
|
|
184
|
+
const warm = new Set();
|
|
185
|
+
assert.strictEqual(usageSessionColdStart("sess-a", warm), true, "first record for a session is cold");
|
|
186
|
+
assert.strictEqual(usageSessionColdStart("sess-a", warm), false, "second record for the same session is warm");
|
|
187
|
+
assert.strictEqual(usageSessionColdStart("sess-b", warm), true, "a different session is cold on its first record");
|
|
188
|
+
assert.strictEqual(usageSessionColdStart(null, warm), true, "no session id → always treated cold");
|
|
189
|
+
assert.strictEqual(usageSessionColdStart(null, warm), true, "null never warms");
|
|
190
|
+
}
|
|
191
|
+
|
|
179
192
|
fs.rmSync(configDir, { recursive: true, force: true });
|
|
180
193
|
console.log("usage accounting OK");
|