leadsradar-agent 1.0.0 → 1.0.1

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/README.md CHANGED
@@ -58,7 +58,7 @@ Chrome 探栗插件(数据全部在本机浏览器内采集)
58
58
 
59
59
  **方式一 · WorkBuddy / 豆包工作(推荐,全自动)**
60
60
 
61
- 1. 打开插件**设置页 → 智能体接管**,选择你的宿主
61
+ 1. 打开插件**设置页 → 智能体连接**,选择你的宿主
62
62
  2. 点**「一键复制 XX 提示词」**(接入包自动下载到本机)
63
63
  3. 粘贴给你的 AI,照它提示操作——你唯一要动手的是在插件里**核对指纹码后点「批准」**
64
64
 
@@ -3733,7 +3733,7 @@ import {
3733
3733
  createHash,
3734
3734
  createPrivateKey
3735
3735
  } from "node:crypto";
3736
- import { readFileSync, writeFileSync, existsSync } from "node:fs";
3736
+ import { readFileSync, writeFileSync, existsSync, readdirSync, copyFileSync, unlinkSync } from "node:fs";
3737
3737
  import { homedir } from "node:os";
3738
3738
  import { fileURLToPath } from "node:url";
3739
3739
  import { dirname, join } from "node:path";
@@ -3742,7 +3742,7 @@ import { createServer as createHttpServer } from "node:http";
3742
3742
  var __dirname = dirname(fileURLToPath(import.meta.url));
3743
3743
  var PORT = Number(process.env.LEADSRADAR_AGENT_PORT) || 17373;
3744
3744
  var PROTOCOL_VERSION = "3";
3745
- var ADAPTER_VERSION = "1.2.15";
3745
+ var ADAPTER_VERSION = "1.2.18";
3746
3746
  var HOST_LABELS = {
3747
3747
  "doubao-work": "\u8C46\u5305\u5DE5\u4F5C",
3748
3748
  // ⭐r85 字节系多客户端实测(用户已验证豆包客户端支持 stdio 连接器,与豆包工作同源):
@@ -3767,7 +3767,7 @@ var HOST_LABELS = {
3767
3767
  };
3768
3768
  var HOST = (process.env.LEADSRADAR_AGENT_HOST || "doubao-work").toLowerCase();
3769
3769
  var HTTP_MODE = process.argv.includes("--http") || process.env.LEADSRADAR_AGENT_HTTP === "1";
3770
- var HTTP_PORT = Number(process.env.LEADSRADAR_AGENT_HTTP_PORT) || 17374;
3770
+ var HTTP_PORT = Number(process.env.LEADSRADAR_AGENT_HTTP_PORT) || 17375;
3771
3771
  var DEVICE_NAME = process.env.LEADSRADAR_AGENT_DEVICE_NAME || `\u63A2\u6817 Agent (${HOST_LABELS[HOST] || "\u672C\u673A"})`;
3772
3772
  var DEVICE_ID = `leadsradar-agent-${HOST.replace(/[^a-z0-9-]/g, "") || "local"}`;
3773
3773
  var KEY_FILE = join(homedir(), `.leadsradar-agent-key-${HOST}.json`);
@@ -3775,6 +3775,58 @@ var LEGACY_KEY_FILE = join(__dirname, ".agent-key.json");
3775
3775
  function log(...args) {
3776
3776
  process.stderr.write(args.join(" ") + "\n");
3777
3777
  }
3778
+ var __extProfileCache = { at: 0, result: null };
3779
+ function detectExtensionProfiles() {
3780
+ if (Date.now() - __extProfileCache.at < 1e4) return __extProfileCache.result;
3781
+ const found = [];
3782
+ const roots = [
3783
+ { browser: "chrome", dir: join(homedir(), "AppData/Local/Google/Chrome/User Data") },
3784
+ { browser: "msedge", dir: join(homedir(), "AppData/Local/Microsoft/Edge/User Data") }
3785
+ ];
3786
+ for (const { browser, dir } of roots) {
3787
+ let profiles = [];
3788
+ try {
3789
+ profiles = readdirSync(dir).filter((n) => n === "Default" || /^Profile /.test(n));
3790
+ } catch {
3791
+ continue;
3792
+ }
3793
+ for (const prof of profiles) {
3794
+ if (found.some((f) => f.browser === browser && f.profile === prof)) continue;
3795
+ for (const file of ["Secure Preferences", "Preferences"]) {
3796
+ const p = join(dir, prof, file);
3797
+ let text = null;
3798
+ try {
3799
+ const tmp = `${p}.lf-probe.tmp`;
3800
+ copyFileSync(p, tmp);
3801
+ text = readFileSync(tmp, "utf8");
3802
+ unlinkSync(tmp);
3803
+ } catch {
3804
+ continue;
3805
+ }
3806
+ try {
3807
+ const settings = JSON.parse(text)?.extensions?.settings || {};
3808
+ for (const v of Object.values(settings)) {
3809
+ const extPath = String(v?.path || "");
3810
+ if (extPath && /leads?find|leads?radar/i.test(extPath)) {
3811
+ found.push({ browser, profile: prof });
3812
+ break;
3813
+ }
3814
+ }
3815
+ } catch {
3816
+ }
3817
+ if (found.some((f) => f.browser === browser && f.profile === prof)) break;
3818
+ }
3819
+ }
3820
+ }
3821
+ __extProfileCache = { at: Date.now(), result: found };
3822
+ return found;
3823
+ }
3824
+ function extensionLaunchHint() {
3825
+ const found = detectExtensionProfiles();
3826
+ if (!found.length) return null;
3827
+ const cmd = found.map((f) => `start ${f.browser} '-profile-directory="${f.profile}"'`).join(" \u6216 ");
3828
+ return `\u5DF2\u63A2\u6D4B\u5230\u63D2\u4EF6\u88C5\u5728 ${found.map((f) => `${f.browser === "msedge" ? "Edge" : "Chrome"} \u7684 ${f.profile}`).join(" \u548C ")}\u2014\u2014\u82E5\u4F60\u6709\u7EC8\u7AEF\u6743\u9650\uFF0C\u8FD0\u884C ${cmd} \u6253\u5F00\u5BF9\u5E94\u7A97\u53E3\uFF0C\u63D2\u4EF6\u7EA6 5 \u79D2\u81EA\u52A8\u91CD\u8FDE\u540E\u91CD\u8BD5`;
3829
+ }
3778
3830
  function loadOrCreateKeys() {
3779
3831
  for (const f of [KEY_FILE, LEGACY_KEY_FILE]) {
3780
3832
  if (!existsSync(f)) continue;
@@ -3829,6 +3881,7 @@ var AgentBroker = class {
3829
3881
  this.awaitingSince = 0;
3830
3882
  this.selfPaired = false;
3831
3883
  this.lastDisconnect = "";
3884
+ this.lastClose = null;
3832
3885
  this.client = null;
3833
3886
  this.downstreams = /* @__PURE__ */ new Map();
3834
3887
  this.relay = null;
@@ -4107,6 +4160,9 @@ var AgentBroker = class {
4107
4160
  extensionVersion: String(msg.extensionVersion || ""),
4108
4161
  sessionId: String(msg.sessionId || ""),
4109
4162
  pendingResultCount: Number(msg.pendingResultCount) || 0,
4163
+ // ⭐B7b-P1 未领取结果摘要透传(connection_status 展示 + claim_pending_result 领取引导);
4164
+ // 旧版插件 hello 无此字段=空数组(不保留上次值:摘要必须与插件真源同步,宁缺勿旧)
4165
+ pendingResults: Array.isArray(msg.pendingResults) ? msg.pendingResults.slice(0, 10) : [],
4110
4166
  // ⭐r81 P0-3 登录态随握手上报(宿主 connection_status 可见,需登录的动作可动手前预检);
4111
4167
  // 旧 hello 无此字段时保留上次值(防重握手瞬间丢登录态)
4112
4168
  account: msg.account ? { loggedIn: msg.account.loggedIn === true, nickname: String(msg.account.nickname || "") || null } : this.extension.account || null
@@ -4115,6 +4171,19 @@ var AgentBroker = class {
4115
4171
  this.notifyDownstreamsExtension(true);
4116
4172
  return;
4117
4173
  }
4174
+ case "extension.account.update": {
4175
+ if (msg.account) {
4176
+ this.extension = {
4177
+ ...this.extension,
4178
+ account: {
4179
+ loggedIn: msg.account.loggedIn === true,
4180
+ nickname: String(msg.account.nickname || "") || null
4181
+ }
4182
+ };
4183
+ log(`[lf-agent] \u767B\u5F55\u6001\u66F4\u65B0\uFF1A${msg.account.loggedIn === true ? `\u5DF2\u767B\u5F55\uFF08${msg.account.nickname || "?"}\uFF09` : "\u672A\u767B\u5F55"}`);
4184
+ }
4185
+ return;
4186
+ }
4118
4187
  case "task.progress": {
4119
4188
  const p = msg.progress || {};
4120
4189
  const dsProgress = this.downstreams.get(String(msg.deviceId || ""));
@@ -4215,7 +4284,7 @@ var AgentBroker = class {
4215
4284
  return;
4216
4285
  }
4217
4286
  });
4218
- ws.on("close", () => {
4287
+ ws.on("close", (code, reasonBuf) => {
4219
4288
  if (pokeTimer) clearTimeout(pokeTimer);
4220
4289
  clearInterval(pingTimer);
4221
4290
  if (ws.__lfDownstreamId) {
@@ -4231,11 +4300,14 @@ var AgentBroker = class {
4231
4300
  if (this.client === ws) {
4232
4301
  this.client = null;
4233
4302
  this.clientExtensionId = null;
4303
+ const closeReason = String(reasonBuf?.toString?.("utf8") || "");
4304
+ this.lastClose = { code: Number(code) || 0, reason: closeReason, at: (/* @__PURE__ */ new Date()).toISOString() };
4234
4305
  if (this.extension.connected) {
4235
4306
  this.extension = { ...this.extension, connected: false };
4236
4307
  this.lastDisconnect = (/* @__PURE__ */ new Date()).toISOString();
4237
- log("[lf-agent] \u63D2\u4EF6\u65AD\u5F00\uFF08\u7B49\u63D2\u4EF6\u81EA\u52A8\u91CD\u8FDE\uFF1B\u82E5\u957F\u65F6\u95F4\u672A\u6062\u590D\uFF0C\u68C0\u67E5\u63D2\u4EF6\u5F00\u5173/\u91CD\u8F7D\u63D2\u4EF6\uFF09");
4238
- this.notifyDownstreamsExtension(false);
4308
+ log(`[lf-agent] \u63D2\u4EF6\u65AD\u5F00\uFF08close code=${code}${closeReason ? ` reason="${closeReason}"` : ""}\uFF1B\u7B49\u63D2\u4EF6\u81EA\u52A8\u91CD\u8FDE\uFF1B\u82E5\u957F\u65F6\u95F4\u672A\u6062\u590D\uFF0C\u68C0\u67E5\u63D2\u4EF6\u5F00\u5173/\u91CD\u8F7D\u63D2\u4EF6\uFF09`);
4309
+ this.abortPendingTasks();
4310
+ this.notifyDownstreamsExtension(false, this.lastClose);
4239
4311
  }
4240
4312
  if (this.awaitingPairing) {
4241
4313
  this.awaitingPairing = false;
@@ -4251,11 +4323,11 @@ var AgentBroker = class {
4251
4323
  const devices = [DEVICE, ...[...this.downstreams.values()].map((d) => d.device)];
4252
4324
  this.send({ type: "broker.hello", protocolVersion: PROTOCOL_VERSION, serverName: "LeadsRadar Agent Adapter", devices });
4253
4325
  }
4254
- /** 插件在线状态变化时通知所有卫星(卫星据此维护自己的 extension.connected / step) */
4255
- notifyDownstreamsExtension(connected) {
4326
+ /** 插件在线状态变化时通知所有卫星(卫星据此维护自己的 extension.connected / step);⭐1.2.17 B3:断开时顺带透传 close frame 诊断 */
4327
+ notifyDownstreamsExtension(connected, lastClose = null) {
4256
4328
  for (const entry of this.downstreams.values()) {
4257
4329
  try {
4258
- entry.ws.send(JSON.stringify({ type: "device.extension.state", connected: connected === true }));
4330
+ entry.ws.send(JSON.stringify({ type: "device.extension.state", connected: connected === true, lastClose: lastClose ? { code: lastClose.code, reason: lastClose.reason } : null, pendingResults: (this.extension.pendingResults || []).slice(0, 10) }));
4259
4331
  } catch {
4260
4332
  }
4261
4333
  }
@@ -4422,13 +4494,20 @@ var AgentBroker = class {
4422
4494
  switch (msg.type) {
4423
4495
  case "device.extension.state":
4424
4496
  relay.extensionConnected = msg.connected === true;
4497
+ if (Array.isArray(msg.pendingResults)) {
4498
+ this.extension = { ...this.extension, pendingResults: msg.pendingResults.slice(0, 10), pendingResultCount: msg.pendingResults.length };
4499
+ }
4425
4500
  if (msg.connected === true && this.awaitingPairing) {
4426
4501
  this.awaitingPairing = false;
4427
4502
  log("[lf-agent] \u536B\u661F\u6A21\u5F0F\u5DF2\u590D\u4F4D\u7B49\u5F85\u6279\u51C6\u72B6\u6001");
4428
4503
  }
4429
4504
  if (this.extension.connected !== relay.extensionConnected) {
4430
4505
  this.extension = { ...this.extension, connected: relay.extensionConnected };
4431
- if (!relay.extensionConnected) this.lastDisconnect = (/* @__PURE__ */ new Date()).toISOString();
4506
+ if (!relay.extensionConnected) {
4507
+ this.lastDisconnect = (/* @__PURE__ */ new Date()).toISOString();
4508
+ if (relay.lastClose) this.lastClose = { code: relay.lastClose.code || 0, reason: relay.lastClose.reason || "", at: (/* @__PURE__ */ new Date()).toISOString() };
4509
+ this.abortPendingTasks();
4510
+ }
4432
4511
  log(`[lf-agent] \u4E2D\u8F6C\u901A\u77E5\uFF1A\u63D2\u4EF6${relay.extensionConnected ? "\u5DF2\u8FDE\u63A5" : "\u5DF2\u65AD\u5F00"}`);
4433
4512
  }
4434
4513
  return;
@@ -4526,6 +4605,45 @@ var AgentBroker = class {
4526
4605
  waiter.response = response;
4527
4606
  waiter.resolve(response);
4528
4607
  }
4608
+ /** ⭐1.2.17 B3 断开原因诊断:按插件断开的 close frame(code+reason)给精准指引。
4609
+ * 返回非空字符串=有诊断(调用方以此为首选指引,跳过 start chrome 类无效指引);null=无可用诊断(回落旧文案)。
4610
+ * reason 真源在插件 local-agent-connector.ts:stop() 关开关 → "LF Agent disabled";
4611
+ * stopForAvailability() 桥不可用 → "LF Agent bridge is not publicly available";
4612
+ * 浏览器被关/插件被禁用/重载 = SW 直接被杀无告别帧 → ws 层报 1006(1005/1001 同归异常断) */
4613
+ disconnectDiagnosis() {
4614
+ const c = this.lastClose;
4615
+ if (!c) return null;
4616
+ if (Date.now() - new Date(c.at).getTime() > 10 * 60 * 1e3) return null;
4617
+ if (c.reason.includes("disabled")) {
4618
+ return `\u8BCA\u65AD\uFF1A\u63D2\u4EF6\u7684\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u5F00\u5173\u88AB\u5173\u95ED\uFF08\u6D4F\u89C8\u5668\u8FD8\u5F00\u7740\uFF0C\u65E0\u9700\u91CD\u542F\uFF09\u2014\u2014\u8BF7\u7528\u6237\u5230\u63D2\u4EF6\u8BBE\u7F6E \u2192 \u667A\u80FD\u4F53\u8FDE\u63A5 \u2192 \u91CD\u65B0\u6253\u5F00\u9876\u90E8\u5F00\u5173\uFF0C\u914D\u5BF9\u5173\u7CFB\u4FDD\u7559\uFF0C\u7EA6 5 \u79D2\u81EA\u52A8\u91CD\u8FDE\u540E\u91CD\u8BD5`;
4619
+ }
4620
+ if (c.reason.includes("not publicly available")) {
4621
+ return `\u8BCA\u65AD\uFF1A\u63D2\u4EF6\u4FA7\u5224\u5B9A\u672C\u5730\u6865\u4E0D\u53EF\u7528\u5DF2\u81EA\u52A8\u65AD\u5F00\u2014\u2014\u8BF7\u7528\u6237\u91CD\u542F\u6D4F\u89C8\u5668\uFF0C\u518D\u5230\u63D2\u4EF6\u8BBE\u7F6E \u2192 \u667A\u80FD\u4F53\u8FDE\u63A5\u91CD\u65B0\u6253\u5F00\u5F00\u5173`;
4622
+ }
4623
+ if (c.code === 1006 || c.code === 1005 || c.code === 1001) {
4624
+ return `\u8BCA\u65AD\uFF1A\u8FDE\u63A5\u88AB\u76F4\u63A5\u5207\u65AD\uFF08\u6D4F\u89C8\u5668\u88AB\u5173\u95ED\uFF0C\u6216\u63D2\u4EF6\u88AB\u7981\u7528/\u91CD\u8F7D\u2014\u2014\u65E0\u544A\u522B\u5E27\uFF09\u2014\u2014\u8BF7\u7528\u6237\u91CD\u5F00\u6D4F\u89C8\u5668\u5E76\u786E\u8BA4\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\uFF0C\u63D2\u4EF6\u7EA6 5 \u79D2\u81EA\u52A8\u91CD\u8FDE\u540E\u91CD\u8BD5`;
4625
+ }
4626
+ if (c.code === 1e3 && c.reason) {
4627
+ return `\u8BCA\u65AD\uFF1A\u63D2\u4EF6\u4E3B\u52A8\u65AD\u5F00\uFF08${c.reason}\uFF09\u2014\u2014\u8BF7\u7528\u6237\u68C0\u67E5\u63D2\u4EF6\u8BBE\u7F6E \u2192 \u667A\u80FD\u4F53\u8FDE\u63A5\u7684\u5F00\u5173\u72B6\u6001\u540E\u91CD\u8BD5`;
4628
+ }
4629
+ return null;
4630
+ }
4631
+ /** ⭐1.2.16 B7:插件连接断开时中止所有等待中的任务(浏览器被关/插件掉线不再傻等 TASK_TIMEOUT,秒级返回失败) */
4632
+ abortPendingTasks() {
4633
+ if (!this.taskWaiters.size) return;
4634
+ const ids = [...this.taskWaiters.keys()];
4635
+ log(`[lf-agent] \u63D2\u4EF6\u65AD\u5F00 \u2192 \u79D2\u7EA7\u4E2D\u6B62 ${ids.length} \u4E2A\u7B49\u5F85\u4E2D\u7684\u4EFB\u52A1`);
4636
+ for (const id of ids) {
4637
+ this.finishTask(id, {
4638
+ ok: false,
4639
+ error: {
4640
+ code: "TASK_ABORTED",
4641
+ // ⭐1.2.17 B3:诊断优先(开关关→指开关;被杀→指重开浏览器),无诊断才回落探测命令
4642
+ message: `\u63D2\u4EF6\u8FDE\u63A5\u5DF2\u65AD\u5F00\uFF0C\u4EFB\u52A1\u5DF2\u4E2D\u6B62\u3002${this.disconnectDiagnosis() || extensionLaunchHint() || "\u8BF7\u91CD\u5F00\u6D4F\u89C8\u5668\uFF08\u63D2\u4EF6\u7EA6 5 \u79D2\u81EA\u52A8\u91CD\u8FDE\uFF09\u540E\u91CD\u8BD5\u3002"}`
4643
+ }
4644
+ });
4645
+ }
4646
+ }
4529
4647
  /**
4530
4648
  * 下发任务并等待结果。
4531
4649
  * @returns {Promise<{ok:boolean, error?:object, [k:string]:unknown}>}
@@ -4533,11 +4651,12 @@ var AgentBroker = class {
4533
4651
  startTask(task, timeoutMs = LIST_TASK_TIMEOUT_MS) {
4534
4652
  if (this.relay) return this.startTaskViaRelay(task, timeoutMs);
4535
4653
  if (!this.extension.connected) {
4654
+ const diag = this.disconnectDiagnosis();
4536
4655
  return Promise.resolve({
4537
4656
  ok: false,
4538
4657
  error: {
4539
4658
  code: "EXTENSION_NOT_CONNECTED",
4540
- message: this.awaitingPairing ? "\u63D2\u4EF6\u5DF2\u68C0\u6D4B\u5230\u672C\u8BBE\u5907\uFF0C\u4F46\u5C1A\u672A\u6279\u51C6\u914D\u5BF9\u3002\u8BF7\u8BA9\u7528\u6237\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u4E2D\u6838\u5BF9\u8BBE\u5907\u6307\u7EB9\u5E76\u70B9\u51FB\u6279\u51C6\uFF0C\u7136\u540E\u91CD\u8BD5\u3002" : "\u672A\u8FDE\u63A5\u5230\u63A2\u6817\uFF08LeadsRadar\uFF09\u6D4F\u89C8\u5668\u63D2\u4EF6\u3002\u82E5\u4F60\u6709\u7EC8\u7AEF\u6743\u9650\uFF0C\u53EF\u76F4\u63A5\u8FD0\u884C start chrome\uFF08\u6216 start msedge\uFF09\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8FDE\uFF08\u7EA6\u7B49 5 \u79D2\u518D\u91CD\u8BD5\uFF09\uFF1B\u5426\u5219\u8BF7\u8BA9\u7528\u6237\uFF1A1) \u6253\u5F00\u6D4F\u89C8\u5668\u5E76\u786E\u8BA4\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\uFF1B2) \u63D2\u4EF6\u8BBE\u7F6E\u9875 \u2192\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u2192 \u5F00\u542F\u9876\u90E8\u5F00\u5173\uFF0C\u7136\u540E\u91CD\u8BD5\u3002"
4659
+ message: this.awaitingPairing ? "\u63D2\u4EF6\u5DF2\u68C0\u6D4B\u5230\u672C\u8BBE\u5907\uFF0C\u4F46\u5C1A\u672A\u6279\u51C6\u914D\u5BF9\u3002\u8BF7\u8BA9\u7528\u6237\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u4E2D\u6838\u5BF9\u8BBE\u5907\u6307\u7EB9\u5E76\u70B9\u51FB\u6279\u51C6\uFF0C\u7136\u540E\u91CD\u8BD5\u3002" : diag ? `\u672A\u8FDE\u63A5\u5230\u63A2\u6817\uFF08LeadsRadar\uFF09\u6D4F\u89C8\u5668\u63D2\u4EF6\u3002${diag}\u3002` : `\u672A\u8FDE\u63A5\u5230\u63A2\u6817\uFF08LeadsRadar\uFF09\u6D4F\u89C8\u5668\u63D2\u4EF6\u3002${extensionLaunchHint() || "\u82E5\u4F60\u6709\u7EC8\u7AEF\u6743\u9650\uFF0C\u53EF\u76F4\u63A5\u8FD0\u884C start chrome\uFF08\u6216 start msedge\uFF09\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8FDE\uFF08\u7EA6\u7B49 5 \u79D2\u518D\u91CD\u8BD5\uFF09"}\uFF1B\u5426\u5219\u8BF7\u8BA9\u7528\u6237\uFF1A1) \u6253\u5F00\u6D4F\u89C8\u5668\u5E76\u786E\u8BA4\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\uFF1B2) \u63D2\u4EF6\u8BBE\u7F6E\u9875 \u2192\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u2192 \u5F00\u542F\u9876\u90E8\u5F00\u5173\uFF0C\u7136\u540E\u91CD\u8BD5\u3002`
4541
4660
  }
4542
4661
  });
4543
4662
  }
@@ -4549,7 +4668,7 @@ var AgentBroker = class {
4549
4668
  ok: false,
4550
4669
  error: {
4551
4670
  code: "TASK_TIMEOUT",
4552
- message: `\u4EFB\u52A1\u8D85\u65F6\uFF08${Math.round(timeoutMs / 1e3)}s\uFF09\u3002\u63D2\u4EF6\u53EF\u80FD\u88AB\u6D4F\u89C8\u5668\u4F11\u7720\u6216\u91C7\u96C6\u4E2D\u65AD\uFF0C\u53EF\u8BA9\u7528\u6237\u6253\u5F00\u4E00\u6B21\u63D2\u4EF6\u9762\u677F\u540E\u91CD\u8BD5\u3002`
4671
+ message: `\u4EFB\u52A1\u8D85\u65F6\uFF08${Math.round(timeoutMs / 1e3)}s\uFF09\u3002\u6CE8\u610F\uFF1A\u63D2\u4EF6\u53EF\u80FD\u4ECD\u5728\u6267\u884C\uFF0C\u4E14\u91C7\u96C6\u6570\u636E\u4F1A\u5148\u843D\u63A2\u6817\u672C\u5730\u5E93\u2014\u2014\u8BF7\u5148\u7528 query_local_data \u67E5\u8BE2\u8BE5\u4EFB\u52A1\u7684 keyword/url \u5BF9\u5E94\u7ED3\u679C\uFF0C\u67E5\u5230\u5C31\u76F4\u63A5\u7528\u3001\u4E0D\u8981\u91CD\u53D1\uFF1B\u786E\u5B9E\u6CA1\u6709\u518D\u91CD\u53D1\u4EFB\u52A1\uFF08\u907F\u514D\u91CD\u590D\u6D88\u8017\u91C7\u96C6\u989D\u5EA6\uFF09\u3002`
4553
4672
  }
4554
4673
  });
4555
4674
  }, timeoutMs);
@@ -4562,11 +4681,12 @@ var AgentBroker = class {
4562
4681
  startTaskViaRelay(task, timeoutMs) {
4563
4682
  const relay = this.relay;
4564
4683
  if (!relay || !relay.attached || !relay.extensionConnected) {
4684
+ const diag = this.disconnectDiagnosis();
4565
4685
  return Promise.resolve({
4566
4686
  ok: false,
4567
4687
  error: {
4568
4688
  code: "EXTENSION_NOT_CONNECTED",
4569
- message: this.awaitingPairing ? `\u63D2\u4EF6\u5DF2\u68C0\u6D4B\u5230\u672C\u8BBE\u5907\uFF08\u536B\u661F\u6A21\u5F0F\uFF09\uFF0C\u4F46\u5C1A\u672A\u6279\u51C6\u914D\u5BF9\u3002\u8BF7\u8BA9\u7528\u6237\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u4E2D\u6838\u5BF9\u8BBE\u5907\u6307\u7EB9\u5E76\u70B9\u51FB\u6279\u51C6\uFF0C\u7136\u540E\u91CD\u8BD5\u3002\u6307\u7EB9\uFF1A${fingerprint}` : `\u672A\u8FDE\u63A5\u5230\u63A2\u6817\u63D2\u4EF6\u3002\u5F53\u524D\u4E3A\u591A\u5BBF\u4E3B\u4E2D\u8F6C\u6A21\u5F0F\uFF08\u7AEF\u53E3\u88AB ${relay?.hostLabel || "\u53E6\u4E00\u5BBF\u4E3B"} \u7684\u9002\u914D\u5668\u5360\u7528\uFF0C\u672C\u8BBE\u5907\u7ECF\u5176\u4E2D\u8F6C\uFF09\u2014\u2014\u8BF7\u8BA9\u7528\u6237\uFF1A1) \u786E\u8BA4\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u4E14\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\uFF1B2) \u63D2\u4EF6\u8BBE\u7F6E\u9875 \u2192\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u2192 \u5F00\u542F\u9876\u90E8\u5F00\u5173\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u91CD\u8BD5\u4E00\u6B21\u3002`
4689
+ message: this.awaitingPairing ? `\u63D2\u4EF6\u5DF2\u68C0\u6D4B\u5230\u672C\u8BBE\u5907\uFF08\u536B\u661F\u6A21\u5F0F\uFF09\uFF0C\u4F46\u5C1A\u672A\u6279\u51C6\u914D\u5BF9\u3002\u8BF7\u8BA9\u7528\u6237\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u4E2D\u6838\u5BF9\u8BBE\u5907\u6307\u7EB9\u5E76\u70B9\u51FB\u6279\u51C6\uFF0C\u7136\u540E\u91CD\u8BD5\u3002\u6307\u7EB9\uFF1A${fingerprint}` : diag ? `\u672A\u8FDE\u63A5\u5230\u63A2\u6817\u63D2\u4EF6\uFF08\u591A\u5BBF\u4E3B\u4E2D\u8F6C\u6A21\u5F0F\uFF09\u3002${diag}\u3002` : `\u672A\u8FDE\u63A5\u5230\u63A2\u6817\u63D2\u4EF6\u3002\u5F53\u524D\u4E3A\u591A\u5BBF\u4E3B\u4E2D\u8F6C\u6A21\u5F0F\uFF08\u7AEF\u53E3\u88AB ${relay?.hostLabel || "\u53E6\u4E00\u5BBF\u4E3B"} \u7684\u9002\u914D\u5668\u5360\u7528\uFF0C\u672C\u8BBE\u5907\u7ECF\u5176\u4E2D\u8F6C\uFF09\u3002${extensionLaunchHint() || "\u8BF7\u8BA9\u7528\u6237\uFF1A1) \u786E\u8BA4\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u4E14\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\uFF1B2) \u63D2\u4EF6\u8BBE\u7F6E\u9875 \u2192\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u2192 \u5F00\u542F\u9876\u90E8\u5F00\u5173"}\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u91CD\u8BD5\u4E00\u6B21\u3002`
4570
4690
  }
4571
4691
  });
4572
4692
  }
@@ -4578,7 +4698,7 @@ var AgentBroker = class {
4578
4698
  ok: false,
4579
4699
  error: {
4580
4700
  code: "TASK_TIMEOUT",
4581
- message: `\u4EFB\u52A1\u8D85\u65F6\uFF08${Math.round(timeoutMs / 1e3)}s\uFF0C\u7ECF\u4E3B Broker \u4E2D\u8F6C\uFF09\u3002\u63D2\u4EF6\u53EF\u80FD\u88AB\u6D4F\u89C8\u5668\u4F11\u7720\u6216\u91C7\u96C6\u4E2D\u65AD\uFF0C\u53EF\u8BA9\u7528\u6237\u6253\u5F00\u4E00\u6B21\u63D2\u4EF6\u9762\u677F\u540E\u91CD\u8BD5\u3002`
4701
+ message: `\u4EFB\u52A1\u8D85\u65F6\uFF08${Math.round(timeoutMs / 1e3)}s\uFF0C\u7ECF\u4E3B Broker \u4E2D\u8F6C\uFF09\u3002\u6CE8\u610F\uFF1A\u63D2\u4EF6\u53EF\u80FD\u4ECD\u5728\u6267\u884C\uFF0C\u4E14\u91C7\u96C6\u6570\u636E\u4F1A\u5148\u843D\u63A2\u6817\u672C\u5730\u5E93\u2014\u2014\u8BF7\u5148\u7528 query_local_data \u67E5\u8BE2\u8BE5\u4EFB\u52A1\u7684 keyword/url \u5BF9\u5E94\u7ED3\u679C\uFF0C\u67E5\u5230\u5C31\u76F4\u63A5\u7528\u3001\u4E0D\u8981\u91CD\u53D1\uFF1B\u786E\u5B9E\u6CA1\u6709\u518D\u91CD\u53D1\u4EFB\u52A1\uFF08\u907F\u514D\u91CD\u590D\u6D88\u8017\u91C7\u96C6\u989D\u5EA6\uFF09\u3002`
4582
4702
  }
4583
4703
  });
4584
4704
  }, timeoutMs);
@@ -4594,6 +4714,13 @@ var AgentBroker = class {
4594
4714
  });
4595
4715
  }
4596
4716
  /** connection_status 快照(驱动宿主 AI 的 onboarding 状态机);⭐403 token 经济版:welcome 全文仅在 step=3 下发,step 1/2 轮询每轮省约 200 tokens */
4717
+ /** ⭐B7b-P1 存货提示(主/卫星 hint 共用):插件侧有已完成未领取结果时,引导 AI 先领取再决定重发 */
4718
+ pendingResultsHint() {
4719
+ const list = this.extension.pendingResults || [];
4720
+ if (!list.length) return "";
4721
+ const brief = list.slice(0, 3).map((p) => `${p.mode || "?"}\xB7${p.keyword || p.url || p.taskId}`).join("\u3001");
4722
+ return `\u6CE8\u610F\uFF1A\u6709 ${list.length} \u6761\u5DF2\u5B8C\u6210\u7684\u4EFB\u52A1\u7ED3\u679C\u672A\u88AB\u9886\u53D6\uFF08${brief}${list.length > 3 ? " \u7B49" : ""}\uFF09\u2014\u2014\u8BF7\u5148\u7528 leadsradar_claim_pending_result \u9886\u53D6\uFF08\u4E0D\u5360\u91C7\u96C6\u9650\u989D\uFF09\uFF0C\u9886\u5230\u76F4\u63A5\u7528\uFF1B\u6CA1\u6709\u9700\u8981\u7684\u518D\u8003\u8651\u65B0\u4EFB\u52A1\uFF0C\u907F\u514D\u91CD\u590D\u6D88\u8017\u989D\u5EA6\u3002`;
4723
+ }
4597
4724
  statusSnapshot() {
4598
4725
  const connected = this.relay ? this.relay.extensionConnected : this.extension.connected;
4599
4726
  const awaitingPairing = this.awaitingPairing;
@@ -4619,7 +4746,13 @@ var AgentBroker = class {
4619
4746
  extensionId: this.extension.extensionId || null,
4620
4747
  extensionVersion: this.extension.extensionVersion || null,
4621
4748
  pendingResultCount: this.extension.pendingResultCount,
4749
+ // ⭐B7b-P1 未领取结果摘要透出:非空=插件侧有已完成结果待 leadsradar_claim_pending_result 领取
4750
+ pendingResults: this.extension.pendingResults || [],
4622
4751
  lastDisconnectedAt: this.lastDisconnect || null,
4752
+ // ⭐1.2.17 B3 断开原因透出:close frame 原文(code+reason)+诊断结论,宿主 AI 可直接读
4753
+ lastDisconnectCode: this.lastClose?.code ?? null,
4754
+ lastDisconnectReason: this.lastClose?.reason || null,
4755
+ disconnectDiagnosis: this.extension.connected ? null : this.disconnectDiagnosis(),
4623
4756
  // ⭐r81 P0-3 登录态透出:save_topic/云端入库等需登录——宿主动手前用 connection_status
4624
4757
  // 预检(wb 实测采完详情才报 CLOUD_ERROR,白消耗采集额度)。注意:握手时点的快照,
4625
4758
  // 中途登录/退出会过期,动作层 LOGIN_REQUIRED 报错是第二道防线
@@ -4634,7 +4767,7 @@ var AgentBroker = class {
4634
4767
  awaitingPairing,
4635
4768
  portBound: false,
4636
4769
  relayMode: true,
4637
- hint: step === 3 ? `\u5DF2\u8FDE\u63A5\uFF08\u591A\u5BBF\u4E3B\u4E2D\u8F6C\u6A21\u5F0F\uFF1A\u7ECF\u300C${this.relay.hostLabel || this.relay.host}\u300D\u7684\u8FDE\u63A5\u5668\u5171\u4EAB\u63D2\u4EF6\u8FDE\u63A5\uFF0C\u8C46\u5305\u5DE5\u4F5C/WorkBuddy \u53EF\u540C\u65F6\u5728\u7EBF\uFF09\uFF0C\u53EF\u4EE5\u5F00\u59CB\u91C7\u96C6\u4EFB\u52A1\u3002` : step === 2 ? `\u7B49\u5F85\u7528\u6237\u6279\u51C6\u914D\u5BF9\uFF08\u536B\u661F\u6A21\u5F0F\uFF0C\u7ECF\u300C${this.relay.hostLabel || this.relay.host}\u300D\u4E2D\u8F6C\uFF09\uFF1A\u628A adapter.fingerprint \u539F\u6837\u8F6C\u544A\u7528\u6237\uFF0C\u8BF7\u5176\u5230\u63D2\u4EF6\u8BBE\u7F6E\u9875\u786E\u8BA4\u662F\u81EA\u5DF1\u7684\u8BBE\u5907\u540E\u70B9\u300C\u6279\u51C6\u300D\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\uFF08\u6CE8\u610F\uFF1A\u6279\u51C6\u524D\u4EFB\u52A1\u4F1A\u88AB\u62D2\u7EDD\uFF0C\u522B\u6025\u7740\u5F00\u5DE5\uFF09\u3002` : `\u4E2D\u8F6C\u6A21\u5F0F\u7B49\u5F85\u63D2\u4EF6\uFF1A\u8BF7\u8BA9\u7528\u6237\u6253\u5F00\u6D4F\u89C8\u5668\u786E\u8BA4\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\u3001\u8BBE\u7F6E\u9875\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u5F00\u5173\u5DF2\u6253\u5F00\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\u3002`
4770
+ hint: step === 3 ? `\u5DF2\u8FDE\u63A5\uFF08\u591A\u5BBF\u4E3B\u4E2D\u8F6C\u6A21\u5F0F\uFF1A\u7ECF\u300C${this.relay.hostLabel || this.relay.host}\u300D\u7684\u8FDE\u63A5\u5668\u5171\u4EAB\u63D2\u4EF6\u8FDE\u63A5\uFF0C\u8C46\u5305\u5DE5\u4F5C/WorkBuddy \u53EF\u540C\u65F6\u5728\u7EBF\uFF09\uFF0C\u53EF\u4EE5\u5F00\u59CB\u91C7\u96C6\u4EFB\u52A1\u3002${this.pendingResultsHint()}` : step === 2 ? `\u7B49\u5F85\u7528\u6237\u6279\u51C6\u914D\u5BF9\uFF08\u536B\u661F\u6A21\u5F0F\uFF0C\u7ECF\u300C${this.relay.hostLabel || this.relay.host}\u300D\u4E2D\u8F6C\uFF09\uFF1A\u628A adapter.fingerprint \u539F\u6837\u8F6C\u544A\u7528\u6237\uFF0C\u8BF7\u5176\u5230\u63D2\u4EF6\u8BBE\u7F6E\u9875\u786E\u8BA4\u662F\u81EA\u5DF1\u7684\u8BBE\u5907\u540E\u70B9\u300C\u6279\u51C6\u300D\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\uFF08\u6CE8\u610F\uFF1A\u6279\u51C6\u524D\u4EFB\u52A1\u4F1A\u88AB\u62D2\u7EDD\uFF0C\u522B\u6025\u7740\u5F00\u5DE5\uFF09\u3002` : `\u4E2D\u8F6C\u6A21\u5F0F\u7B49\u5F85\u63D2\u4EF6\uFF1A\u8BF7\u8BA9\u7528\u6237\u6253\u5F00\u6D4F\u89C8\u5668\u786E\u8BA4\u63A2\u6817\u63D2\u4EF6\u5DF2\u542F\u7528\u3001\u8BBE\u7F6E\u9875\u300C\u667A\u80FD\u4F53\u8FDE\u63A5\u300D\u5F00\u5173\u5DF2\u6253\u5F00\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\u3002`
4638
4771
  },
4639
4772
  welcome: step === 3 ? { fixedCopy: WELCOME_FIXED_COPY } : null
4640
4773
  };
@@ -4648,7 +4781,7 @@ var AgentBroker = class {
4648
4781
  // ⭐r85 portBlocked:EADDRINUSE 且占用者探测无应答 → Windows 动态端口保留段圈走端口。
4649
4782
  // 让宿主 AI 直接把根治命令转告用户,而不是一起懵在「连接器连接失败」
4650
4783
  portBlocked: this.portBlocked === true,
4651
- hint: this.portBlocked ? `\u9002\u914D\u5668\u7AEF\u53E3 ${PORT} \u88AB Windows \u52A8\u6001\u7AEF\u53E3\u4FDD\u7559\uFF08Hyper-V/WSL \u6392\u9664\u6BB5\uFF09\u6321\u4F4F\uFF1Abind \u88AB\u62D2\u4F46\u65E0\u8FDB\u7A0B\u5360\u7528\uFF0C\u901A\u5E38\u51E0\u5341\u5206\u949F\u5185\u81EA\u52A8\u6062\u590D\uFF0C\u9002\u914D\u5668\u5728\u6301\u7EED\u91CD\u8BD5\u3002\u8BF7\u628A\u8FD9\u6761\u6839\u6CBB\u547D\u4EE4\u8F6C\u544A\u7528\u6237\uFF08\u7BA1\u7406\u5458 PowerShell \u8FD0\u884C\u4E00\u6B21\u6C38\u4E45\u751F\u6548\uFF09\uFF1Anet stop winnat ; netsh int ipv4 add excludedportrange protocol=tcp startport=${PORT} numberofports=1 ; net start winnat\uFF08\u8FD0\u884C\u5B8C\u91CD\u542F\u672C\u4F1A\u8BDD\u5373\u8FDE\u4E0A\uFF09\u3002\u8F6C\u544A\u540E\u6BCF 30 \u79D2\u590D\u67E5\u4E00\u6B21\u3002` : !this.portBound ? `\u672C\u9002\u914D\u5668\u7684\u672C\u673A\u7AEF\u53E3 ${PORT} \u6B63\u5728\u88AB\u5360\u7528\u4E14\u5C1A\u672A\u5B8C\u6210\u4E2D\u8F6C\u534F\u5546\uFF08\u6B63\u5728\u81EA\u52A8\u5904\u7406\uFF1A\u540C\u5BBF\u4E3B\u5B64\u513F\u4F1A\u88AB\u63A5\u7BA1\uFF0C\u5F02\u5BBF\u4E3B\u4F1A\u81EA\u52A8\u8F6C\u5165\u4E2D\u8F6C\u5171\u5B58\uFF09\u3002\u8BF7\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\uFF1B\u82E5\u8D85\u8FC7 1 \u5206\u949F\u4ECD\u505C\u7559\u5728\u6B64\u72B6\u6001\uFF0C\u8BF7\u7528\u6237\u5B8C\u5168\u9000\u51FA\u6240\u6709 AI \u52A9\u624B\uFF08\u542B\u6258\u76D8\uFF09\u540E\u91CD\u5F00\u3002` : step === 1 ? "\u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A\u82E5\u4F60\u6709\u7EC8\u7AEF\u6743\u9650\u53EF\u8FD0\u884C start chrome\uFF08\u6216 start msedge\uFF09\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u63D2\u4EF6\u7EA6 5 \u79D2\u5185\u81EA\u52A8\u91CD\u8FDE\uFF1B\u5426\u5219\u8BF7\u7528\u6237\u5728\u6D4F\u89C8\u5668\u6253\u5F00\u63A2\u6817\u63D2\u4EF6 \u2192 \u8BBE\u7F6E \u2192 \u667A\u80FD\u4F53\u8FDE\u63A5 \u2192 \u6253\u5F00\u9876\u90E8\u5F00\u5173\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\u3002" : step === 2 ? `\u7B49\u5F85\u7528\u6237\u6279\u51C6\u914D\u5BF9\uFF1A\u628A adapter.fingerprint \u539F\u6837\u8F6C\u544A\u7528\u6237\uFF0C\u8BF7\u5176\u5230\u63D2\u4EF6\u8BBE\u7F6E\u9875\u6838\u5BF9\u6307\u7EB9\u4E00\u81F4\u540E\u70B9\u300C\u6279\u51C6\u300D\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\u3002\u6CE8\u610F\uFF1A\u82E5 extension.connected \u4E3A false\uFF0C\u8BF4\u660E\u8FD9\u662F\u63D2\u4EF6\u65AD\u5F00\u524D\u7684\u65E7\u72B6\u6001\u2014\u2014\u8BF7\u63D0\u9192\u7528\u6237\u68C0\u67E5\u63D2\u4EF6\u5F00\u5173\u662F\u5426\u6253\u5F00\uFF08\u6216\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u540E\u91CD\u65B0\u6253\u5F00\uFF09\uFF0C\u63D2\u4EF6\u91CD\u8FDE\u4F1A\u81EA\u52A8\u91CD\u65B0\u63E1\u624B\u3002` : "\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u91C7\u96C6\u4EFB\u52A1\u3002"
4784
+ hint: this.portBlocked ? `\u9002\u914D\u5668\u7AEF\u53E3 ${PORT} \u88AB Windows \u52A8\u6001\u7AEF\u53E3\u4FDD\u7559\uFF08Hyper-V/WSL \u6392\u9664\u6BB5\uFF09\u6321\u4F4F\uFF1Abind \u88AB\u62D2\u4F46\u65E0\u8FDB\u7A0B\u5360\u7528\uFF0C\u901A\u5E38\u51E0\u5341\u5206\u949F\u5185\u81EA\u52A8\u6062\u590D\uFF0C\u9002\u914D\u5668\u5728\u6301\u7EED\u91CD\u8BD5\u3002\u8BF7\u628A\u8FD9\u6761\u6839\u6CBB\u547D\u4EE4\u8F6C\u544A\u7528\u6237\uFF08\u7BA1\u7406\u5458 PowerShell \u8FD0\u884C\u4E00\u6B21\u6C38\u4E45\u751F\u6548\uFF09\uFF1Anet stop winnat ; netsh int ipv4 add excludedportrange protocol=tcp startport=${PORT} numberofports=1 ; net start winnat\uFF08\u8FD0\u884C\u5B8C\u91CD\u542F\u672C\u4F1A\u8BDD\u5373\u8FDE\u4E0A\uFF09\u3002\u8F6C\u544A\u540E\u6BCF 30 \u79D2\u590D\u67E5\u4E00\u6B21\u3002` : !this.portBound ? `\u672C\u9002\u914D\u5668\u7684\u672C\u673A\u7AEF\u53E3 ${PORT} \u6B63\u5728\u88AB\u5360\u7528\u4E14\u5C1A\u672A\u5B8C\u6210\u4E2D\u8F6C\u534F\u5546\uFF08\u6B63\u5728\u81EA\u52A8\u5904\u7406\uFF1A\u540C\u5BBF\u4E3B\u5B64\u513F\u4F1A\u88AB\u63A5\u7BA1\uFF0C\u5F02\u5BBF\u4E3B\u4F1A\u81EA\u52A8\u8F6C\u5165\u4E2D\u8F6C\u5171\u5B58\uFF09\u3002\u8BF7\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\uFF1B\u82E5\u8D85\u8FC7 1 \u5206\u949F\u4ECD\u505C\u7559\u5728\u6B64\u72B6\u6001\uFF0C\u8BF7\u7528\u6237\u5B8C\u5168\u9000\u51FA\u6240\u6709 AI \u52A9\u624B\uFF08\u542B\u6258\u76D8\uFF09\u540E\u91CD\u5F00\u3002` : step === 1 ? `\u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A${this.disconnectDiagnosis() || extensionLaunchHint() || "\u82E5\u4F60\u6709\u7EC8\u7AEF\u6743\u9650\u53EF\u8FD0\u884C start chrome\uFF08\u6216 start msedge\uFF09\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u63D2\u4EF6\u7EA6 5 \u79D2\u5185\u81EA\u52A8\u91CD\u8FDE"}\uFF1B\u5426\u5219\u8BF7\u7528\u6237\u5728\u6D4F\u89C8\u5668\u6253\u5F00\u63A2\u6817\u63D2\u4EF6 \u2192 \u8BBE\u7F6E \u2192 \u667A\u80FD\u4F53\u8FDE\u63A5 \u2192 \u6253\u5F00\u9876\u90E8\u5F00\u5173\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\u3002` : step === 2 ? `\u7B49\u5F85\u7528\u6237\u6279\u51C6\u914D\u5BF9\uFF1A\u628A adapter.fingerprint \u539F\u6837\u8F6C\u544A\u7528\u6237\uFF0C\u8BF7\u5176\u5230\u63D2\u4EF6\u8BBE\u7F6E\u9875\u6838\u5BF9\u6307\u7EB9\u4E00\u81F4\u540E\u70B9\u300C\u6279\u51C6\u300D\uFF0C\u7136\u540E\u6BCF 8 \u79D2\u590D\u67E5\u4E00\u6B21\u3002\u6CE8\u610F\uFF1A\u82E5 extension.connected \u4E3A false\uFF0C\u8BF4\u660E\u8FD9\u662F\u63D2\u4EF6\u65AD\u5F00\u524D\u7684\u65E7\u72B6\u6001\u2014\u2014\u8BF7\u63D0\u9192\u7528\u6237\u68C0\u67E5\u63D2\u4EF6\u5F00\u5173\u662F\u5426\u6253\u5F00\uFF08\u6216\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u540E\u91CD\u65B0\u6253\u5F00\uFF09\uFF0C\u63D2\u4EF6\u91CD\u8FDE\u4F1A\u81EA\u52A8\u91CD\u65B0\u63E1\u624B\u3002` : `\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u91C7\u96C6\u4EFB\u52A1\u3002${this.pendingResultsHint()}`
4652
4785
  },
4653
4786
  // step<3 不下发 welcome(省 token);step=3 由宿主 AI 原样展示给用户
4654
4787
  welcome: step === 3 ? { fixedCopy: WELCOME_FIXED_COPY } : null
@@ -4786,6 +4919,17 @@ var TOOLS = [
4786
4919
  required: ["source"],
4787
4920
  additionalProperties: false
4788
4921
  }
4922
+ },
4923
+ {
4924
+ name: "leadsradar_claim_pending_result",
4925
+ description: "\u9886\u53D6\u63A2\u6817\u63D2\u4EF6\u4FA7\u5DF2\u5B8C\u6210\u4F46\u672A\u88AB\u8BA4\u9886\u7684\u4EFB\u52A1\u7ED3\u679C\uFF08\u4E0D\u5360\u91C7\u96C6\u9650\u989D\u3001\u79D2\u56DE\uFF09\u3002\u4F55\u65F6\u7528\uFF1A\u2460\u4EFB\u52A1\u8D85\u65F6\u540E\uFF08\u63D2\u4EF6\u53EF\u80FD\u5DF2\u8DD1\u5B8C\uFF0C\u5148\u9886\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u53D1\uFF0C\u907F\u514D\u53CC\u8017\u91C7\u96C6\u989D\u5EA6\uFF09\uFF1B\u2461\u5BBF\u4E3B/\u4F1A\u8BDD\u91CD\u542F\u540E\u56DE\u6765\uFF1B\u2462connection_status \u7684 extension.pendingResults \u5217\u8868\u975E\u7A7A\u65F6\u3002\u9886\u53D6\u7ED3\u679C\u91CC results \u4E3A\u7ED3\u679C\u672C\u4F53\u53EF\u76F4\u63A5\u4F7F\u7528\uFF0Cpending \u4E3A\u5269\u4F59\u672A\u9886\u53D6\u6458\u8981\u3002\u9886\u4E0D\u5230\u518D\u7528 query_local_data \u67E5\u672C\u5730\u5E93\uFF0C\u90FD\u6CA1\u6709\u624D\u8003\u8651\u91CD\u53D1\u4EFB\u52A1\u3002",
4926
+ inputSchema: {
4927
+ type: "object",
4928
+ properties: {
4929
+ taskId: { type: "string", description: "\u53EF\u9009\uFF1A\u8981\u9886\u53D6\u7684 taskId\uFF08\u6765\u81EA connection_status \u7684 extension.pendingResults\uFF09\uFF1B\u4E0D\u4F20=\u9886\u53D6\u5168\u90E8\u672A\u9886\u53D6\u7ED3\u679C" }
4930
+ },
4931
+ additionalProperties: false
4932
+ }
4789
4933
  }
4790
4934
  ];
4791
4935
  async function callTool(name, args) {
@@ -4886,6 +5030,18 @@ async function callTool(name, args) {
4886
5030
  3e4
4887
5031
  );
4888
5032
  }
5033
+ case "leadsradar_claim_pending_result": {
5034
+ const claimTaskId = String(args.taskId || "").trim();
5035
+ return broker.startTask(
5036
+ {
5037
+ mode: "claim_pending_result",
5038
+ ...claimTaskId ? { claimTaskId } : { claimAll: true },
5039
+ source: HOST,
5040
+ agentContext: { host: HOST }
5041
+ },
5042
+ 3e4
5043
+ );
5044
+ }
4889
5045
  default:
4890
5046
  return { ok: false, error: { code: "UNKNOWN_TOOL", message: `\u672A\u77E5\u5DE5\u5177 ${name}` } };
4891
5047
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leadsradar-agent",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "探栗(LeadsRadar)AI 助手接入包——让豆包工作 / WorkBuddy / Codex / Claude 直接管住你的小红书·抖音获客雷达,全程本机流转",
5
5
  "type": "module",
6
6
  "main": "leadsradar-agent.mjs",
@@ -32,7 +32,7 @@ requires:
32
32
 
33
33
  1. 调用 `leadsradar_connection_status`
34
34
  2. `extension.connected = true` → 直接开工
35
- 3. 未连接 → 按返回的 onboarding 指引转述给用户(开插件「智能体接管」开关 / 批准配对),每 8 秒复查一次,最多 12 次;期间不要让用户重复操作
35
+ 3. 未连接 → 按返回的 onboarding 指引转述给用户(开插件「智能体连接」开关 / 批准配对),每 8 秒复查一次,最多 12 次;期间不要让用户重复操作
36
36
 
37
37
  ## 三、输入分流
38
38