feat: token usage tracking via llm_output hook#6
Merged
Conversation
…tion.timeoutSeconds (openclaw#46889) * feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds The hardcoded 5-minute (300s) compaction timeout causes large sessions to enter a death spiral where compaction repeatedly fails and the session grows indefinitely. This adds agents.defaults.compaction.timeoutSeconds to allow operators to override the compaction safety timeout. Default raised to 900s (15min) which is sufficient for sessions up to ~400k tokens. The resolved timeout is also used for the session write lock duration so locks don't expire before compaction completes. Fixes openclaw#38233 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * test: add resolveCompactionTimeoutMs tests Cover config resolution edge cases: undefined config, missing compaction section, valid seconds, fractional values, zero, negative, NaN, and Infinity. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: add timeoutSeconds to compaction Zod schema The compaction object schema uses .strict(), so setting the new timeoutSeconds config option would fail validation at startup. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: enforce integer constraint on compaction timeoutSeconds schema Prevents sub-second values like 0.5 which would floor to 0ms and cause immediate compaction timeout. Matches pattern of other integer timeout fields in the schema. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: clamp compaction timeout to Node timer-safe maximum Values above ~2.1B ms overflow Node's setTimeout to 1ms, causing immediate timeout. Clamp to MAX_SAFE_TIMEOUT_MS matching the pattern in agents/timeout.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: add FIELD_LABELS entry for compaction timeoutSeconds Maintains label/help parity invariant enforced by schema.help.quality.test.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: align compaction timeouts with abort handling * fix: land compaction timeout handling (openclaw#46889) (thanks @asyncjason) --------- Co-authored-by: Jason Separovic <[email protected]> Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
) Co-authored-by: webdevpraveen <[email protected]>
openclaw#41563) Update 5 references to the old "Clawdbot" name in skills/apple-reminders/SKILL.md and skills/imsg/SKILL.md. Co-authored-by: imanisynapse <[email protected]>
…atible APIs (openclaw#45497) Merged via squash. Prepared head SHA: 20fe05f Co-authored-by: sahancava <[email protected]> Co-authored-by: frankekn <[email protected]> Reviewed-by: @frankekn
…nclaw#45111) (openclaw#47119) Merged via squash. Prepared head SHA: d791190 Co-authored-by: thepagent <[email protected]> Reviewed-by: @frankekn
* fix(android): support android node `calllog.search` * fix(android): support android node calllog.search * fix(android): wire callLog through shared surfaces * fix: land Android callLog support (openclaw#44073) (thanks @lxk7280) --------- Co-authored-by: lixuankai <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
…tor, handle Long timestamps (openclaw#42588) Merged via squash. Prepared head SHA: 8ce59bb Co-authored-by: MonkeyLeeT <[email protected]> Co-authored-by: scoootscooob <[email protected]> Reviewed-by: @scoootscooob
…w#27910) * fix(web): handle 515 Stream Error during WhatsApp QR pairing getStatusCode() never unwrapped the lastDisconnect wrapper object, so login.errorStatus was always undefined and the 515 restart path in restartLoginSocket was dead code. - Add err.error?.output?.statusCode fallback to getStatusCode() - Export waitForCredsSaveQueue() so callers can await pending creds - Await creds flush in restartLoginSocket before creating new socket Fixes openclaw#3942 * test: update session mock for getStatusCode unwrap + waitForCredsSaveQueue Mirror the getStatusCode fix (err.error?.output?.statusCode fallback) in the test mock and export waitForCredsSaveQueue so restartLoginSocket tests work correctly. * fix(web): scope creds save queue per-authDir to avoid cross-account blocking The credential save queue was a single global promise chain shared by all WhatsApp accounts. In multi-account setups, a slow save on one account blocked credential writes and 515 restart recovery for unrelated accounts. Replace the global queue with a per-authDir Map so each account's creds serialize independently. waitForCredsSaveQueue() now accepts an optional authDir to wait on a single account's queue, or waits on all when omitted. Co-Authored-By: Claude Opus 4.6 <[email protected]> * test: use real Baileys v7 error shape in 515 restart test The test was using { output: { statusCode: 515 } } which was already handled before the fix. Updated to use the actual Baileys v7 shape { error: { output: { statusCode: 515 } } } to cover the new fallback path in getStatusCode. Co-Authored-By: Claude Code (Opus 4.6) <[email protected]> * fix(web): bound credential-queue wait during 515 restart Prevents restartLoginSocket from blocking indefinitely if a queued saveCreds() promise stalls (e.g. hung filesystem write). Co-Authored-By: Claude <[email protected]> * fix: clear flush timeout handle and assert creds queue in test Co-Authored-By: Claude <[email protected]> * fix: evict settled credsSaveQueues entries to prevent unbounded growth Co-Authored-By: Claude <[email protected]> * fix: share WhatsApp 515 creds flush handling (openclaw#27910) (thanks @asyncjason) --------- Co-authored-by: Jason Separovic <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
…aw#40996) Merged via squash. Prepared head SHA: 38d8048 Co-authored-by: xaeon2026 <[email protected]> Co-authored-by: frankekn <[email protected]> Reviewed-by: @frankekn
…nclaw#42931) (openclaw#47148) When auth is completely disabled (mode=none), requiring device pairing for Control UI operator sessions adds friction without security value since any client can already connect without credentials. Add authMode parameter to shouldSkipControlUiPairing so the bypass fires only for Control UI + operator role + auth.mode=none. This avoids the openclaw#43478 regression where a top-level OR disabled pairing for ALL websocket clients.
…#47274) * fix: preserve Telegram chunk word boundaries * fix: address Telegram chunking review feedback * fix: preserve Telegram retry separators * fix: preserve Telegram chunking boundaries (openclaw#47274)
* feat(feishu): add ACP session support * fix(feishu): preserve sender-scoped ACP rebinding * fix(feishu): recover sender scope from bound ACP sessions * fix(feishu): support DM ACP binding placement * feat(feishu): add current-conversation session binding * fix(feishu): avoid DM parent binding fallback * fix(feishu): require canonical topic sender ids * fix(feishu): honor sender-scoped ACP bindings * fix(feishu): allow user-id ACP DM bindings * fix(feishu): recover user-id ACP DM bindings
* ACP: fail closed on conflicting tool identity hints * ACP: restore rawInput fallback for safe tool resolution * ACP tests: cover rawInput-only safe tool approval
* Nodes: recheck queued actions before delivery * Nodes tests: cover pull-time policy recheck * Nodes tests: type node policy mocks explicitly
…#47413) * Plugins: preserve scoped ids and reserve bundled duplicates * Changelog: add plugin scoped id note * Plugins: harden scoped install ids * Plugins: reserve scoped install dirs * Plugins: migrate legacy scoped update ids
* CLI: lazy-load channel subcommand handlers * Channels: defer add command dependencies * CLI: skip status JSON plugin preload * CLI: cover status JSON route preload * Status: trim JSON security audit path * Status: update JSON fast-path tests * CLI: cover root help fast path * CLI: fast-path root help * Status: keep JSON security parity * Status: restore JSON security tests * CLI: document status plugin preload * Channels: reuse Telegram account import
…#46787) * Integrations: harden inbound callback and allowlist handling * Integrations: address review follow-ups * Update CHANGELOG.md * Mattermost: avoid command-gating open button callbacks
* ACP: require admin scope for mutating internal actions * ACP: cover operator admin mutating actions * ACP: gate internal status behind admin scope
The model selector was using just the model ID (e.g. "gpt-5.2") as the
option value. When sent to sessions.patch, the server would fall back to
the session's current provider ("anthropic") yielding "anthropic/gpt-5.2"
instead of "openai/gpt-5.2".
Now option values use "provider/model" format, and resolveModelOverrideValue
and resolveDefaultModelValue also return the full provider-prefixed key so
selected state stays consistent.
The default option showed 'Default (openai/gpt-5.2)' while individual options used the friendlier 'gpt-5.2 · openai' format.
… refactor) Upstream moved WA files from src/web/ to extensions/whatsapp/. Updated all import paths for our custom files (brazil-jid-resolver, contact-names, wa-streaming-utils, process-message, server-chat mirror). Conflicts resolved: - send-api.ts: upstream import paths + our normalizeE164 - dispatch-from-config.ts: upstream inboundClaimContext + our hooks - server-methods/chat.ts: upstream /btw refactor + our mirror patch - process-message.ts: upstream import paths + our streaming imports Rollback: git reset --hard alpha-pre-upstream-update Co-authored-by: Bob
…f relative source paths
- Add optional bundled cluster filtering to listBundledPluginBuildEntries() to skip extensions with native dependencies (matrix, whatsapp, etc.) that cannot be bundled by rolldown on all platforms - Filter plugin-sdk entries for optional clusters to prevent native .node binary bundling failures - Matches upstream's shouldBuildBundledCluster() pattern - butley-api dist output now correctly contains both the tool registration AND the llm_output hook in the register() default export
…utput" This reverts commit 4558c9d.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements automatic token usage tracking in the butley-api extension.
Changes
butley-api extension (
extensions/butley-api/index.ts)llm_outputhook: fires after every LLM call, extracts token counts, model and provider, calls Convex mutation fire-and-forget (non-blocking)butley-api tool (
extensions/butley-api/src/butley-api-tool.ts)get_token_usage: queries current month usage for the calling installationlist_billing_periods: lists all billing periods