@martian-engineering/lossless-claw 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.js +971 -0
- package/docs/configuration.md +15 -5
- package/openclaw.plugin.json +27 -3
- package/package.json +7 -6
- package/skills/lossless-claw/references/config.md +37 -0
- package/index.ts +0 -2
- package/src/assembler.ts +0 -1196
- package/src/compaction.ts +0 -1753
- package/src/db/config.ts +0 -345
- package/src/db/connection.ts +0 -151
- package/src/db/features.ts +0 -61
- package/src/db/migration.ts +0 -868
- package/src/engine.ts +0 -4486
- package/src/estimate-tokens.ts +0 -80
- package/src/expansion-auth.ts +0 -365
- package/src/expansion-policy.ts +0 -303
- package/src/expansion.ts +0 -383
- package/src/integrity.ts +0 -600
- package/src/large-files.ts +0 -546
- package/src/lcm-log.ts +0 -37
- package/src/openclaw-bridge.ts +0 -22
- package/src/plugin/index.ts +0 -2037
- package/src/plugin/lcm-command.ts +0 -1040
- package/src/plugin/lcm-doctor-apply.ts +0 -540
- package/src/plugin/lcm-doctor-cleaners.ts +0 -655
- package/src/plugin/lcm-doctor-shared.ts +0 -210
- package/src/plugin/shared-init.ts +0 -59
- package/src/prune.ts +0 -391
- package/src/retrieval.ts +0 -360
- package/src/session-patterns.ts +0 -23
- package/src/startup-banner-log.ts +0 -49
- package/src/store/compaction-telemetry-store.ts +0 -156
- package/src/store/conversation-store.ts +0 -929
- package/src/store/fts5-sanitize.ts +0 -50
- package/src/store/full-text-fallback.ts +0 -83
- package/src/store/full-text-sort.ts +0 -21
- package/src/store/index.ts +0 -39
- package/src/store/parse-utc-timestamp.ts +0 -25
- package/src/store/summary-store.ts +0 -1519
- package/src/summarize.ts +0 -1508
- package/src/tools/common.ts +0 -53
- package/src/tools/lcm-conversation-scope.ts +0 -127
- package/src/tools/lcm-describe-tool.ts +0 -245
- package/src/tools/lcm-expand-query-tool.ts +0 -1235
- package/src/tools/lcm-expand-tool.delegation.ts +0 -580
- package/src/tools/lcm-expand-tool.ts +0 -453
- package/src/tools/lcm-expansion-recursion-guard.ts +0 -373
- package/src/tools/lcm-grep-tool.ts +0 -228
- package/src/transaction-mutex.ts +0 -136
- package/src/transcript-repair.ts +0 -301
- package/src/types.ts +0 -165
package/src/transaction-mutex.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-database async transaction mutex.
|
|
3
|
-
*
|
|
4
|
-
* Hotfix for https://github.com/Martian-Engineering/lossless-claw/issues/260
|
|
5
|
-
*
|
|
6
|
-
* Problem: Multiple async operations (from different sessions) share one
|
|
7
|
-
* synchronous DatabaseSync handle. SQLite does not support nested transactions.
|
|
8
|
-
* When two async code paths both try to BEGIN while an earlier BEGIN is still
|
|
9
|
-
* in-flight (awaiting async work inside the transaction), the second BEGIN
|
|
10
|
-
* fails with "cannot start a transaction within a transaction".
|
|
11
|
-
*
|
|
12
|
-
* Solution: A per-database async mutex that serializes all explicit transaction
|
|
13
|
-
* entry points. Uses a WeakMap keyed on the DatabaseSync instance so each
|
|
14
|
-
* database gets its own queue, and databases are garbage-collected normally.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
|
-
import type { DatabaseSync } from "node:sqlite";
|
|
19
|
-
|
|
20
|
-
interface MutexState {
|
|
21
|
-
/** Tail of the promise chain — each acquirer appends to this. */
|
|
22
|
-
tail: Promise<void>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const mutexMap = new WeakMap<DatabaseSync, MutexState>();
|
|
26
|
-
const heldLockContext = new AsyncLocalStorage<Map<DatabaseSync, number>>();
|
|
27
|
-
|
|
28
|
-
let nextSavepointId = 0;
|
|
29
|
-
|
|
30
|
-
function getOrCreateMutex(db: DatabaseSync): MutexState {
|
|
31
|
-
let state = mutexMap.get(db);
|
|
32
|
-
if (!state) {
|
|
33
|
-
state = { tail: Promise.resolve() };
|
|
34
|
-
mutexMap.set(db, state);
|
|
35
|
-
}
|
|
36
|
-
return state;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function getHeldLockDepth(db: DatabaseSync): number {
|
|
40
|
-
return heldLockContext.getStore()?.get(db) ?? 0;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function nextSavepointName(): string {
|
|
44
|
-
nextSavepointId += 1;
|
|
45
|
-
return `lcm_txn_savepoint_${nextSavepointId}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Acquire exclusive async access to the database for a transaction.
|
|
50
|
-
*
|
|
51
|
-
* Direct lock acquisition is intentionally low-level and non-reentrant.
|
|
52
|
-
* Callers that need nested transaction scopes should use
|
|
53
|
-
* `withDatabaseTransaction()`, which reuses the held lock and isolates nested
|
|
54
|
-
* work with SQLite savepoints.
|
|
55
|
-
*
|
|
56
|
-
* Usage:
|
|
57
|
-
* const release = await acquireTransactionLock(this.db);
|
|
58
|
-
* try {
|
|
59
|
-
* this.db.exec("BEGIN IMMEDIATE");
|
|
60
|
-
* // ... do work ...
|
|
61
|
-
* this.db.exec("COMMIT");
|
|
62
|
-
* } catch (err) {
|
|
63
|
-
* this.db.exec("ROLLBACK");
|
|
64
|
-
* throw err;
|
|
65
|
-
* } finally {
|
|
66
|
-
* release();
|
|
67
|
-
* }
|
|
68
|
-
*
|
|
69
|
-
* Returns a release function that MUST be called in a finally block.
|
|
70
|
-
*/
|
|
71
|
-
export function acquireTransactionLock(db: DatabaseSync): Promise<() => void> {
|
|
72
|
-
const mutex = getOrCreateMutex(db);
|
|
73
|
-
|
|
74
|
-
let releaseResolve!: () => void;
|
|
75
|
-
const releasePromise = new Promise<void>((resolve) => {
|
|
76
|
-
releaseResolve = resolve;
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Capture the current tail — we wait on it
|
|
80
|
-
const waitOn = mutex.tail;
|
|
81
|
-
|
|
82
|
-
// Advance the tail — next acquirer will wait on our release
|
|
83
|
-
mutex.tail = releasePromise;
|
|
84
|
-
|
|
85
|
-
// Wait for the previous holder to release, then return our release fn
|
|
86
|
-
return waitOn.then(() => releaseResolve);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export type BeginTransactionStatement = "BEGIN" | "BEGIN IMMEDIATE";
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Run an operation inside a serialized database transaction.
|
|
93
|
-
*
|
|
94
|
-
* The first scope on an async path acquires the per-database mutex and opens
|
|
95
|
-
* the requested transaction mode. Nested scopes on the same database reuse the
|
|
96
|
-
* held lock and isolate their work with a savepoint instead of hanging.
|
|
97
|
-
*/
|
|
98
|
-
export async function withDatabaseTransaction<T>(
|
|
99
|
-
db: DatabaseSync,
|
|
100
|
-
beginStatement: BeginTransactionStatement,
|
|
101
|
-
operation: () => Promise<T> | T,
|
|
102
|
-
): Promise<T> {
|
|
103
|
-
if (getHeldLockDepth(db) > 0) {
|
|
104
|
-
const savepointName = nextSavepointName();
|
|
105
|
-
db.exec(`SAVEPOINT ${savepointName}`);
|
|
106
|
-
try {
|
|
107
|
-
const result = await operation();
|
|
108
|
-
db.exec(`RELEASE SAVEPOINT ${savepointName}`);
|
|
109
|
-
return result;
|
|
110
|
-
} catch (error) {
|
|
111
|
-
db.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`);
|
|
112
|
-
db.exec(`RELEASE SAVEPOINT ${savepointName}`);
|
|
113
|
-
throw error;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const release = await acquireTransactionLock(db);
|
|
118
|
-
try {
|
|
119
|
-
const heldLocks = new Map(heldLockContext.getStore() ?? []);
|
|
120
|
-
heldLocks.set(db, (heldLocks.get(db) ?? 0) + 1);
|
|
121
|
-
|
|
122
|
-
return await heldLockContext.run(heldLocks, async () => {
|
|
123
|
-
db.exec(beginStatement);
|
|
124
|
-
try {
|
|
125
|
-
const result = await operation();
|
|
126
|
-
db.exec("COMMIT");
|
|
127
|
-
return result;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
db.exec("ROLLBACK");
|
|
130
|
-
throw error;
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
} finally {
|
|
134
|
-
release();
|
|
135
|
-
}
|
|
136
|
-
}
|
package/src/transcript-repair.ts
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool use/result pairing repair for assembled context.
|
|
3
|
-
*
|
|
4
|
-
* Copied from openclaw core (src/agents/session-transcript-repair.ts +
|
|
5
|
-
* src/agents/tool-call-id.ts) to avoid depending on unexported internals.
|
|
6
|
-
* When the plugin SDK exports sanitizeToolUseResultPairing, this file can
|
|
7
|
-
* be removed in favor of the SDK import.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// -- Types (minimal, matching AgentMessage shape) --
|
|
11
|
-
|
|
12
|
-
type AgentMessageLike = {
|
|
13
|
-
role: string;
|
|
14
|
-
content?: unknown;
|
|
15
|
-
toolCallId?: string;
|
|
16
|
-
toolUseId?: string;
|
|
17
|
-
toolName?: string;
|
|
18
|
-
stopReason?: string;
|
|
19
|
-
isError?: boolean;
|
|
20
|
-
timestamp?: number;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
type ToolCallLike = {
|
|
24
|
-
id: string;
|
|
25
|
-
name?: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// -- Extraction helpers (from tool-call-id.ts) --
|
|
29
|
-
|
|
30
|
-
const TOOL_CALL_TYPES = new Set([
|
|
31
|
-
"toolCall",
|
|
32
|
-
"toolUse",
|
|
33
|
-
"tool_use",
|
|
34
|
-
"tool-use",
|
|
35
|
-
"functionCall",
|
|
36
|
-
"function_call",
|
|
37
|
-
]);
|
|
38
|
-
const OPENAI_FUNCTION_CALL_TYPES = new Set(["functionCall", "function_call"]);
|
|
39
|
-
|
|
40
|
-
function extractToolCallId(block: { id?: unknown; call_id?: unknown }): string | null {
|
|
41
|
-
if (typeof block.id === "string" && block.id) {
|
|
42
|
-
return block.id;
|
|
43
|
-
}
|
|
44
|
-
if (typeof block.call_id === "string" && block.call_id) {
|
|
45
|
-
return block.call_id;
|
|
46
|
-
}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function normalizeAssistantReasoningBlocks<T extends AgentMessageLike>(message: T): T {
|
|
51
|
-
if (!Array.isArray(message.content)) {
|
|
52
|
-
return message;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let sawToolCall = false;
|
|
56
|
-
let reasoningAfterToolCall = false;
|
|
57
|
-
let functionCallCount = 0;
|
|
58
|
-
|
|
59
|
-
for (const block of message.content) {
|
|
60
|
-
if (!block || typeof block !== "object") {
|
|
61
|
-
return message;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const type = (block as { type?: unknown }).type;
|
|
65
|
-
if (type === "reasoning" || type === "thinking") {
|
|
66
|
-
if (sawToolCall) {
|
|
67
|
-
reasoningAfterToolCall = true;
|
|
68
|
-
}
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (typeof type === "string" && TOOL_CALL_TYPES.has(type)) {
|
|
73
|
-
sawToolCall = true;
|
|
74
|
-
if (OPENAI_FUNCTION_CALL_TYPES.has(type)) {
|
|
75
|
-
functionCallCount += 1;
|
|
76
|
-
}
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return message;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Only repair the specific OpenAI shape we need: a single function call that
|
|
84
|
-
// has one or more reasoning blocks after it. Multi-call turns may use
|
|
85
|
-
// interleaved reasoning intentionally, so leave them untouched.
|
|
86
|
-
if (!reasoningAfterToolCall || functionCallCount !== 1) {
|
|
87
|
-
return message;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const reasoning = message.content.filter((block) => {
|
|
91
|
-
const type = (block as { type?: unknown }).type;
|
|
92
|
-
return type === "reasoning" || type === "thinking";
|
|
93
|
-
});
|
|
94
|
-
const toolCalls = message.content.filter((block) => {
|
|
95
|
-
const type = (block as { type?: unknown }).type;
|
|
96
|
-
return typeof type === "string" && TOOL_CALL_TYPES.has(type);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
...message,
|
|
101
|
-
content: [...reasoning, ...toolCalls],
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function extractToolCallsFromAssistant(msg: AgentMessageLike): ToolCallLike[] {
|
|
106
|
-
const content = msg.content;
|
|
107
|
-
if (!Array.isArray(content)) {
|
|
108
|
-
return [];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const toolCalls: ToolCallLike[] = [];
|
|
112
|
-
for (const block of content) {
|
|
113
|
-
if (!block || typeof block !== "object") {
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
const rec = block as { type?: unknown; id?: unknown; call_id?: unknown; name?: unknown };
|
|
117
|
-
const id = extractToolCallId(rec);
|
|
118
|
-
if (!id) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (typeof rec.type === "string" && TOOL_CALL_TYPES.has(rec.type)) {
|
|
122
|
-
toolCalls.push({
|
|
123
|
-
id,
|
|
124
|
-
name: typeof rec.name === "string" ? rec.name : undefined,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return toolCalls;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function extractToolResultId(msg: AgentMessageLike): string | null {
|
|
132
|
-
if (typeof msg.toolCallId === "string" && msg.toolCallId) {
|
|
133
|
-
return msg.toolCallId;
|
|
134
|
-
}
|
|
135
|
-
if (typeof msg.toolUseId === "string" && msg.toolUseId) {
|
|
136
|
-
return msg.toolUseId;
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// -- Repair logic (from session-transcript-repair.ts) --
|
|
142
|
-
|
|
143
|
-
function makeMissingToolResult(params: {
|
|
144
|
-
toolCallId: string;
|
|
145
|
-
toolName?: string;
|
|
146
|
-
}): AgentMessageLike {
|
|
147
|
-
return {
|
|
148
|
-
role: "toolResult",
|
|
149
|
-
toolCallId: params.toolCallId,
|
|
150
|
-
toolName: params.toolName ?? "unknown",
|
|
151
|
-
content: [
|
|
152
|
-
{
|
|
153
|
-
type: "text",
|
|
154
|
-
text: "[lossless-claw] missing tool result in session history; inserted synthetic error result for transcript repair.",
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
isError: true,
|
|
158
|
-
timestamp: Date.now(),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Repair tool use/result pairing in an assembled message transcript.
|
|
164
|
-
*
|
|
165
|
-
* Anthropic (and Cloud Code Assist) reject transcripts where assistant tool
|
|
166
|
-
* calls are not immediately followed by matching tool results. This function:
|
|
167
|
-
* - Moves matching toolResult messages directly after their assistant toolCall turn
|
|
168
|
-
* - Inserts synthetic error toolResults for missing IDs
|
|
169
|
-
* - Drops duplicate toolResults for the same ID
|
|
170
|
-
* - Drops orphaned toolResults with no matching tool call
|
|
171
|
-
*/
|
|
172
|
-
export function sanitizeToolUseResultPairing<T extends AgentMessageLike>(messages: T[]): T[] {
|
|
173
|
-
const out: T[] = [];
|
|
174
|
-
const seenToolResultIds = new Set<string>();
|
|
175
|
-
let droppedDuplicateCount = 0;
|
|
176
|
-
let droppedOrphanCount = 0;
|
|
177
|
-
let moved = false;
|
|
178
|
-
let changed = false;
|
|
179
|
-
|
|
180
|
-
const pushToolResult = (msg: T) => {
|
|
181
|
-
const id = extractToolResultId(msg);
|
|
182
|
-
if (id && seenToolResultIds.has(id)) {
|
|
183
|
-
droppedDuplicateCount += 1;
|
|
184
|
-
changed = true;
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
if (id) {
|
|
188
|
-
seenToolResultIds.add(id);
|
|
189
|
-
}
|
|
190
|
-
out.push(msg);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
for (let i = 0; i < messages.length; i += 1) {
|
|
194
|
-
const msg = messages[i];
|
|
195
|
-
if (!msg || typeof msg !== "object") {
|
|
196
|
-
out.push(msg);
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const role = msg.role;
|
|
201
|
-
if (role !== "assistant") {
|
|
202
|
-
if (role !== "toolResult") {
|
|
203
|
-
out.push(msg);
|
|
204
|
-
} else {
|
|
205
|
-
droppedOrphanCount += 1;
|
|
206
|
-
changed = true;
|
|
207
|
-
}
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const normalizedAssistant = normalizeAssistantReasoningBlocks(msg);
|
|
212
|
-
if (normalizedAssistant !== msg) {
|
|
213
|
-
changed = true;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Skip tool call extraction for aborted or errored assistant messages.
|
|
217
|
-
// When stopReason is "error" or "aborted", the tool_use blocks may be incomplete
|
|
218
|
-
// and should not have synthetic tool_results created.
|
|
219
|
-
const stopReason = normalizedAssistant.stopReason;
|
|
220
|
-
if (stopReason === "error" || stopReason === "aborted") {
|
|
221
|
-
out.push(normalizedAssistant as T);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const toolCalls = extractToolCallsFromAssistant(normalizedAssistant);
|
|
226
|
-
if (toolCalls.length === 0) {
|
|
227
|
-
out.push(normalizedAssistant as T);
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const toolCallIds = new Set(toolCalls.map((t) => t.id));
|
|
232
|
-
|
|
233
|
-
const spanResultsById = new Map<string, T>();
|
|
234
|
-
const remainder: T[] = [];
|
|
235
|
-
|
|
236
|
-
let j = i + 1;
|
|
237
|
-
for (; j < messages.length; j += 1) {
|
|
238
|
-
const next = messages[j];
|
|
239
|
-
if (!next || typeof next !== "object") {
|
|
240
|
-
remainder.push(next);
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const nextRole = next.role;
|
|
245
|
-
if (nextRole === "assistant") {
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (nextRole === "toolResult") {
|
|
250
|
-
const id = extractToolResultId(next);
|
|
251
|
-
if (id && toolCallIds.has(id)) {
|
|
252
|
-
if (seenToolResultIds.has(id)) {
|
|
253
|
-
droppedDuplicateCount += 1;
|
|
254
|
-
changed = true;
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
if (!spanResultsById.has(id)) {
|
|
258
|
-
spanResultsById.set(id, next);
|
|
259
|
-
}
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (next.role !== "toolResult") {
|
|
265
|
-
remainder.push(next);
|
|
266
|
-
} else {
|
|
267
|
-
droppedOrphanCount += 1;
|
|
268
|
-
changed = true;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
out.push(normalizedAssistant as T);
|
|
273
|
-
|
|
274
|
-
if (spanResultsById.size > 0 && remainder.length > 0) {
|
|
275
|
-
moved = true;
|
|
276
|
-
changed = true;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
for (const call of toolCalls) {
|
|
280
|
-
const existing = spanResultsById.get(call.id);
|
|
281
|
-
if (existing) {
|
|
282
|
-
pushToolResult(existing);
|
|
283
|
-
} else {
|
|
284
|
-
const missing = makeMissingToolResult({
|
|
285
|
-
toolCallId: call.id,
|
|
286
|
-
toolName: call.name,
|
|
287
|
-
});
|
|
288
|
-
changed = true;
|
|
289
|
-
pushToolResult(missing as T);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
for (const rem of remainder) {
|
|
294
|
-
out.push(rem);
|
|
295
|
-
}
|
|
296
|
-
i = j - 1;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const changedOrMoved = changed || moved;
|
|
300
|
-
return changedOrMoved ? out : messages;
|
|
301
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core type definitions for the LCM plugin.
|
|
3
|
-
*
|
|
4
|
-
* These types define the contracts between LCM and OpenClaw core,
|
|
5
|
-
* abstracting away direct imports from core internals.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { LcmConfig } from "./db/config.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Minimal LLM completion interface needed by LCM for summarization.
|
|
12
|
-
* Matches the signature of completeSimple from @mariozechner/pi-ai.
|
|
13
|
-
*/
|
|
14
|
-
export type CompletionContentBlock = {
|
|
15
|
-
type: string;
|
|
16
|
-
text?: string;
|
|
17
|
-
[key: string]: unknown;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type CompletionErrorInfo = {
|
|
21
|
-
kind?: string;
|
|
22
|
-
message?: string;
|
|
23
|
-
code?: string;
|
|
24
|
-
statusCode?: number;
|
|
25
|
-
[key: string]: unknown;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type CompletionResult = {
|
|
29
|
-
content: CompletionContentBlock[];
|
|
30
|
-
error?: CompletionErrorInfo;
|
|
31
|
-
[key: string]: unknown;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type CompleteFn = (params: {
|
|
35
|
-
provider?: string;
|
|
36
|
-
model: string;
|
|
37
|
-
apiKey?: string;
|
|
38
|
-
providerApi?: string;
|
|
39
|
-
authProfileId?: string;
|
|
40
|
-
agentDir?: string;
|
|
41
|
-
runtimeConfig?: unknown;
|
|
42
|
-
skipModelAuth?: boolean;
|
|
43
|
-
messages: Array<{ role: string; content: unknown }>;
|
|
44
|
-
system?: string;
|
|
45
|
-
maxTokens: number;
|
|
46
|
-
temperature?: number;
|
|
47
|
-
reasoning?: string;
|
|
48
|
-
}) => Promise<CompletionResult>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Gateway RPC call interface.
|
|
52
|
-
*/
|
|
53
|
-
export type CallGatewayFn = (params: {
|
|
54
|
-
method: string;
|
|
55
|
-
params?: Record<string, unknown>;
|
|
56
|
-
timeoutMs?: number;
|
|
57
|
-
}) => Promise<unknown>;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Model resolution function — resolves model aliases and defaults.
|
|
61
|
-
* When providerHint is supplied, it takes precedence over env/defaults.
|
|
62
|
-
*/
|
|
63
|
-
export type ResolveModelFn = (modelRef?: string, providerHint?: string) => {
|
|
64
|
-
provider: string;
|
|
65
|
-
model: string;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* API key resolution function.
|
|
70
|
-
*/
|
|
71
|
-
export type ApiKeyLookupOptions = {
|
|
72
|
-
profileId?: string;
|
|
73
|
-
preferredProfile?: string;
|
|
74
|
-
agentDir?: string;
|
|
75
|
-
runtimeConfig?: unknown;
|
|
76
|
-
skipModelAuth?: boolean;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export type GetApiKeyFn = (
|
|
80
|
-
provider: string,
|
|
81
|
-
model: string,
|
|
82
|
-
options?: ApiKeyLookupOptions,
|
|
83
|
-
) => Promise<string | undefined>;
|
|
84
|
-
|
|
85
|
-
export type RequireApiKeyFn = (
|
|
86
|
-
provider: string,
|
|
87
|
-
model: string,
|
|
88
|
-
options?: ApiKeyLookupOptions,
|
|
89
|
-
) => Promise<string>;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Session key utilities.
|
|
93
|
-
*/
|
|
94
|
-
export type ParseAgentSessionKeyFn = (sessionKey: string) => {
|
|
95
|
-
agentId: string;
|
|
96
|
-
suffix: string;
|
|
97
|
-
} | null;
|
|
98
|
-
|
|
99
|
-
export type IsSubagentSessionKeyFn = (sessionKey: string) => boolean;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Dependencies injected into the LCM engine at registration time.
|
|
103
|
-
* These replace all direct imports from OpenClaw core.
|
|
104
|
-
*/
|
|
105
|
-
export interface LcmDependencies {
|
|
106
|
-
/** LCM configuration (from env vars + plugin config) */
|
|
107
|
-
config: LcmConfig;
|
|
108
|
-
|
|
109
|
-
/** LLM completion function for summarization */
|
|
110
|
-
complete: CompleteFn;
|
|
111
|
-
|
|
112
|
-
/** Whether a provider uses runtime-managed OAuth / auth profiles instead of direct API keys. */
|
|
113
|
-
isRuntimeManagedAuthProvider?: (provider: string, providerApi?: string) => boolean;
|
|
114
|
-
|
|
115
|
-
/** Gateway RPC call function (for subagent spawning, session ops) */
|
|
116
|
-
callGateway: CallGatewayFn;
|
|
117
|
-
|
|
118
|
-
/** Resolve model alias to provider/model pair */
|
|
119
|
-
resolveModel: ResolveModelFn;
|
|
120
|
-
|
|
121
|
-
/** Get API key for a provider/model pair */
|
|
122
|
-
getApiKey: GetApiKeyFn;
|
|
123
|
-
|
|
124
|
-
/** Require API key (throws if missing) */
|
|
125
|
-
requireApiKey: RequireApiKeyFn;
|
|
126
|
-
|
|
127
|
-
/** Parse agent session key into components */
|
|
128
|
-
parseAgentSessionKey: ParseAgentSessionKeyFn;
|
|
129
|
-
|
|
130
|
-
/** Check if a session key is a subagent key */
|
|
131
|
-
isSubagentSessionKey: IsSubagentSessionKeyFn;
|
|
132
|
-
|
|
133
|
-
/** Normalize an agent ID */
|
|
134
|
-
normalizeAgentId: (id?: string) => string;
|
|
135
|
-
|
|
136
|
-
/** Build system prompt for subagent sessions */
|
|
137
|
-
buildSubagentSystemPrompt: (params: {
|
|
138
|
-
depth: number;
|
|
139
|
-
maxDepth: number;
|
|
140
|
-
taskSummary?: string;
|
|
141
|
-
}) => string;
|
|
142
|
-
|
|
143
|
-
/** Read the latest assistant reply from a session's messages */
|
|
144
|
-
readLatestAssistantReply: (messages: unknown[]) => string | undefined;
|
|
145
|
-
|
|
146
|
-
/** Sanitize tool use/result pairing in message arrays */
|
|
147
|
-
// sanitizeToolUseResultPairing removed — now imported directly in assembler from transcript-repair.ts
|
|
148
|
-
|
|
149
|
-
/** Resolve the OpenClaw agent directory */
|
|
150
|
-
resolveAgentDir: () => string;
|
|
151
|
-
|
|
152
|
-
/** Resolve runtime session id from an agent session key */
|
|
153
|
-
resolveSessionIdFromSessionKey: (sessionKey: string) => Promise<string | undefined>;
|
|
154
|
-
|
|
155
|
-
/** Agent lane constant for subagents */
|
|
156
|
-
agentLaneSubagent: string;
|
|
157
|
-
|
|
158
|
-
/** Logger */
|
|
159
|
-
log: {
|
|
160
|
-
info: (msg: string) => void;
|
|
161
|
-
warn: (msg: string) => void;
|
|
162
|
-
error: (msg: string) => void;
|
|
163
|
-
debug: (msg: string) => void;
|
|
164
|
-
};
|
|
165
|
-
}
|