@renxqoo/renx-code 0.0.4 → 0.0.5

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 (209) hide show
  1. package/bin/renx.cjs +16 -0
  2. package/package.json +2 -45
  3. package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
  4. package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
  5. package/src/agent/runtime/runtime.test.ts +7 -4
  6. package/src/agent/runtime/runtime.ts +3 -9
  7. package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
  8. package/src/agent/runtime/source-modules.test.ts +16 -35
  9. package/src/agent/runtime/source-modules.ts +17 -0
  10. package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
  11. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
  12. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
  13. package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
  14. package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
  15. package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
  16. package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
  17. package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
  18. package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
  19. package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
  20. package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
  21. package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
  22. package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
  23. package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
  24. package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
  25. package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
  26. package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
  27. package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
  28. package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
  29. package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
  30. package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
  31. package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
  32. package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
  33. package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
  34. package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
  35. package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
  36. package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
  37. package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
  38. package/vendor/agent-root/src/agent/agent/error.ts +198 -0
  39. package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
  40. package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
  41. package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
  42. package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
  43. package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
  44. package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
  45. package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
  46. package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
  47. package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
  48. package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
  49. package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
  50. package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
  51. package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
  52. package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
  53. package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
  54. package/vendor/agent-root/src/agent/app/index.ts +5 -0
  55. package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
  56. package/vendor/agent-root/src/agent/app/ports.ts +72 -0
  57. package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
  58. package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
  59. package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
  60. package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
  61. package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
  62. package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
  63. package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
  64. package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
  65. package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
  66. package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
  67. package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
  68. package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
  69. package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
  70. package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
  71. package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
  72. package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
  73. package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
  74. package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
  75. package/vendor/agent-root/src/agent/error-contract.ts +154 -0
  76. package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
  77. package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
  78. package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
  79. package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
  80. package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
  81. package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
  82. package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
  83. package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
  84. package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
  85. package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
  86. package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
  87. package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
  88. package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
  89. package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
  90. package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
  91. package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
  92. package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
  93. package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
  94. package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
  95. package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
  96. package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
  97. package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
  98. package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
  99. package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
  100. package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
  101. package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
  102. package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
  103. package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
  104. package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
  105. package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
  106. package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
  107. package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
  108. package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
  109. package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
  110. package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
  111. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
  112. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
  113. package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
  114. package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
  115. package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
  116. package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
  117. package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
  118. package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
  119. package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
  120. package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
  121. package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
  122. package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
  123. package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
  124. package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
  125. package/vendor/agent-root/src/agent/tool/error.ts +131 -0
  126. package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
  127. package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
  128. package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
  129. package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
  130. package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
  131. package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
  132. package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
  133. package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
  134. package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
  135. package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
  136. package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
  137. package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
  138. package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
  139. package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
  140. package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
  141. package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
  142. package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
  143. package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
  144. package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
  145. package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
  146. package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
  147. package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
  148. package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
  149. package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
  150. package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
  151. package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
  152. package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
  153. package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
  154. package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
  155. package/vendor/agent-root/src/agent/tool/task.ts +209 -0
  156. package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
  157. package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
  158. package/vendor/agent-root/src/agent/tool/types.ts +116 -0
  159. package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
  160. package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
  161. package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
  162. package/vendor/agent-root/src/agent/types.ts +232 -0
  163. package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
  164. package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
  165. package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
  166. package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
  167. package/vendor/agent-root/src/agent/utils/index.ts +16 -0
  168. package/vendor/agent-root/src/agent/utils/message.ts +171 -0
  169. package/vendor/agent-root/src/agent/utils/token.ts +28 -0
  170. package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
  171. package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
  172. package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
  173. package/vendor/agent-root/src/config/index.ts +54 -0
  174. package/vendor/agent-root/src/config/loader.ts +431 -0
  175. package/vendor/agent-root/src/config/paths.ts +30 -0
  176. package/vendor/agent-root/src/config/runtime.ts +163 -0
  177. package/vendor/agent-root/src/config/types.ts +70 -0
  178. package/vendor/agent-root/src/logger/index.ts +57 -0
  179. package/vendor/agent-root/src/logger/logger.ts +819 -0
  180. package/vendor/agent-root/src/logger/types.ts +150 -0
  181. package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
  182. package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
  183. package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
  184. package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
  185. package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
  186. package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
  187. package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
  188. package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
  189. package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
  190. package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
  191. package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
  192. package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
  193. package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
  194. package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
  195. package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
  196. package/vendor/agent-root/src/providers/http/client.ts +289 -0
  197. package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
  198. package/vendor/agent-root/src/providers/index.ts +76 -0
  199. package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
  200. package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
  201. package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
  202. package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
  203. package/vendor/agent-root/src/providers/registry.ts +135 -0
  204. package/vendor/agent-root/src/providers/types/api.ts +284 -0
  205. package/vendor/agent-root/src/providers/types/config.ts +58 -0
  206. package/vendor/agent-root/src/providers/types/errors.ts +323 -0
  207. package/vendor/agent-root/src/providers/types/index.ts +72 -0
  208. package/vendor/agent-root/src/providers/types/provider.ts +45 -0
  209. package/vendor/agent-root/src/providers/types/registry.ts +88 -0
@@ -0,0 +1,732 @@
1
+ import * as os from 'node:os';
2
+ import * as path from 'node:path';
3
+ import * as fsPromises from 'node:fs/promises';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { TaskCreateTool } from '../task-create';
6
+ import { TaskGetTool } from '../task-get';
7
+ import { TaskOutputTool } from '../task-output';
8
+ import { TaskStopTool } from '../task-stop';
9
+ import { TaskStore } from '../task-store';
10
+ import { TaskTool } from '../task';
11
+ import type { SubagentRunnerAdapter } from '../task-runner-adapter';
12
+ import type { AgentRunEntity, TaskEntity, TaskNamespaceState } from '../task-types';
13
+ import type { ToolExecutionContext } from '../types';
14
+
15
+ function parseOutput<T>(output: string | undefined): T {
16
+ return JSON.parse(output || '{}') as T;
17
+ }
18
+
19
+ function makeRun(overrides: Partial<AgentRunEntity> = {}): AgentRunEntity {
20
+ const now = Date.now();
21
+ return {
22
+ agentId: overrides.agentId || 'agent-x',
23
+ status: overrides.status || 'running',
24
+ subagentType: overrides.subagentType || 'Plan',
25
+ prompt: overrides.prompt || 'prompt',
26
+ description: overrides.description,
27
+ model: overrides.model,
28
+ maxTurns: overrides.maxTurns,
29
+ allowedTools: overrides.allowedTools,
30
+ linkedTaskId: overrides.linkedTaskId,
31
+ output: overrides.output,
32
+ error: overrides.error,
33
+ progress: overrides.progress,
34
+ createdAt: overrides.createdAt || now,
35
+ startedAt: overrides.startedAt || now,
36
+ endedAt: overrides.endedAt,
37
+ updatedAt: overrides.updatedAt || now,
38
+ outputFile: overrides.outputFile,
39
+ metadata: overrides.metadata || {},
40
+ version: overrides.version || 1,
41
+ };
42
+ }
43
+
44
+ function makeTask(overrides: Partial<TaskEntity> = {}): TaskEntity {
45
+ const now = 1;
46
+ return {
47
+ id: overrides.id || 'task-x',
48
+ subject: overrides.subject || 'Subject',
49
+ description: overrides.description || 'Long enough description field.',
50
+ activeForm: overrides.activeForm || 'Active subject',
51
+ status: overrides.status || 'pending',
52
+ priority: overrides.priority || 'normal',
53
+ owner: overrides.owner === undefined ? null : overrides.owner,
54
+ blockedBy: overrides.blockedBy || [],
55
+ blocks: overrides.blocks || [],
56
+ progress: overrides.progress || 0,
57
+ checkpoints: overrides.checkpoints || [],
58
+ retryConfig: overrides.retryConfig || {
59
+ maxRetries: 3,
60
+ retryDelayMs: 100,
61
+ backoffMultiplier: 2,
62
+ retryOn: ['timeout'],
63
+ },
64
+ retryCount: overrides.retryCount || 0,
65
+ tags: overrides.tags || [],
66
+ metadata: overrides.metadata || {},
67
+ history: overrides.history || [],
68
+ createdAt: overrides.createdAt || now,
69
+ updatedAt: overrides.updatedAt || now,
70
+ version: overrides.version || 1,
71
+ agentId: overrides.agentId,
72
+ startedAt: overrides.startedAt,
73
+ completedAt: overrides.completedAt,
74
+ cancelledAt: overrides.cancelledAt,
75
+ lastError: overrides.lastError,
76
+ lastErrorAt: overrides.lastErrorAt,
77
+ timeoutMs: overrides.timeoutMs,
78
+ };
79
+ }
80
+
81
+ describe('task tool runtime edge branches', () => {
82
+ let baseDir: string;
83
+ let store: TaskStore;
84
+ let taskCreate: TaskCreateTool;
85
+
86
+ beforeEach(async () => {
87
+ baseDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'renx-task-runtime-edge-'));
88
+ store = new TaskStore({ baseDir });
89
+ taskCreate = new TaskCreateTool({ store });
90
+ });
91
+
92
+ afterEach(async () => {
93
+ await fsPromises.rm(baseDir, { recursive: true, force: true });
94
+ });
95
+
96
+ it('covers task schema preprocess, concurrency metadata and linked-task validation errors', async () => {
97
+ const runner: SubagentRunnerAdapter = {
98
+ start: async () => makeRun({ status: 'completed' }),
99
+ poll: async () => null,
100
+ cancel: async () => null,
101
+ };
102
+ const taskTool = new TaskTool({ store, runner, defaultNamespace: 'def' });
103
+
104
+ const parsedTrue = taskTool.safeValidateArgs({
105
+ subagent_type: 'Plan',
106
+ prompt: 'x',
107
+ run_in_background: 'true',
108
+ });
109
+ const parsedFalse = taskTool.safeValidateArgs({
110
+ subagent_type: 'Plan',
111
+ prompt: 'x',
112
+ run_in_background: 'false',
113
+ });
114
+ const parsedBoolean = taskTool.safeValidateArgs({
115
+ subagent_type: 'Plan',
116
+ prompt: 'x',
117
+ run_in_background: true,
118
+ });
119
+ expect(parsedTrue.success && parsedTrue.data.run_in_background).toBe(true);
120
+ expect(parsedFalse.success && parsedFalse.data.run_in_background).toBe(false);
121
+ expect(parsedBoolean.success && parsedBoolean.data.run_in_background).toBe(true);
122
+
123
+ expect(taskTool.getConcurrencyMode()).toBe('exclusive');
124
+ expect(taskTool.getConcurrencyLockKey({} as never)).toBe('taskns:def');
125
+ expect(taskTool.getConcurrencyLockKey({ namespace: 'n1' } as never)).toBe('taskns:n1');
126
+
127
+ const linkedMissing = await taskTool.execute({
128
+ namespace: 'n1',
129
+ subagent_type: 'Plan',
130
+ prompt: 'x',
131
+ linked_task_id: 'missing',
132
+ });
133
+ expect(linkedMissing.success).toBe(false);
134
+ expect(linkedMissing.output).toContain('TASK_NOT_FOUND');
135
+
136
+ const blocker = parseOutput<{ task: { id: string } }>(
137
+ (
138
+ await taskCreate.execute({
139
+ namespace: 'n2',
140
+ subject: 'blocker',
141
+ description: 'blocker description long enough.',
142
+ })
143
+ ).output
144
+ ).task.id;
145
+ const blocked = parseOutput<{ task: { id: string } }>(
146
+ (
147
+ await taskCreate.execute({
148
+ namespace: 'n2',
149
+ subject: 'blocked',
150
+ description: 'blocked description long enough.',
151
+ })
152
+ ).output
153
+ ).task.id;
154
+ await store.updateState('n2', (state) => {
155
+ state.tasks[blocked].blockedBy = [blocker];
156
+ return null;
157
+ });
158
+
159
+ const blockedResult = await taskTool.execute({
160
+ namespace: 'n2',
161
+ subagent_type: 'Plan',
162
+ prompt: 'x',
163
+ linked_task_id: blocked,
164
+ });
165
+ expect(blockedResult.success).toBe(false);
166
+ expect(blockedResult.output).toContain('TASK_BLOCKED');
167
+ });
168
+
169
+ it('covers task linked mapping for cancelled run, missing task on post-run update, and catch fallback', async () => {
170
+ const cancelledRunner: SubagentRunnerAdapter = {
171
+ start: async () => makeRun({ agentId: 'agent-cancel', status: 'cancelled' }),
172
+ poll: async () => null,
173
+ cancel: async () => null,
174
+ };
175
+ const taskToolCancelled = new TaskTool({ store, runner: cancelledRunner });
176
+ const taskGet = new TaskGetTool({ store });
177
+ const linked = parseOutput<{ task: { id: string } }>(
178
+ (
179
+ await taskCreate.execute({
180
+ namespace: 'n3',
181
+ subject: 'cancel-me',
182
+ description: 'cancel me with enough detail text.',
183
+ })
184
+ ).output
185
+ ).task.id;
186
+
187
+ const cancelled = await taskToolCancelled.execute({
188
+ namespace: 'n3',
189
+ subagent_type: 'Plan',
190
+ prompt: 'x',
191
+ linked_task_id: linked,
192
+ });
193
+ expect(cancelled.success).toBe(true);
194
+ const taskAfterCancelled = await taskGet.execute({
195
+ namespace: 'n3',
196
+ task_id: linked,
197
+ });
198
+ expect(taskAfterCancelled.success).toBe(true);
199
+ expect(parseOutput<{ task: { status: string } }>(taskAfterCancelled.output).task.status).toBe(
200
+ 'cancelled'
201
+ );
202
+
203
+ const fakeStore = {
204
+ normalizeNamespace: (ns?: string) => (ns || 'default').trim() || 'default',
205
+ getState: async () => ({
206
+ namespace: 'fake',
207
+ tasks: { t1: makeTask({ id: 't1' }) },
208
+ agentRuns: {},
209
+ graph: { adjacency: {}, reverse: {} },
210
+ updatedAt: 0,
211
+ schemaVersion: 1 as const,
212
+ }),
213
+ updateState: async (
214
+ _ns: string | undefined,
215
+ updater: (state: TaskNamespaceState) => unknown
216
+ ) => {
217
+ const state: TaskNamespaceState = {
218
+ namespace: 'fake',
219
+ tasks: {},
220
+ agentRuns: {},
221
+ graph: { adjacency: {}, reverse: {} },
222
+ updatedAt: 0,
223
+ schemaVersion: 1 as const,
224
+ };
225
+ const result = await updater(state);
226
+ return { state, result };
227
+ },
228
+ } as unknown as TaskStore;
229
+ const fakeRunner: SubagentRunnerAdapter = {
230
+ start: async () => makeRun({ status: 'completed', agentId: 'a1' }),
231
+ poll: async () => null,
232
+ cancel: async () => null,
233
+ };
234
+ const taskToolMissingPostLink = new TaskTool({ store: fakeStore, runner: fakeRunner });
235
+ const postLinkResult = await taskToolMissingPostLink.execute({
236
+ namespace: 'fake',
237
+ subagent_type: 'Plan',
238
+ prompt: 'x',
239
+ linked_task_id: 't1',
240
+ });
241
+ expect(postLinkResult.success).toBe(true);
242
+
243
+ const throwRunner: SubagentRunnerAdapter = {
244
+ start: async () => {
245
+ throw 'runner failed';
246
+ },
247
+ poll: async () => null,
248
+ cancel: async () => null,
249
+ };
250
+ const taskToolError = new TaskTool({ store, runner: throwRunner });
251
+ const failed = await taskToolError.execute({
252
+ namespace: 'n4',
253
+ subagent_type: 'Plan',
254
+ prompt: 'x',
255
+ });
256
+ expect(failed.success).toBe(false);
257
+ expect(failed.output).toContain('TASK_OPERATION_FAILED');
258
+ });
259
+
260
+ it('covers task default constructor, namespace fallback, failed run fallback error, and Error catch path', async () => {
261
+ const defaultTool = new TaskTool();
262
+ expect(defaultTool.getConcurrencyLockKey({} as never)).toBe('taskns:default');
263
+
264
+ const failedRunner: SubagentRunnerAdapter = {
265
+ start: async () => makeRun({ status: 'failed', error: undefined, agentId: 'agent-failed' }),
266
+ poll: async () => null,
267
+ cancel: async () => null,
268
+ };
269
+ const taskTool = new TaskTool({ store, runner: failedRunner });
270
+
271
+ const linked = parseOutput<{ task: { id: string } }>(
272
+ (
273
+ await taskCreate.execute({
274
+ subject: 'default namespace linked',
275
+ description: 'Task in default namespace for failed fallback branch.',
276
+ })
277
+ ).output
278
+ ).task.id;
279
+ const failedLinked = await taskTool.execute({
280
+ subagent_type: 'Plan',
281
+ prompt: 'x',
282
+ linked_task_id: linked,
283
+ });
284
+ expect(failedLinked.success).toBe(true);
285
+ const detail = await new TaskGetTool({ store }).execute({
286
+ task_id: linked,
287
+ });
288
+ expect(parseOutput<{ task: { lastError?: string } }>(detail.output).task.lastError).toContain(
289
+ 'linked agent failed'
290
+ );
291
+
292
+ const noNamespace = await taskTool.execute({
293
+ subagent_type: 'Plan',
294
+ prompt: 'no namespace run',
295
+ });
296
+ expect(noNamespace.success).toBe(true);
297
+
298
+ const errorRunner: SubagentRunnerAdapter = {
299
+ start: async () => {
300
+ throw new Error('TASK_CUSTOM_ERROR: exploded');
301
+ },
302
+ poll: async () => null,
303
+ cancel: async () => null,
304
+ };
305
+ const errorTool = new TaskTool({ store, runner: errorRunner });
306
+ const err = await errorTool.execute({
307
+ subagent_type: 'Plan',
308
+ prompt: 'x',
309
+ });
310
+ expect(err.success).toBe(false);
311
+ expect(err.output).toContain('TASK_CUSTOM_ERROR');
312
+ });
313
+
314
+ it('covers blocked-message fallback branch via mocked evaluateTaskCanStart', async () => {
315
+ vi.resetModules();
316
+ vi.doMock('../task-types', async () => {
317
+ const actual = await vi.importActual<typeof import('../task-types')>('../task-types');
318
+ return {
319
+ ...actual,
320
+ evaluateTaskCanStart: () => ({ canStart: false }),
321
+ };
322
+ });
323
+ const { TaskTool: MockedTaskTool } = await import('../task');
324
+
325
+ await store.updateState('m1', (state) => {
326
+ state.tasks.t1 = makeTask({ id: 't1', status: 'pending' });
327
+ return null;
328
+ });
329
+ const runner: SubagentRunnerAdapter = {
330
+ start: async () => makeRun({ status: 'completed' }),
331
+ poll: async () => null,
332
+ cancel: async () => null,
333
+ };
334
+ const mockedTool = new MockedTaskTool({ store, runner });
335
+ const result = await mockedTool.execute({
336
+ namespace: 'm1',
337
+ subagent_type: 'Plan',
338
+ prompt: 'x',
339
+ linked_task_id: 't1',
340
+ });
341
+ expect(result.success).toBe(false);
342
+ expect(result.output).toContain('linked task is blocked');
343
+
344
+ vi.doUnmock('../task-types');
345
+ vi.resetModules();
346
+ });
347
+ });
348
+
349
+ describe('task_output edge branches', () => {
350
+ let baseDir: string;
351
+ let store: TaskStore;
352
+
353
+ beforeEach(async () => {
354
+ baseDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'renx-task-output-edge-'));
355
+ store = new TaskStore({ baseDir });
356
+ });
357
+
358
+ afterEach(async () => {
359
+ await fsPromises.rm(baseDir, { recursive: true, force: true });
360
+ });
361
+
362
+ it('covers schema preprocess and concurrency metadata', () => {
363
+ const runner: SubagentRunnerAdapter = {
364
+ start: async () => makeRun(),
365
+ poll: async () => null,
366
+ cancel: async () => null,
367
+ };
368
+ const tool = new TaskOutputTool({ store, runner, defaultNamespace: 'def-out' });
369
+ const defaultTool = new TaskOutputTool();
370
+ expect(defaultTool.getConcurrencyLockKey({} as never)).toBe('taskns:default:agent:unknown');
371
+
372
+ const parsedTrue = tool.safeValidateArgs({ agent_id: 'a', block: 'true' });
373
+ const parsedFalse = tool.safeValidateArgs({ agent_id: 'a', block: 'false' });
374
+ const parsedBoolean = tool.safeValidateArgs({ agent_id: 'a', block: true });
375
+ expect(parsedTrue.success && parsedTrue.data.block).toBe(true);
376
+ expect(parsedFalse.success && parsedFalse.data.block).toBe(false);
377
+ expect(parsedBoolean.success && parsedBoolean.data.block).toBe(true);
378
+
379
+ expect(tool.getConcurrencyMode()).toBe('parallel-safe');
380
+ expect(tool.getConcurrencyLockKey({} as never)).toBe('taskns:def-out:agent:unknown');
381
+ expect(tool.getConcurrencyLockKey({ namespace: 'n1', agent_id: 'a1' } as never)).toBe(
382
+ 'taskns:n1:agent:a1'
383
+ );
384
+ expect(tool.getConcurrencyLockKey({ namespace: 'n1', task_id: 't1' } as never)).toBe(
385
+ 'taskns:n1:agent:t1'
386
+ );
387
+ });
388
+
389
+ it('covers resolve target errors and poll-not-found branches', async () => {
390
+ const runnerMissing: SubagentRunnerAdapter = {
391
+ start: async () => makeRun(),
392
+ poll: async () => null,
393
+ cancel: async () => null,
394
+ };
395
+ const outputTool = new TaskOutputTool({ store, runner: runnerMissing });
396
+
397
+ const targetRequired = await outputTool.execute({ namespace: 'n1' });
398
+ expect(targetRequired.success).toBe(false);
399
+ expect(targetRequired.output).toContain('TASK_OUTPUT_TARGET_REQUIRED');
400
+
401
+ const taskNotFound = await outputTool.execute({
402
+ namespace: 'n1',
403
+ task_id: 'missing',
404
+ });
405
+ expect(taskNotFound.success).toBe(false);
406
+ expect(taskNotFound.output).toContain('TASK_NOT_FOUND');
407
+
408
+ await store.updateState('n1', (state) => {
409
+ state.tasks.t1 = makeTask({ id: 't1', agentId: undefined });
410
+ return null;
411
+ });
412
+ const noAgent = await outputTool.execute({
413
+ namespace: 'n1',
414
+ task_id: 't1',
415
+ });
416
+ expect(noAgent.success).toBe(false);
417
+ expect(noAgent.output).toContain('AGENT_RUN_NOT_FOUND');
418
+
419
+ const nonBlockingMissing = await outputTool.execute({
420
+ namespace: 'n1',
421
+ agent_id: 'missing-agent',
422
+ block: false,
423
+ });
424
+ expect(nonBlockingMissing.success).toBe(false);
425
+ expect(nonBlockingMissing.output).toContain('AGENT_RUN_NOT_FOUND');
426
+
427
+ const blockingMissing = await outputTool.execute({
428
+ namespace: 'n1',
429
+ agent_id: 'missing-agent',
430
+ timeout_ms: 50,
431
+ poll_interval_ms: 20,
432
+ });
433
+ expect(blockingMissing.success).toBe(false);
434
+ expect(blockingMissing.output).toContain('AGENT_RUN_NOT_FOUND');
435
+
436
+ const defaultNamespaceMissing = await outputTool.execute({
437
+ agent_id: 'missing-agent',
438
+ block: false,
439
+ });
440
+ expect(defaultNamespaceMissing.success).toBe(false);
441
+
442
+ const byTaskRunner: SubagentRunnerAdapter = {
443
+ start: async () => makeRun(),
444
+ poll: async () =>
445
+ makeRun({ status: 'completed', agentId: 'agent-by-task', endedAt: Date.now() }),
446
+ cancel: async () => null,
447
+ };
448
+ await store.updateState('n1', (state) => {
449
+ state.tasks.t2 = makeTask({ id: 't2', agentId: 'agent-by-task' });
450
+ return null;
451
+ });
452
+ const byTaskTool = new TaskOutputTool({ store, runner: byTaskRunner });
453
+ const byTask = await byTaskTool.execute({
454
+ namespace: 'n1',
455
+ task_id: 't2',
456
+ block: false,
457
+ });
458
+ expect(byTask.success).toBe(true);
459
+ });
460
+
461
+ it('covers timeout hit, final-poll missing, and abort branches in blocking mode', async () => {
462
+ const alwaysRunning: SubagentRunnerAdapter = {
463
+ start: async () => makeRun(),
464
+ poll: async () => makeRun({ status: 'running', agentId: 'a-time' }),
465
+ cancel: async () => null,
466
+ };
467
+ const outputTimeout = new TaskOutputTool({ store, runner: alwaysRunning });
468
+ const timeoutRes = await outputTimeout.execute(
469
+ {
470
+ namespace: 'n2',
471
+ agent_id: 'a-time',
472
+ timeout_ms: 40,
473
+ poll_interval_ms: 20,
474
+ },
475
+ {
476
+ toolCallId: 'timeout-with-signal',
477
+ loopIndex: 1,
478
+ agent: {},
479
+ toolAbortSignal: new AbortController().signal,
480
+ }
481
+ );
482
+ expect(timeoutRes.success).toBe(true);
483
+ const timeoutPayload = parseOutput<{ completed: boolean; timeout_hit: boolean }>(
484
+ timeoutRes.output
485
+ );
486
+ expect(timeoutPayload.completed).toBe(false);
487
+ expect(timeoutPayload.timeout_hit).toBe(true);
488
+
489
+ let pollCount = 0;
490
+ const latestMissingRunner: SubagentRunnerAdapter = {
491
+ start: async () => makeRun(),
492
+ poll: async () => {
493
+ pollCount += 1;
494
+ return pollCount === 1 ? makeRun({ status: 'running', agentId: 'a-latest' }) : null;
495
+ },
496
+ cancel: async () => null,
497
+ };
498
+ const outputLatestMissing = new TaskOutputTool({ store, runner: latestMissingRunner });
499
+ const latestMissing = await outputLatestMissing.execute({
500
+ namespace: 'n3',
501
+ agent_id: 'a-latest',
502
+ timeout_ms: 1,
503
+ poll_interval_ms: 1,
504
+ });
505
+ expect(latestMissing.success).toBe(false);
506
+ expect(latestMissing.output).toContain('AGENT_RUN_NOT_FOUND');
507
+
508
+ const abortRunner: SubagentRunnerAdapter = {
509
+ start: async () => makeRun(),
510
+ poll: async () => makeRun({ status: 'running', agentId: 'a-abort' }),
511
+ cancel: async () => null,
512
+ };
513
+ const outputAbort = new TaskOutputTool({ store, runner: abortRunner });
514
+
515
+ const controller1 = new AbortController();
516
+ controller1.abort();
517
+ const context1 = {
518
+ toolCallId: 'c1',
519
+ loopIndex: 1,
520
+ agent: {},
521
+ toolAbortSignal: controller1.signal,
522
+ } as ToolExecutionContext;
523
+ await expect(
524
+ outputAbort.execute(
525
+ {
526
+ namespace: 'n4',
527
+ agent_id: 'a-abort',
528
+ timeout_ms: 50,
529
+ poll_interval_ms: 20,
530
+ },
531
+ context1
532
+ )
533
+ ).rejects.toThrow('TASK_OUTPUT_ABORTED');
534
+
535
+ const controller2 = new AbortController();
536
+ const context2 = {
537
+ toolCallId: 'c2',
538
+ loopIndex: 2,
539
+ agent: {},
540
+ toolAbortSignal: controller2.signal,
541
+ } as ToolExecutionContext;
542
+ const promise = outputAbort.execute(
543
+ {
544
+ namespace: 'n4',
545
+ agent_id: 'a-abort',
546
+ timeout_ms: 1000,
547
+ poll_interval_ms: 200,
548
+ },
549
+ context2
550
+ );
551
+ setTimeout(() => controller2.abort(), 30);
552
+ await expect(promise).rejects.toThrow('TASK_OUTPUT_ABORTED');
553
+ });
554
+ });
555
+
556
+ describe('task_stop edge branches', () => {
557
+ let baseDir: string;
558
+ let store: TaskStore;
559
+
560
+ beforeEach(async () => {
561
+ baseDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'renx-task-stop-edge-'));
562
+ store = new TaskStore({ baseDir });
563
+ });
564
+
565
+ afterEach(async () => {
566
+ await fsPromises.rm(baseDir, { recursive: true, force: true });
567
+ });
568
+
569
+ it('covers schema preprocess and concurrency metadata', () => {
570
+ const runner: SubagentRunnerAdapter = {
571
+ start: async () => makeRun(),
572
+ poll: async () => null,
573
+ cancel: async () => null,
574
+ };
575
+ const stopTool = new TaskStopTool({ store, runner, defaultNamespace: 'def-stop' });
576
+ const defaultTool = new TaskStopTool();
577
+ expect(defaultTool.getConcurrencyLockKey({} as never)).toBe('taskns:default');
578
+ const parsedTrue = stopTool.safeValidateArgs({ agent_id: 'a', cancel_linked_task: 'true' });
579
+ const parsedFalse = stopTool.safeValidateArgs({ agent_id: 'a', cancel_linked_task: 'false' });
580
+ const parsedBoolean = stopTool.safeValidateArgs({ agent_id: 'a', cancel_linked_task: true });
581
+ expect(parsedTrue.success && parsedTrue.data.cancel_linked_task).toBe(true);
582
+ expect(parsedFalse.success && parsedFalse.data.cancel_linked_task).toBe(false);
583
+ expect(parsedBoolean.success && parsedBoolean.data.cancel_linked_task).toBe(true);
584
+
585
+ expect(stopTool.getConcurrencyMode()).toBe('exclusive');
586
+ expect(stopTool.getConcurrencyLockKey({} as never)).toBe('taskns:def-stop');
587
+ expect(stopTool.getConcurrencyLockKey({ namespace: 'n1' } as never)).toBe('taskns:n1');
588
+ });
589
+
590
+ it('covers resolve errors, pre-cancel error branches, and cancel returns null', async () => {
591
+ const running = makeRun({ status: 'running', agentId: 'a1' });
592
+ const terminal = makeRun({ status: 'completed', agentId: 'a2', endedAt: Date.now() });
593
+
594
+ const runnerBase: SubagentRunnerAdapter = {
595
+ start: async () => makeRun(),
596
+ poll: async (_ns, id) => {
597
+ if (id === 'a1') return running;
598
+ if (id === 'a2') return terminal;
599
+ return null;
600
+ },
601
+ cancel: async () => null,
602
+ };
603
+ const stopTool = new TaskStopTool({ store, runner: runnerBase });
604
+
605
+ const targetRequired = await stopTool.execute({ namespace: 'n1' });
606
+ expect(targetRequired.success).toBe(false);
607
+ expect(targetRequired.output).toContain('TASK_STOP_TARGET_REQUIRED');
608
+
609
+ const notFound = await stopTool.execute({
610
+ namespace: 'n1',
611
+ task_id: 'missing',
612
+ });
613
+ expect(notFound.success).toBe(false);
614
+ expect(notFound.output).toContain('TASK_NOT_FOUND');
615
+
616
+ await store.updateState('n1', (state) => {
617
+ state.tasks.t1 = makeTask({ id: 't1' });
618
+ return null;
619
+ });
620
+ const noAgent = await stopTool.execute({
621
+ namespace: 'n1',
622
+ task_id: 't1',
623
+ });
624
+ expect(noAgent.success).toBe(false);
625
+ expect(noAgent.output).toContain('AGENT_RUN_NOT_FOUND');
626
+
627
+ const beforeMissing = await stopTool.execute({
628
+ namespace: 'n1',
629
+ agent_id: 'missing-agent',
630
+ });
631
+ expect(beforeMissing.success).toBe(false);
632
+ expect(beforeMissing.output).toContain('AGENT_RUN_NOT_FOUND');
633
+
634
+ const beforeTerminal = await stopTool.execute({
635
+ namespace: 'n1',
636
+ agent_id: 'a2',
637
+ });
638
+ expect(beforeTerminal.success).toBe(false);
639
+ expect(beforeTerminal.output).toContain('AGENT_RUN_ALREADY_TERMINAL');
640
+
641
+ const cancelNull = await stopTool.execute({
642
+ namespace: 'n1',
643
+ agent_id: 'a1',
644
+ });
645
+ expect(cancelNull.success).toBe(false);
646
+ expect(cancelNull.output).toContain('AGENT_RUN_NOT_FOUND');
647
+ });
648
+
649
+ it('covers cancel_linked_task false and true(with skip completed/cancelled)', async () => {
650
+ const runner: SubagentRunnerAdapter = {
651
+ start: async () => makeRun(),
652
+ poll: async () => makeRun({ status: 'running', agentId: 'agent-main' }),
653
+ cancel: async () =>
654
+ makeRun({ status: 'cancelled', agentId: 'agent-main', endedAt: Date.now() }),
655
+ };
656
+ const stopTool = new TaskStopTool({ store, runner });
657
+
658
+ await store.updateState('n2', (state) => {
659
+ state.tasks.p1 = makeTask({ id: 'p1', status: 'pending', agentId: 'agent-main', owner: 'x' });
660
+ return null;
661
+ });
662
+
663
+ const noLinkedCancel = await stopTool.execute({
664
+ namespace: 'n2',
665
+ agent_id: 'agent-main',
666
+ cancel_linked_task: false,
667
+ });
668
+ expect(noLinkedCancel.success).toBe(true);
669
+ const noLinkedPayload = parseOutput<{ cancelled_task_ids: string[] }>(noLinkedCancel.output);
670
+ expect(noLinkedPayload.cancelled_task_ids).toEqual([]);
671
+ const p1State = await store.getState('n2');
672
+ expect(p1State.tasks.p1.status).toBe('pending');
673
+
674
+ const defaultLinkedCancel = await stopTool.execute({
675
+ namespace: 'n2',
676
+ task_id: 'p1',
677
+ });
678
+ expect(defaultLinkedCancel.success).toBe(true);
679
+ const defaultLinkedPayload = parseOutput<{ cancelled_task_ids: string[] }>(
680
+ defaultLinkedCancel.output
681
+ );
682
+ expect(defaultLinkedPayload.cancelled_task_ids).toContain('p1');
683
+
684
+ await store.updateState('n3', (state) => {
685
+ state.tasks.t_pending = makeTask({
686
+ id: 't_pending',
687
+ status: 'in_progress',
688
+ owner: 'agent:agent-main',
689
+ agentId: 'agent-main',
690
+ });
691
+ state.tasks.t_completed = makeTask({
692
+ id: 't_completed',
693
+ status: 'completed',
694
+ agentId: 'agent-main',
695
+ });
696
+ state.tasks.t_cancelled = makeTask({
697
+ id: 't_cancelled',
698
+ status: 'cancelled',
699
+ agentId: 'agent-main',
700
+ });
701
+ return null;
702
+ });
703
+
704
+ const linkedCancel = await stopTool.execute({
705
+ namespace: 'n3',
706
+ agent_id: 'agent-main',
707
+ cancel_linked_task: true,
708
+ });
709
+ expect(linkedCancel.success).toBe(true);
710
+ const linkedPayload = parseOutput<{ cancelled_task_ids: string[] }>(linkedCancel.output);
711
+ expect(linkedPayload.cancelled_task_ids).toEqual(['t_pending']);
712
+
713
+ const stateAfter = await store.getState('n3');
714
+ expect(stateAfter.tasks.t_pending.status).toBe('cancelled');
715
+ expect(stateAfter.tasks.t_completed.status).toBe('completed');
716
+ expect(stateAfter.tasks.t_cancelled.status).toBe('cancelled');
717
+
718
+ await store.updateState(undefined, (state) => {
719
+ state.tasks.default_task = makeTask({
720
+ id: 'default_task',
721
+ status: 'in_progress',
722
+ owner: 'agent:agent-main',
723
+ agentId: 'agent-main',
724
+ });
725
+ return null;
726
+ });
727
+ const noNamespaceStop = await stopTool.execute({
728
+ task_id: 'default_task',
729
+ });
730
+ expect(noNamespaceStop.success).toBe(true);
731
+ });
732
+ });