Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
172b7a4b1This file is pinned to the selected release. Download the original to open it in your design tool.
import { randomUUID } from 'node:crypto'
export interface SelectionFocus { connId: string; machineId: string; agentId: string }
export interface SelectionCommand {
agentId: string
op: 'begin' | 'step' | 'extend' | 'pin' | 'cancel' | 'read' | 'search' | 'match' | 'lines'
selectionId?: string
revision?: number
delta?: number
extend?: boolean
query?: string
}
export type SelectionResult = { ok: false; error: string } | {
ok: true; selectionId: string; revision: number; excerpt: string; rows: number; extending: boolean
text?: string
query?: string; match?: number; matches?: number
}
interface Selection extends SelectionFocus {
id: string
revision: number
pinned: boolean
last?: Extract<SelectionResult, { ok: true }>
}
interface Pending {
selection: Selection
requestId: string
revision: number
op: SelectionCommand['op']
resolve: (result: SelectionResult) => void
timer: ReturnType<typeof setTimeout>
}
/** One reading cursor on THIS desk. A request goes to the exact connection
* that reported app_focus, including when that socket views a remote machine.
* No cloud broadcast, inferred recipient, automatic retry, or terminal input. */
export class WindowSelection {
private selection?: Selection
private pending?: Pending
constructor(private readonly wiring: {
focus: () => SelectionFocus | undefined
send: (connId: string, payload: Record<string, unknown>) => boolean
timeoutMs?: number
}) {}
async command(command: SelectionCommand): Promise<SelectionResult> {
if (command.op === 'cancel') {
const current = this.selection
if (current && current.id === command.selectionId && current.agentId === command.agentId) this.cancel()
return { ok: false, error: 'Selection closed.' }
}
const focus = this.wiring.focus()
if (!focus || focus.agentId !== command.agentId) {
this.cancel()
return { ok: false, error: 'Select that pane in Harness first.' }
}
if (command.op === 'begin') {
if (command.selectionId !== undefined && !/^[a-zA-Z0-9-]{1,48}$/.test(command.selectionId)) {
return { ok: false, error: 'Invalid selection identity.' }
}
this.cancel()
this.selection = { ...focus, id: command.selectionId ?? randomUUID(), revision: 0, pinned: false }
}
const selection = this.selection
if (!selection || !sameFocus(focus, selection) ||
(command.op !== 'begin' && (selection.id !== command.selectionId || selection.revision !== command.revision))) {
return { ok: false, error: 'That selection expired. Choose the text again.' }
}
if (this.pending || selection.pinned) return { ok: false, error: 'Wait for the current selection.' }
if (command.op === 'read') return selection.last ? { ...selection.last } : { ok: false, error: 'Choose the text again.' }
if ((command.op === 'step' || command.op === 'match') && (!Number.isSafeInteger(command.delta) || command.delta === 0 || Math.abs(command.delta!) > 8)) {
return { ok: false, error: 'Invalid selection movement.' }
}
if (selection.last?.query && (command.op === 'step' || command.op === 'extend')) return { ok: false, error: 'Choose Lines to adjust this passage.' }
if (command.op === 'match' && !selection.last?.query) return { ok: false, error: 'Say what to find first.' }
if (command.op === 'search' && (typeof command.query !== 'string' || !command.query.trim() ||
Buffer.byteLength(command.query, 'utf8') > 120 || /[\x00-\x1f\x7f-\x9f]/.test(command.query))) {
return { ok: false, error: 'Say a short phrase to find.' }
}
if (command.op === 'extend' && typeof command.extend !== 'boolean') return { ok: false, error: 'Invalid selection range.' }
const requestId = randomUUID()
const revision = ++selection.revision
return new Promise(resolve => {
const timer = setTimeout(() => {
if (this.pending?.requestId !== requestId) return
this.finish({ ok: false, error: 'The app did not answer. Update or reopen Harness.' })
this.cancel()
}, this.wiring.timeoutMs ?? 2000)
this.pending = { selection, requestId, revision, op: command.op, resolve, timer }
const sent = this.wiring.send(focus.connId, {
requestId, selectionId: selection.id, revision, agentId: focus.agentId,
machineId: focus.machineId, op: command.op,
...(command.op === 'step' || command.op === 'match' ? { delta: command.delta } : {}),
...(command.op === 'extend' ? { extend: command.extend } : {}),
...(command.op === 'search' ? { query: command.query } : {}),
})
if (!sent) {
this.finish({ ok: false, error: 'Open Harness on this computer.' })
this.cancel()
}
})
}
reply(connId: string, machineId: string, payload: Record<string, unknown>): void {
const p = this.pending
if (!p || connId !== p.selection.connId || machineId !== p.selection.machineId ||
payload.requestId !== p.requestId || payload.selectionId !== p.selection.id ||
payload.agentId !== p.selection.agentId || payload.machineId !== machineId || payload.revision !== p.revision) return
if (!sameFocus(this.wiring.focus(), p.selection)) {
this.finish({ ok: false, error: 'The selected pane changed. Choose the text again.' })
this.cancel()
return
}
if (payload.ok !== true) {
// Errors are product text, bounded independently from any selected content.
this.finish({ ok: false, error: typeof payload.error === 'string' ? payload.error.slice(0, 180) : 'Choose the text again.' })
this.cancel()
return
}
const { excerpt, rows, extending, text, query, match, matches } = payload
const searching = typeof query === 'string' && !!query.trim() && Buffer.byteLength(query, 'utf8') <= 120 &&
!/[\x00-\x1f\x7f-\x9f]/.test(query) && Number.isSafeInteger(matches) && (matches as number) >= 0 &&
(matches as number) <= 0x7fffffff && Number.isSafeInteger(match) &&
((matches === 0 && match === 0 && rows === 0 && excerpt === '' && extending === false) || ((match as number) >= 1 && (match as number) <= (matches as number)))
if (typeof excerpt !== 'string' || excerpt.length > 240 || !Number.isSafeInteger(rows) ||
(rows as number) < (searching && matches === 0 ? 0 : 1) || (rows as number) > 16 || typeof extending !== 'boolean' ||
((query !== undefined || p.op === 'search' || p.op === 'match') && !searching) ||
(p.op === 'pin' && (typeof text !== 'string' || !text.trim() || Buffer.byteLength(text, 'utf8') > 4096))) {
this.finish({ ok: false, error: 'The app returned an invalid selection.' })
this.cancel()
return
}
if (p.op === 'pin') p.selection.pinned = true
const result: SelectionResult = { ok: true, selectionId: p.selection.id, revision: p.revision,
excerpt, rows: rows as number, extending, ...(p.op === 'pin' ? { text: text as string } : {}),
...(searching ? { query: query as string, match: match as number, matches: matches as number } : {}) }
p.selection.last = result
this.finish(result)
}
cancel(): void {
const selection = this.selection
this.selection = undefined
this.finish({ ok: false, error: 'Selection closed.' })
if (selection) this.wiring.send(selection.connId, {
requestId: randomUUID(), selectionId: selection.id, revision: selection.revision + 1,
machineId: selection.machineId, agentId: selection.agentId, op: 'cancel',
})
}
focusChanged(): void {
if (this.selection && !sameFocus(this.wiring.focus(), this.selection)) this.cancel()
}
private finish(result: SelectionResult): void {
const pending = this.pending
this.pending = undefined
if (!pending) return
clearTimeout(pending.timer)
pending.resolve(result)
}
}
function sameFocus(a: SelectionFocus | undefined, b: SelectionFocus): boolean {
return !!a && a.connId === b.connId && a.machineId === b.machineId && a.agentId === b.agentId
}
/** Keep the user's words intact. The selected output is visibly quoted context,
* not a command from the device and not an assertion that a file was edited. */
export function withSelectedPassage(instruction: string, passage: string): string {
return `${instruction}\n\nContext I selected from this agent's terminal:\n${passage.split(/\r?\n/).map(line => `> ${line}`).join('\n')}`
}