farming-code 2.2.17 → 2.2.18
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/backend/agent-manager.js
CHANGED
|
@@ -79,6 +79,9 @@ const AGENT_DISCOVERY_CACHE_MAX_AGE_MS = 3_000;
|
|
|
79
79
|
const UNCERTAIN_TERMINAL_STOP_TIMEOUT_MS = 5_000;
|
|
80
80
|
const TERMINAL_STOP_STATE_READ_TIMEOUT_MS = 1_000;
|
|
81
81
|
const TERMINAL_STOP_POLL_MS = 50;
|
|
82
|
+
const CODEX_TERMINAL_START_READY_TIMEOUT_MS = 30_000;
|
|
83
|
+
const CODEX_TERMINAL_START_READY_POLL_MS = 50;
|
|
84
|
+
const CODEX_TERMINAL_START_OUTPUT_LIMIT = 64 * 1024;
|
|
82
85
|
const SHELL_PROMPT_ENV_KEYS = [
|
|
83
86
|
'PS1',
|
|
84
87
|
'PS2',
|
|
@@ -100,6 +103,15 @@ function withBoundedWait(promise, timeoutMs, label) {
|
|
|
100
103
|
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
function canonicalProviderHomePath(value) {
|
|
107
|
+
const resolved = path.resolve(String(value || '').trim() || '.');
|
|
108
|
+
try {
|
|
109
|
+
return fs.realpathSync.native(resolved);
|
|
110
|
+
} catch {
|
|
111
|
+
return resolved;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
103
115
|
async function deletePrecreatedProviderSession(options = {}) {
|
|
104
116
|
const provider = String(options.provider || '').trim().toLowerCase();
|
|
105
117
|
const sessionId = String(options.sessionId || '').trim();
|
|
@@ -591,6 +603,8 @@ class AgentManager extends EventEmitter {
|
|
|
591
603
|
this.resizeDrains = new Map();
|
|
592
604
|
this.inputQueues = new Map();
|
|
593
605
|
this.codexTerminalProfileQueues = new Map();
|
|
606
|
+
this.codexTerminalStartQueues = new Map();
|
|
607
|
+
this.codexTerminalStartOutput = new Map();
|
|
594
608
|
this.agentWorktreeResolveGeneration = new Map();
|
|
595
609
|
this.permissionRestartInFlight = new Map();
|
|
596
610
|
this.runtimeRestartInFlight = new Map();
|
|
@@ -757,6 +771,13 @@ class AgentManager extends EventEmitter {
|
|
|
757
771
|
|
|
758
772
|
this.reviveAgentRuntime(agent);
|
|
759
773
|
agent.output = trimSessionOutput(agent.output + data);
|
|
774
|
+
if (this.codexTerminalStartOutput.has(sessionId)) {
|
|
775
|
+
const startupOutput = `${this.codexTerminalStartOutput.get(sessionId) || ''}${data}`;
|
|
776
|
+
this.codexTerminalStartOutput.set(
|
|
777
|
+
sessionId,
|
|
778
|
+
startupOutput.slice(-CODEX_TERMINAL_START_OUTPUT_LIMIT),
|
|
779
|
+
);
|
|
780
|
+
}
|
|
760
781
|
if (agent.providerSessionProvider === 'codex' && agent.providerSessionTemporary === true) {
|
|
761
782
|
void this.providerSessionService.resolveTemporaryCodex(sessionId);
|
|
762
783
|
}
|
|
@@ -2264,6 +2285,8 @@ class AgentManager extends EventEmitter {
|
|
|
2264
2285
|
this.resizeDrains.clear();
|
|
2265
2286
|
this.inputQueues.clear();
|
|
2266
2287
|
this.codexTerminalProfileQueues.clear();
|
|
2288
|
+
this.codexTerminalStartQueues.clear();
|
|
2289
|
+
this.codexTerminalStartOutput.clear();
|
|
2267
2290
|
this.acpSessionOptionsByKey.clear();
|
|
2268
2291
|
if (this.jsonCliRuntime) {
|
|
2269
2292
|
for (const agentId of this.agents.keys()) this.jsonCliRuntime.unregisterAgent(agentId);
|
|
@@ -2373,6 +2396,37 @@ class AgentManager extends EventEmitter {
|
|
|
2373
2396
|
throw new Error(`Terminal ${agentId} did not reach an exited state after kill`);
|
|
2374
2397
|
}
|
|
2375
2398
|
|
|
2399
|
+
async waitForCodexTerminalStart(agentId) {
|
|
2400
|
+
const deadline = Date.now() + CODEX_TERMINAL_START_READY_TIMEOUT_MS;
|
|
2401
|
+
while (Date.now() <= deadline) {
|
|
2402
|
+
const agent = this.agents.get(agentId);
|
|
2403
|
+
if (!agent) throw new Error(`Codex Terminal ${agentId} disappeared during startup`);
|
|
2404
|
+
if (['dead', 'stopped'].includes(agent.status) || agent.engineStatus === 'exited') {
|
|
2405
|
+
const detail = trimSessionOutput(agent.previewText || agent.output || '').trim();
|
|
2406
|
+
throw new Error(detail || `Codex Terminal ${agentId} exited during startup`);
|
|
2407
|
+
}
|
|
2408
|
+
if ((this.codexTerminalStartOutput.get(agentId) || '').includes('\u001b')) {
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
await new Promise(resolve => setTimeout(resolve, CODEX_TERMINAL_START_READY_POLL_MS));
|
|
2412
|
+
}
|
|
2413
|
+
throw new Error(`Codex Terminal ${agentId} did not become ready within ${CODEX_TERMINAL_START_READY_TIMEOUT_MS}ms`);
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
async enqueueCodexTerminalStart(providerHomeKey, operation) {
|
|
2417
|
+
const key = canonicalProviderHomePath(providerHomeKey || '.');
|
|
2418
|
+
const previous = this.codexTerminalStartQueues.get(key) || Promise.resolve();
|
|
2419
|
+
const next = previous.catch(() => {}).then(operation);
|
|
2420
|
+
this.codexTerminalStartQueues.set(key, next);
|
|
2421
|
+
try {
|
|
2422
|
+
return await next;
|
|
2423
|
+
} finally {
|
|
2424
|
+
if (this.codexTerminalStartQueues.get(key) === next) {
|
|
2425
|
+
this.codexTerminalStartQueues.delete(key);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2376
2430
|
async startAgent(command, customWorkspace, callback, options = {}) {
|
|
2377
2431
|
if (options.wantsMain !== false && options.skipRecoveryWait !== true) {
|
|
2378
2432
|
await this.whenRecovered();
|
|
@@ -3016,14 +3070,34 @@ class AgentManager extends EventEmitter {
|
|
|
3016
3070
|
}
|
|
3017
3071
|
|
|
3018
3072
|
if (!useJsonCli && !useAcp) {
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3073
|
+
const serializeCodexStartup = agentRecord.providerSessionProvider === 'codex'
|
|
3074
|
+
&& resolution.engineName === 'native';
|
|
3075
|
+
const startTerminal = async () => {
|
|
3076
|
+
const engineLaunch = {
|
|
3077
|
+
command: spawnProgram,
|
|
3078
|
+
args,
|
|
3079
|
+
cwd: workspace,
|
|
3080
|
+
category: resolution.spec ? resolution.spec.category : 'shell',
|
|
3081
|
+
reviveState: options.reviveTerminalState || null,
|
|
3082
|
+
};
|
|
3083
|
+
if (serializeCodexStartup) this.codexTerminalStartOutput.set(agentId, '');
|
|
3084
|
+
try {
|
|
3085
|
+
await this.createAgentEngineSession(agentRecord, resolution.engine, engineLaunch);
|
|
3086
|
+
if (serializeCodexStartup) {
|
|
3087
|
+
await this.waitForCodexTerminalStart(agentId);
|
|
3088
|
+
}
|
|
3089
|
+
} finally {
|
|
3090
|
+
if (serializeCodexStartup) this.codexTerminalStartOutput.delete(agentId);
|
|
3091
|
+
}
|
|
3025
3092
|
};
|
|
3026
|
-
|
|
3093
|
+
if (serializeCodexStartup) {
|
|
3094
|
+
await this.enqueueCodexTerminalStart(
|
|
3095
|
+
providerHomePath || resolvedProviderHomeId || 'default',
|
|
3096
|
+
startTerminal
|
|
3097
|
+
);
|
|
3098
|
+
} else {
|
|
3099
|
+
await startTerminal();
|
|
3100
|
+
}
|
|
3027
3101
|
}
|
|
3028
3102
|
agentRecord.persistentSessionId = this.ensurePersistentAgentSession(agentRecord, {
|
|
3029
3103
|
visibleOnMainPage: true,
|