mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
docs: document agent helper contracts
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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/);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<string>;
|
||||
initialPendingRunIds?: Iterable<string>;
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user