refactor(core): adopt CoreToolCallStatus enum for type safety (#18998)

This commit is contained in:
Jerop Kipruto
2026-02-13 11:27:20 -05:00
committed by GitHub
parent d0c6a56c65
commit 60be42f095
22 changed files with 631 additions and 431 deletions
+6 -2
View File
@@ -17,7 +17,11 @@ import {
type ToolConfirmationPayload,
type ToolCallConfirmationDetails,
} from '../tools/tools.js';
import type { ValidatingToolCall, WaitingToolCall } from './types.js';
import {
type ValidatingToolCall,
type WaitingToolCall,
CoreToolCallStatus,
} from './types.js';
import type { Config } from '../config/config.js';
import type { SchedulerStateManager } from './state-manager.js';
import type { ToolModificationHandler } from './tool-modifier.js';
@@ -145,7 +149,7 @@ export async function resolveConfirmation(
const ideConfirmation =
'ideConfirmation' in details ? details.ideConfirmation : undefined;
state.updateStatus(callId, 'awaiting_approval', {
state.updateStatus(callId, CoreToolCallStatus.AwaitingApproval, {
confirmationDetails: serializableDetails,
correlationId,
});
+51 -45
View File
@@ -77,7 +77,7 @@ import type {
CompletedToolCall,
ToolCallResponseInfo,
} from './types.js';
import { ROOT_SCHEDULER_ID } from './types.js';
import { CoreToolCallStatus, ROOT_SCHEDULER_ID } from './types.js';
import { ToolErrorType } from '../tools/tool-error.js';
import * as ToolUtils from '../utils/tool-utils.js';
import type { EditorType } from '../utils/editor.js';
@@ -276,7 +276,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.enqueue).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({
status: 'error',
status: CoreToolCallStatus.Error,
response: expect.objectContaining({
errorType: ToolErrorType.TOOL_NOT_REGISTERED,
}),
@@ -295,7 +295,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.enqueue).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({
status: 'error',
status: CoreToolCallStatus.Error,
response: expect.objectContaining({
errorType: ToolErrorType.INVALID_TOOL_PARAMS,
}),
@@ -310,7 +310,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.enqueue).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation,
@@ -325,7 +325,7 @@ describe('Scheduler (Orchestrator)', () => {
describe('Phase 2: Queue Management', () => {
it('should drain the queue if multiple calls are scheduled', async () => {
const validatingCall: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -355,7 +355,7 @@ describe('Scheduler (Orchestrator)', () => {
// Execute is the end of the loop, stub it
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall);
await scheduler.schedule(req1, signal);
@@ -382,14 +382,14 @@ describe('Scheduler (Orchestrator)', () => {
});
const validatingCall1: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
};
const validatingCall2: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req2,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -419,7 +419,9 @@ describe('Scheduler (Orchestrator)', () => {
// Yield to the event loop deterministically using queueMicrotask
await new Promise<void>((resolve) => queueMicrotask(resolve));
executionLog.push(`end-${id}`);
return { status: 'success' } as unknown as SuccessfulToolCall;
return {
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall;
});
// Action: Schedule batch of 2 tools
@@ -436,14 +438,14 @@ describe('Scheduler (Orchestrator)', () => {
it('should queue and process multiple schedule() calls made synchronously', async () => {
const validatingCall1: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
};
const validatingCall2: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req2, // Second request
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -483,7 +485,7 @@ describe('Scheduler (Orchestrator)', () => {
// Executor succeeds instantly
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall);
// ACT: Call schedule twice synchronously (without awaiting the first)
@@ -500,14 +502,14 @@ describe('Scheduler (Orchestrator)', () => {
it('should queue requests when scheduler is busy (overlapping batches)', async () => {
const validatingCall1: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
};
const validatingCall2: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req2, // Second request
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -554,13 +556,17 @@ describe('Scheduler (Orchestrator)', () => {
executionLog.push('start-batch-1');
await firstBatchPromise; // Simulating long-running tool execution
executionLog.push('end-batch-1');
return { status: 'success' } as unknown as SuccessfulToolCall;
return {
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall;
});
mockExecutor.execute.mockImplementationOnce(async () => {
executionLog.push('start-batch-2');
executionLog.push('end-batch-2');
return { status: 'success' } as unknown as SuccessfulToolCall;
return {
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall;
});
// 3. ACTIONS
@@ -608,7 +614,7 @@ describe('Scheduler (Orchestrator)', () => {
it('cancelAll() should cancel active call and clear queue', () => {
const activeCall: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -623,7 +629,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'cancelled',
CoreToolCallStatus.Cancelled,
'Operation cancelled by user',
);
// finalizeCall is handled by the processing loop, not synchronously by cancelAll
@@ -656,7 +662,7 @@ describe('Scheduler (Orchestrator)', () => {
describe('Phase 3: Policy & Confirmation Loop', () => {
const validatingCall: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -684,7 +690,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'error',
CoreToolCallStatus.Error,
expect.objectContaining({
errorType: ToolErrorType.POLICY_VIOLATION,
}),
@@ -706,7 +712,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'error',
CoreToolCallStatus.Error,
expect.objectContaining({
errorType: ToolErrorType.POLICY_VIOLATION,
responseParts: expect.arrayContaining([
@@ -731,7 +737,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'error',
CoreToolCallStatus.Error,
expect.objectContaining({
errorType: ToolErrorType.UNHANDLED_EXCEPTION,
responseParts: expect.arrayContaining([
@@ -757,7 +763,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'error',
CoreToolCallStatus.Error,
expect.objectContaining({
errorType: ToolErrorType.POLICY_VIOLATION,
responseParts: expect.arrayContaining([
@@ -786,7 +792,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'error',
CoreToolCallStatus.Error,
expect.objectContaining({
errorType: ToolErrorType.POLICY_VIOLATION,
responseParts: expect.arrayContaining([
@@ -810,7 +816,7 @@ describe('Scheduler (Orchestrator)', () => {
// Provide a mock execute to finish the loop
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall);
await scheduler.schedule(req1, signal);
@@ -827,7 +833,7 @@ describe('Scheduler (Orchestrator)', () => {
// Triggered execution
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'executing',
CoreToolCallStatus.Executing,
);
expect(mockExecutor.execute).toHaveBeenCalled();
});
@@ -835,13 +841,13 @@ describe('Scheduler (Orchestrator)', () => {
it('should auto-approve remaining identical tools in batch after ProceedAlways', async () => {
// Setup: two identical tools
const validatingCall1: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
};
const validatingCall2: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req2,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -874,7 +880,7 @@ describe('Scheduler (Orchestrator)', () => {
});
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall);
await scheduler.schedule([req1, req2], signal);
@@ -904,7 +910,7 @@ describe('Scheduler (Orchestrator)', () => {
vi.mocked(resolveConfirmation).mockResolvedValue(resolution);
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
} as unknown as SuccessfulToolCall);
await scheduler.schedule(req1, signal);
@@ -949,7 +955,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'cancelled',
CoreToolCallStatus.Cancelled,
'User denied execution.',
);
expect(mockStateManager.cancelAllQueued).toHaveBeenCalledWith(
@@ -979,7 +985,7 @@ describe('Scheduler (Orchestrator)', () => {
// Because the signal is aborted, the catch block should convert the error to a cancellation
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'cancelled',
CoreToolCallStatus.Cancelled,
'Operation cancelled',
);
});
@@ -1010,7 +1016,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'cancelled',
CoreToolCallStatus.Cancelled,
'User denied execution.',
);
// We assume the state manager stores these details.
@@ -1021,7 +1027,7 @@ describe('Scheduler (Orchestrator)', () => {
describe('Phase 4: Execution Outcomes', () => {
const validatingCall: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -1047,7 +1053,7 @@ describe('Scheduler (Orchestrator)', () => {
} as unknown as ToolCallResponseInfo;
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
response: mockResponse,
} as unknown as SuccessfulToolCall);
@@ -1055,14 +1061,14 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'success',
CoreToolCallStatus.Success,
mockResponse,
);
});
it('should update state to cancelled when executor returns cancelled status', async () => {
mockExecutor.execute.mockResolvedValue({
status: 'cancelled',
status: CoreToolCallStatus.Cancelled,
response: { callId: 'call-1', responseParts: [] },
} as unknown as CancelledToolCall);
@@ -1070,7 +1076,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'cancelled',
CoreToolCallStatus.Cancelled,
'Operation cancelled',
);
});
@@ -1082,7 +1088,7 @@ describe('Scheduler (Orchestrator)', () => {
} as unknown as ToolCallResponseInfo;
mockExecutor.execute.mockResolvedValue({
status: 'error',
status: CoreToolCallStatus.Error,
response: mockResponse,
} as unknown as ErroredToolCall);
@@ -1090,7 +1096,7 @@ describe('Scheduler (Orchestrator)', () => {
expect(mockStateManager.updateStatus).toHaveBeenCalledWith(
'call-1',
'error',
CoreToolCallStatus.Error,
mockResponse,
);
});
@@ -1103,14 +1109,14 @@ describe('Scheduler (Orchestrator)', () => {
// Mock the execution so the state advances
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
response: mockResponse,
} as unknown as SuccessfulToolCall);
// Mock the state manager to return a SUCCESS state when getToolCall is
// called
const successfulCall: SuccessfulToolCall = {
status: 'success',
status: CoreToolCallStatus.Success,
request: req1,
response: mockResponse,
tool: mockTool,
@@ -1145,7 +1151,7 @@ describe('Scheduler (Orchestrator)', () => {
};
mockExecutor.execute.mockResolvedValue({
status: 'success',
status: CoreToolCallStatus.Success,
response,
} as unknown as SuccessfulToolCall);
@@ -1172,7 +1178,7 @@ describe('Scheduler (Orchestrator)', () => {
});
const validatingCall: ValidatingToolCall = {
status: 'validating',
status: CoreToolCallStatus.Validating,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
@@ -1200,7 +1206,7 @@ describe('Scheduler (Orchestrator)', () => {
mockExecutor.execute.mockImplementation(async () => {
capturedContext = getToolCallContext();
return {
status: 'success',
status: CoreToolCallStatus.Success,
request: req1,
tool: mockTool,
invocation: mockInvocation as unknown as AnyToolInvocation,
+58 -26
View File
@@ -19,6 +19,7 @@ import {
type ExecutingToolCall,
type ValidatingToolCall,
type ErroredToolCall,
CoreToolCallStatus,
} from './types.js';
import { ToolErrorType } from '../tools/tool-error.js';
import { PolicyDecision } from '../policy/types.js';
@@ -212,7 +213,7 @@ export class Scheduler {
if (activeCall && !this.isTerminal(activeCall.status)) {
this.state.updateStatus(
activeCall.request.callId,
'cancelled',
CoreToolCallStatus.Cancelled,
'Operation cancelled by user',
);
}
@@ -226,7 +227,11 @@ export class Scheduler {
}
private isTerminal(status: string) {
return status === 'success' || status === 'error' || status === 'cancelled';
return (
status === CoreToolCallStatus.Success ||
status === CoreToolCallStatus.Error ||
status === CoreToolCallStatus.Cancelled
);
}
// --- Phase 1: Ingestion & Resolution ---
@@ -250,10 +255,12 @@ export class Scheduler {
const tool = toolRegistry.getTool(request.name);
if (!tool) {
return this._createToolNotFoundErroredToolCall(
enrichedRequest,
toolRegistry.getAllToolNames(),
);
return {
...this._createToolNotFoundErroredToolCall(
enrichedRequest,
toolRegistry.getAllToolNames(),
),
};
}
return this._validateAndCreateToolCall(enrichedRequest, tool);
@@ -275,7 +282,7 @@ export class Scheduler {
): ErroredToolCall {
const suggestion = getToolSuggestion(request.name, toolNames);
return {
status: 'error',
status: CoreToolCallStatus.Error,
request,
response: createErrorResponse(
request,
@@ -301,7 +308,7 @@ export class Scheduler {
try {
const invocation = tool.build(request.args);
return {
status: 'validating',
status: CoreToolCallStatus.Validating,
request,
tool,
invocation,
@@ -310,7 +317,7 @@ export class Scheduler {
};
} catch (e) {
return {
status: 'error',
status: CoreToolCallStatus.Error,
request,
tool,
response: createErrorResponse(
@@ -349,8 +356,12 @@ export class Scheduler {
const next = this.state.dequeue();
if (!next) return false;
if (next.status === 'error') {
this.state.updateStatus(next.request.callId, 'error', next.response);
if (next.status === CoreToolCallStatus.Error) {
this.state.updateStatus(
next.request.callId,
CoreToolCallStatus.Error,
next.response,
);
this.state.finalizeCall(next.request.callId);
return true;
}
@@ -359,7 +370,7 @@ export class Scheduler {
const active = this.state.firstActiveCall;
if (!active) return false;
if (active.status === 'validating') {
if (active.status === CoreToolCallStatus.Validating) {
await this._processValidatingCall(active, signal);
}
@@ -379,13 +390,13 @@ export class Scheduler {
if (signal.aborted || err.name === 'AbortError') {
this.state.updateStatus(
active.request.callId,
'cancelled',
CoreToolCallStatus.Cancelled,
'Operation cancelled',
);
} else {
this.state.updateStatus(
active.request.callId,
'error',
CoreToolCallStatus.Error,
createErrorResponse(
active.request,
err,
@@ -417,7 +428,7 @@ export class Scheduler {
this.state.updateStatus(
callId,
'error',
CoreToolCallStatus.Error,
createErrorResponse(
toolCall.request,
new Error(errorMessage),
@@ -456,7 +467,11 @@ export class Scheduler {
// Handle cancellation (cascades to entire batch)
if (outcome === ToolConfirmationOutcome.Cancel) {
this.state.updateStatus(callId, 'cancelled', 'User denied execution.');
this.state.updateStatus(
callId,
CoreToolCallStatus.Cancelled,
'User denied execution.',
);
this.state.finalizeCall(callId);
this.state.cancelAllQueued('User cancelled operation');
return; // Skip execution
@@ -472,9 +487,9 @@ export class Scheduler {
* Executes the tool and records the result.
*/
private async _execute(callId: string, signal: AbortSignal): Promise<void> {
this.state.updateStatus(callId, 'scheduled');
this.state.updateStatus(callId, CoreToolCallStatus.Scheduled);
if (signal.aborted) throw new Error('Operation cancelled');
this.state.updateStatus(callId, 'executing');
this.state.updateStatus(callId, CoreToolCallStatus.Executing);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const activeCall = this.state.firstActiveCall as ExecutingToolCall;
@@ -490,10 +505,15 @@ export class Scheduler {
call: activeCall,
signal,
outputUpdateHandler: (id, out) =>
this.state.updateStatus(id, 'executing', { liveOutput: out }),
this.state.updateStatus(id, CoreToolCallStatus.Executing, {
liveOutput: out,
}),
onUpdateToolCall: (updated) => {
if (updated.status === 'executing' && updated.pid) {
this.state.updateStatus(callId, 'executing', {
if (
updated.status === CoreToolCallStatus.Executing &&
updated.pid
) {
this.state.updateStatus(callId, CoreToolCallStatus.Executing, {
pid: updated.pid,
});
}
@@ -501,12 +521,24 @@ export class Scheduler {
}),
);
if (result.status === 'success') {
this.state.updateStatus(callId, 'success', result.response);
} else if (result.status === 'cancelled') {
this.state.updateStatus(callId, 'cancelled', 'Operation cancelled');
if (result.status === CoreToolCallStatus.Success) {
this.state.updateStatus(
callId,
CoreToolCallStatus.Success,
result.response,
);
} else if (result.status === CoreToolCallStatus.Cancelled) {
this.state.updateStatus(
callId,
CoreToolCallStatus.Cancelled,
'Operation cancelled',
);
} else {
this.state.updateStatus(callId, 'error', result.response);
this.state.updateStatus(
callId,
CoreToolCallStatus.Error,
result.response,
);
}
}
@@ -16,6 +16,7 @@ import type {
ToolCallRequestInfo,
ToolCallResponseInfo,
} from './types.js';
import { CoreToolCallStatus, ROOT_SCHEDULER_ID } from './types.js';
import {
ToolConfirmationOutcome,
type AnyDeclarativeTool,
@@ -23,7 +24,6 @@ import {
} from '../tools/tools.js';
import { MessageBusType } from '../confirmation-bus/types.js';
import type { MessageBus } from '../confirmation-bus/message-bus.js';
import { ROOT_SCHEDULER_ID } from './types.js';
describe('SchedulerStateManager', () => {
const mockRequest: ToolCallRequestInfo = {
@@ -44,7 +44,7 @@ describe('SchedulerStateManager', () => {
} as unknown as AnyToolInvocation;
const createValidatingCall = (id = 'call-1'): ValidatingToolCall => ({
status: 'validating',
status: CoreToolCallStatus.Validating,
request: { ...mockRequest, callId: id },
tool: mockTool,
invocation: mockInvocation,
@@ -97,7 +97,7 @@ describe('SchedulerStateManager', () => {
manager.dequeue();
manager.updateStatus(
call.request.callId,
'success',
CoreToolCallStatus.Success,
createMockResponse(call.request.callId),
);
manager.finalizeCall(call.request.callId);
@@ -105,7 +105,7 @@ describe('SchedulerStateManager', () => {
expect(onTerminalCall).toHaveBeenCalledTimes(1);
expect(onTerminalCall).toHaveBeenCalledWith(
expect.objectContaining({
status: 'success',
status: CoreToolCallStatus.Success,
request: expect.objectContaining({ callId: call.request.callId }),
}),
);
@@ -125,13 +125,13 @@ describe('SchedulerStateManager', () => {
expect(onTerminalCall).toHaveBeenCalledTimes(2);
expect(onTerminalCall).toHaveBeenCalledWith(
expect.objectContaining({
status: 'cancelled',
status: CoreToolCallStatus.Cancelled,
request: expect.objectContaining({ callId: '1' }),
}),
);
expect(onTerminalCall).toHaveBeenCalledWith(
expect.objectContaining({
status: 'cancelled',
status: CoreToolCallStatus.Cancelled,
request: expect.objectContaining({ callId: '2' }),
}),
);
@@ -167,7 +167,7 @@ describe('SchedulerStateManager', () => {
stateManager.dequeue();
stateManager.updateStatus(
'completed-1',
'success',
CoreToolCallStatus.Success,
createMockResponse('completed-1'),
);
stateManager.finalizeCall('completed-1');
@@ -212,10 +212,13 @@ describe('SchedulerStateManager', () => {
stateManager.enqueue([call]);
stateManager.dequeue();
stateManager.updateStatus(call.request.callId, 'scheduled');
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Scheduled,
);
const snapshot = stateManager.getSnapshot();
expect(snapshot[0].status).toBe('scheduled');
expect(snapshot[0].status).toBe(CoreToolCallStatus.Scheduled);
expect(snapshot[0].request.callId).toBe(call.request.callId);
});
@@ -223,11 +226,19 @@ describe('SchedulerStateManager', () => {
const call = createValidatingCall();
stateManager.enqueue([call]);
stateManager.dequeue();
stateManager.updateStatus(call.request.callId, 'scheduled');
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Scheduled,
);
stateManager.updateStatus(call.request.callId, 'executing');
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Executing,
);
expect(stateManager.firstActiveCall?.status).toBe('executing');
expect(stateManager.firstActiveCall?.status).toBe(
CoreToolCallStatus.Executing,
);
});
it('should transition to success and move to completed batch', () => {
@@ -244,7 +255,11 @@ describe('SchedulerStateManager', () => {
};
vi.mocked(onUpdate).mockClear();
stateManager.updateStatus(call.request.callId, 'success', response);
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Success,
response,
);
expect(onUpdate).toHaveBeenCalledTimes(1);
vi.mocked(onUpdate).mockClear();
@@ -254,7 +269,7 @@ describe('SchedulerStateManager', () => {
expect(stateManager.isActive).toBe(false);
expect(stateManager.completedBatch).toHaveLength(1);
const completed = stateManager.completedBatch[0] as SuccessfulToolCall;
expect(completed.status).toBe('success');
expect(completed.status).toBe(CoreToolCallStatus.Success);
expect(completed.response).toEqual(response);
expect(completed.durationMs).toBeDefined();
});
@@ -272,13 +287,17 @@ describe('SchedulerStateManager', () => {
errorType: undefined,
};
stateManager.updateStatus(call.request.callId, 'error', response);
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Error,
response,
);
stateManager.finalizeCall(call.request.callId);
expect(stateManager.isActive).toBe(false);
expect(stateManager.completedBatch).toHaveLength(1);
const completed = stateManager.completedBatch[0] as ErroredToolCall;
expect(completed.status).toBe('error');
expect(completed.status).toBe(CoreToolCallStatus.Error);
expect(completed.response).toEqual(response);
});
@@ -296,12 +315,12 @@ describe('SchedulerStateManager', () => {
stateManager.updateStatus(
call.request.callId,
'awaiting_approval',
CoreToolCallStatus.AwaitingApproval,
details,
);
const active = stateManager.firstActiveCall as WaitingToolCall;
expect(active.status).toBe('awaiting_approval');
expect(active.status).toBe(CoreToolCallStatus.AwaitingApproval);
expect(active.confirmationDetails).toEqual(details);
});
@@ -322,12 +341,12 @@ describe('SchedulerStateManager', () => {
stateManager.updateStatus(
call.request.callId,
'awaiting_approval',
CoreToolCallStatus.AwaitingApproval,
eventDrivenData,
);
const active = stateManager.firstActiveCall as WaitingToolCall;
expect(active.status).toBe('awaiting_approval');
expect(active.status).toBe(CoreToolCallStatus.AwaitingApproval);
expect(active.correlationId).toBe('corr-123');
expect(active.confirmationDetails).toEqual(details);
});
@@ -350,18 +369,18 @@ describe('SchedulerStateManager', () => {
stateManager.updateStatus(
call.request.callId,
'awaiting_approval',
CoreToolCallStatus.AwaitingApproval,
details,
);
stateManager.updateStatus(
call.request.callId,
'cancelled',
CoreToolCallStatus.Cancelled,
'User said no',
);
stateManager.finalizeCall(call.request.callId);
const completed = stateManager.completedBatch[0] as CancelledToolCall;
expect(completed.status).toBe('cancelled');
expect(completed.status).toBe(CoreToolCallStatus.Cancelled);
expect(completed.response.resultDisplay).toEqual({
fileDiff: 'diff',
fileName: 'test.txt',
@@ -372,7 +391,7 @@ describe('SchedulerStateManager', () => {
});
it('should ignore status updates for non-existent callIds', () => {
stateManager.updateStatus('unknown', 'scheduled');
stateManager.updateStatus('unknown', CoreToolCallStatus.Scheduled);
expect(onUpdate).not.toHaveBeenCalled();
});
@@ -382,13 +401,16 @@ describe('SchedulerStateManager', () => {
stateManager.dequeue();
stateManager.updateStatus(
call.request.callId,
'success',
CoreToolCallStatus.Success,
createMockResponse(call.request.callId),
);
stateManager.finalizeCall(call.request.callId);
vi.mocked(onUpdate).mockClear();
stateManager.updateStatus(call.request.callId, 'scheduled');
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Scheduled,
);
expect(onUpdate).not.toHaveBeenCalled();
});
@@ -397,7 +419,10 @@ describe('SchedulerStateManager', () => {
stateManager.enqueue([call]);
stateManager.dequeue();
stateManager.updateStatus(call.request.callId, 'executing');
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Executing,
);
stateManager.finalizeCall(call.request.callId);
expect(stateManager.isActive).toBe(true);
@@ -405,7 +430,7 @@ describe('SchedulerStateManager', () => {
stateManager.updateStatus(
call.request.callId,
'success',
CoreToolCallStatus.Success,
createMockResponse(call.request.callId),
);
stateManager.finalizeCall(call.request.callId);
@@ -420,30 +445,45 @@ describe('SchedulerStateManager', () => {
stateManager.dequeue();
// Start executing
stateManager.updateStatus(call.request.callId, 'executing');
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Executing,
);
let active = stateManager.firstActiveCall as ExecutingToolCall;
expect(active.status).toBe('executing');
expect(active.status).toBe(CoreToolCallStatus.Executing);
expect(active.liveOutput).toBeUndefined();
// Update with live output
stateManager.updateStatus(call.request.callId, 'executing', {
liveOutput: 'chunk 1',
});
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Executing,
{
liveOutput: 'chunk 1',
},
);
active = stateManager.firstActiveCall as ExecutingToolCall;
expect(active.liveOutput).toBe('chunk 1');
// Update with pid (should preserve liveOutput)
stateManager.updateStatus(call.request.callId, 'executing', {
pid: 1234,
});
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Executing,
{
pid: 1234,
},
);
active = stateManager.firstActiveCall as ExecutingToolCall;
expect(active.liveOutput).toBe('chunk 1');
expect(active.pid).toBe(1234);
// Update live output again (should preserve pid)
stateManager.updateStatus(call.request.callId, 'executing', {
liveOutput: 'chunk 2',
});
stateManager.updateStatus(
call.request.callId,
CoreToolCallStatus.Executing,
{
liveOutput: 'chunk 2',
},
);
active = stateManager.firstActiveCall as ExecutingToolCall;
expect(active.liveOutput).toBe('chunk 2');
expect(active.pid).toBe(1234);
@@ -475,7 +515,7 @@ describe('SchedulerStateManager', () => {
stateManager.dequeue();
stateManager.updateStatus(
call.request.callId,
'error',
CoreToolCallStatus.Error,
createMockResponse(call.request.callId),
);
stateManager.finalizeCall(call.request.callId);
@@ -521,7 +561,9 @@ describe('SchedulerStateManager', () => {
expect(stateManager.queueLength).toBe(0);
expect(stateManager.completedBatch).toHaveLength(2);
expect(
stateManager.completedBatch.every((c) => c.status === 'cancelled'),
stateManager.completedBatch.every(
(c) => c.status === CoreToolCallStatus.Cancelled,
),
).toBe(true);
expect(onUpdate).toHaveBeenCalledTimes(1);
});
@@ -538,7 +580,7 @@ describe('SchedulerStateManager', () => {
stateManager.dequeue();
stateManager.updateStatus(
call.request.callId,
'success',
CoreToolCallStatus.Success,
createMockResponse(call.request.callId),
);
stateManager.finalizeCall(call.request.callId);
@@ -555,7 +597,7 @@ describe('SchedulerStateManager', () => {
stateManager.dequeue();
stateManager.updateStatus(
call.request.callId,
'success',
CoreToolCallStatus.Success,
createMockResponse(call.request.callId),
);
stateManager.finalizeCall(call.request.callId);
@@ -578,7 +620,11 @@ describe('SchedulerStateManager', () => {
const call1 = createValidatingCall('1');
stateManager.enqueue([call1]);
stateManager.dequeue();
stateManager.updateStatus('1', 'success', createMockResponse('1'));
stateManager.updateStatus(
'1',
CoreToolCallStatus.Success,
createMockResponse('1'),
);
stateManager.finalizeCall('1');
// 2. Active
+45 -30
View File
@@ -17,6 +17,7 @@ import type {
ExecutingToolCall,
ToolCallResponseInfo,
} from './types.js';
import { CoreToolCallStatus } from './types.js';
import { ROOT_SCHEDULER_ID } from './types.js';
import type {
ToolConfirmationOutcome,
@@ -98,17 +99,17 @@ export class SchedulerStateManager {
*/
updateStatus(
callId: string,
status: 'success',
status: CoreToolCallStatus.Success,
data: ToolCallResponseInfo,
): void;
updateStatus(
callId: string,
status: 'error',
status: CoreToolCallStatus.Error,
data: ToolCallResponseInfo,
): void;
updateStatus(
callId: string,
status: 'awaiting_approval',
status: CoreToolCallStatus.AwaitingApproval,
data:
| ToolCallConfirmationDetails
| {
@@ -116,13 +117,20 @@ export class SchedulerStateManager {
confirmationDetails: SerializableConfirmationDetails;
},
): void;
updateStatus(callId: string, status: 'cancelled', data: string): void;
updateStatus(
callId: string,
status: 'executing',
status: CoreToolCallStatus.Cancelled,
data: string,
): void;
updateStatus(
callId: string,
status: CoreToolCallStatus.Executing,
data?: Partial<ExecutingToolCall>,
): void;
updateStatus(callId: string, status: 'scheduled' | 'validating'): void;
updateStatus(
callId: string,
status: CoreToolCallStatus.Scheduled | CoreToolCallStatus.Validating,
): void;
updateStatus(callId: string, status: Status, auxiliaryData?: unknown): void {
const call = this.activeCalls.get(callId);
if (!call) return;
@@ -152,7 +160,7 @@ export class SchedulerStateManager {
newInvocation: AnyToolInvocation,
): void {
const call = this.activeCalls.get(callId);
if (!call || call.status === 'error') return;
if (!call || call.status === CoreToolCallStatus.Error) return;
this.activeCalls.set(
callId,
@@ -179,7 +187,7 @@ export class SchedulerStateManager {
while (this.queue.length > 0) {
const queuedCall = this.queue.shift()!;
if (queuedCall.status === 'error') {
if (queuedCall.status === CoreToolCallStatus.Error) {
this._completedBatch.push(queuedCall);
this.onTerminalCall?.(queuedCall);
continue;
@@ -222,7 +230,11 @@ export class SchedulerStateManager {
private isTerminalCall(call: ToolCall): call is CompletedToolCall {
const { status } = call;
return status === 'success' || status === 'error' || status === 'cancelled';
return (
status === CoreToolCallStatus.Success ||
status === CoreToolCallStatus.Error ||
status === CoreToolCallStatus.Cancelled
);
}
private transitionCall(
@@ -231,7 +243,7 @@ export class SchedulerStateManager {
auxiliaryData?: unknown,
): ToolCall {
switch (newStatus) {
case 'success': {
case CoreToolCallStatus.Success: {
if (!this.isToolCallResponseInfo(auxiliaryData)) {
throw new Error(
`Invalid data for 'success' transition (callId: ${call.request.callId})`,
@@ -239,7 +251,7 @@ export class SchedulerStateManager {
}
return this.toSuccess(call, auxiliaryData);
}
case 'error': {
case CoreToolCallStatus.Error: {
if (!this.isToolCallResponseInfo(auxiliaryData)) {
throw new Error(
`Invalid data for 'error' transition (callId: ${call.request.callId})`,
@@ -247,7 +259,7 @@ export class SchedulerStateManager {
}
return this.toError(call, auxiliaryData);
}
case 'awaiting_approval': {
case CoreToolCallStatus.AwaitingApproval: {
if (!auxiliaryData) {
throw new Error(
`Missing data for 'awaiting_approval' transition (callId: ${call.request.callId})`,
@@ -255,9 +267,9 @@ export class SchedulerStateManager {
}
return this.toAwaitingApproval(call, auxiliaryData);
}
case 'scheduled':
case CoreToolCallStatus.Scheduled:
return this.toScheduled(call);
case 'cancelled': {
case CoreToolCallStatus.Cancelled: {
if (typeof auxiliaryData !== 'string') {
throw new Error(
`Invalid reason (string) for 'cancelled' transition (callId: ${call.request.callId})`,
@@ -265,9 +277,9 @@ export class SchedulerStateManager {
}
return this.toCancelled(call, auxiliaryData);
}
case 'validating':
case CoreToolCallStatus.Validating:
return this.toValidating(call);
case 'executing': {
case CoreToolCallStatus.Executing: {
if (
auxiliaryData !== undefined &&
!this.isExecutingToolCallPatch(auxiliaryData)
@@ -327,13 +339,13 @@ export class SchedulerStateManager {
call: ToolCall,
response: ToolCallResponseInfo,
): SuccessfulToolCall {
this.validateHasToolAndInvocation(call, 'success');
this.validateHasToolAndInvocation(call, CoreToolCallStatus.Success);
const startTime = 'startTime' in call ? call.startTime : undefined;
return {
request: call.request,
tool: call.tool,
invocation: call.invocation,
status: 'success',
status: CoreToolCallStatus.Success,
response,
durationMs: startTime ? Date.now() - startTime : undefined,
outcome: call.outcome,
@@ -348,7 +360,7 @@ export class SchedulerStateManager {
const startTime = 'startTime' in call ? call.startTime : undefined;
return {
request: call.request,
status: 'error',
status: CoreToolCallStatus.Error,
tool: 'tool' in call ? call.tool : undefined,
response,
durationMs: startTime ? Date.now() - startTime : undefined,
@@ -358,7 +370,10 @@ export class SchedulerStateManager {
}
private toAwaitingApproval(call: ToolCall, data: unknown): WaitingToolCall {
this.validateHasToolAndInvocation(call, 'awaiting_approval');
this.validateHasToolAndInvocation(
call,
CoreToolCallStatus.AwaitingApproval,
);
let confirmationDetails:
| ToolCallConfirmationDetails
@@ -377,7 +392,7 @@ export class SchedulerStateManager {
return {
request: call.request,
tool: call.tool,
status: 'awaiting_approval',
status: CoreToolCallStatus.AwaitingApproval,
correlationId,
confirmationDetails,
startTime: 'startTime' in call ? call.startTime : undefined,
@@ -400,11 +415,11 @@ export class SchedulerStateManager {
}
private toScheduled(call: ToolCall): ScheduledToolCall {
this.validateHasToolAndInvocation(call, 'scheduled');
this.validateHasToolAndInvocation(call, CoreToolCallStatus.Scheduled);
return {
request: call.request,
tool: call.tool,
status: 'scheduled',
status: CoreToolCallStatus.Scheduled,
startTime: 'startTime' in call ? call.startTime : undefined,
outcome: call.outcome,
invocation: call.invocation,
@@ -413,7 +428,7 @@ export class SchedulerStateManager {
}
private toCancelled(call: ToolCall, reason: string): CancelledToolCall {
this.validateHasToolAndInvocation(call, 'cancelled');
this.validateHasToolAndInvocation(call, CoreToolCallStatus.Cancelled);
const startTime = 'startTime' in call ? call.startTime : undefined;
// TODO: Refactor this tool-specific logic into the confirmation details payload.
@@ -444,7 +459,7 @@ export class SchedulerStateManager {
request: call.request,
tool: call.tool,
invocation: call.invocation,
status: 'cancelled',
status: CoreToolCallStatus.Cancelled,
response: {
callId: call.request.callId,
responseParts: [
@@ -468,7 +483,7 @@ export class SchedulerStateManager {
}
private isWaitingToolCall(call: ToolCall): call is WaitingToolCall {
return call.status === 'awaiting_approval';
return call.status === CoreToolCallStatus.AwaitingApproval;
}
private patchCall<T extends ToolCall>(call: T, patch: Partial<T>): T {
@@ -476,11 +491,11 @@ export class SchedulerStateManager {
}
private toValidating(call: ToolCall): ValidatingToolCall {
this.validateHasToolAndInvocation(call, 'validating');
this.validateHasToolAndInvocation(call, CoreToolCallStatus.Validating);
return {
request: call.request,
tool: call.tool,
status: 'validating',
status: CoreToolCallStatus.Validating,
startTime: 'startTime' in call ? call.startTime : undefined,
outcome: call.outcome,
invocation: call.invocation,
@@ -489,7 +504,7 @@ export class SchedulerStateManager {
}
private toExecuting(call: ToolCall, data?: unknown): ExecutingToolCall {
this.validateHasToolAndInvocation(call, 'executing');
this.validateHasToolAndInvocation(call, CoreToolCallStatus.Executing);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const execData = data as Partial<ExecutingToolCall> | undefined;
const liveOutput =
@@ -500,7 +515,7 @@ export class SchedulerStateManager {
return {
request: call.request,
tool: call.tool,
status: 'executing',
status: CoreToolCallStatus.Executing,
startTime: 'startTime' in call ? call.startTime : undefined,
outcome: call.outcome,
invocation: call.invocation,
@@ -11,6 +11,7 @@ import type { ToolResult } from '../tools/tools.js';
import { makeFakeConfig } from '../test-utils/config.js';
import { MockTool } from '../test-utils/mock-tool.js';
import type { ScheduledToolCall } from './types.js';
import { CoreToolCallStatus } from './types.js';
import type { AnyToolInvocation } from '../index.js';
import { SHELL_TOOL_NAME } from '../tools/tool-names.js';
import * as fileUtils from '../utils/fileUtils.js';
@@ -71,7 +72,7 @@ describe('ToolExecutor', () => {
} as ToolResult);
const scheduledCall: ScheduledToolCall = {
status: 'scheduled',
status: CoreToolCallStatus.Scheduled,
request: {
callId: 'call-1',
name: 'testTool',
@@ -91,8 +92,8 @@ describe('ToolExecutor', () => {
onUpdateToolCall,
});
expect(result.status).toBe('success');
if (result.status === 'success') {
expect(result.status).toBe(CoreToolCallStatus.Success);
if (result.status === CoreToolCallStatus.Success) {
const response = result.response.responseParts[0]?.functionResponse
?.response as Record<string, unknown>;
expect(response).toEqual({ output: 'Tool output' });
@@ -111,7 +112,7 @@ describe('ToolExecutor', () => {
);
const scheduledCall: ScheduledToolCall = {
status: 'scheduled',
status: CoreToolCallStatus.Scheduled,
request: {
callId: 'call-2',
name: 'failTool',
@@ -130,8 +131,8 @@ describe('ToolExecutor', () => {
onUpdateToolCall: vi.fn(),
});
expect(result.status).toBe('error');
if (result.status === 'error') {
expect(result.status).toBe(CoreToolCallStatus.Error);
if (result.status === CoreToolCallStatus.Error) {
expect(result.response.error?.message).toBe('Tool Failed');
}
});
@@ -151,7 +152,7 @@ describe('ToolExecutor', () => {
);
const scheduledCall: ScheduledToolCall = {
status: 'scheduled',
status: CoreToolCallStatus.Scheduled,
request: {
callId: 'call-3',
name: 'slowTool',
@@ -174,7 +175,7 @@ describe('ToolExecutor', () => {
controller.abort();
const result = await promise;
expect(result.status).toBe('cancelled');
expect(result.status).toBe(CoreToolCallStatus.Cancelled);
});
it('should truncate large shell output', async () => {
@@ -193,7 +194,7 @@ describe('ToolExecutor', () => {
});
const scheduledCall: ScheduledToolCall = {
status: 'scheduled',
status: CoreToolCallStatus.Scheduled,
request: {
callId: 'call-trunc',
name: SHELL_TOOL_NAME,
@@ -228,8 +229,8 @@ describe('ToolExecutor', () => {
10, // threshold (maxChars)
);
expect(result.status).toBe('success');
if (result.status === 'success') {
expect(result.status).toBe(CoreToolCallStatus.Success);
if (result.status === CoreToolCallStatus.Success) {
const response = result.response.responseParts[0]?.functionResponse
?.response as Record<string, unknown>;
// The content should be the *truncated* version returned by the mock formatTruncatedToolOutput
@@ -262,7 +263,7 @@ describe('ToolExecutor', () => {
);
const scheduledCall: ScheduledToolCall = {
status: 'scheduled',
status: CoreToolCallStatus.Scheduled,
request: {
callId: 'call-pid',
name: SHELL_TOOL_NAME,
@@ -287,7 +288,7 @@ describe('ToolExecutor', () => {
// 4. Verify PID was reported
expect(onUpdateToolCall).toHaveBeenCalledWith(
expect.objectContaining({
status: 'executing',
status: CoreToolCallStatus.Executing,
pid: testPid,
}),
);
+5 -4
View File
@@ -33,6 +33,7 @@ import type {
SuccessfulToolCall,
CancelledToolCall,
} from './types.js';
import { CoreToolCallStatus } from './types.js';
export interface ToolExecutionContext {
call: ToolCall;
@@ -81,7 +82,7 @@ export class ToolExecutor {
const setPidCallback = (pid: number) => {
const executingCall: ExecutingToolCall = {
...call,
status: 'executing',
status: CoreToolCallStatus.Executing,
tool,
invocation,
pid,
@@ -170,7 +171,7 @@ export class ToolExecutor {
}
return {
status: 'cancelled',
status: CoreToolCallStatus.Cancelled,
request: call.request,
response: {
callId: call.request.callId,
@@ -256,7 +257,7 @@ export class ToolExecutor {
}
return {
status: 'success',
status: CoreToolCallStatus.Success,
request: call.request,
tool: call.tool,
response: successResponse,
@@ -281,7 +282,7 @@ export class ToolExecutor {
const startTime = 'startTime' in call ? call.startTime : undefined;
return {
status: 'error',
status: CoreToolCallStatus.Error,
request: call.request,
response,
tool: call.tool,
@@ -7,6 +7,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ToolModificationHandler } from './tool-modifier.js';
import type { WaitingToolCall, ToolCallRequestInfo } from './types.js';
import { CoreToolCallStatus } from './types.js';
import * as modifiableToolModule from '../tools/modifiable-tool.js';
import * as Diff from 'diff';
import { MockModifiableTool, MockTool } from '../test-utils/mock-tool.js';
@@ -37,7 +38,7 @@ function createMockWaitingToolCall(
overrides: Partial<WaitingToolCall> = {},
): WaitingToolCall {
return {
status: 'awaiting_approval',
status: CoreToolCallStatus.AwaitingApproval,
request: {
callId: 'test-call-id',
name: 'test-tool',
+20 -7
View File
@@ -18,6 +18,19 @@ import type { SerializableConfirmationDetails } from '../confirmation-bus/types.
export const ROOT_SCHEDULER_ID = 'root';
/**
* Internal core statuses for the tool call state machine.
*/
export enum CoreToolCallStatus {
Validating = 'validating',
Scheduled = 'scheduled',
Error = 'error',
Success = 'success',
Executing = 'executing',
Cancelled = 'cancelled',
AwaitingApproval = 'awaiting_approval',
}
export interface ToolCallRequestInfo {
callId: string;
name: string;
@@ -45,7 +58,7 @@ export interface ToolCallResponseInfo {
}
export type ValidatingToolCall = {
status: 'validating';
status: CoreToolCallStatus.Validating;
request: ToolCallRequestInfo;
tool: AnyDeclarativeTool;
invocation: AnyToolInvocation;
@@ -55,7 +68,7 @@ export type ValidatingToolCall = {
};
export type ScheduledToolCall = {
status: 'scheduled';
status: CoreToolCallStatus.Scheduled;
request: ToolCallRequestInfo;
tool: AnyDeclarativeTool;
invocation: AnyToolInvocation;
@@ -65,7 +78,7 @@ export type ScheduledToolCall = {
};
export type ErroredToolCall = {
status: 'error';
status: CoreToolCallStatus.Error;
request: ToolCallRequestInfo;
response: ToolCallResponseInfo;
tool?: AnyDeclarativeTool;
@@ -75,7 +88,7 @@ export type ErroredToolCall = {
};
export type SuccessfulToolCall = {
status: 'success';
status: CoreToolCallStatus.Success;
request: ToolCallRequestInfo;
tool: AnyDeclarativeTool;
response: ToolCallResponseInfo;
@@ -86,7 +99,7 @@ export type SuccessfulToolCall = {
};
export type ExecutingToolCall = {
status: 'executing';
status: CoreToolCallStatus.Executing;
request: ToolCallRequestInfo;
tool: AnyDeclarativeTool;
invocation: AnyToolInvocation;
@@ -98,7 +111,7 @@ export type ExecutingToolCall = {
};
export type CancelledToolCall = {
status: 'cancelled';
status: CoreToolCallStatus.Cancelled;
request: ToolCallRequestInfo;
response: ToolCallResponseInfo;
tool: AnyDeclarativeTool;
@@ -109,7 +122,7 @@ export type CancelledToolCall = {
};
export type WaitingToolCall = {
status: 'awaiting_approval';
status: CoreToolCallStatus.AwaitingApproval;
request: ToolCallRequestInfo;
tool: AnyDeclarativeTool;
invocation: AnyToolInvocation;