lody 0.52.0 → 0.52.2
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 +154 -47
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -36822,7 +36822,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36822
36822
|
return client;
|
|
36823
36823
|
}
|
|
36824
36824
|
const name = "lody";
|
|
36825
|
-
const version$4 = "0.52.
|
|
36825
|
+
const version$4 = "0.52.2";
|
|
36826
36826
|
const description$1 = "Lody Agent CLI tool for managing remote command execution";
|
|
36827
36827
|
const type$2 = "module";
|
|
36828
36828
|
const main$3 = "dist/index.js";
|
|
@@ -36865,7 +36865,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36865
36865
|
"node": ">=18.0.0"
|
|
36866
36866
|
};
|
|
36867
36867
|
const optionalDependencies = {
|
|
36868
|
-
"acp-extension-claude": "0.34.
|
|
36868
|
+
"acp-extension-claude": "0.34.3",
|
|
36869
36869
|
"acp-extension-codex": "0.14.3"
|
|
36870
36870
|
};
|
|
36871
36871
|
const devDependencies = {
|
|
@@ -85476,13 +85476,14 @@ ${tailedOutput}` : null;
|
|
|
85476
85476
|
};
|
|
85477
85477
|
function withSlowOperationWarning(promise, logger2, operationName, sessionId, intervalMs = 1e4) {
|
|
85478
85478
|
let completed = false;
|
|
85479
|
-
|
|
85479
|
+
const startMs = Date.now();
|
|
85480
85480
|
const interval2 = setInterval(() => {
|
|
85481
|
-
elapsedMs += intervalMs;
|
|
85482
85481
|
if (!completed) {
|
|
85482
|
+
const elapsedMs = Date.now() - startMs;
|
|
85483
85483
|
logger2.debug(`[${sessionId}] Operation "${operationName}" is still pending after ${Math.round(elapsedMs / 1e3)}s - possible hang`);
|
|
85484
85484
|
}
|
|
85485
85485
|
}, intervalMs);
|
|
85486
|
+
interval2.unref?.();
|
|
85486
85487
|
return promise.finally(() => {
|
|
85487
85488
|
completed = true;
|
|
85488
85489
|
clearInterval(interval2);
|
|
@@ -98397,11 +98398,13 @@ stream:${scope2.streamId}`;
|
|
|
98397
98398
|
machineId = null;
|
|
98398
98399
|
machineKey = null;
|
|
98399
98400
|
sessionKeys = /* @__PURE__ */ new Map();
|
|
98401
|
+
machineHeartbeatSeq = 0;
|
|
98400
98402
|
started = false;
|
|
98401
98403
|
stopped = false;
|
|
98402
98404
|
start() {
|
|
98403
98405
|
if (this.started || this.stopped) return;
|
|
98404
98406
|
this.started = true;
|
|
98407
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Joining Loro presence room`);
|
|
98405
98408
|
void this.transport.join({
|
|
98406
98409
|
onStatusChange: (status) => {
|
|
98407
98410
|
this.options.logger.debug(`[${this.options.workspaceId}] Loro presence room status: ${status}`);
|
|
@@ -98413,6 +98416,7 @@ stream:${scope2.streamId}`;
|
|
|
98413
98416
|
}
|
|
98414
98417
|
if (result.ok) {
|
|
98415
98418
|
this.subscription = result.value;
|
|
98419
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Loro presence subscription established`);
|
|
98416
98420
|
return;
|
|
98417
98421
|
}
|
|
98418
98422
|
this.options.logger.debug(`[${this.options.workspaceId}] Failed to join Loro presence room: ${formatErrorMessage(result.error)}`);
|
|
@@ -98442,6 +98446,7 @@ stream:${scope2.streamId}`;
|
|
|
98442
98446
|
}
|
|
98443
98447
|
writeMachineHeartbeat() {
|
|
98444
98448
|
if (this.stopped || !this.machineId || !this.machineKey) return;
|
|
98449
|
+
const seq2 = ++this.machineHeartbeatSeq;
|
|
98445
98450
|
const state2 = {
|
|
98446
98451
|
kind: "machine",
|
|
98447
98452
|
machineId: this.machineId,
|
|
@@ -98449,6 +98454,7 @@ stream:${scope2.streamId}`;
|
|
|
98449
98454
|
updatedAt: getServerNow()
|
|
98450
98455
|
};
|
|
98451
98456
|
this.store.set(this.machineKey, state2);
|
|
98457
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Loro presence machine heartbeat written (seq=${seq2} updatedAt=${state2.updatedAt})`);
|
|
98452
98458
|
}
|
|
98453
98459
|
setSessionPresence(args2) {
|
|
98454
98460
|
if (this.stopped) return;
|
|
@@ -116856,6 +116862,18 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116856
116862
|
let metaSub = null;
|
|
116857
116863
|
const initialMetaSync = createDeferred();
|
|
116858
116864
|
let initialMetaSyncCompleted = false;
|
|
116865
|
+
let persistFlushSeq = 0;
|
|
116866
|
+
const flushRepoForPersist = async (reason) => {
|
|
116867
|
+
const currentRepo = repo;
|
|
116868
|
+
if (!currentRepo) {
|
|
116869
|
+
return;
|
|
116870
|
+
}
|
|
116871
|
+
const seq2 = ++persistFlushSeq;
|
|
116872
|
+
const startedAt = Date.now();
|
|
116873
|
+
logger2.debug(`[${workspaceId}] Loro repo flush started (reason=${reason} seq=${seq2})`);
|
|
116874
|
+
await withSlowOperationWarning(currentRepo.flush(), logger2, `loro-repo.flush(${reason}, seq=${seq2})`, workspaceId);
|
|
116875
|
+
logger2.debug(`[${workspaceId}] Loro repo flush completed (reason=${reason} seq=${seq2} duration=${Date.now() - startedAt}ms)`);
|
|
116876
|
+
};
|
|
116859
116877
|
const transportAdapter = new StreamsTransportAdapter({
|
|
116860
116878
|
bucketId: LORO_STREAMS_BUCKET_ID,
|
|
116861
116879
|
metaStreamId: getLoroMetaStreamId(workspaceId),
|
|
@@ -116869,10 +116887,10 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116869
116887
|
debounceMs: 5e3
|
|
116870
116888
|
},
|
|
116871
116889
|
onPersistDoc: async () => {
|
|
116872
|
-
await
|
|
116890
|
+
await flushRepoForPersist("doc");
|
|
116873
116891
|
},
|
|
116874
116892
|
onPersistMeta: async () => {
|
|
116875
|
-
await
|
|
116893
|
+
await flushRepoForPersist("meta");
|
|
116876
116894
|
}
|
|
116877
116895
|
});
|
|
116878
116896
|
let manager = null;
|
|
@@ -117166,8 +117184,11 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
117166
117184
|
if (!this.machine) {
|
|
117167
117185
|
return;
|
|
117168
117186
|
}
|
|
117169
|
-
|
|
117187
|
+
const startedAt = Date.now();
|
|
117188
|
+
this.logger.debug(`[${this.workspaceId}] Machine heartbeat writing presence and meta`);
|
|
117170
117189
|
this.presenceRuntime?.writeMachineHeartbeat();
|
|
117190
|
+
await withSlowOperationWarning(this.machine.sendHeartbeat(), this.logger, "machine.sendHeartbeat repo.upsertDocMeta(lastSeen)", this.workspaceId);
|
|
117191
|
+
this.logger.debug(`[${this.workspaceId}] Machine heartbeat meta write completed (duration=${Date.now() - startedAt}ms)`);
|
|
117171
117192
|
}
|
|
117172
117193
|
async restoreMachineDocument(machineId) {
|
|
117173
117194
|
const machineRoomId = getMachineRoomId(machineId);
|
|
@@ -122002,11 +122023,19 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122002
122023
|
return {};
|
|
122003
122024
|
}
|
|
122004
122025
|
async extMethod(method, params) {
|
|
122005
|
-
|
|
122026
|
+
try {
|
|
122027
|
+
this.handleExtensionMessage(method, params);
|
|
122028
|
+
} catch (error2) {
|
|
122029
|
+
this.logger.warn(`Error handling extension method ${method}: ${error2}`);
|
|
122030
|
+
}
|
|
122006
122031
|
return {};
|
|
122007
122032
|
}
|
|
122008
122033
|
async extNotification(method, params) {
|
|
122009
|
-
|
|
122034
|
+
try {
|
|
122035
|
+
this.handleExtensionMessage(method, params);
|
|
122036
|
+
} catch (error2) {
|
|
122037
|
+
this.logger.warn(`Error handling extension notification ${method}: ${error2}`);
|
|
122038
|
+
}
|
|
122010
122039
|
}
|
|
122011
122040
|
handleExtensionMessage(method, params) {
|
|
122012
122041
|
const resolvedMethod = method.startsWith("_") ? method.slice(1) : method;
|
|
@@ -122106,7 +122135,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122106
122135
|
this.supportsClose = !!closeCapability;
|
|
122107
122136
|
const hasCloseMethod = typeof connection.closeSession === "function";
|
|
122108
122137
|
this.logger.debug(`[${this.options.sessionId}] ACP capabilities (loadSession=${this.supportsLoadSession ? "yes" : "no"} loadSessionMethod=${hasLoadSessionMethod ? "yes" : "no"} resume=${this.supportsResume ? "yes" : "no"} resumeMethod=${hasResumeMethod ? "yes" : "no"} close=${this.supportsClose ? "yes" : "no"} closeMethod=${hasCloseMethod ? "yes" : "no"})`);
|
|
122109
|
-
this.logger.debug(`[${this.options.sessionId}] About to
|
|
122138
|
+
this.logger.debug(`[${this.options.sessionId}] About to establish ACP session`);
|
|
122110
122139
|
const newSessionStart = performance$1.now();
|
|
122111
122140
|
this.options.onStartupStage?.({
|
|
122112
122141
|
type: "new_session_start"
|
|
@@ -122133,11 +122162,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122133
122162
|
const loadStart = performance$1.now();
|
|
122134
122163
|
try {
|
|
122135
122164
|
this.logger.debug(`[${this.options.sessionId}] Attempting ACP loadSession (acpSessionId=${resumeSessionId})`);
|
|
122136
|
-
const
|
|
122165
|
+
const ACP_LOAD_SESSION_TIMEOUT_MS = Math.max(0, timeoutOptions.loadSessionTimeoutMs ?? timeoutOptions.newSessionTimeoutMs ?? 12e4);
|
|
122166
|
+
const loadResponse = await withTimeout$2(withAbort(connection.loadSession({
|
|
122137
122167
|
sessionId: resumeSessionId,
|
|
122138
122168
|
cwd: workdir,
|
|
122139
122169
|
mcpServers
|
|
122140
|
-
}), startupAbort);
|
|
122170
|
+
}), startupAbort), this.logger, "connection.loadSession", this.options.sessionId, ACP_LOAD_SESSION_TIMEOUT_MS);
|
|
122141
122171
|
const loadDurationMs = performance$1.now() - loadStart;
|
|
122142
122172
|
sessionResponse = {
|
|
122143
122173
|
sessionId: resumeSessionId,
|
|
@@ -122158,11 +122188,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122158
122188
|
const resumeStart = performance$1.now();
|
|
122159
122189
|
try {
|
|
122160
122190
|
this.logger.debug(`[${this.options.sessionId}] Attempting ACP resume (acpSessionId=${resumeSessionId})`);
|
|
122161
|
-
const
|
|
122191
|
+
const ACP_RESUME_SESSION_TIMEOUT_MS = Math.max(0, timeoutOptions.resumeSessionTimeoutMs ?? timeoutOptions.newSessionTimeoutMs ?? 12e4);
|
|
122192
|
+
const resumeResponse = await withTimeout$2(withAbort(connection.resumeSession({
|
|
122162
122193
|
sessionId: resumeSessionId,
|
|
122163
122194
|
cwd: workdir,
|
|
122164
122195
|
mcpServers
|
|
122165
|
-
}), startupAbort);
|
|
122196
|
+
}), startupAbort), this.logger, "connection.resumeSession", this.options.sessionId, ACP_RESUME_SESSION_TIMEOUT_MS);
|
|
122166
122197
|
const resumeDurationMs = performance$1.now() - resumeStart;
|
|
122167
122198
|
sessionResponse = {
|
|
122168
122199
|
sessionId: resumeSessionId,
|
|
@@ -122218,21 +122249,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122218
122249
|
this.logger.error(`[${this.options.sessionId}] connection.prompt returned undefined - connection may be closed`);
|
|
122219
122250
|
return void 0;
|
|
122220
122251
|
}
|
|
122221
|
-
const PROMPT_WARN_AFTER_MS = 6e5;
|
|
122222
|
-
const PROMPT_NO_UPDATES_MS = 12e4;
|
|
122223
|
-
const PROMPT_WARN_INTERVAL_MS = 3e5;
|
|
122224
|
-
const startMs = Date.now();
|
|
122225
|
-
let completed = false;
|
|
122226
122252
|
let abortListener;
|
|
122227
|
-
const warningInterval = setInterval(() => {
|
|
122228
|
-
if (completed) return;
|
|
122229
|
-
const nowMs = Date.now();
|
|
122230
|
-
const elapsedMs = nowMs - startMs;
|
|
122231
|
-
if (elapsedMs < PROMPT_WARN_AFTER_MS) return;
|
|
122232
|
-
const noUpdatesMs = nowMs - this.lastSessionUpdateAtMs;
|
|
122233
|
-
if (noUpdatesMs < PROMPT_NO_UPDATES_MS) return;
|
|
122234
|
-
this.logger.debug(`[${this.options.sessionId}] Operation "connection.prompt" is still pending after ${Math.round(elapsedMs / 1e3)}s (no session updates for ${Math.round(noUpdatesMs / 1e3)}s)`);
|
|
122235
|
-
}, PROMPT_WARN_INTERVAL_MS);
|
|
122236
122253
|
try {
|
|
122237
122254
|
const abortPromise = abortSignal ? new Promise((_2, reject) => {
|
|
122238
122255
|
abortListener = () => {
|
|
@@ -122253,11 +122270,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122253
122270
|
this.logger.debug(`[${this.options.sessionId}] connection.prompt returned`);
|
|
122254
122271
|
return result;
|
|
122255
122272
|
} finally {
|
|
122256
|
-
completed = true;
|
|
122257
122273
|
if (abortSignal && abortListener) {
|
|
122258
122274
|
abortSignal.removeEventListener("abort", abortListener);
|
|
122259
122275
|
}
|
|
122260
|
-
clearInterval(warningInterval);
|
|
122261
122276
|
}
|
|
122262
122277
|
}
|
|
122263
122278
|
async cancel(sessionId) {
|
|
@@ -122428,7 +122443,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122428
122443
|
const BuiltinACPSetting = {
|
|
122429
122444
|
claude: {
|
|
122430
122445
|
packageName: "acp-extension-claude",
|
|
122431
|
-
version: "0.34.
|
|
122446
|
+
version: "0.34.3",
|
|
122432
122447
|
binName: "acp-extension-claude"
|
|
122433
122448
|
},
|
|
122434
122449
|
codex: {
|
|
@@ -122919,6 +122934,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122919
122934
|
args: args2,
|
|
122920
122935
|
spawnImpl: options.spawnImpl
|
|
122921
122936
|
});
|
|
122937
|
+
options.logger.debug(`[acp-startup] spawned ACP process (cliType=${options.cliType} agentType=${options.agentType} workdir=${options.workdir})`);
|
|
122922
122938
|
const stderrStream = agentProcess.stderr;
|
|
122923
122939
|
let stderrTail = "";
|
|
122924
122940
|
if (stderrStream) {
|
|
@@ -122980,6 +122996,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122980
122996
|
const input2 = createStdinWritableStream(agentProcess.stdin);
|
|
122981
122997
|
const stream2 = ndJsonStream(input2, output);
|
|
122982
122998
|
try {
|
|
122999
|
+
options.logger.debug("[acp-startup] creating ACP client");
|
|
122983
123000
|
const started = await createAcpClient({
|
|
122984
123001
|
stream: stream2,
|
|
122985
123002
|
workdir: options.workdir,
|
|
@@ -122994,6 +123011,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122994
123011
|
onRequestPermission: options.onRequestPermission,
|
|
122995
123012
|
startupAbort: startupMonitor.abortPromise
|
|
122996
123013
|
});
|
|
123014
|
+
options.logger.debug(`[acp-startup] ACP client ready (acpSessionId=${started.acpSessionId})`);
|
|
122997
123015
|
return {
|
|
122998
123016
|
agentProcess,
|
|
122999
123017
|
client: started.client,
|
|
@@ -123137,6 +123155,8 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123137
123155
|
const sleep2 = (ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2));
|
|
123138
123156
|
const tryGenerateWithArgs = async (extraArgs) => {
|
|
123139
123157
|
let collectedText = "";
|
|
123158
|
+
const startupStartedAt = Date.now();
|
|
123159
|
+
options.logger.debug(`[title-generator] Starting isolated title ACP agent (cliType=${options.cliType} agentType=${options.agentType})`);
|
|
123140
123160
|
const { agentProcess, client, acpSessionId } = await startLocalAcpAgent({
|
|
123141
123161
|
cliType: options.cliType,
|
|
123142
123162
|
agentType: options.agentType,
|
|
@@ -123162,6 +123182,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123162
123182
|
}),
|
|
123163
123183
|
extraArgs
|
|
123164
123184
|
});
|
|
123185
|
+
options.logger.debug(`[title-generator] Isolated title ACP agent ready (acpSessionId=${acpSessionId} startupDuration=${Date.now() - startupStartedAt}ms)`);
|
|
123165
123186
|
try {
|
|
123166
123187
|
const prompt2 = buildTitlePrompt(options.taskPrompt);
|
|
123167
123188
|
const configOptionValues = options.titleConfig?.configOptionValues;
|
|
@@ -123170,7 +123191,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123170
123191
|
await client?.setSessionConfigOption(acpSessionId, key2, value);
|
|
123171
123192
|
}
|
|
123172
123193
|
}
|
|
123194
|
+
options.logger.debug(`[title-generator] Sending title prompt (acpSessionId=${acpSessionId})`);
|
|
123173
123195
|
const response = await client?.prompt(acpSessionId, prompt2);
|
|
123196
|
+
options.logger.debug(`[title-generator] Title prompt returned (acpSessionId=${acpSessionId})`);
|
|
123174
123197
|
const deadline = Date.now() + 1e4;
|
|
123175
123198
|
while (Date.now() < deadline && collectedText.trim() === "") {
|
|
123176
123199
|
await sleep2(100);
|
|
@@ -127168,6 +127191,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
127168
127191
|
const resumeSource = requestedResumeSessionId ? "request" : storedResumeSessionId ? "meta" : "none";
|
|
127169
127192
|
self2.deps.logger.debug(`[${sessionId}] Session not found in memory; restoring (project=${project?.kind === "github" ? project.repoFullName : project?.kind === "local" ? `local:${project.localProjectId}` : "none"} resume=${resumeSessionId ? "yes" : "no"} resumeSource=${resumeSource} resumeSessionId=${resumeSessionId ?? "none"})`);
|
|
127170
127193
|
yield* self2.tryPromise(() => sessionDoc.setStatus(SessionStatusFactory.initializing("resuming")));
|
|
127194
|
+
self2.deps.logger.debug(`[${sessionId}] Resuming status published; preparing session restore (resumeSource=${resumeSource} resumeSessionId=${resumeSessionId ?? "none"})`);
|
|
127171
127195
|
const restoreConfig = {
|
|
127172
127196
|
sessionId,
|
|
127173
127197
|
workspaceId: message.workspaceId,
|
|
@@ -127191,11 +127215,13 @@ $mem | ConvertTo-Json -Compress
|
|
|
127191
127215
|
if (resumeSessionId) {
|
|
127192
127216
|
yield* acpReplaySuppression.acquire;
|
|
127193
127217
|
}
|
|
127218
|
+
self2.deps.logger.debug(`[${sessionId}] Session restore createSession started (resumeSessionId=${resumeSessionId ?? "none"})`);
|
|
127194
127219
|
const restoredSession = yield* ctx.trackPendingSession(() => self2.deps.sessionManager.createSession(restoreConfig, {
|
|
127195
127220
|
resumeSessionId
|
|
127196
127221
|
}), {
|
|
127197
127222
|
terminateOnCancel: true
|
|
127198
127223
|
});
|
|
127224
|
+
self2.deps.logger.debug(`[${sessionId}] Session restore createSession returned (acpSessionId=${restoredSession.acpSessionId ?? "null"})`);
|
|
127199
127225
|
ctx.bindSession(restoredSession);
|
|
127200
127226
|
yield* ctx.abortIfCancelled({
|
|
127201
127227
|
terminateSession: true
|
|
@@ -127221,9 +127247,11 @@ $mem | ConvertTo-Json -Compress
|
|
|
127221
127247
|
...restoreConfig
|
|
127222
127248
|
};
|
|
127223
127249
|
const fallbackAttempt = gen(function* () {
|
|
127250
|
+
self2.deps.logger.debug(`[${sessionId}] Fallback restore createSession started`);
|
|
127224
127251
|
const fallbackSession = yield* ctx.trackPendingSession(() => self2.deps.sessionManager.createSession(fallbackConfig), {
|
|
127225
127252
|
terminateOnCancel: true
|
|
127226
127253
|
});
|
|
127254
|
+
self2.deps.logger.debug(`[${sessionId}] Fallback restore createSession returned (acpSessionId=${fallbackSession.acpSessionId ?? "null"})`);
|
|
127227
127255
|
ctx.bindSession(fallbackSession);
|
|
127228
127256
|
yield* ctx.abortIfCancelled({
|
|
127229
127257
|
terminateSession: true
|
|
@@ -128343,6 +128371,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
128343
128371
|
static REALTIME_WAIT_TIMEOUT_MS = 3e4;
|
|
128344
128372
|
static REMOTE_SYNC_TIMEOUT_MS = 15e3;
|
|
128345
128373
|
static HISTORY_SYNC_WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
128374
|
+
static HISTORY_SYNC_PROGRESS_LOG_MS = 3e4;
|
|
128346
128375
|
static HISTORY_RECONNECT_JITTER_MIN_MS = 500;
|
|
128347
128376
|
static HISTORY_RECONNECT_JITTER_MAX_MS = 1500;
|
|
128348
128377
|
static setUnrefTimeout(callback, delayMs) {
|
|
@@ -128415,12 +128444,17 @@ $mem | ConvertTo-Json -Compress
|
|
|
128415
128444
|
let reconnectAttempted = false;
|
|
128416
128445
|
let unsubscribeMirror;
|
|
128417
128446
|
let unsubscribeStatus;
|
|
128447
|
+
let progressTimer = null;
|
|
128448
|
+
const waitStartedAt = Date.now();
|
|
128418
128449
|
const cleanup = () => {
|
|
128419
128450
|
if (settled) {
|
|
128420
128451
|
return;
|
|
128421
128452
|
}
|
|
128422
128453
|
settled = true;
|
|
128423
128454
|
clearTimeout(timer2);
|
|
128455
|
+
if (progressTimer) {
|
|
128456
|
+
clearInterval(progressTimer);
|
|
128457
|
+
}
|
|
128424
128458
|
unsubscribeMirror?.();
|
|
128425
128459
|
unsubscribeStatus?.();
|
|
128426
128460
|
};
|
|
@@ -128475,6 +128509,13 @@ $mem | ConvertTo-Json -Compress
|
|
|
128475
128509
|
this.deps.logger.warn(`[${sessionId}] User turn did not arrive in history after ${SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS / 1e3}s; entering dispatch recovery`);
|
|
128476
128510
|
resolve2(null);
|
|
128477
128511
|
}, SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS);
|
|
128512
|
+
progressTimer = setInterval(() => {
|
|
128513
|
+
if (settled) {
|
|
128514
|
+
return;
|
|
128515
|
+
}
|
|
128516
|
+
this.deps.logger.warn(`[${sessionId}] Still waiting for pending user turn history sync (elapsed=${Date.now() - waitStartedAt}ms docRoom=${sessionDoc.getDocRoomStatus() ?? "unknown"})`);
|
|
128517
|
+
}, SessionDispatchWatcher.HISTORY_SYNC_PROGRESS_LOG_MS);
|
|
128518
|
+
progressTimer.unref?.();
|
|
128478
128519
|
unsubscribeMirror = sessionDoc.mirror?.subscribe(checkForTurn);
|
|
128479
128520
|
if (!unsubscribeMirror) {
|
|
128480
128521
|
this.deps.logger.debug(`[${sessionId}] Session mirror is unavailable during history sync wait`);
|
|
@@ -132059,6 +132100,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132059
132100
|
static CONTEXT_WINDOW_USAGE_THROTTLE_MS = 400;
|
|
132060
132101
|
permissionRequestStartTimes = /* @__PURE__ */ new Map();
|
|
132061
132102
|
machineHeartbeatTimer = null;
|
|
132103
|
+
machineHeartbeatInFlightStartedAt = null;
|
|
132104
|
+
machineHeartbeatSeq = 0;
|
|
132062
132105
|
static MACHINE_HEARTBEAT_INTERVAL_MS = 2e4;
|
|
132063
132106
|
evictForMemoryPressureFn = async () => ({
|
|
132064
132107
|
availableMemoryBytes: 0,
|
|
@@ -132089,24 +132132,35 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132089
132132
|
startMachineHeartbeat() {
|
|
132090
132133
|
this.stopMachineHeartbeat();
|
|
132091
132134
|
this.logger.debug(`Machine heartbeat started (${Math.round(MessageHandler.MACHINE_HEARTBEAT_INTERVAL_MS / 1e3)}s interval)`);
|
|
132092
|
-
|
|
132093
|
-
this.logger.debug("Machine heartbeat sent (initial)");
|
|
132094
|
-
}).catch((error2) => {
|
|
132095
|
-
this.logger.debug(`Initial machine heartbeat failed: ${formatErrorMessage(error2)}`);
|
|
132096
|
-
});
|
|
132135
|
+
this.sendMachineHeartbeatWithDiagnostics("initial");
|
|
132097
132136
|
this.machineHeartbeatTimer = setInterval(() => {
|
|
132098
|
-
|
|
132099
|
-
this.logger.debug("Machine heartbeat sent");
|
|
132100
|
-
}).catch((error2) => {
|
|
132101
|
-
this.logger.debug(`Machine heartbeat failed: ${formatErrorMessage(error2)}`);
|
|
132102
|
-
});
|
|
132137
|
+
this.sendMachineHeartbeatWithDiagnostics("periodic");
|
|
132103
132138
|
}, MessageHandler.MACHINE_HEARTBEAT_INTERVAL_MS);
|
|
132139
|
+
this.machineHeartbeatTimer.unref?.();
|
|
132140
|
+
}
|
|
132141
|
+
sendMachineHeartbeatWithDiagnostics(reason) {
|
|
132142
|
+
const now2 = Date.now();
|
|
132143
|
+
if (this.machineHeartbeatInFlightStartedAt !== null) {
|
|
132144
|
+
this.logger.warn(`Machine heartbeat skipped (reason=${reason}); previous heartbeat still pending for ${now2 - this.machineHeartbeatInFlightStartedAt}ms`);
|
|
132145
|
+
return;
|
|
132146
|
+
}
|
|
132147
|
+
const seq2 = ++this.machineHeartbeatSeq;
|
|
132148
|
+
this.machineHeartbeatInFlightStartedAt = now2;
|
|
132149
|
+
this.logger.debug(`Machine heartbeat tick started (reason=${reason} seq=${seq2})`);
|
|
132150
|
+
void withSlowOperationWarning(this.workspaceDocument.sendMachineHeartbeat(), this.logger, `workspaceDocument.sendMachineHeartbeat(reason=${reason}, seq=${seq2})`, this.machineId).then(() => {
|
|
132151
|
+
this.logger.debug(`Machine heartbeat sent (reason=${reason} seq=${seq2} duration=${Date.now() - now2}ms)`);
|
|
132152
|
+
}).catch((error2) => {
|
|
132153
|
+
this.logger.debug(`Machine heartbeat failed (reason=${reason} seq=${seq2} duration=${Date.now() - now2}ms): ${formatErrorMessage(error2)}`);
|
|
132154
|
+
}).finally(() => {
|
|
132155
|
+
this.machineHeartbeatInFlightStartedAt = null;
|
|
132156
|
+
});
|
|
132104
132157
|
}
|
|
132105
132158
|
stopMachineHeartbeat() {
|
|
132106
132159
|
if (this.machineHeartbeatTimer) {
|
|
132107
132160
|
clearInterval(this.machineHeartbeatTimer);
|
|
132108
132161
|
this.machineHeartbeatTimer = null;
|
|
132109
132162
|
}
|
|
132163
|
+
this.machineHeartbeatInFlightStartedAt = null;
|
|
132110
132164
|
}
|
|
132111
132165
|
preferredBaseBranch = (process.env.LODY_BASE_BRANCH || "main").trim() || "main";
|
|
132112
132166
|
resolveGitHubProjectBranch(meta, preferredBranch) {
|
|
@@ -134850,6 +134904,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134850
134904
|
}
|
|
134851
134905
|
queue;
|
|
134852
134906
|
isStopped = false;
|
|
134907
|
+
static QUEUE_WAIT_WARNING_MS = 1e4;
|
|
134908
|
+
static PROCESSING_WARNING_MS = 3e4;
|
|
134853
134909
|
enqueue(message, handler) {
|
|
134854
134910
|
if (this.isStopped) {
|
|
134855
134911
|
this.logger.debug("MessageProcessor is stopped, ignoring new message");
|
|
@@ -134857,11 +134913,30 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134857
134913
|
}
|
|
134858
134914
|
const sessionId = this.extractSessionId(message);
|
|
134859
134915
|
const queueKey = this.extractQueueKey(message);
|
|
134860
|
-
|
|
134916
|
+
const queuedAt = Date.now();
|
|
134917
|
+
let started = false;
|
|
134918
|
+
const waitWarning = setInterval(() => {
|
|
134919
|
+
if (started) {
|
|
134920
|
+
return;
|
|
134921
|
+
}
|
|
134922
|
+
this.logger.warn(`Message still waiting in queue type=${message.type} sessionId=${sessionId || "N/A"} queuedFor=${Date.now() - queuedAt}ms active=${this.queue.active} waiting=${this.queue.waiting}`);
|
|
134923
|
+
}, MessageProcessor.QUEUE_WAIT_WARNING_MS);
|
|
134924
|
+
waitWarning.unref?.();
|
|
134925
|
+
this.logger.debug(`Enqueued message type=${message.type} sessionId=${sessionId || "N/A"} active=${this.queue.active} waiting=${this.queue.waiting}`);
|
|
134861
134926
|
void this.queue.enqueue(queueKey, async () => {
|
|
134862
134927
|
const startTime = Date.now();
|
|
134928
|
+
started = true;
|
|
134929
|
+
clearInterval(waitWarning);
|
|
134930
|
+
let processingCompleted = false;
|
|
134931
|
+
const processingWarning = setInterval(() => {
|
|
134932
|
+
if (processingCompleted) {
|
|
134933
|
+
return;
|
|
134934
|
+
}
|
|
134935
|
+
this.logger.warn(`Message still processing type=${message.type} sessionId=${sessionId || "N/A"} runningFor=${Date.now() - startTime}ms active=${this.queue.active} waiting=${this.queue.waiting}`);
|
|
134936
|
+
}, MessageProcessor.PROCESSING_WARNING_MS);
|
|
134937
|
+
processingWarning.unref?.();
|
|
134863
134938
|
try {
|
|
134864
|
-
this.logger.debug(`Processing message type=${message.type} sessionId=${sessionId || "N/A"}`);
|
|
134939
|
+
this.logger.debug(`Processing message type=${message.type} sessionId=${sessionId || "N/A"} queueWait=${startTime - queuedAt}ms active=${this.queue.active} waiting=${this.queue.waiting}`);
|
|
134865
134940
|
await handler(message);
|
|
134866
134941
|
const duration2 = Date.now() - startTime;
|
|
134867
134942
|
this.logger.debug(`Processed message type=${message.type} sessionId=${sessionId || "N/A"} duration=${duration2}ms`);
|
|
@@ -134871,6 +134946,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134871
134946
|
const err2 = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
134872
134947
|
this.logger.error(`Failed to process message type=${message.type} sessionId=${sessionId || "N/A"} duration=${duration2}ms: ${err2.message}`);
|
|
134873
134948
|
this.emit("message:error", err2, message);
|
|
134949
|
+
} finally {
|
|
134950
|
+
processingCompleted = true;
|
|
134951
|
+
clearInterval(processingWarning);
|
|
134874
134952
|
}
|
|
134875
134953
|
});
|
|
134876
134954
|
}
|
|
@@ -135235,6 +135313,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135235
135313
|
}
|
|
135236
135314
|
async dispatchLocalMessageForResponse(message) {
|
|
135237
135315
|
const handler = this.requireHandler();
|
|
135316
|
+
const dispatchStartedAt = Date.now();
|
|
135317
|
+
this.options.logger.debug(`Local control dispatch started type=${message.type} sessionId=${"sessionId" in message ? message.sessionId : "N/A"} active=${this.messageProcessor.getActiveSessions()} waiting=${this.messageProcessor.getQueueSize()}`);
|
|
135238
135318
|
return await new Promise((resolve2, reject) => {
|
|
135239
135319
|
this.messageProcessor.enqueue(message, async (nextMessage) => {
|
|
135240
135320
|
const responses = [];
|
|
@@ -135244,6 +135324,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135244
135324
|
return;
|
|
135245
135325
|
}
|
|
135246
135326
|
settled = true;
|
|
135327
|
+
this.options.logger.debug(`Local control dispatch resolved type=${message.type} sessionId=${"sessionId" in message ? message.sessionId : "N/A"} duration=${Date.now() - dispatchStartedAt}ms responses=${responses.length} active=${this.messageProcessor.getActiveSessions()} waiting=${this.messageProcessor.getQueueSize()}`);
|
|
135247
135328
|
resolve2([
|
|
135248
135329
|
...responses
|
|
135249
135330
|
]);
|
|
@@ -135253,6 +135334,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135253
135334
|
return;
|
|
135254
135335
|
}
|
|
135255
135336
|
settled = true;
|
|
135337
|
+
this.options.logger.debug(`Local control dispatch rejected type=${message.type} sessionId=${"sessionId" in message ? message.sessionId : "N/A"} duration=${Date.now() - dispatchStartedAt}ms: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
135256
135338
|
reject(error2);
|
|
135257
135339
|
};
|
|
135258
135340
|
const context2 = {
|
|
@@ -142420,7 +142502,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142420
142502
|
await this.workspaceDocument.getOrCreateSessionDoc(config2.sessionId);
|
|
142421
142503
|
this.logger.debug(`[${config2.sessionId}] Session will enter create inner`);
|
|
142422
142504
|
const createInnerStart = performance$1.now();
|
|
142423
|
-
const session = await this.createSessionInner(config2);
|
|
142505
|
+
const session = await withSlowOperationWarning(this.createSessionInner(config2), this.logger, "session.createSessionInner", config2.sessionId);
|
|
142424
142506
|
session.ghTokenInjected = ghTokenInjected;
|
|
142425
142507
|
const createInnerDurationMs = performance$1.now() - createInnerStart;
|
|
142426
142508
|
this.logger.debug(`[${config2.sessionId}] Session create inner finished in ${Math.round(createInnerDurationMs)}ms`);
|
|
@@ -142441,7 +142523,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142441
142523
|
this.logger.debug(`[${sessionId}] About to call session.createAgent`);
|
|
142442
142524
|
try {
|
|
142443
142525
|
const createAgentStart = performance$1.now();
|
|
142444
|
-
acpSessionId = await session.createAgent({
|
|
142526
|
+
acpSessionId = await withSlowOperationWarning(session.createAgent({
|
|
142445
142527
|
cliType: config2.agentCliType,
|
|
142446
142528
|
agentType: config2.agentType,
|
|
142447
142529
|
command: setting.exec.command,
|
|
@@ -142501,7 +142583,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142501
142583
|
onCodexImageGenerationEnd: (event) => {
|
|
142502
142584
|
this.emit("onCodexImageGenerationEnd", sessionId, event);
|
|
142503
142585
|
}
|
|
142504
|
-
});
|
|
142586
|
+
}), this.logger, `session.createAgent(resume=${requestedResumeSessionId ? "yes" : "no"})`, sessionId);
|
|
142505
142587
|
const createAgentDurationMs = performance$1.now() - createAgentStart;
|
|
142506
142588
|
this.logger.debug(`[${sessionId}] Session createAgent finished in ${Math.round(createAgentDurationMs)}ms (acpSessionId=${acpSessionId})`);
|
|
142507
142589
|
this.logger.debug(`[${sessionId}] createAgent returned successfully`);
|
|
@@ -145144,6 +145226,23 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145144
145226
|
shutdown: shutdown2
|
|
145145
145227
|
};
|
|
145146
145228
|
}
|
|
145229
|
+
function startEventLoopLagMonitor(logger2, options) {
|
|
145230
|
+
const intervalMs = Math.max(100, options.intervalMs ?? 1e3);
|
|
145231
|
+
const warnThresholdMs = Math.max(intervalMs, options.warnThresholdMs ?? 5e3);
|
|
145232
|
+
let expectedAt = Date.now() + intervalMs;
|
|
145233
|
+
const timer2 = setInterval(() => {
|
|
145234
|
+
const now2 = Date.now();
|
|
145235
|
+
const lagMs = now2 - expectedAt;
|
|
145236
|
+
expectedAt = now2 + intervalMs;
|
|
145237
|
+
if (lagMs >= warnThresholdMs) {
|
|
145238
|
+
logger2.warn(`[event-loop] ${options.label} timer lag detected: fired ${Math.round(lagMs)}ms late (threshold=${warnThresholdMs}ms interval=${intervalMs}ms)`);
|
|
145239
|
+
}
|
|
145240
|
+
}, intervalMs);
|
|
145241
|
+
timer2.unref?.();
|
|
145242
|
+
return {
|
|
145243
|
+
stop: () => clearInterval(timer2)
|
|
145244
|
+
};
|
|
145245
|
+
}
|
|
145147
145246
|
const ELECTRON_BOOTSTRAP_ENV = "LODY_ELECTRON_BOOTSTRAP";
|
|
145148
145247
|
const ELECTRON_SESSION_TOKEN_ENV = "LODY_ELECTRON_SESSION_TOKEN";
|
|
145149
145248
|
const ELECTRON_SESSION_USER_ID_ENV = "LODY_ELECTRON_SESSION_USER_ID";
|
|
@@ -145371,7 +145470,13 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145371
145470
|
runtimeStateReporter,
|
|
145372
145471
|
onFatalAuthFailure: (error2) => triggerFatalAuthShutdown?.(error2)
|
|
145373
145472
|
});
|
|
145374
|
-
|
|
145473
|
+
const eventLoopLagMonitor = startEventLoopLagMonitor(logger2, {
|
|
145474
|
+
label: "lody start"
|
|
145475
|
+
});
|
|
145476
|
+
registerProcessCleanup(async () => {
|
|
145477
|
+
eventLoopLagMonitor.stop();
|
|
145478
|
+
await fleet.shutdown();
|
|
145479
|
+
});
|
|
145375
145480
|
const shutdownSignals = process.platform === "win32" ? [
|
|
145376
145481
|
"SIGINT",
|
|
145377
145482
|
"SIGTERM",
|
|
@@ -145387,6 +145492,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145387
145492
|
logger: logger2,
|
|
145388
145493
|
shutdown: async () => {
|
|
145389
145494
|
unregisterProcessCleanup();
|
|
145495
|
+
eventLoopLagMonitor.stop();
|
|
145390
145496
|
await fleet.shutdown();
|
|
145391
145497
|
},
|
|
145392
145498
|
flushTelemetry: () => shutdownPostHog(),
|
|
@@ -145425,6 +145531,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145425
145531
|
});
|
|
145426
145532
|
shutdownController.unregister();
|
|
145427
145533
|
unregisterProcessCleanup();
|
|
145534
|
+
eventLoopLagMonitor.stop();
|
|
145428
145535
|
await fleet.shutdown().catch((err2) => {
|
|
145429
145536
|
logger2.error(`Cleanup failed: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
145430
145537
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lody",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.2",
|
|
4
4
|
"description": "Lody Agent CLI tool for managing remote command execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"node": ">=18.0.0"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"acp-extension-claude": "0.34.
|
|
23
|
+
"acp-extension-claude": "0.34.3",
|
|
24
24
|
"acp-extension-codex": "0.14.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
@@ -74,10 +74,10 @@
|
|
|
74
74
|
"ws": "^8.18.3",
|
|
75
75
|
"zod": "^4.1.5",
|
|
76
76
|
"@lody/cli-supervisor": "0.0.1",
|
|
77
|
-
"@lody/convex": "0.0.1",
|
|
78
77
|
"@lody/loro-streams-rpc": "0.0.1",
|
|
78
|
+
"@lody/shared": "0.0.1",
|
|
79
79
|
"loro-code": "0.0.1",
|
|
80
|
-
"@lody/
|
|
80
|
+
"@lody/convex": "0.0.1"
|
|
81
81
|
},
|
|
82
82
|
"files": [
|
|
83
83
|
"dist",
|