mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
refactor: expand acp core package (#88618)
* refactor: expand acp core package * chore: drop acp core package symlink * fix: keep acp core dependency graph stable * fix: add acp core tsconfig subpaths * fix: sync acp core boundary path artifacts * fix: use kysely for cron run-log queries * fix: resolve acp core subpaths in loaders
This commit is contained in:
committed by
GitHub
parent
cc290050b4
commit
7dea283756
@@ -257,6 +257,9 @@
|
|||||||
"@openclaw/acp-core/record-shared": [
|
"@openclaw/acp-core/record-shared": [
|
||||||
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
|
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
|
||||||
],
|
],
|
||||||
|
"@openclaw/acp-core/runtime/errors": [
|
||||||
|
"../dist/plugin-sdk/packages/acp-core/src/runtime/errors.d.ts"
|
||||||
|
],
|
||||||
"@openclaw/acp-core/runtime/types": [
|
"@openclaw/acp-core/runtime/types": [
|
||||||
"../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts"
|
"../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -243,6 +243,9 @@
|
|||||||
"@openclaw/acp-core/record-shared": [
|
"@openclaw/acp-core/record-shared": [
|
||||||
"../../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
|
"../../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
|
||||||
],
|
],
|
||||||
|
"@openclaw/acp-core/runtime/errors": [
|
||||||
|
"../../dist/plugin-sdk/packages/acp-core/src/runtime/errors.d.ts"
|
||||||
|
],
|
||||||
"@openclaw/acp-core/runtime/types": [
|
"@openclaw/acp-core/runtime/types": [
|
||||||
"../../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts"
|
"../../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts"
|
||||||
],
|
],
|
||||||
|
|||||||
10
packages/acp-core/dist/error-format.d.mts
vendored
Normal file
10
packages/acp-core/dist/error-format.d.mts
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//#region src/error-format.d.ts
|
||||||
|
declare function configureAcpErrorRedactor(redactor: ((value: string) => string) | undefined): void;
|
||||||
|
declare function redactSensitiveText(value: string): string;
|
||||||
|
/**
|
||||||
|
* Render a non-Error `cause` value without leaking `[object Object]` or throwing
|
||||||
|
* while formatting nested ACP runtime failures.
|
||||||
|
*/
|
||||||
|
declare function stringifyNonErrorCause(value: unknown): string;
|
||||||
|
//#endregion
|
||||||
|
export { configureAcpErrorRedactor, redactSensitiveText, stringifyNonErrorCause };
|
||||||
64
packages/acp-core/dist/error-format.mjs
vendored
Normal file
64
packages/acp-core/dist/error-format.mjs
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
//#region src/error-format.ts
|
||||||
|
const SECRET_PATTERNS = [
|
||||||
|
/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CARD[_-]?NUMBER|CARD[_-]?CVC|CARD[_-]?CVV|CVC|CVV|SECURITY[_-]?CODE|PAYMENT[_-]?CREDENTIAL|SHARED[_-]?PAYMENT[_-]?TOKEN)\b\s*[=:]\s*(["']?)([^\s"'\\]+)\1/g,
|
||||||
|
/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CARD[_-]?NUMBER|CARD[_-]?CVC|CARD[_-]?CVV|CVC|CVV|SECURITY[_-]?CODE|PAYMENT[_-]?CREDENTIAL|SHARED[_-]?PAYMENT[_-]?TOKEN)\b\s*[=:]\s*\\+(["'])([^\s"'\\]+)\\+\1/g,
|
||||||
|
/[?&](?:access[-_]?token|auth[-_]?token|hook[-_]?token|refresh[-_]?token|api[-_]?key|client[-_]?secret|token|key|secret|password|pass|passwd|auth|signature|card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token)=([^&\s"'<>]+)/gi,
|
||||||
|
/"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken|cardNumber|card_number|cardCvc|card_cvc|cardCvv|card_cvv|cvc|cvv|securityCode|security_code|paymentCredential|payment_credential|sharedPaymentToken|shared_payment_token)"\s*:\s*"([^"]+)"/g,
|
||||||
|
/(^|[\s,{])["']?(?:api[-_]key|access[-_]token|refresh[-_]token|authToken|auth[-_]token|clientSecret|client[-_]secret|appSecret|app[-_]secret)["']?\s*[:=]\s*(["'])([^"'\r\n]+)\2/gi,
|
||||||
|
/(^|[\s,{])["']?(?:authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-auth-token)["']?\s*[:=]\s*(["'])([^"'\r\n]+)\2/gi,
|
||||||
|
/--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd|card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token)\s+(["']?)([^\s"']+)\1/gi,
|
||||||
|
/Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/gi,
|
||||||
|
/Authorization\s*[:=]\s*Basic\s+([A-Za-z0-9+/=]+)/gi,
|
||||||
|
/(?:X-OpenClaw-Token|x-pomerium-jwt-assertion|X-Api-Key|X-Auth-Token)\s*[:=]\s*([^\s"',;]+)/gi,
|
||||||
|
/\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/g,
|
||||||
|
/(^|[\s,;])(?:access_token|refresh_token|auth[-_]?token|api[-_]?key|client[-_]?secret|app[-_]?secret|token|secret|password|passwd|card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token)=([^\s&#]+)/gi,
|
||||||
|
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
|
||||||
|
/\b(sk-[A-Za-z0-9_-]{8,})\b/g,
|
||||||
|
/(ghp_[A-Za-z0-9]{20,})/g,
|
||||||
|
/(github_pat_[A-Za-z0-9_]{20,})/g,
|
||||||
|
/(xox[baprs]-[A-Za-z0-9-]{10,})/g,
|
||||||
|
/(xapp-[A-Za-z0-9-]{10,})/g,
|
||||||
|
/(gsk_[A-Za-z0-9_-]{10,})/g,
|
||||||
|
/(AIza[0-9A-Za-z\-_]{20,})/g,
|
||||||
|
/(ya29\.[0-9A-Za-z_\-./+=]{10,})/g,
|
||||||
|
/(1\/\/0[0-9A-Za-z_\-./+=]{10,})/g,
|
||||||
|
/(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})/g,
|
||||||
|
/(pplx-[A-Za-z0-9_-]{10,})/g,
|
||||||
|
/(npm_[A-Za-z0-9]{10,})/g,
|
||||||
|
/(AKID[A-Za-z0-9]{10,})/g,
|
||||||
|
/(LTAI[A-Za-z0-9]{10,})/g,
|
||||||
|
/(hf_[A-Za-z0-9]{10,})/g,
|
||||||
|
/(r8_[A-Za-z0-9]{10,})/g,
|
||||||
|
/\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g,
|
||||||
|
/\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g
|
||||||
|
];
|
||||||
|
let configuredRedactor;
|
||||||
|
function configureAcpErrorRedactor(redactor) {
|
||||||
|
configuredRedactor = redactor;
|
||||||
|
}
|
||||||
|
function redactSensitiveText(value) {
|
||||||
|
if (configuredRedactor) return configuredRedactor(value);
|
||||||
|
let redacted = value;
|
||||||
|
for (const pattern of SECRET_PATTERNS) redacted = redacted.replace(pattern, (match, ...args) => {
|
||||||
|
if (match.includes("PRIVATE KEY-----")) return "[REDACTED_PRIVATE_KEY]";
|
||||||
|
const token = args.slice(0, -2).findLast((group) => typeof group === "string" && group.length > 0);
|
||||||
|
return token ? match.replace(token, "[REDACTED]") : "[REDACTED]";
|
||||||
|
});
|
||||||
|
return redacted;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Render a non-Error `cause` value without leaking `[object Object]` or throwing
|
||||||
|
* while formatting nested ACP runtime failures.
|
||||||
|
*/
|
||||||
|
function stringifyNonErrorCause(value) {
|
||||||
|
if (value === null) return "null";
|
||||||
|
if (typeof value === "string") return value;
|
||||||
|
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
||||||
|
try {
|
||||||
|
return JSON.stringify(value);
|
||||||
|
} catch {
|
||||||
|
return Object.prototype.toString.call(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { configureAcpErrorRedactor, redactSensitiveText, stringifyNonErrorCause };
|
||||||
15
packages/acp-core/dist/index.d.mts
vendored
Normal file
15
packages/acp-core/dist/index.d.mts
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { configureAcpErrorRedactor, redactSensitiveText, stringifyNonErrorCause } from "./error-format.mjs";
|
||||||
|
import { readBool, readNonNegativeInteger, readNumber, readString } from "./meta.mjs";
|
||||||
|
import { normalizeText } from "./normalize-text.mjs";
|
||||||
|
import { resolveIntegerOption } from "./numeric-options.mjs";
|
||||||
|
import { asRecord } from "./record-shared.mjs";
|
||||||
|
import { isParentOwnedBackgroundAcpSession, isRequesterParentOfBackgroundAcpSession } from "./session-interaction-mode.mjs";
|
||||||
|
import { AcpSessionLineageMeta, AcpSessionLineageRow, toAcpSessionLineageMeta } from "./session-lineage-meta.mjs";
|
||||||
|
import { AcpProvenanceMode, AcpServerOptions, AcpSession, AcpSessionRuntimeOptions, SessionAcpIdentity, SessionAcpIdentitySource, SessionAcpIdentityState, SessionAcpMeta, SessionId, normalizeAcpProvenanceMode } from "./types.mjs";
|
||||||
|
import { AcpSessionStore, createInMemorySessionStore, defaultAcpSessionStore } from "./session.mjs";
|
||||||
|
import { ACP_ERROR_CODES, AcpRuntimeError, AcpRuntimeErrorCode, formatAcpErrorChain, isAcpRuntimeError, toAcpRuntimeError, withAcpRuntimeErrorBoundary } from "./runtime/errors.mjs";
|
||||||
|
import { formatAcpRuntimeErrorText, toAcpRuntimeErrorText } from "./runtime/error-text.mjs";
|
||||||
|
import { ACP_SESSION_IDENTITY_RENDERER_VERSION, AcpSessionIdentifierRenderMode, resolveAcpSessionCwd, resolveAcpSessionIdentifierLines, resolveAcpSessionIdentifierLinesFromIdentity, resolveAcpThreadSessionDetailLines } from "./runtime/session-identifiers.mjs";
|
||||||
|
import { AcpRuntime, AcpRuntimeCapabilities, AcpRuntimeControl, AcpRuntimeDoctorReport, AcpRuntimeEnsureInput, AcpRuntimeEvent, AcpRuntimeHandle, AcpRuntimePromptMode, AcpRuntimeSessionMode, AcpRuntimeStatus, AcpRuntimeTurn, AcpRuntimeTurnAttachment, AcpRuntimeTurnInput, AcpRuntimeTurnResult, AcpRuntimeTurnResultError, AcpSessionUpdateTag } from "./runtime/types.mjs";
|
||||||
|
import { createIdentityFromEnsure, createIdentityFromHandleEvent, createIdentityFromStatus, identityEquals, identityHasStableSessionId, isSessionIdentityPending, mergeSessionIdentity, resolveRuntimeHandleIdentifiersFromIdentity, resolveRuntimeResumeSessionId, resolveSessionIdentityFromMeta } from "./runtime/session-identity.mjs";
|
||||||
|
export { ACP_ERROR_CODES, ACP_SESSION_IDENTITY_RENDERER_VERSION, AcpProvenanceMode, AcpRuntime, AcpRuntimeCapabilities, AcpRuntimeControl, AcpRuntimeDoctorReport, AcpRuntimeEnsureInput, AcpRuntimeError, AcpRuntimeErrorCode, AcpRuntimeEvent, AcpRuntimeHandle, AcpRuntimePromptMode, AcpRuntimeSessionMode, AcpRuntimeStatus, AcpRuntimeTurn, AcpRuntimeTurnAttachment, AcpRuntimeTurnInput, AcpRuntimeTurnResult, AcpRuntimeTurnResultError, AcpServerOptions, AcpSession, AcpSessionIdentifierRenderMode, AcpSessionLineageMeta, AcpSessionLineageRow, AcpSessionRuntimeOptions, AcpSessionStore, AcpSessionUpdateTag, SessionAcpIdentity, SessionAcpIdentitySource, SessionAcpIdentityState, SessionAcpMeta, SessionId, asRecord, configureAcpErrorRedactor, createIdentityFromEnsure, createIdentityFromHandleEvent, createIdentityFromStatus, createInMemorySessionStore, defaultAcpSessionStore, formatAcpErrorChain, formatAcpRuntimeErrorText, identityEquals, identityHasStableSessionId, isAcpRuntimeError, isParentOwnedBackgroundAcpSession, isRequesterParentOfBackgroundAcpSession, isSessionIdentityPending, mergeSessionIdentity, normalizeAcpProvenanceMode, normalizeText, readBool, readNonNegativeInteger, readNumber, readString, redactSensitiveText, resolveAcpSessionCwd, resolveAcpSessionIdentifierLines, resolveAcpSessionIdentifierLinesFromIdentity, resolveAcpThreadSessionDetailLines, resolveIntegerOption, resolveRuntimeHandleIdentifiersFromIdentity, resolveRuntimeResumeSessionId, resolveSessionIdentityFromMeta, stringifyNonErrorCause, toAcpRuntimeError, toAcpRuntimeErrorText, toAcpSessionLineageMeta, withAcpRuntimeErrorBoundary };
|
||||||
15
packages/acp-core/dist/index.mjs
vendored
Normal file
15
packages/acp-core/dist/index.mjs
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { configureAcpErrorRedactor, redactSensitiveText, stringifyNonErrorCause } from "./error-format.mjs";
|
||||||
|
import { readBool, readNonNegativeInteger, readNumber, readString } from "./meta.mjs";
|
||||||
|
import { normalizeText } from "./normalize-text.mjs";
|
||||||
|
import { resolveIntegerOption } from "./numeric-options.mjs";
|
||||||
|
import { asRecord } from "./record-shared.mjs";
|
||||||
|
import { isParentOwnedBackgroundAcpSession, isRequesterParentOfBackgroundAcpSession } from "./session-interaction-mode.mjs";
|
||||||
|
import { toAcpSessionLineageMeta } from "./session-lineage-meta.mjs";
|
||||||
|
import { createInMemorySessionStore, defaultAcpSessionStore } from "./session.mjs";
|
||||||
|
import { normalizeAcpProvenanceMode } from "./types.mjs";
|
||||||
|
import { ACP_ERROR_CODES, AcpRuntimeError, formatAcpErrorChain, isAcpRuntimeError, toAcpRuntimeError, withAcpRuntimeErrorBoundary } from "./runtime/errors.mjs";
|
||||||
|
import { formatAcpRuntimeErrorText, toAcpRuntimeErrorText } from "./runtime/error-text.mjs";
|
||||||
|
import { createIdentityFromEnsure, createIdentityFromHandleEvent, createIdentityFromStatus, identityEquals, identityHasStableSessionId, isSessionIdentityPending, mergeSessionIdentity, resolveRuntimeHandleIdentifiersFromIdentity, resolveRuntimeResumeSessionId, resolveSessionIdentityFromMeta } from "./runtime/session-identity.mjs";
|
||||||
|
import { ACP_SESSION_IDENTITY_RENDERER_VERSION, resolveAcpSessionCwd, resolveAcpSessionIdentifierLines, resolveAcpSessionIdentifierLinesFromIdentity, resolveAcpThreadSessionDetailLines } from "./runtime/session-identifiers.mjs";
|
||||||
|
import "./runtime/types.mjs";
|
||||||
|
export { ACP_ERROR_CODES, ACP_SESSION_IDENTITY_RENDERER_VERSION, AcpRuntimeError, asRecord, configureAcpErrorRedactor, createIdentityFromEnsure, createIdentityFromHandleEvent, createIdentityFromStatus, createInMemorySessionStore, defaultAcpSessionStore, formatAcpErrorChain, formatAcpRuntimeErrorText, identityEquals, identityHasStableSessionId, isAcpRuntimeError, isParentOwnedBackgroundAcpSession, isRequesterParentOfBackgroundAcpSession, isSessionIdentityPending, mergeSessionIdentity, normalizeAcpProvenanceMode, normalizeText, readBool, readNonNegativeInteger, readNumber, readString, redactSensitiveText, resolveAcpSessionCwd, resolveAcpSessionIdentifierLines, resolveAcpSessionIdentifierLinesFromIdentity, resolveAcpThreadSessionDetailLines, resolveIntegerOption, resolveRuntimeHandleIdentifiersFromIdentity, resolveRuntimeResumeSessionId, resolveSessionIdentityFromMeta, stringifyNonErrorCause, toAcpRuntimeError, toAcpRuntimeErrorText, toAcpSessionLineageMeta, withAcpRuntimeErrorBoundary };
|
||||||
7
packages/acp-core/dist/meta.d.mts
vendored
Normal file
7
packages/acp-core/dist/meta.d.mts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//#region src/meta.d.ts
|
||||||
|
declare function readString(meta: Record<string, unknown> | null | undefined, keys: string[]): string | undefined;
|
||||||
|
declare function readBool(meta: Record<string, unknown> | null | undefined, keys: string[]): boolean | undefined;
|
||||||
|
declare function readNumber(meta: Record<string, unknown> | null | undefined, keys: string[]): number | undefined;
|
||||||
|
declare function readNonNegativeInteger(meta: Record<string, unknown> | null | undefined, keys: string[]): number | undefined;
|
||||||
|
//#endregion
|
||||||
|
export { readBool, readNonNegativeInteger, readNumber, readString };
|
||||||
23
packages/acp-core/dist/meta.mjs
vendored
Normal file
23
packages/acp-core/dist/meta.mjs
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
//#region src/meta.ts
|
||||||
|
function readMetaValue(meta, keys, normalize) {
|
||||||
|
if (!meta) return;
|
||||||
|
for (const key of keys) {
|
||||||
|
const normalized = normalize(meta[key]);
|
||||||
|
if (normalized !== void 0) return normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function readString(meta, keys) {
|
||||||
|
return readMetaValue(meta, keys, normalizeOptionalString);
|
||||||
|
}
|
||||||
|
function readBool(meta, keys) {
|
||||||
|
return readMetaValue(meta, keys, (value) => typeof value === "boolean" ? value : void 0);
|
||||||
|
}
|
||||||
|
function readNumber(meta, keys) {
|
||||||
|
return readMetaValue(meta, keys, (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0);
|
||||||
|
}
|
||||||
|
function readNonNegativeInteger(meta, keys) {
|
||||||
|
return readMetaValue(meta, keys, (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0);
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { readBool, readNonNegativeInteger, readNumber, readString };
|
||||||
2
packages/acp-core/dist/normalize-text.d.mts
vendored
Normal file
2
packages/acp-core/dist/normalize-text.d.mts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
export { normalizeText };
|
||||||
2
packages/acp-core/dist/normalize-text.mjs
vendored
Normal file
2
packages/acp-core/dist/normalize-text.mjs
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
export { normalizeText };
|
||||||
6
packages/acp-core/dist/numeric-options.d.mts
vendored
Normal file
6
packages/acp-core/dist/numeric-options.d.mts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//#region src/numeric-options.d.ts
|
||||||
|
declare function resolveIntegerOption(value: number | undefined, fallback: number, params: {
|
||||||
|
min: number;
|
||||||
|
}): number;
|
||||||
|
//#endregion
|
||||||
|
export { resolveIntegerOption };
|
||||||
7
packages/acp-core/dist/numeric-options.mjs
vendored
Normal file
7
packages/acp-core/dist/numeric-options.mjs
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { resolveIntegerOption as resolveIntegerOption$1 } from "@openclaw/normalization-core/number-coercion";
|
||||||
|
//#region src/numeric-options.ts
|
||||||
|
function resolveIntegerOption(value, fallback, params) {
|
||||||
|
return resolveIntegerOption$1(value, fallback, params);
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { resolveIntegerOption };
|
||||||
2
packages/acp-core/dist/record-shared.d.mts
vendored
Normal file
2
packages/acp-core/dist/record-shared.d.mts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { asOptionalRecord as asRecord } from "@openclaw/normalization-core/record-coerce";
|
||||||
|
export { asRecord };
|
||||||
2
packages/acp-core/dist/record-shared.mjs
vendored
Normal file
2
packages/acp-core/dist/record-shared.mjs
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { asOptionalRecord as asRecord } from "@openclaw/normalization-core/record-coerce";
|
||||||
|
export { asRecord };
|
||||||
11
packages/acp-core/dist/runtime/error-text.d.mts
vendored
Normal file
11
packages/acp-core/dist/runtime/error-text.d.mts
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { AcpRuntimeError, AcpRuntimeErrorCode } from "./errors.mjs";
|
||||||
|
|
||||||
|
//#region src/runtime/error-text.d.ts
|
||||||
|
declare function formatAcpRuntimeErrorText(error: AcpRuntimeError): string;
|
||||||
|
declare function toAcpRuntimeErrorText(params: {
|
||||||
|
error: unknown;
|
||||||
|
fallbackCode: AcpRuntimeErrorCode;
|
||||||
|
fallbackMessage: string;
|
||||||
|
}): string;
|
||||||
|
//#endregion
|
||||||
|
export { formatAcpRuntimeErrorText, toAcpRuntimeErrorText };
|
||||||
24
packages/acp-core/dist/runtime/error-text.mjs
vendored
Normal file
24
packages/acp-core/dist/runtime/error-text.mjs
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { toAcpRuntimeError } from "./errors.mjs";
|
||||||
|
//#region src/runtime/error-text.ts
|
||||||
|
function resolveAcpRuntimeErrorNextStep(error) {
|
||||||
|
if (error.code === "ACP_BACKEND_MISSING" || error.code === "ACP_BACKEND_UNAVAILABLE") return "Run `/acp doctor`, install/enable the backend plugin, then retry.";
|
||||||
|
if (error.code === "ACP_DISPATCH_DISABLED") return "Enable `acp.dispatch.enabled=true` to allow thread-message ACP turns.";
|
||||||
|
if (error.code === "ACP_SESSION_INIT_FAILED") return "If this session is stale, recreate it with `/acp spawn` and rebind the thread.";
|
||||||
|
if (error.code === "ACP_INVALID_RUNTIME_OPTION") return "Use `/acp status` to inspect options and pass valid values.";
|
||||||
|
if (error.code === "ACP_BACKEND_UNSUPPORTED_CONTROL") return "This backend does not support that control; use a supported command.";
|
||||||
|
if (error.code === "ACP_TURN_FAILED") return "Retry, or use `/acp cancel` and send the message again.";
|
||||||
|
}
|
||||||
|
function formatAcpRuntimeErrorText(error) {
|
||||||
|
const next = resolveAcpRuntimeErrorNextStep(error);
|
||||||
|
if (!next) return `ACP error (${error.code}): ${error.message}`;
|
||||||
|
return `ACP error (${error.code}): ${error.message}\nnext: ${next}`;
|
||||||
|
}
|
||||||
|
function toAcpRuntimeErrorText(params) {
|
||||||
|
return formatAcpRuntimeErrorText(toAcpRuntimeError({
|
||||||
|
error: params.error,
|
||||||
|
fallbackCode: params.fallbackCode,
|
||||||
|
fallbackMessage: params.fallbackMessage
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { formatAcpRuntimeErrorText, toAcpRuntimeErrorText };
|
||||||
33
packages/acp-core/dist/runtime/errors.d.mts
vendored
Normal file
33
packages/acp-core/dist/runtime/errors.d.mts
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//#region src/runtime/errors.d.ts
|
||||||
|
declare const ACP_ERROR_CODES: readonly ["ACP_BACKEND_MISSING", "ACP_BACKEND_UNAVAILABLE", "ACP_BACKEND_UNSUPPORTED_CONTROL", "ACP_DISPATCH_DISABLED", "ACP_INVALID_RUNTIME_OPTION", "ACP_SESSION_INIT_FAILED", "ACP_TURN_FAILED"];
|
||||||
|
type AcpRuntimeErrorCode = (typeof ACP_ERROR_CODES)[number];
|
||||||
|
declare class AcpRuntimeError extends Error {
|
||||||
|
readonly code: AcpRuntimeErrorCode;
|
||||||
|
readonly cause?: unknown;
|
||||||
|
constructor(code: AcpRuntimeErrorCode, message: string, options?: {
|
||||||
|
cause?: unknown;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
declare function isAcpRuntimeError(value: unknown): value is AcpRuntimeError;
|
||||||
|
declare function toAcpRuntimeError(params: {
|
||||||
|
error: unknown;
|
||||||
|
fallbackCode: AcpRuntimeErrorCode;
|
||||||
|
fallbackMessage: string;
|
||||||
|
}): AcpRuntimeError;
|
||||||
|
/**
|
||||||
|
* Render an error and its `.cause` chain as a single human-readable line for
|
||||||
|
* logs, lifecycle events, and tool results. Format is
|
||||||
|
* `Name [code]: message <- Name [code]: message <- ...`. Number codes also
|
||||||
|
* appear, so JSON-RPC error codes like `-32603` survive into surfaces that
|
||||||
|
* downstream consumers see (gateway logs, telegram replies, tool_result text).
|
||||||
|
*
|
||||||
|
* Depth is capped to defend against self-referential `.cause` cycles.
|
||||||
|
*/
|
||||||
|
declare function formatAcpErrorChain(error: unknown): string;
|
||||||
|
declare function withAcpRuntimeErrorBoundary<T>(params: {
|
||||||
|
run: () => Promise<T>;
|
||||||
|
fallbackCode: AcpRuntimeErrorCode;
|
||||||
|
fallbackMessage: string;
|
||||||
|
}): Promise<T>;
|
||||||
|
//#endregion
|
||||||
|
export { ACP_ERROR_CODES, AcpRuntimeError, AcpRuntimeErrorCode, formatAcpErrorChain, isAcpRuntimeError, toAcpRuntimeError, withAcpRuntimeErrorBoundary };
|
||||||
97
packages/acp-core/dist/runtime/errors.mjs
vendored
Normal file
97
packages/acp-core/dist/runtime/errors.mjs
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { redactSensitiveText, stringifyNonErrorCause } from "../error-format.mjs";
|
||||||
|
//#region src/runtime/errors.ts
|
||||||
|
const ACP_ERROR_CODES = [
|
||||||
|
"ACP_BACKEND_MISSING",
|
||||||
|
"ACP_BACKEND_UNAVAILABLE",
|
||||||
|
"ACP_BACKEND_UNSUPPORTED_CONTROL",
|
||||||
|
"ACP_DISPATCH_DISABLED",
|
||||||
|
"ACP_INVALID_RUNTIME_OPTION",
|
||||||
|
"ACP_SESSION_INIT_FAILED",
|
||||||
|
"ACP_TURN_FAILED"
|
||||||
|
];
|
||||||
|
const ACP_ERROR_CODE_SET = new Set(ACP_ERROR_CODES);
|
||||||
|
var AcpRuntimeError = class extends Error {
|
||||||
|
constructor(code, message, options) {
|
||||||
|
super(message);
|
||||||
|
this.name = "AcpRuntimeError";
|
||||||
|
this.code = code;
|
||||||
|
this.cause = options?.cause;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function getForeignAcpRuntimeError(value) {
|
||||||
|
if (!(value instanceof Error)) return null;
|
||||||
|
const code = value.code;
|
||||||
|
if (typeof code !== "string" || !ACP_ERROR_CODE_SET.has(code)) return null;
|
||||||
|
return {
|
||||||
|
code,
|
||||||
|
message: value.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function readAcpRequestErrorDetails(value) {
|
||||||
|
if (typeof value.code !== "number") return;
|
||||||
|
const data = value.data;
|
||||||
|
if (!data || typeof data !== "object") return;
|
||||||
|
const details = data.details;
|
||||||
|
if (details === void 0 || details === null) return;
|
||||||
|
const rendered = redactSensitiveText(stringifyNonErrorCause(details)).trim();
|
||||||
|
return rendered.length > 0 ? rendered : void 0;
|
||||||
|
}
|
||||||
|
function messageWithAcpRequestErrorDetails(error) {
|
||||||
|
const details = readAcpRequestErrorDetails(error);
|
||||||
|
if (!details || error.message.includes(details)) return error.message;
|
||||||
|
return `${error.message}: ${details}`;
|
||||||
|
}
|
||||||
|
function isAcpRuntimeError(value) {
|
||||||
|
return value instanceof AcpRuntimeError || getForeignAcpRuntimeError(value) !== null;
|
||||||
|
}
|
||||||
|
function toAcpRuntimeError(params) {
|
||||||
|
if (params.error instanceof AcpRuntimeError) return params.error;
|
||||||
|
const foreignAcpRuntimeError = getForeignAcpRuntimeError(params.error);
|
||||||
|
if (foreignAcpRuntimeError) return new AcpRuntimeError(foreignAcpRuntimeError.code, foreignAcpRuntimeError.message, { cause: params.error });
|
||||||
|
if (params.error instanceof Error) return new AcpRuntimeError(params.fallbackCode, messageWithAcpRequestErrorDetails(params.error), { cause: params.error });
|
||||||
|
return new AcpRuntimeError(params.fallbackCode, params.fallbackMessage, { cause: params.error });
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Render an error and its `.cause` chain as a single human-readable line for
|
||||||
|
* logs, lifecycle events, and tool results. Format is
|
||||||
|
* `Name [code]: message <- Name [code]: message <- ...`. Number codes also
|
||||||
|
* appear, so JSON-RPC error codes like `-32603` survive into surfaces that
|
||||||
|
* downstream consumers see (gateway logs, telegram replies, tool_result text).
|
||||||
|
*
|
||||||
|
* Depth is capped to defend against self-referential `.cause` cycles.
|
||||||
|
*/
|
||||||
|
function formatAcpErrorChain(error) {
|
||||||
|
if (!(error instanceof Error)) return redactSensitiveText(String(error));
|
||||||
|
const segments = [renderSingleError(error)];
|
||||||
|
let current = error.cause;
|
||||||
|
let depth = 0;
|
||||||
|
while (current !== void 0 && current !== null && depth < 8) {
|
||||||
|
if (current instanceof Error) {
|
||||||
|
segments.push(renderSingleError(current));
|
||||||
|
current = current.cause;
|
||||||
|
} else {
|
||||||
|
segments.push(stringifyNonErrorCause(current));
|
||||||
|
current = void 0;
|
||||||
|
}
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
|
return redactSensitiveText(segments.join(" <- "));
|
||||||
|
}
|
||||||
|
function renderSingleError(error) {
|
||||||
|
const codeValue = error.code;
|
||||||
|
const codeSuffix = typeof codeValue === "string" || typeof codeValue === "number" ? ` [${codeValue}]` : "";
|
||||||
|
return `${error.name}${codeSuffix}: ${error.message}`;
|
||||||
|
}
|
||||||
|
async function withAcpRuntimeErrorBoundary(params) {
|
||||||
|
try {
|
||||||
|
return await params.run();
|
||||||
|
} catch (error) {
|
||||||
|
throw toAcpRuntimeError({
|
||||||
|
error,
|
||||||
|
fallbackCode: params.fallbackCode,
|
||||||
|
fallbackMessage: params.fallbackMessage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { ACP_ERROR_CODES, AcpRuntimeError, formatAcpErrorChain, isAcpRuntimeError, toAcpRuntimeError, withAcpRuntimeErrorBoundary };
|
||||||
21
packages/acp-core/dist/runtime/session-identifiers.d.mts
vendored
Normal file
21
packages/acp-core/dist/runtime/session-identifiers.d.mts
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { SessionAcpIdentity, SessionAcpMeta } from "../types.mjs";
|
||||||
|
|
||||||
|
//#region src/runtime/session-identifiers.d.ts
|
||||||
|
declare const ACP_SESSION_IDENTITY_RENDERER_VERSION = "v1";
|
||||||
|
type AcpSessionIdentifierRenderMode = "status" | "thread";
|
||||||
|
declare function resolveAcpSessionIdentifierLines(params: {
|
||||||
|
sessionKey: string;
|
||||||
|
meta?: SessionAcpMeta;
|
||||||
|
}): string[];
|
||||||
|
declare function resolveAcpSessionIdentifierLinesFromIdentity(params: {
|
||||||
|
backend: string;
|
||||||
|
identity?: SessionAcpIdentity;
|
||||||
|
mode?: AcpSessionIdentifierRenderMode;
|
||||||
|
}): string[];
|
||||||
|
declare function resolveAcpSessionCwd(meta?: SessionAcpMeta): string | undefined;
|
||||||
|
declare function resolveAcpThreadSessionDetailLines(params: {
|
||||||
|
sessionKey: string;
|
||||||
|
meta?: SessionAcpMeta;
|
||||||
|
}): string[];
|
||||||
|
//#endregion
|
||||||
|
export { ACP_SESSION_IDENTITY_RENDERER_VERSION, AcpSessionIdentifierRenderMode, resolveAcpSessionCwd, resolveAcpSessionIdentifierLines, resolveAcpSessionIdentifierLinesFromIdentity, resolveAcpThreadSessionDetailLines };
|
||||||
72
packages/acp-core/dist/runtime/session-identifiers.mjs
vendored
Normal file
72
packages/acp-core/dist/runtime/session-identifiers.mjs
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { normalizeText } from "../normalize-text.mjs";
|
||||||
|
import { isSessionIdentityPending, resolveSessionIdentityFromMeta } from "./session-identity.mjs";
|
||||||
|
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
//#region src/runtime/session-identifiers.ts
|
||||||
|
const ACP_SESSION_IDENTITY_RENDERER_VERSION = "v1";
|
||||||
|
const ACP_AGENT_RESUME_HINT_BY_KEY = new Map([
|
||||||
|
["codex", ({ agentSessionId }) => `resume in Codex CLI: \`codex resume ${agentSessionId}\` (continues this conversation).`],
|
||||||
|
["openai", ({ agentSessionId }) => `resume in Codex CLI: \`codex resume ${agentSessionId}\` (continues this conversation).`],
|
||||||
|
["codex-cli", ({ agentSessionId }) => `resume in Codex CLI: \`codex resume ${agentSessionId}\` (continues this conversation).`],
|
||||||
|
["kimi", ({ agentSessionId }) => `resume in Kimi CLI: \`kimi resume ${agentSessionId}\` (continues this conversation).`],
|
||||||
|
["moonshot-kimi", ({ agentSessionId }) => `resume in Kimi CLI: \`kimi resume ${agentSessionId}\` (continues this conversation).`]
|
||||||
|
]);
|
||||||
|
function normalizeAgentHintKey(value) {
|
||||||
|
const normalized = normalizeText(value);
|
||||||
|
if (!normalized) return;
|
||||||
|
return normalizeLowercaseStringOrEmpty(normalized).replace(/[\s_]+/g, "-");
|
||||||
|
}
|
||||||
|
function resolveAcpAgentResumeHintLine(params) {
|
||||||
|
const agentSessionId = normalizeText(params.agentSessionId);
|
||||||
|
const agentKey = normalizeAgentHintKey(params.agentId);
|
||||||
|
if (!agentSessionId || !agentKey) return;
|
||||||
|
const resolver = ACP_AGENT_RESUME_HINT_BY_KEY.get(agentKey);
|
||||||
|
return resolver ? resolver({ agentSessionId }) : void 0;
|
||||||
|
}
|
||||||
|
function resolveAcpSessionIdentifierLines(params) {
|
||||||
|
return resolveAcpSessionIdentifierLinesFromIdentity({
|
||||||
|
backend: normalizeText(params.meta?.backend) ?? "backend",
|
||||||
|
identity: resolveSessionIdentityFromMeta(params.meta),
|
||||||
|
mode: "status"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function resolveAcpSessionIdentifierLinesFromIdentity(params) {
|
||||||
|
const backend = normalizeText(params.backend) ?? "backend";
|
||||||
|
const mode = params.mode ?? "status";
|
||||||
|
const identity = params.identity;
|
||||||
|
const agentSessionId = normalizeText(identity?.agentSessionId);
|
||||||
|
const acpxSessionId = normalizeText(identity?.acpxSessionId);
|
||||||
|
const acpxRecordId = normalizeText(identity?.acpxRecordId);
|
||||||
|
const hasIdentifier = Boolean(agentSessionId || acpxSessionId || acpxRecordId);
|
||||||
|
if (isSessionIdentityPending(identity) && hasIdentifier) {
|
||||||
|
if (mode === "status") return ["session ids: pending (available after the first reply)"];
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const lines = [];
|
||||||
|
if (agentSessionId) lines.push(`agent session id: ${agentSessionId}`);
|
||||||
|
if (acpxSessionId) lines.push(`${backend} session id: ${acpxSessionId}`);
|
||||||
|
if (acpxRecordId) lines.push(`${backend} record id: ${acpxRecordId}`);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
function resolveAcpSessionCwd(meta) {
|
||||||
|
const runtimeCwd = normalizeText(meta?.runtimeOptions?.cwd);
|
||||||
|
if (runtimeCwd) return runtimeCwd;
|
||||||
|
return normalizeText(meta?.cwd);
|
||||||
|
}
|
||||||
|
function resolveAcpThreadSessionDetailLines(params) {
|
||||||
|
const meta = params.meta;
|
||||||
|
const identity = resolveSessionIdentityFromMeta(meta);
|
||||||
|
const lines = resolveAcpSessionIdentifierLinesFromIdentity({
|
||||||
|
backend: normalizeText(meta?.backend) ?? "backend",
|
||||||
|
identity,
|
||||||
|
mode: "thread"
|
||||||
|
});
|
||||||
|
if (lines.length === 0) return lines;
|
||||||
|
const hint = resolveAcpAgentResumeHintLine({
|
||||||
|
agentId: meta?.agent,
|
||||||
|
agentSessionId: identity?.agentSessionId
|
||||||
|
});
|
||||||
|
if (hint) lines.push(hint);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { ACP_SESSION_IDENTITY_RENDERER_VERSION, resolveAcpSessionCwd, resolveAcpSessionIdentifierLines, resolveAcpSessionIdentifierLinesFromIdentity, resolveAcpThreadSessionDetailLines };
|
||||||
32
packages/acp-core/dist/runtime/session-identity.d.mts
vendored
Normal file
32
packages/acp-core/dist/runtime/session-identity.d.mts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { SessionAcpIdentity, SessionAcpMeta } from "../types.mjs";
|
||||||
|
import { AcpRuntimeHandle, AcpRuntimeStatus } from "./types.mjs";
|
||||||
|
|
||||||
|
//#region src/runtime/session-identity.d.ts
|
||||||
|
declare function resolveSessionIdentityFromMeta(meta: SessionAcpMeta | undefined): SessionAcpIdentity | undefined;
|
||||||
|
declare function identityHasStableSessionId(identity: SessionAcpIdentity | undefined): boolean;
|
||||||
|
declare function resolveRuntimeResumeSessionId(identity: SessionAcpIdentity | undefined): string | undefined;
|
||||||
|
declare function isSessionIdentityPending(identity: SessionAcpIdentity | undefined): boolean;
|
||||||
|
declare function identityEquals(left: SessionAcpIdentity | undefined, right: SessionAcpIdentity | undefined): boolean;
|
||||||
|
declare function mergeSessionIdentity(params: {
|
||||||
|
current: SessionAcpIdentity | undefined;
|
||||||
|
incoming: SessionAcpIdentity | undefined;
|
||||||
|
now: number;
|
||||||
|
}): SessionAcpIdentity | undefined;
|
||||||
|
declare function createIdentityFromEnsure(params: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
now: number;
|
||||||
|
}): SessionAcpIdentity | undefined;
|
||||||
|
declare function createIdentityFromHandleEvent(params: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
now: number;
|
||||||
|
}): SessionAcpIdentity | undefined;
|
||||||
|
declare function createIdentityFromStatus(params: {
|
||||||
|
status: AcpRuntimeStatus | undefined;
|
||||||
|
now: number;
|
||||||
|
}): SessionAcpIdentity | undefined;
|
||||||
|
declare function resolveRuntimeHandleIdentifiersFromIdentity(identity: SessionAcpIdentity | undefined): {
|
||||||
|
backendSessionId?: string;
|
||||||
|
agentSessionId?: string;
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
export { createIdentityFromEnsure, createIdentityFromHandleEvent, createIdentityFromStatus, identityEquals, identityHasStableSessionId, isSessionIdentityPending, mergeSessionIdentity, resolveRuntimeHandleIdentifiersFromIdentity, resolveRuntimeResumeSessionId, resolveSessionIdentityFromMeta };
|
||||||
139
packages/acp-core/dist/runtime/session-identity.mjs
vendored
Normal file
139
packages/acp-core/dist/runtime/session-identity.mjs
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { normalizeText } from "../normalize-text.mjs";
|
||||||
|
//#region src/runtime/session-identity.ts
|
||||||
|
function normalizeIdentityState(value) {
|
||||||
|
if (value !== "pending" && value !== "resolved") return;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function normalizeIdentitySource(value) {
|
||||||
|
if (value !== "ensure" && value !== "status" && value !== "event") return;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function normalizeIdentity(identity) {
|
||||||
|
if (!identity) return;
|
||||||
|
const state = normalizeIdentityState(identity.state);
|
||||||
|
const source = normalizeIdentitySource(identity.source);
|
||||||
|
const acpxRecordId = normalizeText(identity.acpxRecordId);
|
||||||
|
const acpxSessionId = normalizeText(identity.acpxSessionId);
|
||||||
|
const agentSessionId = normalizeText(identity.agentSessionId);
|
||||||
|
const lastUpdatedAt = typeof identity.lastUpdatedAt === "number" && Number.isFinite(identity.lastUpdatedAt) ? identity.lastUpdatedAt : void 0;
|
||||||
|
if (!state && !source && !Boolean(acpxRecordId || acpxSessionId || agentSessionId) && lastUpdatedAt === void 0) return;
|
||||||
|
return {
|
||||||
|
state: state ?? (Boolean(acpxSessionId || agentSessionId) ? "resolved" : "pending"),
|
||||||
|
...acpxRecordId ? { acpxRecordId } : {},
|
||||||
|
...acpxSessionId ? { acpxSessionId } : {},
|
||||||
|
...agentSessionId ? { agentSessionId } : {},
|
||||||
|
source: source ?? "status",
|
||||||
|
lastUpdatedAt: lastUpdatedAt ?? Date.now()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function readIdentityIdsFromHandle(handle) {
|
||||||
|
return {
|
||||||
|
acpxRecordId: normalizeText(handle.acpxRecordId),
|
||||||
|
acpxSessionId: normalizeText(handle.backendSessionId),
|
||||||
|
agentSessionId: normalizeText(handle.agentSessionId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function buildSessionIdentity(params) {
|
||||||
|
const { acpxRecordId, acpxSessionId, agentSessionId } = params.ids;
|
||||||
|
if (!acpxRecordId && !acpxSessionId && !agentSessionId) return;
|
||||||
|
return {
|
||||||
|
state: params.state,
|
||||||
|
...acpxRecordId ? { acpxRecordId } : {},
|
||||||
|
...acpxSessionId ? { acpxSessionId } : {},
|
||||||
|
...agentSessionId ? { agentSessionId } : {},
|
||||||
|
source: params.source,
|
||||||
|
lastUpdatedAt: params.now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function resolveSessionIdentityFromMeta(meta) {
|
||||||
|
if (!meta) return;
|
||||||
|
return normalizeIdentity(meta.identity);
|
||||||
|
}
|
||||||
|
function identityHasStableSessionId(identity) {
|
||||||
|
return Boolean(identity?.acpxSessionId || identity?.agentSessionId);
|
||||||
|
}
|
||||||
|
function resolveRuntimeResumeSessionId(identity) {
|
||||||
|
if (!identity) return;
|
||||||
|
return normalizeText(identity.agentSessionId) ?? normalizeText(identity.acpxSessionId);
|
||||||
|
}
|
||||||
|
function isSessionIdentityPending(identity) {
|
||||||
|
if (!identity) return true;
|
||||||
|
return identity.state === "pending";
|
||||||
|
}
|
||||||
|
function identityEquals(left, right) {
|
||||||
|
const a = normalizeIdentity(left);
|
||||||
|
const b = normalizeIdentity(right);
|
||||||
|
if (!a && !b) return true;
|
||||||
|
if (!a || !b) return false;
|
||||||
|
return a.state === b.state && a.acpxRecordId === b.acpxRecordId && a.acpxSessionId === b.acpxSessionId && a.agentSessionId === b.agentSessionId && a.source === b.source;
|
||||||
|
}
|
||||||
|
function mergeSessionIdentity(params) {
|
||||||
|
const current = normalizeIdentity(params.current);
|
||||||
|
const incoming = normalizeIdentity(params.incoming);
|
||||||
|
if (!current) {
|
||||||
|
if (!incoming) return;
|
||||||
|
return {
|
||||||
|
...incoming,
|
||||||
|
lastUpdatedAt: params.now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!incoming) return current;
|
||||||
|
const currentResolved = current.state === "resolved";
|
||||||
|
const incomingResolved = incoming.state === "resolved";
|
||||||
|
const allowIncomingValue = !currentResolved || incomingResolved;
|
||||||
|
const nextRecordId = allowIncomingValue && incoming.acpxRecordId ? incoming.acpxRecordId : current.acpxRecordId;
|
||||||
|
const nextAcpxSessionId = allowIncomingValue && incoming.acpxSessionId ? incoming.acpxSessionId : current.acpxSessionId;
|
||||||
|
const nextAgentSessionId = allowIncomingValue && incoming.agentSessionId ? incoming.agentSessionId : current.agentSessionId;
|
||||||
|
const nextState = Boolean(nextAcpxSessionId || nextAgentSessionId) ? "resolved" : currentResolved ? "resolved" : incoming.state;
|
||||||
|
const nextSource = allowIncomingValue ? incoming.source : current.source;
|
||||||
|
return {
|
||||||
|
state: nextState,
|
||||||
|
...nextRecordId ? { acpxRecordId: nextRecordId } : {},
|
||||||
|
...nextAcpxSessionId ? { acpxSessionId: nextAcpxSessionId } : {},
|
||||||
|
...nextAgentSessionId ? { agentSessionId: nextAgentSessionId } : {},
|
||||||
|
source: nextSource,
|
||||||
|
lastUpdatedAt: params.now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function createIdentityFromEnsure(params) {
|
||||||
|
return buildSessionIdentity({
|
||||||
|
ids: readIdentityIdsFromHandle(params.handle),
|
||||||
|
state: "pending",
|
||||||
|
source: "ensure",
|
||||||
|
now: params.now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createIdentityFromHandleEvent(params) {
|
||||||
|
const ids = readIdentityIdsFromHandle(params.handle);
|
||||||
|
return buildSessionIdentity({
|
||||||
|
ids,
|
||||||
|
state: ids.agentSessionId ? "resolved" : "pending",
|
||||||
|
source: "event",
|
||||||
|
now: params.now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createIdentityFromStatus(params) {
|
||||||
|
if (!params.status) return;
|
||||||
|
const details = params.status.details;
|
||||||
|
const acpxRecordId = normalizeText(params.status.acpxRecordId) ?? normalizeText(details?.acpxRecordId);
|
||||||
|
const acpxSessionId = normalizeText(params.status.backendSessionId) ?? normalizeText(details?.backendSessionId) ?? normalizeText(details?.acpxSessionId);
|
||||||
|
const agentSessionId = normalizeText(params.status.agentSessionId) ?? normalizeText(details?.agentSessionId);
|
||||||
|
if (!acpxRecordId && !acpxSessionId && !agentSessionId) return;
|
||||||
|
return {
|
||||||
|
state: Boolean(acpxSessionId || agentSessionId) ? "resolved" : "pending",
|
||||||
|
...acpxRecordId ? { acpxRecordId } : {},
|
||||||
|
...acpxSessionId ? { acpxSessionId } : {},
|
||||||
|
...agentSessionId ? { agentSessionId } : {},
|
||||||
|
source: "status",
|
||||||
|
lastUpdatedAt: params.now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function resolveRuntimeHandleIdentifiersFromIdentity(identity) {
|
||||||
|
if (!identity) return {};
|
||||||
|
return {
|
||||||
|
...identity.acpxSessionId ? { backendSessionId: identity.acpxSessionId } : {},
|
||||||
|
...identity.agentSessionId ? { agentSessionId: identity.agentSessionId } : {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { createIdentityFromEnsure, createIdentityFromHandleEvent, createIdentityFromStatus, identityEquals, identityHasStableSessionId, isSessionIdentityPending, mergeSessionIdentity, resolveRuntimeHandleIdentifiersFromIdentity, resolveRuntimeResumeSessionId, resolveSessionIdentityFromMeta };
|
||||||
162
packages/acp-core/dist/runtime/types.d.mts
vendored
Normal file
162
packages/acp-core/dist/runtime/types.d.mts
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
//#region src/runtime/types.d.ts
|
||||||
|
type AcpRuntimePromptMode = "prompt" | "steer";
|
||||||
|
type AcpRuntimeSessionMode = "persistent" | "oneshot";
|
||||||
|
type AcpSessionUpdateTag = "agent_message_chunk" | "agent_thought_chunk" | "tool_call" | "tool_call_update" | "usage_update" | "available_commands_update" | "current_mode_update" | "config_option_update" | "session_info_update" | "plan" | (string & {});
|
||||||
|
type AcpRuntimeControl = "session/set_mode" | "session/set_config_option" | "session/status";
|
||||||
|
type AcpRuntimeHandle = {
|
||||||
|
sessionKey: string;
|
||||||
|
backend: string;
|
||||||
|
runtimeSessionName: string; /** Effective runtime working directory for this ACP session, if exposed by adapter/runtime. */
|
||||||
|
cwd?: string; /** Backend-local record identifier, if exposed by adapter/runtime (for example acpx record id). */
|
||||||
|
acpxRecordId?: string; /** Backend-level ACP session identifier, if exposed by adapter/runtime. */
|
||||||
|
backendSessionId?: string; /** Upstream harness session identifier, if exposed by adapter/runtime. */
|
||||||
|
agentSessionId?: string;
|
||||||
|
};
|
||||||
|
type AcpRuntimeEnsureInput = {
|
||||||
|
sessionKey: string;
|
||||||
|
agent: string;
|
||||||
|
mode: AcpRuntimeSessionMode;
|
||||||
|
resumeSessionId?: string; /** Optional runtime model override that must be available during session creation. */
|
||||||
|
model?: string; /** Optional runtime thinking/reasoning override that must be available during session creation. */
|
||||||
|
thinking?: string;
|
||||||
|
cwd?: string;
|
||||||
|
env?: Record<string, string>;
|
||||||
|
};
|
||||||
|
type AcpRuntimeTurnAttachment = {
|
||||||
|
mediaType: string;
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
type AcpRuntimeTurnInput = {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
text: string;
|
||||||
|
attachments?: AcpRuntimeTurnAttachment[];
|
||||||
|
mode: AcpRuntimePromptMode;
|
||||||
|
requestId: string;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
};
|
||||||
|
type AcpRuntimeCapabilities = {
|
||||||
|
controls: AcpRuntimeControl[];
|
||||||
|
/**
|
||||||
|
* Optional backend-advertised option keys for session/set_config_option.
|
||||||
|
* Empty/undefined means "backend accepts keys, but did not advertise a strict list".
|
||||||
|
*/
|
||||||
|
configOptionKeys?: string[];
|
||||||
|
};
|
||||||
|
type AcpRuntimeStatus = {
|
||||||
|
summary?: string; /** Backend-local record identifier, if exposed by adapter/runtime. */
|
||||||
|
acpxRecordId?: string; /** Backend-level ACP session identifier, if known at status time. */
|
||||||
|
backendSessionId?: string; /** Upstream harness session identifier, if known at status time. */
|
||||||
|
agentSessionId?: string;
|
||||||
|
details?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
type AcpRuntimeDoctorReport = {
|
||||||
|
ok: boolean;
|
||||||
|
code?: string;
|
||||||
|
message: string;
|
||||||
|
installCommand?: string;
|
||||||
|
details?: string[];
|
||||||
|
};
|
||||||
|
type AcpRuntimeEvent = {
|
||||||
|
type: "text_delta";
|
||||||
|
text: string;
|
||||||
|
stream?: "output" | "thought";
|
||||||
|
tag?: AcpSessionUpdateTag;
|
||||||
|
} | {
|
||||||
|
type: "status";
|
||||||
|
text: string;
|
||||||
|
tag?: AcpSessionUpdateTag;
|
||||||
|
used?: number;
|
||||||
|
size?: number;
|
||||||
|
} | {
|
||||||
|
type: "tool_call";
|
||||||
|
text: string;
|
||||||
|
tag?: AcpSessionUpdateTag;
|
||||||
|
toolCallId?: string;
|
||||||
|
status?: string;
|
||||||
|
title?: string;
|
||||||
|
} | {
|
||||||
|
type: "done";
|
||||||
|
stopReason?: string;
|
||||||
|
} | {
|
||||||
|
type: "error";
|
||||||
|
message: string;
|
||||||
|
code?: string;
|
||||||
|
detailCode?: string;
|
||||||
|
retryable?: boolean;
|
||||||
|
};
|
||||||
|
type AcpRuntimeTurnResultError = {
|
||||||
|
message: string;
|
||||||
|
code?: string;
|
||||||
|
detailCode?: string;
|
||||||
|
retryable?: boolean;
|
||||||
|
};
|
||||||
|
type AcpRuntimeTurnResult = {
|
||||||
|
status: "completed";
|
||||||
|
stopReason?: string;
|
||||||
|
} | {
|
||||||
|
status: "cancelled";
|
||||||
|
stopReason?: string;
|
||||||
|
} | {
|
||||||
|
status: "failed";
|
||||||
|
error: AcpRuntimeTurnResultError;
|
||||||
|
};
|
||||||
|
interface AcpRuntimeTurn {
|
||||||
|
readonly requestId: string;
|
||||||
|
readonly events: AsyncIterable<AcpRuntimeEvent>;
|
||||||
|
readonly result: Promise<AcpRuntimeTurnResult>;
|
||||||
|
cancel(input?: {
|
||||||
|
reason?: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
closeStream(input?: {
|
||||||
|
reason?: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
}
|
||||||
|
interface AcpRuntime {
|
||||||
|
ensureSession(input: AcpRuntimeEnsureInput): Promise<AcpRuntimeHandle>;
|
||||||
|
/**
|
||||||
|
* Preferred turn API. Live events are streamed separately from the terminal
|
||||||
|
* result so adapters can report failures without relying on legacy done/error
|
||||||
|
* events in the stream.
|
||||||
|
*/
|
||||||
|
startTurn?(input: AcpRuntimeTurnInput): AcpRuntimeTurn;
|
||||||
|
runTurn(input: AcpRuntimeTurnInput): AsyncIterable<AcpRuntimeEvent>;
|
||||||
|
getCapabilities?(input: {
|
||||||
|
handle?: AcpRuntimeHandle;
|
||||||
|
}): Promise<AcpRuntimeCapabilities> | AcpRuntimeCapabilities;
|
||||||
|
getStatus?(input: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}): Promise<AcpRuntimeStatus>;
|
||||||
|
setMode?(input: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
mode: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
setConfigOption?(input: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
doctor?(): Promise<AcpRuntimeDoctorReport>;
|
||||||
|
/**
|
||||||
|
* Prepare the next ensureSession for this session key to start fresh instead
|
||||||
|
* of reopening backend-owned persistent state.
|
||||||
|
*/
|
||||||
|
prepareFreshSession?(input: {
|
||||||
|
sessionKey: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
cancel(input: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
reason?: string;
|
||||||
|
}): Promise<void>;
|
||||||
|
close(input: {
|
||||||
|
handle: AcpRuntimeHandle;
|
||||||
|
reason: string;
|
||||||
|
/**
|
||||||
|
* Discard backend-owned persistent session state so the next ensureSession
|
||||||
|
* starts fresh instead of reopening the same conversation.
|
||||||
|
*/
|
||||||
|
discardPersistentState?: boolean;
|
||||||
|
}): Promise<void>;
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { AcpRuntime, AcpRuntimeCapabilities, AcpRuntimeControl, AcpRuntimeDoctorReport, AcpRuntimeEnsureInput, AcpRuntimeEvent, AcpRuntimeHandle, AcpRuntimePromptMode, AcpRuntimeSessionMode, AcpRuntimeStatus, AcpRuntimeTurn, AcpRuntimeTurnAttachment, AcpRuntimeTurnInput, AcpRuntimeTurnResult, AcpRuntimeTurnResultError, AcpSessionUpdateTag };
|
||||||
1
packages/acp-core/dist/runtime/types.mjs
vendored
Normal file
1
packages/acp-core/dist/runtime/types.mjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
21
packages/acp-core/dist/session-interaction-mode.d.mts
vendored
Normal file
21
packages/acp-core/dist/session-interaction-mode.d.mts
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//#region src/session-interaction-mode.d.ts
|
||||||
|
type SessionInteractionEntry = {
|
||||||
|
spawnedBy?: string;
|
||||||
|
parentSessionKey?: string;
|
||||||
|
acp?: unknown;
|
||||||
|
};
|
||||||
|
declare function isParentOwnedBackgroundAcpSession(entry?: SessionInteractionEntry | null): boolean;
|
||||||
|
/**
|
||||||
|
* Returns true when `entry` is a parent-owned background ACP session AND the
|
||||||
|
* given `requesterSessionKey` is the session that spawned/owns it. This is a
|
||||||
|
* strictly narrower check than {@link isParentOwnedBackgroundAcpSession}: the
|
||||||
|
* target must match *and* the caller must be the parent.
|
||||||
|
*
|
||||||
|
* Used to gate behaviors that only make sense for the parent↔own-child pair
|
||||||
|
* (e.g. skipping the A2A ping-pong flow in `sessions_send`), so that an
|
||||||
|
* unrelated session with broad visibility (e.g. `tools.sessions.visibility=all`)
|
||||||
|
* sending to the same target is still routed through the normal A2A path.
|
||||||
|
*/
|
||||||
|
declare function isRequesterParentOfBackgroundAcpSession(entry: SessionInteractionEntry | null | undefined, requesterSessionKey: string | null | undefined): boolean;
|
||||||
|
//#endregion
|
||||||
|
export { isParentOwnedBackgroundAcpSession, isRequesterParentOfBackgroundAcpSession };
|
||||||
31
packages/acp-core/dist/session-interaction-mode.mjs
vendored
Normal file
31
packages/acp-core/dist/session-interaction-mode.mjs
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
//#region src/session-interaction-mode.ts
|
||||||
|
function resolveAcpSessionInteractionMode(entry) {
|
||||||
|
if (!entry?.acp) return "interactive";
|
||||||
|
if (normalizeOptionalString(entry.spawnedBy) || normalizeOptionalString(entry.parentSessionKey)) return "parent-owned-background";
|
||||||
|
return "interactive";
|
||||||
|
}
|
||||||
|
function isParentOwnedBackgroundAcpSession(entry) {
|
||||||
|
return resolveAcpSessionInteractionMode(entry) === "parent-owned-background";
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns true when `entry` is a parent-owned background ACP session AND the
|
||||||
|
* given `requesterSessionKey` is the session that spawned/owns it. This is a
|
||||||
|
* strictly narrower check than {@link isParentOwnedBackgroundAcpSession}: the
|
||||||
|
* target must match *and* the caller must be the parent.
|
||||||
|
*
|
||||||
|
* Used to gate behaviors that only make sense for the parent↔own-child pair
|
||||||
|
* (e.g. skipping the A2A ping-pong flow in `sessions_send`), so that an
|
||||||
|
* unrelated session with broad visibility (e.g. `tools.sessions.visibility=all`)
|
||||||
|
* sending to the same target is still routed through the normal A2A path.
|
||||||
|
*/
|
||||||
|
function isRequesterParentOfBackgroundAcpSession(entry, requesterSessionKey) {
|
||||||
|
if (!isParentOwnedBackgroundAcpSession(entry)) return false;
|
||||||
|
const requester = normalizeOptionalString(requesterSessionKey);
|
||||||
|
if (!requester) return false;
|
||||||
|
const spawnedBy = normalizeOptionalString(entry?.spawnedBy);
|
||||||
|
const parentSessionKey = normalizeOptionalString(entry?.parentSessionKey);
|
||||||
|
return requester === spawnedBy || requester === parentSessionKey;
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { isParentOwnedBackgroundAcpSession, isRequesterParentOfBackgroundAcpSession };
|
||||||
32
packages/acp-core/dist/session-lineage-meta.d.mts
vendored
Normal file
32
packages/acp-core/dist/session-lineage-meta.d.mts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//#region src/session-lineage-meta.d.ts
|
||||||
|
declare const SUBAGENT_ROLES: readonly ["orchestrator", "leaf"];
|
||||||
|
declare const SUBAGENT_CONTROL_SCOPES: readonly ["children", "none"];
|
||||||
|
type SubagentRole = (typeof SUBAGENT_ROLES)[number];
|
||||||
|
type SubagentControlScope = (typeof SUBAGENT_CONTROL_SCOPES)[number];
|
||||||
|
type AcpSessionLineageMeta = {
|
||||||
|
sessionKey: string;
|
||||||
|
kind?: string;
|
||||||
|
channel?: string;
|
||||||
|
parentSessionId?: string;
|
||||||
|
spawnedBy?: string;
|
||||||
|
spawnDepth?: number;
|
||||||
|
subagentRole?: SubagentRole;
|
||||||
|
subagentControlScope?: SubagentControlScope;
|
||||||
|
spawnedWorkspaceDir?: string;
|
||||||
|
spawnedCwd?: string;
|
||||||
|
};
|
||||||
|
type AcpSessionLineageRow = {
|
||||||
|
key: string;
|
||||||
|
kind?: string;
|
||||||
|
channel?: string;
|
||||||
|
parentSessionKey?: string;
|
||||||
|
spawnedBy?: string;
|
||||||
|
spawnDepth?: number;
|
||||||
|
subagentRole?: string;
|
||||||
|
subagentControlScope?: string;
|
||||||
|
spawnedWorkspaceDir?: string;
|
||||||
|
spawnedCwd?: string;
|
||||||
|
};
|
||||||
|
declare function toAcpSessionLineageMeta(row: AcpSessionLineageRow): AcpSessionLineageMeta;
|
||||||
|
//#endregion
|
||||||
|
export { AcpSessionLineageMeta, AcpSessionLineageRow, toAcpSessionLineageMeta };
|
||||||
38
packages/acp-core/dist/session-lineage-meta.mjs
vendored
Normal file
38
packages/acp-core/dist/session-lineage-meta.mjs
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
//#region src/session-lineage-meta.ts
|
||||||
|
const SUBAGENT_ROLES = ["orchestrator", "leaf"];
|
||||||
|
const SUBAGENT_CONTROL_SCOPES = ["children", "none"];
|
||||||
|
function readInteger(value) {
|
||||||
|
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) return;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function readEnum(value, allowed) {
|
||||||
|
const normalized = normalizeOptionalString(value);
|
||||||
|
return allowed.find((candidate) => candidate === normalized);
|
||||||
|
}
|
||||||
|
function toAcpSessionLineageMeta(row) {
|
||||||
|
const sessionKey = normalizeOptionalString(row.key) ?? row.key;
|
||||||
|
const kind = normalizeOptionalString(row.kind);
|
||||||
|
const channel = normalizeOptionalString(row.channel);
|
||||||
|
const parentSessionId = normalizeOptionalString(row.parentSessionKey) ?? normalizeOptionalString(row.spawnedBy);
|
||||||
|
const spawnedBy = normalizeOptionalString(row.spawnedBy);
|
||||||
|
const spawnDepth = readInteger(row.spawnDepth);
|
||||||
|
const subagentRole = readEnum(row.subagentRole, SUBAGENT_ROLES);
|
||||||
|
const subagentControlScope = readEnum(row.subagentControlScope, SUBAGENT_CONTROL_SCOPES);
|
||||||
|
const spawnedWorkspaceDir = normalizeOptionalString(row.spawnedWorkspaceDir);
|
||||||
|
const spawnedCwd = normalizeOptionalString(row.spawnedCwd);
|
||||||
|
return {
|
||||||
|
sessionKey,
|
||||||
|
...kind ? { kind } : {},
|
||||||
|
...channel ? { channel } : {},
|
||||||
|
...parentSessionId ? { parentSessionId } : {},
|
||||||
|
...spawnedBy ? { spawnedBy } : {},
|
||||||
|
...spawnDepth !== void 0 ? { spawnDepth } : {},
|
||||||
|
...subagentRole ? { subagentRole } : {},
|
||||||
|
...subagentControlScope ? { subagentControlScope } : {},
|
||||||
|
...spawnedWorkspaceDir ? { spawnedWorkspaceDir } : {},
|
||||||
|
...spawnedCwd ? { spawnedCwd } : {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { toAcpSessionLineageMeta };
|
||||||
28
packages/acp-core/dist/session.d.mts
vendored
Normal file
28
packages/acp-core/dist/session.d.mts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { AcpSession } from "./types.mjs";
|
||||||
|
|
||||||
|
//#region src/session.d.ts
|
||||||
|
type AcpSessionStore = {
|
||||||
|
createSession: (params: {
|
||||||
|
sessionKey: string;
|
||||||
|
cwd: string;
|
||||||
|
sessionId?: string;
|
||||||
|
ledgerSessionId?: string;
|
||||||
|
}) => AcpSession;
|
||||||
|
hasSession: (sessionId: string) => boolean;
|
||||||
|
getSession: (sessionId: string) => AcpSession | undefined;
|
||||||
|
getSessionByRunId: (runId: string) => AcpSession | undefined;
|
||||||
|
setActiveRun: (sessionId: string, runId: string, abortController: AbortController) => void;
|
||||||
|
clearActiveRun: (sessionId: string) => void;
|
||||||
|
cancelActiveRun: (sessionId: string) => boolean;
|
||||||
|
deleteSession: (sessionId: string) => boolean;
|
||||||
|
clearAllSessionsForTest: () => void;
|
||||||
|
};
|
||||||
|
type AcpSessionStoreOptions = {
|
||||||
|
maxSessions?: number;
|
||||||
|
idleTtlMs?: number;
|
||||||
|
now?: () => number;
|
||||||
|
};
|
||||||
|
declare function createInMemorySessionStore(options?: AcpSessionStoreOptions): AcpSessionStore;
|
||||||
|
declare const defaultAcpSessionStore: AcpSessionStore;
|
||||||
|
//#endregion
|
||||||
|
export { AcpSessionStore, createInMemorySessionStore, defaultAcpSessionStore };
|
||||||
128
packages/acp-core/dist/session.mjs
vendored
Normal file
128
packages/acp-core/dist/session.mjs
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { resolveIntegerOption } from "./numeric-options.mjs";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
//#region src/session.ts
|
||||||
|
const DEFAULT_MAX_SESSIONS = 5e3;
|
||||||
|
const DEFAULT_IDLE_TTL_MS = 1440 * 60 * 1e3;
|
||||||
|
function createInMemorySessionStore(options = {}) {
|
||||||
|
const maxSessions = resolveIntegerOption(options.maxSessions, DEFAULT_MAX_SESSIONS, { min: 1 });
|
||||||
|
const idleTtlMs = resolveIntegerOption(options.idleTtlMs, DEFAULT_IDLE_TTL_MS, { min: 1e3 });
|
||||||
|
const now = options.now ?? Date.now;
|
||||||
|
const sessions = /* @__PURE__ */ new Map();
|
||||||
|
const runIdToSessionId = /* @__PURE__ */ new Map();
|
||||||
|
const touchSession = (session, nowMs) => {
|
||||||
|
session.lastTouchedAt = nowMs;
|
||||||
|
};
|
||||||
|
const removeSession = (sessionId) => {
|
||||||
|
const session = sessions.get(sessionId);
|
||||||
|
if (!session) return false;
|
||||||
|
if (session.activeRunId) runIdToSessionId.delete(session.activeRunId);
|
||||||
|
session.abortController?.abort();
|
||||||
|
sessions.delete(sessionId);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const reapIdleSessions = (nowMs) => {
|
||||||
|
const idleBefore = nowMs - idleTtlMs;
|
||||||
|
for (const [sessionId, session] of sessions.entries()) {
|
||||||
|
if (session.activeRunId || session.abortController) continue;
|
||||||
|
if (session.lastTouchedAt > idleBefore) continue;
|
||||||
|
removeSession(sessionId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const evictOldestIdleSession = () => {
|
||||||
|
let oldestSessionId = null;
|
||||||
|
let oldestLastTouchedAt = Number.POSITIVE_INFINITY;
|
||||||
|
for (const [sessionId, session] of sessions.entries()) {
|
||||||
|
if (session.activeRunId || session.abortController) continue;
|
||||||
|
if (session.lastTouchedAt >= oldestLastTouchedAt) continue;
|
||||||
|
oldestLastTouchedAt = session.lastTouchedAt;
|
||||||
|
oldestSessionId = sessionId;
|
||||||
|
}
|
||||||
|
if (!oldestSessionId) return false;
|
||||||
|
return removeSession(oldestSessionId);
|
||||||
|
};
|
||||||
|
const createSession = (params) => {
|
||||||
|
const nowMs = now();
|
||||||
|
const sessionId = params.sessionId ?? randomUUID();
|
||||||
|
const existingSession = sessions.get(sessionId);
|
||||||
|
if (existingSession) {
|
||||||
|
existingSession.sessionKey = params.sessionKey;
|
||||||
|
if ("ledgerSessionId" in params) existingSession.ledgerSessionId = params.ledgerSessionId;
|
||||||
|
existingSession.cwd = params.cwd;
|
||||||
|
touchSession(existingSession, nowMs);
|
||||||
|
return existingSession;
|
||||||
|
}
|
||||||
|
reapIdleSessions(nowMs);
|
||||||
|
if (sessions.size >= maxSessions && !evictOldestIdleSession()) throw new Error(`ACP session limit reached (max ${maxSessions}). Close idle ACP clients and retry.`);
|
||||||
|
const session = {
|
||||||
|
sessionId,
|
||||||
|
sessionKey: params.sessionKey,
|
||||||
|
...params.ledgerSessionId ? { ledgerSessionId: params.ledgerSessionId } : {},
|
||||||
|
cwd: params.cwd,
|
||||||
|
createdAt: nowMs,
|
||||||
|
lastTouchedAt: nowMs,
|
||||||
|
abortController: null,
|
||||||
|
activeRunId: null
|
||||||
|
};
|
||||||
|
sessions.set(sessionId, session);
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
const hasSession = (sessionId) => sessions.has(sessionId);
|
||||||
|
const getSession = (sessionId) => {
|
||||||
|
const session = sessions.get(sessionId);
|
||||||
|
if (session) touchSession(session, now());
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
const getSessionByRunId = (runId) => {
|
||||||
|
const sessionId = runIdToSessionId.get(runId);
|
||||||
|
if (!sessionId) return;
|
||||||
|
const session = sessions.get(sessionId);
|
||||||
|
if (session) touchSession(session, now());
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
const setActiveRun = (sessionId, runId, abortController) => {
|
||||||
|
const session = sessions.get(sessionId);
|
||||||
|
if (!session) return;
|
||||||
|
session.activeRunId = runId;
|
||||||
|
session.abortController = abortController;
|
||||||
|
runIdToSessionId.set(runId, sessionId);
|
||||||
|
touchSession(session, now());
|
||||||
|
};
|
||||||
|
const clearActiveRun = (sessionId) => {
|
||||||
|
const session = sessions.get(sessionId);
|
||||||
|
if (!session) return;
|
||||||
|
if (session.activeRunId) runIdToSessionId.delete(session.activeRunId);
|
||||||
|
session.activeRunId = null;
|
||||||
|
session.abortController = null;
|
||||||
|
touchSession(session, now());
|
||||||
|
};
|
||||||
|
const cancelActiveRun = (sessionId) => {
|
||||||
|
const session = sessions.get(sessionId);
|
||||||
|
if (!session?.abortController) return false;
|
||||||
|
session.abortController.abort();
|
||||||
|
if (session.activeRunId) runIdToSessionId.delete(session.activeRunId);
|
||||||
|
session.abortController = null;
|
||||||
|
session.activeRunId = null;
|
||||||
|
touchSession(session, now());
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const deleteSession = (sessionId) => removeSession(sessionId);
|
||||||
|
const clearAllSessionsForTest = () => {
|
||||||
|
for (const session of sessions.values()) session.abortController?.abort();
|
||||||
|
sessions.clear();
|
||||||
|
runIdToSessionId.clear();
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
createSession,
|
||||||
|
hasSession,
|
||||||
|
getSession,
|
||||||
|
getSessionByRunId,
|
||||||
|
setActiveRun,
|
||||||
|
clearActiveRun,
|
||||||
|
cancelActiveRun,
|
||||||
|
deleteSession,
|
||||||
|
clearAllSessionsForTest
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const defaultAcpSessionStore = createInMemorySessionStore();
|
||||||
|
//#endregion
|
||||||
|
export { createInMemorySessionStore, defaultAcpSessionStore };
|
||||||
67
packages/acp-core/dist/types.d.mts
vendored
Normal file
67
packages/acp-core/dist/types.d.mts
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
//#region src/types.d.ts
|
||||||
|
declare const ACP_PROVENANCE_MODE_VALUES: readonly ["off", "meta", "meta+receipt"];
|
||||||
|
type SessionId = string;
|
||||||
|
type AcpProvenanceMode = (typeof ACP_PROVENANCE_MODE_VALUES)[number];
|
||||||
|
declare function normalizeAcpProvenanceMode(value: string | undefined): AcpProvenanceMode | undefined;
|
||||||
|
type AcpSession = {
|
||||||
|
sessionId: SessionId;
|
||||||
|
sessionKey: string;
|
||||||
|
ledgerSessionId?: string;
|
||||||
|
cwd: string;
|
||||||
|
createdAt: number;
|
||||||
|
lastTouchedAt: number;
|
||||||
|
abortController: AbortController | null;
|
||||||
|
activeRunId: string | null;
|
||||||
|
};
|
||||||
|
type AcpServerOptions = {
|
||||||
|
gatewayUrl?: string;
|
||||||
|
gatewayToken?: string;
|
||||||
|
gatewayPassword?: string;
|
||||||
|
defaultSessionKey?: string;
|
||||||
|
defaultSessionLabel?: string;
|
||||||
|
requireExistingSession?: boolean;
|
||||||
|
resetSession?: boolean;
|
||||||
|
prefixCwd?: boolean;
|
||||||
|
provenanceMode?: AcpProvenanceMode;
|
||||||
|
sessionCreateRateLimit?: {
|
||||||
|
maxRequests?: number;
|
||||||
|
windowMs?: number;
|
||||||
|
};
|
||||||
|
verbose?: boolean;
|
||||||
|
};
|
||||||
|
type SessionAcpIdentitySource = "ensure" | "status" | "event";
|
||||||
|
type SessionAcpIdentityState = "pending" | "resolved";
|
||||||
|
type SessionAcpIdentity = {
|
||||||
|
state: SessionAcpIdentityState;
|
||||||
|
acpxRecordId?: string;
|
||||||
|
acpxSessionId?: string;
|
||||||
|
agentSessionId?: string;
|
||||||
|
source: SessionAcpIdentitySource;
|
||||||
|
lastUpdatedAt: number;
|
||||||
|
};
|
||||||
|
type AcpSessionRuntimeOptions = {
|
||||||
|
/**
|
||||||
|
* ACP runtime mode set via session/set_mode (for example: "plan", "normal", "auto").
|
||||||
|
*/
|
||||||
|
runtimeMode?: string; /** ACP runtime config option: model id. */
|
||||||
|
model?: string; /** ACP runtime config option: thinking/reasoning effort. */
|
||||||
|
thinking?: string; /** Working directory override for ACP session turns. */
|
||||||
|
cwd?: string; /** ACP runtime config option: permission profile id. */
|
||||||
|
permissionProfile?: string; /** ACP runtime config option: per-turn timeout in seconds. */
|
||||||
|
timeoutSeconds?: number; /** Backend-specific option bag mapped through session/set_config_option. */
|
||||||
|
backendExtras?: Record<string, string>;
|
||||||
|
};
|
||||||
|
type SessionAcpMeta = {
|
||||||
|
backend: string;
|
||||||
|
agent: string;
|
||||||
|
runtimeSessionName: string;
|
||||||
|
identity?: SessionAcpIdentity;
|
||||||
|
mode: "persistent" | "oneshot";
|
||||||
|
runtimeOptions?: AcpSessionRuntimeOptions;
|
||||||
|
cwd?: string;
|
||||||
|
state: "idle" | "running" | "error";
|
||||||
|
lastActivityAt: number;
|
||||||
|
lastError?: string;
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
export { AcpProvenanceMode, AcpServerOptions, AcpSession, AcpSessionRuntimeOptions, SessionAcpIdentity, SessionAcpIdentitySource, SessionAcpIdentityState, SessionAcpMeta, SessionId, normalizeAcpProvenanceMode };
|
||||||
14
packages/acp-core/dist/types.mjs
vendored
Normal file
14
packages/acp-core/dist/types.mjs
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
//#region src/types.ts
|
||||||
|
const ACP_PROVENANCE_MODE_VALUES = [
|
||||||
|
"off",
|
||||||
|
"meta",
|
||||||
|
"meta+receipt"
|
||||||
|
];
|
||||||
|
function normalizeAcpProvenanceMode(value) {
|
||||||
|
const normalized = normalizeOptionalLowercaseString(value);
|
||||||
|
if (!normalized) return;
|
||||||
|
return ACP_PROVENANCE_MODE_VALUES.includes(normalized) ? normalized : void 0;
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
export { normalizeAcpProvenanceMode };
|
||||||
@@ -19,11 +19,61 @@
|
|||||||
"import": "./dist/normalize-text.mjs",
|
"import": "./dist/normalize-text.mjs",
|
||||||
"default": "./dist/normalize-text.mjs"
|
"default": "./dist/normalize-text.mjs"
|
||||||
},
|
},
|
||||||
|
"./meta": {
|
||||||
|
"types": "./dist/meta.d.mts",
|
||||||
|
"import": "./dist/meta.mjs",
|
||||||
|
"default": "./dist/meta.mjs"
|
||||||
|
},
|
||||||
|
"./numeric-options": {
|
||||||
|
"types": "./dist/numeric-options.d.mts",
|
||||||
|
"import": "./dist/numeric-options.mjs",
|
||||||
|
"default": "./dist/numeric-options.mjs"
|
||||||
|
},
|
||||||
"./record-shared": {
|
"./record-shared": {
|
||||||
"types": "./dist/record-shared.d.mts",
|
"types": "./dist/record-shared.d.mts",
|
||||||
"import": "./dist/record-shared.mjs",
|
"import": "./dist/record-shared.mjs",
|
||||||
"default": "./dist/record-shared.mjs"
|
"default": "./dist/record-shared.mjs"
|
||||||
},
|
},
|
||||||
|
"./session": {
|
||||||
|
"types": "./dist/session.d.mts",
|
||||||
|
"import": "./dist/session.mjs",
|
||||||
|
"default": "./dist/session.mjs"
|
||||||
|
},
|
||||||
|
"./session-interaction-mode": {
|
||||||
|
"types": "./dist/session-interaction-mode.d.mts",
|
||||||
|
"import": "./dist/session-interaction-mode.mjs",
|
||||||
|
"default": "./dist/session-interaction-mode.mjs"
|
||||||
|
},
|
||||||
|
"./session-lineage-meta": {
|
||||||
|
"types": "./dist/session-lineage-meta.d.mts",
|
||||||
|
"import": "./dist/session-lineage-meta.mjs",
|
||||||
|
"default": "./dist/session-lineage-meta.mjs"
|
||||||
|
},
|
||||||
|
"./types": {
|
||||||
|
"types": "./dist/types.d.mts",
|
||||||
|
"import": "./dist/types.mjs",
|
||||||
|
"default": "./dist/types.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/error-text": {
|
||||||
|
"types": "./dist/runtime/error-text.d.mts",
|
||||||
|
"import": "./dist/runtime/error-text.mjs",
|
||||||
|
"default": "./dist/runtime/error-text.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/errors": {
|
||||||
|
"types": "./dist/runtime/errors.d.mts",
|
||||||
|
"import": "./dist/runtime/errors.mjs",
|
||||||
|
"default": "./dist/runtime/errors.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/session-identifiers": {
|
||||||
|
"types": "./dist/runtime/session-identifiers.d.mts",
|
||||||
|
"import": "./dist/runtime/session-identifiers.mjs",
|
||||||
|
"default": "./dist/runtime/session-identifiers.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/session-identity": {
|
||||||
|
"types": "./dist/runtime/session-identity.d.mts",
|
||||||
|
"import": "./dist/runtime/session-identity.mjs",
|
||||||
|
"default": "./dist/runtime/session-identity.mjs"
|
||||||
|
},
|
||||||
"./runtime/types": {
|
"./runtime/types": {
|
||||||
"types": "./dist/runtime/types.d.mts",
|
"types": "./dist/runtime/types.d.mts",
|
||||||
"import": "./dist/runtime/types.mjs",
|
"import": "./dist/runtime/types.mjs",
|
||||||
@@ -34,6 +84,6 @@
|
|||||||
"@openclaw/normalization-core": "workspace:*"
|
"@openclaw/normalization-core": "workspace:*"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsdown src/index.ts src/normalize-text.ts src/record-shared.ts src/runtime/types.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
|
"build": "tsdown src/index.ts src/error-format.ts src/meta.ts src/normalize-text.ts src/numeric-options.ts src/record-shared.ts src/session.ts src/session-interaction-mode.ts src/session-lineage-meta.ts src/types.ts src/runtime/error-text.ts src/runtime/errors.ts src/runtime/session-identifiers.ts src/runtime/session-identity.ts src/runtime/types.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
78
packages/acp-core/src/error-format.ts
Normal file
78
packages/acp-core/src/error-format.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const SECRET_PATTERNS: RegExp[] = [
|
||||||
|
/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CARD[_-]?NUMBER|CARD[_-]?CVC|CARD[_-]?CVV|CVC|CVV|SECURITY[_-]?CODE|PAYMENT[_-]?CREDENTIAL|SHARED[_-]?PAYMENT[_-]?TOKEN)\b\s*[=:]\s*(["']?)([^\s"'\\]+)\1/g,
|
||||||
|
/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CARD[_-]?NUMBER|CARD[_-]?CVC|CARD[_-]?CVV|CVC|CVV|SECURITY[_-]?CODE|PAYMENT[_-]?CREDENTIAL|SHARED[_-]?PAYMENT[_-]?TOKEN)\b\s*[=:]\s*\\+(["'])([^\s"'\\]+)\\+\1/g,
|
||||||
|
/[?&](?:access[-_]?token|auth[-_]?token|hook[-_]?token|refresh[-_]?token|api[-_]?key|client[-_]?secret|token|key|secret|password|pass|passwd|auth|signature|card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token)=([^&\s"'<>]+)/gi,
|
||||||
|
/"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken|cardNumber|card_number|cardCvc|card_cvc|cardCvv|card_cvv|cvc|cvv|securityCode|security_code|paymentCredential|payment_credential|sharedPaymentToken|shared_payment_token)"\s*:\s*"([^"]+)"/g,
|
||||||
|
/(^|[\s,{])["']?(?:api[-_]key|access[-_]token|refresh[-_]token|authToken|auth[-_]token|clientSecret|client[-_]secret|appSecret|app[-_]secret)["']?\s*[:=]\s*(["'])([^"'\r\n]+)\2/gi,
|
||||||
|
/(^|[\s,{])["']?(?:authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-auth-token)["']?\s*[:=]\s*(["'])([^"'\r\n]+)\2/gi,
|
||||||
|
/--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd|card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token)\s+(["']?)([^\s"']+)\1/gi,
|
||||||
|
/Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/gi,
|
||||||
|
/Authorization\s*[:=]\s*Basic\s+([A-Za-z0-9+/=]+)/gi,
|
||||||
|
/(?:X-OpenClaw-Token|x-pomerium-jwt-assertion|X-Api-Key|X-Auth-Token)\s*[:=]\s*([^\s"',;]+)/gi,
|
||||||
|
/\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b/g,
|
||||||
|
/(^|[\s,;])(?:access_token|refresh_token|auth[-_]?token|api[-_]?key|client[-_]?secret|app[-_]?secret|token|secret|password|passwd|card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token)=([^\s&#]+)/gi,
|
||||||
|
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,
|
||||||
|
/\b(sk-[A-Za-z0-9_-]{8,})\b/g,
|
||||||
|
/(ghp_[A-Za-z0-9]{20,})/g,
|
||||||
|
/(github_pat_[A-Za-z0-9_]{20,})/g,
|
||||||
|
/(xox[baprs]-[A-Za-z0-9-]{10,})/g,
|
||||||
|
/(xapp-[A-Za-z0-9-]{10,})/g,
|
||||||
|
/(gsk_[A-Za-z0-9_-]{10,})/g,
|
||||||
|
/(AIza[0-9A-Za-z\-_]{20,})/g,
|
||||||
|
/(ya29\.[0-9A-Za-z_\-./+=]{10,})/g,
|
||||||
|
/(1\/\/0[0-9A-Za-z_\-./+=]{10,})/g,
|
||||||
|
/(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})/g,
|
||||||
|
/(pplx-[A-Za-z0-9_-]{10,})/g,
|
||||||
|
/(npm_[A-Za-z0-9]{10,})/g,
|
||||||
|
/(AKID[A-Za-z0-9]{10,})/g,
|
||||||
|
/(LTAI[A-Za-z0-9]{10,})/g,
|
||||||
|
/(hf_[A-Za-z0-9]{10,})/g,
|
||||||
|
/(r8_[A-Za-z0-9]{10,})/g,
|
||||||
|
/\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b/g,
|
||||||
|
/\b(\d{6,}:[A-Za-z0-9_-]{20,})\b/g,
|
||||||
|
];
|
||||||
|
|
||||||
|
let configuredRedactor: ((value: string) => string) | undefined;
|
||||||
|
|
||||||
|
export function configureAcpErrorRedactor(redactor: ((value: string) => string) | undefined): void {
|
||||||
|
configuredRedactor = redactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function redactSensitiveText(value: string): string {
|
||||||
|
if (configuredRedactor) {
|
||||||
|
return configuredRedactor(value);
|
||||||
|
}
|
||||||
|
let redacted = value;
|
||||||
|
for (const pattern of SECRET_PATTERNS) {
|
||||||
|
redacted = redacted.replace(pattern, (match, ...args: string[]) => {
|
||||||
|
if (match.includes("PRIVATE KEY-----")) {
|
||||||
|
return "[REDACTED_PRIVATE_KEY]";
|
||||||
|
}
|
||||||
|
const groups = args.slice(0, -2);
|
||||||
|
const token = groups.findLast((group) => typeof group === "string" && group.length > 0);
|
||||||
|
return token ? match.replace(token, "[REDACTED]") : "[REDACTED]";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return redacted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a non-Error `cause` value without leaking `[object Object]` or throwing
|
||||||
|
* while formatting nested ACP runtime failures.
|
||||||
|
*/
|
||||||
|
export function stringifyNonErrorCause(value: unknown): string {
|
||||||
|
if (value === null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
if (typeof value === "string") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.stringify(value);
|
||||||
|
} catch {
|
||||||
|
return Object.prototype.toString.call(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,14 @@
|
|||||||
|
export * from "./error-format.js";
|
||||||
|
export * from "./meta.js";
|
||||||
export * from "./normalize-text.js";
|
export * from "./normalize-text.js";
|
||||||
|
export * from "./numeric-options.js";
|
||||||
export * from "./record-shared.js";
|
export * from "./record-shared.js";
|
||||||
|
export * from "./session-interaction-mode.js";
|
||||||
|
export * from "./session-lineage-meta.js";
|
||||||
|
export * from "./session.js";
|
||||||
|
export * from "./types.js";
|
||||||
|
export * from "./runtime/error-text.js";
|
||||||
|
export * from "./runtime/errors.js";
|
||||||
|
export * from "./runtime/session-identifiers.js";
|
||||||
|
export * from "./runtime/session-identity.js";
|
||||||
export * from "./runtime/types.js";
|
export * from "./runtime/types.js";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
import { configureAcpErrorRedactor } from "../error-format.js";
|
||||||
import {
|
import {
|
||||||
AcpRuntimeError,
|
AcpRuntimeError,
|
||||||
formatAcpErrorChain,
|
formatAcpErrorChain,
|
||||||
@@ -17,6 +18,10 @@ async function expectRejectedAcpRuntimeError(promise: Promise<unknown>): Promise
|
|||||||
throw new Error("expected ACP runtime error rejection");
|
throw new Error("expected ACP runtime error rejection");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
configureAcpErrorRedactor(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
describe("withAcpRuntimeErrorBoundary", () => {
|
describe("withAcpRuntimeErrorBoundary", () => {
|
||||||
it("wraps generic errors with fallback code and source message", async () => {
|
it("wraps generic errors with fallback code and source message", async () => {
|
||||||
const sourceError = new Error("boom");
|
const sourceError = new Error("boom");
|
||||||
@@ -155,4 +160,32 @@ describe("formatAcpErrorChain redaction", () => {
|
|||||||
expect(out).toMatch(/upstream rejected/);
|
expect(out).toMatch(/upstream rejected/);
|
||||||
expect(out).not.toContain(token);
|
expect(out).not.toContain(token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("redacts common HTTP, provider, and private-key credentials in ACP error text", () => {
|
||||||
|
const secrets = [
|
||||||
|
"Authorization: Basic dXNlcjpwYXNzd29yZGFiY2RlZg==",
|
||||||
|
"Bearer eyJabcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz",
|
||||||
|
"github_pat_abcdefghijklmnopqrstuvwxyz123456",
|
||||||
|
["xoxb", "1234567890", "abcdefghijklmnop"].join("-"),
|
||||||
|
"bot123456789:abcdefghijklmnopqrstuvwxyz123456",
|
||||||
|
"-----BEGIN PRIVATE KEY-----\nabcdefghijklmnopqrstuvwxyz\n-----END PRIVATE KEY-----",
|
||||||
|
];
|
||||||
|
const out = formatAcpErrorChain(
|
||||||
|
new AcpRuntimeError("ACP_TURN_FAILED", `backend failed: ${secrets.join(" ")}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const secret of secrets) {
|
||||||
|
expect(out).not.toContain(secret);
|
||||||
|
}
|
||||||
|
expect(out).toContain("backend failed");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses a configured host redactor before rendering ACP error text", () => {
|
||||||
|
configureAcpErrorRedactor((value) => value.replaceAll("custom-secret", "[CUSTOM]"));
|
||||||
|
|
||||||
|
const out = formatAcpErrorChain(new AcpRuntimeError("ACP_TURN_FAILED", "custom-secret"));
|
||||||
|
|
||||||
|
expect(out).toContain("[CUSTOM]");
|
||||||
|
expect(out).not.toContain("custom-secret");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
152
packages/acp-core/src/runtime/errors.ts
Normal file
152
packages/acp-core/src/runtime/errors.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { redactSensitiveText, stringifyNonErrorCause } from "../error-format.js";
|
||||||
|
|
||||||
|
export const ACP_ERROR_CODES = [
|
||||||
|
"ACP_BACKEND_MISSING",
|
||||||
|
"ACP_BACKEND_UNAVAILABLE",
|
||||||
|
"ACP_BACKEND_UNSUPPORTED_CONTROL",
|
||||||
|
"ACP_DISPATCH_DISABLED",
|
||||||
|
"ACP_INVALID_RUNTIME_OPTION",
|
||||||
|
"ACP_SESSION_INIT_FAILED",
|
||||||
|
"ACP_TURN_FAILED",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type AcpRuntimeErrorCode = (typeof ACP_ERROR_CODES)[number];
|
||||||
|
const ACP_ERROR_CODE_SET = new Set<AcpRuntimeErrorCode>(ACP_ERROR_CODES);
|
||||||
|
|
||||||
|
export class AcpRuntimeError extends Error {
|
||||||
|
readonly code: AcpRuntimeErrorCode;
|
||||||
|
override readonly cause?: unknown;
|
||||||
|
|
||||||
|
constructor(code: AcpRuntimeErrorCode, message: string, options?: { cause?: unknown }) {
|
||||||
|
super(message);
|
||||||
|
this.name = "AcpRuntimeError";
|
||||||
|
this.code = code;
|
||||||
|
this.cause = options?.cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getForeignAcpRuntimeError(value: unknown): {
|
||||||
|
code: AcpRuntimeErrorCode;
|
||||||
|
message: string;
|
||||||
|
} | null {
|
||||||
|
if (!(value instanceof Error)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const code = (value as { code?: unknown }).code;
|
||||||
|
if (typeof code !== "string" || !ACP_ERROR_CODE_SET.has(code as AcpRuntimeErrorCode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: code as AcpRuntimeErrorCode,
|
||||||
|
message: value.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function readAcpRequestErrorDetails(value: Error): string | undefined {
|
||||||
|
const code = (value as { code?: unknown }).code;
|
||||||
|
if (typeof code !== "number") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const data = (value as { data?: unknown }).data;
|
||||||
|
if (!data || typeof data !== "object") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const details = (data as { details?: unknown }).details;
|
||||||
|
if (details === undefined || details === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const rendered = redactSensitiveText(stringifyNonErrorCause(details)).trim();
|
||||||
|
return rendered.length > 0 ? rendered : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageWithAcpRequestErrorDetails(error: Error): string {
|
||||||
|
const details = readAcpRequestErrorDetails(error);
|
||||||
|
if (!details || error.message.includes(details)) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
return `${error.message}: ${details}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAcpRuntimeError(value: unknown): value is AcpRuntimeError {
|
||||||
|
return value instanceof AcpRuntimeError || getForeignAcpRuntimeError(value) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toAcpRuntimeError(params: {
|
||||||
|
error: unknown;
|
||||||
|
fallbackCode: AcpRuntimeErrorCode;
|
||||||
|
fallbackMessage: string;
|
||||||
|
}): AcpRuntimeError {
|
||||||
|
if (params.error instanceof AcpRuntimeError) {
|
||||||
|
return params.error;
|
||||||
|
}
|
||||||
|
const foreignAcpRuntimeError = getForeignAcpRuntimeError(params.error);
|
||||||
|
if (foreignAcpRuntimeError) {
|
||||||
|
return new AcpRuntimeError(foreignAcpRuntimeError.code, foreignAcpRuntimeError.message, {
|
||||||
|
cause: params.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (params.error instanceof Error) {
|
||||||
|
return new AcpRuntimeError(
|
||||||
|
params.fallbackCode,
|
||||||
|
messageWithAcpRequestErrorDetails(params.error),
|
||||||
|
{
|
||||||
|
cause: params.error,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new AcpRuntimeError(params.fallbackCode, params.fallbackMessage, {
|
||||||
|
cause: params.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render an error and its `.cause` chain as a single human-readable line for
|
||||||
|
* logs, lifecycle events, and tool results. Format is
|
||||||
|
* `Name [code]: message <- Name [code]: message <- ...`. Number codes also
|
||||||
|
* appear, so JSON-RPC error codes like `-32603` survive into surfaces that
|
||||||
|
* downstream consumers see (gateway logs, telegram replies, tool_result text).
|
||||||
|
*
|
||||||
|
* Depth is capped to defend against self-referential `.cause` cycles.
|
||||||
|
*/
|
||||||
|
export function formatAcpErrorChain(error: unknown): string {
|
||||||
|
if (!(error instanceof Error)) {
|
||||||
|
return redactSensitiveText(String(error));
|
||||||
|
}
|
||||||
|
const segments: string[] = [renderSingleError(error)];
|
||||||
|
let current: unknown = (error as unknown as { cause?: unknown }).cause;
|
||||||
|
let depth = 0;
|
||||||
|
while (current !== undefined && current !== null && depth < 8) {
|
||||||
|
if (current instanceof Error) {
|
||||||
|
segments.push(renderSingleError(current));
|
||||||
|
current = (current as unknown as { cause?: unknown }).cause;
|
||||||
|
} else {
|
||||||
|
segments.push(stringifyNonErrorCause(current));
|
||||||
|
current = undefined;
|
||||||
|
}
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
|
return redactSensitiveText(segments.join(" <- "));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSingleError(error: Error): string {
|
||||||
|
const codeValue = (error as unknown as { code?: unknown }).code;
|
||||||
|
const codeSuffix =
|
||||||
|
typeof codeValue === "string" || typeof codeValue === "number" ? ` [${codeValue}]` : "";
|
||||||
|
return `${error.name}${codeSuffix}: ${error.message}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function withAcpRuntimeErrorBoundary<T>(params: {
|
||||||
|
run: () => Promise<T>;
|
||||||
|
fallbackCode: AcpRuntimeErrorCode;
|
||||||
|
fallbackMessage: string;
|
||||||
|
}): Promise<T> {
|
||||||
|
try {
|
||||||
|
return await params.run();
|
||||||
|
} catch (error) {
|
||||||
|
throw toAcpRuntimeError({
|
||||||
|
error,
|
||||||
|
fallbackCode: params.fallbackCode,
|
||||||
|
fallbackMessage: params.fallbackMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { normalizeText } from "@openclaw/acp-core/normalize-text";
|
|
||||||
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
|
||||||
import type { SessionAcpIdentity, SessionAcpMeta } from "../../config/sessions/types.js";
|
import { normalizeText } from "../normalize-text.js";
|
||||||
|
import type { SessionAcpIdentity, SessionAcpMeta } from "../types.js";
|
||||||
import { isSessionIdentityPending, resolveSessionIdentityFromMeta } from "./session-identity.js";
|
import { isSessionIdentityPending, resolveSessionIdentityFromMeta } from "./session-identity.js";
|
||||||
|
|
||||||
export const ACP_SESSION_IDENTITY_RENDERER_VERSION = "v1";
|
export const ACP_SESSION_IDENTITY_RENDERER_VERSION = "v1";
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import { normalizeText } from "@openclaw/acp-core/normalize-text";
|
import { normalizeText } from "../normalize-text.js";
|
||||||
import type { AcpRuntimeHandle, AcpRuntimeStatus } from "@openclaw/acp-core/runtime/types";
|
import type { SessionAcpIdentity, SessionAcpIdentitySource, SessionAcpMeta } from "../types.js";
|
||||||
import type {
|
import type { AcpRuntimeHandle, AcpRuntimeStatus } from "./types.js";
|
||||||
SessionAcpIdentity,
|
|
||||||
SessionAcpIdentitySource,
|
|
||||||
SessionAcpMeta,
|
|
||||||
} from "../../config/sessions/types.js";
|
|
||||||
|
|
||||||
function normalizeIdentityState(value: unknown): SessionAcpIdentity["state"] | undefined {
|
function normalizeIdentityState(value: unknown): SessionAcpIdentity["state"] | undefined {
|
||||||
if (value !== "pending" && value !== "resolved") {
|
if (value !== "pending" && value !== "resolved") {
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import type { SessionEntry } from "../config/sessions/types.js";
|
|
||||||
|
|
||||||
type AcpSessionInteractionMode = "interactive" | "parent-owned-background";
|
type AcpSessionInteractionMode = "interactive" | "parent-owned-background";
|
||||||
|
|
||||||
type SessionInteractionEntry = Pick<SessionEntry, "spawnedBy" | "parentSessionKey" | "acp">;
|
type SessionInteractionEntry = {
|
||||||
|
spawnedBy?: string;
|
||||||
|
parentSessionKey?: string;
|
||||||
|
acp?: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
function resolveAcpSessionInteractionMode(
|
function resolveAcpSessionInteractionMode(
|
||||||
entry?: SessionInteractionEntry | null,
|
entry?: SessionInteractionEntry | null,
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import type { GatewaySessionRow } from "../gateway/session-utils.js";
|
|
||||||
|
|
||||||
const SUBAGENT_ROLES = ["orchestrator", "leaf"] as const;
|
const SUBAGENT_ROLES = ["orchestrator", "leaf"] as const;
|
||||||
const SUBAGENT_CONTROL_SCOPES = ["children", "none"] as const;
|
const SUBAGENT_CONTROL_SCOPES = ["children", "none"] as const;
|
||||||
@@ -20,19 +19,18 @@ export type AcpSessionLineageMeta = {
|
|||||||
spawnedCwd?: string;
|
spawnedCwd?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AcpSessionLineageRow = Pick<
|
export type AcpSessionLineageRow = {
|
||||||
GatewaySessionRow,
|
key: string;
|
||||||
| "key"
|
kind?: string;
|
||||||
| "kind"
|
channel?: string;
|
||||||
| "channel"
|
parentSessionKey?: string;
|
||||||
| "parentSessionKey"
|
spawnedBy?: string;
|
||||||
| "spawnedBy"
|
spawnDepth?: number;
|
||||||
| "spawnDepth"
|
subagentRole?: string;
|
||||||
| "subagentRole"
|
subagentControlScope?: string;
|
||||||
| "subagentControlScope"
|
spawnedWorkspaceDir?: string;
|
||||||
| "spawnedWorkspaceDir"
|
spawnedCwd?: string;
|
||||||
| "spawnedCwd"
|
};
|
||||||
>;
|
|
||||||
|
|
||||||
function readInteger(value: unknown): number | undefined {
|
function readInteger(value: unknown): number | undefined {
|
||||||
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
||||||
92
packages/acp-core/src/types.ts
Normal file
92
packages/acp-core/src/types.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
|
||||||
|
|
||||||
|
const ACP_PROVENANCE_MODE_VALUES = ["off", "meta", "meta+receipt"] as const;
|
||||||
|
|
||||||
|
export type SessionId = string;
|
||||||
|
|
||||||
|
export type AcpProvenanceMode = (typeof ACP_PROVENANCE_MODE_VALUES)[number];
|
||||||
|
|
||||||
|
export function normalizeAcpProvenanceMode(
|
||||||
|
value: string | undefined,
|
||||||
|
): AcpProvenanceMode | undefined {
|
||||||
|
const normalized = normalizeOptionalLowercaseString(value);
|
||||||
|
if (!normalized) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return (ACP_PROVENANCE_MODE_VALUES as readonly string[]).includes(normalized)
|
||||||
|
? (normalized as AcpProvenanceMode)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AcpSession = {
|
||||||
|
sessionId: SessionId;
|
||||||
|
sessionKey: string;
|
||||||
|
ledgerSessionId?: string;
|
||||||
|
cwd: string;
|
||||||
|
createdAt: number;
|
||||||
|
lastTouchedAt: number;
|
||||||
|
abortController: AbortController | null;
|
||||||
|
activeRunId: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AcpServerOptions = {
|
||||||
|
gatewayUrl?: string;
|
||||||
|
gatewayToken?: string;
|
||||||
|
gatewayPassword?: string;
|
||||||
|
defaultSessionKey?: string;
|
||||||
|
defaultSessionLabel?: string;
|
||||||
|
requireExistingSession?: boolean;
|
||||||
|
resetSession?: boolean;
|
||||||
|
prefixCwd?: boolean;
|
||||||
|
provenanceMode?: AcpProvenanceMode;
|
||||||
|
sessionCreateRateLimit?: {
|
||||||
|
maxRequests?: number;
|
||||||
|
windowMs?: number;
|
||||||
|
};
|
||||||
|
verbose?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SessionAcpIdentitySource = "ensure" | "status" | "event";
|
||||||
|
|
||||||
|
export type SessionAcpIdentityState = "pending" | "resolved";
|
||||||
|
|
||||||
|
export type SessionAcpIdentity = {
|
||||||
|
state: SessionAcpIdentityState;
|
||||||
|
acpxRecordId?: string;
|
||||||
|
acpxSessionId?: string;
|
||||||
|
agentSessionId?: string;
|
||||||
|
source: SessionAcpIdentitySource;
|
||||||
|
lastUpdatedAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AcpSessionRuntimeOptions = {
|
||||||
|
/**
|
||||||
|
* ACP runtime mode set via session/set_mode (for example: "plan", "normal", "auto").
|
||||||
|
*/
|
||||||
|
runtimeMode?: string;
|
||||||
|
/** ACP runtime config option: model id. */
|
||||||
|
model?: string;
|
||||||
|
/** ACP runtime config option: thinking/reasoning effort. */
|
||||||
|
thinking?: string;
|
||||||
|
/** Working directory override for ACP session turns. */
|
||||||
|
cwd?: string;
|
||||||
|
/** ACP runtime config option: permission profile id. */
|
||||||
|
permissionProfile?: string;
|
||||||
|
/** ACP runtime config option: per-turn timeout in seconds. */
|
||||||
|
timeoutSeconds?: number;
|
||||||
|
/** Backend-specific option bag mapped through session/set_config_option. */
|
||||||
|
backendExtras?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SessionAcpMeta = {
|
||||||
|
backend: string;
|
||||||
|
agent: string;
|
||||||
|
runtimeSessionName: string;
|
||||||
|
identity?: SessionAcpIdentity;
|
||||||
|
mode: "persistent" | "oneshot";
|
||||||
|
runtimeOptions?: AcpSessionRuntimeOptions;
|
||||||
|
cwd?: string;
|
||||||
|
state: "idle" | "running" | "error";
|
||||||
|
lastActivityAt: number;
|
||||||
|
lastError?: string;
|
||||||
|
};
|
||||||
@@ -154,6 +154,9 @@ export const EXTENSION_PACKAGE_BOUNDARY_BASE_PATHS = {
|
|||||||
"@openclaw/acp-core/record-shared": [
|
"@openclaw/acp-core/record-shared": [
|
||||||
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts",
|
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts",
|
||||||
],
|
],
|
||||||
|
"@openclaw/acp-core/runtime/errors": [
|
||||||
|
"../dist/plugin-sdk/packages/acp-core/src/runtime/errors.d.ts",
|
||||||
|
],
|
||||||
"@openclaw/acp-core/runtime/types": [
|
"@openclaw/acp-core/runtime/types": [
|
||||||
"../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts",
|
"../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ const runTsgoScript = path.join(repoRoot, "scripts/run-tsgo.mjs");
|
|||||||
const TYPE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".d.ts", ".js", ".mjs", ".json"]);
|
const TYPE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".d.ts", ".js", ".mjs", ".json"]);
|
||||||
const VALID_MODES = new Set(["all", "package-boundary"]);
|
const VALID_MODES = new Set(["all", "package-boundary"]);
|
||||||
const ROOT_SHIMS_TIMEOUT_MS = resolveBoundaryRootShimsTimeoutMs(process.env);
|
const ROOT_SHIMS_TIMEOUT_MS = resolveBoundaryRootShimsTimeoutMs(process.env);
|
||||||
|
const ROOT_SHIMS_MAX_OLD_SPACE_SIZE =
|
||||||
|
process.env.OPENCLAW_ROOT_SHIMS_MAX_OLD_SPACE_SIZE?.trim() || "8192";
|
||||||
const ROOT_SHIMS_NODE_OPTIONS =
|
const ROOT_SHIMS_NODE_OPTIONS =
|
||||||
`${process.env.NODE_OPTIONS ?? ""} --max-old-space-size=4096`.trim();
|
`${process.env.NODE_OPTIONS ?? ""} --max-old-space-size=${ROOT_SHIMS_MAX_OLD_SPACE_SIZE}`.trim();
|
||||||
|
|
||||||
const PLUGIN_SDK_TYPE_INPUTS = [
|
const PLUGIN_SDK_TYPE_INPUTS = [
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
@@ -64,6 +66,10 @@ const ROOT_DTS_REQUIRED_OUTPUTS = [
|
|||||||
"dist/plugin-sdk/packages/media-core/src/mime.d.ts",
|
"dist/plugin-sdk/packages/media-core/src/mime.d.ts",
|
||||||
"dist/plugin-sdk/packages/media-core/src/read-byte-stream-with-limit.d.ts",
|
"dist/plugin-sdk/packages/media-core/src/read-byte-stream-with-limit.d.ts",
|
||||||
"dist/plugin-sdk/packages/media-core/src/read-response-with-limit.d.ts",
|
"dist/plugin-sdk/packages/media-core/src/read-response-with-limit.d.ts",
|
||||||
|
"dist/plugin-sdk/packages/acp-core/src/index.d.ts",
|
||||||
|
"dist/plugin-sdk/packages/acp-core/src/normalize-text.d.ts",
|
||||||
|
"dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts",
|
||||||
|
"dist/plugin-sdk/packages/acp-core/src/runtime/errors.d.ts",
|
||||||
"dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts",
|
"dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts",
|
||||||
"dist/plugin-sdk/packages/terminal-core/src/ansi.d.ts",
|
"dist/plugin-sdk/packages/terminal-core/src/ansi.d.ts",
|
||||||
"dist/plugin-sdk/packages/terminal-core/src/decorative-emoji.d.ts",
|
"dist/plugin-sdk/packages/terminal-core/src/decorative-emoji.d.ts",
|
||||||
@@ -122,6 +128,10 @@ const PACKAGE_DTS_REQUIRED_OUTPUTS = [
|
|||||||
"packages/plugin-sdk/dist/packages/media-core/src/mime.d.ts",
|
"packages/plugin-sdk/dist/packages/media-core/src/mime.d.ts",
|
||||||
"packages/plugin-sdk/dist/packages/media-core/src/read-byte-stream-with-limit.d.ts",
|
"packages/plugin-sdk/dist/packages/media-core/src/read-byte-stream-with-limit.d.ts",
|
||||||
"packages/plugin-sdk/dist/packages/media-core/src/read-response-with-limit.d.ts",
|
"packages/plugin-sdk/dist/packages/media-core/src/read-response-with-limit.d.ts",
|
||||||
|
"packages/plugin-sdk/dist/packages/acp-core/src/index.d.ts",
|
||||||
|
"packages/plugin-sdk/dist/packages/acp-core/src/normalize-text.d.ts",
|
||||||
|
"packages/plugin-sdk/dist/packages/acp-core/src/record-shared.d.ts",
|
||||||
|
"packages/plugin-sdk/dist/packages/acp-core/src/runtime/errors.d.ts",
|
||||||
"packages/plugin-sdk/dist/packages/acp-core/src/runtime/types.d.ts",
|
"packages/plugin-sdk/dist/packages/acp-core/src/runtime/types.d.ts",
|
||||||
"packages/plugin-sdk/dist/packages/model-catalog-core/src/configured-model-refs.d.ts",
|
"packages/plugin-sdk/dist/packages/model-catalog-core/src/configured-model-refs.d.ts",
|
||||||
"packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-normalize.d.ts",
|
"packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-normalize.d.ts",
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
import {
|
||||||
|
createIdentityFromEnsure,
|
||||||
|
identityHasStableSessionId,
|
||||||
|
identityEquals,
|
||||||
|
isSessionIdentityPending,
|
||||||
|
mergeSessionIdentity,
|
||||||
|
resolveRuntimeResumeSessionId,
|
||||||
|
resolveRuntimeHandleIdentifiersFromIdentity,
|
||||||
|
resolveSessionIdentityFromMeta,
|
||||||
|
} from "@openclaw/acp-core/runtime/session-identity";
|
||||||
import type {
|
import type {
|
||||||
AcpRuntime,
|
AcpRuntime,
|
||||||
AcpRuntimeCapabilities,
|
AcpRuntimeCapabilities,
|
||||||
@@ -29,16 +39,6 @@ import {
|
|||||||
withAcpRuntimeErrorBoundary,
|
withAcpRuntimeErrorBoundary,
|
||||||
} from "../runtime/errors.js";
|
} from "../runtime/errors.js";
|
||||||
import type { AcpRuntimeErrorCode } from "../runtime/errors.js";
|
import type { AcpRuntimeErrorCode } from "../runtime/errors.js";
|
||||||
import {
|
|
||||||
createIdentityFromEnsure,
|
|
||||||
identityHasStableSessionId,
|
|
||||||
identityEquals,
|
|
||||||
isSessionIdentityPending,
|
|
||||||
mergeSessionIdentity,
|
|
||||||
resolveRuntimeResumeSessionId,
|
|
||||||
resolveRuntimeHandleIdentifiersFromIdentity,
|
|
||||||
resolveSessionIdentityFromMeta,
|
|
||||||
} from "../runtime/session-identity.js";
|
|
||||||
import { clearAcpTurnActive, markAcpTurnActive } from "./active-turns.js";
|
import { clearAcpTurnActive, markAcpTurnActive } from "./active-turns.js";
|
||||||
import { reconcileManagerRuntimeSessionIdentifiers } from "./manager.identity-reconcile.js";
|
import { reconcileManagerRuntimeSessionIdentifiers } from "./manager.identity-reconcile.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
import {
|
||||||
|
createIdentityFromHandleEvent,
|
||||||
|
createIdentityFromStatus,
|
||||||
|
identityEquals,
|
||||||
|
mergeSessionIdentity,
|
||||||
|
resolveRuntimeHandleIdentifiersFromIdentity,
|
||||||
|
resolveSessionIdentityFromMeta,
|
||||||
|
} from "@openclaw/acp-core/runtime/session-identity";
|
||||||
import type {
|
import type {
|
||||||
AcpRuntime,
|
AcpRuntime,
|
||||||
AcpRuntimeHandle,
|
AcpRuntimeHandle,
|
||||||
@@ -6,14 +14,6 @@ import type {
|
|||||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||||
import { logVerbose } from "../../globals.js";
|
import { logVerbose } from "../../globals.js";
|
||||||
import { withAcpRuntimeErrorBoundary } from "../runtime/errors.js";
|
import { withAcpRuntimeErrorBoundary } from "../runtime/errors.js";
|
||||||
import {
|
|
||||||
createIdentityFromHandleEvent,
|
|
||||||
createIdentityFromStatus,
|
|
||||||
identityEquals,
|
|
||||||
mergeSessionIdentity,
|
|
||||||
resolveRuntimeHandleIdentifiersFromIdentity,
|
|
||||||
resolveSessionIdentityFromMeta,
|
|
||||||
} from "../runtime/session-identity.js";
|
|
||||||
import type { SessionAcpMeta, SessionEntry } from "./manager.types.js";
|
import type { SessionAcpMeta, SessionEntry } from "./manager.types.js";
|
||||||
import { hasLegacyAcpIdentityProjection } from "./manager.utils.js";
|
import { hasLegacyAcpIdentityProjection } from "./manager.utils.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { ContentBlock, SessionUpdate } from "@agentclientprotocol/sdk";
|
import type { ContentBlock, SessionUpdate } from "@agentclientprotocol/sdk";
|
||||||
|
import { resolveIntegerOption } from "@openclaw/acp-core/numeric-options";
|
||||||
import { resolveStateDir } from "../config/paths.js";
|
import { resolveStateDir } from "../config/paths.js";
|
||||||
import { withFileLock } from "../infra/file-lock.js";
|
import { withFileLock } from "../infra/file-lock.js";
|
||||||
import { readJsonFile, writeTextAtomic } from "../infra/json-files.js";
|
import { readJsonFile, writeTextAtomic } from "../infra/json-files.js";
|
||||||
import { isRecord } from "../utils.js";
|
import { isRecord } from "../utils.js";
|
||||||
import { resolveIntegerOption } from "./numeric-options.js";
|
|
||||||
|
|
||||||
const LEDGER_VERSION = 1;
|
const LEDGER_VERSION = 1;
|
||||||
const DEFAULT_MAX_SESSIONS = 200;
|
const DEFAULT_MAX_SESSIONS = 200;
|
||||||
|
|||||||
@@ -1,153 +1,6 @@
|
|||||||
import { stringifyNonErrorCause } from "../../infra/errors.js";
|
import { configureAcpErrorRedactor } from "@openclaw/acp-core";
|
||||||
import { redactSensitiveText } from "../../logging/redact.js";
|
import { redactSensitiveText } from "../../logging/redact.js";
|
||||||
|
|
||||||
export const ACP_ERROR_CODES = [
|
configureAcpErrorRedactor(redactSensitiveText);
|
||||||
"ACP_BACKEND_MISSING",
|
|
||||||
"ACP_BACKEND_UNAVAILABLE",
|
|
||||||
"ACP_BACKEND_UNSUPPORTED_CONTROL",
|
|
||||||
"ACP_DISPATCH_DISABLED",
|
|
||||||
"ACP_INVALID_RUNTIME_OPTION",
|
|
||||||
"ACP_SESSION_INIT_FAILED",
|
|
||||||
"ACP_TURN_FAILED",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export type AcpRuntimeErrorCode = (typeof ACP_ERROR_CODES)[number];
|
export * from "@openclaw/acp-core/runtime/errors";
|
||||||
const ACP_ERROR_CODE_SET = new Set<AcpRuntimeErrorCode>(ACP_ERROR_CODES);
|
|
||||||
|
|
||||||
export class AcpRuntimeError extends Error {
|
|
||||||
readonly code: AcpRuntimeErrorCode;
|
|
||||||
override readonly cause?: unknown;
|
|
||||||
|
|
||||||
constructor(code: AcpRuntimeErrorCode, message: string, options?: { cause?: unknown }) {
|
|
||||||
super(message);
|
|
||||||
this.name = "AcpRuntimeError";
|
|
||||||
this.code = code;
|
|
||||||
this.cause = options?.cause;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getForeignAcpRuntimeError(value: unknown): {
|
|
||||||
code: AcpRuntimeErrorCode;
|
|
||||||
message: string;
|
|
||||||
} | null {
|
|
||||||
if (!(value instanceof Error)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const code = (value as { code?: unknown }).code;
|
|
||||||
if (typeof code !== "string" || !ACP_ERROR_CODE_SET.has(code as AcpRuntimeErrorCode)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
code: code as AcpRuntimeErrorCode,
|
|
||||||
message: value.message,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function readAcpRequestErrorDetails(value: Error): string | undefined {
|
|
||||||
const code = (value as { code?: unknown }).code;
|
|
||||||
if (typeof code !== "number") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const data = (value as { data?: unknown }).data;
|
|
||||||
if (!data || typeof data !== "object") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const details = (data as { details?: unknown }).details;
|
|
||||||
if (details === undefined || details === null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const rendered = redactSensitiveText(stringifyNonErrorCause(details)).trim();
|
|
||||||
return rendered.length > 0 ? rendered : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function messageWithAcpRequestErrorDetails(error: Error): string {
|
|
||||||
const details = readAcpRequestErrorDetails(error);
|
|
||||||
if (!details || error.message.includes(details)) {
|
|
||||||
return error.message;
|
|
||||||
}
|
|
||||||
return `${error.message}: ${details}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isAcpRuntimeError(value: unknown): value is AcpRuntimeError {
|
|
||||||
return value instanceof AcpRuntimeError || getForeignAcpRuntimeError(value) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toAcpRuntimeError(params: {
|
|
||||||
error: unknown;
|
|
||||||
fallbackCode: AcpRuntimeErrorCode;
|
|
||||||
fallbackMessage: string;
|
|
||||||
}): AcpRuntimeError {
|
|
||||||
if (params.error instanceof AcpRuntimeError) {
|
|
||||||
return params.error;
|
|
||||||
}
|
|
||||||
const foreignAcpRuntimeError = getForeignAcpRuntimeError(params.error);
|
|
||||||
if (foreignAcpRuntimeError) {
|
|
||||||
return new AcpRuntimeError(foreignAcpRuntimeError.code, foreignAcpRuntimeError.message, {
|
|
||||||
cause: params.error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (params.error instanceof Error) {
|
|
||||||
return new AcpRuntimeError(
|
|
||||||
params.fallbackCode,
|
|
||||||
messageWithAcpRequestErrorDetails(params.error),
|
|
||||||
{
|
|
||||||
cause: params.error,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return new AcpRuntimeError(params.fallbackCode, params.fallbackMessage, {
|
|
||||||
cause: params.error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render an error and its `.cause` chain as a single human-readable line for
|
|
||||||
* logs, lifecycle events, and tool results. Format is
|
|
||||||
* `Name [code]: message <- Name [code]: message <- ...`. Number codes also
|
|
||||||
* appear, so JSON-RPC error codes like `-32603` survive into surfaces that
|
|
||||||
* downstream consumers see (gateway logs, telegram replies, tool_result text).
|
|
||||||
*
|
|
||||||
* Depth is capped to defend against self-referential `.cause` cycles.
|
|
||||||
*/
|
|
||||||
export function formatAcpErrorChain(error: unknown): string {
|
|
||||||
if (!(error instanceof Error)) {
|
|
||||||
return redactSensitiveText(String(error));
|
|
||||||
}
|
|
||||||
const segments: string[] = [renderSingleError(error)];
|
|
||||||
let current: unknown = (error as unknown as { cause?: unknown }).cause;
|
|
||||||
let depth = 0;
|
|
||||||
while (current !== undefined && current !== null && depth < 8) {
|
|
||||||
if (current instanceof Error) {
|
|
||||||
segments.push(renderSingleError(current));
|
|
||||||
current = (current as unknown as { cause?: unknown }).cause;
|
|
||||||
} else {
|
|
||||||
segments.push(stringifyNonErrorCause(current));
|
|
||||||
current = undefined;
|
|
||||||
}
|
|
||||||
depth += 1;
|
|
||||||
}
|
|
||||||
return redactSensitiveText(segments.join(" <- "));
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSingleError(error: Error): string {
|
|
||||||
const codeValue = (error as unknown as { code?: unknown }).code;
|
|
||||||
const codeSuffix =
|
|
||||||
typeof codeValue === "string" || typeof codeValue === "number" ? ` [${codeValue}]` : "";
|
|
||||||
return `${error.name}${codeSuffix}: ${error.message}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function withAcpRuntimeErrorBoundary<T>(params: {
|
|
||||||
run: () => Promise<T>;
|
|
||||||
fallbackCode: AcpRuntimeErrorCode;
|
|
||||||
fallbackMessage: string;
|
|
||||||
}): Promise<T> {
|
|
||||||
try {
|
|
||||||
return await params.run();
|
|
||||||
} catch (error) {
|
|
||||||
throw toAcpRuntimeError({
|
|
||||||
error,
|
|
||||||
fallbackCode: params.fallbackCode,
|
|
||||||
fallbackMessage: params.fallbackMessage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { readBool, readString } from "@openclaw/acp-core/meta";
|
||||||
|
import type { AcpServerOptions } from "@openclaw/acp-core/types";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { readBool, readString } from "./meta.js";
|
|
||||||
import type { AcpServerOptions } from "./types.js";
|
|
||||||
|
|
||||||
type AcpSessionMeta = {
|
type AcpSessionMeta = {
|
||||||
sessionKey?: string;
|
sessionKey?: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { CancelNotification, PromptRequest, PromptResponse } from "@agentclientprotocol/sdk";
|
import type { CancelNotification, PromptRequest, PromptResponse } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import type {
|
|||||||
NewSessionRequest,
|
NewSessionRequest,
|
||||||
PromptRequest,
|
PromptRequest,
|
||||||
} from "@agentclientprotocol/sdk";
|
} from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemoryAcpEventLedger, type AcpEventLedger } from "./event-ledger.js";
|
import { createInMemoryAcpEventLedger, type AcpEventLedger } from "./event-ledger.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import type {
|
|||||||
ResumeSessionRequest,
|
ResumeSessionRequest,
|
||||||
} from "@agentclientprotocol/sdk";
|
} from "@agentclientprotocol/sdk";
|
||||||
import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import type { GatewaySessionRow } from "../gateway/session-utils.js";
|
import type { GatewaySessionRow } from "../gateway/session-utils.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { CancelNotification } from "@agentclientprotocol/sdk";
|
import type { CancelNotification } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { promptAgent } from "./translator.prompt-harness.test-support.js";
|
import { promptAgent } from "./translator.prompt-harness.test-support.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { PromptRequest } from "@agentclientprotocol/sdk";
|
import type { PromptRequest } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { expect, vi } from "vitest";
|
import { expect, vi } from "vitest";
|
||||||
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { PromptRequest } from "@agentclientprotocol/sdk";
|
import type { PromptRequest } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { ListSessionsRequest, LoadSessionRequest } from "@agentclientprotocol/sdk";
|
import type { ListSessionsRequest, LoadSessionRequest } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import type {
|
|||||||
SetSessionConfigOptionRequest,
|
SetSessionConfigOptionRequest,
|
||||||
SetSessionModeRequest,
|
SetSessionModeRequest,
|
||||||
} from "@agentclientprotocol/sdk";
|
} from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { SetSessionModeRequest } from "@agentclientprotocol/sdk";
|
import type { SetSessionModeRequest } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { PromptRequest } from "@agentclientprotocol/sdk";
|
import type { PromptRequest } from "@agentclientprotocol/sdk";
|
||||||
|
import { createInMemorySessionStore } from "@openclaw/acp-core/session";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { GatewayClient } from "../gateway/client.js";
|
import type { GatewayClient } from "../gateway/client.js";
|
||||||
import { createInMemorySessionStore } from "./session.js";
|
|
||||||
import { AcpGatewayAgent } from "./translator.js";
|
import { AcpGatewayAgent } from "./translator.js";
|
||||||
import {
|
import {
|
||||||
createChatEvent,
|
createChatEvent,
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ import type {
|
|||||||
ToolCallLocation,
|
ToolCallLocation,
|
||||||
ToolKind,
|
ToolKind,
|
||||||
} from "@agentclientprotocol/sdk";
|
} from "@agentclientprotocol/sdk";
|
||||||
|
import { readBool, readNonNegativeInteger, readNumber, readString } from "@openclaw/acp-core/meta";
|
||||||
|
import { defaultAcpSessionStore, type AcpSessionStore } from "@openclaw/acp-core/session";
|
||||||
|
import {
|
||||||
|
toAcpSessionLineageMeta,
|
||||||
|
type AcpSessionLineageMeta,
|
||||||
|
} from "@openclaw/acp-core/session-lineage-meta";
|
||||||
import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
|
import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
|
||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
import type { EventFrame } from "../../packages/gateway-protocol/src/index.js";
|
||||||
@@ -58,7 +64,6 @@ import {
|
|||||||
formatToolTitle,
|
formatToolTitle,
|
||||||
inferToolKind,
|
inferToolKind,
|
||||||
} from "./event-mapper.js";
|
} from "./event-mapper.js";
|
||||||
import { readBool, readNonNegativeInteger, readNumber, readString } from "./meta.js";
|
|
||||||
import {
|
import {
|
||||||
buildAcpPermissionRequest,
|
buildAcpPermissionRequest,
|
||||||
parseGatewayExecApprovalEventData,
|
parseGatewayExecApprovalEventData,
|
||||||
@@ -68,9 +73,7 @@ import {
|
|||||||
type GatewayExecApprovalDetails,
|
type GatewayExecApprovalDetails,
|
||||||
type GatewayExecApprovalEvent,
|
type GatewayExecApprovalEvent,
|
||||||
} from "./permission-relay.js";
|
} from "./permission-relay.js";
|
||||||
import { toAcpSessionLineageMeta, type AcpSessionLineageMeta } from "./session-lineage-meta.js";
|
|
||||||
import { parseSessionMeta, resetSessionIfNeeded, resolveSessionKey } from "./session-mapper.js";
|
import { parseSessionMeta, resetSessionIfNeeded, resolveSessionKey } from "./session-mapper.js";
|
||||||
import { defaultAcpSessionStore, type AcpSessionStore } from "./session.js";
|
|
||||||
import { ACP_AGENT_INFO, type AcpServerOptions } from "./types.js";
|
import { ACP_AGENT_INFO, type AcpServerOptions } from "./types.js";
|
||||||
|
|
||||||
// Maximum allowed prompt size (2MB) to prevent DoS via memory exhaustion (CWE-400, GHSA-cxpw-2g23-2vgw)
|
// Maximum allowed prompt size (2MB) to prevent DoS via memory exhaustion (CWE-400, GHSA-cxpw-2g23-2vgw)
|
||||||
|
|||||||
@@ -1,51 +1,7 @@
|
|||||||
import type { SessionId } from "@agentclientprotocol/sdk";
|
export type { AcpProvenanceMode, AcpServerOptions, AcpSession } from "@openclaw/acp-core/types";
|
||||||
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
|
export { normalizeAcpProvenanceMode } from "@openclaw/acp-core/types";
|
||||||
import { VERSION } from "../version.js";
|
import { VERSION } from "../version.js";
|
||||||
|
|
||||||
const ACP_PROVENANCE_MODE_VALUES = ["off", "meta", "meta+receipt"] as const;
|
|
||||||
|
|
||||||
type AcpProvenanceMode = (typeof ACP_PROVENANCE_MODE_VALUES)[number];
|
|
||||||
|
|
||||||
export function normalizeAcpProvenanceMode(
|
|
||||||
value: string | undefined,
|
|
||||||
): AcpProvenanceMode | undefined {
|
|
||||||
const normalized = normalizeOptionalLowercaseString(value);
|
|
||||||
if (!normalized) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return (ACP_PROVENANCE_MODE_VALUES as readonly string[]).includes(normalized)
|
|
||||||
? (normalized as AcpProvenanceMode)
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AcpSession = {
|
|
||||||
sessionId: SessionId;
|
|
||||||
sessionKey: string;
|
|
||||||
ledgerSessionId?: string;
|
|
||||||
cwd: string;
|
|
||||||
createdAt: number;
|
|
||||||
lastTouchedAt: number;
|
|
||||||
abortController: AbortController | null;
|
|
||||||
activeRunId: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AcpServerOptions = {
|
|
||||||
gatewayUrl?: string;
|
|
||||||
gatewayToken?: string;
|
|
||||||
gatewayPassword?: string;
|
|
||||||
defaultSessionKey?: string;
|
|
||||||
defaultSessionLabel?: string;
|
|
||||||
requireExistingSession?: boolean;
|
|
||||||
resetSession?: boolean;
|
|
||||||
prefixCwd?: boolean;
|
|
||||||
provenanceMode?: AcpProvenanceMode;
|
|
||||||
sessionCreateRateLimit?: {
|
|
||||||
maxRequests?: number;
|
|
||||||
windowMs?: number;
|
|
||||||
};
|
|
||||||
verbose?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ACP_AGENT_INFO = {
|
export const ACP_AGENT_INFO = {
|
||||||
name: "openclaw-acp",
|
name: "openclaw-acp",
|
||||||
title: "OpenClaw ACP Gateway",
|
title: "OpenClaw ACP Gateway",
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
import {
|
||||||
|
resolveAcpSessionCwd,
|
||||||
|
resolveAcpThreadSessionDetailLines,
|
||||||
|
} from "@openclaw/acp-core/runtime/session-identifiers";
|
||||||
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
|
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
|
||||||
import {
|
import {
|
||||||
normalizeOptionalLowercaseString,
|
normalizeOptionalLowercaseString,
|
||||||
@@ -12,10 +16,6 @@ import {
|
|||||||
type AcpSpawnRuntimeCloseHandle,
|
type AcpSpawnRuntimeCloseHandle,
|
||||||
} from "../acp/control-plane/spawn.js";
|
} from "../acp/control-plane/spawn.js";
|
||||||
import { isAcpEnabledByPolicy, resolveAcpAgentPolicyError } from "../acp/policy.js";
|
import { isAcpEnabledByPolicy, resolveAcpAgentPolicyError } from "../acp/policy.js";
|
||||||
import {
|
|
||||||
resolveAcpSessionCwd,
|
|
||||||
resolveAcpThreadSessionDetailLines,
|
|
||||||
} from "../acp/runtime/session-identifiers.js";
|
|
||||||
import { DEFAULT_HEARTBEAT_EVERY } from "../auto-reply/heartbeat.js";
|
import { DEFAULT_HEARTBEAT_EVERY } from "../auto-reply/heartbeat.js";
|
||||||
import {
|
import {
|
||||||
resolveChannelDefaultBindingPlacement,
|
resolveChannelDefaultBindingPlacement,
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ vi.mock("../acp/runtime/errors.js", () => ({
|
|||||||
error instanceof Error ? error : new Error(String(error)),
|
error instanceof Error ? error : new Error(String(error)),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../acp/runtime/session-identifiers.js", () => ({
|
vi.mock("@openclaw/acp-core/runtime/session-identifiers", () => ({
|
||||||
resolveAcpSessionCwd: () => "/tmp",
|
resolveAcpSessionCwd: () => "/tmp",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ type AgentAttemptResult = Awaited<ReturnType<AttemptExecutionRuntime["runAgentAt
|
|||||||
type AcpManagerRuntime = typeof import("../acp/control-plane/manager.js");
|
type AcpManagerRuntime = typeof import("../acp/control-plane/manager.js");
|
||||||
type AcpPolicyRuntime = typeof import("../acp/policy.js");
|
type AcpPolicyRuntime = typeof import("../acp/policy.js");
|
||||||
type AcpRuntimeErrorsRuntime = typeof import("../acp/runtime/errors.js");
|
type AcpRuntimeErrorsRuntime = typeof import("../acp/runtime/errors.js");
|
||||||
type AcpSessionIdentifiersRuntime = typeof import("../acp/runtime/session-identifiers.js");
|
type AcpSessionIdentifiersRuntime = typeof import("@openclaw/acp-core/runtime/session-identifiers");
|
||||||
type DeliveryRuntime = typeof import("./command/delivery.runtime.js");
|
type DeliveryRuntime = typeof import("./command/delivery.runtime.js");
|
||||||
type SessionStoreRuntime = typeof import("./command/session-store.runtime.js");
|
type SessionStoreRuntime = typeof import("./command/session-store.runtime.js");
|
||||||
type CliCompactionRuntime = typeof import("./command/cli-compaction.js");
|
type CliCompactionRuntime = typeof import("./command/cli-compaction.js");
|
||||||
@@ -150,7 +150,7 @@ const acpRuntimeErrorsRuntimeLoader = createLazyImportLoader<AcpRuntimeErrorsRun
|
|||||||
() => import("../acp/runtime/errors.js"),
|
() => import("../acp/runtime/errors.js"),
|
||||||
);
|
);
|
||||||
const acpSessionIdentifiersRuntimeLoader = createLazyImportLoader<AcpSessionIdentifiersRuntime>(
|
const acpSessionIdentifiersRuntimeLoader = createLazyImportLoader<AcpSessionIdentifiersRuntime>(
|
||||||
() => import("../acp/runtime/session-identifiers.js"),
|
() => import("@openclaw/acp-core/runtime/session-identifiers"),
|
||||||
);
|
);
|
||||||
const deliveryRuntimeLoader = createLazyImportLoader<DeliveryRuntime>(
|
const deliveryRuntimeLoader = createLazyImportLoader<DeliveryRuntime>(
|
||||||
() => import("./command/delivery.runtime.js"),
|
() => import("./command/delivery.runtime.js"),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
import { isRequesterParentOfBackgroundAcpSession } from "@openclaw/acp-core/session-interaction-mode";
|
||||||
import { finiteSecondsToTimerSafeMilliseconds } from "@openclaw/normalization-core/number-coercion";
|
import { finiteSecondsToTimerSafeMilliseconds } from "@openclaw/normalization-core/number-coercion";
|
||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import { Type } from "typebox";
|
import { Type } from "typebox";
|
||||||
import { isRequesterParentOfBackgroundAcpSession } from "../../acp/session-interaction-mode.js";
|
|
||||||
import { parseSessionThreadInfoFast } from "../../config/sessions/thread-info.js";
|
import { parseSessionThreadInfoFast } from "../../config/sessions/thread-info.js";
|
||||||
import type { SessionEntry } from "../../config/sessions/types.js";
|
import type { SessionEntry } from "../../config/sessions/types.js";
|
||||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||||
|
|||||||
@@ -98,10 +98,7 @@ vi.mock("../../agents/acp-spawn.js", () => ({
|
|||||||
params.cfg?.agents?.defaults?.sandbox?.mode === "all"
|
params.cfg?.agents?.defaults?.sandbox?.mode === "all"
|
||||||
? 'Sandboxed sessions cannot spawn ACP sessions because runtime="acp" runs on the host. Use runtime="subagent" from sandboxed sessions.'
|
? 'Sandboxed sessions cannot spawn ACP sessions because runtime="acp" runs on the host. Use runtime="subagent" from sandboxed sessions.'
|
||||||
: undefined,
|
: undefined,
|
||||||
resolveRuntimeCwdForAcpSpawn: async (params: {
|
resolveRuntimeCwdForAcpSpawn: async (params: { explicitCwd?: string; resolvedCwd?: string }) => {
|
||||||
explicitCwd?: string;
|
|
||||||
resolvedCwd?: string;
|
|
||||||
}) => {
|
|
||||||
if (params.explicitCwd) {
|
if (params.explicitCwd) {
|
||||||
return params.resolvedCwd;
|
return params.resolvedCwd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { formatAcpRuntimeErrorText } from "@openclaw/acp-core/runtime/error-text";
|
||||||
import {
|
import {
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "@openclaw/normalization-core/string-coerce";
|
} from "@openclaw/normalization-core/string-coerce";
|
||||||
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
|
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
|
||||||
import { formatAcpRuntimeErrorText } from "../../../acp/runtime/error-text.js";
|
|
||||||
import { toAcpRuntimeError } from "../../../acp/runtime/errors.js";
|
import { toAcpRuntimeError } from "../../../acp/runtime/errors.js";
|
||||||
import { getAcpRuntimeBackend, requireAcpRuntimeBackend } from "../../../acp/runtime/registry.js";
|
import { getAcpRuntimeBackend, requireAcpRuntimeBackend } from "../../../acp/runtime/registry.js";
|
||||||
import { resolveSessionStorePathForAcp } from "../../../acp/runtime/session-meta.js";
|
import { resolveSessionStorePathForAcp } from "../../../acp/runtime/session-meta.js";
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import {
|
||||||
|
resolveAcpSessionCwd,
|
||||||
|
resolveAcpThreadSessionDetailLines,
|
||||||
|
} from "@openclaw/acp-core/runtime/session-identifiers";
|
||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
|
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
|
||||||
import { resolveAcpSessionResolutionError } from "../../../acp/control-plane/manager.utils.js";
|
import { resolveAcpSessionResolutionError } from "../../../acp/control-plane/manager.utils.js";
|
||||||
@@ -12,10 +16,6 @@ import {
|
|||||||
resolveAcpDispatchPolicyError,
|
resolveAcpDispatchPolicyError,
|
||||||
resolveAcpDispatchPolicyMessage,
|
resolveAcpDispatchPolicyMessage,
|
||||||
} from "../../../acp/policy.js";
|
} from "../../../acp/policy.js";
|
||||||
import {
|
|
||||||
resolveAcpSessionCwd,
|
|
||||||
resolveAcpThreadSessionDetailLines,
|
|
||||||
} from "../../../acp/runtime/session-identifiers.js";
|
|
||||||
import {
|
import {
|
||||||
resolveAcpSpawnRuntimePolicyError,
|
resolveAcpSpawnRuntimePolicyError,
|
||||||
resolveRuntimeCwdForAcpSpawn,
|
resolveRuntimeCwdForAcpSpawn,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { resolveAcpSessionIdentifierLinesFromIdentity } from "@openclaw/acp-core/runtime/session-identifiers";
|
||||||
import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
|
import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
|
||||||
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
|
||||||
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
|
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
validateRuntimeModelInput,
|
validateRuntimeModelInput,
|
||||||
validateRuntimePermissionProfileInput,
|
validateRuntimePermissionProfileInput,
|
||||||
} from "../../../acp/control-plane/runtime-options.js";
|
} from "../../../acp/control-plane/runtime-options.js";
|
||||||
import { resolveAcpSessionIdentifierLinesFromIdentity } from "../../../acp/runtime/session-identifiers.js";
|
|
||||||
import { findLatestTaskForRelatedSessionKeyForOwner } from "../../../tasks/task-owner-access.js";
|
import { findLatestTaskForRelatedSessionKeyForOwner } from "../../../tasks/task-owner-access.js";
|
||||||
import { sanitizeTaskStatusText } from "../../../tasks/task-status.js";
|
import { sanitizeTaskStatusText } from "../../../tasks/task-status.js";
|
||||||
import type { CommandHandlerResult, HandleCommandsParams } from "../commands-types.js";
|
import type { CommandHandlerResult, HandleCommandsParams } from "../commands-types.js";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { toAcpRuntimeErrorText } from "@openclaw/acp-core/runtime/error-text";
|
||||||
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
|
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
|
||||||
import {
|
import {
|
||||||
normalizeOptionalLowercaseString,
|
normalizeOptionalLowercaseString,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "@openclaw/normalization-core/string-coerce";
|
} from "@openclaw/normalization-core/string-coerce";
|
||||||
import { toAcpRuntimeErrorText } from "../../../acp/runtime/error-text.js";
|
|
||||||
import type { AcpRuntimeError } from "../../../acp/runtime/errors.js";
|
import type { AcpRuntimeError } from "../../../acp/runtime/errors.js";
|
||||||
import { supportsAutomaticThreadBindingSpawn } from "../../../channels/thread-bindings-policy.js";
|
import { supportsAutomaticThreadBindingSpawn } from "../../../channels/thread-bindings-policy.js";
|
||||||
import type { AcpSessionRuntimeOptions } from "../../../config/sessions/types.js";
|
import type { AcpSessionRuntimeOptions } from "../../../config/sessions/types.js";
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function buildFocusSessionBindingService() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
vi.mock("../../acp/runtime/session-identifiers.js", () => ({
|
vi.mock("@openclaw/acp-core/runtime/session-identifiers", () => ({
|
||||||
resolveAcpSessionCwd: () => undefined,
|
resolveAcpSessionCwd: () => undefined,
|
||||||
resolveAcpThreadSessionDetailLines: (params: {
|
resolveAcpThreadSessionDetailLines: (params: {
|
||||||
meta?: { identity?: Record<string, unknown> };
|
meta?: { identity?: Record<string, unknown> };
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
|
||||||
import {
|
import {
|
||||||
resolveAcpSessionCwd,
|
resolveAcpSessionCwd,
|
||||||
resolveAcpThreadSessionDetailLines,
|
resolveAcpThreadSessionDetailLines,
|
||||||
} from "../../../acp/runtime/session-identifiers.js";
|
} from "@openclaw/acp-core/runtime/session-identifiers";
|
||||||
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import { readAcpSessionEntry } from "../../../acp/runtime/session-meta.js";
|
import { readAcpSessionEntry } from "../../../acp/runtime/session-meta.js";
|
||||||
import { normalizeChatType } from "../../../channels/chat-type.js";
|
import { normalizeChatType } from "../../../channels/chat-type.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { resolveAcpSessionCwd } from "../../acp/runtime/session-identifiers.js";
|
import { resolveAcpSessionCwd } from "@openclaw/acp-core/runtime/session-identifiers";
|
||||||
import { resolveSessionAgentId } from "../../agents/agent-scope.js";
|
import { resolveSessionAgentId } from "../../agents/agent-scope.js";
|
||||||
import { persistAcpTurnTranscript } from "../../agents/command/attempt-execution.js";
|
import { persistAcpTurnTranscript } from "../../agents/command/attempt-execution.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
|
import { formatAcpRuntimeErrorText } from "@openclaw/acp-core/runtime/error-text";
|
||||||
|
import { resolveAcpThreadSessionDetailLines } from "@openclaw/acp-core/runtime/session-identifiers";
|
||||||
|
import {
|
||||||
|
isSessionIdentityPending,
|
||||||
|
resolveSessionIdentityFromMeta,
|
||||||
|
} from "@openclaw/acp-core/runtime/session-identity";
|
||||||
import {
|
import {
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
normalizeOptionalLowercaseString,
|
normalizeOptionalLowercaseString,
|
||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "@openclaw/normalization-core/string-coerce";
|
} from "@openclaw/normalization-core/string-coerce";
|
||||||
import { resolveAcpAgentPolicyError, resolveAcpDispatchPolicyError } from "../../acp/policy.js";
|
import { resolveAcpAgentPolicyError, resolveAcpDispatchPolicyError } from "../../acp/policy.js";
|
||||||
import { formatAcpRuntimeErrorText } from "../../acp/runtime/error-text.js";
|
|
||||||
import { type AcpRuntimeError, toAcpRuntimeError } from "../../acp/runtime/errors.js";
|
import { type AcpRuntimeError, toAcpRuntimeError } from "../../acp/runtime/errors.js";
|
||||||
import { resolveAcpThreadSessionDetailLines } from "../../acp/runtime/session-identifiers.js";
|
|
||||||
import {
|
|
||||||
isSessionIdentityPending,
|
|
||||||
resolveSessionIdentityFromMeta,
|
|
||||||
} from "../../acp/runtime/session-identity.js";
|
|
||||||
import { resolveAgentDir, resolveAgentWorkspaceDir } from "../../agents/agent-scope.js";
|
import { resolveAgentDir, resolveAgentWorkspaceDir } from "../../agents/agent-scope.js";
|
||||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||||
import type { TtsAutoMode } from "../../config/types.tts.js";
|
import type { TtsAutoMode } from "../../config/types.tts.js";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
import { isParentOwnedBackgroundAcpSession } from "@openclaw/acp-core/session-interaction-mode";
|
||||||
import {
|
import {
|
||||||
normalizeLowercaseStringOrEmpty,
|
normalizeLowercaseStringOrEmpty,
|
||||||
normalizeOptionalLowercaseString,
|
normalizeOptionalLowercaseString,
|
||||||
@@ -8,7 +9,6 @@ import {
|
|||||||
hasOutboundReplyContent,
|
hasOutboundReplyContent,
|
||||||
resolveSendableOutboundReplyParts,
|
resolveSendableOutboundReplyParts,
|
||||||
} from "openclaw/plugin-sdk/reply-payload";
|
} from "openclaw/plugin-sdk/reply-payload";
|
||||||
import { isParentOwnedBackgroundAcpSession } from "../../acp/session-interaction-mode.js";
|
|
||||||
import {
|
import {
|
||||||
resolveAgentConfig,
|
resolveAgentConfig,
|
||||||
resolveAgentWorkspaceDir,
|
resolveAgentWorkspaceDir,
|
||||||
@@ -206,7 +206,12 @@ function routeThreadIdsDiffer(
|
|||||||
function isSlackDirectRoutedThreadTurn(
|
function isSlackDirectRoutedThreadTurn(
|
||||||
ctx: Pick<
|
ctx: Pick<
|
||||||
FinalizedMsgContext,
|
FinalizedMsgContext,
|
||||||
"ChatType" | "MessageThreadId" | "OriginatingChannel" | "Provider" | "Surface" | "TransportThreadId"
|
| "ChatType"
|
||||||
|
| "MessageThreadId"
|
||||||
|
| "OriginatingChannel"
|
||||||
|
| "Provider"
|
||||||
|
| "Surface"
|
||||||
|
| "TransportThreadId"
|
||||||
>,
|
>,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (normalizeChatType(ctx.ChatType) !== "direct") {
|
if (normalizeChatType(ctx.ChatType) !== "direct") {
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
import type {
|
||||||
|
AcpSessionRuntimeOptions,
|
||||||
|
SessionAcpIdentity,
|
||||||
|
SessionAcpIdentitySource,
|
||||||
|
SessionAcpIdentityState,
|
||||||
|
SessionAcpMeta,
|
||||||
|
} from "@openclaw/acp-core/types";
|
||||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||||
import type { ChatType } from "../../channels/chat-type.js";
|
import type { ChatType } from "../../channels/chat-type.js";
|
||||||
import type { ChannelId } from "../../channels/plugins/channel-id.types.js";
|
import type { ChannelId } from "../../channels/plugins/channel-id.types.js";
|
||||||
@@ -26,49 +33,12 @@ export type SessionOrigin = {
|
|||||||
threadId?: string | number;
|
threadId?: string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SessionAcpIdentitySource = "ensure" | "status" | "event";
|
export type {
|
||||||
|
AcpSessionRuntimeOptions,
|
||||||
export type SessionAcpIdentityState = "pending" | "resolved";
|
SessionAcpIdentity,
|
||||||
|
SessionAcpIdentitySource,
|
||||||
export type SessionAcpIdentity = {
|
SessionAcpIdentityState,
|
||||||
state: SessionAcpIdentityState;
|
SessionAcpMeta,
|
||||||
acpxRecordId?: string;
|
|
||||||
acpxSessionId?: string;
|
|
||||||
agentSessionId?: string;
|
|
||||||
source: SessionAcpIdentitySource;
|
|
||||||
lastUpdatedAt: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SessionAcpMeta = {
|
|
||||||
backend: string;
|
|
||||||
agent: string;
|
|
||||||
runtimeSessionName: string;
|
|
||||||
identity?: SessionAcpIdentity;
|
|
||||||
mode: "persistent" | "oneshot";
|
|
||||||
runtimeOptions?: AcpSessionRuntimeOptions;
|
|
||||||
cwd?: string;
|
|
||||||
state: "idle" | "running" | "error";
|
|
||||||
lastActivityAt: number;
|
|
||||||
lastError?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AcpSessionRuntimeOptions = {
|
|
||||||
/**
|
|
||||||
* ACP runtime mode set via session/set_mode (for example: "plan", "normal", "auto").
|
|
||||||
*/
|
|
||||||
runtimeMode?: string;
|
|
||||||
/** ACP runtime config option: model id. */
|
|
||||||
model?: string;
|
|
||||||
/** ACP runtime config option: thinking/reasoning effort. */
|
|
||||||
thinking?: string;
|
|
||||||
/** Working directory override for ACP session turns. */
|
|
||||||
cwd?: string;
|
|
||||||
/** ACP runtime config option: permission profile id. */
|
|
||||||
permissionProfile?: string;
|
|
||||||
/** ACP runtime config option: per-turn timeout in seconds. */
|
|
||||||
timeoutSeconds?: number;
|
|
||||||
/** Backend-specific option bag mapped through session/set_config_option. */
|
|
||||||
backendExtras?: Record<string, string>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CliSessionBinding = {
|
export type CliSessionBinding = {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import type { DatabaseSync } from "node:sqlite";
|
import type { DatabaseSync } from "node:sqlite";
|
||||||
import type { Insertable, Selectable } from "kysely";
|
import type { Insertable, Selectable, SelectQueryBuilder } from "kysely";
|
||||||
import { executeSqliteQuerySync, getNodeSqliteKysely } from "../../infra/kysely-sync.js";
|
import {
|
||||||
|
executeSqliteQuerySync,
|
||||||
|
executeSqliteQueryTakeFirstSync,
|
||||||
|
getNodeSqliteKysely,
|
||||||
|
} from "../../infra/kysely-sync.js";
|
||||||
import type { DB as OpenClawStateKyselyDatabase } from "../../state/openclaw-state-db.generated.js";
|
import type { DB as OpenClawStateKyselyDatabase } from "../../state/openclaw-state-db.generated.js";
|
||||||
import type { CronRunLogEntry } from "../run-log-types.js";
|
import type { CronRunLogEntry } from "../run-log-types.js";
|
||||||
import type { CronDeliveryStatus, CronRunStatus } from "../types.js";
|
import type { CronDeliveryStatus, CronRunStatus } from "../types.js";
|
||||||
@@ -10,6 +14,13 @@ type CronRunLogsTable = OpenClawStateKyselyDatabase["cron_run_logs"];
|
|||||||
type CronRunLogDatabase = Pick<OpenClawStateKyselyDatabase, "cron_run_logs">;
|
type CronRunLogDatabase = Pick<OpenClawStateKyselyDatabase, "cron_run_logs">;
|
||||||
type CronRunLogRow = Selectable<CronRunLogsTable>;
|
type CronRunLogRow = Selectable<CronRunLogsTable>;
|
||||||
type CronRunLogInsert = Insertable<CronRunLogsTable>;
|
type CronRunLogInsert = Insertable<CronRunLogsTable>;
|
||||||
|
type CronRunLogFilterParams = {
|
||||||
|
storeKey: string;
|
||||||
|
jobId?: string;
|
||||||
|
statuses: CronRunStatus[] | null;
|
||||||
|
deliveryStatuses: CronDeliveryStatus[] | null;
|
||||||
|
runId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
function getCronRunLogKysely(db: DatabaseSync) {
|
function getCronRunLogKysely(db: DatabaseSync) {
|
||||||
return getNodeSqliteKysely<CronRunLogDatabase>(db);
|
return getNodeSqliteKysely<CronRunLogDatabase>(db);
|
||||||
@@ -110,37 +121,33 @@ export function readCronRunLogRows(
|
|||||||
return executeSqliteQuerySync(db, query.orderBy("ts", "asc").orderBy("seq", "asc")).rows;
|
return executeSqliteQuerySync(db, query.orderBy("ts", "asc").orderBy("seq", "asc")).rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRunLogWhereClause(params: {
|
function applyRunLogFilters<Output>(
|
||||||
storeKey: string;
|
query: SelectQueryBuilder<CronRunLogDatabase, "cron_run_logs", Output>,
|
||||||
jobId?: string;
|
params: CronRunLogFilterParams,
|
||||||
statuses: CronRunStatus[] | null;
|
): SelectQueryBuilder<CronRunLogDatabase, "cron_run_logs", Output> {
|
||||||
deliveryStatuses: CronDeliveryStatus[] | null;
|
let next = query.where("store_key", "=", params.storeKey);
|
||||||
runId?: string;
|
|
||||||
}): { whereSql: string; values: Array<string | number> } {
|
|
||||||
const clauses = ["store_key = ?"];
|
|
||||||
const values: Array<string | number> = [params.storeKey];
|
|
||||||
if (params.jobId) {
|
if (params.jobId) {
|
||||||
clauses.push("job_id = ?");
|
next = next.where("job_id", "=", params.jobId);
|
||||||
values.push(params.jobId);
|
|
||||||
}
|
}
|
||||||
if (params.statuses?.length) {
|
if (params.statuses?.length) {
|
||||||
clauses.push(`status IN (${params.statuses.map(() => "?").join(", ")})`);
|
next = next.where("status", "in", params.statuses);
|
||||||
values.push(...params.statuses);
|
|
||||||
}
|
}
|
||||||
if (params.deliveryStatuses?.length) {
|
if (params.deliveryStatuses?.length) {
|
||||||
clauses.push(
|
next = next.where((eb) =>
|
||||||
`COALESCE(delivery_status, 'not-requested') IN (${params.deliveryStatuses
|
eb.or(
|
||||||
.map(() => "?")
|
params.deliveryStatuses!.map((status) =>
|
||||||
.join(", ")})`,
|
status === "not-requested"
|
||||||
|
? eb.or([eb("delivery_status", "is", null), eb("delivery_status", "=", status)])
|
||||||
|
: eb("delivery_status", "=", status),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
values.push(...params.deliveryStatuses);
|
|
||||||
}
|
}
|
||||||
const runId = params.runId?.trim();
|
const runId = params.runId?.trim();
|
||||||
if (runId) {
|
if (runId) {
|
||||||
clauses.push("run_id = ?");
|
next = next.where("run_id", "=", runId);
|
||||||
values.push(runId);
|
|
||||||
}
|
}
|
||||||
return { whereSql: clauses.join(" AND "), values };
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function countCronRunLogRows(params: {
|
export function countCronRunLogRows(params: {
|
||||||
@@ -151,10 +158,15 @@ export function countCronRunLogRows(params: {
|
|||||||
deliveryStatuses: CronDeliveryStatus[] | null;
|
deliveryStatuses: CronDeliveryStatus[] | null;
|
||||||
runId?: string;
|
runId?: string;
|
||||||
}): number {
|
}): number {
|
||||||
const { whereSql, values } = buildRunLogWhereClause(params);
|
const row = executeSqliteQueryTakeFirstSync(
|
||||||
const row = params.db
|
params.db,
|
||||||
.prepare(`SELECT COUNT(*) AS count FROM cron_run_logs WHERE ${whereSql}`)
|
applyRunLogFilters(
|
||||||
.get(...values) as { count?: number | bigint } | undefined;
|
getCronRunLogKysely(params.db)
|
||||||
|
.selectFrom("cron_run_logs")
|
||||||
|
.select((eb) => eb.fn.countAll<number | bigint>().as("count")),
|
||||||
|
params,
|
||||||
|
),
|
||||||
|
);
|
||||||
return normalizeNumber(row?.count ?? null) ?? 0;
|
return normalizeNumber(row?.count ?? null) ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,25 +181,27 @@ export function readCronRunLogRowsPage(params: {
|
|||||||
offset?: number;
|
offset?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): CronRunLogRow[] {
|
}): CronRunLogRow[] {
|
||||||
const { whereSql, values } = buildRunLogWhereClause(params);
|
let query = applyRunLogFilters(
|
||||||
const order = params.sortDir === "asc" ? "ASC" : "DESC";
|
getCronRunLogKysely(params.db).selectFrom("cron_run_logs").selectAll(),
|
||||||
const limitSql =
|
params,
|
||||||
params.limit === undefined || params.offset === undefined ? "" : " LIMIT ? OFFSET ?";
|
)
|
||||||
const limitValues =
|
.orderBy("ts", params.sortDir)
|
||||||
params.limit === undefined || params.offset === undefined ? [] : [params.limit, params.offset];
|
.orderBy("seq", params.sortDir);
|
||||||
return params.db
|
if (params.limit !== undefined && params.offset !== undefined) {
|
||||||
.prepare(
|
query = query.limit(params.limit).offset(params.offset);
|
||||||
`SELECT * FROM cron_run_logs WHERE ${whereSql} ORDER BY ts ${order}, seq ${order}${limitSql}`,
|
}
|
||||||
)
|
return executeSqliteQuerySync(params.db, query).rows;
|
||||||
.all(...values, ...limitValues) as CronRunLogRow[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextCronRunLogSeq(db: DatabaseSync, storeKey: string, jobId: string): number {
|
function nextCronRunLogSeq(db: DatabaseSync, storeKey: string, jobId: string): number {
|
||||||
const row = db
|
const row = executeSqliteQueryTakeFirstSync(
|
||||||
.prepare(
|
db,
|
||||||
"SELECT COALESCE(MAX(seq), 0) AS seq FROM cron_run_logs WHERE store_key = ? AND job_id = ?",
|
getCronRunLogKysely(db)
|
||||||
)
|
.selectFrom("cron_run_logs")
|
||||||
.get(storeKey, jobId) as { seq?: number | bigint } | undefined;
|
.select((eb) => eb.fn.max<number | bigint>("seq").as("seq"))
|
||||||
|
.where("store_key", "=", storeKey)
|
||||||
|
.where("job_id", "=", jobId),
|
||||||
|
);
|
||||||
return (normalizeNumber(row?.seq ?? null) ?? 0) + 1;
|
return (normalizeNumber(row?.seq ?? null) ?? 0) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,14 +226,19 @@ export function pruneCronRunLogRows(
|
|||||||
keepLines: number,
|
keepLines: number,
|
||||||
): void {
|
): void {
|
||||||
const keep = Math.max(1, Math.floor(keepLines));
|
const keep = Math.max(1, Math.floor(keepLines));
|
||||||
db.prepare(
|
const keepSeqs = getCronRunLogKysely(db)
|
||||||
`DELETE FROM cron_run_logs
|
.selectFrom("cron_run_logs")
|
||||||
WHERE store_key = ? AND job_id = ?
|
.select("seq")
|
||||||
AND seq NOT IN (
|
.where("store_key", "=", storeKey)
|
||||||
SELECT seq FROM cron_run_logs
|
.where("job_id", "=", jobId)
|
||||||
WHERE store_key = ? AND job_id = ?
|
.orderBy("seq", "desc")
|
||||||
ORDER BY seq DESC
|
.limit(keep);
|
||||||
LIMIT ?
|
executeSqliteQuerySync(
|
||||||
)`,
|
db,
|
||||||
).run(storeKey, jobId, storeKey, jobId, keep);
|
getCronRunLogKysely(db)
|
||||||
|
.deleteFrom("cron_run_logs")
|
||||||
|
.where("store_key", "=", storeKey)
|
||||||
|
.where("job_id", "=", jobId)
|
||||||
|
.where("seq", "not in", keepSeqs),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -858,7 +858,7 @@ export async function startGatewaySidecars(params: {
|
|||||||
const [{ getAcpSessionManager }, { ACP_SESSION_IDENTITY_RENDERER_VERSION }] =
|
const [{ getAcpSessionManager }, { ACP_SESSION_IDENTITY_RENDERER_VERSION }] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
import("../acp/control-plane/manager.js"),
|
import("../acp/control-plane/manager.js"),
|
||||||
import("../acp/runtime/session-identifiers.js"),
|
import("@openclaw/acp-core/runtime/session-identifiers"),
|
||||||
]);
|
]);
|
||||||
const result = await getAcpSessionManager().reconcilePendingSessionIdentities({
|
const result = await getAcpSessionManager().reconcilePendingSessionIdentities({
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
|
|||||||
@@ -79,8 +79,18 @@ const INTERNAL_CORE_PACKAGE_ALIASES = [
|
|||||||
packageDir: "acp-core",
|
packageDir: "acp-core",
|
||||||
subpaths: [
|
subpaths: [
|
||||||
["", "index.ts"],
|
["", "index.ts"],
|
||||||
|
["meta", "meta.ts"],
|
||||||
["normalize-text", "normalize-text.ts"],
|
["normalize-text", "normalize-text.ts"],
|
||||||
|
["numeric-options", "numeric-options.ts"],
|
||||||
["record-shared", "record-shared.ts"],
|
["record-shared", "record-shared.ts"],
|
||||||
|
["session", "session.ts"],
|
||||||
|
["session-interaction-mode", "session-interaction-mode.ts"],
|
||||||
|
["session-lineage-meta", "session-lineage-meta.ts"],
|
||||||
|
["types", "types.ts"],
|
||||||
|
["runtime/error-text", path.join("runtime", "error-text.ts")],
|
||||||
|
["runtime/errors", path.join("runtime", "errors.ts")],
|
||||||
|
["runtime/session-identifiers", path.join("runtime", "session-identifiers.ts")],
|
||||||
|
["runtime/session-identity", path.join("runtime", "session-identity.ts")],
|
||||||
["runtime/types", path.join("runtime", "types.ts")],
|
["runtime/types", path.join("runtime", "types.ts")],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -754,6 +754,13 @@ const WORKSPACE_PACKAGE_ALIAS_ENTRIES = [
|
|||||||
srcFile: "index.ts",
|
srcFile: "index.ts",
|
||||||
distFile: "index.mjs",
|
distFile: "index.mjs",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "meta",
|
||||||
|
srcFile: "meta.ts",
|
||||||
|
distFile: "meta.mjs",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
packageName: "@openclaw/acp-core",
|
packageName: "@openclaw/acp-core",
|
||||||
packageDir: "acp-core",
|
packageDir: "acp-core",
|
||||||
@@ -761,6 +768,13 @@ const WORKSPACE_PACKAGE_ALIAS_ENTRIES = [
|
|||||||
srcFile: "normalize-text.ts",
|
srcFile: "normalize-text.ts",
|
||||||
distFile: "normalize-text.mjs",
|
distFile: "normalize-text.mjs",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "numeric-options",
|
||||||
|
srcFile: "numeric-options.ts",
|
||||||
|
distFile: "numeric-options.mjs",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
packageName: "@openclaw/acp-core",
|
packageName: "@openclaw/acp-core",
|
||||||
packageDir: "acp-core",
|
packageDir: "acp-core",
|
||||||
@@ -768,6 +782,62 @@ const WORKSPACE_PACKAGE_ALIAS_ENTRIES = [
|
|||||||
srcFile: "record-shared.ts",
|
srcFile: "record-shared.ts",
|
||||||
distFile: "record-shared.mjs",
|
distFile: "record-shared.mjs",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "session",
|
||||||
|
srcFile: "session.ts",
|
||||||
|
distFile: "session.mjs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "session-interaction-mode",
|
||||||
|
srcFile: "session-interaction-mode.ts",
|
||||||
|
distFile: "session-interaction-mode.mjs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "session-lineage-meta",
|
||||||
|
srcFile: "session-lineage-meta.ts",
|
||||||
|
distFile: "session-lineage-meta.mjs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "types",
|
||||||
|
srcFile: "types.ts",
|
||||||
|
distFile: "types.mjs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "runtime/error-text",
|
||||||
|
srcFile: path.join("runtime", "error-text.ts"),
|
||||||
|
distFile: path.join("runtime", "error-text.mjs"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "runtime/errors",
|
||||||
|
srcFile: path.join("runtime", "errors.ts"),
|
||||||
|
distFile: path.join("runtime", "errors.mjs"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "runtime/session-identifiers",
|
||||||
|
srcFile: path.join("runtime", "session-identifiers.ts"),
|
||||||
|
distFile: path.join("runtime", "session-identifiers.mjs"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageName: "@openclaw/acp-core",
|
||||||
|
packageDir: "acp-core",
|
||||||
|
subpath: "runtime/session-identity",
|
||||||
|
srcFile: path.join("runtime", "session-identity.ts"),
|
||||||
|
distFile: path.join("runtime", "session-identity.mjs"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
packageName: "@openclaw/acp-core",
|
packageName: "@openclaw/acp-core",
|
||||||
packageDir: "acp-core",
|
packageDir: "acp-core",
|
||||||
|
|||||||
@@ -401,8 +401,18 @@ export const sharedVitestConfig = {
|
|||||||
sourcePackageAlias("media-core", "read-byte-stream-with-limit"),
|
sourcePackageAlias("media-core", "read-byte-stream-with-limit"),
|
||||||
sourcePackageAlias("media-core", "read-response-with-limit"),
|
sourcePackageAlias("media-core", "read-response-with-limit"),
|
||||||
sourcePackageAlias("media-core"),
|
sourcePackageAlias("media-core"),
|
||||||
|
sourcePackageAlias("acp-core", "meta"),
|
||||||
sourcePackageAlias("acp-core", "normalize-text"),
|
sourcePackageAlias("acp-core", "normalize-text"),
|
||||||
|
sourcePackageAlias("acp-core", "numeric-options"),
|
||||||
sourcePackageAlias("acp-core", "record-shared"),
|
sourcePackageAlias("acp-core", "record-shared"),
|
||||||
|
sourcePackageAlias("acp-core", "session"),
|
||||||
|
sourcePackageAlias("acp-core", "session-interaction-mode"),
|
||||||
|
sourcePackageAlias("acp-core", "session-lineage-meta"),
|
||||||
|
sourcePackageAlias("acp-core", "types"),
|
||||||
|
sourcePackageAlias("acp-core", "runtime/error-text"),
|
||||||
|
sourcePackageAlias("acp-core", "runtime/errors"),
|
||||||
|
sourcePackageAlias("acp-core", "runtime/session-identifiers"),
|
||||||
|
sourcePackageAlias("acp-core", "runtime/session-identity"),
|
||||||
sourcePackageAlias("acp-core", "runtime/types"),
|
sourcePackageAlias("acp-core", "runtime/types"),
|
||||||
sourcePackageAlias("acp-core"),
|
sourcePackageAlias("acp-core"),
|
||||||
...sourcePluginSdkSubpaths.map((subpath) => ({
|
...sourcePluginSdkSubpaths.map((subpath) => ({
|
||||||
|
|||||||
@@ -143,8 +143,28 @@
|
|||||||
],
|
],
|
||||||
"@openclaw/normalization-core/*": ["./packages/normalization-core/src/*"],
|
"@openclaw/normalization-core/*": ["./packages/normalization-core/src/*"],
|
||||||
"@openclaw/acp-core": ["./packages/acp-core/src/index.ts"],
|
"@openclaw/acp-core": ["./packages/acp-core/src/index.ts"],
|
||||||
|
"@openclaw/acp-core/meta": ["./packages/acp-core/src/meta.ts"],
|
||||||
|
"@openclaw/acp-core/numeric-options": ["./packages/acp-core/src/numeric-options.ts"],
|
||||||
"@openclaw/acp-core/normalize-text": ["./packages/acp-core/src/normalize-text.ts"],
|
"@openclaw/acp-core/normalize-text": ["./packages/acp-core/src/normalize-text.ts"],
|
||||||
"@openclaw/acp-core/record-shared": ["./packages/acp-core/src/record-shared.ts"],
|
"@openclaw/acp-core/record-shared": ["./packages/acp-core/src/record-shared.ts"],
|
||||||
|
"@openclaw/acp-core/session": ["./packages/acp-core/src/session.ts"],
|
||||||
|
"@openclaw/acp-core/session-interaction-mode": [
|
||||||
|
"./packages/acp-core/src/session-interaction-mode.ts"
|
||||||
|
],
|
||||||
|
"@openclaw/acp-core/session-lineage-meta": [
|
||||||
|
"./packages/acp-core/src/session-lineage-meta.ts"
|
||||||
|
],
|
||||||
|
"@openclaw/acp-core/types": ["./packages/acp-core/src/types.ts"],
|
||||||
|
"@openclaw/acp-core/runtime/error-text": [
|
||||||
|
"./packages/acp-core/src/runtime/error-text.ts"
|
||||||
|
],
|
||||||
|
"@openclaw/acp-core/runtime/errors": ["./packages/acp-core/src/runtime/errors.ts"],
|
||||||
|
"@openclaw/acp-core/runtime/session-identifiers": [
|
||||||
|
"./packages/acp-core/src/runtime/session-identifiers.ts"
|
||||||
|
],
|
||||||
|
"@openclaw/acp-core/runtime/session-identity": [
|
||||||
|
"./packages/acp-core/src/runtime/session-identity.ts"
|
||||||
|
],
|
||||||
"@openclaw/acp-core/runtime/types": ["./packages/acp-core/src/runtime/types.ts"],
|
"@openclaw/acp-core/runtime/types": ["./packages/acp-core/src/runtime/types.ts"],
|
||||||
"@openclaw/acp-core/*": ["./packages/acp-core/src/*"],
|
"@openclaw/acp-core/*": ["./packages/acp-core/src/*"],
|
||||||
"@openclaw/terminal-core": ["./packages/terminal-core/src/index.ts"],
|
"@openclaw/terminal-core": ["./packages/terminal-core/src/index.ts"],
|
||||||
|
|||||||
@@ -459,9 +459,20 @@ function buildMediaCoreDistEntries(): Record<string, string> {
|
|||||||
|
|
||||||
function buildAcpCoreDistEntries(): Record<string, string> {
|
function buildAcpCoreDistEntries(): Record<string, string> {
|
||||||
return {
|
return {
|
||||||
|
"error-format": "packages/acp-core/src/error-format.ts",
|
||||||
index: "packages/acp-core/src/index.ts",
|
index: "packages/acp-core/src/index.ts",
|
||||||
|
meta: "packages/acp-core/src/meta.ts",
|
||||||
"normalize-text": "packages/acp-core/src/normalize-text.ts",
|
"normalize-text": "packages/acp-core/src/normalize-text.ts",
|
||||||
|
"numeric-options": "packages/acp-core/src/numeric-options.ts",
|
||||||
"record-shared": "packages/acp-core/src/record-shared.ts",
|
"record-shared": "packages/acp-core/src/record-shared.ts",
|
||||||
|
session: "packages/acp-core/src/session.ts",
|
||||||
|
"session-interaction-mode": "packages/acp-core/src/session-interaction-mode.ts",
|
||||||
|
"session-lineage-meta": "packages/acp-core/src/session-lineage-meta.ts",
|
||||||
|
types: "packages/acp-core/src/types.ts",
|
||||||
|
"runtime/error-text": "packages/acp-core/src/runtime/error-text.ts",
|
||||||
|
"runtime/errors": "packages/acp-core/src/runtime/errors.ts",
|
||||||
|
"runtime/session-identifiers": "packages/acp-core/src/runtime/session-identifiers.ts",
|
||||||
|
"runtime/session-identity": "packages/acp-core/src/runtime/session-identity.ts",
|
||||||
"runtime/types": "packages/acp-core/src/runtime/types.ts",
|
"runtime/types": "packages/acp-core/src/runtime/types.ts",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user