Skip to content

feat(cli): add privacy-safe activity summaries - #116

Open
abouchard11 wants to merge 2 commits into
ActivityWatch:masterfrom
abouchard11:agent/privacy-safe-summary
Open

feat(cli): add privacy-safe activity summaries#116
abouchard11 wants to merge 2 commits into
ActivityWatch:masterfrom
abouchard11:agent/privacy-safe-summary

Conversation

@abouchard11

Copy link
Copy Markdown

Summary

  • add aw-client summary for bounded, AFK-filtered category, application, and domain totals
  • aggregate on the local ActivityWatch server and emit either a table or provider-neutral JSON
  • make application/domain inclusion optional and document exactly which fields are included or omitted
  • reuse the active client when loading category settings so host, port, testing mode, and authentication stay consistent

Why

ActivityWatch's agent/AI guidance recommends locally aggregated, review-before-send context instead of raw exports. Today, users need to write the query and normalization code themselves. This command turns that workflow into a reproducible CLI path without exposing raw titles, full URLs, document names, message subjects, or raw event history.

Related: ActivityWatch/activitywatch#1388
Related: ActivityWatch/aw-webui#925

Validation

  • ruff check aw_client tests/test_summary.py
  • ruff format --check aw_client tests/test_summary.py
  • mypy aw_client tests/test_summary.py
  • XDG_DATA_HOME=/tmp/activitywatch-test-data pytest -q tests/test_summary.py tests/test_auth.py tests/test_requestqueue.py — 17 passed
  • real-server integration test against seeded window, AFK, and browser buckets; verified aggregates and confirmed confidential titles and full URL paths were absent from output

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a locally aggregated aw-client summary command with table and JSON output, configurable application/domain inclusion, and consistent category-settings lookup through the active client.

  • Builds bounded category, application, and domain aggregates on the ActivityWatch server.
  • Adds normalization, redaction metadata, human-readable formatting, documentation, and focused tests.
  • Extends category-settings loading to reuse the caller's configured client.

Confidence Score: 3/5

The PR should not merge until browser buckets with unknown hostname metadata can no longer disclose another machine's domain activity in a host-specific summary.

The new selector assigns every legacy unknown-host browser bucket to any requested hostname, so shared-server deployments can mix another machine's overlapping browser activity into privacy-oriented output.

Files Needing Attention: aw_client/summary.py and tests/test_summary.py

Security Review

The browser-bucket selector can associate legacy unknown-host buckets from other machines with the requested hostname, allowing overlapping domain activity to appear in a host-specific summary.

Important Files Changed

Filename Overview
aw_client/summary.py Adds bucket discovery and summary formatting, but unknown-host bucket inclusion can mix browser activity across machines.
aw_client/queries.py Adds bounded server-side category, application, and domain aggregation using existing canonical event semantics.
aw_client/cli.py Adds the summary command and consistently reuses the active client for category settings.
aw_client/classes.py Generalizes category-settings retrieval to accept a compatible caller-provided client while preserving fallback behavior.
tests/test_summary.py Covers aggregation, redaction, formatting, and CLI output, but codifies globally accepting unknown-host browser buckets.
README.md Documents valid summary invocations, output sensitivity, and application/domain omission controls.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    CLI[summary command] --> Buckets[Discover browser buckets]
    Buckets --> Query[Build privacySummary query]
    Settings[Load category settings through active client] --> Query
    Query --> Server[Local ActivityWatch aggregation]
    Server --> Normalize[Normalize bounded result]
    Normalize --> JSON[Provider-neutral JSON]
    Normalize --> Table[Human-readable table]
Loading

Reviews (1): Last reviewed commit: "feat(cli): add privacy-safe activity sum..." | Re-trigger Greptile

Comment thread aw_client/summary.py
@abouchard11
abouchard11 marked this pull request as ready for review August 25, 2026 18:41
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@ErikBjare

Copy link
Copy Markdown
Member

@TimeToBuildBob check this out

Comment thread tests/test_summary.py
Comment thread tests/test_summary.py
Comment thread aw_client/classes.py
@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Greptile's concern is valid — wanted to flag the specific issue clearly.

In summary.py, find_browser_buckets() includes buckets where hostname is None, "", or "unknown":

if bucket_hostname not in (None, "", "unknown", hostname):
    continue

On a shared AW server (multiple machines reporting to one instance), a legacy browser bucket from machine B with no hostname metadata would be included in machine A's privacy-safe summary. That's a meaningful privacy leak for the exact use case this PR targets.

Simple fix: exclude unknown-host buckets by default and add an explicit --include-legacy-buckets flag for users who knowingly run a single-machine setup. The test in test_summary.py that asserts unknown-host buckets are globally accepted should become a test for the opt-in flag instead.

Everything else looks solid — the redaction policy, schema versioning, AFK filtering, and test coverage are all well-done.

find_browser_buckets() treated buckets reporting no hostname, an empty
hostname, or "unknown" as matching any host. On a server collecting from
several machines, a legacy browser bucket belonging to machine B was folded
into machine A's summary, leaking another host's browsing domains through a
command whose purpose is a privacy-safe summary.

Exclude unattributed buckets by default and add --include-legacy-buckets for
single-machine servers that want them counted. Record the choice in the
payload's redaction block so a consumer can tell which policy produced it.

The existing test asserted the leaking behaviour was correct; it now covers
the default exclusion, with a second test for the opt-in path.
@abouchard11
abouchard11 force-pushed the agent/privacy-safe-summary branch from dfdbd09 to 20cdf83 Compare August 25, 2026 19:16
@abouchard11

Copy link
Copy Markdown
Author

Good catch, thanks — fixed.

find_browser_buckets() now scopes to the requested hostname and excludes buckets with no hostname or unknown, since those can't be attributed to a machine. Added --include-legacy-buckets for single-machine servers that want them counted.

Updated the test that asserted unknown-host buckets were accepted, and added one for the opt-in path. Also recorded the choice in the redaction block as legacy_unknown_host_buckets — happy to drop that if you'd rather not change the schema.

Rebased on master to clear the conflict with #117.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Fix looks correct — now rejects unattributed hostnames by default and the include_legacy=True opt-in path is explicitly tested. The redaction block recording the policy choice is a nice touch too.

Copy link
Copy Markdown
Author

Review cleanup complete: the host-isolation finding is fixed on the current head (20cdf83), and the corrected default/opt-in behavior has been confirmed in review. I also documented and resolved the three CodeQL annotations: two are fixed-fixture assertions in redaction tests, and one is the conventional ellipsis body of a typing.Protocol declaration.

The Build, CodeQL, Examples, and Lint workflow runs currently show action_required with no jobs created, so they appear to be awaiting approval for workflows from the fork. @ErikBjare, when convenient, could you approve the runs so CI can execute? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants