mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
feat(sidebar): show reference origin as an icon and record native chat origins (#33049)
Move the "Seen in" chip onto the review's meta line as a terminal icon with a tooltip. Native chat tabs now record PR links their tools print, so the icon appears for PRs created from chat too.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { useAppStore } from '@/store'
|
||||
import { sameStructuredReadTarget } from '@/attention/agent-subject-read-actions'
|
||||
import { structuredAgentSessionTargetForTab } from '@/runtime/structured-agent-session-owner'
|
||||
import type { AgentSessionSubscribeEvent } from '../../../../shared/agent-session-wire'
|
||||
import type { RuntimeClientTarget } from '../../../../shared/runtime-client-target'
|
||||
import { createTerminalGitHubPRLinkDetector } from '../../../../shared/terminal-github-pr-link-detector'
|
||||
import { getStructuredAgentSessionTabs } from './structured-agent-session-tabs'
|
||||
|
||||
/** Records PRs a chat's tools print (e.g. `gh pr create`) on its workspace, like terminal output. */
|
||||
export function createStructuredAgentSessionPRLinkObserver(
|
||||
sessionId: string,
|
||||
target: RuntimeClientTarget
|
||||
): (event: AgentSessionSubscribeEvent) => void {
|
||||
const detect = createTerminalGitHubPRLinkDetector()
|
||||
return (event) => {
|
||||
// Why: only live batches; replayed history would re-link PRs the user may have unlinked.
|
||||
if (event.type !== 'batch') {
|
||||
return
|
||||
}
|
||||
const links = event.batch.items.flatMap((item) =>
|
||||
item.body.kind === 'tool-call' && item.body.output?.head
|
||||
? detect(`${item.body.output.head}\n`)
|
||||
: []
|
||||
)
|
||||
if (!links.length) {
|
||||
return
|
||||
}
|
||||
const state = useAppStore.getState()
|
||||
const tab = getStructuredAgentSessionTabs(state.unifiedTabsByWorktree).find((candidate) => {
|
||||
const tabTarget =
|
||||
candidate.entityId === sessionId && structuredAgentSessionTargetForTab(state, candidate)
|
||||
return Boolean(tabTarget && sameStructuredReadTarget(tabTarget, target))
|
||||
})
|
||||
if (!tab) {
|
||||
return
|
||||
}
|
||||
for (const link of links) {
|
||||
state.observeTerminalGitHubPullRequestLink(tab.worktreeId, link, {
|
||||
kind: 'agent-session',
|
||||
tabId: tab.id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
readStructuredAgentSessionOlderPage
|
||||
} from './structured-agent-session-older-page-read'
|
||||
import { startStructuredAgentSessionReadTransport } from './structured-agent-session-read-transport'
|
||||
import { createStructuredAgentSessionPRLinkObserver } from './structured-agent-session-pr-link-observer'
|
||||
|
||||
import type {
|
||||
StructuredAgentSessionReadSnapshot,
|
||||
@@ -61,6 +62,7 @@ function createReadOwner(
|
||||
let captureActiveHistoryReadGuard = (): (() => boolean) => retiredHistoryRead
|
||||
const activations = new Set<symbol>()
|
||||
const listeners = new Set<() => void>()
|
||||
const observePRLinks = createStructuredAgentSessionPRLinkObserver(sessionId, target)
|
||||
|
||||
const emit = (): void => {
|
||||
for (const listener of listeners) {
|
||||
@@ -196,7 +198,10 @@ function createReadOwner(
|
||||
}
|
||||
const transport = startStructuredAgentSessionReadTransport({
|
||||
applyRequests: apply,
|
||||
applyEvent: (event, options) => apply({ type: 'event', event, ...options }),
|
||||
applyEvent: (event, options) => {
|
||||
apply({ type: 'event', event, ...options })
|
||||
observePRLinks(event)
|
||||
},
|
||||
applyError: (message, refusal) => {
|
||||
// Once loaded, a failure with no host refusal attached is treated as lost contact: the
|
||||
// transport retries it and the chat stays as it is. A refusal is the host's answer: shown.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react'
|
||||
import { Terminal } from 'lucide-react'
|
||||
import { useAppStore } from '@/store'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { resolveNotificationTabOwner } from '@/attention/notification-subject-owner'
|
||||
import { terminalLayoutContainsLeaf } from '../../../../shared/workspace-session-pane-ownership'
|
||||
@@ -24,12 +26,32 @@ function useReferenceTabs(workspace: Worktree): Tab[] {
|
||||
})
|
||||
}
|
||||
|
||||
function focusOrigin(workspace: Worktree, tab: Tab, paneKey: string | undefined): void {
|
||||
const state = useAppStore.getState()
|
||||
if (!state.setActiveWorktree(workspace.id, workspace.hostId)) {
|
||||
return
|
||||
}
|
||||
state.activateTab(tab.id, { worktreeId: workspace.id })
|
||||
const pane = paneKey ? parsePaneKey(paneKey) : null
|
||||
if (
|
||||
tab.contentType === 'terminal' &&
|
||||
pane &&
|
||||
pane.tabId === tab.entityId &&
|
||||
state.tabsByWorktree[workspace.id]?.some((entry) => entry.id === tab.entityId) &&
|
||||
terminalLayoutContainsLeaf(state.terminalLayoutsByTabId[tab.entityId]?.root, pane.leafId)
|
||||
) {
|
||||
activateTabAndFocusPane(tab.entityId, pane.leafId)
|
||||
}
|
||||
}
|
||||
|
||||
export function WorkspaceAttachmentOrigins({
|
||||
item,
|
||||
workspace
|
||||
workspace,
|
||||
compact = false
|
||||
}: {
|
||||
item: WorkspaceAttachment
|
||||
workspace: Worktree
|
||||
compact?: boolean
|
||||
}): React.JSX.Element | null {
|
||||
const tabs = useReferenceTabs(workspace)
|
||||
const origins = item.origins?.filter((origin) => origin.kind === 'observed') ?? []
|
||||
@@ -37,7 +59,9 @@ export function WorkspaceAttachmentOrigins({
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-1 pl-5">
|
||||
<div
|
||||
className={compact ? 'flex shrink-0 items-center' : 'flex flex-wrap items-center gap-1 pl-5'}
|
||||
>
|
||||
{origins.map((origin) => {
|
||||
const tab = tabs.find(
|
||||
(candidate) =>
|
||||
@@ -51,6 +75,35 @@ export function WorkspaceAttachmentOrigins({
|
||||
origin.agent ||
|
||||
translate('workspace.links.terminal', 'Terminal')
|
||||
const description = translate('workspace.links.seenIn', 'Seen in {{label}}', { label })
|
||||
if (compact) {
|
||||
const unavailable = translate('workspace.links.unavailableTab', 'Tab unavailable')
|
||||
return (
|
||||
<Tooltip key={getWorkspaceAttachmentOriginKey(origin)}>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
className="size-4"
|
||||
aria-disabled={!tab || undefined}
|
||||
aria-label={tab ? description : `${description} · ${unavailable}`}
|
||||
onKeyDown={(event) => event.stopPropagation()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
if (tab) {
|
||||
focusOrigin(workspace, tab, origin.paneKey)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Terminal className="size-3 text-muted-foreground" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={4}>
|
||||
{tab ? description : `${description} · ${unavailable}`}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
return tab ? (
|
||||
<Button
|
||||
key={getWorkspaceAttachmentOriginKey(origin)}
|
||||
@@ -65,24 +118,7 @@ export function WorkspaceAttachmentOrigins({
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
const state = useAppStore.getState()
|
||||
if (!state.setActiveWorktree(workspace.id, workspace.hostId)) {
|
||||
return
|
||||
}
|
||||
state.activateTab(tab.id, { worktreeId: workspace.id })
|
||||
const pane = origin.paneKey ? parsePaneKey(origin.paneKey) : null
|
||||
if (
|
||||
tab.contentType === 'terminal' &&
|
||||
pane &&
|
||||
pane.tabId === tab.entityId &&
|
||||
state.tabsByWorktree[workspace.id]?.some((entry) => entry.id === tab.entityId) &&
|
||||
terminalLayoutContainsLeaf(
|
||||
state.terminalLayoutsByTabId[tab.entityId]?.root,
|
||||
pane.leafId
|
||||
)
|
||||
) {
|
||||
activateTabAndFocusPane(tab.entityId, pane.leafId)
|
||||
}
|
||||
focusOrigin(workspace, tab, origin.paneKey)
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
|
||||
@@ -113,6 +113,11 @@ export function WorktreeCardLinkedItemsSection({
|
||||
url={url}
|
||||
onOpen={(url, event) => open({ ...item, url }, event)}
|
||||
onManage={onManage ? (event) => open(item, event) : undefined}
|
||||
origins={
|
||||
item.origins?.length ? (
|
||||
<WorkspaceAttachmentOrigins item={item} workspace={workspace} compact />
|
||||
) : undefined
|
||||
}
|
||||
actions={
|
||||
item.type !== 'issue' ? renderReviewActions?.(item, live) : undefined
|
||||
}
|
||||
@@ -139,9 +144,6 @@ export function WorktreeCardLinkedItemsSection({
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{item.origins?.length ? (
|
||||
<WorkspaceAttachmentOrigins item={item} workspace={workspace} />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -31,6 +31,7 @@ export function WorktreeCardReferenceRow({
|
||||
url,
|
||||
onOpen,
|
||||
onManage,
|
||||
origins,
|
||||
actions
|
||||
}: {
|
||||
review: WorktreeCardPrDisplay | null
|
||||
@@ -43,6 +44,7 @@ export function WorktreeCardReferenceRow({
|
||||
url?: string
|
||||
onOpen: (url: string, event: MouseEvent) => void
|
||||
onManage?: (event: MouseEvent) => void
|
||||
origins?: ReactNode
|
||||
actions?: ReactNode
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
@@ -127,6 +129,7 @@ export function WorktreeCardReferenceRow({
|
||||
{provider === 'GitHub' ? null : `· ${provider}`}
|
||||
{source ? ` · ${source}` : ''}
|
||||
</span>
|
||||
{origins}
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+44
-1
@@ -3,7 +3,10 @@ import type { AppState } from '@/store/types'
|
||||
import { makeTerminalTab } from '../store/slices/worktrees-slice-test-fixtures'
|
||||
import { singlePaneLayoutSnapshot } from '../store/slices/terminal-helpers'
|
||||
import { makePaneKey } from '../../../shared/stable-pane-id'
|
||||
import { getWorkspaceAttachmentTerminalOrigin } from './workspace-attachment-terminal-origin'
|
||||
import {
|
||||
getWorkspaceAttachmentObservedOrigin,
|
||||
getWorkspaceAttachmentTerminalOrigin
|
||||
} from './workspace-attachment-observed-origin'
|
||||
|
||||
const workspaceId = 'repo::/feature'
|
||||
const leaf = '11111111-1111-4111-8111-111111111111'
|
||||
@@ -91,3 +94,43 @@ describe('reference evidence terminal identity', () => {
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('reference evidence native chat identity', () => {
|
||||
it('records the chat tab and its session without a terminal pane', () => {
|
||||
const chat = {
|
||||
...state(),
|
||||
unifiedTabsByWorktree: {
|
||||
[workspaceId]: [
|
||||
{
|
||||
id: 'chat-tab',
|
||||
entityId: 'chat-session',
|
||||
groupId: 'group',
|
||||
worktreeId: workspaceId,
|
||||
contentType: 'agent-session' as const,
|
||||
agentSessionAgent: 'claude' as const,
|
||||
label: 'Plan refactor',
|
||||
customLabel: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
expect(
|
||||
getWorkspaceAttachmentObservedOrigin(
|
||||
chat,
|
||||
workspaceId,
|
||||
{ kind: 'agent-session', tabId: 'chat-tab' },
|
||||
'local'
|
||||
)
|
||||
).toEqual({
|
||||
kind: 'observed',
|
||||
tabId: 'chat-tab',
|
||||
hostId: 'local',
|
||||
label: 'Plan refactor',
|
||||
agent: 'claude',
|
||||
sessionId: 'chat-session'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { AppState } from '@/store/types'
|
||||
import type { ExecutionHostId } from '../../../shared/execution-host'
|
||||
import type { NotificationWorkspaceOwner } from '../../../shared/notification-source'
|
||||
import {
|
||||
resolveNotificationTabOwner,
|
||||
resolveTerminalNotificationOwner
|
||||
} from '@/attention/notification-subject-owner'
|
||||
import type { WorkspaceAttachmentOrigin } from '../../../shared/worktree/types'
|
||||
import { parsePaneKey } from '../../../shared/stable-pane-id'
|
||||
import { terminalLayoutContainsLeaf } from '../../../shared/workspace-session-pane-ownership'
|
||||
import type { Tab } from '../../../shared/tab-types'
|
||||
|
||||
export type WorkspaceReferenceTerminalContext = {
|
||||
kind?: 'terminal'
|
||||
tabId: string
|
||||
paneKey: string
|
||||
ptyId?: string | null
|
||||
executionHostId?: ExecutionHostId
|
||||
}
|
||||
|
||||
export type WorkspaceReferenceAgentSessionContext = { kind: 'agent-session'; tabId: string }
|
||||
|
||||
export type WorkspaceReferenceObservationContext =
|
||||
| WorkspaceReferenceTerminalContext
|
||||
| WorkspaceReferenceAgentSessionContext
|
||||
|
||||
type ObservationState = Parameters<typeof resolveTerminalNotificationOwner>[0] &
|
||||
Pick<AppState, 'tabsByWorktree' | 'agentStatusByPaneKey' | 'terminalLayoutsByTabId'>
|
||||
|
||||
function findAgentSessionTab(
|
||||
state: Partial<Pick<AppState, 'unifiedTabsByWorktree'>>,
|
||||
worktreeId: string,
|
||||
tabId: string
|
||||
): Tab | undefined {
|
||||
return state.unifiedTabsByWorktree?.[worktreeId]?.find(
|
||||
(tab) => tab.id === tabId && tab.contentType === 'agent-session'
|
||||
)
|
||||
}
|
||||
|
||||
export function resolveWorkspaceReferenceObservationOwner(
|
||||
state: ObservationState,
|
||||
worktreeId: string,
|
||||
context: WorkspaceReferenceObservationContext
|
||||
): NotificationWorkspaceOwner | null {
|
||||
if (context.kind === 'agent-session') {
|
||||
const tab = findAgentSessionTab(state, worktreeId, context.tabId)
|
||||
return tab ? resolveNotificationTabOwner(state, tab) : null
|
||||
}
|
||||
return resolveTerminalNotificationOwner(state, worktreeId, context)
|
||||
}
|
||||
|
||||
export function getWorkspaceAttachmentObservedOrigin(
|
||||
state: ObservationState,
|
||||
worktreeId: string,
|
||||
context: WorkspaceReferenceObservationContext,
|
||||
hostId: ExecutionHostId | undefined
|
||||
): WorkspaceAttachmentOrigin | undefined {
|
||||
if (context.kind !== 'agent-session') {
|
||||
return getWorkspaceAttachmentTerminalOrigin(state, worktreeId, context, hostId)
|
||||
}
|
||||
const tab = findAgentSessionTab(state, worktreeId, context.tabId)
|
||||
if (!tab) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
kind: 'observed',
|
||||
tabId: tab.id,
|
||||
...(hostId ? { hostId } : {}),
|
||||
label: tab.customLabel || tab.label,
|
||||
...(tab.agentSessionAgent ? { agent: tab.agentSessionAgent } : {}),
|
||||
sessionId: tab.entityId
|
||||
}
|
||||
}
|
||||
|
||||
export function getWorkspaceAttachmentTerminalOrigin(
|
||||
state: Pick<AppState, 'tabsByWorktree' | 'agentStatusByPaneKey' | 'terminalLayoutsByTabId'>,
|
||||
worktreeId: string,
|
||||
context: WorkspaceReferenceTerminalContext,
|
||||
hostId: ExecutionHostId | undefined
|
||||
): WorkspaceAttachmentOrigin | undefined {
|
||||
const tab = state.tabsByWorktree?.[worktreeId]?.find(
|
||||
(candidate) => candidate.id === context.tabId
|
||||
)
|
||||
const pane = parsePaneKey(context.paneKey)
|
||||
const layout = state.terminalLayoutsByTabId?.[context.tabId]
|
||||
if (!tab || !pane || pane.tabId !== tab.id || !layout) {
|
||||
return undefined
|
||||
}
|
||||
if (!terminalLayoutContainsLeaf(layout.root, pane.leafId)) {
|
||||
return undefined
|
||||
}
|
||||
const ptyId =
|
||||
layout.ptyIdsByLeafId?.[pane.leafId] ?? (layout.root?.type === 'leaf' ? tab.ptyId : null)
|
||||
if (!context.ptyId || context.ptyId !== ptyId) {
|
||||
return undefined
|
||||
}
|
||||
const entry = state.agentStatusByPaneKey?.[context.paneKey]
|
||||
const matchingEntry =
|
||||
(entry?.worktreeId && entry.worktreeId !== worktreeId) ||
|
||||
(entry?.terminalHandle && entry.terminalHandle !== ptyId)
|
||||
? undefined
|
||||
: entry
|
||||
return {
|
||||
kind: 'observed',
|
||||
tabId: tab.id,
|
||||
paneKey: context.paneKey,
|
||||
...(hostId ? { hostId } : {}),
|
||||
label: tab.customTitle || tab.defaultTitle || tab.title,
|
||||
...(matchingEntry?.agentType ? { agent: matchingEntry.agentType } : {}),
|
||||
...(matchingEntry?.providerSession?.id ? { sessionId: matchingEntry.providerSession.id } : {})
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { AppState } from '@/store/types'
|
||||
import type { ExecutionHostId } from '../../../shared/execution-host'
|
||||
import type { WorkspaceAttachmentOrigin } from '../../../shared/worktree/types'
|
||||
import { parsePaneKey } from '../../../shared/stable-pane-id'
|
||||
import { terminalLayoutContainsLeaf } from '../../../shared/workspace-session-pane-ownership'
|
||||
|
||||
export type WorkspaceReferenceTerminalContext = {
|
||||
tabId: string
|
||||
paneKey: string
|
||||
ptyId?: string | null
|
||||
executionHostId?: ExecutionHostId
|
||||
}
|
||||
|
||||
export function getWorkspaceAttachmentTerminalOrigin(
|
||||
state: Pick<AppState, 'tabsByWorktree' | 'agentStatusByPaneKey' | 'terminalLayoutsByTabId'>,
|
||||
worktreeId: string,
|
||||
context: WorkspaceReferenceTerminalContext,
|
||||
hostId: ExecutionHostId | undefined
|
||||
): WorkspaceAttachmentOrigin | undefined {
|
||||
const tab = state.tabsByWorktree?.[worktreeId]?.find(
|
||||
(candidate) => candidate.id === context.tabId
|
||||
)
|
||||
const pane = parsePaneKey(context.paneKey)
|
||||
const layout = state.terminalLayoutsByTabId?.[context.tabId]
|
||||
if (!tab || !pane || pane.tabId !== tab.id || !layout) {
|
||||
return undefined
|
||||
}
|
||||
if (!terminalLayoutContainsLeaf(layout.root, pane.leafId)) {
|
||||
return undefined
|
||||
}
|
||||
const ptyId =
|
||||
layout.ptyIdsByLeafId?.[pane.leafId] ?? (layout.root?.type === 'leaf' ? tab.ptyId : null)
|
||||
if (!context.ptyId || context.ptyId !== ptyId) {
|
||||
return undefined
|
||||
}
|
||||
const entry = state.agentStatusByPaneKey?.[context.paneKey]
|
||||
const matchingEntry =
|
||||
(entry?.worktreeId && entry.worktreeId !== worktreeId) ||
|
||||
(entry?.terminalHandle && entry.terminalHandle !== ptyId)
|
||||
? undefined
|
||||
: entry
|
||||
return {
|
||||
kind: 'observed',
|
||||
tabId: tab.id,
|
||||
paneKey: context.paneKey,
|
||||
...(hostId ? { hostId } : {}),
|
||||
label: tab.customTitle || tab.defaultTitle || tab.title,
|
||||
...(matchingEntry?.agentType ? { agent: matchingEntry.agentType } : {}),
|
||||
...(matchingEntry?.providerSession?.id ? { sessionId: matchingEntry.providerSession.id } : {})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { WorkspaceAttachmentMutation } from '../../../../shared/workspace-attachment-mutation'
|
||||
import type { WorkspaceReferenceTerminalContext } from '@/lib/workspace-attachment-terminal-origin'
|
||||
import type { WorkspaceReferenceObservationContext } from '@/lib/workspace-attachment-observed-origin'
|
||||
import type { CreateWorktreeCallOptions } from './worktrees/create/worktree-create-payload'
|
||||
import type { WorktreeCatalogVersion } from '../../../../shared/worktree/catalog-version'
|
||||
import type { WorkspaceKey } from '../../../../shared/folder-workspace-types'
|
||||
@@ -288,7 +288,7 @@ export type WorktreeSlice = {
|
||||
observeTerminalGitHubPullRequestLink: (
|
||||
worktreeId: string,
|
||||
link: TerminalGitHubPRLink,
|
||||
context?: WorkspaceReferenceTerminalContext
|
||||
context?: WorkspaceReferenceObservationContext
|
||||
) => void
|
||||
bumpWorktreeActivity: (worktreeId: string) => void
|
||||
/**
|
||||
|
||||
@@ -8,8 +8,10 @@ import { getRuntimeEnvironmentRevision } from '@/runtime/runtime-environment-rev
|
||||
import type { WorktreeSlice } from '../../worktree-helpers'
|
||||
import type { WorktreeSliceGet } from '../listing/worktree-slice-types'
|
||||
import { branchName } from '@/lib/git-utils'
|
||||
import { resolveTerminalNotificationOwner } from '@/attention/notification-subject-owner'
|
||||
import { getWorkspaceAttachmentTerminalOrigin } from '@/lib/workspace-attachment-terminal-origin'
|
||||
import {
|
||||
getWorkspaceAttachmentObservedOrigin,
|
||||
resolveWorkspaceReferenceObservationOwner
|
||||
} from '@/lib/workspace-attachment-observed-origin'
|
||||
import { getWorkspaceAttachments } from '../../../../../../shared/workspace-attachments'
|
||||
import { getWorkspaceAttachmentKey } from '../../../../../../shared/workspace-attachment-normalization'
|
||||
import {
|
||||
@@ -46,9 +48,12 @@ export function createObserveTerminalGitHubPullRequestLink(
|
||||
const confirmations = new PrioritySemaphore(3)
|
||||
return (worktreeId, link, context) => {
|
||||
const state = get()
|
||||
const owner = context ? resolveTerminalNotificationOwner(state, worktreeId, context) : null
|
||||
const owner = context
|
||||
? resolveWorkspaceReferenceObservationOwner(state, worktreeId, context)
|
||||
: null
|
||||
const hostId = owner?.executionHostId ?? undefined
|
||||
if (context && (!hostId || (context.executionHostId && context.executionHostId !== hostId))) {
|
||||
const contextHostId = context?.kind === 'agent-session' ? undefined : context?.executionHostId
|
||||
if (context && (!hostId || (contextHostId && contextHostId !== hostId))) {
|
||||
return
|
||||
}
|
||||
const worktree = findKnownWorktreeById(state, worktreeId, hostId)
|
||||
@@ -75,7 +80,7 @@ export function createObserveTerminalGitHubPullRequestLink(
|
||||
const pushBranch = worktree.pushTarget?.branchName
|
||||
let confirmedPushBranch: string | undefined
|
||||
const origin = context
|
||||
? getWorkspaceAttachmentTerminalOrigin(state, worktreeId, context, hostId)
|
||||
? getWorkspaceAttachmentObservedOrigin(state, worktreeId, context, hostId)
|
||||
: undefined
|
||||
if (context && !origin) {
|
||||
return
|
||||
@@ -85,8 +90,8 @@ export function createObserveTerminalGitHubPullRequestLink(
|
||||
return true
|
||||
}
|
||||
const latest = get()
|
||||
const latestOwner = resolveTerminalNotificationOwner(latest, worktreeId, context)
|
||||
const latestOrigin = getWorkspaceAttachmentTerminalOrigin(latest, worktreeId, context, hostId)
|
||||
const latestOwner = resolveWorkspaceReferenceObservationOwner(latest, worktreeId, context)
|
||||
const latestOrigin = getWorkspaceAttachmentObservedOrigin(latest, worktreeId, context, hostId)
|
||||
return (
|
||||
latestOwner?.executionHostId === hostId &&
|
||||
Boolean(
|
||||
|
||||
Reference in New Issue
Block a user