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

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:
2026-07-05 09:36:54 +00:00
parent 3216769225
commit bedb527145
21108 changed files with 6010766 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}"
ROOT_DIR="$(cd "$ROOT_DIR" && pwd)"
TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}"
TRUSTED_HARNESS_DIR="$(cd "$TRUSTED_HARNESS_DIR" && pwd)"
source "$TRUSTED_HARNESS_DIR/scripts/lib/docker-e2e-image.sh"
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-code-mode-namespace-live-e2e" OPENCLAW_CODE_MODE_NAMESPACE_LIVE_E2E_IMAGE)"
SKIP_BUILD="${OPENCLAW_CODE_MODE_NAMESPACE_LIVE_E2E_SKIP_BUILD:-0}"
PROFILE_FILE="${OPENCLAW_CODE_MODE_NAMESPACE_LIVE_PROFILE_FILE:-${OPENCLAW_TESTBOX_PROFILE_FILE:-$HOME/.openclaw-testbox-live.profile}}"
run_log=""
if [ ! -f "$PROFILE_FILE" ] && [ -f "$HOME/.profile" ]; then
PROFILE_FILE="$HOME/.profile"
fi
cleanup() {
if [ -n "${run_log:-}" ]; then
rm -f "$run_log"
fi
}
trap cleanup EXIT
docker_e2e_build_or_reuse "$IMAGE_NAME" code-mode-namespace-live "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD"
PROFILE_MOUNT=()
PROFILE_STATUS="none"
if [ -f "$PROFILE_FILE" ] && [ -r "$PROFILE_FILE" ]; then
set -a
# shellcheck disable=SC1090
source "$PROFILE_FILE"
set +a
PROFILE_MOUNT=(-v "$PROFILE_FILE":/home/appuser/.profile:ro)
PROFILE_STATUS="$PROFILE_FILE"
fi
echo "Running code mode namespace live Docker E2E..."
echo "Profile file: $PROFILE_STATUS"
run_log="$(docker_e2e_run_log code-mode-namespace-live)"
if ! docker_e2e_run_with_harness \
--user root \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
-e OPENAI_API_KEY \
-e OPENAI_BASE_URL \
-e "OPENCLAW_CODE_MODE_LIVE_MODEL=${OPENCLAW_CODE_MODE_LIVE_MODEL:-gpt-5.4-mini}" \
-e "OPENCLAW_CODE_MODE_LIVE_TASKS=${OPENCLAW_CODE_MODE_LIVE_TASKS:-3}" \
-v "$ROOT_DIR":/src:ro \
"${PROFILE_MOUNT[@]}" \
"$IMAGE_NAME" \
bash /src/scripts/repro/code-mode-namespace-live-scenario.sh >"$run_log" 2>&1; then
docker_e2e_print_log "$run_log"
exit 1
fi
docker_e2e_print_log "$run_log"
echo "Code mode namespace live Docker E2E passed"

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
source scripts/lib/live-docker-stage.sh
for profile_path in "$HOME/.profile" /home/appuser/.profile; do
if [ -f "$profile_path" ] && [ -r "$profile_path" ]; then
set +e +u
# shellcheck disable=SC1090
source "$profile_path"
set -euo pipefail
break
fi
done
if [ -z "${OPENAI_API_KEY:-}" ]; then
echo "ERROR: OPENAI_API_KEY is required for the code mode namespace live Docker test." >&2
exit 1
fi
export OPENAI_API_KEY
if [ -n "${OPENAI_BASE_URL:-}" ]; then
export OPENAI_BASE_URL
fi
tmp_dir="$(mktemp -d)"
cleanup() {
rm -rf "$tmp_dir"
}
trap cleanup EXIT
openclaw_live_stage_source_tree "$tmp_dir"
openclaw_live_stage_node_modules "$tmp_dir"
openclaw_live_link_runtime_tree "$tmp_dir"
cd "$tmp_dir"
tsx scripts/repro/code-mode-namespace-live.ts

View File

@@ -0,0 +1,662 @@
#!/usr/bin/env -S node --import tsx
// Code Mode Namespace Live script supports OpenClaw repository automation.
import { performance } from "node:perf_hooks";
import { pathToFileURL } from "node:url";
import { Type } from "typebox";
import type { Model } from "../../packages/agent-core/src/llm.js";
import type { AgentEvent, AgentTool } from "../../packages/agent-core/src/types.js";
import {
clearCodeModeNamespacesForPlugin,
createCodeModeNamespaceTool,
registerCodeModeNamespaceForPlugin,
} from "../../src/agents/code-mode-namespaces.js";
import { applyCodeModeCatalog, createCodeModeTools } from "../../src/agents/code-mode.js";
import { Agent } from "../../src/agents/runtime/index.js";
import { createToolSearchCatalogRef } from "../../src/agents/tool-search.js";
import { jsonResult, type AnyAgentTool } from "../../src/agents/tools/common.js";
import { setPluginToolMeta } from "../../src/plugins/tools.js";
type Mode = "regular" | "code-catalog" | "code-namespace";
type FictionTitle = {
id: string;
title: string;
lead: string;
status: string;
riskScore: number;
dependencies: Array<{ id: string; cleared: boolean }>;
};
type FictionScene = {
id: string;
titleId: string;
pages: number;
blocked: boolean;
};
type FictionDefect = {
id: string;
titleId: string;
sceneId: string;
state: "open" | "closed";
};
type FictionInvoice = {
id: string;
titleId: string;
author: string;
amount: number;
paid: boolean;
};
type FictionServiceState = {
titles: FictionTitle[];
scenes: FictionScene[];
defects: FictionDefect[];
invoices: FictionInvoice[];
};
type FictionService = ReturnType<typeof createFictionService>;
type Task = {
id: string;
prompt: string;
validate(answer: unknown, service: FictionService): { ok: boolean; reason?: string };
};
type RunMetrics = {
mode: Mode;
task: string;
ok: boolean;
reason?: string;
latencyMs: number;
modelTurns: number;
assistantMessages: number;
topLevelToolCalls: number;
serviceCalls: number;
finalText: string;
stopReason?: string;
errorMessage?: string;
toolResults?: unknown[];
};
const PLUGIN_ID = "fictions-live";
const POSITIVE_INTEGER_PATTERN = /^[1-9]\d*$/u;
function cloneState(): FictionServiceState {
return {
titles: [
{
id: "PX-73",
title: "The Glass Orchard",
lead: "Mira Vale",
status: "draft",
riskScore: 77,
dependencies: [
{ id: "outline", cleared: true },
{ id: "rights", cleared: true },
],
},
{
id: "NM-12",
title: "Night Market of Moons",
lead: "Oren Quill",
status: "revision",
riskScore: 91,
dependencies: [
{ id: "continuity", cleared: true },
{ id: "copyedit", cleared: false },
],
},
{
id: "RS-40",
title: "River Static",
lead: "Nia Rowan",
status: "locked",
riskScore: 54,
dependencies: [{ id: "legal", cleared: true }],
},
],
scenes: [
{ id: "PX-73-S1", titleId: "PX-73", pages: 32, blocked: false },
{ id: "PX-73-S2", titleId: "PX-73", pages: 28, blocked: false },
{ id: "PX-73-S3", titleId: "PX-73", pages: 36, blocked: false },
{ id: "NM-12-S1", titleId: "NM-12", pages: 44, blocked: false },
{ id: "NM-12-S2", titleId: "NM-12", pages: 39, blocked: true },
{ id: "RS-40-S1", titleId: "RS-40", pages: 51, blocked: false },
],
defects: [
{ id: "D-101", titleId: "NM-12", sceneId: "NM-12-S1", state: "open" },
{ id: "D-102", titleId: "NM-12", sceneId: "NM-12-S2", state: "open" },
{ id: "D-103", titleId: "NM-12", sceneId: "NM-12-S2", state: "open" },
{ id: "D-104", titleId: "PX-73", sceneId: "PX-73-S3", state: "closed" },
{ id: "D-105", titleId: "RS-40", sceneId: "RS-40-S1", state: "open" },
],
invoices: [
{ id: "I-200", titleId: "PX-73", author: "Mira Vale", amount: 4200, paid: false },
{ id: "I-201", titleId: "NM-12", author: "Oren Quill", amount: 6100, paid: false },
{ id: "I-202", titleId: "RS-40", author: "Nia Rowan", amount: 3700, paid: true },
],
};
}
function createFictionService() {
const state = cloneState();
let calls = 0;
const note = () => {
calls += 1;
};
const title = (id: string) => state.titles.find((entry) => entry.id === id);
return {
get calls() {
return calls;
},
snapshot() {
note();
return structuredClone(state);
},
listTitles() {
note();
return structuredClone(state.titles);
},
getTitle(id: string) {
note();
return title(id) ?? null;
},
listScenes(titleId?: string) {
note();
return structuredClone(state.scenes.filter((entry) => !titleId || entry.titleId === titleId));
},
listDefects(titleId?: string) {
note();
return state.defects
.filter((entry) => !titleId || entry.titleId === titleId)
.map((entry) => structuredClone(entry));
},
listInvoices(author?: string) {
note();
return structuredClone(state.invoices.filter((entry) => !author || entry.author === author));
},
updateStatus(id: string, status: string) {
note();
const entry = title(id);
if (!entry) {
return { ok: false, error: "unknown title", id };
}
entry.status = status;
return { ok: true, id, status };
},
currentStatus(id: string) {
return title(id)?.status;
},
};
}
function stringParam(params: Record<string, unknown>, key: string): string {
const value = params[key];
return typeof value === "string" ? value : "";
}
function makeTool(
name: string,
description: string,
properties: Record<string, unknown>,
execute: (params: Record<string, unknown>) => unknown,
): AnyAgentTool {
const tool = {
name,
label: name,
description,
parameters: Type.Object(properties),
execute: async (_toolCallId: string, params: unknown) =>
jsonResult(
execute((params && typeof params === "object" ? params : {}) as Record<string, unknown>),
),
} satisfies AnyAgentTool;
setPluginToolMeta(tool, { pluginId: PLUGIN_ID, optional: true });
return tool;
}
function createFictionTools(service: FictionService): AnyAgentTool[] {
return [
makeTool("fictions_list_titles", "List fiction titles with status and risk.", {}, () =>
service.listTitles(),
),
makeTool(
"fictions_get_title",
"Get one fiction title by id.",
{ id: Type.String() },
(params) => service.getTitle(stringParam(params, "id")),
),
makeTool(
"fictions_list_scenes",
"List scenes, optionally filtered by title id.",
{ titleId: Type.Optional(Type.String()) },
(params) =>
service.listScenes(typeof params.titleId === "string" ? params.titleId : undefined),
),
makeTool(
"fictions_list_defects",
"List defects, optionally filtered by title id.",
{ titleId: Type.Optional(Type.String()) },
(params) =>
service.listDefects(typeof params.titleId === "string" ? params.titleId : undefined),
),
makeTool(
"fictions_list_invoices",
"List invoices, optionally filtered by author.",
{ author: Type.Optional(Type.String()) },
(params) =>
service.listInvoices(typeof params.author === "string" ? params.author : undefined),
),
makeTool(
"fictions_update_status",
"Update a fiction title status.",
{ id: Type.String(), status: Type.String() },
(params) => service.updateStatus(stringParam(params, "id"), stringParam(params, "status")),
),
];
}
function createFictionNamespaceTools(service: FictionService): AnyAgentTool[] {
return [
makeTool("fictions_snapshot", "Return the complete fiction production snapshot.", {}, () =>
service.snapshot(),
),
makeTool("fictions_risk_audit", "Return highest-risk title audit.", {}, () => {
const data = service.snapshot();
const highest = data.titles.toSorted((a, b) => b.riskScore - a.riskScore)[0];
if (!highest) {
return null;
}
return {
task: "risk-audit",
id: highest.id,
lead: highest.lead,
status: highest.status,
unresolvedDefects: data.defects.filter(
(defect) => defect.titleId === highest.id && defect.state === "open",
).length,
blockedScenes: data.scenes
.filter((scene) => scene.titleId === highest.id && scene.blocked)
.map((scene) => scene.id),
};
}),
makeTool(
"fictions_promote_if_ready",
"Promote a title if dependencies and page count allow it.",
{ id: Type.String(), status: Type.String() },
(params) => {
const id = stringParam(params, "id");
const status = stringParam(params, "status");
const data = service.snapshot();
const title = data.titles.find((entry) => entry.id === id);
const scenes = data.scenes.filter((scene) => scene.titleId === id);
const totalPages = scenes.reduce((sum, scene) => sum + scene.pages, 0);
const dependenciesCleared =
title?.dependencies.every((dependency) => dependency.cleared) ?? false;
if (!title || totalPages >= 110 || !dependenciesCleared) {
return {
task: "promote",
id,
action: "blocked",
totalPages,
finalStatus: title?.status ?? null,
};
}
const updated = service.updateStatus(id, status);
return {
task: "promote",
id,
action: updated.ok ? "updated" : "blocked",
totalPages,
finalStatus: service.currentStatus(id) ?? null,
};
},
),
makeTool(
"fictions_unpaid_over",
"Return unpaid invoices over a numeric threshold.",
{ amount: Type.Number() },
(params) => {
const amount = typeof params.amount === "number" ? params.amount : 0;
const data = service.snapshot();
const invoices = data.invoices.filter(
(invoice) => !invoice.paid && invoice.amount > amount,
);
return {
task: "invoice",
invoiceIds: invoices.map((invoice) => invoice.id),
totalUnpaidOver5000: invoices.reduce((sum, invoice) => sum + invoice.amount, 0),
};
},
),
];
}
function registerFictionNamespace(): void {
clearCodeModeNamespacesForPlugin(PLUGIN_ID);
registerCodeModeNamespaceForPlugin(PLUGIN_ID, {
id: "fictions",
globalName: "Fictions",
description: "Fiction production service helpers.",
requiredToolNames: [
"fictions_promote_if_ready",
"fictions_risk_audit",
"fictions_snapshot",
"fictions_unpaid_over",
],
prompt:
"Use Fictions.riskAudit(), Fictions.promoteIfReady(id, status), Fictions.unpaidOver(amount), and Fictions.snapshot().",
createScope: () => ({
snapshot: createCodeModeNamespaceTool("fictions_snapshot"),
riskAudit: createCodeModeNamespaceTool("fictions_risk_audit"),
promoteIfReady: createCodeModeNamespaceTool("fictions_promote_if_ready", ([id, status]) => ({
id: typeof id === "string" ? id : "",
status: typeof status === "string" ? status : "",
})),
unpaidOver: createCodeModeNamespaceTool("fictions_unpaid_over", ([amount]) => ({
amount: typeof amount === "number" ? amount : 0,
})),
}),
});
}
function createModel(modelId: string): Model<"openai-responses"> {
const baseUrl = process.env.OPENAI_BASE_URL?.trim() || "https://api.openai.com/v1";
return {
id: modelId,
name: modelId,
api: "openai-responses",
provider: "openai",
baseUrl,
reasoning: modelId.startsWith("gpt-5"),
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 400_000,
maxTokens: 128_000,
};
}
function systemPromptForMode(mode: Mode): string {
const base =
"You are testing a fiction production service. Use the available tools, never invent data, and return only one minified JSON object. No markdown.";
if (mode === "regular") {
return `${base} Use the Fictions tools directly.`;
}
if (mode === "code-catalog") {
return `${base} Use code_mode_exec with JavaScript and always return the final JSON object from the code. In code, call direct tool helpers such as await tools.fictions_list_titles({}), await tools.fictions_list_scenes({titleId:"PX-73"}), await tools.fictions_list_defects({titleId:"NM-12"}), await tools.fictions_list_invoices({}), and await tools.fictions_update_status({id:"PX-73",status:"preproduction"}). Call code_mode_wait until the code result is completed, then return that completed value as your final answer.`;
}
return `${base} Use code_mode_exec with JavaScript and always return the final JSON object from the code. In code, prefer the namespace helpers: return await Fictions.riskAudit(); return await Fictions.promoteIfReady("PX-73","preproduction"); return await Fictions.unpaidOver(5000). Call code_mode_wait until the code result is completed, then return that completed value as your final answer.`;
}
function toolsForMode(mode: Mode, service: FictionService): AgentTool[] {
const fictionTools = createFictionTools(service);
if (mode === "regular") {
return fictionTools as AgentTool[];
}
if (mode === "code-namespace") {
registerFictionNamespace();
} else {
clearCodeModeNamespacesForPlugin(PLUGIN_ID);
}
const config = {
tools: {
codeMode: {
enabled: true,
timeoutMs: 20_000,
maxPendingToolCalls: 32,
},
},
};
const catalogRef = createToolSearchCatalogRef();
const codeModeTools = createCodeModeTools({
config,
runtimeConfig: config,
sessionId: `live-${mode}`,
sessionKey: `agent:live-${mode}:main`,
agentId: "live",
runId: `run-${mode}`,
catalogRef,
});
const catalogTools =
mode === "code-namespace" ? createFictionNamespaceTools(service) : fictionTools;
return applyCodeModeCatalog({
tools: [...codeModeTools, ...catalogTools],
config,
sessionId: `live-${mode}`,
sessionKey: `agent:live-${mode}:main`,
agentId: "live",
runId: `run-${mode}`,
catalogRef,
}).tools as AgentTool[];
}
function textFromMessageContent(content: unknown): string {
if (typeof content === "string") {
return content;
}
if (!Array.isArray(content)) {
return "";
}
return content
.filter(
(entry) => entry && typeof entry === "object" && (entry as { type?: string }).type === "text",
)
.map((entry) => (entry as { text?: string }).text ?? "")
.join("");
}
function parseFirstJson(text: string): unknown {
const trimmed = text.trim();
try {
return JSON.parse(trimmed) as unknown;
} catch {
const start = trimmed.indexOf("{");
const end = trimmed.lastIndexOf("}");
if (start >= 0 && end > start) {
return JSON.parse(trimmed.slice(start, end + 1)) as unknown;
}
throw new Error("assistant did not return JSON");
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value && typeof value === "object" && !Array.isArray(value));
}
const tasks: Task[] = [
{
id: "risk-audit",
prompt:
'Find the fiction title with the highest riskScore. Return JSON with keys task, id, lead, status, unresolvedDefects, blockedScenes. blockedScenes must be an array of blocked scene ids, not a count. task must be "risk-audit".',
validate(answer) {
if (!isRecord(answer)) {
return { ok: false, reason: "answer is not an object" };
}
const blockedScenes = Array.isArray(answer.blockedScenes)
? answer.blockedScenes.map(String).toSorted()
: [];
const unresolvedDefects = Array.isArray(answer.unresolvedDefects)
? answer.unresolvedDefects.length
: answer.unresolvedDefects;
const ok =
answer.task === "risk-audit" &&
answer.id === "NM-12" &&
answer.lead === "Oren Quill" &&
answer.status === "revision" &&
unresolvedDefects === 3 &&
JSON.stringify(blockedScenes) === JSON.stringify(["NM-12-S2"]);
return ok ? { ok } : { ok, reason: `unexpected risk audit: ${JSON.stringify(answer)}` };
},
},
{
id: "promote",
prompt:
'For PX-73, if total scene pages are below 110 and every dependency is cleared, update its status to "preproduction". If a Fictions.promoteIfReady helper exists, use it. Return JSON with keys task, id, action, totalPages, finalStatus. action must be exactly "updated" when the command succeeds. task must be "promote".',
validate(answer, service) {
if (!isRecord(answer)) {
return { ok: false, reason: "answer is not an object" };
}
const ok =
answer.task === "promote" &&
answer.id === "PX-73" &&
typeof answer.action === "string" &&
answer.action.includes("updated") &&
answer.totalPages === 96 &&
answer.finalStatus === "preproduction" &&
service.currentStatus("PX-73") === "preproduction";
return ok ? { ok } : { ok, reason: `unexpected promote result: ${JSON.stringify(answer)}` };
},
},
{
id: "invoice",
prompt:
'For unpaid invoices over 5000, return JSON with keys task, invoiceIds, totalUnpaidOver5000. task must be "invoice".',
validate(answer) {
if (!isRecord(answer)) {
return { ok: false, reason: "answer is not an object" };
}
const invoiceIds = Array.isArray(answer.invoiceIds)
? answer.invoiceIds.map(String).toSorted()
: [];
const ok =
answer.task === "invoice" &&
JSON.stringify(invoiceIds) === JSON.stringify(["I-201"]) &&
answer.totalUnpaidOver5000 === 6100;
return ok ? { ok } : { ok, reason: `unexpected invoice result: ${JSON.stringify(answer)}` };
},
},
];
async function runOne(mode: Mode, task: Task, model: string, apiKey: string): Promise<RunMetrics> {
const service = createFictionService();
const counts = {
modelTurns: 0,
assistantMessages: 0,
topLevelToolCalls: 0,
};
const toolResults: unknown[] = [];
const agent = new Agent({
sessionId: `code-mode-live-${mode}-${task.id}`,
initialState: {
model: createModel(model),
systemPrompt: systemPromptForMode(mode),
tools: toolsForMode(mode, service),
thinkingLevel: "off",
},
getApiKey: (provider) => (provider === "openai" ? apiKey : undefined),
toolExecution: "parallel",
maxRetryDelayMs: 10_000,
});
agent.subscribe((event: AgentEvent) => {
if (event.type === "turn_start") {
counts.modelTurns += 1;
} else if (event.type === "message_end" && event.message.role === "assistant") {
counts.assistantMessages += 1;
} else if (event.type === "tool_execution_start") {
counts.topLevelToolCalls += 1;
} else if (event.type === "tool_execution_end") {
toolResults.push(event.result);
}
});
const started = performance.now();
await agent.prompt(task.prompt);
const latencyMs = Math.round(performance.now() - started);
const lastAssistant = agent.state.messages
.toReversed()
.find((message) => message.role === "assistant");
const finalText = textFromMessageContent(lastAssistant?.content).trim();
let validation: { ok: boolean; reason?: string };
try {
validation = task.validate(parseFirstJson(finalText), service);
} catch (error) {
validation = {
ok: false,
reason: error instanceof Error ? error.message : String(error),
};
}
return {
mode,
task: task.id,
ok: validation.ok,
...(validation.reason ? { reason: validation.reason } : {}),
latencyMs,
modelTurns: counts.modelTurns,
assistantMessages: counts.assistantMessages,
topLevelToolCalls: counts.topLevelToolCalls,
serviceCalls: service.calls,
finalText,
...(lastAssistant?.stopReason ? { stopReason: lastAssistant.stopReason } : {}),
...(lastAssistant?.errorMessage ? { errorMessage: lastAssistant.errorMessage } : {}),
...(process.env.OPENCLAW_CODE_MODE_LIVE_DEBUG === "1" ? { toolResults } : {}),
};
}
function readArg(name: string): string | undefined {
const prefix = `--${name}=`;
const match = process.argv.find((arg) => arg.startsWith(prefix));
return match?.slice(prefix.length);
}
export function parseTaskLimit(raw: string | undefined, label: string): number {
const text = raw?.trim() ?? "3";
if (!POSITIVE_INTEGER_PATTERN.test(text)) {
throw new Error(`${label} must be a positive integer`);
}
const parsed = Number(text);
if (!Number.isSafeInteger(parsed)) {
throw new Error(`${label} must be a safe positive integer`);
}
return parsed;
}
export async function main() {
const model = readArg("model") ?? process.env.OPENCLAW_CODE_MODE_LIVE_MODEL ?? "gpt-5.4-mini";
const modeArg = readArg("modes");
const modes = (modeArg ? modeArg.split(",") : ["regular", "code-namespace"]) as Mode[];
const taskArg = readArg("tasks");
const taskLimit = parseTaskLimit(
taskArg ?? process.env.OPENCLAW_CODE_MODE_LIVE_TASKS,
taskArg === undefined ? "OPENCLAW_CODE_MODE_LIVE_TASKS" : "--tasks",
);
const apiKey = process.env.OPENAI_API_KEY?.trim();
if (!apiKey) {
throw new Error("OPENAI_API_KEY is required");
}
const selectedTasks = tasks.slice(0, taskLimit);
const results: RunMetrics[] = [];
for (const task of selectedTasks) {
for (const mode of modes) {
results.push(await runOne(mode, task, model, apiKey));
}
}
const summary = {
model,
tasks: selectedTasks.map((task) => task.id),
results,
aggregate: modes.map((mode) => {
const entries = results.filter((entry) => entry.mode === mode);
return {
mode,
ok: entries.filter((entry) => entry.ok).length,
total: entries.length,
latencyMs: entries.reduce((sum, entry) => sum + entry.latencyMs, 0),
modelTurns: entries.reduce((sum, entry) => sum + entry.modelTurns, 0),
topLevelToolCalls: entries.reduce((sum, entry) => sum + entry.topLevelToolCalls, 0),
serviceCalls: entries.reduce((sum, entry) => sum + entry.serviceCalls, 0),
};
}),
};
console.log(JSON.stringify(summary, null, 2));
if (results.some((entry) => !entry.ok)) {
process.exitCode = 1;
}
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
await main().finally(() => {
clearCodeModeNamespacesForPlugin(PLUGIN_ID);
});
}

View File

@@ -0,0 +1,81 @@
/**
* Live proof script for PR #95484 — assistant reply lost after compaction rotation.
*
* Demonstrates that:
* 1. BEFORE fix: successor context shows [compactionSummary, user, ...]
* — the assistant reply is silently dropped.
* 2. AFTER fix: successor context shows [compactionSummary, assistant, user, ...]
* — the assistant reply is preserved.
*
* Usage: node --import tsx scripts/repro/issue-76729-compaction-assistant-loss-proof.mts
*/
import { mkdtempSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { SessionManager } from "openclaw/plugin-sdk/agent-sessions";
import type { AgentMessage } from "openclaw/plugin-sdk/agent-core";
import type { TextContent } from "openclaw/plugin-sdk/llm";
import { makeAgentAssistantMessage } from "../../src/agents/test-helpers/agent-message-fixtures.js";
import { rotateTranscriptAfterCompaction } from "../../src/agents/embedded-agent-runner/compaction-successor-transcript.js";
const sessionDir = mkdtempSync(join(tmpdir(), "compaction-proof-"));
console.log("Session dir:", sessionDir);
console.log();
const manager = SessionManager.create(sessionDir, sessionDir);
// Build session: user("Summarize") → assistant("Here is the summary") → user("Analyze Q3") → assistant("Q3 analysis...") → compaction(firstKept=user_Analyze_Q3)
manager.appendMessage({ role: "user", content: "Summarize reports", timestamp: 1 });
manager.appendMessage(makeAgentAssistantMessage({ content: [{ type: "text", text: "Here is the summary" }], timestamp: 2 }));
const firstKeptId = manager.appendMessage({ role: "user", content: "Analyze Q3", timestamp: 3 });
manager.appendMessage(makeAgentAssistantMessage({ content: [{ type: "text", text: "Q3 analysis shows..." }], timestamp: 4 }));
manager.appendCompaction("Summary of previous work.", firstKeptId, 5000);
// Post-compaction
manager.appendMessage({ role: "user", content: "Any more insights?", timestamp: 5 });
manager.appendMessage(makeAgentAssistantMessage({ content: [{ type: "text", text: "Additional insights" }], timestamp: 6 }));
const sessionFile = manager.getSessionFile()!;
console.log("Source session file:", sessionFile);
console.log();
const result = await rotateTranscriptAfterCompaction({
sessionManager: manager,
sessionFile,
now: () => new Date("2026-06-21T12:00:00.000Z"),
});
console.log("Rotation result:", JSON.stringify(result, null, 2));
console.log();
// Open successor and inspect context
const successor = SessionManager.open(result.sessionFile!);
const context = successor.buildSessionContext();
console.log("=== SUCCESSOR CONTEXT ===");
console.log("Roles:", JSON.stringify(context.messages.map((m) => m.role)));
for (const msg of context.messages) {
if (msg.role === "compactionSummary") {
const summary = (msg as AgentMessage & { summary: string }).summary;
console.log(` [compactionSummary] summary="${summary}"`);
} else if ("content" in msg) {
const text = Array.isArray(msg.content)
? (msg.content[0] as TextContent)?.text ?? JSON.stringify(msg.content)
: msg.content;
console.log(` [${msg.role}] "${text}"`);
}
}
console.log();
const roles = context.messages.map((m) => m.role);
console.log("=== VERIFICATION ===");
console.log("Role sequence:", JSON.stringify(roles));
console.log("compactionSummary → assistant (no gap):", roles[0] === "compactionSummary" && roles[1] === "assistant");
console.log("BEFORE fix shows: [compactionSummary, user, assistant, ...] ← missing assistant");
console.log("AFTER fix shows:", JSON.stringify(roles));
console.log();
// Cleanup
rmSync(sessionDir, { recursive: true, force: true });
console.log("Temp dir cleaned up.");

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env node
/**
* Live repro for issue #95198: OpenRouter short model IDs get double-prefixed.
*
* Verifies that `openrouter/deepseek-v4-flash` resolves to the upstream API
* model ID `deepseek/deepseek-v4-flash` instead of the broken
* `openrouter/openrouter/deepseek-v4-flash`.
*/
import { normalizeOpenRouterApiModelId } from "../../extensions/openrouter/models.js";
const cases = [
{ input: "openrouter/deepseek-v4-flash", expected: "deepseek/deepseek-v4-flash" },
{ input: "openrouter/deepseek-v4-pro", expected: "deepseek/deepseek-v4-pro" },
{ input: "openrouter/deepseek/deepseek-v4-flash", expected: "deepseek/deepseek-v4-flash" },
{ input: "openrouter/auto", expected: "openrouter/auto" },
{ input: "openrouter/free", expected: "openrouter/free" },
{ input: "deepseek/deepseek-v4-flash", expected: "deepseek/deepseek-v4-flash" },
];
let failed = false;
console.log("=== Reproduction for issue #95198 ===");
for (const { input, expected } of cases) {
const actual = normalizeOpenRouterApiModelId(input);
const pass = actual === expected;
console.log(`${pass ? "PASS" : "FAIL"}: ${input} -> ${actual} (expected ${expected})`);
if (!pass) {
failed = true;
}
}
if (failed) {
console.error("\nFAIL: one or more OpenRouter model ID normalizations are incorrect.");
process.exitCode = 1;
} else {
console.log("\nPASS: short OpenRouter model IDs resolve to the correct upstream slugs.");
}

View File

@@ -0,0 +1,133 @@
#!/usr/bin/env node
// Live repro for numeric limit edge cases across diagnostics, usage, and voice-call CLI.
import assert from "node:assert/strict";
/**
* Live repro for limit/CLI numeric fixes (PR #82679). Run: pnpm exec tsx scripts/repro/limit-edge-case-live-proof.mjs
*/
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { testing as voiceCallCliTesting } from "../../extensions/voice-call/src/cli.ts";
import { loadSessionLogs, loadSessionUsageTimeSeries } from "../../src/infra/session-cost-usage.ts";
import {
getRecentDiagnosticPhases,
recordDiagnosticPhase,
resetDiagnosticPhasesForTest,
} from "../../src/logging/diagnostic-phase.ts";
/**
* Creates and cleans a temp root for live proof fixtures.
*/
export async function withProofTempRoot(callback) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-proof-"));
try {
return await callback(root);
} finally {
fs.rmSync(root, { force: true, recursive: true });
}
}
async function main() {
resetDiagnosticPhasesForTest();
recordDiagnosticPhase({
name: "phase-a",
startedAt: 1,
endedAt: 2,
durationMs: 1,
cpuUserMs: 0,
cpuSystemMs: 0,
cpuTotalMs: 0,
cpuCoreRatio: 0,
});
recordDiagnosticPhase({
name: "phase-b",
startedAt: 3,
endedAt: 4,
durationMs: 1,
cpuUserMs: 0,
cpuSystemMs: 0,
cpuTotalMs: 0,
cpuCoreRatio: 0,
});
const zeroPhases = getRecentDiagnosticPhases(0);
assert.equal(zeroPhases.length, 0);
console.log("getRecentDiagnosticPhases(0).length =", zeroPhases.length);
await withProofTempRoot(async (root) => {
const sessionFile = path.join(root, "s.jsonl");
fs.writeFileSync(
sessionFile,
[
JSON.stringify({
type: "message",
timestamp: "2026-01-01T00:00:00.000Z",
message: { role: "user", content: "a" },
}),
JSON.stringify({
type: "message",
timestamp: "2026-01-01T00:01:00.000Z",
message: {
role: "assistant",
content: "b",
provider: "openai",
model: "gpt-5.5",
usage: {
input: 1,
output: 2,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 3,
cost: { total: 0.001 },
},
},
}),
JSON.stringify({
type: "message",
timestamp: "2026-01-01T00:02:00.000Z",
message: {
role: "assistant",
content: "c",
provider: "openai",
model: "gpt-5.5",
usage: {
input: 3,
output: 4,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 7,
cost: { total: 0.002 },
},
},
}),
].join("\n"),
);
const logs = await loadSessionLogs({ sessionFile, limit: 0 });
const series = await loadSessionUsageTimeSeries({ sessionFile, maxPoints: 0 });
const positiveLogs = await loadSessionLogs({ sessionFile, limit: 10 });
const positiveSeries = await loadSessionUsageTimeSeries({ sessionFile, maxPoints: 10 });
assert.equal(logs?.length, 0);
assert.equal(series.points.length, 0);
assert.equal(positiveLogs?.length, 3);
assert.equal(positiveSeries.points.length, 2);
console.log("loadSessionLogs({ limit: 0 }).length =", logs?.length);
console.log(
"loadSessionUsageTimeSeries({ maxPoints: 0 }).points.length =",
series?.points.length,
);
try {
voiceCallCliTesting.parseVoiceCallIntOption("nope", "--port", { min: 1 });
assert.fail("expected invalid voicecall --port value to throw");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
assert.equal(message, "Invalid numeric value for --port: nope");
console.log("parseVoiceCallIntOption('nope', '--port') error:", message);
}
});
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
await main();
}

View File

@@ -0,0 +1,4 @@
// Tsx Name Repro script supports OpenClaw repository automation.
import "../../src/logging/subsystem.js";
console.log("tsx-name-repro: loaded logging/subsystem");