diff --git a/src/agents/tools/sessions-helpers.ts b/src/agents/tools/sessions-helpers.ts index 64680cc7f66..1b399de5a80 100644 --- a/src/agents/tools/sessions-helpers.ts +++ b/src/agents/tools/sessions-helpers.ts @@ -1,6 +1,10 @@ import type { OpenClawConfig } from "../../config/config.js"; import { callGateway } from "../../gateway/call.js"; -import { isAcpSessionKey, normalizeMainKey } from "../../routing/session-key.js"; +import { + isAcpSessionKey, + isSubagentSessionKey, + normalizeMainKey, +} from "../../routing/session-key.js"; import { sanitizeUserFacingText } from "../pi-embedded-helpers.js"; import { stripDowngradedToolCallText, @@ -69,6 +73,39 @@ export function resolveInternalSessionKey(params: { key: string; alias: string; return params.key; } +export function resolveSandboxSessionToolsVisibility(cfg: OpenClawConfig): "spawned" | "all" { + return cfg.agents?.defaults?.sandbox?.sessionToolsVisibility ?? "spawned"; +} + +export function resolveSandboxedSessionToolContext(params: { + cfg: OpenClawConfig; + agentSessionKey?: string; + sandboxed?: boolean; +}): { + mainKey: string; + alias: string; + visibility: "spawned" | "all"; + requesterInternalKey: string | undefined; + restrictToSpawned: boolean; +} { + const { mainKey, alias } = resolveMainSessionAlias(params.cfg); + const visibility = resolveSandboxSessionToolsVisibility(params.cfg); + const requesterInternalKey = + typeof params.agentSessionKey === "string" && params.agentSessionKey.trim() + ? resolveInternalSessionKey({ + key: params.agentSessionKey, + alias, + mainKey, + }) + : undefined; + const restrictToSpawned = + params.sandboxed === true && + visibility === "spawned" && + !!requesterInternalKey && + !isSubagentSessionKey(requesterInternalKey); + return { mainKey, alias, visibility, requesterInternalKey, restrictToSpawned }; +} + export type AgentToAgentPolicy = { enabled: boolean; matchesAllow: (agentId: string) => boolean; diff --git a/src/agents/tools/sessions-history-tool.ts b/src/agents/tools/sessions-history-tool.ts index 9038e9b902a..a2b9741d639 100644 --- a/src/agents/tools/sessions-history-tool.ts +++ b/src/agents/tools/sessions-history-tool.ts @@ -3,15 +3,14 @@ import type { AnyAgentTool } from "./common.js"; import { loadConfig } from "../../config/config.js"; import { callGateway } from "../../gateway/call.js"; import { capArrayByJsonBytes } from "../../gateway/session-utils.fs.js"; -import { isSubagentSessionKey, resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; +import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import { truncateUtf16Safe } from "../../utils.js"; import { jsonResult, readStringParam } from "./common.js"; import { createAgentToAgentPolicy, resolveSessionReference, - resolveMainSessionAlias, - resolveInternalSessionKey, SessionListRow, + resolveSandboxedSessionToolContext, stripToolMessages, } from "./sessions-helpers.js"; @@ -24,6 +23,8 @@ const SessionsHistoryToolSchema = Type.Object({ const SESSIONS_HISTORY_MAX_BYTES = 80 * 1024; const SESSIONS_HISTORY_TEXT_MAX_CHARS = 4000; +// sandbox policy handling is shared with sessions-list-tool via sessions-helpers.ts + function truncateHistoryText(text: string): { text: string; truncated: boolean } { if (text.length <= SESSIONS_HISTORY_TEXT_MAX_CHARS) { return { text, truncated: false }; @@ -146,10 +147,6 @@ function enforceSessionsHistoryHardCap(params: { return { items: placeholder, bytes: jsonUtf8Bytes(placeholder), hardCapped: true }; } -function resolveSandboxSessionToolsVisibility(cfg: ReturnType) { - return cfg.agents?.defaults?.sandbox?.sessionToolsVisibility ?? "spawned"; -} - async function isSpawnedSessionAllowed(params: { requesterSessionKey: string; targetSessionKey: string; @@ -186,21 +183,12 @@ export function createSessionsHistoryTool(opts?: { required: true, }); const cfg = loadConfig(); - const { mainKey, alias } = resolveMainSessionAlias(cfg); - const visibility = resolveSandboxSessionToolsVisibility(cfg); - const requesterInternalKey = - typeof opts?.agentSessionKey === "string" && opts.agentSessionKey.trim() - ? resolveInternalSessionKey({ - key: opts.agentSessionKey, - alias, - mainKey, - }) - : undefined; - const restrictToSpawned = - opts?.sandboxed === true && - visibility === "spawned" && - !!requesterInternalKey && - !isSubagentSessionKey(requesterInternalKey); + const { mainKey, alias, requesterInternalKey, restrictToSpawned } = + resolveSandboxedSessionToolContext({ + cfg, + agentSessionKey: opts?.agentSessionKey, + sandboxed: opts?.sandboxed, + }); const resolvedSession = await resolveSessionReference({ sessionKey: sessionKeyParam, alias, @@ -215,7 +203,7 @@ export function createSessionsHistoryTool(opts?: { const resolvedKey = resolvedSession.key; const displayKey = resolvedSession.displayKey; const resolvedViaSessionId = resolvedSession.resolvedViaSessionId; - if (restrictToSpawned && !resolvedViaSessionId) { + if (restrictToSpawned && requesterInternalKey && !resolvedViaSessionId) { const ok = await isSpawnedSessionAllowed({ requesterSessionKey: requesterInternalKey, targetSessionKey: resolvedKey, diff --git a/src/agents/tools/sessions-list-tool.ts b/src/agents/tools/sessions-list-tool.ts index 70fc97e0740..abbb6b4958d 100644 --- a/src/agents/tools/sessions-list-tool.ts +++ b/src/agents/tools/sessions-list-tool.ts @@ -4,7 +4,7 @@ import type { AnyAgentTool } from "./common.js"; import { loadConfig } from "../../config/config.js"; import { resolveSessionFilePath } from "../../config/sessions.js"; import { callGateway } from "../../gateway/call.js"; -import { isSubagentSessionKey, resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; +import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import { jsonResult, readStringArrayParam } from "./common.js"; import { createAgentToAgentPolicy, @@ -12,7 +12,7 @@ import { deriveChannel, resolveDisplaySessionKey, resolveInternalSessionKey, - resolveMainSessionAlias, + resolveSandboxedSessionToolContext, type SessionListRow, stripToolMessages, } from "./sessions-helpers.js"; @@ -24,10 +24,6 @@ const SessionsListToolSchema = Type.Object({ messageLimit: Type.Optional(Type.Number({ minimum: 0 })), }); -function resolveSandboxSessionToolsVisibility(cfg: ReturnType) { - return cfg.agents?.defaults?.sandbox?.sessionToolsVisibility ?? "spawned"; -} - export function createSessionsListTool(opts?: { agentSessionKey?: string; sandboxed?: boolean; @@ -40,21 +36,12 @@ export function createSessionsListTool(opts?: { execute: async (_toolCallId, args) => { const params = args as Record; const cfg = loadConfig(); - const { mainKey, alias } = resolveMainSessionAlias(cfg); - const visibility = resolveSandboxSessionToolsVisibility(cfg); - const requesterInternalKey = - typeof opts?.agentSessionKey === "string" && opts.agentSessionKey.trim() - ? resolveInternalSessionKey({ - key: opts.agentSessionKey, - alias, - mainKey, - }) - : undefined; - const restrictToSpawned = - opts?.sandboxed === true && - visibility === "spawned" && - requesterInternalKey && - !isSubagentSessionKey(requesterInternalKey); + const { mainKey, alias, requesterInternalKey, restrictToSpawned } = + resolveSandboxedSessionToolContext({ + cfg, + agentSessionKey: opts?.agentSessionKey, + sandboxed: opts?.sandboxed, + }); const kindsRaw = readStringArrayParam(params, "kinds")?.map((value) => value.trim().toLowerCase(),