Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
256
extensions/copilot/src/workspace-bootstrap.ts
Normal file
256
extensions/copilot/src/workspace-bootstrap.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copilot plugin module implements workspace bootstrap behavior.
|
||||
import path from "node:path";
|
||||
import type {
|
||||
AgentHarnessAttemptParams,
|
||||
EmbeddedContextFile,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import {
|
||||
resolveBootstrapContextForRun,
|
||||
resolveUserPath,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
|
||||
// Filenames the Copilot SDK already loads natively from the working
|
||||
// directory / instructionDirectories (per
|
||||
// `@github/copilot-sdk/dist/types.d.ts:1036,1155` —
|
||||
// "custom instruction files (.github/copilot-instructions.md,
|
||||
// AGENTS.md, etc.) are always loaded from the working directory").
|
||||
// Filtering them out of the OpenClaw bootstrap injection avoids
|
||||
// duplicating their content into `SessionConfig.systemMessage`, which
|
||||
// would otherwise inflate every prompt with the same text the SDK
|
||||
// already includes. Mirrors codex's CODEX_NATIVE_PROJECT_DOC_BASENAMES
|
||||
// (extensions/codex/src/app-server/run-attempt.ts:160).
|
||||
const COPILOT_NATIVE_PROJECT_DOC_BASENAMES = new Set(["agents.md"]);
|
||||
|
||||
// Persona/identity files get sorted to the top of the rendered block
|
||||
// so they precede the freer-form context like USER.md / MEMORY.md.
|
||||
// Mirrors codex's CODEX_BOOTSTRAP_CONTEXT_ORDER ordering (same files).
|
||||
const COPILOT_BOOTSTRAP_CONTEXT_ORDER = new Map<string, number>([
|
||||
["soul.md", 10],
|
||||
["identity.md", 20],
|
||||
["heartbeat.md", 30],
|
||||
["bootstrap.md", 40],
|
||||
["tools.md", 50],
|
||||
["user.md", 60],
|
||||
["memory.md", 70],
|
||||
]);
|
||||
|
||||
export type CopilotWorkspaceBootstrapResult = {
|
||||
bootstrapFiles: Awaited<ReturnType<typeof resolveBootstrapContextForRun>>["bootstrapFiles"];
|
||||
contextFiles: EmbeddedContextFile[];
|
||||
instructions?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads OpenClaw workspace bootstrap files (IDENTITY.md, SOUL.md,
|
||||
* HEARTBEAT.md, USER.md, TOOLS.md, BOOTSTRAP.md, MEMORY.md, ...) using
|
||||
* the shared core helper PI and codex both use, then renders them as a
|
||||
* single string suitable for `SessionConfig.systemMessage.content` on
|
||||
* the Copilot SDK.
|
||||
*
|
||||
* Returns `instructions: undefined` when there are no relevant files
|
||||
* (after filtering out SDK-native docs) so the caller can omit the
|
||||
* `systemMessage` field entirely rather than passing an empty string.
|
||||
*
|
||||
* Mirrors codex's `buildCodexWorkspaceBootstrapContext` /
|
||||
* `renderCodexWorkspaceBootstrapInstructions` pair
|
||||
* (`extensions/codex/src/app-server/run-attempt.ts:2877,3047`). The
|
||||
* shape divergence — codex returns instructions inside the same object
|
||||
* as bootstrapFiles+contextFiles for its developerInstructions field;
|
||||
* copilot exposes the rendered string for SDK `systemMessage` — is the
|
||||
* intended difference between the two runtimes' system-prompt
|
||||
* surfaces.
|
||||
*/
|
||||
export async function resolveCopilotWorkspaceBootstrapContext(params: {
|
||||
attempt: AgentHarnessAttemptParams;
|
||||
/**
|
||||
* Sandbox-aware working directory the SDK session will run in.
|
||||
* When this differs from the canonical `attempt.workspaceDir`
|
||||
* (sandbox `ro` / `none` runs that redirect to a copy), bootstrap
|
||||
* context file paths are remapped so the rendered `systemMessage`
|
||||
* shows the model the same workspace the SDK's native loader and
|
||||
* bridged tools operate on. Pass `undefined` only when no sandbox
|
||||
* resolution has happened (e.g. tests not exercising sandbox
|
||||
* redirection). Required so future callers cannot silently miss
|
||||
* the remap. Mirrors PI's
|
||||
* `remapInjectedContextFilesToWorkspace` call in
|
||||
* `src/agents/pi-embedded-runner/run/attempt.ts:1595`.
|
||||
*/
|
||||
effectiveWorkspaceDir: string | undefined;
|
||||
warn?: (message: string) => void;
|
||||
}): Promise<CopilotWorkspaceBootstrapResult> {
|
||||
const { attempt } = params;
|
||||
const workspaceDir = readResolvedWorkspacePath(attempt.workspaceDir);
|
||||
if (!workspaceDir) {
|
||||
return { bootstrapFiles: [], contextFiles: [] };
|
||||
}
|
||||
try {
|
||||
const bootstrapContext = await resolveBootstrapContextForRun({
|
||||
workspaceDir,
|
||||
config: attempt.config,
|
||||
sessionKey: readNonEmptyString((attempt as { sessionKey?: unknown }).sessionKey),
|
||||
sessionId: readNonEmptyString(attempt.sessionId),
|
||||
agentId: readNonEmptyString(attempt.agentId),
|
||||
warn: params.warn,
|
||||
contextMode: attempt.bootstrapContextMode,
|
||||
runKind: attempt.bootstrapContextRunKind,
|
||||
});
|
||||
// Remap context-file paths from the workspace we LOADED them
|
||||
// from (`workspaceDir`, the canonical host workspace where
|
||||
// SOUL.md / IDENTITY.md / .openclaw conventions live) onto the
|
||||
// workspace the SDK session will actually OPERATE in
|
||||
// (`effectiveWorkspaceDir`). When the two are identical (no
|
||||
// sandbox, or sandbox `rw`), remap is a no-op. The render below
|
||||
// and the returned `contextFiles` use the remapped array so the
|
||||
// model never sees a host path while its native loader and
|
||||
// bridged tools see only the sandbox copy.
|
||||
const contextFiles = remapCopilotBootstrapContextFiles({
|
||||
files: bootstrapContext.contextFiles,
|
||||
sourceWorkspaceDir: workspaceDir,
|
||||
targetWorkspaceDir: readResolvedWorkspacePath(params.effectiveWorkspaceDir) ?? workspaceDir,
|
||||
});
|
||||
return {
|
||||
bootstrapFiles: bootstrapContext.bootstrapFiles,
|
||||
contextFiles,
|
||||
instructions: renderCopilotWorkspaceBootstrapInstructions(contextFiles),
|
||||
};
|
||||
} catch (error) {
|
||||
params.warn?.(
|
||||
`[copilot-attempt] failed to load workspace bootstrap instructions: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
return { bootstrapFiles: [], contextFiles: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites context-file paths from a source workspace root to a
|
||||
* target workspace root, mirroring PI's
|
||||
* `remapInjectedContextFilesToWorkspace`
|
||||
* (`src/agents/pi-embedded-runner/run/attempt.ts:603`). Files whose
|
||||
* resolved relative path escapes the source workspace (parent
|
||||
* traversal or absolute) are left untouched so we never pretend a
|
||||
* file lives inside the sandbox when it does not. Exported for unit
|
||||
* tests; intentionally local to the Copilot extension (codex keeps
|
||||
* similar helpers extension-local rather than importing from PI).
|
||||
*/
|
||||
export function remapCopilotBootstrapContextFiles(params: {
|
||||
files: EmbeddedContextFile[];
|
||||
sourceWorkspaceDir: string;
|
||||
targetWorkspaceDir: string;
|
||||
}): EmbeddedContextFile[] {
|
||||
if (params.sourceWorkspaceDir === params.targetWorkspaceDir) {
|
||||
return params.files;
|
||||
}
|
||||
return params.files.map((file) => {
|
||||
const relative = path.relative(params.sourceWorkspaceDir, file.path);
|
||||
if (!isRelativePathInsideOrEqual(relative)) {
|
||||
return file;
|
||||
}
|
||||
return {
|
||||
...file,
|
||||
path:
|
||||
relative === ""
|
||||
? params.targetWorkspaceDir
|
||||
: path.join(params.targetWorkspaceDir, relative),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function isRelativePathInsideOrEqual(relativePath: string): boolean {
|
||||
return (
|
||||
relativePath === "" ||
|
||||
(relativePath !== ".." &&
|
||||
!relativePath.startsWith(`..${path.sep}`) &&
|
||||
!path.isAbsolute(relativePath))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders bootstrap context files into a single string for
|
||||
* `SessionConfig.systemMessage.content` (append mode). Returns
|
||||
* `undefined` when no relevant files remain after filtering, so the
|
||||
* caller can skip setting `systemMessage` altogether.
|
||||
*
|
||||
* Files whose basename matches a doc the Copilot SDK already loads
|
||||
* natively (see {@link COPILOT_NATIVE_PROJECT_DOC_BASENAMES}) are
|
||||
* dropped to avoid duplication with SDK-managed sections.
|
||||
*/
|
||||
export function renderCopilotWorkspaceBootstrapInstructions(
|
||||
contextFiles: EmbeddedContextFile[],
|
||||
): string | undefined {
|
||||
const files = contextFiles
|
||||
.filter((file) => {
|
||||
const baseName = getCopilotContextFileBasename(file.path);
|
||||
return baseName.length > 0 && !COPILOT_NATIVE_PROJECT_DOC_BASENAMES.has(baseName);
|
||||
})
|
||||
.toSorted(compareCopilotContextFiles);
|
||||
if (files.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const hasSoulFile = files.some((file) => getCopilotContextFileBasename(file.path) === "soul.md");
|
||||
const lines: string[] = [
|
||||
"OpenClaw loaded these user-editable workspace files. Treat them as project/user context. The Copilot SDK loads AGENTS.md natively from its instruction directories, so AGENTS.md is not repeated here.",
|
||||
"",
|
||||
"# Project Context",
|
||||
"",
|
||||
"The following project context files have been loaded:",
|
||||
];
|
||||
if (hasSoulFile) {
|
||||
lines.push("SOUL.md: persona/tone. Follow it unless higher-priority instructions override.");
|
||||
}
|
||||
lines.push("");
|
||||
for (const file of files) {
|
||||
lines.push(`## ${file.path}`, "", file.content, "");
|
||||
}
|
||||
return lines.join("\n").trim();
|
||||
}
|
||||
|
||||
function compareCopilotContextFiles(left: EmbeddedContextFile, right: EmbeddedContextFile): number {
|
||||
const leftBase = getCopilotContextFileBasename(left.path);
|
||||
const rightBase = getCopilotContextFileBasename(right.path);
|
||||
const leftOrder = COPILOT_BOOTSTRAP_CONTEXT_ORDER.get(leftBase) ?? Number.MAX_SAFE_INTEGER;
|
||||
const rightOrder = COPILOT_BOOTSTRAP_CONTEXT_ORDER.get(rightBase) ?? Number.MAX_SAFE_INTEGER;
|
||||
if (leftOrder !== rightOrder) {
|
||||
return leftOrder - rightOrder;
|
||||
}
|
||||
const leftPath = normalizeCopilotContextFilePath(left.path);
|
||||
const rightPath = normalizeCopilotContextFilePath(right.path);
|
||||
if (leftPath < rightPath) {
|
||||
return -1;
|
||||
}
|
||||
if (leftPath > rightPath) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function normalizeCopilotContextFilePath(filePath: string): string {
|
||||
return filePath.trim().replaceAll("\\", "/").toLowerCase();
|
||||
}
|
||||
|
||||
function getCopilotContextFileBasename(filePath: string): string {
|
||||
return normalizeCopilotContextFilePath(filePath).split("/").pop() ?? "";
|
||||
}
|
||||
|
||||
function readNonEmptyString(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function readResolvedWorkspacePath(value: unknown): string | undefined {
|
||||
const raw = readNonEmptyString(value);
|
||||
if (!raw) {
|
||||
return undefined;
|
||||
}
|
||||
if (process.platform !== "win32" && /^[A-Za-z]:[\\/]/.test(raw)) {
|
||||
return raw.trim();
|
||||
}
|
||||
return resolveUserPath(raw);
|
||||
}
|
||||
|
||||
export const TESTING_EXPORTS = {
|
||||
COPILOT_NATIVE_PROJECT_DOC_BASENAMES,
|
||||
COPILOT_BOOTSTRAP_CONTEXT_ORDER,
|
||||
compareCopilotContextFiles,
|
||||
getCopilotContextFileBasename,
|
||||
};
|
||||
Reference in New Issue
Block a user