@ai-setting/roy-agent-core 1.6.5 → 1.6.7
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/dist/env/agent/index.js +3 -3
- package/dist/env/event-source/index.js +1 -1
- package/dist/env/index.js +12 -12
- package/dist/env/llm/index.js +2 -2
- package/dist/env/plugin/index.js +1 -1
- package/dist/env/task/index.js +2 -2
- package/dist/env/task/tools/index.js +1 -1
- package/dist/env/tool/built-in/index.js +1 -1
- package/dist/env/tool/index.js +2 -2
- package/dist/env/workflow/engine/index.js +3 -3
- package/dist/env/workflow/index.js +5 -5
- package/dist/env/workflow/nodes/index.js +6 -2
- package/dist/env/workflow/tools/index.js +1 -1
- package/dist/index.js +14 -14
- package/dist/shared/@ai-setting/{roy-agent-core-ba6243hr.js → roy-agent-core-0tfv18rf.js} +175 -1
- package/dist/shared/@ai-setting/{roy-agent-core-z7dxedn6.js → roy-agent-core-2r1rc0fe.js} +12 -2
- package/dist/shared/@ai-setting/{roy-agent-core-xxfkvqqh.js → roy-agent-core-34xt19yg.js} +8 -7
- package/dist/shared/@ai-setting/{roy-agent-core-fb3wyr3d.js → roy-agent-core-39rvx3fw.js} +41 -3
- package/dist/shared/@ai-setting/{roy-agent-core-y1m7c2v7.js → roy-agent-core-8jmkghjc.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-65qs21kf.js → roy-agent-core-dcd1s7ct.js} +4 -1
- package/dist/shared/@ai-setting/{roy-agent-core-bcd3y74k.js → roy-agent-core-gdkq55yg.js} +3 -2
- package/dist/shared/@ai-setting/{roy-agent-core-r3209bcd.js → roy-agent-core-gw43m8yd.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-hp1n198d.js → roy-agent-core-jzza6c2r.js} +166 -45
- package/dist/shared/@ai-setting/{roy-agent-core-5rjccr3c.js → roy-agent-core-ka4b12eg.js} +88 -47
- package/dist/shared/@ai-setting/{roy-agent-core-p8zgxdgy.js → roy-agent-core-kenhsgnf.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-155408xq.js → roy-agent-core-r08qk37k.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-ktbkp661.js → roy-agent-core-sxqy40cs.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-vf72n6qn.js → roy-agent-core-w7mhnc4c.js} +22 -4
- package/package.json +1 -1
- /package/dist/shared/@ai-setting/{roy-agent-core-6kygfd9x.js → roy-agent-core-02n40zbs.js} +0 -0
|
@@ -7,19 +7,21 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
AgentComponentAdapter,
|
|
9
9
|
init_agent_component_adapter
|
|
10
|
-
} from "./roy-agent-core-
|
|
10
|
+
} from "./roy-agent-core-w7mhnc4c.js";
|
|
11
11
|
import {
|
|
12
12
|
TemplateResolver,
|
|
13
13
|
init_template_resolver
|
|
14
14
|
} from "./roy-agent-core-q88vxmx5.js";
|
|
15
15
|
import {
|
|
16
|
+
PythonCallNode,
|
|
16
17
|
SkillNode,
|
|
17
18
|
ToolNode,
|
|
18
19
|
WorkflowNode,
|
|
20
|
+
init_python_call_node,
|
|
19
21
|
init_skill_node,
|
|
20
22
|
init_tool_node,
|
|
21
23
|
init_workflow_node
|
|
22
|
-
} from "./roy-agent-core-
|
|
24
|
+
} from "./roy-agent-core-0tfv18rf.js";
|
|
23
25
|
import {
|
|
24
26
|
WorkflowError,
|
|
25
27
|
createWorkflowEvent,
|
|
@@ -1025,6 +1027,7 @@ var init_agent_node = __esm(() => {
|
|
|
1025
1027
|
const promptTemplate = config.prompt || "";
|
|
1026
1028
|
resolvedPrompt = context.templateResolver?.resolveValue(promptTemplate) ?? promptTemplate;
|
|
1027
1029
|
}
|
|
1030
|
+
resolvedPrompt = this.applyPromptInjection(resolvedPrompt, context.workflowInput?.prompt_injection);
|
|
1028
1031
|
let resolvedAgentType = agentType;
|
|
1029
1032
|
let inheritedModel;
|
|
1030
1033
|
let inheritedAllowedTools;
|
|
@@ -1135,6 +1138,16 @@ var init_agent_node = __esm(() => {
|
|
|
1135
1138
|
};
|
|
1136
1139
|
}
|
|
1137
1140
|
}
|
|
1141
|
+
applyPromptInjection(basePrompt, promptInjection) {
|
|
1142
|
+
if (typeof promptInjection !== "string" || promptInjection.length === 0) {
|
|
1143
|
+
return basePrompt;
|
|
1144
|
+
}
|
|
1145
|
+
return `${basePrompt}
|
|
1146
|
+
|
|
1147
|
+
---
|
|
1148
|
+
[Caller Injection]
|
|
1149
|
+
${promptInjection}`;
|
|
1150
|
+
}
|
|
1138
1151
|
async resolveAgentSessionIdFromWorkflow(context) {
|
|
1139
1152
|
const sessionComponent = context.sessionComponent;
|
|
1140
1153
|
if (!sessionComponent) {
|
|
@@ -1197,6 +1210,9 @@ var init_agent_node = __esm(() => {
|
|
|
1197
1210
|
__legacyDecorateClassTS([
|
|
1198
1211
|
TracedAs("workflow.agent.execute")
|
|
1199
1212
|
], AgentNode.prototype, "execute", null);
|
|
1213
|
+
__legacyDecorateClassTS([
|
|
1214
|
+
TracedAs("workflow.agent.applyPromptInjection", { recordParams: true, recordResult: false })
|
|
1215
|
+
], AgentNode.prototype, "applyPromptInjection", null);
|
|
1200
1216
|
__legacyDecorateClassTS([
|
|
1201
1217
|
TracedAs("agent.node.createAgentSubSession", { recordParams: true, recordResult: true, log: true })
|
|
1202
1218
|
], AgentNode.prototype, "createAgentSubSession", null);
|
|
@@ -1297,6 +1313,9 @@ class NodeRegistry {
|
|
|
1297
1313
|
this.factories.set("ask_user", (definition) => {
|
|
1298
1314
|
return new AskUserNode(definition);
|
|
1299
1315
|
});
|
|
1316
|
+
this.factories.set("python_call", (definition) => {
|
|
1317
|
+
return new PythonCallNode(definition);
|
|
1318
|
+
});
|
|
1300
1319
|
}
|
|
1301
1320
|
register(type, factory) {
|
|
1302
1321
|
this.factories.set(type, factory);
|
|
@@ -1323,6 +1342,7 @@ var init_node_registry = __esm(() => {
|
|
|
1323
1342
|
init_agent_component_adapter();
|
|
1324
1343
|
init_workflow_node();
|
|
1325
1344
|
init_ask_user_node();
|
|
1345
|
+
init_python_call_node();
|
|
1326
1346
|
});
|
|
1327
1347
|
|
|
1328
1348
|
// src/env/workflow/utils/agent-session-metadata.ts
|
|
@@ -1395,7 +1415,16 @@ var init_engine = __esm(() => {
|
|
|
1395
1415
|
const definition = "definition" in workflow ? workflow.definition : workflow;
|
|
1396
1416
|
const workflowId = "id" in workflow ? workflow.id : `inline_${definition.name}`;
|
|
1397
1417
|
const workflowName = definition.name;
|
|
1398
|
-
|
|
1418
|
+
let runId;
|
|
1419
|
+
if (options?.runId) {
|
|
1420
|
+
const reserved = options.runId;
|
|
1421
|
+
if (typeof reserved !== "string" || !reserved.startsWith("workflow_")) {
|
|
1422
|
+
throw new Error(`[WorkflowEngine.createSession] reserved runId must start with 'workflow_'; got ${JSON.stringify(reserved)}`);
|
|
1423
|
+
}
|
|
1424
|
+
runId = reserved;
|
|
1425
|
+
} else {
|
|
1426
|
+
runId = this.generateRunId();
|
|
1427
|
+
}
|
|
1399
1428
|
const sessionId = this.getSessionId(runId);
|
|
1400
1429
|
const metadata = {
|
|
1401
1430
|
type: "workflow",
|
|
@@ -2169,6 +2198,9 @@ var init_engine = __esm(() => {
|
|
|
2169
2198
|
let timer;
|
|
2170
2199
|
const timeoutPromise = new Promise((_, reject) => {
|
|
2171
2200
|
timer = setTimeout(() => {
|
|
2201
|
+
this.stop(sessionId, "execution timeout").catch((stopError) => {
|
|
2202
|
+
logger3.warn(`[WorkflowEngine.waitForCompletion] Failed to stop ${sessionId} after execution timeout: ${stopError instanceof Error ? stopError.message : String(stopError)}`);
|
|
2203
|
+
});
|
|
2172
2204
|
reject(new Error(`Workflow execution timeout: ${timeoutMs}ms`));
|
|
2173
2205
|
}, timeoutMs);
|
|
2174
2206
|
});
|
|
@@ -2251,6 +2283,12 @@ var init_engine = __esm(() => {
|
|
|
2251
2283
|
__legacyDecorateClassTS([
|
|
2252
2284
|
TracedAs("workflow.engine.failWorkflow", { recordParams: true, recordResult: true })
|
|
2253
2285
|
], WorkflowEngine.prototype, "failWorkflow", null);
|
|
2286
|
+
__legacyDecorateClassTS([
|
|
2287
|
+
TracedAs("workflow.engine.waitForCompletion", {
|
|
2288
|
+
recordParams: true,
|
|
2289
|
+
recordResult: true
|
|
2290
|
+
})
|
|
2291
|
+
], WorkflowEngine.prototype, "waitForCompletion", null);
|
|
2254
2292
|
});
|
|
2255
2293
|
|
|
2256
2294
|
// src/env/workflow/engine/index.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentComponentAdapter,
|
|
3
3
|
init_agent_component_adapter
|
|
4
|
-
} from "./roy-agent-core-
|
|
4
|
+
} from "./roy-agent-core-w7mhnc4c.js";
|
|
5
5
|
import"./roy-agent-core-1db4vpc6.js";
|
|
6
6
|
import"./roy-agent-core-e25xkv53.js";
|
|
7
7
|
import"./roy-agent-core-gp872e7m.js";
|
|
@@ -432,7 +432,10 @@ function processThinkingStream(textDelta, config, state) {
|
|
|
432
432
|
async function invoke(config, options, ctx) {
|
|
433
433
|
try {
|
|
434
434
|
const emitToEnv = createEnvEventEmitter(options.env, options.context);
|
|
435
|
-
logger.info("[invoke] Created event emitter
|
|
435
|
+
logger.info("[invoke] Created event emitter", {
|
|
436
|
+
envAvailable: !!options.env,
|
|
437
|
+
hasContext: options.context !== undefined
|
|
438
|
+
});
|
|
436
439
|
const { providerId, modelId } = parseModelString(options.model || config.model);
|
|
437
440
|
const provider = createProviderInstance(providerId, config.apiKey, config.baseURL);
|
|
438
441
|
if (!provider) {
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
searchTasksTool,
|
|
25
25
|
treeTasksTool,
|
|
26
26
|
updateTaskTool
|
|
27
|
-
} from "./roy-agent-core-
|
|
27
|
+
} from "./roy-agent-core-sxqy40cs.js";
|
|
28
28
|
import {
|
|
29
29
|
createOperationTool,
|
|
30
30
|
deleteOperationTool,
|
|
@@ -148,7 +148,8 @@ class DefaultTagService {
|
|
|
148
148
|
return this.store.searchTasks(query, limit);
|
|
149
149
|
}
|
|
150
150
|
async searchTasksByKeywords(keywords, options) {
|
|
151
|
-
|
|
151
|
+
const result = await this.store.searchTasksByKeywords(keywords, options);
|
|
152
|
+
return result.tasks;
|
|
152
153
|
}
|
|
153
154
|
async findSimilarTasksByKeywords(keywords, options) {
|
|
154
155
|
return this.store.findSimilarTasksByKeywords(keywords, options);
|
|
@@ -73,7 +73,7 @@ var killTree = (proc, signal = "SIGKILL") => {
|
|
|
73
73
|
var makeBashSpawner = () => {
|
|
74
74
|
return {
|
|
75
75
|
run: (command, options) => {
|
|
76
|
-
return Effect.gen(function* () {
|
|
76
|
+
return Effect.scoped(Effect.gen(function* () {
|
|
77
77
|
const cwd = options?.cwd;
|
|
78
78
|
const timeout = options?.timeout ?? 60000;
|
|
79
79
|
const env = options?.env ?? {};
|
|
@@ -175,7 +175,7 @@ var makeBashSpawner = () => {
|
|
|
175
175
|
success: !timedOut && exitCode === 0,
|
|
176
176
|
timedOut
|
|
177
177
|
};
|
|
178
|
-
});
|
|
178
|
+
}));
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
181
|
};
|
|
@@ -194,15 +194,23 @@ var timerHandler = {
|
|
|
194
194
|
|
|
195
195
|
// src/env/event-source/bounty-im-handler.ts
|
|
196
196
|
function readBountyIMConfig(config) {
|
|
197
|
-
const topLevelAddress = config.address;
|
|
198
197
|
const topLevelUrl = config.imServerUrl;
|
|
199
|
-
const
|
|
198
|
+
const topLevelEmail = config.email;
|
|
200
199
|
const optionsUrl = config.options?.imServerUrl;
|
|
200
|
+
const optionsEmail = config.options?.email;
|
|
201
201
|
return {
|
|
202
|
-
|
|
202
|
+
email: topLevelEmail || optionsEmail,
|
|
203
203
|
imServerUrl: topLevelUrl || optionsUrl
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
|
+
function assertNoLegacyAddressField(config) {
|
|
207
|
+
const legacyTopLevel = config.address;
|
|
208
|
+
const legacyInOptions = config.options?.address;
|
|
209
|
+
const legacyValue = typeof legacyTopLevel === "string" && legacyTopLevel.trim() !== "" && legacyTopLevel || typeof legacyInOptions === "string" && legacyInOptions.trim() !== "" && legacyInOptions;
|
|
210
|
+
if (legacyValue) {
|
|
211
|
+
throw new Error(`[BountyIM] v0.14 strict email-only: legacy 'address' field is no longer supported. ` + `Found: address="${legacyValue}". ` + `Migration: remove the 'address' field and add 'email' (your registered bounty email) ` + `to event-sources.json. Example: { "id": "<source-id>", "type": "bounty-im", ` + `"email": "<you@bounty.example.com>", "imServerUrl": "<ws-url>" }`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
206
214
|
function readBountyIMTlsSkipVerify(config) {
|
|
207
215
|
const topLevel = config.tlsSkipVerify;
|
|
208
216
|
const fromOptions = config.options?.tlsSkipVerify;
|
|
@@ -213,26 +221,101 @@ function buildDefaultBountyIMSystemPrompt(input) {
|
|
|
213
221
|
const server = input.imServerUrl || "ws://localhost:4005/ws";
|
|
214
222
|
const httpScheme = server.startsWith("wss://") ? "https://" : "http://";
|
|
215
223
|
const endpoint = server.replace(/^wss?:\/\//, httpScheme).replace(/\/ws$/, "");
|
|
224
|
+
const email = input.currentEmail || "<your-registered-email>";
|
|
225
|
+
const command = `bounty com send --server-url ${endpoint} --from-email ${email} --to-email <recipient-email> --body "..."`;
|
|
226
|
+
const tlsCommand = ` bounty com send --server-url ${endpoint} --from-email ${email} --to-email <recipient-email> --body "..."`;
|
|
227
|
+
const rule = `规则:from-email 永远固定为 ${email}; to-email 是收到消息的 from 字段。`;
|
|
216
228
|
return [
|
|
217
229
|
"你正在通过 Bounty IM 与其他 agent 通信。",
|
|
218
230
|
"",
|
|
219
|
-
`你的 IM
|
|
231
|
+
`你的 IM 身份(v0.14 strict email-only):${email}`,
|
|
220
232
|
`IM Server:${endpoint} (k8s remote server)`,
|
|
221
233
|
"",
|
|
222
|
-
"回复收到的消息时,**必须**使用以下命令格式(注意 --
|
|
234
|
+
"回复收到的消息时,**必须**使用以下命令格式(注意 --server-url 不可省略,省略会默认连 localhost:4000 而失败):",
|
|
223
235
|
"",
|
|
224
|
-
`
|
|
236
|
+
` ${command}`,
|
|
225
237
|
"",
|
|
226
238
|
"如果遇到 TLS 错误(自签名证书),加 -k:",
|
|
227
|
-
|
|
239
|
+
tlsCommand,
|
|
228
240
|
"",
|
|
229
|
-
|
|
241
|
+
rule
|
|
230
242
|
].join(`
|
|
231
243
|
`);
|
|
232
244
|
}
|
|
245
|
+
function translateImServerUrlToHttpApi(imServerUrl) {
|
|
246
|
+
const httpScheme = imServerUrl.startsWith("wss://") ? "https://" : "http://";
|
|
247
|
+
return imServerUrl.replace(/^wss?:\/\//, httpScheme).replace(/\/ws$/, "");
|
|
248
|
+
}
|
|
249
|
+
function resolveCanonicalToEmail(canonical, profileEmail) {
|
|
250
|
+
if (!canonical)
|
|
251
|
+
return null;
|
|
252
|
+
const trimmed = canonical.trim();
|
|
253
|
+
if (!trimmed)
|
|
254
|
+
return null;
|
|
255
|
+
const UUID_HOST = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}@(.+)$/i;
|
|
256
|
+
const match = trimmed.match(UUID_HOST);
|
|
257
|
+
if (match) {
|
|
258
|
+
const host = match[1];
|
|
259
|
+
if (host === "authenticated") {
|
|
260
|
+
return profileEmail?.trim() || null;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
|
|
265
|
+
return trimmed;
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
233
269
|
function resolveBountyIMSystemPrompt(input) {
|
|
234
270
|
return input.configSystemPrompt || buildDefaultBountyIMSystemPrompt(input);
|
|
235
271
|
}
|
|
272
|
+
function isSelfEcho(msg, currentEmail) {
|
|
273
|
+
if (!msg || typeof msg !== "object")
|
|
274
|
+
return false;
|
|
275
|
+
const m = msg;
|
|
276
|
+
const fromEmailRaw = typeof m.fromEmail === "string" ? m.fromEmail.trim() : "";
|
|
277
|
+
const currentTrim = typeof currentEmail === "string" ? currentEmail.trim() : "";
|
|
278
|
+
if (fromEmailRaw && currentTrim) {
|
|
279
|
+
return fromEmailRaw.toLowerCase() === currentTrim.toLowerCase();
|
|
280
|
+
}
|
|
281
|
+
const fromAddr = typeof m.from === "string" ? m.from : undefined;
|
|
282
|
+
const toAddr = typeof m.to === "string" ? m.to : undefined;
|
|
283
|
+
if (fromAddr && toAddr) {
|
|
284
|
+
const fromLocal = fromAddr.split("@")[0]?.trim();
|
|
285
|
+
const toLocal = toAddr.split("@")[0]?.trim();
|
|
286
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
287
|
+
if (fromLocal && toLocal && UUID_RE.test(fromLocal) && UUID_RE.test(toLocal)) {
|
|
288
|
+
return fromLocal.toLowerCase() === toLocal.toLowerCase();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
function isWrongAddress(msg, currentEmail) {
|
|
294
|
+
if (!msg || typeof msg !== "object")
|
|
295
|
+
return false;
|
|
296
|
+
const m = msg;
|
|
297
|
+
const toAddr = typeof m.to === "string" ? m.to.trim() : "";
|
|
298
|
+
if (!toAddr)
|
|
299
|
+
return false;
|
|
300
|
+
const currentTrim = typeof currentEmail === "string" ? currentEmail.trim() : "";
|
|
301
|
+
if (!currentTrim)
|
|
302
|
+
return false;
|
|
303
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
304
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
305
|
+
const toLocal = toAddr.split("@")[0] ?? "";
|
|
306
|
+
const currentLocal = currentTrim.split("@")[0] ?? "";
|
|
307
|
+
const toIsEmail = EMAIL_RE.test(toAddr) && !UUID_RE.test(toLocal);
|
|
308
|
+
const currentIsEmail = EMAIL_RE.test(currentTrim) && !UUID_RE.test(currentLocal);
|
|
309
|
+
const toIsUuid = UUID_RE.test(toLocal);
|
|
310
|
+
const currentIsUuid = UUID_RE.test(currentLocal);
|
|
311
|
+
if (toIsEmail && currentIsEmail) {
|
|
312
|
+
return toAddr.toLowerCase() !== currentTrim.toLowerCase();
|
|
313
|
+
}
|
|
314
|
+
if (toIsUuid && currentIsUuid) {
|
|
315
|
+
return toLocal.toLowerCase() !== currentLocal.toLowerCase();
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
236
319
|
function shouldWriteBunTlsSkipEnv(tlsSkipVerify, isBunRuntimeOverride) {
|
|
237
320
|
const isBun = isBunRuntimeOverride ?? typeof Bun !== "undefined";
|
|
238
321
|
return tlsSkipVerify === true && isBun;
|
|
@@ -247,15 +330,19 @@ class BountyIMInstance {
|
|
|
247
330
|
constructor(config) {
|
|
248
331
|
this.config = config;
|
|
249
332
|
}
|
|
333
|
+
getCurrentIdentity() {
|
|
334
|
+
const { email } = readBountyIMConfig(this.config);
|
|
335
|
+
return { email };
|
|
336
|
+
}
|
|
250
337
|
getCurrentAddress() {
|
|
251
|
-
const {
|
|
252
|
-
return
|
|
338
|
+
const { email } = readBountyIMConfig(this.config);
|
|
339
|
+
return email || "unknown";
|
|
253
340
|
}
|
|
254
341
|
getCurrentSystemPrompt() {
|
|
255
|
-
const imServerUrl = readBountyIMConfig(this.config)
|
|
342
|
+
const { email, imServerUrl } = readBountyIMConfig(this.config);
|
|
256
343
|
return resolveBountyIMSystemPrompt({
|
|
257
344
|
configSystemPrompt: this.config.systemPrompt,
|
|
258
|
-
|
|
345
|
+
currentEmail: email,
|
|
259
346
|
imServerUrl
|
|
260
347
|
});
|
|
261
348
|
}
|
|
@@ -266,13 +353,14 @@ class BountyIMInstance {
|
|
|
266
353
|
if (this.status === "running")
|
|
267
354
|
return;
|
|
268
355
|
this.setStatus("starting");
|
|
269
|
-
|
|
356
|
+
assertNoLegacyAddressField(this.config);
|
|
357
|
+
const { email, imServerUrl } = readBountyIMConfig(this.config);
|
|
270
358
|
if (!imServerUrl) {
|
|
271
359
|
throw new Error("imServerUrl is required for bounty-im event source");
|
|
272
360
|
}
|
|
273
361
|
const wsUrl = new URL(imServerUrl);
|
|
274
|
-
if (
|
|
275
|
-
wsUrl.searchParams.set("
|
|
362
|
+
if (email) {
|
|
363
|
+
wsUrl.searchParams.set("email", email);
|
|
276
364
|
}
|
|
277
365
|
console.log(`[BountyIM] Connecting to ${wsUrl.toString()}...`);
|
|
278
366
|
try {
|
|
@@ -287,7 +375,7 @@ class BountyIMInstance {
|
|
|
287
375
|
rejectUnauthorized: !tlsSkipVerify
|
|
288
376
|
});
|
|
289
377
|
this.ws.on("open", () => {
|
|
290
|
-
console.log(`[BountyIM] Connected${
|
|
378
|
+
console.log(`[BountyIM] Connected${email ? ` as ${email}` : ""}`);
|
|
291
379
|
this.setStatus("running");
|
|
292
380
|
});
|
|
293
381
|
this.ws.on("message", (data) => {
|
|
@@ -325,6 +413,15 @@ class BountyIMInstance {
|
|
|
325
413
|
setStatus(status) {
|
|
326
414
|
this.status = status;
|
|
327
415
|
}
|
|
416
|
+
sendAck(messageId) {
|
|
417
|
+
if (!messageId || !this.ws)
|
|
418
|
+
return;
|
|
419
|
+
try {
|
|
420
|
+
this.ws.send(JSON.stringify({ event: "ack", data: { messageIds: [messageId] } }));
|
|
421
|
+
} catch (ackErr) {
|
|
422
|
+
console.error(`[BountyIM] 发送 ACK 失败:`, ackErr);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
328
425
|
handleMessage(data) {
|
|
329
426
|
this.buffer += data;
|
|
330
427
|
try {
|
|
@@ -351,25 +448,44 @@ class BountyIMInstance {
|
|
|
351
448
|
}
|
|
352
449
|
const eventType = `bounty-im.${evt.event || "message"}`;
|
|
353
450
|
const fromAddress = msg.from || "unknown";
|
|
354
|
-
const
|
|
355
|
-
const toAddress = msg.to ||
|
|
356
|
-
if (msg
|
|
357
|
-
console.
|
|
451
|
+
const currentEmail = this.getCurrentIdentity().email || "unknown";
|
|
452
|
+
const toAddress = msg.to || currentEmail;
|
|
453
|
+
if (isSelfEcho(msg, currentEmail)) {
|
|
454
|
+
console.log(`[BountyIM] Skipped self-echo: from=${msg.from ?? "?"} to=${msg.to ?? "?"}`);
|
|
455
|
+
this.sendAck(msg.id);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (isWrongAddress(msg, currentEmail)) {
|
|
459
|
+
console.warn(`[BountyIM] Skipped wrong-address event: to=${msg.to ?? "?"} currentEmail=${currentEmail}`);
|
|
460
|
+
this.sendAck(msg.id);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (msg.to && msg.to !== currentEmail) {
|
|
464
|
+
console.warn(`[BountyIM] Warning: message.to (${msg.to}) !== current email (${currentEmail})`);
|
|
358
465
|
}
|
|
359
466
|
const msgContent = msg.content;
|
|
360
467
|
const content = msgContent?.type === "text" ? String(msgContent.body || "") : "";
|
|
361
|
-
const { imServerUrl } = readBountyIMConfig(this.config);
|
|
468
|
+
const { imServerUrl, email } = readBountyIMConfig(this.config);
|
|
362
469
|
const agent = this.config.handleRule?.agent;
|
|
470
|
+
const fromEmailRaw = msg.fromEmail;
|
|
471
|
+
const fromEmailResolved = resolveCanonicalToEmail(fromAddress, email);
|
|
472
|
+
const senderEmail = typeof fromEmailRaw === "string" && fromEmailRaw.trim() !== "" && fromEmailRaw.trim() || fromEmailResolved || fromAddress;
|
|
363
473
|
const metadata = {
|
|
364
474
|
eventType,
|
|
365
475
|
senderId: fromAddress,
|
|
476
|
+
senderEmail,
|
|
366
477
|
chatId: toAddress,
|
|
367
478
|
agent
|
|
368
479
|
};
|
|
480
|
+
const httpApiUrl = imServerUrl ? translateImServerUrlToHttpApi(imServerUrl) : "<your-server-url>";
|
|
481
|
+
const fromEmailForCmd = email || "<your-registered-email>";
|
|
482
|
+
const toEmailForCmd = senderEmail !== fromAddress ? senderEmail : "<recipient-registered-email>";
|
|
483
|
+
const replyCommandHint = `bounty com send --server-url ${httpApiUrl} --from-email ${fromEmailForCmd} --to-email ${toEmailForCmd} --body "..."`;
|
|
484
|
+
const inlineReplyHint = `bounty com send --server-url ${httpApiUrl} --from-email ${fromEmailForCmd} --to-email ${toEmailForCmd} --body "回复内容"`;
|
|
369
485
|
let recommendedAction;
|
|
370
|
-
if (evt.event === "message") {
|
|
486
|
+
if (evt.event === "message" && fromAddress !== toAddress) {
|
|
371
487
|
recommendedAction = {
|
|
372
|
-
action: `处理消息并通过 bounty com send
|
|
488
|
+
action: `处理消息并通过 bounty com send 回复发件人。使用格式:${replyCommandHint}`,
|
|
373
489
|
replyTo: {
|
|
374
490
|
chatId: fromAddress
|
|
375
491
|
}
|
|
@@ -379,14 +495,15 @@ class BountyIMInstance {
|
|
|
379
495
|
type: "bounty-im",
|
|
380
496
|
chatId: fromAddress,
|
|
381
497
|
params: {
|
|
382
|
-
from: toAddress,
|
|
498
|
+
from: email || toAddress,
|
|
383
499
|
to: fromAddress,
|
|
384
500
|
imServerUrl
|
|
385
501
|
}
|
|
386
502
|
};
|
|
387
|
-
const
|
|
503
|
+
const displayFrom = senderEmail;
|
|
504
|
+
const displayMessage = content ? `[From ${displayFrom}] ${content}
|
|
388
505
|
|
|
389
|
-
\uD83D\uDCA1 回复:
|
|
506
|
+
\uD83D\uDCA1 回复: ${inlineReplyHint}` : this.formatMessage(rawEvent);
|
|
390
507
|
const event = {
|
|
391
508
|
id: `es-${this.config.id}-${Date.now()}`,
|
|
392
509
|
type: "event-source.event.bounty-im",
|
|
@@ -410,18 +527,10 @@ class BountyIMInstance {
|
|
|
410
527
|
}
|
|
411
528
|
};
|
|
412
529
|
this.eventHandler?.(event);
|
|
413
|
-
if (msg.id
|
|
414
|
-
|
|
415
|
-
const ackPayload = JSON.stringify({
|
|
416
|
-
event: "ack",
|
|
417
|
-
data: { messageIds: [msg.id] }
|
|
418
|
-
});
|
|
419
|
-
this.ws.send(ackPayload);
|
|
420
|
-
console.log(`[BountyIM] 已发送 ACK for message: ${msg.id}`);
|
|
421
|
-
} catch (ackErr) {
|
|
422
|
-
console.error(`[BountyIM] 发送 ACK 失败:`, ackErr);
|
|
423
|
-
}
|
|
530
|
+
if (msg.id) {
|
|
531
|
+
console.log(`[BountyIM] 已发送 ACK for message: ${msg.id}`);
|
|
424
532
|
}
|
|
533
|
+
this.sendAck(msg.id);
|
|
425
534
|
} catch (err) {
|
|
426
535
|
console.error(`[BountyIM] Error processing message:`, err);
|
|
427
536
|
}
|
|
@@ -451,11 +560,19 @@ var bountyIMHandler = {
|
|
|
451
560
|
errors.push("EventSource ID is required");
|
|
452
561
|
if (!config.name)
|
|
453
562
|
errors.push("EventSource name is required");
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
563
|
+
try {
|
|
564
|
+
assertNoLegacyAddressField(config);
|
|
565
|
+
} catch (err) {
|
|
566
|
+
errors.push(err instanceof Error ? err.message : String(err));
|
|
567
|
+
}
|
|
568
|
+
const { imServerUrl, email } = readBountyIMConfig(config);
|
|
569
|
+
if (!email) {
|
|
570
|
+
errors.push("Bounty IM email is required (v0.14 strict email-only; top-level email or options.email)");
|
|
571
|
+
}
|
|
572
|
+
if (email !== undefined && email !== "") {
|
|
573
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
574
|
+
errors.push("Bounty IM email format invalid (expected: user@domain.tld)");
|
|
575
|
+
}
|
|
459
576
|
}
|
|
460
577
|
if (!imServerUrl) {
|
|
461
578
|
errors.push("Bounty IM imServerUrl is required (top-level imServerUrl or options.imServerUrl)");
|
|
@@ -540,7 +657,7 @@ function extractMetadata(rawEvent, eventType) {
|
|
|
540
657
|
const profileFlag = profile2 ? `--profile ${profile2} ` : "";
|
|
541
658
|
const profileHint = profile2 ? `(⚠️ 必须使用 profile="${profile2}" 调用 lark-cli,否则会因 openId 跨 app 失败)` : "";
|
|
542
659
|
recommendedAction = {
|
|
543
|
-
action: `处理飞书消息并以机器人身份回复${profileHint},例如:lark-cli ${profileFlag}im +messages-reply --message-id "xxxxid" --as bot --
|
|
660
|
+
action: `处理飞书消息并以机器人身份回复${profileHint},例如:lark-cli ${profileFlag}im +messages-reply --message-id "xxxxid" --as bot --markdown "Markdown 格式回复内容"`,
|
|
544
661
|
replyTo: {
|
|
545
662
|
appId: metadata.appId,
|
|
546
663
|
profile: profile2,
|
|
@@ -808,8 +925,9 @@ class LarkCliInstance {
|
|
|
808
925
|
}
|
|
809
926
|
const event = rawEvent;
|
|
810
927
|
let eventType;
|
|
811
|
-
|
|
812
|
-
|
|
928
|
+
const eventHeader = event.header;
|
|
929
|
+
if (eventHeader?.event_type) {
|
|
930
|
+
eventType = eventHeader.event_type;
|
|
813
931
|
} else if (event.schema) {
|
|
814
932
|
eventType = `lark.${event.schema}`;
|
|
815
933
|
} else if (event.type) {
|
|
@@ -1310,6 +1428,9 @@ class EventSourceComponent extends BaseComponent {
|
|
|
1310
1428
|
return Array.from(this.sources.values());
|
|
1311
1429
|
}
|
|
1312
1430
|
getStatus(id) {
|
|
1431
|
+
if (id === undefined) {
|
|
1432
|
+
return this._status;
|
|
1433
|
+
}
|
|
1313
1434
|
return this.statuses.get(id);
|
|
1314
1435
|
}
|
|
1315
1436
|
getEventSourceStatus(id) {
|