replicas-engine 0.1.276 → 0.1.278
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 +90 -733
- package/package.json +1 -2
package/dist/src/index.js
CHANGED
|
@@ -286,7 +286,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
286
286
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
287
287
|
|
|
288
288
|
// ../shared/src/e2b.ts
|
|
289
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-07-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-07-v3";
|
|
290
290
|
|
|
291
291
|
// ../shared/src/runtime-env.ts
|
|
292
292
|
function parsePosixEnvFile(content) {
|
|
@@ -3236,6 +3236,14 @@ async function getCurrentBranch(cwd) {
|
|
|
3236
3236
|
return null;
|
|
3237
3237
|
}
|
|
3238
3238
|
}
|
|
3239
|
+
async function resolveDiffBase(repoPath, defaultBranch) {
|
|
3240
|
+
const baseBranch = `origin/${defaultBranch}`;
|
|
3241
|
+
try {
|
|
3242
|
+
return await runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3243
|
+
} catch {
|
|
3244
|
+
}
|
|
3245
|
+
return await branchExists(baseBranch, repoPath) ? baseBranch : "HEAD";
|
|
3246
|
+
}
|
|
3239
3247
|
|
|
3240
3248
|
// src/git/service.ts
|
|
3241
3249
|
var FULL_DIFF_MAX_BUFFER = 50 * 1024 * 1024;
|
|
@@ -3450,7 +3458,7 @@ var GitService = class {
|
|
|
3450
3458
|
getGitDiffStats(repoPath, defaultBranch) {
|
|
3451
3459
|
return this.diffStatsInFlight.run(repoPath, async () => {
|
|
3452
3460
|
try {
|
|
3453
|
-
const diffBase = await
|
|
3461
|
+
const diffBase = await resolveDiffBase(repoPath, defaultBranch);
|
|
3454
3462
|
const shortstat = await runGitCommand(["diff", diffBase, "--shortstat", "-M"], repoPath);
|
|
3455
3463
|
let added = 0;
|
|
3456
3464
|
let removed = 0;
|
|
@@ -3468,7 +3476,7 @@ var GitService = class {
|
|
|
3468
3476
|
removed
|
|
3469
3477
|
};
|
|
3470
3478
|
} catch (error) {
|
|
3471
|
-
console.error
|
|
3479
|
+
console.warn(`[GitService] getGitDiffStats failed for ${repoPath} (defaultBranch=${defaultBranch}):`, error instanceof Error ? error.message : error);
|
|
3472
3480
|
return null;
|
|
3473
3481
|
}
|
|
3474
3482
|
});
|
|
@@ -3559,7 +3567,7 @@ var GitService = class {
|
|
|
3559
3567
|
getFullGitDiff(repoPath, defaultBranch) {
|
|
3560
3568
|
return this.fullDiffInFlight.run(repoPath, async () => {
|
|
3561
3569
|
try {
|
|
3562
|
-
const diffBase = await
|
|
3570
|
+
const diffBase = await resolveDiffBase(repoPath, defaultBranch);
|
|
3563
3571
|
const { stdout: trackedDiff } = await execFileAsync("git", ["diff", diffBase, "-M", "-C"], {
|
|
3564
3572
|
cwd: repoPath,
|
|
3565
3573
|
encoding: "utf-8",
|
|
@@ -3580,14 +3588,6 @@ var GitService = class {
|
|
|
3580
3588
|
return "";
|
|
3581
3589
|
}
|
|
3582
3590
|
}
|
|
3583
|
-
async getDiffBase(repoPath, defaultBranch) {
|
|
3584
|
-
const baseBranch = `origin/${defaultBranch}`;
|
|
3585
|
-
try {
|
|
3586
|
-
return await runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3587
|
-
} catch {
|
|
3588
|
-
return baseBranch;
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
3591
|
async getPullRequestUrl(repoName, repoPath, currentBranchArg, persistedRepoStateArg) {
|
|
3592
3592
|
try {
|
|
3593
3593
|
const currentBranch = currentBranchArg ?? await getCurrentBranch(repoPath);
|
|
@@ -3676,9 +3676,7 @@ var GitService = class {
|
|
|
3676
3676
|
this.defaultBranchCache.set(repoPath, fromSymbolicRef);
|
|
3677
3677
|
return fromSymbolicRef;
|
|
3678
3678
|
}
|
|
3679
|
-
|
|
3680
|
-
this.defaultBranchCache.set(repoPath, fallback);
|
|
3681
|
-
return fallback;
|
|
3679
|
+
return "main";
|
|
3682
3680
|
}
|
|
3683
3681
|
async resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3684
3682
|
try {
|
|
@@ -4666,10 +4664,10 @@ async function registerDesktopPreview() {
|
|
|
4666
4664
|
}
|
|
4667
4665
|
|
|
4668
4666
|
// src/services/chat/chat-service.ts
|
|
4669
|
-
import { existsSync as
|
|
4670
|
-
import { appendFile as appendFile5, copyFile, mkdir as
|
|
4671
|
-
import { homedir as
|
|
4672
|
-
import { join as
|
|
4667
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4668
|
+
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile8, rename as rename2, rm } from "fs/promises";
|
|
4669
|
+
import { homedir as homedir12 } from "os";
|
|
4670
|
+
import { join as join14 } from "path";
|
|
4673
4671
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
4674
4672
|
|
|
4675
4673
|
// src/managers/claude-manager.ts
|
|
@@ -4849,212 +4847,6 @@ function mapTodoStatus(status) {
|
|
|
4849
4847
|
}
|
|
4850
4848
|
return "pending";
|
|
4851
4849
|
}
|
|
4852
|
-
function convertCodexEvent(event, linearSessionId) {
|
|
4853
|
-
if (event.type === "turn.started") {
|
|
4854
|
-
return {
|
|
4855
|
-
linearSessionId,
|
|
4856
|
-
content: {
|
|
4857
|
-
type: "thought",
|
|
4858
|
-
body: "Processing..."
|
|
4859
|
-
}
|
|
4860
|
-
};
|
|
4861
|
-
}
|
|
4862
|
-
if (event.type === "item.started") {
|
|
4863
|
-
const item = event.item;
|
|
4864
|
-
if (!item) return null;
|
|
4865
|
-
if (item.type === "agent_message") {
|
|
4866
|
-
const text = "text" in item ? String(item.text || "") : "";
|
|
4867
|
-
if (text) {
|
|
4868
|
-
return {
|
|
4869
|
-
linearSessionId,
|
|
4870
|
-
content: {
|
|
4871
|
-
type: "thought",
|
|
4872
|
-
body: text
|
|
4873
|
-
}
|
|
4874
|
-
};
|
|
4875
|
-
}
|
|
4876
|
-
}
|
|
4877
|
-
if (item.type === "reasoning") {
|
|
4878
|
-
const text = "text" in item ? String(item.text || "") : "";
|
|
4879
|
-
if (text) {
|
|
4880
|
-
return {
|
|
4881
|
-
linearSessionId,
|
|
4882
|
-
content: {
|
|
4883
|
-
type: "thought",
|
|
4884
|
-
body: text
|
|
4885
|
-
}
|
|
4886
|
-
};
|
|
4887
|
-
}
|
|
4888
|
-
}
|
|
4889
|
-
if (item.type === "command_execution") {
|
|
4890
|
-
const command = "command" in item ? String(item.command || "") : "";
|
|
4891
|
-
return {
|
|
4892
|
-
linearSessionId,
|
|
4893
|
-
content: {
|
|
4894
|
-
type: "action",
|
|
4895
|
-
action: "Running command",
|
|
4896
|
-
parameter: command
|
|
4897
|
-
}
|
|
4898
|
-
};
|
|
4899
|
-
}
|
|
4900
|
-
if (item.type === "file_change") {
|
|
4901
|
-
const changes = "changes" in item && Array.isArray(item.changes) ? item.changes : [];
|
|
4902
|
-
const paths = changes.map((c) => c.path || "").filter(Boolean);
|
|
4903
|
-
return {
|
|
4904
|
-
linearSessionId,
|
|
4905
|
-
content: {
|
|
4906
|
-
type: "action",
|
|
4907
|
-
action: "File change",
|
|
4908
|
-
parameter: paths.join(", ") || ""
|
|
4909
|
-
}
|
|
4910
|
-
};
|
|
4911
|
-
}
|
|
4912
|
-
if (item.type === "mcp_tool_call") {
|
|
4913
|
-
const tool2 = "tool" in item ? String(item.tool || "") : "";
|
|
4914
|
-
return {
|
|
4915
|
-
linearSessionId,
|
|
4916
|
-
content: {
|
|
4917
|
-
type: "action",
|
|
4918
|
-
action: tool2 || "MCP tool call",
|
|
4919
|
-
parameter: ""
|
|
4920
|
-
}
|
|
4921
|
-
};
|
|
4922
|
-
}
|
|
4923
|
-
if (item.type === "web_search") {
|
|
4924
|
-
const query2 = "query" in item ? String(item.query || "") : "";
|
|
4925
|
-
return {
|
|
4926
|
-
linearSessionId,
|
|
4927
|
-
content: {
|
|
4928
|
-
type: "action",
|
|
4929
|
-
action: "Web search",
|
|
4930
|
-
parameter: query2
|
|
4931
|
-
}
|
|
4932
|
-
};
|
|
4933
|
-
}
|
|
4934
|
-
if (item.type === "todo_list") {
|
|
4935
|
-
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
4936
|
-
const completed = items.filter((t) => t.completed).length;
|
|
4937
|
-
const total = items.length;
|
|
4938
|
-
const currentTask = items.find((t) => !t.completed);
|
|
4939
|
-
const summary = `${completed}/${total}`;
|
|
4940
|
-
const parameter = currentTask?.text ? `${summary}: ${currentTask.text}` : summary;
|
|
4941
|
-
return {
|
|
4942
|
-
linearSessionId,
|
|
4943
|
-
content: {
|
|
4944
|
-
type: "action",
|
|
4945
|
-
action: "Updating plan",
|
|
4946
|
-
parameter
|
|
4947
|
-
}
|
|
4948
|
-
};
|
|
4949
|
-
}
|
|
4950
|
-
}
|
|
4951
|
-
if (event.type === "item.completed") {
|
|
4952
|
-
const item = event.item;
|
|
4953
|
-
if (!item) return null;
|
|
4954
|
-
if (item.type === "command_execution") {
|
|
4955
|
-
const command = "command" in item ? String(item.command || "") : "";
|
|
4956
|
-
const output = "aggregated_output" in item ? String(item.aggregated_output || "") : "";
|
|
4957
|
-
const exitCode = "exit_code" in item ? item.exit_code : void 0;
|
|
4958
|
-
const result = exitCode !== void 0 ? `Exit code: ${exitCode}` : output || "Done";
|
|
4959
|
-
return {
|
|
4960
|
-
linearSessionId,
|
|
4961
|
-
content: {
|
|
4962
|
-
type: "action",
|
|
4963
|
-
action: "Running command",
|
|
4964
|
-
parameter: command,
|
|
4965
|
-
result
|
|
4966
|
-
}
|
|
4967
|
-
};
|
|
4968
|
-
}
|
|
4969
|
-
if (item.type === "file_change") {
|
|
4970
|
-
const changes = "changes" in item && Array.isArray(item.changes) ? item.changes : [];
|
|
4971
|
-
const paths = changes.map((c) => c.path || "").filter(Boolean);
|
|
4972
|
-
const status = "status" in item ? String(item.status || "") : "";
|
|
4973
|
-
return {
|
|
4974
|
-
linearSessionId,
|
|
4975
|
-
content: {
|
|
4976
|
-
type: "action",
|
|
4977
|
-
action: "File change",
|
|
4978
|
-
parameter: paths.join(", ") || "",
|
|
4979
|
-
result: status === "completed" ? "Done" : status || "Done"
|
|
4980
|
-
}
|
|
4981
|
-
};
|
|
4982
|
-
}
|
|
4983
|
-
if (item.type === "mcp_tool_call") {
|
|
4984
|
-
const tool2 = "tool" in item ? String(item.tool || "") : "";
|
|
4985
|
-
const status = "status" in item ? String(item.status || "") : "";
|
|
4986
|
-
return {
|
|
4987
|
-
linearSessionId,
|
|
4988
|
-
content: {
|
|
4989
|
-
type: "action",
|
|
4990
|
-
action: tool2 || "MCP tool call",
|
|
4991
|
-
parameter: "",
|
|
4992
|
-
result: status === "completed" ? "Done" : status || "Done"
|
|
4993
|
-
}
|
|
4994
|
-
};
|
|
4995
|
-
}
|
|
4996
|
-
if (item.type === "web_search") {
|
|
4997
|
-
const query2 = "query" in item ? String(item.query || "") : "";
|
|
4998
|
-
return {
|
|
4999
|
-
linearSessionId,
|
|
5000
|
-
content: {
|
|
5001
|
-
type: "action",
|
|
5002
|
-
action: "Web search",
|
|
5003
|
-
parameter: query2,
|
|
5004
|
-
result: "Done"
|
|
5005
|
-
}
|
|
5006
|
-
};
|
|
5007
|
-
}
|
|
5008
|
-
if (item.type === "todo_list") {
|
|
5009
|
-
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
5010
|
-
const completed = items.filter((t) => t.completed).length;
|
|
5011
|
-
const total = items.length;
|
|
5012
|
-
const currentTask = items.find((t) => !t.completed);
|
|
5013
|
-
const summary = `${completed}/${total}`;
|
|
5014
|
-
const parameter = currentTask?.text ? `${summary}: ${currentTask.text}` : summary;
|
|
5015
|
-
return {
|
|
5016
|
-
linearSessionId,
|
|
5017
|
-
content: {
|
|
5018
|
-
type: "action",
|
|
5019
|
-
action: "Updating plan",
|
|
5020
|
-
parameter,
|
|
5021
|
-
result: "Done"
|
|
5022
|
-
}
|
|
5023
|
-
};
|
|
5024
|
-
}
|
|
5025
|
-
if (item.type === "agent_message") {
|
|
5026
|
-
const text = "text" in item ? String(item.text || "") : "";
|
|
5027
|
-
if (text) {
|
|
5028
|
-
return {
|
|
5029
|
-
linearSessionId,
|
|
5030
|
-
content: {
|
|
5031
|
-
type: "thought",
|
|
5032
|
-
body: text
|
|
5033
|
-
}
|
|
5034
|
-
};
|
|
5035
|
-
}
|
|
5036
|
-
}
|
|
5037
|
-
}
|
|
5038
|
-
return null;
|
|
5039
|
-
}
|
|
5040
|
-
function extractPlanFromCodexEvent(event) {
|
|
5041
|
-
if (event.type !== "item.started" && event.type !== "item.completed") {
|
|
5042
|
-
return null;
|
|
5043
|
-
}
|
|
5044
|
-
const item = event.item;
|
|
5045
|
-
if (!item || item.type !== "todo_list") {
|
|
5046
|
-
return null;
|
|
5047
|
-
}
|
|
5048
|
-
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
5049
|
-
if (items.length === 0) {
|
|
5050
|
-
return null;
|
|
5051
|
-
}
|
|
5052
|
-
const hasIncomplete = items.some((entry) => !entry.completed);
|
|
5053
|
-
return items.filter((entry) => Boolean(entry.text && entry.text.trim().length > 0)).map((entry) => ({
|
|
5054
|
-
content: entry.text.trim(),
|
|
5055
|
-
status: entry.completed ? "completed" : hasIncomplete ? "inProgress" : "pending"
|
|
5056
|
-
}));
|
|
5057
|
-
}
|
|
5058
4850
|
function codexAspReasoningText(item) {
|
|
5059
4851
|
return [...item.summary, ...item.content].filter(Boolean).join("\n").trim();
|
|
5060
4852
|
}
|
|
@@ -5618,12 +5410,6 @@ function buildCodexAgentEnv() {
|
|
|
5618
5410
|
delete env.GH_CONFIG_DIR;
|
|
5619
5411
|
return env;
|
|
5620
5412
|
}
|
|
5621
|
-
function resolveCodexApiKey() {
|
|
5622
|
-
if (shouldStripOpenAIApiKey()) {
|
|
5623
|
-
return void 0;
|
|
5624
|
-
}
|
|
5625
|
-
return ENGINE_ENV.OPENAI_API_KEY;
|
|
5626
|
-
}
|
|
5627
5413
|
function shouldStripAnthropicApiKey() {
|
|
5628
5414
|
const method = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
|
|
5629
5415
|
return method === "oauth" || method === "bedrock";
|
|
@@ -6746,7 +6532,7 @@ var AspClient = class {
|
|
|
6746
6532
|
// src/managers/codex-asp/app-server-process.ts
|
|
6747
6533
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6748
6534
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6749
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6535
|
+
var ENGINE_PACKAGE_VERSION = "0.1.278";
|
|
6750
6536
|
var INITIALIZE_METHOD = "initialize";
|
|
6751
6537
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6752
6538
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6949,25 +6735,6 @@ function buildCodexRateLimitsSnapshot(fields) {
|
|
|
6949
6735
|
planType: fields.planType
|
|
6950
6736
|
};
|
|
6951
6737
|
}
|
|
6952
|
-
function extractCodexRateLimitsSnapshotFromJsonl(parsed) {
|
|
6953
|
-
if (!isRecord4(parsed)) return null;
|
|
6954
|
-
const payload = isRecord4(parsed.payload) ? parsed.payload : parsed;
|
|
6955
|
-
const rateLimits = isRecord4(payload.rate_limits) ? payload.rate_limits : null;
|
|
6956
|
-
if (!rateLimits) return null;
|
|
6957
|
-
const credits = isRecord4(rateLimits.credits) ? rateLimits.credits : null;
|
|
6958
|
-
const hasCredits = credits && typeof credits.has_credits === "boolean" ? credits.has_credits : null;
|
|
6959
|
-
const unlimited = credits && typeof credits.unlimited === "boolean" ? credits.unlimited : null;
|
|
6960
|
-
const balance = credits && typeof credits.balance === "string" ? credits.balance : null;
|
|
6961
|
-
const rateLimitResetType = typeof rateLimits.rate_limit_reached_type === "string" && rateLimits.rate_limit_reached_type.length > 0 ? rateLimits.rate_limit_reached_type : null;
|
|
6962
|
-
const planType = typeof rateLimits.plan_type === "string" ? rateLimits.plan_type : null;
|
|
6963
|
-
return buildCodexRateLimitsSnapshot({
|
|
6964
|
-
unlimited,
|
|
6965
|
-
hasCredits,
|
|
6966
|
-
balance,
|
|
6967
|
-
rateLimitResetType,
|
|
6968
|
-
planType
|
|
6969
|
-
});
|
|
6970
|
-
}
|
|
6971
6738
|
var CodexQuotaStatusTracker = class {
|
|
6972
6739
|
lastEmittedQuotaState = "ok";
|
|
6973
6740
|
latestQuotaSnapshot = null;
|
|
@@ -8446,410 +8213,6 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8446
8213
|
}
|
|
8447
8214
|
};
|
|
8448
8215
|
|
|
8449
|
-
// src/managers/codex-manager.ts
|
|
8450
|
-
import { Codex } from "@openai/codex-sdk";
|
|
8451
|
-
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir11, readFile as readFile8 } from "fs/promises";
|
|
8452
|
-
import { existsSync as existsSync7 } from "fs";
|
|
8453
|
-
import { join as join14 } from "path";
|
|
8454
|
-
import { homedir as homedir12 } from "os";
|
|
8455
|
-
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
8456
|
-
var CODEX_CONFIG_PATH = join14(homedir12(), ".codex", "config.toml");
|
|
8457
|
-
function isJsonlEvent2(value) {
|
|
8458
|
-
if (!isRecord4(value)) {
|
|
8459
|
-
return false;
|
|
8460
|
-
}
|
|
8461
|
-
return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord4(value.payload);
|
|
8462
|
-
}
|
|
8463
|
-
function sleep(ms) {
|
|
8464
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
8465
|
-
}
|
|
8466
|
-
var CodexManager = class extends CodingAgentManager {
|
|
8467
|
-
codex;
|
|
8468
|
-
currentThreadId = null;
|
|
8469
|
-
currentThread = null;
|
|
8470
|
-
activeAbortController = null;
|
|
8471
|
-
quotaStatus = new CodexQuotaStatusTracker();
|
|
8472
|
-
constructor(options) {
|
|
8473
|
-
super(options);
|
|
8474
|
-
this.codex = this.createCodexClient();
|
|
8475
|
-
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8476
|
-
}
|
|
8477
|
-
createCodexClient() {
|
|
8478
|
-
const codexApiKey = resolveCodexApiKey();
|
|
8479
|
-
return new Codex({
|
|
8480
|
-
env: buildCodexAgentEnv(),
|
|
8481
|
-
...codexApiKey ? { apiKey: codexApiKey } : {}
|
|
8482
|
-
});
|
|
8483
|
-
}
|
|
8484
|
-
resetCodexClient() {
|
|
8485
|
-
this.codex = this.createCodexClient();
|
|
8486
|
-
this.currentThread = null;
|
|
8487
|
-
}
|
|
8488
|
-
async initialize() {
|
|
8489
|
-
if (this.initialSessionId) {
|
|
8490
|
-
this.currentThreadId = this.initialSessionId;
|
|
8491
|
-
console.log(`[CodexManager] Restored thread ID from persisted state: ${this.currentThreadId}`);
|
|
8492
|
-
}
|
|
8493
|
-
}
|
|
8494
|
-
async flushQuotaSnapshotFromCurrentSession() {
|
|
8495
|
-
if (!this.currentThreadId) return;
|
|
8496
|
-
try {
|
|
8497
|
-
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
8498
|
-
if (!sessionFile) return;
|
|
8499
|
-
const content = await readFile8(sessionFile, "utf-8");
|
|
8500
|
-
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8501
|
-
let latest = null;
|
|
8502
|
-
for (const line of lines) {
|
|
8503
|
-
try {
|
|
8504
|
-
const parsed = JSON.parse(line);
|
|
8505
|
-
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
8506
|
-
if (snapshot) {
|
|
8507
|
-
latest = snapshot;
|
|
8508
|
-
}
|
|
8509
|
-
} catch {
|
|
8510
|
-
}
|
|
8511
|
-
}
|
|
8512
|
-
if (latest) {
|
|
8513
|
-
this.emitQuotaStatus(latest);
|
|
8514
|
-
}
|
|
8515
|
-
} catch (error) {
|
|
8516
|
-
console.warn("[CodexManager] Failed to flush quota snapshot from session:", error);
|
|
8517
|
-
}
|
|
8518
|
-
}
|
|
8519
|
-
emitQuotaStatus(snapshot, force = false) {
|
|
8520
|
-
const event = this.quotaStatus.apply(snapshot, force);
|
|
8521
|
-
if (event) this.onEvent(event);
|
|
8522
|
-
}
|
|
8523
|
-
async interruptActiveTurn() {
|
|
8524
|
-
if (this.activeAbortController) {
|
|
8525
|
-
this.activeAbortController.abort();
|
|
8526
|
-
}
|
|
8527
|
-
}
|
|
8528
|
-
/**
|
|
8529
|
-
* Update the developer_instructions in ~/.codex/config.toml
|
|
8530
|
-
* This sets the system prompt that Codex will use for this turn
|
|
8531
|
-
*/
|
|
8532
|
-
async updateCodexConfig(developerInstructions) {
|
|
8533
|
-
try {
|
|
8534
|
-
const codexDir = join14(homedir12(), ".codex");
|
|
8535
|
-
await mkdir11(codexDir, { recursive: true });
|
|
8536
|
-
let config = {};
|
|
8537
|
-
if (existsSync7(CODEX_CONFIG_PATH)) {
|
|
8538
|
-
try {
|
|
8539
|
-
const existingContent = await readFile8(CODEX_CONFIG_PATH, "utf-8");
|
|
8540
|
-
const parsed = parseToml(existingContent);
|
|
8541
|
-
if (isRecord4(parsed)) {
|
|
8542
|
-
config = parsed;
|
|
8543
|
-
}
|
|
8544
|
-
} catch (parseError) {
|
|
8545
|
-
console.warn("[CodexManager] Failed to parse existing config.toml, starting fresh:", parseError);
|
|
8546
|
-
}
|
|
8547
|
-
}
|
|
8548
|
-
if (developerInstructions) {
|
|
8549
|
-
config.developer_instructions = developerInstructions;
|
|
8550
|
-
} else {
|
|
8551
|
-
delete config.developer_instructions;
|
|
8552
|
-
}
|
|
8553
|
-
const tomlContent = stringifyToml(config);
|
|
8554
|
-
await writeFile6(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
8555
|
-
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
8556
|
-
} catch (error) {
|
|
8557
|
-
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
8558
|
-
}
|
|
8559
|
-
}
|
|
8560
|
-
async processMessageInternal(request) {
|
|
8561
|
-
try {
|
|
8562
|
-
await this.executeCodexTurn(request);
|
|
8563
|
-
} catch (error) {
|
|
8564
|
-
if (isCodexAuthError(error)) {
|
|
8565
|
-
const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
|
|
8566
|
-
if (refreshed) {
|
|
8567
|
-
this.resetCodexClient();
|
|
8568
|
-
await this.executeCodexTurn(request);
|
|
8569
|
-
return;
|
|
8570
|
-
}
|
|
8571
|
-
}
|
|
8572
|
-
throw error;
|
|
8573
|
-
}
|
|
8574
|
-
}
|
|
8575
|
-
async executeCodexTurn(request) {
|
|
8576
|
-
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
8577
|
-
await this.flushQuotaSnapshotFromCurrentSession();
|
|
8578
|
-
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
8579
|
-
this.emitQuotaStatus(this.quotaStatus.latestSnapshot, true);
|
|
8580
|
-
try {
|
|
8581
|
-
await this.onTurnComplete();
|
|
8582
|
-
} catch (error) {
|
|
8583
|
-
console.error("[CodexManager] onTurnComplete failed during quota-blocked turn:", error);
|
|
8584
|
-
}
|
|
8585
|
-
return;
|
|
8586
|
-
}
|
|
8587
|
-
}
|
|
8588
|
-
const {
|
|
8589
|
-
message,
|
|
8590
|
-
model,
|
|
8591
|
-
customInstructions,
|
|
8592
|
-
images,
|
|
8593
|
-
permissionMode,
|
|
8594
|
-
thinkingLevel
|
|
8595
|
-
} = request;
|
|
8596
|
-
const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
|
|
8597
|
-
let tempImagePaths = [];
|
|
8598
|
-
let stopTail = null;
|
|
8599
|
-
let abortController = null;
|
|
8600
|
-
try {
|
|
8601
|
-
if (images && images.length > 0) {
|
|
8602
|
-
const normalizedImages = await normalizeImages(images);
|
|
8603
|
-
tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
|
|
8604
|
-
}
|
|
8605
|
-
const developerInstructions = this.buildCombinedInstructions(customInstructions);
|
|
8606
|
-
await this.updateCodexConfig(developerInstructions);
|
|
8607
|
-
const sandboxMode = "danger-full-access";
|
|
8608
|
-
const webSearchMode = "live";
|
|
8609
|
-
const codexReasoningEffort = codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
8610
|
-
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
8611
|
-
const threadOptions = {
|
|
8612
|
-
workingDirectory: this.workingDirectory,
|
|
8613
|
-
skipGitRepoCheck: true,
|
|
8614
|
-
sandboxMode,
|
|
8615
|
-
model: model || DEFAULT_CODEX_MODEL,
|
|
8616
|
-
webSearchMode,
|
|
8617
|
-
additionalDirectories,
|
|
8618
|
-
...codexReasoningEffort ? { modelReasoningEffort: codexReasoningEffort } : {}
|
|
8619
|
-
};
|
|
8620
|
-
abortController = new AbortController();
|
|
8621
|
-
this.activeAbortController = abortController;
|
|
8622
|
-
if (this.currentThreadId) {
|
|
8623
|
-
this.currentThread = this.codex.resumeThread(this.currentThreadId, threadOptions);
|
|
8624
|
-
} else {
|
|
8625
|
-
this.currentThread = this.codex.startThread(threadOptions);
|
|
8626
|
-
const { events } = await this.currentThread.runStreamed("Hello", { signal: abortController.signal });
|
|
8627
|
-
for await (const event of events) {
|
|
8628
|
-
if (event.type === "thread.started") {
|
|
8629
|
-
this.currentThreadId = event.thread_id;
|
|
8630
|
-
await this.onSaveSessionId(this.currentThreadId);
|
|
8631
|
-
console.log(`[CodexManager] Captured and persisted thread ID: ${this.currentThreadId}`);
|
|
8632
|
-
}
|
|
8633
|
-
}
|
|
8634
|
-
if (!this.currentThreadId && this.currentThread.id) {
|
|
8635
|
-
this.currentThreadId = this.currentThread.id;
|
|
8636
|
-
await this.onSaveSessionId(this.currentThreadId);
|
|
8637
|
-
console.log(`[CodexManager] Captured and persisted thread ID from thread.id: ${this.currentThreadId}`);
|
|
8638
|
-
}
|
|
8639
|
-
}
|
|
8640
|
-
stopTail = this.currentThreadId ? await this.startSessionTail(this.currentThreadId) : null;
|
|
8641
|
-
let input;
|
|
8642
|
-
if (tempImagePaths.length > 0) {
|
|
8643
|
-
const inputItems = [
|
|
8644
|
-
{ type: "text", text: message },
|
|
8645
|
-
...tempImagePaths.map((path4) => ({ type: "local_image", path: path4 }))
|
|
8646
|
-
];
|
|
8647
|
-
input = inputItems;
|
|
8648
|
-
} else {
|
|
8649
|
-
input = message;
|
|
8650
|
-
}
|
|
8651
|
-
try {
|
|
8652
|
-
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
8653
|
-
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
8654
|
-
for await (const event of events) {
|
|
8655
|
-
if (linearSessionId) {
|
|
8656
|
-
linearForwarder.sendPlan(extractPlanFromCodexEvent(event));
|
|
8657
|
-
linearForwarder.sendEvent(convertCodexEvent(event, linearSessionId));
|
|
8658
|
-
}
|
|
8659
|
-
}
|
|
8660
|
-
linearForwarder.flushThoughtAsResponse();
|
|
8661
|
-
} catch (error) {
|
|
8662
|
-
await this.flushQuotaSnapshotFromCurrentSession();
|
|
8663
|
-
if (this.quotaStatus.blocked) {
|
|
8664
|
-
return;
|
|
8665
|
-
}
|
|
8666
|
-
throw error;
|
|
8667
|
-
}
|
|
8668
|
-
} finally {
|
|
8669
|
-
if (stopTail) {
|
|
8670
|
-
await stopTail();
|
|
8671
|
-
}
|
|
8672
|
-
await removeTempImageFiles(tempImagePaths);
|
|
8673
|
-
try {
|
|
8674
|
-
await this.onTurnComplete();
|
|
8675
|
-
} catch (error) {
|
|
8676
|
-
console.error("[CodexManager] onTurnComplete failed:", error);
|
|
8677
|
-
}
|
|
8678
|
-
this.activeAbortController = null;
|
|
8679
|
-
}
|
|
8680
|
-
}
|
|
8681
|
-
async getHistory() {
|
|
8682
|
-
if (!this.currentThreadId) {
|
|
8683
|
-
return {
|
|
8684
|
-
thread_id: null,
|
|
8685
|
-
events: []
|
|
8686
|
-
};
|
|
8687
|
-
}
|
|
8688
|
-
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
8689
|
-
if (!sessionFile) {
|
|
8690
|
-
return {
|
|
8691
|
-
thread_id: this.currentThreadId,
|
|
8692
|
-
events: []
|
|
8693
|
-
};
|
|
8694
|
-
}
|
|
8695
|
-
const events = await readJSONL(sessionFile);
|
|
8696
|
-
return {
|
|
8697
|
-
thread_id: this.currentThreadId,
|
|
8698
|
-
events
|
|
8699
|
-
};
|
|
8700
|
-
}
|
|
8701
|
-
// Helper methods for finding session files
|
|
8702
|
-
async findSessionFile(threadId) {
|
|
8703
|
-
const sessionsDir = join14(homedir12(), ".codex", "sessions");
|
|
8704
|
-
try {
|
|
8705
|
-
const now = /* @__PURE__ */ new Date();
|
|
8706
|
-
const year = now.getFullYear();
|
|
8707
|
-
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
8708
|
-
const day = String(now.getDate()).padStart(2, "0");
|
|
8709
|
-
const todayDir = join14(sessionsDir, String(year), month, day);
|
|
8710
|
-
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
8711
|
-
if (file) return file;
|
|
8712
|
-
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
8713
|
-
const date = new Date(now);
|
|
8714
|
-
date.setDate(date.getDate() - daysAgo);
|
|
8715
|
-
const searchYear = date.getFullYear();
|
|
8716
|
-
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
8717
|
-
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
8718
|
-
const searchDir = join14(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
8719
|
-
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
8720
|
-
if (file2) return file2;
|
|
8721
|
-
}
|
|
8722
|
-
return null;
|
|
8723
|
-
} catch (error) {
|
|
8724
|
-
return null;
|
|
8725
|
-
}
|
|
8726
|
-
}
|
|
8727
|
-
async findFileInDirectory(directory, threadId) {
|
|
8728
|
-
try {
|
|
8729
|
-
const files = await readdir3(directory);
|
|
8730
|
-
for (const file of files) {
|
|
8731
|
-
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
8732
|
-
const fullPath = join14(directory, file);
|
|
8733
|
-
const stats = await stat2(fullPath);
|
|
8734
|
-
if (stats.isFile()) {
|
|
8735
|
-
return fullPath;
|
|
8736
|
-
}
|
|
8737
|
-
}
|
|
8738
|
-
}
|
|
8739
|
-
return null;
|
|
8740
|
-
} catch (error) {
|
|
8741
|
-
return null;
|
|
8742
|
-
}
|
|
8743
|
-
}
|
|
8744
|
-
async waitForSessionFile(threadId, timeoutMs = 5e3) {
|
|
8745
|
-
const start = Date.now();
|
|
8746
|
-
while (Date.now() - start < timeoutMs) {
|
|
8747
|
-
const sessionFile = await this.findSessionFile(threadId);
|
|
8748
|
-
if (sessionFile) {
|
|
8749
|
-
return sessionFile;
|
|
8750
|
-
}
|
|
8751
|
-
await sleep(100);
|
|
8752
|
-
}
|
|
8753
|
-
return null;
|
|
8754
|
-
}
|
|
8755
|
-
// @openai/codex-sdk doesn't expose manual /compact (TUI-only); we only mirror the auto-compaction rollout entries to the UI.
|
|
8756
|
-
trackNativeCompaction(event) {
|
|
8757
|
-
if (event.type === "compacted") {
|
|
8758
|
-
this.setCompacting(false);
|
|
8759
|
-
return;
|
|
8760
|
-
}
|
|
8761
|
-
if (event.type !== "event_msg") return;
|
|
8762
|
-
const msg = event.payload.msg;
|
|
8763
|
-
if (!msg) return;
|
|
8764
|
-
const itemType = msg.payload?.item?.type;
|
|
8765
|
-
if (itemType !== "context_compaction") return;
|
|
8766
|
-
if (msg.type === "item_started") {
|
|
8767
|
-
this.setCompacting(true);
|
|
8768
|
-
} else if (msg.type === "item_completed") {
|
|
8769
|
-
this.setCompacting(false);
|
|
8770
|
-
}
|
|
8771
|
-
}
|
|
8772
|
-
async startSessionTail(threadId) {
|
|
8773
|
-
const sessionFile = await this.waitForSessionFile(threadId);
|
|
8774
|
-
if (!sessionFile) {
|
|
8775
|
-
return async () => {
|
|
8776
|
-
};
|
|
8777
|
-
}
|
|
8778
|
-
let active = true;
|
|
8779
|
-
const seenLines = /* @__PURE__ */ new Set();
|
|
8780
|
-
const seedSeenLines = async () => {
|
|
8781
|
-
try {
|
|
8782
|
-
const content = await readFile8(sessionFile, "utf-8");
|
|
8783
|
-
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8784
|
-
let latest = null;
|
|
8785
|
-
for (const line of lines) {
|
|
8786
|
-
seenLines.add(line);
|
|
8787
|
-
try {
|
|
8788
|
-
const parsed = JSON.parse(line);
|
|
8789
|
-
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
8790
|
-
if (snapshot) latest = snapshot;
|
|
8791
|
-
} catch {
|
|
8792
|
-
}
|
|
8793
|
-
}
|
|
8794
|
-
if (latest) {
|
|
8795
|
-
this.quotaStatus.prime(latest);
|
|
8796
|
-
}
|
|
8797
|
-
} catch {
|
|
8798
|
-
}
|
|
8799
|
-
};
|
|
8800
|
-
await seedSeenLines();
|
|
8801
|
-
const pump = async () => {
|
|
8802
|
-
let emitted = 0;
|
|
8803
|
-
try {
|
|
8804
|
-
const content = await readFile8(sessionFile, "utf-8");
|
|
8805
|
-
const lines = content.split("\n");
|
|
8806
|
-
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
8807
|
-
for (const line of completeLines) {
|
|
8808
|
-
const trimmed = line.trim();
|
|
8809
|
-
if (!trimmed || seenLines.has(trimmed)) {
|
|
8810
|
-
continue;
|
|
8811
|
-
}
|
|
8812
|
-
seenLines.add(trimmed);
|
|
8813
|
-
try {
|
|
8814
|
-
const parsed = JSON.parse(trimmed);
|
|
8815
|
-
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
8816
|
-
if (snapshot) {
|
|
8817
|
-
this.emitQuotaStatus(snapshot);
|
|
8818
|
-
}
|
|
8819
|
-
if (isJsonlEvent2(parsed)) {
|
|
8820
|
-
this.trackNativeCompaction(parsed);
|
|
8821
|
-
this.onEvent(parsed);
|
|
8822
|
-
emitted += 1;
|
|
8823
|
-
}
|
|
8824
|
-
} catch {
|
|
8825
|
-
}
|
|
8826
|
-
}
|
|
8827
|
-
} catch {
|
|
8828
|
-
}
|
|
8829
|
-
return emitted;
|
|
8830
|
-
};
|
|
8831
|
-
const loop = (async () => {
|
|
8832
|
-
while (active) {
|
|
8833
|
-
await pump();
|
|
8834
|
-
await sleep(100);
|
|
8835
|
-
}
|
|
8836
|
-
await pump();
|
|
8837
|
-
})();
|
|
8838
|
-
return async () => {
|
|
8839
|
-
active = false;
|
|
8840
|
-
await loop;
|
|
8841
|
-
const deadline = Date.now() + 1500;
|
|
8842
|
-
while (Date.now() < deadline) {
|
|
8843
|
-
const emitted = await pump();
|
|
8844
|
-
if (emitted > 0) {
|
|
8845
|
-
continue;
|
|
8846
|
-
}
|
|
8847
|
-
await sleep(100);
|
|
8848
|
-
}
|
|
8849
|
-
};
|
|
8850
|
-
}
|
|
8851
|
-
};
|
|
8852
|
-
|
|
8853
8216
|
// src/managers/relay-tools.ts
|
|
8854
8217
|
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
8855
8218
|
import { z } from "zod";
|
|
@@ -9451,19 +8814,19 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
9451
8814
|
};
|
|
9452
8815
|
|
|
9453
8816
|
// src/services/chat/chat-service.ts
|
|
9454
|
-
var ENGINE_DIR2 =
|
|
9455
|
-
var CHATS_FILE =
|
|
9456
|
-
var CLAUDE_HISTORY_DIR =
|
|
9457
|
-
var RELAY_HISTORY_DIR =
|
|
9458
|
-
var CHAT_SENDERS_DIR =
|
|
9459
|
-
var CODEX_AUTH_PATH2 =
|
|
8817
|
+
var ENGINE_DIR2 = join14(homedir12(), ".replicas", "engine");
|
|
8818
|
+
var CHATS_FILE = join14(ENGINE_DIR2, "chats.json");
|
|
8819
|
+
var CLAUDE_HISTORY_DIR = join14(ENGINE_DIR2, "claude-histories");
|
|
8820
|
+
var RELAY_HISTORY_DIR = join14(ENGINE_DIR2, "relay-histories");
|
|
8821
|
+
var CHAT_SENDERS_DIR = join14(ENGINE_DIR2, "chat-senders");
|
|
8822
|
+
var CODEX_AUTH_PATH2 = join14(homedir12(), ".codex", "auth.json");
|
|
9460
8823
|
var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
|
|
9461
8824
|
function isChatMessageSender(value) {
|
|
9462
8825
|
if (!isRecord4(value)) return false;
|
|
9463
8826
|
return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
9464
8827
|
}
|
|
9465
8828
|
function isCodexAvailable() {
|
|
9466
|
-
return
|
|
8829
|
+
return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
|
|
9467
8830
|
}
|
|
9468
8831
|
function isSameAcceptedUserEvent(event, acceptedEvent) {
|
|
9469
8832
|
if (areSameUserMessageEvents(event, acceptedEvent)) return true;
|
|
@@ -9505,18 +8868,18 @@ function isPersistedChat(value) {
|
|
|
9505
8868
|
return false;
|
|
9506
8869
|
}
|
|
9507
8870
|
const candidate = value;
|
|
9508
|
-
return typeof candidate.id === "string" && (candidate.provider === "claude" || candidate.provider === "codex" || candidate.provider === "relay") && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string")
|
|
9509
|
-
}
|
|
9510
|
-
function codexBackendForChat(chat) {
|
|
9511
|
-
if (chat.provider !== "codex") return "asp";
|
|
9512
|
-
if (chat.codexBackend) return chat.codexBackend;
|
|
9513
|
-
return chat.providerSessionId ? "sdk" : "asp";
|
|
8871
|
+
return typeof candidate.id === "string" && (candidate.provider === "claude" || candidate.provider === "codex" || candidate.provider === "relay") && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string");
|
|
9514
8872
|
}
|
|
9515
8873
|
function normalizePersistedChat(chat) {
|
|
8874
|
+
const isLegacyCodexSdkChat = chat.provider === "codex" && (chat.codexBackend === "sdk" || chat.codexBackend === void 0 && chat.providerSessionId !== null);
|
|
9516
8875
|
return {
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
8876
|
+
id: chat.id,
|
|
8877
|
+
provider: chat.provider,
|
|
8878
|
+
title: chat.title,
|
|
8879
|
+
createdAt: chat.createdAt,
|
|
8880
|
+
updatedAt: chat.updatedAt,
|
|
8881
|
+
providerSessionId: isLegacyCodexSdkChat ? null : chat.providerSessionId,
|
|
8882
|
+
parentChatId: chat.parentChatId ?? null
|
|
9520
8883
|
};
|
|
9521
8884
|
}
|
|
9522
8885
|
function parsePersistedChatsContent(content) {
|
|
@@ -9551,10 +8914,10 @@ var ChatService = class {
|
|
|
9551
8914
|
chats = /* @__PURE__ */ new Map();
|
|
9552
8915
|
writeChain = Promise.resolve();
|
|
9553
8916
|
async initialize() {
|
|
9554
|
-
await
|
|
9555
|
-
await
|
|
9556
|
-
await
|
|
9557
|
-
await
|
|
8917
|
+
await mkdir11(ENGINE_DIR2, { recursive: true });
|
|
8918
|
+
await mkdir11(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
8919
|
+
await mkdir11(RELAY_HISTORY_DIR, { recursive: true });
|
|
8920
|
+
await mkdir11(CHAT_SENDERS_DIR, { recursive: true });
|
|
9558
8921
|
const persisted = await this.loadChats();
|
|
9559
8922
|
for (const chat of persisted) {
|
|
9560
8923
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -9606,8 +8969,7 @@ var ChatService = class {
|
|
|
9606
8969
|
createdAt: now,
|
|
9607
8970
|
updatedAt: now,
|
|
9608
8971
|
providerSessionId: null,
|
|
9609
|
-
parentChatId
|
|
9610
|
-
...request.provider === "codex" ? { codexBackend: "asp" } : {}
|
|
8972
|
+
parentChatId
|
|
9611
8973
|
};
|
|
9612
8974
|
const runtime = this.createRuntimeChat(persisted);
|
|
9613
8975
|
this.chats.set(persisted.id, runtime);
|
|
@@ -9653,7 +9015,7 @@ var ChatService = class {
|
|
|
9653
9015
|
};
|
|
9654
9016
|
}
|
|
9655
9017
|
senderFilePath(chatId) {
|
|
9656
|
-
return
|
|
9018
|
+
return join14(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
9657
9019
|
}
|
|
9658
9020
|
async appendSender(chatId, sender) {
|
|
9659
9021
|
try {
|
|
@@ -9664,7 +9026,7 @@ var ChatService = class {
|
|
|
9664
9026
|
}
|
|
9665
9027
|
async readSenders(chatId) {
|
|
9666
9028
|
try {
|
|
9667
|
-
const content = await
|
|
9029
|
+
const content = await readFile8(this.senderFilePath(chatId), "utf-8");
|
|
9668
9030
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
9669
9031
|
const senders = [];
|
|
9670
9032
|
for (const line of lines) {
|
|
@@ -9803,7 +9165,7 @@ var ChatService = class {
|
|
|
9803
9165
|
async deleteHistoryFile(persisted) {
|
|
9804
9166
|
if (persisted.provider === "claude" || persisted.provider === "relay") {
|
|
9805
9167
|
const dir = persisted.provider === "claude" ? CLAUDE_HISTORY_DIR : RELAY_HISTORY_DIR;
|
|
9806
|
-
await rm(
|
|
9168
|
+
await rm(join14(dir, `${persisted.id}.jsonl`), { force: true });
|
|
9807
9169
|
}
|
|
9808
9170
|
await rm(this.senderFilePath(persisted.id), { force: true });
|
|
9809
9171
|
}
|
|
@@ -9860,7 +9222,7 @@ var ChatService = class {
|
|
|
9860
9222
|
if (persisted.provider === "claude") {
|
|
9861
9223
|
provider = new ClaudeManager({
|
|
9862
9224
|
workingDirectory: this.workingDirectory,
|
|
9863
|
-
historyFilePath:
|
|
9225
|
+
historyFilePath: join14(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
9864
9226
|
initialSessionId: persisted.providerSessionId,
|
|
9865
9227
|
onSaveSessionId: saveSession,
|
|
9866
9228
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -9869,7 +9231,7 @@ var ChatService = class {
|
|
|
9869
9231
|
} else if (persisted.provider === "relay") {
|
|
9870
9232
|
provider = new RelayManager({
|
|
9871
9233
|
workingDirectory: this.workingDirectory,
|
|
9872
|
-
historyFilePath:
|
|
9234
|
+
historyFilePath: join14(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
9873
9235
|
initialSessionId: persisted.providerSessionId,
|
|
9874
9236
|
onSaveSessionId: saveSession,
|
|
9875
9237
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -9878,8 +9240,7 @@ var ChatService = class {
|
|
|
9878
9240
|
codexAvailable: isCodexAvailable()
|
|
9879
9241
|
});
|
|
9880
9242
|
} else {
|
|
9881
|
-
|
|
9882
|
-
provider = new CodexProviderCtor({
|
|
9243
|
+
provider = new CodexAspManager({
|
|
9883
9244
|
workingDirectory: this.workingDirectory,
|
|
9884
9245
|
initialSessionId: persisted.providerSessionId,
|
|
9885
9246
|
onSaveSessionId: saveSession,
|
|
@@ -10021,7 +9382,7 @@ var ChatService = class {
|
|
|
10021
9382
|
}
|
|
10022
9383
|
async loadChats() {
|
|
10023
9384
|
try {
|
|
10024
|
-
const content = await
|
|
9385
|
+
const content = await readFile8(CHATS_FILE, "utf-8");
|
|
10025
9386
|
return parsePersistedChatsContent(content);
|
|
10026
9387
|
} catch (error) {
|
|
10027
9388
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -10036,7 +9397,7 @@ var ChatService = class {
|
|
|
10036
9397
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
10037
9398
|
}
|
|
10038
9399
|
try {
|
|
10039
|
-
const backupContent = await
|
|
9400
|
+
const backupContent = await readFile8(CHATS_BACKUP_FILE, "utf-8");
|
|
10040
9401
|
return parsePersistedChatsContent(backupContent);
|
|
10041
9402
|
} catch (backupError) {
|
|
10042
9403
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -10111,8 +9472,8 @@ var ChatService = class {
|
|
|
10111
9472
|
|
|
10112
9473
|
// src/services/repo-file-service.ts
|
|
10113
9474
|
import { execFile as execFile2 } from "child_process";
|
|
10114
|
-
import { readFile as
|
|
10115
|
-
import { join as
|
|
9475
|
+
import { readFile as readFile9, realpath, stat as stat2 } from "fs/promises";
|
|
9476
|
+
import { join as join15, resolve, extname } from "path";
|
|
10116
9477
|
var CACHE_TTL_MS = 3e4;
|
|
10117
9478
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
10118
9479
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -10272,11 +9633,11 @@ var RepoFileService = class {
|
|
|
10272
9633
|
const repo = repos.find((r) => r.name === repoName);
|
|
10273
9634
|
if (!repo) return null;
|
|
10274
9635
|
try {
|
|
10275
|
-
const fullPath = await realpath(resolve(
|
|
9636
|
+
const fullPath = await realpath(resolve(join15(repo.path, filePath)));
|
|
10276
9637
|
const repoRoot = await realpath(repo.path);
|
|
10277
9638
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
10278
9639
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
10279
|
-
const fileStat = await
|
|
9640
|
+
const fileStat = await stat2(fullPath);
|
|
10280
9641
|
if (!fileStat.isFile()) return null;
|
|
10281
9642
|
const sizeBytes = fileStat.size;
|
|
10282
9643
|
if (isBinaryExtension(filePath)) {
|
|
@@ -10301,7 +9662,7 @@ var RepoFileService = class {
|
|
|
10301
9662
|
tooLarge: true
|
|
10302
9663
|
};
|
|
10303
9664
|
}
|
|
10304
|
-
const content = await
|
|
9665
|
+
const content = await readFile9(fullPath, "utf-8");
|
|
10305
9666
|
return {
|
|
10306
9667
|
repoName,
|
|
10307
9668
|
path: filePath,
|
|
@@ -10346,13 +9707,9 @@ var RepoFileService = class {
|
|
|
10346
9707
|
async listChangedFiles(repoPath, defaultBranch) {
|
|
10347
9708
|
const changedFiles = /* @__PURE__ */ new Set();
|
|
10348
9709
|
try {
|
|
10349
|
-
const
|
|
10350
|
-
["merge-base", `origin/${defaultBranch}`, "HEAD"],
|
|
10351
|
-
repoPath,
|
|
10352
|
-
SEARCH_TIMEOUT_MS
|
|
10353
|
-
);
|
|
9710
|
+
const diffBase = await resolveDiffBase(repoPath, defaultBranch);
|
|
10354
9711
|
const diffOutput = await execGitAsync(
|
|
10355
|
-
["diff", "--name-only", "-M",
|
|
9712
|
+
["diff", "--name-only", "-M", diffBase],
|
|
10356
9713
|
repoPath,
|
|
10357
9714
|
SEARCH_TIMEOUT_MS
|
|
10358
9715
|
);
|
|
@@ -10383,16 +9740,16 @@ var RepoFileService = class {
|
|
|
10383
9740
|
// src/v1-routes.ts
|
|
10384
9741
|
import { Hono } from "hono";
|
|
10385
9742
|
import { z as z2 } from "zod";
|
|
10386
|
-
import { readdir as
|
|
10387
|
-
import { join as
|
|
9743
|
+
import { readdir as readdir5, stat as stat4, readFile as readFile13 } from "fs/promises";
|
|
9744
|
+
import { join as join19, resolve as resolve2 } from "path";
|
|
10388
9745
|
|
|
10389
9746
|
// src/services/canvas-service.ts
|
|
10390
|
-
import { readdir as
|
|
10391
|
-
import { homedir as
|
|
10392
|
-
import { basename, join as
|
|
9747
|
+
import { readdir as readdir3, readFile as readFile10, stat as stat3 } from "fs/promises";
|
|
9748
|
+
import { homedir as homedir13 } from "os";
|
|
9749
|
+
import { basename, join as join16 } from "path";
|
|
10393
9750
|
var CANVAS_DIRECTORIES = [
|
|
10394
|
-
|
|
10395
|
-
|
|
9751
|
+
join16(homedir13(), ".claude", "plans"),
|
|
9752
|
+
join16(homedir13(), ".replicas", "canvas")
|
|
10396
9753
|
];
|
|
10397
9754
|
var MAX_CANVAS_FILE_BYTES = 5 * 1024 * 1024;
|
|
10398
9755
|
function isTextKind(kind) {
|
|
@@ -10407,7 +9764,7 @@ var CanvasService = class {
|
|
|
10407
9764
|
for (const directory of CANVAS_DIRECTORIES) {
|
|
10408
9765
|
let entries;
|
|
10409
9766
|
try {
|
|
10410
|
-
entries = await
|
|
9767
|
+
entries = await readdir3(directory, { withFileTypes: true });
|
|
10411
9768
|
} catch {
|
|
10412
9769
|
continue;
|
|
10413
9770
|
}
|
|
@@ -10418,7 +9775,7 @@ var CanvasService = class {
|
|
|
10418
9775
|
const { kind } = classifyCanvasFilename(entry.name);
|
|
10419
9776
|
let sizeBytes = 0;
|
|
10420
9777
|
try {
|
|
10421
|
-
const s = await
|
|
9778
|
+
const s = await stat3(join16(directory, entry.name));
|
|
10422
9779
|
sizeBytes = s.size;
|
|
10423
9780
|
} catch {
|
|
10424
9781
|
continue;
|
|
@@ -10433,10 +9790,10 @@ var CanvasService = class {
|
|
|
10433
9790
|
if (!safe || safe !== filename || safe.startsWith(".")) return null;
|
|
10434
9791
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
10435
9792
|
for (const directory of CANVAS_DIRECTORIES) {
|
|
10436
|
-
const filePath =
|
|
9793
|
+
const filePath = join16(directory, safe);
|
|
10437
9794
|
let sizeBytes = 0;
|
|
10438
9795
|
try {
|
|
10439
|
-
const s = await
|
|
9796
|
+
const s = await stat3(filePath);
|
|
10440
9797
|
sizeBytes = s.size;
|
|
10441
9798
|
} catch {
|
|
10442
9799
|
continue;
|
|
@@ -10452,10 +9809,10 @@ var CanvasService = class {
|
|
|
10452
9809
|
}
|
|
10453
9810
|
try {
|
|
10454
9811
|
if (isTextKind(kind)) {
|
|
10455
|
-
const content = await
|
|
9812
|
+
const content = await readFile10(filePath, "utf-8");
|
|
10456
9813
|
return { filename: safe, kind, sizeBytes, mimeType, content };
|
|
10457
9814
|
}
|
|
10458
|
-
const buf = await
|
|
9815
|
+
const buf = await readFile10(filePath);
|
|
10459
9816
|
return { filename: safe, kind, sizeBytes, mimeType, base64: buf.toString("base64") };
|
|
10460
9817
|
} catch {
|
|
10461
9818
|
continue;
|
|
@@ -10468,16 +9825,16 @@ var canvasService = new CanvasService();
|
|
|
10468
9825
|
|
|
10469
9826
|
// src/services/warm-hooks-service.ts
|
|
10470
9827
|
import { spawn as spawn4 } from "child_process";
|
|
10471
|
-
import { readFile as
|
|
10472
|
-
import { existsSync as
|
|
10473
|
-
import { join as
|
|
9828
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
9829
|
+
import { existsSync as existsSync8 } from "fs";
|
|
9830
|
+
import { join as join18 } from "path";
|
|
10474
9831
|
|
|
10475
9832
|
// src/services/warm-hook-logs-service.ts
|
|
10476
|
-
import { mkdir as
|
|
10477
|
-
import { homedir as
|
|
10478
|
-
import { join as
|
|
10479
|
-
var LOGS_DIR2 =
|
|
10480
|
-
var CURRENT_RUN_LOG =
|
|
9833
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile6, readdir as readdir4, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
|
|
9834
|
+
import { homedir as homedir14 } from "os";
|
|
9835
|
+
import { join as join17 } from "path";
|
|
9836
|
+
var LOGS_DIR2 = join17(homedir14(), ".replicas", "warm-hook-logs");
|
|
9837
|
+
var CURRENT_RUN_LOG = join17(LOGS_DIR2, "current-run.log");
|
|
10481
9838
|
var GLOBAL_FILENAME = "global.json";
|
|
10482
9839
|
function withPreview2(stored) {
|
|
10483
9840
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -10485,7 +9842,7 @@ function withPreview2(stored) {
|
|
|
10485
9842
|
}
|
|
10486
9843
|
var WarmHookLogsService = class {
|
|
10487
9844
|
async ensureDir() {
|
|
10488
|
-
await
|
|
9845
|
+
await mkdir12(LOGS_DIR2, { recursive: true });
|
|
10489
9846
|
}
|
|
10490
9847
|
async saveGlobalHookLog(entry) {
|
|
10491
9848
|
await this.ensureDir();
|
|
@@ -10494,7 +9851,7 @@ var WarmHookLogsService = class {
|
|
|
10494
9851
|
hookName: "organization",
|
|
10495
9852
|
...entry
|
|
10496
9853
|
};
|
|
10497
|
-
await
|
|
9854
|
+
await writeFile6(join17(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
10498
9855
|
`, "utf-8");
|
|
10499
9856
|
}
|
|
10500
9857
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -10504,7 +9861,7 @@ var WarmHookLogsService = class {
|
|
|
10504
9861
|
hookName: "environment",
|
|
10505
9862
|
...entry
|
|
10506
9863
|
};
|
|
10507
|
-
await
|
|
9864
|
+
await writeFile6(join17(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
10508
9865
|
`, "utf-8");
|
|
10509
9866
|
}
|
|
10510
9867
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -10514,13 +9871,13 @@ var WarmHookLogsService = class {
|
|
|
10514
9871
|
hookName: repoName,
|
|
10515
9872
|
...entry
|
|
10516
9873
|
};
|
|
10517
|
-
await
|
|
9874
|
+
await writeFile6(join17(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
10518
9875
|
`, "utf-8");
|
|
10519
9876
|
}
|
|
10520
9877
|
async getAllLogs() {
|
|
10521
9878
|
let files;
|
|
10522
9879
|
try {
|
|
10523
|
-
files = await
|
|
9880
|
+
files = await readdir4(LOGS_DIR2);
|
|
10524
9881
|
} catch (err) {
|
|
10525
9882
|
if (err.code === "ENOENT") {
|
|
10526
9883
|
return [];
|
|
@@ -10533,7 +9890,7 @@ var WarmHookLogsService = class {
|
|
|
10533
9890
|
continue;
|
|
10534
9891
|
}
|
|
10535
9892
|
try {
|
|
10536
|
-
const raw = await
|
|
9893
|
+
const raw = await readFile11(join17(LOGS_DIR2, file), "utf-8");
|
|
10537
9894
|
const stored = JSON.parse(raw);
|
|
10538
9895
|
logs.push(withPreview2(stored));
|
|
10539
9896
|
} catch {
|
|
@@ -10562,7 +9919,7 @@ var WarmHookLogsService = class {
|
|
|
10562
9919
|
}
|
|
10563
9920
|
async getCurrentRunLog() {
|
|
10564
9921
|
try {
|
|
10565
|
-
return await
|
|
9922
|
+
return await readFile11(CURRENT_RUN_LOG, "utf-8");
|
|
10566
9923
|
} catch (err) {
|
|
10567
9924
|
if (err.code === "ENOENT") return null;
|
|
10568
9925
|
throw err;
|
|
@@ -10571,7 +9928,7 @@ var WarmHookLogsService = class {
|
|
|
10571
9928
|
async getFullOutput(hookType, hookName) {
|
|
10572
9929
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
10573
9930
|
try {
|
|
10574
|
-
const raw = await
|
|
9931
|
+
const raw = await readFile11(join17(LOGS_DIR2, filename), "utf-8");
|
|
10575
9932
|
const stored = JSON.parse(raw);
|
|
10576
9933
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
10577
9934
|
return null;
|
|
@@ -10590,12 +9947,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
10590
9947
|
// src/services/warm-hooks-service.ts
|
|
10591
9948
|
async function readRepoWarmHook(repoPath) {
|
|
10592
9949
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
10593
|
-
const configPath =
|
|
10594
|
-
if (!
|
|
9950
|
+
const configPath = join18(repoPath, filename);
|
|
9951
|
+
if (!existsSync8(configPath)) {
|
|
10595
9952
|
continue;
|
|
10596
9953
|
}
|
|
10597
9954
|
try {
|
|
10598
|
-
const raw = await
|
|
9955
|
+
const raw = await readFile12(configPath, "utf-8");
|
|
10599
9956
|
const config = parseReplicasConfigString(raw, filename);
|
|
10600
9957
|
if (!config.warmHook) {
|
|
10601
9958
|
return null;
|
|
@@ -11488,12 +10845,12 @@ function createV1Routes(deps) {
|
|
|
11488
10845
|
});
|
|
11489
10846
|
app2.get("/logs", async (c) => {
|
|
11490
10847
|
try {
|
|
11491
|
-
const files = await
|
|
10848
|
+
const files = await readdir5(LOG_DIR).catch(() => []);
|
|
11492
10849
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
11493
10850
|
const sessions = await Promise.all(
|
|
11494
10851
|
logFiles.map(async (filename) => {
|
|
11495
|
-
const filePath =
|
|
11496
|
-
const fileStat = await
|
|
10852
|
+
const filePath = join19(LOG_DIR, filename);
|
|
10853
|
+
const fileStat = await stat4(filePath);
|
|
11497
10854
|
const sessionId = filename.replace(/\.log$/, "");
|
|
11498
10855
|
return {
|
|
11499
10856
|
sessionId,
|
|
@@ -11529,7 +10886,7 @@ function createV1Routes(deps) {
|
|
|
11529
10886
|
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
11530
10887
|
let content;
|
|
11531
10888
|
try {
|
|
11532
|
-
content = await
|
|
10889
|
+
content = await readFile13(filePath, "utf-8");
|
|
11533
10890
|
} catch {
|
|
11534
10891
|
return c.json(jsonError("Log session not found"), 404);
|
|
11535
10892
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.278",
|
|
4
4
|
"description": "Lightweight API server for Replicas workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "0.2.112",
|
|
34
34
|
"@hono/node-server": "^1.19.5",
|
|
35
|
-
"@openai/codex-sdk": "^0.124.0",
|
|
36
35
|
"hono": "^4.10.3",
|
|
37
36
|
"smol-toml": "^1.6.0",
|
|
38
37
|
"yaml": "^2.8.2",
|