replicas-engine 0.1.396 → 0.1.398
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 +129 -28
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -484,7 +484,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
484
484
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
485
485
|
|
|
486
486
|
// ../shared/src/e2b.ts
|
|
487
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-04-
|
|
487
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-04-v5";
|
|
488
488
|
|
|
489
489
|
// ../shared/src/runtime-env.ts
|
|
490
490
|
function parsePosixEnvFile(content) {
|
|
@@ -3261,6 +3261,21 @@ var monolithService = new MonolithService();
|
|
|
3261
3261
|
// src/utils/file.ts
|
|
3262
3262
|
import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
|
|
3263
3263
|
import { dirname } from "path";
|
|
3264
|
+
|
|
3265
|
+
// src/utils/async-lock.ts
|
|
3266
|
+
var AsyncLock = class {
|
|
3267
|
+
chain = Promise.resolve();
|
|
3268
|
+
run(operation) {
|
|
3269
|
+
const result = this.chain.then(operation, operation);
|
|
3270
|
+
this.chain = result.then(() => void 0, () => void 0);
|
|
3271
|
+
return result;
|
|
3272
|
+
}
|
|
3273
|
+
async drain() {
|
|
3274
|
+
await this.chain;
|
|
3275
|
+
}
|
|
3276
|
+
};
|
|
3277
|
+
|
|
3278
|
+
// src/utils/file.ts
|
|
3264
3279
|
async function atomicWriteFile(path5, data, options) {
|
|
3265
3280
|
const tmpFile = `${path5}.${process.pid}.${Date.now()}.tmp`;
|
|
3266
3281
|
try {
|
|
@@ -3277,9 +3292,9 @@ async function writeSecureCredentialFile(filePath, content, options) {
|
|
|
3277
3292
|
}
|
|
3278
3293
|
await atomicWriteFile(filePath, content, { mode: 384 });
|
|
3279
3294
|
}
|
|
3280
|
-
var
|
|
3295
|
+
var credentialFileLock = new AsyncLock();
|
|
3281
3296
|
function upsertCredentialFileLines(filePath, hosts, lines) {
|
|
3282
|
-
|
|
3297
|
+
return credentialFileLock.run(async () => {
|
|
3283
3298
|
let existing = [];
|
|
3284
3299
|
try {
|
|
3285
3300
|
existing = (await readFile(filePath, "utf-8")).split("\n").filter(Boolean);
|
|
@@ -3289,11 +3304,9 @@ function upsertCredentialFileLines(filePath, hosts, lines) {
|
|
|
3289
3304
|
const kept = existing.filter((line) => !hosts.some((host) => line.endsWith(`@${host}`)));
|
|
3290
3305
|
await writeSecureCredentialFile(filePath, [...lines, ...kept, ""].join("\n"));
|
|
3291
3306
|
});
|
|
3292
|
-
credentialFileQueue = task.catch(() => void 0);
|
|
3293
|
-
return task;
|
|
3294
3307
|
}
|
|
3295
3308
|
function removeCredentialFileLines(filePath, shouldRemove) {
|
|
3296
|
-
|
|
3309
|
+
return credentialFileLock.run(async () => {
|
|
3297
3310
|
let existing = [];
|
|
3298
3311
|
try {
|
|
3299
3312
|
existing = (await readFile(filePath, "utf-8")).split("\n").filter(Boolean);
|
|
@@ -3303,8 +3316,6 @@ function removeCredentialFileLines(filePath, shouldRemove) {
|
|
|
3303
3316
|
const kept = existing.filter((line) => !shouldRemove(line));
|
|
3304
3317
|
await writeSecureCredentialFile(filePath, [...kept, ""].join("\n"));
|
|
3305
3318
|
});
|
|
3306
|
-
credentialFileQueue = task.catch(() => void 0);
|
|
3307
|
-
return task;
|
|
3308
3319
|
}
|
|
3309
3320
|
|
|
3310
3321
|
// src/utils/git-identity.ts
|
|
@@ -3675,14 +3686,9 @@ var STATE_FILE = join3(STATE_DIR, "engine-state.json");
|
|
|
3675
3686
|
var DEFAULT_STATE = {
|
|
3676
3687
|
repos: {}
|
|
3677
3688
|
};
|
|
3678
|
-
var
|
|
3679
|
-
function enqueueStateWrite(operation) {
|
|
3680
|
-
const result = stateWriteChain.then(operation);
|
|
3681
|
-
stateWriteChain = result.then(() => void 0, () => void 0);
|
|
3682
|
-
return result;
|
|
3683
|
-
}
|
|
3689
|
+
var stateWriteLock = new AsyncLock();
|
|
3684
3690
|
async function updateEngineState(updater) {
|
|
3685
|
-
await
|
|
3691
|
+
await stateWriteLock.run(async () => {
|
|
3686
3692
|
await mkdir2(STATE_DIR, { recursive: true });
|
|
3687
3693
|
const currentState = await loadEngineState();
|
|
3688
3694
|
const nextState = updater(currentState);
|
|
@@ -5951,6 +5957,22 @@ var MessageQueueService = class {
|
|
|
5951
5957
|
this.queue = [];
|
|
5952
5958
|
return true;
|
|
5953
5959
|
}
|
|
5960
|
+
takeFromQueue(messageId) {
|
|
5961
|
+
const index = this.queue.findIndex((m) => m.id === messageId);
|
|
5962
|
+
if (index === -1) return null;
|
|
5963
|
+
const [message] = this.queue.splice(index, 1);
|
|
5964
|
+
return { message, index };
|
|
5965
|
+
}
|
|
5966
|
+
restoreToQueue(message, index) {
|
|
5967
|
+
const clamped = Math.max(0, Math.min(index, this.queue.length));
|
|
5968
|
+
this.queue.splice(clamped, 0, message);
|
|
5969
|
+
}
|
|
5970
|
+
resumeIfIdle() {
|
|
5971
|
+
if (this.processing || this.queue.length === 0) return;
|
|
5972
|
+
void this.processNextInQueue().catch((error) => {
|
|
5973
|
+
console.error("[MessageQueue] Error resuming idle queue:", error);
|
|
5974
|
+
});
|
|
5975
|
+
}
|
|
5954
5976
|
/**
|
|
5955
5977
|
* Move a message to a new position in the queue
|
|
5956
5978
|
* @returns true if the message was found and moved
|
|
@@ -6072,6 +6094,28 @@ var CodingAgentManager = class {
|
|
|
6072
6094
|
await this.initialized;
|
|
6073
6095
|
return this.messageQueue.enqueue(request);
|
|
6074
6096
|
}
|
|
6097
|
+
// Take the message off the queue before steering: a turn completing mid-steer could otherwise pop it too.
|
|
6098
|
+
async steerFromQueue(messageId) {
|
|
6099
|
+
await this.initialized;
|
|
6100
|
+
const taken = this.messageQueue.takeFromQueue(messageId);
|
|
6101
|
+
if (!taken) {
|
|
6102
|
+
return { success: false, queue: this.getQueue() };
|
|
6103
|
+
}
|
|
6104
|
+
let steered = false;
|
|
6105
|
+
try {
|
|
6106
|
+
steered = await this.steerRequest(taken.message);
|
|
6107
|
+
} catch (error) {
|
|
6108
|
+
console.error("[CodingAgentManager] steerRequest threw, keeping message queued:", error);
|
|
6109
|
+
}
|
|
6110
|
+
if (!steered) {
|
|
6111
|
+
this.messageQueue.restoreToQueue(taken.message, taken.index);
|
|
6112
|
+
this.messageQueue.resumeIfIdle();
|
|
6113
|
+
}
|
|
6114
|
+
return { success: steered, queue: this.getQueue() };
|
|
6115
|
+
}
|
|
6116
|
+
steerRequest(_request) {
|
|
6117
|
+
return Promise.resolve(false);
|
|
6118
|
+
}
|
|
6075
6119
|
emitContextUsage(payload) {
|
|
6076
6120
|
const event = {
|
|
6077
6121
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6574,15 +6618,17 @@ var CodexHistoryFile = class {
|
|
|
6574
6618
|
this.filePath = filePath;
|
|
6575
6619
|
}
|
|
6576
6620
|
filePath;
|
|
6577
|
-
|
|
6621
|
+
writeLock = new AsyncLock();
|
|
6578
6622
|
/** Best-effort ordered append; failures must not disrupt the turn. */
|
|
6579
6623
|
append(event) {
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6624
|
+
void this.writeLock.run(
|
|
6625
|
+
() => appendFile2(this.filePath, JSON.stringify(event) + "\n", "utf-8").catch((error) => {
|
|
6626
|
+
console.error("[CodexHistoryFile] Failed to append event:", error);
|
|
6627
|
+
})
|
|
6628
|
+
);
|
|
6583
6629
|
}
|
|
6584
6630
|
async flush() {
|
|
6585
|
-
await this.
|
|
6631
|
+
await this.writeLock.drain();
|
|
6586
6632
|
}
|
|
6587
6633
|
async load() {
|
|
6588
6634
|
try {
|
|
@@ -7893,7 +7939,7 @@ var AspClient = class {
|
|
|
7893
7939
|
// src/managers/codex-asp/app-server-process.ts
|
|
7894
7940
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
7895
7941
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
7896
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
7942
|
+
var ENGINE_PACKAGE_VERSION = "0.1.398";
|
|
7897
7943
|
var INITIALIZE_METHOD = "initialize";
|
|
7898
7944
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
7899
7945
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -8146,6 +8192,7 @@ var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
|
|
|
8146
8192
|
var THREAD_SETTINGS_UPDATE_METHOD = "thread/settings/update";
|
|
8147
8193
|
var TURN_START_METHOD = "turn/start";
|
|
8148
8194
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
8195
|
+
var TURN_STEER_METHOD = "turn/steer";
|
|
8149
8196
|
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
8150
8197
|
var MODEL_LIST_METHOD = "model/list";
|
|
8151
8198
|
var SKILLS_LIST_METHOD = "skills/list";
|
|
@@ -8759,6 +8806,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8759
8806
|
activeServiceTier;
|
|
8760
8807
|
slashCommandsCache = null;
|
|
8761
8808
|
slashCommandsRequest = null;
|
|
8809
|
+
steeredTempImagePaths = [];
|
|
8810
|
+
steerLock = new AsyncLock();
|
|
8762
8811
|
constructor(options) {
|
|
8763
8812
|
super(options);
|
|
8764
8813
|
this.historyFile = options.historyFilePath ? new CodexHistoryFile(options.historyFilePath) : null;
|
|
@@ -8789,6 +8838,36 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8789
8838
|
console.warn("[CodexAspManager] Failed to interrupt active turn:", error);
|
|
8790
8839
|
}
|
|
8791
8840
|
}
|
|
8841
|
+
// Serialize so concurrent steers can't race one `turn/steer` RPC ahead of another.
|
|
8842
|
+
steerRequest(request) {
|
|
8843
|
+
return this.steerLock.run(() => this.steerActiveTurnLocked(request));
|
|
8844
|
+
}
|
|
8845
|
+
async steerActiveTurnLocked(request) {
|
|
8846
|
+
const threadId = this.currentThreadId;
|
|
8847
|
+
const expectedTurnId = this.activeTurnId;
|
|
8848
|
+
if (!threadId || !expectedTurnId || !this.messageQueue.isProcessing()) return false;
|
|
8849
|
+
if (this.isCompacting()) return false;
|
|
8850
|
+
if (getGoalCommand(request.message, request.goalMode)) return false;
|
|
8851
|
+
const { input, tempImagePaths } = await buildTurnInput(request);
|
|
8852
|
+
try {
|
|
8853
|
+
const host = await getCodexAspHost();
|
|
8854
|
+
await host.client.request(
|
|
8855
|
+
TURN_STEER_METHOD,
|
|
8856
|
+
{ threadId, expectedTurnId, input }
|
|
8857
|
+
);
|
|
8858
|
+
} catch (error) {
|
|
8859
|
+
await removeTempImageFiles(tempImagePaths);
|
|
8860
|
+
console.warn("[CodexAspManager] Failed to steer active turn, keeping message queued:", error);
|
|
8861
|
+
return false;
|
|
8862
|
+
}
|
|
8863
|
+
if (this.activeTurnId === expectedTurnId) {
|
|
8864
|
+
this.steeredTempImagePaths.push(...tempImagePaths);
|
|
8865
|
+
} else {
|
|
8866
|
+
await removeTempImageFiles(tempImagePaths);
|
|
8867
|
+
}
|
|
8868
|
+
this.recordUserMessageEvent(request);
|
|
8869
|
+
return true;
|
|
8870
|
+
}
|
|
8792
8871
|
async getHistory() {
|
|
8793
8872
|
if (!this.currentThreadId) {
|
|
8794
8873
|
return { thread_id: null, events: [], goal: null };
|
|
@@ -8879,18 +8958,21 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8879
8958
|
this.recordGoalChange(null, true);
|
|
8880
8959
|
return null;
|
|
8881
8960
|
}
|
|
8961
|
+
recordUserMessageEvent(request, extraPayload = {}) {
|
|
8962
|
+
const images = imageContentToUserMessageImages(request.images);
|
|
8963
|
+
this.recordHistoryEvent("event_msg", {
|
|
8964
|
+
type: "user_message",
|
|
8965
|
+
message: request.message,
|
|
8966
|
+
...images ? { images } : {},
|
|
8967
|
+
...extraPayload
|
|
8968
|
+
});
|
|
8969
|
+
}
|
|
8882
8970
|
async processMessageInternal(request) {
|
|
8883
8971
|
let userMessageRecorded = false;
|
|
8884
8972
|
const recordUserMessage = (extraPayload = {}) => {
|
|
8885
8973
|
if (userMessageRecorded) return;
|
|
8886
8974
|
userMessageRecorded = true;
|
|
8887
|
-
|
|
8888
|
-
this.recordHistoryEvent("event_msg", {
|
|
8889
|
-
type: "user_message",
|
|
8890
|
-
message: request.message,
|
|
8891
|
-
...images ? { images } : {},
|
|
8892
|
-
...extraPayload
|
|
8893
|
-
});
|
|
8975
|
+
this.recordUserMessageEvent(request, extraPayload);
|
|
8894
8976
|
};
|
|
8895
8977
|
const goalCommand = getGoalCommand(request.message, request.goalMode);
|
|
8896
8978
|
const dispatch = async () => {
|
|
@@ -8924,6 +9006,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8924
9006
|
this.transcriptUpdateCoalescer.flushPending();
|
|
8925
9007
|
this.transcriptUpdateCoalescer.dispose();
|
|
8926
9008
|
this.activeTurnId = null;
|
|
9009
|
+
await removeTempImageFiles(this.steeredTempImagePaths.splice(0));
|
|
8927
9010
|
await this.historyFile?.flush();
|
|
8928
9011
|
await this.onTurnComplete();
|
|
8929
9012
|
}
|
|
@@ -11129,6 +11212,9 @@ var RelayManager = class {
|
|
|
11129
11212
|
reorderQueue(messageId, newPosition) {
|
|
11130
11213
|
return this.inner.reorderQueue(messageId, newPosition);
|
|
11131
11214
|
}
|
|
11215
|
+
steerFromQueue(messageId) {
|
|
11216
|
+
return this.inner.steerFromQueue(messageId);
|
|
11217
|
+
}
|
|
11132
11218
|
};
|
|
11133
11219
|
|
|
11134
11220
|
// src/services/keep-alive-service.ts
|
|
@@ -11808,6 +11894,10 @@ var ChatService = class {
|
|
|
11808
11894
|
queue: chat.provider.getQueue()
|
|
11809
11895
|
};
|
|
11810
11896
|
}
|
|
11897
|
+
steerFromQueue(chatId, messageId) {
|
|
11898
|
+
const chat = this.requireChat(chatId);
|
|
11899
|
+
return chat.provider.steerFromQueue(messageId);
|
|
11900
|
+
}
|
|
11811
11901
|
async respondToToolInput(chatId, requestId, selectionId) {
|
|
11812
11902
|
const chat = this.requireChat(chatId);
|
|
11813
11903
|
if (!chat.provider.respondToToolInput) {
|
|
@@ -13162,6 +13252,17 @@ function createV1Routes(deps) {
|
|
|
13162
13252
|
return c.json(jsonError("Failed to remove from queue", error instanceof Error ? error.message : "Unknown error"), 500);
|
|
13163
13253
|
}
|
|
13164
13254
|
});
|
|
13255
|
+
app2.post("/chats/:chatId/queue/:messageId/steer", async (c) => {
|
|
13256
|
+
try {
|
|
13257
|
+
const result = await deps.chatService.steerFromQueue(c.req.param("chatId"), c.req.param("messageId"));
|
|
13258
|
+
return c.json(result);
|
|
13259
|
+
} catch (error) {
|
|
13260
|
+
if (error instanceof ChatNotFoundError) {
|
|
13261
|
+
return c.json(jsonError("Chat not found", error.message), 404);
|
|
13262
|
+
}
|
|
13263
|
+
return c.json(jsonError("Failed to steer queued message", error instanceof Error ? error.message : "Unknown error"), 500);
|
|
13264
|
+
}
|
|
13265
|
+
});
|
|
13165
13266
|
app2.patch("/chats/:chatId/queue/reorder", async (c) => {
|
|
13166
13267
|
try {
|
|
13167
13268
|
const body = await c.req.json();
|