/** * @license * Copyright 2026 Qwen * SPDX-License-Identifier: Apache-2.0 */ /** * Short-handle registries. The realtime model refers to sessions as * `session_N`, jobs as `job_N`, and screen-capture assets as `asset_N` — * short, speakable, and hard to mistranscribe. These maps own the * bidirectional translation to the real identities. */ import type { BackendHandle } from '../adaptor/types.js'; export interface JobRecord { jobHandle: string; sessionHandle: string; backend: BackendHandle; /** Adaptor-side correlation id (qwen serve: promptId). */ jobRef?: string; state: 'accepted' | 'running' | 'done' | 'failed' | 'cancelled' | 'interrupted'; task: string; createdAt: number; } export interface AssetRecord { assetHandle: string; path: string; mimeType: string; createdAt: number; } export declare class HandleRegistry { private readonly sessions; private readonly sessionsByBackendId; /** Sessions whose backend reported closure — handles no longer resolve. */ private readonly closedSessions; private readonly jobs; /** * Keyed by `${backend.adaptor}:${jobRef}` — jobRefs are adaptor-local * (qwen serve mints prompt UUIDs, an ACP adaptor counts turn-1, turn-2), * so the owning adaptor scopes the key or two backends collide. */ private readonly jobsByRef; private readonly assets; private sessionSeq; private jobSeq; private assetSeq; private deliverySeq; private readonly deliveries; /** Receipt handles are deliberately separate from jobs and their counters. */ delivery(adaptor: string, id: string): string; retainDeliveries(records: ReadonlyArray<{ adaptor: string; id: string; }>): void; resolveDelivery(handle: string): { adaptor: string; id: string; } | undefined; /** Register (or return the existing handle for) a backend session. */ session(backend: BackendHandle): string; resolveSession(handle: string): BackendHandle | undefined; /** Mark a session closed: its handle stops resolving from now on. */ closeSession(handle: string): void; /** * Register a new job. Idempotent by (backend, jobRef): a jobRef already * registered for that backend means the backend attributed the prompt to * that existing turn (e.g. a steer that joined it), so the existing record * is returned instead of minting a second job that would orphan the first * in 'running'. */ createJob(fields: { sessionHandle: string; backend: BackendHandle; jobRef?: string; task: string; }): JobRecord; resolveJob(handle: string): JobRecord | undefined; /** Full owned active-job set, not a paginated UI projection. Aliases share * one record and must not count as separate cancellation candidates. */ activeJobs(): readonly JobRecord[]; /** Bind an exact join acknowledgement while preserving any promised alias. */ bindJoinedJob(handle: string, backend: BackendHandle, jobRef: string): JobRecord | undefined; /** Find the job a backend event's jobRef (on its backend) belongs to. */ jobByRef(backend: BackendHandle, jobRef: string): JobRecord | undefined; /** * Reconcile a session's non-terminal jobs against reality: the backend * reports idle, so any 'accepted'/'running' record left over from a * terminal event lost on transport interruption becomes 'interrupted'. Gated on * the caller's isBusy check so a genuinely busy backend is never * touched. */ reconcileIdleSession(sessionHandle: string): JobRecord[]; /** The most recent non-terminal job for a session, if any. */ activeJobForSession(sessionHandle: string): JobRecord | undefined; registerAsset(fields: { path: string; mimeType: string; }): AssetRecord; resolveAsset(handle: string): AssetRecord | undefined; }