feat(api): read-only activity API for agents and automations - #477
Open
biztex wants to merge 1 commit into
Open
Conversation
Activity verification was dashboard-only: the request-log service was reachable from a Next.js server action but had no HTTP surface, so an agent could not confirm its own calls actually flowed through OneCLI without driving a browser. Add GET /v1/activity (keyset-paginated feed) and GET /v1/activity/:id, both project-scoped, plus the field-level narrowing an automation needs to answer "did MY call land?": agentId, host, provider, method, status, since/until, on top of the existing all/hide-llm/blocked filter. This is a second surface over the SAME service, not a way around it. Both reads pass the caller as the viewer, so the admin-only org-rule redaction the Activity page applies is applied here too, and the single-event read is scoped in its where clause - a foreign id is indistinguishable from a missing one, so ids cannot be probed. Invalid input fails loudly rather than silently widening a query: an unparseable timestamp, an inverted window, an out-of-range limit, an unknown filter, or a half-supplied cursor all 400. A silently dropped bound would answer "nothing since X" with everything, which reads as "OneCLI is not logging" - the exact confusion this endpoint exists to remove. Query conditions are ANDed rather than assigned onto the where root: filter=blocked already owns `status` and the cursor owns `OR`, so a root assignment would drop one of them. Closes onecli#411
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I have read the CONTRIBUTING.md file.
YES
What kind of change does this PR introduce?
Feature — a read-only HTTP surface over data that already exists. Closes #411.
What is the current behavior?
Activity verification is dashboard-only.
request-log-serviceis reachable from a Next.js server action (lib/actions/request-logs.ts), but there is no API route for it, so an agent cannot confirm its own calls actually flowed through OneCLI without driving a browser. As @fireharp put it, for an agent-first product the agent should be able to check "did my request land, with the expected agent/credential mapping?" itself.What is the new behavior?
GET /v1/activity— keyset-paginated feed, andGET /v1/activity/:id— one event. Both project-scoped, both read-only.On top of the existing
all/hide-llm/blockedfilter, the field-level narrowing an automation actually needs:agentIdhost,providerapi.github.comor justgithub)methodstatussince/untillimit(≤200),cursorCreatedAt+cursorIdTwo properties this had to get right
1. It's a second surface over one service, never a way around it. Both reads pass the caller as the
viewer, so the admin-only org-rule redaction the Activity page applies is applied here identically — a non-admin cannot use the API to read org rule names the dashboard hides from them. The single-event read scopes the lookup in itswhere, so a foreign id is indistinguishable from a missing one and ids can't be probed. Both are pinned by tests, including a serialize-the-whole-payload check that no org rule identifier leaks.2. Invalid input fails loudly. An unparseable timestamp, an inverted window, an out-of-range
limit, an unknownfilter, or a half-supplied cursor all 400. A silently dropped bound would answer "nothing since X" with everything — which reads as "OneCLI isn't logging", the exact confusion this endpoint exists to remove. A half-cursor would restart at page one and loop a paging automation forever.One implementation note worth flagging for review: query conditions are ANDed rather than assigned onto the
whereroot, becausefilter=blockedalready ownsstatusand the cursor ownsOR— a root assignment would silently drop one of them (filter=blocked&status=200would return every 200 instead of nothing). There's a regression test for exactly that.Tests
routes/activity.test.ts(12): auth required; project scoping; viewer threading; all filters parsed through to the service; cursor forwarding; and each 400 path asserting the service was not called.services/request-log-service.test.ts(+10):ActivityQuery→ Prismawhere(including the AND-vs-root regression and the cursor/filter coexistence), plusgetRequestLogByIdfor hit, unknown id, foreign project id, redaction parity with the feed, and fail-safe with no viewer.packages/apisuite: 835 passed.apps/websuite: 13 passed.turbo run lint check-typesgreen for api/web/db/ui; prettier clean.Deliberately out of scope
extraDatashaping. The payload is returned exactly as the dashboard receives it, so this endpoint exposes nothing new. Worth noting for the issue's "no sensitive credential values" requirement: that guarantee comes from what the gateway writes intoextra_data, not from this route — if you'd like an explicit allow-list projection for the API, happy to add it in a follow-up./v1/activityacross a whole org) — the underlying service is project-scoped today; say the word if you want that axis too.