feat(discord): add /usage slash command backed by kiro-cli ACP usage query#1392
Conversation
…query
Adds a Discord /usage slash command that reports the backend agent's
account-level usage and billing (plan, credits consumed vs limit,
overage charges, billing cycle reset).
- protocol.rs: UsageReport/UsageBreakdown types + parse_usage_report()
for the response of kiro-cli's /usage command executed over ACP.
Respects hasLimit=false (pooled/no-cap accounts hide the limit).
- connection.rs: store agentInfo.name from initialize; get_usage()
sends the _kiro.dev/commands/execute extension request with the
adjacently-tagged TuiCommand shape {command: "usage", args: {}}.
Gated on the agent name containing "kiro" because a malformed or
unrecognized shape is a deserialization error that terminates the
ACP connection rather than returning a JSON-RPC error. Never retried.
- pool.rs: SessionPool::get_usage(thread_id) plumbing.
- discord.rs: register /usage globally, defer ephemeral response
(ACP round-trip can exceed Discord's 3s deadline), render a compact
report with progress bar and overage warning.
Non-kiro backends and threads without an active session get a clear
ephemeral error message.
This comment has been minimized.
This comment has been minimized.
Replace the plain-text reply with a CreateEmbed: plan name as title, breakdown lines as description, billing cycle reset in the footer, and a green/red sidebar depending on whether any breakdown is over its plan limit. Still ephemeral — only the invoking user sees it. Error paths remain plain-text ephemeral messages.
This comment has been minimized.
This comment has been minimized.
Live payload from the pr1392 preview (B0, 2026-07-13) carries limit=10000.0 but no hasLimit field; the unwrap_or(false) default dropped the cap and progress bar for an account 130% over limit. Treat a missing hasLimit as 'has a limit if a numeric limit is present', keeping the explicit hasLimit=false pooled-account sentinel behavior. Two regression tests added (one with the exact captured payload).
This comment has been minimized.
This comment has been minimized.
Discord clients render embed descriptions smaller than message content. Put the report title+body in content (normal font) and keep a minimal embed as the green/red color strip with the billing-cycle footer. Pinned by usage_reply_body_in_content_not_embed.
|
Note LGTM ✅ — Clean, well-guarded feature addition with solid test coverage and correct Discord interaction flow. What This PR DoesAdds a Discord How It Works
Findings
Finding Details🟢 F1: Agent-name gateThe safety note in the PR description correctly identifies the critical risk: a malformed 🟢 F2: Deferred ephemeral interactionFollows the same proven pattern as the existing 🟢 F3: hasLimit fallbackReal-world edge case: some kiro-cli versions omit 🟢 F4: Test coverageTests cover: full report, no-limit enterprise, missing 🟢 F5: UX — body in contentDiscord embeds render description text at a smaller font. Putting the usage numbers in Baseline Check
What's Good (🟢)
5️⃣ Three Reasons We Might Not Need This PR
|
Summary
Adds a Discord
/usageslash command that reports the backend agent's account-level usage and billing: plan name, credits used vs plan limit, overage charges, and billing cycle reset date.How it works
kiro-cli exposes its
/usageslash command over ACP via the_kiro.dev/commands/executeextension method (verified against kiro-cli 2.12.1). The response carries structured JSON (planName,usageBreakdowns[],overageCharges, …) rather than rendered text.acp/protocol.rs—UsageReport/UsageBreakdowntypes +parse_usage_report(). Only parsessuccess: trueresponses; respectshasLimit: false(pooled/no-cap enterprise accounts hide the limit and progress bar, mirroring the kiro TUI behavior).acp/connection.rs— storesagentInfo.namefrominitialize; newget_usage()sends the extension request with the adjacently-taggedTuiCommandshape{"command": "usage", "args": {}}.acp/pool.rs—SessionPool::get_usage(thread_id)plumbing (same pattern asset_config_option).discord.rs— registers/usageglobally, defers with an ephemeral response (the ACP round-trip can exceed Discord's 3s interaction deadline), then follows up with the formatted report.Safety notes
_kiro.dev/*is a Kiro-specific extension namespace, not part of the ACP spec. A malformed or unrecognizedcommandvalue is a serde deserialization error in kiro-cli that terminates the whole ACP connection (no JSON-RPC error is returned).get_usage()is therefore gated on the initialize-advertised agent name containing "kiro" and is never retried.Testing
parse_usage_report(full report, no-limit sentinel hiding, failure→None, empty breakdowns) and 3 forformat_usage_report(over-limit warning, no-limit consumption-only, under-limit bar rendering).cargo test -p openab-core: 641 passed, 1 failed —secrets::tests::resolve_exec_nonzero_exit, verified pre-existing on cleanmain(f89e9fe), environment-specific, unrelated.cargo clippy -p openab-core -- -D warnings: clean.kiro-cli acp2.12.1 (initialize → session/new → commands/execute → structured usage JSON).