diff --git a/src/api/rest.rst b/src/api/rest.rst index 707447b..5693938 100644 --- a/src/api/rest.rst +++ b/src/api/rest.rst @@ -5,6 +5,10 @@ ActivityWatch uses a REST API for all communication between aw-server and client Most applications should never use HTTP directly but should instead use the client libraries available. If no such library yet exists for a given language, this document is meant to provide enough specification to create one. +If you are building an AI assistant or agent integration, start with +:doc:`../examples/agents-and-ai` and prefer bounded, aggregated payloads over raw +event access. + .. warning:: The API is currently under development, and is subject to change. It will be documented in better detail when first version has been frozen. diff --git a/src/examples.rst b/src/examples.rst index 6eb5113..0ef7afb 100644 --- a/src/examples.rst +++ b/src/examples.rst @@ -12,10 +12,12 @@ Getting Your Data Out :maxdepth: 1 examples/working-with-data + examples/agents-and-ai This comprehensive guide covers: * **Canonical Events** - Get processed activity data (what the web UI uses) +* **Agents and AI** - Share bounded, reviewed ActivityWatch summaries with assistants * **Custom Queries** - Write your own analysis using the query language * **Raw Events** - Advanced direct access to bucket data * **Safety Best Practices** - Avoiding data corruption with proper testing and dry-run modes diff --git a/src/examples/agents-and-ai.rst b/src/examples/agents-and-ai.rst new file mode 100644 index 0000000..5aa1773 --- /dev/null +++ b/src/examples/agents-and-ai.rst @@ -0,0 +1,200 @@ +ActivityWatch with agents and AI +================================ + +ActivityWatch can be useful context for AI assistants because it records what you +actually did on your computer. The safest workflow is to keep ActivityWatch data +local, reduce it to a small summary, review that summary, and only then decide +whether any model should see it. + +Start with categorized summaries, not raw event exports. Window titles, browser +URLs, document names, and chat subjects can contain sensitive information. + +Recommended workflow +-------------------- + +1. Pick a bounded time range, such as the last work session or the current day. +2. Start from canonical events so AFK time and category rules are applied. +3. Aggregate locally by category, app, domain, or coarse timeline block. +4. Remove or reduce sensitive fields before model access. +5. Review the exact payload that will be sent. +6. Prefer local models or an agent you already run locally. Use third-party + provider upload only when you understand what data is included. + +Good questions for an assistant are specific: + +* "Summarize my work since 09:00." +* "Which uncategorized activities should become category rules?" +* "Prepare a standup note from today's coding and communication blocks." + +Avoid broad prompts such as "analyze my life" or "send all my ActivityWatch data +to a model." They create large payloads, weaker answers, and unnecessary privacy +risk. + +Get canonical activity +---------------------- + +If you have ``aw-client`` installed, the canonical query is the easiest starting +point: + +.. code-block:: sh + + aw-client canonical HOSTNAME --start 2026-08-03T08:00:00 --stop 2026-08-03T12:00:00 --cache + +Replace ``HOSTNAME`` with the hostname suffix used in your buckets. Canonical +events use the same kind of processing as the ActivityWatch web UI: active time +filtering, event merging, and categorization. + +You can also use the Python client directly: + +.. code-block:: python + + import socket + from datetime import datetime + + from aw_client import ActivityWatchClient + from aw_client.queries import DesktopQueryParams, canonicalEvents + + client = ActivityWatchClient("agent-summary", testing=False) + start = datetime.fromisoformat("2026-08-03T08:00:00").astimezone() + end = datetime.fromisoformat("2026-08-03T12:00:00").astimezone() + hostname = socket.gethostname() + + query = canonicalEvents( + DesktopQueryParams( + bid_window=f"aw-watcher-window_{hostname}", + bid_afk=f"aw-watcher-afk_{hostname}", + ) + ) + events = client.query(f"{query}\nRETURN = events;", [(start, end)])[0] + + category_seconds = {} + app_seconds = {} + for event in events: + seconds = event["duration"] + category = tuple(event["data"].get("$category", ["Uncategorized"])) + app = event["data"].get("app", "unknown") + category_seconds[category] = category_seconds.get(category, 0) + seconds + app_seconds[app] = app_seconds.get(app, 0) + seconds + + print("Categories") + for category, seconds in sorted(category_seconds.items(), key=lambda item: -item[1]): + print(f"{' / '.join(category)}: {seconds / 3600:.2f}h") + + print("Apps") + for app, seconds in sorted(app_seconds.items(), key=lambda item: -item[1]): + print(f"{app}: {seconds / 3600:.2f}h") + +This produces a bounded, AFK-filtered summary without giving a model raw window +titles or raw event history. + +Compact context example +----------------------- + +A small provider-neutral context payload is usually enough: + +.. code-block:: json + + { + "source": "activitywatch", + "range": { + "start": "2026-08-03T08:00:00", + "end": "2026-08-03T12:00:00" + }, + "totals": { + "active_seconds": 12600, + "afk_seconds": 1800 + }, + "categories": [ + {"name": ["Coding"], "seconds": 7200}, + {"name": ["Communication"], "seconds": 1800} + ], + "apps": [ + {"app": "Code", "seconds": 5400}, + {"app": "Firefox", "seconds": 2400} + ], + "redaction": { + "window_titles": "omitted", + "urls": "domain_only" + } + } + +Keep durations as seconds and timestamps as ISO 8601 strings. Preserve nested +categories as arrays. Include the redaction policy so the assistant knows what it +can and cannot infer from the payload. + +Sensitive fields +---------------- + +Safe defaults: + +* category totals +* app totals +* domain-only browser totals +* coarse timeline blocks +* active and AFK totals + +Review carefully before including: + +* full window titles +* full URLs +* document names +* chat or email subjects +* raw bucket exports +* long timeline histories + +If you use a hosted model, make the boundary explicit before sending data: + +.. code-block:: text + + This will send 4 hours of aggregated ActivityWatch data to the selected provider. + Included: category totals, app totals, domain totals. + Excluded: window titles, full URLs, raw events. + +Local-first workflow +-------------------- + +For local summarization: + +1. Generate the compact context locally with ``aw-client`` or the Python client. +2. Review the payload. +3. Pass only the compact context to a local model or local assistant runtime. +4. Ask for a short answer with a narrow task, such as a standup summary or + category-rule suggestions. + +For category-rule assistance, send examples of uncategorized app/title patterns +only after removing private names. Ask the assistant to propose rules, then add +them manually in ActivityWatch's categorization settings. + +Agent and tool workflow +----------------------- + +Agents can access ActivityWatch through several layers: + +* Export: good for offline analysis and backups. See :doc:`../features/exporting-data`. +* Python client: good for scripts and local summarization. See :doc:`working-with-data`. +* Query API: good for custom local tools. See :doc:`../api/rest`. +* MCP or other agent tooling: good when your assistant supports tool calls. + +The same privacy rule applies to all of them: aggregate and redact before model +access. Do not give an agent write access or raw historical exports unless the +workflow really needs it. + +Third-party provider upload +--------------------------- + +Using a third-party model provider is a user choice, not the default +ActivityWatch workflow. Before uploading data: + +* prefer aggregated summaries over raw events +* remove titles and full URLs unless they are necessary +* check the provider's retention and training policy +* keep the exact sent payload in your own notes if you need auditability +* use a shorter time range than you would use for local analysis + +Related docs +------------ + +* :doc:`working-with-data` +* :doc:`../features/exporting-data` +* :doc:`../features/categorization` +* :doc:`../api/rest` diff --git a/src/examples/working-with-data.rst b/src/examples/working-with-data.rst index cf93a56..18559b8 100644 --- a/src/examples/working-with-data.rst +++ b/src/examples/working-with-data.rst @@ -5,6 +5,9 @@ This guide covers how to retrieve and work with your ActivityWatch data, from si Most users will want to start with canonical events, which provide processed, meaningful activity data using the same logic as the web UI. +If you want to use this data with an AI assistant, see :doc:`agents-and-ai` for +privacy-first workflows that aggregate and review data before model access. + .. contents:: :local: @@ -247,4 +250,4 @@ API Reference For low-level API access, see: * :doc:`../api/rest` - HTTP REST API documentation -* :doc:`../api/python` - Python client library API \ No newline at end of file +* :doc:`../api/python` - Python client library API diff --git a/src/features/categorization.rst b/src/features/categorization.rst index 14b441e..93e4680 100644 --- a/src/features/categorization.rst +++ b/src/features/categorization.rst @@ -5,6 +5,11 @@ When you look at the "Activity" view in the ActivityWatch UI you will see that t Categories are used to group together multiple events, which create more easily understandable labels for the data such as "Work", "Gaming" or "Social Media". +Categorized summaries are also the safest starting point for AI-assisted +analysis. See :doc:`../examples/agents-and-ai` for a workflow that shares +category and app totals with an assistant while avoiding raw titles and URLs by +default. + Each category has a title, a parent category (optional), child categories (optional), and a categorization rule which is used to match events on window titles and application names. A category can have child categories ("Work" might have "Mail", "Gaming" might have "Minecraft", etc). Child categories have independent categorization rules, but the time attributed to the children of a parent category is often added to the parent category in visualizations. diff --git a/src/features/exporting-data.rst b/src/features/exporting-data.rst index 748f62c..36f7242 100644 --- a/src/features/exporting-data.rst +++ b/src/features/exporting-data.rst @@ -5,6 +5,10 @@ If you go to the "Raw Data" page in the ActivityWatch webui you can download any If running on localhost with the default port, then you can find this at http://localhost:5600/#/buckets. Each bucket can be exported individually, or all of the buckets can be exported by clicking the "Export all buckets as JSON" button at the bottom. +If you plan to use exported data with an AI assistant or third-party model, first +read :doc:`../examples/agents-and-ai`. In most cases you should aggregate and +redact ActivityWatch data locally instead of uploading raw bucket exports. + To export programatically, you can make a simple GET request via the REST API. If for example, you want to export all of the buckets with ``wget`` you could call