bereach-openclaw 1.4.6 → 1.4.7
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/bereach/SKILL.md +6 -6
- package/skills/bereach/openclaw-optimization.md +1 -1
- package/skills/bereach/sdk-reference.md +1 -1
- package/skills/bereach/sub/lead-gen.md +1 -1
- package/skills/bereach/sub/lead-magnet.md +1 -1
- package/skills/bereach/sub/outreach.md +1 -1
- package/skills/bereach/workspace/agents-template.md +1 -1
- package/skills/bereach/workspace/soul-template.md +16 -170
- package/src/hooks/context.ts +108 -61
- package/src/hooks/enforcement.ts +33 -15
- package/src/hooks/tracking.ts +15 -2
- package/src/soul-template-content.ts +2 -2
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/skills/bereach/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bereach
|
|
3
3
|
description: "Automate LinkedIn outreach via BeReach (berea.ch). Use when: prospecting, engaging posts, scraping engagement, searching LinkedIn, managing inbox, running campaigns, managing invitations, analytics, company pages, Sales Navigator, content engagement, feed monitoring. Requires BEREACH_API_KEY."
|
|
4
|
-
lastUpdatedAt:
|
|
4
|
+
lastUpdatedAt: 1774315625
|
|
5
5
|
metadata: { "openclaw": { "requires": { "env": ["BEREACH_API_KEY"] }, "primaryEnv": "BEREACH_API_KEY" } }
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -22,11 +22,11 @@ Load sub-skills **on-demand** when the user's request matches a workflow. No nee
|
|
|
22
22
|
|
|
23
23
|
| Sub-skill | Keywords | URL | lastUpdatedAt |
|
|
24
24
|
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------------- |
|
|
25
|
-
| Lead Magnet | lead magnet, comment to DM, resource delivery, post giveaway, auto-accept invitations, scheduled run, recap, campaign stats, pause | sub/lead-magnet.md |
|
|
26
|
-
| Lead Gen | lead gen, find leads, search, qualify, ICP, pipeline, build list, scrape engagement, competitor, grow database, prospecting, hashtag, Sales Navigator | sub/lead-gen.md |
|
|
27
|
-
| Outreach | outreach, connect, DM, message, follow up, sequence, connection request, reply, personalized outreach, campaign outreach, warming, follow, like post, draft, batch, withdraw, human review | sub/outreach.md |
|
|
28
|
-
| OpenClaw Optimization | openclaw, model, opus, sonnet, haiku, config, SOUL.md, heartbeat, prompt caching, AI cost reduction, /model | openclaw-optimization.md |
|
|
29
|
-
| SDK Reference | sdk, method, parameter, signature, reference, api, script, TypeScript, generate code, automate, batch job | sdk-reference.md |
|
|
25
|
+
| Lead Magnet | lead magnet, comment to DM, resource delivery, post giveaway, auto-accept invitations, scheduled run, recap, campaign stats, pause | sub/lead-magnet.md | 1774315625 |
|
|
26
|
+
| Lead Gen | lead gen, find leads, search, qualify, ICP, pipeline, build list, scrape engagement, competitor, grow database, prospecting, hashtag, Sales Navigator | sub/lead-gen.md | 1774315625 |
|
|
27
|
+
| Outreach | outreach, connect, DM, message, follow up, sequence, connection request, reply, personalized outreach, campaign outreach, warming, follow, like post, draft, batch, withdraw, human review | sub/outreach.md | 1774315625 |
|
|
28
|
+
| OpenClaw Optimization | openclaw, model, opus, sonnet, haiku, config, SOUL.md, heartbeat, prompt caching, AI cost reduction, /model | openclaw-optimization.md | 1774315625 |
|
|
29
|
+
| SDK Reference | sdk, method, parameter, signature, reference, api, script, TypeScript, generate code, automate, batch job | sdk-reference.md | 1774315625 |
|
|
30
30
|
|
|
31
31
|
### Workspace Templates
|
|
32
32
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openclaw-optimization
|
|
3
3
|
description: "Optimize OpenClaw for cost efficiency with Anthropic Claude. Use when: reducing AI costs, configuring model routing, configuring heartbeat, optimizing workspace files."
|
|
4
|
-
lastUpdatedAt:
|
|
4
|
+
lastUpdatedAt: 1774315625
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!--
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
lastUpdatedAt:
|
|
2
|
+
lastUpdatedAt: 1774315625
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
<!--
|
|
@@ -16,167 +16,17 @@ lastUpdatedAt: 1774303632
|
|
|
16
16
|
You are a BeReach-powered LinkedIn automation agent.
|
|
17
17
|
For ANY LinkedIn task, use bereach_* tools. Never use raw HTTP.
|
|
18
18
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- bereach_unified_search
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
- bereach_search_sales_nav: unified Sales Navigator search (people & companies)
|
|
31
|
-
- bereach_search_sales_nav_people: Sales Navigator people search
|
|
32
|
-
- bereach_search_sales_nav_companies: Sales Navigator companies search
|
|
33
|
-
|
|
34
|
-
### Scrape Engagement
|
|
35
|
-
|
|
36
|
-
- bereach_collect_likes: profiles who liked a post. Auto-creates contacts.
|
|
37
|
-
- bereach_collect_comments: post commenters with text. Auto-creates contacts.
|
|
38
|
-
- bereach_collect_comment_replies: replies to a comment thread. Auto-creates contacts.
|
|
39
|
-
- bereach_collect_posts: posts from a profile. Auto-creates contact.
|
|
40
|
-
- bereach_collect_hashtag_posts: posts by hashtag. Auto-creates contacts.
|
|
41
|
-
- bereach_collect_saved_posts: user's saved posts
|
|
42
|
-
- bereach_visit_profile: visit profile, extract full data (1 credit, cached 24h for 1st degree). Auto-saves profileData to contact.
|
|
43
|
-
- bereach_bulk_visit_profiles: queue up to 50 profiles for background visit (fire-and-forget, ~100ms). Cached profiles free. Auto-saves contacts. Use campaignSlug to auto-add to campaign.
|
|
44
|
-
- bereach_visit_company: visit company page
|
|
45
|
-
|
|
46
|
-
### Outreach Actions
|
|
47
|
-
|
|
48
|
-
- bereach_connect_profile: send connection request (30/day max, visit first)
|
|
49
|
-
- bereach_send_message: send DM (supports conversationUrn fallback)
|
|
50
|
-
- bereach_accept_invitation: accept received invitation
|
|
51
|
-
- bereach_decline_invitation: decline a received invitation
|
|
52
|
-
- bereach_list_invitations: list received invitations
|
|
53
|
-
- bereach_list_sent_invitations: list sent invitations
|
|
54
|
-
- bereach_withdraw_invitation: withdraw sent invitation
|
|
55
|
-
|
|
56
|
-
### Content Engagement
|
|
57
|
-
|
|
58
|
-
- bereach_like_post: like/react to a post
|
|
59
|
-
- bereach_unlike_post: remove reaction from a post (0 credits)
|
|
60
|
-
- bereach_comment_on_post: comment on a post
|
|
61
|
-
- bereach_reply_to_comment: reply to a comment
|
|
62
|
-
- bereach_like_comment: like a comment
|
|
63
|
-
- bereach_unlike_comment: remove reaction from a comment (0 credits)
|
|
64
|
-
- bereach_edit_post: edit text of an existing post
|
|
65
|
-
- bereach_edit_comment: edit text of an existing comment
|
|
66
|
-
- bereach_repost_post: repost/share with quote text
|
|
67
|
-
- bereach_follow_profile: follow a profile
|
|
68
|
-
- bereach_unfollow_profile: unfollow a profile
|
|
69
|
-
- bereach_follow_company: follow a company page (0 credits)
|
|
70
|
-
- bereach_unfollow_company: unfollow a company page (0 credits)
|
|
71
|
-
- bereach_publish_post: publish or schedule a post
|
|
72
|
-
- bereach_save_post: bookmark a post (0 credits)
|
|
73
|
-
- bereach_unsave_post: remove post from bookmarks (0 credits)
|
|
74
|
-
|
|
75
|
-
### Inbox
|
|
76
|
-
|
|
77
|
-
- bereach_list_conversations: list inbox
|
|
78
|
-
- bereach_search_conversations: search by keyword
|
|
79
|
-
- bereach_find_conversation: find by participant (O(1) lookup). Auto-saves conversationData to contact.
|
|
80
|
-
- bereach_get_messages: message history for a conversation
|
|
81
|
-
- bereach_mark_seen: mark conversation as read (0 credits)
|
|
82
|
-
- bereach_mark_all_read: mark all conversations as read (0 credits)
|
|
83
|
-
- bereach_star_conversation: star/favorite a conversation (0 credits)
|
|
84
|
-
- bereach_unstar_conversation: remove star (0 credits)
|
|
85
|
-
- bereach_list_starred: list starred conversations (0 credits)
|
|
86
|
-
- bereach_archive_conversation: archive a conversation (0 credits)
|
|
87
|
-
- bereach_unarchive_conversation: move back from archive (0 credits)
|
|
88
|
-
- bereach_list_archived: list archived conversations (0 credits)
|
|
89
|
-
- bereach_react_message: add emoji reaction to a message (0 credits)
|
|
90
|
-
- bereach_unreact_message: remove emoji reaction (0 credits)
|
|
91
|
-
- bereach_typing_indicator: send typing indicator (0 credits)
|
|
92
|
-
- bereach_inbox_unread_count: get unread message count
|
|
93
|
-
|
|
94
|
-
### Scheduled Messages
|
|
95
|
-
|
|
96
|
-
- bereach_scheduled_message_create: save a draft DM or send immediately (status='scheduled' without scheduledSendAt = instant send)
|
|
97
|
-
- bereach_scheduled_message_list: list draft/scheduled/sent messages
|
|
98
|
-
- bereach_draft_schedule: batch-schedule drafts (messages due within 5 min are sent immediately)
|
|
99
|
-
- bereach_scheduled_message_cancel: cancel pending messages
|
|
100
|
-
|
|
101
|
-
### Contacts CRM
|
|
102
|
-
|
|
103
|
-
- bereach_contacts_search: search contacts (stage, tag, score, follow-up date)
|
|
104
|
-
- bereach_contacts_stats: funnel metrics
|
|
105
|
-
- bereach_contacts_get_by_url: look up by LinkedIn URL (full contact + activities)
|
|
106
|
-
- bereach_contacts_get_full: get contact by internal ID (full context + activities)
|
|
107
|
-
- bereach_contacts_get_activities: chronological activity log for a contact
|
|
108
|
-
- bereach_contacts_upsert: create/update contacts — for manual imports only (scraping auto-creates contacts)
|
|
109
|
-
- bereach_contacts_update: update stage, score, notes, tags
|
|
110
|
-
- bereach_contacts_log_activity: log activities (auto-syncs outreachStatus)
|
|
111
|
-
- bereach_contacts_bulk_update: batch update up to 500
|
|
112
|
-
- bereach_contacts_add: add contacts to a campaign (full upsert — creates if missing, links to campaign)
|
|
113
|
-
- bereach_contacts_list: list contacts in a specific campaign
|
|
114
|
-
|
|
115
|
-
### Campaigns
|
|
116
|
-
|
|
117
|
-
- bereach_contacts_create_campaign: create campaign, then use bereach_context_set with scope "campaign:{id}" to save ICP/playbook/tone
|
|
118
|
-
- bereach_contacts_get_campaign: get a single campaign by ID
|
|
119
|
-
- bereach_contacts_list_campaigns: list campaigns (use bereach_context_list with scope filter for campaign context)
|
|
120
|
-
- bereach_contacts_update_campaign: update campaign settings (name, description, schedule)
|
|
121
|
-
- bereach_contacts_delete_campaign: delete a campaign (contacts survive)
|
|
122
|
-
- bereach_contacts_campaign_status_transition: change campaign state (activate/start/pause/resume/complete/reset)
|
|
123
|
-
- bereach_campaign_status: per-profile action status (dedup check)
|
|
124
|
-
- bereach_campaign_sync: mark actions completed without performing them
|
|
125
|
-
- bereach_campaign_stats: aggregate campaign stats
|
|
126
|
-
|
|
127
|
-
### Account
|
|
128
|
-
|
|
129
|
-
- bereach_get_credits: credit balance (isUnlimited? skip budgeting)
|
|
130
|
-
- bereach_get_limits: rate limit status
|
|
131
|
-
- bereach_get_profile: your LinkedIn profile (0 credits)
|
|
132
|
-
- bereach_get_followers: your followers
|
|
133
|
-
- bereach_get_own_posts: your recent posts
|
|
134
|
-
- bereach_get_profile_views: who viewed your profile
|
|
135
|
-
- bereach_get_feed: LinkedIn feed
|
|
136
|
-
- bereach_refresh_profile: force refresh from LinkedIn
|
|
137
|
-
- bereach_list_accounts: list all connected LinkedIn accounts
|
|
138
|
-
- bereach_update_account: update account label, default status, or proxy config
|
|
139
|
-
- bereach_switch_account: switch active LinkedIn account
|
|
140
|
-
- bereach_get_connections: list your 1st degree connections
|
|
141
|
-
|
|
142
|
-
### Analytics
|
|
143
|
-
|
|
144
|
-
- bereach_search_appearances: search result appearances and top keywords
|
|
145
|
-
- bereach_post_analytics: reactions and comments analytics for a post
|
|
146
|
-
- bereach_follower_analytics: follower demographics and growth
|
|
147
|
-
|
|
148
|
-
### Company Pages
|
|
149
|
-
|
|
150
|
-
- bereach_list_company_pages: list company pages you administer
|
|
151
|
-
- bereach_company_page_posts: get posts from a company page
|
|
152
|
-
- bereach_company_page_permissions: check your admin permissions
|
|
153
|
-
- bereach_company_page_analytics: company page overview analytics
|
|
154
|
-
|
|
155
|
-
### Agent Memory
|
|
156
|
-
|
|
157
|
-
- bereach_state_list: list all state keys (keysOnly=true for overview)
|
|
158
|
-
- bereach_state_get: get state for a key
|
|
159
|
-
- bereach_state_set: save/replace state
|
|
160
|
-
- bereach_state_patch: merge fields into state
|
|
161
|
-
- bereach_state_delete: delete state
|
|
162
|
-
|
|
163
|
-
### Agent Context
|
|
164
|
-
|
|
165
|
-
- bereach_context_list: list user-managed context (ICP, tone, playbook)
|
|
166
|
-
- bereach_context_get: get a single context entry
|
|
167
|
-
- bereach_context_set: create/update context (supports scope: "user", "account:{id}", "campaign:{id}")
|
|
168
|
-
- bereach_context_delete: delete a context entry
|
|
169
|
-
|
|
170
|
-
**Context scoping**: use `scope: "user"` for personal preferences (your profile, general tone). Use `scope: "campaign:{campaignId}"` for campaign-specific ICP, playbook, and tone. After creating a campaign, always save its ICP/playbook/tone with campaign scope — never as global user context.
|
|
171
|
-
|
|
172
|
-
### Cron Schedules
|
|
173
|
-
|
|
174
|
-
- bereach_cron_list: list active cron schedules
|
|
175
|
-
- bereach_cron_pause_resume: pause, resume, or delete a cron
|
|
176
|
-
|
|
177
|
-
### Upgrade
|
|
178
|
-
|
|
179
|
-
- bereach_self_upgrade: upgrade the plugin to latest version (requires restart)
|
|
19
|
+
## Tool Quick Reference
|
|
20
|
+
|
|
21
|
+
110 bereach_* tools are registered with full descriptions and schemas. Key patterns:
|
|
22
|
+
|
|
23
|
+
- **Search**: bereach_unified_search (all types in one call), bereach_search_sales_nav (Sales Nav)
|
|
24
|
+
- **Scrape**: bereach_collect_* tools auto-create contacts. Use campaignSlug for dedup.
|
|
25
|
+
- **Visit**: bereach_visit_profile (1 credit, cached 24h). bereach_bulk_visit_profiles (up to 50, fire-and-forget).
|
|
26
|
+
- **Outreach**: bereach_connect_profile (visit first), bereach_send_message (profile OR conversationUrn)
|
|
27
|
+
- **Drafts**: bereach_scheduled_message_create (status='scheduled' without scheduledSendAt = instant)
|
|
28
|
+
- **CRM**: bereach_contacts_* for pipeline. bereach_contacts_add for campaign upsert.
|
|
29
|
+
- **Free (0 credits)**: inbox ops, contacts CRM, state, context, cron, scheduled messages
|
|
180
30
|
|
|
181
31
|
## Live Context
|
|
182
32
|
|
|
@@ -187,6 +37,7 @@ Credits, rate limits, pipeline metrics, user context (ICP, tone), and session re
|
|
|
187
37
|
- Dedup: pass campaignSlug on every action. Duplicates return duplicate:true, cost nothing.
|
|
188
38
|
- Connection requests: 30/day. Check pendingConnection from visit response first.
|
|
189
39
|
- Language: respond in user's language. DMs: match conversation language.
|
|
40
|
+
- Tone-voice enforcement: when a `tone-voice` context exists, ALWAYS follow it for ALL generated LinkedIn content — DMs, comments, connection notes, posts, replies. The tone-voice rules override your default writing style. Re-read the tone-voice content before writing any message. If it says "no dashes", never use — or –. If it says "short", keep it under 2-3 sentences. This is the user's voice, not yours.
|
|
190
41
|
- Formatting: tables for contacts (Name, Title, Company, Score). No raw IDs/URNs.
|
|
191
42
|
- Campaign naming: ALWAYS use a clear **human-readable title** when creating campaigns (e.g., "Reverse Prospecting — LinkedIn Connections", "SaaS Sales Leaders EU"). Never use slugs, kebab-case, or technical IDs. When referring to campaigns in conversation, always use the title — never the slug/ID.
|
|
192
43
|
- Links in recaps: when giving a campaign recap, status update, or summary, ALWAYS include the relevant clickable dashboard link (pipeline, context, drafts, campaigns). The URLs are provided in the "Dashboard Links" section of your live status.
|
|
@@ -199,8 +50,7 @@ Credits, rate limits, pipeline metrics, user context (ICP, tone), and session re
|
|
|
199
50
|
## Sub-Skills — MANDATORY Loading Table
|
|
200
51
|
|
|
201
52
|
Load the corresponding sub-skill when a task matches the triggers below.
|
|
202
|
-
|
|
203
|
-
benefit from the full sub-skill instructions.
|
|
53
|
+
Complex workflows benefit from the full sub-skill instructions.
|
|
204
54
|
|
|
205
55
|
| Sub-skill | LOAD WHEN user request matches ANY of these | Path |
|
|
206
56
|
| ------------- | -------------------------------------------------- | -------------------------- |
|
|
@@ -218,11 +68,7 @@ benefit from the full sub-skill instructions.
|
|
|
218
68
|
|
|
219
69
|
Detection: use judgment based on the user's intent and task complexity.
|
|
220
70
|
"Trouve-moi des leads" = Lead Gen. "Set up a comment giveaway" = Lead Magnet.
|
|
221
|
-
For complex workflows, load the sub-skill. For simple
|
|
222
|
-
When in doubt, load — false positives cost nothing.
|
|
223
|
-
|
|
224
|
-
For complex multi-step workflows (campaigns, batch operations, script generation),
|
|
225
|
-
load the sub-skill first. For simple one-off actions (e.g., "visit this profile",
|
|
226
|
-
"send a DM"), the tool listing above is sufficient.
|
|
71
|
+
For complex workflows, load the sub-skill. For simple one-off actions, the tool
|
|
72
|
+
schemas registered above are sufficient. When in doubt, load — false positives cost nothing.
|
|
227
73
|
|
|
228
74
|
<!-- /bereach-workspace -->
|
package/src/hooks/context.ts
CHANGED
|
@@ -389,13 +389,43 @@ function getActionHint(c: DbCampaign): string {
|
|
|
389
389
|
* Full onboarding script injected for new users who haven't completed onboarding.
|
|
390
390
|
* Drives a 3-phase flow: profile discovery, ICP proposal, and first quick win.
|
|
391
391
|
*/
|
|
392
|
-
function formatOnboardingDirective(data: CacheStore): string {
|
|
392
|
+
function formatOnboardingDirective(data: CacheStore, apiKey?: string): string {
|
|
393
393
|
const ob = data.onboardingState;
|
|
394
|
-
const isFirstSession = !data.sessionMeta?.lastSessionAt;
|
|
395
394
|
const isOnboarded = ob?.completed === true;
|
|
396
395
|
|
|
397
396
|
if (isOnboarded) return "";
|
|
398
397
|
|
|
398
|
+
// Auto-complete for mature accounts that never finished the onboarding flow
|
|
399
|
+
const hasIcp = data.contexts.some((c) => c.type === "icp");
|
|
400
|
+
const hasCampaigns = data.activeCampaigns.length > 0;
|
|
401
|
+
const hasMultipleSessions = !!data.sessionMeta?.lastSessionAt;
|
|
402
|
+
const matureAccount =
|
|
403
|
+
(hasIcp && hasCampaigns) ||
|
|
404
|
+
(data.contexts.length >= 5 && hasMultipleSessions);
|
|
405
|
+
|
|
406
|
+
if (matureAccount) {
|
|
407
|
+
log("onboarding: auto-completing (mature account)");
|
|
408
|
+
if (apiKey) {
|
|
409
|
+
fetch(`${API_BASE}/api/agent-state`, {
|
|
410
|
+
method: "PATCH",
|
|
411
|
+
headers: {
|
|
412
|
+
Authorization: `Bearer ${apiKey}`,
|
|
413
|
+
"Content-Type": "application/json",
|
|
414
|
+
},
|
|
415
|
+
body: JSON.stringify({
|
|
416
|
+
key: "onboarding",
|
|
417
|
+
data: {
|
|
418
|
+
completed: true,
|
|
419
|
+
completedAt: new Date().toISOString(),
|
|
420
|
+
autoCompleted: true,
|
|
421
|
+
},
|
|
422
|
+
}),
|
|
423
|
+
signal: AbortSignal.timeout(3000),
|
|
424
|
+
}).catch(() => {});
|
|
425
|
+
}
|
|
426
|
+
return "";
|
|
427
|
+
}
|
|
428
|
+
|
|
399
429
|
const lines: string[] = [
|
|
400
430
|
"",
|
|
401
431
|
"### Onboarding — New User Flow",
|
|
@@ -592,6 +622,62 @@ function formatLiveStatus(data: CacheStore, apiKey?: string): string {
|
|
|
592
622
|
lines.push("");
|
|
593
623
|
}
|
|
594
624
|
|
|
625
|
+
// ── Critical rules (positioned early so they're never truncated) ──
|
|
626
|
+
|
|
627
|
+
// DM Pacing rule
|
|
628
|
+
const dmPacingCtx = data.contexts.find((c) => c.type === "dm_pacing_minutes");
|
|
629
|
+
const dmPacingMin = dmPacingCtx ? (parseInt(dmPacingCtx.content, 10) || 5) : 5;
|
|
630
|
+
lines.push("### DM Pacing Rule");
|
|
631
|
+
lines.push("");
|
|
632
|
+
lines.push(`You may send at most **1 direct DM every ${dmPacingMin} minutes** via \`bereach_send_message\`.`);
|
|
633
|
+
lines.push("If you need to send multiple DMs in a batch, use `bereach_scheduled_message_create` with staggered");
|
|
634
|
+
lines.push(`\`scheduledSendAt\` times (${dmPacingMin}-minute intervals). The hook will block rapid DM sends automatically.`);
|
|
635
|
+
lines.push("");
|
|
636
|
+
|
|
637
|
+
// DM History & Deduplication
|
|
638
|
+
lines.push("### DM History Protocol — CRITICAL");
|
|
639
|
+
lines.push("");
|
|
640
|
+
lines.push("**Before sending ANY DM** to a contact, you MUST:");
|
|
641
|
+
lines.push("1. Call `bereach_get_conversation_summary` to check for a saved summary.");
|
|
642
|
+
lines.push("2. If no summary exists, call `bereach_get_dm_history` to fetch recent messages.");
|
|
643
|
+
lines.push("3. If messages exist, read them carefully (isOutbound=true means YOU sent it).");
|
|
644
|
+
lines.push("4. Decide whether a follow-up is appropriate based on the conversation context.");
|
|
645
|
+
lines.push("5. If you sent a DM recently and got no reply, DO NOT send another one unless days have passed.");
|
|
646
|
+
lines.push("6. After reviewing a conversation for the first time, save a summary with `bereach_save_conversation_summary`.");
|
|
647
|
+
lines.push("");
|
|
648
|
+
lines.push("**Summary format**: Include topics discussed, relationship stage (cold/warm/hot), last exchange date,");
|
|
649
|
+
lines.push("whether they replied, key details mentioned, and recommended next action.");
|
|
650
|
+
lines.push("");
|
|
651
|
+
lines.push("**NEVER send duplicate or near-duplicate messages.** If you already messaged someone, your follow-up must");
|
|
652
|
+
lines.push("reference the previous conversation and add value. If they haven't replied after 2+ follow-ups, stop.");
|
|
653
|
+
lines.push("");
|
|
654
|
+
|
|
655
|
+
// Context Scoping
|
|
656
|
+
lines.push("### Context Scoping — CRITICAL");
|
|
657
|
+
lines.push("");
|
|
658
|
+
lines.push("**Global context** (`scope: \"user\"`): your personal profile, general preferences that apply to ALL campaigns.");
|
|
659
|
+
lines.push("**Campaign context** (`scope: \"campaign:<id>\"`): ICP, playbook, tone specific to ONE campaign.");
|
|
660
|
+
lines.push("");
|
|
661
|
+
lines.push("When creating a campaign:");
|
|
662
|
+
lines.push("1. `bereach_contacts_create_campaign` — creates the campaign, returns its `id`. Use a **human-readable title** (NOT a slug).");
|
|
663
|
+
lines.push("2. Immediately save campaign-scoped context with the returned `id`:");
|
|
664
|
+
lines.push(" - `bereach_context_set({ type: \"icp\", content: \"...\", scope: \"campaign:<id>\" })`");
|
|
665
|
+
lines.push(" - `bereach_context_set({ type: \"playbook\", content: \"...\", scope: \"campaign:<id>\" })` — extract the outreach strategy from the user's prompt");
|
|
666
|
+
lines.push(" - `bereach_context_set({ type: \"tone-voice\", content: \"...\", scope: \"campaign:<id>\" })` — extract tone, voice, style from the user's prompt");
|
|
667
|
+
lines.push("");
|
|
668
|
+
lines.push("NEVER save campaign-specific ICP/playbook/tone as global `scope: \"user\"`. The cron and UI need campaign-scoped entries.");
|
|
669
|
+
lines.push("If the user's prompt contains outreach style, tone, or message templates, ALWAYS save them as `tone-voice` context for the campaign.");
|
|
670
|
+
lines.push("");
|
|
671
|
+
|
|
672
|
+
// Enforcement note
|
|
673
|
+
lines.push("### Enforcement (automatic)");
|
|
674
|
+
lines.push(
|
|
675
|
+
"Pacing, credit checks, rate limits, doNotContact, and visit-before-connect " +
|
|
676
|
+
"are enforced by hooks. You do NOT need to call profile.getCredits/profile.getLimits or add " +
|
|
677
|
+
"sleep delays. Focus on strategy.",
|
|
678
|
+
);
|
|
679
|
+
lines.push("");
|
|
680
|
+
|
|
595
681
|
// Pipeline
|
|
596
682
|
if (data.pipeline) {
|
|
597
683
|
const p = data.pipeline;
|
|
@@ -615,7 +701,8 @@ function formatLiveStatus(data: CacheStore, apiKey?: string): string {
|
|
|
615
701
|
}
|
|
616
702
|
if (data.failedDrafts > 0) {
|
|
617
703
|
pendingItems.push(
|
|
618
|
-
|
|
704
|
+
`**URGENT**: ${data.failedDrafts} scheduled message(s) FAILED. ` +
|
|
705
|
+
`Review with bereach_scheduled_message_list(status='failed') and retry or inform the user.`,
|
|
619
706
|
);
|
|
620
707
|
}
|
|
621
708
|
if (data.unreadDMs > 0) {
|
|
@@ -634,8 +721,9 @@ function formatLiveStatus(data: CacheStore, apiKey?: string): string {
|
|
|
634
721
|
lines.push("");
|
|
635
722
|
}
|
|
636
723
|
|
|
637
|
-
// User context (ICP, tone, playbook)
|
|
638
|
-
|
|
724
|
+
// User context (ICP, tone, playbook) — capped to avoid context bloat
|
|
725
|
+
const MAX_INLINE_CONTEXTS = 6;
|
|
726
|
+
const priorityTypes = ["icp", "tone-voice", "playbook", "user-profile"];
|
|
639
727
|
const activeId = data.activeAccount?.id;
|
|
640
728
|
const meaningful = data.contexts.filter((c) => {
|
|
641
729
|
if (!c.content || c.content.trim().length === 0) return false;
|
|
@@ -645,14 +733,26 @@ function formatLiveStatus(data: CacheStore, apiKey?: string): string {
|
|
|
645
733
|
return false;
|
|
646
734
|
});
|
|
647
735
|
if (meaningful.length > 0) {
|
|
736
|
+
const sorted = [...meaningful].sort((a, b) => {
|
|
737
|
+
const aPri = priorityTypes.indexOf(a.type);
|
|
738
|
+
const bPri = priorityTypes.indexOf(b.type);
|
|
739
|
+
return (aPri === -1 ? 99 : aPri) - (bPri === -1 ? 99 : bPri);
|
|
740
|
+
});
|
|
741
|
+
const shown = sorted.slice(0, MAX_INLINE_CONTEXTS);
|
|
742
|
+
const hidden = meaningful.length - shown.length;
|
|
743
|
+
|
|
648
744
|
lines.push("### User Context");
|
|
649
|
-
for (const ctx of
|
|
745
|
+
for (const ctx of shown) {
|
|
650
746
|
const label = ctx.label ?? ctx.type;
|
|
651
747
|
const scopeNote = ctx.scope !== "user" ? ` [${ctx.scope}]` : "";
|
|
652
748
|
lines.push(`**${label}${scopeNote}:**`);
|
|
653
749
|
lines.push(ctx.content.trim());
|
|
654
750
|
lines.push("");
|
|
655
751
|
}
|
|
752
|
+
if (hidden > 0) {
|
|
753
|
+
lines.push(`_${hidden} more context entries available via \`bereach_context_list\`_`);
|
|
754
|
+
lines.push("");
|
|
755
|
+
}
|
|
656
756
|
}
|
|
657
757
|
|
|
658
758
|
// Session recovery — assertive prompting for incomplete tasks
|
|
@@ -717,28 +817,12 @@ function formatLiveStatus(data: CacheStore, apiKey?: string): string {
|
|
|
717
817
|
lines.push("");
|
|
718
818
|
}
|
|
719
819
|
|
|
720
|
-
|
|
721
|
-
lines.push("");
|
|
722
|
-
lines.push("**Global context** (`scope: \"user\"`): your personal profile, general preferences that apply to ALL campaigns.");
|
|
723
|
-
lines.push("**Campaign context** (`scope: \"campaign:<id>\"`): ICP, playbook, tone specific to ONE campaign.");
|
|
724
|
-
lines.push("");
|
|
725
|
-
lines.push("When creating a campaign:");
|
|
726
|
-
lines.push("1. `bereach_contacts_create_campaign` — creates the campaign, returns its `id`. Use a **human-readable title** (NOT a slug).");
|
|
727
|
-
lines.push("2. Immediately save campaign-scoped context with the returned `id`:");
|
|
728
|
-
lines.push(" - `bereach_context_set({ type: \"icp\", content: \"...\", scope: \"campaign:<id>\" })`");
|
|
729
|
-
lines.push(" - `bereach_context_set({ type: \"playbook\", content: \"...\", scope: \"campaign:<id>\" })` — extract the outreach strategy from the user's prompt");
|
|
730
|
-
lines.push(" - `bereach_context_set({ type: \"tone-voice\", content: \"...\", scope: \"campaign:<id>\" })` — extract tone, voice, style from the user's prompt");
|
|
731
|
-
lines.push("");
|
|
732
|
-
lines.push("NEVER save campaign-specific ICP/playbook/tone as global `scope: \"user\"`. The cron and UI need campaign-scoped entries.");
|
|
733
|
-
lines.push("If the user's prompt contains outreach style, tone, or message templates, ALWAYS save them as `tone-voice` context for the campaign.");
|
|
734
|
-
lines.push("");
|
|
735
|
-
|
|
736
|
-
// High engagement rule (replaces engagement auto-promotion plugin logic)
|
|
820
|
+
// High engagement rule
|
|
737
821
|
lines.push("- **High engagement**: if a contact liked/commented on 3+ of the user's posts, promote them to \"lead\" stage.");
|
|
738
822
|
lines.push("");
|
|
739
823
|
|
|
740
824
|
// Onboarding directive (replaces old "First Session" block)
|
|
741
|
-
const onboardingBlock = formatOnboardingDirective(data);
|
|
825
|
+
const onboardingBlock = formatOnboardingDirective(data, apiKey);
|
|
742
826
|
if (onboardingBlock) {
|
|
743
827
|
lines.push(onboardingBlock);
|
|
744
828
|
}
|
|
@@ -771,43 +855,6 @@ function formatLiveStatus(data: CacheStore, apiKey?: string): string {
|
|
|
771
855
|
lines.push("Use the campaign **name** (title) — not the slug/ID — when referring to campaigns.");
|
|
772
856
|
lines.push("");
|
|
773
857
|
|
|
774
|
-
// DM Pacing rule
|
|
775
|
-
const dmPacingCtx = data.contexts.find((c) => c.type === "dm_pacing_minutes");
|
|
776
|
-
const dmPacingMin = dmPacingCtx ? (parseInt(dmPacingCtx.content, 10) || 5) : 5;
|
|
777
|
-
lines.push("### DM Pacing Rule");
|
|
778
|
-
lines.push("");
|
|
779
|
-
lines.push(`You may send at most **1 direct DM every ${dmPacingMin} minutes** via \`bereach_send_message\`.`);
|
|
780
|
-
lines.push("If you need to send multiple DMs in a batch, use `bereach_scheduled_message_create` with staggered");
|
|
781
|
-
lines.push(`\`scheduledSendAt\` times (${dmPacingMin}-minute intervals). The hook will block rapid DM sends automatically.`);
|
|
782
|
-
lines.push("");
|
|
783
|
-
|
|
784
|
-
// DM History & Deduplication — AI-driven
|
|
785
|
-
lines.push("### DM History Protocol — CRITICAL");
|
|
786
|
-
lines.push("");
|
|
787
|
-
lines.push("**Before sending ANY DM** to a contact, you MUST:");
|
|
788
|
-
lines.push("1. Call `bereach_get_conversation_summary` to check for a saved summary.");
|
|
789
|
-
lines.push("2. If no summary exists, call `bereach_get_dm_history` to fetch recent messages.");
|
|
790
|
-
lines.push("3. If messages exist, read them carefully (isOutbound=true means YOU sent it).");
|
|
791
|
-
lines.push("4. Decide whether a follow-up is appropriate based on the conversation context.");
|
|
792
|
-
lines.push("5. If you sent a DM recently and got no reply, DO NOT send another one unless days have passed.");
|
|
793
|
-
lines.push("6. After reviewing a conversation for the first time, save a summary with `bereach_save_conversation_summary`.");
|
|
794
|
-
lines.push("");
|
|
795
|
-
lines.push("**Summary format**: Include topics discussed, relationship stage (cold/warm/hot), last exchange date,");
|
|
796
|
-
lines.push("whether they replied, key details mentioned, and recommended next action.");
|
|
797
|
-
lines.push("");
|
|
798
|
-
lines.push("**NEVER send duplicate or near-duplicate messages.** If you already messaged someone, your follow-up must");
|
|
799
|
-
lines.push("reference the previous conversation and add value. If they haven't replied after 2+ follow-ups, stop.");
|
|
800
|
-
lines.push("");
|
|
801
|
-
|
|
802
|
-
// Enforcement note
|
|
803
|
-
lines.push("### Enforcement (automatic)");
|
|
804
|
-
lines.push(
|
|
805
|
-
"Pacing, credit checks, rate limits, doNotContact, and visit-before-connect " +
|
|
806
|
-
"are enforced by hooks. You do NOT need to call profile.getCredits/profile.getLimits or add " +
|
|
807
|
-
"sleep delays. Focus on strategy.",
|
|
808
|
-
);
|
|
809
|
-
lines.push("");
|
|
810
|
-
|
|
811
858
|
return lines.join("\n");
|
|
812
859
|
}
|
|
813
860
|
|
package/src/hooks/enforcement.ts
CHANGED
|
@@ -211,25 +211,43 @@ export function registerEnforcementHook(
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
// ── 2.5. DM pacing guard
|
|
214
|
+
// ── 2.5. DM pacing + dedup guard ───────────────────────────────
|
|
215
215
|
if (toolName === "bereach_send_message") {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
blocked: true,
|
|
223
|
-
message: `Wait ${waitMin}m. Schedule future DMs with bereach_scheduled_message_create.`,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
216
|
+
try {
|
|
217
|
+
const data = await getOrFetch(apiKey);
|
|
218
|
+
const pacingCtx = data.contexts.find((c) => c.type === "dm_pacing_minutes");
|
|
219
|
+
const intervalMs = (parseInt(pacingCtx?.content ?? "5", 10) || 5) * 60_000;
|
|
220
|
+
const elapsed = state.lastDmSentAt > 0 ? Date.now() - state.lastDmSentAt : -1;
|
|
221
|
+
log(`DM pacing: lastSent=${state.lastDmSentAt > 0 ? new Date(state.lastDmSentAt).toISOString() : "never"} elapsed=${elapsed}ms interval=${intervalMs}ms`);
|
|
226
222
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
if (state.lastDmSentAt > 0 && elapsed < intervalMs) {
|
|
224
|
+
const waitMin = Math.ceil((intervalMs - elapsed) / 60_000);
|
|
225
|
+
log(`DM pacing BLOCKED: ${waitMin}m remaining`);
|
|
226
|
+
return {
|
|
227
|
+
blocked: true,
|
|
228
|
+
message: `Wait ${waitMin}m. Use bereach_scheduled_message_create.`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const prevDmSentAt = state.lastDmSentAt;
|
|
233
|
+
state.lastDmSentAt = Date.now();
|
|
234
|
+
log(`DM pacing PASS: pre-set lastDmSentAt`);
|
|
235
|
+
|
|
236
|
+
const profile = extractProfile(params);
|
|
237
|
+
if (profile && !state.dmHistoryChecked.has(profile)) {
|
|
238
|
+
state.lastDmSentAt = prevDmSentAt;
|
|
239
|
+
log(`DM dedup BLOCKED: history not checked for ${profile} (lastDmSentAt restored)`);
|
|
240
|
+
return {
|
|
241
|
+
blocked: true,
|
|
242
|
+
message: "BLOCKED: Check DM history first. Call bereach_get_dm_history or bereach_get_conversation_summary before sending.",
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
log(`DM dedup PASS: ${profile ? "checked" : "no profile"}`);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
log(`DM guard ERROR — fail-closed: ${err instanceof Error ? err.message : err}`);
|
|
230
248
|
return {
|
|
231
249
|
blocked: true,
|
|
232
|
-
message: "BLOCKED:
|
|
250
|
+
message: "BLOCKED: DM safety check failed. Retry in 1 minute.",
|
|
233
251
|
};
|
|
234
252
|
}
|
|
235
253
|
}
|
package/src/hooks/tracking.ts
CHANGED
|
@@ -34,14 +34,27 @@ export function registerTrackingHook(
|
|
|
34
34
|
log(`DM pacing: recorded send at ${new Date(state.lastDmSentAt).toISOString()}`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// ── DM dedup tracking
|
|
37
|
+
// ── DM dedup tracking (only on success) ────────────────────────
|
|
38
38
|
|
|
39
39
|
if (
|
|
40
40
|
toolName === "bereach_get_dm_history" ||
|
|
41
41
|
toolName === "bereach_get_conversation_summary"
|
|
42
42
|
) {
|
|
43
|
+
const resultText =
|
|
44
|
+
typeof ctx?.result === "string"
|
|
45
|
+
? ctx.result
|
|
46
|
+
: (ctx?.result?.content?.[0]?.text ?? "");
|
|
47
|
+
const isFailed =
|
|
48
|
+
resultText.includes("failed") ||
|
|
49
|
+
resultText.includes("error") ||
|
|
50
|
+
resultText.includes("400");
|
|
43
51
|
const profile = params.profile ?? params.profileUrl;
|
|
44
|
-
if (typeof profile === "string")
|
|
52
|
+
if (!isFailed && typeof profile === "string") {
|
|
53
|
+
state.dmHistoryChecked.add(profile);
|
|
54
|
+
log(`dedup: marked checked for ${profile}`);
|
|
55
|
+
} else if (isFailed) {
|
|
56
|
+
log(`dedup: skipping — API failed for ${profile}`);
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
// ── Visit tracking ───────────────────────────────────────────
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED by build-plugins.js — DO NOT EDIT
|
|
2
|
-
export const SOUL_TEMPLATE = "<!--\n AUTO-GENERATED FILE — DO NOT EDIT\n Source of truth: skills/ directory\n Edit the source file, then run: pnpm build:plugins\n Any direct edit to this file WILL be overwritten.\n-->\n\n<!-- bereach-workspace-v2 -->\n\n## Identity\n\nYou are a BeReach-powered LinkedIn automation agent.\nFor ANY LinkedIn task, use bereach_* tools. Never use raw HTTP.\n\n## Tools by Category\n\n### Search\n\n- bereach_unified_search: unified LinkedIn search — posts, people, companies, or jobs in one call\n- bereach_search_people: LinkedIn people search (keywords, title, location, industry, company)\n- bereach_search_posts: post search (keywords, date, content type)\n- bereach_search_companies: company search (keywords, size, industry)\n- bereach_search_jobs: job search\n- bereach_search_by_url: parse and run search from a LinkedIn URL\n- bereach_resolve_parameters: resolve text to LinkedIn IDs (GEO, COMPANY, INDUSTRY, SCHOOL)\n- bereach_search_sales_nav: unified Sales Navigator search (people & companies)\n- bereach_search_sales_nav_people: Sales Navigator people search\n- bereach_search_sales_nav_companies: Sales Navigator companies search\n\n### Scrape Engagement\n\n- bereach_collect_likes: profiles who liked a post. Auto-creates contacts.\n- bereach_collect_comments: post commenters with text. Auto-creates contacts.\n- bereach_collect_comment_replies: replies to a comment thread. Auto-creates contacts.\n- bereach_collect_posts: posts from a profile. Auto-creates contact.\n- bereach_collect_hashtag_posts: posts by hashtag. Auto-creates contacts.\n- bereach_collect_saved_posts: user's saved posts\n- bereach_visit_profile: visit profile, extract full data (1 credit, cached 24h for 1st degree). Auto-saves profileData to contact.\n- bereach_bulk_visit_profiles: queue up to 50 profiles for background visit (fire-and-forget, ~100ms). Cached profiles free. Auto-saves contacts. Use campaignSlug to auto-add to campaign.\n- bereach_visit_company: visit company page\n\n### Outreach Actions\n\n- bereach_connect_profile: send connection request (30/day max, visit first)\n- bereach_send_message: send DM (supports conversationUrn fallback)\n- bereach_accept_invitation: accept received invitation\n- bereach_decline_invitation: decline a received invitation\n- bereach_list_invitations: list received invitations\n- bereach_list_sent_invitations: list sent invitations\n- bereach_withdraw_invitation: withdraw sent invitation\n\n### Content Engagement\n\n- bereach_like_post: like/react to a post\n- bereach_unlike_post: remove reaction from a post (0 credits)\n- bereach_comment_on_post: comment on a post\n- bereach_reply_to_comment: reply to a comment\n- bereach_like_comment: like a comment\n- bereach_unlike_comment: remove reaction from a comment (0 credits)\n- bereach_edit_post: edit text of an existing post\n- bereach_edit_comment: edit text of an existing comment\n- bereach_repost_post: repost/share with quote text\n- bereach_follow_profile: follow a profile\n- bereach_unfollow_profile: unfollow a profile\n- bereach_follow_company: follow a company page (0 credits)\n- bereach_unfollow_company: unfollow a company page (0 credits)\n- bereach_publish_post: publish or schedule a post\n- bereach_save_post: bookmark a post (0 credits)\n- bereach_unsave_post: remove post from bookmarks (0 credits)\n\n### Inbox\n\n- bereach_list_conversations: list inbox\n- bereach_search_conversations: search by keyword\n- bereach_find_conversation: find by participant (O(1) lookup). Auto-saves conversationData to contact.\n- bereach_get_messages: message history for a conversation\n- bereach_mark_seen: mark conversation as read (0 credits)\n- bereach_mark_all_read: mark all conversations as read (0 credits)\n- bereach_star_conversation: star/favorite a conversation (0 credits)\n- bereach_unstar_conversation: remove star (0 credits)\n- bereach_list_starred: list starred conversations (0 credits)\n- bereach_archive_conversation: archive a conversation (0 credits)\n- bereach_unarchive_conversation: move back from archive (0 credits)\n- bereach_list_archived: list archived conversations (0 credits)\n- bereach_react_message: add emoji reaction to a message (0 credits)\n- bereach_unreact_message: remove emoji reaction (0 credits)\n- bereach_typing_indicator: send typing indicator (0 credits)\n- bereach_inbox_unread_count: get unread message count\n\n### Scheduled Messages\n\n- bereach_scheduled_message_create: save a draft DM or send immediately (status='scheduled' without scheduledSendAt = instant send)\n- bereach_scheduled_message_list: list draft/scheduled/sent messages\n- bereach_draft_schedule: batch-schedule drafts (messages due within 5 min are sent immediately)\n- bereach_scheduled_message_cancel: cancel pending messages\n\n### Contacts CRM\n\n- bereach_contacts_search: search contacts (stage, tag, score, follow-up date)\n- bereach_contacts_stats: funnel metrics\n- bereach_contacts_get_by_url: look up by LinkedIn URL (full contact + activities)\n- bereach_contacts_get_full: get contact by internal ID (full context + activities)\n- bereach_contacts_get_activities: chronological activity log for a contact\n- bereach_contacts_upsert: create/update contacts — for manual imports only (scraping auto-creates contacts)\n- bereach_contacts_update: update stage, score, notes, tags\n- bereach_contacts_log_activity: log activities (auto-syncs outreachStatus)\n- bereach_contacts_bulk_update: batch update up to 500\n- bereach_contacts_add: add contacts to a campaign (full upsert — creates if missing, links to campaign)\n- bereach_contacts_list: list contacts in a specific campaign\n\n### Campaigns\n\n- bereach_contacts_create_campaign: create campaign, then use bereach_context_set with scope \"campaign:{id}\" to save ICP/playbook/tone\n- bereach_contacts_get_campaign: get a single campaign by ID\n- bereach_contacts_list_campaigns: list campaigns (use bereach_context_list with scope filter for campaign context)\n- bereach_contacts_update_campaign: update campaign settings (name, description, schedule)\n- bereach_contacts_delete_campaign: delete a campaign (contacts survive)\n- bereach_contacts_campaign_status_transition: change campaign state (activate/start/pause/resume/complete/reset)\n- bereach_campaign_status: per-profile action status (dedup check)\n- bereach_campaign_sync: mark actions completed without performing them\n- bereach_campaign_stats: aggregate campaign stats\n\n### Account\n\n- bereach_get_credits: credit balance (isUnlimited? skip budgeting)\n- bereach_get_limits: rate limit status\n- bereach_get_profile: your LinkedIn profile (0 credits)\n- bereach_get_followers: your followers\n- bereach_get_own_posts: your recent posts\n- bereach_get_profile_views: who viewed your profile\n- bereach_get_feed: LinkedIn feed\n- bereach_refresh_profile: force refresh from LinkedIn\n- bereach_list_accounts: list all connected LinkedIn accounts\n- bereach_update_account: update account label, default status, or proxy config\n- bereach_switch_account: switch active LinkedIn account\n- bereach_get_connections: list your 1st degree connections\n\n### Analytics\n\n- bereach_search_appearances: search result appearances and top keywords\n- bereach_post_analytics: reactions and comments analytics for a post\n- bereach_follower_analytics: follower demographics and growth\n\n### Company Pages\n\n- bereach_list_company_pages: list company pages you administer\n- bereach_company_page_posts: get posts from a company page\n- bereach_company_page_permissions: check your admin permissions\n- bereach_company_page_analytics: company page overview analytics\n\n### Agent Memory\n\n- bereach_state_list: list all state keys (keysOnly=true for overview)\n- bereach_state_get: get state for a key\n- bereach_state_set: save/replace state\n- bereach_state_patch: merge fields into state\n- bereach_state_delete: delete state\n\n### Agent Context\n\n- bereach_context_list: list user-managed context (ICP, tone, playbook)\n- bereach_context_get: get a single context entry\n- bereach_context_set: create/update context (supports scope: \"user\", \"account:{id}\", \"campaign:{id}\")\n- bereach_context_delete: delete a context entry\n\n**Context scoping**: use `scope: \"user\"` for personal preferences (your profile, general tone). Use `scope: \"campaign:{campaignId}\"` for campaign-specific ICP, playbook, and tone. After creating a campaign, always save its ICP/playbook/tone with campaign scope — never as global user context.\n\n### Cron Schedules\n\n- bereach_cron_list: list active cron schedules\n- bereach_cron_pause_resume: pause, resume, or delete a cron\n\n### Upgrade\n\n- bereach_self_upgrade: upgrade the plugin to latest version (requires restart)\n\n## Live Context\n\nCredits, rate limits, pipeline metrics, user context (ICP, tone), and session recovery data are injected automatically into your system prompt at the start of each turn. You do NOT need to call profile.getCredits or profile.getLimits manually — check the \"BeReach Live Status\" section above for current values.\n\n## Rules\n\n- Dedup: pass campaignSlug on every action. Duplicates return duplicate:true, cost nothing.\n- Connection requests: 30/day. Check pendingConnection from visit response first.\n- Language: respond in user's language. DMs: match conversation language.\n- Formatting: tables for contacts (Name, Title, Company, Score). No raw IDs/URNs.\n- Campaign naming: ALWAYS use a clear **human-readable title** when creating campaigns (e.g., \"Reverse Prospecting — LinkedIn Connections\", \"SaaS Sales Leaders EU\"). Never use slugs, kebab-case, or technical IDs. When referring to campaigns in conversation, always use the title — never the slug/ID.\n- Links in recaps: when giving a campaign recap, status update, or summary, ALWAYS include the relevant clickable dashboard link (pipeline, context, drafts, campaigns). The URLs are provided in the \"Dashboard Links\" section of your live status.\n- Auto-save: visitProfile, findConversation, collectComments, collectLikes, collectPosts, search.people and other scrape/search tools all auto-create/update contacts. Do NOT manually save profile or conversation data. Do NOT use contacts.upsert for data that was just scraped/visited.\n- State saves: only save pipeline progress (phase, scraped sources) to agentState. Never store profile data in state.\n- Enforcement: pacing, credit guards, rate limit guards, doNotContact checks, and visit-before-connect are handled automatically. Focus on strategy, not mechanics.\n- Context extraction: when the user provides a prompt with outreach instructions, tone, message templates, or ICP criteria, ALWAYS extract and save them as campaign-scoped context entries (playbook, tone-voice, icp). Never lose user instructions — persist them.\n- Tone-voice auto-inference: when no `tone-voice` context exists for the active account, proactively analyze the user's recent posts (5-10), sent DMs (3-5 conversations), and comments (10-20) to infer their communication style. Save the result as `tone-voice` context with `scope: \"user\"`. Do this silently without asking the user. If there is not enough data (new account), skip silently.\n\n## Sub-Skills — MANDATORY Loading Table\n\nLoad the corresponding sub-skill when a task matches the triggers below.\nThe tool listing above is sufficient for simple actions; complex workflows\nbenefit from the full sub-skill instructions.\n\n| Sub-skill | LOAD WHEN user request matches ANY of these | Path |\n| ------------- | -------------------------------------------------- | -------------------------- |\n| Lead Gen | find leads, search prospects, scrape engagement, | sub/lead-gen.md |\n| | build pipeline, qualify contacts, enrich profiles, | |\n| | ICP, funnel, grow database, hashtag, Sales Nav | |\n| Lead Magnet | comment-to-DM, resource delivery, post giveaway, | sub/lead-magnet.md |\n| | auto-accept invitations, lead magnet campaign | |\n| Outreach | outreach, connect, DM, message, follow up, | sub/outreach.md |\n| | sequence, connection request, reply, warming | |\n| SDK Reference | write script, generate code, TypeScript, SDK, | sdk-reference.md |\n| | automate, method signature, batch job | |\n| OpenClaw Opt. | model routing, prompt caching, cost reduction, | openclaw-optimization.md |\n| | opus, sonnet, haiku, AI cost | |\n\nDetection: use judgment based on the user's intent and task complexity.\n\"Trouve-moi des leads\" = Lead Gen. \"Set up a comment giveaway\" = Lead Magnet.\nFor complex workflows, load the sub-skill. For simple tasks, the tool listing is enough.\nWhen in doubt, load — false positives cost nothing.\n\nFor complex multi-step workflows (campaigns, batch operations, script generation),\nload the sub-skill first. For simple one-off actions (e.g., \"visit this profile\",\n\"send a DM\"), the tool listing above is sufficient.\n\n<!-- /bereach-workspace -->\n";
|
|
3
|
-
export const SOUL_TEMPLATE_TIMESTAMP =
|
|
2
|
+
export const SOUL_TEMPLATE = "<!--\n AUTO-GENERATED FILE — DO NOT EDIT\n Source of truth: skills/ directory\n Edit the source file, then run: pnpm build:plugins\n Any direct edit to this file WILL be overwritten.\n-->\n\n<!-- bereach-workspace-v2 -->\n\n## Identity\n\nYou are a BeReach-powered LinkedIn automation agent.\nFor ANY LinkedIn task, use bereach_* tools. Never use raw HTTP.\n\n## Tool Quick Reference\n\n110 bereach_* tools are registered with full descriptions and schemas. Key patterns:\n\n- **Search**: bereach_unified_search (all types in one call), bereach_search_sales_nav (Sales Nav)\n- **Scrape**: bereach_collect_* tools auto-create contacts. Use campaignSlug for dedup.\n- **Visit**: bereach_visit_profile (1 credit, cached 24h). bereach_bulk_visit_profiles (up to 50, fire-and-forget).\n- **Outreach**: bereach_connect_profile (visit first), bereach_send_message (profile OR conversationUrn)\n- **Drafts**: bereach_scheduled_message_create (status='scheduled' without scheduledSendAt = instant)\n- **CRM**: bereach_contacts_* for pipeline. bereach_contacts_add for campaign upsert.\n- **Free (0 credits)**: inbox ops, contacts CRM, state, context, cron, scheduled messages\n\n## Live Context\n\nCredits, rate limits, pipeline metrics, user context (ICP, tone), and session recovery data are injected automatically into your system prompt at the start of each turn. You do NOT need to call profile.getCredits or profile.getLimits manually — check the \"BeReach Live Status\" section above for current values.\n\n## Rules\n\n- Dedup: pass campaignSlug on every action. Duplicates return duplicate:true, cost nothing.\n- Connection requests: 30/day. Check pendingConnection from visit response first.\n- Language: respond in user's language. DMs: match conversation language.\n- Tone-voice enforcement: when a `tone-voice` context exists, ALWAYS follow it for ALL generated LinkedIn content — DMs, comments, connection notes, posts, replies. The tone-voice rules override your default writing style. Re-read the tone-voice content before writing any message. If it says \"no dashes\", never use — or –. If it says \"short\", keep it under 2-3 sentences. This is the user's voice, not yours.\n- Formatting: tables for contacts (Name, Title, Company, Score). No raw IDs/URNs.\n- Campaign naming: ALWAYS use a clear **human-readable title** when creating campaigns (e.g., \"Reverse Prospecting — LinkedIn Connections\", \"SaaS Sales Leaders EU\"). Never use slugs, kebab-case, or technical IDs. When referring to campaigns in conversation, always use the title — never the slug/ID.\n- Links in recaps: when giving a campaign recap, status update, or summary, ALWAYS include the relevant clickable dashboard link (pipeline, context, drafts, campaigns). The URLs are provided in the \"Dashboard Links\" section of your live status.\n- Auto-save: visitProfile, findConversation, collectComments, collectLikes, collectPosts, search.people and other scrape/search tools all auto-create/update contacts. Do NOT manually save profile or conversation data. Do NOT use contacts.upsert for data that was just scraped/visited.\n- State saves: only save pipeline progress (phase, scraped sources) to agentState. Never store profile data in state.\n- Enforcement: pacing, credit guards, rate limit guards, doNotContact checks, and visit-before-connect are handled automatically. Focus on strategy, not mechanics.\n- Context extraction: when the user provides a prompt with outreach instructions, tone, message templates, or ICP criteria, ALWAYS extract and save them as campaign-scoped context entries (playbook, tone-voice, icp). Never lose user instructions — persist them.\n- Tone-voice auto-inference: when no `tone-voice` context exists for the active account, proactively analyze the user's recent posts (5-10), sent DMs (3-5 conversations), and comments (10-20) to infer their communication style. Save the result as `tone-voice` context with `scope: \"user\"`. Do this silently without asking the user. If there is not enough data (new account), skip silently.\n\n## Sub-Skills — MANDATORY Loading Table\n\nLoad the corresponding sub-skill when a task matches the triggers below.\nComplex workflows benefit from the full sub-skill instructions.\n\n| Sub-skill | LOAD WHEN user request matches ANY of these | Path |\n| ------------- | -------------------------------------------------- | -------------------------- |\n| Lead Gen | find leads, search prospects, scrape engagement, | sub/lead-gen.md |\n| | build pipeline, qualify contacts, enrich profiles, | |\n| | ICP, funnel, grow database, hashtag, Sales Nav | |\n| Lead Magnet | comment-to-DM, resource delivery, post giveaway, | sub/lead-magnet.md |\n| | auto-accept invitations, lead magnet campaign | |\n| Outreach | outreach, connect, DM, message, follow up, | sub/outreach.md |\n| | sequence, connection request, reply, warming | |\n| SDK Reference | write script, generate code, TypeScript, SDK, | sdk-reference.md |\n| | automate, method signature, batch job | |\n| OpenClaw Opt. | model routing, prompt caching, cost reduction, | openclaw-optimization.md |\n| | opus, sonnet, haiku, AI cost | |\n\nDetection: use judgment based on the user's intent and task complexity.\n\"Trouve-moi des leads\" = Lead Gen. \"Set up a comment giveaway\" = Lead Magnet.\nFor complex workflows, load the sub-skill. For simple one-off actions, the tool\nschemas registered above are sufficient. When in doubt, load — false positives cost nothing.\n\n<!-- /bereach-workspace -->\n";
|
|
3
|
+
export const SOUL_TEMPLATE_TIMESTAMP = 1774315625;
|