• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

waldiez / jupyter / 19225337525

10 Nov 2025 08:24AM UTC coverage: 88.942% (+0.2%) from 88.709%
19225337525

push

github

lazToum
update deps, bump to 0.6.2

359 of 444 branches covered (80.86%)

Branch coverage included in aggregate %.

1499 of 1645 relevant lines covered (91.12%)

9.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.93
/src/editor/executionManager.ts
1
/**
2
 * SPDX-License-Identifier: Apache-2.0
3
 * Copyright 2024 - 2025 Waldiez & contributors
4
 */
5
import { WALDIEZ_STRINGS } from "../constants";
10✔
6
import { WaldiezLogger } from "../logger";
10✔
7
import { afterInterrupt, getWaldiezActualPath } from "../rest";
10✔
8
import { WaldiezStandardRunner, WaldiezStepRunner } from "../runner";
10✔
9
import { WaldiezKernelManager } from "./kernelManager";
10✔
10
import type { IEditorState, IExecutionContext } from "./types";
11
import type { ISessionContext } from "@jupyterlab/apputils";
12
import type { IInputRequestMsg } from "@jupyterlab/services/lib/kernel/messages";
13
import { Signal } from "@lumino/signaling";
10✔
14

15
import type {
16
    WaldiezBreakpoint,
17
    WaldiezChatConfig,
18
    WaldiezChatHandlers,
19
    WaldiezChatMessage,
20
    WaldiezChatUserInput,
21
    WaldiezDebugInputResponse,
22
    WaldiezStepByStep,
23
    WaldiezStepHandlers,
24
} from "@waldiez/react";
25

26
/**
27
 * Manages execution state and runner coordination.
28
 */
29
export class WaldiezExecutionManager {
10✔
30
    private _standardRunner: WaldiezStandardRunner;
59✔
31
    private _stepRunner: WaldiezStepRunner;
59✔
32
    private _state: IEditorState;
59✔
33
    private readonly _logger: WaldiezLogger;
59✔
34
    private _signal: Signal<
59✔
35
        any,
36
        { chat: WaldiezChatConfig | undefined; stepByStep: WaldiezStepByStep | undefined }
37
    >;
38

39
    private _sessionContext: ISessionContext | null = null;
59✔
40
    private _kernelManager: WaldiezKernelManager | null = null;
59✔
41

42
    constructor(
43
        logger: WaldiezLogger,
44
        baseUrl: string,
45
        signal: Signal<
46
            any,
47
            { chat: WaldiezChatConfig | undefined; stepByStep: WaldiezStepByStep | undefined }
48
        >,
49
    ) {
50
        this._logger = logger;
59✔
51
        this._signal = signal;
59✔
52

53
        // Initialize runners
54
        this._standardRunner = new WaldiezStandardRunner({
59✔
55
            logger: this._logger,
56
            baseUrl,
57
            onStdin: this._onChatStdin.bind(this),
58
            onUpdate: this._onChatUpdate.bind(this),
59
            onEnd: this._onEnd.bind(this),
60
        });
61

62
        this._stepRunner = new WaldiezStepRunner({
59✔
63
            logger: this._logger,
64
            baseUrl,
65
            onStdin: this._onStepStdin.bind(this),
66
            onUpdate: this._onStepUpdate.bind(this),
67
            onEnd: this._onEnd.bind(this),
68
        });
69

70
        const chatHandlers: WaldiezChatHandlers = {
59✔
71
            onUserInput: this._createUserInputHandler(),
72
            onInterrupt: this._createInterruptHandler(),
73
            onClose: this._createCloseHandler(),
74
        };
75

76
        const stepByStepHandlers: WaldiezStepHandlers = {
59✔
77
            sendControl: this._createSendControlHandler(),
78
            close: this.closeStepByStepSession.bind(this),
79
            respond: this._createStepByStepRespondHandler(),
80
        };
81

82
        this._state = {
59✔
83
            chat: {
84
                show: false,
85
                active: false,
86
                messages: [] as WaldiezChatMessage[],
87
                timeline: undefined,
88
                userParticipants: [],
89
                handlers: chatHandlers,
90
            },
91
            stepByStep: {
92
                show: false,
93
                active: false,
94
                stepMode: true,
95
                autoContinue: false,
96
                breakpoints: [],
97
                eventHistory: [],
98
                currentEvent: undefined,
99
                timeline: undefined,
100
                participants: [],
101
                help: undefined,
102
                lastError: undefined,
103
                pendingControlInput: null,
104
                activeRequest: null,
105
                handlers: stepByStepHandlers,
106
            },
107
            stdinRequest: null,
108
        };
109
    }
110

111
    _resetStepByStepState() {
112
        this._state.stepByStep = {
1✔
113
            ...this._state.stepByStep,
114
            active: false,
115
            stepMode: true,
116
            autoContinue: false,
117
            breakpoints: [],
118
            eventHistory: [],
119
            currentEvent: undefined,
120
            timeline: undefined,
121
            participants: [],
122
            help: undefined,
123
            lastError: undefined,
124
            pendingControlInput: null,
125
            activeRequest: null,
126
        };
127
    }
128

129
    /**
130
     * Set the dependencies that handlers need access to.
131
     * This should be called after the manager is created.
132
     */
133
    setDependencies(sessionContext: ISessionContext, kernelManager: WaldiezKernelManager): void {
134
        this._sessionContext = sessionContext;
23✔
135
        this._kernelManager = kernelManager;
23✔
136
    }
137

138
    // Handler factory methods that capture dependencies
139
    private _createUserInputHandler() {
140
        return (userInput: WaldiezChatUserInput) => {
61✔
141
            if (!this._sessionContext) {
2!
142
                console.error("SessionContext not available for user input");
2✔
143
                return;
2✔
144
            }
145
            /* istanbul ignore next */
146
            this.handleUserInput(userInput, this._sessionContext);
147
        };
148
    }
149

150
    private _createInterruptHandler() {
151
        return () => {
61✔
152
            if (!this._kernelManager) {
2!
153
                console.error("KernelManager not available for interrupt");
2✔
154
                return;
2✔
155
            }
156
            /* istanbul ignore next */
157
            this.handleInterrupt(this._kernelManager);
158
        };
159
    }
160

161
    private _createCloseHandler() {
162
        return () => {
59✔
163
            /* istanbul ignore next */
164
            if (!this._kernelManager) {
165
                console.error("KernelManager not available for close");
166
                return;
167
            }
168
            /* istanbul ignore next */
169
            this.handleClose();
170
        };
171
    }
172

173
    private _createSendControlHandler() {
174
        return (input: Pick<WaldiezDebugInputResponse, "data" | "request_id">) => {
61✔
175
            /* istanbul ignore next */
176
            if (!this._sessionContext) {
177
                console.error("SessionContext not available for send control");
178
                return;
179
            }
180
            /* istanbul ignore next */
181
            this.sendControl(input, this._sessionContext);
182
        };
183
    }
184

185
    private _createStepByStepRespondHandler() {
186
        return (response: WaldiezChatUserInput) => {
61✔
187
            if (!this._sessionContext) {
2!
188
                console.error("SessionContext not available for step response");
2✔
189
                return;
2✔
190
            }
191
            this.stepByStepRespond(response, this._sessionContext);
×
192
        };
193
    }
194

195
    // Standard execution methods
196
    async executeStandard(context: IExecutionContext): Promise<void> {
197
        if (!context.kernel) {
3✔
198
            throw new Error(WALDIEZ_STRINGS.NO_KERNEL);
1✔
199
        }
200

201
        this._signal.emit({ chat: undefined, stepByStep: undefined });
2✔
202

203
        try {
2✔
204
            const actualPath = await getWaldiezActualPath(context.filePath);
2✔
205
            this._standardRunner.run(context.kernel, actualPath);
1✔
206
        } catch (err) {
207
            this._logger.log({
1✔
208
                data: String(err),
209
                level: "error",
210
                type: "text",
211
            });
212
            throw err;
1✔
213
        }
214
    }
215

216
    // Step-by-step execution methods
217
    async executeStepByStep(
218
        context: IExecutionContext,
219
        breakpoints?: (string | WaldiezBreakpoint)[],
220
        checkpoint?: string | null,
221
    ): Promise<void> {
222
        if (!context.kernel) {
2✔
223
            throw new Error(WALDIEZ_STRINGS.NO_KERNEL);
1✔
224
        }
225

226
        this._signal.emit({ chat: undefined, stepByStep: undefined });
1✔
227

228
        try {
1✔
229
            const actualPath = await getWaldiezActualPath(context.filePath);
1✔
230
            this._stepRunner.start(context.kernel, actualPath, breakpoints, checkpoint);
1✔
231
        } catch (err) {
232
            /* istanbul ignore next */
233
            this._logger.log({
234
                data: (err as Error).message || String(err),
235
                level: "error",
236
                type: "text",
237
            });
238
            throw err;
×
239
        }
240
    }
241

242
    // Input handling methods
243
    handleUserInput(userInput: WaldiezChatUserInput, sessionContext: ISessionContext): void {
244
        if (this._state.stdinRequest) {
2✔
245
            this._logger.log({
1✔
246
                data: JSON.stringify(userInput),
247
                level: "info",
248
                type: "text",
249
            });
250
            sessionContext.session?.kernel?.sendInputReply(
1✔
251
                { value: JSON.stringify(userInput), status: "ok" },
252
                this._state.stdinRequest.parent_header as any,
253
            );
254
            this._state.stdinRequest = null;
1✔
255
        }
256
    }
257

258
    handleInterrupt(kernelManager: WaldiezKernelManager): void {
259
        this._state.stdinRequest = null;
1✔
260
        this._standardRunner.reset();
1✔
261
        this._standardRunner.setTimelineData(undefined);
1✔
262
        this._signal.emit({
1✔
263
            chat: {
264
                show: false,
265
                active: false,
266
                messages: this._standardRunner.getPreviousMessages(),
267
                timeline: undefined,
268
                userParticipants: this._standardRunner.getUserParticipants(),
269
                activeRequest: undefined,
270
            },
271
            stepByStep: undefined,
272
        });
273
        try {
1✔
274
            // noinspection JSIgnoredPromiseFromCall
275
            kernelManager.restart();
1✔
276
            afterInterrupt();
1✔
277
        } catch {
278
            //
279
        }
280
    }
281

282
    handleClose(): void {
283
        this._state.stdinRequest = null;
1✔
284
        this._standardRunner.reset(true);
1✔
285
        this._signal.emit({
1✔
286
            chat: undefined,
287
            stepByStep: undefined,
288
        });
289
        try {
1✔
290
            if (this._kernelManager) {
1!
291
                // noinspection JSIgnoredPromiseFromCall
292
                this._kernelManager.restart();
×
293
                afterInterrupt();
×
294
            }
295
        } catch {
296
            //
297
        }
298
    }
299

300
    sendControl(
301
        input: Pick<WaldiezDebugInputResponse, "data" | "request_id">,
302
        sessionContext: ISessionContext,
303
    ): void {
304
        if (this._state.stdinRequest) {
2✔
305
            input.request_id = this._stepRunner.requestId || input.request_id || "<unknown>";
1!
306
            sessionContext.session?.kernel?.sendInputReply(
1✔
307
                { value: JSON.stringify({ ...input, type: "debug_input_response" }), status: "ok" },
308
                this._state.stdinRequest.parent_header as any,
309
            );
310
        } else {
311
            console.error("StepByStep response received without stdin request");
1✔
312
        }
313
        this._state.stdinRequest = null;
2✔
314
        const isQuit = input.data === "q";
2✔
315
        if (isQuit) {
2!
316
            this.closeStepByStepSession();
×
317
        } else {
318
            this._stepRunner.responded();
2✔
319
        }
320
    }
321

322
    stepByStepRespond(response: WaldiezChatUserInput, sessionContext: ISessionContext): void {
323
        if (this._state.stdinRequest) {
1!
324
            sessionContext.session?.kernel?.sendInputReply(
1✔
325
                { value: JSON.stringify(response), status: "ok" },
326
                this._state.stdinRequest.parent_header as any,
327
            );
328
        }
329
        this._state.stdinRequest = null;
1✔
330
        this._stepRunner.responded();
1✔
331
    }
332

333
    closeStepByStepSession(): void {
334
        this._resetStepByStepState();
1✔
335
        this._signal.emit({ chat: undefined, stepByStep: undefined });
1✔
336
        this._stepRunner.reset();
1✔
337

338
        if (this._kernelManager) {
1!
339
            // noinspection JSIgnoredPromiseFromCall
340
            this._kernelManager.restart();
×
341
        }
342
        try {
1✔
343
            afterInterrupt();
1✔
344
        } catch {
345
            //
346
        }
347
    }
348

349
    // Event handlers for runners
350
    private _onChatStdin(msg: IInputRequestMsg): void {
351
        let prompt = msg.content.prompt;
2✔
352
        if (prompt === ">" || prompt === "> ") {
2✔
353
            prompt = WALDIEZ_STRINGS.ON_EMPTY_PROMPT;
1✔
354
        }
355
        this._logger.log({
2✔
356
            data: prompt,
357
            level: "warning",
358
            type: "text",
359
        });
360
        this._state.stdinRequest = msg;
2✔
361
        this._askForInput();
2✔
362
    }
363

364
    private _onStepStdin(msg: IInputRequestMsg): void {
365
        let prompt = msg.content.prompt;
1✔
366
        if (prompt === ">" || prompt === "> ") {
1!
367
            prompt = WALDIEZ_STRINGS.ON_EMPTY_PROMPT;
×
368
        }
369
        this._logger.log({
1✔
370
            data: prompt,
371
            level: "warning",
372
            type: "text",
373
        });
374
        this._state.stdinRequest = msg;
1✔
375
    }
376

377
    private _askForInput(): void {
378
        const messages = this._standardRunner.getPreviousMessages();
2✔
379
        let request_id: string;
380
        if (typeof this._state.stdinRequest?.metadata.request_id === "string") {
2✔
381
            request_id = this._state.stdinRequest.metadata.request_id;
1✔
382
        } else {
383
            request_id = this._standardRunner.requestId ?? this._getRequestIdFromPreviousMessages(messages);
1!
384
        }
385

386
        const chat: WaldiezChatConfig = {
2✔
387
            ...this._state.chat,
388
            show: true,
389
            active: true,
390
            messages,
391
            userParticipants: this._standardRunner.getUserParticipants(),
392
            activeRequest: {
393
                request_id,
394
                prompt: this._state.stdinRequest?.content.prompt ?? "> ",
2!
395
                password: this._state.stdinRequest?.content.password ?? false,
2!
396
            },
397
        };
398
        this._signal.emit({ chat, stepByStep: undefined });
2✔
399
    }
400

401
    private _onEnd(): void {
402
        this._signal.emit({
1✔
403
            chat: {
404
                ...this._state.chat,
405
                messages: this._standardRunner.getPreviousMessages(),
406
                timeline: this._standardRunner.getTimelineData(),
407
                userParticipants: this._standardRunner.getUserParticipants(),
408
                activeRequest: undefined,
409
                handlers: undefined,
410
            },
411
            stepByStep: undefined,
412
        });
413
    }
414

415
    private _onChatUpdate(updateData: Partial<WaldiezChatConfig>): void {
416
        const { handlers, ...restUpdateData } = updateData;
1✔
417

418
        const chatConfig = {
1✔
419
            ...this._state.chat,
420
            ...restUpdateData,
421
            handlers: {
422
                ...this._state.chat.handlers,
423
                ...(handlers || {}),
2✔
424
            },
425
        };
426
        this._state.chat = chatConfig;
1✔
427
        this._signal.emit({
1✔
428
            chat: chatConfig,
429
            stepByStep: undefined,
430
        });
431
    }
432

433
    private _onStepUpdate(updateData: Partial<WaldiezStepByStep>): void {
434
        const { show, active, ...restStepUpdateData } = updateData;
3✔
435
        this._state.stepByStep = {
3✔
436
            ...this._state.stepByStep,
437
            ...restStepUpdateData,
438
            show: typeof show === "boolean" ? show : this._state.stepByStep.show,
3!
439
            active: typeof active === "boolean" ? active : this._state.stepByStep.active,
3✔
440
        };
441
        this._signal.emit({
3✔
442
            chat: undefined,
443
            stepByStep: this._state.stepByStep,
444
        });
445
    }
446

447
    private _getRequestIdFromPreviousMessages(previousMessages: WaldiezChatMessage[]): string {
448
        const inputRequestMessage = previousMessages.find(msg => msg.type === "input_request");
11✔
449
        if (inputRequestMessage) {
8✔
450
            return inputRequestMessage.request_id ?? "<unknown>";
6✔
451
        }
452
        return "<unknown>";
2✔
453
    }
454

455
    dispose(): void {
456
        this._standardRunner.reset(true);
80✔
457
        this._stepRunner.reset();
80✔
458
    }
459
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc