@evermore.work/adapter-codex-local 2026.509.0-canary.0
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/cli/format-event.d.ts +2 -0
- package/dist/cli/format-event.d.ts.map +1 -0
- package/dist/cli/format-event.js +213 -0
- package/dist/cli/format-event.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/quota-probe.d.ts +3 -0
- package/dist/cli/quota-probe.d.ts.map +1 -0
- package/dist/cli/quota-probe.js +97 -0
- package/dist/cli/quota-probe.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/server/codex-args.d.ts +11 -0
- package/dist/server/codex-args.d.ts.map +1 -0
- package/dist/server/codex-args.js +55 -0
- package/dist/server/codex-args.js.map +1 -0
- package/dist/server/codex-args.test.d.ts +2 -0
- package/dist/server/codex-args.test.d.ts.map +1 -0
- package/dist/server/codex-args.test.js +63 -0
- package/dist/server/codex-args.test.js.map +1 -0
- package/dist/server/codex-home.d.ts +15 -0
- package/dist/server/codex-home.d.ts.map +1 -0
- package/dist/server/codex-home.js +107 -0
- package/dist/server/codex-home.js.map +1 -0
- package/dist/server/execute.d.ts +15 -0
- package/dist/server/execute.d.ts.map +1 -0
- package/dist/server/execute.js +669 -0
- package/dist/server/execute.js.map +1 -0
- package/dist/server/execute.remote.test.d.ts +2 -0
- package/dist/server/execute.remote.test.d.ts.map +1 -0
- package/dist/server/execute.remote.test.js +382 -0
- package/dist/server/execute.remote.test.js.map +1 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +57 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/parse.d.ts +22 -0
- package/dist/server/parse.d.ts.map +1 -0
- package/dist/server/parse.js +213 -0
- package/dist/server/parse.js.map +1 -0
- package/dist/server/parse.test.d.ts +2 -0
- package/dist/server/parse.test.d.ts.map +1 -0
- package/dist/server/parse.test.js +107 -0
- package/dist/server/parse.test.js.map +1 -0
- package/dist/server/quota-spawn-error.test.d.ts +2 -0
- package/dist/server/quota-spawn-error.test.d.ts.map +1 -0
- package/dist/server/quota-spawn-error.test.js +77 -0
- package/dist/server/quota-spawn-error.test.js.map +1 -0
- package/dist/server/quota.d.ts +64 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +432 -0
- package/dist/server/quota.js.map +1 -0
- package/dist/server/skills.d.ts +8 -0
- package/dist/server/skills.d.ts.map +1 -0
- package/dist/server/skills.js +65 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/test.d.ts +3 -0
- package/dist/server/test.d.ts.map +1 -0
- package/dist/server/test.js +259 -0
- package/dist/server/test.js.map +1 -0
- package/dist/ui/build-config.d.ts +3 -0
- package/dist/ui/build-config.d.ts.map +1 -0
- package/dist/ui/build-config.js +113 -0
- package/dist/ui/build-config.js.map +1 -0
- package/dist/ui/build-config.test.d.ts +2 -0
- package/dist/ui/build-config.test.d.ts.map +1 -0
- package/dist/ui/build-config.test.js +49 -0
- package/dist/ui/build-config.test.js.map +1 -0
- package/dist/ui/index.d.ts +3 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/parse-stdout.d.ts +3 -0
- package/dist/ui/parse-stdout.d.ts.map +1 -0
- package/dist/ui/parse-stdout.js +261 -0
- package/dist/ui/parse-stdout.js.map +1 -0
- package/dist/ui/parse-stdout.test.d.ts +2 -0
- package/dist/ui/parse-stdout.test.d.ts.map +1 -0
- package/dist/ui/parse-stdout.test.js +77 -0
- package/dist/ui/parse-stdout.test.js.map +1 -0
- package/package.json +55 -0
- package/skills/diagnose-why-work-stopped/SKILL.md +161 -0
- package/skills/evermore/SKILL.md +366 -0
- package/skills/evermore/references/api-reference.md +899 -0
- package/skills/evermore/references/company-skills.md +193 -0
- package/skills/evermore/references/issue-workspaces.md +80 -0
- package/skills/evermore/references/routines.md +187 -0
- package/skills/evermore/references/workflows.md +141 -0
- package/skills/evermore-converting-plans-to-tasks/SKILL.md +42 -0
- package/skills/evermore-create-agent/SKILL.md +163 -0
- package/skills/evermore-create-agent/references/agent-instruction-templates.md +123 -0
- package/skills/evermore-create-agent/references/agents/coder.md +64 -0
- package/skills/evermore-create-agent/references/agents/qa.md +88 -0
- package/skills/evermore-create-agent/references/agents/securityengineer.md +135 -0
- package/skills/evermore-create-agent/references/agents/uxdesigner.md +115 -0
- package/skills/evermore-create-agent/references/api-reference.md +110 -0
- package/skills/evermore-create-agent/references/baseline-role-guide.md +168 -0
- package/skills/evermore-create-agent/references/draft-review-checklist.md +95 -0
- package/skills/evermore-create-plugin/SKILL.md +101 -0
- package/skills/evermore-dev/SKILL.md +267 -0
- package/skills/para-memory-files/SKILL.md +104 -0
- package/skills/para-memory-files/references/schemas.md +35 -0
- package/skills/terminal-bench-loop/SKILL.md +236 -0
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { inferOpenAiCompatibleBiller } from "@evermore.work/adapter-utils";
|
|
5
|
+
import { adapterExecutionTargetIsRemote, adapterExecutionTargetRemoteCwd, adapterExecutionTargetSessionIdentity, adapterExecutionTargetSessionMatches, adapterExecutionTargetUsesEvermoreBridge, describeAdapterExecutionTarget, ensureAdapterExecutionTargetCommandResolvable, ensureAdapterExecutionTargetRuntimeCommandInstalled, prepareAdapterExecutionTargetRuntime, readAdapterExecutionTarget, resolveAdapterExecutionTargetCommandForLogs, runAdapterExecutionTargetProcess, startAdapterExecutionTargetEvermoreBridge, } from "@evermore.work/adapter-utils/execution-target";
|
|
6
|
+
import { asString, asNumber, parseObject, applyEvermoreWorkspaceEnv, buildEvermoreEnv, buildInvocationEnvForLogs, ensureAbsoluteDirectory, ensureEvermoreSkillSymlink, ensurePathInEnv, readEvermoreRuntimeSkillEntries, readEvermoreIssueWorkModeFromContext, renderTemplate, renderEvermoreWakePrompt, shapeEvermoreWorkspaceEnvForExecution, stringifyEvermoreWakePayload, DEFAULT_EVERMORE_AGENT_PROMPT_TEMPLATE, joinPromptSections, } from "@evermore.work/adapter-utils/server-utils";
|
|
7
|
+
import { parseCodexJsonl, extractCodexRetryNotBefore, isCodexTransientUpstreamError, isCodexUnknownSessionError, } from "./parse.js";
|
|
8
|
+
import { pathExists, prepareManagedCodexHome, resolveManagedCodexHomeDir, resolveSharedCodexHomeDir } from "./codex-home.js";
|
|
9
|
+
import { resolveCodexDesiredSkillNames } from "./skills.js";
|
|
10
|
+
import { buildCodexExecArgs } from "./codex-args.js";
|
|
11
|
+
import { SANDBOX_INSTALL_COMMAND } from "../index.js";
|
|
12
|
+
const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const CODEX_ROLLOUT_NOISE_RE = /^\d{4}-\d{2}-\d{2}T[^\s]+\s+ERROR\s+codex_core::rollout::list:\s+state db missing rollout path for thread\s+[a-z0-9-]+$/i;
|
|
14
|
+
function stripCodexRolloutNoise(text) {
|
|
15
|
+
const parts = text.split(/\r?\n/);
|
|
16
|
+
const kept = [];
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
const trimmed = part.trim();
|
|
19
|
+
if (!trimmed) {
|
|
20
|
+
kept.push(part);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (CODEX_ROLLOUT_NOISE_RE.test(trimmed))
|
|
24
|
+
continue;
|
|
25
|
+
kept.push(part);
|
|
26
|
+
}
|
|
27
|
+
return kept.join("\n");
|
|
28
|
+
}
|
|
29
|
+
function firstNonEmptyLine(text) {
|
|
30
|
+
return (text
|
|
31
|
+
.split(/\r?\n/)
|
|
32
|
+
.map((line) => line.trim())
|
|
33
|
+
.find(Boolean) ?? "");
|
|
34
|
+
}
|
|
35
|
+
function hasNonEmptyEnvValue(env, key) {
|
|
36
|
+
const raw = env[key];
|
|
37
|
+
return typeof raw === "string" && raw.trim().length > 0;
|
|
38
|
+
}
|
|
39
|
+
function resolveCodexBillingType(env) {
|
|
40
|
+
// Codex uses API-key auth when OPENAI_API_KEY is present; otherwise rely on local login/session auth.
|
|
41
|
+
return hasNonEmptyEnvValue(env, "OPENAI_API_KEY") ? "api" : "subscription";
|
|
42
|
+
}
|
|
43
|
+
function resolveCodexBiller(env, billingType) {
|
|
44
|
+
const openAiCompatibleBiller = inferOpenAiCompatibleBiller(env, "openai");
|
|
45
|
+
if (openAiCompatibleBiller === "openrouter")
|
|
46
|
+
return "openrouter";
|
|
47
|
+
return billingType === "subscription" ? "chatgpt" : openAiCompatibleBiller ?? "openai";
|
|
48
|
+
}
|
|
49
|
+
async function isLikelyEvermoreRepoRoot(candidate) {
|
|
50
|
+
const [hasWorkspace, hasPackageJson, hasServerDir, hasAdapterUtilsDir] = await Promise.all([
|
|
51
|
+
pathExists(path.join(candidate, "pnpm-workspace.yaml")),
|
|
52
|
+
pathExists(path.join(candidate, "package.json")),
|
|
53
|
+
pathExists(path.join(candidate, "server")),
|
|
54
|
+
pathExists(path.join(candidate, "packages", "adapter-utils")),
|
|
55
|
+
]);
|
|
56
|
+
return hasWorkspace && hasPackageJson && hasServerDir && hasAdapterUtilsDir;
|
|
57
|
+
}
|
|
58
|
+
async function isLikelyEvermoreRuntimeSkillPath(candidate, skillName, options = {}) {
|
|
59
|
+
if (path.basename(candidate) !== skillName)
|
|
60
|
+
return false;
|
|
61
|
+
const skillsRoot = path.dirname(candidate);
|
|
62
|
+
if (path.basename(skillsRoot) !== "skills")
|
|
63
|
+
return false;
|
|
64
|
+
if (options.requireSkillMarkdown !== false && !(await pathExists(path.join(candidate, "SKILL.md")))) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
let cursor = path.dirname(skillsRoot);
|
|
68
|
+
for (let depth = 0; depth < 6; depth += 1) {
|
|
69
|
+
if (await isLikelyEvermoreRepoRoot(cursor))
|
|
70
|
+
return true;
|
|
71
|
+
const parent = path.dirname(cursor);
|
|
72
|
+
if (parent === cursor)
|
|
73
|
+
break;
|
|
74
|
+
cursor = parent;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
async function pruneBrokenUnavailableEvermoreSkillSymlinks(skillsHome, allowedSkillNames, onLog) {
|
|
79
|
+
const allowed = new Set(Array.from(allowedSkillNames));
|
|
80
|
+
const entries = await fs.readdir(skillsHome, { withFileTypes: true }).catch(() => []);
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
if (allowed.has(entry.name) || !entry.isSymbolicLink())
|
|
83
|
+
continue;
|
|
84
|
+
const target = path.join(skillsHome, entry.name);
|
|
85
|
+
const linkedPath = await fs.readlink(target).catch(() => null);
|
|
86
|
+
if (!linkedPath)
|
|
87
|
+
continue;
|
|
88
|
+
const resolvedLinkedPath = path.resolve(path.dirname(target), linkedPath);
|
|
89
|
+
if (await pathExists(resolvedLinkedPath))
|
|
90
|
+
continue;
|
|
91
|
+
if (!(await isLikelyEvermoreRuntimeSkillPath(resolvedLinkedPath, entry.name, {
|
|
92
|
+
requireSkillMarkdown: false,
|
|
93
|
+
}))) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
await fs.unlink(target).catch(() => { });
|
|
97
|
+
await onLog("stdout", `[evermore] Removed stale Codex skill "${entry.name}" from ${skillsHome}\n`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function resolveCodexSkillsDir(codexHome) {
|
|
101
|
+
return path.join(codexHome, "skills");
|
|
102
|
+
}
|
|
103
|
+
function readCodexTransientFallbackMode(context) {
|
|
104
|
+
const value = asString(context.codexTransientFallbackMode, "").trim();
|
|
105
|
+
switch (value) {
|
|
106
|
+
case "same_session":
|
|
107
|
+
case "safer_invocation":
|
|
108
|
+
case "fresh_session":
|
|
109
|
+
case "fresh_session_safer_invocation":
|
|
110
|
+
return value;
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function fallbackModeUsesSaferInvocation(mode) {
|
|
116
|
+
return mode === "safer_invocation" || mode === "fresh_session_safer_invocation";
|
|
117
|
+
}
|
|
118
|
+
function fallbackModeUsesFreshSession(mode) {
|
|
119
|
+
return mode === "fresh_session" || mode === "fresh_session_safer_invocation";
|
|
120
|
+
}
|
|
121
|
+
function buildCodexTransientHandoffNote(input) {
|
|
122
|
+
return [
|
|
123
|
+
"Evermore session handoff:",
|
|
124
|
+
input.previousSessionId ? `- Previous session: ${input.previousSessionId}` : "",
|
|
125
|
+
"- Rotation reason: repeated Codex transient remote-compaction failures",
|
|
126
|
+
`- Fallback mode: ${input.fallbackMode}`,
|
|
127
|
+
input.continuationSummaryBody
|
|
128
|
+
? `- Issue continuation summary: ${input.continuationSummaryBody.slice(0, 1_500)}`
|
|
129
|
+
: "",
|
|
130
|
+
"Continue from the current task state. Rebuild only the minimum context you need.",
|
|
131
|
+
]
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.join("\n");
|
|
134
|
+
}
|
|
135
|
+
export async function ensureCodexSkillsInjected(onLog, options = {}) {
|
|
136
|
+
const allSkillsEntries = options.skillsEntries ?? await readEvermoreRuntimeSkillEntries({}, __moduleDir);
|
|
137
|
+
const desiredSkillNames = options.desiredSkillNames ?? allSkillsEntries.map((entry) => entry.key);
|
|
138
|
+
const desiredSet = new Set(desiredSkillNames);
|
|
139
|
+
const skillsEntries = allSkillsEntries.filter((entry) => desiredSet.has(entry.key));
|
|
140
|
+
if (skillsEntries.length === 0)
|
|
141
|
+
return;
|
|
142
|
+
const skillsHome = options.skillsHome ?? resolveCodexSkillsDir(resolveSharedCodexHomeDir());
|
|
143
|
+
await fs.mkdir(skillsHome, { recursive: true });
|
|
144
|
+
const linkSkill = options.linkSkill;
|
|
145
|
+
for (const entry of skillsEntries) {
|
|
146
|
+
const target = path.join(skillsHome, entry.runtimeName);
|
|
147
|
+
try {
|
|
148
|
+
const existing = await fs.lstat(target).catch(() => null);
|
|
149
|
+
if (existing?.isSymbolicLink()) {
|
|
150
|
+
const linkedPath = await fs.readlink(target).catch(() => null);
|
|
151
|
+
const resolvedLinkedPath = linkedPath
|
|
152
|
+
? path.resolve(path.dirname(target), linkedPath)
|
|
153
|
+
: null;
|
|
154
|
+
if (resolvedLinkedPath &&
|
|
155
|
+
resolvedLinkedPath !== entry.source &&
|
|
156
|
+
(await isLikelyEvermoreRuntimeSkillPath(resolvedLinkedPath, entry.runtimeName))) {
|
|
157
|
+
await fs.unlink(target);
|
|
158
|
+
if (linkSkill) {
|
|
159
|
+
await linkSkill(entry.source, target);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
await fs.symlink(entry.source, target);
|
|
163
|
+
}
|
|
164
|
+
await onLog("stdout", `[evermore] Repaired Codex skill "${entry.runtimeName}" into ${skillsHome}\n`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const result = await ensureEvermoreSkillSymlink(entry.source, target, linkSkill);
|
|
169
|
+
if (result === "skipped")
|
|
170
|
+
continue;
|
|
171
|
+
await onLog("stdout", `[evermore] ${result === "repaired" ? "Repaired" : "Injected"} Codex skill "${entry.runtimeName}" into ${skillsHome}\n`);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
await onLog("stderr", `[evermore] Failed to inject Codex skill "${entry.key}" into ${skillsHome}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
await pruneBrokenUnavailableEvermoreSkillSymlinks(skillsHome, skillsEntries.map((entry) => entry.runtimeName), onLog);
|
|
178
|
+
}
|
|
179
|
+
export async function execute(ctx) {
|
|
180
|
+
const { runId, agent, runtime, config, context, onLog, onMeta, onSpawn, authToken } = ctx;
|
|
181
|
+
const promptTemplate = asString(config.promptTemplate, DEFAULT_EVERMORE_AGENT_PROMPT_TEMPLATE);
|
|
182
|
+
const command = asString(config.command, "codex");
|
|
183
|
+
const model = asString(config.model, "");
|
|
184
|
+
const workspaceContext = parseObject(context.evermoreWorkspace);
|
|
185
|
+
const workspaceCwd = asString(workspaceContext.cwd, "");
|
|
186
|
+
const workspaceSource = asString(workspaceContext.source, "");
|
|
187
|
+
const workspaceStrategy = asString(workspaceContext.strategy, "");
|
|
188
|
+
const workspaceId = asString(workspaceContext.workspaceId, "");
|
|
189
|
+
const workspaceRepoUrl = asString(workspaceContext.repoUrl, "");
|
|
190
|
+
const workspaceRepoRef = asString(workspaceContext.repoRef, "");
|
|
191
|
+
const workspaceBranch = asString(workspaceContext.branchName, "");
|
|
192
|
+
const workspaceWorktreePath = asString(workspaceContext.worktreePath, "");
|
|
193
|
+
const agentHome = asString(workspaceContext.agentHome, "");
|
|
194
|
+
const workspaceHints = Array.isArray(context.evermoreWorkspaces)
|
|
195
|
+
? context.evermoreWorkspaces.filter((value) => typeof value === "object" && value !== null)
|
|
196
|
+
: [];
|
|
197
|
+
const runtimeServiceIntents = Array.isArray(context.evermoreRuntimeServiceIntents)
|
|
198
|
+
? context.evermoreRuntimeServiceIntents.filter((value) => typeof value === "object" && value !== null)
|
|
199
|
+
: [];
|
|
200
|
+
const runtimeServices = Array.isArray(context.evermoreRuntimeServices)
|
|
201
|
+
? context.evermoreRuntimeServices.filter((value) => typeof value === "object" && value !== null)
|
|
202
|
+
: [];
|
|
203
|
+
const runtimePrimaryUrl = asString(context.evermoreRuntimePrimaryUrl, "");
|
|
204
|
+
const configuredCwd = asString(config.cwd, "");
|
|
205
|
+
const useConfiguredInsteadOfAgentHome = workspaceSource === "agent_home" && configuredCwd.length > 0;
|
|
206
|
+
const effectiveWorkspaceCwd = useConfiguredInsteadOfAgentHome ? "" : workspaceCwd;
|
|
207
|
+
const cwd = effectiveWorkspaceCwd || configuredCwd || process.cwd();
|
|
208
|
+
const envConfig = parseObject(config.env);
|
|
209
|
+
const executionTarget = readAdapterExecutionTarget({
|
|
210
|
+
executionTarget: ctx.executionTarget,
|
|
211
|
+
legacyRemoteExecution: ctx.executionTransport?.remoteExecution,
|
|
212
|
+
});
|
|
213
|
+
const executionTargetIsRemote = adapterExecutionTargetIsRemote(executionTarget);
|
|
214
|
+
const configuredCodexHome = typeof envConfig.CODEX_HOME === "string" && envConfig.CODEX_HOME.trim().length > 0
|
|
215
|
+
? path.resolve(envConfig.CODEX_HOME.trim())
|
|
216
|
+
: null;
|
|
217
|
+
const codexSkillEntries = await readEvermoreRuntimeSkillEntries(config, __moduleDir);
|
|
218
|
+
const desiredSkillNames = resolveCodexDesiredSkillNames(config, codexSkillEntries);
|
|
219
|
+
await ensureAbsoluteDirectory(cwd, { createIfMissing: true });
|
|
220
|
+
const configuredOpenAiApiKey = typeof envConfig.OPENAI_API_KEY === "string" && envConfig.OPENAI_API_KEY.trim().length > 0
|
|
221
|
+
? envConfig.OPENAI_API_KEY.trim()
|
|
222
|
+
: null;
|
|
223
|
+
const preparedManagedCodexHome = configuredCodexHome
|
|
224
|
+
? null
|
|
225
|
+
: await prepareManagedCodexHome(process.env, onLog, agent.companyId, {
|
|
226
|
+
apiKey: configuredOpenAiApiKey,
|
|
227
|
+
});
|
|
228
|
+
const defaultCodexHome = resolveManagedCodexHomeDir(process.env, agent.companyId);
|
|
229
|
+
const effectiveCodexHome = configuredCodexHome ?? preparedManagedCodexHome ?? defaultCodexHome;
|
|
230
|
+
await fs.mkdir(effectiveCodexHome, { recursive: true });
|
|
231
|
+
// Inject skills into the same CODEX_HOME that Codex will actually run with
|
|
232
|
+
// (managed home in the default case, or an explicit override from adapter config).
|
|
233
|
+
const codexSkillsDir = resolveCodexSkillsDir(effectiveCodexHome);
|
|
234
|
+
await ensureCodexSkillsInjected(onLog, {
|
|
235
|
+
skillsHome: codexSkillsDir,
|
|
236
|
+
skillsEntries: codexSkillEntries,
|
|
237
|
+
desiredSkillNames,
|
|
238
|
+
});
|
|
239
|
+
const effectiveExecutionCwd = adapterExecutionTargetRemoteCwd(executionTarget, cwd);
|
|
240
|
+
const shapedWorkspaceEnv = shapeEvermoreWorkspaceEnvForExecution({
|
|
241
|
+
workspaceCwd: effectiveWorkspaceCwd,
|
|
242
|
+
workspaceWorktreePath,
|
|
243
|
+
workspaceHints,
|
|
244
|
+
executionTargetIsRemote,
|
|
245
|
+
executionCwd: effectiveExecutionCwd,
|
|
246
|
+
});
|
|
247
|
+
const preparedExecutionTargetRuntime = executionTargetIsRemote
|
|
248
|
+
? await (async () => {
|
|
249
|
+
await onLog("stdout", `[evermore] Syncing workspace and CODEX_HOME to ${describeAdapterExecutionTarget(executionTarget)}.\n`);
|
|
250
|
+
return await prepareAdapterExecutionTargetRuntime({
|
|
251
|
+
target: executionTarget,
|
|
252
|
+
adapterKey: "codex",
|
|
253
|
+
workspaceLocalDir: cwd,
|
|
254
|
+
installCommand: SANDBOX_INSTALL_COMMAND,
|
|
255
|
+
detectCommand: command,
|
|
256
|
+
assets: [
|
|
257
|
+
{
|
|
258
|
+
key: "home",
|
|
259
|
+
localDir: effectiveCodexHome,
|
|
260
|
+
followSymlinks: true,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
});
|
|
264
|
+
})()
|
|
265
|
+
: null;
|
|
266
|
+
const restoreRemoteWorkspace = preparedExecutionTargetRuntime
|
|
267
|
+
? () => preparedExecutionTargetRuntime.restoreWorkspace()
|
|
268
|
+
: null;
|
|
269
|
+
let evermoreBridge = null;
|
|
270
|
+
const remoteCodexHome = executionTargetIsRemote
|
|
271
|
+
? preparedExecutionTargetRuntime?.assetDirs.home ??
|
|
272
|
+
path.posix.join(effectiveExecutionCwd, ".evermore-runtime", "codex", "home")
|
|
273
|
+
: null;
|
|
274
|
+
const hasExplicitApiKey = typeof envConfig.EVERMORE_API_KEY === "string" && envConfig.EVERMORE_API_KEY.trim().length > 0;
|
|
275
|
+
const env = { ...buildEvermoreEnv(agent) };
|
|
276
|
+
env.EVERMORE_RUN_ID = runId;
|
|
277
|
+
const wakeTaskId = (typeof context.taskId === "string" && context.taskId.trim().length > 0 && context.taskId.trim()) ||
|
|
278
|
+
(typeof context.issueId === "string" && context.issueId.trim().length > 0 && context.issueId.trim()) ||
|
|
279
|
+
null;
|
|
280
|
+
const wakeReason = typeof context.wakeReason === "string" && context.wakeReason.trim().length > 0
|
|
281
|
+
? context.wakeReason.trim()
|
|
282
|
+
: null;
|
|
283
|
+
const wakeCommentId = (typeof context.wakeCommentId === "string" && context.wakeCommentId.trim().length > 0 && context.wakeCommentId.trim()) ||
|
|
284
|
+
(typeof context.commentId === "string" && context.commentId.trim().length > 0 && context.commentId.trim()) ||
|
|
285
|
+
null;
|
|
286
|
+
const approvalId = typeof context.approvalId === "string" && context.approvalId.trim().length > 0
|
|
287
|
+
? context.approvalId.trim()
|
|
288
|
+
: null;
|
|
289
|
+
const approvalStatus = typeof context.approvalStatus === "string" && context.approvalStatus.trim().length > 0
|
|
290
|
+
? context.approvalStatus.trim()
|
|
291
|
+
: null;
|
|
292
|
+
const linkedIssueIds = Array.isArray(context.issueIds)
|
|
293
|
+
? context.issueIds.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
294
|
+
: [];
|
|
295
|
+
const wakePayloadJson = stringifyEvermoreWakePayload(context.evermoreWake);
|
|
296
|
+
const issueWorkMode = readEvermoreIssueWorkModeFromContext(context);
|
|
297
|
+
if (wakeTaskId) {
|
|
298
|
+
env.EVERMORE_TASK_ID = wakeTaskId;
|
|
299
|
+
}
|
|
300
|
+
if (issueWorkMode) {
|
|
301
|
+
env.EVERMORE_ISSUE_WORK_MODE = issueWorkMode;
|
|
302
|
+
}
|
|
303
|
+
if (wakeReason) {
|
|
304
|
+
env.EVERMORE_WAKE_REASON = wakeReason;
|
|
305
|
+
}
|
|
306
|
+
if (wakeCommentId) {
|
|
307
|
+
env.EVERMORE_WAKE_COMMENT_ID = wakeCommentId;
|
|
308
|
+
}
|
|
309
|
+
if (approvalId) {
|
|
310
|
+
env.EVERMORE_APPROVAL_ID = approvalId;
|
|
311
|
+
}
|
|
312
|
+
if (approvalStatus) {
|
|
313
|
+
env.EVERMORE_APPROVAL_STATUS = approvalStatus;
|
|
314
|
+
}
|
|
315
|
+
if (linkedIssueIds.length > 0) {
|
|
316
|
+
env.EVERMORE_LINKED_ISSUE_IDS = linkedIssueIds.join(",");
|
|
317
|
+
}
|
|
318
|
+
if (wakePayloadJson) {
|
|
319
|
+
env.EVERMORE_WAKE_PAYLOAD_JSON = wakePayloadJson;
|
|
320
|
+
}
|
|
321
|
+
applyEvermoreWorkspaceEnv(env, {
|
|
322
|
+
workspaceCwd: shapedWorkspaceEnv.workspaceCwd,
|
|
323
|
+
workspaceSource,
|
|
324
|
+
workspaceStrategy,
|
|
325
|
+
workspaceId,
|
|
326
|
+
workspaceRepoUrl,
|
|
327
|
+
workspaceRepoRef,
|
|
328
|
+
workspaceBranch,
|
|
329
|
+
workspaceWorktreePath: shapedWorkspaceEnv.workspaceWorktreePath,
|
|
330
|
+
agentHome,
|
|
331
|
+
});
|
|
332
|
+
if (shapedWorkspaceEnv.workspaceHints.length > 0) {
|
|
333
|
+
env.EVERMORE_WORKSPACES_JSON = JSON.stringify(shapedWorkspaceEnv.workspaceHints);
|
|
334
|
+
}
|
|
335
|
+
if (runtimeServiceIntents.length > 0) {
|
|
336
|
+
env.EVERMORE_RUNTIME_SERVICE_INTENTS_JSON = JSON.stringify(runtimeServiceIntents);
|
|
337
|
+
}
|
|
338
|
+
if (runtimeServices.length > 0) {
|
|
339
|
+
env.EVERMORE_RUNTIME_SERVICES_JSON = JSON.stringify(runtimeServices);
|
|
340
|
+
}
|
|
341
|
+
if (runtimePrimaryUrl) {
|
|
342
|
+
env.EVERMORE_RUNTIME_PRIMARY_URL = runtimePrimaryUrl;
|
|
343
|
+
}
|
|
344
|
+
for (const [k, v] of Object.entries(envConfig)) {
|
|
345
|
+
if (typeof v === "string")
|
|
346
|
+
env[k] = v;
|
|
347
|
+
}
|
|
348
|
+
env.CODEX_HOME = remoteCodexHome ?? effectiveCodexHome;
|
|
349
|
+
if (!hasExplicitApiKey && authToken) {
|
|
350
|
+
env.EVERMORE_API_KEY = authToken;
|
|
351
|
+
}
|
|
352
|
+
if (executionTargetIsRemote && adapterExecutionTargetUsesEvermoreBridge(executionTarget)) {
|
|
353
|
+
evermoreBridge = await startAdapterExecutionTargetEvermoreBridge({
|
|
354
|
+
runId,
|
|
355
|
+
target: executionTarget,
|
|
356
|
+
runtimeRootDir: preparedExecutionTargetRuntime?.runtimeRootDir,
|
|
357
|
+
adapterKey: "codex",
|
|
358
|
+
hostApiToken: env.EVERMORE_API_KEY,
|
|
359
|
+
onLog,
|
|
360
|
+
});
|
|
361
|
+
if (evermoreBridge) {
|
|
362
|
+
Object.assign(env, evermoreBridge.env);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const effectiveEnv = Object.fromEntries(Object.entries({ ...process.env, ...env }).filter((entry) => typeof entry[1] === "string"));
|
|
366
|
+
const billingType = resolveCodexBillingType(effectiveEnv);
|
|
367
|
+
const runtimeEnv = Object.fromEntries(Object.entries(ensurePathInEnv(effectiveEnv)).filter((entry) => typeof entry[1] === "string"));
|
|
368
|
+
await ensureAdapterExecutionTargetRuntimeCommandInstalled({
|
|
369
|
+
runId,
|
|
370
|
+
target: executionTarget,
|
|
371
|
+
installCommand: ctx.runtimeCommandSpec?.installCommand,
|
|
372
|
+
detectCommand: ctx.runtimeCommandSpec?.detectCommand,
|
|
373
|
+
cwd,
|
|
374
|
+
env: runtimeEnv,
|
|
375
|
+
timeoutSec: asNumber(config.timeoutSec, 0),
|
|
376
|
+
graceSec: asNumber(config.graceSec, 20),
|
|
377
|
+
onLog,
|
|
378
|
+
});
|
|
379
|
+
await ensureAdapterExecutionTargetCommandResolvable(command, executionTarget, cwd, runtimeEnv);
|
|
380
|
+
const resolvedCommand = await resolveAdapterExecutionTargetCommandForLogs(command, executionTarget, cwd, runtimeEnv);
|
|
381
|
+
const loggedEnv = buildInvocationEnvForLogs(env, {
|
|
382
|
+
runtimeEnv,
|
|
383
|
+
includeRuntimeKeys: ["HOME"],
|
|
384
|
+
resolvedCommand,
|
|
385
|
+
});
|
|
386
|
+
const timeoutSec = asNumber(config.timeoutSec, 0);
|
|
387
|
+
const graceSec = asNumber(config.graceSec, 20);
|
|
388
|
+
const runtimeSessionParams = parseObject(runtime.sessionParams);
|
|
389
|
+
const runtimeSessionId = asString(runtimeSessionParams.sessionId, runtime.sessionId ?? "");
|
|
390
|
+
const runtimeSessionCwd = asString(runtimeSessionParams.cwd, "");
|
|
391
|
+
const runtimeRemoteExecution = parseObject(runtimeSessionParams.remoteExecution);
|
|
392
|
+
const canResumeSession = runtimeSessionId.length > 0 &&
|
|
393
|
+
(runtimeSessionCwd.length === 0 || path.resolve(runtimeSessionCwd) === path.resolve(effectiveExecutionCwd)) &&
|
|
394
|
+
adapterExecutionTargetSessionMatches(runtimeRemoteExecution, executionTarget);
|
|
395
|
+
const codexTransientFallbackMode = readCodexTransientFallbackMode(context);
|
|
396
|
+
const forceSaferInvocation = fallbackModeUsesSaferInvocation(codexTransientFallbackMode);
|
|
397
|
+
const forceFreshSession = fallbackModeUsesFreshSession(codexTransientFallbackMode);
|
|
398
|
+
const sessionId = canResumeSession && !forceFreshSession ? runtimeSessionId : null;
|
|
399
|
+
if (executionTargetIsRemote && runtimeSessionId && !canResumeSession) {
|
|
400
|
+
await onLog("stdout", `[evermore] Codex session "${runtimeSessionId}" does not match the current remote execution identity and will not be resumed in "${effectiveExecutionCwd}". Starting a fresh remote session.\n`);
|
|
401
|
+
}
|
|
402
|
+
else if (runtimeSessionId && !canResumeSession) {
|
|
403
|
+
await onLog("stdout", `[evermore] Codex session "${runtimeSessionId}" was saved for cwd "${runtimeSessionCwd}" and will not be resumed in "${effectiveExecutionCwd}".\n`);
|
|
404
|
+
}
|
|
405
|
+
const instructionsFilePath = asString(config.instructionsFilePath, "").trim();
|
|
406
|
+
const instructionsDir = instructionsFilePath ? `${path.dirname(instructionsFilePath)}/` : "";
|
|
407
|
+
let instructionsPrefix = "";
|
|
408
|
+
let instructionsChars = 0;
|
|
409
|
+
if (instructionsFilePath) {
|
|
410
|
+
try {
|
|
411
|
+
const instructionsContents = await fs.readFile(instructionsFilePath, "utf8");
|
|
412
|
+
instructionsPrefix =
|
|
413
|
+
`${instructionsContents}\n\n` +
|
|
414
|
+
`The above agent instructions were loaded from ${instructionsFilePath}. ` +
|
|
415
|
+
`Resolve any relative file references from ${instructionsDir}.\n\n`;
|
|
416
|
+
instructionsChars = instructionsPrefix.length;
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
420
|
+
await onLog("stdout", `[evermore] Warning: could not read agent instructions file "${instructionsFilePath}": ${reason}\n`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const repoAgentsNote = "Codex exec automatically applies repo-scoped AGENTS.md instructions from the current workspace; Evermore does not currently suppress that discovery.";
|
|
424
|
+
const bootstrapPromptTemplate = asString(config.bootstrapPromptTemplate, "");
|
|
425
|
+
const templateData = {
|
|
426
|
+
agentId: agent.id,
|
|
427
|
+
companyId: agent.companyId,
|
|
428
|
+
runId,
|
|
429
|
+
company: { id: agent.companyId },
|
|
430
|
+
agent,
|
|
431
|
+
run: { id: runId, source: "on_demand" },
|
|
432
|
+
context,
|
|
433
|
+
};
|
|
434
|
+
const renderedBootstrapPrompt = !sessionId && bootstrapPromptTemplate.trim().length > 0
|
|
435
|
+
? renderTemplate(bootstrapPromptTemplate, templateData).trim()
|
|
436
|
+
: "";
|
|
437
|
+
const wakePrompt = renderEvermoreWakePrompt(context.evermoreWake, { resumedSession: Boolean(sessionId) });
|
|
438
|
+
const shouldUseResumeDeltaPrompt = Boolean(sessionId) && wakePrompt.length > 0;
|
|
439
|
+
const promptInstructionsPrefix = shouldUseResumeDeltaPrompt ? "" : instructionsPrefix;
|
|
440
|
+
instructionsChars = promptInstructionsPrefix.length;
|
|
441
|
+
const continuationSummary = parseObject(context.evermoreContinuationSummary);
|
|
442
|
+
const continuationSummaryBody = asString(continuationSummary.body, "").trim() || null;
|
|
443
|
+
const codexFallbackHandoffNote = forceFreshSession
|
|
444
|
+
? buildCodexTransientHandoffNote({
|
|
445
|
+
previousSessionId: runtimeSessionId || runtime.sessionId || null,
|
|
446
|
+
fallbackMode: codexTransientFallbackMode ?? "fresh_session",
|
|
447
|
+
continuationSummaryBody,
|
|
448
|
+
})
|
|
449
|
+
: "";
|
|
450
|
+
const commandNotes = (() => {
|
|
451
|
+
if (!instructionsFilePath) {
|
|
452
|
+
const notes = [repoAgentsNote];
|
|
453
|
+
if (forceSaferInvocation) {
|
|
454
|
+
notes.push("Codex transient fallback requested safer invocation settings for this retry.");
|
|
455
|
+
}
|
|
456
|
+
if (forceFreshSession) {
|
|
457
|
+
notes.push("Codex transient fallback forced a fresh session with a continuation handoff.");
|
|
458
|
+
}
|
|
459
|
+
return notes;
|
|
460
|
+
}
|
|
461
|
+
if (instructionsPrefix.length > 0) {
|
|
462
|
+
if (shouldUseResumeDeltaPrompt) {
|
|
463
|
+
const notes = [
|
|
464
|
+
`Loaded agent instructions from ${instructionsFilePath}`,
|
|
465
|
+
"Skipped stdin instruction reinjection because an existing Codex session is being resumed with a wake delta.",
|
|
466
|
+
repoAgentsNote,
|
|
467
|
+
];
|
|
468
|
+
if (forceSaferInvocation) {
|
|
469
|
+
notes.push("Codex transient fallback requested safer invocation settings for this retry.");
|
|
470
|
+
}
|
|
471
|
+
if (forceFreshSession) {
|
|
472
|
+
notes.push("Codex transient fallback forced a fresh session with a continuation handoff.");
|
|
473
|
+
}
|
|
474
|
+
return notes;
|
|
475
|
+
}
|
|
476
|
+
const notes = [
|
|
477
|
+
`Loaded agent instructions from ${instructionsFilePath}`,
|
|
478
|
+
`Prepended instructions + path directive to stdin prompt (relative references from ${instructionsDir}).`,
|
|
479
|
+
repoAgentsNote,
|
|
480
|
+
];
|
|
481
|
+
if (forceSaferInvocation) {
|
|
482
|
+
notes.push("Codex transient fallback requested safer invocation settings for this retry.");
|
|
483
|
+
}
|
|
484
|
+
if (forceFreshSession) {
|
|
485
|
+
notes.push("Codex transient fallback forced a fresh session with a continuation handoff.");
|
|
486
|
+
}
|
|
487
|
+
return notes;
|
|
488
|
+
}
|
|
489
|
+
const notes = [
|
|
490
|
+
`Configured instructionsFilePath ${instructionsFilePath}, but file could not be read; continuing without injected instructions.`,
|
|
491
|
+
repoAgentsNote,
|
|
492
|
+
];
|
|
493
|
+
if (forceSaferInvocation) {
|
|
494
|
+
notes.push("Codex transient fallback requested safer invocation settings for this retry.");
|
|
495
|
+
}
|
|
496
|
+
if (forceFreshSession) {
|
|
497
|
+
notes.push("Codex transient fallback forced a fresh session with a continuation handoff.");
|
|
498
|
+
}
|
|
499
|
+
return notes;
|
|
500
|
+
})();
|
|
501
|
+
const renderedPrompt = shouldUseResumeDeltaPrompt ? "" : renderTemplate(promptTemplate, templateData);
|
|
502
|
+
const sessionHandoffNote = asString(context.evermoreSessionHandoffMarkdown, "").trim();
|
|
503
|
+
const prompt = joinPromptSections([
|
|
504
|
+
promptInstructionsPrefix,
|
|
505
|
+
renderedBootstrapPrompt,
|
|
506
|
+
wakePrompt,
|
|
507
|
+
codexFallbackHandoffNote,
|
|
508
|
+
sessionHandoffNote,
|
|
509
|
+
renderedPrompt,
|
|
510
|
+
]);
|
|
511
|
+
const promptMetrics = {
|
|
512
|
+
promptChars: prompt.length,
|
|
513
|
+
instructionsChars,
|
|
514
|
+
bootstrapPromptChars: renderedBootstrapPrompt.length,
|
|
515
|
+
wakePromptChars: wakePrompt.length,
|
|
516
|
+
sessionHandoffChars: sessionHandoffNote.length,
|
|
517
|
+
heartbeatPromptChars: renderedPrompt.length,
|
|
518
|
+
};
|
|
519
|
+
const runAttempt = async (resumeSessionId) => {
|
|
520
|
+
const execArgs = buildCodexExecArgs(forceSaferInvocation ? { ...config, fastMode: false } : config, { resumeSessionId });
|
|
521
|
+
const args = execArgs.args;
|
|
522
|
+
const commandNotesWithFastMode = execArgs.fastModeIgnoredReason == null
|
|
523
|
+
? commandNotes
|
|
524
|
+
: [...commandNotes, execArgs.fastModeIgnoredReason];
|
|
525
|
+
if (onMeta) {
|
|
526
|
+
await onMeta({
|
|
527
|
+
adapterType: "codex_local",
|
|
528
|
+
command: resolvedCommand,
|
|
529
|
+
cwd: effectiveExecutionCwd,
|
|
530
|
+
commandNotes: commandNotesWithFastMode,
|
|
531
|
+
commandArgs: args.map((value, idx) => {
|
|
532
|
+
if (idx === args.length - 1 && value !== "-")
|
|
533
|
+
return `<prompt ${prompt.length} chars>`;
|
|
534
|
+
return value;
|
|
535
|
+
}),
|
|
536
|
+
env: loggedEnv,
|
|
537
|
+
prompt,
|
|
538
|
+
promptMetrics,
|
|
539
|
+
context,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
const proc = await runAdapterExecutionTargetProcess(runId, executionTarget, command, args, {
|
|
543
|
+
cwd,
|
|
544
|
+
env,
|
|
545
|
+
stdin: prompt,
|
|
546
|
+
timeoutSec,
|
|
547
|
+
graceSec,
|
|
548
|
+
onSpawn,
|
|
549
|
+
onLog: async (stream, chunk) => {
|
|
550
|
+
if (stream !== "stderr") {
|
|
551
|
+
await onLog(stream, chunk);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const cleaned = stripCodexRolloutNoise(chunk);
|
|
555
|
+
if (!cleaned.trim())
|
|
556
|
+
return;
|
|
557
|
+
await onLog(stream, cleaned);
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
const cleanedStderr = stripCodexRolloutNoise(proc.stderr);
|
|
561
|
+
return {
|
|
562
|
+
proc: {
|
|
563
|
+
...proc,
|
|
564
|
+
stderr: cleanedStderr,
|
|
565
|
+
},
|
|
566
|
+
rawStderr: proc.stderr,
|
|
567
|
+
parsed: parseCodexJsonl(proc.stdout),
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
const toResult = (attempt, clearSessionOnMissingSession = false, isRetry = false) => {
|
|
571
|
+
if (attempt.proc.timedOut) {
|
|
572
|
+
return {
|
|
573
|
+
exitCode: attempt.proc.exitCode,
|
|
574
|
+
signal: attempt.proc.signal,
|
|
575
|
+
timedOut: true,
|
|
576
|
+
errorMessage: `Timed out after ${timeoutSec}s`,
|
|
577
|
+
clearSession: clearSessionOnMissingSession,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
const canFallbackToRuntimeSession = !isRetry && !forceFreshSession;
|
|
581
|
+
const resolvedSessionId = attempt.parsed.sessionId ??
|
|
582
|
+
(canFallbackToRuntimeSession ? (runtimeSessionId ?? runtime.sessionId ?? null) : null);
|
|
583
|
+
const resolvedSessionParams = resolvedSessionId
|
|
584
|
+
? {
|
|
585
|
+
sessionId: resolvedSessionId,
|
|
586
|
+
cwd: effectiveExecutionCwd,
|
|
587
|
+
...(executionTargetIsRemote
|
|
588
|
+
? {
|
|
589
|
+
remoteExecution: adapterExecutionTargetSessionIdentity(executionTarget),
|
|
590
|
+
}
|
|
591
|
+
: {}),
|
|
592
|
+
...(workspaceId ? { workspaceId } : {}),
|
|
593
|
+
...(workspaceRepoUrl ? { repoUrl: workspaceRepoUrl } : {}),
|
|
594
|
+
...(workspaceRepoRef ? { repoRef: workspaceRepoRef } : {}),
|
|
595
|
+
}
|
|
596
|
+
: null;
|
|
597
|
+
const parsedError = typeof attempt.parsed.errorMessage === "string" ? attempt.parsed.errorMessage.trim() : "";
|
|
598
|
+
const stderrLine = firstNonEmptyLine(attempt.proc.stderr);
|
|
599
|
+
const fallbackErrorMessage = parsedError ||
|
|
600
|
+
stderrLine ||
|
|
601
|
+
`Codex exited with code ${attempt.proc.exitCode ?? -1}`;
|
|
602
|
+
const transientRetryNotBefore = (attempt.proc.exitCode ?? 0) !== 0
|
|
603
|
+
? extractCodexRetryNotBefore({
|
|
604
|
+
stdout: attempt.proc.stdout,
|
|
605
|
+
stderr: attempt.proc.stderr,
|
|
606
|
+
errorMessage: fallbackErrorMessage,
|
|
607
|
+
})
|
|
608
|
+
: null;
|
|
609
|
+
const transientUpstream = (attempt.proc.exitCode ?? 0) !== 0 &&
|
|
610
|
+
isCodexTransientUpstreamError({
|
|
611
|
+
stdout: attempt.proc.stdout,
|
|
612
|
+
stderr: attempt.proc.stderr,
|
|
613
|
+
errorMessage: fallbackErrorMessage,
|
|
614
|
+
});
|
|
615
|
+
return {
|
|
616
|
+
exitCode: attempt.proc.exitCode,
|
|
617
|
+
signal: attempt.proc.signal,
|
|
618
|
+
timedOut: false,
|
|
619
|
+
errorMessage: (attempt.proc.exitCode ?? 0) === 0
|
|
620
|
+
? null
|
|
621
|
+
: fallbackErrorMessage,
|
|
622
|
+
errorCode: transientUpstream
|
|
623
|
+
? "codex_transient_upstream"
|
|
624
|
+
: null,
|
|
625
|
+
errorFamily: transientUpstream ? "transient_upstream" : null,
|
|
626
|
+
retryNotBefore: transientRetryNotBefore ? transientRetryNotBefore.toISOString() : null,
|
|
627
|
+
usage: attempt.parsed.usage,
|
|
628
|
+
sessionId: resolvedSessionId,
|
|
629
|
+
sessionParams: resolvedSessionParams,
|
|
630
|
+
sessionDisplayId: resolvedSessionId,
|
|
631
|
+
provider: "openai",
|
|
632
|
+
biller: resolveCodexBiller(effectiveEnv, billingType),
|
|
633
|
+
model,
|
|
634
|
+
billingType,
|
|
635
|
+
costUsd: null,
|
|
636
|
+
resultJson: {
|
|
637
|
+
stdout: attempt.proc.stdout,
|
|
638
|
+
stderr: attempt.proc.stderr,
|
|
639
|
+
...(transientUpstream ? { errorFamily: "transient_upstream" } : {}),
|
|
640
|
+
...(transientRetryNotBefore ? { retryNotBefore: transientRetryNotBefore.toISOString() } : {}),
|
|
641
|
+
...(transientRetryNotBefore ? { transientRetryNotBefore: transientRetryNotBefore.toISOString() } : {}),
|
|
642
|
+
},
|
|
643
|
+
summary: attempt.parsed.summary,
|
|
644
|
+
clearSession: Boolean((clearSessionOnMissingSession || forceFreshSession) && !resolvedSessionId),
|
|
645
|
+
};
|
|
646
|
+
};
|
|
647
|
+
try {
|
|
648
|
+
const initial = await runAttempt(sessionId);
|
|
649
|
+
if (sessionId &&
|
|
650
|
+
!initial.proc.timedOut &&
|
|
651
|
+
(initial.proc.exitCode ?? 0) !== 0 &&
|
|
652
|
+
isCodexUnknownSessionError(initial.proc.stdout, initial.rawStderr)) {
|
|
653
|
+
await onLog("stdout", `[evermore] Codex resume session "${sessionId}" is unavailable; retrying with a fresh session.\n`);
|
|
654
|
+
const retry = await runAttempt(null);
|
|
655
|
+
return toResult(retry, true, true);
|
|
656
|
+
}
|
|
657
|
+
return toResult(initial, false, false);
|
|
658
|
+
}
|
|
659
|
+
finally {
|
|
660
|
+
if (evermoreBridge) {
|
|
661
|
+
await evermoreBridge.stop();
|
|
662
|
+
}
|
|
663
|
+
if (restoreRemoteWorkspace) {
|
|
664
|
+
await onLog("stdout", `[evermore] Restoring workspace changes from ${describeAdapterExecutionTarget(executionTarget)}.\n`);
|
|
665
|
+
await restoreRemoteWorkspace();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
//# sourceMappingURL=execute.js.map
|