happy-imou-cloud 2.1.49 → 2.1.51
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/AcpBackend-CqO3D07V.mjs +2619 -0
- package/dist/AcpBackend-XPiTd6ph.cjs +2621 -0
- package/dist/{BaseReasoningProcessor-Dn9NcoHz.cjs → BaseReasoningProcessor-BD9tiwep.cjs} +1 -144
- package/dist/{BaseReasoningProcessor-CAVeOdyo.mjs → BaseReasoningProcessor-CjlayL2f.mjs} +2 -144
- package/dist/ConversationHistory-Bl2doTA-.cjs +780 -0
- package/dist/ConversationHistory-CI5bBfuA.mjs +771 -0
- package/dist/{ProviderSelectionHandler-BJJc7qOR.cjs → ProviderSelectionHandler-C7GE5QjX.cjs} +6 -6
- package/dist/{ProviderSelectionHandler-DIYidT13.mjs → ProviderSelectionHandler-uQ8jzdzr.mjs} +2 -2
- package/dist/RuntimeShell-BDt42io_.mjs +252 -0
- package/dist/RuntimeShell-D_Te12wq.cjs +258 -0
- package/dist/bootstrapManagedProviderSession-Bln-TwyB.cjs +147 -0
- package/dist/bootstrapManagedProviderSession-D2Z6YU3n.mjs +145 -0
- package/dist/claude-BKNT-2fG.cjs +1080 -0
- package/dist/claude-CnN5WCWj.mjs +1073 -0
- package/dist/codex-DLGP8WF6.mjs +577 -0
- package/dist/codex-Fv2eali8.cjs +582 -0
- package/dist/{command-VcH4hbhi.cjs → command-BWPlJyCN.cjs} +16 -8
- package/dist/{command-CzfRRhVe.mjs → command-CELwsYoG.mjs} +15 -7
- package/dist/config-CFL0Gkqt.cjs +184 -0
- package/dist/config-ChSPe7p9.mjs +174 -0
- package/dist/createDefaultRuntimeShell-BXu3vCvT.cjs +33 -0
- package/dist/createDefaultRuntimeShell-DOg6g3-G.mjs +31 -0
- package/dist/cursor-Blq1cHdr.cjs +91 -0
- package/dist/cursor-CwPNSy_A.mjs +88 -0
- package/dist/future-Dq4Ha1Dn.cjs +24 -0
- package/dist/future-xRdLl3vf.mjs +22 -0
- package/dist/{index-xa1kwZoj.cjs → index-B_JYgMUS.cjs} +189 -5352
- package/dist/{index-7Z93BoVn.mjs → index-CX-F_fuk.mjs} +177 -5331
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/installFatalProcessHandlers-0vaw9MAz.mjs +55 -0
- package/dist/installFatalProcessHandlers-CyURn5Bp.cjs +57 -0
- package/dist/launch-BoCCEd5p.mjs +63 -0
- package/dist/launch-wZA5BcvS.cjs +66 -0
- package/dist/lib.cjs +2 -3
- package/dist/lib.d.cts +20 -17
- package/dist/lib.d.mts +20 -17
- package/dist/lib.mjs +1 -2
- package/dist/resolveCommand-B3BGyBE2.mjs +189 -0
- package/dist/resolveCommand-DYMd9PNC.cjs +193 -0
- package/dist/{runClaude-zCwRhpOw.mjs → runClaude-Be0myF9k.mjs} +8 -5
- package/dist/{runClaude-BBGNmGj6.cjs → runClaude-DZJt5er7.cjs} +46 -43
- package/dist/{runCodex-BbgLVjb9.mjs → runCodex-BSnyN4m7.mjs} +226 -117
- package/dist/{runCodex-jUU6U2tZ.cjs → runCodex-DTCcGRue.cjs} +269 -160
- package/dist/runCursor-Bn1PuwJy.cjs +506 -0
- package/dist/runCursor-M6dQ6bGF.mjs +504 -0
- package/dist/{runGemini-DcwNsudA.mjs → runGemini-BNm4vYKA.mjs} +279 -5
- package/dist/{runGemini-C0NT8MHK.cjs → runGemini-Bn3lFhz6.cjs} +309 -35
- package/dist/{registerKillSessionHandler-DLDg2EES.mjs → sessionControl-1bT_7OI6.mjs} +1643 -2405
- package/dist/{registerKillSessionHandler-CfCya6si.cjs → sessionControl-flKnQrx0.cjs} +1647 -2417
- package/dist/{api-DnqaNvyV.mjs → types-B5vtxa38.mjs} +55 -5
- package/dist/{api-D7nAeZi7.cjs → types-CttABk32.cjs} +55 -4
- package/package.json +2 -2
- package/dist/types-CiliQpqS.mjs +0 -52
- package/dist/types-DVk3crez.cjs +0 -54
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { B as BasePermissionHandler, d as
|
|
3
|
-
import {
|
|
1
|
+
import { m as preserveSessionRuntimeMetadata, l as logger, h as hashObject, d as AssistantMessageStream, b as connectionState, A as ApiClient } from './types-B5vtxa38.mjs';
|
|
2
|
+
import { B as BasePermissionHandler, d as buildHappyOrgTurnPrompt, w as waitForResponseCompleteWithAbort, f as finalizeHappyOrgTurnWithBusinessAck, r as registerKillSessionHandler, i as renderTerminalOutputPreview, p as prepareTerminalOutputForForwarding, j as inferToolResultError, h as forwardAgentMessageToProviderSession, e as ensureManagedProviderMachine, M as MissingMachineIdError, b as MessageQueue2, c as resolveHappyOrgQueuedTurn, s as syncControlledByUserState } from './sessionControl-1bT_7OI6.mjs';
|
|
3
|
+
import { b as closeProviderSession, s as stopCaffeinate, r as readManagedSessionTag, a as resolveManagedSessionTag } from './index-CX-F_fuk.mjs';
|
|
4
4
|
import 'cross-spawn';
|
|
5
5
|
import '@agentclientprotocol/sdk';
|
|
6
6
|
import { randomUUID } from 'node:crypto';
|
|
@@ -10,7 +10,7 @@ import 'path';
|
|
|
10
10
|
import 'os';
|
|
11
11
|
import 'child_process';
|
|
12
12
|
import fs from 'node:fs';
|
|
13
|
-
import { join,
|
|
13
|
+
import { join, normalize, basename, delimiter } from 'node:path';
|
|
14
14
|
import os from 'node:os';
|
|
15
15
|
import { spawn, execFileSync } from 'node:child_process';
|
|
16
16
|
import 'node:readline';
|
|
@@ -24,12 +24,19 @@ import 'tweetnacl';
|
|
|
24
24
|
import 'open';
|
|
25
25
|
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
26
26
|
import { useStdout, useInput, Box, Text, render } from 'ink';
|
|
27
|
-
import { c as createKeepAliveController, P as ProviderSelectionHandler, r as runModeLoop } from './ProviderSelectionHandler-
|
|
28
|
-
import { B as BaseReasoningProcessor, b as bootstrapManagedProviderSession } from './BaseReasoningProcessor-CAVeOdyo.mjs';
|
|
29
|
-
import 'zod';
|
|
27
|
+
import { c as createKeepAliveController, P as ProviderSelectionHandler, r as runModeLoop } from './ProviderSelectionHandler-uQ8jzdzr.mjs';
|
|
30
28
|
import 'socket.io-client';
|
|
31
29
|
import 'expo-server-sdk';
|
|
32
|
-
import './
|
|
30
|
+
import { M as MessageBuffer, C as ConversationHistory$1, b as buildTurnResultPushNotification, a as buildReadyPushNotification, l as launchRuntimeHandleWithFactoryResult, d as createSessionTranscriptInkRenderer, e as extractPermissionRequestPushContext, c as buildPermissionPushNotification } from './ConversationHistory-CI5bBfuA.mjs';
|
|
31
|
+
import { f as formatDisplayMessage, t as truncateDisplayMessage } from './RuntimeShell-BDt42io_.mjs';
|
|
32
|
+
import { A as AcpBackend } from './AcpBackend-CqO3D07V.mjs';
|
|
33
|
+
import { v as validateCodexAcpSpawn } from './resolveCommand-B3BGyBE2.mjs';
|
|
34
|
+
import { c as createCodexBackend, a as resolveCodexExecutable, s as shouldUseShellForCodex } from './codex-DLGP8WF6.mjs';
|
|
35
|
+
import { B as BaseReasoningProcessor } from './BaseReasoningProcessor-CjlayL2f.mjs';
|
|
36
|
+
import { F as Future } from './future-xRdLl3vf.mjs';
|
|
37
|
+
import { b as bootstrapManagedProviderSession } from './bootstrapManagedProviderSession-D2Z6YU3n.mjs';
|
|
38
|
+
import { i as installFatalProcessHandlers } from './installFatalProcessHandlers-0vaw9MAz.mjs';
|
|
39
|
+
import 'zod';
|
|
33
40
|
import 'qrcode-terminal';
|
|
34
41
|
import 'node:module';
|
|
35
42
|
import 'url';
|
|
@@ -238,12 +245,19 @@ class CodexSession {
|
|
|
238
245
|
this.sessionId = sessionId;
|
|
239
246
|
this.client.updateMetadata((metadata) => ({
|
|
240
247
|
...metadata,
|
|
241
|
-
codexSessionId: sessionId
|
|
248
|
+
codexSessionId: sessionId,
|
|
249
|
+
codexSessionCwd: this.path
|
|
242
250
|
}));
|
|
243
251
|
logger.debug(`[CodexSession] Session ID ${sessionId} added to metadata`);
|
|
244
252
|
};
|
|
245
253
|
clearSessionId = () => {
|
|
246
254
|
this.sessionId = null;
|
|
255
|
+
this.client.updateMetadata((metadata) => {
|
|
256
|
+
const nextMetadata = { ...metadata };
|
|
257
|
+
delete nextMetadata.codexSessionId;
|
|
258
|
+
delete nextMetadata.codexSessionCwd;
|
|
259
|
+
return nextMetadata;
|
|
260
|
+
});
|
|
247
261
|
logger.debug("[CodexSession] Session ID cleared");
|
|
248
262
|
};
|
|
249
263
|
handleUserObserverEphemeral = (event) => {
|
|
@@ -515,6 +529,113 @@ function getCodexExecutionFingerprint(mode) {
|
|
|
515
529
|
});
|
|
516
530
|
}
|
|
517
531
|
|
|
532
|
+
function getCodexSessionsRoot(options = {}) {
|
|
533
|
+
const codexHomeDir = options.codexHomeDir || process.env.CODEX_HOME || join(os.homedir(), ".codex");
|
|
534
|
+
return join(codexHomeDir, "sessions");
|
|
535
|
+
}
|
|
536
|
+
function collectFilesRecursive(dir, acc = []) {
|
|
537
|
+
let entries;
|
|
538
|
+
try {
|
|
539
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
540
|
+
} catch {
|
|
541
|
+
return acc;
|
|
542
|
+
}
|
|
543
|
+
for (const entry of entries) {
|
|
544
|
+
const full = join(dir, entry.name);
|
|
545
|
+
if (entry.isDirectory()) {
|
|
546
|
+
collectFilesRecursive(full, acc);
|
|
547
|
+
} else if (entry.isFile()) {
|
|
548
|
+
acc.push(full);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return acc;
|
|
552
|
+
}
|
|
553
|
+
function extractCodexSessionIdFromPath(filePath) {
|
|
554
|
+
const match = filePath.match(/-([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\.jsonl$/);
|
|
555
|
+
return match?.[1] ?? null;
|
|
556
|
+
}
|
|
557
|
+
function normalizePathForSessionMatch(path) {
|
|
558
|
+
const normalizedPath = normalize(path).replace(/[\\/]+/g, "/").replace(/\/+$/, "");
|
|
559
|
+
return process.platform === "win32" ? normalizedPath.toLowerCase() : normalizedPath;
|
|
560
|
+
}
|
|
561
|
+
function extractCodexSessionCwdFromFile(filePath) {
|
|
562
|
+
let contents;
|
|
563
|
+
try {
|
|
564
|
+
contents = fs.readFileSync(filePath, "utf8");
|
|
565
|
+
} catch {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
569
|
+
if (!line.trim()) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
let record;
|
|
573
|
+
try {
|
|
574
|
+
record = JSON.parse(line);
|
|
575
|
+
} catch {
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (record.type !== "session_meta" || typeof record.payload !== "object" || record.payload === null) {
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
const cwd = record.payload.cwd;
|
|
582
|
+
return typeof cwd === "string" && cwd.trim().length > 0 ? cwd : null;
|
|
583
|
+
}
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
function codexSessionFileMatchesCwd(filePath, cwd) {
|
|
587
|
+
const expectedCwd = cwd?.trim();
|
|
588
|
+
if (!expectedCwd) {
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
const actualCwd = extractCodexSessionCwdFromFile(filePath);
|
|
592
|
+
if (!actualCwd) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
return normalizePathForSessionMatch(actualCwd) === normalizePathForSessionMatch(expectedCwd);
|
|
596
|
+
}
|
|
597
|
+
function findCodexResumeFile(sessionId, options = {}) {
|
|
598
|
+
if (!sessionId) return null;
|
|
599
|
+
try {
|
|
600
|
+
const candidates = collectFilesRecursive(getCodexSessionsRoot(options)).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
|
|
601
|
+
try {
|
|
602
|
+
return fs.statSync(full).isFile();
|
|
603
|
+
} catch {
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
}).filter((full) => codexSessionFileMatchesCwd(full, options.cwd)).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
607
|
+
return candidates[0] || null;
|
|
608
|
+
} catch {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function isCodexResumeSessionValidForCwd(sessionId, cwd, options = {}) {
|
|
613
|
+
if (!sessionId?.trim()) {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
return findCodexResumeFile(sessionId, { ...options, cwd }) !== null;
|
|
617
|
+
}
|
|
618
|
+
function findLatestCodexSessionIdSince(startTimeMs, options = {}) {
|
|
619
|
+
try {
|
|
620
|
+
const candidates = collectFilesRecursive(getCodexSessionsRoot(options)).filter((full) => full.endsWith(".jsonl")).filter((full) => {
|
|
621
|
+
try {
|
|
622
|
+
return fs.statSync(full).mtimeMs >= startTimeMs;
|
|
623
|
+
} catch {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
}).filter((full) => codexSessionFileMatchesCwd(full, options.cwd)).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
627
|
+
for (const candidate of candidates) {
|
|
628
|
+
const sessionId = extractCodexSessionIdFromPath(candidate);
|
|
629
|
+
if (sessionId) {
|
|
630
|
+
return sessionId;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
|
|
518
639
|
function delay(ms) {
|
|
519
640
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
520
641
|
}
|
|
@@ -690,6 +811,17 @@ function shouldInjectLargeOutputRecoveryHint(error) {
|
|
|
690
811
|
].filter(Boolean).join("\n").toLowerCase();
|
|
691
812
|
return searchableText.includes("memory allocation of") || searchableText.includes("out of memory") || searchableText.includes("oversized command output");
|
|
692
813
|
}
|
|
814
|
+
function isCodexUnknownResumeSessionError(error) {
|
|
815
|
+
const record = typeof error === "object" && error !== null ? error : null;
|
|
816
|
+
const searchableText = [
|
|
817
|
+
formatDisplayMessage(error).trim(),
|
|
818
|
+
record ? formatDisplayMessage(record.message).trim() : "",
|
|
819
|
+
record ? formatDisplayMessage(record.stderr).trim() : "",
|
|
820
|
+
record ? formatDisplayMessage(record.detail).trim() : "",
|
|
821
|
+
record ? formatDisplayMessage(record.data).trim() : ""
|
|
822
|
+
].filter(Boolean).join("\n");
|
|
823
|
+
return /unknown (session|thread)|session .* not found|thread .* not found|conversation .* not found|missing rollout path for thread|state db missing rollout path|no rollout found for thread id|resource not found/i.test(searchableText);
|
|
824
|
+
}
|
|
693
825
|
function normalizeCodexBackendError(error) {
|
|
694
826
|
const record = typeof error === "object" && error !== null ? error : null;
|
|
695
827
|
const text = formatDisplayMessage(error).trim();
|
|
@@ -1173,6 +1305,11 @@ async function codexRemoteLauncher(session) {
|
|
|
1173
1305
|
};
|
|
1174
1306
|
const createRuntimeHandle = async (mode) => {
|
|
1175
1307
|
const executionMode = resolveCodexAcpExecutionMode(mode);
|
|
1308
|
+
const resumeSessionId = session.sessionId && isCodexResumeSessionValidForCwd(session.sessionId, session.path) ? session.sessionId : null;
|
|
1309
|
+
if (session.sessionId && !resumeSessionId) {
|
|
1310
|
+
logger.debug(`[Codex] Refusing to resume Codex session ${session.sessionId} because its local session file does not match cwd ${session.path}`);
|
|
1311
|
+
session.clearSessionId();
|
|
1312
|
+
}
|
|
1176
1313
|
const validation = validateCodexAcpSpawn({
|
|
1177
1314
|
command: process.env.HAPPY_CODEX_ACP_COMMAND,
|
|
1178
1315
|
model: executionMode.model,
|
|
@@ -1193,7 +1330,7 @@ async function codexRemoteLauncher(session) {
|
|
|
1193
1330
|
sandbox: executionMode.sandbox,
|
|
1194
1331
|
approvalPolicy: executionMode.approvalPolicy,
|
|
1195
1332
|
permissionHandler,
|
|
1196
|
-
resumeSessionId
|
|
1333
|
+
resumeSessionId,
|
|
1197
1334
|
selectionHandler: {
|
|
1198
1335
|
handleSelection: (request) => selectionHandler.requestSelection(request)
|
|
1199
1336
|
}
|
|
@@ -1344,12 +1481,31 @@ async function codexRemoteLauncher(session) {
|
|
|
1344
1481
|
const turnSignal = abortController.signal;
|
|
1345
1482
|
let sentTurnResultPush = false;
|
|
1346
1483
|
let turnStatus = "task_complete";
|
|
1484
|
+
let retryingWithFreshSession = false;
|
|
1347
1485
|
currentHappyOrgTurn = message.mode.happyOrg ?? null;
|
|
1348
1486
|
try {
|
|
1349
1487
|
turnInFlight = true;
|
|
1350
1488
|
shouldCommitAccumulatedResponse = false;
|
|
1351
1489
|
const isNewRuntimeSession = runtimeHandle === null;
|
|
1352
|
-
const
|
|
1490
|
+
const attemptedResumeSessionId = isNewRuntimeSession ? session.sessionId : null;
|
|
1491
|
+
let activeRuntimeHandle;
|
|
1492
|
+
try {
|
|
1493
|
+
activeRuntimeHandle = runtimeHandle ?? await createRuntimeHandle(message.mode);
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
if (!isNewRuntimeSession || !attemptedResumeSessionId || !isCodexUnknownResumeSessionError(error) || turnSignal.aborted) {
|
|
1496
|
+
throw error;
|
|
1497
|
+
}
|
|
1498
|
+
logger.debug(`[Codex] Resume session ${attemptedResumeSessionId} failed during ACP startup; retrying with a fresh session`, error);
|
|
1499
|
+
queueHistoryInjectionForRestart("Codex resume session is unavailable. Retrying with a fresh Codex session...");
|
|
1500
|
+
session.clearSessionId();
|
|
1501
|
+
currentModeHash = null;
|
|
1502
|
+
permissionHandler.reset();
|
|
1503
|
+
selectionHandler.reset();
|
|
1504
|
+
resetTurnState();
|
|
1505
|
+
pending = message;
|
|
1506
|
+
retryingWithFreshSession = true;
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1353
1509
|
if (!activeRuntimeHandle) {
|
|
1354
1510
|
throw new Error("Failed to create Codex ACP backend");
|
|
1355
1511
|
}
|
|
@@ -1376,8 +1532,25 @@ ${message.message}` : message.message;
|
|
|
1376
1532
|
promptToSend = buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
|
|
1377
1533
|
}
|
|
1378
1534
|
conversationHistory.addUserMessage(message.message);
|
|
1379
|
-
|
|
1380
|
-
|
|
1535
|
+
try {
|
|
1536
|
+
await activeRuntimeHandle.sendPrompt(promptToSend);
|
|
1537
|
+
await waitForResponseCompleteWithAbort(activeRuntimeHandle.backend, turnSignal);
|
|
1538
|
+
} catch (error) {
|
|
1539
|
+
if (!isNewRuntimeSession || !attemptedResumeSessionId || !isCodexUnknownResumeSessionError(error) || turnSignal.aborted) {
|
|
1540
|
+
throw error;
|
|
1541
|
+
}
|
|
1542
|
+
logger.debug(`[Codex] Resume session ${attemptedResumeSessionId} is unavailable; retrying with a fresh ACP session`, error);
|
|
1543
|
+
queueHistoryInjectionForRestart("Codex resume session is unavailable. Retrying with a fresh Codex session...");
|
|
1544
|
+
await disposeRuntimeHandle();
|
|
1545
|
+
session.clearSessionId();
|
|
1546
|
+
currentModeHash = null;
|
|
1547
|
+
permissionHandler.reset();
|
|
1548
|
+
selectionHandler.reset();
|
|
1549
|
+
resetTurnState();
|
|
1550
|
+
pending = message;
|
|
1551
|
+
retryingWithFreshSession = true;
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1381
1554
|
reasoningProcessor.completeCurrent();
|
|
1382
1555
|
shouldCommitAccumulatedResponse = true;
|
|
1383
1556
|
shouldInjectHistoryOnNextSession = false;
|
|
@@ -1407,6 +1580,9 @@ ${message.message}` : message.message;
|
|
|
1407
1580
|
}
|
|
1408
1581
|
} finally {
|
|
1409
1582
|
turnInFlight = false;
|
|
1583
|
+
if (retryingWithFreshSession) {
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1410
1586
|
const finalizedTurn = await finalizeHappyOrgTurnWithBusinessAck({
|
|
1411
1587
|
metadata: session.runtimeSession.getMetadataSnapshot?.() ?? null,
|
|
1412
1588
|
queuedTurn: message.mode.happyOrg,
|
|
@@ -1502,52 +1678,6 @@ ${message.message}` : message.message;
|
|
|
1502
1678
|
return shouldSwitchToLocal ? "switch" : "exit";
|
|
1503
1679
|
}
|
|
1504
1680
|
|
|
1505
|
-
function getCodexSessionsRoot(options = {}) {
|
|
1506
|
-
const codexHomeDir = options.codexHomeDir || process.env.CODEX_HOME || join(os.homedir(), ".codex");
|
|
1507
|
-
return join(codexHomeDir, "sessions");
|
|
1508
|
-
}
|
|
1509
|
-
function collectFilesRecursive(dir, acc = []) {
|
|
1510
|
-
let entries;
|
|
1511
|
-
try {
|
|
1512
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1513
|
-
} catch {
|
|
1514
|
-
return acc;
|
|
1515
|
-
}
|
|
1516
|
-
for (const entry of entries) {
|
|
1517
|
-
const full = join(dir, entry.name);
|
|
1518
|
-
if (entry.isDirectory()) {
|
|
1519
|
-
collectFilesRecursive(full, acc);
|
|
1520
|
-
} else if (entry.isFile()) {
|
|
1521
|
-
acc.push(full);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
return acc;
|
|
1525
|
-
}
|
|
1526
|
-
function extractCodexSessionIdFromPath(filePath) {
|
|
1527
|
-
const match = filePath.match(/-([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\.jsonl$/);
|
|
1528
|
-
return match?.[1] ?? null;
|
|
1529
|
-
}
|
|
1530
|
-
function findLatestCodexSessionIdSince(startTimeMs, options = {}) {
|
|
1531
|
-
try {
|
|
1532
|
-
const candidates = collectFilesRecursive(getCodexSessionsRoot(options)).filter((full) => full.endsWith(".jsonl")).filter((full) => {
|
|
1533
|
-
try {
|
|
1534
|
-
return fs.statSync(full).mtimeMs >= startTimeMs;
|
|
1535
|
-
} catch {
|
|
1536
|
-
return false;
|
|
1537
|
-
}
|
|
1538
|
-
}).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
1539
|
-
for (const candidate of candidates) {
|
|
1540
|
-
const sessionId = extractCodexSessionIdFromPath(candidate);
|
|
1541
|
-
if (sessionId) {
|
|
1542
|
-
return sessionId;
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
} catch {
|
|
1546
|
-
return null;
|
|
1547
|
-
}
|
|
1548
|
-
return null;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
1681
|
const MANAGED_CODEX_HOME_PREFIX = "happy-codex-home-";
|
|
1552
1682
|
function isManagedCodexHomePath(value) {
|
|
1553
1683
|
const trimmed = value?.trim();
|
|
@@ -1614,12 +1744,17 @@ function terminateCodexLocalChild(pid) {
|
|
|
1614
1744
|
}
|
|
1615
1745
|
async function codexLocal(opts) {
|
|
1616
1746
|
const baseArgs = [...opts.codexArgs ?? []];
|
|
1617
|
-
const args = opts.sessionId ? ["resume", opts.sessionId, ...baseArgs] : baseArgs;
|
|
1618
|
-
const startTime = Date.now();
|
|
1619
|
-
let detectedSessionId = opts.sessionId;
|
|
1620
|
-
const codexExecutable = resolveCodexExecutable();
|
|
1621
1747
|
const codexEnv = buildCodexLocalEnv(process.env);
|
|
1622
1748
|
const codexHomeDir = resolveCodexLocalHomeDir(codexEnv);
|
|
1749
|
+
const resumeSessionId = opts.sessionId && isCodexResumeSessionValidForCwd(opts.sessionId, opts.path, { codexHomeDir }) ? opts.sessionId : null;
|
|
1750
|
+
if (opts.sessionId && !resumeSessionId) {
|
|
1751
|
+
logger.debug(`[CodexLocal] Refusing to resume Codex session ${opts.sessionId} because its local session file does not match cwd ${opts.path}`);
|
|
1752
|
+
opts.onResumeSessionInvalid?.(opts.sessionId);
|
|
1753
|
+
}
|
|
1754
|
+
const args = resumeSessionId ? ["resume", resumeSessionId, ...baseArgs] : baseArgs;
|
|
1755
|
+
const startTime = Date.now();
|
|
1756
|
+
let detectedSessionId = resumeSessionId;
|
|
1757
|
+
const codexExecutable = resolveCodexExecutable();
|
|
1623
1758
|
logger.debug(`[CodexLocal] Spawning ${codexExecutable} with args: ${JSON.stringify(args)}`);
|
|
1624
1759
|
releaseTerminalInputForChild();
|
|
1625
1760
|
clearTerminalForChild();
|
|
@@ -1645,7 +1780,7 @@ async function codexLocal(opts) {
|
|
|
1645
1780
|
if (detectedSessionId) {
|
|
1646
1781
|
return;
|
|
1647
1782
|
}
|
|
1648
|
-
const nextSessionId = findLatestCodexSessionIdSince(startTime, { codexHomeDir });
|
|
1783
|
+
const nextSessionId = findLatestCodexSessionIdSince(startTime, { codexHomeDir, cwd: opts.path });
|
|
1649
1784
|
if (nextSessionId) {
|
|
1650
1785
|
detectedSessionId = nextSessionId;
|
|
1651
1786
|
logger.debug(`[CodexLocal] Detected session ID: ${nextSessionId}`);
|
|
@@ -1788,6 +1923,7 @@ async function codexLocalLauncher(session) {
|
|
|
1788
1923
|
abort: processAbortController.signal,
|
|
1789
1924
|
codexArgs: session.codexArgs,
|
|
1790
1925
|
onSessionFound: recordSessionFound,
|
|
1926
|
+
onResumeSessionInvalid: session.clearSessionId,
|
|
1791
1927
|
onThinkingChange: session.onThinkingChange
|
|
1792
1928
|
});
|
|
1793
1929
|
if (sessionId) {
|
|
@@ -1861,58 +1997,6 @@ async function codexLoop(opts) {
|
|
|
1861
1997
|
});
|
|
1862
1998
|
}
|
|
1863
1999
|
|
|
1864
|
-
function normalizeFatalProcessError(error) {
|
|
1865
|
-
if (error instanceof Error) {
|
|
1866
|
-
return error;
|
|
1867
|
-
}
|
|
1868
|
-
const message = typeof error === "string" ? error : (() => {
|
|
1869
|
-
try {
|
|
1870
|
-
const serialized = JSON.stringify(error);
|
|
1871
|
-
return serialized && serialized !== "{}" ? serialized : String(error);
|
|
1872
|
-
} catch {
|
|
1873
|
-
return String(error);
|
|
1874
|
-
}
|
|
1875
|
-
})();
|
|
1876
|
-
return new Error(message || "Unknown fatal process error");
|
|
1877
|
-
}
|
|
1878
|
-
function installFatalProcessHandlers(options) {
|
|
1879
|
-
const target = options.target ?? process;
|
|
1880
|
-
const exit = options.exit ?? process.exit;
|
|
1881
|
-
let shuttingDown = false;
|
|
1882
|
-
let disposed = false;
|
|
1883
|
-
const handleFatalEvent = async (event, reason) => {
|
|
1884
|
-
const error = normalizeFatalProcessError(reason);
|
|
1885
|
-
logger.debug(`[${options.label}] Fatal ${event}`, error);
|
|
1886
|
-
if (shuttingDown) {
|
|
1887
|
-
logger.debug(`[${options.label}] Ignoring duplicate fatal ${event} while shutdown is already in progress`);
|
|
1888
|
-
return;
|
|
1889
|
-
}
|
|
1890
|
-
shuttingDown = true;
|
|
1891
|
-
try {
|
|
1892
|
-
await options.cleanup({ event, error });
|
|
1893
|
-
} catch (cleanupError) {
|
|
1894
|
-
logger.debug(`[${options.label}] Fatal cleanup failed`, cleanupError);
|
|
1895
|
-
}
|
|
1896
|
-
exit(1);
|
|
1897
|
-
};
|
|
1898
|
-
const onUncaughtException = (error) => {
|
|
1899
|
-
void handleFatalEvent("uncaughtException", error);
|
|
1900
|
-
};
|
|
1901
|
-
const onUnhandledRejection = (reason) => {
|
|
1902
|
-
void handleFatalEvent("unhandledRejection", reason);
|
|
1903
|
-
};
|
|
1904
|
-
target.on("uncaughtException", onUncaughtException);
|
|
1905
|
-
target.on("unhandledRejection", onUnhandledRejection);
|
|
1906
|
-
return () => {
|
|
1907
|
-
if (disposed) {
|
|
1908
|
-
return;
|
|
1909
|
-
}
|
|
1910
|
-
disposed = true;
|
|
1911
|
-
target.off("uncaughtException", onUncaughtException);
|
|
1912
|
-
target.off("unhandledRejection", onUnhandledRejection);
|
|
1913
|
-
};
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
2000
|
function normalizeResumeSessionId(value) {
|
|
1917
2001
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1918
2002
|
}
|
|
@@ -1957,6 +2041,17 @@ function normalizeCodexArgsForHappy(options) {
|
|
|
1957
2041
|
};
|
|
1958
2042
|
}
|
|
1959
2043
|
|
|
2044
|
+
function normalizePathForCodexResumeIdentity(path) {
|
|
2045
|
+
const normalized = path.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "");
|
|
2046
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
2047
|
+
}
|
|
2048
|
+
function codexResumeCwdMatches(expected, actual) {
|
|
2049
|
+
const normalizedExpected = expected?.trim();
|
|
2050
|
+
if (!normalizedExpected) {
|
|
2051
|
+
return true;
|
|
2052
|
+
}
|
|
2053
|
+
return normalizePathForCodexResumeIdentity(normalizedExpected) === normalizePathForCodexResumeIdentity(actual);
|
|
2054
|
+
}
|
|
1960
2055
|
function resolveInitialCodexPermissionMode(opts) {
|
|
1961
2056
|
if (opts.permissionMode) {
|
|
1962
2057
|
return opts.permissionMode;
|
|
@@ -2056,6 +2151,20 @@ async function runCodex(opts) {
|
|
|
2056
2151
|
}
|
|
2057
2152
|
});
|
|
2058
2153
|
sessionClient = initialSession;
|
|
2154
|
+
const metadataCodexSessionCwd = typeof metadata.codexSessionCwd === "string" ? metadata.codexSessionCwd : null;
|
|
2155
|
+
const requestedResumeSessionId = normalizedArgs.resumeSessionId;
|
|
2156
|
+
const requestedResumeCwd = opts.resumeSessionCwd ?? metadataCodexSessionCwd;
|
|
2157
|
+
const resumeSessionMatchesCwd = requestedResumeSessionId ? codexResumeCwdMatches(requestedResumeCwd, metadata.path) && isCodexResumeSessionValidForCwd(requestedResumeSessionId, metadata.path) : false;
|
|
2158
|
+
const initialCodexSessionId = requestedResumeSessionId && resumeSessionMatchesCwd ? requestedResumeSessionId : null;
|
|
2159
|
+
if (requestedResumeSessionId && !resumeSessionMatchesCwd) {
|
|
2160
|
+
logger.debug(`[codex] Ignoring resume session ${requestedResumeSessionId} because it does not match cwd ${metadata.path}`);
|
|
2161
|
+
sessionClient.updateMetadata((currentMetadata) => {
|
|
2162
|
+
const nextMetadata = { ...currentMetadata };
|
|
2163
|
+
delete nextMetadata.codexSessionId;
|
|
2164
|
+
delete nextMetadata.codexSessionCwd;
|
|
2165
|
+
return nextMetadata;
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2059
2168
|
const messageQueue = new MessageQueue2(getCodexExecutionFingerprint);
|
|
2060
2169
|
let currentPermissionMode = initialPermissionMode;
|
|
2061
2170
|
let currentModel;
|
|
@@ -2102,7 +2211,7 @@ async function runCodex(opts) {
|
|
|
2102
2211
|
path: metadata.path,
|
|
2103
2212
|
logPath: logger.logFilePath,
|
|
2104
2213
|
startupRolePrompt: happyOrgStartupBinding?.identityPrompt ?? null,
|
|
2105
|
-
sessionId:
|
|
2214
|
+
sessionId: initialCodexSessionId,
|
|
2106
2215
|
mode: requestedStartingMode,
|
|
2107
2216
|
messageQueue,
|
|
2108
2217
|
codexArgs: normalizedArgs.codexArgs,
|