@fangyb/ahchat-bridge 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs 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\uFF0C\u4ECE\u4EE5\u4E0B\u9009\u4E00\u4E2A\u6700\u5339\u914D\u89D2\u8272\u7684\uFF1Aavatar_dev\uFF08\u5F00\u53D1\uFF09, avatar_pm\uFF08\u4EA7\u54C1/\u7BA1\u7406\uFF09, avatar_designer\uFF08\u8BBE\u8BA1\uFF09, avatar_qa\uFF08\u6D4B\u8BD5/QA\uFF09, avatar_writer\uFF08\u5199\u4F5C/\u6587\u6863\uFF09, avatar_analyst\uFF08\u5206\u6790/\u6570\u636E\uFF09, avatar_coach\uFF08\u654F\u6377\u6559\u7EC3/\u5E26\u6559\uFF09, avatar_scientist\uFF08\u7814\u7A76/\u7B97\u6CD5\uFF09, avatar_lawyer\uFF08\u6CD5\u52A1/\u5408\u89C4\uFF09, avatar_human_man\uFF08\u901A\u7528\u7537\u6027\uFF09, avatar_human_woman\uFF08\u901A\u7528\u5973\u6027\uFF09, avatar_human_nerd\uFF08\u6781\u5BA2\uFF09, avatar_human_cool\uFF08\u9177\uFF09, avatar_human_cowboy\uFF08\u72C2\u91CE\uFF09, avatar_human_artist\uFF08\u827A\u672F\u5BB6\uFF09\u3002\u7559\u7A7A\u5219\u7528\u9ED8\u8BA4 avatar_default\uFF08\u{1F916}\uFF09\u3002"
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 / avatar_human_man \u7B49\uFF09\u3002"
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
- logger12.info("SDK system subtype unhandled", {
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)",
@@ -117680,6 +117728,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
117680
117728
  const isMcpTool = parseMcpRuntimeToolName(toolName) != null;
117681
117729
  proc.currentMcpInvocationId = isMcpTool ? createMcpToolInvocationId() : null;
117682
117730
  proc.currentMcpInvocationStartedAt = isMcpTool ? (/* @__PURE__ */ new Date()).toISOString() : null;
117731
+ proc.currentMcpInvocationToolUseId = isMcpTool ? toolUseId ?? null : null;
117683
117732
  if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
117684
117733
  emit({
117685
117734
  type: "agent:tool_use",
@@ -117690,6 +117739,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
117690
117739
  input: {}
117691
117740
  }
117692
117741
  });
117742
+ }
117743
+ if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
117693
117744
  proc.contentBlocks.push({
117694
117745
  type: "tool_use",
117695
117746
  ...toolUseId ? { toolUseId } : {},
@@ -117866,6 +117917,14 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
117866
117917
  }
117867
117918
  if (proc.currentToolName && !proc.suppressCurrentToolUse && proc.currentMcpInvocationId && proc.currentMcpInvocationStartedAt && parseMcpRuntimeToolName(proc.currentToolName)) {
117868
117919
  try {
117920
+ logger12.info("MCP audit start paired with tool_use", {
117921
+ agentId: proc.agentId,
117922
+ replyMessageId: base.replyMessageId,
117923
+ traceId: base.traceId,
117924
+ toolUseId: proc.currentMcpInvocationToolUseId ?? null,
117925
+ runtimeToolName: proc.currentToolName,
117926
+ mcpInvocationId: proc.currentMcpInvocationId
117927
+ });
117869
117928
  proc.mcpAuditRecorder?.recordStart({
117870
117929
  id: proc.currentMcpInvocationId,
117871
117930
  runtimeToolName: proc.currentToolName,
@@ -117946,6 +118005,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
117946
118005
  });
117947
118006
  proc.currentMcpInvocationId = null;
117948
118007
  proc.currentMcpInvocationStartedAt = null;
118008
+ proc.currentMcpInvocationToolUseId = null;
117949
118009
  proc.activeToolUseStartedAt = void 0;
117950
118010
  proc.currentToolName = null;
117951
118011
  proc.currentToolUseId = null;
@@ -117962,6 +118022,16 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
117962
118022
  }
117963
118023
  if (proc.currentMcpInvocationId && parseMcpRuntimeToolName(toolName)) {
117964
118024
  try {
118025
+ logger12.info("MCP audit result paired with tool_result", {
118026
+ agentId: proc.agentId,
118027
+ replyMessageId: base.replyMessageId,
118028
+ traceId: base.traceId,
118029
+ toolUseId,
118030
+ startedToolUseId: proc.currentMcpInvocationToolUseId ?? null,
118031
+ runtimeToolName: toolName,
118032
+ mcpInvocationId: proc.currentMcpInvocationId,
118033
+ isError
118034
+ });
117965
118035
  proc.mcpAuditRecorder?.recordResult({
117966
118036
  id: proc.currentMcpInvocationId,
117967
118037
  runtimeToolName: toolName,
@@ -117983,6 +118053,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
117983
118053
  }
117984
118054
  proc.currentMcpInvocationId = null;
117985
118055
  proc.currentMcpInvocationStartedAt = null;
118056
+ proc.currentMcpInvocationToolUseId = null;
117986
118057
  }
117987
118058
  if (shouldStreamInternals(proc)) {
117988
118059
  emit({
@@ -118010,6 +118081,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118010
118081
  }
118011
118082
  }
118012
118083
  }
118084
+ recordWorkdirSignal(proc, toolName, isError);
118013
118085
  proc.activeToolUseStartedAt = void 0;
118014
118086
  proc.currentToolUseId = null;
118015
118087
  }
@@ -118071,7 +118143,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118071
118143
  if (isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: groupMode })) {
118072
118144
  checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
118073
118145
  emitUsageReported(proc, emit, base, usage);
118074
- if (groupMode && proc.contentBlocks.length > 0) {
118146
+ if (groupMode && (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0)) {
118075
118147
  emitGroupSegment(
118076
118148
  proc,
118077
118149
  emit,
@@ -118116,11 +118188,12 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118116
118188
  proc.segmentBuffer = proc.accumulatedText;
118117
118189
  flushTextSegmentOnBlockStop(proc, emit, base);
118118
118190
  }
118119
- if (proc.contentBlocks.length > 0) {
118191
+ if (proc.contentBlocks.length > 0 || (proc.workdirSignals?.length ?? 0) > 0) {
118120
118192
  logger12.info("Group turn trailing audit segment", {
118121
118193
  agentId: proc.agentId,
118122
118194
  replyMessageId: base.replyMessageId,
118123
118195
  blockCount: proc.contentBlocks.length,
118196
+ workdirSignalCount: proc.workdirSignals?.length ?? 0,
118124
118197
  traceId: base.traceId
118125
118198
  });
118126
118199
  emitGroupSegment(proc, emit, base, "", proc.contentBlocks, true);
@@ -118265,6 +118338,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118265
118338
  payload: { ...wireBase(base), error: errorText }
118266
118339
  });
118267
118340
  proc.contentBlocks = [];
118341
+ proc.workdirSignals = [];
118268
118342
  proc.accumulatedText = "";
118269
118343
  proc.accumulatedThinking = "";
118270
118344
  proc.segmentBuffer = "";
@@ -118297,6 +118371,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118297
118371
  proc.apiErrorEmitted = true;
118298
118372
  }
118299
118373
  proc.contentBlocks = [];
118374
+ proc.workdirSignals = [];
118300
118375
  proc.accumulatedText = "";
118301
118376
  proc.accumulatedThinking = "";
118302
118377
  proc.segmentBuffer = "";
@@ -118323,6 +118398,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118323
118398
  proc.apiErrorEmitted = true;
118324
118399
  }
118325
118400
  proc.contentBlocks = [];
118401
+ proc.workdirSignals = [];
118326
118402
  proc.accumulatedText = "";
118327
118403
  proc.accumulatedThinking = "";
118328
118404
  proc.segmentBuffer = "";
@@ -118359,6 +118435,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onPost
118359
118435
  });
118360
118436
  }
118361
118437
  proc.contentBlocks = [];
118438
+ proc.workdirSignals = [];
118362
118439
  proc.accumulatedText = "";
118363
118440
  proc.accumulatedThinking = "";
118364
118441
  proc.segmentBuffer = "";
@@ -118396,9 +118473,11 @@ function resetAccumulators(proc) {
118396
118473
  proc.currentToolUseId = null;
118397
118474
  proc.currentMcpInvocationId = null;
118398
118475
  proc.currentMcpInvocationStartedAt = null;
118476
+ proc.currentMcpInvocationToolUseId = null;
118399
118477
  proc.activeToolUseStartedAt = void 0;
118400
118478
  proc.segmentBuffer = "";
118401
118479
  proc.segmentCount = 0;
118480
+ proc.workdirSignals = [];
118402
118481
  proc.accumulatedToolInput = "";
118403
118482
  proc.apiErrorEmitted = false;
118404
118483
  proc.peakContextUsage = void 0;
@@ -118598,7 +118677,7 @@ function missingSubscriptionMessage(subscriptionId) {
118598
118677
  }
118599
118678
  var NODE_USER_UID = 1e3;
118600
118679
  var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
118601
- var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v4";
118680
+ var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v6";
118602
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;
118603
118682
  var IMAGE_READ_EXT_RE = /\.(?:bmp|gif|jpeg|jpg|png|webp)$/i;
118604
118683
  var DOCUMENT_READING_RULES = `DOCUMENT READING:
@@ -118614,6 +118693,31 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
118614
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.
118615
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.
118616
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
+ ];
118617
118721
  function resolveVisionMcpToolHints(externalMcp) {
118618
118722
  let describe3 = null;
118619
118723
  let ocr = null;
@@ -119878,7 +119982,7 @@ ${cfg.instructions.trim()}` : "";
119878
119982
  } catch (error51) {
119879
119983
  logger15.error("Failed to read existing API-key agent settings; starting fresh", {
119880
119984
  agentId: agentConfig.id,
119881
- settingsPath,
119985
+ settingsFile: "settings.json",
119882
119986
  error: error51
119883
119987
  });
119884
119988
  }
@@ -119891,7 +119995,7 @@ ${cfg.instructions.trim()}` : "";
119891
119995
  await import_promises3.default.writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
119892
119996
  logger15.info("API-key agent using isolated config dir", {
119893
119997
  agentId: agentConfig.id,
119894
- dir: effectiveConfigDir,
119998
+ configDirKind: "api-key-agent",
119895
119999
  isNew,
119896
120000
  hasBaseUrl: !!cfg.apiBaseUrl,
119897
120001
  settingsWritten: Object.keys(envEntries)
@@ -119985,12 +120089,21 @@ ${cfg.instructions.trim()}` : "";
119985
120089
  });
119986
120090
  }
119987
120091
  }
119988
- const externalMcp = this.mcpRegistry?.buildForAgent({
120092
+ const resolvedExternalMcp = this.mcpRegistry?.buildForAgent({
119989
120093
  agentId: agentConfig.id,
119990
120094
  capabilityTier: cfg.capabilityTier,
119991
120095
  isSmith: smithAgent,
119992
120096
  cwd: agentCwd
119993
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
+ }
119994
120107
  const visionMcpTools = resolveVisionMcpToolHints(externalMcp);
119995
120108
  logger15.info("External MCP resolved for runtime", {
119996
120109
  agentId: agentConfig.id,
@@ -120063,6 +120176,7 @@ ${cfg.instructions.trim()}` : "";
120063
120176
  logger15.info("Creating Agent query", {
120064
120177
  agentId: agentConfig.id,
120065
120178
  scope: scopeKey(scope),
120179
+ systemPromptMode: smithAgent ? "string" : "preset",
120066
120180
  cwd: agentCwd,
120067
120181
  resume: !!savedSessionId,
120068
120182
  sessionId: savedSessionId,
@@ -120083,71 +120197,63 @@ ${cfg.instructions.trim()}` : "";
120083
120197
  });
120084
120198
  const planModeRef = { active: false, denyCount: 0 };
120085
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
+ ];
120086
120247
  const options = {
120087
120248
  cwd: agentCwd,
120088
- systemPrompt: {
120089
- type: "preset",
120090
- preset: "claude_code",
120091
- append: [
120092
- PLATFORM_AGENT_RULES,
120093
- nativeReadToolDisabled ? buildNativeReadDisabledRules(visionMcpTools) : "",
120094
- DOCUMENT_READING_RULES,
120095
- MEDIA_GENERATION_RULES,
120096
- agentConfig.systemPrompt,
120097
- scopedInstructions,
120098
- notebookSection,
120099
- forkHistorySection,
120100
- scopesSection
120101
- ].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
120102
- },
120249
+ systemPrompt,
120103
120250
  permissionMode: "bypassPermissions",
120104
120251
  allowDangerouslySkipPermissions: true,
120105
120252
  // allowedTools is the visibility whitelist passed to Claude Code. MCP tools
120106
120253
  // with always_ask must still be included here so the model can request them;
120107
120254
  // the MCP server policy/permission layer decides whether execution asks.
120108
120255
  allowedTools: [
120109
- ...nativeReadToolDisabled ? [] : ["Read"],
120110
- "Edit",
120111
- "Write",
120112
- "Bash",
120113
- "Glob",
120114
- "Grep",
120115
- ...builtinWebSearchAllowed ? ["WebSearch"] : [],
120116
- "WebFetch",
120117
- "TodoWrite",
120118
- "TaskCreate",
120119
- "TaskUpdate",
120120
- "AskUserQuestion",
120121
- "mcp__neural__neural_send",
120122
- "mcp__neural__neural_list_scopes",
120123
- "mcp__neural__self_note",
120124
- "mcp__neural__list_contacts",
120125
- "mcp__neural__create_group",
120126
- "mcp__neural__add_to_group",
120127
- "mcp__neural__leave_group",
120128
- "mcp__neural__remove_from_group",
120129
- "mcp__neural__create_group_issue",
120130
- "mcp__neural__resolve_group_issue",
120131
- "mcp__neural__list_group_tasks",
120132
- "mcp__neural__update_group_task",
120133
- "mcp__neural__transfer_group_owner",
120134
- "mcp__neural__post_to_moments",
120135
- "mcp__neural__post_to_forum",
120136
- "mcp__neural__read_moments",
120137
- "mcp__neural__read_chat_history",
120138
- "mcp__neural__read_document",
120139
- "mcp__neural__list_available_skills",
120140
- "mcp__neural__list_skill_index",
120141
- ...isSmithAgent2(agentConfig) ? [
120142
- "mcp__neural__create_agent",
120143
- "mcp__neural__update_agent_profile",
120144
- "mcp__neural__recommend_agent_skills",
120145
- "mcp__neural__read_skill",
120146
- "mcp__neural__list_friends",
120147
- "mcp__neural__accept_friend",
120148
- "mcp__neural__add_friend",
120149
- "mcp__neural__fetch_logs"
120150
- ] : [],
120256
+ ...smithAgent ? SMITH_ALLOWED_TOOLS : universalAllowedTools,
120151
120257
  ...externalMcp.allowedTools
120152
120258
  ],
120153
120259
  // Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
@@ -120159,7 +120265,7 @@ ${cfg.instructions.trim()}` : "";
120159
120265
  // instructions as the workflow body (replacing the default code-implementation
120160
120266
  // phases). The SDK wraps it with read-only enforcement + ExitPlanMode protocol.
120161
120267
  planModeInstructions: (() => {
120162
- const planTools = [
120268
+ const planTools = (smithAgent ? ["AskUserQuestion", "Write (plan file only)"] : [
120163
120269
  ...nativeReadToolDisabled ? [] : ["Read"],
120164
120270
  "Glob",
120165
120271
  "Grep",
@@ -120167,19 +120273,19 @@ ${cfg.instructions.trim()}` : "";
120167
120273
  "WebFetch",
120168
120274
  "AskUserQuestion",
120169
120275
  "Write (plan file only)"
120170
- ].join(", ");
120171
- const researchTools = [
120276
+ ]).join(", ");
120277
+ const researchTools = (smithAgent ? ["AskUserQuestion"] : [
120172
120278
  ...nativeReadToolDisabled ? [] : ["Read"],
120173
120279
  "Grep",
120174
120280
  ...builtinWebSearchAllowed ? ["WebSearch"] : []
120175
- ].join(", ");
120176
- const unavailableTools = [
120281
+ ]).join(", ");
120282
+ const unavailableTools = (smithAgent ? ["Read", "Edit", "Bash", "Glob", "Grep", "WebFetch", "TodoWrite", "ExitPlanMode"] : [
120177
120283
  "Edit",
120178
120284
  "Bash",
120179
120285
  "TodoWrite",
120180
120286
  "ExitPlanMode",
120181
120287
  ...nativeReadToolDisabled ? ["Read"] : []
120182
- ].join(", ");
120288
+ ]).join(", ");
120183
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." : "";
120184
120290
  return `You are a PLANNER, NOT an executor. The user will execute your plan later.
120185
120291
 
@@ -120409,17 +120515,17 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
120409
120515
  }
120410
120516
  };
120411
120517
  const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
120412
- const appendStr = options.systemPrompt.append;
120413
120518
  logger15.info("Platform rules attached", {
120414
120519
  agentId: agentConfig.id,
120415
120520
  scope: scopeKey(scope),
120416
- platformRulesLen: PLATFORM_AGENT_RULES.length,
120521
+ systemPromptMode: smithAgent ? "string" : "preset",
120522
+ platformRulesLen: platformRules.length,
120417
120523
  userPromptLen: userPromptTrimmed.length,
120418
120524
  hasUserPrompt: userPromptTrimmed.length > 0,
120419
120525
  notebookLen: notebookSection.length,
120420
120526
  forkHistoryLen: forkHistorySection.length,
120421
120527
  scopesLen: scopesSection.length,
120422
- appendLen: appendStr.length,
120528
+ appendLen: appendText.length,
120423
120529
  hasCreateAgentTool: smithAgent,
120424
120530
  hasLogDetectiveTools: smithAgent && this.skillStore !== null
120425
120531
  });
@@ -120489,6 +120595,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
120489
120595
  mcpAuditRecorder: this.mcpAuditRecorder,
120490
120596
  segmentBuffer: "",
120491
120597
  segmentCount: 0,
120598
+ workdirSignals: [],
120492
120599
  accumulatedToolInput: "",
120493
120600
  planModeRef,
120494
120601
  mediaGenerationTurnGuard,
@@ -121522,6 +121629,7 @@ ${lines.join("\n")}`;
121522
121629
  proc.currentToolName = null;
121523
121630
  proc.segmentBuffer = "";
121524
121631
  proc.segmentCount = 0;
121632
+ proc.workdirSignals = [];
121525
121633
  }
121526
121634
  clearPostMergeContinuationTimer(runtime) {
121527
121635
  if (runtime.postMergeContinuationTimer) {
@@ -124297,6 +124405,7 @@ var HttpMcpRegistry = class {
124297
124405
  localStore;
124298
124406
  serverConnections = /* @__PURE__ */ new Map();
124299
124407
  localConnections = /* @__PURE__ */ new Map();
124408
+ missingSecretWarnedKeys = /* @__PURE__ */ new Set();
124300
124409
  lastRefreshCount = null;
124301
124410
  apiUrl(suffix) {
124302
124411
  const base = this.serverApiUrl.replace(/\/$/, "");
@@ -124397,13 +124506,19 @@ var HttpMcpRegistry = class {
124397
124506
  return false;
124398
124507
  });
124399
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
+ }
124400
124519
  toSdkServerConfig(connection, ctx) {
124401
124520
  if (mcpConnectionRequiresSecret(connection) && !connection.hasAuthSecret) {
124402
- logger20.warn("Skipping MCP connection without required secret", {
124403
- id: connection.id,
124404
- providerId: connection.providerId,
124405
- serverName: connection.serverName
124406
- });
124521
+ this.warnMissingSecretOnce(connection);
124407
124522
  return null;
124408
124523
  }
124409
124524
  const policies = toolPolicies(connection);
@@ -126600,8 +126715,47 @@ var import_node_fs11 = __toESM(require("fs"), 1);
126600
126715
  var import_promises7 = __toESM(require("fs/promises"), 1);
126601
126716
  var import_node_path21 = __toESM(require("path"), 1);
126602
126717
  var logger29 = createModuleLogger("bridge.logUploader");
126718
+ var STALE_RATE_LIMIT_SKIP_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
126603
126719
  var DEFAULT_LOG_UPLOAD_INTERVAL_MS = 24 * 60 * 60 * 1e3;
126604
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
+ }
126605
126759
  function defaultLogFile(dataDir) {
126606
126760
  return import_node_path21.default.join(dataDir, "logs", "bridge.log");
126607
126761
  }
@@ -126691,26 +126845,38 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
126691
126845
  return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
126692
126846
  }
126693
126847
  const processed = raw.slice(0, lastNewline + 1);
126694
- const lines = processed.split(/\r?\n/);
126848
+ const linePattern = /.*?\n/g;
126695
126849
  const entries = [];
126696
126850
  let lineNum = cursor.lineNum;
126697
- for (let i = 0; i < lines.length - 1; i += 1) {
126698
- const line = lines[i] ?? "";
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);
126699
126856
  lineNum += 1;
126857
+ offset += Buffer.byteLength(rawLine, "utf8");
126858
+ const cursorAfter = {
126859
+ offset,
126860
+ lineNum,
126861
+ ...fingerprint ? { fingerprint } : {}
126862
+ };
126700
126863
  if (line.length === 0) continue;
126701
126864
  const parsed = parseLogLine(line);
126702
126865
  if (!parsed) continue;
126703
126866
  entries.push({
126704
- ...parsed,
126705
- raw: line,
126706
- file: fileName,
126707
- lineNum
126867
+ entry: {
126868
+ ...parsed,
126869
+ raw: line,
126870
+ file: fileName,
126871
+ lineNum
126872
+ },
126873
+ cursorAfter
126708
126874
  });
126709
126875
  }
126710
126876
  return {
126711
126877
  entries,
126712
126878
  nextCursor: {
126713
- offset: cursor.offset + Buffer.byteLength(processed, "utf8"),
126879
+ offset,
126714
126880
  lineNum,
126715
126881
  ...fingerprint ? { fingerprint } : {}
126716
126882
  },
@@ -126718,15 +126884,35 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
126718
126884
  reason: "advanced"
126719
126885
  };
126720
126886
  }
126721
- function chunkEntries(entries, size) {
126722
- const chunks = [];
126723
- for (let i = 0; i < entries.length; i += size) {
126724
- chunks.push(entries.slice(i, i + size));
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
+ }
126725
126903
  }
126726
- return chunks;
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 };
126727
126912
  }
126728
126913
  var BridgeLogUploader = class {
126729
126914
  options;
126915
+ retryAfterByTarget = /* @__PURE__ */ new Map();
126730
126916
  timer = null;
126731
126917
  running = false;
126732
126918
  stopped = false;
@@ -126743,7 +126929,7 @@ var BridgeLogUploader = class {
126743
126929
  bridgeId: options.bridgeId,
126744
126930
  hostname: options.hostname,
126745
126931
  intervalMs: options.intervalMs ?? DEFAULT_LOG_UPLOAD_INTERVAL_MS,
126746
- batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
126932
+ batchSize: Math.max(1, Math.floor(options.batchSize ?? DEFAULT_BATCH_SIZE)),
126747
126933
  primaryTarget,
126748
126934
  extraTargets: (options.extraLogFiles ?? []).map((file2) => normalizeTarget(file2, options.dataDir)),
126749
126935
  includeRotatedBridgeLogs: options.includeRotatedBridgeLogs ?? true
@@ -126785,13 +126971,26 @@ var BridgeLogUploader = class {
126785
126971
  bridgeEntryCount: 0,
126786
126972
  uploadedChunkCount: 0,
126787
126973
  accepted: 0,
126788
- skipped: 0
126974
+ skipped: 0,
126975
+ dropped: 0
126789
126976
  };
126790
126977
  try {
126791
126978
  const targets = await this.resolveTargets();
126792
126979
  summary.targetCount = targets.length;
126793
126980
  for (const target of targets) {
126981
+ const targetKey = this.targetKey(target);
126794
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);
126795
126994
  const cursor = await readCursor(target.cursorFile);
126796
126995
  const batch = await this.readNewEntries(target, cursor);
126797
126996
  if (!batch.advanced) {
@@ -126803,28 +127002,45 @@ var BridgeLogUploader = class {
126803
127002
  summary.advancedTargetCount += 1;
126804
127003
  summary.parsedEntryCount += batch.entries.length;
126805
127004
  if (batch.entries.length > 0) {
126806
- const result = await this.uploadEntries(batch.entries);
127005
+ const result = await this.uploadEntries(batch.entries, target.cursorFile, batch.nextCursor);
126807
127006
  summary.bridgeEntryCount += result.bridgeEntryCount;
126808
127007
  summary.uploadedChunkCount += result.uploadedChunkCount;
126809
127008
  summary.accepted += result.accepted;
126810
127009
  summary.skipped += result.skipped;
127010
+ summary.dropped += result.dropped;
127011
+ } else {
127012
+ await writeCursor(target.cursorFile, batch.nextCursor);
126811
127013
  }
126812
- await writeCursor(target.cursorFile, batch.nextCursor);
127014
+ this.retryAfterByTarget.delete(targetKey);
126813
127015
  } catch (e) {
126814
127016
  summary.failedTargetCount += 1;
126815
- logger29.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
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
+ }
126816
127029
  }
126817
127030
  }
126818
127031
  } catch (e) {
126819
127032
  logger29.warn("Bridge log upload cycle failed", { error: e });
126820
127033
  } finally {
126821
- logger29.info("Bridge log upload cycle summary", {
127034
+ logger29.debug("Bridge log upload cycle summary", {
126822
127035
  ...summary,
126823
127036
  durationMs: Date.now() - startedAt
126824
127037
  });
126825
127038
  this.running = false;
126826
127039
  }
126827
127040
  }
127041
+ targetKey(target) {
127042
+ return `${target.logFile}\0${target.cursorFile}`;
127043
+ }
126828
127044
  async resolveTargets() {
126829
127045
  const bridgeTargets = this.options.includeRotatedBridgeLogs ? await listRotatedBridgeTargets(this.options.primaryTarget, this.options.dataDir) : [this.options.primaryTarget];
126830
127046
  const seen = /* @__PURE__ */ new Set();
@@ -126857,15 +127073,40 @@ var BridgeLogUploader = class {
126857
127073
  const raw = await readStreamText(target.logFile, normalizedCursor.offset);
126858
127074
  return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
126859
127075
  }
126860
- async uploadEntries(entries) {
126861
- const bridgeEntries = entries.filter((entry) => entry.source === "bridge");
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");
126862
127101
  const result = {
126863
127102
  bridgeEntryCount: bridgeEntries.length,
126864
127103
  uploadedChunkCount: 0,
126865
127104
  accepted: 0,
126866
- skipped: 0
127105
+ skipped: 0,
127106
+ dropped: 0
126867
127107
  };
126868
- for (const chunk of chunkEntries(bridgeEntries, this.options.batchSize)) {
127108
+ for (const chunk of this.chunkUploadEntries(bridgeEntries)) {
127109
+ const payloadEntries = chunk.map((item) => item.entry);
126869
127110
  const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
126870
127111
  method: "POST",
126871
127112
  headers: {
@@ -126875,7 +127116,7 @@ var BridgeLogUploader = class {
126875
127116
  body: JSON.stringify({
126876
127117
  bridgeId: this.options.bridgeId,
126877
127118
  hostname: this.options.hostname,
126878
- entries: chunk
127119
+ entries: payloadEntries
126879
127120
  })
126880
127121
  });
126881
127122
  if (!res.ok) {
@@ -126883,13 +127124,27 @@ var BridgeLogUploader = class {
126883
127124
  logger29.debug("Failed to read log upload error body", { error: e });
126884
127125
  return "";
126885
127126
  });
126886
- throw new Error(`upload failed HTTP ${res.status}: ${body2.slice(0, 160)}`);
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);
126887
127139
  }
126888
127140
  const body = await res.json();
126889
127141
  result.uploadedChunkCount += 1;
126890
127142
  result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
126891
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);
126892
127146
  }
127147
+ await writeCursor(cursorFile, finalCursor);
126893
127148
  return result;
126894
127149
  }
126895
127150
  };
@@ -126973,7 +127228,7 @@ var SkillStore = class {
126973
127228
  if (!isNotFoundError(e)) logger30.warn("Skill seed existing read failed", { name, filePath, error: e });
126974
127229
  }
126975
127230
  if (existing === content) {
126976
- logger30.info("Skill already in sync", { name, bytes: content.length });
127231
+ logger30.debug("Skill already in sync", { name, bytes: content.length });
126977
127232
  return;
126978
127233
  }
126979
127234
  import_node_fs12.default.writeFileSync(tmpPath, content, "utf-8");