@agentrix/cli 0.0.12 → 0.0.13
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-B_Hr2zCf.cjs} +578 -239
- package/dist/{index-BdFjpsoN.cjs → index-Cs99FGLe.cjs} +8 -90
- package/dist/{index-D1ipaIV_.mjs → index-DNDJMHW8.mjs} +8 -90
- package/dist/{index-Dzjo3uhE.mjs → index-yYa7NL_3.mjs} +579 -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-BzpMLIL-.mjs} +158 -24
- package/dist/{logger---ZD5a2u.cjs → logger-DzYRcKN1.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-BzpMLIL-.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-BzpMLIL-.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();
|
|
@@ -13855,6 +14061,7 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13855
14061
|
systemPrompt: finalSystemPrompt,
|
|
13856
14062
|
mcpServers,
|
|
13857
14063
|
allowedTools,
|
|
14064
|
+
plugins: agentConfig.customPlugins,
|
|
13858
14065
|
abortController: this.abortController,
|
|
13859
14066
|
env: this.buildEnvironmentOverrides(),
|
|
13860
14067
|
maxTurns: agentConfig.customMaxTurns ?? this.options.input.maxTurns ?? 50,
|
|
@@ -13864,18 +14071,16 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13864
14071
|
}
|
|
13865
14072
|
});
|
|
13866
14073
|
if (this.messageQueue.length > 0) {
|
|
13867
|
-
this.
|
|
13868
|
-
this.coordinator.setWorkerState("processing-sdk");
|
|
14074
|
+
this.coordinator.setWorkerState("running");
|
|
13869
14075
|
} else {
|
|
13870
14076
|
this.timerManager.startIdleTimer();
|
|
13871
|
-
this.context.workClient.sendWorkerReady();
|
|
13872
14077
|
}
|
|
13873
14078
|
for await (const message of response) {
|
|
13874
14079
|
this.timerManager.clearIdleTimer();
|
|
13875
14080
|
this.context.logger.debug(`sdk message: ${JSON.stringify(message)}`);
|
|
13876
14081
|
if (message.type === "system" && message.subtype === "init") {
|
|
13877
14082
|
this.context.workClient.sendUpdateTaskAgentSessionId(message.session_id);
|
|
13878
|
-
this.
|
|
14083
|
+
this.coordinator.setWorkerState("running");
|
|
13879
14084
|
continue;
|
|
13880
14085
|
}
|
|
13881
14086
|
const filteredMessage = this.filterMessages(message);
|
|
@@ -13885,9 +14090,8 @@ Please must use the mcp__agentrix__create_pr tool to create the pull request wit
|
|
|
13885
14090
|
if (message.type === "result") {
|
|
13886
14091
|
this.coordinator.setWorkerState("idle");
|
|
13887
14092
|
this.timerManager.startIdleTimer();
|
|
13888
|
-
this.context.workClient.sendWorkerReady();
|
|
13889
14093
|
} else {
|
|
13890
|
-
this.coordinator.setWorkerState("
|
|
14094
|
+
this.coordinator.setWorkerState("running");
|
|
13891
14095
|
}
|
|
13892
14096
|
}
|
|
13893
14097
|
this.log("info", "AGENT", `Claude agent finished for task ${this.taskId}`);
|
|
@@ -14047,33 +14251,117 @@ Type: ${mimeType}`
|
|
|
14047
14251
|
}
|
|
14048
14252
|
createPermissionHandler() {
|
|
14049
14253
|
return async (toolName, input) => {
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
}
|
|
14254
|
+
if (this.grantedPermissions.has(toolName)) {
|
|
14255
|
+
this.log("info", "PERMISSION", `Tool "${toolName}" already granted, skipping`);
|
|
14256
|
+
return { behavior: "allow", updatedInput: input };
|
|
14257
|
+
}
|
|
14258
|
+
const pending = this.pendingPermissions.get(toolName);
|
|
14259
|
+
if (pending) {
|
|
14260
|
+
this.log("info", "PERMISSION", `Tool "${toolName}" has pending request, waiting...`);
|
|
14261
|
+
const decision = await pending;
|
|
14262
|
+
if (decision === "allow") {
|
|
14263
|
+
return { behavior: "allow", updatedInput: input };
|
|
14264
|
+
} else {
|
|
14265
|
+
return { behavior: "deny", message: "Permission denied by user" };
|
|
14062
14266
|
}
|
|
14063
|
-
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14267
|
+
}
|
|
14268
|
+
this.log("info", "PERMISSION", `Requesting permission for "${toolName}"`);
|
|
14269
|
+
let resolveDecision;
|
|
14270
|
+
const permissionPromise = new Promise((resolve) => {
|
|
14271
|
+
resolveDecision = resolve;
|
|
14272
|
+
});
|
|
14273
|
+
this.pendingPermissions.set(toolName, permissionPromise);
|
|
14274
|
+
try {
|
|
14275
|
+
const decision = await this.requestToolPermission(toolName);
|
|
14276
|
+
resolveDecision(decision);
|
|
14277
|
+
if (decision === "allow") {
|
|
14278
|
+
this.grantedPermissions.add(toolName);
|
|
14279
|
+
return { behavior: "allow", updatedInput: input };
|
|
14280
|
+
} else {
|
|
14281
|
+
return { behavior: "deny", message: "Permission denied by user" };
|
|
14282
|
+
}
|
|
14283
|
+
} catch (error) {
|
|
14284
|
+
resolveDecision("deny");
|
|
14285
|
+
return { behavior: "deny", message: "Permission request failed" };
|
|
14286
|
+
} finally {
|
|
14287
|
+
this.pendingPermissions.delete(toolName);
|
|
14074
14288
|
}
|
|
14075
14289
|
};
|
|
14076
14290
|
}
|
|
14291
|
+
async requestToolPermission(toolName) {
|
|
14292
|
+
const questions = [{
|
|
14293
|
+
question: `Tool "${toolName}" is requesting permission to execute. Allow this operation?`,
|
|
14294
|
+
header: "Permission",
|
|
14295
|
+
multiSelect: false,
|
|
14296
|
+
options: [
|
|
14297
|
+
{ label: "Allow", description: "Allow this tool to execute" },
|
|
14298
|
+
{ label: "Deny", description: "Deny this tool execution" }
|
|
14299
|
+
]
|
|
14300
|
+
}];
|
|
14301
|
+
try {
|
|
14302
|
+
const response = await this.askUser(questions);
|
|
14303
|
+
const answer = response.answers[0];
|
|
14304
|
+
return answer === "Allow" ? "allow" : "deny";
|
|
14305
|
+
} catch (error) {
|
|
14306
|
+
this.log("warn", "PERMISSION", `Permission request failed: ${error}`);
|
|
14307
|
+
return "deny";
|
|
14308
|
+
}
|
|
14309
|
+
}
|
|
14310
|
+
/**
|
|
14311
|
+
* Ask user questions and wait for response
|
|
14312
|
+
* Sends ask_user message via task-message and waits for ask_user_response
|
|
14313
|
+
* @param questions - Array of questions (1-4)
|
|
14314
|
+
* @returns Promise resolving to user's response
|
|
14315
|
+
*/
|
|
14316
|
+
async askUser(questions) {
|
|
14317
|
+
const eventId = this.context.workClient.sendAskUser(questions);
|
|
14318
|
+
const timeoutMs = 3e5;
|
|
14319
|
+
return new Promise((resolve, reject) => {
|
|
14320
|
+
const timeout = setTimeout(() => {
|
|
14321
|
+
this.askUserAwaiter.delete(eventId);
|
|
14322
|
+
reject(new Error("Ask user request timed out"));
|
|
14323
|
+
}, timeoutMs);
|
|
14324
|
+
this.askUserAwaiter.set(eventId, (response) => {
|
|
14325
|
+
clearTimeout(timeout);
|
|
14326
|
+
resolve(response);
|
|
14327
|
+
});
|
|
14328
|
+
});
|
|
14329
|
+
}
|
|
14330
|
+
/**
|
|
14331
|
+
* Ask user how to handle uncommitted changes
|
|
14332
|
+
* @returns The action to take: ignore, commit, stash, or abort
|
|
14333
|
+
*/
|
|
14334
|
+
async askUncommittedChangesAction() {
|
|
14335
|
+
const questions = [{
|
|
14336
|
+
question: "Uncommitted changes detected in the working directory. How would you like to proceed?",
|
|
14337
|
+
header: "Git Status",
|
|
14338
|
+
multiSelect: false,
|
|
14339
|
+
options: [
|
|
14340
|
+
{ label: "Ignore", description: "Keep changes and continue with the task" },
|
|
14341
|
+
{ label: "Commit", description: "Commit current changes before starting" },
|
|
14342
|
+
{ label: "Stash", description: "Stash changes (git stash) before starting" },
|
|
14343
|
+
{ label: "Abort", description: "Cancel the task, do nothing" }
|
|
14344
|
+
]
|
|
14345
|
+
}];
|
|
14346
|
+
try {
|
|
14347
|
+
const response = await this.askUser(questions);
|
|
14348
|
+
const answer = response.answers[0];
|
|
14349
|
+
if (answer.startsWith("other:")) {
|
|
14350
|
+
this.log("info", "GIT", `User provided custom input: ${answer}, defaulting to Abort`);
|
|
14351
|
+
return "Abort";
|
|
14352
|
+
}
|
|
14353
|
+
const labelToAction = {
|
|
14354
|
+
"Ignore": "Ignore",
|
|
14355
|
+
"Commit": "Commit",
|
|
14356
|
+
"Stash": "Stash",
|
|
14357
|
+
"Abort": "Abort"
|
|
14358
|
+
};
|
|
14359
|
+
return labelToAction[answer] || "Abort";
|
|
14360
|
+
} catch (error) {
|
|
14361
|
+
this.log("warn", "GIT", `Failed to get user response for uncommitted changes: ${error}`);
|
|
14362
|
+
return "Abort";
|
|
14363
|
+
}
|
|
14364
|
+
}
|
|
14077
14365
|
createAgentrixMcpServer() {
|
|
14078
14366
|
return createSdkMcpServer({
|
|
14079
14367
|
name: "agentrix",
|
|
@@ -14134,6 +14422,50 @@ URL: ${result.pullRequestUrl}`
|
|
|
14134
14422
|
};
|
|
14135
14423
|
}
|
|
14136
14424
|
}
|
|
14425
|
+
),
|
|
14426
|
+
tool(
|
|
14427
|
+
"ask_user",
|
|
14428
|
+
'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.',
|
|
14429
|
+
{
|
|
14430
|
+
questions: z.array(z.object({
|
|
14431
|
+
question: z.string().describe("The complete question to ask the user"),
|
|
14432
|
+
header: z.string().max(12).describe("Short label displayed as a chip/tag (max 12 chars)"),
|
|
14433
|
+
multiSelect: z.boolean().describe("Set to true to allow multiple option selections"),
|
|
14434
|
+
options: z.array(z.object({
|
|
14435
|
+
label: z.string().describe("Option label (1-5 words)"),
|
|
14436
|
+
description: z.string().describe("Explanation of what this option means")
|
|
14437
|
+
})).min(2).max(4).describe("Available choices (2-4 options)")
|
|
14438
|
+
})).min(1).max(4).describe("Questions to ask (1-4 questions)")
|
|
14439
|
+
},
|
|
14440
|
+
async (args) => {
|
|
14441
|
+
try {
|
|
14442
|
+
const questionsWithOther = args.questions.map((q) => ({
|
|
14443
|
+
...q,
|
|
14444
|
+
options: [...q.options, { label: "Other", description: "" }]
|
|
14445
|
+
}));
|
|
14446
|
+
const result = await this.askUser(questionsWithOther);
|
|
14447
|
+
const answerText = result.answers.map((answer) => {
|
|
14448
|
+
if (answer.startsWith("other:")) {
|
|
14449
|
+
return `Other: "${answer.slice(6)}"`;
|
|
14450
|
+
}
|
|
14451
|
+
return answer;
|
|
14452
|
+
}).join("\n");
|
|
14453
|
+
return {
|
|
14454
|
+
content: [{ type: "text", text: `User answers:
|
|
14455
|
+
${answerText}` }]
|
|
14456
|
+
};
|
|
14457
|
+
} catch (error) {
|
|
14458
|
+
this.log("error", "ASK_USER", "Failed to get user response:", error);
|
|
14459
|
+
return {
|
|
14460
|
+
content: [
|
|
14461
|
+
{
|
|
14462
|
+
type: "text",
|
|
14463
|
+
text: `Failed to get user response: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
14464
|
+
}
|
|
14465
|
+
]
|
|
14466
|
+
};
|
|
14467
|
+
}
|
|
14468
|
+
}
|
|
14137
14469
|
)
|
|
14138
14470
|
]
|
|
14139
14471
|
});
|
|
@@ -14149,7 +14481,7 @@ URL: ${result.pullRequestUrl}`
|
|
|
14149
14481
|
} catch (error) {
|
|
14150
14482
|
this.log("warn", "GIT", "Failed to handle git state on worker stop:", error);
|
|
14151
14483
|
}
|
|
14152
|
-
this.
|
|
14484
|
+
this.coordinator.setWorkerState("idle");
|
|
14153
14485
|
}
|
|
14154
14486
|
filterMessages(message) {
|
|
14155
14487
|
const msg = message;
|
|
@@ -14190,38 +14522,45 @@ URL: ${result.pullRequestUrl}`
|
|
|
14190
14522
|
return this.createDefaultAgentConfig();
|
|
14191
14523
|
}
|
|
14192
14524
|
const claudeConfig = agentConfig.claude;
|
|
14193
|
-
const
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14525
|
+
const customPlugins = claudeConfig.plugins.map((path) => ({
|
|
14526
|
+
type: "local",
|
|
14527
|
+
path
|
|
14528
|
+
}));
|
|
14529
|
+
this.log("info", "AGENT", `Agent ${this.options.input.agentId} loaded successfully (${customPlugins.length} plugins)`);
|
|
14530
|
+
const config = {
|
|
14198
14531
|
customSystemPrompt: claudeConfig.systemPrompt,
|
|
14199
|
-
// Loaded string content
|
|
14200
14532
|
customModel: claudeConfig.config.model,
|
|
14201
14533
|
customFallbackModel: claudeConfig.config.fallbackModel,
|
|
14202
14534
|
customMaxTurns: claudeConfig.config.maxTurns,
|
|
14203
14535
|
customExtraArgs: claudeConfig.config.extraArgs,
|
|
14204
14536
|
customPermissionMode: claudeConfig.config.settings?.permissionMode,
|
|
14205
|
-
customMcpServers,
|
|
14206
14537
|
customAllowedTools: claudeConfig.config.settings?.allowedTools || [],
|
|
14538
|
+
customPlugins,
|
|
14207
14539
|
systemPromptMode: claudeConfig.config.systemPrompt?.mode ?? "append",
|
|
14208
|
-
hooks: this.loadedHooks
|
|
14209
|
-
|
|
14540
|
+
hooks: this.loadedHooks,
|
|
14541
|
+
customPRPromptTemplate: claudeConfig.prPromptTemplate,
|
|
14542
|
+
prPromptMode: claudeConfig.config.pullRequestPrompt?.mode ?? "append"
|
|
14210
14543
|
};
|
|
14544
|
+
this.loadedAgentConfig = config;
|
|
14545
|
+
return config;
|
|
14211
14546
|
} catch (error) {
|
|
14212
14547
|
this.log("error", "AGENT", `Failed to load agent: ${error instanceof Error ? error.message : String(error)}`);
|
|
14213
14548
|
return this.createDefaultAgentConfig();
|
|
14214
14549
|
}
|
|
14215
14550
|
}
|
|
14216
14551
|
createDefaultAgentConfig() {
|
|
14217
|
-
|
|
14552
|
+
const config = {
|
|
14218
14553
|
customSystemPrompt: void 0,
|
|
14219
14554
|
customModel: void 0,
|
|
14220
14555
|
customMaxTurns: void 0,
|
|
14221
|
-
customMcpServers: {},
|
|
14222
14556
|
customAllowedTools: [],
|
|
14223
|
-
|
|
14557
|
+
customPlugins: [],
|
|
14558
|
+
systemPromptMode: "append",
|
|
14559
|
+
customPRPromptTemplate: void 0,
|
|
14560
|
+
prPromptMode: "append"
|
|
14224
14561
|
};
|
|
14562
|
+
this.loadedAgentConfig = config;
|
|
14563
|
+
return config;
|
|
14225
14564
|
}
|
|
14226
14565
|
buildEnvironmentOverrides() {
|
|
14227
14566
|
return {
|
|
@@ -14329,18 +14668,20 @@ URL: ${result.pullRequestUrl}`
|
|
|
14329
14668
|
this.timerManager.stopTask("event");
|
|
14330
14669
|
},
|
|
14331
14670
|
onTaskMessage: async (message) => {
|
|
14332
|
-
if (message
|
|
14671
|
+
if (isAskUserResponseMessage(message)) {
|
|
14672
|
+
const [eventId, awaiter] = this.askUserAwaiter.entries().next().value || [];
|
|
14673
|
+
if (eventId && awaiter) {
|
|
14674
|
+
this.askUserAwaiter.delete(eventId);
|
|
14675
|
+
awaiter(message);
|
|
14676
|
+
}
|
|
14677
|
+
this.timerManager.clearIdleTimer();
|
|
14678
|
+
return;
|
|
14679
|
+
}
|
|
14680
|
+
if ("type" in message && message.type === "user") {
|
|
14333
14681
|
const userMessage = message;
|
|
14334
14682
|
await this.coordinator.enqueue(userMessage);
|
|
14335
14683
|
this.timerManager.clearIdleTimer();
|
|
14336
14684
|
}
|
|
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
14685
|
}
|
|
14345
14686
|
}
|
|
14346
14687
|
};
|
|
@@ -14379,8 +14720,7 @@ URL: ${result.pullRequestUrl}`
|
|
|
14379
14720
|
this.log("info", "GIT", `Patch: ${gitStateResult.patchPath}`);
|
|
14380
14721
|
}
|
|
14381
14722
|
}
|
|
14382
|
-
|
|
14383
|
-
const { createLogger } = await import('./logger-D-ioMWe6.mjs').then(function (n) { return n.b; });
|
|
14723
|
+
createWorkerLogger(options) {
|
|
14384
14724
|
return createLogger(options);
|
|
14385
14725
|
}
|
|
14386
14726
|
log(level, category, message, ...args) {
|
|
@@ -16119,6 +16459,7 @@ class CodexWorker {
|
|
|
16119
16459
|
};
|
|
16120
16460
|
this.coordinator = new MessageCoordinator({
|
|
16121
16461
|
workerType: "codex",
|
|
16462
|
+
workClient,
|
|
16122
16463
|
handlers: {
|
|
16123
16464
|
onNormalMessage: async (message) => {
|
|
16124
16465
|
const input = await this.convertSDKMessageToCodexInput(message);
|
|
@@ -16191,7 +16532,6 @@ class CodexWorker {
|
|
|
16191
16532
|
}
|
|
16192
16533
|
);
|
|
16193
16534
|
this.timerManager.startIdleTimer();
|
|
16194
|
-
this.context.workClient.sendWorkerReady();
|
|
16195
16535
|
this.log("info", "BASH", `Worker ready after command execution (exit code: ${exitCode})`);
|
|
16196
16536
|
}
|
|
16197
16537
|
async buildCreatePRPrompt() {
|
|
@@ -16248,8 +16588,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16248
16588
|
skipGitRepoCheck: true
|
|
16249
16589
|
});
|
|
16250
16590
|
}
|
|
16251
|
-
this.
|
|
16252
|
-
this.coordinator.setWorkerState("processing-sdk");
|
|
16591
|
+
this.coordinator.setWorkerState("running");
|
|
16253
16592
|
while (!this.isStopping) {
|
|
16254
16593
|
this.log("debug", "AGENT", `Loop iteration: turnCount=${this.turnCount}, queueLength=${this.messageQueue.length}`);
|
|
16255
16594
|
let userInput = null;
|
|
@@ -16261,7 +16600,6 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16261
16600
|
await this.reportGitState("idle");
|
|
16262
16601
|
this.coordinator.setWorkerState("idle");
|
|
16263
16602
|
this.timerManager.startIdleTimer();
|
|
16264
|
-
this.context.workClient.sendWorkerReady();
|
|
16265
16603
|
this.log("info", "AGENT", "Sent worker-ready, waiting for next message");
|
|
16266
16604
|
userInput = await this.waitForNextMessage();
|
|
16267
16605
|
}
|
|
@@ -16271,7 +16609,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16271
16609
|
}
|
|
16272
16610
|
try {
|
|
16273
16611
|
this.timerManager.clearIdleTimer();
|
|
16274
|
-
this.coordinator.setWorkerState("
|
|
16612
|
+
this.coordinator.setWorkerState("running");
|
|
16275
16613
|
await this.processUserInput(userInput);
|
|
16276
16614
|
this.turnCount++;
|
|
16277
16615
|
this.log("debug", "AGENT", `Message processed, turnCount now ${this.turnCount}`);
|
|
@@ -16307,7 +16645,7 @@ ${stats.files.map((f) => ` ${f.path}: +${f.insertions}/-${f.deletions}`).join("
|
|
|
16307
16645
|
const turnOptions = this.inMergeRequest ? { outputSchema: getPROutputSchema() } : {};
|
|
16308
16646
|
this.log("debug", "AGENT", "Calling thread.runStreamed");
|
|
16309
16647
|
const { events } = await this.thread.runStreamed(input, turnOptions);
|
|
16310
|
-
this.
|
|
16648
|
+
this.coordinator.setWorkerState("running");
|
|
16311
16649
|
this.log("debug", "AGENT", "Starting to process events");
|
|
16312
16650
|
for await (const event of events) {
|
|
16313
16651
|
if (this.isStopping) {
|
|
@@ -16598,7 +16936,7 @@ Type: ${mimeType}`
|
|
|
16598
16936
|
}
|
|
16599
16937
|
async handleStopHook() {
|
|
16600
16938
|
await this.reportGitState("stop");
|
|
16601
|
-
this.
|
|
16939
|
+
this.coordinator.setWorkerState("idle");
|
|
16602
16940
|
}
|
|
16603
16941
|
async loadAgentConfiguration() {
|
|
16604
16942
|
if (!this.options.input.agentId || this.options.input.agentId === "default") {
|
|
@@ -16690,14 +17028,15 @@ Type: ${mimeType}`
|
|
|
16690
17028
|
this.timerManager.stopTask("event");
|
|
16691
17029
|
},
|
|
16692
17030
|
onTaskMessage: async (message) => {
|
|
16693
|
-
if (message
|
|
17031
|
+
if (isAskUserResponseMessage(message)) {
|
|
17032
|
+
this.log("debug", "AGENT", "Received ask_user_response (not used by Codex)");
|
|
17033
|
+
return;
|
|
17034
|
+
}
|
|
17035
|
+
if ("type" in message && message.type === "user") {
|
|
16694
17036
|
const userMessage = message;
|
|
16695
17037
|
await this.coordinator.enqueue(userMessage);
|
|
16696
17038
|
this.timerManager.clearIdleTimer();
|
|
16697
17039
|
}
|
|
16698
|
-
},
|
|
16699
|
-
onPermissionResponse: async (response) => {
|
|
16700
|
-
this.log("debug", "AGENT", `Permission response received: ${response.behavior}`);
|
|
16701
17040
|
}
|
|
16702
17041
|
}
|
|
16703
17042
|
};
|
|
@@ -17052,7 +17391,7 @@ cli.command("upgrade", "Upgrade CLI to the latest version", {}, async (argv) =>
|
|
|
17052
17391
|
}
|
|
17053
17392
|
}
|
|
17054
17393
|
try {
|
|
17055
|
-
const { version } = await import('./logger-
|
|
17394
|
+
const { version } = await import('./logger-BzpMLIL-.mjs').then(function (n) { return n._; });
|
|
17056
17395
|
console.log(chalk.green(`
|
|
17057
17396
|
\u2713 Now running version: ${version}`));
|
|
17058
17397
|
} catch {
|
|
@@ -17234,7 +17573,7 @@ cli.command(
|
|
|
17234
17573
|
},
|
|
17235
17574
|
async (argv) => {
|
|
17236
17575
|
try {
|
|
17237
|
-
const { testCommand } = await import('./index-
|
|
17576
|
+
const { testCommand } = await import('./index-DNDJMHW8.mjs');
|
|
17238
17577
|
await testCommand(argv);
|
|
17239
17578
|
} catch (error) {
|
|
17240
17579
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Test mode failed");
|