@fangyb/ahchat-bridge 0.1.39 → 0.1.41
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 +362 -107
- package/dist/feedbackWorkerCli.cjs +30 -0
- package/dist/index.js +362 -107
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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": {
|
|
@@ -26860,7 +26890,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
|
|
|
26860
26890
|
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
26891
|
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
26892
|
avatar: external_exports.string().optional().describe(
|
|
26863
|
-
"\u5934\u50CF key\
|
|
26893
|
+
"\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
26894
|
),
|
|
26865
26895
|
initial_instruction: external_exports.string().optional().describe(
|
|
26866
26896
|
'\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 +27233,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
|
|
|
27203
27233
|
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
27234
|
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
27235
|
avatar: external_exports.string().optional().describe(
|
|
27206
|
-
"\u5934\u50CF key\uFF08\u5982 avatar_dev / avatar_pm /
|
|
27236
|
+
"\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
27237
|
),
|
|
27208
27238
|
machine_bridge_key: external_exports.string().optional().describe(
|
|
27209
27239
|
'\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'
|
|
@@ -28199,9 +28229,11 @@ function buildGroupInboxPrompt(entries, opts = {}) {
|
|
|
28199
28229
|
|
|
28200
28230
|
// src/sdkEventMapper.ts
|
|
28201
28231
|
var logger11 = createModuleLogger("sdk.mapper");
|
|
28232
|
+
var DEBUG_ONLY_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set(["status", "thinking_tokens"]);
|
|
28202
28233
|
var HIGH_WATERMARK_INPUT_TOKENS = 12e4;
|
|
28203
28234
|
var WARN_THRESHOLD_INPUT_TOKENS = 1e5;
|
|
28204
28235
|
var LIVE_INPUT_PREVIEW_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
|
|
28236
|
+
var WORKDIR_MUTATION_TOOL_NAMES = /* @__PURE__ */ new Set(["write", "edit", "multiedit", "notebookedit", "bash"]);
|
|
28205
28237
|
var CONTEXT_OVERFLOW_LOCK_MS = 6e4;
|
|
28206
28238
|
function parseJsonRecord(text) {
|
|
28207
28239
|
try {
|
|
@@ -28223,6 +28255,17 @@ function isSuccessfulOfficialMediaOutput(toolName, output) {
|
|
|
28223
28255
|
if (!["succeeded", "success", "completed", "done"].includes(state)) return false;
|
|
28224
28256
|
return Array.isArray(parsed.images) && parsed.images.length > 0 || typeof parsed.video_url === "string" || typeof parsed.videoUrl === "string";
|
|
28225
28257
|
}
|
|
28258
|
+
function isWorkdirMutationToolName(toolName) {
|
|
28259
|
+
return WORKDIR_MUTATION_TOOL_NAMES.has(toolName.trim().toLowerCase());
|
|
28260
|
+
}
|
|
28261
|
+
function recordWorkdirSignal(proc, toolName, isError) {
|
|
28262
|
+
if (isError || !isGroupTask(proc) || !isWorkdirMutationToolName(toolName)) return;
|
|
28263
|
+
const signals = proc.workdirSignals ?? [];
|
|
28264
|
+
if (!signals.some((signal) => signal.toolName === toolName)) {
|
|
28265
|
+
signals.push({ toolName });
|
|
28266
|
+
}
|
|
28267
|
+
proc.workdirSignals = signals;
|
|
28268
|
+
}
|
|
28226
28269
|
function isContextOverflowText(text) {
|
|
28227
28270
|
const trimmed = text.trim();
|
|
28228
28271
|
return /^prompt is too long\b/i.test(trimmed) || /context length .* exceed/i.test(trimmed);
|
|
@@ -28727,6 +28770,7 @@ function countByStatus(todos) {
|
|
|
28727
28770
|
function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = false) {
|
|
28728
28771
|
const groupId = proc.currentTask?.groupId;
|
|
28729
28772
|
if (!groupId) return;
|
|
28773
|
+
const workdirSignals = proc.workdirSignals?.length ? [...proc.workdirSignals] : void 0;
|
|
28730
28774
|
proc.segmentCount += 1;
|
|
28731
28775
|
logger11.info("Group segment emitted", {
|
|
28732
28776
|
agentId: base.agentId,
|
|
@@ -28736,6 +28780,7 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
28736
28780
|
contentLen: content.length,
|
|
28737
28781
|
blockCount: contentBlocks.length,
|
|
28738
28782
|
blockTypes: contentBlocks.map((b) => b.type),
|
|
28783
|
+
workdirSignalCount: workdirSignals?.length ?? 0,
|
|
28739
28784
|
traceId: base.traceId,
|
|
28740
28785
|
isAuditOnly: content.length === 0,
|
|
28741
28786
|
isSilent
|
|
@@ -28748,9 +28793,11 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
28748
28793
|
groupId,
|
|
28749
28794
|
content,
|
|
28750
28795
|
contentBlocks: [...contentBlocks],
|
|
28796
|
+
...workdirSignals ? { workdirSignals } : {},
|
|
28751
28797
|
...isSilent ? { isSilent: true } : {}
|
|
28752
28798
|
}
|
|
28753
28799
|
});
|
|
28800
|
+
proc.workdirSignals = [];
|
|
28754
28801
|
}
|
|
28755
28802
|
function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
28756
28803
|
const trimmed = proc.segmentBuffer.trim();
|
|
@@ -28769,7 +28816,7 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
28769
28816
|
traceId: base.traceId
|
|
28770
28817
|
});
|
|
28771
28818
|
}
|
|
28772
|
-
} else if (proc.contentBlocks.length > 0) {
|
|
28819
|
+
} else if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
|
|
28773
28820
|
const blockCount = proc.contentBlocks.length;
|
|
28774
28821
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
|
|
28775
28822
|
proc.contentBlocks = [];
|
|
@@ -28859,7 +28906,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
28859
28906
|
proc.activeSubagentTaskIds?.delete(subagentTaskId);
|
|
28860
28907
|
}
|
|
28861
28908
|
}
|
|
28862
|
-
|
|
28909
|
+
const logUnhandledSystemSubtype = DEBUG_ONLY_SYSTEM_SUBTYPES.has(String(sysMsg.subtype ?? "")) ? logger11.debug.bind(logger11) : logger11.info.bind(logger11);
|
|
28910
|
+
logUnhandledSystemSubtype("SDK system subtype unhandled", {
|
|
28863
28911
|
agentId: proc.agentId,
|
|
28864
28912
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28865
28913
|
subtype: sysMsg.subtype ?? "(none)",
|
|
@@ -28905,6 +28953,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
28905
28953
|
const isMcpTool = parseMcpRuntimeToolName(toolName) != null;
|
|
28906
28954
|
proc.currentMcpInvocationId = isMcpTool ? createMcpToolInvocationId() : null;
|
|
28907
28955
|
proc.currentMcpInvocationStartedAt = isMcpTool ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
28956
|
+
proc.currentMcpInvocationToolUseId = isMcpTool ? toolUseId ?? null : null;
|
|
28908
28957
|
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
28909
28958
|
emit({
|
|
28910
28959
|
type: "agent:tool_use",
|
|
@@ -28915,6 +28964,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
28915
28964
|
input: {}
|
|
28916
28965
|
}
|
|
28917
28966
|
});
|
|
28967
|
+
}
|
|
28968
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
28918
28969
|
proc.contentBlocks.push({
|
|
28919
28970
|
type: "tool_use",
|
|
28920
28971
|
...toolUseId ? { toolUseId } : {},
|
|
@@ -29091,6 +29142,14 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29091
29142
|
}
|
|
29092
29143
|
if (proc.currentToolName && !proc.suppressCurrentToolUse && proc.currentMcpInvocationId && proc.currentMcpInvocationStartedAt && parseMcpRuntimeToolName(proc.currentToolName)) {
|
|
29093
29144
|
try {
|
|
29145
|
+
logger11.info("MCP audit start paired with tool_use", {
|
|
29146
|
+
agentId: proc.agentId,
|
|
29147
|
+
replyMessageId: base.replyMessageId,
|
|
29148
|
+
traceId: base.traceId,
|
|
29149
|
+
toolUseId: proc.currentMcpInvocationToolUseId ?? null,
|
|
29150
|
+
runtimeToolName: proc.currentToolName,
|
|
29151
|
+
mcpInvocationId: proc.currentMcpInvocationId
|
|
29152
|
+
});
|
|
29094
29153
|
proc.mcpAuditRecorder?.recordStart({
|
|
29095
29154
|
id: proc.currentMcpInvocationId,
|
|
29096
29155
|
runtimeToolName: proc.currentToolName,
|
|
@@ -29171,6 +29230,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29171
29230
|
});
|
|
29172
29231
|
proc.currentMcpInvocationId = null;
|
|
29173
29232
|
proc.currentMcpInvocationStartedAt = null;
|
|
29233
|
+
proc.currentMcpInvocationToolUseId = null;
|
|
29174
29234
|
proc.activeToolUseStartedAt = void 0;
|
|
29175
29235
|
proc.currentToolName = null;
|
|
29176
29236
|
proc.currentToolUseId = null;
|
|
@@ -29187,6 +29247,16 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29187
29247
|
}
|
|
29188
29248
|
if (proc.currentMcpInvocationId && parseMcpRuntimeToolName(toolName)) {
|
|
29189
29249
|
try {
|
|
29250
|
+
logger11.info("MCP audit result paired with tool_result", {
|
|
29251
|
+
agentId: proc.agentId,
|
|
29252
|
+
replyMessageId: base.replyMessageId,
|
|
29253
|
+
traceId: base.traceId,
|
|
29254
|
+
toolUseId,
|
|
29255
|
+
startedToolUseId: proc.currentMcpInvocationToolUseId ?? null,
|
|
29256
|
+
runtimeToolName: toolName,
|
|
29257
|
+
mcpInvocationId: proc.currentMcpInvocationId,
|
|
29258
|
+
isError
|
|
29259
|
+
});
|
|
29190
29260
|
proc.mcpAuditRecorder?.recordResult({
|
|
29191
29261
|
id: proc.currentMcpInvocationId,
|
|
29192
29262
|
runtimeToolName: toolName,
|
|
@@ -29208,6 +29278,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29208
29278
|
}
|
|
29209
29279
|
proc.currentMcpInvocationId = null;
|
|
29210
29280
|
proc.currentMcpInvocationStartedAt = null;
|
|
29281
|
+
proc.currentMcpInvocationToolUseId = null;
|
|
29211
29282
|
}
|
|
29212
29283
|
if (shouldStreamInternals(proc)) {
|
|
29213
29284
|
emit({
|
|
@@ -29235,6 +29306,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29235
29306
|
}
|
|
29236
29307
|
}
|
|
29237
29308
|
}
|
|
29309
|
+
recordWorkdirSignal(proc, toolName, isError);
|
|
29238
29310
|
proc.activeToolUseStartedAt = void 0;
|
|
29239
29311
|
proc.currentToolUseId = null;
|
|
29240
29312
|
}
|
|
@@ -29296,7 +29368,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29296
29368
|
if (isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: groupMode })) {
|
|
29297
29369
|
checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
|
|
29298
29370
|
emitUsageReported(proc, emit, base, usage);
|
|
29299
|
-
if (groupMode && proc.contentBlocks.length > 0) {
|
|
29371
|
+
if (groupMode && (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0)) {
|
|
29300
29372
|
emitGroupSegment(
|
|
29301
29373
|
proc,
|
|
29302
29374
|
emit,
|
|
@@ -29341,11 +29413,12 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29341
29413
|
proc.segmentBuffer = proc.accumulatedText;
|
|
29342
29414
|
flushTextSegmentOnBlockStop(proc, emit, base);
|
|
29343
29415
|
}
|
|
29344
|
-
if (proc.contentBlocks.length > 0) {
|
|
29416
|
+
if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
|
|
29345
29417
|
logger11.info("Group turn trailing audit segment", {
|
|
29346
29418
|
agentId: proc.agentId,
|
|
29347
29419
|
replyMessageId: base.replyMessageId,
|
|
29348
29420
|
blockCount: proc.contentBlocks.length,
|
|
29421
|
+
workdirSignalCount: proc.workdirSignals?.length ?? 0,
|
|
29349
29422
|
traceId: base.traceId
|
|
29350
29423
|
});
|
|
29351
29424
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
|
|
@@ -29490,6 +29563,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29490
29563
|
payload: { ...wireBase(base), error: errorText }
|
|
29491
29564
|
});
|
|
29492
29565
|
proc.contentBlocks = [];
|
|
29566
|
+
proc.workdirSignals = [];
|
|
29493
29567
|
proc.accumulatedText = "";
|
|
29494
29568
|
proc.accumulatedThinking = "";
|
|
29495
29569
|
proc.segmentBuffer = "";
|
|
@@ -29522,6 +29596,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29522
29596
|
proc.apiErrorEmitted = true;
|
|
29523
29597
|
}
|
|
29524
29598
|
proc.contentBlocks = [];
|
|
29599
|
+
proc.workdirSignals = [];
|
|
29525
29600
|
proc.accumulatedText = "";
|
|
29526
29601
|
proc.accumulatedThinking = "";
|
|
29527
29602
|
proc.segmentBuffer = "";
|
|
@@ -29548,6 +29623,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29548
29623
|
proc.apiErrorEmitted = true;
|
|
29549
29624
|
}
|
|
29550
29625
|
proc.contentBlocks = [];
|
|
29626
|
+
proc.workdirSignals = [];
|
|
29551
29627
|
proc.accumulatedText = "";
|
|
29552
29628
|
proc.accumulatedThinking = "";
|
|
29553
29629
|
proc.segmentBuffer = "";
|
|
@@ -29584,6 +29660,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29584
29660
|
});
|
|
29585
29661
|
}
|
|
29586
29662
|
proc.contentBlocks = [];
|
|
29663
|
+
proc.workdirSignals = [];
|
|
29587
29664
|
proc.accumulatedText = "";
|
|
29588
29665
|
proc.accumulatedThinking = "";
|
|
29589
29666
|
proc.segmentBuffer = "";
|
|
@@ -29621,9 +29698,11 @@ function resetAccumulators(proc) {
|
|
|
29621
29698
|
proc.currentToolUseId = null;
|
|
29622
29699
|
proc.currentMcpInvocationId = null;
|
|
29623
29700
|
proc.currentMcpInvocationStartedAt = null;
|
|
29701
|
+
proc.currentMcpInvocationToolUseId = null;
|
|
29624
29702
|
proc.activeToolUseStartedAt = void 0;
|
|
29625
29703
|
proc.segmentBuffer = "";
|
|
29626
29704
|
proc.segmentCount = 0;
|
|
29705
|
+
proc.workdirSignals = [];
|
|
29627
29706
|
proc.accumulatedToolInput = "";
|
|
29628
29707
|
proc.apiErrorEmitted = false;
|
|
29629
29708
|
proc.peakContextUsage = void 0;
|
|
@@ -29819,7 +29898,7 @@ function missingSubscriptionMessage(subscriptionId) {
|
|
|
29819
29898
|
}
|
|
29820
29899
|
var NODE_USER_UID = 1e3;
|
|
29821
29900
|
var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
|
|
29822
|
-
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-
|
|
29901
|
+
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v6";
|
|
29823
29902
|
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;
|
|
29824
29903
|
var IMAGE_READ_EXT_RE = /\.(?:bmp|gif|jpeg|jpg|png|webp)$/i;
|
|
29825
29904
|
var DOCUMENT_READING_RULES = `DOCUMENT READING:
|
|
@@ -29835,6 +29914,31 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
|
|
|
29835
29914
|
- 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.
|
|
29836
29915
|
- 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.
|
|
29837
29916
|
- 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.`;
|
|
29917
|
+
var SMITH_ALLOWED_TOOLS = [
|
|
29918
|
+
// creation / configuration (Smith-only)
|
|
29919
|
+
"mcp__neural__create_agent",
|
|
29920
|
+
"mcp__neural__update_agent_profile",
|
|
29921
|
+
"mcp__neural__recommend_agent_skills",
|
|
29922
|
+
"mcp__neural__read_skill",
|
|
29923
|
+
// diagnostics (log detective)
|
|
29924
|
+
"mcp__neural__fetch_logs",
|
|
29925
|
+
// friend approval (Smith-only)
|
|
29926
|
+
"mcp__neural__list_friends",
|
|
29927
|
+
"mcp__neural__accept_friend",
|
|
29928
|
+
"mcp__neural__add_friend",
|
|
29929
|
+
// team assembly
|
|
29930
|
+
"mcp__neural__list_contacts",
|
|
29931
|
+
"mcp__neural__create_group",
|
|
29932
|
+
"mcp__neural__add_to_group",
|
|
29933
|
+
"mcp__neural__transfer_group_owner",
|
|
29934
|
+
"mcp__neural__leave_group",
|
|
29935
|
+
// neural bridge — cross-scope coordination when Smith is pulled into a group
|
|
29936
|
+
"mcp__neural__neural_send",
|
|
29937
|
+
"mcp__neural__neural_list_scopes",
|
|
29938
|
+
// interaction / planning
|
|
29939
|
+
"AskUserQuestion",
|
|
29940
|
+
"Write"
|
|
29941
|
+
];
|
|
29838
29942
|
function resolveVisionMcpToolHints(externalMcp) {
|
|
29839
29943
|
let describe3 = null;
|
|
29840
29944
|
let ocr = null;
|
|
@@ -31099,7 +31203,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31099
31203
|
} catch (error51) {
|
|
31100
31204
|
logger14.error("Failed to read existing API-key agent settings; starting fresh", {
|
|
31101
31205
|
agentId: agentConfig.id,
|
|
31102
|
-
|
|
31206
|
+
settingsFile: "settings.json",
|
|
31103
31207
|
error: error51
|
|
31104
31208
|
});
|
|
31105
31209
|
}
|
|
@@ -31112,7 +31216,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31112
31216
|
await fs6.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
|
|
31113
31217
|
logger14.info("API-key agent using isolated config dir", {
|
|
31114
31218
|
agentId: agentConfig.id,
|
|
31115
|
-
|
|
31219
|
+
configDirKind: "api-key-agent",
|
|
31116
31220
|
isNew,
|
|
31117
31221
|
hasBaseUrl: !!cfg.apiBaseUrl,
|
|
31118
31222
|
settingsWritten: Object.keys(envEntries)
|
|
@@ -31206,12 +31310,21 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31206
31310
|
});
|
|
31207
31311
|
}
|
|
31208
31312
|
}
|
|
31209
|
-
const
|
|
31313
|
+
const resolvedExternalMcp = this.mcpRegistry?.buildForAgent({
|
|
31210
31314
|
agentId: agentConfig.id,
|
|
31211
31315
|
capabilityTier: cfg.capabilityTier,
|
|
31212
31316
|
isSmith: smithAgent,
|
|
31213
31317
|
cwd: agentCwd
|
|
31214
31318
|
}) ?? { mcpServers: {}, allowedTools: [], toolAbi: [] };
|
|
31319
|
+
const externalMcp = smithAgent ? { mcpServers: {}, allowedTools: [], toolAbi: [] } : resolvedExternalMcp;
|
|
31320
|
+
if (smithAgent && (Object.keys(resolvedExternalMcp.mcpServers).length > 0 || resolvedExternalMcp.allowedTools.length > 0 || (resolvedExternalMcp.toolAbi?.length ?? 0) > 0)) {
|
|
31321
|
+
logger14.info("Smith external MCP ignored by fixed tool whitelist", {
|
|
31322
|
+
agentId: agentConfig.id,
|
|
31323
|
+
scope: scopeKey(scope),
|
|
31324
|
+
serverNames: Object.keys(resolvedExternalMcp.mcpServers),
|
|
31325
|
+
allowedToolCount: resolvedExternalMcp.allowedTools.length
|
|
31326
|
+
});
|
|
31327
|
+
}
|
|
31215
31328
|
const visionMcpTools = resolveVisionMcpToolHints(externalMcp);
|
|
31216
31329
|
logger14.info("External MCP resolved for runtime", {
|
|
31217
31330
|
agentId: agentConfig.id,
|
|
@@ -31284,6 +31397,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31284
31397
|
logger14.info("Creating Agent query", {
|
|
31285
31398
|
agentId: agentConfig.id,
|
|
31286
31399
|
scope: scopeKey(scope),
|
|
31400
|
+
systemPromptMode: smithAgent ? "string" : "preset",
|
|
31287
31401
|
cwd: agentCwd,
|
|
31288
31402
|
resume: !!savedSessionId,
|
|
31289
31403
|
sessionId: savedSessionId,
|
|
@@ -31304,71 +31418,63 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31304
31418
|
});
|
|
31305
31419
|
const planModeRef = { active: false, denyCount: 0 };
|
|
31306
31420
|
const mediaGenerationTurnGuard = createOfficialMediaGenerationTurnGuard();
|
|
31421
|
+
const platformRules = scope.kind === "group" ? PLATFORM_AGENT_RULES : PLATFORM_AGENT_RULES_SINGLE;
|
|
31422
|
+
const appendText = [
|
|
31423
|
+
platformRules,
|
|
31424
|
+
nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
|
|
31425
|
+
DOCUMENT_READING_RULES,
|
|
31426
|
+
MEDIA_GENERATION_RULES,
|
|
31427
|
+
agentConfig.systemPrompt,
|
|
31428
|
+
scopedInstructions,
|
|
31429
|
+
notebookSection,
|
|
31430
|
+
forkHistorySection,
|
|
31431
|
+
scopesSection
|
|
31432
|
+
].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n");
|
|
31433
|
+
const systemPrompt = smithAgent ? appendText : { type: "preset", preset: "claude_code", append: appendText };
|
|
31434
|
+
const universalAllowedTools = [
|
|
31435
|
+
...nativeReadToolDisabled ? [] : ["Read"],
|
|
31436
|
+
"Edit",
|
|
31437
|
+
"Write",
|
|
31438
|
+
"Bash",
|
|
31439
|
+
"Glob",
|
|
31440
|
+
"Grep",
|
|
31441
|
+
...builtinWebSearchAllowed ? ["WebSearch"] : [],
|
|
31442
|
+
"WebFetch",
|
|
31443
|
+
"TodoWrite",
|
|
31444
|
+
"TaskCreate",
|
|
31445
|
+
"TaskUpdate",
|
|
31446
|
+
"AskUserQuestion",
|
|
31447
|
+
"mcp__neural__neural_send",
|
|
31448
|
+
"mcp__neural__neural_list_scopes",
|
|
31449
|
+
"mcp__neural__self_note",
|
|
31450
|
+
"mcp__neural__list_contacts",
|
|
31451
|
+
"mcp__neural__create_group",
|
|
31452
|
+
"mcp__neural__add_to_group",
|
|
31453
|
+
"mcp__neural__leave_group",
|
|
31454
|
+
"mcp__neural__remove_from_group",
|
|
31455
|
+
"mcp__neural__create_group_issue",
|
|
31456
|
+
"mcp__neural__resolve_group_issue",
|
|
31457
|
+
"mcp__neural__list_group_tasks",
|
|
31458
|
+
"mcp__neural__update_group_task",
|
|
31459
|
+
"mcp__neural__transfer_group_owner",
|
|
31460
|
+
"mcp__neural__post_to_moments",
|
|
31461
|
+
"mcp__neural__post_to_forum",
|
|
31462
|
+
"mcp__neural__read_moments",
|
|
31463
|
+
"mcp__neural__read_chat_history",
|
|
31464
|
+
"mcp__neural__read_document",
|
|
31465
|
+
"mcp__neural__list_available_skills",
|
|
31466
|
+
"mcp__neural__list_skill_index"
|
|
31467
|
+
];
|
|
31307
31468
|
const options = {
|
|
31308
31469
|
cwd: agentCwd,
|
|
31309
|
-
systemPrompt
|
|
31310
|
-
type: "preset",
|
|
31311
|
-
preset: "claude_code",
|
|
31312
|
-
append: [
|
|
31313
|
-
PLATFORM_AGENT_RULES,
|
|
31314
|
-
nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
|
|
31315
|
-
DOCUMENT_READING_RULES,
|
|
31316
|
-
MEDIA_GENERATION_RULES,
|
|
31317
|
-
agentConfig.systemPrompt,
|
|
31318
|
-
scopedInstructions,
|
|
31319
|
-
notebookSection,
|
|
31320
|
-
forkHistorySection,
|
|
31321
|
-
scopesSection
|
|
31322
|
-
].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
|
|
31323
|
-
},
|
|
31470
|
+
systemPrompt,
|
|
31324
31471
|
permissionMode: "bypassPermissions",
|
|
31325
31472
|
allowDangerouslySkipPermissions: true,
|
|
31326
31473
|
// allowedTools is the visibility whitelist passed to Claude Code. MCP tools
|
|
31327
31474
|
// with always_ask must still be included here so the model can request them;
|
|
31328
31475
|
// the MCP server policy/permission layer decides whether execution asks.
|
|
31329
31476
|
allowedTools: [
|
|
31330
|
-
...
|
|
31331
|
-
"Edit",
|
|
31332
|
-
"Write",
|
|
31333
|
-
"Bash",
|
|
31334
|
-
"Glob",
|
|
31335
|
-
"Grep",
|
|
31336
|
-
...builtinWebSearchAllowed ? ["WebSearch"] : [],
|
|
31337
|
-
"WebFetch",
|
|
31338
|
-
"TodoWrite",
|
|
31339
|
-
"TaskCreate",
|
|
31340
|
-
"TaskUpdate",
|
|
31341
|
-
"AskUserQuestion",
|
|
31342
|
-
"mcp__neural__neural_send",
|
|
31343
|
-
"mcp__neural__neural_list_scopes",
|
|
31344
|
-
"mcp__neural__self_note",
|
|
31345
|
-
"mcp__neural__list_contacts",
|
|
31346
|
-
"mcp__neural__create_group",
|
|
31347
|
-
"mcp__neural__add_to_group",
|
|
31348
|
-
"mcp__neural__leave_group",
|
|
31349
|
-
"mcp__neural__remove_from_group",
|
|
31350
|
-
"mcp__neural__create_group_issue",
|
|
31351
|
-
"mcp__neural__resolve_group_issue",
|
|
31352
|
-
"mcp__neural__list_group_tasks",
|
|
31353
|
-
"mcp__neural__update_group_task",
|
|
31354
|
-
"mcp__neural__transfer_group_owner",
|
|
31355
|
-
"mcp__neural__post_to_moments",
|
|
31356
|
-
"mcp__neural__post_to_forum",
|
|
31357
|
-
"mcp__neural__read_moments",
|
|
31358
|
-
"mcp__neural__read_chat_history",
|
|
31359
|
-
"mcp__neural__read_document",
|
|
31360
|
-
"mcp__neural__list_available_skills",
|
|
31361
|
-
"mcp__neural__list_skill_index",
|
|
31362
|
-
...isSmithAgent2(agentConfig) ? [
|
|
31363
|
-
"mcp__neural__create_agent",
|
|
31364
|
-
"mcp__neural__update_agent_profile",
|
|
31365
|
-
"mcp__neural__recommend_agent_skills",
|
|
31366
|
-
"mcp__neural__read_skill",
|
|
31367
|
-
"mcp__neural__list_friends",
|
|
31368
|
-
"mcp__neural__accept_friend",
|
|
31369
|
-
"mcp__neural__add_friend",
|
|
31370
|
-
"mcp__neural__fetch_logs"
|
|
31371
|
-
] : [],
|
|
31477
|
+
...smithAgent ? SMITH_ALLOWED_TOOLS : universalAllowedTools,
|
|
31372
31478
|
...externalMcp.allowedTools
|
|
31373
31479
|
],
|
|
31374
31480
|
// Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
|
|
@@ -31380,7 +31486,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31380
31486
|
// instructions as the workflow body (replacing the default code-implementation
|
|
31381
31487
|
// phases). The SDK wraps it with read-only enforcement + ExitPlanMode protocol.
|
|
31382
31488
|
planModeInstructions: (() => {
|
|
31383
|
-
const planTools = [
|
|
31489
|
+
const planTools = (smithAgent ? ["AskUserQuestion", "Write (plan file only)"] : [
|
|
31384
31490
|
...nativeReadToolDisabled ? [] : ["Read"],
|
|
31385
31491
|
"Glob",
|
|
31386
31492
|
"Grep",
|
|
@@ -31388,19 +31494,19 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31388
31494
|
"WebFetch",
|
|
31389
31495
|
"AskUserQuestion",
|
|
31390
31496
|
"Write (plan file only)"
|
|
31391
|
-
].join(", ");
|
|
31392
|
-
const researchTools = [
|
|
31497
|
+
]).join(", ");
|
|
31498
|
+
const researchTools = (smithAgent ? ["AskUserQuestion"] : [
|
|
31393
31499
|
...nativeReadToolDisabled ? [] : ["Read"],
|
|
31394
31500
|
"Grep",
|
|
31395
31501
|
...builtinWebSearchAllowed ? ["WebSearch"] : []
|
|
31396
|
-
].join(", ");
|
|
31397
|
-
const unavailableTools = [
|
|
31502
|
+
]).join(", ");
|
|
31503
|
+
const unavailableTools = (smithAgent ? ["Read", "Edit", "Bash", "Glob", "Grep", "WebFetch", "TodoWrite", "ExitPlanMode"] : [
|
|
31398
31504
|
"Edit",
|
|
31399
31505
|
"Bash",
|
|
31400
31506
|
"TodoWrite",
|
|
31401
31507
|
"ExitPlanMode",
|
|
31402
31508
|
...nativeReadToolDisabled ? ["Read"] : []
|
|
31403
|
-
].join(", ");
|
|
31509
|
+
]).join(", ");
|
|
31404
31510
|
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." : "";
|
|
31405
31511
|
return `You are a PLANNER, NOT an executor. The user will execute your plan later.
|
|
31406
31512
|
|
|
@@ -31630,17 +31736,17 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31630
31736
|
}
|
|
31631
31737
|
};
|
|
31632
31738
|
const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
|
|
31633
|
-
const appendStr = options.systemPrompt.append;
|
|
31634
31739
|
logger14.info("Platform rules attached", {
|
|
31635
31740
|
agentId: agentConfig.id,
|
|
31636
31741
|
scope: scopeKey(scope),
|
|
31637
|
-
|
|
31742
|
+
systemPromptMode: smithAgent ? "string" : "preset",
|
|
31743
|
+
platformRulesLen: platformRules.length,
|
|
31638
31744
|
userPromptLen: userPromptTrimmed.length,
|
|
31639
31745
|
hasUserPrompt: userPromptTrimmed.length > 0,
|
|
31640
31746
|
notebookLen: notebookSection.length,
|
|
31641
31747
|
forkHistoryLen: forkHistorySection.length,
|
|
31642
31748
|
scopesLen: scopesSection.length,
|
|
31643
|
-
appendLen:
|
|
31749
|
+
appendLen: appendText.length,
|
|
31644
31750
|
hasCreateAgentTool: smithAgent,
|
|
31645
31751
|
hasLogDetectiveTools: smithAgent && this.skillStore !== null
|
|
31646
31752
|
});
|
|
@@ -31710,6 +31816,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31710
31816
|
mcpAuditRecorder: this.mcpAuditRecorder,
|
|
31711
31817
|
segmentBuffer: "",
|
|
31712
31818
|
segmentCount: 0,
|
|
31819
|
+
workdirSignals: [],
|
|
31713
31820
|
accumulatedToolInput: "",
|
|
31714
31821
|
planModeRef,
|
|
31715
31822
|
mediaGenerationTurnGuard,
|
|
@@ -32743,6 +32850,7 @@ ${lines.join("\n")}`;
|
|
|
32743
32850
|
proc.currentToolName = null;
|
|
32744
32851
|
proc.segmentBuffer = "";
|
|
32745
32852
|
proc.segmentCount = 0;
|
|
32853
|
+
proc.workdirSignals = [];
|
|
32746
32854
|
}
|
|
32747
32855
|
clearPostMergeContinuationTimer(runtime) {
|
|
32748
32856
|
if (runtime.postMergeContinuationTimer) {
|
|
@@ -35513,6 +35621,7 @@ var HttpMcpRegistry = class {
|
|
|
35513
35621
|
localStore;
|
|
35514
35622
|
serverConnections = /* @__PURE__ */ new Map();
|
|
35515
35623
|
localConnections = /* @__PURE__ */ new Map();
|
|
35624
|
+
missingSecretWarnedKeys = /* @__PURE__ */ new Set();
|
|
35516
35625
|
lastRefreshCount = null;
|
|
35517
35626
|
apiUrl(suffix) {
|
|
35518
35627
|
const base = this.serverApiUrl.replace(/\/$/, "");
|
|
@@ -35613,13 +35722,19 @@ var HttpMcpRegistry = class {
|
|
|
35613
35722
|
return false;
|
|
35614
35723
|
});
|
|
35615
35724
|
}
|
|
35725
|
+
warnMissingSecretOnce(connection) {
|
|
35726
|
+
const key = `${connection.id}:${connection.providerId}:${connection.serverName}`;
|
|
35727
|
+
if (this.missingSecretWarnedKeys.has(key)) return;
|
|
35728
|
+
this.missingSecretWarnedKeys.add(key);
|
|
35729
|
+
logger19.warn("Skipping MCP connection without required secret", {
|
|
35730
|
+
id: connection.id,
|
|
35731
|
+
providerId: connection.providerId,
|
|
35732
|
+
serverName: connection.serverName
|
|
35733
|
+
});
|
|
35734
|
+
}
|
|
35616
35735
|
toSdkServerConfig(connection, ctx) {
|
|
35617
35736
|
if (mcpConnectionRequiresSecret(connection) && !connection.hasAuthSecret) {
|
|
35618
|
-
|
|
35619
|
-
id: connection.id,
|
|
35620
|
-
providerId: connection.providerId,
|
|
35621
|
-
serverName: connection.serverName
|
|
35622
|
-
});
|
|
35737
|
+
this.warnMissingSecretOnce(connection);
|
|
35623
35738
|
return null;
|
|
35624
35739
|
}
|
|
35625
35740
|
const policies = toolPolicies(connection);
|
|
@@ -37806,8 +37921,47 @@ import fs14 from "fs";
|
|
|
37806
37921
|
import fsp from "fs/promises";
|
|
37807
37922
|
import path21 from "path";
|
|
37808
37923
|
var logger28 = createModuleLogger("bridge.logUploader");
|
|
37924
|
+
var STALE_RATE_LIMIT_SKIP_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
37809
37925
|
var DEFAULT_LOG_UPLOAD_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
37810
37926
|
var DEFAULT_BATCH_SIZE = 200;
|
|
37927
|
+
var MAX_UPLOAD_CHUNK_BODY_BYTES = 448 * 1024;
|
|
37928
|
+
var MAX_UPLOAD_RETRY_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
37929
|
+
var LogUploadHttpError = class extends Error {
|
|
37930
|
+
status;
|
|
37931
|
+
scope;
|
|
37932
|
+
retryAfterMs;
|
|
37933
|
+
constructor(status, bodyText) {
|
|
37934
|
+
const parsed = parseUploadErrorBody(bodyText);
|
|
37935
|
+
const summary = parsed.error ?? bodyText;
|
|
37936
|
+
super(`upload failed HTTP ${status}: ${summary.slice(0, 160)}`);
|
|
37937
|
+
this.name = "LogUploadHttpError";
|
|
37938
|
+
this.status = status;
|
|
37939
|
+
if (parsed.scope) this.scope = parsed.scope;
|
|
37940
|
+
if (parsed.retryAfterMs !== void 0) this.retryAfterMs = parsed.retryAfterMs;
|
|
37941
|
+
}
|
|
37942
|
+
};
|
|
37943
|
+
function normalizeRetryAfterMs(value) {
|
|
37944
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
|
|
37945
|
+
return Math.min(Math.ceil(value), MAX_UPLOAD_RETRY_AFTER_MS);
|
|
37946
|
+
}
|
|
37947
|
+
function parseUploadErrorBody(bodyText) {
|
|
37948
|
+
try {
|
|
37949
|
+
const parsed = JSON.parse(bodyText);
|
|
37950
|
+
if (typeof parsed !== "object" || parsed === null) return {};
|
|
37951
|
+
const body = parsed;
|
|
37952
|
+
return {
|
|
37953
|
+
...typeof body.error === "string" && body.error.length > 0 ? { error: body.error } : {},
|
|
37954
|
+
...typeof body.scope === "string" && body.scope.length > 0 ? { scope: body.scope } : {},
|
|
37955
|
+
...(() => {
|
|
37956
|
+
const retryAfterMs = normalizeRetryAfterMs(body.retryAfterMs);
|
|
37957
|
+
return retryAfterMs === void 0 ? {} : { retryAfterMs };
|
|
37958
|
+
})()
|
|
37959
|
+
};
|
|
37960
|
+
} catch (e) {
|
|
37961
|
+
logger28.debug("Failed to parse log upload error body", { error: e });
|
|
37962
|
+
return {};
|
|
37963
|
+
}
|
|
37964
|
+
}
|
|
37811
37965
|
function defaultLogFile(dataDir) {
|
|
37812
37966
|
return path21.join(dataDir, "logs", "bridge.log");
|
|
37813
37967
|
}
|
|
@@ -37897,26 +38051,38 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
37897
38051
|
return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
|
|
37898
38052
|
}
|
|
37899
38053
|
const processed = raw.slice(0, lastNewline + 1);
|
|
37900
|
-
const
|
|
38054
|
+
const linePattern = /.*?\n/g;
|
|
37901
38055
|
const entries = [];
|
|
37902
38056
|
let lineNum = cursor.lineNum;
|
|
37903
|
-
|
|
37904
|
-
|
|
38057
|
+
let offset = cursor.offset;
|
|
38058
|
+
let match;
|
|
38059
|
+
while ((match = linePattern.exec(processed)) !== null) {
|
|
38060
|
+
const rawLine = match[0];
|
|
38061
|
+
const line = rawLine.endsWith("\r\n") ? rawLine.slice(0, -2) : rawLine.slice(0, -1);
|
|
37905
38062
|
lineNum += 1;
|
|
38063
|
+
offset += Buffer.byteLength(rawLine, "utf8");
|
|
38064
|
+
const cursorAfter = {
|
|
38065
|
+
offset,
|
|
38066
|
+
lineNum,
|
|
38067
|
+
...fingerprint ? { fingerprint } : {}
|
|
38068
|
+
};
|
|
37906
38069
|
if (line.length === 0) continue;
|
|
37907
38070
|
const parsed = parseLogLine(line);
|
|
37908
38071
|
if (!parsed) continue;
|
|
37909
38072
|
entries.push({
|
|
37910
|
-
|
|
37911
|
-
|
|
37912
|
-
|
|
37913
|
-
|
|
38073
|
+
entry: {
|
|
38074
|
+
...parsed,
|
|
38075
|
+
raw: line,
|
|
38076
|
+
file: fileName,
|
|
38077
|
+
lineNum
|
|
38078
|
+
},
|
|
38079
|
+
cursorAfter
|
|
37914
38080
|
});
|
|
37915
38081
|
}
|
|
37916
38082
|
return {
|
|
37917
38083
|
entries,
|
|
37918
38084
|
nextCursor: {
|
|
37919
|
-
offset
|
|
38085
|
+
offset,
|
|
37920
38086
|
lineNum,
|
|
37921
38087
|
...fingerprint ? { fingerprint } : {}
|
|
37922
38088
|
},
|
|
@@ -37924,15 +38090,35 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
37924
38090
|
reason: "advanced"
|
|
37925
38091
|
};
|
|
37926
38092
|
}
|
|
37927
|
-
function
|
|
37928
|
-
|
|
37929
|
-
|
|
37930
|
-
|
|
38093
|
+
function logEntryTimeRange(entries) {
|
|
38094
|
+
let minTime = Number.POSITIVE_INFINITY;
|
|
38095
|
+
let maxTime = Number.NEGATIVE_INFINITY;
|
|
38096
|
+
let minTs = "";
|
|
38097
|
+
let maxTs = "";
|
|
38098
|
+
for (const entry of entries) {
|
|
38099
|
+
const parsed = Date.parse(entry.ts);
|
|
38100
|
+
if (!Number.isFinite(parsed)) return null;
|
|
38101
|
+
if (parsed < minTime) {
|
|
38102
|
+
minTime = parsed;
|
|
38103
|
+
minTs = entry.ts;
|
|
38104
|
+
}
|
|
38105
|
+
if (parsed > maxTime) {
|
|
38106
|
+
maxTime = parsed;
|
|
38107
|
+
maxTs = entry.ts;
|
|
38108
|
+
}
|
|
37931
38109
|
}
|
|
37932
|
-
return
|
|
38110
|
+
if (!minTs || !maxTs) return null;
|
|
38111
|
+
return { minTs, maxTs, maxTime };
|
|
38112
|
+
}
|
|
38113
|
+
function isStaleRateLimitedChunk(entries, nowMs) {
|
|
38114
|
+
const range = logEntryTimeRange(entries);
|
|
38115
|
+
if (!range) return null;
|
|
38116
|
+
if (nowMs - range.maxTime <= STALE_RATE_LIMIT_SKIP_AGE_MS) return null;
|
|
38117
|
+
return { minTs: range.minTs, maxTs: range.maxTs };
|
|
37933
38118
|
}
|
|
37934
38119
|
var BridgeLogUploader = class {
|
|
37935
38120
|
options;
|
|
38121
|
+
retryAfterByTarget = /* @__PURE__ */ new Map();
|
|
37936
38122
|
timer = null;
|
|
37937
38123
|
running = false;
|
|
37938
38124
|
stopped = false;
|
|
@@ -37949,7 +38135,7 @@ var BridgeLogUploader = class {
|
|
|
37949
38135
|
bridgeId: options.bridgeId,
|
|
37950
38136
|
hostname: options.hostname,
|
|
37951
38137
|
intervalMs: options.intervalMs ?? DEFAULT_LOG_UPLOAD_INTERVAL_MS,
|
|
37952
|
-
batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
38138
|
+
batchSize: Math.max(1, Math.floor(options.batchSize ?? DEFAULT_BATCH_SIZE)),
|
|
37953
38139
|
primaryTarget,
|
|
37954
38140
|
extraTargets: (options.extraLogFiles ?? []).map((file2) => normalizeTarget(file2, options.dataDir)),
|
|
37955
38141
|
includeRotatedBridgeLogs: options.includeRotatedBridgeLogs ?? true
|
|
@@ -37991,13 +38177,26 @@ var BridgeLogUploader = class {
|
|
|
37991
38177
|
bridgeEntryCount: 0,
|
|
37992
38178
|
uploadedChunkCount: 0,
|
|
37993
38179
|
accepted: 0,
|
|
37994
|
-
skipped: 0
|
|
38180
|
+
skipped: 0,
|
|
38181
|
+
dropped: 0
|
|
37995
38182
|
};
|
|
37996
38183
|
try {
|
|
37997
38184
|
const targets = await this.resolveTargets();
|
|
37998
38185
|
summary.targetCount = targets.length;
|
|
37999
38186
|
for (const target of targets) {
|
|
38187
|
+
const targetKey = this.targetKey(target);
|
|
38000
38188
|
try {
|
|
38189
|
+
const retryUntil = this.retryAfterByTarget.get(targetKey);
|
|
38190
|
+
const now = Date.now();
|
|
38191
|
+
if (retryUntil && retryUntil > now) {
|
|
38192
|
+
summary.idleTargetCount += 1;
|
|
38193
|
+
logger28.debug("Bridge log upload target backoff active", {
|
|
38194
|
+
logFile: target.logFile,
|
|
38195
|
+
retryAfterMs: retryUntil - now
|
|
38196
|
+
});
|
|
38197
|
+
continue;
|
|
38198
|
+
}
|
|
38199
|
+
if (retryUntil) this.retryAfterByTarget.delete(targetKey);
|
|
38001
38200
|
const cursor = await readCursor(target.cursorFile);
|
|
38002
38201
|
const batch = await this.readNewEntries(target, cursor);
|
|
38003
38202
|
if (!batch.advanced) {
|
|
@@ -38009,28 +38208,45 @@ var BridgeLogUploader = class {
|
|
|
38009
38208
|
summary.advancedTargetCount += 1;
|
|
38010
38209
|
summary.parsedEntryCount += batch.entries.length;
|
|
38011
38210
|
if (batch.entries.length > 0) {
|
|
38012
|
-
const result = await this.uploadEntries(batch.entries);
|
|
38211
|
+
const result = await this.uploadEntries(batch.entries, target.cursorFile, batch.nextCursor);
|
|
38013
38212
|
summary.bridgeEntryCount += result.bridgeEntryCount;
|
|
38014
38213
|
summary.uploadedChunkCount += result.uploadedChunkCount;
|
|
38015
38214
|
summary.accepted += result.accepted;
|
|
38016
38215
|
summary.skipped += result.skipped;
|
|
38216
|
+
summary.dropped += result.dropped;
|
|
38217
|
+
} else {
|
|
38218
|
+
await writeCursor(target.cursorFile, batch.nextCursor);
|
|
38017
38219
|
}
|
|
38018
|
-
|
|
38220
|
+
this.retryAfterByTarget.delete(targetKey);
|
|
38019
38221
|
} catch (e) {
|
|
38020
38222
|
summary.failedTargetCount += 1;
|
|
38021
|
-
|
|
38223
|
+
if (e instanceof LogUploadHttpError && e.retryAfterMs !== void 0) {
|
|
38224
|
+
this.retryAfterByTarget.set(targetKey, Date.now() + e.retryAfterMs);
|
|
38225
|
+
logger28.warn("Bridge log upload target failed", {
|
|
38226
|
+
error: e,
|
|
38227
|
+
logFile: target.logFile,
|
|
38228
|
+
status: e.status,
|
|
38229
|
+
quotaScope: e.scope,
|
|
38230
|
+
retryAfterMs: e.retryAfterMs
|
|
38231
|
+
});
|
|
38232
|
+
} else {
|
|
38233
|
+
logger28.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
|
|
38234
|
+
}
|
|
38022
38235
|
}
|
|
38023
38236
|
}
|
|
38024
38237
|
} catch (e) {
|
|
38025
38238
|
logger28.warn("Bridge log upload cycle failed", { error: e });
|
|
38026
38239
|
} finally {
|
|
38027
|
-
logger28.
|
|
38240
|
+
logger28.debug("Bridge log upload cycle summary", {
|
|
38028
38241
|
...summary,
|
|
38029
38242
|
durationMs: Date.now() - startedAt
|
|
38030
38243
|
});
|
|
38031
38244
|
this.running = false;
|
|
38032
38245
|
}
|
|
38033
38246
|
}
|
|
38247
|
+
targetKey(target) {
|
|
38248
|
+
return `${target.logFile}\0${target.cursorFile}`;
|
|
38249
|
+
}
|
|
38034
38250
|
async resolveTargets() {
|
|
38035
38251
|
const bridgeTargets = this.options.includeRotatedBridgeLogs ? await listRotatedBridgeTargets(this.options.primaryTarget, this.options.dataDir) : [this.options.primaryTarget];
|
|
38036
38252
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -38063,15 +38279,40 @@ var BridgeLogUploader = class {
|
|
|
38063
38279
|
const raw = await readStreamText(target.logFile, normalizedCursor.offset);
|
|
38064
38280
|
return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
|
|
38065
38281
|
}
|
|
38066
|
-
|
|
38067
|
-
|
|
38282
|
+
uploadBodyBytes(entries) {
|
|
38283
|
+
return Buffer.byteLength(JSON.stringify({
|
|
38284
|
+
bridgeId: this.options.bridgeId,
|
|
38285
|
+
hostname: this.options.hostname,
|
|
38286
|
+
entries
|
|
38287
|
+
}), "utf8");
|
|
38288
|
+
}
|
|
38289
|
+
chunkUploadEntries(entries) {
|
|
38290
|
+
const chunks = [];
|
|
38291
|
+
let current = [];
|
|
38292
|
+
for (const entry of entries) {
|
|
38293
|
+
const next = [...current, entry];
|
|
38294
|
+
const nextBodyBytes = this.uploadBodyBytes(next.map((item) => item.entry));
|
|
38295
|
+
if (current.length > 0 && (current.length >= this.options.batchSize || nextBodyBytes > MAX_UPLOAD_CHUNK_BODY_BYTES)) {
|
|
38296
|
+
chunks.push(current);
|
|
38297
|
+
current = [entry];
|
|
38298
|
+
} else {
|
|
38299
|
+
current = next;
|
|
38300
|
+
}
|
|
38301
|
+
}
|
|
38302
|
+
if (current.length > 0) chunks.push(current);
|
|
38303
|
+
return chunks;
|
|
38304
|
+
}
|
|
38305
|
+
async uploadEntries(entries, cursorFile, finalCursor) {
|
|
38306
|
+
const bridgeEntries = entries.filter((item) => item.entry.source === "bridge");
|
|
38068
38307
|
const result = {
|
|
38069
38308
|
bridgeEntryCount: bridgeEntries.length,
|
|
38070
38309
|
uploadedChunkCount: 0,
|
|
38071
38310
|
accepted: 0,
|
|
38072
|
-
skipped: 0
|
|
38311
|
+
skipped: 0,
|
|
38312
|
+
dropped: 0
|
|
38073
38313
|
};
|
|
38074
|
-
for (const chunk of
|
|
38314
|
+
for (const chunk of this.chunkUploadEntries(bridgeEntries)) {
|
|
38315
|
+
const payloadEntries = chunk.map((item) => item.entry);
|
|
38075
38316
|
const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
|
|
38076
38317
|
method: "POST",
|
|
38077
38318
|
headers: {
|
|
@@ -38081,7 +38322,7 @@ var BridgeLogUploader = class {
|
|
|
38081
38322
|
body: JSON.stringify({
|
|
38082
38323
|
bridgeId: this.options.bridgeId,
|
|
38083
38324
|
hostname: this.options.hostname,
|
|
38084
|
-
entries:
|
|
38325
|
+
entries: payloadEntries
|
|
38085
38326
|
})
|
|
38086
38327
|
});
|
|
38087
38328
|
if (!res.ok) {
|
|
@@ -38089,13 +38330,27 @@ var BridgeLogUploader = class {
|
|
|
38089
38330
|
logger28.debug("Failed to read log upload error body", { error: e });
|
|
38090
38331
|
return "";
|
|
38091
38332
|
});
|
|
38092
|
-
|
|
38333
|
+
const staleChunk = res.status === 429 ? isStaleRateLimitedChunk(payloadEntries, Date.now()) : null;
|
|
38334
|
+
if (staleChunk) {
|
|
38335
|
+
result.dropped += chunk.length;
|
|
38336
|
+
logger28.warn("Skipping stale bridge log upload chunk after rate limit", {
|
|
38337
|
+
status: res.status,
|
|
38338
|
+
entryCount: chunk.length,
|
|
38339
|
+
minEntryTs: staleChunk.minTs,
|
|
38340
|
+
maxEntryTs: staleChunk.maxTs
|
|
38341
|
+
});
|
|
38342
|
+
continue;
|
|
38343
|
+
}
|
|
38344
|
+
throw new LogUploadHttpError(res.status, body2);
|
|
38093
38345
|
}
|
|
38094
38346
|
const body = await res.json();
|
|
38095
38347
|
result.uploadedChunkCount += 1;
|
|
38096
38348
|
result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
|
|
38097
38349
|
result.skipped += typeof body.skipped === "number" ? body.skipped : 0;
|
|
38350
|
+
const last = chunk[chunk.length - 1];
|
|
38351
|
+
if (last) await writeCursor(cursorFile, last.cursorAfter);
|
|
38098
38352
|
}
|
|
38353
|
+
await writeCursor(cursorFile, finalCursor);
|
|
38099
38354
|
return result;
|
|
38100
38355
|
}
|
|
38101
38356
|
};
|
|
@@ -38178,7 +38433,7 @@ var SkillStore = class {
|
|
|
38178
38433
|
if (!isNotFoundError(e)) logger29.warn("Skill seed existing read failed", { name, filePath, error: e });
|
|
38179
38434
|
}
|
|
38180
38435
|
if (existing === content) {
|
|
38181
|
-
logger29.
|
|
38436
|
+
logger29.debug("Skill already in sync", { name, bytes: content.length });
|
|
38182
38437
|
return;
|
|
38183
38438
|
}
|
|
38184
38439
|
fs15.writeFileSync(tmpPath, content, "utf-8");
|