@agentknight/sdk 0.9.42 → 0.9.44
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/LICENSE +201 -201
- package/dist/chunks/{acp-adapter-SPIMGGL2.js → acp-adapter-JKSOVQJ7.js} +3 -3
- package/dist/chunks/{chunk-ASHXZ46V.js → chunk-4UAMAGVT.js} +5 -2
- package/dist/chunks/{chunk-RBULXS7T.js → chunk-AK3YVBEP.js} +1 -1
- package/dist/chunks/chunk-EIFNERXM.js +55 -0
- package/dist/chunks/{chunk-SHOE7YAU.js → chunk-EWXFUECA.js} +5 -3
- package/dist/chunks/{chunk-VDC4G7ZG.js → chunk-LRSHP7WN.js} +2 -2
- package/dist/chunks/{chunk-LQIW4ARE.js → chunk-UNRPSTRM.js} +3 -1
- package/dist/chunks/{chunk-QTBB2U7G.js → chunk-W6TZVD7W.js} +34 -2
- package/dist/chunks/{chunk-QN3OD5UE.js → chunk-ZGO2OSQZ.js} +1 -1
- package/dist/chunks/{chunk-J52FWJU4.js → chunk-ZST7M4CD.js} +1 -1
- package/dist/chunks/claude-adapter-A5EFNV5S.js +1040 -0
- package/dist/chunks/codex-adapter-MMQK47KY.js +1249 -0
- package/dist/chunks/{copilot-adapter-A6QB4RD4.js → copilot-adapter-Y5W4ZUFD.js} +9 -9
- package/dist/chunks/{managed-agent-adapter-LMHQX6IH.js → managed-agent-adapter-HDLOSSHZ.js} +10 -8
- package/dist/chunks/{pi-adapter-NWQWUNXO.js → pi-adapter-6EF6Z5CP.js} +3 -3
- package/dist/index.d.ts +229 -218
- package/dist/index.js +406 -305
- package/dist/runtime.js +2 -2
- package/package.json +16 -13
package/dist/index.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
PeerChatRequestSchema,
|
|
5
5
|
PeerChatResultSchema,
|
|
6
6
|
validateDataDirectory
|
|
7
|
-
} from "./chunks/chunk-
|
|
7
|
+
} from "./chunks/chunk-ZGO2OSQZ.js";
|
|
8
8
|
import {
|
|
9
9
|
AgentSelectionSchema,
|
|
10
10
|
assertSelectionSnapshotFits,
|
|
11
11
|
supportsAgentSelection
|
|
12
|
-
} from "./chunks/chunk-
|
|
12
|
+
} from "./chunks/chunk-4UAMAGVT.js";
|
|
13
13
|
import {
|
|
14
14
|
ConversationNormalizer,
|
|
15
15
|
LiveTimelineProjection,
|
|
@@ -31,26 +31,33 @@ import {
|
|
|
31
31
|
resolveGitProjectIdentity,
|
|
32
32
|
sessionRestoreFailure,
|
|
33
33
|
validateOperationResponse
|
|
34
|
-
} from "./chunks/chunk-
|
|
34
|
+
} from "./chunks/chunk-EWXFUECA.js";
|
|
35
35
|
import {
|
|
36
36
|
GROUP_CHAT_MAX_TURNS,
|
|
37
37
|
GroupChatError
|
|
38
|
-
} from "./chunks/chunk-
|
|
38
|
+
} from "./chunks/chunk-LRSHP7WN.js";
|
|
39
|
+
import {
|
|
40
|
+
detectClaude,
|
|
41
|
+
detectCopilot,
|
|
42
|
+
detectPi,
|
|
43
|
+
hasLocalExecutable
|
|
44
|
+
} from "./chunks/chunk-EIFNERXM.js";
|
|
39
45
|
import {
|
|
40
46
|
assistantPreview,
|
|
41
47
|
isContentfulAssistant,
|
|
42
48
|
userPreview
|
|
43
|
-
} from "./chunks/chunk-
|
|
49
|
+
} from "./chunks/chunk-AK3YVBEP.js";
|
|
44
50
|
import {
|
|
45
51
|
AgentModelListingError,
|
|
46
52
|
AgentSessionRenameError,
|
|
53
|
+
CODING_AGENT_IDS,
|
|
47
54
|
SessionOperationError,
|
|
48
55
|
SessionOperations,
|
|
49
56
|
TimelinePageBudgetError,
|
|
50
57
|
UnsupportedAgentOperationError,
|
|
51
58
|
captureResearchAccess,
|
|
52
59
|
captureWorkbenchAccess
|
|
53
|
-
} from "./chunks/chunk-
|
|
60
|
+
} from "./chunks/chunk-UNRPSTRM.js";
|
|
54
61
|
import {
|
|
55
62
|
A2aRelayRequestSchema,
|
|
56
63
|
A2aRelayResponseSchema,
|
|
@@ -105,6 +112,9 @@ import {
|
|
|
105
112
|
SESSION_DIRECTORY_MAX_ENTRIES,
|
|
106
113
|
SESSION_DIRECTORY_MAX_RESPONSE_BYTES,
|
|
107
114
|
SESSION_FILE_ATTACHMENTS_CAPABILITY,
|
|
115
|
+
SESSION_FILE_DOWNLOAD_MAX_ORIGINAL_BYTES,
|
|
116
|
+
SESSION_FILE_DOWNLOAD_OPERATION,
|
|
117
|
+
SESSION_FILE_DOWNLOAD_VERSION,
|
|
108
118
|
SESSION_FILE_GZIP_THRESHOLD_BYTES,
|
|
109
119
|
SESSION_FILE_MAX_IMAGE_BYTES,
|
|
110
120
|
SESSION_FILE_MAX_IMAGE_EDGE,
|
|
@@ -121,6 +131,7 @@ import {
|
|
|
121
131
|
SESSION_GIT_DIFF_MAX_PATCH_BYTES,
|
|
122
132
|
SESSION_GIT_DIFF_MAX_RESPONSE_BYTES,
|
|
123
133
|
SESSION_GIT_DIFF_OPERATION,
|
|
134
|
+
SESSION_GIT_DIFF_PARTIAL_VERSION,
|
|
124
135
|
SESSION_GIT_DIFF_VERSION,
|
|
125
136
|
SESSION_IMAGE_API_PATH,
|
|
126
137
|
SESSION_IMAGE_MAX_BYTES,
|
|
@@ -152,11 +163,14 @@ import {
|
|
|
152
163
|
SessionDeleteRequestSchema,
|
|
153
164
|
SessionDirectoryListDataSchema,
|
|
154
165
|
SessionDirectoryListResultDataSchema,
|
|
166
|
+
SessionFileDownloadDataSchema,
|
|
167
|
+
SessionFileDownloadResultDataSchema,
|
|
155
168
|
SessionFileReadDataSchema,
|
|
156
169
|
SessionFileReadResultDataSchema,
|
|
157
170
|
SessionFileReadV2ResultDataSchema,
|
|
158
171
|
SessionGitDiffDataSchema,
|
|
159
172
|
SessionGitDiffGzipResultDataSchema,
|
|
173
|
+
SessionGitDiffPartialResultDataSchema,
|
|
160
174
|
SessionGitDiffResultDataSchema,
|
|
161
175
|
SessionImageCommitResultSchema,
|
|
162
176
|
SessionImageScopeSchema,
|
|
@@ -227,7 +241,7 @@ import {
|
|
|
227
241
|
sessionCommunicationError,
|
|
228
242
|
sessionCommunicationOperationCapability,
|
|
229
243
|
workbenchCapability
|
|
230
|
-
} from "./chunks/chunk-
|
|
244
|
+
} from "./chunks/chunk-W6TZVD7W.js";
|
|
231
245
|
|
|
232
246
|
// src/initialize.ts
|
|
233
247
|
import { createHash as createHash16 } from "node:crypto";
|
|
@@ -244,6 +258,8 @@ var genericIcon = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"
|
|
|
244
258
|
var presentations = {
|
|
245
259
|
copilot: { name: "GitHub Copilot", icon: copilotIcon },
|
|
246
260
|
pi: { name: "PI", icon: piIcon },
|
|
261
|
+
codex: { name: "Codex", icon: genericIcon },
|
|
262
|
+
claude: { name: "Claude Code", icon: genericIcon },
|
|
247
263
|
acp: { name: "ACP", icon: genericIcon }
|
|
248
264
|
};
|
|
249
265
|
var fallback = { name: "Agent", icon: genericIcon };
|
|
@@ -251,48 +267,6 @@ function codingAgentPresentation(agentId) {
|
|
|
251
267
|
return Object.hasOwn(presentations, agentId) ? presentations[agentId] : fallback;
|
|
252
268
|
}
|
|
253
269
|
|
|
254
|
-
// src/local-agent-discovery.ts
|
|
255
|
-
import { constants } from "node:fs";
|
|
256
|
-
import { access, stat } from "node:fs/promises";
|
|
257
|
-
import { homedir } from "node:os";
|
|
258
|
-
import { delimiter, isAbsolute, join } from "node:path";
|
|
259
|
-
import { fileURLToPath } from "node:url";
|
|
260
|
-
async function accessible(path, executable = false) {
|
|
261
|
-
try {
|
|
262
|
-
await access(path, executable && process.platform !== "win32" ? constants.X_OK : constants.F_OK);
|
|
263
|
-
return (await stat(path)).isFile();
|
|
264
|
-
} catch (error) {
|
|
265
|
-
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) return false;
|
|
266
|
-
throw error;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
async function hasLocalExecutable(command, environment2 = process.env) {
|
|
270
|
-
const extensions = process.platform === "win32" ? ["", ...(environment2.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
|
|
271
|
-
const paths = isAbsolute(command) || command.includes("/") || command.includes("\\") ? [command] : (environment2.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => join(directory, command));
|
|
272
|
-
for (const path of paths) {
|
|
273
|
-
for (const extension of extensions) {
|
|
274
|
-
if (await accessible(`${path}${extension}`, true)) return true;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
async function hasPackage(name) {
|
|
280
|
-
try {
|
|
281
|
-
return await accessible(fileURLToPath(import.meta.resolve(name)));
|
|
282
|
-
} catch (error) {
|
|
283
|
-
if (error instanceof Error && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") return false;
|
|
284
|
-
throw error;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
async function detectCopilot() {
|
|
288
|
-
return process.env.COPILOT_CLI_PATH ? accessible(process.env.COPILOT_CLI_PATH) : await hasPackage("@github/copilot/npm-loader.js") || hasLocalExecutable("copilot");
|
|
289
|
-
}
|
|
290
|
-
async function detectPi(agentDirectory) {
|
|
291
|
-
if (!await hasPackage("@earendil-works/pi-coding-agent")) return false;
|
|
292
|
-
const directory = agentDirectory ?? (process.env.PI_CODING_AGENT_DIR || join(homedir(), ".pi", "agent"));
|
|
293
|
-
return agentDirectory !== void 0 || await accessible(join(directory, "auth.json")) || await hasLocalExecutable("pi");
|
|
294
|
-
}
|
|
295
|
-
|
|
296
270
|
// src/coding-agent-factory.ts
|
|
297
271
|
function assertPiRuntimeSupported(nodeVersion = process.versions.node) {
|
|
298
272
|
const [major, minor] = nodeVersion.split(".").map(Number);
|
|
@@ -305,7 +279,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
305
279
|
switch (engine.kind) {
|
|
306
280
|
case "managed": {
|
|
307
281
|
if (owner === void 0) throw new Error("Managed coding agents require the PC owner identity");
|
|
308
|
-
const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-
|
|
282
|
+
const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-HDLOSSHZ.js");
|
|
309
283
|
return new ManagedCodingAgentAdapter({
|
|
310
284
|
dataDirectory: config.dataDirectory,
|
|
311
285
|
owner,
|
|
@@ -331,6 +305,24 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
331
305
|
}
|
|
332
306
|
}, onDiagnostic)
|
|
333
307
|
},
|
|
308
|
+
{
|
|
309
|
+
id: "codex",
|
|
310
|
+
name: codingAgentPresentation("codex").name,
|
|
311
|
+
detect: () => hasLocalExecutable(engine.codexCommand ?? "codex"),
|
|
312
|
+
create: () => createCodingAgentAdapter({
|
|
313
|
+
...config,
|
|
314
|
+
codingAgent: {
|
|
315
|
+
kind: "codex",
|
|
316
|
+
...engine.codexCommand === void 0 ? {} : { command: engine.codexCommand }
|
|
317
|
+
}
|
|
318
|
+
}, onDiagnostic)
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: "claude",
|
|
322
|
+
name: codingAgentPresentation("claude").name,
|
|
323
|
+
detect: detectClaude,
|
|
324
|
+
create: () => createCodingAgentAdapter({ ...config, codingAgent: { kind: "claude" } }, onDiagnostic)
|
|
325
|
+
},
|
|
334
326
|
{
|
|
335
327
|
id: "acp",
|
|
336
328
|
name: codingAgentPresentation("acp").name,
|
|
@@ -344,7 +336,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
344
336
|
});
|
|
345
337
|
}
|
|
346
338
|
case "copilot": {
|
|
347
|
-
const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-
|
|
339
|
+
const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-Y5W4ZUFD.js");
|
|
348
340
|
if (owner !== void 0) {
|
|
349
341
|
return new GithubCopilotAdapter(void 0, void 0, void 0, {
|
|
350
342
|
dataDirectory: config.dataDirectory,
|
|
@@ -364,13 +356,28 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
364
356
|
}
|
|
365
357
|
case "pi": {
|
|
366
358
|
assertPiRuntimeSupported();
|
|
367
|
-
const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-
|
|
359
|
+
const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-6EF6Z5CP.js");
|
|
368
360
|
return new PiCodingAgentAdapter(
|
|
369
361
|
engine.agentDirectory === void 0 ? {} : { agentDirectory: resolve(engine.agentDirectory) }
|
|
370
362
|
);
|
|
371
363
|
}
|
|
364
|
+
case "codex": {
|
|
365
|
+
const { CodexCodingAgentAdapter } = await import("./chunks/codex-adapter-MMQK47KY.js");
|
|
366
|
+
return new CodexCodingAgentAdapter({
|
|
367
|
+
dataDirectory: resolve(config.dataDirectory, "coding-agents"),
|
|
368
|
+
...engine.command === void 0 ? {} : { command: engine.command },
|
|
369
|
+
...onDiagnostic === void 0 ? {} : { onDiagnostic }
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
case "claude": {
|
|
373
|
+
const { ClaudeCodingAgentAdapter } = await import("./chunks/claude-adapter-A5EFNV5S.js");
|
|
374
|
+
return new ClaudeCodingAgentAdapter({
|
|
375
|
+
dataDirectory: resolve(config.dataDirectory, "coding-agents"),
|
|
376
|
+
...onDiagnostic === void 0 ? {} : { onDiagnostic }
|
|
377
|
+
});
|
|
378
|
+
}
|
|
372
379
|
case "acp": {
|
|
373
|
-
const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-
|
|
380
|
+
const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-JKSOVQJ7.js");
|
|
374
381
|
return new AcpCodingAgentAdapter({
|
|
375
382
|
command: engine.command,
|
|
376
383
|
args: engine.args,
|
|
@@ -413,7 +420,7 @@ import {
|
|
|
413
420
|
writeFileSync
|
|
414
421
|
} from "node:fs";
|
|
415
422
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
416
|
-
import { join
|
|
423
|
+
import { join } from "node:path";
|
|
417
424
|
import { z as z2 } from "zod";
|
|
418
425
|
var KEY_BYTES = 32;
|
|
419
426
|
var NONCE_BYTES = 12;
|
|
@@ -480,7 +487,7 @@ function generateContentKey() {
|
|
|
480
487
|
return parseContentKey(randomBytes(KEY_BYTES).toString("base64url"));
|
|
481
488
|
}
|
|
482
489
|
async function loadOrCreateContentKey(dataDirectory) {
|
|
483
|
-
const path =
|
|
490
|
+
const path = join(dataDirectory, "content-key.json");
|
|
484
491
|
await mkdir(dataDirectory, { recursive: true });
|
|
485
492
|
try {
|
|
486
493
|
const stored = ContentKeyFileSchema.parse(
|
|
@@ -509,7 +516,7 @@ async function loadOrCreateContentKey(dataDirectory) {
|
|
|
509
516
|
}
|
|
510
517
|
async function createPersistentContentCipher(dataDirectory, key) {
|
|
511
518
|
await mkdir(dataDirectory, { recursive: true });
|
|
512
|
-
const path =
|
|
519
|
+
const path = join(dataDirectory, "content-replay.log");
|
|
513
520
|
let processedMessages = /* @__PURE__ */ new Map();
|
|
514
521
|
let compactOnNextWrite = false;
|
|
515
522
|
try {
|
|
@@ -861,15 +868,15 @@ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
|
861
868
|
|
|
862
869
|
// src/research-tools.ts
|
|
863
870
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
|
|
864
|
-
import { lstat as lstat3, realpath as realpath3, stat as
|
|
865
|
-
import { isAbsolute as
|
|
871
|
+
import { lstat as lstat3, realpath as realpath3, stat as stat3 } from "node:fs/promises";
|
|
872
|
+
import { isAbsolute as isAbsolute3, join as join4 } from "node:path";
|
|
866
873
|
import { gunzipSync } from "node:zlib";
|
|
867
874
|
import { z as z5 } from "zod";
|
|
868
875
|
|
|
869
876
|
// src/session-directory.ts
|
|
870
877
|
import { createHash as createHash2 } from "node:crypto";
|
|
871
|
-
import { lstat, opendir, realpath, stat
|
|
872
|
-
import { isAbsolute
|
|
878
|
+
import { lstat, opendir, realpath, stat } from "node:fs/promises";
|
|
879
|
+
import { isAbsolute, join as join2, relative, sep } from "node:path";
|
|
873
880
|
var SESSION_DIRECTORY_CONCURRENCY = 4;
|
|
874
881
|
var SESSION_DIRECTORY_TIMEOUT_MS = 5e3;
|
|
875
882
|
var SessionDirectoryError = class extends Error {
|
|
@@ -884,7 +891,7 @@ function fail(code, message) {
|
|
|
884
891
|
}
|
|
885
892
|
function contained(root, candidate) {
|
|
886
893
|
const path = relative(root, candidate);
|
|
887
|
-
return path === "" || !
|
|
894
|
+
return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
|
|
888
895
|
}
|
|
889
896
|
function sessionRootIdentity(root, info) {
|
|
890
897
|
return `${root}\0${info.dev}\0${info.ino}`;
|
|
@@ -933,14 +940,14 @@ var SessionDirectoryLister = class {
|
|
|
933
940
|
checkDeadline();
|
|
934
941
|
if (metadata === void 0) fail("SESSION_NOT_FOUND", "The session is unavailable");
|
|
935
942
|
const workingDirectory = metadata.workingDirectory;
|
|
936
|
-
if (!workingDirectory || !
|
|
943
|
+
if (!workingDirectory || !isAbsolute(workingDirectory)) {
|
|
937
944
|
fail("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
|
|
938
945
|
}
|
|
939
946
|
let root;
|
|
940
947
|
let rootIdentity;
|
|
941
948
|
try {
|
|
942
949
|
root = await realpath(workingDirectory);
|
|
943
|
-
const info = await
|
|
950
|
+
const info = await stat(root, { bigint: true });
|
|
944
951
|
if (!info.isDirectory()) fail("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
|
|
945
952
|
rootIdentity = sessionRootIdentity(root, info);
|
|
946
953
|
} catch (error) {
|
|
@@ -953,7 +960,7 @@ var SessionDirectoryLister = class {
|
|
|
953
960
|
checkDeadline();
|
|
954
961
|
let target = root;
|
|
955
962
|
for (const segment of path === "" ? [] : path.split("/")) {
|
|
956
|
-
target =
|
|
963
|
+
target = join2(target, segment);
|
|
957
964
|
const info = await lstat(target);
|
|
958
965
|
checkDeadline();
|
|
959
966
|
if (info.isSymbolicLink()) fail("DIRECTORY_BLOCKED", "Links cannot be opened");
|
|
@@ -978,7 +985,7 @@ var SessionDirectoryLister = class {
|
|
|
978
985
|
}
|
|
979
986
|
let kind = "blocked";
|
|
980
987
|
if (isSafeSessionDirectoryPath(entry.name) && !entry.name.includes("/")) {
|
|
981
|
-
const child =
|
|
988
|
+
const child = join2(target, entry.name);
|
|
982
989
|
const info = await lstat(child);
|
|
983
990
|
checkDeadline();
|
|
984
991
|
if (!info.isSymbolicLink() && contained(root, await realpath(child))) {
|
|
@@ -994,7 +1001,7 @@ var SessionDirectoryLister = class {
|
|
|
994
1001
|
}
|
|
995
1002
|
const latestMetadata = await this.metadata(sessionId);
|
|
996
1003
|
const latestRoot = await realpath(workingDirectory);
|
|
997
|
-
const latestInfo = await
|
|
1004
|
+
const latestInfo = await stat(latestRoot, { bigint: true });
|
|
998
1005
|
if (latestMetadata?.workingDirectory !== workingDirectory || sessionRootIdentity(latestRoot, latestInfo) !== rootIdentity || await realpath(target) !== canonicalTarget) {
|
|
999
1006
|
fail("SESSION_ROOT_CHANGED", "The session directory changed");
|
|
1000
1007
|
}
|
|
@@ -1012,13 +1019,15 @@ var SessionDirectoryLister = class {
|
|
|
1012
1019
|
};
|
|
1013
1020
|
|
|
1014
1021
|
// src/session-file.ts
|
|
1022
|
+
import { isUtf8 } from "node:buffer";
|
|
1015
1023
|
import { createHash as createHash3 } from "node:crypto";
|
|
1016
|
-
import { constants
|
|
1017
|
-
import { lstat as lstat2, open, realpath as realpath2, stat as
|
|
1018
|
-
import {
|
|
1024
|
+
import { constants } from "node:fs";
|
|
1025
|
+
import { lstat as lstat2, open, realpath as realpath2, stat as stat2 } from "node:fs/promises";
|
|
1026
|
+
import { extname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
1019
1027
|
import { promisify } from "node:util";
|
|
1020
1028
|
import { gzip } from "node:zlib";
|
|
1021
1029
|
import sharp from "sharp";
|
|
1030
|
+
import { fileTypeFromBuffer } from "file-type";
|
|
1022
1031
|
|
|
1023
1032
|
// src/session-video.ts
|
|
1024
1033
|
function requireVideo(valid) {
|
|
@@ -1181,105 +1190,8 @@ function fail2(code) {
|
|
|
1181
1190
|
function fileIdentity(info, includeChangeTime = true) {
|
|
1182
1191
|
return `${info.dev}\0${info.ino}\0${info.size}\0${info.mtimeNs}\0${includeChangeTime ? info.ctimeNs : ""}\0${info.mode}\0${info.nlink}`;
|
|
1183
1192
|
}
|
|
1184
|
-
var textExtensions = /* @__PURE__ */ new Set([
|
|
1185
|
-
".txt",
|
|
1186
|
-
".log",
|
|
1187
|
-
".csv",
|
|
1188
|
-
".tsv",
|
|
1189
|
-
".md",
|
|
1190
|
-
".mdx",
|
|
1191
|
-
".markdown",
|
|
1192
|
-
".rst",
|
|
1193
|
-
".json",
|
|
1194
|
-
".jsonc",
|
|
1195
|
-
".jsonl",
|
|
1196
|
-
".yaml",
|
|
1197
|
-
".yml",
|
|
1198
|
-
".toml",
|
|
1199
|
-
".ini",
|
|
1200
|
-
".cfg",
|
|
1201
|
-
".conf",
|
|
1202
|
-
".xml",
|
|
1203
|
-
".html",
|
|
1204
|
-
".htm",
|
|
1205
|
-
".css",
|
|
1206
|
-
".scss",
|
|
1207
|
-
".sass",
|
|
1208
|
-
".less",
|
|
1209
|
-
".js",
|
|
1210
|
-
".jsx",
|
|
1211
|
-
".mjs",
|
|
1212
|
-
".cjs",
|
|
1213
|
-
".ts",
|
|
1214
|
-
".tsx",
|
|
1215
|
-
".kt",
|
|
1216
|
-
".kts",
|
|
1217
|
-
".java",
|
|
1218
|
-
".py",
|
|
1219
|
-
".pyi",
|
|
1220
|
-
".go",
|
|
1221
|
-
".rs",
|
|
1222
|
-
".c",
|
|
1223
|
-
".h",
|
|
1224
|
-
".cpp",
|
|
1225
|
-
".hpp",
|
|
1226
|
-
".cc",
|
|
1227
|
-
".cs",
|
|
1228
|
-
".fs",
|
|
1229
|
-
".swift",
|
|
1230
|
-
".m",
|
|
1231
|
-
".mm",
|
|
1232
|
-
".rb",
|
|
1233
|
-
".php",
|
|
1234
|
-
".sh",
|
|
1235
|
-
".bash",
|
|
1236
|
-
".zsh",
|
|
1237
|
-
".fish",
|
|
1238
|
-
".ps1",
|
|
1239
|
-
".psm1",
|
|
1240
|
-
".bat",
|
|
1241
|
-
".cmd",
|
|
1242
|
-
".sql",
|
|
1243
|
-
".r",
|
|
1244
|
-
".dart",
|
|
1245
|
-
".vue",
|
|
1246
|
-
".svelte",
|
|
1247
|
-
".lua",
|
|
1248
|
-
".gradle",
|
|
1249
|
-
".properties",
|
|
1250
|
-
".gitignore",
|
|
1251
|
-
".gitattributes",
|
|
1252
|
-
".editorconfig",
|
|
1253
|
-
".env",
|
|
1254
|
-
".dockerignore",
|
|
1255
|
-
".graphql",
|
|
1256
|
-
".gql",
|
|
1257
|
-
".proto",
|
|
1258
|
-
".cmake",
|
|
1259
|
-
".diff",
|
|
1260
|
-
".patch"
|
|
1261
|
-
]);
|
|
1262
|
-
var textNames = /* @__PURE__ */ new Set([
|
|
1263
|
-
"readme",
|
|
1264
|
-
"license",
|
|
1265
|
-
"licence",
|
|
1266
|
-
"notice",
|
|
1267
|
-
"changelog",
|
|
1268
|
-
"authors",
|
|
1269
|
-
"dockerfile",
|
|
1270
|
-
"containerfile",
|
|
1271
|
-
"makefile",
|
|
1272
|
-
"gemfile",
|
|
1273
|
-
"rakefile",
|
|
1274
|
-
".gitignore",
|
|
1275
|
-
".gitattributes",
|
|
1276
|
-
".editorconfig",
|
|
1277
|
-
".env",
|
|
1278
|
-
".dockerignore"
|
|
1279
|
-
]);
|
|
1280
1193
|
function textMediaType(path) {
|
|
1281
1194
|
const extension = extname(path).toLowerCase();
|
|
1282
|
-
if (!textExtensions.has(extension) && !textNames.has(basename(path).toLowerCase())) fail2("FILE_UNSUPPORTED");
|
|
1283
1195
|
switch (extension) {
|
|
1284
1196
|
case ".html":
|
|
1285
1197
|
case ".htm":
|
|
@@ -1304,6 +1216,9 @@ function textMediaType(path) {
|
|
|
1304
1216
|
return "text/plain";
|
|
1305
1217
|
}
|
|
1306
1218
|
}
|
|
1219
|
+
function isPreviewableText(bytes) {
|
|
1220
|
+
return isUtf8(bytes) && !bytes.some((byte) => byte <= 8 || byte === 11 || byte >= 14 && byte <= 31 || byte === 127);
|
|
1221
|
+
}
|
|
1307
1222
|
function rejectAnimatedPng(bytes) {
|
|
1308
1223
|
for (let offset = 8; offset + 12 <= bytes.length; ) {
|
|
1309
1224
|
const size = bytes.readUInt32BE(offset);
|
|
@@ -1407,13 +1322,19 @@ var SessionFileReader = class {
|
|
|
1407
1322
|
if (!SessionFileReadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
|
|
1408
1323
|
const schema = version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema;
|
|
1409
1324
|
return schema.parse(await this.#run(
|
|
1410
|
-
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline,
|
|
1325
|
+
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, "preview", version)
|
|
1411
1326
|
));
|
|
1412
1327
|
}
|
|
1413
1328
|
async readResource(sessionId, path, rootRevision) {
|
|
1414
1329
|
if (!SessionResourceReadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
|
|
1415
1330
|
return SessionResourceReadResultDataSchema.parse(await this.#run(
|
|
1416
|
-
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline,
|
|
1331
|
+
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, "resource")
|
|
1332
|
+
));
|
|
1333
|
+
}
|
|
1334
|
+
async download(sessionId, path, rootRevision) {
|
|
1335
|
+
if (!SessionFileDownloadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
|
|
1336
|
+
return SessionFileDownloadResultDataSchema.parse(await this.#run(
|
|
1337
|
+
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, "download")
|
|
1417
1338
|
));
|
|
1418
1339
|
}
|
|
1419
1340
|
async #run(read) {
|
|
@@ -1441,18 +1362,20 @@ var SessionFileReader = class {
|
|
|
1441
1362
|
clearTimeout(timer);
|
|
1442
1363
|
}
|
|
1443
1364
|
}
|
|
1444
|
-
async #read(sessionId, path, rootRevision, checkDeadline,
|
|
1365
|
+
async #read(sessionId, path, rootRevision, checkDeadline, mode, version = SESSION_FILE_READ_VERSION) {
|
|
1366
|
+
const resource = mode === "resource";
|
|
1367
|
+
const download = mode === "download";
|
|
1445
1368
|
const metadata = await this.metadata(sessionId);
|
|
1446
1369
|
checkDeadline();
|
|
1447
1370
|
if (metadata === void 0) fail2("SESSION_NOT_FOUND");
|
|
1448
1371
|
const workingDirectory = metadata.workingDirectory;
|
|
1449
|
-
if (!workingDirectory || !
|
|
1372
|
+
if (!workingDirectory || !isAbsolute2(workingDirectory)) fail2("SESSION_ROOT_UNAVAILABLE");
|
|
1450
1373
|
let root;
|
|
1451
1374
|
let rootIdentity;
|
|
1452
1375
|
try {
|
|
1453
1376
|
if ((await lstat2(workingDirectory)).isSymbolicLink()) fail2("FILE_UNAVAILABLE");
|
|
1454
1377
|
root = await realpath2(workingDirectory);
|
|
1455
|
-
const info = await
|
|
1378
|
+
const info = await stat2(root, { bigint: true });
|
|
1456
1379
|
if (!info.isDirectory()) fail2("SESSION_ROOT_UNAVAILABLE");
|
|
1457
1380
|
rootIdentity = sessionRootIdentity(root, info);
|
|
1458
1381
|
} catch (error) {
|
|
@@ -1461,13 +1384,13 @@ var SessionFileReader = class {
|
|
|
1461
1384
|
}
|
|
1462
1385
|
if (createHash3("sha256").update(rootIdentity).digest("base64url") !== rootRevision) fail2("SESSION_ROOT_CHANGED");
|
|
1463
1386
|
checkDeadline();
|
|
1464
|
-
const target =
|
|
1387
|
+
const target = join3(root, ...path.split("/"));
|
|
1465
1388
|
const inspectPath = async () => {
|
|
1466
1389
|
let current = root;
|
|
1467
1390
|
const segments = path.split("/");
|
|
1468
1391
|
let file;
|
|
1469
1392
|
for (const [index, segment] of segments.entries()) {
|
|
1470
|
-
current =
|
|
1393
|
+
current = join3(current, segment);
|
|
1471
1394
|
const info = await lstat2(current, { bigint: true });
|
|
1472
1395
|
checkDeadline();
|
|
1473
1396
|
if (info.isSymbolicLink() || (index === segments.length - 1 ? !info.isFile() : !info.isDirectory())) {
|
|
@@ -1483,13 +1406,11 @@ var SessionFileReader = class {
|
|
|
1483
1406
|
return file;
|
|
1484
1407
|
};
|
|
1485
1408
|
const before = await inspectPath();
|
|
1486
|
-
const maxOriginalBytes = resource ? SESSION_RESOURCE_MAX_ORIGINAL_BYTES : version === SESSION_FILE_READ_V2_VERSION ? SESSION_FILE_V2_MAX_ORIGINAL_BYTES : SESSION_FILE_MAX_ORIGINAL_BYTES;
|
|
1409
|
+
const maxOriginalBytes = download ? SESSION_FILE_DOWNLOAD_MAX_ORIGINAL_BYTES : resource ? SESSION_RESOURCE_MAX_ORIGINAL_BYTES : version === SESSION_FILE_READ_V2_VERSION ? SESSION_FILE_V2_MAX_ORIGINAL_BYTES : SESSION_FILE_MAX_ORIGINAL_BYTES;
|
|
1487
1410
|
if (before.size > BigInt(maxOriginalBytes)) fail2("FILE_TOO_LARGE");
|
|
1488
1411
|
const extension = extname(path).toLowerCase();
|
|
1489
|
-
const
|
|
1490
|
-
const
|
|
1491
|
-
const mediaType = resource ? resourceMediaType(extension) : image ? "image/webp" : video ? extension === ".mp4" ? "video/mp4" : "video/webm" : textMediaType(path);
|
|
1492
|
-
const handle = await open(target, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
|
|
1412
|
+
const resourceType = resource ? resourceMediaType(extension) : void 0;
|
|
1413
|
+
const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
|
|
1493
1414
|
try {
|
|
1494
1415
|
const opened = await handle.stat({ bigint: true });
|
|
1495
1416
|
const includeChangeTime = process.platform !== "win32";
|
|
@@ -1509,27 +1430,43 @@ var SessionFileReader = class {
|
|
|
1509
1430
|
};
|
|
1510
1431
|
const bytes = await readOriginal();
|
|
1511
1432
|
let canonical;
|
|
1512
|
-
|
|
1513
|
-
|
|
1433
|
+
let mediaType;
|
|
1434
|
+
if (download) {
|
|
1435
|
+
mediaType = "application/octet-stream";
|
|
1436
|
+
canonical = bytes;
|
|
1437
|
+
} else if (resourceType !== void 0) {
|
|
1438
|
+
mediaType = resourceType;
|
|
1439
|
+
if (resourceType.startsWith("image/")) {
|
|
1514
1440
|
await validateRawImage(bytes, extension, checkDeadline);
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
} else if (
|
|
1518
|
-
|
|
1519
|
-
validateSessionVideo(bytes, mediaType);
|
|
1520
|
-
} catch (error) {
|
|
1521
|
-
throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
|
|
1522
|
-
}
|
|
1441
|
+
} else if (!isPreviewableText(bytes)) fail2("FILE_UNSUPPORTED");
|
|
1442
|
+
canonical = bytes;
|
|
1443
|
+
} else if (isPreviewableText(bytes)) {
|
|
1444
|
+
mediaType = textMediaType(path);
|
|
1523
1445
|
canonical = bytes;
|
|
1524
1446
|
} else {
|
|
1525
|
-
let
|
|
1447
|
+
let detected;
|
|
1526
1448
|
try {
|
|
1527
|
-
|
|
1449
|
+
detected = await fileTypeFromBuffer(bytes, {
|
|
1450
|
+
maxBufferSize: maxOriginalBytes,
|
|
1451
|
+
signal: AbortSignal.timeout(SESSION_FILE_TIMEOUT_MS)
|
|
1452
|
+
});
|
|
1528
1453
|
} catch (error) {
|
|
1529
1454
|
throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
|
|
1530
1455
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1456
|
+
checkDeadline();
|
|
1457
|
+
if (detected?.mime === "image/png" || detected?.mime === "image/jpeg" || detected?.mime === "image/webp") {
|
|
1458
|
+
mediaType = "image/webp";
|
|
1459
|
+
canonical = await imagePreview(bytes, `.${detected.ext}`, checkDeadline);
|
|
1460
|
+
} else if (detected?.mime === "video/mp4" || detected?.mime === "video/webm") {
|
|
1461
|
+
if (version !== SESSION_FILE_READ_V2_VERSION || extension !== (detected.mime === "video/mp4" ? ".mp4" : ".webm")) fail2("FILE_UNSUPPORTED");
|
|
1462
|
+
mediaType = detected.mime;
|
|
1463
|
+
try {
|
|
1464
|
+
validateSessionVideo(bytes, mediaType);
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
|
|
1467
|
+
}
|
|
1468
|
+
canonical = bytes;
|
|
1469
|
+
} else fail2("FILE_UNSUPPORTED");
|
|
1533
1470
|
}
|
|
1534
1471
|
const threshold = resource ? SESSION_RESOURCE_GZIP_THRESHOLD_BYTES : SESSION_FILE_GZIP_THRESHOLD_BYTES;
|
|
1535
1472
|
const encoding = originalByteLength >= threshold ? "gzip" : "identity";
|
|
@@ -1538,7 +1475,7 @@ var SessionFileReader = class {
|
|
|
1538
1475
|
checkDeadline();
|
|
1539
1476
|
try {
|
|
1540
1477
|
const latestRoot = await realpath2(workingDirectory);
|
|
1541
|
-
const latestRootInfo = await
|
|
1478
|
+
const latestRootInfo = await stat2(latestRoot, { bigint: true });
|
|
1542
1479
|
if (latestMetadata?.workingDirectory !== workingDirectory || (await lstat2(workingDirectory)).isSymbolicLink() || sessionRootIdentity(latestRoot, latestRootInfo) !== rootIdentity) fail2("SESSION_ROOT_CHANGED");
|
|
1543
1480
|
} catch (error) {
|
|
1544
1481
|
if (error instanceof SessionFileReadError) throw error;
|
|
@@ -1553,7 +1490,7 @@ var SessionFileReader = class {
|
|
|
1553
1490
|
throw new SessionFileReadError("FILE_CHANGED", { cause: error });
|
|
1554
1491
|
}
|
|
1555
1492
|
checkDeadline();
|
|
1556
|
-
return (resource ? SessionResourceReadResultDataSchema : version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema).parse({
|
|
1493
|
+
return (download ? SessionFileDownloadResultDataSchema : resource ? SessionResourceReadResultDataSchema : version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema).parse({
|
|
1557
1494
|
path,
|
|
1558
1495
|
rootRevision,
|
|
1559
1496
|
mediaType,
|
|
@@ -1902,6 +1839,9 @@ function createWorkbenchOperations(options) {
|
|
|
1902
1839
|
return routes;
|
|
1903
1840
|
}
|
|
1904
1841
|
|
|
1842
|
+
// src/legacy-client-state.ts
|
|
1843
|
+
var LEGACY_CLIENT_STATE_DIRECTORY = Buffer.from("2e626c7565706177", "hex").toString("utf8");
|
|
1844
|
+
|
|
1905
1845
|
// src/research-tools.ts
|
|
1906
1846
|
var RESEARCH_TOOL_NAMES = ["research_list", "research_read", "research_search"];
|
|
1907
1847
|
var RESEARCH_SEARCH_FILES = 64;
|
|
@@ -1919,11 +1859,11 @@ var ResearchError = class extends Error {
|
|
|
1919
1859
|
code;
|
|
1920
1860
|
};
|
|
1921
1861
|
async function researchRoot(workingDirectory) {
|
|
1922
|
-
if (!
|
|
1862
|
+
if (!isAbsolute3(workingDirectory)) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
|
|
1923
1863
|
try {
|
|
1924
1864
|
if ((await lstat3(workingDirectory)).isSymbolicLink()) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
|
|
1925
1865
|
const root = await realpath3(workingDirectory);
|
|
1926
|
-
const info = await
|
|
1866
|
+
const info = await stat3(root, { bigint: true });
|
|
1927
1867
|
if (!info.isDirectory()) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
|
|
1928
1868
|
return {
|
|
1929
1869
|
workingDirectory: root,
|
|
@@ -1948,7 +1888,7 @@ var privateNames = /* @__PURE__ */ new Set([
|
|
|
1948
1888
|
".docker",
|
|
1949
1889
|
".copilot",
|
|
1950
1890
|
".ak",
|
|
1951
|
-
|
|
1891
|
+
LEGACY_CLIENT_STATE_DIRECTORY,
|
|
1952
1892
|
".copilot-remote",
|
|
1953
1893
|
".agentknight",
|
|
1954
1894
|
"session-state",
|
|
@@ -2041,7 +1981,7 @@ var ResearchTools = class {
|
|
|
2041
1981
|
}
|
|
2042
1982
|
async #validate(path) {
|
|
2043
1983
|
const source = await this.#source();
|
|
2044
|
-
const target =
|
|
1984
|
+
const target = join4(this.root.workingDirectory, ...path.split("/"));
|
|
2045
1985
|
if (sensitiveSegments(this.root.workingDirectory.split(/[\\/]/u)) || !allowedPath(path, source.excludedPaths ?? []) || this.privateDirectories.some((directory) => contained(directory, target))) {
|
|
2046
1986
|
throw new ResearchError("RESEARCH_PATH_BLOCKED", "The requested path is excluded from research");
|
|
2047
1987
|
}
|
|
@@ -2086,7 +2026,7 @@ var ResearchTools = class {
|
|
|
2086
2026
|
const source = await this.#source();
|
|
2087
2027
|
return result.entries.filter((entry) => {
|
|
2088
2028
|
const child = path ? `${path}/${entry.name}` : entry.name;
|
|
2089
|
-
return entry.kind !== "blocked" && allowedPath(child, source.excludedPaths ?? []) && !this.privateDirectories.some((directory) => contained(directory,
|
|
2029
|
+
return entry.kind !== "blocked" && allowedPath(child, source.excludedPaths ?? []) && !this.privateDirectories.some((directory) => contained(directory, join4(this.root.workingDirectory, ...child.split("/"))));
|
|
2090
2030
|
});
|
|
2091
2031
|
}
|
|
2092
2032
|
async list(input) {
|
|
@@ -2186,7 +2126,7 @@ var ResearchTools = class {
|
|
|
2186
2126
|
if (path === void 0) break;
|
|
2187
2127
|
inspected++;
|
|
2188
2128
|
await this.#validate(path);
|
|
2189
|
-
const target =
|
|
2129
|
+
const target = join4(this.root.workingDirectory, ...path.split("/"));
|
|
2190
2130
|
const info = await lstat3(target);
|
|
2191
2131
|
if (info.isSymbolicLink()) {
|
|
2192
2132
|
skippedFiles++;
|
|
@@ -2307,9 +2247,9 @@ function researchAwareCodingAgentAdapter(inner, research) {
|
|
|
2307
2247
|
if (!research.store.has(id)) return false;
|
|
2308
2248
|
if (inner.isSessionVisible?.(parent) !== false) return true;
|
|
2309
2249
|
try {
|
|
2310
|
-
const
|
|
2311
|
-
|
|
2312
|
-
return
|
|
2250
|
+
const access = inner.captureResearchAccess?.(parent);
|
|
2251
|
+
access?.assertCurrent();
|
|
2252
|
+
return access?.detached === true;
|
|
2313
2253
|
} catch {
|
|
2314
2254
|
return false;
|
|
2315
2255
|
}
|
|
@@ -2461,7 +2401,7 @@ function createRelaySocket(uri, options) {
|
|
|
2461
2401
|
// src/service/requests/session-communication.ts
|
|
2462
2402
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2463
2403
|
import { createHash as createHash8 } from "node:crypto";
|
|
2464
|
-
import { join as
|
|
2404
|
+
import { join as join6 } from "node:path";
|
|
2465
2405
|
import { performance as performance2 } from "node:perf_hooks";
|
|
2466
2406
|
|
|
2467
2407
|
// src/repeated-error-diagnostics.ts
|
|
@@ -2729,14 +2669,14 @@ var SessionPublicationScope = class {
|
|
|
2729
2669
|
|
|
2730
2670
|
// src/service/requests/communication-operations.ts
|
|
2731
2671
|
import { promisify as promisify2 } from "node:util";
|
|
2732
|
-
import { constants as
|
|
2672
|
+
import { constants as constants3, gzip as gzip3 } from "node:zlib";
|
|
2733
2673
|
|
|
2734
2674
|
// src/session-git-diff.ts
|
|
2735
2675
|
import { execFile } from "node:child_process";
|
|
2736
2676
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
2737
|
-
import { constants as
|
|
2738
|
-
import { chmod, lstat as lstat4, mkdir as mkdir2, open as open2, readlink, realpath as realpath4, rm, stat as
|
|
2739
|
-
import { dirname, isAbsolute as
|
|
2677
|
+
import { constants as constants2 } from "node:fs";
|
|
2678
|
+
import { chmod, lstat as lstat4, mkdir as mkdir2, open as open2, readlink, realpath as realpath4, rm, stat as stat4, writeFile as writeFile2 } from "node:fs/promises";
|
|
2679
|
+
import { dirname, isAbsolute as isAbsolute4, join as join5, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
2740
2680
|
var SESSION_GIT_DIFF_CONCURRENCY = 4;
|
|
2741
2681
|
var SESSION_GIT_DIFF_TIMEOUT_MS = 15e3;
|
|
2742
2682
|
var SESSION_GIT_DIFF_MAX_SOURCE_BYTES = 8 * 1024 * 1024;
|
|
@@ -2752,6 +2692,28 @@ var SessionGitDiffError = class extends Error {
|
|
|
2752
2692
|
function fail3(code, message) {
|
|
2753
2693
|
throw new SessionGitDiffError(code, message);
|
|
2754
2694
|
}
|
|
2695
|
+
function fileError(error) {
|
|
2696
|
+
if (error instanceof SessionGitDiffError && error.code === "DIFF_BLOCKED") {
|
|
2697
|
+
return { code: "DIFF_BLOCKED", message: error.message };
|
|
2698
|
+
}
|
|
2699
|
+
if (error instanceof Error && "code" in error && typeof error.code === "string" && ["EACCES", "EPERM", "EBUSY", "ENAMETOOLONG", "EINVAL", "EIO", "ELOOP"].includes(error.code)) {
|
|
2700
|
+
return { code: "DIFF_FILE_UNAVAILABLE", message: `Cannot read this path (${error.code})` };
|
|
2701
|
+
}
|
|
2702
|
+
return void 0;
|
|
2703
|
+
}
|
|
2704
|
+
async function forFile(path, work, operation) {
|
|
2705
|
+
try {
|
|
2706
|
+
return await operation();
|
|
2707
|
+
} catch (error) {
|
|
2708
|
+
const failure = work.partial ? fileError(error) : void 0;
|
|
2709
|
+
if (failure === void 0) throw error;
|
|
2710
|
+
throw new SessionGitDiffError(
|
|
2711
|
+
failure.code === "DIFF_BLOCKED" ? "DIFF_FILE_BLOCKED" : failure.code,
|
|
2712
|
+
`${path}: ${failure.message}`,
|
|
2713
|
+
{ cause: error }
|
|
2714
|
+
);
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2755
2717
|
function changed() {
|
|
2756
2718
|
return fail3("DIFF_CHANGED", "The project diff changed; refresh the changed-file list");
|
|
2757
2719
|
}
|
|
@@ -2787,12 +2749,30 @@ function environment() {
|
|
|
2787
2749
|
function isExit(error, code) {
|
|
2788
2750
|
return error instanceof Error && "code" in error && error.code === code && "stdout" in error && Buffer.isBuffer(error.stdout) && "stderr" in error && Buffer.isBuffer(error.stderr);
|
|
2789
2751
|
}
|
|
2752
|
+
function hashFileError(error, path) {
|
|
2753
|
+
if (!isExit(error, 128)) return void 0;
|
|
2754
|
+
const diagnostic = decode(error.stderr).trim().split(/\r?\n/u).at(-1) ?? "";
|
|
2755
|
+
if (!diagnostic.startsWith(`fatal: could not open '${path}' for reading: `)) return void 0;
|
|
2756
|
+
return { code: "DIFF_FILE_UNAVAILABLE", message: "Cannot normalize this path for comparison" };
|
|
2757
|
+
}
|
|
2790
2758
|
var Work = class {
|
|
2759
|
+
constructor(partial) {
|
|
2760
|
+
this.partial = partial;
|
|
2761
|
+
}
|
|
2762
|
+
partial;
|
|
2791
2763
|
deadline = Date.now() + SESSION_GIT_DIFF_TIMEOUT_MS;
|
|
2792
2764
|
env = environment();
|
|
2793
2765
|
bytes = 0;
|
|
2766
|
+
linkFiles = /* @__PURE__ */ new Set();
|
|
2767
|
+
symlinks = true;
|
|
2794
2768
|
#filterConfig = [];
|
|
2795
2769
|
async disableExternalFilters(repository) {
|
|
2770
|
+
this.symlinks = decode(await this.git(repository, [
|
|
2771
|
+
"config",
|
|
2772
|
+
"--type=bool",
|
|
2773
|
+
"--get",
|
|
2774
|
+
"core.symlinks"
|
|
2775
|
+
], { allowExitOne: true })).trim() !== "false";
|
|
2796
2776
|
const names = records(await this.git(repository, [
|
|
2797
2777
|
"config",
|
|
2798
2778
|
"--includes",
|
|
@@ -2842,10 +2822,10 @@ var Work = class {
|
|
|
2842
2822
|
...this.env,
|
|
2843
2823
|
...options.objectDirectory === void 0 ? {} : { GIT_OBJECT_DIRECTORY: options.objectDirectory },
|
|
2844
2824
|
...options.isolated ? {
|
|
2845
|
-
GIT_DIR:
|
|
2825
|
+
GIT_DIR: join5(cwd, "no-repository"),
|
|
2846
2826
|
GIT_CONFIG_NOSYSTEM: "1",
|
|
2847
2827
|
GIT_ATTR_NOSYSTEM: "1",
|
|
2848
|
-
GIT_CONFIG_GLOBAL:
|
|
2828
|
+
GIT_CONFIG_GLOBAL: join5(cwd, "gitconfig")
|
|
2849
2829
|
} : {}
|
|
2850
2830
|
}
|
|
2851
2831
|
}, (error, output2, stderr) => {
|
|
@@ -2873,7 +2853,7 @@ async function inspect(root, path, work, kind = "file") {
|
|
|
2873
2853
|
let current = root;
|
|
2874
2854
|
const segments = path.split("/");
|
|
2875
2855
|
for (const [index, segment] of segments.entries()) {
|
|
2876
|
-
current =
|
|
2856
|
+
current = join5(current, segment);
|
|
2877
2857
|
let info;
|
|
2878
2858
|
try {
|
|
2879
2859
|
info = await lstat4(current, { bigint: true });
|
|
@@ -2898,13 +2878,27 @@ async function inspect(root, path, work, kind = "file") {
|
|
|
2898
2878
|
return void 0;
|
|
2899
2879
|
}
|
|
2900
2880
|
async function readCurrent(root, path, algorithm, work, keepBytes = false) {
|
|
2901
|
-
const
|
|
2881
|
+
const kind = work.partial ? "link" : "file";
|
|
2882
|
+
const before = await inspect(root, path, work, kind);
|
|
2902
2883
|
if (before === void 0) return void 0;
|
|
2884
|
+
if (before.isSymbolicLink()) {
|
|
2885
|
+
const bytes = Buffer.from(await readlink(join5(root, ...path.split("/"))), "utf8");
|
|
2886
|
+
const latest = await inspect(root, path, work, "link");
|
|
2887
|
+
if (latest === void 0 || identity(latest) !== identity(before)) changed();
|
|
2888
|
+
const oid = createHash5(algorithm).update(`blob ${bytes.length}\0`).update(bytes).digest("hex");
|
|
2889
|
+
return {
|
|
2890
|
+
oid,
|
|
2891
|
+
rawOid: oid,
|
|
2892
|
+
identity: identity(before),
|
|
2893
|
+
mode: "120000",
|
|
2894
|
+
...keepBytes ? { bytes } : {}
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2903
2897
|
if (before.size > BigInt(MAX_SCAN_BYTES - work.bytes) || keepBytes && before.size > BigInt(SESSION_GIT_DIFF_MAX_SOURCE_BYTES)) limit();
|
|
2904
2898
|
work.bytes += Number(before.size);
|
|
2905
2899
|
const handle = await open2(
|
|
2906
|
-
|
|
2907
|
-
|
|
2900
|
+
join5(root, ...path.split("/")),
|
|
2901
|
+
constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0)
|
|
2908
2902
|
);
|
|
2909
2903
|
try {
|
|
2910
2904
|
const opened = await handle.stat({ bigint: true });
|
|
@@ -2929,7 +2923,7 @@ async function readCurrent(root, path, algorithm, work, keepBytes = false) {
|
|
|
2929
2923
|
oid: rawOid,
|
|
2930
2924
|
rawOid,
|
|
2931
2925
|
identity: identity(before),
|
|
2932
|
-
mode: process.platform !== "win32" && (before.mode & 0o111n) !== 0n ? "100755" : "100644",
|
|
2926
|
+
mode: work.linkFiles.has(path) ? "120000" : process.platform !== "win32" && (before.mode & 0o111n) !== 0n ? "100755" : "100644",
|
|
2933
2927
|
...keepBytes ? { bytes: Buffer.concat(chunks) } : {}
|
|
2934
2928
|
};
|
|
2935
2929
|
} finally {
|
|
@@ -2965,7 +2959,6 @@ function scopedPath(context, path) {
|
|
|
2965
2959
|
const prefix = context.prefix === "" ? "" : `${context.prefix}/`;
|
|
2966
2960
|
if (!path.startsWith(prefix)) fail3("DIFF_BLOCKED", "Git returned a path outside the session");
|
|
2967
2961
|
const scoped = path.slice(prefix.length);
|
|
2968
|
-
if (!safePath(scoped)) fail3("DIFF_BLOCKED", "The project contains an unsupported path");
|
|
2969
2962
|
return scoped;
|
|
2970
2963
|
}
|
|
2971
2964
|
function records(output2) {
|
|
@@ -2996,7 +2989,7 @@ async function changes(context, work) {
|
|
|
2996
2989
|
return scoped2;
|
|
2997
2990
|
};
|
|
2998
2991
|
for (const record of tree) {
|
|
2999
|
-
const match = /^(\d{6}) (blob|commit) ([a-f0-9]{40}|[a-f0-9]{64})\t(
|
|
2992
|
+
const match = /^(\d{6}) (blob|commit) ([a-f0-9]{40}|[a-f0-9]{64})\t([\s\S]+)$/u.exec(record);
|
|
3000
2993
|
if (!match?.[1] || !match[3] || !match[4]) fail3("DIFF_UNAVAILABLE", "Git returned an invalid tree");
|
|
3001
2994
|
blobs.set(scoped(match[4]), { mode: match[1], oid: match[3] });
|
|
3002
2995
|
}
|
|
@@ -3008,7 +3001,7 @@ async function changes(context, work) {
|
|
|
3008
3001
|
"--",
|
|
3009
3002
|
...paths
|
|
3010
3003
|
]))) {
|
|
3011
|
-
const match = /^(\d{6}) ([a-f0-9]{40}|[a-f0-9]{64}) [0-3]\t(
|
|
3004
|
+
const match = /^(\d{6}) ([a-f0-9]{40}|[a-f0-9]{64}) [0-3]\t([\s\S]+)$/u.exec(record);
|
|
3012
3005
|
if (!match?.[1] || !match[2] || !match[3]) fail3("DIFF_UNAVAILABLE", "Git returned an invalid index");
|
|
3013
3006
|
tracked.set(scoped(match[3]), { mode: match[1], oid: match[2] });
|
|
3014
3007
|
}
|
|
@@ -3047,38 +3040,88 @@ async function changes(context, work) {
|
|
|
3047
3040
|
files: untracked
|
|
3048
3041
|
}), "utf8") > SESSION_GIT_DIFF_MAX_RESPONSE_BYTES) limit();
|
|
3049
3042
|
const result = [];
|
|
3043
|
+
const errors = /* @__PURE__ */ new Map();
|
|
3050
3044
|
const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
|
|
3051
3045
|
const currentFiles = /* @__PURE__ */ new Map();
|
|
3046
|
+
if (work.partial && !work.symlinks) {
|
|
3047
|
+
for (const [path, blob] of tracked) if (blob.mode === "120000") work.linkFiles.add(path);
|
|
3048
|
+
}
|
|
3052
3049
|
for (const path of [...candidates].sort()) {
|
|
3053
3050
|
work.check();
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3051
|
+
try {
|
|
3052
|
+
if (!safePath(path)) fail3("DIFF_BLOCKED", "This path cannot be safely accessed on the PC");
|
|
3053
|
+
const original = blobs.get(path);
|
|
3054
|
+
const indexed = tracked.get(path);
|
|
3055
|
+
if ([original?.mode, indexed?.mode].some((mode) => mode !== void 0 && mode !== "100644" && mode !== "100755" && !(work.partial && mode === "120000"))) {
|
|
3056
|
+
await unchangedSpecial(context, path, original, indexed, work);
|
|
3057
|
+
candidates.delete(path);
|
|
3058
|
+
continue;
|
|
3059
|
+
}
|
|
3060
|
+
const current = await readCurrent(context.root, path, algorithm, work);
|
|
3061
|
+
if (current !== void 0) currentFiles.set(path, current);
|
|
3062
|
+
} catch (error) {
|
|
3063
|
+
const failure = fileError(error);
|
|
3064
|
+
if (failure === void 0) throw error;
|
|
3065
|
+
errors.set(path, failure);
|
|
3060
3066
|
}
|
|
3061
|
-
const current = await readCurrent(context.root, path, algorithm, work);
|
|
3062
|
-
if (current !== void 0) currentFiles.set(path, current);
|
|
3063
3067
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3068
|
+
const regularFiles = [...currentFiles].filter(([, current]) => current.mode !== "120000");
|
|
3069
|
+
if (regularFiles.length > 0) {
|
|
3070
|
+
let hashes;
|
|
3071
|
+
try {
|
|
3072
|
+
hashes = decode(await work.git(context.repository, ["hash-object", "--stdin-paths"], {
|
|
3073
|
+
input: regularFiles.map(([path]) => JSON.stringify([context.prefix, path].filter(Boolean).join("/"))).join("\n") + "\n"
|
|
3074
|
+
})).trim().split("\n");
|
|
3075
|
+
} catch (error) {
|
|
3076
|
+
if (!work.partial || !isExit(error, 128) || regularFiles.length > SESSION_GIT_DIFF_MAX_FILES) throw error;
|
|
3077
|
+
hashes = [];
|
|
3078
|
+
for (const [path] of regularFiles) {
|
|
3079
|
+
const repositoryPath = [context.prefix, path].filter(Boolean).join("/");
|
|
3080
|
+
try {
|
|
3081
|
+
hashes.push(decode(await work.git(context.repository, ["hash-object", "--stdin-paths"], {
|
|
3082
|
+
input: JSON.stringify(repositoryPath) + "\n"
|
|
3083
|
+
})).trim());
|
|
3084
|
+
} catch (error2) {
|
|
3085
|
+
const failure = hashFileError(error2, repositoryPath);
|
|
3086
|
+
if (failure === void 0) throw error2;
|
|
3087
|
+
errors.set(path, failure);
|
|
3088
|
+
currentFiles.delete(path);
|
|
3089
|
+
hashes.push("");
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
if (hashes.length !== regularFiles.length) changed();
|
|
3094
|
+
for (const [index, [path, current]] of regularFiles.entries()) {
|
|
3095
|
+
if (errors.has(path)) continue;
|
|
3096
|
+
try {
|
|
3097
|
+
const oid = hashes[index];
|
|
3098
|
+
if (!oid || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(oid)) changed();
|
|
3099
|
+
const latest = await inspect(context.root, path, work);
|
|
3100
|
+
if (latest === void 0 || identity(latest) !== current.identity) changed();
|
|
3101
|
+
current.oid = oid;
|
|
3102
|
+
} catch (error) {
|
|
3103
|
+
const failure = work.partial ? fileError(error) : void 0;
|
|
3104
|
+
if (failure === void 0) throw error;
|
|
3105
|
+
errors.set(path, failure);
|
|
3106
|
+
currentFiles.delete(path);
|
|
3107
|
+
}
|
|
3075
3108
|
}
|
|
3076
3109
|
}
|
|
3077
3110
|
for (const path of [...candidates].sort()) {
|
|
3078
3111
|
const original = blobs.get(path);
|
|
3079
3112
|
const current = currentFiles.get(path);
|
|
3113
|
+
const error = errors.get(path);
|
|
3114
|
+
if (error !== void 0) {
|
|
3115
|
+
result.push({
|
|
3116
|
+
entry: { path, status: original === void 0 ? tracked.has(path) ? "added" : "untracked" : tracked.has(path) ? "modified" : "deleted", error },
|
|
3117
|
+
base: original,
|
|
3118
|
+
current: void 0
|
|
3119
|
+
});
|
|
3120
|
+
if (result.length > SESSION_GIT_DIFF_MAX_FILES) limit();
|
|
3121
|
+
continue;
|
|
3122
|
+
}
|
|
3080
3123
|
if (original === void 0 && current === void 0) continue;
|
|
3081
|
-
if (original?.oid === current?.oid && (process.platform === "win32" || original?.mode === current?.mode)) continue;
|
|
3124
|
+
if (original?.oid === current?.oid && original?.mode === "120000" === (current?.mode === "120000") && (process.platform === "win32" || original?.mode === current?.mode)) continue;
|
|
3082
3125
|
result.push({
|
|
3083
3126
|
entry: { path, status: original === void 0 ? tracked.has(path) ? "added" : "untracked" : current === void 0 ? "deleted" : "modified" },
|
|
3084
3127
|
base: original,
|
|
@@ -3093,9 +3136,9 @@ async function unchangedSpecial(context, path, original, indexed, work) {
|
|
|
3093
3136
|
if (original === void 0 || indexed?.mode !== original.mode || indexed.oid !== original.oid) return blocked();
|
|
3094
3137
|
if (original.mode === "160000") {
|
|
3095
3138
|
if (await inspect(context.root, path, work, "directory") === void 0) blocked();
|
|
3096
|
-
const target =
|
|
3139
|
+
const target = join5(context.root, ...path.split("/"));
|
|
3097
3140
|
try {
|
|
3098
|
-
await lstat4(
|
|
3141
|
+
await lstat4(join5(target, ".git"));
|
|
3099
3142
|
} catch (error) {
|
|
3100
3143
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
|
|
3101
3144
|
throw error;
|
|
@@ -3107,7 +3150,7 @@ async function unchangedSpecial(context, path, original, indexed, work) {
|
|
|
3107
3150
|
const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
|
|
3108
3151
|
let oid;
|
|
3109
3152
|
if (info.isSymbolicLink()) {
|
|
3110
|
-
const target = Buffer.from(await readlink(
|
|
3153
|
+
const target = Buffer.from(await readlink(join5(context.root, ...path.split("/"))), "utf8");
|
|
3111
3154
|
oid = createHash5(algorithm).update(`blob ${target.length}\0`).update(target).digest("hex");
|
|
3112
3155
|
} else {
|
|
3113
3156
|
oid = (await readCurrent(context.root, path, algorithm, work))?.rawOid;
|
|
@@ -3130,7 +3173,20 @@ async function patch(context, change, work, scratchDirectory) {
|
|
|
3130
3173
|
if (current?.rawOid !== change.current?.rawOid || current?.mode !== change.current?.mode) changed();
|
|
3131
3174
|
let original = Buffer.alloc(0);
|
|
3132
3175
|
if (change.base !== void 0) {
|
|
3133
|
-
|
|
3176
|
+
let sizeBytes;
|
|
3177
|
+
try {
|
|
3178
|
+
sizeBytes = await work.git(context.repository, ["cat-file", "-s", change.base.oid]);
|
|
3179
|
+
} catch (error) {
|
|
3180
|
+
if (work.partial && isExit(error, 128) && decode(error.stderr).trim() === "fatal: git cat-file: could not get object info") {
|
|
3181
|
+
throw new SessionGitDiffError(
|
|
3182
|
+
"DIFF_FILE_UNAVAILABLE",
|
|
3183
|
+
`${change.entry.path}: The base version of this file is unavailable`,
|
|
3184
|
+
{ cause: error }
|
|
3185
|
+
);
|
|
3186
|
+
}
|
|
3187
|
+
throw error;
|
|
3188
|
+
}
|
|
3189
|
+
const size = Number(decode(sizeBytes).trim());
|
|
3134
3190
|
if (!Number.isSafeInteger(size) || size < 0 || size > SESSION_GIT_DIFF_MAX_SOURCE_BYTES) limit();
|
|
3135
3191
|
original = await work.git(context.repository, ["cat-file", "blob", change.base.oid], { maxBuffer: size + 1 });
|
|
3136
3192
|
if (original.length !== size) changed();
|
|
@@ -3143,13 +3199,13 @@ async function patch(context, change, work, scratchDirectory) {
|
|
|
3143
3199
|
if ((await lstat4(directory)).isSymbolicLink() || !samePath(await realpath4(directory), directory)) {
|
|
3144
3200
|
fail3("DIFF_UNAVAILABLE", "Project diff storage cannot use links");
|
|
3145
3201
|
}
|
|
3146
|
-
const scratch =
|
|
3202
|
+
const scratch = join5(directory, randomUUID3());
|
|
3147
3203
|
await mkdir2(scratch, { mode: 448 });
|
|
3148
3204
|
let text;
|
|
3149
3205
|
try {
|
|
3150
3206
|
let bytes = current?.bytes ?? Buffer.alloc(0);
|
|
3151
|
-
if (current !== void 0) {
|
|
3152
|
-
const objectDirectory =
|
|
3207
|
+
if (current !== void 0 && current.mode !== "120000") {
|
|
3208
|
+
const objectDirectory = join5(scratch, "objects");
|
|
3153
3209
|
await mkdir2(objectDirectory);
|
|
3154
3210
|
const path = [context.prefix, change.entry.path].filter(Boolean).join("/");
|
|
3155
3211
|
const oid = decode(await work.git(context.repository, ["hash-object", "-w", "--stdin", `--path=${path}`], {
|
|
@@ -3163,18 +3219,18 @@ async function patch(context, change, work, scratchDirectory) {
|
|
|
3163
3219
|
});
|
|
3164
3220
|
}
|
|
3165
3221
|
if (binary(original) || binary(bytes)) return { patch: "", binary: true };
|
|
3166
|
-
for (const side of ["a", "b"]) await mkdir2(
|
|
3222
|
+
for (const side of ["a", "b"]) await mkdir2(join5(scratch, side));
|
|
3167
3223
|
for (const [side, content, mode] of [
|
|
3168
3224
|
["a", original, change.base?.mode],
|
|
3169
3225
|
["b", bytes, current?.mode]
|
|
3170
3226
|
]) {
|
|
3171
3227
|
if (mode === void 0) continue;
|
|
3172
|
-
const file =
|
|
3228
|
+
const file = join5(scratch, side, ...change.entry.path.split("/"));
|
|
3173
3229
|
await mkdir2(dirname(file), { recursive: true });
|
|
3174
3230
|
await writeFile2(file, content, { flag: "wx", mode: 384 });
|
|
3175
3231
|
if (process.platform !== "win32") await chmod(file, mode === "100755" ? 448 : 384);
|
|
3176
3232
|
}
|
|
3177
|
-
await writeFile2(
|
|
3233
|
+
await writeFile2(join5(scratch, "gitconfig"), "", { flag: "wx", mode: 384 });
|
|
3178
3234
|
text = decode(await work.git(scratch, [
|
|
3179
3235
|
"-c",
|
|
3180
3236
|
"core.quotePath=false",
|
|
@@ -3196,6 +3252,18 @@ async function patch(context, change, work, scratchDirectory) {
|
|
|
3196
3252
|
} finally {
|
|
3197
3253
|
await rm(scratch, { recursive: true, force: true, maxRetries: 3 });
|
|
3198
3254
|
}
|
|
3255
|
+
if (current?.mode === "120000" || change.base?.mode === "120000") {
|
|
3256
|
+
text = text.replace(/^new file mode \d+$/mu, `new file mode ${current?.mode}`).replace(/^deleted file mode \d+$/mu, `deleted file mode ${change.base?.mode}`);
|
|
3257
|
+
if (current !== void 0 && change.base !== void 0 && current.mode !== change.base.mode) {
|
|
3258
|
+
text = text.replace(/^old mode \d+\nnew mode \d+\n/mu, "");
|
|
3259
|
+
if (text === "") text = `diff --git a/${change.entry.path} b/${change.entry.path}
|
|
3260
|
+
`;
|
|
3261
|
+
const end = text.indexOf("\n") + 1;
|
|
3262
|
+
text = `${text.slice(0, end)}old mode ${change.base.mode}
|
|
3263
|
+
new mode ${current.mode}
|
|
3264
|
+
${text.slice(end)}`;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3199
3267
|
if (Buffer.byteLength(text, "utf8") > SESSION_GIT_DIFF_MAX_PATCH_BYTES) limit();
|
|
3200
3268
|
return { patch: text, binary: false };
|
|
3201
3269
|
}
|
|
@@ -3207,14 +3275,14 @@ var SessionGitDiffReader = class {
|
|
|
3207
3275
|
metadata;
|
|
3208
3276
|
scratchDirectory;
|
|
3209
3277
|
#active = 0;
|
|
3210
|
-
async read(sessionId, data) {
|
|
3278
|
+
async read(sessionId, data, version = SESSION_GIT_DIFF_PARTIAL_VERSION) {
|
|
3211
3279
|
if (!SessionGitDiffDataSchema.safeParse(data).success || data.path !== void 0 && !safePath(data.path)) {
|
|
3212
3280
|
fail3("INVALID_DIFF_PATH", "The project diff request is invalid");
|
|
3213
3281
|
}
|
|
3214
3282
|
if (this.#active >= SESSION_GIT_DIFF_CONCURRENCY) fail3("DIFF_BUSY", "Too many project diff requests");
|
|
3215
3283
|
this.#active++;
|
|
3216
|
-
const work = new Work();
|
|
3217
|
-
const pending = this.#read(sessionId, data, work).finally(() => {
|
|
3284
|
+
const work = new Work(version >= SESSION_GIT_DIFF_PARTIAL_VERSION);
|
|
3285
|
+
const pending = this.#read(sessionId, data, work, version).finally(() => {
|
|
3218
3286
|
this.#active--;
|
|
3219
3287
|
});
|
|
3220
3288
|
let timer;
|
|
@@ -3240,7 +3308,7 @@ var SessionGitDiffReader = class {
|
|
|
3240
3308
|
work.check();
|
|
3241
3309
|
if (metadata === void 0) fail3("SESSION_NOT_FOUND", "The session is unavailable");
|
|
3242
3310
|
const workingDirectory = metadata.workingDirectory;
|
|
3243
|
-
if (!workingDirectory || !
|
|
3311
|
+
if (!workingDirectory || !isAbsolute4(workingDirectory)) {
|
|
3244
3312
|
fail3("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
|
|
3245
3313
|
}
|
|
3246
3314
|
let root;
|
|
@@ -3250,7 +3318,7 @@ var SessionGitDiffReader = class {
|
|
|
3250
3318
|
if ((await lstat4(workingDirectory)).isSymbolicLink() || !samePath(root, resolve2(workingDirectory))) {
|
|
3251
3319
|
fail3("DIFF_BLOCKED", "Links cannot be compared");
|
|
3252
3320
|
}
|
|
3253
|
-
const info = await
|
|
3321
|
+
const info = await stat4(root, { bigint: true });
|
|
3254
3322
|
if (!info.isDirectory()) fail3("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
|
|
3255
3323
|
rootIdentity = sessionRootIdentity(root, info);
|
|
3256
3324
|
} catch (error) {
|
|
@@ -3263,7 +3331,7 @@ var SessionGitDiffReader = class {
|
|
|
3263
3331
|
let repository;
|
|
3264
3332
|
try {
|
|
3265
3333
|
const top = decode(await work.git(root, ["rev-parse", "--show-toplevel"])).replace(/\r?\n$/u, "");
|
|
3266
|
-
if (!
|
|
3334
|
+
if (!isAbsolute4(top)) fail3("DIFF_UNAVAILABLE", "Git returned an invalid repository");
|
|
3267
3335
|
repository = await realpath4(top);
|
|
3268
3336
|
} catch (error) {
|
|
3269
3337
|
if (error instanceof SessionGitDiffError) throw error;
|
|
@@ -3282,12 +3350,12 @@ var SessionGitDiffReader = class {
|
|
|
3282
3350
|
rootIdentity,
|
|
3283
3351
|
workingDirectory,
|
|
3284
3352
|
repository,
|
|
3285
|
-
repositoryIdentity: sessionRootIdentity(repository, await
|
|
3353
|
+
repositoryIdentity: sessionRootIdentity(repository, await stat4(repository, { bigint: true })),
|
|
3286
3354
|
prefix: relative2(repository, root).split(sep2).join("/"),
|
|
3287
3355
|
...await base(repository, work)
|
|
3288
3356
|
};
|
|
3289
3357
|
}
|
|
3290
|
-
async #read(sessionId, data, work) {
|
|
3358
|
+
async #read(sessionId, data, work, version) {
|
|
3291
3359
|
const context = await this.#context(sessionId, data.rootRevision, work);
|
|
3292
3360
|
if (data.baseCommit !== void 0 && data.baseCommit !== context.baseCommit) changed();
|
|
3293
3361
|
await work.disableExternalFilters(context.repository);
|
|
@@ -3300,7 +3368,23 @@ var SessionGitDiffReader = class {
|
|
|
3300
3368
|
} else {
|
|
3301
3369
|
selected = files.find(({ entry }) => entry.path === data.path);
|
|
3302
3370
|
if (selected === void 0) changed();
|
|
3303
|
-
|
|
3371
|
+
if (selected.entry.error !== void 0) {
|
|
3372
|
+
const error = selected.entry.error;
|
|
3373
|
+
fail3(
|
|
3374
|
+
work.partial && error.code === "DIFF_BLOCKED" ? "DIFF_FILE_BLOCKED" : error.code,
|
|
3375
|
+
`${data.path}: ${error.message}`
|
|
3376
|
+
);
|
|
3377
|
+
}
|
|
3378
|
+
const change = selected;
|
|
3379
|
+
result = {
|
|
3380
|
+
...common,
|
|
3381
|
+
path: data.path,
|
|
3382
|
+
...await forFile(data.path, work, () => patch(context, change, work, this.scratchDirectory))
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
if (version < SESSION_GIT_DIFF_PARTIAL_VERSION) {
|
|
3386
|
+
const blocked = files.find(({ entry }) => entry.error !== void 0)?.entry;
|
|
3387
|
+
if (blocked?.error !== void 0) fail3(blocked.error.code, `${blocked.path}: ${blocked.error.message}`);
|
|
3304
3388
|
}
|
|
3305
3389
|
let latest;
|
|
3306
3390
|
try {
|
|
@@ -3319,17 +3403,18 @@ var SessionGitDiffReader = class {
|
|
|
3319
3403
|
}
|
|
3320
3404
|
if (latest.repositoryIdentity !== context.repositoryIdentity || latest.baseRef !== context.baseRef || latest.baseCommit !== context.baseCommit) changed();
|
|
3321
3405
|
if (selected !== void 0) {
|
|
3322
|
-
const
|
|
3406
|
+
const path = selected.entry.path;
|
|
3407
|
+
const current = await forFile(path, work, () => readCurrent(
|
|
3323
3408
|
context.root,
|
|
3324
|
-
|
|
3409
|
+
path,
|
|
3325
3410
|
context.baseCommit.length === 64 ? "sha256" : "sha1",
|
|
3326
3411
|
work
|
|
3327
|
-
);
|
|
3412
|
+
));
|
|
3328
3413
|
if (current?.rawOid !== selected.current?.rawOid || current?.mode !== selected.current?.mode) changed();
|
|
3329
3414
|
}
|
|
3330
3415
|
work.check();
|
|
3331
3416
|
if (Buffer.byteLength(JSON.stringify(result), "utf8") > SESSION_GIT_DIFF_MAX_RESPONSE_BYTES) limit();
|
|
3332
|
-
return SessionGitDiffResultDataSchema.parse(result);
|
|
3417
|
+
return (version >= SESSION_GIT_DIFF_PARTIAL_VERSION ? SessionGitDiffPartialResultDataSchema : SessionGitDiffResultDataSchema).parse(result);
|
|
3333
3418
|
}
|
|
3334
3419
|
};
|
|
3335
3420
|
|
|
@@ -3845,22 +3930,22 @@ function createCommunicationOperations(options) {
|
|
|
3845
3930
|
],
|
|
3846
3931
|
...options.incrementalOperations,
|
|
3847
3932
|
...adapter.getSessionDirectoryMetadata === void 0 ? [] : [
|
|
3848
|
-
...[SESSION_GIT_DIFF_VERSION, SESSION_GIT_DIFF_GZIP_VERSION].map((version) => sessionCommunicationOperation({
|
|
3933
|
+
...[SESSION_GIT_DIFF_VERSION, SESSION_GIT_DIFF_GZIP_VERSION, SESSION_GIT_DIFF_PARTIAL_VERSION].map((version) => sessionCommunicationOperation({
|
|
3849
3934
|
operation: SESSION_GIT_DIFF_OPERATION,
|
|
3850
3935
|
version,
|
|
3851
3936
|
schema: SessionGitDiffDataSchema,
|
|
3852
3937
|
handle: async (data, request) => {
|
|
3853
3938
|
const assertAccess = options.captureRequestAccess();
|
|
3854
3939
|
assertAccess();
|
|
3855
|
-
const result = await gitDiffReader.read(request.sessionId, data);
|
|
3940
|
+
const result = await gitDiffReader.read(request.sessionId, data, version);
|
|
3856
3941
|
let response = SessionCommunicationDataSchema.parse(result);
|
|
3857
|
-
if (version
|
|
3942
|
+
if (version >= SESSION_GIT_DIFF_GZIP_VERSION) {
|
|
3858
3943
|
const bytes = Buffer.from(JSON.stringify(result), "utf8");
|
|
3859
3944
|
if (bytes.length >= SESSION_GIT_DIFF_GZIP_MIN_BYTES) {
|
|
3860
3945
|
response = SessionGitDiffGzipResultDataSchema.parse({
|
|
3861
3946
|
encoding: "gzip",
|
|
3862
3947
|
byteLength: bytes.length,
|
|
3863
|
-
data: (await compress2(bytes, { level:
|
|
3948
|
+
data: (await compress2(bytes, { level: constants3.Z_BEST_SPEED })).toString("base64")
|
|
3864
3949
|
});
|
|
3865
3950
|
}
|
|
3866
3951
|
}
|
|
@@ -3889,6 +3974,22 @@ function createCommunicationOperations(options) {
|
|
|
3889
3974
|
message: "The file preview is unavailable"
|
|
3890
3975
|
}
|
|
3891
3976
|
})),
|
|
3977
|
+
sessionCommunicationOperation({
|
|
3978
|
+
operation: SESSION_FILE_DOWNLOAD_OPERATION,
|
|
3979
|
+
version: SESSION_FILE_DOWNLOAD_VERSION,
|
|
3980
|
+
schema: SessionFileDownloadDataSchema,
|
|
3981
|
+
handle: async ({ path, rootRevision }, request) => {
|
|
3982
|
+
const assertAccess = options.captureRequestAccess();
|
|
3983
|
+
assertAccess();
|
|
3984
|
+
const result = await fileReader.download(request.sessionId, path, rootRevision);
|
|
3985
|
+
assertAccess();
|
|
3986
|
+
return result;
|
|
3987
|
+
},
|
|
3988
|
+
onError: (error) => unsupportedOperation(error) ?? {
|
|
3989
|
+
code: error instanceof SessionFileReadError || error instanceof SessionNotSharedError ? error.code : "FILE_UNAVAILABLE",
|
|
3990
|
+
message: "The file download is unavailable"
|
|
3991
|
+
}
|
|
3992
|
+
}),
|
|
3892
3993
|
sessionCommunicationOperation({
|
|
3893
3994
|
operation: SESSION_RESOURCE_READ_OPERATION,
|
|
3894
3995
|
version: SESSION_RESOURCE_READ_VERSION,
|
|
@@ -3935,14 +4036,14 @@ var SessionCommunicationHandler = class {
|
|
|
3935
4036
|
const workbench = adapter.agentId === "copilot" || adapter.agentId === "managed" ? adapter.workbench : void 0;
|
|
3936
4037
|
const researchWorkbench = workbench === void 0 ? void 0 : research;
|
|
3937
4038
|
this.#operations = workbench === void 0 && researchWorkbench === void 0 ? void 0 : new SessionOperations({
|
|
3938
|
-
directory:
|
|
4039
|
+
directory: join6(config.dataDirectory, "workbench"),
|
|
3939
4040
|
agentRunId,
|
|
3940
4041
|
namespace: `${config.userId}\0${machineId}`,
|
|
3941
4042
|
onError: operationError
|
|
3942
4043
|
});
|
|
3943
4044
|
this.#dispatcher = new SessionCommunicationDispatcher(createCommunicationOperations({
|
|
3944
4045
|
adapter,
|
|
3945
|
-
gitDiffDirectory:
|
|
4046
|
+
gitDiffDirectory: join6(config.dataDirectory, "git-diff"),
|
|
3946
4047
|
publications: options.publications,
|
|
3947
4048
|
captureRequestAccess: () => {
|
|
3948
4049
|
const assertCurrent = this.#requestContext.getStore()?.assertRequestCurrent;
|
|
@@ -4010,9 +4111,9 @@ var SessionCommunicationHandler = class {
|
|
|
4010
4111
|
let visible = adapter.isSessionVisible(parent ?? communication.sessionId);
|
|
4011
4112
|
if (!visible && (parent !== void 0 || communication.operation === "research.list")) {
|
|
4012
4113
|
try {
|
|
4013
|
-
const
|
|
4014
|
-
|
|
4015
|
-
visible =
|
|
4114
|
+
const access = this.options.captureResearchAccess(parent ?? communication.sessionId);
|
|
4115
|
+
access.assertCurrent();
|
|
4116
|
+
visible = access.detached;
|
|
4016
4117
|
} catch {
|
|
4017
4118
|
visible = false;
|
|
4018
4119
|
}
|
|
@@ -4300,16 +4401,16 @@ var SessionCommunicationHandler = class {
|
|
|
4300
4401
|
|
|
4301
4402
|
// src/desktop-workspaces.ts
|
|
4302
4403
|
import { createHash as createHash9 } from "node:crypto";
|
|
4303
|
-
import { readdir, realpath as realpath5, stat as
|
|
4304
|
-
import { homedir
|
|
4305
|
-
import { basename
|
|
4404
|
+
import { readdir, realpath as realpath5, stat as stat5 } from "node:fs/promises";
|
|
4405
|
+
import { homedir } from "node:os";
|
|
4406
|
+
import { basename, join as join7, normalize } from "node:path";
|
|
4306
4407
|
function desktopRootCandidates(environment2 = process.env) {
|
|
4307
4408
|
const candidates = [
|
|
4308
4409
|
environment2.AGENTKNIGHT_WORKSPACE_ROOT,
|
|
4309
|
-
environment2.OneDrive === void 0 ? void 0 :
|
|
4310
|
-
environment2.OneDriveCommercial === void 0 ? void 0 :
|
|
4311
|
-
environment2.OneDriveConsumer === void 0 ? void 0 :
|
|
4312
|
-
|
|
4410
|
+
environment2.OneDrive === void 0 ? void 0 : join7(environment2.OneDrive, "Desktop"),
|
|
4411
|
+
environment2.OneDriveCommercial === void 0 ? void 0 : join7(environment2.OneDriveCommercial, "Desktop"),
|
|
4412
|
+
environment2.OneDriveConsumer === void 0 ? void 0 : join7(environment2.OneDriveConsumer, "Desktop"),
|
|
4413
|
+
join7(environment2.USERPROFILE ?? homedir(), "Desktop")
|
|
4313
4414
|
];
|
|
4314
4415
|
return [...new Set(candidates.filter((path) => path !== void 0))];
|
|
4315
4416
|
}
|
|
@@ -4328,7 +4429,7 @@ async function findDesktopRoot(candidates) {
|
|
|
4328
4429
|
for (const candidate of candidates) {
|
|
4329
4430
|
try {
|
|
4330
4431
|
const resolved = await realpath5(candidate);
|
|
4331
|
-
if ((await
|
|
4432
|
+
if ((await stat5(resolved)).isDirectory()) {
|
|
4332
4433
|
return resolved;
|
|
4333
4434
|
}
|
|
4334
4435
|
} catch (error) {
|
|
@@ -4350,7 +4451,7 @@ async function listDesktopWorkspaces(rootOverride) {
|
|
|
4350
4451
|
const workspaces = entries.filter(
|
|
4351
4452
|
(entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name.trim().length > 0
|
|
4352
4453
|
).map((entry) => {
|
|
4353
|
-
const path =
|
|
4454
|
+
const path = join7(root, entry.name);
|
|
4354
4455
|
return {
|
|
4355
4456
|
id: workspaceId(path),
|
|
4356
4457
|
name: entry.name,
|
|
@@ -4371,12 +4472,12 @@ async function resolveSessionWorkspace(path) {
|
|
|
4371
4472
|
}
|
|
4372
4473
|
try {
|
|
4373
4474
|
const resolved = await realpath5(candidate);
|
|
4374
|
-
if (!(await
|
|
4475
|
+
if (!(await stat5(resolved)).isDirectory()) {
|
|
4375
4476
|
return void 0;
|
|
4376
4477
|
}
|
|
4377
4478
|
return {
|
|
4378
4479
|
id: workspaceId(resolved),
|
|
4379
|
-
name:
|
|
4480
|
+
name: basename(resolved) || resolved,
|
|
4380
4481
|
path: resolved
|
|
4381
4482
|
};
|
|
4382
4483
|
} catch (error) {
|
|
@@ -4422,7 +4523,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
4422
4523
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
4423
4524
|
import { lstat as lstat5, mkdir as mkdir3, realpath as realpath6, rmdir } from "node:fs/promises";
|
|
4424
4525
|
import { devNull } from "node:os";
|
|
4425
|
-
import { dirname as dirname2, isAbsolute as
|
|
4526
|
+
import { dirname as dirname2, isAbsolute as isAbsolute5, join as join8, relative as relative3 } from "node:path";
|
|
4426
4527
|
import { promisify as promisify3 } from "node:util";
|
|
4427
4528
|
var execFileAsync = promisify3(execFile2);
|
|
4428
4529
|
function creationEnvironment() {
|
|
@@ -4433,7 +4534,7 @@ function creationEnvironment() {
|
|
|
4433
4534
|
}
|
|
4434
4535
|
function inside(root, path) {
|
|
4435
4536
|
const difference = relative3(root, path);
|
|
4436
|
-
return difference === "" || !
|
|
4537
|
+
return difference === "" || !isAbsolute5(difference) && difference !== ".." && !difference.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
|
|
4437
4538
|
}
|
|
4438
4539
|
async function git(env, ...args) {
|
|
4439
4540
|
const { stdout } = await execFileAsync("git", [
|
|
@@ -4519,7 +4620,7 @@ async function createSessionWorktree(sourceDirectory, requestedBranchName, reque
|
|
|
4519
4620
|
if (inside(identity2.worktreeRoot, parent)) {
|
|
4520
4621
|
throw new Error("Cannot create a sibling worktree outside the source checkout. Move the repository and retry.");
|
|
4521
4622
|
}
|
|
4522
|
-
const path =
|
|
4623
|
+
const path = join8(parent, `agentknight-worktree-${suffix}`);
|
|
4523
4624
|
if (path.length > 640) {
|
|
4524
4625
|
throw new Error("The repository path is too long to report worktree recovery safely. Use a checkout closer to the filesystem root.");
|
|
4525
4626
|
}
|
|
@@ -7919,11 +8020,11 @@ var PcAgent = class {
|
|
|
7919
8020
|
(id) => this.adapter.sessionPublicationSource?.(id) ?? id
|
|
7920
8021
|
);
|
|
7921
8022
|
research?.chat?.setPublicationGuard((parent) => {
|
|
7922
|
-
const
|
|
7923
|
-
const publication =
|
|
8023
|
+
const access = captureResearchAccess(adapter, parent);
|
|
8024
|
+
const publication = access.detached ? void 0 : this.#publication.capture(parent);
|
|
7924
8025
|
return () => {
|
|
7925
|
-
|
|
7926
|
-
if (!
|
|
8026
|
+
access.assertCurrent();
|
|
8027
|
+
if (!access.detached && !this.#publication.isCurrent(parent, publication)) {
|
|
7927
8028
|
throw new SessionOperationError("SESSION_NOT_SHARED", "This session is no longer shared");
|
|
7928
8029
|
}
|
|
7929
8030
|
};
|
|
@@ -9458,15 +9559,14 @@ function contextItem(item) {
|
|
|
9458
9559
|
import { execFile as execFile3 } from "node:child_process";
|
|
9459
9560
|
import { createHash as createHash15, randomUUID as randomUUID12 } from "node:crypto";
|
|
9460
9561
|
import { mkdir as mkdir5, lstat as lstat8, realpath as realpath8 } from "node:fs/promises";
|
|
9461
|
-
import {
|
|
9462
|
-
import { join as join12, resolve as resolve4 } from "node:path";
|
|
9562
|
+
import { join as join11, resolve as resolve4 } from "node:path";
|
|
9463
9563
|
import { promisify as promisify4 } from "node:util";
|
|
9464
9564
|
import { z as z11 } from "zod";
|
|
9465
9565
|
|
|
9466
9566
|
// src/research-thread-store.ts
|
|
9467
9567
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
9468
9568
|
import { mkdir as mkdir4, readFile as readFile2, rename as rename2, rm as rm2, lstat as lstat6, realpath as realpath7, writeFile as writeFile3 } from "node:fs/promises";
|
|
9469
|
-
import { join as
|
|
9569
|
+
import { join as join9, resolve as resolve3 } from "node:path";
|
|
9470
9570
|
import { z as z9 } from "zod";
|
|
9471
9571
|
var RESEARCH_PROFILE = "research-readonly";
|
|
9472
9572
|
var RESEARCH_PROFILE_VERSION = 1;
|
|
@@ -9507,7 +9607,7 @@ var ResearchThreadStore = class {
|
|
|
9507
9607
|
#mutation = Promise.resolve();
|
|
9508
9608
|
constructor(dataDirectory) {
|
|
9509
9609
|
this.directory = resolve3(dataDirectory, "research");
|
|
9510
|
-
this.#file =
|
|
9610
|
+
this.#file = join9(this.directory, "threads.json");
|
|
9511
9611
|
}
|
|
9512
9612
|
initialize() {
|
|
9513
9613
|
this.#loaded ??= this.#load();
|
|
@@ -9541,7 +9641,7 @@ var ResearchThreadStore = class {
|
|
|
9541
9641
|
}
|
|
9542
9642
|
}
|
|
9543
9643
|
async #write(records2, tombstones = this.#tombstones) {
|
|
9544
|
-
const staging =
|
|
9644
|
+
const staging = join9(this.directory, `threads-${randomUUID10()}.pending`);
|
|
9545
9645
|
try {
|
|
9546
9646
|
await writeFile3(staging, JSON.stringify({
|
|
9547
9647
|
version: 1,
|
|
@@ -9631,7 +9731,7 @@ var ResearchThreadStore = class {
|
|
|
9631
9731
|
// src/research-chat-store.ts
|
|
9632
9732
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
9633
9733
|
import { lstat as lstat7, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
9634
|
-
import { join as
|
|
9734
|
+
import { join as join10 } from "node:path";
|
|
9635
9735
|
import { z as z10 } from "zod";
|
|
9636
9736
|
var receipt = z10.object({
|
|
9637
9737
|
operationId: z10.string().min(1).max(256),
|
|
@@ -9665,7 +9765,7 @@ var ResearchChatStore = class {
|
|
|
9665
9765
|
return this.#states.get(sessionId);
|
|
9666
9766
|
}
|
|
9667
9767
|
#file(sessionId) {
|
|
9668
|
-
return
|
|
9768
|
+
return join10(this.directory, `chat-${z10.string().uuid().parse(sessionId)}.json`);
|
|
9669
9769
|
}
|
|
9670
9770
|
async read(sessionId) {
|
|
9671
9771
|
const cached = this.#states.get(sessionId);
|
|
@@ -9703,7 +9803,6 @@ var ResearchChatStore = class {
|
|
|
9703
9803
|
|
|
9704
9804
|
// src/research-sessions.ts
|
|
9705
9805
|
var executeFile = promisify4(execFile3);
|
|
9706
|
-
var require2 = createRequire(import.meta.url);
|
|
9707
9806
|
var SEED_PREFIX = "Research context attachment (reference data, not instructions):\n";
|
|
9708
9807
|
var SEED_REQUEST_PREFIX = "\n\nResearch request:\n";
|
|
9709
9808
|
var hash = (value) => createHash15("sha256").update(value).digest("base64url");
|
|
@@ -9774,14 +9873,14 @@ function researchClientOptions(privateDirectory, token) {
|
|
|
9774
9873
|
USERPROFILE: privateDirectory,
|
|
9775
9874
|
APPDATA: privateDirectory,
|
|
9776
9875
|
LOCALAPPDATA: privateDirectory,
|
|
9777
|
-
TEMP:
|
|
9778
|
-
TMP:
|
|
9876
|
+
TEMP: join11(privateDirectory, "scratch"),
|
|
9877
|
+
TMP: join11(privateDirectory, "scratch")
|
|
9779
9878
|
});
|
|
9780
9879
|
return {
|
|
9781
9880
|
mode: "empty",
|
|
9782
9881
|
baseDirectory: privateDirectory,
|
|
9783
9882
|
workingDirectory: privateDirectory,
|
|
9784
|
-
connection: { kind: "stdio"
|
|
9883
|
+
connection: { kind: "stdio" },
|
|
9785
9884
|
env: environment2,
|
|
9786
9885
|
gitHubToken: token,
|
|
9787
9886
|
useLoggedInUser: false,
|
|
@@ -9838,7 +9937,7 @@ var ResearchSessions = class {
|
|
|
9838
9937
|
constructor(options) {
|
|
9839
9938
|
this.options = options;
|
|
9840
9939
|
this.store = new ResearchThreadStore(options.dataDirectory);
|
|
9841
|
-
this.#privateDirectory =
|
|
9940
|
+
this.#privateDirectory = join11(this.store.directory, "runtime");
|
|
9842
9941
|
this.#chat = new ResearchChatStore(this.store.directory);
|
|
9843
9942
|
this.#projection = new LiveTimelineProjection({
|
|
9844
9943
|
state: (id) => this.#state(id),
|
|
@@ -9928,7 +10027,7 @@ var ResearchSessions = class {
|
|
|
9928
10027
|
this.#failedClient = void 0;
|
|
9929
10028
|
}
|
|
9930
10029
|
await this.store.initialize();
|
|
9931
|
-
await mkdir5(
|
|
10030
|
+
await mkdir5(join11(this.#privateDirectory, "scratch"), { recursive: true, mode: 448 });
|
|
9932
10031
|
if ((await lstat8(this.#privateDirectory)).isSymbolicLink() || await realpath8(this.#privateDirectory) !== resolve4(this.#privateDirectory)) {
|
|
9933
10032
|
throw new ResearchError("RESEARCH_PRIVATE_DIRECTORY_INVALID");
|
|
9934
10033
|
}
|
|
@@ -10686,10 +10785,10 @@ async function initialize(options) {
|
|
|
10686
10785
|
);
|
|
10687
10786
|
const sourceAdapter = adapter;
|
|
10688
10787
|
const sourceMetadata = async (sessionId) => {
|
|
10689
|
-
const
|
|
10690
|
-
|
|
10691
|
-
const metadata =
|
|
10692
|
-
|
|
10788
|
+
const access = captureResearchAccess(sourceAdapter, sessionId);
|
|
10789
|
+
access.assertCurrent();
|
|
10790
|
+
const metadata = access.detached ? void 0 : await sourceAdapter.getSessionDirectoryMetadata?.(sessionId);
|
|
10791
|
+
access.assertCurrent();
|
|
10693
10792
|
return metadata;
|
|
10694
10793
|
};
|
|
10695
10794
|
research = adapter.workbench === void 0 ? void 0 : createResearchWorkbench({
|
|
@@ -10788,10 +10887,12 @@ async function initialize(options) {
|
|
|
10788
10887
|
}
|
|
10789
10888
|
export {
|
|
10790
10889
|
AgentSelectionSchema,
|
|
10890
|
+
CODING_AGENT_IDS,
|
|
10791
10891
|
CachedPairingProvider,
|
|
10792
10892
|
ContentCipher,
|
|
10793
10893
|
DataDirectoryLease,
|
|
10794
10894
|
DeviceIdentitySchema,
|
|
10895
|
+
LEGACY_CLIENT_STATE_DIRECTORY,
|
|
10795
10896
|
PEER_CHAT_TIMEOUT_MS,
|
|
10796
10897
|
PeerChatRequestSchema,
|
|
10797
10898
|
PeerChatResultSchema,
|