@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/cli.cjs +1121 -587
- package/dist/feedbackWorkerCli.cjs +30 -0
- package/dist/index.js +989 -455
- package/package.json +12 -12
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
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:
|
|
4751
|
+
const { interval, path: path35, intervalBoundary } = this.options;
|
|
4752
4752
|
for (let index = 1; index < 1e3; ++index) {
|
|
4753
|
-
const filename =
|
|
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:
|
|
4783
|
+
const { compress, path: path35, rotate } = this.options;
|
|
4784
4784
|
let rotatedName = "";
|
|
4785
4785
|
for (let count = rotate; count > 0; --count) {
|
|
4786
|
-
const currName =
|
|
4787
|
-
const prevName = count === 1 ? this.filename :
|
|
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
|
|
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 || !
|
|
8348
|
+
if (!id || !path35 || !name || !displayName || !url2) return null;
|
|
8319
8349
|
return {
|
|
8320
8350
|
id,
|
|
8321
|
-
path:
|
|
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
|
|
8738
|
+
import fs7 from "fs/promises";
|
|
8709
8739
|
import os7 from "os";
|
|
8710
|
-
import
|
|
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,
|
|
10353
|
-
if (!
|
|
10384
|
+
function getElementAtPath(obj, path35) {
|
|
10385
|
+
if (!path35)
|
|
10354
10386
|
return obj;
|
|
10355
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 }, [...
|
|
10950
|
+
issue2.errors.map((issues) => processError({ issues }, [...path35, ...issue2.path]));
|
|
10919
10951
|
} else if (issue2.code === "invalid_key") {
|
|
10920
|
-
processError({ issues: issue2.issues }, [...
|
|
10952
|
+
processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
|
|
10921
10953
|
} else if (issue2.code === "invalid_element") {
|
|
10922
|
-
processError({ issues: issue2.issues }, [...
|
|
10954
|
+
processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
|
|
10923
10955
|
} else {
|
|
10924
|
-
const fullpath = [...
|
|
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,
|
|
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 }, [...
|
|
10987
|
+
issue2.errors.map((issues) => processError({ issues }, [...path35, ...issue2.path]));
|
|
10956
10988
|
} else if (issue2.code === "invalid_key") {
|
|
10957
|
-
processError({ issues: issue2.issues }, [...
|
|
10989
|
+
processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
|
|
10958
10990
|
} else if (issue2.code === "invalid_element") {
|
|
10959
|
-
processError({ issues: issue2.issues }, [...
|
|
10991
|
+
processError({ issues: issue2.issues }, [...path35, ...issue2.path]);
|
|
10960
10992
|
} else {
|
|
10961
|
-
const fullpath = [...
|
|
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
|
|
10994
|
-
for (const seg of
|
|
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
|
|
23687
|
-
if (
|
|
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 (
|
|
23692
|
-
const key =
|
|
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\
|
|
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 /
|
|
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 = [
|
|
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
|
-
|
|
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
|
|
29661
|
-
import * as
|
|
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
|
|
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
|
|
29669
|
-
await
|
|
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
|
|
29988
|
+
const raw = await fs6.readFile(fp, "utf-8");
|
|
29676
29989
|
const meta3 = JSON.parse(raw);
|
|
29677
|
-
await
|
|
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
|
|
29705
|
-
var CLAUDE_PROJECTS_DIR =
|
|
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
|
|
30023
|
+
return path12.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
|
|
29711
30024
|
}
|
|
29712
30025
|
function sessionFilePath(cwd, sessionId) {
|
|
29713
|
-
return
|
|
30026
|
+
return path12.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
|
|
29714
30027
|
}
|
|
29715
30028
|
|
|
29716
30029
|
// src/workdirMapper.ts
|
|
29717
|
-
import
|
|
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
|
|
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 ?
|
|
30071
|
+
const remappedPath = parts.length > 0 ? path13.join(workspacesDir, ...parts) : workspacesDir;
|
|
29759
30072
|
return {
|
|
29760
30073
|
path: remappedPath,
|
|
29761
|
-
remapped:
|
|
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-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
30256
|
-
this.agentConfigDir =
|
|
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 =
|
|
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 ??
|
|
30279
|
-
this.agentConfigDir = options?.agentConfigDir ??
|
|
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 ??
|
|
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
|
|
30708
|
+
return path14.join(this.workspacesDir, ahchatSuffix);
|
|
30330
30709
|
}
|
|
30331
|
-
return
|
|
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
|
|
30721
|
+
return path14.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
|
|
30343
30722
|
}
|
|
30344
|
-
const local =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
31027
|
+
return path14.join(effectiveConfigDir, "settings.json");
|
|
30649
31028
|
}
|
|
30650
31029
|
isSameRuntimeCwd(a, b) {
|
|
30651
|
-
const left =
|
|
30652
|
-
const right =
|
|
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
|
-
|
|
30658
|
-
|
|
30659
|
-
|
|
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) =>
|
|
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 =
|
|
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" ?
|
|
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" ?
|
|
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(
|
|
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
|
|
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 =
|
|
31473
|
+
effectiveConfigDir = path14.join(this.agentConfigDir, "api-key-agents", agentConfig.id);
|
|
31095
31474
|
let isNew = false;
|
|
31096
31475
|
try {
|
|
31097
|
-
await
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
...
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
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:
|
|
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
|
|
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 =
|
|
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
|
|
32651
|
-
filePath =
|
|
32652
|
-
await
|
|
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 =
|
|
33067
|
+
const candidate = path14.resolve(remapped.path);
|
|
32682
33068
|
if (!this.isPathInsideBase(candidate, runtime.cwd)) return null;
|
|
32683
33069
|
try {
|
|
32684
|
-
const stat3 = await
|
|
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 =
|
|
32700
|
-
const resolvedBase =
|
|
32701
|
-
const relative =
|
|
32702
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
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 =
|
|
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,
|
|
35082
|
-
return new URL(
|
|
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
|
|
35337
|
-
return `${base}${
|
|
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
|
|
35441
|
-
return `${base}${
|
|
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
|
|
35520
|
-
import
|
|
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
|
|
35542
|
-
return `${base}${
|
|
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
|
-
|
|
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 ??
|
|
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 ??
|
|
36125
|
+
const existsSync3 = options.existsSync ?? fs8.existsSync;
|
|
35732
36126
|
const distCliPath = firstExistingPath(
|
|
35733
36127
|
[
|
|
35734
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35757
|
-
|
|
35758
|
-
|
|
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
|
-
|
|
35772
|
-
|
|
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
|
|
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
|
|
35832
|
-
import
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
35859
|
-
return () =>
|
|
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
|
|
35868
|
-
import
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
35935
|
-
return () =>
|
|
36328
|
+
fs10.watchFile(this.filePath, { interval: 1e3 }, listener);
|
|
36329
|
+
return () => fs10.unwatchFile(this.filePath, listener);
|
|
35936
36330
|
}
|
|
35937
36331
|
write(skills) {
|
|
35938
|
-
|
|
36332
|
+
fs10.mkdirSync(this.dataDir, { recursive: true });
|
|
35939
36333
|
const payload = normalizeLocalSkillsFile({ version: 1, skills });
|
|
35940
36334
|
const tmpPath = `${this.filePath}.tmp`;
|
|
35941
|
-
|
|
36335
|
+
fs10.writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}
|
|
35942
36336
|
`, { encoding: "utf8", mode: 384 });
|
|
35943
|
-
|
|
36337
|
+
fs10.renameSync(tmpPath, this.filePath);
|
|
35944
36338
|
try {
|
|
35945
|
-
|
|
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
|
|
37023
|
+
import fs11 from "fs/promises";
|
|
36630
37024
|
import os9 from "os";
|
|
36631
|
-
import
|
|
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
|
|
37055
|
+
return path18.join(projectsDir, slug, `${sessionId}.jsonl`);
|
|
36662
37056
|
}
|
|
36663
37057
|
function resolveProjectDirInProjectsDir(projectsDir, cwd) {
|
|
36664
37058
|
const slug = cwdToProjectSlug(cwd);
|
|
36665
|
-
return
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
36721
|
-
|
|
37114
|
+
path18.join(configDir, "api-key-agents", opts.agentId, "projects"),
|
|
37115
|
+
path18.join(configDir, "projects")
|
|
36722
37116
|
]),
|
|
36723
|
-
|
|
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
|
|
37122
|
+
if (suffix) return path18.join(workspacesDir, suffix);
|
|
36729
37123
|
const normalized = requestedCwd.trim();
|
|
36730
|
-
const basename = normalized ?
|
|
36731
|
-
const segment = basename && basename !== "." && basename !==
|
|
36732
|
-
return
|
|
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
|
|
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 (
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
37208
|
+
const jsonlPath = path18.join(projectDir, entry.name);
|
|
36815
37209
|
let stat3;
|
|
36816
37210
|
try {
|
|
36817
|
-
stat3 = await
|
|
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 =
|
|
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 =
|
|
36832
|
-
const marker =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
37254
|
-
await
|
|
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 ?
|
|
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 =
|
|
37382
|
-
await
|
|
37775
|
+
const filePath = path18.join(dumpDir, filename);
|
|
37776
|
+
await fs11.writeFile(filePath, html, "utf-8");
|
|
37383
37777
|
dumpedFiles.push(filename);
|
|
37384
|
-
const stat3 = await
|
|
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
|
|
37818
|
+
import fs12 from "fs/promises";
|
|
37425
37819
|
import os10 from "os";
|
|
37426
|
-
import
|
|
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("~\\") ?
|
|
37438
|
-
return
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
37926
|
+
const buffer = await fs12.readFile(resolvedPath);
|
|
37533
37927
|
const mimeType = mimeTypeForFileName(resolvedPath);
|
|
37534
37928
|
return {
|
|
37535
|
-
fileName:
|
|
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
|
|
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
|
|
37609
|
-
import
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
37657
|
-
if (relative.startsWith("..") ||
|
|
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 (
|
|
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
|
-
|
|
37669
|
-
|
|
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 =
|
|
38071
|
+
const targetPath = path20.resolve(resolvedBaseDir, safeRelativePath);
|
|
37678
38072
|
ensureInsideBase(resolvedBaseDir, targetPath);
|
|
37679
38073
|
const buffer = Buffer.from(opts.contentBase64, "base64");
|
|
37680
|
-
await
|
|
37681
|
-
await
|
|
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
|
|
38087
|
+
const stat3 = await fs13.stat(safePath);
|
|
37694
38088
|
if (!stat3.isFile()) throw new Error("path is not a file");
|
|
37695
|
-
const buffer = await
|
|
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:
|
|
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 =
|
|
38102
|
+
const candidate = path20.join(trashDir, `${stamp}-${safeName}${suffix}`);
|
|
37709
38103
|
try {
|
|
37710
|
-
await
|
|
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
|
-
|
|
37727
|
-
|
|
38120
|
+
fs13.realpath(resolvedBaseDir),
|
|
38121
|
+
fs13.realpath(resolvedTargetPath)
|
|
37728
38122
|
]);
|
|
37729
38123
|
ensureInsideBase(realBase, realTarget);
|
|
37730
38124
|
ensureNotBaseDir(realBase, realTarget);
|
|
37731
|
-
const trashDir =
|
|
37732
|
-
await
|
|
37733
|
-
const trashedPath = await allocateTrashPath(trashDir,
|
|
37734
|
-
await
|
|
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
|
|
37746
|
-
import
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
37765
|
-
const stream =
|
|
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 =
|
|
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
|
|
38221
|
+
import fs15 from "fs";
|
|
37828
38222
|
import fsp from "fs/promises";
|
|
37829
|
-
import
|
|
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
|
|
38267
|
+
return path22.join(dataDir, "logs", "bridge.log");
|
|
37835
38268
|
}
|
|
37836
38269
|
function defaultCursorFile(dataDir) {
|
|
37837
|
-
return
|
|
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 ||
|
|
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 ??
|
|
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
|
|
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 =
|
|
37864
|
-
const baseName =
|
|
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:
|
|
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(
|
|
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 =
|
|
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
|
|
38355
|
+
const linePattern = /.*?\n/g;
|
|
37923
38356
|
const entries = [];
|
|
37924
38357
|
let lineNum = cursor.lineNum;
|
|
37925
|
-
|
|
37926
|
-
|
|
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
|
-
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
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
|
|
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
|
|
37950
|
-
|
|
37951
|
-
|
|
37952
|
-
|
|
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
|
|
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
|
-
|
|
38521
|
+
this.retryAfterByTarget.delete(targetKey);
|
|
38041
38522
|
} catch (e) {
|
|
38042
38523
|
summary.failedTargetCount += 1;
|
|
38043
|
-
|
|
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.
|
|
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
|
-
|
|
38089
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
38127
|
-
import
|
|
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 =
|
|
38157
|
-
this.indexPath =
|
|
38158
|
-
|
|
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 =
|
|
38700
|
+
const filePath = path23.join(this.skillsDir, `${name}.md`);
|
|
38167
38701
|
try {
|
|
38168
|
-
const content =
|
|
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
|
|
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 =
|
|
38728
|
+
const filePath = path23.join(this.skillsDir, `${name}.md`);
|
|
38195
38729
|
const tmpPath = `${filePath}.tmp`;
|
|
38196
38730
|
let existing = "";
|
|
38197
38731
|
try {
|
|
38198
|
-
existing =
|
|
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.
|
|
38737
|
+
logger29.debug("Skill already in sync", { name, bytes: content.length });
|
|
38204
38738
|
return;
|
|
38205
38739
|
}
|
|
38206
|
-
|
|
38207
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
38266
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
38347
|
-
import
|
|
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 (
|
|
38423
|
-
return
|
|
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 =
|
|
38983
|
+
const file2 = path24.join(dataDir, "bridge.lock");
|
|
38450
38984
|
lockPath = file2;
|
|
38451
|
-
if (
|
|
38452
|
-
const raw =
|
|
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
|
-
|
|
38462
|
-
|
|
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 &&
|
|
38472
|
-
const current =
|
|
39005
|
+
if (lockPath && fs17.existsSync(lockPath)) {
|
|
39006
|
+
const current = fs17.readFileSync(lockPath, "utf-8").trim();
|
|
38473
39007
|
if (current === String(process.pid)) {
|
|
38474
|
-
|
|
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
|
|
38487
|
-
import
|
|
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 =
|
|
39026
|
+
this.filePath = path25.join(dataDir, LOCAL_WORKDIR_OVERRIDES_FILENAME);
|
|
38493
39027
|
}
|
|
38494
39028
|
read() {
|
|
38495
39029
|
try {
|
|
38496
|
-
const raw =
|
|
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
|
-
|
|
38516
|
-
|
|
38517
|
-
|
|
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
|
|
38935
|
-
import
|
|
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 =
|
|
38944
|
-
this.fingerprintPath =
|
|
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 (!
|
|
39003
|
-
const raw =
|
|
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 (!
|
|
39028
|
-
const raw =
|
|
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 =
|
|
39044
|
-
|
|
39045
|
-
|
|
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 =
|
|
39053
|
-
|
|
39054
|
-
|
|
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
|
|
39070
|
-
import
|
|
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 (!
|
|
39083
|
-
|
|
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
|
|
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
|
|
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
|
|
39718
|
+
return path28.dirname(realpathSync(sdkEntry));
|
|
39185
39719
|
} catch {
|
|
39186
|
-
return
|
|
39720
|
+
return path28.dirname(sdkEntry);
|
|
39187
39721
|
}
|
|
39188
39722
|
}
|
|
39189
39723
|
function findPnpmStoreDir(fromDir) {
|
|
39190
39724
|
let current = fromDir;
|
|
39191
|
-
while (current !==
|
|
39192
|
-
if (
|
|
39193
|
-
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 =
|
|
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
|
-
|
|
39244
|
-
|
|
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
|
|
39377
|
-
import * as
|
|
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
|
|
39923
|
+
const stat3 = await fs21.stat(sourceWorkdir).catch(() => null);
|
|
39390
39924
|
if (stat3?.isDirectory()) {
|
|
39391
|
-
await
|
|
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
|
|
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 =
|
|
39407
|
-
const dstNotebookDir =
|
|
39408
|
-
const dstNotebook =
|
|
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
|
|
39944
|
+
const nbStat = await fs21.stat(srcNotebook).catch(() => null);
|
|
39411
39945
|
if (nbStat?.isFile()) {
|
|
39412
|
-
await
|
|
39413
|
-
await
|
|
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
|
|
39965
|
+
const srcStat = await fs21.stat(srcPath).catch(() => null);
|
|
39432
39966
|
if (srcStat?.isFile()) {
|
|
39433
39967
|
const dstDir = sessionDirForCwd(newWorkdir);
|
|
39434
|
-
await
|
|
39435
|
-
const dstPath =
|
|
39436
|
-
await
|
|
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
|
|
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:
|
|
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) ??
|
|
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${
|
|
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
|
|
40414
|
+
import fs22 from "fs/promises";
|
|
39881
40415
|
import os12 from "os";
|
|
39882
|
-
import
|
|
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 ??
|
|
39888
|
-
await
|
|
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
|
|
40487
|
+
import fs23 from "fs/promises";
|
|
39954
40488
|
import os13 from "os";
|
|
39955
|
-
import
|
|
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
|
|
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
|
|
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
|
|
40066
|
-
const tmpDir = await
|
|
40067
|
-
const tmpBinary =
|
|
40068
|
-
const tmpSums =
|
|
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
|
|
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 =
|
|
40091
|
-
await
|
|
40092
|
-
await
|
|
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
|
|
40095
|
-
await
|
|
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
|
|
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
|
|
40670
|
+
import fs24 from "fs/promises";
|
|
40137
40671
|
import os14 from "os";
|
|
40138
|
-
import
|
|
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 ??
|
|
40183
|
-
await
|
|
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 =
|
|
40446
|
-
const
|
|
40979
|
+
const rootClaudeDir = path34.join(process.env.HOME ?? "/root", ".claude");
|
|
40980
|
+
const fs25 = await import("fs/promises");
|
|
40447
40981
|
try {
|
|
40448
|
-
await
|
|
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 =
|
|
40457
|
-
const dest =
|
|
40990
|
+
const src = path34.join(rootClaudeDir, file2);
|
|
40991
|
+
const dest = path34.join(agentConfigDir, file2);
|
|
40458
40992
|
try {
|
|
40459
|
-
await
|
|
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
|
|
41002
|
+
const fs25 = await import("fs/promises");
|
|
40469
41003
|
try {
|
|
40470
|
-
await
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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);
|