From 53d08d4aefecc9ea843befd0f531a2be2d42793c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 06:23:32 -0400 Subject: [PATCH] docs: document agent helper contracts --- src/agents/bootstrap-cache.test.ts | 4 ++++ src/agents/bootstrap-cache.ts | 11 +++++++++++ src/agents/cli-session.test.ts | 4 ++++ src/agents/cli-session.ts | 7 +++++-- src/agents/identity-file.test.ts | 4 ++++ src/agents/identity-file.ts | 9 +++++++-- src/agents/identity.test.ts | 4 ++++ src/agents/identity.ts | 12 ++++++++++++ src/agents/model-catalog-browse.test.ts | 4 ++++ src/agents/model-catalog-visibility.test.ts | 4 ++++ src/agents/model-catalog.types.ts | 8 ++++++-- src/agents/model-discovery-context.ts | 7 +++++-- src/agents/pty-dsr.ts | 5 +++++ src/agents/pty-keys.test.ts | 4 ++++ src/agents/run-wait.test.ts | 4 ++++ src/agents/run-wait.ts | 15 +++++++++++++++ src/agents/workspace.bootstrap-cache.test.ts | 4 ++++ 17 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/agents/bootstrap-cache.test.ts b/src/agents/bootstrap-cache.test.ts index f458566af6b6..43489bd604a9 100644 --- a/src/agents/bootstrap-cache.test.ts +++ b/src/agents/bootstrap-cache.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for per-session workspace bootstrap caching. + * Verifies reuse, refresh, pruning, and explicit cache clears. + */ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { WorkspaceBootstrapFile } from "./workspace.js"; diff --git a/src/agents/bootstrap-cache.ts b/src/agents/bootstrap-cache.ts index 250024475114..db5a7a073935 100644 --- a/src/agents/bootstrap-cache.ts +++ b/src/agents/bootstrap-cache.ts @@ -1,3 +1,8 @@ +/** + * Per-session workspace bootstrap snapshot cache. + * Reuses unchanged bootstrap file arrays while refreshing each turn so edits + * become visible to long-lived agent sessions. + */ import { loadWorkspaceBootstrapFiles, type WorkspaceBootstrapFile } from "./workspace.js"; type BootstrapSnapshot = { @@ -38,6 +43,7 @@ function pruneOldestBootstrapSnapshots(): void { } } +/** Load bootstrap files for a session, reusing the prior snapshot when content is unchanged. */ export async function getOrLoadBootstrapFiles(params: { workspaceDir: string; sessionKey: string; @@ -62,18 +68,22 @@ export async function getOrLoadBootstrapFiles(params: { return files; } +/** Test helper exposing the bounded snapshot cache size. */ export function getBootstrapSnapshotCacheSizeForTest(): number { return cache.size; } +/** Test helper for asserting one session snapshot is cached. */ export function hasBootstrapSnapshotForTest(sessionKey: string): boolean { return cache.has(sessionKey); } +/** Drop one cached bootstrap snapshot. */ export function clearBootstrapSnapshot(sessionKey: string): void { cache.delete(sessionKey); } +/** Clear bootstrap state when a visible session rolls over to a new backing session. */ export function clearBootstrapSnapshotOnSessionRollover(params: { sessionKey?: string; previousSessionId?: string; @@ -85,6 +95,7 @@ export function clearBootstrapSnapshotOnSessionRollover(params: { clearBootstrapSnapshot(params.sessionKey); } +/** Clear all cached bootstrap snapshots. */ export function clearAllBootstrapSnapshots(): void { cache.clear(); } diff --git a/src/agents/cli-session.test.ts b/src/agents/cli-session.test.ts index f04e0b35ccf1..38335bc0bac5 100644 --- a/src/agents/cli-session.test.ts +++ b/src/agents/cli-session.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for CLI session persistence helpers. + * Verifies provider-keyed bindings, legacy Claude state, and reuse invalidation. + */ import { describe, expect, it } from "vitest"; import type { SessionEntry } from "../config/sessions.js"; import { diff --git a/src/agents/cli-session.ts b/src/agents/cli-session.ts index c2934f293307..eb7dd54755ab 100644 --- a/src/agents/cli-session.ts +++ b/src/agents/cli-session.ts @@ -1,10 +1,13 @@ +/** + * CLI session persistence helpers. + * Keeps provider-keyed session bindings, reuse fingerprints, and legacy + * Claude CLI state in one normalized session-store contract. + */ import crypto from "node:crypto"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import type { CliSessionBinding, SessionEntry } from "../config/sessions.js"; import { normalizeProviderId } from "./model-selection.js"; -// CLI-backed agents persist reusable provider session IDs in the session store. -// These helpers keep legacy Claude-only state and provider-keyed bindings aligned. const CLAUDE_CLI_BACKEND_ID = "claude-cli"; /** Hash CLI session-sensitive text so reuse checks can compare stable fingerprints. */ diff --git a/src/agents/identity-file.test.ts b/src/agents/identity-file.test.ts index eb63370b57f4..421565224c43 100644 --- a/src/agents/identity-file.test.ts +++ b/src/agents/identity-file.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for IDENTITY.md parsing and merging. + * Ensures placeholders are ignored and rich identity fields stay stable. + */ import { describe, expect, it } from "vitest"; import { mergeIdentityMarkdownContent, parseIdentityMarkdown } from "./identity-file.js"; diff --git a/src/agents/identity-file.ts b/src/agents/identity-file.ts index 9a3d177b72cf..e6131260d4b5 100644 --- a/src/agents/identity-file.ts +++ b/src/agents/identity-file.ts @@ -1,10 +1,14 @@ +/** + * IDENTITY.md parsing and writing support. + * The parser accepts human-authored markdown, while the writer only updates + * stable rich identity fields. + */ import fs from "node:fs"; import path from "node:path"; import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; import { DEFAULT_IDENTITY_FILENAME } from "./workspace.js"; -// IDENTITY.md parsing/writing support. The parser accepts human-authored -// markdown, while the writer only updates stable rich identity fields. +/** Parsed rich identity values from a workspace `IDENTITY.md` file. */ export type AgentIdentityFile = { name?: string; emoji?: string; @@ -52,6 +56,7 @@ function isIdentityPlaceholder(value: string): boolean { return IDENTITY_PLACEHOLDER_VALUES.has(normalized); } +/** Parse rich identity fields from human-authored markdown content. */ export function parseIdentityMarkdown(content: string): AgentIdentityFile { const identity: AgentIdentityFile = {}; const lines = content.split(/\r?\n/); diff --git a/src/agents/identity.test.ts b/src/agents/identity.test.ts index 7ff865fe1487..65376add0567 100644 --- a/src/agents/identity.test.ts +++ b/src/agents/identity.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for identity-driven acknowledgement reactions. + * Confirms account, channel, global, identity, and explicit-empty precedence. + */ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { resolveAckReaction } from "./identity.js"; diff --git a/src/agents/identity.ts b/src/agents/identity.ts index 9bafd5ed52be..fc16660e93c5 100644 --- a/src/agents/identity.ts +++ b/src/agents/identity.ts @@ -1,9 +1,15 @@ +/** + * Agent identity and message-prefix resolution. + * Applies account, channel, global, and per-agent precedence for reactions, + * prefixes, and human-delay settings. + */ import type { HumanDelayConfig, IdentityConfig } from "../config/types.base.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveAgentConfig } from "./agent-scope.js"; const DEFAULT_ACK_REACTION = "👀"; +/** Resolve the configured identity block for one agent. */ export function resolveAgentIdentity( cfg: OpenClawConfig, agentId: string, @@ -11,6 +17,7 @@ export function resolveAgentIdentity( return resolveAgentConfig(cfg, agentId)?.identity; } +/** Resolve the acknowledgement reaction using account, channel, global, then identity fallback. */ export function resolveAckReaction( cfg: OpenClawConfig, agentId: string, @@ -46,6 +53,7 @@ export function resolveAckReaction( return emoji || DEFAULT_ACK_REACTION; } +/** Build the automatic `[name]` prefix for an agent identity. */ export function resolveIdentityNamePrefix( cfg: OpenClawConfig, agentId: string, @@ -57,6 +65,7 @@ export function resolveIdentityNamePrefix( return `[${name}]`; } +/** Resolve the outbound message prefix, preserving explicit empty prefixes. */ export function resolveMessagePrefix( cfg: OpenClawConfig, agentId: string, @@ -87,6 +96,7 @@ function getChannelConfig( : undefined; } +/** Resolve the optional response prefix, expanding `auto` to the identity name prefix. */ export function resolveResponsePrefix( cfg: OpenClawConfig, agentId: string, @@ -128,6 +138,7 @@ export function resolveResponsePrefix( return undefined; } +/** Resolve message and response prefix values together for channel delivery. */ export function resolveEffectiveMessagesConfig( cfg: OpenClawConfig, agentId: string, @@ -150,6 +161,7 @@ export function resolveEffectiveMessagesConfig( }; } +/** Resolve per-agent human-delay settings over global agent defaults. */ export function resolveHumanDelayConfig( cfg: OpenClawConfig, agentId: string, diff --git a/src/agents/model-catalog-browse.test.ts b/src/agents/model-catalog-browse.test.ts index bfeb4141bc3f..08dd0a5d3856 100644 --- a/src/agents/model-catalog-browse.test.ts +++ b/src/agents/model-catalog-browse.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for model catalog browsing. + * Verifies filtered catalog output and pending load behavior. + */ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js"; diff --git a/src/agents/model-catalog-visibility.test.ts b/src/agents/model-catalog-visibility.test.ts index d31a4ab21ed9..fbe816d0e296 100644 --- a/src/agents/model-catalog-visibility.test.ts +++ b/src/agents/model-catalog-visibility.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for model catalog visibility filtering. + * Keeps provider/model allow and hide rules aligned with catalog row metadata. + */ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveVisibleModelCatalog } from "./model-catalog-visibility.js"; diff --git a/src/agents/model-catalog.types.ts b/src/agents/model-catalog.types.ts index 48100dac91b4..0dc4954321b4 100644 --- a/src/agents/model-catalog.types.ts +++ b/src/agents/model-catalog.types.ts @@ -1,7 +1,11 @@ +/** + * Shared model catalog row types. + * Used by discovery, browsing, visibility, and provider-auth code so renderers + * and filters agree on stable model metadata. + */ import type { ModelApi, ModelCompatConfig, ModelMediaInputConfig } from "../config/types.models.js"; -// Public catalog row shape shared by browse/search/provider-auth code. Keep this -// narrow: fields here are the stable model facts consumers can render or filter. +/** Input modalities a catalog entry can advertise. */ export type ModelInputType = "text" | "image" | "audio" | "video" | "document"; /** Normalized model metadata exposed by the agent model catalog. */ diff --git a/src/agents/model-discovery-context.ts b/src/agents/model-discovery-context.ts index a48a84ef7da4..efc479d72687 100644 --- a/src/agents/model-discovery-context.ts +++ b/src/agents/model-discovery-context.ts @@ -1,3 +1,8 @@ +/** + * Shared context resolvers for model discovery. + * Keeps callers from reaching into runtime config or plugin metadata snapshot + * plumbing directly. + */ import { getRuntimeConfig } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { getCurrentPluginMetadataSnapshot } from "../plugins/current-plugin-metadata-snapshot.js"; @@ -5,8 +10,6 @@ import { resolvePluginMetadataSnapshot } from "../plugins/plugin-metadata-snapsh import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "./agent-scope.js"; import type { PluginModelCatalogMetadataSnapshot } from "./plugin-model-catalog.js"; -// Shared context resolvers for model discovery. They keep callers from reaching -// into runtime config or plugin metadata snapshot plumbing directly. /** Resolve the workspace directory model discovery should use for agent scope. */ export function resolveModelWorkspaceDir( cfg: OpenClawConfig | undefined, diff --git a/src/agents/pty-dsr.ts b/src/agents/pty-dsr.ts index f117a2cf2c66..c30be89f20ca 100644 --- a/src/agents/pty-dsr.ts +++ b/src/agents/pty-dsr.ts @@ -1,3 +1,8 @@ +/** + * Terminal device-status-report helpers. + * Intercepts cursor-position requests from PTY output and generates compact + * responses when a real terminal cannot answer them. + */ const ESC = String.fromCharCode(0x1b); const DSR_PATTERN = new RegExp(`${ESC}\\[\\??6n`, "g"); diff --git a/src/agents/pty-keys.test.ts b/src/agents/pty-keys.test.ts index 85614e76b56d..063ab867ceea 100644 --- a/src/agents/pty-keys.test.ts +++ b/src/agents/pty-keys.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for PTY key encoding and DSR stripping. + * Protects terminal control bytes used by process send-keys and PTY sessions. + */ import { expect, test } from "vitest"; import { buildCursorPositionResponse, stripDsrRequests } from "./pty-dsr.js"; import { diff --git a/src/agents/run-wait.test.ts b/src/agents/run-wait.test.ts index 3d9d2ee0c981..21bfa71ab1db 100644 --- a/src/agents/run-wait.test.ts +++ b/src/agents/run-wait.test.ts @@ -1,3 +1,7 @@ +/** + * Regression coverage for gateway-backed agent run waiting. + * Exercises timeout normalization, reply snapshots, and dynamic drain loops. + */ import { addTimerTimeoutGraceMs, MAX_DATE_TIMESTAMP_MS, diff --git a/src/agents/run-wait.ts b/src/agents/run-wait.ts index 1bfe3fbb5242..d85e7b1e0d6c 100644 --- a/src/agents/run-wait.ts +++ b/src/agents/run-wait.ts @@ -1,3 +1,8 @@ +/** + * Gateway-backed agent run wait helpers. + * Normalizes run wait responses, reads the latest assistant reply, and drains + * pending run sets for tools that need synchronous completion semantics. + */ import { addTimerTimeoutGraceMs, asDateTimestampMs, @@ -44,11 +49,13 @@ function resolveRunWaitDeadlineAtMs(params: { deadlineAtMs?: number; timeoutMs?: ); } +/** Latest assistant reply plus a stable fingerprint for baseline comparisons. */ export type AssistantReplySnapshot = { text?: string; fingerprint?: string; }; +/** Normalized terminal or pending state returned by `agent.wait`. */ export type AgentWaitResult = { status: "ok" | "timeout" | "error" | "pending"; error?: string; @@ -62,6 +69,7 @@ export type AgentWaitResult = { providerStarted?: boolean; }; +/** Summary returned after waiting for a dynamic set of pending runs to drain. */ export type AgentRunsDrainResult = { timedOut: boolean; pendingRunIds: string[]; @@ -128,6 +136,7 @@ const RECOVERABLE_AGENT_WAIT_ERROR_PATTERNS: readonly RegExp[] = [ /\b(ECONNRESET|ECONNREFUSED|ETIMEDOUT|EPIPE|EHOSTUNREACH|ENETUNREACH)\b/i, ]; +/** Return true for transient gateway/transport failures that callers may retry. */ export function isRecoverableAgentWaitError(error: string | undefined): boolean { const message = error?.trim(); if (!message) { @@ -175,6 +184,7 @@ function resolveLatestAssistantReplySnapshot(messages: unknown[]): AssistantRepl return {}; } +/** Read the latest non-tool assistant message for a session. */ export async function readLatestAssistantReplySnapshot(params: { sessionKey: string; limit?: number; @@ -191,6 +201,7 @@ export async function readLatestAssistantReplySnapshot(params: { ); } +/** Read only the latest assistant text for call sites that do not need fingerprints. */ export async function readLatestAssistantReply(params: { sessionKey: string; limit?: number; @@ -205,6 +216,7 @@ export async function readLatestAssistantReply(params: { ).text; } +/** Wait for one agent run through the gateway and normalize timeout/error states. */ export async function waitForAgentRun(params: { runId: string; timeoutMs: number; @@ -239,6 +251,7 @@ export async function waitForAgentRun(params: { } } +/** Wait for a run and return a reply only when it differs from the supplied baseline. */ export async function waitForAgentRunAndReadUpdatedAssistantReply(params: { runId: string; sessionKey: string; @@ -272,6 +285,7 @@ export async function waitForAgentRunAndReadUpdatedAssistantReply(params: { }; } +/** Wait until the current and newly spawned pending run IDs are drained or timed out. */ export async function waitForAgentRunsToDrain(params: { getPendingRunIds: () => Iterable; initialPendingRunIds?: Iterable; @@ -307,6 +321,7 @@ export async function waitForAgentRunsToDrain(params: { }; } +/** Test-only dependency injection for gateway calls. */ export const testing = { setDepsForTest(overrides?: Partial<{ callGateway: GatewayCaller }>) { runWaitDeps = overrides diff --git a/src/agents/workspace.bootstrap-cache.test.ts b/src/agents/workspace.bootstrap-cache.test.ts index 84376a6b6d82..3685811d7005 100644 --- a/src/agents/workspace.bootstrap-cache.test.ts +++ b/src/agents/workspace.bootstrap-cache.test.ts @@ -1,3 +1,7 @@ +/** + * Integration coverage for workspace bootstrap cache reads. + * Uses temp workspaces to verify real file loading through the cache layer. + */ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest";