libp2p-mesh 2026.6.23 → 2026.6.24
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.
|
@@ -6,6 +6,7 @@ type Logger = {
|
|
|
6
6
|
warn?: (message: string) => void;
|
|
7
7
|
};
|
|
8
8
|
export declare function extractLatestAssistantText(messages: unknown[]): string | undefined;
|
|
9
|
+
export declare function extractCompletionText(result: unknown): string | undefined;
|
|
9
10
|
export declare function parseUserMdAttributeResponse(text: string): UserPublicAttribute[];
|
|
10
11
|
export declare function createOpenClawUserMdAttributeExtractor(api: OpenClawPluginApi, options?: {
|
|
11
12
|
timeoutMs?: number;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
2
1
|
import { validateExtractedUserMdTags } from "./user-md-agent-attributes.js";
|
|
3
2
|
const EXTRACTION_TIMEOUT_MS = 30000;
|
|
4
|
-
const SESSION_HASH_LENGTH = 16;
|
|
5
3
|
export const USER_MD_ATTRIBUTE_EXTRACTION_PROMPT = [
|
|
6
4
|
"你是 libp2p-mesh 的 USER.md 公开属性提取器。",
|
|
7
5
|
"",
|
|
@@ -16,9 +14,6 @@ export const USER_MD_ATTRIBUTE_EXTRACTION_PROMPT = [
|
|
|
16
14
|
"- 不要提取“刚认识”“还在了解”“随时告诉我”这类无分类价值内容。",
|
|
17
15
|
"- 优先提取稳定身份、技术方向、项目方向、长期偏好。",
|
|
18
16
|
].join("\n");
|
|
19
|
-
function hashForSessionKey(value) {
|
|
20
|
-
return createHash("sha256").update(value, "utf8").digest("hex").slice(0, SESSION_HASH_LENGTH);
|
|
21
|
-
}
|
|
22
17
|
function buildUserMdExtractionMessage(markdown, sourcePath) {
|
|
23
18
|
return [
|
|
24
19
|
`sourcePath: ${sourcePath}`,
|
|
@@ -75,6 +70,42 @@ export function extractLatestAssistantText(messages) {
|
|
|
75
70
|
}
|
|
76
71
|
return undefined;
|
|
77
72
|
}
|
|
73
|
+
export function extractCompletionText(result) {
|
|
74
|
+
if (typeof result === "string") {
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
if (!isRecord(result)) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const direct = extractTextFromContent(result.content) ??
|
|
81
|
+
(typeof result.text === "string" ? result.text : undefined) ??
|
|
82
|
+
(typeof result.outputText === "string" ? result.outputText : undefined) ??
|
|
83
|
+
(typeof result.message === "string" ? result.message : undefined);
|
|
84
|
+
if (direct?.trim()) {
|
|
85
|
+
return direct;
|
|
86
|
+
}
|
|
87
|
+
const choices = result.choices;
|
|
88
|
+
if (!Array.isArray(choices)) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
for (const choice of choices) {
|
|
92
|
+
if (!isRecord(choice)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const message = choice.message;
|
|
96
|
+
if (isRecord(message)) {
|
|
97
|
+
const text = extractTextFromContent(message.content);
|
|
98
|
+
if (text?.trim()) {
|
|
99
|
+
return text;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const text = extractTextFromContent(choice.content);
|
|
103
|
+
if (text?.trim()) {
|
|
104
|
+
return text;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
78
109
|
function stripJsonFence(text) {
|
|
79
110
|
const trimmed = text.trim();
|
|
80
111
|
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
@@ -91,32 +122,38 @@ export function parseUserMdAttributeResponse(text) {
|
|
|
91
122
|
function unavailable(reason) {
|
|
92
123
|
return { unavailable: true, reason };
|
|
93
124
|
}
|
|
125
|
+
function runtimeLlm(api) {
|
|
126
|
+
const runtime = api.runtime;
|
|
127
|
+
if (!isRecord(runtime)) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
const llm = runtime.llm;
|
|
131
|
+
if (!isRecord(llm) || typeof llm.complete !== "function") {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return llm;
|
|
135
|
+
}
|
|
94
136
|
export function createOpenClawUserMdAttributeExtractor(api, options) {
|
|
95
137
|
const timeoutMs = options?.timeoutMs ?? EXTRACTION_TIMEOUT_MS;
|
|
96
138
|
const logger = options?.logger ?? api.logger;
|
|
97
139
|
return {
|
|
98
140
|
async extract({ markdown, sourcePath }) {
|
|
99
|
-
const
|
|
100
|
-
if (!
|
|
101
|
-
return unavailable("OpenClaw
|
|
141
|
+
const llm = runtimeLlm(api);
|
|
142
|
+
if (!llm) {
|
|
143
|
+
return unavailable("OpenClaw runtime llm.complete is unavailable");
|
|
102
144
|
}
|
|
103
|
-
const sessionKey = `libp2p-mesh:user-md-attributes:${hashForSessionKey(sourcePath)}:${randomUUID()}`;
|
|
104
145
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
146
|
+
const result = await llm.complete({
|
|
147
|
+
messages: [
|
|
148
|
+
{ role: "system", content: USER_MD_ATTRIBUTE_EXTRACTION_PROMPT },
|
|
149
|
+
{ role: "user", content: buildUserMdExtractionMessage(markdown, sourcePath) },
|
|
150
|
+
],
|
|
151
|
+
purpose: "libp2p-mesh.user-md-attributes",
|
|
152
|
+
maxTokens: 512,
|
|
153
|
+
temperature: 0.1,
|
|
154
|
+
timeoutMs,
|
|
113
155
|
});
|
|
114
|
-
const
|
|
115
|
-
if (waitResult.status !== "ok") {
|
|
116
|
-
return unavailable(waitResult.error ?? `OpenClaw subagent extraction ${waitResult.status}`);
|
|
117
|
-
}
|
|
118
|
-
const { messages } = await subagent.getSessionMessages({ sessionKey, limit: 10 });
|
|
119
|
-
const text = extractLatestAssistantText(messages);
|
|
156
|
+
const text = extractCompletionText(result);
|
|
120
157
|
if (!text) {
|
|
121
158
|
return [];
|
|
122
159
|
}
|
|
@@ -127,9 +164,6 @@ export function createOpenClawUserMdAttributeExtractor(api, options) {
|
|
|
127
164
|
logger?.warn?.(`[libp2p-mesh] USER.md agent extraction failed: ${reason}`);
|
|
128
165
|
return unavailable(reason);
|
|
129
166
|
}
|
|
130
|
-
finally {
|
|
131
|
-
await subagent.deleteSession?.({ sessionKey, deleteTranscript: true }).catch(() => undefined);
|
|
132
|
-
}
|
|
133
167
|
},
|
|
134
168
|
};
|
|
135
169
|
}
|
package/package.json
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
2
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
3
2
|
import type { UserPublicAttribute } from "./types.js";
|
|
4
3
|
import type {
|
|
@@ -8,7 +7,6 @@ import type {
|
|
|
8
7
|
import { validateExtractedUserMdTags } from "./user-md-agent-attributes.js";
|
|
9
8
|
|
|
10
9
|
const EXTRACTION_TIMEOUT_MS = 30000;
|
|
11
|
-
const SESSION_HASH_LENGTH = 16;
|
|
12
10
|
|
|
13
11
|
export const USER_MD_ATTRIBUTE_EXTRACTION_PROMPT = [
|
|
14
12
|
"你是 libp2p-mesh 的 USER.md 公开属性提取器。",
|
|
@@ -29,9 +27,15 @@ type Logger = {
|
|
|
29
27
|
warn?: (message: string) => void;
|
|
30
28
|
};
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
30
|
+
type RuntimeLlmComplete = {
|
|
31
|
+
complete(request: {
|
|
32
|
+
messages: Array<{ role: "system" | "user" | "assistant"; content: string }>;
|
|
33
|
+
purpose: string;
|
|
34
|
+
maxTokens?: number;
|
|
35
|
+
temperature?: number;
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}): Promise<unknown>;
|
|
38
|
+
};
|
|
35
39
|
|
|
36
40
|
function buildUserMdExtractionMessage(markdown: string, sourcePath: string): string {
|
|
37
41
|
return [
|
|
@@ -99,6 +103,48 @@ export function extractLatestAssistantText(messages: unknown[]): string | undefi
|
|
|
99
103
|
return undefined;
|
|
100
104
|
}
|
|
101
105
|
|
|
106
|
+
export function extractCompletionText(result: unknown): string | undefined {
|
|
107
|
+
if (typeof result === "string") {
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
if (!isRecord(result)) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const direct =
|
|
115
|
+
extractTextFromContent(result.content) ??
|
|
116
|
+
(typeof result.text === "string" ? result.text : undefined) ??
|
|
117
|
+
(typeof result.outputText === "string" ? result.outputText : undefined) ??
|
|
118
|
+
(typeof result.message === "string" ? result.message : undefined);
|
|
119
|
+
if (direct?.trim()) {
|
|
120
|
+
return direct;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const choices = result.choices;
|
|
124
|
+
if (!Array.isArray(choices)) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const choice of choices) {
|
|
129
|
+
if (!isRecord(choice)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const message = choice.message;
|
|
133
|
+
if (isRecord(message)) {
|
|
134
|
+
const text = extractTextFromContent(message.content);
|
|
135
|
+
if (text?.trim()) {
|
|
136
|
+
return text;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const text = extractTextFromContent(choice.content);
|
|
140
|
+
if (text?.trim()) {
|
|
141
|
+
return text;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
102
148
|
function stripJsonFence(text: string): string {
|
|
103
149
|
const trimmed = text.trim();
|
|
104
150
|
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
@@ -117,6 +163,18 @@ function unavailable(reason: string): UserMdAttributeExtractionUnavailable {
|
|
|
117
163
|
return { unavailable: true, reason };
|
|
118
164
|
}
|
|
119
165
|
|
|
166
|
+
function runtimeLlm(api: OpenClawPluginApi): RuntimeLlmComplete | undefined {
|
|
167
|
+
const runtime = api.runtime as unknown;
|
|
168
|
+
if (!isRecord(runtime)) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
const llm = runtime.llm;
|
|
172
|
+
if (!isRecord(llm) || typeof llm.complete !== "function") {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
return llm as RuntimeLlmComplete;
|
|
176
|
+
}
|
|
177
|
+
|
|
120
178
|
export function createOpenClawUserMdAttributeExtractor(
|
|
121
179
|
api: OpenClawPluginApi,
|
|
122
180
|
options?: {
|
|
@@ -129,31 +187,23 @@ export function createOpenClawUserMdAttributeExtractor(
|
|
|
129
187
|
|
|
130
188
|
return {
|
|
131
189
|
async extract({ markdown, sourcePath }) {
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
134
|
-
return unavailable("OpenClaw
|
|
190
|
+
const llm = runtimeLlm(api);
|
|
191
|
+
if (!llm) {
|
|
192
|
+
return unavailable("OpenClaw runtime llm.complete is unavailable");
|
|
135
193
|
}
|
|
136
194
|
|
|
137
|
-
const sessionKey = `libp2p-mesh:user-md-attributes:${hashForSessionKey(sourcePath)}:${randomUUID()}`;
|
|
138
|
-
|
|
139
195
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
196
|
+
const result = await llm.complete({
|
|
197
|
+
messages: [
|
|
198
|
+
{ role: "system", content: USER_MD_ATTRIBUTE_EXTRACTION_PROMPT },
|
|
199
|
+
{ role: "user", content: buildUserMdExtractionMessage(markdown, sourcePath) },
|
|
200
|
+
],
|
|
201
|
+
purpose: "libp2p-mesh.user-md-attributes",
|
|
202
|
+
maxTokens: 512,
|
|
203
|
+
temperature: 0.1,
|
|
204
|
+
timeoutMs,
|
|
148
205
|
});
|
|
149
|
-
|
|
150
|
-
const waitResult = await subagent.waitForRun({ runId, timeoutMs });
|
|
151
|
-
if (waitResult.status !== "ok") {
|
|
152
|
-
return unavailable(waitResult.error ?? `OpenClaw subagent extraction ${waitResult.status}`);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const { messages } = await subagent.getSessionMessages({ sessionKey, limit: 10 });
|
|
156
|
-
const text = extractLatestAssistantText(messages);
|
|
206
|
+
const text = extractCompletionText(result);
|
|
157
207
|
if (!text) {
|
|
158
208
|
return [];
|
|
159
209
|
}
|
|
@@ -163,8 +213,6 @@ export function createOpenClawUserMdAttributeExtractor(
|
|
|
163
213
|
const reason = error instanceof Error ? error.message : String(error);
|
|
164
214
|
logger?.warn?.(`[libp2p-mesh] USER.md agent extraction failed: ${reason}`);
|
|
165
215
|
return unavailable(reason);
|
|
166
|
-
} finally {
|
|
167
|
-
await subagent.deleteSession?.({ sessionKey, deleteTranscript: true }).catch(() => undefined);
|
|
168
216
|
}
|
|
169
217
|
},
|
|
170
218
|
};
|