replicas-engine 0.1.133 → 0.1.135
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 +175 -30
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -967,6 +967,9 @@ import { homedir as homedir5 } from "os";
|
|
|
967
967
|
import { exec } from "child_process";
|
|
968
968
|
import { promisify as promisify2 } from "util";
|
|
969
969
|
|
|
970
|
+
// ../shared/src/event.ts
|
|
971
|
+
var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
|
|
972
|
+
|
|
970
973
|
// ../shared/src/pricing.ts
|
|
971
974
|
var PLANS = {
|
|
972
975
|
hobby: {
|
|
@@ -1166,7 +1169,7 @@ function parseReplicasConfigString(content, filename) {
|
|
|
1166
1169
|
}
|
|
1167
1170
|
|
|
1168
1171
|
// ../shared/src/engine/environment.ts
|
|
1169
|
-
var DAYTONA_SNAPSHOT_ID = "
|
|
1172
|
+
var DAYTONA_SNAPSHOT_ID = "02-05-2026-royal-york-v1";
|
|
1170
1173
|
|
|
1171
1174
|
// ../shared/src/engine/types.ts
|
|
1172
1175
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -1180,6 +1183,16 @@ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
|
1180
1183
|
var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
1181
1184
|
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
1182
1185
|
|
|
1186
|
+
// ../shared/src/audit-log.ts
|
|
1187
|
+
var AUDIT_LOG_ACTION = {
|
|
1188
|
+
CREATE: "create",
|
|
1189
|
+
UPDATE: "update",
|
|
1190
|
+
DELETE: "delete",
|
|
1191
|
+
CONNECT: "connect",
|
|
1192
|
+
DISCONNECT: "disconnect"
|
|
1193
|
+
};
|
|
1194
|
+
var AUDIT_LOG_ACTIONS = Object.values(AUDIT_LOG_ACTION);
|
|
1195
|
+
|
|
1183
1196
|
// ../shared/src/routes/environment.ts
|
|
1184
1197
|
var RESERVED_MCP_NAME_PREFIXES = ["relay-", "replicas-"];
|
|
1185
1198
|
function isReservedMcpName(name) {
|
|
@@ -2828,12 +2841,40 @@ function isJsonlEvent2(value) {
|
|
|
2828
2841
|
function sleep(ms) {
|
|
2829
2842
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2830
2843
|
}
|
|
2844
|
+
function extractRateLimitsSnapshot(parsed) {
|
|
2845
|
+
if (!isRecord(parsed)) return null;
|
|
2846
|
+
const payload = isRecord(parsed.payload) ? parsed.payload : parsed;
|
|
2847
|
+
const rateLimits = isRecord(payload.rate_limits) ? payload.rate_limits : null;
|
|
2848
|
+
if (!rateLimits) return null;
|
|
2849
|
+
const credits = isRecord(rateLimits.credits) ? rateLimits.credits : null;
|
|
2850
|
+
const hasCredits = credits && typeof credits.has_credits === "boolean" ? credits.has_credits : null;
|
|
2851
|
+
const unlimited = credits && typeof credits.unlimited === "boolean" ? credits.unlimited : null;
|
|
2852
|
+
const balance = credits && typeof credits.balance === "string" ? credits.balance : null;
|
|
2853
|
+
if (unlimited === true) return null;
|
|
2854
|
+
const rateLimitResetType = typeof rateLimits.rate_limit_reached_type === "string" && rateLimits.rate_limit_reached_type.length > 0 ? rateLimits.rate_limit_reached_type : null;
|
|
2855
|
+
const planType = typeof rateLimits.plan_type === "string" ? rateLimits.plan_type : null;
|
|
2856
|
+
let state = "ok";
|
|
2857
|
+
if (hasCredits === false) {
|
|
2858
|
+
state = "out_of_credits";
|
|
2859
|
+
} else if (rateLimitResetType !== null) {
|
|
2860
|
+
state = "rate_limited";
|
|
2861
|
+
}
|
|
2862
|
+
return { state, balance, rateLimitResetType, planType };
|
|
2863
|
+
}
|
|
2831
2864
|
var CodexManager = class extends CodingAgentManager {
|
|
2832
2865
|
codex;
|
|
2833
2866
|
currentThreadId = null;
|
|
2834
2867
|
currentThread = null;
|
|
2835
2868
|
tempImageDir;
|
|
2836
2869
|
activeAbortController = null;
|
|
2870
|
+
/** Most recent quota state observed from a rollout `rate_limits` payload. */
|
|
2871
|
+
latestQuotaState = "ok";
|
|
2872
|
+
/** Last state actually emitted to the UI. Drives dedup so seeded historical state still emits the first time it surfaces in a turn. */
|
|
2873
|
+
lastEmittedQuotaState = "ok";
|
|
2874
|
+
/** Last full snapshot, retained so we can re-emit when a user retries while blocked. */
|
|
2875
|
+
latestQuotaSnapshot = null;
|
|
2876
|
+
/** When true, new turns short-circuit instead of running. Set by `out_of_credits`. */
|
|
2877
|
+
quotaBlocked = false;
|
|
2837
2878
|
constructor(options) {
|
|
2838
2879
|
super(options);
|
|
2839
2880
|
const codexApiKey = resolveCodexApiKey();
|
|
@@ -2850,6 +2891,73 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2850
2891
|
console.log(`[CodexManager] Restored thread ID from persisted state: ${this.currentThreadId}`);
|
|
2851
2892
|
}
|
|
2852
2893
|
}
|
|
2894
|
+
/**
|
|
2895
|
+
* One-shot pass over the current session's rollout jsonl to find the most
|
|
2896
|
+
* recent `rate_limits` entry and emit a quota state if it has changed.
|
|
2897
|
+
* Used after `runStreamed` throws — the tail loop may not have pumped the
|
|
2898
|
+
* fatal line before the SDK exited.
|
|
2899
|
+
*/
|
|
2900
|
+
async flushQuotaSnapshotFromCurrentSession() {
|
|
2901
|
+
if (!this.currentThreadId) return;
|
|
2902
|
+
try {
|
|
2903
|
+
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
2904
|
+
if (!sessionFile) return;
|
|
2905
|
+
const content = await readFile7(sessionFile, "utf-8");
|
|
2906
|
+
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
2907
|
+
let latest = null;
|
|
2908
|
+
for (const line of lines) {
|
|
2909
|
+
try {
|
|
2910
|
+
const parsed = JSON.parse(line);
|
|
2911
|
+
const snapshot = extractRateLimitsSnapshot(parsed);
|
|
2912
|
+
if (snapshot) {
|
|
2913
|
+
latest = snapshot;
|
|
2914
|
+
}
|
|
2915
|
+
} catch {
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
if (latest) {
|
|
2919
|
+
this.emitQuotaStatus(latest);
|
|
2920
|
+
}
|
|
2921
|
+
} catch (error) {
|
|
2922
|
+
console.warn("[CodexManager] Failed to flush quota snapshot from session:", error);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
/**
|
|
2926
|
+
* Emit a synthetic codex-quota-status AgentEvent and update local state.
|
|
2927
|
+
* Dedupes against `lastEmittedQuotaState` (what the UI has actually seen),
|
|
2928
|
+
* not `latestQuotaState`, so a state primed silently by `seedSeenLines` on
|
|
2929
|
+
* engine restart still emits the first time it surfaces in a turn. Pass
|
|
2930
|
+
* `force` to re-emit on a retry so users see the banner reappear.
|
|
2931
|
+
*/
|
|
2932
|
+
emitQuotaStatus(snapshot, force = false) {
|
|
2933
|
+
const stateChanged = snapshot.state !== this.lastEmittedQuotaState;
|
|
2934
|
+
if (!stateChanged && !force) {
|
|
2935
|
+
return;
|
|
2936
|
+
}
|
|
2937
|
+
this.latestQuotaState = snapshot.state;
|
|
2938
|
+
this.lastEmittedQuotaState = snapshot.state;
|
|
2939
|
+
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
2940
|
+
this.latestQuotaSnapshot = snapshot;
|
|
2941
|
+
const payload = {
|
|
2942
|
+
state: snapshot.state,
|
|
2943
|
+
balance: snapshot.balance,
|
|
2944
|
+
rateLimitResetType: snapshot.rateLimitResetType,
|
|
2945
|
+
planType: snapshot.planType
|
|
2946
|
+
};
|
|
2947
|
+
this.onEvent({
|
|
2948
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2949
|
+
type: CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
2950
|
+
payload
|
|
2951
|
+
});
|
|
2952
|
+
if (!stateChanged) return;
|
|
2953
|
+
if (snapshot.state === "out_of_credits") {
|
|
2954
|
+
console.warn("[CodexManager] Codex account out of credits \u2014 pausing turn processing.");
|
|
2955
|
+
} else if (snapshot.state === "rate_limited") {
|
|
2956
|
+
console.warn(`[CodexManager] Codex rate limit reached (${snapshot.rateLimitResetType}).`);
|
|
2957
|
+
} else {
|
|
2958
|
+
console.log("[CodexManager] Codex quota recovered.");
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2853
2961
|
async interruptActiveTurn() {
|
|
2854
2962
|
if (this.activeAbortController) {
|
|
2855
2963
|
this.activeAbortController.abort();
|
|
@@ -2908,6 +3016,18 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2908
3016
|
* Internal method that actually processes the message
|
|
2909
3017
|
*/
|
|
2910
3018
|
async processMessageInternal(request) {
|
|
3019
|
+
if (this.quotaBlocked && this.latestQuotaSnapshot) {
|
|
3020
|
+
await this.flushQuotaSnapshotFromCurrentSession();
|
|
3021
|
+
if (this.quotaBlocked && this.latestQuotaSnapshot) {
|
|
3022
|
+
this.emitQuotaStatus(this.latestQuotaSnapshot, true);
|
|
3023
|
+
try {
|
|
3024
|
+
await this.onTurnComplete();
|
|
3025
|
+
} catch (error) {
|
|
3026
|
+
console.error("[CodexManager] onTurnComplete failed during quota-blocked turn:", error);
|
|
3027
|
+
}
|
|
3028
|
+
return;
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
2911
3031
|
const {
|
|
2912
3032
|
message,
|
|
2913
3033
|
model,
|
|
@@ -2944,8 +3064,8 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2944
3064
|
this.currentThread = this.codex.resumeThread(this.currentThreadId, threadOptions);
|
|
2945
3065
|
} else {
|
|
2946
3066
|
this.currentThread = this.codex.startThread(threadOptions);
|
|
2947
|
-
const { events
|
|
2948
|
-
for await (const event of
|
|
3067
|
+
const { events } = await this.currentThread.runStreamed("Hello", { signal: abortController.signal });
|
|
3068
|
+
for await (const event of events) {
|
|
2949
3069
|
if (event.type === "thread.started") {
|
|
2950
3070
|
this.currentThreadId = event.thread_id;
|
|
2951
3071
|
await this.onSaveSessionId(this.currentThreadId);
|
|
@@ -2969,38 +3089,47 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2969
3089
|
} else {
|
|
2970
3090
|
input = message;
|
|
2971
3091
|
}
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
}
|
|
2984
|
-
const linearEvent = convertCodexEvent(event, linearSessionId);
|
|
2985
|
-
if (linearEvent) {
|
|
2986
|
-
if (latestThoughtEvent) {
|
|
2987
|
-
monolithService.sendEvent({ type: "agent_update", payload: latestThoughtEvent }).catch(() => {
|
|
3092
|
+
try {
|
|
3093
|
+
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
3094
|
+
let latestThoughtEvent = null;
|
|
3095
|
+
for await (const event of events) {
|
|
3096
|
+
if (linearSessionId) {
|
|
3097
|
+
const plan = extractPlanFromCodexEvent(event);
|
|
3098
|
+
if (plan) {
|
|
3099
|
+
monolithService.sendEvent({
|
|
3100
|
+
type: "agent_plan_update",
|
|
3101
|
+
payload: { linearSessionId, plan }
|
|
3102
|
+
}).catch(() => {
|
|
2988
3103
|
});
|
|
2989
3104
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
}
|
|
3105
|
+
const linearEvent = convertCodexEvent(event, linearSessionId);
|
|
3106
|
+
if (linearEvent) {
|
|
3107
|
+
if (latestThoughtEvent) {
|
|
3108
|
+
monolithService.sendEvent({ type: "agent_update", payload: latestThoughtEvent }).catch(() => {
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
if (isLinearThoughtEvent2(linearEvent)) {
|
|
3112
|
+
latestThoughtEvent = linearEvent;
|
|
3113
|
+
} else {
|
|
3114
|
+
latestThoughtEvent = null;
|
|
3115
|
+
monolithService.sendEvent({ type: "agent_update", payload: linearEvent }).catch(() => {
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
2996
3118
|
}
|
|
2997
3119
|
}
|
|
2998
3120
|
}
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
}
|
|
3121
|
+
if (linearSessionId && latestThoughtEvent) {
|
|
3122
|
+
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
3123
|
+
monolithService.sendEvent({ type: "agent_update", payload: responseEvent }).catch(() => {
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
} catch (error) {
|
|
3127
|
+
await this.flushQuotaSnapshotFromCurrentSession();
|
|
3128
|
+
if (this.quotaBlocked) {
|
|
3129
|
+
console.warn("[CodexManager] runStreamed failed while quota was blocked \u2014 surfacing as quota state.");
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
throw error;
|
|
3004
3133
|
}
|
|
3005
3134
|
} finally {
|
|
3006
3135
|
if (stopTail) {
|
|
@@ -3100,8 +3229,20 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
3100
3229
|
try {
|
|
3101
3230
|
const content = await readFile7(sessionFile, "utf-8");
|
|
3102
3231
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
3232
|
+
let latest = null;
|
|
3103
3233
|
for (const line of lines) {
|
|
3104
3234
|
seenLines.add(line);
|
|
3235
|
+
try {
|
|
3236
|
+
const parsed = JSON.parse(line);
|
|
3237
|
+
const snapshot = extractRateLimitsSnapshot(parsed);
|
|
3238
|
+
if (snapshot) latest = snapshot;
|
|
3239
|
+
} catch {
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
if (latest) {
|
|
3243
|
+
this.latestQuotaState = latest.state;
|
|
3244
|
+
this.latestQuotaSnapshot = latest;
|
|
3245
|
+
this.quotaBlocked = latest.state === "out_of_credits";
|
|
3105
3246
|
}
|
|
3106
3247
|
} catch {
|
|
3107
3248
|
}
|
|
@@ -3121,6 +3262,10 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
3121
3262
|
seenLines.add(trimmed);
|
|
3122
3263
|
try {
|
|
3123
3264
|
const parsed = JSON.parse(trimmed);
|
|
3265
|
+
const snapshot = extractRateLimitsSnapshot(parsed);
|
|
3266
|
+
if (snapshot) {
|
|
3267
|
+
this.emitQuotaStatus(snapshot);
|
|
3268
|
+
}
|
|
3124
3269
|
if (isJsonlEvent2(parsed)) {
|
|
3125
3270
|
this.onEvent(parsed);
|
|
3126
3271
|
emitted += 1;
|