lody 0.50.2 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1385 -181
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import require$$3$5, { randomUUID,
|
|
2
|
+
import require$$3$5, { randomUUID, createHash as createHash$1, randomBytes } from "crypto";
|
|
3
3
|
import require$$0$5, { inspect as inspect$1, types as types$6 } from "util";
|
|
4
4
|
import require$$2$6 from "url";
|
|
5
5
|
import * as path$2 from "path";
|
|
@@ -56,6 +56,7 @@ import fsPromises, { stat, open } from "node:fs/promises";
|
|
|
56
56
|
import { fileURLToPath } from "node:url";
|
|
57
57
|
import require$$2$7 from "assert";
|
|
58
58
|
import { performance as performance$1 } from "perf_hooks";
|
|
59
|
+
import { createRequire as createRequire$1 } from "node:module";
|
|
59
60
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
60
61
|
import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
|
|
61
62
|
let program;
|
|
@@ -36821,7 +36822,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36821
36822
|
return client;
|
|
36822
36823
|
}
|
|
36823
36824
|
const name = "lody";
|
|
36824
|
-
const version$4 = "0.
|
|
36825
|
+
const version$4 = "0.51.0";
|
|
36825
36826
|
const description$1 = "Lody Agent CLI tool for managing remote command execution";
|
|
36826
36827
|
const type$2 = "module";
|
|
36827
36828
|
const main$3 = "dist/index.js";
|
|
@@ -36865,7 +36866,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36865
36866
|
};
|
|
36866
36867
|
const optionalDependencies = {
|
|
36867
36868
|
"acp-extension-claude": "0.31.1",
|
|
36868
|
-
"acp-extension-codex": "0.14.
|
|
36869
|
+
"acp-extension-codex": "0.14.3"
|
|
36869
36870
|
};
|
|
36870
36871
|
const devDependencies = {
|
|
36871
36872
|
"@agentclientprotocol/sdk": "catalog:",
|
|
@@ -66971,14 +66972,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
66971
66972
|
email: string$2(),
|
|
66972
66973
|
name: string$2().nullable().optional()
|
|
66973
66974
|
}).passthrough();
|
|
66974
|
-
function isRecord$
|
|
66975
|
+
function isRecord$6(value) {
|
|
66975
66976
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66976
66977
|
}
|
|
66977
66978
|
function asRecord(value) {
|
|
66978
|
-
return isRecord$
|
|
66979
|
+
return isRecord$6(value) ? value : null;
|
|
66979
66980
|
}
|
|
66980
66981
|
function readSessionUserFromResponse(response) {
|
|
66981
|
-
if (!isRecord$
|
|
66982
|
+
if (!isRecord$6(response)) {
|
|
66982
66983
|
return null;
|
|
66983
66984
|
}
|
|
66984
66985
|
if ("data" in response) {
|
|
@@ -78661,6 +78662,27 @@ Task description:
|
|
|
78661
78662
|
localProjectId: LocalProjectIdSchema,
|
|
78662
78663
|
branchName: string$2()
|
|
78663
78664
|
}).strict();
|
|
78665
|
+
const LocalProjectHistoryProviderSchema = _enum$1([
|
|
78666
|
+
"codex",
|
|
78667
|
+
"claude"
|
|
78668
|
+
]);
|
|
78669
|
+
const LocalProjectSyncHistoryRequestSchema = object$1({
|
|
78670
|
+
type: literal("local-project/sync-history"),
|
|
78671
|
+
machineId: MachineIdSchema,
|
|
78672
|
+
workspaceId: WorkspaceIdSchema,
|
|
78673
|
+
localProjectId: LocalProjectIdSchema,
|
|
78674
|
+
provider: LocalProjectHistoryProviderSchema,
|
|
78675
|
+
requestedByUserId: string$2().trim().min(1).optional()
|
|
78676
|
+
}).strict();
|
|
78677
|
+
const LocalProjectImportHistoryRequestSchema = object$1({
|
|
78678
|
+
type: literal("local-project/import-history"),
|
|
78679
|
+
machineId: MachineIdSchema,
|
|
78680
|
+
workspaceId: WorkspaceIdSchema,
|
|
78681
|
+
localProjectId: LocalProjectIdSchema,
|
|
78682
|
+
provider: LocalProjectHistoryProviderSchema,
|
|
78683
|
+
acpSessionIds: array$3(ACPSessionIdSchema),
|
|
78684
|
+
requestedByUserId: string$2().trim().min(1).optional()
|
|
78685
|
+
}).strict();
|
|
78664
78686
|
const WorktreeListFilesRequestSchema = object$1({
|
|
78665
78687
|
type: literal("worktree/list-files"),
|
|
78666
78688
|
machineId: MachineIdSchema,
|
|
@@ -78684,6 +78706,8 @@ Task description:
|
|
|
78684
78706
|
LocalProjectListFilesRequestSchema,
|
|
78685
78707
|
LocalProjectReadFileRequestSchema,
|
|
78686
78708
|
LocalProjectCheckoutBranchRequestSchema,
|
|
78709
|
+
LocalProjectSyncHistoryRequestSchema,
|
|
78710
|
+
LocalProjectImportHistoryRequestSchema,
|
|
78687
78711
|
WorktreeListFilesRequestSchema,
|
|
78688
78712
|
WorktreeReadFileRequestSchema
|
|
78689
78713
|
]);
|
|
@@ -78725,12 +78749,45 @@ Task description:
|
|
|
78725
78749
|
error: string$2()
|
|
78726
78750
|
}).strict()
|
|
78727
78751
|
]);
|
|
78752
|
+
const LocalProjectHistorySyncSummarySchema = object$1({
|
|
78753
|
+
listed: number$3().int().nonnegative(),
|
|
78754
|
+
imported: number$3().int().nonnegative(),
|
|
78755
|
+
refreshed: number$3().int().nonnegative(),
|
|
78756
|
+
skipped: number$3().int().nonnegative(),
|
|
78757
|
+
conflicted: number$3().int().nonnegative(),
|
|
78758
|
+
failed: number$3().int().nonnegative(),
|
|
78759
|
+
failures: array$3(object$1({
|
|
78760
|
+
acpSessionId: string$2(),
|
|
78761
|
+
message: string$2()
|
|
78762
|
+
}).strict())
|
|
78763
|
+
}).strict();
|
|
78764
|
+
const LocalProjectHistoryCatalogItemSchema = object$1({
|
|
78765
|
+
acpSessionId: string$2(),
|
|
78766
|
+
title: string$2(),
|
|
78767
|
+
updatedAt: string$2().optional(),
|
|
78768
|
+
importedSessionId: string$2().optional(),
|
|
78769
|
+
status: _enum$1([
|
|
78770
|
+
"available",
|
|
78771
|
+
"imported",
|
|
78772
|
+
"sync_conflict"
|
|
78773
|
+
]).optional()
|
|
78774
|
+
}).strict();
|
|
78775
|
+
const LocalProjectHistoryCatalogResultSchema = object$1({
|
|
78776
|
+
listed: number$3().int().nonnegative(),
|
|
78777
|
+
lastListedAt: number$3().int().nonnegative(),
|
|
78778
|
+
sessions: array$3(LocalProjectHistoryCatalogItemSchema)
|
|
78779
|
+
}).strict();
|
|
78780
|
+
const LocalProjectHistoryImportResultSchema = object$1({
|
|
78781
|
+
summary: LocalProjectHistorySyncSummarySchema,
|
|
78782
|
+
catalog: LocalProjectHistoryCatalogResultSchema
|
|
78783
|
+
}).strict();
|
|
78728
78784
|
const LocalProjectControlErrorCodeSchema = _enum$1([
|
|
78729
78785
|
"invalid_request",
|
|
78730
78786
|
"machine_mismatch",
|
|
78731
78787
|
"workspace_required",
|
|
78732
78788
|
"workspace_not_found",
|
|
78733
78789
|
"daemon_unavailable",
|
|
78790
|
+
"access_denied",
|
|
78734
78791
|
"local_project_not_found",
|
|
78735
78792
|
"path_invalid",
|
|
78736
78793
|
"execution_failed",
|
|
@@ -78746,6 +78803,8 @@ Task description:
|
|
|
78746
78803
|
"local-project/list-files",
|
|
78747
78804
|
"local-project/read-file",
|
|
78748
78805
|
"local-project/checkout-branch",
|
|
78806
|
+
"local-project/sync-history",
|
|
78807
|
+
"local-project/import-history",
|
|
78749
78808
|
"worktree/list-files",
|
|
78750
78809
|
"worktree/read-file"
|
|
78751
78810
|
]),
|
|
@@ -78809,6 +78868,16 @@ Task description:
|
|
|
78809
78868
|
type: literal("local-project/checkout-branch"),
|
|
78810
78869
|
result: LocalProjectCheckoutBranchResultSchema
|
|
78811
78870
|
}).strict(),
|
|
78871
|
+
object$1({
|
|
78872
|
+
ok: literal(true),
|
|
78873
|
+
type: literal("local-project/sync-history"),
|
|
78874
|
+
result: LocalProjectHistoryCatalogResultSchema
|
|
78875
|
+
}).strict(),
|
|
78876
|
+
object$1({
|
|
78877
|
+
ok: literal(true),
|
|
78878
|
+
type: literal("local-project/import-history"),
|
|
78879
|
+
result: LocalProjectHistoryImportResultSchema
|
|
78880
|
+
}).strict(),
|
|
78812
78881
|
object$1({
|
|
78813
78882
|
ok: literal(true),
|
|
78814
78883
|
type: literal("worktree/list-files"),
|
|
@@ -79127,7 +79196,7 @@ Task description:
|
|
|
79127
79196
|
"claude",
|
|
79128
79197
|
"codex"
|
|
79129
79198
|
]);
|
|
79130
|
-
function isRecord$
|
|
79199
|
+
function isRecord$5(value) {
|
|
79131
79200
|
return typeof value === "object" && value !== null;
|
|
79132
79201
|
}
|
|
79133
79202
|
function getTrimmedString(value) {
|
|
@@ -79144,7 +79213,7 @@ Task description:
|
|
|
79144
79213
|
return value === "builtin" || value === "registry";
|
|
79145
79214
|
}
|
|
79146
79215
|
function normalizeLegacyProjectRef(value) {
|
|
79147
|
-
if (!isRecord$
|
|
79216
|
+
if (!isRecord$5(value)) {
|
|
79148
79217
|
return value;
|
|
79149
79218
|
}
|
|
79150
79219
|
const kind = value.kind;
|
|
@@ -79160,13 +79229,13 @@ Task description:
|
|
|
79160
79229
|
normalized.branch = existingBranch;
|
|
79161
79230
|
} else {
|
|
79162
79231
|
const legacyBranchFromString = getTrimmedString(legacyProject);
|
|
79163
|
-
const legacyBranchFromObject = isRecord$
|
|
79232
|
+
const legacyBranchFromObject = isRecord$5(legacyProject) ? getTrimmedString(legacyProject.branch) ?? getTrimmedString(legacyProject.project) : void 0;
|
|
79164
79233
|
const resolvedBranch = legacyBranchFromString ?? legacyBranchFromObject;
|
|
79165
79234
|
if (resolvedBranch) {
|
|
79166
79235
|
normalized.branch = resolvedBranch;
|
|
79167
79236
|
}
|
|
79168
79237
|
}
|
|
79169
|
-
if (isRecord$
|
|
79238
|
+
if (isRecord$5(legacyProject)) {
|
|
79170
79239
|
if (kind === "github" && !getTrimmedString(normalized.repoFullName)) {
|
|
79171
79240
|
const repoFullName = getTrimmedString(legacyProject.repoFullName);
|
|
79172
79241
|
if (repoFullName) {
|
|
@@ -79203,7 +79272,7 @@ Task description:
|
|
|
79203
79272
|
};
|
|
79204
79273
|
normalized.project = normalizeLegacyProjectRef(normalized.project);
|
|
79205
79274
|
const currentProject = normalized.project;
|
|
79206
|
-
const projectRecord = isRecord$
|
|
79275
|
+
const projectRecord = isRecord$5(currentProject) ? currentProject : void 0;
|
|
79207
79276
|
const explicitBranch = getTrimmedString(normalized.branch) ?? getTrimmedString(currentProject) ?? (projectRecord ? getTrimmedString(projectRecord.branch) ?? getTrimmedString(projectRecord.project) : void 0);
|
|
79208
79277
|
const repoFullName = (projectRecord ? getTrimmedString(projectRecord.repoFullName) : void 0) ?? getTrimmedString(normalized.repoFullName) ?? getTrimmedString(normalized.githubRepo);
|
|
79209
79278
|
const localProjectId = (projectRecord ? getTrimmedString(projectRecord.localProjectId) : void 0) ?? getTrimmedString(normalized.localProjectId);
|
|
@@ -79246,7 +79315,7 @@ Task description:
|
|
|
79246
79315
|
return normalized;
|
|
79247
79316
|
}
|
|
79248
79317
|
function normalizeLegacyAcpSessionConfig(value) {
|
|
79249
|
-
if (!isRecord$
|
|
79318
|
+
if (!isRecord$5(value)) {
|
|
79250
79319
|
return value;
|
|
79251
79320
|
}
|
|
79252
79321
|
const normalized = {
|
|
@@ -79281,13 +79350,13 @@ Task description:
|
|
|
79281
79350
|
return normalized;
|
|
79282
79351
|
}
|
|
79283
79352
|
function normalizeLegacySessionMessage(parsed) {
|
|
79284
|
-
if (!isRecord$
|
|
79353
|
+
if (!isRecord$5(parsed)) {
|
|
79285
79354
|
return parsed;
|
|
79286
79355
|
}
|
|
79287
79356
|
const messageType = parsed.type;
|
|
79288
79357
|
if (messageType === "session/create" || messageType === "session/chat") {
|
|
79289
79358
|
const normalized = normalizeLegacySessionProject(parsed);
|
|
79290
|
-
if (!isRecord$
|
|
79359
|
+
if (!isRecord$5(normalized)) {
|
|
79291
79360
|
return normalized;
|
|
79292
79361
|
}
|
|
79293
79362
|
normalized.acpSessionConfig = normalizeLegacyAcpSessionConfig(normalized.acpSessionConfig);
|
|
@@ -81512,7 +81581,7 @@ Task description:
|
|
|
81512
81581
|
"read"
|
|
81513
81582
|
]);
|
|
81514
81583
|
const MAX_STORED_TERMINAL_OUTPUT_CHARS = 1024;
|
|
81515
|
-
const defaultCreateId = () => {
|
|
81584
|
+
const defaultCreateId$1 = () => {
|
|
81516
81585
|
const maybeCrypto = globalThis.crypto;
|
|
81517
81586
|
if (typeof maybeCrypto?.randomUUID === "function") {
|
|
81518
81587
|
return maybeCrypto.randomUUID();
|
|
@@ -82193,7 +82262,7 @@ Task description:
|
|
|
82193
82262
|
constructor(history, options, model) {
|
|
82194
82263
|
this.model = model;
|
|
82195
82264
|
this.history = history;
|
|
82196
|
-
this.createId = options.createId ?? defaultCreateId;
|
|
82265
|
+
this.createId = options.createId ?? defaultCreateId$1;
|
|
82197
82266
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
82198
82267
|
this.parsedItemsByEntryIndex = Array.from({
|
|
82199
82268
|
length: history.length
|
|
@@ -82445,28 +82514,149 @@ Task description:
|
|
|
82445
82514
|
const applyNotificationOnHistory = (history, notifications, model, options = {}) => {
|
|
82446
82515
|
return new NotificationOnHistoryApplier(history, options, model).apply(notifications);
|
|
82447
82516
|
};
|
|
82448
|
-
const
|
|
82517
|
+
const defaultNow = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
82518
|
+
const defaultCreateId = () => {
|
|
82519
|
+
const maybeCrypto = globalThis.crypto;
|
|
82520
|
+
if (typeof maybeCrypto?.randomUUID === "function") {
|
|
82521
|
+
return maybeCrypto.randomUUID();
|
|
82522
|
+
}
|
|
82523
|
+
return `history-replay-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
82524
|
+
};
|
|
82525
|
+
function textFromContentBlock(content) {
|
|
82526
|
+
if (!content || content.type !== "text") {
|
|
82527
|
+
return null;
|
|
82528
|
+
}
|
|
82529
|
+
return content.text;
|
|
82530
|
+
}
|
|
82531
|
+
function appendUserText(entry2, text) {
|
|
82532
|
+
const items2 = Array.isArray(entry2.items) ? [
|
|
82533
|
+
...entry2.items
|
|
82534
|
+
] : [];
|
|
82535
|
+
const last2 = items2[items2.length - 1];
|
|
82536
|
+
if (last2?.type === "text") {
|
|
82537
|
+
items2[items2.length - 1] = {
|
|
82538
|
+
...last2,
|
|
82539
|
+
text: `${last2.text}${text}`
|
|
82540
|
+
};
|
|
82541
|
+
} else {
|
|
82542
|
+
items2.push({
|
|
82543
|
+
type: "text",
|
|
82544
|
+
text
|
|
82545
|
+
});
|
|
82546
|
+
}
|
|
82547
|
+
return {
|
|
82548
|
+
...entry2,
|
|
82549
|
+
items: items2,
|
|
82550
|
+
inputConfig: entry2.inputConfig ? {
|
|
82551
|
+
...entry2.inputConfig,
|
|
82552
|
+
prompt: `${entry2.inputConfig.prompt ?? ""}${text}`
|
|
82553
|
+
} : entry2.inputConfig
|
|
82554
|
+
};
|
|
82555
|
+
}
|
|
82556
|
+
function createUserEntry(args2) {
|
|
82557
|
+
const inputConfig = {
|
|
82558
|
+
prompt: args2.text,
|
|
82559
|
+
cliType: "builtin",
|
|
82560
|
+
agentType: args2.agentType,
|
|
82561
|
+
...{}
|
|
82562
|
+
};
|
|
82563
|
+
return {
|
|
82564
|
+
id: args2.id,
|
|
82565
|
+
role: "user",
|
|
82566
|
+
items: [
|
|
82567
|
+
{
|
|
82568
|
+
type: "text",
|
|
82569
|
+
text: args2.text
|
|
82570
|
+
}
|
|
82571
|
+
],
|
|
82572
|
+
timestamp: args2.timestamp,
|
|
82573
|
+
status: args2.mode === "resumable" ? "seen" : "handled",
|
|
82574
|
+
read: true,
|
|
82575
|
+
userId: args2.userId,
|
|
82576
|
+
finished: true,
|
|
82577
|
+
fileDiff: [],
|
|
82578
|
+
inputConfig
|
|
82579
|
+
};
|
|
82580
|
+
}
|
|
82581
|
+
function buildHistoryReplayImport(notifications, options) {
|
|
82582
|
+
const now2 = options.now ?? defaultNow;
|
|
82583
|
+
const createId = options.createId ?? defaultCreateId;
|
|
82584
|
+
const mode2 = options.mode;
|
|
82585
|
+
const agentType = options.agentType ?? "codex";
|
|
82586
|
+
let history = [];
|
|
82587
|
+
let lastWasUserChunk = false;
|
|
82588
|
+
let droppedNotifications = 0;
|
|
82589
|
+
for (const notification of notifications) {
|
|
82590
|
+
if (notification.update.sessionUpdate === "user_message_chunk") {
|
|
82591
|
+
const text = textFromContentBlock(notification.update.content);
|
|
82592
|
+
if (text === null) {
|
|
82593
|
+
droppedNotifications += 1;
|
|
82594
|
+
lastWasUserChunk = false;
|
|
82595
|
+
continue;
|
|
82596
|
+
}
|
|
82597
|
+
const lastIndex = history.length - 1;
|
|
82598
|
+
const last2 = lastIndex >= 0 ? history[lastIndex] : void 0;
|
|
82599
|
+
if (lastWasUserChunk && last2?.role === "user") {
|
|
82600
|
+
history[lastIndex] = appendUserText(last2, text);
|
|
82601
|
+
} else {
|
|
82602
|
+
history.push(createUserEntry({
|
|
82603
|
+
id: createId(),
|
|
82604
|
+
text,
|
|
82605
|
+
timestamp: now2(),
|
|
82606
|
+
userId: options.userId,
|
|
82607
|
+
agentType,
|
|
82608
|
+
mode: mode2
|
|
82609
|
+
}));
|
|
82610
|
+
}
|
|
82611
|
+
lastWasUserChunk = true;
|
|
82612
|
+
continue;
|
|
82613
|
+
}
|
|
82614
|
+
const beforeLength = history.length;
|
|
82615
|
+
const beforeSnapshot = JSON.stringify(history);
|
|
82616
|
+
history = applyNotificationOnHistory(history, [
|
|
82617
|
+
notification
|
|
82618
|
+
], void 0, {
|
|
82619
|
+
createId,
|
|
82620
|
+
now: now2
|
|
82621
|
+
});
|
|
82622
|
+
if (history.length === beforeLength && JSON.stringify(history) === beforeSnapshot) {
|
|
82623
|
+
const updateType = notification.update.sessionUpdate;
|
|
82624
|
+
if (updateType !== "session_info_update" && updateType !== "current_mode_update" && updateType !== "config_option_update" && updateType !== "usage_update" && updateType !== "available_commands_update") {
|
|
82625
|
+
droppedNotifications += 1;
|
|
82626
|
+
}
|
|
82627
|
+
}
|
|
82628
|
+
lastWasUserChunk = false;
|
|
82629
|
+
}
|
|
82630
|
+
return {
|
|
82631
|
+
history: history.map((entry2) => entry2.role === "assistant" ? {
|
|
82632
|
+
...entry2,
|
|
82633
|
+
finished: entry2.finished ?? true
|
|
82634
|
+
} : entry2),
|
|
82635
|
+
droppedNotifications
|
|
82636
|
+
};
|
|
82637
|
+
}
|
|
82638
|
+
const isRecord$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82449
82639
|
const getClaudeCodeMeta = (meta) => {
|
|
82450
|
-
if (!isRecord$
|
|
82640
|
+
if (!isRecord$4(meta)) return null;
|
|
82451
82641
|
const claudeCode = meta.claudeCode;
|
|
82452
|
-
return isRecord$
|
|
82642
|
+
return isRecord$4(claudeCode) ? claudeCode : null;
|
|
82453
82643
|
};
|
|
82454
82644
|
function parseAskUserQuestionPermissionMeta(meta) {
|
|
82455
82645
|
const claudeCode = getClaudeCodeMeta(meta);
|
|
82456
82646
|
if (!claudeCode) return null;
|
|
82457
82647
|
const raw = claudeCode.askUserQuestion;
|
|
82458
|
-
if (!isRecord$
|
|
82648
|
+
if (!isRecord$4(raw)) return null;
|
|
82459
82649
|
const rawQuestions = raw.questions;
|
|
82460
82650
|
if (!Array.isArray(rawQuestions) || rawQuestions.length === 0) return null;
|
|
82461
82651
|
const questions = [];
|
|
82462
82652
|
for (const rawQuestion of rawQuestions) {
|
|
82463
|
-
if (!isRecord$
|
|
82653
|
+
if (!isRecord$4(rawQuestion)) return null;
|
|
82464
82654
|
if (typeof rawQuestion.question !== "string") return null;
|
|
82465
82655
|
if (typeof rawQuestion.header !== "string") return null;
|
|
82466
82656
|
if (!Array.isArray(rawQuestion.options)) return null;
|
|
82467
82657
|
const options = [];
|
|
82468
82658
|
for (const rawOption of rawQuestion.options) {
|
|
82469
|
-
if (!isRecord$
|
|
82659
|
+
if (!isRecord$4(rawOption)) return null;
|
|
82470
82660
|
if (typeof rawOption.label !== "string") return null;
|
|
82471
82661
|
options.push({
|
|
82472
82662
|
label: rawOption.label,
|
|
@@ -85171,7 +85361,7 @@ ${tailedOutput}` : null;
|
|
|
85171
85361
|
];
|
|
85172
85362
|
const buildPreviewTunnelRefreshPath = (tunnelId) => `${PREVIEW_TUNNELS_API_PATH}/${encodeURIComponent(tunnelId)}/refresh`;
|
|
85173
85363
|
const buildPreviewTunnelRevokePath = (tunnelId) => `${PREVIEW_TUNNELS_API_PATH}/${encodeURIComponent(tunnelId)}/revoke`;
|
|
85174
|
-
const isRecord$
|
|
85364
|
+
const isRecord$3 = (value) => typeof value === "object" && value !== null;
|
|
85175
85365
|
const isString$2 = (value) => typeof value === "string";
|
|
85176
85366
|
const isOptionalNumber = (value) => value === void 0 || typeof value === "number";
|
|
85177
85367
|
const isOptionalBoolean = (value) => value === void 0 || typeof value === "boolean";
|
|
@@ -85179,11 +85369,11 @@ ${tailedOutput}` : null;
|
|
|
85179
85369
|
const isStringArray$1 = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
85180
85370
|
const isHeaderEntries = (value) => Array.isArray(value) && value.every((entry2) => Array.isArray(entry2) && entry2.length === 2 && typeof entry2[0] === "string" && typeof entry2[1] === "string");
|
|
85181
85371
|
const isPreviewTunnelBinaryPayloadStream = (value) => value === "request-body" || value === "response-body" || value === "websocket-frame";
|
|
85182
|
-
const isPreviewResourceLimits = (value) => isRecord$
|
|
85372
|
+
const isPreviewResourceLimits = (value) => isRecord$3(value) && isPositiveInteger(value.maxRequestBodyBytes) && isPositiveInteger(value.maxResponseBodyBytes) && isPositiveInteger(value.maxRequestDurationMs);
|
|
85183
85373
|
const parseJsonRecord = (raw) => {
|
|
85184
85374
|
try {
|
|
85185
85375
|
const parsed = JSON.parse(raw);
|
|
85186
|
-
return isRecord$
|
|
85376
|
+
return isRecord$3(parsed) ? parsed : null;
|
|
85187
85377
|
} catch {
|
|
85188
85378
|
return null;
|
|
85189
85379
|
}
|
|
@@ -85222,8 +85412,8 @@ ${tailedOutput}` : null;
|
|
|
85222
85412
|
const parsed = parseJsonRecord(raw);
|
|
85223
85413
|
return parsed && isPreviewTunnelServerMessage(parsed) ? parsed : null;
|
|
85224
85414
|
};
|
|
85225
|
-
const isPreviewTunnelCreateResponse = (value) => isRecord$
|
|
85226
|
-
const isPreviewTunnelRefreshResponse = (value) => isRecord$
|
|
85415
|
+
const isPreviewTunnelCreateResponse = (value) => isRecord$3(value) && isString$2(value.tunnelId) && isString$2(value.publicUrl) && isString$2(value.websocketUrl) && isString$2(value.sessionToken) && typeof value.expiresAt === "number" && (value.resourceLimits === void 0 || isPreviewResourceLimits(value.resourceLimits));
|
|
85416
|
+
const isPreviewTunnelRefreshResponse = (value) => isRecord$3(value) && isString$2(value.websocketUrl) && isString$2(value.sessionToken) && typeof value.expiresAt === "number";
|
|
85227
85417
|
const LOCAL_PROBE_PORT$1 = 17789;
|
|
85228
85418
|
const LOCAL_SESSION_CONTROL_PORT = 17790;
|
|
85229
85419
|
const IMAGE_UPLOAD_PATH = "/image-upload";
|
|
@@ -90150,18 +90340,18 @@ ${val.stack}`;
|
|
|
90150
90340
|
}
|
|
90151
90341
|
return next;
|
|
90152
90342
|
}
|
|
90153
|
-
function isRecord$
|
|
90343
|
+
function isRecord$2(value) {
|
|
90154
90344
|
return typeof value === "object" && value !== null;
|
|
90155
90345
|
}
|
|
90156
90346
|
function normalizeRecoveryReport(raw) {
|
|
90157
|
-
if (!isRecord$
|
|
90347
|
+
if (!isRecord$2(raw) || !Array.isArray(raw.skipped)) {
|
|
90158
90348
|
return {
|
|
90159
90349
|
skipped: []
|
|
90160
90350
|
};
|
|
90161
90351
|
}
|
|
90162
90352
|
return {
|
|
90163
90353
|
skipped: raw.skipped.flatMap((entry2) => {
|
|
90164
|
-
if (!isRecord$
|
|
90354
|
+
if (!isRecord$2(entry2)) {
|
|
90165
90355
|
return [];
|
|
90166
90356
|
}
|
|
90167
90357
|
const key2 = Array.isArray(entry2.key) ? cloneJson(entry2.key) : void 0;
|
|
@@ -99013,7 +99203,7 @@ stream:${scope2.streamId}`;
|
|
|
99013
99203
|
}
|
|
99014
99204
|
return parsed;
|
|
99015
99205
|
};
|
|
99016
|
-
const withTimeout$
|
|
99206
|
+
const withTimeout$3 = async (promise, timeoutMs, message) => {
|
|
99017
99207
|
if (timeoutMs <= 0) {
|
|
99018
99208
|
return promise;
|
|
99019
99209
|
}
|
|
@@ -105853,7 +106043,7 @@ stream:${scope2.streamId}`;
|
|
|
105853
106043
|
const timeoutMs = options.timeoutMs ?? readTimeoutEnv("LODY_LORO_WAIT_CODE_SESSION_SYNC_TIMEOUT_MS", readTimeoutEnv("LODY_LORO_WAIT_DOC_SYNC_TIMEOUT_MS", 4e3));
|
|
105854
106044
|
const timeoutMessage = `Timeout waiting for code session pending writes (session=${this.sessionId})`;
|
|
105855
106045
|
try {
|
|
105856
|
-
await withTimeout$
|
|
106046
|
+
await withTimeout$3(this.subscription.waitUntilSynced(), timeoutMs, timeoutMessage);
|
|
105857
106047
|
return true;
|
|
105858
106048
|
} catch {
|
|
105859
106049
|
return false;
|
|
@@ -115733,7 +115923,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
115733
115923
|
this.logger.debug(`[${this.workspaceId}] Triggering Loro streams reconnect (reason=${reason}, transport=${this.transportStatus}, metaRoom=${this.metaRoomStatus ?? "unknown"})`);
|
|
115734
115924
|
}
|
|
115735
115925
|
await this.ensureMetaRoomJoined(reason);
|
|
115736
|
-
await withTimeout$
|
|
115926
|
+
await withTimeout$3(this.repo.reconnect({
|
|
115737
115927
|
resetBackoff: true,
|
|
115738
115928
|
timeout: timeoutMs
|
|
115739
115929
|
}), timeoutMs, `Timeout waiting for Loro streams reconnect (workspace=${this.workspaceId})`);
|
|
@@ -115755,7 +115945,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
115755
115945
|
const joinMetaTimeoutMs = readTimeoutEnv("LODY_LORO_JOIN_META_TIMEOUT_MS", 3e4);
|
|
115756
115946
|
const startedAt = Date.now();
|
|
115757
115947
|
this.logger.debug(`[${this.workspaceId}] Joining Loro meta room (reason=${reason})`);
|
|
115758
|
-
const metaSub = await withTimeout$
|
|
115948
|
+
const metaSub = await withTimeout$3(this.repo.joinMetaRoom(), joinMetaTimeoutMs, `Timeout waiting for repo.joinMetaRoom during reconnect (workspace=${this.workspaceId})`);
|
|
115759
115949
|
this.attachMetaRoomStatusLogger(metaSub);
|
|
115760
115950
|
this.logger.debug(`[${this.workspaceId}] Meta room join returned in ${Date.now() - startedAt}ms (reason=${reason})`);
|
|
115761
115951
|
}
|
|
@@ -115767,7 +115957,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
115767
115957
|
const startedAt = Date.now();
|
|
115768
115958
|
try {
|
|
115769
115959
|
const syncPromise = this.initialMetaSyncCompleted ? metaSub.waitUntilSynced() : metaSub.firstSyncedWithRemote;
|
|
115770
|
-
await withTimeout$
|
|
115960
|
+
await withTimeout$3(syncPromise, syncMetaTimeoutMs, `Timeout waiting for Loro meta room sync (workspace=${this.workspaceId})`);
|
|
115771
115961
|
if (this.metaSub !== metaSub || this.isCleanedUp) {
|
|
115772
115962
|
return;
|
|
115773
115963
|
}
|
|
@@ -116689,7 +116879,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116689
116879
|
});
|
|
116690
116880
|
try {
|
|
116691
116881
|
const createRepoStartMs = Date.now();
|
|
116692
|
-
repo = await withTimeout$
|
|
116882
|
+
repo = await withTimeout$3(LoroRepo.create({
|
|
116693
116883
|
storageAdapter: new FileSystemStorageAdaptor({
|
|
116694
116884
|
baseDir: storageBaseDir
|
|
116695
116885
|
}),
|
|
@@ -116707,7 +116897,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116707
116897
|
}
|
|
116708
116898
|
try {
|
|
116709
116899
|
const joinMetaStartMs = Date.now();
|
|
116710
|
-
metaSub = await withTimeout$
|
|
116900
|
+
metaSub = await withTimeout$3(repo.joinMetaRoom(), joinMetaTimeoutMs, "Timeout waiting for repo.joinMetaRoom");
|
|
116711
116901
|
logger2.debug(`[${workspaceId}] Meta room join returned in ${Date.now() - joinMetaStartMs}ms`);
|
|
116712
116902
|
} catch (error2) {
|
|
116713
116903
|
logger2.debug(`[${workspaceId}] Failed to join Loro meta room; continuing without initial meta join: ${formatErrorMessage(error2)}`);
|
|
@@ -116724,7 +116914,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116724
116914
|
});
|
|
116725
116915
|
try {
|
|
116726
116916
|
const syncStartMs = Date.now();
|
|
116727
|
-
await withTimeout$
|
|
116917
|
+
await withTimeout$3(metaSub.firstSyncedWithRemote, syncMetaTimeoutMs, initialMetaSyncTimeoutMessage);
|
|
116728
116918
|
initialMetaSyncCompleted = true;
|
|
116729
116919
|
logger2.debug(`[${workspaceId}] Meta room synced in ${Date.now() - syncStartMs}ms`);
|
|
116730
116920
|
} catch (error2) {
|
|
@@ -116772,7 +116962,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116772
116962
|
}
|
|
116773
116963
|
const timeoutMessage = `Timeout waiting for initial meta sync (workspace=${this.workspaceId})`;
|
|
116774
116964
|
try {
|
|
116775
|
-
return await withTimeout$
|
|
116965
|
+
return await withTimeout$3(this.initialMetaSyncPromise, timeoutMs, timeoutMessage);
|
|
116776
116966
|
} catch (error2) {
|
|
116777
116967
|
if (error2 instanceof Error && error2.message === timeoutMessage) {
|
|
116778
116968
|
return false;
|
|
@@ -116795,7 +116985,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116795
116985
|
const destroyTimeoutMs = readTimeoutEnv("LODY_LORO_DESTROY_TIMEOUT_MS", 1e3);
|
|
116796
116986
|
const timeoutMessage = `Timeout waiting for repo.destroy (workspace=${this.workspaceId})`;
|
|
116797
116987
|
try {
|
|
116798
|
-
await withTimeout$
|
|
116988
|
+
await withTimeout$3(repoDestroyPromise, destroyTimeoutMs, timeoutMessage);
|
|
116799
116989
|
} catch (error2) {
|
|
116800
116990
|
if (!(error2 instanceof Error) || error2.message !== timeoutMessage) {
|
|
116801
116991
|
throw error2;
|
|
@@ -117271,7 +117461,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
117271
117461
|
this.docSub = joinedSub;
|
|
117272
117462
|
const syncDocTimeoutMs = readTimeoutEnv("LODY_LORO_SYNC_DOC_TIMEOUT_MS", 8e3);
|
|
117273
117463
|
const timeoutMessage = `Timeout waiting for session doc initial sync (room=${this.roomId})`;
|
|
117274
|
-
await withTimeout$
|
|
117464
|
+
await withTimeout$3(joinedSub.firstSyncedWithRemote, syncDocTimeoutMs, timeoutMessage);
|
|
117275
117465
|
return;
|
|
117276
117466
|
} catch (error2) {
|
|
117277
117467
|
const errMsg = formatErrorMessage(error2);
|
|
@@ -117333,7 +117523,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
117333
117523
|
const timeoutMs = options.timeoutMs ?? readTimeoutEnv("LODY_LORO_WAIT_DOC_SYNC_TIMEOUT_MS", 4e3);
|
|
117334
117524
|
const timeoutMessage = `Timeout waiting for session doc pending writes (room=${this.roomId})`;
|
|
117335
117525
|
try {
|
|
117336
|
-
await withTimeout$
|
|
117526
|
+
await withTimeout$3(sub.waitUntilSynced(), timeoutMs, timeoutMessage);
|
|
117337
117527
|
return true;
|
|
117338
117528
|
} catch (error2) {
|
|
117339
117529
|
this.logger.debug(`[${this.sessionId}] Session doc pending writes were not confirmed before continuing: ${formatErrorMessage(error2)}`);
|
|
@@ -119481,7 +119671,8 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
119481
119671
|
"machine/status",
|
|
119482
119672
|
"machine/acp-capabilities-refresh",
|
|
119483
119673
|
"session/preview-create",
|
|
119484
|
-
"session/preview-revoke"
|
|
119674
|
+
"session/preview-revoke",
|
|
119675
|
+
"local-project/control"
|
|
119485
119676
|
]);
|
|
119486
119677
|
const LoroStreamsRpcErrorSchema = object$1({
|
|
119487
119678
|
code: string$2().trim().min(1),
|
|
@@ -119527,11 +119718,18 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
119527
119718
|
reason: string$2().trim().min(1).optional()
|
|
119528
119719
|
}).strict()
|
|
119529
119720
|
}).strict();
|
|
119721
|
+
const LoroLocalProjectControlRpcRequestSchema = BaseRpcRequestSchema.extend({
|
|
119722
|
+
method: literal("local-project/control"),
|
|
119723
|
+
params: object$1({
|
|
119724
|
+
request: LocalProjectControlRequestSchema
|
|
119725
|
+
}).strict()
|
|
119726
|
+
}).strict();
|
|
119530
119727
|
const LoroStreamsRpcRequestSchema = discriminatedUnion("method", [
|
|
119531
119728
|
LoroMachineStatusRpcRequestSchema,
|
|
119532
119729
|
LoroMachineAcpCapabilitiesRefreshRpcRequestSchema,
|
|
119533
119730
|
LoroSessionPreviewCreateRpcRequestSchema,
|
|
119534
|
-
LoroSessionPreviewRevokeRpcRequestSchema
|
|
119731
|
+
LoroSessionPreviewRevokeRpcRequestSchema,
|
|
119732
|
+
LoroLocalProjectControlRpcRequestSchema
|
|
119535
119733
|
]);
|
|
119536
119734
|
object$1({
|
|
119537
119735
|
jsonrpc: literal(JSON_RPC_VERSION$1),
|
|
@@ -119870,6 +120068,18 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
119870
120068
|
await this.appendResultResponse(request.replyTo, request.id, request.method, response);
|
|
119871
120069
|
return;
|
|
119872
120070
|
}
|
|
120071
|
+
case "local-project/control": {
|
|
120072
|
+
if (!this.deps.dispatchLocalProjectControl) {
|
|
120073
|
+
await this.appendErrorResponse(request.replyTo, request.id, request.method, {
|
|
120074
|
+
code: "method_unavailable",
|
|
120075
|
+
message: "Local project control is not available on this machine."
|
|
120076
|
+
});
|
|
120077
|
+
return;
|
|
120078
|
+
}
|
|
120079
|
+
const response = await this.deps.dispatchLocalProjectControl(request.params.request);
|
|
120080
|
+
await this.appendResultResponse(request.replyTo, request.id, request.method, response);
|
|
120081
|
+
return;
|
|
120082
|
+
}
|
|
119873
120083
|
}
|
|
119874
120084
|
} catch (error2) {
|
|
119875
120085
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -121455,7 +121665,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121455
121665
|
this.name = "AcpTimeoutError";
|
|
121456
121666
|
}
|
|
121457
121667
|
}
|
|
121458
|
-
function withTimeout$
|
|
121668
|
+
function withTimeout$2(promise, logger2, operationName, sessionId, timeoutMs, warningIntervalMs = 1e4) {
|
|
121459
121669
|
let completed = false;
|
|
121460
121670
|
let elapsedMs = 0;
|
|
121461
121671
|
let timeoutHandle;
|
|
@@ -121849,7 +122059,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121849
122059
|
type: "initialize_start"
|
|
121850
122060
|
});
|
|
121851
122061
|
const ACP_INIT_TIMEOUT_MS = Math.max(0, timeoutOptions.initTimeoutMs ?? 12e4);
|
|
121852
|
-
const initResponse = await withTimeout$
|
|
122062
|
+
const initResponse = await withTimeout$2(withAbort(connection.initialize({
|
|
121853
122063
|
protocolVersion: PROTOCOL_VERSION,
|
|
121854
122064
|
clientCapabilities: {
|
|
121855
122065
|
terminal: this.terminalEnabled,
|
|
@@ -121957,7 +122167,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121957
122167
|
} else {
|
|
121958
122168
|
this.logger.debug(`[${this.options.sessionId}] Calling connection.newSession (cwd=${workdir})`);
|
|
121959
122169
|
const ACP_NEW_SESSION_TIMEOUT_MS = Math.max(0, timeoutOptions.newSessionTimeoutMs ?? 12e4);
|
|
121960
|
-
sessionResponse = await withTimeout$
|
|
122170
|
+
sessionResponse = await withTimeout$2(withAbort(connection.newSession({
|
|
121961
122171
|
cwd: workdir,
|
|
121962
122172
|
mcpServers
|
|
121963
122173
|
}), startupAbort), this.logger, "connection.newSession", this.options.sessionId, ACP_NEW_SESSION_TIMEOUT_MS);
|
|
@@ -122070,7 +122280,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122070
122280
|
return false;
|
|
122071
122281
|
}
|
|
122072
122282
|
this.logger.debug(`[${this.options.sessionId}] Closing ACP session (acpSessionId=${sessionId} timeoutMs=${timeoutMs})`);
|
|
122073
|
-
await withTimeout$
|
|
122283
|
+
await withTimeout$2(closeSession2.call(this.connection, {
|
|
122074
122284
|
sessionId
|
|
122075
122285
|
}), this.logger, "connection.closeSession", this.options.sessionId, timeoutMs, Math.min(timeoutMs, 1e3));
|
|
122076
122286
|
this.logger.debug(`[${this.options.sessionId}] ACP session close finished (acpSessionId=${sessionId})`);
|
|
@@ -122208,7 +122418,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122208
122418
|
},
|
|
122209
122419
|
codex: {
|
|
122210
122420
|
packageName: "acp-extension-codex",
|
|
122211
|
-
version: "0.14.
|
|
122421
|
+
version: "0.14.3",
|
|
122212
122422
|
binName: "acp-extension-codex",
|
|
122213
122423
|
args: [
|
|
122214
122424
|
"-c",
|
|
@@ -122228,12 +122438,50 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122228
122438
|
"claude",
|
|
122229
122439
|
"codex"
|
|
122230
122440
|
]);
|
|
122441
|
+
const moduleRequire = createRequire$1(import.meta.url);
|
|
122442
|
+
function isRecord$1(value) {
|
|
122443
|
+
return typeof value === "object" && value !== null;
|
|
122444
|
+
}
|
|
122445
|
+
function resolveInstalledPackageBin(setting) {
|
|
122446
|
+
try {
|
|
122447
|
+
const packageJsonPath = moduleRequire.resolve(`${setting.packageName}/package.json`);
|
|
122448
|
+
const packageJson = JSON.parse(readFileSync$1(packageJsonPath, "utf8"));
|
|
122449
|
+
if (!isRecord$1(packageJson) || packageJson.version !== setting.version) {
|
|
122450
|
+
return void 0;
|
|
122451
|
+
}
|
|
122452
|
+
const bin2 = packageJson.bin;
|
|
122453
|
+
const namedBin = isRecord$1(bin2) ? bin2[setting.binName] : void 0;
|
|
122454
|
+
const relativeBin = typeof bin2 === "string" ? bin2 : typeof namedBin === "string" ? namedBin : void 0;
|
|
122455
|
+
if (!relativeBin) {
|
|
122456
|
+
return void 0;
|
|
122457
|
+
}
|
|
122458
|
+
const binPath = resolve$2(dirname$1(packageJsonPath), relativeBin);
|
|
122459
|
+
return existsSync(binPath) ? binPath : void 0;
|
|
122460
|
+
} catch {
|
|
122461
|
+
return void 0;
|
|
122462
|
+
}
|
|
122463
|
+
}
|
|
122231
122464
|
function resolveBuiltinACPSetting(agentType) {
|
|
122232
122465
|
if (!builtinTypeSet.has(agentType)) {
|
|
122233
122466
|
throw new Error(`Unsupported builtin ACP type: ${agentType}`);
|
|
122234
122467
|
}
|
|
122235
122468
|
const builtinType = agentType;
|
|
122236
122469
|
const setting = BuiltinACPSetting[builtinType];
|
|
122470
|
+
if (builtinType === "claude") {
|
|
122471
|
+
const localClaudeAcpPath = resolveInstalledPackageBin(setting);
|
|
122472
|
+
if (localClaudeAcpPath) {
|
|
122473
|
+
return {
|
|
122474
|
+
status: {
|
|
122475
|
+
agent: `local ${setting.packageName}@${setting.version}`,
|
|
122476
|
+
command: localClaudeAcpPath
|
|
122477
|
+
},
|
|
122478
|
+
exec: {
|
|
122479
|
+
command: localClaudeAcpPath,
|
|
122480
|
+
args: setting.args ?? []
|
|
122481
|
+
}
|
|
122482
|
+
};
|
|
122483
|
+
}
|
|
122484
|
+
}
|
|
122237
122485
|
const agent = `${setting.packageName}@${setting.version}`;
|
|
122238
122486
|
const packageSpec = `${setting.packageName}@${setting.version}`;
|
|
122239
122487
|
const args2 = [
|
|
@@ -122270,11 +122518,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122270
122518
|
}
|
|
122271
122519
|
return `${agent.id}@${agent.version}`;
|
|
122272
122520
|
}
|
|
122273
|
-
function
|
|
122274
|
-
const agent = registryAgentsById[agentType];
|
|
122275
|
-
if (!agent) {
|
|
122276
|
-
throw new Error(`Unknown registry ACP type: ${agentType}`);
|
|
122277
|
-
}
|
|
122521
|
+
function resolveRegistryAgentACPSetting(agent) {
|
|
122278
122522
|
if (agent.distribution.local?.command) {
|
|
122279
122523
|
const isNpx = agent.distribution.local.command === "npx";
|
|
122280
122524
|
const args2 = [
|
|
@@ -122331,7 +122575,14 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122331
122575
|
}
|
|
122332
122576
|
};
|
|
122333
122577
|
}
|
|
122334
|
-
throw new Error(`Registry ACP ${
|
|
122578
|
+
throw new Error(`Registry ACP ${agent.id} has no supported launcher`);
|
|
122579
|
+
}
|
|
122580
|
+
function resolveRegistryACPSetting(agentType) {
|
|
122581
|
+
const agent = registryAgentsById[agentType];
|
|
122582
|
+
if (!agent) {
|
|
122583
|
+
throw new Error(`Unknown registry ACP type: ${agentType}`);
|
|
122584
|
+
}
|
|
122585
|
+
return resolveRegistryAgentACPSetting(agent);
|
|
122335
122586
|
}
|
|
122336
122587
|
function resolveACPSetting(input2) {
|
|
122337
122588
|
if (input2.cliType === "builtin") {
|
|
@@ -122542,7 +122793,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122542
122793
|
sessionResponse
|
|
122543
122794
|
};
|
|
122544
122795
|
};
|
|
122545
|
-
function waitForChildProcessExit(child, timeoutMs) {
|
|
122796
|
+
function waitForChildProcessExit$1(child, timeoutMs) {
|
|
122546
122797
|
if (child.exitCode !== null) {
|
|
122547
122798
|
return Promise.resolve(true);
|
|
122548
122799
|
}
|
|
@@ -122563,32 +122814,32 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122563
122814
|
child.once("exit", onExit2);
|
|
122564
122815
|
});
|
|
122565
122816
|
}
|
|
122566
|
-
function signalChildProcess(child, signal) {
|
|
122817
|
+
function signalChildProcess$1(child, signal) {
|
|
122567
122818
|
if (process.platform !== "win32" && typeof child.pid === "number" && child.pid > 0) {
|
|
122568
122819
|
process.kill(-child.pid, signal);
|
|
122569
122820
|
return;
|
|
122570
122821
|
}
|
|
122571
122822
|
child.kill(signal);
|
|
122572
122823
|
}
|
|
122573
|
-
async function terminateChildProcess(child, logger2, sessionLabel, exitTimeoutMs) {
|
|
122824
|
+
async function terminateChildProcess$1(child, logger2, sessionLabel, exitTimeoutMs) {
|
|
122574
122825
|
if (child.exitCode !== null) {
|
|
122575
122826
|
return;
|
|
122576
122827
|
}
|
|
122577
122828
|
try {
|
|
122578
|
-
signalChildProcess(child, "SIGTERM");
|
|
122829
|
+
signalChildProcess$1(child, "SIGTERM");
|
|
122579
122830
|
} catch {
|
|
122580
122831
|
return;
|
|
122581
122832
|
}
|
|
122582
|
-
if (await waitForChildProcessExit(child, exitTimeoutMs)) {
|
|
122833
|
+
if (await waitForChildProcessExit$1(child, exitTimeoutMs)) {
|
|
122583
122834
|
return;
|
|
122584
122835
|
}
|
|
122585
122836
|
logger2.debug(`[${sessionLabel}] ACP agent process did not exit within ${exitTimeoutMs}ms of SIGTERM; escalating to SIGKILL`);
|
|
122586
122837
|
try {
|
|
122587
|
-
signalChildProcess(child, "SIGKILL");
|
|
122838
|
+
signalChildProcess$1(child, "SIGKILL");
|
|
122588
122839
|
} catch {
|
|
122589
122840
|
return;
|
|
122590
122841
|
}
|
|
122591
|
-
await waitForChildProcessExit(child, exitTimeoutMs);
|
|
122842
|
+
await waitForChildProcessExit$1(child, exitTimeoutMs);
|
|
122592
122843
|
}
|
|
122593
122844
|
const spawnAcpProcess = (options) => {
|
|
122594
122845
|
const setting = resolveACPSetting({
|
|
@@ -122735,7 +122986,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122735
122986
|
sessionResponse: started.sessionResponse
|
|
122736
122987
|
};
|
|
122737
122988
|
} catch (error2) {
|
|
122738
|
-
await terminateChildProcess(agentProcess, options.logger, "acp-startup", 3e3);
|
|
122989
|
+
await terminateChildProcess$1(agentProcess, options.logger, "acp-startup", 3e3);
|
|
122739
122990
|
throw error2;
|
|
122740
122991
|
} finally {
|
|
122741
122992
|
startupMonitor.dispose();
|
|
@@ -122751,7 +123002,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122751
123002
|
options.logger.debug(`[${options.sessionLabel}] ACP session close failed during local agent shutdown: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
122752
123003
|
}
|
|
122753
123004
|
}
|
|
122754
|
-
await terminateChildProcess(options.agentProcess, options.logger, options.sessionLabel, exitTimeoutMs);
|
|
123005
|
+
await terminateChildProcess$1(options.agentProcess, options.logger, options.sessionLabel, exitTimeoutMs);
|
|
122755
123006
|
}
|
|
122756
123007
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
122757
123008
|
const getStringField = (obj, key2) => {
|
|
@@ -125761,6 +126012,154 @@ $mem | ConvertTo-Json -Compress
|
|
|
125761
126012
|
return null;
|
|
125762
126013
|
}
|
|
125763
126014
|
}
|
|
126015
|
+
function getImportedAcpSourceAcpSessionId(meta) {
|
|
126016
|
+
const externalHistory = meta.externalHistory;
|
|
126017
|
+
const provider2 = externalHistory?.provider;
|
|
126018
|
+
if (!externalHistory || provider2 !== "codex" && provider2 !== "claude") {
|
|
126019
|
+
return void 0;
|
|
126020
|
+
}
|
|
126021
|
+
return externalHistory.sourceAcpSessionId;
|
|
126022
|
+
}
|
|
126023
|
+
function isImportedAcpReplayUserTurn(entry2, meta) {
|
|
126024
|
+
const provider2 = meta.externalHistory?.provider;
|
|
126025
|
+
const sourceAcpSessionId = getImportedAcpSourceAcpSessionId(meta);
|
|
126026
|
+
return !!provider2 && !!sourceAcpSessionId && entry2.id.startsWith(`${provider2}:${sourceAcpSessionId}:turn:`);
|
|
126027
|
+
}
|
|
126028
|
+
function resolveResumableAcpSessionId(meta) {
|
|
126029
|
+
const acpSessionId = meta?.acpSessionId;
|
|
126030
|
+
if (!meta || !acpSessionId) {
|
|
126031
|
+
return void 0;
|
|
126032
|
+
}
|
|
126033
|
+
if (!meta.externalHistory) {
|
|
126034
|
+
return acpSessionId;
|
|
126035
|
+
}
|
|
126036
|
+
const sourceAcpSessionId = meta.externalHistory.sourceAcpSessionId;
|
|
126037
|
+
if (!sourceAcpSessionId) {
|
|
126038
|
+
return void 0;
|
|
126039
|
+
}
|
|
126040
|
+
return acpSessionId === sourceAcpSessionId ? void 0 : acpSessionId;
|
|
126041
|
+
}
|
|
126042
|
+
function resolveDispatchAcpSessionId(meta) {
|
|
126043
|
+
const liveSessionId = resolveResumableAcpSessionId(meta);
|
|
126044
|
+
if (liveSessionId || !meta?.externalHistory || meta.externalHistory.status === "sync_conflict") {
|
|
126045
|
+
return liveSessionId;
|
|
126046
|
+
}
|
|
126047
|
+
return meta.externalHistory.sourceAcpSessionId;
|
|
126048
|
+
}
|
|
126049
|
+
function resolveSessionDispatchAction(snapshot, machineId) {
|
|
126050
|
+
const { meta, history, hasActiveTurn, hasBlockingPendingCreate, hasReusableSession } = snapshot;
|
|
126051
|
+
if (!meta || meta.machineId !== machineId) {
|
|
126052
|
+
return {
|
|
126053
|
+
type: "noop",
|
|
126054
|
+
reason: "not-owned"
|
|
126055
|
+
};
|
|
126056
|
+
}
|
|
126057
|
+
if (meta.isArchived) {
|
|
126058
|
+
return {
|
|
126059
|
+
type: "noop",
|
|
126060
|
+
reason: "archived"
|
|
126061
|
+
};
|
|
126062
|
+
}
|
|
126063
|
+
if (hasBlockingPendingCreate) {
|
|
126064
|
+
return {
|
|
126065
|
+
type: "noop",
|
|
126066
|
+
reason: "pending-create"
|
|
126067
|
+
};
|
|
126068
|
+
}
|
|
126069
|
+
const statusType = meta.status?.type;
|
|
126070
|
+
if (statusType === "running" || statusType === "requestPermission" || statusType === "initializing") {
|
|
126071
|
+
if (hasActiveTurn) {
|
|
126072
|
+
return {
|
|
126073
|
+
type: "noop",
|
|
126074
|
+
reason: "active-session"
|
|
126075
|
+
};
|
|
126076
|
+
}
|
|
126077
|
+
return {
|
|
126078
|
+
type: "reset-stale-status",
|
|
126079
|
+
statusType
|
|
126080
|
+
};
|
|
126081
|
+
}
|
|
126082
|
+
const turn = findNextDispatchableUserTurn(history, meta);
|
|
126083
|
+
if (!turn) {
|
|
126084
|
+
return {
|
|
126085
|
+
type: "no-dispatchable-turn"
|
|
126086
|
+
};
|
|
126087
|
+
}
|
|
126088
|
+
const mode2 = hasReusableSession || resolveDispatchAcpSessionId(meta) ? "continue" : "create";
|
|
126089
|
+
return {
|
|
126090
|
+
type: "dispatch",
|
|
126091
|
+
mode: mode2,
|
|
126092
|
+
turn
|
|
126093
|
+
};
|
|
126094
|
+
}
|
|
126095
|
+
function resolveSessionCancelAction(meta, lastSeenCancelTurn, machineId) {
|
|
126096
|
+
if (!meta || meta.machineId !== machineId) {
|
|
126097
|
+
return {
|
|
126098
|
+
type: "noop",
|
|
126099
|
+
reason: "not-owned"
|
|
126100
|
+
};
|
|
126101
|
+
}
|
|
126102
|
+
if (meta.isArchived) {
|
|
126103
|
+
return {
|
|
126104
|
+
type: "noop",
|
|
126105
|
+
reason: "archived"
|
|
126106
|
+
};
|
|
126107
|
+
}
|
|
126108
|
+
const lastCanceledTurn = meta.lastCanceledTurn;
|
|
126109
|
+
if (typeof lastCanceledTurn !== "string" || !lastCanceledTurn) {
|
|
126110
|
+
return {
|
|
126111
|
+
type: "noop",
|
|
126112
|
+
reason: "no-cancel-turn"
|
|
126113
|
+
};
|
|
126114
|
+
}
|
|
126115
|
+
if (lastCanceledTurn === lastSeenCancelTurn) {
|
|
126116
|
+
return {
|
|
126117
|
+
type: "noop",
|
|
126118
|
+
reason: "already-seen"
|
|
126119
|
+
};
|
|
126120
|
+
}
|
|
126121
|
+
return {
|
|
126122
|
+
type: "cancel",
|
|
126123
|
+
turnId: lastCanceledTurn
|
|
126124
|
+
};
|
|
126125
|
+
}
|
|
126126
|
+
function findNextDispatchableUserTurn(history, meta) {
|
|
126127
|
+
for (const entry2 of history) {
|
|
126128
|
+
if (entry2.role !== "user") {
|
|
126129
|
+
continue;
|
|
126130
|
+
}
|
|
126131
|
+
if (isImportedAcpReplayUserTurn(entry2, meta)) {
|
|
126132
|
+
continue;
|
|
126133
|
+
}
|
|
126134
|
+
if (typeof entry2.status === "string") {
|
|
126135
|
+
if (entry2.status === "pending" || entry2.status === "seen" || entry2.status === "processing") {
|
|
126136
|
+
return entry2;
|
|
126137
|
+
}
|
|
126138
|
+
continue;
|
|
126139
|
+
}
|
|
126140
|
+
if (entry2.read === false) {
|
|
126141
|
+
return entry2;
|
|
126142
|
+
}
|
|
126143
|
+
if (entry2.id === meta.processingUserMsgId) {
|
|
126144
|
+
return entry2;
|
|
126145
|
+
}
|
|
126146
|
+
if (entry2.id === meta.latestUserMsgId && entry2.id !== meta.lastHandledUserMsgId) {
|
|
126147
|
+
return entry2;
|
|
126148
|
+
}
|
|
126149
|
+
}
|
|
126150
|
+
return null;
|
|
126151
|
+
}
|
|
126152
|
+
function resolveDispatchTurnInput(entry2) {
|
|
126153
|
+
const historyBlocks = historyItemsToInputBlocks(entry2.items);
|
|
126154
|
+
const configuredBlocks = normalizeSessionInputBlocks(entry2.inputConfig?.inputBlocks, "");
|
|
126155
|
+
const fallbackBlocks = normalizeSessionInputBlocks(void 0, entry2.inputConfig?.prompt ?? "");
|
|
126156
|
+
const inputBlocks = configuredBlocks.length > 0 ? configuredBlocks : historyBlocks.length > 0 ? historyBlocks : fallbackBlocks;
|
|
126157
|
+
const prompt2 = entry2.inputConfig?.prompt ?? extractPromptPreviewFromInputBlocks(inputBlocks.length > 0 ? inputBlocks : historyBlocks);
|
|
126158
|
+
return {
|
|
126159
|
+
inputBlocks,
|
|
126160
|
+
prompt: prompt2
|
|
126161
|
+
};
|
|
126162
|
+
}
|
|
125764
126163
|
class SessionTurnCancelled extends TaggedError("SessionTurnCancelled") {
|
|
125765
126164
|
}
|
|
125766
126165
|
class SessionTurnHalted extends TaggedError("SessionTurnHalted") {
|
|
@@ -126753,7 +127152,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
126753
127152
|
const agentConfigEnv = agentConfigEnvResolution.env ?? void 0;
|
|
126754
127153
|
self2.deps.logger.debug(`[${sessionId}] Resume env resolved (agentConfigId=${meta?.agentConfigId ?? "none"} reason=${agentConfigEnvResolution.reason} keys=${agentConfigEnv ? Object.keys(agentConfigEnv).length : 0})`);
|
|
126755
127154
|
const requestedResumeSessionId = acpSessionConfig.resume;
|
|
126756
|
-
const storedResumeSessionId = meta
|
|
127155
|
+
const storedResumeSessionId = resolveResumableAcpSessionId(meta);
|
|
126757
127156
|
const resumeSessionId = requestedResumeSessionId ?? storedResumeSessionId;
|
|
126758
127157
|
const resumeSource = requestedResumeSessionId ? "request" : storedResumeSessionId ? "meta" : "none";
|
|
126759
127158
|
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"})`);
|
|
@@ -127470,117 +127869,6 @@ $mem | ConvertTo-Json -Compress
|
|
|
127470
127869
|
};
|
|
127471
127870
|
}
|
|
127472
127871
|
}
|
|
127473
|
-
function resolveSessionDispatchAction(snapshot, machineId) {
|
|
127474
|
-
const { meta, history, hasActiveTurn, hasBlockingPendingCreate, hasReusableSession } = snapshot;
|
|
127475
|
-
if (!meta || meta.machineId !== machineId) {
|
|
127476
|
-
return {
|
|
127477
|
-
type: "noop",
|
|
127478
|
-
reason: "not-owned"
|
|
127479
|
-
};
|
|
127480
|
-
}
|
|
127481
|
-
if (meta.isArchived) {
|
|
127482
|
-
return {
|
|
127483
|
-
type: "noop",
|
|
127484
|
-
reason: "archived"
|
|
127485
|
-
};
|
|
127486
|
-
}
|
|
127487
|
-
if (hasBlockingPendingCreate) {
|
|
127488
|
-
return {
|
|
127489
|
-
type: "noop",
|
|
127490
|
-
reason: "pending-create"
|
|
127491
|
-
};
|
|
127492
|
-
}
|
|
127493
|
-
const statusType = meta.status?.type;
|
|
127494
|
-
if (statusType === "running" || statusType === "requestPermission" || statusType === "initializing") {
|
|
127495
|
-
if (hasActiveTurn) {
|
|
127496
|
-
return {
|
|
127497
|
-
type: "noop",
|
|
127498
|
-
reason: "active-session"
|
|
127499
|
-
};
|
|
127500
|
-
}
|
|
127501
|
-
return {
|
|
127502
|
-
type: "reset-stale-status",
|
|
127503
|
-
statusType
|
|
127504
|
-
};
|
|
127505
|
-
}
|
|
127506
|
-
const turn = findNextDispatchableUserTurn(history, meta);
|
|
127507
|
-
if (!turn) {
|
|
127508
|
-
return {
|
|
127509
|
-
type: "no-dispatchable-turn"
|
|
127510
|
-
};
|
|
127511
|
-
}
|
|
127512
|
-
const mode2 = hasReusableSession || meta.acpSessionId ? "continue" : "create";
|
|
127513
|
-
return {
|
|
127514
|
-
type: "dispatch",
|
|
127515
|
-
mode: mode2,
|
|
127516
|
-
turn
|
|
127517
|
-
};
|
|
127518
|
-
}
|
|
127519
|
-
function resolveSessionCancelAction(meta, lastSeenCancelTurn, machineId) {
|
|
127520
|
-
if (!meta || meta.machineId !== machineId) {
|
|
127521
|
-
return {
|
|
127522
|
-
type: "noop",
|
|
127523
|
-
reason: "not-owned"
|
|
127524
|
-
};
|
|
127525
|
-
}
|
|
127526
|
-
if (meta.isArchived) {
|
|
127527
|
-
return {
|
|
127528
|
-
type: "noop",
|
|
127529
|
-
reason: "archived"
|
|
127530
|
-
};
|
|
127531
|
-
}
|
|
127532
|
-
const lastCanceledTurn = meta.lastCanceledTurn;
|
|
127533
|
-
if (typeof lastCanceledTurn !== "string" || !lastCanceledTurn) {
|
|
127534
|
-
return {
|
|
127535
|
-
type: "noop",
|
|
127536
|
-
reason: "no-cancel-turn"
|
|
127537
|
-
};
|
|
127538
|
-
}
|
|
127539
|
-
if (lastCanceledTurn === lastSeenCancelTurn) {
|
|
127540
|
-
return {
|
|
127541
|
-
type: "noop",
|
|
127542
|
-
reason: "already-seen"
|
|
127543
|
-
};
|
|
127544
|
-
}
|
|
127545
|
-
return {
|
|
127546
|
-
type: "cancel",
|
|
127547
|
-
turnId: lastCanceledTurn
|
|
127548
|
-
};
|
|
127549
|
-
}
|
|
127550
|
-
function findNextDispatchableUserTurn(history, meta) {
|
|
127551
|
-
for (const entry2 of history) {
|
|
127552
|
-
if (entry2.role !== "user") {
|
|
127553
|
-
continue;
|
|
127554
|
-
}
|
|
127555
|
-
if (typeof entry2.status === "string") {
|
|
127556
|
-
if (entry2.status === "pending" || entry2.status === "seen" || entry2.status === "processing") {
|
|
127557
|
-
return entry2;
|
|
127558
|
-
}
|
|
127559
|
-
continue;
|
|
127560
|
-
}
|
|
127561
|
-
if (entry2.read === false) {
|
|
127562
|
-
return entry2;
|
|
127563
|
-
}
|
|
127564
|
-
if (entry2.id === meta.processingUserMsgId) {
|
|
127565
|
-
return entry2;
|
|
127566
|
-
}
|
|
127567
|
-
if (entry2.id === meta.latestUserMsgId && entry2.id !== meta.lastHandledUserMsgId) {
|
|
127568
|
-
return entry2;
|
|
127569
|
-
}
|
|
127570
|
-
}
|
|
127571
|
-
return null;
|
|
127572
|
-
}
|
|
127573
|
-
function resolveDispatchTurnInput(entry2) {
|
|
127574
|
-
const historyBlocks = historyItemsToInputBlocks(entry2.items);
|
|
127575
|
-
const configuredBlocks = normalizeSessionInputBlocks(entry2.inputConfig?.inputBlocks, "");
|
|
127576
|
-
const fallbackBlocks = normalizeSessionInputBlocks(void 0, entry2.inputConfig?.prompt ?? "");
|
|
127577
|
-
const inputBlocks = configuredBlocks.length > 0 ? configuredBlocks : historyBlocks.length > 0 ? historyBlocks : fallbackBlocks;
|
|
127578
|
-
const prompt2 = entry2.inputConfig?.prompt ?? extractPromptPreviewFromInputBlocks(inputBlocks.length > 0 ? inputBlocks : historyBlocks);
|
|
127579
|
-
return {
|
|
127580
|
-
inputBlocks,
|
|
127581
|
-
prompt: prompt2
|
|
127582
|
-
};
|
|
127583
|
-
}
|
|
127584
127872
|
const isConfigOptionValueRecord = (value) => {
|
|
127585
127873
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
127586
127874
|
return false;
|
|
@@ -127955,7 +128243,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
127955
128243
|
modelId: entry2.inputConfig?.modelId,
|
|
127956
128244
|
configOptionValues: entry2.inputConfig?.configOptionValues,
|
|
127957
128245
|
issuePRMentions: entry2.inputConfig?.issuePRMentions,
|
|
127958
|
-
resume: entry2.inputConfig?.resume ?? meta
|
|
128246
|
+
resume: entry2.inputConfig?.resume ?? resolveDispatchAcpSessionId(meta)
|
|
127959
128247
|
},
|
|
127960
128248
|
userTurnId: entry2.id,
|
|
127961
128249
|
userId: entry2.userId ?? meta.userId,
|
|
@@ -128017,7 +128305,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
128017
128305
|
modelId: queuedItem.acpSessionConfig?.modelId,
|
|
128018
128306
|
configOptionValues: isConfigOptionValueRecord(queuedItem.acpSessionConfig?.configOptionValues) ? queuedItem.acpSessionConfig.configOptionValues : void 0,
|
|
128019
128307
|
issuePRMentions: queuedItem.acpSessionConfig?.issuePRMentions,
|
|
128020
|
-
resume: meta
|
|
128308
|
+
resume: resolveResumableAcpSessionId(meta)
|
|
128021
128309
|
});
|
|
128022
128310
|
const pendingEntry = buildPendingUserHistoryEntry({
|
|
128023
128311
|
userId: queuedItem.userId ?? meta.userId,
|
|
@@ -129053,7 +129341,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
129053
129341
|
}
|
|
129054
129342
|
})();
|
|
129055
129343
|
try {
|
|
129056
|
-
await withTimeout(readyPromise, TUNNEL_READY_TIMEOUT_MS, "Timed out waiting for preview tunnel control connection to become ready");
|
|
129344
|
+
await withTimeout$1(readyPromise, TUNNEL_READY_TIMEOUT_MS, "Timed out waiting for preview tunnel control connection to become ready");
|
|
129057
129345
|
} catch (error2) {
|
|
129058
129346
|
await close2("Preview tunnel failed to become ready");
|
|
129059
129347
|
throw error2;
|
|
@@ -129876,7 +130164,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
129876
130164
|
function asError(error2) {
|
|
129877
130165
|
return error2 instanceof Error ? error2 : new Error(formatError(error2));
|
|
129878
130166
|
}
|
|
129879
|
-
async function withTimeout(promise, timeoutMs, message) {
|
|
130167
|
+
async function withTimeout$1(promise, timeoutMs, message) {
|
|
129880
130168
|
let timeoutHandle;
|
|
129881
130169
|
const timeoutPromise = new Promise((_2, reject) => {
|
|
129882
130170
|
timeoutHandle = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
@@ -130738,6 +131026,794 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
130738
131026
|
return Math.round(this.deps.now?.() ?? getServerNow());
|
|
130739
131027
|
}
|
|
130740
131028
|
}
|
|
131029
|
+
const ACP_OPERATION_TIMEOUT_MS = 12e4;
|
|
131030
|
+
const ACP_PROCESS_EXIT_TIMEOUT_MS = 3e3;
|
|
131031
|
+
function waitForChildProcessExit(child, timeoutMs) {
|
|
131032
|
+
if (child.exitCode !== null) {
|
|
131033
|
+
return Promise.resolve(true);
|
|
131034
|
+
}
|
|
131035
|
+
return new Promise((resolve2) => {
|
|
131036
|
+
const timeout2 = setTimeout(() => {
|
|
131037
|
+
child.off("exit", onExit2);
|
|
131038
|
+
resolve2(child.exitCode !== null);
|
|
131039
|
+
}, timeoutMs);
|
|
131040
|
+
const onExit2 = () => {
|
|
131041
|
+
clearTimeout(timeout2);
|
|
131042
|
+
resolve2(true);
|
|
131043
|
+
};
|
|
131044
|
+
child.once("exit", onExit2);
|
|
131045
|
+
});
|
|
131046
|
+
}
|
|
131047
|
+
function signalChildProcess(child, signal) {
|
|
131048
|
+
if (process.platform !== "win32" && typeof child.pid === "number" && child.pid > 0) {
|
|
131049
|
+
process.kill(-child.pid, signal);
|
|
131050
|
+
return;
|
|
131051
|
+
}
|
|
131052
|
+
child.kill(signal);
|
|
131053
|
+
}
|
|
131054
|
+
async function terminateChildProcess(child) {
|
|
131055
|
+
if (child.exitCode !== null) {
|
|
131056
|
+
return;
|
|
131057
|
+
}
|
|
131058
|
+
try {
|
|
131059
|
+
signalChildProcess(child, "SIGTERM");
|
|
131060
|
+
} catch {
|
|
131061
|
+
return;
|
|
131062
|
+
}
|
|
131063
|
+
if (await waitForChildProcessExit(child, ACP_PROCESS_EXIT_TIMEOUT_MS)) {
|
|
131064
|
+
return;
|
|
131065
|
+
}
|
|
131066
|
+
try {
|
|
131067
|
+
signalChildProcess(child, "SIGKILL");
|
|
131068
|
+
} catch {
|
|
131069
|
+
return;
|
|
131070
|
+
}
|
|
131071
|
+
await waitForChildProcessExit(child, ACP_PROCESS_EXIT_TIMEOUT_MS);
|
|
131072
|
+
}
|
|
131073
|
+
async function withTimeout(promise, label2) {
|
|
131074
|
+
let timeout2 = null;
|
|
131075
|
+
try {
|
|
131076
|
+
return await Promise.race([
|
|
131077
|
+
promise,
|
|
131078
|
+
new Promise((_resolve, reject) => {
|
|
131079
|
+
timeout2 = setTimeout(() => reject(new Error(`${label2} timed out after ${ACP_OPERATION_TIMEOUT_MS}ms`)), ACP_OPERATION_TIMEOUT_MS);
|
|
131080
|
+
})
|
|
131081
|
+
]);
|
|
131082
|
+
} finally {
|
|
131083
|
+
if (timeout2) {
|
|
131084
|
+
clearTimeout(timeout2);
|
|
131085
|
+
}
|
|
131086
|
+
}
|
|
131087
|
+
}
|
|
131088
|
+
class AcpReplayCollectorClient {
|
|
131089
|
+
notifications = [];
|
|
131090
|
+
async requestPermission() {
|
|
131091
|
+
return {
|
|
131092
|
+
outcome: {
|
|
131093
|
+
outcome: "cancelled"
|
|
131094
|
+
}
|
|
131095
|
+
};
|
|
131096
|
+
}
|
|
131097
|
+
async sessionUpdate(params) {
|
|
131098
|
+
this.notifications.push(parseSessionNotification(params));
|
|
131099
|
+
}
|
|
131100
|
+
}
|
|
131101
|
+
function getProviderLabel$1(provider2) {
|
|
131102
|
+
return provider2 === "claude" ? "Claude" : "Codex";
|
|
131103
|
+
}
|
|
131104
|
+
async function createHistoryAcpConnection(args2) {
|
|
131105
|
+
const setting = resolveACPSetting({
|
|
131106
|
+
cliType: "builtin",
|
|
131107
|
+
agentType: args2.provider
|
|
131108
|
+
});
|
|
131109
|
+
const env2 = setting.exec.env ? {
|
|
131110
|
+
...process.env,
|
|
131111
|
+
...setting.exec.env
|
|
131112
|
+
} : process.env;
|
|
131113
|
+
const agentProcess = spawnAcpProcess({
|
|
131114
|
+
cliType: "builtin",
|
|
131115
|
+
agentType: args2.provider,
|
|
131116
|
+
workdir: args2.workdir,
|
|
131117
|
+
env: env2
|
|
131118
|
+
});
|
|
131119
|
+
agentProcess.stderr?.setEncoding("utf8");
|
|
131120
|
+
agentProcess.stderr?.on("data", (chunk) => {
|
|
131121
|
+
if (!chunk) return;
|
|
131122
|
+
args2.logger.debug(`[${args2.provider}-history-sync] ACP stderr: ${chunk.slice(0, 1200)}`);
|
|
131123
|
+
});
|
|
131124
|
+
if (!agentProcess.stdout || !agentProcess.stdin) {
|
|
131125
|
+
await terminateChildProcess(agentProcess);
|
|
131126
|
+
throw new Error(`${getProviderLabel$1(args2.provider)} ACP process did not expose stdio streams`);
|
|
131127
|
+
}
|
|
131128
|
+
const output = createStdoutReadableStream(agentProcess.stdout);
|
|
131129
|
+
const input2 = createStdinWritableStream(agentProcess.stdin);
|
|
131130
|
+
const stream2 = ndJsonStream(input2, output);
|
|
131131
|
+
const collector = new AcpReplayCollectorClient();
|
|
131132
|
+
const connection = new ClientSideConnection(() => collector, stream2);
|
|
131133
|
+
try {
|
|
131134
|
+
const initResponse = await withTimeout(connection.initialize({
|
|
131135
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
131136
|
+
clientCapabilities: {
|
|
131137
|
+
terminal: false,
|
|
131138
|
+
fs: {
|
|
131139
|
+
readTextFile: false,
|
|
131140
|
+
writeTextFile: false
|
|
131141
|
+
}
|
|
131142
|
+
}
|
|
131143
|
+
}), `${getProviderLabel$1(args2.provider)} ACP initialize`);
|
|
131144
|
+
return {
|
|
131145
|
+
agentProcess,
|
|
131146
|
+
connection,
|
|
131147
|
+
collector,
|
|
131148
|
+
initResponse
|
|
131149
|
+
};
|
|
131150
|
+
} catch (error2) {
|
|
131151
|
+
await terminateChildProcess(agentProcess);
|
|
131152
|
+
throw error2;
|
|
131153
|
+
}
|
|
131154
|
+
}
|
|
131155
|
+
async function resolveCatalogQueryPaths(rootPath) {
|
|
131156
|
+
const resolved = path__default.resolve(rootPath);
|
|
131157
|
+
let real = null;
|
|
131158
|
+
try {
|
|
131159
|
+
real = await fs$5.realpath(resolved);
|
|
131160
|
+
} catch {
|
|
131161
|
+
real = null;
|
|
131162
|
+
}
|
|
131163
|
+
const paths = [
|
|
131164
|
+
resolved
|
|
131165
|
+
];
|
|
131166
|
+
if (real && real !== resolved) {
|
|
131167
|
+
paths.push(real);
|
|
131168
|
+
}
|
|
131169
|
+
return paths;
|
|
131170
|
+
}
|
|
131171
|
+
async function listPaginatedHistorySessions(cwd, listPage) {
|
|
131172
|
+
const sessions = [];
|
|
131173
|
+
let cursor;
|
|
131174
|
+
do {
|
|
131175
|
+
const response = await listPage({
|
|
131176
|
+
cwd,
|
|
131177
|
+
cursor
|
|
131178
|
+
});
|
|
131179
|
+
sessions.push(...response.sessions);
|
|
131180
|
+
cursor = response.nextCursor;
|
|
131181
|
+
} while (cursor);
|
|
131182
|
+
return sessions;
|
|
131183
|
+
}
|
|
131184
|
+
async function listHistorySessionsForLocalProject(args2) {
|
|
131185
|
+
const queryPaths = await resolveCatalogQueryPaths(args2.rootPath);
|
|
131186
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
131187
|
+
for (const cwd of queryPaths) {
|
|
131188
|
+
const { agentProcess, connection, initResponse } = await createHistoryAcpConnection({
|
|
131189
|
+
provider: args2.provider,
|
|
131190
|
+
workdir: cwd,
|
|
131191
|
+
logger: args2.logger
|
|
131192
|
+
});
|
|
131193
|
+
try {
|
|
131194
|
+
if (!initResponse.agentCapabilities?.sessionCapabilities?.list) {
|
|
131195
|
+
throw new Error(`${getProviderLabel$1(args2.provider)} ACP agent does not advertise sessionCapabilities.list`);
|
|
131196
|
+
}
|
|
131197
|
+
const sessions = await listPaginatedHistorySessions(cwd, async ({ cursor }) => withTimeout(connection.listSessions({
|
|
131198
|
+
cwd,
|
|
131199
|
+
cursor
|
|
131200
|
+
}), `${getProviderLabel$1(args2.provider)} ACP listSessions (${cwd})`));
|
|
131201
|
+
for (const session of sessions) {
|
|
131202
|
+
bySessionId.set(session.sessionId, session);
|
|
131203
|
+
}
|
|
131204
|
+
} finally {
|
|
131205
|
+
await terminateChildProcess(agentProcess);
|
|
131206
|
+
}
|
|
131207
|
+
}
|
|
131208
|
+
return {
|
|
131209
|
+
sessions: [
|
|
131210
|
+
...bySessionId.values()
|
|
131211
|
+
],
|
|
131212
|
+
queryPaths
|
|
131213
|
+
};
|
|
131214
|
+
}
|
|
131215
|
+
async function loadHistorySessionReplay(args2) {
|
|
131216
|
+
const cwd = path__default.resolve(args2.rootPath);
|
|
131217
|
+
const { agentProcess, connection, collector, initResponse } = await createHistoryAcpConnection({
|
|
131218
|
+
provider: args2.provider,
|
|
131219
|
+
workdir: cwd,
|
|
131220
|
+
logger: args2.logger
|
|
131221
|
+
});
|
|
131222
|
+
try {
|
|
131223
|
+
if (!initResponse.agentCapabilities?.loadSession) {
|
|
131224
|
+
throw new Error(`${getProviderLabel$1(args2.provider)} ACP agent does not advertise loadSession`);
|
|
131225
|
+
}
|
|
131226
|
+
await withTimeout(connection.loadSession({
|
|
131227
|
+
sessionId: args2.acpSessionId,
|
|
131228
|
+
cwd,
|
|
131229
|
+
mcpServers: []
|
|
131230
|
+
}), `${getProviderLabel$1(args2.provider)} ACP loadSession (${args2.acpSessionId})`);
|
|
131231
|
+
return collector.notifications;
|
|
131232
|
+
} catch (error2) {
|
|
131233
|
+
throw new Error(`Failed to load ${getProviderLabel$1(args2.provider)} session ${args2.acpSessionId}: ${formatErrorMessage(error2)}`, {
|
|
131234
|
+
cause: error2
|
|
131235
|
+
});
|
|
131236
|
+
} finally {
|
|
131237
|
+
await terminateChildProcess(agentProcess);
|
|
131238
|
+
}
|
|
131239
|
+
}
|
|
131240
|
+
const syncLeases = /* @__PURE__ */ new Set();
|
|
131241
|
+
const machineCatalogWriteChains = /* @__PURE__ */ new Map();
|
|
131242
|
+
async function withMachineCatalogWriteLock(machineRoomId, fn) {
|
|
131243
|
+
const prev = machineCatalogWriteChains.get(machineRoomId);
|
|
131244
|
+
const current2 = (async () => {
|
|
131245
|
+
if (prev) {
|
|
131246
|
+
await prev.catch(() => {
|
|
131247
|
+
});
|
|
131248
|
+
}
|
|
131249
|
+
return fn();
|
|
131250
|
+
})();
|
|
131251
|
+
machineCatalogWriteChains.set(machineRoomId, current2);
|
|
131252
|
+
try {
|
|
131253
|
+
return await current2;
|
|
131254
|
+
} finally {
|
|
131255
|
+
if (machineCatalogWriteChains.get(machineRoomId) === current2) {
|
|
131256
|
+
machineCatalogWriteChains.delete(machineRoomId);
|
|
131257
|
+
}
|
|
131258
|
+
}
|
|
131259
|
+
}
|
|
131260
|
+
function emptySummary() {
|
|
131261
|
+
return {
|
|
131262
|
+
listed: 0,
|
|
131263
|
+
imported: 0,
|
|
131264
|
+
refreshed: 0,
|
|
131265
|
+
skipped: 0,
|
|
131266
|
+
conflicted: 0,
|
|
131267
|
+
failed: 0,
|
|
131268
|
+
failures: []
|
|
131269
|
+
};
|
|
131270
|
+
}
|
|
131271
|
+
function stableJson(value) {
|
|
131272
|
+
if (value === null || typeof value !== "object") {
|
|
131273
|
+
return JSON.stringify(value);
|
|
131274
|
+
}
|
|
131275
|
+
if (Array.isArray(value)) {
|
|
131276
|
+
return `[${value.map((item) => stableJson(item)).join(",")}]`;
|
|
131277
|
+
}
|
|
131278
|
+
const record2 = value;
|
|
131279
|
+
const entries = Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(record2[key2])}`);
|
|
131280
|
+
return `{${entries.join(",")}}`;
|
|
131281
|
+
}
|
|
131282
|
+
function hashText(value) {
|
|
131283
|
+
return createHash$1("sha256").update(value).digest("hex");
|
|
131284
|
+
}
|
|
131285
|
+
function normalizeHistoryEntryForHash(entry2) {
|
|
131286
|
+
return {
|
|
131287
|
+
role: entry2.role,
|
|
131288
|
+
items: entry2.items ?? [],
|
|
131289
|
+
plan: entry2.plan ?? []
|
|
131290
|
+
};
|
|
131291
|
+
}
|
|
131292
|
+
function hashHistoryEntry(entry2) {
|
|
131293
|
+
return hashText(stableJson(normalizeHistoryEntryForHash(entry2)));
|
|
131294
|
+
}
|
|
131295
|
+
function materializeReplay(args2) {
|
|
131296
|
+
let tempId = 0;
|
|
131297
|
+
const nowIso = new Date(getServerNow()).toISOString();
|
|
131298
|
+
const replay = buildHistoryReplayImport(args2.replayNotifications, {
|
|
131299
|
+
agentType: args2.provider,
|
|
131300
|
+
userId: args2.userId,
|
|
131301
|
+
now: () => nowIso,
|
|
131302
|
+
createId: () => `${args2.provider}:${args2.acpSessionId}:tmp:${tempId++}`,
|
|
131303
|
+
mode: "imported_snapshot"
|
|
131304
|
+
});
|
|
131305
|
+
const turnHashes = replay.history.map(hashHistoryEntry);
|
|
131306
|
+
const history = replay.history.map((entry2, index2) => ({
|
|
131307
|
+
...entry2,
|
|
131308
|
+
id: `${args2.provider}:${args2.acpSessionId}:turn:${index2}:${turnHashes[index2].slice(0, 16)}`
|
|
131309
|
+
}));
|
|
131310
|
+
return {
|
|
131311
|
+
history,
|
|
131312
|
+
turnHashes,
|
|
131313
|
+
replayDigest: hashText(turnHashes.join("\n"))
|
|
131314
|
+
};
|
|
131315
|
+
}
|
|
131316
|
+
function isPrefix(prefix, value) {
|
|
131317
|
+
if (prefix.length > value.length) {
|
|
131318
|
+
return false;
|
|
131319
|
+
}
|
|
131320
|
+
for (let index2 = 0; index2 < prefix.length; index2 += 1) {
|
|
131321
|
+
if (prefix[index2] !== value[index2]) {
|
|
131322
|
+
return false;
|
|
131323
|
+
}
|
|
131324
|
+
}
|
|
131325
|
+
return true;
|
|
131326
|
+
}
|
|
131327
|
+
function decideHistoryRefresh(args2) {
|
|
131328
|
+
if (args2.replayDigest === args2.externalHistory.replayDigest) {
|
|
131329
|
+
return {
|
|
131330
|
+
status: "skipped",
|
|
131331
|
+
reason: "digest_match"
|
|
131332
|
+
};
|
|
131333
|
+
}
|
|
131334
|
+
if (!isPrefix(args2.externalHistory.importedTurnHashes, args2.turnHashes)) {
|
|
131335
|
+
return {
|
|
131336
|
+
status: "conflicted",
|
|
131337
|
+
reason: "prefix_mismatch"
|
|
131338
|
+
};
|
|
131339
|
+
}
|
|
131340
|
+
if (args2.currentHistoryLength !== void 0 && args2.currentHistoryLength !== args2.externalHistory.importedTurnCount) {
|
|
131341
|
+
return {
|
|
131342
|
+
status: "conflicted",
|
|
131343
|
+
reason: "local_history_has_untracked_suffix"
|
|
131344
|
+
};
|
|
131345
|
+
}
|
|
131346
|
+
const appendFromIndex = args2.externalHistory.importedTurnCount;
|
|
131347
|
+
return args2.turnHashes.length > appendFromIndex ? {
|
|
131348
|
+
status: "refreshed",
|
|
131349
|
+
reason: "prefix_append",
|
|
131350
|
+
appendFromIndex
|
|
131351
|
+
} : {
|
|
131352
|
+
status: "skipped",
|
|
131353
|
+
reason: "empty_suffix",
|
|
131354
|
+
appendFromIndex
|
|
131355
|
+
};
|
|
131356
|
+
}
|
|
131357
|
+
async function listWorkspaceSessionMetas(manager) {
|
|
131358
|
+
const scanner = manager.repo.getMeta();
|
|
131359
|
+
if (!scanner) {
|
|
131360
|
+
return [];
|
|
131361
|
+
}
|
|
131362
|
+
const roomIds = /* @__PURE__ */ new Set();
|
|
131363
|
+
for (const row of await scanner.scan({
|
|
131364
|
+
prefix: [
|
|
131365
|
+
"m"
|
|
131366
|
+
]
|
|
131367
|
+
})) {
|
|
131368
|
+
const key2 = row.key;
|
|
131369
|
+
if (!Array.isArray(key2) || key2.length < 2) {
|
|
131370
|
+
continue;
|
|
131371
|
+
}
|
|
131372
|
+
const roomId = key2[1];
|
|
131373
|
+
if (typeof roomId === "string" && isSessionDocRoomId(roomId)) {
|
|
131374
|
+
roomIds.add(roomId);
|
|
131375
|
+
}
|
|
131376
|
+
}
|
|
131377
|
+
const metas = await Promise.all([
|
|
131378
|
+
...roomIds
|
|
131379
|
+
].map(async (roomId) => {
|
|
131380
|
+
const record2 = await manager.repo.getDocMeta(roomId);
|
|
131381
|
+
if (!record2?.meta || isLoroRepoDocDeleted(record2)) {
|
|
131382
|
+
return null;
|
|
131383
|
+
}
|
|
131384
|
+
const sessionId = roomId.slice("session-".length);
|
|
131385
|
+
return {
|
|
131386
|
+
sessionId,
|
|
131387
|
+
meta: record2.meta
|
|
131388
|
+
};
|
|
131389
|
+
}));
|
|
131390
|
+
return metas.filter((meta) => meta !== null);
|
|
131391
|
+
}
|
|
131392
|
+
function buildExistingHistorySessionIndex(metas, machineId, provider2) {
|
|
131393
|
+
const index2 = /* @__PURE__ */ new Map();
|
|
131394
|
+
for (const entry2 of metas) {
|
|
131395
|
+
if (entry2.meta.machineId !== machineId) continue;
|
|
131396
|
+
if (entry2.meta.cliType !== "builtin") continue;
|
|
131397
|
+
if (entry2.meta.agentType !== provider2) continue;
|
|
131398
|
+
const acpSessionIds = /* @__PURE__ */ new Set();
|
|
131399
|
+
if (entry2.meta.externalHistory?.provider === provider2) {
|
|
131400
|
+
const sourceAcpSessionId = entry2.meta.externalHistory.sourceAcpSessionId;
|
|
131401
|
+
if (sourceAcpSessionId) {
|
|
131402
|
+
acpSessionIds.add(sourceAcpSessionId);
|
|
131403
|
+
}
|
|
131404
|
+
if (entry2.meta.acpSessionId && entry2.meta.acpSessionId !== sourceAcpSessionId) {
|
|
131405
|
+
acpSessionIds.add(entry2.meta.acpSessionId);
|
|
131406
|
+
}
|
|
131407
|
+
} else if (entry2.meta.acpSessionId) {
|
|
131408
|
+
acpSessionIds.add(entry2.meta.acpSessionId);
|
|
131409
|
+
}
|
|
131410
|
+
for (const acpSessionId of acpSessionIds) {
|
|
131411
|
+
index2.set(acpSessionId, entry2);
|
|
131412
|
+
}
|
|
131413
|
+
}
|
|
131414
|
+
return index2;
|
|
131415
|
+
}
|
|
131416
|
+
function getProviderLabel(provider2) {
|
|
131417
|
+
return provider2 === "claude" ? "Claude" : "Codex";
|
|
131418
|
+
}
|
|
131419
|
+
function resolveSessionTitle(info, provider2) {
|
|
131420
|
+
const title2 = info.title?.trim();
|
|
131421
|
+
return title2 || `${getProviderLabel(provider2)} session`;
|
|
131422
|
+
}
|
|
131423
|
+
function parseUpdatedAtMs(updatedAt) {
|
|
131424
|
+
if (!updatedAt) return 0;
|
|
131425
|
+
const parsed = Date.parse(updatedAt);
|
|
131426
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
131427
|
+
}
|
|
131428
|
+
function compareCatalogItems(left2, right2) {
|
|
131429
|
+
const leftUpdatedAt = parseUpdatedAtMs(left2.updatedAt);
|
|
131430
|
+
const rightUpdatedAt = parseUpdatedAtMs(right2.updatedAt);
|
|
131431
|
+
if (leftUpdatedAt !== rightUpdatedAt) {
|
|
131432
|
+
return rightUpdatedAt - leftUpdatedAt;
|
|
131433
|
+
}
|
|
131434
|
+
return left2.title.localeCompare(right2.title);
|
|
131435
|
+
}
|
|
131436
|
+
function getHistoryCatalogStatus(existing) {
|
|
131437
|
+
if (!existing) return "available";
|
|
131438
|
+
return existing.meta.externalHistory?.status === "sync_conflict" ? "sync_conflict" : "imported";
|
|
131439
|
+
}
|
|
131440
|
+
function buildCatalogItem(provider2, info, existing) {
|
|
131441
|
+
const acpSessionId = info.sessionId;
|
|
131442
|
+
return {
|
|
131443
|
+
acpSessionId,
|
|
131444
|
+
title: resolveSessionTitle(info, provider2),
|
|
131445
|
+
updatedAt: info.updatedAt ?? void 0,
|
|
131446
|
+
importedSessionId: existing?.sessionId,
|
|
131447
|
+
status: getHistoryCatalogStatus(existing)
|
|
131448
|
+
};
|
|
131449
|
+
}
|
|
131450
|
+
function shouldSkipBySourceUpdatedAt(info, externalHistory) {
|
|
131451
|
+
if (externalHistory.status === "metadata_only") {
|
|
131452
|
+
return false;
|
|
131453
|
+
}
|
|
131454
|
+
if (!info.updatedAt || !externalHistory.sourceUpdatedAt) {
|
|
131455
|
+
return false;
|
|
131456
|
+
}
|
|
131457
|
+
const next = Date.parse(info.updatedAt);
|
|
131458
|
+
const current2 = Date.parse(externalHistory.sourceUpdatedAt);
|
|
131459
|
+
return Number.isFinite(next) && Number.isFinite(current2) && next <= current2;
|
|
131460
|
+
}
|
|
131461
|
+
function resolveSourceUpdatedAtMs(info, fallback2) {
|
|
131462
|
+
if (!info.updatedAt) {
|
|
131463
|
+
return fallback2;
|
|
131464
|
+
}
|
|
131465
|
+
const parsed = Date.parse(info.updatedAt);
|
|
131466
|
+
return Number.isFinite(parsed) ? parsed : fallback2;
|
|
131467
|
+
}
|
|
131468
|
+
function buildExternalHistoryMeta(args2) {
|
|
131469
|
+
return {
|
|
131470
|
+
provider: args2.provider,
|
|
131471
|
+
source: args2.provider === "claude" ? "local-claude-home" : "local-codex-home",
|
|
131472
|
+
sourceAcpSessionId: args2.sourceAcpSessionId,
|
|
131473
|
+
sourceUpdatedAt: args2.sourceUpdatedAt ?? void 0,
|
|
131474
|
+
replayDigest: args2.materialized.replayDigest,
|
|
131475
|
+
importedTurnCount: args2.materialized.turnHashes.length,
|
|
131476
|
+
importedTurnHashes: args2.materialized.turnHashes,
|
|
131477
|
+
lastSyncAt: getServerNow(),
|
|
131478
|
+
status: args2.status ?? "synced",
|
|
131479
|
+
conflictReason: args2.conflictReason
|
|
131480
|
+
};
|
|
131481
|
+
}
|
|
131482
|
+
function buildMetadataOnlyExternalHistoryMeta(args2) {
|
|
131483
|
+
return {
|
|
131484
|
+
provider: args2.provider,
|
|
131485
|
+
source: args2.provider === "claude" ? "local-claude-home" : "local-codex-home",
|
|
131486
|
+
sourceAcpSessionId: args2.sourceAcpSessionId,
|
|
131487
|
+
sourceUpdatedAt: args2.sourceUpdatedAt ?? void 0,
|
|
131488
|
+
importedTurnCount: 0,
|
|
131489
|
+
importedTurnHashes: [],
|
|
131490
|
+
lastSyncAt: getServerNow(),
|
|
131491
|
+
status: "metadata_only"
|
|
131492
|
+
};
|
|
131493
|
+
}
|
|
131494
|
+
class LocalProjectHistorySyncService {
|
|
131495
|
+
constructor(manager, logger2, context2, provider2 = "codex") {
|
|
131496
|
+
this.manager = manager;
|
|
131497
|
+
this.logger = logger2;
|
|
131498
|
+
this.context = context2;
|
|
131499
|
+
this.provider = provider2;
|
|
131500
|
+
}
|
|
131501
|
+
provider;
|
|
131502
|
+
async syncLocalProject(args2) {
|
|
131503
|
+
const leaseKey = `${this.provider}:${this.context.workspaceId}:${this.context.machineId}:${args2.localProjectId}`;
|
|
131504
|
+
if (syncLeases.has(leaseKey)) {
|
|
131505
|
+
throw new Error(`${getProviderLabel(this.provider)} history sync is already running for this local project`);
|
|
131506
|
+
}
|
|
131507
|
+
syncLeases.add(leaseKey);
|
|
131508
|
+
try {
|
|
131509
|
+
return await this.syncLocalProjectInner(args2);
|
|
131510
|
+
} finally {
|
|
131511
|
+
syncLeases.delete(leaseKey);
|
|
131512
|
+
}
|
|
131513
|
+
}
|
|
131514
|
+
async syncLocalProjectInner(args2) {
|
|
131515
|
+
const snapshot = await this.listCatalogSnapshot(args2.rootPath);
|
|
131516
|
+
return await this.writeCatalogResult({
|
|
131517
|
+
localProjectId: args2.localProjectId,
|
|
131518
|
+
sessions: snapshot.sessions,
|
|
131519
|
+
existingByAcpSessionId: snapshot.existingByAcpSessionId
|
|
131520
|
+
});
|
|
131521
|
+
}
|
|
131522
|
+
async importLocalProjectSessions(args2) {
|
|
131523
|
+
const leaseKey = `${this.provider}:${this.context.workspaceId}:${this.context.machineId}:${args2.localProjectId}`;
|
|
131524
|
+
if (syncLeases.has(leaseKey)) {
|
|
131525
|
+
throw new Error(`${getProviderLabel(this.provider)} history sync is already running for this local project`);
|
|
131526
|
+
}
|
|
131527
|
+
syncLeases.add(leaseKey);
|
|
131528
|
+
try {
|
|
131529
|
+
return await this.importLocalProjectSessionsInner(args2);
|
|
131530
|
+
} finally {
|
|
131531
|
+
syncLeases.delete(leaseKey);
|
|
131532
|
+
}
|
|
131533
|
+
}
|
|
131534
|
+
async importLocalProjectSessionsInner(args2) {
|
|
131535
|
+
const summary2 = emptySummary();
|
|
131536
|
+
const selectedIds = [
|
|
131537
|
+
...new Set(args2.acpSessionIds)
|
|
131538
|
+
];
|
|
131539
|
+
summary2.listed = selectedIds.length;
|
|
131540
|
+
const snapshot = await this.listCatalogSnapshot(args2.rootPath);
|
|
131541
|
+
const infoByAcpSessionId = new Map(snapshot.sessions.map((info) => [
|
|
131542
|
+
info.sessionId,
|
|
131543
|
+
info
|
|
131544
|
+
]));
|
|
131545
|
+
const project = {
|
|
131546
|
+
kind: "local",
|
|
131547
|
+
localProjectId: args2.localProjectId
|
|
131548
|
+
};
|
|
131549
|
+
for (const selectedId of selectedIds) {
|
|
131550
|
+
const acpSessionId = selectedId;
|
|
131551
|
+
const info = infoByAcpSessionId.get(selectedId);
|
|
131552
|
+
try {
|
|
131553
|
+
if (!info) {
|
|
131554
|
+
throw new Error(`${getProviderLabel(this.provider)} session was not found in the local project catalog`);
|
|
131555
|
+
}
|
|
131556
|
+
const existing = snapshot.existingByAcpSessionId.get(selectedId);
|
|
131557
|
+
if (!existing) {
|
|
131558
|
+
const importedSession = await this.importNewSession({
|
|
131559
|
+
info,
|
|
131560
|
+
acpSessionId,
|
|
131561
|
+
project
|
|
131562
|
+
});
|
|
131563
|
+
snapshot.existingByAcpSessionId.set(selectedId, importedSession);
|
|
131564
|
+
summary2.imported += 1;
|
|
131565
|
+
continue;
|
|
131566
|
+
}
|
|
131567
|
+
const status = await this.refreshExistingSession({
|
|
131568
|
+
existing,
|
|
131569
|
+
info,
|
|
131570
|
+
acpSessionId,
|
|
131571
|
+
rootPath: args2.rootPath
|
|
131572
|
+
});
|
|
131573
|
+
summary2[status] += 1;
|
|
131574
|
+
} catch (error2) {
|
|
131575
|
+
summary2.failed += 1;
|
|
131576
|
+
summary2.failures.push({
|
|
131577
|
+
acpSessionId,
|
|
131578
|
+
message: formatErrorMessage(error2)
|
|
131579
|
+
});
|
|
131580
|
+
this.logger.debug(`[${this.provider}-history-sync] Failed to import ${getProviderLabel(this.provider)} session ${acpSessionId}: ${formatErrorMessage(error2)}`);
|
|
131581
|
+
}
|
|
131582
|
+
}
|
|
131583
|
+
const catalog = await this.writeCatalogResult({
|
|
131584
|
+
localProjectId: args2.localProjectId,
|
|
131585
|
+
sessions: snapshot.sessions,
|
|
131586
|
+
existingByAcpSessionId: snapshot.existingByAcpSessionId
|
|
131587
|
+
});
|
|
131588
|
+
return {
|
|
131589
|
+
summary: summary2,
|
|
131590
|
+
catalog
|
|
131591
|
+
};
|
|
131592
|
+
}
|
|
131593
|
+
async listCatalogSnapshot(rootPath) {
|
|
131594
|
+
const catalog = await listHistorySessionsForLocalProject({
|
|
131595
|
+
provider: this.provider,
|
|
131596
|
+
rootPath,
|
|
131597
|
+
logger: this.logger
|
|
131598
|
+
});
|
|
131599
|
+
const sessionMetas = await listWorkspaceSessionMetas(this.manager);
|
|
131600
|
+
const existingByAcpSessionId = buildExistingHistorySessionIndex(sessionMetas, this.context.machineId, this.provider);
|
|
131601
|
+
return {
|
|
131602
|
+
sessions: catalog.sessions,
|
|
131603
|
+
existingByAcpSessionId
|
|
131604
|
+
};
|
|
131605
|
+
}
|
|
131606
|
+
async writeCatalogResult(args2) {
|
|
131607
|
+
const lastListedAt = Math.round(getServerNow());
|
|
131608
|
+
const sessions = args2.sessions.map((info) => buildCatalogItem(this.provider, info, args2.existingByAcpSessionId.get(info.sessionId))).sort(compareCatalogItems);
|
|
131609
|
+
const catalog = {
|
|
131610
|
+
listed: sessions.length,
|
|
131611
|
+
lastListedAt,
|
|
131612
|
+
sessions
|
|
131613
|
+
};
|
|
131614
|
+
const machineRoomId = getMachineRoomId(this.context.machineId);
|
|
131615
|
+
await withMachineCatalogWriteLock(machineRoomId, async () => {
|
|
131616
|
+
const current2 = await this.manager.repo.getDocMeta(machineRoomId);
|
|
131617
|
+
const rawExisting = current2?.meta?.localProjects;
|
|
131618
|
+
const existing = rawExisting && typeof rawExisting === "object" ? rawExisting : {};
|
|
131619
|
+
const previous = existing[args2.localProjectId];
|
|
131620
|
+
if (!previous) {
|
|
131621
|
+
return;
|
|
131622
|
+
}
|
|
131623
|
+
await this.manager.repo.upsertDocMeta(machineRoomId, {
|
|
131624
|
+
localProjects: {
|
|
131625
|
+
...existing,
|
|
131626
|
+
[args2.localProjectId]: {
|
|
131627
|
+
...previous,
|
|
131628
|
+
history: {
|
|
131629
|
+
...previous.history ?? {},
|
|
131630
|
+
[this.provider]: {
|
|
131631
|
+
lastListedAt,
|
|
131632
|
+
sessions: Object.fromEntries(sessions.map((item) => [
|
|
131633
|
+
item.acpSessionId,
|
|
131634
|
+
item
|
|
131635
|
+
]))
|
|
131636
|
+
}
|
|
131637
|
+
}
|
|
131638
|
+
}
|
|
131639
|
+
}
|
|
131640
|
+
});
|
|
131641
|
+
});
|
|
131642
|
+
return catalog;
|
|
131643
|
+
}
|
|
131644
|
+
async importNewSession(args2) {
|
|
131645
|
+
const sessionId = v4();
|
|
131646
|
+
const roomId = getSessionRoomId(sessionId);
|
|
131647
|
+
const nowMs = getServerNow();
|
|
131648
|
+
const lastMessageAt = resolveSourceUpdatedAtMs(args2.info, nowMs);
|
|
131649
|
+
const meta = {
|
|
131650
|
+
id: sessionId,
|
|
131651
|
+
machineId: this.context.machineId,
|
|
131652
|
+
createdAt: new Date(nowMs).toISOString(),
|
|
131653
|
+
userId: this.context.userId,
|
|
131654
|
+
status: SessionStatusFactory.idle(),
|
|
131655
|
+
isArchived: false,
|
|
131656
|
+
origin: this.provider,
|
|
131657
|
+
cliType: "builtin",
|
|
131658
|
+
agentType: this.provider,
|
|
131659
|
+
project: args2.project,
|
|
131660
|
+
title: resolveSessionTitle(args2.info, this.provider),
|
|
131661
|
+
lastMessageAt,
|
|
131662
|
+
externalHistory: buildMetadataOnlyExternalHistoryMeta({
|
|
131663
|
+
provider: this.provider,
|
|
131664
|
+
sourceAcpSessionId: args2.acpSessionId,
|
|
131665
|
+
sourceUpdatedAt: args2.info.updatedAt
|
|
131666
|
+
})
|
|
131667
|
+
};
|
|
131668
|
+
await this.manager.repo.upsertDocMeta(roomId, meta);
|
|
131669
|
+
return {
|
|
131670
|
+
sessionId,
|
|
131671
|
+
meta
|
|
131672
|
+
};
|
|
131673
|
+
}
|
|
131674
|
+
async refreshExistingSession(args2) {
|
|
131675
|
+
const externalHistory = args2.existing.meta.externalHistory;
|
|
131676
|
+
if (externalHistory?.provider !== this.provider) {
|
|
131677
|
+
return "skipped";
|
|
131678
|
+
}
|
|
131679
|
+
if (shouldSkipBySourceUpdatedAt(args2.info, externalHistory)) {
|
|
131680
|
+
return "skipped";
|
|
131681
|
+
}
|
|
131682
|
+
const replayNotifications = await loadHistorySessionReplay({
|
|
131683
|
+
provider: this.provider,
|
|
131684
|
+
rootPath: args2.rootPath,
|
|
131685
|
+
acpSessionId: args2.acpSessionId,
|
|
131686
|
+
logger: this.logger
|
|
131687
|
+
});
|
|
131688
|
+
const materialized = materializeReplay({
|
|
131689
|
+
provider: this.provider,
|
|
131690
|
+
acpSessionId: args2.acpSessionId,
|
|
131691
|
+
replayNotifications,
|
|
131692
|
+
userId: this.context.userId
|
|
131693
|
+
});
|
|
131694
|
+
const replayDecision = decideHistoryRefresh({
|
|
131695
|
+
externalHistory,
|
|
131696
|
+
replayDigest: materialized.replayDigest,
|
|
131697
|
+
turnHashes: materialized.turnHashes
|
|
131698
|
+
});
|
|
131699
|
+
if (replayDecision.reason === "digest_match") {
|
|
131700
|
+
await this.manager.repo.upsertDocMeta(getSessionRoomId(args2.existing.sessionId), {
|
|
131701
|
+
origin: this.provider,
|
|
131702
|
+
externalHistory: buildExternalHistoryMeta({
|
|
131703
|
+
provider: this.provider,
|
|
131704
|
+
sourceAcpSessionId: args2.acpSessionId,
|
|
131705
|
+
sourceUpdatedAt: args2.info.updatedAt,
|
|
131706
|
+
materialized
|
|
131707
|
+
})
|
|
131708
|
+
});
|
|
131709
|
+
return "skipped";
|
|
131710
|
+
}
|
|
131711
|
+
if (replayDecision.status === "conflicted") {
|
|
131712
|
+
await this.markConflict(args2.existing.sessionId, args2.info, materialized, replayDecision.reason);
|
|
131713
|
+
return "conflicted";
|
|
131714
|
+
}
|
|
131715
|
+
const sessionDoc = await this.manager.getOrCreateSessionDoc(args2.existing.sessionId);
|
|
131716
|
+
const currentHistory = await sessionDoc.getHistory();
|
|
131717
|
+
const appendDecision = decideHistoryRefresh({
|
|
131718
|
+
externalHistory,
|
|
131719
|
+
replayDigest: materialized.replayDigest,
|
|
131720
|
+
turnHashes: materialized.turnHashes,
|
|
131721
|
+
currentHistoryLength: currentHistory.length
|
|
131722
|
+
});
|
|
131723
|
+
if (appendDecision.status === "conflicted") {
|
|
131724
|
+
await this.markConflict(args2.existing.sessionId, args2.info, materialized, appendDecision.reason);
|
|
131725
|
+
const synced2 = await sessionDoc.waitUntilSynced();
|
|
131726
|
+
if (!synced2) {
|
|
131727
|
+
this.logger.debug(`[${this.provider}-history-sync] Conflict marker for ${args2.existing.sessionId} did not confirm sync before unload; clients may see the previous state until next sync.`);
|
|
131728
|
+
}
|
|
131729
|
+
await this.manager.cleanSessionDoc(args2.existing.sessionId, {
|
|
131730
|
+
preserveStatus: true
|
|
131731
|
+
});
|
|
131732
|
+
return "conflicted";
|
|
131733
|
+
}
|
|
131734
|
+
const suffix = materialized.history.slice(appendDecision.appendFromIndex);
|
|
131735
|
+
await sessionDoc.updateHistory((history) => [
|
|
131736
|
+
...history,
|
|
131737
|
+
...suffix
|
|
131738
|
+
]);
|
|
131739
|
+
await this.manager.repo.upsertDocMeta(getSessionRoomId(args2.existing.sessionId), {
|
|
131740
|
+
origin: this.provider,
|
|
131741
|
+
lastMessageAt: resolveSourceUpdatedAtMs(args2.info, getServerNow()),
|
|
131742
|
+
externalHistory: buildExternalHistoryMeta({
|
|
131743
|
+
provider: this.provider,
|
|
131744
|
+
sourceAcpSessionId: args2.acpSessionId,
|
|
131745
|
+
sourceUpdatedAt: args2.info.updatedAt,
|
|
131746
|
+
materialized
|
|
131747
|
+
})
|
|
131748
|
+
});
|
|
131749
|
+
const synced = await sessionDoc.waitUntilSynced();
|
|
131750
|
+
if (!synced) {
|
|
131751
|
+
this.logger.debug(`[${this.provider}-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
|
+
}
|
|
131753
|
+
await this.manager.cleanSessionDoc(args2.existing.sessionId, {
|
|
131754
|
+
preserveStatus: true
|
|
131755
|
+
});
|
|
131756
|
+
return suffix.length > 0 ? "refreshed" : "skipped";
|
|
131757
|
+
}
|
|
131758
|
+
async markConflict(sessionId, info, materialized, reason) {
|
|
131759
|
+
await this.manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
|
|
131760
|
+
origin: this.provider,
|
|
131761
|
+
externalHistory: buildExternalHistoryMeta({
|
|
131762
|
+
provider: this.provider,
|
|
131763
|
+
sourceAcpSessionId: info.sessionId,
|
|
131764
|
+
sourceUpdatedAt: info.updatedAt,
|
|
131765
|
+
materialized,
|
|
131766
|
+
status: "sync_conflict",
|
|
131767
|
+
conflictReason: reason
|
|
131768
|
+
})
|
|
131769
|
+
});
|
|
131770
|
+
}
|
|
131771
|
+
}
|
|
131772
|
+
const HISTORY_REQUEST_TYPES = /* @__PURE__ */ new Set([
|
|
131773
|
+
"local-project/sync-history",
|
|
131774
|
+
"local-project/import-history"
|
|
131775
|
+
]);
|
|
131776
|
+
function isHistoryRequestType(value) {
|
|
131777
|
+
return HISTORY_REQUEST_TYPES.has(value);
|
|
131778
|
+
}
|
|
131779
|
+
function precheckLocalProjectHistoryRequest(args2) {
|
|
131780
|
+
const { request, expectedMachineId, expectedWorkspaceId } = args2;
|
|
131781
|
+
if (request.machineId !== expectedMachineId) {
|
|
131782
|
+
return {
|
|
131783
|
+
ok: false,
|
|
131784
|
+
error: "machine_mismatch",
|
|
131785
|
+
message: `Machine mismatch: expected ${expectedMachineId}`
|
|
131786
|
+
};
|
|
131787
|
+
}
|
|
131788
|
+
if (!isHistoryRequestType(request.type)) {
|
|
131789
|
+
return {
|
|
131790
|
+
ok: false,
|
|
131791
|
+
error: "invalid_request",
|
|
131792
|
+
message: `Unsupported remote local project control request: ${request.type}`
|
|
131793
|
+
};
|
|
131794
|
+
}
|
|
131795
|
+
const historyRequest = request;
|
|
131796
|
+
const requesterUserId = historyRequest.requestedByUserId?.trim();
|
|
131797
|
+
if (!requesterUserId) {
|
|
131798
|
+
return {
|
|
131799
|
+
ok: false,
|
|
131800
|
+
error: "invalid_request",
|
|
131801
|
+
message: "Local project history requests require requestedByUserId"
|
|
131802
|
+
};
|
|
131803
|
+
}
|
|
131804
|
+
if (historyRequest.workspaceId !== expectedWorkspaceId) {
|
|
131805
|
+
return {
|
|
131806
|
+
ok: false,
|
|
131807
|
+
error: "workspace_not_found",
|
|
131808
|
+
message: `Workspace mismatch: expected ${expectedWorkspaceId}`
|
|
131809
|
+
};
|
|
131810
|
+
}
|
|
131811
|
+
return {
|
|
131812
|
+
ok: true,
|
|
131813
|
+
request: historyRequest,
|
|
131814
|
+
requesterUserId
|
|
131815
|
+
};
|
|
131816
|
+
}
|
|
130741
131817
|
const SESSION_IMAGE_MIME_TYPE_BY_EXTENSION = {
|
|
130742
131818
|
png: "image/png",
|
|
130743
131819
|
jpg: "image/jpeg",
|
|
@@ -130904,6 +131980,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
130904
131980
|
requestedByUserId,
|
|
130905
131981
|
reason
|
|
130906
131982
|
}),
|
|
131983
|
+
dispatchLocalProjectControl: async (request) => await this.dispatchLocalProjectControlViaRpc(request),
|
|
130907
131984
|
onFatalAuthFailure: (error2) => this.onFatalAuthFailure?.(error2)
|
|
130908
131985
|
});
|
|
130909
131986
|
} else {
|
|
@@ -131789,6 +132866,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131789
132866
|
cliVersion: this.cliVersion,
|
|
131790
132867
|
os: process.platform,
|
|
131791
132868
|
rpcVersion: supportsStreamsRpc ? LORO_STREAMS_RPC_VERSION : void 0,
|
|
132869
|
+
supportsLocalProjectHistoryRpc: supportsStreamsRpc,
|
|
131792
132870
|
supportRegistryAgentTypes: this.supportRegistryAgentTypes,
|
|
131793
132871
|
sessions: [],
|
|
131794
132872
|
needToArchiveSessions: {},
|
|
@@ -132626,6 +133704,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132626
133704
|
cliVersion: this.cliVersion,
|
|
132627
133705
|
os: process.platform,
|
|
132628
133706
|
rpcVersion: supportsStreamsRpc ? LORO_STREAMS_RPC_VERSION : machineMeta?.rpcVersion,
|
|
133707
|
+
supportsLocalProjectHistoryRpc: supportsStreamsRpc,
|
|
132629
133708
|
supportRegistryAgentTypes: this.supportRegistryAgentTypes,
|
|
132630
133709
|
acpCapabilities: machineMeta?.acpCapabilities,
|
|
132631
133710
|
sessions: machineMeta?.sessions ?? [],
|
|
@@ -133266,9 +134345,75 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
133266
134345
|
}
|
|
133267
134346
|
await this.workspaceDocument.registerMachine(this.machineId, {
|
|
133268
134347
|
...existingMeta,
|
|
133269
|
-
rpcVersion: LORO_STREAMS_RPC_VERSION
|
|
134348
|
+
rpcVersion: LORO_STREAMS_RPC_VERSION,
|
|
134349
|
+
supportsLocalProjectHistoryRpc: true
|
|
133270
134350
|
});
|
|
133271
134351
|
}
|
|
134352
|
+
toLocalProjectControlError(type2, error2, message, data) {
|
|
134353
|
+
return {
|
|
134354
|
+
ok: false,
|
|
134355
|
+
type: type2,
|
|
134356
|
+
error: error2,
|
|
134357
|
+
message,
|
|
134358
|
+
data
|
|
134359
|
+
};
|
|
134360
|
+
}
|
|
134361
|
+
async dispatchLocalProjectControlViaRpc(message) {
|
|
134362
|
+
const requestType = message.type;
|
|
134363
|
+
const precheck = precheckLocalProjectHistoryRequest({
|
|
134364
|
+
request: message,
|
|
134365
|
+
expectedMachineId: this.machineId,
|
|
134366
|
+
expectedWorkspaceId: this.workspaceId
|
|
134367
|
+
});
|
|
134368
|
+
if (!precheck.ok) {
|
|
134369
|
+
return this.toLocalProjectControlError(requestType, precheck.error, precheck.message);
|
|
134370
|
+
}
|
|
134371
|
+
const { requesterUserId, request } = precheck;
|
|
134372
|
+
try {
|
|
134373
|
+
const access = await canUseMachineForCliToken({
|
|
134374
|
+
token: this.token,
|
|
134375
|
+
workspaceId: this.workspaceId,
|
|
134376
|
+
machineId: this.machineId,
|
|
134377
|
+
requesterUserId,
|
|
134378
|
+
localProjectId: request.localProjectId
|
|
134379
|
+
});
|
|
134380
|
+
if (!access.allowed) {
|
|
134381
|
+
return this.toLocalProjectControlError(requestType, "access_denied", `Machine access denied: ${access.reason}`);
|
|
134382
|
+
}
|
|
134383
|
+
const rootPath = await resolveWorkspaceLocalProjectRootPath(this.workspaceDocument.repo, this.machineId, request.localProjectId);
|
|
134384
|
+
if (!rootPath) {
|
|
134385
|
+
return this.toLocalProjectControlError(requestType, "local_project_not_found", `Local project not found in workspace: ${request.localProjectId}`);
|
|
134386
|
+
}
|
|
134387
|
+
const service = new LocalProjectHistorySyncService(this.workspaceDocument, this.logger, {
|
|
134388
|
+
workspaceId: this.workspaceId,
|
|
134389
|
+
machineId: this.machineId,
|
|
134390
|
+
userId: this.userId
|
|
134391
|
+
}, request.provider);
|
|
134392
|
+
if (request.type === "local-project/sync-history") {
|
|
134393
|
+
const result2 = await service.syncLocalProject({
|
|
134394
|
+
localProjectId: request.localProjectId,
|
|
134395
|
+
rootPath
|
|
134396
|
+
});
|
|
134397
|
+
return {
|
|
134398
|
+
ok: true,
|
|
134399
|
+
type: "local-project/sync-history",
|
|
134400
|
+
result: result2
|
|
134401
|
+
};
|
|
134402
|
+
}
|
|
134403
|
+
const result = await service.importLocalProjectSessions({
|
|
134404
|
+
localProjectId: request.localProjectId,
|
|
134405
|
+
rootPath,
|
|
134406
|
+
acpSessionIds: request.acpSessionIds
|
|
134407
|
+
});
|
|
134408
|
+
return {
|
|
134409
|
+
ok: true,
|
|
134410
|
+
type: "local-project/import-history",
|
|
134411
|
+
result
|
|
134412
|
+
};
|
|
134413
|
+
} catch (error2) {
|
|
134414
|
+
return this.toLocalProjectControlError(requestType, "execution_failed", formatErrorMessage(error2));
|
|
134415
|
+
}
|
|
134416
|
+
}
|
|
133272
134417
|
cancelPendingPermissionRequests() {
|
|
133273
134418
|
this.permissionRequestStartTimes.clear();
|
|
133274
134419
|
}
|
|
@@ -142115,6 +143260,18 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142115
143260
|
}
|
|
142116
143261
|
return typeof value.error === "string";
|
|
142117
143262
|
}
|
|
143263
|
+
function isLocalProjectHistorySyncSummary(value) {
|
|
143264
|
+
return isObjectRecord(value) && typeof value.listed === "number" && Number.isInteger(value.listed) && value.listed >= 0 && typeof value.imported === "number" && Number.isInteger(value.imported) && value.imported >= 0 && typeof value.refreshed === "number" && Number.isInteger(value.refreshed) && value.refreshed >= 0 && typeof value.skipped === "number" && Number.isInteger(value.skipped) && value.skipped >= 0 && typeof value.conflicted === "number" && Number.isInteger(value.conflicted) && value.conflicted >= 0 && typeof value.failed === "number" && Number.isInteger(value.failed) && value.failed >= 0 && Array.isArray(value.failures) && value.failures.every((failure) => isObjectRecord(failure) && typeof failure.acpSessionId === "string" && typeof failure.message === "string");
|
|
143265
|
+
}
|
|
143266
|
+
function isLocalProjectHistoryCatalogItem(value) {
|
|
143267
|
+
return isObjectRecord(value) && typeof value.acpSessionId === "string" && typeof value.title === "string" && (typeof value.updatedAt === "undefined" || typeof value.updatedAt === "string") && (typeof value.importedSessionId === "undefined" || typeof value.importedSessionId === "string") && (typeof value.status === "undefined" || value.status === "available" || value.status === "imported" || value.status === "sync_conflict");
|
|
143268
|
+
}
|
|
143269
|
+
function isLocalProjectHistoryCatalogResult(value) {
|
|
143270
|
+
return isObjectRecord(value) && typeof value.listed === "number" && Number.isInteger(value.listed) && value.listed >= 0 && typeof value.lastListedAt === "number" && Number.isInteger(value.lastListedAt) && value.lastListedAt >= 0 && Array.isArray(value.sessions) && value.sessions.every(isLocalProjectHistoryCatalogItem);
|
|
143271
|
+
}
|
|
143272
|
+
function isLocalProjectHistoryImportResult(value) {
|
|
143273
|
+
return isObjectRecord(value) && isLocalProjectHistorySyncSummary(value.summary) && isLocalProjectHistoryCatalogResult(value.catalog);
|
|
143274
|
+
}
|
|
142118
143275
|
function isWorkspaceIds(value) {
|
|
142119
143276
|
return isStringArray(value);
|
|
142120
143277
|
}
|
|
@@ -142146,6 +143303,12 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142146
143303
|
if (value.type === "local-project/checkout-branch") {
|
|
142147
143304
|
return isLocalProjectCheckoutBranchResult(value.result);
|
|
142148
143305
|
}
|
|
143306
|
+
if (value.type === "local-project/sync-history") {
|
|
143307
|
+
return isLocalProjectHistoryCatalogResult(value.result);
|
|
143308
|
+
}
|
|
143309
|
+
if (value.type === "local-project/import-history") {
|
|
143310
|
+
return isLocalProjectHistoryImportResult(value.result);
|
|
143311
|
+
}
|
|
142149
143312
|
return false;
|
|
142150
143313
|
}
|
|
142151
143314
|
const SESSION_CONTROL_PATH$2 = "/session-control";
|
|
@@ -142160,6 +143323,8 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142160
143323
|
"local-project/list-files",
|
|
142161
143324
|
"local-project/read-file",
|
|
142162
143325
|
"local-project/checkout-branch",
|
|
143326
|
+
"local-project/sync-history",
|
|
143327
|
+
"local-project/import-history",
|
|
142163
143328
|
"worktree/list-files",
|
|
142164
143329
|
"worktree/read-file"
|
|
142165
143330
|
];
|
|
@@ -143437,6 +144602,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
143437
144602
|
localProjects: {
|
|
143438
144603
|
...existing,
|
|
143439
144604
|
[entry2.localProjectId]: {
|
|
144605
|
+
...previous ?? {},
|
|
143440
144606
|
id: entry2.localProjectId,
|
|
143441
144607
|
name: entry2.name,
|
|
143442
144608
|
rootPath: entry2.rootPath,
|
|
@@ -143618,6 +144784,43 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
143618
144784
|
result: this.localProjectControlService.checkoutProjectBranch(rootPath, message.branchName)
|
|
143619
144785
|
};
|
|
143620
144786
|
}
|
|
144787
|
+
if (message.type === "local-project/sync-history") {
|
|
144788
|
+
const runtime = await this.resolveWorkspaceRuntime(message.workspaceId);
|
|
144789
|
+
const rootPath = await this.resolveWorkspaceProjectRootPath(runtime, message.localProjectId);
|
|
144790
|
+
const service = new LocalProjectHistorySyncService(runtime.lody.documentManager, this.logger, {
|
|
144791
|
+
workspaceId: message.workspaceId,
|
|
144792
|
+
machineId: this.machineId,
|
|
144793
|
+
userId: this.userId
|
|
144794
|
+
}, message.provider);
|
|
144795
|
+
const result = await service.syncLocalProject({
|
|
144796
|
+
localProjectId: message.localProjectId,
|
|
144797
|
+
rootPath
|
|
144798
|
+
});
|
|
144799
|
+
return {
|
|
144800
|
+
ok: true,
|
|
144801
|
+
type: "local-project/sync-history",
|
|
144802
|
+
result
|
|
144803
|
+
};
|
|
144804
|
+
}
|
|
144805
|
+
if (message.type === "local-project/import-history") {
|
|
144806
|
+
const runtime = await this.resolveWorkspaceRuntime(message.workspaceId);
|
|
144807
|
+
const rootPath = await this.resolveWorkspaceProjectRootPath(runtime, message.localProjectId);
|
|
144808
|
+
const service = new LocalProjectHistorySyncService(runtime.lody.documentManager, this.logger, {
|
|
144809
|
+
workspaceId: message.workspaceId,
|
|
144810
|
+
machineId: this.machineId,
|
|
144811
|
+
userId: this.userId
|
|
144812
|
+
}, message.provider);
|
|
144813
|
+
const result = await service.importLocalProjectSessions({
|
|
144814
|
+
localProjectId: message.localProjectId,
|
|
144815
|
+
rootPath,
|
|
144816
|
+
acpSessionIds: message.acpSessionIds
|
|
144817
|
+
});
|
|
144818
|
+
return {
|
|
144819
|
+
ok: true,
|
|
144820
|
+
type: "local-project/import-history",
|
|
144821
|
+
result
|
|
144822
|
+
};
|
|
144823
|
+
}
|
|
143621
144824
|
if (message.type === "worktree/list-files") {
|
|
143622
144825
|
return {
|
|
143623
144826
|
ok: true,
|
|
@@ -175829,6 +177032,7 @@ ${result.stderr}`;
|
|
|
175829
177032
|
this.lastExitCode = result.code;
|
|
175830
177033
|
this.lastExitAtMs = Date.now();
|
|
175831
177034
|
this.publishState();
|
|
177035
|
+
if (!this.triggered) return;
|
|
175832
177036
|
if (this.restartPending) return;
|
|
175833
177037
|
if (isAlreadyRunningOutcome(result)) {
|
|
175834
177038
|
if (this.alreadyRunningIsFatal) {
|