lody 0.50.2 → 0.52.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 +1416 -186
- package/package.json +6 -6
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.52.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";
|
|
@@ -36864,8 +36865,8 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36864
36865
|
"node": ">=18.0.0"
|
|
36865
36866
|
};
|
|
36866
36867
|
const optionalDependencies = {
|
|
36867
|
-
"acp-extension-claude": "0.
|
|
36868
|
-
"acp-extension-codex": "0.14.
|
|
36868
|
+
"acp-extension-claude": "0.34.1",
|
|
36869
|
+
"acp-extension-codex": "0.14.3"
|
|
36869
36870
|
};
|
|
36870
36871
|
const devDependencies = {
|
|
36871
36872
|
"@agentclientprotocol/sdk": "catalog:",
|
|
@@ -37075,7 +37076,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
37075
37076
|
const runtimeEnv = getRuntimeEnv();
|
|
37076
37077
|
const environment$1 = "production";
|
|
37077
37078
|
const dsn = "https://080f9de535ff335a1a0440d0e385f796@o4510491299086336.ingest.us.sentry.io/4510559045681152";
|
|
37078
|
-
const postHogHost = process.env.LODY_POSTHOG_HOST ?? "https://
|
|
37079
|
+
const postHogHost = process.env.LODY_POSTHOG_HOST ?? "https://m.lody.ai";
|
|
37079
37080
|
const postHogKey = process.env.LODY_POSTHOG_KEY ?? "phc_LFS5i5WIwg4irAhrG5oJR04iYPhReVZ3DdFZOKqCkjG";
|
|
37080
37081
|
const tracesSampleRate = Number(process.env.SENTRY_TRACES_SAMPLE_RATE) || 0.2;
|
|
37081
37082
|
const profilesSampleRate = Number(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 0.1;
|
|
@@ -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) {
|
|
@@ -77780,6 +77781,9 @@ Task description:
|
|
|
77780
77781
|
}
|
|
77781
77782
|
return void 0;
|
|
77782
77783
|
};
|
|
77784
|
+
function getLocalProjectHistoryProviderKey(provider2) {
|
|
77785
|
+
return `${provider2.cliType}:${provider2.agentType}`;
|
|
77786
|
+
}
|
|
77783
77787
|
const DEFAULT_BASE_BRANCH = "main";
|
|
77784
77788
|
function getTrimmedBranch(value) {
|
|
77785
77789
|
if (typeof value !== "string") {
|
|
@@ -78661,6 +78665,27 @@ Task description:
|
|
|
78661
78665
|
localProjectId: LocalProjectIdSchema,
|
|
78662
78666
|
branchName: string$2()
|
|
78663
78667
|
}).strict();
|
|
78668
|
+
const LocalProjectHistoryProviderSchema = object$1({
|
|
78669
|
+
cliType: AgentConfigCliTypeSchema,
|
|
78670
|
+
agentType: string$2().trim().min(1)
|
|
78671
|
+
}).strict();
|
|
78672
|
+
const LocalProjectSyncHistoryRequestSchema = object$1({
|
|
78673
|
+
type: literal("local-project/sync-history"),
|
|
78674
|
+
machineId: MachineIdSchema,
|
|
78675
|
+
workspaceId: WorkspaceIdSchema,
|
|
78676
|
+
localProjectId: LocalProjectIdSchema,
|
|
78677
|
+
provider: LocalProjectHistoryProviderSchema,
|
|
78678
|
+
requestedByUserId: string$2().trim().min(1).optional()
|
|
78679
|
+
}).strict();
|
|
78680
|
+
const LocalProjectImportHistoryRequestSchema = object$1({
|
|
78681
|
+
type: literal("local-project/import-history"),
|
|
78682
|
+
machineId: MachineIdSchema,
|
|
78683
|
+
workspaceId: WorkspaceIdSchema,
|
|
78684
|
+
localProjectId: LocalProjectIdSchema,
|
|
78685
|
+
provider: LocalProjectHistoryProviderSchema,
|
|
78686
|
+
acpSessionIds: array$3(ACPSessionIdSchema),
|
|
78687
|
+
requestedByUserId: string$2().trim().min(1).optional()
|
|
78688
|
+
}).strict();
|
|
78664
78689
|
const WorktreeListFilesRequestSchema = object$1({
|
|
78665
78690
|
type: literal("worktree/list-files"),
|
|
78666
78691
|
machineId: MachineIdSchema,
|
|
@@ -78684,6 +78709,8 @@ Task description:
|
|
|
78684
78709
|
LocalProjectListFilesRequestSchema,
|
|
78685
78710
|
LocalProjectReadFileRequestSchema,
|
|
78686
78711
|
LocalProjectCheckoutBranchRequestSchema,
|
|
78712
|
+
LocalProjectSyncHistoryRequestSchema,
|
|
78713
|
+
LocalProjectImportHistoryRequestSchema,
|
|
78687
78714
|
WorktreeListFilesRequestSchema,
|
|
78688
78715
|
WorktreeReadFileRequestSchema
|
|
78689
78716
|
]);
|
|
@@ -78725,12 +78752,45 @@ Task description:
|
|
|
78725
78752
|
error: string$2()
|
|
78726
78753
|
}).strict()
|
|
78727
78754
|
]);
|
|
78755
|
+
const LocalProjectHistorySyncSummarySchema = object$1({
|
|
78756
|
+
listed: number$3().int().nonnegative(),
|
|
78757
|
+
imported: number$3().int().nonnegative(),
|
|
78758
|
+
refreshed: number$3().int().nonnegative(),
|
|
78759
|
+
skipped: number$3().int().nonnegative(),
|
|
78760
|
+
conflicted: number$3().int().nonnegative(),
|
|
78761
|
+
failed: number$3().int().nonnegative(),
|
|
78762
|
+
failures: array$3(object$1({
|
|
78763
|
+
acpSessionId: string$2(),
|
|
78764
|
+
message: string$2()
|
|
78765
|
+
}).strict())
|
|
78766
|
+
}).strict();
|
|
78767
|
+
const LocalProjectHistoryCatalogItemSchema = object$1({
|
|
78768
|
+
acpSessionId: string$2(),
|
|
78769
|
+
title: string$2(),
|
|
78770
|
+
updatedAt: string$2().optional(),
|
|
78771
|
+
importedSessionId: string$2().optional(),
|
|
78772
|
+
status: _enum$1([
|
|
78773
|
+
"available",
|
|
78774
|
+
"imported",
|
|
78775
|
+
"sync_conflict"
|
|
78776
|
+
]).optional()
|
|
78777
|
+
}).strict();
|
|
78778
|
+
const LocalProjectHistoryCatalogResultSchema = object$1({
|
|
78779
|
+
listed: number$3().int().nonnegative(),
|
|
78780
|
+
lastListedAt: number$3().int().nonnegative(),
|
|
78781
|
+
sessions: array$3(LocalProjectHistoryCatalogItemSchema)
|
|
78782
|
+
}).strict();
|
|
78783
|
+
const LocalProjectHistoryImportResultSchema = object$1({
|
|
78784
|
+
summary: LocalProjectHistorySyncSummarySchema,
|
|
78785
|
+
catalog: LocalProjectHistoryCatalogResultSchema
|
|
78786
|
+
}).strict();
|
|
78728
78787
|
const LocalProjectControlErrorCodeSchema = _enum$1([
|
|
78729
78788
|
"invalid_request",
|
|
78730
78789
|
"machine_mismatch",
|
|
78731
78790
|
"workspace_required",
|
|
78732
78791
|
"workspace_not_found",
|
|
78733
78792
|
"daemon_unavailable",
|
|
78793
|
+
"access_denied",
|
|
78734
78794
|
"local_project_not_found",
|
|
78735
78795
|
"path_invalid",
|
|
78736
78796
|
"execution_failed",
|
|
@@ -78746,6 +78806,8 @@ Task description:
|
|
|
78746
78806
|
"local-project/list-files",
|
|
78747
78807
|
"local-project/read-file",
|
|
78748
78808
|
"local-project/checkout-branch",
|
|
78809
|
+
"local-project/sync-history",
|
|
78810
|
+
"local-project/import-history",
|
|
78749
78811
|
"worktree/list-files",
|
|
78750
78812
|
"worktree/read-file"
|
|
78751
78813
|
]),
|
|
@@ -78809,6 +78871,16 @@ Task description:
|
|
|
78809
78871
|
type: literal("local-project/checkout-branch"),
|
|
78810
78872
|
result: LocalProjectCheckoutBranchResultSchema
|
|
78811
78873
|
}).strict(),
|
|
78874
|
+
object$1({
|
|
78875
|
+
ok: literal(true),
|
|
78876
|
+
type: literal("local-project/sync-history"),
|
|
78877
|
+
result: LocalProjectHistoryCatalogResultSchema
|
|
78878
|
+
}).strict(),
|
|
78879
|
+
object$1({
|
|
78880
|
+
ok: literal(true),
|
|
78881
|
+
type: literal("local-project/import-history"),
|
|
78882
|
+
result: LocalProjectHistoryImportResultSchema
|
|
78883
|
+
}).strict(),
|
|
78812
78884
|
object$1({
|
|
78813
78885
|
ok: literal(true),
|
|
78814
78886
|
type: literal("worktree/list-files"),
|
|
@@ -79127,7 +79199,7 @@ Task description:
|
|
|
79127
79199
|
"claude",
|
|
79128
79200
|
"codex"
|
|
79129
79201
|
]);
|
|
79130
|
-
function isRecord$
|
|
79202
|
+
function isRecord$5(value) {
|
|
79131
79203
|
return typeof value === "object" && value !== null;
|
|
79132
79204
|
}
|
|
79133
79205
|
function getTrimmedString(value) {
|
|
@@ -79144,7 +79216,7 @@ Task description:
|
|
|
79144
79216
|
return value === "builtin" || value === "registry";
|
|
79145
79217
|
}
|
|
79146
79218
|
function normalizeLegacyProjectRef(value) {
|
|
79147
|
-
if (!isRecord$
|
|
79219
|
+
if (!isRecord$5(value)) {
|
|
79148
79220
|
return value;
|
|
79149
79221
|
}
|
|
79150
79222
|
const kind = value.kind;
|
|
@@ -79160,13 +79232,13 @@ Task description:
|
|
|
79160
79232
|
normalized.branch = existingBranch;
|
|
79161
79233
|
} else {
|
|
79162
79234
|
const legacyBranchFromString = getTrimmedString(legacyProject);
|
|
79163
|
-
const legacyBranchFromObject = isRecord$
|
|
79235
|
+
const legacyBranchFromObject = isRecord$5(legacyProject) ? getTrimmedString(legacyProject.branch) ?? getTrimmedString(legacyProject.project) : void 0;
|
|
79164
79236
|
const resolvedBranch = legacyBranchFromString ?? legacyBranchFromObject;
|
|
79165
79237
|
if (resolvedBranch) {
|
|
79166
79238
|
normalized.branch = resolvedBranch;
|
|
79167
79239
|
}
|
|
79168
79240
|
}
|
|
79169
|
-
if (isRecord$
|
|
79241
|
+
if (isRecord$5(legacyProject)) {
|
|
79170
79242
|
if (kind === "github" && !getTrimmedString(normalized.repoFullName)) {
|
|
79171
79243
|
const repoFullName = getTrimmedString(legacyProject.repoFullName);
|
|
79172
79244
|
if (repoFullName) {
|
|
@@ -79203,7 +79275,7 @@ Task description:
|
|
|
79203
79275
|
};
|
|
79204
79276
|
normalized.project = normalizeLegacyProjectRef(normalized.project);
|
|
79205
79277
|
const currentProject = normalized.project;
|
|
79206
|
-
const projectRecord = isRecord$
|
|
79278
|
+
const projectRecord = isRecord$5(currentProject) ? currentProject : void 0;
|
|
79207
79279
|
const explicitBranch = getTrimmedString(normalized.branch) ?? getTrimmedString(currentProject) ?? (projectRecord ? getTrimmedString(projectRecord.branch) ?? getTrimmedString(projectRecord.project) : void 0);
|
|
79208
79280
|
const repoFullName = (projectRecord ? getTrimmedString(projectRecord.repoFullName) : void 0) ?? getTrimmedString(normalized.repoFullName) ?? getTrimmedString(normalized.githubRepo);
|
|
79209
79281
|
const localProjectId = (projectRecord ? getTrimmedString(projectRecord.localProjectId) : void 0) ?? getTrimmedString(normalized.localProjectId);
|
|
@@ -79246,7 +79318,7 @@ Task description:
|
|
|
79246
79318
|
return normalized;
|
|
79247
79319
|
}
|
|
79248
79320
|
function normalizeLegacyAcpSessionConfig(value) {
|
|
79249
|
-
if (!isRecord$
|
|
79321
|
+
if (!isRecord$5(value)) {
|
|
79250
79322
|
return value;
|
|
79251
79323
|
}
|
|
79252
79324
|
const normalized = {
|
|
@@ -79281,13 +79353,13 @@ Task description:
|
|
|
79281
79353
|
return normalized;
|
|
79282
79354
|
}
|
|
79283
79355
|
function normalizeLegacySessionMessage(parsed) {
|
|
79284
|
-
if (!isRecord$
|
|
79356
|
+
if (!isRecord$5(parsed)) {
|
|
79285
79357
|
return parsed;
|
|
79286
79358
|
}
|
|
79287
79359
|
const messageType = parsed.type;
|
|
79288
79360
|
if (messageType === "session/create" || messageType === "session/chat") {
|
|
79289
79361
|
const normalized = normalizeLegacySessionProject(parsed);
|
|
79290
|
-
if (!isRecord$
|
|
79362
|
+
if (!isRecord$5(normalized)) {
|
|
79291
79363
|
return normalized;
|
|
79292
79364
|
}
|
|
79293
79365
|
normalized.acpSessionConfig = normalizeLegacyAcpSessionConfig(normalized.acpSessionConfig);
|
|
@@ -81512,7 +81584,7 @@ Task description:
|
|
|
81512
81584
|
"read"
|
|
81513
81585
|
]);
|
|
81514
81586
|
const MAX_STORED_TERMINAL_OUTPUT_CHARS = 1024;
|
|
81515
|
-
const defaultCreateId = () => {
|
|
81587
|
+
const defaultCreateId$1 = () => {
|
|
81516
81588
|
const maybeCrypto = globalThis.crypto;
|
|
81517
81589
|
if (typeof maybeCrypto?.randomUUID === "function") {
|
|
81518
81590
|
return maybeCrypto.randomUUID();
|
|
@@ -82193,7 +82265,7 @@ Task description:
|
|
|
82193
82265
|
constructor(history, options, model) {
|
|
82194
82266
|
this.model = model;
|
|
82195
82267
|
this.history = history;
|
|
82196
|
-
this.createId = options.createId ?? defaultCreateId;
|
|
82268
|
+
this.createId = options.createId ?? defaultCreateId$1;
|
|
82197
82269
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
82198
82270
|
this.parsedItemsByEntryIndex = Array.from({
|
|
82199
82271
|
length: history.length
|
|
@@ -82445,28 +82517,149 @@ Task description:
|
|
|
82445
82517
|
const applyNotificationOnHistory = (history, notifications, model, options = {}) => {
|
|
82446
82518
|
return new NotificationOnHistoryApplier(history, options, model).apply(notifications);
|
|
82447
82519
|
};
|
|
82448
|
-
const
|
|
82520
|
+
const defaultNow = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
82521
|
+
const defaultCreateId = () => {
|
|
82522
|
+
const maybeCrypto = globalThis.crypto;
|
|
82523
|
+
if (typeof maybeCrypto?.randomUUID === "function") {
|
|
82524
|
+
return maybeCrypto.randomUUID();
|
|
82525
|
+
}
|
|
82526
|
+
return `history-replay-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
82527
|
+
};
|
|
82528
|
+
function textFromContentBlock(content) {
|
|
82529
|
+
if (!content || content.type !== "text") {
|
|
82530
|
+
return null;
|
|
82531
|
+
}
|
|
82532
|
+
return content.text;
|
|
82533
|
+
}
|
|
82534
|
+
function appendUserText(entry2, text) {
|
|
82535
|
+
const items2 = Array.isArray(entry2.items) ? [
|
|
82536
|
+
...entry2.items
|
|
82537
|
+
] : [];
|
|
82538
|
+
const last2 = items2[items2.length - 1];
|
|
82539
|
+
if (last2?.type === "text") {
|
|
82540
|
+
items2[items2.length - 1] = {
|
|
82541
|
+
...last2,
|
|
82542
|
+
text: `${last2.text}${text}`
|
|
82543
|
+
};
|
|
82544
|
+
} else {
|
|
82545
|
+
items2.push({
|
|
82546
|
+
type: "text",
|
|
82547
|
+
text
|
|
82548
|
+
});
|
|
82549
|
+
}
|
|
82550
|
+
return {
|
|
82551
|
+
...entry2,
|
|
82552
|
+
items: items2,
|
|
82553
|
+
inputConfig: entry2.inputConfig ? {
|
|
82554
|
+
...entry2.inputConfig,
|
|
82555
|
+
prompt: `${entry2.inputConfig.prompt ?? ""}${text}`
|
|
82556
|
+
} : entry2.inputConfig
|
|
82557
|
+
};
|
|
82558
|
+
}
|
|
82559
|
+
function createUserEntry(args2) {
|
|
82560
|
+
const inputConfig = {
|
|
82561
|
+
prompt: args2.text,
|
|
82562
|
+
cliType: args2.provider.cliType,
|
|
82563
|
+
agentType: args2.provider.agentType,
|
|
82564
|
+
...{}
|
|
82565
|
+
};
|
|
82566
|
+
return {
|
|
82567
|
+
id: args2.id,
|
|
82568
|
+
role: "user",
|
|
82569
|
+
items: [
|
|
82570
|
+
{
|
|
82571
|
+
type: "text",
|
|
82572
|
+
text: args2.text
|
|
82573
|
+
}
|
|
82574
|
+
],
|
|
82575
|
+
timestamp: args2.timestamp,
|
|
82576
|
+
status: args2.mode === "resumable" ? "seen" : "handled",
|
|
82577
|
+
read: true,
|
|
82578
|
+
userId: args2.userId,
|
|
82579
|
+
finished: true,
|
|
82580
|
+
fileDiff: [],
|
|
82581
|
+
inputConfig
|
|
82582
|
+
};
|
|
82583
|
+
}
|
|
82584
|
+
function buildHistoryReplayImport(notifications, options) {
|
|
82585
|
+
const now2 = options.now ?? defaultNow;
|
|
82586
|
+
const createId = options.createId ?? defaultCreateId;
|
|
82587
|
+
const mode2 = options.mode;
|
|
82588
|
+
const provider2 = options.provider;
|
|
82589
|
+
let history = [];
|
|
82590
|
+
let lastWasUserChunk = false;
|
|
82591
|
+
let droppedNotifications = 0;
|
|
82592
|
+
for (const notification of notifications) {
|
|
82593
|
+
if (notification.update.sessionUpdate === "user_message_chunk") {
|
|
82594
|
+
const text = textFromContentBlock(notification.update.content);
|
|
82595
|
+
if (text === null) {
|
|
82596
|
+
droppedNotifications += 1;
|
|
82597
|
+
lastWasUserChunk = false;
|
|
82598
|
+
continue;
|
|
82599
|
+
}
|
|
82600
|
+
const lastIndex = history.length - 1;
|
|
82601
|
+
const last2 = lastIndex >= 0 ? history[lastIndex] : void 0;
|
|
82602
|
+
if (lastWasUserChunk && last2?.role === "user") {
|
|
82603
|
+
history[lastIndex] = appendUserText(last2, text);
|
|
82604
|
+
} else {
|
|
82605
|
+
history.push(createUserEntry({
|
|
82606
|
+
id: createId(),
|
|
82607
|
+
text,
|
|
82608
|
+
timestamp: now2(),
|
|
82609
|
+
userId: options.userId,
|
|
82610
|
+
provider: provider2,
|
|
82611
|
+
mode: mode2
|
|
82612
|
+
}));
|
|
82613
|
+
}
|
|
82614
|
+
lastWasUserChunk = true;
|
|
82615
|
+
continue;
|
|
82616
|
+
}
|
|
82617
|
+
const beforeLength = history.length;
|
|
82618
|
+
const beforeSnapshot = JSON.stringify(history);
|
|
82619
|
+
history = applyNotificationOnHistory(history, [
|
|
82620
|
+
notification
|
|
82621
|
+
], void 0, {
|
|
82622
|
+
createId,
|
|
82623
|
+
now: now2
|
|
82624
|
+
});
|
|
82625
|
+
if (history.length === beforeLength && JSON.stringify(history) === beforeSnapshot) {
|
|
82626
|
+
const updateType = notification.update.sessionUpdate;
|
|
82627
|
+
if (updateType !== "session_info_update" && updateType !== "current_mode_update" && updateType !== "config_option_update" && updateType !== "usage_update" && updateType !== "available_commands_update") {
|
|
82628
|
+
droppedNotifications += 1;
|
|
82629
|
+
}
|
|
82630
|
+
}
|
|
82631
|
+
lastWasUserChunk = false;
|
|
82632
|
+
}
|
|
82633
|
+
return {
|
|
82634
|
+
history: history.map((entry2) => entry2.role === "assistant" ? {
|
|
82635
|
+
...entry2,
|
|
82636
|
+
finished: entry2.finished ?? true
|
|
82637
|
+
} : entry2),
|
|
82638
|
+
droppedNotifications
|
|
82639
|
+
};
|
|
82640
|
+
}
|
|
82641
|
+
const isRecord$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82449
82642
|
const getClaudeCodeMeta = (meta) => {
|
|
82450
|
-
if (!isRecord$
|
|
82643
|
+
if (!isRecord$4(meta)) return null;
|
|
82451
82644
|
const claudeCode = meta.claudeCode;
|
|
82452
|
-
return isRecord$
|
|
82645
|
+
return isRecord$4(claudeCode) ? claudeCode : null;
|
|
82453
82646
|
};
|
|
82454
82647
|
function parseAskUserQuestionPermissionMeta(meta) {
|
|
82455
82648
|
const claudeCode = getClaudeCodeMeta(meta);
|
|
82456
82649
|
if (!claudeCode) return null;
|
|
82457
82650
|
const raw = claudeCode.askUserQuestion;
|
|
82458
|
-
if (!isRecord$
|
|
82651
|
+
if (!isRecord$4(raw)) return null;
|
|
82459
82652
|
const rawQuestions = raw.questions;
|
|
82460
82653
|
if (!Array.isArray(rawQuestions) || rawQuestions.length === 0) return null;
|
|
82461
82654
|
const questions = [];
|
|
82462
82655
|
for (const rawQuestion of rawQuestions) {
|
|
82463
|
-
if (!isRecord$
|
|
82656
|
+
if (!isRecord$4(rawQuestion)) return null;
|
|
82464
82657
|
if (typeof rawQuestion.question !== "string") return null;
|
|
82465
82658
|
if (typeof rawQuestion.header !== "string") return null;
|
|
82466
82659
|
if (!Array.isArray(rawQuestion.options)) return null;
|
|
82467
82660
|
const options = [];
|
|
82468
82661
|
for (const rawOption of rawQuestion.options) {
|
|
82469
|
-
if (!isRecord$
|
|
82662
|
+
if (!isRecord$4(rawOption)) return null;
|
|
82470
82663
|
if (typeof rawOption.label !== "string") return null;
|
|
82471
82664
|
options.push({
|
|
82472
82665
|
label: rawOption.label,
|
|
@@ -85171,7 +85364,7 @@ ${tailedOutput}` : null;
|
|
|
85171
85364
|
];
|
|
85172
85365
|
const buildPreviewTunnelRefreshPath = (tunnelId) => `${PREVIEW_TUNNELS_API_PATH}/${encodeURIComponent(tunnelId)}/refresh`;
|
|
85173
85366
|
const buildPreviewTunnelRevokePath = (tunnelId) => `${PREVIEW_TUNNELS_API_PATH}/${encodeURIComponent(tunnelId)}/revoke`;
|
|
85174
|
-
const isRecord$
|
|
85367
|
+
const isRecord$3 = (value) => typeof value === "object" && value !== null;
|
|
85175
85368
|
const isString$2 = (value) => typeof value === "string";
|
|
85176
85369
|
const isOptionalNumber = (value) => value === void 0 || typeof value === "number";
|
|
85177
85370
|
const isOptionalBoolean = (value) => value === void 0 || typeof value === "boolean";
|
|
@@ -85179,11 +85372,11 @@ ${tailedOutput}` : null;
|
|
|
85179
85372
|
const isStringArray$1 = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
85180
85373
|
const isHeaderEntries = (value) => Array.isArray(value) && value.every((entry2) => Array.isArray(entry2) && entry2.length === 2 && typeof entry2[0] === "string" && typeof entry2[1] === "string");
|
|
85181
85374
|
const isPreviewTunnelBinaryPayloadStream = (value) => value === "request-body" || value === "response-body" || value === "websocket-frame";
|
|
85182
|
-
const isPreviewResourceLimits = (value) => isRecord$
|
|
85375
|
+
const isPreviewResourceLimits = (value) => isRecord$3(value) && isPositiveInteger(value.maxRequestBodyBytes) && isPositiveInteger(value.maxResponseBodyBytes) && isPositiveInteger(value.maxRequestDurationMs);
|
|
85183
85376
|
const parseJsonRecord = (raw) => {
|
|
85184
85377
|
try {
|
|
85185
85378
|
const parsed = JSON.parse(raw);
|
|
85186
|
-
return isRecord$
|
|
85379
|
+
return isRecord$3(parsed) ? parsed : null;
|
|
85187
85380
|
} catch {
|
|
85188
85381
|
return null;
|
|
85189
85382
|
}
|
|
@@ -85222,8 +85415,8 @@ ${tailedOutput}` : null;
|
|
|
85222
85415
|
const parsed = parseJsonRecord(raw);
|
|
85223
85416
|
return parsed && isPreviewTunnelServerMessage(parsed) ? parsed : null;
|
|
85224
85417
|
};
|
|
85225
|
-
const isPreviewTunnelCreateResponse = (value) => isRecord$
|
|
85226
|
-
const isPreviewTunnelRefreshResponse = (value) => isRecord$
|
|
85418
|
+
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));
|
|
85419
|
+
const isPreviewTunnelRefreshResponse = (value) => isRecord$3(value) && isString$2(value.websocketUrl) && isString$2(value.sessionToken) && typeof value.expiresAt === "number";
|
|
85227
85420
|
const LOCAL_PROBE_PORT$1 = 17789;
|
|
85228
85421
|
const LOCAL_SESSION_CONTROL_PORT = 17790;
|
|
85229
85422
|
const IMAGE_UPLOAD_PATH = "/image-upload";
|
|
@@ -90150,18 +90343,18 @@ ${val.stack}`;
|
|
|
90150
90343
|
}
|
|
90151
90344
|
return next;
|
|
90152
90345
|
}
|
|
90153
|
-
function isRecord$
|
|
90346
|
+
function isRecord$2(value) {
|
|
90154
90347
|
return typeof value === "object" && value !== null;
|
|
90155
90348
|
}
|
|
90156
90349
|
function normalizeRecoveryReport(raw) {
|
|
90157
|
-
if (!isRecord$
|
|
90350
|
+
if (!isRecord$2(raw) || !Array.isArray(raw.skipped)) {
|
|
90158
90351
|
return {
|
|
90159
90352
|
skipped: []
|
|
90160
90353
|
};
|
|
90161
90354
|
}
|
|
90162
90355
|
return {
|
|
90163
90356
|
skipped: raw.skipped.flatMap((entry2) => {
|
|
90164
|
-
if (!isRecord$
|
|
90357
|
+
if (!isRecord$2(entry2)) {
|
|
90165
90358
|
return [];
|
|
90166
90359
|
}
|
|
90167
90360
|
const key2 = Array.isArray(entry2.key) ? cloneJson(entry2.key) : void 0;
|
|
@@ -99013,7 +99206,7 @@ stream:${scope2.streamId}`;
|
|
|
99013
99206
|
}
|
|
99014
99207
|
return parsed;
|
|
99015
99208
|
};
|
|
99016
|
-
const withTimeout$
|
|
99209
|
+
const withTimeout$3 = async (promise, timeoutMs, message) => {
|
|
99017
99210
|
if (timeoutMs <= 0) {
|
|
99018
99211
|
return promise;
|
|
99019
99212
|
}
|
|
@@ -105853,7 +106046,7 @@ stream:${scope2.streamId}`;
|
|
|
105853
106046
|
const timeoutMs = options.timeoutMs ?? readTimeoutEnv("LODY_LORO_WAIT_CODE_SESSION_SYNC_TIMEOUT_MS", readTimeoutEnv("LODY_LORO_WAIT_DOC_SYNC_TIMEOUT_MS", 4e3));
|
|
105854
106047
|
const timeoutMessage = `Timeout waiting for code session pending writes (session=${this.sessionId})`;
|
|
105855
106048
|
try {
|
|
105856
|
-
await withTimeout$
|
|
106049
|
+
await withTimeout$3(this.subscription.waitUntilSynced(), timeoutMs, timeoutMessage);
|
|
105857
106050
|
return true;
|
|
105858
106051
|
} catch {
|
|
105859
106052
|
return false;
|
|
@@ -115733,7 +115926,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
115733
115926
|
this.logger.debug(`[${this.workspaceId}] Triggering Loro streams reconnect (reason=${reason}, transport=${this.transportStatus}, metaRoom=${this.metaRoomStatus ?? "unknown"})`);
|
|
115734
115927
|
}
|
|
115735
115928
|
await this.ensureMetaRoomJoined(reason);
|
|
115736
|
-
await withTimeout$
|
|
115929
|
+
await withTimeout$3(this.repo.reconnect({
|
|
115737
115930
|
resetBackoff: true,
|
|
115738
115931
|
timeout: timeoutMs
|
|
115739
115932
|
}), timeoutMs, `Timeout waiting for Loro streams reconnect (workspace=${this.workspaceId})`);
|
|
@@ -115755,7 +115948,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
115755
115948
|
const joinMetaTimeoutMs = readTimeoutEnv("LODY_LORO_JOIN_META_TIMEOUT_MS", 3e4);
|
|
115756
115949
|
const startedAt = Date.now();
|
|
115757
115950
|
this.logger.debug(`[${this.workspaceId}] Joining Loro meta room (reason=${reason})`);
|
|
115758
|
-
const metaSub = await withTimeout$
|
|
115951
|
+
const metaSub = await withTimeout$3(this.repo.joinMetaRoom(), joinMetaTimeoutMs, `Timeout waiting for repo.joinMetaRoom during reconnect (workspace=${this.workspaceId})`);
|
|
115759
115952
|
this.attachMetaRoomStatusLogger(metaSub);
|
|
115760
115953
|
this.logger.debug(`[${this.workspaceId}] Meta room join returned in ${Date.now() - startedAt}ms (reason=${reason})`);
|
|
115761
115954
|
}
|
|
@@ -115767,7 +115960,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
115767
115960
|
const startedAt = Date.now();
|
|
115768
115961
|
try {
|
|
115769
115962
|
const syncPromise = this.initialMetaSyncCompleted ? metaSub.waitUntilSynced() : metaSub.firstSyncedWithRemote;
|
|
115770
|
-
await withTimeout$
|
|
115963
|
+
await withTimeout$3(syncPromise, syncMetaTimeoutMs, `Timeout waiting for Loro meta room sync (workspace=${this.workspaceId})`);
|
|
115771
115964
|
if (this.metaSub !== metaSub || this.isCleanedUp) {
|
|
115772
115965
|
return;
|
|
115773
115966
|
}
|
|
@@ -116689,7 +116882,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116689
116882
|
});
|
|
116690
116883
|
try {
|
|
116691
116884
|
const createRepoStartMs = Date.now();
|
|
116692
|
-
repo = await withTimeout$
|
|
116885
|
+
repo = await withTimeout$3(LoroRepo.create({
|
|
116693
116886
|
storageAdapter: new FileSystemStorageAdaptor({
|
|
116694
116887
|
baseDir: storageBaseDir
|
|
116695
116888
|
}),
|
|
@@ -116707,7 +116900,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116707
116900
|
}
|
|
116708
116901
|
try {
|
|
116709
116902
|
const joinMetaStartMs = Date.now();
|
|
116710
|
-
metaSub = await withTimeout$
|
|
116903
|
+
metaSub = await withTimeout$3(repo.joinMetaRoom(), joinMetaTimeoutMs, "Timeout waiting for repo.joinMetaRoom");
|
|
116711
116904
|
logger2.debug(`[${workspaceId}] Meta room join returned in ${Date.now() - joinMetaStartMs}ms`);
|
|
116712
116905
|
} catch (error2) {
|
|
116713
116906
|
logger2.debug(`[${workspaceId}] Failed to join Loro meta room; continuing without initial meta join: ${formatErrorMessage(error2)}`);
|
|
@@ -116724,7 +116917,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116724
116917
|
});
|
|
116725
116918
|
try {
|
|
116726
116919
|
const syncStartMs = Date.now();
|
|
116727
|
-
await withTimeout$
|
|
116920
|
+
await withTimeout$3(metaSub.firstSyncedWithRemote, syncMetaTimeoutMs, initialMetaSyncTimeoutMessage);
|
|
116728
116921
|
initialMetaSyncCompleted = true;
|
|
116729
116922
|
logger2.debug(`[${workspaceId}] Meta room synced in ${Date.now() - syncStartMs}ms`);
|
|
116730
116923
|
} catch (error2) {
|
|
@@ -116772,7 +116965,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116772
116965
|
}
|
|
116773
116966
|
const timeoutMessage = `Timeout waiting for initial meta sync (workspace=${this.workspaceId})`;
|
|
116774
116967
|
try {
|
|
116775
|
-
return await withTimeout$
|
|
116968
|
+
return await withTimeout$3(this.initialMetaSyncPromise, timeoutMs, timeoutMessage);
|
|
116776
116969
|
} catch (error2) {
|
|
116777
116970
|
if (error2 instanceof Error && error2.message === timeoutMessage) {
|
|
116778
116971
|
return false;
|
|
@@ -116795,7 +116988,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
116795
116988
|
const destroyTimeoutMs = readTimeoutEnv("LODY_LORO_DESTROY_TIMEOUT_MS", 1e3);
|
|
116796
116989
|
const timeoutMessage = `Timeout waiting for repo.destroy (workspace=${this.workspaceId})`;
|
|
116797
116990
|
try {
|
|
116798
|
-
await withTimeout$
|
|
116991
|
+
await withTimeout$3(repoDestroyPromise, destroyTimeoutMs, timeoutMessage);
|
|
116799
116992
|
} catch (error2) {
|
|
116800
116993
|
if (!(error2 instanceof Error) || error2.message !== timeoutMessage) {
|
|
116801
116994
|
throw error2;
|
|
@@ -117271,7 +117464,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
117271
117464
|
this.docSub = joinedSub;
|
|
117272
117465
|
const syncDocTimeoutMs = readTimeoutEnv("LODY_LORO_SYNC_DOC_TIMEOUT_MS", 8e3);
|
|
117273
117466
|
const timeoutMessage = `Timeout waiting for session doc initial sync (room=${this.roomId})`;
|
|
117274
|
-
await withTimeout$
|
|
117467
|
+
await withTimeout$3(joinedSub.firstSyncedWithRemote, syncDocTimeoutMs, timeoutMessage);
|
|
117275
117468
|
return;
|
|
117276
117469
|
} catch (error2) {
|
|
117277
117470
|
const errMsg = formatErrorMessage(error2);
|
|
@@ -117333,7 +117526,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
117333
117526
|
const timeoutMs = options.timeoutMs ?? readTimeoutEnv("LODY_LORO_WAIT_DOC_SYNC_TIMEOUT_MS", 4e3);
|
|
117334
117527
|
const timeoutMessage = `Timeout waiting for session doc pending writes (room=${this.roomId})`;
|
|
117335
117528
|
try {
|
|
117336
|
-
await withTimeout$
|
|
117529
|
+
await withTimeout$3(sub.waitUntilSynced(), timeoutMs, timeoutMessage);
|
|
117337
117530
|
return true;
|
|
117338
117531
|
} catch (error2) {
|
|
117339
117532
|
this.logger.debug(`[${this.sessionId}] Session doc pending writes were not confirmed before continuing: ${formatErrorMessage(error2)}`);
|
|
@@ -119481,7 +119674,8 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
119481
119674
|
"machine/status",
|
|
119482
119675
|
"machine/acp-capabilities-refresh",
|
|
119483
119676
|
"session/preview-create",
|
|
119484
|
-
"session/preview-revoke"
|
|
119677
|
+
"session/preview-revoke",
|
|
119678
|
+
"local-project/control"
|
|
119485
119679
|
]);
|
|
119486
119680
|
const LoroStreamsRpcErrorSchema = object$1({
|
|
119487
119681
|
code: string$2().trim().min(1),
|
|
@@ -119527,11 +119721,18 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
119527
119721
|
reason: string$2().trim().min(1).optional()
|
|
119528
119722
|
}).strict()
|
|
119529
119723
|
}).strict();
|
|
119724
|
+
const LoroLocalProjectControlRpcRequestSchema = BaseRpcRequestSchema.extend({
|
|
119725
|
+
method: literal("local-project/control"),
|
|
119726
|
+
params: object$1({
|
|
119727
|
+
request: LocalProjectControlRequestSchema
|
|
119728
|
+
}).strict()
|
|
119729
|
+
}).strict();
|
|
119530
119730
|
const LoroStreamsRpcRequestSchema = discriminatedUnion("method", [
|
|
119531
119731
|
LoroMachineStatusRpcRequestSchema,
|
|
119532
119732
|
LoroMachineAcpCapabilitiesRefreshRpcRequestSchema,
|
|
119533
119733
|
LoroSessionPreviewCreateRpcRequestSchema,
|
|
119534
|
-
LoroSessionPreviewRevokeRpcRequestSchema
|
|
119734
|
+
LoroSessionPreviewRevokeRpcRequestSchema,
|
|
119735
|
+
LoroLocalProjectControlRpcRequestSchema
|
|
119535
119736
|
]);
|
|
119536
119737
|
object$1({
|
|
119537
119738
|
jsonrpc: literal(JSON_RPC_VERSION$1),
|
|
@@ -119870,6 +120071,18 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
119870
120071
|
await this.appendResultResponse(request.replyTo, request.id, request.method, response);
|
|
119871
120072
|
return;
|
|
119872
120073
|
}
|
|
120074
|
+
case "local-project/control": {
|
|
120075
|
+
if (!this.deps.dispatchLocalProjectControl) {
|
|
120076
|
+
await this.appendErrorResponse(request.replyTo, request.id, request.method, {
|
|
120077
|
+
code: "method_unavailable",
|
|
120078
|
+
message: "Local project control is not available on this machine."
|
|
120079
|
+
});
|
|
120080
|
+
return;
|
|
120081
|
+
}
|
|
120082
|
+
const response = await this.deps.dispatchLocalProjectControl(request.params.request);
|
|
120083
|
+
await this.appendResultResponse(request.replyTo, request.id, request.method, response);
|
|
120084
|
+
return;
|
|
120085
|
+
}
|
|
119873
120086
|
}
|
|
119874
120087
|
} catch (error2) {
|
|
119875
120088
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -120904,6 +121117,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
120904
121117
|
nes_reject: "nes/reject",
|
|
120905
121118
|
nes_start: "nes/start",
|
|
120906
121119
|
nes_suggest: "nes/suggest",
|
|
121120
|
+
providers_disable: "providers/disable",
|
|
121121
|
+
providers_list: "providers/list",
|
|
121122
|
+
providers_set: "providers/set",
|
|
120907
121123
|
session_cancel: "session/cancel",
|
|
120908
121124
|
session_close: "session/close",
|
|
120909
121125
|
session_fork: "session/fork",
|
|
@@ -121105,6 +121321,15 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121105
121321
|
async authenticate(params) {
|
|
121106
121322
|
return await this.connection.sendRequest(AGENT_METHODS.authenticate, params) ?? {};
|
|
121107
121323
|
}
|
|
121324
|
+
async unstable_listProviders(params) {
|
|
121325
|
+
return await this.connection.sendRequest(AGENT_METHODS.providers_list, params);
|
|
121326
|
+
}
|
|
121327
|
+
async unstable_setProvider(params) {
|
|
121328
|
+
return await this.connection.sendRequest(AGENT_METHODS.providers_set, params) ?? {};
|
|
121329
|
+
}
|
|
121330
|
+
async unstable_disableProvider(params) {
|
|
121331
|
+
return await this.connection.sendRequest(AGENT_METHODS.providers_disable, params) ?? {};
|
|
121332
|
+
}
|
|
121108
121333
|
async unstable_logout(params) {
|
|
121109
121334
|
return await this.connection.sendRequest(AGENT_METHODS.logout, params) ?? {};
|
|
121110
121335
|
}
|
|
@@ -121455,7 +121680,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121455
121680
|
this.name = "AcpTimeoutError";
|
|
121456
121681
|
}
|
|
121457
121682
|
}
|
|
121458
|
-
function withTimeout$
|
|
121683
|
+
function withTimeout$2(promise, logger2, operationName, sessionId, timeoutMs, warningIntervalMs = 1e4) {
|
|
121459
121684
|
let completed = false;
|
|
121460
121685
|
let elapsedMs = 0;
|
|
121461
121686
|
let timeoutHandle;
|
|
@@ -121849,7 +122074,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121849
122074
|
type: "initialize_start"
|
|
121850
122075
|
});
|
|
121851
122076
|
const ACP_INIT_TIMEOUT_MS = Math.max(0, timeoutOptions.initTimeoutMs ?? 12e4);
|
|
121852
|
-
const initResponse = await withTimeout$
|
|
122077
|
+
const initResponse = await withTimeout$2(withAbort(connection.initialize({
|
|
121853
122078
|
protocolVersion: PROTOCOL_VERSION,
|
|
121854
122079
|
clientCapabilities: {
|
|
121855
122080
|
terminal: this.terminalEnabled,
|
|
@@ -121957,7 +122182,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
121957
122182
|
} else {
|
|
121958
122183
|
this.logger.debug(`[${this.options.sessionId}] Calling connection.newSession (cwd=${workdir})`);
|
|
121959
122184
|
const ACP_NEW_SESSION_TIMEOUT_MS = Math.max(0, timeoutOptions.newSessionTimeoutMs ?? 12e4);
|
|
121960
|
-
sessionResponse = await withTimeout$
|
|
122185
|
+
sessionResponse = await withTimeout$2(withAbort(connection.newSession({
|
|
121961
122186
|
cwd: workdir,
|
|
121962
122187
|
mcpServers
|
|
121963
122188
|
}), startupAbort), this.logger, "connection.newSession", this.options.sessionId, ACP_NEW_SESSION_TIMEOUT_MS);
|
|
@@ -122070,7 +122295,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122070
122295
|
return false;
|
|
122071
122296
|
}
|
|
122072
122297
|
this.logger.debug(`[${this.options.sessionId}] Closing ACP session (acpSessionId=${sessionId} timeoutMs=${timeoutMs})`);
|
|
122073
|
-
await withTimeout$
|
|
122298
|
+
await withTimeout$2(closeSession2.call(this.connection, {
|
|
122074
122299
|
sessionId
|
|
122075
122300
|
}), this.logger, "connection.closeSession", this.options.sessionId, timeoutMs, Math.min(timeoutMs, 1e3));
|
|
122076
122301
|
this.logger.debug(`[${this.options.sessionId}] ACP session close finished (acpSessionId=${sessionId})`);
|
|
@@ -122203,12 +122428,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122203
122428
|
const BuiltinACPSetting = {
|
|
122204
122429
|
claude: {
|
|
122205
122430
|
packageName: "acp-extension-claude",
|
|
122206
|
-
version: "0.
|
|
122431
|
+
version: "0.34.1",
|
|
122207
122432
|
binName: "acp-extension-claude"
|
|
122208
122433
|
},
|
|
122209
122434
|
codex: {
|
|
122210
122435
|
packageName: "acp-extension-codex",
|
|
122211
|
-
version: "0.14.
|
|
122436
|
+
version: "0.14.3",
|
|
122212
122437
|
binName: "acp-extension-codex",
|
|
122213
122438
|
args: [
|
|
122214
122439
|
"-c",
|
|
@@ -122228,12 +122453,50 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122228
122453
|
"claude",
|
|
122229
122454
|
"codex"
|
|
122230
122455
|
]);
|
|
122456
|
+
const moduleRequire = createRequire$1(import.meta.url);
|
|
122457
|
+
function isRecord$1(value) {
|
|
122458
|
+
return typeof value === "object" && value !== null;
|
|
122459
|
+
}
|
|
122460
|
+
function resolveInstalledPackageBin(setting) {
|
|
122461
|
+
try {
|
|
122462
|
+
const packageJsonPath = moduleRequire.resolve(`${setting.packageName}/package.json`);
|
|
122463
|
+
const packageJson = JSON.parse(readFileSync$1(packageJsonPath, "utf8"));
|
|
122464
|
+
if (!isRecord$1(packageJson) || packageJson.version !== setting.version) {
|
|
122465
|
+
return void 0;
|
|
122466
|
+
}
|
|
122467
|
+
const bin2 = packageJson.bin;
|
|
122468
|
+
const namedBin = isRecord$1(bin2) ? bin2[setting.binName] : void 0;
|
|
122469
|
+
const relativeBin = typeof bin2 === "string" ? bin2 : typeof namedBin === "string" ? namedBin : void 0;
|
|
122470
|
+
if (!relativeBin) {
|
|
122471
|
+
return void 0;
|
|
122472
|
+
}
|
|
122473
|
+
const binPath = resolve$2(dirname$1(packageJsonPath), relativeBin);
|
|
122474
|
+
return existsSync(binPath) ? binPath : void 0;
|
|
122475
|
+
} catch {
|
|
122476
|
+
return void 0;
|
|
122477
|
+
}
|
|
122478
|
+
}
|
|
122231
122479
|
function resolveBuiltinACPSetting(agentType) {
|
|
122232
122480
|
if (!builtinTypeSet.has(agentType)) {
|
|
122233
122481
|
throw new Error(`Unsupported builtin ACP type: ${agentType}`);
|
|
122234
122482
|
}
|
|
122235
122483
|
const builtinType = agentType;
|
|
122236
122484
|
const setting = BuiltinACPSetting[builtinType];
|
|
122485
|
+
if (builtinType === "claude") {
|
|
122486
|
+
const localClaudeAcpPath = resolveInstalledPackageBin(setting);
|
|
122487
|
+
if (localClaudeAcpPath) {
|
|
122488
|
+
return {
|
|
122489
|
+
status: {
|
|
122490
|
+
agent: `local ${setting.packageName}@${setting.version}`,
|
|
122491
|
+
command: localClaudeAcpPath
|
|
122492
|
+
},
|
|
122493
|
+
exec: {
|
|
122494
|
+
command: localClaudeAcpPath,
|
|
122495
|
+
args: setting.args ?? []
|
|
122496
|
+
}
|
|
122497
|
+
};
|
|
122498
|
+
}
|
|
122499
|
+
}
|
|
122237
122500
|
const agent = `${setting.packageName}@${setting.version}`;
|
|
122238
122501
|
const packageSpec = `${setting.packageName}@${setting.version}`;
|
|
122239
122502
|
const args2 = [
|
|
@@ -122270,11 +122533,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122270
122533
|
}
|
|
122271
122534
|
return `${agent.id}@${agent.version}`;
|
|
122272
122535
|
}
|
|
122273
|
-
function
|
|
122274
|
-
const agent = registryAgentsById[agentType];
|
|
122275
|
-
if (!agent) {
|
|
122276
|
-
throw new Error(`Unknown registry ACP type: ${agentType}`);
|
|
122277
|
-
}
|
|
122536
|
+
function resolveRegistryAgentACPSetting(agent) {
|
|
122278
122537
|
if (agent.distribution.local?.command) {
|
|
122279
122538
|
const isNpx = agent.distribution.local.command === "npx";
|
|
122280
122539
|
const args2 = [
|
|
@@ -122331,7 +122590,14 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122331
122590
|
}
|
|
122332
122591
|
};
|
|
122333
122592
|
}
|
|
122334
|
-
throw new Error(`Registry ACP ${
|
|
122593
|
+
throw new Error(`Registry ACP ${agent.id} has no supported launcher`);
|
|
122594
|
+
}
|
|
122595
|
+
function resolveRegistryACPSetting(agentType) {
|
|
122596
|
+
const agent = registryAgentsById[agentType];
|
|
122597
|
+
if (!agent) {
|
|
122598
|
+
throw new Error(`Unknown registry ACP type: ${agentType}`);
|
|
122599
|
+
}
|
|
122600
|
+
return resolveRegistryAgentACPSetting(agent);
|
|
122335
122601
|
}
|
|
122336
122602
|
function resolveACPSetting(input2) {
|
|
122337
122603
|
if (input2.cliType === "builtin") {
|
|
@@ -122542,7 +122808,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122542
122808
|
sessionResponse
|
|
122543
122809
|
};
|
|
122544
122810
|
};
|
|
122545
|
-
function waitForChildProcessExit(child, timeoutMs) {
|
|
122811
|
+
function waitForChildProcessExit$1(child, timeoutMs) {
|
|
122546
122812
|
if (child.exitCode !== null) {
|
|
122547
122813
|
return Promise.resolve(true);
|
|
122548
122814
|
}
|
|
@@ -122563,32 +122829,32 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122563
122829
|
child.once("exit", onExit2);
|
|
122564
122830
|
});
|
|
122565
122831
|
}
|
|
122566
|
-
function signalChildProcess(child, signal) {
|
|
122832
|
+
function signalChildProcess$1(child, signal) {
|
|
122567
122833
|
if (process.platform !== "win32" && typeof child.pid === "number" && child.pid > 0) {
|
|
122568
122834
|
process.kill(-child.pid, signal);
|
|
122569
122835
|
return;
|
|
122570
122836
|
}
|
|
122571
122837
|
child.kill(signal);
|
|
122572
122838
|
}
|
|
122573
|
-
async function terminateChildProcess(child, logger2, sessionLabel, exitTimeoutMs) {
|
|
122839
|
+
async function terminateChildProcess$1(child, logger2, sessionLabel, exitTimeoutMs) {
|
|
122574
122840
|
if (child.exitCode !== null) {
|
|
122575
122841
|
return;
|
|
122576
122842
|
}
|
|
122577
122843
|
try {
|
|
122578
|
-
signalChildProcess(child, "SIGTERM");
|
|
122844
|
+
signalChildProcess$1(child, "SIGTERM");
|
|
122579
122845
|
} catch {
|
|
122580
122846
|
return;
|
|
122581
122847
|
}
|
|
122582
|
-
if (await waitForChildProcessExit(child, exitTimeoutMs)) {
|
|
122848
|
+
if (await waitForChildProcessExit$1(child, exitTimeoutMs)) {
|
|
122583
122849
|
return;
|
|
122584
122850
|
}
|
|
122585
122851
|
logger2.debug(`[${sessionLabel}] ACP agent process did not exit within ${exitTimeoutMs}ms of SIGTERM; escalating to SIGKILL`);
|
|
122586
122852
|
try {
|
|
122587
|
-
signalChildProcess(child, "SIGKILL");
|
|
122853
|
+
signalChildProcess$1(child, "SIGKILL");
|
|
122588
122854
|
} catch {
|
|
122589
122855
|
return;
|
|
122590
122856
|
}
|
|
122591
|
-
await waitForChildProcessExit(child, exitTimeoutMs);
|
|
122857
|
+
await waitForChildProcessExit$1(child, exitTimeoutMs);
|
|
122592
122858
|
}
|
|
122593
122859
|
const spawnAcpProcess = (options) => {
|
|
122594
122860
|
const setting = resolveACPSetting({
|
|
@@ -122735,7 +123001,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122735
123001
|
sessionResponse: started.sessionResponse
|
|
122736
123002
|
};
|
|
122737
123003
|
} catch (error2) {
|
|
122738
|
-
await terminateChildProcess(agentProcess, options.logger, "acp-startup", 3e3);
|
|
123004
|
+
await terminateChildProcess$1(agentProcess, options.logger, "acp-startup", 3e3);
|
|
122739
123005
|
throw error2;
|
|
122740
123006
|
} finally {
|
|
122741
123007
|
startupMonitor.dispose();
|
|
@@ -122751,7 +123017,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
122751
123017
|
options.logger.debug(`[${options.sessionLabel}] ACP session close failed during local agent shutdown: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
122752
123018
|
}
|
|
122753
123019
|
}
|
|
122754
|
-
await terminateChildProcess(options.agentProcess, options.logger, options.sessionLabel, exitTimeoutMs);
|
|
123020
|
+
await terminateChildProcess$1(options.agentProcess, options.logger, options.sessionLabel, exitTimeoutMs);
|
|
122755
123021
|
}
|
|
122756
123022
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
122757
123023
|
const getStringField = (obj, key2) => {
|
|
@@ -125761,6 +126027,150 @@ $mem | ConvertTo-Json -Compress
|
|
|
125761
126027
|
return null;
|
|
125762
126028
|
}
|
|
125763
126029
|
}
|
|
126030
|
+
function getImportedAcpSourceAcpSessionId(meta) {
|
|
126031
|
+
const externalHistory = meta.externalHistory;
|
|
126032
|
+
return externalHistory?.sourceAcpSessionId;
|
|
126033
|
+
}
|
|
126034
|
+
function isImportedAcpReplayUserTurn(entry2, meta) {
|
|
126035
|
+
const provider2 = meta.externalHistory ? getLocalProjectHistoryProviderKey(meta.externalHistory.provider) : null;
|
|
126036
|
+
const sourceAcpSessionId = getImportedAcpSourceAcpSessionId(meta);
|
|
126037
|
+
return !!provider2 && !!sourceAcpSessionId && entry2.id.startsWith(`${provider2}:${sourceAcpSessionId}:turn:`);
|
|
126038
|
+
}
|
|
126039
|
+
function resolveResumableAcpSessionId(meta) {
|
|
126040
|
+
const acpSessionId = meta?.acpSessionId;
|
|
126041
|
+
if (!meta || !acpSessionId) {
|
|
126042
|
+
return void 0;
|
|
126043
|
+
}
|
|
126044
|
+
if (!meta.externalHistory) {
|
|
126045
|
+
return acpSessionId;
|
|
126046
|
+
}
|
|
126047
|
+
const sourceAcpSessionId = meta.externalHistory.sourceAcpSessionId;
|
|
126048
|
+
if (!sourceAcpSessionId) {
|
|
126049
|
+
return void 0;
|
|
126050
|
+
}
|
|
126051
|
+
return acpSessionId === sourceAcpSessionId ? void 0 : acpSessionId;
|
|
126052
|
+
}
|
|
126053
|
+
function resolveDispatchAcpSessionId(meta) {
|
|
126054
|
+
const liveSessionId = resolveResumableAcpSessionId(meta);
|
|
126055
|
+
if (liveSessionId || !meta?.externalHistory || meta.externalHistory.status === "sync_conflict") {
|
|
126056
|
+
return liveSessionId;
|
|
126057
|
+
}
|
|
126058
|
+
return meta.externalHistory.sourceAcpSessionId;
|
|
126059
|
+
}
|
|
126060
|
+
function resolveSessionDispatchAction(snapshot, machineId) {
|
|
126061
|
+
const { meta, history, hasActiveTurn, hasBlockingPendingCreate, hasReusableSession } = snapshot;
|
|
126062
|
+
if (!meta || meta.machineId !== machineId) {
|
|
126063
|
+
return {
|
|
126064
|
+
type: "noop",
|
|
126065
|
+
reason: "not-owned"
|
|
126066
|
+
};
|
|
126067
|
+
}
|
|
126068
|
+
if (meta.isArchived) {
|
|
126069
|
+
return {
|
|
126070
|
+
type: "noop",
|
|
126071
|
+
reason: "archived"
|
|
126072
|
+
};
|
|
126073
|
+
}
|
|
126074
|
+
if (hasBlockingPendingCreate) {
|
|
126075
|
+
return {
|
|
126076
|
+
type: "noop",
|
|
126077
|
+
reason: "pending-create"
|
|
126078
|
+
};
|
|
126079
|
+
}
|
|
126080
|
+
const statusType = meta.status?.type;
|
|
126081
|
+
if (statusType === "running" || statusType === "requestPermission" || statusType === "initializing") {
|
|
126082
|
+
if (hasActiveTurn) {
|
|
126083
|
+
return {
|
|
126084
|
+
type: "noop",
|
|
126085
|
+
reason: "active-session"
|
|
126086
|
+
};
|
|
126087
|
+
}
|
|
126088
|
+
return {
|
|
126089
|
+
type: "reset-stale-status",
|
|
126090
|
+
statusType
|
|
126091
|
+
};
|
|
126092
|
+
}
|
|
126093
|
+
const turn = findNextDispatchableUserTurn(history, meta);
|
|
126094
|
+
if (!turn) {
|
|
126095
|
+
return {
|
|
126096
|
+
type: "no-dispatchable-turn"
|
|
126097
|
+
};
|
|
126098
|
+
}
|
|
126099
|
+
const mode2 = hasReusableSession || resolveDispatchAcpSessionId(meta) ? "continue" : "create";
|
|
126100
|
+
return {
|
|
126101
|
+
type: "dispatch",
|
|
126102
|
+
mode: mode2,
|
|
126103
|
+
turn
|
|
126104
|
+
};
|
|
126105
|
+
}
|
|
126106
|
+
function resolveSessionCancelAction(meta, lastSeenCancelTurn, machineId) {
|
|
126107
|
+
if (!meta || meta.machineId !== machineId) {
|
|
126108
|
+
return {
|
|
126109
|
+
type: "noop",
|
|
126110
|
+
reason: "not-owned"
|
|
126111
|
+
};
|
|
126112
|
+
}
|
|
126113
|
+
if (meta.isArchived) {
|
|
126114
|
+
return {
|
|
126115
|
+
type: "noop",
|
|
126116
|
+
reason: "archived"
|
|
126117
|
+
};
|
|
126118
|
+
}
|
|
126119
|
+
const lastCanceledTurn = meta.lastCanceledTurn;
|
|
126120
|
+
if (typeof lastCanceledTurn !== "string" || !lastCanceledTurn) {
|
|
126121
|
+
return {
|
|
126122
|
+
type: "noop",
|
|
126123
|
+
reason: "no-cancel-turn"
|
|
126124
|
+
};
|
|
126125
|
+
}
|
|
126126
|
+
if (lastCanceledTurn === lastSeenCancelTurn) {
|
|
126127
|
+
return {
|
|
126128
|
+
type: "noop",
|
|
126129
|
+
reason: "already-seen"
|
|
126130
|
+
};
|
|
126131
|
+
}
|
|
126132
|
+
return {
|
|
126133
|
+
type: "cancel",
|
|
126134
|
+
turnId: lastCanceledTurn
|
|
126135
|
+
};
|
|
126136
|
+
}
|
|
126137
|
+
function findNextDispatchableUserTurn(history, meta) {
|
|
126138
|
+
for (const entry2 of history) {
|
|
126139
|
+
if (entry2.role !== "user") {
|
|
126140
|
+
continue;
|
|
126141
|
+
}
|
|
126142
|
+
if (isImportedAcpReplayUserTurn(entry2, meta)) {
|
|
126143
|
+
continue;
|
|
126144
|
+
}
|
|
126145
|
+
if (typeof entry2.status === "string") {
|
|
126146
|
+
if (entry2.status === "pending" || entry2.status === "seen" || entry2.status === "processing") {
|
|
126147
|
+
return entry2;
|
|
126148
|
+
}
|
|
126149
|
+
continue;
|
|
126150
|
+
}
|
|
126151
|
+
if (entry2.read === false) {
|
|
126152
|
+
return entry2;
|
|
126153
|
+
}
|
|
126154
|
+
if (entry2.id === meta.processingUserMsgId) {
|
|
126155
|
+
return entry2;
|
|
126156
|
+
}
|
|
126157
|
+
if (entry2.id === meta.latestUserMsgId && entry2.id !== meta.lastHandledUserMsgId) {
|
|
126158
|
+
return entry2;
|
|
126159
|
+
}
|
|
126160
|
+
}
|
|
126161
|
+
return null;
|
|
126162
|
+
}
|
|
126163
|
+
function resolveDispatchTurnInput(entry2) {
|
|
126164
|
+
const historyBlocks = historyItemsToInputBlocks(entry2.items);
|
|
126165
|
+
const configuredBlocks = normalizeSessionInputBlocks(entry2.inputConfig?.inputBlocks, "");
|
|
126166
|
+
const fallbackBlocks = normalizeSessionInputBlocks(void 0, entry2.inputConfig?.prompt ?? "");
|
|
126167
|
+
const inputBlocks = configuredBlocks.length > 0 ? configuredBlocks : historyBlocks.length > 0 ? historyBlocks : fallbackBlocks;
|
|
126168
|
+
const prompt2 = entry2.inputConfig?.prompt ?? extractPromptPreviewFromInputBlocks(inputBlocks.length > 0 ? inputBlocks : historyBlocks);
|
|
126169
|
+
return {
|
|
126170
|
+
inputBlocks,
|
|
126171
|
+
prompt: prompt2
|
|
126172
|
+
};
|
|
126173
|
+
}
|
|
125764
126174
|
class SessionTurnCancelled extends TaggedError("SessionTurnCancelled") {
|
|
125765
126175
|
}
|
|
125766
126176
|
class SessionTurnHalted extends TaggedError("SessionTurnHalted") {
|
|
@@ -126753,7 +127163,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
126753
127163
|
const agentConfigEnv = agentConfigEnvResolution.env ?? void 0;
|
|
126754
127164
|
self2.deps.logger.debug(`[${sessionId}] Resume env resolved (agentConfigId=${meta?.agentConfigId ?? "none"} reason=${agentConfigEnvResolution.reason} keys=${agentConfigEnv ? Object.keys(agentConfigEnv).length : 0})`);
|
|
126755
127165
|
const requestedResumeSessionId = acpSessionConfig.resume;
|
|
126756
|
-
const storedResumeSessionId = meta
|
|
127166
|
+
const storedResumeSessionId = resolveResumableAcpSessionId(meta);
|
|
126757
127167
|
const resumeSessionId = requestedResumeSessionId ?? storedResumeSessionId;
|
|
126758
127168
|
const resumeSource = requestedResumeSessionId ? "request" : storedResumeSessionId ? "meta" : "none";
|
|
126759
127169
|
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 +127880,6 @@ $mem | ConvertTo-Json -Compress
|
|
|
127470
127880
|
};
|
|
127471
127881
|
}
|
|
127472
127882
|
}
|
|
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
127883
|
const isConfigOptionValueRecord = (value) => {
|
|
127585
127884
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
127586
127885
|
return false;
|
|
@@ -127955,7 +128254,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
127955
128254
|
modelId: entry2.inputConfig?.modelId,
|
|
127956
128255
|
configOptionValues: entry2.inputConfig?.configOptionValues,
|
|
127957
128256
|
issuePRMentions: entry2.inputConfig?.issuePRMentions,
|
|
127958
|
-
resume: entry2.inputConfig?.resume ?? meta
|
|
128257
|
+
resume: entry2.inputConfig?.resume ?? resolveDispatchAcpSessionId(meta)
|
|
127959
128258
|
},
|
|
127960
128259
|
userTurnId: entry2.id,
|
|
127961
128260
|
userId: entry2.userId ?? meta.userId,
|
|
@@ -128017,7 +128316,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
128017
128316
|
modelId: queuedItem.acpSessionConfig?.modelId,
|
|
128018
128317
|
configOptionValues: isConfigOptionValueRecord(queuedItem.acpSessionConfig?.configOptionValues) ? queuedItem.acpSessionConfig.configOptionValues : void 0,
|
|
128019
128318
|
issuePRMentions: queuedItem.acpSessionConfig?.issuePRMentions,
|
|
128020
|
-
resume: meta
|
|
128319
|
+
resume: resolveResumableAcpSessionId(meta)
|
|
128021
128320
|
});
|
|
128022
128321
|
const pendingEntry = buildPendingUserHistoryEntry({
|
|
128023
128322
|
userId: queuedItem.userId ?? meta.userId,
|
|
@@ -129053,7 +129352,7 @@ $mem | ConvertTo-Json -Compress
|
|
|
129053
129352
|
}
|
|
129054
129353
|
})();
|
|
129055
129354
|
try {
|
|
129056
|
-
await withTimeout(readyPromise, TUNNEL_READY_TIMEOUT_MS, "Timed out waiting for preview tunnel control connection to become ready");
|
|
129355
|
+
await withTimeout$1(readyPromise, TUNNEL_READY_TIMEOUT_MS, "Timed out waiting for preview tunnel control connection to become ready");
|
|
129057
129356
|
} catch (error2) {
|
|
129058
129357
|
await close2("Preview tunnel failed to become ready");
|
|
129059
129358
|
throw error2;
|
|
@@ -129876,7 +130175,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
129876
130175
|
function asError(error2) {
|
|
129877
130176
|
return error2 instanceof Error ? error2 : new Error(formatError(error2));
|
|
129878
130177
|
}
|
|
129879
|
-
async function withTimeout(promise, timeoutMs, message) {
|
|
130178
|
+
async function withTimeout$1(promise, timeoutMs, message) {
|
|
129880
130179
|
let timeoutHandle;
|
|
129881
130180
|
const timeoutPromise = new Promise((_2, reject) => {
|
|
129882
130181
|
timeoutHandle = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
@@ -130738,6 +131037,796 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
130738
131037
|
return Math.round(this.deps.now?.() ?? getServerNow());
|
|
130739
131038
|
}
|
|
130740
131039
|
}
|
|
131040
|
+
const ACP_OPERATION_TIMEOUT_MS = 12e4;
|
|
131041
|
+
const ACP_PROCESS_EXIT_TIMEOUT_MS = 3e3;
|
|
131042
|
+
function waitForChildProcessExit(child, timeoutMs) {
|
|
131043
|
+
if (child.exitCode !== null) {
|
|
131044
|
+
return Promise.resolve(true);
|
|
131045
|
+
}
|
|
131046
|
+
return new Promise((resolve2) => {
|
|
131047
|
+
const timeout2 = setTimeout(() => {
|
|
131048
|
+
child.off("exit", onExit2);
|
|
131049
|
+
resolve2(child.exitCode !== null);
|
|
131050
|
+
}, timeoutMs);
|
|
131051
|
+
const onExit2 = () => {
|
|
131052
|
+
clearTimeout(timeout2);
|
|
131053
|
+
resolve2(true);
|
|
131054
|
+
};
|
|
131055
|
+
child.once("exit", onExit2);
|
|
131056
|
+
});
|
|
131057
|
+
}
|
|
131058
|
+
function signalChildProcess(child, signal) {
|
|
131059
|
+
if (process.platform !== "win32" && typeof child.pid === "number" && child.pid > 0) {
|
|
131060
|
+
process.kill(-child.pid, signal);
|
|
131061
|
+
return;
|
|
131062
|
+
}
|
|
131063
|
+
child.kill(signal);
|
|
131064
|
+
}
|
|
131065
|
+
async function terminateChildProcess(child) {
|
|
131066
|
+
if (child.exitCode !== null) {
|
|
131067
|
+
return;
|
|
131068
|
+
}
|
|
131069
|
+
try {
|
|
131070
|
+
signalChildProcess(child, "SIGTERM");
|
|
131071
|
+
} catch {
|
|
131072
|
+
return;
|
|
131073
|
+
}
|
|
131074
|
+
if (await waitForChildProcessExit(child, ACP_PROCESS_EXIT_TIMEOUT_MS)) {
|
|
131075
|
+
return;
|
|
131076
|
+
}
|
|
131077
|
+
try {
|
|
131078
|
+
signalChildProcess(child, "SIGKILL");
|
|
131079
|
+
} catch {
|
|
131080
|
+
return;
|
|
131081
|
+
}
|
|
131082
|
+
await waitForChildProcessExit(child, ACP_PROCESS_EXIT_TIMEOUT_MS);
|
|
131083
|
+
}
|
|
131084
|
+
async function withTimeout(promise, label2) {
|
|
131085
|
+
let timeout2 = null;
|
|
131086
|
+
try {
|
|
131087
|
+
return await Promise.race([
|
|
131088
|
+
promise,
|
|
131089
|
+
new Promise((_resolve, reject) => {
|
|
131090
|
+
timeout2 = setTimeout(() => reject(new Error(`${label2} timed out after ${ACP_OPERATION_TIMEOUT_MS}ms`)), ACP_OPERATION_TIMEOUT_MS);
|
|
131091
|
+
})
|
|
131092
|
+
]);
|
|
131093
|
+
} finally {
|
|
131094
|
+
if (timeout2) {
|
|
131095
|
+
clearTimeout(timeout2);
|
|
131096
|
+
}
|
|
131097
|
+
}
|
|
131098
|
+
}
|
|
131099
|
+
class AcpReplayCollectorClient {
|
|
131100
|
+
notifications = [];
|
|
131101
|
+
async requestPermission() {
|
|
131102
|
+
return {
|
|
131103
|
+
outcome: {
|
|
131104
|
+
outcome: "cancelled"
|
|
131105
|
+
}
|
|
131106
|
+
};
|
|
131107
|
+
}
|
|
131108
|
+
async sessionUpdate(params) {
|
|
131109
|
+
this.notifications.push(parseSessionNotification(params));
|
|
131110
|
+
}
|
|
131111
|
+
}
|
|
131112
|
+
function getProviderLabel$1(provider2) {
|
|
131113
|
+
return getLocalProjectHistoryProviderKey(provider2);
|
|
131114
|
+
}
|
|
131115
|
+
async function createHistoryAcpConnection(args2) {
|
|
131116
|
+
const setting = resolveACPSetting(args2.provider);
|
|
131117
|
+
const env2 = setting.exec.env ? {
|
|
131118
|
+
...process.env,
|
|
131119
|
+
...setting.exec.env
|
|
131120
|
+
} : process.env;
|
|
131121
|
+
const agentProcess = spawnAcpProcess({
|
|
131122
|
+
cliType: args2.provider.cliType,
|
|
131123
|
+
agentType: args2.provider.agentType,
|
|
131124
|
+
workdir: args2.workdir,
|
|
131125
|
+
env: env2
|
|
131126
|
+
});
|
|
131127
|
+
agentProcess.stderr?.setEncoding("utf8");
|
|
131128
|
+
agentProcess.stderr?.on("data", (chunk) => {
|
|
131129
|
+
if (!chunk) return;
|
|
131130
|
+
args2.logger.debug(`[${getProviderLabel$1(args2.provider)}-history-sync] ACP stderr: ${chunk.slice(0, 1200)}`);
|
|
131131
|
+
});
|
|
131132
|
+
if (!agentProcess.stdout || !agentProcess.stdin) {
|
|
131133
|
+
await terminateChildProcess(agentProcess);
|
|
131134
|
+
throw new Error(`${getProviderLabel$1(args2.provider)} ACP process did not expose stdio streams`);
|
|
131135
|
+
}
|
|
131136
|
+
const output = createStdoutReadableStream(agentProcess.stdout);
|
|
131137
|
+
const input2 = createStdinWritableStream(agentProcess.stdin);
|
|
131138
|
+
const stream2 = ndJsonStream(input2, output);
|
|
131139
|
+
const collector = new AcpReplayCollectorClient();
|
|
131140
|
+
const connection = new ClientSideConnection(() => collector, stream2);
|
|
131141
|
+
try {
|
|
131142
|
+
const initResponse = await withTimeout(connection.initialize({
|
|
131143
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
131144
|
+
clientCapabilities: {
|
|
131145
|
+
terminal: false,
|
|
131146
|
+
fs: {
|
|
131147
|
+
readTextFile: false,
|
|
131148
|
+
writeTextFile: false
|
|
131149
|
+
}
|
|
131150
|
+
}
|
|
131151
|
+
}), `${getProviderLabel$1(args2.provider)} ACP initialize`);
|
|
131152
|
+
return {
|
|
131153
|
+
agentProcess,
|
|
131154
|
+
connection,
|
|
131155
|
+
collector,
|
|
131156
|
+
initResponse
|
|
131157
|
+
};
|
|
131158
|
+
} catch (error2) {
|
|
131159
|
+
await terminateChildProcess(agentProcess);
|
|
131160
|
+
throw error2;
|
|
131161
|
+
}
|
|
131162
|
+
}
|
|
131163
|
+
async function resolveCatalogQueryPaths(rootPath) {
|
|
131164
|
+
const resolved = path__default.resolve(rootPath);
|
|
131165
|
+
let real = null;
|
|
131166
|
+
try {
|
|
131167
|
+
real = await fs$5.realpath(resolved);
|
|
131168
|
+
} catch {
|
|
131169
|
+
real = null;
|
|
131170
|
+
}
|
|
131171
|
+
const paths = [
|
|
131172
|
+
resolved
|
|
131173
|
+
];
|
|
131174
|
+
if (real && real !== resolved) {
|
|
131175
|
+
paths.push(real);
|
|
131176
|
+
}
|
|
131177
|
+
return paths;
|
|
131178
|
+
}
|
|
131179
|
+
async function listPaginatedHistorySessions(cwd, listPage) {
|
|
131180
|
+
const sessions = [];
|
|
131181
|
+
let cursor;
|
|
131182
|
+
do {
|
|
131183
|
+
const response = await listPage({
|
|
131184
|
+
cwd,
|
|
131185
|
+
cursor
|
|
131186
|
+
});
|
|
131187
|
+
sessions.push(...response.sessions);
|
|
131188
|
+
cursor = response.nextCursor;
|
|
131189
|
+
} while (cursor);
|
|
131190
|
+
return sessions;
|
|
131191
|
+
}
|
|
131192
|
+
async function listHistorySessionsForLocalProject(args2) {
|
|
131193
|
+
const queryPaths = await resolveCatalogQueryPaths(args2.rootPath);
|
|
131194
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
131195
|
+
for (const cwd of queryPaths) {
|
|
131196
|
+
const { agentProcess, connection, initResponse } = await createHistoryAcpConnection({
|
|
131197
|
+
provider: args2.provider,
|
|
131198
|
+
workdir: cwd,
|
|
131199
|
+
logger: args2.logger
|
|
131200
|
+
});
|
|
131201
|
+
try {
|
|
131202
|
+
if (!initResponse.agentCapabilities?.sessionCapabilities?.list) {
|
|
131203
|
+
throw new Error(`${getProviderLabel$1(args2.provider)} ACP agent does not advertise sessionCapabilities.list`);
|
|
131204
|
+
}
|
|
131205
|
+
const sessions = await listPaginatedHistorySessions(cwd, async ({ cursor }) => withTimeout(connection.listSessions({
|
|
131206
|
+
cwd,
|
|
131207
|
+
cursor
|
|
131208
|
+
}), `${getProviderLabel$1(args2.provider)} ACP listSessions (${cwd})`));
|
|
131209
|
+
for (const session of sessions) {
|
|
131210
|
+
bySessionId.set(session.sessionId, session);
|
|
131211
|
+
}
|
|
131212
|
+
} finally {
|
|
131213
|
+
await terminateChildProcess(agentProcess);
|
|
131214
|
+
}
|
|
131215
|
+
}
|
|
131216
|
+
return {
|
|
131217
|
+
sessions: [
|
|
131218
|
+
...bySessionId.values()
|
|
131219
|
+
],
|
|
131220
|
+
queryPaths
|
|
131221
|
+
};
|
|
131222
|
+
}
|
|
131223
|
+
async function loadHistorySessionReplay(args2) {
|
|
131224
|
+
const cwd = path__default.resolve(args2.rootPath);
|
|
131225
|
+
const { agentProcess, connection, collector, initResponse } = await createHistoryAcpConnection({
|
|
131226
|
+
provider: args2.provider,
|
|
131227
|
+
workdir: cwd,
|
|
131228
|
+
logger: args2.logger
|
|
131229
|
+
});
|
|
131230
|
+
try {
|
|
131231
|
+
if (!initResponse.agentCapabilities?.loadSession) {
|
|
131232
|
+
throw new Error(`${getProviderLabel$1(args2.provider)} ACP agent does not advertise loadSession`);
|
|
131233
|
+
}
|
|
131234
|
+
await withTimeout(connection.loadSession({
|
|
131235
|
+
sessionId: args2.acpSessionId,
|
|
131236
|
+
cwd,
|
|
131237
|
+
mcpServers: []
|
|
131238
|
+
}), `${getProviderLabel$1(args2.provider)} ACP loadSession (${args2.acpSessionId})`);
|
|
131239
|
+
return collector.notifications;
|
|
131240
|
+
} catch (error2) {
|
|
131241
|
+
const message = `Failed to load ${getProviderLabel$1(args2.provider)} session ${args2.acpSessionId}: ${formatErrorMessage(error2)}`;
|
|
131242
|
+
throw new Error(message, {
|
|
131243
|
+
cause: error2
|
|
131244
|
+
});
|
|
131245
|
+
} finally {
|
|
131246
|
+
await terminateChildProcess(agentProcess);
|
|
131247
|
+
}
|
|
131248
|
+
}
|
|
131249
|
+
const syncLeases = /* @__PURE__ */ new Set();
|
|
131250
|
+
const machineCatalogWriteChains = /* @__PURE__ */ new Map();
|
|
131251
|
+
async function withMachineCatalogWriteLock(machineRoomId, fn) {
|
|
131252
|
+
const prev = machineCatalogWriteChains.get(machineRoomId);
|
|
131253
|
+
const current2 = (async () => {
|
|
131254
|
+
if (prev) {
|
|
131255
|
+
await prev.catch(() => {
|
|
131256
|
+
});
|
|
131257
|
+
}
|
|
131258
|
+
return fn();
|
|
131259
|
+
})();
|
|
131260
|
+
machineCatalogWriteChains.set(machineRoomId, current2);
|
|
131261
|
+
try {
|
|
131262
|
+
return await current2;
|
|
131263
|
+
} finally {
|
|
131264
|
+
if (machineCatalogWriteChains.get(machineRoomId) === current2) {
|
|
131265
|
+
machineCatalogWriteChains.delete(machineRoomId);
|
|
131266
|
+
}
|
|
131267
|
+
}
|
|
131268
|
+
}
|
|
131269
|
+
function emptySummary() {
|
|
131270
|
+
return {
|
|
131271
|
+
listed: 0,
|
|
131272
|
+
imported: 0,
|
|
131273
|
+
refreshed: 0,
|
|
131274
|
+
skipped: 0,
|
|
131275
|
+
conflicted: 0,
|
|
131276
|
+
failed: 0,
|
|
131277
|
+
failures: []
|
|
131278
|
+
};
|
|
131279
|
+
}
|
|
131280
|
+
function stableJson(value) {
|
|
131281
|
+
if (value === null || typeof value !== "object") {
|
|
131282
|
+
return JSON.stringify(value);
|
|
131283
|
+
}
|
|
131284
|
+
if (Array.isArray(value)) {
|
|
131285
|
+
return `[${value.map((item) => stableJson(item)).join(",")}]`;
|
|
131286
|
+
}
|
|
131287
|
+
const record2 = value;
|
|
131288
|
+
const entries = Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(record2[key2])}`);
|
|
131289
|
+
return `{${entries.join(",")}}`;
|
|
131290
|
+
}
|
|
131291
|
+
function hashText(value) {
|
|
131292
|
+
return createHash$1("sha256").update(value).digest("hex");
|
|
131293
|
+
}
|
|
131294
|
+
function normalizeHistoryEntryForHash(entry2) {
|
|
131295
|
+
return {
|
|
131296
|
+
role: entry2.role,
|
|
131297
|
+
items: entry2.items ?? [],
|
|
131298
|
+
plan: entry2.plan ?? []
|
|
131299
|
+
};
|
|
131300
|
+
}
|
|
131301
|
+
function hashHistoryEntry(entry2) {
|
|
131302
|
+
return hashText(stableJson(normalizeHistoryEntryForHash(entry2)));
|
|
131303
|
+
}
|
|
131304
|
+
function materializeReplay(args2) {
|
|
131305
|
+
let tempId = 0;
|
|
131306
|
+
const nowIso = new Date(getServerNow()).toISOString();
|
|
131307
|
+
const providerKey = getLocalProjectHistoryProviderKey(args2.provider);
|
|
131308
|
+
const replay = buildHistoryReplayImport(args2.replayNotifications, {
|
|
131309
|
+
provider: args2.provider,
|
|
131310
|
+
userId: args2.userId,
|
|
131311
|
+
now: () => nowIso,
|
|
131312
|
+
createId: () => `${providerKey}:${args2.acpSessionId}:tmp:${tempId++}`,
|
|
131313
|
+
mode: "imported_snapshot"
|
|
131314
|
+
});
|
|
131315
|
+
const turnHashes = replay.history.map(hashHistoryEntry);
|
|
131316
|
+
const history = replay.history.map((entry2, index2) => ({
|
|
131317
|
+
...entry2,
|
|
131318
|
+
id: `${providerKey}:${args2.acpSessionId}:turn:${index2}:${turnHashes[index2].slice(0, 16)}`
|
|
131319
|
+
}));
|
|
131320
|
+
return {
|
|
131321
|
+
history,
|
|
131322
|
+
turnHashes,
|
|
131323
|
+
replayDigest: hashText(turnHashes.join("\n"))
|
|
131324
|
+
};
|
|
131325
|
+
}
|
|
131326
|
+
function isPrefix(prefix, value) {
|
|
131327
|
+
if (prefix.length > value.length) {
|
|
131328
|
+
return false;
|
|
131329
|
+
}
|
|
131330
|
+
for (let index2 = 0; index2 < prefix.length; index2 += 1) {
|
|
131331
|
+
if (prefix[index2] !== value[index2]) {
|
|
131332
|
+
return false;
|
|
131333
|
+
}
|
|
131334
|
+
}
|
|
131335
|
+
return true;
|
|
131336
|
+
}
|
|
131337
|
+
function decideHistoryRefresh(args2) {
|
|
131338
|
+
if (args2.replayDigest === args2.externalHistory.replayDigest) {
|
|
131339
|
+
return {
|
|
131340
|
+
status: "skipped",
|
|
131341
|
+
reason: "digest_match"
|
|
131342
|
+
};
|
|
131343
|
+
}
|
|
131344
|
+
if (!isPrefix(args2.externalHistory.importedTurnHashes, args2.turnHashes)) {
|
|
131345
|
+
return {
|
|
131346
|
+
status: "conflicted",
|
|
131347
|
+
reason: "prefix_mismatch"
|
|
131348
|
+
};
|
|
131349
|
+
}
|
|
131350
|
+
if (args2.currentHistoryLength !== void 0 && args2.currentHistoryLength !== args2.externalHistory.importedTurnCount) {
|
|
131351
|
+
return {
|
|
131352
|
+
status: "conflicted",
|
|
131353
|
+
reason: "local_history_has_untracked_suffix"
|
|
131354
|
+
};
|
|
131355
|
+
}
|
|
131356
|
+
const appendFromIndex = args2.externalHistory.importedTurnCount;
|
|
131357
|
+
return args2.turnHashes.length > appendFromIndex ? {
|
|
131358
|
+
status: "refreshed",
|
|
131359
|
+
reason: "prefix_append",
|
|
131360
|
+
appendFromIndex
|
|
131361
|
+
} : {
|
|
131362
|
+
status: "skipped",
|
|
131363
|
+
reason: "empty_suffix",
|
|
131364
|
+
appendFromIndex
|
|
131365
|
+
};
|
|
131366
|
+
}
|
|
131367
|
+
async function listWorkspaceSessionMetas(manager) {
|
|
131368
|
+
const scanner = manager.repo.getMeta();
|
|
131369
|
+
if (!scanner) {
|
|
131370
|
+
return [];
|
|
131371
|
+
}
|
|
131372
|
+
const roomIds = /* @__PURE__ */ new Set();
|
|
131373
|
+
for (const row of await scanner.scan({
|
|
131374
|
+
prefix: [
|
|
131375
|
+
"m"
|
|
131376
|
+
]
|
|
131377
|
+
})) {
|
|
131378
|
+
const key2 = row.key;
|
|
131379
|
+
if (!Array.isArray(key2) || key2.length < 2) {
|
|
131380
|
+
continue;
|
|
131381
|
+
}
|
|
131382
|
+
const roomId = key2[1];
|
|
131383
|
+
if (typeof roomId === "string" && isSessionDocRoomId(roomId)) {
|
|
131384
|
+
roomIds.add(roomId);
|
|
131385
|
+
}
|
|
131386
|
+
}
|
|
131387
|
+
const metas = await Promise.all([
|
|
131388
|
+
...roomIds
|
|
131389
|
+
].map(async (roomId) => {
|
|
131390
|
+
const record2 = await manager.repo.getDocMeta(roomId);
|
|
131391
|
+
if (!record2?.meta || isLoroRepoDocDeleted(record2)) {
|
|
131392
|
+
return null;
|
|
131393
|
+
}
|
|
131394
|
+
const sessionId = roomId.slice("session-".length);
|
|
131395
|
+
return {
|
|
131396
|
+
sessionId,
|
|
131397
|
+
meta: record2.meta
|
|
131398
|
+
};
|
|
131399
|
+
}));
|
|
131400
|
+
return metas.filter((meta) => meta !== null);
|
|
131401
|
+
}
|
|
131402
|
+
function buildExistingHistorySessionIndex(metas, machineId, provider2) {
|
|
131403
|
+
const index2 = /* @__PURE__ */ new Map();
|
|
131404
|
+
const providerKey = getLocalProjectHistoryProviderKey(provider2);
|
|
131405
|
+
for (const entry2 of metas) {
|
|
131406
|
+
if (entry2.meta.machineId !== machineId) continue;
|
|
131407
|
+
if (entry2.meta.cliType !== provider2.cliType) continue;
|
|
131408
|
+
if (entry2.meta.agentType !== provider2.agentType) continue;
|
|
131409
|
+
const acpSessionIds = /* @__PURE__ */ new Set();
|
|
131410
|
+
if (entry2.meta.externalHistory && getLocalProjectHistoryProviderKey(entry2.meta.externalHistory.provider) === providerKey) {
|
|
131411
|
+
const sourceAcpSessionId = entry2.meta.externalHistory.sourceAcpSessionId;
|
|
131412
|
+
if (sourceAcpSessionId) {
|
|
131413
|
+
acpSessionIds.add(sourceAcpSessionId);
|
|
131414
|
+
}
|
|
131415
|
+
if (entry2.meta.acpSessionId && entry2.meta.acpSessionId !== sourceAcpSessionId) {
|
|
131416
|
+
acpSessionIds.add(entry2.meta.acpSessionId);
|
|
131417
|
+
}
|
|
131418
|
+
} else if (entry2.meta.acpSessionId) {
|
|
131419
|
+
acpSessionIds.add(entry2.meta.acpSessionId);
|
|
131420
|
+
}
|
|
131421
|
+
for (const acpSessionId of acpSessionIds) {
|
|
131422
|
+
index2.set(acpSessionId, entry2);
|
|
131423
|
+
}
|
|
131424
|
+
}
|
|
131425
|
+
return index2;
|
|
131426
|
+
}
|
|
131427
|
+
function getProviderLabel(provider2) {
|
|
131428
|
+
return getLocalProjectHistoryProviderKey(provider2);
|
|
131429
|
+
}
|
|
131430
|
+
function resolveSessionTitle(info, provider2) {
|
|
131431
|
+
const title2 = info.title?.trim();
|
|
131432
|
+
return title2 || `${getProviderLabel(provider2)} session`;
|
|
131433
|
+
}
|
|
131434
|
+
function parseUpdatedAtMs(updatedAt) {
|
|
131435
|
+
if (!updatedAt) return 0;
|
|
131436
|
+
const parsed = Date.parse(updatedAt);
|
|
131437
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
131438
|
+
}
|
|
131439
|
+
function compareCatalogItems(left2, right2) {
|
|
131440
|
+
const leftUpdatedAt = parseUpdatedAtMs(left2.updatedAt);
|
|
131441
|
+
const rightUpdatedAt = parseUpdatedAtMs(right2.updatedAt);
|
|
131442
|
+
if (leftUpdatedAt !== rightUpdatedAt) {
|
|
131443
|
+
return rightUpdatedAt - leftUpdatedAt;
|
|
131444
|
+
}
|
|
131445
|
+
return left2.title.localeCompare(right2.title);
|
|
131446
|
+
}
|
|
131447
|
+
function getHistoryCatalogStatus(existing) {
|
|
131448
|
+
if (!existing) return "available";
|
|
131449
|
+
return existing.meta.externalHistory?.status === "sync_conflict" ? "sync_conflict" : "imported";
|
|
131450
|
+
}
|
|
131451
|
+
function buildCatalogItem(provider2, info, existing) {
|
|
131452
|
+
const acpSessionId = info.sessionId;
|
|
131453
|
+
return {
|
|
131454
|
+
acpSessionId,
|
|
131455
|
+
title: resolveSessionTitle(info, provider2),
|
|
131456
|
+
updatedAt: info.updatedAt ?? void 0,
|
|
131457
|
+
importedSessionId: existing?.sessionId,
|
|
131458
|
+
status: getHistoryCatalogStatus(existing)
|
|
131459
|
+
};
|
|
131460
|
+
}
|
|
131461
|
+
function shouldSkipBySourceUpdatedAt(info, externalHistory) {
|
|
131462
|
+
if (externalHistory.status === "metadata_only") {
|
|
131463
|
+
return false;
|
|
131464
|
+
}
|
|
131465
|
+
if (!info.updatedAt || !externalHistory.sourceUpdatedAt) {
|
|
131466
|
+
return false;
|
|
131467
|
+
}
|
|
131468
|
+
const next = Date.parse(info.updatedAt);
|
|
131469
|
+
const current2 = Date.parse(externalHistory.sourceUpdatedAt);
|
|
131470
|
+
return Number.isFinite(next) && Number.isFinite(current2) && next <= current2;
|
|
131471
|
+
}
|
|
131472
|
+
function resolveSourceUpdatedAtMs(info, fallback2) {
|
|
131473
|
+
if (!info.updatedAt) {
|
|
131474
|
+
return fallback2;
|
|
131475
|
+
}
|
|
131476
|
+
const parsed = Date.parse(info.updatedAt);
|
|
131477
|
+
return Number.isFinite(parsed) ? parsed : fallback2;
|
|
131478
|
+
}
|
|
131479
|
+
function buildExternalHistoryMeta(args2) {
|
|
131480
|
+
return {
|
|
131481
|
+
provider: args2.provider,
|
|
131482
|
+
source: "local-acp-history",
|
|
131483
|
+
sourceAcpSessionId: args2.sourceAcpSessionId,
|
|
131484
|
+
sourceUpdatedAt: args2.sourceUpdatedAt ?? void 0,
|
|
131485
|
+
replayDigest: args2.materialized.replayDigest,
|
|
131486
|
+
importedTurnCount: args2.materialized.turnHashes.length,
|
|
131487
|
+
importedTurnHashes: args2.materialized.turnHashes,
|
|
131488
|
+
lastSyncAt: getServerNow(),
|
|
131489
|
+
status: args2.status ?? "synced",
|
|
131490
|
+
conflictReason: args2.conflictReason
|
|
131491
|
+
};
|
|
131492
|
+
}
|
|
131493
|
+
function buildMetadataOnlyExternalHistoryMeta(args2) {
|
|
131494
|
+
return {
|
|
131495
|
+
provider: args2.provider,
|
|
131496
|
+
source: "local-acp-history",
|
|
131497
|
+
sourceAcpSessionId: args2.sourceAcpSessionId,
|
|
131498
|
+
sourceUpdatedAt: args2.sourceUpdatedAt ?? void 0,
|
|
131499
|
+
importedTurnCount: 0,
|
|
131500
|
+
importedTurnHashes: [],
|
|
131501
|
+
lastSyncAt: getServerNow(),
|
|
131502
|
+
status: "metadata_only"
|
|
131503
|
+
};
|
|
131504
|
+
}
|
|
131505
|
+
class LocalProjectHistorySyncService {
|
|
131506
|
+
constructor(manager, logger2, context2, provider2) {
|
|
131507
|
+
this.manager = manager;
|
|
131508
|
+
this.logger = logger2;
|
|
131509
|
+
this.context = context2;
|
|
131510
|
+
this.provider = provider2;
|
|
131511
|
+
this.providerKey = getLocalProjectHistoryProviderKey(provider2);
|
|
131512
|
+
}
|
|
131513
|
+
provider;
|
|
131514
|
+
providerKey;
|
|
131515
|
+
async syncLocalProject(args2) {
|
|
131516
|
+
const leaseKey = `${this.providerKey}:${this.context.workspaceId}:${this.context.machineId}:${args2.localProjectId}`;
|
|
131517
|
+
if (syncLeases.has(leaseKey)) {
|
|
131518
|
+
throw new Error(`${getProviderLabel(this.provider)} history sync is already running for this local project`);
|
|
131519
|
+
}
|
|
131520
|
+
syncLeases.add(leaseKey);
|
|
131521
|
+
try {
|
|
131522
|
+
return await this.syncLocalProjectInner(args2);
|
|
131523
|
+
} finally {
|
|
131524
|
+
syncLeases.delete(leaseKey);
|
|
131525
|
+
}
|
|
131526
|
+
}
|
|
131527
|
+
async syncLocalProjectInner(args2) {
|
|
131528
|
+
const snapshot = await this.listCatalogSnapshot(args2.rootPath);
|
|
131529
|
+
return await this.writeCatalogResult({
|
|
131530
|
+
localProjectId: args2.localProjectId,
|
|
131531
|
+
sessions: snapshot.sessions,
|
|
131532
|
+
existingByAcpSessionId: snapshot.existingByAcpSessionId
|
|
131533
|
+
});
|
|
131534
|
+
}
|
|
131535
|
+
async importLocalProjectSessions(args2) {
|
|
131536
|
+
const leaseKey = `${this.providerKey}:${this.context.workspaceId}:${this.context.machineId}:${args2.localProjectId}`;
|
|
131537
|
+
if (syncLeases.has(leaseKey)) {
|
|
131538
|
+
throw new Error(`${getProviderLabel(this.provider)} history sync is already running for this local project`);
|
|
131539
|
+
}
|
|
131540
|
+
syncLeases.add(leaseKey);
|
|
131541
|
+
try {
|
|
131542
|
+
return await this.importLocalProjectSessionsInner(args2);
|
|
131543
|
+
} finally {
|
|
131544
|
+
syncLeases.delete(leaseKey);
|
|
131545
|
+
}
|
|
131546
|
+
}
|
|
131547
|
+
async importLocalProjectSessionsInner(args2) {
|
|
131548
|
+
const summary2 = emptySummary();
|
|
131549
|
+
const selectedIds = [
|
|
131550
|
+
...new Set(args2.acpSessionIds)
|
|
131551
|
+
];
|
|
131552
|
+
summary2.listed = selectedIds.length;
|
|
131553
|
+
const snapshot = await this.listCatalogSnapshot(args2.rootPath);
|
|
131554
|
+
const infoByAcpSessionId = new Map(snapshot.sessions.map((info) => [
|
|
131555
|
+
info.sessionId,
|
|
131556
|
+
info
|
|
131557
|
+
]));
|
|
131558
|
+
const project = {
|
|
131559
|
+
kind: "local",
|
|
131560
|
+
localProjectId: args2.localProjectId
|
|
131561
|
+
};
|
|
131562
|
+
for (const selectedId of selectedIds) {
|
|
131563
|
+
const acpSessionId = selectedId;
|
|
131564
|
+
const info = infoByAcpSessionId.get(selectedId);
|
|
131565
|
+
try {
|
|
131566
|
+
if (!info) {
|
|
131567
|
+
throw new Error(`${getProviderLabel(this.provider)} session was not found in the local project catalog`);
|
|
131568
|
+
}
|
|
131569
|
+
const existing = snapshot.existingByAcpSessionId.get(selectedId);
|
|
131570
|
+
if (!existing) {
|
|
131571
|
+
const importedSession = await this.importNewSession({
|
|
131572
|
+
info,
|
|
131573
|
+
acpSessionId,
|
|
131574
|
+
project
|
|
131575
|
+
});
|
|
131576
|
+
snapshot.existingByAcpSessionId.set(selectedId, importedSession);
|
|
131577
|
+
summary2.imported += 1;
|
|
131578
|
+
continue;
|
|
131579
|
+
}
|
|
131580
|
+
const status = await this.refreshExistingSession({
|
|
131581
|
+
existing,
|
|
131582
|
+
info,
|
|
131583
|
+
acpSessionId,
|
|
131584
|
+
rootPath: args2.rootPath
|
|
131585
|
+
});
|
|
131586
|
+
summary2[status] += 1;
|
|
131587
|
+
} catch (error2) {
|
|
131588
|
+
summary2.failed += 1;
|
|
131589
|
+
summary2.failures.push({
|
|
131590
|
+
acpSessionId,
|
|
131591
|
+
message: formatErrorMessage(error2)
|
|
131592
|
+
});
|
|
131593
|
+
this.logger.debug(`[${this.providerKey}-history-sync] Failed to import ${getProviderLabel(this.provider)} session ${acpSessionId}: ${formatErrorMessage(error2)}`);
|
|
131594
|
+
}
|
|
131595
|
+
}
|
|
131596
|
+
const catalog = await this.writeCatalogResult({
|
|
131597
|
+
localProjectId: args2.localProjectId,
|
|
131598
|
+
sessions: snapshot.sessions,
|
|
131599
|
+
existingByAcpSessionId: snapshot.existingByAcpSessionId
|
|
131600
|
+
});
|
|
131601
|
+
return {
|
|
131602
|
+
summary: summary2,
|
|
131603
|
+
catalog
|
|
131604
|
+
};
|
|
131605
|
+
}
|
|
131606
|
+
async listCatalogSnapshot(rootPath) {
|
|
131607
|
+
const catalog = await listHistorySessionsForLocalProject({
|
|
131608
|
+
provider: this.provider,
|
|
131609
|
+
rootPath,
|
|
131610
|
+
logger: this.logger
|
|
131611
|
+
});
|
|
131612
|
+
const sessionMetas = await listWorkspaceSessionMetas(this.manager);
|
|
131613
|
+
const existingByAcpSessionId = buildExistingHistorySessionIndex(sessionMetas, this.context.machineId, this.provider);
|
|
131614
|
+
return {
|
|
131615
|
+
sessions: catalog.sessions,
|
|
131616
|
+
existingByAcpSessionId
|
|
131617
|
+
};
|
|
131618
|
+
}
|
|
131619
|
+
async writeCatalogResult(args2) {
|
|
131620
|
+
const lastListedAt = Math.round(getServerNow());
|
|
131621
|
+
const sessions = args2.sessions.map((info) => buildCatalogItem(this.provider, info, args2.existingByAcpSessionId.get(info.sessionId))).sort(compareCatalogItems);
|
|
131622
|
+
const catalog = {
|
|
131623
|
+
listed: sessions.length,
|
|
131624
|
+
lastListedAt,
|
|
131625
|
+
sessions
|
|
131626
|
+
};
|
|
131627
|
+
const machineRoomId = getMachineRoomId(this.context.machineId);
|
|
131628
|
+
await withMachineCatalogWriteLock(machineRoomId, async () => {
|
|
131629
|
+
const current2 = await this.manager.repo.getDocMeta(machineRoomId);
|
|
131630
|
+
const rawExisting = current2?.meta?.localProjects;
|
|
131631
|
+
const existing = rawExisting && typeof rawExisting === "object" ? rawExisting : {};
|
|
131632
|
+
const previous = existing[args2.localProjectId];
|
|
131633
|
+
if (!previous) {
|
|
131634
|
+
return;
|
|
131635
|
+
}
|
|
131636
|
+
await this.manager.repo.upsertDocMeta(machineRoomId, {
|
|
131637
|
+
localProjects: {
|
|
131638
|
+
...existing,
|
|
131639
|
+
[args2.localProjectId]: {
|
|
131640
|
+
...previous,
|
|
131641
|
+
history: {
|
|
131642
|
+
...previous.history ?? {},
|
|
131643
|
+
[this.providerKey]: {
|
|
131644
|
+
lastListedAt,
|
|
131645
|
+
sessions: Object.fromEntries(sessions.map((item) => [
|
|
131646
|
+
item.acpSessionId,
|
|
131647
|
+
item
|
|
131648
|
+
]))
|
|
131649
|
+
}
|
|
131650
|
+
}
|
|
131651
|
+
}
|
|
131652
|
+
}
|
|
131653
|
+
});
|
|
131654
|
+
});
|
|
131655
|
+
return catalog;
|
|
131656
|
+
}
|
|
131657
|
+
async importNewSession(args2) {
|
|
131658
|
+
const sessionId = v4();
|
|
131659
|
+
const roomId = getSessionRoomId(sessionId);
|
|
131660
|
+
const nowMs = getServerNow();
|
|
131661
|
+
const lastMessageAt = resolveSourceUpdatedAtMs(args2.info, nowMs);
|
|
131662
|
+
const meta = {
|
|
131663
|
+
id: sessionId,
|
|
131664
|
+
machineId: this.context.machineId,
|
|
131665
|
+
createdAt: new Date(nowMs).toISOString(),
|
|
131666
|
+
userId: this.context.userId,
|
|
131667
|
+
status: SessionStatusFactory.idle(),
|
|
131668
|
+
isArchived: false,
|
|
131669
|
+
origin: "external-acp",
|
|
131670
|
+
cliType: this.provider.cliType,
|
|
131671
|
+
agentType: this.provider.agentType,
|
|
131672
|
+
project: args2.project,
|
|
131673
|
+
title: resolveSessionTitle(args2.info, this.provider),
|
|
131674
|
+
lastMessageAt,
|
|
131675
|
+
externalHistory: buildMetadataOnlyExternalHistoryMeta({
|
|
131676
|
+
provider: this.provider,
|
|
131677
|
+
sourceAcpSessionId: args2.acpSessionId,
|
|
131678
|
+
sourceUpdatedAt: args2.info.updatedAt
|
|
131679
|
+
})
|
|
131680
|
+
};
|
|
131681
|
+
await this.manager.repo.upsertDocMeta(roomId, meta);
|
|
131682
|
+
return {
|
|
131683
|
+
sessionId,
|
|
131684
|
+
meta
|
|
131685
|
+
};
|
|
131686
|
+
}
|
|
131687
|
+
async refreshExistingSession(args2) {
|
|
131688
|
+
const externalHistory = args2.existing.meta.externalHistory;
|
|
131689
|
+
if (!externalHistory || getLocalProjectHistoryProviderKey(externalHistory.provider) !== this.providerKey) {
|
|
131690
|
+
return "skipped";
|
|
131691
|
+
}
|
|
131692
|
+
if (shouldSkipBySourceUpdatedAt(args2.info, externalHistory)) {
|
|
131693
|
+
return "skipped";
|
|
131694
|
+
}
|
|
131695
|
+
const replayNotifications = await loadHistorySessionReplay({
|
|
131696
|
+
provider: this.provider,
|
|
131697
|
+
rootPath: args2.rootPath,
|
|
131698
|
+
acpSessionId: args2.acpSessionId,
|
|
131699
|
+
logger: this.logger
|
|
131700
|
+
});
|
|
131701
|
+
const materialized = materializeReplay({
|
|
131702
|
+
provider: this.provider,
|
|
131703
|
+
acpSessionId: args2.acpSessionId,
|
|
131704
|
+
replayNotifications,
|
|
131705
|
+
userId: this.context.userId
|
|
131706
|
+
});
|
|
131707
|
+
const replayDecision = decideHistoryRefresh({
|
|
131708
|
+
externalHistory,
|
|
131709
|
+
replayDigest: materialized.replayDigest,
|
|
131710
|
+
turnHashes: materialized.turnHashes
|
|
131711
|
+
});
|
|
131712
|
+
if (replayDecision.reason === "digest_match") {
|
|
131713
|
+
await this.manager.repo.upsertDocMeta(getSessionRoomId(args2.existing.sessionId), {
|
|
131714
|
+
origin: "external-acp",
|
|
131715
|
+
externalHistory: buildExternalHistoryMeta({
|
|
131716
|
+
provider: this.provider,
|
|
131717
|
+
sourceAcpSessionId: args2.acpSessionId,
|
|
131718
|
+
sourceUpdatedAt: args2.info.updatedAt,
|
|
131719
|
+
materialized
|
|
131720
|
+
})
|
|
131721
|
+
});
|
|
131722
|
+
return "skipped";
|
|
131723
|
+
}
|
|
131724
|
+
if (replayDecision.status === "conflicted") {
|
|
131725
|
+
await this.markConflict(args2.existing.sessionId, args2.info, materialized, replayDecision.reason);
|
|
131726
|
+
return "conflicted";
|
|
131727
|
+
}
|
|
131728
|
+
const sessionDoc = await this.manager.getOrCreateSessionDoc(args2.existing.sessionId);
|
|
131729
|
+
const currentHistory = await sessionDoc.getHistory();
|
|
131730
|
+
const appendDecision = decideHistoryRefresh({
|
|
131731
|
+
externalHistory,
|
|
131732
|
+
replayDigest: materialized.replayDigest,
|
|
131733
|
+
turnHashes: materialized.turnHashes,
|
|
131734
|
+
currentHistoryLength: currentHistory.length
|
|
131735
|
+
});
|
|
131736
|
+
if (appendDecision.status === "conflicted") {
|
|
131737
|
+
await this.markConflict(args2.existing.sessionId, args2.info, materialized, appendDecision.reason);
|
|
131738
|
+
const synced2 = await sessionDoc.waitUntilSynced();
|
|
131739
|
+
if (!synced2) {
|
|
131740
|
+
this.logger.debug(`[${this.providerKey}-history-sync] Conflict marker for ${args2.existing.sessionId} did not confirm sync before unload; clients may see the previous state until next sync.`);
|
|
131741
|
+
}
|
|
131742
|
+
await this.manager.cleanSessionDoc(args2.existing.sessionId, {
|
|
131743
|
+
preserveStatus: true
|
|
131744
|
+
});
|
|
131745
|
+
return "conflicted";
|
|
131746
|
+
}
|
|
131747
|
+
const suffix = materialized.history.slice(appendDecision.appendFromIndex);
|
|
131748
|
+
await sessionDoc.updateHistory((history) => [
|
|
131749
|
+
...history,
|
|
131750
|
+
...suffix
|
|
131751
|
+
]);
|
|
131752
|
+
await this.manager.repo.upsertDocMeta(getSessionRoomId(args2.existing.sessionId), {
|
|
131753
|
+
origin: "external-acp",
|
|
131754
|
+
lastMessageAt: resolveSourceUpdatedAtMs(args2.info, getServerNow()),
|
|
131755
|
+
externalHistory: buildExternalHistoryMeta({
|
|
131756
|
+
provider: this.provider,
|
|
131757
|
+
sourceAcpSessionId: args2.acpSessionId,
|
|
131758
|
+
sourceUpdatedAt: args2.info.updatedAt,
|
|
131759
|
+
materialized
|
|
131760
|
+
})
|
|
131761
|
+
});
|
|
131762
|
+
const synced = await sessionDoc.waitUntilSynced();
|
|
131763
|
+
if (!synced) {
|
|
131764
|
+
this.logger.debug(`[${this.providerKey}-history-sync] Appended history for ${args2.existing.sessionId} did not confirm sync before unload; other clients may see the previous state until next sync.`);
|
|
131765
|
+
}
|
|
131766
|
+
await this.manager.cleanSessionDoc(args2.existing.sessionId, {
|
|
131767
|
+
preserveStatus: true
|
|
131768
|
+
});
|
|
131769
|
+
return suffix.length > 0 ? "refreshed" : "skipped";
|
|
131770
|
+
}
|
|
131771
|
+
async markConflict(sessionId, info, materialized, reason) {
|
|
131772
|
+
await this.manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
|
|
131773
|
+
origin: "external-acp",
|
|
131774
|
+
externalHistory: buildExternalHistoryMeta({
|
|
131775
|
+
provider: this.provider,
|
|
131776
|
+
sourceAcpSessionId: info.sessionId,
|
|
131777
|
+
sourceUpdatedAt: info.updatedAt,
|
|
131778
|
+
materialized,
|
|
131779
|
+
status: "sync_conflict",
|
|
131780
|
+
conflictReason: reason
|
|
131781
|
+
})
|
|
131782
|
+
});
|
|
131783
|
+
}
|
|
131784
|
+
}
|
|
131785
|
+
const HISTORY_REQUEST_TYPES = /* @__PURE__ */ new Set([
|
|
131786
|
+
"local-project/sync-history",
|
|
131787
|
+
"local-project/import-history"
|
|
131788
|
+
]);
|
|
131789
|
+
function isHistoryRequestType(value) {
|
|
131790
|
+
return HISTORY_REQUEST_TYPES.has(value);
|
|
131791
|
+
}
|
|
131792
|
+
function precheckLocalProjectHistoryRequest(args2) {
|
|
131793
|
+
const { request, expectedMachineId, expectedWorkspaceId } = args2;
|
|
131794
|
+
if (request.machineId !== expectedMachineId) {
|
|
131795
|
+
return {
|
|
131796
|
+
ok: false,
|
|
131797
|
+
error: "machine_mismatch",
|
|
131798
|
+
message: `Machine mismatch: expected ${expectedMachineId}`
|
|
131799
|
+
};
|
|
131800
|
+
}
|
|
131801
|
+
if (!isHistoryRequestType(request.type)) {
|
|
131802
|
+
return {
|
|
131803
|
+
ok: false,
|
|
131804
|
+
error: "invalid_request",
|
|
131805
|
+
message: `Unsupported remote local project control request: ${request.type}`
|
|
131806
|
+
};
|
|
131807
|
+
}
|
|
131808
|
+
const historyRequest = request;
|
|
131809
|
+
const requesterUserId = historyRequest.requestedByUserId?.trim();
|
|
131810
|
+
if (!requesterUserId) {
|
|
131811
|
+
return {
|
|
131812
|
+
ok: false,
|
|
131813
|
+
error: "invalid_request",
|
|
131814
|
+
message: "Local project history requests require requestedByUserId"
|
|
131815
|
+
};
|
|
131816
|
+
}
|
|
131817
|
+
if (historyRequest.workspaceId !== expectedWorkspaceId) {
|
|
131818
|
+
return {
|
|
131819
|
+
ok: false,
|
|
131820
|
+
error: "workspace_not_found",
|
|
131821
|
+
message: `Workspace mismatch: expected ${expectedWorkspaceId}`
|
|
131822
|
+
};
|
|
131823
|
+
}
|
|
131824
|
+
return {
|
|
131825
|
+
ok: true,
|
|
131826
|
+
request: historyRequest,
|
|
131827
|
+
requesterUserId
|
|
131828
|
+
};
|
|
131829
|
+
}
|
|
130741
131830
|
const SESSION_IMAGE_MIME_TYPE_BY_EXTENSION = {
|
|
130742
131831
|
png: "image/png",
|
|
130743
131832
|
jpg: "image/jpeg",
|
|
@@ -130904,6 +131993,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
130904
131993
|
requestedByUserId,
|
|
130905
131994
|
reason
|
|
130906
131995
|
}),
|
|
131996
|
+
dispatchLocalProjectControl: async (request) => await this.dispatchLocalProjectControlViaRpc(request),
|
|
130907
131997
|
onFatalAuthFailure: (error2) => this.onFatalAuthFailure?.(error2)
|
|
130908
131998
|
});
|
|
130909
131999
|
} else {
|
|
@@ -131789,6 +132879,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
131789
132879
|
cliVersion: this.cliVersion,
|
|
131790
132880
|
os: process.platform,
|
|
131791
132881
|
rpcVersion: supportsStreamsRpc ? LORO_STREAMS_RPC_VERSION : void 0,
|
|
132882
|
+
supportsLocalProjectHistoryRpc: supportsStreamsRpc,
|
|
131792
132883
|
supportRegistryAgentTypes: this.supportRegistryAgentTypes,
|
|
131793
132884
|
sessions: [],
|
|
131794
132885
|
needToArchiveSessions: {},
|
|
@@ -132626,6 +133717,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
132626
133717
|
cliVersion: this.cliVersion,
|
|
132627
133718
|
os: process.platform,
|
|
132628
133719
|
rpcVersion: supportsStreamsRpc ? LORO_STREAMS_RPC_VERSION : machineMeta?.rpcVersion,
|
|
133720
|
+
supportsLocalProjectHistoryRpc: supportsStreamsRpc,
|
|
132629
133721
|
supportRegistryAgentTypes: this.supportRegistryAgentTypes,
|
|
132630
133722
|
acpCapabilities: machineMeta?.acpCapabilities,
|
|
132631
133723
|
sessions: machineMeta?.sessions ?? [],
|
|
@@ -133266,8 +134358,74 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
133266
134358
|
}
|
|
133267
134359
|
await this.workspaceDocument.registerMachine(this.machineId, {
|
|
133268
134360
|
...existingMeta,
|
|
133269
|
-
rpcVersion: LORO_STREAMS_RPC_VERSION
|
|
134361
|
+
rpcVersion: LORO_STREAMS_RPC_VERSION,
|
|
134362
|
+
supportsLocalProjectHistoryRpc: true
|
|
134363
|
+
});
|
|
134364
|
+
}
|
|
134365
|
+
toLocalProjectControlError(type2, error2, message, data) {
|
|
134366
|
+
return {
|
|
134367
|
+
ok: false,
|
|
134368
|
+
type: type2,
|
|
134369
|
+
error: error2,
|
|
134370
|
+
message,
|
|
134371
|
+
data
|
|
134372
|
+
};
|
|
134373
|
+
}
|
|
134374
|
+
async dispatchLocalProjectControlViaRpc(message) {
|
|
134375
|
+
const requestType = message.type;
|
|
134376
|
+
const precheck = precheckLocalProjectHistoryRequest({
|
|
134377
|
+
request: message,
|
|
134378
|
+
expectedMachineId: this.machineId,
|
|
134379
|
+
expectedWorkspaceId: this.workspaceId
|
|
133270
134380
|
});
|
|
134381
|
+
if (!precheck.ok) {
|
|
134382
|
+
return this.toLocalProjectControlError(requestType, precheck.error, precheck.message);
|
|
134383
|
+
}
|
|
134384
|
+
const { requesterUserId, request } = precheck;
|
|
134385
|
+
try {
|
|
134386
|
+
const access = await canUseMachineForCliToken({
|
|
134387
|
+
token: this.token,
|
|
134388
|
+
workspaceId: this.workspaceId,
|
|
134389
|
+
machineId: this.machineId,
|
|
134390
|
+
requesterUserId,
|
|
134391
|
+
localProjectId: request.localProjectId
|
|
134392
|
+
});
|
|
134393
|
+
if (!access.allowed) {
|
|
134394
|
+
return this.toLocalProjectControlError(requestType, "access_denied", `Machine access denied: ${access.reason}`);
|
|
134395
|
+
}
|
|
134396
|
+
const rootPath = await resolveWorkspaceLocalProjectRootPath(this.workspaceDocument.repo, this.machineId, request.localProjectId);
|
|
134397
|
+
if (!rootPath) {
|
|
134398
|
+
return this.toLocalProjectControlError(requestType, "local_project_not_found", `Local project not found in workspace: ${request.localProjectId}`);
|
|
134399
|
+
}
|
|
134400
|
+
const service = new LocalProjectHistorySyncService(this.workspaceDocument, this.logger, {
|
|
134401
|
+
workspaceId: this.workspaceId,
|
|
134402
|
+
machineId: this.machineId,
|
|
134403
|
+
userId: this.userId
|
|
134404
|
+
}, request.provider);
|
|
134405
|
+
if (request.type === "local-project/sync-history") {
|
|
134406
|
+
const result2 = await service.syncLocalProject({
|
|
134407
|
+
localProjectId: request.localProjectId,
|
|
134408
|
+
rootPath
|
|
134409
|
+
});
|
|
134410
|
+
return {
|
|
134411
|
+
ok: true,
|
|
134412
|
+
type: "local-project/sync-history",
|
|
134413
|
+
result: result2
|
|
134414
|
+
};
|
|
134415
|
+
}
|
|
134416
|
+
const result = await service.importLocalProjectSessions({
|
|
134417
|
+
localProjectId: request.localProjectId,
|
|
134418
|
+
rootPath,
|
|
134419
|
+
acpSessionIds: request.acpSessionIds
|
|
134420
|
+
});
|
|
134421
|
+
return {
|
|
134422
|
+
ok: true,
|
|
134423
|
+
type: "local-project/import-history",
|
|
134424
|
+
result
|
|
134425
|
+
};
|
|
134426
|
+
} catch (error2) {
|
|
134427
|
+
return this.toLocalProjectControlError(requestType, "execution_failed", formatErrorMessage(error2));
|
|
134428
|
+
}
|
|
133271
134429
|
}
|
|
133272
134430
|
cancelPendingPermissionRequests() {
|
|
133273
134431
|
this.permissionRequestStartTimes.clear();
|
|
@@ -133400,8 +134558,15 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
133400
134558
|
if (meta.processingUserMsgId) {
|
|
133401
134559
|
return true;
|
|
133402
134560
|
}
|
|
134561
|
+
if (meta.lastGoalCommand) {
|
|
134562
|
+
return true;
|
|
134563
|
+
}
|
|
133403
134564
|
return Boolean(meta.latestUserMsgId && meta.latestUserMsgId !== meta.lastHandledUserMsgId);
|
|
133404
134565
|
}
|
|
134566
|
+
async hasActiveGoal(sessionId) {
|
|
134567
|
+
const meta = (await this.workspaceDocument.repo.getDocMeta(getSessionRoomId(sessionId)))?.meta;
|
|
134568
|
+
return isSessionGoalWorking(meta?.latestGoal);
|
|
134569
|
+
}
|
|
133405
134570
|
isArchiveInFlight(sessionId) {
|
|
133406
134571
|
return this.archiveInFlight.has(sessionId);
|
|
133407
134572
|
}
|
|
@@ -133979,6 +135144,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
133979
135144
|
if (this.deps.hasActiveTurn(sessionId)) {
|
|
133980
135145
|
return false;
|
|
133981
135146
|
}
|
|
135147
|
+
if (await this.deps.hasActiveGoal(sessionId)) {
|
|
135148
|
+
return false;
|
|
135149
|
+
}
|
|
133982
135150
|
if (this.deps.hasPendingUpdates(sessionId)) {
|
|
133983
135151
|
return false;
|
|
133984
135152
|
}
|
|
@@ -134118,6 +135286,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
|
|
|
134118
135286
|
this.gcManager = new SessionGCManager(gcConfig, {
|
|
134119
135287
|
getSessionLastActivity: (sessionId) => handler.getLastActivity(sessionId),
|
|
134120
135288
|
hasActiveTurn: (sessionId) => handler.hasActiveTurn(sessionId),
|
|
135289
|
+
hasActiveGoal: async (sessionId) => await handler.hasActiveGoal(sessionId),
|
|
134121
135290
|
hasPendingUpdates: (sessionId) => handler.hasPendingUpdates(sessionId),
|
|
134122
135291
|
hasPendingUserWork: async (sessionId) => await handler.hasPendingUserWork(sessionId),
|
|
134123
135292
|
isArchiveInFlight: (sessionId) => handler.isArchiveInFlight(sessionId),
|
|
@@ -142115,6 +143284,18 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142115
143284
|
}
|
|
142116
143285
|
return typeof value.error === "string";
|
|
142117
143286
|
}
|
|
143287
|
+
function isLocalProjectHistorySyncSummary(value) {
|
|
143288
|
+
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");
|
|
143289
|
+
}
|
|
143290
|
+
function isLocalProjectHistoryCatalogItem(value) {
|
|
143291
|
+
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");
|
|
143292
|
+
}
|
|
143293
|
+
function isLocalProjectHistoryCatalogResult(value) {
|
|
143294
|
+
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);
|
|
143295
|
+
}
|
|
143296
|
+
function isLocalProjectHistoryImportResult(value) {
|
|
143297
|
+
return isObjectRecord(value) && isLocalProjectHistorySyncSummary(value.summary) && isLocalProjectHistoryCatalogResult(value.catalog);
|
|
143298
|
+
}
|
|
142118
143299
|
function isWorkspaceIds(value) {
|
|
142119
143300
|
return isStringArray(value);
|
|
142120
143301
|
}
|
|
@@ -142146,6 +143327,12 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142146
143327
|
if (value.type === "local-project/checkout-branch") {
|
|
142147
143328
|
return isLocalProjectCheckoutBranchResult(value.result);
|
|
142148
143329
|
}
|
|
143330
|
+
if (value.type === "local-project/sync-history") {
|
|
143331
|
+
return isLocalProjectHistoryCatalogResult(value.result);
|
|
143332
|
+
}
|
|
143333
|
+
if (value.type === "local-project/import-history") {
|
|
143334
|
+
return isLocalProjectHistoryImportResult(value.result);
|
|
143335
|
+
}
|
|
142149
143336
|
return false;
|
|
142150
143337
|
}
|
|
142151
143338
|
const SESSION_CONTROL_PATH$2 = "/session-control";
|
|
@@ -142160,6 +143347,8 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
142160
143347
|
"local-project/list-files",
|
|
142161
143348
|
"local-project/read-file",
|
|
142162
143349
|
"local-project/checkout-branch",
|
|
143350
|
+
"local-project/sync-history",
|
|
143351
|
+
"local-project/import-history",
|
|
142163
143352
|
"worktree/list-files",
|
|
142164
143353
|
"worktree/read-file"
|
|
142165
143354
|
];
|
|
@@ -143433,10 +144622,12 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
143433
144622
|
const existing = rawExisting && typeof rawExisting === "object" ? rawExisting : {};
|
|
143434
144623
|
const previous = existing[entry2.localProjectId];
|
|
143435
144624
|
const nowMs = getServerNow();
|
|
143436
|
-
|
|
144625
|
+
const repo = runtime.lody.documentManager.repo;
|
|
144626
|
+
await repo.upsertDocMeta(machineRoomId, {
|
|
143437
144627
|
localProjects: {
|
|
143438
144628
|
...existing,
|
|
143439
144629
|
[entry2.localProjectId]: {
|
|
144630
|
+
...previous ?? {},
|
|
143440
144631
|
id: entry2.localProjectId,
|
|
143441
144632
|
name: entry2.name,
|
|
143442
144633
|
rootPath: entry2.rootPath,
|
|
@@ -143458,7 +144649,8 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
143458
144649
|
...existing
|
|
143459
144650
|
};
|
|
143460
144651
|
delete next[localProjectId];
|
|
143461
|
-
|
|
144652
|
+
const repo = runtime.lody.documentManager.repo;
|
|
144653
|
+
await repo.upsertDocMeta(machineRoomId, {
|
|
143462
144654
|
localProjects: next
|
|
143463
144655
|
});
|
|
143464
144656
|
}
|
|
@@ -143618,6 +144810,43 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
|
|
|
143618
144810
|
result: this.localProjectControlService.checkoutProjectBranch(rootPath, message.branchName)
|
|
143619
144811
|
};
|
|
143620
144812
|
}
|
|
144813
|
+
if (message.type === "local-project/sync-history") {
|
|
144814
|
+
const runtime = await this.resolveWorkspaceRuntime(message.workspaceId);
|
|
144815
|
+
const rootPath = await this.resolveWorkspaceProjectRootPath(runtime, message.localProjectId);
|
|
144816
|
+
const service = new LocalProjectHistorySyncService(runtime.lody.documentManager, this.logger, {
|
|
144817
|
+
workspaceId: message.workspaceId,
|
|
144818
|
+
machineId: this.machineId,
|
|
144819
|
+
userId: this.userId
|
|
144820
|
+
}, message.provider);
|
|
144821
|
+
const result = await service.syncLocalProject({
|
|
144822
|
+
localProjectId: message.localProjectId,
|
|
144823
|
+
rootPath
|
|
144824
|
+
});
|
|
144825
|
+
return {
|
|
144826
|
+
ok: true,
|
|
144827
|
+
type: "local-project/sync-history",
|
|
144828
|
+
result
|
|
144829
|
+
};
|
|
144830
|
+
}
|
|
144831
|
+
if (message.type === "local-project/import-history") {
|
|
144832
|
+
const runtime = await this.resolveWorkspaceRuntime(message.workspaceId);
|
|
144833
|
+
const rootPath = await this.resolveWorkspaceProjectRootPath(runtime, message.localProjectId);
|
|
144834
|
+
const service = new LocalProjectHistorySyncService(runtime.lody.documentManager, this.logger, {
|
|
144835
|
+
workspaceId: message.workspaceId,
|
|
144836
|
+
machineId: this.machineId,
|
|
144837
|
+
userId: this.userId
|
|
144838
|
+
}, message.provider);
|
|
144839
|
+
const result = await service.importLocalProjectSessions({
|
|
144840
|
+
localProjectId: message.localProjectId,
|
|
144841
|
+
rootPath,
|
|
144842
|
+
acpSessionIds: message.acpSessionIds
|
|
144843
|
+
});
|
|
144844
|
+
return {
|
|
144845
|
+
ok: true,
|
|
144846
|
+
type: "local-project/import-history",
|
|
144847
|
+
result
|
|
144848
|
+
};
|
|
144849
|
+
}
|
|
143621
144850
|
if (message.type === "worktree/list-files") {
|
|
143622
144851
|
return {
|
|
143623
144852
|
ok: true,
|
|
@@ -175829,6 +177058,7 @@ ${result.stderr}`;
|
|
|
175829
177058
|
this.lastExitCode = result.code;
|
|
175830
177059
|
this.lastExitAtMs = Date.now();
|
|
175831
177060
|
this.publishState();
|
|
177061
|
+
if (!this.triggered) return;
|
|
175832
177062
|
if (this.restartPending) return;
|
|
175833
177063
|
if (isAlreadyRunningOutcome(result)) {
|
|
175834
177064
|
if (this.alreadyRunningIsFatal) {
|