lody 0.45.1 → 0.46.0
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/index.js +924 -307
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -45,13 +45,13 @@ import require$$1$6 from "string_decoder";
|
|
|
45
45
|
import * as http$2 from "http";
|
|
46
46
|
import http__default from "http";
|
|
47
47
|
import require$$1$7 from "https";
|
|
48
|
-
import require$$0$a, { execSync, exec, execFile as execFile$1 } from "child_process";
|
|
48
|
+
import require$$0$a, { execSync, exec, execFileSync, execFile as execFile$1 } from "child_process";
|
|
49
49
|
import { randomFillSync, randomUUID as randomUUID$1, createHash as createHash$1 } from "node:crypto";
|
|
50
50
|
import require$$0$b from "net";
|
|
51
51
|
import require$$4$3 from "tls";
|
|
52
52
|
import { i as imports, _ as __wbg_set_wasm$1, r as rawWasm, L as LoroDoc, E as EphemeralStoreWasm, U as UndoManager, c as callPendingEvents$3, a as LoroTree, b as LoroText, d as LoroMovableList, e as LoroList, f as LoroMap, g as __vite__initWasm, V as VersionVector, h as decodeImportBlobMeta, __tla as __tla_0 } from "./chunks/loro_wasm_bg-DgxHrrrp.js";
|
|
53
53
|
import * as fs$5 from "fs/promises";
|
|
54
|
-
import fs__default$1, { stat as stat$1, readFile as readFile$1 } from "fs/promises";
|
|
54
|
+
import fs__default$1, { stat as stat$1, readFile as readFile$1, statfs } from "fs/promises";
|
|
55
55
|
import fsPromises, { stat, open } from "node:fs/promises";
|
|
56
56
|
import { fileURLToPath } from "node:url";
|
|
57
57
|
import require$$2$7 from "assert";
|
|
@@ -36820,7 +36820,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36820
36820
|
return client;
|
|
36821
36821
|
}
|
|
36822
36822
|
const name = "lody";
|
|
36823
|
-
const version$4 = "0.
|
|
36823
|
+
const version$4 = "0.46.0";
|
|
36824
36824
|
const description = "Lody Agent CLI tool for managing remote command execution";
|
|
36825
36825
|
const type = "module";
|
|
36826
36826
|
const main$3 = "dist/index.js";
|
|
@@ -36864,7 +36864,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36864
36864
|
};
|
|
36865
36865
|
const optionalDependencies = {
|
|
36866
36866
|
"acp-extension-claude": "0.30.0",
|
|
36867
|
-
"acp-extension-codex": "0.
|
|
36867
|
+
"acp-extension-codex": "0.12.0"
|
|
36868
36868
|
};
|
|
36869
36869
|
const devDependencies = {
|
|
36870
36870
|
"@agentclientprotocol/sdk": "catalog:",
|
|
@@ -78436,6 +78436,7 @@ Task description:
|
|
|
78436
78436
|
"session_init_failed",
|
|
78437
78437
|
"session_restore_failed",
|
|
78438
78438
|
"session_not_found",
|
|
78439
|
+
"memory_pressure",
|
|
78439
78440
|
"acp_not_ready",
|
|
78440
78441
|
"agent_disconnected",
|
|
78441
78442
|
"turn_pre_prompt_failed",
|
|
@@ -81990,6 +81991,59 @@ ${tailedOutput}` : null;
|
|
|
81990
81991
|
gatewayBaseUrl: string$2().url().optional()
|
|
81991
81992
|
});
|
|
81992
81993
|
const LORO_STREAMS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
81994
|
+
const LORO_STREAMS_TOKEN_STORAGE_KEY_PREFIX = "lody:loroStreamsToken";
|
|
81995
|
+
function getTokenStorageKey(workspaceId) {
|
|
81996
|
+
return `${LORO_STREAMS_TOKEN_STORAGE_KEY_PREFIX}:${workspaceId}`;
|
|
81997
|
+
}
|
|
81998
|
+
function getLocalStorage() {
|
|
81999
|
+
try {
|
|
82000
|
+
const storage = globalThis.localStorage;
|
|
82001
|
+
if (storage && typeof storage.getItem === "function" && typeof storage.setItem === "function" && typeof storage.removeItem === "function") {
|
|
82002
|
+
return storage;
|
|
82003
|
+
}
|
|
82004
|
+
} catch {
|
|
82005
|
+
}
|
|
82006
|
+
return void 0;
|
|
82007
|
+
}
|
|
82008
|
+
function readCachedTokenFromStorage(workspaceId) {
|
|
82009
|
+
const storage = getLocalStorage();
|
|
82010
|
+
if (!storage) return null;
|
|
82011
|
+
try {
|
|
82012
|
+
const raw = storage.getItem(getTokenStorageKey(workspaceId));
|
|
82013
|
+
if (!raw) return null;
|
|
82014
|
+
const parsed = JSON.parse(raw);
|
|
82015
|
+
if (typeof parsed === "object" && parsed !== null && "token" in parsed && typeof parsed.token === "string" && "expiresAtMs" in parsed && typeof parsed.expiresAtMs === "number") {
|
|
82016
|
+
const result = {
|
|
82017
|
+
token: parsed.token,
|
|
82018
|
+
expiresAtMs: parsed.expiresAtMs
|
|
82019
|
+
};
|
|
82020
|
+
const gatewayBaseUrl = parsed.gatewayBaseUrl;
|
|
82021
|
+
if (typeof gatewayBaseUrl === "string") {
|
|
82022
|
+
result.gatewayBaseUrl = gatewayBaseUrl;
|
|
82023
|
+
}
|
|
82024
|
+
return result;
|
|
82025
|
+
}
|
|
82026
|
+
return null;
|
|
82027
|
+
} catch {
|
|
82028
|
+
return null;
|
|
82029
|
+
}
|
|
82030
|
+
}
|
|
82031
|
+
function writeCachedTokenToStorage(workspaceId, token2) {
|
|
82032
|
+
const storage = getLocalStorage();
|
|
82033
|
+
if (!storage) return;
|
|
82034
|
+
try {
|
|
82035
|
+
storage.setItem(getTokenStorageKey(workspaceId), JSON.stringify(token2));
|
|
82036
|
+
} catch {
|
|
82037
|
+
}
|
|
82038
|
+
}
|
|
82039
|
+
function clearCachedTokenFromStorage(workspaceId) {
|
|
82040
|
+
const storage = getLocalStorage();
|
|
82041
|
+
if (!storage) return;
|
|
82042
|
+
try {
|
|
82043
|
+
storage.removeItem(getTokenStorageKey(workspaceId));
|
|
82044
|
+
} catch {
|
|
82045
|
+
}
|
|
82046
|
+
}
|
|
81993
82047
|
const buildLoroStreamsTokenEndpoint = (baseUrl) => `${baseUrl.replace(/\/+$/g, "")}/api/loro-streams/token`;
|
|
81994
82048
|
class LoroStreamsTokenAuthError extends Error {
|
|
81995
82049
|
constructor(message, status, detail) {
|
|
@@ -82002,7 +82056,7 @@ ${tailedOutput}` : null;
|
|
|
82002
82056
|
function createLoroStreamsTokenProvider(options) {
|
|
82003
82057
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
82004
82058
|
const refreshSkewMs = options.refreshSkewMs ?? LORO_STREAMS_TOKEN_REFRESH_SKEW_MS;
|
|
82005
|
-
let cached2 =
|
|
82059
|
+
let cached2 = readCachedTokenFromStorage(options.workspaceId);
|
|
82006
82060
|
let inFlight = null;
|
|
82007
82061
|
let generation = 0;
|
|
82008
82062
|
const resolveAuthToken = async () => {
|
|
@@ -82049,6 +82103,7 @@ ${tailedOutput}` : null;
|
|
|
82049
82103
|
inFlight = fetchToken().then((nextToken) => {
|
|
82050
82104
|
if (generation === fetchGeneration) {
|
|
82051
82105
|
cached2 = nextToken;
|
|
82106
|
+
writeCachedTokenToStorage(options.workspaceId, nextToken);
|
|
82052
82107
|
}
|
|
82053
82108
|
return nextToken;
|
|
82054
82109
|
}).finally(() => {
|
|
@@ -82063,6 +82118,7 @@ ${tailedOutput}` : null;
|
|
|
82063
82118
|
cached2 = null;
|
|
82064
82119
|
inFlight = null;
|
|
82065
82120
|
generation++;
|
|
82121
|
+
clearCachedTokenFromStorage(options.workspaceId);
|
|
82066
82122
|
};
|
|
82067
82123
|
return {
|
|
82068
82124
|
getToken: async () => {
|
|
@@ -89037,8 +89093,20 @@ ${val.stack}`;
|
|
|
89037
89093
|
const patchObject = patch2;
|
|
89038
89094
|
for (const key2 of Object.keys(patchObject)) {
|
|
89039
89095
|
const rawValue = patchObject[key2];
|
|
89040
|
-
|
|
89041
|
-
if (
|
|
89096
|
+
const hadKey = base ? key2 in base : false;
|
|
89097
|
+
if (rawValue === void 0) {
|
|
89098
|
+
if (!hadKey) continue;
|
|
89099
|
+
this.metaFlock.delete([
|
|
89100
|
+
"m",
|
|
89101
|
+
docId,
|
|
89102
|
+
key2
|
|
89103
|
+
]);
|
|
89104
|
+
delete next[key2];
|
|
89105
|
+
outPatch[key2] = null;
|
|
89106
|
+
changed = true;
|
|
89107
|
+
continue;
|
|
89108
|
+
}
|
|
89109
|
+
if (jsonEquals(hadKey ? base[key2] : void 0, rawValue)) continue;
|
|
89042
89110
|
this.metaFlock.put([
|
|
89043
89111
|
"m",
|
|
89044
89112
|
docId,
|
|
@@ -91144,7 +91212,7 @@ ${val.stack}`;
|
|
|
91144
91212
|
live: "sse"
|
|
91145
91213
|
});
|
|
91146
91214
|
const controller = new AbortController();
|
|
91147
|
-
const signal = mergeAbortSignals(input2.signal, controller.signal);
|
|
91215
|
+
const signal = mergeAbortSignals$2(input2.signal, controller.signal);
|
|
91148
91216
|
try {
|
|
91149
91217
|
const response = await this.http.fetchAuthorized(requestUrl, {
|
|
91150
91218
|
method: "GET",
|
|
@@ -91647,7 +91715,7 @@ ${val.stack}`;
|
|
|
91647
91715
|
}
|
|
91648
91716
|
};
|
|
91649
91717
|
}
|
|
91650
|
-
function mergeAbortSignals(...signals2) {
|
|
91718
|
+
function mergeAbortSignals$2(...signals2) {
|
|
91651
91719
|
const active2 = signals2.filter((signal) => signal != null);
|
|
91652
91720
|
if (active2.length === 0) return;
|
|
91653
91721
|
if (active2.length === 1) return active2[0];
|
|
@@ -91666,84 +91734,6 @@ ${val.stack}`;
|
|
|
91666
91734
|
}
|
|
91667
91735
|
return controller.signal;
|
|
91668
91736
|
}
|
|
91669
|
-
function composeBeforeRemoteCursorSaveHooks(...hooks2) {
|
|
91670
|
-
const active2 = hooks2.filter((hook) => hook != null);
|
|
91671
|
-
if (active2.length === 0) return;
|
|
91672
|
-
return async (context2) => {
|
|
91673
|
-
for (const hook of active2) await hook(context2);
|
|
91674
|
-
};
|
|
91675
|
-
}
|
|
91676
|
-
async function persistRemoteCursor(input2) {
|
|
91677
|
-
const next = {
|
|
91678
|
-
...input2.cursor,
|
|
91679
|
-
updatedAtMs: (input2.now ?? Date.now)()
|
|
91680
|
-
};
|
|
91681
|
-
if (input2.beforeRemoteCursorSave != null) await input2.beforeRemoteCursorSave({
|
|
91682
|
-
cursor: next,
|
|
91683
|
-
source: input2.source
|
|
91684
|
-
});
|
|
91685
|
-
await input2.remoteCursorStore.save(next);
|
|
91686
|
-
return next;
|
|
91687
|
-
}
|
|
91688
|
-
function key(streamUrl) {
|
|
91689
|
-
return streamUrl;
|
|
91690
|
-
}
|
|
91691
|
-
function cloneJsonValue(value) {
|
|
91692
|
-
return structuredClone(value);
|
|
91693
|
-
}
|
|
91694
|
-
function cloneRemoteCursor(cursor) {
|
|
91695
|
-
return {
|
|
91696
|
-
...cursor,
|
|
91697
|
-
serverLowerBoundVersion: cloneJsonValue(cursor.serverLowerBoundVersion)
|
|
91698
|
-
};
|
|
91699
|
-
}
|
|
91700
|
-
var InMemoryRemoteCursorStore = class {
|
|
91701
|
-
data = /* @__PURE__ */ new Map();
|
|
91702
|
-
async load(streamUrl) {
|
|
91703
|
-
const value = this.data.get(key(streamUrl));
|
|
91704
|
-
return value == null ? null : cloneRemoteCursor(value);
|
|
91705
|
-
}
|
|
91706
|
-
async save(cursor) {
|
|
91707
|
-
this.data.set(key(cursor.streamUrl), cloneRemoteCursor(cursor));
|
|
91708
|
-
}
|
|
91709
|
-
async delete(streamUrl) {
|
|
91710
|
-
this.data.delete(key(streamUrl));
|
|
91711
|
-
}
|
|
91712
|
-
};
|
|
91713
|
-
function createInitialRemoteCursor(input2) {
|
|
91714
|
-
return {
|
|
91715
|
-
streamUrl: input2.streamUrl,
|
|
91716
|
-
nextOffset: input2.nextOffset,
|
|
91717
|
-
serverLowerBoundVersion: cloneJsonValue(input2.serverLowerBoundVersion),
|
|
91718
|
-
updatedAtMs: Date.now()
|
|
91719
|
-
};
|
|
91720
|
-
}
|
|
91721
|
-
const DEFAULT_RECONNECT_CONFIG = {
|
|
91722
|
-
delays: [
|
|
91723
|
-
0,
|
|
91724
|
-
500,
|
|
91725
|
-
1e3,
|
|
91726
|
-
2e3,
|
|
91727
|
-
4e3,
|
|
91728
|
-
8e3,
|
|
91729
|
-
15e3
|
|
91730
|
-
],
|
|
91731
|
-
jitterFraction: 0.2,
|
|
91732
|
-
maxAttempts: Infinity,
|
|
91733
|
-
connectTimeoutMs: 1e4,
|
|
91734
|
-
pollTimeoutMs: 3e4,
|
|
91735
|
-
liveInactivityTimeoutMs: 45e3
|
|
91736
|
-
};
|
|
91737
|
-
function applyJitter(baseMs, fraction) {
|
|
91738
|
-
if (baseMs === 0) return 0;
|
|
91739
|
-
const jitter = baseMs * fraction * (2 * Math.random() - 1);
|
|
91740
|
-
return Math.max(0, Math.round(baseMs + jitter));
|
|
91741
|
-
}
|
|
91742
|
-
function computeRetryDelay(attempt, config2 = DEFAULT_RECONNECT_CONFIG) {
|
|
91743
|
-
const { delays, jitterFraction } = config2;
|
|
91744
|
-
const base = delays[Math.min(attempt, delays.length - 1)];
|
|
91745
|
-
return applyJitter(base, jitterFraction);
|
|
91746
|
-
}
|
|
91747
91737
|
const DEFAULT_COMPACT_THRESHOLD = 64;
|
|
91748
91738
|
const DEFAULT_MIN_CAPACITY = 16;
|
|
91749
91739
|
var Deque = class {
|
|
@@ -91863,6 +91853,32 @@ ${val.stack}`;
|
|
|
91863
91853
|
this.tail = length2;
|
|
91864
91854
|
}
|
|
91865
91855
|
};
|
|
91856
|
+
const DEFAULT_RECONNECT_CONFIG = {
|
|
91857
|
+
delays: [
|
|
91858
|
+
0,
|
|
91859
|
+
500,
|
|
91860
|
+
1e3,
|
|
91861
|
+
2e3,
|
|
91862
|
+
4e3,
|
|
91863
|
+
8e3,
|
|
91864
|
+
15e3
|
|
91865
|
+
],
|
|
91866
|
+
jitterFraction: 0.2,
|
|
91867
|
+
maxAttempts: Infinity,
|
|
91868
|
+
connectTimeoutMs: 1e4,
|
|
91869
|
+
pollTimeoutMs: 3e4,
|
|
91870
|
+
liveInactivityTimeoutMs: 45e3
|
|
91871
|
+
};
|
|
91872
|
+
function applyJitter(baseMs, fraction) {
|
|
91873
|
+
if (baseMs === 0) return 0;
|
|
91874
|
+
const jitter = baseMs * fraction * (2 * Math.random() - 1);
|
|
91875
|
+
return Math.max(0, Math.round(baseMs + jitter));
|
|
91876
|
+
}
|
|
91877
|
+
function computeRetryDelay(attempt, config2 = DEFAULT_RECONNECT_CONFIG) {
|
|
91878
|
+
const { delays, jitterFraction } = config2;
|
|
91879
|
+
const base = delays[Math.min(attempt, delays.length - 1)];
|
|
91880
|
+
return applyJitter(base, jitterFraction);
|
|
91881
|
+
}
|
|
91866
91882
|
const BINARY_CONTENT_TYPE = "application/octet-stream";
|
|
91867
91883
|
function encodeItems(items) {
|
|
91868
91884
|
const total = items.reduce((sum, item) => sum + 4 + item.byteLength, 0);
|
|
@@ -92305,6 +92321,25 @@ stream:${scope2.streamId}`;
|
|
|
92305
92321
|
});
|
|
92306
92322
|
});
|
|
92307
92323
|
}
|
|
92324
|
+
function mergeAbortSignals(...signals2) {
|
|
92325
|
+
const active2 = signals2.filter((signal) => signal != null);
|
|
92326
|
+
if (active2.length === 0) return;
|
|
92327
|
+
if (active2.length === 1) return active2[0];
|
|
92328
|
+
if (typeof AbortSignal.any === "function") return AbortSignal.any(active2);
|
|
92329
|
+
const controller = new AbortController();
|
|
92330
|
+
for (const signal of active2) {
|
|
92331
|
+
if (signal.aborted) {
|
|
92332
|
+
controller.abort(signal.reason);
|
|
92333
|
+
break;
|
|
92334
|
+
}
|
|
92335
|
+
signal.addEventListener("abort", () => {
|
|
92336
|
+
controller.abort(signal.reason);
|
|
92337
|
+
}, {
|
|
92338
|
+
once: true
|
|
92339
|
+
});
|
|
92340
|
+
}
|
|
92341
|
+
return controller.signal;
|
|
92342
|
+
}
|
|
92308
92343
|
function classifyFetchError(err2) {
|
|
92309
92344
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
92310
92345
|
const status = err2 instanceof HttpError ? err2.status : void 0;
|
|
@@ -92508,7 +92543,10 @@ stream:${scope2.streamId}`;
|
|
|
92508
92543
|
streamUrl;
|
|
92509
92544
|
adapter;
|
|
92510
92545
|
client;
|
|
92546
|
+
fetchImpl;
|
|
92547
|
+
authProvider;
|
|
92511
92548
|
reconnectConfig;
|
|
92549
|
+
authRefreshInFlight;
|
|
92512
92550
|
constructor(options) {
|
|
92513
92551
|
this.streamUrl = options.streamUrl;
|
|
92514
92552
|
this.adapter = options.adapter;
|
|
@@ -92524,6 +92562,8 @@ stream:${scope2.streamId}`;
|
|
|
92524
92562
|
pollTimeoutMs: options.reconnectConfig.pollTimeoutMs
|
|
92525
92563
|
}
|
|
92526
92564
|
});
|
|
92565
|
+
this.fetchImpl = options.fetchImpl;
|
|
92566
|
+
this.authProvider = options.authProvider;
|
|
92527
92567
|
this.reconnectConfig = options.reconnectConfig;
|
|
92528
92568
|
}
|
|
92529
92569
|
async createStreamRequest() {
|
|
@@ -92625,6 +92665,166 @@ stream:${scope2.streamId}`;
|
|
|
92625
92665
|
};
|
|
92626
92666
|
throw this.toTransportFailure(error2);
|
|
92627
92667
|
}
|
|
92668
|
+
async appendItems(updates) {
|
|
92669
|
+
const result = await this.client.append({
|
|
92670
|
+
part: {
|
|
92671
|
+
contentType: BINARY_CONTENT_TYPE,
|
|
92672
|
+
body: encodeItems(updates)
|
|
92673
|
+
}
|
|
92674
|
+
});
|
|
92675
|
+
if (result.ok) return result.result.nextOffset;
|
|
92676
|
+
throw this.toTransportFailure(result.result);
|
|
92677
|
+
}
|
|
92678
|
+
async appendEphemeralUpdate(update2, signal) {
|
|
92679
|
+
if (update2.byteLength === 0) return;
|
|
92680
|
+
const response = await this.fetchAuthorized(this.streamUrl, {
|
|
92681
|
+
method: "POST",
|
|
92682
|
+
headers: {
|
|
92683
|
+
"Content-Type": BINARY_CONTENT_TYPE
|
|
92684
|
+
},
|
|
92685
|
+
body: update2,
|
|
92686
|
+
signal
|
|
92687
|
+
}, this.reconnectConfig.connectTimeoutMs, "connect");
|
|
92688
|
+
if (response.status === 200 || response.status === 204) return;
|
|
92689
|
+
throw await this.responseToEphemeralFailure(response, "ephemeral append");
|
|
92690
|
+
}
|
|
92691
|
+
async readEphemeralSse(onBootstrap, onData, signal) {
|
|
92692
|
+
const requestUrl = this.ephemeralReadEndpoint();
|
|
92693
|
+
const response = await this.fetchAuthorized(requestUrl, {
|
|
92694
|
+
method: "GET",
|
|
92695
|
+
headers: {
|
|
92696
|
+
Accept: "text/event-stream"
|
|
92697
|
+
},
|
|
92698
|
+
signal
|
|
92699
|
+
}, this.reconnectConfig.connectTimeoutMs, "connect");
|
|
92700
|
+
if (response.status === 404) return {
|
|
92701
|
+
kind: "not-found"
|
|
92702
|
+
};
|
|
92703
|
+
if (!response.ok) {
|
|
92704
|
+
if (response.status === 400) {
|
|
92705
|
+
const message = await response.text();
|
|
92706
|
+
if (isSseUnsupportedErrorText(message)) return {
|
|
92707
|
+
kind: "unsupported"
|
|
92708
|
+
};
|
|
92709
|
+
throw new HttpError(message.length > 0 ? `ephemeral live read failed: ${message}` : "ephemeral live read failed", response.status);
|
|
92710
|
+
}
|
|
92711
|
+
throw await this.responseToEphemeralFailure(response, "ephemeral live read");
|
|
92712
|
+
}
|
|
92713
|
+
if (response.body == null) throw new ProtocolError("ephemeral sse response missing body");
|
|
92714
|
+
if (!(response.headers.get("Content-Type") ?? "").toLowerCase().includes("text/event-stream")) throw new ProtocolError("ephemeral sse response must use text/event-stream content type");
|
|
92715
|
+
const dataEncoding = response.headers.get("Stream-SSE-Data-Encoding") ?? void 0;
|
|
92716
|
+
const consume = async () => {
|
|
92717
|
+
let sawBootstrap = false;
|
|
92718
|
+
for await (const event of readSseEvents(response.body)) {
|
|
92719
|
+
if (!sawBootstrap) {
|
|
92720
|
+
if (event.event !== "bootstrap") throw new ProtocolError(`ephemeral sse must start with bootstrap, got '${event.event}'`);
|
|
92721
|
+
await onBootstrap(decodeSsePayload(event.data, dataEncoding));
|
|
92722
|
+
sawBootstrap = true;
|
|
92723
|
+
continue;
|
|
92724
|
+
}
|
|
92725
|
+
if (event.event === "data") {
|
|
92726
|
+
await onData(decodeSsePayload(event.data, dataEncoding));
|
|
92727
|
+
continue;
|
|
92728
|
+
}
|
|
92729
|
+
throw new ProtocolError(`unexpected ephemeral sse event '${event.event}'`);
|
|
92730
|
+
}
|
|
92731
|
+
if (!sawBootstrap) throw new ProtocolError("ephemeral sse ended before bootstrap");
|
|
92732
|
+
return {
|
|
92733
|
+
kind: "ok"
|
|
92734
|
+
};
|
|
92735
|
+
};
|
|
92736
|
+
let abortHandler;
|
|
92737
|
+
const abortRace = signal != null && !signal.aborted ? new Promise((_2, reject) => {
|
|
92738
|
+
abortHandler = () => {
|
|
92739
|
+
response.body?.cancel().catch(() => {
|
|
92740
|
+
});
|
|
92741
|
+
reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
|
|
92742
|
+
};
|
|
92743
|
+
signal.addEventListener("abort", abortHandler, {
|
|
92744
|
+
once: true
|
|
92745
|
+
});
|
|
92746
|
+
}) : null;
|
|
92747
|
+
try {
|
|
92748
|
+
if (signal?.aborted) {
|
|
92749
|
+
await response.body.cancel().catch(() => {
|
|
92750
|
+
});
|
|
92751
|
+
throw signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
92752
|
+
}
|
|
92753
|
+
return abortRace == null ? await consume() : await Promise.race([
|
|
92754
|
+
consume(),
|
|
92755
|
+
abortRace
|
|
92756
|
+
]);
|
|
92757
|
+
} finally {
|
|
92758
|
+
if (abortHandler != null && signal != null) signal.removeEventListener("abort", abortHandler);
|
|
92759
|
+
}
|
|
92760
|
+
}
|
|
92761
|
+
async fetchAuthorized(input2, init2, timeoutMs, phase) {
|
|
92762
|
+
const firstToken = await this.resolveToken({
|
|
92763
|
+
reason: "request"
|
|
92764
|
+
});
|
|
92765
|
+
const firstResponse = await this.fetchWithTimeout(input2, {
|
|
92766
|
+
...init2,
|
|
92767
|
+
headers: this.attachAuthorization(init2.headers, firstToken)
|
|
92768
|
+
}, timeoutMs, phase);
|
|
92769
|
+
if (this.authProvider == null || firstResponse.status !== 401 && firstResponse.status !== 403) return firstResponse;
|
|
92770
|
+
await firstResponse.body?.cancel().catch(() => {
|
|
92771
|
+
});
|
|
92772
|
+
const refreshedToken = await this.refreshTokenAfterFailure(firstResponse.status, firstToken);
|
|
92773
|
+
return this.fetchWithTimeout(input2, {
|
|
92774
|
+
...init2,
|
|
92775
|
+
headers: this.attachAuthorization(init2.headers, refreshedToken)
|
|
92776
|
+
}, timeoutMs, phase);
|
|
92777
|
+
}
|
|
92778
|
+
async fetchWithTimeout(input2, init2, timeoutMs, phase) {
|
|
92779
|
+
const controller = new AbortController();
|
|
92780
|
+
const signal = mergeAbortSignals(init2.signal, controller.signal);
|
|
92781
|
+
const timer2 = setTimeout(() => {
|
|
92782
|
+
controller.abort(new TimeoutError(phase, timeoutMs));
|
|
92783
|
+
}, timeoutMs);
|
|
92784
|
+
try {
|
|
92785
|
+
return await this.fetchImpl(input2, {
|
|
92786
|
+
...init2,
|
|
92787
|
+
signal
|
|
92788
|
+
});
|
|
92789
|
+
} catch (error2) {
|
|
92790
|
+
if (controller.signal.aborted && controller.signal.reason instanceof TimeoutError) throw controller.signal.reason;
|
|
92791
|
+
throw error2;
|
|
92792
|
+
} finally {
|
|
92793
|
+
clearTimeout(timer2);
|
|
92794
|
+
}
|
|
92795
|
+
}
|
|
92796
|
+
async resolveToken(context2) {
|
|
92797
|
+
return await this.authProvider?.(context2);
|
|
92798
|
+
}
|
|
92799
|
+
async refreshTokenAfterFailure(status, previousToken) {
|
|
92800
|
+
if (this.authProvider == null) return;
|
|
92801
|
+
if (this.authRefreshInFlight == null) this.authRefreshInFlight = this.resolveToken({
|
|
92802
|
+
reason: "unauthorized",
|
|
92803
|
+
status,
|
|
92804
|
+
previousToken
|
|
92805
|
+
}).finally(() => {
|
|
92806
|
+
this.authRefreshInFlight = void 0;
|
|
92807
|
+
});
|
|
92808
|
+
return await this.authRefreshInFlight;
|
|
92809
|
+
}
|
|
92810
|
+
attachAuthorization(base, token2) {
|
|
92811
|
+
const headers = new Headers(base);
|
|
92812
|
+
if (token2 == null || token2.length === 0) {
|
|
92813
|
+
headers.delete("Authorization");
|
|
92814
|
+
return headers;
|
|
92815
|
+
}
|
|
92816
|
+
headers.set("Authorization", `Bearer ${token2}`);
|
|
92817
|
+
return headers;
|
|
92818
|
+
}
|
|
92819
|
+
async responseToEphemeralFailure(response, context2) {
|
|
92820
|
+
const message = await response.text().catch(() => "");
|
|
92821
|
+
return new HttpError(message.length > 0 ? `${context2} failed: ${message}` : `${context2} failed with status ${response.status}`, response.status);
|
|
92822
|
+
}
|
|
92823
|
+
ephemeralReadEndpoint() {
|
|
92824
|
+
const target = new URL(this.streamUrl);
|
|
92825
|
+
target.searchParams.set("live", "sse");
|
|
92826
|
+
return target.toString();
|
|
92827
|
+
}
|
|
92628
92828
|
toTransportFailure(error2) {
|
|
92629
92829
|
switch (error2.code) {
|
|
92630
92830
|
case "bad_request":
|
|
@@ -92766,6 +92966,58 @@ stream:${scope2.streamId}`;
|
|
|
92766
92966
|
};
|
|
92767
92967
|
}
|
|
92768
92968
|
};
|
|
92969
|
+
function composeBeforeRemoteCursorSaveHooks(...hooks2) {
|
|
92970
|
+
const active2 = hooks2.filter((hook) => hook != null);
|
|
92971
|
+
if (active2.length === 0) return;
|
|
92972
|
+
return async (context2) => {
|
|
92973
|
+
for (const hook of active2) await hook(context2);
|
|
92974
|
+
};
|
|
92975
|
+
}
|
|
92976
|
+
async function persistRemoteCursor(input2) {
|
|
92977
|
+
const next = {
|
|
92978
|
+
...input2.cursor,
|
|
92979
|
+
updatedAtMs: (input2.now ?? Date.now)()
|
|
92980
|
+
};
|
|
92981
|
+
if (input2.beforeRemoteCursorSave != null) await input2.beforeRemoteCursorSave({
|
|
92982
|
+
cursor: next,
|
|
92983
|
+
source: input2.source
|
|
92984
|
+
});
|
|
92985
|
+
await input2.remoteCursorStore.save(next);
|
|
92986
|
+
return next;
|
|
92987
|
+
}
|
|
92988
|
+
function key(streamUrl) {
|
|
92989
|
+
return streamUrl;
|
|
92990
|
+
}
|
|
92991
|
+
function cloneJsonValue(value) {
|
|
92992
|
+
return structuredClone(value);
|
|
92993
|
+
}
|
|
92994
|
+
function cloneRemoteCursor(cursor) {
|
|
92995
|
+
return {
|
|
92996
|
+
...cursor,
|
|
92997
|
+
serverLowerBoundVersion: cloneJsonValue(cursor.serverLowerBoundVersion)
|
|
92998
|
+
};
|
|
92999
|
+
}
|
|
93000
|
+
var InMemoryRemoteCursorStore = class {
|
|
93001
|
+
data = /* @__PURE__ */ new Map();
|
|
93002
|
+
async load(streamUrl) {
|
|
93003
|
+
const value = this.data.get(key(streamUrl));
|
|
93004
|
+
return value == null ? null : cloneRemoteCursor(value);
|
|
93005
|
+
}
|
|
93006
|
+
async save(cursor) {
|
|
93007
|
+
this.data.set(key(cursor.streamUrl), cloneRemoteCursor(cursor));
|
|
93008
|
+
}
|
|
93009
|
+
async delete(streamUrl) {
|
|
93010
|
+
this.data.delete(key(streamUrl));
|
|
93011
|
+
}
|
|
93012
|
+
};
|
|
93013
|
+
function createInitialRemoteCursor(input2) {
|
|
93014
|
+
return {
|
|
93015
|
+
streamUrl: input2.streamUrl,
|
|
93016
|
+
nextOffset: input2.nextOffset,
|
|
93017
|
+
serverLowerBoundVersion: cloneJsonValue(input2.serverLowerBoundVersion),
|
|
93018
|
+
updatedAtMs: Date.now()
|
|
93019
|
+
};
|
|
93020
|
+
}
|
|
92769
93021
|
var TransportCursorManager = class {
|
|
92770
93022
|
adapter;
|
|
92771
93023
|
decodeSnapshot;
|
|
@@ -93023,6 +93275,8 @@ stream:${scope2.streamId}`;
|
|
|
93023
93275
|
producerId = createProducerId();
|
|
93024
93276
|
producerEpoch = 0;
|
|
93025
93277
|
nextProducerSeq = 0;
|
|
93278
|
+
frozenLocalAppend;
|
|
93279
|
+
committedPendingCount;
|
|
93026
93280
|
pendingWriteOnly = new Deque();
|
|
93027
93281
|
writeOnlyCursor;
|
|
93028
93282
|
unsubscribeWriteOnlyLocal;
|
|
@@ -93202,6 +93456,8 @@ stream:${scope2.streamId}`;
|
|
|
93202
93456
|
await this.remoteCursorStore.delete?.(this.streamUrl);
|
|
93203
93457
|
this.producerEpoch = 0;
|
|
93204
93458
|
this.nextProducerSeq = 0;
|
|
93459
|
+
this.frozenLocalAppend = void 0;
|
|
93460
|
+
this.committedPendingCount = void 0;
|
|
93205
93461
|
return deleted;
|
|
93206
93462
|
})
|
|
93207
93463
|
}
|
|
@@ -93242,15 +93498,17 @@ stream:${scope2.streamId}`;
|
|
|
93242
93498
|
localVersion: this.writeOnlyCursor.serverLowerBoundVersion
|
|
93243
93499
|
}
|
|
93244
93500
|
};
|
|
93245
|
-
const
|
|
93246
|
-
|
|
93247
|
-
|
|
93501
|
+
const appended = await this.appendLocalBatchRemote(this.writeOnlyCursor, drained.batch, drained.count, "write-only");
|
|
93502
|
+
const committedCount = appended.pendingCount ?? drained.count;
|
|
93503
|
+
for (let i2 = 0; i2 < committedCount; i2 += 1) this.pendingWriteOnly.popFront();
|
|
93504
|
+
this.writeOnlyCursor = appended.cursor;
|
|
93505
|
+
this.commitProducerAck(appended.producerAck);
|
|
93248
93506
|
return {
|
|
93249
93507
|
ok: true,
|
|
93250
93508
|
value: {
|
|
93251
93509
|
appended: true,
|
|
93252
|
-
nextOffset:
|
|
93253
|
-
localVersion:
|
|
93510
|
+
nextOffset: appended.cursor.nextOffset,
|
|
93511
|
+
localVersion: appended.cursor.serverLowerBoundVersion
|
|
93254
93512
|
}
|
|
93255
93513
|
};
|
|
93256
93514
|
} catch (error2) {
|
|
@@ -93425,7 +93683,7 @@ stream:${scope2.streamId}`;
|
|
|
93425
93683
|
}
|
|
93426
93684
|
}
|
|
93427
93685
|
async syncOnce() {
|
|
93428
|
-
let cursor = (await this.resolveInitialRemoteState()).cursor;
|
|
93686
|
+
let cursor = (await this.finalizeDirectLocalAppendIfNeeded(await this.resolveInitialRemoteState())).cursor;
|
|
93429
93687
|
const localBatch = this.exportUpdates(cursor.serverLowerBoundVersion);
|
|
93430
93688
|
if (localBatch.batch != null) {
|
|
93431
93689
|
cursor = await this.appendLocalBatch(cursor, localBatch.batch);
|
|
@@ -93434,7 +93692,7 @@ stream:${scope2.streamId}`;
|
|
|
93434
93692
|
return cursor;
|
|
93435
93693
|
}
|
|
93436
93694
|
async performInitialJoinSync(preferredLiveMode) {
|
|
93437
|
-
const initial = await this.resolveInitialRemoteState();
|
|
93695
|
+
const initial = await this.finalizeDirectLocalAppendIfNeeded(await this.resolveInitialRemoteState());
|
|
93438
93696
|
let cursor = initial.cursor;
|
|
93439
93697
|
let localExportRefVersion = cursor.serverLowerBoundVersion;
|
|
93440
93698
|
let streamCursor = initial.streamCursor;
|
|
@@ -93534,17 +93792,52 @@ stream:${scope2.streamId}`;
|
|
|
93534
93792
|
};
|
|
93535
93793
|
}
|
|
93536
93794
|
}
|
|
93537
|
-
async appendLocalBatch(cursor, batch,
|
|
93538
|
-
|
|
93539
|
-
|
|
93540
|
-
|
|
93541
|
-
|
|
93795
|
+
async appendLocalBatch(cursor, batch, pendingCount) {
|
|
93796
|
+
const source = pendingCount == null ? "direct" : "pending";
|
|
93797
|
+
const committed = await this.appendLocalBatchDurably(cursor, batch, pendingCount, source);
|
|
93798
|
+
this.committedPendingCount = committed.pendingCount;
|
|
93799
|
+
return committed.cursor;
|
|
93800
|
+
}
|
|
93801
|
+
async appendLocalBatchDurably(cursor, batch, pendingCount, source = pendingCount == null ? "direct" : "pending") {
|
|
93802
|
+
const frozen = await this.getOrCreateFrozenLocalAppend(cursor, batch, pendingCount, source);
|
|
93803
|
+
const appended = await this.appendFrozenLocalBatchRemote(frozen);
|
|
93804
|
+
const saved = await this.saveRemoteCursor(appended.cursor, "local");
|
|
93805
|
+
this.commitProducerAck(appended.producerAck);
|
|
93806
|
+
return {
|
|
93807
|
+
cursor: saved,
|
|
93808
|
+
pendingCount: frozen.pendingCount
|
|
93809
|
+
};
|
|
93810
|
+
}
|
|
93811
|
+
async appendLocalBatchRemote(cursor, batch, pendingCount, source = pendingCount == null ? "direct" : "pending") {
|
|
93812
|
+
const frozen = await this.getOrCreateFrozenLocalAppend(cursor, batch, pendingCount, source);
|
|
93813
|
+
return {
|
|
93814
|
+
...await this.appendFrozenLocalBatchRemote(frozen),
|
|
93815
|
+
pendingCount: frozen.pendingCount
|
|
93542
93816
|
};
|
|
93817
|
+
}
|
|
93818
|
+
async getOrCreateFrozenLocalAppend(cursor, batch, pendingCount, source = pendingCount == null ? "direct" : "pending") {
|
|
93819
|
+
const existing = this.frozenLocalAppend;
|
|
93820
|
+
if (existing != null) {
|
|
93821
|
+
if (existing.source !== source) throw new Error(`${existing.source} local append must be finalized before ${source} append`);
|
|
93822
|
+
return existing;
|
|
93823
|
+
}
|
|
93824
|
+
const frozen = {
|
|
93825
|
+
source,
|
|
93826
|
+
baseCursor: cursor,
|
|
93827
|
+
batch,
|
|
93828
|
+
pendingCount,
|
|
93829
|
+
wireUpdates: this.encodeUpdateItems == null ? batch.updates : await this.encodeUpdateItems(batch.updates),
|
|
93830
|
+
producerSession: this.createCurrentProducerSession()
|
|
93831
|
+
};
|
|
93832
|
+
this.frozenLocalAppend = frozen;
|
|
93833
|
+
return frozen;
|
|
93834
|
+
}
|
|
93835
|
+
async appendFrozenLocalBatchRemote(frozen) {
|
|
93836
|
+
if (frozen.acknowledged != null) return frozen.acknowledged;
|
|
93543
93837
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
93544
93838
|
let result;
|
|
93545
93839
|
try {
|
|
93546
|
-
|
|
93547
|
-
result = await this.client.appendUpdates(wireUpdates, producerSession);
|
|
93840
|
+
result = await this.client.appendUpdates(frozen.wireUpdates, frozen.producerSession);
|
|
93548
93841
|
} catch (error2) {
|
|
93549
93842
|
if (isStreamNotFoundError(error2) && this.createStreamIfMissing) {
|
|
93550
93843
|
await this.ensureStreamExists();
|
|
@@ -93553,30 +93846,66 @@ stream:${scope2.streamId}`;
|
|
|
93553
93846
|
throw error2;
|
|
93554
93847
|
}
|
|
93555
93848
|
if (result.kind === "ok") {
|
|
93556
|
-
|
|
93557
|
-
|
|
93558
|
-
|
|
93559
|
-
|
|
93560
|
-
|
|
93561
|
-
|
|
93849
|
+
const appended = {
|
|
93850
|
+
cursor: {
|
|
93851
|
+
...frozen.baseCursor,
|
|
93852
|
+
nextOffset: result.nextOffset,
|
|
93853
|
+
serverLowerBoundVersion: this.adapter.mergeVersions(frozen.baseCursor.serverLowerBoundVersion, frozen.batch.version)
|
|
93854
|
+
},
|
|
93855
|
+
producerAck: {
|
|
93856
|
+
epoch: result.ackEpoch,
|
|
93857
|
+
seq: result.ackSeq
|
|
93858
|
+
}
|
|
93562
93859
|
};
|
|
93563
|
-
|
|
93860
|
+
frozen.acknowledged = appended;
|
|
93861
|
+
return appended;
|
|
93564
93862
|
}
|
|
93565
93863
|
if (result.kind === "seq-gap") {
|
|
93566
|
-
producerSession = {
|
|
93567
|
-
...producerSession,
|
|
93864
|
+
frozen.producerSession = {
|
|
93865
|
+
...frozen.producerSession,
|
|
93568
93866
|
nextProducerSeq: result.expectedSeq
|
|
93569
93867
|
};
|
|
93570
93868
|
continue;
|
|
93571
93869
|
}
|
|
93572
|
-
producerSession = {
|
|
93573
|
-
...producerSession,
|
|
93870
|
+
frozen.producerSession = {
|
|
93871
|
+
...frozen.producerSession,
|
|
93574
93872
|
producerEpoch: result.serverEpoch + 1,
|
|
93575
93873
|
nextProducerSeq: 0
|
|
93576
93874
|
};
|
|
93577
93875
|
}
|
|
93578
93876
|
throw new Error("append retry budget exhausted");
|
|
93579
93877
|
}
|
|
93878
|
+
async finalizeDirectLocalAppendIfNeeded(state2) {
|
|
93879
|
+
const frozen = this.frozenLocalAppend;
|
|
93880
|
+
if (frozen == null || frozen.pendingCount != null) return state2;
|
|
93881
|
+
const appended = await this.appendFrozenLocalBatchRemote(frozen);
|
|
93882
|
+
const saved = await this.saveRemoteCursor(appended.cursor, "local");
|
|
93883
|
+
this.commitProducerAck(appended.producerAck);
|
|
93884
|
+
return await this.catchup(saved, void 0, state2.streamCursor);
|
|
93885
|
+
}
|
|
93886
|
+
createCurrentProducerSession() {
|
|
93887
|
+
return {
|
|
93888
|
+
producerId: this.producerId,
|
|
93889
|
+
producerEpoch: this.producerEpoch,
|
|
93890
|
+
nextProducerSeq: this.nextProducerSeq
|
|
93891
|
+
};
|
|
93892
|
+
}
|
|
93893
|
+
commitProducerAck(ack) {
|
|
93894
|
+
this.producerEpoch = ack.epoch;
|
|
93895
|
+
this.nextProducerSeq = ack.seq + 1;
|
|
93896
|
+
this.frozenLocalAppend = void 0;
|
|
93897
|
+
}
|
|
93898
|
+
abandonFrozenLocalAppend(frozen) {
|
|
93899
|
+
if (this.frozenLocalAppend !== frozen) return;
|
|
93900
|
+
this.frozenLocalAppend = void 0;
|
|
93901
|
+
this.producerEpoch = Math.max(this.producerEpoch, frozen.producerSession.producerEpoch + 1);
|
|
93902
|
+
this.nextProducerSeq = 0;
|
|
93903
|
+
}
|
|
93904
|
+
consumeCommittedPendingCount() {
|
|
93905
|
+
const count2 = this.committedPendingCount;
|
|
93906
|
+
this.committedPendingCount = void 0;
|
|
93907
|
+
return count2;
|
|
93908
|
+
}
|
|
93580
93909
|
async ensureStreamExists() {
|
|
93581
93910
|
try {
|
|
93582
93911
|
await this.client.createStreamRequest();
|
|
@@ -93621,9 +93950,17 @@ stream:${scope2.streamId}`;
|
|
|
93621
93950
|
const inferredRemote = await this.bootstrapState(bootstrapCursor, {
|
|
93622
93951
|
cursorManager
|
|
93623
93952
|
});
|
|
93624
|
-
|
|
93625
|
-
if (
|
|
93626
|
-
|
|
93953
|
+
let pendingAppend;
|
|
93954
|
+
if (this.frozenLocalAppend != null) {
|
|
93955
|
+
if (this.frozenLocalAppend.pendingCount != null) throw new Error("pending local append must be finalized by pending flush");
|
|
93956
|
+
pendingAppend = await this.appendFrozenLocalBatchRemote(this.frozenLocalAppend);
|
|
93957
|
+
} else {
|
|
93958
|
+
const localBatch = this.exportUpdates(inferredRemote.cursor.serverLowerBoundVersion);
|
|
93959
|
+
if (localBatch.batch != null) pendingAppend = await this.appendLocalBatchRemote(inferredRemote.cursor, localBatch.batch);
|
|
93960
|
+
}
|
|
93961
|
+
const result = await this.bootstrapState(bootstrapCursor);
|
|
93962
|
+
if (pendingAppend != null) this.commitProducerAck(pendingAppend.producerAck);
|
|
93963
|
+
return result;
|
|
93627
93964
|
}
|
|
93628
93965
|
async recoverFromGoneWithLocalBootstrap(cursor) {
|
|
93629
93966
|
const isolatedCursorManager = await this.createIsolatedCursorManager();
|
|
@@ -93641,9 +93978,17 @@ stream:${scope2.streamId}`;
|
|
|
93641
93978
|
});
|
|
93642
93979
|
await this.adapter.applySnapshot(preservedLocalSnapshot);
|
|
93643
93980
|
restoredLocalSnapshot = true;
|
|
93644
|
-
|
|
93645
|
-
if (
|
|
93646
|
-
|
|
93981
|
+
let pendingAppend;
|
|
93982
|
+
if (this.frozenLocalAppend != null) {
|
|
93983
|
+
if (this.frozenLocalAppend.pendingCount != null) throw new Error("pending local append must be finalized by pending flush");
|
|
93984
|
+
pendingAppend = await this.appendFrozenLocalBatchRemote(this.frozenLocalAppend);
|
|
93985
|
+
} else {
|
|
93986
|
+
const localBatch = this.exportUpdates(inferredRemote.cursor.serverLowerBoundVersion);
|
|
93987
|
+
if (localBatch.batch != null) pendingAppend = await this.appendLocalBatchRemote(inferredRemote.cursor, localBatch.batch);
|
|
93988
|
+
}
|
|
93989
|
+
const result = await this.bootstrapState(bootstrapCursor);
|
|
93990
|
+
if (pendingAppend != null) this.commitProducerAck(pendingAppend.producerAck);
|
|
93991
|
+
return result;
|
|
93647
93992
|
} catch (error2) {
|
|
93648
93993
|
if (!restoredLocalSnapshot) await this.adapter.applySnapshot(preservedLocalSnapshot);
|
|
93649
93994
|
throw error2;
|
|
@@ -93855,7 +94200,6 @@ stream:${scope2.streamId}`;
|
|
|
93855
94200
|
let mergedVersion = first2.version;
|
|
93856
94201
|
const allUpdates = Array.from(first2.updates);
|
|
93857
94202
|
let count2 = 1;
|
|
93858
|
-
await this.measureAppendBodyByteLength(allUpdates);
|
|
93859
94203
|
while (count2 < pendingLocal.length) {
|
|
93860
94204
|
const next = pendingLocal.at(count2);
|
|
93861
94205
|
const candidateUpdates = [
|
|
@@ -93894,9 +94238,10 @@ stream:${scope2.streamId}`;
|
|
|
93894
94238
|
if (drained == null) return;
|
|
93895
94239
|
let attempts = 0;
|
|
93896
94240
|
while (!state2.closed) try {
|
|
93897
|
-
state2.cursor = await this.enqueueExclusive(async () => this.appendLocalBatch(state2.cursor, drained.batch));
|
|
93898
|
-
|
|
93899
|
-
|
|
94241
|
+
state2.cursor = await this.enqueueExclusive(async () => this.appendLocalBatch(state2.cursor, drained.batch, drained.count));
|
|
94242
|
+
const committedCount = this.consumeCommittedPendingCount() ?? drained.count;
|
|
94243
|
+
for (let i2 = 0; i2 < committedCount; i2 += 1) state2.pendingLocal.popFront();
|
|
94244
|
+
this.notifySyncWaiters(state2, committedCount);
|
|
93900
94245
|
this.setWriteSubStatus(state2, "ok");
|
|
93901
94246
|
this.snapshotManager.schedule(state2);
|
|
93902
94247
|
break;
|
|
@@ -93986,6 +94331,8 @@ stream:${scope2.streamId}`;
|
|
|
93986
94331
|
return updates.reduce((sum, update2) => sum + 4 + update2.byteLength, 0);
|
|
93987
94332
|
}
|
|
93988
94333
|
disableWriteOnlyMode(reason) {
|
|
94334
|
+
const frozen = this.frozenLocalAppend;
|
|
94335
|
+
if (frozen?.source === "write-only") this.abandonFrozenLocalAppend(frozen);
|
|
93989
94336
|
if (this.writeOnlyDisabledReason != null) return;
|
|
93990
94337
|
this.writeOnlyDisabledReason = reason;
|
|
93991
94338
|
this.pendingWriteOnly.clear();
|
|
@@ -112230,6 +112577,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
112230
112577
|
isTransportConnected() {
|
|
112231
112578
|
return this.transportStatus === "connected";
|
|
112232
112579
|
}
|
|
112580
|
+
isRecovering() {
|
|
112581
|
+
return !this.isCleanedUp && !this.isStreamsHealthy();
|
|
112582
|
+
}
|
|
112233
112583
|
setTransportStatus(status) {
|
|
112234
112584
|
this.transportStatus = status;
|
|
112235
112585
|
if (status === "disconnected") {
|
|
@@ -113429,6 +113779,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113429
113779
|
isTransportConnected() {
|
|
113430
113780
|
return this.connectionRecovery.isTransportConnected();
|
|
113431
113781
|
}
|
|
113782
|
+
isTransportRecovering() {
|
|
113783
|
+
return this.connectionRecovery.isRecovering();
|
|
113784
|
+
}
|
|
113432
113785
|
getConnectedRoomCount() {
|
|
113433
113786
|
return this.sessions.size + this.codeSessions.size;
|
|
113434
113787
|
}
|
|
@@ -116919,7 +117272,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116919
117272
|
},
|
|
116920
117273
|
codex: {
|
|
116921
117274
|
packageName: "acp-extension-codex",
|
|
116922
|
-
version: "0.
|
|
117275
|
+
version: "0.12.0",
|
|
116923
117276
|
binName: "acp-extension-codex",
|
|
116924
117277
|
args: [
|
|
116925
117278
|
"-c",
|
|
@@ -120210,6 +120563,221 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120210
120563
|
}
|
|
120211
120564
|
return resolveAgentConfigEnvForSessionResume(repo, options.agentConfigId);
|
|
120212
120565
|
};
|
|
120566
|
+
function getMemoryPressureSnapshot() {
|
|
120567
|
+
const windowsStatus = getWindowsMemoryStatus();
|
|
120568
|
+
const systemAvailable = getSystemAvailableMemoryBytes(windowsStatus);
|
|
120569
|
+
const cgroupAvailable = getCgroupAvailableMemoryBytes();
|
|
120570
|
+
const availableMemoryBytes = cgroupAvailable !== null ? Math.min(systemAvailable, cgroupAvailable) : systemAvailable;
|
|
120571
|
+
const effectiveMemoryLimitBytes = getEffectiveMemoryLimitBytes();
|
|
120572
|
+
return {
|
|
120573
|
+
availableMemoryBytes,
|
|
120574
|
+
effectiveMemoryLimitBytes,
|
|
120575
|
+
...windowsStatus ? {
|
|
120576
|
+
availableCommitBytes: windowsStatus.availableCommitBytes,
|
|
120577
|
+
commitLimitBytes: windowsStatus.commitLimitBytes,
|
|
120578
|
+
committedBytes: windowsStatus.committedBytes
|
|
120579
|
+
} : {}
|
|
120580
|
+
};
|
|
120581
|
+
}
|
|
120582
|
+
function getEffectiveMemoryLimitBytes() {
|
|
120583
|
+
const totalMem = os__default.totalmem();
|
|
120584
|
+
const cgroupMax = getCgroupMemoryMaxBytes();
|
|
120585
|
+
if (cgroupMax !== null) {
|
|
120586
|
+
return Math.min(totalMem, cgroupMax);
|
|
120587
|
+
}
|
|
120588
|
+
return totalMem;
|
|
120589
|
+
}
|
|
120590
|
+
function getSystemAvailableMemoryBytes(windowsStatus) {
|
|
120591
|
+
if (windowsStatus) {
|
|
120592
|
+
return Math.max(windowsStatus.availableBytes, os__default.freemem());
|
|
120593
|
+
}
|
|
120594
|
+
const darwinAvailable = getDarwinAvailableMemoryBytes();
|
|
120595
|
+
if (darwinAvailable !== null) {
|
|
120596
|
+
return darwinAvailable;
|
|
120597
|
+
}
|
|
120598
|
+
try {
|
|
120599
|
+
const meminfo = readFileSync("/proc/meminfo", "utf8");
|
|
120600
|
+
const match5 = meminfo.match(/MemAvailable:\s+(\d+)/);
|
|
120601
|
+
if (match5?.[1]) {
|
|
120602
|
+
return parseInt(match5[1], 10) * 1024;
|
|
120603
|
+
}
|
|
120604
|
+
} catch {
|
|
120605
|
+
}
|
|
120606
|
+
return os__default.freemem();
|
|
120607
|
+
}
|
|
120608
|
+
function getWindowsMemoryStatus() {
|
|
120609
|
+
if (process.platform !== "win32") {
|
|
120610
|
+
return null;
|
|
120611
|
+
}
|
|
120612
|
+
try {
|
|
120613
|
+
const script = `
|
|
120614
|
+
$mem = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfOS_Memory |
|
|
120615
|
+
Select-Object AvailableBytes, CommitLimit, CommittedBytes
|
|
120616
|
+
$mem | ConvertTo-Json -Compress
|
|
120617
|
+
`;
|
|
120618
|
+
const output = execFileSync("powershell.exe", [
|
|
120619
|
+
"-NoProfile",
|
|
120620
|
+
"-NonInteractive",
|
|
120621
|
+
"-ExecutionPolicy",
|
|
120622
|
+
"Bypass",
|
|
120623
|
+
"-Command",
|
|
120624
|
+
script
|
|
120625
|
+
], {
|
|
120626
|
+
encoding: "utf8"
|
|
120627
|
+
});
|
|
120628
|
+
return parseWindowsMemoryStatus(output);
|
|
120629
|
+
} catch {
|
|
120630
|
+
return null;
|
|
120631
|
+
}
|
|
120632
|
+
}
|
|
120633
|
+
function parseWindowsMemoryStatus(rawJson) {
|
|
120634
|
+
try {
|
|
120635
|
+
const parsed = JSON.parse(rawJson);
|
|
120636
|
+
const availableBytes = Number(parsed.AvailableBytes);
|
|
120637
|
+
const commitLimitBytes = Number(parsed.CommitLimit);
|
|
120638
|
+
const committedBytes = Number(parsed.CommittedBytes);
|
|
120639
|
+
if (!Number.isFinite(availableBytes) || availableBytes < 0 || !Number.isFinite(commitLimitBytes) || commitLimitBytes <= 0 || !Number.isFinite(committedBytes) || committedBytes < 0) {
|
|
120640
|
+
return null;
|
|
120641
|
+
}
|
|
120642
|
+
return {
|
|
120643
|
+
availableBytes,
|
|
120644
|
+
commitLimitBytes,
|
|
120645
|
+
committedBytes,
|
|
120646
|
+
availableCommitBytes: Math.max(0, commitLimitBytes - committedBytes)
|
|
120647
|
+
};
|
|
120648
|
+
} catch {
|
|
120649
|
+
return null;
|
|
120650
|
+
}
|
|
120651
|
+
}
|
|
120652
|
+
function getDarwinAvailableMemoryBytes() {
|
|
120653
|
+
if (process.platform !== "darwin") {
|
|
120654
|
+
return null;
|
|
120655
|
+
}
|
|
120656
|
+
try {
|
|
120657
|
+
const vmStatOutput = execFileSync("vm_stat", {
|
|
120658
|
+
encoding: "utf8"
|
|
120659
|
+
});
|
|
120660
|
+
const parsed = parseDarwinAvailableMemoryBytes(vmStatOutput);
|
|
120661
|
+
if (parsed !== null) {
|
|
120662
|
+
return Math.max(parsed, os__default.freemem());
|
|
120663
|
+
}
|
|
120664
|
+
} catch {
|
|
120665
|
+
}
|
|
120666
|
+
return null;
|
|
120667
|
+
}
|
|
120668
|
+
function parseDarwinAvailableMemoryBytes(vmStatOutput) {
|
|
120669
|
+
const pageSizeMatch = vmStatOutput.match(/page size of\s+(\d+)\s+bytes/i);
|
|
120670
|
+
if (!pageSizeMatch?.[1]) {
|
|
120671
|
+
return null;
|
|
120672
|
+
}
|
|
120673
|
+
const pageSize = parseInt(pageSizeMatch[1], 10);
|
|
120674
|
+
if (!Number.isFinite(pageSize) || pageSize <= 0) {
|
|
120675
|
+
return null;
|
|
120676
|
+
}
|
|
120677
|
+
const counters = /* @__PURE__ */ new Map();
|
|
120678
|
+
for (const rawLine of vmStatOutput.split("\n")) {
|
|
120679
|
+
const line3 = rawLine.trim();
|
|
120680
|
+
const match5 = line3.match(/^"?([^":]+?)"?:\s+(\d+)\.?$/);
|
|
120681
|
+
if (!match5?.[1] || !match5[2]) {
|
|
120682
|
+
continue;
|
|
120683
|
+
}
|
|
120684
|
+
counters.set(match5[1].toLowerCase(), parseInt(match5[2], 10));
|
|
120685
|
+
}
|
|
120686
|
+
const freePages = counters.get("pages free") ?? 0;
|
|
120687
|
+
const speculativePages = counters.get("pages speculative") ?? 0;
|
|
120688
|
+
const purgeablePages = counters.get("pages purgeable") ?? 0;
|
|
120689
|
+
const inactivePages = counters.get("pages inactive") ?? 0;
|
|
120690
|
+
const fileBackedPages = counters.get("file-backed pages");
|
|
120691
|
+
if (freePages === 0 && speculativePages === 0 && purgeablePages === 0 && inactivePages === 0) {
|
|
120692
|
+
return null;
|
|
120693
|
+
}
|
|
120694
|
+
const reclaimableCachedPages = fileBackedPages !== void 0 ? Math.min(fileBackedPages, inactivePages) : inactivePages;
|
|
120695
|
+
const availablePages = freePages + speculativePages + purgeablePages + reclaimableCachedPages;
|
|
120696
|
+
return availablePages * pageSize;
|
|
120697
|
+
}
|
|
120698
|
+
function getCgroupMemoryMaxBytes() {
|
|
120699
|
+
try {
|
|
120700
|
+
const cgroupPath = readSelfCgroupPath();
|
|
120701
|
+
if (cgroupPath === null) return null;
|
|
120702
|
+
let tightest = null;
|
|
120703
|
+
let current2 = cgroupPath;
|
|
120704
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
120705
|
+
const memMaxPath = `/sys/fs/cgroup${current2 === "/" ? "" : current2}/memory.max`;
|
|
120706
|
+
const raw = readFileSafe(memMaxPath);
|
|
120707
|
+
if (raw !== null) {
|
|
120708
|
+
const trimmed = raw.trim();
|
|
120709
|
+
if (trimmed !== "max") {
|
|
120710
|
+
const value = parseInt(trimmed, 10);
|
|
120711
|
+
if (Number.isFinite(value) && value > 0) {
|
|
120712
|
+
tightest = tightest === null ? value : Math.min(tightest, value);
|
|
120713
|
+
}
|
|
120714
|
+
}
|
|
120715
|
+
}
|
|
120716
|
+
if (current2 === "/" || current2 === "") break;
|
|
120717
|
+
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
120718
|
+
if (parent === current2) break;
|
|
120719
|
+
current2 = parent;
|
|
120720
|
+
}
|
|
120721
|
+
return tightest;
|
|
120722
|
+
} catch {
|
|
120723
|
+
return null;
|
|
120724
|
+
}
|
|
120725
|
+
}
|
|
120726
|
+
function getCgroupAvailableMemoryBytes() {
|
|
120727
|
+
try {
|
|
120728
|
+
const cgroupPath = readSelfCgroupPath();
|
|
120729
|
+
if (cgroupPath === null) return null;
|
|
120730
|
+
let tightestMax = null;
|
|
120731
|
+
let tightestPath = null;
|
|
120732
|
+
let current2 = cgroupPath;
|
|
120733
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
120734
|
+
const prefix = `/sys/fs/cgroup${current2 === "/" ? "" : current2}`;
|
|
120735
|
+
const raw = readFileSafe(`${prefix}/memory.max`);
|
|
120736
|
+
if (raw !== null) {
|
|
120737
|
+
const trimmed = raw.trim();
|
|
120738
|
+
if (trimmed !== "max") {
|
|
120739
|
+
const value = parseInt(trimmed, 10);
|
|
120740
|
+
if (Number.isFinite(value) && value > 0) {
|
|
120741
|
+
if (tightestMax === null || value < tightestMax) {
|
|
120742
|
+
tightestMax = value;
|
|
120743
|
+
tightestPath = prefix;
|
|
120744
|
+
}
|
|
120745
|
+
}
|
|
120746
|
+
}
|
|
120747
|
+
}
|
|
120748
|
+
if (current2 === "/" || current2 === "") break;
|
|
120749
|
+
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
120750
|
+
if (parent === current2) break;
|
|
120751
|
+
current2 = parent;
|
|
120752
|
+
}
|
|
120753
|
+
if (tightestMax === null || tightestPath === null) return null;
|
|
120754
|
+
const currentRaw = readFileSafe(`${tightestPath}/memory.current`);
|
|
120755
|
+
if (currentRaw === null) return null;
|
|
120756
|
+
const currentUsage = parseInt(currentRaw.trim(), 10);
|
|
120757
|
+
if (!Number.isFinite(currentUsage)) return null;
|
|
120758
|
+
return Math.max(0, tightestMax - currentUsage);
|
|
120759
|
+
} catch {
|
|
120760
|
+
return null;
|
|
120761
|
+
}
|
|
120762
|
+
}
|
|
120763
|
+
function readSelfCgroupPath() {
|
|
120764
|
+
try {
|
|
120765
|
+
const content = readFileSync("/proc/self/cgroup", "utf8");
|
|
120766
|
+
const line3 = content.split("\n").map((l) => l.trim()).find((l) => l.startsWith("0::"));
|
|
120767
|
+
if (!line3) return null;
|
|
120768
|
+
const cgroupPath = line3.slice(3).trim();
|
|
120769
|
+
return cgroupPath || "/";
|
|
120770
|
+
} catch {
|
|
120771
|
+
return null;
|
|
120772
|
+
}
|
|
120773
|
+
}
|
|
120774
|
+
function readFileSafe(filePath) {
|
|
120775
|
+
try {
|
|
120776
|
+
return readFileSync(filePath, "utf8");
|
|
120777
|
+
} catch {
|
|
120778
|
+
return null;
|
|
120779
|
+
}
|
|
120780
|
+
}
|
|
120213
120781
|
class SessionTurnCancelled extends TaggedError("SessionTurnCancelled") {
|
|
120214
120782
|
}
|
|
120215
120783
|
class SessionTurnHalted extends TaggedError("SessionTurnHalted") {
|
|
@@ -120230,6 +120798,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120230
120798
|
AUTH_REQUIRED: -32e3,
|
|
120231
120799
|
RESOURCE_NOT_FOUND: -32002
|
|
120232
120800
|
};
|
|
120801
|
+
const BYTES_PER_GIB = 1024 * 1024 * 1024;
|
|
120233
120802
|
const shouldRedactEnvKey = (key2) => /token|secret|password|passwd|key/i.test(key2);
|
|
120234
120803
|
const redactEnvForLog = (env2) => {
|
|
120235
120804
|
if (!env2) {
|
|
@@ -120265,6 +120834,46 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120265
120834
|
canceledTurnBySession = /* @__PURE__ */ new Map();
|
|
120266
120835
|
currentTurnBySession = /* @__PURE__ */ new Map();
|
|
120267
120836
|
turnRuntimeBySession = /* @__PURE__ */ new Map();
|
|
120837
|
+
formatGiB(bytes) {
|
|
120838
|
+
if (bytes === null || !Number.isFinite(bytes) || bytes < 0) {
|
|
120839
|
+
return "unavailable";
|
|
120840
|
+
}
|
|
120841
|
+
return `${(bytes / BYTES_PER_GIB).toFixed(1)}GB`;
|
|
120842
|
+
}
|
|
120843
|
+
formatPercent(value) {
|
|
120844
|
+
if (value === null || !Number.isFinite(value) || value < 0) {
|
|
120845
|
+
return "unavailable";
|
|
120846
|
+
}
|
|
120847
|
+
return `${value.toFixed(1)}%`;
|
|
120848
|
+
}
|
|
120849
|
+
async getFreeDiskBytes() {
|
|
120850
|
+
try {
|
|
120851
|
+
const stats = await statfs(os__default.homedir());
|
|
120852
|
+
const bsize = typeof stats.bsize === "bigint" ? Number(stats.bsize) : stats.bsize;
|
|
120853
|
+
const bavail = typeof stats.bavail === "bigint" ? Number(stats.bavail) : stats.bavail;
|
|
120854
|
+
if (!Number.isFinite(bsize) || !Number.isFinite(bavail) || bsize <= 0 || bavail < 0) {
|
|
120855
|
+
return null;
|
|
120856
|
+
}
|
|
120857
|
+
return bsize * bavail;
|
|
120858
|
+
} catch {
|
|
120859
|
+
return null;
|
|
120860
|
+
}
|
|
120861
|
+
}
|
|
120862
|
+
async logTurnStartResources(sessionId, mode2) {
|
|
120863
|
+
try {
|
|
120864
|
+
const memorySnapshot = getMemoryPressureSnapshot();
|
|
120865
|
+
const availableMemoryBytes = memorySnapshot.availableMemoryBytes;
|
|
120866
|
+
const memoryFreePercent = memorySnapshot.effectiveMemoryLimitBytes > 0 ? availableMemoryBytes / memorySnapshot.effectiveMemoryLimitBytes * 100 : null;
|
|
120867
|
+
const [resources, freeDiskBytes] = await Promise.all([
|
|
120868
|
+
this.deps.collectMachineResources().catch(() => null),
|
|
120869
|
+
this.getFreeDiskBytes()
|
|
120870
|
+
]);
|
|
120871
|
+
const cpuUsagePercent = resources?.cpuUsagePercent ?? null;
|
|
120872
|
+
this.deps.logger.info(`[${sessionId}] Turn start resources (mode=${mode2}): memoryAvailable=${this.formatGiB(availableMemoryBytes)} memoryAvailablePercent=${this.formatPercent(memoryFreePercent)} cpuUsage=${this.formatPercent(cpuUsagePercent)} diskFree=${this.formatGiB(freeDiskBytes)}`);
|
|
120873
|
+
} catch (error2) {
|
|
120874
|
+
this.deps.logger.debug(`[${sessionId}] Failed to log turn start resources: ${formatErrorMessage(error2)}`);
|
|
120875
|
+
}
|
|
120876
|
+
}
|
|
120268
120877
|
createTurnRuntime(sessionId, turnId, userTurnId, session) {
|
|
120269
120878
|
return {
|
|
120270
120879
|
sessionId,
|
|
@@ -120447,10 +121056,19 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120447
121056
|
async recordKnownChatFailure(options) {
|
|
120448
121057
|
await this.deps.recordChatFailure(options.sessionDoc, options.reason, options.message);
|
|
120449
121058
|
if (options.userTurnId) {
|
|
120450
|
-
await this.
|
|
121059
|
+
await this.markTurnFailed(options.sessionId, options.sessionDoc, options.userTurnId);
|
|
120451
121060
|
}
|
|
120452
121061
|
await options.sessionDoc.setStatus(SessionStatusFactory.idle());
|
|
120453
121062
|
}
|
|
121063
|
+
formatMemoryPressureWarningMessage(result) {
|
|
121064
|
+
const availableMb = Math.round(result.availableMemoryBytes / 1024 / 1024);
|
|
121065
|
+
const thresholdMb = Math.round(result.thresholdBytes / 1024 / 1024);
|
|
121066
|
+
const commitText = result.availableCommitBytes !== void 0 && (result.pressureReason === "commit" || result.pressureReason === "physical_and_commit") && result.commitThresholdBytes !== void 0 ? ` Commit headroom is ${Math.round(result.availableCommitBytes / 1024 / 1024)}MB (threshold: ${Math.round(result.commitThresholdBytes / 1024 / 1024)}MB).` : "";
|
|
121067
|
+
return `The machine is under memory pressure (${availableMb}MB available, ${thresholdMb}MB required to start a turn). Proceeding anyway because the new turn may be used to free resources.${commitText}`;
|
|
121068
|
+
}
|
|
121069
|
+
warnOnMemoryPressure(sessionId, result) {
|
|
121070
|
+
this.deps.logger.warn(`[${sessionId}] ${this.formatMemoryPressureWarningMessage(result)}`);
|
|
121071
|
+
}
|
|
120454
121072
|
recordKnownChatFailureAndHaltEffect(options) {
|
|
120455
121073
|
return this.tryPromise(() => this.recordKnownChatFailure(options)).pipe(flatMap$1(() => fail(new SessionTurnHalted({
|
|
120456
121074
|
sessionId: options.sessionId,
|
|
@@ -120474,7 +121092,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120474
121092
|
}
|
|
120475
121093
|
this.deps.logger.error(options.describe(options.error), options.error);
|
|
120476
121094
|
if (options.userTurnId) {
|
|
120477
|
-
await this.
|
|
121095
|
+
await this.markTurnFailed(options.sessionId, options.sessionDoc, options.userTurnId);
|
|
120478
121096
|
}
|
|
120479
121097
|
await this.handleTurnError(options.sessionId, options.sessionDoc, options.error);
|
|
120480
121098
|
await options.onUnhandledError?.(options.error);
|
|
@@ -120810,8 +121428,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120810
121428
|
await this.setUserTurnStatus(sessionDoc, userTurnId, "processing");
|
|
120811
121429
|
await this.upsertSessionMeta(sessionId, {
|
|
120812
121430
|
latestUserMsgId: userTurnId,
|
|
120813
|
-
processingUserMsgId: userTurnId
|
|
120814
|
-
dispatchError: void 0
|
|
121431
|
+
processingUserMsgId: userTurnId
|
|
120815
121432
|
});
|
|
120816
121433
|
}
|
|
120817
121434
|
async clearDispatchProcessing(sessionId) {
|
|
@@ -120860,34 +121477,31 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120860
121477
|
await this.upsertSessionMeta(sessionId, {
|
|
120861
121478
|
latestUserMsgId: existingMeta?.latestUserMsgId ?? cancelledUserMsgId,
|
|
120862
121479
|
lastHandledUserMsgId: cancelledUserMsgId,
|
|
120863
|
-
processingUserMsgId: void 0
|
|
120864
|
-
dispatchError: void 0
|
|
121480
|
+
processingUserMsgId: void 0
|
|
120865
121481
|
});
|
|
120866
121482
|
return;
|
|
120867
121483
|
}
|
|
120868
121484
|
await this.clearDispatchProcessing(sessionId);
|
|
120869
121485
|
}
|
|
121486
|
+
resolveLatestUserMsgIdForTerminalTurn(meta, terminalUserTurnId) {
|
|
121487
|
+
return meta?.latestUserMsgId && meta.latestUserMsgId !== terminalUserTurnId ? meta.latestUserMsgId : terminalUserTurnId;
|
|
121488
|
+
}
|
|
120870
121489
|
async setDispatchHandled(sessionId, sessionDoc, userTurnId) {
|
|
121490
|
+
const existingMeta = await this.getSessionMeta(sessionId);
|
|
120871
121491
|
await this.setUserTurnStatus(sessionDoc, userTurnId, "handled");
|
|
120872
121492
|
await this.upsertSessionMeta(sessionId, {
|
|
120873
|
-
latestUserMsgId: userTurnId,
|
|
121493
|
+
latestUserMsgId: this.resolveLatestUserMsgIdForTerminalTurn(existingMeta, userTurnId),
|
|
120874
121494
|
lastHandledUserMsgId: userTurnId,
|
|
120875
|
-
processingUserMsgId: void 0
|
|
120876
|
-
dispatchError: void 0
|
|
121495
|
+
processingUserMsgId: void 0
|
|
120877
121496
|
});
|
|
120878
121497
|
}
|
|
120879
|
-
async
|
|
121498
|
+
async markTurnFailed(sessionId, sessionDoc, userTurnId) {
|
|
121499
|
+
const existingMeta = await this.getSessionMeta(sessionId);
|
|
120880
121500
|
await this.setUserTurnStatus(sessionDoc, userTurnId, "failed");
|
|
120881
121501
|
await this.upsertSessionMeta(sessionId, {
|
|
120882
|
-
latestUserMsgId: userTurnId,
|
|
120883
|
-
|
|
120884
|
-
|
|
120885
|
-
code: code2,
|
|
120886
|
-
...message ? {
|
|
120887
|
-
message
|
|
120888
|
-
} : {},
|
|
120889
|
-
at: getServerNow()
|
|
120890
|
-
}
|
|
121502
|
+
latestUserMsgId: this.resolveLatestUserMsgIdForTerminalTurn(existingMeta, userTurnId),
|
|
121503
|
+
lastHandledUserMsgId: userTurnId,
|
|
121504
|
+
processingUserMsgId: void 0
|
|
120891
121505
|
});
|
|
120892
121506
|
}
|
|
120893
121507
|
resolveGitHubProjectBranch(meta, preferredBranch) {
|
|
@@ -120981,15 +121595,18 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
120981
121595
|
}
|
|
120982
121596
|
async continueSession(message) {
|
|
120983
121597
|
const { sessionId, acpSessionConfig, userId, userName, userEmail, userTurnId } = message;
|
|
120984
|
-
await this.deps.evictForMemoryPressure(sessionId);
|
|
121598
|
+
const memoryPressureResult = await this.deps.evictForMemoryPressure(sessionId);
|
|
121599
|
+
if (memoryPressureResult.stillUnderPressure) {
|
|
121600
|
+
this.warnOnMemoryPressure(sessionId, memoryPressureResult);
|
|
121601
|
+
}
|
|
121602
|
+
await this.logTurnStartResources(sessionId, "continue");
|
|
121603
|
+
const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
|
|
120985
121604
|
this.deps.touchSession(sessionId);
|
|
120986
121605
|
this.deps.logger.info(`Session chat received: ${sessionId}`);
|
|
120987
121606
|
this.deps.logger.debug(`[${sessionId}] Received chat request (userTurnId=${userTurnId})`);
|
|
120988
121607
|
await this.upsertSessionMeta(sessionId, {
|
|
120989
|
-
latestUserMsgId: userTurnId
|
|
120990
|
-
dispatchError: void 0
|
|
121608
|
+
latestUserMsgId: userTurnId
|
|
120991
121609
|
});
|
|
120992
|
-
const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
|
|
120993
121610
|
const incomingProjectBranch = message.project?.branch?.trim();
|
|
120994
121611
|
if (incomingProjectBranch) {
|
|
120995
121612
|
await sessionDoc.setBaseBranch(incomingProjectBranch);
|
|
@@ -121326,7 +121943,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121326
121943
|
}));
|
|
121327
121944
|
}
|
|
121328
121945
|
async startSession(message) {
|
|
121329
|
-
await this.deps.evictForMemoryPressure(message.sessionId);
|
|
121946
|
+
const memoryPressureResult = await this.deps.evictForMemoryPressure(message.sessionId);
|
|
121330
121947
|
const { sessionId, acpSessionConfig, workspaceId, env: env2 } = message;
|
|
121331
121948
|
const userTurnId = typeof message.userTurnId === "string" && message.userTurnId.trim() ? message.userTurnId.trim() : void 0;
|
|
121332
121949
|
const project = message.project;
|
|
@@ -121339,6 +121956,10 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121339
121956
|
const promptText = agentConfig.prompt ?? "";
|
|
121340
121957
|
const promptBytes = Buffer.byteLength(promptText, "utf8");
|
|
121341
121958
|
const promptPreview = promptText.length > 200 ? `${promptText.slice(0, 200)}\u2026` : promptText;
|
|
121959
|
+
if (memoryPressureResult.stillUnderPressure) {
|
|
121960
|
+
this.warnOnMemoryPressure(sessionId, memoryPressureResult);
|
|
121961
|
+
}
|
|
121962
|
+
await this.logTurnStartResources(sessionId, "start");
|
|
121342
121963
|
const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
|
|
121343
121964
|
const existingMeta = await sessionDoc.getMetaState();
|
|
121344
121965
|
const fromFeedbackPostId = message.meta?.fromFeedbackPostId?.trim() || existingMeta?.fromFeedbackPostId?.trim() || void 0;
|
|
@@ -121362,12 +121983,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121362
121983
|
fromFeedbackPostId
|
|
121363
121984
|
};
|
|
121364
121985
|
this.deps.logger.debug(`[${sessionId}] session/create summary`, configForLog);
|
|
121365
|
-
if (userTurnId) {
|
|
121366
|
-
await this.upsertSessionMeta(sessionId, {
|
|
121367
|
-
latestUserMsgId: userTurnId,
|
|
121368
|
-
dispatchError: void 0
|
|
121369
|
-
});
|
|
121370
|
-
}
|
|
121371
121986
|
const sessionConfig = {
|
|
121372
121987
|
sessionId,
|
|
121373
121988
|
workspaceId,
|
|
@@ -121388,6 +122003,11 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
121388
122003
|
if (project) {
|
|
121389
122004
|
await sessionDoc.setProject(project);
|
|
121390
122005
|
}
|
|
122006
|
+
if (userTurnId) {
|
|
122007
|
+
await this.upsertSessionMeta(sessionId, {
|
|
122008
|
+
latestUserMsgId: userTurnId
|
|
122009
|
+
});
|
|
122010
|
+
}
|
|
121391
122011
|
if (branch) {
|
|
121392
122012
|
await sessionDoc.setBaseBranch(branch);
|
|
121393
122013
|
}
|
|
@@ -122051,9 +122671,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122051
122671
|
if (meta.lastCanceledTurn && meta.lastCanceledTurn !== this.cancelSeenTurn.get(meta.id)) {
|
|
122052
122672
|
return true;
|
|
122053
122673
|
}
|
|
122054
|
-
if (meta.dispatchError?.code === SessionDispatchWatcher.DISPATCH_HISTORY_SYNC_TIMEOUT_CODE) {
|
|
122055
|
-
return false;
|
|
122056
|
-
}
|
|
122057
122674
|
if (!meta.lastHandledUserMsgId) {
|
|
122058
122675
|
return true;
|
|
122059
122676
|
}
|
|
@@ -122149,12 +122766,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122149
122766
|
await this.deps.workspaceDocument.repo.upsertDocMeta?.(getSessionRoomId(sessionId), {
|
|
122150
122767
|
latestUserMsgId: userTurnId,
|
|
122151
122768
|
lastHandledUserMsgId: userTurnId,
|
|
122152
|
-
processingUserMsgId: void 0
|
|
122153
|
-
dispatchError: {
|
|
122154
|
-
code: reason === "cli_token_invalid" ? "cli_token_invalid" : "machine_access_denied",
|
|
122155
|
-
message,
|
|
122156
|
-
at: getServerNow()
|
|
122157
|
-
}
|
|
122769
|
+
processingUserMsgId: void 0
|
|
122158
122770
|
});
|
|
122159
122771
|
await sessionDoc.setStatus(SessionStatusFactory.idle());
|
|
122160
122772
|
}
|
|
@@ -122284,7 +122896,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122284
122896
|
static HISTORY_SYNC_WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
122285
122897
|
static HISTORY_RECONNECT_JITTER_MIN_MS = 500;
|
|
122286
122898
|
static HISTORY_RECONNECT_JITTER_MAX_MS = 1500;
|
|
122287
|
-
static DISPATCH_HISTORY_SYNC_TIMEOUT_CODE = "dispatch_recovery_unhealthy";
|
|
122288
122899
|
static setUnrefTimeout(callback, delayMs) {
|
|
122289
122900
|
const timer2 = setTimeout(callback, delayMs);
|
|
122290
122901
|
if (typeof timer2 === "object" && "unref" in timer2) {
|
|
@@ -122445,11 +123056,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
122445
123056
|
const pendingUserMsgId = meta.processingUserMsgId ?? meta.latestUserMsgId ?? meta.lastHandledUserMsgId;
|
|
122446
123057
|
const recoveryPatch = {
|
|
122447
123058
|
status: SessionStatusFactory.idle(),
|
|
122448
|
-
|
|
122449
|
-
code: SessionDispatchWatcher.DISPATCH_HISTORY_SYNC_TIMEOUT_CODE,
|
|
122450
|
-
message: "Dispatch recovery could not reconnect to this session after 5 minutes. Send a new message to retry.",
|
|
122451
|
-
at: getServerNow()
|
|
122452
|
-
}
|
|
123059
|
+
processingUserMsgId: void 0
|
|
122453
123060
|
};
|
|
122454
123061
|
if (pendingUserMsgId) {
|
|
122455
123062
|
recoveryPatch.lastHandledUserMsgId = pendingUserMsgId;
|
|
@@ -123202,8 +123809,14 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
123202
123809
|
permissionRequestStartTimes = /* @__PURE__ */ new Map();
|
|
123203
123810
|
machineHeartbeatTimer = null;
|
|
123204
123811
|
static MACHINE_HEARTBEAT_INTERVAL_MS = 2e4;
|
|
123205
|
-
evictForMemoryPressureFn = async () => {
|
|
123206
|
-
|
|
123812
|
+
evictForMemoryPressureFn = async () => ({
|
|
123813
|
+
availableMemoryBytes: 0,
|
|
123814
|
+
thresholdBytes: 0,
|
|
123815
|
+
hadMemoryPressure: false,
|
|
123816
|
+
stillUnderPressure: false,
|
|
123817
|
+
evictedSessionIds: [],
|
|
123818
|
+
pressureReason: null
|
|
123819
|
+
});
|
|
123207
123820
|
executionService;
|
|
123208
123821
|
sessionDispatchWatcher;
|
|
123209
123822
|
autoPromptRunner;
|
|
@@ -125215,6 +125828,16 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125215
125828
|
hasPendingUpdates(sessionId) {
|
|
125216
125829
|
return this.store.has(sessionId) && this.store.get(sessionId).acpUpdateBuffer.length > 0;
|
|
125217
125830
|
}
|
|
125831
|
+
async hasPendingUserWork(sessionId) {
|
|
125832
|
+
const meta = (await this.workspaceDocument.repo.getDocMeta(getSessionRoomId(sessionId)))?.meta;
|
|
125833
|
+
if (!meta) {
|
|
125834
|
+
return false;
|
|
125835
|
+
}
|
|
125836
|
+
if (meta.processingUserMsgId) {
|
|
125837
|
+
return true;
|
|
125838
|
+
}
|
|
125839
|
+
return Boolean(meta.latestUserMsgId && meta.latestUserMsgId !== meta.lastHandledUserMsgId);
|
|
125840
|
+
}
|
|
125218
125841
|
isArchiveInFlight(sessionId) {
|
|
125219
125842
|
return this.archiveInFlight.has(sessionId);
|
|
125220
125843
|
}
|
|
@@ -125571,116 +126194,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125571
126194
|
}
|
|
125572
126195
|
}
|
|
125573
126196
|
}
|
|
125574
|
-
function getAvailableMemoryBytes() {
|
|
125575
|
-
const systemAvailable = getSystemAvailableMemoryBytes();
|
|
125576
|
-
const cgroupAvailable = getCgroupAvailableMemoryBytes();
|
|
125577
|
-
if (cgroupAvailable !== null) {
|
|
125578
|
-
return Math.min(systemAvailable, cgroupAvailable);
|
|
125579
|
-
}
|
|
125580
|
-
return systemAvailable;
|
|
125581
|
-
}
|
|
125582
|
-
function getEffectiveMemoryLimitBytes() {
|
|
125583
|
-
const totalMem = os__default.totalmem();
|
|
125584
|
-
const cgroupMax = getCgroupMemoryMaxBytes();
|
|
125585
|
-
if (cgroupMax !== null) {
|
|
125586
|
-
return Math.min(totalMem, cgroupMax);
|
|
125587
|
-
}
|
|
125588
|
-
return totalMem;
|
|
125589
|
-
}
|
|
125590
|
-
function getSystemAvailableMemoryBytes() {
|
|
125591
|
-
try {
|
|
125592
|
-
const meminfo = readFileSync("/proc/meminfo", "utf8");
|
|
125593
|
-
const match5 = meminfo.match(/MemAvailable:\s+(\d+)/);
|
|
125594
|
-
if (match5?.[1]) {
|
|
125595
|
-
return parseInt(match5[1], 10) * 1024;
|
|
125596
|
-
}
|
|
125597
|
-
} catch {
|
|
125598
|
-
}
|
|
125599
|
-
return os__default.freemem();
|
|
125600
|
-
}
|
|
125601
|
-
function getCgroupMemoryMaxBytes() {
|
|
125602
|
-
try {
|
|
125603
|
-
const cgroupPath = readSelfCgroupPath();
|
|
125604
|
-
if (cgroupPath === null) return null;
|
|
125605
|
-
let tightest = null;
|
|
125606
|
-
let current2 = cgroupPath;
|
|
125607
|
-
for (let depth = 0; depth < 20; depth++) {
|
|
125608
|
-
const memMaxPath = `/sys/fs/cgroup${current2 === "/" ? "" : current2}/memory.max`;
|
|
125609
|
-
const raw = readFileSafe(memMaxPath);
|
|
125610
|
-
if (raw !== null) {
|
|
125611
|
-
const trimmed = raw.trim();
|
|
125612
|
-
if (trimmed !== "max") {
|
|
125613
|
-
const value = parseInt(trimmed, 10);
|
|
125614
|
-
if (Number.isFinite(value) && value > 0) {
|
|
125615
|
-
tightest = tightest === null ? value : Math.min(tightest, value);
|
|
125616
|
-
}
|
|
125617
|
-
}
|
|
125618
|
-
}
|
|
125619
|
-
if (current2 === "/" || current2 === "") break;
|
|
125620
|
-
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
125621
|
-
if (parent === current2) break;
|
|
125622
|
-
current2 = parent;
|
|
125623
|
-
}
|
|
125624
|
-
return tightest;
|
|
125625
|
-
} catch {
|
|
125626
|
-
return null;
|
|
125627
|
-
}
|
|
125628
|
-
}
|
|
125629
|
-
function getCgroupAvailableMemoryBytes() {
|
|
125630
|
-
try {
|
|
125631
|
-
const cgroupPath = readSelfCgroupPath();
|
|
125632
|
-
if (cgroupPath === null) return null;
|
|
125633
|
-
let tightestMax = null;
|
|
125634
|
-
let tightestPath = null;
|
|
125635
|
-
let current2 = cgroupPath;
|
|
125636
|
-
for (let depth = 0; depth < 20; depth++) {
|
|
125637
|
-
const prefix = `/sys/fs/cgroup${current2 === "/" ? "" : current2}`;
|
|
125638
|
-
const raw = readFileSafe(`${prefix}/memory.max`);
|
|
125639
|
-
if (raw !== null) {
|
|
125640
|
-
const trimmed = raw.trim();
|
|
125641
|
-
if (trimmed !== "max") {
|
|
125642
|
-
const value = parseInt(trimmed, 10);
|
|
125643
|
-
if (Number.isFinite(value) && value > 0) {
|
|
125644
|
-
if (tightestMax === null || value < tightestMax) {
|
|
125645
|
-
tightestMax = value;
|
|
125646
|
-
tightestPath = prefix;
|
|
125647
|
-
}
|
|
125648
|
-
}
|
|
125649
|
-
}
|
|
125650
|
-
}
|
|
125651
|
-
if (current2 === "/" || current2 === "") break;
|
|
125652
|
-
const parent = current2.substring(0, current2.lastIndexOf("/")) || "/";
|
|
125653
|
-
if (parent === current2) break;
|
|
125654
|
-
current2 = parent;
|
|
125655
|
-
}
|
|
125656
|
-
if (tightestMax === null || tightestPath === null) return null;
|
|
125657
|
-
const currentRaw = readFileSafe(`${tightestPath}/memory.current`);
|
|
125658
|
-
if (currentRaw === null) return null;
|
|
125659
|
-
const currentUsage = parseInt(currentRaw.trim(), 10);
|
|
125660
|
-
if (!Number.isFinite(currentUsage)) return null;
|
|
125661
|
-
return Math.max(0, tightestMax - currentUsage);
|
|
125662
|
-
} catch {
|
|
125663
|
-
return null;
|
|
125664
|
-
}
|
|
125665
|
-
}
|
|
125666
|
-
function readSelfCgroupPath() {
|
|
125667
|
-
try {
|
|
125668
|
-
const content = readFileSync("/proc/self/cgroup", "utf8");
|
|
125669
|
-
const line3 = content.split("\n").map((l) => l.trim()).find((l) => l.startsWith("0::"));
|
|
125670
|
-
if (!line3) return null;
|
|
125671
|
-
const cgroupPath = line3.slice(3).trim();
|
|
125672
|
-
return cgroupPath || "/";
|
|
125673
|
-
} catch {
|
|
125674
|
-
return null;
|
|
125675
|
-
}
|
|
125676
|
-
}
|
|
125677
|
-
function readFileSafe(filePath) {
|
|
125678
|
-
try {
|
|
125679
|
-
return readFileSync(filePath, "utf8");
|
|
125680
|
-
} catch {
|
|
125681
|
-
return null;
|
|
125682
|
-
}
|
|
125683
|
-
}
|
|
125684
126197
|
function readEnvNumber(key2, fallback2) {
|
|
125685
126198
|
const raw = process.env[key2];
|
|
125686
126199
|
if (!raw) return fallback2;
|
|
@@ -125688,6 +126201,8 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125688
126201
|
return Number.isFinite(parsed) ? parsed : fallback2;
|
|
125689
126202
|
}
|
|
125690
126203
|
const GIB = 1024 * 1024 * 1024;
|
|
126204
|
+
const WINDOWS_COMMIT_THRESHOLD_FLOOR_BYTES = 512 * 1024 * 1024;
|
|
126205
|
+
const WINDOWS_COMMIT_THRESHOLD_CEILING_BYTES = 2 * GIB;
|
|
125691
126206
|
function defaultMemoryThresholdBytes() {
|
|
125692
126207
|
const tenPercent = Math.floor(getEffectiveMemoryLimitBytes() * 0.1);
|
|
125693
126208
|
return Math.max(GIB, Math.min(4 * GIB, tenPercent));
|
|
@@ -125700,6 +126215,9 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125700
126215
|
memoryThresholdBytes: readEnvNumber("LODY_SESSION_GC_MEMORY_THRESHOLD_BYTES", defaultMemoryThresholdBytes())
|
|
125701
126216
|
};
|
|
125702
126217
|
}
|
|
126218
|
+
function getWindowsCommitThresholdBytes(memoryThresholdBytes) {
|
|
126219
|
+
return Math.max(WINDOWS_COMMIT_THRESHOLD_FLOOR_BYTES, Math.min(WINDOWS_COMMIT_THRESHOLD_CEILING_BYTES, memoryThresholdBytes));
|
|
126220
|
+
}
|
|
125703
126221
|
class SessionGCManager {
|
|
125704
126222
|
constructor(config2, deps) {
|
|
125705
126223
|
this.config = config2;
|
|
@@ -125723,7 +126241,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125723
126241
|
}
|
|
125724
126242
|
async sweep() {
|
|
125725
126243
|
const sweepStart = Date.now();
|
|
125726
|
-
const candidates = this.getIdleCandidates();
|
|
126244
|
+
const candidates = await this.getIdleCandidates();
|
|
125727
126245
|
if (candidates.length === 0) {
|
|
125728
126246
|
return;
|
|
125729
126247
|
}
|
|
@@ -125731,7 +126249,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125731
126249
|
let cleaned = 0;
|
|
125732
126250
|
let skipped = 0;
|
|
125733
126251
|
for (const { sessionId } of candidates) {
|
|
125734
|
-
if (!this.isStillEligibleForGC(sessionId)) {
|
|
126252
|
+
if (!await this.isStillEligibleForGC(sessionId)) {
|
|
125735
126253
|
skipped++;
|
|
125736
126254
|
continue;
|
|
125737
126255
|
}
|
|
@@ -125749,19 +126267,50 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125749
126267
|
this.deps.logger.debug(`[GC] Sweep completed: cleaned ${cleaned}/${candidates.length} sessions in ${sweepDuration}ms`);
|
|
125750
126268
|
}
|
|
125751
126269
|
async evictForMemoryPressure(excludeSessionId) {
|
|
126270
|
+
const thresholdBytes = this.config.memoryThresholdBytes;
|
|
126271
|
+
const commitThresholdBytes = getWindowsCommitThresholdBytes(thresholdBytes);
|
|
126272
|
+
let memorySnapshot = getMemoryPressureSnapshot();
|
|
126273
|
+
let availableMemory = memorySnapshot.availableMemoryBytes;
|
|
126274
|
+
let pressureReason = this.getPressureReason(memorySnapshot, thresholdBytes, commitThresholdBytes);
|
|
125752
126275
|
if (!this.config.enabled) {
|
|
125753
|
-
return
|
|
126276
|
+
return {
|
|
126277
|
+
availableMemoryBytes: availableMemory,
|
|
126278
|
+
thresholdBytes,
|
|
126279
|
+
hadMemoryPressure: false,
|
|
126280
|
+
stillUnderPressure: false,
|
|
126281
|
+
evictedSessionIds: [],
|
|
126282
|
+
pressureReason: null,
|
|
126283
|
+
...memorySnapshot.availableCommitBytes !== void 0 ? {
|
|
126284
|
+
availableCommitBytes: memorySnapshot.availableCommitBytes,
|
|
126285
|
+
commitThresholdBytes,
|
|
126286
|
+
commitLimitBytes: memorySnapshot.commitLimitBytes,
|
|
126287
|
+
committedBytes: memorySnapshot.committedBytes
|
|
126288
|
+
} : {}
|
|
126289
|
+
};
|
|
125754
126290
|
}
|
|
125755
|
-
|
|
125756
|
-
|
|
125757
|
-
|
|
126291
|
+
if (pressureReason === null) {
|
|
126292
|
+
return {
|
|
126293
|
+
availableMemoryBytes: availableMemory,
|
|
126294
|
+
thresholdBytes,
|
|
126295
|
+
hadMemoryPressure: false,
|
|
126296
|
+
stillUnderPressure: false,
|
|
126297
|
+
evictedSessionIds: [],
|
|
126298
|
+
pressureReason: null,
|
|
126299
|
+
...memorySnapshot.availableCommitBytes !== void 0 ? {
|
|
126300
|
+
availableCommitBytes: memorySnapshot.availableCommitBytes,
|
|
126301
|
+
commitThresholdBytes,
|
|
126302
|
+
commitLimitBytes: memorySnapshot.commitLimitBytes,
|
|
126303
|
+
committedBytes: memorySnapshot.committedBytes
|
|
126304
|
+
} : {}
|
|
126305
|
+
};
|
|
125758
126306
|
}
|
|
125759
|
-
|
|
126307
|
+
const commitText = memorySnapshot.availableCommitBytes !== void 0 ? `, commit headroom ${Math.round(memorySnapshot.availableCommitBytes / 1024 / 1024)}MB (threshold: ${Math.round(commitThresholdBytes / 1024 / 1024)}MB)` : "";
|
|
126308
|
+
this.deps.logger.debug(`[GC] Memory pressure detected: ${Math.round(availableMemory / 1024 / 1024)}MB available (threshold: ${Math.round(thresholdBytes / 1024 / 1024)}MB)${commitText}`);
|
|
125760
126309
|
const sessions = this.getSessionsWithIdleTime();
|
|
125761
126310
|
sessions.sort((a, b) => b.idleMs - a.idleMs);
|
|
125762
|
-
|
|
126311
|
+
const evictedSessionIds = [];
|
|
125763
126312
|
for (const { sessionId, idleMs } of sessions) {
|
|
125764
|
-
if (
|
|
126313
|
+
if (pressureReason === null) {
|
|
125765
126314
|
break;
|
|
125766
126315
|
}
|
|
125767
126316
|
if (excludeSessionId && sessionId === excludeSessionId) {
|
|
@@ -125770,30 +126319,63 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125770
126319
|
if (idleMs === 0) {
|
|
125771
126320
|
continue;
|
|
125772
126321
|
}
|
|
125773
|
-
if (!this.isEligibleForCleanup(sessionId)) {
|
|
126322
|
+
if (!await this.isEligibleForCleanup(sessionId)) {
|
|
125774
126323
|
continue;
|
|
125775
126324
|
}
|
|
125776
126325
|
try {
|
|
125777
126326
|
this.deps.logger.debug(`[GC] Evicting session ${sessionId} (idle ${Math.round(idleMs / 1e3)}s) due to memory pressure`);
|
|
125778
126327
|
await this.deps.cleanSession(sessionId);
|
|
125779
|
-
|
|
125780
|
-
|
|
126328
|
+
evictedSessionIds.push(sessionId);
|
|
126329
|
+
memorySnapshot = getMemoryPressureSnapshot();
|
|
126330
|
+
availableMemory = memorySnapshot.availableMemoryBytes;
|
|
126331
|
+
pressureReason = this.getPressureReason(memorySnapshot, thresholdBytes, commitThresholdBytes);
|
|
125781
126332
|
} catch (error2) {
|
|
125782
126333
|
this.deps.logger.error(`[GC] Failed to evict session ${sessionId}: ${formatErrorMessage(error2)}`);
|
|
125783
126334
|
}
|
|
125784
126335
|
}
|
|
125785
|
-
|
|
125786
|
-
|
|
126336
|
+
const stillUnderPressure = pressureReason !== null;
|
|
126337
|
+
if (evictedSessionIds.length > 0) {
|
|
126338
|
+
this.deps.logger.debug(`[GC] Memory pressure eviction complete: evicted ${evictedSessionIds.length} sessions, available memory now ${Math.round(availableMemory / 1024 / 1024)}MB`);
|
|
126339
|
+
} else if (stillUnderPressure) {
|
|
126340
|
+
this.deps.logger.debug("[GC] Memory pressure persists but no idle sessions were eligible for eviction");
|
|
126341
|
+
}
|
|
126342
|
+
return {
|
|
126343
|
+
availableMemoryBytes: availableMemory,
|
|
126344
|
+
thresholdBytes,
|
|
126345
|
+
hadMemoryPressure: true,
|
|
126346
|
+
stillUnderPressure,
|
|
126347
|
+
evictedSessionIds,
|
|
126348
|
+
pressureReason,
|
|
126349
|
+
...memorySnapshot.availableCommitBytes !== void 0 ? {
|
|
126350
|
+
availableCommitBytes: memorySnapshot.availableCommitBytes,
|
|
126351
|
+
commitThresholdBytes,
|
|
126352
|
+
commitLimitBytes: memorySnapshot.commitLimitBytes,
|
|
126353
|
+
committedBytes: memorySnapshot.committedBytes
|
|
126354
|
+
} : {}
|
|
126355
|
+
};
|
|
126356
|
+
}
|
|
126357
|
+
getPressureReason(snapshot, thresholdBytes, commitThresholdBytes) {
|
|
126358
|
+
const physicalPressure = snapshot.availableMemoryBytes < thresholdBytes;
|
|
126359
|
+
const commitPressure = snapshot.availableCommitBytes !== void 0 && snapshot.availableCommitBytes < commitThresholdBytes;
|
|
126360
|
+
if (physicalPressure && commitPressure) {
|
|
126361
|
+
return "physical_and_commit";
|
|
126362
|
+
}
|
|
126363
|
+
if (physicalPressure) {
|
|
126364
|
+
return "physical";
|
|
125787
126365
|
}
|
|
126366
|
+
if (commitPressure) {
|
|
126367
|
+
return "commit";
|
|
126368
|
+
}
|
|
126369
|
+
return null;
|
|
125788
126370
|
}
|
|
125789
|
-
getIdleCandidates() {
|
|
126371
|
+
async getIdleCandidates() {
|
|
125790
126372
|
const sessions = this.getSessionsWithIdleTime();
|
|
125791
126373
|
const candidates = [];
|
|
125792
126374
|
for (const session of sessions) {
|
|
125793
126375
|
if (session.idleMs < this.config.idleTimeoutMs) {
|
|
125794
126376
|
continue;
|
|
125795
126377
|
}
|
|
125796
|
-
if (!this.isEligibleForCleanup(session.sessionId)) {
|
|
126378
|
+
if (!await this.isEligibleForCleanup(session.sessionId)) {
|
|
125797
126379
|
continue;
|
|
125798
126380
|
}
|
|
125799
126381
|
candidates.push(session);
|
|
@@ -125821,20 +126403,23 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125821
126403
|
}
|
|
125822
126404
|
return result;
|
|
125823
126405
|
}
|
|
125824
|
-
isEligibleForCleanup(sessionId) {
|
|
126406
|
+
async isEligibleForCleanup(sessionId) {
|
|
125825
126407
|
if (this.deps.hasActiveTurn(sessionId)) {
|
|
125826
126408
|
return false;
|
|
125827
126409
|
}
|
|
125828
126410
|
if (this.deps.hasPendingUpdates(sessionId)) {
|
|
125829
126411
|
return false;
|
|
125830
126412
|
}
|
|
126413
|
+
if (await this.deps.hasPendingUserWork(sessionId)) {
|
|
126414
|
+
return false;
|
|
126415
|
+
}
|
|
125831
126416
|
if (this.deps.isArchiveInFlight(sessionId)) {
|
|
125832
126417
|
return false;
|
|
125833
126418
|
}
|
|
125834
126419
|
return true;
|
|
125835
126420
|
}
|
|
125836
|
-
isStillEligibleForGC(sessionId) {
|
|
125837
|
-
if (!this.isEligibleForCleanup(sessionId)) {
|
|
126421
|
+
async isStillEligibleForGC(sessionId) {
|
|
126422
|
+
if (!await this.isEligibleForCleanup(sessionId)) {
|
|
125838
126423
|
return false;
|
|
125839
126424
|
}
|
|
125840
126425
|
const lastActivity = this.deps.getSessionLastActivity(sessionId);
|
|
@@ -125962,6 +126547,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125962
126547
|
getSessionLastActivity: (sessionId) => handler.getLastActivity(sessionId),
|
|
125963
126548
|
hasActiveTurn: (sessionId) => handler.hasActiveTurn(sessionId),
|
|
125964
126549
|
hasPendingUpdates: (sessionId) => handler.hasPendingUpdates(sessionId),
|
|
126550
|
+
hasPendingUserWork: async (sessionId) => await handler.hasPendingUserWork(sessionId),
|
|
125965
126551
|
isArchiveInFlight: (sessionId) => handler.isArchiveInFlight(sessionId),
|
|
125966
126552
|
cleanSession: (sessionId) => handler.cleanSessionForGC(sessionId),
|
|
125967
126553
|
getSessionIds: () => handler.getTrackedSessionIds(),
|
|
@@ -125970,8 +126556,16 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
|
|
|
125970
126556
|
this.gcManager.start();
|
|
125971
126557
|
handler.setEvictForMemoryPressure(async (excludeSessionId) => {
|
|
125972
126558
|
if (this.gcManager) {
|
|
125973
|
-
await this.gcManager.evictForMemoryPressure(excludeSessionId);
|
|
126559
|
+
return await this.gcManager.evictForMemoryPressure(excludeSessionId);
|
|
125974
126560
|
}
|
|
126561
|
+
return {
|
|
126562
|
+
availableMemoryBytes: 0,
|
|
126563
|
+
thresholdBytes: 0,
|
|
126564
|
+
hadMemoryPressure: false,
|
|
126565
|
+
stillUnderPressure: false,
|
|
126566
|
+
evictedSessionIds: [],
|
|
126567
|
+
pressureReason: null
|
|
126568
|
+
};
|
|
125975
126569
|
});
|
|
125976
126570
|
}
|
|
125977
126571
|
requireSessionManager() {
|
|
@@ -133169,6 +133763,9 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
133169
133763
|
isControlPlaneReady() {
|
|
133170
133764
|
return this.documentManager.isTransportConnected();
|
|
133171
133765
|
}
|
|
133766
|
+
isControlPlaneRecovering() {
|
|
133767
|
+
return this.documentManager.isTransportRecovering();
|
|
133768
|
+
}
|
|
133172
133769
|
getActiveSessionCount() {
|
|
133173
133770
|
return this.runtime.getActiveSessionCount();
|
|
133174
133771
|
}
|
|
@@ -134998,23 +135595,26 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
134998
135595
|
refreshRuntimeState() {
|
|
134999
135596
|
const desiredCount = this.desiredWorkspaces.size;
|
|
135000
135597
|
let connectedCount = 0;
|
|
135598
|
+
let reconnectingCount = 0;
|
|
135001
135599
|
let totalActiveSessions = 0;
|
|
135002
135600
|
let totalConnectedRooms = 0;
|
|
135003
135601
|
for (const runtime of this.runtimes.values()) {
|
|
135004
135602
|
if (runtime.lody.isControlPlaneReady()) {
|
|
135005
135603
|
connectedCount += 1;
|
|
135604
|
+
} else if (runtime.lody.isControlPlaneRecovering()) {
|
|
135605
|
+
reconnectingCount += 1;
|
|
135006
135606
|
}
|
|
135007
135607
|
totalActiveSessions += runtime.lody.getActiveSessionCount();
|
|
135008
135608
|
totalConnectedRooms += runtime.lody.getConnectedRoomCount();
|
|
135009
135609
|
}
|
|
135010
135610
|
this.runtimeStateReporter.setActiveSessionCount(totalActiveSessions);
|
|
135011
135611
|
this.runtimeStateReporter.setConnectedRoomCount(totalConnectedRooms);
|
|
135012
|
-
const
|
|
135612
|
+
const hasWorkspaceRetry = this.retryTimers.size > 0 || this.startInFlight.size > 0;
|
|
135613
|
+
const nextConnectivity = desiredCount === 0 ? "online" : connectedCount === desiredCount ? "online" : reconnectingCount > 0 || hasWorkspaceRetry ? "reconnecting" : "offline";
|
|
135013
135614
|
if (this.lastConnectivity !== nextConnectivity) {
|
|
135014
135615
|
this.lastConnectivity = nextConnectivity;
|
|
135015
135616
|
this.runtimeStateReporter.setConnectivity(nextConnectivity);
|
|
135016
135617
|
}
|
|
135017
|
-
const hasWorkspaceRetry = this.retryTimers.size > 0 || this.startInFlight.size > 0;
|
|
135018
135618
|
if (hasWorkspaceRetry && !this.hasWorkspaceRetryIssue) {
|
|
135019
135619
|
this.hasWorkspaceRetryIssue = true;
|
|
135020
135620
|
this.runtimeStateReporter.upsertIssue({
|
|
@@ -167297,10 +167897,18 @@ ${entry2.text}`).join("\n\n");
|
|
|
167297
167897
|
}
|
|
167298
167898
|
const DEFAULT_MIN_RETRY_MS$1 = 1e3;
|
|
167299
167899
|
const DEFAULT_MAX_RETRY_MS$1 = 3e4;
|
|
167300
|
-
|
|
167900
|
+
const DEFAULT_JITTER_FRACTION = 0.2;
|
|
167901
|
+
function buildRetryDelay(attempt, minMs = DEFAULT_MIN_RETRY_MS$1, maxMs = DEFAULT_MAX_RETRY_MS$1, options = {}) {
|
|
167301
167902
|
const exponent = Math.max(0, attempt);
|
|
167302
|
-
const
|
|
167303
|
-
|
|
167903
|
+
const baseDelay = Math.min(minMs * 2 ** exponent, maxMs);
|
|
167904
|
+
const jitterFraction = Math.max(0, options.jitterFraction ?? DEFAULT_JITTER_FRACTION);
|
|
167905
|
+
if (jitterFraction === 0) {
|
|
167906
|
+
return baseDelay;
|
|
167907
|
+
}
|
|
167908
|
+
const random2 = options.random ?? Math.random;
|
|
167909
|
+
const randomValue = Math.min(1, Math.max(0, random2()));
|
|
167910
|
+
const jitterMultiplier = 1 + (randomValue * 2 - 1) * jitterFraction;
|
|
167911
|
+
return Math.min(Math.max(0, Math.round(baseDelay * jitterMultiplier)), maxMs);
|
|
167304
167912
|
}
|
|
167305
167913
|
class FailureWindow {
|
|
167306
167914
|
historyMs = [];
|
|
@@ -167361,6 +167969,7 @@ ${result.stderr}`;
|
|
|
167361
167969
|
probeFailureThreshold;
|
|
167362
167970
|
minRetryMs;
|
|
167363
167971
|
maxRetryMs;
|
|
167972
|
+
retryRandom;
|
|
167364
167973
|
failureWindow;
|
|
167365
167974
|
triggered = false;
|
|
167366
167975
|
inFlight = false;
|
|
@@ -167392,6 +168001,7 @@ ${result.stderr}`;
|
|
|
167392
168001
|
this.probeFailureThreshold = options.probeFailureThreshold ?? DEFAULT_PROBE_FAILURE_THRESHOLD;
|
|
167393
168002
|
this.minRetryMs = options.minRetryMs ?? DEFAULT_MIN_RETRY_MS;
|
|
167394
168003
|
this.maxRetryMs = options.maxRetryMs ?? DEFAULT_MAX_RETRY_MS;
|
|
168004
|
+
this.retryRandom = options.retryRandom ?? Math.random;
|
|
167395
168005
|
this.failureWindow = new FailureWindow(options.fatalFailureWindowMs ?? DEFAULT_FATAL_FAILURE_WINDOW_MS, options.fatalFailureThreshold ?? DEFAULT_FATAL_FAILURE_THRESHOLD);
|
|
167396
168006
|
}
|
|
167397
168007
|
getState() {
|
|
@@ -167471,6 +168081,9 @@ ${result.stderr}`;
|
|
|
167471
168081
|
this.latestRuntimeState = runtimeState;
|
|
167472
168082
|
this.probeFailureCount = 0;
|
|
167473
168083
|
this.probeUnavailable = false;
|
|
168084
|
+
this.clearRetryTimer();
|
|
168085
|
+
this.attempt = 0;
|
|
168086
|
+
this.failureWindow.reset();
|
|
167474
168087
|
this.lastStateMessage = void 0;
|
|
167475
168088
|
} else {
|
|
167476
168089
|
this.probeFailureCount += 1;
|
|
@@ -167491,7 +168104,9 @@ ${result.stderr}`;
|
|
|
167491
168104
|
} else if (this.fatalReason) {
|
|
167492
168105
|
phase = "fatal";
|
|
167493
168106
|
} else if (runtime) {
|
|
167494
|
-
phase = runtime.phase === "fatal" ? "fatal" : runtime.phase;
|
|
168107
|
+
phase = runtime.phase === "fatal" ? "fatal" : runtime.connectivity === "reconnecting" ? "reconnecting" : runtime.phase;
|
|
168108
|
+
} else if (this.retryTimer) {
|
|
168109
|
+
phase = "reconnecting";
|
|
167495
168110
|
} else if (childRunning) {
|
|
167496
168111
|
phase = this.probeUnavailable ? "offline" : "starting";
|
|
167497
168112
|
} else if (this.probeUnavailable && !this.inFlight && !this.retryTimer) {
|
|
@@ -167548,7 +168163,9 @@ ${result.stderr}`;
|
|
|
167548
168163
|
scheduleRetry(reason, recordFailure = true) {
|
|
167549
168164
|
if (this.fatalReason) return;
|
|
167550
168165
|
if (recordFailure && this.recordFailure(reason)) return;
|
|
167551
|
-
const delay2 = buildRetryDelay(this.attempt, this.minRetryMs, this.maxRetryMs
|
|
168166
|
+
const delay2 = buildRetryDelay(this.attempt, this.minRetryMs, this.maxRetryMs, {
|
|
168167
|
+
random: this.retryRandom
|
|
168168
|
+
});
|
|
167552
168169
|
this.attempt += 1;
|
|
167553
168170
|
this.clearRetryTimer();
|
|
167554
168171
|
this.pendingRetryInMs = delay2;
|