@agentknight/sdk 0.9.43 → 0.9.45

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-M2QZ6EFR.js";
7
+ } from "./chunks/chunk-GRXWNM6S.js";
8
8
  import {
9
9
  AgentSelectionSchema,
10
10
  assertSelectionSnapshotFits,
11
11
  supportsAgentSelection
12
- } from "./chunks/chunk-YZ2GB3DD.js";
12
+ } from "./chunks/chunk-DCY33KQC.js";
13
13
  import {
14
14
  ConversationNormalizer,
15
15
  LiveTimelineProjection,
@@ -31,11 +31,11 @@ import {
31
31
  resolveGitProjectIdentity,
32
32
  sessionRestoreFailure,
33
33
  validateOperationResponse
34
- } from "./chunks/chunk-CWJENQ3O.js";
34
+ } from "./chunks/chunk-WAY4EXDI.js";
35
35
  import {
36
36
  GROUP_CHAT_MAX_TURNS,
37
37
  GroupChatError
38
- } from "./chunks/chunk-C2VUD77E.js";
38
+ } from "./chunks/chunk-OPUBIQCO.js";
39
39
  import {
40
40
  detectClaude,
41
41
  detectCopilot,
@@ -46,7 +46,7 @@ import {
46
46
  assistantPreview,
47
47
  isContentfulAssistant,
48
48
  userPreview
49
- } from "./chunks/chunk-DB3EAMYS.js";
49
+ } from "./chunks/chunk-QBKKQQCB.js";
50
50
  import {
51
51
  AgentModelListingError,
52
52
  AgentSessionRenameError,
@@ -57,7 +57,7 @@ import {
57
57
  UnsupportedAgentOperationError,
58
58
  captureResearchAccess,
59
59
  captureWorkbenchAccess
60
- } from "./chunks/chunk-ULM5KUJ7.js";
60
+ } from "./chunks/chunk-7A2SQAZ7.js";
61
61
  import {
62
62
  A2aRelayRequestSchema,
63
63
  A2aRelayResponseSchema,
@@ -131,6 +131,7 @@ import {
131
131
  SESSION_GIT_DIFF_MAX_PATCH_BYTES,
132
132
  SESSION_GIT_DIFF_MAX_RESPONSE_BYTES,
133
133
  SESSION_GIT_DIFF_OPERATION,
134
+ SESSION_GIT_DIFF_PARTIAL_VERSION,
134
135
  SESSION_GIT_DIFF_VERSION,
135
136
  SESSION_IMAGE_API_PATH,
136
137
  SESSION_IMAGE_MAX_BYTES,
@@ -169,6 +170,7 @@ import {
169
170
  SessionFileReadV2ResultDataSchema,
170
171
  SessionGitDiffDataSchema,
171
172
  SessionGitDiffGzipResultDataSchema,
173
+ SessionGitDiffPartialResultDataSchema,
172
174
  SessionGitDiffResultDataSchema,
173
175
  SessionImageCommitResultSchema,
174
176
  SessionImageScopeSchema,
@@ -239,7 +241,7 @@ import {
239
241
  sessionCommunicationError,
240
242
  sessionCommunicationOperationCapability,
241
243
  workbenchCapability
242
- } from "./chunks/chunk-7QOBDG6R.js";
244
+ } from "./chunks/chunk-DUMC5F2D.js";
243
245
 
244
246
  // src/initialize.ts
245
247
  import { createHash as createHash16 } from "node:crypto";
@@ -277,7 +279,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
277
279
  switch (engine.kind) {
278
280
  case "managed": {
279
281
  if (owner === void 0) throw new Error("Managed coding agents require the PC owner identity");
280
- const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-ZXTKIPJN.js");
282
+ const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-UYLBGRNG.js");
281
283
  return new ManagedCodingAgentAdapter({
282
284
  dataDirectory: config.dataDirectory,
283
285
  owner,
@@ -334,7 +336,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
334
336
  });
335
337
  }
336
338
  case "copilot": {
337
- const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-UP6KDSXH.js");
339
+ const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-6PV52BVH.js");
338
340
  if (owner !== void 0) {
339
341
  return new GithubCopilotAdapter(void 0, void 0, void 0, {
340
342
  dataDirectory: config.dataDirectory,
@@ -354,13 +356,13 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
354
356
  }
355
357
  case "pi": {
356
358
  assertPiRuntimeSupported();
357
- const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-KXVZ5QP5.js");
359
+ const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-5PZT5YQN.js");
358
360
  return new PiCodingAgentAdapter(
359
361
  engine.agentDirectory === void 0 ? {} : { agentDirectory: resolve(engine.agentDirectory) }
360
362
  );
361
363
  }
362
364
  case "codex": {
363
- const { CodexCodingAgentAdapter } = await import("./chunks/codex-adapter-VQTLTFNP.js");
365
+ const { CodexCodingAgentAdapter } = await import("./chunks/codex-adapter-IR5E7AMV.js");
364
366
  return new CodexCodingAgentAdapter({
365
367
  dataDirectory: resolve(config.dataDirectory, "coding-agents"),
366
368
  ...engine.command === void 0 ? {} : { command: engine.command },
@@ -368,14 +370,14 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
368
370
  });
369
371
  }
370
372
  case "claude": {
371
- const { ClaudeCodingAgentAdapter } = await import("./chunks/claude-adapter-7VQ4QCNI.js");
373
+ const { ClaudeCodingAgentAdapter } = await import("./chunks/claude-adapter-CBEKRKPT.js");
372
374
  return new ClaudeCodingAgentAdapter({
373
375
  dataDirectory: resolve(config.dataDirectory, "coding-agents"),
374
376
  ...onDiagnostic === void 0 ? {} : { onDiagnostic }
375
377
  });
376
378
  }
377
379
  case "acp": {
378
- const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-LJNYHSSY.js");
380
+ const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-3CQCDP5H.js");
379
381
  return new AcpCodingAgentAdapter({
380
382
  command: engine.command,
381
383
  args: engine.args,
@@ -2690,6 +2692,28 @@ var SessionGitDiffError = class extends Error {
2690
2692
  function fail3(code, message) {
2691
2693
  throw new SessionGitDiffError(code, message);
2692
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
+ }
2693
2717
  function changed() {
2694
2718
  return fail3("DIFF_CHANGED", "The project diff changed; refresh the changed-file list");
2695
2719
  }
@@ -2725,12 +2749,30 @@ function environment() {
2725
2749
  function isExit(error, code) {
2726
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);
2727
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
+ }
2728
2758
  var Work = class {
2759
+ constructor(partial) {
2760
+ this.partial = partial;
2761
+ }
2762
+ partial;
2729
2763
  deadline = Date.now() + SESSION_GIT_DIFF_TIMEOUT_MS;
2730
2764
  env = environment();
2731
2765
  bytes = 0;
2766
+ linkFiles = /* @__PURE__ */ new Set();
2767
+ symlinks = true;
2732
2768
  #filterConfig = [];
2733
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";
2734
2776
  const names = records(await this.git(repository, [
2735
2777
  "config",
2736
2778
  "--includes",
@@ -2836,8 +2878,22 @@ async function inspect(root, path, work, kind = "file") {
2836
2878
  return void 0;
2837
2879
  }
2838
2880
  async function readCurrent(root, path, algorithm, work, keepBytes = false) {
2839
- const before = await inspect(root, path, work);
2881
+ const kind = work.partial ? "link" : "file";
2882
+ const before = await inspect(root, path, work, kind);
2840
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
+ }
2841
2897
  if (before.size > BigInt(MAX_SCAN_BYTES - work.bytes) || keepBytes && before.size > BigInt(SESSION_GIT_DIFF_MAX_SOURCE_BYTES)) limit();
2842
2898
  work.bytes += Number(before.size);
2843
2899
  const handle = await open2(
@@ -2867,7 +2923,7 @@ async function readCurrent(root, path, algorithm, work, keepBytes = false) {
2867
2923
  oid: rawOid,
2868
2924
  rawOid,
2869
2925
  identity: identity(before),
2870
- 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",
2871
2927
  ...keepBytes ? { bytes: Buffer.concat(chunks) } : {}
2872
2928
  };
2873
2929
  } finally {
@@ -2903,7 +2959,6 @@ function scopedPath(context, path) {
2903
2959
  const prefix = context.prefix === "" ? "" : `${context.prefix}/`;
2904
2960
  if (!path.startsWith(prefix)) fail3("DIFF_BLOCKED", "Git returned a path outside the session");
2905
2961
  const scoped = path.slice(prefix.length);
2906
- if (!safePath(scoped)) fail3("DIFF_BLOCKED", "The project contains an unsupported path");
2907
2962
  return scoped;
2908
2963
  }
2909
2964
  function records(output2) {
@@ -2934,7 +2989,7 @@ async function changes(context, work) {
2934
2989
  return scoped2;
2935
2990
  };
2936
2991
  for (const record of tree) {
2937
- const match = /^(\d{6}) (blob|commit) ([a-f0-9]{40}|[a-f0-9]{64})\t(.+)$/u.exec(record);
2992
+ const match = /^(\d{6}) (blob|commit) ([a-f0-9]{40}|[a-f0-9]{64})\t([\s\S]+)$/u.exec(record);
2938
2993
  if (!match?.[1] || !match[3] || !match[4]) fail3("DIFF_UNAVAILABLE", "Git returned an invalid tree");
2939
2994
  blobs.set(scoped(match[4]), { mode: match[1], oid: match[3] });
2940
2995
  }
@@ -2946,7 +3001,7 @@ async function changes(context, work) {
2946
3001
  "--",
2947
3002
  ...paths
2948
3003
  ]))) {
2949
- const match = /^(\d{6}) ([a-f0-9]{40}|[a-f0-9]{64}) [0-3]\t(.+)$/u.exec(record);
3004
+ const match = /^(\d{6}) ([a-f0-9]{40}|[a-f0-9]{64}) [0-3]\t([\s\S]+)$/u.exec(record);
2950
3005
  if (!match?.[1] || !match[2] || !match[3]) fail3("DIFF_UNAVAILABLE", "Git returned an invalid index");
2951
3006
  tracked.set(scoped(match[3]), { mode: match[1], oid: match[2] });
2952
3007
  }
@@ -2985,38 +3040,88 @@ async function changes(context, work) {
2985
3040
  files: untracked
2986
3041
  }), "utf8") > SESSION_GIT_DIFF_MAX_RESPONSE_BYTES) limit();
2987
3042
  const result = [];
3043
+ const errors = /* @__PURE__ */ new Map();
2988
3044
  const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
2989
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
+ }
2990
3049
  for (const path of [...candidates].sort()) {
2991
3050
  work.check();
2992
- const original = blobs.get(path);
2993
- const indexed = tracked.get(path);
2994
- if ([original?.mode, indexed?.mode].some((mode) => mode !== void 0 && mode !== "100644" && mode !== "100755")) {
2995
- await unchangedSpecial(context, path, original, indexed, work);
2996
- candidates.delete(path);
2997
- continue;
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);
2998
3066
  }
2999
- const current = await readCurrent(context.root, path, algorithm, work);
3000
- if (current !== void 0) currentFiles.set(path, current);
3001
3067
  }
3002
- if (currentFiles.size > 0) {
3003
- const hashes = decode(await work.git(context.repository, ["hash-object", "--stdin-paths"], {
3004
- input: [...currentFiles.keys()].map((path) => [context.prefix, path].filter(Boolean).join("/")).join("\n") + "\n"
3005
- })).trim().split("\n");
3006
- if (hashes.length !== currentFiles.size) changed();
3007
- for (const [index, [path, current]] of [...currentFiles].entries()) {
3008
- const oid = hashes[index];
3009
- if (!oid || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(oid)) changed();
3010
- const latest = await inspect(context.root, path, work);
3011
- if (latest === void 0 || identity(latest) !== current.identity) changed();
3012
- current.oid = oid;
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
+ }
3013
3108
  }
3014
3109
  }
3015
3110
  for (const path of [...candidates].sort()) {
3016
3111
  const original = blobs.get(path);
3017
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
+ }
3018
3123
  if (original === void 0 && current === void 0) continue;
3019
- 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;
3020
3125
  result.push({
3021
3126
  entry: { path, status: original === void 0 ? tracked.has(path) ? "added" : "untracked" : current === void 0 ? "deleted" : "modified" },
3022
3127
  base: original,
@@ -3068,7 +3173,20 @@ async function patch(context, change, work, scratchDirectory) {
3068
3173
  if (current?.rawOid !== change.current?.rawOid || current?.mode !== change.current?.mode) changed();
3069
3174
  let original = Buffer.alloc(0);
3070
3175
  if (change.base !== void 0) {
3071
- const size = Number(decode(await work.git(context.repository, ["cat-file", "-s", change.base.oid])).trim());
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());
3072
3190
  if (!Number.isSafeInteger(size) || size < 0 || size > SESSION_GIT_DIFF_MAX_SOURCE_BYTES) limit();
3073
3191
  original = await work.git(context.repository, ["cat-file", "blob", change.base.oid], { maxBuffer: size + 1 });
3074
3192
  if (original.length !== size) changed();
@@ -3086,7 +3204,7 @@ async function patch(context, change, work, scratchDirectory) {
3086
3204
  let text;
3087
3205
  try {
3088
3206
  let bytes = current?.bytes ?? Buffer.alloc(0);
3089
- if (current !== void 0) {
3207
+ if (current !== void 0 && current.mode !== "120000") {
3090
3208
  const objectDirectory = join5(scratch, "objects");
3091
3209
  await mkdir2(objectDirectory);
3092
3210
  const path = [context.prefix, change.entry.path].filter(Boolean).join("/");
@@ -3134,6 +3252,18 @@ async function patch(context, change, work, scratchDirectory) {
3134
3252
  } finally {
3135
3253
  await rm(scratch, { recursive: true, force: true, maxRetries: 3 });
3136
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
+ }
3137
3267
  if (Buffer.byteLength(text, "utf8") > SESSION_GIT_DIFF_MAX_PATCH_BYTES) limit();
3138
3268
  return { patch: text, binary: false };
3139
3269
  }
@@ -3145,14 +3275,14 @@ var SessionGitDiffReader = class {
3145
3275
  metadata;
3146
3276
  scratchDirectory;
3147
3277
  #active = 0;
3148
- async read(sessionId, data) {
3278
+ async read(sessionId, data, version = SESSION_GIT_DIFF_PARTIAL_VERSION) {
3149
3279
  if (!SessionGitDiffDataSchema.safeParse(data).success || data.path !== void 0 && !safePath(data.path)) {
3150
3280
  fail3("INVALID_DIFF_PATH", "The project diff request is invalid");
3151
3281
  }
3152
3282
  if (this.#active >= SESSION_GIT_DIFF_CONCURRENCY) fail3("DIFF_BUSY", "Too many project diff requests");
3153
3283
  this.#active++;
3154
- const work = new Work();
3155
- 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(() => {
3156
3286
  this.#active--;
3157
3287
  });
3158
3288
  let timer;
@@ -3225,7 +3355,7 @@ var SessionGitDiffReader = class {
3225
3355
  ...await base(repository, work)
3226
3356
  };
3227
3357
  }
3228
- async #read(sessionId, data, work) {
3358
+ async #read(sessionId, data, work, version) {
3229
3359
  const context = await this.#context(sessionId, data.rootRevision, work);
3230
3360
  if (data.baseCommit !== void 0 && data.baseCommit !== context.baseCommit) changed();
3231
3361
  await work.disableExternalFilters(context.repository);
@@ -3238,7 +3368,23 @@ var SessionGitDiffReader = class {
3238
3368
  } else {
3239
3369
  selected = files.find(({ entry }) => entry.path === data.path);
3240
3370
  if (selected === void 0) changed();
3241
- result = { ...common, path: data.path, ...await patch(context, selected, work, this.scratchDirectory) };
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}`);
3242
3388
  }
3243
3389
  let latest;
3244
3390
  try {
@@ -3257,17 +3403,18 @@ var SessionGitDiffReader = class {
3257
3403
  }
3258
3404
  if (latest.repositoryIdentity !== context.repositoryIdentity || latest.baseRef !== context.baseRef || latest.baseCommit !== context.baseCommit) changed();
3259
3405
  if (selected !== void 0) {
3260
- const current = await readCurrent(
3406
+ const path = selected.entry.path;
3407
+ const current = await forFile(path, work, () => readCurrent(
3261
3408
  context.root,
3262
- selected.entry.path,
3409
+ path,
3263
3410
  context.baseCommit.length === 64 ? "sha256" : "sha1",
3264
3411
  work
3265
- );
3412
+ ));
3266
3413
  if (current?.rawOid !== selected.current?.rawOid || current?.mode !== selected.current?.mode) changed();
3267
3414
  }
3268
3415
  work.check();
3269
3416
  if (Buffer.byteLength(JSON.stringify(result), "utf8") > SESSION_GIT_DIFF_MAX_RESPONSE_BYTES) limit();
3270
- return SessionGitDiffResultDataSchema.parse(result);
3417
+ return (version >= SESSION_GIT_DIFF_PARTIAL_VERSION ? SessionGitDiffPartialResultDataSchema : SessionGitDiffResultDataSchema).parse(result);
3271
3418
  }
3272
3419
  };
3273
3420
 
@@ -3783,16 +3930,16 @@ function createCommunicationOperations(options) {
3783
3930
  ],
3784
3931
  ...options.incrementalOperations,
3785
3932
  ...adapter.getSessionDirectoryMetadata === void 0 ? [] : [
3786
- ...[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({
3787
3934
  operation: SESSION_GIT_DIFF_OPERATION,
3788
3935
  version,
3789
3936
  schema: SessionGitDiffDataSchema,
3790
3937
  handle: async (data, request) => {
3791
3938
  const assertAccess = options.captureRequestAccess();
3792
3939
  assertAccess();
3793
- const result = await gitDiffReader.read(request.sessionId, data);
3940
+ const result = await gitDiffReader.read(request.sessionId, data, version);
3794
3941
  let response = SessionCommunicationDataSchema.parse(result);
3795
- if (version === SESSION_GIT_DIFF_GZIP_VERSION) {
3942
+ if (version >= SESSION_GIT_DIFF_GZIP_VERSION) {
3796
3943
  const bytes = Buffer.from(JSON.stringify(result), "utf8");
3797
3944
  if (bytes.length >= SESSION_GIT_DIFF_GZIP_MIN_BYTES) {
3798
3945
  response = SessionGitDiffGzipResultDataSchema.parse({
package/dist/runtime.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  PEER_CHAT_TIMEOUT_MS,
4
4
  PeerChatRequestSchema,
5
5
  PeerChatResultSchema
6
- } from "./chunks/chunk-M2QZ6EFR.js";
7
- import "./chunks/chunk-7QOBDG6R.js";
6
+ } from "./chunks/chunk-GRXWNM6S.js";
7
+ import "./chunks/chunk-DUMC5F2D.js";
8
8
  export {
9
9
  DataDirectoryLease,
10
10
  PEER_CHAT_TIMEOUT_MS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentknight/sdk",
3
- "version": "0.9.43",
3
+ "version": "0.9.45",
4
4
  "description": "AgentKnight runtime SDK for local and remote coding sessions",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",