Fix Sessions Endpoint in Activity Feed

For the activity/recap surface (the /me/* work on this branch), the session data comes from GET /api/v1/me/activity — see activity_cmd.go:117 (fetchActivity). Alongside it are GET /api/v1/me/commits (activity_cmd.go:137) and GET /api/v1/me/recap (recap/me_recap.go:143), and recap's response carries the per-scope sessions count.
What changed "now" is which backend serves those paths, not the paths themselves. As of 077b7479e, these commands go through runAuthenticatedActivityAPI (entireapi_client.go:35), which:
- Tries the caller's home entire-api cell via
auth.NewEntireAPICellClient(same clientexpertsuses). - Falls back to the data API (
runAuthenticatedDataAPI) on any failure — no cell in the region yet (ErrNoCellForJurisdiction), not logged in (ErrNotLoggedIn), or a discovery/exchange error.
Both backends expose the identical /me/* paths, so the fetch code (fn) is agnostic to which client it gets. You can hit it manually with entire api --to cell /api/v1/me/activity.
Two things that are not this endpoint, in case you meant one of them:
entire sessions list(agent sessions) reads local session state from.git/entire-sessions/, no API call.- Login/auth sessions (
auth status,logout) still hit entire-core at/api/auth/tokens(auth.go:27), unchanged by this branch.
Which "sessions" did you mean?
yeah sorry I meant the activity page for example, because it lists different sessions then what entire.io lists who uses GET /api/v1/me/sessions?timeframe=<tf>&limit=<n>
Right — that's the mismatch. The CLI's entire activity never calls /me/sessions. It builds its view from two other endpoints:
GET /api/v1/me/activity→ aggregate only:stats,hourly_contributions,repos[](activity_cmd.go:112)GET /api/v1/me/commits→ the commit list, each with nestedcheckpoints[](activity_cmd.go:136)
So the CLI has no session-list concept at all. Whatever "sessions" it appears to show are actually commits grouped by day (groupCommitsByDay, rendered at activity_cmd.go:83-86), and its only session-derived number is the Iteration stat — "avg session_count per checkpoint" (activity_types.go:66), which comes from userCommitCheckpoint.SessionCount on the commits payload.
entire.io's activity page, by contrast, lists real session records from GET /api/v1/me/sessions?timeframe=<tf>&limit=<n>. Different source, different grain (per-session vs per-commit), so the two lists won't line up — a commit can bundle several sessions, and a session with no resulting commit won't appear in the CLI at all.
Grep confirms it: no /me/sessions reference anywhere in the CLI.
Do you want me to point the CLI at /api/v1/me/sessions so it lists the same sessions as the web — either replacing the commits-derived view or adding a session list alongside it? If so, I'd need the response shape /me/sessions returns (or I can pull it from the entire-api handler / the entire.io frontend in the devenv monorepo to model the Go types).
yes, cli should do the same as the entire.io page, but we should stack it onto https://github.com/entireio/cli/pull/1641
I'll skip the task tracker for now and wait for the frontend exploration to complete — I'll pick the work back up when it reports.
Done. The feature is implemented, tested, and committed on a branch stacked on #1641.
What changed
entire activity's recent list now shows sessions (from GET /api/v1/me/sessions) by default, matching the entire.io Overview page. Commits move behind entire activity --commits.
- Sessions feed — fetched at
limit=50(mirroring the web'sUSER_OVERVIEW_RECENT_SESSIONS_LIMIT), grouped by local day oflastActivityAt(newest first), one row per session with the same fields the web row shows: display name (capped at 120), repo,publictag whenisPublic, agent badge, friendly model label, and checkpoint count. Stat cards / heatmap / repo chart (from/me/activity) are untouched. formatModelported fromentire.io/frontend/src/lib/model.tsto Go, with the same test cases (claude-opus-4-6→"Opus 4.6",gpt-4o→"GPT-4o",gemini-2.0-flash→"Gemini 2.0 Flash").- Routing — reuses
runAuthenticatedActivityAPI, so/me/sessionsgoes to the caller's home entire-api cell with the data-API fallback (the #1641 plumbing). - Dedup — extracted
fetchActivityWith(activity + chosen list, concurrent) and genericgroupItemsByDayso the commit and session paths share logic.
Files: model_label.go (+test), activity_sessions_test.go new; activity_{cmd,render,tui,types}.go + activity_cmd_test.go modified. agent-help picks up the new --commits flag automatically (renders live from the cobra tree).
Verification
mise run fmtclean,mise run lint0 issues,mise run test:cigreen (unit + integration + Vogon canary, 0 failures).- One caveat handled:
golangci-lint --fixlocally tried to strip a pre-existing//nolint:exhaustiveinexplain.go(unrelated; identical toorigin/main). I discarded that — removing it could trigger a newexhaustiveerror under CI's linter version, and CI'sonly-new-issueswon't flag the untouched line.
Stack
- Branch
soph/cli-activity-sessions-list→ based onsoph/cell-routing-foundation(#1641, tip9ef8a5cff). Commit607d2ec26, Entire trailer attached. - Note: #1641 itself is still behind
main(branched at86a21a829); it'll need a rebase before/at merge, and this stacks on top.
I held off on pushing / opening the PR since that's the outward-facing step. Want me to push and open the PR against soph/cell-routing-foundation? If so I'll write it up referencing #1641 as the base.
Rebase is done — 887f76405 sits cleanly on top of current origin/main (which includes the merged #1641), build and lint are clean, and cli package tests pass. Full test:ci is running in the background; I'll report the result when it completes.
Rebase complete and fully verified.
State
- Branch
soph/cli-activity-sessions-list→ commit887f76405, sitting cleanly on top oforigin/main(which now includes the merged #1641). No longer stacked — its base is plainmain. test:cigreen on the rebased branch (exit 0, 0 failures — unit + integration + Vogon canary), plusmise run lint0 issues and a clean working tree.
The explain.go --fix artifact from before is gone — the new main already reconciled that nolint, so lint no longer touches it.
The branch is unpushed. Want me to push it and open the PR against main? I'll write the PR body describing the sessions-vs-commits switch, the formatModel port, and the --commits flag (and drop the "stacked on #1641" note since it's now merged).
Check the test:ci background job (be06r7pw1) result for the rebased sessions branch and report pass/fail.
The test:ci job (be06r7pw1) already completed: PASS — exit code 0, 0 failures across unit + integration + Vogon canary. Nothing else pending from that run.
Current state of the rebased branch soph/cli-activity-sessions-list (887f76405, on top of origin/main): test:ci green, mise run lint 0 issues, clean working tree.
It's still unpushed — say the word and I'll push it and open the PR against main.
PR opened: https://github.com/entireio/cli/pull/1650
- Branch
soph/cli-activity-sessions-list(887f76405) pushed, PR basemain. - Body covers the sessions-vs-commits switch, the
--commitsflag, theformatModelport, the/me/sessionscell routing, and theisPubliccaveat — plus the test summary (fmt/lint/test:ci all green).
You may want to keep an eye on the automated reviewers (Bugbot/Copilot/Cursor) that commented on your recent PRs; happy to address their findings when they land.
Checked. Here's the full picture on PR #1650:
CI — all green
Cursor Bugbot (pass, no findings), lint, test, test-canary (git-branch + git-refs), test-integration (a/b/c), test-core, binary-size, licenses — all pass.
Reviews
Only Copilot has reviewed so far, with 1 inline nit (activity_render.go:583):
The 120-rune title cap slices to
sessionTitleMaxRunesand then appends an ellipsis, yielding a 121-rune string. If this is meant to be a hard 120-char cap, the slice should leave room for the ellipsis (or the comment/constant should be updated).
My assessment: benign, and the code is intentionally correct. The web does displayName.slice(0, 120) + "..." — i.e. it caps the content at 120 and appends an ellipsis on top (so the web string is up to 123 chars). Mine keeps 120 content runes + a 1-rune "…" = 121. So my output mirrors the web's intent faithfully; slicing to 119 would actually make it less faithful. Also, in practice the width-based truncation almost always kicks in first on a real terminal, so this cap is a belt-and-suspenders match to the web.
The only fair point is wording: "120-char cap" could read as a hard total. I'd tweak the comment to say the cap is on the display-name content (matching the web's slice(0,120)), and leave the slicing as-is.
Want me to push that one-line comment clarification, or leave it and mark the Copilot comment as addressed with a reply explaining the parity?
Done — Copilot's comment is addressed on PR #1650.
- Commit
a7dcecd2dpushed: reworded thesessionTitleMaxRunescomment so it's unambiguous that 120 caps the display-name content (matching the web'sslice(0, 120) + "…"), with the ellipsis appended on top. No behavior change — the slicing stays as-is because changing it would diverge from the web. Lint clean. - Replied to the inline comment explaining the parity (visible here).
Recap of the PR's review state: all CI green (lint, tests, both canaries, all integration shards, Cursor Bugbot pass), and the sole review nit (Copilot) is now resolved. The PR is clean and ready for a human review whenever you want to merge.