@ai-setting/roy-agent-core 1.7.71 → 1.7.75

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 (94) hide show
  1. package/dist/env/agent/agent-component.js +6 -6
  2. package/dist/env/agent/agent-registry.d.ts +4 -1
  3. package/dist/env/agent/index.js +6 -6
  4. package/dist/env/agent/summary-agent.d.ts +6 -15
  5. package/dist/env/agent/types.d.ts +0 -17
  6. package/dist/env/error-reply.d.ts +46 -0
  7. package/dist/env/error-reply.js +3 -1
  8. package/dist/env/event-source/event-source-handlers.js +12 -12
  9. package/dist/env/event-source/index.js +13 -13
  10. package/dist/env/index.js +30 -31
  11. package/dist/env/llm/index.js +2 -2
  12. package/dist/env/llm/orphan-toolcall.d.ts +107 -0
  13. package/dist/env/llm/temperature-policy.d.ts +107 -0
  14. package/dist/env/llm/types.d.ts +2 -2
  15. package/dist/env/plugin/index.js +3 -3
  16. package/dist/env/prompt/index.js +3 -3
  17. package/dist/env/prompt/prompts-index.d.ts +26 -4
  18. package/dist/env/session/__bench__/replay-compact.d.ts +137 -0
  19. package/dist/env/session/__bench__/run-replay.d.ts +15 -0
  20. package/dist/env/session/compact-checkpoint-content.d.ts +67 -0
  21. package/dist/env/session/index.d.ts +1 -0
  22. package/dist/env/session/index.js +4 -5
  23. package/dist/env/session/session-component.d.ts +47 -30
  24. package/dist/env/session/storage/index.js +3 -3
  25. package/dist/env/session/storage/sqlite.d.ts +8 -0
  26. package/dist/env/session/types.d.ts +45 -30
  27. package/dist/env/task/delegate/delegate-tool.d.ts +8 -0
  28. package/dist/env/task/delegate/delegate-tool.js +4 -4
  29. package/dist/env/task/delegate/index.js +4 -4
  30. package/dist/env/task/index.js +7 -7
  31. package/dist/env/task/mcp/index.js +2 -2
  32. package/dist/env/task/plugins/index.d.ts +3 -0
  33. package/dist/env/task/plugins/index.js +32 -9
  34. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +61 -0
  35. package/dist/env/task/plugins/lark-card-patch-notifier.js +6 -2
  36. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +42 -0
  37. package/dist/env/task/prompt/index.js +1 -1
  38. package/dist/env/task/prompt/task-discipline.d.ts +9 -3
  39. package/dist/env/task/tools/cancel-tool.d.ts +28 -0
  40. package/dist/env/task/tools/index.d.ts +1 -0
  41. package/dist/env/task/tools/index.js +4 -2
  42. package/dist/env/task/tools/task-types.d.ts +22 -0
  43. package/dist/env/tool/built-in/compact-session-tool.d.ts +120 -0
  44. package/dist/env/tool/built-in/index.d.ts +2 -0
  45. package/dist/env/tool/built-in/index.js +6 -2
  46. package/dist/env/tool/index.js +4 -4
  47. package/dist/env/workflow/engine/index.js +6 -7
  48. package/dist/env/workflow/index.js +15 -14
  49. package/dist/env/workflow/plugins/index.js +2 -2
  50. package/dist/env/workflow/tools/ask-user-tool.d.ts +88 -0
  51. package/dist/env/workflow/tools/index.js +11 -10
  52. package/dist/env/workflow/tools/run-workflow.d.ts +2 -2
  53. package/dist/env/workflow/types/index.js +2 -2
  54. package/dist/env/workflow/types/workflow-hil.d.ts +25 -1
  55. package/dist/env/workflow/utils/index.js +5 -5
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.js +33 -34
  58. package/dist/shared/@ai-setting/{roy-agent-core-hy3zn2q2.js → roy-agent-core-0qyrk5k0.js} +22 -2
  59. package/dist/shared/@ai-setting/{roy-agent-core-kmbct9yg.js → roy-agent-core-0snm5hc5.js} +1 -1
  60. package/dist/shared/@ai-setting/{roy-agent-core-4jjwewvw.js → roy-agent-core-11shp00p.js} +31 -10
  61. package/dist/shared/@ai-setting/{roy-agent-core-yr1mryb8.js → roy-agent-core-1v4g0d4x.js} +1 -1
  62. package/dist/shared/@ai-setting/{roy-agent-core-02j24hrg.js → roy-agent-core-264qexac.js} +71 -4
  63. package/dist/shared/@ai-setting/{roy-agent-core-9geem94g.js → roy-agent-core-29b6hc39.js} +1 -1
  64. package/dist/shared/@ai-setting/roy-agent-core-2akfd64z.js +374 -0
  65. package/dist/shared/@ai-setting/{roy-agent-core-zv8bswpw.js → roy-agent-core-3e6gg17d.js} +120 -28
  66. package/dist/shared/@ai-setting/{roy-agent-core-8cx7cfdg.js → roy-agent-core-3fh1f7s7.js} +2 -3
  67. package/dist/shared/@ai-setting/{roy-agent-core-mwa6apzx.js → roy-agent-core-4gr3k7qg.js} +17 -4
  68. package/dist/shared/@ai-setting/{roy-agent-core-zsbh9bfv.js → roy-agent-core-60jbrfye.js} +6 -6
  69. package/dist/shared/@ai-setting/{roy-agent-core-add1zqsh.js → roy-agent-core-6cy43nss.js} +22 -1
  70. package/dist/shared/@ai-setting/{roy-agent-core-mw2cen6s.js → roy-agent-core-6rh8k6ba.js} +5 -5
  71. package/dist/shared/@ai-setting/{roy-agent-core-kpg25s2q.js → roy-agent-core-8ez6mccb.js} +4 -2
  72. package/dist/shared/@ai-setting/{roy-agent-core-sjr1kmfz.js → roy-agent-core-acj8ag08.js} +1 -1
  73. package/dist/shared/@ai-setting/{roy-agent-core-mds8tqd0.js → roy-agent-core-cgtq6427.js} +1 -1
  74. package/dist/shared/@ai-setting/{roy-agent-core-9bshyqjn.js → roy-agent-core-ddyk1rc0.js} +183 -132
  75. package/dist/shared/@ai-setting/{roy-agent-core-hfr6bzts.js → roy-agent-core-djpq6yb0.js} +16 -7
  76. package/dist/shared/@ai-setting/{roy-agent-core-002g6nfk.js → roy-agent-core-dqxec05w.js} +7 -6
  77. package/dist/shared/@ai-setting/{roy-agent-core-07fvy74y.js → roy-agent-core-ed233k5z.js} +21 -7
  78. package/dist/shared/@ai-setting/{roy-agent-core-jrj30vq3.js → roy-agent-core-er070zjq.js} +1 -1
  79. package/dist/shared/@ai-setting/{roy-agent-core-40rv283c.js → roy-agent-core-fghc2m8z.js} +152 -2
  80. package/dist/shared/@ai-setting/{roy-agent-core-7sj9xs9c.js → roy-agent-core-hnyk1by7.js} +110 -2
  81. package/dist/shared/@ai-setting/{roy-agent-core-101qtkyc.js → roy-agent-core-jg65qnwa.js} +11 -8
  82. package/dist/shared/@ai-setting/{roy-agent-core-e25xkv53.js → roy-agent-core-mkc3zshb.js} +3 -1
  83. package/dist/shared/@ai-setting/{roy-agent-core-0cb6w0k7.js → roy-agent-core-mww0mnd9.js} +231 -27
  84. package/dist/shared/@ai-setting/{roy-agent-core-5repth1n.js → roy-agent-core-n9q1encx.js} +1 -1
  85. package/dist/shared/@ai-setting/{roy-agent-core-46rv4d7d.js → roy-agent-core-nmx9934r.js} +92 -2
  86. package/dist/shared/@ai-setting/{roy-agent-core-1w9y7mxj.js → roy-agent-core-p3zv1mx5.js} +1 -1
  87. package/dist/shared/@ai-setting/roy-agent-core-snvh4s2p.js +662 -0
  88. package/dist/shared/@ai-setting/{roy-agent-core-dt104qa1.js → roy-agent-core-v85n42ve.js} +1 -1
  89. package/dist/shared/@ai-setting/{roy-agent-core-37tj4y6a.js → roy-agent-core-v87njmq7.js} +3 -3
  90. package/dist/shared/@ai-setting/{roy-agent-core-k13pxrpk.js → roy-agent-core-y7x2qzwc.js} +553 -92
  91. package/package.json +1 -1
  92. package/dist/shared/@ai-setting/roy-agent-core-1gsbqesr.js +0 -1083
  93. package/dist/shared/@ai-setting/roy-agent-core-58804s3j.js +0 -155
  94. package/dist/shared/@ai-setting/roy-agent-core-ze068vc9.js +0 -37
@@ -1,28 +1,28 @@
1
1
  import {
2
2
  isToolUnavailableError,
3
3
  truncateOutputInline
4
- } from "./roy-agent-core-dt104qa1.js";
4
+ } from "./roy-agent-core-v85n42ve.js";
5
5
  import {
6
6
  AgentRegistry
7
- } from "./roy-agent-core-07fvy74y.js";
7
+ } from "./roy-agent-core-ed233k5z.js";
8
8
  import {
9
9
  enqueueToAddFakeUserMessage
10
10
  } from "./roy-agent-core-f77k3gqt.js";
11
11
  import {
12
12
  SessionMessageConverter
13
- } from "./roy-agent-core-kmbct9yg.js";
13
+ } from "./roy-agent-core-0snm5hc5.js";
14
14
  import {
15
15
  parseToolArgsStrict,
16
16
  safeParseToolArgs,
17
17
  withTimeout
18
- } from "./roy-agent-core-40rv283c.js";
18
+ } from "./roy-agent-core-fghc2m8z.js";
19
19
  import {
20
20
  getCurrentBgTaskId
21
21
  } from "./roy-agent-core-c1v263jn.js";
22
22
  import {
23
23
  AskUserError,
24
24
  init_workflow_hil
25
- } from "./roy-agent-core-e25xkv53.js";
25
+ } from "./roy-agent-core-mkc3zshb.js";
26
26
  import {
27
27
  ContextError
28
28
  } from "./roy-agent-core-ctdhjv68.js";
@@ -1393,6 +1393,7 @@ class AgentComponent extends BaseComponent {
1393
1393
  role: "assistant",
1394
1394
  content: assistantParts
1395
1395
  });
1396
+ this.addRemainingToolResults(hookCtx, llmOutput.toolCalls, 0, "[Tool result missing: run interrupted] ContextError");
1396
1397
  } else if (llmOutput.content) {
1397
1398
  const assistantParts = [];
1398
1399
  if (llmOutput.content) {
@@ -1438,7 +1439,9 @@ class AgentComponent extends BaseComponent {
1438
1439
  query: error.query,
1439
1440
  sessionId: error.sessionId,
1440
1441
  nodeId: error.nodeId,
1441
- nodeType: error.nodeType
1442
+ nodeType: error.nodeType,
1443
+ agentSessionId: error.agentSessionId,
1444
+ delivered: error.delivered === true
1442
1445
  };
1443
1446
  result.error = `__ASK_USER_ERROR__:${JSON.stringify(errorInfo)}`;
1444
1447
  } else {
@@ -1979,7 +1982,13 @@ class AgentComponent extends BaseComponent {
1979
1982
  fallbackModel: ctx.context.fallbackModel,
1980
1983
  ...effectiveFallbackModels.length > 0 ? { fallbackModels: effectiveFallbackModels } : fallbackExplicitlySet ? { fallbackModels: [] } : {},
1981
1984
  workdir: ctx.context.metadata?.workdir,
1982
- metadata: ctx.context.metadata
1985
+ metadata: {
1986
+ ...ctx.context.metadata ?? {},
1987
+ ...ctx._runId ? { runId: ctx._runId } : {},
1988
+ ...ctx.context.sessionId ? { sessionId: ctx.context.sessionId } : {},
1989
+ ...ctx.context.agentType ? { agentType: ctx.context.agentType } : {},
1990
+ ...ctx.agent?.name ? { agentName: ctx.agent.name } : {}
1991
+ }
1983
1992
  };
1984
1993
  const request = {
1985
1994
  name: toolName,
@@ -2,13 +2,10 @@ import {
2
2
  createSubmitJsonOutputTool,
3
3
  init_submit_json_output_tool
4
4
  } from "./roy-agent-core-1db4vpc6.js";
5
- import {
6
- init_loose_json
7
- } from "./roy-agent-core-ze068vc9.js";
8
5
  import {
9
6
  AskUserError,
10
7
  init_workflow_hil
11
- } from "./roy-agent-core-e25xkv53.js";
8
+ } from "./roy-agent-core-mkc3zshb.js";
12
9
  import {
13
10
  globalHookManager,
14
11
  init_global_hook_manager
@@ -27,6 +24,9 @@ import {
27
24
  __legacyDecorateClassTS
28
25
  } from "./roy-agent-core-fs0mn2jk.js";
29
26
 
27
+ // src/utils/loose-json.ts
28
+ var init_loose_json = () => {};
29
+
30
30
  // src/env/workflow/plugins/workflow-json-output-plugin.ts
31
31
  function extractLastAssistantText(messages) {
32
32
  if (!messages || messages.length === 0) {
@@ -324,7 +324,8 @@ var init_agent_component_adapter = __esm(() => {
324
324
  runId,
325
325
  nodeId,
326
326
  sessionId: agentSessionId,
327
- outputSchema
327
+ outputSchema,
328
+ askUserDelivery: "off"
328
329
  };
329
330
  if (outputSchema) {
330
331
  context.pluginEnabled = {
@@ -359,7 +360,7 @@ var init_agent_component_adapter = __esm(() => {
359
360
  if (result.error?.startsWith("__ASK_USER_ERROR__:")) {
360
361
  const jsonStr = result.error.substring("__ASK_USER_ERROR__:".length);
361
362
  const errorInfo = JSON.parse(jsonStr);
362
- throw new AskUserError(runId, errorInfo.sessionId, errorInfo.nodeId, errorInfo.nodeType || "agent", errorInfo.query);
363
+ throw new AskUserError(runId, errorInfo.sessionId, errorInfo.nodeId, errorInfo.nodeType || "agent", errorInfo.query, errorInfo.agentSessionId, undefined, errorInfo.delivered === true);
363
364
  }
364
365
  const duration = Date.now() - startTime;
365
366
  if (agentSessionId) {
@@ -270,34 +270,48 @@ class AgentRegistry {
270
270
  }
271
271
  registerCompactAgent() {
272
272
  const name = "compact";
273
- if (this.agents.has(name))
274
- return;
275
273
  const agent = {
276
274
  name,
277
275
  type: "sub",
278
276
  description: "压缩子智能体 — 从父 session 的历史消息中提取结构化 checkpoint JSON",
279
277
  systemPromptRef: "compact",
280
- allowedTools: ["workflow_submit_output"],
278
+ allowedTools: [],
281
279
  deniedTools: [],
282
280
  maxIterations: 3,
283
281
  filterHistory: false
284
282
  };
283
+ const existing = this.agents.get(name);
284
+ if (existing) {
285
+ this.agents.set(name, {
286
+ ...agent,
287
+ description: existing.description ?? agent.description
288
+ });
289
+ logger.info(`[AgentRegistry] Built-in compact agent overrode user-level definition ` + `(tool whitelist enforced: allowedTools=[])`);
290
+ return;
291
+ }
285
292
  this.agents.set(name, agent);
286
293
  }
287
294
  registerCompactHintAgent() {
288
295
  const name = "compact-hint";
289
- if (this.agents.has(name))
290
- return;
291
296
  const agent = {
292
297
  name,
293
298
  type: "sub",
294
299
  description: "压缩提示子智能体 — 分析父 session 历史,生成场景化压缩指导提示",
295
300
  systemPromptRef: "compact-hint",
296
- allowedTools: ["workflow_submit_output"],
301
+ allowedTools: [],
297
302
  deniedTools: [],
298
303
  maxIterations: 3,
299
304
  filterHistory: false
300
305
  };
306
+ const existing = this.agents.get(name);
307
+ if (existing) {
308
+ this.agents.set(name, {
309
+ ...agent,
310
+ description: existing.description ?? agent.description
311
+ });
312
+ logger.info(`[AgentRegistry] Built-in compact-hint agent overrode user-level definition ` + `(tool whitelist enforced: allowedTools=[])`);
313
+ return;
314
+ }
301
315
  this.agents.set(name, agent);
302
316
  }
303
317
  registerRoyAgent() {
@@ -309,7 +323,7 @@ class AgentRegistry {
309
323
  type: "sub",
310
324
  description: "Roy — versatile AI assistant inspired by Jarvis. Built-in general-purpose subagent for coding, research, planning, and complex multi-step tasks. Always available without `agent add roy`.",
311
325
  systemPromptRef: "roy",
312
- deniedTools: ["delegate_task", "stop_task"]
326
+ deniedTools: ["delegate_task", "stop_task", "task_complete", "task_cancel"]
313
327
  };
314
328
  this.agents.set(name, agent);
315
329
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AskUserError,
3
3
  init_workflow_hil
4
- } from "./roy-agent-core-e25xkv53.js";
4
+ } from "./roy-agent-core-mkc3zshb.js";
5
5
  import {
6
6
  __esm,
7
7
  __require
@@ -838,6 +838,126 @@ import { createGoogleGenerativeAI } from "@ai-sdk/google";
838
838
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
839
839
  init_logger();
840
840
 
841
+ // src/env/llm/orphan-toolcall.ts
842
+ var ORPHAN_PLACEHOLDER_PREFIX = "[Tool result missing: run interrupted]";
843
+ function toolNameOf(tc) {
844
+ return tc.function?.name || tc.name || "unknown";
845
+ }
846
+ function extractPendingEvent(msg, events) {
847
+ const added = [];
848
+ const removed = [];
849
+ const toolCalls = msg.toolCalls;
850
+ if (Array.isArray(toolCalls)) {
851
+ for (const tc of toolCalls) {
852
+ if (tc?.id)
853
+ added.push({ id: tc.id, name: toolNameOf(tc) });
854
+ }
855
+ }
856
+ const content = msg.content;
857
+ if (Array.isArray(content)) {
858
+ for (const part of content) {
859
+ if (!part)
860
+ continue;
861
+ if (part.type === "tool-call" && typeof part.toolCallId === "string") {
862
+ added.push({
863
+ id: part.toolCallId,
864
+ name: typeof part.toolName === "string" ? part.toolName : "unknown"
865
+ });
866
+ }
867
+ if (part.type === "tool-result" && typeof part.toolCallId === "string") {
868
+ removed.push(part.toolCallId);
869
+ }
870
+ }
871
+ }
872
+ if (msg.role === "tool" && typeof msg.toolCallId === "string") {
873
+ removed.push(msg.toolCallId);
874
+ }
875
+ events.push({ added, removed });
876
+ }
877
+ function collectOrphans(roles, events) {
878
+ const pending = new Map;
879
+ const orphans = [];
880
+ const reported = new Set;
881
+ for (let index = 0;index < roles.length; index++) {
882
+ const { added, removed } = events[index];
883
+ for (const id of removed)
884
+ pending.delete(id);
885
+ for (const { id, name } of added) {
886
+ if (!pending.has(id))
887
+ pending.set(id, name);
888
+ }
889
+ const role = roles[index];
890
+ if ((role === "user" || role === "system") && pending.size > 0) {
891
+ for (const [id, name] of pending) {
892
+ if (!reported.has(id)) {
893
+ reported.add(id);
894
+ orphans.push({ toolCallId: id, toolName: name, messageIndex: index });
895
+ }
896
+ }
897
+ pending.clear();
898
+ }
899
+ }
900
+ for (const [id, name] of pending) {
901
+ if (!reported.has(id)) {
902
+ reported.add(id);
903
+ orphans.push({
904
+ toolCallId: id,
905
+ toolName: name,
906
+ messageIndex: roles.length
907
+ });
908
+ }
909
+ }
910
+ return orphans;
911
+ }
912
+ function repairOrphanSdkMessages(messages, options) {
913
+ const events = [];
914
+ const roles = [];
915
+ for (const msg of messages) {
916
+ roles.push(msg.role);
917
+ extractPendingEvent(msg, events);
918
+ }
919
+ const orphans = collectOrphans(roles, events);
920
+ if (orphans.length === 0) {
921
+ return { messages, orphans: [], repairedCount: 0 };
922
+ }
923
+ const reason = options?.reason ?? "context limit or crash";
924
+ const value = JSON.stringify({ error: `${ORPHAN_PLACEHOLDER_PREFIX} ${reason}` });
925
+ const byIndex = new Map;
926
+ for (const orphan of orphans) {
927
+ const list = byIndex.get(orphan.messageIndex) ?? [];
928
+ list.push(orphan);
929
+ byIndex.set(orphan.messageIndex, list);
930
+ }
931
+ const result = [];
932
+ const endGroup = byIndex.get(messages.length);
933
+ const placeholder = (orphan) => ({
934
+ role: "tool",
935
+ content: [
936
+ {
937
+ type: "tool-result",
938
+ toolCallId: orphan.toolCallId,
939
+ toolName: orphan.toolName,
940
+ output: { type: "text", value }
941
+ }
942
+ ]
943
+ });
944
+ messages.forEach((msg, index) => {
945
+ const group = byIndex.get(index);
946
+ if (group) {
947
+ for (const orphan of group) {
948
+ result.push(placeholder(orphan));
949
+ }
950
+ }
951
+ result.push(msg);
952
+ });
953
+ if (endGroup) {
954
+ for (const orphan of endGroup) {
955
+ result.push(placeholder(orphan));
956
+ }
957
+ }
958
+ return { messages: result, orphans, repairedCount: orphans.length };
959
+ }
960
+
841
961
  // src/env/llm/llm-cache.ts
842
962
  function isOpenAICompatibleProvider(providerName) {
843
963
  return providerName === "openai-compatible" || providerName === "openai" || providerName.startsWith("minimax") || providerName === "deepseek" || providerName.startsWith("deepseek") || providerName === "glm" || providerName.startsWith("glm");
@@ -1207,6 +1327,24 @@ function convertToSDKMessages(messages, options) {
1207
1327
  };
1208
1328
  }
1209
1329
  if (msg.role === "tool") {
1330
+ if (!msg.toolCallId && Array.isArray(msg.content)) {
1331
+ const part = msg.content.find((p) => p?.type === "tool-result" && typeof p.toolCallId === "string");
1332
+ if (part) {
1333
+ const convertedPart = {
1334
+ role: "tool",
1335
+ content: [{
1336
+ type: "tool-result",
1337
+ toolCallId: part.toolCallId,
1338
+ toolName: part.toolName || "unknown",
1339
+ output: {
1340
+ type: "text",
1341
+ value: typeof part.output === "string" ? part.output : JSON.stringify(part.output ?? "")
1342
+ }
1343
+ }]
1344
+ };
1345
+ return convertedPart;
1346
+ }
1347
+ }
1210
1348
  if (!msg.toolCallId) {
1211
1349
  logger3.warn("Tool message missing toolCallId", { msg });
1212
1350
  }
@@ -1410,6 +1548,12 @@ async function invoke(config, options, ctx) {
1410
1548
  sessionId: options.context?.sessionId,
1411
1549
  provider: providerId
1412
1550
  });
1551
+ const orphanRepair = repairOrphanSdkMessages(messages, {
1552
+ reason: "repaired before AI SDK fallback (Task #4992 defence 2)"
1553
+ });
1554
+ if (orphanRepair.repairedCount > 0) {
1555
+ logger3.warn(`[invoke] Repaired ${orphanRepair.repairedCount} orphan tool-call(s) before AI SDK fallback: ` + orphanRepair.orphans.map((o) => o.toolCallId).join(", "));
1556
+ }
1413
1557
  const toolCallIds = new Set;
1414
1558
  const assistantToolCalls = [];
1415
1559
  const toolResultIds = [];
@@ -1457,7 +1601,7 @@ async function invoke(config, options, ctx) {
1457
1601
  }
1458
1602
  const streamTextOptions = {
1459
1603
  model: provider.languageModel(modelId),
1460
- messages,
1604
+ messages: orphanRepair.messages,
1461
1605
  tools,
1462
1606
  temperature: providerOptions.temperature,
1463
1607
  maxTokens: providerOptions.maxTokens,
@@ -1704,6 +1848,12 @@ async function invokeNonStream(config, options, ctx) {
1704
1848
  sessionId: options.context?.sessionId,
1705
1849
  provider: providerId
1706
1850
  });
1851
+ const orphanRepair = repairOrphanSdkMessages(messages, {
1852
+ reason: "repaired before AI SDK generateText (Task #4992 defence 2)"
1853
+ });
1854
+ if (orphanRepair.repairedCount > 0) {
1855
+ logger3.warn(`[invokeNonStream] Repaired ${orphanRepair.repairedCount} orphan tool-call(s): ` + orphanRepair.orphans.map((o) => o.toolCallId).join(", "));
1856
+ }
1707
1857
  const tools = options.tools && options.tools.length > 0 ? convertToolsToSDK(options.tools, providerId) : undefined;
1708
1858
  const providerOptions = generateProviderOptions(providerId, {
1709
1859
  temperature: options.temperature,
@@ -1713,7 +1863,7 @@ async function invokeNonStream(config, options, ctx) {
1713
1863
  const genTextFn = generateText;
1714
1864
  const result = await genTextFn({
1715
1865
  model: provider.languageModel(modelId),
1716
- messages,
1866
+ messages: orphanRepair.messages,
1717
1867
  tools,
1718
1868
  temperature: providerOptions.temperature,
1719
1869
  maxTokens: providerOptions.maxTokens,
@@ -7,7 +7,7 @@ import {
7
7
  TASK_COMPLETE_VERIFY_DUTY,
8
8
  TASK_COMPLETION_HINT_ANCHOR,
9
9
  TASK_DISCIPLINE_GOALS
10
- } from "./roy-agent-core-4jjwewvw.js";
10
+ } from "./roy-agent-core-11shp00p.js";
11
11
  import {
12
12
  getEnvContextOrEmpty,
13
13
  init_env_context,
@@ -103,6 +103,9 @@ var DeleteTaskToolSchema = z.object({
103
103
  var CompleteTaskToolSchema = withLegacyTaskId({
104
104
  deliverable_storage_path: z.string().optional().describe("交付物的寻址地址(路径 / URL / 分支名等)。" + "调用本工具前,请先读取任务的 goal(description)和 delivery(goals_and_expected_deliverables)栏位," + "建议验证目标和交付物是否符合预期,然后将实际交付物的寻址地址填入此参数。")
105
105
  });
106
+ var CancelTaskToolSchema = withLegacyTaskId({
107
+ reason: z.string().optional().describe("取消原因(可选)。会写入该任务的 operation 记录,便于事后追溯验收结论。")
108
+ });
106
109
  var BatchArchiveToolSchema = z.object({
107
110
  task_ids: z.array(z.number()).describe("要归档的任务 ID 列表"),
108
111
  reason: z.string().optional().describe("归档原因(可选,会写入每个任务的 operation 记录)")
@@ -828,6 +831,22 @@ function updateTaskTool(taskComponent) {
828
831
  };
829
832
  }
830
833
  }
834
+ const callerAgentType = ctx.metadata?.agentType ?? undefined;
835
+ const callerAgentName = ctx.metadata?.agentName ?? undefined;
836
+ if (params.status === "completed" && callerAgentType === "sub") {
837
+ const agentComponent = taskComponent.env?.getComponent?.("agent");
838
+ const callerAgent = callerAgentName && agentComponent?.getAgent ? agentComponent.getAgent(callerAgentName) : undefined;
839
+ const cfg = callerAgent?.config;
840
+ const hasCompletePermission = cfg ? Array.isArray(cfg.allowedTools) && cfg.allowedTools.includes("task_complete") || !Array.isArray(cfg.allowedTools) && !(Array.isArray(cfg.deniedTools) && cfg.deniedTools.includes("task_complete")) : false;
841
+ if (!hasCompletePermission) {
842
+ return {
843
+ success: false,
844
+ output: "",
845
+ error: `Cannot set status="completed" without acceptance permission (Task #5007 — 验收权分离). ` + `执行方只能推进进度:用 task_update(progress=100) 表示"我这边干完了",` + `再 task_operation_create 记录结论,然后结束返回;` + `任务终态必须由验收方(default agent / 主智能体)调用 task_complete 落定。`,
846
+ metadata: { execution_time_ms: 0 }
847
+ };
848
+ }
849
+ }
831
850
  const task = await taskComponent.updateTask(params.task_id, {
832
851
  title: params.title,
833
852
  description: params.description,
@@ -1020,6 +1039,95 @@ Deliverable: (no path provided)`,
1020
1039
  }
1021
1040
  };
1022
1041
  }
1042
+ // src/env/task/tools/cancel-tool.ts
1043
+ init_logger();
1044
+ var logger5 = createLogger("tool:cancel-task");
1045
+ var NON_CANCELLABLE_STATUSES = new Set(["completed", "cancelled", "archived"]);
1046
+ var TASK_CANCEL_DESCRIPTION = `
1047
+ Cancel a task (task.status = "cancelled"). This is the **acceptance-side** cancel:
1048
+ use it when the work is abandoned, the user asked to stop, or acceptance failed.
1049
+
1050
+ **When to use**:
1051
+ - The user asks to cancel / stop / drop the task
1052
+ - Acceptance failed and re-delegation is not worth it
1053
+ - The sub-agent timed out or went off-track and the task is no longer relevant
1054
+
1055
+ **What it does NOT do**:
1056
+ - It does **NOT** stop a running background process — that is \`stop_task\`
1057
+ (which takes a bg-process id from \`delegate_task\`). If the sub-agent is still
1058
+ running, call \`stop_task\` **first**, then \`task_cancel\` to close the record.
1059
+
1060
+ **Side effects**:
1061
+ - Sets \`status = "cancelled"\` and fires the task terminal-state hooks
1062
+ (notifications / card state machine converge to the cancelled state).
1063
+ - Writes an operation record so the cancellation reason stays auditable.
1064
+ - A cancelled task can be reopened later with \`task_update(status="active")\`.
1065
+
1066
+ **Parameters**:
1067
+ - \`task_id\` (required): the task to cancel
1068
+ - \`reason\` (optional): why it is being cancelled — recorded in the operation trail
1069
+ `.trim();
1070
+ function cancelTaskTool(taskComponent) {
1071
+ return {
1072
+ name: "task_cancel",
1073
+ description: TASK_CANCEL_DESCRIPTION,
1074
+ parameters: CancelTaskToolSchema,
1075
+ execute: async (args, ctx) => {
1076
+ const params = CancelTaskToolSchema.parse(args);
1077
+ const sessionId = ctx.session_id || "unknown";
1078
+ try {
1079
+ const current = await taskComponent.getTask(params.task_id);
1080
+ if (!current) {
1081
+ return {
1082
+ success: false,
1083
+ output: "",
1084
+ error: `Task not found: ${params.task_id}`,
1085
+ metadata: { execution_time_ms: 0 }
1086
+ };
1087
+ }
1088
+ if (NON_CANCELLABLE_STATUSES.has(current.status)) {
1089
+ return {
1090
+ success: false,
1091
+ output: "",
1092
+ error: `Cannot cancel task #${params.task_id}: it is already in terminal status "${current.status}".`,
1093
+ metadata: { execution_time_ms: 0, task_id: params.task_id }
1094
+ };
1095
+ }
1096
+ const task = await taskComponent.updateTask(params.task_id, {
1097
+ status: "cancelled"
1098
+ });
1099
+ if (!task) {
1100
+ return {
1101
+ success: false,
1102
+ output: "",
1103
+ error: `Task not found: ${params.task_id}`,
1104
+ metadata: { execution_time_ms: 0 }
1105
+ };
1106
+ }
1107
+ await taskComponent.createOperation({
1108
+ taskId: params.task_id,
1109
+ sessionId,
1110
+ milestoneType: "decision",
1111
+ milestoneTitle: `Task cancelled: #${params.task_id}`,
1112
+ milestoneDescription: params.reason ? `Cancelled by acceptance side. Reason: ${params.reason}` : "Cancelled by acceptance side (no reason given)."
1113
+ });
1114
+ logger5.info(`[cancelTask] Task #${params.task_id} cancelled (previous status: ${current.status})`);
1115
+ return {
1116
+ success: true,
1117
+ output: `Task cancelled: #${params.task_id} - ${task.title}`,
1118
+ metadata: { execution_time_ms: 0, task }
1119
+ };
1120
+ } catch (error) {
1121
+ return {
1122
+ success: false,
1123
+ output: "",
1124
+ error: error instanceof Error ? error.message : String(error),
1125
+ metadata: { execution_time_ms: 0 }
1126
+ };
1127
+ }
1128
+ }
1129
+ };
1130
+ }
1023
1131
  // src/env/task/tools/task-tree-tool.ts
1024
1132
  function compareTasksDesc(a, b) {
1025
1133
  if (a.updatedAt < b.updatedAt)
@@ -1158,4 +1266,4 @@ function treeTasksTool(taskComponent) {
1158
1266
  }
1159
1267
  };
1160
1268
  }
1161
- export { createTaskTool, getTaskTool, listTasksTool, searchTasksTool, batchArchiveTaskTool, batchDeleteTaskTool, updateTaskTool, deleteTaskTool, completeTaskTool, treeTasksTool };
1269
+ export { createTaskTool, getTaskTool, listTasksTool, searchTasksTool, batchArchiveTaskTool, batchDeleteTaskTool, updateTaskTool, deleteTaskTool, completeTaskTool, cancelTaskTool, treeTasksTool };
@@ -7,13 +7,13 @@ import {
7
7
  } from "./roy-agent-core-na18zwj0.js";
8
8
  import {
9
9
  builtInPrompts
10
- } from "./roy-agent-core-zv8bswpw.js";
10
+ } from "./roy-agent-core-3e6gg17d.js";
11
11
  import {
12
12
  TaskHookPoints
13
13
  } from "./roy-agent-core-1pg0fepg.js";
14
14
  import {
15
15
  DEFAULT_SUBAGENT_PROMPT
16
- } from "./roy-agent-core-07fvy74y.js";
16
+ } from "./roy-agent-core-ed233k5z.js";
17
17
  import {
18
18
  runWithBgTaskIdAsync
19
19
  } from "./roy-agent-core-c1v263jn.js";
@@ -819,7 +819,7 @@ var builtInSubAgents = [
819
819
  mode: "subagent",
820
820
  description: "Versatile AI assistant inspired by Jarvis — intelligent, proactive, and capable. Use for general-purpose tasks, coding, research, and complex multi-step work. Always available (built-in) without `agent add roy`.",
821
821
  promptOverride: builtInPrompts["roy"],
822
- deniedTools: ["delegate_task", "stop_task"]
822
+ deniedTools: ["delegate_task", "stop_task", "task_complete", "task_cancel"]
823
823
  }
824
824
  ];
825
825
  function getSubAgentSpec(id) {
@@ -1089,6 +1089,7 @@ class BackgroundTaskManager {
1089
1089
  nodeId: e.nodeId,
1090
1090
  nodeType: e.nodeType || "agent",
1091
1091
  agentSessionId: e.agentSessionId,
1092
+ delivered: e.delivered === true,
1092
1093
  rawErrorMessage
1093
1094
  };
1094
1095
  }
@@ -1103,6 +1104,7 @@ class BackgroundTaskManager {
1103
1104
  nodeId: parsed.nodeId,
1104
1105
  nodeType: parsed.nodeType || "agent",
1105
1106
  agentSessionId: parsed.agentSessionId,
1107
+ delivered: parsed.delivered === true,
1106
1108
  rawErrorMessage
1107
1109
  };
1108
1110
  } catch (parseErr) {
@@ -1267,9 +1269,9 @@ class BackgroundTaskManager {
1267
1269
  task.lastUpdatedAt = completedAt;
1268
1270
  task.result = result;
1269
1271
  const executionTimeMs = task.completedAt - task.startedAt;
1270
- task.associatedTaskId = getCurrentTaskId();
1272
+ task.associatedTaskId = await this.resolveAssociatedTaskIdBySubSession(task.subSessionId);
1271
1273
  if (task.associatedTaskId === undefined) {
1272
- task.associatedTaskId = await this.resolveAssociatedTaskIdBySubSession(task.subSessionId);
1274
+ task.associatedTaskId = getCurrentTaskId();
1273
1275
  }
1274
1276
  const completedPayload = {
1275
1277
  backgroundTaskId: task.id,
@@ -1294,9 +1296,9 @@ class BackgroundTaskManager {
1294
1296
  const completedAt = Date.now();
1295
1297
  task.completedAt = completedAt;
1296
1298
  task.lastUpdatedAt = completedAt;
1297
- task.associatedTaskId = getCurrentTaskId();
1299
+ task.associatedTaskId = await this.resolveAssociatedTaskIdBySubSession(task.subSessionId);
1298
1300
  if (task.associatedTaskId === undefined) {
1299
- task.associatedTaskId = await this.resolveAssociatedTaskIdBySubSession(task.subSessionId);
1301
+ task.associatedTaskId = getCurrentTaskId();
1300
1302
  }
1301
1303
  if (askUserInfo) {
1302
1304
  task.status = "awaiting-user";
@@ -1313,6 +1315,7 @@ class BackgroundTaskManager {
1313
1315
  nodeId: askUserInfo.nodeId,
1314
1316
  nodeType: askUserInfo.nodeType,
1315
1317
  agentSessionId: askUserInfo.agentSessionId,
1318
+ delivered: askUserInfo.delivered === true,
1316
1319
  error: askUserInfo.rawErrorMessage,
1317
1320
  executionTimeMs,
1318
1321
  hints: [
@@ -1399,7 +1402,7 @@ class BackgroundTaskManager {
1399
1402
  return;
1400
1403
  }
1401
1404
  const currentTaskId = getCurrentTaskId();
1402
- if (currentTaskId !== undefined) {
1405
+ if (currentTaskId !== undefined && task.associatedTaskId === undefined) {
1403
1406
  task.associatedTaskId = currentTaskId;
1404
1407
  }
1405
1408
  task.lastUpdatedAt = Date.now();
@@ -35,9 +35,10 @@ var init_workflow_hil = __esm(() => {
35
35
  query;
36
36
  agentSessionId;
37
37
  timestamp;
38
+ delivered;
38
39
  type = "ask-user";
39
40
  name = "AskUserError";
40
- constructor(runId, sessionId, nodeId, nodeType, query, agentSessionId, timestamp = Date.now()) {
41
+ constructor(runId, sessionId, nodeId, nodeType, query, agentSessionId, timestamp = Date.now(), delivered = false) {
41
42
  super(`[${nodeType}:${nodeId}] Ask user: ${query}`);
42
43
  this.runId = runId;
43
44
  this.sessionId = sessionId;
@@ -46,6 +47,7 @@ var init_workflow_hil = __esm(() => {
46
47
  this.query = query;
47
48
  this.agentSessionId = agentSessionId;
48
49
  this.timestamp = timestamp;
50
+ this.delivered = delivered;
49
51
  }
50
52
  toEvent() {
51
53
  return {