@openclaw/acpx 2026.9.7 → 2026.10.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/assets/icon.png +0 -0
- package/dist/.setup/{config-CdPsIued.mjs → config-76krYh6C.mjs} +145 -17
- package/dist/.setup/{pi-session-catalog-runtime-UhiQSaDw.mjs → pi-session-catalog-runtime-7jc50ZJ4.mjs} +21 -56
- package/dist/.setup/{register.runtime-Bm4HrMCp.mjs → register.runtime-C0nVOTzT.mjs} +15 -38
- package/dist/.setup/runtime-doctor-migrations-hxP-CXX7.mjs +191 -0
- package/dist/.setup/{service-DMae5pPH.mjs → service-BXgGdE56.mjs} +111 -183
- package/dist/.setup/session-owner-migration-C73lfijV.mjs +431 -0
- package/dist/.setup/{session-resource-BaJ6bs5d.mjs → session-resource-DKJsUJPu.mjs} +1 -13
- package/dist/doctor-contract-api.js +10 -104
- package/dist/index.js +43 -22
- package/dist/register.runtime.js +1 -1
- package/openclaw.plugin.json +0 -1
- package/package.json +7 -16
- package/skills/acp-router/SKILL.md +2 -2
- package/dist/.setup/command-line-CPBLOiZM.mjs +0 -138
- package/dist/.setup/process-lease-C3dNPtu8.mjs +0 -138
- package/dist/.setup/session-owner-migration-CdJSYRk1.mjs +0 -203
- package/dist/mcp-command-line.mjs +0 -128
- package/dist/mcp-proxy.mjs +0 -159
package/README.md
CHANGED
|
@@ -12,6 +12,12 @@ openclaw plugins install @openclaw/acpx
|
|
|
12
12
|
|
|
13
13
|
Restart the Gateway after installing or updating the plugin.
|
|
14
14
|
|
|
15
|
+
The generated Codex and Claude ACP wrappers prefer the installed adapter while its
|
|
16
|
+
entry file exists. If plugin capture cleanup removes that file, the next launch
|
|
17
|
+
uses the existing pinned `npm exec` / `npx` fallback. This may require registry
|
|
18
|
+
access when the pinned package is not cached. Restart the Gateway after updating
|
|
19
|
+
to regenerate wrappers written by an older plugin version.
|
|
20
|
+
|
|
15
21
|
## What it provides
|
|
16
22
|
|
|
17
23
|
- ACP-backed agent runtime sessions.
|
package/assets/icon.png
CHANGED
|
Binary file
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { s as splitCommandParts } from "./command-line-CPBLOiZM.mjs";
|
|
2
1
|
import { createRequire } from "node:module";
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
import fs from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
6
|
import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
|
|
8
|
-
import {
|
|
7
|
+
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
8
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
9
|
//#region extensions/acpx/src/config-schema.ts
|
|
10
10
|
/**
|
|
11
11
|
* ACPX plugin configuration schema and public config types. Runtime setup uses
|
|
@@ -56,13 +56,145 @@ const AcpxPluginConfigSchema = z.strictObject({
|
|
|
56
56
|
})).optional()
|
|
57
57
|
});
|
|
58
58
|
//#endregion
|
|
59
|
+
//#region extensions/acpx/src/codex-adapter.ts
|
|
60
|
+
const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
|
|
61
|
+
const CODEX_ACP_BIN = "codex-acp";
|
|
62
|
+
const LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
|
|
63
|
+
const OPENCLAW_CODEX_CONFIG_ARG = "--openclaw-codex-config";
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region extensions/acpx/src/command-line.ts
|
|
66
|
+
/** Match ACPX's persisted argv identity; scalar records keep their original bytes. */
|
|
67
|
+
function renderAgentCommand(command) {
|
|
68
|
+
return typeof command === "string" ? command : command.map((part) => /^[A-Za-z0-9_@%+=:,./^~-]+$/.test(part) ? part : JSON.stringify(part)).join(" ");
|
|
69
|
+
}
|
|
70
|
+
/** Split a command string into argv-like parts using simple quote/backslash rules. */
|
|
71
|
+
function splitCommandParts(value) {
|
|
72
|
+
if (Array.isArray(value)) return value;
|
|
73
|
+
const windows = process.platform === "win32";
|
|
74
|
+
const parts = [];
|
|
75
|
+
let current = "";
|
|
76
|
+
let quote = null;
|
|
77
|
+
let escaping = false;
|
|
78
|
+
let hasPart = false;
|
|
79
|
+
for (const ch of value) {
|
|
80
|
+
if (escaping) {
|
|
81
|
+
current += ch;
|
|
82
|
+
escaping = false;
|
|
83
|
+
hasPart = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (ch === "\\" && quote !== "'" && !windows) {
|
|
87
|
+
escaping = true;
|
|
88
|
+
hasPart = true;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (windows && ch === "\"" && quote !== "'") {
|
|
92
|
+
const backslashes = current.match(/\\+$/)?.[0].length ?? 0;
|
|
93
|
+
current = current.slice(0, current.length - backslashes) + "\\".repeat(Math.floor(backslashes / 2));
|
|
94
|
+
if (backslashes % 2 === 1) {
|
|
95
|
+
current += "\"";
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (quote) {
|
|
100
|
+
if (ch === quote) quote = null;
|
|
101
|
+
else current += ch;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (ch === "'" || ch === "\"") {
|
|
105
|
+
quote = ch;
|
|
106
|
+
hasPart = true;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (/\s/.test(ch)) {
|
|
110
|
+
if (hasPart) {
|
|
111
|
+
parts.push(current);
|
|
112
|
+
current = "";
|
|
113
|
+
hasPart = false;
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
current += ch;
|
|
118
|
+
hasPart = true;
|
|
119
|
+
}
|
|
120
|
+
if (escaping) current += "\\";
|
|
121
|
+
if (quote) throw new Error("Invalid agent command: unterminated quote");
|
|
122
|
+
if (hasPart) parts.push(current);
|
|
123
|
+
return parts;
|
|
124
|
+
}
|
|
125
|
+
function basename(value) {
|
|
126
|
+
return value.split(/[\\/]/).pop() ?? value;
|
|
127
|
+
}
|
|
128
|
+
function isEnvAssignment(value) {
|
|
129
|
+
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value);
|
|
130
|
+
}
|
|
131
|
+
function unwrapEnvCommand(parts) {
|
|
132
|
+
const command = parts.at(0);
|
|
133
|
+
if (!command || basename(command) !== "env") return parts;
|
|
134
|
+
let index = 1;
|
|
135
|
+
while (true) {
|
|
136
|
+
const part = parts.at(index);
|
|
137
|
+
if (!part || !isEnvAssignment(part)) break;
|
|
138
|
+
index += 1;
|
|
139
|
+
}
|
|
140
|
+
return parts.slice(index);
|
|
141
|
+
}
|
|
142
|
+
function matchesExecutableName(value, executableName) {
|
|
143
|
+
const normalized = basename(value).toLowerCase();
|
|
144
|
+
return normalized === executableName || normalized === `${executableName}.exe`;
|
|
145
|
+
}
|
|
146
|
+
function matchesPackageSpec(value, packageName) {
|
|
147
|
+
const normalized = value.trim().toLowerCase();
|
|
148
|
+
return normalized === packageName || normalized.startsWith(`${packageName}@`);
|
|
149
|
+
}
|
|
150
|
+
function stripModuleExtension(value) {
|
|
151
|
+
return value.replace(/\.[cm]?js$/i, "").toLowerCase();
|
|
152
|
+
}
|
|
153
|
+
function isAcpCommand(command, params) {
|
|
154
|
+
if (!command) return false;
|
|
155
|
+
const parts = unwrapEnvCommand(splitCommandParts(command));
|
|
156
|
+
if (!parts.length) return false;
|
|
157
|
+
if (parts.some((part) => matchesPackageSpec(part, params.packageName))) return true;
|
|
158
|
+
const commandName = basename(parts[0] ?? "");
|
|
159
|
+
if (matchesExecutableName(commandName, params.executableName)) return true;
|
|
160
|
+
if (!matchesExecutableName(commandName, "node")) return false;
|
|
161
|
+
const scriptName = stripModuleExtension(basename(parts[1] ?? ""));
|
|
162
|
+
return scriptName === params.executableName || scriptName === `${params.executableName}-wrapper`;
|
|
163
|
+
}
|
|
164
|
+
function isOpenClawBridgeCommand(command) {
|
|
165
|
+
const [executable = "", ...args] = command ? unwrapEnvCommand(splitCommandParts(command)) : [];
|
|
166
|
+
const runtime = basename(executable) !== "openclaw" && (executable === process.execPath || /^(?:node|bun)(?:\.exe)?$/i.test(basename(executable)));
|
|
167
|
+
const scriptName = basename(runtime ? args[0] ?? "" : executable);
|
|
168
|
+
return (runtime ? /^openclaw(?:\.[cm]?js)?$/i.test(scriptName) : scriptName === "openclaw") && args[runtime ? 1 : 0] === "acp";
|
|
169
|
+
}
|
|
170
|
+
function isCodexAcpCommand(command) {
|
|
171
|
+
return isAcpCommand(command, {
|
|
172
|
+
packageName: CODEX_ACP_PACKAGE,
|
|
173
|
+
executableName: "codex-acp"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function isClaudeAcpCommand(command) {
|
|
177
|
+
return isAcpCommand(command, {
|
|
178
|
+
packageName: "@agentclientprotocol/claude-agent-acp",
|
|
179
|
+
executableName: "claude-agent-acp"
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function resolveAgentCommand(params) {
|
|
183
|
+
const normalizedAgentName = normalizeOptionalLowercaseString(params.agentName);
|
|
184
|
+
if (!normalizedAgentName) return;
|
|
185
|
+
return splitCommandParts(params.agentRegistry.resolve(normalizedAgentName));
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
59
188
|
//#region extensions/acpx/src/config.ts
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
189
|
+
const MANAGED_MCP_BRIDGES = [[
|
|
190
|
+
"pluginToolsMcpBridge",
|
|
191
|
+
"openclaw-plugin-tools",
|
|
192
|
+
"plugin-tools-serve"
|
|
193
|
+
], [
|
|
194
|
+
"openClawToolsMcpBridge",
|
|
195
|
+
"openclaw-tools",
|
|
196
|
+
"openclaw-tools-serve"
|
|
197
|
+
]];
|
|
66
198
|
const requireFromHere = createRequire(import.meta.url);
|
|
67
199
|
function isAcpxPluginRoot(dir) {
|
|
68
200
|
return fs.existsSync(path.join(dir, "openclaw.plugin.json")) && fs.existsSync(path.join(dir, "package.json"));
|
|
@@ -142,13 +274,10 @@ function resolveManagedToolsMcpServerConfig(entryPoint, moduleUrl = import.meta.
|
|
|
142
274
|
}
|
|
143
275
|
function resolveConfiguredMcpServers(params) {
|
|
144
276
|
const resolved = { ...params.mcpServers };
|
|
145
|
-
if (params
|
|
146
|
-
|
|
147
|
-
if (params.pluginToolsMcpBridge) resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME] = resolveManagedToolsMcpServerConfig("plugin-tools-serve", params.moduleUrl);
|
|
148
|
-
if (params.openClawToolsMcpBridge) resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME] = resolveManagedToolsMcpServerConfig("openclaw-tools-serve", params.moduleUrl);
|
|
277
|
+
for (const [flag, name] of MANAGED_MCP_BRIDGES) if (params[flag] && resolved[name]) throw new Error(`mcpServers.${name} is reserved when ${flag}=true`);
|
|
278
|
+
for (const [flag, name, entryPoint] of MANAGED_MCP_BRIDGES) if (params[flag]) resolved[name] = resolveManagedToolsMcpServerConfig(entryPoint, params.moduleUrl);
|
|
149
279
|
return resolved;
|
|
150
280
|
}
|
|
151
|
-
/** Convert OpenClaw MCP server config into ACPX runtime MCP server entries. */
|
|
152
281
|
function toAcpMcpServers(mcpServers) {
|
|
153
282
|
return Object.entries(mcpServers).map(([name, server]) => ({
|
|
154
283
|
name,
|
|
@@ -160,15 +289,14 @@ function toAcpMcpServers(mcpServers) {
|
|
|
160
289
|
}))
|
|
161
290
|
}));
|
|
162
291
|
}
|
|
163
|
-
/** Validate and normalize raw ACPX plugin config for runtime startup. */
|
|
164
292
|
function resolveAcpxPluginConfig(params) {
|
|
165
293
|
const { rawConfig } = params;
|
|
166
294
|
const parsed = AcpxPluginConfigSchema.safeParse(rawConfig === void 0 ? {} : rawConfig);
|
|
167
295
|
if (!parsed.success) throw new Error(formatPluginConfigIssue(parsed.error.issues[0]));
|
|
168
296
|
const normalized = parsed.data;
|
|
169
297
|
const workspaceDir = params.workspaceDir?.trim() || process.cwd();
|
|
170
|
-
const cwd = path.resolve(normalized.cwd
|
|
171
|
-
const stateDir = path.resolve(normalized.stateDir
|
|
298
|
+
const cwd = path.resolve(normalized.cwd ?? workspaceDir);
|
|
299
|
+
const stateDir = path.resolve(normalized.stateDir ?? path.join(params.stateDir ?? resolveStateDir(), "acpx"));
|
|
172
300
|
const pluginToolsMcpBridge = normalized.pluginToolsMcpBridge === true;
|
|
173
301
|
const openClawToolsMcpBridge = normalized.openClawToolsMcpBridge === true;
|
|
174
302
|
const mcpServers = resolveConfiguredMcpServers({
|
|
@@ -196,4 +324,4 @@ function resolveAcpxPluginConfig(params) {
|
|
|
196
324
|
};
|
|
197
325
|
}
|
|
198
326
|
//#endregion
|
|
199
|
-
export {
|
|
327
|
+
export { isClaudeAcpCommand as a, renderAgentCommand as c, CODEX_ACP_BIN as d, CODEX_ACP_PACKAGE as f, AcpxNativeAgentsSchema as g, ACPX_NATIVE_AGENT_IDS as h, toAcpMcpServers as i, resolveAgentCommand as l, OPENCLAW_CODEX_CONFIG_ARG as m, resolveAcpxPluginRoot as n, isCodexAcpCommand as o, LEGACY_CODEX_ACP_PACKAGE as p, resolveOpenClawRoot as r, isOpenClawBridgeCommand as s, resolveAcpxPluginConfig as t, splitCommandParts as u };
|
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import { c as PI_SESSION_READ_COMMAND, i as PI_LOCAL_SESSION_HOST_ID, l as PI_TERMINAL_RESUME_COMMAND, n as piSessionStore, o as PI_SESSIONS_LIST_COMMAND, r as piSessionStoreAvailable, s as PI_SESSION_ID_PATTERN, t as piAcpSessionStoreRoot } from "./pi-session-paths-CIvyk6KB.mjs";
|
|
2
|
-
import { parseDateFirstTimestampMs } from "openclaw/plugin-sdk/number-runtime";
|
|
2
|
+
import { parseDateFirstTimestampMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { isRecord, normalizeBoundedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import { resolveNodeHostExecutable } from "openclaw/plugin-sdk/node-host";
|
|
7
7
|
import { createSessionCatalogFamily, importSessionCatalogHistory, isExternalUserText, listAdoptedSessionCatalogSessions, sessionCatalogAdoptedSessionKey } from "openclaw/plugin-sdk/session-catalog";
|
|
8
|
-
import fs$1 from "node:fs/promises";
|
|
9
8
|
import { resolveAcpSessionAvailability } from "openclaw/plugin-sdk/acp-runtime";
|
|
10
9
|
import process from "node:process";
|
|
11
10
|
import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
12
11
|
import { sessionCatalogPaging } from "openclaw/plugin-sdk/session-catalog-paging";
|
|
13
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
|
+
import { safeParseJson, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
|
+
import fs$1 from "node:fs/promises";
|
|
14
14
|
import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
|
|
15
15
|
import { isPathStrictlyInside, readFileRangeAsync } from "openclaw/plugin-sdk/file-access-runtime";
|
|
16
|
-
//#region extensions/acpx/src/pi-session-timestamp.ts
|
|
17
|
-
/** Preserve Pi JSONL's date-first string contract while accepting numeric millisecond values. */
|
|
18
|
-
function parsePiSessionTimestampMs(value) {
|
|
19
|
-
return parseDateFirstTimestampMs(value);
|
|
20
|
-
}
|
|
21
|
-
//#endregion
|
|
22
16
|
//#region extensions/acpx/src/pi-session-store.ts
|
|
23
17
|
const MAX_DISCOVERY_FILES = 1e4;
|
|
24
18
|
const SUMMARY_SCAN_BATCH_SIZE = 100;
|
|
@@ -29,7 +23,6 @@ const APPEND_PROOF_EDGE_BYTES = 65536;
|
|
|
29
23
|
const IO_CONCURRENCY = 8;
|
|
30
24
|
const PI_FILE_CANDIDATE_CACHE_TTL_MS = 32e3;
|
|
31
25
|
const PI_FILE_CANDIDATE_CACHE_MAX_ENTRIES = 8;
|
|
32
|
-
const SESSION_ID_PATTERN = /^(?!-)[A-Za-z0-9._:-]{1,256}$/u;
|
|
33
26
|
const summaryCache = /* @__PURE__ */ new Map();
|
|
34
27
|
const threadFileCache = /* @__PURE__ */ new Map();
|
|
35
28
|
const piFileCandidateCache = /* @__PURE__ */ new Map();
|
|
@@ -153,15 +146,11 @@ async function piFileCandidates(env) {
|
|
|
153
146
|
function parsePiJsonLines(content) {
|
|
154
147
|
return content.split(/\r?\n/u).flatMap((line) => {
|
|
155
148
|
if (!line.trim()) return [];
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return isRecord(value) ? [value] : [];
|
|
159
|
-
} catch {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
149
|
+
const value = safeParseJson(line);
|
|
150
|
+
return isRecord(value) ? [value] : [];
|
|
162
151
|
});
|
|
163
152
|
}
|
|
164
|
-
function
|
|
153
|
+
function piMessageText(content) {
|
|
165
154
|
if (typeof content === "string") return content;
|
|
166
155
|
if (!Array.isArray(content)) return "";
|
|
167
156
|
return content.flatMap((part) => isRecord(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n");
|
|
@@ -178,7 +167,7 @@ function processSummaryLine(state, line) {
|
|
|
178
167
|
return;
|
|
179
168
|
}
|
|
180
169
|
if (entry.type === "session_info") state.name = normalizeBoundedOptionalString(entry.name, 1e3);
|
|
181
|
-
else if (!state.firstMessage && entry.type === "message" && isRecord(entry.message) && entry.message.role === "user") state.firstMessage = normalizeBoundedOptionalString(
|
|
170
|
+
else if (!state.firstMessage && entry.type === "message" && isRecord(entry.message) && entry.message.role === "user") state.firstMessage = normalizeBoundedOptionalString(piMessageText(entry.message.content), 1e3);
|
|
182
171
|
}
|
|
183
172
|
function appendSummaryBytes(state, bytes) {
|
|
184
173
|
if (state.discarding || bytes.length === 0) return;
|
|
@@ -268,9 +257,9 @@ async function readPiSessionSummary(candidate) {
|
|
|
268
257
|
const { header, name, firstMessage } = projectedState;
|
|
269
258
|
const version = header?.type === "session" && typeof header.version === "number" ? header.version : 1;
|
|
270
259
|
const threadId = header?.type === "session" ? normalizeBoundedOptionalString(header.id, 256) : void 0;
|
|
271
|
-
if (header && threadId &&
|
|
260
|
+
if (header && threadId && PI_SESSION_ID_PATTERN.test(threadId)) {
|
|
272
261
|
const cwd = normalizeBoundedOptionalString(header.cwd, 4096);
|
|
273
|
-
const createdAt =
|
|
262
|
+
const createdAt = parseDateFirstTimestampMs(header.timestamp);
|
|
274
263
|
summary = {
|
|
275
264
|
file: candidate.file,
|
|
276
265
|
version,
|
|
@@ -383,7 +372,7 @@ async function readPiSessionById(threadId, env) {
|
|
|
383
372
|
//#region extensions/acpx/src/pi-session-catalog.ts
|
|
384
373
|
const MAX_SEARCH_LENGTH = 500;
|
|
385
374
|
const isExactPiSessionCursor = sessionCatalogPaging.isExactCursor;
|
|
386
|
-
function textFromContent
|
|
375
|
+
function textFromContent(content) {
|
|
387
376
|
if (typeof content === "string") return content;
|
|
388
377
|
if (!Array.isArray(content)) return "";
|
|
389
378
|
return content.flatMap((part) => {
|
|
@@ -421,12 +410,6 @@ async function listLocalPiSessionPage(value) {
|
|
|
421
410
|
...hasMore ? { nextCursor: sessionCatalogPaging.encodeCursor(offset + page.length) } : {}
|
|
422
411
|
};
|
|
423
412
|
}
|
|
424
|
-
function isoTimestamp(message, entry) {
|
|
425
|
-
const value = parsePiSessionTimestampMs(message.timestamp) ?? parsePiSessionTimestampMs(entry.timestamp);
|
|
426
|
-
if (value === void 0) return;
|
|
427
|
-
const date = new Date(value);
|
|
428
|
-
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
429
|
-
}
|
|
430
413
|
function jsonText(value, maxLength = 2e4) {
|
|
431
414
|
try {
|
|
432
415
|
const text = JSON.stringify(value);
|
|
@@ -458,7 +441,7 @@ function piMessageItems(entry) {
|
|
|
458
441
|
const message = entry.message;
|
|
459
442
|
const role = message.role;
|
|
460
443
|
const id = normalizeBoundedOptionalString(entry.id, 256);
|
|
461
|
-
const timestamp =
|
|
444
|
+
const timestamp = timestampMsToIsoString(parseDateFirstTimestampMs(message.timestamp) ?? parseDateFirstTimestampMs(entry.timestamp));
|
|
462
445
|
const model = normalizeBoundedOptionalString(message.model, 256);
|
|
463
446
|
const provider = normalizeBoundedOptionalString(message.provider, 256);
|
|
464
447
|
const modelRef = provider && model ? `${provider}/${model}` : model;
|
|
@@ -468,7 +451,7 @@ function piMessageItems(entry) {
|
|
|
468
451
|
...modelRef ? { model: modelRef } : {}
|
|
469
452
|
};
|
|
470
453
|
if (role === "user") {
|
|
471
|
-
const text = textFromContent
|
|
454
|
+
const text = textFromContent(message.content);
|
|
472
455
|
return text ? [{
|
|
473
456
|
...common,
|
|
474
457
|
type: "userMessage",
|
|
@@ -477,7 +460,7 @@ function piMessageItems(entry) {
|
|
|
477
460
|
}
|
|
478
461
|
if (role === "toolResult") {
|
|
479
462
|
const toolName = normalizeBoundedOptionalString(message.toolName, 256);
|
|
480
|
-
const text = textFromContent
|
|
463
|
+
const text = textFromContent(message.content);
|
|
481
464
|
return [{
|
|
482
465
|
...common,
|
|
483
466
|
type: "toolResult",
|
|
@@ -502,7 +485,7 @@ function piMessageItems(entry) {
|
|
|
502
485
|
if (role === "custom" || role === "hookMessage") {
|
|
503
486
|
if (message.display !== true) return [];
|
|
504
487
|
const customType = normalizeBoundedOptionalString(message.customType, 256);
|
|
505
|
-
const text = textFromContent
|
|
488
|
+
const text = textFromContent(message.content);
|
|
506
489
|
return text ? [{
|
|
507
490
|
...common,
|
|
508
491
|
type: "other",
|
|
@@ -547,18 +530,13 @@ function piTranscriptItems(entries) {
|
|
|
547
530
|
...id ? { id } : {},
|
|
548
531
|
...timestamp ? { timestamp } : {}
|
|
549
532
|
};
|
|
550
|
-
if (entry.type === "compaction" && typeof entry.summary === "string") return [{
|
|
551
|
-
...common,
|
|
552
|
-
type: "other",
|
|
553
|
-
text: entry.summary
|
|
554
|
-
}];
|
|
555
|
-
if (entry.type === "branch_summary" && typeof entry.summary === "string") return [{
|
|
533
|
+
if ((entry.type === "compaction" || entry.type === "branch_summary") && typeof entry.summary === "string") return [{
|
|
556
534
|
...common,
|
|
557
535
|
type: "other",
|
|
558
536
|
text: entry.summary
|
|
559
537
|
}];
|
|
560
538
|
if (entry.type === "custom_message" && entry.display === true) {
|
|
561
|
-
const text = textFromContent
|
|
539
|
+
const text = textFromContent(entry.content);
|
|
562
540
|
return text ? [{
|
|
563
541
|
...common,
|
|
564
542
|
type: "other",
|
|
@@ -594,12 +572,10 @@ function parseCompletePiRows(tail) {
|
|
|
594
572
|
for (let index = 0; index < tail.length; index += 1) {
|
|
595
573
|
if (tail[index] !== 10) continue;
|
|
596
574
|
const line = tail.subarray(lineStart, index).toString("utf8").trim();
|
|
597
|
-
if (line)
|
|
598
|
-
const value =
|
|
575
|
+
if (line) {
|
|
576
|
+
const value = safeParseJson(line);
|
|
599
577
|
if (!isRecord(value)) break;
|
|
600
578
|
entries.push(value);
|
|
601
|
-
} catch {
|
|
602
|
-
break;
|
|
603
579
|
}
|
|
604
580
|
classifiedBytes = index + 1;
|
|
605
581
|
lineStart = index + 1;
|
|
@@ -609,11 +585,6 @@ function parseCompletePiRows(tail) {
|
|
|
609
585
|
classifiedBytes
|
|
610
586
|
};
|
|
611
587
|
}
|
|
612
|
-
function textFromContent(content) {
|
|
613
|
-
if (typeof content === "string") return content;
|
|
614
|
-
if (!Array.isArray(content)) return;
|
|
615
|
-
return content.flatMap((part) => isRecord(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n") || void 0;
|
|
616
|
-
}
|
|
617
588
|
function readFilePath(probe) {
|
|
618
589
|
return isRecord(probe.upstreamRef) && typeof probe.upstreamRef.filePath === "string" ? probe.upstreamRef.filePath : void 0;
|
|
619
590
|
}
|
|
@@ -663,10 +634,10 @@ async function checkPiSessionUpstreamActivity(probe) {
|
|
|
663
634
|
let occurredAt;
|
|
664
635
|
for (const entry of entries) {
|
|
665
636
|
if (entry.type !== "message" || !isRecord(entry.message) || entry.message.role !== "user") continue;
|
|
666
|
-
const text =
|
|
637
|
+
const text = piMessageText(entry.message.content);
|
|
667
638
|
if (!isExternalUserText(probe, text)) continue;
|
|
668
639
|
humanTurns += 1;
|
|
669
|
-
occurredAt = Math.max(occurredAt ?? 0,
|
|
640
|
+
occurredAt = Math.max(occurredAt ?? 0, parseDateFirstTimestampMs(entry.message.timestamp) ?? parseDateFirstTimestampMs(entry.timestamp) ?? stat.mtimeMs);
|
|
670
641
|
}
|
|
671
642
|
const nextOffset = markerOffset + classifiedBytes;
|
|
672
643
|
return {
|
|
@@ -781,12 +752,6 @@ async function createAdoptedPiSession(params) {
|
|
|
781
752
|
function assertPiLocalAccess(hostId, allowProcessHomeFallback) {
|
|
782
753
|
if (hostId === "gateway" && allowProcessHomeFallback === false && piSessionStore(process.env).usesProcessHomeFallback) throw new Error("local Pi sessions are unavailable in isolated state");
|
|
783
754
|
}
|
|
784
|
-
async function listPiSessions(params) {
|
|
785
|
-
return await listLocalPiSessionPage(params);
|
|
786
|
-
}
|
|
787
|
-
async function readPiSession(params) {
|
|
788
|
-
return await readLocalPiTranscriptPage(params);
|
|
789
|
-
}
|
|
790
755
|
function createPiSessionCatalogRuntime(api) {
|
|
791
756
|
return createSessionCatalogFamily({
|
|
792
757
|
runtime: api.runtime,
|
|
@@ -880,4 +845,4 @@ function createPiSessionCatalogRuntime(api) {
|
|
|
880
845
|
}, isExactPiSessionCursor);
|
|
881
846
|
}
|
|
882
847
|
//#endregion
|
|
883
|
-
export { createPiSessionCatalogRuntime, listPiSessions, readPiSession, requireLocalPiSession };
|
|
848
|
+
export { createPiSessionCatalogRuntime, listLocalPiSessionPage as listPiSessions, readLocalPiTranscriptPage as readPiSession, requireLocalPiSession };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getAcpRuntimeBackend, registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
2
|
-
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
2
|
+
import { createLazyRuntimeMethodBinder, createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
3
3
|
//#region extensions/acpx/src/runtime-proxy.ts
|
|
4
4
|
/** Start an ACP turn through a lazy runtime resolver without awaiting resolution up front. */
|
|
5
5
|
function lazyStartRuntimeTurn(resolveRuntime, input) {
|
|
@@ -23,50 +23,27 @@ function lazyStartRuntimeTurn(resolveRuntime, input) {
|
|
|
23
23
|
}
|
|
24
24
|
/** Create an ACP runtime facade backed by an async runtime resolver. */
|
|
25
25
|
function createLazyAcpRuntimeProxy(resolveRuntime) {
|
|
26
|
+
const bind = createLazyRuntimeMethodBinder(resolveRuntime);
|
|
26
27
|
return {
|
|
27
28
|
ownerAwareSessions: 1,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
async shutdown() {
|
|
32
|
-
await (await resolveRuntime()).shutdown();
|
|
33
|
-
},
|
|
34
|
-
async ensureSession(input) {
|
|
35
|
-
return await (await resolveRuntime()).ensureSession(input);
|
|
36
|
-
},
|
|
29
|
+
findSession: bind((runtime) => runtime.findSession.bind(runtime)),
|
|
30
|
+
shutdown: bind((runtime) => runtime.shutdown.bind(runtime)),
|
|
31
|
+
ensureSession: bind((runtime) => runtime.ensureSession.bind(runtime)),
|
|
37
32
|
startTurn(input) {
|
|
38
33
|
return lazyStartRuntimeTurn(resolveRuntime, input);
|
|
39
34
|
},
|
|
40
35
|
async *runTurn(input) {
|
|
41
36
|
yield* (await resolveRuntime()).runTurn(input);
|
|
42
37
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
async setModel(input) {
|
|
53
|
-
await (await resolveRuntime()).setModel(input);
|
|
54
|
-
},
|
|
55
|
-
async setConfigOption(input) {
|
|
56
|
-
return await (await resolveRuntime()).setConfigOption(input);
|
|
57
|
-
},
|
|
58
|
-
async doctor() {
|
|
59
|
-
return await (await resolveRuntime()).doctor();
|
|
60
|
-
},
|
|
61
|
-
async prepareFreshSession(input) {
|
|
62
|
-
await (await resolveRuntime()).prepareFreshSession(input);
|
|
63
|
-
},
|
|
64
|
-
async cancel(input) {
|
|
65
|
-
await (await resolveRuntime()).cancel(input);
|
|
66
|
-
},
|
|
67
|
-
async close(input) {
|
|
68
|
-
await (await resolveRuntime()).close(input);
|
|
69
|
-
}
|
|
38
|
+
getCapabilities: bind((runtime) => runtime.getCapabilities.bind(runtime)),
|
|
39
|
+
getStatus: bind((runtime) => runtime.getStatus.bind(runtime)),
|
|
40
|
+
setMode: bind((runtime) => runtime.setMode.bind(runtime)),
|
|
41
|
+
setModel: bind((runtime) => runtime.setModel.bind(runtime)),
|
|
42
|
+
setConfigOption: bind((runtime) => runtime.setConfigOption.bind(runtime)),
|
|
43
|
+
doctor: bind((runtime) => runtime.doctor.bind(runtime)),
|
|
44
|
+
prepareFreshSession: bind((runtime) => runtime.prepareFreshSession.bind(runtime)),
|
|
45
|
+
cancel: bind((runtime) => runtime.cancel.bind(runtime)),
|
|
46
|
+
close: bind((runtime) => runtime.close.bind(runtime))
|
|
70
47
|
};
|
|
71
48
|
}
|
|
72
49
|
//#endregion
|
|
@@ -76,7 +53,7 @@ function createLazyAcpRuntimeProxy(resolveRuntime) {
|
|
|
76
53
|
* immediately, then imports the heavier service only when a session needs it.
|
|
77
54
|
*/
|
|
78
55
|
const ACPX_BACKEND_ID = "acpx";
|
|
79
|
-
const loadServiceModule = createLazyRuntimeModule(() => import("./service-
|
|
56
|
+
const loadServiceModule = createLazyRuntimeModule(() => import("./service-BXgGdE56.mjs"));
|
|
80
57
|
function unregisterOwnedRuntime(runtime) {
|
|
81
58
|
if (runtime && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime === runtime) unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
82
59
|
}
|