@roll-agent/browser-use-agent 0.7.2 → 0.7.4
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/SKILL.md +34 -5
- package/dist/index.js +1 -1
- package/dist/pages/shared/dynamic-list-scroller.d.ts +51 -0
- package/dist/pages/zhipin/list-surfaces.d.ts +9 -0
- package/dist/pages/zhipin/recommend-filter.d.ts +47 -0
- package/dist/pages/zhipin/selectors.d.ts +2 -0
- package/dist/tools/zhipin-filter-recommend-candidates.d.ts +40 -0
- package/dist/tools/zhipin-get-candidate-list.d.ts +16 -0
- package/dist/tools/zhipin-read-messages.d.ts +2 -0
- package/dist/tools/zhipin-scroll-view.d.ts +35 -0
- package/package.json +2 -2
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: browser-use-agent
|
|
3
|
-
description: 浏览器操控 Agent
|
|
3
|
+
description: 浏览器操控 Agent。控制浏览器操作招聘平台——读取消息、打开聊天、发送回复、换微信、滚动动态列表、查看推荐列表、打招呼、查看简历。
|
|
4
4
|
metadata:
|
|
5
5
|
roll-env-file: references/env.yaml
|
|
6
6
|
---
|
|
@@ -31,10 +31,11 @@ metadata:
|
|
|
31
31
|
## 调试 Tools
|
|
32
32
|
|
|
33
33
|
- `attach_browser_session()` — 调试工具。显式执行一次 `connectOverCDP()`,用于隔离验证“仅 attach”是否会触发站点风控
|
|
34
|
+
- `zhipin_scroll_view(surface, direction?, steps?, distance?, settleMs?)` — 滚动 BOSS直聘页面内部动态列表容器,用于调试或显式翻页。`surface` 支持 `chat-list`、`chat-history`、`recommend-list`;不传 `direction` 时使用该 surface 的默认方向
|
|
34
35
|
|
|
35
36
|
## BOSS直聘 — 聊天 Tools
|
|
36
37
|
|
|
37
|
-
- `zhipin_read_messages(limit?, onlyUnread?, sortBy?)` — 读取消息列表中的候选人,默认返回全部消息;若只看未读,显式传 `onlyUnread=true`
|
|
38
|
+
- `zhipin_read_messages(limit?, onlyUnread?, sortBy?, autoScroll?, maxScrolls?)` — 读取消息列表中的候选人,默认返回全部消息;若只看未读,显式传 `onlyUnread=true`。默认 `autoScroll=true`,会向下滚动左侧消息列表内部容器并按 `conversationId` 合并去重;`maxScrolls` 默认 `4`,用于限制动态列表采集成本
|
|
38
39
|
- `zhipin_open_chat_page()` — 通过点击 Boss 左侧导航切换回「沟通」页;优先复用当前已登录的 Boss 页面,不让编排器去猜站内 URL
|
|
39
40
|
- `zhipin_open_chat(conversationId?, candidateName?, index?, preferUnread?)` — 打开指定候选人的聊天窗口;匹配优先级是 `conversationId` > `candidateName` > `index`
|
|
40
41
|
- `zhipin_get_candidate_info(conversationId?, candidateName?, index?, maxMessages?)` — 提取候选人资料、聊天记录,以及当前选中聊天的 `conversationId` / `candidateId`。输出里的 `candidateInfo.communicationPosition`、`candidateInfo.expectedLocation`、`candidateInfo.expectedPosition` 已按“沟通职位 + 最近关注”结构化解析;若 `communicationPosition` 含连字符类分隔符(`-` / `-` / `—` / `–`),则取第一段作为可选 `preferredBrand`,否则不输出该字段
|
|
@@ -62,11 +63,19 @@ metadata:
|
|
|
62
63
|
5. 禁止把 `zhipin_read_messages` 返回数组里的 `index` 缓存到下一轮,再把它当作会话主键使用
|
|
63
64
|
6. 只有在当前轮次拿不到 `conversationId` 时,才允许临时退回 `candidateName` 或 `index`
|
|
64
65
|
|
|
66
|
+
动态列表要求:
|
|
67
|
+
|
|
68
|
+
- `zhipin_read_messages` 默认会自动滚动左侧消息列表;只有明确需要“只读当前可见 DOM”时才传 `autoScroll=false`
|
|
69
|
+
- 若要手动补采或诊断滚动状态,调用 `zhipin_scroll_view(surface="chat-list")`
|
|
70
|
+
- `onlyUnread=true` 时不要依赖 `limit` 提前停止采集;未读会话可能在当前首屏之后
|
|
71
|
+
- `zhipin_scroll_view(surface="chat-history", direction="up")` 可用于显式加载当前聊天更早的历史消息,但业务回复链路仍应优先用 `zhipin_get_candidate_info(conversationId)` 读取结构化详情
|
|
72
|
+
|
|
65
73
|
错误做法:
|
|
66
74
|
|
|
67
75
|
- `zhipin_read_messages` 拿到 `index=2`,几轮之后再调用 `zhipin_open_chat(index=2)`
|
|
68
76
|
- 用 `candidateName` 重新模糊匹配一个会话,再把历史 `candidateId` 假定为同一个人
|
|
69
77
|
- `smart-reply-agent` 的 `target` 不用 `browser-use-agent` 返回的 `conversationId/candidateId`,而是由 orch 自己重建
|
|
78
|
+
- 手动滚动后继续用旧的 `index` 打开候选人;滚动和动态渲染后 `index` 仍只表示当前 DOM 快照
|
|
70
79
|
|
|
71
80
|
推荐做法:
|
|
72
81
|
|
|
@@ -79,12 +88,31 @@ metadata:
|
|
|
79
88
|
## BOSS直聘 — 推荐列表 Tools
|
|
80
89
|
|
|
81
90
|
- `zhipin_open_recommend_page()` — 通过点击 Boss 左侧导航切换到「推荐牛人」页;优先复用当前已登录的 Boss 页面,不让编排器去猜站内 URL
|
|
82
|
-
- `
|
|
91
|
+
- `zhipin_filter_recommend_candidates(ageMin?, ageMax?, gender?, activity?)` — 在「推荐牛人」页打开筛选面板,只设置年龄、性别、活跃度[单选] 三个维度并提交。未传的维度会重置为 `不限`(年龄默认为 `16-不限`),不会点击岗位下拉,也不会清除学历、薪资、求职状态等其它筛选项
|
|
92
|
+
- `zhipin_get_candidate_list(maxResults?, autoScroll?, maxScrolls?)` — 获取推荐列表页的候选人卡片信息(姓名、年龄、学历、期望薪资等)。默认 `autoScroll=true`,会向下滚动推荐列表内部容器并按 `candidateId` / `data-geek` 合并去重;`maxScrolls` 默认 `4`。返回的 `scrollStats.stopReason` 可用于判断未达到 `maxResults` 的原因:`target-count`、`boundary`、`no-new-items`、`max-steps`
|
|
83
93
|
- `zhipin_say_hello(indices)` — 对推荐列表中的候选人批量点击「打招呼」
|
|
84
94
|
- `zhipin_open_resume(index)` — 点击候选人卡片打开简历详情弹窗
|
|
85
95
|
- `zhipin_locate_resume_canvas()` — 定位简历弹窗中嵌套 iframe 内的 canvas 坐标(用于截图)
|
|
86
96
|
- `zhipin_close_resume()` — 关闭简历详情弹窗
|
|
87
97
|
|
|
98
|
+
## BOSS直聘 — 动态列表滚动规则
|
|
99
|
+
|
|
100
|
+
BOSS 页面通常不是整页滚动,而是内部容器滚动:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
chat-list -> 左侧消息列表,默认向下滚动,去重主键 conversationId
|
|
104
|
+
chat-history -> 右侧聊天记录,默认向上滚动,用于加载更早历史
|
|
105
|
+
recommend-list -> 推荐牛人列表,默认向下滚动,去重主键 candidateId/data-geek
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
使用原则:
|
|
109
|
+
|
|
110
|
+
1. 业务读取优先用自带 `autoScroll` 的工具:`zhipin_read_messages`、`zhipin_get_candidate_list`
|
|
111
|
+
2. `zhipin_scroll_view` 只作为显式翻页、调试和补救工具,不作为业务读取的必经步骤
|
|
112
|
+
3. 动态列表滚动后,`index` 会随当前 DOM 窗口变化;跨 tool 传递必须用 `conversationId` / `candidateId`
|
|
113
|
+
4. `maxScrolls` 用于成本控制;需要更完整列表时再显式调大,不要无界滚动
|
|
114
|
+
5. 推荐列表若 `total < maxResults`,先看 `scrollStats.stopReason`:`boundary` 表示触底后等待追加数据仍无新增,`no-new-items` 表示连续滚动没有新去重项,`max-steps` 表示到达滚动步数上限
|
|
115
|
+
|
|
88
116
|
## 鱼泡 Tools
|
|
89
117
|
|
|
90
118
|
- `yupao_read_messages(limit?)` — 读取鱼泡未读消息列表
|
|
@@ -108,8 +136,9 @@ metadata:
|
|
|
108
136
|
推荐列表链路建议:
|
|
109
137
|
|
|
110
138
|
1. `zhipin_open_recommend_page()` → 通过左侧导航切到 `推荐牛人`
|
|
111
|
-
2. `
|
|
112
|
-
3. `
|
|
139
|
+
2. `zhipin_filter_recommend_candidates(ageMin?, ageMax?, gender?, activity?)` → 需要限定目标人群时先设置年龄、性别、活跃度;例如“男性,20-40 岁,刚刚活跃”对应 `gender="男", ageMin=20, ageMax=40, activity="刚刚活跃"`
|
|
140
|
+
3. `zhipin_get_candidate_list(maxResults?, autoScroll=true, maxScrolls=4)` → 读取候选人卡片;默认会滚动动态列表并去重合并
|
|
141
|
+
4. `zhipin_say_hello(indices)` → 批量打招呼
|
|
113
142
|
|
|
114
143
|
## 支持平台
|
|
115
144
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineAgent as e,createAgentLogger as t}from"@roll-agent/sdk";import{BrowserRuntimeConfigSchema as a}from"@roll-agent/browser";import{defineTool as n}from"@roll-agent/sdk";import{z as r}from"zod";import{createAgentLogger as o}from"@roll-agent/sdk";import{BrowserRuntime as i,BrowserContextManager as s,SessionStore as c}from"@roll-agent/browser";var d,l,u,m=!1,g=o("browser-use-agent");async function f(e){d||(m=!1,u=new c(e.sessionsDir),d=new i(e),await d.start(),l=new s(d,u))}function p(){if(!d)throw new Error("BrowserRuntime not initialized. Call initRuntime() first.");return d}function h(){if(!l)throw new Error("BrowserContextManager not initialized. Call initRuntime() first.");return l}function w(){if(!u)throw new Error("SessionStore not initialized. Call initRuntime() first.");return u}function y(e){m=e}function b(){return m}async function v(){const e=l,t=d,a=[];if(l=void 0,d=void 0,u=void 0,m=!1,e){g.info("Closing browser contexts...");try{await e.closeAll()}catch(e){a.push(new Error("Failed to close browser contexts",{cause:e})),g.error(`Failed to close browser contexts: ${e instanceof Error?e.stack??e.message:String(e)}`)}}if(t){g.info("Stopping browser process...");try{await t.stop()}catch(e){a.push(new Error("Failed to stop browser runtime",{cause:e})),g.error(`Failed to stop browser runtime: ${e instanceof Error?e.stack??e.message:String(e)}`)}}if(g.info("Browser runtime shutdown complete"),a.length>0)throw new AggregateError(a,"Browser runtime shutdown failed")}var x=r.object({success:r.boolean(),mode:r.string(),connected:r.boolean()}),S=n({name:"attach_browser_session",description:"调试工具:显式执行一次 connectOverCDP(),仅建立 Playwright Browser 连接,不做页面导航或 DOM 操作。",input:r.object({}),output:x,execute:async(e,t)=>{const a=p();return t.logger.info("Attaching Playwright browser session over CDP"),await a.getBrowser(),{success:!0,mode:a.mode,connected:!0}}});import{defineTool as I}from"@roll-agent/sdk";import{z as k}from"zod";import{BrowserStatusSchema as C}from"@roll-agent/browser";import{createHash as A}from"node:crypto";import{z as R}from"zod";var M=["REPLY_AUTHORITY_KEYS_URL","BROWSER_VISUAL_CURSOR","BROWSER_VISUAL_ACTIVITY"],P=/^[0-9a-f]{8}$/,E=R.object({present:R.boolean(),fingerprint:R.string().regex(P).optional()}),$=R.record(E);function B(e,t=process.env){return Object.fromEntries(e.map(e=>{const a=t[e];return"string"==typeof a&&a.length>0?[e,{present:!0,fingerprint:_(a)}]:[e,{present:!1}]}))}function _(e){return A("sha256").update(e).digest("hex").slice(0,8)}var N,T=180,z=60,q=280,L="roll-agent-visual-cursor-root",F="__rollVisualCursorState";function j(e){if(void 0!==e)return"true"===e||"false"!==e&&void 0}function O(){return void 0!==N?N:j(process.env.BROWSER_VISUAL_CURSOR)??!0}async function U(e){return await e.evaluate(e=>{const t=e.getBoundingClientRect();return t.width<=0||t.height<=0?null:{x:Math.round(t.left+t.width/2),y:Math.round(t.top+t.height/2)}})}async function D(e,t){await e.evaluate(e=>{const t="roll-agent-visual-cursor-root",a="roll-agent-visual-cursor-pointer",n="__rollVisualCursorState",r=(()=>{const e=document.getElementById(t);if(e)return e;const n=document.createElement("div");n.id=t,n.style.position="fixed",n.style.left="0",n.style.top="0",n.style.width="0",n.style.height="0",n.style.pointerEvents="none",n.style.zIndex="2147483647";const r=document.createElement("div");return r.id=a,r.setAttribute("aria-hidden","true"),r.style.position="fixed",r.style.left="0",r.style.top="0",r.style.width="24px",r.style.height="24px",r.style.opacity="0",r.style.transform="translate(-9999px, -9999px)",r.style.willChange="transform, opacity",r.style.filter="drop-shadow(0 4px 8px rgba(15, 23, 42, 0.28))",r.innerHTML='<svg viewBox="0 0 24 24" width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 2L18 14L11.4 15.3L14.6 22L10.7 23.5L7.6 16.8L3 21V2Z" fill="#FFFFFF" stroke="#0F172A" stroke-width="1.5" stroke-linejoin="round"/></svg>',n.append(r),document.documentElement.append(n),n})(),o=r.querySelector(`#${a}`);if(!o)return;const i=window[n],s=e.point,c=e.durationMs,d=s.x-2,l=s.y-2;if(i?o.style.transition=`transform ${c}ms cubic-bezier(0.22, 1, 0.36, 1), opacity 120ms ease`:(o.style.transition="opacity 120ms ease",o.style.transform=`translate(${d}px, ${l}px)`),o.style.opacity="1",o.style.transform=`translate(${d}px, ${l}px)`,window[n]={x:s.x,y:s.y},!e.clickPulse)return;const u=document.createElement("div");u.setAttribute("aria-hidden","true"),u.style.position="fixed",u.style.left="0",u.style.top="0",u.style.width="18px",u.style.height="18px",u.style.borderRadius="9999px",u.style.border="2px solid rgba(20, 184, 166, 0.95)",u.style.background="rgba(20, 184, 166, 0.18)",u.style.pointerEvents="none",u.style.opacity="0.9",u.style.transform=`translate(${s.x-9}px, ${s.y-9}px) scale(0.55)`,u.style.transition=`transform ${e.pulseDurationMs}ms ease, opacity ${e.pulseDurationMs}ms ease`,r.append(u),requestAnimationFrame(()=>{u.style.opacity="0",u.style.transform=`translate(${s.x-18}px, ${s.y-18}px) scale(2)`}),globalThis.setTimeout(()=>{u.remove()},e.pulseDurationMs+40)},{point:t.point,durationMs:t.durationMs,clickPulse:t.clickPulse,pulseDurationMs:t.pulseDurationMs})}function V(e){const t=[e],a="function"==typeof e.frames?e.frames():[];return t.push(...a),t}async function W(e){await e.evaluate(({rootId:e,stateKey:t})=>{document.getElementById(e)?.remove(),delete window[t]},{rootId:L,stateKey:F})}async function H(e,t={}){if(e.isClosed())return!1;let a=!1;for(const n of V(e))if(n!==t.preserveTarget)try{await W(n),a=!0}catch{}return a}async function Z(e,t,a={}){if(!O()||e.isClosed())return!1;try{await t.scrollIntoViewIfNeeded();const n=await U(t);if(!n)return!1;const r=a.durationMs??T,o=a.settleMs??z,i=a.target??e;return await H(e,{preserveTarget:i}),await D(a.target??e,{point:n,durationMs:r,clickPulse:!1,pulseDurationMs:q}),await e.waitForTimeout(Math.max(r+o,0)),!0}catch{return!1}}async function G(e,t,a={}){if(!O()||e.isClosed())return!1;try{const n=await U(t);if(!n)return!1;const r=a.pulseDurationMs??q,o=a.target??e;return await H(e,{preserveTarget:o}),await D(o,{point:n,durationMs:0,clickPulse:!0,pulseDurationMs:r}),await e.waitForTimeout(r),!0}catch{return!1}}async function Y(e,t,a){return await Z(e,e.locator(t).first(),a)}async function K(e,t){return await G(e,e.locator(t).first())}var J,X={info:{accent:"#14b8a6",accentSoft:"rgba(20, 184, 166, 0.42)",accentGlow:"rgba(20, 184, 166, 0.18)",capsuleBg:"rgba(15, 23, 42, 0.82)",capsuleBorder:"rgba(45, 212, 191, 0.38)",text:"#F8FAFC",dot:"#2DD4BF"},success:{accent:"#22c55e",accentSoft:"rgba(34, 197, 94, 0.42)",accentGlow:"rgba(34, 197, 94, 0.18)",capsuleBg:"rgba(10, 24, 16, 0.86)",capsuleBorder:"rgba(74, 222, 128, 0.38)",text:"#F0FDF4",dot:"#4ADE80"},error:{accent:"#f59e0b",accentSoft:"rgba(245, 158, 11, 0.42)",accentGlow:"rgba(245, 158, 11, 0.2)",capsuleBg:"rgba(41, 24, 10, 0.88)",capsuleBorder:"rgba(251, 191, 36, 0.4)",text:"#FFFBEB",dot:"#FBBF24"}},Q=14,ee=720;function te(e){if(void 0!==e)return"true"===e||"false"!==e&&void 0}function ae(e="info"){return X[e]}function ne(){return void 0!==J?J:te(process.env.BROWSER_VISUAL_ACTIVITY)??!0}async function re(e,t=Q){return await e.evaluate((e,t)=>{const a=e.getBoundingClientRect();if(a.width<=0||a.height<=0)return null;const n=globalThis.innerWidth,r=globalThis.innerHeight,o=Math.max(t,0),i=Math.max(a.left-o,0),s=Math.max(a.top-o,0),c=Math.min(a.right+o,n),d=Math.min(a.bottom+o,r);return{x:Math.round(i),y:Math.round(s),width:Math.max(Math.round(c-i),0),height:Math.max(Math.round(d-s),0)}},t)}async function oe(e,t){await e.evaluate(e=>{const t="roll-agent-visual-activity-style",a="roll-agent-visual-activity-root",n="roll-agent-visual-activity-viewport",r="roll-agent-visual-activity-region",o="roll-agent-visual-activity-region-shine",i="roll-agent-visual-activity-capsule",s="roll-agent-visual-activity-dot",c="roll-agent-visual-activity-label",d=(e,t,a,n=!1)=>{n&&(e.style.transition="none",t.style.transition="none",a.style.transition="none"),e.style.opacity="0",e.style.transform="scale(0.995)",t.style.opacity="0",a.style.opacity="0",a.style.transform="translate(-50%, -8px)",t.style.transform="translate(-9999px, -9999px)",n&&requestAnimationFrame(()=>{e.style.transition="opacity 180ms ease, transform 220ms ease",t.style.transition="transform 220ms cubic-bezier(0.22, 1, 0.36, 1), width 220ms ease, height 220ms ease, opacity 180ms ease",a.style.transition="opacity 180ms ease, transform 220ms ease"})};(()=>{if(document.getElementById(t))return;const e=document.createElement("style");e.id=t,e.textContent="\n @keyframes roll-visual-activity-breathe {\n 0%, 100% { transform: translate(-50%, 0px) scale(1); }\n 50% { transform: translate(-50%, -1px) scale(1.01); }\n }\n @keyframes roll-visual-activity-scan {\n 0% { transform: translateX(-140%) skewX(-18deg); }\n 100% { transform: translateX(200%) skewX(-18deg); }\n }\n ",document.head.append(e)})(),(()=>{const e=document.getElementById(a);if(e)return e;const t=document.createElement("div");t.id=a,t.style.position="fixed",t.style.inset="0",t.style.pointerEvents="none",t.style.zIndex="2147483646";const d=document.createElement("div");d.id=n,d.setAttribute("aria-hidden","true"),d.style.position="fixed",d.style.inset="10px",d.style.borderRadius="20px",d.style.opacity="0",d.style.transform="scale(0.995)",d.style.transition="opacity 180ms ease, transform 220ms ease";const l=document.createElement("div");l.id=r,l.setAttribute("aria-hidden","true"),l.style.position="fixed",l.style.left="0",l.style.top="0",l.style.width="0",l.style.height="0",l.style.borderRadius="18px",l.style.opacity="0",l.style.overflow="hidden",l.style.transform="translate(-9999px, -9999px)",l.style.transition="transform 220ms cubic-bezier(0.22, 1, 0.36, 1), width 220ms ease, height 220ms ease, opacity 180ms ease";const u=document.createElement("div");u.id=o,u.setAttribute("aria-hidden","true"),u.style.position="absolute",u.style.inset="0",u.style.animation="roll-visual-activity-scan 1.6s linear infinite",u.style.opacity="0.9",l.append(u);const m=document.createElement("div");m.id=i,m.setAttribute("aria-hidden","true"),m.style.position="fixed",m.style.left="50%",m.style.top="20px",m.style.display="inline-flex",m.style.alignItems="center",m.style.gap="10px",m.style.padding="10px 14px",m.style.borderRadius="999px",m.style.opacity="0",m.style.transform="translate(-50%, -8px)",m.style.transition="opacity 180ms ease, transform 220ms ease",m.style.backdropFilter="blur(12px)",m.style.animation="roll-visual-activity-breathe 1.8s ease-in-out infinite",m.style.fontSize="13px",m.style.fontWeight="600",m.style.lineHeight="18px",m.style.letterSpacing="0.01em",m.style.whiteSpace="nowrap";const g=document.createElement("div");g.id=s,g.setAttribute("aria-hidden","true"),g.style.width="8px",g.style.height="8px",g.style.borderRadius="999px",g.style.flex="0 0 auto";const f=document.createElement("div");f.id=c,f.setAttribute("aria-live","polite"),m.append(g,f),t.append(d,l,m),document.documentElement.append(t)})();const l=document.getElementById(n),u=document.getElementById(r),m=document.getElementById(o),g=document.getElementById(i),f=document.getElementById(s),p=document.getElementById(c);if(!(l&&u&&m&&g&&f&&p))return;const h=window.__rollVisualActivityTimers??={};if(void 0!==h.hideTimer&&(globalThis.clearTimeout(h.hideTimer),delete h.hideTimer),"clear"===e.mode)return void d(l,u,g,!0);const w=e.theme;if(void 0!==w&&(l.style.border=`1px solid ${w.accentSoft}`,l.style.boxShadow=`inset 0 0 0 1px ${w.accentSoft}, 0 0 52px ${w.accentGlow}`,g.style.border=`1px solid ${w.capsuleBorder}`,g.style.background=w.capsuleBg,g.style.color=w.text,g.style.boxShadow=`0 18px 46px rgba(15, 23, 42, 0.24), 0 0 0 1px ${w.capsuleBorder}`,f.style.background=w.dot,f.style.boxShadow=`0 0 0 5px ${w.accentGlow}`,u.style.border=`1px solid ${w.accentSoft}`,u.style.background=w.accentGlow,u.style.boxShadow=`0 0 0 1px ${w.accentSoft}, 0 16px 42px ${w.accentGlow}`,m.style.background="linear-gradient(115deg, transparent 0%, rgba(255,255,255,0.08) 24%, rgba(255,255,255,0.42) 50%, transparent 76%)"),void 0!==e.label&&(p.textContent=e.label),g.style.opacity="1",g.style.transform="translate(-50%, 0)",l.style.opacity="complete"===e.mode?"0.9":"0.72",l.style.transform="scale(1)","begin"===e.mode)return u.style.opacity="0",void(u.style.transform="translate(-9999px, -9999px)");if(void 0!==e.rect&&(u.style.width=`${e.rect.width}px`,u.style.height=`${e.rect.height}px`,u.style.transform=`translate(${e.rect.x}px, ${e.rect.y}px)`,u.style.opacity="1"),"complete"!==e.mode)return;const y=Math.max(e.lingerMs??0,0);h.hideTimer=globalThis.setTimeout(()=>{d(l,u,g),delete h.hideTimer},y)},t)}async function ie(e,t){if(!ne()||e.isClosed())return!1;try{return await oe(t.target??e,{mode:"begin",label:t.label,theme:ae(t.tone??"info")}),!0}catch{return!1}}async function se(e,t,a={}){if(!ne()||e.isClosed())return!1;try{await t.scrollIntoViewIfNeeded();const n=await re(t,a.padding??Q);return!!n&&(await oe(a.target??e,{mode:"highlight",...void 0!==a.label?{label:a.label}:{},theme:ae(a.tone??"info"),rect:n}),!0)}catch{return!1}}async function ce(e,t,a){return await se(e,e.locator(t).first(),a)}async function de(e,t){if(!ne()||e.isClosed())return!1;try{return await oe(t.target??e,{mode:"complete",label:t.label,theme:ae("error"===t.status?"error":"success"),lingerMs:t.lingerMs??ee}),!0}catch{return!1}}async function le(e,t={}){if(!ne()||e.isClosed())return!1;try{return await oe(t.target??e,{mode:"clear"}),!0}catch{return!1}}var ue=C.extend({replyAuthorityKeysLoaded:k.boolean(),visualCursorEnabled:k.boolean(),visualActivityEnabled:k.boolean(),effectiveEnvSources:$}),me=I({name:"browser_status",description:"查询浏览器运行状态和活跃 session 信息",input:k.object({}),output:ue,execute:async(e,t)=>{t.logger.info("Querying browser status");const a=p(),n=h(),r=w(),o=a.isRunning(),{headless:i,mode:s}=a.getConfig(),c=n.getActivePlatforms(),d=[];for(const e of c){const t=n.getPageCount(e),o=n.getCurrentUrl(e);let i=null,s="unknown";if(a.shouldRestoreSessionSnapshot()){const[t,a]=await Promise.all([r.loadCookies(e),r.loadLocalStorage(e)]);i=void 0!==t&&t.length>0||void 0!==a&&Object.keys(a).length>0,s=i?"snapshot":"none"}else a.usesPersistentProfile()&&(i=null,s="profile");d.push({platform:e,pagesOpen:t,currentUrl:o,hasLoginState:i,loginStateSource:s})}return{running:o,headless:i,mode:s,activeSessions:d,replyAuthorityKeysLoaded:b(),visualCursorEnabled:O(),visualActivityEnabled:ne(),effectiveEnvSources:B(M)}}});import{defineTool as ge}from"@roll-agent/sdk";import{BrowserPageInfoSchema as fe,PlatformSchema as pe}from"@roll-agent/browser";import{z as he}from"zod";import{PLATFORMS as we}from"@roll-agent/browser";var ye={zhipin:"https://www.zhipin.com",yupao:"https://www.yupao.com"};function be(e){return new URL(ye[e]).host}function ve(e,t){try{return new URL(e).host.includes(be(t))}catch{return!1}}function xe(e){return we.find(t=>ve(e,t))}function Se(e,t){const a=xe(t.url)??null;return{pageId:t.targetId,url:t.url,title:t.title,boundPlatform:e.getBoundPlatformForNativePage(t.targetId)??null,detectedPlatform:a,isSelectedForPlatform:e.isNativePageSelected(t.targetId)}}async function Ie(e,t){const a=t.url();return{pageId:e.getPageId(t),url:a,title:await t.title().catch(()=>""),boundPlatform:e.getBoundPlatformForPage(t)??null,detectedPlatform:xe(a)??null,isSelectedForPlatform:e.isSelectedPageForPlatform(t)}}var ke=he.object({platform:pe.optional().describe("可选:仅返回指定平台相关的页面")}),Ce=he.object({pages:he.array(fe)}),Ae=ge({name:"list_pages",description:"通过原生 CDP 列出当前浏览器可见页面及其可选择的 pageId;登录前该值等同于原生 targetId。",input:ke,output:Ce,execute:async(e,t)=>{const a=h();t.logger.info("Listing browser pages");const n=(await a.listNativePages()).map(e=>Se(a,e));return{pages:void 0===e.platform?n:n.filter(t=>t.boundPlatform===e.platform||t.detectedPlatform===e.platform)}}});import{defineTool as Re}from"@roll-agent/sdk";import{BrowserPageInfoSchema as Me}from"@roll-agent/browser";import{z as Pe}from"zod";async function Ee(e,t){return e.useTrackedPage(t,e=>ve(e.url(),t))}async function $e(e,t){return(await e.listNativePages()).find(e=>ve(e.url,t))}async function Be(e,t){const a=await $e(e,t);if(a)return await e.activateNativePage(a.targetId),{page:a,reusedExistingPage:!0};return{page:await e.openNativePage(ye[t]),reusedExistingPage:!1}}var _e,Ne=Pe.object({url:Pe.string().url().describe("要导航到的目标 URL")}),Te=Pe.object({success:Pe.boolean(),page:Me});function ze(){return{getContextManager:h,detectPlatformFromUrl:xe,matchesPlatformHost:ve,findTrackedPlatformPage:Ee,toAttachedPageInfo:Ie,..._e}}async function qe(e,t,a){const n=(await e.listNativePages()).find(e=>t.matchesPlatformHost(e.url,a));if(n)return await e.selectNativePage(a,n.targetId),await e.getPage(a)}async function Le(e,t,a,n){const r=t.detectPlatformFromUrl(a);if(!r){const t=await e.getActivePage();if(!t)throw new Error("No active browser tab detected. Use open_platform or select_page first.");return{page:t}}const o=await t.findTrackedPlatformPage(e,r);if(o)return n.info(`Reusing tracked ${r} page instead of navigating the current unrelated tab`),{page:o,platform:r};const i=await qe(e,t,r);if(i)return n.info(`Reusing native ${r} page instead of navigating the current unrelated tab`),{page:i,platform:r};const s=await e.getActivePage();if(!s)throw new Error("No active browser tab detected. Use open_platform or select_page first.");return n.warn(`No existing ${r} page found; falling back to navigating the current active tab`),{page:s,platform:r}}async function Fe(e,t,a){return await e.selectAttachedPage(a,e.getPageId(t))}async function je(e,t){e.url()!==t&&await e.goto(t,{waitUntil:"domcontentloaded"})}var Oe=Re({name:"navigate_active_tab",description:"导航到指定 URL;若 URL 属于已知平台(Boss/鱼泡),优先复用已打开的平台页,避免把无关 tab 导航成第二个平台页。",input:Ne,output:Te,execute:async(e,t)=>{const a=ze(),n=a.getContextManager();t.logger.info(`Navigating active tab to ${e.url}`);const{page:r,platform:o}=await Le(n,a,e.url,t.logger);await r.bringToFront().catch(()=>{}),await je(r,e.url);const i=o??a.detectPlatformFromUrl(r.url()),s=i?await Fe(n,r,i):r;return i?t.logger.info(`Bound navigated page to ${i}`):n.clearBindingForPage(r),{success:!0,page:await a.toAttachedPageInfo(n,s)}}});import{defineTool as Ue}from"@roll-agent/sdk";import{BrowserPageInfoSchema as De,PlatformSchema as Ve}from"@roll-agent/browser";import{z as We}from"zod";var He=We.object({platform:Ve.describe("目标平台:`zhipin` 代表 BOSS直聘,`yupao` 代表鱼泡")}),Ze=We.object({success:We.boolean(),page:De,reusedExistingTab:We.boolean()}),Ge=Ue({name:"open_platform",description:"打开并聚焦招聘平台主页,供用户手动登录或后续执行站内操作。",input:He,output:Ze,execute:async(e,t)=>{const{platform:a}=e,n=p(),r=h();t.logger.info(`Opening platform page for ${a}`);const{page:o,reusedExistingPage:i}=await Be(n,a);return r.rememberNativePageSelection(a,o),{success:!0,page:Se(r,o),reusedExistingTab:i}}});import{defineTool as Ye}from"@roll-agent/sdk";import{BrowserPageInfoSchema as Ke,PlatformSchema as Je}from"@roll-agent/browser";import{z as Xe}from"zod";var Qe=Xe.object({platform:Je.describe("要将该页面绑定为当前活跃页的平台"),pageId:Xe.string().describe("通过 list_pages 返回的 pageId;登录前就是原生 targetId,登录后仍可作为稳定选择句柄")}),et=Xe.object({success:Xe.boolean(),page:Ke}),tt=Ye({name:"select_page",description:"将指定 pageId 绑定为平台当前活跃页,并切换到前台;登录前走原生 CDP target 激活。",input:Qe,output:et,execute:async(e,t)=>{const a=h();t.logger.info(`Selecting page ${e.pageId} for ${e.platform}`);const n=await a.selectNativePage(e.platform,e.pageId);return{success:!0,page:Se(a,n)}}});import{defineTool as at}from"@roll-agent/sdk";import{z as nt}from"zod";async function rt(e,t=300,a=800){const n=Math.floor(Math.random()*(a-t))+t;await e.waitForTimeout(n)}async function ot(e){const t=Math.random();let a;a=t<.5?800+1200*Math.random():t<.8?500+300*Math.random():t<.95?2e3+2e3*Math.random():4e3+2e3*Math.random(),await e.waitForTimeout(Math.floor(a))}async function it(e,t){const a=t?.minDistance??50,n=t?.maxDistance??200,r=t?.direction??"both",o=Math.floor(Math.random()*(n-a))+a,i="up"===r?-1:"down"===r||Math.random()>.5?1:-1;await e.evaluate(e=>{window.scrollBy({top:e,behavior:"smooth"})},o*i),await rt(e,200,500)}async function st(e){if(Math.random()<.8){const t=100+Math.floor(100*Math.random());await e.evaluate(e=>{window.scrollBy({top:e,behavior:"smooth"})},t),await ot(e)}if(Math.random()<.5){const t=(50+Math.floor(100*Math.random()))*(Math.random()>.5?1:-1);await e.evaluate(e=>{window.scrollBy({top:e,behavior:"smooth"})},t)}}function ct(e=.3){return Math.random()<e}import{setTimeout as dt}from"node:timers/promises";var lt=".geek-item.selected";async function ut(e){try{await e.waitForSelector(lt,{timeout:5e3})}catch{return null}const t=await e.evaluate(()=>{const e=document.querySelector(".geek-item.selected");if(!e)return null;const t=e.getAttribute("data-id")??e.closest('[role="listitem"]')?.getAttribute("key")??"";return{conversationId:t,candidateId:e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??t,candidateName:e.querySelector('[class*="name"], .nickname, .geek-name, .candidate-name')?.textContent?.trim()??""}});return t&&"string"==typeof t.conversationId&&"string"==typeof t.candidateId&&0!==t.conversationId.length&&0!==t.candidateId.length?t:null}async function mt(e){const t=await e.evaluate(()=>{const e=[".chat-conversation",".conversation-box",".conversation-message"],t=[".base-info-single-detial .name-box",".base-info-content .name-box",".base-info-single-container .name-box",".base-info-content .base-name",".chat-user-name",".name-box",".base-name"];for(const a of e){const e=document.querySelector(a);if(e)for(const a of t){const t=e.querySelector(a)?.textContent?.trim()??"";if(t.length>0)return{candidateName:t}}}return null});return t&&"string"==typeof t.candidateName&&0!==t.candidateName.length?t:null}var gt="https://www.zhipin.com/web/geek/chat",ft=".chat-list-wrap, .geek-item",pt=new Set(["消息"]),ht="data-roll-chat-entry-target",wt="data-roll-chat-item-target";function yt(e){return e.trim().toLocaleLowerCase("zh-CN")}function bt(e,t){const a=yt(e),n=yt(t);return a.length>0&&n.length>0&&(a===n||a.includes(n)||n.includes(a))}function vt(e,t){let a=0;for(const n of e)t.includes(n)&&(a+=1);return a}function xt(e,t){if(void 0!==t.conversationId){const a=e.find(e=>e.conversationId===t.conversationId);if(a)return a}const a=t.candidateName;if(a){const t=yt(a),n=e.filter(e=>e.name.length>0);let r=n.find(e=>yt(e.name)===t);if(r)return r;if(r=n.find(e=>{const a=yt(e.name);return a.includes(t)||t.includes(a)}),r)return r;const o=t.length<=2?1:t.length<=4?.75:.6;if(r=n.find(e=>{const a=yt(e.name);return vt(t,a)>=Math.ceil(Math.min(t.length,a.length)*o)}),r)return r}if(void 0!==t.index)return e[t.index]}function St(e){return e.includes("/web/geek/chat")||e.includes("/web/chat")}async function It(e,t=1e4){try{return await e.waitForSelector(ft,{timeout:t}),!0}catch{return!1}}async function kt(e,t){const a=(await e.listAttachedPages()).find(e=>e!==t&&St(e.url()));if(a)return await e.selectAttachedPage("zhipin",e.getPageId(a))}async function Ct(e,t){await e.evaluate(e=>{document.querySelectorAll(`[${e}]`).forEach(t=>{t.removeAttribute(e)})},t).catch(()=>{})}async function At(e,t){const a=e.locator(t).first();await a.scrollIntoViewIfNeeded(),await Z(e,a),await a.hover(),await rt(e,200,400),await G(e,a),await a.click()}async function Rt(e){const t=await e.evaluate(e=>{const t=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},a=t=>e.messageLabels.some(e=>t===e||t.includes(e));document.querySelectorAll(`[${e.markerAttr}]`).forEach(t=>{t.removeAttribute(e.markerAttr)});const n=Array.from(document.querySelectorAll('a[href*="/web/geek/chat"], a[href*="/web/chat"]'));for(const a of n)if(t(a))return a.setAttribute(e.markerAttr,"true"),{found:!0,selector:`[${e.markerAttr}="true"]`};const r=Array.from(document.querySelectorAll('a, button, [role="link"], [role="button"], span, div'));for(const n of r){if(a(n.textContent?.trim()??"")&&t(n))return n.setAttribute(e.markerAttr,"true"),{found:!0,selector:`[${e.markerAttr}="true"]`}}return{found:!1}},{markerAttr:ht,messageLabels:[...pt]});if(!t.found)return!1;try{return await At(e,t.selector),!0}finally{await Ct(e,ht)}}function Mt(e){return e instanceof Error&&/ERR_ABORTED/i.test(e.message)}async function Pt(e){try{return await e.goto(gt,{waitUntil:"domcontentloaded"}),!0}catch(t){return!!Mt(t)&&(!!St(e.url())||await It(e,2e3))}}async function Et(e,t){if(!await Rt(t))return!1;if(await It(t,5e3))return!0;const a=await kt(e,t);return!!a&&await It(a,5e3)}async function $t(e,t){if(St(t.url())&&await It(t))return!0;const a=await kt(e,t);if(a&&await It(a))return!0;if(await Et(e,t))return!0;if(!await Pt(t))return!1;if(await It(t))return!0;await dt(300);const n=await kt(e,t);return!!n&&await It(n,5e3)}async function Bt(e){return e.evaluate(()=>Array.from(document.querySelectorAll(".geek-item")).map((e,t)=>{const a=e.getAttribute("data-id")??e.closest('[role="listitem"]')?.getAttribute("key")??"",n=e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??a,r=e.querySelector('[class*="name"], .nickname, .geek-name, .candidate-name'),o=r?.textContent?.trim()??"",i=e.querySelector(".source-job")?.textContent?.trim()??"",s=e.querySelector(".badge-count"),c=parseInt(s?.textContent?.trim()??"0",10)||0;return{conversationId:a,candidateId:n,name:o,index:t,position:i,hasUnread:c>0||null!==e.querySelector(".red-dot"),unreadCount:c,lastMessageTime:e.querySelector(".time, .time-shadow")?.textContent?.trim()??"",messagePreview:(e.querySelector(".push-text, .chat-last-msg")?.textContent?.trim()??"").slice(0,100)}}))}async function _t(e,t){const a=await e.evaluate(e=>{document.querySelectorAll(`[${e.markerAttr}]`).forEach(t=>{t.removeAttribute(e.markerAttr)});const t=Array.from(document.querySelectorAll(".geek-item")),a=t.find(t=>(t.getAttribute("data-id")??t.closest('[role="listitem"]')?.getAttribute("key")??"")===e.targetConversationId)??t[e.targetIndex];if(!a)return{found:!1};return(a.querySelector(".chat-item-content")??a).setAttribute(e.markerAttr,"true"),{found:!0,selector:`[${e.markerAttr}="true"]`}},{markerAttr:wt,targetConversationId:t.conversationId,targetIndex:t.index});if(!a.found)return!1;try{return await At(e,a.selector),!0}finally{await Ct(e,wt)}}async function Nt(e,t){if(0===t.conversationId.length&&0===t.name.length)return await rt(e,500,900),!0;try{return await e.waitForFunction(e=>{const t=e=>e.trim().toLocaleLowerCase("zh-CN"),a=document.querySelector(".geek-item.selected"),n=a?.getAttribute("data-id")??a?.closest('[role="listitem"]')?.getAttribute("key")??"",r=0===e.conversationId.length||n===e.conversationId,o=(()=>{const e=[".chat-conversation",".conversation-box",".conversation-message"],t=[".base-info-single-detial .name-box",".base-info-content .name-box",".base-info-single-container .name-box",".base-info-content .base-name",".chat-user-name",".name-box",".base-name"];for(const a of e){const e=document.querySelector(a);if(e)for(const a of t){const t=e.querySelector(a)?.textContent?.trim()??"";if(t.length>0)return t}}return""})(),i=0===e.candidateName.length||((e,a)=>{const n=t(e),r=t(a);return n.length>0&&r.length>0&&(n===r||n.includes(r)||r.includes(n))})(e.candidateName,o);return r&&i},{conversationId:t.conversationId,candidateName:t.name},{timeout:5e3}),!0}catch{return await rt(e,800,1200),!1}}async function Tt(e,t,a){if(void 0===a.conversationId&&void 0===a.candidateName&&void 0===a.index)return;if(!await $t(e,t))return{found:!1,conversationId:"",candidateId:"",name:"",index:-1,position:"",hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:"消息列表未加载"};const n=await e.getPage("zhipin"),r=xt(await Bt(n),a);if(!r){return{found:!1,conversationId:"",candidateId:"",name:"",index:-1,position:"",hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:`未找到候选人: ${a.conversationId??a.candidateName??`index ${a.index}`}`}}if(!await _t(n,r))return{...r,found:!1,error:`打开候选人聊天失败: ${r.name||`index ${r.index}`}`};let o=await Nt(n,r);if(!o){await _t(n,r)&&(o=await Nt(n,r))}if(!o)return{...r,found:!1,error:`打开候选人聊天后,右侧会话未同步切换到 ${r.name||r.conversationId}`};const i=await ut(n);if(!i||i.conversationId!==r.conversationId)return{...r,found:!1,error:`当前选中会话与目标会话不一致: ${r.name||r.conversationId}`};const s=await mt(n);return!(r.name.length>0)||s&&bt(r.name,s.candidateName)?{...r,found:!0}:{...r,found:!1,error:`右侧聊天面板仍未切换到 ${r.name}`}}function zt(e){return"function"==typeof e.page}function qt(e){return zt(e)?e.page():e}var Lt=class{page;target;constructor(e){this.page=qt(e),this.target=e}async retarget(e){if(e===this.target)return this.page=qt(e),this.target=e,!0;const t=this.page,a=this.target;return await le(t,{target:a}),this.page=qt(e),this.target=e,!0}async begin(e,t="info"){return await ie(this.page,{label:e,tone:t,target:this.target})}async highlightSelector(e,t={}){return await ce(this.page,e,{...t,target:this.target})}async highlightLocator(e,t={}){return await se(this.page,e,{...t,target:this.target})}async succeed(e,t){return await de(this.page,{label:e,...void 0!==t?{lingerMs:t}:{},status:"success",target:this.target})}async fail(e,t){return await de(this.page,{label:e,...void 0!==t?{lingerMs:t}:{},status:"error",target:this.target})}async clear(){return await le(this.page,{target:this.target})}},Ft=nt.object({name:nt.string(),conversationId:nt.string(),candidateId:nt.string(),position:nt.string(),time:nt.string(),preview:nt.string(),unreadCount:nt.number(),hasUnread:nt.boolean(),index:nt.number()}),jt=nt.object({success:nt.boolean(),candidates:nt.array(Ft),total:nt.number(),stats:nt.object({withName:nt.number(),withUnread:nt.number()})}),Ot=at({name:"zhipin_read_messages",description:"读取 BOSS直聘消息列表,默认返回全部候选人;若只看未读消息,传 onlyUnread=true",input:nt.object({limit:nt.number().optional().describe("最多返回条数"),onlyUnread:nt.boolean().default(!1).describe("是否只返回有未读消息的候选人;用户说“全部/所有消息列表”时应为 false,说“未读消息”时应为 true"),sortBy:nt.enum(["time","unreadCount","name"]).default("time")}),output:jt,execute:async(e,t)=>{const a=e.onlyUnread??!1;t.logger.info(`Reading zhipin messages (limit: ${e.limit??"all"}, onlyUnread: ${a})`);const n=h(),r=await n.getPage("zhipin"),o=new Lt(r),i=a?"正在读取未读消息列表":"正在读取消息列表";await o.begin("正在打开消息列表");try{const s=await $t(n,r),c=await n.getPage("zhipin");if(await o.retarget(c),!s)return await o.fail("未找到消息列表"),{success:!1,candidates:[],total:0,stats:{withName:0,withUnread:0}};await o.begin(i),await o.highlightSelector(".user-list.b-scroll-stable, .chat-user .user-container, .chat-list-wrap",{label:i,padding:8}),await st(c);const d=(await Bt(c)).map(e=>({name:e.name,conversationId:e.conversationId,candidateId:e.candidateId,position:e.position,time:e.lastMessageTime,preview:e.messagePreview,unreadCount:e.unreadCount,hasUnread:e.hasUnread,index:e.index}));let l=a?d.filter(e=>e.hasUnread):d;const u=e.sortBy??"time";"time"===u||("unreadCount"===u?l.sort((e,t)=>t.unreadCount-e.unreadCount):"name"===u&&l.sort((e,t)=>e.name.localeCompare(t.name))),void 0!==e.limit&&(l=l.slice(0,e.limit));const m={withName:d.filter(e=>e.name.length>0).length,withUnread:d.filter(e=>e.hasUnread).length};return await o.succeed(a?`已读取 ${l.length} 条未读消息`:`已读取 ${l.length} 条消息`),t.logger.info(`Found ${l.length} candidates (${m.withUnread} with unread)`),{success:!0,candidates:l,total:d.length,stats:m}}catch(e){throw await o.fail("读取消息列表失败"),e}}});import{defineTool as Ut}from"@roll-agent/sdk";import{BrowserPageInfoSchema as Dt}from"@roll-agent/browser";import{z as Vt}from"zod";var Wt={login:{notLoggedIn:".header-login-btn"},unread:{container:".chat-list-wrap",listItem:'[role="listitem"]',geekItemWrap:".geek-item-wrap",item:".chat-item",unreadBadge:".badge-count",unreadBadgeNew:".badge-count.badge-count-common-less",unreadBadgeSpan:".badge-count span",unreadDot:".red-dot",figure:".figure",badge:".badge",candidateName:".candidate-name",candidateNameAlt:".chat-item-name",candidateNameSelectors:'[class*="name"], .nickname, .geek-name',candidateNameNew:".geek-name",jobTitle:".source-job",lastMessage:".push-text",lastMessageAlt:".chat-last-msg",messageTime:".time, .time-shadow",clickArea:".chat-item-content",unreadCandidates:".geek-item"},chat:{chatContainer:".chat-container",messageList:".message-list",messageItem:".message-item",messageContent:".message-content",messageText:".text-content",userMessage:".message-right",candidateMessage:".message-left",messageTime:".message-time",senderName:".sender-name",inputBox:".chat-input",inputTextarea:"textarea.chat-input",inputEditorId:"#boss-chat-editor-input",sendButton:".btn-send",conversationEditor:".conversation-editor",submitButton:".submit-content .submit",submitButtonActive:".submit-content .submit.active",submitContent:".submit-content",sendButtonAlt:".conversation-editor .submit-content",sendIcon:".submit-content .icon-send",systemMessage:".system-msg"},chatDetails:{candidateInfoContainer:".base-info-single-container, .base-info-content",candidateName:".name-box, .geek-name, .base-name",candidateInfoItem:".geek-info-item, .base-info-item, .base-info-single-detial > div",candidateTag:".geek-tag, .high-light-boss",communicationPosition:".position-name",communicationPositionAlt:".position-item:not(.expect) .value.high-light-boss",candidateExpectContainer:".position-item.expect",candidateExpectValue:".position-item.expect .value.job",candidateExpectSalary:".position-item.expect .high-light-orange",candidatePosition:".geek-position, .position-name",candidatePositionAlt:".position-content .value, .position-item .value",chatMessageContainer:".conversation-message, .message-list",messageItem:".message-item",messageTime:".message-time .time",messageTextSpan:".text span",systemMessage:".item-system",friendMessage:".item-friend",myMessage:".item-myself",resumeMessage:".item-resume",readStatus:".status-read"},exchangeWechat:{exchangeButtonPath:"#container > div:nth-child(1) > div > div.chat-box > div.chat-container > div.chat-conversation > div.conversation-box > div.conversation-operate > div.toolbar-box > div.toolbar-box-right > div.operate-exchange-left > div:nth-child(3) > span.operate-btn",exchangeButtonFallback:".operate-exchange-left .operate-btn",confirmDialog:".exchange-tooltip",confirmButton:".exchange-tooltip .btn-box .boss-btn-primary.boss-btn",confirmButtonPath:"#container > div:nth-child(1) > div > div.chat-box > div.chat-container > div.chat-conversation > div.conversation-box > div.conversation-operate > div.toolbar-box > div.toolbar-box-right > div.operate-exchange-left > div:nth-child(3) > div > div > span.boss-btn-primary.boss-btn",cancelButton:".exchange-tooltip .btn-box .boss-btn-outline.boss-btn",wechatCard:".message-card-top-wrap",wechatCardAlt:'[class*="d-top-text"]'},username:{primary:".nav-item.nav-logout .user-name",fallbacks:["#header > div > div > div.nav-item.nav-logout > div.top-profile-logout.ui-dropmenu.ui-dropmenu-drop-arrow > div.ui-dropmenu-label > div > span.user-name",".ui-dropmenu-label .user-name",".nav-logout .user-name","#header .user-name",".top-profile .user-name",".nav-user .user-name",".user-name",'[class*="user-name"]','[class*="username"]','[data-qa="user-name"]',".header-user-name","#header .label-name"]},nav:{sidebar:".side-wrap.side-wrap-v2",chatLink:'.side-wrap.side-wrap-v2 a[href*="/web/chat/index"]',recommendLink:'.side-wrap.side-wrap-v2 a[href*="/web/geek/recommend"]'},recommend:{iframe:"#recommendFrame",resumeIframe:'iframe[src*="c-resume"]',candidateItem:"[data-geek], .geek-item",candidateName:".name",candidateBaseInfo:".base-info",workExps:".timeline-wrap.work-exps",expectInfo:".row-flex, .timeline-wrap.expect",salaryWrap:".salary-wrap",tagsWrap:".tags-wrap",greetButton:".btn-greet, .op-btn",resumeCanvas:"div#resume > canvas#resume, canvas#resume",closeResumeBtn:".close-btn, .dialog-close",closeResumeBtnV2:".recommendV2 .close-btn"},candidateProfile:{panel:".candidate-info, .resume-info, .geek-info",name:".candidate-info .name, .geek-info .name",age:".candidate-info .age, .geek-info .age",gender:".candidate-info .gender, .geek-info .gender",experience:".candidate-info .experience, .geek-info .work-exp",education:".candidate-info .education, .geek-info .edu",expectedSalary:".candidate-info .salary, .geek-info .expect-salary",expectedPosition:".candidate-info .position, .geek-info .expect-position",activeTime:".candidate-info .active-time, .geek-info .active"}},Ht={chat:"沟通",recommend:"推荐牛人"};function Zt(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function Gt(e){for(const t of e)try{if(await t.count()>0&&await t.first().isVisible())return t.first()}catch{}return null}function Yt(e){return Ht[e]}async function Kt(e,t){const a=e.locator(Wt.nav.sidebar).first(),n=Yt(t),r=new RegExp(`^${Zt(n)}(?:\\s|$)`),o="recommend"===t?[Wt.nav.recommendLink]:[Wt.nav.chatLink],i=[a.getByRole("link",{name:r}).first(),...o.map(e=>a.locator(e).first()),a.locator(`a:has-text("${n}")`).first(),e.getByRole("link",{name:r}).first()];return await Gt(i)}function Jt(e){return!!e.url().includes("/web/geek/recommend")||(null!==e.frame("recommendFrame")||e.frames().some(e=>e.url().includes("recommend")))}async function Xt(e){if(e.url().includes("/web/chat/index"))return!0;try{const t=e.locator("#container.chat-container-private").first();return await t.count()>0&&await t.isVisible()}catch{return!1}}async function Qt(e,t=1e4,a=250){const n=Date.now()+t;for(;Date.now()<n;){if(Jt(e))return!0;if(e.isClosed())return!1;await e.waitForTimeout(Math.min(a,Math.max(n-Date.now(),0)))}return Jt(e)}async function ea(e,t=1e4,a=250){const n=Date.now()+t;for(;Date.now()<n;){if(await Xt(e))return!0;if(e.isClosed())return!1;await e.waitForTimeout(Math.min(a,Math.max(n-Date.now(),0)))}return await Xt(e)}var ta,aa=Vt.object({success:Vt.boolean(),alreadyOnChat:Vt.boolean(),usedSidebarClick:Vt.boolean(),chatReady:Vt.boolean(),page:Dt.optional(),error:Vt.string().optional()});function na(){return{getContextManager:h,findZhipinSidebarSectionLink:Kt,isZhipinChatSurfaceOpen:Xt,waitForZhipinChatSurface:ea,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,randomDelay:rt,toAttachedPageInfo:Ie,createVisualActivitySession:e=>new Lt(e),...ta}}async function ra(e,t,a){return await t.toAttachedPageInfo(e,a)}async function oa(e,t,a,n,r,o){return await a.fail(r),{success:!1,...o,page:await ra(e,t,n),error:r}}async function ia(e,t,a,n){await a.scrollIntoViewIfNeeded(),await t.moveVisualCursorToLocator(e,a,{durationMs:110,settleMs:30}),await a.hover(),await t.randomDelay(e,100,180),await t.showVisualClickOnLocator(e,a,{pulseDurationMs:180}),await a.click(),n.info("Clicked Boss sidebar nav: 沟通")}var sa=Ut({name:"zhipin_open_chat_page",description:"通过点击 Boss 左侧导航切换回「沟通」页,避免让编排器依赖站内 URL 猜测。",input:Vt.object({}),output:aa,execute:async(e,t)=>{const a=na(),n=a.getContextManager();t.logger.info("Opening Boss chat page via sidebar navigation");const r=await n.getPage("zhipin");await r.bringToFront().catch(()=>{});const o=a.createVisualActivitySession(r),i="正在切换到沟通页";if(await o.begin(i),await o.highlightSelector(Wt.nav.sidebar,{label:i,padding:10}),await a.isZhipinChatSurfaceOpen(r))return await o.succeed("已在沟通页"),{success:!0,alreadyOnChat:!0,usedSidebarClick:!1,chatReady:!0,page:await ra(n,a,r)};const s=await a.findZhipinSidebarSectionLink(r,"chat");if(!s)return await oa(n,a,o,r,"未找到沟通导航",{alreadyOnChat:!1,usedSidebarClick:!1,chatReady:!1});try{await ia(r,a,s,t.logger)}catch(e){return await oa(n,a,o,r,e instanceof Error?e.message:"点击沟通导航失败",{alreadyOnChat:!1,usedSidebarClick:!0,chatReady:!1})}return await a.waitForZhipinChatSurface(r)?(await o.succeed("已切换到沟通页"),{success:!0,alreadyOnChat:!1,usedSidebarClick:!0,chatReady:!0,page:await ra(n,a,r)}):await oa(n,a,o,r,"沟通页未就绪",{alreadyOnChat:!1,usedSidebarClick:!0,chatReady:!1})}});import{defineTool as ca}from"@roll-agent/sdk";import{z as da}from"zod";var la=da.object({success:da.boolean(),conversationId:da.string(),candidateId:da.string(),candidateName:da.string(),index:da.number(),hasUnread:da.boolean(),unreadCount:da.number(),lastMessageTime:da.string(),messagePreview:da.string(),error:da.string().optional()}),ua=ca({name:"zhipin_open_chat",description:"打开指定候选人的聊天窗口(优先按 conversationId,其次姓名,最后才用索引)",input:da.object({conversationId:da.string().optional().describe("会话 ID。若已从 `zhipin_read_messages` 获取,优先传这个,最稳定"),candidateName:da.string().optional().describe("候选人姓名。若用户说“打开鲁倩的聊天”,这里应提取为“鲁倩”"),index:da.number().optional().describe("候选人在列表中的索引。仅在缺少 conversationId 时兜底"),preferUnread:da.boolean().default(!1).describe("优先选择有未读消息的候选人")}),output:la,execute:async(e,t)=>{t.logger.info(`Opening chat: name=${e.candidateName??"N/A"}, index=${e.index??"N/A"}`);const a=h(),n=await a.getPage("zhipin");let r={conversationId:e.conversationId,candidateName:e.candidateName,index:e.index};if(e.preferUnread&&void 0===e.conversationId&&void 0===e.candidateName&&void 0===e.index){if(!await $t(a,n))return{success:!1,conversationId:"",candidateId:"",candidateName:"",index:-1,hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:"消息列表未加载"};const e=await a.getPage("zhipin"),t=(await Bt(e)).find(e=>e.hasUnread);t&&(r={conversationId:t.conversationId,candidateName:t.name,index:t.index})}const o=await Tt(a,n,r);return o&&o.found?(t.logger.info(`Opened chat with ${o.name} (index: ${o.index})`),{success:!0,conversationId:o.conversationId,candidateId:o.candidateId,candidateName:o.name,index:o.index,hasUnread:o.hasUnread,unreadCount:o.unreadCount,lastMessageTime:o.lastMessageTime,messagePreview:o.messagePreview}):{success:!1,conversationId:"",candidateId:"",candidateName:e.candidateName??"",index:e.index??-1,hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:o?.error??`未找到候选人: ${e.candidateName??`index ${e.index}`}`}}});import{defineTool as ma}from"@roll-agent/sdk";import{z as ga}from"zod";var fa="·",pa=/[--—–]/;function ha(e){return(e??"").trim()}function wa(e){const t=ha(e);if(!t||!pa.test(t))return;const[a=""]=t.split(pa);return ha(a)||void 0}function ya(e){const t=ha(e);if(!t)return{expectedLocation:"",expectedPosition:""};const[a="",n=""]=t.split(fa).map(e=>ha(e));return{expectedLocation:a,expectedPosition:n}}function ba(e){const t=ha(e.communicationPosition),{expectedLocation:a,expectedPosition:n}=ya(e.expectedJobText),r=wa(t);return{communicationPosition:t,expectedLocation:a,expectedPosition:n,...void 0!==r?{preferredBrand:r}:{}}}var va=ga.object({index:ga.number(),sender:ga.enum(["candidate","recruiter","system"]),messageType:ga.enum(["text","system","resume","wechat-exchange"]),content:ga.string(),time:ga.string()}),xa=ga.object({name:ga.string(),age:ga.string(),experience:ga.string(),education:ga.string(),communicationPosition:ga.string(),expectedPosition:ga.string(),expectedLocation:ga.string(),expectedSalary:ga.string(),tags:ga.array(ga.string())}),Sa=ga.object({success:ga.boolean(),conversationId:ga.string(),candidateId:ga.string(),candidateInfo:xa,preferredBrand:ga.string().optional(),chatMessages:ga.array(va),formattedHistory:ga.array(ga.string()),stats:ga.object({totalMessages:ga.number(),candidateMessages:ga.number(),recruiterMessages:ga.number(),systemMessages:ga.number()}),error:ga.string().optional()});function Ia(){return{name:"",age:"",experience:"",education:"",communicationPosition:"",expectedPosition:"",expectedLocation:"",expectedSalary:"",tags:[]}}function ka(e){return{success:!1,conversationId:"",candidateId:"",candidateInfo:Ia(),chatMessages:[],formattedHistory:[],stats:{totalMessages:0,candidateMessages:0,recruiterMessages:0,systemMessages:0},error:e}}function Ca(e,t){const a=e.trim().toLocaleLowerCase("zh-CN"),n=t.trim().toLocaleLowerCase("zh-CN");return a.length>0&&n.length>0&&(a===n||a.includes(n)||n.includes(a))}var Aa=ma({name:"zhipin_get_candidate_info",description:"提取候选人资料和完整聊天记录。可指定 conversationId 或 candidateName 自动打开对应聊天;若已从 `zhipin_read_messages` 获取 conversationId,优先传它。",input:ga.object({conversationId:ga.string().optional().describe("会话 ID。若已从 `zhipin_read_messages` 获取,优先传这个,最稳定"),candidateName:ga.string().optional().describe("候选人姓名。若用户说“查看鲁倩的聊天详情”,这里应提取为“鲁倩”"),index:ga.number().optional().describe("候选人在列表中的索引(可选,仅兜底)"),maxMessages:ga.number().default(100).describe("最多返回的消息条数")}),output:Sa,execute:async(e,t)=>{const a=e.maxMessages??100,n=h(),r=await n.getPage("zhipin"),o=new Lt(r),i=void 0!==e.conversationId||void 0!==e.candidateName||void 0!==e.index?"正在打开目标聊天":"正在准备当前聊天",s="正在提取聊天记录",c=async(e,t)=>(await o.fail(e),ka(t));await o.begin(i);try{const i=await Tt(n,r,{conversationId:e.conversationId,candidateName:e.candidateName,index:e.index}),d=await n.getPage("zhipin");if(await o.retarget(d),i&&!i.found)return await c("打开聊天失败",i.error??"打开聊天失败");t.logger.info("Extracting candidate info"+(i?` for ${i.name}`:" (current window)")),await o.begin(s),await o.highlightSelector(".chat-conversation, .conversation-box, .conversation-message",{label:s,padding:12});const l=i?.name??e.candidateName??"",u=await mt(d);if(l.length>0&&(!u||!Ca(l,u.candidateName)))return await c("聊天面板未同步",`右侧聊天面板未切换到 ${l}`);const m=await ut(d);if(!m)return await c("未识别当前会话","未能提取当前选中聊天的 conversationId/candidateId");if(i&&m.conversationId!==i.conversationId)return await c("当前会话不一致",`当前选中会话与目标会话不一致: ${i.name||i.conversationId}`);if(u&&m.candidateName.length>0&&!Ca(m.candidateName,u.candidateName))return await c("左右面板不一致",`左侧选中会话与右侧聊天面板不一致: ${m.candidateName} / ${u.candidateName}`);try{await d.waitForSelector(".chat-message-list .message-item, .conversation-message .message-item",{timeout:8e3})}catch{}const g=await d.evaluate(e=>{const t=document.querySelector(".chat-conversation")??document.querySelector(".conversation-box")??document,a=t.querySelector(".base-info-single-detial, .base-info-content, .base-info-single-container"),n=a?.querySelector(".name-box, .base-name, .chat-user-name, .geek-name")?.textContent?.trim()??"",r=a?a.querySelectorAll(":scope > div"):t.querySelectorAll(".geek-info-item, .base-info-item"),o=[];r.forEach(e=>{const t=e.textContent?.trim();t&&o.push(t)});const i=o.join(" "),s=i.match(/(\d{2,3})岁/),c=s?s[1]+"岁":"",d=i.match(/(\d+年(?:以上)?|应届生|在校生)/),l=d?.[1]??"",u=i.match(/(初中|高中|中专|大专|本科|硕士|博士)/)?.[1]??"";let m="";const g=t.querySelector(".position-name");if(g){const e=g.cloneNode(!0);e.querySelectorAll(".popover-wrap, .tooltip-job").forEach(e=>e.remove()),m=e.textContent?.trim()??""}let f="";const p=t.querySelector(".position-item.expect .value.job");p&&(f=p.textContent?.trim()??"");const h=t.querySelector(".position-item.expect .high-light-orange")?.textContent?.trim()??"",w=[];a&&a.querySelectorAll(".geek-tag, .base-info-item .high-light-boss").forEach(e=>{const t=e.textContent?.trim();t&&!t.includes("更换职位")&&t.length<20&&w.push(t)});const y=t.querySelectorAll(".chat-message-list > .message-item, .conversation-message .message-item"),b=/\d{1,2}:\d{2}(?::\d{2})?|\d{4}-\d{2}-\d{2}/,v=[];let x=0;return y.forEach(t=>{if(x>=e)return;const a=null!==t.querySelector(".item-friend"),n=null!==t.querySelector(".item-myself"),r=null!==t.querySelector(".item-system"),o=null!==t.querySelector(".item-resume"),i=null!==t.querySelector(".message-dialog-center");let s="system",c="text";a?s="candidate":n?s="recruiter":(r||i)&&(s="system",c="system"),o&&(c="resume");const d=t.querySelector(".message-card-top-wrap, [class*='d-top-text']");if(d){const e=d.textContent??"";(e.includes("微信")||e.includes("WeChat"))&&(c="wechat-exchange")}const l=t.querySelector(".message-time .time, .message-time"),u=(l?.textContent??"").match(b),m=u?u[0]:"";let g="";if("wechat-exchange"===c&&d){const e=d.textContent??"",t=e.match(/\b(\d{8,15})\b/),a=e.match(/微信[::号]*\s*([a-zA-Z0-9_-]{5,20})/);g=t?`[微信号: ${t[1]}]`:a?`[微信号: ${a[1]}]`:"[交换微信]"}else if(d){const e=t.querySelector(".message-card-top-title"),a=t.querySelector(".dialog-content, .message-card-top-text");g=(e?.textContent?.trim()??a?.textContent?.trim()??"").trim()}else{const e=t.querySelector(".text span, .text-content, .text");e&&(g=(e.textContent?.trim()??"").replace(b,"").replace("已读","").trim())}(g||"text"!==c)&&(v.push({index:x,sender:s,messageType:c,content:g,time:m}),x++)}),{candidateInfo:{name:n,age:c,experience:l,education:u,communicationPosition:m,expectedJobText:f,expectedSalary:h,tags:w},messages:v}},a),f=ba({communicationPosition:g.candidateInfo.communicationPosition,expectedJobText:g.candidateInfo.expectedJobText}),p=g.messages.filter(e=>"candidate"===e.sender||"recruiter"===e.sender).map(e=>`${"candidate"===e.sender?"求职者":"我"}: ${e.content}`),h={totalMessages:g.messages.length,candidateMessages:g.messages.filter(e=>"candidate"===e.sender).length,recruiterMessages:g.messages.filter(e=>"recruiter"===e.sender).length,systemMessages:g.messages.filter(e=>"system"===e.sender).length};return await o.succeed(`已提取 ${h.totalMessages} 条聊天记录`),t.logger.info(`Extracted info for ${g.candidateInfo.name}: ${h.totalMessages} messages`),{success:!0,conversationId:m.conversationId,candidateId:m.candidateId,candidateInfo:{name:g.candidateInfo.name,age:g.candidateInfo.age,experience:g.candidateInfo.experience,education:g.candidateInfo.education,communicationPosition:f.communicationPosition,expectedPosition:f.expectedPosition,expectedLocation:f.expectedLocation,expectedSalary:g.candidateInfo.expectedSalary,tags:g.candidateInfo.tags},...void 0!==f.preferredBrand?{preferredBrand:f.preferredBrand}:{},chatMessages:g.messages,formattedHistory:p,stats:h}}catch(e){throw await o.fail("提取聊天记录失败"),e}}});import{defineTool as Ra}from"@roll-agent/sdk";import{z as Ma}from"zod";var Pa=30,Ea=new Set(["招聘规范","消息","首页","推荐牛人","看简历","我的客服","面试","招聘数据","账号权益","升级VIP","职位","职位管理","牛人","公司","数据统计","设置","帮助中心","登录","注册","退出登录","退出","BOSS直聘","下载APP","搜索","发布职位"]);function $a(e){const t=e.trim();return!(0===t.length||t.length>Pa)&&(!Ea.has(t)&&!/登录|注册|退出|下载|帮助|设置|管理/.test(t))}function Ba(e){const t=[],a=/(link|button|menuitem|img|heading)\s+"([^"]+)"/g;let n;for(;null!==(n=a.exec(e));){const e=n[2]?.trim()??"";e.length>0&&t.push({role:n[1]??"",name:e})}return t}function _a(e){let t=e.priority;return Ea.has(e.text.trim())&&(t+=10),e.text.trim().length>10&&(t+=5),/^[\u4e00-\u9fff]{2,4}$/.test(e.text.trim())&&(t-=.5),void 0!==e.xRatio&&(t-=4*(e.xRatio-.5)),t}function Na(e){const t=e.filter(e=>$a(e.text));if(0===t.length)return{found:!1};const a=new Map;for(const e of t){const t=e.text.trim(),n=a.get(t)??new Set;n.add(e.strategy),a.set(t,n)}const n=t.map(e=>{let t=_a(e);const n=a.get(e.text.trim())?.size??1;return n>1&&(t-=.5*n),{evidence:e,score:t}}).sort((e,t)=>e.score-t.score)[0];return n?{found:!0,username:n.evidence.text.trim(),strategy:n.evidence.strategy,source:n.evidence.source}:{found:!1}}async function Ta(e){const t=[e.getByRole("banner"),e.locator("header").first(),e.getByRole("navigation").first(),e.locator("#header")];for(const e of t)try{if(await e.count()>0&&await e.first().isVisible())return e.first()}catch{}return null}async function za(e,t,a,n){const r=[],o=t.viewportSize(),i=o?.width??1280;try{const t=await e.getByRole(a).all();for(const e of t)try{if(!await e.isVisible())continue;const t=(await e.textContent()??"").trim();if(t.length>0&&t.length<=Pa){let o;try{const t=await e.boundingBox();t&&(o=(t.x+t.width/2)/i)}catch{}r.push({text:t,strategy:n,priority:1,source:`role:${a}`,xRatio:o})}}catch{}}catch{}return r}async function qa(e){const t=[];try{const a=Ba(await e.ariaSnapshot({timeout:3e3}));for(const{role:e,name:n}of a)n.length>0&&n.length<=Pa&&t.push({text:n,strategy:"aria-snapshot",priority:2,source:`aria:${e}:${n}`})}catch{}return t}async function La(e){const t=[];try{const a=await e.evaluate((e,t)=>{const a=[],n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const e=n.currentNode.textContent?.trim();e&&e.length>0&&e.length<=t&&a.push(e)}return a},Pa);for(const e of a)t.push({text:e,strategy:"leaf-text",priority:3,source:"leaf-text"})}catch{}return t}async function Fa(e){const t=[];try{const a=[Wt.username.primary,...Wt.username.fallbacks],n=await e.evaluate(e=>e.map(e=>{try{return{selector:e,text:document.querySelector(e)?.textContent?.trim()??""}}catch{return{selector:e,text:""}}}),a);for(const{selector:e,text:a}of n)a.length>0&&a.length<=Pa&&t.push({text:a,strategy:"css-fallback",priority:4,source:e})}catch{}return t}async function ja(e){const t=await Ta(e),a=t?(await Promise.all([za(t,e,"link","role-link"),za(t,e,"button","role-button"),qa(t)])).flat():[],n=Na(a);if(n.found){const e=n.username;if(new Set(a.filter(t=>t.text.trim()===e).map(e=>e.strategy)).size>=2)return a}const r=(await Promise.all([t?La(t):Promise.resolve([]),Fa(e)])).flat();return[...a,...r]}async function Oa(e,t=ja){const a=Na(await t(e));if(!a.found)throw new Error("未找到用户名,请确认当前页面已登录招聘者账号。");return{platform:"zhipin",username:a.username,strategy:a.strategy,source:a.source}}function Ua(e,t){return void 0!==t.accountId?e.accountId===t.accountId:e.username===t.username}import{z as Da}from"zod";var Va="reply-authority-service",Wa="browser-use-agent/zhipin_send_reply",Ha="zhipin",Za=60,Ga=Da.object({platform:Da.literal(Ha),username:Da.string().min(1),accountId:Da.string().min(1).optional()}),Ya=Da.object({v:Da.literal(2),iss:Da.literal(Va),kid:Da.string().min(1),jti:Da.string().min(1),iat:Da.number().int(),exp:Da.number().int(),aud:Da.literal(Wa),platform:Da.literal(Ha),tenantId:Da.string().min(1),conversationId:Da.string().min(1),candidateId:Da.string().min(1),reply:Da.string(),policyVersion:Da.string().min(1),recruiterBinding:Ga}),Ka=Da.object({kid:Da.string().min(1),algorithm:Da.literal("Ed25519"),publicKey:Da.string().min(1),validFrom:Da.string().min(1),validUntil:Da.string().optional()}),Ja=Da.object({keys:Da.array(Ka)}),Xa=new Map;function Qa(e){for(const[t,a]of Xa)a<e-Za&&Xa.delete(t)}function en(e,t=Math.floor(Date.now()/1e3)){return Qa(t),Xa.has(e)}function tn(e,t,a=Math.floor(Date.now()/1e3)){Qa(a),Xa.set(e,t)}import{createPublicKey as an,verify as nn}from"node:crypto";var rn=new Map,on=null;function sn(){const e=process.env.REPLY_AUTHORITY_KEYS_URL?.trim();if(!e)throw new Error("REPLY_AUTHORITY_KEYS_URL 未配置,browser-use-agent 无法拉取 Reply Authority 公钥。");return e}function cn(e){return rn=new Map(e.map(e=>[e.kid,e]))}async function dn(){if(on)return on;on=(async()=>{const e=await fetch(sn()),t=await e.json();if(!e.ok)throw new Error(`Reply Authority 公钥拉取失败 (${e.status})`);return cn(Ja.parse(t).keys)})();try{return await on}finally{on=null}}async function ln(){await dn()}async function un(e){const t=rn.get(e);if(t)return t;const a=(await dn()).get(e);if(!a)throw new Error(`Unknown key ID: ${e}`);return a}function mn(e){if("object"==typeof e&&null!==e&&"v"in e&&"number"==typeof e.v&&2!==e.v)throw new Error("unexpected envelope version")}function gn(e){const t=e.split("."),a=t[0],n=t[1];if(2!==t.length||void 0===a||void 0===n||0===a.length||0===n.length)throw new Error("Invalid signed envelope format");let r,o="";try{o=Buffer.from(a,"base64url").toString("utf-8")}catch{throw new Error("Invalid signed envelope format")}try{r=JSON.parse(o)}catch{throw new Error("Envelope payload schema validation failed")}mn(r);const i=Ya.safeParse(r);if(!i.success)throw new Error("Envelope payload schema validation failed");return{payload:i.data,payloadJson:o,signatureBase64:n}}function fn(e,t){if(e.exp<=e.iat)throw new Error("Envelope expiry must be after issue time");if(e.exp<t-Za)throw new Error("Envelope expired");if(e.iat>t+Za)throw new Error("Envelope issued in the future")}async function pn(e,t=Math.floor(Date.now()/1e3)){const a=gn(e),n=await un(a.payload.kid),r=an({key:Buffer.from(n.publicKey,"base64url"),format:"der",type:"spki"});if(!nn(null,Buffer.from(a.payloadJson,"utf-8"),r,Buffer.from(a.signatureBase64,"base64url")))throw new Error("Signature verification failed");return fn(a.payload,t),a.payload}var hn=Ma.object({success:Ma.boolean(),sentMessage:Ma.string(),error:Ma.string().optional()});function wn(e,t){const a=e.trim().toLocaleLowerCase("zh-CN"),n=t.trim().toLocaleLowerCase("zh-CN");return a.length>0&&n.length>0&&(a===n||a.includes(n)||n.includes(a))}var yn=Ra({name:"zhipin_send_reply",description:"发送消息。只接受由 Reply Authority Service 签发的 signedEnvelope;可指定 candidateName 自动打开对应聊天后发送,或不传则发送到当前选中的聊天窗口。",input:Ma.object({signedEnvelope:Ma.string().describe("Reply Authority Service 返回的紧凑签名信封"),candidateName:Ma.string().optional().describe("候选人姓名。若用户说“回复鲁倩”,这里应提取为“鲁倩”"),index:Ma.number().optional().describe("候选人在列表中的索引(可选)")}),output:hn,execute:async(e,t)=>{let a="";if(!b()){const e="Reply Authority 公钥尚未成功预加载,当前无法发送签名回复。请检查启动日志、`REPLY_AUTHORITY_KEYS_URL` 配置,以及 `browser_status.replyAuthorityKeysLoaded`。";return t.logger.error(e),{success:!1,sentMessage:a,error:e}}const n=h(),r=await n.getPage("zhipin");let o=r;try{const i=await pn(e.signedEnvelope);if(a=i.reply,en(i.jti))return{success:!1,sentMessage:a,error:"token 已消费,禁止重放"};const s=await Tt(n,r,{conversationId:i.conversationId,candidateName:e.candidateName,index:e.index});if(s&&!s.found)return{success:!1,sentMessage:a,error:s.error};if(!s){if(!await $t(n,r))return{success:!1,sentMessage:a,error:"消息列表未加载"}}o=await n.getPage("zhipin");const c=await mt(o),d=await ut(o);if(!d)return{success:!1,sentMessage:a,error:"未能提取当前聊天的 conversationId/candidateId"};if(c&&d.candidateName.length>0&&!wn(d.candidateName,c.candidateName))return{success:!1,sentMessage:a,error:`左侧选中会话与右侧聊天面板不一致: ${d.candidateName} / ${c.candidateName}`};if(d.conversationId!==i.conversationId||d.candidateId!==i.candidateId)return{success:!1,sentMessage:a,error:"发送目标与签名不匹配"};const l=await Oa(o);if(!Ua(l,i.recruiterBinding))return{success:!1,sentMessage:a,error:`recruiter 绑定不匹配:当前账号 ${l.username} 与签发时 ${i.recruiterBinding.username} 不一致`};t.logger.info(`Sending message (${a.length} chars) to ${d.candidateName||d.candidateId}`);const u="#boss-chat-editor-input, textarea.chat-input, .chat-input";await o.waitForSelector(u,{timeout:5e3});const m=o.locator(u).first();await Z(o,m);await o.evaluate(e=>{const t=document.querySelector(e);return"true"===t?.getAttribute("contenteditable")},u)?(await m.focus(),await o.evaluate(e=>{const t=document.querySelector(e.sel);t&&(t.innerHTML=e.msg.split("\n").map(e=>`<p>${e}</p>`).join(""))},{sel:u,msg:a}),await m.dispatchEvent("input",{bubbles:!0})):await o.fill(u,a),await rt(o,200,500);const g=await o.evaluate(()=>{document.querySelectorAll("[data-roll-send-btn]").forEach(e=>{e.removeAttribute("data-roll-send-btn")});const e=[".submit-content .submit.active",".submit-content .submit",".submit-content",".btn-send"];for(const t of e){const e=document.querySelector(t);if(e&&e.offsetWidth>0)return{found:!0,selector:t}}const t=Array.from(document.querySelectorAll("span"));for(const e of t)if("发送"===e.textContent?.trim()&&e.offsetWidth>0)return e.setAttribute("data-roll-send-btn","true"),{found:!0,selector:'[data-roll-send-btn="true"]'};return{found:!1}});if(!g.found)return{success:!1,sentMessage:a,error:"未找到发送按钮"};const f=o.locator(g.selector).first();return await f.scrollIntoViewIfNeeded(),await Z(o,f),await f.hover(),await ot(o),await G(o,f),await f.click(),await rt(o,500,1200),tn(i.jti,i.exp),t.logger.info("Message sent successfully"),{success:!0,sentMessage:a}}catch(e){return{success:!1,sentMessage:a,error:e instanceof Error?e.message:String(e)}}finally{await o.evaluate(()=>{document.querySelectorAll("[data-roll-send-btn]").forEach(e=>{e.removeAttribute("data-roll-send-btn")})}).catch(()=>{})}}});import{defineTool as bn}from"@roll-agent/sdk";import{z as vn}from"zod";var xn=vn.object({success:vn.boolean(),exchanged:vn.boolean(),wechatNumber:vn.string().optional(),error:vn.string().optional()}),Sn=bn({name:"zhipin_exchange_wechat",description:'换微信。可指定 candidateName 自动打开对应聊天后执行,或不传则在当前窗口执行;例如"和鲁倩换微信"应提取 candidateName=鲁倩。',input:vn.object({conversationId:vn.string().optional().describe("会话 ID。若已从消息列表拿到,优先传这个"),candidateName:vn.string().optional().describe('候选人姓名。若用户说"和鲁倩换微信",这里应提取为"鲁倩"'),index:vn.number().optional().describe("候选人在列表中的索引(可选)")}),output:xn,execute:async(e,t)=>{const a=h(),n=await a.getPage("zhipin"),r=await Tt(a,n,{conversationId:e.conversationId,candidateName:e.candidateName,index:e.index});if(r&&!r.found)return{success:!1,exchanged:!1,error:r.error};t.logger.info("Starting WeChat exchange"+(r?` with ${r.name}`:""));const o=await a.getPage("zhipin");try{if(!(await o.evaluate(()=>{const e=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},t=[".operate-exchange-left .operate-btn","span.operate-btn"];for(const a of t){const t=Array.from(document.querySelectorAll(a));for(const a of t){const t=a.textContent?.trim()??"";if(t.includes("换微信")&&e(a))return a.setAttribute("data-roll-wechat-btn","true"),{found:!0,text:t}}}const a=Array.from(document.querySelectorAll("span"));for(const t of a){const a=t.textContent?.trim()??"";if(a.includes("换微信")&&e(t))return t.setAttribute("data-roll-wechat-btn","true"),{found:!0,text:a}}return{found:!1}})).found)return{success:!1,exchanged:!1,error:"未找到「换微信」按钮"};await rt(o,200,400),await Y(o,'[data-roll-wechat-btn="true"]'),await K(o,'[data-roll-wechat-btn="true"]'),await o.click('[data-roll-wechat-btn="true"]'),await o.evaluate(()=>{document.querySelector("[data-roll-wechat-btn]")?.removeAttribute("data-roll-wechat-btn")}),await rt(o,400,800);let e=!1;for(let t=0;t<8;t++){if(await o.evaluate(()=>{const e=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},t=document.querySelector(".exchange-tooltip");if(t&&e(t))return!0;const a=document.querySelectorAll("div, section, aside");for(const t of Array.from(a)){if((t.textContent??"").includes("交换微信")&&t.querySelector(".boss-btn-primary, .boss-btn")&&e(t))return!0}return!1})){e=!0;break}await rt(o,400,800)}if(!e)return{success:!1,exchanged:!1,error:"确认对话框未弹出"};await ot(o);if(!(await o.evaluate(()=>{const e=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},t=document.querySelector(".exchange-tooltip");if(t){const a=[".btn-box .boss-btn-primary.boss-btn",".btn-box span.boss-btn-primary","span.boss-btn-primary",".boss-btn-primary"];for(const n of a){const a=t.querySelector(n);if(a&&e(a))return a.setAttribute("data-roll-confirm-btn","true"),{found:!0,text:a.textContent?.trim()??""}}}const a=document.querySelectorAll("div, section, aside");for(const t of Array.from(a)){if(!(t.textContent??"").includes("交换微信"))continue;const a=t.querySelectorAll("span.boss-btn-primary, button.boss-btn-primary, span.boss-btn, button.boss-btn");for(const t of Array.from(a)){const a=t.textContent?.trim()??"";if("确定"===a&&e(t))return t.setAttribute("data-roll-confirm-btn","true"),{found:!0,text:a}}}return{found:!1}})).found)return{success:!1,exchanged:!1,error:"未找到确认按钮"};await rt(o,200,400),await Y(o,'[data-roll-confirm-btn="true"]'),await K(o,'[data-roll-confirm-btn="true"]'),await o.click('[data-roll-confirm-btn="true"]'),await o.evaluate(()=>{document.querySelector("[data-roll-confirm-btn]")?.removeAttribute("data-roll-confirm-btn")}),await rt(o,1500,2500);const a=await o.evaluate(()=>{const e=[".message-card-top-wrap",'[class*="d-top-text"]',".message-card-top-title"];for(const t of e){const e=Array.from(document.querySelectorAll(t));for(let t=e.length-1;t>=0;t--){const a=e[t]?.textContent??"",n=a.match(/\b(\d{8,15})\b/);if(n)return n[1];const r=a.match(/微信[::号]*\s*([a-zA-Z0-9_-]{5,20})/);if(r)return r[1];const o=a.match(/\b([a-zA-Z][a-zA-Z0-9_-]{5,19})\b/);if(o&&!["微信","WeChat"].includes(o[1]))return o[1]}}const t=Array.from(document.querySelectorAll(".message-item"));for(let e=t.length-1;e>=0;e--){const a=t[e]?.querySelector('.message-card-top-wrap, [class*="d-top-text"]');if(a){const e=(a.textContent??"").match(/\b(\d{8,15})\b/);if(e)return e[1]}}return null});return t.logger.info("WeChat exchanged"+(a?`, number: ${a}`:"")),{success:!0,exchanged:!0,...null!==a?{wechatNumber:a}:{}}}catch(e){return{success:!1,exchanged:!1,error:e instanceof Error?e.message:String(e)}}}});import{defineTool as In}from"@roll-agent/sdk";import{z as kn}from"zod";var Cn,An=kn.object({success:kn.boolean(),username:kn.string(),usedSelector:kn.string().optional(),usedStrategy:kn.string().optional(),source:kn.string().optional(),error:kn.string().optional()});function Rn(){return{getContextManager:h,findHeaderScope:Ta,getCurrentZhipinRecruiterIdentity:Oa,createVisualActivitySession:e=>new Lt(e),...Cn}}var Mn=In({name:"zhipin_get_username",description:"获取当前登录的招聘者用户名",input:kn.object({}),output:An,execute:async(e,t)=>{t.logger.info("Getting zhipin username");const a=Rn();let n;try{const e=a.getContextManager(),r=await e.getPage("zhipin");n=a.createVisualActivitySession(r),await r.bringToFront().catch(()=>{}),await n.begin("正在识别登录账号");const o=await a.findHeaderScope(r);o&&await n.highlightLocator(o,{label:"正在识别登录账号",padding:10});const i=await a.getCurrentZhipinRecruiterIdentity(r);return await n.succeed(`已识别账号:${i.username}`),t.logger.info(`Username: ${i.username} (strategy: ${i.strategy}, source: ${i.source})`),{success:!0,username:i.username,usedSelector:"css-fallback"===i.strategy?i.source:void 0,usedStrategy:i.strategy,source:i.source}}catch(e){return await(n?.fail("获取用户名失败")),{success:!1,username:"",error:e instanceof Error?`获取用户名失败:${e.message}`:"获取用户名失败"}}}});import{defineTool as Pn}from"@roll-agent/sdk";import{z as En}from"zod";var $n=".candidate-card-wrap",Bn="[data-geek], .geek-item",_n=`${$n}, ${Bn}`;function Nn(e){return e.evaluate(e=>document.querySelectorAll(e.primarySelector).length>0?e.primarySelector:e.fallbackSelector,{primarySelector:$n,fallbackSelector:Bn})}function Tn(e){return e.frame("recommendFrame")??e.frames().find(e=>e.url().includes("recommend"))??e}async function zn(e,t=1e4){try{return await e.waitForSelector(_n,{timeout:t}),!0}catch{return!1}}async function qn(e,t){const a=await Nn(e),n=e.locator(a);if(await n.count()<=t)return{found:!1,cardSelector:a,candidateId:"",name:"",hasGreetButton:!1,error:"索引超出范围"};const r=n.nth(t),o=await r.evaluate(e=>{const t=e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??"",a=e.querySelector(".name")?.textContent?.trim()??"",n=e.querySelector("button.btn.btn-greet");return{candidateId:t,name:a,hasGreetButton:null!==n&&n.offsetWidth>0}});return{found:!0,cardSelector:a,candidateId:o.candidateId,name:o.name,hasGreetButton:o.hasGreetButton}}var Ln,Fn=En.object({index:En.number(),candidateId:En.string(),name:En.string(),age:En.string(),experience:En.string(),education:En.string(),workStatus:En.string(),company:En.string(),currentPosition:En.string(),expectedLocation:En.string(),expectedPosition:En.string(),expectedSalary:En.string(),tags:En.array(En.string()),buttonText:En.string()}),jn=En.object({success:En.boolean(),candidates:En.array(Fn),total:En.number(),error:En.string().optional()});function On(){return{getContextManager:h,getRecommendTarget:Tn,waitForRecommendList:zn,createVisualActivitySession:e=>new Lt(e),...Ln}}var Un=Pn({name:"zhipin_get_candidate_list",description:"获取推荐列表页的候选人卡片信息",input:En.object({maxResults:En.number().optional().describe("最多返回条数")}),output:jn,execute:async(e,t)=>{t.logger.info("Getting candidate list from recommend page");const a=On(),n=a.getContextManager(),r=await n.getPage("zhipin");let o=a.getRecommendTarget(r);const i=a.createVisualActivitySession(o);await i.begin("正在打开推荐列表");const s=await a.waitForRecommendList(o);if(o=a.getRecommendTarget(r),await i.retarget(o),!s)return await i.fail("推荐列表未加载"),{success:!1,candidates:[],total:0,error:"推荐列表未加载"};try{const a="正在读取推荐列表";await i.begin(a),await i.highlightSelector(".candidate-card-wrap, [data-geek], .geek-item",{label:a,padding:8});const n=await o.evaluate(e=>{let t=Array.from(document.querySelectorAll(".candidate-card-wrap"));0===t.length&&(t=Array.from(document.querySelectorAll("[data-geek], .geek-item")));const a=e??t.length,n=[];return t.forEach((e,t)=>{if(t>=a)return;const r=e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??"",o=e.querySelector(".name")?.textContent?.trim()??"";let i="",s="",c="",d="";const l=e.querySelector(".base-info.join-text-wrap, .base-info");if(l){const e=[];if(l.querySelectorAll(":scope > *").forEach(t=>{const a=t.textContent?.trim();a&&e.push(a)}),e.length<=1&&(e.length=0,l.childNodes.forEach(t=>{if(t.nodeType===Node.TEXT_NODE){const a=t.textContent?.trim();a&&e.push(a)}})),e.length<=1){const t=l.textContent?.trim()??"";e.length=0,t.split(/[丨·|]/).forEach(t=>{const a=t.trim();a&&e.push(a)})}for(const t of e)!i&&t.includes("岁")?i=t:!s&&(t.includes("年")||t.includes("应届")||t.includes("在校"))?s=t:!c&&/(初中|高中|中专|中技|大专|本科|硕士|博士)/.test(t)?c=t:!d&&/(在职|离职|在校)/.test(t)&&(d=t)}const u=e.querySelector(".timeline-wrap.work-exps .content.join-text-wrap")??e.querySelector(".timeline-wrap.work-exps .content"),m=(u?.textContent?.trim()??"").split("·").map(e=>e.trim()),g=m[0]??"",f=m[1]??"";let p="",h="";const w=e.querySelector(".row-flex:not(.geek-desc)");if(w){const e=w.querySelector(".label"),t=w.querySelector(".content"),a=e?.textContent??"";if((a.includes("期望")||a.includes("最近关注"))&&t){const e=(t.textContent?.trim()??"").split("·").map(e=>e.trim());p=e[0]??"",h=e[1]??""}}if(!p){const t=e.querySelector(".timeline-wrap.expect .content.join-text-wrap")??e.querySelector(".timeline-wrap.expect .content");if(t){const e=(t.textContent?.trim()??"").split("·").map(e=>e.trim());p=e[0]??"",h=e[1]??""}}const y=e.querySelector(".salary-wrap")?.textContent?.trim()??"",b=[];e.querySelectorAll(".tags-wrap .tag-item, .tags-wrap .tag, .tags-wrap span").forEach(e=>{const t=e.textContent?.trim();t&&b.push(t)});const v=e.querySelector("button.btn.btn-greet")?.textContent?.trim()??"";n.push({index:t,candidateId:r,name:o,age:i,experience:s,education:c,workStatus:d,company:g,currentPosition:f,expectedLocation:p,expectedPosition:h,expectedSalary:y,tags:b,buttonText:v})}),n},e.maxResults);return await i.succeed(`已读取 ${n.length} 位候选人`),t.logger.info(`Found ${n.length} candidates in recommend list`),{success:!0,candidates:n,total:n.length}}catch(e){throw await i.fail("读取推荐列表失败"),e}}});import{defineTool as Dn}from"@roll-agent/sdk";import{BrowserPageInfoSchema as Vn}from"@roll-agent/browser";import{z as Wn}from"zod";var Hn,Zn=Wn.object({success:Wn.boolean(),alreadyOnRecommend:Wn.boolean(),usedSidebarClick:Wn.boolean(),recommendReady:Wn.boolean(),page:Vn.optional(),error:Wn.string().optional()});function Gn(){return{getContextManager:h,getRecommendTarget:Tn,findZhipinSidebarSectionLink:Kt,isZhipinRecommendSurfaceOpen:Jt,waitForZhipinRecommendSurface:Qt,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,randomDelay:rt,toAttachedPageInfo:Ie,createVisualActivitySession:e=>new Lt(e),...Hn}}async function Yn(e,t,a){return await t.toAttachedPageInfo(e,a)}async function Kn(e,t,a,n,r,o){return await a.fail(r),{success:!1,...o,page:await Yn(e,t,n),error:r}}async function Jn(e,t,a,n){await a.scrollIntoViewIfNeeded(),await t.moveVisualCursorToLocator(e,a,{durationMs:110,settleMs:30}),await a.hover(),await t.randomDelay(e,100,180),await t.showVisualClickOnLocator(e,a,{pulseDurationMs:180}),await a.click(),n.info("Clicked Boss sidebar nav: 推荐牛人")}var Xn=Dn({name:"zhipin_open_recommend_page",description:"通过点击 Boss 左侧导航切换到「推荐牛人」页,避免让编排器依赖站内 URL 猜测。",input:Wn.object({}),output:Zn,execute:async(e,t)=>{const a=Gn(),n=a.getContextManager();t.logger.info("Opening Boss recommend page via sidebar navigation");const r=await n.getPage("zhipin");await r.bringToFront().catch(()=>{});const o=a.createVisualActivitySession(r),i="正在切换到推荐牛人页";if(await o.begin(i),await o.highlightSelector(Wt.nav.sidebar,{label:i,padding:10}),a.isZhipinRecommendSurfaceOpen(r))return await o.retarget(a.getRecommendTarget(r)),await o.succeed("已在推荐牛人页"),{success:!0,alreadyOnRecommend:!0,usedSidebarClick:!1,recommendReady:!0,page:await Yn(n,a,r)};const s=await a.findZhipinSidebarSectionLink(r,"recommend");if(!s)return await Kn(n,a,o,r,"未找到推荐牛人导航",{alreadyOnRecommend:!1,usedSidebarClick:!1,recommendReady:!1});try{await Jn(r,a,s,t.logger)}catch(e){return await Kn(n,a,o,r,e instanceof Error?e.message:"点击推荐牛人导航失败",{alreadyOnRecommend:!1,usedSidebarClick:!0,recommendReady:!1})}const c=await a.waitForZhipinRecommendSurface(r);return await o.retarget(a.getRecommendTarget(r)),c?(await o.succeed("已切换到推荐牛人页"),{success:!0,alreadyOnRecommend:!1,usedSidebarClick:!0,recommendReady:!0,page:await Yn(n,a,r)}):await Kn(n,a,o,r,"推荐牛人页未就绪",{alreadyOnRecommend:!1,usedSidebarClick:!0,recommendReady:!1})}});import{defineTool as Qn}from"@roll-agent/sdk";import{z as er}from"zod";var tr,ar=er.object({index:er.number(),candidateName:er.string(),candidateId:er.string(),success:er.boolean(),error:er.string().optional()}),nr=er.object({success:er.boolean(),results:er.array(ar),summary:er.object({total:er.number(),succeeded:er.number(),failed:er.number()})});function rr(){return{getContextManager:h,getRecommendTarget:Tn,waitForRecommendList:zn,inspectRecommendCard:qn,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,humanDelay:ot,shouldAddRandomBehavior:ct,performRandomScroll:it,createVisualActivitySession:e=>new Lt(e),...tr}}var or=Qn({name:"zhipin_say_hello",description:"在推荐列表页对候选人点击「打招呼」按钮(支持批量)",input:er.object({indices:er.array(er.number()).describe("要打招呼的候选人索引列表")}),output:nr,execute:async(e,t)=>{t.logger.info(`Saying hello to ${e.indices.length} candidates`);const a=rr(),n=a.getContextManager(),r=await n.getPage("zhipin");let o=a.getRecommendTarget(r);const i=a.createVisualActivitySession(o),s=e.indices.length>1?"正在批量打招呼":"正在打招呼";await i.begin("正在打开推荐列表");const c=await a.waitForRecommendList(o);if(o=a.getRecommendTarget(r),await i.retarget(o),!c){await i.fail("推荐列表未加载");const t=e.indices.map(e=>({index:e,candidateName:"",candidateId:"",success:!1,error:"推荐列表未加载"}));return{success:!1,results:t,summary:{total:t.length,succeeded:0,failed:t.length}}}await i.begin(s),await i.highlightSelector(".candidate-card-wrap, [data-geek], .geek-item",{label:s,padding:8});const d=[];for(const t of e.indices)try{const e=await a.inspectRecommendCard(o,t);if(e.found)if(e.hasGreetButton){const n=o.locator(e.cardSelector).nth(t),s=n.locator("button.btn.btn-greet").first();await i.highlightLocator(n,{label:`正在定位第 ${t+1} 位候选人`,padding:10}),await s.scrollIntoViewIfNeeded(),await a.moveVisualCursorToLocator(r,s,{durationMs:90,settleMs:20,target:o}),await s.hover(),await a.showVisualClickOnLocator(r,s,{pulseDurationMs:160,target:o}),await s.click(),d.push({index:t,candidateName:e.name,candidateId:e.candidateId,success:!0})}else d.push({index:t,candidateName:e.name,candidateId:e.candidateId,success:!1,error:"未找到打招呼按钮"});else d.push({index:t,candidateName:"",candidateId:"",success:!1,...void 0!==e.error?{error:e.error}:{}});await a.humanDelay(r),a.shouldAddRandomBehavior(.3)&&await a.performRandomScroll(r)}catch(e){d.push({index:t,candidateName:"",candidateId:"",success:!1,error:e instanceof Error?e.message:String(e)})}const l={total:d.length,succeeded:d.filter(e=>e.success).length,failed:d.filter(e=>!e.success).length};return 0===l.failed?await i.succeed(`已完成 ${l.succeeded}/${l.total} 位候选人`):await i.fail(`已完成 ${l.succeeded}/${l.total} 位候选人`),t.logger.info(`Say hello: ${l.succeeded}/${l.total} succeeded`),{success:0===l.failed,results:d,summary:l}}});import{defineTool as ir}from"@roll-agent/sdk";import{z as sr}from"zod";var cr=sr.object({success:sr.boolean(),candidateName:sr.string(),candidateId:sr.string(),error:sr.string().optional()}),dr=ir({name:"zhipin_open_resume",description:"在推荐列表页点击候选人卡片打开简历详情弹窗",input:sr.object({index:sr.number().describe("候选人在列表中的索引")}),output:cr,execute:async(e,t)=>{t.logger.info(`Opening resume for candidate at index ${e.index}`);const a=h(),n=await a.getPage("zhipin"),r=Tn(n);if(!await zn(r))return{success:!1,candidateName:"",candidateId:"",error:"推荐列表未加载"};const o=await qn(r,e.index);if(!o.found)return{success:!1,candidateName:"",candidateId:"",error:o.error??`索引 ${e.index} 超出范围`};const i=r.locator(o.cardSelector).nth(e.index),s=await i.locator("[data-geek], .card-inner, .geek-item").count()>0?i.locator("[data-geek], .card-inner, .geek-item").first():i;return await s.scrollIntoViewIfNeeded(),await Z(n,s,{target:r}),await s.hover(),await rt(n,200,400),await G(n,s,{target:r}),await s.click(),await rt(n,1e3,2e3),t.logger.info(`Opened resume for ${o.name}`),{success:!0,candidateName:o.name,candidateId:o.candidateId}}});import{defineTool as lr}from"@roll-agent/sdk";import{z as ur}from"zod";var mr=ur.object({x:ur.number(),y:ur.number(),width:ur.number(),height:ur.number()}),gr=ur.object({success:ur.boolean(),screenshotArea:mr.optional(),canvasInfo:ur.object({width:ur.number(),height:ur.number()}).optional(),error:ur.string().optional()}),fr=lr({name:"zhipin_locate_resume_canvas",description:"定位简历详情中嵌套 iframe 内的 canvas 元素坐标(用于截图)",input:ur.object({}),output:gr,execute:async(e,t)=>{t.logger.info("Locating resume canvas in nested iframes");const a=h(),n=await a.getPage("zhipin");try{const e=n.frame("recommendFrame")??n.frames().find(e=>e.url().includes("recommend"));if(!e)return{success:!1,error:"未找到推荐页 iframe"};const a=await e.$('iframe[src*="c-resume"]');if(!a)return{success:!1,error:"未找到简历 iframe"};const r=await a.contentFrame();if(!r)return{success:!1,error:"无法访问简历 iframe 内容"};try{await r.waitForSelector("canvas#resume, div#resume canvas",{timeout:5e3})}catch{return{success:!1,error:"简历 canvas 未加载"}}const o=await r.evaluate(()=>{const e=document.querySelector("canvas#resume, div#resume canvas");if(!e)return null;const t=e.getBoundingClientRect();return{width:e.width,height:e.height,clientWidth:t.width,clientHeight:t.height,x:t.x,y:t.y}});if(!o)return{success:!1,error:"无法获取 canvas 信息"};const i=await n.evaluate(()=>{const e=document.querySelector("#recommendFrame");if(!e)return null;const t=e.getBoundingClientRect();return{x:t.x,y:t.y}}),s=await e.evaluate(()=>{const e=document.querySelector('iframe[src*="c-resume"]');if(!e)return null;const t=e.getBoundingClientRect();return{x:t.x,y:t.y}}),c=(i?.x??0)+(s?.x??0),d=(i?.y??0)+(s?.y??0);return t.logger.info(`Canvas located at (${c+o.x}, ${d+o.y})`),{success:!0,screenshotArea:{x:Math.round(c+o.x),y:Math.round(d+o.y),width:Math.round(o.clientWidth),height:Math.round(o.clientHeight)},canvasInfo:{width:o.width,height:o.height}}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}});import{defineTool as pr}from"@roll-agent/sdk";import{z as hr}from"zod";var wr=hr.object({success:hr.boolean(),closed:hr.boolean(),error:hr.string().optional()}),yr=[".recommendV2 .boss-popup__close",".dialog-lib-resume .boss-popup__close",".boss-dialog .boss-popup__close",".boss-popup__close",".close-btn",".dialog-close"],br=[".boss-popup__close",".close-btn",".dialog-close",".modal-close"],vr=pr({name:"zhipin_close_resume",description:"关闭简历详情弹窗",input:hr.object({}),output:wr,execute:async(e,t)=>{t.logger.info("Closing resume detail modal");const a=h(),n=await a.getPage("zhipin"),r=n.frame("recommendFrame")??n.frames().find(e=>e.url().includes("recommend"));if(!await(async()=>{if(r)for(const e of yr){const t=r.locator(e).first();if(await t.isVisible())return await Z(n,t,{target:r}),await G(n,t,{target:r}),await t.click(),!0}for(const e of br){const t=n.locator(e).first();if(await t.isVisible())return await Z(n,t),await G(n,t),await t.click(),!0}return!1})())return{success:!1,closed:!1,error:"未找到关闭按钮"};let o=!1;for(let e=0;e<5;e++){await n.waitForTimeout(300);const e=r?await r.$(".boss-popup__wrapper, .dialog-lib-resume, .boss-dialog"):await n.$(".boss-popup__wrapper");if(!e||!await e.isVisible()){o=!0;break}}return t.logger.info(o?"Resume modal closed and verified":"Resume modal close unverified"),{success:!0,closed:!0}}});import{defineTool as xr}from"@roll-agent/sdk";import{z as Sr}from"zod";import{waitForSelector as Ir}from"@roll-agent/browser";var kr={login:{qrCode:".login-qr img, .qr-code img",loginSuccess:".user-info, .header-user"},messageList:{container:".chat-list, .msg-list",item:".chat-item, .msg-item",candidateName:".chat-item .name, .msg-item .name",lastMessage:".chat-item .msg, .msg-item .content",unreadBadge:".chat-item .unread, .msg-item .badge",timestamp:".chat-item .time, .msg-item .time"},chat:{input:".chat-input textarea, .msg-input textarea",sendButton:".btn-send, .send-btn",messageItem:".chat-msg, .msg-bubble",messageText:".chat-msg .text, .msg-bubble .text"}};import{navigateTo as Cr,waitForSelector as Ar}from"@roll-agent/browser";var Rr="https://www.yupao.com",Mr=`${Rr}/chat`,Pr=`${Rr}/login`;async function Er(e){e.url().includes("/chat")||await Cr(e,Mr),await Ar(e,kr.messageList.container,{timeout:15e3})}async function $r(e,t){const a=`${Rr}/chat?id=${encodeURIComponent(t)}`;await Cr(e,a),await Ar(e,kr.chat.input,{timeout:15e3})}async function Br(e,t){await Er(e),await Ir(e,kr.messageList.item,{timeout:1e4});const a=kr.messageList;return await e.$$eval(a.item,(e,t)=>{const a=[],n=t.maxItems?e.slice(0,t.maxItems):e;for(const e of n){const n=e.querySelector(t.sel.candidateName),r=e.querySelector(t.sel.lastMessage),o=e.querySelector(t.sel.unreadBadge),i=e.querySelector(t.sel.timestamp),s=e.getAttribute("data-id")??e.getAttribute("data-conversation-id")??e.querySelector("a")?.getAttribute("href")?.match(/id=([^&]+)/)?.[1]??"";a.push({conversationId:s,candidateName:n?.textContent?.trim()??"",lastMessage:r?.textContent?.trim()??"",unreadCount:parseInt(o?.textContent?.trim()??"0",10)||0,timestamp:i?.textContent?.trim()??""})}return a},{sel:a,maxItems:t})}var _r=Sr.object({limit:Sr.number().optional().describe("最多返回的消息条数")}),Nr=Sr.object({conversationId:Sr.string(),candidateName:Sr.string(),lastMessage:Sr.string(),unreadCount:Sr.number(),timestamp:Sr.string()}),Tr=Sr.object({messages:Sr.array(Nr),total:Sr.number()}),zr=xr({name:"yupao_read_messages",description:"读取鱼泡未读消息列表",input:_r,output:Tr,execute:async(e,t)=>{t.logger.info(`Reading yupao messages (limit: ${e.limit??"all"})`);const a=h(),n=await a.getPage("yupao"),r=await Br(n,e.limit);return t.logger.info(`Found ${r.length} messages`),{messages:r.map(e=>({...e})),total:r.length}}});import{defineTool as qr}from"@roll-agent/sdk";import{z as Lr}from"zod";import{waitForSelector as Fr,typeText as jr,clickElement as Or}from"@roll-agent/browser";async function Ur(e,t,a){try{return await $r(e,t),await jr(e,kr.chat.input,a),await Or(e,kr.chat.sendButton),await Fr(e,kr.chat.messageItem,{timeout:5e3}),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}var Dr=Lr.object({conversationId:Lr.string().describe("对话 ID"),message:Lr.string().describe("要发送的回复消息")}),Vr=Lr.object({success:Lr.boolean(),conversationId:Lr.string(),sentMessage:Lr.string(),error:Lr.string().optional()}),Wr=qr({name:"yupao_send_reply",description:"向鱼泡指定对话发送回复消息",input:Dr,output:Vr,execute:async(e,t)=>{const{conversationId:a,message:n}=e;t.logger.info(`Sending reply to yupao conversation ${a}`);const r=h(),o=await r.getPage("yupao"),i=await Ur(o,a,n);return i.success?t.logger.info("Reply sent successfully"):t.logger.error(`Failed to send reply: ${i.error}`),{success:i.success,conversationId:a,sentMessage:n,error:i.error}}}),Hr=t("browser-use-agent");function Zr(e){if(void 0!==e){if("true"===e)return!0;if("false"===e)return!1;throw new Error(`Expected boolean env value "true" or "false", received "${e}".`)}}function Gr(e,t){if(void 0===e)return;const a=Number.parseInt(e,10);if(!Number.isInteger(a))throw new Error(`${t} must be an integer, received "${e}".`);return a}function Yr(e){if(void 0===e)return;const t=JSON.parse(e);if(!Array.isArray(t)||!t.every(e=>"string"==typeof e))throw new Error("BROWSER_ARGS_JSON must be a JSON string array.");return t}function Kr(){return a.parse({mode:process.env.BROWSER_MODE,headless:Zr(process.env.BROWSER_HEADLESS),cdpUrl:process.env.BROWSER_CDP_URL,cdpHost:process.env.BROWSER_CDP_HOST,cdpPort:Gr(process.env.BROWSER_CDP_PORT,"BROWSER_CDP_PORT"),channel:process.env.BROWSER_CHANNEL,executablePath:process.env.BROWSER_EXECUTABLE_PATH,userDataDir:process.env.BROWSER_USER_DATA_DIR,args:Yr(process.env.BROWSER_ARGS_JSON),sessionsDir:process.env.BROWSER_SESSIONS_DIR})}var Jr=e({name:"browser-use-agent",tools:[me,Ae,Oe,Ge,tt,Ot,sa,ua,Aa,yn,Sn,Mn,Xn,Un,or,dr,fr,vr,zr,Wr,S]},{onShutdown:v});async function Xr(){await f(Kr());try{await ln(),y(!0)}catch(e){y(!1),Hr.error(`Failed to preload Reply Authority keys during startup; browser_status.replyAuthorityKeysLoaded=false. ${e instanceof Error?e.stack??e.message:String(e)}`)}await Jr.listen({transport:{type:"http",port:parseInt(process.env.BROWSER_AGENT_PORT??"3100",10),host:process.env.BROWSER_AGENT_HOST??"127.0.0.1"}})}Xr().catch(async e=>{Hr.error(`Fatal error: ${e instanceof Error?e.stack??e.message:String(e)}`),await v().catch(()=>{}),process.exit(1)});
|
|
1
|
+
import{defineAgent as e,createAgentLogger as t}from"@roll-agent/sdk";import{BrowserRuntimeConfigSchema as n}from"@roll-agent/browser";import{defineTool as a}from"@roll-agent/sdk";import{z as r}from"zod";import{createAgentLogger as o}from"@roll-agent/sdk";import{BrowserRuntime as i,BrowserContextManager as s,SessionStore as c}from"@roll-agent/browser";var l,d,u,m=!1,g=o("browser-use-agent");async function f(e){l||(m=!1,u=new c(e.sessionsDir),l=new i(e),await l.start(),d=new s(l,u))}function p(){if(!l)throw new Error("BrowserRuntime not initialized. Call initRuntime() first.");return l}function h(){if(!d)throw new Error("BrowserContextManager not initialized. Call initRuntime() first.");return d}function w(){if(!u)throw new Error("SessionStore not initialized. Call initRuntime() first.");return u}function y(e){m=e}function b(){return m}async function v(){const e=d,t=l,n=[];if(d=void 0,l=void 0,u=void 0,m=!1,e){g.info("Closing browser contexts...");try{await e.closeAll()}catch(e){n.push(new Error("Failed to close browser contexts",{cause:e})),g.error(`Failed to close browser contexts: ${e instanceof Error?e.stack??e.message:String(e)}`)}}if(t){g.info("Stopping browser process...");try{await t.stop()}catch(e){n.push(new Error("Failed to stop browser runtime",{cause:e})),g.error(`Failed to stop browser runtime: ${e instanceof Error?e.stack??e.message:String(e)}`)}}if(g.info("Browser runtime shutdown complete"),n.length>0)throw new AggregateError(n,"Browser runtime shutdown failed")}var x=r.object({success:r.boolean(),mode:r.string(),connected:r.boolean()}),S=a({name:"attach_browser_session",description:"调试工具:显式执行一次 connectOverCDP(),仅建立 Playwright Browser 连接,不做页面导航或 DOM 操作。",input:r.object({}),output:x,execute:async(e,t)=>{const n=p();return t.logger.info("Attaching Playwright browser session over CDP"),await n.getBrowser(),{success:!0,mode:n.mode,connected:!0}}});import{defineTool as C}from"@roll-agent/sdk";import{z as A}from"zod";import{BrowserStatusSchema as k}from"@roll-agent/browser";import{createHash as I}from"node:crypto";import{z as M}from"zod";var R=["REPLY_AUTHORITY_KEYS_URL","BROWSER_VISUAL_CURSOR","BROWSER_VISUAL_ACTIVITY"],B=/^[0-9a-f]{8}$/,E=M.object({present:M.boolean(),fingerprint:M.string().regex(B).optional()}),P=M.record(E);function $(e,t=process.env){return Object.fromEntries(e.map(e=>{const n=t[e];return"string"==typeof n&&n.length>0?[e,{present:!0,fingerprint:_(n)}]:[e,{present:!1}]}))}function _(e){return I("sha256").update(e).digest("hex").slice(0,8)}var q,N=180,T=60,L=280,z="roll-agent-visual-cursor-root",F="__rollVisualCursorState";function O(e){if(void 0!==e)return"true"===e||"false"!==e&&void 0}function j(){return void 0!==q?q:O(process.env.BROWSER_VISUAL_CURSOR)??!0}async function V(e){return await e.evaluate(e=>{const t=e.getBoundingClientRect();return t.width<=0||t.height<=0?null:{x:Math.round(t.left+t.width/2),y:Math.round(t.top+t.height/2)}})}async function D(e,t){await e.evaluate(e=>{const t="roll-agent-visual-cursor-root",n="roll-agent-visual-cursor-pointer",a="__rollVisualCursorState",r=(()=>{const e=document.getElementById(t);if(e)return e;const a=document.createElement("div");a.id=t,a.style.position="fixed",a.style.left="0",a.style.top="0",a.style.width="0",a.style.height="0",a.style.pointerEvents="none",a.style.zIndex="2147483647";const r=document.createElement("div");return r.id=n,r.setAttribute("aria-hidden","true"),r.style.position="fixed",r.style.left="0",r.style.top="0",r.style.width="24px",r.style.height="24px",r.style.opacity="0",r.style.transform="translate(-9999px, -9999px)",r.style.willChange="transform, opacity",r.style.filter="drop-shadow(0 4px 8px rgba(15, 23, 42, 0.28))",r.innerHTML='<svg viewBox="0 0 24 24" width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 2L18 14L11.4 15.3L14.6 22L10.7 23.5L7.6 16.8L3 21V2Z" fill="#FFFFFF" stroke="#0F172A" stroke-width="1.5" stroke-linejoin="round"/></svg>',a.append(r),document.documentElement.append(a),a})(),o=r.querySelector(`#${n}`);if(!o)return;const i=window[a],s=e.point,c=e.durationMs,l=s.x-2,d=s.y-2;if(i?o.style.transition=`transform ${c}ms cubic-bezier(0.22, 1, 0.36, 1), opacity 120ms ease`:(o.style.transition="opacity 120ms ease",o.style.transform=`translate(${l}px, ${d}px)`),o.style.opacity="1",o.style.transform=`translate(${l}px, ${d}px)`,window[a]={x:s.x,y:s.y},!e.clickPulse)return;const u=document.createElement("div");u.setAttribute("aria-hidden","true"),u.style.position="fixed",u.style.left="0",u.style.top="0",u.style.width="18px",u.style.height="18px",u.style.borderRadius="9999px",u.style.border="2px solid rgba(20, 184, 166, 0.95)",u.style.background="rgba(20, 184, 166, 0.18)",u.style.pointerEvents="none",u.style.opacity="0.9",u.style.transform=`translate(${s.x-9}px, ${s.y-9}px) scale(0.55)`,u.style.transition=`transform ${e.pulseDurationMs}ms ease, opacity ${e.pulseDurationMs}ms ease`,r.append(u),requestAnimationFrame(()=>{u.style.opacity="0",u.style.transform=`translate(${s.x-18}px, ${s.y-18}px) scale(2)`}),globalThis.setTimeout(()=>{u.remove()},e.pulseDurationMs+40)},{point:t.point,durationMs:t.durationMs,clickPulse:t.clickPulse,pulseDurationMs:t.pulseDurationMs})}function U(e){const t=[e],n="function"==typeof e.frames?e.frames():[];return t.push(...n),t}async function H(e){await e.evaluate(({rootId:e,stateKey:t})=>{document.getElementById(e)?.remove(),delete window[t]},{rootId:z,stateKey:F})}async function W(e,t={}){if(e.isClosed())return!1;let n=!1;for(const a of U(e))if(a!==t.preserveTarget)try{await H(a),n=!0}catch{}return n}async function Z(e,t,n={}){if(!j()||e.isClosed())return!1;try{await t.scrollIntoViewIfNeeded();const a=await V(t);if(!a)return!1;const r=n.durationMs??N,o=n.settleMs??T,i=n.target??e;return await W(e,{preserveTarget:i}),await D(n.target??e,{point:a,durationMs:r,clickPulse:!1,pulseDurationMs:L}),await e.waitForTimeout(Math.max(r+o,0)),!0}catch{return!1}}async function G(e,t,n={}){if(!j()||e.isClosed())return!1;try{const a=await V(t);if(!a)return!1;const r=n.pulseDurationMs??L,o=n.target??e;return await W(e,{preserveTarget:o}),await D(o,{point:a,durationMs:0,clickPulse:!0,pulseDurationMs:r}),await e.waitForTimeout(r),!0}catch{return!1}}async function Y(e,t,n){return await Z(e,e.locator(t).first(),n)}async function K(e,t){return await G(e,e.locator(t).first())}var J,X={info:{accent:"#14b8a6",accentSoft:"rgba(20, 184, 166, 0.42)",accentGlow:"rgba(20, 184, 166, 0.18)",capsuleBg:"rgba(15, 23, 42, 0.82)",capsuleBorder:"rgba(45, 212, 191, 0.38)",text:"#F8FAFC",dot:"#2DD4BF"},success:{accent:"#22c55e",accentSoft:"rgba(34, 197, 94, 0.42)",accentGlow:"rgba(34, 197, 94, 0.18)",capsuleBg:"rgba(10, 24, 16, 0.86)",capsuleBorder:"rgba(74, 222, 128, 0.38)",text:"#F0FDF4",dot:"#4ADE80"},error:{accent:"#f59e0b",accentSoft:"rgba(245, 158, 11, 0.42)",accentGlow:"rgba(245, 158, 11, 0.2)",capsuleBg:"rgba(41, 24, 10, 0.88)",capsuleBorder:"rgba(251, 191, 36, 0.4)",text:"#FFFBEB",dot:"#FBBF24"}},Q=14,ee=720;function te(e){if(void 0!==e)return"true"===e||"false"!==e&&void 0}function ne(e="info"){return X[e]}function ae(){return void 0!==J?J:te(process.env.BROWSER_VISUAL_ACTIVITY)??!0}async function re(e,t=Q){return await e.evaluate((e,t)=>{const n=e.getBoundingClientRect();if(n.width<=0||n.height<=0)return null;const a=globalThis.innerWidth,r=globalThis.innerHeight,o=Math.max(t,0),i=Math.max(n.left-o,0),s=Math.max(n.top-o,0),c=Math.min(n.right+o,a),l=Math.min(n.bottom+o,r);return{x:Math.round(i),y:Math.round(s),width:Math.max(Math.round(c-i),0),height:Math.max(Math.round(l-s),0)}},t)}async function oe(e,t){await e.evaluate(e=>{const t="roll-agent-visual-activity-style",n="roll-agent-visual-activity-root",a="roll-agent-visual-activity-viewport",r="roll-agent-visual-activity-region",o="roll-agent-visual-activity-region-shine",i="roll-agent-visual-activity-capsule",s="roll-agent-visual-activity-dot",c="roll-agent-visual-activity-label",l=(e,t,n,a=!1)=>{a&&(e.style.transition="none",t.style.transition="none",n.style.transition="none"),e.style.opacity="0",e.style.transform="scale(0.995)",t.style.opacity="0",n.style.opacity="0",n.style.transform="translate(-50%, -8px)",t.style.transform="translate(-9999px, -9999px)",a&&requestAnimationFrame(()=>{e.style.transition="opacity 180ms ease, transform 220ms ease",t.style.transition="transform 220ms cubic-bezier(0.22, 1, 0.36, 1), width 220ms ease, height 220ms ease, opacity 180ms ease",n.style.transition="opacity 180ms ease, transform 220ms ease"})};(()=>{if(document.getElementById(t))return;const e=document.createElement("style");e.id=t,e.textContent="\n @keyframes roll-visual-activity-breathe {\n 0%, 100% { transform: translate(-50%, 0px) scale(1); }\n 50% { transform: translate(-50%, -1px) scale(1.01); }\n }\n @keyframes roll-visual-activity-scan {\n 0% { transform: translateX(-140%) skewX(-18deg); }\n 100% { transform: translateX(200%) skewX(-18deg); }\n }\n ",document.head.append(e)})(),(()=>{const e=document.getElementById(n);if(e)return e;const t=document.createElement("div");t.id=n,t.style.position="fixed",t.style.inset="0",t.style.pointerEvents="none",t.style.zIndex="2147483646";const l=document.createElement("div");l.id=a,l.setAttribute("aria-hidden","true"),l.style.position="fixed",l.style.inset="10px",l.style.borderRadius="20px",l.style.opacity="0",l.style.transform="scale(0.995)",l.style.transition="opacity 180ms ease, transform 220ms ease";const d=document.createElement("div");d.id=r,d.setAttribute("aria-hidden","true"),d.style.position="fixed",d.style.left="0",d.style.top="0",d.style.width="0",d.style.height="0",d.style.borderRadius="18px",d.style.opacity="0",d.style.overflow="hidden",d.style.transform="translate(-9999px, -9999px)",d.style.transition="transform 220ms cubic-bezier(0.22, 1, 0.36, 1), width 220ms ease, height 220ms ease, opacity 180ms ease";const u=document.createElement("div");u.id=o,u.setAttribute("aria-hidden","true"),u.style.position="absolute",u.style.inset="0",u.style.animation="roll-visual-activity-scan 1.6s linear infinite",u.style.opacity="0.9",d.append(u);const m=document.createElement("div");m.id=i,m.setAttribute("aria-hidden","true"),m.style.position="fixed",m.style.left="50%",m.style.top="20px",m.style.display="inline-flex",m.style.alignItems="center",m.style.gap="10px",m.style.padding="10px 14px",m.style.borderRadius="999px",m.style.opacity="0",m.style.transform="translate(-50%, -8px)",m.style.transition="opacity 180ms ease, transform 220ms ease",m.style.backdropFilter="blur(12px)",m.style.animation="roll-visual-activity-breathe 1.8s ease-in-out infinite",m.style.fontSize="13px",m.style.fontWeight="600",m.style.lineHeight="18px",m.style.letterSpacing="0.01em",m.style.whiteSpace="nowrap";const g=document.createElement("div");g.id=s,g.setAttribute("aria-hidden","true"),g.style.width="8px",g.style.height="8px",g.style.borderRadius="999px",g.style.flex="0 0 auto";const f=document.createElement("div");f.id=c,f.setAttribute("aria-live","polite"),m.append(g,f),t.append(l,d,m),document.documentElement.append(t)})();const d=document.getElementById(a),u=document.getElementById(r),m=document.getElementById(o),g=document.getElementById(i),f=document.getElementById(s),p=document.getElementById(c);if(!(d&&u&&m&&g&&f&&p))return;const h=window.__rollVisualActivityTimers??={};if(void 0!==h.hideTimer&&(globalThis.clearTimeout(h.hideTimer),delete h.hideTimer),"clear"===e.mode)return void l(d,u,g,!0);const w=e.theme;if(void 0!==w&&(d.style.border=`1px solid ${w.accentSoft}`,d.style.boxShadow=`inset 0 0 0 1px ${w.accentSoft}, 0 0 52px ${w.accentGlow}`,g.style.border=`1px solid ${w.capsuleBorder}`,g.style.background=w.capsuleBg,g.style.color=w.text,g.style.boxShadow=`0 18px 46px rgba(15, 23, 42, 0.24), 0 0 0 1px ${w.capsuleBorder}`,f.style.background=w.dot,f.style.boxShadow=`0 0 0 5px ${w.accentGlow}`,u.style.border=`1px solid ${w.accentSoft}`,u.style.background=w.accentGlow,u.style.boxShadow=`0 0 0 1px ${w.accentSoft}, 0 16px 42px ${w.accentGlow}`,m.style.background="linear-gradient(115deg, transparent 0%, rgba(255,255,255,0.08) 24%, rgba(255,255,255,0.42) 50%, transparent 76%)"),void 0!==e.label&&(p.textContent=e.label),g.style.opacity="1",g.style.transform="translate(-50%, 0)",d.style.opacity="complete"===e.mode?"0.9":"0.72",d.style.transform="scale(1)","begin"===e.mode)return u.style.opacity="0",void(u.style.transform="translate(-9999px, -9999px)");if(void 0!==e.rect&&(u.style.width=`${e.rect.width}px`,u.style.height=`${e.rect.height}px`,u.style.transform=`translate(${e.rect.x}px, ${e.rect.y}px)`,u.style.opacity="1"),"complete"!==e.mode)return;const y=Math.max(e.lingerMs??0,0);h.hideTimer=globalThis.setTimeout(()=>{l(d,u,g),delete h.hideTimer},y)},t)}async function ie(e,t){if(!ae()||e.isClosed())return!1;try{return await oe(t.target??e,{mode:"begin",label:t.label,theme:ne(t.tone??"info")}),!0}catch{return!1}}async function se(e,t,n={}){if(!ae()||e.isClosed())return!1;try{await t.scrollIntoViewIfNeeded();const a=await re(t,n.padding??Q);return!!a&&(await oe(n.target??e,{mode:"highlight",...void 0!==n.label?{label:n.label}:{},theme:ne(n.tone??"info"),rect:a}),!0)}catch{return!1}}async function ce(e,t,n){return await se(e,e.locator(t).first(),n)}async function le(e,t){if(!ae()||e.isClosed())return!1;try{return await oe(t.target??e,{mode:"complete",label:t.label,theme:ne("error"===t.status?"error":"success"),lingerMs:t.lingerMs??ee}),!0}catch{return!1}}async function de(e,t={}){if(!ae()||e.isClosed())return!1;try{return await oe(t.target??e,{mode:"clear"}),!0}catch{return!1}}var ue=k.extend({replyAuthorityKeysLoaded:A.boolean(),visualCursorEnabled:A.boolean(),visualActivityEnabled:A.boolean(),effectiveEnvSources:P}),me=C({name:"browser_status",description:"查询浏览器运行状态和活跃 session 信息",input:A.object({}),output:ue,execute:async(e,t)=>{t.logger.info("Querying browser status");const n=p(),a=h(),r=w(),o=n.isRunning(),{headless:i,mode:s}=n.getConfig(),c=a.getActivePlatforms(),l=[];for(const e of c){const t=a.getPageCount(e),o=a.getCurrentUrl(e);let i=null,s="unknown";if(n.shouldRestoreSessionSnapshot()){const[t,n]=await Promise.all([r.loadCookies(e),r.loadLocalStorage(e)]);i=void 0!==t&&t.length>0||void 0!==n&&Object.keys(n).length>0,s=i?"snapshot":"none"}else n.usesPersistentProfile()&&(i=null,s="profile");l.push({platform:e,pagesOpen:t,currentUrl:o,hasLoginState:i,loginStateSource:s})}return{running:o,headless:i,mode:s,activeSessions:l,replyAuthorityKeysLoaded:b(),visualCursorEnabled:j(),visualActivityEnabled:ae(),effectiveEnvSources:$(R)}}});import{defineTool as ge}from"@roll-agent/sdk";import{BrowserPageInfoSchema as fe,PlatformSchema as pe}from"@roll-agent/browser";import{z as he}from"zod";import{PLATFORMS as we}from"@roll-agent/browser";var ye={zhipin:"https://www.zhipin.com",yupao:"https://www.yupao.com"};function be(e){return new URL(ye[e]).host}function ve(e,t){try{return new URL(e).host.includes(be(t))}catch{return!1}}function xe(e){return we.find(t=>ve(e,t))}function Se(e,t){const n=xe(t.url)??null;return{pageId:t.targetId,url:t.url,title:t.title,boundPlatform:e.getBoundPlatformForNativePage(t.targetId)??null,detectedPlatform:n,isSelectedForPlatform:e.isNativePageSelected(t.targetId)}}async function Ce(e,t){const n=t.url();return{pageId:e.getPageId(t),url:n,title:await t.title().catch(()=>""),boundPlatform:e.getBoundPlatformForPage(t)??null,detectedPlatform:xe(n)??null,isSelectedForPlatform:e.isSelectedPageForPlatform(t)}}var Ae=he.object({platform:pe.optional().describe("可选:仅返回指定平台相关的页面")}),ke=he.object({pages:he.array(fe)}),Ie=ge({name:"list_pages",description:"通过原生 CDP 列出当前浏览器可见页面及其可选择的 pageId;登录前该值等同于原生 targetId。",input:Ae,output:ke,execute:async(e,t)=>{const n=h();t.logger.info("Listing browser pages");const a=(await n.listNativePages()).map(e=>Se(n,e));return{pages:void 0===e.platform?a:a.filter(t=>t.boundPlatform===e.platform||t.detectedPlatform===e.platform)}}});import{defineTool as Me}from"@roll-agent/sdk";import{BrowserPageInfoSchema as Re}from"@roll-agent/browser";import{z as Be}from"zod";async function Ee(e,t){return e.useTrackedPage(t,e=>ve(e.url(),t))}async function Pe(e,t){return(await e.listNativePages()).find(e=>ve(e.url,t))}async function $e(e,t){const n=await Pe(e,t);if(n)return await e.activateNativePage(n.targetId),{page:n,reusedExistingPage:!0};return{page:await e.openNativePage(ye[t]),reusedExistingPage:!1}}var _e,qe=Be.object({url:Be.string().url().describe("要导航到的目标 URL")}),Ne=Be.object({success:Be.boolean(),page:Re});function Te(){return{getContextManager:h,detectPlatformFromUrl:xe,matchesPlatformHost:ve,findTrackedPlatformPage:Ee,toAttachedPageInfo:Ce,..._e}}async function Le(e,t,n){const a=(await e.listNativePages()).find(e=>t.matchesPlatformHost(e.url,n));if(a)return await e.selectNativePage(n,a.targetId),await e.getPage(n)}async function ze(e,t,n,a){const r=t.detectPlatformFromUrl(n);if(!r){const t=await e.getActivePage();if(!t)throw new Error("No active browser tab detected. Use open_platform or select_page first.");return{page:t}}const o=await t.findTrackedPlatformPage(e,r);if(o)return a.info(`Reusing tracked ${r} page instead of navigating the current unrelated tab`),{page:o,platform:r};const i=await Le(e,t,r);if(i)return a.info(`Reusing native ${r} page instead of navigating the current unrelated tab`),{page:i,platform:r};const s=await e.getActivePage();if(!s)throw new Error("No active browser tab detected. Use open_platform or select_page first.");return a.warn(`No existing ${r} page found; falling back to navigating the current active tab`),{page:s,platform:r}}async function Fe(e,t,n){return await e.selectAttachedPage(n,e.getPageId(t))}async function Oe(e,t){e.url()!==t&&await e.goto(t,{waitUntil:"domcontentloaded"})}var je=Me({name:"navigate_active_tab",description:"导航到指定 URL;若 URL 属于已知平台(Boss/鱼泡),优先复用已打开的平台页,避免把无关 tab 导航成第二个平台页。",input:qe,output:Ne,execute:async(e,t)=>{const n=Te(),a=n.getContextManager();t.logger.info(`Navigating active tab to ${e.url}`);const{page:r,platform:o}=await ze(a,n,e.url,t.logger);await r.bringToFront().catch(()=>{}),await Oe(r,e.url);const i=o??n.detectPlatformFromUrl(r.url()),s=i?await Fe(a,r,i):r;return i?t.logger.info(`Bound navigated page to ${i}`):a.clearBindingForPage(r),{success:!0,page:await n.toAttachedPageInfo(a,s)}}});import{defineTool as Ve}from"@roll-agent/sdk";import{BrowserPageInfoSchema as De,PlatformSchema as Ue}from"@roll-agent/browser";import{z as He}from"zod";var We=He.object({platform:Ue.describe("目标平台:`zhipin` 代表 BOSS直聘,`yupao` 代表鱼泡")}),Ze=He.object({success:He.boolean(),page:De,reusedExistingTab:He.boolean()}),Ge=Ve({name:"open_platform",description:"打开并聚焦招聘平台主页,供用户手动登录或后续执行站内操作。",input:We,output:Ze,execute:async(e,t)=>{const{platform:n}=e,a=p(),r=h();t.logger.info(`Opening platform page for ${n}`);const{page:o,reusedExistingPage:i}=await $e(a,n);return r.rememberNativePageSelection(n,o),{success:!0,page:Se(r,o),reusedExistingTab:i}}});import{defineTool as Ye}from"@roll-agent/sdk";import{BrowserPageInfoSchema as Ke,PlatformSchema as Je}from"@roll-agent/browser";import{z as Xe}from"zod";var Qe=Xe.object({platform:Je.describe("要将该页面绑定为当前活跃页的平台"),pageId:Xe.string().describe("通过 list_pages 返回的 pageId;登录前就是原生 targetId,登录后仍可作为稳定选择句柄")}),et=Xe.object({success:Xe.boolean(),page:Ke}),tt=Ye({name:"select_page",description:"将指定 pageId 绑定为平台当前活跃页,并切换到前台;登录前走原生 CDP target 激活。",input:Qe,output:et,execute:async(e,t)=>{const n=h();t.logger.info(`Selecting page ${e.pageId} for ${e.platform}`);const a=await n.selectNativePage(e.platform,e.pageId);return{success:!0,page:Se(n,a)}}});import{defineTool as nt}from"@roll-agent/sdk";import{z as at}from"zod";import{setTimeout as rt}from"node:timers/promises";async function ot(e,t=300,n=800){const a=Math.floor(Math.random()*(n-t))+t;await e.waitForTimeout(a)}async function it(e){const t=Math.random();let n;n=t<.5?800+1200*Math.random():t<.8?500+300*Math.random():t<.95?2e3+2e3*Math.random():4e3+2e3*Math.random(),await e.waitForTimeout(Math.floor(n))}async function st(e,t){const n=t?.minDistance??50,a=t?.maxDistance??200,r=t?.direction??"both",o=Math.floor(Math.random()*(a-n))+n,i="up"===r?-1:"down"===r||Math.random()>.5?1:-1;await e.evaluate(e=>{window.scrollBy({top:e,behavior:"smooth"})},o*i),await ot(e,200,500)}function ct(e=.3){return Math.random()<e}var lt=".geek-item.selected";async function dt(e){try{await e.waitForSelector(lt,{timeout:5e3})}catch{return null}const t=await e.evaluate(()=>{const e=document.querySelector(".geek-item.selected");if(!e)return null;const t=e.getAttribute("data-id")??e.closest('[role="listitem"]')?.getAttribute("key")??"";return{conversationId:t,candidateId:e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??t,candidateName:e.querySelector('[class*="name"], .nickname, .geek-name, .candidate-name')?.textContent?.trim()??""}});return t&&"string"==typeof t.conversationId&&"string"==typeof t.candidateId&&0!==t.conversationId.length&&0!==t.candidateId.length?t:null}async function ut(e){const t=await e.evaluate(()=>{const e=[".chat-conversation",".conversation-box",".conversation-message"],t=[".base-info-single-detial .name-box",".base-info-content .name-box",".base-info-single-container .name-box",".base-info-content .base-name",".chat-user-name",".name-box",".base-name"];for(const n of e){const e=document.querySelector(n);if(e)for(const n of t){const t=e.querySelector(n)?.textContent?.trim()??"";if(t.length>0)return{candidateName:t}}}return null});return t&&"string"==typeof t.candidateName&&0!==t.candidateName.length?t:null}var mt="https://www.zhipin.com/web/geek/chat",gt=".chat-list-wrap, .geek-item",ft=new Set(["消息"]),pt="data-roll-chat-entry-target",ht="data-roll-chat-item-target";function wt(e){return e.trim().toLocaleLowerCase("zh-CN")}function yt(e,t){const n=wt(e),a=wt(t);return n.length>0&&a.length>0&&(n===a||n.includes(a)||a.includes(n))}function bt(e,t){let n=0;for(const a of e)t.includes(a)&&(n+=1);return n}function vt(e,t){if(void 0!==t.conversationId){const n=e.find(e=>e.conversationId===t.conversationId);if(n)return n}const n=t.candidateName;if(n){const t=wt(n),a=e.filter(e=>e.name.length>0);let r=a.find(e=>wt(e.name)===t);if(r)return r;if(r=a.find(e=>{const n=wt(e.name);return n.includes(t)||t.includes(n)}),r)return r;const o=t.length<=2?1:t.length<=4?.75:.6;if(r=a.find(e=>{const n=wt(e.name);return bt(t,n)>=Math.ceil(Math.min(t.length,n.length)*o)}),r)return r}if(void 0!==t.index)return e[t.index]}function xt(e){return e.includes("/web/geek/chat")||e.includes("/web/chat")}async function St(e,t=1e4){try{return await e.waitForSelector(gt,{timeout:t}),!0}catch{return!1}}async function Ct(e,t){const n=(await e.listAttachedPages()).find(e=>e!==t&&xt(e.url()));if(n)return await e.selectAttachedPage("zhipin",e.getPageId(n))}async function At(e,t){await e.evaluate(e=>{document.querySelectorAll(`[${e}]`).forEach(t=>{t.removeAttribute(e)})},t).catch(()=>{})}async function kt(e,t){const n=e.locator(t).first();await n.scrollIntoViewIfNeeded(),await Z(e,n),await n.hover(),await ot(e,200,400),await G(e,n),await n.click()}async function It(e){const t=await e.evaluate(e=>{const t=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},n=t=>e.messageLabels.some(e=>t===e||t.includes(e));document.querySelectorAll(`[${e.markerAttr}]`).forEach(t=>{t.removeAttribute(e.markerAttr)});const a=Array.from(document.querySelectorAll('a[href*="/web/geek/chat"], a[href*="/web/chat"]'));for(const n of a)if(t(n))return n.setAttribute(e.markerAttr,"true"),{found:!0,selector:`[${e.markerAttr}="true"]`};const r=Array.from(document.querySelectorAll('a, button, [role="link"], [role="button"], span, div'));for(const a of r){if(n(a.textContent?.trim()??"")&&t(a))return a.setAttribute(e.markerAttr,"true"),{found:!0,selector:`[${e.markerAttr}="true"]`}}return{found:!1}},{markerAttr:pt,messageLabels:[...ft]});if(!t.found)return!1;try{return await kt(e,t.selector),!0}finally{await At(e,pt)}}function Mt(e){return e instanceof Error&&/ERR_ABORTED/i.test(e.message)}async function Rt(e){try{return await e.goto(mt,{waitUntil:"domcontentloaded"}),!0}catch(t){return!!Mt(t)&&(!!xt(e.url())||await St(e,2e3))}}async function Bt(e,t){if(!await It(t))return!1;if(await St(t,5e3))return!0;const n=await Ct(e,t);return!!n&&await St(n,5e3)}async function Et(e,t){if(xt(t.url())&&await St(t))return!0;const n=await Ct(e,t);if(n&&await St(n))return!0;if(await Bt(e,t))return!0;if(!await Rt(t))return!1;if(await St(t))return!0;await rt(300);const a=await Ct(e,t);return!!a&&await St(a,5e3)}async function Pt(e){return e.evaluate(()=>Array.from(document.querySelectorAll(".geek-item")).map((e,t)=>{const n=e.getAttribute("data-id")??e.closest('[role="listitem"]')?.getAttribute("key")??"",a=e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??n,r=e.querySelector('[class*="name"], .nickname, .geek-name, .candidate-name'),o=r?.textContent?.trim()??"",i=e.querySelector(".source-job")?.textContent?.trim()??"",s=e.querySelector(".badge-count"),c=parseInt(s?.textContent?.trim()??"0",10)||0;return{conversationId:n,candidateId:a,name:o,index:t,position:i,hasUnread:c>0||null!==e.querySelector(".red-dot"),unreadCount:c,lastMessageTime:e.querySelector(".time, .time-shadow")?.textContent?.trim()??"",messagePreview:(e.querySelector(".push-text, .chat-last-msg")?.textContent?.trim()??"").slice(0,100)}}))}async function $t(e,t){const n=await e.evaluate(e=>{document.querySelectorAll(`[${e.markerAttr}]`).forEach(t=>{t.removeAttribute(e.markerAttr)});const t=Array.from(document.querySelectorAll(".geek-item")),n=t.find(t=>(t.getAttribute("data-id")??t.closest('[role="listitem"]')?.getAttribute("key")??"")===e.targetConversationId)??t[e.targetIndex];if(!n)return{found:!1};return(n.querySelector(".chat-item-content")??n).setAttribute(e.markerAttr,"true"),{found:!0,selector:`[${e.markerAttr}="true"]`}},{markerAttr:ht,targetConversationId:t.conversationId,targetIndex:t.index});if(!n.found)return!1;try{return await kt(e,n.selector),!0}finally{await At(e,ht)}}async function _t(e,t){if(0===t.conversationId.length&&0===t.name.length)return await ot(e,500,900),!0;try{return await e.waitForFunction(e=>{const t=e=>e.trim().toLocaleLowerCase("zh-CN"),n=document.querySelector(".geek-item.selected"),a=n?.getAttribute("data-id")??n?.closest('[role="listitem"]')?.getAttribute("key")??"",r=0===e.conversationId.length||a===e.conversationId,o=(()=>{const e=[".chat-conversation",".conversation-box",".conversation-message"],t=[".base-info-single-detial .name-box",".base-info-content .name-box",".base-info-single-container .name-box",".base-info-content .base-name",".chat-user-name",".name-box",".base-name"];for(const n of e){const e=document.querySelector(n);if(e)for(const n of t){const t=e.querySelector(n)?.textContent?.trim()??"";if(t.length>0)return t}}return""})(),i=0===e.candidateName.length||((e,n)=>{const a=t(e),r=t(n);return a.length>0&&r.length>0&&(a===r||a.includes(r)||r.includes(a))})(e.candidateName,o);return r&&i},{conversationId:t.conversationId,candidateName:t.name},{timeout:5e3}),!0}catch{return await ot(e,800,1200),!1}}async function qt(e,t,n){if(void 0===n.conversationId&&void 0===n.candidateName&&void 0===n.index)return;if(!await Et(e,t))return{found:!1,conversationId:"",candidateId:"",name:"",index:-1,position:"",hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:"消息列表未加载"};const a=await e.getPage("zhipin"),r=vt(await Pt(a),n);if(!r){return{found:!1,conversationId:"",candidateId:"",name:"",index:-1,position:"",hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:`未找到候选人: ${n.conversationId??n.candidateName??`index ${n.index}`}`}}if(!await $t(a,r))return{...r,found:!1,error:`打开候选人聊天失败: ${r.name||`index ${r.index}`}`};let o=await _t(a,r);if(!o){await $t(a,r)&&(o=await _t(a,r))}if(!o)return{...r,found:!1,error:`打开候选人聊天后,右侧会话未同步切换到 ${r.name||r.conversationId}`};const i=await dt(a);if(!i||i.conversationId!==r.conversationId)return{...r,found:!1,error:`当前选中会话与目标会话不一致: ${r.name||r.conversationId}`};const s=await ut(a);return!(r.name.length>0)||s&&yt(r.name,s.candidateName)?{...r,found:!0}:{...r,found:!1,error:`右侧聊天面板仍未切换到 ${r.name}`}}var Nt=["chat-list","chat-history","recommend-list"],Tt={"chat-list":{surface:"chat-list",defaultDirection:"down",containerSelectors:[".chat-user .user-container",".chat-user .b-scroll-stable",".chat-list-wrap",".chat-user"],itemSelector:".geek-item",highlightSelector:".chat-user .user-container, .chat-list-wrap, .chat-user"},"chat-history":{surface:"chat-history",defaultDirection:"up",containerSelectors:[".conversation-message",".chat-message-list",".conversation-main",".conversation-box"],itemSelector:".chat-message-list > .message-item, .conversation-message .message-item",highlightSelector:".conversation-message, .chat-message-list, .conversation-main"},"recommend-list":{surface:"recommend-list",defaultDirection:"down",containerSelectors:[".recommend-list-wrap",".recommend-list",".candidate-list",".geek-list",".list-wrap",".recommendV2"],itemSelector:".candidate-card-wrap, [data-geek], .geek-item",highlightSelector:".candidate-card-wrap, [data-geek], .geek-item"}};function Lt(e){return Tt[e]}var zt=["target-count","boundary","no-new-items","max-steps"],Ft=4,Ot=700,jt=2,Vt=2;function Dt(e){return e.direction??"down"}function Ut(e){const t=e.steps??Ft;return Math.max(0,Math.floor(t))}function Ht(e){return Math.max(0,Math.floor(e.settleMs??Ot))}function Wt(e,t){return"up"===t?e.atStart:e.atEnd}async function Zt(e,t){return await e.evaluate(e=>{const t=e=>{const t=window.getComputedStyle(e).overflowY;return"hidden"!==t&&"clip"!==t&&e.scrollHeight>e.clientHeight+2},{element:n,label:a,found:r}=(()=>{for(const n of e.containerSelectors){const e=document.querySelector(n);if(e&&t(e))return{element:e,label:n,found:!0}}const n=document.querySelector(e.itemSelector);let a=n?.parentElement??null;for(;a&&a!==document.body&&a!==document.documentElement;){if(t(a))return{element:a,label:"item-ancestor",found:!0};a=a.parentElement}const r=document.scrollingElement??document.documentElement;return{element:r,label:"document",found:r.scrollHeight>r.clientHeight+2}})(),o=Math.max(0,n.scrollTop),i=o<=2,s=o>=Math.max(0,n.scrollHeight-n.clientHeight)-2;return{containerFound:r,containerLabel:a,scrollTop:o,scrollHeight:n.scrollHeight,clientHeight:n.clientHeight,itemCount:document.querySelectorAll(e.itemSelector).length,atStart:i,atEnd:s}},t)}async function Gt(e,t,n={}){const a=Dt(n),r=Ut(n),o=Ht(n),i=n.stopOnBoundary??!0,s=await Zt(e,t);let c=s,l=0;for(let s=0;s<r&&(!i||!Wt(c,a));s++)c=await e.evaluate(e=>{const t=e=>{const t=window.getComputedStyle(e).overflowY;return"hidden"!==t&&"clip"!==t&&e.scrollHeight>e.clientHeight+2},{element:n,label:a,found:r}=(()=>{for(const n of e.containerSelectors){const e=document.querySelector(n);if(e&&t(e))return{element:e,label:n,found:!0}}const n=document.querySelector(e.itemSelector);let a=n?.parentElement??null;for(;a&&a!==document.body&&a!==document.documentElement;){if(t(a))return{element:a,label:"item-ancestor",found:!0};a=a.parentElement}const r=document.scrollingElement??document.documentElement;return{element:r,label:"document",found:r.scrollHeight>r.clientHeight+2}})(),o=e.distance??Math.max(120,Math.floor(.85*n.clientHeight)),i="up"===e.direction?-o:o;n.scrollBy({top:i,behavior:"auto"});const s=Math.max(0,n.scrollTop),c=Math.max(0,n.scrollHeight-n.clientHeight);return{containerFound:r,containerLabel:a,scrollTop:s,scrollHeight:n.scrollHeight,clientHeight:n.clientHeight,itemCount:document.querySelectorAll(e.itemSelector).length,atStart:s<=2,atEnd:s>=c-2}},{...t,direction:a,distance:n.distance}),l+=1,o>0&&await e.waitForTimeout(o),c=await Zt(e,t);return{success:s.containerFound||c.containerFound,direction:a,stepsRequested:r,stepsCompleted:l,reachedBoundary:Wt(c,a),before:s,after:c}}async function Yt(e,t,n,a,r={}){const o=Dt(r),i=Ut(r),s=Ht(r),c=r.targetCount,l=r.maxNoNewRounds??jt,d=Math.max(0,Math.floor(r.boundaryLoadRetries??Vt)),u=Math.max(0,Math.floor(r.boundarySettleMs??s)),m=await Zt(e,t),g=new Map;let f=0,p=0,h=m,w=0,y="max-steps";const b=async()=>{let e=0;const t=await n();for(const n of t){const t=a(n);void 0!==t&&0!==t.length&&(g.has(t)?f+=1:(g.set(t,n),e+=1))}return e};await b();for(let n=0;n<i;n++){if(void 0!==c&&g.size>=c){y="target-count";break}if(Wt(h,o)){let n=!1;for(let a=0;a<d;a++){u>0&&await e.waitForTimeout(u);const a=await b(),r=await Zt(e,t),i=a>0||r.scrollHeight>h.scrollHeight||r.itemCount>h.itemCount||!Wt(r,o);if(h=r,i){a>0&&(p=0),n=!0;break}}if(!n){y="boundary";break}if(void 0!==c&&g.size>=c){y="target-count";break}}if(p>=l){y="no-new-items";break}const n=await Gt(e,t,{direction:o,steps:1,settleMs:s,...void 0!==r.distance?{distance:r.distance}:{},...void 0!==r.stopOnBoundary?{stopOnBoundary:r.stopOnBoundary}:{}});w+=n.stepsCompleted,h=n.after;p=await b()>0?0:p+1}return void 0!==c&&g.size>=c?y="target-count":w>=i&&(y="max-steps"),{success:m.containerFound||h.containerFound,direction:o,stepsRequested:i,stepsCompleted:w,reachedBoundary:Wt(h,o),before:m,after:h,items:[...g.values()],uniqueCount:g.size,duplicateCount:f,noNewRounds:p,stopReason:y}}function Kt(e){return"function"==typeof e.page}function Jt(e){return Kt(e)?e.page():e}var Xt=class{page;target;constructor(e){this.page=Jt(e),this.target=e}async retarget(e){if(e===this.target)return this.page=Jt(e),this.target=e,!0;const t=this.page,n=this.target;return await de(t,{target:n}),this.page=Jt(e),this.target=e,!0}async begin(e,t="info"){return await ie(this.page,{label:e,tone:t,target:this.target})}async highlightSelector(e,t={}){return await ce(this.page,e,{...t,target:this.target})}async highlightLocator(e,t={}){return await se(this.page,e,{...t,target:this.target})}async succeed(e,t){return await le(this.page,{label:e,...void 0!==t?{lingerMs:t}:{},status:"success",target:this.target})}async fail(e,t){return await le(this.page,{label:e,...void 0!==t?{lingerMs:t}:{},status:"error",target:this.target})}async clear(){return await de(this.page,{target:this.target})}},Qt=at.object({name:at.string(),conversationId:at.string(),candidateId:at.string(),position:at.string(),time:at.string(),preview:at.string(),unreadCount:at.number(),hasUnread:at.boolean(),index:at.number()}),en=at.object({success:at.boolean(),candidates:at.array(Qt),total:at.number(),stats:at.object({withName:at.number(),withUnread:at.number()})});function tn(e){return e.conversationId.length>0?e.conversationId:e.candidateId.length>0?e.candidateId:0!==e.name.length?[e.name,e.position,e.lastMessageTime].join("|"):void 0}var nn=nt({name:"zhipin_read_messages",description:"读取 BOSS直聘消息列表,默认返回全部候选人;若只看未读消息,传 onlyUnread=true",input:at.object({limit:at.number().optional().describe("最多返回条数"),onlyUnread:at.boolean().default(!1).describe("是否只返回有未读消息的候选人;用户说“全部/所有消息列表”时应为 false,说“未读消息”时应为 true"),sortBy:at.enum(["time","unreadCount","name"]).default("time"),autoScroll:at.boolean().default(!0).describe("是否自动向下滚动消息列表并合并采集结果"),maxScrolls:at.number().int().min(0).max(50).default(4).describe("自动滚动的最大步数")}),output:en,execute:async(e,t)=>{const n=e.onlyUnread??!1;t.logger.info(`Reading zhipin messages (limit: ${e.limit??"all"}, onlyUnread: ${n})`);const a=h(),r=await a.getPage("zhipin"),o=new Xt(r),i=n?"正在读取未读消息列表":"正在读取消息列表";await o.begin("正在打开消息列表");try{const s=await Et(a,r),c=await a.getPage("zhipin");if(await o.retarget(c),!s)return await o.fail("未找到消息列表"),{success:!1,candidates:[],total:0,stats:{withName:0,withUnread:0}};await o.begin(i),await o.highlightSelector(".user-list.b-scroll-stable, .chat-user .user-container, .chat-list-wrap",{label:i,padding:8});const l=e.autoScroll??!0,d=e.maxScrolls??4,u=n||void 0===e.limit?{}:{targetCount:e.limit},m=(l&&d>0?(await Yt(c,Lt("chat-list"),()=>Pt(c),tn,{direction:"down",steps:d,...u})).items:await Pt(c)).map(e=>({name:e.name,conversationId:e.conversationId,candidateId:e.candidateId,position:e.position,time:e.lastMessageTime,preview:e.messagePreview,unreadCount:e.unreadCount,hasUnread:e.hasUnread,index:e.index}));let g=n?m.filter(e=>e.hasUnread):m;const f=e.sortBy??"time";"time"===f||("unreadCount"===f?g.sort((e,t)=>t.unreadCount-e.unreadCount):"name"===f&&g.sort((e,t)=>e.name.localeCompare(t.name))),void 0!==e.limit&&(g=g.slice(0,e.limit));const p={withName:m.filter(e=>e.name.length>0).length,withUnread:m.filter(e=>e.hasUnread).length};return await o.succeed(n?`已读取 ${g.length} 条未读消息`:`已读取 ${g.length} 条消息`),t.logger.info(`Found ${g.length} candidates (${p.withUnread} with unread)`),{success:!0,candidates:g,total:m.length,stats:p}}catch(e){throw await o.fail("读取消息列表失败"),e}}});import{defineTool as an}from"@roll-agent/sdk";import{BrowserPageInfoSchema as rn}from"@roll-agent/browser";import{z as on}from"zod";var sn={login:{notLoggedIn:".header-login-btn"},unread:{container:".chat-list-wrap",listItem:'[role="listitem"]',geekItemWrap:".geek-item-wrap",item:".chat-item",unreadBadge:".badge-count",unreadBadgeNew:".badge-count.badge-count-common-less",unreadBadgeSpan:".badge-count span",unreadDot:".red-dot",figure:".figure",badge:".badge",candidateName:".candidate-name",candidateNameAlt:".chat-item-name",candidateNameSelectors:'[class*="name"], .nickname, .geek-name',candidateNameNew:".geek-name",jobTitle:".source-job",lastMessage:".push-text",lastMessageAlt:".chat-last-msg",messageTime:".time, .time-shadow",clickArea:".chat-item-content",unreadCandidates:".geek-item"},chat:{chatContainer:".chat-container",messageList:".message-list",messageItem:".message-item",messageContent:".message-content",messageText:".text-content",userMessage:".message-right",candidateMessage:".message-left",messageTime:".message-time",senderName:".sender-name",inputBox:".chat-input",inputTextarea:"textarea.chat-input",inputEditorId:"#boss-chat-editor-input",sendButton:".btn-send",conversationEditor:".conversation-editor",submitButton:".submit-content .submit",submitButtonActive:".submit-content .submit.active",submitContent:".submit-content",sendButtonAlt:".conversation-editor .submit-content",sendIcon:".submit-content .icon-send",systemMessage:".system-msg"},chatDetails:{candidateInfoContainer:".base-info-single-container, .base-info-content",candidateName:".name-box, .geek-name, .base-name",candidateInfoItem:".geek-info-item, .base-info-item, .base-info-single-detial > div",candidateTag:".geek-tag, .high-light-boss",communicationPosition:".position-name",communicationPositionAlt:".position-item:not(.expect) .value.high-light-boss",candidateExpectContainer:".position-item.expect",candidateExpectValue:".position-item.expect .value.job",candidateExpectSalary:".position-item.expect .high-light-orange",candidatePosition:".geek-position, .position-name",candidatePositionAlt:".position-content .value, .position-item .value",chatMessageContainer:".conversation-message, .message-list",messageItem:".message-item",messageTime:".message-time .time",messageTextSpan:".text span",systemMessage:".item-system",friendMessage:".item-friend",myMessage:".item-myself",resumeMessage:".item-resume",readStatus:".status-read"},exchangeWechat:{exchangeButtonPath:"#container > div:nth-child(1) > div > div.chat-box > div.chat-container > div.chat-conversation > div.conversation-box > div.conversation-operate > div.toolbar-box > div.toolbar-box-right > div.operate-exchange-left > div:nth-child(3) > span.operate-btn",exchangeButtonFallback:".operate-exchange-left .operate-btn",confirmDialog:".exchange-tooltip",confirmButton:".exchange-tooltip .btn-box .boss-btn-primary.boss-btn",confirmButtonPath:"#container > div:nth-child(1) > div > div.chat-box > div.chat-container > div.chat-conversation > div.conversation-box > div.conversation-operate > div.toolbar-box > div.toolbar-box-right > div.operate-exchange-left > div:nth-child(3) > div > div > span.boss-btn-primary.boss-btn",cancelButton:".exchange-tooltip .btn-box .boss-btn-outline.boss-btn",wechatCard:".message-card-top-wrap",wechatCardAlt:'[class*="d-top-text"]'},username:{primary:".nav-item.nav-logout .user-name",fallbacks:["#header > div > div > div.nav-item.nav-logout > div.top-profile-logout.ui-dropmenu.ui-dropmenu-drop-arrow > div.ui-dropmenu-label > div > span.user-name",".ui-dropmenu-label .user-name",".nav-logout .user-name","#header .user-name",".top-profile .user-name",".nav-user .user-name",".user-name",'[class*="user-name"]','[class*="username"]','[data-qa="user-name"]',".header-user-name","#header .label-name"]},nav:{sidebar:".side-wrap.side-wrap-v2",chatLink:'.side-wrap.side-wrap-v2 a[href*="/web/chat/index"]',recommendLink:'.side-wrap.side-wrap-v2 a[href*="/web/geek/recommend"]'},recommend:{iframe:"#recommendFrame",resumeIframe:'iframe[src*="c-resume"]',filterButton:".recommend-filter .filter-label, .filter-label-wrap .filter-label, .filter-label",filterPanel:".filter-panel",candidateItem:"[data-geek], .geek-item",candidateName:".name",candidateBaseInfo:".base-info",workExps:".timeline-wrap.work-exps",expectInfo:".row-flex, .timeline-wrap.expect",salaryWrap:".salary-wrap",tagsWrap:".tags-wrap",greetButton:".btn-greet, .op-btn",resumeCanvas:"div#resume > canvas#resume, canvas#resume",closeResumeBtn:".close-btn, .dialog-close",closeResumeBtnV2:".recommendV2 .close-btn"},candidateProfile:{panel:".candidate-info, .resume-info, .geek-info",name:".candidate-info .name, .geek-info .name",age:".candidate-info .age, .geek-info .age",gender:".candidate-info .gender, .geek-info .gender",experience:".candidate-info .experience, .geek-info .work-exp",education:".candidate-info .education, .geek-info .edu",expectedSalary:".candidate-info .salary, .geek-info .expect-salary",expectedPosition:".candidate-info .position, .geek-info .expect-position",activeTime:".candidate-info .active-time, .geek-info .active"}},cn={chat:"沟通",recommend:"推荐牛人"};function ln(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function dn(e){for(const t of e)try{if(await t.count()>0&&await t.first().isVisible())return t.first()}catch{}return null}function un(e){return cn[e]}async function mn(e,t){const n=e.locator(sn.nav.sidebar).first(),a=un(t),r=new RegExp(`^${ln(a)}(?:\\s|$)`),o="recommend"===t?[sn.nav.recommendLink]:[sn.nav.chatLink],i=[n.getByRole("link",{name:r}).first(),...o.map(e=>n.locator(e).first()),n.locator(`a:has-text("${a}")`).first(),e.getByRole("link",{name:r}).first()];return await dn(i)}function gn(e){return!!e.url().includes("/web/geek/recommend")||(null!==e.frame("recommendFrame")||e.frames().some(e=>e.url().includes("recommend")))}async function fn(e){if(e.url().includes("/web/chat/index"))return!0;try{const t=e.locator("#container.chat-container-private").first();return await t.count()>0&&await t.isVisible()}catch{return!1}}async function pn(e,t=1e4,n=250){const a=Date.now()+t;for(;Date.now()<a;){if(gn(e))return!0;if(e.isClosed())return!1;await e.waitForTimeout(Math.min(n,Math.max(a-Date.now(),0)))}return gn(e)}async function hn(e,t=1e4,n=250){const a=Date.now()+t;for(;Date.now()<a;){if(await fn(e))return!0;if(e.isClosed())return!1;await e.waitForTimeout(Math.min(n,Math.max(a-Date.now(),0)))}return await fn(e)}var wn,yn=on.object({success:on.boolean(),alreadyOnChat:on.boolean(),usedSidebarClick:on.boolean(),chatReady:on.boolean(),page:rn.optional(),error:on.string().optional()});function bn(){return{getContextManager:h,findZhipinSidebarSectionLink:mn,isZhipinChatSurfaceOpen:fn,waitForZhipinChatSurface:hn,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,randomDelay:ot,toAttachedPageInfo:Ce,createVisualActivitySession:e=>new Xt(e),...wn}}async function vn(e,t,n){return await t.toAttachedPageInfo(e,n)}async function xn(e,t,n,a,r,o){return await n.fail(r),{success:!1,...o,page:await vn(e,t,a),error:r}}async function Sn(e,t,n,a){await n.scrollIntoViewIfNeeded(),await t.moveVisualCursorToLocator(e,n,{durationMs:110,settleMs:30}),await n.hover(),await t.randomDelay(e,100,180),await t.showVisualClickOnLocator(e,n,{pulseDurationMs:180}),await n.click(),a.info("Clicked Boss sidebar nav: 沟通")}var Cn=an({name:"zhipin_open_chat_page",description:"通过点击 Boss 左侧导航切换回「沟通」页,避免让编排器依赖站内 URL 猜测。",input:on.object({}),output:yn,execute:async(e,t)=>{const n=bn(),a=n.getContextManager();t.logger.info("Opening Boss chat page via sidebar navigation");const r=await a.getPage("zhipin");await r.bringToFront().catch(()=>{});const o=n.createVisualActivitySession(r),i="正在切换到沟通页";if(await o.begin(i),await o.highlightSelector(sn.nav.sidebar,{label:i,padding:10}),await n.isZhipinChatSurfaceOpen(r))return await o.succeed("已在沟通页"),{success:!0,alreadyOnChat:!0,usedSidebarClick:!1,chatReady:!0,page:await vn(a,n,r)};const s=await n.findZhipinSidebarSectionLink(r,"chat");if(!s)return await xn(a,n,o,r,"未找到沟通导航",{alreadyOnChat:!1,usedSidebarClick:!1,chatReady:!1});try{await Sn(r,n,s,t.logger)}catch(e){return await xn(a,n,o,r,e instanceof Error?e.message:"点击沟通导航失败",{alreadyOnChat:!1,usedSidebarClick:!0,chatReady:!1})}return await n.waitForZhipinChatSurface(r)?(await o.succeed("已切换到沟通页"),{success:!0,alreadyOnChat:!1,usedSidebarClick:!0,chatReady:!0,page:await vn(a,n,r)}):await xn(a,n,o,r,"沟通页未就绪",{alreadyOnChat:!1,usedSidebarClick:!0,chatReady:!1})}});import{defineTool as An}from"@roll-agent/sdk";import{z as kn}from"zod";var In=kn.object({success:kn.boolean(),conversationId:kn.string(),candidateId:kn.string(),candidateName:kn.string(),index:kn.number(),hasUnread:kn.boolean(),unreadCount:kn.number(),lastMessageTime:kn.string(),messagePreview:kn.string(),error:kn.string().optional()}),Mn=An({name:"zhipin_open_chat",description:"打开指定候选人的聊天窗口(优先按 conversationId,其次姓名,最后才用索引)",input:kn.object({conversationId:kn.string().optional().describe("会话 ID。若已从 `zhipin_read_messages` 获取,优先传这个,最稳定"),candidateName:kn.string().optional().describe("候选人姓名。若用户说“打开鲁倩的聊天”,这里应提取为“鲁倩”"),index:kn.number().optional().describe("候选人在列表中的索引。仅在缺少 conversationId 时兜底"),preferUnread:kn.boolean().default(!1).describe("优先选择有未读消息的候选人")}),output:In,execute:async(e,t)=>{t.logger.info(`Opening chat: name=${e.candidateName??"N/A"}, index=${e.index??"N/A"}`);const n=h(),a=await n.getPage("zhipin");let r={conversationId:e.conversationId,candidateName:e.candidateName,index:e.index};if(e.preferUnread&&void 0===e.conversationId&&void 0===e.candidateName&&void 0===e.index){if(!await Et(n,a))return{success:!1,conversationId:"",candidateId:"",candidateName:"",index:-1,hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:"消息列表未加载"};const e=await n.getPage("zhipin"),t=(await Pt(e)).find(e=>e.hasUnread);t&&(r={conversationId:t.conversationId,candidateName:t.name,index:t.index})}const o=await qt(n,a,r);return o&&o.found?(t.logger.info(`Opened chat with ${o.name} (index: ${o.index})`),{success:!0,conversationId:o.conversationId,candidateId:o.candidateId,candidateName:o.name,index:o.index,hasUnread:o.hasUnread,unreadCount:o.unreadCount,lastMessageTime:o.lastMessageTime,messagePreview:o.messagePreview}):{success:!1,conversationId:"",candidateId:"",candidateName:e.candidateName??"",index:e.index??-1,hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:o?.error??`未找到候选人: ${e.candidateName??`index ${e.index}`}`}}});import{defineTool as Rn}from"@roll-agent/sdk";import{z as Bn}from"zod";var En="·",Pn=/[--—–]/;function $n(e){return(e??"").trim()}function _n(e){const t=$n(e);if(!t||!Pn.test(t))return;const[n=""]=t.split(Pn);return $n(n)||void 0}function qn(e){const t=$n(e);if(!t)return{expectedLocation:"",expectedPosition:""};const[n="",a=""]=t.split(En).map(e=>$n(e));return{expectedLocation:n,expectedPosition:a}}function Nn(e){const t=$n(e.communicationPosition),{expectedLocation:n,expectedPosition:a}=qn(e.expectedJobText),r=_n(t);return{communicationPosition:t,expectedLocation:n,expectedPosition:a,...void 0!==r?{preferredBrand:r}:{}}}var Tn=Bn.object({index:Bn.number(),sender:Bn.enum(["candidate","recruiter","system"]),messageType:Bn.enum(["text","system","resume","wechat-exchange"]),content:Bn.string(),time:Bn.string()}),Ln=Bn.object({name:Bn.string(),age:Bn.string(),experience:Bn.string(),education:Bn.string(),communicationPosition:Bn.string(),expectedPosition:Bn.string(),expectedLocation:Bn.string(),expectedSalary:Bn.string(),tags:Bn.array(Bn.string())}),zn=Bn.object({success:Bn.boolean(),conversationId:Bn.string(),candidateId:Bn.string(),candidateInfo:Ln,preferredBrand:Bn.string().optional(),chatMessages:Bn.array(Tn),formattedHistory:Bn.array(Bn.string()),stats:Bn.object({totalMessages:Bn.number(),candidateMessages:Bn.number(),recruiterMessages:Bn.number(),systemMessages:Bn.number()}),error:Bn.string().optional()});function Fn(){return{name:"",age:"",experience:"",education:"",communicationPosition:"",expectedPosition:"",expectedLocation:"",expectedSalary:"",tags:[]}}function On(e){return{success:!1,conversationId:"",candidateId:"",candidateInfo:Fn(),chatMessages:[],formattedHistory:[],stats:{totalMessages:0,candidateMessages:0,recruiterMessages:0,systemMessages:0},error:e}}function jn(e,t){const n=e.trim().toLocaleLowerCase("zh-CN"),a=t.trim().toLocaleLowerCase("zh-CN");return n.length>0&&a.length>0&&(n===a||n.includes(a)||a.includes(n))}var Vn=Rn({name:"zhipin_get_candidate_info",description:"提取候选人资料和完整聊天记录。可指定 conversationId 或 candidateName 自动打开对应聊天;若已从 `zhipin_read_messages` 获取 conversationId,优先传它。",input:Bn.object({conversationId:Bn.string().optional().describe("会话 ID。若已从 `zhipin_read_messages` 获取,优先传这个,最稳定"),candidateName:Bn.string().optional().describe("候选人姓名。若用户说“查看鲁倩的聊天详情”,这里应提取为“鲁倩”"),index:Bn.number().optional().describe("候选人在列表中的索引(可选,仅兜底)"),maxMessages:Bn.number().default(100).describe("最多返回的消息条数")}),output:zn,execute:async(e,t)=>{const n=e.maxMessages??100,a=h(),r=await a.getPage("zhipin"),o=new Xt(r),i=void 0!==e.conversationId||void 0!==e.candidateName||void 0!==e.index?"正在打开目标聊天":"正在准备当前聊天",s="正在提取聊天记录",c=async(e,t)=>(await o.fail(e),On(t));await o.begin(i);try{const i=await qt(a,r,{conversationId:e.conversationId,candidateName:e.candidateName,index:e.index}),l=await a.getPage("zhipin");if(await o.retarget(l),i&&!i.found)return await c("打开聊天失败",i.error??"打开聊天失败");t.logger.info("Extracting candidate info"+(i?` for ${i.name}`:" (current window)")),await o.begin(s),await o.highlightSelector(".chat-conversation, .conversation-box, .conversation-message",{label:s,padding:12});const d=i?.name??e.candidateName??"",u=await ut(l);if(d.length>0&&(!u||!jn(d,u.candidateName)))return await c("聊天面板未同步",`右侧聊天面板未切换到 ${d}`);const m=await dt(l);if(!m)return await c("未识别当前会话","未能提取当前选中聊天的 conversationId/candidateId");if(i&&m.conversationId!==i.conversationId)return await c("当前会话不一致",`当前选中会话与目标会话不一致: ${i.name||i.conversationId}`);if(u&&m.candidateName.length>0&&!jn(m.candidateName,u.candidateName))return await c("左右面板不一致",`左侧选中会话与右侧聊天面板不一致: ${m.candidateName} / ${u.candidateName}`);try{await l.waitForSelector(".chat-message-list .message-item, .conversation-message .message-item",{timeout:8e3})}catch{}const g=await l.evaluate(e=>{const t=document.querySelector(".chat-conversation")??document.querySelector(".conversation-box")??document,n=t.querySelector(".base-info-single-detial, .base-info-content, .base-info-single-container"),a=n?.querySelector(".name-box, .base-name, .chat-user-name, .geek-name")?.textContent?.trim()??"",r=n?n.querySelectorAll(":scope > div"):t.querySelectorAll(".geek-info-item, .base-info-item"),o=[];r.forEach(e=>{const t=e.textContent?.trim();t&&o.push(t)});const i=o.join(" "),s=i.match(/(\d{2,3})岁/),c=s?s[1]+"岁":"",l=i.match(/(\d+年(?:以上)?|应届生|在校生)/),d=l?.[1]??"",u=i.match(/(初中|高中|中专|大专|本科|硕士|博士)/)?.[1]??"";let m="";const g=t.querySelector(".position-name");if(g){const e=g.cloneNode(!0);e.querySelectorAll(".popover-wrap, .tooltip-job").forEach(e=>e.remove()),m=e.textContent?.trim()??""}let f="";const p=t.querySelector(".position-item.expect .value.job");p&&(f=p.textContent?.trim()??"");const h=t.querySelector(".position-item.expect .high-light-orange")?.textContent?.trim()??"",w=[];n&&n.querySelectorAll(".geek-tag, .base-info-item .high-light-boss").forEach(e=>{const t=e.textContent?.trim();t&&!t.includes("更换职位")&&t.length<20&&w.push(t)});const y=t.querySelectorAll(".chat-message-list > .message-item, .conversation-message .message-item"),b=/\d{1,2}:\d{2}(?::\d{2})?|\d{4}-\d{2}-\d{2}/,v=[];let x=0;return y.forEach(t=>{if(x>=e)return;const n=null!==t.querySelector(".item-friend"),a=null!==t.querySelector(".item-myself"),r=null!==t.querySelector(".item-system"),o=null!==t.querySelector(".item-resume"),i=null!==t.querySelector(".message-dialog-center");let s="system",c="text";n?s="candidate":a?s="recruiter":(r||i)&&(s="system",c="system"),o&&(c="resume");const l=t.querySelector(".message-card-top-wrap, [class*='d-top-text']");if(l){const e=l.textContent??"";(e.includes("微信")||e.includes("WeChat"))&&(c="wechat-exchange")}const d=t.querySelector(".message-time .time, .message-time"),u=(d?.textContent??"").match(b),m=u?u[0]:"";let g="";if("wechat-exchange"===c&&l){const e=l.textContent??"",t=e.match(/\b(\d{8,15})\b/),n=e.match(/微信[::号]*\s*([a-zA-Z0-9_-]{5,20})/);g=t?`[微信号: ${t[1]}]`:n?`[微信号: ${n[1]}]`:"[交换微信]"}else if(l){const e=t.querySelector(".message-card-top-title"),n=t.querySelector(".dialog-content, .message-card-top-text");g=(e?.textContent?.trim()??n?.textContent?.trim()??"").trim()}else{const e=t.querySelector(".text span, .text-content, .text");e&&(g=(e.textContent?.trim()??"").replace(b,"").replace("已读","").trim())}(g||"text"!==c)&&(v.push({index:x,sender:s,messageType:c,content:g,time:m}),x++)}),{candidateInfo:{name:a,age:c,experience:d,education:u,communicationPosition:m,expectedJobText:f,expectedSalary:h,tags:w},messages:v}},n),f=Nn({communicationPosition:g.candidateInfo.communicationPosition,expectedJobText:g.candidateInfo.expectedJobText}),p=g.messages.filter(e=>"candidate"===e.sender||"recruiter"===e.sender).map(e=>`${"candidate"===e.sender?"求职者":"我"}: ${e.content}`),h={totalMessages:g.messages.length,candidateMessages:g.messages.filter(e=>"candidate"===e.sender).length,recruiterMessages:g.messages.filter(e=>"recruiter"===e.sender).length,systemMessages:g.messages.filter(e=>"system"===e.sender).length};return await o.succeed(`已提取 ${h.totalMessages} 条聊天记录`),t.logger.info(`Extracted info for ${g.candidateInfo.name}: ${h.totalMessages} messages`),{success:!0,conversationId:m.conversationId,candidateId:m.candidateId,candidateInfo:{name:g.candidateInfo.name,age:g.candidateInfo.age,experience:g.candidateInfo.experience,education:g.candidateInfo.education,communicationPosition:f.communicationPosition,expectedPosition:f.expectedPosition,expectedLocation:f.expectedLocation,expectedSalary:g.candidateInfo.expectedSalary,tags:g.candidateInfo.tags},...void 0!==f.preferredBrand?{preferredBrand:f.preferredBrand}:{},chatMessages:g.messages,formattedHistory:p,stats:h}}catch(e){throw await o.fail("提取聊天记录失败"),e}}});import{defineTool as Dn}from"@roll-agent/sdk";import{z as Un}from"zod";var Hn=30,Wn=new Set(["招聘规范","消息","首页","推荐牛人","看简历","我的客服","面试","招聘数据","账号权益","升级VIP","职位","职位管理","牛人","公司","数据统计","设置","帮助中心","登录","注册","退出登录","退出","BOSS直聘","下载APP","搜索","发布职位"]);function Zn(e){const t=e.trim();return!(0===t.length||t.length>Hn)&&(!Wn.has(t)&&!/登录|注册|退出|下载|帮助|设置|管理/.test(t))}function Gn(e){const t=[],n=/(link|button|menuitem|img|heading)\s+"([^"]+)"/g;let a;for(;null!==(a=n.exec(e));){const e=a[2]?.trim()??"";e.length>0&&t.push({role:a[1]??"",name:e})}return t}function Yn(e){let t=e.priority;return Wn.has(e.text.trim())&&(t+=10),e.text.trim().length>10&&(t+=5),/^[\u4e00-\u9fff]{2,4}$/.test(e.text.trim())&&(t-=.5),void 0!==e.xRatio&&(t-=4*(e.xRatio-.5)),t}function Kn(e){const t=e.filter(e=>Zn(e.text));if(0===t.length)return{found:!1};const n=new Map;for(const e of t){const t=e.text.trim(),a=n.get(t)??new Set;a.add(e.strategy),n.set(t,a)}const a=t.map(e=>{let t=Yn(e);const a=n.get(e.text.trim())?.size??1;return a>1&&(t-=.5*a),{evidence:e,score:t}}).sort((e,t)=>e.score-t.score)[0];return a?{found:!0,username:a.evidence.text.trim(),strategy:a.evidence.strategy,source:a.evidence.source}:{found:!1}}async function Jn(e){const t=[e.getByRole("banner"),e.locator("header").first(),e.getByRole("navigation").first(),e.locator("#header")];for(const e of t)try{if(await e.count()>0&&await e.first().isVisible())return e.first()}catch{}return null}async function Xn(e,t,n,a){const r=[],o=t.viewportSize(),i=o?.width??1280;try{const t=await e.getByRole(n).all();for(const e of t)try{if(!await e.isVisible())continue;const t=(await e.textContent()??"").trim();if(t.length>0&&t.length<=Hn){let o;try{const t=await e.boundingBox();t&&(o=(t.x+t.width/2)/i)}catch{}r.push({text:t,strategy:a,priority:1,source:`role:${n}`,xRatio:o})}}catch{}}catch{}return r}async function Qn(e){const t=[];try{const n=Gn(await e.ariaSnapshot({timeout:3e3}));for(const{role:e,name:a}of n)a.length>0&&a.length<=Hn&&t.push({text:a,strategy:"aria-snapshot",priority:2,source:`aria:${e}:${a}`})}catch{}return t}async function ea(e){const t=[];try{const n=await e.evaluate((e,t)=>{const n=[],a=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;a.nextNode();){const e=a.currentNode.textContent?.trim();e&&e.length>0&&e.length<=t&&n.push(e)}return n},Hn);for(const e of n)t.push({text:e,strategy:"leaf-text",priority:3,source:"leaf-text"})}catch{}return t}async function ta(e){const t=[];try{const n=[sn.username.primary,...sn.username.fallbacks],a=await e.evaluate(e=>e.map(e=>{try{return{selector:e,text:document.querySelector(e)?.textContent?.trim()??""}}catch{return{selector:e,text:""}}}),n);for(const{selector:e,text:n}of a)n.length>0&&n.length<=Hn&&t.push({text:n,strategy:"css-fallback",priority:4,source:e})}catch{}return t}async function na(e){const t=await Jn(e),n=t?(await Promise.all([Xn(t,e,"link","role-link"),Xn(t,e,"button","role-button"),Qn(t)])).flat():[],a=Kn(n);if(a.found){const e=a.username;if(new Set(n.filter(t=>t.text.trim()===e).map(e=>e.strategy)).size>=2)return n}const r=(await Promise.all([t?ea(t):Promise.resolve([]),ta(e)])).flat();return[...n,...r]}async function aa(e,t=na){const n=Kn(await t(e));if(!n.found)throw new Error("未找到用户名,请确认当前页面已登录招聘者账号。");return{platform:"zhipin",username:n.username,strategy:n.strategy,source:n.source}}function ra(e,t){return void 0!==t.accountId?e.accountId===t.accountId:e.username===t.username}import{z as oa}from"zod";var ia="reply-authority-service",sa="browser-use-agent/zhipin_send_reply",ca="zhipin",la=60,da=oa.object({platform:oa.literal(ca),username:oa.string().min(1),accountId:oa.string().min(1).optional()}),ua=oa.object({v:oa.literal(2),iss:oa.literal(ia),kid:oa.string().min(1),jti:oa.string().min(1),iat:oa.number().int(),exp:oa.number().int(),aud:oa.literal(sa),platform:oa.literal(ca),tenantId:oa.string().min(1),conversationId:oa.string().min(1),candidateId:oa.string().min(1),reply:oa.string(),policyVersion:oa.string().min(1),recruiterBinding:da}),ma=oa.object({kid:oa.string().min(1),algorithm:oa.literal("Ed25519"),publicKey:oa.string().min(1),validFrom:oa.string().min(1),validUntil:oa.string().optional()}),ga=oa.object({keys:oa.array(ma)}),fa=new Map;function pa(e){for(const[t,n]of fa)n<e-la&&fa.delete(t)}function ha(e,t=Math.floor(Date.now()/1e3)){return pa(t),fa.has(e)}function wa(e,t,n=Math.floor(Date.now()/1e3)){pa(n),fa.set(e,t)}import{createPublicKey as ya,verify as ba}from"node:crypto";var va=new Map,xa=null;function Sa(){const e=process.env.REPLY_AUTHORITY_KEYS_URL?.trim();if(!e)throw new Error("REPLY_AUTHORITY_KEYS_URL 未配置,browser-use-agent 无法拉取 Reply Authority 公钥。");return e}function Ca(e){return va=new Map(e.map(e=>[e.kid,e]))}async function Aa(){if(xa)return xa;xa=(async()=>{const e=await fetch(Sa()),t=await e.json();if(!e.ok)throw new Error(`Reply Authority 公钥拉取失败 (${e.status})`);return Ca(ga.parse(t).keys)})();try{return await xa}finally{xa=null}}async function ka(){await Aa()}async function Ia(e){const t=va.get(e);if(t)return t;const n=(await Aa()).get(e);if(!n)throw new Error(`Unknown key ID: ${e}`);return n}function Ma(e){if("object"==typeof e&&null!==e&&"v"in e&&"number"==typeof e.v&&2!==e.v)throw new Error("unexpected envelope version")}function Ra(e){const t=e.split("."),n=t[0],a=t[1];if(2!==t.length||void 0===n||void 0===a||0===n.length||0===a.length)throw new Error("Invalid signed envelope format");let r,o="";try{o=Buffer.from(n,"base64url").toString("utf-8")}catch{throw new Error("Invalid signed envelope format")}try{r=JSON.parse(o)}catch{throw new Error("Envelope payload schema validation failed")}Ma(r);const i=ua.safeParse(r);if(!i.success)throw new Error("Envelope payload schema validation failed");return{payload:i.data,payloadJson:o,signatureBase64:a}}function Ba(e,t){if(e.exp<=e.iat)throw new Error("Envelope expiry must be after issue time");if(e.exp<t-la)throw new Error("Envelope expired");if(e.iat>t+la)throw new Error("Envelope issued in the future")}async function Ea(e,t=Math.floor(Date.now()/1e3)){const n=Ra(e),a=await Ia(n.payload.kid),r=ya({key:Buffer.from(a.publicKey,"base64url"),format:"der",type:"spki"});if(!ba(null,Buffer.from(n.payloadJson,"utf-8"),r,Buffer.from(n.signatureBase64,"base64url")))throw new Error("Signature verification failed");return Ba(n.payload,t),n.payload}var Pa=Un.object({success:Un.boolean(),sentMessage:Un.string(),error:Un.string().optional()});function $a(e,t){const n=e.trim().toLocaleLowerCase("zh-CN"),a=t.trim().toLocaleLowerCase("zh-CN");return n.length>0&&a.length>0&&(n===a||n.includes(a)||a.includes(n))}var _a=Dn({name:"zhipin_send_reply",description:"发送消息。只接受由 Reply Authority Service 签发的 signedEnvelope;可指定 candidateName 自动打开对应聊天后发送,或不传则发送到当前选中的聊天窗口。",input:Un.object({signedEnvelope:Un.string().describe("Reply Authority Service 返回的紧凑签名信封"),candidateName:Un.string().optional().describe("候选人姓名。若用户说“回复鲁倩”,这里应提取为“鲁倩”"),index:Un.number().optional().describe("候选人在列表中的索引(可选)")}),output:Pa,execute:async(e,t)=>{let n="";if(!b()){const e="Reply Authority 公钥尚未成功预加载,当前无法发送签名回复。请检查启动日志、`REPLY_AUTHORITY_KEYS_URL` 配置,以及 `browser_status.replyAuthorityKeysLoaded`。";return t.logger.error(e),{success:!1,sentMessage:n,error:e}}const a=h(),r=await a.getPage("zhipin");let o=r;try{const i=await Ea(e.signedEnvelope);if(n=i.reply,ha(i.jti))return{success:!1,sentMessage:n,error:"token 已消费,禁止重放"};const s=await qt(a,r,{conversationId:i.conversationId,candidateName:e.candidateName,index:e.index});if(s&&!s.found)return{success:!1,sentMessage:n,error:s.error};if(!s){if(!await Et(a,r))return{success:!1,sentMessage:n,error:"消息列表未加载"}}o=await a.getPage("zhipin");const c=await ut(o),l=await dt(o);if(!l)return{success:!1,sentMessage:n,error:"未能提取当前聊天的 conversationId/candidateId"};if(c&&l.candidateName.length>0&&!$a(l.candidateName,c.candidateName))return{success:!1,sentMessage:n,error:`左侧选中会话与右侧聊天面板不一致: ${l.candidateName} / ${c.candidateName}`};if(l.conversationId!==i.conversationId||l.candidateId!==i.candidateId)return{success:!1,sentMessage:n,error:"发送目标与签名不匹配"};const d=await aa(o);if(!ra(d,i.recruiterBinding))return{success:!1,sentMessage:n,error:`recruiter 绑定不匹配:当前账号 ${d.username} 与签发时 ${i.recruiterBinding.username} 不一致`};t.logger.info(`Sending message (${n.length} chars) to ${l.candidateName||l.candidateId}`);const u="#boss-chat-editor-input, textarea.chat-input, .chat-input";await o.waitForSelector(u,{timeout:5e3});const m=o.locator(u).first();await Z(o,m);await o.evaluate(e=>{const t=document.querySelector(e);return"true"===t?.getAttribute("contenteditable")},u)?(await m.focus(),await o.evaluate(e=>{const t=document.querySelector(e.sel);t&&(t.innerHTML=e.msg.split("\n").map(e=>`<p>${e}</p>`).join(""))},{sel:u,msg:n}),await m.dispatchEvent("input",{bubbles:!0})):await o.fill(u,n),await ot(o,200,500);const g=await o.evaluate(()=>{document.querySelectorAll("[data-roll-send-btn]").forEach(e=>{e.removeAttribute("data-roll-send-btn")});const e=[".submit-content .submit.active",".submit-content .submit",".submit-content",".btn-send"];for(const t of e){const e=document.querySelector(t);if(e&&e.offsetWidth>0)return{found:!0,selector:t}}const t=Array.from(document.querySelectorAll("span"));for(const e of t)if("发送"===e.textContent?.trim()&&e.offsetWidth>0)return e.setAttribute("data-roll-send-btn","true"),{found:!0,selector:'[data-roll-send-btn="true"]'};return{found:!1}});if(!g.found)return{success:!1,sentMessage:n,error:"未找到发送按钮"};const f=o.locator(g.selector).first();return await f.scrollIntoViewIfNeeded(),await Z(o,f),await f.hover(),await it(o),await G(o,f),await f.click(),await ot(o,500,1200),wa(i.jti,i.exp),t.logger.info("Message sent successfully"),{success:!0,sentMessage:n}}catch(e){return{success:!1,sentMessage:n,error:e instanceof Error?e.message:String(e)}}finally{await o.evaluate(()=>{document.querySelectorAll("[data-roll-send-btn]").forEach(e=>{e.removeAttribute("data-roll-send-btn")})}).catch(()=>{})}}});import{defineTool as qa}from"@roll-agent/sdk";import{z as Na}from"zod";var Ta=Na.object({success:Na.boolean(),exchanged:Na.boolean(),wechatNumber:Na.string().optional(),error:Na.string().optional()}),La="data-roll-wechat-btn",za="data-roll-confirm-btn";function Fa(e,t){const n=e.trim().toLocaleLowerCase("zh-CN"),a=t.trim().toLocaleLowerCase("zh-CN");return n.length>0&&a.length>0&&(n===a||n.includes(a)||a.includes(n))}var Oa=qa({name:"zhipin_exchange_wechat",description:'换微信。可指定 candidateName 自动打开对应聊天后执行,或不传则在当前窗口执行;例如"和鲁倩换微信"应提取 candidateName=鲁倩。',input:Na.object({conversationId:Na.string().optional().describe("会话 ID。若已从消息列表拿到,优先传这个"),candidateName:Na.string().optional().describe('候选人姓名。若用户说"和鲁倩换微信",这里应提取为"鲁倩"'),index:Na.number().optional().describe("候选人在列表中的索引(可选)")}),output:Ta,execute:async(e,t)=>{const n=h(),a=await n.getPage("zhipin"),r=await qt(n,a,{conversationId:e.conversationId,candidateName:e.candidateName,index:e.index});if(r&&!r.found)return{success:!1,exchanged:!1,error:r.error};t.logger.info("Starting WeChat exchange"+(r?` with ${r.name}`:""));const o=await n.getPage("zhipin");try{const e=await dt(o);if(!e)return{success:!1,exchanged:!1,error:"未选中聊天联系人,无法点击当前聊天输入区的「换微信」按钮"};const n=await ut(o);if(n&&e.candidateName.length>0&&!Fa(e.candidateName,n.candidateName))return{success:!1,exchanged:!1,error:`左侧选中会话与右侧聊天面板不一致: ${e.candidateName} / ${n.candidateName}`};if(!(await o.evaluate(e=>{const t=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},n=e=>e.replace(/\s+/g,"").trim();document.querySelectorAll(`[${e}]`).forEach(t=>{t.removeAttribute(e)});const a=[".chat-conversation .conversation-operate .operate-exchange-left span.operate-btn",".chat-conversation .conversation-operate span.operate-btn",".conversation-box .conversation-operate .operate-exchange-left span.operate-btn",".conversation-box .conversation-operate span.operate-btn",".conversation-operate .operate-exchange-left span.operate-btn",".conversation-operate .operate-exchange-left span"];for(const r of a){const a=Array.from(document.querySelectorAll(r));for(const r of a){const a=n(r.textContent??"");if("换微信"===a&&t(r))return r.setAttribute(e,"true"),{found:!0,text:a}}}return{found:!1}},La)).found)return{success:!1,exchanged:!1,error:"未找到当前聊天输入区的「换微信」按钮"};await ot(o,200,400),await Y(o,`[${La}="true"]`),await K(o,`[${La}="true"]`),await o.click(`[${La}="true"]`),await o.evaluate(()=>{document.querySelector("[data-roll-wechat-btn]")?.removeAttribute("data-roll-wechat-btn")}),await ot(o,400,800);let a=!1;for(let e=0;e<8;e++){if(await o.evaluate(()=>{const e=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0},t=document.querySelector(".exchange-tooltip");if(t&&e(t))return!0;const n=document.querySelectorAll("div, section, aside");for(const t of Array.from(n)){if((t.textContent??"").includes("交换微信")&&t.querySelector(".boss-btn-primary, .boss-btn")&&e(t))return!0}return!1})){a=!0;break}await ot(o,400,800)}if(!a)return{success:!1,exchanged:!1,error:"确认对话框未弹出"};await it(o);if(!(await o.evaluate(e=>{const t=e=>{const t=e.getBoundingClientRect();return t.width>0&&t.height>0};document.querySelectorAll(`[${e}]`).forEach(t=>{t.removeAttribute(e)});const n=document.querySelector(".exchange-tooltip");if(n){const a=[".btn-box .boss-btn-primary.boss-btn",".btn-box span.boss-btn-primary","span.boss-btn-primary",".boss-btn-primary"];for(const r of a){const a=n.querySelector(r);if(a&&t(a))return a.setAttribute(e,"true"),{found:!0,text:a.textContent?.trim()??""}}}const a=document.querySelectorAll("div, section, aside");for(const n of Array.from(a)){if(!(n.textContent??"").includes("交换微信"))continue;const a=n.querySelectorAll("span.boss-btn-primary, button.boss-btn-primary, span.boss-btn, button.boss-btn");for(const n of Array.from(a)){const a=n.textContent?.trim()??"";if("确定"===a&&t(n))return n.setAttribute(e,"true"),{found:!0,text:a}}}return{found:!1}},za)).found)return{success:!1,exchanged:!1,error:"未找到确认按钮"};await ot(o,200,400),await Y(o,`[${za}="true"]`),await K(o,`[${za}="true"]`),await o.click(`[${za}="true"]`),await o.evaluate(()=>{document.querySelector("[data-roll-confirm-btn]")?.removeAttribute("data-roll-confirm-btn")}),await ot(o,1500,2500);const r=await o.evaluate(()=>{const e=[".message-card-top-wrap",'[class*="d-top-text"]',".message-card-top-title"];for(const t of e){const e=Array.from(document.querySelectorAll(t));for(let t=e.length-1;t>=0;t--){const n=e[t]?.textContent??"",a=n.match(/\b(\d{8,15})\b/);if(a)return a[1];const r=n.match(/微信[::号]*\s*([a-zA-Z0-9_-]{5,20})/);if(r)return r[1];const o=n.match(/\b([a-zA-Z][a-zA-Z0-9_-]{5,19})\b/);if(o&&!["微信","WeChat"].includes(o[1]))return o[1]}}const t=Array.from(document.querySelectorAll(".message-item"));for(let e=t.length-1;e>=0;e--){const n=t[e]?.querySelector('.message-card-top-wrap, [class*="d-top-text"]');if(n){const e=(n.textContent??"").match(/\b(\d{8,15})\b/);if(e)return e[1]}}return null});return t.logger.info("WeChat exchanged"+(r?`, number: ${r}`:"")),{success:!0,exchanged:!0,...null!==r?{wechatNumber:r}:{}}}catch(e){return{success:!1,exchanged:!1,error:e instanceof Error?e.message:String(e)}}finally{await o.evaluate(e=>{for(const t of e)document.querySelectorAll(`[${t}]`).forEach(e=>{e.removeAttribute(t)})},[La,za]).catch(()=>{})}}});import{defineTool as ja}from"@roll-agent/sdk";import{z as Va}from"zod";var Da,Ua=Va.object({success:Va.boolean(),username:Va.string(),usedSelector:Va.string().optional(),usedStrategy:Va.string().optional(),source:Va.string().optional(),error:Va.string().optional()});function Ha(){return{getContextManager:h,findHeaderScope:Jn,getCurrentZhipinRecruiterIdentity:aa,createVisualActivitySession:e=>new Xt(e),...Da}}var Wa=ja({name:"zhipin_get_username",description:"获取当前登录的招聘者用户名",input:Va.object({}),output:Ua,execute:async(e,t)=>{t.logger.info("Getting zhipin username");const n=Ha();let a;try{const e=n.getContextManager(),r=await e.getPage("zhipin");a=n.createVisualActivitySession(r),await r.bringToFront().catch(()=>{}),await a.begin("正在识别登录账号");const o=await n.findHeaderScope(r);o&&await a.highlightLocator(o,{label:"正在识别登录账号",padding:10});const i=await n.getCurrentZhipinRecruiterIdentity(r);return await a.succeed(`已识别账号:${i.username}`),t.logger.info(`Username: ${i.username} (strategy: ${i.strategy}, source: ${i.source})`),{success:!0,username:i.username,usedSelector:"css-fallback"===i.strategy?i.source:void 0,usedStrategy:i.strategy,source:i.source}}catch(e){return await(a?.fail("获取用户名失败")),{success:!1,username:"",error:e instanceof Error?`获取用户名失败:${e.message}`:"获取用户名失败"}}}});import{defineTool as Za}from"@roll-agent/sdk";import{z as Ga}from"zod";var Ya=".candidate-card-wrap",Ka="[data-geek], .geek-item",Ja=`${Ya}, ${Ka}`;function Xa(e){return e.evaluate(e=>document.querySelectorAll(e.primarySelector).length>0?e.primarySelector:e.fallbackSelector,{primarySelector:Ya,fallbackSelector:Ka})}function Qa(e){return e.frame("recommendFrame")??e.frames().find(e=>e.url().includes("recommend"))??e}async function er(e,t=1e4){try{return await e.waitForSelector(Ja,{timeout:t}),!0}catch{return!1}}async function tr(e,t){const n=await Xa(e),a=e.locator(n);if(await a.count()<=t)return{found:!1,cardSelector:n,candidateId:"",name:"",hasGreetButton:!1,error:"索引超出范围"};const r=a.nth(t),o=await r.evaluate(e=>{const t=e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??"",n=e.querySelector(".name")?.textContent?.trim()??"",a=e.querySelector("button.btn.btn-greet");return{candidateId:t,name:n,hasGreetButton:null!==a&&a.offsetWidth>0}});return{found:!0,cardSelector:n,candidateId:o.candidateId,name:o.name,hasGreetButton:o.hasGreetButton}}var nr=Ga.object({containerFound:Ga.boolean(),containerLabel:Ga.string(),scrollTop:Ga.number(),scrollHeight:Ga.number(),clientHeight:Ga.number(),itemCount:Ga.number(),atStart:Ga.boolean(),atEnd:Ga.boolean()}),ar=Ga.object({success:Ga.boolean(),surface:Ga.enum(Nt),direction:Ga.enum(["up","down"]),stepsRequested:Ga.number(),stepsCompleted:Ga.number(),reachedBoundary:Ga.boolean(),before:nr,after:nr,error:Ga.string().optional()});async function rr(e){const t=h(),n=await t.getPage("zhipin");if("chat-list"===e){const e=await Et(t,n),a=await t.getPage("zhipin");return{target:a,session:new Xt(a),ready:e}}if("recommend-list"===e){const e=Qa(n),t=await er(e);return{target:e,session:new Xt(e),ready:t}}try{const t=Lt(e);return await n.waitForSelector(t.itemSelector,{timeout:3e3}),{target:n,session:new Xt(n),ready:!0}}catch{return{target:n,session:new Xt(n),ready:!1}}}var or=Za({name:"zhipin_scroll_view",description:"滚动 BOSS直聘页面内部动态列表容器。用于调试或显式翻页,支持 chat-list、chat-history、recommend-list。",input:Ga.object({surface:Ga.enum(Nt).describe("要滚动的页面区域"),direction:Ga.enum(["up","down"]).optional().describe("滚动方向;不传则使用该区域默认方向"),steps:Ga.number().int().min(1).max(20).default(1).describe("滚动步数"),distance:Ga.number().int().positive().optional().describe("每步滚动像素;不传则按容器高度估算"),settleMs:Ga.number().int().min(0).max(5e3).default(700).describe("每步后等待 DOM 更新的毫秒数")}),output:ar,execute:async(e,t)=>{const n=Lt(e.surface),a=e.direction??n.defaultDirection,r=e.steps??1,o=e.settleMs??700,{target:i,session:s,ready:c}=await rr(e.surface),l=`正在滚动 ${e.surface}`;if(await s.begin(l),await s.highlightSelector(n.highlightSelector,{label:l,padding:8}),!c){await s.fail("列表未加载");const t={containerFound:!1,containerLabel:"",scrollTop:0,scrollHeight:0,clientHeight:0,itemCount:0,atStart:!0,atEnd:!0};return{success:!1,surface:e.surface,direction:a,stepsRequested:r,stepsCompleted:0,reachedBoundary:!0,before:t,after:t,error:"列表未加载"}}try{const c=await Gt(i,n,{direction:a,steps:r,settleMs:o,...void 0!==e.distance?{distance:e.distance}:{}});return await s.succeed(`已滚动 ${c.stepsCompleted}/${c.stepsRequested} 步`),t.logger.info(`Scrolled ${e.surface}: ${c.stepsCompleted}/${c.stepsRequested}, items ${c.before.itemCount} -> ${c.after.itemCount}`),{success:c.success,surface:e.surface,direction:c.direction,stepsRequested:c.stepsRequested,stepsCompleted:c.stepsCompleted,reachedBoundary:c.reachedBoundary,before:c.before,after:c.after}}catch(e){throw await s.fail("滚动失败"),e}}});import{defineTool as ir}from"@roll-agent/sdk";import{z as sr}from"zod";var cr=["不限","男","女"],lr=["不限","刚刚活跃","今日活跃","3日内活跃","本周活跃","本月活跃"],dr=["applied","recommend_not_ready","filter_not_found","requires_vip","age_not_applied","submit_failed","error"],ur=16,mr=50,gr=650,fr=10,pr=.015,hr=["data-roll-recommend-filter-button","data-roll-recommend-filter-option","data-roll-recommend-filter-submit","data-roll-recommend-filter-age-track","data-roll-recommend-filter-age-min-handle","data-roll-recommend-filter-age-max-handle"],wr="active|selected|checked|current|choose|chosen",yr="button, a, label, li, span, div, [role='button'], [role='radio']";async function br(e,t,n,a){a&&(await a.moveToLocator(e,n,{durationMs:100,settleMs:25,target:t}),await a.showClickOnLocator(e,n,{pulseDurationMs:170,target:t}))}function vr(e,t,n={}){return{status:t,requested:e,...void 0!==n.applied?{applied:n.applied}:{},...void 0!==n.filterButtonText?{filterButtonText:n.filterButtonText}:{},...void 0!==n.error?{error:n.error}:{}}}async function xr(e,t=1e4){try{return await e.waitForSelector(`${sn.recommend.filterButton}, .candidate-card-wrap, ${sn.recommend.candidateItem}`,{timeout:t}),!0}catch{return!1}}async function Sr(e){try{const t=await e.locator(sn.recommend.filterButton).first().textContent({timeout:1e3});return t?.replace(/\s+/g," ").trim()}catch{return}}async function Cr(e){return await e.evaluate(()=>{const e=e=>(e??"").replace(/\s+/g," ").trim(),t=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity},n=n=>{const a=Array.from(n.querySelectorAll("button, a, span, div, [role='button']")).filter(e=>t(e));for(const t of a){const n=e(t.textContent);if(/^(取消|不应用|否|关闭|稍后)$/.test(n))return t.click(),!0}return!1},a=Array.from(document.body.querySelectorAll("div, section, aside")).filter(e=>t(e)).sort((e,t)=>{const n=e.getBoundingClientRect(),a=t.getBoundingClientRect();return n.width*n.height-a.width*a.height});for(const t of a){const a=e(t.textContent);if(a.includes("是否应用上次")||a.includes("上次的筛选条件"))return n(t)}return!1})}async function Ar(e,t,n){return await e.evaluate(e=>{const t=e=>(e??"").replace(/\s+/g," ").trim(),n=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity},a=e=>{const t=e.getBoundingClientRect();return t.width*t.height},r=(e,t)=>{let n=e;for(;n&&n!==t.parentElement;){const e=n.tagName.toLowerCase(),t=n.getAttribute("role")??"";if("button"===e||"a"===e||"label"===e||"li"===e||"button"===t||"radio"===t)return n;n=n.parentElement}return e};document.querySelectorAll(`[${e.markerAttribute}]`).forEach(t=>t.removeAttribute(e.markerAttribute));const o=Array.from(document.querySelectorAll(e.panelSelector)).filter(e=>n(e)).sort((e,t)=>a(e)-a(t))[0];if(!o)return!1;const i=Array.from(o.querySelectorAll("div, li, dl, dd, section, ul")).filter(a=>{const r=t(a.textContent);return n(a)&&r.includes(e.rowLabel)&&r.includes(e.optionLabel)}).sort((e,n)=>{const r=a(e)-a(n);return 0!==r?r:t(e.textContent).length-t(n.textContent).length});for(const o of i){const i=Array.from(o.querySelectorAll(e.clickableOptionSelector)).filter(e=>n(e)).filter(n=>t(n.textContent)===e.optionLabel).sort((e,t)=>a(e)-a(t))[0];if(i)return r(i,o).setAttribute(e.markerAttribute,"1"),!0}return!1},{panelSelector:sn.recommend.filterPanel,rowLabel:t,optionLabel:n,markerAttribute:"data-roll-recommend-filter-option",clickableOptionSelector:yr})}async function kr(e,t,n,a,r){if(!await Ar(e,n,a))return!1;try{const n=e.locator('[data-roll-recommend-filter-option="1"]').first();return await br(t,e,n,r),await n.click({timeout:2e3}),await e.waitForTimeout(120),!0}catch{return!1}}async function Ir(e){return await e.evaluate(e=>{const t=e=>(e??"").replace(/\s+/g," ").trim(),n=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity},a=e=>{const t=e.getBoundingClientRect();return t.width*t.height},r=e=>"string"==typeof e.className?e.className:"",o=e=>{const t=r(e),n=e.getAttribute("role")??"";return/slider|range|track|bar/i.test(t)||"slider"===n},i=e=>{const t=e.getBoundingClientRect(),n=r(e);return"slider"===(e.getAttribute("role")??"")||/handle|handler|button|thumb|slider-btn|dot|point|circle|knob/i.test(n)&&t.width<=80&&t.height<=80};for(const t of e.markerAttributes)document.querySelectorAll(`[${t}]`).forEach(e=>e.removeAttribute(t));const s=Array.from(document.querySelectorAll(e.panelSelector)).filter(e=>n(e)).sort((e,t)=>a(e)-a(t))[0];if(!s)return{ok:!1,error:"未找到筛选面板"};const c=Array.from(s.querySelectorAll("div, li, section, dl, dd")).filter(e=>{const a=t(e.textContent);return n(e)&&a.includes("年龄")&&Array.from(e.querySelectorAll("*")).some(e=>o(e)||i(e))}).sort((e,n)=>{const r=a(e)-a(n);return 0!==r?r:t(e.textContent).length-t(n.textContent).length})[0];if(!c)return{ok:!1,error:"未找到年龄滑块"};const l=Array.from(c.querySelectorAll(".vue-slider-dot")).filter(e=>n(e)).sort((e,t)=>e.getBoundingClientRect().left-t.getBoundingClientRect().left),d=Array.from(c.querySelectorAll("*")).filter(e=>n(e)&&i(e)).sort((e,t)=>e.getBoundingClientRect().left-t.getBoundingClientRect().left),u=l.length>=2?l:d;if(u.length<2)return{ok:!1,error:"未找到年龄滑块双手柄"};const m=u[0],g=u[u.length-1];if(!m||!g)return{ok:!1,error:"未找到年龄滑块双手柄"};const f=m.getBoundingClientRect(),p=g.getBoundingClientRect(),h=Math.max(40,p.left-f.left),w=Array.from(c.querySelectorAll(".vue-slider-rail, .vue-slider")).filter(e=>{if(!n(e))return!1;const t=e.getBoundingClientRect();return t.width>=h&&t.height<=80}).sort((e,t)=>{const n=r(e),a=r(t);if(/vue-slider-rail/.test(n)&&!/vue-slider-rail/.test(a))return-1;if(!/vue-slider-rail/.test(n)&&/vue-slider-rail/.test(a))return 1;const o=e.getBoundingClientRect(),i=t.getBoundingClientRect(),s=o.height-i.height;return 0!==s?s:i.width-o.width}),y=Array.from(c.querySelectorAll("*")).filter(e=>{if(!n(e)||!o(e))return!1;const t=e.getBoundingClientRect();return t.width>=Math.max(80,h)&&t.height<=80&&t.left<=f.left+f.width&&t.right>=p.right-p.width}).sort((e,t)=>{const n=e.getBoundingClientRect(),a=t.getBoundingClientRect(),r=n.height-a.height;return 0!==r?r:a.width-n.width}),b=Array.from(c.querySelectorAll("*")).filter(e=>{if(!n(e)||!e.contains(m)||!e.contains(g))return!1;const t=e.getBoundingClientRect();return t.width>=h&&t.height<=140}).sort((e,t)=>{const n=a(e)-a(t);return 0!==n?n:e.getBoundingClientRect().height-t.getBoundingClientRect().height}),v=w[0]??y[0]??b[0];return v?(v.setAttribute("data-roll-recommend-filter-age-track","1"),m.setAttribute("data-roll-recommend-filter-age-min-handle","1"),g.setAttribute("data-roll-recommend-filter-age-max-handle","1"),{ok:!0,current:(e=>{const n=t(e),a=n.includes("年龄")?n.slice(n.indexOf("年龄")+2):n,r=Array.from(a.matchAll(/\d+/g),e=>Number.parseInt(e[0],10)).filter(e=>Number.isInteger(e)),o=r[0],i=a.includes("不限")?void 0:r[1];return{...void 0!==o?{ageMin:o}:{},...void 0!==i?{ageMax:i}:{}}})(t(c.textContent))}):{ok:!1,error:"未找到年龄滑块轨道"}},{panelSelector:sn.recommend.filterPanel,markerAttributes:hr})}async function Mr(e){return await e.evaluate(e=>{const t=e=>(e??"").replace(/\s+/g," ").trim(),n=e=>{if(e.includes("不限"))return;const t=e.match(/\d+/);return t?Number.parseInt(t[0],10):void 0},a=(e,t)=>{const n=e.style.left;if(n.endsWith("%")){const e=Number.parseFloat(n);if(Number.isFinite(e))return Math.max(0,Math.min(1,e/100))}const a=e.getBoundingClientRect(),r=t.getBoundingClientRect();if(!(r.width<=0))return Math.max(0,Math.min(1,(a.left+a.width/2-r.left)/r.width))},r=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity},o=e=>{const t=e.getBoundingClientRect();return t.width*t.height},i=e=>{const t=(e=>"string"==typeof e.className?e.className:"")(e),n=e.getAttribute("role")??"";return/slider|range|track|bar/i.test(t)||"slider"===n},s=Array.from(document.querySelectorAll(e)).filter(e=>r(e)).sort((e,t)=>o(e)-o(t))[0];if(!s)return{};const c=Array.from(s.querySelectorAll("div, li, section, dl, dd")).filter(e=>{const n=t(e.textContent);return r(e)&&n.includes("年龄")&&(/\d+|不限/.test(n)||Array.from(e.querySelectorAll("*")).some(i))}).sort((e,n)=>{const a=o(e)-o(n);return 0!==a?a:t(e.textContent).length-t(n.textContent).length})[0];if(!c)return{};const l=Array.from(c.querySelectorAll(".vue-slider-dot")).filter(e=>r(e)).sort((e,t)=>e.getBoundingClientRect().left-t.getBoundingClientRect().left),d=c.querySelector(".vue-slider-rail, .vue-slider");if(l.length>=2){const e=l[0],r=l[l.length-1],o=t(e?.querySelector(".vue-slider-dot-tooltip-text")?.textContent),i=t(r?.querySelector(".vue-slider-dot-tooltip-text")?.textContent),s=n(o),c=n(i);return{...void 0!==s?{ageMin:s}:{},...void 0!==c?{ageMax:c}:{},...d&&e?{minRatio:a(e,d)}:{},...d&&r?{maxRatio:a(r,d)}:{}}}return(e=>{const n=t(e),a=n.includes("年龄")?n.slice(n.indexOf("年龄")+2):n,r=Array.from(a.matchAll(/\d+/g),e=>Number.parseInt(e[0],10)).filter(e=>Number.isInteger(e)),o=r[0],i=a.includes("不限")?void 0:r[1];return{...void 0!==o?{ageMin:o}:{},...void 0!==i?{ageMax:i}:{}}})(t(c.textContent))},sn.recommend.filterPanel)}async function Rr(e,t,n,a,r){if(!(await Ir(e)).ok)return!1;const o=e.locator('[data-roll-recommend-filter-age-track="1"]').first(),i="min"===n?'[data-roll-recommend-filter-age-min-handle="1"]':'[data-roll-recommend-filter-age-max-handle="1"]',s=e.locator(i).first(),c=await o.boundingBox();if(!c)return!1;const l=Math.max(0,Math.min(c.width,c.width*a)),d=Math.max(1,c.height/2);try{return r&&await r.moveToLocator(t,s,{durationMs:90,settleMs:20,target:e}),await s.dragTo(o,{force:!0,targetPosition:{x:l,y:d},timeout:2e3}),await e.waitForTimeout(gr),!0}catch{return!1}}function Br(e){return Math.max(0,Math.min(1,(e-ur)/(mr-ur)))}function Er(e,t,n){return Math.max(t,Math.min(n,e))}async function Pr(e,t,n,a,r){const o=await Mr(e),i=o.minRatio??0,s=o.maxRatio??1;let c="min"===n?0:Math.min(1,i+pr),l="min"===n?Math.max(0,s-pr):1,d=Er(Br(a),c,l);for(let o=0;o<fr;o+=1){if(!await Rr(e,t,n,d,r))return!1;const o=await Mr(e),i="min"===n?o.ageMin:o.ageMax;if(i===a)return!0;void 0===i?"max"===n?l=d:c=d:i<a?c=d:l=d;const s=(c+l)/2;if(Math.abs(s-d)<.001)break;d=Er(s,c,l)}const u=await Mr(e);return"min"===n?u.ageMin===a:u.ageMax===a}function $r(e,t,n){return e.ageMin===t&&e.ageMax===n}async function _r(e,t,n,a){const r=n.ageMin??ur,o=n.ageMax,i=await Ir(e);if(!i.ok)return{success:!1,error:i.error};if(!await Rr(e,t,"max",1,a))return{success:!1,error:"年龄上限无法重置为不限"};if(!await Pr(e,t,"min",r,a))return{success:!1,error:`年龄下限无法设置为 ${r}`};if(void 0===o){if(!await Rr(e,t,"max",1,a))return{success:!1,error:"年龄上限无法设置为不限"}}else if(!await Pr(e,t,"max",o,a))return{success:!1,error:`年龄上限无法设置为 ${o}`};const s=await Mr(e);if(!$r(s,r,o)){const e=void 0===s.ageMax?"不限":String(s.ageMax);return{success:!1,error:`年龄筛选未精确生效,当前为 ${s.ageMin??"未知"}-${e}`}}return{success:!0,state:s}}async function qr(e){return await e.evaluate(()=>{const e=e=>(e??"").replace(/\s+/g," ").trim(),t=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity},n=/(购买VIP|VIP账号|开通VIP|开启VIP|专享筛选特权|扫码支付|立即开通|支付金额)/;if(!Array.from(document.body.querySelectorAll("div, section, aside")).filter(a=>t(a)&&n.test(e(a.textContent))).sort((e,t)=>{const n=e.getBoundingClientRect(),a=t.getBoundingClientRect();return n.width*n.height-a.width*a.height})[0])return!1;const a=Array.from(document.querySelectorAll(".boss-dialog__close, .dialog-close, .close-btn, [class*='close'], button, span, i")).filter(e=>t(e));for(const t of a){const n=e(t.textContent),a="string"==typeof t.className?t.className:"";if("×"===n||"关闭"===n||/close/i.test(a)){t.click();break}}return!0})}async function Nr(e,t){return!!await qr(t)||!(t===e||!await qr(e))}async function Tr(e,t,n){return await e.evaluate(e=>{const t=e=>(e??"").replace(/\s+/g," ").trim(),n=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity},a=e=>{const t=e.getBoundingClientRect();return t.width*t.height},r=Array.from(document.querySelectorAll(e.panelSelector)).filter(e=>n(e)).sort((e,t)=>a(e)-a(t))[0];if(!r)return e.fallback;const o=Array.from(r.querySelectorAll("div, li, dl, dd, section, ul")).filter(a=>{const r=t(a.textContent);return n(a)&&r.includes(e.rowLabel)}).sort((e,n)=>{const r=a(e)-a(n);return 0!==r?r:t(e.textContent).length-t(n.textContent).length})[0];if(!o)return e.fallback;return Array.from(o.querySelectorAll(e.clickableOptionSelector)).filter(t=>n(t)&&(t=>{const n="string"==typeof t.className?t.className:"";return new RegExp(e.selectedClassPattern,"i").test(n)||"true"===t.getAttribute("aria-checked")||"true"===t.getAttribute("aria-selected")})(t)).map(e=>t(e.textContent)).find(t=>""!==t&&t!==e.rowLabel)??e.fallback},{panelSelector:sn.recommend.filterPanel,rowLabel:t,fallback:n,selectedClassPattern:wr,clickableOptionSelector:yr})}async function Lr(e,t,n){return{...void 0!==n.ageMin?{ageMin:n.ageMin}:{},...void 0!==n.ageMax?{ageMax:n.ageMax}:{},gender:await Tr(e,"性别",t.gender),activity:await Tr(e,"活跃度",t.activity)}}async function zr(e,t,n){if(!await e.evaluate(e=>{const t=e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity};document.querySelectorAll(`[${e.markerAttribute}]`).forEach(t=>t.removeAttribute(e.markerAttribute));const n=Array.from(document.querySelectorAll(e.panelSelector)).filter(e=>t(e))[0];if(!n)return!1;const a=Array.from(n.querySelectorAll("button, a, span, div, [role='button']")).filter(e=>t(e)).find(e=>"确定"===(e.textContent??"").replace(/\s+/g," ").trim());return!!a&&(a.setAttribute(e.markerAttribute,"1"),!0)},{panelSelector:sn.recommend.filterPanel,markerAttribute:"data-roll-recommend-filter-submit"}))return!1;try{const a=e.locator('[data-roll-recommend-filter-submit="1"]').first();return await br(t,e,a,n),await a.click({timeout:2e3}),await e.waitForSelector(sn.recommend.filterPanel,{state:"hidden",timeout:4e3}),await e.waitForTimeout(600),!0}catch{return!1}}async function Fr(e,t,n){const a=async()=>await e.evaluate(e=>{const t=e=>"string"==typeof e.className?e.className:"",n=e=>{const n=t(e),a=e.parentElement?t(e.parentElement):"",r=null!==e.closest(".recommend-filter, .filter-label-wrap, .filter-wrap")?"recommend-filter":"";let o=0;for(const e of[n,a,r])/recommend-filter/.test(e)?o+=3:/filter-label/.test(e)?o+=2:/filter/.test(e)&&(o+=1);return o};for(const t of e.markerAttributes)document.querySelectorAll(`[${t}]`).forEach(e=>e.removeAttribute(t));const a=[...Array.from(document.querySelectorAll(e.filterButtonSelector)),...Array.from(document.querySelectorAll("button, a, span, div, [role='button']")).filter(e=>/^筛选(?:·\d+)?$/.test((e.textContent??"").replace(/\s+/g," ").trim()))].filter(e=>(e=>{const t=e.getBoundingClientRect(),n=window.getComputedStyle(e);return t.width>0&&t.height>0&&"none"!==n.display&&"hidden"!==n.visibility&&"0"!==n.opacity})(e)).sort((e,t)=>{const a=n(t)-n(e);if(0!==a)return a;const r=e.getBoundingClientRect(),o=t.getBoundingClientRect();return r.width*r.height-o.width*o.height})[0];return!!a&&(a.setAttribute(e.markerAttribute,"1"),!0)},{filterButtonSelector:sn.recommend.filterButton,markerAttribute:"data-roll-recommend-filter-button",markerAttributes:hr}),r=async()=>{try{const a=e.locator(sn.recommend.filterButton).first();if(await a.count()>0&&await a.isVisible())return await a.scrollIntoViewIfNeeded(),await br(t,e,a,n),await a.click({timeout:2e3}),!0}catch{}if(!await a())return!1;const r=e.locator('[data-roll-recommend-filter-button="1"]').first();return await br(t,e,r,n),await r.click({timeout:2e3}),!0};if(await(async()=>{try{const t=e.locator(sn.recommend.filterPanel).first();return await t.count()>0&&await t.isVisible()}catch{return!1}})())return!0;for(let t=0;t<3;t+=1){await Cr(e);try{if(await r())return await e.waitForSelector(sn.recommend.filterPanel,{state:"visible",timeout:4e3}),await Cr(e),!0}catch{}await e.waitForTimeout(300)}return!1}async function Or(e,t,n,a){const r=await xr(t,3e3);if(!await Fr(t,e,a))return vr(n,r?"filter_not_found":"recommend_not_ready",{error:r?"未找到或无法打开筛选按钮":"推荐牛人页未就绪"});if(await Nr(e,t))return vr(n,"requires_vip",{error:"筛选条件触发 VIP 弹窗"});if(!await kr(t,e,"性别",n.gender,a))return vr(n,"filter_not_found",{error:`未找到性别筛选项:${n.gender}`});if(!await kr(t,e,"活跃度",n.activity,a))return vr(n,"filter_not_found",{error:`未找到活跃度筛选项:${n.activity}`});const o=await _r(t,e,n,a);if(!o.success)return vr(n,"age_not_applied",{error:o.error});if(await Nr(e,t))return vr(n,"requires_vip",{error:"年龄筛选触发 VIP 弹窗"});const i=await Lr(t,n,o.state);if(!await zr(t,e,a))return vr(n,"submit_failed",{applied:i,error:"筛选确认失败"});const s=await Sr(t);return vr(n,"applied",{applied:i,...void 0!==s?{filterButtonText:s}:{}})}async function jr(e,t,n,a){const r=await Or(e,t,n,a);if(t!==e&&("filter_not_found"===r.status||"recommend_not_ready"===r.status)){const t=await Or(e,e,n,a);if("filter_not_found"!==t.status&&"recommend_not_ready"!==t.status)return t}return r}var Vr,Dr=sr.object({ageMin:sr.number().int().min(16).optional(),ageMax:sr.number().int().min(16).optional(),gender:sr.enum(cr),activity:sr.enum(lr)}),Ur=sr.object({ageMin:sr.number().optional(),ageMax:sr.number().optional(),gender:sr.string(),activity:sr.string()}),Hr=sr.object({success:sr.boolean(),status:sr.enum(dr),requested:Dr,applied:Ur.optional(),filterButtonText:sr.string().optional(),error:sr.string().optional()}),Wr=sr.object({ageMin:sr.number().int().min(16).optional().describe("年龄下限;未传则重置为 16"),ageMax:sr.number().int().min(16).optional().describe("年龄上限;未传则重置为不限"),gender:sr.enum(cr).default("不限").describe("性别筛选,只支持:不限、男、女"),activity:sr.enum(lr).default("不限").describe("活跃度[单选],只支持:不限、刚刚活跃、今日活跃、3日内活跃、本周活跃、本月活跃")}).refine(e=>void 0===e.ageMin||void 0===e.ageMax||e.ageMin<=e.ageMax,{path:["ageMax"],message:"ageMax must be greater than or equal to ageMin"});function Zr(){return{getContextManager:h,getRecommendTarget:Qa,waitForRecommendFilterSurface:xr,applyRecommendFilter:jr,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,createVisualActivitySession:e=>new Xt(e),...Vr}}function Gr(e){const t=e.gender??"不限",n=e.activity??"不限";return{...void 0!==e.ageMin?{ageMin:e.ageMin}:{},...void 0!==e.ageMax?{ageMax:e.ageMax}:{},gender:t,activity:n}}function Yr(e){return{success:"applied"===e.status,status:e.status,requested:e.requested,...void 0!==e.applied?{applied:e.applied}:{},...void 0!==e.filterButtonText?{filterButtonText:e.filterButtonText}:{},...void 0!==e.error?{error:e.error}:{}}}var Kr=ir({name:"zhipin_filter_recommend_candidates",description:"在 BOSS「推荐牛人」页打开筛选面板,只设置年龄、性别、活跃度[单选] 三个维度并提交。",input:Wr,output:Hr,execute:async(e,t)=>{const n=Zr(),a=Gr(e);t.logger.info(`Filtering Boss recommend candidates: gender=${a.gender}, activity=${a.activity}, ageMin=${a.ageMin??"16"}, ageMax=${a.ageMax??"不限"}`);const r=n.getContextManager(),o=await r.getPage("zhipin");await o.bringToFront().catch(()=>{});let i=n.getRecommendTarget(o);const s=n.createVisualActivitySession(i);await s.begin("正在打开推荐筛选");let c=await n.waitForRecommendFilterSurface(i);if(c||(i=n.getRecommendTarget(o),await s.retarget(i),c=await n.waitForRecommendFilterSurface(i,2500)),!c)return await s.fail("推荐牛人页未就绪"),Yr({status:"recommend_not_ready",requested:a,error:"推荐牛人页未就绪"});await s.retarget(i),await s.begin("正在设置推荐筛选"),await s.highlightSelector(sn.recommend.filterButton,{label:"正在设置推荐筛选",padding:8});const l=await n.applyRecommendFilter(o,i,a,{moveToLocator:n.moveVisualCursorToLocator,showClickOnLocator:n.showVisualClickOnLocator});return"applied"===l.status?await s.succeed("已应用推荐筛选"):await s.fail(l.error??l.status),Yr(l)}});import{defineTool as Jr}from"@roll-agent/sdk";import{z as Xr}from"zod";var Qr,eo=Xr.object({index:Xr.number(),candidateId:Xr.string(),name:Xr.string(),age:Xr.string(),experience:Xr.string(),education:Xr.string(),workStatus:Xr.string(),company:Xr.string(),currentPosition:Xr.string(),expectedLocation:Xr.string(),expectedPosition:Xr.string(),expectedSalary:Xr.string(),tags:Xr.array(Xr.string()),buttonText:Xr.string()}),to=Xr.object({containerLabel:Xr.string(),stepsRequested:Xr.number(),stepsCompleted:Xr.number(),reachedBoundary:Xr.boolean(),stopReason:Xr.enum(zt),uniqueCount:Xr.number(),duplicateCount:Xr.number(),noNewRounds:Xr.number(),beforeItemCount:Xr.number(),afterItemCount:Xr.number(),beforeScrollHeight:Xr.number(),afterScrollHeight:Xr.number()}),no=Xr.object({success:Xr.boolean(),candidates:Xr.array(eo),total:Xr.number(),scrollStats:to.optional(),error:Xr.string().optional()});function ao(){return{getContextManager:h,getRecommendTarget:Qa,waitForRecommendList:er,createVisualActivitySession:e=>new Xt(e),...Qr}}async function ro(e){return await e.evaluate(()=>{let e=Array.from(document.querySelectorAll(".candidate-card-wrap"));0===e.length&&(e=Array.from(document.querySelectorAll("[data-geek], .geek-item")));const t=[];return e.forEach((e,n)=>{const a=e.getAttribute("data-geek")??e.querySelector("[data-geek]")?.getAttribute("data-geek")??"",r=e.querySelector(".name")?.textContent?.trim()??"";let o="",i="",s="",c="";const l=e.querySelector(".base-info.join-text-wrap, .base-info");if(l){const e=[];if(l.querySelectorAll(":scope > *").forEach(t=>{const n=t.textContent?.trim();n&&e.push(n)}),e.length<=1&&(e.length=0,l.childNodes.forEach(t=>{if(t.nodeType===Node.TEXT_NODE){const n=t.textContent?.trim();n&&e.push(n)}})),e.length<=1){const t=l.textContent?.trim()??"";e.length=0,t.split(/[丨·|]/).forEach(t=>{const n=t.trim();n&&e.push(n)})}for(const t of e)!o&&t.includes("岁")?o=t:!i&&(t.includes("年")||t.includes("应届")||t.includes("在校"))?i=t:!s&&/(初中|高中|中专|中技|大专|本科|硕士|博士)/.test(t)?s=t:!c&&/(在职|离职|在校)/.test(t)&&(c=t)}const d=e.querySelector(".timeline-wrap.work-exps .content.join-text-wrap")??e.querySelector(".timeline-wrap.work-exps .content"),u=(d?.textContent?.trim()??"").split("·").map(e=>e.trim()),m=u[0]??"",g=u[1]??"";let f="",p="";const h=e.querySelector(".row-flex:not(.geek-desc)");if(h){const e=h.querySelector(".label"),t=h.querySelector(".content"),n=e?.textContent??"";if((n.includes("期望")||n.includes("最近关注"))&&t){const e=(t.textContent?.trim()??"").split("·").map(e=>e.trim());f=e[0]??"",p=e[1]??""}}if(!f){const t=e.querySelector(".timeline-wrap.expect .content.join-text-wrap")??e.querySelector(".timeline-wrap.expect .content");if(t){const e=(t.textContent?.trim()??"").split("·").map(e=>e.trim());f=e[0]??"",p=e[1]??""}}const w=e.querySelector(".salary-wrap")?.textContent?.trim()??"",y=[];e.querySelectorAll(".tags-wrap .tag-item, .tags-wrap .tag, .tags-wrap span").forEach(e=>{const t=e.textContent?.trim();t&&y.push(t)});const b=e.querySelector("button.btn.btn-greet")?.textContent?.trim()??"";t.push({index:n,candidateId:a,name:r,age:o,experience:i,education:s,workStatus:c,company:m,currentPosition:g,expectedLocation:f,expectedPosition:p,expectedSalary:w,tags:y,buttonText:b})}),t})}function oo(e){return e.candidateId.length>0?e.candidateId:0!==e.name.length?[e.name,e.age,e.experience,e.expectedLocation,e.expectedPosition,e.expectedSalary].join("|"):void 0}var io=Jr({name:"zhipin_get_candidate_list",description:"获取推荐列表页的候选人卡片信息",input:Xr.object({maxResults:Xr.number().optional().describe("最多返回条数"),autoScroll:Xr.boolean().default(!0).describe("是否自动向下滚动动态列表并合并采集结果"),maxScrolls:Xr.number().int().min(0).max(50).default(4).describe("自动滚动的最大步数")}),output:no,execute:async(e,t)=>{t.logger.info("Getting candidate list from recommend page");const n=ao(),a=n.getContextManager(),r=await a.getPage("zhipin");let o=n.getRecommendTarget(r);const i=n.createVisualActivitySession(o);await i.begin("正在打开推荐列表");const s=await n.waitForRecommendList(o);if(o=n.getRecommendTarget(r),await i.retarget(o),!s)return await i.fail("推荐列表未加载"),{success:!1,candidates:[],total:0,error:"推荐列表未加载"};try{const n="正在读取推荐列表";await i.begin(n),await i.highlightSelector(".candidate-card-wrap, [data-geek], .geek-item",{label:n,padding:8});const a=e.autoScroll??!0,r=e.maxScrolls??4;let s,c;if(a&&r>0){const t=await Yt(o,Lt("recommend-list"),()=>ro(o),oo,{direction:"down",steps:r,settleMs:900,maxNoNewRounds:4,boundaryLoadRetries:4,boundarySettleMs:1200,...void 0!==e.maxResults?{targetCount:e.maxResults}:{}});c=[...t.items],s={containerLabel:t.after.containerLabel,stepsRequested:t.stepsRequested,stepsCompleted:t.stepsCompleted,reachedBoundary:t.reachedBoundary,stopReason:t.stopReason,uniqueCount:t.uniqueCount,duplicateCount:t.duplicateCount,noNewRounds:t.noNewRounds,beforeItemCount:t.before.itemCount,afterItemCount:t.after.itemCount,beforeScrollHeight:t.before.scrollHeight,afterScrollHeight:t.after.scrollHeight}}else c=await ro(o);const l=void 0!==e.maxResults?c.slice(0,e.maxResults):c;return await i.succeed(`已读取 ${l.length} 位候选人`),t.logger.info(`Found ${l.length} candidates in recommend list`+(s?`, scroll stop: ${s.stopReason}`:"")),{success:!0,candidates:l,total:l.length,...void 0!==s?{scrollStats:s}:{}}}catch(e){throw await i.fail("读取推荐列表失败"),e}}});import{defineTool as so}from"@roll-agent/sdk";import{BrowserPageInfoSchema as co}from"@roll-agent/browser";import{z as lo}from"zod";var uo,mo=lo.object({success:lo.boolean(),alreadyOnRecommend:lo.boolean(),usedSidebarClick:lo.boolean(),recommendReady:lo.boolean(),page:co.optional(),error:lo.string().optional()});function go(){return{getContextManager:h,getRecommendTarget:Qa,findZhipinSidebarSectionLink:mn,isZhipinRecommendSurfaceOpen:gn,waitForZhipinRecommendSurface:pn,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,randomDelay:ot,toAttachedPageInfo:Ce,createVisualActivitySession:e=>new Xt(e),...uo}}async function fo(e,t,n){return await t.toAttachedPageInfo(e,n)}async function po(e,t,n,a,r,o){return await n.fail(r),{success:!1,...o,page:await fo(e,t,a),error:r}}async function ho(e,t,n,a){await n.scrollIntoViewIfNeeded(),await t.moveVisualCursorToLocator(e,n,{durationMs:110,settleMs:30}),await n.hover(),await t.randomDelay(e,100,180),await t.showVisualClickOnLocator(e,n,{pulseDurationMs:180}),await n.click(),a.info("Clicked Boss sidebar nav: 推荐牛人")}var wo=so({name:"zhipin_open_recommend_page",description:"通过点击 Boss 左侧导航切换到「推荐牛人」页,避免让编排器依赖站内 URL 猜测。",input:lo.object({}),output:mo,execute:async(e,t)=>{const n=go(),a=n.getContextManager();t.logger.info("Opening Boss recommend page via sidebar navigation");const r=await a.getPage("zhipin");await r.bringToFront().catch(()=>{});const o=n.createVisualActivitySession(r),i="正在切换到推荐牛人页";if(await o.begin(i),await o.highlightSelector(sn.nav.sidebar,{label:i,padding:10}),n.isZhipinRecommendSurfaceOpen(r))return await o.retarget(n.getRecommendTarget(r)),await o.succeed("已在推荐牛人页"),{success:!0,alreadyOnRecommend:!0,usedSidebarClick:!1,recommendReady:!0,page:await fo(a,n,r)};const s=await n.findZhipinSidebarSectionLink(r,"recommend");if(!s)return await po(a,n,o,r,"未找到推荐牛人导航",{alreadyOnRecommend:!1,usedSidebarClick:!1,recommendReady:!1});try{await ho(r,n,s,t.logger)}catch(e){return await po(a,n,o,r,e instanceof Error?e.message:"点击推荐牛人导航失败",{alreadyOnRecommend:!1,usedSidebarClick:!0,recommendReady:!1})}const c=await n.waitForZhipinRecommendSurface(r);return await o.retarget(n.getRecommendTarget(r)),c?(await o.succeed("已切换到推荐牛人页"),{success:!0,alreadyOnRecommend:!1,usedSidebarClick:!0,recommendReady:!0,page:await fo(a,n,r)}):await po(a,n,o,r,"推荐牛人页未就绪",{alreadyOnRecommend:!1,usedSidebarClick:!0,recommendReady:!1})}});import{defineTool as yo}from"@roll-agent/sdk";import{z as bo}from"zod";var vo,xo=bo.object({index:bo.number(),candidateName:bo.string(),candidateId:bo.string(),success:bo.boolean(),error:bo.string().optional()}),So=bo.object({success:bo.boolean(),results:bo.array(xo),summary:bo.object({total:bo.number(),succeeded:bo.number(),failed:bo.number()})});function Co(){return{getContextManager:h,getRecommendTarget:Qa,waitForRecommendList:er,inspectRecommendCard:tr,moveVisualCursorToLocator:Z,showVisualClickOnLocator:G,humanDelay:it,shouldAddRandomBehavior:ct,performRandomScroll:st,createVisualActivitySession:e=>new Xt(e),...vo}}var Ao=yo({name:"zhipin_say_hello",description:"在推荐列表页对候选人点击「打招呼」按钮(支持批量)",input:bo.object({indices:bo.array(bo.number()).describe("要打招呼的候选人索引列表")}),output:So,execute:async(e,t)=>{t.logger.info(`Saying hello to ${e.indices.length} candidates`);const n=Co(),a=n.getContextManager(),r=await a.getPage("zhipin");let o=n.getRecommendTarget(r);const i=n.createVisualActivitySession(o),s=e.indices.length>1?"正在批量打招呼":"正在打招呼";await i.begin("正在打开推荐列表");const c=await n.waitForRecommendList(o);if(o=n.getRecommendTarget(r),await i.retarget(o),!c){await i.fail("推荐列表未加载");const t=e.indices.map(e=>({index:e,candidateName:"",candidateId:"",success:!1,error:"推荐列表未加载"}));return{success:!1,results:t,summary:{total:t.length,succeeded:0,failed:t.length}}}await i.begin(s),await i.highlightSelector(".candidate-card-wrap, [data-geek], .geek-item",{label:s,padding:8});const l=[];for(const t of e.indices)try{const e=await n.inspectRecommendCard(o,t);if(e.found)if(e.hasGreetButton){const a=o.locator(e.cardSelector).nth(t),s=a.locator("button.btn.btn-greet").first();await i.highlightLocator(a,{label:`正在定位第 ${t+1} 位候选人`,padding:10}),await s.scrollIntoViewIfNeeded(),await n.moveVisualCursorToLocator(r,s,{durationMs:90,settleMs:20,target:o}),await s.hover(),await n.showVisualClickOnLocator(r,s,{pulseDurationMs:160,target:o}),await s.click(),l.push({index:t,candidateName:e.name,candidateId:e.candidateId,success:!0})}else l.push({index:t,candidateName:e.name,candidateId:e.candidateId,success:!1,error:"未找到打招呼按钮"});else l.push({index:t,candidateName:"",candidateId:"",success:!1,...void 0!==e.error?{error:e.error}:{}});await n.humanDelay(r),n.shouldAddRandomBehavior(.3)&&await n.performRandomScroll(r)}catch(e){l.push({index:t,candidateName:"",candidateId:"",success:!1,error:e instanceof Error?e.message:String(e)})}const d={total:l.length,succeeded:l.filter(e=>e.success).length,failed:l.filter(e=>!e.success).length};return 0===d.failed?await i.succeed(`已完成 ${d.succeeded}/${d.total} 位候选人`):await i.fail(`已完成 ${d.succeeded}/${d.total} 位候选人`),t.logger.info(`Say hello: ${d.succeeded}/${d.total} succeeded`),{success:0===d.failed,results:l,summary:d}}});import{defineTool as ko}from"@roll-agent/sdk";import{z as Io}from"zod";var Mo=Io.object({success:Io.boolean(),candidateName:Io.string(),candidateId:Io.string(),error:Io.string().optional()}),Ro=ko({name:"zhipin_open_resume",description:"在推荐列表页点击候选人卡片打开简历详情弹窗",input:Io.object({index:Io.number().describe("候选人在列表中的索引")}),output:Mo,execute:async(e,t)=>{t.logger.info(`Opening resume for candidate at index ${e.index}`);const n=h(),a=await n.getPage("zhipin"),r=Qa(a);if(!await er(r))return{success:!1,candidateName:"",candidateId:"",error:"推荐列表未加载"};const o=await tr(r,e.index);if(!o.found)return{success:!1,candidateName:"",candidateId:"",error:o.error??`索引 ${e.index} 超出范围`};const i=r.locator(o.cardSelector).nth(e.index),s=await i.locator("[data-geek], .card-inner, .geek-item").count()>0?i.locator("[data-geek], .card-inner, .geek-item").first():i;return await s.scrollIntoViewIfNeeded(),await Z(a,s,{target:r}),await s.hover(),await ot(a,200,400),await G(a,s,{target:r}),await s.click(),await ot(a,1e3,2e3),t.logger.info(`Opened resume for ${o.name}`),{success:!0,candidateName:o.name,candidateId:o.candidateId}}});import{defineTool as Bo}from"@roll-agent/sdk";import{z as Eo}from"zod";var Po=Eo.object({x:Eo.number(),y:Eo.number(),width:Eo.number(),height:Eo.number()}),$o=Eo.object({success:Eo.boolean(),screenshotArea:Po.optional(),canvasInfo:Eo.object({width:Eo.number(),height:Eo.number()}).optional(),error:Eo.string().optional()}),_o=Bo({name:"zhipin_locate_resume_canvas",description:"定位简历详情中嵌套 iframe 内的 canvas 元素坐标(用于截图)",input:Eo.object({}),output:$o,execute:async(e,t)=>{t.logger.info("Locating resume canvas in nested iframes");const n=h(),a=await n.getPage("zhipin");try{const e=a.frame("recommendFrame")??a.frames().find(e=>e.url().includes("recommend"));if(!e)return{success:!1,error:"未找到推荐页 iframe"};const n=await e.$('iframe[src*="c-resume"]');if(!n)return{success:!1,error:"未找到简历 iframe"};const r=await n.contentFrame();if(!r)return{success:!1,error:"无法访问简历 iframe 内容"};try{await r.waitForSelector("canvas#resume, div#resume canvas",{timeout:5e3})}catch{return{success:!1,error:"简历 canvas 未加载"}}const o=await r.evaluate(()=>{const e=document.querySelector("canvas#resume, div#resume canvas");if(!e)return null;const t=e.getBoundingClientRect();return{width:e.width,height:e.height,clientWidth:t.width,clientHeight:t.height,x:t.x,y:t.y}});if(!o)return{success:!1,error:"无法获取 canvas 信息"};const i=await a.evaluate(()=>{const e=document.querySelector("#recommendFrame");if(!e)return null;const t=e.getBoundingClientRect();return{x:t.x,y:t.y}}),s=await e.evaluate(()=>{const e=document.querySelector('iframe[src*="c-resume"]');if(!e)return null;const t=e.getBoundingClientRect();return{x:t.x,y:t.y}}),c=(i?.x??0)+(s?.x??0),l=(i?.y??0)+(s?.y??0);return t.logger.info(`Canvas located at (${c+o.x}, ${l+o.y})`),{success:!0,screenshotArea:{x:Math.round(c+o.x),y:Math.round(l+o.y),width:Math.round(o.clientWidth),height:Math.round(o.clientHeight)},canvasInfo:{width:o.width,height:o.height}}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}});import{defineTool as qo}from"@roll-agent/sdk";import{z as No}from"zod";var To=No.object({success:No.boolean(),closed:No.boolean(),error:No.string().optional()}),Lo=[".recommendV2 .boss-popup__close",".dialog-lib-resume .boss-popup__close",".boss-dialog .boss-popup__close",".boss-popup__close",".close-btn",".dialog-close"],zo=[".boss-popup__close",".close-btn",".dialog-close",".modal-close"],Fo=qo({name:"zhipin_close_resume",description:"关闭简历详情弹窗",input:No.object({}),output:To,execute:async(e,t)=>{t.logger.info("Closing resume detail modal");const n=h(),a=await n.getPage("zhipin"),r=a.frame("recommendFrame")??a.frames().find(e=>e.url().includes("recommend"));if(!await(async()=>{if(r)for(const e of Lo){const t=r.locator(e).first();if(await t.isVisible())return await Z(a,t,{target:r}),await G(a,t,{target:r}),await t.click(),!0}for(const e of zo){const t=a.locator(e).first();if(await t.isVisible())return await Z(a,t),await G(a,t),await t.click(),!0}return!1})())return{success:!1,closed:!1,error:"未找到关闭按钮"};let o=!1;for(let e=0;e<5;e++){await a.waitForTimeout(300);const e=r?await r.$(".boss-popup__wrapper, .dialog-lib-resume, .boss-dialog"):await a.$(".boss-popup__wrapper");if(!e||!await e.isVisible()){o=!0;break}}return t.logger.info(o?"Resume modal closed and verified":"Resume modal close unverified"),{success:!0,closed:!0}}});import{defineTool as Oo}from"@roll-agent/sdk";import{z as jo}from"zod";import{waitForSelector as Vo}from"@roll-agent/browser";var Do={login:{qrCode:".login-qr img, .qr-code img",loginSuccess:".user-info, .header-user"},messageList:{container:".chat-list, .msg-list",item:".chat-item, .msg-item",candidateName:".chat-item .name, .msg-item .name",lastMessage:".chat-item .msg, .msg-item .content",unreadBadge:".chat-item .unread, .msg-item .badge",timestamp:".chat-item .time, .msg-item .time"},chat:{input:".chat-input textarea, .msg-input textarea",sendButton:".btn-send, .send-btn",messageItem:".chat-msg, .msg-bubble",messageText:".chat-msg .text, .msg-bubble .text"}};import{navigateTo as Uo,waitForSelector as Ho}from"@roll-agent/browser";var Wo="https://www.yupao.com",Zo=`${Wo}/chat`,Go=`${Wo}/login`;async function Yo(e){e.url().includes("/chat")||await Uo(e,Zo),await Ho(e,Do.messageList.container,{timeout:15e3})}async function Ko(e,t){const n=`${Wo}/chat?id=${encodeURIComponent(t)}`;await Uo(e,n),await Ho(e,Do.chat.input,{timeout:15e3})}async function Jo(e,t){await Yo(e),await Vo(e,Do.messageList.item,{timeout:1e4});const n=Do.messageList;return await e.$$eval(n.item,(e,t)=>{const n=[],a=t.maxItems?e.slice(0,t.maxItems):e;for(const e of a){const a=e.querySelector(t.sel.candidateName),r=e.querySelector(t.sel.lastMessage),o=e.querySelector(t.sel.unreadBadge),i=e.querySelector(t.sel.timestamp),s=e.getAttribute("data-id")??e.getAttribute("data-conversation-id")??e.querySelector("a")?.getAttribute("href")?.match(/id=([^&]+)/)?.[1]??"";n.push({conversationId:s,candidateName:a?.textContent?.trim()??"",lastMessage:r?.textContent?.trim()??"",unreadCount:parseInt(o?.textContent?.trim()??"0",10)||0,timestamp:i?.textContent?.trim()??""})}return n},{sel:n,maxItems:t})}var Xo=jo.object({limit:jo.number().optional().describe("最多返回的消息条数")}),Qo=jo.object({conversationId:jo.string(),candidateName:jo.string(),lastMessage:jo.string(),unreadCount:jo.number(),timestamp:jo.string()}),ei=jo.object({messages:jo.array(Qo),total:jo.number()}),ti=Oo({name:"yupao_read_messages",description:"读取鱼泡未读消息列表",input:Xo,output:ei,execute:async(e,t)=>{t.logger.info(`Reading yupao messages (limit: ${e.limit??"all"})`);const n=h(),a=await n.getPage("yupao"),r=await Jo(a,e.limit);return t.logger.info(`Found ${r.length} messages`),{messages:r.map(e=>({...e})),total:r.length}}});import{defineTool as ni}from"@roll-agent/sdk";import{z as ai}from"zod";import{waitForSelector as ri,typeText as oi,clickElement as ii}from"@roll-agent/browser";async function si(e,t,n){try{return await Ko(e,t),await oi(e,Do.chat.input,n),await ii(e,Do.chat.sendButton),await ri(e,Do.chat.messageItem,{timeout:5e3}),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}var ci=ai.object({conversationId:ai.string().describe("对话 ID"),message:ai.string().describe("要发送的回复消息")}),li=ai.object({success:ai.boolean(),conversationId:ai.string(),sentMessage:ai.string(),error:ai.string().optional()}),di=ni({name:"yupao_send_reply",description:"向鱼泡指定对话发送回复消息",input:ci,output:li,execute:async(e,t)=>{const{conversationId:n,message:a}=e;t.logger.info(`Sending reply to yupao conversation ${n}`);const r=h(),o=await r.getPage("yupao"),i=await si(o,n,a);return i.success?t.logger.info("Reply sent successfully"):t.logger.error(`Failed to send reply: ${i.error}`),{success:i.success,conversationId:n,sentMessage:a,error:i.error}}}),ui=t("browser-use-agent");function mi(e){if(void 0!==e){if("true"===e)return!0;if("false"===e)return!1;throw new Error(`Expected boolean env value "true" or "false", received "${e}".`)}}function gi(e,t){if(void 0===e)return;const n=Number.parseInt(e,10);if(!Number.isInteger(n))throw new Error(`${t} must be an integer, received "${e}".`);return n}function fi(e){if(void 0===e)return;const t=JSON.parse(e);if(!Array.isArray(t)||!t.every(e=>"string"==typeof e))throw new Error("BROWSER_ARGS_JSON must be a JSON string array.");return t}function pi(){return n.parse({mode:process.env.BROWSER_MODE,headless:mi(process.env.BROWSER_HEADLESS),cdpUrl:process.env.BROWSER_CDP_URL,cdpHost:process.env.BROWSER_CDP_HOST,cdpPort:gi(process.env.BROWSER_CDP_PORT,"BROWSER_CDP_PORT"),channel:process.env.BROWSER_CHANNEL,executablePath:process.env.BROWSER_EXECUTABLE_PATH,userDataDir:process.env.BROWSER_USER_DATA_DIR,args:fi(process.env.BROWSER_ARGS_JSON),sessionsDir:process.env.BROWSER_SESSIONS_DIR})}var hi=e({name:"browser-use-agent",tools:[me,Ie,je,Ge,tt,nn,Cn,Mn,Vn,_a,Oa,Wa,or,wo,Kr,io,Ao,Ro,_o,Fo,ti,di,S]},{onShutdown:v});async function wi(){await f(pi());try{await ka(),y(!0)}catch(e){y(!1),ui.error(`Failed to preload Reply Authority keys during startup; browser_status.replyAuthorityKeysLoaded=false. ${e instanceof Error?e.stack??e.message:String(e)}`)}await hi.listen({transport:{type:"http",port:parseInt(process.env.BROWSER_AGENT_PORT??"3100",10),host:process.env.BROWSER_AGENT_HOST??"127.0.0.1"}})}wi().catch(async e=>{ui.error(`Fatal error: ${e instanceof Error?e.stack??e.message:String(e)}`),await v().catch(()=>{}),process.exit(1)});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Page } from "@roll-agent/browser";
|
|
2
|
+
export type DynamicListTarget = Pick<Page, "evaluate" | "waitForTimeout">;
|
|
3
|
+
export type ScrollDirection = "up" | "down";
|
|
4
|
+
export type DynamicListScrollConfig = {
|
|
5
|
+
readonly containerSelectors: readonly string[];
|
|
6
|
+
readonly itemSelector: string;
|
|
7
|
+
};
|
|
8
|
+
export type DynamicListScrollOptions = {
|
|
9
|
+
readonly direction?: ScrollDirection;
|
|
10
|
+
readonly steps?: number;
|
|
11
|
+
readonly distance?: number;
|
|
12
|
+
readonly settleMs?: number;
|
|
13
|
+
readonly stopOnBoundary?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type DynamicListSnapshot = {
|
|
16
|
+
readonly containerFound: boolean;
|
|
17
|
+
readonly containerLabel: string;
|
|
18
|
+
readonly scrollTop: number;
|
|
19
|
+
readonly scrollHeight: number;
|
|
20
|
+
readonly clientHeight: number;
|
|
21
|
+
readonly itemCount: number;
|
|
22
|
+
readonly atStart: boolean;
|
|
23
|
+
readonly atEnd: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type DynamicListScrollResult = {
|
|
26
|
+
readonly success: boolean;
|
|
27
|
+
readonly direction: ScrollDirection;
|
|
28
|
+
readonly stepsRequested: number;
|
|
29
|
+
readonly stepsCompleted: number;
|
|
30
|
+
readonly reachedBoundary: boolean;
|
|
31
|
+
readonly before: DynamicListSnapshot;
|
|
32
|
+
readonly after: DynamicListSnapshot;
|
|
33
|
+
};
|
|
34
|
+
export type DynamicListCollectionOptions = DynamicListScrollOptions & {
|
|
35
|
+
readonly targetCount?: number;
|
|
36
|
+
readonly maxNoNewRounds?: number;
|
|
37
|
+
readonly boundaryLoadRetries?: number;
|
|
38
|
+
readonly boundarySettleMs?: number;
|
|
39
|
+
};
|
|
40
|
+
export declare const DYNAMIC_LIST_COLLECTION_STOP_REASONS: readonly ["target-count", "boundary", "no-new-items", "max-steps"];
|
|
41
|
+
export type DynamicListCollectionStopReason = (typeof DYNAMIC_LIST_COLLECTION_STOP_REASONS)[number];
|
|
42
|
+
export type DynamicListCollectionResult<TItem> = DynamicListScrollResult & {
|
|
43
|
+
readonly items: readonly TItem[];
|
|
44
|
+
readonly uniqueCount: number;
|
|
45
|
+
readonly duplicateCount: number;
|
|
46
|
+
readonly noNewRounds: number;
|
|
47
|
+
readonly stopReason: DynamicListCollectionStopReason;
|
|
48
|
+
};
|
|
49
|
+
export declare function inspectDynamicList(target: DynamicListTarget, config: DynamicListScrollConfig): Promise<DynamicListSnapshot>;
|
|
50
|
+
export declare function scrollDynamicList(target: DynamicListTarget, config: DynamicListScrollConfig, options?: DynamicListScrollOptions): Promise<DynamicListScrollResult>;
|
|
51
|
+
export declare function collectDynamicListItems<TItem>(target: DynamicListTarget, config: DynamicListScrollConfig, readItems: () => Promise<readonly TItem[]>, getItemKey: (item: TItem) => string | undefined, options?: DynamicListCollectionOptions): Promise<DynamicListCollectionResult<TItem>>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DynamicListScrollConfig, ScrollDirection } from "../shared/dynamic-list-scroller.ts";
|
|
2
|
+
export declare const ZHIPIN_LIST_SURFACE_VALUES: readonly ["chat-list", "chat-history", "recommend-list"];
|
|
3
|
+
export type ZhipinListSurface = (typeof ZHIPIN_LIST_SURFACE_VALUES)[number];
|
|
4
|
+
export type ZhipinListSurfaceConfig = DynamicListScrollConfig & {
|
|
5
|
+
readonly surface: ZhipinListSurface;
|
|
6
|
+
readonly defaultDirection: ScrollDirection;
|
|
7
|
+
readonly highlightSelector: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function getZhipinListSurfaceConfig(surface: ZhipinListSurface): ZhipinListSurfaceConfig;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Page } from "@roll-agent/browser";
|
|
2
|
+
export declare const ZHIPIN_RECOMMEND_GENDER_VALUES: readonly ["不限", "男", "女"];
|
|
3
|
+
export type ZhipinRecommendGender = (typeof ZHIPIN_RECOMMEND_GENDER_VALUES)[number];
|
|
4
|
+
export declare const ZHIPIN_RECOMMEND_ACTIVITY_VALUES: readonly ["不限", "刚刚活跃", "今日活跃", "3日内活跃", "本周活跃", "本月活跃"];
|
|
5
|
+
export type ZhipinRecommendActivity = (typeof ZHIPIN_RECOMMEND_ACTIVITY_VALUES)[number];
|
|
6
|
+
export declare const ZHIPIN_RECOMMEND_FILTER_STATUS_VALUES: readonly ["applied", "recommend_not_ready", "filter_not_found", "requires_vip", "age_not_applied", "submit_failed", "error"];
|
|
7
|
+
export type ZhipinRecommendFilterStatus = (typeof ZHIPIN_RECOMMEND_FILTER_STATUS_VALUES)[number];
|
|
8
|
+
export type RecommendTarget = Page | NonNullable<ReturnType<Page["frame"]>>;
|
|
9
|
+
type PageLocator = ReturnType<Page["locator"]>;
|
|
10
|
+
export type ZhipinRecommendFilterRequest = {
|
|
11
|
+
readonly ageMin?: number;
|
|
12
|
+
readonly ageMax?: number;
|
|
13
|
+
readonly gender: ZhipinRecommendGender;
|
|
14
|
+
readonly activity: ZhipinRecommendActivity;
|
|
15
|
+
};
|
|
16
|
+
export type ZhipinRecommendFilterApplied = {
|
|
17
|
+
readonly ageMin?: number;
|
|
18
|
+
readonly ageMax?: number;
|
|
19
|
+
readonly gender: string;
|
|
20
|
+
readonly activity: string;
|
|
21
|
+
};
|
|
22
|
+
export type ZhipinRecommendFilterApplyResult = {
|
|
23
|
+
readonly status: ZhipinRecommendFilterStatus;
|
|
24
|
+
readonly requested: ZhipinRecommendFilterRequest;
|
|
25
|
+
readonly applied?: ZhipinRecommendFilterApplied;
|
|
26
|
+
readonly filterButtonText?: string;
|
|
27
|
+
readonly error?: string;
|
|
28
|
+
};
|
|
29
|
+
export type RecommendFilterVisualFeedback = {
|
|
30
|
+
readonly moveToLocator: (page: Page, locator: PageLocator, options?: {
|
|
31
|
+
readonly durationMs?: number;
|
|
32
|
+
readonly settleMs?: number;
|
|
33
|
+
readonly target?: RecommendTarget;
|
|
34
|
+
}) => Promise<boolean>;
|
|
35
|
+
readonly showClickOnLocator: (page: Page, locator: PageLocator, options?: {
|
|
36
|
+
readonly pulseDurationMs?: number;
|
|
37
|
+
readonly target?: RecommendTarget;
|
|
38
|
+
}) => Promise<boolean>;
|
|
39
|
+
};
|
|
40
|
+
type RecommendAgeState = {
|
|
41
|
+
readonly ageMin?: number;
|
|
42
|
+
readonly ageMax?: number;
|
|
43
|
+
};
|
|
44
|
+
export declare function parseRecommendAgeStateText(text: string): RecommendAgeState;
|
|
45
|
+
export declare function waitForRecommendFilterSurface(target: RecommendTarget, timeout?: number): Promise<boolean>;
|
|
46
|
+
export declare function applyRecommendFilter(page: Page, target: RecommendTarget, requested: ZhipinRecommendFilterRequest, visualFeedback?: RecommendFilterVisualFeedback): Promise<ZhipinRecommendFilterApplyResult>;
|
|
47
|
+
export {};
|
|
@@ -95,6 +95,8 @@ export declare const ZHIPIN_SELECTORS: {
|
|
|
95
95
|
readonly recommend: {
|
|
96
96
|
readonly iframe: "#recommendFrame";
|
|
97
97
|
readonly resumeIframe: "iframe[src*=\"c-resume\"]";
|
|
98
|
+
readonly filterButton: ".recommend-filter .filter-label, .filter-label-wrap .filter-label, .filter-label";
|
|
99
|
+
readonly filterPanel: ".filter-panel";
|
|
98
100
|
readonly candidateItem: "[data-geek], .geek-item";
|
|
99
101
|
readonly candidateName: ".name";
|
|
100
102
|
readonly candidateBaseInfo: ".base-info";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getContextManager } from "../runtime-holder.ts";
|
|
2
|
+
import { applyRecommendFilter, waitForRecommendFilterSurface, type RecommendTarget } from "../pages/zhipin/recommend-filter.ts";
|
|
3
|
+
import { getRecommendTarget } from "../pages/zhipin/recommend-list.ts";
|
|
4
|
+
import { VisualActivitySession } from "../visual-activity-session.ts";
|
|
5
|
+
import { moveVisualCursorToLocator, showVisualClickOnLocator } from "../visual-cursor.ts";
|
|
6
|
+
type VisualActivitySessionLike = Pick<VisualActivitySession, "begin" | "highlightSelector" | "retarget" | "succeed" | "fail">;
|
|
7
|
+
type ZhipinFilterRecommendCandidatesDeps = {
|
|
8
|
+
readonly getContextManager: typeof getContextManager;
|
|
9
|
+
readonly getRecommendTarget: typeof getRecommendTarget;
|
|
10
|
+
readonly waitForRecommendFilterSurface: typeof waitForRecommendFilterSurface;
|
|
11
|
+
readonly applyRecommendFilter: typeof applyRecommendFilter;
|
|
12
|
+
readonly moveVisualCursorToLocator: typeof moveVisualCursorToLocator;
|
|
13
|
+
readonly showVisualClickOnLocator: typeof showVisualClickOnLocator;
|
|
14
|
+
readonly createVisualActivitySession: (target: RecommendTarget) => VisualActivitySessionLike;
|
|
15
|
+
};
|
|
16
|
+
export declare function setZhipinFilterRecommendCandidatesDepsForTests(override: Partial<ZhipinFilterRecommendCandidatesDeps> | undefined): void;
|
|
17
|
+
export declare const zhipinFilterRecommendCandidates: import("@roll-agent/sdk").ToolDefinition<{
|
|
18
|
+
activity?: "不限" | "刚刚活跃" | "今日活跃" | "3日内活跃" | "本周活跃" | "本月活跃" | undefined;
|
|
19
|
+
ageMin?: number | undefined;
|
|
20
|
+
ageMax?: number | undefined;
|
|
21
|
+
gender?: "不限" | "男" | "女" | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
status: "error" | "applied" | "recommend_not_ready" | "filter_not_found" | "requires_vip" | "age_not_applied" | "submit_failed";
|
|
24
|
+
success: boolean;
|
|
25
|
+
requested: {
|
|
26
|
+
activity: "不限" | "刚刚活跃" | "今日活跃" | "3日内活跃" | "本周活跃" | "本月活跃";
|
|
27
|
+
gender: "不限" | "男" | "女";
|
|
28
|
+
ageMin?: number | undefined;
|
|
29
|
+
ageMax?: number | undefined;
|
|
30
|
+
};
|
|
31
|
+
error?: string | undefined;
|
|
32
|
+
applied?: {
|
|
33
|
+
activity: string;
|
|
34
|
+
gender: string;
|
|
35
|
+
ageMin?: number | undefined;
|
|
36
|
+
ageMax?: number | undefined;
|
|
37
|
+
} | undefined;
|
|
38
|
+
filterButtonText?: string | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
export {};
|
|
@@ -11,6 +11,8 @@ type ZhipinGetCandidateListDeps = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function setZhipinGetCandidateListDepsForTests(override: Partial<ZhipinGetCandidateListDeps> | undefined): void;
|
|
13
13
|
export declare const zhipinGetCandidateList: import("@roll-agent/sdk").ToolDefinition<{
|
|
14
|
+
autoScroll?: boolean | undefined;
|
|
15
|
+
maxScrolls?: number | undefined;
|
|
14
16
|
maxResults?: number | undefined;
|
|
15
17
|
}, {
|
|
16
18
|
success: boolean;
|
|
@@ -32,5 +34,19 @@ export declare const zhipinGetCandidateList: import("@roll-agent/sdk").ToolDefin
|
|
|
32
34
|
}[];
|
|
33
35
|
total: number;
|
|
34
36
|
error?: string | undefined;
|
|
37
|
+
scrollStats?: {
|
|
38
|
+
stopReason: "target-count" | "boundary" | "no-new-items" | "max-steps";
|
|
39
|
+
containerLabel: string;
|
|
40
|
+
stepsRequested: number;
|
|
41
|
+
stepsCompleted: number;
|
|
42
|
+
reachedBoundary: boolean;
|
|
43
|
+
uniqueCount: number;
|
|
44
|
+
duplicateCount: number;
|
|
45
|
+
noNewRounds: number;
|
|
46
|
+
beforeItemCount: number;
|
|
47
|
+
afterItemCount: number;
|
|
48
|
+
beforeScrollHeight: number;
|
|
49
|
+
afterScrollHeight: number;
|
|
50
|
+
} | undefined;
|
|
35
51
|
}>;
|
|
36
52
|
export {};
|
|
@@ -2,6 +2,8 @@ export declare const zhipinReadMessages: import("@roll-agent/sdk").ToolDefinitio
|
|
|
2
2
|
limit?: number | undefined;
|
|
3
3
|
onlyUnread?: boolean | undefined;
|
|
4
4
|
sortBy?: "name" | "time" | "unreadCount" | undefined;
|
|
5
|
+
autoScroll?: boolean | undefined;
|
|
6
|
+
maxScrolls?: number | undefined;
|
|
5
7
|
}, {
|
|
6
8
|
success: boolean;
|
|
7
9
|
candidates: {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const zhipinScrollView: import("@roll-agent/sdk").ToolDefinition<{
|
|
2
|
+
surface: "chat-list" | "chat-history" | "recommend-list";
|
|
3
|
+
direction?: "up" | "down" | undefined;
|
|
4
|
+
distance?: number | undefined;
|
|
5
|
+
steps?: number | undefined;
|
|
6
|
+
settleMs?: number | undefined;
|
|
7
|
+
}, {
|
|
8
|
+
success: boolean;
|
|
9
|
+
direction: "up" | "down";
|
|
10
|
+
stepsRequested: number;
|
|
11
|
+
stepsCompleted: number;
|
|
12
|
+
before: {
|
|
13
|
+
containerFound: boolean;
|
|
14
|
+
containerLabel: string;
|
|
15
|
+
scrollTop: number;
|
|
16
|
+
scrollHeight: number;
|
|
17
|
+
clientHeight: number;
|
|
18
|
+
atStart: boolean;
|
|
19
|
+
atEnd: boolean;
|
|
20
|
+
itemCount: number;
|
|
21
|
+
};
|
|
22
|
+
after: {
|
|
23
|
+
containerFound: boolean;
|
|
24
|
+
containerLabel: string;
|
|
25
|
+
scrollTop: number;
|
|
26
|
+
scrollHeight: number;
|
|
27
|
+
clientHeight: number;
|
|
28
|
+
atStart: boolean;
|
|
29
|
+
atEnd: boolean;
|
|
30
|
+
itemCount: number;
|
|
31
|
+
};
|
|
32
|
+
reachedBoundary: boolean;
|
|
33
|
+
surface: "chat-list" | "chat-history" | "recommend-list";
|
|
34
|
+
error?: string | undefined;
|
|
35
|
+
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roll-agent/browser-use-agent",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"zod": "^3.25.76",
|
|
48
|
-
"@roll-agent/sdk": "0.1.
|
|
48
|
+
"@roll-agent/sdk": "0.1.6",
|
|
49
49
|
"@roll-agent/browser": "0.2.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|