@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
|
@@ -6,8 +6,7 @@ var chalk = require('chalk');
|
|
|
6
6
|
var shared = require('@agentrix/shared');
|
|
7
7
|
var node_crypto = require('node:crypto');
|
|
8
8
|
var axios = require('axios');
|
|
9
|
-
var _package = require('./logger
|
|
10
|
-
var fs$1 = require('node:fs');
|
|
9
|
+
var _package = require('./logger-DFD0a_cW.cjs');
|
|
11
10
|
var node_readline = require('node:readline');
|
|
12
11
|
var fs = require('fs');
|
|
13
12
|
var path = require('path');
|
|
@@ -15,10 +14,13 @@ var os = require('node:os');
|
|
|
15
14
|
var open = require('open');
|
|
16
15
|
var socket_ioClient = require('socket.io-client');
|
|
17
16
|
var node_events = require('node:events');
|
|
17
|
+
var fs$1 = require('node:fs');
|
|
18
18
|
var path$1 = require('node:path');
|
|
19
19
|
var child_process = require('child_process');
|
|
20
20
|
var psList = require('ps-list');
|
|
21
21
|
var spawn = require('cross-spawn');
|
|
22
|
+
var sandboxRuntime = require('@xmz-ai/sandbox-runtime');
|
|
23
|
+
var platform_js = require('@xmz-ai/sandbox-runtime/dist/utils/platform.js');
|
|
22
24
|
var fastify = require('fastify');
|
|
23
25
|
var zod = require('zod');
|
|
24
26
|
var fastifyTypeProviderZod = require('fastify-type-provider-zod');
|
|
@@ -64,22 +66,12 @@ async function daemonPost(path, body) {
|
|
|
64
66
|
error: errorMessage
|
|
65
67
|
};
|
|
66
68
|
}
|
|
67
|
-
try {
|
|
68
|
-
process.kill(state.pid, 0);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
const errorMessage = "Daemon is not running, file is stale";
|
|
71
|
-
_package.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
72
|
-
return {
|
|
73
|
-
error: errorMessage
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
69
|
try {
|
|
77
70
|
const timeout = process.env.AGENTRIX_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.AGENTRIX_DAEMON_HTTP_TIMEOUT) : 1e4;
|
|
78
|
-
const response = await fetch(`http://
|
|
71
|
+
const response = await fetch(`http://agentrix-local.xmz.ai:${state.port}${path}`, {
|
|
79
72
|
method: "POST",
|
|
80
73
|
headers: { "Content-Type": "application/json" },
|
|
81
74
|
body: JSON.stringify(body || {}),
|
|
82
|
-
// Mostly increased for stress test
|
|
83
75
|
signal: AbortSignal.timeout(timeout)
|
|
84
76
|
});
|
|
85
77
|
if (!response.ok) {
|
|
@@ -225,11 +217,8 @@ async function handleAuthLogout() {
|
|
|
225
217
|
console.log(chalk.gray("Stopped daemon"));
|
|
226
218
|
} catch {
|
|
227
219
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
fs$1.rmSync(paths.rootDir, { recursive: true, force: true });
|
|
231
|
-
console.log(chalk.gray(`Removed agentrix home directory`));
|
|
232
|
-
}
|
|
220
|
+
await _package.machine.clearCredentials();
|
|
221
|
+
console.log(chalk.gray(`Removed credentials`));
|
|
233
222
|
console.log(chalk.green("\u2713 Successfully logged out"));
|
|
234
223
|
} catch (error) {
|
|
235
224
|
throw new Error(`Failed to logout: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -11885,7 +11874,7 @@ async function findAllAgentrixProcesses() {
|
|
|
11885
11874
|
async function findRunawayAgentrixProcesses() {
|
|
11886
11875
|
const allProcesses = await findAllAgentrixProcesses();
|
|
11887
11876
|
return allProcesses.filter(
|
|
11888
|
-
(p) => p.pid !== process.pid && (p.type === "daemon" || p.type === "worker")
|
|
11877
|
+
(p) => p.pid !== process.pid && (p.type === "daemon" || p.type === "worker" || p.type === "upgrade-daemon")
|
|
11889
11878
|
).map((p) => ({ pid: p.pid, command: p.command }));
|
|
11890
11879
|
}
|
|
11891
11880
|
async function killRunawayAgentrixProcesses() {
|
|
@@ -11987,6 +11976,27 @@ async function runDoctorCommand(filter) {
|
|
|
11987
11976
|
} catch (error) {
|
|
11988
11977
|
console.log(chalk.red("\u274C Error reading credentials"));
|
|
11989
11978
|
}
|
|
11979
|
+
console.log(chalk.bold("\n\u{1F512} Sandbox Dependencies"));
|
|
11980
|
+
const platform = platform_js.getPlatform();
|
|
11981
|
+
if (sandboxRuntime.isSupportedPlatform(platform)) {
|
|
11982
|
+
console.log(`Platform: ${chalk.green(platform)} (supported)`);
|
|
11983
|
+
const depsOk = sandboxRuntime.checkSandboxDependencies();
|
|
11984
|
+
if (depsOk) {
|
|
11985
|
+
console.log(chalk.green("\u2713 All sandbox dependencies available"));
|
|
11986
|
+
} else {
|
|
11987
|
+
console.log(chalk.yellow("\u26A0\uFE0F Some sandbox dependencies missing"));
|
|
11988
|
+
if (platform === "linux") {
|
|
11989
|
+
console.log(chalk.gray(" Required: bubblewrap, socat"));
|
|
11990
|
+
console.log(chalk.gray(" Install: sudo apt install bubblewrap socat"));
|
|
11991
|
+
} else if (platform === "macos") {
|
|
11992
|
+
console.log(chalk.gray(" Required: ripgrep"));
|
|
11993
|
+
console.log(chalk.gray(" Install: brew install ripgrep"));
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
} else {
|
|
11997
|
+
console.log(`Platform: ${chalk.yellow(platform)} (not supported)`);
|
|
11998
|
+
console.log(chalk.gray(" \u26A0\uFE0F Sandbox will be disabled"));
|
|
11999
|
+
}
|
|
11990
12000
|
}
|
|
11991
12001
|
console.log(chalk.bold("\n\u{1F916} Daemon Status"));
|
|
11992
12002
|
try {
|
|
@@ -12211,9 +12221,11 @@ function createPromiseWithTimeout(options) {
|
|
|
12211
12221
|
class TaskWorkerManager {
|
|
12212
12222
|
pidToTrackedSession;
|
|
12213
12223
|
pidToAwaiter;
|
|
12214
|
-
|
|
12224
|
+
sandboxPool;
|
|
12225
|
+
constructor(sandboxPool) {
|
|
12215
12226
|
this.pidToTrackedSession = /* @__PURE__ */ new Map();
|
|
12216
12227
|
this.pidToAwaiter = /* @__PURE__ */ new Map();
|
|
12228
|
+
this.sandboxPool = sandboxPool || null;
|
|
12217
12229
|
}
|
|
12218
12230
|
getCurrentSessions() {
|
|
12219
12231
|
return Array.from(this.pidToTrackedSession.values());
|
|
@@ -12254,9 +12266,15 @@ class TaskWorkerManager {
|
|
|
12254
12266
|
this.pidToTrackedSession.set(workerProcess.pid, tracked);
|
|
12255
12267
|
workerProcess.on("exit", (code, signal) => {
|
|
12256
12268
|
this.pidToTrackedSession.delete(workerProcess.pid);
|
|
12269
|
+
if (this.sandboxPool) {
|
|
12270
|
+
this.sandboxPool.disposeWorkerSandbox(data.taskId);
|
|
12271
|
+
}
|
|
12257
12272
|
});
|
|
12258
12273
|
workerProcess.on("error", (error) => {
|
|
12259
12274
|
this.pidToTrackedSession.delete(workerProcess.pid);
|
|
12275
|
+
if (this.sandboxPool) {
|
|
12276
|
+
this.sandboxPool.disposeWorkerSandbox(data.taskId);
|
|
12277
|
+
}
|
|
12260
12278
|
});
|
|
12261
12279
|
}
|
|
12262
12280
|
async startWorker(options) {
|
|
@@ -12281,14 +12299,53 @@ class TaskWorkerManager {
|
|
|
12281
12299
|
"--idle-timeout",
|
|
12282
12300
|
"120"
|
|
12283
12301
|
];
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12302
|
+
let workerProcess;
|
|
12303
|
+
if (this.sandboxPool?.isEnabled()) {
|
|
12304
|
+
try {
|
|
12305
|
+
const sandbox = await this.sandboxPool.createWorkerSandbox(
|
|
12306
|
+
options.taskId,
|
|
12307
|
+
options.userId,
|
|
12308
|
+
cwd
|
|
12309
|
+
);
|
|
12310
|
+
if (!sandbox) {
|
|
12311
|
+
throw new Error("Failed to create sandbox instance");
|
|
12312
|
+
}
|
|
12313
|
+
const { projectPath } = await Promise.resolve().then(function () { return require('./logger-DFD0a_cW.cjs'); }).then(function (n) { return n.machine$1; });
|
|
12314
|
+
const { join } = await import('path');
|
|
12315
|
+
const entrypoint = join(projectPath(), "dist", "index.mjs");
|
|
12316
|
+
const nodeArgs = ["--no-warnings", "--no-deprecation", entrypoint, ...args];
|
|
12317
|
+
const originalCommand = `"${process.execPath}" ${nodeArgs.map((a) => `"${a}"`).join(" ")}`;
|
|
12318
|
+
const sandboxedCommand = await this.sandboxPool.wrapWorkerCommand(
|
|
12319
|
+
options.taskId,
|
|
12320
|
+
originalCommand
|
|
12321
|
+
);
|
|
12322
|
+
_package.logger.debug(`[SESSION] Sandboxed command for task ${options.taskId}: ${sandboxedCommand}`);
|
|
12323
|
+
workerProcess = child_process.spawn(sandboxedCommand, {
|
|
12324
|
+
shell: true,
|
|
12325
|
+
cwd,
|
|
12326
|
+
detached: true,
|
|
12327
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12328
|
+
env: {
|
|
12329
|
+
...process.env
|
|
12330
|
+
// Environment variables controlled by SandboxManager
|
|
12331
|
+
}
|
|
12332
|
+
});
|
|
12333
|
+
_package.logger.info(`[SESSION] Worker started with sandbox, PID: ${workerProcess.pid}`);
|
|
12334
|
+
} catch (error) {
|
|
12335
|
+
_package.logger.error(`[SESSION] Failed to setup sandbox for task ${options.taskId}:`, error);
|
|
12336
|
+
ack.status = "failed";
|
|
12337
|
+
ack.message = `Sandbox setup failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
12338
|
+
return ack;
|
|
12290
12339
|
}
|
|
12291
|
-
}
|
|
12340
|
+
} else {
|
|
12341
|
+
workerProcess = spawnAgentrixCLI(args, {
|
|
12342
|
+
cwd,
|
|
12343
|
+
detached: true,
|
|
12344
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12345
|
+
env: { ...process.env }
|
|
12346
|
+
});
|
|
12347
|
+
_package.logger.info(`[SESSION] Worker started without sandbox, PID: ${workerProcess.pid}`);
|
|
12348
|
+
}
|
|
12292
12349
|
if (process.env.DEBUG) {
|
|
12293
12350
|
workerProcess.stdout?.on("data", (data) => {
|
|
12294
12351
|
_package.logger.debug(`[Daemon] worker stdout: ${data.toString()}`);
|
|
@@ -12407,6 +12464,114 @@ function setupGracefulShutdown(options) {
|
|
|
12407
12464
|
};
|
|
12408
12465
|
}
|
|
12409
12466
|
|
|
12467
|
+
class SandboxPool {
|
|
12468
|
+
networkManager = null;
|
|
12469
|
+
workerSandboxes = /* @__PURE__ */ new Map();
|
|
12470
|
+
settings = null;
|
|
12471
|
+
platform;
|
|
12472
|
+
constructor() {
|
|
12473
|
+
this.platform = platform_js.getPlatform();
|
|
12474
|
+
}
|
|
12475
|
+
async initialize(settings) {
|
|
12476
|
+
this.settings = settings;
|
|
12477
|
+
if (!settings.enabled) {
|
|
12478
|
+
_package.logger.info("[SANDBOX] Sandbox disabled via settings");
|
|
12479
|
+
return false;
|
|
12480
|
+
}
|
|
12481
|
+
if (!sandboxRuntime.isSupportedPlatform(this.platform)) {
|
|
12482
|
+
_package.logger.warn("[SANDBOX] Platform not supported, sandbox disabled");
|
|
12483
|
+
return false;
|
|
12484
|
+
}
|
|
12485
|
+
try {
|
|
12486
|
+
const apiHost = new URL(_package.machine.serverUrl).hostname;
|
|
12487
|
+
const networkConfig = {
|
|
12488
|
+
allowedDomains: [
|
|
12489
|
+
apiHost,
|
|
12490
|
+
...settings.network.allowedDomains
|
|
12491
|
+
],
|
|
12492
|
+
deniedDomains: settings.network.deniedDomains,
|
|
12493
|
+
allowLocalBinding: false
|
|
12494
|
+
};
|
|
12495
|
+
this.networkManager = new sandboxRuntime.NetworkManager();
|
|
12496
|
+
await this.networkManager.initialize(networkConfig);
|
|
12497
|
+
_package.logger.info("[SANDBOX] Sandbox pool initialized successfully");
|
|
12498
|
+
return true;
|
|
12499
|
+
} catch (error) {
|
|
12500
|
+
_package.logger.error("[SANDBOX] Failed to initialize:", error);
|
|
12501
|
+
throw error;
|
|
12502
|
+
}
|
|
12503
|
+
}
|
|
12504
|
+
async createWorkerSandbox(taskId, userId, workingDirectory) {
|
|
12505
|
+
if (!this.networkManager || !this.settings?.enabled) {
|
|
12506
|
+
return null;
|
|
12507
|
+
}
|
|
12508
|
+
try {
|
|
12509
|
+
const taskDir = _package.machine.resolveTaskDir(userId, taskId);
|
|
12510
|
+
const logsDir = _package.machine.getStatePaths().logsDir;
|
|
12511
|
+
const baseFilesystem = this.settings.filesystem || {};
|
|
12512
|
+
const baseEnv = this.settings.env || {};
|
|
12513
|
+
const filesystemConfig = {
|
|
12514
|
+
...baseFilesystem,
|
|
12515
|
+
allowWrite: [
|
|
12516
|
+
...baseFilesystem.allowWrite || [],
|
|
12517
|
+
taskDir,
|
|
12518
|
+
workingDirectory,
|
|
12519
|
+
logsDir
|
|
12520
|
+
]
|
|
12521
|
+
};
|
|
12522
|
+
if (this.platform === "linux" && baseFilesystem.allowRead) {
|
|
12523
|
+
filesystemConfig.allowRead = [
|
|
12524
|
+
...baseFilesystem.allowRead
|
|
12525
|
+
];
|
|
12526
|
+
}
|
|
12527
|
+
const instanceConfig = {
|
|
12528
|
+
filesystem: filesystemConfig,
|
|
12529
|
+
env: baseEnv
|
|
12530
|
+
};
|
|
12531
|
+
const sandbox = new sandboxRuntime.SandboxManager(this.networkManager, instanceConfig);
|
|
12532
|
+
this.workerSandboxes.set(taskId, sandbox);
|
|
12533
|
+
_package.logger.info(`[SANDBOX] Created sandbox for task ${taskId}`);
|
|
12534
|
+
return sandbox;
|
|
12535
|
+
} catch (error) {
|
|
12536
|
+
_package.logger.error(`[SANDBOX] Failed to create sandbox for task ${taskId}:`, error);
|
|
12537
|
+
return null;
|
|
12538
|
+
}
|
|
12539
|
+
}
|
|
12540
|
+
async wrapWorkerCommand(taskId, command) {
|
|
12541
|
+
const sandbox = this.workerSandboxes.get(taskId);
|
|
12542
|
+
if (!sandbox) {
|
|
12543
|
+
throw new Error(`No sandbox found for task ${taskId}`);
|
|
12544
|
+
}
|
|
12545
|
+
const wrapped = await sandbox.wrapWithSandbox(command);
|
|
12546
|
+
_package.logger.debug(`[SANDBOX] Wrapped command for task ${taskId}`);
|
|
12547
|
+
return wrapped;
|
|
12548
|
+
}
|
|
12549
|
+
disposeWorkerSandbox(taskId) {
|
|
12550
|
+
const sandbox = this.workerSandboxes.get(taskId);
|
|
12551
|
+
if (sandbox) {
|
|
12552
|
+
sandbox.dispose();
|
|
12553
|
+
this.workerSandboxes.delete(taskId);
|
|
12554
|
+
_package.logger.debug(`[SANDBOX] Disposed sandbox for task ${taskId}`);
|
|
12555
|
+
}
|
|
12556
|
+
}
|
|
12557
|
+
async shutdown() {
|
|
12558
|
+
_package.logger.info("[SANDBOX] Shutting down sandbox pool");
|
|
12559
|
+
for (const [taskId, sandbox] of this.workerSandboxes.entries()) {
|
|
12560
|
+
sandbox.dispose();
|
|
12561
|
+
_package.logger.debug(`[SANDBOX] Disposed sandbox for task ${taskId}`);
|
|
12562
|
+
}
|
|
12563
|
+
this.workerSandboxes.clear();
|
|
12564
|
+
if (this.networkManager) {
|
|
12565
|
+
await this.networkManager.shutdown();
|
|
12566
|
+
this.networkManager = null;
|
|
12567
|
+
_package.logger.info("[SANDBOX] Network manager shutdown complete");
|
|
12568
|
+
}
|
|
12569
|
+
}
|
|
12570
|
+
isEnabled() {
|
|
12571
|
+
return this.settings?.enabled === true;
|
|
12572
|
+
}
|
|
12573
|
+
}
|
|
12574
|
+
|
|
12410
12575
|
async function startDaemon() {
|
|
12411
12576
|
Object.assign(_package.logger, _package.createLogger({ type: "daemon" }));
|
|
12412
12577
|
const { requestShutdown, shutdownPromise } = setupGracefulShutdown({
|
|
@@ -12435,7 +12600,9 @@ async function startDaemon() {
|
|
|
12435
12600
|
}
|
|
12436
12601
|
const credentials = await authAndSetupMachineIfNeeded();
|
|
12437
12602
|
_package.logger.debug("[DAEMON RUN] Auth and machine setup complete");
|
|
12438
|
-
const
|
|
12603
|
+
const sandboxPool = new SandboxPool();
|
|
12604
|
+
await sandboxPool.initialize(_package.machine.getSandboxSettings());
|
|
12605
|
+
const sessionManager = new TaskWorkerManager(sandboxPool);
|
|
12439
12606
|
const { port: controlPort, stop: stopControlServer } = await startDaemonControlServer({
|
|
12440
12607
|
getChildren: () => sessionManager.getCurrentSessions(),
|
|
12441
12608
|
stopSession: (id) => sessionManager.stopSession(id),
|
|
@@ -12483,6 +12650,7 @@ async function startDaemon() {
|
|
|
12483
12650
|
const cleanupAndShutdown = async (source, errorMessage) => {
|
|
12484
12651
|
await machineClient.disconnect();
|
|
12485
12652
|
await stopControlServer();
|
|
12653
|
+
await sandboxPool.shutdown();
|
|
12486
12654
|
await cleanupDaemonState();
|
|
12487
12655
|
await stopCaffeinate();
|
|
12488
12656
|
await _package.machine.releaseDaemonLock(daemonLockHandle);
|
|
@@ -12599,14 +12767,6 @@ function createWorkerEventHandlers(context) {
|
|
|
12599
12767
|
}
|
|
12600
12768
|
}
|
|
12601
12769
|
}
|
|
12602
|
-
},
|
|
12603
|
-
"require-permission-response": async (data) => {
|
|
12604
|
-
if (data.taskId !== context.taskId) {
|
|
12605
|
-
return;
|
|
12606
|
-
}
|
|
12607
|
-
if (context.onPermissionResponse) {
|
|
12608
|
-
await context.onPermissionResponse(data);
|
|
12609
|
-
}
|
|
12610
12770
|
}
|
|
12611
12771
|
};
|
|
12612
12772
|
}
|
|
@@ -12647,7 +12807,6 @@ class WorkerClient {
|
|
|
12647
12807
|
cwd: normalizedCwd,
|
|
12648
12808
|
stopTask: options.stopTask,
|
|
12649
12809
|
onTaskMessage: options.onTaskMessage,
|
|
12650
|
-
onPermissionResponse: options.onPermissionResponse,
|
|
12651
12810
|
onGitPush: options.onGitPush,
|
|
12652
12811
|
dataEncryptionKey: config.dataEncryptionKey
|
|
12653
12812
|
};
|
|
@@ -12676,26 +12835,32 @@ class WorkerClient {
|
|
|
12676
12835
|
}
|
|
12677
12836
|
this.client.disconnect();
|
|
12678
12837
|
}
|
|
12679
|
-
sendTaskMessage(message) {
|
|
12680
|
-
const
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12838
|
+
sendTaskMessage(message, options) {
|
|
12839
|
+
const { replaceCwd = true } = options || {};
|
|
12840
|
+
let processedMessage = message;
|
|
12841
|
+
if (replaceCwd) {
|
|
12842
|
+
const cwdWithSlash = this.context.cwd;
|
|
12843
|
+
const cwdWithoutSlash = cwdWithSlash.slice(0, -1);
|
|
12844
|
+
let content = JSON.stringify(message);
|
|
12845
|
+
content = content.replaceAll(cwdWithSlash, "");
|
|
12846
|
+
content = content.replaceAll(cwdWithoutSlash, ".");
|
|
12847
|
+
processedMessage = JSON.parse(content);
|
|
12848
|
+
}
|
|
12686
12849
|
let encryptedMessage;
|
|
12687
12850
|
if (this.context.dataEncryptionKey) {
|
|
12688
|
-
encryptedMessage = shared.encryptSdkMessage(
|
|
12689
|
-
|
|
12851
|
+
encryptedMessage = shared.encryptSdkMessage(processedMessage, this.context.dataEncryptionKey);
|
|
12852
|
+
processedMessage = void 0;
|
|
12690
12853
|
}
|
|
12854
|
+
const eventId = shared.createEventId();
|
|
12691
12855
|
const payload = {
|
|
12692
|
-
eventId
|
|
12856
|
+
eventId,
|
|
12693
12857
|
taskId: this.context.taskId,
|
|
12694
12858
|
from: "worker",
|
|
12695
|
-
message:
|
|
12859
|
+
message: processedMessage,
|
|
12696
12860
|
encryptedMessage
|
|
12697
12861
|
};
|
|
12698
12862
|
this.client.send("task-message", payload);
|
|
12863
|
+
return eventId;
|
|
12699
12864
|
}
|
|
12700
12865
|
sendWorkerInitializing() {
|
|
12701
12866
|
const workerInitializingEvent = {
|
|
@@ -12706,12 +12871,13 @@ class WorkerClient {
|
|
|
12706
12871
|
};
|
|
12707
12872
|
this.client.send("worker-initializing", workerInitializingEvent);
|
|
12708
12873
|
}
|
|
12709
|
-
sendWorkerReady() {
|
|
12874
|
+
sendWorkerReady(duration) {
|
|
12710
12875
|
const workerReadyEvent = {
|
|
12711
12876
|
eventId: shared.createEventId(),
|
|
12712
12877
|
taskId: this.context.taskId,
|
|
12713
12878
|
machineId: this.context.machineId,
|
|
12714
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
12879
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12880
|
+
...duration !== void 0 && { duration }
|
|
12715
12881
|
};
|
|
12716
12882
|
this.client.send("worker-ready", workerReadyEvent);
|
|
12717
12883
|
}
|
|
@@ -12769,17 +12935,13 @@ ${errorMessage}`,
|
|
|
12769
12935
|
};
|
|
12770
12936
|
this.sendTaskMessage(systemMessage);
|
|
12771
12937
|
}
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
};
|
|
12780
|
-
this.client.send("require-permission", permissionRequest);
|
|
12781
|
-
_package.logger.info(`[AGENT] Permission requested for tool: ${toolName}`);
|
|
12782
|
-
return eventId;
|
|
12938
|
+
/**
|
|
12939
|
+
* Send ask-user message to request user input
|
|
12940
|
+
* @param questions - Array of questions (1-4)
|
|
12941
|
+
* @returns eventId for tracking the request
|
|
12942
|
+
*/
|
|
12943
|
+
sendAskUser(questions) {
|
|
12944
|
+
return this.sendTaskMessage({ type: "ask_user", questions }, { replaceCwd: false });
|
|
12783
12945
|
}
|
|
12784
12946
|
sendUpdateTaskAgentSessionId(agentSessionId) {
|
|
12785
12947
|
const updateSessionEvent = {
|
|
@@ -12834,7 +12996,6 @@ ${errorMessage}`,
|
|
|
12834
12996
|
this.client.onEvent("cancel-task", handlers["cancel-task"]);
|
|
12835
12997
|
this.client.onEvent("stop-task", handlers["stop-task"]);
|
|
12836
12998
|
this.client.onEvent("task-message", handlers["task-message"]);
|
|
12837
|
-
this.client.onEvent("require-permission-response", handlers["require-permission-response"]);
|
|
12838
12999
|
}
|
|
12839
13000
|
}
|
|
12840
13001
|
|
|
@@ -13037,6 +13198,12 @@ async function hasUncommittedChanges(dir) {
|
|
|
13037
13198
|
const status = await git.status();
|
|
13038
13199
|
return !status.isClean();
|
|
13039
13200
|
}
|
|
13201
|
+
async function gitStash(dir, message) {
|
|
13202
|
+
const git = simpleGit(dir);
|
|
13203
|
+
{
|
|
13204
|
+
await git.stash(["push"]);
|
|
13205
|
+
}
|
|
13206
|
+
}
|
|
13040
13207
|
async function getCurrentCommitHash(dir) {
|
|
13041
13208
|
const git = simpleGit(dir);
|
|
13042
13209
|
const log = await git.log({ maxCount: 1 });
|
|
@@ -13126,6 +13293,30 @@ async function executeHook(hooks, hookName, input, logger) {
|
|
|
13126
13293
|
}
|
|
13127
13294
|
}
|
|
13128
13295
|
|
|
13296
|
+
async function checkUncommittedChanges(workingDirectory) {
|
|
13297
|
+
const isRepo = await isGitRepository(workingDirectory);
|
|
13298
|
+
if (!isRepo) {
|
|
13299
|
+
return false;
|
|
13300
|
+
}
|
|
13301
|
+
return await hasUncommittedChanges(workingDirectory);
|
|
13302
|
+
}
|
|
13303
|
+
async function handleUncommittedChanges(workingDirectory, action) {
|
|
13304
|
+
switch (action) {
|
|
13305
|
+
case "Ignore":
|
|
13306
|
+
console.log("[GIT] User chose to ignore uncommitted changes");
|
|
13307
|
+
break;
|
|
13308
|
+
case "Commit":
|
|
13309
|
+
console.log("[GIT] Auto-committing uncommitted changes");
|
|
13310
|
+
await autoCommit(workingDirectory, "WIP: Auto-commit before task");
|
|
13311
|
+
break;
|
|
13312
|
+
case "Stash":
|
|
13313
|
+
console.log("[GIT] Stashing uncommitted changes");
|
|
13314
|
+
await gitStash(workingDirectory);
|
|
13315
|
+
break;
|
|
13316
|
+
case "Abort":
|
|
13317
|
+
throw new Error("Task aborted by user due to uncommitted changes");
|
|
13318
|
+
}
|
|
13319
|
+
}
|
|
13129
13320
|
function createTaskBranchName(taskId) {
|
|
13130
13321
|
return `agentrix/${taskId}`;
|
|
13131
13322
|
}
|
|
@@ -13165,17 +13356,6 @@ async function setupLocalWorkspace(workingDirectory, taskId, hooks) {
|
|
|
13165
13356
|
const isRepo = await isGitRepository(workingDirectory);
|
|
13166
13357
|
const isEmpty = isDirectoryEmpty(workingDirectory);
|
|
13167
13358
|
if (isRepo) {
|
|
13168
|
-
const hasChanges = await hasUncommittedChanges(workingDirectory);
|
|
13169
|
-
if (hasChanges) {
|
|
13170
|
-
throw new Error(
|
|
13171
|
-
`Directory ${workingDirectory} has uncommitted changes.
|
|
13172
|
-
|
|
13173
|
-
Please commit or stash your changes before starting:
|
|
13174
|
-
git add . && git commit -m "WIP"
|
|
13175
|
-
or:
|
|
13176
|
-
git stash`
|
|
13177
|
-
);
|
|
13178
|
-
}
|
|
13179
13359
|
const hasCommits = await hasAnyCommits(workingDirectory);
|
|
13180
13360
|
if (!hasCommits) {
|
|
13181
13361
|
console.log("[GIT] Repository has no commits, creating initial commit");
|
|
@@ -13303,6 +13483,29 @@ async function markCommitAsSent(userId, taskId, commitHash) {
|
|
|
13303
13483
|
await _package.machine.writeLastSentCommitHash(userId, taskId, commitHash);
|
|
13304
13484
|
}
|
|
13305
13485
|
|
|
13486
|
+
function getDefaultPRPrompt(params) {
|
|
13487
|
+
return `All changes have been pushed to branch "${params.branchName}".
|
|
13488
|
+
|
|
13489
|
+
Commit range: ${params.initialCommitHash}..${params.currentCommitHash}
|
|
13490
|
+
|
|
13491
|
+
Based on our conversation context, create a Pull Request:
|
|
13492
|
+
- Title: conventional commits format (feat/fix/docs/refactor/test/chore: description)
|
|
13493
|
+
- Description: what changed, why, and any important decisions
|
|
13494
|
+
|
|
13495
|
+
Use mcp__agentrix__create_pr tool to create the PR.`;
|
|
13496
|
+
}
|
|
13497
|
+
function applyTemplateVariables(template, params) {
|
|
13498
|
+
return template.replace(/\{\{initialCommitHash\}\}/g, params.initialCommitHash).replace(/\{\{currentCommitHash\}\}/g, params.currentCommitHash).replace(/\{\{branchName\}\}/g, params.branchName);
|
|
13499
|
+
}
|
|
13500
|
+
function buildPRPrompt(params, config) {
|
|
13501
|
+
const defaultPrompt = getDefaultPRPrompt(params);
|
|
13502
|
+
if (!config?.customTemplate) {
|
|
13503
|
+
return defaultPrompt;
|
|
13504
|
+
}
|
|
13505
|
+
const customPrompt = applyTemplateVariables(config.customTemplate, params);
|
|
13506
|
+
return config.mode === "replace" ? customPrompt : defaultPrompt + "\n\n" + customPrompt;
|
|
13507
|
+
}
|
|
13508
|
+
|
|
13306
13509
|
function executeCommandStreaming(command, cwd, callbacks, timeoutMs = 6e4) {
|
|
13307
13510
|
return new Promise((resolve) => {
|
|
13308
13511
|
const toolUseId = `shell_${require$$1.randomUUID().replace(/-/g, "")}`;
|
|
@@ -13420,6 +13623,7 @@ class MessageCoordinator {
|
|
|
13420
13623
|
currentMessageId = null;
|
|
13421
13624
|
messageIdCounter = 0;
|
|
13422
13625
|
isStopped = false;
|
|
13626
|
+
runStartTime = null;
|
|
13423
13627
|
constructor(config) {
|
|
13424
13628
|
this.config = config;
|
|
13425
13629
|
}
|
|
@@ -13539,11 +13743,11 @@ class MessageCoordinator {
|
|
|
13539
13743
|
async processBashCommand(envelope) {
|
|
13540
13744
|
this.log("info", "COORDINATOR", `Processing bash command: ${envelope.content}`);
|
|
13541
13745
|
await this.waitForState("idle");
|
|
13542
|
-
this.
|
|
13746
|
+
this.setWorkerState("running");
|
|
13543
13747
|
try {
|
|
13544
13748
|
await this.config.handlers.onBashCommand(envelope.content, envelope.originalMessage);
|
|
13545
13749
|
} finally {
|
|
13546
|
-
this.
|
|
13750
|
+
this.setWorkerState("idle");
|
|
13547
13751
|
}
|
|
13548
13752
|
}
|
|
13549
13753
|
/**
|
|
@@ -13552,11 +13756,11 @@ class MessageCoordinator {
|
|
|
13552
13756
|
async processMergeRequest(envelope) {
|
|
13553
13757
|
this.log("info", "COORDINATOR", "Processing merge-request command");
|
|
13554
13758
|
await this.waitForState("idle");
|
|
13555
|
-
this.
|
|
13759
|
+
this.setWorkerState("running");
|
|
13556
13760
|
try {
|
|
13557
13761
|
await this.config.handlers.onMergeRequest(envelope.originalMessage);
|
|
13558
13762
|
} finally {
|
|
13559
|
-
this.
|
|
13763
|
+
this.setWorkerState("idle");
|
|
13560
13764
|
}
|
|
13561
13765
|
}
|
|
13562
13766
|
async waitForState(targetState) {
|
|
@@ -13576,11 +13780,24 @@ class MessageCoordinator {
|
|
|
13576
13780
|
}
|
|
13577
13781
|
/**
|
|
13578
13782
|
* Set the worker state (called by worker when state changes)
|
|
13783
|
+
* Automatically sends WebSocket events and tracks execution duration
|
|
13579
13784
|
*/
|
|
13580
13785
|
setWorkerState(state) {
|
|
13581
|
-
if (this.workerState
|
|
13582
|
-
|
|
13583
|
-
|
|
13786
|
+
if (this.workerState === state) return;
|
|
13787
|
+
const prevState = this.workerState;
|
|
13788
|
+
this.log("info", "COORDINATOR", `Worker state: ${prevState} \u2192 ${state}`);
|
|
13789
|
+
this.workerState = state;
|
|
13790
|
+
if (state === "running" && prevState === "idle") {
|
|
13791
|
+
this.runStartTime = Date.now();
|
|
13792
|
+
this.config.workClient.sendWorkRunning();
|
|
13793
|
+
}
|
|
13794
|
+
if (state === "idle" && prevState === "running") {
|
|
13795
|
+
let duration;
|
|
13796
|
+
if (this.runStartTime) {
|
|
13797
|
+
duration = Date.now() - this.runStartTime;
|
|
13798
|
+
this.runStartTime = null;
|
|
13799
|
+
}
|
|
13800
|
+
this.config.workClient.sendWorkerReady(duration);
|
|
13584
13801
|
}
|
|
13585
13802
|
}
|
|
13586
13803
|
/**
|
|
@@ -13620,7 +13837,7 @@ class ClaudeWorker {
|
|
|
13620
13837
|
messageQueue = [];
|
|
13621
13838
|
messageResolverRef = { current: null };
|
|
13622
13839
|
abortController = new AbortController();
|
|
13623
|
-
|
|
13840
|
+
askUserAwaiter = /* @__PURE__ */ new Map();
|
|
13624
13841
|
filteredToolUseIds = /* @__PURE__ */ new Set();
|
|
13625
13842
|
timerManager;
|
|
13626
13843
|
context;
|
|
@@ -13630,6 +13847,11 @@ class ClaudeWorker {
|
|
|
13630
13847
|
dataEncryptionKey = null;
|
|
13631
13848
|
coordinator;
|
|
13632
13849
|
loadedHooks;
|
|
13850
|
+
loadedAgentConfig;
|
|
13851
|
+
// Pending permission requests: toolName -> Promise<'allow' | 'deny'> (to dedupe concurrent requests)
|
|
13852
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
13853
|
+
// Granted permissions cache: toolName -> true (to avoid repeated asks for same tool)
|
|
13854
|
+
grantedPermissions = /* @__PURE__ */ new Set();
|
|
13633
13855
|
async start() {
|
|
13634
13856
|
try {
|
|
13635
13857
|
await this.initialize();
|
|
@@ -13650,18 +13872,69 @@ class ClaudeWorker {
|
|
|
13650
13872
|
if (this.timerManager) {
|
|
13651
13873
|
this.timerManager.clearIdleTimer();
|
|
13652
13874
|
}
|
|
13875
|
+
if (this.logger) {
|
|
13876
|
+
await new Promise((resolve) => {
|
|
13877
|
+
this.logger.on("finish", resolve);
|
|
13878
|
+
this.logger.end();
|
|
13879
|
+
});
|
|
13880
|
+
}
|
|
13653
13881
|
process.exit(0);
|
|
13654
13882
|
}
|
|
13655
13883
|
}
|
|
13656
13884
|
async initialize() {
|
|
13657
13885
|
const taskId = this.options.input.taskId;
|
|
13658
13886
|
const userId = this.options.input.userId;
|
|
13659
|
-
|
|
13660
|
-
let initialCommitHash;
|
|
13661
|
-
this.logger = await this.createLogger({ type: "worker", taskId });
|
|
13887
|
+
this.logger = this.createWorkerLogger({ type: "worker", taskId });
|
|
13662
13888
|
if (this.options.input.dataEncryptionKey && this.options.secretKey) {
|
|
13663
13889
|
this.dataEncryptionKey = shared.decryptWithEphemeralKey(shared.decodeBase64(this.options.input.dataEncryptionKey), this.options.secretKey);
|
|
13664
13890
|
}
|
|
13891
|
+
if (this.options.input.encryptedMessage && this.dataEncryptionKey) {
|
|
13892
|
+
this.options.input.message = shared.decryptSdkMessage(this.options.input.encryptedMessage, this.dataEncryptionKey) || void 0;
|
|
13893
|
+
}
|
|
13894
|
+
let workingDirectory = this.options.input.cwd ? this.options.input.cwd.replace(/^~/, os.homedir()) : process.cwd();
|
|
13895
|
+
const idleTimeoutMs = Math.max(0, this.options.idleTimeoutSecond ?? 0) * 1e3;
|
|
13896
|
+
this.timerManager = this.createIdleTimerManager(idleTimeoutMs, taskId);
|
|
13897
|
+
const workerConfig = this.createWorkerClientConfig(userId, taskId, workingDirectory);
|
|
13898
|
+
const workClient = new WorkerClient(workerConfig.config, workerConfig.handlers);
|
|
13899
|
+
await workClient.connect();
|
|
13900
|
+
workClient.sendWorkerInitializing();
|
|
13901
|
+
this.context = {
|
|
13902
|
+
credentials: this.credentials,
|
|
13903
|
+
options: this.options,
|
|
13904
|
+
workClient,
|
|
13905
|
+
workingDirectory,
|
|
13906
|
+
initialCommitHash: "",
|
|
13907
|
+
// Will be set after setupWorkspace
|
|
13908
|
+
logger: this.logger
|
|
13909
|
+
};
|
|
13910
|
+
this.coordinator = new MessageCoordinator({
|
|
13911
|
+
workerType: "claude",
|
|
13912
|
+
workClient,
|
|
13913
|
+
handlers: {
|
|
13914
|
+
onNormalMessage: async (message) => {
|
|
13915
|
+
await this.enqueueMessage(message);
|
|
13916
|
+
},
|
|
13917
|
+
onBashCommand: async (command, _originalMessage) => {
|
|
13918
|
+
await this.executeBashCommand(command);
|
|
13919
|
+
},
|
|
13920
|
+
onMergeRequest: async (_originalMessage) => {
|
|
13921
|
+
await this.executeMergeRequest();
|
|
13922
|
+
}
|
|
13923
|
+
},
|
|
13924
|
+
logger: (level, category, message) => {
|
|
13925
|
+
const validLevel = level;
|
|
13926
|
+
this.log(validLevel, category, message);
|
|
13927
|
+
}
|
|
13928
|
+
});
|
|
13929
|
+
if (!this.options.input.gitUrl) {
|
|
13930
|
+
const hasChanges = await checkUncommittedChanges(workingDirectory);
|
|
13931
|
+
if (hasChanges) {
|
|
13932
|
+
this.log("info", "GIT", "Detected uncommitted changes, asking user for action");
|
|
13933
|
+
const action = await this.askUncommittedChangesAction();
|
|
13934
|
+
await handleUncommittedChanges(workingDirectory, action);
|
|
13935
|
+
}
|
|
13936
|
+
}
|
|
13937
|
+
let initialCommitHash;
|
|
13665
13938
|
try {
|
|
13666
13939
|
const hooks = await this.loadAgentHooks();
|
|
13667
13940
|
const workspaceResult = await setupWorkspace({
|
|
@@ -13673,6 +13946,8 @@ class ClaudeWorker {
|
|
|
13673
13946
|
}, hooks);
|
|
13674
13947
|
workingDirectory = workspaceResult.workingDirectory;
|
|
13675
13948
|
initialCommitHash = workspaceResult.initialCommitHash;
|
|
13949
|
+
this.context.workingDirectory = workingDirectory;
|
|
13950
|
+
this.context.initialCommitHash = initialCommitHash;
|
|
13676
13951
|
await _package.machine.writeInitialCommitHash(userId, taskId, initialCommitHash);
|
|
13677
13952
|
this.log("info", "GIT", `Initial commit: ${initialCommitHash}`);
|
|
13678
13953
|
this.initialCommitHashForPR = initialCommitHash;
|
|
@@ -13680,20 +13955,10 @@ class ClaudeWorker {
|
|
|
13680
13955
|
this.logGitStateResult(gitStateResult, "start");
|
|
13681
13956
|
} catch (error) {
|
|
13682
13957
|
this.log("error", "GIT", "Failed to setup workspace:", error);
|
|
13683
|
-
const basicConfig = this.createBasicWorkerConfig(userId, taskId, workingDirectory);
|
|
13684
13958
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
13685
|
-
await
|
|
13686
|
-
basicConfig,
|
|
13687
|
-
`Failed to setup workspace: ${errorMessage}`
|
|
13688
|
-
);
|
|
13959
|
+
await workClient.sendErrorMessageAndExit(`Failed to setup workspace: ${errorMessage}`);
|
|
13689
13960
|
process.exit(1);
|
|
13690
13961
|
}
|
|
13691
|
-
const idleTimeoutMs = Math.max(0, this.options.idleTimeoutSecond ?? 0) * 1e3;
|
|
13692
|
-
this.timerManager = this.createIdleTimerManager(idleTimeoutMs, taskId);
|
|
13693
|
-
const workerConfig = this.createWorkerClientConfig(userId, taskId, workingDirectory);
|
|
13694
|
-
const workClient = new WorkerClient(workerConfig.config, workerConfig.handlers);
|
|
13695
|
-
await workClient.connect();
|
|
13696
|
-
workClient.sendWorkerInitializing();
|
|
13697
13962
|
try {
|
|
13698
13963
|
const metadata = {
|
|
13699
13964
|
cwd: workingDirectory,
|
|
@@ -13710,35 +13975,6 @@ class ClaudeWorker {
|
|
|
13710
13975
|
} catch (error) {
|
|
13711
13976
|
this.log("warn", "DAEMON", "Failed to report session:", error);
|
|
13712
13977
|
}
|
|
13713
|
-
if (this.options.input.encryptedMessage && this.dataEncryptionKey) {
|
|
13714
|
-
this.options.input.message = shared.decryptSdkMessage(this.options.input.encryptedMessage, this.dataEncryptionKey) || void 0;
|
|
13715
|
-
}
|
|
13716
|
-
this.context = {
|
|
13717
|
-
credentials: this.credentials,
|
|
13718
|
-
options: this.options,
|
|
13719
|
-
workClient,
|
|
13720
|
-
workingDirectory,
|
|
13721
|
-
initialCommitHash,
|
|
13722
|
-
logger: this.logger
|
|
13723
|
-
};
|
|
13724
|
-
this.coordinator = new MessageCoordinator({
|
|
13725
|
-
workerType: "claude",
|
|
13726
|
-
handlers: {
|
|
13727
|
-
onNormalMessage: async (message) => {
|
|
13728
|
-
await this.enqueueMessage(message);
|
|
13729
|
-
},
|
|
13730
|
-
onBashCommand: async (command, _originalMessage) => {
|
|
13731
|
-
await this.executeBashCommand(command);
|
|
13732
|
-
},
|
|
13733
|
-
onMergeRequest: async (_originalMessage) => {
|
|
13734
|
-
await this.executeMergeRequest();
|
|
13735
|
-
}
|
|
13736
|
-
},
|
|
13737
|
-
logger: (level, category, message) => {
|
|
13738
|
-
const validLevel = level;
|
|
13739
|
-
this.log(validLevel, category, message);
|
|
13740
|
-
}
|
|
13741
|
-
});
|
|
13742
13978
|
}
|
|
13743
13979
|
async handleEvent() {
|
|
13744
13980
|
if (this.options.input.message) {
|
|
@@ -13756,15 +13992,30 @@ class ClaudeWorker {
|
|
|
13756
13992
|
}
|
|
13757
13993
|
const hasChanges = await hasUncommittedChanges(this.context.workingDirectory);
|
|
13758
13994
|
if (hasChanges) {
|
|
13759
|
-
await autoCommit(this.context.workingDirectory, "
|
|
13995
|
+
await autoCommit(this.context.workingDirectory, "Checkpoint for PR generation");
|
|
13760
13996
|
this.log("info", "MERGE", "Auto-committed changes");
|
|
13761
13997
|
}
|
|
13998
|
+
const currentHash = await getCurrentCommitHash(this.context.workingDirectory);
|
|
13999
|
+
const diffStats = await getDiffStats(this.context.workingDirectory, this.initialCommitHashForPR, currentHash);
|
|
14000
|
+
if (diffStats.files.length === 0) {
|
|
14001
|
+
const errorMessage = "No changes to create PR: no files changed since task started";
|
|
14002
|
+
this.log("error", "MERGE", errorMessage);
|
|
14003
|
+
this.context.workClient.sendSystemErrorMessage(errorMessage);
|
|
14004
|
+
return;
|
|
14005
|
+
}
|
|
14006
|
+
this.log("info", "MERGE", `Found ${diffStats.files.length} files changed`);
|
|
13762
14007
|
const branchName = await getCurrentBranch(this.context.workingDirectory);
|
|
13763
14008
|
this.log("info", "MERGE", `Pushing branch ${branchName} to remote`);
|
|
13764
14009
|
await gitPush(this.context.workingDirectory, branchName, false);
|
|
13765
14010
|
this.log("info", "MERGE", "Successfully pushed branch to remote");
|
|
14011
|
+
const prPrompt = buildPRPrompt(
|
|
14012
|
+
{ initialCommitHash: this.initialCommitHashForPR, currentCommitHash: currentHash, branchName },
|
|
14013
|
+
this.loadedAgentConfig?.customPRPromptTemplate ? {
|
|
14014
|
+
customTemplate: this.loadedAgentConfig.customPRPromptTemplate,
|
|
14015
|
+
mode: this.loadedAgentConfig.prPromptMode
|
|
14016
|
+
} : void 0
|
|
14017
|
+
);
|
|
13766
14018
|
this.inMergeRequest = true;
|
|
13767
|
-
const prPrompt = await this.buildCreatePRPrompt();
|
|
13768
14019
|
await this.enqueueMessage({
|
|
13769
14020
|
type: "user",
|
|
13770
14021
|
message: {
|
|
@@ -13793,53 +14044,8 @@ class ClaudeWorker {
|
|
|
13793
14044
|
}
|
|
13794
14045
|
);
|
|
13795
14046
|
this.timerManager.startIdleTimer();
|
|
13796
|
-
this.context.workClient.sendWorkerReady();
|
|
13797
14047
|
this.log("info", "BASH", `Worker ready after command execution (exit code: ${exitCode})`);
|
|
13798
14048
|
}
|
|
13799
|
-
async buildCreatePRPrompt() {
|
|
13800
|
-
if (!this.initialCommitHashForPR) {
|
|
13801
|
-
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").';
|
|
13802
|
-
}
|
|
13803
|
-
try {
|
|
13804
|
-
const currentHash = await autoCommit(this.context.workingDirectory, "Checkpoint for PR generation");
|
|
13805
|
-
const stats = await getDiffStats(
|
|
13806
|
-
this.context.workingDirectory,
|
|
13807
|
-
this.initialCommitHashForPR,
|
|
13808
|
-
currentHash
|
|
13809
|
-
);
|
|
13810
|
-
const diff = await generateDiffPatch(
|
|
13811
|
-
this.context.workingDirectory,
|
|
13812
|
-
this.initialCommitHashForPR,
|
|
13813
|
-
currentHash
|
|
13814
|
-
);
|
|
13815
|
-
const statsText = `Files changed: ${stats.files.length}, +${stats.totalInsertions}/-${stats.totalDeletions}
|
|
13816
|
-
|
|
13817
|
-
Detailed changes:
|
|
13818
|
-
${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("\n")}`;
|
|
13819
|
-
return `All the changed has been successfully pushed to the git branch. Please create a Pull Request by analyzing the changes below.
|
|
13820
|
-
|
|
13821
|
-
Changes made:
|
|
13822
|
-
${statsText}
|
|
13823
|
-
|
|
13824
|
-
Diff (first 5000 chars):
|
|
13825
|
-
\`\`\`
|
|
13826
|
-
${diff.substring(0, 5e3)}
|
|
13827
|
-
\`\`\`
|
|
13828
|
-
|
|
13829
|
-
Requirements:
|
|
13830
|
-
- Title: Use conventional commits format (feat/fix/docs/refactor/test/chore: description), maximum 50 characters
|
|
13831
|
-
- Description: Provide a clear, detailed explanation of:
|
|
13832
|
-
* What changed (the actual modifications made)
|
|
13833
|
-
* Why these changes were necessary (the problem being solved)
|
|
13834
|
-
* Any important technical decisions or trade-offs
|
|
13835
|
-
* Impact on existing functionality
|
|
13836
|
-
|
|
13837
|
-
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.`;
|
|
13838
|
-
} catch (error) {
|
|
13839
|
-
this.log("warn", "GIT", "Failed to generate diff for PR prompt:", error);
|
|
13840
|
-
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").';
|
|
13841
|
-
}
|
|
13842
|
-
}
|
|
13843
14049
|
async runClaude() {
|
|
13844
14050
|
this.log("info", "AGENT", `Starting Claude agent for task ${this.taskId}`);
|
|
13845
14051
|
const agentSessionId = "agentSessionId" in this.options.input ? this.options.input.agentSessionId : void 0;
|
|
@@ -13850,12 +14056,12 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13850
14056
|
);
|
|
13851
14057
|
const sdkMcpServer = this.createAgentrixMcpServer();
|
|
13852
14058
|
const mcpServers = {
|
|
13853
|
-
agentrix: sdkMcpServer
|
|
13854
|
-
...agentConfig.customMcpServers
|
|
14059
|
+
agentrix: sdkMcpServer
|
|
13855
14060
|
};
|
|
13856
14061
|
const allowedTools = [
|
|
13857
14062
|
"mcp__agentrix__change_task_title",
|
|
13858
14063
|
"mcp__agentrix__create_pr",
|
|
14064
|
+
"mcp__agentrix__ask_user",
|
|
13859
14065
|
...agentConfig.customAllowedTools
|
|
13860
14066
|
];
|
|
13861
14067
|
const messageConsumer = this.createMessageConsumer();
|
|
@@ -13865,6 +14071,7 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13865
14071
|
prompt: messageConsumer(),
|
|
13866
14072
|
options: {
|
|
13867
14073
|
stderr: (_data) => {
|
|
14074
|
+
this.log("debug", "SDK", _data);
|
|
13868
14075
|
},
|
|
13869
14076
|
model: agentConfig.customModel || this.options.input.model,
|
|
13870
14077
|
fallbackModel: agentConfig.customFallbackModel,
|
|
@@ -13875,6 +14082,7 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13875
14082
|
systemPrompt: finalSystemPrompt,
|
|
13876
14083
|
mcpServers,
|
|
13877
14084
|
allowedTools,
|
|
14085
|
+
plugins: agentConfig.customPlugins,
|
|
13878
14086
|
abortController: this.abortController,
|
|
13879
14087
|
env: this.buildEnvironmentOverrides(),
|
|
13880
14088
|
maxTurns: agentConfig.customMaxTurns ?? this.options.input.maxTurns ?? 50,
|
|
@@ -13884,18 +14092,16 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13884
14092
|
}
|
|
13885
14093
|
});
|
|
13886
14094
|
if (this.messageQueue.length > 0) {
|
|
13887
|
-
this.
|
|
13888
|
-
this.coordinator.setWorkerState("processing-sdk");
|
|
14095
|
+
this.coordinator.setWorkerState("running");
|
|
13889
14096
|
} else {
|
|
13890
14097
|
this.timerManager.startIdleTimer();
|
|
13891
|
-
this.context.workClient.sendWorkerReady();
|
|
13892
14098
|
}
|
|
13893
14099
|
for await (const message of response) {
|
|
13894
14100
|
this.timerManager.clearIdleTimer();
|
|
13895
14101
|
this.context.logger.debug(`sdk message: ${JSON.stringify(message)}`);
|
|
13896
14102
|
if (message.type === "system" && message.subtype === "init") {
|
|
13897
14103
|
this.context.workClient.sendUpdateTaskAgentSessionId(message.session_id);
|
|
13898
|
-
this.
|
|
14104
|
+
this.coordinator.setWorkerState("running");
|
|
13899
14105
|
continue;
|
|
13900
14106
|
}
|
|
13901
14107
|
const filteredMessage = this.filterMessages(message);
|
|
@@ -13905,9 +14111,8 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13905
14111
|
if (message.type === "result") {
|
|
13906
14112
|
this.coordinator.setWorkerState("idle");
|
|
13907
14113
|
this.timerManager.startIdleTimer();
|
|
13908
|
-
this.context.workClient.sendWorkerReady();
|
|
13909
14114
|
} else {
|
|
13910
|
-
this.coordinator.setWorkerState("
|
|
14115
|
+
this.coordinator.setWorkerState("running");
|
|
13911
14116
|
}
|
|
13912
14117
|
}
|
|
13913
14118
|
this.log("info", "AGENT", `Claude agent finished for task ${this.taskId}`);
|
|
@@ -14067,33 +14272,117 @@ Type: ${mimeType}`
|
|
|
14067
14272
|
}
|
|
14068
14273
|
createPermissionHandler() {
|
|
14069
14274
|
return async (toolName, input) => {
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
}
|
|
14275
|
+
if (this.grantedPermissions.has(toolName)) {
|
|
14276
|
+
this.log("info", "PERMISSION", `Tool "${toolName}" already granted, skipping`);
|
|
14277
|
+
return { behavior: "allow", updatedInput: input };
|
|
14278
|
+
}
|
|
14279
|
+
const pending = this.pendingPermissions.get(toolName);
|
|
14280
|
+
if (pending) {
|
|
14281
|
+
this.log("info", "PERMISSION", `Tool "${toolName}" has pending request, waiting...`);
|
|
14282
|
+
const decision = await pending;
|
|
14283
|
+
if (decision === "allow") {
|
|
14284
|
+
return { behavior: "allow", updatedInput: input };
|
|
14285
|
+
} else {
|
|
14286
|
+
return { behavior: "deny", message: "Permission denied by user" };
|
|
14082
14287
|
}
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14288
|
+
}
|
|
14289
|
+
this.log("info", "PERMISSION", `Requesting permission for "${toolName}"`);
|
|
14290
|
+
let resolveDecision;
|
|
14291
|
+
const permissionPromise = new Promise((resolve) => {
|
|
14292
|
+
resolveDecision = resolve;
|
|
14293
|
+
});
|
|
14294
|
+
this.pendingPermissions.set(toolName, permissionPromise);
|
|
14295
|
+
try {
|
|
14296
|
+
const decision = await this.requestToolPermission(toolName);
|
|
14297
|
+
resolveDecision(decision);
|
|
14298
|
+
if (decision === "allow") {
|
|
14299
|
+
this.grantedPermissions.add(toolName);
|
|
14300
|
+
return { behavior: "allow", updatedInput: input };
|
|
14301
|
+
} else {
|
|
14302
|
+
return { behavior: "deny", message: "Permission denied by user" };
|
|
14303
|
+
}
|
|
14304
|
+
} catch (error) {
|
|
14305
|
+
resolveDecision("deny");
|
|
14306
|
+
return { behavior: "deny", message: "Permission request failed" };
|
|
14307
|
+
} finally {
|
|
14308
|
+
this.pendingPermissions.delete(toolName);
|
|
14094
14309
|
}
|
|
14095
14310
|
};
|
|
14096
14311
|
}
|
|
14312
|
+
async requestToolPermission(toolName) {
|
|
14313
|
+
const questions = [{
|
|
14314
|
+
question: `Tool "${toolName}" is requesting permission to execute. Allow this operation?`,
|
|
14315
|
+
header: "Permission",
|
|
14316
|
+
multiSelect: false,
|
|
14317
|
+
options: [
|
|
14318
|
+
{ label: "Allow", description: "Allow this tool to execute" },
|
|
14319
|
+
{ label: "Deny", description: "Deny this tool execution" }
|
|
14320
|
+
]
|
|
14321
|
+
}];
|
|
14322
|
+
try {
|
|
14323
|
+
const response = await this.askUser(questions);
|
|
14324
|
+
const answer = response.answers[0];
|
|
14325
|
+
return answer === "Allow" ? "allow" : "deny";
|
|
14326
|
+
} catch (error) {
|
|
14327
|
+
this.log("warn", "PERMISSION", `Permission request failed: ${error}`);
|
|
14328
|
+
return "deny";
|
|
14329
|
+
}
|
|
14330
|
+
}
|
|
14331
|
+
/**
|
|
14332
|
+
* Ask user questions and wait for response
|
|
14333
|
+
* Sends ask_user message via task-message and waits for ask_user_response
|
|
14334
|
+
* @param questions - Array of questions (1-4)
|
|
14335
|
+
* @returns Promise resolving to user's response
|
|
14336
|
+
*/
|
|
14337
|
+
async askUser(questions) {
|
|
14338
|
+
const eventId = this.context.workClient.sendAskUser(questions);
|
|
14339
|
+
const timeoutMs = 3e5;
|
|
14340
|
+
return new Promise((resolve, reject) => {
|
|
14341
|
+
const timeout = setTimeout(() => {
|
|
14342
|
+
this.askUserAwaiter.delete(eventId);
|
|
14343
|
+
reject(new Error("Ask user request timed out"));
|
|
14344
|
+
}, timeoutMs);
|
|
14345
|
+
this.askUserAwaiter.set(eventId, (response) => {
|
|
14346
|
+
clearTimeout(timeout);
|
|
14347
|
+
resolve(response);
|
|
14348
|
+
});
|
|
14349
|
+
});
|
|
14350
|
+
}
|
|
14351
|
+
/**
|
|
14352
|
+
* Ask user how to handle uncommitted changes
|
|
14353
|
+
* @returns The action to take: ignore, commit, stash, or abort
|
|
14354
|
+
*/
|
|
14355
|
+
async askUncommittedChangesAction() {
|
|
14356
|
+
const questions = [{
|
|
14357
|
+
question: "Uncommitted changes detected in the working directory. How would you like to proceed?",
|
|
14358
|
+
header: "Git Status",
|
|
14359
|
+
multiSelect: false,
|
|
14360
|
+
options: [
|
|
14361
|
+
{ label: "Ignore", description: "Keep changes and continue with the task" },
|
|
14362
|
+
{ label: "Commit", description: "Commit current changes before starting" },
|
|
14363
|
+
{ label: "Stash", description: "Stash changes (git stash) before starting" },
|
|
14364
|
+
{ label: "Abort", description: "Cancel the task, do nothing" }
|
|
14365
|
+
]
|
|
14366
|
+
}];
|
|
14367
|
+
try {
|
|
14368
|
+
const response = await this.askUser(questions);
|
|
14369
|
+
const answer = response.answers[0];
|
|
14370
|
+
if (answer.startsWith("other:")) {
|
|
14371
|
+
this.log("info", "GIT", `User provided custom input: ${answer}, defaulting to Abort`);
|
|
14372
|
+
return "Abort";
|
|
14373
|
+
}
|
|
14374
|
+
const labelToAction = {
|
|
14375
|
+
"Ignore": "Ignore",
|
|
14376
|
+
"Commit": "Commit",
|
|
14377
|
+
"Stash": "Stash",
|
|
14378
|
+
"Abort": "Abort"
|
|
14379
|
+
};
|
|
14380
|
+
return labelToAction[answer] || "Abort";
|
|
14381
|
+
} catch (error) {
|
|
14382
|
+
this.log("warn", "GIT", `Failed to get user response for uncommitted changes: ${error}`);
|
|
14383
|
+
return "Abort";
|
|
14384
|
+
}
|
|
14385
|
+
}
|
|
14097
14386
|
createAgentrixMcpServer() {
|
|
14098
14387
|
return claudeAgentSdk.createSdkMcpServer({
|
|
14099
14388
|
name: "agentrix",
|
|
@@ -14154,6 +14443,50 @@ URL: ${result.pullRequestUrl}`
|
|
|
14154
14443
|
};
|
|
14155
14444
|
}
|
|
14156
14445
|
}
|
|
14446
|
+
),
|
|
14447
|
+
claudeAgentSdk.tool(
|
|
14448
|
+
"ask_user",
|
|
14449
|
+
'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.',
|
|
14450
|
+
{
|
|
14451
|
+
questions: zod.z.array(zod.z.object({
|
|
14452
|
+
question: zod.z.string().describe("The complete question to ask the user"),
|
|
14453
|
+
header: zod.z.string().max(12).describe("Short label displayed as a chip/tag (max 12 chars)"),
|
|
14454
|
+
multiSelect: zod.z.boolean().describe("Set to true to allow multiple option selections"),
|
|
14455
|
+
options: zod.z.array(zod.z.object({
|
|
14456
|
+
label: zod.z.string().describe("Option label (1-5 words)"),
|
|
14457
|
+
description: zod.z.string().describe("Explanation of what this option means")
|
|
14458
|
+
})).min(2).max(4).describe("Available choices (2-4 options)")
|
|
14459
|
+
})).min(1).max(4).describe("Questions to ask (1-4 questions)")
|
|
14460
|
+
},
|
|
14461
|
+
async (args) => {
|
|
14462
|
+
try {
|
|
14463
|
+
const questionsWithOther = args.questions.map((q) => ({
|
|
14464
|
+
...q,
|
|
14465
|
+
options: [...q.options, { label: "Other", description: "" }]
|
|
14466
|
+
}));
|
|
14467
|
+
const result = await this.askUser(questionsWithOther);
|
|
14468
|
+
const answerText = result.answers.map((answer) => {
|
|
14469
|
+
if (answer.startsWith("other:")) {
|
|
14470
|
+
return `Other: "${answer.slice(6)}"`;
|
|
14471
|
+
}
|
|
14472
|
+
return answer;
|
|
14473
|
+
}).join("\n");
|
|
14474
|
+
return {
|
|
14475
|
+
content: [{ type: "text", text: `User answers:
|
|
14476
|
+
${answerText}` }]
|
|
14477
|
+
};
|
|
14478
|
+
} catch (error) {
|
|
14479
|
+
this.log("error", "ASK_USER", "Failed to get user response:", error);
|
|
14480
|
+
return {
|
|
14481
|
+
content: [
|
|
14482
|
+
{
|
|
14483
|
+
type: "text",
|
|
14484
|
+
text: `Failed to get user response: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
14485
|
+
}
|
|
14486
|
+
]
|
|
14487
|
+
};
|
|
14488
|
+
}
|
|
14489
|
+
}
|
|
14157
14490
|
)
|
|
14158
14491
|
]
|
|
14159
14492
|
});
|
|
@@ -14169,7 +14502,7 @@ URL: ${result.pullRequestUrl}`
|
|
|
14169
14502
|
} catch (error) {
|
|
14170
14503
|
this.log("warn", "GIT", "Failed to handle git state on worker stop:", error);
|
|
14171
14504
|
}
|
|
14172
|
-
this.
|
|
14505
|
+
this.coordinator.setWorkerState("idle");
|
|
14173
14506
|
}
|
|
14174
14507
|
filterMessages(message) {
|
|
14175
14508
|
const msg = message;
|
|
@@ -14210,38 +14543,45 @@ URL: ${result.pullRequestUrl}`
|
|
|
14210
14543
|
return this.createDefaultAgentConfig();
|
|
14211
14544
|
}
|
|
14212
14545
|
const claudeConfig = agentConfig.claude;
|
|
14213
|
-
const
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14546
|
+
const customPlugins = claudeConfig.plugins.map((path) => ({
|
|
14547
|
+
type: "local",
|
|
14548
|
+
path
|
|
14549
|
+
}));
|
|
14550
|
+
this.log("info", "AGENT", `Agent ${this.options.input.agentId} loaded successfully (${customPlugins.length} plugins)`);
|
|
14551
|
+
const config = {
|
|
14218
14552
|
customSystemPrompt: claudeConfig.systemPrompt,
|
|
14219
|
-
// Loaded string content
|
|
14220
14553
|
customModel: claudeConfig.config.model,
|
|
14221
14554
|
customFallbackModel: claudeConfig.config.fallbackModel,
|
|
14222
14555
|
customMaxTurns: claudeConfig.config.maxTurns,
|
|
14223
14556
|
customExtraArgs: claudeConfig.config.extraArgs,
|
|
14224
14557
|
customPermissionMode: claudeConfig.config.settings?.permissionMode,
|
|
14225
|
-
customMcpServers,
|
|
14226
14558
|
customAllowedTools: claudeConfig.config.settings?.allowedTools || [],
|
|
14559
|
+
customPlugins,
|
|
14227
14560
|
systemPromptMode: claudeConfig.config.systemPrompt?.mode ?? "append",
|
|
14228
|
-
hooks: this.loadedHooks
|
|
14229
|
-
|
|
14561
|
+
hooks: this.loadedHooks,
|
|
14562
|
+
customPRPromptTemplate: claudeConfig.prPromptTemplate,
|
|
14563
|
+
prPromptMode: claudeConfig.config.pullRequestPrompt?.mode ?? "append"
|
|
14230
14564
|
};
|
|
14565
|
+
this.loadedAgentConfig = config;
|
|
14566
|
+
return config;
|
|
14231
14567
|
} catch (error) {
|
|
14232
14568
|
this.log("error", "AGENT", `Failed to load agent: ${error instanceof Error ? error.message : String(error)}`);
|
|
14233
14569
|
return this.createDefaultAgentConfig();
|
|
14234
14570
|
}
|
|
14235
14571
|
}
|
|
14236
14572
|
createDefaultAgentConfig() {
|
|
14237
|
-
|
|
14573
|
+
const config = {
|
|
14238
14574
|
customSystemPrompt: void 0,
|
|
14239
14575
|
customModel: void 0,
|
|
14240
14576
|
customMaxTurns: void 0,
|
|
14241
|
-
customMcpServers: {},
|
|
14242
14577
|
customAllowedTools: [],
|
|
14243
|
-
|
|
14578
|
+
customPlugins: [],
|
|
14579
|
+
systemPromptMode: "append",
|
|
14580
|
+
customPRPromptTemplate: void 0,
|
|
14581
|
+
prPromptMode: "append"
|
|
14244
14582
|
};
|
|
14583
|
+
this.loadedAgentConfig = config;
|
|
14584
|
+
return config;
|
|
14245
14585
|
}
|
|
14246
14586
|
buildEnvironmentOverrides() {
|
|
14247
14587
|
return {
|
|
@@ -14349,18 +14689,20 @@ URL: ${result.pullRequestUrl}`
|
|
|
14349
14689
|
this.timerManager.stopTask("event");
|
|
14350
14690
|
},
|
|
14351
14691
|
onTaskMessage: async (message) => {
|
|
14352
|
-
if (message
|
|
14692
|
+
if (shared.isAskUserResponseMessage(message)) {
|
|
14693
|
+
const [eventId, awaiter] = this.askUserAwaiter.entries().next().value || [];
|
|
14694
|
+
if (eventId && awaiter) {
|
|
14695
|
+
this.askUserAwaiter.delete(eventId);
|
|
14696
|
+
awaiter(message);
|
|
14697
|
+
}
|
|
14698
|
+
this.timerManager.clearIdleTimer();
|
|
14699
|
+
return;
|
|
14700
|
+
}
|
|
14701
|
+
if ("type" in message && message.type === "user") {
|
|
14353
14702
|
const userMessage = message;
|
|
14354
14703
|
await this.coordinator.enqueue(userMessage);
|
|
14355
14704
|
this.timerManager.clearIdleTimer();
|
|
14356
14705
|
}
|
|
14357
|
-
},
|
|
14358
|
-
onPermissionResponse: async (response) => {
|
|
14359
|
-
const awaiter = this.permissionAwaiter.get(response.opCode);
|
|
14360
|
-
if (awaiter) {
|
|
14361
|
-
this.permissionAwaiter.delete(response.opCode);
|
|
14362
|
-
awaiter(response);
|
|
14363
|
-
}
|
|
14364
14706
|
}
|
|
14365
14707
|
}
|
|
14366
14708
|
};
|
|
@@ -14399,9 +14741,8 @@ URL: ${result.pullRequestUrl}`
|
|
|
14399
14741
|
this.log("info", "GIT", `Patch: ${gitStateResult.patchPath}`);
|
|
14400
14742
|
}
|
|
14401
14743
|
}
|
|
14402
|
-
|
|
14403
|
-
|
|
14404
|
-
return createLogger(options);
|
|
14744
|
+
createWorkerLogger(options) {
|
|
14745
|
+
return _package.createLogger(options);
|
|
14405
14746
|
}
|
|
14406
14747
|
log(level, category, message, ...args) {
|
|
14407
14748
|
if (this.logger) {
|
|
@@ -16139,6 +16480,7 @@ class CodexWorker {
|
|
|
16139
16480
|
};
|
|
16140
16481
|
this.coordinator = new MessageCoordinator({
|
|
16141
16482
|
workerType: "codex",
|
|
16483
|
+
workClient,
|
|
16142
16484
|
handlers: {
|
|
16143
16485
|
onNormalMessage: async (message) => {
|
|
16144
16486
|
const input = await this.convertSDKMessageToCodexInput(message);
|
|
@@ -16211,7 +16553,6 @@ class CodexWorker {
|
|
|
16211
16553
|
}
|
|
16212
16554
|
);
|
|
16213
16555
|
this.timerManager.startIdleTimer();
|
|
16214
|
-
this.context.workClient.sendWorkerReady();
|
|
16215
16556
|
this.log("info", "BASH", `Worker ready after command execution (exit code: ${exitCode})`);
|
|
16216
16557
|
}
|
|
16217
16558
|
async buildCreatePRPrompt() {
|
|
@@ -16268,8 +16609,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16268
16609
|
skipGitRepoCheck: true
|
|
16269
16610
|
});
|
|
16270
16611
|
}
|
|
16271
|
-
this.
|
|
16272
|
-
this.coordinator.setWorkerState("processing-sdk");
|
|
16612
|
+
this.coordinator.setWorkerState("running");
|
|
16273
16613
|
while (!this.isStopping) {
|
|
16274
16614
|
this.log("debug", "AGENT", `Loop iteration: turnCount=${this.turnCount}, queueLength=${this.messageQueue.length}`);
|
|
16275
16615
|
let userInput = null;
|
|
@@ -16281,7 +16621,6 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16281
16621
|
await this.reportGitState("idle");
|
|
16282
16622
|
this.coordinator.setWorkerState("idle");
|
|
16283
16623
|
this.timerManager.startIdleTimer();
|
|
16284
|
-
this.context.workClient.sendWorkerReady();
|
|
16285
16624
|
this.log("info", "AGENT", "Sent worker-ready, waiting for next message");
|
|
16286
16625
|
userInput = await this.waitForNextMessage();
|
|
16287
16626
|
}
|
|
@@ -16291,7 +16630,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16291
16630
|
}
|
|
16292
16631
|
try {
|
|
16293
16632
|
this.timerManager.clearIdleTimer();
|
|
16294
|
-
this.coordinator.setWorkerState("
|
|
16633
|
+
this.coordinator.setWorkerState("running");
|
|
16295
16634
|
await this.processUserInput(userInput);
|
|
16296
16635
|
this.turnCount++;
|
|
16297
16636
|
this.log("debug", "AGENT", `Message processed, turnCount now ${this.turnCount}`);
|
|
@@ -16327,7 +16666,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16327
16666
|
const turnOptions = this.inMergeRequest ? { outputSchema: getPROutputSchema() } : {};
|
|
16328
16667
|
this.log("debug", "AGENT", "Calling thread.runStreamed");
|
|
16329
16668
|
const { events } = await this.thread.runStreamed(input, turnOptions);
|
|
16330
|
-
this.
|
|
16669
|
+
this.coordinator.setWorkerState("running");
|
|
16331
16670
|
this.log("debug", "AGENT", "Starting to process events");
|
|
16332
16671
|
for await (const event of events) {
|
|
16333
16672
|
if (this.isStopping) {
|
|
@@ -16618,7 +16957,7 @@ Type: ${mimeType}`
|
|
|
16618
16957
|
}
|
|
16619
16958
|
async handleStopHook() {
|
|
16620
16959
|
await this.reportGitState("stop");
|
|
16621
|
-
this.
|
|
16960
|
+
this.coordinator.setWorkerState("idle");
|
|
16622
16961
|
}
|
|
16623
16962
|
async loadAgentConfiguration() {
|
|
16624
16963
|
if (!this.options.input.agentId || this.options.input.agentId === "default") {
|
|
@@ -16710,14 +17049,15 @@ Type: ${mimeType}`
|
|
|
16710
17049
|
this.timerManager.stopTask("event");
|
|
16711
17050
|
},
|
|
16712
17051
|
onTaskMessage: async (message) => {
|
|
16713
|
-
if (message
|
|
17052
|
+
if (shared.isAskUserResponseMessage(message)) {
|
|
17053
|
+
this.log("debug", "AGENT", "Received ask_user_response (not used by Codex)");
|
|
17054
|
+
return;
|
|
17055
|
+
}
|
|
17056
|
+
if ("type" in message && message.type === "user") {
|
|
16714
17057
|
const userMessage = message;
|
|
16715
17058
|
await this.coordinator.enqueue(userMessage);
|
|
16716
17059
|
this.timerManager.clearIdleTimer();
|
|
16717
17060
|
}
|
|
16718
|
-
},
|
|
16719
|
-
onPermissionResponse: async (response) => {
|
|
16720
|
-
this.log("debug", "AGENT", `Permission response received: ${response.behavior}`);
|
|
16721
17061
|
}
|
|
16722
17062
|
}
|
|
16723
17063
|
};
|
|
@@ -17072,7 +17412,7 @@ cli.command("upgrade", "Upgrade CLI to the latest version", {}, async (argv) =>
|
|
|
17072
17412
|
}
|
|
17073
17413
|
}
|
|
17074
17414
|
try {
|
|
17075
|
-
const { version } = await Promise.resolve().then(function () { return require('./logger
|
|
17415
|
+
const { version } = await Promise.resolve().then(function () { return require('./logger-DFD0a_cW.cjs'); }).then(function (n) { return n._package; });
|
|
17076
17416
|
console.log(chalk.green(`
|
|
17077
17417
|
\u2713 Now running version: ${version}`));
|
|
17078
17418
|
} catch {
|
|
@@ -17254,7 +17594,7 @@ cli.command(
|
|
|
17254
17594
|
},
|
|
17255
17595
|
async (argv) => {
|
|
17256
17596
|
try {
|
|
17257
|
-
const { testCommand } = await Promise.resolve().then(function () { return require('./index-
|
|
17597
|
+
const { testCommand } = await Promise.resolve().then(function () { return require('./index-DDSUzta4.cjs'); });
|
|
17258
17598
|
await testCommand(argv);
|
|
17259
17599
|
} catch (error) {
|
|
17260
17600
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Test mode failed");
|