From e112fb939a9b88fecedfae6e178bea680b74cf15 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 05:31:15 -0400 Subject: [PATCH] docs: document acp translator bridge --- src/acp/translator.bridge-test-helpers.ts | 7 +++++++ src/acp/translator.cancel-scoping.test.ts | 1 + src/acp/translator.error-kind.test.ts | 1 + src/acp/translator.event-ledger.test.ts | 1 + src/acp/translator.final-snapshots.test.ts | 1 + src/acp/translator.lifecycle.test.ts | 1 + src/acp/translator.permission-relay.test.ts | 1 + src/acp/translator.presentation.ts | 7 +++++++ src/acp/translator.prompt-harness.test-support.ts | 6 ++++++ src/acp/translator.prompt-prefix.test.ts | 1 + src/acp/translator.prompt-size.test.ts | 1 + src/acp/translator.replay.test.ts | 1 + src/acp/translator.session-config.test.ts | 1 + src/acp/translator.session-lineage-meta.test.ts | 1 + src/acp/translator.session-list.test.ts | 1 + src/acp/translator.session-list.ts | 6 ++++++ src/acp/translator.session-rate-limit.test.ts | 1 + src/acp/translator.session-setup.test.ts | 1 + src/acp/translator.session-snapshot.test.ts | 1 + src/acp/translator.session-updates.ts | 3 +++ src/acp/translator.set-session-mode.test.ts | 1 + src/acp/translator.stop-reason.test.ts | 1 + src/acp/translator.test-helpers.ts | 2 ++ src/acp/translator.tool-streaming.test.ts | 1 + src/acp/translator.ts | 2 ++ src/acp/types.ts | 1 + 26 files changed, 52 insertions(+) diff --git a/src/acp/translator.bridge-test-helpers.ts b/src/acp/translator.bridge-test-helpers.ts index e357e2cc3d16..88b2bcbca4c7 100644 --- a/src/acp/translator.bridge-test-helpers.ts +++ b/src/acp/translator.bridge-test-helpers.ts @@ -1,3 +1,4 @@ +/** Shared ACP translator bridge request/event fixtures and assertions. */ import type { LoadSessionRequest, NewSessionRequest, @@ -21,6 +22,7 @@ export function createNewSessionRequest(cwd = "/tmp"): NewSessionRequest { } as unknown as NewSessionRequest; } +/** Builds a minimal ACP load-session request for translator tests. */ export function createLoadSessionRequest(sessionId: string, cwd = "/tmp"): LoadSessionRequest { return { sessionId, @@ -30,6 +32,7 @@ export function createLoadSessionRequest(sessionId: string, cwd = "/tmp"): LoadS } as unknown as LoadSessionRequest; } +/** Builds a minimal ACP prompt request for translator tests. */ export function createPromptRequest( sessionId: string, text: string, @@ -42,6 +45,7 @@ export function createPromptRequest( } as unknown as PromptRequest; } +/** Builds a minimal ACP set-session-mode request for translator tests. */ export function createSetSessionModeRequest( sessionId: string, modeId: string, @@ -53,6 +57,7 @@ export function createSetSessionModeRequest( } as unknown as SetSessionModeRequest; } +/** Builds a minimal ACP set-session-config-option request for translator tests. */ export function createSetSessionConfigOptionRequest( sessionId: string, configId: string, @@ -66,6 +71,7 @@ export function createSetSessionConfigOptionRequest( } as unknown as SetSessionConfigOptionRequest; } +/** Builds a Gateway tool event fixture for translator tests. */ export function createToolEvent(params: { sessionKey: string; phase: "start" | "update" | "result"; @@ -94,6 +100,7 @@ export function createToolEvent(params: { } as unknown as EventFrame; } +/** Builds a Gateway final chat event fixture for translator tests. */ export function createChatFinalEvent(sessionKey: string): EventFrame { return { event: "chat", diff --git a/src/acp/translator.cancel-scoping.test.ts b/src/acp/translator.cancel-scoping.test.ts index 266714d12b79..79cb365ecbdb 100644 --- a/src/acp/translator.cancel-scoping.test.ts +++ b/src/acp/translator.cancel-scoping.test.ts @@ -1,3 +1,4 @@ +/** Tests prompt cancellation scoping across concurrent ACP sessions and Gateway runs. */ import type { CancelNotification, PromptRequest, PromptResponse } from "@agentclientprotocol/sdk"; import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; diff --git a/src/acp/translator.error-kind.test.ts b/src/acp/translator.error-kind.test.ts index 87e5cdf3a2d8..84af34e8750d 100644 --- a/src/acp/translator.error-kind.test.ts +++ b/src/acp/translator.error-kind.test.ts @@ -1,3 +1,4 @@ +/** Tests Gateway errorKind to ACP stopReason mapping. */ import { describe, expect, it } from "vitest"; import { createChatEvent, diff --git a/src/acp/translator.event-ledger.test.ts b/src/acp/translator.event-ledger.test.ts index c4d1298ef6ab..f8f0b74d952b 100644 --- a/src/acp/translator.event-ledger.test.ts +++ b/src/acp/translator.event-ledger.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator replay ledger recording and load-session replay behavior. */ import type { LoadSessionRequest, NewSessionRequest, diff --git a/src/acp/translator.final-snapshots.test.ts b/src/acp/translator.final-snapshots.test.ts index fd7e09d3e719..965e3ce4a3eb 100644 --- a/src/acp/translator.final-snapshots.test.ts +++ b/src/acp/translator.final-snapshots.test.ts @@ -1,3 +1,4 @@ +/** Tests final Gateway snapshots are emitted before ACP prompt resolution. */ import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; import type { EventFrame } from "../../packages/gateway-protocol/src/index.js"; diff --git a/src/acp/translator.lifecycle.test.ts b/src/acp/translator.lifecycle.test.ts index 25c46ec29fdf..cc948bfacdcb 100644 --- a/src/acp/translator.lifecycle.test.ts +++ b/src/acp/translator.lifecycle.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator initialize/session lifecycle and prompt bridge behavior. */ import type { CloseSessionRequest, InitializeRequest, diff --git a/src/acp/translator.permission-relay.test.ts b/src/acp/translator.permission-relay.test.ts index 35eebd0fcdca..5c64da573d5e 100644 --- a/src/acp/translator.permission-relay.test.ts +++ b/src/acp/translator.permission-relay.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator permission relay for Gateway exec approvals. */ import type { CancelNotification } from "@agentclientprotocol/sdk"; import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; diff --git a/src/acp/translator.presentation.ts b/src/acp/translator.presentation.ts index 3715b91abcca..83a3c9cf4633 100644 --- a/src/acp/translator.presentation.ts +++ b/src/acp/translator.presentation.ts @@ -1,3 +1,4 @@ +/** Builds ACP session presentation, metadata, usage, and config-option snapshots. */ import type { InitializeRequest, SessionConfigOption, @@ -23,6 +24,7 @@ export const ACP_ELEVATED_LEVEL_CONFIG_ID = "elevated_level"; export const ACP_TIMEOUT_CONFIG_ID = "timeout"; export const ACP_TIMEOUT_SECONDS_CONFIG_ID = "timeout_seconds"; +/** Normalized ACP client capability flags used to choose session controls. */ export type ClientCapabilityState = { readTextFile: boolean; writeTextFile: boolean; @@ -61,27 +63,32 @@ export type GatewaySessionPresentationRow = Pick< | "contextTokens" >; +/** ACP session controls and modes shown to the client. */ export type SessionPresentation = { configOptions: SessionConfigOption[]; modes: SessionModeState; }; +/** ACP session metadata plus lineage information. */ export type SessionMetadata = { title?: string | null; updatedAt?: string | null; _meta?: AcpSessionLineageMeta; }; +/** Context/token usage snapshot for ACP clients that expose progress meters. */ export type SessionUsageSnapshot = { size: number; used: number; }; +/** Full session snapshot sent after load/list/prompt completion. */ export type SessionSnapshot = SessionPresentation & { metadata?: SessionMetadata; usage?: SessionUsageSnapshot; }; +/** Normalizes optional ACP initialize capabilities into stable booleans. */ export function normalizeClientCapabilities( capabilities: InitializeRequest["clientCapabilities"] | undefined, ): ClientCapabilityState { diff --git a/src/acp/translator.prompt-harness.test-support.ts b/src/acp/translator.prompt-harness.test-support.ts index 5ac2d2aa97cc..bcd131e2cf9d 100644 --- a/src/acp/translator.prompt-harness.test-support.ts +++ b/src/acp/translator.prompt-harness.test-support.ts @@ -1,3 +1,4 @@ +/** Prompt harness helpers for ACP translator lifecycle/cancel/stop-reason tests. */ import type { PromptRequest } from "@agentclientprotocol/sdk"; import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { expect, vi } from "vitest"; @@ -17,6 +18,7 @@ const DEFAULT_SESSION_ID = "session-1"; export const DEFAULT_SESSION_KEY = "agent:main:main"; const DEFAULT_PROMPT_TEXT = "hello"; +/** Creates an ACP translator instance with one preloaded session. */ export function createSessionAgentHarness( request: GatewayClient["request"], options: { sessionId?: string; sessionKey?: string; cwd?: string } = {}, @@ -41,6 +43,7 @@ export function createSessionAgentHarness( }; } +/** Starts a prompt against a translator test agent. */ export function promptAgent( agent: AcpGatewayAgent, sessionId = DEFAULT_SESSION_ID, @@ -53,6 +56,7 @@ export function promptAgent( } as unknown as PromptRequest); } +/** Observes prompt promise settlement without awaiting it immediately. */ export function observeSettlement(promise: ReturnType) { const settleSpy = vi.fn(); void promise.then( @@ -62,6 +66,7 @@ export function observeSettlement(promise: ReturnType return settleSpy; } +/** Starts a prompt that remains pending until tests inject Gateway events. */ export async function createPendingPromptHarness(): Promise { let runId: string | undefined; const request = vi.fn(async (method: string, params?: Record) => { @@ -86,6 +91,7 @@ export async function createPendingPromptHarness(): Promise): EventFrame { return { type: "event", diff --git a/src/acp/translator.prompt-prefix.test.ts b/src/acp/translator.prompt-prefix.test.ts index 416e071cbc25..162db7553b37 100644 --- a/src/acp/translator.prompt-prefix.test.ts +++ b/src/acp/translator.prompt-prefix.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP prompt cwd-prefix provenance behavior. */ import os from "node:os"; import path from "node:path"; import type { PromptRequest } from "@agentclientprotocol/sdk"; diff --git a/src/acp/translator.prompt-size.test.ts b/src/acp/translator.prompt-size.test.ts index a86159f9e6dd..e95f13ccfd57 100644 --- a/src/acp/translator.prompt-size.test.ts +++ b/src/acp/translator.prompt-size.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator prompt-size hardening. */ import { describe, it, vi } from "vitest"; import { expectOversizedPromptRejected } from "./translator.bridge-test-helpers.js"; diff --git a/src/acp/translator.replay.test.ts b/src/acp/translator.replay.test.ts index f6bdab1982ee..b27392db2e5c 100644 --- a/src/acp/translator.replay.test.ts +++ b/src/acp/translator.replay.test.ts @@ -1,3 +1,4 @@ +/** Tests transcript replay conversion into ACP session update chunks. */ import { describe, expect, it } from "vitest"; import { extractReplayChunks } from "./translator.replay.js"; diff --git a/src/acp/translator.session-config.test.ts b/src/acp/translator.session-config.test.ts index 3b338df837e0..907d3efc36df 100644 --- a/src/acp/translator.session-config.test.ts +++ b/src/acp/translator.session-config.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP setSessionMode and setSessionConfigOption Gateway bridge behavior. */ import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; import type { GatewayClient } from "../gateway/client.js"; diff --git a/src/acp/translator.session-lineage-meta.test.ts b/src/acp/translator.session-lineage-meta.test.ts index 87c8e46264de..b350e7b77f6e 100644 --- a/src/acp/translator.session-lineage-meta.test.ts +++ b/src/acp/translator.session-lineage-meta.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP session lineage metadata in list/load session responses. */ import type { ListSessionsRequest, LoadSessionRequest } from "@agentclientprotocol/sdk"; import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; diff --git a/src/acp/translator.session-list.test.ts b/src/acp/translator.session-list.test.ts index 5f3dce442168..f862eda39e29 100644 --- a/src/acp/translator.session-list.test.ts +++ b/src/acp/translator.session-list.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator session-list cursor and page-size helpers. */ import { describe, expect, it } from "vitest"; import { ACP_LIST_SESSIONS_MAX_FETCH_LIMIT, diff --git a/src/acp/translator.session-list.ts b/src/acp/translator.session-list.ts index 6c0e04ae8c77..38bbd8c12e51 100644 --- a/src/acp/translator.session-list.ts +++ b/src/acp/translator.session-list.ts @@ -1,3 +1,4 @@ +/** Cursor and pagination helpers for ACP session/list requests. */ import path from "node:path"; import { readNumber } from "@openclaw/acp-core/meta"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; @@ -10,15 +11,18 @@ const ACP_LIST_SESSIONS_MAX_CURSOR_OFFSET = 10_000; export const ACP_LIST_SESSIONS_MAX_FETCH_LIMIT = ACP_LIST_SESSIONS_MAX_CURSOR_OFFSET + ACP_LIST_SESSIONS_MAX_PAGE_SIZE + 1; +/** Opaque cursor payload used to page ACP session list results. */ export type ListSessionsCursor = { offset: number; cwd?: string; }; +/** Encodes an ACP session-list cursor as base64url JSON. */ export function encodeListSessionsCursor(cursor: ListSessionsCursor): string { return Buffer.from(JSON.stringify({ v: 1, ...cursor }), "utf8").toString("base64url"); } +/** Decodes and validates an ACP session-list cursor, defaulting to the first page. */ export function decodeListSessionsCursor(value: string | null | undefined): ListSessionsCursor { if (!value) { return { offset: 0 }; @@ -51,12 +55,14 @@ export function decodeListSessionsCursor(value: string | null | undefined): List }; } +/** Throws when an ACP method receives a relative cwd filter/path. */ export function assertAbsoluteCwd(cwd: string, method: string): void { if (!path.isAbsolute(cwd)) { throw new Error(`ACP ${method} requires an absolute cwd.`); } } +/** Resolves requested ACP session-list page size with bridge limits. */ export function resolveListSessionsPageSize( meta: Record | null | undefined, ): number { diff --git a/src/acp/translator.session-rate-limit.test.ts b/src/acp/translator.session-rate-limit.test.ts index 40ffc2ee153d..09c3e89c61e3 100644 --- a/src/acp/translator.session-rate-limit.test.ts +++ b/src/acp/translator.session-rate-limit.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator session creation rate limiting. */ import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; import { diff --git a/src/acp/translator.session-setup.test.ts b/src/acp/translator.session-setup.test.ts index 871ccba575d8..7f517cf02c92 100644 --- a/src/acp/translator.session-setup.test.ts +++ b/src/acp/translator.session-setup.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP translator session setup constraints and initial updates. */ import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; import type { GatewayClient } from "../gateway/client.js"; diff --git a/src/acp/translator.session-snapshot.test.ts b/src/acp/translator.session-snapshot.test.ts index 8ccd42206272..dd4a63e826fc 100644 --- a/src/acp/translator.session-snapshot.test.ts +++ b/src/acp/translator.session-snapshot.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP session metadata and usage snapshots after prompts. */ import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; import type { GatewayClient } from "../gateway/client.js"; diff --git a/src/acp/translator.session-updates.ts b/src/acp/translator.session-updates.ts index 77dfdc05b836..883558e74af3 100644 --- a/src/acp/translator.session-updates.ts +++ b/src/acp/translator.session-updates.ts @@ -1,3 +1,4 @@ +/** Emits ACP session updates and mirrors replayable updates into the event ledger. */ import type { AgentSideConnection, AvailableCommand, @@ -6,6 +7,7 @@ import type { } from "@agentclientprotocol/sdk"; import type { AcpEventLedger, AcpEventLedgerReplay } from "./event-ledger.js"; +/** Session identity used when emitting and recording ACP translator updates. */ export type AcpTranslatorSessionRef = { sessionId: string; sessionKey: string; @@ -28,6 +30,7 @@ function resolveLedgerSessionId(session: { sessionId: string; ledgerSessionId?: return session.ledgerSessionId ?? session.sessionId; } +/** Helper that keeps ACP client updates and replay ledger writes in sync. */ export class AcpTranslatorSessionUpdates { constructor(private options: AcpTranslatorSessionUpdatesOptions) {} diff --git a/src/acp/translator.set-session-mode.test.ts b/src/acp/translator.set-session-mode.test.ts index 944c43fd936b..1d6204922b25 100644 --- a/src/acp/translator.set-session-mode.test.ts +++ b/src/acp/translator.set-session-mode.test.ts @@ -1,3 +1,4 @@ +/** Tests ACP setSessionMode request translation and error propagation. */ import type { SetSessionModeRequest } from "@agentclientprotocol/sdk"; import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it } from "vitest"; diff --git a/src/acp/translator.stop-reason.test.ts b/src/acp/translator.stop-reason.test.ts index 38d19a52985a..3a2e1e698105 100644 --- a/src/acp/translator.stop-reason.test.ts +++ b/src/acp/translator.stop-reason.test.ts @@ -1,3 +1,4 @@ +/** Tests Gateway final/error states to ACP prompt stopReason mapping. */ import type { PromptRequest } from "@agentclientprotocol/sdk"; import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; diff --git a/src/acp/translator.test-helpers.ts b/src/acp/translator.test-helpers.ts index dd7ee8d86134..209a07b84188 100644 --- a/src/acp/translator.test-helpers.ts +++ b/src/acp/translator.test-helpers.ts @@ -1,3 +1,4 @@ +/** Shared mocked ACP connection and Gateway client helpers for translator tests. */ import type { AgentSideConnection } from "@agentclientprotocol/sdk"; import { vi } from "vitest"; import type { GatewayClient } from "../gateway/client.js"; @@ -24,6 +25,7 @@ export function createAcpConnection( } as unknown as TestAcpConnection; } +/** Creates a mocked Gateway client for translator tests. */ export function createAcpGateway( request: GatewayClient["request"] = vi.fn(async () => ({ ok: true })) as GatewayClient["request"], ): GatewayClient { diff --git a/src/acp/translator.tool-streaming.test.ts b/src/acp/translator.tool-streaming.test.ts index 3c15888cd390..8489fb12d72d 100644 --- a/src/acp/translator.tool-streaming.test.ts +++ b/src/acp/translator.tool-streaming.test.ts @@ -1,3 +1,4 @@ +/** Tests Gateway tool streaming to ACP tool-call update mapping. */ import { createInMemorySessionStore } from "@openclaw/acp-core/session"; import { describe, expect, it, vi } from "vitest"; import type { GatewayClient } from "../gateway/client.js"; diff --git a/src/acp/translator.ts b/src/acp/translator.ts index 236c7de6a4c4..786abfc40a39 100644 --- a/src/acp/translator.ts +++ b/src/acp/translator.ts @@ -1,3 +1,4 @@ +/** Agent Client Protocol bridge that translates ACP sessions/prompts to Gateway chat sessions. */ import { randomUUID } from "node:crypto"; import os from "node:os"; import type { @@ -222,6 +223,7 @@ function hasExplicitSessionRouting( ); } +/** ACP Agent implementation backed by the OpenClaw Gateway and replay ledger. */ export class AcpGatewayAgent implements Agent { private connection: AgentSideConnection; private gateway: GatewayClient; diff --git a/src/acp/types.ts b/src/acp/types.ts index 0772827178c6..8602b11f69ef 100644 --- a/src/acp/types.ts +++ b/src/acp/types.ts @@ -1,3 +1,4 @@ +/** ACP server option re-exports and OpenClaw agent identity metadata. */ export type { AcpProvenanceMode, AcpServerOptions, AcpSession } from "@openclaw/acp-core/types"; export { normalizeAcpProvenanceMode } from "@openclaw/acp-core/types"; import { VERSION } from "../version.js";