feat(provider): live usage events, available-commands subscription, and slash-command helpers#35
Draft
DaniAkash wants to merge 1 commit into
Draft
feat(provider): live usage events, available-commands subscription, and slash-command helpers#35DaniAkash wants to merge 1 commit into
DaniAkash wants to merge 1 commit into
Conversation
…nd slash-command helpers Builds on the runtime additions in openclaw/acpx#345 (cost, breakdown, availableCommands on status events + AcpRuntimeStatus). Provider work: - Add EventEmitter on AcpxProvider with 'usage' and 'availableCommands' channels. Provider records each event per session into in-memory maps and re-emits to subscribers, so multiple consumers (context bar, telemetry, auto-compact watcher) can react independently of which stream is currently being consumed. - Sync getters AcpxProvider.getUsage() and getAvailableCommands() return the most recent snapshot for a session without spawning the agent. - New AcpxProvider.runSlashCommand({ name, sessionKey?, agent? }) sends a slash-command prompt and drains the resulting turn so any follow-on usage_update / available_commands_update events propagate naturally through the same event channel. - AcpxProvider.compact({ sessionKey? }) wraps runSlashCommand and picks up the advertised /compact name from the available-commands list. - EventTranslator gains onUsageUpdate / onAvailableCommands callback hooks. The language model wires them into the provider's record/emit methods on every turn. - accumulatedUsage() now reads the breakdown from _meta.usage when the agent populates it and falls back to top-level 'used' for totalTokens when it doesn't. The previous transport hack that shoehorned 'size' into cachedInputTokens is removed. 'size' (context window ceiling) and 'cost' now ride on providerMetadata.acpx.contextWindow and .cost on the finish part. - New AcpxUsageSnapshot public type plus AcpxProviderEvents map for the EventEmitter generic. The acpx/runtime types (AcpRuntimeAvailableCommand, AcpRuntimeUsageBreakdown, AcpRuntimeUsageCost, AcpRuntimeSessionUsage) are re-exported from this package. Bumps acpx-ai-provider 0.0.6 -> 0.1.0 and the acpx peer dep to >=0.11.0 (the upstream release that will carry the new contract). The dev dep on acpx is intentionally omitted in this draft because acpx@0.11.0 is not on npm yet; once openclaw/acpx#345 merges and releases, the dev dep should be added back as ^0.11.0. Closes a downstream gap for live context-window display and agent-initiated compaction UI on top of acpx-ai-provider.
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
Surfaces the new runtime channels to AI SDK consumers and adds the helpers Herbie (or any downstream chat UI) needs to render a live context-window bar and trigger agent-initiated compaction.
What changed
AcpxProviderpublic surfaceprovider.events: EventEmitter<AcpxProviderEvents>'usage'fires on everyusage_update;'availableCommands'fires on everyavailable_commands_update.provider.getUsage(sessionKey?)AcpxUsageSnapshotfor a session.provider.getAvailableCommands(sessionKey?)provider.runSlashCommand({ name, sessionKey?, agent?, timeoutMs?, signal? })provider.compact({ sessionKey?, agent? })/compact-style command fromavailableCommandsand callsrunSlashCommand. Throws when no compact-like command is advertised.AcpxUsageSnapshot,AcpxProviderEventsacpx/runtimetypes (AcpRuntimeAvailableCommand,AcpRuntimeUsageBreakdown,AcpRuntimeUsageCost,AcpRuntimeSessionUsage).EventTranslator(inconvert-events.ts)onUsageUpdate(event)andonAvailableCommands(commands)callback hooks on the constructor. The language model wires them intoprovider.recordUsage/provider.recordAvailableCommandson each turn, which in turn updates the per-session maps and emits onprovider.events.handleStatusnow translatesavailable_commands_updateinto a callback fire (previously it was a no-op).lastUsageEventstores the fullusage_updateevent sofinish()can place the context-window ceiling and cost ontoproviderMetadata.acpxinstead of mangling them throughLanguageModelV2Usage.accumulatedUsage()mapping fixThe provider previously transported
size(context-window ceiling) throughcachedInputTokens. That was a hack —cachedInputTokensis supposed to be the prompt-cache hit count. The new mapping:_meta.usage.inputTokensinputTokens_meta.usage.outputTokensoutputTokens_meta.usage.totalTokens(orusedfallback)totalTokens_meta.usage.cachedReadTokenscachedInputTokens_meta.usage.thoughtTokensreasoningTokensusage_update.sizeproviderMetadata.acpx.contextWindowusage_update.costproviderMetadata.acpx.costBehavior change for any consumer reading
usage.cachedInputTokensto mean "context window size" — switch toproviderMetadata.acpx.contextWindow. Documented in the new README section and the migration callout.Other
packages/acpx-ai-provider/package.json— version0.0.6→0.1.0; peerDepacpx >=0.11.0; dev dep onacpxremoved pending upstream release.biome.json— addsconvert-events.tsto thenoExcessiveLinesPerFile: offoverride list (existing pattern for orchestrator files).Test plan
bun run typecheckclean across all three packages.bun run lintclean (one pre-existing warning unrelated to this PR).bun test packages/acpx-ai-provider/test/{unit,integration,contract}— 197 pass / 0 fail across 17 files.test/unit/usage-events.test.tscovers: usage event emission with cost + breakdown,getUsage()sync read, availableCommands event emission,runSlashCommandhappy path + failed-turn error,compact()throws when no compact command is advertised,compact()sends the advertised name as a slash command.test/integration/{do-stream,do-generate,with-stream-text,with-generate-text}.test.tsupdated to assert the newproviderMetadata.acpx.contextWindowshape instead of the oldcachedInputTokens === sizehack.acpx@0.11.0ships from feat(runtime): surface cost, usage breakdown, and available commands on status events and getStatus openclaw/acpx#345.Coordination with upstream
acpx@0.11.0→ re-pindevDependencies.acpx = "^0.11.0"in this PR → un-draft and ship.convert-events.ts,provider.ts, and the type re-exports intypes.ts/index.ts.Out of scope (deliberate)
provider.events.on('usage', ...). Belongs in the consumer (Herbie or other), not here.AvailableCommandInputthrough.compact/condense.compact()matches those two names; consumers wanting different commands can userunSlashCommandwith the explicit name fromgetAvailableCommands().