lody 0.52.0 → 0.52.1
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 +142 -27
- package/package.json +3 -3
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.1";
|
|
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";
|
|
@@ -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);
|
|
@@ -122106,7 +122127,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122106
122127
|
this.supportsClose = !!closeCapability;
|
|
122107
122128
|
const hasCloseMethod = typeof connection.closeSession === "function";
|
|
122108
122129
|
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
|
|
122130
|
+
this.logger.debug(`[${this.options.sessionId}] About to establish ACP session`);
|
|
122110
122131
|
const newSessionStart = performance$1.now();
|
|
122111
122132
|
this.options.onStartupStage?.({
|
|
122112
122133
|
type: "new_session_start"
|
|
@@ -122133,11 +122154,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122133
122154
|
const loadStart = performance$1.now();
|
|
122134
122155
|
try {
|
|
122135
122156
|
this.logger.debug(`[${this.options.sessionId}] Attempting ACP loadSession (acpSessionId=${resumeSessionId})`);
|
|
122136
|
-
const
|
|
122157
|
+
const ACP_LOAD_SESSION_TIMEOUT_MS = Math.max(0, timeoutOptions.loadSessionTimeoutMs ?? timeoutOptions.newSessionTimeoutMs ?? 12e4);
|
|
122158
|
+
const loadResponse = await withTimeout$2(withAbort(connection.loadSession({
|
|
122137
122159
|
sessionId: resumeSessionId,
|
|
122138
122160
|
cwd: workdir,
|
|
122139
122161
|
mcpServers
|
|
122140
|
-
}), startupAbort);
|
|
122162
|
+
}), startupAbort), this.logger, "connection.loadSession", this.options.sessionId, ACP_LOAD_SESSION_TIMEOUT_MS);
|
|
122141
122163
|
const loadDurationMs = performance$1.now() - loadStart;
|
|
122142
122164
|
sessionResponse = {
|
|
122143
122165
|
sessionId: resumeSessionId,
|
|
@@ -122158,11 +122180,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122158
122180
|
const resumeStart = performance$1.now();
|
|
122159
122181
|
try {
|
|
122160
122182
|
this.logger.debug(`[${this.options.sessionId}] Attempting ACP resume (acpSessionId=${resumeSessionId})`);
|
|
122161
|
-
const
|
|
122183
|
+
const ACP_RESUME_SESSION_TIMEOUT_MS = Math.max(0, timeoutOptions.resumeSessionTimeoutMs ?? timeoutOptions.newSessionTimeoutMs ?? 12e4);
|
|
122184
|
+
const resumeResponse = await withTimeout$2(withAbort(connection.resumeSession({
|
|
122162
122185
|
sessionId: resumeSessionId,
|
|
122163
122186
|
cwd: workdir,
|
|
122164
122187
|
mcpServers
|
|
122165
|
-
}), startupAbort);
|
|
122188
|
+
}), startupAbort), this.logger, "connection.resumeSession", this.options.sessionId, ACP_RESUME_SESSION_TIMEOUT_MS);
|
|
122166
122189
|
const resumeDurationMs = performance$1.now() - resumeStart;
|
|
122167
122190
|
sessionResponse = {
|
|
122168
122191
|
sessionId: resumeSessionId,
|
|
@@ -122919,6 +122942,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122919
122942
|
args: args2,
|
|
122920
122943
|
spawnImpl: options.spawnImpl
|
|
122921
122944
|
});
|
|
122945
|
+
options.logger.debug(`[acp-startup] spawned ACP process (cliType=${options.cliType} agentType=${options.agentType} workdir=${options.workdir})`);
|
|
122922
122946
|
const stderrStream = agentProcess.stderr;
|
|
122923
122947
|
let stderrTail = "";
|
|
122924
122948
|
if (stderrStream) {
|
|
@@ -122980,6 +123004,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122980
123004
|
const input2 = createStdinWritableStream(agentProcess.stdin);
|
|
122981
123005
|
const stream2 = ndJsonStream(input2, output);
|
|
122982
123006
|
try {
|
|
123007
|
+
options.logger.debug("[acp-startup] creating ACP client");
|
|
122983
123008
|
const started = await createAcpClient({
|
|
122984
123009
|
stream: stream2,
|
|
122985
123010
|
workdir: options.workdir,
|
|
@@ -122994,6 +123019,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122994
123019
|
onRequestPermission: options.onRequestPermission,
|
|
122995
123020
|
startupAbort: startupMonitor.abortPromise
|
|
122996
123021
|
});
|
|
123022
|
+
options.logger.debug(`[acp-startup] ACP client ready (acpSessionId=${started.acpSessionId})`);
|
|
122997
123023
|
return {
|
|
122998
123024
|
agentProcess,
|
|
122999
123025
|
client: started.client,
|
|
@@ -123137,6 +123163,8 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123137
123163
|
const sleep2 = (ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2));
|
|
123138
123164
|
const tryGenerateWithArgs = async (extraArgs) => {
|
|
123139
123165
|
let collectedText = "";
|
|
123166
|
+
const startupStartedAt = Date.now();
|
|
123167
|
+
options.logger.debug(`[title-generator] Starting isolated title ACP agent (cliType=${options.cliType} agentType=${options.agentType})`);
|
|
123140
123168
|
const { agentProcess, client, acpSessionId } = await startLocalAcpAgent({
|
|
123141
123169
|
cliType: options.cliType,
|
|
123142
123170
|
agentType: options.agentType,
|
|
@@ -123162,6 +123190,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123162
123190
|
}),
|
|
123163
123191
|
extraArgs
|
|
123164
123192
|
});
|
|
123193
|
+
options.logger.debug(`[title-generator] Isolated title ACP agent ready (acpSessionId=${acpSessionId} startupDuration=${Date.now() - startupStartedAt}ms)`);
|
|
123165
123194
|
try {
|
|
123166
123195
|
const prompt2 = buildTitlePrompt(options.taskPrompt);
|
|
123167
123196
|
const configOptionValues = options.titleConfig?.configOptionValues;
|
|
@@ -123170,7 +123199,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123170
123199
|
await client?.setSessionConfigOption(acpSessionId, key2, value);
|
|
123171
123200
|
}
|
|
123172
123201
|
}
|
|
123202
|
+
options.logger.debug(`[title-generator] Sending title prompt (acpSessionId=${acpSessionId})`);
|
|
123173
123203
|
const response = await client?.prompt(acpSessionId, prompt2);
|
|
123204
|
+
options.logger.debug(`[title-generator] Title prompt returned (acpSessionId=${acpSessionId})`);
|
|
123174
123205
|
const deadline = Date.now() + 1e4;
|
|
123175
123206
|
while (Date.now() < deadline && collectedText.trim() === "") {
|
|
123176
123207
|
await sleep2(100);
|
|
@@ -127168,6 +127199,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
127168
127199
|
const resumeSource = requestedResumeSessionId ? "request" : storedResumeSessionId ? "meta" : "none";
|
|
127169
127200
|
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
127201
|
yield* self2.tryPromise(() => sessionDoc.setStatus(SessionStatusFactory.initializing("resuming")));
|
|
127202
|
+
self2.deps.logger.debug(`[${sessionId}] Resuming status published; preparing session restore (resumeSource=${resumeSource} resumeSessionId=${resumeSessionId ?? "none"})`);
|
|
127171
127203
|
const restoreConfig = {
|
|
127172
127204
|
sessionId,
|
|
127173
127205
|
workspaceId: message.workspaceId,
|
|
@@ -127191,11 +127223,13 @@ $mem | ConvertTo-Json -Compress
|
|
|
127191
127223
|
if (resumeSessionId) {
|
|
127192
127224
|
yield* acpReplaySuppression.acquire;
|
|
127193
127225
|
}
|
|
127226
|
+
self2.deps.logger.debug(`[${sessionId}] Session restore createSession started (resumeSessionId=${resumeSessionId ?? "none"})`);
|
|
127194
127227
|
const restoredSession = yield* ctx.trackPendingSession(() => self2.deps.sessionManager.createSession(restoreConfig, {
|
|
127195
127228
|
resumeSessionId
|
|
127196
127229
|
}), {
|
|
127197
127230
|
terminateOnCancel: true
|
|
127198
127231
|
});
|
|
127232
|
+
self2.deps.logger.debug(`[${sessionId}] Session restore createSession returned (acpSessionId=${restoredSession.acpSessionId ?? "null"})`);
|
|
127199
127233
|
ctx.bindSession(restoredSession);
|
|
127200
127234
|
yield* ctx.abortIfCancelled({
|
|
127201
127235
|
terminateSession: true
|
|
@@ -127221,9 +127255,11 @@ $mem | ConvertTo-Json -Compress
|
|
|
127221
127255
|
...restoreConfig
|
|
127222
127256
|
};
|
|
127223
127257
|
const fallbackAttempt = gen(function* () {
|
|
127258
|
+
self2.deps.logger.debug(`[${sessionId}] Fallback restore createSession started`);
|
|
127224
127259
|
const fallbackSession = yield* ctx.trackPendingSession(() => self2.deps.sessionManager.createSession(fallbackConfig), {
|
|
127225
127260
|
terminateOnCancel: true
|
|
127226
127261
|
});
|
|
127262
|
+
self2.deps.logger.debug(`[${sessionId}] Fallback restore createSession returned (acpSessionId=${fallbackSession.acpSessionId ?? "null"})`);
|
|
127227
127263
|
ctx.bindSession(fallbackSession);
|
|
127228
127264
|
yield* ctx.abortIfCancelled({
|
|
127229
127265
|
terminateSession: true
|
|
@@ -128343,6 +128379,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
128343
128379
|
static REALTIME_WAIT_TIMEOUT_MS = 3e4;
|
|
128344
128380
|
static REMOTE_SYNC_TIMEOUT_MS = 15e3;
|
|
128345
128381
|
static HISTORY_SYNC_WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
128382
|
+
static HISTORY_SYNC_PROGRESS_LOG_MS = 3e4;
|
|
128346
128383
|
static HISTORY_RECONNECT_JITTER_MIN_MS = 500;
|
|
128347
128384
|
static HISTORY_RECONNECT_JITTER_MAX_MS = 1500;
|
|
128348
128385
|
static setUnrefTimeout(callback, delayMs) {
|
|
@@ -128415,12 +128452,17 @@ $mem | ConvertTo-Json -Compress
|
|
|
128415
128452
|
let reconnectAttempted = false;
|
|
128416
128453
|
let unsubscribeMirror;
|
|
128417
128454
|
let unsubscribeStatus;
|
|
128455
|
+
let progressTimer = null;
|
|
128456
|
+
const waitStartedAt = Date.now();
|
|
128418
128457
|
const cleanup = () => {
|
|
128419
128458
|
if (settled) {
|
|
128420
128459
|
return;
|
|
128421
128460
|
}
|
|
128422
128461
|
settled = true;
|
|
128423
128462
|
clearTimeout(timer2);
|
|
128463
|
+
if (progressTimer) {
|
|
128464
|
+
clearInterval(progressTimer);
|
|
128465
|
+
}
|
|
128424
128466
|
unsubscribeMirror?.();
|
|
128425
128467
|
unsubscribeStatus?.();
|
|
128426
128468
|
};
|
|
@@ -128475,6 +128517,13 @@ $mem | ConvertTo-Json -Compress
|
|
|
128475
128517
|
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
128518
|
resolve2(null);
|
|
128477
128519
|
}, SessionDispatchWatcher.HISTORY_SYNC_WAIT_TIMEOUT_MS);
|
|
128520
|
+
progressTimer = setInterval(() => {
|
|
128521
|
+
if (settled) {
|
|
128522
|
+
return;
|
|
128523
|
+
}
|
|
128524
|
+
this.deps.logger.warn(`[${sessionId}] Still waiting for pending user turn history sync (elapsed=${Date.now() - waitStartedAt}ms docRoom=${sessionDoc.getDocRoomStatus() ?? "unknown"})`);
|
|
128525
|
+
}, SessionDispatchWatcher.HISTORY_SYNC_PROGRESS_LOG_MS);
|
|
128526
|
+
progressTimer.unref?.();
|
|
128478
128527
|
unsubscribeMirror = sessionDoc.mirror?.subscribe(checkForTurn);
|
|
128479
128528
|
if (!unsubscribeMirror) {
|
|
128480
128529
|
this.deps.logger.debug(`[${sessionId}] Session mirror is unavailable during history sync wait`);
|
|
@@ -132059,6 +132108,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132059
132108
|
static CONTEXT_WINDOW_USAGE_THROTTLE_MS = 400;
|
|
132060
132109
|
permissionRequestStartTimes = /* @__PURE__ */ new Map();
|
|
132061
132110
|
machineHeartbeatTimer = null;
|
|
132111
|
+
machineHeartbeatInFlightStartedAt = null;
|
|
132112
|
+
machineHeartbeatSeq = 0;
|
|
132062
132113
|
static MACHINE_HEARTBEAT_INTERVAL_MS = 2e4;
|
|
132063
132114
|
evictForMemoryPressureFn = async () => ({
|
|
132064
132115
|
availableMemoryBytes: 0,
|
|
@@ -132089,24 +132140,35 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132089
132140
|
startMachineHeartbeat() {
|
|
132090
132141
|
this.stopMachineHeartbeat();
|
|
132091
132142
|
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
|
-
});
|
|
132143
|
+
this.sendMachineHeartbeatWithDiagnostics("initial");
|
|
132097
132144
|
this.machineHeartbeatTimer = setInterval(() => {
|
|
132098
|
-
|
|
132099
|
-
this.logger.debug("Machine heartbeat sent");
|
|
132100
|
-
}).catch((error2) => {
|
|
132101
|
-
this.logger.debug(`Machine heartbeat failed: ${formatErrorMessage(error2)}`);
|
|
132102
|
-
});
|
|
132145
|
+
this.sendMachineHeartbeatWithDiagnostics("periodic");
|
|
132103
132146
|
}, MessageHandler.MACHINE_HEARTBEAT_INTERVAL_MS);
|
|
132147
|
+
this.machineHeartbeatTimer.unref?.();
|
|
132148
|
+
}
|
|
132149
|
+
sendMachineHeartbeatWithDiagnostics(reason) {
|
|
132150
|
+
const now2 = Date.now();
|
|
132151
|
+
if (this.machineHeartbeatInFlightStartedAt !== null) {
|
|
132152
|
+
this.logger.warn(`Machine heartbeat skipped (reason=${reason}); previous heartbeat still pending for ${now2 - this.machineHeartbeatInFlightStartedAt}ms`);
|
|
132153
|
+
return;
|
|
132154
|
+
}
|
|
132155
|
+
const seq2 = ++this.machineHeartbeatSeq;
|
|
132156
|
+
this.machineHeartbeatInFlightStartedAt = now2;
|
|
132157
|
+
this.logger.debug(`Machine heartbeat tick started (reason=${reason} seq=${seq2})`);
|
|
132158
|
+
void withSlowOperationWarning(this.workspaceDocument.sendMachineHeartbeat(), this.logger, `workspaceDocument.sendMachineHeartbeat(reason=${reason}, seq=${seq2})`, this.machineId).then(() => {
|
|
132159
|
+
this.logger.debug(`Machine heartbeat sent (reason=${reason} seq=${seq2} duration=${Date.now() - now2}ms)`);
|
|
132160
|
+
}).catch((error2) => {
|
|
132161
|
+
this.logger.debug(`Machine heartbeat failed (reason=${reason} seq=${seq2} duration=${Date.now() - now2}ms): ${formatErrorMessage(error2)}`);
|
|
132162
|
+
}).finally(() => {
|
|
132163
|
+
this.machineHeartbeatInFlightStartedAt = null;
|
|
132164
|
+
});
|
|
132104
132165
|
}
|
|
132105
132166
|
stopMachineHeartbeat() {
|
|
132106
132167
|
if (this.machineHeartbeatTimer) {
|
|
132107
132168
|
clearInterval(this.machineHeartbeatTimer);
|
|
132108
132169
|
this.machineHeartbeatTimer = null;
|
|
132109
132170
|
}
|
|
132171
|
+
this.machineHeartbeatInFlightStartedAt = null;
|
|
132110
132172
|
}
|
|
132111
132173
|
preferredBaseBranch = (process.env.LODY_BASE_BRANCH || "main").trim() || "main";
|
|
132112
132174
|
resolveGitHubProjectBranch(meta, preferredBranch) {
|
|
@@ -134850,6 +134912,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134850
134912
|
}
|
|
134851
134913
|
queue;
|
|
134852
134914
|
isStopped = false;
|
|
134915
|
+
static QUEUE_WAIT_WARNING_MS = 1e4;
|
|
134916
|
+
static PROCESSING_WARNING_MS = 3e4;
|
|
134853
134917
|
enqueue(message, handler) {
|
|
134854
134918
|
if (this.isStopped) {
|
|
134855
134919
|
this.logger.debug("MessageProcessor is stopped, ignoring new message");
|
|
@@ -134857,11 +134921,30 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134857
134921
|
}
|
|
134858
134922
|
const sessionId = this.extractSessionId(message);
|
|
134859
134923
|
const queueKey = this.extractQueueKey(message);
|
|
134860
|
-
|
|
134924
|
+
const queuedAt = Date.now();
|
|
134925
|
+
let started = false;
|
|
134926
|
+
const waitWarning = setInterval(() => {
|
|
134927
|
+
if (started) {
|
|
134928
|
+
return;
|
|
134929
|
+
}
|
|
134930
|
+
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}`);
|
|
134931
|
+
}, MessageProcessor.QUEUE_WAIT_WARNING_MS);
|
|
134932
|
+
waitWarning.unref?.();
|
|
134933
|
+
this.logger.debug(`Enqueued message type=${message.type} sessionId=${sessionId || "N/A"} active=${this.queue.active} waiting=${this.queue.waiting}`);
|
|
134861
134934
|
void this.queue.enqueue(queueKey, async () => {
|
|
134862
134935
|
const startTime = Date.now();
|
|
134936
|
+
started = true;
|
|
134937
|
+
clearInterval(waitWarning);
|
|
134938
|
+
let processingCompleted = false;
|
|
134939
|
+
const processingWarning = setInterval(() => {
|
|
134940
|
+
if (processingCompleted) {
|
|
134941
|
+
return;
|
|
134942
|
+
}
|
|
134943
|
+
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}`);
|
|
134944
|
+
}, MessageProcessor.PROCESSING_WARNING_MS);
|
|
134945
|
+
processingWarning.unref?.();
|
|
134863
134946
|
try {
|
|
134864
|
-
this.logger.debug(`Processing message type=${message.type} sessionId=${sessionId || "N/A"}`);
|
|
134947
|
+
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
134948
|
await handler(message);
|
|
134866
134949
|
const duration2 = Date.now() - startTime;
|
|
134867
134950
|
this.logger.debug(`Processed message type=${message.type} sessionId=${sessionId || "N/A"} duration=${duration2}ms`);
|
|
@@ -134871,6 +134954,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134871
134954
|
const err2 = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
134872
134955
|
this.logger.error(`Failed to process message type=${message.type} sessionId=${sessionId || "N/A"} duration=${duration2}ms: ${err2.message}`);
|
|
134873
134956
|
this.emit("message:error", err2, message);
|
|
134957
|
+
} finally {
|
|
134958
|
+
processingCompleted = true;
|
|
134959
|
+
clearInterval(processingWarning);
|
|
134874
134960
|
}
|
|
134875
134961
|
});
|
|
134876
134962
|
}
|
|
@@ -135235,6 +135321,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135235
135321
|
}
|
|
135236
135322
|
async dispatchLocalMessageForResponse(message) {
|
|
135237
135323
|
const handler = this.requireHandler();
|
|
135324
|
+
const dispatchStartedAt = Date.now();
|
|
135325
|
+
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
135326
|
return await new Promise((resolve2, reject) => {
|
|
135239
135327
|
this.messageProcessor.enqueue(message, async (nextMessage) => {
|
|
135240
135328
|
const responses = [];
|
|
@@ -135244,6 +135332,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135244
135332
|
return;
|
|
135245
135333
|
}
|
|
135246
135334
|
settled = true;
|
|
135335
|
+
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
135336
|
resolve2([
|
|
135248
135337
|
...responses
|
|
135249
135338
|
]);
|
|
@@ -135253,6 +135342,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135253
135342
|
return;
|
|
135254
135343
|
}
|
|
135255
135344
|
settled = true;
|
|
135345
|
+
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
135346
|
reject(error2);
|
|
135257
135347
|
};
|
|
135258
135348
|
const context2 = {
|
|
@@ -142420,7 +142510,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142420
142510
|
await this.workspaceDocument.getOrCreateSessionDoc(config2.sessionId);
|
|
142421
142511
|
this.logger.debug(`[${config2.sessionId}] Session will enter create inner`);
|
|
142422
142512
|
const createInnerStart = performance$1.now();
|
|
142423
|
-
const session = await this.createSessionInner(config2);
|
|
142513
|
+
const session = await withSlowOperationWarning(this.createSessionInner(config2), this.logger, "session.createSessionInner", config2.sessionId);
|
|
142424
142514
|
session.ghTokenInjected = ghTokenInjected;
|
|
142425
142515
|
const createInnerDurationMs = performance$1.now() - createInnerStart;
|
|
142426
142516
|
this.logger.debug(`[${config2.sessionId}] Session create inner finished in ${Math.round(createInnerDurationMs)}ms`);
|
|
@@ -142441,7 +142531,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142441
142531
|
this.logger.debug(`[${sessionId}] About to call session.createAgent`);
|
|
142442
142532
|
try {
|
|
142443
142533
|
const createAgentStart = performance$1.now();
|
|
142444
|
-
acpSessionId = await session.createAgent({
|
|
142534
|
+
acpSessionId = await withSlowOperationWarning(session.createAgent({
|
|
142445
142535
|
cliType: config2.agentCliType,
|
|
142446
142536
|
agentType: config2.agentType,
|
|
142447
142537
|
command: setting.exec.command,
|
|
@@ -142501,7 +142591,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142501
142591
|
onCodexImageGenerationEnd: (event) => {
|
|
142502
142592
|
this.emit("onCodexImageGenerationEnd", sessionId, event);
|
|
142503
142593
|
}
|
|
142504
|
-
});
|
|
142594
|
+
}), this.logger, `session.createAgent(resume=${requestedResumeSessionId ? "yes" : "no"})`, sessionId);
|
|
142505
142595
|
const createAgentDurationMs = performance$1.now() - createAgentStart;
|
|
142506
142596
|
this.logger.debug(`[${sessionId}] Session createAgent finished in ${Math.round(createAgentDurationMs)}ms (acpSessionId=${acpSessionId})`);
|
|
142507
142597
|
this.logger.debug(`[${sessionId}] createAgent returned successfully`);
|
|
@@ -145144,6 +145234,23 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145144
145234
|
shutdown: shutdown2
|
|
145145
145235
|
};
|
|
145146
145236
|
}
|
|
145237
|
+
function startEventLoopLagMonitor(logger2, options) {
|
|
145238
|
+
const intervalMs = Math.max(100, options.intervalMs ?? 1e3);
|
|
145239
|
+
const warnThresholdMs = Math.max(intervalMs, options.warnThresholdMs ?? 5e3);
|
|
145240
|
+
let expectedAt = Date.now() + intervalMs;
|
|
145241
|
+
const timer2 = setInterval(() => {
|
|
145242
|
+
const now2 = Date.now();
|
|
145243
|
+
const lagMs = now2 - expectedAt;
|
|
145244
|
+
expectedAt = now2 + intervalMs;
|
|
145245
|
+
if (lagMs >= warnThresholdMs) {
|
|
145246
|
+
logger2.warn(`[event-loop] ${options.label} timer lag detected: fired ${Math.round(lagMs)}ms late (threshold=${warnThresholdMs}ms interval=${intervalMs}ms)`);
|
|
145247
|
+
}
|
|
145248
|
+
}, intervalMs);
|
|
145249
|
+
timer2.unref?.();
|
|
145250
|
+
return {
|
|
145251
|
+
stop: () => clearInterval(timer2)
|
|
145252
|
+
};
|
|
145253
|
+
}
|
|
145147
145254
|
const ELECTRON_BOOTSTRAP_ENV = "LODY_ELECTRON_BOOTSTRAP";
|
|
145148
145255
|
const ELECTRON_SESSION_TOKEN_ENV = "LODY_ELECTRON_SESSION_TOKEN";
|
|
145149
145256
|
const ELECTRON_SESSION_USER_ID_ENV = "LODY_ELECTRON_SESSION_USER_ID";
|
|
@@ -145371,7 +145478,13 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145371
145478
|
runtimeStateReporter,
|
|
145372
145479
|
onFatalAuthFailure: (error2) => triggerFatalAuthShutdown?.(error2)
|
|
145373
145480
|
});
|
|
145374
|
-
|
|
145481
|
+
const eventLoopLagMonitor = startEventLoopLagMonitor(logger2, {
|
|
145482
|
+
label: "lody start"
|
|
145483
|
+
});
|
|
145484
|
+
registerProcessCleanup(async () => {
|
|
145485
|
+
eventLoopLagMonitor.stop();
|
|
145486
|
+
await fleet.shutdown();
|
|
145487
|
+
});
|
|
145375
145488
|
const shutdownSignals = process.platform === "win32" ? [
|
|
145376
145489
|
"SIGINT",
|
|
145377
145490
|
"SIGTERM",
|
|
@@ -145387,6 +145500,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145387
145500
|
logger: logger2,
|
|
145388
145501
|
shutdown: async () => {
|
|
145389
145502
|
unregisterProcessCleanup();
|
|
145503
|
+
eventLoopLagMonitor.stop();
|
|
145390
145504
|
await fleet.shutdown();
|
|
145391
145505
|
},
|
|
145392
145506
|
flushTelemetry: () => shutdownPostHog(),
|
|
@@ -145425,6 +145539,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145425
145539
|
});
|
|
145426
145540
|
shutdownController.unregister();
|
|
145427
145541
|
unregisterProcessCleanup();
|
|
145542
|
+
eventLoopLagMonitor.stop();
|
|
145428
145543
|
await fleet.shutdown().catch((err2) => {
|
|
145429
145544
|
logger2.error(`Cleanup failed: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
145430
145545
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lody",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.1",
|
|
4
4
|
"description": "Lody Agent CLI tool for managing remote command execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
"winston-transport": "^4.7.1",
|
|
74
74
|
"ws": "^8.18.3",
|
|
75
75
|
"zod": "^4.1.5",
|
|
76
|
-
"@lody/cli-supervisor": "0.0.1",
|
|
77
76
|
"@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/cli-supervisor": "0.0.1"
|
|
81
81
|
},
|
|
82
82
|
"files": [
|
|
83
83
|
"dist",
|