Skip to content

feat: add cached referenced_users enrichment for messages and search#72

Closed
cpitt wants to merge 1 commit intostablyai:mainfrom
cpitt:feature/enhance-message-response-with-user-info
Closed

feat: add cached referenced_users enrichment for messages and search#72
cpitt wants to merge 1 commit intostablyai:mainfrom
cpitt:feature/enhance-message-response-with-user-info

Conversation

@cpitt
Copy link
Copy Markdown
Contributor

@cpitt cpitt commented Mar 24, 2026

Summary

  • add a per-workspace cached user resolver and shared referenced_users output for message-bearing commands
  • preserve canonical user IDs in message/search payload fields while exposing user metadata separately
  • extend the same behavior to search messages and search all, plus add --refresh-users
  • add focused tests for referenced user collection, search output shaping, and refresh-user plumbing

Why

Raw Slack user IDs repeat heavily across message and search results, which makes CLI output harder to read and forces downstream consumers to do their own repeated lookups. A shared referenced_users map keeps payload fields canonical and machine-stable while providing enough metadata for human-friendly post-processing.

Changes

  • add src/slack/user-cache.ts for targeted per-workspace users.info caching with TTL
  • add top-level referenced_users to message get/list
  • add top-level referenced_users to search messages/all
  • add --refresh-users to message and search flows
  • keep canonical IDs in structured fields (author.user_id, rendered @U... mentions, and reactions only when returned)
  • scope referenced_users to users actually represented in the returned payload
  • update docs to describe the canonical-ID-plus-reference-map contract
  • add tests for user reference extraction, search referenced user shaping, and refresh flag behavior

Validation

  • bun run typecheck
  • bun run test

Closes #70

Add a per-workspace user cache with TTL and refresh controls, then wire it into message get/list and search messages/all so outputs include a shared referenced_users map without changing canonical ID fields. Also add --refresh-users flags for message and search commands and update skill/reference docs to document the canonical-ID-plus-reference-map contract.
@AmethystLiang
Copy link
Copy Markdown
Contributor

Hey @cpitt , thanks for the PR! Will take a look today and try to get it landed

@AmethystLiang
Copy link
Copy Markdown
Contributor

AmethystLiang commented Mar 31, 2026

This is an incredibly useful feature! Resolving raw Slack user IDs to human-readable names is critical for both AI agents and humans reading the CLI output, and this will save a lot of unnecessary API round-trips. Thank you for putting this together! 🚀

I've reviewed the implementation and while the proposed output shape, caching strategy, and ID collection logic are spot on, there are a few operational risks with the current always-on approach.

Here is a summarized design review with a proposed Opt-In architecture:

The Risks

  1. Always-on overhead: User resolution is unconditional. Every read command (message get, search, etc.) now makes extra API calls, adding hidden latency and rate limit consumption even for scripts that don't need names.
  2. Rate Limits: users.info is fired in parallel without a concurrency limit, risking HTTP 429 errors on cold caches.
  3. Cache Inefficiency: The cache is written to disk on every command (even when unchanged), and cross-workspace pollution can happen if the workspace URL is unknown.

Proposed Architecture

  1. Make it Opt-In (--resolve-users): Add a --resolve-users flag. If absent, keep the original, fast behavior (no extra API calls). If present, attach the referenced_users map.
  2. Concurrency Control: Limit parallel users.info calls (e.g., max 5) to stay safely within Slack's Tier 4 rate limits.
  3. Shared "Warm-Through" Cache: Let existing commands like agent-slack user get and user list also populate this cache. If an agent previously fetched users, future message resolutions become instantly free.
  4. Smart Cache Writes: Only write to disk if entries actually changed. If the workspace URL is unknown, safely skip resolution to avoid collisions.
  5. Keep the Code DRY: Reuse the existing toCompactUser function from users.ts instead of duplicating it, and extract the duplicated search resolution logic into a shared helper.

What to keep: The referenced_users side-channel map, the 24h TTL disk cache, and the strict scoping to referenced users are perfect. The --refresh-users flag is also a great escape hatch (though it should ideally imply --resolve-users).

Let me know what you think of this approach!

@AmethystLiang
Copy link
Copy Markdown
Contributor

Superseded by #78 which implements the requested opt-in architecture and concurrency controls. Thank you @cpitt for the original implementation!

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.

Feature: add shared referenced_users enrichment (cached) while preserving canonical user IDs

2 participants