@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/cli.cjs
CHANGED
|
@@ -94678,6 +94678,25 @@ Transform tasks into verifiable goals:
|
|
|
94678
94678
|
- "Refactor X" \u2192 ensure tests pass before and after.
|
|
94679
94679
|
For multi-step tasks, state a brief plan with verification checks.
|
|
94680
94680
|
`.trim();
|
|
94681
|
+
var GROUP_ONLY_SECTION_HEADERS = [
|
|
94682
|
+
"# \u7FA4\u804A\u516C\u7406\uFF08Group Chat Axiom \u2014 \u6700\u9AD8\u4F18\u5148\u7EA7\uFF09",
|
|
94683
|
+
"# Runtime payload \u2014 how to read unread messages",
|
|
94684
|
+
"# Group chat \u2014 when to speak",
|
|
94685
|
+
"# Group chat \u2014 recency & commitment",
|
|
94686
|
+
"# Length & conciseness in group chat",
|
|
94687
|
+
"# Group chat \u2014 shared task board",
|
|
94688
|
+
"# Group chat \u2014 batched inbox handling",
|
|
94689
|
+
"# \u7FA4\u804A\u4EA7\u7269\u7EAA\u5F8B\uFF08What to capture in group chats\uFF09",
|
|
94690
|
+
"# When a user joins or leaves your group"
|
|
94691
|
+
];
|
|
94692
|
+
function stripGroupOnlySections(full, headers) {
|
|
94693
|
+
const headerSet = new Set(headers);
|
|
94694
|
+
return full.split(/\n(?=# )/).filter((section) => !headerSet.has(section.split("\n", 1)[0].trim())).join("\n").trim();
|
|
94695
|
+
}
|
|
94696
|
+
var PLATFORM_AGENT_RULES_SINGLE = stripGroupOnlySections(
|
|
94697
|
+
PLATFORM_AGENT_RULES,
|
|
94698
|
+
GROUP_ONLY_SECTION_HEADERS
|
|
94699
|
+
);
|
|
94681
94700
|
var FAN_OUT_TRACE_TTL_MS = 10 * 6e4;
|
|
94682
94701
|
var MAX_FILE_SIZE = 20 * 1024 * 1024;
|
|
94683
94702
|
var MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|
@@ -94849,6 +94868,14 @@ function assertArrayPayloadField(type, payload, field) {
|
|
|
94849
94868
|
throw invalidWsMessage(type, field);
|
|
94850
94869
|
}
|
|
94851
94870
|
}
|
|
94871
|
+
function assertWorkdirSignalsPayloadField(type, payload, field) {
|
|
94872
|
+
assertArrayPayloadField(type, payload, field);
|
|
94873
|
+
for (const [index, item] of payload[field].entries()) {
|
|
94874
|
+
if (!isPlainRecord(item) || typeof item.toolName !== "string") {
|
|
94875
|
+
throw invalidWsMessage(type, `${field}[${index}].toolName`);
|
|
94876
|
+
}
|
|
94877
|
+
}
|
|
94878
|
+
}
|
|
94852
94879
|
function assertRecordPayloadField(type, payload, field) {
|
|
94853
94880
|
if (!isPlainRecord(payload[field])) {
|
|
94854
94881
|
throw invalidWsMessage(type, field);
|
|
@@ -94929,6 +94956,9 @@ function validateWSMessageShape(msg) {
|
|
|
94929
94956
|
"traceId"
|
|
94930
94957
|
]);
|
|
94931
94958
|
assertArrayPayloadField(type, payload, "contentBlocks");
|
|
94959
|
+
if ("workdirSignals" in payload && payload.workdirSignals !== void 0) {
|
|
94960
|
+
assertWorkdirSignalsPayloadField(type, payload, "workdirSignals");
|
|
94961
|
+
}
|
|
94932
94962
|
return;
|
|
94933
94963
|
}
|
|
94934
94964
|
case "agent:turn_complete": {
|
|
@@ -115629,7 +115659,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
|
|
|
115629
115659
|
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"),
|
|
115630
115660
|
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"),
|
|
115631
115661
|
avatar: external_exports.string().optional().describe(
|
|
115632
|
-
"\u5934\u50CF key\
|
|
115662
|
+
"\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"
|
|
115633
115663
|
),
|
|
115634
115664
|
initial_instruction: external_exports.string().optional().describe(
|
|
115635
115665
|
'\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'
|
|
@@ -115972,7 +116002,7 @@ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=
|
|
|
115972
116002
|
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"),
|
|
115973
116003
|
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"),
|
|
115974
116004
|
avatar: external_exports.string().optional().describe(
|
|
115975
|
-
"\u5934\u50CF key\uFF08\u5982 avatar_dev / avatar_pm /
|
|
116005
|
+
"\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"
|
|
115976
116006
|
),
|
|
115977
116007
|
machine_bridge_key: external_exports.string().optional().describe(
|
|
115978
116008
|
'\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'
|
|
@@ -116974,9 +117004,11 @@ function buildGroupInboxPrompt(entries, opts = {}) {
|
|
|
116974
117004
|
// src/sdkEventMapper.ts
|
|
116975
117005
|
init_cjs_shims();
|
|
116976
117006
|
var logger12 = createModuleLogger("sdk.mapper");
|
|
117007
|
+
var DEBUG_ONLY_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set(["status", "thinking_tokens"]);
|
|
116977
117008
|
var HIGH_WATERMARK_INPUT_TOKENS = 12e4;
|
|
116978
117009
|
var WARN_THRESHOLD_INPUT_TOKENS = 1e5;
|
|
116979
117010
|
var LIVE_INPUT_PREVIEW_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
|
|
117011
|
+
var WORKDIR_MUTATION_TOOL_NAMES = /* @__PURE__ */ new Set(["write", "edit", "multiedit", "notebookedit", "bash"]);
|
|
116980
117012
|
var CONTEXT_OVERFLOW_LOCK_MS = 6e4;
|
|
116981
117013
|
function parseJsonRecord(text) {
|
|
116982
117014
|
try {
|
|
@@ -116998,6 +117030,17 @@ function isSuccessfulOfficialMediaOutput(toolName, output) {
|
|
|
116998
117030
|
if (!["succeeded", "success", "completed", "done"].includes(state)) return false;
|
|
116999
117031
|
return Array.isArray(parsed.images) && parsed.images.length > 0 || typeof parsed.video_url === "string" || typeof parsed.videoUrl === "string";
|
|
117000
117032
|
}
|
|
117033
|
+
function isWorkdirMutationToolName(toolName) {
|
|
117034
|
+
return WORKDIR_MUTATION_TOOL_NAMES.has(toolName.trim().toLowerCase());
|
|
117035
|
+
}
|
|
117036
|
+
function recordWorkdirSignal(proc, toolName, isError) {
|
|
117037
|
+
if (isError || !isGroupTask(proc) || !isWorkdirMutationToolName(toolName)) return;
|
|
117038
|
+
const signals = proc.workdirSignals ?? [];
|
|
117039
|
+
if (!signals.some((signal) => signal.toolName === toolName)) {
|
|
117040
|
+
signals.push({ toolName });
|
|
117041
|
+
}
|
|
117042
|
+
proc.workdirSignals = signals;
|
|
117043
|
+
}
|
|
117001
117044
|
function isContextOverflowText(text) {
|
|
117002
117045
|
const trimmed = text.trim();
|
|
117003
117046
|
return /^prompt is too long\b/i.test(trimmed) || /context length .* exceed/i.test(trimmed);
|
|
@@ -117502,6 +117545,7 @@ function countByStatus(todos) {
|
|
|
117502
117545
|
function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = false) {
|
|
117503
117546
|
const groupId = proc.currentTask?.groupId;
|
|
117504
117547
|
if (!groupId) return;
|
|
117548
|
+
const workdirSignals = proc.workdirSignals?.length ? [...proc.workdirSignals] : void 0;
|
|
117505
117549
|
proc.segmentCount += 1;
|
|
117506
117550
|
logger12.info("Group segment emitted", {
|
|
117507
117551
|
agentId: base.agentId,
|
|
@@ -117511,6 +117555,7 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
117511
117555
|
contentLen: content.length,
|
|
117512
117556
|
blockCount: contentBlocks.length,
|
|
117513
117557
|
blockTypes: contentBlocks.map((b) => b.type),
|
|
117558
|
+
workdirSignalCount: workdirSignals?.length ?? 0,
|
|
117514
117559
|
traceId: base.traceId,
|
|
117515
117560
|
isAuditOnly: content.length === 0,
|
|
117516
117561
|
isSilent
|
|
@@ -117523,9 +117568,11 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
117523
117568
|
groupId,
|
|
117524
117569
|
content,
|
|
117525
117570
|
contentBlocks: [...contentBlocks],
|
|
117571
|
+
...workdirSignals ? { workdirSignals } : {},
|
|
117526
117572
|
...isSilent ? { isSilent: true } : {}
|
|
117527
117573
|
}
|
|
117528
117574
|
});
|
|
117575
|
+
proc.workdirSignals = [];
|
|
117529
117576
|
}
|
|
117530
117577
|
function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
117531
117578
|
const trimmed = proc.segmentBuffer.trim();
|
|
@@ -117544,7 +117591,7 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
117544
117591
|
traceId: base.traceId
|
|
117545
117592
|
});
|
|
117546
117593
|
}
|
|
117547
|
-
} else if (proc.contentBlocks.length > 0) {
|
|
117594
|
+
} else if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
|
|
117548
117595
|
const blockCount = proc.contentBlocks.length;
|
|
117549
117596
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
|
|
117550
117597
|
proc.contentBlocks = [];
|
|
@@ -117634,7 +117681,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
117634
117681
|
proc.activeSubagentTaskIds?.delete(subagentTaskId);
|
|
117635
117682
|
}
|
|
117636
117683
|
}
|
|
117637
|
-
|
|
117684
|
+
const logUnhandledSystemSubtype = DEBUG_ONLY_SYSTEM_SUBTYPES.has(String(sysMsg.subtype ?? "")) ? logger12.debug.bind(logger12) : logger12.info.bind(logger12);
|
|
117685
|
+
logUnhandledSystemSubtype("SDK system subtype unhandled", {
|
|
117638
117686
|
agentId: proc.agentId,
|
|
117639
117687
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
117640
117688
|
subtype: sysMsg.subtype ?? "(none)",
|
|
@@ -117691,6 +117739,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
117691
117739
|
input: {}
|
|
117692
117740
|
}
|
|
117693
117741
|
});
|
|
117742
|
+
}
|
|
117743
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
117694
117744
|
proc.contentBlocks.push({
|
|
117695
117745
|
type: "tool_use",
|
|
117696
117746
|
...toolUseId ? { toolUseId } : {},
|
|
@@ -118031,6 +118081,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118031
118081
|
}
|
|
118032
118082
|
}
|
|
118033
118083
|
}
|
|
118084
|
+
recordWorkdirSignal(proc, toolName, isError);
|
|
118034
118085
|
proc.activeToolUseStartedAt = void 0;
|
|
118035
118086
|
proc.currentToolUseId = null;
|
|
118036
118087
|
}
|
|
@@ -118092,7 +118143,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118092
118143
|
if (isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: groupMode })) {
|
|
118093
118144
|
checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
|
|
118094
118145
|
emitUsageReported(proc, emit, base, usage);
|
|
118095
|
-
if (groupMode && proc.contentBlocks.length > 0) {
|
|
118146
|
+
if (groupMode && (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0)) {
|
|
118096
118147
|
emitGroupSegment(
|
|
118097
118148
|
proc,
|
|
118098
118149
|
emit,
|
|
@@ -118137,11 +118188,12 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118137
118188
|
proc.segmentBuffer = proc.accumulatedText;
|
|
118138
118189
|
flushTextSegmentOnBlockStop(proc, emit, base);
|
|
118139
118190
|
}
|
|
118140
|
-
if (proc.contentBlocks.length > 0) {
|
|
118191
|
+
if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
|
|
118141
118192
|
logger12.info("Group turn trailing audit segment", {
|
|
118142
118193
|
agentId: proc.agentId,
|
|
118143
118194
|
replyMessageId: base.replyMessageId,
|
|
118144
118195
|
blockCount: proc.contentBlocks.length,
|
|
118196
|
+
workdirSignalCount: proc.workdirSignals?.length ?? 0,
|
|
118145
118197
|
traceId: base.traceId
|
|
118146
118198
|
});
|
|
118147
118199
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
|
|
@@ -118286,6 +118338,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118286
118338
|
payload: { ...wireBase(base), error: errorText }
|
|
118287
118339
|
});
|
|
118288
118340
|
proc.contentBlocks = [];
|
|
118341
|
+
proc.workdirSignals = [];
|
|
118289
118342
|
proc.accumulatedText = "";
|
|
118290
118343
|
proc.accumulatedThinking = "";
|
|
118291
118344
|
proc.segmentBuffer = "";
|
|
@@ -118318,6 +118371,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118318
118371
|
proc.apiErrorEmitted = true;
|
|
118319
118372
|
}
|
|
118320
118373
|
proc.contentBlocks = [];
|
|
118374
|
+
proc.workdirSignals = [];
|
|
118321
118375
|
proc.accumulatedText = "";
|
|
118322
118376
|
proc.accumulatedThinking = "";
|
|
118323
118377
|
proc.segmentBuffer = "";
|
|
@@ -118344,6 +118398,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118344
118398
|
proc.apiErrorEmitted = true;
|
|
118345
118399
|
}
|
|
118346
118400
|
proc.contentBlocks = [];
|
|
118401
|
+
proc.workdirSignals = [];
|
|
118347
118402
|
proc.accumulatedText = "";
|
|
118348
118403
|
proc.accumulatedThinking = "";
|
|
118349
118404
|
proc.segmentBuffer = "";
|
|
@@ -118380,6 +118435,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
|
|
|
118380
118435
|
});
|
|
118381
118436
|
}
|
|
118382
118437
|
proc.contentBlocks = [];
|
|
118438
|
+
proc.workdirSignals = [];
|
|
118383
118439
|
proc.accumulatedText = "";
|
|
118384
118440
|
proc.accumulatedThinking = "";
|
|
118385
118441
|
proc.segmentBuffer = "";
|
|
@@ -118421,6 +118477,7 @@ function resetAccumulators(proc) {
|
|
|
118421
118477
|
proc.activeToolUseStartedAt = void 0;
|
|
118422
118478
|
proc.segmentBuffer = "";
|
|
118423
118479
|
proc.segmentCount = 0;
|
|
118480
|
+
proc.workdirSignals = [];
|
|
118424
118481
|
proc.accumulatedToolInput = "";
|
|
118425
118482
|
proc.apiErrorEmitted = false;
|
|
118426
118483
|
proc.peakContextUsage = void 0;
|
|
@@ -118620,7 +118677,7 @@ function missingSubscriptionMessage(subscriptionId) {
|
|
|
118620
118677
|
}
|
|
118621
118678
|
var NODE_USER_UID = 1e3;
|
|
118622
118679
|
var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
|
|
118623
|
-
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-
|
|
118680
|
+
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v6";
|
|
118624
118681
|
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;
|
|
118625
118682
|
var IMAGE_READ_EXT_RE = /\.(?:bmp|gif|jpeg|jpg|png|webp)$/i;
|
|
118626
118683
|
var DOCUMENT_READING_RULES = `DOCUMENT READING:
|
|
@@ -118636,6 +118693,31 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
|
|
|
118636
118693
|
- 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.
|
|
118637
118694
|
- 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.
|
|
118638
118695
|
- 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.`;
|
|
118696
|
+
var SMITH_ALLOWED_TOOLS = [
|
|
118697
|
+
// creation / configuration (Smith-only)
|
|
118698
|
+
"mcp__neural__create_agent",
|
|
118699
|
+
"mcp__neural__update_agent_profile",
|
|
118700
|
+
"mcp__neural__recommend_agent_skills",
|
|
118701
|
+
"mcp__neural__read_skill",
|
|
118702
|
+
// diagnostics (log detective)
|
|
118703
|
+
"mcp__neural__fetch_logs",
|
|
118704
|
+
// friend approval (Smith-only)
|
|
118705
|
+
"mcp__neural__list_friends",
|
|
118706
|
+
"mcp__neural__accept_friend",
|
|
118707
|
+
"mcp__neural__add_friend",
|
|
118708
|
+
// team assembly
|
|
118709
|
+
"mcp__neural__list_contacts",
|
|
118710
|
+
"mcp__neural__create_group",
|
|
118711
|
+
"mcp__neural__add_to_group",
|
|
118712
|
+
"mcp__neural__transfer_group_owner",
|
|
118713
|
+
"mcp__neural__leave_group",
|
|
118714
|
+
// neural bridge — cross-scope coordination when Smith is pulled into a group
|
|
118715
|
+
"mcp__neural__neural_send",
|
|
118716
|
+
"mcp__neural__neural_list_scopes",
|
|
118717
|
+
// interaction / planning
|
|
118718
|
+
"AskUserQuestion",
|
|
118719
|
+
"Write"
|
|
118720
|
+
];
|
|
118639
118721
|
function resolveVisionMcpToolHints(externalMcp) {
|
|
118640
118722
|
let describe3 = null;
|
|
118641
118723
|
let ocr = null;
|
|
@@ -119900,7 +119982,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
119900
119982
|
} catch (error51) {
|
|
119901
119983
|
logger15.error("Failed to read existing API-key agent settings; starting fresh", {
|
|
119902
119984
|
agentId: agentConfig.id,
|
|
119903
|
-
|
|
119985
|
+
settingsFile: "settings.json",
|
|
119904
119986
|
error: error51
|
|
119905
119987
|
});
|
|
119906
119988
|
}
|
|
@@ -119913,7 +119995,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
119913
119995
|
await import_promises3.default.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
|
|
119914
119996
|
logger15.info("API-key agent using isolated config dir", {
|
|
119915
119997
|
agentId: agentConfig.id,
|
|
119916
|
-
|
|
119998
|
+
configDirKind: "api-key-agent",
|
|
119917
119999
|
isNew,
|
|
119918
120000
|
hasBaseUrl: !!cfg.apiBaseUrl,
|
|
119919
120001
|
settingsWritten: Object.keys(envEntries)
|
|
@@ -120007,12 +120089,21 @@ ${cfg.instructions.trim()}` : "";
|
|
|
120007
120089
|
});
|
|
120008
120090
|
}
|
|
120009
120091
|
}
|
|
120010
|
-
const
|
|
120092
|
+
const resolvedExternalMcp = this.mcpRegistry?.buildForAgent({
|
|
120011
120093
|
agentId: agentConfig.id,
|
|
120012
120094
|
capabilityTier: cfg.capabilityTier,
|
|
120013
120095
|
isSmith: smithAgent,
|
|
120014
120096
|
cwd: agentCwd
|
|
120015
120097
|
}) ?? { mcpServers: {}, allowedTools: [], toolAbi: [] };
|
|
120098
|
+
const externalMcp = smithAgent ? { mcpServers: {}, allowedTools: [], toolAbi: [] } : resolvedExternalMcp;
|
|
120099
|
+
if (smithAgent && (Object.keys(resolvedExternalMcp.mcpServers).length > 0 || resolvedExternalMcp.allowedTools.length > 0 || (resolvedExternalMcp.toolAbi?.length ?? 0) > 0)) {
|
|
120100
|
+
logger15.info("Smith external MCP ignored by fixed tool whitelist", {
|
|
120101
|
+
agentId: agentConfig.id,
|
|
120102
|
+
scope: scopeKey(scope),
|
|
120103
|
+
serverNames: Object.keys(resolvedExternalMcp.mcpServers),
|
|
120104
|
+
allowedToolCount: resolvedExternalMcp.allowedTools.length
|
|
120105
|
+
});
|
|
120106
|
+
}
|
|
120016
120107
|
const visionMcpTools = resolveVisionMcpToolHints(externalMcp);
|
|
120017
120108
|
logger15.info("External MCP resolved for runtime", {
|
|
120018
120109
|
agentId: agentConfig.id,
|
|
@@ -120085,6 +120176,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
120085
120176
|
logger15.info("Creating Agent query", {
|
|
120086
120177
|
agentId: agentConfig.id,
|
|
120087
120178
|
scope: scopeKey(scope),
|
|
120179
|
+
systemPromptMode: smithAgent ? "string" : "preset",
|
|
120088
120180
|
cwd: agentCwd,
|
|
120089
120181
|
resume: !!savedSessionId,
|
|
120090
120182
|
sessionId: savedSessionId,
|
|
@@ -120105,71 +120197,63 @@ ${cfg.instructions.trim()}` : "";
|
|
|
120105
120197
|
});
|
|
120106
120198
|
const planModeRef = { active: false, denyCount: 0 };
|
|
120107
120199
|
const mediaGenerationTurnGuard = createOfficialMediaGenerationTurnGuard();
|
|
120200
|
+
const platformRules = scope.kind === "group" ? PLATFORM_AGENT_RULES : PLATFORM_AGENT_RULES_SINGLE;
|
|
120201
|
+
const appendText = [
|
|
120202
|
+
platformRules,
|
|
120203
|
+
nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
|
|
120204
|
+
DOCUMENT_READING_RULES,
|
|
120205
|
+
MEDIA_GENERATION_RULES,
|
|
120206
|
+
agentConfig.systemPrompt,
|
|
120207
|
+
scopedInstructions,
|
|
120208
|
+
notebookSection,
|
|
120209
|
+
forkHistorySection,
|
|
120210
|
+
scopesSection
|
|
120211
|
+
].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n");
|
|
120212
|
+
const systemPrompt = smithAgent ? appendText : { type: "preset", preset: "claude_code", append: appendText };
|
|
120213
|
+
const universalAllowedTools = [
|
|
120214
|
+
...nativeReadToolDisabled ? [] : ["Read"],
|
|
120215
|
+
"Edit",
|
|
120216
|
+
"Write",
|
|
120217
|
+
"Bash",
|
|
120218
|
+
"Glob",
|
|
120219
|
+
"Grep",
|
|
120220
|
+
...builtinWebSearchAllowed ? ["WebSearch"] : [],
|
|
120221
|
+
"WebFetch",
|
|
120222
|
+
"TodoWrite",
|
|
120223
|
+
"TaskCreate",
|
|
120224
|
+
"TaskUpdate",
|
|
120225
|
+
"AskUserQuestion",
|
|
120226
|
+
"mcp__neural__neural_send",
|
|
120227
|
+
"mcp__neural__neural_list_scopes",
|
|
120228
|
+
"mcp__neural__self_note",
|
|
120229
|
+
"mcp__neural__list_contacts",
|
|
120230
|
+
"mcp__neural__create_group",
|
|
120231
|
+
"mcp__neural__add_to_group",
|
|
120232
|
+
"mcp__neural__leave_group",
|
|
120233
|
+
"mcp__neural__remove_from_group",
|
|
120234
|
+
"mcp__neural__create_group_issue",
|
|
120235
|
+
"mcp__neural__resolve_group_issue",
|
|
120236
|
+
"mcp__neural__list_group_tasks",
|
|
120237
|
+
"mcp__neural__update_group_task",
|
|
120238
|
+
"mcp__neural__transfer_group_owner",
|
|
120239
|
+
"mcp__neural__post_to_moments",
|
|
120240
|
+
"mcp__neural__post_to_forum",
|
|
120241
|
+
"mcp__neural__read_moments",
|
|
120242
|
+
"mcp__neural__read_chat_history",
|
|
120243
|
+
"mcp__neural__read_document",
|
|
120244
|
+
"mcp__neural__list_available_skills",
|
|
120245
|
+
"mcp__neural__list_skill_index"
|
|
120246
|
+
];
|
|
120108
120247
|
const options = {
|
|
120109
120248
|
cwd: agentCwd,
|
|
120110
|
-
systemPrompt
|
|
120111
|
-
type: "preset",
|
|
120112
|
-
preset: "claude_code",
|
|
120113
|
-
append: [
|
|
120114
|
-
PLATFORM_AGENT_RULES,
|
|
120115
|
-
nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
|
|
120116
|
-
DOCUMENT_READING_RULES,
|
|
120117
|
-
MEDIA_GENERATION_RULES,
|
|
120118
|
-
agentConfig.systemPrompt,
|
|
120119
|
-
scopedInstructions,
|
|
120120
|
-
notebookSection,
|
|
120121
|
-
forkHistorySection,
|
|
120122
|
-
scopesSection
|
|
120123
|
-
].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
|
|
120124
|
-
},
|
|
120249
|
+
systemPrompt,
|
|
120125
120250
|
permissionMode: "bypassPermissions",
|
|
120126
120251
|
allowDangerouslySkipPermissions: true,
|
|
120127
120252
|
// allowedTools is the visibility whitelist passed to Claude Code. MCP tools
|
|
120128
120253
|
// with always_ask must still be included here so the model can request them;
|
|
120129
120254
|
// the MCP server policy/permission layer decides whether execution asks.
|
|
120130
120255
|
allowedTools: [
|
|
120131
|
-
...
|
|
120132
|
-
"Edit",
|
|
120133
|
-
"Write",
|
|
120134
|
-
"Bash",
|
|
120135
|
-
"Glob",
|
|
120136
|
-
"Grep",
|
|
120137
|
-
...builtinWebSearchAllowed ? ["WebSearch"] : [],
|
|
120138
|
-
"WebFetch",
|
|
120139
|
-
"TodoWrite",
|
|
120140
|
-
"TaskCreate",
|
|
120141
|
-
"TaskUpdate",
|
|
120142
|
-
"AskUserQuestion",
|
|
120143
|
-
"mcp__neural__neural_send",
|
|
120144
|
-
"mcp__neural__neural_list_scopes",
|
|
120145
|
-
"mcp__neural__self_note",
|
|
120146
|
-
"mcp__neural__list_contacts",
|
|
120147
|
-
"mcp__neural__create_group",
|
|
120148
|
-
"mcp__neural__add_to_group",
|
|
120149
|
-
"mcp__neural__leave_group",
|
|
120150
|
-
"mcp__neural__remove_from_group",
|
|
120151
|
-
"mcp__neural__create_group_issue",
|
|
120152
|
-
"mcp__neural__resolve_group_issue",
|
|
120153
|
-
"mcp__neural__list_group_tasks",
|
|
120154
|
-
"mcp__neural__update_group_task",
|
|
120155
|
-
"mcp__neural__transfer_group_owner",
|
|
120156
|
-
"mcp__neural__post_to_moments",
|
|
120157
|
-
"mcp__neural__post_to_forum",
|
|
120158
|
-
"mcp__neural__read_moments",
|
|
120159
|
-
"mcp__neural__read_chat_history",
|
|
120160
|
-
"mcp__neural__read_document",
|
|
120161
|
-
"mcp__neural__list_available_skills",
|
|
120162
|
-
"mcp__neural__list_skill_index",
|
|
120163
|
-
...isSmithAgent2(agentConfig) ? [
|
|
120164
|
-
"mcp__neural__create_agent",
|
|
120165
|
-
"mcp__neural__update_agent_profile",
|
|
120166
|
-
"mcp__neural__recommend_agent_skills",
|
|
120167
|
-
"mcp__neural__read_skill",
|
|
120168
|
-
"mcp__neural__list_friends",
|
|
120169
|
-
"mcp__neural__accept_friend",
|
|
120170
|
-
"mcp__neural__add_friend",
|
|
120171
|
-
"mcp__neural__fetch_logs"
|
|
120172
|
-
] : [],
|
|
120256
|
+
...smithAgent ? SMITH_ALLOWED_TOOLS : universalAllowedTools,
|
|
120173
120257
|
...externalMcp.allowedTools
|
|
120174
120258
|
],
|
|
120175
120259
|
// Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
|
|
@@ -120181,7 +120265,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
120181
120265
|
// instructions as the workflow body (replacing the default code-implementation
|
|
120182
120266
|
// phases). The SDK wraps it with read-only enforcement + ExitPlanMode protocol.
|
|
120183
120267
|
planModeInstructions: (() => {
|
|
120184
|
-
const planTools = [
|
|
120268
|
+
const planTools = (smithAgent ? ["AskUserQuestion", "Write (plan file only)"] : [
|
|
120185
120269
|
...nativeReadToolDisabled ? [] : ["Read"],
|
|
120186
120270
|
"Glob",
|
|
120187
120271
|
"Grep",
|
|
@@ -120189,19 +120273,19 @@ ${cfg.instructions.trim()}` : "";
|
|
|
120189
120273
|
"WebFetch",
|
|
120190
120274
|
"AskUserQuestion",
|
|
120191
120275
|
"Write (plan file only)"
|
|
120192
|
-
].join(", ");
|
|
120193
|
-
const researchTools = [
|
|
120276
|
+
]).join(", ");
|
|
120277
|
+
const researchTools = (smithAgent ? ["AskUserQuestion"] : [
|
|
120194
120278
|
...nativeReadToolDisabled ? [] : ["Read"],
|
|
120195
120279
|
"Grep",
|
|
120196
120280
|
...builtinWebSearchAllowed ? ["WebSearch"] : []
|
|
120197
|
-
].join(", ");
|
|
120198
|
-
const unavailableTools = [
|
|
120281
|
+
]).join(", ");
|
|
120282
|
+
const unavailableTools = (smithAgent ? ["Read", "Edit", "Bash", "Glob", "Grep", "WebFetch", "TodoWrite", "ExitPlanMode"] : [
|
|
120199
120283
|
"Edit",
|
|
120200
120284
|
"Bash",
|
|
120201
120285
|
"TodoWrite",
|
|
120202
120286
|
"ExitPlanMode",
|
|
120203
120287
|
...nativeReadToolDisabled ? ["Read"] : []
|
|
120204
|
-
].join(", ");
|
|
120288
|
+
]).join(", ");
|
|
120205
120289
|
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." : "";
|
|
120206
120290
|
return `You are a PLANNER, NOT an executor. The user will execute your plan later.
|
|
120207
120291
|
|
|
@@ -120431,17 +120515,17 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
120431
120515
|
}
|
|
120432
120516
|
};
|
|
120433
120517
|
const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
|
|
120434
|
-
const appendStr = options.systemPrompt.append;
|
|
120435
120518
|
logger15.info("Platform rules attached", {
|
|
120436
120519
|
agentId: agentConfig.id,
|
|
120437
120520
|
scope: scopeKey(scope),
|
|
120438
|
-
|
|
120521
|
+
systemPromptMode: smithAgent ? "string" : "preset",
|
|
120522
|
+
platformRulesLen: platformRules.length,
|
|
120439
120523
|
userPromptLen: userPromptTrimmed.length,
|
|
120440
120524
|
hasUserPrompt: userPromptTrimmed.length > 0,
|
|
120441
120525
|
notebookLen: notebookSection.length,
|
|
120442
120526
|
forkHistoryLen: forkHistorySection.length,
|
|
120443
120527
|
scopesLen: scopesSection.length,
|
|
120444
|
-
appendLen:
|
|
120528
|
+
appendLen: appendText.length,
|
|
120445
120529
|
hasCreateAgentTool: smithAgent,
|
|
120446
120530
|
hasLogDetectiveTools: smithAgent && this.skillStore !== null
|
|
120447
120531
|
});
|
|
@@ -120511,6 +120595,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
120511
120595
|
mcpAuditRecorder: this.mcpAuditRecorder,
|
|
120512
120596
|
segmentBuffer: "",
|
|
120513
120597
|
segmentCount: 0,
|
|
120598
|
+
workdirSignals: [],
|
|
120514
120599
|
accumulatedToolInput: "",
|
|
120515
120600
|
planModeRef,
|
|
120516
120601
|
mediaGenerationTurnGuard,
|
|
@@ -121544,6 +121629,7 @@ ${lines.join("\n")}`;
|
|
|
121544
121629
|
proc.currentToolName = null;
|
|
121545
121630
|
proc.segmentBuffer = "";
|
|
121546
121631
|
proc.segmentCount = 0;
|
|
121632
|
+
proc.workdirSignals = [];
|
|
121547
121633
|
}
|
|
121548
121634
|
clearPostMergeContinuationTimer(runtime) {
|
|
121549
121635
|
if (runtime.postMergeContinuationTimer) {
|
|
@@ -124319,6 +124405,7 @@ var HttpMcpRegistry = class {
|
|
|
124319
124405
|
localStore;
|
|
124320
124406
|
serverConnections = /* @__PURE__ */ new Map();
|
|
124321
124407
|
localConnections = /* @__PURE__ */ new Map();
|
|
124408
|
+
missingSecretWarnedKeys = /* @__PURE__ */ new Set();
|
|
124322
124409
|
lastRefreshCount = null;
|
|
124323
124410
|
apiUrl(suffix) {
|
|
124324
124411
|
const base = this.serverApiUrl.replace(/\/$/, "");
|
|
@@ -124419,13 +124506,19 @@ var HttpMcpRegistry = class {
|
|
|
124419
124506
|
return false;
|
|
124420
124507
|
});
|
|
124421
124508
|
}
|
|
124509
|
+
warnMissingSecretOnce(connection) {
|
|
124510
|
+
const key = `${connection.id}:${connection.providerId}:${connection.serverName}`;
|
|
124511
|
+
if (this.missingSecretWarnedKeys.has(key)) return;
|
|
124512
|
+
this.missingSecretWarnedKeys.add(key);
|
|
124513
|
+
logger20.warn("Skipping MCP connection without required secret", {
|
|
124514
|
+
id: connection.id,
|
|
124515
|
+
providerId: connection.providerId,
|
|
124516
|
+
serverName: connection.serverName
|
|
124517
|
+
});
|
|
124518
|
+
}
|
|
124422
124519
|
toSdkServerConfig(connection, ctx) {
|
|
124423
124520
|
if (mcpConnectionRequiresSecret(connection) && !connection.hasAuthSecret) {
|
|
124424
|
-
|
|
124425
|
-
id: connection.id,
|
|
124426
|
-
providerId: connection.providerId,
|
|
124427
|
-
serverName: connection.serverName
|
|
124428
|
-
});
|
|
124521
|
+
this.warnMissingSecretOnce(connection);
|
|
124429
124522
|
return null;
|
|
124430
124523
|
}
|
|
124431
124524
|
const policies = toolPolicies(connection);
|
|
@@ -126622,8 +126715,47 @@ var import_node_fs11 = __toESM(require("fs"), 1);
|
|
|
126622
126715
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
126623
126716
|
var import_node_path21 = __toESM(require("path"), 1);
|
|
126624
126717
|
var logger29 = createModuleLogger("bridge.logUploader");
|
|
126718
|
+
var STALE_RATE_LIMIT_SKIP_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
126625
126719
|
var DEFAULT_LOG_UPLOAD_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
126626
126720
|
var DEFAULT_BATCH_SIZE = 200;
|
|
126721
|
+
var MAX_UPLOAD_CHUNK_BODY_BYTES = 448 * 1024;
|
|
126722
|
+
var MAX_UPLOAD_RETRY_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
126723
|
+
var LogUploadHttpError = class extends Error {
|
|
126724
|
+
status;
|
|
126725
|
+
scope;
|
|
126726
|
+
retryAfterMs;
|
|
126727
|
+
constructor(status, bodyText) {
|
|
126728
|
+
const parsed = parseUploadErrorBody(bodyText);
|
|
126729
|
+
const summary = parsed.error ?? bodyText;
|
|
126730
|
+
super(`upload failed HTTP ${status}: ${summary.slice(0, 160)}`);
|
|
126731
|
+
this.name = "LogUploadHttpError";
|
|
126732
|
+
this.status = status;
|
|
126733
|
+
if (parsed.scope) this.scope = parsed.scope;
|
|
126734
|
+
if (parsed.retryAfterMs !== void 0) this.retryAfterMs = parsed.retryAfterMs;
|
|
126735
|
+
}
|
|
126736
|
+
};
|
|
126737
|
+
function normalizeRetryAfterMs(value) {
|
|
126738
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
|
|
126739
|
+
return Math.min(Math.ceil(value), MAX_UPLOAD_RETRY_AFTER_MS);
|
|
126740
|
+
}
|
|
126741
|
+
function parseUploadErrorBody(bodyText) {
|
|
126742
|
+
try {
|
|
126743
|
+
const parsed = JSON.parse(bodyText);
|
|
126744
|
+
if (typeof parsed !== "object" || parsed === null) return {};
|
|
126745
|
+
const body = parsed;
|
|
126746
|
+
return {
|
|
126747
|
+
...typeof body.error === "string" && body.error.length > 0 ? { error: body.error } : {},
|
|
126748
|
+
...typeof body.scope === "string" && body.scope.length > 0 ? { scope: body.scope } : {},
|
|
126749
|
+
...(() => {
|
|
126750
|
+
const retryAfterMs = normalizeRetryAfterMs(body.retryAfterMs);
|
|
126751
|
+
return retryAfterMs === void 0 ? {} : { retryAfterMs };
|
|
126752
|
+
})()
|
|
126753
|
+
};
|
|
126754
|
+
} catch (e) {
|
|
126755
|
+
logger29.debug("Failed to parse log upload error body", { error: e });
|
|
126756
|
+
return {};
|
|
126757
|
+
}
|
|
126758
|
+
}
|
|
126627
126759
|
function defaultLogFile(dataDir) {
|
|
126628
126760
|
return import_node_path21.default.join(dataDir, "logs", "bridge.log");
|
|
126629
126761
|
}
|
|
@@ -126713,26 +126845,38 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
126713
126845
|
return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
|
|
126714
126846
|
}
|
|
126715
126847
|
const processed = raw.slice(0, lastNewline + 1);
|
|
126716
|
-
const
|
|
126848
|
+
const linePattern = /.*?\n/g;
|
|
126717
126849
|
const entries = [];
|
|
126718
126850
|
let lineNum = cursor.lineNum;
|
|
126719
|
-
|
|
126720
|
-
|
|
126851
|
+
let offset = cursor.offset;
|
|
126852
|
+
let match;
|
|
126853
|
+
while ((match = linePattern.exec(processed)) !== null) {
|
|
126854
|
+
const rawLine = match[0];
|
|
126855
|
+
const line = rawLine.endsWith("\r\n") ? rawLine.slice(0, -2) : rawLine.slice(0, -1);
|
|
126721
126856
|
lineNum += 1;
|
|
126857
|
+
offset += Buffer.byteLength(rawLine, "utf8");
|
|
126858
|
+
const cursorAfter = {
|
|
126859
|
+
offset,
|
|
126860
|
+
lineNum,
|
|
126861
|
+
...fingerprint ? { fingerprint } : {}
|
|
126862
|
+
};
|
|
126722
126863
|
if (line.length === 0) continue;
|
|
126723
126864
|
const parsed = parseLogLine(line);
|
|
126724
126865
|
if (!parsed) continue;
|
|
126725
126866
|
entries.push({
|
|
126726
|
-
|
|
126727
|
-
|
|
126728
|
-
|
|
126729
|
-
|
|
126867
|
+
entry: {
|
|
126868
|
+
...parsed,
|
|
126869
|
+
raw: line,
|
|
126870
|
+
file: fileName,
|
|
126871
|
+
lineNum
|
|
126872
|
+
},
|
|
126873
|
+
cursorAfter
|
|
126730
126874
|
});
|
|
126731
126875
|
}
|
|
126732
126876
|
return {
|
|
126733
126877
|
entries,
|
|
126734
126878
|
nextCursor: {
|
|
126735
|
-
offset
|
|
126879
|
+
offset,
|
|
126736
126880
|
lineNum,
|
|
126737
126881
|
...fingerprint ? { fingerprint } : {}
|
|
126738
126882
|
},
|
|
@@ -126740,15 +126884,35 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
126740
126884
|
reason: "advanced"
|
|
126741
126885
|
};
|
|
126742
126886
|
}
|
|
126743
|
-
function
|
|
126744
|
-
|
|
126745
|
-
|
|
126746
|
-
|
|
126887
|
+
function logEntryTimeRange(entries) {
|
|
126888
|
+
let minTime = Number.POSITIVE_INFINITY;
|
|
126889
|
+
let maxTime = Number.NEGATIVE_INFINITY;
|
|
126890
|
+
let minTs = "";
|
|
126891
|
+
let maxTs = "";
|
|
126892
|
+
for (const entry of entries) {
|
|
126893
|
+
const parsed = Date.parse(entry.ts);
|
|
126894
|
+
if (!Number.isFinite(parsed)) return null;
|
|
126895
|
+
if (parsed < minTime) {
|
|
126896
|
+
minTime = parsed;
|
|
126897
|
+
minTs = entry.ts;
|
|
126898
|
+
}
|
|
126899
|
+
if (parsed > maxTime) {
|
|
126900
|
+
maxTime = parsed;
|
|
126901
|
+
maxTs = entry.ts;
|
|
126902
|
+
}
|
|
126747
126903
|
}
|
|
126748
|
-
return
|
|
126904
|
+
if (!minTs || !maxTs) return null;
|
|
126905
|
+
return { minTs, maxTs, maxTime };
|
|
126906
|
+
}
|
|
126907
|
+
function isStaleRateLimitedChunk(entries, nowMs) {
|
|
126908
|
+
const range = logEntryTimeRange(entries);
|
|
126909
|
+
if (!range) return null;
|
|
126910
|
+
if (nowMs - range.maxTime <= STALE_RATE_LIMIT_SKIP_AGE_MS) return null;
|
|
126911
|
+
return { minTs: range.minTs, maxTs: range.maxTs };
|
|
126749
126912
|
}
|
|
126750
126913
|
var BridgeLogUploader = class {
|
|
126751
126914
|
options;
|
|
126915
|
+
retryAfterByTarget = /* @__PURE__ */ new Map();
|
|
126752
126916
|
timer = null;
|
|
126753
126917
|
running = false;
|
|
126754
126918
|
stopped = false;
|
|
@@ -126765,7 +126929,7 @@ var BridgeLogUploader = class {
|
|
|
126765
126929
|
bridgeId: options.bridgeId,
|
|
126766
126930
|
hostname: options.hostname,
|
|
126767
126931
|
intervalMs: options.intervalMs ?? DEFAULT_LOG_UPLOAD_INTERVAL_MS,
|
|
126768
|
-
batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
126932
|
+
batchSize: Math.max(1, Math.floor(options.batchSize ?? DEFAULT_BATCH_SIZE)),
|
|
126769
126933
|
primaryTarget,
|
|
126770
126934
|
extraTargets: (options.extraLogFiles ?? []).map((file2) => normalizeTarget(file2, options.dataDir)),
|
|
126771
126935
|
includeRotatedBridgeLogs: options.includeRotatedBridgeLogs ?? true
|
|
@@ -126807,13 +126971,26 @@ var BridgeLogUploader = class {
|
|
|
126807
126971
|
bridgeEntryCount: 0,
|
|
126808
126972
|
uploadedChunkCount: 0,
|
|
126809
126973
|
accepted: 0,
|
|
126810
|
-
skipped: 0
|
|
126974
|
+
skipped: 0,
|
|
126975
|
+
dropped: 0
|
|
126811
126976
|
};
|
|
126812
126977
|
try {
|
|
126813
126978
|
const targets = await this.resolveTargets();
|
|
126814
126979
|
summary.targetCount = targets.length;
|
|
126815
126980
|
for (const target of targets) {
|
|
126981
|
+
const targetKey = this.targetKey(target);
|
|
126816
126982
|
try {
|
|
126983
|
+
const retryUntil = this.retryAfterByTarget.get(targetKey);
|
|
126984
|
+
const now = Date.now();
|
|
126985
|
+
if (retryUntil && retryUntil > now) {
|
|
126986
|
+
summary.idleTargetCount += 1;
|
|
126987
|
+
logger29.debug("Bridge log upload target backoff active", {
|
|
126988
|
+
logFile: target.logFile,
|
|
126989
|
+
retryAfterMs: retryUntil - now
|
|
126990
|
+
});
|
|
126991
|
+
continue;
|
|
126992
|
+
}
|
|
126993
|
+
if (retryUntil) this.retryAfterByTarget.delete(targetKey);
|
|
126817
126994
|
const cursor = await readCursor(target.cursorFile);
|
|
126818
126995
|
const batch = await this.readNewEntries(target, cursor);
|
|
126819
126996
|
if (!batch.advanced) {
|
|
@@ -126825,28 +127002,45 @@ var BridgeLogUploader = class {
|
|
|
126825
127002
|
summary.advancedTargetCount += 1;
|
|
126826
127003
|
summary.parsedEntryCount += batch.entries.length;
|
|
126827
127004
|
if (batch.entries.length > 0) {
|
|
126828
|
-
const result = await this.uploadEntries(batch.entries);
|
|
127005
|
+
const result = await this.uploadEntries(batch.entries, target.cursorFile, batch.nextCursor);
|
|
126829
127006
|
summary.bridgeEntryCount += result.bridgeEntryCount;
|
|
126830
127007
|
summary.uploadedChunkCount += result.uploadedChunkCount;
|
|
126831
127008
|
summary.accepted += result.accepted;
|
|
126832
127009
|
summary.skipped += result.skipped;
|
|
127010
|
+
summary.dropped += result.dropped;
|
|
127011
|
+
} else {
|
|
127012
|
+
await writeCursor(target.cursorFile, batch.nextCursor);
|
|
126833
127013
|
}
|
|
126834
|
-
|
|
127014
|
+
this.retryAfterByTarget.delete(targetKey);
|
|
126835
127015
|
} catch (e) {
|
|
126836
127016
|
summary.failedTargetCount += 1;
|
|
126837
|
-
|
|
127017
|
+
if (e instanceof LogUploadHttpError && e.retryAfterMs !== void 0) {
|
|
127018
|
+
this.retryAfterByTarget.set(targetKey, Date.now() + e.retryAfterMs);
|
|
127019
|
+
logger29.warn("Bridge log upload target failed", {
|
|
127020
|
+
error: e,
|
|
127021
|
+
logFile: target.logFile,
|
|
127022
|
+
status: e.status,
|
|
127023
|
+
quotaScope: e.scope,
|
|
127024
|
+
retryAfterMs: e.retryAfterMs
|
|
127025
|
+
});
|
|
127026
|
+
} else {
|
|
127027
|
+
logger29.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
|
|
127028
|
+
}
|
|
126838
127029
|
}
|
|
126839
127030
|
}
|
|
126840
127031
|
} catch (e) {
|
|
126841
127032
|
logger29.warn("Bridge log upload cycle failed", { error: e });
|
|
126842
127033
|
} finally {
|
|
126843
|
-
logger29.
|
|
127034
|
+
logger29.debug("Bridge log upload cycle summary", {
|
|
126844
127035
|
...summary,
|
|
126845
127036
|
durationMs: Date.now() - startedAt
|
|
126846
127037
|
});
|
|
126847
127038
|
this.running = false;
|
|
126848
127039
|
}
|
|
126849
127040
|
}
|
|
127041
|
+
targetKey(target) {
|
|
127042
|
+
return `${target.logFile}\0${target.cursorFile}`;
|
|
127043
|
+
}
|
|
126850
127044
|
async resolveTargets() {
|
|
126851
127045
|
const bridgeTargets = this.options.includeRotatedBridgeLogs ? await listRotatedBridgeTargets(this.options.primaryTarget, this.options.dataDir) : [this.options.primaryTarget];
|
|
126852
127046
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -126879,15 +127073,40 @@ var BridgeLogUploader = class {
|
|
|
126879
127073
|
const raw = await readStreamText(target.logFile, normalizedCursor.offset);
|
|
126880
127074
|
return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
|
|
126881
127075
|
}
|
|
126882
|
-
|
|
126883
|
-
|
|
127076
|
+
uploadBodyBytes(entries) {
|
|
127077
|
+
return Buffer.byteLength(JSON.stringify({
|
|
127078
|
+
bridgeId: this.options.bridgeId,
|
|
127079
|
+
hostname: this.options.hostname,
|
|
127080
|
+
entries
|
|
127081
|
+
}), "utf8");
|
|
127082
|
+
}
|
|
127083
|
+
chunkUploadEntries(entries) {
|
|
127084
|
+
const chunks = [];
|
|
127085
|
+
let current = [];
|
|
127086
|
+
for (const entry of entries) {
|
|
127087
|
+
const next = [...current, entry];
|
|
127088
|
+
const nextBodyBytes = this.uploadBodyBytes(next.map((item) => item.entry));
|
|
127089
|
+
if (current.length > 0 && (current.length >= this.options.batchSize || nextBodyBytes > MAX_UPLOAD_CHUNK_BODY_BYTES)) {
|
|
127090
|
+
chunks.push(current);
|
|
127091
|
+
current = [entry];
|
|
127092
|
+
} else {
|
|
127093
|
+
current = next;
|
|
127094
|
+
}
|
|
127095
|
+
}
|
|
127096
|
+
if (current.length > 0) chunks.push(current);
|
|
127097
|
+
return chunks;
|
|
127098
|
+
}
|
|
127099
|
+
async uploadEntries(entries, cursorFile, finalCursor) {
|
|
127100
|
+
const bridgeEntries = entries.filter((item) => item.entry.source === "bridge");
|
|
126884
127101
|
const result = {
|
|
126885
127102
|
bridgeEntryCount: bridgeEntries.length,
|
|
126886
127103
|
uploadedChunkCount: 0,
|
|
126887
127104
|
accepted: 0,
|
|
126888
|
-
skipped: 0
|
|
127105
|
+
skipped: 0,
|
|
127106
|
+
dropped: 0
|
|
126889
127107
|
};
|
|
126890
|
-
for (const chunk of
|
|
127108
|
+
for (const chunk of this.chunkUploadEntries(bridgeEntries)) {
|
|
127109
|
+
const payloadEntries = chunk.map((item) => item.entry);
|
|
126891
127110
|
const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
|
|
126892
127111
|
method: "POST",
|
|
126893
127112
|
headers: {
|
|
@@ -126897,7 +127116,7 @@ var BridgeLogUploader = class {
|
|
|
126897
127116
|
body: JSON.stringify({
|
|
126898
127117
|
bridgeId: this.options.bridgeId,
|
|
126899
127118
|
hostname: this.options.hostname,
|
|
126900
|
-
entries:
|
|
127119
|
+
entries: payloadEntries
|
|
126901
127120
|
})
|
|
126902
127121
|
});
|
|
126903
127122
|
if (!res.ok) {
|
|
@@ -126905,13 +127124,27 @@ var BridgeLogUploader = class {
|
|
|
126905
127124
|
logger29.debug("Failed to read log upload error body", { error: e });
|
|
126906
127125
|
return "";
|
|
126907
127126
|
});
|
|
126908
|
-
|
|
127127
|
+
const staleChunk = res.status === 429 ? isStaleRateLimitedChunk(payloadEntries, Date.now()) : null;
|
|
127128
|
+
if (staleChunk) {
|
|
127129
|
+
result.dropped += chunk.length;
|
|
127130
|
+
logger29.warn("Skipping stale bridge log upload chunk after rate limit", {
|
|
127131
|
+
status: res.status,
|
|
127132
|
+
entryCount: chunk.length,
|
|
127133
|
+
minEntryTs: staleChunk.minTs,
|
|
127134
|
+
maxEntryTs: staleChunk.maxTs
|
|
127135
|
+
});
|
|
127136
|
+
continue;
|
|
127137
|
+
}
|
|
127138
|
+
throw new LogUploadHttpError(res.status, body2);
|
|
126909
127139
|
}
|
|
126910
127140
|
const body = await res.json();
|
|
126911
127141
|
result.uploadedChunkCount += 1;
|
|
126912
127142
|
result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
|
|
126913
127143
|
result.skipped += typeof body.skipped === "number" ? body.skipped : 0;
|
|
127144
|
+
const last = chunk[chunk.length - 1];
|
|
127145
|
+
if (last) await writeCursor(cursorFile, last.cursorAfter);
|
|
126914
127146
|
}
|
|
127147
|
+
await writeCursor(cursorFile, finalCursor);
|
|
126915
127148
|
return result;
|
|
126916
127149
|
}
|
|
126917
127150
|
};
|
|
@@ -126995,7 +127228,7 @@ var SkillStore = class {
|
|
|
126995
127228
|
if (!isNotFoundError(e)) logger30.warn("Skill seed existing read failed", { name, filePath, error: e });
|
|
126996
127229
|
}
|
|
126997
127230
|
if (existing === content) {
|
|
126998
|
-
logger30.
|
|
127231
|
+
logger30.debug("Skill already in sync", { name, bytes: content.length });
|
|
126999
127232
|
return;
|
|
127000
127233
|
}
|
|
127001
127234
|
import_node_fs12.default.writeFileSync(tmpPath, content, "utf-8");
|