__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