@renxqoo/renx-code 0.0.4 → 0.0.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 (210) hide show
  1. package/README.md +82 -51
  2. package/bin/renx.cjs +16 -0
  3. package/package.json +2 -45
  4. package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
  5. package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
  6. package/src/agent/runtime/runtime.test.ts +7 -4
  7. package/src/agent/runtime/runtime.ts +3 -9
  8. package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
  9. package/src/agent/runtime/source-modules.test.ts +16 -35
  10. package/src/agent/runtime/source-modules.ts +17 -0
  11. package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
  12. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
  13. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
  14. package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
  15. package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
  16. package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
  17. package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
  18. package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
  19. package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
  20. package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
  21. package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
  22. package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
  23. package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
  24. package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
  25. package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
  26. package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
  27. package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
  28. package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
  29. package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
  30. package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
  31. package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
  32. package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
  33. package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
  34. package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
  35. package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
  36. package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
  37. package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
  38. package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
  39. package/vendor/agent-root/src/agent/agent/error.ts +198 -0
  40. package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
  41. package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
  42. package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
  43. package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
  44. package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
  45. package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
  46. package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
  47. package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
  48. package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
  49. package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
  50. package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
  51. package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
  52. package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
  53. package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
  54. package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
  55. package/vendor/agent-root/src/agent/app/index.ts +5 -0
  56. package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
  57. package/vendor/agent-root/src/agent/app/ports.ts +72 -0
  58. package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
  59. package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
  60. package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
  61. package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
  62. package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
  63. package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
  64. package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
  65. package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
  66. package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
  67. package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
  68. package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
  69. package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
  70. package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
  71. package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
  72. package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
  73. package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
  74. package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
  75. package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
  76. package/vendor/agent-root/src/agent/error-contract.ts +154 -0
  77. package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
  78. package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
  79. package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
  80. package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
  81. package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
  82. package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
  83. package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
  84. package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
  85. package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
  86. package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
  87. package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
  88. package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
  89. package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
  90. package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
  91. package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
  92. package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
  93. package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
  94. package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
  95. package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
  96. package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
  97. package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
  98. package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
  99. package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
  100. package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
  101. package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
  102. package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
  103. package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
  104. package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
  105. package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
  106. package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
  107. package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
  108. package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
  109. package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
  110. package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
  111. package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
  112. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
  113. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
  114. package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
  115. package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
  116. package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
  117. package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
  118. package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
  119. package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
  120. package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
  121. package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
  122. package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
  123. package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
  124. package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
  125. package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
  126. package/vendor/agent-root/src/agent/tool/error.ts +131 -0
  127. package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
  128. package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
  129. package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
  130. package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
  131. package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
  132. package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
  133. package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
  134. package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
  135. package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
  136. package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
  137. package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
  138. package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
  139. package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
  140. package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
  141. package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
  142. package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
  143. package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
  144. package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
  145. package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
  146. package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
  147. package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
  148. package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
  149. package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
  150. package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
  151. package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
  152. package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
  153. package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
  154. package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
  155. package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
  156. package/vendor/agent-root/src/agent/tool/task.ts +209 -0
  157. package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
  158. package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
  159. package/vendor/agent-root/src/agent/tool/types.ts +116 -0
  160. package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
  161. package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
  162. package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
  163. package/vendor/agent-root/src/agent/types.ts +232 -0
  164. package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
  165. package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
  166. package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
  167. package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
  168. package/vendor/agent-root/src/agent/utils/index.ts +16 -0
  169. package/vendor/agent-root/src/agent/utils/message.ts +171 -0
  170. package/vendor/agent-root/src/agent/utils/token.ts +28 -0
  171. package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
  172. package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
  173. package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
  174. package/vendor/agent-root/src/config/index.ts +54 -0
  175. package/vendor/agent-root/src/config/loader.ts +431 -0
  176. package/vendor/agent-root/src/config/paths.ts +30 -0
  177. package/vendor/agent-root/src/config/runtime.ts +163 -0
  178. package/vendor/agent-root/src/config/types.ts +70 -0
  179. package/vendor/agent-root/src/logger/index.ts +57 -0
  180. package/vendor/agent-root/src/logger/logger.ts +819 -0
  181. package/vendor/agent-root/src/logger/types.ts +150 -0
  182. package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
  183. package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
  184. package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
  185. package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
  186. package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
  187. package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
  188. package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
  189. package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
  190. package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
  191. package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
  192. package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
  193. package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
  194. package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
  195. package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
  196. package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
  197. package/vendor/agent-root/src/providers/http/client.ts +289 -0
  198. package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
  199. package/vendor/agent-root/src/providers/index.ts +76 -0
  200. package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
  201. package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
  202. package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
  203. package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
  204. package/vendor/agent-root/src/providers/registry.ts +135 -0
  205. package/vendor/agent-root/src/providers/types/api.ts +284 -0
  206. package/vendor/agent-root/src/providers/types/config.ts +58 -0
  207. package/vendor/agent-root/src/providers/types/errors.ts +323 -0
  208. package/vendor/agent-root/src/providers/types/index.ts +72 -0
  209. package/vendor/agent-root/src/providers/types/provider.ts +45 -0
  210. package/vendor/agent-root/src/providers/types/registry.ts +88 -0
@@ -0,0 +1,287 @@
1
+ import * as os from 'node:os';
2
+ import * as path from 'node:path';
3
+ import { promises as fs } from 'node:fs';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { TaskStore } from '../task-store';
6
+ import { attachParentAbortCascade, PARENT_ABORT_REASON } from '../task-parent-abort';
7
+ import type { SubagentRunnerAdapter } from '../task-runner-adapter';
8
+ import type { AgentRunEntity, TaskEntity } from '../task-types';
9
+ import type { ToolExecutionContext, ToolStreamEventInput } from '../types';
10
+
11
+ function makeRun(agentId: string, status: AgentRunEntity['status'] = 'cancelled'): AgentRunEntity {
12
+ const now = Date.now();
13
+ return {
14
+ agentId,
15
+ status,
16
+ subagentType: 'Plan',
17
+ prompt: 'p',
18
+ createdAt: now,
19
+ startedAt: now,
20
+ updatedAt: now,
21
+ metadata: {},
22
+ version: 1,
23
+ };
24
+ }
25
+
26
+ function makeTask(id: string, status: TaskEntity['status'], agentId?: string): TaskEntity {
27
+ const now = Date.now();
28
+ return {
29
+ id,
30
+ subject: id,
31
+ description: `${id} description long enough`,
32
+ activeForm: id,
33
+ status,
34
+ priority: 'normal',
35
+ owner: status === 'in_progress' ? `agent:${agentId || 'x'}` : null,
36
+ blockedBy: [],
37
+ blocks: [],
38
+ progress: 0,
39
+ checkpoints: [],
40
+ retryConfig: {
41
+ maxRetries: 3,
42
+ retryDelayMs: 100,
43
+ backoffMultiplier: 2,
44
+ retryOn: ['timeout'],
45
+ },
46
+ retryCount: 0,
47
+ tags: [],
48
+ metadata: {},
49
+ history: [],
50
+ agentId,
51
+ createdAt: now,
52
+ updatedAt: now,
53
+ version: 1,
54
+ };
55
+ }
56
+
57
+ function makeRunner(
58
+ cancelImpl: (
59
+ namespace: string,
60
+ agentId: string,
61
+ reason?: string
62
+ ) => Promise<AgentRunEntity | null>
63
+ ): SubagentRunnerAdapter {
64
+ return {
65
+ start: async () => makeRun('unused', 'running'),
66
+ poll: async () => null,
67
+ cancel: cancelImpl,
68
+ };
69
+ }
70
+
71
+ async function waitUntil(check: () => boolean | Promise<boolean>, timeoutMs = 1000): Promise<void> {
72
+ const start = Date.now();
73
+ while (Date.now() - start < timeoutMs) {
74
+ if (await check()) {
75
+ return;
76
+ }
77
+ await new Promise((resolve) => setTimeout(resolve, 10));
78
+ }
79
+ throw new Error('Timed out waiting for condition');
80
+ }
81
+
82
+ async function rmDirWithRetry(dir: string): Promise<void> {
83
+ for (let attempt = 0; attempt < 5; attempt += 1) {
84
+ try {
85
+ await fs.rm(dir, { recursive: true, force: true });
86
+ return;
87
+ } catch (error) {
88
+ const code = (error as NodeJS.ErrnoException).code;
89
+ if (code !== 'ENOTEMPTY' && code !== 'EBUSY' && code !== 'EPERM') {
90
+ throw error;
91
+ }
92
+ await new Promise((resolve) => setTimeout(resolve, 20));
93
+ }
94
+ }
95
+ }
96
+
97
+ function makeContext(signal: AbortSignal, chunks: ToolStreamEventInput[]): ToolExecutionContext {
98
+ return {
99
+ toolCallId: 'tc',
100
+ loopIndex: 1,
101
+ agent: {},
102
+ toolAbortSignal: signal,
103
+ onChunk: async (event) => {
104
+ chunks.push(event);
105
+ },
106
+ };
107
+ }
108
+
109
+ describe('task-parent-abort cascade', () => {
110
+ let baseDir: string;
111
+ let store: TaskStore;
112
+
113
+ beforeEach(async () => {
114
+ baseDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-task-parent-abort-'));
115
+ store = new TaskStore({ baseDir });
116
+ });
117
+
118
+ afterEach(async () => {
119
+ await rmDirWithRetry(baseDir);
120
+ });
121
+
122
+ it('returns noop detach when context has no abort signal', async () => {
123
+ let cancelCalls = 0;
124
+ const runner = makeRunner(async () => {
125
+ cancelCalls += 1;
126
+ return makeRun('a1');
127
+ });
128
+
129
+ const detach = attachParentAbortCascade({
130
+ namespace: 'n1',
131
+ agentId: 'a1',
132
+ runner,
133
+ store,
134
+ });
135
+ detach();
136
+
137
+ await new Promise((resolve) => setTimeout(resolve, 20));
138
+ expect(cancelCalls).toBe(0);
139
+ });
140
+
141
+ it('deduplicates abort callbacks and supports detach with custom signal', async () => {
142
+ let cancelCalls = 0;
143
+ let removed = false;
144
+ let registered: (() => void) | undefined;
145
+ const runner = makeRunner(async () => {
146
+ cancelCalls += 1;
147
+ return makeRun('a2');
148
+ });
149
+
150
+ const signal = {
151
+ aborted: true,
152
+ addEventListener: (_name: string, cb: () => void) => {
153
+ registered = cb;
154
+ cb();
155
+ },
156
+ removeEventListener: (_name: string, cb: () => void) => {
157
+ removed = registered === cb;
158
+ },
159
+ } as unknown as AbortSignal;
160
+
161
+ const detach = attachParentAbortCascade({
162
+ namespace: 'n1',
163
+ agentId: 'a2',
164
+ runner,
165
+ store,
166
+ context: makeContext(signal, []),
167
+ });
168
+
169
+ await waitUntil(() => cancelCalls === 1);
170
+ expect(cancelCalls).toBe(1);
171
+ detach();
172
+ expect(removed).toBe(true);
173
+ });
174
+
175
+ it('skips linked task updates when task missing, mismatched, or already terminal', async () => {
176
+ await store.updateState('n2', (state) => {
177
+ state.tasks.t_mismatch = makeTask('t_mismatch', 'in_progress', 'agent-other');
178
+ state.tasks.t_done = makeTask('t_done', 'completed', 'agent-main');
179
+ return null;
180
+ });
181
+
182
+ let cancelCalls = 0;
183
+ const runner = makeRunner(async () => {
184
+ cancelCalls += 1;
185
+ return makeRun('agent-main');
186
+ });
187
+
188
+ const chunks: ToolStreamEventInput[] = [];
189
+
190
+ const c1 = new AbortController();
191
+ attachParentAbortCascade({
192
+ namespace: 'n2',
193
+ agentId: 'agent-main',
194
+ linkedTaskId: 'missing',
195
+ runner,
196
+ store,
197
+ context: makeContext(c1.signal, chunks),
198
+ });
199
+ c1.abort();
200
+
201
+ const c2 = new AbortController();
202
+ attachParentAbortCascade({
203
+ namespace: 'n2',
204
+ agentId: 'agent-main',
205
+ linkedTaskId: 't_mismatch',
206
+ runner,
207
+ store,
208
+ context: makeContext(c2.signal, chunks),
209
+ });
210
+ c2.abort();
211
+
212
+ const c3 = new AbortController();
213
+ attachParentAbortCascade({
214
+ namespace: 'n2',
215
+ agentId: 'agent-main',
216
+ linkedTaskId: 't_done',
217
+ runner,
218
+ store,
219
+ context: makeContext(c3.signal, chunks),
220
+ });
221
+ c3.abort();
222
+
223
+ await waitUntil(() => cancelCalls === 3);
224
+ const state = await store.getState('n2');
225
+ expect(state.tasks.t_mismatch.status).toBe('in_progress');
226
+ expect(state.tasks.t_done.status).toBe('completed');
227
+ await new Promise((resolve) => setTimeout(resolve, 50));
228
+ });
229
+
230
+ it('cancels linked task and writes parent-abort history', async () => {
231
+ await store.updateState('n3', (state) => {
232
+ state.tasks.t_active = makeTask('t_active', 'in_progress', 'agent-ok');
233
+ return null;
234
+ });
235
+
236
+ const chunks: ToolStreamEventInput[] = [];
237
+ const runner = makeRunner(async () => makeRun('agent-ok'));
238
+ const controller = new AbortController();
239
+
240
+ attachParentAbortCascade({
241
+ namespace: 'n3',
242
+ agentId: 'agent-ok',
243
+ linkedTaskId: 't_active',
244
+ runner,
245
+ store,
246
+ context: makeContext(controller.signal, chunks),
247
+ });
248
+ controller.abort();
249
+
250
+ await waitUntil(async () => {
251
+ const state = await store.getState('n3');
252
+ return state.tasks.t_active.status === 'cancelled';
253
+ });
254
+
255
+ const state = await store.getState('n3');
256
+ const task = state.tasks.t_active;
257
+ expect(task.status).toBe('cancelled');
258
+ expect(task.history.some((entry) => entry.actor === 'task-parent-abort')).toBe(true);
259
+ expect(task.history.some((entry) => entry.reason === PARENT_ABORT_REASON)).toBe(true);
260
+ expect(chunks.some((entry) => entry.type === 'info')).toBe(true);
261
+ });
262
+
263
+ it('emits stderr chunk when cancel throws non-Error value', async () => {
264
+ const chunks: ToolStreamEventInput[] = [];
265
+ const runner = makeRunner(async () => {
266
+ throw 'raw-failure';
267
+ });
268
+ const controller = new AbortController();
269
+
270
+ attachParentAbortCascade({
271
+ namespace: 'n4',
272
+ agentId: 'agent-fail',
273
+ runner,
274
+ store,
275
+ context: makeContext(controller.signal, chunks),
276
+ });
277
+ controller.abort();
278
+
279
+ await waitUntil(() =>
280
+ chunks.some(
281
+ (entry) =>
282
+ entry.type === 'stderr' &&
283
+ String(entry.content || '').includes('failed to cascade parent abort: raw-failure')
284
+ )
285
+ );
286
+ });
287
+ });
@@ -0,0 +1,190 @@
1
+ import * as os from 'node:os';
2
+ import * as path from 'node:path';
3
+ import { promises as fs } from 'node:fs';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { TaskStore } from '../task-store';
6
+ import { RealSubagentRunnerAdapter } from '../task-runner-adapter';
7
+ import type { RunRecord } from '../../app/contracts';
8
+ import type { RunForegroundRequest, RunForegroundResult } from '../../app/agent-app-service';
9
+ import type { Message } from '../../types';
10
+
11
+ function sleep(ms: number): Promise<void> {
12
+ return new Promise((resolve) => setTimeout(resolve, ms));
13
+ }
14
+
15
+ function makeRunRecord(
16
+ executionId: string,
17
+ conversationId: string,
18
+ status: RunRecord['status']
19
+ ): RunRecord {
20
+ const now = Date.now();
21
+ return {
22
+ executionId,
23
+ runId: executionId,
24
+ conversationId,
25
+ status,
26
+ createdAt: now,
27
+ updatedAt: now,
28
+ stepIndex: status === 'COMPLETED' ? 2 : 1,
29
+ startedAt: now,
30
+ completedAt: status === 'COMPLETED' || status === 'CANCELLED' ? now : undefined,
31
+ terminalReason:
32
+ status === 'COMPLETED' ? 'stop' : status === 'CANCELLED' ? 'aborted' : undefined,
33
+ };
34
+ }
35
+
36
+ class FakeSubagentExecutionService {
37
+ readonly runs = new Map<string, RunRecord>();
38
+ readonly contextMessages = new Map<string, Message[]>();
39
+ delayMs = 30;
40
+ completionText = 'subagent final answer';
41
+
42
+ async runForeground(request: RunForegroundRequest): Promise<RunForegroundResult> {
43
+ const executionId = request.executionId as string;
44
+ const conversationId = request.conversationId;
45
+ const running = makeRunRecord(executionId, conversationId, 'RUNNING');
46
+ this.runs.set(executionId, running);
47
+
48
+ const abortSignal = request.abortSignal;
49
+ if (abortSignal?.aborted) {
50
+ const cancelled = makeRunRecord(executionId, conversationId, 'CANCELLED');
51
+ this.runs.set(executionId, cancelled);
52
+ return {
53
+ executionId,
54
+ conversationId,
55
+ messages: [],
56
+ events: [],
57
+ finishReason: 'error',
58
+ steps: 1,
59
+ run: cancelled,
60
+ };
61
+ }
62
+
63
+ await sleep(this.delayMs);
64
+
65
+ if (abortSignal?.aborted) {
66
+ const cancelled = makeRunRecord(executionId, conversationId, 'CANCELLED');
67
+ this.runs.set(executionId, cancelled);
68
+ return {
69
+ executionId,
70
+ conversationId,
71
+ messages: [],
72
+ events: [],
73
+ finishReason: 'error',
74
+ steps: 1,
75
+ run: cancelled,
76
+ };
77
+ }
78
+
79
+ const assistantMessage: Message = {
80
+ messageId: `msg_${Date.now()}`,
81
+ role: 'assistant',
82
+ type: 'assistant-text',
83
+ content: this.completionText,
84
+ timestamp: Date.now(),
85
+ };
86
+ this.contextMessages.set(conversationId, [assistantMessage]);
87
+
88
+ const completed = makeRunRecord(executionId, conversationId, 'COMPLETED');
89
+ this.runs.set(executionId, completed);
90
+ return {
91
+ executionId,
92
+ conversationId,
93
+ messages: [assistantMessage],
94
+ events: [],
95
+ finishReason: 'stop',
96
+ steps: 2,
97
+ run: completed,
98
+ };
99
+ }
100
+
101
+ async getRun(executionId: string): Promise<RunRecord | null> {
102
+ return this.runs.get(executionId) || null;
103
+ }
104
+
105
+ async listContextMessages(conversationId: string): Promise<Message[]> {
106
+ return this.contextMessages.get(conversationId) || [];
107
+ }
108
+ }
109
+
110
+ describe('real subagent runner adapter', () => {
111
+ let baseDir: string;
112
+ let store: TaskStore;
113
+ let appService: FakeSubagentExecutionService;
114
+ let runner: RealSubagentRunnerAdapter;
115
+
116
+ beforeEach(async () => {
117
+ baseDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-task-real-runner-'));
118
+ store = new TaskStore({ baseDir });
119
+ appService = new FakeSubagentExecutionService();
120
+ runner = new RealSubagentRunnerAdapter({
121
+ store,
122
+ appService,
123
+ resolveTools: (allowedTools) =>
124
+ (allowedTools || []).map((name) => ({
125
+ type: 'function',
126
+ function: {
127
+ name,
128
+ description: name,
129
+ parameters: {},
130
+ },
131
+ })),
132
+ resolveModelId: () => 'glm-5',
133
+ });
134
+ });
135
+
136
+ afterEach(async () => {
137
+ await fs.rm(baseDir, { recursive: true, force: true });
138
+ });
139
+
140
+ it('returns real foreground output instead of mock prompt echo', async () => {
141
+ const run = await runner.start('ns1', {
142
+ subagentType: 'Plan',
143
+ prompt: 'analyze codebase deeply',
144
+ runInBackground: false,
145
+ allowedTools: ['glob', 'grep'],
146
+ });
147
+
148
+ expect(run.status).toBe('completed');
149
+ expect(run.output).toBe('subagent final answer');
150
+ expect(run.output).not.toContain('completed: analyze codebase deeply');
151
+ });
152
+
153
+ it('does not expose partial output before completion in background mode', async () => {
154
+ appService.delayMs = 80;
155
+ const started = await runner.start('ns2', {
156
+ subagentType: 'Explore',
157
+ prompt: 'background investigation',
158
+ runInBackground: true,
159
+ allowedTools: ['glob'],
160
+ });
161
+ expect(started.status).toBe('running');
162
+
163
+ const inProgress = await runner.poll('ns2', started.agentId);
164
+ expect(inProgress?.status).toBe('running');
165
+ expect(inProgress?.output).toBeUndefined();
166
+
167
+ await sleep(120);
168
+ const completed = await runner.poll('ns2', started.agentId);
169
+ expect(completed?.status).toBe('completed');
170
+ expect(completed?.output).toBe('subagent final answer');
171
+ });
172
+
173
+ it('cancels live background run and reports cancelled status', async () => {
174
+ appService.delayMs = 200;
175
+ const started = await runner.start('ns3', {
176
+ subagentType: 'general-purpose',
177
+ prompt: 'long running operation',
178
+ runInBackground: true,
179
+ allowedTools: ['bash'],
180
+ });
181
+
182
+ const cancelled = await runner.cancel('ns3', started.agentId, 'stop now');
183
+ expect(cancelled?.status).toBe('cancelled');
184
+ expect(cancelled?.error).toBe('stop now');
185
+
186
+ await sleep(30);
187
+ const polled = await runner.poll('ns3', started.agentId);
188
+ expect(polled?.status).toBe('cancelled');
189
+ });
190
+ });