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.
@@ -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 engineLaunch = {
3020
- command: spawnProgram,
3021
- args,
3022
- cwd: workspace,
3023
- category: resolution.spec ? resolution.spec.category : 'shell',
3024
- reviveState: options.reviveTerminalState || null,
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
- await this.createAgentEngineSession(agentRecord, resolution.engine, engineLaunch);
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,