replicas-engine 0.1.216 → 0.1.218
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/src/index.js +1136 -385
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -20,9 +20,21 @@ import { readFileSync } from "fs";
|
|
|
20
20
|
import { homedir } from "os";
|
|
21
21
|
import { join } from "path";
|
|
22
22
|
|
|
23
|
+
// ../shared/src/agent.ts
|
|
24
|
+
var CODEX_REASONING_EFFORT_BY_THINKING_LEVEL = {
|
|
25
|
+
low: "low",
|
|
26
|
+
medium: "medium",
|
|
27
|
+
high: "high",
|
|
28
|
+
max: "xhigh"
|
|
29
|
+
};
|
|
30
|
+
function codexReasoningEffortForThinkingLevel(thinkingLevel) {
|
|
31
|
+
return thinkingLevel ? CODEX_REASONING_EFFORT_BY_THINKING_LEVEL[thinkingLevel] : void 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
// ../shared/src/event.ts
|
|
24
35
|
var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
|
|
25
36
|
var COMPACTION_STATUS_EVENT_TYPE = "compaction-status";
|
|
37
|
+
var CHAT_GOAL_EVENT_TYPE = "chat-goal";
|
|
26
38
|
var CONTEXT_USAGE_EVENT_TYPE = "context-usage";
|
|
27
39
|
|
|
28
40
|
// ../shared/src/languages.ts
|
|
@@ -115,6 +127,10 @@ function detectLanguageByPath(filePath) {
|
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
// ../shared/src/context-usage.ts
|
|
130
|
+
var CODEX_CATEGORY_COLORS = {
|
|
131
|
+
input: "#66bb6a",
|
|
132
|
+
output: "#56b6c2"
|
|
133
|
+
};
|
|
118
134
|
function clampPercentage(value) {
|
|
119
135
|
if (!Number.isFinite(value)) return 0;
|
|
120
136
|
if (value < 0) return 0;
|
|
@@ -130,6 +146,54 @@ function clampTokensToWindow(rawTokens, maxTokens) {
|
|
|
130
146
|
if (rawTokens <= maxTokens) return { totalTokens: rawTokens };
|
|
131
147
|
return { totalTokens: maxTokens, totalProcessedTokens: rawTokens };
|
|
132
148
|
}
|
|
149
|
+
function compactCategories(categories) {
|
|
150
|
+
return categories.filter((category) => Boolean(category && category.tokens > 0)).map((category) => ({
|
|
151
|
+
...category,
|
|
152
|
+
percentage: clampPercentage(category.percentage)
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
function buildCodexTokenUsageContextUsagePayload(usage) {
|
|
156
|
+
const maxTokens = usage.modelContextWindow;
|
|
157
|
+
const inputTokens = usage.last.inputTokens;
|
|
158
|
+
const outputTokens = usage.last.outputTokens;
|
|
159
|
+
const lastTurnTokens = usage.last.totalTokens;
|
|
160
|
+
const { totalTokens } = clampTokensToWindow(lastTurnTokens, maxTokens);
|
|
161
|
+
const cumulativeTotalTokens = usage.total?.totalTokens ?? null;
|
|
162
|
+
const totalProcessedTokens = cumulativeTotalTokens !== null && cumulativeTotalTokens > totalTokens ? cumulativeTotalTokens : clampTokensToWindow(lastTurnTokens, maxTokens).totalProcessedTokens;
|
|
163
|
+
return {
|
|
164
|
+
provider: "codex",
|
|
165
|
+
source: "codex_token_count",
|
|
166
|
+
...usage.model !== void 0 ? { model: usage.model } : {},
|
|
167
|
+
totalTokens,
|
|
168
|
+
...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {},
|
|
169
|
+
maxTokens,
|
|
170
|
+
rawMaxTokens: maxTokens,
|
|
171
|
+
percentage: percentage(totalTokens, maxTokens),
|
|
172
|
+
compactsAutomatically: true,
|
|
173
|
+
categories: compactCategories([
|
|
174
|
+
{
|
|
175
|
+
name: "Input context",
|
|
176
|
+
tokens: inputTokens,
|
|
177
|
+
percentage: percentage(inputTokens, maxTokens),
|
|
178
|
+
color: CODEX_CATEGORY_COLORS.input
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "Generated output",
|
|
182
|
+
tokens: outputTokens,
|
|
183
|
+
percentage: percentage(outputTokens, maxTokens),
|
|
184
|
+
color: CODEX_CATEGORY_COLORS.output
|
|
185
|
+
}
|
|
186
|
+
]),
|
|
187
|
+
apiUsage: {
|
|
188
|
+
inputTokens,
|
|
189
|
+
outputTokens,
|
|
190
|
+
...usage.last.cachedInputTokens !== null && usage.last.cachedInputTokens !== void 0 ? { cachedInputTokens: usage.last.cachedInputTokens } : {},
|
|
191
|
+
...usage.last.reasoningOutputTokens !== null && usage.last.reasoningOutputTokens !== void 0 ? { reasoningOutputTokens: usage.last.reasoningOutputTokens } : {},
|
|
192
|
+
...cumulativeTotalTokens !== null ? { totalTokens: cumulativeTotalTokens } : {}
|
|
193
|
+
},
|
|
194
|
+
updatedAt: usage.updatedAt
|
|
195
|
+
};
|
|
196
|
+
}
|
|
133
197
|
|
|
134
198
|
// ../shared/src/pricing.ts
|
|
135
199
|
var PLANS = {
|
|
@@ -1565,6 +1629,15 @@ var DEFAULT_DEFAULT_SKILLS = {
|
|
|
1565
1629
|
}
|
|
1566
1630
|
};
|
|
1567
1631
|
|
|
1632
|
+
// ../shared/src/prompts.ts
|
|
1633
|
+
function parseGoalCommand(message) {
|
|
1634
|
+
const match = message.trim().match(/^\/goal(?:\s+([\s\S]*))?$/i);
|
|
1635
|
+
const value = match?.[1]?.trim();
|
|
1636
|
+
if (!value) return null;
|
|
1637
|
+
if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
|
|
1638
|
+
return { type: "set", objective: value };
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1568
1641
|
// ../shared/src/replicas-config.ts
|
|
1569
1642
|
import { parse as parseYaml } from "yaml";
|
|
1570
1643
|
|
|
@@ -1686,7 +1759,7 @@ function parseReplicasConfigString(content, filename) {
|
|
|
1686
1759
|
}
|
|
1687
1760
|
|
|
1688
1761
|
// ../shared/src/engine/environment.ts
|
|
1689
|
-
var DAYTONA_SNAPSHOT_ID = "26-05-2026-royal-york-
|
|
1762
|
+
var DAYTONA_SNAPSHOT_ID = "26-05-2026-royal-york-v5";
|
|
1690
1763
|
|
|
1691
1764
|
// ../shared/src/engine/types.ts
|
|
1692
1765
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -3548,19 +3621,19 @@ var previewService = new PreviewService();
|
|
|
3548
3621
|
|
|
3549
3622
|
// src/services/chat/chat-service.ts
|
|
3550
3623
|
import { existsSync as existsSync7 } from "fs";
|
|
3551
|
-
import { appendFile as appendFile5, mkdir as
|
|
3552
|
-
import { homedir as
|
|
3553
|
-
import { join as
|
|
3624
|
+
import { appendFile as appendFile5, mkdir as mkdir11, readFile as readFile8, rm, writeFile as writeFile9 } from "fs/promises";
|
|
3625
|
+
import { homedir as homedir13 } from "os";
|
|
3626
|
+
import { join as join15 } from "path";
|
|
3554
3627
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
3555
3628
|
|
|
3556
3629
|
// src/managers/claude-manager.ts
|
|
3557
3630
|
import {
|
|
3558
3631
|
query
|
|
3559
3632
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
3560
|
-
import { randomUUID as
|
|
3561
|
-
import { join as
|
|
3562
|
-
import { mkdir as
|
|
3563
|
-
import { homedir as
|
|
3633
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
3634
|
+
import { join as join13 } from "path";
|
|
3635
|
+
import { mkdir as mkdir9, appendFile as appendFile4 } from "fs/promises";
|
|
3636
|
+
import { homedir as homedir11 } from "os";
|
|
3564
3637
|
|
|
3565
3638
|
// src/utils/jsonl-reader.ts
|
|
3566
3639
|
import { readFile as readFile6 } from "fs/promises";
|
|
@@ -3593,33 +3666,6 @@ async function readJSONL(filePath) {
|
|
|
3593
3666
|
}
|
|
3594
3667
|
}
|
|
3595
3668
|
|
|
3596
|
-
// src/services/monolith-service.ts
|
|
3597
|
-
var MonolithService = class {
|
|
3598
|
-
async sendEvent(event) {
|
|
3599
|
-
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
3600
|
-
return;
|
|
3601
|
-
}
|
|
3602
|
-
try {
|
|
3603
|
-
const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
|
|
3604
|
-
method: "POST",
|
|
3605
|
-
headers: {
|
|
3606
|
-
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
3607
|
-
"X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
|
|
3608
|
-
"Content-Type": "application/json"
|
|
3609
|
-
},
|
|
3610
|
-
body: JSON.stringify(event)
|
|
3611
|
-
});
|
|
3612
|
-
if (!response.ok) {
|
|
3613
|
-
const errorText = await response.text();
|
|
3614
|
-
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
3615
|
-
}
|
|
3616
|
-
} catch (error) {
|
|
3617
|
-
console.error("[MonolithService] Failed to send event:", error);
|
|
3618
|
-
}
|
|
3619
|
-
}
|
|
3620
|
-
};
|
|
3621
|
-
var monolithService = new MonolithService();
|
|
3622
|
-
|
|
3623
3669
|
// src/utils/linear-converter.ts
|
|
3624
3670
|
function linearThoughtToResponse(thought) {
|
|
3625
3671
|
return {
|
|
@@ -3999,8 +4045,138 @@ function extractPlanFromCodexEvent(event) {
|
|
|
3999
4045
|
status: entry.completed ? "completed" : hasIncomplete ? "inProgress" : "pending"
|
|
4000
4046
|
}));
|
|
4001
4047
|
}
|
|
4048
|
+
function codexAspReasoningText(item) {
|
|
4049
|
+
return [...item.summary, ...item.content].filter(Boolean).join("\n").trim();
|
|
4050
|
+
}
|
|
4051
|
+
function codexAspChangePaths(item) {
|
|
4052
|
+
return item.changes.map((change) => change.path).filter(Boolean).join(", ");
|
|
4053
|
+
}
|
|
4054
|
+
function codexAspStatusResult(status) {
|
|
4055
|
+
return status === "completed" ? "Done" : status || "Done";
|
|
4056
|
+
}
|
|
4057
|
+
function convertCodexAspItem(item, lifecycle, linearSessionId) {
|
|
4058
|
+
if (item.type === "agentMessage") {
|
|
4059
|
+
if (!item.text) return null;
|
|
4060
|
+
return {
|
|
4061
|
+
linearSessionId,
|
|
4062
|
+
content: {
|
|
4063
|
+
type: "thought",
|
|
4064
|
+
body: item.text
|
|
4065
|
+
}
|
|
4066
|
+
};
|
|
4067
|
+
}
|
|
4068
|
+
if (item.type === "reasoning") {
|
|
4069
|
+
const text = codexAspReasoningText(item);
|
|
4070
|
+
if (!text) return null;
|
|
4071
|
+
return {
|
|
4072
|
+
linearSessionId,
|
|
4073
|
+
content: {
|
|
4074
|
+
type: "thought",
|
|
4075
|
+
body: text
|
|
4076
|
+
}
|
|
4077
|
+
};
|
|
4078
|
+
}
|
|
4079
|
+
if (item.type === "commandExecution") {
|
|
4080
|
+
return {
|
|
4081
|
+
linearSessionId,
|
|
4082
|
+
content: {
|
|
4083
|
+
type: "action",
|
|
4084
|
+
action: "Running command",
|
|
4085
|
+
parameter: item.command,
|
|
4086
|
+
...lifecycle === "completed" ? { result: item.exitCode !== null ? `Exit code: ${item.exitCode}` : item.aggregatedOutput || "Done" } : {}
|
|
4087
|
+
}
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
if (item.type === "fileChange") {
|
|
4091
|
+
return {
|
|
4092
|
+
linearSessionId,
|
|
4093
|
+
content: {
|
|
4094
|
+
type: "action",
|
|
4095
|
+
action: "File change",
|
|
4096
|
+
parameter: codexAspChangePaths(item),
|
|
4097
|
+
...lifecycle === "completed" ? { result: codexAspStatusResult(item.status) } : {}
|
|
4098
|
+
}
|
|
4099
|
+
};
|
|
4100
|
+
}
|
|
4101
|
+
if (item.type === "mcpToolCall") {
|
|
4102
|
+
return {
|
|
4103
|
+
linearSessionId,
|
|
4104
|
+
content: {
|
|
4105
|
+
type: "action",
|
|
4106
|
+
action: item.tool || "MCP tool call",
|
|
4107
|
+
parameter: item.server,
|
|
4108
|
+
...lifecycle === "completed" ? { result: item.error?.message ?? codexAspStatusResult(item.status) } : {}
|
|
4109
|
+
}
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
if (item.type === "dynamicToolCall") {
|
|
4113
|
+
return {
|
|
4114
|
+
linearSessionId,
|
|
4115
|
+
content: {
|
|
4116
|
+
type: "action",
|
|
4117
|
+
action: item.tool,
|
|
4118
|
+
parameter: item.namespace ?? "",
|
|
4119
|
+
...lifecycle === "completed" ? { result: item.success === false ? "Failed" : codexAspStatusResult(item.status) } : {}
|
|
4120
|
+
}
|
|
4121
|
+
};
|
|
4122
|
+
}
|
|
4123
|
+
if (item.type === "webSearch") {
|
|
4124
|
+
return {
|
|
4125
|
+
linearSessionId,
|
|
4126
|
+
content: {
|
|
4127
|
+
type: "action",
|
|
4128
|
+
action: "Web search",
|
|
4129
|
+
parameter: item.query,
|
|
4130
|
+
...lifecycle === "completed" ? { result: "Done" } : {}
|
|
4131
|
+
}
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
if (item.type === "plan") {
|
|
4135
|
+
return {
|
|
4136
|
+
linearSessionId,
|
|
4137
|
+
content: {
|
|
4138
|
+
type: "action",
|
|
4139
|
+
action: "Updating plan",
|
|
4140
|
+
parameter: item.text,
|
|
4141
|
+
...lifecycle === "completed" ? { result: "Done" } : {}
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
}
|
|
4145
|
+
return null;
|
|
4146
|
+
}
|
|
4147
|
+
function convertCodexAspNotification(notification, linearSessionId) {
|
|
4148
|
+
if (notification.method === "turn/started") {
|
|
4149
|
+
return {
|
|
4150
|
+
linearSessionId,
|
|
4151
|
+
content: {
|
|
4152
|
+
type: "thought",
|
|
4153
|
+
body: "Processing..."
|
|
4154
|
+
}
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
4157
|
+
if (notification.method === "item/started") {
|
|
4158
|
+
return convertCodexAspItem(notification.params.item, "started", linearSessionId);
|
|
4159
|
+
}
|
|
4160
|
+
if (notification.method === "item/completed") {
|
|
4161
|
+
return convertCodexAspItem(notification.params.item, "completed", linearSessionId);
|
|
4162
|
+
}
|
|
4163
|
+
return null;
|
|
4164
|
+
}
|
|
4165
|
+
function extractPlanFromCodexAspNotification(notification) {
|
|
4166
|
+
if (notification.method !== "turn/plan/updated" || notification.params.plan.length === 0) {
|
|
4167
|
+
return null;
|
|
4168
|
+
}
|
|
4169
|
+
return notification.params.plan.filter((item) => item.step.trim().length > 0).map((item) => ({
|
|
4170
|
+
content: item.step.trim(),
|
|
4171
|
+
status: item.status
|
|
4172
|
+
}));
|
|
4173
|
+
}
|
|
4002
4174
|
|
|
4003
4175
|
// src/utils/image-utils.ts
|
|
4176
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4177
|
+
import { mkdir as mkdir8, unlink as unlink2, writeFile as writeFile7 } from "fs/promises";
|
|
4178
|
+
import { homedir as homedir10 } from "os";
|
|
4179
|
+
import { join as join12 } from "path";
|
|
4004
4180
|
function isImageMediaType(value) {
|
|
4005
4181
|
return IMAGE_MEDIA_TYPES.includes(value);
|
|
4006
4182
|
}
|
|
@@ -4078,6 +4254,26 @@ async function normalizeImages(images) {
|
|
|
4078
4254
|
}
|
|
4079
4255
|
return normalized;
|
|
4080
4256
|
}
|
|
4257
|
+
async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(homedir10(), ".replicas", "codex", "temp-images")) {
|
|
4258
|
+
await mkdir8(tempImageDir, { recursive: true });
|
|
4259
|
+
const tempPaths = [];
|
|
4260
|
+
try {
|
|
4261
|
+
for (const image of images) {
|
|
4262
|
+
const ext = image.source.media_type.split("/")[1] || "png";
|
|
4263
|
+
const filename = `img_${randomUUID3()}.${ext}`;
|
|
4264
|
+
const filepath = join12(tempImageDir, filename);
|
|
4265
|
+
await writeFile7(filepath, Buffer.from(image.source.data, "base64"));
|
|
4266
|
+
tempPaths.push(filepath);
|
|
4267
|
+
}
|
|
4268
|
+
} catch (error) {
|
|
4269
|
+
await removeTempImageFiles(tempPaths);
|
|
4270
|
+
throw error;
|
|
4271
|
+
}
|
|
4272
|
+
return tempPaths;
|
|
4273
|
+
}
|
|
4274
|
+
async function removeTempImageFiles(paths) {
|
|
4275
|
+
await Promise.allSettled(paths.map((path4) => unlink2(path4)));
|
|
4276
|
+
}
|
|
4081
4277
|
|
|
4082
4278
|
// src/services/message-queue-service.ts
|
|
4083
4279
|
var MessageQueueService = class {
|
|
@@ -4216,6 +4412,33 @@ var MessageQueueService = class {
|
|
|
4216
4412
|
}
|
|
4217
4413
|
};
|
|
4218
4414
|
|
|
4415
|
+
// src/services/monolith-service.ts
|
|
4416
|
+
var MonolithService = class {
|
|
4417
|
+
async sendEvent(event) {
|
|
4418
|
+
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
4419
|
+
return;
|
|
4420
|
+
}
|
|
4421
|
+
try {
|
|
4422
|
+
const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
|
|
4423
|
+
method: "POST",
|
|
4424
|
+
headers: {
|
|
4425
|
+
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
4426
|
+
"X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
|
|
4427
|
+
"Content-Type": "application/json"
|
|
4428
|
+
},
|
|
4429
|
+
body: JSON.stringify(event)
|
|
4430
|
+
});
|
|
4431
|
+
if (!response.ok) {
|
|
4432
|
+
const errorText = await response.text();
|
|
4433
|
+
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
4434
|
+
}
|
|
4435
|
+
} catch (error) {
|
|
4436
|
+
console.error("[MonolithService] Failed to send event:", error);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
};
|
|
4440
|
+
var monolithService = new MonolithService();
|
|
4441
|
+
|
|
4219
4442
|
// src/managers/coding-agent-manager.ts
|
|
4220
4443
|
var MAX_INTERRUPT_QUEUE_ITEMS = 1e3;
|
|
4221
4444
|
var MAX_INTERRUPT_QUEUE_CHARS = 2e5;
|
|
@@ -4425,6 +4648,49 @@ async function getAgentAdditionalDirectories() {
|
|
|
4425
4648
|
}
|
|
4426
4649
|
}
|
|
4427
4650
|
|
|
4651
|
+
// src/utils/linear-event-forwarder.ts
|
|
4652
|
+
function isLinearThoughtEvent(event) {
|
|
4653
|
+
return event.content.type === "thought";
|
|
4654
|
+
}
|
|
4655
|
+
var LinearEventForwarder = class {
|
|
4656
|
+
linearSessionId;
|
|
4657
|
+
latestThoughtEvent = null;
|
|
4658
|
+
constructor(linearSessionId) {
|
|
4659
|
+
this.linearSessionId = linearSessionId;
|
|
4660
|
+
}
|
|
4661
|
+
sendPlan(plan) {
|
|
4662
|
+
if (!this.linearSessionId || !plan) return;
|
|
4663
|
+
monolithService.sendEvent({
|
|
4664
|
+
type: "agent_plan_update",
|
|
4665
|
+
payload: { linearSessionId: this.linearSessionId, plan }
|
|
4666
|
+
}).catch(() => {
|
|
4667
|
+
});
|
|
4668
|
+
}
|
|
4669
|
+
sendEvent(linearEvent) {
|
|
4670
|
+
if (!this.linearSessionId || !linearEvent) return;
|
|
4671
|
+
if (this.latestThoughtEvent) {
|
|
4672
|
+
monolithService.sendEvent({ type: "agent_update", payload: this.latestThoughtEvent }).catch(() => {
|
|
4673
|
+
});
|
|
4674
|
+
}
|
|
4675
|
+
if (isLinearThoughtEvent(linearEvent)) {
|
|
4676
|
+
this.latestThoughtEvent = linearEvent;
|
|
4677
|
+
return;
|
|
4678
|
+
}
|
|
4679
|
+
this.latestThoughtEvent = null;
|
|
4680
|
+
monolithService.sendEvent({ type: "agent_update", payload: linearEvent }).catch(() => {
|
|
4681
|
+
});
|
|
4682
|
+
}
|
|
4683
|
+
flushThoughtAsResponse() {
|
|
4684
|
+
if (!this.linearSessionId || !this.latestThoughtEvent) return;
|
|
4685
|
+
monolithService.sendEvent({
|
|
4686
|
+
type: "agent_update",
|
|
4687
|
+
payload: linearThoughtToResponse(this.latestThoughtEvent)
|
|
4688
|
+
}).catch(() => {
|
|
4689
|
+
});
|
|
4690
|
+
this.latestThoughtEvent = null;
|
|
4691
|
+
}
|
|
4692
|
+
};
|
|
4693
|
+
|
|
4428
4694
|
// src/managers/claude-manager.ts
|
|
4429
4695
|
var PromptStream = class {
|
|
4430
4696
|
queue = [];
|
|
@@ -4464,9 +4730,6 @@ var PromptStream = class {
|
|
|
4464
4730
|
};
|
|
4465
4731
|
}
|
|
4466
4732
|
};
|
|
4467
|
-
function isLinearThoughtEvent(event) {
|
|
4468
|
-
return event.content.type === "thought";
|
|
4469
|
-
}
|
|
4470
4733
|
var ALWAYS_ALLOWED_TOOLS = [
|
|
4471
4734
|
"Read",
|
|
4472
4735
|
"Glob",
|
|
@@ -4589,7 +4852,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4589
4852
|
pendingToolInputs = /* @__PURE__ */ new Map();
|
|
4590
4853
|
constructor(options) {
|
|
4591
4854
|
super(options);
|
|
4592
|
-
this.historyFile = options.historyFilePath ??
|
|
4855
|
+
this.historyFile = options.historyFilePath ?? join13(homedir11(), ".replicas", "claude", "history.jsonl");
|
|
4593
4856
|
this.systemPromptOverride = options.systemPromptOverride;
|
|
4594
4857
|
this.toolsOverride = options.tools;
|
|
4595
4858
|
this.mcpServersConfig = options.mcpServers;
|
|
@@ -4653,7 +4916,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4653
4916
|
if (!handler) {
|
|
4654
4917
|
return { behavior: "allow" };
|
|
4655
4918
|
}
|
|
4656
|
-
const requestId =
|
|
4919
|
+
const requestId = randomUUID4();
|
|
4657
4920
|
const toolUseId = options.toolUseID;
|
|
4658
4921
|
const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
|
|
4659
4922
|
const result = await new Promise((resolve3) => {
|
|
@@ -4813,32 +5076,12 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4813
5076
|
this.activePromptStream?.close();
|
|
4814
5077
|
await this.activeQuery.interrupt();
|
|
4815
5078
|
}
|
|
4816
|
-
|
|
5079
|
+
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
4817
5080
|
for await (const msg of response) {
|
|
4818
5081
|
await this.handleMessage(msg);
|
|
4819
5082
|
if (linearSessionId) {
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
monolithService.sendEvent({
|
|
4823
|
-
type: "agent_plan_update",
|
|
4824
|
-
payload: { linearSessionId, plan }
|
|
4825
|
-
}).catch(() => {
|
|
4826
|
-
});
|
|
4827
|
-
}
|
|
4828
|
-
const linearEvent = convertClaudeEvent(msg, linearSessionId);
|
|
4829
|
-
if (linearEvent) {
|
|
4830
|
-
if (latestThoughtEvent) {
|
|
4831
|
-
monolithService.sendEvent({ type: "agent_update", payload: latestThoughtEvent }).catch(() => {
|
|
4832
|
-
});
|
|
4833
|
-
}
|
|
4834
|
-
if (isLinearThoughtEvent(linearEvent)) {
|
|
4835
|
-
latestThoughtEvent = linearEvent;
|
|
4836
|
-
} else {
|
|
4837
|
-
latestThoughtEvent = null;
|
|
4838
|
-
monolithService.sendEvent({ type: "agent_update", payload: linearEvent }).catch(() => {
|
|
4839
|
-
});
|
|
4840
|
-
}
|
|
4841
|
-
}
|
|
5083
|
+
linearForwarder.sendPlan(extractPlanFromClaudeEvent(msg));
|
|
5084
|
+
linearForwarder.sendEvent(convertClaudeEvent(msg, linearSessionId));
|
|
4842
5085
|
}
|
|
4843
5086
|
if (msg.type === "result") {
|
|
4844
5087
|
await this.recordContextUsage(response);
|
|
@@ -4846,11 +5089,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4846
5089
|
break;
|
|
4847
5090
|
}
|
|
4848
5091
|
}
|
|
4849
|
-
|
|
4850
|
-
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
4851
|
-
monolithService.sendEvent({ type: "agent_update", payload: responseEvent }).catch(() => {
|
|
4852
|
-
});
|
|
4853
|
-
}
|
|
5092
|
+
linearForwarder.flushThoughtAsResponse();
|
|
4854
5093
|
} finally {
|
|
4855
5094
|
this.activeQuery = null;
|
|
4856
5095
|
this.activePromptStream?.close();
|
|
@@ -4927,8 +5166,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4927
5166
|
};
|
|
4928
5167
|
}
|
|
4929
5168
|
async initialize() {
|
|
4930
|
-
const historyDir =
|
|
4931
|
-
await
|
|
5169
|
+
const historyDir = join13(homedir11(), ".replicas", "claude");
|
|
5170
|
+
await mkdir9(historyDir, { recursive: true });
|
|
4932
5171
|
if (this.initialSessionId) {
|
|
4933
5172
|
this.sessionId = this.initialSessionId;
|
|
4934
5173
|
console.log(`[ClaudeManager] Restored session ID from persisted state: ${this.sessionId}`);
|
|
@@ -4990,27 +5229,29 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4990
5229
|
|
|
4991
5230
|
// src/managers/codex-manager.ts
|
|
4992
5231
|
import { Codex } from "@openai/codex-sdk";
|
|
4993
|
-
import {
|
|
4994
|
-
import { readdir as readdir3, stat as stat2, writeFile as writeFile7, mkdir as mkdir9, readFile as readFile7 } from "fs/promises";
|
|
5232
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile8, mkdir as mkdir10, readFile as readFile7 } from "fs/promises";
|
|
4995
5233
|
import { existsSync as existsSync6 } from "fs";
|
|
4996
|
-
import { join as
|
|
4997
|
-
import { homedir as
|
|
5234
|
+
import { join as join14 } from "path";
|
|
5235
|
+
import { homedir as homedir12 } from "os";
|
|
4998
5236
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
function
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5237
|
+
|
|
5238
|
+
// src/utils/codex-quota.ts
|
|
5239
|
+
function buildCodexRateLimitsSnapshot(fields) {
|
|
5240
|
+
if (fields.unlimited === true) return null;
|
|
5241
|
+
let state = "ok";
|
|
5242
|
+
if (fields.hasCredits === false) {
|
|
5243
|
+
state = "out_of_credits";
|
|
5244
|
+
} else if (fields.rateLimitResetType !== null) {
|
|
5245
|
+
state = "rate_limited";
|
|
5007
5246
|
}
|
|
5008
|
-
return
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5247
|
+
return {
|
|
5248
|
+
state,
|
|
5249
|
+
balance: fields.balance,
|
|
5250
|
+
rateLimitResetType: fields.rateLimitResetType,
|
|
5251
|
+
planType: fields.planType
|
|
5252
|
+
};
|
|
5012
5253
|
}
|
|
5013
|
-
function
|
|
5254
|
+
function extractCodexRateLimitsSnapshotFromJsonl(parsed) {
|
|
5014
5255
|
if (!isRecord3(parsed)) return null;
|
|
5015
5256
|
const payload = isRecord3(parsed.payload) ? parsed.payload : parsed;
|
|
5016
5257
|
const rateLimits = isRecord3(payload.rate_limits) ? payload.rate_limits : null;
|
|
@@ -5019,60 +5260,100 @@ function extractRateLimitsSnapshot(parsed) {
|
|
|
5019
5260
|
const hasCredits = credits && typeof credits.has_credits === "boolean" ? credits.has_credits : null;
|
|
5020
5261
|
const unlimited = credits && typeof credits.unlimited === "boolean" ? credits.unlimited : null;
|
|
5021
5262
|
const balance = credits && typeof credits.balance === "string" ? credits.balance : null;
|
|
5022
|
-
if (unlimited === true) return null;
|
|
5023
5263
|
const rateLimitResetType = typeof rateLimits.rate_limit_reached_type === "string" && rateLimits.rate_limit_reached_type.length > 0 ? rateLimits.rate_limit_reached_type : null;
|
|
5024
5264
|
const planType = typeof rateLimits.plan_type === "string" ? rateLimits.plan_type : null;
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5265
|
+
return buildCodexRateLimitsSnapshot({
|
|
5266
|
+
unlimited,
|
|
5267
|
+
hasCredits,
|
|
5268
|
+
balance,
|
|
5269
|
+
rateLimitResetType,
|
|
5270
|
+
planType
|
|
5271
|
+
});
|
|
5032
5272
|
}
|
|
5033
|
-
var
|
|
5034
|
-
codex;
|
|
5035
|
-
currentThreadId = null;
|
|
5036
|
-
currentThread = null;
|
|
5037
|
-
tempImageDir;
|
|
5038
|
-
activeAbortController = null;
|
|
5039
|
-
/** Most recent quota state observed from a rollout `rate_limits` payload. */
|
|
5040
|
-
latestQuotaState = "ok";
|
|
5041
|
-
/** Last state actually emitted to the UI. Drives dedup so seeded historical state still emits the first time it surfaces in a turn. */
|
|
5273
|
+
var CodexQuotaStatusTracker = class {
|
|
5042
5274
|
lastEmittedQuotaState = "ok";
|
|
5043
|
-
/** Last full snapshot, retained so we can re-emit when a user retries while blocked. */
|
|
5044
5275
|
latestQuotaSnapshot = null;
|
|
5045
|
-
/** When true, new turns short-circuit instead of running. Set by `out_of_credits`. */
|
|
5046
5276
|
quotaBlocked = false;
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
this.codex = this.createCodexClient();
|
|
5050
|
-
this.tempImageDir = join13(homedir11(), ".replicas", "codex", "temp-images");
|
|
5051
|
-
this.initializeManager(this.processMessageInternal.bind(this));
|
|
5277
|
+
get blocked() {
|
|
5278
|
+
return this.quotaBlocked;
|
|
5052
5279
|
}
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
return new Codex({
|
|
5056
|
-
env: buildCodexAgentEnv(),
|
|
5057
|
-
...codexApiKey ? { apiKey: codexApiKey } : {}
|
|
5058
|
-
});
|
|
5280
|
+
get latestSnapshot() {
|
|
5281
|
+
return this.latestQuotaSnapshot;
|
|
5059
5282
|
}
|
|
5060
|
-
|
|
5061
|
-
this.
|
|
5062
|
-
this.
|
|
5283
|
+
prime(snapshot) {
|
|
5284
|
+
this.latestQuotaSnapshot = snapshot;
|
|
5285
|
+
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
5063
5286
|
}
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5287
|
+
apply(snapshot, force = false) {
|
|
5288
|
+
const stateChanged = snapshot.state !== this.lastEmittedQuotaState;
|
|
5289
|
+
if (!stateChanged && !force) {
|
|
5290
|
+
return null;
|
|
5291
|
+
}
|
|
5292
|
+
this.lastEmittedQuotaState = snapshot.state;
|
|
5293
|
+
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
5294
|
+
this.latestQuotaSnapshot = snapshot;
|
|
5295
|
+
const payload = {
|
|
5296
|
+
state: snapshot.state,
|
|
5297
|
+
balance: snapshot.balance,
|
|
5298
|
+
rateLimitResetType: snapshot.rateLimitResetType,
|
|
5299
|
+
planType: snapshot.planType
|
|
5300
|
+
};
|
|
5301
|
+
const event = {
|
|
5302
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5303
|
+
type: CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
5304
|
+
payload
|
|
5305
|
+
};
|
|
5306
|
+
return event;
|
|
5307
|
+
}
|
|
5308
|
+
};
|
|
5309
|
+
|
|
5310
|
+
// src/utils/codex-auth.ts
|
|
5311
|
+
function isCodexAuthError(error) {
|
|
5312
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
5313
|
+
const lower = msg.toLowerCase();
|
|
5314
|
+
return lower.includes("unauthorized") || lower.includes("authentication") || lower.includes("invalid api key") || lower.includes("incorrect api key") || lower.includes("api key") && lower.includes("invalid") || lower.includes("401") || lower.includes('codexerrorinfo="unauthorized"') || lower.includes("codexerrorinfo=unauthorized");
|
|
5315
|
+
}
|
|
5316
|
+
|
|
5317
|
+
// src/managers/codex-manager.ts
|
|
5318
|
+
var DEFAULT_MODEL = "gpt-5.5";
|
|
5319
|
+
var CODEX_CONFIG_PATH = join14(homedir12(), ".codex", "config.toml");
|
|
5320
|
+
function isJsonlEvent2(value) {
|
|
5321
|
+
if (!isRecord3(value)) {
|
|
5322
|
+
return false;
|
|
5323
|
+
}
|
|
5324
|
+
return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord3(value.payload);
|
|
5325
|
+
}
|
|
5326
|
+
function sleep(ms) {
|
|
5327
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5328
|
+
}
|
|
5329
|
+
var CodexManager = class extends CodingAgentManager {
|
|
5330
|
+
codex;
|
|
5331
|
+
currentThreadId = null;
|
|
5332
|
+
currentThread = null;
|
|
5333
|
+
activeAbortController = null;
|
|
5334
|
+
quotaStatus = new CodexQuotaStatusTracker();
|
|
5335
|
+
constructor(options) {
|
|
5336
|
+
super(options);
|
|
5337
|
+
this.codex = this.createCodexClient();
|
|
5338
|
+
this.initializeManager(this.processMessageInternal.bind(this));
|
|
5339
|
+
}
|
|
5340
|
+
createCodexClient() {
|
|
5341
|
+
const codexApiKey = resolveCodexApiKey();
|
|
5342
|
+
return new Codex({
|
|
5343
|
+
env: buildCodexAgentEnv(),
|
|
5344
|
+
...codexApiKey ? { apiKey: codexApiKey } : {}
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
resetCodexClient() {
|
|
5348
|
+
this.codex = this.createCodexClient();
|
|
5349
|
+
this.currentThread = null;
|
|
5350
|
+
}
|
|
5351
|
+
async initialize() {
|
|
5352
|
+
if (this.initialSessionId) {
|
|
5353
|
+
this.currentThreadId = this.initialSessionId;
|
|
5354
|
+
console.log(`[CodexManager] Restored thread ID from persisted state: ${this.currentThreadId}`);
|
|
5068
5355
|
}
|
|
5069
5356
|
}
|
|
5070
|
-
/**
|
|
5071
|
-
* One-shot pass over the current session's rollout jsonl to find the most
|
|
5072
|
-
* recent `rate_limits` entry and emit a quota state if it has changed.
|
|
5073
|
-
* Used after `runStreamed` throws — the tail loop may not have pumped the
|
|
5074
|
-
* fatal line before the SDK exited.
|
|
5075
|
-
*/
|
|
5076
5357
|
async flushQuotaSnapshotFromCurrentSession() {
|
|
5077
5358
|
if (!this.currentThreadId) return;
|
|
5078
5359
|
try {
|
|
@@ -5084,7 +5365,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5084
5365
|
for (const line of lines) {
|
|
5085
5366
|
try {
|
|
5086
5367
|
const parsed = JSON.parse(line);
|
|
5087
|
-
const snapshot =
|
|
5368
|
+
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
5088
5369
|
if (snapshot) {
|
|
5089
5370
|
latest = snapshot;
|
|
5090
5371
|
}
|
|
@@ -5098,41 +5379,9 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5098
5379
|
console.warn("[CodexManager] Failed to flush quota snapshot from session:", error);
|
|
5099
5380
|
}
|
|
5100
5381
|
}
|
|
5101
|
-
/**
|
|
5102
|
-
* Emit a synthetic codex-quota-status AgentEvent and update local state.
|
|
5103
|
-
* Dedupes against `lastEmittedQuotaState` (what the UI has actually seen),
|
|
5104
|
-
* not `latestQuotaState`, so a state primed silently by `seedSeenLines` on
|
|
5105
|
-
* engine restart still emits the first time it surfaces in a turn. Pass
|
|
5106
|
-
* `force` to re-emit on a retry so users see the banner reappear.
|
|
5107
|
-
*/
|
|
5108
5382
|
emitQuotaStatus(snapshot, force = false) {
|
|
5109
|
-
const
|
|
5110
|
-
if (
|
|
5111
|
-
return;
|
|
5112
|
-
}
|
|
5113
|
-
this.latestQuotaState = snapshot.state;
|
|
5114
|
-
this.lastEmittedQuotaState = snapshot.state;
|
|
5115
|
-
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
5116
|
-
this.latestQuotaSnapshot = snapshot;
|
|
5117
|
-
const payload = {
|
|
5118
|
-
state: snapshot.state,
|
|
5119
|
-
balance: snapshot.balance,
|
|
5120
|
-
rateLimitResetType: snapshot.rateLimitResetType,
|
|
5121
|
-
planType: snapshot.planType
|
|
5122
|
-
};
|
|
5123
|
-
this.onEvent({
|
|
5124
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5125
|
-
type: CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
5126
|
-
payload
|
|
5127
|
-
});
|
|
5128
|
-
if (!stateChanged) return;
|
|
5129
|
-
if (snapshot.state === "out_of_credits") {
|
|
5130
|
-
console.warn("[CodexManager] Codex account out of credits \u2014 pausing turn processing.");
|
|
5131
|
-
} else if (snapshot.state === "rate_limited") {
|
|
5132
|
-
console.warn(`[CodexManager] Codex rate limit reached (${snapshot.rateLimitResetType}).`);
|
|
5133
|
-
} else {
|
|
5134
|
-
console.log("[CodexManager] Codex quota recovered.");
|
|
5135
|
-
}
|
|
5383
|
+
const event = this.quotaStatus.apply(snapshot, force);
|
|
5384
|
+
if (event) this.onEvent(event);
|
|
5136
5385
|
}
|
|
5137
5386
|
async interruptActiveTurn() {
|
|
5138
5387
|
if (this.activeAbortController) {
|
|
@@ -5145,8 +5394,8 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5145
5394
|
*/
|
|
5146
5395
|
async updateCodexConfig(developerInstructions) {
|
|
5147
5396
|
try {
|
|
5148
|
-
const codexDir =
|
|
5149
|
-
await
|
|
5397
|
+
const codexDir = join14(homedir12(), ".codex");
|
|
5398
|
+
await mkdir10(codexDir, { recursive: true });
|
|
5150
5399
|
let config = {};
|
|
5151
5400
|
if (existsSync6(CODEX_CONFIG_PATH)) {
|
|
5152
5401
|
try {
|
|
@@ -5165,34 +5414,17 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5165
5414
|
delete config.developer_instructions;
|
|
5166
5415
|
}
|
|
5167
5416
|
const tomlContent = stringifyToml(config);
|
|
5168
|
-
await
|
|
5417
|
+
await writeFile8(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
5169
5418
|
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
5170
5419
|
} catch (error) {
|
|
5171
5420
|
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
5172
5421
|
}
|
|
5173
5422
|
}
|
|
5174
|
-
/**
|
|
5175
|
-
* Helper method to save normalized images to temp files for Codex SDK
|
|
5176
|
-
* @returns Array of temp file paths
|
|
5177
|
-
*/
|
|
5178
|
-
async saveImagesToTempFiles(images) {
|
|
5179
|
-
await mkdir9(this.tempImageDir, { recursive: true });
|
|
5180
|
-
const tempPaths = [];
|
|
5181
|
-
for (const image of images) {
|
|
5182
|
-
const ext = image.source.media_type.split("/")[1] || "png";
|
|
5183
|
-
const filename = `img_${randomUUID4()}.${ext}`;
|
|
5184
|
-
const filepath = join13(this.tempImageDir, filename);
|
|
5185
|
-
const buffer = Buffer.from(image.source.data, "base64");
|
|
5186
|
-
await writeFile7(filepath, buffer);
|
|
5187
|
-
tempPaths.push(filepath);
|
|
5188
|
-
}
|
|
5189
|
-
return tempPaths;
|
|
5190
|
-
}
|
|
5191
5423
|
async processMessageInternal(request) {
|
|
5192
5424
|
try {
|
|
5193
5425
|
await this.executeCodexTurn(request);
|
|
5194
5426
|
} catch (error) {
|
|
5195
|
-
if (
|
|
5427
|
+
if (isCodexAuthError(error)) {
|
|
5196
5428
|
const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
|
|
5197
5429
|
if (refreshed) {
|
|
5198
5430
|
this.resetCodexClient();
|
|
@@ -5204,10 +5436,10 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5204
5436
|
}
|
|
5205
5437
|
}
|
|
5206
5438
|
async executeCodexTurn(request) {
|
|
5207
|
-
if (this.
|
|
5439
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
5208
5440
|
await this.flushQuotaSnapshotFromCurrentSession();
|
|
5209
|
-
if (this.
|
|
5210
|
-
this.emitQuotaStatus(this.
|
|
5441
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
5442
|
+
this.emitQuotaStatus(this.quotaStatus.latestSnapshot, true);
|
|
5211
5443
|
try {
|
|
5212
5444
|
await this.onTurnComplete();
|
|
5213
5445
|
} catch (error) {
|
|
@@ -5231,13 +5463,13 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5231
5463
|
try {
|
|
5232
5464
|
if (images && images.length > 0) {
|
|
5233
5465
|
const normalizedImages = await normalizeImages(images);
|
|
5234
|
-
tempImagePaths = await
|
|
5466
|
+
tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
|
|
5235
5467
|
}
|
|
5236
5468
|
const developerInstructions = this.buildCombinedInstructions(customInstructions);
|
|
5237
5469
|
await this.updateCodexConfig(developerInstructions);
|
|
5238
5470
|
const sandboxMode = "danger-full-access";
|
|
5239
5471
|
const webSearchMode = "live";
|
|
5240
|
-
const codexReasoningEffort = thinkingLevel
|
|
5472
|
+
const codexReasoningEffort = codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
5241
5473
|
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
5242
5474
|
const threadOptions = {
|
|
5243
5475
|
workingDirectory: this.workingDirectory,
|
|
@@ -5281,42 +5513,17 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5281
5513
|
}
|
|
5282
5514
|
try {
|
|
5283
5515
|
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
5284
|
-
|
|
5516
|
+
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
5285
5517
|
for await (const event of events) {
|
|
5286
5518
|
if (linearSessionId) {
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
monolithService.sendEvent({
|
|
5290
|
-
type: "agent_plan_update",
|
|
5291
|
-
payload: { linearSessionId, plan }
|
|
5292
|
-
}).catch(() => {
|
|
5293
|
-
});
|
|
5294
|
-
}
|
|
5295
|
-
const linearEvent = convertCodexEvent(event, linearSessionId);
|
|
5296
|
-
if (linearEvent) {
|
|
5297
|
-
if (latestThoughtEvent) {
|
|
5298
|
-
monolithService.sendEvent({ type: "agent_update", payload: latestThoughtEvent }).catch(() => {
|
|
5299
|
-
});
|
|
5300
|
-
}
|
|
5301
|
-
if (isLinearThoughtEvent2(linearEvent)) {
|
|
5302
|
-
latestThoughtEvent = linearEvent;
|
|
5303
|
-
} else {
|
|
5304
|
-
latestThoughtEvent = null;
|
|
5305
|
-
monolithService.sendEvent({ type: "agent_update", payload: linearEvent }).catch(() => {
|
|
5306
|
-
});
|
|
5307
|
-
}
|
|
5308
|
-
}
|
|
5519
|
+
linearForwarder.sendPlan(extractPlanFromCodexEvent(event));
|
|
5520
|
+
linearForwarder.sendEvent(convertCodexEvent(event, linearSessionId));
|
|
5309
5521
|
}
|
|
5310
5522
|
}
|
|
5311
|
-
|
|
5312
|
-
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
5313
|
-
monolithService.sendEvent({ type: "agent_update", payload: responseEvent }).catch(() => {
|
|
5314
|
-
});
|
|
5315
|
-
}
|
|
5523
|
+
linearForwarder.flushThoughtAsResponse();
|
|
5316
5524
|
} catch (error) {
|
|
5317
5525
|
await this.flushQuotaSnapshotFromCurrentSession();
|
|
5318
|
-
if (this.
|
|
5319
|
-
console.warn("[CodexManager] runStreamed failed while quota was blocked \u2014 surfacing as quota state.");
|
|
5526
|
+
if (this.quotaStatus.blocked) {
|
|
5320
5527
|
return;
|
|
5321
5528
|
}
|
|
5322
5529
|
throw error;
|
|
@@ -5325,6 +5532,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5325
5532
|
if (stopTail) {
|
|
5326
5533
|
await stopTail();
|
|
5327
5534
|
}
|
|
5535
|
+
await removeTempImageFiles(tempImagePaths);
|
|
5328
5536
|
try {
|
|
5329
5537
|
await this.onTurnComplete();
|
|
5330
5538
|
} catch (error) {
|
|
@@ -5333,11 +5541,6 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5333
5541
|
this.activeAbortController = null;
|
|
5334
5542
|
}
|
|
5335
5543
|
}
|
|
5336
|
-
static isAuthError(error) {
|
|
5337
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
5338
|
-
const lower = msg.toLowerCase();
|
|
5339
|
-
return lower.includes("unauthorized") || lower.includes("authentication") || lower.includes("invalid api key") || lower.includes("incorrect api key") || lower.includes("api key") && lower.includes("invalid") || lower.includes("login") || lower.includes("401");
|
|
5340
|
-
}
|
|
5341
5544
|
async getHistory() {
|
|
5342
5545
|
if (!this.currentThreadId) {
|
|
5343
5546
|
return {
|
|
@@ -5360,13 +5563,13 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5360
5563
|
}
|
|
5361
5564
|
// Helper methods for finding session files
|
|
5362
5565
|
async findSessionFile(threadId) {
|
|
5363
|
-
const sessionsDir =
|
|
5566
|
+
const sessionsDir = join14(homedir12(), ".codex", "sessions");
|
|
5364
5567
|
try {
|
|
5365
5568
|
const now = /* @__PURE__ */ new Date();
|
|
5366
5569
|
const year = now.getFullYear();
|
|
5367
5570
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
5368
5571
|
const day = String(now.getDate()).padStart(2, "0");
|
|
5369
|
-
const todayDir =
|
|
5572
|
+
const todayDir = join14(sessionsDir, String(year), month, day);
|
|
5370
5573
|
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
5371
5574
|
if (file) return file;
|
|
5372
5575
|
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
@@ -5375,7 +5578,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5375
5578
|
const searchYear = date.getFullYear();
|
|
5376
5579
|
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
5377
5580
|
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
5378
|
-
const searchDir =
|
|
5581
|
+
const searchDir = join14(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
5379
5582
|
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
5380
5583
|
if (file2) return file2;
|
|
5381
5584
|
}
|
|
@@ -5389,7 +5592,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5389
5592
|
const files = await readdir3(directory);
|
|
5390
5593
|
for (const file of files) {
|
|
5391
5594
|
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
5392
|
-
const fullPath =
|
|
5595
|
+
const fullPath = join14(directory, file);
|
|
5393
5596
|
const stats = await stat2(fullPath);
|
|
5394
5597
|
if (stats.isFile()) {
|
|
5395
5598
|
return fullPath;
|
|
@@ -5446,15 +5649,13 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5446
5649
|
seenLines.add(line);
|
|
5447
5650
|
try {
|
|
5448
5651
|
const parsed = JSON.parse(line);
|
|
5449
|
-
const snapshot =
|
|
5652
|
+
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
5450
5653
|
if (snapshot) latest = snapshot;
|
|
5451
5654
|
} catch {
|
|
5452
5655
|
}
|
|
5453
5656
|
}
|
|
5454
5657
|
if (latest) {
|
|
5455
|
-
this.
|
|
5456
|
-
this.latestQuotaSnapshot = latest;
|
|
5457
|
-
this.quotaBlocked = latest.state === "out_of_credits";
|
|
5658
|
+
this.quotaStatus.prime(latest);
|
|
5458
5659
|
}
|
|
5459
5660
|
} catch {
|
|
5460
5661
|
}
|
|
@@ -5474,7 +5675,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5474
5675
|
seenLines.add(trimmed);
|
|
5475
5676
|
try {
|
|
5476
5677
|
const parsed = JSON.parse(trimmed);
|
|
5477
|
-
const snapshot =
|
|
5678
|
+
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
5478
5679
|
if (snapshot) {
|
|
5479
5680
|
this.emitQuotaStatus(snapshot);
|
|
5480
5681
|
}
|
|
@@ -5787,6 +5988,26 @@ var AppServerProcess = class {
|
|
|
5787
5988
|
throw error;
|
|
5788
5989
|
}
|
|
5789
5990
|
}
|
|
5991
|
+
async stop() {
|
|
5992
|
+
const child = this.child;
|
|
5993
|
+
this.shuttingDown = true;
|
|
5994
|
+
this.client?.dispose(new Error("ASP process stopped"));
|
|
5995
|
+
this.client = null;
|
|
5996
|
+
this.child = null;
|
|
5997
|
+
if (!child || child.killed) {
|
|
5998
|
+
return;
|
|
5999
|
+
}
|
|
6000
|
+
await new Promise((resolve3) => {
|
|
6001
|
+
const timer = setTimeout(() => {
|
|
6002
|
+
child.kill("SIGKILL");
|
|
6003
|
+
}, 2e3);
|
|
6004
|
+
child.once("exit", () => {
|
|
6005
|
+
clearTimeout(timer);
|
|
6006
|
+
resolve3();
|
|
6007
|
+
});
|
|
6008
|
+
child.kill("SIGTERM");
|
|
6009
|
+
});
|
|
6010
|
+
}
|
|
5790
6011
|
async loginWithConfiguredApiKey(client) {
|
|
5791
6012
|
if (ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD !== "api_key" || !ENGINE_ENV.OPENAI_API_KEY) {
|
|
5792
6013
|
return;
|
|
@@ -5814,12 +6035,21 @@ var AppServerProcess = class {
|
|
|
5814
6035
|
|
|
5815
6036
|
// src/managers/codex-asp/asp-host.ts
|
|
5816
6037
|
var hostPromise = null;
|
|
6038
|
+
var activeProcess = null;
|
|
6039
|
+
var restartPromise = null;
|
|
5817
6040
|
async function getCodexAspHost() {
|
|
6041
|
+
if (restartPromise) {
|
|
6042
|
+
await restartPromise;
|
|
6043
|
+
}
|
|
5818
6044
|
hostPromise ??= (async () => {
|
|
5819
6045
|
try {
|
|
5820
6046
|
const process2 = new AppServerProcess();
|
|
5821
6047
|
const { client } = await process2.start();
|
|
6048
|
+
activeProcess = process2;
|
|
5822
6049
|
process2.on("exit", () => {
|
|
6050
|
+
if (activeProcess === process2) {
|
|
6051
|
+
activeProcess = null;
|
|
6052
|
+
}
|
|
5823
6053
|
hostPromise = null;
|
|
5824
6054
|
});
|
|
5825
6055
|
return { client };
|
|
@@ -5830,21 +6060,332 @@ async function getCodexAspHost() {
|
|
|
5830
6060
|
})();
|
|
5831
6061
|
return hostPromise;
|
|
5832
6062
|
}
|
|
6063
|
+
async function restartCodexAspHost() {
|
|
6064
|
+
if (restartPromise) {
|
|
6065
|
+
return restartPromise;
|
|
6066
|
+
}
|
|
6067
|
+
restartPromise = (async () => {
|
|
6068
|
+
const process2 = activeProcess;
|
|
6069
|
+
hostPromise = null;
|
|
6070
|
+
activeProcess = null;
|
|
6071
|
+
if (process2) {
|
|
6072
|
+
await process2.stop();
|
|
6073
|
+
}
|
|
6074
|
+
})();
|
|
6075
|
+
try {
|
|
6076
|
+
await restartPromise;
|
|
6077
|
+
} finally {
|
|
6078
|
+
restartPromise = null;
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
5833
6081
|
|
|
5834
|
-
// src/managers/codex-asp/
|
|
6082
|
+
// src/managers/codex-asp/mappers.ts
|
|
5835
6083
|
var DEFAULT_MODEL2 = "gpt-5.5";
|
|
5836
6084
|
var THREAD_START_METHOD = "thread/start";
|
|
6085
|
+
var THREAD_RESUME_METHOD = "thread/resume";
|
|
6086
|
+
var THREAD_READ_METHOD = "thread/read";
|
|
6087
|
+
var THREAD_GOAL_SET_METHOD = "thread/goal/set";
|
|
6088
|
+
var THREAD_GOAL_GET_METHOD = "thread/goal/get";
|
|
6089
|
+
var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
|
|
5837
6090
|
var TURN_START_METHOD = "turn/start";
|
|
5838
6091
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
6092
|
+
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
6093
|
+
function toReasoningEffort(thinkingLevel) {
|
|
6094
|
+
return codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
6095
|
+
}
|
|
6096
|
+
function extractRateLimitsSnapshot(rateLimits) {
|
|
6097
|
+
const credits = rateLimits.credits;
|
|
6098
|
+
return buildCodexRateLimitsSnapshot({
|
|
6099
|
+
unlimited: credits?.unlimited ?? null,
|
|
6100
|
+
hasCredits: credits?.hasCredits ?? null,
|
|
6101
|
+
balance: credits?.balance ?? null,
|
|
6102
|
+
rateLimitResetType: rateLimits.rateLimitReachedType || null,
|
|
6103
|
+
planType: rateLimits.planType
|
|
6104
|
+
});
|
|
6105
|
+
}
|
|
6106
|
+
function timestampFromSeconds(value) {
|
|
6107
|
+
return new Date((value ?? Date.now() / 1e3) * 1e3).toISOString();
|
|
6108
|
+
}
|
|
6109
|
+
function aspGoalToChatGoal(goal) {
|
|
6110
|
+
return {
|
|
6111
|
+
threadId: goal.threadId,
|
|
6112
|
+
objective: goal.objective,
|
|
6113
|
+
status: goal.status,
|
|
6114
|
+
tokenBudget: goal.tokenBudget,
|
|
6115
|
+
tokensUsed: goal.tokensUsed,
|
|
6116
|
+
timeUsedSeconds: goal.timeUsedSeconds,
|
|
6117
|
+
createdAt: timestampFromSeconds(goal.createdAt),
|
|
6118
|
+
updatedAt: timestampFromSeconds(goal.updatedAt)
|
|
6119
|
+
};
|
|
6120
|
+
}
|
|
6121
|
+
function formatTurnFailure(turn) {
|
|
6122
|
+
const turnError = turn.error;
|
|
6123
|
+
if (!turnError) {
|
|
6124
|
+
return "Codex ASP turn failed without error details";
|
|
6125
|
+
}
|
|
6126
|
+
const parts = [`Codex ASP turn failed: ${turnError.message}`];
|
|
6127
|
+
if (turnError.codexErrorInfo !== null) {
|
|
6128
|
+
parts.push(`codexErrorInfo=${JSON.stringify(turnError.codexErrorInfo)}`);
|
|
6129
|
+
}
|
|
6130
|
+
if (turnError.additionalDetails) {
|
|
6131
|
+
parts.push(`details=${turnError.additionalDetails}`);
|
|
6132
|
+
}
|
|
6133
|
+
return parts.join(" ");
|
|
6134
|
+
}
|
|
6135
|
+
function formatCommandOutput(item) {
|
|
6136
|
+
const exitCode = item.exitCode ?? (item.status === "completed" ? 0 : 1);
|
|
6137
|
+
const output = item.aggregatedOutput ?? "";
|
|
6138
|
+
return output ? `Exit code: ${exitCode}
|
|
6139
|
+
${output}` : `Exit code: ${exitCode}`;
|
|
6140
|
+
}
|
|
6141
|
+
function fileChangeToPatchInput(item) {
|
|
6142
|
+
const lines = ["*** Begin Patch"];
|
|
6143
|
+
for (const change of item.changes) {
|
|
6144
|
+
if (change.kind.type === "add") {
|
|
6145
|
+
lines.push(`*** Add File: ${change.path}`);
|
|
6146
|
+
} else if (change.kind.type === "delete") {
|
|
6147
|
+
lines.push(`*** Delete File: ${change.path}`);
|
|
6148
|
+
} else {
|
|
6149
|
+
lines.push(`*** Update File: ${change.path}`);
|
|
6150
|
+
if (change.kind.move_path) {
|
|
6151
|
+
lines.push(`*** Move to: ${change.kind.move_path}`);
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
if (change.diff.trim().length > 0) {
|
|
6155
|
+
lines.push(change.diff);
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
lines.push("*** End Patch");
|
|
6159
|
+
return lines.join("\n");
|
|
6160
|
+
}
|
|
6161
|
+
function itemToAgentEventDrafts(item, lifecycle) {
|
|
6162
|
+
if (item.type === "agentMessage") {
|
|
6163
|
+
if (lifecycle !== "completed" || !item.text) return [];
|
|
6164
|
+
return [{
|
|
6165
|
+
type: "response_item",
|
|
6166
|
+
payload: {
|
|
6167
|
+
type: "message",
|
|
6168
|
+
role: "assistant",
|
|
6169
|
+
content: [{
|
|
6170
|
+
type: "output_text",
|
|
6171
|
+
text: item.text
|
|
6172
|
+
}]
|
|
6173
|
+
}
|
|
6174
|
+
}];
|
|
6175
|
+
}
|
|
6176
|
+
if (item.type === "reasoning") {
|
|
6177
|
+
if (lifecycle !== "completed") return [];
|
|
6178
|
+
const text = [...item.summary, ...item.content].filter(Boolean).join("\n").trim();
|
|
6179
|
+
if (!text) return [];
|
|
6180
|
+
return [{
|
|
6181
|
+
type: "event_msg",
|
|
6182
|
+
payload: {
|
|
6183
|
+
type: "agent_reasoning",
|
|
6184
|
+
text
|
|
6185
|
+
}
|
|
6186
|
+
}];
|
|
6187
|
+
}
|
|
6188
|
+
if (item.type === "commandExecution") {
|
|
6189
|
+
if (lifecycle === "started") {
|
|
6190
|
+
return [{
|
|
6191
|
+
type: "response_item",
|
|
6192
|
+
payload: {
|
|
6193
|
+
type: "function_call",
|
|
6194
|
+
name: "exec_command",
|
|
6195
|
+
call_id: item.id,
|
|
6196
|
+
arguments: JSON.stringify({ cmd: item.command })
|
|
6197
|
+
}
|
|
6198
|
+
}];
|
|
6199
|
+
}
|
|
6200
|
+
return [{
|
|
6201
|
+
type: "response_item",
|
|
6202
|
+
payload: {
|
|
6203
|
+
type: "function_call_output",
|
|
6204
|
+
call_id: item.id,
|
|
6205
|
+
output: formatCommandOutput(item)
|
|
6206
|
+
}
|
|
6207
|
+
}];
|
|
6208
|
+
}
|
|
6209
|
+
if (item.type === "fileChange") {
|
|
6210
|
+
if (lifecycle === "started") {
|
|
6211
|
+
return [{
|
|
6212
|
+
type: "response_item",
|
|
6213
|
+
payload: {
|
|
6214
|
+
type: "custom_tool_call",
|
|
6215
|
+
name: "apply_patch",
|
|
6216
|
+
call_id: item.id,
|
|
6217
|
+
input: fileChangeToPatchInput(item),
|
|
6218
|
+
status: "in_progress"
|
|
6219
|
+
}
|
|
6220
|
+
}];
|
|
6221
|
+
}
|
|
6222
|
+
return [{
|
|
6223
|
+
type: "response_item",
|
|
6224
|
+
payload: {
|
|
6225
|
+
type: "custom_tool_call_output",
|
|
6226
|
+
call_id: item.id,
|
|
6227
|
+
output: JSON.stringify({
|
|
6228
|
+
output: item.status,
|
|
6229
|
+
metadata: { exit_code: item.status === "completed" ? 0 : 1 }
|
|
6230
|
+
})
|
|
6231
|
+
}
|
|
6232
|
+
}];
|
|
6233
|
+
}
|
|
6234
|
+
if (item.type === "mcpToolCall" || item.type === "dynamicToolCall" || item.type === "webSearch") {
|
|
6235
|
+
const tool2 = item.type === "webSearch" ? "web_search" : item.tool;
|
|
6236
|
+
const input = item.type === "webSearch" ? item.query : JSON.stringify(item.arguments);
|
|
6237
|
+
if (lifecycle === "started") {
|
|
6238
|
+
return [{
|
|
6239
|
+
type: "response_item",
|
|
6240
|
+
payload: {
|
|
6241
|
+
type: "custom_tool_call",
|
|
6242
|
+
name: tool2,
|
|
6243
|
+
call_id: item.id,
|
|
6244
|
+
input,
|
|
6245
|
+
status: "in_progress"
|
|
6246
|
+
}
|
|
6247
|
+
}];
|
|
6248
|
+
}
|
|
6249
|
+
const failed = item.type === "mcpToolCall" && item.status === "failed" || item.type === "dynamicToolCall" && item.success === false;
|
|
6250
|
+
return [{
|
|
6251
|
+
type: "response_item",
|
|
6252
|
+
payload: {
|
|
6253
|
+
type: "custom_tool_call_output",
|
|
6254
|
+
call_id: item.id,
|
|
6255
|
+
output: JSON.stringify({
|
|
6256
|
+
output: failed ? "Failed" : "Done",
|
|
6257
|
+
metadata: { exit_code: failed ? 1 : 0 }
|
|
6258
|
+
})
|
|
6259
|
+
}
|
|
6260
|
+
}];
|
|
6261
|
+
}
|
|
6262
|
+
return [];
|
|
6263
|
+
}
|
|
6264
|
+
function threadToHistoryEvents(thread) {
|
|
6265
|
+
const events = [];
|
|
6266
|
+
for (const turn of thread.turns) {
|
|
6267
|
+
const startedAt = timestampFromSeconds(turn.startedAt);
|
|
6268
|
+
const completedAt = timestampFromSeconds(turn.completedAt ?? turn.startedAt);
|
|
6269
|
+
for (const item of turn.items) {
|
|
6270
|
+
if (item.type === "userMessage") {
|
|
6271
|
+
const message = item.content.filter((input) => input.type === "text").map((input) => input.text).join("\n");
|
|
6272
|
+
if (message) {
|
|
6273
|
+
events.push({
|
|
6274
|
+
timestamp: startedAt,
|
|
6275
|
+
type: "event_msg",
|
|
6276
|
+
payload: { type: "user_message", message }
|
|
6277
|
+
});
|
|
6278
|
+
}
|
|
6279
|
+
continue;
|
|
6280
|
+
}
|
|
6281
|
+
for (const draft of itemToAgentEventDrafts(item, "started")) {
|
|
6282
|
+
events.push({ timestamp: startedAt, ...draft });
|
|
6283
|
+
}
|
|
6284
|
+
for (const draft of itemToAgentEventDrafts(item, "completed")) {
|
|
6285
|
+
events.push({ timestamp: completedAt, ...draft });
|
|
6286
|
+
}
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
return events;
|
|
6290
|
+
}
|
|
6291
|
+
async function buildThreadStartParams(workingDirectory, request, developerInstructions) {
|
|
6292
|
+
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
6293
|
+
return {
|
|
6294
|
+
model: request.model ?? DEFAULT_MODEL2,
|
|
6295
|
+
cwd: workingDirectory,
|
|
6296
|
+
runtimeWorkspaceRoots: additionalDirectories,
|
|
6297
|
+
sandbox: "danger-full-access",
|
|
6298
|
+
developerInstructions: developerInstructions ?? null,
|
|
6299
|
+
config: { web_search: "live" },
|
|
6300
|
+
experimentalRawEvents: false,
|
|
6301
|
+
persistExtendedHistory: false
|
|
6302
|
+
};
|
|
6303
|
+
}
|
|
6304
|
+
async function buildThreadResumeParams(workingDirectory, threadId, request, developerInstructions) {
|
|
6305
|
+
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
6306
|
+
return {
|
|
6307
|
+
threadId,
|
|
6308
|
+
model: request.model ?? DEFAULT_MODEL2,
|
|
6309
|
+
cwd: workingDirectory,
|
|
6310
|
+
runtimeWorkspaceRoots: additionalDirectories,
|
|
6311
|
+
sandbox: "danger-full-access",
|
|
6312
|
+
developerInstructions: developerInstructions ?? null,
|
|
6313
|
+
config: { web_search: "live" },
|
|
6314
|
+
excludeTurns: false,
|
|
6315
|
+
persistExtendedHistory: false
|
|
6316
|
+
};
|
|
6317
|
+
}
|
|
6318
|
+
async function buildTurnInput(request) {
|
|
6319
|
+
const input = [{
|
|
6320
|
+
type: "text",
|
|
6321
|
+
text: request.message,
|
|
6322
|
+
text_elements: []
|
|
6323
|
+
}];
|
|
6324
|
+
if (!request.images || request.images.length === 0) {
|
|
6325
|
+
return { input, tempImagePaths: [] };
|
|
6326
|
+
}
|
|
6327
|
+
const normalizedImages = await normalizeImages(request.images);
|
|
6328
|
+
const tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
|
|
6329
|
+
input.push(...tempImagePaths.map((path4) => ({
|
|
6330
|
+
type: "localImage",
|
|
6331
|
+
path: path4
|
|
6332
|
+
})));
|
|
6333
|
+
return { input, tempImagePaths };
|
|
6334
|
+
}
|
|
6335
|
+
async function buildTurnStartParams(threadId, request, developerInstructions) {
|
|
6336
|
+
const effort = toReasoningEffort(request.thinkingLevel);
|
|
6337
|
+
const model = request.model ?? DEFAULT_MODEL2;
|
|
6338
|
+
const { input, tempImagePaths } = await buildTurnInput(request);
|
|
6339
|
+
return {
|
|
6340
|
+
params: {
|
|
6341
|
+
threadId,
|
|
6342
|
+
input,
|
|
6343
|
+
model,
|
|
6344
|
+
...effort ? { effort } : {},
|
|
6345
|
+
...developerInstructions ? {
|
|
6346
|
+
collaborationMode: {
|
|
6347
|
+
mode: "default",
|
|
6348
|
+
settings: {
|
|
6349
|
+
model,
|
|
6350
|
+
reasoning_effort: effort ?? null,
|
|
6351
|
+
developer_instructions: developerInstructions
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
} : {}
|
|
6355
|
+
},
|
|
6356
|
+
tempImagePaths
|
|
6357
|
+
};
|
|
6358
|
+
}
|
|
6359
|
+
|
|
6360
|
+
// src/managers/codex-asp/notification-dispatch.ts
|
|
6361
|
+
var TURN_STARTED_METHOD = "turn/started";
|
|
5839
6362
|
var TURN_COMPLETED_METHOD = "turn/completed";
|
|
6363
|
+
var TURN_PLAN_UPDATED_METHOD = "turn/plan/updated";
|
|
6364
|
+
var THREAD_GOAL_UPDATED_METHOD = "thread/goal/updated";
|
|
6365
|
+
var THREAD_GOAL_CLEARED_METHOD = "thread/goal/cleared";
|
|
6366
|
+
var ITEM_STARTED_METHOD = "item/started";
|
|
5840
6367
|
var ITEM_COMPLETED_METHOD = "item/completed";
|
|
5841
6368
|
var AGENT_MESSAGE_DELTA_METHOD = "item/agentMessage/delta";
|
|
6369
|
+
var ACCOUNT_RATE_LIMITS_UPDATED_METHOD = "account/rateLimits/updated";
|
|
6370
|
+
var THREAD_TOKEN_USAGE_UPDATED_METHOD = "thread/tokenUsage/updated";
|
|
6371
|
+
var THREAD_COMPACTED_METHOD = "thread/compacted";
|
|
5842
6372
|
var COMMAND_APPROVAL_METHOD = "item/commandExecution/requestApproval";
|
|
5843
6373
|
var FILE_CHANGE_APPROVAL_METHOD = "item/fileChange/requestApproval";
|
|
6374
|
+
function dispatchAspNotification(notification, handlers) {
|
|
6375
|
+
const handler = handlers[notification.method];
|
|
6376
|
+
if (!handler) return;
|
|
6377
|
+
handler(notification);
|
|
6378
|
+
}
|
|
6379
|
+
|
|
6380
|
+
// src/managers/codex-asp/codex-asp-manager.ts
|
|
5844
6381
|
var CodexAspManager = class extends CodingAgentManager {
|
|
5845
6382
|
currentThreadId = null;
|
|
5846
6383
|
activeTurnId = null;
|
|
6384
|
+
threadAttached = false;
|
|
5847
6385
|
historyEvents = [];
|
|
6386
|
+
emittedItemStages = /* @__PURE__ */ new Set();
|
|
6387
|
+
quotaStatus = new CodexQuotaStatusTracker();
|
|
6388
|
+
currentGoal = null;
|
|
5848
6389
|
constructor(options) {
|
|
5849
6390
|
super(options);
|
|
5850
6391
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
@@ -5870,95 +6411,172 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
5870
6411
|
}
|
|
5871
6412
|
}
|
|
5872
6413
|
async getHistory() {
|
|
6414
|
+
if (!this.currentThreadId) {
|
|
6415
|
+
return { thread_id: null, events: [], goal: null };
|
|
6416
|
+
}
|
|
6417
|
+
try {
|
|
6418
|
+
const host = await getCodexAspHost();
|
|
6419
|
+
const [response] = await Promise.all([
|
|
6420
|
+
host.client.request(
|
|
6421
|
+
THREAD_READ_METHOD,
|
|
6422
|
+
{ threadId: this.currentThreadId, includeTurns: true }
|
|
6423
|
+
),
|
|
6424
|
+
this.refreshThreadGoal(host, this.currentThreadId)
|
|
6425
|
+
]);
|
|
6426
|
+
const events = threadToHistoryEvents(response.thread);
|
|
6427
|
+
if (events.length > 0) {
|
|
6428
|
+
const supplementalEvents = this.historyEvents.filter((event) => event.type === CODEX_QUOTA_STATUS_EVENT_TYPE || event.type === CONTEXT_USAGE_EVENT_TYPE || event.type === CHAT_GOAL_EVENT_TYPE || event.type === "event_msg" && event.payload.command === "goal");
|
|
6429
|
+
return {
|
|
6430
|
+
thread_id: this.currentThreadId,
|
|
6431
|
+
events: [...events, ...supplementalEvents].sort((a, b) => a.timestamp.localeCompare(b.timestamp)),
|
|
6432
|
+
goal: this.currentGoal
|
|
6433
|
+
};
|
|
6434
|
+
}
|
|
6435
|
+
} catch {
|
|
6436
|
+
}
|
|
5873
6437
|
return {
|
|
5874
6438
|
thread_id: this.currentThreadId,
|
|
5875
|
-
events: [...this.historyEvents]
|
|
6439
|
+
events: [...this.historyEvents],
|
|
6440
|
+
goal: this.currentGoal
|
|
5876
6441
|
};
|
|
5877
6442
|
}
|
|
6443
|
+
getGoal() {
|
|
6444
|
+
return this.currentGoal;
|
|
6445
|
+
}
|
|
5878
6446
|
async processMessageInternal(request) {
|
|
5879
|
-
let
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
6447
|
+
let userMessageRecorded = false;
|
|
6448
|
+
const recordUserMessage = (extraPayload = {}) => {
|
|
6449
|
+
if (userMessageRecorded) return;
|
|
6450
|
+
userMessageRecorded = true;
|
|
5883
6451
|
this.recordHistoryEvent("event_msg", {
|
|
5884
6452
|
type: "user_message",
|
|
5885
|
-
message: request.message
|
|
6453
|
+
message: request.message,
|
|
6454
|
+
...extraPayload
|
|
5886
6455
|
});
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
);
|
|
5892
|
-
|
|
5893
|
-
await this.onSaveSessionId(this.currentThreadId);
|
|
6456
|
+
};
|
|
6457
|
+
const goalCommand = parseGoalCommand(request.message);
|
|
6458
|
+
const dispatch = async () => {
|
|
6459
|
+
if (goalCommand?.type === "clear") {
|
|
6460
|
+
await this.executeGoalClearCommand(request, recordUserMessage);
|
|
6461
|
+
return;
|
|
5894
6462
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
}
|
|
5902
|
-
const parts = [`Codex ASP turn failed: ${turnError.message}`];
|
|
5903
|
-
if (turnError.codexErrorInfo !== null) {
|
|
5904
|
-
parts.push(`codexErrorInfo=${JSON.stringify(turnError.codexErrorInfo)}`);
|
|
5905
|
-
}
|
|
5906
|
-
if (turnError.additionalDetails) {
|
|
5907
|
-
parts.push(`details=${turnError.additionalDetails}`);
|
|
5908
|
-
}
|
|
5909
|
-
throw new Error(parts.join(" "));
|
|
6463
|
+
if (goalCommand?.type === "set") {
|
|
6464
|
+
await this.executeAspTurn(request, recordUserMessage, {
|
|
6465
|
+
runTurn: (host, threadId) => this.runGoalTurn(host, threadId, request, goalCommand.objective),
|
|
6466
|
+
userMessagePayload: { command: "goal" }
|
|
6467
|
+
});
|
|
6468
|
+
return;
|
|
5910
6469
|
}
|
|
5911
|
-
|
|
5912
|
-
|
|
6470
|
+
await this.executeAspTurn(request, recordUserMessage);
|
|
6471
|
+
};
|
|
6472
|
+
try {
|
|
6473
|
+
await dispatch();
|
|
6474
|
+
} catch (error) {
|
|
6475
|
+
if (isCodexAuthError(error)) {
|
|
6476
|
+
const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
|
|
6477
|
+
if (refreshed) {
|
|
6478
|
+
await restartCodexAspHost();
|
|
6479
|
+
this.threadAttached = false;
|
|
6480
|
+
await dispatch();
|
|
6481
|
+
return;
|
|
6482
|
+
}
|
|
5913
6483
|
}
|
|
6484
|
+
throw error;
|
|
5914
6485
|
} finally {
|
|
5915
6486
|
this.activeTurnId = null;
|
|
5916
|
-
if (host?.client.isDisposed) {
|
|
5917
|
-
this.currentThreadId = null;
|
|
5918
|
-
void this.onSaveSessionId(null).catch(() => {
|
|
5919
|
-
});
|
|
5920
|
-
}
|
|
5921
6487
|
await this.onTurnComplete();
|
|
5922
6488
|
}
|
|
5923
6489
|
}
|
|
5924
|
-
async
|
|
5925
|
-
const
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
6490
|
+
async executeGoalClearCommand(request, recordUserMessage) {
|
|
6491
|
+
const host = await getCodexAspHost();
|
|
6492
|
+
const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
|
|
6493
|
+
const threadId = await this.ensureThread(host, request, developerInstructions);
|
|
6494
|
+
recordUserMessage({ command: "goal" });
|
|
6495
|
+
await host.client.request(
|
|
6496
|
+
THREAD_GOAL_CLEAR_METHOD,
|
|
6497
|
+
{ threadId }
|
|
6498
|
+
);
|
|
6499
|
+
this.recordGoalChange(null, true);
|
|
6500
|
+
}
|
|
6501
|
+
async executeAspTurn(request, recordUserMessage, options = {}) {
|
|
6502
|
+
const host = await getCodexAspHost();
|
|
6503
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
6504
|
+
await this.refreshQuotaSnapshot(host);
|
|
6505
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
6506
|
+
recordUserMessage(options.userMessagePayload);
|
|
6507
|
+
this.emitQuotaStatus(this.quotaStatus.latestSnapshot, true);
|
|
6508
|
+
return;
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
|
|
6512
|
+
const threadId = await this.ensureThread(host, request, developerInstructions);
|
|
6513
|
+
recordUserMessage(options.userMessagePayload);
|
|
6514
|
+
const runTurn = options.runTurn ?? ((aspHost, aspThreadId, aspInstructions) => this.runTurn(aspHost, aspThreadId, request, aspInstructions));
|
|
6515
|
+
let completedTurn;
|
|
6516
|
+
try {
|
|
6517
|
+
completedTurn = await runTurn(host, threadId, developerInstructions);
|
|
6518
|
+
} catch (error) {
|
|
6519
|
+
await this.refreshQuotaSnapshot(host);
|
|
6520
|
+
if (this.quotaStatus.blocked) {
|
|
6521
|
+
return;
|
|
6522
|
+
}
|
|
6523
|
+
throw error;
|
|
6524
|
+
}
|
|
6525
|
+
if (completedTurn.status === "failed") {
|
|
6526
|
+
await this.refreshQuotaSnapshot(host);
|
|
6527
|
+
if (this.quotaStatus.blocked) {
|
|
6528
|
+
return;
|
|
6529
|
+
}
|
|
6530
|
+
throw new Error(formatTurnFailure(completedTurn));
|
|
6531
|
+
}
|
|
6532
|
+
if (completedTurn.status === "completed") {
|
|
6533
|
+
this.emitTurnCompletedItems(completedTurn);
|
|
6534
|
+
}
|
|
5936
6535
|
}
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
};
|
|
6536
|
+
async ensureThread(host, request, developerInstructions) {
|
|
6537
|
+
if (this.currentThreadId) {
|
|
6538
|
+
if (!this.threadAttached) {
|
|
6539
|
+
const response = await host.client.request(
|
|
6540
|
+
THREAD_RESUME_METHOD,
|
|
6541
|
+
await buildThreadResumeParams(this.workingDirectory, this.currentThreadId, request, developerInstructions)
|
|
6542
|
+
);
|
|
6543
|
+
this.currentThreadId = response.thread.id;
|
|
6544
|
+
this.threadAttached = true;
|
|
6545
|
+
this.seedHistoryFromThread(response.thread);
|
|
6546
|
+
await this.onSaveSessionId(this.currentThreadId);
|
|
6547
|
+
}
|
|
6548
|
+
return this.currentThreadId;
|
|
6549
|
+
}
|
|
6550
|
+
const threadStartResponse = await host.client.request(
|
|
6551
|
+
THREAD_START_METHOD,
|
|
6552
|
+
await buildThreadStartParams(this.workingDirectory, request, developerInstructions)
|
|
6553
|
+
);
|
|
6554
|
+
this.currentThreadId = threadStartResponse.thread.id;
|
|
6555
|
+
this.threadAttached = true;
|
|
6556
|
+
await this.onSaveSessionId(this.currentThreadId);
|
|
6557
|
+
return this.currentThreadId;
|
|
5960
6558
|
}
|
|
5961
6559
|
async runTurn(host, threadId, request, developerInstructions) {
|
|
6560
|
+
const { params, tempImagePaths } = await buildTurnStartParams(threadId, request, developerInstructions);
|
|
6561
|
+
return this.observeTurn(host, threadId, request, async () => {
|
|
6562
|
+
const turnStartResponse = await host.client.request(
|
|
6563
|
+
TURN_START_METHOD,
|
|
6564
|
+
params
|
|
6565
|
+
);
|
|
6566
|
+
return { turn: turnStartResponse.turn, tempImagePaths };
|
|
6567
|
+
});
|
|
6568
|
+
}
|
|
6569
|
+
async runGoalTurn(host, threadId, request, objective) {
|
|
6570
|
+
return this.observeTurn(host, threadId, request, async () => {
|
|
6571
|
+
const response = await host.client.request(
|
|
6572
|
+
THREAD_GOAL_SET_METHOD,
|
|
6573
|
+
{ threadId, objective, status: "active" }
|
|
6574
|
+
);
|
|
6575
|
+
this.recordGoalChange(response.goal, true);
|
|
6576
|
+
return { turn: null, tempImagePaths: [] };
|
|
6577
|
+
});
|
|
6578
|
+
}
|
|
6579
|
+
async observeTurn(host, threadId, request, startTurn) {
|
|
5962
6580
|
let resolveCompleted;
|
|
5963
6581
|
const completed = new Promise((resolve3) => {
|
|
5964
6582
|
resolveCompleted = resolve3;
|
|
@@ -5973,24 +6591,66 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
5973
6591
|
let observedTurnId = null;
|
|
5974
6592
|
const completedItems = [];
|
|
5975
6593
|
const agentMessageDeltas = /* @__PURE__ */ new Map();
|
|
5976
|
-
const
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
6594
|
+
const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
|
|
6595
|
+
const model = request.model ?? DEFAULT_MODEL2;
|
|
6596
|
+
let tempImagePaths = [];
|
|
6597
|
+
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
6598
|
+
const matchesTurn = (notificationThreadId, notificationTurnId) => notificationThreadId === threadId && (!observedTurnId || notificationTurnId === null || notificationTurnId === observedTurnId);
|
|
6599
|
+
const handlers = {
|
|
6600
|
+
[ACCOUNT_RATE_LIMITS_UPDATED_METHOD]: (notification) => {
|
|
6601
|
+
this.handleRateLimits(notification.params.rateLimits);
|
|
6602
|
+
},
|
|
6603
|
+
[THREAD_TOKEN_USAGE_UPDATED_METHOD]: (notification) => {
|
|
6604
|
+
if (notification.params.threadId !== threadId) return;
|
|
6605
|
+
this.emitCodexTokenUsage(notification.params.tokenUsage, model);
|
|
6606
|
+
},
|
|
6607
|
+
[THREAD_GOAL_UPDATED_METHOD]: (notification) => {
|
|
6608
|
+
if (notification.params.threadId !== threadId) return;
|
|
6609
|
+
this.recordGoalChange(notification.params.goal);
|
|
6610
|
+
},
|
|
6611
|
+
[THREAD_GOAL_CLEARED_METHOD]: (notification) => {
|
|
6612
|
+
if (notification.params.threadId !== threadId) return;
|
|
6613
|
+
this.recordGoalChange(null);
|
|
6614
|
+
},
|
|
6615
|
+
[THREAD_COMPACTED_METHOD]: (notification) => {
|
|
6616
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6617
|
+
this.setCompacting(false);
|
|
6618
|
+
},
|
|
6619
|
+
[TURN_STARTED_METHOD]: (notification) => {
|
|
6620
|
+
if (notification.params.threadId !== threadId) return;
|
|
6621
|
+
observedTurnId = notification.params.turn.id;
|
|
6622
|
+
this.activeTurnId = notification.params.turn.id;
|
|
6623
|
+
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
6624
|
+
},
|
|
6625
|
+
[TURN_PLAN_UPDATED_METHOD]: (notification) => {
|
|
6626
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6627
|
+
this.emitPlanUpdate(notification.params.plan);
|
|
6628
|
+
linearForwarder.sendPlan(extractPlanFromCodexAspNotification(notification));
|
|
6629
|
+
},
|
|
6630
|
+
[ITEM_STARTED_METHOD]: (notification) => {
|
|
6631
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6632
|
+
if (notification.params.item.type === "contextCompaction") {
|
|
6633
|
+
this.setCompacting(true);
|
|
5987
6634
|
}
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
6635
|
+
this.emitThreadItemLifecycle(notification.params.item, "started");
|
|
6636
|
+
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
6637
|
+
},
|
|
6638
|
+
[ITEM_COMPLETED_METHOD]: (notification) => {
|
|
6639
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6640
|
+
completedItems.push(notification.params.item);
|
|
6641
|
+
if (notification.params.item.type === "contextCompaction") {
|
|
6642
|
+
this.setCompacting(false);
|
|
5993
6643
|
}
|
|
6644
|
+
this.emitThreadItemLifecycle(notification.params.item, "completed");
|
|
6645
|
+
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
6646
|
+
},
|
|
6647
|
+
[AGENT_MESSAGE_DELTA_METHOD]: (notification) => {
|
|
6648
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6649
|
+
const currentText = agentMessageDeltas.get(notification.params.itemId) ?? "";
|
|
6650
|
+
agentMessageDeltas.set(notification.params.itemId, currentText + notification.params.delta);
|
|
6651
|
+
},
|
|
6652
|
+
[TURN_COMPLETED_METHOD]: (notification) => {
|
|
6653
|
+
if (notification.params.threadId !== threadId) return;
|
|
5994
6654
|
observedTurnId = notification.params.turn.id;
|
|
5995
6655
|
const turn = notification.params.turn;
|
|
5996
6656
|
const items = turn.items.length > 0 ? [...turn.items] : [];
|
|
@@ -6015,6 +6675,9 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6015
6675
|
resolveCompleted(items.length > 0 ? { ...turn, items, itemsView: "full" } : turn);
|
|
6016
6676
|
}
|
|
6017
6677
|
};
|
|
6678
|
+
const onNotification = (notification) => {
|
|
6679
|
+
dispatchAspNotification(notification, handlers);
|
|
6680
|
+
};
|
|
6018
6681
|
const onServerRequest = (serverRequest) => {
|
|
6019
6682
|
if (serverRequest.method !== COMMAND_APPROVAL_METHOD && serverRequest.method !== FILE_CHANGE_APPROVAL_METHOD) {
|
|
6020
6683
|
return;
|
|
@@ -6023,10 +6686,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6023
6686
|
host.client.respond(serverRequest.id, { decision: "accept" });
|
|
6024
6687
|
};
|
|
6025
6688
|
const onDispose = (reason) => {
|
|
6026
|
-
this.
|
|
6689
|
+
this.threadAttached = false;
|
|
6027
6690
|
this.activeTurnId = null;
|
|
6028
|
-
void this.onSaveSessionId(null).catch(() => {
|
|
6029
|
-
});
|
|
6030
6691
|
const turnLabel = observedTurnId ? ` ${observedTurnId}` : "";
|
|
6031
6692
|
rejectDisposed(new Error(`Codex ASP client disposed before turn${turnLabel} completed: ${reason.message}`));
|
|
6032
6693
|
};
|
|
@@ -6034,41 +6695,88 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6034
6695
|
host.client.on("serverRequest", onServerRequest);
|
|
6035
6696
|
host.client.on("dispose", onDispose);
|
|
6036
6697
|
try {
|
|
6037
|
-
const
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6698
|
+
const started = await startTurn();
|
|
6699
|
+
tempImagePaths = started.tempImagePaths;
|
|
6700
|
+
if (started.turn) {
|
|
6701
|
+
observedTurnId = started.turn.id;
|
|
6702
|
+
this.activeTurnId = started.turn.id;
|
|
6703
|
+
}
|
|
6704
|
+
const turn = await Promise.race([completed, disposed]);
|
|
6705
|
+
linearForwarder.flushThoughtAsResponse();
|
|
6706
|
+
return turn;
|
|
6044
6707
|
} finally {
|
|
6045
6708
|
host.client.off("notification", onNotification);
|
|
6046
6709
|
host.client.off("serverRequest", onServerRequest);
|
|
6047
6710
|
host.client.off("dispose", onDispose);
|
|
6711
|
+
await removeTempImageFiles(tempImagePaths);
|
|
6712
|
+
if (host.client.isDisposed) {
|
|
6713
|
+
this.threadAttached = false;
|
|
6714
|
+
}
|
|
6048
6715
|
}
|
|
6049
6716
|
}
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6717
|
+
emitThreadItemLifecycle(item, lifecycle) {
|
|
6718
|
+
const stageKey = `${lifecycle}:${item.id}`;
|
|
6719
|
+
if (this.emittedItemStages.has(stageKey)) {
|
|
6720
|
+
return;
|
|
6721
|
+
}
|
|
6722
|
+
if (lifecycle === "completed" && !this.emittedItemStages.has(`started:${item.id}`)) {
|
|
6723
|
+
this.emittedItemStages.add(`started:${item.id}`);
|
|
6724
|
+
this.recordAndEmitDrafts(itemToAgentEventDrafts(item, "started"));
|
|
6725
|
+
}
|
|
6726
|
+
this.emittedItemStages.add(stageKey);
|
|
6727
|
+
this.recordAndEmitDrafts(itemToAgentEventDrafts(item, lifecycle));
|
|
6728
|
+
}
|
|
6729
|
+
emitTurnCompletedItems(turn) {
|
|
6730
|
+
for (const item of turn.items) {
|
|
6731
|
+
this.emitThreadItemLifecycle(item, "completed");
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
emitPlanUpdate(plan) {
|
|
6735
|
+
if (plan.length === 0) return;
|
|
6736
|
+
this.recordAndEmitDrafts([{
|
|
6737
|
+
type: "response_item",
|
|
6738
|
+
payload: {
|
|
6739
|
+
type: "function_call",
|
|
6740
|
+
name: "update_plan",
|
|
6741
|
+
call_id: `plan-${Date.now()}`,
|
|
6742
|
+
arguments: JSON.stringify({ plan })
|
|
6057
6743
|
}
|
|
6744
|
+
}]);
|
|
6745
|
+
}
|
|
6746
|
+
seedHistoryFromThread(thread) {
|
|
6747
|
+
const events = threadToHistoryEvents(thread);
|
|
6748
|
+
if (events.length === 0) return;
|
|
6749
|
+
this.historyEvents.splice(0, this.historyEvents.length, ...events);
|
|
6750
|
+
}
|
|
6751
|
+
async refreshThreadGoal(host, threadId) {
|
|
6752
|
+
try {
|
|
6753
|
+
const response = await host.client.request(
|
|
6754
|
+
THREAD_GOAL_GET_METHOD,
|
|
6755
|
+
{ threadId }
|
|
6756
|
+
);
|
|
6757
|
+
this.currentGoal = response.goal ? aspGoalToChatGoal(response.goal) : null;
|
|
6758
|
+
} catch (error) {
|
|
6759
|
+
console.warn("[CodexAspManager] Failed to read ASP thread goal:", error);
|
|
6058
6760
|
}
|
|
6059
|
-
|
|
6761
|
+
}
|
|
6762
|
+
recordGoalChange(goal, force = false) {
|
|
6763
|
+
const nextGoal = goal ? aspGoalToChatGoal(goal) : null;
|
|
6764
|
+
if (!force && JSON.stringify(this.currentGoal) === JSON.stringify(nextGoal)) {
|
|
6060
6765
|
return;
|
|
6061
6766
|
}
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
text
|
|
6068
|
-
}]
|
|
6069
|
-
});
|
|
6767
|
+
this.currentGoal = nextGoal;
|
|
6768
|
+
const event = this.recordHistoryEvent(
|
|
6769
|
+
CHAT_GOAL_EVENT_TYPE,
|
|
6770
|
+
{ goal: nextGoal }
|
|
6771
|
+
);
|
|
6070
6772
|
this.onEvent(event);
|
|
6071
6773
|
}
|
|
6774
|
+
recordAndEmitDrafts(drafts) {
|
|
6775
|
+
for (const draft of drafts) {
|
|
6776
|
+
const event = this.recordHistoryEvent(draft.type, draft.payload);
|
|
6777
|
+
this.onEvent(event);
|
|
6778
|
+
}
|
|
6779
|
+
}
|
|
6072
6780
|
recordHistoryEvent(type, payload) {
|
|
6073
6781
|
const event = {
|
|
6074
6782
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6078,6 +6786,47 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6078
6786
|
this.historyEvents.push(event);
|
|
6079
6787
|
return event;
|
|
6080
6788
|
}
|
|
6789
|
+
handleRateLimits(rateLimits) {
|
|
6790
|
+
const snapshot = extractRateLimitsSnapshot(rateLimits);
|
|
6791
|
+
if (snapshot) {
|
|
6792
|
+
this.emitQuotaStatus(snapshot);
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6795
|
+
async refreshQuotaSnapshot(host) {
|
|
6796
|
+
try {
|
|
6797
|
+
const response = await host.client.request(
|
|
6798
|
+
ACCOUNT_RATE_LIMITS_READ_METHOD,
|
|
6799
|
+
void 0
|
|
6800
|
+
);
|
|
6801
|
+
this.handleRateLimits(response.rateLimits);
|
|
6802
|
+
} catch {
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6805
|
+
emitQuotaStatus(snapshot, force = false) {
|
|
6806
|
+
const event = this.quotaStatus.apply(snapshot, force);
|
|
6807
|
+
if (!event) return;
|
|
6808
|
+
this.historyEvents.push(event);
|
|
6809
|
+
this.onEvent(event);
|
|
6810
|
+
}
|
|
6811
|
+
emitCodexTokenUsage(tokenUsage, model) {
|
|
6812
|
+
const payload = buildCodexTokenUsageContextUsagePayload({
|
|
6813
|
+
model,
|
|
6814
|
+
modelContextWindow: tokenUsage.modelContextWindow,
|
|
6815
|
+
last: {
|
|
6816
|
+
inputTokens: tokenUsage.last.inputTokens,
|
|
6817
|
+
outputTokens: tokenUsage.last.outputTokens,
|
|
6818
|
+
totalTokens: tokenUsage.last.totalTokens,
|
|
6819
|
+
cachedInputTokens: tokenUsage.last.cachedInputTokens,
|
|
6820
|
+
reasoningOutputTokens: tokenUsage.last.reasoningOutputTokens
|
|
6821
|
+
},
|
|
6822
|
+
total: {
|
|
6823
|
+
totalTokens: tokenUsage.total.totalTokens
|
|
6824
|
+
},
|
|
6825
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6826
|
+
});
|
|
6827
|
+
const event = this.emitContextUsage(payload);
|
|
6828
|
+
this.historyEvents.push(event);
|
|
6829
|
+
}
|
|
6081
6830
|
};
|
|
6082
6831
|
|
|
6083
6832
|
// src/managers/relay-tools.ts
|
|
@@ -6686,12 +7435,12 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
6686
7435
|
};
|
|
6687
7436
|
|
|
6688
7437
|
// src/services/chat/chat-service.ts
|
|
6689
|
-
var ENGINE_DIR2 =
|
|
6690
|
-
var CHATS_FILE =
|
|
6691
|
-
var CLAUDE_HISTORY_DIR =
|
|
6692
|
-
var RELAY_HISTORY_DIR =
|
|
6693
|
-
var CHAT_SENDERS_DIR =
|
|
6694
|
-
var CODEX_AUTH_PATH2 =
|
|
7438
|
+
var ENGINE_DIR2 = join15(homedir13(), ".replicas", "engine");
|
|
7439
|
+
var CHATS_FILE = join15(ENGINE_DIR2, "chats.json");
|
|
7440
|
+
var CLAUDE_HISTORY_DIR = join15(ENGINE_DIR2, "claude-histories");
|
|
7441
|
+
var RELAY_HISTORY_DIR = join15(ENGINE_DIR2, "relay-histories");
|
|
7442
|
+
var CHAT_SENDERS_DIR = join15(ENGINE_DIR2, "chat-senders");
|
|
7443
|
+
var CODEX_AUTH_PATH2 = join15(homedir13(), ".codex", "auth.json");
|
|
6695
7444
|
function isChatMessageSender(value) {
|
|
6696
7445
|
if (!isRecord3(value)) return false;
|
|
6697
7446
|
return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
@@ -6714,10 +7463,10 @@ var ChatService = class {
|
|
|
6714
7463
|
chats = /* @__PURE__ */ new Map();
|
|
6715
7464
|
writeChain = Promise.resolve();
|
|
6716
7465
|
async initialize() {
|
|
6717
|
-
await
|
|
6718
|
-
await
|
|
6719
|
-
await
|
|
6720
|
-
await
|
|
7466
|
+
await mkdir11(ENGINE_DIR2, { recursive: true });
|
|
7467
|
+
await mkdir11(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
7468
|
+
await mkdir11(RELAY_HISTORY_DIR, { recursive: true });
|
|
7469
|
+
await mkdir11(CHAT_SENDERS_DIR, { recursive: true });
|
|
6721
7470
|
const persisted = await this.loadChats();
|
|
6722
7471
|
for (const chat of persisted) {
|
|
6723
7472
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -6812,7 +7561,7 @@ var ChatService = class {
|
|
|
6812
7561
|
};
|
|
6813
7562
|
}
|
|
6814
7563
|
senderFilePath(chatId) {
|
|
6815
|
-
return
|
|
7564
|
+
return join15(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
6816
7565
|
}
|
|
6817
7566
|
async appendSender(chatId, sender) {
|
|
6818
7567
|
try {
|
|
@@ -6929,7 +7678,7 @@ var ChatService = class {
|
|
|
6929
7678
|
async deleteHistoryFile(persisted) {
|
|
6930
7679
|
if (persisted.provider === "claude" || persisted.provider === "relay") {
|
|
6931
7680
|
const dir = persisted.provider === "claude" ? CLAUDE_HISTORY_DIR : RELAY_HISTORY_DIR;
|
|
6932
|
-
await rm(
|
|
7681
|
+
await rm(join15(dir, `${persisted.id}.jsonl`), { force: true });
|
|
6933
7682
|
}
|
|
6934
7683
|
await rm(this.senderFilePath(persisted.id), { force: true });
|
|
6935
7684
|
}
|
|
@@ -6942,6 +7691,7 @@ var ChatService = class {
|
|
|
6942
7691
|
return {
|
|
6943
7692
|
thread_id: history.thread_id,
|
|
6944
7693
|
events: history.events,
|
|
7694
|
+
goal: history.goal ?? chat.provider.getGoal?.() ?? null,
|
|
6945
7695
|
senders
|
|
6946
7696
|
};
|
|
6947
7697
|
}
|
|
@@ -6977,7 +7727,7 @@ var ChatService = class {
|
|
|
6977
7727
|
if (persisted.provider === "claude") {
|
|
6978
7728
|
provider = new ClaudeManager({
|
|
6979
7729
|
workingDirectory: this.workingDirectory,
|
|
6980
|
-
historyFilePath:
|
|
7730
|
+
historyFilePath: join15(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
6981
7731
|
initialSessionId: persisted.providerSessionId,
|
|
6982
7732
|
onSaveSessionId: saveSession,
|
|
6983
7733
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -6986,7 +7736,7 @@ var ChatService = class {
|
|
|
6986
7736
|
} else if (persisted.provider === "relay") {
|
|
6987
7737
|
provider = new RelayManager({
|
|
6988
7738
|
workingDirectory: this.workingDirectory,
|
|
6989
|
-
historyFilePath:
|
|
7739
|
+
historyFilePath: join15(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
6990
7740
|
initialSessionId: persisted.providerSessionId,
|
|
6991
7741
|
onSaveSessionId: saveSession,
|
|
6992
7742
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -7027,6 +7777,7 @@ var ChatService = class {
|
|
|
7027
7777
|
processing: chat.provider.isProcessing(),
|
|
7028
7778
|
awaitingInput: chat.provider.isAwaitingInput?.() ?? false,
|
|
7029
7779
|
isCompacting: chat.provider.isCompacting?.() ?? false,
|
|
7780
|
+
goal: chat.provider.getGoal?.() ?? null,
|
|
7030
7781
|
parentChatId: chat.persisted.parentChatId
|
|
7031
7782
|
};
|
|
7032
7783
|
}
|
|
@@ -7069,7 +7820,7 @@ var ChatService = class {
|
|
|
7069
7820
|
}
|
|
7070
7821
|
}).catch(() => {
|
|
7071
7822
|
});
|
|
7072
|
-
if (event.type === "replicas-tool-input-request" || event.type === "replicas-tool-input-resolved" || event.type === "compaction-status") {
|
|
7823
|
+
if (event.type === "replicas-tool-input-request" || event.type === "replicas-tool-input-resolved" || event.type === "compaction-status" || event.type === CHAT_GOAL_EVENT_TYPE) {
|
|
7073
7824
|
this.publish({
|
|
7074
7825
|
type: "chat.updated",
|
|
7075
7826
|
payload: { chat: this.toSummary(chat) }
|
|
@@ -7122,7 +7873,7 @@ var ChatService = class {
|
|
|
7122
7873
|
this.writeChain = this.writeChain.catch(() => {
|
|
7123
7874
|
}).then(async () => {
|
|
7124
7875
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
7125
|
-
await
|
|
7876
|
+
await writeFile9(CHATS_FILE, JSON.stringify(payload, null, 2), "utf-8");
|
|
7126
7877
|
});
|
|
7127
7878
|
await this.writeChain;
|
|
7128
7879
|
}
|
|
@@ -7175,7 +7926,7 @@ var ChatService = class {
|
|
|
7175
7926
|
// src/services/repo-file-service.ts
|
|
7176
7927
|
import { execFile as execFile2 } from "child_process";
|
|
7177
7928
|
import { readFile as readFile9, realpath, stat as stat3 } from "fs/promises";
|
|
7178
|
-
import { join as
|
|
7929
|
+
import { join as join16, resolve, extname } from "path";
|
|
7179
7930
|
var CACHE_TTL_MS = 3e4;
|
|
7180
7931
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
7181
7932
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -7335,7 +8086,7 @@ var RepoFileService = class {
|
|
|
7335
8086
|
const repo = repos.find((r) => r.name === repoName);
|
|
7336
8087
|
if (!repo) return null;
|
|
7337
8088
|
try {
|
|
7338
|
-
const fullPath = await realpath(resolve(
|
|
8089
|
+
const fullPath = await realpath(resolve(join16(repo.path, filePath)));
|
|
7339
8090
|
const repoRoot = await realpath(repo.path);
|
|
7340
8091
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
7341
8092
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -7447,15 +8198,15 @@ var RepoFileService = class {
|
|
|
7447
8198
|
import { Hono } from "hono";
|
|
7448
8199
|
import { z as z2 } from "zod";
|
|
7449
8200
|
import { readdir as readdir6, stat as stat4, readFile as readFile13 } from "fs/promises";
|
|
7450
|
-
import { join as
|
|
8201
|
+
import { join as join20, resolve as resolve2 } from "path";
|
|
7451
8202
|
|
|
7452
8203
|
// src/services/plan-service.ts
|
|
7453
8204
|
import { readdir as readdir4, readFile as readFile10 } from "fs/promises";
|
|
7454
|
-
import { homedir as
|
|
7455
|
-
import { basename, join as
|
|
8205
|
+
import { homedir as homedir14 } from "os";
|
|
8206
|
+
import { basename, join as join17 } from "path";
|
|
7456
8207
|
var PLAN_DIRECTORIES = [
|
|
7457
|
-
|
|
7458
|
-
|
|
8208
|
+
join17(homedir14(), ".claude", "plans"),
|
|
8209
|
+
join17(homedir14(), ".replicas", "plans")
|
|
7459
8210
|
];
|
|
7460
8211
|
function isMarkdownFile(filename) {
|
|
7461
8212
|
return filename.toLowerCase().endsWith(".md");
|
|
@@ -7489,7 +8240,7 @@ var PlanService = class {
|
|
|
7489
8240
|
return null;
|
|
7490
8241
|
}
|
|
7491
8242
|
for (const directory of PLAN_DIRECTORIES) {
|
|
7492
|
-
const filePath =
|
|
8243
|
+
const filePath = join17(directory, safeFilename);
|
|
7493
8244
|
try {
|
|
7494
8245
|
const content = await readFile10(filePath, "utf-8");
|
|
7495
8246
|
return { filename: safeFilename, content };
|
|
@@ -7505,14 +8256,14 @@ var planService = new PlanService();
|
|
|
7505
8256
|
import { spawn as spawn2 } from "child_process";
|
|
7506
8257
|
import { readFile as readFile12 } from "fs/promises";
|
|
7507
8258
|
import { existsSync as existsSync8 } from "fs";
|
|
7508
|
-
import { join as
|
|
8259
|
+
import { join as join19 } from "path";
|
|
7509
8260
|
|
|
7510
8261
|
// src/services/warm-hook-logs-service.ts
|
|
7511
8262
|
import { createHash as createHash2 } from "crypto";
|
|
7512
|
-
import { mkdir as
|
|
7513
|
-
import { homedir as
|
|
7514
|
-
import { join as
|
|
7515
|
-
var LOGS_DIR2 =
|
|
8263
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile10, readdir as readdir5 } from "fs/promises";
|
|
8264
|
+
import { homedir as homedir15 } from "os";
|
|
8265
|
+
import { join as join18 } from "path";
|
|
8266
|
+
var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
|
|
7516
8267
|
function sanitizeFilename2(name) {
|
|
7517
8268
|
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
7518
8269
|
const hash = createHash2("sha256").update(name).digest("hex").slice(0, 8);
|
|
@@ -7533,7 +8284,7 @@ function withPreview2(stored) {
|
|
|
7533
8284
|
}
|
|
7534
8285
|
var WarmHookLogsService = class {
|
|
7535
8286
|
async ensureDir() {
|
|
7536
|
-
await
|
|
8287
|
+
await mkdir12(LOGS_DIR2, { recursive: true });
|
|
7537
8288
|
}
|
|
7538
8289
|
async saveGlobalHookLog(entry) {
|
|
7539
8290
|
await this.ensureDir();
|
|
@@ -7542,7 +8293,7 @@ var WarmHookLogsService = class {
|
|
|
7542
8293
|
hookName: "organization",
|
|
7543
8294
|
...entry
|
|
7544
8295
|
};
|
|
7545
|
-
await
|
|
8296
|
+
await writeFile10(join18(LOGS_DIR2, globalFilename()), `${JSON.stringify(log, null, 2)}
|
|
7546
8297
|
`, "utf-8");
|
|
7547
8298
|
}
|
|
7548
8299
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -7552,7 +8303,7 @@ var WarmHookLogsService = class {
|
|
|
7552
8303
|
hookName: "environment",
|
|
7553
8304
|
...entry
|
|
7554
8305
|
};
|
|
7555
|
-
await
|
|
8306
|
+
await writeFile10(join18(LOGS_DIR2, environmentFilename()), `${JSON.stringify(log, null, 2)}
|
|
7556
8307
|
`, "utf-8");
|
|
7557
8308
|
}
|
|
7558
8309
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -7562,7 +8313,7 @@ var WarmHookLogsService = class {
|
|
|
7562
8313
|
hookName: repoName,
|
|
7563
8314
|
...entry
|
|
7564
8315
|
};
|
|
7565
|
-
await
|
|
8316
|
+
await writeFile10(join18(LOGS_DIR2, repoFilename2(repoName)), `${JSON.stringify(log, null, 2)}
|
|
7566
8317
|
`, "utf-8");
|
|
7567
8318
|
}
|
|
7568
8319
|
async getAllLogs() {
|
|
@@ -7581,7 +8332,7 @@ var WarmHookLogsService = class {
|
|
|
7581
8332
|
continue;
|
|
7582
8333
|
}
|
|
7583
8334
|
try {
|
|
7584
|
-
const raw = await readFile11(
|
|
8335
|
+
const raw = await readFile11(join18(LOGS_DIR2, file), "utf-8");
|
|
7585
8336
|
const stored = JSON.parse(raw);
|
|
7586
8337
|
logs.push(withPreview2(stored));
|
|
7587
8338
|
} catch {
|
|
@@ -7599,7 +8350,7 @@ var WarmHookLogsService = class {
|
|
|
7599
8350
|
async getFullOutput(hookType, hookName) {
|
|
7600
8351
|
const filename = hookType === "global" ? globalFilename() : hookType === "environment" ? environmentFilename() : repoFilename2(hookName);
|
|
7601
8352
|
try {
|
|
7602
|
-
const raw = await readFile11(
|
|
8353
|
+
const raw = await readFile11(join18(LOGS_DIR2, filename), "utf-8");
|
|
7603
8354
|
const stored = JSON.parse(raw);
|
|
7604
8355
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
7605
8356
|
return null;
|
|
@@ -7618,7 +8369,7 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
7618
8369
|
// src/services/warm-hooks-service.ts
|
|
7619
8370
|
async function readRepoWarmHook(repoPath) {
|
|
7620
8371
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
7621
|
-
const configPath =
|
|
8372
|
+
const configPath = join19(repoPath, filename);
|
|
7622
8373
|
if (!existsSync8(configPath)) {
|
|
7623
8374
|
continue;
|
|
7624
8375
|
}
|
|
@@ -8422,7 +9173,7 @@ function createV1Routes(deps) {
|
|
|
8422
9173
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
8423
9174
|
const sessions = await Promise.all(
|
|
8424
9175
|
logFiles.map(async (filename) => {
|
|
8425
|
-
const filePath =
|
|
9176
|
+
const filePath = join20(LOG_DIR, filename);
|
|
8426
9177
|
const fileStat = await stat4(filePath);
|
|
8427
9178
|
const sessionId = filename.replace(/\.log$/, "");
|
|
8428
9179
|
return {
|