# Internal Workings of Request Tracking and Logging in ThePrimeagen/99

> Explore the internal workings of request tracking and logging in ThePrimeagen/99. Learn how unique IDs, JSON logs, and lifecycle hooks manage AI editor operations efficiently.

- Repository: [ThePrimeagen/99](https://github.com/theprimeagen/99)
- Tags: internals
- Published: 2026-02-16

---

**ThePrimeagen/99 tracks every AI-driven editor operation through a centralized state object that assigns unique request IDs, caches per-request logs in JSON format, and provides lifecycle hooks for cancellation and UI updates.**

ThePrimeagen/99 is a Neovim plugin that orchestrates AI-driven editor operations through a robust request tracking and logging system. Understanding the internal workings of request tracking and logging reveals how the plugin maintains state consistency, enables request cancellation, and provides detailed debugging capabilities through per-request log isolation.

## Request Lifecycle: From Context to Completion

Every AI operation in 99 follows a strict lifecycle managed by three core components: `RequestContext`, `Request`, and the provider layer.

### Context Creation and Unique ID Assignment

When a user triggers an operation like search or tutorial, `RequestContext.from_current_buffer` in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) constructs a context object containing:

- A unique `xid` (request ID)
- Buffer information and temporary file paths
- A scoped logger instance via `Logger:set_id(xid)`

```lua
-- From lua/99/request-context.lua
local context = RequestContext.from_current_buffer("search")
-- context.xid is now a unique identifier
-- context.logger is scoped to this specific request

```

### Request Initialization and State Management

The `Request.new(context)` constructor in [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) creates a request object with initial state `"ready"` and scopes the logger to the `"Request"` area using `context.logger:set_area("Request")`.

### Starting the Request and Provider Execution

When `Request:start(observer)` is called, it:

1. Validates the request state
2. Finalizes the context (adds markdown files, range info, temp-file location)
3. Builds and saves the prompt
4. Forwards the request to the selected provider

The provider layer in [`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua) handles `BaseProvider:make_request`, which spawns a system process and streams stdout/stderr back through the observer.

### Completion and State Tracking

When the provider finishes, `observer.on_complete` triggers `context._99:finish_request(context, status)` in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua). This updates the request status to `"success"`, `"failed"`, or `"cancelled"` and stores operation-specific data like tutorials or search results.

### Cancellation and Cleanup

The `Request:cancel()` method aborts running system processes and sets state to `"cancelled"`, with the logger recording the cancellation event.

## Centralized Request Tracking with _99_State

All active and historic requests are stored in the singleton `_99_State` object initialized during plugin setup.

### Tracking New Requests

The `track_request` function in [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) creates a structured entry for every request:

```lua
function _99_State:track_request(context)
  assert(context.operation, "must have an operation defined to track the request")
  local point = context.range and context.range.start or Point:from_cursor()
  local entry = {
    context = context,
    status  = "requesting",
    point   = point,
    started_at = time.now(),
    operation_data = nil,
  }
  table.insert(self.__request_history, entry)
  self.__request_by_id[context.xid] = entry
  return entry
end

```

This maintains two data structures:

- `__request_history`: Ordered list for UI components like the quick-fix list
- `__request_by_id`: Map keyed by `xid` for fast lookup and log association

### Finishing and Storing Results

The `finish_request` method updates the entry status and routes successful results to type-specific storage:

```lua
function _99_State:finish_request(context, status)
  local id = context.xid
  local entry = self.__request_by_id[id]
  if not entry then return end
  entry.status = status
  local data = entry.operation_data
  if entry.status == "success" and data then
    if data.type == "tutorial" then
      table.insert(self.__tutorials, data)
    elseif data.type == "search" then
      table.insert(self.__searches, data)
    end
  end
end

```

### Querying Active and Historic Requests

The state object provides several APIs for UI interaction:

- `active_request_count()`: Returns the number of requests with `status == "requesting"` for the throbber UI
- `stop_all_requests()`: Iterates over `__request_by_id` and calls `context:stop()` on each in-flight request
- `previous_requests_to_qfix()`: Transforms `__request_history` into quick-fix items for `:copen`
- `clear_previous_requests()`: Removes non-active entries and purges associated logs

## Per-Request Logging Architecture

The logging system isolates logs by request ID using a lightweight `Logger` module with configurable sinks.

### Logger Sinks and Configuration

Three sink implementations control log output:

- **VoidSink**: Discards logs (default)
- **PrintSink**: Calls `print()` for each line
- **FileSink**: Writes JSON lines to a file using `vim.uv.fs_open`

Configuration happens through `Logger:configure`:

```lua
require("99").setup({
  logger = {
    level = require("99.logger.level").DEBUG,
    type  = "file",
    path  = vim.fn.stdpath("cache") .. "/99.log",
    max_requests_cached = 10,
  },
})

```

### Request Scoping and Log Caching

Each request receives a scoped logger instance:

```lua
-- In RequestContext creation
logger = Logger:set_id(xid)          -- Adds request ID for caching
logger = logger:set_area("Request")  -- Adds Area meta-field

```

The `set_id` method is critical because `_cache_log` stores each line in `logger_cache[id]`. The cache automatically trims to `max_requests_in_logger_cache` (default 5) to prevent memory bloat.

All log calls flow through `_log`, which:

1. Validates the logger's level
2. Builds a table with `level`, `msg`, and key/value pairs
3. Merges `extra_params` (including `id` and `Area`)
4. Encodes to JSON and writes to the sink
5. Caches the JSON string under the request ID

### Viewing Cached Logs

The plugin exposes functions to inspect logs:

```lua
_99.view_logs()          -- Opens most recent cached logs full-screen
_99.next_request_logs()  -- Scroll forward through cached sets
_99.prev_request_logs()  -- Scroll backward through cached sets

```

These functions pull JSON-encoded logs from `Logger.logs()` and display them using the `Window` helper.

## Practical Code Examples

### Initiating a Search Request

```lua
-- In your Neovim configuration
require("99").setup({ show_in_flight_requests = true })

-- Trigger a search operation
require("99").search()

```

This invokes `search` (line 50 of [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua)), which creates a context with `operation = "search"`, tracks it via `_99_State:track_request`, and delegates to `ops.search` which ultimately calls `Request:start()`.

### Cancelling All In-Flight Requests

```lua
require("99").stop_all_requests()

```

This calls `stop_all_requests` (lines 42-48 of [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua)), which iterates through all entries in `__request_by_id` and invokes each entry's `context:stop()`. The context's `stop` method runs cleanup callbacks and the request's `cancel` method kills the underlying system process.

### Accessing Per-Request Logs

```lua
-- Show the most recent request logs in a full-screen buffer
require("99").view_logs()

-- Cycle through cached logs
require("99").next_request_logs()
require("99").prev_request_logs()

```

These functions retrieve JSON-encoded logs from the per-request cache and display them using the internal `Window` helper.

### Custom Logger Configuration

```lua
require("99").setup({
  logger = {
    level = require("99.logger.level").DEBUG,
    type  = "file",
    path  = vim.fn.stdpath("cache") .. "/99.log",
    max_requests_cached = 10,
  },
})

```

`Logger:configure` (lines 89-115 of [`lua/99/logger/logger.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/logger/logger.lua)) switches the sink to `FileSink`, sets the threshold level, and adjusts the cache limit to retain logs for the last 10 requests instead of the default 5.

## Key Source Files

| File | Role | Direct Link |
|------|------|-------------|
| [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua) | Builds per-buffer context, assigns unique logger (`Logger:set_id`). | [request-context.lua](https://github.com/ThePrimeagen/99/blob/master/lua/99/request-context.lua) |
| [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) | Implements the `Request` object (state machine, start, cancel, prompt handling). | [request/init.lua](https://github.com/ThePrimeagen/99/blob/master/lua/99/request/init.lua) |
| [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) | Central state (`_99_State`), request tracking (`track_request`, `finish_request`), UI helpers, public API. | [init.lua](https://github.com/ThePrimeagen/99/blob/master/lua/99/init.lua) |
| [`lua/99/logger/logger.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/logger/logger.lua) | Core logging implementation, sinks, per-request caching, configuration. | [logger/logger.lua](https://github.com/ThePrimeagen/99/blob/master/lua/99/logger/logger.lua) |
| [`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua) | Provider abstraction that runs external AI commands and reports via observer. | [providers.lua](https://github.com/ThePrimeagen/99/blob/master/lua/99/providers.lua) |
| `lua/99/ops/*.lua` | High-level operations (search, tutorial, etc.) that orchestrate request creation. | [ops/search.lua](https://github.com/ThePrimeagen/99/blob/master/lua/99/ops/search.lua) |

## Summary

- **Unique request IDs (`xid`)** bind together loggers, contexts, and cache entries, enabling isolated per-request debugging and safe concurrent handling of multiple AI operations.
- The central `_99_State` object provides a **single source of truth** for request metadata, making UI components like the throbber and quick-fix list trivial to implement without direct provider coupling.
- The **logger cache** guarantees that recent logs remain accessible even after the original request object is garbage collected, while `max_requests_cached` protects memory through automatic LRU-style trimming.
- All logging is **JSON-encoded**, simplifying downstream processing or external log aggregation without requiring custom parsers.
- **Structured lifecycle hooks** (track, finish, cancel) ensure that every AI operation integrates automatically with state management and logging without additional boilerplate in operation-specific code.

## Frequently Asked Questions

### How does 99 assign unique identifiers to each request?

The plugin generates a unique `xid` during context creation in [`lua/99/request-context.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request-context.lua). This identifier is immediately passed to `Logger:set_id(xid)` to scope all subsequent logs to this specific request, and is used as the key in `__request_by_id` for state tracking. The `xid` binds together the context, logger, and state entry throughout the request lifecycle.

### What happens to logs after a request completes?

Logs are cached in memory under the request's `xid` with a default limit of 5 requests (`max_requests_in_logger_cache`). The `Logger` module automatically trims old entries when this limit is exceeded to prevent memory bloat. If configured with a `FileSink`, logs are also persisted to disk as JSON lines before the in-memory cache is cleared, ensuring durable records even after the request object is garbage collected.

### Can I cancel a running AI request in 99?

Yes. The `Request:cancel()` method in [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) aborts the underlying system process and updates the state to `"cancelled"`, with the logger recording the cancellation event. You can cancel all in-flight requests at once using `require("99").stop_all_requests()`, which iterates through `__request_by_id` and invokes the stop callback for each active entry.

### Where are request statuses stored and how does the UI access them?

Request statuses are stored in the `_99_State` singleton within [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua). The state maintains `__request_history` (an ordered list for the quick-fix window) and `__request_by_id` (a map for fast lookup). UI components query `active_request_count()` to display the throbber and call `previous_requests_to_qfix()` to populate the quick-fix list with historic request data, including file locations and status information.