grix-connector 3.33.2 → 3.33.3

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.
Files changed (39) hide show
  1. package/dist/adapter/acp/acp-adapter.js +2 -2
  2. package/dist/adapter/agy/agy-adapter.js +5 -5
  3. package/dist/adapter/agy/model-list.js +2 -2
  4. package/dist/adapter/claude/claude-adapter.js +5 -5
  5. package/dist/adapter/claude/model-list.js +1 -1
  6. package/dist/adapter/codewhale/codewhale-adapter.js +2 -2
  7. package/dist/adapter/codewhale/model-list.js +2 -2
  8. package/dist/adapter/codex/codex-bridge.js +1 -1
  9. package/dist/adapter/cursor/cursor-adapter.js +7 -7
  10. package/dist/adapter/cursor/fetch-models.js +2 -2
  11. package/dist/adapter/cursor/resolve-command.js +1 -1
  12. package/dist/adapter/openhuman/openhuman-adapter.js +3 -3
  13. package/dist/assets/dsh-bridge/{grix-dsh-bridge-3.33.2.tgz → grix-dsh-bridge-3.33.3.tgz} +0 -0
  14. package/dist/assets/dsh-bridge/manifest.json +5 -5
  15. package/dist/audit/adapters/openclaw/openclaw-audit-adapter.js +1 -1
  16. package/dist/audit/adapters/pi/pi-transcript-source.js +1 -1
  17. package/dist/audit/core/transcript-fingerprint.js +1 -0
  18. package/dist/audit/producer/ipc-audit-producer.js +1 -1
  19. package/dist/bridge/adapter-pool.js +1 -1
  20. package/dist/bridge/bridge.js +9 -9
  21. package/dist/bridge/cli-install-offer.js +1 -0
  22. package/dist/bridge/respawn-manager.js +1 -1
  23. package/dist/core/files/file-serve.js +1 -1
  24. package/dist/core/installer/installer.js +6 -6
  25. package/dist/core/installer/npm-registry.js +3 -3
  26. package/dist/core/installer/prereq-installer.js +1 -1
  27. package/dist/core/proxy/hermes-profile-relay.js +1 -1
  28. package/dist/core/runtime/spawn.js +3 -3
  29. package/dist/core/upgrade/npm-upgrader.js +2 -2
  30. package/dist/core/util/cli-probe.js +2 -2
  31. package/dist/core/util/git-project-root.js +1 -1
  32. package/dist/default-skills/grix-egg/SKILL.md +2 -2
  33. package/dist/default-skills/message-send/SKILL.md +1 -1
  34. package/dist/default-skills/message-unsend/SKILL.md +1 -1
  35. package/dist/manager.js +1 -1
  36. package/dist/service/process-control.js +1 -1
  37. package/openclaw-plugin/audit-recorder-worker.js +170 -44
  38. package/openclaw-plugin/index.js +88 -14
  39. package/package.json +1 -1
@@ -3560,13 +3560,60 @@ var CodexAuditAdapter = class {
3560
3560
  };
3561
3561
 
3562
3562
  // src/audit/adapters/pi/pi-transcript-source.ts
3563
- import { open as open3, stat as stat4 } from "node:fs/promises";
3563
+ import { open as open3 } from "node:fs/promises";
3564
+
3565
+ // src/audit/core/transcript-fingerprint.ts
3566
+ import { createHash as createHash3 } from "node:crypto";
3567
+ var READ_CHUNK_BYTES = 64 * 1024;
3568
+ async function readAndHashFilePrefixRange(handle, start, end) {
3569
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start) {
3570
+ throw new Error("Transcript range must contain safe non-negative offsets");
3571
+ }
3572
+ const digest = createHash3("sha256").update(String(end)).update("\0");
3573
+ const output = Buffer.allocUnsafe(end - start);
3574
+ const chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, Math.max(1, end)));
3575
+ let position = 0;
3576
+ while (position < end) {
3577
+ const requested = Math.min(chunk.length, end - position);
3578
+ const { bytesRead } = await handle.read(chunk, 0, requested, position);
3579
+ if (bytesRead === 0) {
3580
+ throw new Error(`Transcript ended before prefix offset ${end}`);
3581
+ }
3582
+ const value = chunk.subarray(0, bytesRead);
3583
+ digest.update(value);
3584
+ const overlapStart = Math.max(position, start);
3585
+ const overlapEnd = Math.min(position + bytesRead, end);
3586
+ if (overlapStart < overlapEnd) {
3587
+ value.copy(
3588
+ output,
3589
+ overlapStart - start,
3590
+ overlapStart - position,
3591
+ overlapEnd - position
3592
+ );
3593
+ }
3594
+ position += bytesRead;
3595
+ }
3596
+ return { buffer: output, fingerprint: digest.digest("hex") };
3597
+ }
3598
+
3599
+ // src/audit/adapters/pi/pi-transcript-source.ts
3564
3600
  function isRecord4(value) {
3565
3601
  return typeof value === "object" && value !== null && !Array.isArray(value);
3566
3602
  }
3567
3603
  function finiteOffset3(value, fallback) {
3568
3604
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : fallback;
3569
3605
  }
3606
+ function recordIntegrityFailure(evidence, transcriptPath, detail) {
3607
+ evidence.anomalies.push({
3608
+ code: "pi_transcript_source_changed",
3609
+ message: `${transcriptPath}: ${detail}`
3610
+ });
3611
+ evidence.gaps.push({
3612
+ category: "main_transcript",
3613
+ reason: "correlation_failed",
3614
+ detail: `${transcriptPath}: ${detail}`
3615
+ });
3616
+ }
3570
3617
  async function collectPiTranscript(boundary) {
3571
3618
  const transcriptPath = typeof boundary.transcriptPath === "string" ? boundary.transcriptPath : void 0;
3572
3619
  if (!transcriptPath) {
@@ -3591,31 +3638,57 @@ async function collectPiTranscript(boundary) {
3591
3638
  sourcePaths: [transcriptPath]
3592
3639
  };
3593
3640
  try {
3594
- const sourceStat = await stat4(transcriptPath);
3595
- const requestedStart = finiteOffset3(boundary.transcriptStartOffset, 0);
3596
- const requestedEnd = typeof boundary.transcriptEndOffset === "number" ? finiteOffset3(boundary.transcriptEndOffset, sourceStat.size) : sourceStat.size;
3597
- if (requestedStart > sourceStat.size) {
3598
- evidence.anomalies.push({
3599
- code: "pi_transcript_boundary_out_of_range",
3600
- message: `${transcriptPath}: start ${requestedStart} exceeds size ${sourceStat.size}`
3601
- });
3602
- evidence.gaps.push({
3603
- category: "main_transcript",
3604
- reason: "correlation_failed",
3605
- detail: `${transcriptPath}:${requestedStart}-${requestedEnd}`
3606
- });
3607
- return evidence;
3608
- }
3609
- const start = requestedStart;
3610
- const end = Math.min(Math.max(requestedEnd, start), sourceStat.size);
3611
- const length = end - start;
3612
- if (length === 0) return evidence;
3613
3641
  const handle = await open3(transcriptPath, "r");
3614
3642
  let buffer;
3643
+ let start;
3615
3644
  try {
3616
- buffer = Buffer.allocUnsafe(length);
3617
- const { bytesRead } = await handle.read(buffer, 0, length, start);
3618
- buffer = buffer.subarray(0, bytesRead);
3645
+ const sourceStat = await handle.stat();
3646
+ const requestedStart = finiteOffset3(boundary.transcriptStartOffset, 0);
3647
+ const requestedEnd = typeof boundary.transcriptEndOffset === "number" ? finiteOffset3(boundary.transcriptEndOffset, sourceStat.size) : sourceStat.size;
3648
+ if (requestedStart > sourceStat.size || requestedEnd > sourceStat.size) {
3649
+ evidence.anomalies.push({
3650
+ code: "pi_transcript_boundary_out_of_range",
3651
+ message: `${transcriptPath}: boundary ${requestedStart}-${requestedEnd} exceeds size ${sourceStat.size}`
3652
+ });
3653
+ evidence.gaps.push({
3654
+ category: "main_transcript",
3655
+ reason: "correlation_failed",
3656
+ detail: `${transcriptPath}:${requestedStart}-${requestedEnd}`
3657
+ });
3658
+ return evidence;
3659
+ }
3660
+ const expectedIdentity = typeof boundary.transcriptFileIdentity === "string" ? boundary.transcriptFileIdentity : void 0;
3661
+ const actualIdentity = `${sourceStat.dev}:${sourceStat.ino}`;
3662
+ if (expectedIdentity && actualIdentity !== expectedIdentity) {
3663
+ recordIntegrityFailure(
3664
+ evidence,
3665
+ transcriptPath,
3666
+ `identity ${actualIdentity} does not match ${expectedIdentity}`
3667
+ );
3668
+ return evidence;
3669
+ }
3670
+ const expectedFingerprint = typeof boundary.transcriptPrefixFingerprint === "string" ? boundary.transcriptPrefixFingerprint : void 0;
3671
+ start = requestedStart;
3672
+ const end = Math.max(requestedEnd, start);
3673
+ const length = end - start;
3674
+ if (expectedFingerprint) {
3675
+ const verified = await readAndHashFilePrefixRange(handle, start, end);
3676
+ if (verified.fingerprint !== expectedFingerprint) {
3677
+ recordIntegrityFailure(
3678
+ evidence,
3679
+ transcriptPath,
3680
+ "closed transcript prefix fingerprint changed"
3681
+ );
3682
+ return evidence;
3683
+ }
3684
+ buffer = verified.buffer;
3685
+ } else {
3686
+ if (length === 0) return evidence;
3687
+ buffer = Buffer.allocUnsafe(length);
3688
+ const { bytesRead } = await handle.read(buffer, 0, length, start);
3689
+ buffer = buffer.subarray(0, bytesRead);
3690
+ }
3691
+ if (length === 0) return evidence;
3619
3692
  } finally {
3620
3693
  await handle.close();
3621
3694
  }
@@ -4273,7 +4346,7 @@ var PiAuditAdapter = class {
4273
4346
  };
4274
4347
 
4275
4348
  // src/audit/adapters/acp/generic-acp-audit-adapter.ts
4276
- import { readFile as readFile3, readdir as readdir2, stat as stat5 } from "node:fs/promises";
4349
+ import { readFile as readFile3, readdir as readdir2, stat as stat4 } from "node:fs/promises";
4277
4350
  import { homedir } from "node:os";
4278
4351
  import { basename as basename4, join as join2 } from "node:path";
4279
4352
  var DEFAULT_CAPTURE4 = Object.freeze({
@@ -4364,7 +4437,7 @@ async function collectQwenUsage(job, providerSessionId, cwd) {
4364
4437
  `${providerSessionId}.jsonl`
4365
4438
  );
4366
4439
  try {
4367
- await stat5(sourcePath);
4440
+ await stat4(sourcePath);
4368
4441
  } catch {
4369
4442
  return { records: [] };
4370
4443
  }
@@ -4413,7 +4486,7 @@ async function locateGeminiSession(providerSessionId, cwd) {
4413
4486
  const candidates = names.filter((name) => name.startsWith("session-") && name.includes(shortId) && (name.endsWith(".json") || name.endsWith(".jsonl")));
4414
4487
  const ranked = await Promise.all(candidates.map(async (name) => {
4415
4488
  const sourcePath = join2(chats, name);
4416
- return { sourcePath, modifiedAt: (await stat5(sourcePath)).mtimeMs };
4489
+ return { sourcePath, modifiedAt: (await stat4(sourcePath)).mtimeMs };
4417
4490
  }));
4418
4491
  return ranked.sort((left, right) => right.modifiedAt - left.modifiedAt)[0]?.sourcePath;
4419
4492
  } catch {
@@ -4895,7 +4968,7 @@ var GenericAcpAuditAdapter = class {
4895
4968
  };
4896
4969
 
4897
4970
  // src/audit/adapters/acp/kimi-audit-adapter.ts
4898
- import { readFile as readFile4, readdir as readdir3, stat as stat6 } from "node:fs/promises";
4971
+ import { readFile as readFile4, readdir as readdir3, stat as stat5 } from "node:fs/promises";
4899
4972
  import { homedir as homedir2 } from "node:os";
4900
4973
  import { basename as basename5, dirname, join as join3 } from "node:path";
4901
4974
  var DEFAULT_CAPTURE5 = Object.freeze({
@@ -5027,7 +5100,7 @@ async function locateSessionDirectory(job) {
5027
5100
  const path = join3(root, workspace.name, providerSessionId);
5028
5101
  const wirePath = join3(path, "agents", "main", "wire.jsonl");
5029
5102
  try {
5030
- const wireStat = await stat6(wirePath);
5103
+ const wireStat = await stat5(wirePath);
5031
5104
  let cwdMatches = false;
5032
5105
  const cwd = stringValue5(job.boundary.cwd);
5033
5106
  if (cwd) {
@@ -5868,7 +5941,7 @@ var KimiAuditAdapter = class {
5868
5941
  };
5869
5942
 
5870
5943
  // src/audit/adapters/acp/reasonix-transcript-source.ts
5871
- import { open as open4, stat as stat7 } from "node:fs/promises";
5944
+ import { open as open4, stat as stat6 } from "node:fs/promises";
5872
5945
  function isRecord8(value) {
5873
5946
  return typeof value === "object" && value !== null && !Array.isArray(value);
5874
5947
  }
@@ -5921,7 +5994,7 @@ async function collectReasonixTranscript(boundary) {
5921
5994
  return evidence;
5922
5995
  }
5923
5996
  try {
5924
- const sourceStat = await stat7(transcriptPath);
5997
+ const sourceStat = await stat6(transcriptPath);
5925
5998
  const requestedStart = finiteOffset4(boundary.transcriptStartOffset, 0);
5926
5999
  const requestedEnd = typeof boundary.transcriptEndOffset === "number" ? finiteOffset4(boundary.transcriptEndOffset, sourceStat.size) : sourceStat.size;
5927
6000
  if (requestedStart > sourceStat.size) {
@@ -8232,23 +8305,76 @@ function usageSpan(job, parentSpanId, sequence, at, raw, requestId2, modelId, pr
8232
8305
  }
8233
8306
 
8234
8307
  // src/audit/adapters/openclaw/openclaw-audit-adapter.ts
8235
- import { readFile as readFile5 } from "node:fs/promises";
8236
- import { dirname as dirname2, join as join4 } from "node:path";
8308
+ import { readFile as readFile5, readdir as readdir4, stat as stat7 } from "node:fs/promises";
8309
+ import { basename as basename6, dirname as dirname2, join as join4 } from "node:path";
8237
8310
  function isRecord14(value) {
8238
8311
  return typeof value === "object" && value !== null && !Array.isArray(value);
8239
8312
  }
8240
- async function resolveTranscriptBoundary(boundary) {
8241
- if (typeof boundary.transcriptPath === "string") return boundary;
8313
+ function replaceTranscriptPath(boundary, transcriptPath) {
8314
+ const next = { ...boundary, transcriptPath };
8315
+ delete next.transcriptStartOffset;
8316
+ delete next.transcriptEndOffset;
8317
+ delete next.transcriptFileIdentity;
8318
+ delete next.transcriptPrefixFingerprint;
8319
+ return next;
8320
+ }
8321
+ async function fileIdentity(path) {
8322
+ try {
8323
+ const source = await stat7(path);
8324
+ return `${source.dev}:${source.ino}`;
8325
+ } catch {
8326
+ return void 0;
8327
+ }
8328
+ }
8329
+ async function resolveResetTranscriptPath(transcriptPath, expectedIdentity) {
8330
+ if (!expectedIdentity) return void 0;
8331
+ try {
8332
+ const directory = dirname2(transcriptPath);
8333
+ const prefix = `${basename6(transcriptPath)}.reset`;
8334
+ const candidates = (await readdir4(directory)).filter((entry) => entry.startsWith(prefix)).sort().reverse();
8335
+ for (const entry of candidates) {
8336
+ const candidate = join4(directory, entry);
8337
+ if (await fileIdentity(candidate) === expectedIdentity) return candidate;
8338
+ }
8339
+ } catch {
8340
+ }
8341
+ return void 0;
8342
+ }
8343
+ async function resolveCurrentTranscriptPath(boundary) {
8242
8344
  const storePath = typeof boundary.sessionStorePath === "string" ? boundary.sessionStorePath : void 0;
8243
8345
  const sessionKey = typeof boundary.routeSessionKey === "string" ? boundary.routeSessionKey : void 0;
8244
- if (!storePath || !sessionKey) return boundary;
8346
+ if (!storePath || !sessionKey) return void 0;
8245
8347
  try {
8246
8348
  const parsed = JSON.parse(await readFile5(storePath, "utf8"));
8247
- if (!isRecord14(parsed) || !isRecord14(parsed[sessionKey])) return boundary;
8349
+ if (!isRecord14(parsed) || !isRecord14(parsed[sessionKey])) return void 0;
8248
8350
  const session = parsed[sessionKey];
8249
- const sessionFile = typeof session.sessionFile === "string" ? session.sessionFile : typeof session.sessionId === "string" ? join4(dirname2(storePath), `${session.sessionId}.jsonl`) : void 0;
8250
- return sessionFile ? { ...boundary, transcriptPath: sessionFile } : boundary;
8351
+ return typeof session.sessionFile === "string" ? session.sessionFile : typeof session.sessionId === "string" ? join4(dirname2(storePath), `${session.sessionId}.jsonl`) : void 0;
8352
+ } catch {
8353
+ return void 0;
8354
+ }
8355
+ }
8356
+ async function resolveTranscriptBoundary(boundary) {
8357
+ const transcriptPath = typeof boundary.transcriptPath === "string" ? boundary.transcriptPath : void 0;
8358
+ if (!transcriptPath) {
8359
+ const currentTranscriptPath = await resolveCurrentTranscriptPath(boundary);
8360
+ return currentTranscriptPath ? replaceTranscriptPath(boundary, currentTranscriptPath) : boundary;
8361
+ }
8362
+ try {
8363
+ await stat7(transcriptPath);
8364
+ return boundary;
8251
8365
  } catch {
8366
+ const expectedIdentity = typeof boundary.transcriptFileIdentity === "string" ? boundary.transcriptFileIdentity : void 0;
8367
+ const resetTranscriptPath = await resolveResetTranscriptPath(
8368
+ transcriptPath,
8369
+ expectedIdentity
8370
+ );
8371
+ if (resetTranscriptPath) {
8372
+ return { ...boundary, transcriptPath: resetTranscriptPath };
8373
+ }
8374
+ const currentTranscriptPath = await resolveCurrentTranscriptPath(boundary);
8375
+ if (currentTranscriptPath && expectedIdentity && await fileIdentity(currentTranscriptPath) === expectedIdentity) {
8376
+ return { ...boundary, transcriptPath: currentTranscriptPath };
8377
+ }
8252
8378
  return boundary;
8253
8379
  }
8254
8380
  }
@@ -8317,7 +8443,7 @@ var OpenClawAuditAdapter = class {
8317
8443
 
8318
8444
  // src/audit/storage/file-audit-replay-store.ts
8319
8445
  import {
8320
- createHash as createHash3,
8446
+ createHash as createHash4,
8321
8447
  randomUUID
8322
8448
  } from "node:crypto";
8323
8449
  import {
@@ -8325,12 +8451,12 @@ import {
8325
8451
  open as open5,
8326
8452
  mkdir,
8327
8453
  readFile as readFile6,
8328
- readdir as readdir4,
8454
+ readdir as readdir5,
8329
8455
  rename,
8330
8456
  rm,
8331
8457
  writeFile
8332
8458
  } from "node:fs/promises";
8333
- import { basename as basename6, dirname as dirname3, join as join5, relative, resolve as resolve3, sep as sep3 } from "node:path";
8459
+ import { basename as basename7, dirname as dirname3, join as join5, relative, resolve as resolve3, sep as sep3 } from "node:path";
8334
8460
  var SHA256_PATTERN = /^[a-f0-9]{64}$/;
8335
8461
  var NO_CAPTURE = Object.freeze({
8336
8462
  inputOutput: false,
@@ -8728,7 +8854,7 @@ var FileAuditReplayStore = class {
8728
8854
  await mkdir(directory, { recursive: true, mode: 448 });
8729
8855
  const temporary = join5(
8730
8856
  directory,
8731
- `.${basename6(path)}.${process.pid}.${randomUUID()}.tmp`
8857
+ `.${basename7(path)}.${process.pid}.${randomUUID()}.tmp`
8732
8858
  );
8733
8859
  try {
8734
8860
  await writeFile(temporary, body, { mode: 384, flag: "wx" });
@@ -8860,7 +8986,7 @@ function encodeJson(value) {
8860
8986
  `, "utf8");
8861
8987
  }
8862
8988
  function sha256(value) {
8863
- return createHash3("sha256").update(value).digest("hex");
8989
+ return createHash4("sha256").update(value).digest("hex");
8864
8990
  }
8865
8991
  function idKey(id) {
8866
8992
  return sha256(Buffer.from(id, "utf8"));
@@ -8876,7 +9002,7 @@ async function pathExists(path) {
8876
9002
  }
8877
9003
  async function readDirectory(path) {
8878
9004
  try {
8879
- return await readdir4(path);
9005
+ return await readdir5(path);
8880
9006
  } catch (error) {
8881
9007
  if (isNotFound(error)) return [];
8882
9008
  throw error;
@@ -2489,7 +2489,10 @@ var IpcAuditProducer = class {
2489
2489
  try {
2490
2490
  child = fork(this.workerPath, [], {
2491
2491
  stdio: ["ignore", "ignore", "ignore", "ipc"],
2492
- serialization: "json"
2492
+ serialization: "json",
2493
+ // ForkOptions 类型定义缺 windowsHide(Node 运行时实际支持),无控制台父进程
2494
+ // 下缺省会新建可见控制台窗口,故加类型断言补上。
2495
+ windowsHide: true
2493
2496
  });
2494
2497
  } catch (error) {
2495
2498
  this.diagnostic({
@@ -3834,7 +3837,7 @@ function isTailnetIPv4(addr) {
3834
3837
  }
3835
3838
  function detectViaCLI() {
3836
3839
  return new Promise((resolve4) => {
3837
- execFile2("tailscale", ["ip", "-4"], { timeout: 3e3 }, (err2, stdout) => {
3840
+ execFile2("tailscale", ["ip", "-4"], { timeout: 3e3, windowsHide: true }, (err2, stdout) => {
3838
3841
  if (err2) {
3839
3842
  resolve4(void 0);
3840
3843
  return;
@@ -7383,7 +7386,7 @@ function createAppendOnlyReplyStream(params) {
7383
7386
 
7384
7387
  // src/openclaw/audit-runtime.ts
7385
7388
  import { randomUUID as randomUUID6 } from "node:crypto";
7386
- import { stat as stat2, readFile as readFile4 } from "node:fs/promises";
7389
+ import { open as open2, readFile as readFile4 } from "node:fs/promises";
7387
7390
  import { dirname as dirname8, join as join11 } from "node:path";
7388
7391
  import { fileURLToPath as fileURLToPath4 } from "node:url";
7389
7392
 
@@ -8258,6 +8261,41 @@ function outcomeForStatus(status) {
8258
8261
  return status === "canceled" ? "cancelled" : "failed";
8259
8262
  }
8260
8263
 
8264
+ // src/audit/core/transcript-fingerprint.ts
8265
+ import { createHash as createHash2 } from "node:crypto";
8266
+ var READ_CHUNK_BYTES = 64 * 1024;
8267
+ async function hashFilePrefixes(handle, lengths) {
8268
+ const uniqueLengths = [...new Set(lengths)].sort((left, right) => left - right);
8269
+ if (uniqueLengths.some((length) => !Number.isSafeInteger(length) || length < 0)) {
8270
+ throw new Error("Transcript prefix lengths must be non-negative safe integers");
8271
+ }
8272
+ const digests = new Map(uniqueLengths.map((length) => [
8273
+ length,
8274
+ createHash2("sha256").update(String(length)).update("\0")
8275
+ ]));
8276
+ const maxLength = uniqueLengths.at(-1) ?? 0;
8277
+ const chunk = Buffer.allocUnsafe(
8278
+ Math.min(READ_CHUNK_BYTES, Math.max(1, maxLength))
8279
+ );
8280
+ let position = 0;
8281
+ while (position < maxLength) {
8282
+ const requested = Math.min(chunk.length, maxLength - position);
8283
+ const { bytesRead } = await handle.read(chunk, 0, requested, position);
8284
+ if (bytesRead === 0) {
8285
+ throw new Error(`Transcript ended before prefix offset ${maxLength}`);
8286
+ }
8287
+ for (const [length, digest] of digests) {
8288
+ if (position >= length) continue;
8289
+ digest.update(chunk.subarray(0, Math.min(bytesRead, length - position)));
8290
+ }
8291
+ position += bytesRead;
8292
+ }
8293
+ return new Map([...digests].map(([length, digest]) => [
8294
+ length,
8295
+ digest.digest("hex")
8296
+ ]));
8297
+ }
8298
+
8261
8299
  // src/openclaw/audit-runtime.ts
8262
8300
  function isRecord3(value) {
8263
8301
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -8273,12 +8311,29 @@ async function resolveTranscriptPath(storePath, sessionKey) {
8273
8311
  return void 0;
8274
8312
  }
8275
8313
  }
8276
- async function fileSize(path7) {
8314
+ async function fileSnapshot(path7, comparisonLength) {
8277
8315
  if (!path7) return void 0;
8316
+ const handle = await open2(path7, "r").catch(() => void 0);
8317
+ if (!handle) return void 0;
8278
8318
  try {
8279
- return (await stat2(path7)).size;
8319
+ const source = await handle.stat();
8320
+ const lengths = [source.size];
8321
+ if (comparisonLength !== void 0 && Number.isSafeInteger(comparisonLength) && comparisonLength >= 0 && comparisonLength <= source.size) {
8322
+ lengths.push(comparisonLength);
8323
+ }
8324
+ const fingerprints = await hashFilePrefixes(handle, lengths);
8325
+ const prefixFingerprint = fingerprints.get(source.size);
8326
+ if (!prefixFingerprint) return void 0;
8327
+ return {
8328
+ ...comparisonLength === void 0 ? {} : { comparisonFingerprint: fingerprints.get(comparisonLength) },
8329
+ identity: `${source.dev}:${source.ino}`,
8330
+ prefixFingerprint,
8331
+ size: source.size
8332
+ };
8280
8333
  } catch {
8281
8334
  return void 0;
8335
+ } finally {
8336
+ await handle.close();
8282
8337
  }
8283
8338
  }
8284
8339
  async function captureOpenClawAuditBoundary(input) {
@@ -8286,23 +8341,42 @@ async function captureOpenClawAuditBoundary(input) {
8286
8341
  input.sessionStorePath,
8287
8342
  input.routeSessionKey
8288
8343
  );
8289
- const transcriptStartOffset = await fileSize(transcriptPath);
8344
+ const transcriptSnapshot = await fileSnapshot(transcriptPath);
8290
8345
  return {
8291
8346
  adapterType: "openclaw",
8292
8347
  ...input,
8293
8348
  ...transcriptPath ? { transcriptPath } : {},
8294
- ...transcriptStartOffset === void 0 ? {} : { transcriptStartOffset }
8349
+ ...transcriptSnapshot === void 0 ? {} : {
8350
+ transcriptFileIdentity: transcriptSnapshot.identity,
8351
+ transcriptPrefixFingerprint: transcriptSnapshot.prefixFingerprint,
8352
+ transcriptStartOffset: transcriptSnapshot.size
8353
+ }
8295
8354
  };
8296
8355
  }
8297
8356
  async function closeOpenClawAuditBoundary(boundary) {
8298
- const transcriptPath = typeof boundary.transcriptPath === "string" ? boundary.transcriptPath : await resolveTranscriptPath(
8357
+ const capturedTranscriptPath = typeof boundary.transcriptPath === "string" ? boundary.transcriptPath : void 0;
8358
+ const currentTranscriptPath = await resolveTranscriptPath(
8299
8359
  boundary.sessionStorePath,
8300
8360
  boundary.routeSessionKey
8301
8361
  );
8302
- const transcriptEndOffset = await fileSize(transcriptPath);
8362
+ const transcriptPath = currentTranscriptPath ?? capturedTranscriptPath;
8363
+ const capturedIdentity = typeof boundary.transcriptFileIdentity === "string" ? boundary.transcriptFileIdentity : void 0;
8364
+ const capturedPrefixFingerprint = typeof boundary.transcriptPrefixFingerprint === "string" ? boundary.transcriptPrefixFingerprint : void 0;
8365
+ const capturedStartOffset = typeof boundary.transcriptStartOffset === "number" ? boundary.transcriptStartOffset : void 0;
8366
+ const transcriptSnapshot = await fileSnapshot(
8367
+ transcriptPath,
8368
+ capturedStartOffset
8369
+ );
8370
+ const currentPrefixFingerprint = transcriptSnapshot?.comparisonFingerprint;
8371
+ const transcriptReset = currentTranscriptPath !== void 0 && currentTranscriptPath !== capturedTranscriptPath || capturedIdentity !== void 0 && transcriptSnapshot !== void 0 && transcriptSnapshot.identity !== capturedIdentity || capturedStartOffset !== void 0 && transcriptSnapshot !== void 0 && transcriptSnapshot.size < capturedStartOffset || capturedPrefixFingerprint !== void 0 && transcriptSnapshot !== void 0 && currentPrefixFingerprint !== capturedPrefixFingerprint;
8303
8372
  return {
8304
8373
  ...transcriptPath ? { transcriptPath } : {},
8305
- ...transcriptEndOffset === void 0 ? {} : { transcriptEndOffset }
8374
+ ...transcriptReset ? { transcriptStartOffset: 0 } : {},
8375
+ ...transcriptSnapshot === void 0 ? {} : {
8376
+ transcriptFileIdentity: transcriptSnapshot.identity,
8377
+ transcriptPrefixFingerprint: transcriptSnapshot.prefixFingerprint,
8378
+ transcriptEndOffset: transcriptSnapshot.size
8379
+ }
8306
8380
  };
8307
8381
  }
8308
8382
  function openClawAuditConfigurationError(error) {
@@ -11602,7 +11676,7 @@ function registerGrixAdminCli(params) {
11602
11676
  }
11603
11677
 
11604
11678
  // src/openclaw/skill-tools/delegated-skill-tool.ts
11605
- import { createHash as createHash2 } from "node:crypto";
11679
+ import { createHash as createHash3 } from "node:crypto";
11606
11680
  var DelegatedSkillToolSchema = {
11607
11681
  type: "object",
11608
11682
  additionalProperties: false,
@@ -11648,7 +11722,7 @@ function buildSkillTaskMessage(params) {
11648
11722
  ].join("\n");
11649
11723
  }
11650
11724
  function buildInternalIdempotencyKey(params) {
11651
- const digest = createHash2("sha256").update(params.spec.name).update("\n").update(params.toolCallId).update("\n").update(params.subagentSessionKey).update("\n").update(params.task).digest("hex");
11725
+ const digest = createHash3("sha256").update(params.spec.name).update("\n").update(params.toolCallId).update("\n").update(params.subagentSessionKey).update("\n").update(params.task).digest("hex");
11652
11726
  return `plugin:${params.spec.name}:subagent:${digest}`;
11653
11727
  }
11654
11728
  function createDelegatedSkillTool(params) {
@@ -12100,7 +12174,7 @@ function buildConversationCard(params) {
12100
12174
  }
12101
12175
 
12102
12176
  // src/openclaw/egg/package-downloader.ts
12103
- import { createHash as createHash3 } from "node:crypto";
12177
+ import { createHash as createHash4 } from "node:crypto";
12104
12178
  var PackageDownloadError = class extends Error {
12105
12179
  constructor(message) {
12106
12180
  super(`[egg:download] ${message}`);
@@ -12119,7 +12193,7 @@ async function downloadPackage(params) {
12119
12193
  const arrayBuffer = await response.arrayBuffer();
12120
12194
  const buffer = Buffer.from(arrayBuffer);
12121
12195
  if (expectedHash) {
12122
- const actualHash = createHash3(hashAlgorithm).update(buffer).digest("hex");
12196
+ const actualHash = createHash4(hashAlgorithm).update(buffer).digest("hex");
12123
12197
  if (actualHash !== expectedHash.toLowerCase()) {
12124
12198
  throw new PackageDownloadError(
12125
12199
  `hash mismatch: expected ${expectedHash}, got ${actualHash}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "3.33.2",
3
+ "version": "3.33.3",
4
4
  "description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",