@ai-setting/roy-agent-core 1.5.89 → 1.5.90

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.
Files changed (26) hide show
  1. package/dist/env/agent/index.js +3 -3
  2. package/dist/env/event-source/index.js +8 -4
  3. package/dist/env/index.js +9 -9
  4. package/dist/env/llm/index.js +4 -2
  5. package/dist/env/session/index.js +3 -3
  6. package/dist/env/session/storage/index.js +2 -2
  7. package/dist/env/task/plugins/index.js +1 -1
  8. package/dist/env/workflow/engine/index.js +2 -2
  9. package/dist/env/workflow/index.js +3 -3
  10. package/dist/env/workflow/nodes/index.js +1 -1
  11. package/dist/index.js +119 -14
  12. package/dist/shared/@ai-setting/{roy-agent-core-r0m0at3x.js → roy-agent-core-0r4ndyn9.js} +119 -7
  13. package/dist/shared/@ai-setting/{roy-agent-core-fvfc7f6v.js → roy-agent-core-2c8eraxq.js} +62 -8
  14. package/dist/shared/@ai-setting/{roy-agent-core-h2d1s8yd.js → roy-agent-core-5ykms33a.js} +1 -1
  15. package/dist/shared/@ai-setting/{roy-agent-core-fgpnv7dt.js → roy-agent-core-8zjntsbb.js} +71 -28
  16. package/dist/shared/@ai-setting/{roy-agent-core-vneyghpg.js → roy-agent-core-ddq5hcp5.js} +1 -1
  17. package/dist/shared/@ai-setting/{roy-agent-core-qhhxx2x2.js → roy-agent-core-ds5f75pg.js} +2 -1
  18. package/dist/shared/@ai-setting/{roy-agent-core-3f6k060j.js → roy-agent-core-j0107ww1.js} +1 -1
  19. package/dist/shared/@ai-setting/{roy-agent-core-2q7cshpm.js → roy-agent-core-m683wd1n.js} +283 -15
  20. package/dist/shared/@ai-setting/{roy-agent-core-qbq3jgrn.js → roy-agent-core-rkz8r2sx.js} +1 -1
  21. package/dist/shared/@ai-setting/{roy-agent-core-c1j7ev4e.js → roy-agent-core-txwf64pd.js} +70 -0
  22. package/dist/shared/@ai-setting/{roy-agent-core-m3dkyprg.js → roy-agent-core-w0kb72ve.js} +7 -2
  23. package/dist/shared/@ai-setting/{roy-agent-core-hc20420t.js → roy-agent-core-z240ts1r.js} +27 -3
  24. package/dist/shared/@ai-setting/{roy-agent-core-q7sqeax6.js → roy-agent-core-zky9jse4.js} +163 -3
  25. package/package.json +1 -1
  26. /package/dist/shared/@ai-setting/{roy-agent-core-4f3976cd.js → roy-agent-core-k5hxvaf0.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TaskTagPlugin,
3
3
  createLarkCliTaskNotifyHook
4
- } from "./roy-agent-core-m3dkyprg.js";
4
+ } from "./roy-agent-core-w0kb72ve.js";
5
5
  import {
6
6
  envKeyToConfigKey
7
7
  } from "./roy-agent-core-qxhq8ven.js";
@@ -192,6 +192,249 @@ var timerHandler = {
192
192
  }
193
193
  };
194
194
 
195
+ // src/env/event-source/bounty-im-handler.ts
196
+ function readBountyIMConfig(config) {
197
+ const topLevelAddress = config.address;
198
+ const topLevelUrl = config.imServerUrl;
199
+ const optionsAddress = config.options?.address;
200
+ const optionsUrl = config.options?.imServerUrl;
201
+ return {
202
+ address: topLevelAddress || optionsAddress,
203
+ imServerUrl: topLevelUrl || optionsUrl
204
+ };
205
+ }
206
+ function readBountyIMTlsSkipVerify(config) {
207
+ const topLevel = config.tlsSkipVerify;
208
+ const fromOptions = config.options?.tlsSkipVerify;
209
+ const raw = topLevel !== undefined ? topLevel : fromOptions;
210
+ return raw === true;
211
+ }
212
+ function shouldWriteBunTlsSkipEnv(tlsSkipVerify, isBunRuntimeOverride) {
213
+ const isBun = isBunRuntimeOverride ?? typeof Bun !== "undefined";
214
+ return tlsSkipVerify === true && isBun;
215
+ }
216
+
217
+ class BountyIMInstance {
218
+ config;
219
+ status = "created";
220
+ ws = null;
221
+ buffer = "";
222
+ eventHandler;
223
+ constructor(config) {
224
+ this.config = config;
225
+ }
226
+ getCurrentAddress() {
227
+ const { address } = readBountyIMConfig(this.config);
228
+ return address || "unknown";
229
+ }
230
+ getStatus() {
231
+ return this.status;
232
+ }
233
+ async start() {
234
+ if (this.status === "running")
235
+ return;
236
+ this.setStatus("starting");
237
+ const { address, imServerUrl } = readBountyIMConfig(this.config);
238
+ if (!imServerUrl) {
239
+ throw new Error("imServerUrl is required for bounty-im event source");
240
+ }
241
+ const wsUrl = new URL(imServerUrl);
242
+ if (address) {
243
+ wsUrl.searchParams.set("address", address);
244
+ }
245
+ console.log(`[BountyIM] Connecting to ${wsUrl.toString()}...`);
246
+ try {
247
+ const { WebSocket } = await import("ws");
248
+ const tlsSkipVerify = readBountyIMTlsSkipVerify(this.config);
249
+ const isWss = imServerUrl.startsWith("wss://");
250
+ if (isWss && shouldWriteBunTlsSkipEnv(tlsSkipVerify)) {
251
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
252
+ }
253
+ this.ws = new WebSocket(wsUrl.toString(), {
254
+ headers: this.config.headers,
255
+ rejectUnauthorized: !tlsSkipVerify
256
+ });
257
+ this.ws.on("open", () => {
258
+ console.log(`[BountyIM] Connected${address ? ` as ${address}` : ""}`);
259
+ this.setStatus("running");
260
+ });
261
+ this.ws.on("message", (data) => {
262
+ this.handleMessage(data.toString());
263
+ });
264
+ this.ws.on("error", (error) => {
265
+ console.error(`[BountyIM] Error:`, error.message);
266
+ this.setStatus("error");
267
+ });
268
+ this.ws.on("close", () => {
269
+ console.log(`[BountyIM] Connection closed`);
270
+ this.setStatus("stopped");
271
+ });
272
+ } catch (error) {
273
+ this.setStatus("error");
274
+ throw new Error(`Failed to connect to Bounty IM server: ${error}`);
275
+ }
276
+ }
277
+ async stop() {
278
+ if (this.status === "stopped" || this.status === "created")
279
+ return;
280
+ this.setStatus("stopping");
281
+ if (this.ws) {
282
+ this.ws.close();
283
+ this.ws = null;
284
+ }
285
+ this.setStatus("stopped");
286
+ }
287
+ onEvent(handler) {
288
+ this.eventHandler = handler;
289
+ }
290
+ offEvent() {
291
+ this.eventHandler = undefined;
292
+ }
293
+ setStatus(status) {
294
+ this.status = status;
295
+ }
296
+ handleMessage(data) {
297
+ this.buffer += data;
298
+ try {
299
+ JSON.parse(this.buffer);
300
+ this.processLine(this.buffer);
301
+ this.buffer = "";
302
+ } catch {}
303
+ }
304
+ processLine(rawData) {
305
+ try {
306
+ let rawEvent;
307
+ try {
308
+ rawEvent = JSON.parse(rawData);
309
+ } catch {
310
+ rawEvent = {
311
+ event: "message",
312
+ data: { content: { type: "text", body: rawData } }
313
+ };
314
+ }
315
+ const evt = rawEvent;
316
+ const msg = evt.data || {};
317
+ if (evt.event !== "message") {
318
+ return;
319
+ }
320
+ const eventType = `bounty-im.${evt.event || "message"}`;
321
+ const fromAddress = msg.from || "unknown";
322
+ const currentAddress = this.getCurrentAddress();
323
+ const toAddress = msg.to || currentAddress;
324
+ if (msg.to && msg.to !== currentAddress) {
325
+ console.warn(`[BountyIM] Warning: message.to (${msg.to}) !== current address (${currentAddress})`);
326
+ }
327
+ const msgContent = msg.content;
328
+ const content = msgContent?.type === "text" ? String(msgContent.body || "") : "";
329
+ const { imServerUrl } = readBountyIMConfig(this.config);
330
+ const agent = this.config.handleRule?.agent;
331
+ const metadata = {
332
+ eventType,
333
+ senderId: fromAddress,
334
+ chatId: toAddress,
335
+ agent
336
+ };
337
+ let recommendedAction;
338
+ if (evt.event === "message") {
339
+ recommendedAction = {
340
+ action: `处理消息并通过 bounty com send 回复发件人。使用格式:bounty com send -f ${toAddress} -t ${fromAddress} -b "回复内容"`,
341
+ replyTo: {
342
+ chatId: fromAddress
343
+ }
344
+ };
345
+ }
346
+ const replyChannel = {
347
+ type: "bounty-im",
348
+ chatId: fromAddress,
349
+ params: {
350
+ from: toAddress,
351
+ to: fromAddress,
352
+ imServerUrl
353
+ }
354
+ };
355
+ const displayMessage = content ? `[From ${fromAddress}] ${content}
356
+
357
+ \uD83D\uDCA1 回复: bounty com send -f ${toAddress} -t ${fromAddress} -b "回复内容"` : this.formatMessage(rawEvent);
358
+ const event = {
359
+ id: `es-${this.config.id}-${Date.now()}`,
360
+ type: eventType,
361
+ timestamp: Date.now(),
362
+ metadata: {
363
+ source: "event-source",
364
+ sourceId: this.config.id,
365
+ agent
366
+ },
367
+ payload: {
368
+ sourceId: this.config.id,
369
+ sourceType: "bounty-im",
370
+ rawEvent,
371
+ message: displayMessage,
372
+ metadata,
373
+ recommendedAction,
374
+ replyChannel,
375
+ timestamp: Date.now()
376
+ }
377
+ };
378
+ this.eventHandler?.(event);
379
+ if (msg.id && this.ws) {
380
+ try {
381
+ const ackPayload = JSON.stringify({
382
+ event: "ack",
383
+ data: { messageIds: [msg.id] }
384
+ });
385
+ this.ws.send(ackPayload);
386
+ console.log(`[BountyIM] 已发送 ACK for message: ${msg.id}`);
387
+ } catch (ackErr) {
388
+ console.error(`[BountyIM] 发送 ACK 失败:`, ackErr);
389
+ }
390
+ }
391
+ } catch (err) {
392
+ console.error(`[BountyIM] Error processing message:`, err);
393
+ }
394
+ }
395
+ formatMessage(rawEvent) {
396
+ const evt = rawEvent;
397
+ if (evt.event === "connected") {
398
+ return `✅ 已连接到 bounty IM 服务器`;
399
+ }
400
+ if (evt.event === "message") {
401
+ const msg = evt.data || {};
402
+ const from = msg.from || "unknown";
403
+ let content = "未知内容";
404
+ if (msg.content?.type === "text") {
405
+ content = String(msg.content?.body || "");
406
+ }
407
+ return `[${from}] ${content}`;
408
+ }
409
+ return `事件: ${evt.event || "unknown"}`;
410
+ }
411
+ }
412
+ var bountyIMHandler = {
413
+ type: "bounty-im",
414
+ validateConfig(config) {
415
+ const errors = [];
416
+ if (!config.id)
417
+ errors.push("EventSource ID is required");
418
+ if (!config.name)
419
+ errors.push("EventSource name is required");
420
+ const { address, imServerUrl } = readBountyIMConfig(config);
421
+ if (!address) {
422
+ errors.push("Bounty IM address is required (top-level address or options.address)");
423
+ } else if (!/^[\w-]+@[\w.-]+$/.test(address)) {
424
+ errors.push("Bounty IM address format invalid (expected: agent-id@host)");
425
+ }
426
+ if (!imServerUrl) {
427
+ errors.push("Bounty IM imServerUrl is required (top-level imServerUrl or options.imServerUrl)");
428
+ } else if (!imServerUrl.startsWith("ws://") && !imServerUrl.startsWith("wss://")) {
429
+ errors.push("Bounty IM imServerUrl must start with ws:// or wss://");
430
+ }
431
+ return errors;
432
+ },
433
+ createInstance(config) {
434
+ return new BountyIMInstance(config);
435
+ }
436
+ };
437
+
195
438
  // src/env/event-source/event-source-handlers.ts
196
439
  init_global_hook_manager();
197
440
  import { spawn } from "child_process";
@@ -259,18 +502,24 @@ function extractMetadata(rawEvent, eventType) {
259
502
  }
260
503
  let recommendedAction;
261
504
  if (eventType === "im.message.receive_v1" || event.type === "im.message.receive_v1") {
505
+ const profile2 = event.profile;
506
+ const profileFlag = profile2 ? `--profile ${profile2} ` : "";
507
+ const profileHint = profile2 ? `(⚠️ 必须使用 profile="${profile2}" 调用 lark-cli,否则会因 openId 跨 app 失败)` : "";
262
508
  recommendedAction = {
263
- action: '处理飞书消息并以机器人身份回复,比如lark-cli im +messages-reply --message-id "xxxxid" --as bot --text "回复内容"',
509
+ action: `处理飞书消息并以机器人身份回复${profileHint},例如:lark-cli ${profileFlag}im +messages-reply --message-id "xxxxid" --as bot --text "回复内容"`,
264
510
  replyTo: {
265
511
  appId: metadata.appId,
512
+ profile: profile2,
266
513
  chatId: metadata.chatId,
267
514
  messageId: metadata.messageId
268
515
  }
269
516
  };
270
517
  }
518
+ const profile = event.profile;
271
519
  const replyChannel = {
272
520
  type: "lark-cli",
273
521
  appId: metadata.appId,
522
+ profile,
274
523
  chatId: messageData?.chat_id,
275
524
  messageId: messageData?.message_id
276
525
  };
@@ -291,7 +540,14 @@ class LarkCliInstance {
291
540
  if (this.status === "running")
292
541
  return;
293
542
  await this.loadPlugins();
294
- const command = this.config.command || "lark-cli event consume im.message.receive_v1 --as bot";
543
+ const profile = this.config.profile;
544
+ const profileFlag = profile ? `--profile ${profile} ` : "";
545
+ let command;
546
+ if (this.config.command) {
547
+ command = this.config.command.replace(/^(lark-cli)(\s+|$)/, (_, head, tail) => `${head} ${profileFlag.trim()}${tail}`);
548
+ } else {
549
+ command = `lark-cli ${profileFlag}event consume im.message.receive_v1 --as bot`;
550
+ }
295
551
  this.child = spawn("exec " + command, [], {
296
552
  shell: true,
297
553
  stdio: ["pipe", "pipe", "pipe"],
@@ -530,7 +786,12 @@ class LarkCliInstance {
530
786
  if (this.config.eventTypes?.length && !matchEventType(eventType, this.config.eventTypes)) {
531
787
  return;
532
788
  }
533
- const { metadata, replyChannel, recommendedAction } = extractMetadata(rawEvent, eventType);
789
+ const enrichedEvent = {
790
+ ...rawEvent,
791
+ profile: this.config.profile,
792
+ eventSourceId: this.config.id
793
+ };
794
+ const { metadata, replyChannel, recommendedAction } = extractMetadata(enrichedEvent, eventType);
534
795
  const message = JSON.stringify({
535
796
  rawData,
536
797
  recommendedAction
@@ -598,18 +859,21 @@ class LarkCliInstance {
598
859
  if (!replyChannel || !replyChannel.chatId && !replyChannel.messageId) {
599
860
  return;
600
861
  }
862
+ const profile = replyChannel.profile;
601
863
  return {
602
864
  type: replyChannel.type,
603
865
  messageId: replyChannel.messageId,
604
866
  chatId: replyChannel.chatId,
605
867
  userId: replyChannel.params?.userId,
868
+ profile,
606
869
  identity: "bot",
607
870
  sendText: async (text) => {
608
871
  await this.sendLarkMessage({
609
872
  chatId: replyChannel.chatId,
610
873
  userId: replyChannel.params?.userId,
611
874
  content: text,
612
- as: "bot"
875
+ as: "bot",
876
+ profile
613
877
  });
614
878
  },
615
879
  sendMarkdown: async (markdown) => {
@@ -618,7 +882,8 @@ class LarkCliInstance {
618
882
  userId: replyChannel.params?.userId,
619
883
  content: markdown,
620
884
  as: "bot",
621
- format: "markdown"
885
+ format: "markdown",
886
+ profile
622
887
  });
623
888
  },
624
889
  sendMessage: async (content) => {
@@ -626,7 +891,8 @@ class LarkCliInstance {
626
891
  chatId: replyChannel.chatId,
627
892
  userId: replyChannel.params?.userId,
628
893
  content: JSON.stringify(content),
629
- as: "bot"
894
+ as: "bot",
895
+ profile
630
896
  });
631
897
  },
632
898
  reply: async (content) => {
@@ -634,7 +900,8 @@ class LarkCliInstance {
634
900
  await this.sendLarkMessage({
635
901
  messageId: replyChannel.messageId,
636
902
  content: JSON.stringify(content),
637
- as: "bot"
903
+ as: "bot",
904
+ profile
638
905
  });
639
906
  }
640
907
  }
@@ -642,7 +909,7 @@ class LarkCliInstance {
642
909
  }
643
910
  async sendLarkMessage(options) {
644
911
  return new Promise((resolve, reject) => {
645
- const { chatId, userId, messageId, content, as: identity, format = "text" } = options;
912
+ const { chatId, userId, messageId, content, as: identity, format = "text", profile } = options;
646
913
  const contentFlag = format === "markdown" ? "--markdown" : "--text";
647
914
  const args = ["im", "+messages-send", "--as", identity];
648
915
  if (messageId) {
@@ -655,6 +922,9 @@ class LarkCliInstance {
655
922
  reject(new Error("No target specified for sending message"));
656
923
  return;
657
924
  }
925
+ if (profile) {
926
+ args.unshift("--profile", profile);
927
+ }
658
928
  const proc = spawn("lark-cli", args, {
659
929
  shell: false,
660
930
  stdio: ["pipe", "pipe", "pipe"]
@@ -691,9 +961,6 @@ var larkCliHandler = {
691
961
  type: "lark-cli",
692
962
  validateConfig(config) {
693
963
  const errors = [];
694
- if (!config.command) {
695
- errors.push("command is optional, will use default: lark-cli event consume im.message.receive_v1 --as bot");
696
- }
697
964
  return errors;
698
965
  },
699
966
  createInstance(config) {
@@ -702,7 +969,8 @@ var larkCliHandler = {
702
969
  };
703
970
  var builtInHandlers = [
704
971
  larkCliHandler,
705
- timerHandler
972
+ timerHandler,
973
+ bountyIMHandler
706
974
  ];
707
975
  function getBuiltInHandler(type) {
708
976
  return builtInHandlers.find((h) => h.type === type);
@@ -804,7 +1072,7 @@ class EventSourceComponent extends BaseComponent {
804
1072
  }
805
1073
  }
806
1074
  async executeInitHooks() {
807
- const { EventSourceInitHooks } = await import("./roy-agent-core-vneyghpg.js");
1075
+ const { EventSourceInitHooks } = await import("./roy-agent-core-ddq5hcp5.js");
808
1076
  await EventSourceInitHooks.execute(this);
809
1077
  }
810
1078
  registerHandler(handler) {
@@ -1146,4 +1414,4 @@ __legacyDecorateClassTS([
1146
1414
  TracedAs("event-source.update", { recordParams: false, log: false })
1147
1415
  ], EventSourceComponent.prototype, "update", null);
1148
1416
 
1149
- export { TimerInstance, timerHandler, larkCliHandler, builtInHandlers, getBuiltInHandler, EventSourceComponent };
1417
+ export { TimerInstance, timerHandler, BountyIMInstance, bountyIMHandler, larkCliHandler, builtInHandlers, getBuiltInHandler, EventSourceComponent };
@@ -6,7 +6,7 @@ import {
6
6
  WorkflowEngine,
7
7
  exports_engine,
8
8
  init_engine
9
- } from "./roy-agent-core-3f6k060j.js";
9
+ } from "./roy-agent-core-j0107ww1.js";
10
10
  import {
11
11
  askUserTool,
12
12
  createRunWorkflowTool,
@@ -5,6 +5,41 @@ import {
5
5
  // src/env/session/session-message-converter.ts
6
6
  import { randomUUID } from "crypto";
7
7
  var DEFAULT_REASONING_BUDGET_TOKENS = 1e4;
8
+ function toModelImagePart(imagePart) {
9
+ return {
10
+ type: "image",
11
+ image: normalizeImagePayload(imagePart.image, imagePart.mimeType),
12
+ mimeType: imagePart.mimeType
13
+ };
14
+ }
15
+ function isLegacyBufferShape(v) {
16
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Uint8Array) && !(v instanceof URL) && v.type === "Buffer" && Array.isArray(v.data);
17
+ }
18
+ function normalizeImagePayload(image, mimeType) {
19
+ if (typeof image === "string" || image instanceof URL) {
20
+ return image;
21
+ }
22
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
23
+ const mime = mimeType ?? "image/jpeg";
24
+ return `data:${mime};base64,${image.toString("base64")}`;
25
+ }
26
+ if (image instanceof Uint8Array) {
27
+ if (typeof Buffer !== "undefined") {
28
+ const mime2 = mimeType ?? "image/jpeg";
29
+ return `data:${mime2};base64,${Buffer.from(image).toString("base64")}`;
30
+ }
31
+ let binary = "";
32
+ for (let i = 0;i < image.length; i++) {
33
+ binary += String.fromCharCode(image[i]);
34
+ }
35
+ const mime = mimeType ?? "image/jpeg";
36
+ return `data:${mime};base64,${globalThis.btoa?.(binary) ?? binary}`;
37
+ }
38
+ if (isLegacyBufferShape(image)) {
39
+ return new Uint8Array(image.data);
40
+ }
41
+ return image;
42
+ }
8
43
 
9
44
  class SessionMessageConverter {
10
45
  toModelMessage(msg) {
@@ -47,6 +82,8 @@ class SessionMessageConverter {
47
82
  mimeType: filePart.mime,
48
83
  filename: filePart.filename
49
84
  });
85
+ } else if (part.type === "image") {
86
+ content.push(toModelImagePart(part));
50
87
  } else if (part.type === "tool-result" || part.type === "checkpoint") {} else {
51
88
  console.debug(`[session-message-converter] Skipping unsupported part type in assistant: ${part.type}`);
52
89
  }
@@ -68,6 +105,32 @@ class SessionMessageConverter {
68
105
  }]
69
106
  };
70
107
  }
108
+ if (msg.role === "user" || msg.role === "system") {
109
+ const parts = msg.parts || [];
110
+ const hasMultimodal = parts.some((p) => p.type === "image" || p.type === "file");
111
+ if (hasMultimodal) {
112
+ const content = [];
113
+ for (const part of parts) {
114
+ if (part.type === "text") {
115
+ content.push({ type: "text", text: part.content });
116
+ } else if (part.type === "image") {
117
+ content.push(toModelImagePart(part));
118
+ } else if (part.type === "file") {
119
+ const filePart = part;
120
+ content.push({
121
+ type: "file",
122
+ url: filePart.url,
123
+ mimeType: filePart.mime,
124
+ filename: filePart.filename
125
+ });
126
+ }
127
+ }
128
+ return {
129
+ role: msg.role,
130
+ content
131
+ };
132
+ }
133
+ }
71
134
  return {
72
135
  role: msg.role,
73
136
  content: msg.content || ""
@@ -121,6 +184,13 @@ class SessionMessageConverter {
121
184
  url: p.url,
122
185
  filename: p.filename
123
186
  });
187
+ } else if (p.type === "image") {
188
+ const imageData = normalizeImagePayload(p.image, p.mimeType);
189
+ parts.push({
190
+ type: "image",
191
+ image: imageData,
192
+ mimeType: p.mimeType
193
+ });
124
194
  } else {
125
195
  console.debug(`[session-message-converter] Unknown part type: ${p.type}`);
126
196
  }
@@ -461,10 +461,12 @@ ${content}`;
461
461
  if (isTaskNotifyDebugEnabled()) {
462
462
  logger2.debug(`Sending message - messageId: ${messageId}`);
463
463
  }
464
+ const profile = replyChannel?.profile;
464
465
  await this.sendLarkMessage({
465
466
  chatId,
466
467
  messageId,
467
- content: message
468
+ content: message,
469
+ profile
468
470
  });
469
471
  logger2.info(`Notification sent: ${type}`);
470
472
  } catch (error) {
@@ -473,7 +475,7 @@ ${content}`;
473
475
  }
474
476
  sendLarkMessage(options) {
475
477
  return new Promise((resolve, reject) => {
476
- const { chatId, messageId, content } = options;
478
+ const { chatId, messageId, content, profile } = options;
477
479
  let args;
478
480
  if (messageId) {
479
481
  args = [
@@ -496,6 +498,9 @@ ${content}`;
496
498
  content
497
499
  ];
498
500
  }
501
+ if (profile) {
502
+ args.unshift("--profile", profile);
503
+ }
499
504
  const proc = spawn("lark-cli", args, {
500
505
  shell: false,
501
506
  stdio: ["pipe", "pipe", "pipe"]
@@ -8,7 +8,22 @@ import {
8
8
  } from "./roy-agent-core-fs0mn2jk.js";
9
9
 
10
10
  // src/env/workflow/nodes/tool-node.ts
11
- var ToolNode;
11
+ function isPositiveNumber(v) {
12
+ return typeof v === "number" && Number.isFinite(v) && v > 0;
13
+ }
14
+ function getToolTimeoutMs(input, config) {
15
+ if (input && typeof input === "object") {
16
+ const inputTimeout = input.timeout;
17
+ if (isPositiveNumber(inputTimeout)) {
18
+ return inputTimeout;
19
+ }
20
+ }
21
+ if (config && isPositiveNumber(config.timeout)) {
22
+ return config.timeout;
23
+ }
24
+ return DEFAULT_TOOL_TIMEOUT_MS;
25
+ }
26
+ var DEFAULT_TOOL_TIMEOUT_MS = 60000, ToolNode;
12
27
  var init_tool_node = __esm(() => {
13
28
  init_decorator();
14
29
  ToolNode = class ToolNode {
@@ -88,11 +103,20 @@ var init_tool_node = __esm(() => {
88
103
  nodeId: context.nodeId
89
104
  }
90
105
  };
106
+ const toolTimeoutMs = getToolTimeoutMs(input, this.definition.config);
91
107
  const executePromise = tool.execute(input, toolContext);
108
+ let timeoutHandle;
92
109
  const timeoutPromise = new Promise((_, reject) => {
93
- setTimeout(() => reject(new Error(`Tool '${toolName}' execution timeout after 60 seconds`)), 60000);
110
+ timeoutHandle = setTimeout(() => reject(new Error(`Tool '${toolName}' execution timeout after ${Math.round(toolTimeoutMs / 1000)} seconds`)), toolTimeoutMs);
94
111
  });
95
- output = await Promise.race([executePromise, timeoutPromise]);
112
+ try {
113
+ output = await Promise.race([executePromise, timeoutPromise]);
114
+ } finally {
115
+ if (timeoutHandle !== undefined) {
116
+ clearTimeout(timeoutHandle);
117
+ timeoutHandle = undefined;
118
+ }
119
+ }
96
120
  } catch (toolError) {
97
121
  if (toolError instanceof TypeError && toolError.message.includes("context") && toolError.message.includes("not a function")) {
98
122
  output = await tool.execute(input);