/** * Caller identity, workspace authorization, and visible lineage projection. * * @module @deepseek-ai/dsh-tool-session-query/workspace-access */ import type { Context } from 'cordis' import { HarnessError } from '@deepseek-ai/dsh-llm' import { SessionId, type SessionEvent, type SessionHeader, type SessionId as SessionIdValue, } from '@deepseek-ai/dsh-session' import type { SessionLineageNode, SessionRecord, } from '@deepseek-ai/dsh-session-query' import type { ToolRunContext } from '@deepseek-ai/dsh-tools' import { serviceBoundary } from './service-boundary.ts' interface Caller { readonly id: SessionIdValue readonly header: SessionHeader readonly events: readonly SessionEvent[] } interface TitleView { readonly text: string readonly unavailableCode?: string } interface CompleteTitleMap extends ReadonlyMap { get(id: SessionIdValue): TitleView } interface AuthorizedDescendant { readonly record: SessionRecord readonly descendants: Array } interface DescendantProjectionFrame { readonly node: SessionLineageNode readonly target: Array readonly next: DescendantProjectionFrame | undefined } interface DescendantVisit { readonly node: AuthorizedDescendant | null readonly depth: number readonly next: DescendantVisit | undefined } function callerOf(exec: ToolRunContext): Caller { const agent = exec.agent if (agent === undefined) { throw new HarnessError( 'session query tools require an agent-bound caller', 'SESSION_QUERY_TOOL_MISSING_AGENT', ) } return { id: agent.session.id, header: agent.session.header, events: agent.session.events, } } function targetId(args: { readonly session_id?: string }, caller: Caller): SessionIdValue { return args.session_id === undefined ? caller.id : SessionId(args.session_id) } async function authorizeTarget( ctx: Context, caller: Caller, target: SessionIdValue, signal: AbortSignal, ): Promise { if (target === caller.id) return const cwd = caller.header.cwd if (cwd === undefined) throw serviceBoundary.unauthorizedTarget() const records = await serviceBoundary.call(ctx, signal, 'target authorization', () => ctx.sessionQuery.filterSessions([ { kind: 'id', values: [target] }, { kind: 'cwd', values: [cwd] }, ], signal)) if (records.length !== 1) throw serviceBoundary.unauthorizedTarget() } function recordAuthorized(record: SessionRecord, caller: Caller): boolean { return headerAuthorized(record.header, caller) } function headerAuthorized(header: SessionHeader, caller: Caller): boolean { if (header.id === caller.id) return header.cwd === caller.header.cwd return caller.header.cwd !== undefined && header.cwd === caller.header.cwd } function assertObservedTargetAuthorized( caller: Caller, target: SessionIdValue, observed: SessionHeader, ): void { if (observed.id !== target || !headerAuthorized(observed, caller)) { throw serviceBoundary.unauthorizedTarget() } } async function authorizeSessionIds( ctx: Context, caller: Caller, ids: readonly SessionIdValue[], signal: AbortSignal, ): Promise> { const unique = [...new Set(ids)] const authorized = new Set() if (unique.includes(caller.id)) authorized.add(caller.id) const cwd = caller.header.cwd const other = unique.filter(id => id !== caller.id) if (cwd === undefined || other.length === 0) return authorized const records = await serviceBoundary.call(ctx, signal, 'session-id authorization', () => ctx.sessionQuery.filterSessions([ { kind: 'id', values: other }, { kind: 'cwd', values: [cwd] }, ], signal)) const requested = new Set(other) for (const record of records) { if (requested.has(record.header.id) && recordAuthorized(record, caller)) { authorized.add(record.header.id) } } return authorized } async function readTitles( ctx: Context, caller: Caller, ids: readonly SessionIdValue[], signal: AbortSignal, ): Promise { const result = new Map() const observations = await serviceBoundary.call(ctx, signal, 'title observation', () => ctx.sessionQuery.readTitleSnapshots(ids, signal)) for (const observation of observations) { if (observation.status === 'rejected') { result.set(observation.sessionId, unavailableTitle(ctx, observation.reason)) continue } assertObservedTargetAuthorized(caller, observation.sessionId, observation.value.session) result.set(observation.sessionId, { text: observation.value.title?.title ?? 'untitled' }) } return result as CompleteTitleMap } async function readTitle( ctx: Context, caller: Caller, id: SessionIdValue, signal: AbortSignal, ): Promise { return (await readTitles(ctx, caller, [id], signal)).get(id) } function unavailableTitle( ctx: Context, error: unknown, ): TitleView { const sanitized = serviceBoundary.sanitizeError(ctx, 'title observation item', error) if (sanitized.code === 'SESSION_QUERY_TOOL_UNAUTHORIZED') throw sanitized return { text: 'untitled', unavailableCode: sanitized.code } } function authorizeDescendants( nodes: readonly SessionLineageNode[], caller: Caller, ): Array { const result: Array = [] let pending: DescendantProjectionFrame | undefined for (const node of [...nodes].reverse()) { pending = { node, target: result, next: pending } } while (pending !== undefined) { const current = pending pending = current.next if (!recordAuthorized(current.node.session, caller)) { current.target.push(null) continue } const projected: AuthorizedDescendant = { record: current.node.session, descendants: [], } current.target.push(projected) for (const child of [...current.node.descendants].reverse()) { pending = { node: child, target: projected.descendants, next: pending, } } } return result } function * visitDescendants( nodes: readonly (AuthorizedDescendant | null)[], ): Generator { let pending: DescendantVisit | undefined for (const node of [...nodes].reverse()) { pending = { node, depth: 0, next: pending } } while (pending !== undefined) { const current = pending pending = current.next yield current if (current.node === null) continue for (const child of [...current.node.descendants].reverse()) { pending = { node: child, depth: current.depth + 1, next: pending, } } } } function descendantIds(nodes: readonly (AuthorizedDescendant | null)[]): SessionIdValue[] { const ids: SessionIdValue[] = [] for (const { node } of visitDescendants(nodes)) { if (node !== null) ids.push(node.record.header.id) } return ids } function titleText(view: TitleView): string { return view.unavailableCode === undefined ? view.text : `${view.text} (title unavailable: ${view.unavailableCode})` } /** Workspace-scoped caller authorization, title access, and lineage projection. */ export const workspaceAccess = { callerOf, targetId, authorizeTarget, recordAuthorized, assertObservedTargetAuthorized, authorizeSessionIds, readTitles, readTitle, authorizeDescendants, visitDescendants, descendantIds, titleText, }