@agentknight/sdk 0.9.36 → 0.9.38

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-IXV2BIUR.js";
7
+ } from "./chunks/chunk-4FC2WG64.js";
8
8
  import {
9
9
  AgentSelectionSchema,
10
10
  assertSelectionSnapshotFits,
11
11
  supportsAgentSelection
12
- } from "./chunks/chunk-U2HBI4SH.js";
12
+ } from "./chunks/chunk-6YF3VZLR.js";
13
13
  import {
14
14
  ConversationNormalizer,
15
15
  LiveTimelineProjection,
@@ -27,19 +27,20 @@ import {
27
27
  projectWorkbenchInsights,
28
28
  readWorkbenchMetrics,
29
29
  recordOperationFailure,
30
+ recordTimelineSyncDiagnostic,
30
31
  resolveGitProjectIdentity,
31
32
  sessionRestoreFailure,
32
33
  validateOperationResponse
33
- } from "./chunks/chunk-7JRY2FRZ.js";
34
+ } from "./chunks/chunk-UIM3KQRC.js";
34
35
  import {
35
36
  GROUP_CHAT_MAX_TURNS,
36
37
  GroupChatError
37
- } from "./chunks/chunk-VXZ25DKC.js";
38
+ } from "./chunks/chunk-YSJRQ35E.js";
38
39
  import {
39
40
  assistantPreview,
40
41
  isContentfulAssistant,
41
42
  userPreview
42
- } from "./chunks/chunk-QLGX7333.js";
43
+ } from "./chunks/chunk-34DUD2Y4.js";
43
44
  import {
44
45
  AgentModelListingError,
45
46
  AgentSessionRenameError,
@@ -48,7 +49,7 @@ import {
48
49
  UnsupportedAgentOperationError,
49
50
  captureResearchAccess,
50
51
  captureWorkbenchAccess
51
- } from "./chunks/chunk-UJZPAP64.js";
52
+ } from "./chunks/chunk-6GVMVE5N.js";
52
53
  import {
53
54
  A2aRelayRequestSchema,
54
55
  A2aRelayResponseSchema,
@@ -108,6 +109,13 @@ import {
108
109
  SESSION_FILE_MAX_ORIGINAL_BYTES,
109
110
  SESSION_FILE_READ_OPERATION,
110
111
  SESSION_FILE_READ_VERSION,
112
+ SESSION_GIT_DIFF_GZIP_MIN_BYTES,
113
+ SESSION_GIT_DIFF_GZIP_VERSION,
114
+ SESSION_GIT_DIFF_MAX_FILES,
115
+ SESSION_GIT_DIFF_MAX_PATCH_BYTES,
116
+ SESSION_GIT_DIFF_MAX_RESPONSE_BYTES,
117
+ SESSION_GIT_DIFF_OPERATION,
118
+ SESSION_GIT_DIFF_VERSION,
111
119
  SESSION_LIST_UPDATES_CAPABILITY,
112
120
  SESSION_OPTIONS_PARTIAL_CAPABILITY,
113
121
  SESSION_PROJECT_IDENTITY_OPERATION,
@@ -136,6 +144,9 @@ import {
136
144
  SessionDirectoryListResultDataSchema,
137
145
  SessionFileReadDataSchema,
138
146
  SessionFileReadResultDataSchema,
147
+ SessionGitDiffDataSchema,
148
+ SessionGitDiffGzipResultDataSchema,
149
+ SessionGitDiffResultDataSchema,
139
150
  SessionListRequestSchema,
140
151
  SessionListResultSchema,
141
152
  SessionOptionsRequestSchema,
@@ -197,10 +208,10 @@ import {
197
208
  sessionCommunicationError,
198
209
  sessionCommunicationOperationCapability,
199
210
  workbenchCapability
200
- } from "./chunks/chunk-N2LIXMIM.js";
211
+ } from "./chunks/chunk-GNQC2IS2.js";
201
212
 
202
213
  // src/initialize.ts
203
- import { createHash as createHash13 } from "node:crypto";
214
+ import { createHash as createHash14 } from "node:crypto";
204
215
  import { readFile as readFile4 } from "node:fs/promises";
205
216
  import { z as z10 } from "zod";
206
217
 
@@ -236,9 +247,9 @@ async function accessible(path, executable = false) {
236
247
  throw error;
237
248
  }
238
249
  }
239
- async function hasLocalExecutable(command, environment = process.env) {
240
- const extensions = process.platform === "win32" ? ["", ...(environment.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
241
- const paths = isAbsolute(command) || command.includes("/") || command.includes("\\") ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => join(directory, command));
250
+ async function hasLocalExecutable(command, environment2 = process.env) {
251
+ const extensions = process.platform === "win32" ? ["", ...(environment2.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
252
+ const paths = isAbsolute(command) || command.includes("/") || command.includes("\\") ? [command] : (environment2.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => join(directory, command));
242
253
  for (const path of paths) {
243
254
  for (const extension of extensions) {
244
255
  if (await accessible(`${path}${extension}`, true)) return true;
@@ -275,7 +286,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
275
286
  switch (engine.kind) {
276
287
  case "managed": {
277
288
  if (owner === void 0) throw new Error("Managed coding agents require the PC owner identity");
278
- const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-4W4HC67T.js");
289
+ const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-EMBDKITT.js");
279
290
  return new ManagedCodingAgentAdapter({
280
291
  dataDirectory: config.dataDirectory,
281
292
  owner,
@@ -314,7 +325,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
314
325
  });
315
326
  }
316
327
  case "copilot": {
317
- const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-ULOIFWJ3.js");
328
+ const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-USX4UEWH.js");
318
329
  if (owner !== void 0) {
319
330
  return new GithubCopilotAdapter(void 0, void 0, void 0, {
320
331
  dataDirectory: config.dataDirectory,
@@ -334,13 +345,13 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
334
345
  }
335
346
  case "pi": {
336
347
  assertPiRuntimeSupported();
337
- const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-IFNN3F4N.js");
348
+ const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-KT5M7Z6R.js");
338
349
  return new PiCodingAgentAdapter(
339
350
  engine.agentDirectory === void 0 ? {} : { agentDirectory: resolve(engine.agentDirectory) }
340
351
  );
341
352
  }
342
353
  case "acp": {
343
- const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-QK4MVTNN.js");
354
+ const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-DZ3EE36H.js");
344
355
  return new AcpCodingAgentAdapter({
345
356
  command: engine.command,
346
357
  args: engine.args,
@@ -638,7 +649,7 @@ var ContentCipher = class {
638
649
  openBytes(input) {
639
650
  return this.#open(input, (plaintext) => plaintext);
640
651
  }
641
- #open(input, decode) {
652
+ #open(input, decode2) {
642
653
  const message = SecureMessageSchema.parse(input);
643
654
  if (message.keyId !== this.#key.keyId) {
644
655
  throw new Error("Encrypted message key ID does not match this device");
@@ -670,7 +681,7 @@ var ContentCipher = class {
670
681
  cause: error
671
682
  });
672
683
  }
673
- const payload = decode(plaintext);
684
+ const payload = decode2(plaintext);
674
685
  while (activeReplayEntries.size >= MAX_REPLAY_ENTRIES) {
675
686
  const oldest = activeReplayEntries.keys().next().value;
676
687
  if (oldest === void 0) {
@@ -757,12 +768,12 @@ function buildPairingUri(relayUrl, code, machineId, contentKey) {
757
768
  uri.searchParams.set("key", contentKey);
758
769
  return uri.toString();
759
770
  }
760
- async function requestPairingUri(config, identity, contentKey) {
771
+ async function requestPairingUri(config, identity2, contentKey) {
761
772
  const payload = {
762
773
  userId: config.userId,
763
- machineId: identity.machineId,
764
- stableDeviceId: identity.stableDeviceId,
765
- machineName: identity.machineName,
774
+ machineId: identity2.machineId,
775
+ stableDeviceId: identity2.stableDeviceId,
776
+ machineName: identity2.machineName,
766
777
  keyId: contentKey.keyId
767
778
  };
768
779
  const response = await fetch(
@@ -786,7 +797,7 @@ async function requestPairingUri(config, identity, contentKey) {
786
797
  uri: buildPairingUri(
787
798
  config.relayUrl,
788
799
  result.code,
789
- identity.machineId,
800
+ identity2.machineId,
790
801
  contentKey.encoded
791
802
  ),
792
803
  expiresAt: result.expiresAt
@@ -1263,8 +1274,8 @@ var SessionFileReader = class {
1263
1274
  fail2("FILE_UNAVAILABLE");
1264
1275
  }
1265
1276
  const canonical = await realpath2(current);
1266
- const samePath = canonical === current || resource && process.platform === "win32" && canonical.toLowerCase() === current.toLowerCase();
1267
- if (!contained(root, canonical) || !samePath) fail2("FILE_UNAVAILABLE");
1277
+ const samePath2 = canonical === current || resource && process.platform === "win32" && canonical.toLowerCase() === current.toLowerCase();
1278
+ if (!contained(root, canonical) || !samePath2) fail2("FILE_UNAVAILABLE");
1268
1279
  checkDeadline();
1269
1280
  file = info;
1270
1281
  }
@@ -2190,7 +2201,7 @@ function researchAwareCodingAgentAdapter(inner, research) {
2190
2201
  }
2191
2202
 
2192
2203
  // src/pc-agent.ts
2193
- import { randomUUID as randomUUID8 } from "node:crypto";
2204
+ import { randomUUID as randomUUID9 } from "node:crypto";
2194
2205
  import { platform as platform2 } from "node:os";
2195
2206
  import { performance as performance5 } from "node:perf_hooks";
2196
2207
 
@@ -2214,17 +2225,17 @@ import { io } from "socket.io-client";
2214
2225
 
2215
2226
  // src/service/requests/session-communication.ts
2216
2227
  import { AsyncLocalStorage } from "node:async_hooks";
2217
- import { createHash as createHash5 } from "node:crypto";
2218
- import { join as join6 } from "node:path";
2228
+ import { createHash as createHash6 } from "node:crypto";
2229
+ import { join as join7 } from "node:path";
2219
2230
  import { performance as performance2 } from "node:perf_hooks";
2220
2231
 
2221
2232
  // src/repeated-error-diagnostics.ts
2222
2233
  var RepeatedErrorDiagnostics = class {
2223
- constructor(logger, event, intervalMs = 3e4, limit = 256) {
2234
+ constructor(logger, event, intervalMs = 3e4, limit2 = 256) {
2224
2235
  this.logger = logger;
2225
2236
  this.event = event;
2226
2237
  this.intervalMs = intervalMs;
2227
- this.limit = limit;
2238
+ this.limit = limit2;
2228
2239
  }
2229
2240
  logger;
2230
2241
  event;
@@ -2307,7 +2318,7 @@ var MAX_COMPRESSION_JOBS = 2;
2307
2318
  var activeCompressionJobs = 0;
2308
2319
  var ContentEncoder = class {
2309
2320
  async encodeTimelineResult(plaintext, options = {}) {
2310
- const identity = (reason) => ({
2321
+ const identity2 = (reason) => ({
2311
2322
  bytes: plaintext,
2312
2323
  encoding: "identity",
2313
2324
  reason
@@ -2315,28 +2326,28 @@ var ContentEncoder = class {
2315
2326
  if (plaintext.length > CONTENT_ENCODING_MAX_DECODED_BYTES) {
2316
2327
  throw new Error("Timeline content exceeds the decoded byte budget");
2317
2328
  }
2318
- if (options.enabled !== true) return identity("disabled");
2329
+ if (options.enabled !== true) return identity2("disabled");
2319
2330
  if (!options.acceptContentEncodings?.includes("gzip-v1")) {
2320
- return identity("not-negotiated");
2331
+ return identity2("not-negotiated");
2321
2332
  }
2322
- if (plaintext.length < CONTENT_ENCODING_MIN_INPUT_BYTES) return identity("too-small");
2323
- if (activeCompressionJobs >= MAX_COMPRESSION_JOBS) return identity("busy");
2333
+ if (plaintext.length < CONTENT_ENCODING_MIN_INPUT_BYTES) return identity2("too-small");
2334
+ if (activeCompressionJobs >= MAX_COMPRESSION_JOBS) return identity2("busy");
2324
2335
  activeCompressionJobs += 1;
2325
2336
  try {
2326
- const compressed = await new Promise((resolve4, reject) => {
2337
+ const compressed = await new Promise((resolve5, reject) => {
2327
2338
  gzip2(plaintext, { level: 6 }, (error, result) => {
2328
2339
  if (error !== null) reject(error);
2329
- else resolve4(result);
2340
+ else resolve5(result);
2330
2341
  });
2331
2342
  });
2332
2343
  if (compressed.length > CONTENT_ENCODING_MAX_GZIP_BYTES) {
2333
- return identity("gzip-too-large");
2344
+ return identity2("gzip-too-large");
2334
2345
  }
2335
2346
  if (plaintext.length > compressed.length * CONTENT_ENCODING_MAX_RATIO) {
2336
- return identity("ratio-limit");
2347
+ return identity2("ratio-limit");
2337
2348
  }
2338
2349
  if ((compressed.length + CONTENT_ENCODING_HEADER_BYTES) * 100 > plaintext.length * 85) {
2339
- return identity("insufficient-gain");
2350
+ return identity2("insufficient-gain");
2340
2351
  }
2341
2352
  const header = Buffer.alloc(CONTENT_ENCODING_HEADER_BYTES);
2342
2353
  MAGIC.copy(header);
@@ -2481,6 +2492,611 @@ var SessionPublicationScope = class {
2481
2492
  }
2482
2493
  };
2483
2494
 
2495
+ // src/service/requests/communication-operations.ts
2496
+ import { promisify as promisify2 } from "node:util";
2497
+ import { constants as constants4, gzip as gzip3 } from "node:zlib";
2498
+
2499
+ // src/session-git-diff.ts
2500
+ import { execFile } from "node:child_process";
2501
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
2502
+ import { constants as constants3 } from "node:fs";
2503
+ 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";
2504
+ import { dirname, isAbsolute as isAbsolute5, join as join6, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
2505
+ var SESSION_GIT_DIFF_CONCURRENCY = 4;
2506
+ var SESSION_GIT_DIFF_TIMEOUT_MS = 15e3;
2507
+ var SESSION_GIT_DIFF_MAX_SOURCE_BYTES = 8 * 1024 * 1024;
2508
+ var MAX_SCAN_BYTES = 256 * 1024 * 1024;
2509
+ var MAX_SCAN_FILES = 5e4;
2510
+ var SessionGitDiffError = class extends Error {
2511
+ constructor(code, message, options) {
2512
+ super(message, options);
2513
+ this.code = code;
2514
+ }
2515
+ code;
2516
+ };
2517
+ function fail3(code, message) {
2518
+ throw new SessionGitDiffError(code, message);
2519
+ }
2520
+ function changed() {
2521
+ return fail3("DIFF_CHANGED", "The project diff changed; refresh the changed-file list");
2522
+ }
2523
+ function limit() {
2524
+ return fail3("DIFF_LIMIT_EXCEEDED", "The project diff is too large");
2525
+ }
2526
+ function samePath(a, b) {
2527
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
2528
+ }
2529
+ function safePath(path) {
2530
+ return path !== "" && isSafeSessionDirectoryPath(path) && !path.split("/").some((segment) => segment.toLowerCase() === ".git");
2531
+ }
2532
+ function decode(bytes) {
2533
+ try {
2534
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
2535
+ } catch (error) {
2536
+ throw new SessionGitDiffError("DIFF_UNAVAILABLE", "Git returned invalid text", { cause: error });
2537
+ }
2538
+ }
2539
+ function environment() {
2540
+ const env = { ...process.env, LC_ALL: "C" };
2541
+ for (const key of Object.keys(env)) {
2542
+ if (key.toUpperCase().startsWith("GIT_")) delete env[key];
2543
+ }
2544
+ return {
2545
+ ...env,
2546
+ GIT_OPTIONAL_LOCKS: "0",
2547
+ GIT_NO_LAZY_FETCH: "1",
2548
+ GIT_NO_REPLACE_OBJECTS: "1",
2549
+ GIT_TERMINAL_PROMPT: "0"
2550
+ };
2551
+ }
2552
+ function isExit(error, code) {
2553
+ return error instanceof Error && "code" in error && error.code === code && "stdout" in error && Buffer.isBuffer(error.stdout) && "stderr" in error && Buffer.isBuffer(error.stderr);
2554
+ }
2555
+ var Work = class {
2556
+ deadline = Date.now() + SESSION_GIT_DIFF_TIMEOUT_MS;
2557
+ env = environment();
2558
+ bytes = 0;
2559
+ #filterConfig = [];
2560
+ async disableExternalFilters(repository) {
2561
+ const names = records(await this.git(repository, [
2562
+ "config",
2563
+ "--includes",
2564
+ "--null",
2565
+ "--name-only",
2566
+ "--get-regexp",
2567
+ "^filter\\."
2568
+ ], { allowExitOne: true }));
2569
+ const drivers = /* @__PURE__ */ new Set();
2570
+ for (const name of names) {
2571
+ const match = /^filter\.([^=\r\n]+)\.[^.]+$/u.exec(name);
2572
+ if (!match?.[1]) fail3("DIFF_BLOCKED", "The project contains an unsupported filter configuration");
2573
+ drivers.add(match[1]);
2574
+ }
2575
+ this.#filterConfig = [...drivers].flatMap((name) => [
2576
+ "-c",
2577
+ `filter.${name}.clean=`,
2578
+ "-c",
2579
+ `filter.${name}.process=`,
2580
+ "-c",
2581
+ `filter.${name}.required=false`
2582
+ ]);
2583
+ if (this.#filterConfig.join(" ").length > 16384) limit();
2584
+ }
2585
+ check() {
2586
+ if (Date.now() >= this.deadline) fail3("DIFF_TIMEOUT", "The project diff request timed out");
2587
+ }
2588
+ async git(cwd, args, options = {}) {
2589
+ this.check();
2590
+ try {
2591
+ const stdout = await new Promise((resolve5, reject) => {
2592
+ const child = execFile("git", [
2593
+ "--literal-pathspecs",
2594
+ "-c",
2595
+ "core.fsmonitor=false",
2596
+ "-c",
2597
+ "core.hooksPath=",
2598
+ ...this.#filterConfig,
2599
+ ...args
2600
+ ], {
2601
+ cwd,
2602
+ encoding: "buffer",
2603
+ windowsHide: true,
2604
+ timeout: Math.max(1, this.deadline - Date.now()),
2605
+ maxBuffer: options.maxBuffer ?? SESSION_GIT_DIFF_MAX_SOURCE_BYTES,
2606
+ env: {
2607
+ ...this.env,
2608
+ ...options.objectDirectory === void 0 ? {} : { GIT_OBJECT_DIRECTORY: options.objectDirectory },
2609
+ ...options.isolated ? {
2610
+ GIT_DIR: join6(cwd, "no-repository"),
2611
+ GIT_CONFIG_NOSYSTEM: "1",
2612
+ GIT_ATTR_NOSYSTEM: "1",
2613
+ GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null"
2614
+ } : {}
2615
+ }
2616
+ }, (error, output2, stderr) => {
2617
+ if (error) reject(Object.assign(error, { stdout: output2, stderr }));
2618
+ else resolve5(output2);
2619
+ });
2620
+ child.stdin?.on("error", reject);
2621
+ child.stdin?.end(options.input);
2622
+ });
2623
+ this.check();
2624
+ return stdout;
2625
+ } catch (error) {
2626
+ this.check();
2627
+ if (options.allowExitOne && isExit(error, 1)) return error.stdout;
2628
+ if (error instanceof Error && "code" in error && error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") limit();
2629
+ throw error;
2630
+ }
2631
+ }
2632
+ };
2633
+ function identity(info) {
2634
+ return `${info.dev}\0${info.ino}\0${info.size}\0${info.mtimeNs}\0${info.mode}\0${info.nlink}`;
2635
+ }
2636
+ async function inspect(root, path, work, kind = "file") {
2637
+ if (!safePath(path)) fail3("DIFF_BLOCKED", "The selected path cannot be compared");
2638
+ let current = root;
2639
+ const segments = path.split("/");
2640
+ for (const [index, segment] of segments.entries()) {
2641
+ current = join6(current, segment);
2642
+ let info;
2643
+ try {
2644
+ info = await lstat4(current, { bigint: true });
2645
+ } catch (error) {
2646
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
2647
+ throw error;
2648
+ }
2649
+ work.check();
2650
+ const last = index === segments.length - 1;
2651
+ if (last && kind === "link" && info.isSymbolicLink()) return info;
2652
+ if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
2653
+ fail3("DIFF_BLOCKED", "Links, submodules and special files cannot be compared");
2654
+ }
2655
+ const canonical = await realpath4(current);
2656
+ if (!contained(root, canonical) || !samePath(canonical, current)) {
2657
+ fail3("DIFF_BLOCKED", "The selected path is outside the session");
2658
+ }
2659
+ work.check();
2660
+ if (info.isDirectory() !== (!last || kind === "directory")) return void 0;
2661
+ if (index === segments.length - 1) return info;
2662
+ }
2663
+ return void 0;
2664
+ }
2665
+ async function readCurrent(root, path, algorithm, work, keepBytes = false) {
2666
+ const before = await inspect(root, path, work);
2667
+ if (before === void 0) return void 0;
2668
+ if (before.size > BigInt(MAX_SCAN_BYTES - work.bytes) || keepBytes && before.size > BigInt(SESSION_GIT_DIFF_MAX_SOURCE_BYTES)) limit();
2669
+ work.bytes += Number(before.size);
2670
+ const handle = await open2(
2671
+ join6(root, ...path.split("/")),
2672
+ constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0) | (constants3.O_NONBLOCK ?? 0)
2673
+ );
2674
+ try {
2675
+ const opened = await handle.stat({ bigint: true });
2676
+ if (!opened.isFile() || identity(opened) !== identity(before)) changed();
2677
+ const hash2 = createHash5(algorithm).update(`blob ${before.size}\0`);
2678
+ const buffer = Buffer.alloc(64 * 1024);
2679
+ const chunks = [];
2680
+ let offset = 0;
2681
+ for (; ; ) {
2682
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, offset);
2683
+ work.check();
2684
+ if (bytesRead === 0) break;
2685
+ offset += bytesRead;
2686
+ if (offset > Number(before.size)) changed();
2687
+ hash2.update(buffer.subarray(0, bytesRead));
2688
+ if (keepBytes) chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
2689
+ }
2690
+ const latest = await inspect(root, path, work);
2691
+ if (offset !== Number(before.size) || latest === void 0 || identity(latest) !== identity(before) || identity(await handle.stat({ bigint: true })) !== identity(before)) changed();
2692
+ const rawOid = hash2.digest("hex");
2693
+ return {
2694
+ oid: rawOid,
2695
+ rawOid,
2696
+ identity: identity(before),
2697
+ mode: process.platform !== "win32" && (before.mode & 0o111n) !== 0n ? "100755" : "100644",
2698
+ ...keepBytes ? { bytes: Buffer.concat(chunks) } : {}
2699
+ };
2700
+ } finally {
2701
+ await handle.close();
2702
+ }
2703
+ }
2704
+ async function base(repository, work) {
2705
+ for (const [baseRef, ref] of [
2706
+ ["origin/main", "refs/remotes/origin/main"],
2707
+ ["main", "refs/heads/main"]
2708
+ ]) {
2709
+ const commit = decode(await work.git(repository, [
2710
+ "rev-parse",
2711
+ "--verify",
2712
+ "--quiet",
2713
+ `${ref}^{commit}`
2714
+ ], { allowExitOne: true })).trim();
2715
+ if (!commit) continue;
2716
+ if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(commit)) {
2717
+ fail3("DIFF_BASE_UNAVAILABLE", "The main branch could not be resolved");
2718
+ }
2719
+ const baseCommit = decode(await work.git(repository, ["merge-base", "--all", "HEAD", commit], {
2720
+ allowExitOne: true
2721
+ })).trim();
2722
+ if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(baseCommit)) {
2723
+ fail3("DIFF_BASE_UNAVAILABLE", "The session and main do not have a unique merge base");
2724
+ }
2725
+ return { baseRef, baseCommit };
2726
+ }
2727
+ return fail3("DIFF_MAIN_UNAVAILABLE", "No local origin/main or main reference is available");
2728
+ }
2729
+ function scopedPath(context, path) {
2730
+ const prefix = context.prefix === "" ? "" : `${context.prefix}/`;
2731
+ if (!path.startsWith(prefix)) fail3("DIFF_BLOCKED", "Git returned a path outside the session");
2732
+ const scoped = path.slice(prefix.length);
2733
+ if (!safePath(scoped)) fail3("DIFF_BLOCKED", "The project contains an unsupported path");
2734
+ return scoped;
2735
+ }
2736
+ function records(output2) {
2737
+ const text = decode(output2);
2738
+ if (text === "") return [];
2739
+ if (!text.endsWith("\0")) fail3("DIFF_UNAVAILABLE", "Git returned an incomplete file list");
2740
+ return text.slice(0, -1).split("\0");
2741
+ }
2742
+ async function changes(context, work) {
2743
+ const paths = context.prefix === "" ? [] : [context.prefix];
2744
+ const tree = records(await work.git(context.repository, [
2745
+ "ls-tree",
2746
+ "-r",
2747
+ "-z",
2748
+ "--full-tree",
2749
+ context.baseCommit,
2750
+ "--",
2751
+ ...paths
2752
+ ]));
2753
+ const blobs = /* @__PURE__ */ new Map();
2754
+ const candidates = /* @__PURE__ */ new Set();
2755
+ const tracked = /* @__PURE__ */ new Map();
2756
+ const scanned = /* @__PURE__ */ new Set();
2757
+ const scoped = (path) => {
2758
+ const scoped2 = scopedPath(context, path);
2759
+ scanned.add(scoped2);
2760
+ if (scanned.size > MAX_SCAN_FILES) limit();
2761
+ return scoped2;
2762
+ };
2763
+ for (const record of tree) {
2764
+ const match = /^(\d{6}) (blob|commit) ([a-f0-9]{40}|[a-f0-9]{64})\t(.+)$/u.exec(record);
2765
+ if (!match?.[1] || !match[3] || !match[4]) fail3("DIFF_UNAVAILABLE", "Git returned an invalid tree");
2766
+ blobs.set(scoped(match[4]), { mode: match[1], oid: match[3] });
2767
+ }
2768
+ for (const record of records(await work.git(context.repository, [
2769
+ "ls-files",
2770
+ "--stage",
2771
+ "--full-name",
2772
+ "-z",
2773
+ "--",
2774
+ ...paths
2775
+ ]))) {
2776
+ const match = /^(\d{6}) ([a-f0-9]{40}|[a-f0-9]{64}) [0-3]\t(.+)$/u.exec(record);
2777
+ if (!match?.[1] || !match[2] || !match[3]) fail3("DIFF_UNAVAILABLE", "Git returned an invalid index");
2778
+ tracked.set(scoped(match[3]), { mode: match[1], oid: match[2] });
2779
+ }
2780
+ for (const path of scanned) {
2781
+ const original = blobs.get(path);
2782
+ const indexed = tracked.get(path);
2783
+ if (original?.oid !== indexed?.oid || original?.mode !== indexed?.mode || original !== void 0 && !["100644", "100755"].includes(original.mode)) candidates.add(path);
2784
+ }
2785
+ for (const path of records(await work.git(context.repository, [
2786
+ "ls-files",
2787
+ "--modified",
2788
+ "--deleted",
2789
+ "--full-name",
2790
+ "-z",
2791
+ "--",
2792
+ ...paths
2793
+ ]))) candidates.add(scoped(path));
2794
+ const untracked = [];
2795
+ for (const path of records(await work.git(context.repository, [
2796
+ "ls-files",
2797
+ "--others",
2798
+ "--exclude-standard",
2799
+ "--full-name",
2800
+ "-z",
2801
+ "--",
2802
+ ...paths
2803
+ ]))) {
2804
+ const relativePath = scoped(path);
2805
+ candidates.add(relativePath);
2806
+ if (!blobs.has(relativePath)) untracked.push({ path: relativePath, status: "untracked" });
2807
+ }
2808
+ if (untracked.length > SESSION_GIT_DIFF_MAX_FILES || Buffer.byteLength(JSON.stringify({
2809
+ rootRevision: context.rootRevision,
2810
+ baseRef: context.baseRef,
2811
+ baseCommit: context.baseCommit,
2812
+ files: untracked
2813
+ }), "utf8") > SESSION_GIT_DIFF_MAX_RESPONSE_BYTES) limit();
2814
+ const result = [];
2815
+ const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
2816
+ const currentFiles = /* @__PURE__ */ new Map();
2817
+ for (const path of [...candidates].sort()) {
2818
+ work.check();
2819
+ const original = blobs.get(path);
2820
+ const indexed = tracked.get(path);
2821
+ if ([original?.mode, indexed?.mode].some((mode) => mode !== void 0 && mode !== "100644" && mode !== "100755")) {
2822
+ await unchangedSpecial(context, path, original, indexed, work);
2823
+ candidates.delete(path);
2824
+ continue;
2825
+ }
2826
+ const current = await readCurrent(context.root, path, algorithm, work);
2827
+ if (current !== void 0) currentFiles.set(path, current);
2828
+ }
2829
+ if (currentFiles.size > 0) {
2830
+ const hashes = decode(await work.git(context.repository, ["hash-object", "--stdin-paths"], {
2831
+ input: [...currentFiles.keys()].map((path) => [context.prefix, path].filter(Boolean).join("/")).join("\n") + "\n"
2832
+ })).trim().split("\n");
2833
+ if (hashes.length !== currentFiles.size) changed();
2834
+ for (const [index, [path, current]] of [...currentFiles].entries()) {
2835
+ const oid = hashes[index];
2836
+ if (!oid || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(oid)) changed();
2837
+ const latest = await inspect(context.root, path, work);
2838
+ if (latest === void 0 || identity(latest) !== current.identity) changed();
2839
+ current.oid = oid;
2840
+ }
2841
+ }
2842
+ for (const path of [...candidates].sort()) {
2843
+ const original = blobs.get(path);
2844
+ const current = currentFiles.get(path);
2845
+ if (original === void 0 && current === void 0) continue;
2846
+ if (original?.oid === current?.oid && (process.platform === "win32" || original?.mode === current?.mode)) continue;
2847
+ result.push({
2848
+ entry: { path, status: original === void 0 ? tracked.has(path) ? "added" : "untracked" : current === void 0 ? "deleted" : "modified" },
2849
+ base: original,
2850
+ current
2851
+ });
2852
+ if (result.length > SESSION_GIT_DIFF_MAX_FILES) limit();
2853
+ }
2854
+ return result;
2855
+ }
2856
+ async function unchangedSpecial(context, path, original, indexed, work) {
2857
+ const blocked = () => fail3("DIFF_BLOCKED", "Changed links and submodules cannot be compared");
2858
+ if (original === void 0 || indexed?.mode !== original.mode || indexed.oid !== original.oid) return blocked();
2859
+ if (original.mode === "160000") {
2860
+ if (await inspect(context.root, path, work, "directory") === void 0) blocked();
2861
+ const target = join6(context.root, ...path.split("/"));
2862
+ try {
2863
+ await lstat4(join6(target, ".git"));
2864
+ } catch (error) {
2865
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
2866
+ throw error;
2867
+ }
2868
+ if (decode(await work.git(target, ["rev-parse", "--verify", "HEAD"])).trim() !== original.oid) blocked();
2869
+ } else if (original.mode === "120000") {
2870
+ const info = await inspect(context.root, path, work, "link");
2871
+ if (info === void 0) return blocked();
2872
+ const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
2873
+ let oid;
2874
+ if (info.isSymbolicLink()) {
2875
+ const target = Buffer.from(await readlink(join6(context.root, ...path.split("/"))), "utf8");
2876
+ oid = createHash5(algorithm).update(`blob ${target.length}\0`).update(target).digest("hex");
2877
+ } else {
2878
+ oid = (await readCurrent(context.root, path, algorithm, work))?.rawOid;
2879
+ }
2880
+ if (oid !== original.oid) blocked();
2881
+ } else blocked();
2882
+ }
2883
+ function binary(bytes) {
2884
+ if (bytes.includes(0)) return true;
2885
+ try {
2886
+ new TextDecoder("utf-8", { fatal: true }).decode(bytes);
2887
+ return false;
2888
+ } catch {
2889
+ return true;
2890
+ }
2891
+ }
2892
+ async function patch(context, change, work, scratchDirectory) {
2893
+ const algorithm = context.baseCommit.length === 64 ? "sha256" : "sha1";
2894
+ const current = await readCurrent(context.root, change.entry.path, algorithm, work, true);
2895
+ if (current?.rawOid !== change.current?.rawOid || current?.mode !== change.current?.mode) changed();
2896
+ let original = Buffer.alloc(0);
2897
+ if (change.base !== void 0) {
2898
+ const size = Number(decode(await work.git(context.repository, ["cat-file", "-s", change.base.oid])).trim());
2899
+ if (!Number.isSafeInteger(size) || size < 0 || size > SESSION_GIT_DIFF_MAX_SOURCE_BYTES) limit();
2900
+ original = await work.git(context.repository, ["cat-file", "blob", change.base.oid], { maxBuffer: size + 1 });
2901
+ if (original.length !== size) changed();
2902
+ }
2903
+ const directory = resolve2(scratchDirectory);
2904
+ if (contained(context.repository, directory)) {
2905
+ fail3("DIFF_UNAVAILABLE", "Project diff storage must be outside the session repository");
2906
+ }
2907
+ await mkdir2(directory, { recursive: true, mode: 448 });
2908
+ if ((await lstat4(directory)).isSymbolicLink() || !samePath(await realpath4(directory), directory)) {
2909
+ fail3("DIFF_UNAVAILABLE", "Project diff storage cannot use links");
2910
+ }
2911
+ const scratch = join6(directory, randomUUID3());
2912
+ await mkdir2(scratch, { mode: 448 });
2913
+ let text;
2914
+ try {
2915
+ let bytes = current?.bytes ?? Buffer.alloc(0);
2916
+ if (current !== void 0) {
2917
+ const objectDirectory = join6(scratch, "objects");
2918
+ await mkdir2(objectDirectory);
2919
+ const path = [context.prefix, change.entry.path].filter(Boolean).join("/");
2920
+ const oid = decode(await work.git(context.repository, ["hash-object", "-w", "--stdin", `--path=${path}`], {
2921
+ input: bytes,
2922
+ objectDirectory
2923
+ })).trim();
2924
+ if (oid !== change.current?.oid) changed();
2925
+ bytes = await work.git(context.repository, ["cat-file", "blob", oid], {
2926
+ objectDirectory,
2927
+ maxBuffer: SESSION_GIT_DIFF_MAX_SOURCE_BYTES
2928
+ });
2929
+ }
2930
+ if (binary(original) || binary(bytes)) return { patch: "", binary: true };
2931
+ for (const side of ["a", "b"]) await mkdir2(join6(scratch, side));
2932
+ for (const [side, content, mode] of [
2933
+ ["a", original, change.base?.mode],
2934
+ ["b", bytes, current?.mode]
2935
+ ]) {
2936
+ if (mode === void 0) continue;
2937
+ const file = join6(scratch, side, ...change.entry.path.split("/"));
2938
+ await mkdir2(dirname(file), { recursive: true });
2939
+ await writeFile2(file, content, { flag: "wx", mode: 384 });
2940
+ if (process.platform !== "win32") await chmod(file, mode === "100755" ? 448 : 384);
2941
+ }
2942
+ text = decode(await work.git(scratch, [
2943
+ "-c",
2944
+ "core.quotePath=false",
2945
+ "-c",
2946
+ "core.autocrlf=false",
2947
+ "diff",
2948
+ "--no-index",
2949
+ "--no-prefix",
2950
+ "--no-ext-diff",
2951
+ "--no-textconv",
2952
+ "--no-renames",
2953
+ "--no-color",
2954
+ "--text",
2955
+ "--unified=3",
2956
+ "--",
2957
+ "a",
2958
+ "b"
2959
+ ], { isolated: true, allowExitOne: true, maxBuffer: SESSION_GIT_DIFF_MAX_PATCH_BYTES }));
2960
+ } finally {
2961
+ await rm(scratch, { recursive: true, force: true, maxRetries: 3 });
2962
+ }
2963
+ if (Buffer.byteLength(text, "utf8") > SESSION_GIT_DIFF_MAX_PATCH_BYTES) limit();
2964
+ return { patch: text, binary: false };
2965
+ }
2966
+ var SessionGitDiffReader = class {
2967
+ constructor(metadata, scratchDirectory) {
2968
+ this.metadata = metadata;
2969
+ this.scratchDirectory = scratchDirectory;
2970
+ }
2971
+ metadata;
2972
+ scratchDirectory;
2973
+ #active = 0;
2974
+ async read(sessionId, data) {
2975
+ if (!SessionGitDiffDataSchema.safeParse(data).success || data.path !== void 0 && !safePath(data.path)) {
2976
+ fail3("INVALID_DIFF_PATH", "The project diff request is invalid");
2977
+ }
2978
+ if (this.#active >= SESSION_GIT_DIFF_CONCURRENCY) fail3("DIFF_BUSY", "Too many project diff requests");
2979
+ this.#active++;
2980
+ const work = new Work();
2981
+ const pending = this.#read(sessionId, data, work).finally(() => {
2982
+ this.#active--;
2983
+ });
2984
+ let timer;
2985
+ try {
2986
+ return await Promise.race([
2987
+ pending,
2988
+ new Promise((_, reject) => {
2989
+ timer = setTimeout(() => reject(new SessionGitDiffError(
2990
+ "DIFF_TIMEOUT",
2991
+ "The project diff request timed out"
2992
+ )), SESSION_GIT_DIFF_TIMEOUT_MS);
2993
+ })
2994
+ ]);
2995
+ } catch (error) {
2996
+ if (error instanceof SessionGitDiffError) throw error;
2997
+ throw new SessionGitDiffError("DIFF_UNAVAILABLE", "The project diff is unavailable", { cause: error });
2998
+ } finally {
2999
+ clearTimeout(timer);
3000
+ }
3001
+ }
3002
+ async #context(sessionId, rootRevision, work) {
3003
+ const metadata = await this.metadata(sessionId);
3004
+ work.check();
3005
+ if (metadata === void 0) fail3("SESSION_NOT_FOUND", "The session is unavailable");
3006
+ const workingDirectory = metadata.workingDirectory;
3007
+ if (!workingDirectory || !isAbsolute5(workingDirectory)) {
3008
+ fail3("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
3009
+ }
3010
+ let root;
3011
+ let rootIdentity;
3012
+ try {
3013
+ root = await realpath4(workingDirectory);
3014
+ if ((await lstat4(workingDirectory)).isSymbolicLink() || !samePath(root, resolve2(workingDirectory))) {
3015
+ fail3("DIFF_BLOCKED", "Links cannot be compared");
3016
+ }
3017
+ const info = await stat5(root, { bigint: true });
3018
+ if (!info.isDirectory()) fail3("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable");
3019
+ rootIdentity = sessionRootIdentity(root, info);
3020
+ } catch (error) {
3021
+ if (error instanceof SessionGitDiffError) throw error;
3022
+ throw new SessionGitDiffError("SESSION_ROOT_UNAVAILABLE", "The session working directory is unavailable", { cause: error });
3023
+ }
3024
+ if (createHash5("sha256").update(rootIdentity).digest("base64url") !== rootRevision) {
3025
+ fail3("SESSION_ROOT_CHANGED", "The session directory changed");
3026
+ }
3027
+ let repository;
3028
+ try {
3029
+ const top = decode(await work.git(root, ["rev-parse", "--show-toplevel"])).replace(/\r?\n$/u, "");
3030
+ if (!isAbsolute5(top)) fail3("DIFF_UNAVAILABLE", "Git returned an invalid repository");
3031
+ repository = await realpath4(top);
3032
+ } catch (error) {
3033
+ if (error instanceof SessionGitDiffError) throw error;
3034
+ if (isExit(error, 128) && decode(error.stderr).startsWith("fatal: not a git repository")) {
3035
+ throw new SessionGitDiffError("DIFF_NOT_REPOSITORY", "The session directory is not a Git repository", { cause: error });
3036
+ }
3037
+ throw error;
3038
+ }
3039
+ if (!contained(repository, root)) fail3("DIFF_BLOCKED", "The repository is outside the session root");
3040
+ if (decode(await work.git(root, ["rev-parse", "--show-superproject-working-tree"])).trim() !== "") {
3041
+ fail3("DIFF_BLOCKED", "Submodule content cannot be compared");
3042
+ }
3043
+ return {
3044
+ root,
3045
+ rootRevision,
3046
+ rootIdentity,
3047
+ workingDirectory,
3048
+ repository,
3049
+ repositoryIdentity: sessionRootIdentity(repository, await stat5(repository, { bigint: true })),
3050
+ prefix: relative2(repository, root).split(sep2).join("/"),
3051
+ ...await base(repository, work)
3052
+ };
3053
+ }
3054
+ async #read(sessionId, data, work) {
3055
+ const context = await this.#context(sessionId, data.rootRevision, work);
3056
+ if (data.baseCommit !== void 0 && data.baseCommit !== context.baseCommit) changed();
3057
+ await work.disableExternalFilters(context.repository);
3058
+ const files = await changes(context, work);
3059
+ const common = { rootRevision: data.rootRevision, baseRef: context.baseRef, baseCommit: context.baseCommit };
3060
+ let result;
3061
+ let selected;
3062
+ if (data.path === void 0) {
3063
+ result = { ...common, files: files.map(({ entry }) => entry) };
3064
+ } else {
3065
+ selected = files.find(({ entry }) => entry.path === data.path);
3066
+ if (selected === void 0) changed();
3067
+ result = { ...common, path: data.path, ...await patch(context, selected, work, this.scratchDirectory) };
3068
+ }
3069
+ let latest;
3070
+ try {
3071
+ latest = await this.#context(sessionId, data.rootRevision, work);
3072
+ } catch (error) {
3073
+ if (error instanceof SessionGitDiffError) {
3074
+ if (["SESSION_NOT_FOUND", "SESSION_ROOT_UNAVAILABLE"].includes(error.code)) {
3075
+ throw new SessionGitDiffError("SESSION_ROOT_CHANGED", "The session directory changed", { cause: error });
3076
+ }
3077
+ if (["DIFF_MAIN_UNAVAILABLE", "DIFF_BASE_UNAVAILABLE", "DIFF_NOT_REPOSITORY"].includes(error.code)) changed();
3078
+ }
3079
+ throw error;
3080
+ }
3081
+ if (latest.workingDirectory !== context.workingDirectory || latest.rootIdentity !== context.rootIdentity) {
3082
+ fail3("SESSION_ROOT_CHANGED", "The session directory changed");
3083
+ }
3084
+ if (latest.repositoryIdentity !== context.repositoryIdentity || latest.baseRef !== context.baseRef || latest.baseCommit !== context.baseCommit) changed();
3085
+ if (selected !== void 0) {
3086
+ const current = await readCurrent(
3087
+ context.root,
3088
+ selected.entry.path,
3089
+ context.baseCommit.length === 64 ? "sha256" : "sha1",
3090
+ work
3091
+ );
3092
+ if (current?.rawOid !== selected.current?.rawOid || current?.mode !== selected.current?.mode) changed();
3093
+ }
3094
+ work.check();
3095
+ if (Buffer.byteLength(JSON.stringify(result), "utf8") > SESSION_GIT_DIFF_MAX_RESPONSE_BYTES) limit();
3096
+ return SessionGitDiffResultDataSchema.parse(result);
3097
+ }
3098
+ };
3099
+
2484
3100
  // src/service/requests/request-errors.ts
2485
3101
  function operationErrorCode(agentId, operation, error) {
2486
3102
  if (isSessionRestoreFailure(error)) return "SESSION_RESTORE_FAILED";
@@ -2495,10 +3111,12 @@ function unsupportedOperation(error) {
2495
3111
  }
2496
3112
 
2497
3113
  // src/service/requests/communication-operations.ts
3114
+ var compress2 = promisify2(gzip3);
2498
3115
  function createCommunicationOperations(options) {
2499
3116
  const { adapter, publications } = options;
2500
3117
  const directoryLister = new SessionDirectoryLister(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId));
2501
3118
  const fileReader = new SessionFileReader(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId));
3119
+ const gitDiffReader = new SessionGitDiffReader(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId), options.gitDiffDirectory);
2502
3120
  const getSessionProjectIdentity = adapter.getSessionProjectIdentity?.bind(adapter);
2503
3121
  const createGroupChat = adapter.createGroupChat?.bind(adapter);
2504
3122
  const renameSession = adapter.renameSession?.bind(adapter);
@@ -2546,6 +3164,30 @@ function createCommunicationOperations(options) {
2546
3164
  ],
2547
3165
  ...options.incrementalOperations,
2548
3166
  ...adapter.getSessionDirectoryMetadata === void 0 ? [] : [
3167
+ ...[SESSION_GIT_DIFF_VERSION, SESSION_GIT_DIFF_GZIP_VERSION].map((version) => sessionCommunicationOperation({
3168
+ operation: SESSION_GIT_DIFF_OPERATION,
3169
+ version,
3170
+ schema: SessionGitDiffDataSchema,
3171
+ handle: async (data, request) => {
3172
+ const assertAccess = options.captureRequestAccess();
3173
+ assertAccess();
3174
+ const result = await gitDiffReader.read(request.sessionId, data);
3175
+ let response = SessionCommunicationDataSchema.parse(result);
3176
+ if (version === SESSION_GIT_DIFF_GZIP_VERSION) {
3177
+ const bytes = Buffer.from(JSON.stringify(result), "utf8");
3178
+ if (bytes.length >= SESSION_GIT_DIFF_GZIP_MIN_BYTES) {
3179
+ response = SessionGitDiffGzipResultDataSchema.parse({
3180
+ encoding: "gzip",
3181
+ byteLength: bytes.length,
3182
+ data: (await compress2(bytes, { level: constants4.Z_BEST_SPEED })).toString("base64")
3183
+ });
3184
+ }
3185
+ }
3186
+ assertAccess();
3187
+ return response;
3188
+ },
3189
+ onError: (error) => unsupportedOperation(error) ?? (error instanceof SessionGitDiffError || error instanceof SessionNotSharedError ? { code: error.code, message: error.message } : { code: "DIFF_UNAVAILABLE", message: "The project diff is unavailable" })
3190
+ })),
2549
3191
  sessionCommunicationOperation({
2550
3192
  operation: SESSION_DIRECTORY_LIST_OPERATION,
2551
3193
  version: SESSION_DIRECTORY_LIST_VERSION,
@@ -2612,13 +3254,14 @@ var SessionCommunicationHandler = class {
2612
3254
  const workbench = adapter.agentId === "copilot" || adapter.agentId === "managed" ? adapter.workbench : void 0;
2613
3255
  const researchWorkbench = workbench === void 0 ? void 0 : research;
2614
3256
  this.#operations = workbench === void 0 && researchWorkbench === void 0 ? void 0 : new SessionOperations({
2615
- directory: join6(config.dataDirectory, "workbench"),
3257
+ directory: join7(config.dataDirectory, "workbench"),
2616
3258
  agentRunId,
2617
3259
  namespace: `${config.userId}\0${machineId}`,
2618
3260
  onError: operationError
2619
3261
  });
2620
3262
  this.#dispatcher = new SessionCommunicationDispatcher(createCommunicationOperations({
2621
3263
  adapter,
3264
+ gitDiffDirectory: join7(config.dataDirectory, "git-diff"),
2622
3265
  publications: options.publications,
2623
3266
  captureRequestAccess: () => {
2624
3267
  const assertCurrent = this.#requestContext.getStore()?.assertRequestCurrent;
@@ -2653,6 +3296,8 @@ var SessionCommunicationHandler = class {
2653
3296
  #operations;
2654
3297
  #dispatcher;
2655
3298
  #errors;
3299
+ #diagnosticTimers = /* @__PURE__ */ new Set();
3300
+ #closed = false;
2656
3301
  get capabilities() {
2657
3302
  const schedules = this.options.adapter.workbench?.schedules;
2658
3303
  const schedulesAvailable = schedules !== void 0 && schedules.isAvailable?.() !== false;
@@ -2669,6 +3314,9 @@ var SessionCommunicationHandler = class {
2669
3314
  await this.#operations?.stopAccepting();
2670
3315
  }
2671
3316
  async close() {
3317
+ this.#closed = true;
3318
+ for (const timer of this.#diagnosticTimers) clearTimeout(timer);
3319
+ this.#diagnosticTimers.clear();
2672
3320
  await this.#operations?.close();
2673
3321
  this.#researchParents.clear();
2674
3322
  this.#requestContext.disable();
@@ -2690,11 +3338,106 @@ var SessionCommunicationHandler = class {
2690
3338
  return visible;
2691
3339
  }
2692
3340
  async handle(request, scope) {
2693
- const { adapter, config, machineId, research, publications, audience, emit, logger, metrics } = this.options;
2694
- if (!this.options.enabled()) return;
3341
+ if (request.operation !== TIMELINE_SYNC_OPERATION) return this.#handle(request, scope);
3342
+ const started = performance2.now();
3343
+ const parsed = TimelineSyncRequestSchema.safeParse(request.data);
3344
+ const mode = parsed.success && parsed.data.watermark !== void 0 ? parsed.data.resume === true ? "resume" : "replay" : "snapshot";
3345
+ const diagnostics = new OperationDiagnostics();
3346
+ const lifecycle = {
3347
+ preflightPhase: "arrival",
3348
+ lifecyclePhase: "preflight",
3349
+ outcome: "dropped",
3350
+ emitOutcome: "not-attempted"
3351
+ };
3352
+ const fields = () => ({
3353
+ ...diagnostics.fields(request),
3354
+ mode,
3355
+ lifecycleMs: Math.round(performance2.now() - started),
3356
+ preflightMs: Math.round((lifecycle.preflightEnded ?? performance2.now()) - started),
3357
+ preflightPhase: lifecycle.preflightPhase,
3358
+ lifecyclePhase: lifecycle.lifecyclePhase
3359
+ });
3360
+ let pendingLogged = false;
3361
+ const pendingAfterMs = 5e3 + (parsed.success ? parsed.data.waitMs ?? 0 : 0);
3362
+ const timer = this.#closed ? void 0 : setTimeout(() => {
3363
+ if (timer !== void 0) this.#diagnosticTimers.delete(timer);
3364
+ pendingLogged = true;
3365
+ this.options.logger.info({ ...fields(), pendingAfterMs }, "session.communication.timeline-sync-pending");
3366
+ }, pendingAfterMs);
3367
+ if (timer !== void 0) {
3368
+ timer.unref();
3369
+ this.#diagnosticTimers.add(timer);
3370
+ }
3371
+ try {
3372
+ await diagnostics.run(async () => {
3373
+ diagnostics.recordTimelineSync({ mode });
3374
+ if (mode !== "replay") {
3375
+ this.options.logger.info(fields(), "session.communication.timeline-sync-started");
3376
+ }
3377
+ await this.#handle(request, scope, diagnostics, lifecycle);
3378
+ }, { measureHandle: false });
3379
+ } catch (error) {
3380
+ lifecycle.outcome = "failure";
3381
+ lifecycle.reason ??= "exception";
3382
+ diagnostics.recordFailure(error, lifecycle.lifecyclePhase === "preflight" ? "preflight" : lifecycle.lifecyclePhase === "emit" ? "emit" : "handle");
3383
+ throw error;
3384
+ } finally {
3385
+ if (timer !== void 0) {
3386
+ clearTimeout(timer);
3387
+ this.#diagnosticTimers.delete(timer);
3388
+ }
3389
+ const details = {
3390
+ ...fields(),
3391
+ outcome: lifecycle.outcome,
3392
+ emitOutcome: lifecycle.emitOutcome,
3393
+ ...lifecycle.reason === void 0 ? {} : { reason: lifecycle.reason }
3394
+ };
3395
+ if (mode !== "replay" || pendingLogged || lifecycle.outcome !== "success" || lifecycle.responseStatus === "resync" || diagnostics.activeMs >= 1e3) {
3396
+ this.options.logger.info(details, "session.communication.timeline-sync-completed");
3397
+ } else {
3398
+ this.options.logger.debug(details, "session.communication.timeline-sync-completed");
3399
+ }
3400
+ }
3401
+ }
3402
+ async #handle(request, scope, diagnostics = new OperationDiagnostics(), lifecycle) {
3403
+ const { adapter, config, machineId, research, publications, audience, logger, metrics } = this.options;
3404
+ const reject = (reason) => {
3405
+ if (lifecycle !== void 0) {
3406
+ lifecycle.outcome = "failure";
3407
+ lifecycle.reason = reason;
3408
+ }
3409
+ };
3410
+ const emit = (_event, response2) => {
3411
+ if (lifecycle !== void 0) {
3412
+ lifecycle.preflightEnded ??= performance2.now();
3413
+ lifecycle.lifecyclePhase = "emit";
3414
+ lifecycle.outcome = response2.kind === "error" ? "failure" : "success";
3415
+ if (response2.kind === "error") lifecycle.reason ??= "operation-error";
3416
+ if (response2.kind === "response") {
3417
+ const status = response2.data["status"];
3418
+ if (status === "snapshot" || status === "replay" || status === "resync") {
3419
+ lifecycle.responseStatus = status;
3420
+ diagnostics.recordTimelineSync({ status });
3421
+ }
3422
+ }
3423
+ }
3424
+ try {
3425
+ this.options.emit("session.communication", response2);
3426
+ if (lifecycle !== void 0) lifecycle.emitOutcome = "local-enqueued";
3427
+ } catch (error) {
3428
+ if (lifecycle !== void 0) lifecycle.emitOutcome = "threw";
3429
+ reject(error instanceof SecureMessageBudgetError ? "transport-budget" : "emit-failed");
3430
+ diagnostics.recordFailure(error, "emit");
3431
+ throw error;
3432
+ }
3433
+ };
3434
+ if (!this.options.enabled()) {
3435
+ if (lifecycle !== void 0) lifecycle.reason = "disabled";
3436
+ return;
3437
+ }
2695
3438
  const clientId = scope.clientId;
2696
3439
  const owner = [config.userId, machineId, clientId, request.sessionId];
2697
- const key = (parts) => createHash5("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 24);
3440
+ const key = (parts) => createHash6("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 24);
2698
3441
  const knownSource = this.#researchParents.get(request.sessionId) ?? adapter.sessionPublicationSource?.(request.sessionId) ?? request.sessionId;
2699
3442
  const arrivalPublication = adapter.isSessionVisible?.(knownSource) === true ? publications.capture(knownSource) : void 0;
2700
3443
  let researchParent = this.#researchParents.get(request.sessionId);
@@ -2706,9 +3449,12 @@ var SessionCommunicationHandler = class {
2706
3449
  return;
2707
3450
  }
2708
3451
  if (research !== void 0) {
3452
+ if (lifecycle !== void 0) lifecycle.preflightPhase = "ownership";
2709
3453
  try {
2710
3454
  researchParent = await research.parentSessionId(request.sessionId) ?? researchParent;
2711
3455
  } catch (error) {
3456
+ reject("ownership-unavailable");
3457
+ if (lifecycle !== void 0) diagnostics.recordFailure(error, "preflight");
2712
3458
  logger.error({
2713
3459
  stage: "research.ownership",
2714
3460
  ownerKey: key(owner),
@@ -2723,8 +3469,10 @@ var SessionCommunicationHandler = class {
2723
3469
  }
2724
3470
  }
2725
3471
  if (researchParent !== void 0) {
3472
+ if (lifecycle !== void 0) lifecycle.preflightPhase = "isolation";
2726
3473
  const unifiedSync = research?.chat !== void 0 && this.options.supportsResearchChat?.(clientId) === true && (request.operation === TIMELINE_SYNC_OPERATION || request.operation === TIMELINE_IMAGE_OPERATION);
2727
3474
  if (!unifiedSync && !["research.timeline", "research.send", "research.abort", "research.delete", "operation.get", "insights.read"].includes(request.operation)) {
3475
+ reject("research-isolated");
2728
3476
  emit("session.communication", sessionCommunicationError(request, {
2729
3477
  code: "RESEARCH_SESSION_ISOLATED",
2730
3478
  message: "Research sessions require the isolated research operations"
@@ -2738,6 +3486,7 @@ var SessionCommunicationHandler = class {
2738
3486
  }
2739
3487
  }
2740
3488
  const publicationSessionId = researchParent ?? request.sessionId;
3489
+ if (lifecycle !== void 0) lifecycle.preflightPhase = "access";
2741
3490
  let publication;
2742
3491
  let researchAccess;
2743
3492
  const publicationCurrent = () => {
@@ -2745,6 +3494,7 @@ var SessionCommunicationHandler = class {
2745
3494
  researchAccess?.assertCurrent();
2746
3495
  return researchAccess?.detached === true || publications.isCurrent(publicationSessionId, publication);
2747
3496
  } catch {
3497
+ reject("not-shared");
2748
3498
  return false;
2749
3499
  }
2750
3500
  };
@@ -2757,21 +3507,28 @@ var SessionCommunicationHandler = class {
2757
3507
  }
2758
3508
  if (!publicationCurrent()) throw new SessionNotSharedError();
2759
3509
  } catch {
3510
+ reject("not-shared");
2760
3511
  const error = new SessionNotSharedError();
2761
3512
  emit("session.communication", sessionCommunicationError(request, { code: error.code, message: error.message }));
2762
3513
  return;
2763
3514
  }
2764
3515
  const token = clientId === void 0 || request.operation !== TIMELINE_SYNC_OPERATION ? void 0 : audience.syncToken(clientId, request.sessionId);
2765
3516
  if (request.operation === TIMELINE_SYNC_OPERATION && !audience.confirmed) {
2766
- const { data: _data, kind: _kind, ...identity } = request;
3517
+ if (lifecycle !== void 0) lifecycle.preflightPhase = "audience";
3518
+ reject("audience-unconfirmed");
3519
+ const { data: _data, kind: _kind, ...identity2 } = request;
2767
3520
  emit("session.communication", {
2768
- ...identity,
3521
+ ...identity2,
2769
3522
  kind: "error",
2770
3523
  error: { code: "TIMELINE_SYNC_UNSUPPORTED", message: "Relay audience replay is not confirmed" }
2771
3524
  });
2772
3525
  return;
2773
3526
  }
2774
- const diagnostics = new OperationDiagnostics();
3527
+ if (lifecycle !== void 0) {
3528
+ lifecycle.preflightEnded = performance2.now();
3529
+ lifecycle.preflightPhase = "complete";
3530
+ lifecycle.lifecyclePhase = "dispatch";
3531
+ }
2775
3532
  const response = await diagnostics.run(() => clientId === void 0 ? this.#dispatcher.dispatch(request) : this.#requestContext.run({
2776
3533
  clientId,
2777
3534
  researchSession: researchParent !== void 0,
@@ -2800,8 +3557,16 @@ var SessionCommunicationHandler = class {
2800
3557
  ...typeof request.data["operationId"] !== "string" ? {} : { operationKey: key([...owner, request.data["operationId"]]) }
2801
3558
  }
2802
3559
  }, () => this.#dispatcher.dispatch(request)));
2803
- if (!scope.isCurrent()) return;
3560
+ if (lifecycle !== void 0) lifecycle.lifecyclePhase = "reply-check";
3561
+ if (!scope.isCurrent()) {
3562
+ if (lifecycle !== void 0) {
3563
+ lifecycle.outcome = "dropped";
3564
+ lifecycle.reason = "stale-scope";
3565
+ }
3566
+ return;
3567
+ }
2804
3568
  if (!publicationCurrent()) {
3569
+ reject("not-shared");
2805
3570
  const error = new SessionNotSharedError();
2806
3571
  emit("session.communication", sessionCommunicationError(request, { code: error.code, message: error.message }));
2807
3572
  return;
@@ -2852,23 +3617,23 @@ var SessionCommunicationHandler = class {
2852
3617
  };
2853
3618
 
2854
3619
  // src/desktop-workspaces.ts
2855
- import { createHash as createHash6 } from "node:crypto";
2856
- import { readdir, realpath as realpath4, stat as stat5 } from "node:fs/promises";
3620
+ import { createHash as createHash7 } from "node:crypto";
3621
+ import { readdir, realpath as realpath5, stat as stat6 } from "node:fs/promises";
2857
3622
  import { homedir as homedir2 } from "node:os";
2858
- import { basename as basename2, join as join7, normalize } from "node:path";
2859
- function desktopRootCandidates(environment = process.env) {
3623
+ import { basename as basename2, join as join8, normalize } from "node:path";
3624
+ function desktopRootCandidates(environment2 = process.env) {
2860
3625
  const candidates = [
2861
- environment.AGENTKNIGHT_WORKSPACE_ROOT,
2862
- environment.OneDrive === void 0 ? void 0 : join7(environment.OneDrive, "Desktop"),
2863
- environment.OneDriveCommercial === void 0 ? void 0 : join7(environment.OneDriveCommercial, "Desktop"),
2864
- environment.OneDriveConsumer === void 0 ? void 0 : join7(environment.OneDriveConsumer, "Desktop"),
2865
- join7(environment.USERPROFILE ?? homedir2(), "Desktop")
3626
+ environment2.AGENTKNIGHT_WORKSPACE_ROOT,
3627
+ environment2.OneDrive === void 0 ? void 0 : join8(environment2.OneDrive, "Desktop"),
3628
+ environment2.OneDriveCommercial === void 0 ? void 0 : join8(environment2.OneDriveCommercial, "Desktop"),
3629
+ environment2.OneDriveConsumer === void 0 ? void 0 : join8(environment2.OneDriveConsumer, "Desktop"),
3630
+ join8(environment2.USERPROFILE ?? homedir2(), "Desktop")
2866
3631
  ];
2867
3632
  return [...new Set(candidates.filter((path) => path !== void 0))];
2868
3633
  }
2869
3634
  function workspaceId(path) {
2870
3635
  const normalizedPath = workspacePathKey(path);
2871
- return createHash6("sha256").update(normalizedPath).digest("base64url").slice(0, 32);
3636
+ return createHash7("sha256").update(normalizedPath).digest("base64url").slice(0, 32);
2872
3637
  }
2873
3638
  function workspacePathKey(path) {
2874
3639
  const normalizedPath = normalize(path);
@@ -2880,8 +3645,8 @@ function isMissingPathError(error) {
2880
3645
  async function findDesktopRoot(candidates) {
2881
3646
  for (const candidate of candidates) {
2882
3647
  try {
2883
- const resolved = await realpath4(candidate);
2884
- if ((await stat5(resolved)).isDirectory()) {
3648
+ const resolved = await realpath5(candidate);
3649
+ if ((await stat6(resolved)).isDirectory()) {
2885
3650
  return resolved;
2886
3651
  }
2887
3652
  } catch (error) {
@@ -2903,7 +3668,7 @@ async function listDesktopWorkspaces(rootOverride) {
2903
3668
  const workspaces = entries.filter(
2904
3669
  (entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name.trim().length > 0
2905
3670
  ).map((entry) => {
2906
- const path = join7(root, entry.name);
3671
+ const path = join8(root, entry.name);
2907
3672
  return {
2908
3673
  id: workspaceId(path),
2909
3674
  name: entry.name,
@@ -2923,8 +3688,8 @@ async function resolveSessionWorkspace(path) {
2923
3688
  return void 0;
2924
3689
  }
2925
3690
  try {
2926
- const resolved = await realpath4(candidate);
2927
- if (!(await stat5(resolved)).isDirectory()) {
3691
+ const resolved = await realpath5(candidate);
3692
+ if (!(await stat6(resolved)).isDirectory()) {
2928
3693
  return void 0;
2929
3694
  }
2930
3695
  return {
@@ -2971,13 +3736,13 @@ async function resolveSelectableWorkspace(id, sessionWorkingDirectories, rootOve
2971
3736
  }
2972
3737
 
2973
3738
  // src/git-session-worktree.ts
2974
- import { execFile } from "node:child_process";
2975
- import { randomUUID as randomUUID3 } from "node:crypto";
2976
- import { lstat as lstat4, mkdir as mkdir2, realpath as realpath5, rmdir } from "node:fs/promises";
3739
+ import { execFile as execFile2 } from "node:child_process";
3740
+ import { randomUUID as randomUUID4 } from "node:crypto";
3741
+ import { lstat as lstat5, mkdir as mkdir3, realpath as realpath6, rmdir } from "node:fs/promises";
2977
3742
  import { devNull } from "node:os";
2978
- import { dirname, isAbsolute as isAbsolute5, join as join8, relative as relative2 } from "node:path";
2979
- import { promisify as promisify2 } from "node:util";
2980
- var execFileAsync = promisify2(execFile);
3743
+ import { dirname as dirname2, isAbsolute as isAbsolute6, join as join9, relative as relative3 } from "node:path";
3744
+ import { promisify as promisify3 } from "node:util";
3745
+ var execFileAsync = promisify3(execFile2);
2981
3746
  function creationEnvironment() {
2982
3747
  const env = Object.fromEntries(
2983
3748
  Object.entries(process.env).filter(([key]) => !key.toUpperCase().startsWith("GIT_"))
@@ -2985,8 +3750,8 @@ function creationEnvironment() {
2985
3750
  return { ...env, LC_ALL: "C" };
2986
3751
  }
2987
3752
  function inside(root, path) {
2988
- const difference = relative2(root, path);
2989
- return difference === "" || !isAbsolute5(difference) && difference !== ".." && !difference.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
3753
+ const difference = relative3(root, path);
3754
+ return difference === "" || !isAbsolute6(difference) && difference !== ".." && !difference.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
2990
3755
  }
2991
3756
  async function git(env, ...args) {
2992
3757
  const { stdout } = await execFileAsync("git", [
@@ -3002,11 +3767,11 @@ async function git(env, ...args) {
3002
3767
  }
3003
3768
  async function listSessionWorktreeBranches(sourceDirectory) {
3004
3769
  const env = creationEnvironment();
3005
- const identity = await resolveGitProjectIdentity(sourceDirectory, env);
3770
+ const identity2 = await resolveGitProjectIdentity(sourceDirectory, env);
3006
3771
  const output2 = await git(
3007
3772
  env,
3008
3773
  "-C",
3009
- identity.worktreeRoot,
3774
+ identity2.worktreeRoot,
3010
3775
  "for-each-ref",
3011
3776
  "--format=%(refname)",
3012
3777
  "--sort=refname",
@@ -3020,7 +3785,7 @@ async function listSessionWorktreeBranches(sourceDirectory) {
3020
3785
  const branches = refs.map((ref) => ref.slice(prefix.length));
3021
3786
  let currentRef;
3022
3787
  try {
3023
- currentRef = await git(env, "-C", identity.worktreeRoot, "symbolic-ref", "--quiet", "HEAD");
3788
+ currentRef = await git(env, "-C", identity2.worktreeRoot, "symbolic-ref", "--quiet", "HEAD");
3024
3789
  } catch (error) {
3025
3790
  if (!(error instanceof Error && "code" in error && error.code === 1)) throw error;
3026
3791
  }
@@ -3029,7 +3794,7 @@ async function listSessionWorktreeBranches(sourceDirectory) {
3029
3794
  }
3030
3795
  async function createSessionWorktree(sourceDirectory, requestedBranchName, requestedBaseBranch) {
3031
3796
  const env = creationEnvironment();
3032
- const identity = await resolveGitProjectIdentity(sourceDirectory, env);
3797
+ const identity2 = await resolveGitProjectIdentity(sourceDirectory, env);
3033
3798
  const baseBranch = requestedBaseBranch?.trim();
3034
3799
  const baseRef = baseBranch === void 0 ? "HEAD" : `refs/heads/${baseBranch}`;
3035
3800
  if (baseBranch !== void 0) {
@@ -3042,7 +3807,7 @@ async function createSessionWorktree(sourceDirectory, requestedBranchName, reque
3042
3807
  }
3043
3808
  let head;
3044
3809
  try {
3045
- head = await git(env, "-C", identity.worktreeRoot, "rev-parse", "--verify", `${baseRef}^{commit}`);
3810
+ head = await git(env, "-C", identity2.worktreeRoot, "rev-parse", "--verify", `${baseRef}^{commit}`);
3046
3811
  if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(head)) throw new Error("Git returned an invalid commit ID");
3047
3812
  } catch (cause) {
3048
3813
  throw new Error(
@@ -3050,10 +3815,10 @@ async function createSessionWorktree(sourceDirectory, requestedBranchName, reque
3050
3815
  { cause }
3051
3816
  );
3052
3817
  }
3053
- const suffix = randomUUID3();
3818
+ const suffix = randomUUID4();
3054
3819
  const branchName = requestedBranchName?.trim() ?? `agentknight/${suffix}`;
3055
3820
  try {
3056
- if (branchName.length === 0 || branchName.length > 120 || await git(env, "-C", identity.worktreeRoot, "check-ref-format", "--branch", branchName) !== branchName) {
3821
+ if (branchName.length === 0 || branchName.length > 120 || await git(env, "-C", identity2.worktreeRoot, "check-ref-format", "--branch", branchName) !== branchName) {
3057
3822
  throw new Error("Invalid branch name");
3058
3823
  }
3059
3824
  } catch (cause) {
@@ -3061,29 +3826,29 @@ async function createSessionWorktree(sourceDirectory, requestedBranchName, reque
3061
3826
  }
3062
3827
  let exists = true;
3063
3828
  try {
3064
- await git(env, "-C", identity.worktreeRoot, "show-ref", "--verify", "--quiet", `refs/heads/${branchName}`);
3829
+ await git(env, "-C", identity2.worktreeRoot, "show-ref", "--verify", "--quiet", `refs/heads/${branchName}`);
3065
3830
  } catch (error) {
3066
3831
  if (!(error instanceof Error && "code" in error && error.code === 1)) throw error;
3067
3832
  exists = false;
3068
3833
  }
3069
3834
  if (exists) throw new Error(`Git branch "${branchName}" already exists. Choose a new branch name.`);
3070
- const primary = await realpath5(identity.mainWorktreeRoot ?? identity.worktreeRoot);
3071
- const parent = dirname(primary);
3072
- if (inside(identity.worktreeRoot, parent)) {
3835
+ const primary = await realpath6(identity2.mainWorktreeRoot ?? identity2.worktreeRoot);
3836
+ const parent = dirname2(primary);
3837
+ if (inside(identity2.worktreeRoot, parent)) {
3073
3838
  throw new Error("Cannot create a sibling worktree outside the source checkout. Move the repository and retry.");
3074
3839
  }
3075
- const path = join8(parent, `agentknight-worktree-${suffix}`);
3840
+ const path = join9(parent, `agentknight-worktree-${suffix}`);
3076
3841
  if (path.length > 640) {
3077
3842
  throw new Error("The repository path is too long to report worktree recovery safely. Use a checkout closer to the filesystem root.");
3078
3843
  }
3079
- await mkdir2(path, { mode: 448 });
3080
- const reserved = await lstat4(path);
3844
+ await mkdir3(path, { mode: 448 });
3845
+ const reserved = await lstat5(path);
3081
3846
  try {
3082
- await git(env, "-C", identity.worktreeRoot, "worktree", "add", "-b", branchName, "--", path, head);
3847
+ await git(env, "-C", identity2.worktreeRoot, "worktree", "add", "-b", branchName, "--", path, head);
3083
3848
  } catch (cause) {
3084
3849
  let cleanupError;
3085
3850
  try {
3086
- const current = await lstat4(path);
3851
+ const current = await lstat5(path);
3087
3852
  if (current.dev === reserved.dev && current.ino === reserved.ino) await rmdir(path);
3088
3853
  } catch (error) {
3089
3854
  if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) cleanupError = error;
@@ -3129,7 +3894,7 @@ var SessionOptionsHandler = class {
3129
3894
  const sessionWorkingDirectories = request.sessionId === void 0 ? adapter.listSessions(config.userId, machineId).then((sessions) => sessions.flatMap((session) => session.workingDirectory === void 0 ? [] : [session.workingDirectory])) : Promise.resolve([]);
3130
3895
  const environmentDiagnostics = new OperationDiagnostics();
3131
3896
  const environmentSessionId = request.sessionId;
3132
- const sessionEnvironment = environmentSessionId === void 0 ? Promise.resolve(void 0) : environmentDiagnostics.run(() => adapter.getSessionEnvironment(environmentSessionId)).then((environment2) => {
3897
+ const sessionEnvironment = environmentSessionId === void 0 ? Promise.resolve(void 0) : environmentDiagnostics.run(() => adapter.getSessionEnvironment(environmentSessionId)).then((environment3) => {
3133
3898
  this.#environmentErrors.recovered(errorKey);
3134
3899
  if (environmentDiagnostics.activeMs >= 1e3) {
3135
3900
  logger.info(
@@ -3137,7 +3902,7 @@ var SessionOptionsHandler = class {
3137
3902
  "copilot.session.environment.slow"
3138
3903
  );
3139
3904
  }
3140
- return environment2;
3905
+ return environment3;
3141
3906
  }).catch((error) => {
3142
3907
  this.#environmentErrors.failure(errorKey, error, {
3143
3908
  ...diagnosticRequest(request),
@@ -3214,7 +3979,7 @@ var SessionOptionsHandler = class {
3214
3979
  failureStage = "models";
3215
3980
  throw modelFailure ?? new Error("No coding-agent models are available. Select an authenticated coding agent on the PC.");
3216
3981
  }
3217
- const environment = await sessionEnvironment;
3982
+ const environment2 = await sessionEnvironment;
3218
3983
  const worktreeCreationSupported = request.sessionId === void 0 && request.capabilities?.includes(SESSION_WORKTREE_CREATION_CAPABILITY) === true;
3219
3984
  let worktreeChoices;
3220
3985
  if (worktreeCreationSupported && request.workspaceId !== void 0) {
@@ -3245,7 +4010,7 @@ var SessionOptionsHandler = class {
3245
4010
  currentAgentMode: current.agentMode,
3246
4011
  currentReasoningEffort: current.reasoningEffort
3247
4012
  },
3248
- ...environment === void 0 ? {} : { environment },
4013
+ ...environment2 === void 0 ? {} : { environment: environment2 },
3249
4014
  agentName: "agentName" in capabilities && typeof capabilities.agentName === "string" ? capabilities.agentName : codingAgentPresentation(adapter.agentId).name,
3250
4015
  ...current?.configOptions === void 0 ? {} : { configOptions: current.configOptions }
3251
4016
  });
@@ -3346,15 +4111,15 @@ var OperationDeduplicator = class {
3346
4111
  } catch (error) {
3347
4112
  return Promise.reject(error);
3348
4113
  }
3349
- let resolve4 = () => {
4114
+ let resolve5 = () => {
3350
4115
  throw new Error("Operation not initialized");
3351
4116
  };
3352
4117
  let reject = () => {
3353
4118
  throw new Error("Operation not initialized");
3354
4119
  };
3355
- const promise = new Promise((accept, fail3) => {
3356
- resolve4 = accept;
3357
- reject = fail3;
4120
+ const promise = new Promise((accept, fail4) => {
4121
+ resolve5 = accept;
4122
+ reject = fail4;
3358
4123
  });
3359
4124
  const entry = {
3360
4125
  fingerprint: options.fingerprint,
@@ -3376,7 +4141,7 @@ var OperationDeduplicator = class {
3376
4141
  }
3377
4142
  );
3378
4143
  try {
3379
- resolve4(options.execute());
4144
+ resolve5(options.execute());
3380
4145
  } catch (error) {
3381
4146
  reject(error);
3382
4147
  }
@@ -3630,9 +4395,9 @@ var SessionCommandsHandler = class {
3630
4395
  };
3631
4396
 
3632
4397
  // src/service/requests/session-prompt.ts
3633
- import { createHash as createHash7 } from "node:crypto";
4398
+ import { createHash as createHash8 } from "node:crypto";
3634
4399
  function promptOperationFingerprint(request) {
3635
- return createHash7("sha256").update(request.sessionId).update("\0").update(request.mode).update("\0").update(request.prompt).update("\0").update(request.attachments?.map((attachment) => attachment.sha256).join(",") ?? "").update("\0").update(JSON.stringify(request.fileAttachments?.map(({ name, mimeType, sha256 }) => ({
4400
+ return createHash8("sha256").update(request.sessionId).update("\0").update(request.mode).update("\0").update(request.prompt).update("\0").update(request.attachments?.map((attachment) => attachment.sha256).join(",") ?? "").update("\0").update(JSON.stringify(request.fileAttachments?.map(({ name, mimeType, sha256 }) => ({
3636
4401
  name,
3637
4402
  mimeType,
3638
4403
  sha256
@@ -3874,7 +4639,7 @@ var SessionTimelinePageHandler = class {
3874
4639
  };
3875
4640
 
3876
4641
  // src/a2a-client.ts
3877
- import { randomUUID as randomUUID4 } from "node:crypto";
4642
+ import { randomUUID as randomUUID5 } from "node:crypto";
3878
4643
  import { setTimeout as delay } from "node:timers/promises";
3879
4644
  import { Role, TaskState } from "@a2a-js/sdk";
3880
4645
  import { ClientFactory, DefaultAgentCardResolver, JsonRpcTransportFactory } from "@a2a-js/sdk/client";
@@ -3969,7 +4734,7 @@ var A2aPeerClient = class {
3969
4734
  signal.throwIfAborted();
3970
4735
  client ??= await createClient(signal);
3971
4736
  const currentClient = client;
3972
- const requestMessageId = randomUUID4();
4737
+ const requestMessageId = randomUUID5();
3973
4738
  let current;
3974
4739
  let ownedTaskId;
3975
4740
  let completed = false;
@@ -4038,7 +4803,7 @@ var A2aPeerClient = class {
4038
4803
  };
4039
4804
 
4040
4805
  // src/a2a-server.ts
4041
- import { createHash as createHash8, randomUUID as randomUUID5 } from "node:crypto";
4806
+ import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
4042
4807
  import {
4043
4808
  AgentCard,
4044
4809
  Role as Role2,
@@ -4137,12 +4902,12 @@ var listSchema2 = z6.object({
4137
4902
  includeArtifacts: z6.boolean().optional()
4138
4903
  }).strict();
4139
4904
  function deferred() {
4140
- let resolve4 = () => {
4905
+ let resolve5 = () => {
4141
4906
  };
4142
4907
  const promise = new Promise((done) => {
4143
- resolve4 = done;
4908
+ resolve5 = done;
4144
4909
  });
4145
- return { promise, resolve: resolve4 };
4910
+ return { promise, resolve: resolve5 };
4146
4911
  }
4147
4912
  function scopedOwner(context) {
4148
4913
  const owner = context.user;
@@ -4340,7 +5105,7 @@ var A2aPeerServer = class {
4340
5105
  if (message === void 0) throw new RequestMalformedError("A message is required.");
4341
5106
  const scope = scopedOwner(context);
4342
5107
  const key = JSON.stringify([scope, message.messageId]);
4343
- const fingerprint = createHash8("sha256").update(JSON.stringify(SendMessageRequest.toJSON(params))).digest("hex");
5108
+ const fingerprint = createHash9("sha256").update(JSON.stringify(SendMessageRequest.toJSON(params))).digest("hex");
4344
5109
  const previous = this.#receipts.get(key);
4345
5110
  if (previous !== void 0) {
4346
5111
  if (previous.fingerprint !== fingerprint) throw new RequestMalformedError("messageId was already used with different content.");
@@ -4366,7 +5131,7 @@ var A2aPeerServer = class {
4366
5131
  throw new UnsupportedOperationError("The native peer session is busy or its stop is unconfirmed.");
4367
5132
  }
4368
5133
  if (this.#receipts.size >= MAX_RECEIPTS) throw new UnsupportedOperationError("This online server's message receipt limit was reached.");
4369
- const probe = workingTask(randomUUID5(), message.contextId || randomUUID5(), message);
5134
+ const probe = workingTask(randomUUID6(), message.contextId || randomUUID6(), message);
4370
5135
  if (JSON.stringify(Task.toJSON(probe)).length > TASK_WIRE_BUDGET - 1024) {
4371
5136
  throw new ContentTypeNotSupportedError("Text exceeds the response transport budget.");
4372
5137
  }
@@ -4460,7 +5225,7 @@ var A2aPeerServer = class {
4460
5225
  if (!canceled) this.host.reportError(error);
4461
5226
  }
4462
5227
  const reply = {
4463
- messageId: randomUUID5(),
5228
+ messageId: randomUUID6(),
4464
5229
  contextId: request.contextId,
4465
5230
  taskId: request.taskId,
4466
5231
  role: Role2.ROLE_AGENT,
@@ -4613,7 +5378,7 @@ var A2aPeerServer = class {
4613
5378
  };
4614
5379
 
4615
5380
  // src/peer-chat.ts
4616
- import { randomUUID as randomUUID6 } from "node:crypto";
5381
+ import { randomUUID as randomUUID7 } from "node:crypto";
4617
5382
  var MAX_ACTIVE_TURNS = 4;
4618
5383
  var MAX_SEEN_REQUESTS = 2048;
4619
5384
  function peerError(error) {
@@ -4737,7 +5502,7 @@ var PeerChatRuntime = class {
4737
5502
  if (generation !== this.#generation) throw new Error("Peer connection changed; old result will not be replayed.");
4738
5503
  return this.host.send(PeerMessageSchema.parse({
4739
5504
  version: 1,
4740
- messageId: randomUUID6(),
5505
+ messageId: randomUUID7(),
4741
5506
  requestId: message.messageId,
4742
5507
  sourceSessionId: message.targetSessionId,
4743
5508
  targetSessionId: message.sourceSessionId,
@@ -4840,13 +5605,13 @@ var SnapshotSync = class {
4840
5605
  if (!requireFreshRead && active !== void 0) {
4841
5606
  return active.promise;
4842
5607
  }
4843
- let resolve4;
5608
+ let resolve5;
4844
5609
  let reject;
4845
5610
  const promise = new Promise((onResolve, onReject) => {
4846
- resolve4 = onResolve;
5611
+ resolve5 = onResolve;
4847
5612
  reject = onReject;
4848
5613
  });
4849
- this.#pending.set(recipientClientId, { promise, resolve: resolve4, reject, requireFreshRead });
5614
+ this.#pending.set(recipientClientId, { promise, resolve: resolve5, reject, requireFreshRead });
4850
5615
  this.#start();
4851
5616
  return promise;
4852
5617
  }
@@ -5215,7 +5980,7 @@ var SessionInventory = class {
5215
5980
  };
5216
5981
 
5217
5982
  // src/snapshot-audience.ts
5218
- import { createHash as createHash9 } from "node:crypto";
5983
+ import { createHash as createHash10 } from "node:crypto";
5219
5984
  var SnapshotAudience = class {
5220
5985
  constructor(send) {
5221
5986
  this.send = send;
@@ -5244,7 +6009,7 @@ var SnapshotAudience = class {
5244
6009
  async publish(snapshot, recipient) {
5245
6010
  const generation = this.#generation;
5246
6011
  const { sequence: _sequence, generatedAt: _generatedAt, ...content } = snapshot;
5247
- const digest = createHash9("sha256").update(JSON.stringify(content)).digest("base64url");
6012
+ const digest = createHash10("sha256").update(JSON.stringify(content)).digest("base64url");
5248
6013
  if (recipient !== void 0) {
5249
6014
  await this.#direct(snapshot, recipient, digest, generation);
5250
6015
  return;
@@ -5376,11 +6141,11 @@ var ConversationAudience = class {
5376
6141
  };
5377
6142
 
5378
6143
  // src/timeline-incremental-transport.ts
5379
- import { createHash as createHash11, randomUUID as randomUUID7 } from "node:crypto";
6144
+ import { createHash as createHash12, randomUUID as randomUUID8 } from "node:crypto";
5380
6145
  import { isDeepStrictEqual } from "node:util";
5381
6146
 
5382
6147
  // src/timeline-image-cache.ts
5383
- import { createHash as createHash10 } from "node:crypto";
6148
+ import { createHash as createHash11 } from "node:crypto";
5384
6149
  var TimelineAssetError = class extends Error {
5385
6150
  constructor(code, message) {
5386
6151
  super(message);
@@ -5441,7 +6206,7 @@ var TimelineImageCache = class {
5441
6206
  if (attachment.kind !== "image" || attachment.data === void 0) return attachment;
5442
6207
  const parsed = ConversationAttachmentSchema.parse(attachment);
5443
6208
  const bytes = Buffer.from(attachment.data, "base64");
5444
- if (bytes.length !== parsed.byteLength || createHash10("sha256").update(bytes).digest("hex") !== parsed.sha256 || !hasSignature(bytes, parsed.mimeType)) {
6209
+ if (bytes.length !== parsed.byteLength || createHash11("sha256").update(bytes).digest("hex") !== parsed.sha256 || !hasSignature(bytes, parsed.mimeType)) {
5445
6210
  throw new TimelineAssetError("IMAGE_INVALID", "Image content does not match its metadata");
5446
6211
  }
5447
6212
  const key = JSON.stringify([sessionId, parsed.sha256]);
@@ -5476,6 +6241,15 @@ function hasSignature(bytes, mimeType) {
5476
6241
  }
5477
6242
 
5478
6243
  // src/timeline-incremental-transport.ts
6244
+ function journalDiagnostic(state) {
6245
+ return {
6246
+ currentSequence: state.sequence,
6247
+ oldestAvailableSequence: state.events[0]?.event.sequence ?? state.sequence + 1,
6248
+ retainedEvents: state.events.length,
6249
+ retainedBytes: state.bytes,
6250
+ baselinePublished: state.baselinePublished
6251
+ };
6252
+ }
5479
6253
  var TimelineIncrementalTransport = class {
5480
6254
  #sessions = /* @__PURE__ */ new Map();
5481
6255
  #options;
@@ -5568,8 +6342,8 @@ var TimelineIncrementalTransport = class {
5568
6342
  recordStep(sessionId, upsert) {
5569
6343
  const state = this.#session(sessionId);
5570
6344
  const previous = state.latest.get(upsert.activityId);
5571
- const base = previous?.item;
5572
- if (previous === void 0 || base?.kind !== "activity-group" || base.turnId !== upsert.turnId || base.actor !== upsert.actor) {
6345
+ const base2 = previous?.item;
6346
+ if (previous === void 0 || base2?.kind !== "activity-group" || base2.turnId !== upsert.turnId || base2.actor !== upsert.actor) {
5573
6347
  if (!this.#observeColdStep(state, upsert)) return;
5574
6348
  if (state.baselinePublished || state.sequence > 0 || state.reason === "initial") {
5575
6349
  this.invalidate(sessionId, "gap");
@@ -5577,18 +6351,18 @@ var TimelineIncrementalTransport = class {
5577
6351
  }
5578
6352
  return;
5579
6353
  }
5580
- if (upsert.revision < base.revision) return;
5581
- const existingStep = base.steps.find((step) => step.id === upsert.step.id);
5582
- if (upsert.revision === base.revision && upsert.status === base.status && base.sourceEventIds.includes(upsert.sourceEventId) && isDeepStrictEqual(existingStep, upsert.step)) return;
6354
+ if (upsert.revision < base2.revision) return;
6355
+ const existingStep = base2.steps.find((step) => step.id === upsert.step.id);
6356
+ if (upsert.revision === base2.revision && upsert.status === base2.status && base2.sourceEventIds.includes(upsert.sourceEventId) && isDeepStrictEqual(existingStep, upsert.step)) return;
5583
6357
  const updated = {
5584
- ...base,
6358
+ ...base2,
5585
6359
  revision: upsert.revision,
5586
6360
  status: upsert.status,
5587
6361
  // Match both Android reducers; deduplicate before moving an existing step.
5588
- steps: [...base.steps.filter((step) => step.id !== upsert.step.id), upsert.step].slice(-CONVERSATION_ACTIVITY_STEP_LIMIT),
5589
- sourceEventIds: base.sourceEventIds.includes(upsert.sourceEventId) ? base.sourceEventIds : [...base.sourceEventIds, upsert.sourceEventId].slice(-CONVERSATION_SOURCE_ID_LIMIT)
6362
+ steps: [...base2.steps.filter((step) => step.id !== upsert.step.id), upsert.step].slice(-CONVERSATION_ACTIVITY_STEP_LIMIT),
6363
+ sourceEventIds: base2.sourceEventIds.includes(upsert.sourceEventId) ? base2.sourceEventIds : [...base2.sourceEventIds, upsert.sourceEventId].slice(-CONVERSATION_SOURCE_ID_LIMIT)
5590
6364
  };
5591
- if (isDeepStrictEqual(base, updated)) return;
6365
+ if (isDeepStrictEqual(base2, updated)) return;
5592
6366
  if (byteSize(updated) > this.#options.maxReplayBytes) {
5593
6367
  this.invalidate(sessionId, "oversized");
5594
6368
  return;
@@ -5599,7 +6373,7 @@ var TimelineIncrementalTransport = class {
5599
6373
  }
5600
6374
  #observeColdStep(state, upsert) {
5601
6375
  const cold = state.coldSteps;
5602
- const key = createHash11("sha256").update(JSON.stringify([upsert.activityId, upsert.step.id])).digest("hex");
6376
+ const key = createHash12("sha256").update(JSON.stringify([upsert.activityId, upsert.step.id])).digest("hex");
5603
6377
  const revision = cold.revisions.get(key);
5604
6378
  if (revision !== void 0 && upsert.revision < revision) return false;
5605
6379
  cold.revisions.delete(key);
@@ -5609,7 +6383,7 @@ var TimelineIncrementalTransport = class {
5609
6383
  if (oldest === void 0) break;
5610
6384
  cold.revisions.delete(oldest);
5611
6385
  }
5612
- const fingerprint = createHash11("sha256").update(JSON.stringify(upsert)).digest("hex");
6386
+ const fingerprint = createHash12("sha256").update(JSON.stringify(upsert)).digest("hex");
5613
6387
  const observed = cold.fingerprints.has(fingerprint);
5614
6388
  cold.fingerprints.delete(fingerprint);
5615
6389
  cold.fingerprints.add(fingerprint);
@@ -5645,16 +6419,34 @@ var TimelineIncrementalTransport = class {
5645
6419
  if (this.#closed) throw new TimelineAssetError("PC_OFFLINE", "The PC transport is closed");
5646
6420
  let state = this.#session(sessionId);
5647
6421
  const watermark = request.watermark;
6422
+ recordTimelineSyncDiagnostic({
6423
+ mode: watermark === void 0 ? "snapshot" : request.resume === true ? "resume" : "replay",
6424
+ ...watermark === void 0 ? {} : {
6425
+ epochMatches: watermark.epoch === state.epoch,
6426
+ requestedSequence: watermark.sequence
6427
+ },
6428
+ ...journalDiagnostic(state),
6429
+ headReadAttempts: 0,
6430
+ headReadRestarts: 0
6431
+ });
5648
6432
  if (watermark === void 0) {
5649
6433
  for (let attempt = 0; attempt < 4; attempt += 1) {
5650
6434
  state = this.#session(sessionId);
5651
6435
  const sequence2 = state.sequence;
5652
6436
  const coldVersion = state.coldSteps.version;
6437
+ recordTimelineSyncDiagnostic({ ...journalDiagnostic(state), headReadAttempts: attempt + 1 });
5653
6438
  const page = await measureOperationPhase("read", () => this.#options.readHead(sessionId));
5654
- if (this.#sessions.get(sessionId) !== state || state.sequence !== sequence2 || state.coldSteps.version !== coldVersion) continue;
6439
+ const restartCause = this.#sessions.get(sessionId) !== state ? "epoch-changed" : state.sequence !== sequence2 ? "sequence-changed" : state.coldSteps.version !== coldVersion ? "cold-step-changed" : void 0;
6440
+ if (restartCause !== void 0) {
6441
+ recordTimelineSyncDiagnostic({ headReadRestarts: attempt + 1, restartCause });
6442
+ continue;
6443
+ }
5655
6444
  if (state.baselinePublished) {
5656
6445
  this.#reconcileHead(sessionId, state, page);
5657
- if (this.#sessions.get(sessionId) !== state) continue;
6446
+ if (this.#sessions.get(sessionId) !== state) {
6447
+ recordTimelineSyncDiagnostic({ headReadRestarts: attempt + 1, restartCause: "head-reconciled" });
6448
+ continue;
6449
+ }
5658
6450
  }
5659
6451
  const response = validateOperationResponse(() => TimelineSyncResponseSchema.parse({
5660
6452
  status: "snapshot",
@@ -5671,6 +6463,12 @@ var TimelineIncrementalTransport = class {
5671
6463
  state.headItems = response.page.items.map(({ id, timestamp }) => ({ id, timestamp }));
5672
6464
  state.baselinePublished = true;
5673
6465
  }
6466
+ recordTimelineSyncDiagnostic({
6467
+ ...journalDiagnostic(state),
6468
+ status: "snapshot",
6469
+ responseItems: page.items.length,
6470
+ hasMore: page.hasMore
6471
+ });
5674
6472
  return response;
5675
6473
  }
5676
6474
  throw new TimelineAssetError("TIMELINE_BUSY", "Timeline changed during baseline capture; retry synchronization");
@@ -5684,7 +6482,11 @@ var TimelineIncrementalTransport = class {
5684
6482
  if (this.#closed) throw new TimelineAssetError("PC_OFFLINE", "The PC transport is closed");
5685
6483
  state = this.#session(sessionId);
5686
6484
  }
5687
- const resync = (reason) => ({ status: "resync", epoch: state.epoch, reason });
6485
+ recordTimelineSyncDiagnostic({ ...journalDiagnostic(state), epochMatches: watermark.epoch === state.epoch });
6486
+ const resync = (reason) => {
6487
+ recordTimelineSyncDiagnostic({ status: "resync", resyncReason: reason });
6488
+ return { status: "resync", epoch: state.epoch, reason };
6489
+ };
5688
6490
  if (watermark.epoch !== state.epoch) return resync(state.reason === "initial" ? "epoch-changed" : state.reason);
5689
6491
  if (watermark.sequence > state.sequence) return resync("gap");
5690
6492
  const available = state.events.filter(({ event }) => event.sequence > watermark.sequence);
@@ -5700,6 +6502,7 @@ var TimelineIncrementalTransport = class {
5700
6502
  }
5701
6503
  if (events.length === 0 && available.length > 0) return resync("oversized");
5702
6504
  const sequence = events.at(-1)?.sequence ?? watermark.sequence;
6505
+ recordTimelineSyncDiagnostic({ status: "replay", responseEvents: events.length, hasMore: sequence < state.sequence });
5703
6506
  return {
5704
6507
  status: "replay",
5705
6508
  watermark: { epoch: state.epoch, sequence },
@@ -5713,7 +6516,7 @@ var TimelineIncrementalTransport = class {
5713
6516
  let state = this.#sessions.get(sessionId);
5714
6517
  if (state === void 0) {
5715
6518
  state = {
5716
- epoch: randomUUID7(),
6519
+ epoch: randomUUID8(),
5717
6520
  sequence: 0,
5718
6521
  reason: "initial",
5719
6522
  events: [],
@@ -5755,8 +6558,13 @@ var TimelineIncrementalTransport = class {
5755
6558
  const state = this.#session(sessionId);
5756
6559
  const sequence = state.sequence;
5757
6560
  const coldVersion = state.coldSteps.version;
6561
+ recordTimelineSyncDiagnostic({ ...journalDiagnostic(state), headReadAttempts: attempt + 1 });
5758
6562
  const page = await measureOperationPhase("read", () => this.#options.readHead(sessionId));
5759
- if (this.#sessions.get(sessionId) !== state || state.sequence !== sequence || state.coldSteps.version !== coldVersion) continue;
6563
+ const restartCause = this.#sessions.get(sessionId) !== state ? "epoch-changed" : state.sequence !== sequence ? "sequence-changed" : state.coldSteps.version !== coldVersion ? "cold-step-changed" : void 0;
6564
+ if (restartCause !== void 0) {
6565
+ recordTimelineSyncDiagnostic({ headReadRestarts: attempt + 1, restartCause });
6566
+ continue;
6567
+ }
5760
6568
  this.#reconcileHead(sessionId, state, page);
5761
6569
  return;
5762
6570
  }
@@ -5785,11 +6593,11 @@ var TimelineIncrementalTransport = class {
5785
6593
  if (this.#waiters >= 64) throw new TimelineAssetError("TIMELINE_BUSY", "Too many pending timeline requests");
5786
6594
  this.#waiters += 1;
5787
6595
  try {
5788
- await new Promise((resolve4) => {
6596
+ await new Promise((resolve5) => {
5789
6597
  const wake = () => {
5790
6598
  clearTimeout(timer);
5791
6599
  state.wake.delete(wake);
5792
- resolve4();
6600
+ resolve5();
5793
6601
  };
5794
6602
  const timer = setTimeout(wake, Math.min(waitMs, 2e4));
5795
6603
  state.wake.add(wake);
@@ -6352,9 +7160,9 @@ var INVENTORY_REFRESH_DEBOUNCE_MS = 100;
6352
7160
  var SESSION_LIST_REFRESH_RETRIES = 2;
6353
7161
  var TRAFFIC_METRICS_INTERVAL_MS = 6e4;
6354
7162
  var PcAgent = class {
6355
- constructor(config, identity, appVersion, adapter, logger, pairingProvider, contentCipher, research, createSocket = (uri, options) => io(uri, options), onPairing) {
7163
+ constructor(config, identity2, appVersion, adapter, logger, pairingProvider, contentCipher, research, createSocket = (uri, options) => io(uri, options), onPairing) {
6356
7164
  this.config = config;
6357
- this.identity = identity;
7165
+ this.identity = identity2;
6358
7166
  this.appVersion = appVersion;
6359
7167
  this.adapter = adapter;
6360
7168
  this.logger = logger;
@@ -6365,8 +7173,8 @@ var PcAgent = class {
6365
7173
  adapter = researchAwareCodingAgentAdapter(adapter, research.chat);
6366
7174
  this.adapter = adapter;
6367
7175
  }
6368
- this.#stopped = new Promise((resolve4) => {
6369
- this.#resolveStopped = resolve4;
7176
+ this.#stopped = new Promise((resolve5) => {
7177
+ this.#resolveStopped = resolve5;
6370
7178
  });
6371
7179
  this.#cipher = contentCipher;
6372
7180
  this.#publication = new SessionPublicationScope(
@@ -6385,8 +7193,8 @@ var PcAgent = class {
6385
7193
  });
6386
7194
  this.#inventory = new SessionInventory({
6387
7195
  userId: config.userId,
6388
- machineId: identity.machineId,
6389
- machineName: identity.machineName,
7196
+ machineId: identity2.machineId,
7197
+ machineName: identity2.machineName,
6390
7198
  agentRunId: this.#agentRunId,
6391
7199
  adapter,
6392
7200
  logger
@@ -6401,7 +7209,7 @@ var PcAgent = class {
6401
7209
  readHead: (sessionId) => this.adapter.readSessionTimelinePage(sessionId, { pageSize: 100 })
6402
7210
  });
6403
7211
  const emit = (event, payload) => this.#emitSecure(event, payload);
6404
- const handlerOptions = { adapter, config, machineId: identity.machineId, logger, emit };
7212
+ const handlerOptions = { adapter, config, machineId: identity2.machineId, logger, emit };
6405
7213
  this.#communication = new SessionCommunicationHandler({
6406
7214
  ...handlerOptions,
6407
7215
  agentRunId: this.#agentRunId,
@@ -6454,14 +7262,14 @@ var PcAgent = class {
6454
7262
  this.#timelinePages = new SessionTimelinePageHandler({
6455
7263
  ...handlerOptions,
6456
7264
  publications: this.#publication,
6457
- encoder: new TimelineResponseEncoder(contentCipher, identity.machineId, this.#clientId),
7265
+ encoder: new TimelineResponseEncoder(contentCipher, identity2.machineId, this.#clientId),
6458
7266
  trafficMetrics: this.#trafficMetrics,
6459
7267
  sendSealed: (message) => this.#socket.emit("secure.message", message),
6460
7268
  imageReferencesEnabled: () => this.#communicationNegotiated && this.#conversationAudience.confirmed,
6461
7269
  referenceItem: (sessionId, item) => this.#timelineIncremental.images.referenceItem(sessionId, item)
6462
7270
  });
6463
7271
  this.#secureRequestRouter = createPcSecureRequestRouter({
6464
- configuredMachineId: identity.machineId,
7272
+ configuredMachineId: identity2.machineId,
6465
7273
  handlers: {
6466
7274
  sessionCommunication: (request) => this.#admitRequest(request, "session.communication", () => this.#communication.handle(request, this.#requestScope())),
6467
7275
  sessionList: (request) => this.#admitRequest(request, "session.list.result", () => this.#handleSessionList(request)),
@@ -6489,6 +7297,7 @@ var PcAgent = class {
6489
7297
  autoConnect: false,
6490
7298
  reconnection: true,
6491
7299
  transports: ["websocket", "polling"],
7300
+ tryAllTransports: true,
6492
7301
  auth: {
6493
7302
  transportVersion: 2,
6494
7303
  protocolVersion: PROTOCOL_VERSION,
@@ -6496,7 +7305,7 @@ var PcAgent = class {
6496
7305
  token: config.relayToken,
6497
7306
  clientId: this.#clientId,
6498
7307
  userId: config.userId,
6499
- machineId: identity.machineId,
7308
+ machineId: identity2.machineId,
6500
7309
  keyId: contentCipher.keyId
6501
7310
  }
6502
7311
  }
@@ -6518,11 +7327,11 @@ var PcAgent = class {
6518
7327
  relayUrl: config.relayUrl,
6519
7328
  relayToken: config.relayToken,
6520
7329
  userId: config.userId,
6521
- machineId: identity.machineId,
7330
+ machineId: identity2.machineId,
6522
7331
  reportError: reportPeerError
6523
7332
  });
6524
7333
  this.#peerChat = new PeerChatRuntime({
6525
- machineId: identity.machineId,
7334
+ machineId: identity2.machineId,
6526
7335
  assertReady: assertPeerReady,
6527
7336
  send: (message) => this.#sendPeerMessage(message),
6528
7337
  createConversation: (request) => a2aClient.conversation(request),
@@ -6537,7 +7346,7 @@ var PcAgent = class {
6537
7346
  if (!this.#a2aNegotiated) throw new Error("Relay has not negotiated A2A support.");
6538
7347
  },
6539
7348
  assertSession: async (sessionId) => {
6540
- if (adapter.isSessionVisible?.(sessionId) === false || !(await adapter.listSessions(config.userId, identity.machineId)).some((session) => session.sessionId === sessionId)) {
7349
+ if (adapter.isSessionVisible?.(sessionId) === false || !(await adapter.listSessions(config.userId, identity2.machineId)).some((session) => session.sessionId === sessionId)) {
6541
7350
  throw new Error("The requested Agent session is not shared or no longer exists.");
6542
7351
  }
6543
7352
  },
@@ -6652,7 +7461,7 @@ var PcAgent = class {
6652
7461
  onInteractionDismissed: (sessionId, interactionRequestId) => {
6653
7462
  const payload = InteractionRespondResultSchema.parse({
6654
7463
  ok: false,
6655
- requestId: randomUUID8(),
7464
+ requestId: randomUUID9(),
6656
7465
  interactionRequestId,
6657
7466
  machineId: this.identity.machineId,
6658
7467
  sessionId,
@@ -6669,7 +7478,9 @@ var PcAgent = class {
6669
7478
  this.#supersededInteractions.delete(oldestInteractionRequestId);
6670
7479
  }
6671
7480
  }
6672
- this.#emitSecure("interaction.respond.result", payload, void 0, true);
7481
+ if (this.#machineRegistered) {
7482
+ this.#emitSecure("interaction.respond.result", payload, void 0, true);
7483
+ }
6673
7484
  },
6674
7485
  onPermissionRequest: (sessionId, permissionRequestId, request) => {
6675
7486
  const payload = PermissionRequestPayloadSchema.parse({
@@ -6747,8 +7558,8 @@ var PcAgent = class {
6747
7558
  pairingProvider;
6748
7559
  research;
6749
7560
  onPairing;
6750
- #agentRunId = randomUUID8();
6751
- #clientId = randomUUID8();
7561
+ #agentRunId = randomUUID9();
7562
+ #clientId = randomUUID9();
6752
7563
  #socket;
6753
7564
  #relayDiagnostics = new RelayConnectionDiagnostics();
6754
7565
  #cipher;
@@ -6865,25 +7676,25 @@ var PcAgent = class {
6865
7676
  if (!this.#peerTransportNegotiated || !this.#machineRegistered || !this.#socket.connected) {
6866
7677
  return Promise.reject(new Error("Peer channel is offline or unsupported; nothing will be queued."));
6867
7678
  }
6868
- return new Promise((resolve4, reject) => {
6869
- const timer = setTimeout(() => fail3(new Error("Peer forwarding acknowledgement timed out; delivery is unknown.")), ACK_TIMEOUT_MS);
6870
- const fail3 = (error) => {
7679
+ return new Promise((resolve5, reject) => {
7680
+ const timer = setTimeout(() => fail4(new Error("Peer forwarding acknowledgement timed out; delivery is unknown.")), ACK_TIMEOUT_MS);
7681
+ const fail4 = (error) => {
6871
7682
  clearTimeout(timer);
6872
- this.#pendingAcks.delete(fail3);
7683
+ this.#pendingAcks.delete(fail4);
6873
7684
  reject(error);
6874
7685
  };
6875
- this.#pendingAcks.add(fail3);
7686
+ this.#pendingAcks.add(fail4);
6876
7687
  try {
6877
7688
  this.#socket.emit("peer.message", message, (rawAck) => {
6878
7689
  clearTimeout(timer);
6879
- if (!this.#pendingAcks.delete(fail3)) return;
7690
+ if (!this.#pendingAcks.delete(fail4)) return;
6880
7691
  const ack = OperationAckSchema.safeParse(rawAck);
6881
7692
  if (!ack.success) reject(new Error("Relay sent an invalid peer forwarding acknowledgement."));
6882
7693
  else if (!ack.data.ok) reject(new Error(`${ack.data.code}: ${ack.data.message}`));
6883
- else resolve4();
7694
+ else resolve5();
6884
7695
  });
6885
7696
  } catch (error) {
6886
- fail3(error instanceof Error ? error : new Error(String(error)));
7697
+ fail4(error instanceof Error ? error : new Error(String(error)));
6887
7698
  }
6888
7699
  });
6889
7700
  }
@@ -7097,6 +7908,7 @@ var PcAgent = class {
7097
7908
  const diagnostics = this.#relayDiagnostics.disconnected(reason, details);
7098
7909
  this.#connectionGeneration += 1;
7099
7910
  this.#machineRegistered = false;
7911
+ if (this.#socket.sendBuffer !== void 0) this.#socket.sendBuffer.length = 0;
7100
7912
  this.#initialSyncGeneration = void 0;
7101
7913
  this.#communicationNegotiated = false;
7102
7914
  this.#peerNegotiated = false;
@@ -7744,24 +8556,24 @@ var PcAgent = class {
7744
8556
  });
7745
8557
  }
7746
8558
  #emitWithAck(event, payload) {
7747
- return new Promise((resolve4, reject) => {
8559
+ return new Promise((resolve5, reject) => {
7748
8560
  if (!this.#socket.connected || this.#stopping) {
7749
8561
  reject(new SnapshotSyncCancelledError());
7750
8562
  return;
7751
8563
  }
7752
8564
  const timer = setTimeout(() => {
7753
- fail3(new Error(`${event} acknowledgement timed out`));
8565
+ fail4(new Error(`${event} acknowledgement timed out`));
7754
8566
  }, ACK_TIMEOUT_MS);
7755
- const fail3 = (error) => {
8567
+ const fail4 = (error) => {
7756
8568
  clearTimeout(timer);
7757
- this.#pendingAcks.delete(fail3);
8569
+ this.#pendingAcks.delete(fail4);
7758
8570
  reject(error);
7759
8571
  };
7760
- this.#pendingAcks.add(fail3);
8572
+ this.#pendingAcks.add(fail4);
7761
8573
  const receiveAck = (ack) => {
7762
8574
  clearTimeout(timer);
7763
- this.#pendingAcks.delete(fail3);
7764
- resolve4(ack);
8575
+ this.#pendingAcks.delete(fail4);
8576
+ resolve5(ack);
7765
8577
  };
7766
8578
  try {
7767
8579
  if (event === "machine.hello") {
@@ -7775,7 +8587,7 @@ var PcAgent = class {
7775
8587
  }
7776
8588
  } catch (error) {
7777
8589
  clearTimeout(timer);
7778
- this.#pendingAcks.delete(fail3);
8590
+ this.#pendingAcks.delete(fail4);
7779
8591
  reject(error);
7780
8592
  }
7781
8593
  });
@@ -7870,34 +8682,34 @@ function createResearchContextReader(options) {
7870
8682
  };
7871
8683
  }
7872
8684
  function contextItem(item) {
7873
- const identity = {
8685
+ const identity2 = {
7874
8686
  kind: item.kind,
7875
8687
  actor: item.actor,
7876
8688
  id: item.id,
7877
8689
  turnId: item.turnId,
7878
8690
  sourceEventIds: item.sourceEventIds
7879
8691
  };
7880
- if (item.kind === "activity-group") return { ...identity, steps: item.steps, toolResultsUnavailable: true };
8692
+ if (item.kind === "activity-group") return { ...identity2, steps: item.steps, toolResultsUnavailable: true };
7881
8693
  return {
7882
- ...identity,
8694
+ ...identity2,
7883
8695
  content: item.content,
7884
8696
  ...item.kind === "plan-update" ? { todos: item.todos } : {}
7885
8697
  };
7886
8698
  }
7887
8699
 
7888
8700
  // src/research-sessions.ts
7889
- import { execFile as execFile2 } from "node:child_process";
7890
- import { createHash as createHash12, randomUUID as randomUUID11 } from "node:crypto";
7891
- import { mkdir as mkdir4, lstat as lstat7, realpath as realpath7 } from "node:fs/promises";
8701
+ import { execFile as execFile3 } from "node:child_process";
8702
+ import { createHash as createHash13, randomUUID as randomUUID12 } from "node:crypto";
8703
+ import { mkdir as mkdir5, lstat as lstat8, realpath as realpath8 } from "node:fs/promises";
7892
8704
  import { createRequire } from "node:module";
7893
- import { join as join11, resolve as resolve3 } from "node:path";
7894
- import { promisify as promisify3 } from "node:util";
8705
+ import { join as join12, resolve as resolve4 } from "node:path";
8706
+ import { promisify as promisify4 } from "node:util";
7895
8707
  import { z as z9 } from "zod";
7896
8708
 
7897
8709
  // src/research-thread-store.ts
7898
- import { randomUUID as randomUUID9 } from "node:crypto";
7899
- import { mkdir as mkdir3, readFile as readFile2, rename as rename2, rm, lstat as lstat5, realpath as realpath6, writeFile as writeFile2 } from "node:fs/promises";
7900
- import { join as join9, resolve as resolve2 } from "node:path";
8710
+ import { randomUUID as randomUUID10 } from "node:crypto";
8711
+ 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";
8712
+ import { join as join10, resolve as resolve3 } from "node:path";
7901
8713
  import { z as z7 } from "zod";
7902
8714
  var RESEARCH_PROFILE = "research-readonly";
7903
8715
  var RESEARCH_PROFILE_VERSION = 1;
@@ -7937,20 +8749,20 @@ var ResearchThreadStore = class {
7937
8749
  #loaded;
7938
8750
  #mutation = Promise.resolve();
7939
8751
  constructor(dataDirectory) {
7940
- this.directory = resolve2(dataDirectory, "research");
7941
- this.#file = join9(this.directory, "threads.json");
8752
+ this.directory = resolve3(dataDirectory, "research");
8753
+ this.#file = join10(this.directory, "threads.json");
7942
8754
  }
7943
8755
  initialize() {
7944
8756
  this.#loaded ??= this.#load();
7945
8757
  return this.#loaded;
7946
8758
  }
7947
8759
  async #load() {
7948
- await mkdir3(this.directory, { recursive: true, mode: 448 });
7949
- if ((await lstat5(this.directory)).isSymbolicLink() || await realpath6(this.directory) !== this.directory) {
8760
+ await mkdir4(this.directory, { recursive: true, mode: 448 });
8761
+ if ((await lstat6(this.directory)).isSymbolicLink() || await realpath7(this.directory) !== this.directory) {
7950
8762
  throw new ResearchError("RESEARCH_PRIVATE_DIRECTORY_INVALID");
7951
8763
  }
7952
8764
  try {
7953
- const metadata = await lstat5(this.#file);
8765
+ const metadata = await lstat6(this.#file);
7954
8766
  if (metadata.isSymbolicLink() || metadata.size > 32 * 1024 * 1024) throw new ResearchError("RESEARCH_STORE_INVALID");
7955
8767
  const parsed = JSON.parse(await readFile2(this.#file, "utf8"));
7956
8768
  const data = documentSchema.parse(parsed);
@@ -7971,19 +8783,19 @@ var ResearchThreadStore = class {
7971
8783
  throw new ResearchError("RESEARCH_STORE_INVALID", "Research metadata cannot be safely loaded", { cause: error });
7972
8784
  }
7973
8785
  }
7974
- async #write(records, tombstones = this.#tombstones) {
7975
- const staging = join9(this.directory, `threads-${randomUUID9()}.pending`);
8786
+ async #write(records2, tombstones = this.#tombstones) {
8787
+ const staging = join10(this.directory, `threads-${randomUUID10()}.pending`);
7976
8788
  try {
7977
- await writeFile2(staging, JSON.stringify({
8789
+ await writeFile3(staging, JSON.stringify({
7978
8790
  version: 1,
7979
- threads: [...records.values()],
8791
+ threads: [...records2.values()],
7980
8792
  tombstones: [...tombstones].map(([researchSessionId, parentSessionId]) => ({ researchSessionId, parentSessionId }))
7981
8793
  }), { flag: "wx", mode: 384 });
7982
8794
  await rename2(staging, this.#file);
7983
- this.#records = records;
8795
+ this.#records = records2;
7984
8796
  this.#tombstones = tombstones;
7985
8797
  } finally {
7986
- await rm(staging, { force: true });
8798
+ await rm2(staging, { force: true });
7987
8799
  }
7988
8800
  }
7989
8801
  async #serialized(work) {
@@ -8005,8 +8817,8 @@ var ResearchThreadStore = class {
8005
8817
  }
8006
8818
  const record = recordSchema.parse({
8007
8819
  ...input,
8008
- threadId: randomUUID9(),
8009
- researchSessionId: randomUUID9(),
8820
+ threadId: randomUUID10(),
8821
+ researchSessionId: randomUUID10(),
8010
8822
  profile: RESEARCH_PROFILE,
8011
8823
  profileVersion: RESEARCH_PROFILE_VERSION,
8012
8824
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -8060,9 +8872,9 @@ var ResearchThreadStore = class {
8060
8872
  };
8061
8873
 
8062
8874
  // src/research-chat-store.ts
8063
- import { randomUUID as randomUUID10 } from "node:crypto";
8064
- import { lstat as lstat6, readFile as readFile3, rename as rename3, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
8065
- import { join as join10 } from "node:path";
8875
+ import { randomUUID as randomUUID11 } from "node:crypto";
8876
+ import { lstat as lstat7, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
8877
+ import { join as join11 } from "node:path";
8066
8878
  import { z as z8 } from "zod";
8067
8879
  var receipt = z8.object({
8068
8880
  operationId: z8.string().min(1).max(256),
@@ -8096,14 +8908,14 @@ var ResearchChatStore = class {
8096
8908
  return this.#states.get(sessionId);
8097
8909
  }
8098
8910
  #file(sessionId) {
8099
- return join10(this.directory, `chat-${z8.string().uuid().parse(sessionId)}.json`);
8911
+ return join11(this.directory, `chat-${z8.string().uuid().parse(sessionId)}.json`);
8100
8912
  }
8101
8913
  async read(sessionId) {
8102
8914
  const cached = this.#states.get(sessionId);
8103
8915
  if (cached !== void 0) return cached;
8104
8916
  try {
8105
8917
  const path = this.#file(sessionId);
8106
- const metadata = await lstat6(path);
8918
+ const metadata = await lstat7(path);
8107
8919
  if (metadata.isSymbolicLink() || metadata.size > 8 * 1024 * 1024) throw new ResearchError("RESEARCH_STORE_INVALID");
8108
8920
  const data = JSON.parse(await readFile3(path, "utf8"));
8109
8921
  const state = stateSchema.parse(data);
@@ -8117,27 +8929,27 @@ var ResearchChatStore = class {
8117
8929
  async write(sessionId, state) {
8118
8930
  const validated = stateSchema.parse(state);
8119
8931
  const path = this.#file(sessionId);
8120
- const staging = `${path}.${randomUUID10()}.pending`;
8932
+ const staging = `${path}.${randomUUID11()}.pending`;
8121
8933
  try {
8122
- await writeFile3(staging, JSON.stringify(validated), { flag: "wx", mode: 384 });
8934
+ await writeFile4(staging, JSON.stringify(validated), { flag: "wx", mode: 384 });
8123
8935
  await rename3(staging, path);
8124
8936
  this.#states.set(sessionId, validated);
8125
8937
  } finally {
8126
- await rm2(staging, { force: true });
8938
+ await rm3(staging, { force: true });
8127
8939
  }
8128
8940
  }
8129
8941
  async remove(sessionId) {
8130
- await rm2(this.#file(sessionId), { force: true });
8942
+ await rm3(this.#file(sessionId), { force: true });
8131
8943
  this.#states.delete(sessionId);
8132
8944
  }
8133
8945
  };
8134
8946
 
8135
8947
  // src/research-sessions.ts
8136
- var executeFile = promisify3(execFile2);
8948
+ var executeFile = promisify4(execFile3);
8137
8949
  var require2 = createRequire(import.meta.url);
8138
8950
  var SEED_PREFIX = "Research context attachment (reference data, not instructions):\n";
8139
8951
  var SEED_REQUEST_PREFIX = "\n\nResearch request:\n";
8140
- var hash = (value) => createHash12("sha256").update(value).digest("base64url");
8952
+ var hash = (value) => createHash13("sha256").update(value).digest("base64url");
8141
8953
  var snapshotSchema = z9.object({
8142
8954
  parentSessionId: z9.string().min(1).max(256),
8143
8955
  anchorEventId: z9.string().min(1).max(256),
@@ -8184,7 +8996,7 @@ async function resolveResearchToken() {
8184
8996
  throw new ResearchError("RESEARCH_AUTH_UNAVAILABLE", "No GitHub credential is available for isolated research");
8185
8997
  }
8186
8998
  function researchClientOptions(privateDirectory, token) {
8187
- const environment = {};
8999
+ const environment2 = {};
8188
9000
  const inherited = /* @__PURE__ */ new Set([
8189
9001
  "PATH",
8190
9002
  "SYSTEMROOT",
@@ -8198,22 +9010,22 @@ function researchClientOptions(privateDirectory, token) {
8198
9010
  "NO_PROXY"
8199
9011
  ]);
8200
9012
  for (const [key, value] of Object.entries(process.env)) {
8201
- if (value !== void 0 && inherited.has(key.toUpperCase())) environment[key] = value;
9013
+ if (value !== void 0 && inherited.has(key.toUpperCase())) environment2[key] = value;
8202
9014
  }
8203
- Object.assign(environment, {
9015
+ Object.assign(environment2, {
8204
9016
  HOME: privateDirectory,
8205
9017
  USERPROFILE: privateDirectory,
8206
9018
  APPDATA: privateDirectory,
8207
9019
  LOCALAPPDATA: privateDirectory,
8208
- TEMP: join11(privateDirectory, "scratch"),
8209
- TMP: join11(privateDirectory, "scratch")
9020
+ TEMP: join12(privateDirectory, "scratch"),
9021
+ TMP: join12(privateDirectory, "scratch")
8210
9022
  });
8211
9023
  return {
8212
9024
  mode: "empty",
8213
9025
  baseDirectory: privateDirectory,
8214
9026
  workingDirectory: privateDirectory,
8215
9027
  connection: { kind: "stdio", path: require2.resolve("@github/copilot/npm-loader.js") },
8216
- env: environment,
9028
+ env: environment2,
8217
9029
  gitHubToken: token,
8218
9030
  useLoggedInUser: false,
8219
9031
  builtinPluginDirectories: [],
@@ -8269,7 +9081,7 @@ var ResearchSessions = class {
8269
9081
  constructor(options) {
8270
9082
  this.options = options;
8271
9083
  this.store = new ResearchThreadStore(options.dataDirectory);
8272
- this.#privateDirectory = join11(this.store.directory, "runtime");
9084
+ this.#privateDirectory = join12(this.store.directory, "runtime");
8273
9085
  this.#chat = new ResearchChatStore(this.store.directory);
8274
9086
  this.#projection = new LiveTimelineProjection({
8275
9087
  state: (id) => this.#state(id),
@@ -8290,7 +9102,7 @@ var ResearchSessions = class {
8290
9102
  });
8291
9103
  }
8292
9104
  options;
8293
- #generation = randomUUID11();
9105
+ #generation = randomUUID12();
8294
9106
  store;
8295
9107
  #privateDirectory;
8296
9108
  #active = /* @__PURE__ */ new Map();
@@ -8359,8 +9171,8 @@ var ResearchSessions = class {
8359
9171
  this.#failedClient = void 0;
8360
9172
  }
8361
9173
  await this.store.initialize();
8362
- await mkdir4(join11(this.#privateDirectory, "scratch"), { recursive: true, mode: 448 });
8363
- if ((await lstat7(this.#privateDirectory)).isSymbolicLink() || await realpath7(this.#privateDirectory) !== resolve3(this.#privateDirectory)) {
9174
+ await mkdir5(join12(this.#privateDirectory, "scratch"), { recursive: true, mode: 448 });
9175
+ if ((await lstat8(this.#privateDirectory)).isSymbolicLink() || await realpath8(this.#privateDirectory) !== resolve4(this.#privateDirectory)) {
8364
9176
  throw new ResearchError("RESEARCH_PRIVATE_DIRECTORY_INVALID");
8365
9177
  }
8366
9178
  const token = await (this.options.resolveToken ?? resolveResearchToken)();
@@ -8412,7 +9224,7 @@ var ResearchSessions = class {
8412
9224
  record.parentSessionId,
8413
9225
  { workingDirectory: record.workingDirectory, rootIdentity: record.rootIdentity },
8414
9226
  this.options.sourceMetadata,
8415
- [resolve3(this.options.dataDirectory), this.#privateDirectory],
9227
+ [resolve4(this.options.dataDirectory), this.#privateDirectory],
8416
9228
  (error, context) => this.options.onError?.(error, {
8417
9229
  ...context,
8418
9230
  researchSessionId: record.researchSessionId
@@ -8549,7 +9361,7 @@ var ResearchSessions = class {
8549
9361
  if (snapshot.parentSessionId !== input.parentSessionId) throw new ResearchError("RESEARCH_SOURCE_MISMATCH");
8550
9362
  const title = z9.string().trim().min(1).max(200).parse(input.title ?? "Research");
8551
9363
  const prompt = z9.string().trim().min(1).max(16e3).parse(input.prompt ?? "Acknowledge the context briefly and wait for my research question.");
8552
- const fingerprint = createHash12("sha256").update(JSON.stringify({
9364
+ const fingerprint = createHash13("sha256").update(JSON.stringify({
8553
9365
  parentSessionId: input.parentSessionId,
8554
9366
  title,
8555
9367
  prompt,
@@ -8672,8 +9484,8 @@ var ResearchSessions = class {
8672
9484
  };
8673
9485
  }
8674
9486
  async list(parentSessionId) {
8675
- const records = (await this.store.list(parentSessionId)).filter((record) => this.options.sourceVisible?.(record.parentSessionId) !== false);
8676
- for (const record of records) {
9487
+ const records2 = (await this.store.list(parentSessionId)).filter((record) => this.options.sourceVisible?.(record.parentSessionId) !== false);
9488
+ for (const record of records2) {
8677
9489
  if (record.state === "deleting" && !this.#mutating.has(record.researchSessionId) && !this.#deletionAttempts.has(record.researchSessionId)) {
8678
9490
  await this.delete(record.researchSessionId);
8679
9491
  }
@@ -8750,7 +9562,7 @@ var ResearchSessions = class {
8750
9562
  compactDisabledReason: "Research context compaction is unavailable"
8751
9563
  });
8752
9564
  }
8753
- async send(sessionId, text, operationId = randomUUID11()) {
9565
+ async send(sessionId, text, operationId = randomUUID12()) {
8754
9566
  if (this.#active.get(sessionId)?.runtime.activity.busy) throw new ResearchError("RESEARCH_BUSY");
8755
9567
  return (await this.sendPrompt(sessionId, text, "immediate", operationId)).messageId;
8756
9568
  }
@@ -9081,7 +9893,7 @@ async function initialize(options) {
9081
9893
  ...parsed.secureMessagePacketBudget === void 0 ? {} : { secureMessagePacketBudget: parsed.secureMessagePacketBudget },
9082
9894
  ...options.codingAgent === void 0 ? {} : { codingAgent: options.codingAgent }
9083
9895
  };
9084
- const identity = DeviceIdentitySchema.parse(options.identity);
9896
+ const identity2 = DeviceIdentitySchema.parse(options.identity);
9085
9897
  const appVersion = options.appVersion ?? z10.object({ version: z10.string().min(1) }).parse(JSON.parse(await readFile4(new URL("../package.json", import.meta.url), "utf8"))).version;
9086
9898
  const lease = options.directoryLease ?? await DataDirectoryLease.acquire(dataDirectory);
9087
9899
  let detach;
@@ -9096,11 +9908,11 @@ async function initialize(options) {
9096
9908
  adapter = options.adapter ?? await createCodingAgentAdapter(
9097
9909
  config,
9098
9910
  (message) => options.logger.warn({ error: { message } }, "coding-agent.diagnostic"),
9099
- { userId: config.userId, machineId: identity.machineId }
9911
+ { userId: config.userId, machineId: identity2.machineId }
9100
9912
  );
9101
9913
  const contentKey = await loadOrCreateContentKey(dataDirectory);
9102
9914
  const cipher = await createPersistentContentCipher(dataDirectory, contentKey);
9103
- const pairingProvider = new CachedPairingProvider(() => requestPairingUri(config, identity, contentKey));
9915
+ const pairingProvider = new CachedPairingProvider(() => requestPairingUri(config, identity2, contentKey));
9104
9916
  const sourceAdapter = adapter;
9105
9917
  const sourceMetadata = async (sessionId) => {
9106
9918
  const access2 = captureResearchAccess(sourceAdapter, sessionId);
@@ -9138,7 +9950,7 @@ async function initialize(options) {
9138
9950
  }, "research.operation.failed")
9139
9951
  });
9140
9952
  class InitializedAgent extends PcAgent {
9141
- identityHash = createHash13("sha256").update(`${config.userId}:${identity.machineId}:${contentKey.keyId}`).digest("hex");
9953
+ identityHash = createHash14("sha256").update(`${config.userId}:${identity2.machineId}:${contentKey.keyId}`).digest("hex");
9142
9954
  #started;
9143
9955
  #starting;
9144
9956
  #closed;
@@ -9178,7 +9990,7 @@ async function initialize(options) {
9178
9990
  }
9179
9991
  return new InitializedAgent(
9180
9992
  config,
9181
- identity,
9993
+ identity2,
9182
9994
  appVersion,
9183
9995
  adapter,
9184
9996
  options.logger,