Skip to content

Commit f5dd276

Browse files
authored
fix(openclaw-plugin): use plugin-sdk exports for compaction delegation (fixes #833) openclaw ≥ v2026.3.22 (#1000)
* 增加过程错误信息 * fix(openclaw-plugin): delegate compaction via plugin-sdk exports
1 parent f0689e1 commit f5dd276

1 file changed

Lines changed: 109 additions & 23 deletions

File tree

examples/openclaw-plugin/context-engine.ts

Lines changed: 109 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ type CompactResult = {
4343
result?: unknown;
4444
};
4545

46+
type CompactDelegate = (arg: {
47+
sessionId: string;
48+
sessionFile: string;
49+
tokenBudget?: number;
50+
force?: boolean;
51+
currentTokenCount?: number;
52+
compactionTarget?: "budget" | "threshold";
53+
customInstructions?: string;
54+
runtimeContext?: Record<string, unknown>;
55+
}) => Promise<CompactResult>;
56+
4657
type ContextEngine = {
4758
info: ContextEngineInfo;
4859
ingest: (params: { sessionId: string; message: AgentMessage; isHeartbeat?: boolean }) => Promise<IngestResult>;
@@ -93,38 +104,113 @@ function estimateTokens(messages: AgentMessage[]): number {
93104
return Math.max(1, messages.length * 80);
94105
}
95106

96-
async function tryLegacyCompact(params: {
97-
sessionId: string;
98-
sessionFile: string;
99-
tokenBudget?: number;
100-
force?: boolean;
101-
currentTokenCount?: number;
102-
compactionTarget?: "budget" | "threshold";
103-
customInstructions?: string;
104-
runtimeContext?: Record<string, unknown>;
105-
}): Promise<CompactResult | null> {
107+
function describeError(err: unknown): string {
108+
if (err instanceof Error) {
109+
return err.message || err.name || String(err);
110+
}
111+
return String(err);
112+
}
113+
114+
function isModuleResolutionError(err: unknown): boolean {
115+
if (!(err instanceof Error)) {
116+
return false;
117+
}
118+
const message = `${err.name}: ${err.message}`.toLowerCase();
119+
return (
120+
message.includes("cannot find module") ||
121+
message.includes("module not found") ||
122+
message.includes("package path not exported") ||
123+
message.includes("is not defined by 'exports'") ||
124+
message.includes("unsupported dir import") ||
125+
message.includes("failed to resolve module specifier")
126+
);
127+
}
128+
129+
async function tryDelegatedCompact(
130+
params: {
131+
sessionId: string;
132+
sessionFile: string;
133+
tokenBudget?: number;
134+
force?: boolean;
135+
currentTokenCount?: number;
136+
compactionTarget?: "budget" | "threshold";
137+
customInstructions?: string;
138+
runtimeContext?: Record<string, unknown>;
139+
},
140+
logger: Logger,
141+
): Promise<CompactResult | null> {
142+
let delegateCompactionToRuntime: CompactDelegate | null;
143+
try {
144+
delegateCompactionToRuntime = await loadCompactDelegate(logger);
145+
} catch (err) {
146+
return {
147+
ok: false,
148+
compacted: false,
149+
reason: `delegate_compact_import_failed:${describeError(err)}`,
150+
};
151+
}
152+
153+
if (!delegateCompactionToRuntime) {
154+
if (cachedCompactUnavailableReason) {
155+
warnOrInfo(
156+
logger,
157+
`openviking: delegated compaction unavailable (${cachedCompactUnavailableReason})`,
158+
);
159+
}
160+
return null;
161+
}
162+
163+
try {
164+
return await delegateCompactionToRuntime(params);
165+
} catch (err) {
166+
logger.error(`openviking: delegated compaction failed: ${describeError(err)}`);
167+
return {
168+
ok: false,
169+
compacted: false,
170+
reason: `delegate_compact_failed:${describeError(err)}`,
171+
};
172+
}
173+
}
174+
175+
let cachedCompactDelegate: CompactDelegate | null | undefined;
176+
let cachedCompactUnavailableReason: string | undefined;
177+
178+
async function loadCompactDelegate(logger: Logger): Promise<CompactDelegate | null> {
179+
if (cachedCompactDelegate !== undefined) {
180+
return cachedCompactDelegate;
181+
}
182+
106183
const candidates = [
107-
"openclaw/context-engine/legacy",
108-
"openclaw/dist/context-engine/legacy.js",
184+
"openclaw/plugin-sdk/core",
185+
"openclaw/plugin-sdk",
109186
];
187+
const importErrors: string[] = [];
110188

111189
for (const path of candidates) {
112190
try {
113191
const mod = (await import(path)) as {
114-
LegacyContextEngine?: new () => {
115-
compact: (arg: typeof params) => Promise<CompactResult>;
116-
};
192+
delegateCompactionToRuntime?: CompactDelegate;
117193
};
118-
if (!mod?.LegacyContextEngine) {
194+
if (!mod?.delegateCompactionToRuntime) {
195+
importErrors.push(`${path}: delegateCompactionToRuntime export missing`);
119196
continue;
120197
}
121-
const legacy = new mod.LegacyContextEngine();
122-
return legacy.compact(params);
123-
} catch {
124-
// continue
198+
cachedCompactDelegate = mod.delegateCompactionToRuntime;
199+
cachedCompactUnavailableReason = undefined;
200+
return cachedCompactDelegate;
201+
} catch (err) {
202+
const detail = `${path}: ${describeError(err)}`;
203+
importErrors.push(detail);
204+
if (!isModuleResolutionError(err)) {
205+
logger.error(`openviking: delegated compaction import failed: ${detail}`);
206+
throw err;
207+
}
125208
}
126209
}
127210

211+
cachedCompactUnavailableReason =
212+
`failed to load compact delegate from candidates: ${importErrors.join(" | ")}`;
213+
cachedCompactDelegate = null;
128214
return null;
129215
}
130216

@@ -295,20 +381,20 @@ export function createMemoryOpenVikingContextEngine(params: {
295381
},
296382

297383
async compact(compactParams): Promise<CompactResult> {
298-
const delegated = await tryLegacyCompact(compactParams);
384+
const delegated = await tryDelegatedCompact(compactParams, logger);
299385
if (delegated) {
300386
return delegated;
301387
}
302388

303389
warnOrInfo(
304390
logger,
305-
"openviking: legacy compaction delegation unavailable; skipping compact",
391+
"openviking: delegated compaction unavailable; skipping compact",
306392
);
307393

308394
return {
309395
ok: true,
310396
compacted: false,
311-
reason: "legacy_compact_unavailable",
397+
reason: "delegate_compact_unavailable",
312398
};
313399
},
314400
};

0 commit comments

Comments
 (0)