Skip to content

feat(integrations): every integration identifies itself with its own User-Agent - #5466

Merged
nicoloboschi merged 4 commits into
mainfrom
feat/integration-user-agents
Oct 12, 2026
Merged

nicoloboschi merged 4 commits into
mainfrom
feat/integration-user-agents

Conversation

@nicoloboschi

@nicoloboschi nicoloboschi commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

What

Every integration that calls the Hindsight API now sends its own User-Agent, hindsight-<name>/<version>. Before, these sent the client default (hindsight-client-python/x, hindsight-client-typescript/x) or the stdlib default (Python-urllib/x, node), so the server couldn't tell which integration a request came from. About 25 integrations already did this. This PR covers the rest.

Client change: keep the client version

Before this PR, a caller's User-Agent replaced the client's own, so every integration that set one hid which client version it ran on. The Python and TypeScript wrappers, and Rust's client_with_user_agent, now put the caller's value in front of their own:

hindsight-ag2/1.2.0 hindsight-client-python/0.10.4

Integrations need no change for this, and they work with both old and new clients. On an old client they send hindsight-ag2/1.2.0; on a new client they send both names. Nothing in the repo checks the exact header a real client sends, since integration tests check what the integration passes in. Go is unchanged: it has no wrapper option for this, only the raw cfg.UserAgent.

Covered

Python: agentcore, composio, dify (client + credential health check), hermes (client + /version probe), pipecat, smolagents, vapi, cursor (hook client + daemon health check), devin-desktop, meta-muse (preflight CLI session)

TypeScript / JS: coding-agents (shared client + daemon health check), flowise, opencode, paperclip, eve, obsidian, n8n (credential-level header), zapier (middleware)

If a user passes their own client, we don't touch it, so their User-Agent is kept.

Version source, per integration

  • Installed packages: importlib.metadata / package.json, falling back to 0.0.0 (the same as ag2 and openclaw).
  • Directory-loaded plugins (hermes, dify, cursor): read from their manifest (plugin.yaml, manifest.yaml, .cursor-plugin/plugin.json).
  • coding-agents and obsidian: the version is copied into the built file at build time, because the shipped bundle has no package.json beside it.
  • flowise and n8n: no version. Their node files are copied or compiled at different depths, so there's no stable path to package.json.
  • zapier: hindsight-zapier/<v> Zapier. The trailing Zapier stays, because the platform uses it to spot its own requests and would otherwise log every request twice.

Left out on purpose

  • ai-sdk, chat, eliza: the user builds the client and passes it in, so the integration never creates one.
  • agent-plugin, gemini-spark, github-copilot, openhands, roo-code, zed, nemoclaw, grok-bot: these only set up MCP or install files and never call the API.
  • cloudflare-oauth-proxy: it forwards the caller's own requests, so it should keep their header.

Tests

Each integration got or extended one test that checks the header is sent, and each integration's suite passes locally. ./scripts/hooks/lint.sh is clean.

…t version

Review finding: the public reqwest builder that the docs point integrations at
still replaced the client token, so only client_with_user_agent kept it.
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.

1 participant