@agentrix/cli 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-A8QO7gdw.cjs → index-CCpqyHjv.cjs} +579 -239
- package/dist/{index-BdFjpsoN.cjs → index-DDSUzta4.cjs} +8 -90
- package/dist/{index-D1ipaIV_.mjs → index-DoalEmuX.mjs} +8 -90
- package/dist/{index-Dzjo3uhE.mjs → index-Dtz97eJO.mjs} +580 -240
- package/dist/index.cjs +6 -3
- package/dist/index.mjs +6 -3
- package/dist/lib.cjs +5 -1
- package/dist/lib.d.cts +111 -0
- package/dist/lib.d.mts +111 -0
- package/dist/lib.mjs +5 -1
- package/dist/{logger-D-ioMWe6.mjs → logger-BRmB311i.mjs} +158 -24
- package/dist/{logger---ZD5a2u.cjs → logger-DFD0a_cW.cjs} +140 -5
- package/dist/sandbox/node-proxy-boot.js +13 -0
- package/package.json +6 -4
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import yargs from 'yargs';
|
|
2
2
|
import { hideBin } from 'yargs/helpers';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { encodeBase64, createKeyPairWithUit8Array, encryptMachineEncryptionKey, generateAESKey, decodeBase64, decryptWithEphemeralKey, createEventId, encryptFileContent, machineAuth, encryptSdkMessage, decryptSdkMessage, loadAgentConfig, getAgentContext, workerAuth } from '@agentrix/shared';
|
|
4
|
+
import { encodeBase64, createKeyPairWithUit8Array, encryptMachineEncryptionKey, generateAESKey, decodeBase64, decryptWithEphemeralKey, createEventId, encryptFileContent, machineAuth, encryptSdkMessage, decryptSdkMessage, loadAgentConfig, getAgentContext, workerAuth, isAskUserResponseMessage } from '@agentrix/shared';
|
|
5
5
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
6
6
|
import axios from 'axios';
|
|
7
|
-
import { m as machine, l as logger, p as projectPath, a as packageJson, c as createLogger, g as getLogPath, b as logger$1 } from './logger-
|
|
8
|
-
import * as fs from 'node:fs';
|
|
9
|
-
import { existsSync, rmSync, createWriteStream, readdirSync, mkdirSync } from 'node:fs';
|
|
7
|
+
import { m as machine, l as logger, p as projectPath, a as packageJson, c as createLogger, g as getLogPath, b as logger$1 } from './logger-BRmB311i.mjs';
|
|
10
8
|
import { createInterface } from 'node:readline';
|
|
11
9
|
import fs$1, { readFileSync, existsSync as existsSync$1 } from 'fs';
|
|
12
10
|
import path$1, { join } from 'path';
|
|
@@ -14,11 +12,15 @@ import os, { homedir } from 'node:os';
|
|
|
14
12
|
import open from 'open';
|
|
15
13
|
import { io } from 'socket.io-client';
|
|
16
14
|
import { EventEmitter } from 'node:events';
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import { existsSync, createWriteStream, readdirSync, mkdirSync } from 'node:fs';
|
|
17
17
|
import * as path from 'node:path';
|
|
18
18
|
import { join as join$1, basename, extname, dirname } from 'node:path';
|
|
19
19
|
import { spawn, execSync } from 'child_process';
|
|
20
20
|
import psList from 'ps-list';
|
|
21
21
|
import spawn$1 from 'cross-spawn';
|
|
22
|
+
import { isSupportedPlatform, checkSandboxDependencies, NetworkManager, SandboxManager } from '@xmz-ai/sandbox-runtime';
|
|
23
|
+
import { getPlatform } from '@xmz-ai/sandbox-runtime/dist/utils/platform.js';
|
|
22
24
|
import fastify from 'fastify';
|
|
23
25
|
import { z } from 'zod';
|
|
24
26
|
import { validatorCompiler, serializerCompiler } from 'fastify-type-provider-zod';
|
|
@@ -44,22 +46,12 @@ async function daemonPost(path, body) {
|
|
|
44
46
|
error: errorMessage
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
|
-
try {
|
|
48
|
-
process.kill(state.pid, 0);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
const errorMessage = "Daemon is not running, file is stale";
|
|
51
|
-
logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
52
|
-
return {
|
|
53
|
-
error: errorMessage
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
49
|
try {
|
|
57
50
|
const timeout = process.env.AGENTRIX_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.AGENTRIX_DAEMON_HTTP_TIMEOUT) : 1e4;
|
|
58
|
-
const response = await fetch(`http://
|
|
51
|
+
const response = await fetch(`http://agentrix-local.xmz.ai:${state.port}${path}`, {
|
|
59
52
|
method: "POST",
|
|
60
53
|
headers: { "Content-Type": "application/json" },
|
|
61
54
|
body: JSON.stringify(body || {}),
|
|
62
|
-
// Mostly increased for stress test
|
|
63
55
|
signal: AbortSignal.timeout(timeout)
|
|
64
56
|
});
|
|
65
57
|
if (!response.ok) {
|
|
@@ -205,11 +197,8 @@ async function handleAuthLogout() {
|
|
|
205
197
|
console.log(chalk.gray("Stopped daemon"));
|
|
206
198
|
} catch {
|
|
207
199
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
rmSync(paths.rootDir, { recursive: true, force: true });
|
|
211
|
-
console.log(chalk.gray(`Removed agentrix home directory`));
|
|
212
|
-
}
|
|
200
|
+
await machine.clearCredentials();
|
|
201
|
+
console.log(chalk.gray(`Removed credentials`));
|
|
213
202
|
console.log(chalk.green("\u2713 Successfully logged out"));
|
|
214
203
|
} catch (error) {
|
|
215
204
|
throw new Error(`Failed to logout: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -11865,7 +11854,7 @@ async function findAllAgentrixProcesses() {
|
|
|
11865
11854
|
async function findRunawayAgentrixProcesses() {
|
|
11866
11855
|
const allProcesses = await findAllAgentrixProcesses();
|
|
11867
11856
|
return allProcesses.filter(
|
|
11868
|
-
(p) => p.pid !== process.pid && (p.type === "daemon" || p.type === "worker")
|
|
11857
|
+
(p) => p.pid !== process.pid && (p.type === "daemon" || p.type === "worker" || p.type === "upgrade-daemon")
|
|
11869
11858
|
).map((p) => ({ pid: p.pid, command: p.command }));
|
|
11870
11859
|
}
|
|
11871
11860
|
async function killRunawayAgentrixProcesses() {
|
|
@@ -11967,6 +11956,27 @@ async function runDoctorCommand(filter) {
|
|
|
11967
11956
|
} catch (error) {
|
|
11968
11957
|
console.log(chalk.red("\u274C Error reading credentials"));
|
|
11969
11958
|
}
|
|
11959
|
+
console.log(chalk.bold("\n\u{1F512} Sandbox Dependencies"));
|
|
11960
|
+
const platform = getPlatform();
|
|
11961
|
+
if (isSupportedPlatform(platform)) {
|
|
11962
|
+
console.log(`Platform: ${chalk.green(platform)} (supported)`);
|
|
11963
|
+
const depsOk = checkSandboxDependencies();
|
|
11964
|
+
if (depsOk) {
|
|
11965
|
+
console.log(chalk.green("\u2713 All sandbox dependencies available"));
|
|
11966
|
+
} else {
|
|
11967
|
+
console.log(chalk.yellow("\u26A0\uFE0F Some sandbox dependencies missing"));
|
|
11968
|
+
if (platform === "linux") {
|
|
11969
|
+
console.log(chalk.gray(" Required: bubblewrap, socat"));
|
|
11970
|
+
console.log(chalk.gray(" Install: sudo apt install bubblewrap socat"));
|
|
11971
|
+
} else if (platform === "macos") {
|
|
11972
|
+
console.log(chalk.gray(" Required: ripgrep"));
|
|
11973
|
+
console.log(chalk.gray(" Install: brew install ripgrep"));
|
|
11974
|
+
}
|
|
11975
|
+
}
|
|
11976
|
+
} else {
|
|
11977
|
+
console.log(`Platform: ${chalk.yellow(platform)} (not supported)`);
|
|
11978
|
+
console.log(chalk.gray(" \u26A0\uFE0F Sandbox will be disabled"));
|
|
11979
|
+
}
|
|
11970
11980
|
}
|
|
11971
11981
|
console.log(chalk.bold("\n\u{1F916} Daemon Status"));
|
|
11972
11982
|
try {
|
|
@@ -12191,9 +12201,11 @@ function createPromiseWithTimeout(options) {
|
|
|
12191
12201
|
class TaskWorkerManager {
|
|
12192
12202
|
pidToTrackedSession;
|
|
12193
12203
|
pidToAwaiter;
|
|
12194
|
-
|
|
12204
|
+
sandboxPool;
|
|
12205
|
+
constructor(sandboxPool) {
|
|
12195
12206
|
this.pidToTrackedSession = /* @__PURE__ */ new Map();
|
|
12196
12207
|
this.pidToAwaiter = /* @__PURE__ */ new Map();
|
|
12208
|
+
this.sandboxPool = sandboxPool || null;
|
|
12197
12209
|
}
|
|
12198
12210
|
getCurrentSessions() {
|
|
12199
12211
|
return Array.from(this.pidToTrackedSession.values());
|
|
@@ -12234,9 +12246,15 @@ class TaskWorkerManager {
|
|
|
12234
12246
|
this.pidToTrackedSession.set(workerProcess.pid, tracked);
|
|
12235
12247
|
workerProcess.on("exit", (code, signal) => {
|
|
12236
12248
|
this.pidToTrackedSession.delete(workerProcess.pid);
|
|
12249
|
+
if (this.sandboxPool) {
|
|
12250
|
+
this.sandboxPool.disposeWorkerSandbox(data.taskId);
|
|
12251
|
+
}
|
|
12237
12252
|
});
|
|
12238
12253
|
workerProcess.on("error", (error) => {
|
|
12239
12254
|
this.pidToTrackedSession.delete(workerProcess.pid);
|
|
12255
|
+
if (this.sandboxPool) {
|
|
12256
|
+
this.sandboxPool.disposeWorkerSandbox(data.taskId);
|
|
12257
|
+
}
|
|
12240
12258
|
});
|
|
12241
12259
|
}
|
|
12242
12260
|
async startWorker(options) {
|
|
@@ -12261,14 +12279,53 @@ class TaskWorkerManager {
|
|
|
12261
12279
|
"--idle-timeout",
|
|
12262
12280
|
"120"
|
|
12263
12281
|
];
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12282
|
+
let workerProcess;
|
|
12283
|
+
if (this.sandboxPool?.isEnabled()) {
|
|
12284
|
+
try {
|
|
12285
|
+
const sandbox = await this.sandboxPool.createWorkerSandbox(
|
|
12286
|
+
options.taskId,
|
|
12287
|
+
options.userId,
|
|
12288
|
+
cwd
|
|
12289
|
+
);
|
|
12290
|
+
if (!sandbox) {
|
|
12291
|
+
throw new Error("Failed to create sandbox instance");
|
|
12292
|
+
}
|
|
12293
|
+
const { projectPath } = await import('./logger-BRmB311i.mjs').then(function (n) { return n.d; });
|
|
12294
|
+
const { join } = await import('path');
|
|
12295
|
+
const entrypoint = join(projectPath(), "dist", "index.mjs");
|
|
12296
|
+
const nodeArgs = ["--no-warnings", "--no-deprecation", entrypoint, ...args];
|
|
12297
|
+
const originalCommand = `"${process.execPath}" ${nodeArgs.map((a) => `"${a}"`).join(" ")}`;
|
|
12298
|
+
const sandboxedCommand = await this.sandboxPool.wrapWorkerCommand(
|
|
12299
|
+
options.taskId,
|
|
12300
|
+
originalCommand
|
|
12301
|
+
);
|
|
12302
|
+
logger.debug(`[SESSION] Sandboxed command for task ${options.taskId}: ${sandboxedCommand}`);
|
|
12303
|
+
workerProcess = spawn(sandboxedCommand, {
|
|
12304
|
+
shell: true,
|
|
12305
|
+
cwd,
|
|
12306
|
+
detached: true,
|
|
12307
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12308
|
+
env: {
|
|
12309
|
+
...process.env
|
|
12310
|
+
// Environment variables controlled by SandboxManager
|
|
12311
|
+
}
|
|
12312
|
+
});
|
|
12313
|
+
logger.info(`[SESSION] Worker started with sandbox, PID: ${workerProcess.pid}`);
|
|
12314
|
+
} catch (error) {
|
|
12315
|
+
logger.error(`[SESSION] Failed to setup sandbox for task ${options.taskId}:`, error);
|
|
12316
|
+
ack.status = "failed";
|
|
12317
|
+
ack.message = `Sandbox setup failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
12318
|
+
return ack;
|
|
12270
12319
|
}
|
|
12271
|
-
}
|
|
12320
|
+
} else {
|
|
12321
|
+
workerProcess = spawnAgentrixCLI(args, {
|
|
12322
|
+
cwd,
|
|
12323
|
+
detached: true,
|
|
12324
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12325
|
+
env: { ...process.env }
|
|
12326
|
+
});
|
|
12327
|
+
logger.info(`[SESSION] Worker started without sandbox, PID: ${workerProcess.pid}`);
|
|
12328
|
+
}
|
|
12272
12329
|
if (process.env.DEBUG) {
|
|
12273
12330
|
workerProcess.stdout?.on("data", (data) => {
|
|
12274
12331
|
logger.debug(`[Daemon] worker stdout: ${data.toString()}`);
|
|
@@ -12387,6 +12444,114 @@ function setupGracefulShutdown(options) {
|
|
|
12387
12444
|
};
|
|
12388
12445
|
}
|
|
12389
12446
|
|
|
12447
|
+
class SandboxPool {
|
|
12448
|
+
networkManager = null;
|
|
12449
|
+
workerSandboxes = /* @__PURE__ */ new Map();
|
|
12450
|
+
settings = null;
|
|
12451
|
+
platform;
|
|
12452
|
+
constructor() {
|
|
12453
|
+
this.platform = getPlatform();
|
|
12454
|
+
}
|
|
12455
|
+
async initialize(settings) {
|
|
12456
|
+
this.settings = settings;
|
|
12457
|
+
if (!settings.enabled) {
|
|
12458
|
+
logger.info("[SANDBOX] Sandbox disabled via settings");
|
|
12459
|
+
return false;
|
|
12460
|
+
}
|
|
12461
|
+
if (!isSupportedPlatform(this.platform)) {
|
|
12462
|
+
logger.warn("[SANDBOX] Platform not supported, sandbox disabled");
|
|
12463
|
+
return false;
|
|
12464
|
+
}
|
|
12465
|
+
try {
|
|
12466
|
+
const apiHost = new URL(machine.serverUrl).hostname;
|
|
12467
|
+
const networkConfig = {
|
|
12468
|
+
allowedDomains: [
|
|
12469
|
+
apiHost,
|
|
12470
|
+
...settings.network.allowedDomains
|
|
12471
|
+
],
|
|
12472
|
+
deniedDomains: settings.network.deniedDomains,
|
|
12473
|
+
allowLocalBinding: false
|
|
12474
|
+
};
|
|
12475
|
+
this.networkManager = new NetworkManager();
|
|
12476
|
+
await this.networkManager.initialize(networkConfig);
|
|
12477
|
+
logger.info("[SANDBOX] Sandbox pool initialized successfully");
|
|
12478
|
+
return true;
|
|
12479
|
+
} catch (error) {
|
|
12480
|
+
logger.error("[SANDBOX] Failed to initialize:", error);
|
|
12481
|
+
throw error;
|
|
12482
|
+
}
|
|
12483
|
+
}
|
|
12484
|
+
async createWorkerSandbox(taskId, userId, workingDirectory) {
|
|
12485
|
+
if (!this.networkManager || !this.settings?.enabled) {
|
|
12486
|
+
return null;
|
|
12487
|
+
}
|
|
12488
|
+
try {
|
|
12489
|
+
const taskDir = machine.resolveTaskDir(userId, taskId);
|
|
12490
|
+
const logsDir = machine.getStatePaths().logsDir;
|
|
12491
|
+
const baseFilesystem = this.settings.filesystem || {};
|
|
12492
|
+
const baseEnv = this.settings.env || {};
|
|
12493
|
+
const filesystemConfig = {
|
|
12494
|
+
...baseFilesystem,
|
|
12495
|
+
allowWrite: [
|
|
12496
|
+
...baseFilesystem.allowWrite || [],
|
|
12497
|
+
taskDir,
|
|
12498
|
+
workingDirectory,
|
|
12499
|
+
logsDir
|
|
12500
|
+
]
|
|
12501
|
+
};
|
|
12502
|
+
if (this.platform === "linux" && baseFilesystem.allowRead) {
|
|
12503
|
+
filesystemConfig.allowRead = [
|
|
12504
|
+
...baseFilesystem.allowRead
|
|
12505
|
+
];
|
|
12506
|
+
}
|
|
12507
|
+
const instanceConfig = {
|
|
12508
|
+
filesystem: filesystemConfig,
|
|
12509
|
+
env: baseEnv
|
|
12510
|
+
};
|
|
12511
|
+
const sandbox = new SandboxManager(this.networkManager, instanceConfig);
|
|
12512
|
+
this.workerSandboxes.set(taskId, sandbox);
|
|
12513
|
+
logger.info(`[SANDBOX] Created sandbox for task ${taskId}`);
|
|
12514
|
+
return sandbox;
|
|
12515
|
+
} catch (error) {
|
|
12516
|
+
logger.error(`[SANDBOX] Failed to create sandbox for task ${taskId}:`, error);
|
|
12517
|
+
return null;
|
|
12518
|
+
}
|
|
12519
|
+
}
|
|
12520
|
+
async wrapWorkerCommand(taskId, command) {
|
|
12521
|
+
const sandbox = this.workerSandboxes.get(taskId);
|
|
12522
|
+
if (!sandbox) {
|
|
12523
|
+
throw new Error(`No sandbox found for task ${taskId}`);
|
|
12524
|
+
}
|
|
12525
|
+
const wrapped = await sandbox.wrapWithSandbox(command);
|
|
12526
|
+
logger.debug(`[SANDBOX] Wrapped command for task ${taskId}`);
|
|
12527
|
+
return wrapped;
|
|
12528
|
+
}
|
|
12529
|
+
disposeWorkerSandbox(taskId) {
|
|
12530
|
+
const sandbox = this.workerSandboxes.get(taskId);
|
|
12531
|
+
if (sandbox) {
|
|
12532
|
+
sandbox.dispose();
|
|
12533
|
+
this.workerSandboxes.delete(taskId);
|
|
12534
|
+
logger.debug(`[SANDBOX] Disposed sandbox for task ${taskId}`);
|
|
12535
|
+
}
|
|
12536
|
+
}
|
|
12537
|
+
async shutdown() {
|
|
12538
|
+
logger.info("[SANDBOX] Shutting down sandbox pool");
|
|
12539
|
+
for (const [taskId, sandbox] of this.workerSandboxes.entries()) {
|
|
12540
|
+
sandbox.dispose();
|
|
12541
|
+
logger.debug(`[SANDBOX] Disposed sandbox for task ${taskId}`);
|
|
12542
|
+
}
|
|
12543
|
+
this.workerSandboxes.clear();
|
|
12544
|
+
if (this.networkManager) {
|
|
12545
|
+
await this.networkManager.shutdown();
|
|
12546
|
+
this.networkManager = null;
|
|
12547
|
+
logger.info("[SANDBOX] Network manager shutdown complete");
|
|
12548
|
+
}
|
|
12549
|
+
}
|
|
12550
|
+
isEnabled() {
|
|
12551
|
+
return this.settings?.enabled === true;
|
|
12552
|
+
}
|
|
12553
|
+
}
|
|
12554
|
+
|
|
12390
12555
|
async function startDaemon() {
|
|
12391
12556
|
Object.assign(logger, createLogger({ type: "daemon" }));
|
|
12392
12557
|
const { requestShutdown, shutdownPromise } = setupGracefulShutdown({
|
|
@@ -12415,7 +12580,9 @@ async function startDaemon() {
|
|
|
12415
12580
|
}
|
|
12416
12581
|
const credentials = await authAndSetupMachineIfNeeded();
|
|
12417
12582
|
logger.debug("[DAEMON RUN] Auth and machine setup complete");
|
|
12418
|
-
const
|
|
12583
|
+
const sandboxPool = new SandboxPool();
|
|
12584
|
+
await sandboxPool.initialize(machine.getSandboxSettings());
|
|
12585
|
+
const sessionManager = new TaskWorkerManager(sandboxPool);
|
|
12419
12586
|
const { port: controlPort, stop: stopControlServer } = await startDaemonControlServer({
|
|
12420
12587
|
getChildren: () => sessionManager.getCurrentSessions(),
|
|
12421
12588
|
stopSession: (id) => sessionManager.stopSession(id),
|
|
@@ -12463,6 +12630,7 @@ async function startDaemon() {
|
|
|
12463
12630
|
const cleanupAndShutdown = async (source, errorMessage) => {
|
|
12464
12631
|
await machineClient.disconnect();
|
|
12465
12632
|
await stopControlServer();
|
|
12633
|
+
await sandboxPool.shutdown();
|
|
12466
12634
|
await cleanupDaemonState();
|
|
12467
12635
|
await stopCaffeinate();
|
|
12468
12636
|
await machine.releaseDaemonLock(daemonLockHandle);
|
|
@@ -12579,14 +12747,6 @@ function createWorkerEventHandlers(context) {
|
|
|
12579
12747
|
}
|
|
12580
12748
|
}
|
|
12581
12749
|
}
|
|
12582
|
-
},
|
|
12583
|
-
"require-permission-response": async (data) => {
|
|
12584
|
-
if (data.taskId !== context.taskId) {
|
|
12585
|
-
return;
|
|
12586
|
-
}
|
|
12587
|
-
if (context.onPermissionResponse) {
|
|
12588
|
-
await context.onPermissionResponse(data);
|
|
12589
|
-
}
|
|
12590
12750
|
}
|
|
12591
12751
|
};
|
|
12592
12752
|
}
|
|
@@ -12627,7 +12787,6 @@ class WorkerClient {
|
|
|
12627
12787
|
cwd: normalizedCwd,
|
|
12628
12788
|
stopTask: options.stopTask,
|
|
12629
12789
|
onTaskMessage: options.onTaskMessage,
|
|
12630
|
-
onPermissionResponse: options.onPermissionResponse,
|
|
12631
12790
|
onGitPush: options.onGitPush,
|
|
12632
12791
|
dataEncryptionKey: config.dataEncryptionKey
|
|
12633
12792
|
};
|
|
@@ -12656,26 +12815,32 @@ class WorkerClient {
|
|
|
12656
12815
|
}
|
|
12657
12816
|
this.client.disconnect();
|
|
12658
12817
|
}
|
|
12659
|
-
sendTaskMessage(message) {
|
|
12660
|
-
const
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12818
|
+
sendTaskMessage(message, options) {
|
|
12819
|
+
const { replaceCwd = true } = options || {};
|
|
12820
|
+
let processedMessage = message;
|
|
12821
|
+
if (replaceCwd) {
|
|
12822
|
+
const cwdWithSlash = this.context.cwd;
|
|
12823
|
+
const cwdWithoutSlash = cwdWithSlash.slice(0, -1);
|
|
12824
|
+
let content = JSON.stringify(message);
|
|
12825
|
+
content = content.replaceAll(cwdWithSlash, "");
|
|
12826
|
+
content = content.replaceAll(cwdWithoutSlash, ".");
|
|
12827
|
+
processedMessage = JSON.parse(content);
|
|
12828
|
+
}
|
|
12666
12829
|
let encryptedMessage;
|
|
12667
12830
|
if (this.context.dataEncryptionKey) {
|
|
12668
|
-
encryptedMessage = encryptSdkMessage(
|
|
12669
|
-
|
|
12831
|
+
encryptedMessage = encryptSdkMessage(processedMessage, this.context.dataEncryptionKey);
|
|
12832
|
+
processedMessage = void 0;
|
|
12670
12833
|
}
|
|
12834
|
+
const eventId = createEventId();
|
|
12671
12835
|
const payload = {
|
|
12672
|
-
eventId
|
|
12836
|
+
eventId,
|
|
12673
12837
|
taskId: this.context.taskId,
|
|
12674
12838
|
from: "worker",
|
|
12675
|
-
message:
|
|
12839
|
+
message: processedMessage,
|
|
12676
12840
|
encryptedMessage
|
|
12677
12841
|
};
|
|
12678
12842
|
this.client.send("task-message", payload);
|
|
12843
|
+
return eventId;
|
|
12679
12844
|
}
|
|
12680
12845
|
sendWorkerInitializing() {
|
|
12681
12846
|
const workerInitializingEvent = {
|
|
@@ -12686,12 +12851,13 @@ class WorkerClient {
|
|
|
12686
12851
|
};
|
|
12687
12852
|
this.client.send("worker-initializing", workerInitializingEvent);
|
|
12688
12853
|
}
|
|
12689
|
-
sendWorkerReady() {
|
|
12854
|
+
sendWorkerReady(duration) {
|
|
12690
12855
|
const workerReadyEvent = {
|
|
12691
12856
|
eventId: createEventId(),
|
|
12692
12857
|
taskId: this.context.taskId,
|
|
12693
12858
|
machineId: this.context.machineId,
|
|
12694
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
12859
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12860
|
+
...duration !== void 0 && { duration }
|
|
12695
12861
|
};
|
|
12696
12862
|
this.client.send("worker-ready", workerReadyEvent);
|
|
12697
12863
|
}
|
|
@@ -12749,17 +12915,13 @@ ${errorMessage}`,
|
|
|
12749
12915
|
};
|
|
12750
12916
|
this.sendTaskMessage(systemMessage);
|
|
12751
12917
|
}
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
|
|
12759
|
-
};
|
|
12760
|
-
this.client.send("require-permission", permissionRequest);
|
|
12761
|
-
logger.info(`[AGENT] Permission requested for tool: ${toolName}`);
|
|
12762
|
-
return eventId;
|
|
12918
|
+
/**
|
|
12919
|
+
* Send ask-user message to request user input
|
|
12920
|
+
* @param questions - Array of questions (1-4)
|
|
12921
|
+
* @returns eventId for tracking the request
|
|
12922
|
+
*/
|
|
12923
|
+
sendAskUser(questions) {
|
|
12924
|
+
return this.sendTaskMessage({ type: "ask_user", questions }, { replaceCwd: false });
|
|
12763
12925
|
}
|
|
12764
12926
|
sendUpdateTaskAgentSessionId(agentSessionId) {
|
|
12765
12927
|
const updateSessionEvent = {
|
|
@@ -12814,7 +12976,6 @@ ${errorMessage}`,
|
|
|
12814
12976
|
this.client.onEvent("cancel-task", handlers["cancel-task"]);
|
|
12815
12977
|
this.client.onEvent("stop-task", handlers["stop-task"]);
|
|
12816
12978
|
this.client.onEvent("task-message", handlers["task-message"]);
|
|
12817
|
-
this.client.onEvent("require-permission-response", handlers["require-permission-response"]);
|
|
12818
12979
|
}
|
|
12819
12980
|
}
|
|
12820
12981
|
|
|
@@ -13017,6 +13178,12 @@ async function hasUncommittedChanges(dir) {
|
|
|
13017
13178
|
const status = await git.status();
|
|
13018
13179
|
return !status.isClean();
|
|
13019
13180
|
}
|
|
13181
|
+
async function gitStash(dir, message) {
|
|
13182
|
+
const git = simpleGit(dir);
|
|
13183
|
+
{
|
|
13184
|
+
await git.stash(["push"]);
|
|
13185
|
+
}
|
|
13186
|
+
}
|
|
13020
13187
|
async function getCurrentCommitHash(dir) {
|
|
13021
13188
|
const git = simpleGit(dir);
|
|
13022
13189
|
const log = await git.log({ maxCount: 1 });
|
|
@@ -13106,6 +13273,30 @@ async function executeHook(hooks, hookName, input, logger) {
|
|
|
13106
13273
|
}
|
|
13107
13274
|
}
|
|
13108
13275
|
|
|
13276
|
+
async function checkUncommittedChanges(workingDirectory) {
|
|
13277
|
+
const isRepo = await isGitRepository(workingDirectory);
|
|
13278
|
+
if (!isRepo) {
|
|
13279
|
+
return false;
|
|
13280
|
+
}
|
|
13281
|
+
return await hasUncommittedChanges(workingDirectory);
|
|
13282
|
+
}
|
|
13283
|
+
async function handleUncommittedChanges(workingDirectory, action) {
|
|
13284
|
+
switch (action) {
|
|
13285
|
+
case "Ignore":
|
|
13286
|
+
console.log("[GIT] User chose to ignore uncommitted changes");
|
|
13287
|
+
break;
|
|
13288
|
+
case "Commit":
|
|
13289
|
+
console.log("[GIT] Auto-committing uncommitted changes");
|
|
13290
|
+
await autoCommit(workingDirectory, "WIP: Auto-commit before task");
|
|
13291
|
+
break;
|
|
13292
|
+
case "Stash":
|
|
13293
|
+
console.log("[GIT] Stashing uncommitted changes");
|
|
13294
|
+
await gitStash(workingDirectory);
|
|
13295
|
+
break;
|
|
13296
|
+
case "Abort":
|
|
13297
|
+
throw new Error("Task aborted by user due to uncommitted changes");
|
|
13298
|
+
}
|
|
13299
|
+
}
|
|
13109
13300
|
function createTaskBranchName(taskId) {
|
|
13110
13301
|
return `agentrix/${taskId}`;
|
|
13111
13302
|
}
|
|
@@ -13145,17 +13336,6 @@ async function setupLocalWorkspace(workingDirectory, taskId, hooks) {
|
|
|
13145
13336
|
const isRepo = await isGitRepository(workingDirectory);
|
|
13146
13337
|
const isEmpty = isDirectoryEmpty(workingDirectory);
|
|
13147
13338
|
if (isRepo) {
|
|
13148
|
-
const hasChanges = await hasUncommittedChanges(workingDirectory);
|
|
13149
|
-
if (hasChanges) {
|
|
13150
|
-
throw new Error(
|
|
13151
|
-
`Directory ${workingDirectory} has uncommitted changes.
|
|
13152
|
-
|
|
13153
|
-
Please commit or stash your changes before starting:
|
|
13154
|
-
git add . && git commit -m "WIP"
|
|
13155
|
-
or:
|
|
13156
|
-
git stash`
|
|
13157
|
-
);
|
|
13158
|
-
}
|
|
13159
13339
|
const hasCommits = await hasAnyCommits(workingDirectory);
|
|
13160
13340
|
if (!hasCommits) {
|
|
13161
13341
|
console.log("[GIT] Repository has no commits, creating initial commit");
|
|
@@ -13283,6 +13463,29 @@ async function markCommitAsSent(userId, taskId, commitHash) {
|
|
|
13283
13463
|
await machine.writeLastSentCommitHash(userId, taskId, commitHash);
|
|
13284
13464
|
}
|
|
13285
13465
|
|
|
13466
|
+
function getDefaultPRPrompt(params) {
|
|
13467
|
+
return `All changes have been pushed to branch "${params.branchName}".
|
|
13468
|
+
|
|
13469
|
+
Commit range: ${params.initialCommitHash}..${params.currentCommitHash}
|
|
13470
|
+
|
|
13471
|
+
Based on our conversation context, create a Pull Request:
|
|
13472
|
+
- Title: conventional commits format (feat/fix/docs/refactor/test/chore: description)
|
|
13473
|
+
- Description: what changed, why, and any important decisions
|
|
13474
|
+
|
|
13475
|
+
Use mcp__agentrix__create_pr tool to create the PR.`;
|
|
13476
|
+
}
|
|
13477
|
+
function applyTemplateVariables(template, params) {
|
|
13478
|
+
return template.replace(/\{\{initialCommitHash\}\}/g, params.initialCommitHash).replace(/\{\{currentCommitHash\}\}/g, params.currentCommitHash).replace(/\{\{branchName\}\}/g, params.branchName);
|
|
13479
|
+
}
|
|
13480
|
+
function buildPRPrompt(params, config) {
|
|
13481
|
+
const defaultPrompt = getDefaultPRPrompt(params);
|
|
13482
|
+
if (!config?.customTemplate) {
|
|
13483
|
+
return defaultPrompt;
|
|
13484
|
+
}
|
|
13485
|
+
const customPrompt = applyTemplateVariables(config.customTemplate, params);
|
|
13486
|
+
return config.mode === "replace" ? customPrompt : defaultPrompt + "\n\n" + customPrompt;
|
|
13487
|
+
}
|
|
13488
|
+
|
|
13286
13489
|
function executeCommandStreaming(command, cwd, callbacks, timeoutMs = 6e4) {
|
|
13287
13490
|
return new Promise((resolve) => {
|
|
13288
13491
|
const toolUseId = `shell_${randomUUID$1().replace(/-/g, "")}`;
|
|
@@ -13400,6 +13603,7 @@ class MessageCoordinator {
|
|
|
13400
13603
|
currentMessageId = null;
|
|
13401
13604
|
messageIdCounter = 0;
|
|
13402
13605
|
isStopped = false;
|
|
13606
|
+
runStartTime = null;
|
|
13403
13607
|
constructor(config) {
|
|
13404
13608
|
this.config = config;
|
|
13405
13609
|
}
|
|
@@ -13519,11 +13723,11 @@ class MessageCoordinator {
|
|
|
13519
13723
|
async processBashCommand(envelope) {
|
|
13520
13724
|
this.log("info", "COORDINATOR", `Processing bash command: ${envelope.content}`);
|
|
13521
13725
|
await this.waitForState("idle");
|
|
13522
|
-
this.
|
|
13726
|
+
this.setWorkerState("running");
|
|
13523
13727
|
try {
|
|
13524
13728
|
await this.config.handlers.onBashCommand(envelope.content, envelope.originalMessage);
|
|
13525
13729
|
} finally {
|
|
13526
|
-
this.
|
|
13730
|
+
this.setWorkerState("idle");
|
|
13527
13731
|
}
|
|
13528
13732
|
}
|
|
13529
13733
|
/**
|
|
@@ -13532,11 +13736,11 @@ class MessageCoordinator {
|
|
|
13532
13736
|
async processMergeRequest(envelope) {
|
|
13533
13737
|
this.log("info", "COORDINATOR", "Processing merge-request command");
|
|
13534
13738
|
await this.waitForState("idle");
|
|
13535
|
-
this.
|
|
13739
|
+
this.setWorkerState("running");
|
|
13536
13740
|
try {
|
|
13537
13741
|
await this.config.handlers.onMergeRequest(envelope.originalMessage);
|
|
13538
13742
|
} finally {
|
|
13539
|
-
this.
|
|
13743
|
+
this.setWorkerState("idle");
|
|
13540
13744
|
}
|
|
13541
13745
|
}
|
|
13542
13746
|
async waitForState(targetState) {
|
|
@@ -13556,11 +13760,24 @@ class MessageCoordinator {
|
|
|
13556
13760
|
}
|
|
13557
13761
|
/**
|
|
13558
13762
|
* Set the worker state (called by worker when state changes)
|
|
13763
|
+
* Automatically sends WebSocket events and tracks execution duration
|
|
13559
13764
|
*/
|
|
13560
13765
|
setWorkerState(state) {
|
|
13561
|
-
if (this.workerState
|
|
13562
|
-
|
|
13563
|
-
|
|
13766
|
+
if (this.workerState === state) return;
|
|
13767
|
+
const prevState = this.workerState;
|
|
13768
|
+
this.log("info", "COORDINATOR", `Worker state: ${prevState} \u2192 ${state}`);
|
|
13769
|
+
this.workerState = state;
|
|
13770
|
+
if (state === "running" && prevState === "idle") {
|
|
13771
|
+
this.runStartTime = Date.now();
|
|
13772
|
+
this.config.workClient.sendWorkRunning();
|
|
13773
|
+
}
|
|
13774
|
+
if (state === "idle" && prevState === "running") {
|
|
13775
|
+
let duration;
|
|
13776
|
+
if (this.runStartTime) {
|
|
13777
|
+
duration = Date.now() - this.runStartTime;
|
|
13778
|
+
this.runStartTime = null;
|
|
13779
|
+
}
|
|
13780
|
+
this.config.workClient.sendWorkerReady(duration);
|
|
13564
13781
|
}
|
|
13565
13782
|
}
|
|
13566
13783
|
/**
|
|
@@ -13600,7 +13817,7 @@ class ClaudeWorker {
|
|
|
13600
13817
|
messageQueue = [];
|
|
13601
13818
|
messageResolverRef = { current: null };
|
|
13602
13819
|
abortController = new AbortController();
|
|
13603
|
-
|
|
13820
|
+
askUserAwaiter = /* @__PURE__ */ new Map();
|
|
13604
13821
|
filteredToolUseIds = /* @__PURE__ */ new Set();
|
|
13605
13822
|
timerManager;
|
|
13606
13823
|
context;
|
|
@@ -13610,6 +13827,11 @@ class ClaudeWorker {
|
|
|
13610
13827
|
dataEncryptionKey = null;
|
|
13611
13828
|
coordinator;
|
|
13612
13829
|
loadedHooks;
|
|
13830
|
+
loadedAgentConfig;
|
|
13831
|
+
// Pending permission requests: toolName -> Promise<'allow' | 'deny'> (to dedupe concurrent requests)
|
|
13832
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
13833
|
+
// Granted permissions cache: toolName -> true (to avoid repeated asks for same tool)
|
|
13834
|
+
grantedPermissions = /* @__PURE__ */ new Set();
|
|
13613
13835
|
async start() {
|
|
13614
13836
|
try {
|
|
13615
13837
|
await this.initialize();
|
|
@@ -13630,18 +13852,69 @@ class ClaudeWorker {
|
|
|
13630
13852
|
if (this.timerManager) {
|
|
13631
13853
|
this.timerManager.clearIdleTimer();
|
|
13632
13854
|
}
|
|
13855
|
+
if (this.logger) {
|
|
13856
|
+
await new Promise((resolve) => {
|
|
13857
|
+
this.logger.on("finish", resolve);
|
|
13858
|
+
this.logger.end();
|
|
13859
|
+
});
|
|
13860
|
+
}
|
|
13633
13861
|
process.exit(0);
|
|
13634
13862
|
}
|
|
13635
13863
|
}
|
|
13636
13864
|
async initialize() {
|
|
13637
13865
|
const taskId = this.options.input.taskId;
|
|
13638
13866
|
const userId = this.options.input.userId;
|
|
13639
|
-
|
|
13640
|
-
let initialCommitHash;
|
|
13641
|
-
this.logger = await this.createLogger({ type: "worker", taskId });
|
|
13867
|
+
this.logger = this.createWorkerLogger({ type: "worker", taskId });
|
|
13642
13868
|
if (this.options.input.dataEncryptionKey && this.options.secretKey) {
|
|
13643
13869
|
this.dataEncryptionKey = decryptWithEphemeralKey(decodeBase64(this.options.input.dataEncryptionKey), this.options.secretKey);
|
|
13644
13870
|
}
|
|
13871
|
+
if (this.options.input.encryptedMessage && this.dataEncryptionKey) {
|
|
13872
|
+
this.options.input.message = decryptSdkMessage(this.options.input.encryptedMessage, this.dataEncryptionKey) || void 0;
|
|
13873
|
+
}
|
|
13874
|
+
let workingDirectory = this.options.input.cwd ? this.options.input.cwd.replace(/^~/, homedir()) : process.cwd();
|
|
13875
|
+
const idleTimeoutMs = Math.max(0, this.options.idleTimeoutSecond ?? 0) * 1e3;
|
|
13876
|
+
this.timerManager = this.createIdleTimerManager(idleTimeoutMs, taskId);
|
|
13877
|
+
const workerConfig = this.createWorkerClientConfig(userId, taskId, workingDirectory);
|
|
13878
|
+
const workClient = new WorkerClient(workerConfig.config, workerConfig.handlers);
|
|
13879
|
+
await workClient.connect();
|
|
13880
|
+
workClient.sendWorkerInitializing();
|
|
13881
|
+
this.context = {
|
|
13882
|
+
credentials: this.credentials,
|
|
13883
|
+
options: this.options,
|
|
13884
|
+
workClient,
|
|
13885
|
+
workingDirectory,
|
|
13886
|
+
initialCommitHash: "",
|
|
13887
|
+
// Will be set after setupWorkspace
|
|
13888
|
+
logger: this.logger
|
|
13889
|
+
};
|
|
13890
|
+
this.coordinator = new MessageCoordinator({
|
|
13891
|
+
workerType: "claude",
|
|
13892
|
+
workClient,
|
|
13893
|
+
handlers: {
|
|
13894
|
+
onNormalMessage: async (message) => {
|
|
13895
|
+
await this.enqueueMessage(message);
|
|
13896
|
+
},
|
|
13897
|
+
onBashCommand: async (command, _originalMessage) => {
|
|
13898
|
+
await this.executeBashCommand(command);
|
|
13899
|
+
},
|
|
13900
|
+
onMergeRequest: async (_originalMessage) => {
|
|
13901
|
+
await this.executeMergeRequest();
|
|
13902
|
+
}
|
|
13903
|
+
},
|
|
13904
|
+
logger: (level, category, message) => {
|
|
13905
|
+
const validLevel = level;
|
|
13906
|
+
this.log(validLevel, category, message);
|
|
13907
|
+
}
|
|
13908
|
+
});
|
|
13909
|
+
if (!this.options.input.gitUrl) {
|
|
13910
|
+
const hasChanges = await checkUncommittedChanges(workingDirectory);
|
|
13911
|
+
if (hasChanges) {
|
|
13912
|
+
this.log("info", "GIT", "Detected uncommitted changes, asking user for action");
|
|
13913
|
+
const action = await this.askUncommittedChangesAction();
|
|
13914
|
+
await handleUncommittedChanges(workingDirectory, action);
|
|
13915
|
+
}
|
|
13916
|
+
}
|
|
13917
|
+
let initialCommitHash;
|
|
13645
13918
|
try {
|
|
13646
13919
|
const hooks = await this.loadAgentHooks();
|
|
13647
13920
|
const workspaceResult = await setupWorkspace({
|
|
@@ -13653,6 +13926,8 @@ class ClaudeWorker {
|
|
|
13653
13926
|
}, hooks);
|
|
13654
13927
|
workingDirectory = workspaceResult.workingDirectory;
|
|
13655
13928
|
initialCommitHash = workspaceResult.initialCommitHash;
|
|
13929
|
+
this.context.workingDirectory = workingDirectory;
|
|
13930
|
+
this.context.initialCommitHash = initialCommitHash;
|
|
13656
13931
|
await machine.writeInitialCommitHash(userId, taskId, initialCommitHash);
|
|
13657
13932
|
this.log("info", "GIT", `Initial commit: ${initialCommitHash}`);
|
|
13658
13933
|
this.initialCommitHashForPR = initialCommitHash;
|
|
@@ -13660,20 +13935,10 @@ class ClaudeWorker {
|
|
|
13660
13935
|
this.logGitStateResult(gitStateResult, "start");
|
|
13661
13936
|
} catch (error) {
|
|
13662
13937
|
this.log("error", "GIT", "Failed to setup workspace:", error);
|
|
13663
|
-
const basicConfig = this.createBasicWorkerConfig(userId, taskId, workingDirectory);
|
|
13664
13938
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
13665
|
-
await
|
|
13666
|
-
basicConfig,
|
|
13667
|
-
`Failed to setup workspace: ${errorMessage}`
|
|
13668
|
-
);
|
|
13939
|
+
await workClient.sendErrorMessageAndExit(`Failed to setup workspace: ${errorMessage}`);
|
|
13669
13940
|
process.exit(1);
|
|
13670
13941
|
}
|
|
13671
|
-
const idleTimeoutMs = Math.max(0, this.options.idleTimeoutSecond ?? 0) * 1e3;
|
|
13672
|
-
this.timerManager = this.createIdleTimerManager(idleTimeoutMs, taskId);
|
|
13673
|
-
const workerConfig = this.createWorkerClientConfig(userId, taskId, workingDirectory);
|
|
13674
|
-
const workClient = new WorkerClient(workerConfig.config, workerConfig.handlers);
|
|
13675
|
-
await workClient.connect();
|
|
13676
|
-
workClient.sendWorkerInitializing();
|
|
13677
13942
|
try {
|
|
13678
13943
|
const metadata = {
|
|
13679
13944
|
cwd: workingDirectory,
|
|
@@ -13690,35 +13955,6 @@ class ClaudeWorker {
|
|
|
13690
13955
|
} catch (error) {
|
|
13691
13956
|
this.log("warn", "DAEMON", "Failed to report session:", error);
|
|
13692
13957
|
}
|
|
13693
|
-
if (this.options.input.encryptedMessage && this.dataEncryptionKey) {
|
|
13694
|
-
this.options.input.message = decryptSdkMessage(this.options.input.encryptedMessage, this.dataEncryptionKey) || void 0;
|
|
13695
|
-
}
|
|
13696
|
-
this.context = {
|
|
13697
|
-
credentials: this.credentials,
|
|
13698
|
-
options: this.options,
|
|
13699
|
-
workClient,
|
|
13700
|
-
workingDirectory,
|
|
13701
|
-
initialCommitHash,
|
|
13702
|
-
logger: this.logger
|
|
13703
|
-
};
|
|
13704
|
-
this.coordinator = new MessageCoordinator({
|
|
13705
|
-
workerType: "claude",
|
|
13706
|
-
handlers: {
|
|
13707
|
-
onNormalMessage: async (message) => {
|
|
13708
|
-
await this.enqueueMessage(message);
|
|
13709
|
-
},
|
|
13710
|
-
onBashCommand: async (command, _originalMessage) => {
|
|
13711
|
-
await this.executeBashCommand(command);
|
|
13712
|
-
},
|
|
13713
|
-
onMergeRequest: async (_originalMessage) => {
|
|
13714
|
-
await this.executeMergeRequest();
|
|
13715
|
-
}
|
|
13716
|
-
},
|
|
13717
|
-
logger: (level, category, message) => {
|
|
13718
|
-
const validLevel = level;
|
|
13719
|
-
this.log(validLevel, category, message);
|
|
13720
|
-
}
|
|
13721
|
-
});
|
|
13722
13958
|
}
|
|
13723
13959
|
async handleEvent() {
|
|
13724
13960
|
if (this.options.input.message) {
|
|
@@ -13736,15 +13972,30 @@ class ClaudeWorker {
|
|
|
13736
13972
|
}
|
|
13737
13973
|
const hasChanges = await hasUncommittedChanges(this.context.workingDirectory);
|
|
13738
13974
|
if (hasChanges) {
|
|
13739
|
-
await autoCommit(this.context.workingDirectory, "
|
|
13975
|
+
await autoCommit(this.context.workingDirectory, "Checkpoint for PR generation");
|
|
13740
13976
|
this.log("info", "MERGE", "Auto-committed changes");
|
|
13741
13977
|
}
|
|
13978
|
+
const currentHash = await getCurrentCommitHash(this.context.workingDirectory);
|
|
13979
|
+
const diffStats = await getDiffStats(this.context.workingDirectory, this.initialCommitHashForPR, currentHash);
|
|
13980
|
+
if (diffStats.files.length === 0) {
|
|
13981
|
+
const errorMessage = "No changes to create PR: no files changed since task started";
|
|
13982
|
+
this.log("error", "MERGE", errorMessage);
|
|
13983
|
+
this.context.workClient.sendSystemErrorMessage(errorMessage);
|
|
13984
|
+
return;
|
|
13985
|
+
}
|
|
13986
|
+
this.log("info", "MERGE", `Found ${diffStats.files.length} files changed`);
|
|
13742
13987
|
const branchName = await getCurrentBranch(this.context.workingDirectory);
|
|
13743
13988
|
this.log("info", "MERGE", `Pushing branch ${branchName} to remote`);
|
|
13744
13989
|
await gitPush(this.context.workingDirectory, branchName, false);
|
|
13745
13990
|
this.log("info", "MERGE", "Successfully pushed branch to remote");
|
|
13991
|
+
const prPrompt = buildPRPrompt(
|
|
13992
|
+
{ initialCommitHash: this.initialCommitHashForPR, currentCommitHash: currentHash, branchName },
|
|
13993
|
+
this.loadedAgentConfig?.customPRPromptTemplate ? {
|
|
13994
|
+
customTemplate: this.loadedAgentConfig.customPRPromptTemplate,
|
|
13995
|
+
mode: this.loadedAgentConfig.prPromptMode
|
|
13996
|
+
} : void 0
|
|
13997
|
+
);
|
|
13746
13998
|
this.inMergeRequest = true;
|
|
13747
|
-
const prPrompt = await this.buildCreatePRPrompt();
|
|
13748
13999
|
await this.enqueueMessage({
|
|
13749
14000
|
type: "user",
|
|
13750
14001
|
message: {
|
|
@@ -13773,53 +14024,8 @@ class ClaudeWorker {
|
|
|
13773
14024
|
}
|
|
13774
14025
|
);
|
|
13775
14026
|
this.timerManager.startIdleTimer();
|
|
13776
|
-
this.context.workClient.sendWorkerReady();
|
|
13777
14027
|
this.log("info", "BASH", `Worker ready after command execution (exit code: ${exitCode})`);
|
|
13778
14028
|
}
|
|
13779
|
-
async buildCreatePRPrompt() {
|
|
13780
|
-
if (!this.initialCommitHashForPR) {
|
|
13781
|
-
return 'Forbidden create PR by yourself. Must use the mcp__agentrix__create_pr tool. All the changed has been pushed. Please analyze the changes and use the mcp__agentrix__create_pr tool to create a pull request with a title and description. Use conventional commits format for the title (e.g., "feat: add new feature").';
|
|
13782
|
-
}
|
|
13783
|
-
try {
|
|
13784
|
-
const currentHash = await autoCommit(this.context.workingDirectory, "Checkpoint for PR generation");
|
|
13785
|
-
const stats = await getDiffStats(
|
|
13786
|
-
this.context.workingDirectory,
|
|
13787
|
-
this.initialCommitHashForPR,
|
|
13788
|
-
currentHash
|
|
13789
|
-
);
|
|
13790
|
-
const diff = await generateDiffPatch(
|
|
13791
|
-
this.context.workingDirectory,
|
|
13792
|
-
this.initialCommitHashForPR,
|
|
13793
|
-
currentHash
|
|
13794
|
-
);
|
|
13795
|
-
const statsText = `Files changed: ${stats.files.length}, +${stats.totalInsertions}/-${stats.totalDeletions}
|
|
13796
|
-
|
|
13797
|
-
Detailed changes:
|
|
13798
|
-
${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("\n")}`;
|
|
13799
|
-
return `All the changed has been successfully pushed to the git branch. Please create a Pull Request by analyzing the changes below.
|
|
13800
|
-
|
|
13801
|
-
Changes made:
|
|
13802
|
-
${statsText}
|
|
13803
|
-
|
|
13804
|
-
Diff (first 5000 chars):
|
|
13805
|
-
\`\`\`
|
|
13806
|
-
${diff.substring(0, 5e3)}
|
|
13807
|
-
\`\`\`
|
|
13808
|
-
|
|
13809
|
-
Requirements:
|
|
13810
|
-
- Title: Use conventional commits format (feat/fix/docs/refactor/test/chore: description), maximum 50 characters
|
|
13811
|
-
- Description: Provide a clear, detailed explanation of:
|
|
13812
|
-
* What changed (the actual modifications made)
|
|
13813
|
-
* Why these changes were necessary (the problem being solved)
|
|
13814
|
-
* Any important technical decisions or trade-offs
|
|
13815
|
-
* Impact on existing functionality
|
|
13816
|
-
|
|
13817
|
-
Please must use the mcp__agentrix__create_pr tool to create the pull request with the generated title and description.Forbidden create PR by yourself.`;
|
|
13818
|
-
} catch (error) {
|
|
13819
|
-
this.log("warn", "GIT", "Failed to generate diff for PR prompt:", error);
|
|
13820
|
-
return 'The code has been committed. Please use the mcp__agentrix__create_pr tool to create a pull request with a title and description. Use conventional commits format for the title (e.g., "feat: add new feature").';
|
|
13821
|
-
}
|
|
13822
|
-
}
|
|
13823
14029
|
async runClaude() {
|
|
13824
14030
|
this.log("info", "AGENT", `Starting Claude agent for task ${this.taskId}`);
|
|
13825
14031
|
const agentSessionId = "agentSessionId" in this.options.input ? this.options.input.agentSessionId : void 0;
|
|
@@ -13830,12 +14036,12 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13830
14036
|
);
|
|
13831
14037
|
const sdkMcpServer = this.createAgentrixMcpServer();
|
|
13832
14038
|
const mcpServers = {
|
|
13833
|
-
agentrix: sdkMcpServer
|
|
13834
|
-
...agentConfig.customMcpServers
|
|
14039
|
+
agentrix: sdkMcpServer
|
|
13835
14040
|
};
|
|
13836
14041
|
const allowedTools = [
|
|
13837
14042
|
"mcp__agentrix__change_task_title",
|
|
13838
14043
|
"mcp__agentrix__create_pr",
|
|
14044
|
+
"mcp__agentrix__ask_user",
|
|
13839
14045
|
...agentConfig.customAllowedTools
|
|
13840
14046
|
];
|
|
13841
14047
|
const messageConsumer = this.createMessageConsumer();
|
|
@@ -13845,6 +14051,7 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13845
14051
|
prompt: messageConsumer(),
|
|
13846
14052
|
options: {
|
|
13847
14053
|
stderr: (_data) => {
|
|
14054
|
+
this.log("debug", "SDK", _data);
|
|
13848
14055
|
},
|
|
13849
14056
|
model: agentConfig.customModel || this.options.input.model,
|
|
13850
14057
|
fallbackModel: agentConfig.customFallbackModel,
|
|
@@ -13855,6 +14062,7 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13855
14062
|
systemPrompt: finalSystemPrompt,
|
|
13856
14063
|
mcpServers,
|
|
13857
14064
|
allowedTools,
|
|
14065
|
+
plugins: agentConfig.customPlugins,
|
|
13858
14066
|
abortController: this.abortController,
|
|
13859
14067
|
env: this.buildEnvironmentOverrides(),
|
|
13860
14068
|
maxTurns: agentConfig.customMaxTurns ?? this.options.input.maxTurns ?? 50,
|
|
@@ -13864,18 +14072,16 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13864
14072
|
}
|
|
13865
14073
|
});
|
|
13866
14074
|
if (this.messageQueue.length > 0) {
|
|
13867
|
-
this.
|
|
13868
|
-
this.coordinator.setWorkerState("processing-sdk");
|
|
14075
|
+
this.coordinator.setWorkerState("running");
|
|
13869
14076
|
} else {
|
|
13870
14077
|
this.timerManager.startIdleTimer();
|
|
13871
|
-
this.context.workClient.sendWorkerReady();
|
|
13872
14078
|
}
|
|
13873
14079
|
for await (const message of response) {
|
|
13874
14080
|
this.timerManager.clearIdleTimer();
|
|
13875
14081
|
this.context.logger.debug(`sdk message: ${JSON.stringify(message)}`);
|
|
13876
14082
|
if (message.type === "system" && message.subtype === "init") {
|
|
13877
14083
|
this.context.workClient.sendUpdateTaskAgentSessionId(message.session_id);
|
|
13878
|
-
this.
|
|
14084
|
+
this.coordinator.setWorkerState("running");
|
|
13879
14085
|
continue;
|
|
13880
14086
|
}
|
|
13881
14087
|
const filteredMessage = this.filterMessages(message);
|
|
@@ -13885,9 +14091,8 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13885
14091
|
if (message.type === "result") {
|
|
13886
14092
|
this.coordinator.setWorkerState("idle");
|
|
13887
14093
|
this.timerManager.startIdleTimer();
|
|
13888
|
-
this.context.workClient.sendWorkerReady();
|
|
13889
14094
|
} else {
|
|
13890
|
-
this.coordinator.setWorkerState("
|
|
14095
|
+
this.coordinator.setWorkerState("running");
|
|
13891
14096
|
}
|
|
13892
14097
|
}
|
|
13893
14098
|
this.log("info", "AGENT", `Claude agent finished for task ${this.taskId}`);
|
|
@@ -14047,33 +14252,117 @@ Type: ${mimeType}`
|
|
|
14047
14252
|
}
|
|
14048
14253
|
createPermissionHandler() {
|
|
14049
14254
|
return async (toolName, input) => {
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
}
|
|
14255
|
+
if (this.grantedPermissions.has(toolName)) {
|
|
14256
|
+
this.log("info", "PERMISSION", `Tool "${toolName}" already granted, skipping`);
|
|
14257
|
+
return { behavior: "allow", updatedInput: input };
|
|
14258
|
+
}
|
|
14259
|
+
const pending = this.pendingPermissions.get(toolName);
|
|
14260
|
+
if (pending) {
|
|
14261
|
+
this.log("info", "PERMISSION", `Tool "${toolName}" has pending request, waiting...`);
|
|
14262
|
+
const decision = await pending;
|
|
14263
|
+
if (decision === "allow") {
|
|
14264
|
+
return { behavior: "allow", updatedInput: input };
|
|
14265
|
+
} else {
|
|
14266
|
+
return { behavior: "deny", message: "Permission denied by user" };
|
|
14062
14267
|
}
|
|
14063
|
-
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14268
|
+
}
|
|
14269
|
+
this.log("info", "PERMISSION", `Requesting permission for "${toolName}"`);
|
|
14270
|
+
let resolveDecision;
|
|
14271
|
+
const permissionPromise = new Promise((resolve) => {
|
|
14272
|
+
resolveDecision = resolve;
|
|
14273
|
+
});
|
|
14274
|
+
this.pendingPermissions.set(toolName, permissionPromise);
|
|
14275
|
+
try {
|
|
14276
|
+
const decision = await this.requestToolPermission(toolName);
|
|
14277
|
+
resolveDecision(decision);
|
|
14278
|
+
if (decision === "allow") {
|
|
14279
|
+
this.grantedPermissions.add(toolName);
|
|
14280
|
+
return { behavior: "allow", updatedInput: input };
|
|
14281
|
+
} else {
|
|
14282
|
+
return { behavior: "deny", message: "Permission denied by user" };
|
|
14283
|
+
}
|
|
14284
|
+
} catch (error) {
|
|
14285
|
+
resolveDecision("deny");
|
|
14286
|
+
return { behavior: "deny", message: "Permission request failed" };
|
|
14287
|
+
} finally {
|
|
14288
|
+
this.pendingPermissions.delete(toolName);
|
|
14074
14289
|
}
|
|
14075
14290
|
};
|
|
14076
14291
|
}
|
|
14292
|
+
async requestToolPermission(toolName) {
|
|
14293
|
+
const questions = [{
|
|
14294
|
+
question: `Tool "${toolName}" is requesting permission to execute. Allow this operation?`,
|
|
14295
|
+
header: "Permission",
|
|
14296
|
+
multiSelect: false,
|
|
14297
|
+
options: [
|
|
14298
|
+
{ label: "Allow", description: "Allow this tool to execute" },
|
|
14299
|
+
{ label: "Deny", description: "Deny this tool execution" }
|
|
14300
|
+
]
|
|
14301
|
+
}];
|
|
14302
|
+
try {
|
|
14303
|
+
const response = await this.askUser(questions);
|
|
14304
|
+
const answer = response.answers[0];
|
|
14305
|
+
return answer === "Allow" ? "allow" : "deny";
|
|
14306
|
+
} catch (error) {
|
|
14307
|
+
this.log("warn", "PERMISSION", `Permission request failed: ${error}`);
|
|
14308
|
+
return "deny";
|
|
14309
|
+
}
|
|
14310
|
+
}
|
|
14311
|
+
/**
|
|
14312
|
+
* Ask user questions and wait for response
|
|
14313
|
+
* Sends ask_user message via task-message and waits for ask_user_response
|
|
14314
|
+
* @param questions - Array of questions (1-4)
|
|
14315
|
+
* @returns Promise resolving to user's response
|
|
14316
|
+
*/
|
|
14317
|
+
async askUser(questions) {
|
|
14318
|
+
const eventId = this.context.workClient.sendAskUser(questions);
|
|
14319
|
+
const timeoutMs = 3e5;
|
|
14320
|
+
return new Promise((resolve, reject) => {
|
|
14321
|
+
const timeout = setTimeout(() => {
|
|
14322
|
+
this.askUserAwaiter.delete(eventId);
|
|
14323
|
+
reject(new Error("Ask user request timed out"));
|
|
14324
|
+
}, timeoutMs);
|
|
14325
|
+
this.askUserAwaiter.set(eventId, (response) => {
|
|
14326
|
+
clearTimeout(timeout);
|
|
14327
|
+
resolve(response);
|
|
14328
|
+
});
|
|
14329
|
+
});
|
|
14330
|
+
}
|
|
14331
|
+
/**
|
|
14332
|
+
* Ask user how to handle uncommitted changes
|
|
14333
|
+
* @returns The action to take: ignore, commit, stash, or abort
|
|
14334
|
+
*/
|
|
14335
|
+
async askUncommittedChangesAction() {
|
|
14336
|
+
const questions = [{
|
|
14337
|
+
question: "Uncommitted changes detected in the working directory. How would you like to proceed?",
|
|
14338
|
+
header: "Git Status",
|
|
14339
|
+
multiSelect: false,
|
|
14340
|
+
options: [
|
|
14341
|
+
{ label: "Ignore", description: "Keep changes and continue with the task" },
|
|
14342
|
+
{ label: "Commit", description: "Commit current changes before starting" },
|
|
14343
|
+
{ label: "Stash", description: "Stash changes (git stash) before starting" },
|
|
14344
|
+
{ label: "Abort", description: "Cancel the task, do nothing" }
|
|
14345
|
+
]
|
|
14346
|
+
}];
|
|
14347
|
+
try {
|
|
14348
|
+
const response = await this.askUser(questions);
|
|
14349
|
+
const answer = response.answers[0];
|
|
14350
|
+
if (answer.startsWith("other:")) {
|
|
14351
|
+
this.log("info", "GIT", `User provided custom input: ${answer}, defaulting to Abort`);
|
|
14352
|
+
return "Abort";
|
|
14353
|
+
}
|
|
14354
|
+
const labelToAction = {
|
|
14355
|
+
"Ignore": "Ignore",
|
|
14356
|
+
"Commit": "Commit",
|
|
14357
|
+
"Stash": "Stash",
|
|
14358
|
+
"Abort": "Abort"
|
|
14359
|
+
};
|
|
14360
|
+
return labelToAction[answer] || "Abort";
|
|
14361
|
+
} catch (error) {
|
|
14362
|
+
this.log("warn", "GIT", `Failed to get user response for uncommitted changes: ${error}`);
|
|
14363
|
+
return "Abort";
|
|
14364
|
+
}
|
|
14365
|
+
}
|
|
14077
14366
|
createAgentrixMcpServer() {
|
|
14078
14367
|
return createSdkMcpServer({
|
|
14079
14368
|
name: "agentrix",
|
|
@@ -14134,6 +14423,50 @@ URL: ${result.pullRequestUrl}`
|
|
|
14134
14423
|
};
|
|
14135
14424
|
}
|
|
14136
14425
|
}
|
|
14426
|
+
),
|
|
14427
|
+
tool(
|
|
14428
|
+
"ask_user",
|
|
14429
|
+
'Ask the user questions when you need clarification or user input. Supports 1-4 questions with 2-4 options each. Use this when you need user decisions or additional information. An "Other" option with free text input is automatically added.',
|
|
14430
|
+
{
|
|
14431
|
+
questions: z.array(z.object({
|
|
14432
|
+
question: z.string().describe("The complete question to ask the user"),
|
|
14433
|
+
header: z.string().max(12).describe("Short label displayed as a chip/tag (max 12 chars)"),
|
|
14434
|
+
multiSelect: z.boolean().describe("Set to true to allow multiple option selections"),
|
|
14435
|
+
options: z.array(z.object({
|
|
14436
|
+
label: z.string().describe("Option label (1-5 words)"),
|
|
14437
|
+
description: z.string().describe("Explanation of what this option means")
|
|
14438
|
+
})).min(2).max(4).describe("Available choices (2-4 options)")
|
|
14439
|
+
})).min(1).max(4).describe("Questions to ask (1-4 questions)")
|
|
14440
|
+
},
|
|
14441
|
+
async (args) => {
|
|
14442
|
+
try {
|
|
14443
|
+
const questionsWithOther = args.questions.map((q) => ({
|
|
14444
|
+
...q,
|
|
14445
|
+
options: [...q.options, { label: "Other", description: "" }]
|
|
14446
|
+
}));
|
|
14447
|
+
const result = await this.askUser(questionsWithOther);
|
|
14448
|
+
const answerText = result.answers.map((answer) => {
|
|
14449
|
+
if (answer.startsWith("other:")) {
|
|
14450
|
+
return `Other: "${answer.slice(6)}"`;
|
|
14451
|
+
}
|
|
14452
|
+
return answer;
|
|
14453
|
+
}).join("\n");
|
|
14454
|
+
return {
|
|
14455
|
+
content: [{ type: "text", text: `User answers:
|
|
14456
|
+
${answerText}` }]
|
|
14457
|
+
};
|
|
14458
|
+
} catch (error) {
|
|
14459
|
+
this.log("error", "ASK_USER", "Failed to get user response:", error);
|
|
14460
|
+
return {
|
|
14461
|
+
content: [
|
|
14462
|
+
{
|
|
14463
|
+
type: "text",
|
|
14464
|
+
text: `Failed to get user response: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
14465
|
+
}
|
|
14466
|
+
]
|
|
14467
|
+
};
|
|
14468
|
+
}
|
|
14469
|
+
}
|
|
14137
14470
|
)
|
|
14138
14471
|
]
|
|
14139
14472
|
});
|
|
@@ -14149,7 +14482,7 @@ URL: ${result.pullRequestUrl}`
|
|
|
14149
14482
|
} catch (error) {
|
|
14150
14483
|
this.log("warn", "GIT", "Failed to handle git state on worker stop:", error);
|
|
14151
14484
|
}
|
|
14152
|
-
this.
|
|
14485
|
+
this.coordinator.setWorkerState("idle");
|
|
14153
14486
|
}
|
|
14154
14487
|
filterMessages(message) {
|
|
14155
14488
|
const msg = message;
|
|
@@ -14190,38 +14523,45 @@ URL: ${result.pullRequestUrl}`
|
|
|
14190
14523
|
return this.createDefaultAgentConfig();
|
|
14191
14524
|
}
|
|
14192
14525
|
const claudeConfig = agentConfig.claude;
|
|
14193
|
-
const
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14526
|
+
const customPlugins = claudeConfig.plugins.map((path) => ({
|
|
14527
|
+
type: "local",
|
|
14528
|
+
path
|
|
14529
|
+
}));
|
|
14530
|
+
this.log("info", "AGENT", `Agent ${this.options.input.agentId} loaded successfully (${customPlugins.length} plugins)`);
|
|
14531
|
+
const config = {
|
|
14198
14532
|
customSystemPrompt: claudeConfig.systemPrompt,
|
|
14199
|
-
// Loaded string content
|
|
14200
14533
|
customModel: claudeConfig.config.model,
|
|
14201
14534
|
customFallbackModel: claudeConfig.config.fallbackModel,
|
|
14202
14535
|
customMaxTurns: claudeConfig.config.maxTurns,
|
|
14203
14536
|
customExtraArgs: claudeConfig.config.extraArgs,
|
|
14204
14537
|
customPermissionMode: claudeConfig.config.settings?.permissionMode,
|
|
14205
|
-
customMcpServers,
|
|
14206
14538
|
customAllowedTools: claudeConfig.config.settings?.allowedTools || [],
|
|
14539
|
+
customPlugins,
|
|
14207
14540
|
systemPromptMode: claudeConfig.config.systemPrompt?.mode ?? "append",
|
|
14208
|
-
hooks: this.loadedHooks
|
|
14209
|
-
|
|
14541
|
+
hooks: this.loadedHooks,
|
|
14542
|
+
customPRPromptTemplate: claudeConfig.prPromptTemplate,
|
|
14543
|
+
prPromptMode: claudeConfig.config.pullRequestPrompt?.mode ?? "append"
|
|
14210
14544
|
};
|
|
14545
|
+
this.loadedAgentConfig = config;
|
|
14546
|
+
return config;
|
|
14211
14547
|
} catch (error) {
|
|
14212
14548
|
this.log("error", "AGENT", `Failed to load agent: ${error instanceof Error ? error.message : String(error)}`);
|
|
14213
14549
|
return this.createDefaultAgentConfig();
|
|
14214
14550
|
}
|
|
14215
14551
|
}
|
|
14216
14552
|
createDefaultAgentConfig() {
|
|
14217
|
-
|
|
14553
|
+
const config = {
|
|
14218
14554
|
customSystemPrompt: void 0,
|
|
14219
14555
|
customModel: void 0,
|
|
14220
14556
|
customMaxTurns: void 0,
|
|
14221
|
-
customMcpServers: {},
|
|
14222
14557
|
customAllowedTools: [],
|
|
14223
|
-
|
|
14558
|
+
customPlugins: [],
|
|
14559
|
+
systemPromptMode: "append",
|
|
14560
|
+
customPRPromptTemplate: void 0,
|
|
14561
|
+
prPromptMode: "append"
|
|
14224
14562
|
};
|
|
14563
|
+
this.loadedAgentConfig = config;
|
|
14564
|
+
return config;
|
|
14225
14565
|
}
|
|
14226
14566
|
buildEnvironmentOverrides() {
|
|
14227
14567
|
return {
|
|
@@ -14329,18 +14669,20 @@ URL: ${result.pullRequestUrl}`
|
|
|
14329
14669
|
this.timerManager.stopTask("event");
|
|
14330
14670
|
},
|
|
14331
14671
|
onTaskMessage: async (message) => {
|
|
14332
|
-
if (message
|
|
14672
|
+
if (isAskUserResponseMessage(message)) {
|
|
14673
|
+
const [eventId, awaiter] = this.askUserAwaiter.entries().next().value || [];
|
|
14674
|
+
if (eventId && awaiter) {
|
|
14675
|
+
this.askUserAwaiter.delete(eventId);
|
|
14676
|
+
awaiter(message);
|
|
14677
|
+
}
|
|
14678
|
+
this.timerManager.clearIdleTimer();
|
|
14679
|
+
return;
|
|
14680
|
+
}
|
|
14681
|
+
if ("type" in message && message.type === "user") {
|
|
14333
14682
|
const userMessage = message;
|
|
14334
14683
|
await this.coordinator.enqueue(userMessage);
|
|
14335
14684
|
this.timerManager.clearIdleTimer();
|
|
14336
14685
|
}
|
|
14337
|
-
},
|
|
14338
|
-
onPermissionResponse: async (response) => {
|
|
14339
|
-
const awaiter = this.permissionAwaiter.get(response.opCode);
|
|
14340
|
-
if (awaiter) {
|
|
14341
|
-
this.permissionAwaiter.delete(response.opCode);
|
|
14342
|
-
awaiter(response);
|
|
14343
|
-
}
|
|
14344
14686
|
}
|
|
14345
14687
|
}
|
|
14346
14688
|
};
|
|
@@ -14379,8 +14721,7 @@ URL: ${result.pullRequestUrl}`
|
|
|
14379
14721
|
this.log("info", "GIT", `Patch: ${gitStateResult.patchPath}`);
|
|
14380
14722
|
}
|
|
14381
14723
|
}
|
|
14382
|
-
|
|
14383
|
-
const { createLogger } = await import('./logger-D-ioMWe6.mjs').then(function (n) { return n.b; });
|
|
14724
|
+
createWorkerLogger(options) {
|
|
14384
14725
|
return createLogger(options);
|
|
14385
14726
|
}
|
|
14386
14727
|
log(level, category, message, ...args) {
|
|
@@ -16119,6 +16460,7 @@ class CodexWorker {
|
|
|
16119
16460
|
};
|
|
16120
16461
|
this.coordinator = new MessageCoordinator({
|
|
16121
16462
|
workerType: "codex",
|
|
16463
|
+
workClient,
|
|
16122
16464
|
handlers: {
|
|
16123
16465
|
onNormalMessage: async (message) => {
|
|
16124
16466
|
const input = await this.convertSDKMessageToCodexInput(message);
|
|
@@ -16191,7 +16533,6 @@ class CodexWorker {
|
|
|
16191
16533
|
}
|
|
16192
16534
|
);
|
|
16193
16535
|
this.timerManager.startIdleTimer();
|
|
16194
|
-
this.context.workClient.sendWorkerReady();
|
|
16195
16536
|
this.log("info", "BASH", `Worker ready after command execution (exit code: ${exitCode})`);
|
|
16196
16537
|
}
|
|
16197
16538
|
async buildCreatePRPrompt() {
|
|
@@ -16248,8 +16589,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16248
16589
|
skipGitRepoCheck: true
|
|
16249
16590
|
});
|
|
16250
16591
|
}
|
|
16251
|
-
this.
|
|
16252
|
-
this.coordinator.setWorkerState("processing-sdk");
|
|
16592
|
+
this.coordinator.setWorkerState("running");
|
|
16253
16593
|
while (!this.isStopping) {
|
|
16254
16594
|
this.log("debug", "AGENT", `Loop iteration: turnCount=${this.turnCount}, queueLength=${this.messageQueue.length}`);
|
|
16255
16595
|
let userInput = null;
|
|
@@ -16261,7 +16601,6 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16261
16601
|
await this.reportGitState("idle");
|
|
16262
16602
|
this.coordinator.setWorkerState("idle");
|
|
16263
16603
|
this.timerManager.startIdleTimer();
|
|
16264
|
-
this.context.workClient.sendWorkerReady();
|
|
16265
16604
|
this.log("info", "AGENT", "Sent worker-ready, waiting for next message");
|
|
16266
16605
|
userInput = await this.waitForNextMessage();
|
|
16267
16606
|
}
|
|
@@ -16271,7 +16610,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16271
16610
|
}
|
|
16272
16611
|
try {
|
|
16273
16612
|
this.timerManager.clearIdleTimer();
|
|
16274
|
-
this.coordinator.setWorkerState("
|
|
16613
|
+
this.coordinator.setWorkerState("running");
|
|
16275
16614
|
await this.processUserInput(userInput);
|
|
16276
16615
|
this.turnCount++;
|
|
16277
16616
|
this.log("debug", "AGENT", `Message processed, turnCount now ${this.turnCount}`);
|
|
@@ -16307,7 +16646,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16307
16646
|
const turnOptions = this.inMergeRequest ? { outputSchema: getPROutputSchema() } : {};
|
|
16308
16647
|
this.log("debug", "AGENT", "Calling thread.runStreamed");
|
|
16309
16648
|
const { events } = await this.thread.runStreamed(input, turnOptions);
|
|
16310
|
-
this.
|
|
16649
|
+
this.coordinator.setWorkerState("running");
|
|
16311
16650
|
this.log("debug", "AGENT", "Starting to process events");
|
|
16312
16651
|
for await (const event of events) {
|
|
16313
16652
|
if (this.isStopping) {
|
|
@@ -16598,7 +16937,7 @@ Type: ${mimeType}`
|
|
|
16598
16937
|
}
|
|
16599
16938
|
async handleStopHook() {
|
|
16600
16939
|
await this.reportGitState("stop");
|
|
16601
|
-
this.
|
|
16940
|
+
this.coordinator.setWorkerState("idle");
|
|
16602
16941
|
}
|
|
16603
16942
|
async loadAgentConfiguration() {
|
|
16604
16943
|
if (!this.options.input.agentId || this.options.input.agentId === "default") {
|
|
@@ -16690,14 +17029,15 @@ Type: ${mimeType}`
|
|
|
16690
17029
|
this.timerManager.stopTask("event");
|
|
16691
17030
|
},
|
|
16692
17031
|
onTaskMessage: async (message) => {
|
|
16693
|
-
if (message
|
|
17032
|
+
if (isAskUserResponseMessage(message)) {
|
|
17033
|
+
this.log("debug", "AGENT", "Received ask_user_response (not used by Codex)");
|
|
17034
|
+
return;
|
|
17035
|
+
}
|
|
17036
|
+
if ("type" in message && message.type === "user") {
|
|
16694
17037
|
const userMessage = message;
|
|
16695
17038
|
await this.coordinator.enqueue(userMessage);
|
|
16696
17039
|
this.timerManager.clearIdleTimer();
|
|
16697
17040
|
}
|
|
16698
|
-
},
|
|
16699
|
-
onPermissionResponse: async (response) => {
|
|
16700
|
-
this.log("debug", "AGENT", `Permission response received: ${response.behavior}`);
|
|
16701
17041
|
}
|
|
16702
17042
|
}
|
|
16703
17043
|
};
|
|
@@ -17052,7 +17392,7 @@ cli.command("upgrade", "Upgrade CLI to the latest version", {}, async (argv) =>
|
|
|
17052
17392
|
}
|
|
17053
17393
|
}
|
|
17054
17394
|
try {
|
|
17055
|
-
const { version } = await import('./logger-
|
|
17395
|
+
const { version } = await import('./logger-BRmB311i.mjs').then(function (n) { return n._; });
|
|
17056
17396
|
console.log(chalk.green(`
|
|
17057
17397
|
\u2713 Now running version: ${version}`));
|
|
17058
17398
|
} catch {
|
|
@@ -17234,7 +17574,7 @@ cli.command(
|
|
|
17234
17574
|
},
|
|
17235
17575
|
async (argv) => {
|
|
17236
17576
|
try {
|
|
17237
|
-
const { testCommand } = await import('./index-
|
|
17577
|
+
const { testCommand } = await import('./index-DoalEmuX.mjs');
|
|
17238
17578
|
await testCommand(argv);
|
|
17239
17579
|
} catch (error) {
|
|
17240
17580
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Test mode failed");
|