mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
chore: remove verified unused Orca code (#27175)
* chore: remove verified unused Orca code * chore: remove translations orphaned by dead code cleanup
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
|
||||
import type { AgentKind } from '../../../shared/telemetry-events'
|
||||
import {
|
||||
getAgentResumeArgv,
|
||||
@@ -84,7 +82,3 @@ export function paneCacheKeyTabId(key: string): string | null {
|
||||
export function paneCacheKeyMatchesTab(key: string, tabId: string): boolean {
|
||||
return paneCacheKeyTabId(key) === tabId
|
||||
}
|
||||
|
||||
export function hashLaunchToken(value: string): string {
|
||||
return createHash('sha256').update(value).digest('hex')
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export async function resolveWslHookDefaultDistro(
|
||||
currentDistro: string | null,
|
||||
listDistros: () => Promise<string[]>
|
||||
): Promise<string | null> {
|
||||
if (currentDistro) {
|
||||
return currentDistro
|
||||
}
|
||||
try {
|
||||
return (await listDistros())[0] ?? null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
export function resumeStoppedWslHookRelays(
|
||||
stoppedByHooksOff: Map<string, string | undefined>,
|
||||
isDistroRunning: (distro: string) => Promise<boolean>,
|
||||
ensureForDistro: (distro: string, codexHomePath?: string) => void
|
||||
): void {
|
||||
const distros = [...stoppedByHooksOff]
|
||||
stoppedByHooksOff.clear()
|
||||
for (const [distro, codexHomePath] of distros) {
|
||||
void isDistroRunning(distro)
|
||||
.then((running) => {
|
||||
if (running) {
|
||||
ensureForDistro(distro, codexHomePath)
|
||||
}
|
||||
})
|
||||
.catch(() => undefined)
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,6 @@ const cache = new Map<string, RemoteSessionParseCacheEntry>()
|
||||
|
||||
export type RemoteSessionParseStats = { reused: number; parsed: number }
|
||||
|
||||
export function createRemoteSessionParseStats(): RemoteSessionParseStats {
|
||||
return { reused: 0, parsed: 0 }
|
||||
}
|
||||
|
||||
export function resetRemoteSessionParseCacheForTests(): void {
|
||||
cache.clear()
|
||||
}
|
||||
|
||||
@@ -54,12 +54,3 @@ export async function fetchBitbucketUserResult(
|
||||
return { ok: false, reason: 'unreachable' }
|
||||
}
|
||||
}
|
||||
|
||||
/** Convenience for callers that only need the account, not the failure reason. */
|
||||
export async function fetchBitbucketUser(
|
||||
config: BitbucketAuthConfig,
|
||||
timeoutMs: number = USER_REQUEST_TIMEOUT_MS
|
||||
): Promise<RawBitbucketUser | null> {
|
||||
const result = await fetchBitbucketUserResult(config, timeoutMs)
|
||||
return result.ok ? result.user : null
|
||||
}
|
||||
|
||||
@@ -43,12 +43,6 @@ export function cookieEncryptionVersion(encryptedBuffer: Buffer): string | null
|
||||
return /^v\d\d$/.test(version) ? version : null
|
||||
}
|
||||
|
||||
// Why: Chrome/Edge 140+ on Windows prefix every cookie with `v20` (app-bound encryption), which
|
||||
// only the writing browser can unwrap. Classify it before decrypt so it is not folded into corruption.
|
||||
export function isAppBoundEncryptedCookie(encryptedBuffer: Buffer): boolean {
|
||||
return cookieEncryptionVersion(encryptedBuffer) === 'v20'
|
||||
}
|
||||
|
||||
// Why: a named cause must carry only its exact count; tied causes fall back to unknown.
|
||||
export function buildUndecryptableWarning(counts: {
|
||||
decryptFailed: number
|
||||
|
||||
@@ -117,13 +117,6 @@ function notifySnapshotListeners(snapshot: BrowserIdentityModeSnapshot): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function onBrowserIdentityModeSnapshotChanged(
|
||||
listener: (snapshot: BrowserIdentityModeSnapshot) => void
|
||||
): () => void {
|
||||
snapshotListeners.add(listener)
|
||||
return () => snapshotListeners.delete(listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits an explicit choice. The record lands durably before this resolves.
|
||||
*
|
||||
|
||||
@@ -57,10 +57,6 @@ export function isLocalSshBrowserPartition(partition: string): boolean {
|
||||
return preparedByPartition.has(partition)
|
||||
}
|
||||
|
||||
export function localSshBrowserPartitionTargetId(partition: string): string | null {
|
||||
return preparedByPartition.get(partition)?.targetId ?? null
|
||||
}
|
||||
|
||||
export function localSshBrowserPartitionForSession(webSession: Session): string | null {
|
||||
return partitionBySession.get(webSession) ?? null
|
||||
}
|
||||
@@ -218,8 +214,3 @@ export async function releaseLocalSshBrowserPartitionsForTarget(targetId: string
|
||||
}
|
||||
|
||||
registerBrowserRoutePartitionRetentionProbe((partition) => preparedByPartition.has(partition))
|
||||
|
||||
export function resetLocalSshBrowserPartitionsForTests(): void {
|
||||
preparedByIdentityKey.clear()
|
||||
preparedByPartition.clear()
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import {
|
||||
normalizeCodexHookSourcePath,
|
||||
parseTrustKey,
|
||||
readHookTrustEntries,
|
||||
readHookTrustEntriesFromContent,
|
||||
removeHookTrustEntries,
|
||||
removeHookTrustEntriesFromContent,
|
||||
type CodexEventLabel,
|
||||
type CodexHookTrustState,
|
||||
type CodexTrustEntry
|
||||
@@ -148,17 +146,6 @@ function getCodexManagedHookTrustEntryKeys(
|
||||
return ownedKeys
|
||||
}
|
||||
|
||||
export function stripCodexManagedHookTrustEntriesFromConfig(
|
||||
contents: string,
|
||||
options: CodexManagedHookTrustOwnershipOptions
|
||||
): string {
|
||||
const ownedKeys = getCodexManagedHookTrustEntryKeys(
|
||||
readHookTrustEntriesFromContent(contents),
|
||||
options
|
||||
)
|
||||
return removeHookTrustEntriesFromContent(contents, ownedKeys)
|
||||
}
|
||||
|
||||
export function removeCodexManagedHookTrustEntries(
|
||||
options: CodexManagedHookTrustOwnershipOptions & { tomlPath: string }
|
||||
): void {
|
||||
|
||||
@@ -99,10 +99,6 @@ function resolveDumpDirectory(): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCrashpadDumpDirectory(): string | null {
|
||||
return crashpadDumpDirectory
|
||||
}
|
||||
|
||||
/** Test seam; production callers go through startCrashpadCapture. */
|
||||
export function _setCrashpadCaptureStateForTest(
|
||||
state: { dumpDirectory: string | null; started: boolean; startedAtMs?: number } | null
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// hour's loss; fsync stops it from happening.
|
||||
|
||||
import { closeSync, fsyncSync, openSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { copyFile, open, readdir, rm, stat } from 'node:fs/promises'
|
||||
import { open, readdir, rm, stat } from 'node:fs/promises'
|
||||
import { basename, dirname, join } from 'node:path'
|
||||
import {
|
||||
publishFileWithoutOverwrite,
|
||||
@@ -92,42 +92,6 @@ export async function writeTempFileDurable(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy `sourcePath` onto `finalPath` durably: a fresh inode, fsynced, then renamed into place. A
|
||||
* plain copyFile can be interrupted and leave a torn destination — fatal when the destination is
|
||||
* the backup someone will fall back to. Returns false when the source does not exist.
|
||||
*/
|
||||
export async function copyFileDurable(sourcePath: string, finalPath: string): Promise<boolean> {
|
||||
const tmpPath = durableWriteTempPath(finalPath)
|
||||
let renamed = false
|
||||
try {
|
||||
try {
|
||||
// copyFile stays in the kernel — and clones the extents outright on APFS and btrfs — so
|
||||
// this does not pull the whole file through the process on every commit.
|
||||
await copyFile(sourcePath, tmpPath)
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
return false
|
||||
}
|
||||
throw error
|
||||
}
|
||||
const handle = await open(tmpPath, 'r+')
|
||||
try {
|
||||
await handle.sync()
|
||||
} finally {
|
||||
await handle.close()
|
||||
}
|
||||
await renameFileWithWindowsRetryAsync(tmpPath, finalPath)
|
||||
renamed = true
|
||||
await syncDirectory(dirname(finalPath))
|
||||
return true
|
||||
} finally {
|
||||
if (!renamed) {
|
||||
await rm(tmpPath, { force: true }).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Write `payload` to `tmpPath`, fsync it, then rename onto `finalPath` and fsync the directory. */
|
||||
export async function writeFileDurable(
|
||||
tmpPath: string,
|
||||
|
||||
@@ -20,10 +20,6 @@ export function writeHangDetectionMarker(markerPath: string, marker: HangDetecti
|
||||
writeFileSync(markerPath, JSON.stringify(marker), { mode: 0o600 })
|
||||
}
|
||||
|
||||
export function readHangDetectionMarker(markerPath: string): HangDetectionMarker | null {
|
||||
return readAvailableHangDetectionMarker(markerPath) ?? null
|
||||
}
|
||||
|
||||
function readAvailableHangDetectionMarker(
|
||||
markerPath: string
|
||||
): HangDetectionMarker | null | undefined {
|
||||
|
||||
@@ -89,14 +89,3 @@ export async function readLocalLogSnapshot(filePath: string): Promise<LocalFileC
|
||||
await handle.close()
|
||||
}
|
||||
}
|
||||
|
||||
export function isDirectoryEntry(entry: {
|
||||
isDirectory(): boolean
|
||||
isSymbolicLink(): boolean
|
||||
}): boolean {
|
||||
// Why: following a symlink in readDir can touch macOS TCC-protected containers; treat links as file-like until explicitly opened.
|
||||
if (entry.isSymbolicLink()) {
|
||||
return false
|
||||
}
|
||||
return entry.isDirectory()
|
||||
}
|
||||
|
||||
@@ -22,11 +22,6 @@ export const JCODE_HOOK_EVENTS = [
|
||||
] as const
|
||||
export type JcodeHookEvent = (typeof JCODE_HOOK_EVENTS)[number]
|
||||
|
||||
/** jcode waits for this one; the managed script must never block on it. */
|
||||
export function isJcodeGateHookEvent(event: JcodeHookEvent): boolean {
|
||||
return event === 'pre_tool'
|
||||
}
|
||||
|
||||
export function getJcodeConfigPath(env: NodeJS.ProcessEnv = process.env): string {
|
||||
const explicit = env.JCODE_HOME?.trim()
|
||||
return explicit ? join(explicit, 'config.toml') : join(homedir(), '.jcode', 'config.toml')
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
wslHomeSessionDirs,
|
||||
type SessionHomeAgent
|
||||
} from '../ai-vault/session-scanner-roots'
|
||||
import { getWslHomeAsync, listRunningWslDistrosAsync, listRunningWslHomeDirsAsync } from '../wsl'
|
||||
import { getWslHomeAsync, listRunningWslHomeDirsAsync } from '../wsl'
|
||||
import {
|
||||
filterPathsToRunningWslDistrosAsync,
|
||||
filterPathsToWslDistros
|
||||
@@ -50,20 +50,6 @@ export type WslTranscriptResolutionSnapshot = {
|
||||
homeDirs?: string[]
|
||||
}
|
||||
|
||||
/** One running-distro view shared by every WSL lookup in a resolve attempt. */
|
||||
export async function createWslTranscriptResolutionSnapshot(
|
||||
options: {
|
||||
includeHomes?: boolean
|
||||
} = {}
|
||||
): Promise<WslTranscriptResolutionSnapshot> {
|
||||
const runningDistros = await listRunningWslDistrosAsync()
|
||||
if (options.includeHomes === false) {
|
||||
return { runningDistros }
|
||||
}
|
||||
const homes = await Promise.all(runningDistros.map((distro) => getWslHomeAsync(distro)))
|
||||
return { runningDistros, homeDirs: homes.filter((home): home is string => home !== null) }
|
||||
}
|
||||
|
||||
async function snapshotHomeDirs(snapshot: WslTranscriptResolutionSnapshot): Promise<string[]> {
|
||||
if (snapshot.homeDirs) {
|
||||
return snapshot.homeDirs
|
||||
|
||||
@@ -90,10 +90,3 @@ export function isWslTranscriptFsProcessHandle(
|
||||
): value is WslTranscriptFsProcessHandle {
|
||||
return 'wslTranscriptFsProcessHandle' in value
|
||||
}
|
||||
|
||||
export function resetWslTranscriptFsProcessClientForTests(): void {
|
||||
for (const client of clientsByLane.values()) {
|
||||
client.dispose()
|
||||
}
|
||||
clientsByLane.clear()
|
||||
}
|
||||
|
||||
@@ -162,20 +162,6 @@ export function isPosixPtyRootStopped(output: string, rootPid: number): boolean
|
||||
)
|
||||
}
|
||||
|
||||
/** The root was stopped by flow control; preserve independently stopped jobs. */
|
||||
export function getPosixPtyStoppedJobGroups(output: string, rootPid: number): Set<number> {
|
||||
const rows = parseProcessRows(output)
|
||||
const root = rows.find((row) => row.pid === rootPid)
|
||||
return new Set(
|
||||
rows
|
||||
.filter(
|
||||
(row) =>
|
||||
root && row.tty === root.tty && row.pgid !== root.pgid && /^[Tt]/.test(row.state ?? '')
|
||||
)
|
||||
.map((row) => row.pgid)
|
||||
)
|
||||
}
|
||||
|
||||
export function getPosixPtyProcessGroups(
|
||||
output: string,
|
||||
rootPid: number,
|
||||
|
||||
@@ -52,17 +52,3 @@ export const ANTIGRAVITY_USAGE_MAX_OUTPUT_BYTES = 512 * 1024
|
||||
export function antigravityCommandName(): string {
|
||||
return TUI_AGENT_CONFIG.antigravity.detectCmd
|
||||
}
|
||||
|
||||
/**
|
||||
* Why the args are never appended to a configured launch command: a user's Antigravity launch
|
||||
* command may carry its own flags, a wrapper script, or a shell pipeline, and appending `-p /usage`
|
||||
* to that either runs the wrong program or feeds the slash command to the wrong argv slot. The quota
|
||||
* read resolves the plain executable itself instead.
|
||||
*/
|
||||
export function isPlainAntigravityExecutable(command: string): boolean {
|
||||
const trimmed = command.trim()
|
||||
if (trimmed.length === 0) {
|
||||
return false
|
||||
}
|
||||
return !/[\s"'|&;<>$`()]/.test(trimmed)
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
export type WallClockDateParts = {
|
||||
year: number
|
||||
monthIndex: number
|
||||
day: number
|
||||
hour: number
|
||||
minute: number
|
||||
}
|
||||
|
||||
export function buildWallClockTimestamp(
|
||||
parts: WallClockDateParts,
|
||||
timeZone: string | null
|
||||
): number | null {
|
||||
if (!timeZone) {
|
||||
const localDate = new Date(parts.year, parts.monthIndex, parts.day, parts.hour, parts.minute)
|
||||
return isMatchingDateParts(localDate, parts) ? localDate.getTime() : null
|
||||
}
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone,
|
||||
hourCycle: 'h23',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
const timestamp = buildTimeZoneTimestamp(parts, formatter)
|
||||
if (timestamp === null) {
|
||||
return null
|
||||
}
|
||||
const resolvedParts = getTimeZoneDateParts(timestamp, formatter)
|
||||
return resolvedParts && areMatchingWallClockParts(resolvedParts, parts) ? timestamp : null
|
||||
}
|
||||
|
||||
function buildTimeZoneTimestamp(
|
||||
parts: WallClockDateParts,
|
||||
formatter: Intl.DateTimeFormat
|
||||
): number | null {
|
||||
const utcGuess = Date.UTC(parts.year, parts.monthIndex, parts.day, parts.hour, parts.minute)
|
||||
const firstOffset = getTimeZoneOffsetMs(utcGuess, formatter)
|
||||
if (firstOffset === null) {
|
||||
return null
|
||||
}
|
||||
const firstTimestamp = utcGuess - firstOffset
|
||||
const secondOffset = getTimeZoneOffsetMs(firstTimestamp, formatter)
|
||||
return secondOffset === null ? null : utcGuess - secondOffset
|
||||
}
|
||||
|
||||
function getTimeZoneOffsetMs(timestamp: number, formatter: Intl.DateTimeFormat): number | null {
|
||||
const parts = getTimeZoneDateParts(timestamp, formatter)
|
||||
if (!parts) {
|
||||
return null
|
||||
}
|
||||
return Date.UTC(parts.year, parts.monthIndex, parts.day, parts.hour, parts.minute) - timestamp
|
||||
}
|
||||
|
||||
function getTimeZoneDateParts(
|
||||
timestamp: number,
|
||||
formatter: Intl.DateTimeFormat
|
||||
): WallClockDateParts | null {
|
||||
const parts = Object.fromEntries(
|
||||
formatter.formatToParts(new Date(timestamp)).map((part) => [part.type, part.value])
|
||||
)
|
||||
const year = Number(parts.year)
|
||||
const monthIndex = Number(parts.month) - 1
|
||||
const day = Number(parts.day)
|
||||
const hour = Number(parts.hour)
|
||||
const minute = Number(parts.minute)
|
||||
return [year, monthIndex, day, hour, minute].every(Number.isFinite)
|
||||
? { year, monthIndex, day, hour, minute }
|
||||
: null
|
||||
}
|
||||
|
||||
function isMatchingDateParts(date: Date, parts: WallClockDateParts): boolean {
|
||||
return (
|
||||
date.getFullYear() === parts.year &&
|
||||
date.getMonth() === parts.monthIndex &&
|
||||
date.getDate() === parts.day &&
|
||||
date.getHours() === parts.hour &&
|
||||
date.getMinutes() === parts.minute
|
||||
)
|
||||
}
|
||||
|
||||
function areMatchingWallClockParts(left: WallClockDateParts, right: WallClockDateParts): boolean {
|
||||
return (
|
||||
left.year === right.year &&
|
||||
left.monthIndex === right.monthIndex &&
|
||||
left.day === right.day &&
|
||||
left.hour === right.hour &&
|
||||
left.minute === right.minute
|
||||
)
|
||||
}
|
||||
@@ -53,21 +53,6 @@ export async function scanAgentSessionSpawnTokenProcesses(
|
||||
return observed
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic evidence only. A null result is deliberately typed as
|
||||
* `unverifiable`, not as an empty process set; callers must never use this
|
||||
* Linux read-back as ownership proof.
|
||||
*/
|
||||
export async function scanAgentSessionSpawnTokenEvidence(
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
variable: string = CODEX_SPAWN_TOKEN_ENV
|
||||
): Promise<AgentSessionSpawnTokenScanEvidence> {
|
||||
const processes = await scanAgentSessionSpawnTokenProcesses(platform, variable)
|
||||
return processes === null
|
||||
? { status: 'unverifiable', processes: null, platform }
|
||||
: { status: 'verified', processes }
|
||||
}
|
||||
|
||||
/** Pids carrying one specific token, or null when the host could not enumerate. */
|
||||
export async function findAgentSessionSpawnTokenProcesses(
|
||||
spawnToken: string,
|
||||
|
||||
@@ -31,10 +31,6 @@ export function setRuntimeTerminalUnavailableCause(
|
||||
unavailableCause = cause
|
||||
}
|
||||
|
||||
export function runtimeTerminalUnavailableCause(): RuntimeTerminalUnavailableCause | null {
|
||||
return unavailableCause
|
||||
}
|
||||
|
||||
/** The degradation entry for the recorded cause, or null when nothing is degraded. */
|
||||
export function runtimeTerminalDegradation(): RuntimeDegradation | null {
|
||||
const cause = unavailableCause
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { RuntimeCapability } from '../../../shared/protocol-version'
|
||||
import { parseRemoteRuntimeJsonText } from '../../../shared/remote-runtime-request-frames'
|
||||
import { parseRuntimeClientCapabilities } from './runtime-client-capabilities'
|
||||
|
||||
export function parseMobileE2EEV2ClientCapabilities(
|
||||
plaintext: string
|
||||
): readonly RuntimeCapability[] | null {
|
||||
try {
|
||||
const message = parseRemoteRuntimeJsonText(plaintext) as Record<string, unknown>
|
||||
if (
|
||||
Object.keys(message).sort().join(',') !== 'clientCapabilities,type,v' ||
|
||||
message.type !== 'e2ee_client_capabilities' ||
|
||||
message.v !== 1
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return parseRuntimeClientCapabilities(message.clientCapabilities)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -173,17 +173,3 @@ export function isMobileBinaryPath(relativePath: string): boolean {
|
||||
}
|
||||
return MOBILE_BINARY_EXTENSIONS.has(basename.slice(dotIndex).toLowerCase())
|
||||
}
|
||||
|
||||
export function isRuntimeDirectoryEntry(entry: {
|
||||
isDirectory(): boolean
|
||||
isSymbolicLink(): boolean
|
||||
}): boolean {
|
||||
// Why: listings are passive UI reads; don't stat symlink targets here (explicit open/expand resolves them).
|
||||
if (entry.isSymbolicLink()) {
|
||||
return false
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -263,13 +263,6 @@ export function mapExplicitAgentStateToRuntimeTerminalStatus(
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeWorktreeStatus(
|
||||
current: RuntimeWorktreeStatus,
|
||||
next: RuntimeWorktreeStatus
|
||||
): RuntimeWorktreeStatus {
|
||||
return WORKTREE_STATUS_PRIORITY[next] > WORKTREE_STATUS_PRIORITY[current] ? next : current
|
||||
}
|
||||
|
||||
export function mergeWorktreeSummaryStatus(
|
||||
summary: RuntimeWorktreePsSummary,
|
||||
next: RuntimeWorktreeStatus,
|
||||
|
||||
@@ -40,14 +40,6 @@ const NON_RETRYABLE_SKILL_TRANSFER_ERRORS = new Set([
|
||||
'skill-install-ssh-download-unavailable'
|
||||
])
|
||||
|
||||
export async function supportsSkillManagementOnSsh(
|
||||
provider: SkillSshProviderSource
|
||||
): Promise<boolean> {
|
||||
return (await skillSshRelayCapabilities(requireSkillSshRelayClient(provider))).includes(
|
||||
SKILL_MANAGEMENT_CAPABILITY
|
||||
)
|
||||
}
|
||||
|
||||
export async function installSkillOnSshHost(input: {
|
||||
provider: SkillSshProviderSource
|
||||
userDataPath: string
|
||||
|
||||
@@ -84,7 +84,3 @@ export function scheduleManagedServerFenceRecheck(targetId: string, loop: FenceR
|
||||
}
|
||||
timer = setTimeout(() => void tick(0), loop.intervalMs ?? FENCE_RECHECK_INTERVAL_MS)
|
||||
}
|
||||
|
||||
export function cancelManagedServerFenceRecheck(targetId: string): void {
|
||||
loops.get(targetId)?.()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,3 @@ export function rememberOrcadTunnelTransport(
|
||||
export function knownOrcadTunnelTransport(targetId: string): OrcadTunnelTransport | null {
|
||||
return transports.get(targetId) ?? null
|
||||
}
|
||||
|
||||
export function resetOrcadTunnelTransportMemoForTests(): void {
|
||||
transports.clear()
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@ export function remoteSocketPathFitsLimit(host: RemoteHostPlatform, sockPath: st
|
||||
/** Fixed-length, per-uid base. `/tmp` is the only POSIX directory whose length is not user-dependent. */
|
||||
export const SHORT_RELAY_SOCKET_DIR_PREFIX = '/tmp/.orca-relay-'
|
||||
|
||||
export function shortRelaySocketDirForUid(uid: string): string {
|
||||
return `${SHORT_RELAY_SOCKET_DIR_PREFIX}${uid}`
|
||||
}
|
||||
|
||||
/**
|
||||
* The version segment the relocated socket lives under, named to match the version
|
||||
* directories in `$HOME/.orca-remote` so one sweep pattern covers both bases.
|
||||
|
||||
@@ -66,10 +66,6 @@ const CLAIM_CACHE_TTL_MS = 5_000
|
||||
|
||||
let cachedClaims: { key: string; readAt: number; claims: SshConfigAliasClaims } | null = null
|
||||
|
||||
export function invalidateSshConfigAliasClaimCache(): void {
|
||||
cachedClaims = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse of `~/.ssh/config` (Includes expanded), or null when it cannot be read.
|
||||
*
|
||||
|
||||
@@ -17,13 +17,7 @@ import {
|
||||
import { logStartupMilestone } from './startup-diagnostics'
|
||||
import { notifyMainWindowBecameVisible } from '../window/main-window-visibility'
|
||||
import { setTrayAttention } from '../tray/system-tray'
|
||||
import {
|
||||
createSystemTrayDeferred,
|
||||
getSystemTrayOptions,
|
||||
showMainWindowFromTray,
|
||||
showRendererRecoveryPrompt,
|
||||
syncMacMenuBarIcon
|
||||
} from './main-window-actions'
|
||||
import { createSystemTrayDeferred, showRendererRecoveryPrompt } from './main-window-actions'
|
||||
import { attachMainWindowCoreServices } from './main-window-core-services'
|
||||
import {
|
||||
clearMainWindowAgentStatusListeners,
|
||||
@@ -230,10 +224,3 @@ export function openMainWindow(options: { revealOnDidFinishLoad?: boolean } = {}
|
||||
loadMainWindow(window)
|
||||
return window
|
||||
}
|
||||
|
||||
export function configureWindowActions(): void {
|
||||
// Kept as a named seam for startup composition; action callbacks are state-backed.
|
||||
void getSystemTrayOptions
|
||||
void showMainWindowFromTray
|
||||
void syncMacMenuBarIcon
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { registerPaneKeyTeardownListener, getPtyIdForPaneKey } from '../ipc/pty'
|
||||
import { agentHookServer } from '../agent-hooks/server'
|
||||
import type { AgentStatusState } from '../../shared/agent-status-types'
|
||||
import {
|
||||
getSyntheticAgentTitleProfile,
|
||||
shouldDriveSyntheticAgentTitleFromHook,
|
||||
type SyntheticAgentTitleProfile
|
||||
} from '../../shared/synthetic-agent-title'
|
||||
import type { SyntheticAgentTitleProfile } from '../../shared/synthetic-agent-title'
|
||||
import {
|
||||
advanceSyntheticTitleSpinnerEntries,
|
||||
getSyntheticTitleSpinnerPaneKeyToStop,
|
||||
@@ -164,14 +160,3 @@ export function initializeSyntheticTitleRuntime(): void {
|
||||
})
|
||||
agentHookServer.subscribeStatusDrop(stopSyntheticTitleSpinner)
|
||||
}
|
||||
|
||||
export function driveSyntheticTitleForAgentStatus(
|
||||
paneKey: string,
|
||||
agentType: string | null | undefined,
|
||||
agentState: AgentStatusState
|
||||
): void {
|
||||
const profile = getSyntheticAgentTitleProfile(agentType)
|
||||
if (profile && shouldDriveSyntheticAgentTitleFromHook(agentType, agentState)) {
|
||||
driveSyntheticTitleFromHook(paneKey, agentState, profile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,19 +42,3 @@ export function selectReplayableCachedPanes(input: {
|
||||
|
||||
/** How many panes keep a cached status. Bounds a long-lived relay's per-pane state. */
|
||||
export const MAX_CACHED_PANES = 256
|
||||
|
||||
/** Drop the longest-idle panes until the cache is back under its cap. Map insertion order is
|
||||
* recency (writers delete-then-set), so the first key is always the oldest. */
|
||||
export function evictCachedPanesOverCap(
|
||||
cachedByPaneKey: ReadonlyMap<string, unknown>,
|
||||
dropPane: (paneKey: string) => void,
|
||||
maxPanes: number = MAX_CACHED_PANES
|
||||
): void {
|
||||
while (cachedByPaneKey.size > maxPanes) {
|
||||
const oldest = cachedByPaneKey.keys().next().value
|
||||
if (oldest === undefined) {
|
||||
return
|
||||
}
|
||||
dropPane(oldest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
import { AgentStateDot } from '@/components/AgentStateDot'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { useNow } from '@/hooks/use-now'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { formatShortTimeAgo } from '@/lib/short-time-ago'
|
||||
@@ -52,23 +51,6 @@ export function EventTime({
|
||||
)
|
||||
}
|
||||
|
||||
export function ActivityProjectLabel({ repo }: { repo: Repo | null }): React.JSX.Element {
|
||||
const label =
|
||||
repo?.displayName?.trim() ||
|
||||
translate('auto.components.activity.ActivityPrototypePage.5651b216c6', 'Unknown project')
|
||||
return (
|
||||
<div className="flex min-w-0 items-center gap-1.5">
|
||||
{repo ? <RepoBadgeMark color={repo.badgeColor} /> : null}
|
||||
<span
|
||||
className="min-w-0 truncate text-[11px] font-semibold uppercase tracking-[0.04em] text-muted-foreground"
|
||||
title={label}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function EventRepoBadge({ repo }: { repo: Repo | null }): React.JSX.Element | null {
|
||||
if (!repo) {
|
||||
return null
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* the scheduler reads the store-derived one.
|
||||
*/
|
||||
|
||||
import type { AutomationHostCache } from './automation-host-cache'
|
||||
import type {
|
||||
AutomationHostCacheEntry,
|
||||
AutomationHostQueryErrorCode
|
||||
@@ -123,10 +122,3 @@ export function automationHostLoadCounts(catalog: AutomationHostCatalog): Automa
|
||||
totalHostCount: counted.length
|
||||
}
|
||||
}
|
||||
|
||||
/** Adapter so callers can pass a live cache where a plain reader is expected. */
|
||||
export function automationHostCacheReader(
|
||||
cache: AutomationHostCache
|
||||
): (stableKey: string) => AutomationHostCacheEntry | null {
|
||||
return (stableKey) => cache.getByKey(stableKey)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import type {
|
||||
Automation,
|
||||
AutomationCreateInput,
|
||||
AutomationRun,
|
||||
AutomationRunsPage,
|
||||
AutomationUpdateInput
|
||||
} from '../../../../shared/automations-types'
|
||||
import type { AutomationAuthorityRef } from '../../../../shared/automation-owner-ref'
|
||||
@@ -133,20 +132,6 @@ export async function listAutomationRunsForTarget(
|
||||
return result.runs
|
||||
}
|
||||
|
||||
export async function listAutomationRunsPageForTarget(
|
||||
target: AutomationHostTarget,
|
||||
automationId: string,
|
||||
options: { limit?: number; cursor?: string } = {}
|
||||
): Promise<AutomationRunsPage> {
|
||||
const result = await callRuntimeRpc<AutomationRunsPage | { runs: AutomationRun[] }>(
|
||||
target,
|
||||
'automation.runs',
|
||||
{ automationId, ...options },
|
||||
{ timeoutMs: 15_000 }
|
||||
)
|
||||
return { runs: result.runs, nextCursor: 'nextCursor' in result ? result.nextCursor : null }
|
||||
}
|
||||
|
||||
export async function updateAutomationForTarget(
|
||||
automation: Automation,
|
||||
updates: AutomationUpdateInput,
|
||||
|
||||
@@ -62,7 +62,3 @@ export function startBrowserClientPageMetadataPublisher(options: {
|
||||
export function forgetBrowserClientPageMetadataReports(browserPageId: string): void {
|
||||
warnedByPageId.delete(browserPageId)
|
||||
}
|
||||
|
||||
export function resetBrowserClientPageMetadataReportsForTests(): void {
|
||||
warnedByPageId.clear()
|
||||
}
|
||||
|
||||
@@ -32,21 +32,6 @@ export function releaseBrowserPageMount(pageId: string): void {
|
||||
emit()
|
||||
}
|
||||
|
||||
export function useBrowserPageMountAdmission(pageId: string): boolean {
|
||||
useSyncExternalStore(
|
||||
(listener) => {
|
||||
listeners.add(listener)
|
||||
return () => listeners.delete(listener)
|
||||
},
|
||||
() => {
|
||||
void version
|
||||
return isBrowserPageMountAdmitted(pageId)
|
||||
},
|
||||
() => false
|
||||
)
|
||||
return isBrowserPageMountAdmitted(pageId)
|
||||
}
|
||||
|
||||
export function useAnyBrowserPageMountAdmission(pageIds: readonly string[]): boolean {
|
||||
useSyncExternalStore(
|
||||
(listener) => {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
DashboardCard,
|
||||
DashboardCardReview,
|
||||
DashboardWorkspace
|
||||
} from '../../../../shared/dashboard-snapshot'
|
||||
import type { DashboardCard, DashboardCardReview } from '../../../../shared/dashboard-snapshot'
|
||||
|
||||
export type DashboardReviewFilter = DashboardCardReview['state'] | 'none'
|
||||
|
||||
@@ -40,34 +36,6 @@ function cardSearchText(card: DashboardCard): string {
|
||||
.toLocaleLowerCase()
|
||||
}
|
||||
|
||||
function workspaceSearchText(workspace: DashboardWorkspace): string {
|
||||
return [
|
||||
workspace.worktreeName,
|
||||
workspace.repoName,
|
||||
workspace.review ? `#${workspace.review.number}` : ''
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.toLocaleLowerCase()
|
||||
}
|
||||
|
||||
function workspaceMatchesFilters(
|
||||
workspace: DashboardWorkspace,
|
||||
normalizedQuery: string,
|
||||
filters: DashboardFilters
|
||||
): boolean {
|
||||
const reviewState = workspace.review?.state ?? (workspace.hasReview ? null : 'none')
|
||||
return (
|
||||
(normalizedQuery.length === 0 || workspaceSearchText(workspace).includes(normalizedQuery)) &&
|
||||
(filters.projects.length === 0 || filters.projects.includes(workspace.repoId)) &&
|
||||
(filters.workspaceStatuses.length === 0 ||
|
||||
(workspace.workspaceStatusId !== undefined &&
|
||||
filters.workspaceStatuses.includes(workspace.workspaceStatusId))) &&
|
||||
(filters.reviewStates.length === 0 ||
|
||||
(reviewState !== null && filters.reviewStates.includes(reviewState)))
|
||||
)
|
||||
}
|
||||
|
||||
export function filterDashboardCards(
|
||||
cards: DashboardCard[],
|
||||
query: string,
|
||||
@@ -88,17 +56,6 @@ export function filterDashboardCards(
|
||||
})
|
||||
}
|
||||
|
||||
export function filterDashboardWorkspaces(
|
||||
workspaces: DashboardWorkspace[],
|
||||
query: string,
|
||||
filters: DashboardFilters
|
||||
): DashboardWorkspace[] {
|
||||
const normalizedQuery = query.trim().toLocaleLowerCase()
|
||||
return workspaces.filter((workspace) =>
|
||||
workspaceMatchesFilters(workspace, normalizedQuery, filters)
|
||||
)
|
||||
}
|
||||
|
||||
export function toggleDashboardFilter<T extends string>(values: T[], value: T): T[] {
|
||||
return values.includes(value)
|
||||
? values.filter((candidate) => candidate !== value)
|
||||
|
||||
@@ -69,18 +69,6 @@ export function isExternalReloadableEditorTab(file: OpenFile): boolean {
|
||||
)
|
||||
}
|
||||
|
||||
// Why: combined "Changes"/"All Changes" tabs render working-tree diffs but
|
||||
// match no single path (their relativePath is a label, filePath the worktree
|
||||
// root). The fs watcher must still notify them so terminal/agent edits reload
|
||||
// the affected section. Branch/commit combined diffs compare committed refs, so
|
||||
// working-tree changes don't affect them and are intentionally excluded.
|
||||
export function isWorkingTreeCombinedDiffTab(file: OpenFile): boolean {
|
||||
return (
|
||||
file.mode === 'diff' &&
|
||||
(file.diffSource === 'combined-uncommitted' || file.diffSource === 'combined-all')
|
||||
)
|
||||
}
|
||||
|
||||
export function canAutoSaveOpenFile(file: OpenFile): boolean {
|
||||
// Why: read-only tabs (AI Vault View Log) must never autosave — writing an
|
||||
// agent-owned transcript can corrupt the provider's resume history.
|
||||
|
||||
@@ -51,10 +51,6 @@ export function markdownFenceRanges(content: string): MarkdownFenceRanges {
|
||||
return ranges
|
||||
}
|
||||
|
||||
export function isInsideRange(index: number, ranges: MarkdownFenceRanges): boolean {
|
||||
return ranges.some(([start, end]) => index >= start && index < end)
|
||||
}
|
||||
|
||||
function rangeEndAt(index: number, ranges: MarkdownFenceRanges): number {
|
||||
for (const [start, end] of ranges) {
|
||||
if (index >= start && index < end) {
|
||||
|
||||
+1
-18
@@ -6,15 +6,7 @@ import type {
|
||||
GitHubIssueTimelineTarget
|
||||
} from '../../../../../shared/github/comment-types'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import {
|
||||
getTimelineStateReasonLabel,
|
||||
getTimelineTargetLabel,
|
||||
type IssueConversationEntry
|
||||
} from './issue-conversation-entries'
|
||||
import {
|
||||
renderCommentCard,
|
||||
type ConversationCommentCardContext
|
||||
} from './conversation-tab-comment-card'
|
||||
import { getTimelineStateReasonLabel, getTimelineTargetLabel } from './issue-conversation-entries'
|
||||
|
||||
export function renderTimelineTarget(
|
||||
target: GitHubIssueTimelineTarget | undefined
|
||||
@@ -154,15 +146,6 @@ export function renderTimelineActivity(activity: GitHubIssueTimelineItem): React
|
||||
)
|
||||
}
|
||||
|
||||
export function renderIssueConversationEntry(
|
||||
entry: IssueConversationEntry,
|
||||
ctx: ConversationCommentCardContext
|
||||
): React.JSX.Element {
|
||||
return entry.kind === 'comment'
|
||||
? renderCommentCard(entry.comment, false, ctx)
|
||||
: renderTimelineActivity(entry.activity)
|
||||
}
|
||||
|
||||
export function ConversationTabTimelineActivity({
|
||||
activity
|
||||
}: {
|
||||
|
||||
@@ -17,11 +17,6 @@ export function unnamedSenderLabel(): string {
|
||||
return translate('components.native-chat.agentMessage.unnamedSender', 'an agent')
|
||||
}
|
||||
|
||||
/** The name recorded on the message. */
|
||||
export function agentMessageSenderLabel(sender: AgentMessageSender): string {
|
||||
return sender.name ?? unnamedSenderLabel()
|
||||
}
|
||||
|
||||
function findChatTab(
|
||||
state: AppState,
|
||||
sessionId: string,
|
||||
|
||||
@@ -4,14 +4,6 @@ export function getBrowserLinkRoutingShortcutLabel(platform: { isMac: boolean })
|
||||
return platform.isMac ? '⇧⌘-click' : 'Shift+Ctrl+click'
|
||||
}
|
||||
|
||||
export function getTerminalLinkActionsDescription(platform: { isMac: boolean }): string {
|
||||
return translate(
|
||||
'auto.components.settings.BrowserTerminalLinkActionsSetting.description',
|
||||
'Show available actions when you click a link in the terminal or a chat transcript. Turn this off to require {{modifier}}-click in the terminal.',
|
||||
{ modifier: platform.isMac ? '⌘' : 'Ctrl' }
|
||||
)
|
||||
}
|
||||
|
||||
// Why: "always" stops being true once inverting is on, so only then does the nested
|
||||
// row take over the chord sentence — with it off this reads exactly as it always has.
|
||||
// Each variant is a complete catalog entry: stitched suffixes break in locales whose
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
/** Corner count pill overlaying its relative parent; absolute so it never
|
||||
* affects layout (frozen-width surfaces like the view toggle depend on this). */
|
||||
export function SidebarCountBadge({
|
||||
count,
|
||||
className
|
||||
}: {
|
||||
count: number
|
||||
className?: string
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'absolute -top-0.5 -right-0.5 flex h-3 min-w-3 items-center justify-center rounded-full bg-primary px-0.5 text-[9px] font-medium leading-none text-primary-foreground',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{count > 9 ? '9+' : count}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { SkillCloudVersion } from '../../../../shared/skill-cloud-contract'
|
||||
|
||||
export function retainManagedSkillVersion(
|
||||
versions: readonly SkillCloudVersion[],
|
||||
current: string
|
||||
): string {
|
||||
return versions.some((version) => version.versionId === current)
|
||||
? current
|
||||
: (versions[0]?.versionId ?? '')
|
||||
}
|
||||
@@ -8,20 +8,3 @@ export function isSkillBundleVersion(
|
||||
): version is SkillCloudVersion & { manifest: SkillBundleManifestV1 } {
|
||||
return 'skills' in version.manifest
|
||||
}
|
||||
|
||||
export function summarizeSkillShareVersion(version: SkillCloudVersion | undefined): {
|
||||
scriptCount: number
|
||||
executableCount: number
|
||||
fileCount: number
|
||||
} {
|
||||
const files = version
|
||||
? 'skills' in version.manifest
|
||||
? version.manifest.skills.flatMap((skill) => skill.files)
|
||||
: version.manifest.files
|
||||
: []
|
||||
return {
|
||||
scriptCount: files.filter((file) => file.path.startsWith('scripts/')).length,
|
||||
executableCount: files.filter((file) => file.executable).length,
|
||||
fileCount: files.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
import { useAppStore } from '../../store'
|
||||
import type { TabBarProps } from '../tab-bar/tab-bar-props'
|
||||
|
||||
/** The tab-strip entries a host can own because only it knows where new things belong. */
|
||||
export type TabGroupNewTabActions = Pick<
|
||||
TabBarProps,
|
||||
| 'onNewTerminalTab'
|
||||
| 'onNewTerminalWithShell'
|
||||
| 'onNewBrowserTab'
|
||||
| 'onNewSimulatorTab'
|
||||
| 'onOpenEntry'
|
||||
| 'onNewFileTab'
|
||||
| 'onOpenFileTab'
|
||||
| 'newTabMenuOrder'
|
||||
>
|
||||
|
||||
/**
|
||||
* What a surface that mounts tab groups tells them about itself. Groups own layout, panes, selection
|
||||
* and closing; the host owns only the chrome around its tab strips and where its new tabs go.
|
||||
*/
|
||||
export type TabGroupHost = {
|
||||
/** Rendered at the start of the top-left group's strip, e.g. space under overlaid window chrome. */
|
||||
headerStart?: React.ReactNode
|
||||
/** Rendered at the end of the top-right group's strip. */
|
||||
headerEnd?: React.ReactNode
|
||||
tabStripChrome?: TabBarProps['tabStripChrome']
|
||||
markdownAnnotationsEnabled?: boolean
|
||||
/** Shown in a group's body while it has no tab to render. */
|
||||
emptyGroupBody?: React.ReactNode
|
||||
/** Replaces the group's own new-tab entries; omitted entries fall back to the group's. */
|
||||
newTabActions?: (groupId: string) => Partial<TabGroupNewTabActions>
|
||||
}
|
||||
|
||||
// Why a spread: React's CSSProperties has no WebkitAppRegion, and a spread skips the excess-property
|
||||
// check that would otherwise need a cast.
|
||||
const NO_DRAG_REGION = { WebkitAppRegion: 'no-drag' } as const
|
||||
|
||||
// Why components rather than values: each subscribes to its own sidebar flag, so a toggle
|
||||
// re-renders one spacer instead of every group.
|
||||
// Why no-drag: Electron's drag hit-test honours no-drag only on DOM descendants, not z-index
|
||||
// siblings, so these keep the overlaid sidebar toggles and window controls clickable.
|
||||
function CollapsedSidebarHeaderReservation(): React.JSX.Element | null {
|
||||
const sidebarOpen = useAppStore((state) => state.sidebarOpen)
|
||||
return sidebarOpen ? null : (
|
||||
<div
|
||||
className="shrink-0"
|
||||
style={{ width: 'var(--collapsed-sidebar-header-width)', ...NO_DRAG_REGION }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ClosedExplorerHeaderReservation(): React.JSX.Element | null {
|
||||
const rightSidebarOpen = useAppStore((state) => state.rightSidebarOpen)
|
||||
return rightSidebarOpen ? null : (
|
||||
<div
|
||||
className="shrink-0"
|
||||
style={{ width: 'calc(40px + var(--window-controls-width, 0px))', ...NO_DRAG_REGION }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const MAIN_WORKSPACE_HOST: TabGroupHost = {
|
||||
headerStart: <CollapsedSidebarHeaderReservation />,
|
||||
headerEnd: <ClosedExplorerHeaderReservation />
|
||||
}
|
||||
|
||||
const TabGroupHostContext = createContext<TabGroupHost>(MAIN_WORKSPACE_HOST)
|
||||
|
||||
export const TabGroupHostProvider = TabGroupHostContext.Provider
|
||||
|
||||
export function useTabGroupHost(): TabGroupHost {
|
||||
return useContext(TabGroupHostContext)
|
||||
}
|
||||
@@ -28,10 +28,6 @@ export function isAgentTaskCompleteOsNotificationEnabledFromState(
|
||||
return notifications?.enabled !== false && notifications?.agentTaskComplete !== false
|
||||
}
|
||||
|
||||
export function isTerminalAttentionEnabledFromState(state: NotificationSettingsState): boolean {
|
||||
return state.settings?.experimentalTerminalAttention === true
|
||||
}
|
||||
|
||||
/** Track completions once settings hydrate; each delivery surface applies its own filter. */
|
||||
export function isAgentTaskCompleteTrackingEnabledFromState(state: {
|
||||
settings: object | null
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
type TerminalQuickCommandCollapsibleRowProps = {
|
||||
open: boolean
|
||||
className?: string
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
/** Why: switching action adds/removes fields; animating the rows keeps the
|
||||
* dialog from snapping between content heights. */
|
||||
export function TerminalQuickCommandCollapsibleRow({
|
||||
open,
|
||||
className,
|
||||
children
|
||||
}: TerminalQuickCommandCollapsibleRowProps): React.JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'grid overflow-hidden transition-[grid-template-rows] duration-200 ease-out',
|
||||
open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'
|
||||
)}
|
||||
aria-hidden={!open}
|
||||
>
|
||||
<div className="min-h-0">
|
||||
<div
|
||||
className={cn(
|
||||
'transition-[opacity,transform] duration-150 ease-out',
|
||||
open ? 'translate-y-0 opacity-100 delay-200' : '-translate-y-1 opacity-0 delay-0',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { Repo } from '../../../../shared/repo-types'
|
||||
import { isFolderRepo } from '../../../../shared/repo-kind'
|
||||
import type { Worktree } from '../../../../shared/worktree/types'
|
||||
import {
|
||||
getPersistedWorkspaceCleanupActivityAt,
|
||||
isWorkspaceOldForCleanup
|
||||
} from '../../../../shared/workspace-cleanup'
|
||||
|
||||
/**
|
||||
* Renderer-side estimate of how many workspaces the cleanup scan would list. It
|
||||
* sees only Orca's persisted activity record, so it disagrees with the scan by
|
||||
* design — the scan additionally reads each worktree's git history and cannot
|
||||
* inspect disconnected remotes.
|
||||
*/
|
||||
export function countEstimatedInactiveWorkspaces(
|
||||
worktrees: readonly Worktree[],
|
||||
repoById: ReadonlyMap<string, Repo>,
|
||||
now: number
|
||||
): number {
|
||||
let count = 0
|
||||
for (const worktree of worktrees) {
|
||||
const repo = repoById.get(worktree.repoId)
|
||||
if (!repo || isFolderRepo(repo) || worktree.isMainWorktree) {
|
||||
continue
|
||||
}
|
||||
// Why: an unstamped workspace (externally created, or never opened here) reads as
|
||||
// epoch 0, which counted every one of them as idle and inflated the estimate far
|
||||
// past what the scan lists. Unknown is not old.
|
||||
const lastActivityAt = getPersistedWorkspaceCleanupActivityAt(worktree)
|
||||
if (
|
||||
lastActivityAt > 0 &&
|
||||
isWorkspaceOldForCleanup({ isArchived: worktree.isArchived, lastActivityAt }, now)
|
||||
) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
getWorkspaceCleanupReviewRank
|
||||
} from '../../../../shared/workspace-cleanup-facet-rankings'
|
||||
import type {
|
||||
WorkspaceCleanupSortDirectionState,
|
||||
WorkspaceCleanupSortField,
|
||||
WorkspaceCleanupSortState
|
||||
} from '../../../../shared/workspace-cleanup-filter-model'
|
||||
@@ -42,16 +41,6 @@ export function sortWorkspaceCleanupFacets(
|
||||
)
|
||||
}
|
||||
|
||||
export function compareWorkspaceCleanupFacets(
|
||||
left: WorkspaceCleanupFacets,
|
||||
right: WorkspaceCleanupFacets,
|
||||
field: WorkspaceCleanupSortField,
|
||||
direction: WorkspaceCleanupSortDirectionState
|
||||
): number {
|
||||
const multiplier = direction === 'asc' ? 1 : -1
|
||||
return comparePrimary(left, right, field, multiplier) || compareTieBreaks(left, right)
|
||||
}
|
||||
|
||||
function comparePrimary(
|
||||
left: WorkspaceCleanupFacets,
|
||||
right: WorkspaceCleanupFacets,
|
||||
|
||||
@@ -122,10 +122,6 @@ export function getWorkspaceCleanupGitState(
|
||||
return candidate.git.clean === false ? 'dirty' : 'unknown'
|
||||
}
|
||||
|
||||
export function hasWorkspaceCleanupUnpushedCommits(candidate: WorkspaceCleanupCandidate): boolean {
|
||||
return hasUnpushedCommits(candidate)
|
||||
}
|
||||
|
||||
function compareWorkspaceCleanupCandidates(
|
||||
left: WorkspaceCleanupCandidate,
|
||||
right: WorkspaceCleanupCandidate,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import type {
|
||||
WorkspaceCleanupScanError,
|
||||
WorkspaceCleanupScanProgress
|
||||
} from '../../../../shared/workspace-cleanup'
|
||||
import type { WorkspaceCleanupScanError } from '../../../../shared/workspace-cleanup'
|
||||
|
||||
function isDisconnectedRemoteScanError(message: string): boolean {
|
||||
return (
|
||||
@@ -48,22 +45,6 @@ export function formatWorkspaceCleanupScanNotice(
|
||||
)
|
||||
}
|
||||
|
||||
export function formatWorkspaceCleanupScanProgress(
|
||||
progress: WorkspaceCleanupScanProgress | null
|
||||
): string {
|
||||
if (!progress || progress.scannedWorktreeCount === 0) {
|
||||
return translate(
|
||||
'auto.components.workspace.cleanup.WorkspaceCleanupDialog.4cc5b73efe',
|
||||
'Finding workspaces...'
|
||||
)
|
||||
}
|
||||
return translate(
|
||||
'auto.components.workspace.cleanup.WorkspaceCleanupDialog.7b7bde5181',
|
||||
'Checked workspaces so far: {{value0}}',
|
||||
{ value0: progress.scannedWorktreeCount }
|
||||
)
|
||||
}
|
||||
|
||||
/** Reports scan size without classifying the user's work. */
|
||||
export function formatWorkspaceCleanupReadyToast(workspaceCount: number): string {
|
||||
if (workspaceCount === 0) {
|
||||
|
||||
@@ -3145,7 +3145,6 @@
|
||||
"efb3843e75": "Filter and sort workspaces",
|
||||
"93b7381d50": "Filters",
|
||||
"a615e24679": "Sort",
|
||||
"4cc5b73efe": "Finding workspaces...",
|
||||
"5bf2e88480": "{{value0}}/{{value1}} {{value2}} scanned",
|
||||
"searchPlaceholder": "Search workspaces",
|
||||
"ageFilter": "Age",
|
||||
@@ -3157,7 +3156,6 @@
|
||||
"1d3503357d": "You can close this and come back while deletion continues.",
|
||||
"4c2990886e": "{{value0}}/{{value1}} deleted",
|
||||
"86ba852118": "{{value0}}, {{value1}} failed",
|
||||
"7b7bde5181": "Checked workspaces so far: {{value0}}",
|
||||
"deletingCount": "Deleting workspaces: {{value0}}",
|
||||
"deleteCount": "Delete workspaces: {{value0}}?",
|
||||
"selectedForDeletionCount": "Selected for deletion: {{value0}}",
|
||||
@@ -11808,7 +11806,6 @@
|
||||
},
|
||||
"BrowserTerminalLinkActionsSetting": {
|
||||
"title": "Terminal URL clicks",
|
||||
"description": "Show available actions when you click a link in the terminal or a chat transcript. Turn this off to require {{modifier}}-click in the terminal.",
|
||||
"descriptionV2": "Control clicks on detected URLs printed in terminal panes and chat transcripts.",
|
||||
"plainClickLabel": "Plain click",
|
||||
"plainClickDescription": "Choose whether a left-click shows actions, opens the URL, or leaves it to the terminal. Cmd/Ctrl-click always opens directly.",
|
||||
|
||||
@@ -2931,7 +2931,6 @@
|
||||
"efb3843e75": "Filtrar y ordenar espacios de trabajo",
|
||||
"93b7381d50": "Filtros",
|
||||
"a615e24679": "Ordenar",
|
||||
"4cc5b73efe": "Buscando espacios de trabajo inactivos...",
|
||||
"5bf2e88480": "{{value0}}/{{value1}} {{value2}} escaneados",
|
||||
"searchPlaceholder": "Buscar espacios de trabajo",
|
||||
"ageFilter": "Edad",
|
||||
@@ -2943,7 +2942,6 @@
|
||||
"1d3503357d": "Puedes cerrar esto y volver mientras la eliminación continúa.",
|
||||
"4c2990886e": "{{value0}}/{{value1}} eliminados",
|
||||
"86ba852118": "{{value0}}, {{value1}} fallidos",
|
||||
"7b7bde5181": "Espacios de trabajo comprobados hasta ahora: {{value0}}",
|
||||
"deletingCount": "Eliminando espacios de trabajo: {{value0}}",
|
||||
"deleteCount": "¿Eliminar espacios de trabajo: {{value0}}?",
|
||||
"selectedForDeletionCount": "Seleccionados para eliminar: {{value0}}",
|
||||
@@ -12254,7 +12252,6 @@
|
||||
},
|
||||
"BrowserTerminalLinkActionsSetting": {
|
||||
"title": "Clics en la URL del terminal",
|
||||
"description": "Muestra las acciones disponibles al hacer clic en un enlace en la terminal o en una transcripción de chat. Desactive esto para requerir {{modifier}}-hacer clic en la terminal.",
|
||||
"descriptionV2": "Controle los clics en las URL detectadas impresas en paneles de terminales y transcripciones de chat.",
|
||||
"plainClickLabel": "clic simple",
|
||||
"plainClickDescription": "Elija si un clic izquierdo muestra acciones, abre la URL o la deja en el terminal. Cmd/Ctrl-clic siempre se abre directamente.",
|
||||
|
||||
@@ -3036,7 +3036,6 @@
|
||||
"efb3843e75": "Filtrer et trier les espaces de travail",
|
||||
"93b7381d50": "Filtres",
|
||||
"a615e24679": "Trier",
|
||||
"4cc5b73efe": "Recherche des espaces de travail...",
|
||||
"5bf2e88480": "{{value0}}/{{value1}} {{value2}} analysés",
|
||||
"searchPlaceholder": "Rechercher des espaces de travail",
|
||||
"ageFilter": "Âge",
|
||||
@@ -3048,7 +3047,6 @@
|
||||
"1d3503357d": "Vous pouvez fermer cette fenêtre et revenir pendant que la suppression continue.",
|
||||
"4c2990886e": "{{value0}}/{{value1}} supprimés",
|
||||
"86ba852118": "{{value0}}, {{value1}} en échec",
|
||||
"7b7bde5181": "Espaces de travail vérifiés jusqu'à présent : {{value0}}",
|
||||
"deletingCount": "Suppression des espaces de travail : {{value0}}",
|
||||
"deleteCount": "Supprimer les espaces de travail : {{value0}} ?",
|
||||
"selectedForDeletionCount": "Sélectionnés pour suppression : {{value0}}",
|
||||
@@ -11665,7 +11663,6 @@
|
||||
},
|
||||
"BrowserTerminalLinkActionsSetting": {
|
||||
"title": "Afficher les actions de lien du terminal",
|
||||
"description": "Affiche les actions disponibles quand vous cliquez sur un lien du terminal. Désactivez pour exiger un {{modifier}}+clic.",
|
||||
"descriptionV2": "Contrôlez les clics sur les URL détectées imprimées dans les volets du terminal et les transcriptions de discussion.",
|
||||
"plainClickLabel": "Clic simple",
|
||||
"plainClickDescription": "Choisissez si un clic gauche affiche les actions, ouvre l'URL ou la laisse au terminal. Cmd/Ctrl-clic s'ouvre toujours directement.",
|
||||
|
||||
@@ -2931,7 +2931,6 @@
|
||||
"efb3843e75": "ワークスペースのフィルターと並べ替え",
|
||||
"93b7381d50": "フィルター",
|
||||
"a615e24679": "並べ替え",
|
||||
"4cc5b73efe": "非アクティブなワークスペースを検索しています…",
|
||||
"5bf2e88480": "{{value0}}/{{value1}} {{value2}} をスキャンしました",
|
||||
"searchPlaceholder": "ワークスペースの検索",
|
||||
"ageFilter": "年",
|
||||
@@ -2943,7 +2942,6 @@
|
||||
"1d3503357d": "削除の続行中にこれを閉じて後で戻れます。",
|
||||
"4c2990886e": "{{value0}}/{{value1}} 件を削除済み",
|
||||
"86ba852118": "{{value0}}、{{value1}} 件が失敗",
|
||||
"7b7bde5181": "これまでに確認したワークスペース: {{value0}}",
|
||||
"deletingCount": "削除中のワークスペース: {{value0}}",
|
||||
"deleteCount": "ワークスペースを削除しますか: {{value0}}?",
|
||||
"selectedForDeletionCount": "削除対象として選択済み: {{value0}}",
|
||||
@@ -11957,7 +11955,6 @@
|
||||
},
|
||||
"BrowserTerminalLinkActionsSetting": {
|
||||
"title": "ターミナル URL のクリック数",
|
||||
"description": "ターミナル内のリンクまたはチャット記録をクリックしたときに利用可能な操作を表示します。ターミナルで {{modifier}} クリックを要求するには、これをオフにします。",
|
||||
"descriptionV2": "ターミナルペインおよびチャットトランスクリプトに出力される検出された URL のクリックを制御します。",
|
||||
"plainClickLabel": "プレーンクリック",
|
||||
"plainClickDescription": "左クリックで操作を表示するか、URL を開くか、ターミナルに任せるかを選択します。 Cmd/Ctrl キーを押しながらクリックすると、常に直接開きます。",
|
||||
|
||||
@@ -2931,7 +2931,6 @@
|
||||
"efb3843e75": "워크스페이스 필터링 및 정렬",
|
||||
"93b7381d50": "필터",
|
||||
"a615e24679": "종류",
|
||||
"4cc5b73efe": "비활성 워크스페이스를 찾는 중...",
|
||||
"5bf2e88480": "{{value0}}/{{value1}} {{value2}} 스캔됨",
|
||||
"searchPlaceholder": "워크스페이스 검색",
|
||||
"ageFilter": "나이",
|
||||
@@ -2943,7 +2942,6 @@
|
||||
"1d3503357d": "삭제가 계속되는 동안 이 창을 닫고 나중에 돌아올 수 있습니다.",
|
||||
"4c2990886e": "{{value0}}/{{value1}}개 삭제됨",
|
||||
"86ba852118": "{{value0}}, {{value1}}개 실패",
|
||||
"7b7bde5181": "지금까지 확인한 워크스페이스: {{value0}}",
|
||||
"deletingCount": "삭제 중인 워크스페이스: {{value0}}",
|
||||
"deleteCount": "워크스페이스 삭제: {{value0}}?",
|
||||
"selectedForDeletionCount": "삭제 대상으로 선택됨: {{value0}}",
|
||||
@@ -12005,7 +12003,6 @@
|
||||
},
|
||||
"BrowserTerminalLinkActionsSetting": {
|
||||
"title": "터미널 URL 클릭",
|
||||
"description": "터미널의 링크나 채팅 내용을 클릭하면 사용 가능한 작업이 표시됩니다. 터미널에서 {{modifier}}-클릭을 요구하려면 이 기능을 끄십시오.",
|
||||
"descriptionV2": "터미널 창과 채팅 기록에 인쇄된 감지된 URL에 대한 클릭을 제어합니다.",
|
||||
"plainClickLabel": "일반 클릭",
|
||||
"plainClickDescription": "왼쪽 클릭으로 작업을 표시할지, URL을 열지, 터미널에 그대로 둘지 선택하세요. Cmd/Ctrl 키를 누른 채 클릭하면 항상 바로 열립니다.",
|
||||
|
||||
@@ -2931,7 +2931,6 @@
|
||||
"efb3843e75": "筛选并排序工作区",
|
||||
"93b7381d50": "筛选",
|
||||
"a615e24679": "排序",
|
||||
"4cc5b73efe": "正在查找非活动工作区...",
|
||||
"5bf2e88480": "已扫描 {{value0}}/{{value1}} 个{{value2}}",
|
||||
"searchPlaceholder": "搜索工作区",
|
||||
"ageFilter": "时间",
|
||||
@@ -2943,7 +2942,6 @@
|
||||
"1d3503357d": "删除继续期间,你可以关闭此窗口并稍后回来。",
|
||||
"4c2990886e": "已删除 {{value0}}/{{value1}}",
|
||||
"86ba852118": "{{value0}},{{value1}} 个失败",
|
||||
"7b7bde5181": "到目前为止已检查工作区:{{value0}}",
|
||||
"deletingCount": "正在删除工作区:{{value0}}",
|
||||
"deleteCount": "删除工作区:{{value0}}?",
|
||||
"selectedForDeletionCount": "已选择删除:{{value0}}",
|
||||
@@ -11970,7 +11968,6 @@
|
||||
},
|
||||
"BrowserTerminalLinkActionsSetting": {
|
||||
"title": "终端 URL 点击次数",
|
||||
"description": "单击终端或聊天记录中的链接时显示可用的操作。关闭此功能需要在终端中单击 {{modifier}}。",
|
||||
"descriptionV2": "控制点击终端窗格和聊天记录中打印的已检测的 URL。",
|
||||
"plainClickLabel": "简单的点击",
|
||||
"plainClickDescription": "选择左键单击是显示操作、打开 URL 还是将其留给终端。 Cmd/Ctrl-单击始终直接打开。",
|
||||
|
||||
@@ -96,11 +96,6 @@ export function getHibernationBoundaryResolvedAtByPaneKey(): Record<string, numb
|
||||
return Object.fromEntries(boundaryResolvedAtByPaneKey)
|
||||
}
|
||||
|
||||
export function forgetHibernationPaneAge(paneKey: string): void {
|
||||
ptyBindingByPaneKey.delete(paneKey)
|
||||
boundaryResolvedAtByPaneKey.delete(paneKey)
|
||||
}
|
||||
|
||||
export function resetHibernationPaneAgeForTests(): void {
|
||||
ptyBindingByPaneKey.clear()
|
||||
boundaryResolvedAtByPaneKey.clear()
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Login shell (`$SHELL`) of the machine running this client, or '' when
|
||||
* unknown (web build, preload unavailable).
|
||||
*
|
||||
* Why: commands Orca hands the user to paste — or types into a locally
|
||||
* spawned pane — are parsed by that shell, and fish does not share the sh
|
||||
* grammar for every construct.
|
||||
*/
|
||||
export function getClientLoginShell(): string {
|
||||
try {
|
||||
return window.api?.platform?.get?.().shell ?? ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
@@ -12,14 +12,6 @@ export const PALETTE_MATCH_QUALITIES = [
|
||||
|
||||
export type PaletteMatchQuality = (typeof PALETTE_MATCH_QUALITIES)[number]
|
||||
|
||||
const QUALITY_RANK = new Map<PaletteMatchQuality, number>(
|
||||
PALETTE_MATCH_QUALITIES.map((quality, index) => [quality, index])
|
||||
)
|
||||
|
||||
export function paletteMatchQualityRank(quality: PaletteMatchQuality): number {
|
||||
return QUALITY_RANK.get(quality) ?? PALETTE_MATCH_QUALITIES.length
|
||||
}
|
||||
|
||||
export function isFuzzyPaletteMatchQuality(quality: PaletteMatchQuality): boolean {
|
||||
return quality === 'typo'
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import {
|
||||
IDLE,
|
||||
collectTabPaneInputs,
|
||||
resolveAttention,
|
||||
type SmartClass,
|
||||
type TabPaneInputSources,
|
||||
type WorktreeAttention
|
||||
type TabPaneInputSources
|
||||
} from '@/components/sidebar/smart-attention'
|
||||
import { tabHasLivePty } from './tab-has-live-pty'
|
||||
import { agentVerdictDisplayMark } from '../../../shared/agent-main-agent-verdict'
|
||||
@@ -49,20 +47,6 @@ const STATUS_BY_ATTENTION_CLASS: Record<SmartClass, WorktreeStatus | null> = {
|
||||
5: null
|
||||
}
|
||||
|
||||
export function resolveRecentWorkspaceTabAttention(
|
||||
row: RecentWorkspaceTabRow,
|
||||
paneSources: TabPaneInputSources,
|
||||
now: number
|
||||
): WorktreeAttention {
|
||||
if (!row.terminalTab) {
|
||||
return IDLE
|
||||
}
|
||||
return resolveAttention(
|
||||
collectTabPaneInputs(row.terminalTab, row.worktreeLastActivityAt, paneSources, now),
|
||||
now
|
||||
)
|
||||
}
|
||||
|
||||
/** Live status dot for a hero row — re-read on agent churn, unlike the frozen ordering. */
|
||||
export function resolveRecentWorkspaceTabStatus(
|
||||
row: RecentWorkspaceTabRow,
|
||||
|
||||
@@ -54,10 +54,6 @@ export function getWorktreeIdFromVisitKey(key: string): string {
|
||||
return key
|
||||
}
|
||||
|
||||
export function isHostQualifiedVisitKey(key: string): boolean {
|
||||
return getWorktreeIdFromVisitKey(key) !== key
|
||||
}
|
||||
|
||||
/** Remove all recency entries belonging to one or more raw worktree ids. */
|
||||
export function removeWorktreeVisitEntries(
|
||||
timestamps: Readonly<Record<string, number>>,
|
||||
|
||||
@@ -45,14 +45,6 @@ export function getBrowserWorkspaceRemoteOwnership(
|
||||
return { kind: 'exact', environmentId: environmentIds[0]! }
|
||||
}
|
||||
|
||||
export function getBrowserWorkspaceRemoteOwnerEnvironmentId(
|
||||
state: RemoteBrowserTabOwnershipState,
|
||||
workspaceId: string
|
||||
): string | null {
|
||||
const ownership = getBrowserWorkspaceRemoteOwnership(state, workspaceId)
|
||||
return ownership.kind === 'exact' ? ownership.environmentId : null
|
||||
}
|
||||
|
||||
export function browserWorkspaceHasRemoteOwner(
|
||||
state: RemoteBrowserTabOwnershipState,
|
||||
workspaceId: string,
|
||||
|
||||
@@ -154,22 +154,6 @@ function exposeFaultApi(): void {
|
||||
|
||||
exposeFaultApi()
|
||||
|
||||
export function observeE2eWebRuntimeBrowserCreation(result: Promise<boolean>): void {
|
||||
if (!e2eConfig.exposeStore || !settleCreation) {
|
||||
return
|
||||
}
|
||||
const settle = settleCreation
|
||||
settleCreation = null
|
||||
void result.then(
|
||||
(created) => settle({ status: 'fulfilled', created }),
|
||||
(error) =>
|
||||
settle({
|
||||
status: 'rejected',
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export function throwIfE2eWebRuntimeBrowserCapabilityUnavailable(): void {
|
||||
if (!e2eConfig.exposeStore || !capabilityRejectionArmed) {
|
||||
return
|
||||
|
||||
@@ -146,10 +146,6 @@ export function shouldRefetchGitHubScopedResultForNoHint(
|
||||
)
|
||||
}
|
||||
|
||||
export function canReuseInflightHint(inflightHintKey: string, nextHintKey: string): boolean {
|
||||
return inflightHintKey === nextHintKey
|
||||
}
|
||||
|
||||
export function isStaleMergedGitHubReviewForHead(
|
||||
cached: HostedReviewCacheEntry<HostedReviewInfo> | undefined,
|
||||
currentHeadOid: string | null | undefined
|
||||
|
||||
@@ -337,12 +337,6 @@ function isAgentLaunchPromptDelivery(value: unknown): value is AgentLaunchPrompt
|
||||
return value === 'submit' || value === 'draft'
|
||||
}
|
||||
|
||||
export function agentLaunchTargetIsCreate(
|
||||
target: AgentLaunchTarget
|
||||
): target is Extract<AgentLaunchTarget, { kind: 'create-worktree' }> {
|
||||
return target.kind === 'create-worktree'
|
||||
}
|
||||
|
||||
/** The agent fields a create payload must not carry: the launch owns placement, and a caller that
|
||||
* sets one of these would route itself around the host's decision. */
|
||||
export const AGENT_LAUNCH_RESERVED_CREATE_FIELDS = [
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
agentChildWorkFencesEqual,
|
||||
type AgentChildWorkId,
|
||||
type AgentChildWorkInvocationFence,
|
||||
type AgentChildWorkKind
|
||||
import type {
|
||||
AgentChildWorkId,
|
||||
AgentChildWorkInvocationFence,
|
||||
AgentChildWorkKind
|
||||
} from './agent-status-child-work'
|
||||
import { parseAgentChildWorkInvocationFence } from './agent-status-child-work-codec'
|
||||
import {
|
||||
@@ -218,14 +217,3 @@ export function deserializeAgentChildWorkAliasKey(
|
||||
}
|
||||
return serializeAgentChildWorkAliasKey(identity) === value ? identity : null
|
||||
}
|
||||
|
||||
export function agentChildWorkAliasesMatch(
|
||||
left: AgentChildWorkAliasInput,
|
||||
right: AgentChildWorkAliasInput
|
||||
): boolean {
|
||||
return (
|
||||
serializeAgentChildWorkAliasKey(left) === serializeAgentChildWorkAliasKey(right) &&
|
||||
left.childWorkId === right.childWorkId &&
|
||||
agentChildWorkFencesEqual(left.fence, right.fence)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ export const AGENT_STATUS_RUNS_RUNTIME_CAPABILITY = 'agent-status.runs.v1' as co
|
||||
|
||||
export const AGENT_STATUS_CAPABILITIES = [AGENT_STATUS_RUNS_RUNTIME_CAPABILITY] as const
|
||||
export type AgentStatusCapability = (typeof AGENT_STATUS_CAPABILITIES)[number]
|
||||
|
||||
const AGENT_STATUS_CAPABILITY_SET: ReadonlySet<string> = new Set(AGENT_STATUS_CAPABILITIES)
|
||||
const MAX_CAPABILITIES = 256
|
||||
const MAX_CAPABILITY_LENGTH = 128
|
||||
|
||||
@@ -44,7 +42,3 @@ export function hasAgentStatusRunCapability(value: unknown): boolean {
|
||||
deserializeAgentStatusCapabilities(value)?.has(AGENT_STATUS_RUNS_RUNTIME_CAPABILITY) === true
|
||||
)
|
||||
}
|
||||
|
||||
export function isAgentStatusCapability(value: unknown): value is AgentStatusCapability {
|
||||
return typeof value === 'string' && AGENT_STATUS_CAPABILITY_SET.has(value)
|
||||
}
|
||||
|
||||
@@ -78,18 +78,6 @@ export function createEmptyAgentStatusStoreState(epoch: string): AgentStatusStor
|
||||
}
|
||||
}
|
||||
|
||||
export function cloneAgentStatusStoreState(state: AgentStatusStoreState): AgentStatusStoreState {
|
||||
return {
|
||||
epoch: state.epoch,
|
||||
revision: state.revision,
|
||||
parents: new Map(state.parents),
|
||||
children: new Map(state.children),
|
||||
aliases: new Map(state.aliases),
|
||||
facts: new Map(state.facts),
|
||||
tombstones: new Map(state.tombstones)
|
||||
}
|
||||
}
|
||||
|
||||
function snapshotCandidateFromAgentStatusStoreState(state: AgentStatusStoreState) {
|
||||
return {
|
||||
version: AGENT_STATUS_STORE_SNAPSHOT_VERSION,
|
||||
|
||||
@@ -153,10 +153,6 @@ export function parseAgentStatusSubject(value: unknown): AgentStatusSubject | nu
|
||||
return null
|
||||
}
|
||||
|
||||
export function isAgentStatusSubject(value: unknown): value is AgentStatusSubject {
|
||||
return parseAgentStatusSubject(value) !== null
|
||||
}
|
||||
|
||||
function subjectKeyTuple(subject: AgentStatusSubject): AgentStatusSubjectKeyTuple {
|
||||
const identity =
|
||||
subject.kind === 'pty-run'
|
||||
|
||||
@@ -17,10 +17,6 @@ export const AGENT_JOURNAL_TURN_OUTCOMES = [
|
||||
] as const
|
||||
export type AgentJournalTurnOutcome = (typeof AGENT_JOURNAL_TURN_OUTCOMES)[number]
|
||||
|
||||
export function isAgentJournalTurnOutcome(value: unknown): value is AgentJournalTurnOutcome {
|
||||
return AGENT_JOURNAL_TURN_OUTCOMES.some((known) => known === value)
|
||||
}
|
||||
|
||||
/** The verdict an agent-status row's `mainAgent.outcome` reports: the provider's, a `cancellation`
|
||||
* Orca inferred from the user's own interrupt keystroke, or what the host observed of a turn's end
|
||||
* when the provider gave no verdict. `interruption` is a death mid-turn the host proved and nobody
|
||||
|
||||
@@ -25,10 +25,6 @@ const LEGACY_SEARCH_AGENTS: readonly AiVaultAgent[] = [
|
||||
'muse'
|
||||
]
|
||||
|
||||
export function needsSearchAgentNegotiation(agents: readonly AiVaultAgent[]): boolean {
|
||||
return agents.some((agent) => !LEGACY_SEARCH_AGENTS.includes(agent))
|
||||
}
|
||||
|
||||
export function compatibleSearchAgents(
|
||||
agents: readonly AiVaultAgent[],
|
||||
status: Pick<
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
BrowserPage,
|
||||
BrowserPageDocLocation,
|
||||
BrowserWorkspace
|
||||
} from './browser-workspace-types'
|
||||
import type { BrowserPageDocLocation } from './browser-workspace-types'
|
||||
import { relativePathInsideRoot, resolveRuntimePath } from './cross-platform-path'
|
||||
|
||||
/**
|
||||
@@ -20,13 +16,6 @@ export function browserPageDocLocationsEqual(
|
||||
return left.worktreeId === right.worktreeId && left.filePath === right.filePath
|
||||
}
|
||||
|
||||
/** True for a page or tab that shows a workspace document rather than a URL. */
|
||||
export function isWorkspaceDocSurface(
|
||||
surface: Pick<BrowserPage | BrowserWorkspace, 'docLocation'>
|
||||
): boolean {
|
||||
return Boolean(surface.docLocation)
|
||||
}
|
||||
|
||||
export function remapBrowserPageDocLocation(
|
||||
location: BrowserPageDocLocation,
|
||||
oldWorktreeId: string,
|
||||
|
||||
@@ -73,18 +73,6 @@ export class ExternalAutomationScopeError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
const SCOPE_CODE_VALUES: readonly ExternalAutomationScopeCode[] = Object.values(
|
||||
EXTERNAL_AUTOMATION_SCOPE_CODES
|
||||
)
|
||||
|
||||
export function isExternalAutomationScopeCode(
|
||||
value: unknown
|
||||
): value is ExternalAutomationScopeCode {
|
||||
return (
|
||||
typeof value === 'string' && SCOPE_CODE_VALUES.includes(value as ExternalAutomationScopeCode)
|
||||
)
|
||||
}
|
||||
|
||||
/** The captured incarnation no longer matches the current SSH registration. */
|
||||
export function externalAutomationHostChangedError(): AutomationOwnerConflictError {
|
||||
return new AutomationOwnerConflictError(AUTOMATION_OWNER_CONFLICT_CODES.ownerChanged)
|
||||
|
||||
@@ -11,10 +11,6 @@ export const ISSUE_LINK_PROVIDERS = [
|
||||
|
||||
export type IssueLinkProvider = (typeof ISSUE_LINK_PROVIDERS)[number]
|
||||
|
||||
export function isIssueLinkProvider(value: unknown): value is IssueLinkProvider {
|
||||
return ISSUE_LINK_PROVIDERS.includes(value as IssueLinkProvider)
|
||||
}
|
||||
|
||||
/** URL input only. Linear and Jira issue keys are byte-identical in shape, so a
|
||||
* bare `STA-335` can never decide a provider — it would override the chip. */
|
||||
export function getIssueLinkProviderFromUrl(input: string): IssueLinkProvider | null {
|
||||
|
||||
@@ -272,11 +272,3 @@ export function parseStrictProcessTableRows(stdout: string): ProcessTableRow[] {
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
/**
|
||||
* Rank a descendant row as a foreground candidate: a `+` (foreground process
|
||||
* group) row always outranks a background one, then the deepest wins.
|
||||
*/
|
||||
export function scoreForegroundCandidateRow(row: ProcessTableRow & { depth: number }): number {
|
||||
return (row.stat.includes('+') ? 10_000 : 0) + row.depth
|
||||
}
|
||||
|
||||
@@ -79,15 +79,3 @@ export function closeSharedControlSubscription(args: {
|
||||
}): void {
|
||||
closeSharedControlConnectionSubscription(args)
|
||||
}
|
||||
|
||||
export function closeRuntimeControlSubscription(args: {
|
||||
subscriptions: Map<string, SharedControlLogicalSubscription<unknown>>
|
||||
retiredRequestIds: SharedControlRetiredRequestIds
|
||||
requestId: string
|
||||
deviceToken: string
|
||||
send: (payload: unknown) => boolean
|
||||
clearWhenIdle: (isIdle: boolean) => void
|
||||
}): void {
|
||||
closeSharedControlSubscription(args)
|
||||
args.clearWhenIdle(args.subscriptions.size === 0)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ import { releaseRemoteRuntimePreparedRequest } from './remote-runtime-prepared-r
|
||||
import { remoteRuntimeUnavailableError } from './remote-runtime-request-frames'
|
||||
import type { RuntimeRpcResponse } from './runtime-rpc-envelope'
|
||||
import type {
|
||||
RemoteRuntimeSharedConnectionDiagnostics,
|
||||
SharedControlConnectionState,
|
||||
SharedControlLogicalSubscription,
|
||||
SharedControlPendingRequest,
|
||||
SharedControlReadyWaiter
|
||||
@@ -13,26 +11,6 @@ import { getSubscriptionId, isEndResult } from './remote-runtime-shared-control-
|
||||
import { withReconnectJitter } from './reconnect-jitter'
|
||||
import { tagRuntimeSubscriptionReplayResponse } from './runtime-subscription-replay'
|
||||
|
||||
export function buildSharedControlDiagnostics(args: {
|
||||
state: SharedControlConnectionState
|
||||
reconnecting: boolean
|
||||
pendingRequestCount: number
|
||||
subscriptionCount: number
|
||||
reconnectAttempt: number
|
||||
diag: Pick<
|
||||
RemoteRuntimeSharedConnectionDiagnostics,
|
||||
'lastConnectedAt' | 'lastClose' | 'lastError'
|
||||
>
|
||||
}): RemoteRuntimeSharedConnectionDiagnostics {
|
||||
return {
|
||||
state: args.reconnecting ? 'reconnecting' : args.state,
|
||||
pendingRequestCount: args.pendingRequestCount,
|
||||
subscriptionCount: args.subscriptionCount,
|
||||
reconnectAttempt: args.reconnectAttempt,
|
||||
...args.diag
|
||||
}
|
||||
}
|
||||
|
||||
export function rejectSharedControlPendingRequest(
|
||||
pendingRequests: Map<string, SharedControlPendingRequest<unknown>>,
|
||||
requestId: string,
|
||||
|
||||
@@ -22,12 +22,3 @@ export function reviewRefreshIntervalMs(input: {
|
||||
}
|
||||
return input.selected ? 60_000 : 120_000
|
||||
}
|
||||
|
||||
export function finishedReviewRefreshIntervalMs(
|
||||
state: HostedReviewState | null | undefined,
|
||||
status: CheckStatus | null | undefined
|
||||
): number | null {
|
||||
return state === 'merged' || state === 'closed'
|
||||
? reviewRefreshIntervalMs({ state, checksStatus: status })
|
||||
: null
|
||||
}
|
||||
|
||||
@@ -106,20 +106,6 @@ export function isSkillInstallProviderId(value: string): value is SkillInstallPr
|
||||
return PROVIDERS_BY_ID.has(value as SkillInstallProviderId)
|
||||
}
|
||||
|
||||
export function skillInstallProvider(
|
||||
id: SkillInstallProviderId
|
||||
): SkillInstallProviderDefinition | undefined {
|
||||
return PROVIDERS_BY_ID.get(id)
|
||||
}
|
||||
|
||||
/** Detected agents Orca can actually place skills for, in registry order. */
|
||||
export function installableSkillProviders(
|
||||
detectedProviders: readonly string[]
|
||||
): SkillInstallProviderDefinition[] {
|
||||
const detected = new Set(detectedProviders)
|
||||
return SKILL_INSTALL_PROVIDERS.filter((provider) => detected.has(provider.id))
|
||||
}
|
||||
|
||||
/**
|
||||
* An explicit choice is authoritative, including agents the target may install
|
||||
* later. Removal passes no choice so it can clean every previously used root.
|
||||
|
||||
@@ -28,18 +28,6 @@ import { createOsc133CommandFinishedScanner } from './terminal-osc133-command-fi
|
||||
/** Ms of title-less output after a working title before it is cleared. */
|
||||
export const STALE_WORKING_TITLE_TIMEOUT_MS = 3000
|
||||
|
||||
// Braille spinner glyphs (U+2800–U+28FF); mirrors the range clearWorkingIndicators strips in agent-detection.ts.
|
||||
// eslint-disable-next-line no-control-regex -- intentional unicode range
|
||||
const BRAILLE_SPINNER_RE = /[\u2800-\u28FF]/g
|
||||
|
||||
/**
|
||||
* Strip decorative braille spinner frame glyphs so titles differing only by the animation frame
|
||||
* compare equal — the gate consumers use to avoid fan-out churn on spinner ticks.
|
||||
*/
|
||||
export function stripBrailleSpinnerGlyphs(title: string): string {
|
||||
return title.replace(BRAILLE_SPINNER_RE, '').trim()
|
||||
}
|
||||
|
||||
/** Provenance for title/idle facts; `staleWorkingTitleClear` marks facts synthesized by the 3s stale timer — not genuine task completions. */
|
||||
export type TerminalTitleFactMeta = {
|
||||
staleWorkingTitleClear?: boolean
|
||||
|
||||
@@ -89,10 +89,6 @@ export function setLeaf(
|
||||
return { ...workspace, leaves }
|
||||
}
|
||||
|
||||
export function paneKeysOf(tab: LayoutTerminalTab): string[] {
|
||||
return leafIdsOf(tab).map((leafId) => paneKeyOf(tab.entityId, leafId))
|
||||
}
|
||||
|
||||
/** The terminal tab and pane a pane key names in this workspace. */
|
||||
export function locatePane(
|
||||
workspace: WorkspaceLayout,
|
||||
|
||||
@@ -10,10 +10,6 @@ export type ZcodePlanSite = 'zai' | 'bigmodel'
|
||||
|
||||
export const ZCODE_PLAN_SITES: readonly ZcodePlanSite[] = ['zai', 'bigmodel']
|
||||
|
||||
export function isZcodePlanSite(value: unknown): value is ZcodePlanSite {
|
||||
return value === 'zai' || value === 'bigmodel'
|
||||
}
|
||||
|
||||
export const ZCODE_PLAN_SITE_BASE_URLS: Record<ZcodePlanSite, string> = {
|
||||
zai: 'https://api.z.ai',
|
||||
bigmodel: 'https://open.bigmodel.cn'
|
||||
|
||||
Reference in New Issue
Block a user