mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
docs: document agent core package
This commit is contained in:
@@ -53,6 +53,8 @@ import {
|
||||
toError,
|
||||
} from "./types.js";
|
||||
|
||||
// CoreAgentHarness coordinates session state, resources, tools, compaction, and
|
||||
// streaming callbacks around the lower-level agent loop.
|
||||
function createUserMessage(text: string, images?: ImageContent[]): UserMessage {
|
||||
const content: Array<{ type: "text"; text: string } | ImageContent> = [{ type: "text", text }];
|
||||
if (images) {
|
||||
@@ -209,6 +211,7 @@ interface AgentHarnessTurnState<
|
||||
activeTools: TTool[];
|
||||
}
|
||||
|
||||
/** Stateful harness for running, steering, compacting, and navigating sessions. */
|
||||
export class CoreAgentHarness<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type Session, SessionError, type SessionMetadata, type SessionRepo } fr
|
||||
import { InMemorySessionStorage } from "./memory-storage.js";
|
||||
import { createSessionId, createTimestamp, getEntriesToFork, toSession } from "./repo-utils.js";
|
||||
|
||||
/** In-memory session repository for tests and ephemeral harness usage. */
|
||||
export class InMemorySessionRepo implements SessionRepo<SessionMetadata, { id?: string }> {
|
||||
private sessions = new Map<string, Session>();
|
||||
|
||||
|
||||
@@ -9,20 +9,24 @@ import {
|
||||
import { Session } from "./session.js";
|
||||
import { uuidv7 } from "./uuid.js";
|
||||
|
||||
/** Create a time-sortable session id. */
|
||||
export function createSessionId(): string {
|
||||
return uuidv7();
|
||||
}
|
||||
|
||||
/** Create a canonical session timestamp string. */
|
||||
export function createTimestamp(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
/** Wrap a storage implementation in the Session facade. */
|
||||
export function toSession<TMetadata extends SessionMetadata>(
|
||||
storage: SessionStorage<TMetadata>,
|
||||
): Session<TMetadata> {
|
||||
return new Session(storage);
|
||||
}
|
||||
|
||||
/** Unwrap filesystem results into session errors with caller context. */
|
||||
export function getFileSystemResultOrThrow<TValue>(
|
||||
result: Result<TValue, FileError>,
|
||||
message: string,
|
||||
@@ -34,6 +38,7 @@ export function getFileSystemResultOrThrow<TValue>(
|
||||
return result.value;
|
||||
}
|
||||
|
||||
/** Select the entries copied into a forked session. */
|
||||
export async function getEntriesToFork(
|
||||
storage: SessionStorage,
|
||||
options: { entryId?: string; position?: "before" | "at" },
|
||||
@@ -49,6 +54,8 @@ export async function getEntriesToFork(
|
||||
if ((options.position ?? "before") === "at") {
|
||||
effectiveLeafId = target.id;
|
||||
} else {
|
||||
// Fork-before only targets user turns so the fork starts where a new prompt
|
||||
// can replace that turn without carrying its response.
|
||||
if (target.type !== "message" || target.message.role !== "user") {
|
||||
throw new SessionError(
|
||||
"invalid_fork_target",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** Parse an ISO-like session timestamp to milliseconds. */
|
||||
export function parseSessionTimestampMs(value: unknown): number | undefined {
|
||||
if (typeof value !== "string" || !value.trim()) {
|
||||
return undefined;
|
||||
@@ -6,6 +7,7 @@ export function parseSessionTimestampMs(value: unknown): number | undefined {
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
/** Parse a required timestamp or throw a labeled validation error. */
|
||||
export function requireSessionTimestampMs(value: string, label: string): number {
|
||||
const parsed = parseSessionTimestampMs(value);
|
||||
if (parsed === undefined) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
let lastTimestamp = -Infinity;
|
||||
let sequence = 0;
|
||||
|
||||
// Small UUIDv7 generator for browser/node package builds without a runtime dep.
|
||||
function fillRandomBytes(bytes: Uint8Array): void {
|
||||
const crypto = globalThis.crypto;
|
||||
if (crypto?.getRandomValues) {
|
||||
@@ -12,6 +13,7 @@ function fillRandomBytes(bytes: Uint8Array): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate a monotonic UUIDv7 string. */
|
||||
export function uuidv7(): string {
|
||||
const random = new Uint8Array(16);
|
||||
fillRandomBytes(random);
|
||||
@@ -21,6 +23,8 @@ export function uuidv7(): string {
|
||||
sequence = random[6] * 0x1000000 + random[7] * 0x10000 + random[8] * 0x100 + random[9];
|
||||
lastTimestamp = timestamp;
|
||||
} else {
|
||||
// Same-ms calls increment the sequence so generated ids remain sortable and
|
||||
// unique even when random bytes repeat.
|
||||
sequence = (sequence + 1) >>> 0;
|
||||
if (sequence === 0) {
|
||||
lastTimestamp++;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../types.js";
|
||||
import { DEFAULT_MAX_BYTES, truncateTail } from "./truncate.js";
|
||||
|
||||
/** Options for shell execution with combined stdout/stderr capture. */
|
||||
export interface ShellCaptureOptions extends Omit<
|
||||
ExecutionEnvExecOptions,
|
||||
"onStdout" | "onStderr"
|
||||
@@ -16,6 +17,7 @@ export interface ShellCaptureOptions extends Omit<
|
||||
onChunk?: (chunk: string) => void;
|
||||
}
|
||||
|
||||
/** Captured shell result, with large output optionally spilled to a file. */
|
||||
export interface ShellCaptureResult {
|
||||
output: string;
|
||||
exitCode: number | undefined;
|
||||
@@ -32,6 +34,7 @@ function toExecutionError(error: unknown): ExecutionError {
|
||||
return new ExecutionError("unknown", cause.message, cause);
|
||||
}
|
||||
|
||||
/** Remove control characters that make terminal/model output unsafe to replay. */
|
||||
export function sanitizeBinaryOutput(str: string): string {
|
||||
return Array.from(str)
|
||||
.filter((char) => {
|
||||
@@ -53,6 +56,7 @@ export function sanitizeBinaryOutput(str: string): string {
|
||||
.join("");
|
||||
}
|
||||
|
||||
/** Execute a command while keeping a bounded tail and optional full-output log. */
|
||||
export async function executeShellWithCapture(
|
||||
env: ExecutionEnv,
|
||||
command: string,
|
||||
@@ -113,6 +117,8 @@ export async function executeShellWithCapture(
|
||||
totalBytes += encoder.encode(chunk).byteLength;
|
||||
const text = sanitizeBinaryOutput(chunk).replace(/\r/g, "");
|
||||
if (totalBytes > DEFAULT_MAX_BYTES && !fullOutputPath) {
|
||||
// Once the response-size budget is exceeded, start a durable log with
|
||||
// everything captured so far and continue streaming only the bounded tail.
|
||||
ensureFullOutputFile(outputChunks.join("") + text);
|
||||
} else {
|
||||
appendFullOutput(text);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Public agent-core package surface: agent loop, harness, session storage,
|
||||
// compaction, execution envs, and utility helpers.
|
||||
export * from "./agent.js";
|
||||
export * from "./agent-loop.js";
|
||||
export * from "./node.js";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
// LLM-core compatibility barrel for agent-core consumers.
|
||||
export * from "@openclaw/llm-core";
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
// Node-specific agent-core entrypoint with the default Node execution env.
|
||||
export { NodeExecutionEnv } from "./harness/env/nodejs.js";
|
||||
export * from "./index.js";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
// Tool validation facade for callers that import validation from agent-core.
|
||||
export { validateToolArguments, validateToolCall } from "@openclaw/llm-core";
|
||||
|
||||
Reference in New Issue
Block a user