replicas-engine 0.1.215 → 0.1.217
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 +968 -331
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -20,6 +20,17 @@ 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";
|
|
@@ -115,6 +126,10 @@ function detectLanguageByPath(filePath) {
|
|
|
115
126
|
}
|
|
116
127
|
|
|
117
128
|
// ../shared/src/context-usage.ts
|
|
129
|
+
var CODEX_CATEGORY_COLORS = {
|
|
130
|
+
input: "#66bb6a",
|
|
131
|
+
output: "#56b6c2"
|
|
132
|
+
};
|
|
118
133
|
function clampPercentage(value) {
|
|
119
134
|
if (!Number.isFinite(value)) return 0;
|
|
120
135
|
if (value < 0) return 0;
|
|
@@ -130,6 +145,54 @@ function clampTokensToWindow(rawTokens, maxTokens) {
|
|
|
130
145
|
if (rawTokens <= maxTokens) return { totalTokens: rawTokens };
|
|
131
146
|
return { totalTokens: maxTokens, totalProcessedTokens: rawTokens };
|
|
132
147
|
}
|
|
148
|
+
function compactCategories(categories) {
|
|
149
|
+
return categories.filter((category) => Boolean(category && category.tokens > 0)).map((category) => ({
|
|
150
|
+
...category,
|
|
151
|
+
percentage: clampPercentage(category.percentage)
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
function buildCodexTokenUsageContextUsagePayload(usage) {
|
|
155
|
+
const maxTokens = usage.modelContextWindow;
|
|
156
|
+
const inputTokens = usage.last.inputTokens;
|
|
157
|
+
const outputTokens = usage.last.outputTokens;
|
|
158
|
+
const lastTurnTokens = usage.last.totalTokens;
|
|
159
|
+
const { totalTokens } = clampTokensToWindow(lastTurnTokens, maxTokens);
|
|
160
|
+
const cumulativeTotalTokens = usage.total?.totalTokens ?? null;
|
|
161
|
+
const totalProcessedTokens = cumulativeTotalTokens !== null && cumulativeTotalTokens > totalTokens ? cumulativeTotalTokens : clampTokensToWindow(lastTurnTokens, maxTokens).totalProcessedTokens;
|
|
162
|
+
return {
|
|
163
|
+
provider: "codex",
|
|
164
|
+
source: "codex_token_count",
|
|
165
|
+
...usage.model !== void 0 ? { model: usage.model } : {},
|
|
166
|
+
totalTokens,
|
|
167
|
+
...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {},
|
|
168
|
+
maxTokens,
|
|
169
|
+
rawMaxTokens: maxTokens,
|
|
170
|
+
percentage: percentage(totalTokens, maxTokens),
|
|
171
|
+
compactsAutomatically: true,
|
|
172
|
+
categories: compactCategories([
|
|
173
|
+
{
|
|
174
|
+
name: "Input context",
|
|
175
|
+
tokens: inputTokens,
|
|
176
|
+
percentage: percentage(inputTokens, maxTokens),
|
|
177
|
+
color: CODEX_CATEGORY_COLORS.input
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "Generated output",
|
|
181
|
+
tokens: outputTokens,
|
|
182
|
+
percentage: percentage(outputTokens, maxTokens),
|
|
183
|
+
color: CODEX_CATEGORY_COLORS.output
|
|
184
|
+
}
|
|
185
|
+
]),
|
|
186
|
+
apiUsage: {
|
|
187
|
+
inputTokens,
|
|
188
|
+
outputTokens,
|
|
189
|
+
...usage.last.cachedInputTokens !== null && usage.last.cachedInputTokens !== void 0 ? { cachedInputTokens: usage.last.cachedInputTokens } : {},
|
|
190
|
+
...usage.last.reasoningOutputTokens !== null && usage.last.reasoningOutputTokens !== void 0 ? { reasoningOutputTokens: usage.last.reasoningOutputTokens } : {},
|
|
191
|
+
...cumulativeTotalTokens !== null ? { totalTokens: cumulativeTotalTokens } : {}
|
|
192
|
+
},
|
|
193
|
+
updatedAt: usage.updatedAt
|
|
194
|
+
};
|
|
195
|
+
}
|
|
133
196
|
|
|
134
197
|
// ../shared/src/pricing.ts
|
|
135
198
|
var PLANS = {
|
|
@@ -1686,7 +1749,7 @@ function parseReplicasConfigString(content, filename) {
|
|
|
1686
1749
|
}
|
|
1687
1750
|
|
|
1688
1751
|
// ../shared/src/engine/environment.ts
|
|
1689
|
-
var DAYTONA_SNAPSHOT_ID = "26-05-2026-royal-york-
|
|
1752
|
+
var DAYTONA_SNAPSHOT_ID = "26-05-2026-royal-york-v4";
|
|
1690
1753
|
|
|
1691
1754
|
// ../shared/src/engine/types.ts
|
|
1692
1755
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -3548,19 +3611,19 @@ var previewService = new PreviewService();
|
|
|
3548
3611
|
|
|
3549
3612
|
// src/services/chat/chat-service.ts
|
|
3550
3613
|
import { existsSync as existsSync7 } from "fs";
|
|
3551
|
-
import { appendFile as appendFile5, mkdir as
|
|
3552
|
-
import { homedir as
|
|
3553
|
-
import { join as
|
|
3614
|
+
import { appendFile as appendFile5, mkdir as mkdir11, readFile as readFile8, rm, writeFile as writeFile9 } from "fs/promises";
|
|
3615
|
+
import { homedir as homedir13 } from "os";
|
|
3616
|
+
import { join as join15 } from "path";
|
|
3554
3617
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
3555
3618
|
|
|
3556
3619
|
// src/managers/claude-manager.ts
|
|
3557
3620
|
import {
|
|
3558
3621
|
query
|
|
3559
3622
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
3560
|
-
import { randomUUID as
|
|
3561
|
-
import { join as
|
|
3562
|
-
import { mkdir as
|
|
3563
|
-
import { homedir as
|
|
3623
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
3624
|
+
import { join as join13 } from "path";
|
|
3625
|
+
import { mkdir as mkdir9, appendFile as appendFile4 } from "fs/promises";
|
|
3626
|
+
import { homedir as homedir11 } from "os";
|
|
3564
3627
|
|
|
3565
3628
|
// src/utils/jsonl-reader.ts
|
|
3566
3629
|
import { readFile as readFile6 } from "fs/promises";
|
|
@@ -3593,33 +3656,6 @@ async function readJSONL(filePath) {
|
|
|
3593
3656
|
}
|
|
3594
3657
|
}
|
|
3595
3658
|
|
|
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
3659
|
// src/utils/linear-converter.ts
|
|
3624
3660
|
function linearThoughtToResponse(thought) {
|
|
3625
3661
|
return {
|
|
@@ -3999,8 +4035,138 @@ function extractPlanFromCodexEvent(event) {
|
|
|
3999
4035
|
status: entry.completed ? "completed" : hasIncomplete ? "inProgress" : "pending"
|
|
4000
4036
|
}));
|
|
4001
4037
|
}
|
|
4038
|
+
function codexAspReasoningText(item) {
|
|
4039
|
+
return [...item.summary, ...item.content].filter(Boolean).join("\n").trim();
|
|
4040
|
+
}
|
|
4041
|
+
function codexAspChangePaths(item) {
|
|
4042
|
+
return item.changes.map((change) => change.path).filter(Boolean).join(", ");
|
|
4043
|
+
}
|
|
4044
|
+
function codexAspStatusResult(status) {
|
|
4045
|
+
return status === "completed" ? "Done" : status || "Done";
|
|
4046
|
+
}
|
|
4047
|
+
function convertCodexAspItem(item, lifecycle, linearSessionId) {
|
|
4048
|
+
if (item.type === "agentMessage") {
|
|
4049
|
+
if (!item.text) return null;
|
|
4050
|
+
return {
|
|
4051
|
+
linearSessionId,
|
|
4052
|
+
content: {
|
|
4053
|
+
type: "thought",
|
|
4054
|
+
body: item.text
|
|
4055
|
+
}
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
if (item.type === "reasoning") {
|
|
4059
|
+
const text = codexAspReasoningText(item);
|
|
4060
|
+
if (!text) return null;
|
|
4061
|
+
return {
|
|
4062
|
+
linearSessionId,
|
|
4063
|
+
content: {
|
|
4064
|
+
type: "thought",
|
|
4065
|
+
body: text
|
|
4066
|
+
}
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
4069
|
+
if (item.type === "commandExecution") {
|
|
4070
|
+
return {
|
|
4071
|
+
linearSessionId,
|
|
4072
|
+
content: {
|
|
4073
|
+
type: "action",
|
|
4074
|
+
action: "Running command",
|
|
4075
|
+
parameter: item.command,
|
|
4076
|
+
...lifecycle === "completed" ? { result: item.exitCode !== null ? `Exit code: ${item.exitCode}` : item.aggregatedOutput || "Done" } : {}
|
|
4077
|
+
}
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
if (item.type === "fileChange") {
|
|
4081
|
+
return {
|
|
4082
|
+
linearSessionId,
|
|
4083
|
+
content: {
|
|
4084
|
+
type: "action",
|
|
4085
|
+
action: "File change",
|
|
4086
|
+
parameter: codexAspChangePaths(item),
|
|
4087
|
+
...lifecycle === "completed" ? { result: codexAspStatusResult(item.status) } : {}
|
|
4088
|
+
}
|
|
4089
|
+
};
|
|
4090
|
+
}
|
|
4091
|
+
if (item.type === "mcpToolCall") {
|
|
4092
|
+
return {
|
|
4093
|
+
linearSessionId,
|
|
4094
|
+
content: {
|
|
4095
|
+
type: "action",
|
|
4096
|
+
action: item.tool || "MCP tool call",
|
|
4097
|
+
parameter: item.server,
|
|
4098
|
+
...lifecycle === "completed" ? { result: item.error?.message ?? codexAspStatusResult(item.status) } : {}
|
|
4099
|
+
}
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
if (item.type === "dynamicToolCall") {
|
|
4103
|
+
return {
|
|
4104
|
+
linearSessionId,
|
|
4105
|
+
content: {
|
|
4106
|
+
type: "action",
|
|
4107
|
+
action: item.tool,
|
|
4108
|
+
parameter: item.namespace ?? "",
|
|
4109
|
+
...lifecycle === "completed" ? { result: item.success === false ? "Failed" : codexAspStatusResult(item.status) } : {}
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
if (item.type === "webSearch") {
|
|
4114
|
+
return {
|
|
4115
|
+
linearSessionId,
|
|
4116
|
+
content: {
|
|
4117
|
+
type: "action",
|
|
4118
|
+
action: "Web search",
|
|
4119
|
+
parameter: item.query,
|
|
4120
|
+
...lifecycle === "completed" ? { result: "Done" } : {}
|
|
4121
|
+
}
|
|
4122
|
+
};
|
|
4123
|
+
}
|
|
4124
|
+
if (item.type === "plan") {
|
|
4125
|
+
return {
|
|
4126
|
+
linearSessionId,
|
|
4127
|
+
content: {
|
|
4128
|
+
type: "action",
|
|
4129
|
+
action: "Updating plan",
|
|
4130
|
+
parameter: item.text,
|
|
4131
|
+
...lifecycle === "completed" ? { result: "Done" } : {}
|
|
4132
|
+
}
|
|
4133
|
+
};
|
|
4134
|
+
}
|
|
4135
|
+
return null;
|
|
4136
|
+
}
|
|
4137
|
+
function convertCodexAspNotification(notification, linearSessionId) {
|
|
4138
|
+
if (notification.method === "turn/started") {
|
|
4139
|
+
return {
|
|
4140
|
+
linearSessionId,
|
|
4141
|
+
content: {
|
|
4142
|
+
type: "thought",
|
|
4143
|
+
body: "Processing..."
|
|
4144
|
+
}
|
|
4145
|
+
};
|
|
4146
|
+
}
|
|
4147
|
+
if (notification.method === "item/started") {
|
|
4148
|
+
return convertCodexAspItem(notification.params.item, "started", linearSessionId);
|
|
4149
|
+
}
|
|
4150
|
+
if (notification.method === "item/completed") {
|
|
4151
|
+
return convertCodexAspItem(notification.params.item, "completed", linearSessionId);
|
|
4152
|
+
}
|
|
4153
|
+
return null;
|
|
4154
|
+
}
|
|
4155
|
+
function extractPlanFromCodexAspNotification(notification) {
|
|
4156
|
+
if (notification.method !== "turn/plan/updated" || notification.params.plan.length === 0) {
|
|
4157
|
+
return null;
|
|
4158
|
+
}
|
|
4159
|
+
return notification.params.plan.filter((item) => item.step.trim().length > 0).map((item) => ({
|
|
4160
|
+
content: item.step.trim(),
|
|
4161
|
+
status: item.status
|
|
4162
|
+
}));
|
|
4163
|
+
}
|
|
4002
4164
|
|
|
4003
4165
|
// src/utils/image-utils.ts
|
|
4166
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4167
|
+
import { mkdir as mkdir8, unlink as unlink2, writeFile as writeFile7 } from "fs/promises";
|
|
4168
|
+
import { homedir as homedir10 } from "os";
|
|
4169
|
+
import { join as join12 } from "path";
|
|
4004
4170
|
function isImageMediaType(value) {
|
|
4005
4171
|
return IMAGE_MEDIA_TYPES.includes(value);
|
|
4006
4172
|
}
|
|
@@ -4078,6 +4244,26 @@ async function normalizeImages(images) {
|
|
|
4078
4244
|
}
|
|
4079
4245
|
return normalized;
|
|
4080
4246
|
}
|
|
4247
|
+
async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(homedir10(), ".replicas", "codex", "temp-images")) {
|
|
4248
|
+
await mkdir8(tempImageDir, { recursive: true });
|
|
4249
|
+
const tempPaths = [];
|
|
4250
|
+
try {
|
|
4251
|
+
for (const image of images) {
|
|
4252
|
+
const ext = image.source.media_type.split("/")[1] || "png";
|
|
4253
|
+
const filename = `img_${randomUUID3()}.${ext}`;
|
|
4254
|
+
const filepath = join12(tempImageDir, filename);
|
|
4255
|
+
await writeFile7(filepath, Buffer.from(image.source.data, "base64"));
|
|
4256
|
+
tempPaths.push(filepath);
|
|
4257
|
+
}
|
|
4258
|
+
} catch (error) {
|
|
4259
|
+
await removeTempImageFiles(tempPaths);
|
|
4260
|
+
throw error;
|
|
4261
|
+
}
|
|
4262
|
+
return tempPaths;
|
|
4263
|
+
}
|
|
4264
|
+
async function removeTempImageFiles(paths) {
|
|
4265
|
+
await Promise.allSettled(paths.map((path4) => unlink2(path4)));
|
|
4266
|
+
}
|
|
4081
4267
|
|
|
4082
4268
|
// src/services/message-queue-service.ts
|
|
4083
4269
|
var MessageQueueService = class {
|
|
@@ -4216,6 +4402,33 @@ var MessageQueueService = class {
|
|
|
4216
4402
|
}
|
|
4217
4403
|
};
|
|
4218
4404
|
|
|
4405
|
+
// src/services/monolith-service.ts
|
|
4406
|
+
var MonolithService = class {
|
|
4407
|
+
async sendEvent(event) {
|
|
4408
|
+
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
4409
|
+
return;
|
|
4410
|
+
}
|
|
4411
|
+
try {
|
|
4412
|
+
const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
|
|
4413
|
+
method: "POST",
|
|
4414
|
+
headers: {
|
|
4415
|
+
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
4416
|
+
"X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
|
|
4417
|
+
"Content-Type": "application/json"
|
|
4418
|
+
},
|
|
4419
|
+
body: JSON.stringify(event)
|
|
4420
|
+
});
|
|
4421
|
+
if (!response.ok) {
|
|
4422
|
+
const errorText = await response.text();
|
|
4423
|
+
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
4424
|
+
}
|
|
4425
|
+
} catch (error) {
|
|
4426
|
+
console.error("[MonolithService] Failed to send event:", error);
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
};
|
|
4430
|
+
var monolithService = new MonolithService();
|
|
4431
|
+
|
|
4219
4432
|
// src/managers/coding-agent-manager.ts
|
|
4220
4433
|
var MAX_INTERRUPT_QUEUE_ITEMS = 1e3;
|
|
4221
4434
|
var MAX_INTERRUPT_QUEUE_CHARS = 2e5;
|
|
@@ -4425,6 +4638,49 @@ async function getAgentAdditionalDirectories() {
|
|
|
4425
4638
|
}
|
|
4426
4639
|
}
|
|
4427
4640
|
|
|
4641
|
+
// src/utils/linear-event-forwarder.ts
|
|
4642
|
+
function isLinearThoughtEvent(event) {
|
|
4643
|
+
return event.content.type === "thought";
|
|
4644
|
+
}
|
|
4645
|
+
var LinearEventForwarder = class {
|
|
4646
|
+
linearSessionId;
|
|
4647
|
+
latestThoughtEvent = null;
|
|
4648
|
+
constructor(linearSessionId) {
|
|
4649
|
+
this.linearSessionId = linearSessionId;
|
|
4650
|
+
}
|
|
4651
|
+
sendPlan(plan) {
|
|
4652
|
+
if (!this.linearSessionId || !plan) return;
|
|
4653
|
+
monolithService.sendEvent({
|
|
4654
|
+
type: "agent_plan_update",
|
|
4655
|
+
payload: { linearSessionId: this.linearSessionId, plan }
|
|
4656
|
+
}).catch(() => {
|
|
4657
|
+
});
|
|
4658
|
+
}
|
|
4659
|
+
sendEvent(linearEvent) {
|
|
4660
|
+
if (!this.linearSessionId || !linearEvent) return;
|
|
4661
|
+
if (this.latestThoughtEvent) {
|
|
4662
|
+
monolithService.sendEvent({ type: "agent_update", payload: this.latestThoughtEvent }).catch(() => {
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
if (isLinearThoughtEvent(linearEvent)) {
|
|
4666
|
+
this.latestThoughtEvent = linearEvent;
|
|
4667
|
+
return;
|
|
4668
|
+
}
|
|
4669
|
+
this.latestThoughtEvent = null;
|
|
4670
|
+
monolithService.sendEvent({ type: "agent_update", payload: linearEvent }).catch(() => {
|
|
4671
|
+
});
|
|
4672
|
+
}
|
|
4673
|
+
flushThoughtAsResponse() {
|
|
4674
|
+
if (!this.linearSessionId || !this.latestThoughtEvent) return;
|
|
4675
|
+
monolithService.sendEvent({
|
|
4676
|
+
type: "agent_update",
|
|
4677
|
+
payload: linearThoughtToResponse(this.latestThoughtEvent)
|
|
4678
|
+
}).catch(() => {
|
|
4679
|
+
});
|
|
4680
|
+
this.latestThoughtEvent = null;
|
|
4681
|
+
}
|
|
4682
|
+
};
|
|
4683
|
+
|
|
4428
4684
|
// src/managers/claude-manager.ts
|
|
4429
4685
|
var PromptStream = class {
|
|
4430
4686
|
queue = [];
|
|
@@ -4464,9 +4720,6 @@ var PromptStream = class {
|
|
|
4464
4720
|
};
|
|
4465
4721
|
}
|
|
4466
4722
|
};
|
|
4467
|
-
function isLinearThoughtEvent(event) {
|
|
4468
|
-
return event.content.type === "thought";
|
|
4469
|
-
}
|
|
4470
4723
|
var ALWAYS_ALLOWED_TOOLS = [
|
|
4471
4724
|
"Read",
|
|
4472
4725
|
"Glob",
|
|
@@ -4589,7 +4842,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4589
4842
|
pendingToolInputs = /* @__PURE__ */ new Map();
|
|
4590
4843
|
constructor(options) {
|
|
4591
4844
|
super(options);
|
|
4592
|
-
this.historyFile = options.historyFilePath ??
|
|
4845
|
+
this.historyFile = options.historyFilePath ?? join13(homedir11(), ".replicas", "claude", "history.jsonl");
|
|
4593
4846
|
this.systemPromptOverride = options.systemPromptOverride;
|
|
4594
4847
|
this.toolsOverride = options.tools;
|
|
4595
4848
|
this.mcpServersConfig = options.mcpServers;
|
|
@@ -4653,7 +4906,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4653
4906
|
if (!handler) {
|
|
4654
4907
|
return { behavior: "allow" };
|
|
4655
4908
|
}
|
|
4656
|
-
const requestId =
|
|
4909
|
+
const requestId = randomUUID4();
|
|
4657
4910
|
const toolUseId = options.toolUseID;
|
|
4658
4911
|
const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
|
|
4659
4912
|
const result = await new Promise((resolve3) => {
|
|
@@ -4813,32 +5066,12 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4813
5066
|
this.activePromptStream?.close();
|
|
4814
5067
|
await this.activeQuery.interrupt();
|
|
4815
5068
|
}
|
|
4816
|
-
|
|
5069
|
+
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
4817
5070
|
for await (const msg of response) {
|
|
4818
5071
|
await this.handleMessage(msg);
|
|
4819
5072
|
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
|
-
}
|
|
5073
|
+
linearForwarder.sendPlan(extractPlanFromClaudeEvent(msg));
|
|
5074
|
+
linearForwarder.sendEvent(convertClaudeEvent(msg, linearSessionId));
|
|
4842
5075
|
}
|
|
4843
5076
|
if (msg.type === "result") {
|
|
4844
5077
|
await this.recordContextUsage(response);
|
|
@@ -4846,11 +5079,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4846
5079
|
break;
|
|
4847
5080
|
}
|
|
4848
5081
|
}
|
|
4849
|
-
|
|
4850
|
-
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
4851
|
-
monolithService.sendEvent({ type: "agent_update", payload: responseEvent }).catch(() => {
|
|
4852
|
-
});
|
|
4853
|
-
}
|
|
5082
|
+
linearForwarder.flushThoughtAsResponse();
|
|
4854
5083
|
} finally {
|
|
4855
5084
|
this.activeQuery = null;
|
|
4856
5085
|
this.activePromptStream?.close();
|
|
@@ -4927,8 +5156,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4927
5156
|
};
|
|
4928
5157
|
}
|
|
4929
5158
|
async initialize() {
|
|
4930
|
-
const historyDir =
|
|
4931
|
-
await
|
|
5159
|
+
const historyDir = join13(homedir11(), ".replicas", "claude");
|
|
5160
|
+
await mkdir9(historyDir, { recursive: true });
|
|
4932
5161
|
if (this.initialSessionId) {
|
|
4933
5162
|
this.sessionId = this.initialSessionId;
|
|
4934
5163
|
console.log(`[ClaudeManager] Restored session ID from persisted state: ${this.sessionId}`);
|
|
@@ -4990,27 +5219,29 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4990
5219
|
|
|
4991
5220
|
// src/managers/codex-manager.ts
|
|
4992
5221
|
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";
|
|
5222
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile8, mkdir as mkdir10, readFile as readFile7 } from "fs/promises";
|
|
4995
5223
|
import { existsSync as existsSync6 } from "fs";
|
|
4996
|
-
import { join as
|
|
4997
|
-
import { homedir as
|
|
5224
|
+
import { join as join14 } from "path";
|
|
5225
|
+
import { homedir as homedir12 } from "os";
|
|
4998
5226
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
function
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5227
|
+
|
|
5228
|
+
// src/utils/codex-quota.ts
|
|
5229
|
+
function buildCodexRateLimitsSnapshot(fields) {
|
|
5230
|
+
if (fields.unlimited === true) return null;
|
|
5231
|
+
let state = "ok";
|
|
5232
|
+
if (fields.hasCredits === false) {
|
|
5233
|
+
state = "out_of_credits";
|
|
5234
|
+
} else if (fields.rateLimitResetType !== null) {
|
|
5235
|
+
state = "rate_limited";
|
|
5007
5236
|
}
|
|
5008
|
-
return
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5237
|
+
return {
|
|
5238
|
+
state,
|
|
5239
|
+
balance: fields.balance,
|
|
5240
|
+
rateLimitResetType: fields.rateLimitResetType,
|
|
5241
|
+
planType: fields.planType
|
|
5242
|
+
};
|
|
5012
5243
|
}
|
|
5013
|
-
function
|
|
5244
|
+
function extractCodexRateLimitsSnapshotFromJsonl(parsed) {
|
|
5014
5245
|
if (!isRecord3(parsed)) return null;
|
|
5015
5246
|
const payload = isRecord3(parsed.payload) ? parsed.payload : parsed;
|
|
5016
5247
|
const rateLimits = isRecord3(payload.rate_limits) ? payload.rate_limits : null;
|
|
@@ -5019,35 +5250,81 @@ function extractRateLimitsSnapshot(parsed) {
|
|
|
5019
5250
|
const hasCredits = credits && typeof credits.has_credits === "boolean" ? credits.has_credits : null;
|
|
5020
5251
|
const unlimited = credits && typeof credits.unlimited === "boolean" ? credits.unlimited : null;
|
|
5021
5252
|
const balance = credits && typeof credits.balance === "string" ? credits.balance : null;
|
|
5022
|
-
if (unlimited === true) return null;
|
|
5023
5253
|
const rateLimitResetType = typeof rateLimits.rate_limit_reached_type === "string" && rateLimits.rate_limit_reached_type.length > 0 ? rateLimits.rate_limit_reached_type : null;
|
|
5024
5254
|
const planType = typeof rateLimits.plan_type === "string" ? rateLimits.plan_type : null;
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5255
|
+
return buildCodexRateLimitsSnapshot({
|
|
5256
|
+
unlimited,
|
|
5257
|
+
hasCredits,
|
|
5258
|
+
balance,
|
|
5259
|
+
rateLimitResetType,
|
|
5260
|
+
planType
|
|
5261
|
+
});
|
|
5262
|
+
}
|
|
5263
|
+
var CodexQuotaStatusTracker = class {
|
|
5264
|
+
lastEmittedQuotaState = "ok";
|
|
5265
|
+
latestQuotaSnapshot = null;
|
|
5266
|
+
quotaBlocked = false;
|
|
5267
|
+
get blocked() {
|
|
5268
|
+
return this.quotaBlocked;
|
|
5269
|
+
}
|
|
5270
|
+
get latestSnapshot() {
|
|
5271
|
+
return this.latestQuotaSnapshot;
|
|
5272
|
+
}
|
|
5273
|
+
prime(snapshot) {
|
|
5274
|
+
this.latestQuotaSnapshot = snapshot;
|
|
5275
|
+
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
5276
|
+
}
|
|
5277
|
+
apply(snapshot, force = false) {
|
|
5278
|
+
const stateChanged = snapshot.state !== this.lastEmittedQuotaState;
|
|
5279
|
+
if (!stateChanged && !force) {
|
|
5280
|
+
return null;
|
|
5281
|
+
}
|
|
5282
|
+
this.lastEmittedQuotaState = snapshot.state;
|
|
5283
|
+
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
5284
|
+
this.latestQuotaSnapshot = snapshot;
|
|
5285
|
+
const payload = {
|
|
5286
|
+
state: snapshot.state,
|
|
5287
|
+
balance: snapshot.balance,
|
|
5288
|
+
rateLimitResetType: snapshot.rateLimitResetType,
|
|
5289
|
+
planType: snapshot.planType
|
|
5290
|
+
};
|
|
5291
|
+
const event = {
|
|
5292
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5293
|
+
type: CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
5294
|
+
payload
|
|
5295
|
+
};
|
|
5296
|
+
return event;
|
|
5030
5297
|
}
|
|
5031
|
-
|
|
5298
|
+
};
|
|
5299
|
+
|
|
5300
|
+
// src/utils/codex-auth.ts
|
|
5301
|
+
function isCodexAuthError(error) {
|
|
5302
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
5303
|
+
const lower = msg.toLowerCase();
|
|
5304
|
+
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");
|
|
5032
5305
|
}
|
|
5033
|
-
|
|
5306
|
+
|
|
5307
|
+
// src/managers/codex-manager.ts
|
|
5308
|
+
var DEFAULT_MODEL = "gpt-5.5";
|
|
5309
|
+
var CODEX_CONFIG_PATH = join14(homedir12(), ".codex", "config.toml");
|
|
5310
|
+
function isJsonlEvent2(value) {
|
|
5311
|
+
if (!isRecord3(value)) {
|
|
5312
|
+
return false;
|
|
5313
|
+
}
|
|
5314
|
+
return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord3(value.payload);
|
|
5315
|
+
}
|
|
5316
|
+
function sleep(ms) {
|
|
5317
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5318
|
+
}
|
|
5319
|
+
var CodexManager = class extends CodingAgentManager {
|
|
5034
5320
|
codex;
|
|
5035
5321
|
currentThreadId = null;
|
|
5036
5322
|
currentThread = null;
|
|
5037
|
-
tempImageDir;
|
|
5038
5323
|
activeAbortController = null;
|
|
5039
|
-
|
|
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. */
|
|
5042
|
-
lastEmittedQuotaState = "ok";
|
|
5043
|
-
/** Last full snapshot, retained so we can re-emit when a user retries while blocked. */
|
|
5044
|
-
latestQuotaSnapshot = null;
|
|
5045
|
-
/** When true, new turns short-circuit instead of running. Set by `out_of_credits`. */
|
|
5046
|
-
quotaBlocked = false;
|
|
5324
|
+
quotaStatus = new CodexQuotaStatusTracker();
|
|
5047
5325
|
constructor(options) {
|
|
5048
5326
|
super(options);
|
|
5049
5327
|
this.codex = this.createCodexClient();
|
|
5050
|
-
this.tempImageDir = join13(homedir11(), ".replicas", "codex", "temp-images");
|
|
5051
5328
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
5052
5329
|
}
|
|
5053
5330
|
createCodexClient() {
|
|
@@ -5067,12 +5344,6 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5067
5344
|
console.log(`[CodexManager] Restored thread ID from persisted state: ${this.currentThreadId}`);
|
|
5068
5345
|
}
|
|
5069
5346
|
}
|
|
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
5347
|
async flushQuotaSnapshotFromCurrentSession() {
|
|
5077
5348
|
if (!this.currentThreadId) return;
|
|
5078
5349
|
try {
|
|
@@ -5084,7 +5355,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5084
5355
|
for (const line of lines) {
|
|
5085
5356
|
try {
|
|
5086
5357
|
const parsed = JSON.parse(line);
|
|
5087
|
-
const snapshot =
|
|
5358
|
+
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
5088
5359
|
if (snapshot) {
|
|
5089
5360
|
latest = snapshot;
|
|
5090
5361
|
}
|
|
@@ -5098,41 +5369,9 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5098
5369
|
console.warn("[CodexManager] Failed to flush quota snapshot from session:", error);
|
|
5099
5370
|
}
|
|
5100
5371
|
}
|
|
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
5372
|
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
|
-
}
|
|
5373
|
+
const event = this.quotaStatus.apply(snapshot, force);
|
|
5374
|
+
if (event) this.onEvent(event);
|
|
5136
5375
|
}
|
|
5137
5376
|
async interruptActiveTurn() {
|
|
5138
5377
|
if (this.activeAbortController) {
|
|
@@ -5145,8 +5384,8 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5145
5384
|
*/
|
|
5146
5385
|
async updateCodexConfig(developerInstructions) {
|
|
5147
5386
|
try {
|
|
5148
|
-
const codexDir =
|
|
5149
|
-
await
|
|
5387
|
+
const codexDir = join14(homedir12(), ".codex");
|
|
5388
|
+
await mkdir10(codexDir, { recursive: true });
|
|
5150
5389
|
let config = {};
|
|
5151
5390
|
if (existsSync6(CODEX_CONFIG_PATH)) {
|
|
5152
5391
|
try {
|
|
@@ -5165,34 +5404,17 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5165
5404
|
delete config.developer_instructions;
|
|
5166
5405
|
}
|
|
5167
5406
|
const tomlContent = stringifyToml(config);
|
|
5168
|
-
await
|
|
5407
|
+
await writeFile8(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
5169
5408
|
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
5170
5409
|
} catch (error) {
|
|
5171
5410
|
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
5172
5411
|
}
|
|
5173
5412
|
}
|
|
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
5413
|
async processMessageInternal(request) {
|
|
5192
5414
|
try {
|
|
5193
5415
|
await this.executeCodexTurn(request);
|
|
5194
5416
|
} catch (error) {
|
|
5195
|
-
if (
|
|
5417
|
+
if (isCodexAuthError(error)) {
|
|
5196
5418
|
const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
|
|
5197
5419
|
if (refreshed) {
|
|
5198
5420
|
this.resetCodexClient();
|
|
@@ -5204,10 +5426,10 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5204
5426
|
}
|
|
5205
5427
|
}
|
|
5206
5428
|
async executeCodexTurn(request) {
|
|
5207
|
-
if (this.
|
|
5429
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
5208
5430
|
await this.flushQuotaSnapshotFromCurrentSession();
|
|
5209
|
-
if (this.
|
|
5210
|
-
this.emitQuotaStatus(this.
|
|
5431
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
5432
|
+
this.emitQuotaStatus(this.quotaStatus.latestSnapshot, true);
|
|
5211
5433
|
try {
|
|
5212
5434
|
await this.onTurnComplete();
|
|
5213
5435
|
} catch (error) {
|
|
@@ -5231,13 +5453,13 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5231
5453
|
try {
|
|
5232
5454
|
if (images && images.length > 0) {
|
|
5233
5455
|
const normalizedImages = await normalizeImages(images);
|
|
5234
|
-
tempImagePaths = await
|
|
5456
|
+
tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
|
|
5235
5457
|
}
|
|
5236
5458
|
const developerInstructions = this.buildCombinedInstructions(customInstructions);
|
|
5237
5459
|
await this.updateCodexConfig(developerInstructions);
|
|
5238
5460
|
const sandboxMode = "danger-full-access";
|
|
5239
5461
|
const webSearchMode = "live";
|
|
5240
|
-
const codexReasoningEffort = thinkingLevel
|
|
5462
|
+
const codexReasoningEffort = codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
5241
5463
|
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
5242
5464
|
const threadOptions = {
|
|
5243
5465
|
workingDirectory: this.workingDirectory,
|
|
@@ -5281,42 +5503,17 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5281
5503
|
}
|
|
5282
5504
|
try {
|
|
5283
5505
|
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
5284
|
-
|
|
5506
|
+
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
5285
5507
|
for await (const event of events) {
|
|
5286
5508
|
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
|
-
}
|
|
5509
|
+
linearForwarder.sendPlan(extractPlanFromCodexEvent(event));
|
|
5510
|
+
linearForwarder.sendEvent(convertCodexEvent(event, linearSessionId));
|
|
5309
5511
|
}
|
|
5310
5512
|
}
|
|
5311
|
-
|
|
5312
|
-
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
5313
|
-
monolithService.sendEvent({ type: "agent_update", payload: responseEvent }).catch(() => {
|
|
5314
|
-
});
|
|
5315
|
-
}
|
|
5513
|
+
linearForwarder.flushThoughtAsResponse();
|
|
5316
5514
|
} catch (error) {
|
|
5317
5515
|
await this.flushQuotaSnapshotFromCurrentSession();
|
|
5318
|
-
if (this.
|
|
5319
|
-
console.warn("[CodexManager] runStreamed failed while quota was blocked \u2014 surfacing as quota state.");
|
|
5516
|
+
if (this.quotaStatus.blocked) {
|
|
5320
5517
|
return;
|
|
5321
5518
|
}
|
|
5322
5519
|
throw error;
|
|
@@ -5325,6 +5522,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5325
5522
|
if (stopTail) {
|
|
5326
5523
|
await stopTail();
|
|
5327
5524
|
}
|
|
5525
|
+
await removeTempImageFiles(tempImagePaths);
|
|
5328
5526
|
try {
|
|
5329
5527
|
await this.onTurnComplete();
|
|
5330
5528
|
} catch (error) {
|
|
@@ -5333,11 +5531,6 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5333
5531
|
this.activeAbortController = null;
|
|
5334
5532
|
}
|
|
5335
5533
|
}
|
|
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
5534
|
async getHistory() {
|
|
5342
5535
|
if (!this.currentThreadId) {
|
|
5343
5536
|
return {
|
|
@@ -5360,13 +5553,13 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5360
5553
|
}
|
|
5361
5554
|
// Helper methods for finding session files
|
|
5362
5555
|
async findSessionFile(threadId) {
|
|
5363
|
-
const sessionsDir =
|
|
5556
|
+
const sessionsDir = join14(homedir12(), ".codex", "sessions");
|
|
5364
5557
|
try {
|
|
5365
5558
|
const now = /* @__PURE__ */ new Date();
|
|
5366
5559
|
const year = now.getFullYear();
|
|
5367
5560
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
5368
5561
|
const day = String(now.getDate()).padStart(2, "0");
|
|
5369
|
-
const todayDir =
|
|
5562
|
+
const todayDir = join14(sessionsDir, String(year), month, day);
|
|
5370
5563
|
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
5371
5564
|
if (file) return file;
|
|
5372
5565
|
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
@@ -5375,7 +5568,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5375
5568
|
const searchYear = date.getFullYear();
|
|
5376
5569
|
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
5377
5570
|
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
5378
|
-
const searchDir =
|
|
5571
|
+
const searchDir = join14(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
5379
5572
|
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
5380
5573
|
if (file2) return file2;
|
|
5381
5574
|
}
|
|
@@ -5389,7 +5582,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5389
5582
|
const files = await readdir3(directory);
|
|
5390
5583
|
for (const file of files) {
|
|
5391
5584
|
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
5392
|
-
const fullPath =
|
|
5585
|
+
const fullPath = join14(directory, file);
|
|
5393
5586
|
const stats = await stat2(fullPath);
|
|
5394
5587
|
if (stats.isFile()) {
|
|
5395
5588
|
return fullPath;
|
|
@@ -5446,15 +5639,13 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5446
5639
|
seenLines.add(line);
|
|
5447
5640
|
try {
|
|
5448
5641
|
const parsed = JSON.parse(line);
|
|
5449
|
-
const snapshot =
|
|
5642
|
+
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
5450
5643
|
if (snapshot) latest = snapshot;
|
|
5451
5644
|
} catch {
|
|
5452
5645
|
}
|
|
5453
5646
|
}
|
|
5454
5647
|
if (latest) {
|
|
5455
|
-
this.
|
|
5456
|
-
this.latestQuotaSnapshot = latest;
|
|
5457
|
-
this.quotaBlocked = latest.state === "out_of_credits";
|
|
5648
|
+
this.quotaStatus.prime(latest);
|
|
5458
5649
|
}
|
|
5459
5650
|
} catch {
|
|
5460
5651
|
}
|
|
@@ -5474,7 +5665,7 @@ var CodexManager = class _CodexManager extends CodingAgentManager {
|
|
|
5474
5665
|
seenLines.add(trimmed);
|
|
5475
5666
|
try {
|
|
5476
5667
|
const parsed = JSON.parse(trimmed);
|
|
5477
|
-
const snapshot =
|
|
5668
|
+
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
5478
5669
|
if (snapshot) {
|
|
5479
5670
|
this.emitQuotaStatus(snapshot);
|
|
5480
5671
|
}
|
|
@@ -5787,6 +5978,26 @@ var AppServerProcess = class {
|
|
|
5787
5978
|
throw error;
|
|
5788
5979
|
}
|
|
5789
5980
|
}
|
|
5981
|
+
async stop() {
|
|
5982
|
+
const child = this.child;
|
|
5983
|
+
this.shuttingDown = true;
|
|
5984
|
+
this.client?.dispose(new Error("ASP process stopped"));
|
|
5985
|
+
this.client = null;
|
|
5986
|
+
this.child = null;
|
|
5987
|
+
if (!child || child.killed) {
|
|
5988
|
+
return;
|
|
5989
|
+
}
|
|
5990
|
+
await new Promise((resolve3) => {
|
|
5991
|
+
const timer = setTimeout(() => {
|
|
5992
|
+
child.kill("SIGKILL");
|
|
5993
|
+
}, 2e3);
|
|
5994
|
+
child.once("exit", () => {
|
|
5995
|
+
clearTimeout(timer);
|
|
5996
|
+
resolve3();
|
|
5997
|
+
});
|
|
5998
|
+
child.kill("SIGTERM");
|
|
5999
|
+
});
|
|
6000
|
+
}
|
|
5790
6001
|
async loginWithConfiguredApiKey(client) {
|
|
5791
6002
|
if (ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD !== "api_key" || !ENGINE_ENV.OPENAI_API_KEY) {
|
|
5792
6003
|
return;
|
|
@@ -5814,12 +6025,21 @@ var AppServerProcess = class {
|
|
|
5814
6025
|
|
|
5815
6026
|
// src/managers/codex-asp/asp-host.ts
|
|
5816
6027
|
var hostPromise = null;
|
|
6028
|
+
var activeProcess = null;
|
|
6029
|
+
var restartPromise = null;
|
|
5817
6030
|
async function getCodexAspHost() {
|
|
6031
|
+
if (restartPromise) {
|
|
6032
|
+
await restartPromise;
|
|
6033
|
+
}
|
|
5818
6034
|
hostPromise ??= (async () => {
|
|
5819
6035
|
try {
|
|
5820
6036
|
const process2 = new AppServerProcess();
|
|
5821
6037
|
const { client } = await process2.start();
|
|
6038
|
+
activeProcess = process2;
|
|
5822
6039
|
process2.on("exit", () => {
|
|
6040
|
+
if (activeProcess === process2) {
|
|
6041
|
+
activeProcess = null;
|
|
6042
|
+
}
|
|
5823
6043
|
hostPromise = null;
|
|
5824
6044
|
});
|
|
5825
6045
|
return { client };
|
|
@@ -5830,21 +6050,67 @@ async function getCodexAspHost() {
|
|
|
5830
6050
|
})();
|
|
5831
6051
|
return hostPromise;
|
|
5832
6052
|
}
|
|
6053
|
+
async function restartCodexAspHost() {
|
|
6054
|
+
if (restartPromise) {
|
|
6055
|
+
return restartPromise;
|
|
6056
|
+
}
|
|
6057
|
+
restartPromise = (async () => {
|
|
6058
|
+
const process2 = activeProcess;
|
|
6059
|
+
hostPromise = null;
|
|
6060
|
+
activeProcess = null;
|
|
6061
|
+
if (process2) {
|
|
6062
|
+
await process2.stop();
|
|
6063
|
+
}
|
|
6064
|
+
})();
|
|
6065
|
+
try {
|
|
6066
|
+
await restartPromise;
|
|
6067
|
+
} finally {
|
|
6068
|
+
restartPromise = null;
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
5833
6071
|
|
|
5834
6072
|
// src/managers/codex-asp/codex-asp-manager.ts
|
|
5835
6073
|
var DEFAULT_MODEL2 = "gpt-5.5";
|
|
5836
6074
|
var THREAD_START_METHOD = "thread/start";
|
|
6075
|
+
var THREAD_RESUME_METHOD = "thread/resume";
|
|
6076
|
+
var THREAD_READ_METHOD = "thread/read";
|
|
5837
6077
|
var TURN_START_METHOD = "turn/start";
|
|
5838
6078
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
6079
|
+
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
6080
|
+
var TURN_STARTED_METHOD = "turn/started";
|
|
5839
6081
|
var TURN_COMPLETED_METHOD = "turn/completed";
|
|
6082
|
+
var TURN_PLAN_UPDATED_METHOD = "turn/plan/updated";
|
|
6083
|
+
var ITEM_STARTED_METHOD = "item/started";
|
|
5840
6084
|
var ITEM_COMPLETED_METHOD = "item/completed";
|
|
5841
6085
|
var AGENT_MESSAGE_DELTA_METHOD = "item/agentMessage/delta";
|
|
6086
|
+
var ACCOUNT_RATE_LIMITS_UPDATED_METHOD = "account/rateLimits/updated";
|
|
6087
|
+
var THREAD_TOKEN_USAGE_UPDATED_METHOD = "thread/tokenUsage/updated";
|
|
6088
|
+
var THREAD_COMPACTED_METHOD = "thread/compacted";
|
|
5842
6089
|
var COMMAND_APPROVAL_METHOD = "item/commandExecution/requestApproval";
|
|
5843
6090
|
var FILE_CHANGE_APPROVAL_METHOD = "item/fileChange/requestApproval";
|
|
6091
|
+
function toReasoningEffort(thinkingLevel) {
|
|
6092
|
+
return codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
6093
|
+
}
|
|
6094
|
+
function extractRateLimitsSnapshot(rateLimits) {
|
|
6095
|
+
const credits = rateLimits.credits;
|
|
6096
|
+
return buildCodexRateLimitsSnapshot({
|
|
6097
|
+
unlimited: credits?.unlimited ?? null,
|
|
6098
|
+
hasCredits: credits?.hasCredits ?? null,
|
|
6099
|
+
balance: credits?.balance ?? null,
|
|
6100
|
+
rateLimitResetType: rateLimits.rateLimitReachedType || null,
|
|
6101
|
+
planType: rateLimits.planType
|
|
6102
|
+
});
|
|
6103
|
+
}
|
|
6104
|
+
function timestampFromSeconds(value) {
|
|
6105
|
+
return new Date((value ?? Date.now() / 1e3) * 1e3).toISOString();
|
|
6106
|
+
}
|
|
5844
6107
|
var CodexAspManager = class extends CodingAgentManager {
|
|
5845
6108
|
currentThreadId = null;
|
|
5846
6109
|
activeTurnId = null;
|
|
6110
|
+
threadAttached = false;
|
|
5847
6111
|
historyEvents = [];
|
|
6112
|
+
emittedItemStages = /* @__PURE__ */ new Set();
|
|
6113
|
+
quotaStatus = new CodexQuotaStatusTracker();
|
|
5848
6114
|
constructor(options) {
|
|
5849
6115
|
super(options);
|
|
5850
6116
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
@@ -5870,57 +6136,115 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
5870
6136
|
}
|
|
5871
6137
|
}
|
|
5872
6138
|
async getHistory() {
|
|
6139
|
+
if (!this.currentThreadId) {
|
|
6140
|
+
return { thread_id: null, events: [] };
|
|
6141
|
+
}
|
|
6142
|
+
try {
|
|
6143
|
+
const host = await getCodexAspHost();
|
|
6144
|
+
const response = await host.client.request(
|
|
6145
|
+
THREAD_READ_METHOD,
|
|
6146
|
+
{ threadId: this.currentThreadId, includeTurns: true }
|
|
6147
|
+
);
|
|
6148
|
+
const events = this.threadToHistoryEvents(response.thread);
|
|
6149
|
+
if (events.length > 0) {
|
|
6150
|
+
const supplementalEvents = this.historyEvents.filter((event) => event.type === CODEX_QUOTA_STATUS_EVENT_TYPE || event.type === CONTEXT_USAGE_EVENT_TYPE);
|
|
6151
|
+
return {
|
|
6152
|
+
thread_id: this.currentThreadId,
|
|
6153
|
+
events: [...events, ...supplementalEvents].sort((a, b) => a.timestamp.localeCompare(b.timestamp))
|
|
6154
|
+
};
|
|
6155
|
+
}
|
|
6156
|
+
} catch {
|
|
6157
|
+
}
|
|
5873
6158
|
return {
|
|
5874
6159
|
thread_id: this.currentThreadId,
|
|
5875
6160
|
events: [...this.historyEvents]
|
|
5876
6161
|
};
|
|
5877
6162
|
}
|
|
5878
6163
|
async processMessageInternal(request) {
|
|
5879
|
-
let
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
6164
|
+
let userMessageRecorded = false;
|
|
6165
|
+
const recordUserMessage = () => {
|
|
6166
|
+
if (userMessageRecorded) return;
|
|
6167
|
+
userMessageRecorded = true;
|
|
5883
6168
|
this.recordHistoryEvent("event_msg", {
|
|
5884
6169
|
type: "user_message",
|
|
5885
6170
|
message: request.message
|
|
5886
6171
|
});
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
const turnError = completedTurn.error;
|
|
5899
|
-
if (!turnError) {
|
|
5900
|
-
throw new Error("Codex ASP turn failed without error details");
|
|
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}`);
|
|
6172
|
+
};
|
|
6173
|
+
try {
|
|
6174
|
+
await this.executeAspTurn(request, recordUserMessage);
|
|
6175
|
+
} catch (error) {
|
|
6176
|
+
if (isCodexAuthError(error)) {
|
|
6177
|
+
const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
|
|
6178
|
+
if (refreshed) {
|
|
6179
|
+
await restartCodexAspHost();
|
|
6180
|
+
this.threadAttached = false;
|
|
6181
|
+
await this.executeAspTurn(request, recordUserMessage);
|
|
6182
|
+
return;
|
|
5908
6183
|
}
|
|
5909
|
-
throw new Error(parts.join(" "));
|
|
5910
|
-
}
|
|
5911
|
-
if (completedTurn.status === "completed") {
|
|
5912
|
-
this.emitFinalAgentMessage(completedTurn);
|
|
5913
6184
|
}
|
|
6185
|
+
throw error;
|
|
5914
6186
|
} finally {
|
|
5915
6187
|
this.activeTurnId = null;
|
|
5916
|
-
if (host?.client.isDisposed) {
|
|
5917
|
-
this.currentThreadId = null;
|
|
5918
|
-
void this.onSaveSessionId(null).catch(() => {
|
|
5919
|
-
});
|
|
5920
|
-
}
|
|
5921
6188
|
await this.onTurnComplete();
|
|
5922
6189
|
}
|
|
5923
6190
|
}
|
|
6191
|
+
async executeAspTurn(request, recordUserMessage) {
|
|
6192
|
+
const host = await getCodexAspHost();
|
|
6193
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
6194
|
+
await this.refreshQuotaSnapshot(host);
|
|
6195
|
+
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
6196
|
+
recordUserMessage();
|
|
6197
|
+
this.emitQuotaStatus(this.quotaStatus.latestSnapshot, true);
|
|
6198
|
+
return;
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
|
|
6202
|
+
const threadId = await this.ensureThread(host, request, developerInstructions);
|
|
6203
|
+
recordUserMessage();
|
|
6204
|
+
let completedTurn;
|
|
6205
|
+
try {
|
|
6206
|
+
completedTurn = await this.runTurn(host, threadId, request, developerInstructions);
|
|
6207
|
+
} catch (error) {
|
|
6208
|
+
await this.refreshQuotaSnapshot(host);
|
|
6209
|
+
if (this.quotaStatus.blocked) {
|
|
6210
|
+
return;
|
|
6211
|
+
}
|
|
6212
|
+
throw error;
|
|
6213
|
+
}
|
|
6214
|
+
if (completedTurn.status === "failed") {
|
|
6215
|
+
await this.refreshQuotaSnapshot(host);
|
|
6216
|
+
if (this.quotaStatus.blocked) {
|
|
6217
|
+
return;
|
|
6218
|
+
}
|
|
6219
|
+
throw new Error(this.formatTurnFailure(completedTurn));
|
|
6220
|
+
}
|
|
6221
|
+
if (completedTurn.status === "completed") {
|
|
6222
|
+
this.emitTurnCompletedItems(completedTurn);
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
async ensureThread(host, request, developerInstructions) {
|
|
6226
|
+
if (this.currentThreadId) {
|
|
6227
|
+
if (!this.threadAttached) {
|
|
6228
|
+
const response = await host.client.request(
|
|
6229
|
+
THREAD_RESUME_METHOD,
|
|
6230
|
+
await this.buildThreadResumeParams(this.currentThreadId, request, developerInstructions)
|
|
6231
|
+
);
|
|
6232
|
+
this.currentThreadId = response.thread.id;
|
|
6233
|
+
this.threadAttached = true;
|
|
6234
|
+
this.seedHistoryFromThread(response.thread);
|
|
6235
|
+
await this.onSaveSessionId(this.currentThreadId);
|
|
6236
|
+
}
|
|
6237
|
+
return this.currentThreadId;
|
|
6238
|
+
}
|
|
6239
|
+
const threadStartResponse = await host.client.request(
|
|
6240
|
+
THREAD_START_METHOD,
|
|
6241
|
+
await this.buildThreadStartParams(request, developerInstructions)
|
|
6242
|
+
);
|
|
6243
|
+
this.currentThreadId = threadStartResponse.thread.id;
|
|
6244
|
+
this.threadAttached = true;
|
|
6245
|
+
await this.onSaveSessionId(this.currentThreadId);
|
|
6246
|
+
return this.currentThreadId;
|
|
6247
|
+
}
|
|
5924
6248
|
async buildThreadStartParams(request, developerInstructions) {
|
|
5925
6249
|
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
5926
6250
|
return {
|
|
@@ -5934,30 +6258,61 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
5934
6258
|
persistExtendedHistory: false
|
|
5935
6259
|
};
|
|
5936
6260
|
}
|
|
5937
|
-
|
|
5938
|
-
const
|
|
5939
|
-
const model = request.model ?? DEFAULT_MODEL2;
|
|
6261
|
+
async buildThreadResumeParams(threadId, request, developerInstructions) {
|
|
6262
|
+
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
5940
6263
|
return {
|
|
5941
6264
|
threadId,
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
6265
|
+
model: request.model ?? DEFAULT_MODEL2,
|
|
6266
|
+
cwd: this.workingDirectory,
|
|
6267
|
+
runtimeWorkspaceRoots: additionalDirectories,
|
|
6268
|
+
sandbox: "danger-full-access",
|
|
6269
|
+
developerInstructions: developerInstructions ?? null,
|
|
6270
|
+
config: { web_search: "live" },
|
|
6271
|
+
excludeTurns: false,
|
|
6272
|
+
persistExtendedHistory: false
|
|
6273
|
+
};
|
|
6274
|
+
}
|
|
6275
|
+
async buildTurnStartParams(threadId, request, developerInstructions) {
|
|
6276
|
+
const effort = toReasoningEffort(request.thinkingLevel);
|
|
6277
|
+
const model = request.model ?? DEFAULT_MODEL2;
|
|
6278
|
+
const { input, tempImagePaths } = await this.buildTurnInput(request);
|
|
6279
|
+
return {
|
|
6280
|
+
params: {
|
|
6281
|
+
threadId,
|
|
6282
|
+
input,
|
|
6283
|
+
model,
|
|
6284
|
+
...effort ? { effort } : {},
|
|
6285
|
+
...developerInstructions ? {
|
|
6286
|
+
collaborationMode: {
|
|
6287
|
+
mode: "default",
|
|
6288
|
+
settings: {
|
|
6289
|
+
model,
|
|
6290
|
+
reasoning_effort: effort ?? null,
|
|
6291
|
+
developer_instructions: developerInstructions
|
|
6292
|
+
}
|
|
5956
6293
|
}
|
|
5957
|
-
}
|
|
5958
|
-
}
|
|
6294
|
+
} : {}
|
|
6295
|
+
},
|
|
6296
|
+
tempImagePaths
|
|
5959
6297
|
};
|
|
5960
6298
|
}
|
|
6299
|
+
async buildTurnInput(request) {
|
|
6300
|
+
const input = [{
|
|
6301
|
+
type: "text",
|
|
6302
|
+
text: request.message,
|
|
6303
|
+
text_elements: []
|
|
6304
|
+
}];
|
|
6305
|
+
if (!request.images || request.images.length === 0) {
|
|
6306
|
+
return { input, tempImagePaths: [] };
|
|
6307
|
+
}
|
|
6308
|
+
const normalizedImages = await normalizeImages(request.images);
|
|
6309
|
+
const tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
|
|
6310
|
+
input.push(...tempImagePaths.map((path4) => ({
|
|
6311
|
+
type: "localImage",
|
|
6312
|
+
path: path4
|
|
6313
|
+
})));
|
|
6314
|
+
return { input, tempImagePaths };
|
|
6315
|
+
}
|
|
5961
6316
|
async runTurn(host, threadId, request, developerInstructions) {
|
|
5962
6317
|
let resolveCompleted;
|
|
5963
6318
|
const completed = new Promise((resolve3) => {
|
|
@@ -5973,15 +6328,62 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
5973
6328
|
let observedTurnId = null;
|
|
5974
6329
|
const completedItems = [];
|
|
5975
6330
|
const agentMessageDeltas = /* @__PURE__ */ new Map();
|
|
6331
|
+
const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
|
|
6332
|
+
const model = request.model ?? DEFAULT_MODEL2;
|
|
6333
|
+
let tempImagePaths = [];
|
|
6334
|
+
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
6335
|
+
const matchesTurn = (notificationThreadId, notificationTurnId) => notificationThreadId === threadId && (!observedTurnId || notificationTurnId === null || notificationTurnId === observedTurnId);
|
|
5976
6336
|
const onNotification = (notification) => {
|
|
6337
|
+
if (notification.method === ACCOUNT_RATE_LIMITS_UPDATED_METHOD) {
|
|
6338
|
+
this.handleRateLimits(notification.params.rateLimits);
|
|
6339
|
+
return;
|
|
6340
|
+
}
|
|
6341
|
+
if (notification.method === THREAD_TOKEN_USAGE_UPDATED_METHOD) {
|
|
6342
|
+
if (notification.params.threadId === threadId) {
|
|
6343
|
+
this.emitCodexTokenUsage(notification.params.tokenUsage, model);
|
|
6344
|
+
}
|
|
6345
|
+
return;
|
|
6346
|
+
}
|
|
6347
|
+
if (notification.method === THREAD_COMPACTED_METHOD) {
|
|
6348
|
+
if (matchesTurn(notification.params.threadId, notification.params.turnId)) {
|
|
6349
|
+
this.setCompacting(false);
|
|
6350
|
+
}
|
|
6351
|
+
return;
|
|
6352
|
+
}
|
|
6353
|
+
if (notification.method === TURN_STARTED_METHOD) {
|
|
6354
|
+
if (notification.params.threadId !== threadId) return;
|
|
6355
|
+
observedTurnId = notification.params.turn.id;
|
|
6356
|
+
this.activeTurnId = notification.params.turn.id;
|
|
6357
|
+
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
6358
|
+
return;
|
|
6359
|
+
}
|
|
6360
|
+
if (notification.method === TURN_PLAN_UPDATED_METHOD) {
|
|
6361
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6362
|
+
this.emitPlanUpdate(notification.params.plan);
|
|
6363
|
+
linearForwarder.sendPlan(extractPlanFromCodexAspNotification(notification));
|
|
6364
|
+
return;
|
|
6365
|
+
}
|
|
6366
|
+
if (notification.method === ITEM_STARTED_METHOD) {
|
|
6367
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6368
|
+
if (notification.params.item.type === "contextCompaction") {
|
|
6369
|
+
this.setCompacting(true);
|
|
6370
|
+
}
|
|
6371
|
+
this.emitThreadItemLifecycle(notification.params.item, "started");
|
|
6372
|
+
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
6373
|
+
return;
|
|
6374
|
+
}
|
|
5977
6375
|
if (notification.method === ITEM_COMPLETED_METHOD) {
|
|
5978
|
-
if (notification.params.threadId
|
|
5979
|
-
|
|
6376
|
+
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
6377
|
+
completedItems.push(notification.params.item);
|
|
6378
|
+
if (notification.params.item.type === "contextCompaction") {
|
|
6379
|
+
this.setCompacting(false);
|
|
5980
6380
|
}
|
|
6381
|
+
this.emitThreadItemLifecycle(notification.params.item, "completed");
|
|
6382
|
+
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
5981
6383
|
return;
|
|
5982
6384
|
}
|
|
5983
6385
|
if (notification.method === AGENT_MESSAGE_DELTA_METHOD) {
|
|
5984
|
-
if (notification.params.threadId
|
|
6386
|
+
if (matchesTurn(notification.params.threadId, notification.params.turnId)) {
|
|
5985
6387
|
const currentText = agentMessageDeltas.get(notification.params.itemId) ?? "";
|
|
5986
6388
|
agentMessageDeltas.set(notification.params.itemId, currentText + notification.params.delta);
|
|
5987
6389
|
}
|
|
@@ -6023,10 +6425,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6023
6425
|
host.client.respond(serverRequest.id, { decision: "accept" });
|
|
6024
6426
|
};
|
|
6025
6427
|
const onDispose = (reason) => {
|
|
6026
|
-
this.
|
|
6428
|
+
this.threadAttached = false;
|
|
6027
6429
|
this.activeTurnId = null;
|
|
6028
|
-
void this.onSaveSessionId(null).catch(() => {
|
|
6029
|
-
});
|
|
6030
6430
|
const turnLabel = observedTurnId ? ` ${observedTurnId}` : "";
|
|
6031
6431
|
rejectDisposed(new Error(`Codex ASP client disposed before turn${turnLabel} completed: ${reason.message}`));
|
|
6032
6432
|
};
|
|
@@ -6034,40 +6434,196 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6034
6434
|
host.client.on("serverRequest", onServerRequest);
|
|
6035
6435
|
host.client.on("dispose", onDispose);
|
|
6036
6436
|
try {
|
|
6437
|
+
const { params, tempImagePaths: turnTempImagePaths } = await this.buildTurnStartParams(threadId, request, developerInstructions);
|
|
6438
|
+
tempImagePaths = turnTempImagePaths;
|
|
6037
6439
|
const turnStartResponse = await host.client.request(
|
|
6038
6440
|
TURN_START_METHOD,
|
|
6039
|
-
|
|
6441
|
+
params
|
|
6040
6442
|
);
|
|
6041
6443
|
observedTurnId = turnStartResponse.turn.id;
|
|
6042
6444
|
this.activeTurnId = turnStartResponse.turn.id;
|
|
6043
|
-
|
|
6445
|
+
const turn = await Promise.race([completed, disposed]);
|
|
6446
|
+
linearForwarder.flushThoughtAsResponse();
|
|
6447
|
+
return turn;
|
|
6044
6448
|
} finally {
|
|
6045
6449
|
host.client.off("notification", onNotification);
|
|
6046
6450
|
host.client.off("serverRequest", onServerRequest);
|
|
6047
6451
|
host.client.off("dispose", onDispose);
|
|
6452
|
+
await removeTempImageFiles(tempImagePaths);
|
|
6453
|
+
if (host.client.isDisposed) {
|
|
6454
|
+
this.threadAttached = false;
|
|
6455
|
+
}
|
|
6048
6456
|
}
|
|
6049
6457
|
}
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6458
|
+
emitThreadItemLifecycle(item, lifecycle) {
|
|
6459
|
+
const stageKey = `${lifecycle}:${item.id}`;
|
|
6460
|
+
if (this.emittedItemStages.has(stageKey)) {
|
|
6461
|
+
return;
|
|
6462
|
+
}
|
|
6463
|
+
if (lifecycle === "completed" && !this.emittedItemStages.has(`started:${item.id}`)) {
|
|
6464
|
+
this.emittedItemStages.add(`started:${item.id}`);
|
|
6465
|
+
this.recordAndEmitDrafts(this.itemToAgentEventDrafts(item, "started"));
|
|
6466
|
+
}
|
|
6467
|
+
this.emittedItemStages.add(stageKey);
|
|
6468
|
+
this.recordAndEmitDrafts(this.itemToAgentEventDrafts(item, lifecycle));
|
|
6469
|
+
}
|
|
6470
|
+
emitTurnCompletedItems(turn) {
|
|
6471
|
+
for (const item of turn.items) {
|
|
6472
|
+
this.emitThreadItemLifecycle(item, "completed");
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
emitPlanUpdate(plan) {
|
|
6476
|
+
if (plan.length === 0) return;
|
|
6477
|
+
this.recordAndEmitDrafts([{
|
|
6478
|
+
type: "response_item",
|
|
6479
|
+
payload: {
|
|
6480
|
+
type: "function_call",
|
|
6481
|
+
name: "update_plan",
|
|
6482
|
+
call_id: `plan-${Date.now()}`,
|
|
6483
|
+
arguments: JSON.stringify({ plan })
|
|
6057
6484
|
}
|
|
6485
|
+
}]);
|
|
6486
|
+
}
|
|
6487
|
+
itemToAgentEventDrafts(item, lifecycle) {
|
|
6488
|
+
if (item.type === "agentMessage") {
|
|
6489
|
+
if (lifecycle !== "completed" || !item.text) return [];
|
|
6490
|
+
return [{
|
|
6491
|
+
type: "response_item",
|
|
6492
|
+
payload: {
|
|
6493
|
+
type: "message",
|
|
6494
|
+
role: "assistant",
|
|
6495
|
+
content: [{
|
|
6496
|
+
type: "output_text",
|
|
6497
|
+
text: item.text
|
|
6498
|
+
}]
|
|
6499
|
+
}
|
|
6500
|
+
}];
|
|
6058
6501
|
}
|
|
6059
|
-
if (
|
|
6060
|
-
return;
|
|
6502
|
+
if (item.type === "reasoning") {
|
|
6503
|
+
if (lifecycle !== "completed") return [];
|
|
6504
|
+
const text = [...item.summary, ...item.content].filter(Boolean).join("\n").trim();
|
|
6505
|
+
if (!text) return [];
|
|
6506
|
+
return [{
|
|
6507
|
+
type: "event_msg",
|
|
6508
|
+
payload: {
|
|
6509
|
+
type: "agent_reasoning",
|
|
6510
|
+
text
|
|
6511
|
+
}
|
|
6512
|
+
}];
|
|
6513
|
+
}
|
|
6514
|
+
if (item.type === "commandExecution") {
|
|
6515
|
+
if (lifecycle === "started") {
|
|
6516
|
+
return [{
|
|
6517
|
+
type: "response_item",
|
|
6518
|
+
payload: {
|
|
6519
|
+
type: "function_call",
|
|
6520
|
+
name: "exec_command",
|
|
6521
|
+
call_id: item.id,
|
|
6522
|
+
arguments: JSON.stringify({ cmd: item.command })
|
|
6523
|
+
}
|
|
6524
|
+
}];
|
|
6525
|
+
}
|
|
6526
|
+
return [{
|
|
6527
|
+
type: "response_item",
|
|
6528
|
+
payload: {
|
|
6529
|
+
type: "function_call_output",
|
|
6530
|
+
call_id: item.id,
|
|
6531
|
+
output: this.formatCommandOutput(item)
|
|
6532
|
+
}
|
|
6533
|
+
}];
|
|
6534
|
+
}
|
|
6535
|
+
if (item.type === "fileChange") {
|
|
6536
|
+
if (lifecycle === "started") {
|
|
6537
|
+
return [{
|
|
6538
|
+
type: "response_item",
|
|
6539
|
+
payload: {
|
|
6540
|
+
type: "custom_tool_call",
|
|
6541
|
+
name: "apply_patch",
|
|
6542
|
+
call_id: item.id,
|
|
6543
|
+
input: this.fileChangeToPatchInput(item),
|
|
6544
|
+
status: "in_progress"
|
|
6545
|
+
}
|
|
6546
|
+
}];
|
|
6547
|
+
}
|
|
6548
|
+
return [{
|
|
6549
|
+
type: "response_item",
|
|
6550
|
+
payload: {
|
|
6551
|
+
type: "custom_tool_call_output",
|
|
6552
|
+
call_id: item.id,
|
|
6553
|
+
output: JSON.stringify({
|
|
6554
|
+
output: item.status,
|
|
6555
|
+
metadata: { exit_code: item.status === "completed" ? 0 : 1 }
|
|
6556
|
+
})
|
|
6557
|
+
}
|
|
6558
|
+
}];
|
|
6559
|
+
}
|
|
6560
|
+
if (item.type === "mcpToolCall" || item.type === "dynamicToolCall" || item.type === "webSearch") {
|
|
6561
|
+
const tool2 = item.type === "webSearch" ? "web_search" : item.tool;
|
|
6562
|
+
const input = item.type === "webSearch" ? item.query : JSON.stringify(item.arguments);
|
|
6563
|
+
if (lifecycle === "started") {
|
|
6564
|
+
return [{
|
|
6565
|
+
type: "response_item",
|
|
6566
|
+
payload: {
|
|
6567
|
+
type: "custom_tool_call",
|
|
6568
|
+
name: tool2,
|
|
6569
|
+
call_id: item.id,
|
|
6570
|
+
input,
|
|
6571
|
+
status: "in_progress"
|
|
6572
|
+
}
|
|
6573
|
+
}];
|
|
6574
|
+
}
|
|
6575
|
+
const failed = item.type === "mcpToolCall" && item.status === "failed" || item.type === "dynamicToolCall" && item.success === false;
|
|
6576
|
+
return [{
|
|
6577
|
+
type: "response_item",
|
|
6578
|
+
payload: {
|
|
6579
|
+
type: "custom_tool_call_output",
|
|
6580
|
+
call_id: item.id,
|
|
6581
|
+
output: JSON.stringify({
|
|
6582
|
+
output: failed ? "Failed" : "Done",
|
|
6583
|
+
metadata: { exit_code: failed ? 1 : 0 }
|
|
6584
|
+
})
|
|
6585
|
+
}
|
|
6586
|
+
}];
|
|
6587
|
+
}
|
|
6588
|
+
return [];
|
|
6589
|
+
}
|
|
6590
|
+
threadToHistoryEvents(thread) {
|
|
6591
|
+
const events = [];
|
|
6592
|
+
for (const turn of thread.turns) {
|
|
6593
|
+
const startedAt = timestampFromSeconds(turn.startedAt);
|
|
6594
|
+
const completedAt = timestampFromSeconds(turn.completedAt ?? turn.startedAt);
|
|
6595
|
+
for (const item of turn.items) {
|
|
6596
|
+
if (item.type === "userMessage") {
|
|
6597
|
+
const message = item.content.filter((input) => input.type === "text").map((input) => input.text).join("\n");
|
|
6598
|
+
if (message) {
|
|
6599
|
+
events.push({
|
|
6600
|
+
timestamp: startedAt,
|
|
6601
|
+
type: "event_msg",
|
|
6602
|
+
payload: { type: "user_message", message }
|
|
6603
|
+
});
|
|
6604
|
+
}
|
|
6605
|
+
continue;
|
|
6606
|
+
}
|
|
6607
|
+
for (const draft of this.itemToAgentEventDrafts(item, "started")) {
|
|
6608
|
+
events.push({ timestamp: startedAt, ...draft });
|
|
6609
|
+
}
|
|
6610
|
+
for (const draft of this.itemToAgentEventDrafts(item, "completed")) {
|
|
6611
|
+
events.push({ timestamp: completedAt, ...draft });
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
return events;
|
|
6616
|
+
}
|
|
6617
|
+
seedHistoryFromThread(thread) {
|
|
6618
|
+
const events = this.threadToHistoryEvents(thread);
|
|
6619
|
+
if (events.length === 0) return;
|
|
6620
|
+
this.historyEvents.splice(0, this.historyEvents.length, ...events);
|
|
6621
|
+
}
|
|
6622
|
+
recordAndEmitDrafts(drafts) {
|
|
6623
|
+
for (const draft of drafts) {
|
|
6624
|
+
const event = this.recordHistoryEvent(draft.type, draft.payload);
|
|
6625
|
+
this.onEvent(event);
|
|
6061
6626
|
}
|
|
6062
|
-
const event = this.recordHistoryEvent("response_item", {
|
|
6063
|
-
type: "message",
|
|
6064
|
-
role: "assistant",
|
|
6065
|
-
content: [{
|
|
6066
|
-
type: "output_text",
|
|
6067
|
-
text
|
|
6068
|
-
}]
|
|
6069
|
-
});
|
|
6070
|
-
this.onEvent(event);
|
|
6071
6627
|
}
|
|
6072
6628
|
recordHistoryEvent(type, payload) {
|
|
6073
6629
|
const event = {
|
|
@@ -6078,6 +6634,87 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6078
6634
|
this.historyEvents.push(event);
|
|
6079
6635
|
return event;
|
|
6080
6636
|
}
|
|
6637
|
+
handleRateLimits(rateLimits) {
|
|
6638
|
+
const snapshot = extractRateLimitsSnapshot(rateLimits);
|
|
6639
|
+
if (snapshot) {
|
|
6640
|
+
this.emitQuotaStatus(snapshot);
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
async refreshQuotaSnapshot(host) {
|
|
6644
|
+
try {
|
|
6645
|
+
const response = await host.client.request(
|
|
6646
|
+
ACCOUNT_RATE_LIMITS_READ_METHOD,
|
|
6647
|
+
void 0
|
|
6648
|
+
);
|
|
6649
|
+
this.handleRateLimits(response.rateLimits);
|
|
6650
|
+
} catch {
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
emitQuotaStatus(snapshot, force = false) {
|
|
6654
|
+
const event = this.quotaStatus.apply(snapshot, force);
|
|
6655
|
+
if (!event) return;
|
|
6656
|
+
this.historyEvents.push(event);
|
|
6657
|
+
this.onEvent(event);
|
|
6658
|
+
}
|
|
6659
|
+
emitCodexTokenUsage(tokenUsage, model) {
|
|
6660
|
+
const payload = buildCodexTokenUsageContextUsagePayload({
|
|
6661
|
+
model,
|
|
6662
|
+
modelContextWindow: tokenUsage.modelContextWindow,
|
|
6663
|
+
last: {
|
|
6664
|
+
inputTokens: tokenUsage.last.inputTokens,
|
|
6665
|
+
outputTokens: tokenUsage.last.outputTokens,
|
|
6666
|
+
totalTokens: tokenUsage.last.totalTokens,
|
|
6667
|
+
cachedInputTokens: tokenUsage.last.cachedInputTokens,
|
|
6668
|
+
reasoningOutputTokens: tokenUsage.last.reasoningOutputTokens
|
|
6669
|
+
},
|
|
6670
|
+
total: {
|
|
6671
|
+
totalTokens: tokenUsage.total.totalTokens
|
|
6672
|
+
},
|
|
6673
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6674
|
+
});
|
|
6675
|
+
const event = this.emitContextUsage(payload);
|
|
6676
|
+
this.historyEvents.push(event);
|
|
6677
|
+
}
|
|
6678
|
+
formatCommandOutput(item) {
|
|
6679
|
+
const exitCode = item.exitCode ?? (item.status === "completed" ? 0 : 1);
|
|
6680
|
+
const output = item.aggregatedOutput ?? "";
|
|
6681
|
+
return output ? `Exit code: ${exitCode}
|
|
6682
|
+
${output}` : `Exit code: ${exitCode}`;
|
|
6683
|
+
}
|
|
6684
|
+
fileChangeToPatchInput(item) {
|
|
6685
|
+
const lines = ["*** Begin Patch"];
|
|
6686
|
+
for (const change of item.changes) {
|
|
6687
|
+
if (change.kind.type === "add") {
|
|
6688
|
+
lines.push(`*** Add File: ${change.path}`);
|
|
6689
|
+
} else if (change.kind.type === "delete") {
|
|
6690
|
+
lines.push(`*** Delete File: ${change.path}`);
|
|
6691
|
+
} else {
|
|
6692
|
+
lines.push(`*** Update File: ${change.path}`);
|
|
6693
|
+
if (change.kind.move_path) {
|
|
6694
|
+
lines.push(`*** Move to: ${change.kind.move_path}`);
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6697
|
+
if (change.diff.trim().length > 0) {
|
|
6698
|
+
lines.push(change.diff);
|
|
6699
|
+
}
|
|
6700
|
+
}
|
|
6701
|
+
lines.push("*** End Patch");
|
|
6702
|
+
return lines.join("\n");
|
|
6703
|
+
}
|
|
6704
|
+
formatTurnFailure(turn) {
|
|
6705
|
+
const turnError = turn.error;
|
|
6706
|
+
if (!turnError) {
|
|
6707
|
+
return "Codex ASP turn failed without error details";
|
|
6708
|
+
}
|
|
6709
|
+
const parts = [`Codex ASP turn failed: ${turnError.message}`];
|
|
6710
|
+
if (turnError.codexErrorInfo !== null) {
|
|
6711
|
+
parts.push(`codexErrorInfo=${JSON.stringify(turnError.codexErrorInfo)}`);
|
|
6712
|
+
}
|
|
6713
|
+
if (turnError.additionalDetails) {
|
|
6714
|
+
parts.push(`details=${turnError.additionalDetails}`);
|
|
6715
|
+
}
|
|
6716
|
+
return parts.join(" ");
|
|
6717
|
+
}
|
|
6081
6718
|
};
|
|
6082
6719
|
|
|
6083
6720
|
// src/managers/relay-tools.ts
|
|
@@ -6686,12 +7323,12 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
6686
7323
|
};
|
|
6687
7324
|
|
|
6688
7325
|
// 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 =
|
|
7326
|
+
var ENGINE_DIR2 = join15(homedir13(), ".replicas", "engine");
|
|
7327
|
+
var CHATS_FILE = join15(ENGINE_DIR2, "chats.json");
|
|
7328
|
+
var CLAUDE_HISTORY_DIR = join15(ENGINE_DIR2, "claude-histories");
|
|
7329
|
+
var RELAY_HISTORY_DIR = join15(ENGINE_DIR2, "relay-histories");
|
|
7330
|
+
var CHAT_SENDERS_DIR = join15(ENGINE_DIR2, "chat-senders");
|
|
7331
|
+
var CODEX_AUTH_PATH2 = join15(homedir13(), ".codex", "auth.json");
|
|
6695
7332
|
function isChatMessageSender(value) {
|
|
6696
7333
|
if (!isRecord3(value)) return false;
|
|
6697
7334
|
return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
@@ -6714,10 +7351,10 @@ var ChatService = class {
|
|
|
6714
7351
|
chats = /* @__PURE__ */ new Map();
|
|
6715
7352
|
writeChain = Promise.resolve();
|
|
6716
7353
|
async initialize() {
|
|
6717
|
-
await
|
|
6718
|
-
await
|
|
6719
|
-
await
|
|
6720
|
-
await
|
|
7354
|
+
await mkdir11(ENGINE_DIR2, { recursive: true });
|
|
7355
|
+
await mkdir11(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
7356
|
+
await mkdir11(RELAY_HISTORY_DIR, { recursive: true });
|
|
7357
|
+
await mkdir11(CHAT_SENDERS_DIR, { recursive: true });
|
|
6721
7358
|
const persisted = await this.loadChats();
|
|
6722
7359
|
for (const chat of persisted) {
|
|
6723
7360
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -6812,7 +7449,7 @@ var ChatService = class {
|
|
|
6812
7449
|
};
|
|
6813
7450
|
}
|
|
6814
7451
|
senderFilePath(chatId) {
|
|
6815
|
-
return
|
|
7452
|
+
return join15(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
6816
7453
|
}
|
|
6817
7454
|
async appendSender(chatId, sender) {
|
|
6818
7455
|
try {
|
|
@@ -6929,7 +7566,7 @@ var ChatService = class {
|
|
|
6929
7566
|
async deleteHistoryFile(persisted) {
|
|
6930
7567
|
if (persisted.provider === "claude" || persisted.provider === "relay") {
|
|
6931
7568
|
const dir = persisted.provider === "claude" ? CLAUDE_HISTORY_DIR : RELAY_HISTORY_DIR;
|
|
6932
|
-
await rm(
|
|
7569
|
+
await rm(join15(dir, `${persisted.id}.jsonl`), { force: true });
|
|
6933
7570
|
}
|
|
6934
7571
|
await rm(this.senderFilePath(persisted.id), { force: true });
|
|
6935
7572
|
}
|
|
@@ -6977,7 +7614,7 @@ var ChatService = class {
|
|
|
6977
7614
|
if (persisted.provider === "claude") {
|
|
6978
7615
|
provider = new ClaudeManager({
|
|
6979
7616
|
workingDirectory: this.workingDirectory,
|
|
6980
|
-
historyFilePath:
|
|
7617
|
+
historyFilePath: join15(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
6981
7618
|
initialSessionId: persisted.providerSessionId,
|
|
6982
7619
|
onSaveSessionId: saveSession,
|
|
6983
7620
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -6986,7 +7623,7 @@ var ChatService = class {
|
|
|
6986
7623
|
} else if (persisted.provider === "relay") {
|
|
6987
7624
|
provider = new RelayManager({
|
|
6988
7625
|
workingDirectory: this.workingDirectory,
|
|
6989
|
-
historyFilePath:
|
|
7626
|
+
historyFilePath: join15(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
6990
7627
|
initialSessionId: persisted.providerSessionId,
|
|
6991
7628
|
onSaveSessionId: saveSession,
|
|
6992
7629
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -7122,7 +7759,7 @@ var ChatService = class {
|
|
|
7122
7759
|
this.writeChain = this.writeChain.catch(() => {
|
|
7123
7760
|
}).then(async () => {
|
|
7124
7761
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
7125
|
-
await
|
|
7762
|
+
await writeFile9(CHATS_FILE, JSON.stringify(payload, null, 2), "utf-8");
|
|
7126
7763
|
});
|
|
7127
7764
|
await this.writeChain;
|
|
7128
7765
|
}
|
|
@@ -7175,7 +7812,7 @@ var ChatService = class {
|
|
|
7175
7812
|
// src/services/repo-file-service.ts
|
|
7176
7813
|
import { execFile as execFile2 } from "child_process";
|
|
7177
7814
|
import { readFile as readFile9, realpath, stat as stat3 } from "fs/promises";
|
|
7178
|
-
import { join as
|
|
7815
|
+
import { join as join16, resolve, extname } from "path";
|
|
7179
7816
|
var CACHE_TTL_MS = 3e4;
|
|
7180
7817
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
7181
7818
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -7335,7 +7972,7 @@ var RepoFileService = class {
|
|
|
7335
7972
|
const repo = repos.find((r) => r.name === repoName);
|
|
7336
7973
|
if (!repo) return null;
|
|
7337
7974
|
try {
|
|
7338
|
-
const fullPath = await realpath(resolve(
|
|
7975
|
+
const fullPath = await realpath(resolve(join16(repo.path, filePath)));
|
|
7339
7976
|
const repoRoot = await realpath(repo.path);
|
|
7340
7977
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
7341
7978
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -7447,15 +8084,15 @@ var RepoFileService = class {
|
|
|
7447
8084
|
import { Hono } from "hono";
|
|
7448
8085
|
import { z as z2 } from "zod";
|
|
7449
8086
|
import { readdir as readdir6, stat as stat4, readFile as readFile13 } from "fs/promises";
|
|
7450
|
-
import { join as
|
|
8087
|
+
import { join as join20, resolve as resolve2 } from "path";
|
|
7451
8088
|
|
|
7452
8089
|
// src/services/plan-service.ts
|
|
7453
8090
|
import { readdir as readdir4, readFile as readFile10 } from "fs/promises";
|
|
7454
|
-
import { homedir as
|
|
7455
|
-
import { basename, join as
|
|
8091
|
+
import { homedir as homedir14 } from "os";
|
|
8092
|
+
import { basename, join as join17 } from "path";
|
|
7456
8093
|
var PLAN_DIRECTORIES = [
|
|
7457
|
-
|
|
7458
|
-
|
|
8094
|
+
join17(homedir14(), ".claude", "plans"),
|
|
8095
|
+
join17(homedir14(), ".replicas", "plans")
|
|
7459
8096
|
];
|
|
7460
8097
|
function isMarkdownFile(filename) {
|
|
7461
8098
|
return filename.toLowerCase().endsWith(".md");
|
|
@@ -7489,7 +8126,7 @@ var PlanService = class {
|
|
|
7489
8126
|
return null;
|
|
7490
8127
|
}
|
|
7491
8128
|
for (const directory of PLAN_DIRECTORIES) {
|
|
7492
|
-
const filePath =
|
|
8129
|
+
const filePath = join17(directory, safeFilename);
|
|
7493
8130
|
try {
|
|
7494
8131
|
const content = await readFile10(filePath, "utf-8");
|
|
7495
8132
|
return { filename: safeFilename, content };
|
|
@@ -7505,14 +8142,14 @@ var planService = new PlanService();
|
|
|
7505
8142
|
import { spawn as spawn2 } from "child_process";
|
|
7506
8143
|
import { readFile as readFile12 } from "fs/promises";
|
|
7507
8144
|
import { existsSync as existsSync8 } from "fs";
|
|
7508
|
-
import { join as
|
|
8145
|
+
import { join as join19 } from "path";
|
|
7509
8146
|
|
|
7510
8147
|
// src/services/warm-hook-logs-service.ts
|
|
7511
8148
|
import { createHash as createHash2 } from "crypto";
|
|
7512
|
-
import { mkdir as
|
|
7513
|
-
import { homedir as
|
|
7514
|
-
import { join as
|
|
7515
|
-
var LOGS_DIR2 =
|
|
8149
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile10, readdir as readdir5 } from "fs/promises";
|
|
8150
|
+
import { homedir as homedir15 } from "os";
|
|
8151
|
+
import { join as join18 } from "path";
|
|
8152
|
+
var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
|
|
7516
8153
|
function sanitizeFilename2(name) {
|
|
7517
8154
|
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
7518
8155
|
const hash = createHash2("sha256").update(name).digest("hex").slice(0, 8);
|
|
@@ -7533,7 +8170,7 @@ function withPreview2(stored) {
|
|
|
7533
8170
|
}
|
|
7534
8171
|
var WarmHookLogsService = class {
|
|
7535
8172
|
async ensureDir() {
|
|
7536
|
-
await
|
|
8173
|
+
await mkdir12(LOGS_DIR2, { recursive: true });
|
|
7537
8174
|
}
|
|
7538
8175
|
async saveGlobalHookLog(entry) {
|
|
7539
8176
|
await this.ensureDir();
|
|
@@ -7542,7 +8179,7 @@ var WarmHookLogsService = class {
|
|
|
7542
8179
|
hookName: "organization",
|
|
7543
8180
|
...entry
|
|
7544
8181
|
};
|
|
7545
|
-
await
|
|
8182
|
+
await writeFile10(join18(LOGS_DIR2, globalFilename()), `${JSON.stringify(log, null, 2)}
|
|
7546
8183
|
`, "utf-8");
|
|
7547
8184
|
}
|
|
7548
8185
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -7552,7 +8189,7 @@ var WarmHookLogsService = class {
|
|
|
7552
8189
|
hookName: "environment",
|
|
7553
8190
|
...entry
|
|
7554
8191
|
};
|
|
7555
|
-
await
|
|
8192
|
+
await writeFile10(join18(LOGS_DIR2, environmentFilename()), `${JSON.stringify(log, null, 2)}
|
|
7556
8193
|
`, "utf-8");
|
|
7557
8194
|
}
|
|
7558
8195
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -7562,7 +8199,7 @@ var WarmHookLogsService = class {
|
|
|
7562
8199
|
hookName: repoName,
|
|
7563
8200
|
...entry
|
|
7564
8201
|
};
|
|
7565
|
-
await
|
|
8202
|
+
await writeFile10(join18(LOGS_DIR2, repoFilename2(repoName)), `${JSON.stringify(log, null, 2)}
|
|
7566
8203
|
`, "utf-8");
|
|
7567
8204
|
}
|
|
7568
8205
|
async getAllLogs() {
|
|
@@ -7581,7 +8218,7 @@ var WarmHookLogsService = class {
|
|
|
7581
8218
|
continue;
|
|
7582
8219
|
}
|
|
7583
8220
|
try {
|
|
7584
|
-
const raw = await readFile11(
|
|
8221
|
+
const raw = await readFile11(join18(LOGS_DIR2, file), "utf-8");
|
|
7585
8222
|
const stored = JSON.parse(raw);
|
|
7586
8223
|
logs.push(withPreview2(stored));
|
|
7587
8224
|
} catch {
|
|
@@ -7599,7 +8236,7 @@ var WarmHookLogsService = class {
|
|
|
7599
8236
|
async getFullOutput(hookType, hookName) {
|
|
7600
8237
|
const filename = hookType === "global" ? globalFilename() : hookType === "environment" ? environmentFilename() : repoFilename2(hookName);
|
|
7601
8238
|
try {
|
|
7602
|
-
const raw = await readFile11(
|
|
8239
|
+
const raw = await readFile11(join18(LOGS_DIR2, filename), "utf-8");
|
|
7603
8240
|
const stored = JSON.parse(raw);
|
|
7604
8241
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
7605
8242
|
return null;
|
|
@@ -7618,7 +8255,7 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
7618
8255
|
// src/services/warm-hooks-service.ts
|
|
7619
8256
|
async function readRepoWarmHook(repoPath) {
|
|
7620
8257
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
7621
|
-
const configPath =
|
|
8258
|
+
const configPath = join19(repoPath, filename);
|
|
7622
8259
|
if (!existsSync8(configPath)) {
|
|
7623
8260
|
continue;
|
|
7624
8261
|
}
|
|
@@ -8422,7 +9059,7 @@ function createV1Routes(deps) {
|
|
|
8422
9059
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
8423
9060
|
const sessions = await Promise.all(
|
|
8424
9061
|
logFiles.map(async (filename) => {
|
|
8425
|
-
const filePath =
|
|
9062
|
+
const filePath = join20(LOG_DIR, filename);
|
|
8426
9063
|
const fileStat = await stat4(filePath);
|
|
8427
9064
|
const sessionId = filename.replace(/\.log$/, "");
|
|
8428
9065
|
return {
|