@fangyb/ahchat-bridge 0.1.24 → 0.1.26

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.
Files changed (3) hide show
  1. package/dist/cli.cjs +382 -60
  2. package/dist/index.js +376 -52
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5467,6 +5467,14 @@ runtime\u3002\u5B83\u4EEC\u4E0D\u662F"\u4E0D\u540C\u7684\u4EBA"\u2014\u2014\u516
5467
5467
  \`# \u3010\u6807\u9898\u3011\` (a single \`#\` followed by Chinese bracket-enclosed title).
5468
5468
  For single-topic or short conversational replies, omit headings entirely.
5469
5469
 
5470
+ # AskUserQuestion tool
5471
+ - When you need the user to choose from options or answer a clarification question, call the real
5472
+ \`AskUserQuestion\` tool with a \`questions\` array.
5473
+ - Never output \`<AskUserQuestion>\`, \`</AskUserQuestion>\`, or raw JSON question payloads as chat text.
5474
+ Text like that is not an interactive panel and the user cannot answer it through the tool flow.
5475
+ - If you are not able to call \`AskUserQuestion\`, ask the question as plain natural language instead of
5476
+ emitting tool-shaped XML or JSON.
5477
+
5470
5478
  # Runtime payload \u2014 how to read unread messages
5471
5479
 
5472
5480
  \u6BCF\u4E2A turn \u91CC runtime \u4F1A\u7ED9\u4F60\u300C\u672A\u8BFB\u7FA4\u6D88\u606F\uFF08N \u6761\uFF0C\u6309\u65F6\u95F4\u987A\u5E8F\uFF09\u300D\u6BB5\u3002\u8BFB\u6CD5\uFF1A
@@ -5896,11 +5904,20 @@ function parseWSMessage(raw) {
5896
5904
  }
5897
5905
  return parsed;
5898
5906
  }
5907
+ function isAskUserQuestionToolName(toolName) {
5908
+ if (!toolName) return false;
5909
+ const normalized = toolName.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
5910
+ return normalized === "askuserquestion" || normalized.endsWith("askuserquestion");
5911
+ }
5899
5912
 
5900
5913
  // ../shared/src/utils/subscription.ts
5914
+ var PRIMARY_COMPANY_SUBSCRIPTION_ID = "sub_company_primary";
5901
5915
  function isSubscriptionType(v) {
5902
5916
  return v === "system" || v === "project";
5903
5917
  }
5918
+ function isPrimaryCompanySubscriptionId(v) {
5919
+ return v === PRIMARY_COMPANY_SUBSCRIPTION_ID;
5920
+ }
5904
5921
 
5905
5922
  // ../shared/src/utils/agentConfig.ts
5906
5923
  function parseAgentConfig(raw) {
@@ -21514,6 +21531,13 @@ function normalizeDocumentText(value) {
21514
21531
  return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n{4,}/g, "\n\n\n").trim();
21515
21532
  }
21516
21533
 
21534
+ // src/bridgeHttp.ts
21535
+ var BRIDGE_TOKEN_HEADER = "X-AHChat-Bridge-Token";
21536
+ function bridgeAuthHeaders(bridgeToken) {
21537
+ const token = bridgeToken?.trim();
21538
+ return token ? { [BRIDGE_TOKEN_HEADER]: token } : {};
21539
+ }
21540
+
21517
21541
  // src/neuralMcpServer.ts
21518
21542
  var logger6 = createModuleLogger("neural.mcpServer");
21519
21543
  function formatScopeLabel(key, groupName) {
@@ -21532,6 +21556,26 @@ function filterContactsByOwner(all, ownerId) {
21532
21556
  (a) => a.kind === "system" || a.ownerId === ownerId || a.ownerId == null || a.kind === "human" && a.id === ownerId
21533
21557
  );
21534
21558
  }
21559
+ async function resolveTierSubscriptionPreference(preferredSubscriptionId, deps) {
21560
+ if (!preferredSubscriptionId || !isPrimaryCompanySubscriptionId(preferredSubscriptionId)) {
21561
+ return preferredSubscriptionId;
21562
+ }
21563
+ if (!deps.serverApiUrl) return preferredSubscriptionId;
21564
+ try {
21565
+ const base = deps.serverApiUrl.replace(/\/$/, "");
21566
+ const res = await fetch(`${base}/api/subscriptions`, {
21567
+ headers: bridgeAuthHeaders(deps.bridgeToken ?? null)
21568
+ });
21569
+ if (!res.ok) return preferredSubscriptionId;
21570
+ const subscriptions = await res.json();
21571
+ return subscriptions.find(
21572
+ (subscription) => subscription.billingMode === "company_billable" && subscription.isPrimaryCompany === true
21573
+ )?.id ?? preferredSubscriptionId;
21574
+ } catch (e) {
21575
+ logger6.warn("Primary company tier preference resolution failed", { error: e });
21576
+ return preferredSubscriptionId;
21577
+ }
21578
+ }
21535
21579
  function resolveMyHuman(registry2, agentId) {
21536
21580
  const all = registry2.getAll();
21537
21581
  const myOwnerId = contactOwnerId(registry2, agentId);
@@ -22687,8 +22731,8 @@ ${result.warnings.map((warning) => `- ${warning}`).join("\n")}
22687
22731
  "fetch_logs",
22688
22732
  `\u62C9\u53D6\u7CFB\u7EDF\u65E5\u5FD7\uFF08\u6309\u65F6\u95F4\u7A97 + \u53EF\u9009 traceId / module / level \u8FC7\u6EE4\uFF09\u3002
22689
22733
  \u8BFB SKILL "log-analysis" \u540E\u624D\u7528\u8FD9\u4E2A\u5DE5\u5177\u3002\u4E00\u6B21\u8C03\u7528\u53EA\u80FD\u62C9\u4E00\u4E2A source\uFF08server \u6216 bridge\uFF09\uFF0C\u5168\u666F\u9700\u8981\u4E24\u6B21\u3002
22690
- limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679C\u9876\u90E8\u5E26\u5206\u7EA7\u7EDF\u8BA1\uFF08ERROR/WARN/INFO \u5404\u591A\u5C11\u6761\uFF09\uFF0C\u6309\u65F6\u95F4\u5347\u5E8F\u6392\u5217\u3002\u6BCF\u884C\u5E26 file / lineNum\uFF0C\u5F15\u7528\u65E5\u5FD7\u65F6\u5FC5\u987B\u7CBE\u786E\u5199 [<file>:L<lineNum>]\u3002
22691
- \u6CE8\u610F\uFF1A\u5982\u679C\u7ED3\u679C\u8FC7\u957F\u4F1A\u88AB\u622A\u65AD\uFF0Cheader \u4F1A\u6807\u6CE8"\u5DF2\u622A\u65AD"\u2014\u2014\u6B64\u65F6\u7F29\u5C0F\u65F6\u95F4\u7A97\u6216\u52A0 module/traceId \u8FC7\u6EE4\u518D\u67E5\u3002`,
22734
+ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\uFF1B\u652F\u6301 offset \u5206\u9875\u3002\u8FD4\u56DE\u7ED3\u679C\u9876\u90E8\u5E26\u5206\u7EA7\u7EDF\u8BA1\uFF08ERROR/WARN/INFO \u5404\u591A\u5C11\u6761\uFF09\uFF0C\u6309\u65F6\u95F4\u5012\u5E8F\u6392\u5217\uFF0C\u6700\u65B0\u65E5\u5FD7\u5728\u524D\u3002\u6BCF\u884C\u5E26 file / lineNum\uFF0C\u5F15\u7528\u65E5\u5FD7\u65F6\u5FC5\u987B\u7CBE\u786E\u5199 [<file>:L<lineNum>]\u3002
22735
+ \u6CE8\u610F\uFF1A\u5982\u679C\u8FD8\u6709\u66F4\u591A\u7ED3\u679C\uFF0Cheader \u4F1A\u6807\u6CE8 nextOffset\uFF1B\u7EE7\u7EED\u67E5\u8BE2\u65F6\u5E26\u4E0A offset=nextOffset\u3002`,
22692
22736
  {
22693
22737
  source: external_exports.enum(["server", "bridge"]).describe('\u65E5\u5FD7\u6765\u6E90\uFF0C\u5FC5\u987B\u662F "server" \u6216 "bridge" \u4E4B\u4E00\u3002'),
22694
22738
  start_iso: external_exports.string().describe('\u8D77\u59CB\u65F6\u95F4\uFF0CISO 8601 \u6216\u76F8\u5BF9\u683C\u5F0F\uFF1A"now-5m" / "now-1h" / "now-24h"\u3002'),
@@ -22697,7 +22741,8 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
22697
22741
  trace_id: external_exports.string().optional().describe("\u8FC7\u6EE4\u7279\u5B9A traceId\uFF08\u7CBE\u786E\u5339\u914D\uFF09\u3002"),
22698
22742
  module: external_exports.string().optional().describe('\u8FC7\u6EE4 module \u524D\u7F00\uFF08\u5982 "ws.handler" / "agent.manager"\uFF09\u3002'),
22699
22743
  msg_contains: external_exports.string().optional().describe("msg \u5B50\u4E32\u8FC7\u6EE4\uFF08\u533A\u5206\u5927\u5C0F\u5199\uFF09\u3002"),
22700
- limit: external_exports.number().int().min(1).max(2e3).optional().describe("\u8FD4\u56DE\u6761\u6570\u4E0A\u9650\uFF0C\u9ED8\u8BA4 500\uFF0C\u6700\u5927 2000\u3002")
22744
+ limit: external_exports.number().int().min(1).max(2e3).optional().describe("\u8FD4\u56DE\u6761\u6570\u4E0A\u9650\uFF0C\u9ED8\u8BA4 500\uFF0C\u6700\u5927 2000\u3002"),
22745
+ offset: external_exports.number().int().min(0).optional().describe("\u5206\u9875\u504F\u79FB\u91CF\uFF1B\u9996\u6B21\u67E5\u8BE2\u4E0D\u4F20\uFF0C\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20\u4E0A\u6B21\u8FD4\u56DE\u7684 nextOffset\u3002")
22701
22746
  },
22702
22747
  async (args) => {
22703
22748
  if (!deps.isSmith) {
@@ -22722,9 +22767,11 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
22722
22767
  traceId: args.trace_id,
22723
22768
  module: args.module,
22724
22769
  levelMin: args.level_min,
22725
- limit: args.limit
22770
+ limit: args.limit,
22771
+ offset: args.offset
22726
22772
  });
22727
22773
  const parsedLimit = typeof args.limit === "number" && Number.isFinite(args.limit) ? args.limit : 500;
22774
+ const parsedOffset = typeof args.offset === "number" && Number.isFinite(args.offset) ? args.offset : 0;
22728
22775
  try {
22729
22776
  const url2 = `${deps.serverApiUrl.replace(/\/$/, "")}/api/admin/logs`;
22730
22777
  const body = {
@@ -22735,7 +22782,8 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
22735
22782
  traceId: args.trace_id,
22736
22783
  module: args.module,
22737
22784
  msgContains: args.msg_contains,
22738
- limit: Number.isFinite(parsedLimit) ? parsedLimit : 500
22785
+ limit: Number.isFinite(parsedLimit) ? parsedLimit : 500,
22786
+ offset: Number.isFinite(parsedOffset) ? parsedOffset : 0
22739
22787
  };
22740
22788
  const res = await fetch(url2, {
22741
22789
  method: "POST",
@@ -22758,7 +22806,9 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
22758
22806
  source,
22759
22807
  count: json2.entries.length,
22760
22808
  truncated: json2.truncated,
22761
- totalScanned: json2.totalScanned
22809
+ totalScanned: json2.totalScanned,
22810
+ totalMatched: json2.totalMatched,
22811
+ nextOffset: json2.nextOffset
22762
22812
  });
22763
22813
  const lines = json2.entries.map((e) => {
22764
22814
  const dataStr = e.data ? ` data=${JSON.stringify(e.data).slice(0, 200)}` : "";
@@ -22771,9 +22821,10 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
22771
22821
  const infoCount = json2.entries.filter((e) => e.level === "INFO").length;
22772
22822
  const traceCount = json2.entries.filter((e) => e.level === "TRACE" || e.level === "DEBUG").length;
22773
22823
  const header = [
22774
- `[${source}] \u5171\u626B ${json2.totalScanned} \u884C\uFF0C\u547D\u4E2D ${json2.entries.length} \u6761`,
22824
+ `[${source}] \u5171\u626B ${json2.totalScanned} \u884C\uFF0C\u5DF2\u8FD4\u56DE ${json2.entries.length} \u6761\uFF0C\u547D\u4E2D ${json2.totalMatched ?? json2.entries.length} \u6761`,
22775
22825
  ` ERROR/FATAL: ${errorCount} | WARN: ${warnCount} | INFO: ${infoCount} | TRACE/DEBUG: ${traceCount}`
22776
- ].join("\n") + (json2.truncated ? "\n\uFF08\u5DF2\u622A\u65AD\uFF0C\u8BF7\u7F29\u5C0F\u65F6\u95F4\u7A97\u6216\u52A0\u8FC7\u6EE4\u6761\u4EF6\u518D\u67E5\uFF09" : "");
22826
+ ].join("\n") + (json2.nextOffset != null ? `
22827
+ nextOffset=${json2.nextOffset}\uFF08\u7EE7\u7EED\u67E5\u8BE2\u65F6\u4F20 offset=${json2.nextOffset}\uFF09` : "");
22777
22828
  const text = [header, "", ...lines].join("\n");
22778
22829
  return { content: [{ type: "text", text }] };
22779
22830
  } catch (e) {
@@ -22860,7 +22911,11 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
22860
22911
  const tiers = await tierRes.json();
22861
22912
  const self = deps.agentRegistry?.getById(deps.agentId);
22862
22913
  const preferredSubscriptionId = parseAgentConfig(self?.config).subscriptionId;
22863
- const tierConfig = tiers.find((t) => t.tier === tier && t.subscriptionId === preferredSubscriptionId) ?? tiers.find((t) => t.tier === tier);
22914
+ const resolvedPreferredSubscriptionId = await resolveTierSubscriptionPreference(
22915
+ preferredSubscriptionId,
22916
+ deps
22917
+ );
22918
+ const tierConfig = tiers.find((t) => t.tier === tier && t.subscriptionId === resolvedPreferredSubscriptionId) ?? tiers.find((t) => t.tier === tier);
22864
22919
  if (tierConfig) {
22865
22920
  agentConfig = JSON.stringify({
22866
22921
  capabilityTier: tier,
@@ -23015,7 +23070,11 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
23015
23070
  if (tierRes.ok) {
23016
23071
  const tiers = await tierRes.json();
23017
23072
  const preferredSubscriptionId = parseAgentConfig(existing.config).subscriptionId;
23018
- const tierConfig = tiers.find((t) => t.tier === tier && t.subscriptionId === preferredSubscriptionId) ?? tiers.find((t) => t.tier === tier);
23073
+ const resolvedPreferredSubscriptionId = await resolveTierSubscriptionPreference(
23074
+ preferredSubscriptionId,
23075
+ deps
23076
+ );
23077
+ const tierConfig = tiers.find((t) => t.tier === tier && t.subscriptionId === resolvedPreferredSubscriptionId) ?? tiers.find((t) => t.tier === tier);
23019
23078
  if (tierConfig) {
23020
23079
  agentConfig = JSON.stringify({
23021
23080
  capabilityTier: tier,
@@ -23646,6 +23705,8 @@ var GroupDispatchMemoryStore = class {
23646
23705
  };
23647
23706
 
23648
23707
  // src/groupInboxPromptBuilder.ts
23708
+ var MAX_SYSTEM_NOTE_COUNT = 8;
23709
+ var MAX_SYSTEM_NOTE_LEN = 1e3;
23649
23710
  function formatHHMM(epochMs) {
23650
23711
  const d = new Date(epochMs);
23651
23712
  const hh = String(d.getHours()).padStart(2, "0");
@@ -23656,6 +23717,37 @@ function truncate(text, maxLen) {
23656
23717
  if (text.length <= maxLen) return text;
23657
23718
  return `${text.slice(0, maxLen)}\u2026`;
23658
23719
  }
23720
+ function formatIsoHHMM(iso) {
23721
+ const epochMs = Date.parse(iso);
23722
+ if (!Number.isFinite(epochMs)) return "";
23723
+ return formatHHMM(epochMs);
23724
+ }
23725
+ function collectSystemNotes(entries) {
23726
+ const byId = /* @__PURE__ */ new Map();
23727
+ for (const entry of entries) {
23728
+ for (const msg of entry.context) {
23729
+ if (msg.role !== "system") continue;
23730
+ byId.set(msg.id, {
23731
+ id: msg.id,
23732
+ time: formatIsoHHMM(msg.createdAt),
23733
+ content: truncate(msg.content, MAX_SYSTEM_NOTE_LEN)
23734
+ });
23735
+ }
23736
+ }
23737
+ return [...byId.values()].slice(-MAX_SYSTEM_NOTE_COUNT);
23738
+ }
23739
+ function appendSystemNotes(lines, entries) {
23740
+ const notes = collectSystemNotes(entries);
23741
+ if (notes.length === 0) return;
23742
+ lines.push(`--- \u7FA4\u5185\u7CFB\u7EDF\u8BB0\u5F55\uFF08${notes.length} \u6761\uFF0C\u4F9B\u4E0A\u4E0B\u6587\uFF1B\u975E\u666E\u901A\u804A\u5929\u5386\u53F2\uFF09---`);
23743
+ lines.push("\u8FD9\u4E9B\u8BB0\u5F55\u53EA\u7528\u4E8E\u7406\u89E3\u7528\u6237\u5DF2\u4F5C\u51FA\u7684 AskUserQuestion/\u7CFB\u7EDF\u51B3\u7B56\uFF1B\u4E0D\u8981\u76F4\u63A5\u56DE\u590D\u7CFB\u7EDF\u8BB0\u5F55\u672C\u8EAB\u3002");
23744
+ for (const note of notes) {
23745
+ const prefix = note.time ? `[${note.time}] system:` : "system:";
23746
+ lines.push(`${prefix} ${note.content}`);
23747
+ }
23748
+ lines.push("--- \u7FA4\u5185\u7CFB\u7EDF\u8BB0\u5F55 end ---");
23749
+ lines.push("");
23750
+ }
23659
23751
  function appendBoardContext(lines, latest) {
23660
23752
  if (latest.boardMeta?.vision) {
23661
23753
  lines.push("--- project vision ---");
@@ -23724,6 +23816,7 @@ function buildGroupInboxPrompt(entries, opts = {}) {
23724
23816
  );
23725
23817
  }
23726
23818
  lines.push("");
23819
+ appendSystemNotes(lines, entries);
23727
23820
  lines.push(`--- \u672A\u8BFB\u7FA4\u6D88\u606F\uFF08${entries.length} \u6761\uFF0C\u6309\u65F6\u95F4\u987A\u5E8F\uFF09---`);
23728
23821
  for (const e of entries) {
23729
23822
  const ts = formatHHMM(e.arrivedAt);
@@ -24067,6 +24160,31 @@ function extractUsage(message) {
24067
24160
  }
24068
24161
  return result;
24069
24162
  }
24163
+ function hasUsageValue(usage) {
24164
+ return typeof usage.tokenCount === "number" || typeof usage.inputTokens === "number" || typeof usage.cacheReadTokens === "number" || typeof usage.cacheCreationTokens === "number" || typeof usage.costUsd === "number";
24165
+ }
24166
+ function emitUsageReported(proc, emit, base, usage, messageId) {
24167
+ if (!hasUsageValue(usage)) return;
24168
+ const groupId = proc.currentTask?.groupId;
24169
+ const scopeKeyValue = proc.scope.kind === "group" ? `group:${proc.scope.groupId}` : "single";
24170
+ emit({
24171
+ type: "agent:usage_reported",
24172
+ payload: {
24173
+ ...wireBase(base),
24174
+ ...messageId ? { messageId } : {},
24175
+ ...groupId ? { groupId } : {},
24176
+ scopeKey: scopeKeyValue,
24177
+ model: usage.model,
24178
+ usage: {
24179
+ inputTokens: usage.inputTokens,
24180
+ outputTokens: usage.tokenCount,
24181
+ cacheReadTokens: usage.cacheReadTokens,
24182
+ cacheCreationTokens: usage.cacheCreationTokens
24183
+ },
24184
+ providerCostUsd: usage.costUsd
24185
+ }
24186
+ });
24187
+ }
24070
24188
  function isGroupTask(proc) {
24071
24189
  return proc.currentTask?.groupId != null;
24072
24190
  }
@@ -24328,7 +24446,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24328
24446
  proc.currentToolName = block.name ?? "unknown";
24329
24447
  proc.accumulatedToolInput = "";
24330
24448
  const toolName = block.name ?? "unknown";
24331
- if (toolName !== "ExitPlanMode" && toolName !== "AskUserQuestion") {
24449
+ if (toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
24332
24450
  emit({
24333
24451
  type: "agent:tool_use",
24334
24452
  payload: {
@@ -24492,9 +24610,9 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24492
24610
  });
24493
24611
  }
24494
24612
  }
24495
- if (proc.currentToolName === "AskUserQuestion") {
24613
+ if (isAskUserQuestionToolName(proc.currentToolName)) {
24496
24614
  const last = proc.contentBlocks[proc.contentBlocks.length - 1];
24497
- if (last?.type === "tool_use" && last.toolName === "AskUserQuestion") {
24615
+ if (last?.type === "tool_use" && isAskUserQuestionToolName(last.toolName)) {
24498
24616
  proc.contentBlocks.pop();
24499
24617
  }
24500
24618
  }
@@ -24546,7 +24664,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24546
24664
  const toolName = proc.currentToolName ?? "unknown";
24547
24665
  const isError = toolName === "ExitPlanMode" ? false : !!b.is_error;
24548
24666
  const output = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
24549
- if (toolName === "AskUserQuestion") {
24667
+ if (isAskUserQuestionToolName(toolName)) {
24550
24668
  proc.currentToolName = null;
24551
24669
  continue;
24552
24670
  }
@@ -24630,6 +24748,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24630
24748
  const watermarkUsage = proc.peakContextUsage ?? usage;
24631
24749
  if (trimmed === NO_REPLY_TOKEN) {
24632
24750
  checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
24751
+ emitUsageReported(proc, emit, base, usage);
24633
24752
  if (groupMode && proc.contentBlocks.length > 0) {
24634
24753
  emitGroupSegment(
24635
24754
  proc,
@@ -24669,6 +24788,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24669
24788
  }
24670
24789
  if (groupMode) {
24671
24790
  checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
24791
+ emitUsageReported(proc, emit, base, usage);
24672
24792
  if (proc.contentBlocks.length > 0) {
24673
24793
  logger8.info("Group turn trailing audit segment", {
24674
24794
  agentId: proc.agentId,
@@ -24703,6 +24823,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24703
24823
  }
24704
24824
  if (proc.accumulatedText.length === 0 && proc.contentBlocks.length === 0) {
24705
24825
  cleanupPlanMode(proc, emit, base, "error");
24826
+ emitUsageReported(proc, emit, base, usage);
24706
24827
  logger8.warn("SDK success produced empty assistant output; emitting agent:error", {
24707
24828
  agentId: proc.agentId,
24708
24829
  ackId: base.replyMessageId,
@@ -24730,6 +24851,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24730
24851
  checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
24731
24852
  cleanupPlanMode(proc, emit, base, "success");
24732
24853
  carrierMessageId = createMessageId();
24854
+ emitUsageReported(proc, emit, base, usage, carrierMessageId);
24733
24855
  logger8.info("Agent task done, emitting agent:done", {
24734
24856
  agentId: proc.agentId,
24735
24857
  ackId: base.replyMessageId,
@@ -24750,6 +24872,11 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
24750
24872
  thinkingDuration: Date.now() - proc.currentTaskStartedAt,
24751
24873
  toolCallCount: proc.contentBlocks.filter((b) => b.type === "tool_use").length,
24752
24874
  tokenCount: usage.tokenCount,
24875
+ outputTokens: usage.tokenCount,
24876
+ inputTokens: usage.inputTokens,
24877
+ cacheReadTokens: usage.cacheReadTokens,
24878
+ cacheCreationTokens: usage.cacheCreationTokens,
24879
+ costUsd: usage.costUsd,
24753
24880
  model: usage.model
24754
24881
  }
24755
24882
  }
@@ -25075,6 +25202,12 @@ var wsMetrics = new WsMetrics();
25075
25202
 
25076
25203
  // src/agentManager.ts
25077
25204
  var logger11 = createModuleLogger("agent.manager");
25205
+ function missingSubscriptionMessage(subscriptionId) {
25206
+ if (isPrimaryCompanySubscriptionId(subscriptionId)) {
25207
+ return "AHChat \u9ED8\u8BA4\u6A21\u578B\u6682\u65F6\u4E0D\u53EF\u7528\uFF1A\u8FD9\u53F0\u673A\u5668\u8FD8\u6CA1\u6709\u540C\u6B65\u5230\u7BA1\u7406\u5458\u542F\u7528\u7684\u9ED8\u8BA4\u6A21\u578B\uFF0C\u8BF7\u66F4\u65B0\u5E76\u91CD\u542F Bridge \u540E\u91CD\u8BD5\u3002";
25208
+ }
25209
+ return `\u8BA2\u9605\u6E90\u4E0D\u53EF\u7528\uFF1A${subscriptionId}\u3002\u8BF7\u91CD\u65B0\u9009\u62E9\u6A21\u578B\u6765\u6E90\uFF0C\u6216\u91CD\u542F Bridge \u540E\u91CD\u8BD5\u3002`;
25210
+ }
25078
25211
  var NODE_USER_UID = 1e3;
25079
25212
  var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
25080
25213
  var DOCUMENT_READING_RULES = `DOCUMENT READING:
@@ -25139,6 +25272,27 @@ var BridgeBusyError = class extends Error {
25139
25272
  this.name = "BridgeBusyError";
25140
25273
  }
25141
25274
  };
25275
+ function senderLabelForQuote(message) {
25276
+ if (message.senderAgentName) return message.senderAgentName;
25277
+ if (message.role === "user") return "user";
25278
+ if (message.role === "agent") return `agent:${message.senderAgentId ?? "unknown"}`;
25279
+ return "system";
25280
+ }
25281
+ function buildSingleReplyPrompt(task) {
25282
+ if (!task.replyToMessage) return task.content;
25283
+ const quoted = task.replyToMessage;
25284
+ const label = senderLabelForQuote(quoted);
25285
+ return [
25286
+ "--- reply-to message ---",
25287
+ `You are replying to this earlier message from [${label}]:`,
25288
+ quoted.content || (quoted.attachments?.length ? "[attachment]" : ""),
25289
+ "--- end reply-to message ---",
25290
+ "",
25291
+ "--- user message ---",
25292
+ task.content,
25293
+ "--- end user message ---"
25294
+ ].join("\n");
25295
+ }
25142
25296
  var AgentManager = class {
25143
25297
  agents = /* @__PURE__ */ new Map();
25144
25298
  lastUsedAt = /* @__PURE__ */ new Map();
@@ -25274,16 +25428,16 @@ var AgentManager = class {
25274
25428
  agentId: agent.id,
25275
25429
  subscriptionId: cfg.subscriptionId
25276
25430
  });
25277
- return cfg;
25431
+ throw new Error(missingSubscriptionMessage(cfg.subscriptionId));
25278
25432
  }
25279
25433
  let sub = this.subscriptionRegistry.getById(cfg.subscriptionId);
25280
25434
  if (!sub) sub = await this.subscriptionRegistry.fetchById(cfg.subscriptionId);
25281
25435
  if (!sub) {
25282
- logger11.warn("Subscription not found; falling back to system OAuth", {
25436
+ logger11.warn("Subscription not found; refusing native OAuth fallback", {
25283
25437
  agentId: agent.id,
25284
25438
  subscriptionId: cfg.subscriptionId
25285
25439
  });
25286
- return { ...cfg, subscriptionType: "system" };
25440
+ throw new Error(missingSubscriptionMessage(cfg.subscriptionId));
25287
25441
  }
25288
25442
  const isPinnedModel = cfg.model && cfg.model !== "default";
25289
25443
  const resolvedModel = isPinnedModel ? cfg.model : sub.defaultModel;
@@ -26482,8 +26636,9 @@ ${lines.join("\n")}`;
26482
26636
  });
26483
26637
  }
26484
26638
  async pushTaskContent(runtime, task, onYielded) {
26639
+ const textContent = buildSingleReplyPrompt(task);
26485
26640
  if (!task.attachments || task.attachments.length === 0) {
26486
- runtime.inputController.push(task.content, runtime.ccSessionId ?? "", onYielded);
26641
+ runtime.inputController.push(textContent, runtime.ccSessionId ?? "", onYielded);
26487
26642
  return;
26488
26643
  }
26489
26644
  const supportsVision = await this.detectVisionSupport();
@@ -26495,7 +26650,7 @@ ${lines.join("\n")}`;
26495
26650
  supportsVision
26496
26651
  }
26497
26652
  );
26498
- const text = task.content.trim() || "\u8BF7\u67E5\u770B\u6211\u53D1\u9001\u7684\u9644\u4EF6\u3002";
26653
+ const text = textContent.trim() || "\u8BF7\u67E5\u770B\u6211\u53D1\u9001\u7684\u9644\u4EF6\u3002";
26499
26654
  const contentParts = [
26500
26655
  { type: "text", text },
26501
26656
  ...attachmentBlocks
@@ -26533,9 +26688,12 @@ ${lines.join("\n")}`;
26533
26688
  return materialized;
26534
26689
  }
26535
26690
  async resolveExistingWorkspaceAttachmentPath(runtime, attachment) {
26691
+ const localWorkspacePath = attachment.metadata?.localWorkspacePath;
26536
26692
  const workspacePath = attachment.metadata?.workspacePath;
26537
- if (typeof workspacePath !== "string" || !workspacePath.trim()) return null;
26538
- const candidate = path11.resolve(workspacePath);
26693
+ const rawPath = typeof localWorkspacePath === "string" && localWorkspacePath.trim() ? localWorkspacePath : workspacePath;
26694
+ if (typeof rawPath !== "string" || !rawPath.trim()) return null;
26695
+ const remapped = remapServerWorkspacePath(rawPath, this.workspacesDir);
26696
+ const candidate = path11.resolve(remapped.path);
26539
26697
  if (!this.isPathInsideBase(candidate, runtime.cwd)) return null;
26540
26698
  try {
26541
26699
  const stat3 = await fs5.stat(candidate);
@@ -26545,6 +26703,8 @@ ${lines.join("\n")}`;
26545
26703
  agentId: runtime.agentId,
26546
26704
  attachmentId: attachment.id,
26547
26705
  workspacePath,
26706
+ localWorkspacePath,
26707
+ resolvedPath: candidate,
26548
26708
  error: e
26549
26709
  });
26550
26710
  return null;
@@ -27743,7 +27903,7 @@ function computeBoardSignature(entry) {
27743
27903
 
27744
27904
  // src/bridgeFetchAuth.ts
27745
27905
  var logger12 = createModuleLogger("bridge.fetchAuth");
27746
- var BRIDGE_TOKEN_HEADER = "X-AHChat-Bridge-Token";
27906
+ var BRIDGE_TOKEN_HEADER2 = "X-AHChat-Bridge-Token";
27747
27907
  function installBridgeFetchAuth(serverApiUrl, token) {
27748
27908
  if (typeof globalThis.fetch !== "function") {
27749
27909
  logger12.warn("globalThis.fetch not available, cannot install bridge fetch auth");
@@ -27771,8 +27931,8 @@ function installBridgeFetchAuth(serverApiUrl, token) {
27771
27931
  else url2 = input.url;
27772
27932
  if (!shouldInject(url2)) return original(input, init);
27773
27933
  const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
27774
- if (!headers.has(BRIDGE_TOKEN_HEADER)) {
27775
- headers.set(BRIDGE_TOKEN_HEADER, token);
27934
+ if (!headers.has(BRIDGE_TOKEN_HEADER2)) {
27935
+ headers.set(BRIDGE_TOKEN_HEADER2, token);
27776
27936
  }
27777
27937
  return original(input, { ...init, headers });
27778
27938
  });
@@ -27782,13 +27942,6 @@ function installBridgeFetchAuth(serverApiUrl, token) {
27782
27942
  });
27783
27943
  }
27784
27944
 
27785
- // src/bridgeHttp.ts
27786
- var BRIDGE_TOKEN_HEADER2 = "X-AHChat-Bridge-Token";
27787
- function bridgeAuthHeaders(bridgeToken) {
27788
- const token = bridgeToken?.trim();
27789
- return token ? { [BRIDGE_TOKEN_HEADER2]: token } : {};
27790
- }
27791
-
27792
27945
  // src/agentRegistry.ts
27793
27946
  var logger13 = createModuleLogger("agent.registry");
27794
27947
  var HttpAgentRegistry = class {
@@ -27902,6 +28055,23 @@ var HttpSubscriptionRegistry = class {
27902
28055
  const path24 = suffix.startsWith("/") ? suffix : `/${suffix}`;
27903
28056
  return `${base}${path24}`;
27904
28057
  }
28058
+ primaryAliasFrom(sub) {
28059
+ return {
28060
+ ...sub,
28061
+ id: PRIMARY_COMPANY_SUBSCRIPTION_ID,
28062
+ name: "\u516C\u53F8\u4E3B\u8BA2\u9605",
28063
+ resolvedSubscriptionId: sub.id,
28064
+ isVirtual: true
28065
+ };
28066
+ }
28067
+ rebuildPrimaryAlias() {
28068
+ this.subscriptions.delete(PRIMARY_COMPANY_SUBSCRIPTION_ID);
28069
+ const primary = Array.from(this.subscriptions.values()).find(
28070
+ (sub) => sub.billingMode === "company_billable" && sub.isPrimaryCompany === true
28071
+ );
28072
+ if (!primary) return;
28073
+ this.subscriptions.set(PRIMARY_COMPANY_SUBSCRIPTION_ID, this.primaryAliasFrom(primary));
28074
+ }
27905
28075
  async refresh() {
27906
28076
  const attempt = async () => {
27907
28077
  try {
@@ -27929,6 +28099,7 @@ var HttpSubscriptionRegistry = class {
27929
28099
  const s = item;
27930
28100
  if (s && typeof s.id === "string") this.subscriptions.set(s.id, s);
27931
28101
  }
28102
+ this.rebuildPrimaryAlias();
27932
28103
  logger14.info("Subscription registry refreshed", { count: this.subscriptions.size });
27933
28104
  } catch (e) {
27934
28105
  logger14.warn("Subscription registry parse failed", { error: e });
@@ -27938,6 +28109,10 @@ var HttpSubscriptionRegistry = class {
27938
28109
  return this.subscriptions.get(id) ?? null;
27939
28110
  }
27940
28111
  async fetchById(id) {
28112
+ if (isPrimaryCompanySubscriptionId(id)) {
28113
+ await this.refresh();
28114
+ return this.getById(id);
28115
+ }
27941
28116
  try {
27942
28117
  const res = await fetch(this.apiUrl(`/api/subscriptions/${encodeURIComponent(id)}`), {
27943
28118
  headers: bridgeAuthHeaders(this.bridgeToken)
@@ -27953,9 +28128,11 @@ var HttpSubscriptionRegistry = class {
27953
28128
  }
27954
28129
  upsert(sub) {
27955
28130
  this.subscriptions.set(sub.id, sub);
28131
+ this.rebuildPrimaryAlias();
27956
28132
  }
27957
28133
  remove(id) {
27958
28134
  this.subscriptions.delete(id);
28135
+ this.rebuildPrimaryAlias();
27959
28136
  }
27960
28137
  };
27961
28138
 
@@ -28894,7 +29071,6 @@ import path14 from "path";
28894
29071
  import os8 from "os";
28895
29072
  import readline from "readline";
28896
29073
  var logger19 = createModuleLogger("bridge.logScanner");
28897
- var DEFAULT_LIMIT = 500;
28898
29074
  var MAX_LIMIT = 2e3;
28899
29075
  function listLogFiles(logsDir, baseName) {
28900
29076
  let names;
@@ -28907,7 +29083,7 @@ function listLogFiles(logsDir, baseName) {
28907
29083
  const pattern = new RegExp(`^${baseName.replace(".", "\\.")}(\\.\\d+)?$`);
28908
29084
  return names.filter((n) => pattern.test(n)).map((n) => path14.join(logsDir, n));
28909
29085
  }
28910
- async function scanFile(filePath, source, filter, limit, state) {
29086
+ async function scanFile(filePath, source, filter, state) {
28911
29087
  const file2 = path14.basename(filePath);
28912
29088
  const stream = fs8.createReadStream(filePath, { encoding: "utf-8" });
28913
29089
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
@@ -28919,31 +29095,36 @@ async function scanFile(filePath, source, filter, limit, state) {
28919
29095
  if (!parsed) continue;
28920
29096
  if (parsed.source !== source) continue;
28921
29097
  if (!matchesFilter(parsed, filter)) continue;
28922
- state.hits.push({ ...parsed, file: file2, lineNum });
28923
- if (state.hits.length > limit) {
28924
- state.truncated = true;
28925
- state.hits.length = limit;
28926
- }
29098
+ state.hits.push({ ...parsed, raw: line, file: file2, lineNum });
28927
29099
  }
28928
29100
  }
28929
29101
  async function scanLocalLogs(logsDir, baseName, filter) {
28930
29102
  const source = filter.source;
28931
- const limit = Math.min(Math.max(filter.limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);
29103
+ const limit = filter.limit == null ? null : Math.min(Math.max(filter.limit, 1), MAX_LIMIT);
29104
+ const offset = Math.max(filter.offset ?? 0, 0);
28932
29105
  const files = listLogFiles(logsDir, baseName);
28933
29106
  const state = { hits: [], totalScanned: 0, truncated: false };
28934
29107
  for (const filePath of files) {
28935
- if (state.truncated) break;
28936
29108
  try {
28937
- await scanFile(filePath, source, filter, limit, state);
29109
+ await scanFile(filePath, source, filter, state);
28938
29110
  } catch (e) {
28939
29111
  logger19.warn("scanLocalLogs: file read failed", { filePath, error: e });
28940
29112
  }
28941
29113
  }
28942
- state.hits.sort((a, b) => a.ts.localeCompare(b.ts));
29114
+ state.hits.sort((a, b) => b.ts.localeCompare(a.ts));
29115
+ const totalMatched = state.hits.length;
29116
+ const endOffset = limit === null ? totalMatched : offset + limit;
29117
+ const entries = state.hits.slice(offset, endOffset);
29118
+ const nextOffset = endOffset < totalMatched ? endOffset : void 0;
29119
+ if (nextOffset !== void 0) {
29120
+ state.truncated = true;
29121
+ }
28943
29122
  return {
28944
- entries: state.hits,
29123
+ entries,
28945
29124
  truncated: state.truncated,
28946
- totalScanned: state.totalScanned
29125
+ totalScanned: state.totalScanned,
29126
+ totalMatched,
29127
+ nextOffset
28947
29128
  };
28948
29129
  }
28949
29130
  async function scanBridgeLogs(filter) {
@@ -28957,6 +29138,8 @@ async function scanBridgeLogs(filter) {
28957
29138
  const result = await scanLocalLogs(logDir, "bridge.log", { ...filter, source: "bridge" });
28958
29139
  logger19.info("scanBridgeLogs complete", {
28959
29140
  hitCount: result.entries.length,
29141
+ totalMatched: result.totalMatched,
29142
+ nextOffset: result.nextOffset,
28960
29143
  totalScanned: result.totalScanned,
28961
29144
  truncated: result.truncated
28962
29145
  });
@@ -29029,6 +29212,10 @@ function isProcessAlive(pid) {
29029
29212
  } catch (e) {
29030
29213
  const err = e;
29031
29214
  if (err.code === "ESRCH") return false;
29215
+ if (err.code === "EPERM") {
29216
+ logger21.warn("Treating inaccessible lock PID as stale", { pid, error: e });
29217
+ return false;
29218
+ }
29032
29219
  throw e;
29033
29220
  }
29034
29221
  }
@@ -29154,6 +29341,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
29154
29341
  conversationId: payload.conversationId,
29155
29342
  content: payload.content,
29156
29343
  attachments: payload.attachments ?? void 0,
29344
+ replyToMessage: payload.replyToMessage ?? void 0,
29157
29345
  replyMessageId: payload.ackId,
29158
29346
  traceId: payload.traceId,
29159
29347
  planMode: payload.planMode ?? void 0
@@ -29480,9 +29668,11 @@ var SessionStore = class {
29480
29668
  };
29481
29669
 
29482
29670
  // src/claudeRuntime.ts
29483
- import { accessSync as accessSync2, constants as constants3, existsSync as existsSync2 } from "fs";
29671
+ import { accessSync as accessSync2, constants as constants3, existsSync as existsSync2, readdirSync as readdirSync2, realpathSync } from "fs";
29672
+ import { createRequire } from "module";
29484
29673
  import path18 from "path";
29485
29674
  var logger25 = createModuleLogger("bridge.claudeRuntime");
29675
+ var require2 = createRequire(import.meta.url);
29486
29676
  var EXPLICIT_CLAUDE_EXECUTABLE_ENV = "AHCHAT_CLAUDE_EXECUTABLE";
29487
29677
  var BUNDLED_CLAUDE_PATH_ENV = "AHCHAT_BUNDLED_CLAUDE_PATH";
29488
29678
  function normalizePath(value) {
@@ -29532,6 +29722,118 @@ function validateCandidate(source, candidatePath) {
29532
29722
  version: version2
29533
29723
  };
29534
29724
  }
29725
+ function getSdkRuntimeTargets() {
29726
+ const binaryName = process.platform === "win32" ? "claude.exe" : "claude";
29727
+ const targetPrefix = "@anthropic-ai/claude-agent-sdk";
29728
+ if (process.platform === "darwin") {
29729
+ if (process.arch === "arm64" || process.arch === "x64") {
29730
+ return [{ packageName: `${targetPrefix}-darwin-${process.arch}`, binaryName }];
29731
+ }
29732
+ return [];
29733
+ }
29734
+ if (process.platform === "win32") {
29735
+ if (process.arch === "arm64" || process.arch === "x64") {
29736
+ return [{ packageName: `${targetPrefix}-win32-${process.arch}`, binaryName }];
29737
+ }
29738
+ return [];
29739
+ }
29740
+ if (process.platform === "linux") {
29741
+ if (process.arch === "arm64" || process.arch === "x64") {
29742
+ return [
29743
+ { packageName: `${targetPrefix}-linux-${process.arch}`, binaryName },
29744
+ { packageName: `${targetPrefix}-linux-${process.arch}-musl`, binaryName }
29745
+ ];
29746
+ }
29747
+ }
29748
+ return [];
29749
+ }
29750
+ function safeResolve(specifier, paths) {
29751
+ try {
29752
+ return require2.resolve(specifier, paths ? { paths } : void 0);
29753
+ } catch {
29754
+ return void 0;
29755
+ }
29756
+ }
29757
+ function resolveClaudeAgentSdkDir() {
29758
+ const sdkEntry = safeResolve("@anthropic-ai/claude-agent-sdk");
29759
+ if (!sdkEntry) return void 0;
29760
+ try {
29761
+ return path18.dirname(realpathSync(sdkEntry));
29762
+ } catch {
29763
+ return path18.dirname(sdkEntry);
29764
+ }
29765
+ }
29766
+ function findPnpmStoreDir(fromDir) {
29767
+ let current = fromDir;
29768
+ while (current !== path18.dirname(current)) {
29769
+ if (path18.basename(current) === ".pnpm") return current;
29770
+ current = path18.dirname(current);
29771
+ }
29772
+ return void 0;
29773
+ }
29774
+ function resolvePnpmRuntimeBinary(sdkDir, target) {
29775
+ const pnpmStoreDir = findPnpmStoreDir(sdkDir);
29776
+ if (!pnpmStoreDir) return void 0;
29777
+ const encodedName = target.packageName.replace("/", "+");
29778
+ let entries;
29779
+ try {
29780
+ entries = readdirSync2(pnpmStoreDir);
29781
+ } catch {
29782
+ return void 0;
29783
+ }
29784
+ for (const entry of entries) {
29785
+ if (!entry.startsWith(`${encodedName}@`)) continue;
29786
+ const candidate = path18.join(
29787
+ pnpmStoreDir,
29788
+ entry,
29789
+ "node_modules",
29790
+ ...target.packageName.split("/"),
29791
+ target.binaryName
29792
+ );
29793
+ if (existsSync2(candidate)) return candidate;
29794
+ }
29795
+ return void 0;
29796
+ }
29797
+ function resolveSdkRuntimeBinary(target) {
29798
+ const directPath = safeResolve(`${target.packageName}/${target.binaryName}`);
29799
+ if (directPath && existsSync2(directPath)) return directPath;
29800
+ const sdkDir = resolveClaudeAgentSdkDir();
29801
+ if (!sdkDir) return void 0;
29802
+ const scopedPackageName = target.packageName.split("/")[1] ?? target.packageName;
29803
+ const candidates = [
29804
+ safeResolve(`${target.packageName}/${target.binaryName}`, [sdkDir]),
29805
+ path18.join(sdkDir, "..", scopedPackageName, target.binaryName),
29806
+ path18.join(sdkDir, "node_modules", ...target.packageName.split("/"), target.binaryName),
29807
+ resolvePnpmRuntimeBinary(sdkDir, target)
29808
+ ].filter((candidate) => Boolean(candidate));
29809
+ return candidates.find((candidate) => existsSync2(candidate));
29810
+ }
29811
+ function resolveSdkBundledAttempts() {
29812
+ const targets = getSdkRuntimeTargets();
29813
+ if (targets.length === 0) {
29814
+ return [{
29815
+ source: "sdk-bundled",
29816
+ ok: false,
29817
+ error: `unsupported platform for Claude Agent SDK runtime: ${process.platform}-${process.arch}`
29818
+ }];
29819
+ }
29820
+ const attempts = [];
29821
+ for (const target of targets) {
29822
+ const binaryPath = resolveSdkRuntimeBinary(target);
29823
+ if (!binaryPath) {
29824
+ attempts.push({
29825
+ source: "sdk-bundled",
29826
+ ok: false,
29827
+ error: `optional runtime package ${target.packageName} is not installed`
29828
+ });
29829
+ continue;
29830
+ }
29831
+ const attempt = validateCandidate("sdk-bundled", binaryPath);
29832
+ attempts.push(attempt);
29833
+ if (attempt.ok) break;
29834
+ }
29835
+ return attempts;
29836
+ }
29535
29837
  function resolvePathCandidate() {
29536
29838
  const resolved = resolveCommand(["claude", "anthropic-cli"]);
29537
29839
  if (!resolved) return void 0;
@@ -29539,12 +29841,13 @@ function resolvePathCandidate() {
29539
29841
  }
29540
29842
  function buildMissingError(attempts) {
29541
29843
  if (attempts.length === 0) {
29542
- return "Claude runtime not found. Install Claude Code manually or use the bundled desktop runtime.";
29844
+ return "Claude runtime not found. Reinstall @fangyb/ahchat-bridge with npm optional dependencies, install Claude Code manually, or use the bundled desktop runtime.";
29543
29845
  }
29544
- return attempts.map((attempt) => {
29846
+ const attemptDetails = attempts.map((attempt) => {
29545
29847
  const pathSuffix = attempt.path ? ` at ${attempt.path}` : "";
29546
29848
  return `${attempt.source}${pathSuffix}: ${attempt.error ?? "unavailable"}`;
29547
29849
  }).join("; ");
29850
+ return `Claude runtime not found. ${attemptDetails}. Reinstall @fangyb/ahchat-bridge with npm optional dependencies, set AHCHAT_CLAUDE_EXECUTABLE, install Claude Code manually, or use the bundled desktop runtime.`;
29548
29851
  }
29549
29852
  function resolveClaudeRuntime(env2 = process.env) {
29550
29853
  const attempts = [];
@@ -29582,6 +29885,19 @@ function resolveClaudeRuntime(env2 = process.env) {
29582
29885
  };
29583
29886
  }
29584
29887
  }
29888
+ const sdkAttempts = resolveSdkBundledAttempts();
29889
+ for (const attempt of sdkAttempts) {
29890
+ attempts.push(attempt);
29891
+ if (attempt.ok) {
29892
+ return {
29893
+ ok: true,
29894
+ source: "sdk-bundled",
29895
+ path: attempt.path,
29896
+ version: attempt.version,
29897
+ attempts
29898
+ };
29899
+ }
29900
+ }
29585
29901
  const pathAttempt = resolvePathCandidate();
29586
29902
  if (pathAttempt) {
29587
29903
  attempts.push(pathAttempt);
@@ -30014,7 +30330,7 @@ Claude runtime is unavailable.
30014
30330
 
30015
30331
  ${claudeRuntime.error ?? "Install Claude Code manually or use the bundled desktop runtime."}
30016
30332
 
30017
- Set AHCHAT_CLAUDE_EXECUTABLE to a valid Claude Code binary path, install Claude Code, or use AHChat Desktop with its bundled runtime.
30333
+ Reinstall @fangyb/ahchat-bridge with npm optional dependencies, set AHCHAT_CLAUDE_EXECUTABLE to a valid Claude Code binary path, install Claude Code, or use AHChat Desktop with its bundled runtime.
30018
30334
  `
30019
30335
  );
30020
30336
  process.exit(1);
@@ -30097,6 +30413,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
30097
30413
  onConnected: async () => {
30098
30414
  await agentRegistry.refresh();
30099
30415
  await groupRegistry.refresh();
30416
+ await subscriptionRegistry.refresh();
30100
30417
  await agentManager.recoverFromRestart(agentRegistry.getAll());
30101
30418
  },
30102
30419
  onServerPush: async (msg) => {
@@ -30224,7 +30541,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
30224
30541
  const entries = await listDirectoryEntries(resolved.path);
30225
30542
  connector?.send({
30226
30543
  type: "bridge:list_dir_response",
30227
- payload: { requestId, entries }
30544
+ payload: { requestId, entries, localPath: resolved.path }
30228
30545
  });
30229
30546
  logger29.info("list_dir response sent", { requestId, count: entries.length });
30230
30547
  } catch (e) {
@@ -30261,6 +30578,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
30261
30578
  payload: {
30262
30579
  requestId,
30263
30580
  path: written.path,
30581
+ bridgePath: written.path,
30264
30582
  relativePath: written.relativePath,
30265
30583
  size: written.size
30266
30584
  }
@@ -30355,7 +30673,9 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
30355
30673
  endIso: filter.endIso,
30356
30674
  traceId: filter.traceId,
30357
30675
  module: filter.module,
30358
- levelMin: filter.levelMin
30676
+ levelMin: filter.levelMin,
30677
+ limit: filter.limit,
30678
+ offset: filter.offset
30359
30679
  });
30360
30680
  try {
30361
30681
  const result = await scanBridgeLogs({ ...filter, source: "bridge" });
@@ -30365,13 +30685,17 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
30365
30685
  requestId,
30366
30686
  entries: result.entries,
30367
30687
  truncated: result.truncated,
30368
- totalScanned: result.totalScanned
30688
+ totalScanned: result.totalScanned,
30689
+ totalMatched: result.totalMatched,
30690
+ nextOffset: result.nextOffset
30369
30691
  }
30370
30692
  });
30371
30693
  logger29.info("fetch_logs response sent", {
30372
30694
  requestId,
30373
30695
  count: result.entries.length,
30374
- truncated: result.truncated
30696
+ truncated: result.truncated,
30697
+ totalMatched: result.totalMatched,
30698
+ nextOffset: result.nextOffset
30375
30699
  });
30376
30700
  } catch (e) {
30377
30701
  const err = e instanceof Error ? e.message : String(e);