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:
251
extensions/copilot/src/replay-shim.ts
Executable file
251
extensions/copilot/src/replay-shim.ts
Executable file
@@ -0,0 +1,251 @@
|
||||
// Replay-shim for the GitHub Copilot agent runtime.
|
||||
//
|
||||
// Owns three concerns:
|
||||
// 1. Pre-call: should this attempt resume an existing SDK session or
|
||||
// start a new one? Honours `initialReplayState.sdkSessionId` and
|
||||
// `initialReplayState.replayInvalid`.
|
||||
// 2. Post-call: if `resumeSession` fails, was the failure recoverable
|
||||
// (session-gone) so we should downgrade to `createSession`, or
|
||||
// unrecoverable so the error should surface as a prompt error?
|
||||
// 3. Result-time: compute the `replayMetadata` to attach to the attempt
|
||||
// result, propagating prior state with worst-case-wins semantics so
|
||||
// the orchestrator never replays an attempt that may have committed
|
||||
// partial side effects.
|
||||
//
|
||||
// Host back-pointers (NOT imported here to keep the package boundary
|
||||
// clean):
|
||||
// - `src/agents/pi-embedded-runner/replay-state.ts` — canonical
|
||||
// `EmbeddedRunReplayState` / `EmbeddedRunReplayMetadata` shapes
|
||||
// and `replayMetadataFromState`.
|
||||
// - `src/agents/pi-embedded-runner/run/types.ts` —
|
||||
// `AgentHarnessAttemptResult.replayMetadata` field requirement.
|
||||
|
||||
export type ReplayDecision =
|
||||
| {
|
||||
readonly action: "resume";
|
||||
readonly sdkSessionId: string;
|
||||
readonly downgradedFromResume: false;
|
||||
}
|
||||
| {
|
||||
readonly action: "create";
|
||||
readonly downgradedFromResume: boolean;
|
||||
readonly downgradeReason: "no-replay-state" | "no-sdk-session-id" | "replay-invalid";
|
||||
};
|
||||
|
||||
export interface ReplayShimInput {
|
||||
readonly sdkSessionId?: string;
|
||||
readonly replayInvalid?: boolean;
|
||||
}
|
||||
|
||||
function normalizeSdkSessionId(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure pre-call decision: should attempt.ts call resumeSession or
|
||||
* createSession?
|
||||
*
|
||||
* Rules:
|
||||
* - No input → create (no-replay-state)
|
||||
* - No (trimmed) sdkSessionId → create (no-sdk-session-id)
|
||||
* - sdkSessionId + replayInvalid=true → create (replay-invalid),
|
||||
* downgradedFromResume=true
|
||||
* - sdkSessionId + replayInvalid=false → resume
|
||||
*/
|
||||
export function decideReplayAction(input?: ReplayShimInput): ReplayDecision {
|
||||
if (!input) {
|
||||
return {
|
||||
action: "create",
|
||||
downgradedFromResume: false,
|
||||
downgradeReason: "no-replay-state",
|
||||
};
|
||||
}
|
||||
const sdkSessionId = normalizeSdkSessionId(input.sdkSessionId);
|
||||
if (!sdkSessionId) {
|
||||
return {
|
||||
action: "create",
|
||||
downgradedFromResume: false,
|
||||
downgradeReason: "no-sdk-session-id",
|
||||
};
|
||||
}
|
||||
if (input.replayInvalid === true) {
|
||||
return {
|
||||
action: "create",
|
||||
downgradedFromResume: true,
|
||||
downgradeReason: "replay-invalid",
|
||||
};
|
||||
}
|
||||
return {
|
||||
action: "resume",
|
||||
sdkSessionId,
|
||||
downgradedFromResume: false,
|
||||
};
|
||||
}
|
||||
|
||||
export type ResumeFailureKind = "missing" | "unknown";
|
||||
|
||||
export interface ResumeFailureClassification {
|
||||
readonly recoverable: boolean;
|
||||
readonly kind: ResumeFailureKind;
|
||||
}
|
||||
|
||||
const MISSING_SESSION_CODES = new Set([
|
||||
"SESSION_NOT_FOUND",
|
||||
"session_not_found",
|
||||
"NotFound",
|
||||
"ENOENT",
|
||||
]);
|
||||
|
||||
const MISSING_SESSION_MESSAGE_PATTERNS: readonly RegExp[] = [
|
||||
/\bsession not found\b/i,
|
||||
/\bsession .* not found\b/i,
|
||||
/\bunknown session id\b/i,
|
||||
/\bsession id .* (does not exist|not found)\b/i,
|
||||
/\bsession .* does not exist\b/i,
|
||||
/\bno such session\b/i,
|
||||
];
|
||||
|
||||
function readErrorField(error: unknown, key: string): unknown {
|
||||
if (!error || typeof error !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
return (error as Record<string, unknown>)[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-call: classify a resumeSession() failure so attempt.ts can
|
||||
* decide whether to downgrade silently to createSession.
|
||||
*
|
||||
* Conservative: only treats clearly session-gone signals as recoverable.
|
||||
* Structured signals (status === 404, recognised code strings) are
|
||||
* checked first; message matching is a fallback because SDK error
|
||||
* messages are not part of the typed contract.
|
||||
*
|
||||
* Everything else (transport errors, auth failures, generic Error) is
|
||||
* unrecoverable and should surface to the outer attempt.ts try/catch
|
||||
* which converts it to a prompt error.
|
||||
*/
|
||||
export function classifyResumeFailure(error: unknown): ResumeFailureClassification {
|
||||
if (error === undefined || error === null) {
|
||||
return { recoverable: false, kind: "unknown" };
|
||||
}
|
||||
|
||||
const status = readErrorField(error, "status");
|
||||
if (status === 404) {
|
||||
return { recoverable: true, kind: "missing" };
|
||||
}
|
||||
const statusCode = readErrorField(error, "statusCode");
|
||||
if (statusCode === 404) {
|
||||
return { recoverable: true, kind: "missing" };
|
||||
}
|
||||
|
||||
const code = readErrorField(error, "code");
|
||||
if (typeof code === "string" && MISSING_SESSION_CODES.has(code)) {
|
||||
return { recoverable: true, kind: "missing" };
|
||||
}
|
||||
|
||||
const message =
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: typeof error === "object"
|
||||
? typeof (error as { message?: unknown }).message === "string"
|
||||
? (error as { message: string }).message
|
||||
: undefined
|
||||
: undefined;
|
||||
if (typeof message === "string") {
|
||||
for (const pattern of MISSING_SESSION_MESSAGE_PATTERNS) {
|
||||
if (pattern.test(message)) {
|
||||
return { recoverable: true, kind: "missing" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { recoverable: false, kind: "unknown" };
|
||||
}
|
||||
|
||||
export interface ReplayMetadataComputeInput {
|
||||
readonly priorReplayInvalid?: boolean;
|
||||
readonly priorHadPotentialSideEffects?: boolean;
|
||||
readonly thisAttemptTimedOut?: boolean;
|
||||
readonly thisAttemptHadPotentialSideEffects?: boolean;
|
||||
readonly thisAttemptDowngradedFromResume?: boolean;
|
||||
readonly thisAttemptResumeFailureRecovered?: boolean;
|
||||
}
|
||||
|
||||
export interface ComputedReplayMetadata {
|
||||
readonly hadPotentialSideEffects: boolean;
|
||||
readonly replaySafe: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the `EmbeddedRunReplayMetadata` to attach to the attempt
|
||||
* result. Worst-case-wins:
|
||||
*
|
||||
* hadPotentialSideEffects = priorHadPotentialSideEffects OR timedOut
|
||||
* OR thisAttemptHadPotentialSideEffects
|
||||
* (timeout means we cannot prove the prompt was not partially
|
||||
* committed server-side; treat as side-effecting so the
|
||||
* orchestrator will not blindly re-issue the same prompt).
|
||||
*
|
||||
* replaySafe = NOT (
|
||||
* priorReplayInvalid
|
||||
* OR thisAttemptDowngradedFromResume
|
||||
* OR thisAttemptResumeFailureRecovered
|
||||
* OR hadPotentialSideEffects
|
||||
* )
|
||||
*
|
||||
* Matches the parity rule in
|
||||
* `src/agents/pi-embedded-runner/replay-state.ts#replayMetadataFromState`.
|
||||
*/
|
||||
export function computeReplayMetadata(input: ReplayMetadataComputeInput): ComputedReplayMetadata {
|
||||
const priorReplayInvalid = input.priorReplayInvalid === true;
|
||||
const priorHadPotentialSideEffects = input.priorHadPotentialSideEffects === true;
|
||||
const timedOut = input.thisAttemptTimedOut === true;
|
||||
const thisAttemptHadPotentialSideEffects = input.thisAttemptHadPotentialSideEffects === true;
|
||||
const downgraded = input.thisAttemptDowngradedFromResume === true;
|
||||
const recovered = input.thisAttemptResumeFailureRecovered === true;
|
||||
const hadPotentialSideEffects =
|
||||
priorHadPotentialSideEffects || timedOut || thisAttemptHadPotentialSideEffects;
|
||||
const replaySafe = !(priorReplayInvalid || downgraded || recovered || hadPotentialSideEffects);
|
||||
return { hadPotentialSideEffects, replaySafe };
|
||||
}
|
||||
|
||||
const COPILOT_REPLAY_SAFE_READ_ONLY_TOOL_NAMES = new Set([
|
||||
"get",
|
||||
"file_read",
|
||||
"glob",
|
||||
"grep",
|
||||
"inspect",
|
||||
"list",
|
||||
"ls",
|
||||
"memory_get",
|
||||
"probe",
|
||||
"query",
|
||||
"read",
|
||||
"search",
|
||||
"sessions_history",
|
||||
"sessions_list",
|
||||
"status",
|
||||
"tool_search",
|
||||
"update_plan",
|
||||
"view",
|
||||
"web_fetch",
|
||||
"web_search",
|
||||
]);
|
||||
|
||||
export function copilotToolMetasHavePotentialSideEffects(
|
||||
toolMetas?: readonly { asyncStarted?: boolean; toolName: string }[],
|
||||
): boolean {
|
||||
return (toolMetas ?? []).some(
|
||||
(entry) => entry.asyncStarted === true || !isReplaySafeReadOnlyToolName(entry.toolName),
|
||||
);
|
||||
}
|
||||
|
||||
function isReplaySafeReadOnlyToolName(toolName: string): boolean {
|
||||
const normalized = toolName.trim().toLowerCase();
|
||||
return COPILOT_REPLAY_SAFE_READ_ONLY_TOOL_NAMES.has(normalized);
|
||||
}
|
||||
Reference in New Issue
Block a user