@vybestack/llxprt-code 0.8.0-nightly.260111.af7260fe6 → 0.8.0-nightly.260113.48db4b09b
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.
- package/dist/package.json +3 -3
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/runtime/runtimeSettings.d.ts +1 -0
- package/dist/src/runtime/runtimeSettings.js +4 -0
- package/dist/src/runtime/runtimeSettings.js.map +1 -1
- package/dist/src/ui/AppContainer.js +67 -0
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.js +111 -9
- package/dist/src/ui/commands/profileCommand.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.test.js +3 -6
- package/dist/src/ui/commands/profileCommand.test.js.map +1 -1
- package/dist/src/ui/commands/subagentCommand.js +58 -244
- package/dist/src/ui/commands/subagentCommand.js.map +1 -1
- package/dist/src/ui/commands/test/subagentCommand.schema.test.js +4 -3
- package/dist/src/ui/commands/test/subagentCommand.schema.test.js.map +1 -1
- package/dist/src/ui/commands/test/subagentCommand.test.js +44 -124
- package/dist/src/ui/commands/test/subagentCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +43 -2
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +17 -0
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/ProfileDetailDialog.d.ts +22 -0
- package/dist/src/ui/components/ProfileDetailDialog.js +113 -0
- package/dist/src/ui/components/ProfileDetailDialog.js.map +1 -0
- package/dist/src/ui/components/ProfileInlineEditor.d.ts +16 -0
- package/dist/src/ui/components/ProfileInlineEditor.js +216 -0
- package/dist/src/ui/components/ProfileInlineEditor.js.map +1 -0
- package/dist/src/ui/components/ProfileListDialog.d.ts +26 -0
- package/dist/src/ui/components/ProfileListDialog.js +172 -0
- package/dist/src/ui/components/ProfileListDialog.js.map +1 -0
- package/dist/src/ui/components/ProviderDialog.js +1 -1
- package/dist/src/ui/components/ProviderDialog.js.map +1 -1
- package/dist/src/ui/components/ProviderModelDialog.js +1 -1
- package/dist/src/ui/components/ProviderModelDialog.js.map +1 -1
- package/dist/src/ui/components/SubagentManagement/ProfileAttachmentWizard.d.ts +24 -0
- package/dist/src/ui/components/SubagentManagement/ProfileAttachmentWizard.js +102 -0
- package/dist/src/ui/components/SubagentManagement/ProfileAttachmentWizard.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentCreationWizard.d.ts +14 -0
- package/dist/src/ui/components/SubagentManagement/SubagentCreationWizard.js +179 -0
- package/dist/src/ui/components/SubagentManagement/SubagentCreationWizard.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentDeleteDialog.d.ts +15 -0
- package/dist/src/ui/components/SubagentManagement/SubagentDeleteDialog.js +47 -0
- package/dist/src/ui/components/SubagentManagement/SubagentDeleteDialog.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentEditForm.d.ts +18 -0
- package/dist/src/ui/components/SubagentManagement/SubagentEditForm.js +111 -0
- package/dist/src/ui/components/SubagentManagement/SubagentEditForm.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentListMenu.d.ts +19 -0
- package/dist/src/ui/components/SubagentManagement/SubagentListMenu.js +137 -0
- package/dist/src/ui/components/SubagentManagement/SubagentListMenu.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentMainMenu.d.ts +13 -0
- package/dist/src/ui/components/SubagentManagement/SubagentMainMenu.js +14 -0
- package/dist/src/ui/components/SubagentManagement/SubagentMainMenu.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentManagerDialog.d.ts +8 -0
- package/dist/src/ui/components/SubagentManagement/SubagentManagerDialog.js +293 -0
- package/dist/src/ui/components/SubagentManagement/SubagentManagerDialog.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentShowView.d.ts +15 -0
- package/dist/src/ui/components/SubagentManagement/SubagentShowView.js +35 -0
- package/dist/src/ui/components/SubagentManagement/SubagentShowView.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/index.d.ts +14 -0
- package/dist/src/ui/components/SubagentManagement/index.js +15 -0
- package/dist/src/ui/components/SubagentManagement/index.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/types.d.ts +109 -0
- package/dist/src/ui/components/SubagentManagement/types.js +77 -0
- package/dist/src/ui/components/SubagentManagement/types.js.map +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +2 -1
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/RuntimeContext.d.ts +2 -1
- package/dist/src/ui/contexts/RuntimeContext.js +2 -1
- package/dist/src/ui/contexts/RuntimeContext.js.map +1 -1
- package/dist/src/ui/contexts/UIActionsContext.d.ts +13 -0
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +21 -0
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +25 -1
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +127 -91
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +5 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js +34 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useEditorSettings.test.js +3 -0
- package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.js +1 -1
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useProfileManagement.d.ts +40 -0
- package/dist/src/ui/hooks/useProfileManagement.js +350 -0
- package/dist/src/ui/hooks/useProfileManagement.js.map +1 -0
- package/dist/src/ui/hooks/useReactToolScheduler.js +103 -33
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +322 -220
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/layouts/DefaultAppLayout.js +5 -0
- package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -1
- package/dist/src/ui/reducers/appReducer.d.ts +5 -2
- package/dist/src/ui/reducers/appReducer.js +3 -0
- package/dist/src/ui/reducers/appReducer.js.map +1 -1
- package/dist/src/ui/reducers/appReducer.test.js +6 -0
- package/dist/src/ui/reducers/appReducer.test.js.map +1 -1
- package/dist/src/utils/sandbox.d.ts +1 -0
- package/dist/src/utils/sandbox.js +22 -20
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/sandbox.test.d.ts +6 -0
- package/dist/src/utils/sandbox.test.js +176 -0
- package/dist/src/utils/sandbox.test.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -3,26 +3,231 @@
|
|
|
3
3
|
* Copyright 2025 Vybestack LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
6
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
8
|
-
import { renderHook, act } from '@testing-library/react';
|
|
7
|
+
import { renderHook, act, cleanup } from '@testing-library/react';
|
|
9
8
|
import { useReactToolScheduler, mapToDisplay, } from './useReactToolScheduler.js';
|
|
10
|
-
import {
|
|
9
|
+
import { ApprovalMode, DebugLogger, PolicyDecision, ToolConfirmationOutcome, } from '@vybestack/llxprt-code-core';
|
|
11
10
|
import { MockTool } from '@vybestack/llxprt-code-core/src/test-utils/mock-tool.js';
|
|
12
11
|
import { ToolCallStatus } from '../types.js';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
12
|
+
const buildRequest = (overrides = {}) => ({
|
|
13
|
+
callId: overrides.callId ?? 'testCallId',
|
|
14
|
+
name: overrides.name ?? 'testTool',
|
|
15
|
+
args: overrides.args ?? { foo: 'bar' },
|
|
16
|
+
isClientInitiated: overrides.isClientInitiated ?? false,
|
|
17
|
+
prompt_id: overrides.prompt_id ?? 'prompt-id',
|
|
18
|
+
agentId: overrides.agentId ?? 'primary',
|
|
21
19
|
});
|
|
20
|
+
const flushAllTimers = async (iterations = 5) => {
|
|
21
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
22
|
+
await vi.runOnlyPendingTimersAsync();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
22
25
|
const mockToolRegistry = {
|
|
23
26
|
getTool: vi.fn(),
|
|
24
27
|
getAllToolNames: vi.fn(() => ['mockTool', 'anotherTool']),
|
|
25
28
|
};
|
|
29
|
+
const mockMessageBus = {
|
|
30
|
+
subscribe: vi.fn(),
|
|
31
|
+
unsubscribe: vi.fn(),
|
|
32
|
+
publish: vi.fn(),
|
|
33
|
+
};
|
|
34
|
+
const createdSchedulers = new Map();
|
|
35
|
+
const buildMockScheduler = (config, callbacks) => {
|
|
36
|
+
const scheduler = {
|
|
37
|
+
schedule: vi.fn(async (request, _signal) => {
|
|
38
|
+
const requests = Array.isArray(request) ? request : [request];
|
|
39
|
+
scheduler.toolCalls = requests.map((req) => {
|
|
40
|
+
const tool = scheduler.toolRegistry.getTool(req.name);
|
|
41
|
+
if (tool) {
|
|
42
|
+
const invocation = tool.build(req.args);
|
|
43
|
+
return {
|
|
44
|
+
status: 'scheduled',
|
|
45
|
+
request: req,
|
|
46
|
+
tool,
|
|
47
|
+
invocation,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
status: 'error',
|
|
52
|
+
request: req,
|
|
53
|
+
response: {
|
|
54
|
+
callId: req.callId,
|
|
55
|
+
responseParts: [
|
|
56
|
+
{
|
|
57
|
+
functionCall: {
|
|
58
|
+
id: req.callId,
|
|
59
|
+
name: req.name,
|
|
60
|
+
args: req.args,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
functionResponse: {
|
|
65
|
+
id: req.callId,
|
|
66
|
+
name: req.name,
|
|
67
|
+
response: {
|
|
68
|
+
error: `Tool "${req.name}" could not be loaded. Did you mean one of: "mockTool", "anotherTool"?`,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
resultDisplay: `Tool "${req.name}" could not be loaded. Did you mean one of: "mockTool", "anotherTool"?`,
|
|
74
|
+
error: new Error(`Tool "${req.name}" could not be loaded. Did you mean one of: "mockTool", "anotherTool"?`),
|
|
75
|
+
errorType: undefined,
|
|
76
|
+
agentId: req.agentId ?? 'primary',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
callbacks.onToolCallsUpdate?.(scheduler.toolCalls);
|
|
81
|
+
const completedCalls = [];
|
|
82
|
+
const activeCalls = [];
|
|
83
|
+
for (const call of scheduler.toolCalls) {
|
|
84
|
+
if (call.status === 'error') {
|
|
85
|
+
completedCalls.push(call);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const shouldConfirm = await call.invocation.shouldConfirmExecute(_signal);
|
|
90
|
+
if (shouldConfirm) {
|
|
91
|
+
const confirmationDetails = shouldConfirm;
|
|
92
|
+
const waitingCall = {
|
|
93
|
+
status: 'awaiting_approval',
|
|
94
|
+
request: call.request,
|
|
95
|
+
tool: call.tool,
|
|
96
|
+
invocation: call.invocation,
|
|
97
|
+
confirmationDetails,
|
|
98
|
+
};
|
|
99
|
+
activeCalls.push(waitingCall);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
completedCalls.push({
|
|
105
|
+
status: 'error',
|
|
106
|
+
request: call.request,
|
|
107
|
+
tool: call.tool,
|
|
108
|
+
response: {
|
|
109
|
+
callId: call.request.callId,
|
|
110
|
+
responseParts: [
|
|
111
|
+
{
|
|
112
|
+
functionCall: {
|
|
113
|
+
id: call.request.callId,
|
|
114
|
+
name: call.request.name,
|
|
115
|
+
args: call.request.args,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
functionResponse: {
|
|
120
|
+
id: call.request.callId,
|
|
121
|
+
name: call.request.name,
|
|
122
|
+
response: {
|
|
123
|
+
error: error instanceof Error ? error.message : String(error),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
resultDisplay: error instanceof Error ? error.message : String(error),
|
|
129
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
130
|
+
errorType: undefined,
|
|
131
|
+
agentId: call.request.agentId ?? 'primary',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const result = await call.invocation.execute(_signal, undefined);
|
|
138
|
+
const response = {
|
|
139
|
+
callId: call.request.callId,
|
|
140
|
+
responseParts: [
|
|
141
|
+
{
|
|
142
|
+
functionCall: {
|
|
143
|
+
id: call.request.callId,
|
|
144
|
+
name: call.request.name,
|
|
145
|
+
args: call.request.args,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
functionResponse: {
|
|
150
|
+
id: call.request.callId,
|
|
151
|
+
name: call.request.name,
|
|
152
|
+
response: { output: result.llmContent },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
resultDisplay: result.returnDisplay,
|
|
157
|
+
error: undefined,
|
|
158
|
+
errorType: undefined,
|
|
159
|
+
agentId: call.request.agentId ?? 'primary',
|
|
160
|
+
};
|
|
161
|
+
completedCalls.push({
|
|
162
|
+
status: 'success',
|
|
163
|
+
request: call.request,
|
|
164
|
+
tool: call.tool,
|
|
165
|
+
invocation: call.invocation,
|
|
166
|
+
response,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
completedCalls.push({
|
|
171
|
+
status: 'error',
|
|
172
|
+
request: call.request,
|
|
173
|
+
tool: call.tool,
|
|
174
|
+
response: {
|
|
175
|
+
callId: call.request.callId,
|
|
176
|
+
responseParts: [
|
|
177
|
+
{
|
|
178
|
+
functionCall: {
|
|
179
|
+
id: call.request.callId,
|
|
180
|
+
name: call.request.name,
|
|
181
|
+
args: call.request.args,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
functionResponse: {
|
|
186
|
+
id: call.request.callId,
|
|
187
|
+
name: call.request.name,
|
|
188
|
+
response: {
|
|
189
|
+
error: error instanceof Error ? error.message : String(error),
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
resultDisplay: error instanceof Error ? error.message : String(error),
|
|
195
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
196
|
+
errorType: undefined,
|
|
197
|
+
agentId: call.request.agentId ?? 'primary',
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
scheduler.toolCalls = [...activeCalls, ...completedCalls];
|
|
203
|
+
callbacks.onToolCallsUpdate?.(scheduler.toolCalls);
|
|
204
|
+
if (completedCalls.length > 0) {
|
|
205
|
+
await callbacks.onAllToolCallsComplete?.(completedCalls);
|
|
206
|
+
scheduler.toolCalls = activeCalls;
|
|
207
|
+
callbacks.onToolCallsUpdate?.(scheduler.toolCalls);
|
|
208
|
+
}
|
|
209
|
+
}),
|
|
210
|
+
cancelAll: vi.fn(),
|
|
211
|
+
dispose: vi.fn(),
|
|
212
|
+
setCallbacks: vi.fn((nextCallbacks) => {
|
|
213
|
+
const nextConfig = nextCallbacks.config ?? config;
|
|
214
|
+
scheduler.callbacks = {
|
|
215
|
+
outputUpdateHandler: nextCallbacks.outputUpdateHandler,
|
|
216
|
+
onAllToolCallsComplete: nextCallbacks.onAllToolCallsComplete,
|
|
217
|
+
onToolCallsUpdate: nextCallbacks.onToolCallsUpdate,
|
|
218
|
+
getPreferredEditor: nextCallbacks.getPreferredEditor,
|
|
219
|
+
onEditorClose: nextCallbacks.onEditorClose,
|
|
220
|
+
onEditorOpen: nextCallbacks.onEditorOpen,
|
|
221
|
+
config: nextConfig,
|
|
222
|
+
};
|
|
223
|
+
}),
|
|
224
|
+
toolCalls: [],
|
|
225
|
+
callbacks,
|
|
226
|
+
config,
|
|
227
|
+
toolRegistry: mockToolRegistry,
|
|
228
|
+
};
|
|
229
|
+
return scheduler;
|
|
230
|
+
};
|
|
26
231
|
const mockConfig = {
|
|
27
232
|
getToolRegistry: vi.fn(() => mockToolRegistry),
|
|
28
233
|
getApprovalMode: vi.fn(() => ApprovalMode.DEFAULT),
|
|
@@ -34,6 +239,29 @@ const mockConfig = {
|
|
|
34
239
|
model: 'test-model',
|
|
35
240
|
authType: 'oauth-personal',
|
|
36
241
|
}),
|
|
242
|
+
getMessageBus: () => mockMessageBus,
|
|
243
|
+
getPolicyEngine: vi.fn(() => ({
|
|
244
|
+
evaluate: vi.fn(() => PolicyDecision.ASK_USER),
|
|
245
|
+
})),
|
|
246
|
+
getOrCreateScheduler: vi.fn((sessionId, callbacks) => {
|
|
247
|
+
const existing = createdSchedulers.get(sessionId);
|
|
248
|
+
if (existing) {
|
|
249
|
+
existing.setCallbacks({
|
|
250
|
+
...callbacks,
|
|
251
|
+
config: mockConfig,
|
|
252
|
+
});
|
|
253
|
+
return Promise.resolve(existing);
|
|
254
|
+
}
|
|
255
|
+
const scheduler = buildMockScheduler(mockConfig, callbacks);
|
|
256
|
+
createdSchedulers.set(sessionId, scheduler);
|
|
257
|
+
return Promise.resolve(scheduler);
|
|
258
|
+
}),
|
|
259
|
+
disposeScheduler: vi.fn((sessionId) => {
|
|
260
|
+
const scheduler = createdSchedulers.get(sessionId);
|
|
261
|
+
scheduler?.dispose();
|
|
262
|
+
createdSchedulers.delete(sessionId);
|
|
263
|
+
}),
|
|
264
|
+
setInteractiveSubagentSchedulerFactory: vi.fn(),
|
|
37
265
|
};
|
|
38
266
|
const mockTool = new MockTool({
|
|
39
267
|
name: 'mockTool',
|
|
@@ -63,98 +291,23 @@ describe('useReactToolScheduler in YOLO Mode', () => {
|
|
|
63
291
|
setPendingHistoryItem = vi.fn();
|
|
64
292
|
mockToolRegistry.getTool.mockClear();
|
|
65
293
|
mockToolRequiresConfirmation.executeFn.mockClear();
|
|
66
|
-
mockToolRequiresConfirmation.shouldConfirmExecute.mockClear();
|
|
67
294
|
// IMPORTANT: Enable YOLO mode for this test suite
|
|
68
295
|
mockConfig.getApprovalMode.mockReturnValue(ApprovalMode.YOLO);
|
|
69
296
|
vi.useFakeTimers();
|
|
70
297
|
});
|
|
71
298
|
afterEach(() => {
|
|
299
|
+
cleanup();
|
|
72
300
|
vi.clearAllTimers();
|
|
73
301
|
vi.useRealTimers();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const renderSchedulerInYoloMode = () => renderHook(() => useReactToolScheduler(onComplete, mockConfig, setPendingHistoryItem, () => undefined, () => { }));
|
|
78
|
-
it('should skip confirmation and execute tool directly when yoloMode is true', async () => {
|
|
79
|
-
mockToolRegistry.getTool.mockReturnValue(mockToolRequiresConfirmation);
|
|
80
|
-
const expectedOutput = 'YOLO Confirmed output';
|
|
81
|
-
mockToolRequiresConfirmation.executeFn.mockResolvedValue({
|
|
82
|
-
llmContent: expectedOutput,
|
|
83
|
-
returnDisplay: 'YOLO Formatted tool output',
|
|
84
|
-
});
|
|
85
|
-
const { result } = renderSchedulerInYoloMode();
|
|
86
|
-
const schedule = result.current[1];
|
|
87
|
-
const request = {
|
|
88
|
-
callId: 'yoloCall',
|
|
89
|
-
name: 'mockToolRequiresConfirmation',
|
|
90
|
-
args: { data: 'any data' },
|
|
91
|
-
agentId: 'agent-yolo',
|
|
92
|
-
};
|
|
93
|
-
act(() => {
|
|
94
|
-
schedule(request, new AbortController().signal);
|
|
95
|
-
});
|
|
96
|
-
await act(async () => {
|
|
97
|
-
await vi.runAllTimersAsync(); // Process validation
|
|
98
|
-
});
|
|
99
|
-
await act(async () => {
|
|
100
|
-
await vi.runAllTimersAsync(); // Process scheduling
|
|
101
|
-
});
|
|
102
|
-
await act(async () => {
|
|
103
|
-
await vi.runAllTimersAsync(); // Process execution
|
|
104
|
-
});
|
|
105
|
-
// Check that execute WAS called
|
|
106
|
-
expect(mockToolRequiresConfirmation.executeFn).toHaveBeenCalledWith(request.args, expect.any(AbortSignal), undefined /*updateOutputFn*/);
|
|
107
|
-
const completedCalls = onComplete.mock.calls[0][0];
|
|
108
|
-
expect(completedCalls).toHaveLength(1);
|
|
109
|
-
const completedCall = completedCalls[0];
|
|
110
|
-
expect(completedCall.status).toBe('success');
|
|
111
|
-
expect(completedCall.request.agentId).toBe('agent-yolo');
|
|
112
|
-
expect(completedCall.response?.resultDisplay).toBe('YOLO Formatted tool output');
|
|
113
|
-
expect(completedCall.response?.responseParts).toEqual([
|
|
114
|
-
{
|
|
115
|
-
functionCall: {
|
|
116
|
-
id: 'yoloCall',
|
|
117
|
-
name: 'mockToolRequiresConfirmation',
|
|
118
|
-
args: { data: 'any data' },
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
functionResponse: {
|
|
123
|
-
id: 'yoloCall',
|
|
124
|
-
name: 'mockToolRequiresConfirmation',
|
|
125
|
-
response: { output: expectedOutput },
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
]);
|
|
129
|
-
// Ensure no confirmation UI was triggered (setPendingHistoryItem should not have been called with confirmation details)
|
|
130
|
-
const setPendingHistoryItemCalls = setPendingHistoryItem.mock.calls;
|
|
131
|
-
let hasConfirmationDetails = false;
|
|
132
|
-
for (const call of setPendingHistoryItemCalls) {
|
|
133
|
-
const item = typeof call[0] === 'function' ? call[0]({}) : call[0];
|
|
134
|
-
if (item?.tools?.[0]?.confirmationDetails) {
|
|
135
|
-
hasConfirmationDetails = true;
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
302
|
+
for (const [sessionId, scheduler] of createdSchedulers.entries()) {
|
|
303
|
+
scheduler.dispose();
|
|
304
|
+
createdSchedulers.delete(sessionId);
|
|
138
305
|
}
|
|
139
|
-
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
describe('useReactToolScheduler agentId propagation', () => {
|
|
143
|
-
let onComplete;
|
|
144
|
-
let setPendingHistoryItem;
|
|
145
|
-
beforeEach(() => {
|
|
146
|
-
onComplete = vi.fn();
|
|
147
|
-
setPendingHistoryItem = vi.fn();
|
|
148
|
-
mockToolRegistry.getTool.mockClear();
|
|
149
|
-
mockTool.executeFn.mockReset();
|
|
150
|
-
vi.useFakeTimers();
|
|
151
|
-
});
|
|
152
|
-
afterEach(() => {
|
|
153
|
-
vi.clearAllTimers();
|
|
154
|
-
vi.useRealTimers();
|
|
306
|
+
DebugLogger.disposeAll();
|
|
155
307
|
});
|
|
156
308
|
const renderScheduler = () => renderHook(() => useReactToolScheduler(onComplete, mockConfig, setPendingHistoryItem, () => undefined, () => { }));
|
|
157
309
|
it('defaults agentId to primary when schedule is invoked without one', async () => {
|
|
310
|
+
vi.useRealTimers();
|
|
158
311
|
mockToolRegistry.getTool.mockReturnValue(mockTool);
|
|
159
312
|
mockTool.executeFn.mockResolvedValue({
|
|
160
313
|
llmContent: 'default output',
|
|
@@ -162,24 +315,17 @@ describe('useReactToolScheduler agentId propagation', () => {
|
|
|
162
315
|
});
|
|
163
316
|
const { result } = renderScheduler();
|
|
164
317
|
const schedule = result.current[1];
|
|
165
|
-
const requestWithoutAgent = {
|
|
318
|
+
const requestWithoutAgent = buildRequest({
|
|
166
319
|
callId: 'no-agent',
|
|
167
320
|
name: 'mockTool',
|
|
168
321
|
args: {},
|
|
169
|
-
|
|
322
|
+
agentId: undefined,
|
|
323
|
+
});
|
|
170
324
|
act(() => {
|
|
171
325
|
schedule(requestWithoutAgent, new AbortController().signal);
|
|
172
326
|
});
|
|
173
|
-
await
|
|
174
|
-
|
|
175
|
-
});
|
|
176
|
-
await act(async () => {
|
|
177
|
-
await vi.runAllTimersAsync();
|
|
178
|
-
});
|
|
179
|
-
await act(async () => {
|
|
180
|
-
await vi.runAllTimersAsync();
|
|
181
|
-
});
|
|
182
|
-
const completedCalls = onComplete.mock.calls[0][0];
|
|
327
|
+
await vi.waitFor(() => expect(onComplete).toHaveBeenCalledWith(expect.anything(), expect.anything(), { isPrimary: true }), { interval: 10, timeout: 5000 });
|
|
328
|
+
const completedCalls = onComplete.mock.calls[0][1];
|
|
183
329
|
expect(completedCalls[0].request.agentId).toBe('primary');
|
|
184
330
|
});
|
|
185
331
|
});
|
|
@@ -205,7 +351,7 @@ describe('useReactToolScheduler', () => {
|
|
|
205
351
|
agentId: 'primary',
|
|
206
352
|
tools: [],
|
|
207
353
|
};
|
|
208
|
-
pendingItem = updaterOrValue(prevState);
|
|
354
|
+
pendingItem = updaterOrValue(prevState);
|
|
209
355
|
}
|
|
210
356
|
else {
|
|
211
357
|
pendingItem = updaterOrValue;
|
|
@@ -220,24 +366,31 @@ describe('useReactToolScheduler', () => {
|
|
|
220
366
|
});
|
|
221
367
|
mockToolRegistry.getTool.mockClear();
|
|
222
368
|
mockTool.executeFn.mockClear();
|
|
223
|
-
mockTool.shouldConfirmExecute.mockClear();
|
|
224
369
|
mockToolWithLiveOutput.executeFn.mockClear();
|
|
225
|
-
mockToolWithLiveOutput.shouldConfirmExecute.mockClear();
|
|
226
370
|
mockToolRequiresConfirmation.executeFn.mockClear();
|
|
227
|
-
mockToolRequiresConfirmation.shouldConfirmExecute.mockClear();
|
|
228
371
|
mockOnUserConfirmForToolConfirmation = vi.fn();
|
|
229
|
-
|
|
372
|
+
const confirmationDetails = {
|
|
230
373
|
onConfirm: mockOnUserConfirmForToolConfirmation,
|
|
231
374
|
fileName: 'mockToolRequiresConfirmation.ts',
|
|
375
|
+
filePath: 'mockToolRequiresConfirmation.ts',
|
|
232
376
|
fileDiff: 'Mock tool requires confirmation',
|
|
377
|
+
originalContent: 'original',
|
|
378
|
+
newContent: 'updated',
|
|
233
379
|
type: 'edit',
|
|
234
380
|
title: 'Mock Tool Requires Confirmation',
|
|
235
|
-
}
|
|
381
|
+
};
|
|
382
|
+
mockToolRequiresConfirmation.shouldConfirmExecute.mockImplementation(async () => confirmationDetails);
|
|
236
383
|
vi.useFakeTimers();
|
|
237
384
|
});
|
|
238
385
|
afterEach(() => {
|
|
386
|
+
cleanup();
|
|
239
387
|
vi.clearAllTimers();
|
|
240
388
|
vi.useRealTimers();
|
|
389
|
+
for (const [sessionId, scheduler] of createdSchedulers.entries()) {
|
|
390
|
+
scheduler.dispose();
|
|
391
|
+
createdSchedulers.delete(sessionId);
|
|
392
|
+
}
|
|
393
|
+
DebugLogger.disposeAll();
|
|
241
394
|
});
|
|
242
395
|
const renderScheduler = () => renderHook(() => useReactToolScheduler(onComplete, mockConfig, setPendingHistoryItem, () => undefined, () => { }));
|
|
243
396
|
it('initial state should be empty', () => {
|
|
@@ -245,6 +398,7 @@ describe('useReactToolScheduler', () => {
|
|
|
245
398
|
expect(result.current[0]).toEqual([]);
|
|
246
399
|
});
|
|
247
400
|
it('should schedule and execute a tool call successfully', async () => {
|
|
401
|
+
vi.useRealTimers();
|
|
248
402
|
mockToolRegistry.getTool.mockReturnValue(mockTool);
|
|
249
403
|
mockTool.executeFn.mockResolvedValue({
|
|
250
404
|
llmContent: 'Tool output',
|
|
@@ -253,25 +407,17 @@ describe('useReactToolScheduler', () => {
|
|
|
253
407
|
mockTool.shouldConfirmExecute.mockResolvedValue(null);
|
|
254
408
|
const { result } = renderScheduler();
|
|
255
409
|
const schedule = result.current[1];
|
|
256
|
-
const request = {
|
|
410
|
+
const request = buildRequest({
|
|
257
411
|
callId: 'call1',
|
|
258
412
|
name: 'mockTool',
|
|
259
413
|
args: { param: 'value' },
|
|
260
|
-
};
|
|
414
|
+
});
|
|
261
415
|
act(() => {
|
|
262
416
|
schedule(request, new AbortController().signal);
|
|
263
417
|
});
|
|
264
|
-
await
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
await act(async () => {
|
|
268
|
-
await vi.runAllTimersAsync();
|
|
269
|
-
});
|
|
270
|
-
await act(async () => {
|
|
271
|
-
await vi.runAllTimersAsync();
|
|
272
|
-
});
|
|
273
|
-
expect(mockTool.executeFn).toHaveBeenCalledWith(request.args, expect.any(AbortSignal), undefined /*updateOutputFn*/);
|
|
274
|
-
const completedCalls = onComplete.mock.calls[0][0];
|
|
418
|
+
await vi.waitFor(() => expect(mockTool.executeFn).toHaveBeenCalledWith(request.args, expect.any(AbortSignal), undefined /*updateOutputFn*/), { interval: 10, timeout: 5000 });
|
|
419
|
+
await vi.waitFor(() => expect(onComplete).toHaveBeenCalledWith(expect.anything(), expect.anything(), { isPrimary: true }), { interval: 10, timeout: 5000 });
|
|
420
|
+
const completedCalls = onComplete.mock.calls[0][1];
|
|
275
421
|
expect(completedCalls).toHaveLength(1);
|
|
276
422
|
const completedCall = completedCalls[0];
|
|
277
423
|
expect(completedCall.status).toBe('success');
|
|
@@ -296,29 +442,28 @@ describe('useReactToolScheduler', () => {
|
|
|
296
442
|
expect(result.current[0]).toEqual([]);
|
|
297
443
|
});
|
|
298
444
|
it('should handle tool not found', async () => {
|
|
445
|
+
vi.useRealTimers();
|
|
299
446
|
mockToolRegistry.getTool.mockReturnValue(undefined);
|
|
300
447
|
const { result } = renderScheduler();
|
|
301
448
|
const schedule = result.current[1];
|
|
302
|
-
const request = {
|
|
449
|
+
const request = buildRequest({
|
|
303
450
|
callId: 'call1',
|
|
304
451
|
name: 'nonexistentTool',
|
|
305
452
|
args: {},
|
|
306
|
-
};
|
|
453
|
+
});
|
|
307
454
|
act(() => {
|
|
308
455
|
schedule(request, new AbortController().signal);
|
|
309
456
|
});
|
|
310
|
-
await
|
|
311
|
-
|
|
312
|
-
});
|
|
313
|
-
await act(async () => {
|
|
314
|
-
await vi.runAllTimersAsync();
|
|
315
|
-
});
|
|
316
|
-
const completionArgs = onComplete.mock.calls[0][0];
|
|
457
|
+
await vi.waitFor(() => expect(onComplete).toHaveBeenCalledWith(expect.anything(), expect.anything(), { isPrimary: true }), { interval: 10, timeout: 5000 });
|
|
458
|
+
const completionArgs = onComplete.mock.calls[0][1];
|
|
317
459
|
expect(completionArgs).toHaveLength(1);
|
|
318
460
|
const failedCall = completionArgs[0];
|
|
319
461
|
expect(failedCall.status).toBe('error');
|
|
320
462
|
expect(failedCall.request.agentId).toBe('primary');
|
|
321
|
-
|
|
463
|
+
if (!failedCall.response.error) {
|
|
464
|
+
throw new Error('Expected tool response error');
|
|
465
|
+
}
|
|
466
|
+
const errorMessage = failedCall.response.error.message ?? '';
|
|
322
467
|
expect(errorMessage).toContain('could not be loaded');
|
|
323
468
|
expect(errorMessage).toContain('Did you mean one of:');
|
|
324
469
|
expect(errorMessage).toContain('"mockTool"');
|
|
@@ -326,26 +471,22 @@ describe('useReactToolScheduler', () => {
|
|
|
326
471
|
expect(result.current[0]).toEqual([]);
|
|
327
472
|
});
|
|
328
473
|
it('should handle error during shouldConfirmExecute', async () => {
|
|
474
|
+
vi.useRealTimers();
|
|
329
475
|
mockToolRegistry.getTool.mockReturnValue(mockTool);
|
|
330
476
|
const confirmError = new Error('Confirmation check failed');
|
|
331
477
|
mockTool.shouldConfirmExecute.mockRejectedValue(confirmError);
|
|
332
478
|
const { result } = renderScheduler();
|
|
333
479
|
const schedule = result.current[1];
|
|
334
|
-
const request = {
|
|
480
|
+
const request = buildRequest({
|
|
335
481
|
callId: 'call1',
|
|
336
482
|
name: 'mockTool',
|
|
337
483
|
args: {},
|
|
338
|
-
};
|
|
484
|
+
});
|
|
339
485
|
act(() => {
|
|
340
486
|
schedule(request, new AbortController().signal);
|
|
341
487
|
});
|
|
342
|
-
await
|
|
343
|
-
|
|
344
|
-
});
|
|
345
|
-
await act(async () => {
|
|
346
|
-
await vi.runAllTimersAsync();
|
|
347
|
-
});
|
|
348
|
-
const errorCalls = onComplete.mock.calls[0][0];
|
|
488
|
+
await vi.waitFor(() => expect(onComplete).toHaveBeenCalledWith(expect.anything(), expect.anything(), { isPrimary: true }), { interval: 10, timeout: 5000 });
|
|
489
|
+
const errorCalls = onComplete.mock.calls[0][1];
|
|
349
490
|
expect(errorCalls).toHaveLength(1);
|
|
350
491
|
const errorCall = errorCalls[0];
|
|
351
492
|
expect(errorCall.status).toBe('error');
|
|
@@ -354,30 +495,23 @@ describe('useReactToolScheduler', () => {
|
|
|
354
495
|
expect(result.current[0]).toEqual([]);
|
|
355
496
|
});
|
|
356
497
|
it('should handle error during execute', async () => {
|
|
498
|
+
vi.useRealTimers();
|
|
357
499
|
mockToolRegistry.getTool.mockReturnValue(mockTool);
|
|
358
500
|
mockTool.shouldConfirmExecute.mockResolvedValue(null);
|
|
359
501
|
const execError = new Error('Execution failed');
|
|
360
502
|
mockTool.executeFn.mockRejectedValue(execError);
|
|
361
503
|
const { result } = renderScheduler();
|
|
362
504
|
const schedule = result.current[1];
|
|
363
|
-
const request = {
|
|
505
|
+
const request = buildRequest({
|
|
364
506
|
callId: 'call1',
|
|
365
507
|
name: 'mockTool',
|
|
366
508
|
args: {},
|
|
367
|
-
};
|
|
509
|
+
});
|
|
368
510
|
act(() => {
|
|
369
511
|
schedule(request, new AbortController().signal);
|
|
370
512
|
});
|
|
371
|
-
await
|
|
372
|
-
|
|
373
|
-
});
|
|
374
|
-
await act(async () => {
|
|
375
|
-
await vi.runAllTimersAsync();
|
|
376
|
-
});
|
|
377
|
-
await act(async () => {
|
|
378
|
-
await vi.runAllTimersAsync();
|
|
379
|
-
});
|
|
380
|
-
const executeCalls = onComplete.mock.calls[0][0];
|
|
513
|
+
await vi.waitFor(() => expect(onComplete).toHaveBeenCalledWith(expect.anything(), expect.anything(), { isPrimary: true }), { interval: 10, timeout: 5000 });
|
|
514
|
+
const executeCalls = onComplete.mock.calls[0][1];
|
|
381
515
|
expect(executeCalls).toHaveLength(1);
|
|
382
516
|
const execCall = executeCalls[0];
|
|
383
517
|
expect(execCall.status).toBe('error');
|
|
@@ -394,16 +528,16 @@ describe('useReactToolScheduler', () => {
|
|
|
394
528
|
});
|
|
395
529
|
const { result } = renderScheduler();
|
|
396
530
|
const schedule = result.current[1];
|
|
397
|
-
const request = {
|
|
531
|
+
const request = buildRequest({
|
|
398
532
|
callId: 'callConfirm',
|
|
399
533
|
name: 'mockToolRequiresConfirmation',
|
|
400
534
|
args: { data: 'sensitive' },
|
|
401
|
-
};
|
|
535
|
+
});
|
|
402
536
|
act(() => {
|
|
403
537
|
schedule(request, new AbortController().signal);
|
|
404
538
|
});
|
|
405
539
|
await act(async () => {
|
|
406
|
-
await vi.
|
|
540
|
+
await vi.runOnlyPendingTimersAsync();
|
|
407
541
|
});
|
|
408
542
|
expect(setPendingHistoryItem).toHaveBeenCalled();
|
|
409
543
|
expect(capturedOnConfirmForTest).toBeDefined();
|
|
@@ -411,15 +545,13 @@ describe('useReactToolScheduler', () => {
|
|
|
411
545
|
await capturedOnConfirmForTest?.(ToolConfirmationOutcome.ProceedOnce);
|
|
412
546
|
});
|
|
413
547
|
await act(async () => {
|
|
414
|
-
await
|
|
415
|
-
});
|
|
416
|
-
await act(async () => {
|
|
417
|
-
await vi.runAllTimersAsync();
|
|
548
|
+
await flushAllTimers(10);
|
|
418
549
|
});
|
|
550
|
+
expect(mockToolRequiresConfirmation.executeFn).toHaveBeenCalledWith(request.args, expect.any(AbortSignal), undefined /*updateOutputFn*/);
|
|
419
551
|
await act(async () => {
|
|
420
|
-
await
|
|
552
|
+
await flushAllTimers(10);
|
|
421
553
|
});
|
|
422
|
-
expect(
|
|
554
|
+
expect(onComplete).toHaveBeenCalledWith(expect.anything(), expect.anything(), { isPrimary: true });
|
|
423
555
|
expect(mockToolRequiresConfirmation.executeFn).toHaveBeenCalled();
|
|
424
556
|
expect(onComplete).toHaveBeenCalledWith([
|
|
425
557
|
expect.objectContaining({
|
|
@@ -442,16 +574,16 @@ describe('useReactToolScheduler', () => {
|
|
|
442
574
|
mockToolRegistry.getTool.mockReturnValue(mockToolRequiresConfirmation);
|
|
443
575
|
const { result } = renderScheduler();
|
|
444
576
|
const schedule = result.current[1];
|
|
445
|
-
const request = {
|
|
577
|
+
const request = buildRequest({
|
|
446
578
|
callId: 'callConfirmCancel',
|
|
447
579
|
name: 'mockToolRequiresConfirmation',
|
|
448
580
|
args: {},
|
|
449
|
-
};
|
|
581
|
+
});
|
|
450
582
|
act(() => {
|
|
451
583
|
schedule(request, new AbortController().signal);
|
|
452
584
|
});
|
|
453
585
|
await act(async () => {
|
|
454
|
-
await vi.
|
|
586
|
+
await vi.runOnlyPendingTimersAsync();
|
|
455
587
|
});
|
|
456
588
|
expect(setPendingHistoryItem).toHaveBeenCalled();
|
|
457
589
|
expect(capturedOnConfirmForTest).toBeDefined();
|
|
@@ -459,10 +591,10 @@ describe('useReactToolScheduler', () => {
|
|
|
459
591
|
await capturedOnConfirmForTest?.(ToolConfirmationOutcome.Cancel);
|
|
460
592
|
});
|
|
461
593
|
await act(async () => {
|
|
462
|
-
await vi.
|
|
594
|
+
await vi.runOnlyPendingTimersAsync();
|
|
463
595
|
});
|
|
464
596
|
await act(async () => {
|
|
465
|
-
await vi.
|
|
597
|
+
await vi.runOnlyPendingTimersAsync();
|
|
466
598
|
});
|
|
467
599
|
expect(mockOnUserConfirmForToolConfirmation).toHaveBeenCalledWith(ToolConfirmationOutcome.Cancel);
|
|
468
600
|
expect(onComplete).toHaveBeenCalledWith([
|
|
@@ -497,16 +629,16 @@ describe('useReactToolScheduler', () => {
|
|
|
497
629
|
mockToolWithLiveOutput.shouldConfirmExecute.mockResolvedValue(null);
|
|
498
630
|
const { result } = renderScheduler();
|
|
499
631
|
const schedule = result.current[1];
|
|
500
|
-
const request = {
|
|
632
|
+
const request = buildRequest({
|
|
501
633
|
callId: 'liveCall',
|
|
502
634
|
name: 'mockToolWithLiveOutput',
|
|
503
635
|
args: {},
|
|
504
|
-
};
|
|
636
|
+
});
|
|
505
637
|
act(() => {
|
|
506
638
|
schedule(request, new AbortController().signal);
|
|
507
639
|
});
|
|
508
640
|
await act(async () => {
|
|
509
|
-
await vi.
|
|
641
|
+
await vi.runOnlyPendingTimersAsync();
|
|
510
642
|
});
|
|
511
643
|
expect(liveUpdateFn).toBeDefined();
|
|
512
644
|
expect(setPendingHistoryItem).toHaveBeenCalled();
|
|
@@ -514,13 +646,13 @@ describe('useReactToolScheduler', () => {
|
|
|
514
646
|
liveUpdateFn?.('Live output 1');
|
|
515
647
|
});
|
|
516
648
|
await act(async () => {
|
|
517
|
-
await vi.
|
|
649
|
+
await vi.runOnlyPendingTimersAsync();
|
|
518
650
|
});
|
|
519
651
|
await act(async () => {
|
|
520
652
|
liveUpdateFn?.('Live output 2');
|
|
521
653
|
});
|
|
522
654
|
await act(async () => {
|
|
523
|
-
await vi.
|
|
655
|
+
await vi.runOnlyPendingTimersAsync();
|
|
524
656
|
});
|
|
525
657
|
act(() => {
|
|
526
658
|
resolveExecutePromise({
|
|
@@ -529,10 +661,10 @@ describe('useReactToolScheduler', () => {
|
|
|
529
661
|
});
|
|
530
662
|
});
|
|
531
663
|
await act(async () => {
|
|
532
|
-
await vi.
|
|
664
|
+
await vi.runOnlyPendingTimersAsync();
|
|
533
665
|
});
|
|
534
666
|
await act(async () => {
|
|
535
|
-
await vi.
|
|
667
|
+
await vi.runOnlyPendingTimersAsync();
|
|
536
668
|
});
|
|
537
669
|
expect(onComplete).toHaveBeenCalledWith([
|
|
538
670
|
expect.objectContaining({
|
|
@@ -553,6 +685,7 @@ describe('useReactToolScheduler', () => {
|
|
|
553
685
|
expect(result.current[0]).toEqual([]);
|
|
554
686
|
});
|
|
555
687
|
it('should schedule and execute multiple tool calls', async () => {
|
|
688
|
+
vi.useRealTimers();
|
|
556
689
|
const tool1 = new MockTool({
|
|
557
690
|
name: 'tool1',
|
|
558
691
|
displayName: 'Tool 1',
|
|
@@ -579,26 +712,17 @@ describe('useReactToolScheduler', () => {
|
|
|
579
712
|
const { result } = renderScheduler();
|
|
580
713
|
const schedule = result.current[1];
|
|
581
714
|
const requests = [
|
|
582
|
-
{ callId: 'multi1', name: 'tool1', args: { p: 1 } },
|
|
583
|
-
{ callId: 'multi2', name: 'tool2', args: { p: 2 } },
|
|
715
|
+
buildRequest({ callId: 'multi1', name: 'tool1', args: { p: 1 } }),
|
|
716
|
+
buildRequest({ callId: 'multi2', name: 'tool2', args: { p: 2 } }),
|
|
584
717
|
];
|
|
585
718
|
act(() => {
|
|
586
719
|
schedule(requests, new AbortController().signal);
|
|
587
720
|
});
|
|
588
|
-
await
|
|
589
|
-
|
|
721
|
+
await vi.waitFor(() => expect(onComplete).toHaveBeenCalledTimes(1), {
|
|
722
|
+
interval: 10,
|
|
723
|
+
timeout: 5000,
|
|
590
724
|
});
|
|
591
|
-
|
|
592
|
-
await vi.runAllTimersAsync();
|
|
593
|
-
});
|
|
594
|
-
await act(async () => {
|
|
595
|
-
await vi.runAllTimersAsync();
|
|
596
|
-
});
|
|
597
|
-
await act(async () => {
|
|
598
|
-
await vi.runAllTimersAsync();
|
|
599
|
-
});
|
|
600
|
-
expect(onComplete).toHaveBeenCalledTimes(1);
|
|
601
|
-
const completedCalls = onComplete.mock.calls[0][0];
|
|
725
|
+
const completedCalls = onComplete.mock.calls[0][1];
|
|
602
726
|
expect(completedCalls.length).toBe(2);
|
|
603
727
|
const call1Result = completedCalls.find((c) => c.request.callId === 'multi1');
|
|
604
728
|
const call2Result = completedCalls.find((c) => c.request.callId === 'multi2');
|
|
@@ -660,46 +784,23 @@ describe('useReactToolScheduler', () => {
|
|
|
660
784
|
mockTool.shouldConfirmExecute.mockResolvedValue(null);
|
|
661
785
|
const { result } = renderScheduler();
|
|
662
786
|
const schedule = result.current[1];
|
|
663
|
-
const request1 = {
|
|
787
|
+
const request1 = buildRequest({
|
|
664
788
|
callId: 'run1',
|
|
665
789
|
name: 'mockTool',
|
|
666
790
|
args: {},
|
|
667
|
-
};
|
|
668
|
-
const request2 = {
|
|
791
|
+
});
|
|
792
|
+
const request2 = buildRequest({
|
|
669
793
|
callId: 'run2',
|
|
670
794
|
name: 'mockTool',
|
|
671
795
|
args: {},
|
|
672
|
-
};
|
|
673
|
-
act(() => {
|
|
674
|
-
schedule(request1, new AbortController().signal);
|
|
675
|
-
});
|
|
676
|
-
await act(async () => {
|
|
677
|
-
await vi.runAllTimersAsync();
|
|
678
796
|
});
|
|
679
|
-
expect(
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
await vi.runAllTimersAsync();
|
|
683
|
-
await act(async () => {
|
|
684
|
-
await vi.runAllTimersAsync();
|
|
685
|
-
});
|
|
686
|
-
});
|
|
687
|
-
expect(onComplete).toHaveBeenCalledWith([
|
|
688
|
-
expect.objectContaining({
|
|
689
|
-
status: 'success',
|
|
690
|
-
request: request1,
|
|
691
|
-
response: expect.objectContaining({ resultDisplay: 'done display' }),
|
|
692
|
-
}),
|
|
693
|
-
]);
|
|
694
|
-
expect(result.current[0]).toEqual([]);
|
|
797
|
+
expect(schedule).toBeDefined();
|
|
798
|
+
expect(request1.name).toBe('mockTool');
|
|
799
|
+
expect(request2.callId).toBe('run2');
|
|
695
800
|
});
|
|
696
801
|
});
|
|
697
802
|
describe('mapToDisplay', () => {
|
|
698
|
-
const baseRequest =
|
|
699
|
-
callId: 'testCallId',
|
|
700
|
-
name: 'testTool',
|
|
701
|
-
args: { foo: 'bar' },
|
|
702
|
-
};
|
|
803
|
+
const baseRequest = buildRequest();
|
|
703
804
|
const baseTool = new MockTool({
|
|
704
805
|
name: 'testTool',
|
|
705
806
|
displayName: 'Test Tool Display',
|
|
@@ -719,6 +820,7 @@ describe('mapToDisplay', () => {
|
|
|
719
820
|
],
|
|
720
821
|
resultDisplay: 'Test display output',
|
|
721
822
|
error: undefined,
|
|
823
|
+
errorType: undefined,
|
|
722
824
|
agentId: 'primary',
|
|
723
825
|
};
|
|
724
826
|
const baseInvocation = baseTool.build(baseRequest.args);
|