@google/gemini-cli-a2a-server 0.52.0 → 0.53.0

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.
@@ -3,17 +3,23 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { GeminiEventType, SimpleExtensionLoader, } from '@google/gemini-cli-core';
6
+ import { GeminiEventType, SimpleExtensionLoader, checkPathTrust, isHeadlessMode, resolveToRealPath, } from '@google/gemini-cli-core';
7
7
  import { v4 as uuidv4 } from 'uuid';
8
8
  import { logger } from '../utils/logger.js';
9
9
  import { CoderAgentEvent, getPersistedState, setPersistedState, getContextIdFromMetadata, getAgentSettingsFromMetadata, } from '../types.js';
10
- import { loadConfig, loadEnvironment, setIsTrusted, setTargetDir, } from '../config/config.js';
10
+ import { loadConfig, setTargetDir, envStorage, cwdSymbol, loadEnvironment, } from '../config/config.js';
11
11
  import { loadSettings } from '../config/settings.js';
12
12
  import { loadExtensions } from '../config/extension.js';
13
13
  import { Task } from './task.js';
14
14
  import { requestStorage } from '../http/requestStorage.js';
15
15
  import { pushTaskStateFailed } from '../utils/executor_utils.js';
16
- import { validateWorkspacePath } from '../utils/path_utils.js';
16
+ function validateWorkspacePath(workspacePath) {
17
+ const trimmed = workspacePath?.trim();
18
+ if (!trimmed) {
19
+ return process.env['CODER_AGENT_WORKSPACE_PATH'] ?? process.cwd();
20
+ }
21
+ return resolveToRealPath(trimmed);
22
+ }
17
23
  /**
18
24
  * Provides a wrapper for Task. Passes data from Task to SDKTask.
19
25
  * The idea is to use this class inside CoderAgentExecutor to replace Task.
@@ -66,13 +72,33 @@ export class CoderAgentExecutor {
66
72
  constructor(taskStore) {
67
73
  this.taskStore = taskStore;
68
74
  }
69
- async getConfig(agentSettings, taskId) {
70
- const workspaceRoot = setTargetDir(agentSettings);
71
- loadEnvironment(); // Will override any global env with workspace envs
72
- const isTrusted = setIsTrusted(agentSettings);
75
+ async getConfigWithEnv(agentSettings, taskId, isTrusted, workspaceRoot) {
73
76
  const settings = loadSettings(workspaceRoot, isTrusted);
74
- const extensions = loadExtensions(workspaceRoot);
75
- return loadConfig(settings, new SimpleExtensionLoader(extensions), taskId, isTrusted);
77
+ const extensions = loadExtensions(workspaceRoot, isTrusted);
78
+ return loadConfig(settings, new SimpleExtensionLoader(extensions), taskId, isTrusted, workspaceRoot);
79
+ }
80
+ async runInIsolatedEnv(agentSettings, fn) {
81
+ const workspaceRoot = await setTargetDir(agentSettings);
82
+ const initialSettings = loadSettings(workspaceRoot, false);
83
+ const { isTrusted } = checkPathTrust({
84
+ path: workspaceRoot,
85
+ isFolderTrustEnabled: initialSettings.folderTrust ?? true,
86
+ isHeadless: isHeadlessMode(),
87
+ });
88
+ const envVars = { ...process.env };
89
+ envVars[cwdSymbol] = workspaceRoot;
90
+ return envStorage.run(envVars, async () => {
91
+ const loadedEnv = await loadEnvironment(isTrusted ?? false, workspaceRoot);
92
+ Object.assign(envVars, loadedEnv);
93
+ return fn(isTrusted ?? false, workspaceRoot);
94
+ });
95
+ }
96
+ cleanupAndEvictTask(taskId) {
97
+ const wrapper = this.tasks.get(taskId);
98
+ if (wrapper) {
99
+ wrapper.task.dispose();
100
+ this.tasks.delete(taskId);
101
+ }
76
102
  }
77
103
  /**
78
104
  * Reconstructs TaskWrapper from SDKTask.
@@ -87,7 +113,7 @@ export class CoderAgentExecutor {
87
113
  try {
88
114
  agentSettings = {
89
115
  ...(persistedState._agentSettings ?? {}),
90
- workspacePath: await validateWorkspacePath(persistedState._agentSettings?.workspacePath),
116
+ workspacePath: validateWorkspacePath(persistedState._agentSettings?.workspacePath),
91
117
  isTrusted: false,
92
118
  };
93
119
  }
@@ -98,28 +124,32 @@ export class CoderAgentExecutor {
98
124
  }
99
125
  throw error; // Re-throw to be caught by caller
100
126
  }
101
- const config = await this.getConfig(agentSettings, sdkTask.id);
102
- const contextId = getContextIdFromMetadata(metadata) || sdkTask.contextId;
103
- const runtimeTask = await Task.create(sdkTask.id, contextId, config, eventBus, agentSettings.autoExecute);
104
- runtimeTask.taskState = persistedState._taskState;
105
- await runtimeTask.geminiClient.initialize();
106
- const wrapper = new TaskWrapper(runtimeTask, agentSettings);
107
- this.tasks.set(sdkTask.id, wrapper);
108
- logger.info(`Task ${sdkTask.id} reconstructed from store.`);
109
- return wrapper;
127
+ return this.runInIsolatedEnv(agentSettings, async (isTrusted, workspaceRoot) => {
128
+ const config = await this.getConfigWithEnv(agentSettings, sdkTask.id, isTrusted, workspaceRoot);
129
+ const contextId = getContextIdFromMetadata(metadata) || sdkTask.contextId;
130
+ const runtimeTask = await Task.create(sdkTask.id, contextId, config, eventBus, agentSettings.autoExecute);
131
+ runtimeTask.taskState = persistedState._taskState;
132
+ await runtimeTask.geminiClient.initialize();
133
+ const wrapper = new TaskWrapper(runtimeTask, agentSettings);
134
+ this.tasks.set(sdkTask.id, wrapper);
135
+ logger.info(`Task ${sdkTask.id} reconstructed from store.`);
136
+ return wrapper;
137
+ });
110
138
  }
111
139
  async createTask(taskId, contextId, agentSettingsInput, eventBus) {
112
140
  const agentSettings = agentSettingsInput || {
113
141
  kind: CoderAgentEvent.StateAgentSettingsEvent,
114
142
  workspacePath: process.cwd(),
115
143
  };
116
- const config = await this.getConfig(agentSettings, taskId);
117
- const runtimeTask = await Task.create(taskId, contextId, config, eventBus, agentSettings.autoExecute);
118
- await runtimeTask.geminiClient.initialize();
119
- const wrapper = new TaskWrapper(runtimeTask, agentSettings);
120
- this.tasks.set(taskId, wrapper);
121
- logger.info(`New task ${taskId} created.`);
122
- return wrapper;
144
+ return this.runInIsolatedEnv(agentSettings, async (isTrusted, workspaceRoot) => {
145
+ const config = await this.getConfigWithEnv(agentSettings, taskId, isTrusted, workspaceRoot);
146
+ const runtimeTask = await Task.create(taskId, contextId, config, eventBus, agentSettings.autoExecute);
147
+ await runtimeTask.geminiClient.initialize();
148
+ const wrapper = new TaskWrapper(runtimeTask, agentSettings);
149
+ this.tasks.set(taskId, wrapper);
150
+ logger.info(`New task ${taskId} created.`);
151
+ return wrapper;
152
+ });
123
153
  }
124
154
  getTask(taskId) {
125
155
  return this.tasks.get(taskId);
@@ -127,14 +157,6 @@ export class CoderAgentExecutor {
127
157
  getAllTasks() {
128
158
  return Array.from(this.tasks.values());
129
159
  }
130
- cleanupAndEvictTask(taskId) {
131
- const wrapper = this.tasks.get(taskId);
132
- if (wrapper) {
133
- logger.info(`[CoderAgentExecutor] Task ${taskId} reached terminal state ${wrapper.task.taskState}. Evicting and disposing.`);
134
- wrapper.task.dispose();
135
- this.tasks.delete(taskId);
136
- }
137
- }
138
160
  cancelTask = async (taskId, eventBus) => {
139
161
  logger.info(`[CoderAgentExecutor] Received cancel request for task ${taskId}`);
140
162
  const abortControllers = this.activeAbortControllers.get(taskId);
@@ -213,7 +235,7 @@ export class CoderAgentExecutor {
213
235
  return;
214
236
  }
215
237
  try {
216
- logger.info(`[CoderAgentExecutor] Initiating cancellation for idle task ${taskId}.`);
238
+ logger.info(`[CoderAgentExecutor] Initiating cancellation for task ${taskId}.`);
217
239
  task.cancelPendingTools('Task canceled by user request.');
218
240
  const stateChange = {
219
241
  kind: CoderAgentEvent.StateChangeEvent,
@@ -259,293 +281,324 @@ export class CoderAgentExecutor {
259
281
  sdkTask?.contextId ||
260
282
  getContextIdFromMetadata(sdkTask?.metadata) ||
261
283
  uuidv4();
262
- logger.info(`[CoderAgentExecutor] Executing for taskId: ${taskId}, contextId: ${contextId}`);
263
- logger.info(`[CoderAgentExecutor] userMessage: ${JSON.stringify(userMessage)}`);
264
- eventBus.on('event', (event) => logger.info('[EventBus event]: ', event));
265
- const store = requestStorage.getStore();
266
- if (!store) {
267
- logger.error('[CoderAgentExecutor] Could not get request from async local storage. Cancellation on socket close will not be handled for this request.');
268
- }
269
- const abortController = new AbortController();
270
- const abortSignal = abortController.signal;
271
- if (!this.activeAbortControllers.has(taskId)) {
272
- this.activeAbortControllers.set(taskId, new Set());
273
- }
274
- this.activeAbortControllers.get(taskId).add(abortController);
275
- let proceedToMainLoop = false;
276
- let wrapper;
277
- let isPrimaryExecution = false;
284
+ let agentSettings;
278
285
  try {
279
- if (store) {
280
- const socket = store.req.socket;
281
- const onSocketEnd = () => {
282
- logger.info(`[CoderAgentExecutor] Socket ended for message ${userMessage.messageId} (task ${taskId}). Aborting execution loop.`);
283
- if (!abortController.signal.aborted) {
284
- abortController.abort();
285
- }
286
- socket.removeListener('end', onSocketEnd);
286
+ const existingWrapper = this.tasks.get(taskId);
287
+ if (existingWrapper) {
288
+ agentSettings = existingWrapper.agentSettings;
289
+ }
290
+ else if (sdkTask) {
291
+ const persistedState = getPersistedState(sdkTask.metadata || {});
292
+ agentSettings = {
293
+ kind: CoderAgentEvent.StateAgentSettingsEvent,
294
+ ...(persistedState?._agentSettings ?? {}),
295
+ workspacePath: validateWorkspacePath(persistedState?._agentSettings?.workspacePath),
296
+ isTrusted: false,
287
297
  };
288
- socket.on('end', onSocketEnd);
289
- socket.once('close', () => socket.removeListener('end', onSocketEnd));
290
- abortSignal.addEventListener('abort', () => socket.removeListener('end', onSocketEnd));
291
- logger.info(`[CoderAgentExecutor] Socket close handler set up for task ${taskId}.`);
292
298
  }
293
- // Check if the task is currently initializing
294
- if (this.initializingTasks.has(taskId)) {
295
- logger.info(`[CoderAgentExecutor] Task ${taskId} is currently initializing. Waiting for initialization to complete.`);
296
- const initPromise = this.initializationPromises.get(taskId);
297
- if (initPromise) {
298
- try {
299
- wrapper = await initPromise;
300
- }
301
- catch {
302
- logger.error(`[CoderAgentExecutor] Failed to wait for task ${taskId} initialization.`);
303
- return;
304
- }
305
- }
299
+ else {
300
+ const rawAgentSettings = getAgentSettingsFromMetadata(userMessage.metadata);
301
+ agentSettings = {
302
+ kind: CoderAgentEvent.StateAgentSettingsEvent,
303
+ ...(rawAgentSettings || {}),
304
+ workspacePath: validateWorkspacePath(rawAgentSettings?.workspacePath),
305
+ isTrusted: false,
306
+ };
307
+ }
308
+ }
309
+ catch (error) {
310
+ logger.error(`[CoderAgentExecutor] Invalid workspace path during execution setup for task ${taskId}:`, error);
311
+ if (eventBus) {
312
+ void pushTaskStateFailed(error, eventBus, taskId, contextId);
306
313
  }
307
- if (!wrapper) {
308
- this.initializingTasks.add(taskId);
309
- const initPromise = (async () => {
310
- let initializedWrapper;
311
- initializedWrapper = this.tasks.get(taskId);
312
- if (initializedWrapper) {
313
- initializedWrapper.task.eventBus = eventBus;
314
- logger.info(`[CoderAgentExecutor] Task ${taskId} found in memory cache.`);
314
+ throw error;
315
+ }
316
+ return this.runInIsolatedEnv(agentSettings, async () => {
317
+ try {
318
+ logger.info(`[CoderAgentExecutor] Executing for taskId: ${taskId}, contextId: ${contextId}`);
319
+ logger.info(`[CoderAgentExecutor] userMessage: ${JSON.stringify(userMessage)}`);
320
+ eventBus.on('event', (event) => logger.info('[EventBus event]: ', event));
321
+ const store = requestStorage.getStore();
322
+ if (!store) {
323
+ logger.error('[CoderAgentExecutor] Could not get request from async local storage. Cancellation on socket close will not be handled for this request.');
324
+ }
325
+ const abortController = new AbortController();
326
+ const abortSignal = abortController.signal;
327
+ if (!this.activeAbortControllers.has(taskId)) {
328
+ this.activeAbortControllers.set(taskId, new Set());
329
+ }
330
+ this.activeAbortControllers.get(taskId).add(abortController);
331
+ let proceedToMainLoop = false;
332
+ let wrapper;
333
+ let isPrimaryExecution = false;
334
+ try {
335
+ if (store) {
336
+ const socket = store.req.socket;
337
+ const onSocketEnd = () => {
338
+ logger.info(`[CoderAgentExecutor] Socket ended for message ${userMessage.messageId} (task ${taskId}). Aborting execution loop.`);
339
+ if (!abortController.signal.aborted) {
340
+ abortController.abort();
341
+ }
342
+ socket.removeListener('end', onSocketEnd);
343
+ };
344
+ socket.on('end', onSocketEnd);
345
+ socket.once('close', () => socket.removeListener('end', onSocketEnd));
346
+ abortSignal.addEventListener('abort', () => socket.removeListener('end', onSocketEnd));
347
+ logger.info(`[CoderAgentExecutor] Socket close handler set up for task ${taskId}.`);
348
+ }
349
+ // Check if the task is currently initializing
350
+ if (this.initializingTasks.has(taskId)) {
351
+ logger.info(`[CoderAgentExecutor] Task ${taskId} is currently initializing. Waiting for initialization to complete.`);
352
+ const initPromise = this.initializationPromises.get(taskId);
353
+ if (initPromise) {
354
+ try {
355
+ wrapper = await initPromise;
356
+ }
357
+ catch {
358
+ logger.error(`[CoderAgentExecutor] Failed to wait for task ${taskId} initialization.`);
359
+ return;
360
+ }
361
+ }
315
362
  }
316
- else if (sdkTask) {
317
- logger.info(`[CoderAgentExecutor] Task ${taskId} found in TaskStore. Reconstructing...`);
363
+ if (!wrapper) {
364
+ this.initializingTasks.add(taskId);
365
+ const initPromise = (async () => {
366
+ let initializedWrapper;
367
+ initializedWrapper = this.tasks.get(taskId);
368
+ if (initializedWrapper) {
369
+ initializedWrapper.task.eventBus = eventBus;
370
+ logger.info(`[CoderAgentExecutor] Task ${taskId} found in memory cache.`);
371
+ }
372
+ else if (sdkTask) {
373
+ logger.info(`[CoderAgentExecutor] Task ${taskId} found in TaskStore. Reconstructing...`);
374
+ try {
375
+ initializedWrapper = await this.reconstruct(sdkTask, eventBus);
376
+ }
377
+ catch (e) {
378
+ logger.error(`[CoderAgentExecutor] Aborting execution due to failed task reconstruction for task ${taskId}:`, e);
379
+ throw e;
380
+ }
381
+ }
382
+ else {
383
+ initializedWrapper = await this.createTask(taskId, contextId, agentSettings, eventBus);
384
+ const newTaskSDK = initializedWrapper.toSDKTask();
385
+ eventBus.publish({
386
+ ...newTaskSDK,
387
+ kind: 'task',
388
+ status: {
389
+ state: 'submitted',
390
+ timestamp: new Date().toISOString(),
391
+ },
392
+ history: [userMessage],
393
+ });
394
+ try {
395
+ await this.taskStore?.save(newTaskSDK);
396
+ logger.info(`[CoderAgentExecutor] New task ${taskId} saved to store.`);
397
+ }
398
+ catch (saveError) {
399
+ logger.error(`[CoderAgentExecutor] Failed to save new task ${taskId} to store:`, saveError);
400
+ }
401
+ }
402
+ return initializedWrapper;
403
+ })();
404
+ this.initializationPromises.set(taskId, initPromise);
318
405
  try {
319
- initializedWrapper = await this.reconstruct(sdkTask, eventBus);
406
+ wrapper = await initPromise;
407
+ }
408
+ catch {
409
+ // Error is already handled/logged inside the promise
410
+ return;
320
411
  }
321
- catch (e) {
322
- logger.error(`[CoderAgentExecutor] Aborting execution due to failed task reconstruction for task ${taskId}:`, e);
323
- throw e;
412
+ finally {
413
+ this.initializingTasks.delete(taskId);
414
+ this.initializationPromises.delete(taskId);
324
415
  }
325
416
  }
326
- else {
327
- let agentSettings;
417
+ if (!wrapper) {
418
+ logger.error(`[CoderAgentExecutor] Task ${taskId} is unexpectedly undefined after load/create.`);
419
+ return;
420
+ }
421
+ const currentTask = wrapper.task;
422
+ if (['canceled', 'failed', 'completed'].includes(currentTask.taskState)) {
423
+ logger.warn(`[CoderAgentExecutor] Attempted to execute task ${taskId} which is already in state ${currentTask.taskState}. Ignoring.`);
424
+ return;
425
+ }
426
+ if (abortSignal.aborted) {
427
+ logger.warn(`[CoderAgentExecutor] Task ${taskId} was aborted during initialization.`);
428
+ const isExplicitCancel = this.explicitlyCanceledTasks.has(taskId);
429
+ const finalState = isExplicitCancel ? 'canceled' : 'input-required';
430
+ const message = isExplicitCancel
431
+ ? 'Task canceled by user request.'
432
+ : 'Execution aborted by client.';
433
+ currentTask.setTaskStateAndPublishUpdate(finalState, { kind: CoderAgentEvent.StateChangeEvent }, message, undefined, true);
328
434
  try {
329
- const rawAgentSettings = getAgentSettingsFromMetadata(userMessage.metadata);
330
- const validatedWorkspacePath = await validateWorkspacePath(rawAgentSettings?.workspacePath);
331
- agentSettings = {
332
- kind: CoderAgentEvent.StateAgentSettingsEvent,
333
- ...(rawAgentSettings || {}),
334
- workspacePath: validatedWorkspacePath,
335
- isTrusted: false,
336
- };
337
- initializedWrapper = await this.createTask(taskId, contextId, agentSettings, eventBus);
435
+ await this.taskStore?.save(wrapper.toSDKTask());
338
436
  }
339
- catch (error) {
340
- logger.error(`[CoderAgentExecutor] Error creating task ${taskId}:`, error);
341
- void pushTaskStateFailed(error, eventBus, taskId, contextId);
342
- throw error;
437
+ catch (saveError) {
438
+ logger.error(`[CoderAgentExecutor] Failed to save task ${taskId} state:`, saveError);
343
439
  }
344
- const newTaskSDK = initializedWrapper.toSDKTask();
345
- eventBus.publish({
346
- ...newTaskSDK,
347
- kind: 'task',
348
- status: {
349
- state: 'submitted',
350
- timestamp: new Date().toISOString(),
351
- },
352
- history: [userMessage],
353
- });
440
+ if (isExplicitCancel) {
441
+ this.cleanupAndEvictTask(taskId);
442
+ }
443
+ return;
444
+ }
445
+ if (this.executingTasks.has(taskId)) {
446
+ logger.info(`[CoderAgentExecutor] Task ${taskId} has a pending execution. Processing message and yielding.`);
447
+ currentTask.eventBus = eventBus;
354
448
  try {
355
- await this.taskStore?.save(newTaskSDK);
356
- logger.info(`[CoderAgentExecutor] New task ${taskId} saved to store.`);
449
+ for await (const _ of currentTask.acceptUserMessage(requestContext, abortController.signal)) {
450
+ logger.info(`[CoderAgentExecutor] Processing user message ${userMessage.messageId} in secondary execution loop for task ${taskId}.`);
451
+ }
357
452
  }
358
- catch (saveError) {
359
- logger.error(`[CoderAgentExecutor] Failed to save new task ${taskId} to store:`, saveError);
453
+ catch (error) {
454
+ if (!abortController.signal.aborted) {
455
+ throw error;
456
+ }
457
+ logger.info(`[CoderAgentExecutor] Secondary execution loop for task ${taskId} was aborted.`);
360
458
  }
459
+ return;
361
460
  }
362
- return initializedWrapper;
363
- })();
364
- this.initializationPromises.set(taskId, initPromise);
365
- try {
366
- wrapper = await initPromise;
367
- }
368
- catch {
369
- // Error is already handled/logged inside the promise
370
- return;
461
+ isPrimaryExecution = true;
462
+ proceedToMainLoop = true;
371
463
  }
372
464
  finally {
373
- this.initializingTasks.delete(taskId);
374
- this.initializationPromises.delete(taskId);
375
- }
376
- }
377
- if (!wrapper) {
378
- logger.error(`[CoderAgentExecutor] Task ${taskId} is unexpectedly undefined after load/create.`);
379
- return;
380
- }
381
- const currentTask = wrapper.task;
382
- if (['canceled', 'failed', 'completed'].includes(currentTask.taskState)) {
383
- logger.warn(`[CoderAgentExecutor] Attempted to execute task ${taskId} which is already in state ${currentTask.taskState}. Ignoring.`);
384
- return;
385
- }
386
- if (abortSignal.aborted) {
387
- logger.warn(`[CoderAgentExecutor] Task ${taskId} was aborted during initialization.`);
388
- const isExplicitCancel = this.explicitlyCanceledTasks.has(taskId);
389
- const finalState = isExplicitCancel ? 'canceled' : 'input-required';
390
- const message = isExplicitCancel
391
- ? 'Task canceled by user request.'
392
- : 'Execution aborted by client.';
393
- currentTask.setTaskStateAndPublishUpdate(finalState, { kind: CoderAgentEvent.StateChangeEvent }, message, undefined, true);
394
- try {
395
- await this.taskStore?.save(wrapper.toSDKTask());
396
- }
397
- catch (saveError) {
398
- logger.error(`[CoderAgentExecutor] Failed to save task ${taskId} state:`, saveError);
399
- }
400
- if (isExplicitCancel) {
401
- this.cleanupAndEvictTask(taskId);
465
+ this.explicitlyCanceledTasks.delete(taskId);
466
+ if (!proceedToMainLoop) {
467
+ const controllers = this.activeAbortControllers.get(taskId);
468
+ if (controllers) {
469
+ controllers.delete(abortController);
470
+ if (controllers.size === 0) {
471
+ this.activeAbortControllers.delete(taskId);
472
+ }
473
+ }
474
+ }
402
475
  }
403
- return;
404
- }
405
- if (this.executingTasks.has(taskId)) {
406
- logger.info(`[CoderAgentExecutor] Task ${taskId} has a pending execution. Processing message and yielding.`);
407
- currentTask.eventBus = eventBus;
476
+ const currentTask = wrapper.task;
408
477
  try {
409
- for await (const _ of currentTask.acceptUserMessage(requestContext, abortController.signal)) {
410
- logger.info(`[CoderAgentExecutor] Processing user message ${userMessage.messageId} in secondary execution loop for task ${taskId}.`);
478
+ logger.info(`[CoderAgentExecutor] Starting main execution for message ${userMessage.messageId} for task ${taskId}.`);
479
+ this.executingTasks.add(taskId);
480
+ let agentTurnActive = true;
481
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: Processing user turn.`);
482
+ let agentEvents = currentTask.acceptUserMessage(requestContext, abortSignal);
483
+ while (agentTurnActive) {
484
+ if (abortSignal.aborted) {
485
+ logger.info(`[CoderAgentExecutor] Task ${taskId} aborted before turn. Exiting loop.`);
486
+ throw new Error('Execution aborted');
487
+ }
488
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: Processing agent turn (LLM stream).`);
489
+ const toolCallRequests = [];
490
+ for await (const event of agentEvents) {
491
+ if (abortSignal.aborted) {
492
+ logger.warn(`[CoderAgentExecutor] Task ${taskId}: Abort signal received during agent event processing.`);
493
+ throw new Error('Execution aborted');
494
+ }
495
+ if (event.type === GeminiEventType.ToolCallRequest) {
496
+ toolCallRequests.push(event.value);
497
+ continue;
498
+ }
499
+ await currentTask.acceptAgentMessage(event);
500
+ }
501
+ if (abortSignal.aborted)
502
+ throw new Error('Execution aborted');
503
+ if (toolCallRequests.length > 0) {
504
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: Found ${toolCallRequests.length} tool call requests. Scheduling as a batch.`);
505
+ await currentTask.scheduleToolCalls(toolCallRequests, abortSignal);
506
+ }
507
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: Waiting for pending tools if any.`);
508
+ await currentTask.waitForPendingTools();
509
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: All pending tools completed or none were pending.`);
510
+ if (abortSignal.aborted)
511
+ throw new Error('Execution aborted');
512
+ if (currentTask.hasPendingTools) {
513
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: There are still ${currentTask.pendingToolsCount} pending tools waiting for approval. Yielding to user.`);
514
+ agentTurnActive = false;
515
+ }
516
+ else {
517
+ const completedTools = currentTask.getAndClearCompletedTools();
518
+ if (completedTools.length > 0) {
519
+ if (completedTools.every((tool) => tool.status === 'cancelled')) {
520
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: All tool calls were cancelled. Updating history and ending agent turn.`);
521
+ currentTask.addToolResponsesToHistory(completedTools);
522
+ agentTurnActive = false;
523
+ const stateChange = {
524
+ kind: CoderAgentEvent.StateChangeEvent,
525
+ };
526
+ currentTask.setTaskStateAndPublishUpdate('input-required', stateChange, undefined, undefined, true);
527
+ }
528
+ else {
529
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: Found ${completedTools.length} completed tool calls. Sending results back to LLM.`);
530
+ agentEvents = currentTask.sendCompletedToolsToLlm(completedTools, abortSignal);
531
+ }
532
+ }
533
+ else {
534
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: No more tool calls to process. Ending agent turn.`);
535
+ agentTurnActive = false;
536
+ }
537
+ }
411
538
  }
539
+ logger.info(`[CoderAgentExecutor] Task ${taskId}: Agent turn finished, setting to input-required.`);
540
+ const stateChange = {
541
+ kind: CoderAgentEvent.StateChangeEvent,
542
+ };
543
+ currentTask.setTaskStateAndPublishUpdate('input-required', stateChange, undefined, undefined, true);
412
544
  }
413
545
  catch (error) {
414
- if (!abortController.signal.aborted) {
415
- throw error;
416
- }
417
- logger.info(`[CoderAgentExecutor] Secondary execution loop for task ${taskId} was aborted.`);
418
- }
419
- return;
420
- }
421
- isPrimaryExecution = true;
422
- proceedToMainLoop = true;
423
- }
424
- finally {
425
- this.explicitlyCanceledTasks.delete(taskId);
426
- if (!proceedToMainLoop) {
427
- const controllers = this.activeAbortControllers.get(taskId);
428
- if (controllers) {
429
- controllers.delete(abortController);
430
- if (controllers.size === 0) {
431
- this.activeAbortControllers.delete(taskId);
432
- }
433
- }
434
- }
435
- }
436
- const currentTask = wrapper.task;
437
- try {
438
- logger.info(`[CoderAgentExecutor] Starting main execution for message ${userMessage.messageId} for task ${taskId}.`);
439
- this.executingTasks.add(taskId);
440
- let agentTurnActive = true;
441
- logger.info(`[CoderAgentExecutor] Task ${taskId}: Processing user turn.`);
442
- let agentEvents = currentTask.acceptUserMessage(requestContext, abortSignal);
443
- while (agentTurnActive) {
444
- if (abortSignal.aborted) {
445
- logger.info(`[CoderAgentExecutor] Task ${taskId} aborted before turn. Exiting loop.`);
446
- throw new Error('Execution aborted');
447
- }
448
- logger.info(`[CoderAgentExecutor] Task ${taskId}: Processing agent turn (LLM stream).`);
449
- const toolCallRequests = [];
450
- for await (const event of agentEvents) {
451
546
  if (abortSignal.aborted) {
452
- logger.warn(`[CoderAgentExecutor] Task ${taskId}: Abort signal received during agent event processing.`);
453
- throw new Error('Execution aborted');
454
- }
455
- if (event.type === GeminiEventType.ToolCallRequest) {
456
- toolCallRequests.push(event.value);
457
- continue;
547
+ logger.warn(`[CoderAgentExecutor] Task ${taskId} execution aborted.`);
548
+ currentTask.cancelPendingTools('Execution aborted');
549
+ if (currentTask.taskState !== 'canceled' &&
550
+ currentTask.taskState !== 'failed') {
551
+ currentTask.setTaskStateAndPublishUpdate('input-required', { kind: CoderAgentEvent.StateChangeEvent }, 'Execution aborted by client.', undefined, true);
552
+ }
458
553
  }
459
- await currentTask.acceptAgentMessage(event);
460
- }
461
- if (abortSignal.aborted)
462
- throw new Error('Execution aborted');
463
- if (toolCallRequests.length > 0) {
464
- logger.info(`[CoderAgentExecutor] Task ${taskId}: Found ${toolCallRequests.length} tool call requests. Scheduling as a batch.`);
465
- await currentTask.scheduleToolCalls(toolCallRequests, abortSignal);
466
- }
467
- logger.info(`[CoderAgentExecutor] Task ${taskId}: Waiting for pending tools if any.`);
468
- await currentTask.waitForPendingTools();
469
- logger.info(`[CoderAgentExecutor] Task ${taskId}: All pending tools completed or none were pending.`);
470
- if (abortSignal.aborted)
471
- throw new Error('Execution aborted');
472
- if (currentTask.hasPendingTools) {
473
- logger.info(`[CoderAgentExecutor] Task ${taskId}: There are still ${currentTask.pendingToolsCount} pending tools waiting for approval. Yielding to user.`);
474
- agentTurnActive = false;
475
- }
476
- else {
477
- const completedTools = currentTask.getAndClearCompletedTools();
478
- if (completedTools.length > 0) {
479
- if (completedTools.every((tool) => tool.status === 'cancelled')) {
480
- logger.info(`[CoderAgentExecutor] Task ${taskId}: All tool calls were cancelled. Updating history and ending agent turn.`);
481
- currentTask.addToolResponsesToHistory(completedTools);
482
- agentTurnActive = false;
554
+ else {
555
+ const errorMessage = error instanceof Error ? error.message : 'Agent execution error';
556
+ logger.error(`[CoderAgentExecutor] Error executing agent for task ${taskId}:`, error);
557
+ currentTask.cancelPendingTools(errorMessage);
558
+ if (currentTask.taskState !== 'failed') {
483
559
  const stateChange = {
484
560
  kind: CoderAgentEvent.StateChangeEvent,
485
561
  };
486
- currentTask.setTaskStateAndPublishUpdate('input-required', stateChange, undefined, undefined, true);
487
- }
488
- else {
489
- logger.info(`[CoderAgentExecutor] Task ${taskId}: Found ${completedTools.length} completed tool calls. Sending results back to LLM.`);
490
- agentEvents = currentTask.sendCompletedToolsToLlm(completedTools, abortSignal);
562
+ currentTask.setTaskStateAndPublishUpdate('failed', stateChange, errorMessage, undefined, true);
491
563
  }
492
564
  }
493
- else {
494
- logger.info(`[CoderAgentExecutor] Task ${taskId}: No more tool calls to process. Ending agent turn.`);
495
- agentTurnActive = false;
496
- }
497
565
  }
498
- }
499
- logger.info(`[CoderAgentExecutor] Task ${taskId}: Agent turn finished, setting to input-required.`);
500
- const stateChange = {
501
- kind: CoderAgentEvent.StateChangeEvent,
502
- };
503
- currentTask.setTaskStateAndPublishUpdate('input-required', stateChange, undefined, undefined, true);
504
- }
505
- catch (error) {
506
- if (abortSignal.aborted) {
507
- logger.warn(`[CoderAgentExecutor] Task ${taskId} execution aborted.`);
508
- currentTask.cancelPendingTools('Execution aborted');
509
- if (currentTask.taskState !== 'canceled' &&
510
- currentTask.taskState !== 'failed') {
511
- currentTask.setTaskStateAndPublishUpdate('input-required', { kind: CoderAgentEvent.StateChangeEvent }, 'Execution aborted by client.', undefined, true);
566
+ finally {
567
+ if (isPrimaryExecution) {
568
+ const controllers = this.activeAbortControllers.get(taskId);
569
+ if (controllers) {
570
+ controllers.delete(abortController);
571
+ if (controllers.size === 0) {
572
+ this.activeAbortControllers.delete(taskId);
573
+ }
574
+ }
575
+ this.executingTasks.delete(taskId);
576
+ logger.info(`[CoderAgentExecutor] Saving final state for task ${taskId}.`);
577
+ try {
578
+ await this.taskStore?.save(wrapper.toSDKTask());
579
+ logger.info(`[CoderAgentExecutor] Task ${taskId} state saved.`);
580
+ }
581
+ catch (saveError) {
582
+ logger.error(`[CoderAgentExecutor] Failed to save task ${taskId} state in finally block:`, saveError);
583
+ }
584
+ if (['canceled', 'failed', 'completed'].includes(currentTask.taskState)) {
585
+ this.cleanupAndEvictTask(taskId);
586
+ }
587
+ }
512
588
  }
513
589
  }
514
- else {
515
- const errorMessage = error instanceof Error ? error.message : 'Agent execution error';
516
- logger.error(`[CoderAgentExecutor] Error executing agent for task ${taskId}:`, error);
517
- currentTask.cancelPendingTools(errorMessage);
518
- if (currentTask.taskState !== 'failed') {
519
- const stateChange = {
520
- kind: CoderAgentEvent.StateChangeEvent,
521
- };
522
- currentTask.setTaskStateAndPublishUpdate('failed', stateChange, errorMessage, undefined, true);
523
- }
590
+ catch (error) {
591
+ logger.error(`[CoderAgentExecutor] Failed to execute task ${taskId}:`, error);
592
+ void pushTaskStateFailed(error, eventBus, taskId, contextId);
593
+ throw error;
524
594
  }
525
- }
526
- finally {
527
- if (isPrimaryExecution) {
528
- const controllers = this.activeAbortControllers.get(taskId);
529
- if (controllers) {
530
- controllers.delete(abortController);
531
- if (controllers.size === 0) {
532
- this.activeAbortControllers.delete(taskId);
533
- }
534
- }
535
- this.executingTasks.delete(taskId);
536
- logger.info(`[CoderAgentExecutor] Saving final state for task ${taskId}.`);
537
- try {
538
- await this.taskStore?.save(wrapper.toSDKTask());
539
- logger.info(`[CoderAgentExecutor] Task ${taskId} state saved.`);
540
- }
541
- catch (saveError) {
542
- logger.error(`[CoderAgentExecutor] Failed to save task ${taskId} state in finally block:`, saveError);
543
- }
544
- if (['canceled', 'failed', 'completed'].includes(currentTask.taskState)) {
545
- this.cleanupAndEvictTask(taskId);
546
- }
595
+ }).catch((error) => {
596
+ logger.error(`[CoderAgentExecutor] Failed to initialize isolated environment for task ${taskId}:`, error);
597
+ if (eventBus) {
598
+ void pushTaskStateFailed(error, eventBus, taskId, contextId);
547
599
  }
548
- }
600
+ throw error;
601
+ });
549
602
  }
550
603
  }
551
604
  //# sourceMappingURL=executor.js.map