__tests__fix(agents): address Greptile review findings on PR #2771
- getAgentChats never applied the conversationId scope the executions branch
already did, so the Chats tab in a conversation-scoped agent workspace
listed every chat with the agent instead of only chats about that
conversation. Thread it through with the same jsonb predicate
chat.listThreadsForConversation already uses.
- AgentRunsTab combined the executions and chats loading states into one
flag, so a slow request on the tab NOT being viewed hid data that was
already ready on the one that WAS. Split into executionsLoading/
chatsLoading and only consult the flag for the currently-selected side.
Co-Authored-By: Claude Sonnet 5 <<email>>
Claude-Session: https://claude.ai/code/session_0187itFj1uakga2xd7kPk4qpSep 12, 2026, 2:30 PMcomponentsMerge remote-tracking branch 'origin/staging' into feat/inbox-client-composed-feed
# Conflicts:
# apps/mail/modules/agents/components/AgentOutputTab.tsx
# apps/mail/tests/modules/linkedin/LinkedInCounterpartCard.test.tsxSep 14, 2026, 8:19 PM1 defecthooksfix(agents): address Greptile review findings on PR #2759
Two real gaps in the org-admin scoping, both confirmed against AgentView's
actual render tree before fixing:
1. targetUserId reached only the five queries AgentView issues directly.
Config, Memory, the connection dialogs, and every header/settings action
(setFolder, setDefaultFile, publish/unpublish, delete, the MCP grant and
connection-scope toggles, playbook sources, rename) are independent
trpc.agent.* calls made by child components several layers down, none of
which had targetUserId threaded to them. An admin could open a teammate's
agent and see the header, but every other tab/action 404'd. Traced the
full tree (AgentHeader -> AgentActionMenu -> AgentShareMenu's dialog
bodies, AgentInstructionsSection -> AgentSettingsSection, AgentSourcesPanel,
AgentConnectionsPanel, AgentAddConnectionDialog, AgentMemoryTab,
AgentOutputTab, plus the useRenameAgent/useSetAgentDefaultFile/
useAgentPlaybookVersion hooks) and threaded targetUserId through every one
as an explicit prop -- never read ambiently, matching AgentView's own
documented rule, since none of these files have (or should have) a picker
of their own.
`duplicate` is unaffected by design (see the prior commit: it always
creates a copy for the caller, and the mutation call there was already
correct without a target). The Output and Memory tabs' underlying file
TREES remain a separate, deeper gap: FileBrowserScope's `user` variant
carries no id at all (by design -- it resolves the signed-in session
internally), so listing a teammate's actual files needs a FileBrowserScope
change this PR doesn't make. Documented at the one prop that touches it
(AgentOutputTab's targetUserId) rather than left silent.
2. CompanyExplorer's targetUserId was read-only URL state while in-page file
selection (a tree row, a breadcrumb crumb) updates only documentId. After
opening teammate A's document and then selecting a different one in the
same session, the stale A scope rode along to documents.getDoc for a
document A does not own -> FORBIDDEN on a selection that should have
succeeded. Cleared the owner param in the same effect that already pushes
an in-page selection into the documentId URL param.
Comment on the test file's `as unknown as DB` / `as Promise<unknown[]> &
Record<string, unknown>` casts: verified these are byte-identical to the
pre-existing, already-merged agent-router-auth.test.ts this file was modeled
on (same stub-DB pattern for a Drizzle query builder chain) -- replying on
the PR rather than diverging from that convention in one file only.
Co-Authored-By: Claude Sonnet 5 <<email>>
Claude-Session: https://claude.ai/code/session_01SfYKPYHXdMgrPLuKhXvWedSep 11, 2026, 10:13 AMutilsmerge: origin/staging — two mechanisms for "whose agent am I looking at", folded into one
Staging shipped org-admin agent viewing while this branch shipped the view-as
perspective, and they are the same feature built from opposite ends. Staging's
`targetUserId` is a PROP threaded by the caller that knows whose document is on screen,
gated by `resolveActorUserId` through `authorize()`. This branch's `asUserId` is read
from context, chosen in a control above the view, gated by `viewerForRead`.
Kept staging's, because it is the one that won on reach: 50 references across the agent
router and ten components against our seven, all of it already merged. Ours survives as
the UI — `ViewAsProvider` still wraps the view, and `AgentView` folds the picked
perspective into `targetUserId` (`viewingAs`, prop first) rather than sending a second
field the server would have to reconcile. Two parameters asking "whose data is this"
would have been two gates to keep in step, which is the failure this branch exists to
remove; the surviving one routes through `authorize()`, where the policy lives.
`viewerForRead` is untouched and still gates `files.*`.
The Memory TAB stays deleted. Staging threaded a prop through it; this branch removed
the tab entirely and made memory a section of the Output browser, and `?tab=memory`
already redirects there.
One thing fixed that was not a conflict: `chat-entry.ts` does not compile on staging
today — the cast bridging its untyped Hono app to `handleChatStreamRequest` stopped
working when `HonoVariables` gained a field. Verified against a clean origin/staging
checkout, so it is not merge fallout; it arrives with the merge and would have turned
this PR red. Typing the app `Hono<HonoContext>` removes the cast rather than widening
it, and nothing about the request changes. Also filled in a `SubagentSummary` stub in
one of staging's new tests, which predates `instructions`.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 11, 2026, 11:23 AMindex.tsrefactor(agents): the agent view gets its own name, and the breadcrumb its own row
The screen an agent link opens was called AgentWorkspace while its counterpart for
the other kind of object this app opens is ConversationView. Renamed to AgentView so
the pair reads as a pair — file, tests, barrel export, lazy import and the doc links
that pointed at the old path.
Breadcrumbs were text-xs and squeezed onto rows beside tabs and buttons, which made a
path you can click back up read as a caption. The shared Breadcrumb now owns its own
row — text-sm crumbs, a leading slot, string labels that truncate — and the six
hand-rolled copies (each with a private CRUMB_BTN class string) were folded into it.
The reading column both views share goes 75ch to 100ch: these panels carry tables,
cards and file rows as well as prose. Four places hold that number — the column, its
padding twin, and the two back gutters that size the margin beside it — and they move
together or the gutter overlaps the content.
Agent names arrive as typed titles ("Deal brain") and as seeded slugs
("daily-sales-coaching"), so half of them rendered as filenames. agentDisplayName
turns separators into spaces and capitalises the first letter, and nothing else —
title-casing would rewrite what a person chose. Applied everywhere the name is shown,
but never to the duplicate dialog's default, which is a value that gets saved.
Also carries another session's FOLDER_LABELS -> AGENT_FOLDER_LABELS move in
AgentActionMenu, which could not be separated from the file.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 12:23 AMqueries.tsfix(agents): address Greptile review findings on PR #2759
Two real gaps in the org-admin scoping, both confirmed against AgentView's
actual render tree before fixing:
1. targetUserId reached only the five queries AgentView issues directly.
Config, Memory, the connection dialogs, and every header/settings action
(setFolder, setDefaultFile, publish/unpublish, delete, the MCP grant and
connection-scope toggles, playbook sources, rename) are independent
trpc.agent.* calls made by child components several layers down, none of
which had targetUserId threaded to them. An admin could open a teammate's
agent and see the header, but every other tab/action 404'd. Traced the
full tree (AgentHeader -> AgentActionMenu -> AgentShareMenu's dialog
bodies, AgentInstructionsSection -> AgentSettingsSection, AgentSourcesPanel,
AgentConnectionsPanel, AgentAddConnectionDialog, AgentMemoryTab,
AgentOutputTab, plus the useRenameAgent/useSetAgentDefaultFile/
useAgentPlaybookVersion hooks) and threaded targetUserId through every one
as an explicit prop -- never read ambiently, matching AgentView's own
documented rule, since none of these files have (or should have) a picker
of their own.
`duplicate` is unaffected by design (see the prior commit: it always
creates a copy for the caller, and the mutation call there was already
correct without a target). The Output and Memory tabs' underlying file
TREES remain a separate, deeper gap: FileBrowserScope's `user` variant
carries no id at all (by design -- it resolves the signed-in session
internally), so listing a teammate's actual files needs a FileBrowserScope
change this PR doesn't make. Documented at the one prop that touches it
(AgentOutputTab's targetUserId) rather than left silent.
2. CompanyExplorer's targetUserId was read-only URL state while in-page file
selection (a tree row, a breadcrumb crumb) updates only documentId. After
opening teammate A's document and then selecting a different one in the
same session, the stale A scope rode along to documents.getDoc for a
document A does not own -> FORBIDDEN on a selection that should have
succeeded. Cleared the owner param in the same effect that already pushes
an in-page selection into the documentId URL param.
Comment on the test file's `as unknown as DB` / `as Promise<unknown[]> &
Record<string, unknown>` casts: verified these are byte-identical to the
pre-existing, already-merged agent-router-auth.test.ts this file was modeled
on (same stub-DB pattern for a Drizzle query builder chain) -- replying on
the PR rather than diverging from that convention in one file only.
Co-Authored-By: Claude Sonnet 5 <<email>>
Claude-Session: https://claude.ai/code/session_01SfYKPYHXdMgrPLuKhXvWedSep 11, 2026, 10:13 AMtypes.tsfeat(agents): Config moves last, and the runs tab leads with chats
Three changes to the agent workspace's tab strip.
Config is now the THIRD tab. It is the tab you visit to change the agent, where
Files and Runs are the tabs you visit to see what it did, and the reading pass is
the frequent one.
"Previous Runs" becomes "Runs and Chats". The tab has held both for a while, so
the old label named the smaller half — and every run in it is previous by
definition, so the word was spent on nothing.
Chats lead inside that tab, and are the default WHERE THERE ARE CHATS. That
condition is the part worth keeping: chat-to-agent binding is not built yet
(the empty state says so), so a fixed 'chats' default would open every agent in
the product on an empty pane. The default is derived from the data rather than
frozen into a useState initial value — which would also have been computed
against undefined, since runs arrive after first render — and the moment the
person picks a side it sticks, even if that side is empty.
The tab KEYS are untouched: `output` is part of the URL contract, so the strip is
reordered and relabelled without renaming anything other surfaces link to.
242 tests pass, including three new ones pinning which side the tab opens on.
Co-Authored-By: Claude Opus 5 <<email>>Sep 7, 2026, 1:11 PM