happy-imou-cloud 2.0.2 → 2.0.4
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/{BaseReasoningProcessor-B6tJ_eL5.cjs → BaseReasoningProcessor-DEEfNi5Y.cjs} +4 -4
- package/dist/{BaseReasoningProcessor-D8VhEbs2.mjs → BaseReasoningProcessor-Di1yEMMv.mjs} +2 -2
- package/dist/{api-MYhAGPLn.mjs → api-CIHTNilH.mjs} +2 -2
- package/dist/{api-D2Njw9Im.cjs → api-CyJG1mr6.cjs} +43 -43
- package/dist/{command-nmK6O-ab.mjs → command-BERqmFB0.mjs} +3 -3
- package/dist/{command-CVldr51S.cjs → command-CPlJKXDn.cjs} +3 -3
- package/dist/{index-Bg-YziG2.cjs → index-1zlH6s7a.cjs} +313 -118
- package/dist/{index-B97L7qLD.mjs → index-vNYxNqVZ.mjs} +226 -31
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-D_2GkJAO.cjs → persistence-BeFVx6kI.cjs} +28 -28
- package/dist/{persistence-Dkm7rm8k.mjs → persistence-sLEqV8vk.mjs} +1 -1
- package/dist/{registerKillSessionHandler-BAXmJQRt.cjs → registerKillSessionHandler-CCxqGFjZ.cjs} +2 -2
- package/dist/{registerKillSessionHandler-5GbrO0FM.mjs → registerKillSessionHandler-uVHqIC4h.mjs} +2 -2
- package/dist/{runClaude-Cii3R2Fv.mjs → runClaude-Dl9nIRIg.mjs} +25 -5
- package/dist/{runClaude-B-GNEkKg.cjs → runClaude-Dz-PCSvb.cjs} +53 -33
- package/dist/{runCodex-CPHyGwj9.cjs → runCodex-BtZplK1R.cjs} +275 -408
- package/dist/{runCodex-C--ZwAhl.mjs → runCodex-DgKKw3IU.mjs} +273 -409
- package/dist/{runGemini-CQp7Nuzn.mjs → runGemini-CM1v3I24.mjs} +10 -8
- package/dist/{runGemini-DaDz1bzQ.cjs → runGemini-DUyH311Z.cjs} +10 -8
- package/package.json +1 -1
- package/dist/future-Dq4Ha1Dn.cjs +0 -24
- package/dist/future-xRdLl3vf.mjs +0 -22
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var node_crypto = require('node:crypto');
|
|
4
|
-
var api = require('./api-
|
|
5
|
-
var persistence = require('./persistence-
|
|
6
|
-
var index = require('./index-
|
|
7
|
-
var BaseReasoningProcessor = require('./BaseReasoningProcessor-
|
|
8
|
-
var registerKillSessionHandler = require('./registerKillSessionHandler-
|
|
9
|
-
var future = require('./future-Dq4Ha1Dn.cjs');
|
|
10
|
-
var node_child_process = require('node:child_process');
|
|
11
|
-
var fs = require('node:fs');
|
|
12
|
-
var os = require('node:os');
|
|
13
|
-
var path = require('node:path');
|
|
4
|
+
var api = require('./api-CyJG1mr6.cjs');
|
|
5
|
+
var persistence = require('./persistence-BeFVx6kI.cjs');
|
|
6
|
+
var index = require('./index-1zlH6s7a.cjs');
|
|
7
|
+
var BaseReasoningProcessor = require('./BaseReasoningProcessor-DEEfNi5Y.cjs');
|
|
8
|
+
var registerKillSessionHandler = require('./registerKillSessionHandler-CCxqGFjZ.cjs');
|
|
14
9
|
var React = require('react');
|
|
15
10
|
var ink = require('ink');
|
|
16
11
|
require('axios');
|
|
17
12
|
require('chalk');
|
|
18
13
|
require('fs');
|
|
14
|
+
require('node:fs');
|
|
15
|
+
require('node:os');
|
|
16
|
+
require('node:path');
|
|
19
17
|
require('node:events');
|
|
20
18
|
require('socket.io-client');
|
|
21
19
|
require('zod');
|
|
@@ -33,6 +31,7 @@ require('qrcode-terminal');
|
|
|
33
31
|
require('node:module');
|
|
34
32
|
require('open');
|
|
35
33
|
require('url');
|
|
34
|
+
require('node:child_process');
|
|
36
35
|
require('ps-list');
|
|
37
36
|
require('cross-spawn');
|
|
38
37
|
require('fastify');
|
|
@@ -130,7 +129,6 @@ class CodexSession {
|
|
|
130
129
|
sessionId;
|
|
131
130
|
mode;
|
|
132
131
|
thinking = false;
|
|
133
|
-
localModePinned = false;
|
|
134
132
|
keepAliveInterval;
|
|
135
133
|
onModeChangeCallback;
|
|
136
134
|
clientSwapCallbacks = [];
|
|
@@ -190,18 +188,6 @@ class CodexSession {
|
|
|
190
188
|
this.client.keepAlive(this.thinking, mode);
|
|
191
189
|
await this.onModeChangeCallback(mode);
|
|
192
190
|
};
|
|
193
|
-
pinLocalMode = () => {
|
|
194
|
-
this.localModePinned = true;
|
|
195
|
-
api.logger.debug("[CodexSession] Local mode pinned");
|
|
196
|
-
};
|
|
197
|
-
clearLocalModePin = () => {
|
|
198
|
-
if (!this.localModePinned) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
this.localModePinned = false;
|
|
202
|
-
api.logger.debug("[CodexSession] Local mode pin cleared");
|
|
203
|
-
};
|
|
204
|
-
isLocalModePinned = () => this.localModePinned;
|
|
205
191
|
onSessionFound = (sessionId) => {
|
|
206
192
|
if (this.sessionId === sessionId) {
|
|
207
193
|
return;
|
|
@@ -219,262 +205,7 @@ class CodexSession {
|
|
|
219
205
|
};
|
|
220
206
|
}
|
|
221
207
|
|
|
222
|
-
|
|
223
|
-
const codexHomeDir = process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
224
|
-
return path.join(codexHomeDir, "sessions");
|
|
225
|
-
}
|
|
226
|
-
function collectFilesRecursive(dir, acc = []) {
|
|
227
|
-
let entries;
|
|
228
|
-
try {
|
|
229
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
230
|
-
} catch {
|
|
231
|
-
return acc;
|
|
232
|
-
}
|
|
233
|
-
for (const entry of entries) {
|
|
234
|
-
const full = path.join(dir, entry.name);
|
|
235
|
-
if (entry.isDirectory()) {
|
|
236
|
-
collectFilesRecursive(full, acc);
|
|
237
|
-
} else if (entry.isFile()) {
|
|
238
|
-
acc.push(full);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return acc;
|
|
242
|
-
}
|
|
243
|
-
function extractCodexSessionIdFromPath(filePath) {
|
|
244
|
-
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$/);
|
|
245
|
-
return match?.[1] ?? null;
|
|
246
|
-
}
|
|
247
|
-
function findLatestCodexSessionIdSince(startTimeMs) {
|
|
248
|
-
try {
|
|
249
|
-
const candidates = collectFilesRecursive(getCodexSessionsRoot()).filter((full) => full.endsWith(".jsonl")).filter((full) => {
|
|
250
|
-
try {
|
|
251
|
-
return fs.statSync(full).mtimeMs >= startTimeMs;
|
|
252
|
-
} catch {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
}).sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
256
|
-
for (const candidate of candidates) {
|
|
257
|
-
const sessionId = extractCodexSessionIdFromPath(candidate);
|
|
258
|
-
if (sessionId) {
|
|
259
|
-
return sessionId;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
} catch {
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function firstExistingPath(candidates) {
|
|
269
|
-
for (const candidate of candidates) {
|
|
270
|
-
try {
|
|
271
|
-
if (fs.existsSync(candidate)) {
|
|
272
|
-
return candidate;
|
|
273
|
-
}
|
|
274
|
-
} catch {
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
function resolveCodexExecutable() {
|
|
280
|
-
if (process.platform === "win32") {
|
|
281
|
-
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
282
|
-
const npmGlobalBin = path.join(appData, "npm");
|
|
283
|
-
const resolved = firstExistingPath([
|
|
284
|
-
path.join(npmGlobalBin, "codex.cmd"),
|
|
285
|
-
path.join(npmGlobalBin, "codex.ps1"),
|
|
286
|
-
path.join(npmGlobalBin, "codex")
|
|
287
|
-
]);
|
|
288
|
-
if (resolved) {
|
|
289
|
-
return resolved;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return "codex";
|
|
293
|
-
}
|
|
294
|
-
function shouldUseShellForCodex(executable) {
|
|
295
|
-
return process.platform === "win32" && /\.(cmd|bat|ps1)$/i.test(executable);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
class CodexExitCodeError extends Error {
|
|
299
|
-
exitCode;
|
|
300
|
-
constructor(exitCode) {
|
|
301
|
-
super(`Codex process exited with code: ${exitCode}`);
|
|
302
|
-
this.name = "CodexExitCodeError";
|
|
303
|
-
this.exitCode = exitCode;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
function terminateCodexLocalChild(pid) {
|
|
307
|
-
if (!pid) {
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
if (process.platform === "win32") {
|
|
312
|
-
node_child_process.execFileSync("taskkill", ["/F", "/T", "/PID", pid.toString()], { stdio: "ignore" });
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
process.kill(pid, "SIGTERM");
|
|
316
|
-
} catch (error) {
|
|
317
|
-
api.logger.debug("[CodexLocal] Failed to terminate child process", error);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
async function codexLocal(opts) {
|
|
321
|
-
const baseArgs = [...opts.codexArgs ?? []];
|
|
322
|
-
const args = opts.sessionId ? ["resume", opts.sessionId, ...baseArgs] : baseArgs;
|
|
323
|
-
const startTime = Date.now();
|
|
324
|
-
let detectedSessionId = opts.sessionId;
|
|
325
|
-
const codexExecutable = resolveCodexExecutable();
|
|
326
|
-
api.logger.debug(`[CodexLocal] Spawning ${codexExecutable} with args: ${JSON.stringify(args)}`);
|
|
327
|
-
process.stdin.pause();
|
|
328
|
-
try {
|
|
329
|
-
await new Promise((resolve, reject) => {
|
|
330
|
-
const child = node_child_process.spawn(codexExecutable, args, {
|
|
331
|
-
cwd: opts.path,
|
|
332
|
-
env: process.env,
|
|
333
|
-
stdio: "inherit",
|
|
334
|
-
signal: opts.abort,
|
|
335
|
-
shell: shouldUseShellForCodex(codexExecutable)
|
|
336
|
-
});
|
|
337
|
-
const abortChild = () => {
|
|
338
|
-
if (child.exitCode === null) {
|
|
339
|
-
api.logger.debug(`[CodexLocal] Abort received, terminating child tree pid=${child.pid ?? "none"}`);
|
|
340
|
-
terminateCodexLocalChild(child.pid);
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
opts.abort.addEventListener("abort", abortChild, { once: true });
|
|
344
|
-
const sessionPoll = setInterval(() => {
|
|
345
|
-
if (detectedSessionId) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
const nextSessionId = findLatestCodexSessionIdSince(startTime);
|
|
349
|
-
if (nextSessionId) {
|
|
350
|
-
detectedSessionId = nextSessionId;
|
|
351
|
-
api.logger.debug(`[CodexLocal] Detected session ID: ${nextSessionId}`);
|
|
352
|
-
opts.onSessionFound(nextSessionId);
|
|
353
|
-
}
|
|
354
|
-
}, 1e3);
|
|
355
|
-
child.on("error", (error) => {
|
|
356
|
-
clearInterval(sessionPoll);
|
|
357
|
-
opts.abort.removeEventListener("abort", abortChild);
|
|
358
|
-
reject(error);
|
|
359
|
-
});
|
|
360
|
-
child.on("exit", (code, signal) => {
|
|
361
|
-
clearInterval(sessionPoll);
|
|
362
|
-
opts.abort.removeEventListener("abort", abortChild);
|
|
363
|
-
opts.onThinkingChange?.(false);
|
|
364
|
-
if (signal === "SIGTERM" && opts.abort.aborted) {
|
|
365
|
-
resolve();
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
if (signal) {
|
|
369
|
-
reject(new Error(`Codex terminated with signal: ${signal}`));
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
if (code !== 0 && code !== null) {
|
|
373
|
-
reject(new CodexExitCodeError(code));
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
resolve();
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
} finally {
|
|
380
|
-
process.stdin.resume();
|
|
381
|
-
opts.onThinkingChange?.(false);
|
|
382
|
-
}
|
|
383
|
-
return detectedSessionId;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async function codexLocalLauncher(session) {
|
|
387
|
-
let exitReason = null;
|
|
388
|
-
const processAbortController = new AbortController();
|
|
389
|
-
const exitFuture = new future.Future();
|
|
390
|
-
try {
|
|
391
|
-
async function abort() {
|
|
392
|
-
if (!processAbortController.signal.aborted) {
|
|
393
|
-
processAbortController.abort();
|
|
394
|
-
}
|
|
395
|
-
await exitFuture.promise;
|
|
396
|
-
}
|
|
397
|
-
async function doSwitch() {
|
|
398
|
-
api.logger.debug("[codex-local]: switching to remote mode");
|
|
399
|
-
if (!exitReason) {
|
|
400
|
-
exitReason = { type: "switch" };
|
|
401
|
-
}
|
|
402
|
-
await abort();
|
|
403
|
-
}
|
|
404
|
-
async function doAbort() {
|
|
405
|
-
api.logger.debug("[codex-local]: abort requested");
|
|
406
|
-
if (!exitReason) {
|
|
407
|
-
exitReason = { type: "switch" };
|
|
408
|
-
}
|
|
409
|
-
session.queue.reset();
|
|
410
|
-
await abort();
|
|
411
|
-
}
|
|
412
|
-
session.client.rpcHandlerManager.registerHandler("abort", doAbort);
|
|
413
|
-
session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
|
|
414
|
-
session.queue.setOnMessage(() => {
|
|
415
|
-
if (session.isLocalModePinned()) {
|
|
416
|
-
api.logger.debug("[codex-local]: message arrived while local mode is pinned; staying local");
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
void doSwitch();
|
|
420
|
-
});
|
|
421
|
-
if (session.queue.size() > 0) {
|
|
422
|
-
if (session.isLocalModePinned()) {
|
|
423
|
-
api.logger.debug("[codex-local]: pending queued messages detected, but local mode is pinned; staying local");
|
|
424
|
-
} else {
|
|
425
|
-
return { type: "switch" };
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
while (true) {
|
|
429
|
-
if (exitReason) {
|
|
430
|
-
return exitReason;
|
|
431
|
-
}
|
|
432
|
-
try {
|
|
433
|
-
const sessionId = await codexLocal({
|
|
434
|
-
path: session.path,
|
|
435
|
-
sessionId: session.sessionId,
|
|
436
|
-
abort: processAbortController.signal,
|
|
437
|
-
codexArgs: session.codexArgs,
|
|
438
|
-
onSessionFound: session.onSessionFound,
|
|
439
|
-
onThinkingChange: session.onThinkingChange
|
|
440
|
-
});
|
|
441
|
-
if (sessionId) {
|
|
442
|
-
session.onSessionFound(sessionId);
|
|
443
|
-
}
|
|
444
|
-
if (!exitReason) {
|
|
445
|
-
exitReason = { type: "exit", code: 0 };
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
} catch (error) {
|
|
449
|
-
api.logger.debug("[codex-local]: launch error", error);
|
|
450
|
-
if (error instanceof CodexExitCodeError && !exitReason) {
|
|
451
|
-
exitReason = { type: "exit", code: error.exitCode };
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "EINVAL") && !exitReason) {
|
|
455
|
-
exitReason = { type: "exit", code: 1 };
|
|
456
|
-
session.runtimeSession.sendSessionEvent({ type: "message", message: "Codex CLI not found. Check your local Codex installation." });
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
if (!exitReason) {
|
|
460
|
-
session.runtimeSession.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
461
|
-
continue;
|
|
462
|
-
}
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
} finally {
|
|
467
|
-
exitFuture.resolve(void 0);
|
|
468
|
-
session.client.rpcHandlerManager.registerHandler("abort", async () => {
|
|
469
|
-
});
|
|
470
|
-
session.client.rpcHandlerManager.registerHandler("switch", async () => {
|
|
471
|
-
});
|
|
472
|
-
session.queue.setOnMessage(null);
|
|
473
|
-
}
|
|
474
|
-
return exitReason || { type: "exit", code: 0 };
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const CodexDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal, title }) => {
|
|
208
|
+
const CodexDisplay = ({ messageBuffer, logPath, onExit, title }) => {
|
|
478
209
|
const [messages, setMessages] = React.useState([]);
|
|
479
210
|
const [confirmationMode, setConfirmationMode] = React.useState(null);
|
|
480
211
|
const [actionInProgress, setActionInProgress] = React.useState(null);
|
|
@@ -523,21 +254,10 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal, title }
|
|
|
523
254
|
}
|
|
524
255
|
return;
|
|
525
256
|
}
|
|
526
|
-
if (input === " ") {
|
|
527
|
-
if (confirmationMode === "switch") {
|
|
528
|
-
resetConfirmation();
|
|
529
|
-
setActionInProgress("switching");
|
|
530
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
531
|
-
onSwitchToLocal?.();
|
|
532
|
-
} else {
|
|
533
|
-
setConfirmationWithTimeout("switch");
|
|
534
|
-
}
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
257
|
if (confirmationMode) {
|
|
538
258
|
resetConfirmation();
|
|
539
259
|
}
|
|
540
|
-
}, [confirmationMode, actionInProgress, onExit,
|
|
260
|
+
}, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
|
|
541
261
|
const getMessageColor = (type) => {
|
|
542
262
|
switch (type) {
|
|
543
263
|
case "user":
|
|
@@ -589,13 +309,13 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal, title }
|
|
|
589
309
|
{
|
|
590
310
|
width: terminalWidth,
|
|
591
311
|
borderStyle: "round",
|
|
592
|
-
borderColor: actionInProgress ? "gray" : confirmationMode === "exit" ? "red" :
|
|
312
|
+
borderColor: actionInProgress ? "gray" : confirmationMode === "exit" ? "red" : "green",
|
|
593
313
|
paddingX: 2,
|
|
594
314
|
justifyContent: "center",
|
|
595
315
|
alignItems: "center",
|
|
596
316
|
flexDirection: "column"
|
|
597
317
|
},
|
|
598
|
-
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", alignItems: "center" }, actionInProgress === "exiting" ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "Exiting agent...") :
|
|
318
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", alignItems: "center" }, actionInProgress === "exiting" ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode === "exit" ? /* @__PURE__ */ React.createElement(ink.Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ink.Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
|
|
599
319
|
));
|
|
600
320
|
};
|
|
601
321
|
|
|
@@ -847,6 +567,152 @@ class ReasoningProcessor extends BaseReasoningProcessor.BaseReasoningProcessor {
|
|
|
847
567
|
}
|
|
848
568
|
}
|
|
849
569
|
|
|
570
|
+
class ConversationHistory {
|
|
571
|
+
messages = [];
|
|
572
|
+
maxMessages;
|
|
573
|
+
maxCharacters;
|
|
574
|
+
constructor(options = {}) {
|
|
575
|
+
this.maxMessages = options.maxMessages ?? 20;
|
|
576
|
+
this.maxCharacters = options.maxCharacters ?? 5e4;
|
|
577
|
+
}
|
|
578
|
+
isDuplicate(role, content) {
|
|
579
|
+
if (this.messages.length === 0) {
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
583
|
+
const message = this.messages[index];
|
|
584
|
+
if (message.role !== role) {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
const normalizedIncoming = content.trim().replace(/\s+/g, " ");
|
|
588
|
+
const normalizedExisting = message.content.replace(/\s+/g, " ");
|
|
589
|
+
return normalizedIncoming === normalizedExisting;
|
|
590
|
+
}
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
addUserMessage(content) {
|
|
594
|
+
this.addMessage("user", content);
|
|
595
|
+
}
|
|
596
|
+
addAssistantMessage(content) {
|
|
597
|
+
this.addMessage("assistant", content);
|
|
598
|
+
}
|
|
599
|
+
hasHistory() {
|
|
600
|
+
return this.messages.length > 0;
|
|
601
|
+
}
|
|
602
|
+
size() {
|
|
603
|
+
return this.messages.length;
|
|
604
|
+
}
|
|
605
|
+
clear() {
|
|
606
|
+
this.messages = [];
|
|
607
|
+
api.logger.debug("[CodexConversationHistory] History cleared");
|
|
608
|
+
}
|
|
609
|
+
getContextForNewSession() {
|
|
610
|
+
if (this.messages.length === 0) {
|
|
611
|
+
return "";
|
|
612
|
+
}
|
|
613
|
+
const formattedMessages = this.messages.map((message) => {
|
|
614
|
+
const role = message.role === "user" ? "User" : "Assistant";
|
|
615
|
+
const content = message.content.length > 2e3 ? `${message.content.slice(0, 2e3)}... [truncated]` : message.content;
|
|
616
|
+
return `${role}: ${content}`;
|
|
617
|
+
}).join("\n\n");
|
|
618
|
+
return [
|
|
619
|
+
"[PREVIOUS CONVERSATION CONTEXT]",
|
|
620
|
+
"Continue from the prior Codex session using the conversation below as context.",
|
|
621
|
+
"",
|
|
622
|
+
formattedMessages,
|
|
623
|
+
"",
|
|
624
|
+
"[END OF PREVIOUS CONTEXT]",
|
|
625
|
+
""
|
|
626
|
+
].join("\n");
|
|
627
|
+
}
|
|
628
|
+
getSummary() {
|
|
629
|
+
const totalChars = this.messages.reduce((sum, message) => sum + message.content.length, 0);
|
|
630
|
+
const userCount = this.messages.filter((message) => message.role === "user").length;
|
|
631
|
+
const assistantCount = this.messages.filter((message) => message.role === "assistant").length;
|
|
632
|
+
return `${this.messages.length} messages (${userCount} user, ${assistantCount} assistant), ${totalChars} chars`;
|
|
633
|
+
}
|
|
634
|
+
addMessage(role, content) {
|
|
635
|
+
const trimmedContent = content.trim();
|
|
636
|
+
if (!trimmedContent) {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (this.isDuplicate(role, trimmedContent)) {
|
|
640
|
+
api.logger.debug(`[CodexConversationHistory] Skipping duplicate ${role} message (${trimmedContent.length} chars)`);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
this.messages.push({
|
|
644
|
+
role,
|
|
645
|
+
content: trimmedContent,
|
|
646
|
+
timestamp: Date.now()
|
|
647
|
+
});
|
|
648
|
+
this.trimHistory();
|
|
649
|
+
api.logger.debug(`[CodexConversationHistory] Added ${role} message (${trimmedContent.length} chars), total: ${this.messages.length}`);
|
|
650
|
+
}
|
|
651
|
+
trimHistory() {
|
|
652
|
+
while (this.messages.length > this.maxMessages) {
|
|
653
|
+
this.messages.shift();
|
|
654
|
+
}
|
|
655
|
+
let totalChars = this.messages.reduce((sum, message) => sum + message.content.length, 0);
|
|
656
|
+
while (totalChars > this.maxCharacters && this.messages.length > 1) {
|
|
657
|
+
const removed = this.messages.shift();
|
|
658
|
+
if (removed) {
|
|
659
|
+
totalChars -= removed.content.length;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function resolveCodexAcpExecutionMode(mode) {
|
|
666
|
+
const approvalPolicy = (() => {
|
|
667
|
+
switch (mode.permissionMode) {
|
|
668
|
+
case "default":
|
|
669
|
+
return "on-request";
|
|
670
|
+
case "read-only":
|
|
671
|
+
return "on-request";
|
|
672
|
+
case "safe-yolo":
|
|
673
|
+
return "on-request";
|
|
674
|
+
case "yolo":
|
|
675
|
+
return "never";
|
|
676
|
+
case "bypassPermissions":
|
|
677
|
+
return "never";
|
|
678
|
+
case "acceptEdits":
|
|
679
|
+
return "on-request";
|
|
680
|
+
case "plan":
|
|
681
|
+
return "on-request";
|
|
682
|
+
default:
|
|
683
|
+
return "on-request";
|
|
684
|
+
}
|
|
685
|
+
})();
|
|
686
|
+
const sandbox = (() => {
|
|
687
|
+
switch (mode.permissionMode) {
|
|
688
|
+
case "default":
|
|
689
|
+
return "workspace-write";
|
|
690
|
+
case "read-only":
|
|
691
|
+
return "read-only";
|
|
692
|
+
case "safe-yolo":
|
|
693
|
+
return "workspace-write";
|
|
694
|
+
case "yolo":
|
|
695
|
+
return "danger-full-access";
|
|
696
|
+
case "bypassPermissions":
|
|
697
|
+
return "danger-full-access";
|
|
698
|
+
case "acceptEdits":
|
|
699
|
+
return "workspace-write";
|
|
700
|
+
case "plan":
|
|
701
|
+
return "read-only";
|
|
702
|
+
default:
|
|
703
|
+
return "read-only";
|
|
704
|
+
}
|
|
705
|
+
})();
|
|
706
|
+
return {
|
|
707
|
+
approvalPolicy,
|
|
708
|
+
sandbox,
|
|
709
|
+
model: mode.model
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function getCodexExecutionFingerprint(mode) {
|
|
713
|
+
return registerKillSessionHandler.hashObject(resolveCodexAcpExecutionMode(mode));
|
|
714
|
+
}
|
|
715
|
+
|
|
850
716
|
function createAbortError() {
|
|
851
717
|
const error = new Error("Operation aborted");
|
|
852
718
|
error.name = "AbortError";
|
|
@@ -872,7 +738,8 @@ function normalizeCodexBackendError(error) {
|
|
|
872
738
|
const text = index.formatDisplayMessage(error).trim();
|
|
873
739
|
const stderrText = record ? index.formatDisplayMessage(record.stderr).trim() : "";
|
|
874
740
|
const detailText = record ? index.formatDisplayMessage(record.detail).trim() : "";
|
|
875
|
-
const
|
|
741
|
+
const dataText = record ? index.formatDisplayMessage(record.data).trim() : "";
|
|
742
|
+
const searchableText = [text, stderrText, detailText, dataText].filter(Boolean).join("\n");
|
|
876
743
|
const prefix = typeof error === "object" && error !== null ? [
|
|
877
744
|
record?.code !== void 0 && record?.code !== null ? `[code=${String(record.code)}]` : "",
|
|
878
745
|
record?.status !== void 0 && record?.status !== null ? `[status=${String(record.status)}]` : ""
|
|
@@ -885,6 +752,9 @@ function normalizeCodexBackendError(error) {
|
|
|
885
752
|
const hint = "The configured Codex ACP command does not speak the ACP protocol. Make sure HAPPY_CODEX_ACP_BIN points to codex-acp, not the codex CLI.";
|
|
886
753
|
return prefix ? `${prefix} ${hint}` : hint;
|
|
887
754
|
}
|
|
755
|
+
if (typeof record?.message === "string" && record.message.trim().toLowerCase() === "internal error" && dataText) {
|
|
756
|
+
return prefix ? `${prefix} ${dataText}` : dataText;
|
|
757
|
+
}
|
|
888
758
|
if (error instanceof Error && text) {
|
|
889
759
|
return text;
|
|
890
760
|
}
|
|
@@ -899,6 +769,9 @@ function normalizeCodexBackendError(error) {
|
|
|
899
769
|
}
|
|
900
770
|
return text || "Codex backend exited unexpectedly";
|
|
901
771
|
}
|
|
772
|
+
function getCodexLegacySwitchIgnoredMessage() {
|
|
773
|
+
return "Codex now runs in ACP-only mode. Staying in the current session.";
|
|
774
|
+
}
|
|
902
775
|
function shouldEmitCodexReadyAfterWait(opts) {
|
|
903
776
|
if (opts.readyAlreadySent) {
|
|
904
777
|
return false;
|
|
@@ -925,58 +798,10 @@ function handleIncomingCodexMessageDuringRemoteTurn(opts) {
|
|
|
925
798
|
}
|
|
926
799
|
return total > 0 || interruptedTurn;
|
|
927
800
|
}
|
|
928
|
-
function resolveCodexAcpExecutionMode(mode) {
|
|
929
|
-
const approvalPolicy = (() => {
|
|
930
|
-
switch (mode.permissionMode) {
|
|
931
|
-
case "default":
|
|
932
|
-
return "on-request";
|
|
933
|
-
case "read-only":
|
|
934
|
-
return "on-request";
|
|
935
|
-
case "safe-yolo":
|
|
936
|
-
return "on-request";
|
|
937
|
-
case "yolo":
|
|
938
|
-
return "never";
|
|
939
|
-
case "bypassPermissions":
|
|
940
|
-
return "never";
|
|
941
|
-
case "acceptEdits":
|
|
942
|
-
return "on-request";
|
|
943
|
-
case "plan":
|
|
944
|
-
return "on-request";
|
|
945
|
-
default:
|
|
946
|
-
return "on-request";
|
|
947
|
-
}
|
|
948
|
-
})();
|
|
949
|
-
const sandbox = (() => {
|
|
950
|
-
switch (mode.permissionMode) {
|
|
951
|
-
case "default":
|
|
952
|
-
return "workspace-write";
|
|
953
|
-
case "read-only":
|
|
954
|
-
return "read-only";
|
|
955
|
-
case "safe-yolo":
|
|
956
|
-
return "workspace-write";
|
|
957
|
-
case "yolo":
|
|
958
|
-
return "danger-full-access";
|
|
959
|
-
case "bypassPermissions":
|
|
960
|
-
return "danger-full-access";
|
|
961
|
-
case "acceptEdits":
|
|
962
|
-
return "workspace-write";
|
|
963
|
-
case "plan":
|
|
964
|
-
return "read-only";
|
|
965
|
-
default:
|
|
966
|
-
return "read-only";
|
|
967
|
-
}
|
|
968
|
-
})();
|
|
969
|
-
return {
|
|
970
|
-
approvalPolicy,
|
|
971
|
-
sandbox,
|
|
972
|
-
model: mode.model
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
801
|
async function codexRemoteLauncher(session) {
|
|
976
802
|
const messageBuffer = new registerKillSessionHandler.MessageBuffer();
|
|
977
803
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
978
804
|
let inkInstance = null;
|
|
979
|
-
let exitReason = null;
|
|
980
805
|
let shouldExit = false;
|
|
981
806
|
let abortController = new AbortController();
|
|
982
807
|
let turnInFlight = false;
|
|
@@ -989,8 +814,10 @@ async function codexRemoteLauncher(session) {
|
|
|
989
814
|
let accumulatedResponse = "";
|
|
990
815
|
let isResponseInProgress = false;
|
|
991
816
|
let taskStartedSent = false;
|
|
817
|
+
let shouldInjectHistoryOnNextSession = false;
|
|
992
818
|
const permissionHandler = new CodexPermissionHandler(session.client);
|
|
993
819
|
const selectionHandler = new CodexSelectionHandler(session.client);
|
|
820
|
+
const conversationHistory = new ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
|
|
994
821
|
const reasoningProcessor = new ReasoningProcessor((message) => {
|
|
995
822
|
session.runtimeSession.sendCodexMessage(message);
|
|
996
823
|
});
|
|
@@ -1043,10 +870,34 @@ async function codexRemoteLauncher(session) {
|
|
|
1043
870
|
api.logger.debug("[Codex] Error disposing ACP backend:", error);
|
|
1044
871
|
}
|
|
1045
872
|
};
|
|
873
|
+
const emitStatusMessage = (message) => {
|
|
874
|
+
messageBuffer.addMessage(message, "status");
|
|
875
|
+
session.runtimeSession.sendSessionEvent({ type: "message", message });
|
|
876
|
+
};
|
|
877
|
+
const emitUserVisibleErrorMessage = (message) => {
|
|
878
|
+
emitStatusMessage(message);
|
|
879
|
+
session.runtimeSession.sendCodexMessage({
|
|
880
|
+
type: "message",
|
|
881
|
+
message,
|
|
882
|
+
id: node_crypto.randomUUID()
|
|
883
|
+
});
|
|
884
|
+
};
|
|
885
|
+
const queueHistoryInjectionForRestart = (reason) => {
|
|
886
|
+
messageBuffer.addMessage("\u2550".repeat(40), "status");
|
|
887
|
+
if (conversationHistory.hasHistory()) {
|
|
888
|
+
shouldInjectHistoryOnNextSession = true;
|
|
889
|
+
const message = `${reason} Preserving ${conversationHistory.size()} earlier messages of context.`;
|
|
890
|
+
emitStatusMessage(message);
|
|
891
|
+
api.logger.debug(`[Codex] Will inject conversation history after restart: ${conversationHistory.getSummary()}`);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
emitStatusMessage(reason);
|
|
895
|
+
};
|
|
1046
896
|
const emitFinalAssistantMessage = () => {
|
|
1047
897
|
if (!accumulatedResponse.trim()) {
|
|
1048
898
|
return;
|
|
1049
899
|
}
|
|
900
|
+
conversationHistory.addAssistantMessage(accumulatedResponse);
|
|
1050
901
|
session.runtimeSession.sendCodexMessage({
|
|
1051
902
|
type: "message",
|
|
1052
903
|
message: accumulatedResponse,
|
|
@@ -1125,7 +976,8 @@ async function codexRemoteLauncher(session) {
|
|
|
1125
976
|
type: "tool-call-result",
|
|
1126
977
|
callId: msg.callId,
|
|
1127
978
|
output: msg.result,
|
|
1128
|
-
id: node_crypto.randomUUID()
|
|
979
|
+
id: node_crypto.randomUUID(),
|
|
980
|
+
isError
|
|
1129
981
|
});
|
|
1130
982
|
return;
|
|
1131
983
|
}
|
|
@@ -1203,7 +1055,8 @@ async function codexRemoteLauncher(session) {
|
|
|
1203
1055
|
stderr: msg.stderr,
|
|
1204
1056
|
success: msg.success
|
|
1205
1057
|
},
|
|
1206
|
-
id: node_crypto.randomUUID()
|
|
1058
|
+
id: node_crypto.randomUUID(),
|
|
1059
|
+
isError: !msg.success
|
|
1207
1060
|
});
|
|
1208
1061
|
return;
|
|
1209
1062
|
}
|
|
@@ -1261,11 +1114,9 @@ async function codexRemoteLauncher(session) {
|
|
|
1261
1114
|
return result.backend;
|
|
1262
1115
|
};
|
|
1263
1116
|
const handleSwitchToLocal = async () => {
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
shouldExit = true;
|
|
1268
|
-
await handleAbort();
|
|
1117
|
+
const message = getCodexLegacySwitchIgnoredMessage();
|
|
1118
|
+
api.logger.debug("[Codex] Ignoring legacy switch request because Codex is ACP-only");
|
|
1119
|
+
emitStatusMessage(message);
|
|
1269
1120
|
};
|
|
1270
1121
|
session.setPendingInteractionSuperseder((reason = BaseReasoningProcessor.INTERACTION_SUPERSEDED_ERROR) => {
|
|
1271
1122
|
return handleIncomingCodexMessageDuringRemoteTurn({
|
|
@@ -1326,17 +1177,9 @@ async function codexRemoteLauncher(session) {
|
|
|
1326
1177
|
inkInstance = ink.render(React.createElement(CodexDisplay, {
|
|
1327
1178
|
messageBuffer,
|
|
1328
1179
|
logPath: process.env.DEBUG ? session.logPath : void 0,
|
|
1329
|
-
title: "
|
|
1180
|
+
title: "Codex Agent Messages",
|
|
1330
1181
|
onExit: async () => {
|
|
1331
|
-
api.logger.debug("[Codex]
|
|
1332
|
-
exitReason = "exit";
|
|
1333
|
-
shouldExit = true;
|
|
1334
|
-
await handleAbort();
|
|
1335
|
-
},
|
|
1336
|
-
onSwitchToLocal: async () => {
|
|
1337
|
-
api.logger.debug("[Codex] Switching to local from local keyboard");
|
|
1338
|
-
session.pinLocalMode();
|
|
1339
|
-
exitReason = "switch";
|
|
1182
|
+
api.logger.debug("[Codex] Exiting Codex ACP session from keyboard");
|
|
1340
1183
|
shouldExit = true;
|
|
1341
1184
|
await handleAbort();
|
|
1342
1185
|
}
|
|
@@ -1376,8 +1219,7 @@ async function codexRemoteLauncher(session) {
|
|
|
1376
1219
|
break;
|
|
1377
1220
|
}
|
|
1378
1221
|
if (wasSessionCreated && currentModeHash && message.hash !== currentModeHash) {
|
|
1379
|
-
|
|
1380
|
-
messageBuffer.addMessage("Starting new Codex session (mode changed)...", "status");
|
|
1222
|
+
queueHistoryInjectionForRestart("Starting new Codex session (execution settings changed)...");
|
|
1381
1223
|
await disposeBackend();
|
|
1382
1224
|
session.clearSessionId();
|
|
1383
1225
|
currentModeHash = null;
|
|
@@ -1404,27 +1246,33 @@ async function codexRemoteLauncher(session) {
|
|
|
1404
1246
|
session.onSessionFound(sessionId);
|
|
1405
1247
|
}
|
|
1406
1248
|
session.onThinkingChange(true);
|
|
1407
|
-
|
|
1249
|
+
let promptToSend = message.message;
|
|
1250
|
+
if (shouldInjectHistoryOnNextSession && conversationHistory.hasHistory()) {
|
|
1251
|
+
const historyContext = conversationHistory.getContextForNewSession();
|
|
1252
|
+
promptToSend = historyContext + promptToSend;
|
|
1253
|
+
api.logger.debug(`[Codex] Injected conversation history context (${historyContext.length} chars)`);
|
|
1254
|
+
}
|
|
1255
|
+
conversationHistory.addUserMessage(message.message);
|
|
1256
|
+
await activeBackend.sendPrompt(acpSessionId, promptToSend);
|
|
1408
1257
|
await waitForResponseCompleteWithAbort(activeBackend, turnSignal);
|
|
1409
1258
|
reasoningProcessor.completeCurrent();
|
|
1259
|
+
shouldInjectHistoryOnNextSession = false;
|
|
1410
1260
|
} catch (error) {
|
|
1411
1261
|
api.logger.warn("Error in codex ACP session:", error);
|
|
1412
1262
|
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1413
|
-
const isExpectedInterruption = isAbortError || turnSignal.aborted || shouldExit
|
|
1414
|
-
if (
|
|
1415
|
-
messageBuffer.addMessage("Switching to local mode...", "status");
|
|
1416
|
-
session.runtimeSession.sendSessionEvent({ type: "message", message: "Switching to local mode..." });
|
|
1417
|
-
} else if (isExpectedInterruption) {
|
|
1263
|
+
const isExpectedInterruption = isAbortError || turnSignal.aborted || shouldExit;
|
|
1264
|
+
if (isExpectedInterruption) {
|
|
1418
1265
|
session.runtimeSession.sendCodexMessage({
|
|
1419
1266
|
type: "turn_aborted",
|
|
1420
1267
|
id: node_crypto.randomUUID()
|
|
1421
1268
|
});
|
|
1422
|
-
|
|
1423
|
-
session.runtimeSession.sendSessionEvent({ type: "message", message: "Aborted by user" });
|
|
1269
|
+
emitStatusMessage("Aborted by user");
|
|
1424
1270
|
} else {
|
|
1425
1271
|
const errorMessage = normalizeCodexBackendError(error);
|
|
1426
|
-
|
|
1427
|
-
|
|
1272
|
+
emitUserVisibleErrorMessage(errorMessage);
|
|
1273
|
+
if (conversationHistory.hasHistory()) {
|
|
1274
|
+
shouldInjectHistoryOnNextSession = true;
|
|
1275
|
+
}
|
|
1428
1276
|
await disposeBackend();
|
|
1429
1277
|
session.clearSessionId();
|
|
1430
1278
|
}
|
|
@@ -1478,33 +1326,16 @@ async function codexRemoteLauncher(session) {
|
|
|
1478
1326
|
}
|
|
1479
1327
|
messageBuffer.clear();
|
|
1480
1328
|
}
|
|
1481
|
-
api.logger.debug(
|
|
1482
|
-
return
|
|
1329
|
+
api.logger.debug("[Codex] ACP remote launcher returning: exit");
|
|
1330
|
+
return "exit";
|
|
1483
1331
|
}
|
|
1484
1332
|
|
|
1485
1333
|
async function codexLoop(opts) {
|
|
1486
|
-
|
|
1487
|
-
await opts.session.onModeChange(
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
const result = await codexLocalLauncher(opts.session);
|
|
1492
|
-
if (result.type === "switch") {
|
|
1493
|
-
opts.session.clearLocalModePin();
|
|
1494
|
-
mode = "remote";
|
|
1495
|
-
await opts.session.onModeChange(mode);
|
|
1496
|
-
continue;
|
|
1497
|
-
}
|
|
1498
|
-
return result.code;
|
|
1499
|
-
}
|
|
1500
|
-
const reason = await codexRemoteLauncher(opts.session);
|
|
1501
|
-
if (reason === "switch") {
|
|
1502
|
-
mode = "local";
|
|
1503
|
-
await opts.session.onModeChange(mode);
|
|
1504
|
-
continue;
|
|
1505
|
-
}
|
|
1506
|
-
return 0;
|
|
1507
|
-
}
|
|
1334
|
+
const displayMode = opts.startingMode ?? "local";
|
|
1335
|
+
await opts.session.onModeChange(displayMode);
|
|
1336
|
+
api.logger.debug(`[codex-loop] Starting ACP-only Codex launcher with display mode: ${displayMode}`);
|
|
1337
|
+
await codexRemoteLauncher(opts.session);
|
|
1338
|
+
return 0;
|
|
1508
1339
|
}
|
|
1509
1340
|
|
|
1510
1341
|
function supportsAgentStateUpdateEvents(sessionClient) {
|
|
@@ -1545,16 +1376,46 @@ async function syncControlledByUserState(sessionClient, controlledByUser) {
|
|
|
1545
1376
|
});
|
|
1546
1377
|
}
|
|
1547
1378
|
function shouldSupersedeCodexPendingInteractions(opts) {
|
|
1548
|
-
return
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
function resolveInitialCodexPermissionMode(opts) {
|
|
1382
|
+
if (opts.permissionMode) {
|
|
1383
|
+
return opts.permissionMode;
|
|
1384
|
+
}
|
|
1385
|
+
const startingMode = opts.startingMode ?? (opts.startedBy === "daemon" ? "remote" : "local");
|
|
1386
|
+
if (opts.startedBy === "daemon" && startingMode === "remote") {
|
|
1387
|
+
return "yolo";
|
|
1388
|
+
}
|
|
1389
|
+
return void 0;
|
|
1390
|
+
}
|
|
1391
|
+
function resolveQueuedCodexPermissionMode(opts) {
|
|
1392
|
+
if (opts.preserveCurrentOnDefault && opts.messagePermissionMode === "default" && opts.currentPermissionMode) {
|
|
1393
|
+
return opts.currentPermissionMode;
|
|
1394
|
+
}
|
|
1395
|
+
return opts.messagePermissionMode ?? opts.currentPermissionMode ?? "default";
|
|
1396
|
+
}
|
|
1397
|
+
function resolveIncomingCodexPermissionMode(opts) {
|
|
1398
|
+
const resolvedPermissionMode = resolveQueuedCodexPermissionMode(opts);
|
|
1399
|
+
return {
|
|
1400
|
+
resolvedPermissionMode,
|
|
1401
|
+
nextCurrentPermissionMode: resolvedPermissionMode
|
|
1402
|
+
};
|
|
1549
1403
|
}
|
|
1550
1404
|
async function runCodex(opts) {
|
|
1551
1405
|
const sessionTag = node_crypto.randomUUID();
|
|
1552
1406
|
api.connectionState.setBackend("Codex");
|
|
1553
|
-
|
|
1407
|
+
const requestedStartingMode = opts.startingMode ?? (opts.startedBy === "daemon" ? "remote" : "local");
|
|
1408
|
+
const initialPermissionMode = resolveInitialCodexPermissionMode({
|
|
1409
|
+
permissionMode: opts.permissionMode,
|
|
1410
|
+
startedBy: opts.startedBy,
|
|
1411
|
+
startingMode: requestedStartingMode
|
|
1412
|
+
});
|
|
1413
|
+
const preserveCurrentPermissionModeForRemoteDefault = opts.startedBy === "daemon" && requestedStartingMode === "remote";
|
|
1414
|
+
if (opts.startedBy === "daemon" && requestedStartingMode === "local") {
|
|
1554
1415
|
throw new Error("Daemon-spawned Codex sessions cannot use local mode.");
|
|
1555
1416
|
}
|
|
1556
1417
|
const api$1 = await api.ApiClient.create(opts.credentials);
|
|
1557
|
-
api.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"},
|
|
1418
|
+
api.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}, requestedStartingMode=${requestedStartingMode}, executionMode=acp-only`);
|
|
1558
1419
|
const settings = await persistence.readSettings();
|
|
1559
1420
|
const machineId = settings?.machineId;
|
|
1560
1421
|
if (!machineId) {
|
|
@@ -1603,18 +1464,21 @@ async function runCodex(opts) {
|
|
|
1603
1464
|
api.logger.debug("[START] Failed to report to daemon:", error);
|
|
1604
1465
|
}
|
|
1605
1466
|
}
|
|
1606
|
-
const messageQueue = new registerKillSessionHandler.MessageQueue2(
|
|
1607
|
-
|
|
1608
|
-
model: mode.model
|
|
1609
|
-
}));
|
|
1610
|
-
let currentPermissionMode;
|
|
1467
|
+
const messageQueue = new registerKillSessionHandler.MessageQueue2(getCodexExecutionFingerprint);
|
|
1468
|
+
let currentPermissionMode = initialPermissionMode;
|
|
1611
1469
|
let currentModel;
|
|
1612
1470
|
sessionClient.onUserMessage((message) => {
|
|
1613
|
-
|
|
1471
|
+
const previousPermissionMode = currentPermissionMode;
|
|
1472
|
+
let messagePermissionMode = previousPermissionMode;
|
|
1614
1473
|
if (message.meta?.permissionMode) {
|
|
1615
1474
|
messagePermissionMode = message.meta.permissionMode;
|
|
1616
|
-
currentPermissionMode = messagePermissionMode;
|
|
1617
1475
|
}
|
|
1476
|
+
const permissionResolution = resolveIncomingCodexPermissionMode({
|
|
1477
|
+
messagePermissionMode,
|
|
1478
|
+
currentPermissionMode: previousPermissionMode,
|
|
1479
|
+
preserveCurrentOnDefault: preserveCurrentPermissionModeForRemoteDefault
|
|
1480
|
+
});
|
|
1481
|
+
currentPermissionMode = permissionResolution.nextCurrentPermissionMode;
|
|
1618
1482
|
let messageModel = currentModel;
|
|
1619
1483
|
if (message.meta?.hasOwnProperty("model")) {
|
|
1620
1484
|
messageModel = message.meta.model || void 0;
|
|
@@ -1628,7 +1492,7 @@ async function runCodex(opts) {
|
|
|
1628
1492
|
codexSession?.supersedePendingInteractions(BaseReasoningProcessor.INTERACTION_SUPERSEDED_ERROR);
|
|
1629
1493
|
}
|
|
1630
1494
|
messageQueue.push(message.content.text, {
|
|
1631
|
-
permissionMode:
|
|
1495
|
+
permissionMode: permissionResolution.resolvedPermissionMode,
|
|
1632
1496
|
model: messageModel
|
|
1633
1497
|
});
|
|
1634
1498
|
});
|
|
@@ -1638,7 +1502,7 @@ async function runCodex(opts) {
|
|
|
1638
1502
|
path: process.cwd(),
|
|
1639
1503
|
logPath: api.logger.logFilePath,
|
|
1640
1504
|
sessionId: null,
|
|
1641
|
-
mode:
|
|
1505
|
+
mode: requestedStartingMode,
|
|
1642
1506
|
messageQueue,
|
|
1643
1507
|
codexArgs: opts.codexArgs,
|
|
1644
1508
|
onModeChange: async (mode) => {
|
|
@@ -1649,7 +1513,7 @@ async function runCodex(opts) {
|
|
|
1649
1513
|
try {
|
|
1650
1514
|
const exitCode = await codexLoop({
|
|
1651
1515
|
session: codexSession,
|
|
1652
|
-
startingMode:
|
|
1516
|
+
startingMode: requestedStartingMode
|
|
1653
1517
|
});
|
|
1654
1518
|
sessionClient.sendSessionDeath();
|
|
1655
1519
|
await sessionClient.flush();
|
|
@@ -1662,6 +1526,9 @@ async function runCodex(opts) {
|
|
|
1662
1526
|
}
|
|
1663
1527
|
}
|
|
1664
1528
|
|
|
1529
|
+
exports.resolveIncomingCodexPermissionMode = resolveIncomingCodexPermissionMode;
|
|
1530
|
+
exports.resolveInitialCodexPermissionMode = resolveInitialCodexPermissionMode;
|
|
1531
|
+
exports.resolveQueuedCodexPermissionMode = resolveQueuedCodexPermissionMode;
|
|
1665
1532
|
exports.runCodex = runCodex;
|
|
1666
1533
|
exports.shouldSupersedeCodexPendingInteractions = shouldSupersedeCodexPendingInteractions;
|
|
1667
1534
|
exports.supportsAgentStateUpdateEvents = supportsAgentStateUpdateEvents;
|