@agentknight/sdk 0.9.41 → 0.9.43

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 CHANGED
@@ -4,12 +4,12 @@ import {
4
4
  PeerChatRequestSchema,
5
5
  PeerChatResultSchema,
6
6
  validateDataDirectory
7
- } from "./chunks/chunk-QN3OD5UE.js";
7
+ } from "./chunks/chunk-M2QZ6EFR.js";
8
8
  import {
9
9
  AgentSelectionSchema,
10
10
  assertSelectionSnapshotFits,
11
11
  supportsAgentSelection
12
- } from "./chunks/chunk-ASHXZ46V.js";
12
+ } from "./chunks/chunk-YZ2GB3DD.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-SHOE7YAU.js";
34
+ } from "./chunks/chunk-CWJENQ3O.js";
35
35
  import {
36
36
  GROUP_CHAT_MAX_TURNS,
37
37
  GroupChatError
38
- } from "./chunks/chunk-VDC4G7ZG.js";
38
+ } from "./chunks/chunk-C2VUD77E.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-RBULXS7T.js";
49
+ } from "./chunks/chunk-DB3EAMYS.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-LQIW4ARE.js";
60
+ } from "./chunks/chunk-ULM5KUJ7.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,
@@ -152,6 +162,8 @@ import {
152
162
  SessionDeleteRequestSchema,
153
163
  SessionDirectoryListDataSchema,
154
164
  SessionDirectoryListResultDataSchema,
165
+ SessionFileDownloadDataSchema,
166
+ SessionFileDownloadResultDataSchema,
155
167
  SessionFileReadDataSchema,
156
168
  SessionFileReadResultDataSchema,
157
169
  SessionFileReadV2ResultDataSchema,
@@ -227,7 +239,7 @@ import {
227
239
  sessionCommunicationError,
228
240
  sessionCommunicationOperationCapability,
229
241
  workbenchCapability
230
- } from "./chunks/chunk-QTBB2U7G.js";
242
+ } from "./chunks/chunk-7QOBDG6R.js";
231
243
 
232
244
  // src/initialize.ts
233
245
  import { createHash as createHash16 } from "node:crypto";
@@ -244,6 +256,8 @@ var genericIcon = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"
244
256
  var presentations = {
245
257
  copilot: { name: "GitHub Copilot", icon: copilotIcon },
246
258
  pi: { name: "PI", icon: piIcon },
259
+ codex: { name: "Codex", icon: genericIcon },
260
+ claude: { name: "Claude Code", icon: genericIcon },
247
261
  acp: { name: "ACP", icon: genericIcon }
248
262
  };
249
263
  var fallback = { name: "Agent", icon: genericIcon };
@@ -251,48 +265,6 @@ function codingAgentPresentation(agentId) {
251
265
  return Object.hasOwn(presentations, agentId) ? presentations[agentId] : fallback;
252
266
  }
253
267
 
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
268
  // src/coding-agent-factory.ts
297
269
  function assertPiRuntimeSupported(nodeVersion = process.versions.node) {
298
270
  const [major, minor] = nodeVersion.split(".").map(Number);
@@ -305,7 +277,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
305
277
  switch (engine.kind) {
306
278
  case "managed": {
307
279
  if (owner === void 0) throw new Error("Managed coding agents require the PC owner identity");
308
- const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-LMHQX6IH.js");
280
+ const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-ZXTKIPJN.js");
309
281
  return new ManagedCodingAgentAdapter({
310
282
  dataDirectory: config.dataDirectory,
311
283
  owner,
@@ -331,6 +303,24 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
331
303
  }
332
304
  }, onDiagnostic)
333
305
  },
306
+ {
307
+ id: "codex",
308
+ name: codingAgentPresentation("codex").name,
309
+ detect: () => hasLocalExecutable(engine.codexCommand ?? "codex"),
310
+ create: () => createCodingAgentAdapter({
311
+ ...config,
312
+ codingAgent: {
313
+ kind: "codex",
314
+ ...engine.codexCommand === void 0 ? {} : { command: engine.codexCommand }
315
+ }
316
+ }, onDiagnostic)
317
+ },
318
+ {
319
+ id: "claude",
320
+ name: codingAgentPresentation("claude").name,
321
+ detect: detectClaude,
322
+ create: () => createCodingAgentAdapter({ ...config, codingAgent: { kind: "claude" } }, onDiagnostic)
323
+ },
334
324
  {
335
325
  id: "acp",
336
326
  name: codingAgentPresentation("acp").name,
@@ -344,7 +334,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
344
334
  });
345
335
  }
346
336
  case "copilot": {
347
- const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-FY75OLIU.js");
337
+ const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-UP6KDSXH.js");
348
338
  if (owner !== void 0) {
349
339
  return new GithubCopilotAdapter(void 0, void 0, void 0, {
350
340
  dataDirectory: config.dataDirectory,
@@ -364,13 +354,28 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
364
354
  }
365
355
  case "pi": {
366
356
  assertPiRuntimeSupported();
367
- const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-NWQWUNXO.js");
357
+ const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-KXVZ5QP5.js");
368
358
  return new PiCodingAgentAdapter(
369
359
  engine.agentDirectory === void 0 ? {} : { agentDirectory: resolve(engine.agentDirectory) }
370
360
  );
371
361
  }
362
+ case "codex": {
363
+ const { CodexCodingAgentAdapter } = await import("./chunks/codex-adapter-VQTLTFNP.js");
364
+ return new CodexCodingAgentAdapter({
365
+ dataDirectory: resolve(config.dataDirectory, "coding-agents"),
366
+ ...engine.command === void 0 ? {} : { command: engine.command },
367
+ ...onDiagnostic === void 0 ? {} : { onDiagnostic }
368
+ });
369
+ }
370
+ case "claude": {
371
+ const { ClaudeCodingAgentAdapter } = await import("./chunks/claude-adapter-7VQ4QCNI.js");
372
+ return new ClaudeCodingAgentAdapter({
373
+ dataDirectory: resolve(config.dataDirectory, "coding-agents"),
374
+ ...onDiagnostic === void 0 ? {} : { onDiagnostic }
375
+ });
376
+ }
372
377
  case "acp": {
373
- const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-SPIMGGL2.js");
378
+ const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-LJNYHSSY.js");
374
379
  return new AcpCodingAgentAdapter({
375
380
  command: engine.command,
376
381
  args: engine.args,
@@ -413,7 +418,7 @@ import {
413
418
  writeFileSync
414
419
  } from "node:fs";
415
420
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
416
- import { join as join2 } from "node:path";
421
+ import { join } from "node:path";
417
422
  import { z as z2 } from "zod";
418
423
  var KEY_BYTES = 32;
419
424
  var NONCE_BYTES = 12;
@@ -480,7 +485,7 @@ function generateContentKey() {
480
485
  return parseContentKey(randomBytes(KEY_BYTES).toString("base64url"));
481
486
  }
482
487
  async function loadOrCreateContentKey(dataDirectory) {
483
- const path = join2(dataDirectory, "content-key.json");
488
+ const path = join(dataDirectory, "content-key.json");
484
489
  await mkdir(dataDirectory, { recursive: true });
485
490
  try {
486
491
  const stored = ContentKeyFileSchema.parse(
@@ -509,7 +514,7 @@ async function loadOrCreateContentKey(dataDirectory) {
509
514
  }
510
515
  async function createPersistentContentCipher(dataDirectory, key) {
511
516
  await mkdir(dataDirectory, { recursive: true });
512
- const path = join2(dataDirectory, "content-replay.log");
517
+ const path = join(dataDirectory, "content-replay.log");
513
518
  let processedMessages = /* @__PURE__ */ new Map();
514
519
  let compactOnNextWrite = false;
515
520
  try {
@@ -861,15 +866,15 @@ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
861
866
 
862
867
  // src/research-tools.ts
863
868
  import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
864
- import { lstat as lstat3, realpath as realpath3, stat as stat4 } from "node:fs/promises";
865
- import { isAbsolute as isAbsolute4, join as join5 } from "node:path";
869
+ import { lstat as lstat3, realpath as realpath3, stat as stat3 } from "node:fs/promises";
870
+ import { isAbsolute as isAbsolute3, join as join4 } from "node:path";
866
871
  import { gunzipSync } from "node:zlib";
867
872
  import { z as z5 } from "zod";
868
873
 
869
874
  // src/session-directory.ts
870
875
  import { createHash as createHash2 } from "node:crypto";
871
- import { lstat, opendir, realpath, stat as stat2 } from "node:fs/promises";
872
- import { isAbsolute as isAbsolute2, join as join3, relative, sep } from "node:path";
876
+ import { lstat, opendir, realpath, stat } from "node:fs/promises";
877
+ import { isAbsolute, join as join2, relative, sep } from "node:path";
873
878
  var SESSION_DIRECTORY_CONCURRENCY = 4;
874
879
  var SESSION_DIRECTORY_TIMEOUT_MS = 5e3;
875
880
  var SessionDirectoryError = class extends Error {
@@ -884,7 +889,7 @@ function fail(code, message) {
884
889
  }
885
890
  function contained(root, candidate) {
886
891
  const path = relative(root, candidate);
887
- return path === "" || !isAbsolute2(path) && path !== ".." && !path.startsWith(`..${sep}`);
892
+ return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
888
893
  }
889
894
  function sessionRootIdentity(root, info) {
890
895
  return `${root}\0${info.dev}\0${info.ino}`;
@@ -933,14 +938,14 @@ var SessionDirectoryLister = class {
933
938
  checkDeadline();
934
939
  if (metadata === void 0) fail("SESSION_NOT_FOUND", "The session is unavailable");
935
940
  const workingDirectory = metadata.workingDirectory;
936
- if (!workingDirectory || !isAbsolute2(workingDirectory)) {
941
+ if (!workingDirectory || !isAbsolute(workingDirectory)) {
937
942
  fail("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
938
943
  }
939
944
  let root;
940
945
  let rootIdentity;
941
946
  try {
942
947
  root = await realpath(workingDirectory);
943
- const info = await stat2(root, { bigint: true });
948
+ const info = await stat(root, { bigint: true });
944
949
  if (!info.isDirectory()) fail("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
945
950
  rootIdentity = sessionRootIdentity(root, info);
946
951
  } catch (error) {
@@ -953,7 +958,7 @@ var SessionDirectoryLister = class {
953
958
  checkDeadline();
954
959
  let target = root;
955
960
  for (const segment of path === "" ? [] : path.split("/")) {
956
- target = join3(target, segment);
961
+ target = join2(target, segment);
957
962
  const info = await lstat(target);
958
963
  checkDeadline();
959
964
  if (info.isSymbolicLink()) fail("DIRECTORY_BLOCKED", "Links cannot be opened");
@@ -978,7 +983,7 @@ var SessionDirectoryLister = class {
978
983
  }
979
984
  let kind = "blocked";
980
985
  if (isSafeSessionDirectoryPath(entry.name) && !entry.name.includes("/")) {
981
- const child = join3(target, entry.name);
986
+ const child = join2(target, entry.name);
982
987
  const info = await lstat(child);
983
988
  checkDeadline();
984
989
  if (!info.isSymbolicLink() && contained(root, await realpath(child))) {
@@ -994,7 +999,7 @@ var SessionDirectoryLister = class {
994
999
  }
995
1000
  const latestMetadata = await this.metadata(sessionId);
996
1001
  const latestRoot = await realpath(workingDirectory);
997
- const latestInfo = await stat2(latestRoot, { bigint: true });
1002
+ const latestInfo = await stat(latestRoot, { bigint: true });
998
1003
  if (latestMetadata?.workingDirectory !== workingDirectory || sessionRootIdentity(latestRoot, latestInfo) !== rootIdentity || await realpath(target) !== canonicalTarget) {
999
1004
  fail("SESSION_ROOT_CHANGED", "The session directory changed");
1000
1005
  }
@@ -1012,13 +1017,15 @@ var SessionDirectoryLister = class {
1012
1017
  };
1013
1018
 
1014
1019
  // src/session-file.ts
1020
+ import { isUtf8 } from "node:buffer";
1015
1021
  import { createHash as createHash3 } from "node:crypto";
1016
- import { constants as constants2 } from "node:fs";
1017
- import { lstat as lstat2, open, realpath as realpath2, stat as stat3 } from "node:fs/promises";
1018
- import { basename, extname, isAbsolute as isAbsolute3, join as join4 } from "node:path";
1022
+ import { constants } from "node:fs";
1023
+ import { lstat as lstat2, open, realpath as realpath2, stat as stat2 } from "node:fs/promises";
1024
+ import { extname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
1019
1025
  import { promisify } from "node:util";
1020
1026
  import { gzip } from "node:zlib";
1021
1027
  import sharp from "sharp";
1028
+ import { fileTypeFromBuffer } from "file-type";
1022
1029
 
1023
1030
  // src/session-video.ts
1024
1031
  function requireVideo(valid) {
@@ -1181,105 +1188,8 @@ function fail2(code) {
1181
1188
  function fileIdentity(info, includeChangeTime = true) {
1182
1189
  return `${info.dev}\0${info.ino}\0${info.size}\0${info.mtimeNs}\0${includeChangeTime ? info.ctimeNs : ""}\0${info.mode}\0${info.nlink}`;
1183
1190
  }
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
1191
  function textMediaType(path) {
1281
1192
  const extension = extname(path).toLowerCase();
1282
- if (!textExtensions.has(extension) && !textNames.has(basename(path).toLowerCase())) fail2("FILE_UNSUPPORTED");
1283
1193
  switch (extension) {
1284
1194
  case ".html":
1285
1195
  case ".htm":
@@ -1304,6 +1214,9 @@ function textMediaType(path) {
1304
1214
  return "text/plain";
1305
1215
  }
1306
1216
  }
1217
+ function isPreviewableText(bytes) {
1218
+ return isUtf8(bytes) && !bytes.some((byte) => byte <= 8 || byte === 11 || byte >= 14 && byte <= 31 || byte === 127);
1219
+ }
1307
1220
  function rejectAnimatedPng(bytes) {
1308
1221
  for (let offset = 8; offset + 12 <= bytes.length; ) {
1309
1222
  const size = bytes.readUInt32BE(offset);
@@ -1407,13 +1320,19 @@ var SessionFileReader = class {
1407
1320
  if (!SessionFileReadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
1408
1321
  const schema = version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema;
1409
1322
  return schema.parse(await this.#run(
1410
- (checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, false, version)
1323
+ (checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, "preview", version)
1411
1324
  ));
1412
1325
  }
1413
1326
  async readResource(sessionId, path, rootRevision) {
1414
1327
  if (!SessionResourceReadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
1415
1328
  return SessionResourceReadResultDataSchema.parse(await this.#run(
1416
- (checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, true)
1329
+ (checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, "resource")
1330
+ ));
1331
+ }
1332
+ async download(sessionId, path, rootRevision) {
1333
+ if (!SessionFileDownloadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
1334
+ return SessionFileDownloadResultDataSchema.parse(await this.#run(
1335
+ (checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, "download")
1417
1336
  ));
1418
1337
  }
1419
1338
  async #run(read) {
@@ -1441,18 +1360,20 @@ var SessionFileReader = class {
1441
1360
  clearTimeout(timer);
1442
1361
  }
1443
1362
  }
1444
- async #read(sessionId, path, rootRevision, checkDeadline, resource, version = SESSION_FILE_READ_VERSION) {
1363
+ async #read(sessionId, path, rootRevision, checkDeadline, mode, version = SESSION_FILE_READ_VERSION) {
1364
+ const resource = mode === "resource";
1365
+ const download = mode === "download";
1445
1366
  const metadata = await this.metadata(sessionId);
1446
1367
  checkDeadline();
1447
1368
  if (metadata === void 0) fail2("SESSION_NOT_FOUND");
1448
1369
  const workingDirectory = metadata.workingDirectory;
1449
- if (!workingDirectory || !isAbsolute3(workingDirectory)) fail2("SESSION_ROOT_UNAVAILABLE");
1370
+ if (!workingDirectory || !isAbsolute2(workingDirectory)) fail2("SESSION_ROOT_UNAVAILABLE");
1450
1371
  let root;
1451
1372
  let rootIdentity;
1452
1373
  try {
1453
1374
  if ((await lstat2(workingDirectory)).isSymbolicLink()) fail2("FILE_UNAVAILABLE");
1454
1375
  root = await realpath2(workingDirectory);
1455
- const info = await stat3(root, { bigint: true });
1376
+ const info = await stat2(root, { bigint: true });
1456
1377
  if (!info.isDirectory()) fail2("SESSION_ROOT_UNAVAILABLE");
1457
1378
  rootIdentity = sessionRootIdentity(root, info);
1458
1379
  } catch (error) {
@@ -1461,13 +1382,13 @@ var SessionFileReader = class {
1461
1382
  }
1462
1383
  if (createHash3("sha256").update(rootIdentity).digest("base64url") !== rootRevision) fail2("SESSION_ROOT_CHANGED");
1463
1384
  checkDeadline();
1464
- const target = join4(root, ...path.split("/"));
1385
+ const target = join3(root, ...path.split("/"));
1465
1386
  const inspectPath = async () => {
1466
1387
  let current = root;
1467
1388
  const segments = path.split("/");
1468
1389
  let file;
1469
1390
  for (const [index, segment] of segments.entries()) {
1470
- current = join4(current, segment);
1391
+ current = join3(current, segment);
1471
1392
  const info = await lstat2(current, { bigint: true });
1472
1393
  checkDeadline();
1473
1394
  if (info.isSymbolicLink() || (index === segments.length - 1 ? !info.isFile() : !info.isDirectory())) {
@@ -1483,13 +1404,11 @@ var SessionFileReader = class {
1483
1404
  return file;
1484
1405
  };
1485
1406
  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;
1407
+ 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
1408
  if (before.size > BigInt(maxOriginalBytes)) fail2("FILE_TOO_LARGE");
1488
1409
  const extension = extname(path).toLowerCase();
1489
- const image = [".png", ".jpg", ".jpeg", ".webp"].includes(extension);
1490
- const video = !resource && version === SESSION_FILE_READ_V2_VERSION && [".mp4", ".webm"].includes(extension);
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));
1410
+ const resourceType = resource ? resourceMediaType(extension) : void 0;
1411
+ const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
1493
1412
  try {
1494
1413
  const opened = await handle.stat({ bigint: true });
1495
1414
  const includeChangeTime = process.platform !== "win32";
@@ -1509,27 +1428,43 @@ var SessionFileReader = class {
1509
1428
  };
1510
1429
  const bytes = await readOriginal();
1511
1430
  let canonical;
1512
- if (image) {
1513
- if (resource) {
1431
+ let mediaType;
1432
+ if (download) {
1433
+ mediaType = "application/octet-stream";
1434
+ canonical = bytes;
1435
+ } else if (resourceType !== void 0) {
1436
+ mediaType = resourceType;
1437
+ if (resourceType.startsWith("image/")) {
1514
1438
  await validateRawImage(bytes, extension, checkDeadline);
1515
- canonical = bytes;
1516
- } else canonical = await imagePreview(bytes, extension, checkDeadline);
1517
- } else if (mediaType === "video/mp4" || mediaType === "video/webm") {
1518
- try {
1519
- validateSessionVideo(bytes, mediaType);
1520
- } catch (error) {
1521
- throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
1522
- }
1439
+ } else if (!isPreviewableText(bytes)) fail2("FILE_UNSUPPORTED");
1440
+ canonical = bytes;
1441
+ } else if (isPreviewableText(bytes)) {
1442
+ mediaType = textMediaType(path);
1523
1443
  canonical = bytes;
1524
1444
  } else {
1525
- let text;
1445
+ let detected;
1526
1446
  try {
1527
- text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
1447
+ detected = await fileTypeFromBuffer(bytes, {
1448
+ maxBufferSize: maxOriginalBytes,
1449
+ signal: AbortSignal.timeout(SESSION_FILE_TIMEOUT_MS)
1450
+ });
1528
1451
  } catch (error) {
1529
1452
  throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
1530
1453
  }
1531
- if (/[\u0000-\u0008\u000b\u000e-\u001f\u007f]/u.test(text)) fail2("FILE_UNSUPPORTED");
1532
- canonical = bytes;
1454
+ checkDeadline();
1455
+ if (detected?.mime === "image/png" || detected?.mime === "image/jpeg" || detected?.mime === "image/webp") {
1456
+ mediaType = "image/webp";
1457
+ canonical = await imagePreview(bytes, `.${detected.ext}`, checkDeadline);
1458
+ } else if (detected?.mime === "video/mp4" || detected?.mime === "video/webm") {
1459
+ if (version !== SESSION_FILE_READ_V2_VERSION || extension !== (detected.mime === "video/mp4" ? ".mp4" : ".webm")) fail2("FILE_UNSUPPORTED");
1460
+ mediaType = detected.mime;
1461
+ try {
1462
+ validateSessionVideo(bytes, mediaType);
1463
+ } catch (error) {
1464
+ throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
1465
+ }
1466
+ canonical = bytes;
1467
+ } else fail2("FILE_UNSUPPORTED");
1533
1468
  }
1534
1469
  const threshold = resource ? SESSION_RESOURCE_GZIP_THRESHOLD_BYTES : SESSION_FILE_GZIP_THRESHOLD_BYTES;
1535
1470
  const encoding = originalByteLength >= threshold ? "gzip" : "identity";
@@ -1538,7 +1473,7 @@ var SessionFileReader = class {
1538
1473
  checkDeadline();
1539
1474
  try {
1540
1475
  const latestRoot = await realpath2(workingDirectory);
1541
- const latestRootInfo = await stat3(latestRoot, { bigint: true });
1476
+ const latestRootInfo = await stat2(latestRoot, { bigint: true });
1542
1477
  if (latestMetadata?.workingDirectory !== workingDirectory || (await lstat2(workingDirectory)).isSymbolicLink() || sessionRootIdentity(latestRoot, latestRootInfo) !== rootIdentity) fail2("SESSION_ROOT_CHANGED");
1543
1478
  } catch (error) {
1544
1479
  if (error instanceof SessionFileReadError) throw error;
@@ -1553,7 +1488,7 @@ var SessionFileReader = class {
1553
1488
  throw new SessionFileReadError("FILE_CHANGED", { cause: error });
1554
1489
  }
1555
1490
  checkDeadline();
1556
- return (resource ? SessionResourceReadResultDataSchema : version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema).parse({
1491
+ return (download ? SessionFileDownloadResultDataSchema : resource ? SessionResourceReadResultDataSchema : version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema).parse({
1557
1492
  path,
1558
1493
  rootRevision,
1559
1494
  mediaType,
@@ -1902,6 +1837,9 @@ function createWorkbenchOperations(options) {
1902
1837
  return routes;
1903
1838
  }
1904
1839
 
1840
+ // src/legacy-client-state.ts
1841
+ var LEGACY_CLIENT_STATE_DIRECTORY = Buffer.from("2e626c7565706177", "hex").toString("utf8");
1842
+
1905
1843
  // src/research-tools.ts
1906
1844
  var RESEARCH_TOOL_NAMES = ["research_list", "research_read", "research_search"];
1907
1845
  var RESEARCH_SEARCH_FILES = 64;
@@ -1919,11 +1857,11 @@ var ResearchError = class extends Error {
1919
1857
  code;
1920
1858
  };
1921
1859
  async function researchRoot(workingDirectory) {
1922
- if (!isAbsolute4(workingDirectory)) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
1860
+ if (!isAbsolute3(workingDirectory)) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
1923
1861
  try {
1924
1862
  if ((await lstat3(workingDirectory)).isSymbolicLink()) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
1925
1863
  const root = await realpath3(workingDirectory);
1926
- const info = await stat4(root, { bigint: true });
1864
+ const info = await stat3(root, { bigint: true });
1927
1865
  if (!info.isDirectory()) throw new ResearchError("SESSION_ROOT_UNAVAILABLE");
1928
1866
  return {
1929
1867
  workingDirectory: root,
@@ -1948,7 +1886,7 @@ var privateNames = /* @__PURE__ */ new Set([
1948
1886
  ".docker",
1949
1887
  ".copilot",
1950
1888
  ".ak",
1951
- ".bluepaw",
1889
+ LEGACY_CLIENT_STATE_DIRECTORY,
1952
1890
  ".copilot-remote",
1953
1891
  ".agentknight",
1954
1892
  "session-state",
@@ -2041,7 +1979,7 @@ var ResearchTools = class {
2041
1979
  }
2042
1980
  async #validate(path) {
2043
1981
  const source = await this.#source();
2044
- const target = join5(this.root.workingDirectory, ...path.split("/"));
1982
+ const target = join4(this.root.workingDirectory, ...path.split("/"));
2045
1983
  if (sensitiveSegments(this.root.workingDirectory.split(/[\\/]/u)) || !allowedPath(path, source.excludedPaths ?? []) || this.privateDirectories.some((directory) => contained(directory, target))) {
2046
1984
  throw new ResearchError("RESEARCH_PATH_BLOCKED", "The requested path is excluded from research");
2047
1985
  }
@@ -2086,7 +2024,7 @@ var ResearchTools = class {
2086
2024
  const source = await this.#source();
2087
2025
  return result.entries.filter((entry) => {
2088
2026
  const child = path ? `${path}/${entry.name}` : entry.name;
2089
- return entry.kind !== "blocked" && allowedPath(child, source.excludedPaths ?? []) && !this.privateDirectories.some((directory) => contained(directory, join5(this.root.workingDirectory, ...child.split("/"))));
2027
+ return entry.kind !== "blocked" && allowedPath(child, source.excludedPaths ?? []) && !this.privateDirectories.some((directory) => contained(directory, join4(this.root.workingDirectory, ...child.split("/"))));
2090
2028
  });
2091
2029
  }
2092
2030
  async list(input) {
@@ -2186,7 +2124,7 @@ var ResearchTools = class {
2186
2124
  if (path === void 0) break;
2187
2125
  inspected++;
2188
2126
  await this.#validate(path);
2189
- const target = join5(this.root.workingDirectory, ...path.split("/"));
2127
+ const target = join4(this.root.workingDirectory, ...path.split("/"));
2190
2128
  const info = await lstat3(target);
2191
2129
  if (info.isSymbolicLink()) {
2192
2130
  skippedFiles++;
@@ -2307,9 +2245,9 @@ function researchAwareCodingAgentAdapter(inner, research) {
2307
2245
  if (!research.store.has(id)) return false;
2308
2246
  if (inner.isSessionVisible?.(parent) !== false) return true;
2309
2247
  try {
2310
- const access2 = inner.captureResearchAccess?.(parent);
2311
- access2?.assertCurrent();
2312
- return access2?.detached === true;
2248
+ const access = inner.captureResearchAccess?.(parent);
2249
+ access?.assertCurrent();
2250
+ return access?.detached === true;
2313
2251
  } catch {
2314
2252
  return false;
2315
2253
  }
@@ -2461,7 +2399,7 @@ function createRelaySocket(uri, options) {
2461
2399
  // src/service/requests/session-communication.ts
2462
2400
  import { AsyncLocalStorage } from "node:async_hooks";
2463
2401
  import { createHash as createHash8 } from "node:crypto";
2464
- import { join as join7 } from "node:path";
2402
+ import { join as join6 } from "node:path";
2465
2403
  import { performance as performance2 } from "node:perf_hooks";
2466
2404
 
2467
2405
  // src/repeated-error-diagnostics.ts
@@ -2729,14 +2667,14 @@ var SessionPublicationScope = class {
2729
2667
 
2730
2668
  // src/service/requests/communication-operations.ts
2731
2669
  import { promisify as promisify2 } from "node:util";
2732
- import { constants as constants4, gzip as gzip3 } from "node:zlib";
2670
+ import { constants as constants3, gzip as gzip3 } from "node:zlib";
2733
2671
 
2734
2672
  // src/session-git-diff.ts
2735
2673
  import { execFile } from "node:child_process";
2736
2674
  import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
2737
- import { constants as constants3 } from "node:fs";
2738
- import { chmod, lstat as lstat4, mkdir as mkdir2, open as open2, readlink, realpath as realpath4, rm, stat as stat5, writeFile as writeFile2 } from "node:fs/promises";
2739
- import { dirname, isAbsolute as isAbsolute5, join as join6, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
2675
+ import { constants as constants2 } from "node:fs";
2676
+ 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";
2677
+ import { dirname, isAbsolute as isAbsolute4, join as join5, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
2740
2678
  var SESSION_GIT_DIFF_CONCURRENCY = 4;
2741
2679
  var SESSION_GIT_DIFF_TIMEOUT_MS = 15e3;
2742
2680
  var SESSION_GIT_DIFF_MAX_SOURCE_BYTES = 8 * 1024 * 1024;
@@ -2842,10 +2780,10 @@ var Work = class {
2842
2780
  ...this.env,
2843
2781
  ...options.objectDirectory === void 0 ? {} : { GIT_OBJECT_DIRECTORY: options.objectDirectory },
2844
2782
  ...options.isolated ? {
2845
- GIT_DIR: join6(cwd, "no-repository"),
2783
+ GIT_DIR: join5(cwd, "no-repository"),
2846
2784
  GIT_CONFIG_NOSYSTEM: "1",
2847
2785
  GIT_ATTR_NOSYSTEM: "1",
2848
- GIT_CONFIG_GLOBAL: join6(cwd, "gitconfig")
2786
+ GIT_CONFIG_GLOBAL: join5(cwd, "gitconfig")
2849
2787
  } : {}
2850
2788
  }
2851
2789
  }, (error, output2, stderr) => {
@@ -2873,7 +2811,7 @@ async function inspect(root, path, work, kind = "file") {
2873
2811
  let current = root;
2874
2812
  const segments = path.split("/");
2875
2813
  for (const [index, segment] of segments.entries()) {
2876
- current = join6(current, segment);
2814
+ current = join5(current, segment);
2877
2815
  let info;
2878
2816
  try {
2879
2817
  info = await lstat4(current, { bigint: true });
@@ -2903,8 +2841,8 @@ async function readCurrent(root, path, algorithm, work, keepBytes = false) {
2903
2841
  if (before.size > BigInt(MAX_SCAN_BYTES - work.bytes) || keepBytes && before.size > BigInt(SESSION_GIT_DIFF_MAX_SOURCE_BYTES)) limit();
2904
2842
  work.bytes += Number(before.size);
2905
2843
  const handle = await open2(
2906
- join6(root, ...path.split("/")),
2907
- constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0) | (constants3.O_NONBLOCK ?? 0)
2844
+ join5(root, ...path.split("/")),
2845
+ constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0)
2908
2846
  );
2909
2847
  try {
2910
2848
  const opened = await handle.stat({ bigint: true });
@@ -3093,9 +3031,9 @@ async function unchangedSpecial(context, path, original, indexed, work) {
3093
3031
  if (original === void 0 || indexed?.mode !== original.mode || indexed.oid !== original.oid) return blocked();
3094
3032
  if (original.mode === "160000") {
3095
3033
  if (await inspect(context.root, path, work, "directory") === void 0) blocked();
3096
- const target = join6(context.root, ...path.split("/"));
3034
+ const target = join5(context.root, ...path.split("/"));
3097
3035
  try {
3098
- await lstat4(join6(target, ".git"));
3036
+ await lstat4(join5(target, ".git"));
3099
3037
  } catch (error) {
3100
3038
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
3101
3039
  throw error;
@@ -3107,7 +3045,7 @@ async function unchangedSpecial(context, path, original, indexed, work) {
3107
3045
  const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
3108
3046
  let oid;
3109
3047
  if (info.isSymbolicLink()) {
3110
- const target = Buffer.from(await readlink(join6(context.root, ...path.split("/"))), "utf8");
3048
+ const target = Buffer.from(await readlink(join5(context.root, ...path.split("/"))), "utf8");
3111
3049
  oid = createHash5(algorithm).update(`blob ${target.length}\0`).update(target).digest("hex");
3112
3050
  } else {
3113
3051
  oid = (await readCurrent(context.root, path, algorithm, work))?.rawOid;
@@ -3143,13 +3081,13 @@ async function patch(context, change, work, scratchDirectory) {
3143
3081
  if ((await lstat4(directory)).isSymbolicLink() || !samePath(await realpath4(directory), directory)) {
3144
3082
  fail3("DIFF_UNAVAILABLE", "Project diff storage cannot use links");
3145
3083
  }
3146
- const scratch = join6(directory, randomUUID3());
3084
+ const scratch = join5(directory, randomUUID3());
3147
3085
  await mkdir2(scratch, { mode: 448 });
3148
3086
  let text;
3149
3087
  try {
3150
3088
  let bytes = current?.bytes ?? Buffer.alloc(0);
3151
3089
  if (current !== void 0) {
3152
- const objectDirectory = join6(scratch, "objects");
3090
+ const objectDirectory = join5(scratch, "objects");
3153
3091
  await mkdir2(objectDirectory);
3154
3092
  const path = [context.prefix, change.entry.path].filter(Boolean).join("/");
3155
3093
  const oid = decode(await work.git(context.repository, ["hash-object", "-w", "--stdin", `--path=${path}`], {
@@ -3163,18 +3101,18 @@ async function patch(context, change, work, scratchDirectory) {
3163
3101
  });
3164
3102
  }
3165
3103
  if (binary(original) || binary(bytes)) return { patch: "", binary: true };
3166
- for (const side of ["a", "b"]) await mkdir2(join6(scratch, side));
3104
+ for (const side of ["a", "b"]) await mkdir2(join5(scratch, side));
3167
3105
  for (const [side, content, mode] of [
3168
3106
  ["a", original, change.base?.mode],
3169
3107
  ["b", bytes, current?.mode]
3170
3108
  ]) {
3171
3109
  if (mode === void 0) continue;
3172
- const file = join6(scratch, side, ...change.entry.path.split("/"));
3110
+ const file = join5(scratch, side, ...change.entry.path.split("/"));
3173
3111
  await mkdir2(dirname(file), { recursive: true });
3174
3112
  await writeFile2(file, content, { flag: "wx", mode: 384 });
3175
3113
  if (process.platform !== "win32") await chmod(file, mode === "100755" ? 448 : 384);
3176
3114
  }
3177
- await writeFile2(join6(scratch, "gitconfig"), "", { flag: "wx", mode: 384 });
3115
+ await writeFile2(join5(scratch, "gitconfig"), "", { flag: "wx", mode: 384 });
3178
3116
  text = decode(await work.git(scratch, [
3179
3117
  "-c",
3180
3118
  "core.quotePath=false",
@@ -3240,7 +3178,7 @@ var SessionGitDiffReader = class {
3240
3178
  work.check();
3241
3179
  if (metadata === void 0) fail3("SESSION_NOT_FOUND", "The session is unavailable");
3242
3180
  const workingDirectory = metadata.workingDirectory;
3243
- if (!workingDirectory || !isAbsolute5(workingDirectory)) {
3181
+ if (!workingDirectory || !isAbsolute4(workingDirectory)) {
3244
3182
  fail3("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
3245
3183
  }
3246
3184
  let root;
@@ -3250,7 +3188,7 @@ var SessionGitDiffReader = class {
3250
3188
  if ((await lstat4(workingDirectory)).isSymbolicLink() || !samePath(root, resolve2(workingDirectory))) {
3251
3189
  fail3("DIFF_BLOCKED", "Links cannot be compared");
3252
3190
  }
3253
- const info = await stat5(root, { bigint: true });
3191
+ const info = await stat4(root, { bigint: true });
3254
3192
  if (!info.isDirectory()) fail3("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
3255
3193
  rootIdentity = sessionRootIdentity(root, info);
3256
3194
  } catch (error) {
@@ -3263,7 +3201,7 @@ var SessionGitDiffReader = class {
3263
3201
  let repository;
3264
3202
  try {
3265
3203
  const top = decode(await work.git(root, ["rev-parse", "--show-toplevel"])).replace(/\r?\n$/u, "");
3266
- if (!isAbsolute5(top)) fail3("DIFF_UNAVAILABLE", "Git returned an invalid repository");
3204
+ if (!isAbsolute4(top)) fail3("DIFF_UNAVAILABLE", "Git returned an invalid repository");
3267
3205
  repository = await realpath4(top);
3268
3206
  } catch (error) {
3269
3207
  if (error instanceof SessionGitDiffError) throw error;
@@ -3282,7 +3220,7 @@ var SessionGitDiffReader = class {
3282
3220
  rootIdentity,
3283
3221
  workingDirectory,
3284
3222
  repository,
3285
- repositoryIdentity: sessionRootIdentity(repository, await stat5(repository, { bigint: true })),
3223
+ repositoryIdentity: sessionRootIdentity(repository, await stat4(repository, { bigint: true })),
3286
3224
  prefix: relative2(repository, root).split(sep2).join("/"),
3287
3225
  ...await base(repository, work)
3288
3226
  };
@@ -3860,7 +3798,7 @@ function createCommunicationOperations(options) {
3860
3798
  response = SessionGitDiffGzipResultDataSchema.parse({
3861
3799
  encoding: "gzip",
3862
3800
  byteLength: bytes.length,
3863
- data: (await compress2(bytes, { level: constants4.Z_BEST_SPEED })).toString("base64")
3801
+ data: (await compress2(bytes, { level: constants3.Z_BEST_SPEED })).toString("base64")
3864
3802
  });
3865
3803
  }
3866
3804
  }
@@ -3889,6 +3827,22 @@ function createCommunicationOperations(options) {
3889
3827
  message: "The file preview is unavailable"
3890
3828
  }
3891
3829
  })),
3830
+ sessionCommunicationOperation({
3831
+ operation: SESSION_FILE_DOWNLOAD_OPERATION,
3832
+ version: SESSION_FILE_DOWNLOAD_VERSION,
3833
+ schema: SessionFileDownloadDataSchema,
3834
+ handle: async ({ path, rootRevision }, request) => {
3835
+ const assertAccess = options.captureRequestAccess();
3836
+ assertAccess();
3837
+ const result = await fileReader.download(request.sessionId, path, rootRevision);
3838
+ assertAccess();
3839
+ return result;
3840
+ },
3841
+ onError: (error) => unsupportedOperation(error) ?? {
3842
+ code: error instanceof SessionFileReadError || error instanceof SessionNotSharedError ? error.code : "FILE_UNAVAILABLE",
3843
+ message: "The file download is unavailable"
3844
+ }
3845
+ }),
3892
3846
  sessionCommunicationOperation({
3893
3847
  operation: SESSION_RESOURCE_READ_OPERATION,
3894
3848
  version: SESSION_RESOURCE_READ_VERSION,
@@ -3935,14 +3889,14 @@ var SessionCommunicationHandler = class {
3935
3889
  const workbench = adapter.agentId === "copilot" || adapter.agentId === "managed" ? adapter.workbench : void 0;
3936
3890
  const researchWorkbench = workbench === void 0 ? void 0 : research;
3937
3891
  this.#operations = workbench === void 0 && researchWorkbench === void 0 ? void 0 : new SessionOperations({
3938
- directory: join7(config.dataDirectory, "workbench"),
3892
+ directory: join6(config.dataDirectory, "workbench"),
3939
3893
  agentRunId,
3940
3894
  namespace: `${config.userId}\0${machineId}`,
3941
3895
  onError: operationError
3942
3896
  });
3943
3897
  this.#dispatcher = new SessionCommunicationDispatcher(createCommunicationOperations({
3944
3898
  adapter,
3945
- gitDiffDirectory: join7(config.dataDirectory, "git-diff"),
3899
+ gitDiffDirectory: join6(config.dataDirectory, "git-diff"),
3946
3900
  publications: options.publications,
3947
3901
  captureRequestAccess: () => {
3948
3902
  const assertCurrent = this.#requestContext.getStore()?.assertRequestCurrent;
@@ -4010,9 +3964,9 @@ var SessionCommunicationHandler = class {
4010
3964
  let visible = adapter.isSessionVisible(parent ?? communication.sessionId);
4011
3965
  if (!visible && (parent !== void 0 || communication.operation === "research.list")) {
4012
3966
  try {
4013
- const access2 = this.options.captureResearchAccess(parent ?? communication.sessionId);
4014
- access2.assertCurrent();
4015
- visible = access2.detached;
3967
+ const access = this.options.captureResearchAccess(parent ?? communication.sessionId);
3968
+ access.assertCurrent();
3969
+ visible = access.detached;
4016
3970
  } catch {
4017
3971
  visible = false;
4018
3972
  }
@@ -4300,16 +4254,16 @@ var SessionCommunicationHandler = class {
4300
4254
 
4301
4255
  // src/desktop-workspaces.ts
4302
4256
  import { createHash as createHash9 } from "node:crypto";
4303
- import { readdir, realpath as realpath5, stat as stat6 } from "node:fs/promises";
4304
- import { homedir as homedir2 } from "node:os";
4305
- import { basename as basename2, join as join8, normalize } from "node:path";
4257
+ import { readdir, realpath as realpath5, stat as stat5 } from "node:fs/promises";
4258
+ import { homedir } from "node:os";
4259
+ import { basename, join as join7, normalize } from "node:path";
4306
4260
  function desktopRootCandidates(environment2 = process.env) {
4307
4261
  const candidates = [
4308
4262
  environment2.AGENTKNIGHT_WORKSPACE_ROOT,
4309
- environment2.OneDrive === void 0 ? void 0 : join8(environment2.OneDrive, "Desktop"),
4310
- environment2.OneDriveCommercial === void 0 ? void 0 : join8(environment2.OneDriveCommercial, "Desktop"),
4311
- environment2.OneDriveConsumer === void 0 ? void 0 : join8(environment2.OneDriveConsumer, "Desktop"),
4312
- join8(environment2.USERPROFILE ?? homedir2(), "Desktop")
4263
+ environment2.OneDrive === void 0 ? void 0 : join7(environment2.OneDrive, "Desktop"),
4264
+ environment2.OneDriveCommercial === void 0 ? void 0 : join7(environment2.OneDriveCommercial, "Desktop"),
4265
+ environment2.OneDriveConsumer === void 0 ? void 0 : join7(environment2.OneDriveConsumer, "Desktop"),
4266
+ join7(environment2.USERPROFILE ?? homedir(), "Desktop")
4313
4267
  ];
4314
4268
  return [...new Set(candidates.filter((path) => path !== void 0))];
4315
4269
  }
@@ -4328,7 +4282,7 @@ async function findDesktopRoot(candidates) {
4328
4282
  for (const candidate of candidates) {
4329
4283
  try {
4330
4284
  const resolved = await realpath5(candidate);
4331
- if ((await stat6(resolved)).isDirectory()) {
4285
+ if ((await stat5(resolved)).isDirectory()) {
4332
4286
  return resolved;
4333
4287
  }
4334
4288
  } catch (error) {
@@ -4350,7 +4304,7 @@ async function listDesktopWorkspaces(rootOverride) {
4350
4304
  const workspaces = entries.filter(
4351
4305
  (entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name.trim().length > 0
4352
4306
  ).map((entry) => {
4353
- const path = join8(root, entry.name);
4307
+ const path = join7(root, entry.name);
4354
4308
  return {
4355
4309
  id: workspaceId(path),
4356
4310
  name: entry.name,
@@ -4371,12 +4325,12 @@ async function resolveSessionWorkspace(path) {
4371
4325
  }
4372
4326
  try {
4373
4327
  const resolved = await realpath5(candidate);
4374
- if (!(await stat6(resolved)).isDirectory()) {
4328
+ if (!(await stat5(resolved)).isDirectory()) {
4375
4329
  return void 0;
4376
4330
  }
4377
4331
  return {
4378
4332
  id: workspaceId(resolved),
4379
- name: basename2(resolved) || resolved,
4333
+ name: basename(resolved) || resolved,
4380
4334
  path: resolved
4381
4335
  };
4382
4336
  } catch (error) {
@@ -4422,7 +4376,7 @@ import { execFile as execFile2 } from "node:child_process";
4422
4376
  import { randomUUID as randomUUID4 } from "node:crypto";
4423
4377
  import { lstat as lstat5, mkdir as mkdir3, realpath as realpath6, rmdir } from "node:fs/promises";
4424
4378
  import { devNull } from "node:os";
4425
- import { dirname as dirname2, isAbsolute as isAbsolute6, join as join9, relative as relative3 } from "node:path";
4379
+ import { dirname as dirname2, isAbsolute as isAbsolute5, join as join8, relative as relative3 } from "node:path";
4426
4380
  import { promisify as promisify3 } from "node:util";
4427
4381
  var execFileAsync = promisify3(execFile2);
4428
4382
  function creationEnvironment() {
@@ -4433,7 +4387,7 @@ function creationEnvironment() {
4433
4387
  }
4434
4388
  function inside(root, path) {
4435
4389
  const difference = relative3(root, path);
4436
- return difference === "" || !isAbsolute6(difference) && difference !== ".." && !difference.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
4390
+ return difference === "" || !isAbsolute5(difference) && difference !== ".." && !difference.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
4437
4391
  }
4438
4392
  async function git(env, ...args) {
4439
4393
  const { stdout } = await execFileAsync("git", [
@@ -4519,7 +4473,7 @@ async function createSessionWorktree(sourceDirectory, requestedBranchName, reque
4519
4473
  if (inside(identity2.worktreeRoot, parent)) {
4520
4474
  throw new Error("Cannot create a sibling worktree outside the source checkout. Move the repository and retry.");
4521
4475
  }
4522
- const path = join9(parent, `agentknight-worktree-${suffix}`);
4476
+ const path = join8(parent, `agentknight-worktree-${suffix}`);
4523
4477
  if (path.length > 640) {
4524
4478
  throw new Error("The repository path is too long to report worktree recovery safely. Use a checkout closer to the filesystem root.");
4525
4479
  }
@@ -7919,11 +7873,11 @@ var PcAgent = class {
7919
7873
  (id) => this.adapter.sessionPublicationSource?.(id) ?? id
7920
7874
  );
7921
7875
  research?.chat?.setPublicationGuard((parent) => {
7922
- const access2 = captureResearchAccess(adapter, parent);
7923
- const publication = access2.detached ? void 0 : this.#publication.capture(parent);
7876
+ const access = captureResearchAccess(adapter, parent);
7877
+ const publication = access.detached ? void 0 : this.#publication.capture(parent);
7924
7878
  return () => {
7925
- access2.assertCurrent();
7926
- if (!access2.detached && !this.#publication.isCurrent(parent, publication)) {
7879
+ access.assertCurrent();
7880
+ if (!access.detached && !this.#publication.isCurrent(parent, publication)) {
7927
7881
  throw new SessionOperationError("SESSION_NOT_SHARED", "This session is no longer shared");
7928
7882
  }
7929
7883
  };
@@ -9458,15 +9412,14 @@ function contextItem(item) {
9458
9412
  import { execFile as execFile3 } from "node:child_process";
9459
9413
  import { createHash as createHash15, randomUUID as randomUUID12 } from "node:crypto";
9460
9414
  import { mkdir as mkdir5, lstat as lstat8, realpath as realpath8 } from "node:fs/promises";
9461
- import { createRequire } from "node:module";
9462
- import { join as join12, resolve as resolve4 } from "node:path";
9415
+ import { join as join11, resolve as resolve4 } from "node:path";
9463
9416
  import { promisify as promisify4 } from "node:util";
9464
9417
  import { z as z11 } from "zod";
9465
9418
 
9466
9419
  // src/research-thread-store.ts
9467
9420
  import { randomUUID as randomUUID10 } from "node:crypto";
9468
9421
  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 join10, resolve as resolve3 } from "node:path";
9422
+ import { join as join9, resolve as resolve3 } from "node:path";
9470
9423
  import { z as z9 } from "zod";
9471
9424
  var RESEARCH_PROFILE = "research-readonly";
9472
9425
  var RESEARCH_PROFILE_VERSION = 1;
@@ -9507,7 +9460,7 @@ var ResearchThreadStore = class {
9507
9460
  #mutation = Promise.resolve();
9508
9461
  constructor(dataDirectory) {
9509
9462
  this.directory = resolve3(dataDirectory, "research");
9510
- this.#file = join10(this.directory, "threads.json");
9463
+ this.#file = join9(this.directory, "threads.json");
9511
9464
  }
9512
9465
  initialize() {
9513
9466
  this.#loaded ??= this.#load();
@@ -9541,7 +9494,7 @@ var ResearchThreadStore = class {
9541
9494
  }
9542
9495
  }
9543
9496
  async #write(records2, tombstones = this.#tombstones) {
9544
- const staging = join10(this.directory, `threads-${randomUUID10()}.pending`);
9497
+ const staging = join9(this.directory, `threads-${randomUUID10()}.pending`);
9545
9498
  try {
9546
9499
  await writeFile3(staging, JSON.stringify({
9547
9500
  version: 1,
@@ -9631,7 +9584,7 @@ var ResearchThreadStore = class {
9631
9584
  // src/research-chat-store.ts
9632
9585
  import { randomUUID as randomUUID11 } from "node:crypto";
9633
9586
  import { lstat as lstat7, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
9634
- import { join as join11 } from "node:path";
9587
+ import { join as join10 } from "node:path";
9635
9588
  import { z as z10 } from "zod";
9636
9589
  var receipt = z10.object({
9637
9590
  operationId: z10.string().min(1).max(256),
@@ -9665,7 +9618,7 @@ var ResearchChatStore = class {
9665
9618
  return this.#states.get(sessionId);
9666
9619
  }
9667
9620
  #file(sessionId) {
9668
- return join11(this.directory, `chat-${z10.string().uuid().parse(sessionId)}.json`);
9621
+ return join10(this.directory, `chat-${z10.string().uuid().parse(sessionId)}.json`);
9669
9622
  }
9670
9623
  async read(sessionId) {
9671
9624
  const cached = this.#states.get(sessionId);
@@ -9703,7 +9656,6 @@ var ResearchChatStore = class {
9703
9656
 
9704
9657
  // src/research-sessions.ts
9705
9658
  var executeFile = promisify4(execFile3);
9706
- var require2 = createRequire(import.meta.url);
9707
9659
  var SEED_PREFIX = "Research context attachment (reference data, not instructions):\n";
9708
9660
  var SEED_REQUEST_PREFIX = "\n\nResearch request:\n";
9709
9661
  var hash = (value) => createHash15("sha256").update(value).digest("base64url");
@@ -9774,14 +9726,14 @@ function researchClientOptions(privateDirectory, token) {
9774
9726
  USERPROFILE: privateDirectory,
9775
9727
  APPDATA: privateDirectory,
9776
9728
  LOCALAPPDATA: privateDirectory,
9777
- TEMP: join12(privateDirectory, "scratch"),
9778
- TMP: join12(privateDirectory, "scratch")
9729
+ TEMP: join11(privateDirectory, "scratch"),
9730
+ TMP: join11(privateDirectory, "scratch")
9779
9731
  });
9780
9732
  return {
9781
9733
  mode: "empty",
9782
9734
  baseDirectory: privateDirectory,
9783
9735
  workingDirectory: privateDirectory,
9784
- connection: { kind: "stdio", path: require2.resolve("@github/copilot/npm-loader.js") },
9736
+ connection: { kind: "stdio" },
9785
9737
  env: environment2,
9786
9738
  gitHubToken: token,
9787
9739
  useLoggedInUser: false,
@@ -9838,7 +9790,7 @@ var ResearchSessions = class {
9838
9790
  constructor(options) {
9839
9791
  this.options = options;
9840
9792
  this.store = new ResearchThreadStore(options.dataDirectory);
9841
- this.#privateDirectory = join12(this.store.directory, "runtime");
9793
+ this.#privateDirectory = join11(this.store.directory, "runtime");
9842
9794
  this.#chat = new ResearchChatStore(this.store.directory);
9843
9795
  this.#projection = new LiveTimelineProjection({
9844
9796
  state: (id) => this.#state(id),
@@ -9928,7 +9880,7 @@ var ResearchSessions = class {
9928
9880
  this.#failedClient = void 0;
9929
9881
  }
9930
9882
  await this.store.initialize();
9931
- await mkdir5(join12(this.#privateDirectory, "scratch"), { recursive: true, mode: 448 });
9883
+ await mkdir5(join11(this.#privateDirectory, "scratch"), { recursive: true, mode: 448 });
9932
9884
  if ((await lstat8(this.#privateDirectory)).isSymbolicLink() || await realpath8(this.#privateDirectory) !== resolve4(this.#privateDirectory)) {
9933
9885
  throw new ResearchError("RESEARCH_PRIVATE_DIRECTORY_INVALID");
9934
9886
  }
@@ -10686,10 +10638,10 @@ async function initialize(options) {
10686
10638
  );
10687
10639
  const sourceAdapter = adapter;
10688
10640
  const sourceMetadata = async (sessionId) => {
10689
- const access2 = captureResearchAccess(sourceAdapter, sessionId);
10690
- access2.assertCurrent();
10691
- const metadata = access2.detached ? void 0 : await sourceAdapter.getSessionDirectoryMetadata?.(sessionId);
10692
- access2.assertCurrent();
10641
+ const access = captureResearchAccess(sourceAdapter, sessionId);
10642
+ access.assertCurrent();
10643
+ const metadata = access.detached ? void 0 : await sourceAdapter.getSessionDirectoryMetadata?.(sessionId);
10644
+ access.assertCurrent();
10693
10645
  return metadata;
10694
10646
  };
10695
10647
  research = adapter.workbench === void 0 ? void 0 : createResearchWorkbench({
@@ -10788,10 +10740,12 @@ async function initialize(options) {
10788
10740
  }
10789
10741
  export {
10790
10742
  AgentSelectionSchema,
10743
+ CODING_AGENT_IDS,
10791
10744
  CachedPairingProvider,
10792
10745
  ContentCipher,
10793
10746
  DataDirectoryLease,
10794
10747
  DeviceIdentitySchema,
10748
+ LEGACY_CLIENT_STATE_DIRECTORY,
10795
10749
  PEER_CHAT_TIMEOUT_MS,
10796
10750
  PeerChatRequestSchema,
10797
10751
  PeerChatResultSchema,