@mobcode/openclaw-plugin 0.1.7 → 0.1.10
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/package.json
CHANGED
|
@@ -1,92 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
async function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function loadSessionUtilsRuntime() {
|
|
12
|
-
return (
|
|
13
|
-
(await tryImport("../../openclaw/src/gateway/session-utils.js")) ??
|
|
14
|
-
(await tryImport("../openclaw/src/gateway/session-utils.js"))
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function readSessionTranscriptMessagesFromInternals(sessionKey) {
|
|
19
|
-
const runtime = await loadSessionUtilsRuntime();
|
|
20
|
-
if (!runtime?.loadSessionEntry || !runtime?.readSessionMessages) {
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const normalizedSessionKey = String(sessionKey ?? "").trim();
|
|
25
|
-
if (!normalizedSessionKey) {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const loaded = runtime.loadSessionEntry(normalizedSessionKey);
|
|
30
|
-
const entry = loaded?.entry;
|
|
31
|
-
const storePath = loaded?.storePath;
|
|
32
|
-
const sessionId =
|
|
33
|
-
typeof entry?.sessionId === "string" && entry.sessionId.trim()
|
|
34
|
-
? entry.sessionId.trim()
|
|
35
|
-
: "";
|
|
36
|
-
if (!sessionId) {
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const rawMessages = runtime.readSessionMessages(sessionId, storePath, entry?.sessionFile);
|
|
41
|
-
return Array.isArray(rawMessages) ? rawMessages : [];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function readSessionMessagesThroughChatHistory(config, sessionKey, logger) {
|
|
45
|
-
const normalizedSessionKey = String(sessionKey ?? "").trim();
|
|
46
|
-
if (!normalizedSessionKey) {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
logger?.info?.(
|
|
50
|
-
`[mobcode-history] chat.history request session=${normalizedSessionKey} limit=1000`,
|
|
51
|
-
);
|
|
52
|
-
const payload = await requestThroughOperatorApprovalsGateway({
|
|
53
|
-
config,
|
|
54
|
-
method: "chat.history",
|
|
55
|
-
requestParams: {
|
|
56
|
-
sessionKey: normalizedSessionKey,
|
|
57
|
-
limit: 1000,
|
|
58
|
-
},
|
|
59
|
-
clientDisplayName: "MobCode History Backfill",
|
|
1
|
+
import { readSessionMessagesFromRuntime } from "./session-transcript-runtime.js";
|
|
2
|
+
|
|
3
|
+
export async function readSessionTranscriptMessages(runtime, _config, sessionKey, logger) {
|
|
4
|
+
const runtimeMessages = await readSessionMessagesFromRuntime({
|
|
5
|
+
runtime,
|
|
6
|
+
config: runtime?.config?.loadConfig?.(),
|
|
7
|
+
sessionKey,
|
|
8
|
+
logger,
|
|
60
9
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`[mobcode-history] chat.history response session=${normalizedSessionKey} sessionId=${String(payload?.sessionId ?? "").trim() || "-"} thinkingLevel=${String(payload?.thinkingLevel ?? "").trim() || "-"} fastMode=${String(payload?.fastMode ?? "").trim() || "-"} verboseLevel=${String(payload?.verboseLevel ?? "").trim() || "-"} messages=${Array.isArray(messages) ? messages.length : 0}`,
|
|
64
|
-
);
|
|
65
|
-
return Array.isArray(messages) ? messages : [];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export async function readSessionTranscriptMessages(config, sessionKey, logger) {
|
|
69
|
-
try {
|
|
70
|
-
const gatewayMessages = await readSessionMessagesThroughChatHistory(
|
|
71
|
-
config,
|
|
72
|
-
sessionKey,
|
|
73
|
-
logger,
|
|
74
|
-
);
|
|
75
|
-
if (gatewayMessages.length > 0) {
|
|
76
|
-
return gatewayMessages;
|
|
77
|
-
}
|
|
78
|
-
} catch (error) {
|
|
79
|
-
logger?.warn?.(
|
|
80
|
-
`[mobcode-history] chat.history failed session=${String(sessionKey ?? "").trim()}: ${error instanceof Error ? error.message : String(error)}`,
|
|
81
|
-
);
|
|
82
|
-
// Fall back to adjacent-source runtime imports for development checkouts.
|
|
10
|
+
if (runtimeMessages.length > 0) {
|
|
11
|
+
return runtimeMessages;
|
|
83
12
|
}
|
|
84
13
|
logger?.info?.(
|
|
85
|
-
`[mobcode-history]
|
|
86
|
-
);
|
|
87
|
-
const internalMessages = await readSessionTranscriptMessagesFromInternals(sessionKey);
|
|
88
|
-
logger?.info?.(
|
|
89
|
-
`[mobcode-history] internal fallback session=${String(sessionKey ?? "").trim()} messages=${internalMessages.length}`,
|
|
14
|
+
`[mobcode-history] no transcript history resolved session=${String(sessionKey ?? "").trim()}`,
|
|
90
15
|
);
|
|
91
|
-
return
|
|
16
|
+
return [];
|
|
92
17
|
}
|
package/src/plugin-definition.js
CHANGED
|
@@ -37,6 +37,7 @@ export function createMobcodePluginDefinition() {
|
|
|
37
37
|
ensureSessionMessages: async (sessionKey) => {
|
|
38
38
|
await store.ensureSessionIndexed(sessionKey, async () =>
|
|
39
39
|
readSessionTranscriptMessages(
|
|
40
|
+
api.runtime,
|
|
40
41
|
api.runtime.config.loadConfig(),
|
|
41
42
|
sessionKey,
|
|
42
43
|
api.logger,
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function trim(value) {
|
|
5
|
+
return typeof value === "string" ? value.trim() : "";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function parseAgentId(sessionKey, config) {
|
|
9
|
+
const raw = trim(sessionKey).toLowerCase();
|
|
10
|
+
if (raw.startsWith("agent:")) {
|
|
11
|
+
const parts = raw.split(":");
|
|
12
|
+
if (parts.length >= 3 && parts[1]) {
|
|
13
|
+
return parts[1];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const configuredAgents = Array.isArray(config?.agents?.list) ? config.agents.list : [];
|
|
17
|
+
const defaultAgentId =
|
|
18
|
+
configuredAgents.find((agent) => agent?.default)?.id ??
|
|
19
|
+
configuredAgents[0]?.id ??
|
|
20
|
+
"main";
|
|
21
|
+
return trim(defaultAgentId) || "main";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveStoreCandidates(runtime, config, sessionKey) {
|
|
25
|
+
const parsedAgentId = parseAgentId(sessionKey, config);
|
|
26
|
+
const configuredAgents = Array.isArray(config?.agents?.list) ? config.agents.list : [];
|
|
27
|
+
const ids = new Set([parsedAgentId]);
|
|
28
|
+
for (const agent of configuredAgents) {
|
|
29
|
+
const id = trim(agent?.id);
|
|
30
|
+
if (id) {
|
|
31
|
+
ids.add(id);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const candidates = [];
|
|
35
|
+
for (const agentId of ids) {
|
|
36
|
+
try {
|
|
37
|
+
const storePath = runtime.agent.session.resolveStorePath(config?.session?.store, { agentId });
|
|
38
|
+
if (trim(storePath)) {
|
|
39
|
+
candidates.push({
|
|
40
|
+
agentId,
|
|
41
|
+
storePath,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// Ignore invalid store path resolutions and continue scanning other agents.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return candidates;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function attachTranscriptMeta(message, meta) {
|
|
52
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
53
|
+
return message;
|
|
54
|
+
}
|
|
55
|
+
const record = message;
|
|
56
|
+
const existing =
|
|
57
|
+
record.__openclaw && typeof record.__openclaw === "object" && !Array.isArray(record.__openclaw)
|
|
58
|
+
? record.__openclaw
|
|
59
|
+
: {};
|
|
60
|
+
return {
|
|
61
|
+
...record,
|
|
62
|
+
__openclaw: {
|
|
63
|
+
...existing,
|
|
64
|
+
...meta,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function readTranscriptMessages(filePath) {
|
|
70
|
+
if (!trim(filePath) || !fs.existsSync(filePath)) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const lines = fs.readFileSync(filePath, "utf-8").split(/\r?\n/);
|
|
74
|
+
const messages = [];
|
|
75
|
+
let seq = 0;
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
if (!trim(line)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(line);
|
|
82
|
+
if (parsed?.message) {
|
|
83
|
+
seq += 1;
|
|
84
|
+
messages.push(
|
|
85
|
+
attachTranscriptMeta(parsed.message, {
|
|
86
|
+
...(trim(parsed?.id) ? { id: trim(parsed.id) } : {}),
|
|
87
|
+
seq,
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (parsed?.type === "compaction") {
|
|
93
|
+
seq += 1;
|
|
94
|
+
messages.push({
|
|
95
|
+
role: "system",
|
|
96
|
+
content: [{ type: "text", text: "Compaction" }],
|
|
97
|
+
__openclaw: {
|
|
98
|
+
kind: "compaction",
|
|
99
|
+
...(trim(parsed?.id) ? { id: trim(parsed.id) } : {}),
|
|
100
|
+
seq,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// Ignore malformed transcript lines.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return messages;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function readSessionMessagesFromRuntime({ runtime, config, sessionKey, logger }) {
|
|
112
|
+
const normalizedSessionKey = trim(sessionKey);
|
|
113
|
+
if (!normalizedSessionKey || !runtime?.agent?.session) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const candidates = resolveStoreCandidates(runtime, config, normalizedSessionKey);
|
|
117
|
+
logger?.info?.(
|
|
118
|
+
`[mobcode-history] runtime reader start session=${normalizedSessionKey} storeCandidates=${candidates.map((candidate) => `${candidate.agentId}:${candidate.storePath}`).join(",") || "-"}`,
|
|
119
|
+
);
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
try {
|
|
122
|
+
const store = runtime.agent.session.loadSessionStore(candidate.storePath, {
|
|
123
|
+
skipCache: true,
|
|
124
|
+
});
|
|
125
|
+
const entry = store?.[normalizedSessionKey];
|
|
126
|
+
if (!entry) {
|
|
127
|
+
logger?.info?.(
|
|
128
|
+
`[mobcode-history] runtime reader miss session=${normalizedSessionKey} agentId=${candidate.agentId} storePath=${candidate.storePath}`,
|
|
129
|
+
);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const sessionId = trim(entry?.sessionId);
|
|
133
|
+
if (!sessionId) {
|
|
134
|
+
logger?.warn?.(
|
|
135
|
+
`[mobcode-history] runtime reader invalid_entry session=${normalizedSessionKey} agentId=${candidate.agentId} storePath=${candidate.storePath} reason=missing_session_id`,
|
|
136
|
+
);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const transcriptFile = trim(entry?.sessionFile)
|
|
140
|
+
? path.resolve(trim(entry.sessionFile))
|
|
141
|
+
: runtime.agent.session.resolveSessionFilePath(sessionId, entry, {
|
|
142
|
+
sessionsDir: path.dirname(candidate.storePath),
|
|
143
|
+
agentId: candidate.agentId,
|
|
144
|
+
});
|
|
145
|
+
const messages = readTranscriptMessages(transcriptFile);
|
|
146
|
+
logger?.info?.(
|
|
147
|
+
`[mobcode-history] runtime reader hit session=${normalizedSessionKey} agentId=${candidate.agentId} storePath=${candidate.storePath} transcriptFile=${transcriptFile} messages=${messages.length}`,
|
|
148
|
+
);
|
|
149
|
+
return messages;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
logger?.warn?.(
|
|
152
|
+
`[mobcode-history] runtime reader error session=${normalizedSessionKey} agentId=${candidate.agentId} storePath=${candidate.storePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
logger?.warn?.(
|
|
157
|
+
`[mobcode-history] runtime reader unresolved session=${normalizedSessionKey}`,
|
|
158
|
+
);
|
|
159
|
+
return [];
|
|
160
|
+
}
|
package/src/state-store.js
CHANGED
|
@@ -811,17 +811,23 @@ export class MobcodeStateStore {
|
|
|
811
811
|
LIMIT 1`,
|
|
812
812
|
)
|
|
813
813
|
.get(normalizedSessionKey);
|
|
814
|
+
const messageCountRow = this._db()
|
|
815
|
+
.prepare(`SELECT COUNT(*) AS count FROM messages WHERE session_key=?`)
|
|
816
|
+
.get(normalizedSessionKey);
|
|
817
|
+
const messageRows = Number(messageCountRow?.count ?? 0);
|
|
814
818
|
const alreadyBackfilled =
|
|
815
819
|
typeof existing?.last_backfill_at === "string" && existing.last_backfill_at.trim().length > 0;
|
|
816
|
-
if (alreadyBackfilled) {
|
|
817
|
-
const messageCountRow = this._db()
|
|
818
|
-
.prepare(`SELECT COUNT(*) AS count FROM messages WHERE session_key=?`)
|
|
819
|
-
.get(normalizedSessionKey);
|
|
820
|
+
if (alreadyBackfilled && messageRows > 0) {
|
|
820
821
|
this.logger?.info?.(
|
|
821
|
-
`[mobcode-store] ensureSessionIndexed skip session=${normalizedSessionKey} reason=already_backfilled lastBackfillAt=${existing?.last_backfill_at ?? "-"} messageRows=${
|
|
822
|
+
`[mobcode-store] ensureSessionIndexed skip session=${normalizedSessionKey} reason=already_backfilled lastBackfillAt=${existing?.last_backfill_at ?? "-"} messageRows=${messageRows}`,
|
|
822
823
|
);
|
|
823
824
|
return;
|
|
824
825
|
}
|
|
826
|
+
if (alreadyBackfilled && messageRows <= 0) {
|
|
827
|
+
this.logger?.warn?.(
|
|
828
|
+
`[mobcode-store] ensureSessionIndexed retry session=${normalizedSessionKey} reason=stale_backfill_marker lastBackfillAt=${existing?.last_backfill_at ?? "-"} messageRows=${messageRows}`,
|
|
829
|
+
);
|
|
830
|
+
}
|
|
825
831
|
this.logger?.info?.(
|
|
826
832
|
`[mobcode-store] ensureSessionIndexed start session=${normalizedSessionKey}`,
|
|
827
833
|
);
|