lody 0.51.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 +219 -78
- package/package.json +5 -5
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.
|
|
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";
|
|
@@ -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.
|
|
36868
|
+
"acp-extension-claude": "0.34.1",
|
|
36869
36869
|
"acp-extension-codex": "0.14.3"
|
|
36870
36870
|
};
|
|
36871
36871
|
const devDependencies = {
|
|
@@ -37076,7 +37076,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
37076
37076
|
const runtimeEnv = getRuntimeEnv();
|
|
37077
37077
|
const environment$1 = "production";
|
|
37078
37078
|
const dsn = "https://080f9de535ff335a1a0440d0e385f796@o4510491299086336.ingest.us.sentry.io/4510559045681152";
|
|
37079
|
-
const postHogHost = process.env.LODY_POSTHOG_HOST ?? "https://
|
|
37079
|
+
const postHogHost = process.env.LODY_POSTHOG_HOST ?? "https://m.lody.ai";
|
|
37080
37080
|
const postHogKey = process.env.LODY_POSTHOG_KEY ?? "phc_LFS5i5WIwg4irAhrG5oJR04iYPhReVZ3DdFZOKqCkjG";
|
|
37081
37081
|
const tracesSampleRate = Number(process.env.SENTRY_TRACES_SAMPLE_RATE) || 0.2;
|
|
37082
37082
|
const profilesSampleRate = Number(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 0.1;
|
|
@@ -77781,6 +77781,9 @@ Task description:
|
|
|
77781
77781
|
}
|
|
77782
77782
|
return void 0;
|
|
77783
77783
|
};
|
|
77784
|
+
function getLocalProjectHistoryProviderKey(provider2) {
|
|
77785
|
+
return `${provider2.cliType}:${provider2.agentType}`;
|
|
77786
|
+
}
|
|
77784
77787
|
const DEFAULT_BASE_BRANCH = "main";
|
|
77785
77788
|
function getTrimmedBranch(value) {
|
|
77786
77789
|
if (typeof value !== "string") {
|
|
@@ -78662,10 +78665,10 @@ Task description:
|
|
|
78662
78665
|
localProjectId: LocalProjectIdSchema,
|
|
78663
78666
|
branchName: string$2()
|
|
78664
78667
|
}).strict();
|
|
78665
|
-
const LocalProjectHistoryProviderSchema =
|
|
78666
|
-
|
|
78667
|
-
|
|
78668
|
-
|
|
78668
|
+
const LocalProjectHistoryProviderSchema = object$1({
|
|
78669
|
+
cliType: AgentConfigCliTypeSchema,
|
|
78670
|
+
agentType: string$2().trim().min(1)
|
|
78671
|
+
}).strict();
|
|
78669
78672
|
const LocalProjectSyncHistoryRequestSchema = object$1({
|
|
78670
78673
|
type: literal("local-project/sync-history"),
|
|
78671
78674
|
machineId: MachineIdSchema,
|
|
@@ -82556,8 +82559,8 @@ Task description:
|
|
|
82556
82559
|
function createUserEntry(args2) {
|
|
82557
82560
|
const inputConfig = {
|
|
82558
82561
|
prompt: args2.text,
|
|
82559
|
-
cliType:
|
|
82560
|
-
agentType: args2.agentType,
|
|
82562
|
+
cliType: args2.provider.cliType,
|
|
82563
|
+
agentType: args2.provider.agentType,
|
|
82561
82564
|
...{}
|
|
82562
82565
|
};
|
|
82563
82566
|
return {
|
|
@@ -82582,7 +82585,7 @@ Task description:
|
|
|
82582
82585
|
const now2 = options.now ?? defaultNow;
|
|
82583
82586
|
const createId = options.createId ?? defaultCreateId;
|
|
82584
82587
|
const mode2 = options.mode;
|
|
82585
|
-
const
|
|
82588
|
+
const provider2 = options.provider;
|
|
82586
82589
|
let history = [];
|
|
82587
82590
|
let lastWasUserChunk = false;
|
|
82588
82591
|
let droppedNotifications = 0;
|
|
@@ -82604,7 +82607,7 @@ Task description:
|
|
|
82604
82607
|
text,
|
|
82605
82608
|
timestamp: now2(),
|
|
82606
82609
|
userId: options.userId,
|
|
82607
|
-
|
|
82610
|
+
provider: provider2,
|
|
82608
82611
|
mode: mode2
|
|
82609
82612
|
}));
|
|
82610
82613
|
}
|
|
@@ -85473,13 +85476,14 @@ ${tailedOutput}` : null;
|
|
|
85473
85476
|
};
|
|
85474
85477
|
function withSlowOperationWarning(promise, logger2, operationName, sessionId, intervalMs = 1e4) {
|
|
85475
85478
|
let completed = false;
|
|
85476
|
-
|
|
85479
|
+
const startMs = Date.now();
|
|
85477
85480
|
const interval2 = setInterval(() => {
|
|
85478
|
-
elapsedMs += intervalMs;
|
|
85479
85481
|
if (!completed) {
|
|
85482
|
+
const elapsedMs = Date.now() - startMs;
|
|
85480
85483
|
logger2.debug(`[${sessionId}] Operation "${operationName}" is still pending after ${Math.round(elapsedMs / 1e3)}s - possible hang`);
|
|
85481
85484
|
}
|
|
85482
85485
|
}, intervalMs);
|
|
85486
|
+
interval2.unref?.();
|
|
85483
85487
|
return promise.finally(() => {
|
|
85484
85488
|
completed = true;
|
|
85485
85489
|
clearInterval(interval2);
|
|
@@ -98394,11 +98398,13 @@ stream:${scope2.streamId}`;
|
|
|
98394
98398
|
machineId = null;
|
|
98395
98399
|
machineKey = null;
|
|
98396
98400
|
sessionKeys = /* @__PURE__ */ new Map();
|
|
98401
|
+
machineHeartbeatSeq = 0;
|
|
98397
98402
|
started = false;
|
|
98398
98403
|
stopped = false;
|
|
98399
98404
|
start() {
|
|
98400
98405
|
if (this.started || this.stopped) return;
|
|
98401
98406
|
this.started = true;
|
|
98407
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Joining Loro presence room`);
|
|
98402
98408
|
void this.transport.join({
|
|
98403
98409
|
onStatusChange: (status) => {
|
|
98404
98410
|
this.options.logger.debug(`[${this.options.workspaceId}] Loro presence room status: ${status}`);
|
|
@@ -98410,6 +98416,7 @@ stream:${scope2.streamId}`;
|
|
|
98410
98416
|
}
|
|
98411
98417
|
if (result.ok) {
|
|
98412
98418
|
this.subscription = result.value;
|
|
98419
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Loro presence subscription established`);
|
|
98413
98420
|
return;
|
|
98414
98421
|
}
|
|
98415
98422
|
this.options.logger.debug(`[${this.options.workspaceId}] Failed to join Loro presence room: ${formatErrorMessage(result.error)}`);
|
|
@@ -98439,6 +98446,7 @@ stream:${scope2.streamId}`;
|
|
|
98439
98446
|
}
|
|
98440
98447
|
writeMachineHeartbeat() {
|
|
98441
98448
|
if (this.stopped || !this.machineId || !this.machineKey) return;
|
|
98449
|
+
const seq2 = ++this.machineHeartbeatSeq;
|
|
98442
98450
|
const state2 = {
|
|
98443
98451
|
kind: "machine",
|
|
98444
98452
|
machineId: this.machineId,
|
|
@@ -98446,6 +98454,7 @@ stream:${scope2.streamId}`;
|
|
|
98446
98454
|
updatedAt: getServerNow()
|
|
98447
98455
|
};
|
|
98448
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})`);
|
|
98449
98458
|
}
|
|
98450
98459
|
setSessionPresence(args2) {
|
|
98451
98460
|
if (this.stopped) return;
|
|
@@ -116853,6 +116862,18 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116853
116862
|
let metaSub = null;
|
|
116854
116863
|
const initialMetaSync = createDeferred();
|
|
116855
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
|
+
};
|
|
116856
116877
|
const transportAdapter = new StreamsTransportAdapter({
|
|
116857
116878
|
bucketId: LORO_STREAMS_BUCKET_ID,
|
|
116858
116879
|
metaStreamId: getLoroMetaStreamId(workspaceId),
|
|
@@ -116866,10 +116887,10 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116866
116887
|
debounceMs: 5e3
|
|
116867
116888
|
},
|
|
116868
116889
|
onPersistDoc: async () => {
|
|
116869
|
-
await
|
|
116890
|
+
await flushRepoForPersist("doc");
|
|
116870
116891
|
},
|
|
116871
116892
|
onPersistMeta: async () => {
|
|
116872
|
-
await
|
|
116893
|
+
await flushRepoForPersist("meta");
|
|
116873
116894
|
}
|
|
116874
116895
|
});
|
|
116875
116896
|
let manager = null;
|
|
@@ -117163,8 +117184,11 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
117163
117184
|
if (!this.machine) {
|
|
117164
117185
|
return;
|
|
117165
117186
|
}
|
|
117166
|
-
|
|
117187
|
+
const startedAt = Date.now();
|
|
117188
|
+
this.logger.debug(`[${this.workspaceId}] Machine heartbeat writing presence and meta`);
|
|
117167
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)`);
|
|
117168
117192
|
}
|
|
117169
117193
|
async restoreMachineDocument(machineId) {
|
|
117170
117194
|
const machineRoomId = getMachineRoomId(machineId);
|
|
@@ -121114,6 +121138,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121114
121138
|
nes_reject: "nes/reject",
|
|
121115
121139
|
nes_start: "nes/start",
|
|
121116
121140
|
nes_suggest: "nes/suggest",
|
|
121141
|
+
providers_disable: "providers/disable",
|
|
121142
|
+
providers_list: "providers/list",
|
|
121143
|
+
providers_set: "providers/set",
|
|
121117
121144
|
session_cancel: "session/cancel",
|
|
121118
121145
|
session_close: "session/close",
|
|
121119
121146
|
session_fork: "session/fork",
|
|
@@ -121315,6 +121342,15 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121315
121342
|
async authenticate(params) {
|
|
121316
121343
|
return await this.connection.sendRequest(AGENT_METHODS.authenticate, params) ?? {};
|
|
121317
121344
|
}
|
|
121345
|
+
async unstable_listProviders(params) {
|
|
121346
|
+
return await this.connection.sendRequest(AGENT_METHODS.providers_list, params);
|
|
121347
|
+
}
|
|
121348
|
+
async unstable_setProvider(params) {
|
|
121349
|
+
return await this.connection.sendRequest(AGENT_METHODS.providers_set, params) ?? {};
|
|
121350
|
+
}
|
|
121351
|
+
async unstable_disableProvider(params) {
|
|
121352
|
+
return await this.connection.sendRequest(AGENT_METHODS.providers_disable, params) ?? {};
|
|
121353
|
+
}
|
|
121318
121354
|
async unstable_logout(params) {
|
|
121319
121355
|
return await this.connection.sendRequest(AGENT_METHODS.logout, params) ?? {};
|
|
121320
121356
|
}
|
|
@@ -122091,7 +122127,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122091
122127
|
this.supportsClose = !!closeCapability;
|
|
122092
122128
|
const hasCloseMethod = typeof connection.closeSession === "function";
|
|
122093
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"})`);
|
|
122094
|
-
this.logger.debug(`[${this.options.sessionId}] About to
|
|
122130
|
+
this.logger.debug(`[${this.options.sessionId}] About to establish ACP session`);
|
|
122095
122131
|
const newSessionStart = performance$1.now();
|
|
122096
122132
|
this.options.onStartupStage?.({
|
|
122097
122133
|
type: "new_session_start"
|
|
@@ -122118,11 +122154,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122118
122154
|
const loadStart = performance$1.now();
|
|
122119
122155
|
try {
|
|
122120
122156
|
this.logger.debug(`[${this.options.sessionId}] Attempting ACP loadSession (acpSessionId=${resumeSessionId})`);
|
|
122121
|
-
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({
|
|
122122
122159
|
sessionId: resumeSessionId,
|
|
122123
122160
|
cwd: workdir,
|
|
122124
122161
|
mcpServers
|
|
122125
|
-
}), startupAbort);
|
|
122162
|
+
}), startupAbort), this.logger, "connection.loadSession", this.options.sessionId, ACP_LOAD_SESSION_TIMEOUT_MS);
|
|
122126
122163
|
const loadDurationMs = performance$1.now() - loadStart;
|
|
122127
122164
|
sessionResponse = {
|
|
122128
122165
|
sessionId: resumeSessionId,
|
|
@@ -122143,11 +122180,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122143
122180
|
const resumeStart = performance$1.now();
|
|
122144
122181
|
try {
|
|
122145
122182
|
this.logger.debug(`[${this.options.sessionId}] Attempting ACP resume (acpSessionId=${resumeSessionId})`);
|
|
122146
|
-
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({
|
|
122147
122185
|
sessionId: resumeSessionId,
|
|
122148
122186
|
cwd: workdir,
|
|
122149
122187
|
mcpServers
|
|
122150
|
-
}), startupAbort);
|
|
122188
|
+
}), startupAbort), this.logger, "connection.resumeSession", this.options.sessionId, ACP_RESUME_SESSION_TIMEOUT_MS);
|
|
122151
122189
|
const resumeDurationMs = performance$1.now() - resumeStart;
|
|
122152
122190
|
sessionResponse = {
|
|
122153
122191
|
sessionId: resumeSessionId,
|
|
@@ -122413,7 +122451,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122413
122451
|
const BuiltinACPSetting = {
|
|
122414
122452
|
claude: {
|
|
122415
122453
|
packageName: "acp-extension-claude",
|
|
122416
|
-
version: "0.
|
|
122454
|
+
version: "0.34.1",
|
|
122417
122455
|
binName: "acp-extension-claude"
|
|
122418
122456
|
},
|
|
122419
122457
|
codex: {
|
|
@@ -122904,6 +122942,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122904
122942
|
args: args2,
|
|
122905
122943
|
spawnImpl: options.spawnImpl
|
|
122906
122944
|
});
|
|
122945
|
+
options.logger.debug(`[acp-startup] spawned ACP process (cliType=${options.cliType} agentType=${options.agentType} workdir=${options.workdir})`);
|
|
122907
122946
|
const stderrStream = agentProcess.stderr;
|
|
122908
122947
|
let stderrTail = "";
|
|
122909
122948
|
if (stderrStream) {
|
|
@@ -122965,6 +123004,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122965
123004
|
const input2 = createStdinWritableStream(agentProcess.stdin);
|
|
122966
123005
|
const stream2 = ndJsonStream(input2, output);
|
|
122967
123006
|
try {
|
|
123007
|
+
options.logger.debug("[acp-startup] creating ACP client");
|
|
122968
123008
|
const started = await createAcpClient({
|
|
122969
123009
|
stream: stream2,
|
|
122970
123010
|
workdir: options.workdir,
|
|
@@ -122979,6 +123019,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122979
123019
|
onRequestPermission: options.onRequestPermission,
|
|
122980
123020
|
startupAbort: startupMonitor.abortPromise
|
|
122981
123021
|
});
|
|
123022
|
+
options.logger.debug(`[acp-startup] ACP client ready (acpSessionId=${started.acpSessionId})`);
|
|
122982
123023
|
return {
|
|
122983
123024
|
agentProcess,
|
|
122984
123025
|
client: started.client,
|
|
@@ -123122,6 +123163,8 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123122
123163
|
const sleep2 = (ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2));
|
|
123123
123164
|
const tryGenerateWithArgs = async (extraArgs) => {
|
|
123124
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})`);
|
|
123125
123168
|
const { agentProcess, client, acpSessionId } = await startLocalAcpAgent({
|
|
123126
123169
|
cliType: options.cliType,
|
|
123127
123170
|
agentType: options.agentType,
|
|
@@ -123147,6 +123190,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123147
123190
|
}),
|
|
123148
123191
|
extraArgs
|
|
123149
123192
|
});
|
|
123193
|
+
options.logger.debug(`[title-generator] Isolated title ACP agent ready (acpSessionId=${acpSessionId} startupDuration=${Date.now() - startupStartedAt}ms)`);
|
|
123150
123194
|
try {
|
|
123151
123195
|
const prompt2 = buildTitlePrompt(options.taskPrompt);
|
|
123152
123196
|
const configOptionValues = options.titleConfig?.configOptionValues;
|
|
@@ -123155,7 +123199,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
123155
123199
|
await client?.setSessionConfigOption(acpSessionId, key2, value);
|
|
123156
123200
|
}
|
|
123157
123201
|
}
|
|
123202
|
+
options.logger.debug(`[title-generator] Sending title prompt (acpSessionId=${acpSessionId})`);
|
|
123158
123203
|
const response = await client?.prompt(acpSessionId, prompt2);
|
|
123204
|
+
options.logger.debug(`[title-generator] Title prompt returned (acpSessionId=${acpSessionId})`);
|
|
123159
123205
|
const deadline = Date.now() + 1e4;
|
|
123160
123206
|
while (Date.now() < deadline && collectedText.trim() === "") {
|
|
123161
123207
|
await sleep2(100);
|
|
@@ -126014,14 +126060,10 @@ $mem | ConvertTo-Json -Compress
|
|
|
126014
126060
|
}
|
|
126015
126061
|
function getImportedAcpSourceAcpSessionId(meta) {
|
|
126016
126062
|
const externalHistory = meta.externalHistory;
|
|
126017
|
-
|
|
126018
|
-
if (!externalHistory || provider2 !== "codex" && provider2 !== "claude") {
|
|
126019
|
-
return void 0;
|
|
126020
|
-
}
|
|
126021
|
-
return externalHistory.sourceAcpSessionId;
|
|
126063
|
+
return externalHistory?.sourceAcpSessionId;
|
|
126022
126064
|
}
|
|
126023
126065
|
function isImportedAcpReplayUserTurn(entry2, meta) {
|
|
126024
|
-
const provider2 = meta.externalHistory
|
|
126066
|
+
const provider2 = meta.externalHistory ? getLocalProjectHistoryProviderKey(meta.externalHistory.provider) : null;
|
|
126025
126067
|
const sourceAcpSessionId = getImportedAcpSourceAcpSessionId(meta);
|
|
126026
126068
|
return !!provider2 && !!sourceAcpSessionId && entry2.id.startsWith(`${provider2}:${sourceAcpSessionId}:turn:`);
|
|
126027
126069
|
}
|
|
@@ -127157,6 +127199,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
127157
127199
|
const resumeSource = requestedResumeSessionId ? "request" : storedResumeSessionId ? "meta" : "none";
|
|
127158
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"})`);
|
|
127159
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"})`);
|
|
127160
127203
|
const restoreConfig = {
|
|
127161
127204
|
sessionId,
|
|
127162
127205
|
workspaceId: message.workspaceId,
|
|
@@ -127180,11 +127223,13 @@ $mem | ConvertTo-Json -Compress
|
|
|
127180
127223
|
if (resumeSessionId) {
|
|
127181
127224
|
yield* acpReplaySuppression.acquire;
|
|
127182
127225
|
}
|
|
127226
|
+
self2.deps.logger.debug(`[${sessionId}] Session restore createSession started (resumeSessionId=${resumeSessionId ?? "none"})`);
|
|
127183
127227
|
const restoredSession = yield* ctx.trackPendingSession(() => self2.deps.sessionManager.createSession(restoreConfig, {
|
|
127184
127228
|
resumeSessionId
|
|
127185
127229
|
}), {
|
|
127186
127230
|
terminateOnCancel: true
|
|
127187
127231
|
});
|
|
127232
|
+
self2.deps.logger.debug(`[${sessionId}] Session restore createSession returned (acpSessionId=${restoredSession.acpSessionId ?? "null"})`);
|
|
127188
127233
|
ctx.bindSession(restoredSession);
|
|
127189
127234
|
yield* ctx.abortIfCancelled({
|
|
127190
127235
|
terminateSession: true
|
|
@@ -127210,9 +127255,11 @@ $mem | ConvertTo-Json -Compress
|
|
|
127210
127255
|
...restoreConfig
|
|
127211
127256
|
};
|
|
127212
127257
|
const fallbackAttempt = gen(function* () {
|
|
127258
|
+
self2.deps.logger.debug(`[${sessionId}] Fallback restore createSession started`);
|
|
127213
127259
|
const fallbackSession = yield* ctx.trackPendingSession(() => self2.deps.sessionManager.createSession(fallbackConfig), {
|
|
127214
127260
|
terminateOnCancel: true
|
|
127215
127261
|
});
|
|
127262
|
+
self2.deps.logger.debug(`[${sessionId}] Fallback restore createSession returned (acpSessionId=${fallbackSession.acpSessionId ?? "null"})`);
|
|
127216
127263
|
ctx.bindSession(fallbackSession);
|
|
127217
127264
|
yield* ctx.abortIfCancelled({
|
|
127218
127265
|
terminateSession: true
|
|
@@ -128332,6 +128379,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
128332
128379
|
static REALTIME_WAIT_TIMEOUT_MS = 3e4;
|
|
128333
128380
|
static REMOTE_SYNC_TIMEOUT_MS = 15e3;
|
|
128334
128381
|
static HISTORY_SYNC_WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
128382
|
+
static HISTORY_SYNC_PROGRESS_LOG_MS = 3e4;
|
|
128335
128383
|
static HISTORY_RECONNECT_JITTER_MIN_MS = 500;
|
|
128336
128384
|
static HISTORY_RECONNECT_JITTER_MAX_MS = 1500;
|
|
128337
128385
|
static setUnrefTimeout(callback, delayMs) {
|
|
@@ -128404,12 +128452,17 @@ $mem | ConvertTo-Json -Compress
|
|
|
128404
128452
|
let reconnectAttempted = false;
|
|
128405
128453
|
let unsubscribeMirror;
|
|
128406
128454
|
let unsubscribeStatus;
|
|
128455
|
+
let progressTimer = null;
|
|
128456
|
+
const waitStartedAt = Date.now();
|
|
128407
128457
|
const cleanup = () => {
|
|
128408
128458
|
if (settled) {
|
|
128409
128459
|
return;
|
|
128410
128460
|
}
|
|
128411
128461
|
settled = true;
|
|
128412
128462
|
clearTimeout(timer2);
|
|
128463
|
+
if (progressTimer) {
|
|
128464
|
+
clearInterval(progressTimer);
|
|
128465
|
+
}
|
|
128413
128466
|
unsubscribeMirror?.();
|
|
128414
128467
|
unsubscribeStatus?.();
|
|
128415
128468
|
};
|
|
@@ -128464,6 +128517,13 @@ $mem | ConvertTo-Json -Compress
|
|
|
128464
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`);
|
|
128465
128518
|
resolve2(null);
|
|
128466
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?.();
|
|
128467
128527
|
unsubscribeMirror = sessionDoc.mirror?.subscribe(checkForTurn);
|
|
128468
128528
|
if (!unsubscribeMirror) {
|
|
128469
128529
|
this.deps.logger.debug(`[${sessionId}] Session mirror is unavailable during history sync wait`);
|
|
@@ -131099,27 +131159,24 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131099
131159
|
}
|
|
131100
131160
|
}
|
|
131101
131161
|
function getProviderLabel$1(provider2) {
|
|
131102
|
-
return provider2
|
|
131162
|
+
return getLocalProjectHistoryProviderKey(provider2);
|
|
131103
131163
|
}
|
|
131104
131164
|
async function createHistoryAcpConnection(args2) {
|
|
131105
|
-
const setting = resolveACPSetting(
|
|
131106
|
-
cliType: "builtin",
|
|
131107
|
-
agentType: args2.provider
|
|
131108
|
-
});
|
|
131165
|
+
const setting = resolveACPSetting(args2.provider);
|
|
131109
131166
|
const env2 = setting.exec.env ? {
|
|
131110
131167
|
...process.env,
|
|
131111
131168
|
...setting.exec.env
|
|
131112
131169
|
} : process.env;
|
|
131113
131170
|
const agentProcess = spawnAcpProcess({
|
|
131114
|
-
cliType:
|
|
131115
|
-
agentType: args2.provider,
|
|
131171
|
+
cliType: args2.provider.cliType,
|
|
131172
|
+
agentType: args2.provider.agentType,
|
|
131116
131173
|
workdir: args2.workdir,
|
|
131117
131174
|
env: env2
|
|
131118
131175
|
});
|
|
131119
131176
|
agentProcess.stderr?.setEncoding("utf8");
|
|
131120
131177
|
agentProcess.stderr?.on("data", (chunk) => {
|
|
131121
131178
|
if (!chunk) return;
|
|
131122
|
-
args2.logger.debug(`[${args2.provider}-history-sync] ACP stderr: ${chunk.slice(0, 1200)}`);
|
|
131179
|
+
args2.logger.debug(`[${getProviderLabel$1(args2.provider)}-history-sync] ACP stderr: ${chunk.slice(0, 1200)}`);
|
|
131123
131180
|
});
|
|
131124
131181
|
if (!agentProcess.stdout || !agentProcess.stdin) {
|
|
131125
131182
|
await terminateChildProcess(agentProcess);
|
|
@@ -131230,7 +131287,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131230
131287
|
}), `${getProviderLabel$1(args2.provider)} ACP loadSession (${args2.acpSessionId})`);
|
|
131231
131288
|
return collector.notifications;
|
|
131232
131289
|
} catch (error2) {
|
|
131233
|
-
|
|
131290
|
+
const message = `Failed to load ${getProviderLabel$1(args2.provider)} session ${args2.acpSessionId}: ${formatErrorMessage(error2)}`;
|
|
131291
|
+
throw new Error(message, {
|
|
131234
131292
|
cause: error2
|
|
131235
131293
|
});
|
|
131236
131294
|
} finally {
|
|
@@ -131295,17 +131353,18 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131295
131353
|
function materializeReplay(args2) {
|
|
131296
131354
|
let tempId = 0;
|
|
131297
131355
|
const nowIso = new Date(getServerNow()).toISOString();
|
|
131356
|
+
const providerKey = getLocalProjectHistoryProviderKey(args2.provider);
|
|
131298
131357
|
const replay = buildHistoryReplayImport(args2.replayNotifications, {
|
|
131299
|
-
|
|
131358
|
+
provider: args2.provider,
|
|
131300
131359
|
userId: args2.userId,
|
|
131301
131360
|
now: () => nowIso,
|
|
131302
|
-
createId: () => `${
|
|
131361
|
+
createId: () => `${providerKey}:${args2.acpSessionId}:tmp:${tempId++}`,
|
|
131303
131362
|
mode: "imported_snapshot"
|
|
131304
131363
|
});
|
|
131305
131364
|
const turnHashes = replay.history.map(hashHistoryEntry);
|
|
131306
131365
|
const history = replay.history.map((entry2, index2) => ({
|
|
131307
131366
|
...entry2,
|
|
131308
|
-
id: `${
|
|
131367
|
+
id: `${providerKey}:${args2.acpSessionId}:turn:${index2}:${turnHashes[index2].slice(0, 16)}`
|
|
131309
131368
|
}));
|
|
131310
131369
|
return {
|
|
131311
131370
|
history,
|
|
@@ -131391,12 +131450,13 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131391
131450
|
}
|
|
131392
131451
|
function buildExistingHistorySessionIndex(metas, machineId, provider2) {
|
|
131393
131452
|
const index2 = /* @__PURE__ */ new Map();
|
|
131453
|
+
const providerKey = getLocalProjectHistoryProviderKey(provider2);
|
|
131394
131454
|
for (const entry2 of metas) {
|
|
131395
131455
|
if (entry2.meta.machineId !== machineId) continue;
|
|
131396
|
-
if (entry2.meta.cliType !==
|
|
131397
|
-
if (entry2.meta.agentType !== provider2) continue;
|
|
131456
|
+
if (entry2.meta.cliType !== provider2.cliType) continue;
|
|
131457
|
+
if (entry2.meta.agentType !== provider2.agentType) continue;
|
|
131398
131458
|
const acpSessionIds = /* @__PURE__ */ new Set();
|
|
131399
|
-
if (entry2.meta.externalHistory
|
|
131459
|
+
if (entry2.meta.externalHistory && getLocalProjectHistoryProviderKey(entry2.meta.externalHistory.provider) === providerKey) {
|
|
131400
131460
|
const sourceAcpSessionId = entry2.meta.externalHistory.sourceAcpSessionId;
|
|
131401
131461
|
if (sourceAcpSessionId) {
|
|
131402
131462
|
acpSessionIds.add(sourceAcpSessionId);
|
|
@@ -131414,7 +131474,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131414
131474
|
return index2;
|
|
131415
131475
|
}
|
|
131416
131476
|
function getProviderLabel(provider2) {
|
|
131417
|
-
return provider2
|
|
131477
|
+
return getLocalProjectHistoryProviderKey(provider2);
|
|
131418
131478
|
}
|
|
131419
131479
|
function resolveSessionTitle(info, provider2) {
|
|
131420
131480
|
const title2 = info.title?.trim();
|
|
@@ -131468,7 +131528,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131468
131528
|
function buildExternalHistoryMeta(args2) {
|
|
131469
131529
|
return {
|
|
131470
131530
|
provider: args2.provider,
|
|
131471
|
-
source:
|
|
131531
|
+
source: "local-acp-history",
|
|
131472
131532
|
sourceAcpSessionId: args2.sourceAcpSessionId,
|
|
131473
131533
|
sourceUpdatedAt: args2.sourceUpdatedAt ?? void 0,
|
|
131474
131534
|
replayDigest: args2.materialized.replayDigest,
|
|
@@ -131482,7 +131542,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131482
131542
|
function buildMetadataOnlyExternalHistoryMeta(args2) {
|
|
131483
131543
|
return {
|
|
131484
131544
|
provider: args2.provider,
|
|
131485
|
-
source:
|
|
131545
|
+
source: "local-acp-history",
|
|
131486
131546
|
sourceAcpSessionId: args2.sourceAcpSessionId,
|
|
131487
131547
|
sourceUpdatedAt: args2.sourceUpdatedAt ?? void 0,
|
|
131488
131548
|
importedTurnCount: 0,
|
|
@@ -131492,15 +131552,17 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131492
131552
|
};
|
|
131493
131553
|
}
|
|
131494
131554
|
class LocalProjectHistorySyncService {
|
|
131495
|
-
constructor(manager, logger2, context2, provider2
|
|
131555
|
+
constructor(manager, logger2, context2, provider2) {
|
|
131496
131556
|
this.manager = manager;
|
|
131497
131557
|
this.logger = logger2;
|
|
131498
131558
|
this.context = context2;
|
|
131499
131559
|
this.provider = provider2;
|
|
131560
|
+
this.providerKey = getLocalProjectHistoryProviderKey(provider2);
|
|
131500
131561
|
}
|
|
131501
131562
|
provider;
|
|
131563
|
+
providerKey;
|
|
131502
131564
|
async syncLocalProject(args2) {
|
|
131503
|
-
const leaseKey = `${this.
|
|
131565
|
+
const leaseKey = `${this.providerKey}:${this.context.workspaceId}:${this.context.machineId}:${args2.localProjectId}`;
|
|
131504
131566
|
if (syncLeases.has(leaseKey)) {
|
|
131505
131567
|
throw new Error(`${getProviderLabel(this.provider)} history sync is already running for this local project`);
|
|
131506
131568
|
}
|
|
@@ -131520,7 +131582,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131520
131582
|
});
|
|
131521
131583
|
}
|
|
131522
131584
|
async importLocalProjectSessions(args2) {
|
|
131523
|
-
const leaseKey = `${this.
|
|
131585
|
+
const leaseKey = `${this.providerKey}:${this.context.workspaceId}:${this.context.machineId}:${args2.localProjectId}`;
|
|
131524
131586
|
if (syncLeases.has(leaseKey)) {
|
|
131525
131587
|
throw new Error(`${getProviderLabel(this.provider)} history sync is already running for this local project`);
|
|
131526
131588
|
}
|
|
@@ -131577,7 +131639,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131577
131639
|
acpSessionId,
|
|
131578
131640
|
message: formatErrorMessage(error2)
|
|
131579
131641
|
});
|
|
131580
|
-
this.logger.debug(`[${this.
|
|
131642
|
+
this.logger.debug(`[${this.providerKey}-history-sync] Failed to import ${getProviderLabel(this.provider)} session ${acpSessionId}: ${formatErrorMessage(error2)}`);
|
|
131581
131643
|
}
|
|
131582
131644
|
}
|
|
131583
131645
|
const catalog = await this.writeCatalogResult({
|
|
@@ -131627,7 +131689,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131627
131689
|
...previous,
|
|
131628
131690
|
history: {
|
|
131629
131691
|
...previous.history ?? {},
|
|
131630
|
-
[this.
|
|
131692
|
+
[this.providerKey]: {
|
|
131631
131693
|
lastListedAt,
|
|
131632
131694
|
sessions: Object.fromEntries(sessions.map((item) => [
|
|
131633
131695
|
item.acpSessionId,
|
|
@@ -131653,9 +131715,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131653
131715
|
userId: this.context.userId,
|
|
131654
131716
|
status: SessionStatusFactory.idle(),
|
|
131655
131717
|
isArchived: false,
|
|
131656
|
-
origin:
|
|
131657
|
-
cliType:
|
|
131658
|
-
agentType: this.provider,
|
|
131718
|
+
origin: "external-acp",
|
|
131719
|
+
cliType: this.provider.cliType,
|
|
131720
|
+
agentType: this.provider.agentType,
|
|
131659
131721
|
project: args2.project,
|
|
131660
131722
|
title: resolveSessionTitle(args2.info, this.provider),
|
|
131661
131723
|
lastMessageAt,
|
|
@@ -131673,7 +131735,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131673
131735
|
}
|
|
131674
131736
|
async refreshExistingSession(args2) {
|
|
131675
131737
|
const externalHistory = args2.existing.meta.externalHistory;
|
|
131676
|
-
if (externalHistory
|
|
131738
|
+
if (!externalHistory || getLocalProjectHistoryProviderKey(externalHistory.provider) !== this.providerKey) {
|
|
131677
131739
|
return "skipped";
|
|
131678
131740
|
}
|
|
131679
131741
|
if (shouldSkipBySourceUpdatedAt(args2.info, externalHistory)) {
|
|
@@ -131698,7 +131760,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131698
131760
|
});
|
|
131699
131761
|
if (replayDecision.reason === "digest_match") {
|
|
131700
131762
|
await this.manager.repo.upsertDocMeta(getSessionRoomId(args2.existing.sessionId), {
|
|
131701
|
-
origin:
|
|
131763
|
+
origin: "external-acp",
|
|
131702
131764
|
externalHistory: buildExternalHistoryMeta({
|
|
131703
131765
|
provider: this.provider,
|
|
131704
131766
|
sourceAcpSessionId: args2.acpSessionId,
|
|
@@ -131724,7 +131786,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131724
131786
|
await this.markConflict(args2.existing.sessionId, args2.info, materialized, appendDecision.reason);
|
|
131725
131787
|
const synced2 = await sessionDoc.waitUntilSynced();
|
|
131726
131788
|
if (!synced2) {
|
|
131727
|
-
this.logger.debug(`[${this.
|
|
131789
|
+
this.logger.debug(`[${this.providerKey}-history-sync] Conflict marker for ${args2.existing.sessionId} did not confirm sync before unload; clients may see the previous state until next sync.`);
|
|
131728
131790
|
}
|
|
131729
131791
|
await this.manager.cleanSessionDoc(args2.existing.sessionId, {
|
|
131730
131792
|
preserveStatus: true
|
|
@@ -131737,7 +131799,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131737
131799
|
...suffix
|
|
131738
131800
|
]);
|
|
131739
131801
|
await this.manager.repo.upsertDocMeta(getSessionRoomId(args2.existing.sessionId), {
|
|
131740
|
-
origin:
|
|
131802
|
+
origin: "external-acp",
|
|
131741
131803
|
lastMessageAt: resolveSourceUpdatedAtMs(args2.info, getServerNow()),
|
|
131742
131804
|
externalHistory: buildExternalHistoryMeta({
|
|
131743
131805
|
provider: this.provider,
|
|
@@ -131748,7 +131810,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131748
131810
|
});
|
|
131749
131811
|
const synced = await sessionDoc.waitUntilSynced();
|
|
131750
131812
|
if (!synced) {
|
|
131751
|
-
this.logger.debug(`[${this.
|
|
131813
|
+
this.logger.debug(`[${this.providerKey}-history-sync] Appended history for ${args2.existing.sessionId} did not confirm sync before unload; other clients may see the previous state until next sync.`);
|
|
131752
131814
|
}
|
|
131753
131815
|
await this.manager.cleanSessionDoc(args2.existing.sessionId, {
|
|
131754
131816
|
preserveStatus: true
|
|
@@ -131757,7 +131819,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131757
131819
|
}
|
|
131758
131820
|
async markConflict(sessionId, info, materialized, reason) {
|
|
131759
131821
|
await this.manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
|
|
131760
|
-
origin:
|
|
131822
|
+
origin: "external-acp",
|
|
131761
131823
|
externalHistory: buildExternalHistoryMeta({
|
|
131762
131824
|
provider: this.provider,
|
|
131763
131825
|
sourceAcpSessionId: info.sessionId,
|
|
@@ -132046,6 +132108,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132046
132108
|
static CONTEXT_WINDOW_USAGE_THROTTLE_MS = 400;
|
|
132047
132109
|
permissionRequestStartTimes = /* @__PURE__ */ new Map();
|
|
132048
132110
|
machineHeartbeatTimer = null;
|
|
132111
|
+
machineHeartbeatInFlightStartedAt = null;
|
|
132112
|
+
machineHeartbeatSeq = 0;
|
|
132049
132113
|
static MACHINE_HEARTBEAT_INTERVAL_MS = 2e4;
|
|
132050
132114
|
evictForMemoryPressureFn = async () => ({
|
|
132051
132115
|
availableMemoryBytes: 0,
|
|
@@ -132076,24 +132140,35 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132076
132140
|
startMachineHeartbeat() {
|
|
132077
132141
|
this.stopMachineHeartbeat();
|
|
132078
132142
|
this.logger.debug(`Machine heartbeat started (${Math.round(MessageHandler.MACHINE_HEARTBEAT_INTERVAL_MS / 1e3)}s interval)`);
|
|
132079
|
-
|
|
132080
|
-
this.logger.debug("Machine heartbeat sent (initial)");
|
|
132081
|
-
}).catch((error2) => {
|
|
132082
|
-
this.logger.debug(`Initial machine heartbeat failed: ${formatErrorMessage(error2)}`);
|
|
132083
|
-
});
|
|
132143
|
+
this.sendMachineHeartbeatWithDiagnostics("initial");
|
|
132084
132144
|
this.machineHeartbeatTimer = setInterval(() => {
|
|
132085
|
-
|
|
132086
|
-
this.logger.debug("Machine heartbeat sent");
|
|
132087
|
-
}).catch((error2) => {
|
|
132088
|
-
this.logger.debug(`Machine heartbeat failed: ${formatErrorMessage(error2)}`);
|
|
132089
|
-
});
|
|
132145
|
+
this.sendMachineHeartbeatWithDiagnostics("periodic");
|
|
132090
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
|
+
});
|
|
132091
132165
|
}
|
|
132092
132166
|
stopMachineHeartbeat() {
|
|
132093
132167
|
if (this.machineHeartbeatTimer) {
|
|
132094
132168
|
clearInterval(this.machineHeartbeatTimer);
|
|
132095
132169
|
this.machineHeartbeatTimer = null;
|
|
132096
132170
|
}
|
|
132171
|
+
this.machineHeartbeatInFlightStartedAt = null;
|
|
132097
132172
|
}
|
|
132098
132173
|
preferredBaseBranch = (process.env.LODY_BASE_BRANCH || "main").trim() || "main";
|
|
132099
132174
|
resolveGitHubProjectBranch(meta, preferredBranch) {
|
|
@@ -134545,8 +134620,15 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134545
134620
|
if (meta.processingUserMsgId) {
|
|
134546
134621
|
return true;
|
|
134547
134622
|
}
|
|
134623
|
+
if (meta.lastGoalCommand) {
|
|
134624
|
+
return true;
|
|
134625
|
+
}
|
|
134548
134626
|
return Boolean(meta.latestUserMsgId && meta.latestUserMsgId !== meta.lastHandledUserMsgId);
|
|
134549
134627
|
}
|
|
134628
|
+
async hasActiveGoal(sessionId) {
|
|
134629
|
+
const meta = (await this.workspaceDocument.repo.getDocMeta(getSessionRoomId(sessionId)))?.meta;
|
|
134630
|
+
return isSessionGoalWorking(meta?.latestGoal);
|
|
134631
|
+
}
|
|
134550
134632
|
isArchiveInFlight(sessionId) {
|
|
134551
134633
|
return this.archiveInFlight.has(sessionId);
|
|
134552
134634
|
}
|
|
@@ -134830,6 +134912,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134830
134912
|
}
|
|
134831
134913
|
queue;
|
|
134832
134914
|
isStopped = false;
|
|
134915
|
+
static QUEUE_WAIT_WARNING_MS = 1e4;
|
|
134916
|
+
static PROCESSING_WARNING_MS = 3e4;
|
|
134833
134917
|
enqueue(message, handler) {
|
|
134834
134918
|
if (this.isStopped) {
|
|
134835
134919
|
this.logger.debug("MessageProcessor is stopped, ignoring new message");
|
|
@@ -134837,11 +134921,30 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134837
134921
|
}
|
|
134838
134922
|
const sessionId = this.extractSessionId(message);
|
|
134839
134923
|
const queueKey = this.extractQueueKey(message);
|
|
134840
|
-
|
|
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}`);
|
|
134841
134934
|
void this.queue.enqueue(queueKey, async () => {
|
|
134842
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?.();
|
|
134843
134946
|
try {
|
|
134844
|
-
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}`);
|
|
134845
134948
|
await handler(message);
|
|
134846
134949
|
const duration2 = Date.now() - startTime;
|
|
134847
134950
|
this.logger.debug(`Processed message type=${message.type} sessionId=${sessionId || "N/A"} duration=${duration2}ms`);
|
|
@@ -134851,6 +134954,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134851
134954
|
const err2 = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
134852
134955
|
this.logger.error(`Failed to process message type=${message.type} sessionId=${sessionId || "N/A"} duration=${duration2}ms: ${err2.message}`);
|
|
134853
134956
|
this.emit("message:error", err2, message);
|
|
134957
|
+
} finally {
|
|
134958
|
+
processingCompleted = true;
|
|
134959
|
+
clearInterval(processingWarning);
|
|
134854
134960
|
}
|
|
134855
134961
|
});
|
|
134856
134962
|
}
|
|
@@ -135124,6 +135230,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135124
135230
|
if (this.deps.hasActiveTurn(sessionId)) {
|
|
135125
135231
|
return false;
|
|
135126
135232
|
}
|
|
135233
|
+
if (await this.deps.hasActiveGoal(sessionId)) {
|
|
135234
|
+
return false;
|
|
135235
|
+
}
|
|
135127
135236
|
if (this.deps.hasPendingUpdates(sessionId)) {
|
|
135128
135237
|
return false;
|
|
135129
135238
|
}
|
|
@@ -135212,6 +135321,8 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135212
135321
|
}
|
|
135213
135322
|
async dispatchLocalMessageForResponse(message) {
|
|
135214
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()}`);
|
|
135215
135326
|
return await new Promise((resolve2, reject) => {
|
|
135216
135327
|
this.messageProcessor.enqueue(message, async (nextMessage) => {
|
|
135217
135328
|
const responses = [];
|
|
@@ -135221,6 +135332,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135221
135332
|
return;
|
|
135222
135333
|
}
|
|
135223
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()}`);
|
|
135224
135336
|
resolve2([
|
|
135225
135337
|
...responses
|
|
135226
135338
|
]);
|
|
@@ -135230,6 +135342,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135230
135342
|
return;
|
|
135231
135343
|
}
|
|
135232
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)}`);
|
|
135233
135346
|
reject(error2);
|
|
135234
135347
|
};
|
|
135235
135348
|
const context2 = {
|
|
@@ -135263,6 +135376,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
135263
135376
|
this.gcManager = new SessionGCManager(gcConfig, {
|
|
135264
135377
|
getSessionLastActivity: (sessionId) => handler.getLastActivity(sessionId),
|
|
135265
135378
|
hasActiveTurn: (sessionId) => handler.hasActiveTurn(sessionId),
|
|
135379
|
+
hasActiveGoal: async (sessionId) => await handler.hasActiveGoal(sessionId),
|
|
135266
135380
|
hasPendingUpdates: (sessionId) => handler.hasPendingUpdates(sessionId),
|
|
135267
135381
|
hasPendingUserWork: async (sessionId) => await handler.hasPendingUserWork(sessionId),
|
|
135268
135382
|
isArchiveInFlight: (sessionId) => handler.isArchiveInFlight(sessionId),
|
|
@@ -142396,7 +142510,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142396
142510
|
await this.workspaceDocument.getOrCreateSessionDoc(config2.sessionId);
|
|
142397
142511
|
this.logger.debug(`[${config2.sessionId}] Session will enter create inner`);
|
|
142398
142512
|
const createInnerStart = performance$1.now();
|
|
142399
|
-
const session = await this.createSessionInner(config2);
|
|
142513
|
+
const session = await withSlowOperationWarning(this.createSessionInner(config2), this.logger, "session.createSessionInner", config2.sessionId);
|
|
142400
142514
|
session.ghTokenInjected = ghTokenInjected;
|
|
142401
142515
|
const createInnerDurationMs = performance$1.now() - createInnerStart;
|
|
142402
142516
|
this.logger.debug(`[${config2.sessionId}] Session create inner finished in ${Math.round(createInnerDurationMs)}ms`);
|
|
@@ -142417,7 +142531,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142417
142531
|
this.logger.debug(`[${sessionId}] About to call session.createAgent`);
|
|
142418
142532
|
try {
|
|
142419
142533
|
const createAgentStart = performance$1.now();
|
|
142420
|
-
acpSessionId = await session.createAgent({
|
|
142534
|
+
acpSessionId = await withSlowOperationWarning(session.createAgent({
|
|
142421
142535
|
cliType: config2.agentCliType,
|
|
142422
142536
|
agentType: config2.agentType,
|
|
142423
142537
|
command: setting.exec.command,
|
|
@@ -142477,7 +142591,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142477
142591
|
onCodexImageGenerationEnd: (event) => {
|
|
142478
142592
|
this.emit("onCodexImageGenerationEnd", sessionId, event);
|
|
142479
142593
|
}
|
|
142480
|
-
});
|
|
142594
|
+
}), this.logger, `session.createAgent(resume=${requestedResumeSessionId ? "yes" : "no"})`, sessionId);
|
|
142481
142595
|
const createAgentDurationMs = performance$1.now() - createAgentStart;
|
|
142482
142596
|
this.logger.debug(`[${sessionId}] Session createAgent finished in ${Math.round(createAgentDurationMs)}ms (acpSessionId=${acpSessionId})`);
|
|
142483
142597
|
this.logger.debug(`[${sessionId}] createAgent returned successfully`);
|
|
@@ -144598,7 +144712,8 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
144598
144712
|
const existing = rawExisting && typeof rawExisting === "object" ? rawExisting : {};
|
|
144599
144713
|
const previous = existing[entry2.localProjectId];
|
|
144600
144714
|
const nowMs = getServerNow();
|
|
144601
|
-
|
|
144715
|
+
const repo = runtime.lody.documentManager.repo;
|
|
144716
|
+
await repo.upsertDocMeta(machineRoomId, {
|
|
144602
144717
|
localProjects: {
|
|
144603
144718
|
...existing,
|
|
144604
144719
|
[entry2.localProjectId]: {
|
|
@@ -144624,7 +144739,8 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
144624
144739
|
...existing
|
|
144625
144740
|
};
|
|
144626
144741
|
delete next[localProjectId];
|
|
144627
|
-
|
|
144742
|
+
const repo = runtime.lody.documentManager.repo;
|
|
144743
|
+
await repo.upsertDocMeta(machineRoomId, {
|
|
144628
144744
|
localProjects: next
|
|
144629
144745
|
});
|
|
144630
144746
|
}
|
|
@@ -145118,6 +145234,23 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145118
145234
|
shutdown: shutdown2
|
|
145119
145235
|
};
|
|
145120
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
|
+
}
|
|
145121
145254
|
const ELECTRON_BOOTSTRAP_ENV = "LODY_ELECTRON_BOOTSTRAP";
|
|
145122
145255
|
const ELECTRON_SESSION_TOKEN_ENV = "LODY_ELECTRON_SESSION_TOKEN";
|
|
145123
145256
|
const ELECTRON_SESSION_USER_ID_ENV = "LODY_ELECTRON_SESSION_USER_ID";
|
|
@@ -145345,7 +145478,13 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145345
145478
|
runtimeStateReporter,
|
|
145346
145479
|
onFatalAuthFailure: (error2) => triggerFatalAuthShutdown?.(error2)
|
|
145347
145480
|
});
|
|
145348
|
-
|
|
145481
|
+
const eventLoopLagMonitor = startEventLoopLagMonitor(logger2, {
|
|
145482
|
+
label: "lody start"
|
|
145483
|
+
});
|
|
145484
|
+
registerProcessCleanup(async () => {
|
|
145485
|
+
eventLoopLagMonitor.stop();
|
|
145486
|
+
await fleet.shutdown();
|
|
145487
|
+
});
|
|
145349
145488
|
const shutdownSignals = process.platform === "win32" ? [
|
|
145350
145489
|
"SIGINT",
|
|
145351
145490
|
"SIGTERM",
|
|
@@ -145361,6 +145500,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145361
145500
|
logger: logger2,
|
|
145362
145501
|
shutdown: async () => {
|
|
145363
145502
|
unregisterProcessCleanup();
|
|
145503
|
+
eventLoopLagMonitor.stop();
|
|
145364
145504
|
await fleet.shutdown();
|
|
145365
145505
|
},
|
|
145366
145506
|
flushTelemetry: () => shutdownPostHog(),
|
|
@@ -145399,6 +145539,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
|
|
|
145399
145539
|
});
|
|
145400
145540
|
shutdownController.unregister();
|
|
145401
145541
|
unregisterProcessCleanup();
|
|
145542
|
+
eventLoopLagMonitor.stop();
|
|
145402
145543
|
await fleet.shutdown().catch((err2) => {
|
|
145403
145544
|
logger2.error(`Cleanup failed: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
145404
145545
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lody",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"node": ">=18.0.0"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"acp-extension-claude": "0.
|
|
23
|
+
"acp-extension-claude": "0.34.1",
|
|
24
24
|
"acp-extension-codex": "0.14.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@agentclientprotocol/sdk": "^0.
|
|
27
|
+
"@agentclientprotocol/sdk": "^0.21.0",
|
|
28
28
|
"@better-auth/api-key": "1.5.5",
|
|
29
29
|
"@convex-dev/better-auth": "0.11.2",
|
|
30
30
|
"@loro-dev/flock-wasm": "^0.3.3",
|
|
@@ -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",
|
|
79
78
|
"@lody/shared": "0.0.1",
|
|
80
|
-
"loro-code": "0.0.1"
|
|
79
|
+
"loro-code": "0.0.1",
|
|
80
|
+
"@lody/cli-supervisor": "0.0.1"
|
|
81
81
|
},
|
|
82
82
|
"files": [
|
|
83
83
|
"dist",
|