fix(cli): fix session show/list JSON output to not redact input messages #6699

Merged
HAL9000 merged 2 commits from fix/issue-6436-session-show-list-redaction into master 2026-04-26 19:57:08 +00:00
3 changed files with 58 additions and 0 deletions
+10
View File
@@ -39,6 +39,11 @@ Feature: Session CLI commands
Then the session CLI output should be valid JSON
And the session CLI JSON should contain "sessions"
Scenario: List sessions JSON matches the documented contract
Given there are mocked existing sessions
When I run session CLI list with --format json
Then the session CLI JSON list entries should match the documented contract
# Show command tests
Scenario: Show session with valid ID
Given there is a mocked session with messages
@@ -59,6 +64,11 @@ Feature: Session CLI commands
When I run session CLI show with --format json
Then the session CLI output should be valid JSON
Scenario: Show session JSON includes token usage counts
Given there is a mocked session with messages
When I run session CLI show with --format json
Then the session CLI JSON token usage should include counts
Scenario: Show session with invalid ID
When I run session CLI show with an invalid session ID
Then the session CLI should exit with error
+46
View File
@@ -514,6 +514,52 @@ def step_json_contains_key(context: Context, key: str) -> None:
assert key in data, f"Key '{key}' not found in JSON: {data}"
@then("the session CLI JSON token usage should include counts")
def step_json_show_token_usage_counts(context: Context) -> None:
parsed = json.loads(context.result.output)
data = _unwrap_envelope(parsed)
token_usage = data.get("token_usage")
assert isinstance(token_usage, dict), (
f"token_usage missing or not an object: {token_usage}"
)
_assert_token_usage_counts_are_ints(token_usage)
@then("the session CLI JSON list entries should match the documented contract")
def step_json_list_contract(context: Context) -> None:
parsed = json.loads(context.result.output)
data = _unwrap_envelope(parsed)
sessions = data.get("sessions")
assert isinstance(sessions, list), f"sessions missing or not a list: {sessions}"
for index, session in enumerate(sessions):
assert isinstance(session, dict), (
f"session entry at index {index} is not an object: {session!r}"
)
expected_keys = {"id", "name", "actor", "messages", "updated"}
actual_keys = set(session.keys())
missing = expected_keys - actual_keys
extra = actual_keys - expected_keys
assert not missing, (
f"session entry {index} missing keys {sorted(missing)}: {session!r}"
)
assert not extra, (
f"session entry {index} has undocumented keys {sorted(extra)}: {session!r}"
)
def _assert_token_usage_counts_are_ints(token_usage: dict[str, Any]) -> None:
input_tokens = token_usage.get("input_tokens")
output_tokens = token_usage.get("output_tokens")
assert isinstance(input_tokens, int), (
f"input_tokens should be int, got {input_tokens!r}"
)
assert isinstance(output_tokens, int), (
f"output_tokens should be int, got {output_tokens!r}"
)
assert input_tokens != "***REDACTED***", "input_tokens should not be redacted"
assert output_tokens != "***REDACTED***", "output_tokens should not be redacted"
@then("the session CLI should exit with error")
def step_exit_with_error(context: Context) -> None:
assert context.result.exit_code != 0, (
+2
View File
@@ -44,6 +44,8 @@ _FALSE_POSITIVE_KEYS: set[str] = {
"token_count",
"token_limit",
"token_usage",
"input_tokens",
"output_tokens",
"max_tokens",
"total_tokens",
"prompt_tokens",