@fangyb/ahchat-bridge 0.1.40 → 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 +340 -107
- package/dist/feedbackWorkerCli.cjs +30 -0
- package/dist/index.js +340 -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)",
|
|
@@ -28916,6 +28964,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
28916
28964
|
input: {}
|
|
28917
28965
|
}
|
|
28918
28966
|
});
|
|
28967
|
+
}
|
|
28968
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
28919
28969
|
proc.contentBlocks.push({
|
|
28920
28970
|
type: "tool_use",
|
|
28921
28971
|
...toolUseId ? { toolUseId } : {},
|
|
@@ -29256,6 +29306,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29256
29306
|
}
|
|
29257
29307
|
}
|
|
29258
29308
|
}
|
|
29309
|
+
recordWorkdirSignal(proc, toolName, isError);
|
|
29259
29310
|
proc.activeToolUseStartedAt = void 0;
|
|
29260
29311
|
proc.currentToolUseId = null;
|
|
29261
29312
|
}
|
|
@@ -29317,7 +29368,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29317
29368
|
if (isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: groupMode })) {
|
|
29318
29369
|
checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
|
|
29319
29370
|
emitUsageReported(proc, emit, base, usage);
|
|
29320
|
-
if (groupMode && proc.contentBlocks.length > 0) {
|
|
29371
|
+
if (groupMode && (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0)) {
|
|
29321
29372
|
emitGroupSegment(
|
|
29322
29373
|
proc,
|
|
29323
29374
|
emit,
|
|
@@ -29362,11 +29413,12 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29362
29413
|
proc.segmentBuffer = proc.accumulatedText;
|
|
29363
29414
|
flushTextSegmentOnBlockStop(proc, emit, base);
|
|
29364
29415
|
}
|
|
29365
|
-
if (proc.contentBlocks.length > 0) {
|
|
29416
|
+
if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
|
|
29366
29417
|
logger11.info("Group turn trailing audit segment", {
|
|
29367
29418
|
agentId: proc.agentId,
|
|
29368
29419
|
replyMessageId: base.replyMessageId,
|
|
29369
29420
|
blockCount: proc.contentBlocks.length,
|
|
29421
|
+
workdirSignalCount: proc.workdirSignals?.length ?? 0,
|
|
29370
29422
|
traceId: base.traceId
|
|
29371
29423
|
});
|
|
29372
29424
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
|
|
@@ -29511,6 +29563,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29511
29563
|
payload: { ...wireBase(base), error: errorText }
|
|
29512
29564
|
});
|
|
29513
29565
|
proc.contentBlocks = [];
|
|
29566
|
+
proc.workdirSignals = [];
|
|
29514
29567
|
proc.accumulatedText = "";
|
|
29515
29568
|
proc.accumulatedThinking = "";
|
|
29516
29569
|
proc.segmentBuffer = "";
|
|
@@ -29543,6 +29596,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29543
29596
|
proc.apiErrorEmitted = true;
|
|
29544
29597
|
}
|
|
29545
29598
|
proc.contentBlocks = [];
|
|
29599
|
+
proc.workdirSignals = [];
|
|
29546
29600
|
proc.accumulatedText = "";
|
|
29547
29601
|
proc.accumulatedThinking = "";
|
|
29548
29602
|
proc.segmentBuffer = "";
|
|
@@ -29569,6 +29623,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29569
29623
|
proc.apiErrorEmitted = true;
|
|
29570
29624
|
}
|
|
29571
29625
|
proc.contentBlocks = [];
|
|
29626
|
+
proc.workdirSignals = [];
|
|
29572
29627
|
proc.accumulatedText = "";
|
|
29573
29628
|
proc.accumulatedThinking = "";
|
|
29574
29629
|
proc.segmentBuffer = "";
|
|
@@ -29605,6 +29660,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
29605
29660
|
});
|
|
29606
29661
|
}
|
|
29607
29662
|
proc.contentBlocks = [];
|
|
29663
|
+
proc.workdirSignals = [];
|
|
29608
29664
|
proc.accumulatedText = "";
|
|
29609
29665
|
proc.accumulatedThinking = "";
|
|
29610
29666
|
proc.segmentBuffer = "";
|
|
@@ -29646,6 +29702,7 @@ function resetAccumulators(proc) {
|
|
|
29646
29702
|
proc.activeToolUseStartedAt = void 0;
|
|
29647
29703
|
proc.segmentBuffer = "";
|
|
29648
29704
|
proc.segmentCount = 0;
|
|
29705
|
+
proc.workdirSignals = [];
|
|
29649
29706
|
proc.accumulatedToolInput = "";
|
|
29650
29707
|
proc.apiErrorEmitted = false;
|
|
29651
29708
|
proc.peakContextUsage = void 0;
|
|
@@ -29841,7 +29898,7 @@ function missingSubscriptionMessage(subscriptionId) {
|
|
|
29841
29898
|
}
|
|
29842
29899
|
var NODE_USER_UID = 1e3;
|
|
29843
29900
|
var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
|
|
29844
|
-
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-
|
|
29901
|
+
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v6";
|
|
29845
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;
|
|
29846
29903
|
var IMAGE_READ_EXT_RE = /\.(?:bmp|gif|jpeg|jpg|png|webp)$/i;
|
|
29847
29904
|
var DOCUMENT_READING_RULES = `DOCUMENT READING:
|
|
@@ -29857,6 +29914,31 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
|
|
|
29857
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.
|
|
29858
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.
|
|
29859
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
|
+
];
|
|
29860
29942
|
function resolveVisionMcpToolHints(externalMcp) {
|
|
29861
29943
|
let describe3 = null;
|
|
29862
29944
|
let ocr = null;
|
|
@@ -31121,7 +31203,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31121
31203
|
} catch (error51) {
|
|
31122
31204
|
logger14.error("Failed to read existing API-key agent settings; starting fresh", {
|
|
31123
31205
|
agentId: agentConfig.id,
|
|
31124
|
-
|
|
31206
|
+
settingsFile: "settings.json",
|
|
31125
31207
|
error: error51
|
|
31126
31208
|
});
|
|
31127
31209
|
}
|
|
@@ -31134,7 +31216,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31134
31216
|
await fs6.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
|
|
31135
31217
|
logger14.info("API-key agent using isolated config dir", {
|
|
31136
31218
|
agentId: agentConfig.id,
|
|
31137
|
-
|
|
31219
|
+
configDirKind: "api-key-agent",
|
|
31138
31220
|
isNew,
|
|
31139
31221
|
hasBaseUrl: !!cfg.apiBaseUrl,
|
|
31140
31222
|
settingsWritten: Object.keys(envEntries)
|
|
@@ -31228,12 +31310,21 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31228
31310
|
});
|
|
31229
31311
|
}
|
|
31230
31312
|
}
|
|
31231
|
-
const
|
|
31313
|
+
const resolvedExternalMcp = this.mcpRegistry?.buildForAgent({
|
|
31232
31314
|
agentId: agentConfig.id,
|
|
31233
31315
|
capabilityTier: cfg.capabilityTier,
|
|
31234
31316
|
isSmith: smithAgent,
|
|
31235
31317
|
cwd: agentCwd
|
|
31236
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
|
+
}
|
|
31237
31328
|
const visionMcpTools = resolveVisionMcpToolHints(externalMcp);
|
|
31238
31329
|
logger14.info("External MCP resolved for runtime", {
|
|
31239
31330
|
agentId: agentConfig.id,
|
|
@@ -31306,6 +31397,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31306
31397
|
logger14.info("Creating Agent query", {
|
|
31307
31398
|
agentId: agentConfig.id,
|
|
31308
31399
|
scope: scopeKey(scope),
|
|
31400
|
+
systemPromptMode: smithAgent ? "string" : "preset",
|
|
31309
31401
|
cwd: agentCwd,
|
|
31310
31402
|
resume: !!savedSessionId,
|
|
31311
31403
|
sessionId: savedSessionId,
|
|
@@ -31326,71 +31418,63 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31326
31418
|
});
|
|
31327
31419
|
const planModeRef = { active: false, denyCount: 0 };
|
|
31328
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
|
+
];
|
|
31329
31468
|
const options = {
|
|
31330
31469
|
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
|
-
},
|
|
31470
|
+
systemPrompt,
|
|
31346
31471
|
permissionMode: "bypassPermissions",
|
|
31347
31472
|
allowDangerouslySkipPermissions: true,
|
|
31348
31473
|
// allowedTools is the visibility whitelist passed to Claude Code. MCP tools
|
|
31349
31474
|
// with always_ask must still be included here so the model can request them;
|
|
31350
31475
|
// the MCP server policy/permission layer decides whether execution asks.
|
|
31351
31476
|
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
|
-
] : [],
|
|
31477
|
+
...smithAgent ? SMITH_ALLOWED_TOOLS : universalAllowedTools,
|
|
31394
31478
|
...externalMcp.allowedTools
|
|
31395
31479
|
],
|
|
31396
31480
|
// Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
|
|
@@ -31402,7 +31486,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31402
31486
|
// instructions as the workflow body (replacing the default code-implementation
|
|
31403
31487
|
// phases). The SDK wraps it with read-only enforcement + ExitPlanMode protocol.
|
|
31404
31488
|
planModeInstructions: (() => {
|
|
31405
|
-
const planTools = [
|
|
31489
|
+
const planTools = (smithAgent ? ["AskUserQuestion", "Write (plan file only)"] : [
|
|
31406
31490
|
...nativeReadToolDisabled ? [] : ["Read"],
|
|
31407
31491
|
"Glob",
|
|
31408
31492
|
"Grep",
|
|
@@ -31410,19 +31494,19 @@ ${cfg.instructions.trim()}` : "";
|
|
|
31410
31494
|
"WebFetch",
|
|
31411
31495
|
"AskUserQuestion",
|
|
31412
31496
|
"Write (plan file only)"
|
|
31413
|
-
].join(", ");
|
|
31414
|
-
const researchTools = [
|
|
31497
|
+
]).join(", ");
|
|
31498
|
+
const researchTools = (smithAgent ? ["AskUserQuestion"] : [
|
|
31415
31499
|
...nativeReadToolDisabled ? [] : ["Read"],
|
|
31416
31500
|
"Grep",
|
|
31417
31501
|
...builtinWebSearchAllowed ? ["WebSearch"] : []
|
|
31418
|
-
].join(", ");
|
|
31419
|
-
const unavailableTools = [
|
|
31502
|
+
]).join(", ");
|
|
31503
|
+
const unavailableTools = (smithAgent ? ["Read", "Edit", "Bash", "Glob", "Grep", "WebFetch", "TodoWrite", "ExitPlanMode"] : [
|
|
31420
31504
|
"Edit",
|
|
31421
31505
|
"Bash",
|
|
31422
31506
|
"TodoWrite",
|
|
31423
31507
|
"ExitPlanMode",
|
|
31424
31508
|
...nativeReadToolDisabled ? ["Read"] : []
|
|
31425
|
-
].join(", ");
|
|
31509
|
+
]).join(", ");
|
|
31426
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." : "";
|
|
31427
31511
|
return `You are a PLANNER, NOT an executor. The user will execute your plan later.
|
|
31428
31512
|
|
|
@@ -31652,17 +31736,17 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31652
31736
|
}
|
|
31653
31737
|
};
|
|
31654
31738
|
const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
|
|
31655
|
-
const appendStr = options.systemPrompt.append;
|
|
31656
31739
|
logger14.info("Platform rules attached", {
|
|
31657
31740
|
agentId: agentConfig.id,
|
|
31658
31741
|
scope: scopeKey(scope),
|
|
31659
|
-
|
|
31742
|
+
systemPromptMode: smithAgent ? "string" : "preset",
|
|
31743
|
+
platformRulesLen: platformRules.length,
|
|
31660
31744
|
userPromptLen: userPromptTrimmed.length,
|
|
31661
31745
|
hasUserPrompt: userPromptTrimmed.length > 0,
|
|
31662
31746
|
notebookLen: notebookSection.length,
|
|
31663
31747
|
forkHistoryLen: forkHistorySection.length,
|
|
31664
31748
|
scopesLen: scopesSection.length,
|
|
31665
|
-
appendLen:
|
|
31749
|
+
appendLen: appendText.length,
|
|
31666
31750
|
hasCreateAgentTool: smithAgent,
|
|
31667
31751
|
hasLogDetectiveTools: smithAgent && this.skillStore !== null
|
|
31668
31752
|
});
|
|
@@ -31732,6 +31816,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31732
31816
|
mcpAuditRecorder: this.mcpAuditRecorder,
|
|
31733
31817
|
segmentBuffer: "",
|
|
31734
31818
|
segmentCount: 0,
|
|
31819
|
+
workdirSignals: [],
|
|
31735
31820
|
accumulatedToolInput: "",
|
|
31736
31821
|
planModeRef,
|
|
31737
31822
|
mediaGenerationTurnGuard,
|
|
@@ -32765,6 +32850,7 @@ ${lines.join("\n")}`;
|
|
|
32765
32850
|
proc.currentToolName = null;
|
|
32766
32851
|
proc.segmentBuffer = "";
|
|
32767
32852
|
proc.segmentCount = 0;
|
|
32853
|
+
proc.workdirSignals = [];
|
|
32768
32854
|
}
|
|
32769
32855
|
clearPostMergeContinuationTimer(runtime) {
|
|
32770
32856
|
if (runtime.postMergeContinuationTimer) {
|
|
@@ -35535,6 +35621,7 @@ var HttpMcpRegistry = class {
|
|
|
35535
35621
|
localStore;
|
|
35536
35622
|
serverConnections = /* @__PURE__ */ new Map();
|
|
35537
35623
|
localConnections = /* @__PURE__ */ new Map();
|
|
35624
|
+
missingSecretWarnedKeys = /* @__PURE__ */ new Set();
|
|
35538
35625
|
lastRefreshCount = null;
|
|
35539
35626
|
apiUrl(suffix) {
|
|
35540
35627
|
const base = this.serverApiUrl.replace(/\/$/, "");
|
|
@@ -35635,13 +35722,19 @@ var HttpMcpRegistry = class {
|
|
|
35635
35722
|
return false;
|
|
35636
35723
|
});
|
|
35637
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
|
+
}
|
|
35638
35735
|
toSdkServerConfig(connection, ctx) {
|
|
35639
35736
|
if (mcpConnectionRequiresSecret(connection) && !connection.hasAuthSecret) {
|
|
35640
|
-
|
|
35641
|
-
id: connection.id,
|
|
35642
|
-
providerId: connection.providerId,
|
|
35643
|
-
serverName: connection.serverName
|
|
35644
|
-
});
|
|
35737
|
+
this.warnMissingSecretOnce(connection);
|
|
35645
35738
|
return null;
|
|
35646
35739
|
}
|
|
35647
35740
|
const policies = toolPolicies(connection);
|
|
@@ -37828,8 +37921,47 @@ import fs14 from "fs";
|
|
|
37828
37921
|
import fsp from "fs/promises";
|
|
37829
37922
|
import path21 from "path";
|
|
37830
37923
|
var logger28 = createModuleLogger("bridge.logUploader");
|
|
37924
|
+
var STALE_RATE_LIMIT_SKIP_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
37831
37925
|
var DEFAULT_LOG_UPLOAD_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
37832
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
|
+
}
|
|
37833
37965
|
function defaultLogFile(dataDir) {
|
|
37834
37966
|
return path21.join(dataDir, "logs", "bridge.log");
|
|
37835
37967
|
}
|
|
@@ -37919,26 +38051,38 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
37919
38051
|
return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
|
|
37920
38052
|
}
|
|
37921
38053
|
const processed = raw.slice(0, lastNewline + 1);
|
|
37922
|
-
const
|
|
38054
|
+
const linePattern = /.*?\n/g;
|
|
37923
38055
|
const entries = [];
|
|
37924
38056
|
let lineNum = cursor.lineNum;
|
|
37925
|
-
|
|
37926
|
-
|
|
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);
|
|
37927
38062
|
lineNum += 1;
|
|
38063
|
+
offset += Buffer.byteLength(rawLine, "utf8");
|
|
38064
|
+
const cursorAfter = {
|
|
38065
|
+
offset,
|
|
38066
|
+
lineNum,
|
|
38067
|
+
...fingerprint ? { fingerprint } : {}
|
|
38068
|
+
};
|
|
37928
38069
|
if (line.length === 0) continue;
|
|
37929
38070
|
const parsed = parseLogLine(line);
|
|
37930
38071
|
if (!parsed) continue;
|
|
37931
38072
|
entries.push({
|
|
37932
|
-
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
38073
|
+
entry: {
|
|
38074
|
+
...parsed,
|
|
38075
|
+
raw: line,
|
|
38076
|
+
file: fileName,
|
|
38077
|
+
lineNum
|
|
38078
|
+
},
|
|
38079
|
+
cursorAfter
|
|
37936
38080
|
});
|
|
37937
38081
|
}
|
|
37938
38082
|
return {
|
|
37939
38083
|
entries,
|
|
37940
38084
|
nextCursor: {
|
|
37941
|
-
offset
|
|
38085
|
+
offset,
|
|
37942
38086
|
lineNum,
|
|
37943
38087
|
...fingerprint ? { fingerprint } : {}
|
|
37944
38088
|
},
|
|
@@ -37946,15 +38090,35 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
37946
38090
|
reason: "advanced"
|
|
37947
38091
|
};
|
|
37948
38092
|
}
|
|
37949
|
-
function
|
|
37950
|
-
|
|
37951
|
-
|
|
37952
|
-
|
|
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
|
+
}
|
|
37953
38109
|
}
|
|
37954
|
-
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 };
|
|
37955
38118
|
}
|
|
37956
38119
|
var BridgeLogUploader = class {
|
|
37957
38120
|
options;
|
|
38121
|
+
retryAfterByTarget = /* @__PURE__ */ new Map();
|
|
37958
38122
|
timer = null;
|
|
37959
38123
|
running = false;
|
|
37960
38124
|
stopped = false;
|
|
@@ -37971,7 +38135,7 @@ var BridgeLogUploader = class {
|
|
|
37971
38135
|
bridgeId: options.bridgeId,
|
|
37972
38136
|
hostname: options.hostname,
|
|
37973
38137
|
intervalMs: options.intervalMs ?? DEFAULT_LOG_UPLOAD_INTERVAL_MS,
|
|
37974
|
-
batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
38138
|
+
batchSize: Math.max(1, Math.floor(options.batchSize ?? DEFAULT_BATCH_SIZE)),
|
|
37975
38139
|
primaryTarget,
|
|
37976
38140
|
extraTargets: (options.extraLogFiles ?? []).map((file2) => normalizeTarget(file2, options.dataDir)),
|
|
37977
38141
|
includeRotatedBridgeLogs: options.includeRotatedBridgeLogs ?? true
|
|
@@ -38013,13 +38177,26 @@ var BridgeLogUploader = class {
|
|
|
38013
38177
|
bridgeEntryCount: 0,
|
|
38014
38178
|
uploadedChunkCount: 0,
|
|
38015
38179
|
accepted: 0,
|
|
38016
|
-
skipped: 0
|
|
38180
|
+
skipped: 0,
|
|
38181
|
+
dropped: 0
|
|
38017
38182
|
};
|
|
38018
38183
|
try {
|
|
38019
38184
|
const targets = await this.resolveTargets();
|
|
38020
38185
|
summary.targetCount = targets.length;
|
|
38021
38186
|
for (const target of targets) {
|
|
38187
|
+
const targetKey = this.targetKey(target);
|
|
38022
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);
|
|
38023
38200
|
const cursor = await readCursor(target.cursorFile);
|
|
38024
38201
|
const batch = await this.readNewEntries(target, cursor);
|
|
38025
38202
|
if (!batch.advanced) {
|
|
@@ -38031,28 +38208,45 @@ var BridgeLogUploader = class {
|
|
|
38031
38208
|
summary.advancedTargetCount += 1;
|
|
38032
38209
|
summary.parsedEntryCount += batch.entries.length;
|
|
38033
38210
|
if (batch.entries.length > 0) {
|
|
38034
|
-
const result = await this.uploadEntries(batch.entries);
|
|
38211
|
+
const result = await this.uploadEntries(batch.entries, target.cursorFile, batch.nextCursor);
|
|
38035
38212
|
summary.bridgeEntryCount += result.bridgeEntryCount;
|
|
38036
38213
|
summary.uploadedChunkCount += result.uploadedChunkCount;
|
|
38037
38214
|
summary.accepted += result.accepted;
|
|
38038
38215
|
summary.skipped += result.skipped;
|
|
38216
|
+
summary.dropped += result.dropped;
|
|
38217
|
+
} else {
|
|
38218
|
+
await writeCursor(target.cursorFile, batch.nextCursor);
|
|
38039
38219
|
}
|
|
38040
|
-
|
|
38220
|
+
this.retryAfterByTarget.delete(targetKey);
|
|
38041
38221
|
} catch (e) {
|
|
38042
38222
|
summary.failedTargetCount += 1;
|
|
38043
|
-
|
|
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
|
+
}
|
|
38044
38235
|
}
|
|
38045
38236
|
}
|
|
38046
38237
|
} catch (e) {
|
|
38047
38238
|
logger28.warn("Bridge log upload cycle failed", { error: e });
|
|
38048
38239
|
} finally {
|
|
38049
|
-
logger28.
|
|
38240
|
+
logger28.debug("Bridge log upload cycle summary", {
|
|
38050
38241
|
...summary,
|
|
38051
38242
|
durationMs: Date.now() - startedAt
|
|
38052
38243
|
});
|
|
38053
38244
|
this.running = false;
|
|
38054
38245
|
}
|
|
38055
38246
|
}
|
|
38247
|
+
targetKey(target) {
|
|
38248
|
+
return `${target.logFile}\0${target.cursorFile}`;
|
|
38249
|
+
}
|
|
38056
38250
|
async resolveTargets() {
|
|
38057
38251
|
const bridgeTargets = this.options.includeRotatedBridgeLogs ? await listRotatedBridgeTargets(this.options.primaryTarget, this.options.dataDir) : [this.options.primaryTarget];
|
|
38058
38252
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -38085,15 +38279,40 @@ var BridgeLogUploader = class {
|
|
|
38085
38279
|
const raw = await readStreamText(target.logFile, normalizedCursor.offset);
|
|
38086
38280
|
return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
|
|
38087
38281
|
}
|
|
38088
|
-
|
|
38089
|
-
|
|
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");
|
|
38090
38307
|
const result = {
|
|
38091
38308
|
bridgeEntryCount: bridgeEntries.length,
|
|
38092
38309
|
uploadedChunkCount: 0,
|
|
38093
38310
|
accepted: 0,
|
|
38094
|
-
skipped: 0
|
|
38311
|
+
skipped: 0,
|
|
38312
|
+
dropped: 0
|
|
38095
38313
|
};
|
|
38096
|
-
for (const chunk of
|
|
38314
|
+
for (const chunk of this.chunkUploadEntries(bridgeEntries)) {
|
|
38315
|
+
const payloadEntries = chunk.map((item) => item.entry);
|
|
38097
38316
|
const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
|
|
38098
38317
|
method: "POST",
|
|
38099
38318
|
headers: {
|
|
@@ -38103,7 +38322,7 @@ var BridgeLogUploader = class {
|
|
|
38103
38322
|
body: JSON.stringify({
|
|
38104
38323
|
bridgeId: this.options.bridgeId,
|
|
38105
38324
|
hostname: this.options.hostname,
|
|
38106
|
-
entries:
|
|
38325
|
+
entries: payloadEntries
|
|
38107
38326
|
})
|
|
38108
38327
|
});
|
|
38109
38328
|
if (!res.ok) {
|
|
@@ -38111,13 +38330,27 @@ var BridgeLogUploader = class {
|
|
|
38111
38330
|
logger28.debug("Failed to read log upload error body", { error: e });
|
|
38112
38331
|
return "";
|
|
38113
38332
|
});
|
|
38114
|
-
|
|
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);
|
|
38115
38345
|
}
|
|
38116
38346
|
const body = await res.json();
|
|
38117
38347
|
result.uploadedChunkCount += 1;
|
|
38118
38348
|
result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
|
|
38119
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);
|
|
38120
38352
|
}
|
|
38353
|
+
await writeCursor(cursorFile, finalCursor);
|
|
38121
38354
|
return result;
|
|
38122
38355
|
}
|
|
38123
38356
|
};
|
|
@@ -38200,7 +38433,7 @@ var SkillStore = class {
|
|
|
38200
38433
|
if (!isNotFoundError(e)) logger29.warn("Skill seed existing read failed", { name, filePath, error: e });
|
|
38201
38434
|
}
|
|
38202
38435
|
if (existing === content) {
|
|
38203
|
-
logger29.
|
|
38436
|
+
logger29.debug("Skill already in sync", { name, bytes: content.length });
|
|
38204
38437
|
return;
|
|
38205
38438
|
}
|
|
38206
38439
|
fs15.writeFileSync(tmpPath, content, "utf-8");
|