mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
wip
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import type { StoredAgentAttentionUnread } from '@/attention/agent-attention-contract'
|
||||
import { emitAgentSubjectReads } from '@/attention/agent-subject-read-actions'
|
||||
import type { AppState } from '@/store/types'
|
||||
import { paneKeyTabId } from '@/store/slices/workspace-attention/workspace-live-markers'
|
||||
import type { UISlice, UISliceGet, UISliceSet } from './ui-slice-contract'
|
||||
import {
|
||||
collectAcknowledgedAgentNotificationId,
|
||||
@@ -21,34 +19,11 @@ type ActivityActions = Pick<
|
||||
| 'clearManuallyUnreadTurns'
|
||||
>
|
||||
|
||||
/** Workspaces holding these panes: terminal tabs, structured chat tabs, or the agent's own record. */
|
||||
function workspaceIdsOfPaneKeys(s: AppState, paneKeys: readonly string[]): Set<string> {
|
||||
const ids = new Set<string>()
|
||||
for (const paneKey of paneKeys) {
|
||||
const tabId = paneKeyTabId(paneKey)
|
||||
for (const worktreeId of [
|
||||
resolvePaneKeyWorktreeIdFromTabs(s, paneKey),
|
||||
s.agentStatusByPaneKey?.[paneKey]?.worktreeId,
|
||||
s.retainedAgentsByPaneKey?.[paneKey]?.worktreeId,
|
||||
...Object.entries(s.unifiedTabsByWorktree ?? {})
|
||||
.filter(([, tabs]) => tabs.some((tab) => tab.id === tabId))
|
||||
.map(([worktreeId]) => worktreeId)
|
||||
]) {
|
||||
if (worktreeId) {
|
||||
ids.add(worktreeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
export function createUiActivityActions(set: UISliceSet, get: UISliceGet): ActivityActions {
|
||||
export function createUiActivityActions(set: UISliceSet, _get: UISliceGet): ActivityActions {
|
||||
return {
|
||||
acknowledgedAgentsByPaneKey: {},
|
||||
acknowledgeAgents: (paneKeys, reads, intent) => {
|
||||
emitAgentSubjectReads(paneKeys, reads, intent)
|
||||
const readWorkspaceIds =
|
||||
intent === 'explicit' ? workspaceIdsOfPaneKeys(get(), paneKeys) : null
|
||||
const notificationIdsToDismiss = new Set<string>()
|
||||
set((s) => {
|
||||
if (paneKeys.length === 0) {
|
||||
@@ -116,10 +91,6 @@ export function createUiActivityActions(set: UISliceSet, get: UISliceGet): Activ
|
||||
...(nextManual ? { manuallyUnreadTurnsByPaneKey: nextManual } : {})
|
||||
}
|
||||
})
|
||||
// Explicit reads skip the on-screen scan that would otherwise lower the workspace flag.
|
||||
if (readWorkspaceIds && readWorkspaceIds.size > 0) {
|
||||
get().settleWorkspacesAfterExplicitRead(readWorkspaceIds)
|
||||
}
|
||||
// Dismiss main’s announced subjects; rebuilt IDs cover restart-empty notification records.
|
||||
if (paneKeys.length > 0 && typeof window !== 'undefined') {
|
||||
void window.api?.notifications?.dismiss?.([...notificationIdsToDismiss], paneKeys)
|
||||
|
||||
@@ -43,6 +43,4 @@ export type WorkspaceAttentionSlice = {
|
||||
isUnread: boolean,
|
||||
executionHostId: ExecutionHostId
|
||||
) => void
|
||||
/** An explicit read elsewhere (Activity, dashboard, a jump) lowers flags left with nothing unread behind them. */
|
||||
settleWorkspacesAfterExplicitRead: (worktreeIds: Iterable<string>) => void
|
||||
}
|
||||
|
||||
@@ -145,7 +145,6 @@ export const createWorkspaceAttentionSlice: StateCreator<
|
||||
noteWorkspaceRead(worktreeId)
|
||||
const removed = removeAndDismiss(workspaceMarkerTabIds(get(), worktreeId))
|
||||
// Update only the clicked host’s row; markers stay workspace-wide because tab keys omit host.
|
||||
// Before the acknowledgement below, so its settle finds the flag already down and skips a second persist.
|
||||
void get().updateWorktreeMeta(worktreeId, { isUnread }, { executionHostId })
|
||||
// Mark read reads the agents too, or their lines keep the dot the user just cleared.
|
||||
// Journal-positioned chat alerts retire only with this explicit read that bounds them.
|
||||
@@ -156,15 +155,6 @@ export const createWorkspaceAttentionSlice: StateCreator<
|
||||
if (paneKeys.size > 0) {
|
||||
get().acknowledgeAgents([...paneKeys], undefined, 'explicit')
|
||||
}
|
||||
},
|
||||
settleWorkspacesAfterExplicitRead: (worktreeIds) => {
|
||||
const marked = get().workspacesMarkedUnreadByUser
|
||||
for (const worktreeId of new Set(worktreeIds)) {
|
||||
// The user's own mark is a source of its own; reading an agent there doesn't answer it.
|
||||
if (!marked[worktreeId]) {
|
||||
settleWorkspaceUnreadFlag(set, get, worktreeId, onScreenHostId(get(), worktreeId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-4
@@ -12,6 +12,7 @@ import {
|
||||
} from './workspace-unread-characterization-fixture'
|
||||
import { completeAgentTurn, toggleUnreadByUser } from './workspace-unread-characterization-driver'
|
||||
import { isWorkspaceFlagSet } from './workspace-unread-flag'
|
||||
import { selectAgentRowsUnread } from '@/components/sidebar/workspace-card/activity/worktree-card-agent-ack-inputs'
|
||||
|
||||
vi.mock('sonner', () => ({
|
||||
toast: { info: vi.fn(), success: vi.fn(), error: vi.fn(), warning: vi.fn(), dismiss: vi.fn() }
|
||||
@@ -51,7 +52,7 @@ function finishedAgentOffScreen(store: AttentionStore): PaneTarget {
|
||||
return pane
|
||||
}
|
||||
|
||||
describe('explicit reads keep the workspace flag and its agents in step', () => {
|
||||
describe('explicit workspace and agent reads', () => {
|
||||
it('Mark read also reads the agents, so their lines lose the dot with the flag', () => {
|
||||
const { store } = createAttentionStore()
|
||||
const pane = finishedAgentOffScreen(store)
|
||||
@@ -61,16 +62,21 @@ describe('explicit reads keep the workspace flag and its agents in step', () =>
|
||||
toggleUnreadByUser(store, row)
|
||||
|
||||
expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(false)
|
||||
expect(store.getState().acknowledgedAgentsByPaneKey[pane.paneKey]).toBeGreaterThanOrEqual(900)
|
||||
expect(selectAgentRowsUnread(store.getState(), [pane])).toEqual([false])
|
||||
})
|
||||
|
||||
it('reading the only unread agent elsewhere lowers the flag instead of leaving a phantom dot', () => {
|
||||
it('reading an agent elsewhere preserves a workspace marked unread before restart', () => {
|
||||
const { store: previousStore } = createAttentionStore()
|
||||
previousStore.getState().setWorkspaceUnreadByUser(WS_A, true, 'local')
|
||||
const { store } = createAttentionStore()
|
||||
store.setState({ worktreesByRepo: previousStore.getState().worktreesByRepo })
|
||||
const pane = finishedAgentOffScreen(store)
|
||||
expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(true)
|
||||
expect(selectAgentRowsUnread(store.getState(), [pane])).toEqual([true])
|
||||
|
||||
store.getState().acknowledgeAgents([pane.paneKey], undefined, 'explicit')
|
||||
|
||||
expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(false)
|
||||
expect(isWorkspaceFlagSet(store.getState(), WS_A)).toBe(true)
|
||||
expect(selectAgentRowsUnread(store.getState(), [pane])).toEqual([false])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user