replicas-engine 0.1.133 → 0.1.134
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 +165 -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 = "01-05-2026-royal-york-v1";
|
|
1170
1173
|
|
|
1171
1174
|
// ../shared/src/engine/types.ts
|
|
1172
1175
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -2828,12 +2831,40 @@ function isJsonlEvent2(value) {
|
|
|
2828
2831
|
function sleep(ms) {
|
|
2829
2832
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2830
2833
|
}
|
|
2834
|
+
function extractRateLimitsSnapshot(parsed) {
|
|
2835
|
+
if (!isRecord(parsed)) return null;
|
|
2836
|
+
const payload = isRecord(parsed.payload) ? parsed.payload : parsed;
|
|
2837
|
+
const rateLimits = isRecord(payload.rate_limits) ? payload.rate_limits : null;
|
|
2838
|
+
if (!rateLimits) return null;
|
|
2839
|
+
const credits = isRecord(rateLimits.credits) ? rateLimits.credits : null;
|
|
2840
|
+
const hasCredits = credits && typeof credits.has_credits === "boolean" ? credits.has_credits : null;
|
|
2841
|
+
const unlimited = credits && typeof credits.unlimited === "boolean" ? credits.unlimited : null;
|
|
2842
|
+
const balance = credits && typeof credits.balance === "string" ? credits.balance : null;
|
|
2843
|
+
if (unlimited === true) return null;
|
|
2844
|
+
const rateLimitResetType = typeof rateLimits.rate_limit_reached_type === "string" && rateLimits.rate_limit_reached_type.length > 0 ? rateLimits.rate_limit_reached_type : null;
|
|
2845
|
+
const planType = typeof rateLimits.plan_type === "string" ? rateLimits.plan_type : null;
|
|
2846
|
+
let state = "ok";
|
|
2847
|
+
if (hasCredits === false) {
|
|
2848
|
+
state = "out_of_credits";
|
|
2849
|
+
} else if (rateLimitResetType !== null) {
|
|
2850
|
+
state = "rate_limited";
|
|
2851
|
+
}
|
|
2852
|
+
return { state, balance, rateLimitResetType, planType };
|
|
2853
|
+
}
|
|
2831
2854
|
var CodexManager = class extends CodingAgentManager {
|
|
2832
2855
|
codex;
|
|
2833
2856
|
currentThreadId = null;
|
|
2834
2857
|
currentThread = null;
|
|
2835
2858
|
tempImageDir;
|
|
2836
2859
|
activeAbortController = null;
|
|
2860
|
+
/** Most recent quota state observed from a rollout `rate_limits` payload. */
|
|
2861
|
+
latestQuotaState = "ok";
|
|
2862
|
+
/** Last state actually emitted to the UI. Drives dedup so seeded historical state still emits the first time it surfaces in a turn. */
|
|
2863
|
+
lastEmittedQuotaState = "ok";
|
|
2864
|
+
/** Last full snapshot, retained so we can re-emit when a user retries while blocked. */
|
|
2865
|
+
latestQuotaSnapshot = null;
|
|
2866
|
+
/** When true, new turns short-circuit instead of running. Set by `out_of_credits`. */
|
|
2867
|
+
quotaBlocked = false;
|
|
2837
2868
|
constructor(options) {
|
|
2838
2869
|
super(options);
|
|
2839
2870
|
const codexApiKey = resolveCodexApiKey();
|
|
@@ -2850,6 +2881,73 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2850
2881
|
console.log(`[CodexManager] Restored thread ID from persisted state: ${this.currentThreadId}`);
|
|
2851
2882
|
}
|
|
2852
2883
|
}
|
|
2884
|
+
/**
|
|
2885
|
+
* One-shot pass over the current session's rollout jsonl to find the most
|
|
2886
|
+
* recent `rate_limits` entry and emit a quota state if it has changed.
|
|
2887
|
+
* Used after `runStreamed` throws — the tail loop may not have pumped the
|
|
2888
|
+
* fatal line before the SDK exited.
|
|
2889
|
+
*/
|
|
2890
|
+
async flushQuotaSnapshotFromCurrentSession() {
|
|
2891
|
+
if (!this.currentThreadId) return;
|
|
2892
|
+
try {
|
|
2893
|
+
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
2894
|
+
if (!sessionFile) return;
|
|
2895
|
+
const content = await readFile7(sessionFile, "utf-8");
|
|
2896
|
+
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
2897
|
+
let latest = null;
|
|
2898
|
+
for (const line of lines) {
|
|
2899
|
+
try {
|
|
2900
|
+
const parsed = JSON.parse(line);
|
|
2901
|
+
const snapshot = extractRateLimitsSnapshot(parsed);
|
|
2902
|
+
if (snapshot) {
|
|
2903
|
+
latest = snapshot;
|
|
2904
|
+
}
|
|
2905
|
+
} catch {
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
if (latest) {
|
|
2909
|
+
this.emitQuotaStatus(latest);
|
|
2910
|
+
}
|
|
2911
|
+
} catch (error) {
|
|
2912
|
+
console.warn("[CodexManager] Failed to flush quota snapshot from session:", error);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Emit a synthetic codex-quota-status AgentEvent and update local state.
|
|
2917
|
+
* Dedupes against `lastEmittedQuotaState` (what the UI has actually seen),
|
|
2918
|
+
* not `latestQuotaState`, so a state primed silently by `seedSeenLines` on
|
|
2919
|
+
* engine restart still emits the first time it surfaces in a turn. Pass
|
|
2920
|
+
* `force` to re-emit on a retry so users see the banner reappear.
|
|
2921
|
+
*/
|
|
2922
|
+
emitQuotaStatus(snapshot, force = false) {
|
|
2923
|
+
const stateChanged = snapshot.state !== this.lastEmittedQuotaState;
|
|
2924
|
+
if (!stateChanged && !force) {
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
this.latestQuotaState = snapshot.state;
|
|
2928
|
+
this.lastEmittedQuotaState = snapshot.state;
|
|
2929
|
+
this.quotaBlocked = snapshot.state === "out_of_credits";
|
|
2930
|
+
this.latestQuotaSnapshot = snapshot;
|
|
2931
|
+
const payload = {
|
|
2932
|
+
state: snapshot.state,
|
|
2933
|
+
balance: snapshot.balance,
|
|
2934
|
+
rateLimitResetType: snapshot.rateLimitResetType,
|
|
2935
|
+
planType: snapshot.planType
|
|
2936
|
+
};
|
|
2937
|
+
this.onEvent({
|
|
2938
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2939
|
+
type: CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
2940
|
+
payload
|
|
2941
|
+
});
|
|
2942
|
+
if (!stateChanged) return;
|
|
2943
|
+
if (snapshot.state === "out_of_credits") {
|
|
2944
|
+
console.warn("[CodexManager] Codex account out of credits \u2014 pausing turn processing.");
|
|
2945
|
+
} else if (snapshot.state === "rate_limited") {
|
|
2946
|
+
console.warn(`[CodexManager] Codex rate limit reached (${snapshot.rateLimitResetType}).`);
|
|
2947
|
+
} else {
|
|
2948
|
+
console.log("[CodexManager] Codex quota recovered.");
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2853
2951
|
async interruptActiveTurn() {
|
|
2854
2952
|
if (this.activeAbortController) {
|
|
2855
2953
|
this.activeAbortController.abort();
|
|
@@ -2908,6 +3006,18 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2908
3006
|
* Internal method that actually processes the message
|
|
2909
3007
|
*/
|
|
2910
3008
|
async processMessageInternal(request) {
|
|
3009
|
+
if (this.quotaBlocked && this.latestQuotaSnapshot) {
|
|
3010
|
+
await this.flushQuotaSnapshotFromCurrentSession();
|
|
3011
|
+
if (this.quotaBlocked && this.latestQuotaSnapshot) {
|
|
3012
|
+
this.emitQuotaStatus(this.latestQuotaSnapshot, true);
|
|
3013
|
+
try {
|
|
3014
|
+
await this.onTurnComplete();
|
|
3015
|
+
} catch (error) {
|
|
3016
|
+
console.error("[CodexManager] onTurnComplete failed during quota-blocked turn:", error);
|
|
3017
|
+
}
|
|
3018
|
+
return;
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
2911
3021
|
const {
|
|
2912
3022
|
message,
|
|
2913
3023
|
model,
|
|
@@ -2944,8 +3054,8 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2944
3054
|
this.currentThread = this.codex.resumeThread(this.currentThreadId, threadOptions);
|
|
2945
3055
|
} else {
|
|
2946
3056
|
this.currentThread = this.codex.startThread(threadOptions);
|
|
2947
|
-
const { events
|
|
2948
|
-
for await (const event of
|
|
3057
|
+
const { events } = await this.currentThread.runStreamed("Hello", { signal: abortController.signal });
|
|
3058
|
+
for await (const event of events) {
|
|
2949
3059
|
if (event.type === "thread.started") {
|
|
2950
3060
|
this.currentThreadId = event.thread_id;
|
|
2951
3061
|
await this.onSaveSessionId(this.currentThreadId);
|
|
@@ -2969,38 +3079,47 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2969
3079
|
} else {
|
|
2970
3080
|
input = message;
|
|
2971
3081
|
}
|
|
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(() => {
|
|
3082
|
+
try {
|
|
3083
|
+
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
3084
|
+
let latestThoughtEvent = null;
|
|
3085
|
+
for await (const event of events) {
|
|
3086
|
+
if (linearSessionId) {
|
|
3087
|
+
const plan = extractPlanFromCodexEvent(event);
|
|
3088
|
+
if (plan) {
|
|
3089
|
+
monolithService.sendEvent({
|
|
3090
|
+
type: "agent_plan_update",
|
|
3091
|
+
payload: { linearSessionId, plan }
|
|
3092
|
+
}).catch(() => {
|
|
2988
3093
|
});
|
|
2989
3094
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
}
|
|
3095
|
+
const linearEvent = convertCodexEvent(event, linearSessionId);
|
|
3096
|
+
if (linearEvent) {
|
|
3097
|
+
if (latestThoughtEvent) {
|
|
3098
|
+
monolithService.sendEvent({ type: "agent_update", payload: latestThoughtEvent }).catch(() => {
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
if (isLinearThoughtEvent2(linearEvent)) {
|
|
3102
|
+
latestThoughtEvent = linearEvent;
|
|
3103
|
+
} else {
|
|
3104
|
+
latestThoughtEvent = null;
|
|
3105
|
+
monolithService.sendEvent({ type: "agent_update", payload: linearEvent }).catch(() => {
|
|
3106
|
+
});
|
|
3107
|
+
}
|
|
2996
3108
|
}
|
|
2997
3109
|
}
|
|
2998
3110
|
}
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
}
|
|
3111
|
+
if (linearSessionId && latestThoughtEvent) {
|
|
3112
|
+
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
3113
|
+
monolithService.sendEvent({ type: "agent_update", payload: responseEvent }).catch(() => {
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
} catch (error) {
|
|
3117
|
+
await this.flushQuotaSnapshotFromCurrentSession();
|
|
3118
|
+
if (this.quotaBlocked) {
|
|
3119
|
+
console.warn("[CodexManager] runStreamed failed while quota was blocked \u2014 surfacing as quota state.");
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
throw error;
|
|
3004
3123
|
}
|
|
3005
3124
|
} finally {
|
|
3006
3125
|
if (stopTail) {
|
|
@@ -3100,8 +3219,20 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
3100
3219
|
try {
|
|
3101
3220
|
const content = await readFile7(sessionFile, "utf-8");
|
|
3102
3221
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
3222
|
+
let latest = null;
|
|
3103
3223
|
for (const line of lines) {
|
|
3104
3224
|
seenLines.add(line);
|
|
3225
|
+
try {
|
|
3226
|
+
const parsed = JSON.parse(line);
|
|
3227
|
+
const snapshot = extractRateLimitsSnapshot(parsed);
|
|
3228
|
+
if (snapshot) latest = snapshot;
|
|
3229
|
+
} catch {
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
if (latest) {
|
|
3233
|
+
this.latestQuotaState = latest.state;
|
|
3234
|
+
this.latestQuotaSnapshot = latest;
|
|
3235
|
+
this.quotaBlocked = latest.state === "out_of_credits";
|
|
3105
3236
|
}
|
|
3106
3237
|
} catch {
|
|
3107
3238
|
}
|
|
@@ -3121,6 +3252,10 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
3121
3252
|
seenLines.add(trimmed);
|
|
3122
3253
|
try {
|
|
3123
3254
|
const parsed = JSON.parse(trimmed);
|
|
3255
|
+
const snapshot = extractRateLimitsSnapshot(parsed);
|
|
3256
|
+
if (snapshot) {
|
|
3257
|
+
this.emitQuotaStatus(snapshot);
|
|
3258
|
+
}
|
|
3124
3259
|
if (isJsonlEvent2(parsed)) {
|
|
3125
3260
|
this.onEvent(parsed);
|
|
3126
3261
|
emitted += 1;
|