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.
Files changed (55) hide show
  1. package/dist/AcpBackend-CqO3D07V.mjs +2619 -0
  2. package/dist/AcpBackend-XPiTd6ph.cjs +2621 -0
  3. package/dist/{BaseReasoningProcessor-Dn9NcoHz.cjs → BaseReasoningProcessor-BD9tiwep.cjs} +1 -144
  4. package/dist/{BaseReasoningProcessor-CAVeOdyo.mjs → BaseReasoningProcessor-CjlayL2f.mjs} +2 -144
  5. package/dist/ConversationHistory-Bl2doTA-.cjs +780 -0
  6. package/dist/ConversationHistory-CI5bBfuA.mjs +771 -0
  7. package/dist/{ProviderSelectionHandler-BJJc7qOR.cjs → ProviderSelectionHandler-C7GE5QjX.cjs} +6 -6
  8. package/dist/{ProviderSelectionHandler-DIYidT13.mjs → ProviderSelectionHandler-uQ8jzdzr.mjs} +2 -2
  9. package/dist/RuntimeShell-BDt42io_.mjs +252 -0
  10. package/dist/RuntimeShell-D_Te12wq.cjs +258 -0
  11. package/dist/bootstrapManagedProviderSession-Bln-TwyB.cjs +147 -0
  12. package/dist/bootstrapManagedProviderSession-D2Z6YU3n.mjs +145 -0
  13. package/dist/claude-BKNT-2fG.cjs +1080 -0
  14. package/dist/claude-CnN5WCWj.mjs +1073 -0
  15. package/dist/codex-DLGP8WF6.mjs +577 -0
  16. package/dist/codex-Fv2eali8.cjs +582 -0
  17. package/dist/{command-VcH4hbhi.cjs → command-BWPlJyCN.cjs} +16 -8
  18. package/dist/{command-CzfRRhVe.mjs → command-CELwsYoG.mjs} +15 -7
  19. package/dist/config-CFL0Gkqt.cjs +184 -0
  20. package/dist/config-ChSPe7p9.mjs +174 -0
  21. package/dist/createDefaultRuntimeShell-BXu3vCvT.cjs +33 -0
  22. package/dist/createDefaultRuntimeShell-DOg6g3-G.mjs +31 -0
  23. package/dist/cursor-Blq1cHdr.cjs +91 -0
  24. package/dist/cursor-CwPNSy_A.mjs +88 -0
  25. package/dist/future-Dq4Ha1Dn.cjs +24 -0
  26. package/dist/future-xRdLl3vf.mjs +22 -0
  27. package/dist/{index-xa1kwZoj.cjs → index-B_JYgMUS.cjs} +189 -5352
  28. package/dist/{index-7Z93BoVn.mjs → index-CX-F_fuk.mjs} +177 -5331
  29. package/dist/index.cjs +2 -2
  30. package/dist/index.mjs +2 -2
  31. package/dist/installFatalProcessHandlers-0vaw9MAz.mjs +55 -0
  32. package/dist/installFatalProcessHandlers-CyURn5Bp.cjs +57 -0
  33. package/dist/launch-BoCCEd5p.mjs +63 -0
  34. package/dist/launch-wZA5BcvS.cjs +66 -0
  35. package/dist/lib.cjs +2 -3
  36. package/dist/lib.d.cts +20 -17
  37. package/dist/lib.d.mts +20 -17
  38. package/dist/lib.mjs +1 -2
  39. package/dist/resolveCommand-B3BGyBE2.mjs +189 -0
  40. package/dist/resolveCommand-DYMd9PNC.cjs +193 -0
  41. package/dist/{runClaude-zCwRhpOw.mjs → runClaude-Be0myF9k.mjs} +8 -5
  42. package/dist/{runClaude-BBGNmGj6.cjs → runClaude-DZJt5er7.cjs} +46 -43
  43. package/dist/{runCodex-BbgLVjb9.mjs → runCodex-BSnyN4m7.mjs} +226 -117
  44. package/dist/{runCodex-jUU6U2tZ.cjs → runCodex-DTCcGRue.cjs} +269 -160
  45. package/dist/runCursor-Bn1PuwJy.cjs +506 -0
  46. package/dist/runCursor-M6dQ6bGF.mjs +504 -0
  47. package/dist/{runGemini-DcwNsudA.mjs → runGemini-BNm4vYKA.mjs} +279 -5
  48. package/dist/{runGemini-C0NT8MHK.cjs → runGemini-Bn3lFhz6.cjs} +309 -35
  49. package/dist/{registerKillSessionHandler-DLDg2EES.mjs → sessionControl-1bT_7OI6.mjs} +1643 -2405
  50. package/dist/{registerKillSessionHandler-CfCya6si.cjs → sessionControl-flKnQrx0.cjs} +1647 -2417
  51. package/dist/{api-DnqaNvyV.mjs → types-B5vtxa38.mjs} +55 -5
  52. package/dist/{api-D7nAeZi7.cjs → types-CttABk32.cjs} +55 -4
  53. package/package.json +2 -2
  54. package/dist/types-CiliQpqS.mjs +0 -52
  55. package/dist/types-DVk3crez.cjs +0 -54
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var persistence = require('./api-D7nAeZi7.cjs');
4
- var registerKillSessionHandler = require('./registerKillSessionHandler-CfCya6si.cjs');
5
- var index = require('./index-xa1kwZoj.cjs');
3
+ var persistence = require('./types-CttABk32.cjs');
4
+ var sessionControl = require('./sessionControl-flKnQrx0.cjs');
5
+ var index = require('./index-B_JYgMUS.cjs');
6
6
  require('cross-spawn');
7
7
  require('@agentclientprotocol/sdk');
8
8
  var node_crypto = require('node:crypto');
@@ -26,12 +26,19 @@ require('tweetnacl');
26
26
  require('open');
27
27
  var React = require('react');
28
28
  var ink = require('ink');
29
- var ProviderSelectionHandler = require('./ProviderSelectionHandler-BJJc7qOR.cjs');
30
- var BaseReasoningProcessor = require('./BaseReasoningProcessor-Dn9NcoHz.cjs');
31
- require('zod');
29
+ var ProviderSelectionHandler = require('./ProviderSelectionHandler-C7GE5QjX.cjs');
32
30
  require('socket.io-client');
33
31
  require('expo-server-sdk');
34
- require('./types-DVk3crez.cjs');
32
+ var ConversationHistory = require('./ConversationHistory-Bl2doTA-.cjs');
33
+ var RuntimeShell = require('./RuntimeShell-D_Te12wq.cjs');
34
+ var AcpBackend = require('./AcpBackend-XPiTd6ph.cjs');
35
+ var resolveCommand = require('./resolveCommand-DYMd9PNC.cjs');
36
+ var codex = require('./codex-Fv2eali8.cjs');
37
+ var BaseReasoningProcessor = require('./BaseReasoningProcessor-BD9tiwep.cjs');
38
+ var future = require('./future-Dq4Ha1Dn.cjs');
39
+ var bootstrapManagedProviderSession = require('./bootstrapManagedProviderSession-Bln-TwyB.cjs');
40
+ var installFatalProcessHandlers = require('./installFatalProcessHandlers-CyURn5Bp.cjs');
41
+ require('zod');
35
42
  require('qrcode-terminal');
36
43
  require('node:module');
37
44
  require('url');
@@ -240,12 +247,19 @@ class CodexSession {
240
247
  this.sessionId = sessionId;
241
248
  this.client.updateMetadata((metadata) => ({
242
249
  ...metadata,
243
- codexSessionId: sessionId
250
+ codexSessionId: sessionId,
251
+ codexSessionCwd: this.path
244
252
  }));
245
253
  persistence.logger.debug(`[CodexSession] Session ID ${sessionId} added to metadata`);
246
254
  };
247
255
  clearSessionId = () => {
248
256
  this.sessionId = null;
257
+ this.client.updateMetadata((metadata) => {
258
+ const nextMetadata = { ...metadata };
259
+ delete nextMetadata.codexSessionId;
260
+ delete nextMetadata.codexSessionCwd;
261
+ return nextMetadata;
262
+ });
249
263
  persistence.logger.debug("[CodexSession] Session ID cleared");
250
264
  };
251
265
  handleUserObserverEphemeral = (event) => {
@@ -360,7 +374,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal, title }
360
374
  }
361
375
  };
362
376
  const formatMessage = (msg) => {
363
- const lines = index.formatDisplayMessage(msg.content).split("\n");
377
+ const lines = RuntimeShell.formatDisplayMessage(msg.content).split("\n");
364
378
  const maxLineLength = terminalWidth - 10;
365
379
  return lines.map((line) => {
366
380
  if (line.length <= maxLineLength) return line;
@@ -402,7 +416,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal, title }
402
416
  ));
403
417
  };
404
418
 
405
- class CodexPermissionHandler extends registerKillSessionHandler.BasePermissionHandler {
419
+ class CodexPermissionHandler extends sessionControl.BasePermissionHandler {
406
420
  constructor(session) {
407
421
  super(session);
408
422
  }
@@ -517,6 +531,113 @@ function getCodexExecutionFingerprint(mode) {
517
531
  });
518
532
  }
519
533
 
534
+ function getCodexSessionsRoot(options = {}) {
535
+ const codexHomeDir = options.codexHomeDir || process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
536
+ return path.join(codexHomeDir, "sessions");
537
+ }
538
+ function collectFilesRecursive(dir, acc = []) {
539
+ let entries;
540
+ try {
541
+ entries = fs.readdirSync(dir, { withFileTypes: true });
542
+ } catch {
543
+ return acc;
544
+ }
545
+ for (const entry of entries) {
546
+ const full = path.join(dir, entry.name);
547
+ if (entry.isDirectory()) {
548
+ collectFilesRecursive(full, acc);
549
+ } else if (entry.isFile()) {
550
+ acc.push(full);
551
+ }
552
+ }
553
+ return acc;
554
+ }
555
+ function extractCodexSessionIdFromPath(filePath) {
556
+ 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$/);
557
+ return match?.[1] ?? null;
558
+ }
559
+ function normalizePathForSessionMatch(path$1) {
560
+ const normalizedPath = path.normalize(path$1).replace(/[\\/]+/g, "/").replace(/\/+$/, "");
561
+ return process.platform === "win32" ? normalizedPath.toLowerCase() : normalizedPath;
562
+ }
563
+ function extractCodexSessionCwdFromFile(filePath) {
564
+ let contents;
565
+ try {
566
+ contents = fs.readFileSync(filePath, "utf8");
567
+ } catch {
568
+ return null;
569
+ }
570
+ for (const line of contents.split(/\r?\n/)) {
571
+ if (!line.trim()) {
572
+ continue;
573
+ }
574
+ let record;
575
+ try {
576
+ record = JSON.parse(line);
577
+ } catch {
578
+ continue;
579
+ }
580
+ if (record.type !== "session_meta" || typeof record.payload !== "object" || record.payload === null) {
581
+ continue;
582
+ }
583
+ const cwd = record.payload.cwd;
584
+ return typeof cwd === "string" && cwd.trim().length > 0 ? cwd : null;
585
+ }
586
+ return null;
587
+ }
588
+ function codexSessionFileMatchesCwd(filePath, cwd) {
589
+ const expectedCwd = cwd?.trim();
590
+ if (!expectedCwd) {
591
+ return true;
592
+ }
593
+ const actualCwd = extractCodexSessionCwdFromFile(filePath);
594
+ if (!actualCwd) {
595
+ return false;
596
+ }
597
+ return normalizePathForSessionMatch(actualCwd) === normalizePathForSessionMatch(expectedCwd);
598
+ }
599
+ function findCodexResumeFile(sessionId, options = {}) {
600
+ if (!sessionId) return null;
601
+ try {
602
+ const candidates = collectFilesRecursive(getCodexSessionsRoot(options)).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
603
+ try {
604
+ return fs.statSync(full).isFile();
605
+ } catch {
606
+ return false;
607
+ }
608
+ }).filter((full) => codexSessionFileMatchesCwd(full, options.cwd)).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
609
+ return candidates[0] || null;
610
+ } catch {
611
+ return null;
612
+ }
613
+ }
614
+ function isCodexResumeSessionValidForCwd(sessionId, cwd, options = {}) {
615
+ if (!sessionId?.trim()) {
616
+ return false;
617
+ }
618
+ return findCodexResumeFile(sessionId, { ...options, cwd }) !== null;
619
+ }
620
+ function findLatestCodexSessionIdSince(startTimeMs, options = {}) {
621
+ try {
622
+ const candidates = collectFilesRecursive(getCodexSessionsRoot(options)).filter((full) => full.endsWith(".jsonl")).filter((full) => {
623
+ try {
624
+ return fs.statSync(full).mtimeMs >= startTimeMs;
625
+ } catch {
626
+ return false;
627
+ }
628
+ }).filter((full) => codexSessionFileMatchesCwd(full, options.cwd)).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
629
+ for (const candidate of candidates) {
630
+ const sessionId = extractCodexSessionIdFromPath(candidate);
631
+ if (sessionId) {
632
+ return sessionId;
633
+ }
634
+ }
635
+ } catch {
636
+ return null;
637
+ }
638
+ return null;
639
+ }
640
+
520
641
  function delay(ms) {
521
642
  return new Promise((resolve) => setTimeout(resolve, ms));
522
643
  }
@@ -622,8 +743,8 @@ function appendCodexAuthScopeHint(message, searchableLower) {
622
743
  }
623
744
  function extractLocalProcessExit(error) {
624
745
  const record = typeof error === "object" && error !== null ? error : null;
625
- const text = index.formatDisplayMessage(error).trim();
626
- const stderrTextFromRecord = record ? index.formatDisplayMessage(record.stderr).trim() : "";
746
+ const text = RuntimeShell.formatDisplayMessage(error).trim();
747
+ const stderrTextFromRecord = record ? RuntimeShell.formatDisplayMessage(record.stderr).trim() : "";
627
748
  const stderrFromTextMatch = text.match(/Recent stderr:\s*([\s\S]+)$/i);
628
749
  const stderrText = stderrTextFromRecord || stderrFromTextMatch?.[1]?.trim() || "";
629
750
  let exitCode = typeof record?.exitCode === "number" && Number.isFinite(record.exitCode) ? record.exitCode : null;
@@ -685,19 +806,30 @@ function formatCodexLocalProcessExit(error) {
685
806
  function shouldInjectLargeOutputRecoveryHint(error) {
686
807
  const record = typeof error === "object" && error !== null ? error : null;
687
808
  const searchableText = [
688
- index.formatDisplayMessage(error).trim(),
689
- record ? index.formatDisplayMessage(record.stderr).trim() : "",
690
- record ? index.formatDisplayMessage(record.detail).trim() : "",
691
- record ? index.formatDisplayMessage(record.data).trim() : ""
809
+ RuntimeShell.formatDisplayMessage(error).trim(),
810
+ record ? RuntimeShell.formatDisplayMessage(record.stderr).trim() : "",
811
+ record ? RuntimeShell.formatDisplayMessage(record.detail).trim() : "",
812
+ record ? RuntimeShell.formatDisplayMessage(record.data).trim() : ""
692
813
  ].filter(Boolean).join("\n").toLowerCase();
693
814
  return searchableText.includes("memory allocation of") || searchableText.includes("out of memory") || searchableText.includes("oversized command output");
694
815
  }
816
+ function isCodexUnknownResumeSessionError(error) {
817
+ const record = typeof error === "object" && error !== null ? error : null;
818
+ const searchableText = [
819
+ RuntimeShell.formatDisplayMessage(error).trim(),
820
+ record ? RuntimeShell.formatDisplayMessage(record.message).trim() : "",
821
+ record ? RuntimeShell.formatDisplayMessage(record.stderr).trim() : "",
822
+ record ? RuntimeShell.formatDisplayMessage(record.detail).trim() : "",
823
+ record ? RuntimeShell.formatDisplayMessage(record.data).trim() : ""
824
+ ].filter(Boolean).join("\n");
825
+ 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);
826
+ }
695
827
  function normalizeCodexBackendError(error) {
696
828
  const record = typeof error === "object" && error !== null ? error : null;
697
- const text = index.formatDisplayMessage(error).trim();
698
- const stderrText = record ? index.formatDisplayMessage(record.stderr).trim() : "";
699
- const detailText = record ? index.formatDisplayMessage(record.detail).trim() : "";
700
- const dataText = record ? index.formatDisplayMessage(record.data).trim() : "";
829
+ const text = RuntimeShell.formatDisplayMessage(error).trim();
830
+ const stderrText = record ? RuntimeShell.formatDisplayMessage(record.stderr).trim() : "";
831
+ const detailText = record ? RuntimeShell.formatDisplayMessage(record.detail).trim() : "";
832
+ const dataText = record ? RuntimeShell.formatDisplayMessage(record.data).trim() : "";
701
833
  const searchableText = [text, stderrText, detailText, dataText].filter(Boolean).join("\n");
702
834
  const prefix = buildErrorPrefix(record);
703
835
  const searchableLower = searchableText.toLowerCase();
@@ -781,12 +913,12 @@ function shouldEmitCodexReadyAfterWait(opts) {
781
913
  function registerCodexRemoteRpcHandlers(clientSession, handlers) {
782
914
  clientSession.rpcHandlerManager.registerHandler("abort", handlers.handleAbort);
783
915
  clientSession.rpcHandlerManager.registerHandler("switch", handlers.handleSwitchToLocal);
784
- registerKillSessionHandler.registerKillSessionHandler(clientSession.rpcHandlerManager, handlers.handleKillSession);
916
+ sessionControl.registerKillSessionHandler(clientSession.rpcHandlerManager, handlers.handleKillSession);
785
917
  }
786
918
  async function codexRemoteLauncher(session) {
787
919
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
788
- const messageBuffer = new registerKillSessionHandler.MessageBuffer({ enabled: hasTTY });
789
- const renderSessionTranscript = registerKillSessionHandler.createSessionTranscriptInkRenderer({ messageBuffer });
920
+ const messageBuffer = new ConversationHistory.MessageBuffer({ enabled: hasTTY });
921
+ const renderSessionTranscript = ConversationHistory.createSessionTranscriptInkRenderer({ messageBuffer });
790
922
  let inkInstance = null;
791
923
  let shouldExit = false;
792
924
  let shouldSwitchToLocal = false;
@@ -812,7 +944,7 @@ async function codexRemoteLauncher(session) {
812
944
  const assistantMessageStream = new persistence.AssistantMessageStream();
813
945
  const permissionHandler = new CodexPermissionHandler(session.client);
814
946
  const selectionHandler = new CodexSelectionHandler(session.client);
815
- const conversationHistory = new registerKillSessionHandler.ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
947
+ const conversationHistory = new ConversationHistory.ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
816
948
  const reasoningProcessor = new ReasoningProcessor((message) => {
817
949
  session.runtimeSession.sendCodexMessage(message);
818
950
  });
@@ -827,7 +959,7 @@ async function codexRemoteLauncher(session) {
827
959
  return;
828
960
  }
829
961
  try {
830
- const notification = registerKillSessionHandler.buildReadyPushNotification({
962
+ const notification = ConversationHistory.buildReadyPushNotification({
831
963
  providerLabel: "Codex",
832
964
  metadata: session.runtimeSession.getMetadataSnapshot?.() ?? null,
833
965
  sessionId: session.runtimeSession.sessionId
@@ -997,7 +1129,7 @@ async function codexRemoteLauncher(session) {
997
1129
  };
998
1130
  const setupRuntimeMessageHandler = (activeRuntimeHandle) => {
999
1131
  const forwardAgentMessage = (agentMessage) => {
1000
- registerKillSessionHandler.forwardAgentMessageToProviderSession(agentMessage, {
1132
+ sessionControl.forwardAgentMessageToProviderSession(agentMessage, {
1001
1133
  provider: "codex",
1002
1134
  send: (body) => session.runtimeSession.sendCodexMessage(body),
1003
1135
  toolResultType: "tool-call-result"
@@ -1063,7 +1195,7 @@ async function codexRemoteLauncher(session) {
1063
1195
  }
1064
1196
  case "tool-call": {
1065
1197
  commitActiveAssistantMessageStream();
1066
- const toolArgs = hasRenderableToolPayload(msg.args) ? index.truncateDisplayMessage(msg.args, 100) : "";
1198
+ const toolArgs = hasRenderableToolPayload(msg.args) ? RuntimeShell.truncateDisplayMessage(msg.args, 100) : "";
1067
1199
  messageBuffer.addMessage(
1068
1200
  `Executing: ${msg.toolName}${toolArgs ? ` ${toolArgs}` : ""}`,
1069
1201
  "tool"
@@ -1072,8 +1204,8 @@ async function codexRemoteLauncher(session) {
1072
1204
  return;
1073
1205
  }
1074
1206
  case "tool-result": {
1075
- const isError = registerKillSessionHandler.inferToolResultError(msg.result);
1076
- const resultText = hasRenderableToolPayload(msg.result) ? index.truncateDisplayMessage(msg.result, 200) : "";
1207
+ const isError = sessionControl.inferToolResultError(msg.result);
1208
+ const resultText = hasRenderableToolPayload(msg.result) ? RuntimeShell.truncateDisplayMessage(msg.result, 200) : "";
1077
1209
  if (isError || resultText) {
1078
1210
  messageBuffer.addMessage(
1079
1211
  `${isError ? "Error:" : "Result:"} ${resultText || "Unknown error"}`.trim(),
@@ -1089,12 +1221,12 @@ async function codexRemoteLauncher(session) {
1089
1221
  return;
1090
1222
  }
1091
1223
  case "terminal-output": {
1092
- const terminalOutput = registerKillSessionHandler.renderTerminalOutputPreview(msg.data);
1224
+ const terminalOutput = sessionControl.renderTerminalOutputPreview(msg.data);
1093
1225
  if (!terminalOutput) {
1094
1226
  return;
1095
1227
  }
1096
1228
  messageBuffer.addMessage(terminalOutput, "result");
1097
- const forwardedOutput = registerKillSessionHandler.prepareTerminalOutputForForwarding(msg.data);
1229
+ const forwardedOutput = sessionControl.prepareTerminalOutputForForwarding(msg.data);
1098
1230
  if (forwardedOutput) {
1099
1231
  forwardAgentMessage({
1100
1232
  ...msg,
@@ -1105,8 +1237,8 @@ async function codexRemoteLauncher(session) {
1105
1237
  }
1106
1238
  case "permission-request": {
1107
1239
  try {
1108
- const permissionRequest = registerKillSessionHandler.extractPermissionRequestPushContext(msg);
1109
- const notification = registerKillSessionHandler.buildPermissionPushNotification({
1240
+ const permissionRequest = ConversationHistory.extractPermissionRequestPushContext(msg);
1241
+ const notification = ConversationHistory.buildPermissionPushNotification({
1110
1242
  providerLabel: "Codex",
1111
1243
  metadata: session.runtimeSession.getMetadataSnapshot?.() ?? null,
1112
1244
  sessionId: session.runtimeSession.sessionId,
@@ -1139,9 +1271,9 @@ async function codexRemoteLauncher(session) {
1139
1271
  }
1140
1272
  case "patch-apply-end": {
1141
1273
  if (msg.success) {
1142
- messageBuffer.addMessage(index.truncateDisplayMessage(msg.stdout || "Files modified successfully", 200), "result");
1274
+ messageBuffer.addMessage(RuntimeShell.truncateDisplayMessage(msg.stdout || "Files modified successfully", 200), "result");
1143
1275
  } else {
1144
- messageBuffer.addMessage(`Error: ${index.truncateDisplayMessage(msg.stderr || "Failed to modify files", 200)}`, "result");
1276
+ messageBuffer.addMessage(`Error: ${RuntimeShell.truncateDisplayMessage(msg.stderr || "Failed to modify files", 200)}`, "result");
1145
1277
  }
1146
1278
  forwardAgentMessage(msg);
1147
1279
  return;
@@ -1175,7 +1307,12 @@ async function codexRemoteLauncher(session) {
1175
1307
  };
1176
1308
  const createRuntimeHandle = async (mode) => {
1177
1309
  const executionMode = resolveCodexAcpExecutionMode(mode);
1178
- const validation = index.validateCodexAcpSpawn({
1310
+ const resumeSessionId = session.sessionId && isCodexResumeSessionValidForCwd(session.sessionId, session.path) ? session.sessionId : null;
1311
+ if (session.sessionId && !resumeSessionId) {
1312
+ persistence.logger.debug(`[Codex] Refusing to resume Codex session ${session.sessionId} because its local session file does not match cwd ${session.path}`);
1313
+ session.clearSessionId();
1314
+ }
1315
+ const validation = resolveCommand.validateCodexAcpSpawn({
1179
1316
  command: process.env.HAPPY_CODEX_ACP_COMMAND,
1180
1317
  model: executionMode.model,
1181
1318
  sandbox: executionMode.sandbox,
@@ -1185,17 +1322,17 @@ async function codexRemoteLauncher(session) {
1185
1322
  if (!validation.ok) {
1186
1323
  throw new Error(validation.errorMessage);
1187
1324
  }
1188
- const { session: nextRuntimeHandle } = await registerKillSessionHandler.launchRuntimeHandleWithFactoryResult({
1325
+ const { session: nextRuntimeHandle } = await ConversationHistory.launchRuntimeHandleWithFactoryResult({
1189
1326
  provider: "codex",
1190
1327
  cwd: session.path,
1191
- createBackendResult: (opts) => index.createCodexBackend({
1328
+ createBackendResult: (opts) => codex.createCodexBackend({
1192
1329
  ...opts,
1193
1330
  baseArgs: session.codexArgs,
1194
1331
  model: executionMode.model,
1195
1332
  sandbox: executionMode.sandbox,
1196
1333
  approvalPolicy: executionMode.approvalPolicy,
1197
1334
  permissionHandler,
1198
- resumeSessionId: session.sessionId,
1335
+ resumeSessionId,
1199
1336
  selectionHandler: {
1200
1337
  handleSelection: (request) => selectionHandler.requestSelection(request)
1201
1338
  }
@@ -1203,7 +1340,7 @@ async function codexRemoteLauncher(session) {
1203
1340
  });
1204
1341
  runtimeHandle = nextRuntimeHandle;
1205
1342
  setupRuntimeMessageHandler(nextRuntimeHandle);
1206
- const providerSessionId = nextRuntimeHandle.backend instanceof index.AcpBackend ? nextRuntimeHandle.backend.getProviderSessionId() : null;
1343
+ const providerSessionId = nextRuntimeHandle.backend instanceof AcpBackend.AcpBackend ? nextRuntimeHandle.backend.getProviderSessionId() : null;
1207
1344
  session.onSessionFound(providerSessionId ?? nextRuntimeHandle.sessionId);
1208
1345
  return nextRuntimeHandle;
1209
1346
  };
@@ -1346,12 +1483,31 @@ async function codexRemoteLauncher(session) {
1346
1483
  const turnSignal = abortController.signal;
1347
1484
  let sentTurnResultPush = false;
1348
1485
  let turnStatus = "task_complete";
1486
+ let retryingWithFreshSession = false;
1349
1487
  currentHappyOrgTurn = message.mode.happyOrg ?? null;
1350
1488
  try {
1351
1489
  turnInFlight = true;
1352
1490
  shouldCommitAccumulatedResponse = false;
1353
1491
  const isNewRuntimeSession = runtimeHandle === null;
1354
- const activeRuntimeHandle = runtimeHandle ?? await createRuntimeHandle(message.mode);
1492
+ const attemptedResumeSessionId = isNewRuntimeSession ? session.sessionId : null;
1493
+ let activeRuntimeHandle;
1494
+ try {
1495
+ activeRuntimeHandle = runtimeHandle ?? await createRuntimeHandle(message.mode);
1496
+ } catch (error) {
1497
+ if (!isNewRuntimeSession || !attemptedResumeSessionId || !isCodexUnknownResumeSessionError(error) || turnSignal.aborted) {
1498
+ throw error;
1499
+ }
1500
+ persistence.logger.debug(`[Codex] Resume session ${attemptedResumeSessionId} failed during ACP startup; retrying with a fresh session`, error);
1501
+ queueHistoryInjectionForRestart("Codex resume session is unavailable. Retrying with a fresh Codex session...");
1502
+ session.clearSessionId();
1503
+ currentModeHash = null;
1504
+ permissionHandler.reset();
1505
+ selectionHandler.reset();
1506
+ resetTurnState();
1507
+ pending = message;
1508
+ retryingWithFreshSession = true;
1509
+ continue;
1510
+ }
1355
1511
  if (!activeRuntimeHandle) {
1356
1512
  throw new Error("Failed to create Codex ACP backend");
1357
1513
  }
@@ -1375,11 +1531,28 @@ async function codexRemoteLauncher(session) {
1375
1531
 
1376
1532
  ${message.message}` : message.message;
1377
1533
  if (message.mode.happyOrg) {
1378
- promptToSend = registerKillSessionHandler.buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
1534
+ promptToSend = sessionControl.buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
1379
1535
  }
1380
1536
  conversationHistory.addUserMessage(message.message);
1381
- await activeRuntimeHandle.sendPrompt(promptToSend);
1382
- await registerKillSessionHandler.waitForResponseCompleteWithAbort(activeRuntimeHandle.backend, turnSignal);
1537
+ try {
1538
+ await activeRuntimeHandle.sendPrompt(promptToSend);
1539
+ await sessionControl.waitForResponseCompleteWithAbort(activeRuntimeHandle.backend, turnSignal);
1540
+ } catch (error) {
1541
+ if (!isNewRuntimeSession || !attemptedResumeSessionId || !isCodexUnknownResumeSessionError(error) || turnSignal.aborted) {
1542
+ throw error;
1543
+ }
1544
+ persistence.logger.debug(`[Codex] Resume session ${attemptedResumeSessionId} is unavailable; retrying with a fresh ACP session`, error);
1545
+ queueHistoryInjectionForRestart("Codex resume session is unavailable. Retrying with a fresh Codex session...");
1546
+ await disposeRuntimeHandle();
1547
+ session.clearSessionId();
1548
+ currentModeHash = null;
1549
+ permissionHandler.reset();
1550
+ selectionHandler.reset();
1551
+ resetTurnState();
1552
+ pending = message;
1553
+ retryingWithFreshSession = true;
1554
+ continue;
1555
+ }
1383
1556
  reasoningProcessor.completeCurrent();
1384
1557
  shouldCommitAccumulatedResponse = true;
1385
1558
  shouldInjectHistoryOnNextSession = false;
@@ -1409,7 +1582,10 @@ ${message.message}` : message.message;
1409
1582
  }
1410
1583
  } finally {
1411
1584
  turnInFlight = false;
1412
- const finalizedTurn = await registerKillSessionHandler.finalizeHappyOrgTurnWithBusinessAck({
1585
+ if (retryingWithFreshSession) {
1586
+ continue;
1587
+ }
1588
+ const finalizedTurn = await sessionControl.finalizeHappyOrgTurnWithBusinessAck({
1413
1589
  metadata: session.runtimeSession.getMetadataSnapshot?.() ?? null,
1414
1590
  queuedTurn: message.mode.happyOrg,
1415
1591
  responseText: accumulatedResponse,
@@ -1435,7 +1611,7 @@ ${message.message}` : message.message;
1435
1611
  emitFinalAssistantMessage(finalizedTurn.cleanedText);
1436
1612
  if (!shouldExit && !shouldSwitchToLocal) {
1437
1613
  try {
1438
- const notification = registerKillSessionHandler.buildTurnResultPushNotification({
1614
+ const notification = ConversationHistory.buildTurnResultPushNotification({
1439
1615
  providerLabel: "Codex",
1440
1616
  metadata: finalizedTurn.nextMetadata ?? session.runtimeSession.getMetadataSnapshot?.() ?? null,
1441
1617
  sessionId: session.runtimeSession.sessionId,
@@ -1504,52 +1680,6 @@ ${message.message}` : message.message;
1504
1680
  return shouldSwitchToLocal ? "switch" : "exit";
1505
1681
  }
1506
1682
 
1507
- function getCodexSessionsRoot(options = {}) {
1508
- const codexHomeDir = options.codexHomeDir || process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
1509
- return path.join(codexHomeDir, "sessions");
1510
- }
1511
- function collectFilesRecursive(dir, acc = []) {
1512
- let entries;
1513
- try {
1514
- entries = fs.readdirSync(dir, { withFileTypes: true });
1515
- } catch {
1516
- return acc;
1517
- }
1518
- for (const entry of entries) {
1519
- const full = path.join(dir, entry.name);
1520
- if (entry.isDirectory()) {
1521
- collectFilesRecursive(full, acc);
1522
- } else if (entry.isFile()) {
1523
- acc.push(full);
1524
- }
1525
- }
1526
- return acc;
1527
- }
1528
- function extractCodexSessionIdFromPath(filePath) {
1529
- 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$/);
1530
- return match?.[1] ?? null;
1531
- }
1532
- function findLatestCodexSessionIdSince(startTimeMs, options = {}) {
1533
- try {
1534
- const candidates = collectFilesRecursive(getCodexSessionsRoot(options)).filter((full) => full.endsWith(".jsonl")).filter((full) => {
1535
- try {
1536
- return fs.statSync(full).mtimeMs >= startTimeMs;
1537
- } catch {
1538
- return false;
1539
- }
1540
- }).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
1541
- for (const candidate of candidates) {
1542
- const sessionId = extractCodexSessionIdFromPath(candidate);
1543
- if (sessionId) {
1544
- return sessionId;
1545
- }
1546
- }
1547
- } catch {
1548
- return null;
1549
- }
1550
- return null;
1551
- }
1552
-
1553
1683
  const MANAGED_CODEX_HOME_PREFIX = "happy-codex-home-";
1554
1684
  function isManagedCodexHomePath(value) {
1555
1685
  const trimmed = value?.trim();
@@ -1616,12 +1746,17 @@ function terminateCodexLocalChild(pid) {
1616
1746
  }
1617
1747
  async function codexLocal(opts) {
1618
1748
  const baseArgs = [...opts.codexArgs ?? []];
1619
- const args = opts.sessionId ? ["resume", opts.sessionId, ...baseArgs] : baseArgs;
1620
- const startTime = Date.now();
1621
- let detectedSessionId = opts.sessionId;
1622
- const codexExecutable = index.resolveCodexExecutable();
1623
1749
  const codexEnv = buildCodexLocalEnv(process.env);
1624
1750
  const codexHomeDir = resolveCodexLocalHomeDir(codexEnv);
1751
+ const resumeSessionId = opts.sessionId && isCodexResumeSessionValidForCwd(opts.sessionId, opts.path, { codexHomeDir }) ? opts.sessionId : null;
1752
+ if (opts.sessionId && !resumeSessionId) {
1753
+ persistence.logger.debug(`[CodexLocal] Refusing to resume Codex session ${opts.sessionId} because its local session file does not match cwd ${opts.path}`);
1754
+ opts.onResumeSessionInvalid?.(opts.sessionId);
1755
+ }
1756
+ const args = resumeSessionId ? ["resume", resumeSessionId, ...baseArgs] : baseArgs;
1757
+ const startTime = Date.now();
1758
+ let detectedSessionId = resumeSessionId;
1759
+ const codexExecutable = codex.resolveCodexExecutable();
1625
1760
  persistence.logger.debug(`[CodexLocal] Spawning ${codexExecutable} with args: ${JSON.stringify(args)}`);
1626
1761
  releaseTerminalInputForChild();
1627
1762
  clearTerminalForChild();
@@ -1631,7 +1766,7 @@ async function codexLocal(opts) {
1631
1766
  cwd: opts.path,
1632
1767
  env: codexEnv,
1633
1768
  stdio: "inherit",
1634
- shell: index.shouldUseShellForCodex(codexExecutable)
1769
+ shell: codex.shouldUseShellForCodex(codexExecutable)
1635
1770
  });
1636
1771
  const abortChild = () => {
1637
1772
  if (child.exitCode === null) {
@@ -1647,7 +1782,7 @@ async function codexLocal(opts) {
1647
1782
  if (detectedSessionId) {
1648
1783
  return;
1649
1784
  }
1650
- const nextSessionId = findLatestCodexSessionIdSince(startTime, { codexHomeDir });
1785
+ const nextSessionId = findLatestCodexSessionIdSince(startTime, { codexHomeDir, cwd: opts.path });
1651
1786
  if (nextSessionId) {
1652
1787
  detectedSessionId = nextSessionId;
1653
1788
  persistence.logger.debug(`[CodexLocal] Detected session ID: ${nextSessionId}`);
@@ -1705,7 +1840,7 @@ async function codexLocalLauncher(session) {
1705
1840
  let switchWithoutSessionTimer = null;
1706
1841
  let reportedSessionId = session.sessionId;
1707
1842
  const processAbortController = new AbortController();
1708
- const exitFuture = new index.Future();
1843
+ const exitFuture = new future.Future();
1709
1844
  try {
1710
1845
  let clearSwitchWithoutSessionTimer2 = function() {
1711
1846
  if (switchWithoutSessionTimer) {
@@ -1790,6 +1925,7 @@ async function codexLocalLauncher(session) {
1790
1925
  abort: processAbortController.signal,
1791
1926
  codexArgs: session.codexArgs,
1792
1927
  onSessionFound: recordSessionFound,
1928
+ onResumeSessionInvalid: session.clearSessionId,
1793
1929
  onThinkingChange: session.onThinkingChange
1794
1930
  });
1795
1931
  if (sessionId) {
@@ -1863,58 +1999,6 @@ async function codexLoop(opts) {
1863
1999
  });
1864
2000
  }
1865
2001
 
1866
- function normalizeFatalProcessError(error) {
1867
- if (error instanceof Error) {
1868
- return error;
1869
- }
1870
- const message = typeof error === "string" ? error : (() => {
1871
- try {
1872
- const serialized = JSON.stringify(error);
1873
- return serialized && serialized !== "{}" ? serialized : String(error);
1874
- } catch {
1875
- return String(error);
1876
- }
1877
- })();
1878
- return new Error(message || "Unknown fatal process error");
1879
- }
1880
- function installFatalProcessHandlers(options) {
1881
- const target = options.target ?? process;
1882
- const exit = options.exit ?? process.exit;
1883
- let shuttingDown = false;
1884
- let disposed = false;
1885
- const handleFatalEvent = async (event, reason) => {
1886
- const error = normalizeFatalProcessError(reason);
1887
- persistence.logger.debug(`[${options.label}] Fatal ${event}`, error);
1888
- if (shuttingDown) {
1889
- persistence.logger.debug(`[${options.label}] Ignoring duplicate fatal ${event} while shutdown is already in progress`);
1890
- return;
1891
- }
1892
- shuttingDown = true;
1893
- try {
1894
- await options.cleanup({ event, error });
1895
- } catch (cleanupError) {
1896
- persistence.logger.debug(`[${options.label}] Fatal cleanup failed`, cleanupError);
1897
- }
1898
- exit(1);
1899
- };
1900
- const onUncaughtException = (error) => {
1901
- void handleFatalEvent("uncaughtException", error);
1902
- };
1903
- const onUnhandledRejection = (reason) => {
1904
- void handleFatalEvent("unhandledRejection", reason);
1905
- };
1906
- target.on("uncaughtException", onUncaughtException);
1907
- target.on("unhandledRejection", onUnhandledRejection);
1908
- return () => {
1909
- if (disposed) {
1910
- return;
1911
- }
1912
- disposed = true;
1913
- target.off("uncaughtException", onUncaughtException);
1914
- target.off("unhandledRejection", onUnhandledRejection);
1915
- };
1916
- }
1917
-
1918
2002
  function normalizeResumeSessionId(value) {
1919
2003
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
1920
2004
  }
@@ -1959,6 +2043,17 @@ function normalizeCodexArgsForHappy(options) {
1959
2043
  };
1960
2044
  }
1961
2045
 
2046
+ function normalizePathForCodexResumeIdentity(path) {
2047
+ const normalized = path.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "");
2048
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
2049
+ }
2050
+ function codexResumeCwdMatches(expected, actual) {
2051
+ const normalizedExpected = expected?.trim();
2052
+ if (!normalizedExpected) {
2053
+ return true;
2054
+ }
2055
+ return normalizePathForCodexResumeIdentity(normalizedExpected) === normalizePathForCodexResumeIdentity(actual);
2056
+ }
1962
2057
  function resolveInitialCodexPermissionMode(opts) {
1963
2058
  if (opts.permissionMode) {
1964
2059
  return opts.permissionMode;
@@ -2027,12 +2122,12 @@ async function runCodex(opts) {
2027
2122
  }
2028
2123
  let machineId;
2029
2124
  try {
2030
- machineId = await registerKillSessionHandler.ensureManagedProviderMachine({
2125
+ machineId = await sessionControl.ensureManagedProviderMachine({
2031
2126
  api,
2032
2127
  missingMachineIdMessage: "[START] No machine ID found in settings."
2033
2128
  });
2034
2129
  } catch (error) {
2035
- if (error instanceof registerKillSessionHandler.MissingMachineIdError) {
2130
+ if (error instanceof sessionControl.MissingMachineIdError) {
2036
2131
  console.error(error.message);
2037
2132
  process.exit(1);
2038
2133
  }
@@ -2040,7 +2135,7 @@ async function runCodex(opts) {
2040
2135
  }
2041
2136
  let sessionClient;
2042
2137
  let codexSession = null;
2043
- const { metadata, session: initialSession, reconnectionHandle, happyOrgStartupBinding } = await BaseReasoningProcessor.bootstrapManagedProviderSession({
2138
+ const { metadata, session: initialSession, reconnectionHandle, happyOrgStartupBinding } = await bootstrapManagedProviderSession.bootstrapManagedProviderSession({
2044
2139
  api,
2045
2140
  sessionTag,
2046
2141
  flavor: "codex",
@@ -2051,18 +2146,32 @@ async function runCodex(opts) {
2051
2146
  onSessionSwap: (newSession) => {
2052
2147
  sessionClient = newSession;
2053
2148
  codexSession?.updateClient(newSession);
2054
- void registerKillSessionHandler.syncControlledByUserState(
2149
+ void sessionControl.syncControlledByUserState(
2055
2150
  newSession,
2056
2151
  (codexSession?.mode ?? requestedStartingMode) === "local"
2057
2152
  );
2058
2153
  }
2059
2154
  });
2060
2155
  sessionClient = initialSession;
2061
- const messageQueue = new registerKillSessionHandler.MessageQueue2(getCodexExecutionFingerprint);
2156
+ const metadataCodexSessionCwd = typeof metadata.codexSessionCwd === "string" ? metadata.codexSessionCwd : null;
2157
+ const requestedResumeSessionId = normalizedArgs.resumeSessionId;
2158
+ const requestedResumeCwd = opts.resumeSessionCwd ?? metadataCodexSessionCwd;
2159
+ const resumeSessionMatchesCwd = requestedResumeSessionId ? codexResumeCwdMatches(requestedResumeCwd, metadata.path) && isCodexResumeSessionValidForCwd(requestedResumeSessionId, metadata.path) : false;
2160
+ const initialCodexSessionId = requestedResumeSessionId && resumeSessionMatchesCwd ? requestedResumeSessionId : null;
2161
+ if (requestedResumeSessionId && !resumeSessionMatchesCwd) {
2162
+ persistence.logger.debug(`[codex] Ignoring resume session ${requestedResumeSessionId} because it does not match cwd ${metadata.path}`);
2163
+ sessionClient.updateMetadata((currentMetadata) => {
2164
+ const nextMetadata = { ...currentMetadata };
2165
+ delete nextMetadata.codexSessionId;
2166
+ delete nextMetadata.codexSessionCwd;
2167
+ return nextMetadata;
2168
+ });
2169
+ }
2170
+ const messageQueue = new sessionControl.MessageQueue2(getCodexExecutionFingerprint);
2062
2171
  let currentPermissionMode = initialPermissionMode;
2063
2172
  let currentModel;
2064
2173
  sessionClient.onUserMessage((message) => {
2065
- const happyOrgResult = registerKillSessionHandler.resolveHappyOrgQueuedTurn({
2174
+ const happyOrgResult = sessionControl.resolveHappyOrgQueuedTurn({
2066
2175
  metadata: sessionClient.getMetadataSnapshot?.() ?? metadata,
2067
2176
  message,
2068
2177
  sessionId: sessionClient.sessionId
@@ -2104,7 +2213,7 @@ async function runCodex(opts) {
2104
2213
  path: metadata.path,
2105
2214
  logPath: persistence.logger.logFilePath,
2106
2215
  startupRolePrompt: happyOrgStartupBinding?.identityPrompt ?? null,
2107
- sessionId: normalizedArgs.resumeSessionId,
2216
+ sessionId: initialCodexSessionId,
2108
2217
  mode: requestedStartingMode,
2109
2218
  messageQueue,
2110
2219
  codexArgs: normalizedArgs.codexArgs,
@@ -2113,7 +2222,7 @@ async function runCodex(opts) {
2113
2222
  userObserver: userScopedObserver,
2114
2223
  onModeChange: async (mode) => {
2115
2224
  sessionClient.sendSessionEvent({ type: "switch", mode });
2116
- await registerKillSessionHandler.syncControlledByUserState(sessionClient, mode === "local");
2225
+ await sessionControl.syncControlledByUserState(sessionClient, mode === "local");
2117
2226
  }
2118
2227
  });
2119
2228
  let exitCode = null;
@@ -2142,7 +2251,7 @@ async function runCodex(opts) {
2142
2251
  reconnectionHandle?.cancel();
2143
2252
  codexSession?.cleanup();
2144
2253
  };
2145
- const disposeFatalProcessHandlers = installFatalProcessHandlers({
2254
+ const disposeFatalProcessHandlers = installFatalProcessHandlers.installFatalProcessHandlers({
2146
2255
  label: "codex",
2147
2256
  cleanup: async ({ event, error }) => {
2148
2257
  await cleanupResources({