@fangyb/ahchat-bridge 0.1.40 → 0.1.42

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
@@ -3801,7 +3801,7 @@ function ensureDir(dirPath) {
3801
3801
 
3802
3802
  // src/start.ts
3803
3803
  import os15 from "os";
3804
- import path33 from "path";
3804
+ import path34 from "path";
3805
3805
  import crypto5 from "crypto";
3806
3806
 
3807
3807
  // ../logger/src/types.ts
@@ -4604,11 +4604,11 @@ var RotatingFileStream = class extends Writable {
4604
4604
  timeout;
4605
4605
  timeoutPromise;
4606
4606
  constructor(generator, options) {
4607
- const { encoding, history, maxFiles, maxSize, path: path34 } = options;
4607
+ const { encoding, history, maxFiles, maxSize, path: path35 } = options;
4608
4608
  super({ decodeStrings: true, defaultEncoding: encoding });
4609
4609
  this.createGzip = createGzip;
4610
4610
  this.exec = exec;
4611
- this.filename = path34 + generator(null);
4611
+ this.filename = path35 + generator(null);
4612
4612
  this.fsCreateReadStream = createReadStream;
4613
4613
  this.fsCreateWriteStream = createWriteStream;
4614
4614
  this.fsOpen = open;
@@ -4620,7 +4620,7 @@ var RotatingFileStream = class extends Writable {
4620
4620
  this.options = options;
4621
4621
  this.stdout = process.stdout;
4622
4622
  if (maxFiles || maxSize)
4623
- options.history = path34 + (history ? history : this.generator(null) + ".txt");
4623
+ options.history = path35 + (history ? history : this.generator(null) + ".txt");
4624
4624
  this.on("close", () => this.finished ? null : this.emit("finish"));
4625
4625
  this.on("finish", () => this.finished = this.clear());
4626
4626
  (async () => {
@@ -4748,9 +4748,9 @@ var RotatingFileStream = class extends Writable {
4748
4748
  return this.move();
4749
4749
  }
4750
4750
  async findName() {
4751
- const { interval, path: path34, intervalBoundary } = this.options;
4751
+ const { interval, path: path35, intervalBoundary } = this.options;
4752
4752
  for (let index = 1; index < 1e3; ++index) {
4753
- const filename = path34 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
4753
+ const filename = path35 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
4754
4754
  if (!await exists(filename))
4755
4755
  return filename;
4756
4756
  }
@@ -4780,11 +4780,11 @@ var RotatingFileStream = class extends Writable {
4780
4780
  return this.unlink(filename);
4781
4781
  }
4782
4782
  async classical() {
4783
- const { compress, path: path34, rotate } = this.options;
4783
+ const { compress, path: path35, rotate } = this.options;
4784
4784
  let rotatedName = "";
4785
4785
  for (let count = rotate; count > 0; --count) {
4786
- const currName = path34 + this.generator(count);
4787
- const prevName = count === 1 ? this.filename : path34 + this.generator(count - 1);
4786
+ const currName = path35 + this.generator(count);
4787
+ const prevName = count === 1 ? this.filename : path35 + this.generator(count - 1);
4788
4788
  if (!await exists(prevName))
4789
4789
  continue;
4790
4790
  if (!rotatedName)
@@ -5908,6 +5908,25 @@ Transform tasks into verifiable goals:
5908
5908
  - "Refactor X" \u2192 ensure tests pass before and after.
5909
5909
  For multi-step tasks, state a brief plan with verification checks.
5910
5910
  `.trim();
5911
+ var GROUP_ONLY_SECTION_HEADERS = [
5912
+ "# \u7FA4\u804A\u516C\u7406\uFF08Group Chat Axiom \u2014 \u6700\u9AD8\u4F18\u5148\u7EA7\uFF09",
5913
+ "# Runtime payload \u2014 how to read unread messages",
5914
+ "# Group chat \u2014 when to speak",
5915
+ "# Group chat \u2014 recency & commitment",
5916
+ "# Length & conciseness in group chat",
5917
+ "# Group chat \u2014 shared task board",
5918
+ "# Group chat \u2014 batched inbox handling",
5919
+ "# \u7FA4\u804A\u4EA7\u7269\u7EAA\u5F8B\uFF08What to capture in group chats\uFF09",
5920
+ "# When a user joins or leaves your group"
5921
+ ];
5922
+ function stripGroupOnlySections(full, headers) {
5923
+ const headerSet = new Set(headers);
5924
+ return full.split(/\n(?=# )/).filter((section) => !headerSet.has(section.split("\n", 1)[0].trim())).join("\n").trim();
5925
+ }
5926
+ var PLATFORM_AGENT_RULES_SINGLE = stripGroupOnlySections(
5927
+ PLATFORM_AGENT_RULES,
5928
+ GROUP_ONLY_SECTION_HEADERS
5929
+ );
5911
5930
  var FAN_OUT_TRACE_TTL_MS = 10 * 6e4;
5912
5931
  var MAX_FILE_SIZE = 20 * 1024 * 1024;
5913
5932
  var MAX_IMAGE_SIZE = 10 * 1024 * 1024;
@@ -6057,6 +6076,14 @@ function assertArrayPayloadField(type, payload, field) {
6057
6076
  throw invalidWsMessage(type, field);
6058
6077
  }
6059
6078
  }
6079
+ function assertWorkdirSignalsPayloadField(type, payload, field) {
6080
+ assertArrayPayloadField(type, payload, field);
6081
+ for (const [index, item] of payload[field].entries()) {
6082
+ if (!isPlainRecord(item) || typeof item.toolName !== "string") {
6083
+ throw invalidWsMessage(type, `${field}[${index}].toolName`);
6084
+ }
6085
+ }
6086
+ }
6060
6087
  function assertRecordPayloadField(type, payload, field) {
6061
6088
  if (!isPlainRecord(payload[field])) {
6062
6089
  throw invalidWsMessage(type, field);
@@ -6137,6 +6164,9 @@ function validateWSMessageShape(msg) {
6137
6164
  "traceId"
6138
6165
  ]);
6139
6166
  assertArrayPayloadField(type, payload, "contentBlocks");
6167
+ if ("workdirSignals" in payload && payload.workdirSignals !== void 0) {
6168
+ assertWorkdirSignalsPayloadField(type, payload, "workdirSignals");
6169
+ }
6140
6170
  return;
6141
6171
  }
6142
6172
  case "agent:turn_complete": {
@@ -8311,14 +8341,14 @@ function agentIdArray(value) {
8311
8341
  function normalizeModelScopeSkill(value) {
8312
8342
  if (!isRecord2(value)) return null;
8313
8343
  const id = stringValue(value.id);
8314
- const path34 = stringValue(value.path);
8344
+ const path35 = stringValue(value.path);
8315
8345
  const name = stringValue(value.name);
8316
8346
  const displayName = stringValue(value.displayName, name);
8317
8347
  const url2 = stringValue(value.url);
8318
- if (!id || !path34 || !name || !displayName || !url2) return null;
8348
+ if (!id || !path35 || !name || !displayName || !url2) return null;
8319
8349
  return {
8320
8350
  id,
8321
- path: path34,
8351
+ path: path35,
8322
8352
  name,
8323
8353
  displayName,
8324
8354
  description: stringValue(value.description),
@@ -8705,9 +8735,9 @@ var AgentMemoryStore = class {
8705
8735
  import { spawn as nodeSpawn } from "child_process";
8706
8736
  import { createHash } from "crypto";
8707
8737
  import fsSync from "fs";
8708
- import fs6 from "fs/promises";
8738
+ import fs7 from "fs/promises";
8709
8739
  import os7 from "os";
8710
- import path13 from "path";
8740
+ import path14 from "path";
8711
8741
  import * as sdk2 from "@anthropic-ai/claude-agent-sdk";
8712
8742
 
8713
8743
  // src/attachmentText.ts
@@ -9581,6 +9611,8 @@ var HttpMcpAuditClient = class {
9581
9611
  };
9582
9612
 
9583
9613
  // src/neuralMcpServer.ts
9614
+ import fs5 from "fs/promises";
9615
+ import path10 from "path";
9584
9616
  import * as sdk from "@anthropic-ai/claude-agent-sdk";
9585
9617
 
9586
9618
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -10349,10 +10381,10 @@ function mergeDefs(...defs) {
10349
10381
  function cloneDef(schema) {
10350
10382
  return mergeDefs(schema._zod.def);
10351
10383
  }
10352
- function getElementAtPath(obj, path34) {
10353
- if (!path34)
10384
+ function getElementAtPath(obj, path35) {
10385
+ if (!path35)
10354
10386
  return obj;
10355
- return path34.reduce((acc, key) => acc?.[key], obj);
10387
+ return path35.reduce((acc, key) => acc?.[key], obj);
10356
10388
  }
10357
10389
  function promiseAllObject(promisesObj) {
10358
10390
  const keys = Object.keys(promisesObj);
@@ -10761,11 +10793,11 @@ function explicitlyAborted(x, startIndex = 0) {
10761
10793
  }
10762
10794
  return false;
10763
10795
  }
10764
- function prefixIssues(path34, issues) {
10796
+ function prefixIssues(path35, issues) {
10765
10797
  return issues.map((iss) => {
10766
10798
  var _a3;
10767
10799
  (_a3 = iss).path ?? (_a3.path = []);
10768
- iss.path.unshift(path34);
10800
+ iss.path.unshift(path35);
10769
10801
  return iss;
10770
10802
  });
10771
10803
  }
@@ -10912,16 +10944,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
10912
10944
  }
10913
10945
  function formatError(error51, mapper = (issue2) => issue2.message) {
10914
10946
  const fieldErrors = { _errors: [] };
10915
- const processError = (error52, path34 = []) => {
10947
+ const processError = (error52, path35 = []) => {
10916
10948
  for (const issue2 of error52.issues) {
10917
10949
  if (issue2.code === "invalid_union" && issue2.errors.length) {
10918
- issue2.errors.map((issues) => processError({ issues }, [...path34, ...issue2.path]));
10950
+ issue2.errors.map((issues) => processError({ issues }, [...path35, ...issue2.path]));
10919
10951
  } else if (issue2.code === "invalid_key") {
10920
- processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
10952
+ processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
10921
10953
  } else if (issue2.code === "invalid_element") {
10922
- processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
10954
+ processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
10923
10955
  } else {
10924
- const fullpath = [...path34, ...issue2.path];
10956
+ const fullpath = [...path35, ...issue2.path];
10925
10957
  if (fullpath.length === 0) {
10926
10958
  fieldErrors._errors.push(mapper(issue2));
10927
10959
  } else {
@@ -10948,17 +10980,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
10948
10980
  }
10949
10981
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
10950
10982
  const result = { errors: [] };
10951
- const processError = (error52, path34 = []) => {
10983
+ const processError = (error52, path35 = []) => {
10952
10984
  var _a3, _b;
10953
10985
  for (const issue2 of error52.issues) {
10954
10986
  if (issue2.code === "invalid_union" && issue2.errors.length) {
10955
- issue2.errors.map((issues) => processError({ issues }, [...path34, ...issue2.path]));
10987
+ issue2.errors.map((issues) => processError({ issues }, [...path35, ...issue2.path]));
10956
10988
  } else if (issue2.code === "invalid_key") {
10957
- processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
10989
+ processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
10958
10990
  } else if (issue2.code === "invalid_element") {
10959
- processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
10991
+ processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
10960
10992
  } else {
10961
- const fullpath = [...path34, ...issue2.path];
10993
+ const fullpath = [...path35, ...issue2.path];
10962
10994
  if (fullpath.length === 0) {
10963
10995
  result.errors.push(mapper(issue2));
10964
10996
  continue;
@@ -10990,8 +11022,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
10990
11022
  }
10991
11023
  function toDotPath(_path) {
10992
11024
  const segs = [];
10993
- const path34 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
10994
- for (const seg of path34) {
11025
+ const path35 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
11026
+ for (const seg of path35) {
10995
11027
  if (typeof seg === "number")
10996
11028
  segs.push(`[${seg}]`);
10997
11029
  else if (typeof seg === "symbol")
@@ -23683,13 +23715,13 @@ function resolveRef(ref, ctx) {
23683
23715
  if (!ref.startsWith("#")) {
23684
23716
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
23685
23717
  }
23686
- const path34 = ref.slice(1).split("/").filter(Boolean);
23687
- if (path34.length === 0) {
23718
+ const path35 = ref.slice(1).split("/").filter(Boolean);
23719
+ if (path35.length === 0) {
23688
23720
  return ctx.rootSchema;
23689
23721
  }
23690
23722
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
23691
- if (path34[0] === defsKey) {
23692
- const key = path34[1];
23723
+ if (path35[0] === defsKey) {
23724
+ const key = path35[1];
23693
23725
  if (!key || !ctx.defs[key]) {
23694
23726
  throw new Error(`Reference not found: ${ref}`);
23695
23727
  }
@@ -24619,6 +24651,138 @@ function normalizeDocumentText(value) {
24619
24651
  var logger9 = createModuleLogger("neural.mcpServer");
24620
24652
  var VIDEO_GENERATION_SKILL_ID = OFFICIAL_MEDIA_SKILL_IDS[0];
24621
24653
  var AUTO_LOCAL_ENHANCER_LIMIT = 2;
24654
+ var WORKDIR_ATTACHMENT_MARKER_KIND = "ahchat_workdir_attachment";
24655
+ var WORKDIR_FIND_DEFAULT_MAX_RESULTS = 20;
24656
+ var WORKDIR_FIND_MAX_RESULTS = 50;
24657
+ var WORKDIR_FIND_MAX_SCANNED_FILES = 5e3;
24658
+ var WORKDIR_FIND_MAX_DEPTH = 8;
24659
+ var WORKDIR_FIND_SKIP_DIRS = /* @__PURE__ */ new Set([
24660
+ ".ahchat-attachments",
24661
+ ".git",
24662
+ ".next",
24663
+ ".turbo",
24664
+ "build",
24665
+ "coverage",
24666
+ "dist",
24667
+ "node_modules"
24668
+ ]);
24669
+ var WORKDIR_FILE_MIME_BY_EXT = {
24670
+ ".csv": "text/csv",
24671
+ ".doc": "application/msword",
24672
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
24673
+ ".gif": "image/gif",
24674
+ ".htm": "text/html",
24675
+ ".html": "text/html",
24676
+ ".jpeg": "image/jpeg",
24677
+ ".jpg": "image/jpeg",
24678
+ ".json": "application/json",
24679
+ ".m4a": "audio/mp4",
24680
+ ".md": "text/markdown",
24681
+ ".mp3": "audio/mpeg",
24682
+ ".mp4": "video/mp4",
24683
+ ".pdf": "application/pdf",
24684
+ ".png": "image/png",
24685
+ ".ppt": "application/vnd.ms-powerpoint",
24686
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
24687
+ ".svg": "image/svg+xml",
24688
+ ".txt": "text/plain",
24689
+ ".wav": "audio/wav",
24690
+ ".webm": "video/webm",
24691
+ ".webp": "image/webp",
24692
+ ".xls": "application/vnd.ms-excel",
24693
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
24694
+ };
24695
+ function normalizeWorkdirRelativePath(value) {
24696
+ return value.split(path10.sep).join("/");
24697
+ }
24698
+ function inferWorkdirFileMimeType(filePath) {
24699
+ return WORKDIR_FILE_MIME_BY_EXT[path10.extname(filePath).toLowerCase()] ?? "application/octet-stream";
24700
+ }
24701
+ function boundedWorkdirResultLimit(value) {
24702
+ if (!Number.isFinite(value)) return WORKDIR_FIND_DEFAULT_MAX_RESULTS;
24703
+ return Math.min(WORKDIR_FIND_MAX_RESULTS, Math.max(1, Math.floor(value)));
24704
+ }
24705
+ async function resolveWorkdirRoot(cwd) {
24706
+ const rawCwd = (cwd ?? process.cwd()).trim();
24707
+ if (!rawCwd) throw new Error("\u5F53\u524D scope \u6CA1\u6709\u53EF\u7528\u5DE5\u4F5C\u76EE\u5F55\u3002");
24708
+ const resolvedCwd = path10.resolve(rawCwd);
24709
+ const stat3 = await fs5.stat(resolvedCwd).catch(() => null);
24710
+ if (!stat3?.isDirectory()) throw new Error(`\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${resolvedCwd}`);
24711
+ return { cwd: resolvedCwd, realCwd: await fs5.realpath(resolvedCwd) };
24712
+ }
24713
+ async function resolveWorkdirFilePath(requestedPath, cwd) {
24714
+ const trimmed = requestedPath.trim();
24715
+ if (!trimmed) throw new Error("path \u4E0D\u80FD\u4E3A\u7A7A\u3002");
24716
+ const root = await resolveWorkdirRoot(cwd);
24717
+ const candidate = path10.resolve(root.cwd, trimmed);
24718
+ let realTarget;
24719
+ try {
24720
+ realTarget = await fs5.realpath(candidate);
24721
+ } catch {
24722
+ throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${trimmed}`);
24723
+ }
24724
+ if (!isPathInside(root.realCwd, realTarget)) {
24725
+ throw new Error("\u53EA\u80FD\u8BBF\u95EE\u5F53\u524D scope \u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u6587\u4EF6\u3002");
24726
+ }
24727
+ const stat3 = await fs5.stat(realTarget);
24728
+ if (!stat3.isFile()) throw new Error(`\u4E0D\u662F\u53EF\u53D1\u9001\u6587\u4EF6\uFF1A${trimmed}`);
24729
+ const relativePath = normalizeWorkdirRelativePath(path10.relative(root.realCwd, realTarget));
24730
+ return {
24731
+ absolutePath: realTarget,
24732
+ fileName: path10.basename(realTarget),
24733
+ relativePath,
24734
+ size: stat3.size,
24735
+ mtimeMs: stat3.mtimeMs
24736
+ };
24737
+ }
24738
+ function formatWorkdirFileSize(bytes) {
24739
+ if (bytes < 1024) return `${bytes} B`;
24740
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
24741
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
24742
+ }
24743
+ async function findWorkdirFiles(args) {
24744
+ const root = await resolveWorkdirRoot(args.cwd);
24745
+ const needle = args.query?.trim().toLowerCase() ?? "";
24746
+ const limit = boundedWorkdirResultLimit(args.maxResults);
24747
+ const results = [];
24748
+ let scanned = 0;
24749
+ async function walk(dir, depth) {
24750
+ if (results.length >= limit || scanned >= WORKDIR_FIND_MAX_SCANNED_FILES || depth > WORKDIR_FIND_MAX_DEPTH) return;
24751
+ let entries;
24752
+ try {
24753
+ entries = await fs5.readdir(dir, { withFileTypes: true });
24754
+ } catch {
24755
+ return;
24756
+ }
24757
+ entries.sort((a, b) => a.name.localeCompare(b.name));
24758
+ for (const entry of entries) {
24759
+ if (results.length >= limit || scanned >= WORKDIR_FIND_MAX_SCANNED_FILES) break;
24760
+ if (entry.isDirectory()) {
24761
+ if (!WORKDIR_FIND_SKIP_DIRS.has(entry.name)) {
24762
+ await walk(path10.join(dir, entry.name), depth + 1);
24763
+ }
24764
+ continue;
24765
+ }
24766
+ if (!entry.isFile()) continue;
24767
+ scanned++;
24768
+ const absolutePath = path10.join(dir, entry.name);
24769
+ const relativePath = normalizeWorkdirRelativePath(path10.relative(root.realCwd, absolutePath));
24770
+ if (needle && !relativePath.toLowerCase().includes(needle) && !entry.name.toLowerCase().includes(needle)) {
24771
+ continue;
24772
+ }
24773
+ const stat3 = await fs5.stat(absolutePath).catch(() => null);
24774
+ if (!stat3?.isFile()) continue;
24775
+ results.push({
24776
+ relativePath,
24777
+ fileName: entry.name,
24778
+ size: stat3.size,
24779
+ mtimeMs: stat3.mtimeMs
24780
+ });
24781
+ }
24782
+ }
24783
+ await walk(root.realCwd, 0);
24784
+ return results.sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, limit);
24785
+ }
24622
24786
  function formatSkillEntry(entry, index) {
24623
24787
  return [
24624
24788
  `${index + 1}. ${entry.displayName} (${entry.name})`,
@@ -25573,6 +25737,122 @@ ${result.warnings.map((warning) => `- ${warning}`).join("\n")}
25573
25737
  },
25574
25738
  {}
25575
25739
  );
25740
+ const findWorkdirFilesTool = sdk.tool(
25741
+ "find_workdir_files",
25742
+ `Find files inside the current scope working directory only.
25743
+ Use this when the user asks you to locate a generated/output file before sending it as an attachment. It returns relative paths that can be passed to send_workdir_file.`,
25744
+ {
25745
+ query: external_exports.string().optional().describe("Optional case-insensitive substring to match against file names or relative paths."),
25746
+ max_results: external_exports.number().int().min(1).max(WORKDIR_FIND_MAX_RESULTS).optional().describe(
25747
+ `Maximum files to return. Defaults to ${WORKDIR_FIND_DEFAULT_MAX_RESULTS}, hard max ${WORKDIR_FIND_MAX_RESULTS}.`
25748
+ )
25749
+ },
25750
+ async (args) => {
25751
+ const query4 = args.query?.trim();
25752
+ const maxResults = boundedWorkdirResultLimit(args.max_results);
25753
+ logger9.info("find_workdir_files tool called", {
25754
+ agentId: deps.agentId,
25755
+ scope: currentScopeKey,
25756
+ query: query4 ?? null,
25757
+ maxResults
25758
+ });
25759
+ try {
25760
+ const files = await findWorkdirFiles({
25761
+ cwd: deps.cwd,
25762
+ query: query4,
25763
+ maxResults
25764
+ });
25765
+ if (files.length === 0) {
25766
+ return {
25767
+ content: [{
25768
+ type: "text",
25769
+ text: `[find_workdir_files] \u672A\u627E\u5230\u5339\u914D\u6587\u4EF6\u3002scope=${currentScopeKey}`
25770
+ }]
25771
+ };
25772
+ }
25773
+ const lines = files.map((file2, index) => {
25774
+ const modified = new Date(file2.mtimeMs).toISOString();
25775
+ return `${index + 1}. ${file2.relativePath} (${formatWorkdirFileSize(file2.size)}, modified ${modified})`;
25776
+ });
25777
+ return {
25778
+ content: [{
25779
+ type: "text",
25780
+ text: [
25781
+ `[find_workdir_files] \u627E\u5230 ${files.length} \u4E2A\u6587\u4EF6\uFF08\u53EA\u80FD\u53D1\u9001\u5F53\u524D scope \u5DE5\u4F5C\u76EE\u5F55\u5185\u6587\u4EF6\uFF09\uFF1A`,
25782
+ ...lines,
25783
+ "",
25784
+ '\u8981\u53D1\u7ED9\u7528\u6237\u65F6\uFF0C\u8C03\u7528 send_workdir_file(path="<\u4E0A\u9762\u7684\u76F8\u5BF9\u8DEF\u5F84>")\u3002'
25785
+ ].join("\n")
25786
+ }]
25787
+ };
25788
+ } catch (e) {
25789
+ const message = e instanceof Error ? e.message : String(e);
25790
+ logger9.error("find_workdir_files failed", {
25791
+ agentId: deps.agentId,
25792
+ scope: currentScopeKey,
25793
+ query: query4 ?? null,
25794
+ error: e
25795
+ });
25796
+ return {
25797
+ content: [{ type: "text", text: `[find_workdir_files] failed: ${message}` }],
25798
+ isError: true
25799
+ };
25800
+ }
25801
+ },
25802
+ {}
25803
+ );
25804
+ const sendWorkdirFileTool = sdk.tool(
25805
+ "send_workdir_file",
25806
+ `Send a real file from the current scope working directory as a clickable AHChat attachment on this visible reply.
25807
+ Only files inside the current scope cwd are allowed. Directories, missing files, and paths outside cwd are rejected. Pass a relative path from find_workdir_files when possible.`,
25808
+ {
25809
+ path: external_exports.string().min(1).describe("File path to send. Prefer a relative path returned by find_workdir_files; absolute paths are accepted only when they are inside the current scope cwd.")
25810
+ },
25811
+ async (args) => {
25812
+ const requestedPath = args.path.trim();
25813
+ logger9.info("send_workdir_file tool called", {
25814
+ agentId: deps.agentId,
25815
+ scope: currentScopeKey,
25816
+ path: requestedPath
25817
+ });
25818
+ try {
25819
+ const file2 = await resolveWorkdirFilePath(requestedPath, deps.cwd);
25820
+ const marker = {
25821
+ ok: true,
25822
+ kind: WORKDIR_ATTACHMENT_MARKER_KIND,
25823
+ path: file2.absolutePath,
25824
+ relative_path: file2.relativePath,
25825
+ file_name: file2.fileName,
25826
+ mime_type: inferWorkdirFileMimeType(file2.absolutePath),
25827
+ size: file2.size,
25828
+ attachment_source: "agent_explicit_send"
25829
+ };
25830
+ return {
25831
+ content: [{
25832
+ type: "text",
25833
+ text: [
25834
+ JSON.stringify(marker),
25835
+ "",
25836
+ `[send_workdir_file] \u5DF2\u51C6\u5907\u53D1\u9001\u9644\u4EF6\uFF1A${file2.relativePath} (${formatWorkdirFileSize(file2.size)})\u3002\u8BF7\u5728\u672C\u8F6E\u53EF\u89C1\u56DE\u590D\u91CC\u7B80\u77ED\u8BF4\u660E\u9644\u4EF6\u5DF2\u9644\u4E0A\uFF0C\u4E0D\u8981\u91CD\u590D\u8F93\u51FA\u6587\u4EF6\u5168\u6587\u3002`
25837
+ ].join("\n")
25838
+ }]
25839
+ };
25840
+ } catch (e) {
25841
+ const message = e instanceof Error ? e.message : String(e);
25842
+ logger9.error("send_workdir_file failed", {
25843
+ agentId: deps.agentId,
25844
+ scope: currentScopeKey,
25845
+ path: requestedPath,
25846
+ error: e
25847
+ });
25848
+ return {
25849
+ content: [{ type: "text", text: `[send_workdir_file] failed: ${message}` }],
25850
+ isError: true
25851
+ };
25852
+ }
25853
+ },
25854
+ {}
25855
+ );
25576
25856
  const createGroupIssueTool = deps.serverApiUrl ? sdk.tool(
25577
25857
  "create_group_issue",
25578
25858
  `\u628A\u5F53\u524D\u7FA4\u91CC\u7684\u771F\u5B9E\u95EE\u9898\u5199\u5165\u95EE\u9898\u9762\u677F\u3002
@@ -26860,7 +27140,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
26860
27140
  system_prompt: external_exports.string().describe("\u8BE5 Agent \u7684 system prompt\uFF0C\u5B9A\u4E49\u4EBA\u683C\u3001\u4E13\u957F\u3001\u884C\u4E3A\u51C6\u5219\u3002\u4E0D\u80FD\u4E3A\u7A7A\u3002"),
26861
27141
  tier: external_exports.enum(["smart", "balanced", "fast"]).describe("\u80FD\u529B\u6863\u4F4D\uFF1Asmart\uFF08\u65D7\u8230\uFF0C\u590D\u6742\u4EFB\u52A1\uFF09\u3001balanced\uFF08\u6807\u51C6\uFF0C\u5E38\u89C4\u4EFB\u52A1\uFF09\u3001fast\uFF08\u8F7B\u91CF\uFF0C\u7B80\u5355\u4EFB\u52A1\uFF09\u3002"),
26862
27142
  avatar: external_exports.string().optional().describe(
26863
- "\u5934\u50CF key\uFF0C\u4ECE\u4EE5\u4E0B\u9009\u4E00\u4E2A\u6700\u5339\u914D\u89D2\u8272\u7684\uFF1Aavatar_dev\uFF08\u5F00\u53D1\uFF09, avatar_pm\uFF08\u4EA7\u54C1/\u7BA1\u7406\uFF09, avatar_designer\uFF08\u8BBE\u8BA1\uFF09, avatar_qa\uFF08\u6D4B\u8BD5/QA\uFF09, avatar_writer\uFF08\u5199\u4F5C/\u6587\u6863\uFF09, avatar_analyst\uFF08\u5206\u6790/\u6570\u636E\uFF09, avatar_coach\uFF08\u654F\u6377\u6559\u7EC3/\u5E26\u6559\uFF09, avatar_scientist\uFF08\u7814\u7A76/\u7B97\u6CD5\uFF09, avatar_lawyer\uFF08\u6CD5\u52A1/\u5408\u89C4\uFF09, avatar_human_man\uFF08\u901A\u7528\u7537\u6027\uFF09, avatar_human_woman\uFF08\u901A\u7528\u5973\u6027\uFF09, avatar_human_nerd\uFF08\u6781\u5BA2\uFF09, avatar_human_cool\uFF08\u9177\uFF09, avatar_human_cowboy\uFF08\u72C2\u91CE\uFF09, avatar_human_artist\uFF08\u827A\u672F\u5BB6\uFF09\u3002\u7559\u7A7A\u5219\u7528\u9ED8\u8BA4 avatar_default\uFF08\u{1F916}\uFF09\u3002"
27143
+ "\u53EF\u9009\u5934\u50CF key\u3002\u901A\u5E38\u7559\u7A7A\uFF0C\u7531\u7CFB\u7EDF\u6839\u636E Agent \u540D\u5B57\u3001\u89D2\u8272\u548C\u63D0\u793A\u8BCD\u81EA\u52A8\u5206\u914D\u673A\u5668\u4EBA\u5934\u50CF\u5E76\u5F02\u6B65\u751F\u6210\u6700\u7EC8\u5934\u50CF\u3002\u4E0D\u8981\u7ED9 Agent \u4F7F\u7528 avatar_human_*\uFF0C\u8FD9\u4E9B\u53EA\u7ED9\u771F\u5B9E\u4EBA\u7C7B\u7528\u6237\u4F7F\u7528\u3002"
26864
27144
  ),
26865
27145
  initial_instruction: external_exports.string().optional().describe(
26866
27146
  '\u53EF\u9009\u3002\u521B\u5EFA\u540E\u7ACB\u5373\u4E0B\u53D1\u7ED9\u65B0 Agent \u7684\u4E00\u53E5\u8BDD\u6307\u4EE4\u3002\u5178\u578B\uFF1A"\u8BF7\u7528 1-2 \u53E5\u8BDD\u5411\u7528\u6237\u505A\u81EA\u6211\u4ECB\u7ECD\uFF0C\u8BF4\u660E\u4F60\u7684\u4E13\u957F"\u3002\u53EA\u5728\u7528\u6237\u660E\u786E\u8981"\u521B\u5EFA\u4E00\u4E2A\u5355 Agent"\u4E14\u5E0C\u671B\u8BE5 Agent \u7ACB\u5373\u9732\u9762\u65F6\u4F20\u3002\u56E2\u961F\u573A\u666F\u8BF7\u52FF\u4F20\u2014\u2014\u7531 Leader \u5728\u7FA4\u91CC\u6253\u62DB\u547C\u3002'
@@ -27203,7 +27483,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
27203
27483
  system_prompt: external_exports.string().optional().describe("\u65B0\u7684 system prompt\uFF08\u5B9A\u4E49\u4EBA\u683C\u3001\u4E13\u957F\u3001\u884C\u4E3A\u51C6\u5219\uFF09\u3002"),
27204
27484
  tier: external_exports.enum(["smart", "balanced", "fast"]).optional().describe("\u80FD\u529B\u6863\u4F4D\u3002\u4F20\u6B64\u53C2\u6570\u4F1A\u66F4\u65B0\u8BE5 Agent \u4F7F\u7528\u7684\u6A21\u578B\u3002"),
27205
27485
  avatar: external_exports.string().optional().describe(
27206
- "\u5934\u50CF key\uFF08\u5982 avatar_dev / avatar_pm / avatar_human_man \u7B49\uFF09\u3002"
27486
+ "\u5934\u50CF key\uFF08\u5982 avatar_dev / avatar_pm / avatar_default \u7B49\uFF09\u3002\u4E0D\u8981\u7ED9 Agent \u4F7F\u7528 avatar_human_*\uFF0C\u8FD9\u4E9B\u53EA\u7ED9\u771F\u5B9E\u4EBA\u7C7B\u7528\u6237\u4F7F\u7528\u3002"
27207
27487
  ),
27208
27488
  machine_bridge_key: external_exports.string().optional().describe(
27209
27489
  '\u53EF\u9009\u3002\u65B0\u7684\u8FD0\u884C\u673A\u5668 bridgeKey\uFF0C\u6765\u81EA list_contacts \u7684"\u53EF\u7528\u673A\u5668"\u3002\u7701\u7565\u8868\u793A\u4E0D\u6539\uFF1B\u4F20 auto \u6216\u7A7A\u5B57\u7B26\u4E32\u6E05\u9664\u673A\u5668\u504F\u597D\u3002'
@@ -27904,7 +28184,13 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
27904
28184
  },
27905
28185
  {}
27906
28186
  ) : null;
27907
- const tools = [neuralSend, neuralListScopes, readDocumentTool];
28187
+ const tools = [
28188
+ neuralSend,
28189
+ neuralListScopes,
28190
+ readDocumentTool,
28191
+ findWorkdirFilesTool,
28192
+ sendWorkdirFileTool
28193
+ ];
27908
28194
  if (readChatHistory) tools.push(readChatHistory);
27909
28195
  if (selfNote) tools.push(selfNote);
27910
28196
  if (listContacts) tools.push(listContacts);
@@ -27936,7 +28222,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
27936
28222
  version: "2.3.0",
27937
28223
  tools
27938
28224
  });
27939
- const toolNames = ["neural_send", "neural_list_scopes", "read_document"];
28225
+ const toolNames = ["neural_send", "neural_list_scopes", "read_document", "find_workdir_files", "send_workdir_file"];
27940
28226
  if (readChatHistory) toolNames.push("read_chat_history");
27941
28227
  if (selfNote) toolNames.push("self_note");
27942
28228
  if (listContacts) toolNames.push("list_contacts");
@@ -28199,9 +28485,11 @@ function buildGroupInboxPrompt(entries, opts = {}) {
28199
28485
 
28200
28486
  // src/sdkEventMapper.ts
28201
28487
  var logger11 = createModuleLogger("sdk.mapper");
28488
+ var DEBUG_ONLY_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set(["status", "thinking_tokens"]);
28202
28489
  var HIGH_WATERMARK_INPUT_TOKENS = 12e4;
28203
28490
  var WARN_THRESHOLD_INPUT_TOKENS = 1e5;
28204
28491
  var LIVE_INPUT_PREVIEW_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
28492
+ var WORKDIR_MUTATION_TOOL_NAMES = /* @__PURE__ */ new Set(["write", "edit", "multiedit", "notebookedit", "bash"]);
28205
28493
  var CONTEXT_OVERFLOW_LOCK_MS = 6e4;
28206
28494
  function parseJsonRecord(text) {
28207
28495
  try {
@@ -28223,6 +28511,17 @@ function isSuccessfulOfficialMediaOutput(toolName, output) {
28223
28511
  if (!["succeeded", "success", "completed", "done"].includes(state)) return false;
28224
28512
  return Array.isArray(parsed.images) && parsed.images.length > 0 || typeof parsed.video_url === "string" || typeof parsed.videoUrl === "string";
28225
28513
  }
28514
+ function isWorkdirMutationToolName(toolName) {
28515
+ return WORKDIR_MUTATION_TOOL_NAMES.has(toolName.trim().toLowerCase());
28516
+ }
28517
+ function recordWorkdirSignal(proc, toolName, isError) {
28518
+ if (isError || !isGroupTask(proc) || !isWorkdirMutationToolName(toolName)) return;
28519
+ const signals = proc.workdirSignals ?? [];
28520
+ if (!signals.some((signal) => signal.toolName === toolName)) {
28521
+ signals.push({ toolName });
28522
+ }
28523
+ proc.workdirSignals = signals;
28524
+ }
28226
28525
  function isContextOverflowText(text) {
28227
28526
  const trimmed = text.trim();
28228
28527
  return /^prompt is too long\b/i.test(trimmed) || /context length .* exceed/i.test(trimmed);
@@ -28727,6 +29026,7 @@ function countByStatus(todos) {
28727
29026
  function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = false) {
28728
29027
  const groupId = proc.currentTask?.groupId;
28729
29028
  if (!groupId) return;
29029
+ const workdirSignals = proc.workdirSignals?.length ? [...proc.workdirSignals] : void 0;
28730
29030
  proc.segmentCount += 1;
28731
29031
  logger11.info("Group segment emitted", {
28732
29032
  agentId: base.agentId,
@@ -28736,6 +29036,7 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
28736
29036
  contentLen: content.length,
28737
29037
  blockCount: contentBlocks.length,
28738
29038
  blockTypes: contentBlocks.map((b) => b.type),
29039
+ workdirSignalCount: workdirSignals?.length ?? 0,
28739
29040
  traceId: base.traceId,
28740
29041
  isAuditOnly: content.length === 0,
28741
29042
  isSilent
@@ -28748,9 +29049,11 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
28748
29049
  groupId,
28749
29050
  content,
28750
29051
  contentBlocks: [...contentBlocks],
29052
+ ...workdirSignals ? { workdirSignals } : {},
28751
29053
  ...isSilent ? { isSilent: true } : {}
28752
29054
  }
28753
29055
  });
29056
+ proc.workdirSignals = [];
28754
29057
  }
28755
29058
  function flushTextSegmentOnBlockStop(proc, emit, base) {
28756
29059
  const trimmed = proc.segmentBuffer.trim();
@@ -28769,7 +29072,7 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
28769
29072
  traceId: base.traceId
28770
29073
  });
28771
29074
  }
28772
- } else if (proc.contentBlocks.length > 0) {
29075
+ } else if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
28773
29076
  const blockCount = proc.contentBlocks.length;
28774
29077
  emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
28775
29078
  proc.contentBlocks = [];
@@ -28859,7 +29162,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
28859
29162
  proc.activeSubagentTaskIds?.delete(subagentTaskId);
28860
29163
  }
28861
29164
  }
28862
- logger11.info("SDK system subtype unhandled", {
29165
+ const logUnhandledSystemSubtype = DEBUG_ONLY_SYSTEM_SUBTYPES.has(String(sysMsg.subtype ?? "")) ? logger11.debug.bind(logger11) : logger11.info.bind(logger11);
29166
+ logUnhandledSystemSubtype("SDK system subtype unhandled", {
28863
29167
  agentId: proc.agentId,
28864
29168
  scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
28865
29169
  subtype: sysMsg.subtype ?? "(none)",
@@ -28916,6 +29220,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
28916
29220
  input: {}
28917
29221
  }
28918
29222
  });
29223
+ }
29224
+ if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
28919
29225
  proc.contentBlocks.push({
28920
29226
  type: "tool_use",
28921
29227
  ...toolUseId ? { toolUseId } : {},
@@ -29256,6 +29562,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29256
29562
  }
29257
29563
  }
29258
29564
  }
29565
+ recordWorkdirSignal(proc, toolName, isError);
29259
29566
  proc.activeToolUseStartedAt = void 0;
29260
29567
  proc.currentToolUseId = null;
29261
29568
  }
@@ -29317,7 +29624,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29317
29624
  if (isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: groupMode })) {
29318
29625
  checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
29319
29626
  emitUsageReported(proc, emit, base, usage);
29320
- if (groupMode && proc.contentBlocks.length > 0) {
29627
+ if (groupMode && (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0)) {
29321
29628
  emitGroupSegment(
29322
29629
  proc,
29323
29630
  emit,
@@ -29362,11 +29669,12 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29362
29669
  proc.segmentBuffer = proc.accumulatedText;
29363
29670
  flushTextSegmentOnBlockStop(proc, emit, base);
29364
29671
  }
29365
- if (proc.contentBlocks.length > 0) {
29672
+ if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
29366
29673
  logger11.info("Group turn trailing audit segment", {
29367
29674
  agentId: proc.agentId,
29368
29675
  replyMessageId: base.replyMessageId,
29369
29676
  blockCount: proc.contentBlocks.length,
29677
+ workdirSignalCount: proc.workdirSignals?.length ?? 0,
29370
29678
  traceId: base.traceId
29371
29679
  });
29372
29680
  emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
@@ -29511,6 +29819,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29511
29819
  payload: { ...wireBase(base), error: errorText }
29512
29820
  });
29513
29821
  proc.contentBlocks = [];
29822
+ proc.workdirSignals = [];
29514
29823
  proc.accumulatedText = "";
29515
29824
  proc.accumulatedThinking = "";
29516
29825
  proc.segmentBuffer = "";
@@ -29543,6 +29852,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29543
29852
  proc.apiErrorEmitted = true;
29544
29853
  }
29545
29854
  proc.contentBlocks = [];
29855
+ proc.workdirSignals = [];
29546
29856
  proc.accumulatedText = "";
29547
29857
  proc.accumulatedThinking = "";
29548
29858
  proc.segmentBuffer = "";
@@ -29569,6 +29879,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29569
29879
  proc.apiErrorEmitted = true;
29570
29880
  }
29571
29881
  proc.contentBlocks = [];
29882
+ proc.workdirSignals = [];
29572
29883
  proc.accumulatedText = "";
29573
29884
  proc.accumulatedThinking = "";
29574
29885
  proc.segmentBuffer = "";
@@ -29605,6 +29916,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
29605
29916
  });
29606
29917
  }
29607
29918
  proc.contentBlocks = [];
29919
+ proc.workdirSignals = [];
29608
29920
  proc.accumulatedText = "";
29609
29921
  proc.accumulatedThinking = "";
29610
29922
  proc.segmentBuffer = "";
@@ -29646,6 +29958,7 @@ function resetAccumulators(proc) {
29646
29958
  proc.activeToolUseStartedAt = void 0;
29647
29959
  proc.segmentBuffer = "";
29648
29960
  proc.segmentCount = 0;
29961
+ proc.workdirSignals = [];
29649
29962
  proc.accumulatedToolInput = "";
29650
29963
  proc.apiErrorEmitted = false;
29651
29964
  proc.peakContextUsage = void 0;
@@ -29657,24 +29970,24 @@ function resetAccumulators(proc) {
29657
29970
  }
29658
29971
 
29659
29972
  // src/forkHistoryReplay.ts
29660
- import * as fs5 from "fs/promises";
29661
- import * as path10 from "path";
29973
+ import * as fs6 from "fs/promises";
29974
+ import * as path11 from "path";
29662
29975
  var logger12 = createModuleLogger("bridge.forkHistoryReplay");
29663
29976
  function metaPath(dataDir, agentId) {
29664
- return path10.join(dataDir, "fork-meta", `${agentId}.json`);
29977
+ return path11.join(dataDir, "fork-meta", `${agentId}.json`);
29665
29978
  }
29666
29979
  async function writeForkMeta(dataDir, agentId, meta3) {
29667
29980
  const fp = metaPath(dataDir, agentId);
29668
- await fs5.mkdir(path10.dirname(fp), { recursive: true });
29669
- await fs5.writeFile(fp, JSON.stringify(meta3), "utf-8");
29981
+ await fs6.mkdir(path11.dirname(fp), { recursive: true });
29982
+ await fs6.writeFile(fp, JSON.stringify(meta3), "utf-8");
29670
29983
  logger12.info("Fork meta written", { agentId, fp, sourceConversationId: meta3.sourceConversationId });
29671
29984
  }
29672
29985
  async function consumeForkMeta(dataDir, agentId) {
29673
29986
  const fp = metaPath(dataDir, agentId);
29674
29987
  try {
29675
- const raw = await fs5.readFile(fp, "utf-8");
29988
+ const raw = await fs6.readFile(fp, "utf-8");
29676
29989
  const meta3 = JSON.parse(raw);
29677
- await fs5.unlink(fp);
29990
+ await fs6.unlink(fp);
29678
29991
  logger12.info("Fork meta consumed (one-shot)", { agentId, sourceConversationId: meta3.sourceConversationId });
29679
29992
  return meta3;
29680
29993
  } catch {
@@ -29701,20 +30014,20 @@ function buildForkHistorySection(messages) {
29701
30014
 
29702
30015
  // src/sessionSlug.ts
29703
30016
  import os6 from "os";
29704
- import path11 from "path";
29705
- var CLAUDE_PROJECTS_DIR = path11.join(os6.homedir(), ".claude", "projects");
30017
+ import path12 from "path";
30018
+ var CLAUDE_PROJECTS_DIR = path12.join(os6.homedir(), ".claude", "projects");
29706
30019
  function cwdToSlug(cwd) {
29707
30020
  return cwd.replace(/[^a-zA-Z0-9-]/g, "-");
29708
30021
  }
29709
30022
  function sessionDirForCwd(cwd) {
29710
- return path11.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
30023
+ return path12.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
29711
30024
  }
29712
30025
  function sessionFilePath(cwd, sessionId) {
29713
- return path11.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
30026
+ return path12.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
29714
30027
  }
29715
30028
 
29716
30029
  // src/workdirMapper.ts
29717
- import path12 from "path";
30030
+ import path13 from "path";
29718
30031
  function extractAhchatWorkspaceParts(requestedPath) {
29719
30032
  const normalized = requestedPath.trim().replace(/\\/g, "/");
29720
30033
  const marker = "/.ahchat/users/";
@@ -29750,15 +30063,15 @@ function extractAhchatWorkspaceParts(requestedPath) {
29750
30063
  function extractAhchatWorkspaceSuffix(requestedPath) {
29751
30064
  const parts = extractAhchatWorkspaceParts(requestedPath);
29752
30065
  if (!parts || parts.length === 0) return null;
29753
- return path12.join(...parts);
30066
+ return path13.join(...parts);
29754
30067
  }
29755
30068
  function remapServerWorkspacePath(requestedPath, workspacesDir) {
29756
30069
  const parts = extractAhchatWorkspaceParts(requestedPath);
29757
30070
  if (!parts) return { path: requestedPath, remapped: false };
29758
- const remappedPath = parts.length > 0 ? path12.join(workspacesDir, ...parts) : workspacesDir;
30071
+ const remappedPath = parts.length > 0 ? path13.join(workspacesDir, ...parts) : workspacesDir;
29759
30072
  return {
29760
30073
  path: remappedPath,
29761
- remapped: path12.normalize(requestedPath) !== path12.normalize(remappedPath)
30074
+ remapped: path13.normalize(requestedPath) !== path13.normalize(remappedPath)
29762
30075
  };
29763
30076
  }
29764
30077
 
@@ -29841,7 +30154,7 @@ function missingSubscriptionMessage(subscriptionId) {
29841
30154
  }
29842
30155
  var NODE_USER_UID = 1e3;
29843
30156
  var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
29844
- var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v4";
30157
+ var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v6";
29845
30158
  var BINARY_ATTACHMENT_EXT_RE = /\.(?:7z|bmp|csv|doc|docx|gif|jpeg|jpg|m4a|mov|mp3|mp4|pdf|png|ppt|pptx|rar|rtf|wav|webm|webp|xls|xlsx|zip)$/i;
29846
30159
  var IMAGE_READ_EXT_RE = /\.(?:bmp|gif|jpeg|jpg|png|webp)$/i;
29847
30160
  var DOCUMENT_READING_RULES = `DOCUMENT READING:
@@ -29857,6 +30170,72 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
29857
30170
  - Keep media replies short. Do not print raw media URLs, request_id, task_id, polling logs, or "let me check again" narration unless the user explicitly asks for diagnostics.
29858
30171
  - When a media task is submitted or completed, write only a natural one-line note such as "\u5DF2\u5F00\u59CB\u751F\u6210\uFF0C\u6211\u4F1A\u5728\u8FD9\u91CC\u66F4\u65B0\u7ED3\u679C\u3002" or "\u751F\u6210\u597D\u4E86\uFF0C\u53EF\u4EE5\u5728\u5361\u7247\u91CC\u67E5\u770B\u3002"; let the media card show status, preview, download, copy, and regenerate actions.
29859
30172
  - If the user asks whether a Seedance task is ready, call mcp__seedance__seedance_check_task once and answer from that result. Do not loop, sleep, or invent external Seedance API endpoints.`;
30173
+ var SMITH_ALLOWED_TOOLS = [
30174
+ // creation / configuration (Smith-only)
30175
+ "mcp__neural__create_agent",
30176
+ "mcp__neural__update_agent_profile",
30177
+ "mcp__neural__recommend_agent_skills",
30178
+ "mcp__neural__read_skill",
30179
+ // diagnostics (log detective)
30180
+ "mcp__neural__fetch_logs",
30181
+ // friend approval (Smith-only)
30182
+ "mcp__neural__list_friends",
30183
+ "mcp__neural__accept_friend",
30184
+ "mcp__neural__add_friend",
30185
+ // team assembly
30186
+ "mcp__neural__list_contacts",
30187
+ "mcp__neural__create_group",
30188
+ "mcp__neural__add_to_group",
30189
+ "mcp__neural__transfer_group_owner",
30190
+ "mcp__neural__leave_group",
30191
+ // neural bridge — cross-scope coordination when Smith is pulled into a group
30192
+ "mcp__neural__neural_send",
30193
+ "mcp__neural__neural_list_scopes",
30194
+ // bounded current-scope workdir attachments
30195
+ "mcp__neural__find_workdir_files",
30196
+ "mcp__neural__send_workdir_file",
30197
+ // interaction / planning
30198
+ "AskUserQuestion",
30199
+ "Write"
30200
+ ];
30201
+ function isVisionMcpServerName(serverName) {
30202
+ return serverName === "vision";
30203
+ }
30204
+ function selectSmithVisionMcp(resolved) {
30205
+ const resolvedMcpServers = resolved.mcpServers ?? {};
30206
+ const resolvedAllowedTools = resolved.allowedTools ?? [];
30207
+ const resolvedToolAbi = resolved.toolAbi ?? [];
30208
+ const visionServerNames = /* @__PURE__ */ new Set();
30209
+ for (const server of resolvedToolAbi) {
30210
+ if (server.providerId === "volcengine_vision" || isVisionMcpServerName(server.serverName)) {
30211
+ visionServerNames.add(server.serverName);
30212
+ }
30213
+ }
30214
+ for (const toolName of resolvedAllowedTools) {
30215
+ const parsed = parseMcpRuntimeToolName(toolName);
30216
+ if (parsed && isVisionMcpServerName(parsed.serverName)) {
30217
+ visionServerNames.add(parsed.serverName);
30218
+ }
30219
+ }
30220
+ for (const serverName of Object.keys(resolvedMcpServers)) {
30221
+ if (isVisionMcpServerName(serverName)) {
30222
+ visionServerNames.add(serverName);
30223
+ }
30224
+ }
30225
+ if (visionServerNames.size === 0) {
30226
+ return { mcpServers: {}, allowedTools: [], toolAbi: [] };
30227
+ }
30228
+ return {
30229
+ mcpServers: Object.fromEntries(
30230
+ Object.entries(resolvedMcpServers).filter(([serverName]) => visionServerNames.has(serverName))
30231
+ ),
30232
+ allowedTools: resolvedAllowedTools.filter((toolName) => {
30233
+ const parsed = parseMcpRuntimeToolName(toolName);
30234
+ return Boolean(parsed && visionServerNames.has(parsed.serverName));
30235
+ }),
30236
+ toolAbi: resolvedToolAbi.filter((server) => visionServerNames.has(server.serverName))
30237
+ };
30238
+ }
29860
30239
  function resolveVisionMcpToolHints(externalMcp) {
29861
30240
  let describe3 = null;
29862
30241
  let ocr = null;
@@ -29924,7 +30303,7 @@ function uniqueNormalizedPaths(paths) {
29924
30303
  for (const item of paths) {
29925
30304
  const trimmed = item.trim();
29926
30305
  if (!trimmed) continue;
29927
- const normalized = path13.normalize(trimmed);
30306
+ const normalized = path14.normalize(trimmed);
29928
30307
  const key = process.platform === "win32" ? normalized.toLowerCase() : normalized;
29929
30308
  if (seen.has(key)) continue;
29930
30309
  seen.add(key);
@@ -29954,7 +30333,7 @@ function resolveReadToolImagePath(input, cwd) {
29954
30333
  if (typeof raw !== "string" || raw.trim().length === 0) return null;
29955
30334
  const trimmed = raw.trim();
29956
30335
  if (!IMAGE_READ_EXT_RE.test(trimmed)) return null;
29957
- const abs = path13.isAbsolute(trimmed) ? path13.normalize(trimmed) : path13.resolve(cwd, trimmed);
30336
+ const abs = path14.isAbsolute(trimmed) ? path14.normalize(trimmed) : path14.resolve(cwd, trimmed);
29958
30337
  return { raw: trimmed, abs };
29959
30338
  }
29960
30339
  function usesIsolatedProjectBackend(cfg) {
@@ -29973,14 +30352,14 @@ function buildModelGatewayBaseUrl(serverApiUrl, subscriptionId) {
29973
30352
  }
29974
30353
  async function chownForRootSpawn(targetPath, target) {
29975
30354
  try {
29976
- await fs6.chown(targetPath, NODE_USER_UID, NODE_USER_UID);
30355
+ await fs7.chown(targetPath, NODE_USER_UID, NODE_USER_UID);
29977
30356
  } catch (error51) {
29978
30357
  logger14.error("Best-effort root chown failed", { error: error51, target, path: targetPath });
29979
30358
  }
29980
30359
  }
29981
30360
  function readCronLockSnapshot() {
29982
30361
  try {
29983
- const lockPath2 = path13.join(os7.homedir(), ".claude", "scheduled_tasks.lock");
30362
+ const lockPath2 = path14.join(os7.homedir(), ".claude", "scheduled_tasks.lock");
29984
30363
  if (!fsSync.existsSync(lockPath2)) {
29985
30364
  return { exists: false, sessionId: null, pid: null };
29986
30365
  }
@@ -30252,8 +30631,8 @@ var AgentManager = class {
30252
30631
  this.emit = emit;
30253
30632
  if (typeof options === "function") {
30254
30633
  this.queryFn = options;
30255
- this.workspacesDir = path13.join(os7.homedir(), ".ahchat", "workspaces");
30256
- this.agentConfigDir = path13.join(os7.homedir(), ".ahchat", "agent-config");
30634
+ this.workspacesDir = path14.join(os7.homedir(), ".ahchat", "workspaces");
30635
+ this.agentConfigDir = path14.join(os7.homedir(), ".ahchat", "agent-config");
30257
30636
  this.queryConfig = DEFAULT_QUERY_CONFIG;
30258
30637
  this.askQuestionRegistry = new AskQuestionRegistry();
30259
30638
  this.groupRegistry = null;
@@ -30270,13 +30649,13 @@ var AgentManager = class {
30270
30649
  this.bridgeToken = null;
30271
30650
  this.currentBridgeKey = null;
30272
30651
  this.defaultModel = null;
30273
- this.dataDir = path13.join(os7.homedir(), ".ahchat");
30652
+ this.dataDir = path14.join(os7.homedir(), ".ahchat");
30274
30653
  this.workdirOverrideStore = null;
30275
30654
  this.officeCliRuntime = null;
30276
30655
  } else {
30277
30656
  this.queryFn = options?.queryFn ?? null;
30278
- this.workspacesDir = options?.workspacesDir ?? path13.join(os7.homedir(), ".ahchat", "workspaces");
30279
- this.agentConfigDir = options?.agentConfigDir ?? path13.join(os7.homedir(), ".ahchat", "agent-config");
30657
+ this.workspacesDir = options?.workspacesDir ?? path14.join(os7.homedir(), ".ahchat", "workspaces");
30658
+ this.agentConfigDir = options?.agentConfigDir ?? path14.join(os7.homedir(), ".ahchat", "agent-config");
30280
30659
  this.queryConfig = options?.queryConfig ?? DEFAULT_QUERY_CONFIG;
30281
30660
  this.askQuestionRegistry = options?.askQuestionRegistry ?? new AskQuestionRegistry();
30282
30661
  this.groupRegistry = options?.groupRegistry ?? null;
@@ -30293,7 +30672,7 @@ var AgentManager = class {
30293
30672
  this.mcpAuditRecorder = options?.mcpAuditRecorder ?? new HttpMcpAuditClient(this.serverApiUrl, this.bridgeToken);
30294
30673
  this.mcpBillingPreflightChecker = options?.mcpBillingPreflightChecker ?? new HttpMcpBillingClient(this.serverApiUrl, this.bridgeToken);
30295
30674
  this.defaultModel = options?.defaultModel ?? null;
30296
- this.dataDir = options?.dataDir ?? path13.join(os7.homedir(), ".ahchat");
30675
+ this.dataDir = options?.dataDir ?? path14.join(os7.homedir(), ".ahchat");
30297
30676
  this.workdirOverrideStore = options?.workdirOverrideStore ?? null;
30298
30677
  this.officeCliRuntime = options?.officeCliRuntime ?? null;
30299
30678
  }
@@ -30326,9 +30705,9 @@ var AgentManager = class {
30326
30705
  const normalized = requestedCwd.trim();
30327
30706
  const ahchatSuffix = extractAhchatWorkspaceSuffix(normalized);
30328
30707
  if (ahchatSuffix) {
30329
- return path13.join(this.workspacesDir, ahchatSuffix);
30708
+ return path14.join(this.workspacesDir, ahchatSuffix);
30330
30709
  }
30331
- return path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30710
+ return path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30332
30711
  }
30333
30712
  localScopeDirName(agentConfig, scope) {
30334
30713
  if (scope.kind === "group") return `Group-${scope.groupId}`;
@@ -30339,9 +30718,9 @@ var AgentManager = class {
30339
30718
  if (scope.kind === "group") {
30340
30719
  const groupCwd = this.groupRegistry?.getById(scope.groupId)?.workingDirectory?.trim();
30341
30720
  if (groupCwd) return groupCwd;
30342
- return path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30721
+ return path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30343
30722
  }
30344
- const local = path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30723
+ const local = path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30345
30724
  return agentConfig.workingDirectory?.trim() || local;
30346
30725
  }
30347
30726
  runtimeCwdInput(agentConfig, scope, cwd) {
@@ -30353,7 +30732,7 @@ var AgentManager = class {
30353
30732
  if (requested && (!agentCwd || !this.isSameRuntimeCwd(requested, agentCwd))) {
30354
30733
  return requested;
30355
30734
  }
30356
- return path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30735
+ return path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30357
30736
  }
30358
30737
  return agentConfig.workingDirectory?.trim() || requested || this.scopeCwdInput(agentConfig, scope);
30359
30738
  }
@@ -30388,7 +30767,7 @@ var AgentManager = class {
30388
30767
  let cwd = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
30389
30768
  let fallbackForensicsId;
30390
30769
  if (!isFullyQualifiedAbsolutePath(cwd)) {
30391
- const fallback = path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30770
+ const fallback = path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
30392
30771
  logger14.error(
30393
30772
  "Working directory is not usable on this machine; using local sandbox fallback",
30394
30773
  {
@@ -30424,7 +30803,7 @@ var AgentManager = class {
30424
30803
  cwd = this.fallbackCwd(agentConfig, scope, cwd);
30425
30804
  }
30426
30805
  try {
30427
- await fs6.mkdir(cwd, { recursive: true });
30806
+ await fs7.mkdir(cwd, { recursive: true });
30428
30807
  return cwd;
30429
30808
  } catch (e) {
30430
30809
  const fallback = this.fallbackCwd(agentConfig, scope, cwd);
@@ -30451,7 +30830,7 @@ var AgentManager = class {
30451
30830
  },
30452
30831
  outcome: { result: "second_layer_fallback" }
30453
30832
  });
30454
- await fs6.mkdir(fallback, { recursive: true });
30833
+ await fs7.mkdir(fallback, { recursive: true });
30455
30834
  return fallback;
30456
30835
  }
30457
30836
  }
@@ -30645,25 +31024,25 @@ var AgentManager = class {
30645
31024
  }
30646
31025
  settingsPathForConfig(cfg, effectiveConfigDir) {
30647
31026
  if (!usesIsolatedProjectBackend(cfg)) return void 0;
30648
- return path13.join(effectiveConfigDir, "settings.json");
31027
+ return path14.join(effectiveConfigDir, "settings.json");
30649
31028
  }
30650
31029
  isSameRuntimeCwd(a, b) {
30651
- const left = path13.normalize(a);
30652
- const right = path13.normalize(b);
31030
+ const left = path14.normalize(a);
31031
+ const right = path14.normalize(b);
30653
31032
  return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
30654
31033
  }
30655
31034
  sessionProjectDirs(agentConfig, effectiveConfigDir) {
30656
31035
  return uniqueNormalizedPaths([
30657
- path13.join(effectiveConfigDir, "api-key-agents", agentConfig.id, "projects"),
30658
- path13.join(effectiveConfigDir, "projects"),
30659
- path13.join(os7.homedir(), ".claude", "projects")
31036
+ path14.join(effectiveConfigDir, "api-key-agents", agentConfig.id, "projects"),
31037
+ path14.join(effectiveConfigDir, "projects"),
31038
+ path14.join(os7.homedir(), ".claude", "projects")
30660
31039
  ]);
30661
31040
  }
30662
31041
  sessionPathsForCwd(projectsDirs, cwd, sessionId) {
30663
31042
  return uniqueNormalizedPaths(
30664
31043
  projectsDirs.flatMap(
30665
31044
  (projectsDir) => claudeProjectSlugCandidates(cwd).map(
30666
- (slug) => path13.join(projectsDir, slug, `${sessionId}.jsonl`)
31045
+ (slug) => path14.join(projectsDir, slug, `${sessionId}.jsonl`)
30667
31046
  )
30668
31047
  )
30669
31048
  );
@@ -30684,7 +31063,7 @@ var AgentManager = class {
30684
31063
  }
30685
31064
  for (const entry of entries) {
30686
31065
  if (!entry.isDirectory()) continue;
30687
- const candidate = path13.join(projectsDir, entry.name, `${sessionId}.jsonl`);
31066
+ const candidate = path14.join(projectsDir, entry.name, `${sessionId}.jsonl`);
30688
31067
  if (fsSync.existsSync(candidate)) found.push(candidate);
30689
31068
  }
30690
31069
  }
@@ -30699,12 +31078,12 @@ var AgentManager = class {
30699
31078
  }
30700
31079
  const currentPathSet = new Set(
30701
31080
  currentCwdPaths.map(
30702
- (candidate) => process.platform === "win32" ? path13.normalize(candidate).toLowerCase() : path13.normalize(candidate)
31081
+ (candidate) => process.platform === "win32" ? path14.normalize(candidate).toLowerCase() : path14.normalize(candidate)
30703
31082
  )
30704
31083
  );
30705
31084
  const foundElsewhere = this.findSessionJsonlFiles(projectsDirs, sessionId).filter(
30706
31085
  (candidate) => {
30707
- const normalized = process.platform === "win32" ? path13.normalize(candidate).toLowerCase() : path13.normalize(candidate);
31086
+ const normalized = process.platform === "win32" ? path14.normalize(candidate).toLowerCase() : path14.normalize(candidate);
30708
31087
  return !currentPathSet.has(normalized);
30709
31088
  }
30710
31089
  );
@@ -30725,7 +31104,7 @@ var AgentManager = class {
30725
31104
  return null;
30726
31105
  }
30727
31106
  scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection, externalMcpFingerprint, runtimeToolPolicyFingerprint = "") {
30728
- const hash2 = createHash("sha256").update(SCOPE_PROMPT_FINGERPRINT_REVISION).update("\0").update(agentConfig.id).update("\0").update(agentConfig.name).update("\0").update(scopeKey(scope)).update("\0").update(path13.normalize(agentCwd)).update("\0").update(scopesSection).update("\0").update(externalMcpFingerprint);
31107
+ const hash2 = createHash("sha256").update(SCOPE_PROMPT_FINGERPRINT_REVISION).update("\0").update(agentConfig.id).update("\0").update(agentConfig.name).update("\0").update(scopeKey(scope)).update("\0").update(path14.normalize(agentCwd)).update("\0").update(scopesSection).update("\0").update(externalMcpFingerprint);
30729
31108
  if (runtimeToolPolicyFingerprint) {
30730
31109
  hash2.update("\0runtimeToolPolicy\0").update(runtimeToolPolicyFingerprint);
30731
31110
  }
@@ -31082,7 +31461,7 @@ var AgentManager = class {
31082
31461
  const scopedInstructions = cfg.instructions?.trim() && scope.kind === "group" ? `# Agent project instructions
31083
31462
  ${cfg.instructions.trim()}` : "";
31084
31463
  if (cfg.instructions?.trim() && scope.kind === "single") {
31085
- await fs6.writeFile(path13.join(agentCwd, "CLAUDE.md"), cfg.instructions.trim(), "utf-8");
31464
+ await fs7.writeFile(path14.join(agentCwd, "CLAUDE.md"), cfg.instructions.trim(), "utf-8");
31086
31465
  logger14.info("CLAUDE.md written", {
31087
31466
  agentId: agentConfig.id,
31088
31467
  scope: scopeKey(scope),
@@ -31091,10 +31470,10 @@ ${cfg.instructions.trim()}` : "";
31091
31470
  }
31092
31471
  let effectiveConfigDir = this.agentConfigDir;
31093
31472
  if (cfg.subscriptionType !== "system" && cfg.apiKey) {
31094
- effectiveConfigDir = path13.join(this.agentConfigDir, "api-key-agents", agentConfig.id);
31473
+ effectiveConfigDir = path14.join(this.agentConfigDir, "api-key-agents", agentConfig.id);
31095
31474
  let isNew = false;
31096
31475
  try {
31097
- await fs6.access(effectiveConfigDir);
31476
+ await fs7.access(effectiveConfigDir);
31098
31477
  } catch (e) {
31099
31478
  logger14.debug("Agent API key config dir missing; creating isolated dir", {
31100
31479
  error: e,
@@ -31102,7 +31481,7 @@ ${cfg.instructions.trim()}` : "";
31102
31481
  });
31103
31482
  isNew = true;
31104
31483
  }
31105
- await fs6.mkdir(effectiveConfigDir, { recursive: true });
31484
+ await fs7.mkdir(effectiveConfigDir, { recursive: true });
31106
31485
  if (isNew) {
31107
31486
  this.sessionStore.delete(agentConfig.id, scope);
31108
31487
  this.dispatchMemory.deleteScope(agentConfig.id, scope);
@@ -31110,18 +31489,18 @@ ${cfg.instructions.trim()}` : "";
31110
31489
  agentId: agentConfig.id
31111
31490
  });
31112
31491
  }
31113
- const settingsPath = path13.join(effectiveConfigDir, "settings.json");
31492
+ const settingsPath = path14.join(effectiveConfigDir, "settings.json");
31114
31493
  const envEntries = buildAnthropicCredentialEnv(cfg);
31115
31494
  if (cfg.apiBaseUrl) envEntries.ANTHROPIC_BASE_URL = cfg.apiBaseUrl;
31116
31495
  let existingSettings = {};
31117
31496
  if (fsSync.existsSync(settingsPath)) {
31118
31497
  try {
31119
- const raw = await fs6.readFile(settingsPath, "utf-8");
31498
+ const raw = await fs7.readFile(settingsPath, "utf-8");
31120
31499
  existingSettings = JSON.parse(raw);
31121
31500
  } catch (error51) {
31122
31501
  logger14.error("Failed to read existing API-key agent settings; starting fresh", {
31123
31502
  agentId: agentConfig.id,
31124
- settingsPath,
31503
+ settingsFile: "settings.json",
31125
31504
  error: error51
31126
31505
  });
31127
31506
  }
@@ -31131,10 +31510,10 @@ ${cfg.instructions.trim()}` : "";
31131
31510
  if (envEntries.ANTHROPIC_AUTH_TOKEN) delete mergedEnv.ANTHROPIC_API_KEY;
31132
31511
  if (envEntries.ANTHROPIC_API_KEY) delete mergedEnv.ANTHROPIC_AUTH_TOKEN;
31133
31512
  const mergedSettings = { ...existingSettings, env: mergedEnv };
31134
- await fs6.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
31513
+ await fs7.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
31135
31514
  logger14.info("API-key agent using isolated config dir", {
31136
31515
  agentId: agentConfig.id,
31137
- dir: effectiveConfigDir,
31516
+ configDirKind: "api-key-agent",
31138
31517
  isNew,
31139
31518
  hasBaseUrl: !!cfg.apiBaseUrl,
31140
31519
  settingsWritten: Object.keys(envEntries)
@@ -31228,12 +31607,23 @@ ${cfg.instructions.trim()}` : "";
31228
31607
  });
31229
31608
  }
31230
31609
  }
31231
- const externalMcp = this.mcpRegistry?.buildForAgent({
31610
+ const resolvedExternalMcp = this.mcpRegistry?.buildForAgent({
31232
31611
  agentId: agentConfig.id,
31233
31612
  capabilityTier: cfg.capabilityTier,
31234
31613
  isSmith: smithAgent,
31235
31614
  cwd: agentCwd
31236
31615
  }) ?? { mcpServers: {}, allowedTools: [], toolAbi: [] };
31616
+ const externalMcp = smithAgent ? selectSmithVisionMcp(resolvedExternalMcp) : resolvedExternalMcp;
31617
+ if (smithAgent && (Object.keys(resolvedExternalMcp.mcpServers).length !== Object.keys(externalMcp.mcpServers).length || resolvedExternalMcp.allowedTools.length !== externalMcp.allowedTools.length || (resolvedExternalMcp.toolAbi?.length ?? 0) !== (externalMcp.toolAbi?.length ?? 0))) {
31618
+ logger14.info("Smith external MCP filtered by fixed tool whitelist", {
31619
+ agentId: agentConfig.id,
31620
+ scope: scopeKey(scope),
31621
+ serverNames: Object.keys(resolvedExternalMcp.mcpServers),
31622
+ allowedToolCount: resolvedExternalMcp.allowedTools.length,
31623
+ retainedServerNames: Object.keys(externalMcp.mcpServers),
31624
+ retainedAllowedToolCount: externalMcp.allowedTools.length
31625
+ });
31626
+ }
31237
31627
  const visionMcpTools = resolveVisionMcpToolHints(externalMcp);
31238
31628
  logger14.info("External MCP resolved for runtime", {
31239
31629
  agentId: agentConfig.id,
@@ -31306,6 +31696,7 @@ ${cfg.instructions.trim()}` : "";
31306
31696
  logger14.info("Creating Agent query", {
31307
31697
  agentId: agentConfig.id,
31308
31698
  scope: scopeKey(scope),
31699
+ systemPromptMode: smithAgent ? "string" : "preset",
31309
31700
  cwd: agentCwd,
31310
31701
  resume: !!savedSessionId,
31311
31702
  sessionId: savedSessionId,
@@ -31326,71 +31717,65 @@ ${cfg.instructions.trim()}` : "";
31326
31717
  });
31327
31718
  const planModeRef = { active: false, denyCount: 0 };
31328
31719
  const mediaGenerationTurnGuard = createOfficialMediaGenerationTurnGuard();
31720
+ const platformRules = scope.kind === "group" ? PLATFORM_AGENT_RULES : PLATFORM_AGENT_RULES_SINGLE;
31721
+ const appendText = [
31722
+ platformRules,
31723
+ nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
31724
+ DOCUMENT_READING_RULES,
31725
+ MEDIA_GENERATION_RULES,
31726
+ agentConfig.systemPrompt,
31727
+ scopedInstructions,
31728
+ notebookSection,
31729
+ forkHistorySection,
31730
+ scopesSection
31731
+ ].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n");
31732
+ const systemPrompt = smithAgent ? appendText : { type: "preset", preset: "claude_code", append: appendText };
31733
+ const universalAllowedTools = [
31734
+ ...nativeReadToolDisabled ? [] : ["Read"],
31735
+ "Edit",
31736
+ "Write",
31737
+ "Bash",
31738
+ "Glob",
31739
+ "Grep",
31740
+ ...builtinWebSearchAllowed ? ["WebSearch"] : [],
31741
+ "WebFetch",
31742
+ "TodoWrite",
31743
+ "TaskCreate",
31744
+ "TaskUpdate",
31745
+ "AskUserQuestion",
31746
+ "mcp__neural__neural_send",
31747
+ "mcp__neural__neural_list_scopes",
31748
+ "mcp__neural__find_workdir_files",
31749
+ "mcp__neural__send_workdir_file",
31750
+ "mcp__neural__self_note",
31751
+ "mcp__neural__list_contacts",
31752
+ "mcp__neural__create_group",
31753
+ "mcp__neural__add_to_group",
31754
+ "mcp__neural__leave_group",
31755
+ "mcp__neural__remove_from_group",
31756
+ "mcp__neural__create_group_issue",
31757
+ "mcp__neural__resolve_group_issue",
31758
+ "mcp__neural__list_group_tasks",
31759
+ "mcp__neural__update_group_task",
31760
+ "mcp__neural__transfer_group_owner",
31761
+ "mcp__neural__post_to_moments",
31762
+ "mcp__neural__post_to_forum",
31763
+ "mcp__neural__read_moments",
31764
+ "mcp__neural__read_chat_history",
31765
+ "mcp__neural__read_document",
31766
+ "mcp__neural__list_available_skills",
31767
+ "mcp__neural__list_skill_index"
31768
+ ];
31329
31769
  const options = {
31330
31770
  cwd: agentCwd,
31331
- systemPrompt: {
31332
- type: "preset",
31333
- preset: "claude_code",
31334
- append: [
31335
- PLATFORM_AGENT_RULES,
31336
- nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
31337
- DOCUMENT_READING_RULES,
31338
- MEDIA_GENERATION_RULES,
31339
- agentConfig.systemPrompt,
31340
- scopedInstructions,
31341
- notebookSection,
31342
- forkHistorySection,
31343
- scopesSection
31344
- ].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
31345
- },
31771
+ systemPrompt,
31346
31772
  permissionMode: "bypassPermissions",
31347
31773
  allowDangerouslySkipPermissions: true,
31348
31774
  // allowedTools is the visibility whitelist passed to Claude Code. MCP tools
31349
31775
  // with always_ask must still be included here so the model can request them;
31350
31776
  // the MCP server policy/permission layer decides whether execution asks.
31351
31777
  allowedTools: [
31352
- ...nativeReadToolDisabled ? [] : ["Read"],
31353
- "Edit",
31354
- "Write",
31355
- "Bash",
31356
- "Glob",
31357
- "Grep",
31358
- ...builtinWebSearchAllowed ? ["WebSearch"] : [],
31359
- "WebFetch",
31360
- "TodoWrite",
31361
- "TaskCreate",
31362
- "TaskUpdate",
31363
- "AskUserQuestion",
31364
- "mcp__neural__neural_send",
31365
- "mcp__neural__neural_list_scopes",
31366
- "mcp__neural__self_note",
31367
- "mcp__neural__list_contacts",
31368
- "mcp__neural__create_group",
31369
- "mcp__neural__add_to_group",
31370
- "mcp__neural__leave_group",
31371
- "mcp__neural__remove_from_group",
31372
- "mcp__neural__create_group_issue",
31373
- "mcp__neural__resolve_group_issue",
31374
- "mcp__neural__list_group_tasks",
31375
- "mcp__neural__update_group_task",
31376
- "mcp__neural__transfer_group_owner",
31377
- "mcp__neural__post_to_moments",
31378
- "mcp__neural__post_to_forum",
31379
- "mcp__neural__read_moments",
31380
- "mcp__neural__read_chat_history",
31381
- "mcp__neural__read_document",
31382
- "mcp__neural__list_available_skills",
31383
- "mcp__neural__list_skill_index",
31384
- ...isSmithAgent2(agentConfig) ? [
31385
- "mcp__neural__create_agent",
31386
- "mcp__neural__update_agent_profile",
31387
- "mcp__neural__recommend_agent_skills",
31388
- "mcp__neural__read_skill",
31389
- "mcp__neural__list_friends",
31390
- "mcp__neural__accept_friend",
31391
- "mcp__neural__add_friend",
31392
- "mcp__neural__fetch_logs"
31393
- ] : [],
31778
+ ...smithAgent ? SMITH_ALLOWED_TOOLS : universalAllowedTools,
31394
31779
  ...externalMcp.allowedTools
31395
31780
  ],
31396
31781
  // Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
@@ -31402,7 +31787,7 @@ ${cfg.instructions.trim()}` : "";
31402
31787
  // instructions as the workflow body (replacing the default code-implementation
31403
31788
  // phases). The SDK wraps it with read-only enforcement + ExitPlanMode protocol.
31404
31789
  planModeInstructions: (() => {
31405
- const planTools = [
31790
+ const planTools = (smithAgent ? ["AskUserQuestion", "Write (plan file only)"] : [
31406
31791
  ...nativeReadToolDisabled ? [] : ["Read"],
31407
31792
  "Glob",
31408
31793
  "Grep",
@@ -31410,19 +31795,19 @@ ${cfg.instructions.trim()}` : "";
31410
31795
  "WebFetch",
31411
31796
  "AskUserQuestion",
31412
31797
  "Write (plan file only)"
31413
- ].join(", ");
31414
- const researchTools = [
31798
+ ]).join(", ");
31799
+ const researchTools = (smithAgent ? ["AskUserQuestion"] : [
31415
31800
  ...nativeReadToolDisabled ? [] : ["Read"],
31416
31801
  "Grep",
31417
31802
  ...builtinWebSearchAllowed ? ["WebSearch"] : []
31418
- ].join(", ");
31419
- const unavailableTools = [
31803
+ ]).join(", ");
31804
+ const unavailableTools = (smithAgent ? ["Read", "Edit", "Bash", "Glob", "Grep", "WebFetch", "TodoWrite", "ExitPlanMode"] : [
31420
31805
  "Edit",
31421
31806
  "Bash",
31422
31807
  "TodoWrite",
31423
31808
  "ExitPlanMode",
31424
31809
  ...nativeReadToolDisabled ? ["Read"] : []
31425
- ].join(", ");
31810
+ ]).join(", ");
31426
31811
  const smithTools = smithAgent ? "\nSMITH-SPECIFIC TOOLS (available in plan mode): mcp__neural__recommend_agent_skills, mcp__neural__read_skill, mcp__neural__fetch_logs, mcp__neural__create_agent, mcp__neural__update_agent_profile \u2014 use these to recommend initial skill sets, research existing skills, check logs, plan agent creation, and adjust Agent profiles." : "";
31427
31812
  return `You are a PLANNER, NOT an executor. The user will execute your plan later.
31428
31813
 
@@ -31652,17 +32037,17 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
31652
32037
  }
31653
32038
  };
31654
32039
  const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
31655
- const appendStr = options.systemPrompt.append;
31656
32040
  logger14.info("Platform rules attached", {
31657
32041
  agentId: agentConfig.id,
31658
32042
  scope: scopeKey(scope),
31659
- platformRulesLen: PLATFORM_AGENT_RULES.length,
32043
+ systemPromptMode: smithAgent ? "string" : "preset",
32044
+ platformRulesLen: platformRules.length,
31660
32045
  userPromptLen: userPromptTrimmed.length,
31661
32046
  hasUserPrompt: userPromptTrimmed.length > 0,
31662
32047
  notebookLen: notebookSection.length,
31663
32048
  forkHistoryLen: forkHistorySection.length,
31664
32049
  scopesLen: scopesSection.length,
31665
- appendLen: appendStr.length,
32050
+ appendLen: appendText.length,
31666
32051
  hasCreateAgentTool: smithAgent,
31667
32052
  hasLogDetectiveTools: smithAgent && this.skillStore !== null
31668
32053
  });
@@ -31682,7 +32067,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
31682
32067
  if (isRunningAsRoot()) {
31683
32068
  await chownForRootSpawn(effectiveConfigDir, "configDir");
31684
32069
  await chownForRootSpawn(agentCwd, "agentCwd");
31685
- const settingsFilePath = path13.join(effectiveConfigDir, "settings.json");
32070
+ const settingsFilePath = path14.join(effectiveConfigDir, "settings.json");
31686
32071
  await chownForRootSpawn(settingsFilePath, "settingsFile");
31687
32072
  options.spawnClaudeCodeProcess = (spawnOptions) => {
31688
32073
  const env2 = { ...spawnOptions.env, HOME: "/home/node" };
@@ -31732,6 +32117,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
31732
32117
  mcpAuditRecorder: this.mcpAuditRecorder,
31733
32118
  segmentBuffer: "",
31734
32119
  segmentCount: 0,
32120
+ workdirSignals: [],
31735
32121
  accumulatedToolInput: "",
31736
32122
  planModeRef,
31737
32123
  mediaGenerationTurnGuard,
@@ -31751,7 +32137,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
31751
32137
  mergedTasks: [],
31752
32138
  planModeBuffer: [],
31753
32139
  createdAt: Date.now(),
31754
- supportsVision: modelInputMode === "vision",
32140
+ supportsVision: cfg.supportsVision === true,
31755
32141
  modelInputMode,
31756
32142
  visionMcpTools,
31757
32143
  quietFlushTimer: null,
@@ -31939,7 +32325,7 @@ ${trimmed}`;
31939
32325
  promptWorkdir(agentConfig, scope, requestedCwd) {
31940
32326
  const remapped = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
31941
32327
  if (!isFullyQualifiedAbsolutePath(remapped)) {
31942
- return path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
32328
+ return path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
31943
32329
  }
31944
32330
  return remapped;
31945
32331
  }
@@ -32644,12 +33030,12 @@ ${lines.join("\n")}`;
32644
33030
  }
32645
33031
  async materializeAttachment(runtime, attachment, buffer) {
32646
33032
  const safeFileName = this.safeAttachmentFileName(attachment.fileName);
32647
- const attachmentDir = path13.join(runtime.cwd, ".ahchat-attachments", attachment.id);
33033
+ const attachmentDir = path14.join(runtime.cwd, ".ahchat-attachments", attachment.id);
32648
33034
  let filePath = await this.resolveExistingWorkspaceAttachmentPath(runtime, attachment);
32649
33035
  if (!filePath) {
32650
- await fs6.mkdir(attachmentDir, { recursive: true });
32651
- filePath = path13.join(attachmentDir, safeFileName);
32652
- await fs6.writeFile(filePath, buffer);
33036
+ await fs7.mkdir(attachmentDir, { recursive: true });
33037
+ filePath = path14.join(attachmentDir, safeFileName);
33038
+ await fs7.writeFile(filePath, buffer);
32653
33039
  }
32654
33040
  const materialized = { filePath };
32655
33041
  if (isReadableDocumentPath(filePath)) {
@@ -32678,10 +33064,10 @@ ${lines.join("\n")}`;
32678
33064
  const rawPath = typeof localWorkspacePath === "string" && localWorkspacePath.trim() ? localWorkspacePath : workspacePath;
32679
33065
  if (typeof rawPath !== "string" || !rawPath.trim()) return null;
32680
33066
  const remapped = remapServerWorkspacePath(rawPath, this.workspacesDir);
32681
- const candidate = path13.resolve(remapped.path);
33067
+ const candidate = path14.resolve(remapped.path);
32682
33068
  if (!this.isPathInsideBase(candidate, runtime.cwd)) return null;
32683
33069
  try {
32684
- const stat3 = await fs6.stat(candidate);
33070
+ const stat3 = await fs7.stat(candidate);
32685
33071
  return stat3.isFile() ? candidate : null;
32686
33072
  } catch (e) {
32687
33073
  logger14.warn("Workspace attachment path unavailable", {
@@ -32696,13 +33082,13 @@ ${lines.join("\n")}`;
32696
33082
  }
32697
33083
  }
32698
33084
  isPathInsideBase(filePath, basePath) {
32699
- const resolvedFile = path13.resolve(filePath);
32700
- const resolvedBase = path13.resolve(basePath);
32701
- const relative = path13.relative(resolvedBase, resolvedFile);
32702
- return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
33085
+ const resolvedFile = path14.resolve(filePath);
33086
+ const resolvedBase = path14.resolve(basePath);
33087
+ const relative = path14.relative(resolvedBase, resolvedFile);
33088
+ return relative === "" || !relative.startsWith("..") && !path14.isAbsolute(relative);
32703
33089
  }
32704
33090
  safeAttachmentFileName(fileName) {
32705
- const baseName = path13.basename(fileName).replace(/[\0/:\\]/g, "_").trim();
33091
+ const baseName = path14.basename(fileName).replace(/[\0/:\\]/g, "_").trim();
32706
33092
  return baseName || "attachment";
32707
33093
  }
32708
33094
  emitTaskPushError(runtime, task, error51) {
@@ -32765,6 +33151,7 @@ ${lines.join("\n")}`;
32765
33151
  proc.currentToolName = null;
32766
33152
  proc.segmentBuffer = "";
32767
33153
  proc.segmentCount = 0;
33154
+ proc.workdirSignals = [];
32768
33155
  }
32769
33156
  clearPostMergeContinuationTimer(runtime) {
32770
33157
  if (runtime.postMergeContinuationTimer) {
@@ -35078,8 +35465,8 @@ async function readJson(res) {
35078
35465
  return null;
35079
35466
  }
35080
35467
  }
35081
- function apiUrl(baseUrl, path34) {
35082
- return new URL(path34, baseUrl).toString();
35468
+ function apiUrl(baseUrl, path35) {
35469
+ return new URL(path35, baseUrl).toString();
35083
35470
  }
35084
35471
  async function fetchServerSkillSet(options) {
35085
35472
  try {
@@ -35333,8 +35720,8 @@ var HttpAgentRegistry = class {
35333
35720
  lastRefreshCount = null;
35334
35721
  apiUrl(suffix) {
35335
35722
  const base = this.serverApiUrl.replace(/\/$/, "");
35336
- const path34 = suffix.startsWith("/") ? suffix : `/${suffix}`;
35337
- return `${base}${path34}`;
35723
+ const path35 = suffix.startsWith("/") ? suffix : `/${suffix}`;
35724
+ return `${base}${path35}`;
35338
35725
  }
35339
35726
  async refresh() {
35340
35727
  const attempt = async () => {
@@ -35437,8 +35824,8 @@ var HttpSubscriptionRegistry = class {
35437
35824
  lastRefreshCount = null;
35438
35825
  apiUrl(suffix) {
35439
35826
  const base = this.serverApiUrl.replace(/\/$/, "");
35440
- const path34 = suffix.startsWith("/") ? suffix : `/${suffix}`;
35441
- return `${base}${path34}`;
35827
+ const path35 = suffix.startsWith("/") ? suffix : `/${suffix}`;
35828
+ return `${base}${path35}`;
35442
35829
  }
35443
35830
  rebuildPrimaryAlias() {
35444
35831
  this.subscriptions.delete(PRIMARY_COMPANY_SUBSCRIPTION_ID);
@@ -35516,8 +35903,8 @@ var HttpSubscriptionRegistry = class {
35516
35903
  };
35517
35904
 
35518
35905
  // src/mcpRegistry.ts
35519
- import fs7 from "fs";
35520
- import path14 from "path";
35906
+ import fs8 from "fs";
35907
+ import path15 from "path";
35521
35908
  import { fileURLToPath } from "url";
35522
35909
  var logger19 = createModuleLogger("mcp.registry");
35523
35910
  var AHCHAT_BUILTIN_MCP_COMMAND = "ahchat-builtin";
@@ -35535,11 +35922,12 @@ var HttpMcpRegistry = class {
35535
35922
  localStore;
35536
35923
  serverConnections = /* @__PURE__ */ new Map();
35537
35924
  localConnections = /* @__PURE__ */ new Map();
35925
+ missingSecretWarnedKeys = /* @__PURE__ */ new Set();
35538
35926
  lastRefreshCount = null;
35539
35927
  apiUrl(suffix) {
35540
35928
  const base = this.serverApiUrl.replace(/\/$/, "");
35541
- const path34 = suffix.startsWith("/") ? suffix : `/${suffix}`;
35542
- return `${base}${path34}`;
35929
+ const path35 = suffix.startsWith("/") ? suffix : `/${suffix}`;
35930
+ return `${base}${path35}`;
35543
35931
  }
35544
35932
  async refresh() {
35545
35933
  this.refreshLocal();
@@ -35635,13 +36023,19 @@ var HttpMcpRegistry = class {
35635
36023
  return false;
35636
36024
  });
35637
36025
  }
36026
+ warnMissingSecretOnce(connection) {
36027
+ const key = `${connection.id}:${connection.providerId}:${connection.serverName}`;
36028
+ if (this.missingSecretWarnedKeys.has(key)) return;
36029
+ this.missingSecretWarnedKeys.add(key);
36030
+ logger19.warn("Skipping MCP connection without required secret", {
36031
+ id: connection.id,
36032
+ providerId: connection.providerId,
36033
+ serverName: connection.serverName
36034
+ });
36035
+ }
35638
36036
  toSdkServerConfig(connection, ctx) {
35639
36037
  if (mcpConnectionRequiresSecret(connection) && !connection.hasAuthSecret) {
35640
- logger19.warn("Skipping MCP connection without required secret", {
35641
- id: connection.id,
35642
- providerId: connection.providerId,
35643
- serverName: connection.serverName
35644
- });
36038
+ this.warnMissingSecretOnce(connection);
35645
36039
  return null;
35646
36040
  }
35647
36041
  const policies = toolPolicies(connection);
@@ -35725,13 +36119,13 @@ function resolveVisionMcpExecutable(extraArgs, options = {}) {
35725
36119
  return resolveBundledMcpExecutable("visionMcpCli", extraArgs, options);
35726
36120
  }
35727
36121
  function resolveBundledMcpExecutable(cliBaseName, extraArgs, options = {}) {
35728
- const currentDir = options.currentDir ?? path14.dirname(fileURLToPath(import.meta.url));
36122
+ const currentDir = options.currentDir ?? path15.dirname(fileURLToPath(import.meta.url));
35729
36123
  const cwd = options.cwd ?? process.cwd();
35730
36124
  const execPath = options.execPath ?? process.execPath;
35731
- const existsSync3 = options.existsSync ?? fs7.existsSync;
36125
+ const existsSync3 = options.existsSync ?? fs8.existsSync;
35732
36126
  const distCliPath = firstExistingPath(
35733
36127
  [
35734
- path14.join(currentDir, `${cliBaseName}.cjs`)
36128
+ path15.join(currentDir, `${cliBaseName}.cjs`)
35735
36129
  ],
35736
36130
  existsSync3
35737
36131
  );
@@ -35744,7 +36138,7 @@ function resolveBundledMcpExecutable(cliBaseName, extraArgs, options = {}) {
35744
36138
  }
35745
36139
  const sourceCliPath = firstExistingPath(
35746
36140
  [
35747
- path14.join(currentDir, `${cliBaseName}.ts`)
36141
+ path15.join(currentDir, `${cliBaseName}.ts`)
35748
36142
  ],
35749
36143
  existsSync3
35750
36144
  );
@@ -35753,9 +36147,9 @@ function resolveBundledMcpExecutable(cliBaseName, extraArgs, options = {}) {
35753
36147
  }
35754
36148
  const workspaceDistCliPath = firstExistingPath(
35755
36149
  [
35756
- path14.resolve(currentDir, `../../bridge/dist/${cliBaseName}.cjs`),
35757
- path14.resolve(cwd, `packages/desktop/dist/${cliBaseName}.cjs`),
35758
- path14.resolve(cwd, `packages/bridge/dist/${cliBaseName}.cjs`)
36150
+ path15.resolve(currentDir, `../../bridge/dist/${cliBaseName}.cjs`),
36151
+ path15.resolve(cwd, `packages/desktop/dist/${cliBaseName}.cjs`),
36152
+ path15.resolve(cwd, `packages/bridge/dist/${cliBaseName}.cjs`)
35759
36153
  ],
35760
36154
  existsSync3
35761
36155
  );
@@ -35768,8 +36162,8 @@ function resolveBundledMcpExecutable(cliBaseName, extraArgs, options = {}) {
35768
36162
  }
35769
36163
  const workspaceSourceCliPath = firstExistingPath(
35770
36164
  [
35771
- path14.resolve(currentDir, `../../bridge/src/${cliBaseName}.ts`),
35772
- path14.resolve(cwd, `packages/bridge/src/${cliBaseName}.ts`)
36165
+ path15.resolve(currentDir, `../../bridge/src/${cliBaseName}.ts`),
36166
+ path15.resolve(cwd, `packages/bridge/src/${cliBaseName}.ts`)
35773
36167
  ],
35774
36168
  existsSync3
35775
36169
  );
@@ -35790,7 +36184,7 @@ function firstExistingPath(paths, existsSync3) {
35790
36184
  function shouldRunExecPathAsNode(execPath, options) {
35791
36185
  if (typeof options.isElectron === "boolean") return options.isElectron;
35792
36186
  if (process.versions.electron) return true;
35793
- return path14.basename(execPath).toLowerCase().includes("electron");
36187
+ return path15.basename(execPath).toLowerCase().includes("electron");
35794
36188
  }
35795
36189
  function uniqueServerName(serverName, usedNames) {
35796
36190
  if (!usedNames.has(serverName)) return serverName;
@@ -35828,19 +36222,19 @@ function toolPolicies(connection) {
35828
36222
  }
35829
36223
 
35830
36224
  // src/localMcpStore.ts
35831
- import fs8 from "fs";
35832
- import path15 from "path";
36225
+ import fs9 from "fs";
36226
+ import path16 from "path";
35833
36227
  var logger20 = createModuleLogger("bridge.localMcpStore");
35834
36228
  var LocalMcpStore = class {
35835
36229
  constructor(dataDir) {
35836
36230
  this.dataDir = dataDir;
35837
- this.filePath = path15.join(dataDir, LOCAL_MCP_CONNECTIONS_FILENAME);
36231
+ this.filePath = path16.join(dataDir, LOCAL_MCP_CONNECTIONS_FILENAME);
35838
36232
  }
35839
36233
  dataDir;
35840
36234
  filePath;
35841
36235
  list() {
35842
36236
  try {
35843
- const raw = fs8.readFileSync(this.filePath, "utf8");
36237
+ const raw = fs9.readFileSync(this.filePath, "utf8");
35844
36238
  return normalizeLocalMcpConnectionsFile(JSON.parse(raw)).connections;
35845
36239
  } catch (error51) {
35846
36240
  if (isNodeErrorCode(error51, "ENOENT")) return [];
@@ -35849,14 +36243,14 @@ var LocalMcpStore = class {
35849
36243
  }
35850
36244
  }
35851
36245
  watch(onChange) {
35852
- fs8.mkdirSync(this.dataDir, { recursive: true });
36246
+ fs9.mkdirSync(this.dataDir, { recursive: true });
35853
36247
  const listener = () => {
35854
36248
  void Promise.resolve(onChange()).catch((error51) => {
35855
36249
  logger20.error("Local MCP change handler failed", { filePath: this.filePath, error: error51 });
35856
36250
  });
35857
36251
  };
35858
- fs8.watchFile(this.filePath, { interval: 1e3 }, listener);
35859
- return () => fs8.unwatchFile(this.filePath, listener);
36252
+ fs9.watchFile(this.filePath, { interval: 1e3 }, listener);
36253
+ return () => fs9.unwatchFile(this.filePath, listener);
35860
36254
  }
35861
36255
  };
35862
36256
  function isNodeErrorCode(error51, code) {
@@ -35864,13 +36258,13 @@ function isNodeErrorCode(error51, code) {
35864
36258
  }
35865
36259
 
35866
36260
  // src/localSkillStore.ts
35867
- import fs9 from "fs";
35868
- import path16 from "path";
36261
+ import fs10 from "fs";
36262
+ import path17 from "path";
35869
36263
  var logger21 = createModuleLogger("bridge.localSkillStore");
35870
36264
  var LocalSkillStore = class {
35871
36265
  constructor(dataDir, runtimeScope = {}) {
35872
36266
  this.dataDir = dataDir;
35873
- this.filePath = path16.join(dataDir, LOCAL_SKILLS_FILENAME);
36267
+ this.filePath = path17.join(dataDir, LOCAL_SKILLS_FILENAME);
35874
36268
  this.runtimeScope = runtimeScope;
35875
36269
  }
35876
36270
  dataDir;
@@ -35881,7 +36275,7 @@ var LocalSkillStore = class {
35881
36275
  }
35882
36276
  readAll() {
35883
36277
  try {
35884
- const raw = fs9.readFileSync(this.filePath, "utf8");
36278
+ const raw = fs10.readFileSync(this.filePath, "utf8");
35885
36279
  return normalizeLocalSkillsFile(JSON.parse(raw)).skills;
35886
36280
  } catch (error51) {
35887
36281
  if (isNodeErrorCode2(error51, "ENOENT")) return [];
@@ -35925,24 +36319,24 @@ var LocalSkillStore = class {
35925
36319
  return nextEntry;
35926
36320
  }
35927
36321
  watch(onChange) {
35928
- fs9.mkdirSync(this.dataDir, { recursive: true });
36322
+ fs10.mkdirSync(this.dataDir, { recursive: true });
35929
36323
  const listener = () => {
35930
36324
  void Promise.resolve(onChange()).catch((error51) => {
35931
36325
  logger21.error("Local skill change handler failed", { filePath: this.filePath, error: error51 });
35932
36326
  });
35933
36327
  };
35934
- fs9.watchFile(this.filePath, { interval: 1e3 }, listener);
35935
- return () => fs9.unwatchFile(this.filePath, listener);
36328
+ fs10.watchFile(this.filePath, { interval: 1e3 }, listener);
36329
+ return () => fs10.unwatchFile(this.filePath, listener);
35936
36330
  }
35937
36331
  write(skills) {
35938
- fs9.mkdirSync(this.dataDir, { recursive: true });
36332
+ fs10.mkdirSync(this.dataDir, { recursive: true });
35939
36333
  const payload = normalizeLocalSkillsFile({ version: 1, skills });
35940
36334
  const tmpPath = `${this.filePath}.tmp`;
35941
- fs9.writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}
36335
+ fs10.writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}
35942
36336
  `, { encoding: "utf8", mode: 384 });
35943
- fs9.renameSync(tmpPath, this.filePath);
36337
+ fs10.renameSync(tmpPath, this.filePath);
35944
36338
  try {
35945
- fs9.chmodSync(this.filePath, 384);
36339
+ fs10.chmodSync(this.filePath, 384);
35946
36340
  } catch (error51) {
35947
36341
  logger21.warn("Failed to harden local skill file permissions", { filePath: this.filePath, error: error51 });
35948
36342
  }
@@ -36626,9 +37020,9 @@ var ServerConnector = class {
36626
37020
  };
36627
37021
 
36628
37022
  // src/contextDumper.ts
36629
- import fs10 from "fs/promises";
37023
+ import fs11 from "fs/promises";
36630
37024
  import os9 from "os";
36631
- import path17 from "path";
37025
+ import path18 from "path";
36632
37026
  import * as sdk3 from "@anthropic-ai/claude-agent-sdk";
36633
37027
  var logger24 = createModuleLogger("bridge.contextDumper");
36634
37028
  var TRUNCATE_THRESHOLD = 5e4;
@@ -36658,11 +37052,11 @@ function cwdToProjectSlug(cwd) {
36658
37052
  }
36659
37053
  function resolveJsonlPathInProjectsDir(projectsDir, sessionId, cwd) {
36660
37054
  const slug = cwdToProjectSlug(cwd);
36661
- return path17.join(projectsDir, slug, `${sessionId}.jsonl`);
37055
+ return path18.join(projectsDir, slug, `${sessionId}.jsonl`);
36662
37056
  }
36663
37057
  function resolveProjectDirInProjectsDir(projectsDir, cwd) {
36664
37058
  const slug = cwdToProjectSlug(cwd);
36665
- return path17.join(projectsDir, slug);
37059
+ return path18.join(projectsDir, slug);
36666
37060
  }
36667
37061
  function errorCode(e) {
36668
37062
  if (e instanceof Error && "code" in e) {
@@ -36673,7 +37067,7 @@ function errorCode(e) {
36673
37067
  }
36674
37068
  async function isReadableFile(filePath) {
36675
37069
  try {
36676
- const stat3 = await fs10.stat(filePath);
37070
+ const stat3 = await fs11.stat(filePath);
36677
37071
  return stat3.isFile();
36678
37072
  } catch (e) {
36679
37073
  const code = errorCode(e);
@@ -36688,7 +37082,7 @@ function uniquePaths(paths) {
36688
37082
  for (const p of paths) {
36689
37083
  const trimmed = p.trim();
36690
37084
  if (!trimmed) continue;
36691
- const key = path17.normalize(trimmed);
37085
+ const key = path18.normalize(trimmed);
36692
37086
  if (seen.has(key)) continue;
36693
37087
  seen.add(key);
36694
37088
  out.push(trimmed);
@@ -36704,7 +37098,7 @@ function inferAhchatUserAgentConfigDir(workdir) {
36704
37098
  const userSlug = afterMarker.split("/").find(Boolean);
36705
37099
  if (!userSlug) return null;
36706
37100
  const userDataDir2 = normalized.slice(0, markerIndex + marker.length + userSlug.length);
36707
- return path17.join(path17.normalize(userDataDir2), "agent-config");
37101
+ return path18.join(path18.normalize(userDataDir2), "agent-config");
36708
37102
  }
36709
37103
  function inferredAgentConfigDirsFromWorkdirs(workdirs) {
36710
37104
  return uniquePaths(workdirs.map((workdir) => inferAhchatUserAgentConfigDir(workdir) ?? ""));
@@ -36717,19 +37111,19 @@ function claudeProjectsDirs(opts) {
36717
37111
  ]);
36718
37112
  return uniquePaths([
36719
37113
  ...configDirs.flatMap((configDir) => [
36720
- path17.join(configDir, "api-key-agents", opts.agentId, "projects"),
36721
- path17.join(configDir, "projects")
37114
+ path18.join(configDir, "api-key-agents", opts.agentId, "projects"),
37115
+ path18.join(configDir, "projects")
36722
37116
  ]),
36723
- path17.join(os9.homedir(), ".claude", "projects")
37117
+ path18.join(os9.homedir(), ".claude", "projects")
36724
37118
  ]);
36725
37119
  }
36726
37120
  function fallbackBridgeWorkspacePath(requestedCwd, workspacesDir, fallbackSegment) {
36727
37121
  const suffix = extractAhchatWorkspaceSuffix(requestedCwd);
36728
- if (suffix) return path17.join(workspacesDir, suffix);
37122
+ if (suffix) return path18.join(workspacesDir, suffix);
36729
37123
  const normalized = requestedCwd.trim();
36730
- const basename = normalized ? path17.basename(path17.normalize(normalized)) : "";
36731
- const segment = basename && basename !== "." && basename !== path17.sep ? basename : fallbackSegment;
36732
- return path17.join(workspacesDir, segment);
37124
+ const basename = normalized ? path18.basename(path18.normalize(normalized)) : "";
37125
+ const segment = basename && basename !== "." && basename !== path18.sep ? basename : fallbackSegment;
37126
+ return path18.join(workspacesDir, segment);
36733
37127
  }
36734
37128
  function cwdCandidatesForBridge(requestedCwd, opts) {
36735
37129
  const candidates = [requestedCwd];
@@ -36765,11 +37159,11 @@ async function resolveDumpWorkdir(requestedCwd, opts) {
36765
37159
  return remapped.path;
36766
37160
  }
36767
37161
  try {
36768
- await fs10.mkdir(requestedCwd, { recursive: true });
37162
+ await fs11.mkdir(requestedCwd, { recursive: true });
36769
37163
  return requestedCwd;
36770
37164
  } catch (e) {
36771
37165
  const fallback = fallbackBridgeWorkspacePath(requestedCwd, opts.workspacesDir, opts.fallbackSegment);
36772
- if (path17.normalize(fallback) === path17.normalize(requestedCwd)) throw e;
37166
+ if (path18.normalize(fallback) === path18.normalize(requestedCwd)) throw e;
36773
37167
  logger24.warn("Dump workdir inaccessible; using local Bridge workspace fallback", {
36774
37168
  agentId: opts.agentId,
36775
37169
  requested: requestedCwd,
@@ -36783,7 +37177,7 @@ async function findJsonlInClaudeProjects(sessionId, projectsDirs) {
36783
37177
  for (const projectsDir of projectsDirs) {
36784
37178
  let dirs;
36785
37179
  try {
36786
- dirs = await fs10.readdir(projectsDir, { withFileTypes: true });
37180
+ dirs = await fs11.readdir(projectsDir, { withFileTypes: true });
36787
37181
  } catch (e) {
36788
37182
  const code = errorCode(e);
36789
37183
  if (code === "ENOENT" || code === "ENOTDIR") continue;
@@ -36792,7 +37186,7 @@ async function findJsonlInClaudeProjects(sessionId, projectsDirs) {
36792
37186
  }
36793
37187
  for (const dir of dirs) {
36794
37188
  if (!dir.isDirectory()) continue;
36795
- const candidate = path17.join(projectsDir, dir.name, `${sessionId}.jsonl`);
37189
+ const candidate = path18.join(projectsDir, dir.name, `${sessionId}.jsonl`);
36796
37190
  if (await isReadableFile(candidate)) return candidate;
36797
37191
  }
36798
37192
  }
@@ -36801,7 +37195,7 @@ async function findJsonlInClaudeProjects(sessionId, projectsDirs) {
36801
37195
  async function latestJsonlInProjectDir(projectDir) {
36802
37196
  let entries;
36803
37197
  try {
36804
- entries = await fs10.readdir(projectDir, { withFileTypes: true });
37198
+ entries = await fs11.readdir(projectDir, { withFileTypes: true });
36805
37199
  } catch (e) {
36806
37200
  const code = errorCode(e);
36807
37201
  if (code === "ENOENT" || code === "ENOTDIR") return null;
@@ -36811,15 +37205,15 @@ async function latestJsonlInProjectDir(projectDir) {
36811
37205
  let latest = null;
36812
37206
  for (const entry of entries) {
36813
37207
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
36814
- const jsonlPath = path17.join(projectDir, entry.name);
37208
+ const jsonlPath = path18.join(projectDir, entry.name);
36815
37209
  let stat3;
36816
37210
  try {
36817
- stat3 = await fs10.stat(jsonlPath);
37211
+ stat3 = await fs11.stat(jsonlPath);
36818
37212
  } catch (e) {
36819
37213
  logger24.warn("Discovered JSONL stat failed", { jsonlPath, error: e });
36820
37214
  continue;
36821
37215
  }
36822
- const sessionId = path17.basename(entry.name, ".jsonl");
37216
+ const sessionId = path18.basename(entry.name, ".jsonl");
36823
37217
  const discovered = { sessionId, jsonlPath, lastModified: stat3.mtimeMs };
36824
37218
  if (!latest || discovered.lastModified > latest.lastModified) {
36825
37219
  latest = discovered;
@@ -36828,8 +37222,8 @@ async function latestJsonlInProjectDir(projectDir) {
36828
37222
  return latest;
36829
37223
  }
36830
37224
  function isAgentIsolatedProjectsDir(projectsDir, agentId) {
36831
- const normalized = path17.normalize(projectsDir).toLowerCase();
36832
- const marker = path17.normalize(path17.join("api-key-agents", agentId, "projects")).toLowerCase();
37225
+ const normalized = path18.normalize(projectsDir).toLowerCase();
37226
+ const marker = path18.normalize(path18.join("api-key-agents", agentId, "projects")).toLowerCase();
36833
37227
  return normalized.endsWith(marker);
36834
37228
  }
36835
37229
  async function discoverLatestJsonlForScope(opts) {
@@ -36848,7 +37242,7 @@ async function discoverLatestJsonlForScope(opts) {
36848
37242
  if (!isAgentIsolatedProjectsDir(projectsDir, opts.agentId)) continue;
36849
37243
  let dirs;
36850
37244
  try {
36851
- dirs = await fs10.readdir(projectsDir, { withFileTypes: true });
37245
+ dirs = await fs11.readdir(projectsDir, { withFileTypes: true });
36852
37246
  } catch (e) {
36853
37247
  const code = errorCode(e);
36854
37248
  if (code === "ENOENT" || code === "ENOTDIR") continue;
@@ -36857,7 +37251,7 @@ async function discoverLatestJsonlForScope(opts) {
36857
37251
  }
36858
37252
  for (const dir of dirs) {
36859
37253
  if (!dir.isDirectory()) continue;
36860
- const latest = await latestJsonlInProjectDir(path17.join(projectsDir, dir.name));
37254
+ const latest = await latestJsonlInProjectDir(path18.join(projectsDir, dir.name));
36861
37255
  if (latest) discovered.push(latest);
36862
37256
  }
36863
37257
  }
@@ -36894,7 +37288,7 @@ function friendlyScopeError(e) {
36894
37288
  }
36895
37289
  var RENDERABLE_TYPES = /* @__PURE__ */ new Set(["user", "assistant", "system", "attachment"]);
36896
37290
  async function readJsonlEntries(filePath) {
36897
- const raw = await fs10.readFile(filePath, "utf-8");
37291
+ const raw = await fs11.readFile(filePath, "utf-8");
36898
37292
  const entries = [];
36899
37293
  for (const line of raw.split("\n")) {
36900
37294
  const trimmed = line.trim();
@@ -37250,8 +37644,8 @@ async function dumpAgentContext(agentId, deps) {
37250
37644
  agentId,
37251
37645
  workdirOverrideStore: deps.workdirOverrideStore
37252
37646
  });
37253
- const dumpDir = path17.join(localWorkdir, "sessioninfo");
37254
- await fs10.mkdir(dumpDir, { recursive: true });
37647
+ const dumpDir = path18.join(localWorkdir, "sessioninfo");
37648
+ await fs11.mkdir(dumpDir, { recursive: true });
37255
37649
  const groupWorkdirs = deps.groupRegistry?.getMyGroups(agentId).map((group) => group.workingDirectory) ?? [];
37256
37650
  const inferredAgentConfigDirs = inferredAgentConfigDirsFromWorkdirs([
37257
37651
  workdir,
@@ -37329,7 +37723,7 @@ async function dumpAgentContext(agentId, deps) {
37329
37723
  projectsDirs,
37330
37724
  sessionStore: deps.sessionStore
37331
37725
  });
37332
- resolvedSessionId = recovered ? path17.basename(jsonlPath, ".jsonl") : resolvedSessionId;
37726
+ resolvedSessionId = recovered ? path18.basename(jsonlPath, ".jsonl") : resolvedSessionId;
37333
37727
  if (recovered) {
37334
37728
  try {
37335
37729
  const info = await sdk3.getSessionInfo(resolvedSessionId);
@@ -37378,10 +37772,10 @@ async function dumpAgentContext(agentId, deps) {
37378
37772
  jsonlPath
37379
37773
  });
37380
37774
  const filename = scopeFilename(agent.name, scopeKey2, groupName);
37381
- const filePath = path17.join(dumpDir, filename);
37382
- await fs10.writeFile(filePath, html, "utf-8");
37775
+ const filePath = path18.join(dumpDir, filename);
37776
+ await fs11.writeFile(filePath, html, "utf-8");
37383
37777
  dumpedFiles.push(filename);
37384
- const stat3 = await fs10.stat(filePath);
37778
+ const stat3 = await fs11.stat(filePath);
37385
37779
  logger24.info("Scope context dumped", {
37386
37780
  agentId,
37387
37781
  scopeKey: scopeKey2,
@@ -37421,9 +37815,9 @@ async function dumpAgentContext(agentId, deps) {
37421
37815
  // src/clipboardFiles.ts
37422
37816
  import { execFile as execFile2 } from "child_process";
37423
37817
  import crypto3 from "crypto";
37424
- import fs11 from "fs/promises";
37818
+ import fs12 from "fs/promises";
37425
37819
  import os10 from "os";
37426
- import path18 from "path";
37820
+ import path19 from "path";
37427
37821
  import { promisify as promisify2 } from "util";
37428
37822
  var logger25 = createModuleLogger("bridge.clipboardFiles");
37429
37823
  var execFileAsync2 = promisify2(execFile2);
@@ -37434,28 +37828,28 @@ function isRecord5(value) {
37434
37828
  }
37435
37829
  function normalizeLocalPath(targetPath) {
37436
37830
  const trimmed = targetPath.trim();
37437
- const expanded = trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("~\\") ? path18.join(os10.homedir(), trimmed.slice(2)) : trimmed;
37438
- return path18.normalize(path18.resolve(expanded));
37831
+ const expanded = trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("~\\") ? path19.join(os10.homedir(), trimmed.slice(2)) : trimmed;
37832
+ return path19.normalize(path19.resolve(expanded));
37439
37833
  }
37440
37834
  function normalizeClipboardIdentityKey(value) {
37441
37835
  return process.platform === "win32" ? value.toLowerCase() : value;
37442
37836
  }
37443
37837
  async function resolveClipboardPathKey(resolvedPath) {
37444
37838
  try {
37445
- return normalizeClipboardIdentityKey(await fs11.realpath(resolvedPath));
37839
+ return normalizeClipboardIdentityKey(await fs12.realpath(resolvedPath));
37446
37840
  } catch (e) {
37447
37841
  logger25.debug("Clipboard realpath read skipped", { error: e, path: resolvedPath });
37448
37842
  return normalizeClipboardIdentityKey(resolvedPath);
37449
37843
  }
37450
37844
  }
37451
37845
  function looksLikeWindowsShortFileName(fileName) {
37452
- return /~\d/i.test(path18.basename(fileName));
37846
+ return /~\d/i.test(path19.basename(fileName));
37453
37847
  }
37454
37848
  function fileContentHash(buffer) {
37455
37849
  return crypto3.createHash("sha256").update(buffer).digest("hex");
37456
37850
  }
37457
37851
  function mimeTypeForFileName(fileName) {
37458
- const ext = path18.extname(fileName).toLowerCase();
37852
+ const ext = path19.extname(fileName).toLowerCase();
37459
37853
  if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
37460
37854
  if (ext === ".png") return "image/png";
37461
37855
  if (ext === ".webp") return "image/webp";
@@ -37520,7 +37914,7 @@ async function readWindowsClipboardPathCandidates() {
37520
37914
  }
37521
37915
  }
37522
37916
  async function filePayloadFromPath(resolvedPath) {
37523
- const stat3 = await fs11.stat(resolvedPath);
37917
+ const stat3 = await fs12.stat(resolvedPath);
37524
37918
  if (!stat3.isFile()) return null;
37525
37919
  if (stat3.size > MAX_CLIPBOARD_FILE_SIZE) {
37526
37920
  logger25.warn("Clipboard file skipped because it is too large", {
@@ -37529,10 +37923,10 @@ async function filePayloadFromPath(resolvedPath) {
37529
37923
  });
37530
37924
  return null;
37531
37925
  }
37532
- const buffer = await fs11.readFile(resolvedPath);
37926
+ const buffer = await fs12.readFile(resolvedPath);
37533
37927
  const mimeType = mimeTypeForFileName(resolvedPath);
37534
37928
  return {
37535
- fileName: path18.basename(resolvedPath),
37929
+ fileName: path19.basename(resolvedPath),
37536
37930
  mimeType,
37537
37931
  contentBase64: buffer.toString("base64"),
37538
37932
  size: buffer.length,
@@ -37556,7 +37950,7 @@ async function readClipboardFiles() {
37556
37950
  const contentIndexes = /* @__PURE__ */ new Map();
37557
37951
  for (const resolvedPath of pathCandidates) {
37558
37952
  try {
37559
- const stat3 = await fs11.stat(resolvedPath);
37953
+ const stat3 = await fs12.stat(resolvedPath);
37560
37954
  if (stat3.isDirectory()) {
37561
37955
  skippedDirectoryCount += 1;
37562
37956
  continue;
@@ -37605,8 +37999,8 @@ async function readClipboardFiles() {
37605
37999
  }
37606
38000
 
37607
38001
  // src/listDir.ts
37608
- import fs12 from "fs/promises";
37609
- import path19 from "path";
38002
+ import fs13 from "fs/promises";
38003
+ import path20 from "path";
37610
38004
  var logger26 = createModuleLogger("bridge.listDir");
37611
38005
  function shouldIncludeEntry(name) {
37612
38006
  if (!name.startsWith(".")) return true;
@@ -37615,16 +38009,16 @@ function shouldIncludeEntry(name) {
37615
38009
  async function listDirectoryEntries(dirPath) {
37616
38010
  const resolvedDirPath = resolveUserPath(dirPath);
37617
38011
  logger26.info("listDirectoryEntries start", { path: dirPath, resolvedPath: resolvedDirPath });
37618
- const raw = await fs12.readdir(resolvedDirPath, { withFileTypes: true });
38012
+ const raw = await fs13.readdir(resolvedDirPath, { withFileTypes: true });
37619
38013
  const entries = [];
37620
38014
  for (const entry of raw) {
37621
38015
  if (!shouldIncludeEntry(entry.name)) continue;
37622
- const fullPath = path19.join(resolvedDirPath, entry.name);
38016
+ const fullPath = path20.join(resolvedDirPath, entry.name);
37623
38017
  const isDir = entry.isDirectory();
37624
38018
  let size;
37625
38019
  let mtime;
37626
38020
  try {
37627
- const stat3 = await fs12.stat(fullPath);
38021
+ const stat3 = await fs13.stat(fullPath);
37628
38022
  mtime = stat3.mtime.toISOString();
37629
38023
  if (!isDir) size = stat3.size;
37630
38024
  } catch (statErr) {
@@ -37653,20 +38047,20 @@ function normalizeRelativePath(relativePath) {
37653
38047
  return normalized;
37654
38048
  }
37655
38049
  function ensureInsideBase(baseDir, targetPath) {
37656
- const relative = path19.relative(baseDir, targetPath);
37657
- if (relative.startsWith("..") || path19.isAbsolute(relative)) {
38050
+ const relative = path20.relative(baseDir, targetPath);
38051
+ if (relative.startsWith("..") || path20.isAbsolute(relative)) {
37658
38052
  throw new Error("path is outside working directory");
37659
38053
  }
37660
38054
  }
37661
38055
  function ensureNotBaseDir(baseDir, targetPath) {
37662
- if (path19.relative(baseDir, targetPath) === "") {
38056
+ if (path20.relative(baseDir, targetPath) === "") {
37663
38057
  throw new Error("refusing to delete working directory root");
37664
38058
  }
37665
38059
  }
37666
38060
  async function ensureRealPathInsideBase(baseDir, targetPath) {
37667
38061
  const [realBase, realTarget] = await Promise.all([
37668
- fs12.realpath(baseDir),
37669
- fs12.realpath(targetPath)
38062
+ fs13.realpath(baseDir),
38063
+ fs13.realpath(targetPath)
37670
38064
  ]);
37671
38065
  ensureInsideBase(realBase, realTarget);
37672
38066
  return realTarget;
@@ -37674,11 +38068,11 @@ async function ensureRealPathInsideBase(baseDir, targetPath) {
37674
38068
  async function writeWorkdirFile(opts) {
37675
38069
  const resolvedBaseDir = resolveUserPath(opts.baseDir);
37676
38070
  const safeRelativePath = normalizeRelativePath(opts.relativePath);
37677
- const targetPath = path19.resolve(resolvedBaseDir, safeRelativePath);
38071
+ const targetPath = path20.resolve(resolvedBaseDir, safeRelativePath);
37678
38072
  ensureInsideBase(resolvedBaseDir, targetPath);
37679
38073
  const buffer = Buffer.from(opts.contentBase64, "base64");
37680
- await fs12.mkdir(path19.dirname(targetPath), { recursive: true });
37681
- await fs12.writeFile(targetPath, buffer, { flag: opts.overwrite ? "w" : "wx" });
38074
+ await fs13.mkdir(path20.dirname(targetPath), { recursive: true });
38075
+ await fs13.writeFile(targetPath, buffer, { flag: opts.overwrite ? "w" : "wx" });
37682
38076
  logger26.info("writeWorkdirFile ok", {
37683
38077
  baseDir: opts.baseDir,
37684
38078
  path: targetPath,
@@ -37690,12 +38084,12 @@ async function writeWorkdirFile(opts) {
37690
38084
  async function readWorkdirFile(filePath, baseDir) {
37691
38085
  const resolvedPath = resolveUserPath(filePath);
37692
38086
  const safePath = baseDir ? await ensureRealPathInsideBase(resolveUserPath(baseDir), resolvedPath) : resolvedPath;
37693
- const stat3 = await fs12.stat(safePath);
38087
+ const stat3 = await fs13.stat(safePath);
37694
38088
  if (!stat3.isFile()) throw new Error("path is not a file");
37695
- const buffer = await fs12.readFile(safePath);
38089
+ const buffer = await fs13.readFile(safePath);
37696
38090
  logger26.info("readWorkdirFile ok", { path: filePath, baseDir, resolvedPath: safePath, size: buffer.length });
37697
38091
  return {
37698
- fileName: path19.basename(safePath),
38092
+ fileName: path20.basename(safePath),
37699
38093
  contentBase64: buffer.toString("base64"),
37700
38094
  size: buffer.length
37701
38095
  };
@@ -37705,9 +38099,9 @@ async function allocateTrashPath(trashDir, name) {
37705
38099
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
37706
38100
  for (let index = 0; index < 1e3; index += 1) {
37707
38101
  const suffix = index === 0 ? "" : `-${index}`;
37708
- const candidate = path19.join(trashDir, `${stamp}-${safeName}${suffix}`);
38102
+ const candidate = path20.join(trashDir, `${stamp}-${safeName}${suffix}`);
37709
38103
  try {
37710
- await fs12.lstat(candidate);
38104
+ await fs13.lstat(candidate);
37711
38105
  } catch (e) {
37712
38106
  if (e instanceof Error && "code" in e && e.code === "ENOENT") return candidate;
37713
38107
  throw e;
@@ -37723,15 +38117,15 @@ async function trashWorkdirPath(opts) {
37723
38117
  const resolvedBaseDir = resolveUserPath(opts.baseDir);
37724
38118
  const resolvedTargetPath = resolveUserPath(opts.targetPath);
37725
38119
  const [realBase, realTarget] = await Promise.all([
37726
- fs12.realpath(resolvedBaseDir),
37727
- fs12.realpath(resolvedTargetPath)
38120
+ fs13.realpath(resolvedBaseDir),
38121
+ fs13.realpath(resolvedTargetPath)
37728
38122
  ]);
37729
38123
  ensureInsideBase(realBase, realTarget);
37730
38124
  ensureNotBaseDir(realBase, realTarget);
37731
- const trashDir = path19.join(resolvedBaseDir, ".ahchat-trash");
37732
- await fs12.mkdir(trashDir, { recursive: true });
37733
- const trashedPath = await allocateTrashPath(trashDir, path19.basename(resolvedTargetPath));
37734
- await fs12.rename(resolvedTargetPath, trashedPath);
38125
+ const trashDir = path20.join(resolvedBaseDir, ".ahchat-trash");
38126
+ await fs13.mkdir(trashDir, { recursive: true });
38127
+ const trashedPath = await allocateTrashPath(trashDir, path20.basename(resolvedTargetPath));
38128
+ await fs13.rename(resolvedTargetPath, trashedPath);
37735
38129
  logger26.info("trashWorkdirPath ok", {
37736
38130
  path: opts.targetPath,
37737
38131
  baseDir: opts.baseDir,
@@ -37742,8 +38136,8 @@ async function trashWorkdirPath(opts) {
37742
38136
  }
37743
38137
 
37744
38138
  // src/logScanner.ts
37745
- import fs13 from "fs";
37746
- import path20 from "path";
38139
+ import fs14 from "fs";
38140
+ import path21 from "path";
37747
38141
  import os11 from "os";
37748
38142
  import readline from "readline";
37749
38143
  var logger27 = createModuleLogger("bridge.logScanner");
@@ -37751,18 +38145,18 @@ var MAX_LIMIT = 2e3;
37751
38145
  function listLogFiles(logsDir, baseName) {
37752
38146
  let names;
37753
38147
  try {
37754
- names = fs13.readdirSync(logsDir);
38148
+ names = fs14.readdirSync(logsDir);
37755
38149
  } catch (e) {
37756
38150
  logger27.warn("listLogFiles: readdir failed", { logsDir, error: e });
37757
38151
  return [];
37758
38152
  }
37759
38153
  const escapedBaseName = baseName.replace(".", "\\.");
37760
38154
  const pattern = new RegExp(`^${escapedBaseName}(?:[.-].+)?$`);
37761
- return names.filter((n) => pattern.test(n)).map((n) => path20.join(logsDir, n));
38155
+ return names.filter((n) => pattern.test(n)).map((n) => path21.join(logsDir, n));
37762
38156
  }
37763
38157
  async function scanFile(filePath, source, filter, state) {
37764
- const file2 = path20.basename(filePath);
37765
- const stream = fs13.createReadStream(filePath, { encoding: "utf-8" });
38158
+ const file2 = path21.basename(filePath);
38159
+ const stream = fs14.createReadStream(filePath, { encoding: "utf-8" });
37766
38160
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
37767
38161
  let lineNum = 0;
37768
38162
  for await (const line of rl) {
@@ -37805,7 +38199,7 @@ async function scanLocalLogs(logsDir, baseName, filter) {
37805
38199
  };
37806
38200
  }
37807
38201
  async function scanBridgeLogs(filter) {
37808
- const logDir = path20.join(loadBridgeConfig().dataDir || path20.join(os11.homedir(), ".ahchat"), "logs");
38202
+ const logDir = path21.join(loadBridgeConfig().dataDir || path21.join(os11.homedir(), ".ahchat"), "logs");
37809
38203
  logger27.info("scanBridgeLogs start", {
37810
38204
  logDir,
37811
38205
  startIso: filter.startIso,
@@ -37824,34 +38218,73 @@ async function scanBridgeLogs(filter) {
37824
38218
  }
37825
38219
 
37826
38220
  // src/logUploader.ts
37827
- import fs14 from "fs";
38221
+ import fs15 from "fs";
37828
38222
  import fsp from "fs/promises";
37829
- import path21 from "path";
38223
+ import path22 from "path";
37830
38224
  var logger28 = createModuleLogger("bridge.logUploader");
38225
+ var STALE_RATE_LIMIT_SKIP_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
37831
38226
  var DEFAULT_LOG_UPLOAD_INTERVAL_MS = 24 * 60 * 60 * 1e3;
37832
38227
  var DEFAULT_BATCH_SIZE = 200;
38228
+ var MAX_UPLOAD_CHUNK_BODY_BYTES = 448 * 1024;
38229
+ var MAX_UPLOAD_RETRY_AFTER_MS = 24 * 60 * 60 * 1e3;
38230
+ var LogUploadHttpError = class extends Error {
38231
+ status;
38232
+ scope;
38233
+ retryAfterMs;
38234
+ constructor(status, bodyText) {
38235
+ const parsed = parseUploadErrorBody(bodyText);
38236
+ const summary = parsed.error ?? bodyText;
38237
+ super(`upload failed HTTP ${status}: ${summary.slice(0, 160)}`);
38238
+ this.name = "LogUploadHttpError";
38239
+ this.status = status;
38240
+ if (parsed.scope) this.scope = parsed.scope;
38241
+ if (parsed.retryAfterMs !== void 0) this.retryAfterMs = parsed.retryAfterMs;
38242
+ }
38243
+ };
38244
+ function normalizeRetryAfterMs(value) {
38245
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
38246
+ return Math.min(Math.ceil(value), MAX_UPLOAD_RETRY_AFTER_MS);
38247
+ }
38248
+ function parseUploadErrorBody(bodyText) {
38249
+ try {
38250
+ const parsed = JSON.parse(bodyText);
38251
+ if (typeof parsed !== "object" || parsed === null) return {};
38252
+ const body = parsed;
38253
+ return {
38254
+ ...typeof body.error === "string" && body.error.length > 0 ? { error: body.error } : {},
38255
+ ...typeof body.scope === "string" && body.scope.length > 0 ? { scope: body.scope } : {},
38256
+ ...(() => {
38257
+ const retryAfterMs = normalizeRetryAfterMs(body.retryAfterMs);
38258
+ return retryAfterMs === void 0 ? {} : { retryAfterMs };
38259
+ })()
38260
+ };
38261
+ } catch (e) {
38262
+ logger28.debug("Failed to parse log upload error body", { error: e });
38263
+ return {};
38264
+ }
38265
+ }
37833
38266
  function defaultLogFile(dataDir) {
37834
- return path21.join(dataDir, "logs", "bridge.log");
38267
+ return path22.join(dataDir, "logs", "bridge.log");
37835
38268
  }
37836
38269
  function defaultCursorFile(dataDir) {
37837
- return path21.join(dataDir, "log-upload-cursor.json");
38270
+ return path22.join(dataDir, "log-upload-cursor.json");
37838
38271
  }
37839
38272
  function uploadedFileName(logFile, explicit) {
37840
38273
  const trimmed = explicit?.trim();
37841
- return trimmed || path21.basename(logFile);
38274
+ return trimmed || path22.basename(logFile);
37842
38275
  }
37843
38276
  function normalizeTarget(file2, dataDir) {
37844
38277
  const fileName = uploadedFileName(file2.logFile, file2.uploadedFileName);
37845
38278
  const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
37846
38279
  return {
37847
38280
  logFile: file2.logFile,
37848
- cursorFile: file2.cursorFile ?? path21.join(dataDir, `log-upload-cursor-${safeName}.json`),
38281
+ cursorFile: file2.cursorFile ?? path22.join(dataDir, `log-upload-cursor-${safeName}.json`),
37849
38282
  uploadedFileName: fileName
37850
38283
  };
37851
38284
  }
37852
38285
  function cursorFileForLog(dataDir, fileName) {
37853
38286
  const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
37854
- return path21.join(dataDir, `log-upload-cursor-${safeName}.json`);
38287
+ return path22.join(dataDir, `log-upload-cursor-${safeName}.json`);
37855
38288
  }
37856
38289
  function logFileFingerprint(stat3) {
37857
38290
  if (typeof stat3.dev === "number" && typeof stat3.ino === "number" && stat3.ino > 0) {
@@ -37860,8 +38293,8 @@ function logFileFingerprint(stat3) {
37860
38293
  return void 0;
37861
38294
  }
37862
38295
  async function listRotatedBridgeTargets(primary, dataDir) {
37863
- const logDir = path21.dirname(primary.logFile);
37864
- const baseName = path21.basename(primary.logFile);
38296
+ const logDir = path22.dirname(primary.logFile);
38297
+ const baseName = path22.basename(primary.logFile);
37865
38298
  let names;
37866
38299
  try {
37867
38300
  names = await fsp.readdir(logDir);
@@ -37874,7 +38307,7 @@ async function listRotatedBridgeTargets(primary, dataDir) {
37874
38307
  return names.filter((name) => pattern.test(name)).sort().map((name) => {
37875
38308
  if (name === baseName) return primary;
37876
38309
  return {
37877
- logFile: path21.join(logDir, name),
38310
+ logFile: path22.join(logDir, name),
37878
38311
  cursorFile: cursorFileForLog(dataDir, name),
37879
38312
  uploadedFileName: name
37880
38313
  };
@@ -37902,11 +38335,11 @@ async function readCursor(filePath) {
37902
38335
  }
37903
38336
  }
37904
38337
  async function writeCursor(filePath, cursor) {
37905
- await fsp.mkdir(path21.dirname(filePath), { recursive: true });
38338
+ await fsp.mkdir(path22.dirname(filePath), { recursive: true });
37906
38339
  await fsp.writeFile(filePath, JSON.stringify(cursor), "utf8");
37907
38340
  }
37908
38341
  async function readStreamText(filePath, start) {
37909
- const stream = fs14.createReadStream(filePath, { start, encoding: "utf8" });
38342
+ const stream = fs15.createReadStream(filePath, { start, encoding: "utf8" });
37910
38343
  let raw = "";
37911
38344
  for await (const chunk of stream) {
37912
38345
  raw += chunk;
@@ -37919,26 +38352,38 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
37919
38352
  return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
37920
38353
  }
37921
38354
  const processed = raw.slice(0, lastNewline + 1);
37922
- const lines = processed.split(/\r?\n/);
38355
+ const linePattern = /.*?\n/g;
37923
38356
  const entries = [];
37924
38357
  let lineNum = cursor.lineNum;
37925
- for (let i = 0; i < lines.length - 1; i += 1) {
37926
- const line = lines[i] ?? "";
38358
+ let offset = cursor.offset;
38359
+ let match;
38360
+ while ((match = linePattern.exec(processed)) !== null) {
38361
+ const rawLine = match[0];
38362
+ const line = rawLine.endsWith("\r\n") ? rawLine.slice(0, -2) : rawLine.slice(0, -1);
37927
38363
  lineNum += 1;
38364
+ offset += Buffer.byteLength(rawLine, "utf8");
38365
+ const cursorAfter = {
38366
+ offset,
38367
+ lineNum,
38368
+ ...fingerprint ? { fingerprint } : {}
38369
+ };
37928
38370
  if (line.length === 0) continue;
37929
38371
  const parsed = parseLogLine(line);
37930
38372
  if (!parsed) continue;
37931
38373
  entries.push({
37932
- ...parsed,
37933
- raw: line,
37934
- file: fileName,
37935
- lineNum
38374
+ entry: {
38375
+ ...parsed,
38376
+ raw: line,
38377
+ file: fileName,
38378
+ lineNum
38379
+ },
38380
+ cursorAfter
37936
38381
  });
37937
38382
  }
37938
38383
  return {
37939
38384
  entries,
37940
38385
  nextCursor: {
37941
- offset: cursor.offset + Buffer.byteLength(processed, "utf8"),
38386
+ offset,
37942
38387
  lineNum,
37943
38388
  ...fingerprint ? { fingerprint } : {}
37944
38389
  },
@@ -37946,15 +38391,35 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
37946
38391
  reason: "advanced"
37947
38392
  };
37948
38393
  }
37949
- function chunkEntries(entries, size) {
37950
- const chunks = [];
37951
- for (let i = 0; i < entries.length; i += size) {
37952
- chunks.push(entries.slice(i, i + size));
38394
+ function logEntryTimeRange(entries) {
38395
+ let minTime = Number.POSITIVE_INFINITY;
38396
+ let maxTime = Number.NEGATIVE_INFINITY;
38397
+ let minTs = "";
38398
+ let maxTs = "";
38399
+ for (const entry of entries) {
38400
+ const parsed = Date.parse(entry.ts);
38401
+ if (!Number.isFinite(parsed)) return null;
38402
+ if (parsed < minTime) {
38403
+ minTime = parsed;
38404
+ minTs = entry.ts;
38405
+ }
38406
+ if (parsed > maxTime) {
38407
+ maxTime = parsed;
38408
+ maxTs = entry.ts;
38409
+ }
37953
38410
  }
37954
- return chunks;
38411
+ if (!minTs || !maxTs) return null;
38412
+ return { minTs, maxTs, maxTime };
38413
+ }
38414
+ function isStaleRateLimitedChunk(entries, nowMs) {
38415
+ const range = logEntryTimeRange(entries);
38416
+ if (!range) return null;
38417
+ if (nowMs - range.maxTime <= STALE_RATE_LIMIT_SKIP_AGE_MS) return null;
38418
+ return { minTs: range.minTs, maxTs: range.maxTs };
37955
38419
  }
37956
38420
  var BridgeLogUploader = class {
37957
38421
  options;
38422
+ retryAfterByTarget = /* @__PURE__ */ new Map();
37958
38423
  timer = null;
37959
38424
  running = false;
37960
38425
  stopped = false;
@@ -37971,7 +38436,7 @@ var BridgeLogUploader = class {
37971
38436
  bridgeId: options.bridgeId,
37972
38437
  hostname: options.hostname,
37973
38438
  intervalMs: options.intervalMs ?? DEFAULT_LOG_UPLOAD_INTERVAL_MS,
37974
- batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
38439
+ batchSize: Math.max(1, Math.floor(options.batchSize ?? DEFAULT_BATCH_SIZE)),
37975
38440
  primaryTarget,
37976
38441
  extraTargets: (options.extraLogFiles ?? []).map((file2) => normalizeTarget(file2, options.dataDir)),
37977
38442
  includeRotatedBridgeLogs: options.includeRotatedBridgeLogs ?? true
@@ -38013,13 +38478,26 @@ var BridgeLogUploader = class {
38013
38478
  bridgeEntryCount: 0,
38014
38479
  uploadedChunkCount: 0,
38015
38480
  accepted: 0,
38016
- skipped: 0
38481
+ skipped: 0,
38482
+ dropped: 0
38017
38483
  };
38018
38484
  try {
38019
38485
  const targets = await this.resolveTargets();
38020
38486
  summary.targetCount = targets.length;
38021
38487
  for (const target of targets) {
38488
+ const targetKey = this.targetKey(target);
38022
38489
  try {
38490
+ const retryUntil = this.retryAfterByTarget.get(targetKey);
38491
+ const now = Date.now();
38492
+ if (retryUntil && retryUntil > now) {
38493
+ summary.idleTargetCount += 1;
38494
+ logger28.debug("Bridge log upload target backoff active", {
38495
+ logFile: target.logFile,
38496
+ retryAfterMs: retryUntil - now
38497
+ });
38498
+ continue;
38499
+ }
38500
+ if (retryUntil) this.retryAfterByTarget.delete(targetKey);
38023
38501
  const cursor = await readCursor(target.cursorFile);
38024
38502
  const batch = await this.readNewEntries(target, cursor);
38025
38503
  if (!batch.advanced) {
@@ -38031,28 +38509,45 @@ var BridgeLogUploader = class {
38031
38509
  summary.advancedTargetCount += 1;
38032
38510
  summary.parsedEntryCount += batch.entries.length;
38033
38511
  if (batch.entries.length > 0) {
38034
- const result = await this.uploadEntries(batch.entries);
38512
+ const result = await this.uploadEntries(batch.entries, target.cursorFile, batch.nextCursor);
38035
38513
  summary.bridgeEntryCount += result.bridgeEntryCount;
38036
38514
  summary.uploadedChunkCount += result.uploadedChunkCount;
38037
38515
  summary.accepted += result.accepted;
38038
38516
  summary.skipped += result.skipped;
38517
+ summary.dropped += result.dropped;
38518
+ } else {
38519
+ await writeCursor(target.cursorFile, batch.nextCursor);
38039
38520
  }
38040
- await writeCursor(target.cursorFile, batch.nextCursor);
38521
+ this.retryAfterByTarget.delete(targetKey);
38041
38522
  } catch (e) {
38042
38523
  summary.failedTargetCount += 1;
38043
- logger28.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
38524
+ if (e instanceof LogUploadHttpError && e.retryAfterMs !== void 0) {
38525
+ this.retryAfterByTarget.set(targetKey, Date.now() + e.retryAfterMs);
38526
+ logger28.warn("Bridge log upload target failed", {
38527
+ error: e,
38528
+ logFile: target.logFile,
38529
+ status: e.status,
38530
+ quotaScope: e.scope,
38531
+ retryAfterMs: e.retryAfterMs
38532
+ });
38533
+ } else {
38534
+ logger28.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
38535
+ }
38044
38536
  }
38045
38537
  }
38046
38538
  } catch (e) {
38047
38539
  logger28.warn("Bridge log upload cycle failed", { error: e });
38048
38540
  } finally {
38049
- logger28.info("Bridge log upload cycle summary", {
38541
+ logger28.debug("Bridge log upload cycle summary", {
38050
38542
  ...summary,
38051
38543
  durationMs: Date.now() - startedAt
38052
38544
  });
38053
38545
  this.running = false;
38054
38546
  }
38055
38547
  }
38548
+ targetKey(target) {
38549
+ return `${target.logFile}\0${target.cursorFile}`;
38550
+ }
38056
38551
  async resolveTargets() {
38057
38552
  const bridgeTargets = this.options.includeRotatedBridgeLogs ? await listRotatedBridgeTargets(this.options.primaryTarget, this.options.dataDir) : [this.options.primaryTarget];
38058
38553
  const seen = /* @__PURE__ */ new Set();
@@ -38085,15 +38580,40 @@ var BridgeLogUploader = class {
38085
38580
  const raw = await readStreamText(target.logFile, normalizedCursor.offset);
38086
38581
  return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
38087
38582
  }
38088
- async uploadEntries(entries) {
38089
- const bridgeEntries = entries.filter((entry) => entry.source === "bridge");
38583
+ uploadBodyBytes(entries) {
38584
+ return Buffer.byteLength(JSON.stringify({
38585
+ bridgeId: this.options.bridgeId,
38586
+ hostname: this.options.hostname,
38587
+ entries
38588
+ }), "utf8");
38589
+ }
38590
+ chunkUploadEntries(entries) {
38591
+ const chunks = [];
38592
+ let current = [];
38593
+ for (const entry of entries) {
38594
+ const next = [...current, entry];
38595
+ const nextBodyBytes = this.uploadBodyBytes(next.map((item) => item.entry));
38596
+ if (current.length > 0 && (current.length >= this.options.batchSize || nextBodyBytes > MAX_UPLOAD_CHUNK_BODY_BYTES)) {
38597
+ chunks.push(current);
38598
+ current = [entry];
38599
+ } else {
38600
+ current = next;
38601
+ }
38602
+ }
38603
+ if (current.length > 0) chunks.push(current);
38604
+ return chunks;
38605
+ }
38606
+ async uploadEntries(entries, cursorFile, finalCursor) {
38607
+ const bridgeEntries = entries.filter((item) => item.entry.source === "bridge");
38090
38608
  const result = {
38091
38609
  bridgeEntryCount: bridgeEntries.length,
38092
38610
  uploadedChunkCount: 0,
38093
38611
  accepted: 0,
38094
- skipped: 0
38612
+ skipped: 0,
38613
+ dropped: 0
38095
38614
  };
38096
- for (const chunk of chunkEntries(bridgeEntries, this.options.batchSize)) {
38615
+ for (const chunk of this.chunkUploadEntries(bridgeEntries)) {
38616
+ const payloadEntries = chunk.map((item) => item.entry);
38097
38617
  const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
38098
38618
  method: "POST",
38099
38619
  headers: {
@@ -38103,7 +38623,7 @@ var BridgeLogUploader = class {
38103
38623
  body: JSON.stringify({
38104
38624
  bridgeId: this.options.bridgeId,
38105
38625
  hostname: this.options.hostname,
38106
- entries: chunk
38626
+ entries: payloadEntries
38107
38627
  })
38108
38628
  });
38109
38629
  if (!res.ok) {
@@ -38111,20 +38631,34 @@ var BridgeLogUploader = class {
38111
38631
  logger28.debug("Failed to read log upload error body", { error: e });
38112
38632
  return "";
38113
38633
  });
38114
- throw new Error(`upload failed HTTP ${res.status}: ${body2.slice(0, 160)}`);
38634
+ const staleChunk = res.status === 429 ? isStaleRateLimitedChunk(payloadEntries, Date.now()) : null;
38635
+ if (staleChunk) {
38636
+ result.dropped += chunk.length;
38637
+ logger28.warn("Skipping stale bridge log upload chunk after rate limit", {
38638
+ status: res.status,
38639
+ entryCount: chunk.length,
38640
+ minEntryTs: staleChunk.minTs,
38641
+ maxEntryTs: staleChunk.maxTs
38642
+ });
38643
+ continue;
38644
+ }
38645
+ throw new LogUploadHttpError(res.status, body2);
38115
38646
  }
38116
38647
  const body = await res.json();
38117
38648
  result.uploadedChunkCount += 1;
38118
38649
  result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
38119
38650
  result.skipped += typeof body.skipped === "number" ? body.skipped : 0;
38651
+ const last = chunk[chunk.length - 1];
38652
+ if (last) await writeCursor(cursorFile, last.cursorAfter);
38120
38653
  }
38654
+ await writeCursor(cursorFile, finalCursor);
38121
38655
  return result;
38122
38656
  }
38123
38657
  };
38124
38658
 
38125
38659
  // src/skillStore.ts
38126
- import fs15 from "fs";
38127
- import path22 from "path";
38660
+ import fs16 from "fs";
38661
+ import path23 from "path";
38128
38662
  var logger29 = createModuleLogger("bridge.skillStore");
38129
38663
  var MANAGED_CACHE_MARKER = "<!-- ahchat-skill-cache";
38130
38664
  var SAFE_SKILL_NAME_RE = /^[a-zA-Z0-9_-]+$/;
@@ -38153,9 +38687,9 @@ var SkillStore = class {
38153
38687
  skillsDir;
38154
38688
  indexPath;
38155
38689
  constructor(dataDir) {
38156
- this.skillsDir = path22.join(dataDir, "skills");
38157
- this.indexPath = path22.join(this.skillsDir, INDEX_FILE_NAME);
38158
- fs15.mkdirSync(this.skillsDir, { recursive: true });
38690
+ this.skillsDir = path23.join(dataDir, "skills");
38691
+ this.indexPath = path23.join(this.skillsDir, INDEX_FILE_NAME);
38692
+ fs16.mkdirSync(this.skillsDir, { recursive: true });
38159
38693
  logger29.info("SkillStore initialized", { skillsDir: this.skillsDir });
38160
38694
  }
38161
38695
  read(name) {
@@ -38163,9 +38697,9 @@ var SkillStore = class {
38163
38697
  logger29.warn("Skill read: unsafe name", { name });
38164
38698
  return "";
38165
38699
  }
38166
- const filePath = path22.join(this.skillsDir, `${name}.md`);
38700
+ const filePath = path23.join(this.skillsDir, `${name}.md`);
38167
38701
  try {
38168
- const content = fs15.readFileSync(filePath, "utf-8");
38702
+ const content = fs16.readFileSync(filePath, "utf-8");
38169
38703
  logger29.info("Skill read", { name, bytes: content.length });
38170
38704
  return content;
38171
38705
  } catch (e) {
@@ -38176,7 +38710,7 @@ var SkillStore = class {
38176
38710
  allowedNames() {
38177
38711
  const names = /* @__PURE__ */ new Set();
38178
38712
  try {
38179
- for (const entry of fs15.readdirSync(this.skillsDir, { withFileTypes: true })) {
38713
+ for (const entry of fs16.readdirSync(this.skillsDir, { withFileTypes: true })) {
38180
38714
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
38181
38715
  const name = entry.name.slice(0, -3);
38182
38716
  if (isSafeSkillName(name)) names.add(name);
@@ -38191,20 +38725,20 @@ var SkillStore = class {
38191
38725
  if (!isSafeSkillName(name)) {
38192
38726
  throw new Error(`Unsafe skill name: ${name}`);
38193
38727
  }
38194
- const filePath = path22.join(this.skillsDir, `${name}.md`);
38728
+ const filePath = path23.join(this.skillsDir, `${name}.md`);
38195
38729
  const tmpPath = `${filePath}.tmp`;
38196
38730
  let existing = "";
38197
38731
  try {
38198
- existing = fs15.readFileSync(filePath, "utf-8");
38732
+ existing = fs16.readFileSync(filePath, "utf-8");
38199
38733
  } catch (e) {
38200
38734
  if (!isNotFoundError(e)) logger29.warn("Skill seed existing read failed", { name, filePath, error: e });
38201
38735
  }
38202
38736
  if (existing === content) {
38203
- logger29.info("Skill already in sync", { name, bytes: content.length });
38737
+ logger29.debug("Skill already in sync", { name, bytes: content.length });
38204
38738
  return;
38205
38739
  }
38206
- fs15.writeFileSync(tmpPath, content, "utf-8");
38207
- fs15.renameSync(tmpPath, filePath);
38740
+ fs16.writeFileSync(tmpPath, content, "utf-8");
38741
+ fs16.renameSync(tmpPath, filePath);
38208
38742
  logger29.info("Skill seeded/re-synced", {
38209
38743
  name,
38210
38744
  bytes: content.length,
@@ -38236,7 +38770,7 @@ var SkillStore = class {
38236
38770
  const content = this.readExisting(name);
38237
38771
  if (!isPrunableManagedSkillCache(content)) continue;
38238
38772
  try {
38239
- fs15.unlinkSync(path22.join(this.skillsDir, `${name}.md`));
38773
+ fs16.unlinkSync(path23.join(this.skillsDir, `${name}.md`));
38240
38774
  pruned += 1;
38241
38775
  logger29.info("Managed skill cache pruned", { name });
38242
38776
  } catch (e) {
@@ -38254,7 +38788,7 @@ var SkillStore = class {
38254
38788
  const tmpPath = `${this.indexPath}.tmp`;
38255
38789
  let existing = "";
38256
38790
  try {
38257
- existing = fs15.readFileSync(this.indexPath, "utf-8");
38791
+ existing = fs16.readFileSync(this.indexPath, "utf-8");
38258
38792
  } catch (e) {
38259
38793
  if (!isNotFoundError(e)) logger29.warn("Runtime skill index existing read failed", { error: e });
38260
38794
  }
@@ -38262,8 +38796,8 @@ var SkillStore = class {
38262
38796
  logger29.info("Runtime skill index already in sync", { count: safeEntries.length });
38263
38797
  return;
38264
38798
  }
38265
- fs15.writeFileSync(tmpPath, content, "utf-8");
38266
- fs15.renameSync(tmpPath, this.indexPath);
38799
+ fs16.writeFileSync(tmpPath, content, "utf-8");
38800
+ fs16.renameSync(tmpPath, this.indexPath);
38267
38801
  logger29.info("Runtime skill index synced", { count: safeEntries.length });
38268
38802
  }
38269
38803
  pruneManagedSkillsBySource(source, desiredNames) {
@@ -38273,7 +38807,7 @@ var SkillStore = class {
38273
38807
  const content = this.readExisting(name);
38274
38808
  if (managedSkillCacheSource(content) !== source) continue;
38275
38809
  try {
38276
- fs15.unlinkSync(path22.join(this.skillsDir, `${name}.md`));
38810
+ fs16.unlinkSync(path23.join(this.skillsDir, `${name}.md`));
38277
38811
  pruned += 1;
38278
38812
  logger29.info("Managed skill cache pruned by source", { name, source });
38279
38813
  } catch (e) {
@@ -38284,7 +38818,7 @@ var SkillStore = class {
38284
38818
  }
38285
38819
  listIndexEntries() {
38286
38820
  try {
38287
- const parsed = JSON.parse(fs15.readFileSync(this.indexPath, "utf-8"));
38821
+ const parsed = JSON.parse(fs16.readFileSync(this.indexPath, "utf-8"));
38288
38822
  if (!Array.isArray(parsed)) return [];
38289
38823
  return parsed.map(normalizeSkillIndexEntry).filter((entry) => entry !== null);
38290
38824
  } catch (e) {
@@ -38297,7 +38831,7 @@ var SkillStore = class {
38297
38831
  }
38298
38832
  readExisting(name) {
38299
38833
  try {
38300
- return fs15.readFileSync(path22.join(this.skillsDir, `${name}.md`), "utf-8");
38834
+ return fs16.readFileSync(path23.join(this.skillsDir, `${name}.md`), "utf-8");
38301
38835
  } catch (e) {
38302
38836
  if (!isNotFoundError(e)) logger29.warn("Managed skill cache read failed", { name, error: e });
38303
38837
  return "";
@@ -38343,8 +38877,8 @@ function normalizeSkillIndexEntry(value) {
38343
38877
 
38344
38878
  // src/lockfile.ts
38345
38879
  import * as childProcess from "child_process";
38346
- import fs16 from "fs";
38347
- import path23 from "path";
38880
+ import fs17 from "fs";
38881
+ import path24 from "path";
38348
38882
  var logger30 = createModuleLogger("bridge.lockfile");
38349
38883
  var lockPath = null;
38350
38884
  var releaseRegistered = false;
@@ -38419,8 +38953,8 @@ function readProcessCommand(pid) {
38419
38953
  return readWindowsProcessCommand(pid);
38420
38954
  }
38421
38955
  const procCmdline = `/proc/${pid}/cmdline`;
38422
- if (fs16.existsSync(procCmdline)) {
38423
- return fs16.readFileSync(procCmdline, "utf-8").replace(/\0/g, " ");
38956
+ if (fs17.existsSync(procCmdline)) {
38957
+ return fs17.readFileSync(procCmdline, "utf-8").replace(/\0/g, " ");
38424
38958
  }
38425
38959
  return childProcess.execFileSync("ps", ["-p", String(pid), "-o", "comm=", "-o", "args="], {
38426
38960
  encoding: "utf-8",
@@ -38446,10 +38980,10 @@ function isLiveBridgeLockOwner(pid) {
38446
38980
  return false;
38447
38981
  }
38448
38982
  function acquireLock(dataDir) {
38449
- const file2 = path23.join(dataDir, "bridge.lock");
38983
+ const file2 = path24.join(dataDir, "bridge.lock");
38450
38984
  lockPath = file2;
38451
- if (fs16.existsSync(file2)) {
38452
- const raw = fs16.readFileSync(file2, "utf-8").trim();
38985
+ if (fs17.existsSync(file2)) {
38986
+ const raw = fs17.readFileSync(file2, "utf-8").trim();
38453
38987
  const pid = Number.parseInt(raw, 10);
38454
38988
  if (Number.isFinite(pid) && pid > 0) {
38455
38989
  if (isLiveBridgeLockOwner(pid)) {
@@ -38458,8 +38992,8 @@ function acquireLock(dataDir) {
38458
38992
  logger30.info("Removing stale bridge.lock", { pid, path: file2 });
38459
38993
  }
38460
38994
  }
38461
- fs16.mkdirSync(path23.dirname(file2), { recursive: true });
38462
- fs16.writeFileSync(file2, String(process.pid), "utf-8");
38995
+ fs17.mkdirSync(path24.dirname(file2), { recursive: true });
38996
+ fs17.writeFileSync(file2, String(process.pid), "utf-8");
38463
38997
  logger30.info("Acquired bridge lock", { path: file2, pid: process.pid });
38464
38998
  if (!releaseRegistered) {
38465
38999
  releaseRegistered = true;
@@ -38468,10 +39002,10 @@ function acquireLock(dataDir) {
38468
39002
  }
38469
39003
  function releaseLock() {
38470
39004
  try {
38471
- if (lockPath && fs16.existsSync(lockPath)) {
38472
- const current = fs16.readFileSync(lockPath, "utf-8").trim();
39005
+ if (lockPath && fs17.existsSync(lockPath)) {
39006
+ const current = fs17.readFileSync(lockPath, "utf-8").trim();
38473
39007
  if (current === String(process.pid)) {
38474
- fs16.unlinkSync(lockPath);
39008
+ fs17.unlinkSync(lockPath);
38475
39009
  logger30.info("Released bridge lock", { path: lockPath });
38476
39010
  }
38477
39011
  }
@@ -38483,17 +39017,17 @@ function releaseLock() {
38483
39017
  }
38484
39018
 
38485
39019
  // src/localWorkdirOverrideStore.ts
38486
- import fs17 from "fs";
38487
- import path24 from "path";
39020
+ import fs18 from "fs";
39021
+ import path25 from "path";
38488
39022
  var logger31 = createModuleLogger("bridge.localWorkdirOverride");
38489
39023
  var LocalWorkdirOverrideStore = class {
38490
39024
  filePath;
38491
39025
  constructor(dataDir) {
38492
- this.filePath = path24.join(dataDir, LOCAL_WORKDIR_OVERRIDES_FILENAME);
39026
+ this.filePath = path25.join(dataDir, LOCAL_WORKDIR_OVERRIDES_FILENAME);
38493
39027
  }
38494
39028
  read() {
38495
39029
  try {
38496
- const raw = fs17.readFileSync(this.filePath, "utf8");
39030
+ const raw = fs18.readFileSync(this.filePath, "utf8");
38497
39031
  return normalizeLocalWorkdirOverridesFile(JSON.parse(raw)).overrides;
38498
39032
  } catch (error51) {
38499
39033
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOENT") return [];
@@ -38512,9 +39046,9 @@ var LocalWorkdirOverrideStore = class {
38512
39046
  if (!args.localWorkdir.trim()) throw new Error("local workdir is required");
38513
39047
  const current = { version: 1, overrides: this.read() };
38514
39048
  const next = upsertLocalWorkdirOverride(current, args);
38515
- fs17.mkdirSync(args.localWorkdir, { recursive: true });
38516
- fs17.mkdirSync(path24.dirname(this.filePath), { recursive: true });
38517
- fs17.writeFileSync(this.filePath, `${JSON.stringify(next, null, 2)}
39049
+ fs18.mkdirSync(args.localWorkdir, { recursive: true });
39050
+ fs18.mkdirSync(path25.dirname(this.filePath), { recursive: true });
39051
+ fs18.writeFileSync(this.filePath, `${JSON.stringify(next, null, 2)}
38518
39052
  `, "utf8");
38519
39053
  const saved = next.overrides.find(
38520
39054
  (item) => item.targetKind === args.targetKind && item.targetId === args.targetId
@@ -38931,8 +39465,8 @@ async function handleGroupArchivedPush(deps, payload) {
38931
39465
  }
38932
39466
 
38933
39467
  // src/sessionStore.ts
38934
- import fs18 from "fs";
38935
- import path25 from "path";
39468
+ import fs19 from "fs";
39469
+ import path26 from "path";
38936
39470
  var logger34 = createModuleLogger("session.store");
38937
39471
  var SessionStore = class {
38938
39472
  filePath;
@@ -38940,8 +39474,8 @@ var SessionStore = class {
38940
39474
  cache;
38941
39475
  fingerprints;
38942
39476
  constructor(dataDir) {
38943
- this.filePath = path25.join(dataDir, "sessions.json");
38944
- this.fingerprintPath = path25.join(dataDir, "session-fingerprints.json");
39477
+ this.filePath = path26.join(dataDir, "sessions.json");
39478
+ this.fingerprintPath = path26.join(dataDir, "session-fingerprints.json");
38945
39479
  this.cache = this.loadFromDisk();
38946
39480
  this.fingerprints = this.loadMapFromDisk(this.fingerprintPath, "session fingerprints");
38947
39481
  }
@@ -38999,8 +39533,8 @@ var SessionStore = class {
38999
39533
  }
39000
39534
  loadFromDisk() {
39001
39535
  try {
39002
- if (!fs18.existsSync(this.filePath)) return {};
39003
- const raw = fs18.readFileSync(this.filePath, "utf-8");
39536
+ if (!fs19.existsSync(this.filePath)) return {};
39537
+ const raw = fs19.readFileSync(this.filePath, "utf-8");
39004
39538
  const parsed = JSON.parse(raw);
39005
39539
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
39006
39540
  const map2 = parsed;
@@ -39024,8 +39558,8 @@ var SessionStore = class {
39024
39558
  }
39025
39559
  loadMapFromDisk(filePath, label) {
39026
39560
  try {
39027
- if (!fs18.existsSync(filePath)) return {};
39028
- const raw = fs18.readFileSync(filePath, "utf-8");
39561
+ if (!fs19.existsSync(filePath)) return {};
39562
+ const raw = fs19.readFileSync(filePath, "utf-8");
39029
39563
  const parsed = JSON.parse(raw);
39030
39564
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
39031
39565
  const out = {};
@@ -39040,18 +39574,18 @@ var SessionStore = class {
39040
39574
  }
39041
39575
  saveToDisk() {
39042
39576
  try {
39043
- const dir = path25.dirname(this.filePath);
39044
- fs18.mkdirSync(dir, { recursive: true });
39045
- fs18.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
39577
+ const dir = path26.dirname(this.filePath);
39578
+ fs19.mkdirSync(dir, { recursive: true });
39579
+ fs19.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
39046
39580
  } catch (e) {
39047
39581
  logger34.error("Failed to save sessions file", { error: e, path: this.filePath });
39048
39582
  }
39049
39583
  }
39050
39584
  saveFingerprintsToDisk() {
39051
39585
  try {
39052
- const dir = path25.dirname(this.fingerprintPath);
39053
- fs18.mkdirSync(dir, { recursive: true });
39054
- fs18.writeFileSync(
39586
+ const dir = path26.dirname(this.fingerprintPath);
39587
+ fs19.mkdirSync(dir, { recursive: true });
39588
+ fs19.writeFileSync(
39055
39589
  this.fingerprintPath,
39056
39590
  JSON.stringify(this.fingerprints, null, 2),
39057
39591
  "utf-8"
@@ -39066,8 +39600,8 @@ var SessionStore = class {
39066
39600
  };
39067
39601
 
39068
39602
  // src/workdirEnsure.ts
39069
- import fs19 from "fs";
39070
- import path26 from "path";
39603
+ import fs20 from "fs";
39604
+ import path27 from "path";
39071
39605
  function resolveBridgeWorkdirPath(requestedPath, workspacesDir, workdirOverrideStore, target) {
39072
39606
  const overridden = target ? workdirOverrideStore?.resolvePath(requestedPath, target) : workdirOverrideStore?.resolvePath(requestedPath);
39073
39607
  if (overridden?.overridden) {
@@ -39079,15 +39613,15 @@ function ensureBridgeWorkdirExists(requestedPath, workspacesDir, workdirOverride
39079
39613
  const trimmed = requestedPath.trim();
39080
39614
  if (!trimmed) return null;
39081
39615
  const resolved = resolveBridgeWorkdirPath(trimmed, workspacesDir, workdirOverrideStore, target);
39082
- if (!path26.isAbsolute(resolved.path)) return { ...resolved, ensured: false };
39083
- fs19.mkdirSync(resolved.path, { recursive: true });
39616
+ if (!path27.isAbsolute(resolved.path)) return { ...resolved, ensured: false };
39617
+ fs20.mkdirSync(resolved.path, { recursive: true });
39084
39618
  return { ...resolved, ensured: true };
39085
39619
  }
39086
39620
 
39087
39621
  // src/claudeRuntime.ts
39088
39622
  import { accessSync as accessSync2, constants as constants3, existsSync as existsSync2, readdirSync as readdirSync2, realpathSync } from "fs";
39089
39623
  import { createRequire } from "module";
39090
- import path27 from "path";
39624
+ import path28 from "path";
39091
39625
  var logger35 = createModuleLogger("bridge.claudeRuntime");
39092
39626
  var require2 = createRequire(import.meta.url);
39093
39627
  var CLAUDE_RUNTIME_VERSION_TIMEOUT_MS = 3e4;
@@ -39097,7 +39631,7 @@ function normalizePath(value) {
39097
39631
  const trimmed = value?.trim();
39098
39632
  if (!trimmed) return void 0;
39099
39633
  const expanded = resolveUserPath(trimmed);
39100
- return path27.isAbsolute(expanded) ? expanded : path27.resolve(expanded);
39634
+ return path28.isAbsolute(expanded) ? expanded : path28.resolve(expanded);
39101
39635
  }
39102
39636
  function canExecute2(candidate) {
39103
39637
  try {
@@ -39181,16 +39715,16 @@ function resolveClaudeAgentSdkDir() {
39181
39715
  const sdkEntry = safeResolve("@anthropic-ai/claude-agent-sdk");
39182
39716
  if (!sdkEntry) return void 0;
39183
39717
  try {
39184
- return path27.dirname(realpathSync(sdkEntry));
39718
+ return path28.dirname(realpathSync(sdkEntry));
39185
39719
  } catch {
39186
- return path27.dirname(sdkEntry);
39720
+ return path28.dirname(sdkEntry);
39187
39721
  }
39188
39722
  }
39189
39723
  function findPnpmStoreDir(fromDir) {
39190
39724
  let current = fromDir;
39191
- while (current !== path27.dirname(current)) {
39192
- if (path27.basename(current) === ".pnpm") return current;
39193
- current = path27.dirname(current);
39725
+ while (current !== path28.dirname(current)) {
39726
+ if (path28.basename(current) === ".pnpm") return current;
39727
+ current = path28.dirname(current);
39194
39728
  }
39195
39729
  return void 0;
39196
39730
  }
@@ -39208,7 +39742,7 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
39208
39742
  for (const entry of entries) {
39209
39743
  if (!entry.startsWith(`${encodedName}@`)) continue;
39210
39744
  const version2 = entry.slice(encodedName.length + 1);
39211
- const candidate = path27.join(
39745
+ const candidate = path28.join(
39212
39746
  pnpmStoreDir,
39213
39747
  entry,
39214
39748
  "node_modules",
@@ -39240,8 +39774,8 @@ function resolveSdkRuntimeBinary(target) {
39240
39774
  const scopedPackageName = target.packageName.split("/")[1] ?? target.packageName;
39241
39775
  const candidates = [
39242
39776
  safeResolve(`${target.packageName}/${target.binaryName}`, [sdkDir]),
39243
- path27.join(sdkDir, "..", scopedPackageName, target.binaryName),
39244
- path27.join(sdkDir, "node_modules", ...target.packageName.split("/"), target.binaryName),
39777
+ path28.join(sdkDir, "..", scopedPackageName, target.binaryName),
39778
+ path28.join(sdkDir, "node_modules", ...target.packageName.split("/"), target.binaryName),
39245
39779
  resolvePnpmRuntimeBinary(sdkDir, target)
39246
39780
  ].filter((candidate) => Boolean(candidate));
39247
39781
  return candidates.find((candidate) => existsSync2(candidate));
@@ -39373,8 +39907,8 @@ function logClaudeRuntimeResolution(resolution) {
39373
39907
  }
39374
39908
 
39375
39909
  // src/forkAgentFiles.ts
39376
- import * as fs20 from "fs/promises";
39377
- import * as path28 from "path";
39910
+ import * as fs21 from "fs/promises";
39911
+ import * as path29 from "path";
39378
39912
  var logger36 = createModuleLogger("bridge.forkAgentFiles");
39379
39913
  async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkdir, dataDir, sessionStore, sourceConversationId) {
39380
39914
  logger36.info("Fork file copy starting", {
@@ -39386,15 +39920,15 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
39386
39920
  sourceConversationId
39387
39921
  });
39388
39922
  try {
39389
- const stat3 = await fs20.stat(sourceWorkdir).catch(() => null);
39923
+ const stat3 = await fs21.stat(sourceWorkdir).catch(() => null);
39390
39924
  if (stat3?.isDirectory()) {
39391
- await fs20.cp(sourceWorkdir, newWorkdir, { recursive: true });
39925
+ await fs21.cp(sourceWorkdir, newWorkdir, { recursive: true });
39392
39926
  logger36.info("Workdir copied", {
39393
39927
  from: sourceWorkdir,
39394
39928
  to: newWorkdir
39395
39929
  });
39396
39930
  } else {
39397
- await fs20.mkdir(newWorkdir, { recursive: true });
39931
+ await fs21.mkdir(newWorkdir, { recursive: true });
39398
39932
  logger36.info("Workdir created (source did not exist)", {
39399
39933
  newWorkdir
39400
39934
  });
@@ -39403,14 +39937,14 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
39403
39937
  logger36.error("Workdir copy failed", { error: e });
39404
39938
  throw e;
39405
39939
  }
39406
- const srcNotebook = path28.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
39407
- const dstNotebookDir = path28.join(dataDir, "agent-memory", newAgentId);
39408
- const dstNotebook = path28.join(dstNotebookDir, "notebook.md");
39940
+ const srcNotebook = path29.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
39941
+ const dstNotebookDir = path29.join(dataDir, "agent-memory", newAgentId);
39942
+ const dstNotebook = path29.join(dstNotebookDir, "notebook.md");
39409
39943
  try {
39410
- const nbStat = await fs20.stat(srcNotebook).catch(() => null);
39944
+ const nbStat = await fs21.stat(srcNotebook).catch(() => null);
39411
39945
  if (nbStat?.isFile()) {
39412
- await fs20.mkdir(dstNotebookDir, { recursive: true });
39413
- await fs20.copyFile(srcNotebook, dstNotebook);
39946
+ await fs21.mkdir(dstNotebookDir, { recursive: true });
39947
+ await fs21.copyFile(srcNotebook, dstNotebook);
39414
39948
  logger36.info("Notebook copied", {
39415
39949
  from: srcNotebook,
39416
39950
  to: dstNotebook
@@ -39428,12 +39962,12 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
39428
39962
  if (sourceSessionId) {
39429
39963
  try {
39430
39964
  const srcPath = sessionFilePath(sourceWorkdir, sourceSessionId);
39431
- const srcStat = await fs20.stat(srcPath).catch(() => null);
39965
+ const srcStat = await fs21.stat(srcPath).catch(() => null);
39432
39966
  if (srcStat?.isFile()) {
39433
39967
  const dstDir = sessionDirForCwd(newWorkdir);
39434
- await fs20.mkdir(dstDir, { recursive: true });
39435
- const dstPath = path28.join(dstDir, `${sourceSessionId}.jsonl`);
39436
- await fs20.copyFile(srcPath, dstPath);
39968
+ await fs21.mkdir(dstDir, { recursive: true });
39969
+ const dstPath = path29.join(dstDir, `${sourceSessionId}.jsonl`);
39970
+ await fs21.copyFile(srcPath, dstPath);
39437
39971
  sessionStore.set(newAgentId, { kind: "single" }, sourceSessionId);
39438
39972
  sessionCopied = true;
39439
39973
  logger36.info("Session JSONL copied and registered", {
@@ -39479,7 +40013,7 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
39479
40013
 
39480
40014
  // src/feedbackCodexAnalyzer.ts
39481
40015
  import { spawn, spawnSync } from "child_process";
39482
- import path29 from "path";
40016
+ import path30 from "path";
39483
40017
  var logger37 = createModuleLogger("feedbackCodexAnalyzer");
39484
40018
  var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
39485
40019
  var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
@@ -39504,12 +40038,12 @@ function resolveFeedbackCodexOptions(defaultWorkdir, env2 = process.env) {
39504
40038
  const logDataDir = readOptionalEnv("AHCHAT_FEEDBACK_LOG_DATA_DIR", env2) ?? DEFAULT_LOG_DATA_DIR;
39505
40039
  return {
39506
40040
  executable: readOptionalEnv("AHCHAT_FEEDBACK_CODEX_EXECUTABLE", env2) ?? readOptionalEnv("CODEX_EXECUTABLE", env2) ?? "codex",
39507
- workdir: path29.resolve(readOptionalEnv("AHCHAT_FEEDBACK_CODEX_WORKDIR", env2) ?? defaultWorkdir),
40041
+ workdir: path30.resolve(readOptionalEnv("AHCHAT_FEEDBACK_CODEX_WORKDIR", env2) ?? defaultWorkdir),
39508
40042
  model: readOptionalEnv("AHCHAT_FEEDBACK_CODEX_MODEL", env2),
39509
40043
  timeoutMs: readPositiveIntEnv("AHCHAT_FEEDBACK_CODEX_TIMEOUT_MS", DEFAULT_TIMEOUT_MS2, env2),
39510
40044
  logSshTarget: readOptionalEnv("AHCHAT_FEEDBACK_LOG_SSH_TARGET", env2) ?? DEFAULT_LOG_SSH_TARGET,
39511
40045
  logDataDir,
39512
- logServerLogDir: readOptionalEnv("AHCHAT_FEEDBACK_LOG_SERVER_LOG_DIR", env2) ?? path29.posix.join(logDataDir, "logs"),
40046
+ logServerLogDir: readOptionalEnv("AHCHAT_FEEDBACK_LOG_SERVER_LOG_DIR", env2) ?? path30.posix.join(logDataDir, "logs"),
39513
40047
  logUploadTable: readOptionalEnv("AHCHAT_FEEDBACK_LOG_UPLOAD_TABLE", env2) ?? DEFAULT_LOG_UPLOAD_TABLE
39514
40048
  };
39515
40049
  }
@@ -39702,7 +40236,7 @@ function buildFeedbackAnalysisPrompt(payload, options, attachments) {
39702
40236
  - \u751F\u4EA7\u65E5\u5FD7\u76EE\u6807\uFF1A${options.logSshTarget}
39703
40237
  - AHChat \u6570\u636E\u76EE\u5F55\uFF1A${options.logDataDir}
39704
40238
  - \u670D\u52A1\u7AEF\u65E5\u5FD7\u76EE\u5F55\uFF1A${options.logServerLogDir}
39705
- - Bridge \u4E0A\u4F20\u65E5\u5FD7\u6570\u636E\u5E93\uFF1A${path29.posix.join(options.logDataDir, "data.db")}
40239
+ - Bridge \u4E0A\u4F20\u65E5\u5FD7\u6570\u636E\u5E93\uFF1A${path30.posix.join(options.logDataDir, "data.db")}
39706
40240
  - \u4E0A\u4F20\u65E5\u5FD7\u8868\uFF1A${options.logUploadTable}
39707
40241
  - \u5982\u679C\u76EE\u6807\u673A\u5668\u4E0A\u7684\u5B9A\u65F6\u4EFB\u52A1\u6216\u670D\u52A1\u914D\u7F6E\u663E\u793A\u65E5\u5FD7\u76EE\u5F55\u4E0D\u540C\uFF0C\u4EE5\u5B9E\u9645\u914D\u7F6E\u4E3A\u51C6\u3002
39708
40242
  - \u53EA\u505A\u53EA\u8BFB\u67E5\u8BE2\uFF1B\u4E0D\u8981\u590D\u5236\u6570\u636E\u5E93\uFF1B\u53EF\u7528 ssh \u5230\u76EE\u6807\u673A\u5668\u540E\u901A\u8FC7 python3/sqlite3 \u67E5\u8BE2\u3002
@@ -39877,15 +40411,15 @@ async function analyzeFeedbackWithLocalCodex(payload, options, attachments = [])
39877
40411
  }
39878
40412
 
39879
40413
  // src/modelQuerier.ts
39880
- import fs21 from "fs/promises";
40414
+ import fs22 from "fs/promises";
39881
40415
  import os12 from "os";
39882
- import path30 from "path";
40416
+ import path31 from "path";
39883
40417
  import * as sdk4 from "@anthropic-ai/claude-agent-sdk";
39884
40418
  var logger38 = createModuleLogger("bridge.modelQuerier");
39885
40419
  async function listModels(queryFn, opts = {}) {
39886
40420
  const t0 = Date.now();
39887
- const cwd = opts.cwd ?? path30.join(os12.homedir(), ".ahchat", "workspaces", "_list_models");
39888
- await fs21.mkdir(cwd, { recursive: true });
40421
+ const cwd = opts.cwd ?? path31.join(os12.homedir(), ".ahchat", "workspaces", "_list_models");
40422
+ await fs22.mkdir(cwd, { recursive: true });
39889
40423
  const fn = queryFn ?? sdk4.query;
39890
40424
  const ic = new InputController();
39891
40425
  ic.push("Reply with exactly: PING", "");
@@ -39950,9 +40484,9 @@ async function listModels(queryFn, opts = {}) {
39950
40484
  import { execFile as execFile3 } from "child_process";
39951
40485
  import crypto4 from "crypto";
39952
40486
  import fsSync2 from "fs";
39953
- import fs22 from "fs/promises";
40487
+ import fs23 from "fs/promises";
39954
40488
  import os13 from "os";
39955
- import path31 from "path";
40489
+ import path32 from "path";
39956
40490
  import { promisify as promisify3 } from "util";
39957
40491
  var execFileAsync3 = promisify3(execFile3);
39958
40492
  var logger39 = createModuleLogger("bridge.officeRuntimeSetup");
@@ -40012,7 +40546,7 @@ async function fetchUrl(url2, outputPath, timeoutMs) {
40012
40546
  const res = await fetch(url2, { signal: controller.signal });
40013
40547
  if (!res.ok) return false;
40014
40548
  const bytes = Buffer.from(await res.arrayBuffer());
40015
- await fs22.writeFile(outputPath, bytes);
40549
+ await fs23.writeFile(outputPath, bytes);
40016
40550
  return true;
40017
40551
  } catch (error51) {
40018
40552
  logger39.error("OfficeCLI runtime download failed", { error: error51, url: url2 });
@@ -40034,7 +40568,7 @@ function parseChecksumManifest(raw, asset) {
40034
40568
  return null;
40035
40569
  }
40036
40570
  async function sha256(filePath) {
40037
- return crypto4.createHash("sha256").update(await fs22.readFile(filePath)).digest("hex");
40571
+ return crypto4.createHash("sha256").update(await fs23.readFile(filePath)).digest("hex");
40038
40572
  }
40039
40573
  async function runBestEffort(command, args) {
40040
40574
  try {
@@ -40062,10 +40596,10 @@ async function installManagedOfficeCliRuntime(env2) {
40062
40596
  const githubBase = `https://github.com/${REPO}/releases/download/${version2}`;
40063
40597
  const mirrorAssetBase = `${mirrorBase(env2)}/releases/download/${version2}`;
40064
40598
  const timeoutMs = readTimeoutMs(env2);
40065
- await fs22.mkdir(binDir, { recursive: true });
40066
- const tmpDir = await fs22.mkdtemp(path31.join(os13.tmpdir(), "ahchat-officecli-"));
40067
- const tmpBinary = path31.join(tmpDir, asset);
40068
- const tmpSums = path31.join(tmpDir, "SHA256SUMS");
40599
+ await fs23.mkdir(binDir, { recursive: true });
40600
+ const tmpDir = await fs23.mkdtemp(path32.join(os13.tmpdir(), "ahchat-officecli-"));
40601
+ const tmpBinary = path32.join(tmpDir, asset);
40602
+ const tmpSums = path32.join(tmpDir, "SHA256SUMS");
40069
40603
  try {
40070
40604
  const binarySource = await fetchWithFallback(
40071
40605
  `${mirrorAssetBase}/${asset}`,
@@ -40081,21 +40615,21 @@ async function installManagedOfficeCliRuntime(env2) {
40081
40615
  timeoutMs
40082
40616
  );
40083
40617
  if (!checksumSource) throw new Error(`Could not download SHA256SUMS for OfficeCLI ${version2}`);
40084
- const expected = parseChecksumManifest(await fs22.readFile(tmpSums, "utf-8"), asset);
40618
+ const expected = parseChecksumManifest(await fs23.readFile(tmpSums, "utf-8"), asset);
40085
40619
  if (!expected) throw new Error(`SHA256SUMS does not contain ${asset}`);
40086
40620
  const actual = await sha256(tmpBinary);
40087
40621
  if (expected !== actual) {
40088
40622
  throw new Error(`Checksum mismatch for ${asset}: expected ${expected}, got ${actual}`);
40089
40623
  }
40090
- const staged = path31.join(binDir, `${path31.basename(target)}.new`);
40091
- await fs22.copyFile(tmpBinary, staged);
40092
- await fs22.chmod(staged, 493);
40624
+ const staged = path32.join(binDir, `${path32.basename(target)}.new`);
40625
+ await fs23.copyFile(tmpBinary, staged);
40626
+ await fs23.chmod(staged, 493);
40093
40627
  await codesignIfNeeded(staged);
40094
- await fs22.rm(target, { force: true });
40095
- await fs22.rename(staged, target);
40628
+ await fs23.rm(target, { force: true });
40629
+ await fs23.rename(staged, target);
40096
40630
  return { path: target, asset, binarySource, checksumSource };
40097
40631
  } finally {
40098
- await fs22.rm(tmpDir, { recursive: true, force: true });
40632
+ await fs23.rm(tmpDir, { recursive: true, force: true });
40099
40633
  }
40100
40634
  }
40101
40635
  async function ensureOfficeCliRuntime(env2 = process.env) {
@@ -40133,9 +40667,9 @@ async function ensureOfficeCliRuntime(env2 = process.env) {
40133
40667
  }
40134
40668
 
40135
40669
  // src/promptOptimizer.ts
40136
- import fs23 from "fs/promises";
40670
+ import fs24 from "fs/promises";
40137
40671
  import os14 from "os";
40138
- import path32 from "path";
40672
+ import path33 from "path";
40139
40673
  import * as sdk5 from "@anthropic-ai/claude-agent-sdk";
40140
40674
  var logger40 = createModuleLogger("bridge.promptOptimizer");
40141
40675
  var OPTIMIZER_SYSTEM_PROMPT = `You are an expert prompt editor for ALL-CAN Agent creation.
@@ -40179,8 +40713,8 @@ async function optimizePrompt(queryFn, opts) {
40179
40713
  const prompt = opts.systemPrompt.trim();
40180
40714
  if (!prompt) throw new Error("systemPrompt is required");
40181
40715
  const t0 = Date.now();
40182
- const cwd = opts.cwd ?? path32.join(os14.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
40183
- await fs23.mkdir(cwd, { recursive: true });
40716
+ const cwd = opts.cwd ?? path33.join(os14.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
40717
+ await fs24.mkdir(cwd, { recursive: true });
40184
40718
  const fn = queryFn ?? sdk5.query;
40185
40719
  const ic = new InputController();
40186
40720
  ic.push(buildUserPrompt(opts), "");
@@ -40442,10 +40976,10 @@ function agentRuntimeConfigSnapshot(rawConfig) {
40442
40976
  };
40443
40977
  }
40444
40978
  async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
40445
- const rootClaudeDir = path33.join(process.env.HOME ?? "/root", ".claude");
40446
- const fs24 = await import("fs/promises");
40979
+ const rootClaudeDir = path34.join(process.env.HOME ?? "/root", ".claude");
40980
+ const fs25 = await import("fs/promises");
40447
40981
  try {
40448
- await fs24.access(rootClaudeDir);
40982
+ await fs25.access(rootClaudeDir);
40449
40983
  } catch (e) {
40450
40984
  logger42.info("No /root/.claude to sync", { rootClaudeDir });
40451
40985
  logger42.debug("Root Claude dir access failed", { error: e });
@@ -40453,10 +40987,10 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
40453
40987
  }
40454
40988
  const filesToSync = [".credentials.json", "settings.json", ".credentials.backup.json"];
40455
40989
  for (const file2 of filesToSync) {
40456
- const src = path33.join(rootClaudeDir, file2);
40457
- const dest = path33.join(agentConfigDir, file2);
40990
+ const src = path34.join(rootClaudeDir, file2);
40991
+ const dest = path34.join(agentConfigDir, file2);
40458
40992
  try {
40459
- await fs24.copyFile(src, dest);
40993
+ await fs25.copyFile(src, dest);
40460
40994
  logger42.info("Synced credential file", { file: file2, from: src, to: dest });
40461
40995
  } catch (e) {
40462
40996
  logger42.debug("Credential file not present, skipping", { file: file2, src });
@@ -40465,26 +40999,26 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
40465
40999
  }
40466
41000
  }
40467
41001
  async function chownRecursive(dirPath, uid, gid) {
40468
- const fs24 = await import("fs/promises");
41002
+ const fs25 = await import("fs/promises");
40469
41003
  try {
40470
- await fs24.chown(dirPath, uid, gid);
41004
+ await fs25.chown(dirPath, uid, gid);
40471
41005
  } catch (e) {
40472
41006
  logger42.debug("chown skipped", { error: e, uid, gid });
40473
41007
  }
40474
41008
  let entries;
40475
41009
  try {
40476
- entries = await fs24.readdir(dirPath, { withFileTypes: true });
41010
+ entries = await fs25.readdir(dirPath, { withFileTypes: true });
40477
41011
  } catch (e) {
40478
41012
  logger42.debug("chown directory read skipped", { error: e });
40479
41013
  return;
40480
41014
  }
40481
41015
  for (const entry of entries) {
40482
- const fullPath = path33.join(dirPath, entry.name);
41016
+ const fullPath = path34.join(dirPath, entry.name);
40483
41017
  if (entry.isDirectory()) {
40484
41018
  await chownRecursive(fullPath, uid, gid);
40485
41019
  } else {
40486
41020
  try {
40487
- await fs24.chown(fullPath, uid, gid);
41021
+ await fs25.chown(fullPath, uid, gid);
40488
41022
  } catch (e) {
40489
41023
  logger42.debug("chown skipped", { error: e, uid, gid });
40490
41024
  }
@@ -40496,7 +41030,7 @@ async function startBridge(config2) {
40496
41030
  configureBridgeLogger(config2);
40497
41031
  ensureDir(config2.dataDir);
40498
41032
  ensureDir(config2.agentConfigDir);
40499
- const workspacesDir = path33.join(config2.dataDir, "workspaces");
41033
+ const workspacesDir = path34.join(config2.dataDir, "workspaces");
40500
41034
  ensureDir(workspacesDir);
40501
41035
  process.env.CLAUDE_CONFIG_DIR = config2.agentConfigDir;
40502
41036
  installBridgeFetchAuth(config2.serverApiUrl, config2.bridgeToken);
@@ -40591,7 +41125,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
40591
41125
  wsMetrics.start(5e3);
40592
41126
  const sessionStore = new SessionStore(config2.dataDir);
40593
41127
  const workdirOverrideStore = new LocalWorkdirOverrideStore(config2.dataDir);
40594
- const memoryRoot = path33.join(config2.dataDir, "agent-memory");
41128
+ const memoryRoot = path34.join(config2.dataDir, "agent-memory");
40595
41129
  const memoryStore = new AgentMemoryStore(memoryRoot, config2.serverApiUrl, config2.bridgeToken);
40596
41130
  logger42.info("Agent memory store initialized", { rootDir: memoryRoot });
40597
41131
  const smithNotebook = memoryStore.read(SMITH_AGENT_ID);