diff --git a/src/plugin-sdk/qa-channel.ts b/src/plugin-sdk/qa-channel.ts index ba7afe2ae726..1cf1de749d0f 100644 --- a/src/plugin-sdk/qa-channel.ts +++ b/src/plugin-sdk/qa-channel.ts @@ -97,53 +97,69 @@ function loadFacadeModule(): FacadeModule { }); } +/** Build a QA bus target string from conversation and optional thread parts. */ export const buildQaTarget: FacadeModule["buildQaTarget"] = ((...args) => loadFacadeModule().buildQaTarget(...args)) as FacadeModule["buildQaTarget"]; +/** Format a QA bus target string for display and CLI output. */ export const formatQaTarget: FacadeModule["buildQaTarget"] = ((...args) => loadFacadeModule().buildQaTarget(...args)) as FacadeModule["buildQaTarget"]; +/** Create a QA bus thread through the bundled QA channel facade. */ export const createQaBusThread: FacadeModule["createQaBusThread"] = ((...args) => loadFacadeModule().createQaBusThread(...args)) as FacadeModule["createQaBusThread"]; +/** Delete a QA bus message through the bundled QA channel facade. */ export const deleteQaBusMessage: FacadeModule["deleteQaBusMessage"] = ((...args) => loadFacadeModule().deleteQaBusMessage(...args)) as FacadeModule["deleteQaBusMessage"]; +/** Edit a QA bus message through the bundled QA channel facade. */ export const editQaBusMessage: FacadeModule["editQaBusMessage"] = ((...args) => loadFacadeModule().editQaBusMessage(...args)) as FacadeModule["editQaBusMessage"]; +/** Read the current QA bus state snapshot. */ export const getQaBusState: FacadeModule["getQaBusState"] = ((...args) => loadFacadeModule().getQaBusState(...args)) as FacadeModule["getQaBusState"]; +/** Inject an inbound QA bus message for channel and gateway tests. */ export const injectQaBusInboundMessage: FacadeModule["injectQaBusInboundMessage"] = ((...args) => loadFacadeModule().injectQaBusInboundMessage( ...args, )) as FacadeModule["injectQaBusInboundMessage"]; +/** Normalize a user-provided QA target string when possible. */ export const normalizeQaTarget: FacadeModule["normalizeQaTarget"] = ((...args) => loadFacadeModule().normalizeQaTarget(...args)) as FacadeModule["normalizeQaTarget"]; +/** Parse a QA target string into chat type, conversation id, and optional thread id. */ export const parseQaTarget: FacadeModule["parseQaTarget"] = ((...args) => loadFacadeModule().parseQaTarget(...args)) as FacadeModule["parseQaTarget"]; +/** Poll the QA bus for new messages from a cursor. */ export const pollQaBus: FacadeModule["pollQaBus"] = ((...args) => loadFacadeModule().pollQaBus(...args)) as FacadeModule["pollQaBus"]; +/** Lazy QA channel plugin object used by plugin loader tests. */ export const qaChannelPlugin: FacadeModule["qaChannelPlugin"] = createLazyFacadeObjectValue( () => loadFacadeModule().qaChannelPlugin, ); +/** Add a reaction to a QA bus message. */ export const reactToQaBusMessage: FacadeModule["reactToQaBusMessage"] = ((...args) => loadFacadeModule().reactToQaBusMessage(...args)) as FacadeModule["reactToQaBusMessage"]; +/** Read one QA bus message by id. */ export const readQaBusMessage: FacadeModule["readQaBusMessage"] = ((...args) => loadFacadeModule().readQaBusMessage(...args)) as FacadeModule["readQaBusMessage"]; +/** Search QA bus messages using the bundled channel facade. */ export const searchQaBusMessages: FacadeModule["searchQaBusMessages"] = ((...args) => loadFacadeModule().searchQaBusMessages(...args)) as FacadeModule["searchQaBusMessages"]; +/** Send an outbound QA bus message with optional attachments and tool calls. */ export const sendQaBusMessage: FacadeModule["sendQaBusMessage"] = ((...args) => loadFacadeModule().sendQaBusMessage(...args)) as FacadeModule["sendQaBusMessage"]; +/** Install a test runtime implementation into the bundled QA channel facade. */ export const setQaChannelRuntime: FacadeModule["setQaChannelRuntime"] = ((...args) => loadFacadeModule().setQaChannelRuntime(...args)) as FacadeModule["setQaChannelRuntime"]; diff --git a/src/plugin-sdk/qa-runtime.ts b/src/plugin-sdk/qa-runtime.ts index 85d8ebac905c..8dab6c79525b 100644 --- a/src/plugin-sdk/qa-runtime.ts +++ b/src/plugin-sdk/qa-runtime.ts @@ -30,6 +30,7 @@ function isMissingQaRuntimeError(error: unknown) { ); } +/** Load the bundled QA lab runtime surface, throwing when the private bundle is absent. */ export function loadQaRuntimeModule(): QaRuntimeSurface { const env = resolvePrivateQaBundledPluginsEnv(); return loadBundledPluginPublicSurfaceModuleSync({ @@ -39,6 +40,7 @@ export function loadQaRuntimeModule(): QaRuntimeSurface { }); } +/** Check whether the bundled QA lab runtime surface is present without hiding other load errors. */ export function isQaRuntimeAvailable(): boolean { try { loadQaRuntimeModule(); @@ -51,6 +53,7 @@ export function isQaRuntimeAvailable(): boolean { } } +/** Normalized options passed from live-transport QA CLIs into lane runners. */ export type LiveTransportQaCommandOptions = { repoRoot?: string; outputDir?: string; @@ -85,16 +88,19 @@ type LiveTransportQaCommanderOptions = { credentialRole?: string; }; +/** Commander registration hook for one live-transport QA subcommand. */ export type LiveTransportQaCliRegistration = { commandName: string; register(qa: Command): void; }; +/** Help text customizations for live credential source and role flags. */ export type LiveTransportQaCredentialCliOptions = { sourceDescription?: string; roleDescription?: string; }; +/** Declarative command metadata and runner used to install a live-transport QA CLI. */ export type LiveTransportQaCliRegistrationOptions = { commandName: string; credentialOptions?: LiveTransportQaCredentialCliOptions; @@ -111,6 +117,7 @@ export type LiveTransportQaCliRegistrationOptions = { run: (opts: LiveTransportQaCommandOptions) => Promise; }; +/** Memoize a lazy CLI runtime import so repeated command paths share one loaded module. */ export function createLazyCliRuntimeLoader(load: () => Promise) { let promise: Promise | null = null; return async () => { @@ -195,6 +202,7 @@ function registerLiveTransportQaCli( }); } +/** Build a Commander registration object for one live-transport QA command. */ export function createLiveTransportQaCliRegistration( params: LiveTransportQaCliRegistrationOptions, ): LiveTransportQaCliRegistration { @@ -209,12 +217,14 @@ export function createLiveTransportQaCliRegistration( }; } +/** One top-level check row in a rendered QA markdown report. */ export type QaReportCheck = { name: string; status: "pass" | "fail" | "skip"; details?: string; }; +/** One scenario section in a rendered QA markdown report. */ export type QaReportScenario = { name: string; status: "pass" | "fail" | "skip"; @@ -231,12 +241,14 @@ export { type LiveTransportStandardScenarioId, } from "./qa-live-transport-scenarios.js"; +/** Docker command runner abstraction used by QA Docker helpers and tests. */ export type QaDockerRunCommand = ( command: string, args: string[], cwd: string, ) => Promise<{ stdout: string; stderr: string }>; +/** Minimal fetch-like health probe used by QA Docker runtime helpers. */ export type QaDockerFetchLike = (input: string) => Promise<{ ok: boolean }>; const DEFAULT_QA_DOCKER_COMMAND_TIMEOUT_MS = 120_000; @@ -250,6 +262,7 @@ function pushQaReportDetailsBlock(lines: string[], label: string, details: strin lines.push("", "```text", details, "```"); } +/** Render checks, scenarios, timeline, and notes into the standard QA markdown report format. */ export function renderQaMarkdownReport(params: { title: string; startedAt: Date; @@ -329,10 +342,12 @@ export function renderQaMarkdownReport(params: { return lines.join("\n"); } +/** Append a formatted live-lane issue while preserving the caller-owned issue list. */ export function appendQaLiveLaneIssue(issues: string[], label: string, error: unknown) { issues.push(`${label}: ${formatErrorMessage(error)}`); } +/** Format a live-lane failure message that includes artifact labels and paths. */ export function buildQaLiveLaneArtifactsError(params: { heading: string; artifacts: Record; @@ -346,6 +361,7 @@ export function buildQaLiveLaneArtifactsError(params: { ].join("\n"); } +/** Print live-transport QA artifact paths with a lane label for CI log parsers. */ export function printLiveTransportQaArtifacts( laneLabel: string, artifacts: Record, @@ -397,6 +413,7 @@ async function findFreeQaDockerPort() { }); } +/** Return the preferred Docker host port unless it is unpinned and already occupied. */ export async function resolveQaDockerHostPort(preferredPort: number, pinned: boolean) { if (pinned || (await isQaDockerPortFree(preferredPort))) { return preferredPort; @@ -471,6 +488,7 @@ async function isQaDockerHealthy(url: string, fetchImpl: QaDockerFetchLike) { } } +/** Create Docker command, health-check, and compose helpers for QA harnesses. */ export function createQaDockerRuntime(params: { auditContext: string; commandTimeoutMs?: number | null; @@ -639,6 +657,7 @@ export function createQaDockerRuntime(params: { type ProcessWriteCallback = (err?: Error | null) => void; +/** Tee stdout and stderr into a private artifact file until the returned stop hook runs. */ export async function startLiveTransportQaOutputTee(params: { fileName: string; outputDir: string;