fix: restore workspace pass-through dropped by design merge

This commit is contained in:
kumarabhirup 2026-02-21 14:05:37 -08:00
parent 92fadd6700
commit 57b7d8bd0f
No known key found for this signature in database
GPG Key ID: DB7CA2289CAB0167
8 changed files with 40 additions and 4 deletions

View File

@ -94,4 +94,6 @@ export type SkillSnapshot = {
/** Skills with `inject: true` whose full content should be included in the system prompt. */
injectedSkills?: InjectedSkillContent[];
version?: number;
/** Workspace dir this snapshot was built for (used to invalidate on profile switch). */
workspaceDir?: string;
};

View File

@ -265,12 +265,28 @@ export function buildWorkspaceSkillSnapshot(
const remoteNote = opts?.eligibility?.remote?.note?.trim();
const prompt = [remoteNote, formatSkillsForPrompt(resolvedSkills)].filter(Boolean).join("\n");
// Read full content of injected skills, substituting workspace path placeholders
// Read full content of injected skills, substituting workspace path placeholders.
// We replace both the tilde form and the expanded default path to handle
// cases where the replacement target is a profile-specific workspace dir.
//
// Use regex with a negative lookahead so "~/.openclaw/workspace" doesn't
// match inside "~/.openclaw/workspace-<profile>", which would double the
// profile suffix (e.g. workspace-kumareth -> workspace-kumareth-kumareth).
const defaultExpandedWorkspace = resolveUserPath("~/.openclaw/workspace");
const escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const tildePattern = new RegExp(escapeRegex("~/.openclaw/workspace") + "(?![\\w-])", "g");
const injectedSkills: InjectedSkillContent[] = [];
for (const entry of injectedEntries) {
const rawContent = readSkillContent(entry.skill.filePath);
if (rawContent) {
const content = rawContent.replaceAll("~/.openclaw/workspace", workspaceDir);
let content = rawContent.replace(tildePattern, workspaceDir);
if (workspaceDir !== defaultExpandedWorkspace) {
const expandedPattern = new RegExp(
escapeRegex(defaultExpandedWorkspace) + "(?![\\w-])",
"g",
);
content = content.replace(expandedPattern, workspaceDir);
}
injectedSkills.push({ name: entry.skill.name, content });
}
}
@ -284,6 +300,7 @@ export function buildWorkspaceSkillSnapshot(
resolvedSkills,
injectedSkills: injectedSkills.length > 0 ? injectedSkills : undefined,
version: opts?.snapshotVersion,
workspaceDir,
};
}

View File

@ -140,6 +140,8 @@ export async function agentViaGatewayCommand(opts: AgentCliOpts, runtime: Runtim
const channel = normalizeMessageChannel(opts.channel) ?? DEFAULT_CHAT_CHANNEL;
const idempotencyKey = opts.runId?.trim() || randomIdempotencyKey();
const workspaceOverride = process.env.OPENCLAW_WORKSPACE?.trim() || undefined;
const response = await withProgress(
{
label: "Waiting for agent reply…",
@ -165,6 +167,7 @@ export async function agentViaGatewayCommand(opts: AgentCliOpts, runtime: Runtim
lane: opts.lane,
extraSystemPrompt: opts.extraSystemPrompt,
idempotencyKey,
workspace: workspaceOverride,
},
expectFinal: true,
timeoutMs: gatewayTimeoutMs,
@ -236,6 +239,8 @@ async function agentViaGatewayStreamJson(opts: AgentCliOpts, _runtime: RuntimeEn
const channel = normalizeMessageChannel(opts.channel) ?? DEFAULT_CHAT_CHANNEL;
const idempotencyKey = opts.runId?.trim() || randomIdempotencyKey();
const streamWorkspaceOverride = process.env.OPENCLAW_WORKSPACE?.trim() || undefined;
// Capture the runId from early gateway events so we can abort the
// correct run when the process receives SIGTERM/SIGINT.
let capturedRunId: string | undefined;
@ -267,6 +272,7 @@ async function agentViaGatewayStreamJson(opts: AgentCliOpts, _runtime: RuntimeEn
lane: opts.lane,
extraSystemPrompt: opts.extraSystemPrompt,
idempotencyKey,
workspace: streamWorkspaceOverride,
},
expectFinal: true,
timeoutMs: gatewayTimeoutMs,

View File

@ -217,7 +217,7 @@ export async function agentCommand(
}
const agentCfg = cfg.agents?.defaults;
const sessionAgentId = agentIdOverride ?? resolveAgentIdFromSessionKey(opts.sessionKey?.trim());
const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId);
const workspaceDirRaw = opts.workspace?.trim() || resolveAgentWorkspaceDir(cfg, sessionAgentId);
const agentDir = resolveAgentDir(cfg, sessionAgentId);
const workspace = await ensureAgentWorkspace({
dir: workspaceDirRaw,
@ -332,7 +332,11 @@ export async function agentCommand(
});
}
const needsSkillsSnapshot = isNewSession || !sessionEntry?.skillsSnapshot;
const cachedSnapshot = sessionEntry?.skillsSnapshot;
const needsSkillsSnapshot =
isNewSession ||
!cachedSnapshot ||
(cachedSnapshot.workspaceDir && cachedSnapshot.workspaceDir !== workspaceDir);
const skillsSnapshotVersion = getSkillsSnapshotVersion(workspaceDir);
const skillFilter = resolveAgentSkillsFilter(cfg, sessionAgentId);
const skillsSnapshot = needsSkillsSnapshot

View File

@ -76,6 +76,8 @@ export type AgentCommandOpts = {
runId?: string;
extraSystemPrompt?: string;
inputProvenance?: InputProvenance;
/** Workspace directory override (passed via RPC from the web UI for profile switching). */
workspace?: string;
/** Per-call stream param overrides (best-effort). */
streamParams?: AgentStreamParams;
};

View File

@ -146,6 +146,8 @@ export type SessionSkillSnapshot = {
skills: Array<{ name: string; primaryEnv?: string }>;
resolvedSkills?: Skill[];
version?: number;
/** Workspace dir this snapshot was built for (used to invalidate on profile switch). */
workspaceDir?: string;
};
export type SessionSystemPromptReport = {

View File

@ -73,6 +73,7 @@ export const AgentParamsSchema = Type.Object(
timeout: Type.Optional(Type.Integer({ minimum: 0 })),
lane: Type.Optional(Type.String()),
extraSystemPrompt: Type.Optional(Type.String()),
workspace: Type.Optional(Type.String()),
inputProvenance: Type.Optional(
Type.Object(
{

View File

@ -191,6 +191,7 @@ export const agentHandlers: GatewayRequestHandlers = {
groupSpace?: string;
lane?: string;
extraSystemPrompt?: string;
workspace?: string;
idempotencyKey: string;
timeout?: number;
label?: string;
@ -558,6 +559,7 @@ export const agentHandlers: GatewayRequestHandlers = {
runId,
lane: request.lane,
extraSystemPrompt: request.extraSystemPrompt,
workspace: typeof request.workspace === "string" ? request.workspace.trim() : undefined,
inputProvenance,
},
defaultRuntime,