@lobu/worker 3.3.0 → 3.4.0
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/dist/embedded/just-bash-bootstrap.d.ts +16 -1
- package/dist/embedded/just-bash-bootstrap.d.ts.map +1 -1
- package/dist/embedded/just-bash-bootstrap.js +33 -4
- package/dist/embedded/just-bash-bootstrap.js.map +1 -1
- package/dist/embedded/mcp-cli-commands.d.ts +75 -0
- package/dist/embedded/mcp-cli-commands.d.ts.map +1 -0
- package/dist/embedded/mcp-cli-commands.js +336 -0
- package/dist/embedded/mcp-cli-commands.js.map +1 -0
- package/dist/openclaw/sandbox-leak.d.ts +28 -0
- package/dist/openclaw/sandbox-leak.d.ts.map +1 -0
- package/dist/openclaw/sandbox-leak.js +65 -0
- package/dist/openclaw/sandbox-leak.js.map +1 -0
- package/dist/openclaw/session-context.d.ts +13 -10
- package/dist/openclaw/session-context.d.ts.map +1 -1
- package/dist/openclaw/session-context.js +50 -8
- package/dist/openclaw/session-context.js.map +1 -1
- package/dist/openclaw/worker.d.ts +1 -0
- package/dist/openclaw/worker.d.ts.map +1 -1
- package/dist/openclaw/worker.js +143 -42
- package/dist/openclaw/worker.js.map +1 -1
- package/dist/shared/provider-auth-hints.d.ts.map +1 -1
- package/dist/shared/provider-auth-hints.js +9 -5
- package/dist/shared/provider-auth-hints.js.map +1 -1
- package/dist/shared/tool-implementations.d.ts.map +1 -1
- package/dist/shared/tool-implementations.js +3 -1
- package/dist/shared/tool-implementations.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Detects and redacts "sandbox leaks" — cases where the agent presents a
|
|
4
|
+
* local workspace path (or a Claude `sandbox://` URL) as if it were a
|
|
5
|
+
* user-downloadable artifact, without having actually called
|
|
6
|
+
* `UploadUserFile`.
|
|
7
|
+
*
|
|
8
|
+
* Key design decision: we only flag **structural** delivery claims, not
|
|
9
|
+
* free-text mentions. An agent that *describes* workspace paths in a probe
|
|
10
|
+
* or ls-style answer is legitimate; an agent that hands the path to the
|
|
11
|
+
* user as a clickable link or file URL is not. This eliminates the false
|
|
12
|
+
* positives that the previous broad substring check produced.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.checkSandboxLeak = checkSandboxLeak;
|
|
16
|
+
/**
|
|
17
|
+
* Matches Claude's `sandbox://` file-reference scheme. Any occurrence of
|
|
18
|
+
* this is unambiguously a delivery claim — the scheme exists for exactly
|
|
19
|
+
* that purpose.
|
|
20
|
+
*/
|
|
21
|
+
const SANDBOX_URL_RE = /\bsandbox:\/{1,2}[^\s)\]}'"<>]+/gi;
|
|
22
|
+
/**
|
|
23
|
+
* Matches a markdown link target pointing at a local workspace path, e.g.
|
|
24
|
+
* `[report](/app/workspaces/foo/bar.pdf)` or `[x](file:///workspace/y)`.
|
|
25
|
+
* The capture group is the URL/path portion so we can rewrite it.
|
|
26
|
+
*/
|
|
27
|
+
const LOCAL_MD_LINK_RE = /\]\(\s*((?:file:\/\/)?(?:\/app\/workspaces\/|\/workspace\/)[^\s)]+)\s*\)/gi;
|
|
28
|
+
/**
|
|
29
|
+
* Matches HTML `href`/`src` pointing at a local workspace path. Capture
|
|
30
|
+
* group 1 is the attribute name (`href` or `src`) so we can preserve it on
|
|
31
|
+
* redact; capture group 2 is the URL target.
|
|
32
|
+
*/
|
|
33
|
+
const LOCAL_HREF_RE = /\b(href|src)\s*=\s*["']((?:file:\/\/)?(?:\/app\/workspaces\/|\/workspace\/)[^"']+)["']/gi;
|
|
34
|
+
/**
|
|
35
|
+
* Inspect the agent's final user-facing message for unfulfilled file-delivery
|
|
36
|
+
* claims. If `sawUploadedFileEvent` is true (the agent actually called
|
|
37
|
+
* UploadUserFile during this turn), no check is performed — the agent did
|
|
38
|
+
* deliver something, and any remaining path references are assumed
|
|
39
|
+
* descriptive.
|
|
40
|
+
*/
|
|
41
|
+
function checkSandboxLeak(finalText, sawUploadedFileEvent) {
|
|
42
|
+
if (sawUploadedFileEvent || !finalText) {
|
|
43
|
+
return { leaked: false, redactedText: finalText };
|
|
44
|
+
}
|
|
45
|
+
const hasSandboxUrl = SANDBOX_URL_RE.test(finalText);
|
|
46
|
+
const hasMdLink = LOCAL_MD_LINK_RE.test(finalText);
|
|
47
|
+
const hasHref = LOCAL_HREF_RE.test(finalText);
|
|
48
|
+
// Reset lastIndex — `test()` on /g regexes advances state.
|
|
49
|
+
SANDBOX_URL_RE.lastIndex = 0;
|
|
50
|
+
LOCAL_MD_LINK_RE.lastIndex = 0;
|
|
51
|
+
LOCAL_HREF_RE.lastIndex = 0;
|
|
52
|
+
if (!hasSandboxUrl && !hasMdLink && !hasHref) {
|
|
53
|
+
return { leaked: false, redactedText: finalText };
|
|
54
|
+
}
|
|
55
|
+
// Redact: neutralise the link targets so the user doesn't see a broken
|
|
56
|
+
// "clickable" path, but keep the surrounding prose intact.
|
|
57
|
+
let redacted = finalText;
|
|
58
|
+
redacted = redacted.replace(SANDBOX_URL_RE, "[local file, not uploaded]");
|
|
59
|
+
redacted = redacted.replace(LOCAL_MD_LINK_RE, "](about:blank)");
|
|
60
|
+
redacted = redacted.replace(LOCAL_HREF_RE, (_match, attr) => `${attr}="about:blank"`);
|
|
61
|
+
const note = "\n\n_Note: I referenced a local file but did not actually upload it. " +
|
|
62
|
+
"Ask me to retry and I will use `UploadUserFile` to deliver it._";
|
|
63
|
+
return { leaked: true, redactedText: `${redacted}${note}` };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=sandbox-leak.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-leak.js","sourceRoot":"","sources":["../../src/openclaw/sandbox-leak.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAwCH,4CAoCC;AA1ED;;;;GAIG;AACH,MAAM,cAAc,GAAG,mCAAmC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,gBAAgB,GACpB,4EAA4E,CAAC;AAE/E;;;;GAIG;AACH,MAAM,aAAa,GACjB,0FAA0F,CAAC;AAU7F;;;;;;GAMG;AACH,SAAgB,gBAAgB,CAC9B,SAAiB,EACjB,oBAA6B;IAE7B,IAAI,oBAAoB,IAAI,CAAC,SAAS,EAAE,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,aAAa,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAE9C,2DAA2D;IAC3D,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,gBAAgB,CAAC,SAAS,GAAG,CAAC,CAAC;IAC/B,aAAa,CAAC,SAAS,GAAG,CAAC,CAAC;IAE5B,IAAI,CAAC,aAAa,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;QAC7C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;IACpD,CAAC;IAED,uEAAuE;IACvE,2DAA2D;IAC3D,IAAI,QAAQ,GAAG,SAAS,CAAC;IACzB,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,4BAA4B,CAAC,CAAC;IAC1E,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;IAChE,QAAQ,GAAG,QAAQ,CAAC,OAAO,CACzB,aAAa,EACb,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE,CAAC,GAAG,IAAI,gBAAgB,CAClD,CAAC;IAEF,MAAM,IAAI,GACR,uEAAuE;QACvE,iEAAiE,CAAC;IAEpE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE,CAAC;AAC9D,CAAC"}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import { type ConfigProviderMeta, type McpToolDef } from "@lobu/core";
|
|
2
|
-
interface McpStatus {
|
|
3
|
-
id: string;
|
|
4
|
-
name: string;
|
|
5
|
-
requiresAuth: boolean;
|
|
6
|
-
requiresInput: boolean;
|
|
7
|
-
authenticated: boolean;
|
|
8
|
-
configured: boolean;
|
|
9
|
-
}
|
|
1
|
+
import { type ConfigProviderMeta, type McpStatus, type McpToolDef } from "@lobu/core";
|
|
10
2
|
export interface ProviderConfig {
|
|
11
3
|
credentialEnvVarName?: string;
|
|
12
4
|
defaultProvider?: string;
|
|
@@ -41,7 +33,18 @@ export declare function invalidateSessionContextCache(): void;
|
|
|
41
33
|
* Caches the result until invalidated by a config_changed SSE event.
|
|
42
34
|
* Skips MCP server config (OpenClaw doesn't use Claude SDK's MCP format).
|
|
43
35
|
*/
|
|
44
|
-
export declare function getOpenClawSessionContext(
|
|
36
|
+
export declare function getOpenClawSessionContext(opts?: {
|
|
37
|
+
mcpExposure?: "tools" | "cli";
|
|
38
|
+
}): Promise<{
|
|
39
|
+
/**
|
|
40
|
+
* Identity/soul/user instructions for this agent. Returned separately from
|
|
41
|
+
* `gatewayInstructions` so the worker can prepend identity BEFORE the
|
|
42
|
+
* pi-coding-agent base prompt (which would otherwise anchor the model with
|
|
43
|
+
* "You are an expert coding assistant" before the agent's real persona is
|
|
44
|
+
* declared).
|
|
45
|
+
*/
|
|
46
|
+
agentInstructions: string;
|
|
47
|
+
/** Platform / network / skills / MCP setup instructions (no identity). */
|
|
45
48
|
gatewayInstructions: string;
|
|
46
49
|
providerConfig: ProviderConfig;
|
|
47
50
|
skillsConfig: SkillContent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../src/openclaw/session-context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,
|
|
1
|
+
{"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../src/openclaw/session-context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,cAAc;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACrD,wFAAwF;IACxF,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjD;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAwCD;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAGpD;AAqGD;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CAAO,GAC3C,OAAO,CAAC;IACT;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,CAAC,CAkHD"}
|
|
@@ -7,6 +7,7 @@ const url_utils_1 = require("../core/url-utils");
|
|
|
7
7
|
const logger = (0, core_1.createLogger)("openclaw-session-context");
|
|
8
8
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
9
9
|
const DEFAULT_SESSION_CONTEXT = {
|
|
10
|
+
agentInstructions: "",
|
|
10
11
|
gatewayInstructions: "",
|
|
11
12
|
providerConfig: {},
|
|
12
13
|
skillsConfig: [],
|
|
@@ -24,7 +25,7 @@ function invalidateSessionContextCache() {
|
|
|
24
25
|
cachedResult = null;
|
|
25
26
|
logger.info("Session context cache invalidated");
|
|
26
27
|
}
|
|
27
|
-
function buildMcpInstructions(mcpStatus, mcpToolIds) {
|
|
28
|
+
function buildMcpInstructions(mcpStatus, mcpToolIds, mcpExposure = "tools") {
|
|
28
29
|
if (!mcpStatus || mcpStatus.length === 0) {
|
|
29
30
|
return "";
|
|
30
31
|
}
|
|
@@ -38,7 +39,13 @@ function buildMcpInstructions(mcpStatus, mcpToolIds) {
|
|
|
38
39
|
}
|
|
39
40
|
const lines = ["## MCP Tools Requiring Setup"];
|
|
40
41
|
for (const mcp of needsAuthentication) {
|
|
41
|
-
|
|
42
|
+
const loginCmd = mcpExposure === "cli"
|
|
43
|
+
? `run \`${mcp.id} auth login\` in Bash`
|
|
44
|
+
: `call \`${mcp.id}_login\``;
|
|
45
|
+
const checkCmd = mcpExposure === "cli"
|
|
46
|
+
? `run \`${mcp.id} auth check\``
|
|
47
|
+
: `call \`${mcp.id}_login_check\``;
|
|
48
|
+
lines.push(`- ⚠️ **${mcp.name}** (id: ${mcp.id}): Authentication is required. To start login, ${loginCmd}. After the user completes login, ${checkCmd}. Newly available MCP tools will refresh on the next message.`);
|
|
42
49
|
}
|
|
43
50
|
for (const mcp of needsConfiguration) {
|
|
44
51
|
lines.push(`- ⚠️ **${mcp.name}** (id: ${mcp.id}): Additional MCP input is required before this server can be used. Tell the user an admin must configure the MCP inputs in settings.`);
|
|
@@ -51,6 +58,33 @@ function buildMcpInstructions(mcpStatus, mcpToolIds) {
|
|
|
51
58
|
}
|
|
52
59
|
return lines.join("\n");
|
|
53
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* CLI-mode header introducing the `<server> <tool>` idiom. Appended to gateway
|
|
63
|
+
* instructions when `mcpExposure === "cli"` so the model understands how to
|
|
64
|
+
* invoke MCP tools through bash instead of as first-class function calls.
|
|
65
|
+
*/
|
|
66
|
+
function buildMcpCliInstructions(mcpStatus) {
|
|
67
|
+
if (!mcpStatus || mcpStatus.length === 0)
|
|
68
|
+
return "";
|
|
69
|
+
const servers = mcpStatus.map((m) => `- \`${m.id}\` — ${m.name}`).join("\n");
|
|
70
|
+
return `## Available MCP CLIs
|
|
71
|
+
|
|
72
|
+
MCP servers are exposed as Bash commands. One command per server. Invoke tools by piping JSON on stdin:
|
|
73
|
+
|
|
74
|
+
\`\`\`bash
|
|
75
|
+
<server> <tool> <<'EOF'
|
|
76
|
+
{ ...json args... }
|
|
77
|
+
EOF
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
Discovery:
|
|
81
|
+
- \`<server> --help\` — list a server's tools
|
|
82
|
+
- \`<server> <tool> --schema\` — print the JSON Schema for a tool
|
|
83
|
+
- \`<server> auth login|check|logout\` — manage OAuth where required
|
|
84
|
+
|
|
85
|
+
Servers:
|
|
86
|
+
${servers}`;
|
|
87
|
+
}
|
|
54
88
|
function buildMcpServerInstructions(mcpInstructions) {
|
|
55
89
|
const entries = Object.entries(mcpInstructions).filter(([, v]) => v);
|
|
56
90
|
if (entries.length === 0)
|
|
@@ -67,8 +101,11 @@ function buildMcpServerInstructions(mcpInstructions) {
|
|
|
67
101
|
* Caches the result until invalidated by a config_changed SSE event.
|
|
68
102
|
* Skips MCP server config (OpenClaw doesn't use Claude SDK's MCP format).
|
|
69
103
|
*/
|
|
70
|
-
async function getOpenClawSessionContext() {
|
|
71
|
-
|
|
104
|
+
async function getOpenClawSessionContext(opts = {}) {
|
|
105
|
+
const mcpExposure = opts.mcpExposure ?? "tools";
|
|
106
|
+
if (cachedResult &&
|
|
107
|
+
cachedResult.mcpExposure === mcpExposure &&
|
|
108
|
+
Date.now() - cachedResult.cachedAt < CACHE_TTL_MS) {
|
|
72
109
|
logger.debug("Returning cached session context");
|
|
73
110
|
return cachedResult;
|
|
74
111
|
}
|
|
@@ -94,25 +131,30 @@ async function getOpenClawSessionContext() {
|
|
|
94
131
|
const data = (await response.json());
|
|
95
132
|
logger.info(`Received session context: ${data.platformInstructions.length} chars platform instructions, ${data.mcpStatus.length} MCP status entries, provider: ${data.providerConfig?.defaultProvider || "none"}, cliBackends: ${data.providerConfig?.cliBackends?.map((b) => b.name).join(", ") || "none"}`);
|
|
96
133
|
const toolMcpIds = new Set(Object.keys(data.mcpTools || {}));
|
|
97
|
-
const mcpSetupInstructions = buildMcpInstructions(data.mcpStatus, toolMcpIds);
|
|
134
|
+
const mcpSetupInstructions = buildMcpInstructions(data.mcpStatus, toolMcpIds, mcpExposure);
|
|
98
135
|
// Include MCP server instructions for all servers (with or without tools).
|
|
99
136
|
// These provide workspace context (available connectors, entity schemas, etc.)
|
|
100
137
|
// that helps the agent use the tools effectively.
|
|
101
138
|
const mcpServerInstructions = buildMcpServerInstructions(data.mcpInstructions || {});
|
|
139
|
+
const mcpCliInstructions = mcpExposure === "cli" ? buildMcpCliInstructions(data.mcpStatus) : "";
|
|
140
|
+
// Identity/soul/user instructions are returned separately so the worker
|
|
141
|
+
// can prepend them BEFORE the pi-coding-agent base prompt.
|
|
142
|
+
const agentInstructions = data.agentInstructions || "";
|
|
102
143
|
const gatewayInstructions = [
|
|
103
|
-
data.agentInstructions,
|
|
104
144
|
data.platformInstructions,
|
|
105
145
|
data.networkInstructions,
|
|
106
146
|
data.skillsInstructions,
|
|
147
|
+
mcpCliInstructions,
|
|
107
148
|
mcpSetupInstructions,
|
|
108
149
|
mcpServerInstructions,
|
|
109
150
|
]
|
|
110
151
|
.filter(Boolean)
|
|
111
152
|
.join("\n\n");
|
|
112
153
|
const mcpTools = data.mcpTools || {};
|
|
113
|
-
logger.info(`Built gateway instructions: agent (${
|
|
154
|
+
logger.info(`Built gateway instructions: agent (${agentInstructions.length} chars, prepended) + platform (${data.platformInstructions.length} chars) + network (${data.networkInstructions.length} chars) + skills (${(data.skillsInstructions || "").length} chars) + MCP setup (${mcpSetupInstructions.length} chars) + MCP server instructions (${mcpServerInstructions.length} chars), mcpTools: ${Object.keys(mcpTools).length} servers`);
|
|
114
155
|
const mcpContext = data.mcpContext || {};
|
|
115
156
|
const result = {
|
|
157
|
+
agentInstructions,
|
|
116
158
|
gatewayInstructions,
|
|
117
159
|
providerConfig: data.providerConfig || {},
|
|
118
160
|
skillsConfig: data.skillsConfig || [],
|
|
@@ -124,7 +166,7 @@ async function getOpenClawSessionContext() {
|
|
|
124
166
|
// transient upstream failure that should be retried on the next message.
|
|
125
167
|
const hasEmptyAuthenticatedMcp = data.mcpStatus.some((mcp) => mcp.authenticated && !toolMcpIds.has(mcp.id));
|
|
126
168
|
if (!hasEmptyAuthenticatedMcp) {
|
|
127
|
-
cachedResult = { ...result, cachedAt: Date.now() };
|
|
169
|
+
cachedResult = { ...result, mcpExposure, cachedAt: Date.now() };
|
|
128
170
|
}
|
|
129
171
|
else {
|
|
130
172
|
logger.warn("Skipping session context cache — authenticated MCP(s) returned no tools", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-context.js","sourceRoot":"","sources":["../../src/openclaw/session-context.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"session-context.js","sourceRoot":"","sources":["../../src/openclaw/session-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ interface ResolvedMemoryFlushConfig {
|
|
|
7
7
|
systemPrompt: string;
|
|
8
8
|
prompt: string;
|
|
9
9
|
}
|
|
10
|
+
export declare function replaceBasePromptIdentity(basePrompt: string, identity: string): string;
|
|
10
11
|
export declare function estimatePromptTokenCost(promptText: string, imageCount: number): number;
|
|
11
12
|
export declare function resolveMemoryFlushConfig(rawOptions: Record<string, unknown>): ResolvedMemoryFlushConfig;
|
|
12
13
|
export declare class OpenClawWorker implements WorkerExecutor {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/openclaw/worker.ts"],"names":[],"mappings":";AAMA,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAWpB,OAAO,KAAK,EAGV,YAAY,EACZ,cAAc,EACf,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/openclaw/worker.ts"],"names":[],"mappings":";AAMA,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAWpB,OAAO,KAAK,EAGV,YAAY,EACZ,cAAc,EACf,MAAM,eAAe,CAAC;AAmDvB,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAiCD,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CAOR;AAsHD,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM,CAIR;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,yBAAyB,CA2B3B;AAED,qBAAa,cAAe,YAAW,cAAc;IACnD,OAAO,CAAC,gBAAgB,CAAmB;IACpC,eAAe,EAAE,eAAe,CAAC;IACxC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,iBAAiB,CAA4B;gBAEzC,MAAM,EAAE,YAAY;IAmChC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmRxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,kBAAkB,IAAI,eAAe,GAAG,IAAI;IAI5C,OAAO,CAAC,mBAAmB;YAIb,gCAAgC;YAqFhC,YAAY;YAq9BZ,kBAAkB;YAwBlB,kBAAkB;IAmEhC,OAAO,KAAK,aAAa,GAOxB;IAED,OAAO,CAAC,MAAM,CAAC,OAAO;IAItB,OAAO,CAAC,qBAAqB;IAiE7B,2EAA2E;IAC3E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAoB;YAE7C,oBAAoB;YAgDpB,yBAAyB;YAkBzB,oCAAoC;CAiCnD"}
|
package/dist/openclaw/worker.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.OpenClawWorker = void 0;
|
|
38
|
+
exports.replaceBasePromptIdentity = replaceBasePromptIdentity;
|
|
38
39
|
exports.estimatePromptTokenCost = estimatePromptTokenCost;
|
|
39
40
|
exports.resolveMemoryFlushConfig = resolveMemoryFlushConfig;
|
|
40
41
|
const fs = __importStar(require("node:fs/promises"));
|
|
@@ -57,6 +58,7 @@ const tool_implementations_1 = require("../shared/tool-implementations");
|
|
|
57
58
|
const custom_tools_1 = require("./custom-tools");
|
|
58
59
|
const instructions_1 = require("./instructions");
|
|
59
60
|
const model_resolver_1 = require("./model-resolver");
|
|
61
|
+
const sandbox_leak_1 = require("./sandbox-leak");
|
|
60
62
|
const plugin_loader_1 = require("./plugin-loader");
|
|
61
63
|
const processor_1 = require("./processor");
|
|
62
64
|
const session_context_1 = require("./session-context");
|
|
@@ -71,6 +73,29 @@ const DEFAULT_MEMORY_FLUSH_CONFIG = {
|
|
|
71
73
|
systemPrompt: "Session nearing compaction. Store durable memories now.",
|
|
72
74
|
prompt: "Write any lasting notes to memory using available memory tools. Reply with NO_REPLY if nothing to store.",
|
|
73
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* Pi-coding-agent's buildSystemPrompt() (in `@mariozechner/pi-coding-agent`)
|
|
78
|
+
* always opens the system prompt with this exact sentence. Lobu agents can
|
|
79
|
+
* override their identity via IDENTITY.md, but unless we strip out this
|
|
80
|
+
* opener the model sees two competing role declarations and tends to favour
|
|
81
|
+
* "expert coding assistant" because it appears first.
|
|
82
|
+
*
|
|
83
|
+
* This helper substitutes the opener with the agent's identity and keeps the
|
|
84
|
+
* rest of the base prompt (tools list, guidelines, docs paths, cwd) intact.
|
|
85
|
+
*
|
|
86
|
+
* If the upstream package ever changes the opener wording, this becomes a
|
|
87
|
+
* no-op and `replaced === original`. In that case we fall back to prepending
|
|
88
|
+
* the identity with a small framing note so identity still wins ordering.
|
|
89
|
+
*/
|
|
90
|
+
const PI_CODING_AGENT_OPENER_RE = /^You are an expert coding assistant operating inside pi, a coding agent harness\.[^\n]*/;
|
|
91
|
+
function replaceBasePromptIdentity(basePrompt, identity) {
|
|
92
|
+
if (PI_CODING_AGENT_OPENER_RE.test(basePrompt)) {
|
|
93
|
+
return basePrompt.replace(PI_CODING_AGENT_OPENER_RE, identity);
|
|
94
|
+
}
|
|
95
|
+
// Upstream wording drifted — prepend identity with a framing note rather
|
|
96
|
+
// than silently letting the upstream opener win.
|
|
97
|
+
return `${identity}\n\nThe section below describes the runtime tooling available to you. It does not change your role.\n\n${basePrompt}`;
|
|
98
|
+
}
|
|
74
99
|
function isLikelyImageGenerationRequest(prompt) {
|
|
75
100
|
const lower = prompt.toLowerCase();
|
|
76
101
|
const explicitToolInstruction = lower.includes("generateimage tool") || lower.includes("use generateimage");
|
|
@@ -364,22 +389,20 @@ class OpenClawWorker {
|
|
|
364
389
|
const audioPermissionHint = hintGatewayUrl && hintWorkerToken
|
|
365
390
|
? await this.maybeBuildAudioPermissionHintMessage(outputSnapshot, hintGatewayUrl, hintWorkerToken)
|
|
366
391
|
: null;
|
|
367
|
-
const leakedSandboxLink = !sawUploadedFileEvent &&
|
|
368
|
-
/(sandbox:\/|\/app\/workspaces\/|\/workspace\/)/i.test(outputSnapshot);
|
|
369
|
-
const fileDeliveryFailureMessage = leakedSandboxLink
|
|
370
|
-
? "I created a local file, but I did not actually upload it for you. Local sandbox/workspace links are not user-accessible, so this file-delivery attempt failed. Please ask me to try again and I must use UploadUserFile."
|
|
371
|
-
: null;
|
|
372
392
|
const finalResult = this.progressProcessor.getFinalResult();
|
|
373
|
-
if (
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
393
|
+
if (finalResult) {
|
|
394
|
+
const leakCheck = (0, sandbox_leak_1.checkSandboxLeak)(finalResult.text, sawUploadedFileEvent);
|
|
395
|
+
if (leakCheck.leaked) {
|
|
396
|
+
logger.warn("Detected unfulfilled file-delivery claim in final message; redacting link targets");
|
|
397
|
+
}
|
|
378
398
|
const finalText = audioPermissionHint
|
|
379
|
-
? `${
|
|
380
|
-
:
|
|
399
|
+
? `${leakCheck.redactedText}\n\n${audioPermissionHint}`
|
|
400
|
+
: leakCheck.redactedText;
|
|
381
401
|
logger.info(`📤 Sending final result (${finalText.length} chars) with deduplication flag`);
|
|
382
|
-
|
|
402
|
+
// When a leak was redacted, the already-streamed content contains the
|
|
403
|
+
// pre-redaction URLs — a delta-append would leave them on the client.
|
|
404
|
+
// Force a full replacement so the client discards the leaky prefix.
|
|
405
|
+
await this.workerTransport.sendStreamDelta(finalText, leakCheck.leaked, finalResult.isFinal);
|
|
383
406
|
}
|
|
384
407
|
else if (audioPermissionHint) {
|
|
385
408
|
logger.info("📤 Sending audio permission settings hint to user");
|
|
@@ -489,9 +512,23 @@ class OpenClawWorker {
|
|
|
489
512
|
const verboseLogging = rawOptions.verboseLogging === true;
|
|
490
513
|
const memoryFlushConfig = resolveMemoryFlushConfig(rawOptions);
|
|
491
514
|
this.progressProcessor.setVerboseLogging(verboseLogging);
|
|
515
|
+
// Resolve how MCP tools should be exposed to the agent. In embedded mode,
|
|
516
|
+
// operators can swap the many first-class MCP tools for a small set of
|
|
517
|
+
// per-server just-bash CLIs (keeps the tool list lean).
|
|
518
|
+
const configuredMcpExposure = rawOptions.toolsConfig?.mcpExposure;
|
|
519
|
+
const envMcpExposure = process.env.LOBU_MCP_EXPOSURE;
|
|
520
|
+
const requestedMcpExposure = configuredMcpExposure === "cli" || envMcpExposure === "cli"
|
|
521
|
+
? "cli"
|
|
522
|
+
: "tools";
|
|
523
|
+
const isEmbedded = process.env.DEPLOYMENT_MODE === "embedded";
|
|
524
|
+
if (requestedMcpExposure === "cli" && !isEmbedded) {
|
|
525
|
+
logger.warn("mcpExposure='cli' requested but DEPLOYMENT_MODE is not 'embedded' — falling back to 'tools'.");
|
|
526
|
+
}
|
|
527
|
+
const mcpExposure = requestedMcpExposure === "cli" && isEmbedded ? "cli" : "tools";
|
|
492
528
|
// Fetch session context BEFORE model resolution so AGENT_DEFAULT_PROVIDER
|
|
493
|
-
// is available when resolveModelRef() needs a fallback provider.
|
|
494
|
-
|
|
529
|
+
// is available when resolveModelRef() needs a fallback provider. Pass
|
|
530
|
+
// `mcpExposure` so MCP setup instructions use the right call syntax.
|
|
531
|
+
const context = await (0, session_context_1.getOpenClawSessionContext)({ mcpExposure });
|
|
495
532
|
// Sync enabled skills to workspace filesystem so the agent can `cat` them.
|
|
496
533
|
// Remove stale skill directories to avoid serving removed/disabled skills.
|
|
497
534
|
const skillsWorkspaceDir = this.getWorkingDirectory();
|
|
@@ -662,10 +699,49 @@ class OpenClawWorker {
|
|
|
662
699
|
allowedTools: rawOptions.allowedTools,
|
|
663
700
|
disallowedTools: rawOptions.disallowedTools,
|
|
664
701
|
});
|
|
702
|
+
// Build a mutable snapshot of MCP runtime state. The embedded CLI handlers
|
|
703
|
+
// read through `mcpRuntimeRef.current` so that `auth check` / `logout` can
|
|
704
|
+
// swap in refreshed tools/state without rebuilding Bash. `refresh()` re-
|
|
705
|
+
// fetches session context — `checkMcpLogin`/`logoutMcp` already invalidate
|
|
706
|
+
// the gateway cache, so the next fetch reaches the gateway.
|
|
707
|
+
const mcpRuntimeRef = {
|
|
708
|
+
current: {
|
|
709
|
+
mcpTools: context.mcpTools,
|
|
710
|
+
mcpStatus: context.mcpStatus,
|
|
711
|
+
mcpContext: context.mcpContext,
|
|
712
|
+
},
|
|
713
|
+
...(mcpExposure === "cli" && {
|
|
714
|
+
refresh: async () => {
|
|
715
|
+
try {
|
|
716
|
+
const fresh = await (0, session_context_1.getOpenClawSessionContext)({ mcpExposure });
|
|
717
|
+
return {
|
|
718
|
+
mcpTools: fresh.mcpTools,
|
|
719
|
+
mcpStatus: fresh.mcpStatus,
|
|
720
|
+
mcpContext: fresh.mcpContext,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
logger.warn(`Failed to refresh MCP session context after auth: ${err instanceof Error ? err.message : String(err)}`);
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
}),
|
|
729
|
+
};
|
|
730
|
+
const gwParams = {
|
|
731
|
+
gatewayUrl: process.env.DISPATCHER_URL ?? "",
|
|
732
|
+
workerToken: process.env.WORKER_TOKEN ?? "",
|
|
733
|
+
channelId: this.config.channelId,
|
|
734
|
+
conversationId: this.config.conversationId,
|
|
735
|
+
platform: this.config.platform,
|
|
736
|
+
};
|
|
665
737
|
let embeddedBashOps;
|
|
666
|
-
if (
|
|
738
|
+
if (isEmbedded) {
|
|
667
739
|
const { createEmbeddedBashOps } = await Promise.resolve().then(() => __importStar(require("../embedded/just-bash-bootstrap")));
|
|
668
|
-
embeddedBashOps = await createEmbeddedBashOps(
|
|
740
|
+
embeddedBashOps = await createEmbeddedBashOps({
|
|
741
|
+
mcpRuntimeRef,
|
|
742
|
+
gw: gwParams,
|
|
743
|
+
mcpExposure,
|
|
744
|
+
});
|
|
669
745
|
}
|
|
670
746
|
let tools = (0, tools_1.createOpenClawTools)(workspaceDir, {
|
|
671
747
|
bashOperations: embeddedBashOps,
|
|
@@ -702,7 +778,9 @@ class OpenClawWorker {
|
|
|
702
778
|
logger.info(`Set runtime API key for ${provider}`);
|
|
703
779
|
}
|
|
704
780
|
else {
|
|
705
|
-
|
|
781
|
+
// Look up the env var by the canonical gateway slug (e.g. "z-ai" → Z_AI_API_KEY),
|
|
782
|
+
// not the model-registry alias (e.g. "zai" → ZAI_API_KEY which nobody sets).
|
|
783
|
+
const fallbackEnvVar = (0, provider_auth_hints_1.getApiKeyEnvVarForProvider)(rawProvider);
|
|
706
784
|
const fallbackValue = credentialStore.get(fallbackEnvVar) || process.env[fallbackEnvVar];
|
|
707
785
|
if (fallbackValue) {
|
|
708
786
|
authStorage.setRuntimeApiKey(provider, fallbackValue);
|
|
@@ -758,13 +836,6 @@ Replace "YOUR_PROMPT_HERE" with the user's request. These agents can read/write
|
|
|
758
836
|
|
|
759
837
|
You have access to GetChannelHistory to view previous messages in this thread.
|
|
760
838
|
Use it when the user references past discussions or you need context.`);
|
|
761
|
-
const gwParams = {
|
|
762
|
-
gatewayUrl,
|
|
763
|
-
workerToken,
|
|
764
|
-
channelId: this.config.channelId,
|
|
765
|
-
conversationId: this.config.conversationId,
|
|
766
|
-
platform: this.config.platform,
|
|
767
|
-
};
|
|
768
839
|
const customTools = (0, custom_tools_1.createOpenClawCustomTools)({
|
|
769
840
|
...gwParams,
|
|
770
841
|
onCustomEvent: async (name, data) => {
|
|
@@ -775,11 +846,19 @@ Use it when the user references past discussions or you need context.`);
|
|
|
775
846
|
});
|
|
776
847
|
},
|
|
777
848
|
});
|
|
778
|
-
// Register MCP tools
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
849
|
+
// Register first-class MCP tools + auth tools. Skipped entirely in CLI
|
|
850
|
+
// mode — MCP tools are instead reachable via the per-server just-bash CLI
|
|
851
|
+
// wired in above, and `<server> auth login|check|logout` supersedes the
|
|
852
|
+
// `<id>_login` / `<id>_login_check` / `<id>_logout` trio.
|
|
853
|
+
if (mcpExposure === "cli") {
|
|
854
|
+
logger.info("mcpExposure='cli' — skipping first-class MCP tool registration (tools reachable via <server> <tool> in Bash).");
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
const mcpToolDefs = (0, custom_tools_1.createMcpToolDefinitions)(context.mcpTools, gwParams, context.mcpContext);
|
|
858
|
+
if (mcpToolDefs.length > 0) {
|
|
859
|
+
customTools.push(...mcpToolDefs);
|
|
860
|
+
logger.info(`Registered ${mcpToolDefs.length} MCP tool(s): ${mcpToolDefs.map((t) => t.name).join(", ")}`);
|
|
861
|
+
}
|
|
783
862
|
}
|
|
784
863
|
// Load OpenClaw plugins
|
|
785
864
|
const pluginsConfig = rawOptions.pluginsConfig;
|
|
@@ -789,10 +868,12 @@ Use it when the user references past discussions or you need context.`);
|
|
|
789
868
|
customTools.push(...pluginTools);
|
|
790
869
|
logger.info(`Loaded ${pluginTools.length} tool(s) from ${loadedPlugins.length} plugin(s)`);
|
|
791
870
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
871
|
+
if (mcpExposure !== "cli") {
|
|
872
|
+
const authToolDefs = (0, custom_tools_1.createMcpAuthToolDefinitions)(context.mcpStatus, gwParams, new Set(customTools.map((tool) => tool.name)));
|
|
873
|
+
if (authToolDefs.length > 0) {
|
|
874
|
+
customTools.push(...authToolDefs);
|
|
875
|
+
logger.info(`Registered ${authToolDefs.length} MCP auth tool(s): ${authToolDefs.map((t) => t.name).join(", ")}`);
|
|
876
|
+
}
|
|
796
877
|
}
|
|
797
878
|
// Apply plugin provider registrations to ModelRegistry
|
|
798
879
|
const modelRegistry = new pi_coding_agent_1.ModelRegistry(authStorage);
|
|
@@ -874,8 +955,25 @@ Use it when the user references past discussions or you need context.`);
|
|
|
874
955
|
modelRegistry,
|
|
875
956
|
});
|
|
876
957
|
session = createdSession.session;
|
|
958
|
+
// Pi-coding-agent's base prompt opens with "You are an expert coding
|
|
959
|
+
// assistant operating inside pi, a coding agent harness…" — that anchor
|
|
960
|
+
// overrides any IDENTITY.md the agent ships with. Replace just that
|
|
961
|
+
// opener with the agent's real identity (or the lobu default) so the
|
|
962
|
+
// tools/guidelines/cwd footer below it still applies, but the role on
|
|
963
|
+
// top is the one we actually want.
|
|
877
964
|
const basePrompt = session.systemPrompt;
|
|
878
|
-
|
|
965
|
+
const identity = context.agentInstructions?.trim();
|
|
966
|
+
const finalSystemPrompt = identity
|
|
967
|
+
? [
|
|
968
|
+
replaceBasePromptIdentity(basePrompt, identity),
|
|
969
|
+
finalInstructionsUpdated,
|
|
970
|
+
]
|
|
971
|
+
.filter(Boolean)
|
|
972
|
+
.join("\n\n---\n\n")
|
|
973
|
+
: [basePrompt, finalInstructionsUpdated]
|
|
974
|
+
.filter(Boolean)
|
|
975
|
+
.join("\n\n---\n\n");
|
|
976
|
+
session.agent.setSystemPrompt(finalSystemPrompt);
|
|
879
977
|
let resolveTurnDone = null;
|
|
880
978
|
let turnNonce = 0;
|
|
881
979
|
let suppressProgressOutput = false;
|
|
@@ -1102,7 +1200,7 @@ Use it when the user references past discussions or you need context.`);
|
|
|
1102
1200
|
},
|
|
1103
1201
|
ctx: pluginHookContext,
|
|
1104
1202
|
});
|
|
1105
|
-
const errorWithHint = await this.maybeBuildAuthHintMessage(sessionError,
|
|
1203
|
+
const errorWithHint = await this.maybeBuildAuthHintMessage(sessionError, rawProvider, modelId, gatewayUrl, workerToken);
|
|
1106
1204
|
return {
|
|
1107
1205
|
success: false,
|
|
1108
1206
|
exitCode: 1,
|
|
@@ -1200,15 +1298,18 @@ Use it when the user references past discussions or you need context.`);
|
|
|
1200
1298
|
logger.info(`Downloading ${files.length} input files...`);
|
|
1201
1299
|
const workspaceDir = this.workspaceManager.getCurrentWorkingDirectory();
|
|
1202
1300
|
const inputDir = path.join(workspaceDir, "input");
|
|
1203
|
-
const dispatcherUrl = process.env.DISPATCHER_URL;
|
|
1204
|
-
const workerToken = process.env.WORKER_TOKEN;
|
|
1205
1301
|
for (const file of files) {
|
|
1206
1302
|
try {
|
|
1303
|
+
if (!file.downloadUrl) {
|
|
1304
|
+
logger.warn({ fileName: file.name, fileId: file.id }, "Inbound file has no downloadUrl; gateway must publish it as an artifact before forwarding");
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1207
1307
|
logger.info(`Downloading file: ${file.name} (${file.id})`);
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1308
|
+
// The gateway pre-publishes every inbound attachment as a signed,
|
|
1309
|
+
// time-limited artifact and embeds the URL in `downloadUrl`. We
|
|
1310
|
+
// fetch through the worker's egress proxy — no platform tokens or
|
|
1311
|
+
// worker JWT cross this boundary anymore.
|
|
1312
|
+
const response = await fetch(file.downloadUrl, {
|
|
1212
1313
|
signal: AbortSignal.timeout(60_000),
|
|
1213
1314
|
});
|
|
1214
1315
|
if (!response.ok) {
|