@vruum/skills 0.6.49 → 0.6.51

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vruum",
3
- "version": "0.6.49",
3
+ "version": "0.6.51",
4
4
  "description": "Vruum AI skills + remote MCP server for B2B GTM teams. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis, paired with the full Vruum MCP tool surface over OAuth 2.1.",
5
5
  "author": {
6
6
  "name": "Vruum AI",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vruum",
3
- "version": "0.6.49",
3
+ "version": "0.6.51",
4
4
  "description": "Vruum AI skills + remote MCP server for B2B GTM teams. Skills for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis, paired with the full Vruum MCP tool surface over OAuth 2.1.",
5
5
  "author": {
6
6
  "name": "Vruum AI",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vruum/skills",
3
- "version": "0.6.49",
3
+ "version": "0.6.51",
4
4
  "description": "Vruum AI skills for Claude Code, Claude Desktop, Codex CLI, and any AI assistant with a skill directory. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis. Pairs with the Vruum MCP server at https://api.vruum.ai/mcp.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -41,5 +41,5 @@
41
41
  "outreach",
42
42
  "gtm"
43
43
  ],
44
- "contentHash": "08900b7700c6e749f7d02081bf8a319ccac72e36b01c794594b4f07d44b2db84"
44
+ "contentHash": "7b005a0d67d55681a74103c681916d07dbaebfc7b86107220ac0f22d7db0a0b9"
45
45
  }
@@ -64,7 +64,7 @@ Once the user says go (or picks a focus area), pull the lightweight message queu
64
64
 
65
65
  **Frame by conversation state, not sequence number.** `first_content_touch=true` means this person has NEVER received a content-bearing message — their history is only connection requests (blank or noted). That item is an OPENER no matter its T-number (a T3 email that fell back from a never-accepted LinkedIn connect is still the first thing this person reads from the sender). `first_content_touch=false` means real prior contact exists — follow-up framing is legitimate. If the field is null/absent (rows drafted before this signal shipped, or an older backend), fall back to the sequence_number heuristic: seq 1 = opener, seq ≥2 = follow-up. The full block lives on the `get_outreach_review` item as `conversation_state` (`{v, first_content_touch, prior_context: none|note_only|inbound_only|content, connection_note_text, last_delivered_channel, designed_channel, channel_rewrite_reason, channel_fallback}`) — same absent-block fallback applies when `v` is unrecognized.
66
66
 
67
- **Authoring mode (needs_draft items).** The backend no longer writes outreach prose — touches arrive as `needs_draft` items carrying the decision context (channel, touch number, signals) and no content. These are not rewrites; they are blank pages. For each needs_draft item the subagent AUTHORS the message: check the person's research freshness from the review item itself — `person_researched_at` / `company_researched_at` / `research_status` are on the payload, no extra fetch needed (older than ~14 days or missing → research first with WebSearch + the research reads, and persist what you learn via `research` action=save_person with `payload.person_id` set to the review item's person UUID (update-only — the response's `updated_fields` confirms what landed; NEVER pass the UUID as the facade `id` argument), plus action=save_company when you learned something about the company, so it compounds), then write the touch from scratch in the seller's voice against the same quality standards as any review, then check it — call `check_prose` with `{item_id: <message id>, item_type: "message", content: <draft>}` and treat the `failures[]` as an advisory checklist: fix what you agree with; severity `block` failures are hard stops that bounce at post time — today: channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener) — then submit it via `manage_messages` action=edit with the content and the returned `rules_version` as `client_rules_version` — that transitions the item to a normal draft — and approve only what the user's standing instructions allow. (The edit re-runs the same lint server-side; annotations are recorded to the label corpus, never rejected — a `block`-severity draft bounces with `prose_gate_blocked` + `failures[].fix` and a `span` naming the exact offending text.) Inbound replies also arrive as needs_draft (category inbound_reply, with the conversation attached): author the reply with full thread context. If a prospect turns out to be a bad fit at authoring time, skip the item and say why — authoring is the second qualification gate, not an obligation to write.
67
+ **Authoring mode (needs_draft items).** The backend no longer writes outreach prose — touches arrive as `needs_draft` items carrying the decision context (channel, touch number, signals) and no content. These are not rewrites; they are blank pages. The full review item includes `manual_notes`, up to 10 recent person notes, newest first. Each note can contain 4,000 characters, each person can contain 8,000, and a full response can contain 30,000 across repeated items. A cut note ends with `...[truncated]`; never treat that incomplete note as an exact saved draft. Use the notes as seller context. When campaign instructions name a complete saved draft in `manual_notes`, treat that named draft as authoritative instead of replacing it with generic researched copy; preserve its wording unless the actual channel limit requires compression. For each needs_draft item the subagent AUTHORS the message: check the person's research freshness from the review item itself — `person_researched_at` / `company_researched_at` / `research_status` are on the payload, no extra fetch needed (older than ~14 days or missing → research first with WebSearch + the research reads, and persist what you learn via `research` action=save_person with `payload.person_id` set to the review item's person UUID (update-only — the response's `updated_fields` confirms what landed; NEVER pass the UUID as the facade `id` argument), plus action=save_company when you learned something about the company, so it compounds), then write the touch in the seller's voice against the same quality standards as any review, then check it — call `check_prose` with `{item_id: <message id>, item_type: "message", content: <draft>}` and treat the `failures[]` as an advisory checklist: fix what you agree with; severity `block` failures are hard stops that bounce at post time — today: channel character caps (cut to fit) and prior-outreach references on a first content touch (rewrite as an opener) — then submit it via `manage_messages` action=edit with the content and the returned `rules_version` as `client_rules_version` — that transitions the item to a normal draft — and approve only what the user's standing instructions allow. (The edit re-runs the same lint server-side; annotations are recorded to the label corpus, never rejected — a `block`-severity draft bounces with `prose_gate_blocked` + `failures[].fix` and a `span` naming the exact offending text.) Inbound replies also arrive as needs_draft (category inbound_reply, with the conversation attached): author the reply with full thread context. If a prospect turns out to be a bad fit at authoring time, skip the item and say why — authoring is the second qualification gate, not an obligation to write.
68
68
 
69
69
  **Dispatch order within the authoring lane.** Pull the WARM lane directly — `search` type=messages with `warm_only=true, status=needs_draft, sort_by=expiring, fields=compact` returns exactly the LinkedIn follow-ups to accepted connections, nearest sweep deadline first, immune to page-1 truncation (warm rows are old by construction, so the default newest-first sort buries them under the cold backlog). Dispatch that lane first — they accepted the invite and the follow-up is the first real message they read. For the remaining lanes, compact rows carry `connection_status` if you need to spot stragglers, and `sort_by=expiring` works on any needs_draft pull.
70
70
 
@@ -131,7 +131,7 @@ You are an outreach review agent.
131
131
 
132
132
  Message IDs: {comma_separated_message_ids}
133
133
 
134
- Call get_outreach_review with message_ids="{comma_separated_message_ids}" and content_length="full" to load your assigned messages.
134
+ Call get_outreach_review with message_ids="{comma_separated_message_ids}" and content_length="full" to load your assigned messages. Inspect `manual_notes`; when campaign instructions name a saved draft there, treat that note as the authoritative source.
135
135
 
136
136
  For each message:
137
137
  1. Check structural compliance (touch sequence, char limits, channel)
@@ -158,7 +158,7 @@ Prospect: {person_name}, {title} at {company}
158
158
  Message type: T{sequence_number} — frame by the item's conversation_state, NOT the T-number: first_content_touch=true means this is an OPENER (their history is only connection requests; never reference prior outreach); false means a real follow-up; block absent → treat T{sequence_number}>=2 as follow-up
159
159
 
160
160
  Steps:
161
- 1. Call get_outreach_review with message_ids="{message_id}" and content_length="full" to get the current message, thread context, campaign instructions, and match analysis.
161
+ 1. Call get_outreach_review with message_ids="{message_id}" and content_length="full" to get the current message, thread context, campaign instructions, match analysis, and complete `manual_notes`. When campaign instructions name a saved draft there, treat that note as the authoritative source.
162
162
  2. Call fetch with type=person_research plus get_person_360 for this person to get everything we know.
163
163
  3. Call fetch with type=company_research to understand the company's product, positioning, and what problems it solves.
164
164
  4. Search the web for this prospect and their company to understand what they actually do, what challenges they face, what they post about.
@@ -146,7 +146,7 @@ The canonical candidate shape in `RESEARCH-ENGINE.md` is person-shaped — a com
146
146
  **Per company:**
147
147
  1. Pull up to `buyers_per_account` people matching the campaign's ICP titles/seniority, using the first available provider in this order (same order as discovery sourcing; apply `source_policy` before any call):
148
148
  - **Structured B2B provider** — e.g. Clay `find-and-enrich-contacts-at-company` with the title/seniority filter.
149
- - **LinkedIn / Sales Nav** people-at-known-company via `import_prospects action=sales_nav_search` — fine here because the company is fixed; the marquee-name skew applies to company discovery, not to enumerating a known committee. Mind LinkedIn quota.
149
+ - **Regular LinkedIn** people-at-known-company via `import_prospects action=linkedin_people_search` — fine here because the company is fixed; the marquee-name skew applies to company discovery, not to enumerating a known committee. It needs a connected LinkedIn account, not Sales Navigator, returns at most 25 people, and charges the shared LinkedIn daily activity budget used by reads and sends.
150
150
  - **Email finder** — Hunter via `search type=companies {domain, seniority}`.
151
151
  - **Web** — `WebSearch` for "{company} {title}" + team/about pages; the universal fallback.
152
152
  2. **Select by title fit, not visibility.** When the provider returns more than `buyers_per_account` matches, rank by ICP title/seniority fit — never by follower count, press coverage, or how often the name appears in research prose. The buyer with no public profile is often the one actually running the function; picking the famous name per account is the same marquee-name skew this skill warns about for Sales Nav.
@@ -186,7 +186,7 @@ Operator gives a brief like "Series A-C SaaS founders, US, 50-500 ppl" or "direc
186
186
  1. **Anchor on campaign ICP** — read the campaign's existing ICP/company profile (via `fetch` type=campaign and `fetch` type=settings subtype=profile) and merge with the operator's brief. Show a one-line synthesis ("OK so: Series A-C SaaS, US, 50-500 ppl, founder/CEO/CTO titles") and confirm before sourcing.
187
187
  2. **Take a source inventory — use the operator's actual toolbox, don't hardcode one provider.** Different operators have different prospecting tools connected. Take inventory of any MCP servers or CLIs this session can reach (inspect or search your available tools for terms like `clay`, `apollo`, `zoominfo`, `enrich`, `company`, `contacts`) and pick the highest-signal one. Prefer in this order:
188
188
  - **Structured B2B data / enrichment provider** (Clay, Apollo, ZoomInfo, Crunchbase, People Data Labs, Clearbit, …) — these firmographic-filter companies AND resolve the buying committee directly, and they reach the long tail, which is the entire point of discovery. If one is connected, it is the primary source. With Clay specifically, that's `find-and-enrich-company` (firmographic company pull) + `find-and-enrich-contacts-at-company` (committee). Mind provider credits / rate limits.
189
- - **LinkedIn / Sales Nav** via `import_prospects action=sales_nav_search` — fine to *resolve people at a company you already found*, but it over-samples well-known names, so never use it as the primary company-discovery channel.
189
+ - **Regular LinkedIn** via `import_prospects action=linkedin_people_search` — fine to *resolve people at a company you already found*, but it over-samples well-known names, so never use it as the primary company-discovery channel. Sales Navigator remains available through the separate `sales_nav_*` actions when its structured filters are necessary.
190
190
  - **Email finder** — Hunter via `search type=companies {domain, seniority}`, or the provider's own email step — to fill the contact emails Phase B needs.
191
191
  - **Web** (`WebSearch` / `WebFetch`) — always available; the universal fallback and a strong long-tail *company* finder (funding announcements, Crunchbase/PitchBook, vertical directories) even when a data provider is connected.
192
192