@ai-setting/roy-agent-core 1.6.4 → 1.6.6

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 (36) hide show
  1. package/dist/env/agent/index.js +3 -3
  2. package/dist/env/event-source/index.js +3 -3
  3. package/dist/env/index.js +15 -15
  4. package/dist/env/llm/index.js +2 -2
  5. package/dist/env/plugin/index.js +1 -1
  6. package/dist/env/prompt/index.js +2 -2
  7. package/dist/env/task/delegate/index.js +2 -2
  8. package/dist/env/task/index.js +3 -3
  9. package/dist/env/task/plugins/index.js +2 -2
  10. package/dist/env/tool/built-in/index.js +1 -1
  11. package/dist/env/tool/index.js +2 -2
  12. package/dist/env/workflow/engine/index.js +3 -3
  13. package/dist/env/workflow/index.js +5 -5
  14. package/dist/env/workflow/nodes/index.js +6 -2
  15. package/dist/env/workflow/tools/index.js +1 -1
  16. package/dist/index.js +18 -18
  17. package/dist/shared/@ai-setting/{roy-agent-core-ba6243hr.js → roy-agent-core-0tfv18rf.js} +175 -1
  18. package/dist/shared/@ai-setting/{roy-agent-core-hd5xrr2q.js → roy-agent-core-12ttd2w1.js} +1 -1
  19. package/dist/shared/@ai-setting/{roy-agent-core-z7dxedn6.js → roy-agent-core-2r1rc0fe.js} +12 -2
  20. package/dist/shared/@ai-setting/{roy-agent-core-xxfkvqqh.js → roy-agent-core-34xt19yg.js} +8 -7
  21. package/dist/shared/@ai-setting/{roy-agent-core-z9m38ytb.js → roy-agent-core-4extx33r.js} +2 -3
  22. package/dist/shared/@ai-setting/{roy-agent-core-kf3mw79k.js → roy-agent-core-4y3hdqw2.js} +20 -8
  23. package/dist/shared/@ai-setting/{roy-agent-core-y1m7c2v7.js → roy-agent-core-8jmkghjc.js} +1 -1
  24. package/dist/shared/@ai-setting/{roy-agent-core-g7hfkszj.js → roy-agent-core-971gwby7.js} +1 -1
  25. package/dist/shared/@ai-setting/{roy-agent-core-tyqzn10b.js → roy-agent-core-a5rde2ky.js} +3 -2
  26. package/dist/shared/@ai-setting/{roy-agent-core-2xpstxhz.js → roy-agent-core-b9bnm4mj.js} +3 -3
  27. package/dist/shared/@ai-setting/{roy-agent-core-7afcdb2m.js → roy-agent-core-c94yan0b.js} +1 -1
  28. package/dist/shared/@ai-setting/{roy-agent-core-65qs21kf.js → roy-agent-core-dcd1s7ct.js} +4 -1
  29. package/dist/shared/@ai-setting/{roy-agent-core-r3209bcd.js → roy-agent-core-gw43m8yd.js} +2 -2
  30. package/dist/shared/@ai-setting/{roy-agent-core-aa0tdfb2.js → roy-agent-core-jayzyg7h.js} +1 -1
  31. package/dist/shared/@ai-setting/{roy-agent-core-bferdaw6.js → roy-agent-core-mzhsk0jb.js} +58 -26
  32. package/dist/shared/@ai-setting/{roy-agent-core-155408xq.js → roy-agent-core-r08qk37k.js} +1 -1
  33. package/dist/shared/@ai-setting/{roy-agent-core-xnga0ksx.js → roy-agent-core-smmr6wr3.js} +43 -40
  34. package/dist/shared/@ai-setting/{roy-agent-core-vf72n6qn.js → roy-agent-core-w7mhnc4c.js} +22 -4
  35. package/package.json +1 -1
  36. /package/dist/shared/@ai-setting/{roy-agent-core-6kygfd9x.js → roy-agent-core-02n40zbs.js} +0 -0
@@ -234,6 +234,9 @@ class PluginComponent extends BaseComponent {
234
234
  return this.registry.has(name);
235
235
  }
236
236
  getConfig(name) {
237
+ if (name === undefined) {
238
+ return super.getConfig();
239
+ }
237
240
  return this.configComponent?.get(`plugin.${name}`);
238
241
  }
239
242
  async registerAll(plugins) {
@@ -299,7 +302,12 @@ class PluginComponent extends BaseComponent {
299
302
  registerTool(tool) {
300
303
  const toolComponent = self.env?.getComponent("tool");
301
304
  if (toolComponent) {
302
- toolComponent.register(tool);
305
+ toolComponent.register({
306
+ name: tool.name,
307
+ description: tool.description,
308
+ parameters: tool.parameters,
309
+ execute: async (args) => tool.handler(args)
310
+ });
303
311
  self.logger.debug(`Registered tool: ${tool.name} (for plugin: ${plugin.name})`);
304
312
  } else {
305
313
  self.logger.warn(`Cannot register tool ${tool.name}: ToolComponent not available`);
@@ -309,7 +317,9 @@ class PluginComponent extends BaseComponent {
309
317
  globalHookManager.register(hook.point, {
310
318
  name: `${plugin.name}:${hook.point}`,
311
319
  priority: hook.priority ?? plugin.priority,
312
- execute: hook.handler
320
+ execute: async (ctx) => {
321
+ await hook.handler(ctx);
322
+ }
313
323
  });
314
324
  self.logger.debug(`Registered hook: ${hook.point} (for plugin: ${plugin.name})`);
315
325
  },
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  withTimeout
3
- } from "./roy-agent-core-65qs21kf.js";
3
+ } from "./roy-agent-core-dcd1s7ct.js";
4
4
  import {
5
5
  truncateOutputInline
6
- } from "./roy-agent-core-6kygfd9x.js";
6
+ } from "./roy-agent-core-02n40zbs.js";
7
7
  import {
8
8
  AskUserError,
9
9
  init_workflow_hil
@@ -672,7 +672,7 @@ class AgentComponent extends BaseComponent {
672
672
  agentType: agent.config.type,
673
673
  model: context?.model ?? agent.config.model,
674
674
  traceId: context?.metadata?.traceId ?? context?.traceId,
675
- abort: abortController.signal
675
+ abort: context?.abort ? AbortSignal.any([abortController.signal, context.abort]) : abortController.signal
676
676
  };
677
677
  const result = {
678
678
  iterations: 0,
@@ -1405,13 +1405,14 @@ ${additionInfo}`
1405
1405
  if (!sessionId) {
1406
1406
  return;
1407
1407
  }
1408
+ let filtered;
1408
1409
  try {
1409
1410
  const sessionComponent = this.env.getComponent("session");
1410
1411
  if (!sessionComponent) {
1411
1412
  logger.warn("SessionComponent not found, skipping session recording");
1412
1413
  return;
1413
1414
  }
1414
- const filtered2 = [];
1415
+ filtered = [];
1415
1416
  for (const msg of allMessages) {
1416
1417
  if (msg.role === "system") {
1417
1418
  continue;
@@ -1424,12 +1425,12 @@ ${additionInfo}`
1424
1425
  continue;
1425
1426
  }
1426
1427
  }
1427
- filtered2.push(this.messageConverter.fromModelMessage(msg, { sessionID: sessionId }));
1428
+ filtered.push(this.messageConverter.fromModelMessage(msg, { sessionID: sessionId }));
1428
1429
  }
1429
- if (filtered2.length === 0) {
1430
+ if (filtered.length === 0) {
1430
1431
  return;
1431
1432
  }
1432
- await sessionComponent.addMessages(sessionId, filtered2);
1433
+ await sessionComponent.addMessages(sessionId, filtered);
1433
1434
  } catch (err) {
1434
1435
  const kind = err && typeof err === "object" && "kind" in err ? err.kind : undefined;
1435
1436
  if (kind === "lock_exhausted") {
@@ -530,7 +530,7 @@ roy-agent workflow list|add|search|get|tag|validate|update|remove|run|pause|resu
530
530
  | \`workflow get <name>\` | Get workflow definition details. |
531
531
  | \`workflow tag list [--sort-by name/usage_count/created_at] [--order desc/asc]\` | List all available tags with usage_count. \`\`\` |
532
532
  | \`workflow validate --file <path>\` | Validate a workflow YAML/JSON before adding. Always run this BEFORE \`workflow add\`. |
533
- | \`workflow run <name> [--input JSON] [--sync/--async]\` | Run a workflow. |
533
+ | \`workflow run <name> [--input JSON]\` | Run a workflow. |
534
534
  | \`workflow status <run-id>\` | Check workflow run status. |
535
535
  | \`workflow pause/resume/stop <run-id>\` | Control workflow execution. |
536
536
  | \`workflow update <id> [--tags] [--description]\` | Update workflow metadata. |
@@ -1022,7 +1022,7 @@ When reusing an existing task, you MUST follow these rules:
1022
1022
  - 返回值:workflow 定义(名称、版本、描述、入参 inputs schema、节点列表),或 run 的状态
1023
1023
 
1024
1024
  3. **\`workflow_run\`** — 运行 workflow
1025
- - 参数:\`workflow_name\`(必填),\`input\`(可选 JSON),\`sync\`(是否等待完成,默认 true),\`timeout\`(可选),\`session\`(resume 用,传 runId 即可),\`node_config\`(per-node 覆盖,Task #1974,JSON 对象 { "<nodeId>": { timeout: 5000 } })
1025
+ - 参数:\`workflow_name\`(必填),\`input\`(可选 JSON),\`timeout\`(可选),\`session\`(resume 用,传 runId 即可),\`node_config\`(per-node 覆盖,Task #1974,JSON 对象 { "<nodeId>": { timeout: 5000 } })
1026
1026
  - 返回值:run_id, status, output, error, duration_ms
1027
1027
 
1028
1028
  4. **\`workflow_run_status\`** — 查询 run 状态
@@ -1170,7 +1170,6 @@ plan/execute/verify 三阶段已完成的会跳过,未完成的会接上。
1170
1170
  | \`workflow_run\` 返回 \`status="failed"\` + error 含 "timeout" | stop → resume(同 session) |
1171
1171
  | \`workflow_run\` 返回 \`status="failed"\` + error 是业务错误(如校验失败) | **不** resume,原样返回给上游 |
1172
1172
  | \`workflow_run\` 返回 \`status="paused"\`(ask_user 中断) | 用 \`session\` resume + 用户 response |
1173
- | \`workflow_run\` 返回 \`status="running"\` 但实际卡死很久 | 先 \`workflow_run_status\` 确认;仍是 running 则 stop → resume |
1174
1173
  | \`workflow_run\` 抛异常(非 status=failed,是 throw) | stop → resume;resume 仍失败则开新 run |
1175
1174
 
1176
1175
  记住:**stop + resume 是默认动作**,不是例外。
@@ -7,19 +7,21 @@ import {
7
7
  import {
8
8
  AgentComponentAdapter,
9
9
  init_agent_component_adapter
10
- } from "./roy-agent-core-vf72n6qn.js";
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-ba6243hr.js";
24
+ } from "./roy-agent-core-0tfv18rf.js";
23
25
  import {
24
26
  WorkflowError,
25
27
  createWorkflowEvent,
@@ -1297,6 +1299,9 @@ class NodeRegistry {
1297
1299
  this.factories.set("ask_user", (definition) => {
1298
1300
  return new AskUserNode(definition);
1299
1301
  });
1302
+ this.factories.set("python_call", (definition) => {
1303
+ return new PythonCallNode(definition);
1304
+ });
1300
1305
  }
1301
1306
  register(type, factory) {
1302
1307
  this.factories.set(type, factory);
@@ -1323,6 +1328,7 @@ var init_node_registry = __esm(() => {
1323
1328
  init_agent_component_adapter();
1324
1329
  init_workflow_node();
1325
1330
  init_ask_user_node();
1331
+ init_python_call_node();
1326
1332
  });
1327
1333
 
1328
1334
  // src/env/workflow/utils/agent-session-metadata.ts
@@ -1395,7 +1401,16 @@ var init_engine = __esm(() => {
1395
1401
  const definition = "definition" in workflow ? workflow.definition : workflow;
1396
1402
  const workflowId = "id" in workflow ? workflow.id : `inline_${definition.name}`;
1397
1403
  const workflowName = definition.name;
1398
- const runId = this.generateRunId();
1404
+ let runId;
1405
+ if (options?.runId) {
1406
+ const reserved = options.runId;
1407
+ if (typeof reserved !== "string" || !reserved.startsWith("workflow_")) {
1408
+ throw new Error(`[WorkflowEngine.createSession] reserved runId must start with 'workflow_'; got ${JSON.stringify(reserved)}`);
1409
+ }
1410
+ runId = reserved;
1411
+ } else {
1412
+ runId = this.generateRunId();
1413
+ }
1399
1414
  const sessionId = this.getSessionId(runId);
1400
1415
  const metadata = {
1401
1416
  type: "workflow",
@@ -1642,11 +1657,8 @@ var init_engine = __esm(() => {
1642
1657
  logger3.error(`Workflow ${sessionId} scheduling error:`, error);
1643
1658
  this.failWorkflow(sessionState, error);
1644
1659
  });
1645
- if (options?.sync !== false) {
1646
- const wfTimeout = options?.timeout ?? sessionState.config.timeout ?? undefined;
1647
- return this.waitForCompletion(sessionId, wfTimeout);
1648
- }
1649
- return { runId, status: "running" };
1660
+ const wfTimeout = options?.timeout ?? sessionState.config.timeout ?? undefined;
1661
+ return this.waitForCompletion(sessionId, wfTimeout);
1650
1662
  }
1651
1663
  async runWorkflow(workflow, options) {
1652
1664
  const definition = "definition" in workflow ? workflow.definition : workflow;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AgentComponentAdapter,
3
3
  init_agent_component_adapter
4
- } from "./roy-agent-core-vf72n6qn.js";
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";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  builtInPrompts
3
- } from "./roy-agent-core-z9m38ytb.js";
3
+ } from "./roy-agent-core-4extx33r.js";
4
4
  import {
5
5
  getXDGPath
6
6
  } from "./roy-agent-core-qxnbvgwe.js";
@@ -8,7 +8,7 @@ import {
8
8
  BackgroundTaskManager,
9
9
  createDelegateTool,
10
10
  createStopTool
11
- } from "./roy-agent-core-7afcdb2m.js";
11
+ } from "./roy-agent-core-c94yan0b.js";
12
12
  import {
13
13
  SQLiteTaskStore,
14
14
  getDefaultTaskDbPath
@@ -148,7 +148,8 @@ class DefaultTagService {
148
148
  return this.store.searchTasks(query, limit);
149
149
  }
150
150
  async searchTasksByKeywords(keywords, options) {
151
- return this.store.searchTasksByKeywords(keywords, options);
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);
@@ -6,7 +6,7 @@ import {
6
6
  WorkflowEngine,
7
7
  exports_engine,
8
8
  init_engine
9
- } from "./roy-agent-core-kf3mw79k.js";
9
+ } from "./roy-agent-core-4y3hdqw2.js";
10
10
  import {
11
11
  askUserTool,
12
12
  createRunWorkflowTool,
@@ -18,7 +18,7 @@ import {
18
18
  createWorkflowSearchTool,
19
19
  createWorkflowTagListTool,
20
20
  createWorkflowValidateTool
21
- } from "./roy-agent-core-xnga0ksx.js";
21
+ } from "./roy-agent-core-smmr6wr3.js";
22
22
  import {
23
23
  WorkflowService
24
24
  } from "./roy-agent-core-b75jtybq.js";
@@ -141,7 +141,7 @@ class WorkflowComponent extends BaseComponent {
141
141
  if (!agentRunner && this._workflowEnv) {
142
142
  const agentComponent = this._workflowEnv.getComponent("agent");
143
143
  if (agentComponent) {
144
- const { AgentComponentAdapter } = await import("./roy-agent-core-y1m7c2v7.js");
144
+ const { AgentComponentAdapter } = await import("./roy-agent-core-8jmkghjc.js");
145
145
  agentRunner = new AgentComponentAdapter(agentComponent, {}, this.sessionComponent);
146
146
  }
147
147
  }
@@ -4,7 +4,7 @@ import {
4
4
  } from "./roy-agent-core-pzeexnhf.js";
5
5
  import {
6
6
  builtInPrompts
7
- } from "./roy-agent-core-z9m38ytb.js";
7
+ } from "./roy-agent-core-4extx33r.js";
8
8
  import {
9
9
  DEFAULT_SUBAGENT_PROMPT
10
10
  } from "./roy-agent-core-as3phr7n.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, env available:", !!options.env, "context:", options.context);
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) {
@@ -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
  };
@@ -6,7 +6,7 @@ import {
6
6
  } from "./roy-agent-core-8wd3qwx5.js";
7
7
  import {
8
8
  WORKFLOW_EXTRACT_PROMPT
9
- } from "./roy-agent-core-z9m38ytb.js";
9
+ } from "./roy-agent-core-4extx33r.js";
10
10
  import {
11
11
  TaskHookPoints
12
12
  } from "./roy-agent-core-1pg0fepg.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TaskTagPlugin,
3
3
  createLarkCliTaskNotifyHook
4
- } from "./roy-agent-core-aa0tdfb2.js";
4
+ } from "./roy-agent-core-jayzyg7h.js";
5
5
  import {
6
6
  envKeyToConfigKey
7
7
  } from "./roy-agent-core-qxhq8ven.js";
@@ -196,11 +196,14 @@ var timerHandler = {
196
196
  function readBountyIMConfig(config) {
197
197
  const topLevelAddress = config.address;
198
198
  const topLevelUrl = config.imServerUrl;
199
+ const topLevelEmail = config.email;
199
200
  const optionsAddress = config.options?.address;
200
201
  const optionsUrl = config.options?.imServerUrl;
202
+ const optionsEmail = config.options?.email;
201
203
  return {
202
204
  address: topLevelAddress || optionsAddress,
203
- imServerUrl: topLevelUrl || optionsUrl
205
+ imServerUrl: topLevelUrl || optionsUrl,
206
+ email: topLevelEmail || optionsEmail
204
207
  };
205
208
  }
206
209
  function readBountyIMTlsSkipVerify(config) {
@@ -213,20 +216,25 @@ function buildDefaultBountyIMSystemPrompt(input) {
213
216
  const server = input.imServerUrl || "ws://localhost:4005/ws";
214
217
  const httpScheme = server.startsWith("wss://") ? "https://" : "http://";
215
218
  const endpoint = server.replace(/^wss?:\/\//, httpScheme).replace(/\/ws$/, "");
219
+ const hasEmail = !!input.currentEmail;
220
+ const identity = input.currentEmail || input.currentAddress || "unknown";
221
+ const command = hasEmail ? `bounty com send --server-url ${endpoint} --from-email ${input.currentEmail} --to-email <recipient-email> --body "..."` : `bounty com send --endpoint ${endpoint} -f ${input.currentAddress} -t <to-address> -b "..."`;
222
+ const rule = hasEmail ? `规则:from-email 永远固定为 ${input.currentEmail}; to-email 是收到消息的 from 字段。` : `规则:to-address 总是收到的消息的 from 字段;from-address 永远固定为 ${input.currentAddress}。`;
223
+ const tlsCommand = hasEmail ? ` bounty com send --server-url ${endpoint} --from-email ${input.currentEmail} --to-email <recipient-email> --body "..."` : ` bounty com send --endpoint ${endpoint} -k -f ${input.currentAddress} -t <to-address> -b "..."`;
216
224
  return [
217
225
  "你正在通过 Bounty IM 与其他 agent 通信。",
218
226
  "",
219
- `你的 IM 地址:${input.currentAddress}`,
227
+ `你的 IM 身份:${identity}`,
220
228
  `IM Server:${endpoint} (k8s remote server)`,
221
229
  "",
222
- "回复收到的消息时,**必须**使用以下命令格式(注意 --endpoint 不可省略,省略会默认连 localhost:4000 而失败):",
230
+ "回复收到的消息时,**必须**使用以下命令格式(注意 --server-url / --endpoint 不可省略,省略会默认连 localhost:4000 而失败):",
223
231
  "",
224
- ` bounty com send --endpoint ${endpoint} -f ${input.currentAddress} -t <to-address> -b "<body>"`,
232
+ ` ${command}`,
225
233
  "",
226
234
  "如果遇到 TLS 错误(自签名证书),加 -k:",
227
- ` bounty com send --endpoint ${endpoint} -k -f ${input.currentAddress} -t <to-address> -b "<body>"`,
235
+ tlsCommand,
228
236
  "",
229
- `规则:to-address 总是收到的消息的 from 字段;from-address 永远固定为 ${input.currentAddress}。`
237
+ rule
230
238
  ].join(`
231
239
  `);
232
240
  }
@@ -247,15 +255,20 @@ class BountyIMInstance {
247
255
  constructor(config) {
248
256
  this.config = config;
249
257
  }
258
+ getCurrentIdentity() {
259
+ const { email, address } = readBountyIMConfig(this.config);
260
+ return { email, address };
261
+ }
250
262
  getCurrentAddress() {
251
- const { address } = readBountyIMConfig(this.config);
252
- return address || "unknown";
263
+ const { email, address } = readBountyIMConfig(this.config);
264
+ return address || email || "unknown";
253
265
  }
254
266
  getCurrentSystemPrompt() {
255
- const imServerUrl = readBountyIMConfig(this.config).imServerUrl;
267
+ const { email, address, imServerUrl } = readBountyIMConfig(this.config);
256
268
  return resolveBountyIMSystemPrompt({
257
269
  configSystemPrompt: this.config.systemPrompt,
258
- currentAddress: this.getCurrentAddress(),
270
+ currentAddress: address,
271
+ currentEmail: email,
259
272
  imServerUrl
260
273
  });
261
274
  }
@@ -266,12 +279,14 @@ class BountyIMInstance {
266
279
  if (this.status === "running")
267
280
  return;
268
281
  this.setStatus("starting");
269
- const { address, imServerUrl } = readBountyIMConfig(this.config);
282
+ const { email, address, imServerUrl } = readBountyIMConfig(this.config);
270
283
  if (!imServerUrl) {
271
284
  throw new Error("imServerUrl is required for bounty-im event source");
272
285
  }
273
286
  const wsUrl = new URL(imServerUrl);
274
- if (address) {
287
+ if (email) {
288
+ wsUrl.searchParams.set("email", email);
289
+ } else if (address) {
275
290
  wsUrl.searchParams.set("address", address);
276
291
  }
277
292
  console.log(`[BountyIM] Connecting to ${wsUrl.toString()}...`);
@@ -352,13 +367,16 @@ class BountyIMInstance {
352
367
  const eventType = `bounty-im.${evt.event || "message"}`;
353
368
  const fromAddress = msg.from || "unknown";
354
369
  const currentAddress = this.getCurrentAddress();
370
+ const currentIdentity = this.getCurrentIdentity();
371
+ const currentEmail = currentIdentity.email;
355
372
  const toAddress = msg.to || currentAddress;
356
- if (msg.to && msg.to !== currentAddress) {
357
- console.warn(`[BountyIM] Warning: message.to (${msg.to}) !== current address (${currentAddress})`);
373
+ const identityMatches = currentEmail ? msg.to === currentEmail || msg.to === currentAddress : msg.to === currentAddress;
374
+ if (msg.to && !identityMatches) {
375
+ console.warn(`[BountyIM] Warning: message.to (${msg.to}) !== current identity (${currentEmail || currentAddress})`);
358
376
  }
359
377
  const msgContent = msg.content;
360
378
  const content = msgContent?.type === "text" ? String(msgContent.body || "") : "";
361
- const { imServerUrl } = readBountyIMConfig(this.config);
379
+ const { imServerUrl, email } = readBountyIMConfig(this.config);
362
380
  const agent = this.config.handleRule?.agent;
363
381
  const metadata = {
364
382
  eventType,
@@ -366,10 +384,12 @@ class BountyIMInstance {
366
384
  chatId: toAddress,
367
385
  agent
368
386
  };
387
+ const replyCommandHint = email ? `bounty com send --server-url ${imServerUrl} --from-email ${email} --to-email <recipient-email> --body "..."` : `bounty com send -f ${toAddress} -t ${fromAddress} -b "回复内容"`;
388
+ const inlineReplyHint = email ? `bounty com send --server-url ${imServerUrl} --from-email ${email} --to-email ${fromAddress} --body "回复内容"` : `bounty com send -f ${toAddress} -t ${fromAddress} -b "回复内容"`;
369
389
  let recommendedAction;
370
390
  if (evt.event === "message") {
371
391
  recommendedAction = {
372
- action: `处理消息并通过 bounty com send 回复发件人。使用格式:bounty com send -f ${toAddress} -t ${fromAddress} -b "回复内容"`,
392
+ action: `处理消息并通过 bounty com send 回复发件人。使用格式:${replyCommandHint}`,
373
393
  replyTo: {
374
394
  chatId: fromAddress
375
395
  }
@@ -379,14 +399,14 @@ class BountyIMInstance {
379
399
  type: "bounty-im",
380
400
  chatId: fromAddress,
381
401
  params: {
382
- from: toAddress,
402
+ from: email || toAddress,
383
403
  to: fromAddress,
384
404
  imServerUrl
385
405
  }
386
406
  };
387
407
  const displayMessage = content ? `[From ${fromAddress}] ${content}
388
408
 
389
- \uD83D\uDCA1 回复: bounty com send -f ${toAddress} -t ${fromAddress} -b "回复内容"` : this.formatMessage(rawEvent);
409
+ \uD83D\uDCA1 回复: ${inlineReplyHint}` : this.formatMessage(rawEvent);
390
410
  const event = {
391
411
  id: `es-${this.config.id}-${Date.now()}`,
392
412
  type: "event-source.event.bounty-im",
@@ -451,11 +471,19 @@ var bountyIMHandler = {
451
471
  errors.push("EventSource ID is required");
452
472
  if (!config.name)
453
473
  errors.push("EventSource name is required");
454
- const { address, imServerUrl } = readBountyIMConfig(config);
455
- if (!address) {
456
- errors.push("Bounty IM address is required (top-level address or options.address)");
457
- } else if (!/^[\w-]+@[\w.-]+$/.test(address)) {
458
- errors.push("Bounty IM address format invalid (expected: agent-id@host)");
474
+ const { address, imServerUrl, email } = readBountyIMConfig(config);
475
+ if (!email && !address) {
476
+ errors.push("Bounty IM email or address is required (top-level email/address or options.email/address)");
477
+ }
478
+ if (email !== undefined && email !== "") {
479
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
480
+ errors.push("Bounty IM email format invalid (expected: user@domain.tld)");
481
+ }
482
+ }
483
+ if (address !== undefined && address !== "") {
484
+ if (!/^[\w-]+@[\w.-]+$/.test(address)) {
485
+ errors.push("Bounty IM address format invalid (expected: agent-id@host)");
486
+ }
459
487
  }
460
488
  if (!imServerUrl) {
461
489
  errors.push("Bounty IM imServerUrl is required (top-level imServerUrl or options.imServerUrl)");
@@ -808,8 +836,9 @@ class LarkCliInstance {
808
836
  }
809
837
  const event = rawEvent;
810
838
  let eventType;
811
- if (event.header?.event_type) {
812
- eventType = event.header.event_type;
839
+ const eventHeader = event.header;
840
+ if (eventHeader?.event_type) {
841
+ eventType = eventHeader.event_type;
813
842
  } else if (event.schema) {
814
843
  eventType = `lark.${event.schema}`;
815
844
  } else if (event.type) {
@@ -1310,6 +1339,9 @@ class EventSourceComponent extends BaseComponent {
1310
1339
  return Array.from(this.sources.values());
1311
1340
  }
1312
1341
  getStatus(id) {
1342
+ if (id === undefined) {
1343
+ return this._status;
1344
+ }
1313
1345
  return this.statuses.get(id);
1314
1346
  }
1315
1347
  getEventSourceStatus(id) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  invoke
3
- } from "./roy-agent-core-65qs21kf.js";
3
+ } from "./roy-agent-core-dcd1s7ct.js";
4
4
  import {
5
5
  ContextError,
6
6
  ErrorCodes
@@ -615,14 +615,23 @@ function createWorkflowSearchTool(workflowService) {
615
615
  init_logger();
616
616
  import { z as z8 } from "zod";
617
617
  var logger7 = createLogger("run-workflow-tool");
618
+ function synthFallbackRunId(now = Date.now()) {
619
+ return `workflow_${now}_fallback_${Math.random().toString(36).slice(2, 8)}`;
620
+ }
621
+ function attemptStopOnTimeout(workflowService, runId, reason) {
622
+ if (typeof workflowService.stopRun !== "function")
623
+ return;
624
+ workflowService.stopRun(runId, reason).catch((stopErr) => {
625
+ logger7.warn(`Failed to stop workflow ${runId} after timeout: ${stopErr instanceof Error ? stopErr.message : String(stopErr)}`);
626
+ });
627
+ }
618
628
  var RunWorkflowInputSchema = z8.object({
619
629
  workflow_name: z8.string().describe("Name of the workflow to run"),
620
630
  input: z8.record(z8.any()).optional().describe("Input to pass to the workflow"),
621
- sync: z8.boolean().default(true).describe("Wait for completion (default: true)"),
622
631
  timeout: z8.number().positive().optional().describe("Timeout in milliseconds (default: 1800000 = 30 min)"),
623
632
  node_config: z8.record(z8.string(), z8.record(z8.string(), z8.any())).optional().describe("Per-node runtime config overrides keyed by node id (Task #1974). " + "Shallow-merged into each node's effective config; input wins over nodeDef.config."),
624
633
  session: z8.string().regex(/^workflow_/, { message: "session must start with 'workflow_'" }).optional().describe("Existing session ID (workflow_xxx) to resume from. Mutually exclusive with fresh `input` runs.")
625
- });
634
+ }).strict();
626
635
  function createRunWorkflowTool(workflowService) {
627
636
  return {
628
637
  name: "workflow_run",
@@ -642,10 +651,9 @@ function createRunWorkflowTool(workflowService) {
642
651
  },
643
652
  execute: async (args, ctx) => {
644
653
  const startTime = Date.now();
645
- const { workflow_name, input, session, sync = true, timeout = 1800000, node_config } = args;
654
+ const { workflow_name, input, session, timeout = 1800000, node_config } = args;
646
655
  logger7.info(`Running workflow: ${workflow_name}`, {
647
656
  input,
648
- sync,
649
657
  timeout,
650
658
  ...session ? { resumeSession: session } : {}
651
659
  });
@@ -657,15 +665,23 @@ function createRunWorkflowTool(workflowService) {
657
665
  const parentSessionId = ctxSessionId ?? envSessionId;
658
666
  let timeoutHandle;
659
667
  let durationMs;
660
- let capturedRunId = session ? session : undefined;
668
+ const reservedRunId = synthFallbackRunId();
669
+ let timedOut = false;
670
+ let capturedByCallback = false;
671
+ let capturedRunId = session ? session : reservedRunId;
661
672
  const onSessionCreated = (sid) => {
662
673
  capturedRunId = sid;
674
+ capturedByCallback = true;
675
+ if (timedOut) {
676
+ attemptStopOnTimeout(workflowService, sid, "timed out");
677
+ }
663
678
  };
664
679
  try {
665
680
  let result;
681
+ const fallbackRunId = reservedRunId;
666
682
  const baseOptions = {
667
- sync,
668
683
  ...parentSessionId ? { parentSessionId } : {},
684
+ ...session ? {} : { runId: reservedRunId },
669
685
  onSessionCreated,
670
686
  ...node_config ? { nodeConfig: node_config } : {}
671
687
  };
@@ -683,6 +699,8 @@ function createRunWorkflowTool(workflowService) {
683
699
  executeCall(),
684
700
  new Promise((_, reject) => {
685
701
  timeoutHandle = setTimeout(() => {
702
+ timedOut = true;
703
+ attemptStopOnTimeout(workflowService, capturedRunId ?? reservedRunId, "timed out");
686
704
  reject(new Error("Workflow execution timed out"));
687
705
  }, timeout);
688
706
  })
@@ -694,46 +712,31 @@ function createRunWorkflowTool(workflowService) {
694
712
  clearTimeout(timeoutHandle);
695
713
  }
696
714
  durationMs = Date.now() - startTime;
697
- const finalRunId = capturedRunId ?? result.runId ?? "";
698
- if (sync) {
699
- return {
700
- success: result.status === "completed" || result.status === "paused",
701
- output: {
702
- run_id: finalRunId,
703
- status: result.status,
704
- output: result.output,
705
- error: result.error,
706
- duration_ms: result.durationMs || durationMs,
707
- ...result.pendingNodeId ? { pending_node_id: result.pendingNodeId } : {},
708
- ...result.query ? { query: result.query } : {},
709
- ...result.agentSessionId ? { agent_session_id: result.agentSessionId } : {}
710
- },
711
- metadata: {
712
- execution_time_ms: durationMs,
713
- run_id: finalRunId
714
- }
715
- };
716
- } else {
717
- return {
718
- success: true,
719
- output: {
720
- run_id: finalRunId,
721
- status: result.status,
722
- duration_ms: durationMs
723
- },
724
- metadata: {
725
- execution_time_ms: durationMs,
726
- run_id: finalRunId
727
- }
728
- };
729
- }
715
+ const finalRunId = capturedByCallback ? capturedRunId : result.runId ?? "";
716
+ return {
717
+ success: result.status === "completed" || result.status === "paused",
718
+ output: {
719
+ run_id: finalRunId,
720
+ status: result.status,
721
+ output: result.output,
722
+ error: result.error,
723
+ duration_ms: result.durationMs || durationMs,
724
+ ...result.pendingNodeId ? { pending_node_id: result.pendingNodeId } : {},
725
+ ...result.query ? { query: result.query } : {},
726
+ ...result.agentSessionId ? { agent_session_id: result.agentSessionId } : {}
727
+ },
728
+ metadata: {
729
+ execution_time_ms: durationMs,
730
+ run_id: finalRunId
731
+ }
732
+ };
730
733
  } catch (error) {
731
734
  if (timeoutHandle) {
732
735
  clearTimeout(timeoutHandle);
733
736
  }
734
737
  durationMs = Date.now() - startTime;
735
738
  const errorMessage = error instanceof Error ? error.message : String(error);
736
- const errorRunId = capturedRunId ?? "";
739
+ const errorRunId = capturedRunId ?? reservedRunId;
737
740
  if (errorMessage.includes("Workflow not found")) {
738
741
  return {
739
742
  success: false,