replicas-engine 0.1.397 → 0.1.399
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 +130 -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-
|
|
487
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-06-v1";
|
|
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);
|
|
@@ -4674,6 +4680,7 @@ var EnvironmentDetailsService = class {
|
|
|
4674
4680
|
detectGitLabAccessConfigured()
|
|
4675
4681
|
]);
|
|
4676
4682
|
details.engineVersion = E2B_TEMPLATE_NAME;
|
|
4683
|
+
details.supportsMidTurnSteering = true;
|
|
4677
4684
|
details.claudeAuthMethod = detectClaudeAuthMethod();
|
|
4678
4685
|
details.codexAuthMethod = detectCodexAuthMethod();
|
|
4679
4686
|
details.cursorAuthMethod = detectCursorAuthMethod();
|
|
@@ -5951,6 +5958,22 @@ var MessageQueueService = class {
|
|
|
5951
5958
|
this.queue = [];
|
|
5952
5959
|
return true;
|
|
5953
5960
|
}
|
|
5961
|
+
takeFromQueue(messageId) {
|
|
5962
|
+
const index = this.queue.findIndex((m) => m.id === messageId);
|
|
5963
|
+
if (index === -1) return null;
|
|
5964
|
+
const [message] = this.queue.splice(index, 1);
|
|
5965
|
+
return { message, index };
|
|
5966
|
+
}
|
|
5967
|
+
restoreToQueue(message, index) {
|
|
5968
|
+
const clamped = Math.max(0, Math.min(index, this.queue.length));
|
|
5969
|
+
this.queue.splice(clamped, 0, message);
|
|
5970
|
+
}
|
|
5971
|
+
resumeIfIdle() {
|
|
5972
|
+
if (this.processing || this.queue.length === 0) return;
|
|
5973
|
+
void this.processNextInQueue().catch((error) => {
|
|
5974
|
+
console.error("[MessageQueue] Error resuming idle queue:", error);
|
|
5975
|
+
});
|
|
5976
|
+
}
|
|
5954
5977
|
/**
|
|
5955
5978
|
* Move a message to a new position in the queue
|
|
5956
5979
|
* @returns true if the message was found and moved
|
|
@@ -6072,6 +6095,28 @@ var CodingAgentManager = class {
|
|
|
6072
6095
|
await this.initialized;
|
|
6073
6096
|
return this.messageQueue.enqueue(request);
|
|
6074
6097
|
}
|
|
6098
|
+
// Take the message off the queue before steering: a turn completing mid-steer could otherwise pop it too.
|
|
6099
|
+
async steerFromQueue(messageId) {
|
|
6100
|
+
await this.initialized;
|
|
6101
|
+
const taken = this.messageQueue.takeFromQueue(messageId);
|
|
6102
|
+
if (!taken) {
|
|
6103
|
+
return { success: false, queue: this.getQueue() };
|
|
6104
|
+
}
|
|
6105
|
+
let steered = false;
|
|
6106
|
+
try {
|
|
6107
|
+
steered = await this.steerRequest(taken.message);
|
|
6108
|
+
} catch (error) {
|
|
6109
|
+
console.error("[CodingAgentManager] steerRequest threw, keeping message queued:", error);
|
|
6110
|
+
}
|
|
6111
|
+
if (!steered) {
|
|
6112
|
+
this.messageQueue.restoreToQueue(taken.message, taken.index);
|
|
6113
|
+
this.messageQueue.resumeIfIdle();
|
|
6114
|
+
}
|
|
6115
|
+
return { success: steered, queue: this.getQueue() };
|
|
6116
|
+
}
|
|
6117
|
+
steerRequest(_request) {
|
|
6118
|
+
return Promise.resolve(false);
|
|
6119
|
+
}
|
|
6075
6120
|
emitContextUsage(payload) {
|
|
6076
6121
|
const event = {
|
|
6077
6122
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6574,15 +6619,17 @@ var CodexHistoryFile = class {
|
|
|
6574
6619
|
this.filePath = filePath;
|
|
6575
6620
|
}
|
|
6576
6621
|
filePath;
|
|
6577
|
-
|
|
6622
|
+
writeLock = new AsyncLock();
|
|
6578
6623
|
/** Best-effort ordered append; failures must not disrupt the turn. */
|
|
6579
6624
|
append(event) {
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6625
|
+
void this.writeLock.run(
|
|
6626
|
+
() => appendFile2(this.filePath, JSON.stringify(event) + "\n", "utf-8").catch((error) => {
|
|
6627
|
+
console.error("[CodexHistoryFile] Failed to append event:", error);
|
|
6628
|
+
})
|
|
6629
|
+
);
|
|
6583
6630
|
}
|
|
6584
6631
|
async flush() {
|
|
6585
|
-
await this.
|
|
6632
|
+
await this.writeLock.drain();
|
|
6586
6633
|
}
|
|
6587
6634
|
async load() {
|
|
6588
6635
|
try {
|
|
@@ -7893,7 +7940,7 @@ var AspClient = class {
|
|
|
7893
7940
|
// src/managers/codex-asp/app-server-process.ts
|
|
7894
7941
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
7895
7942
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
7896
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
7943
|
+
var ENGINE_PACKAGE_VERSION = "0.1.399";
|
|
7897
7944
|
var INITIALIZE_METHOD = "initialize";
|
|
7898
7945
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
7899
7946
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -8146,6 +8193,7 @@ var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
|
|
|
8146
8193
|
var THREAD_SETTINGS_UPDATE_METHOD = "thread/settings/update";
|
|
8147
8194
|
var TURN_START_METHOD = "turn/start";
|
|
8148
8195
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
8196
|
+
var TURN_STEER_METHOD = "turn/steer";
|
|
8149
8197
|
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
8150
8198
|
var MODEL_LIST_METHOD = "model/list";
|
|
8151
8199
|
var SKILLS_LIST_METHOD = "skills/list";
|
|
@@ -8759,6 +8807,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8759
8807
|
activeServiceTier;
|
|
8760
8808
|
slashCommandsCache = null;
|
|
8761
8809
|
slashCommandsRequest = null;
|
|
8810
|
+
steeredTempImagePaths = [];
|
|
8811
|
+
steerLock = new AsyncLock();
|
|
8762
8812
|
constructor(options) {
|
|
8763
8813
|
super(options);
|
|
8764
8814
|
this.historyFile = options.historyFilePath ? new CodexHistoryFile(options.historyFilePath) : null;
|
|
@@ -8789,6 +8839,36 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8789
8839
|
console.warn("[CodexAspManager] Failed to interrupt active turn:", error);
|
|
8790
8840
|
}
|
|
8791
8841
|
}
|
|
8842
|
+
// Serialize so concurrent steers can't race one `turn/steer` RPC ahead of another.
|
|
8843
|
+
steerRequest(request) {
|
|
8844
|
+
return this.steerLock.run(() => this.steerActiveTurnLocked(request));
|
|
8845
|
+
}
|
|
8846
|
+
async steerActiveTurnLocked(request) {
|
|
8847
|
+
const threadId = this.currentThreadId;
|
|
8848
|
+
const expectedTurnId = this.activeTurnId;
|
|
8849
|
+
if (!threadId || !expectedTurnId || !this.messageQueue.isProcessing()) return false;
|
|
8850
|
+
if (this.isCompacting()) return false;
|
|
8851
|
+
if (getGoalCommand(request.message, request.goalMode)) return false;
|
|
8852
|
+
const { input, tempImagePaths } = await buildTurnInput(request);
|
|
8853
|
+
try {
|
|
8854
|
+
const host = await getCodexAspHost();
|
|
8855
|
+
await host.client.request(
|
|
8856
|
+
TURN_STEER_METHOD,
|
|
8857
|
+
{ threadId, expectedTurnId, input }
|
|
8858
|
+
);
|
|
8859
|
+
} catch (error) {
|
|
8860
|
+
await removeTempImageFiles(tempImagePaths);
|
|
8861
|
+
console.warn("[CodexAspManager] Failed to steer active turn, keeping message queued:", error);
|
|
8862
|
+
return false;
|
|
8863
|
+
}
|
|
8864
|
+
if (this.activeTurnId === expectedTurnId) {
|
|
8865
|
+
this.steeredTempImagePaths.push(...tempImagePaths);
|
|
8866
|
+
} else {
|
|
8867
|
+
await removeTempImageFiles(tempImagePaths);
|
|
8868
|
+
}
|
|
8869
|
+
this.recordUserMessageEvent(request);
|
|
8870
|
+
return true;
|
|
8871
|
+
}
|
|
8792
8872
|
async getHistory() {
|
|
8793
8873
|
if (!this.currentThreadId) {
|
|
8794
8874
|
return { thread_id: null, events: [], goal: null };
|
|
@@ -8879,18 +8959,21 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8879
8959
|
this.recordGoalChange(null, true);
|
|
8880
8960
|
return null;
|
|
8881
8961
|
}
|
|
8962
|
+
recordUserMessageEvent(request, extraPayload = {}) {
|
|
8963
|
+
const images = imageContentToUserMessageImages(request.images);
|
|
8964
|
+
this.recordHistoryEvent("event_msg", {
|
|
8965
|
+
type: "user_message",
|
|
8966
|
+
message: request.message,
|
|
8967
|
+
...images ? { images } : {},
|
|
8968
|
+
...extraPayload
|
|
8969
|
+
});
|
|
8970
|
+
}
|
|
8882
8971
|
async processMessageInternal(request) {
|
|
8883
8972
|
let userMessageRecorded = false;
|
|
8884
8973
|
const recordUserMessage = (extraPayload = {}) => {
|
|
8885
8974
|
if (userMessageRecorded) return;
|
|
8886
8975
|
userMessageRecorded = true;
|
|
8887
|
-
|
|
8888
|
-
this.recordHistoryEvent("event_msg", {
|
|
8889
|
-
type: "user_message",
|
|
8890
|
-
message: request.message,
|
|
8891
|
-
...images ? { images } : {},
|
|
8892
|
-
...extraPayload
|
|
8893
|
-
});
|
|
8976
|
+
this.recordUserMessageEvent(request, extraPayload);
|
|
8894
8977
|
};
|
|
8895
8978
|
const goalCommand = getGoalCommand(request.message, request.goalMode);
|
|
8896
8979
|
const dispatch = async () => {
|
|
@@ -8924,6 +9007,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8924
9007
|
this.transcriptUpdateCoalescer.flushPending();
|
|
8925
9008
|
this.transcriptUpdateCoalescer.dispose();
|
|
8926
9009
|
this.activeTurnId = null;
|
|
9010
|
+
await removeTempImageFiles(this.steeredTempImagePaths.splice(0));
|
|
8927
9011
|
await this.historyFile?.flush();
|
|
8928
9012
|
await this.onTurnComplete();
|
|
8929
9013
|
}
|
|
@@ -11129,6 +11213,9 @@ var RelayManager = class {
|
|
|
11129
11213
|
reorderQueue(messageId, newPosition) {
|
|
11130
11214
|
return this.inner.reorderQueue(messageId, newPosition);
|
|
11131
11215
|
}
|
|
11216
|
+
steerFromQueue(messageId) {
|
|
11217
|
+
return this.inner.steerFromQueue(messageId);
|
|
11218
|
+
}
|
|
11132
11219
|
};
|
|
11133
11220
|
|
|
11134
11221
|
// src/services/keep-alive-service.ts
|
|
@@ -11808,6 +11895,10 @@ var ChatService = class {
|
|
|
11808
11895
|
queue: chat.provider.getQueue()
|
|
11809
11896
|
};
|
|
11810
11897
|
}
|
|
11898
|
+
steerFromQueue(chatId, messageId) {
|
|
11899
|
+
const chat = this.requireChat(chatId);
|
|
11900
|
+
return chat.provider.steerFromQueue(messageId);
|
|
11901
|
+
}
|
|
11811
11902
|
async respondToToolInput(chatId, requestId, selectionId) {
|
|
11812
11903
|
const chat = this.requireChat(chatId);
|
|
11813
11904
|
if (!chat.provider.respondToToolInput) {
|
|
@@ -13162,6 +13253,17 @@ function createV1Routes(deps) {
|
|
|
13162
13253
|
return c.json(jsonError("Failed to remove from queue", error instanceof Error ? error.message : "Unknown error"), 500);
|
|
13163
13254
|
}
|
|
13164
13255
|
});
|
|
13256
|
+
app2.post("/chats/:chatId/queue/:messageId/steer", async (c) => {
|
|
13257
|
+
try {
|
|
13258
|
+
const result = await deps.chatService.steerFromQueue(c.req.param("chatId"), c.req.param("messageId"));
|
|
13259
|
+
return c.json(result);
|
|
13260
|
+
} catch (error) {
|
|
13261
|
+
if (error instanceof ChatNotFoundError) {
|
|
13262
|
+
return c.json(jsonError("Chat not found", error.message), 404);
|
|
13263
|
+
}
|
|
13264
|
+
return c.json(jsonError("Failed to steer queued message", error instanceof Error ? error.message : "Unknown error"), 500);
|
|
13265
|
+
}
|
|
13266
|
+
});
|
|
13165
13267
|
app2.patch("/chats/:chatId/queue/reorder", async (c) => {
|
|
13166
13268
|
try {
|
|
13167
13269
|
const body = await c.req.json();
|