@audienti/cli 0.2.0-exora.31 → 0.2.0-exora.33

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/cli/audienti.mjs CHANGED
@@ -95,7 +95,7 @@ Discovery: prospects list|get ID|timeline ID [--limit N]|observation create|cita
95
95
  Qualification: campaigns qualification submit --file INPUT.json
96
96
  Campaign summary: campaign summary [--workspace ID] (campaign counts per state, and per campaign its audience, offer, playbook, goal stage, people, engaged and at-goal counts)
97
97
  Find prospects: campaign find CAMPAIGN_ID --count N --max-credits C (searches with the audience filters, checks each person, admits only fits; stops at N fits or the credit cap and reports the spend)
98
- campaign check CAMPAIGN_ID --url LINKEDIN_URL [--request-id UUID] (checks one person the same way; reuse the request ID after an interruption)
98
+ campaign check CAMPAIGN_ID --url LINKEDIN_URL [--request-id UUID] [--source-url URL --source-industry TEXT [--source-kind website|register|other] [--source-location TEXT] [--source-note TEXT]] (checks one person the same way; reuse the request ID after an interruption; a source confirms a company with no LinkedIn page)
99
99
  Target companies: campaign targets CAMPAIGN_ID [--limit N --after CURSOR] (reads the companies people must work at to join)
100
100
  campaign targets CAMPAIGN_ID --add acme.com,linkedin.com/company/globex [--remove old.com] | --set FILE|- (one company per line; saves a new revision)
101
101
  Playbooks: playbook list [--archived] [--limit N --cursor ID]|actions|show ID [--revision N]|save --file REQUEST.json|archive ID|restore ID|draft show ID|save ID --file REQUEST.json|discard ID [--expected-updated-at T]|publish ID [--request-id UUID]|delete ID|progress PARTICIPATION_ID|record --file REQUEST.json|event PARTICIPATION_ID --kind KIND --reason TEXT (audienti playbook SUBCOMMAND --help for the request shape)
@@ -25,6 +25,6 @@ export const campaignOperations={
25
25
  campaign_pace_set:{schema:z.object({...locator,perDay:z.union([z.number().int().min(1).max(1000),z.literal('derived')]).optional(),maxCredits:z.number().int().min(1).max(100000).nullable().optional()}).strict().refine(a=>a.perDay!==undefined||a.maxCredits!==undefined,{message:'Send perDay, maxCredits or both.'}),method:'POST',path:a=>detail(a)+'/pace',body:a=>({...(a.perDay===undefined?{}:{perDay:a.perDay}),...(a.maxCredits===undefined?{}:{maxCredits:a.maxCredits})}),scoped:true,description:'Save a Campaign pace. Pass the number the user gave, or "derived" to follow the confirmed sending accounts. An owner number stays until changed and is still capped by LinkedIn account and mailbox limits. maxCredits saves the credits a day the user approved for finding (null removes it); each day\'s find task carries it.'},
26
26
  campaign_targets_get:{schema:z.object({...locator,limit:z.number().int().min(1).max(500).optional(),after:z.string().regex(/^\d{1,7}$/).optional()}).strict(),method:'GET',path:a=>detail(a)+'/targets?'+qs(a),scoped:true,description:'Read a Campaign\'s target companies in pages of up to 500: domains and LinkedIn company URLs. Only people whose current company is one of them can join.'},
27
27
  campaign_targets_change:{schema:z.object({...locator,expectedRevision:z.number().int().positive(),add:z.array(z.string()).optional(),remove:z.array(z.string()).optional(),set:z.array(z.string()).optional()}).strict().refine(a=>(a.set===undefined)!==(a.add===undefined&&a.remove===undefined),{message:'Send set, or add and or remove.'}),method:'POST',path:a=>detail(a)+'/targets',body:a=>({expectedRevision:a.expectedRevision,...Object.fromEntries(['add','remove','set'].filter(k=>a[k]!==undefined).map(k=>[k,a[k]]))}),scoped:true,description:'Change a Campaign\'s target companies: add or remove domains (acme.com) and LinkedIn company URLs, or replace them all with set. Saves a new Campaign revision; an empty set removes the targets.'},
28
- campaign_prospect_check:{schema:z.object({...locator,url:z.string().max(1024),requestId:uuid,maxCharge:z.string().regex(/^\d{1,20}$/).optional()}).strict(),method:'POST',path:a=>detail(a)+'/prospect-checks',body:a=>({url:a.url,requestId:a.requestId,...(a.maxCharge!==undefined?{maxCharge:a.maxCharge}:{})}),scoped:true,recoverable:true,timeoutMs:90000,description:'Check one LinkedIn person URL for a Campaign and record the result. Fetches the person and current company (saved profiles are reused free; otherwise each fetch spends hosted credits), runs the pinned Audience rules and records a qualification with the profiles as evidence. outcome fit: admitted, or shortlisted while the Campaign is a Draft. held: an agent must judge a smart rule, a signal or an undecided rule. not_fit: a rule failed. already_found: already in or shortlisted for this Campaign; nothing written. charged and balance are pico USD (1 credit = 10,000,000,000). Generate requestId once and reuse it after an interruption so a retry never pays twice.'},
28
+ campaign_prospect_check:{schema:z.object({...locator,url:z.string().max(1024),requestId:uuid,maxCharge:z.string().regex(/^\d{1,20}$/).optional(),companySource:z.object({kind:z.enum(['website','register','other']),url:z.string().max(2048),industry:z.string().max(200),location:z.string().max(200).optional(),note:z.string().max(500).optional()}).strict().optional()}).strict(),method:'POST',path:a=>detail(a)+'/prospect-checks',body:a=>({url:a.url,requestId:a.requestId,...(a.maxCharge!==undefined?{maxCharge:a.maxCharge}:{}),...(a.companySource!==undefined?{companySource:a.companySource}:{})}),scoped:true,recoverable:true,timeoutMs:90000,description:'Check one LinkedIn person URL for a Campaign and record the result. Fetches the person and current company (saved profiles are reused free; otherwise each fetch spends hosted credits), runs the pinned Audience rules and records a qualification with the profiles as evidence. outcome fit: admitted, or shortlisted while the Campaign is a Draft. held: an agent must judge a smart rule, a signal or an undecided rule. not_fit: a rule failed. already_found: already in or shortlisted for this Campaign; nothing written. charged and balance are pico USD (1 credit = 10,000,000,000). Generate requestId once and reuse it after an interruption so a retry never pays twice. A company with no LinkedIn page cannot be read, so its industry rules stay undecided (reason company_page_missing). Send companySource {kind website|register|other, url, industry, location?, note?} from a page you fetched (its website, or a regulator register such as a central bank list of banks) to confirm it. The industry rule still applies to what the page states, and the receipt records the source.'},
29
29
  campaign_successor:{schema:z.object({...locator,requestId:uuid,expectedRevision:z.number().int(),fields}).strict(),method:'POST',path:a=>detail(a)+'/successors',body:a=>({requestId:a.requestId,expectedRevision:a.expectedRevision,fields:a.fields}),scoped:true},
30
30
  };
package/cli/campaigns.mjs CHANGED
@@ -3,7 +3,7 @@ import {readJsonFile} from './json-file.mjs';
3
3
  export async function campaignCli(args){
4
4
  const position=[],flags={};for(let i=0;i<args.length;i++){
5
5
  if(!args[i].startsWith('--')){position.push(args[i]);continue}
6
- const key=args[i].slice(2);if(!['profile','workspace','file','request-id','expected-revision','revision','archived','limit','after','q','state','operation','expected-state','reason','per-day','count','max-credits','url','add','remove','set'].includes(key)||!args[i+1]||args[i+1].startsWith('--')||Object.hasOwn(flags,key))throw new Error('Unknown or incomplete campaign option.');
6
+ const key=args[i].slice(2);if(!['profile','workspace','file','request-id','expected-revision','revision','archived','limit','after','q','state','operation','expected-state','reason','per-day','count','max-credits','url','add','remove','set','source-url','source-kind','source-industry','source-location','source-note'].includes(key)||!args[i+1]||args[i+1].startsWith('--')||Object.hasOwn(flags,key))throw new Error('Unknown or incomplete campaign option.');
7
7
  flags[key]=args[++i];
8
8
  }
9
9
  const command=position[1];
@@ -17,9 +17,13 @@ export async function campaignCli(args){
17
17
  console.log(JSON.stringify({data:report}));return;
18
18
  }
19
19
  if(command==='check'){
20
- if(position.length!==3||!flags.url)throw new Error('Use campaign check ID --url LINKEDIN_URL [--request-id UUID].');
20
+ if(position.length!==3||!flags.url)throw new Error('Use campaign check ID --url LINKEDIN_URL [--request-id UUID] [--source-url URL [--source-kind website|register|other] --source-industry TEXT [--source-location TEXT] [--source-note TEXT]].');
21
+ const sourceFlags=['source-url','source-kind','source-industry','source-location','source-note'].filter(key=>flags[key]!==undefined);
22
+ const missing=[['source-url','--source-url URL'],['source-industry','--source-industry TEXT']].filter(([key])=>flags[key]===undefined).map(([,text])=>text);
23
+ if(sourceFlags.length&&missing.length)throw new Error(`A company source needs its web address and the industry it states. Add ${missing.join(' and ')}.`);
24
+ const companySource=sourceFlags.length?{kind:String(flags['source-kind']??'website').toLowerCase(),url:flags['source-url'],industry:flags['source-industry'],...(flags['source-location']!==undefined?{location:flags['source-location']}:{}),...(flags['source-note']!==undefined?{note:flags['source-note']}:{})}:undefined;
21
25
  const {randomUUID}=await import('node:crypto');
22
- console.log(JSON.stringify(await callWorkspace('campaign_prospect_check',{workspaceId:flags.workspace,campaignId:position[2],url:flags.url,requestId:flags['request-id']??randomUUID()},configuration(flags.profile))));return;
26
+ console.log(JSON.stringify(await callWorkspace('campaign_prospect_check',{workspaceId:flags.workspace,campaignId:position[2],url:flags.url,requestId:flags['request-id']??randomUUID(),...(companySource?{companySource}:{})},configuration(flags.profile))));return;
23
27
  }
24
28
  if(command==='targets'){
25
29
  if(position.length!==3)throw new Error('Use campaign targets ID [--add a.com,b.com] [--remove a.com] [--set FILE|-] [--expected-revision N].');
@@ -7,6 +7,7 @@ const subject=z.object({kind:z.enum(['person','company']),prospectId:uuid}).stri
7
7
  const text=z.string().nullable().optional();
8
8
  const fields=z.object({name:text,title:text,companyName:text,location:text,bio:text,domain:text,description:text,imageUrl:text}).strict();
9
9
  const status=(values)=>z.object({status:z.enum(values),evidenceIds:z.array(uuid)}).strict();
10
+ const companyStatus=(values)=>z.object({status:z.enum(values),evidenceIds:z.array(uuid),source:z.object({kind:z.enum(['website','register','other']),url:z.string().max(2048),industry:z.string().max(200),location:z.string().max(200).optional(),note:z.string().max(500).optional()}).strict().optional()}).strict();
10
11
  const root=a=>`/workspaces/${a.workspaceId}/prospects`;
11
12
  const body=a=>Object.fromEntries(Object.entries(a).filter(([k])=>k!=='workspaceId'));
12
13
  function qs(a){const values={};for(const[k,v]of Object.entries(a)){if(['workspaceId','prospectId'].includes(k)||v===undefined)continue;if(v&&typeof v==='object')for(const[s,x]of Object.entries(v))values[k+s[0].toUpperCase()+s.slice(1)]=String(x);else values[k]=String(v);}return new URLSearchParams(values);}
@@ -24,5 +25,5 @@ export const discoveryOperations={
24
25
  prospect_record_observation:{schema:z.object({...request,kind:z.enum(['person','company']),prospectId:uuid.optional(),source:z.object({provider:z.string(),providerId:z.string().optional(),url:z.string().optional()}).strict(),fields,provenance:z.object({url:z.string(),excerpt:z.string().optional()}).strict().optional(),observedAt:z.string().nullable().optional(),skill:skill.optional()}).strict(),method:'POST',path:a=>root(a)+'/observations',body,scoped:true},
25
26
  prospect_record_citation:{schema:z.object({...request,source:endpoint,target:endpoint,edgeType:z.enum(['works_for','works_at','worked_at','references']),roles:z.array(z.object({roleId:z.string(),title:z.string(),current:z.boolean(),startDate:text,endDate:text,employmentType:text,observedAt:text}).strict()).optional(),evidenceIds:z.array(uuid).optional(),employmentSelection:z.object({action:z.enum(['set_primary','clear']),roleId:z.string().optional(),evidenceHash:z.string().optional()}).strict().optional()}).strict(),method:'POST',path:a=>root(a)+'/citations',body,scoped:true},
26
27
  prospect_record_evidence:{schema:z.object({...request,subject,campaignId:uuid.optional(),campaignRevision:z.number().int().positive().optional(),kind:z.enum(['signal','interpretation']),data:z.record(z.string(),z.unknown()),sourceUrl:text,observedAt:text,skill}).strict(),method:'POST',path:a=>root(a)+'/evidence',body,scoped:true},
27
- campaign_submit_qualification:{schema:z.object({...request,campaignId:uuid,campaignRevision:z.number().int().positive(),prospectId:uuid,companyProspectId:uuid.optional(),companyIcp:status(['pass','fail','unverified']),personIcp:status(['pass','fail','unverified']),signal:status(['match','no_match','unverified','not_required']),fit:z.object({status:z.enum(['high','moderate','low','no_fit','unscored']),evidenceIds:z.array(uuid),reason:z.string().optional()}).strict(),skill}).strict(),method:'POST',path:a=>`/workspaces/${a.workspaceId}/campaigns/${a.campaignId}/qualifications`,body:a=>Object.fromEntries(Object.entries(body(a)).filter(([k])=>k!=='campaignId')),scoped:true},
28
+ campaign_submit_qualification:{schema:z.object({...request,campaignId:uuid,campaignRevision:z.number().int().positive(),prospectId:uuid,companyProspectId:uuid.optional(),companyIcp:companyStatus(['pass','fail','unverified']),personIcp:status(['pass','fail','unverified']),signal:status(['match','no_match','unverified','not_required']),fit:z.object({status:z.enum(['high','moderate','low','no_fit','unscored']),evidenceIds:z.array(uuid),reason:z.string().optional()}).strict(),skill}).strict(),method:'POST',path:a=>`/workspaces/${a.workspaceId}/campaigns/${a.campaignId}/qualifications`,body:a=>Object.fromEntries(Object.entries(body(a)).filter(([k])=>k!=='campaignId')),scoped:true},
28
29
  };
@@ -8,15 +8,15 @@ export const playbookCommands = {
8
8
  },
9
9
  actions: {
10
10
  usage: 'playbook actions [--workspace ID] [--profile PATH]',
11
- about: 'What a playbook step may name: the step actions (id, channel, label), the step channels (LinkedIn, Email) and the event-rule names. Run it before playbook save. A step\'s actionId must be one of these ids and match the step channel. A save that names something else is refused with the wrong value, the allowed values (allowed) and this command (next).'
11
+ about: 'What a playbook step may name: the step actions (id, channel, label), the step channels (LinkedIn, Email) and the event-rule names, each with the words the screen shows (eventRules.whenLabels, eventRules.doLabels) and the actions that do not run yet (eventRules.notBuilt). Rule actions: stop_wait (End their wait), snooze (Wait before the next step), redirect_to_named_person (Add the person they name), ask_who_is_right (Ask who the right person is), tell_me (Tell me), hand_to_user (Put it in my queue), withdraw_pending_request (Withdraw the pending request). Run it before playbook save. A step\'s actionId must be one of these ids and match the step channel. A save that names something else is refused with the wrong value, the allowed values (allowed) and this command (next).'
12
12
  },
13
13
  show: {
14
14
  usage: 'playbook show PLAYBOOK_ID [--revision N] [--workspace ID] [--profile PATH]',
15
- about: 'The whole playbook (fields) and the campaigns that use it. Without --revision it returns the current one. PLAYBOOK_ID comes from playbook list. warnings is the Playbook check: what is wrong with the playbook (level error or warning, a message, the stage and step, and the default fix). An empty list means nothing to look at.'
15
+ about: 'The whole playbook (fields) and the campaigns that use it. Without --revision it returns the current one. PLAYBOOK_ID comes from playbook list. warnings is the Playbook check: what is wrong with the playbook (level error or warning, a message, the stage and step, the rule, and the default fix). An empty list means nothing to look at. actionLabels maps each rule action id the playbook uses to the words the screen shows, for example snooze to Wait before the next step.'
16
16
  },
17
17
  save: {
18
18
  usage: 'playbook save --file REQUEST.json [--workspace ID] [--profile PATH]',
19
- about: 'Create a playbook or save a new revision. Step action ids and channels: audienti playbook actions. The file is {"requestId": UUID, "expectedRevision": N, "id": PLAYBOOK_ID, "fields": {...}}. requestId: a fresh UUID; reuse it only to retry the same save. id: leave it out for a new playbook. expectedRevision: 0 for a new playbook, otherwise currentRevision from playbook show. A save from a stale revision is refused with revision_conflict and the response gives currentRevision. A playbook that cannot work (no off-ramp, or a stage nobody can reach) is refused with playbook_cannot_work and the list in errors. A saved playbook comes back with warnings, the Playbook check, for things that are legal but probably wrong. fields: name, stages (2 or more, each with a UUID key), goalKey (a stage key after the first stage), offRamps, and optionally eventRules (the example shows one). The actionId of a step is one of the ids from playbook actions. Field rules: audienti skills read playbook-design references/fields.md Your own reply labels: add customLabels [{"name":"asked_pricing","meaning":"They ask what it costs."}] and a rule with when custom.asked_pricing; a label with no rule opens a decision. Read them with audienti intent list --playbook ID.',
19
+ about: 'Create a playbook or save a new revision. Step action ids and channels: audienti playbook actions. The file is {"requestId": UUID, "expectedRevision": N, "id": PLAYBOOK_ID, "fields": {...}}. requestId: a fresh UUID; reuse it only to retry the same save. id: leave it out for a new playbook. expectedRevision: 0 for a new playbook, otherwise currentRevision from playbook show. A save from a stale revision is refused with revision_conflict and the response gives currentRevision. A playbook that cannot work (no off-ramp, a stage nobody can reach, or a rule with an action that does not run yet) is refused with playbook_cannot_work and the list in errors. A saved playbook comes back with warnings, the Playbook check, for things that are legal but probably wrong. fields: name, stages (2 or more, each with a UUID key), goalKey (a stage key after the first stage), offRamps, and optionally eventRules (the example shows one). The actionId of a step is one of the ids from playbook actions. Field rules: audienti skills read playbook-design references/fields.md Your own reply labels: add customLabels [{"name":"asked_pricing","meaning":"They ask what it costs."}] and a rule with when custom.asked_pricing; a label with no rule opens a decision. Read them with audienti intent list --playbook ID.',
20
20
  example: {
21
21
  requestId: 'UUID',
22
22
  expectedRevision: 0,
@@ -42,7 +42,7 @@ export const playbookCommands = {
42
42
  },
43
43
  draft_show: {
44
44
  usage: 'playbook draft show PLAYBOOK_ID [--workspace ID] [--profile PATH]',
45
- about: 'Your draft of a playbook: {draft: {baseRevision, fields, updatedAt, savedBy}} (savedBy is person or agent), or {draft: null} when there is none. The draft is the unsaved work in the playbook editor. It belongs to one person; an agent reads and changes the draft of the person who owns the agent. A campaign cannot use a draft, and playbook show and playbook save never touch it.'
45
+ about: 'Your draft of a playbook: {draft: {baseRevision, fields, actionLabels, updatedAt, savedBy}} (savedBy is person or agent; actionLabels maps each rule action id to the words the screen shows), or {draft: null} when there is none. The draft is the unsaved work in the playbook editor. It belongs to one person; an agent reads and changes the draft of the person who owns the agent. A campaign cannot use a draft, and playbook show and playbook save never touch it.'
46
46
  },
47
47
  draft_save: {
48
48
  usage: 'playbook draft save PLAYBOOK_ID --file REQUEST.json [--workspace ID] [--profile PATH]',
@@ -6,7 +6,7 @@ const identity=z.object({key:z.string().uuid(),name:label}).strict();
6
6
  const routeTarget=z.discriminatedUnion('kind',[z.object({kind:z.literal('stage'),stageKey:z.string().uuid()}).strict(),z.object({kind:z.literal('off_ramp'),offRampKey:z.string().uuid()}).strict(),z.object({kind:z.literal('step'),stepKey:z.string().uuid()}).strict()]);
7
7
  // An event rule (#350 T3): what happens when an event reaches a person. The server checks the event and action ids against the outreach rulebook.
8
8
  const customLabel=z.object({name:z.string().regex(/^[a-z][a-z0-9_]{1,39}$/).describe('Lower case letters, digits and underscores, 2 to 40 characters, starting with a letter. It cannot be the name of a core label (run intent_labels_list).'),direction:z.enum(['inbound']).optional().describe('Only inbound: a message the person sent. Default inbound.'),meaning:z.string().trim().min(1).max(160).describe('One line: what a message with this label says.')}).strict();
9
- const eventRule=z.object({key:z.string().uuid(),when:z.string().min(1).max(60).describe('An event id from the outreach rulebook, for example accepted, replied, wait_timeout or reply.out_of_office, or custom.<name> for a label this playbook declares in customLabels.'),do:z.array(z.enum(['stop_wait','snooze','redirect_to_named_person','ask_who_is_right','tell_me','hand_to_user','withdraw_pending_request'])).max(7).optional(),snoozeDays:z.number().int().min(1).max(90).optional(),goto:z.discriminatedUnion('kind',[z.object({kind:z.literal('stay')}).strict(),z.object({kind:z.literal('gate')}).strict(),...routeTarget.options]).optional()}).strict();
9
+ const eventRule=z.object({key:z.string().uuid(),when:z.string().min(1).max(60).describe('An event id from the outreach rulebook, with the words the screen shows: accepted (They accept your connection request), replied (They reply, any reply), meeting_booked (A meeting is booked), inbound_connection_request (They send you a connection request), not_a_fit (They are recorded as not a fit), wait_timeout (The wait runs out), reply.KIND (They reply: the label of that kind), custom.NAME (a label of this playbook). Run playbook_actions_list for the whole list.'),do:z.array(z.enum(['stop_wait','snooze','redirect_to_named_person','ask_who_is_right','tell_me','hand_to_user','withdraw_pending_request'])).max(7).optional().describe('What the rule does, each action once. Ids with the words the screen shows: stop_wait (End their wait), snooze (Wait before the next step), redirect_to_named_person (Add the person they name), ask_who_is_right (Ask who the right person is), tell_me (Tell me), hand_to_user (Put it in my queue), withdraw_pending_request (Withdraw the pending request). ask_who_is_right and withdraw_pending_request do not run yet: a rule that holds one is refused on save and publish with 422 playbook_cannot_work.'),snoozeDays:z.number().int().min(1).max(90).optional(),goto:z.discriminatedUnion('kind',[z.object({kind:z.literal('stay')}).strict(),z.object({kind:z.literal('gate')}).strict(),...routeTarget.options]).optional()}).strict();
10
10
  const stepRoutes=z.object({accept:routeTarget.optional(),reply:routeTarget.optional(),meeting:routeTarget.optional(),timeout:routeTarget.optional()}).strict().describe("Where an event that reaches the person after this step ran sends them. A route wins over the stage's jump list; an event with no route follows the stage. Going to an earlier stage needs rules.allowBackward.");
11
11
  const playbookStepSchema=z.object({key:z.string().uuid(),channel:z.enum(['LinkedIn','Email','Custom']),action:label,condition:z.enum(['Always','Only if viable','Only if connected']).optional().describe('Left out: Only if connected for a LinkedIn message (it can only reach a connection), Always for everything else. Only if connected skips the step for a person who was asked to connect and has not accepted. Only if viable needs viability.'),viability:text.optional(),instructions:text.optional().describe('What the agent should write or do. Left out, one coaching sentence for the kind of action.'),wait:z.object({until:z.enum(['accept','reply','meeting','timeout','event']),days:z.number().int().min(1).max(60),event:label.optional().describe('Custom steps only, with until event: the name of the event a person records to end the wait, for example Gift delivered.'),followUps:z.number().int().min(0).max(10).optional().describe('Meeting waits only: how many follow-ups your agent writes, one after each wait, before the meeting is marked No answer. Default 3.')}).strict().nullable().optional().describe('How long the step waits and for what. Left out on a new step that sends: a reply wait of 3 days (a connection request waits 7 days for the accept). null means no wait. A step the playbook already has keeps what it has.'),waitDays:z.number().int().min(1).max(60).optional().describe('Older form of wait: the same as wait {until: timeout, days}.'),side:z.boolean().optional(),onTimeout:z.enum(['next','off_ramp']).optional(),humanize:z.boolean().optional().describe('Email only. Rewrite the email text before approval so it reads as written by a person. On by default; set false to send the text as written.'),actionId:z.string().max(80).optional().describe('An action id from the outreach rulebook, for example linkedin.message.send.'),routes:stepRoutes.optional(),eventRules:z.array(eventRule).max(10).optional()}).strict();
12
12
  const completion=z.discriminatedUnion('kind',[z.object({kind:z.literal('next')}).strict(),z.object({kind:z.literal('stay')}).strict(),z.object({kind:z.literal('off_ramp'),offRampKey:z.string().uuid()}).strict()]);
@@ -22,13 +22,13 @@ const fields=playbookFieldsSchema;
22
22
  const base=a=>`/workspaces/${a.workspaceId}`,query=(a,skip=[])=>new URLSearchParams(Object.entries(a).filter(([k,v])=>k!=='workspaceId'&&!skip.includes(k)&&v!==undefined).map(([k,v])=>[k,String(v)]));
23
23
  export const playbookOperations={
24
24
  playbook_list:{schema:z.object({...scope,cursor:uuid.optional().describe('The nextCursor of the previous page.'),limit:z.number().int().min(1).max(100).optional().describe('Playbooks per page. Default 50.'),archived:z.boolean().optional().describe('true lists the archived playbooks instead of the active ones. Default false.')}).strict(),method:'GET',path:a=>`${base(a)}/playbooks?${query(a)}`,scoped:true,description:'List the playbooks in the current workspace, one page of summaries (id, name, currentRevision, system, systemKey, archivedAt, stages, steps, check {errors, warnings}, recommended, updatedAt, updatedBy, reviewPending) with nextCursor. updatedBy names who saved the current version (kind person or agent, name; null on a starter playbook). reviewPending is true while a playbook saved by an agent, which no campaign uses, waits for a person to open it (it is also one card in Operator). Archived playbooks are left out unless archived is true. Read the fields of one with playbook_get. Starter playbooks (Email first, LinkedIn first, Email only) carry system:true and are read-only; clone one by saving its fields without an id. recommended marks the starter playbook that fits the channels your agents reported, and recommendation gives its reason in one line. Free CRM read.'},
25
- playbook_actions_list:{schema:z.object({...scope}).strict(),method:'GET',path:a=>`${base(a)}/playbook-actions`,scoped:true,description:'List what a playbook step may name: the step actions (id, channel, label), the step channels and the event-rule names. Use it before playbook_save when a save names an actionId, a channel or an event the server does not know. Free CRM read.'},
26
- playbook_get:{schema:z.object({...scope,id:uuid,revision:z.number().int().positive().optional()}).strict(),method:'GET',path:a=>`${base(a)}/playbooks/${a.id}?${query(a,['id'])}`,scoped:true,description:'Read a playbook version (updatedBy says who saved it, createdAt when; a person reading the current version clears reviewPending): stage plays, step waits, completion, goal, voice and agent rules. Instructions are executed by the external agent, never by a hosted sequence runner. warnings is the Playbook check (level error or warning, message, stageKey, stepKey, fix): the same list the editor and the campaign picker show.'},
25
+ playbook_actions_list:{schema:z.object({...scope}).strict(),method:'GET',path:a=>`${base(a)}/playbook-actions`,scoped:true,description:'List what a playbook step may name: the step actions (id, channel, label), the step channels and the event-rule names. eventRules.whenLabels and eventRules.doLabels give the words the screen shows for each event and action id, and eventRules.notBuilt lists the actions that do not run yet (a rule that holds one cannot be saved as a version). Use it before playbook_save when a save names an actionId, a channel or an event the server does not know. Free CRM read.'},
26
+ playbook_get:{schema:z.object({...scope,id:uuid,revision:z.number().int().positive().optional()}).strict(),method:'GET',path:a=>`${base(a)}/playbooks/${a.id}?${query(a,['id'])}`,scoped:true,description:'Read a playbook version (updatedBy says who saved it, createdAt when; a person reading the current version clears reviewPending): stage plays, step waits, completion, goal, voice and agent rules. Instructions are executed by the external agent, never by a hosted sequence runner. warnings is the Playbook check (level error or warning, message, stageKey, stepKey, ruleKey, fix): the same list the editor and the campaign picker show. actionLabels maps each rule action id the playbook uses to the words the screen shows for it, for example snooze to Wait before the next step.'},
27
27
  playbook_archive:{schema:z.object({...scope,id:uuid}).strict(),method:'POST',path:a=>`${base(a)}/playbooks/${a.id}/archive`,scoped:true,description:'Archive a playbook: it leaves the list and the campaign picker, and cannot be edited or newly chosen for a campaign until restored. Campaigns that already use it keep running on the version they were set up with. Safe to repeat. Starter playbooks can be archived.'},
28
28
  playbook_restore:{schema:z.object({...scope,id:uuid}).strict(),method:'POST',path:a=>`${base(a)}/playbooks/${a.id}/restore`,scoped:true,description:'Bring an archived playbook back to the list and the campaign picker. Safe to repeat.'},
29
29
  playbook_delete:{schema:z.object({...scope,id:uuid}).strict(),method:'DELETE',path:a=>`${base(a)}/playbooks/${a.id}`,scoped:true,description:'Delete a playbook and all its versions. This cannot be undone. A playbook that any campaign uses (a campaign of any state, finished or archived included, that was ever set up with a version of it) is refused with 409 playbook_in_use, naming the campaigns; archive it instead. Starter playbooks are refused with starter_playbook.'},
30
- playbook_save:{schema:z.object({...scope,id:uuid.optional(),requestId:uuid,expectedRevision:z.number().int().nonnegative().describe('0 for a new playbook, otherwise currentRevision from playbook_get. A stale value is refused with revision_conflict, which carries currentRevision.'),fields}).strict(),method:'POST',path:a=>`${base(a)}/playbooks`,body:({workspaceId,...a})=>a,scoped:true,description:'Create or revise a playbook with optimistic version and idempotent request. A starter playbook refuses revision (409 starter_playbook); save its fields without an id to make an editable copy. Does not change existing campaign or participation pins. A playbook that cannot work (no off-ramp, or a stage nobody can reach) is refused with 422 playbook_cannot_work and the list in errors. A saved playbook returns warnings, the Playbook check, for what is legal but probably wrong. The save is listed in the audit log (category playbooks) with who made it; a save by an agent of a playbook no campaign uses yet marks it reviewPending until a person opens it. A step with channel Custom is done by a person: put what the person does in instructions (for example call, leave a voicemail, handwrite a note, record a video, send a gift); your agent prepares the script, text or choice and the person records the outcome (hand_task_* commands). A Custom step can wait for a named event with wait {until: event, event: name, days}.'},
31
- playbook_draft_get:{schema:z.object({...scope,id:uuid}).strict(),method:'GET',path:a=>`${base(a)}/playbooks/${a.id}/draft`,scoped:true,description:'Read your draft of a playbook: {draft: {baseRevision, fields, updatedAt, savedBy}} or {draft: null}. savedBy is person or agent. The draft is the unsaved work in the playbook editor. It belongs to one person, and an agent reads and changes the draft of the person who owns the agent. No campaign can use a draft. playbook_get and playbook_save never touch it.'},
30
+ playbook_save:{schema:z.object({...scope,id:uuid.optional(),requestId:uuid,expectedRevision:z.number().int().nonnegative().describe('0 for a new playbook, otherwise currentRevision from playbook_get. A stale value is refused with revision_conflict, which carries currentRevision.'),fields}).strict(),method:'POST',path:a=>`${base(a)}/playbooks`,body:({workspaceId,...a})=>a,scoped:true,description:'Create or revise a playbook with optimistic version and idempotent request. A starter playbook refuses revision (409 starter_playbook); save its fields without an id to make an editable copy. Does not change existing campaign or participation pins. A playbook that cannot work (no off-ramp, a stage nobody can reach, or a rule with an action that does not run yet) is refused with 422 playbook_cannot_work and the list in errors. A saved playbook returns warnings, the Playbook check, for what is legal but probably wrong, and actionLabels, the words the screen shows for each rule action it uses. The save is listed in the audit log (category playbooks) with who made it; a save by an agent of a playbook no campaign uses yet marks it reviewPending until a person opens it. A step with channel Custom is done by a person: put what the person does in instructions (for example call, leave a voicemail, handwrite a note, record a video, send a gift); your agent prepares the script, text or choice and the person records the outcome (hand_task_* commands). A Custom step can wait for a named event with wait {until: event, event: name, days}.'},
31
+ playbook_draft_get:{schema:z.object({...scope,id:uuid}).strict(),method:'GET',path:a=>`${base(a)}/playbooks/${a.id}/draft`,scoped:true,description:'Read your draft of a playbook: {draft: {baseRevision, fields, actionLabels, updatedAt, savedBy}} or {draft: null}. savedBy is person or agent. actionLabels maps each rule action id the draft uses to the words the screen shows. The draft is the unsaved work in the playbook editor. It belongs to one person, and an agent reads and changes the draft of the person who owns the agent. No campaign can use a draft. playbook_get and playbook_save never touch it.'},
32
32
  playbook_draft_save:{schema:z.object({...scope,id:uuid,baseRevision:z.number().int().positive().describe('The version the edits start from: currentRevision from playbook_get, or the baseRevision of the draft you read. A value above the current version is refused with revision_conflict, which carries currentRevision. A lower value is kept; playbook_draft_publish checks it and refuses a draft that started from an older version.'),expectedUpdatedAt:z.string().datetime().nullable().describe('The updatedAt of the draft you read with playbook_draft_get, or null when it returned no draft. If the draft changed since (another tab, your agent or the person), the save is refused with draft_conflict and currentUpdatedAt, and nothing is replaced. Read the draft again, combine the changes, then save.'),fields}).strict(),method:'PUT',path:a=>`${base(a)}/playbooks/${a.id}/draft`,body:({workspaceId,id,...a})=>a,scoped:true,description:'Keep your edits as the draft of a playbook. The draft is replaced as a whole, so send every field: read it with playbook_draft_get (or playbook_get when there is no draft) and change what you need. It never makes a version and uses no requestId. The person who owns the draft sees the change in the editor within a few seconds. A stale expectedUpdatedAt is refused with draft_conflict (HTTP 409). Same field rules as playbook_save, but problems do not stop a draft save: the answer carries check (the problems and warnings a publish would see). The draft is not fully checked until publish. Starter and archived playbooks are refused (starter_playbook, playbook_archived).'},
33
33
  playbook_draft_discard:{schema:z.object({...scope,id:uuid,expectedUpdatedAt:z.string().datetime().optional().describe('The updatedAt of the draft you read. When given and the draft changed since, the draft is kept and the call is refused with draft_conflict and currentUpdatedAt. Leave it out to discard whatever the draft holds.')}).strict(),method:'DELETE',path:a=>`${base(a)}/playbooks/${a.id}/draft${a.expectedUpdatedAt?`?expectedUpdatedAt=${encodeURIComponent(a.expectedUpdatedAt)}`:''}`,scoped:true,description:'Throw your draft of a playbook away. The saved versions stay. Safe to repeat: {id, discarded} says whether a draft existed. Pass expectedUpdatedAt to keep a draft that was saved since you read it (draft_conflict).'},
34
34
  playbook_draft_publish:{schema:z.object({...scope,id:uuid,requestId:uuid.describe('A fresh UUID. Reuse it only to retry the same call.')}).strict(),method:'POST',path:a=>`${base(a)}/playbooks/${a.id}/draft/versions`,body:({workspaceId,id,...a})=>a,scoped:true,description:'Save your draft of a playbook as the next version and remove the draft, in one step. Returns the new version, as playbook_save does, with the same checks (including 422 playbook_cannot_work). If a newer version was saved after the draft started, nothing is saved and the call is refused with 409 revision_conflict and currentRevision; read the draft and the new version, then save the draft again with the new baseRevision. No draft gives 404 no_draft. A retry with the same requestId returns the same version.'},
package/cli/skills.mjs CHANGED
@@ -15,7 +15,7 @@ const bundles = {
15
15
  helpers: false,
16
16
  },
17
17
  'playbook-design': {
18
- version: '1.1.0',
18
+ version: '1.2.0',
19
19
  files: ['SKILL.md', 'references/fields.md'],
20
20
  helpers: false,
21
21
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.2.0-exora.31",
3
+ "version": "0.2.0-exora.33",
4
4
  "description": "Audienti CLI and MCP client for app.audienti.com",
5
5
  "type": "module",
6
6
  "bin": {
@@ -127,6 +127,15 @@ To check one person, run `audienti campaign check CAMPAIGN_ID --url LINKEDIN_URL
127
127
  --request-id UUID` (MCP tool `campaign_prospect_check`). Reuse the request ID after an
128
128
  interruption so a retry never pays twice.
129
129
 
130
+ If the person's company has no LinkedIn page, the check holds with "No company page; add a
131
+ source to confirm the industry." Read another page that states the company's industry, such
132
+ as its website or a regulator's register. Run the check again with `--source-url URL
133
+ --source-industry INDUSTRY` (add `--source-kind website|register|other`, website by default; add `--source-location
134
+ PLACE` or `--source-note TEXT` if useful). MCP: `companySource` on `campaign_prospect_check`.
135
+ The industry rule still applies, and the source is recorded on the receipt. A company the
136
+ campaign names in its targets passes the industry rule without a source. A held person is
137
+ not offered again until new evidence, such as a source, arrives.
138
+
130
139
  `campaign find` moves on to the next person only after a definite answer: fit, held, not
131
140
  a fit, already found, or a refusal about that person (a bad URL or no LinkedIn profile).
132
141
  If a check is still running, its response is lost or the server fails, the command retries
@@ -40,8 +40,9 @@ When you change a playbook you already have, a part you leave out (`stages`, `go
40
40
  ## The Playbook check
41
41
 
42
42
  Every read and save of a playbook returns `warnings`, a deterministic check that needs no agent. An item
43
- is `{ id, level, message, stageKey, stepKey, fix }`. A save is refused with `playbook_cannot_work` (422,
44
- the list in `errors`) when `offRamps` is empty or a stage has `on: []` and nothing sends people to it.
43
+ is `{ id, level, message, stageKey, stepKey, ruleKey, fix }`. A save is refused with `playbook_cannot_work` (422,
44
+ the list in `errors`) when `offRamps` is empty, a stage has `on: []` and nothing sends people to it, or a rule
45
+ holds an action that does not run yet (`rule_action_not_built`; see Event rules).
45
46
  The other items are warnings; read them after each save and clear them:
46
47
 
47
48
  | Item `id` | Meaning and default fix |
@@ -54,6 +55,10 @@ The other items are warnings; read them after each save and clear them:
54
55
  | `steps_after_break_up` | A step after the break-up email. Put it before the break-up step. |
55
56
  | `message_without_connection` | A LinkedIn message with condition `Always` and no connection request before it. Use `Only if connected`. |
56
57
  | `stages_loop` | Routes or rules send people between stages in a circle. |
58
+ | `rule_never_fires` | A rule that an earlier rule for the same event in the same place hides. Remove it or change its event. |
59
+ | `rule_event_not_read` | A rule on an event that nothing reads yet, so it cannot fire. |
60
+ | `rule_wait_contradiction` | A rule that does both `stop_wait` and `snooze`. Remove `snooze`. |
61
+ | `rule_moves_back` | A playbook rule that sends people to a stage with stages after it while `rules.allowBackward` is false. Turn it on, or send them to a later stage. |
57
62
 
58
63
  ## Stage
59
64
 
@@ -87,13 +92,13 @@ The other items are warnings; read them after each save and clear them:
87
92
 
88
93
  ## Event rules
89
94
 
90
- An event rule says what happens when an event reaches a person: do these things, then go here. It lives on the playbook (`fields.eventRules`, up to 30), on a stage (`eventRules`, up to 10) or on a step (up to 10). The nearest place wins, then the most specific event: a reply kind beats any reply. A step `routes` entry is the older form of the same thing.
95
+ An event rule says what happens when an event reaches a person: do these things, then go here. It lives on the playbook (`fields.eventRules`, up to 30), on a stage (`eventRules`, up to 10) or on a step (up to 10). When more than one rule fits, the most specific event wins first: a reply kind such as `reply.interested` beats `replied`. For the same event, the nearest place wins: the step, then the stage, then the playbook. After that the first rule in the list wins, so a later rule for the same event in the same place never fires. A step `routes` entry is the older form of the same thing.
91
96
 
92
97
  | Field | Rule |
93
98
  | --- | --- |
94
99
  | `key` | A UUID, unique in the playbook. |
95
100
  | `when` | The event: `accepted`, `replied`, `meeting_booked`, `wait_timeout`, `inbound_connection_request`, or a reply kind `reply.interested`, `reply.question`, `reply.later`, `reply.out_of_office`, `reply.wrong_person`, `reply.referral`. An unknown name is refused with the allowed names; `audienti playbook actions` lists them under `eventRules.when`. |
96
- | `do` | Optional list of `stop_wait`, `snooze`, `redirect_to_named_person`, `ask_who_is_right`, `tell_me`, `hand_to_user`, `withdraw_pending_request`. Also under `eventRules.do`. |
101
+ | `do` | Optional list, each once. The screen's words are in brackets: `stop_wait` (End their wait), `snooze` (Wait before the next step), `redirect_to_named_person` (Add the person they name), `ask_who_is_right` (Ask who the right person is), `tell_me` (Tell me), `hand_to_user` (Put it in my queue), `withdraw_pending_request` (Withdraw the pending request). `ask_who_is_right` and `withdraw_pending_request` do not run yet: a rule that holds either cannot be published or saved as a version. Also under `eventRules.do`. |
97
102
  | `snoozeDays` | 1 to 90. For `snooze`. |
98
103
  | `goto` | Where the person goes after: `{ "kind": "stay" }`, `{ "kind": "gate" }` (the stage's own next step; the default), or `{ "kind": "stage", "stageKey": KEY }`, `{ "kind": "off_ramp", "offRampKey": KEY }` or `{ "kind": "step", "stepKey": KEY }`. |
99
104
 
@@ -101,10 +106,12 @@ Every new playbook starts with these three on the playbook, when you send no `ev
101
106
 
102
107
  - `reply.interested`: `stop_wait`, then `gate`.
103
108
  - `reply.out_of_office`: `snooze` for 7 days, then `stay`.
104
- - `reply.wrong_person`: `stop_wait`, `redirect_to_named_person`, `ask_who_is_right`, then `stay`.
109
+ - `reply.wrong_person`: `stop_wait`, `redirect_to_named_person`, then `stay`.
105
110
 
106
111
  Taking someone off the list and a bounce always run first; no rule changes them.
107
112
 
113
+ A refused rule or route says which one it is and what to change, in the words the screen uses, for example: `The rule for "a meeting is booked" in the stage "Reaching out" goes back to "Ready", but moving back a stage is off. Change that rule first.`
114
+
108
115
  A LinkedIn step whose `actionId` is `linkedin.inmail.send` runs only when the LinkedIn account reports InMail available. Otherwise it is skipped without a card.
109
116
 
110
117
  ## Example request
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "repository": "https://github.com/omalab/exora",
3
- "sourceCommit": "38771355af5ed0dcdad8719dca262070bc915a4e",
3
+ "sourceCommit": "0edcc701f5733a900a072172db624460f74cba29",
4
4
  "files": {
5
5
  "LICENSE": "220f297cfcd5c38eb64e6b4b4a77cab4ebc7f4686d4d99c6260a475d02ca11e5",
6
6
  "README.md": "7ff32cc8ac89dd54b64d7b39c0b41b1f37e154b7ea10ad5a7bceefc3df77d87f",
@@ -16,21 +16,21 @@
16
16
  "cli/attachments.mjs": "2fee6b4abbf82da9847d4a927a3d4d3ca947a198a903a78927596a2ea858cbf4",
17
17
  "cli/audience-operations.mjs": "133b72dc6b9e3a6b4f173ce28f1524163f9c7b049505b885115f83b9e4af7170",
18
18
  "cli/audiences.mjs": "3c377346efc1a8bdde432a16c2d19a3f51f34f3333d344d3a2bfa94c188a017d",
19
- "cli/audienti.mjs": "0ed98e1390d732a460e4b137145375715eb211b737d7aadbde4bca5bf3d8d71f",
19
+ "cli/audienti.mjs": "b9aa625543270cd9104d7f2fc11aac5a65c419c4371fea4008c1a2b95ceeea03",
20
20
  "cli/audit-operations.mjs": "afaf87d592a1e5f6bff2a8649a6a64a8f471950b7d99e98995d9205aa5910398",
21
21
  "cli/audit.mjs": "a7becaed67c58b063e668b5e66844b6dc15c11a63b428faa171b542ad922c001",
22
22
  "cli/budget-operations.mjs": "d74916551ef393e29734c6e13cf9021251e0ed126eeaa529e18cf2332146e1e0",
23
23
  "cli/budgets.mjs": "683e06cb4d410c851286fbd1cdd4fb9f1902726c60fd4fcddb2c13d164786525",
24
24
  "cli/campaign-find.mjs": "9c0a44c69ab7ad3e24a0a2edc97b8f3a14f602e3b6caf7e1943b8e1b93609c4a",
25
- "cli/campaign-operations.mjs": "c1ac42f53f8bf19957fac41cc2a80ab4e26a743a7c1cc0b42c8c0e85fcf6e61b",
26
- "cli/campaigns.mjs": "a4c1e824b729cdaf1838db68785e62c55d4ba7d57c59d3f71666d2197be2fdf8",
25
+ "cli/campaign-operations.mjs": "65c3576df673fee2a23bfbc9f56210973d17f0c882ebeeb27008a56090adb54f",
26
+ "cli/campaigns.mjs": "35aced0c07073ac82a48f6fe4fb2b9ba98ef48559c1ca19345f8ad061b115b19",
27
27
  "cli/cohort-operations.mjs": "91b1d1fc48e75581bb315f42d735ef94e21687dc216f352337d8a5d180bb691e",
28
28
  "cli/cohorts.mjs": "c71940903430f3e6834608f113534910b75a98b3e410a5942f04051607488485",
29
29
  "cli/connection-registry-operations.mjs": "888ea440d00ffc9ed68d4cc084ff73a5a00d0e60bd6293baba8bb4539a25ae62",
30
30
  "cli/content.mjs": "139dee5fd37fd89669c39e2120ac70a2e0286b207b055ff666f54ba3512ab061",
31
31
  "cli/decision-operations.mjs": "0c4c2134c9615678def2b50f2a5254218b14f1174f1ce4e8ba7db78c751661af",
32
32
  "cli/decision.mjs": "3d89d48b333e2620bce0191cf528ef25541ee841a7f8ae6d60249aead434b651",
33
- "cli/discovery-operations.mjs": "9b6873d586d27bf67106a592db8ced75da1abf1a1330f192e69d34f81b2d080f",
33
+ "cli/discovery-operations.mjs": "072a85a0c88de6cbef3c683da632b96f98ad2417f9100d94f58e430c7d046375",
34
34
  "cli/discovery.mjs": "34b3794ee6ca539dbbccef77ec2085fd5411023046bdfb9b83e2537414542a41",
35
35
  "cli/duplicate-operations.mjs": "118f067e2753887ba41b41d59b92c6b54c57eeff47f4adf662e3e1fc490f9ac3",
36
36
  "cli/duplicates.mjs": "341e5c8dae67a819340abdad3502470577cf8b6375bf60acb00068c7188344a7",
@@ -54,8 +54,8 @@
54
54
  "cli/outcome-operations.mjs": "cf133097799f240a006264a38a4c6bd4094078d6dec78a71679700ac3bdeacd5",
55
55
  "cli/outcomes.mjs": "765ccb112a90fc4d228b2a41e5e19c5e1f3e2fe14bc1d8128b0090fc95cdbf3f",
56
56
  "cli/owner-account-client.mjs": "aa2790fd3c16714b1a82922fe13012f261cced99025d692bca5d0c605a717cd1",
57
- "cli/playbook-help.mjs": "664114538865a91dbcbd96bdd2a628c78505f078c9e6c7eb542e51d376fed60a",
58
- "cli/playbook-operations.mjs": "be94b1a9e1c84fa2f56169c2af7c972ea8336cf8402150e4b57b08e354e7b4a9",
57
+ "cli/playbook-help.mjs": "9be239898b0348e7abd2993194558c1e27e35b1ec8750297cdc9d4809f27c367",
58
+ "cli/playbook-operations.mjs": "efaed6ae9bb4a743b6811d51857b877dce959ec9b881edbe2a4189765fb47c42",
59
59
  "cli/profiles.mjs": "45929b224ffc916b6dbd7a94e06ac09a1ff7feec430633b10d160c1608fc2d32",
60
60
  "cli/progress.mjs": "6908f2937adb87a83ebefc1f4324ed18adac65228547b0524a474ac3513b5c67",
61
61
  "cli/prospecting-settings-operations.mjs": "1829eb2fe7208f58601988143756072429adbfe00f8deb5a52613bf650a9eacc",
@@ -74,7 +74,7 @@
74
74
  "cli/sender-account-operations.mjs": "cf1c04e6289937611f5bd666953389c96887881f2b46203d79b9d8f57c55f3b2",
75
75
  "cli/sender-accounts.mjs": "febe2a21f8cda4999e14eb78c01e9924dcc1914ff3c5cadcd845b7dbc4523491",
76
76
  "cli/skills-mcp.mjs": "6a91b16a789f50035ae0fe33f8ae2758bb9829949db7efb05c6d65dde335a9e0",
77
- "cli/skills.mjs": "ea8dfef32664714f9c63251d9a6bbdb44d77d41e272df46bb102ebfcd8df8b66",
77
+ "cli/skills.mjs": "135a5d3b7769a1a0a6c92e68e70f53e6213b4929343f732571016ac7dfa3afa9",
78
78
  "cli/suppression-operations.mjs": "d1f827a785670fc6e44d87b18e4cfb6596b09e5be7c4f84c4b9a75ed13a962c6",
79
79
  "cli/suppression.mjs": "2b10be67e290deec9ed915139c3d98d45d7d10fd41abb67b5a35a630d636a4ed",
80
80
  "cli/tools-operations.mjs": "f64e58f75ef0c02b9317c6b6e07ccc7b9891464cf29824cee75f0fcae9026eb2",
@@ -88,10 +88,10 @@
88
88
  "cli/work-operations.mjs": "66a6155964d1af264b4aa3851cf43b9373155887ee2f9bcac3eb97d9f4680157",
89
89
  "cli/work.mjs": "751107ecc3de5a6be9db4a068cddc41f3ad8e77c87e59686eaf522862caf9e9f",
90
90
  "cli/workspaces.mjs": "7de635a78f80d75421447cc62686ab4cde7779a9dd7d0bb55e48143a5e4c8365",
91
- "package.json": "c88e65406409f92b7c943845d66a6e371076259583f342c087696b56bd31e489",
91
+ "package.json": "f279d289c88e4c80b220b33e80991b0f7d484963e61f5b3dd6f0e816300a34b1",
92
92
  "skills/agent-rooms/SKILL.md": "08f917ff85fee1d23499a4499056ce845b39ce6727029b104819dad41ee87cc2",
93
93
  "skills/agent-rooms/references/commands.md": "ef5afa5543ca9da967a147f638774647998ea1c1a2a821959f3bf87d54c292bd",
94
- "skills/audienti/SKILL.md": "8f2124065bfc3a005b44e194e7dcce9bbf14062ab332e7db66d441eeccb518ed",
94
+ "skills/audienti/SKILL.md": "7307267df9b9b0bd5c02d20f15eb12e206ca81d7fea95fe0a3f81ed197e5b40e",
95
95
  "skills/audienti/references/helpers.md": "e27aff0aa07b92f575d617612ed3def824765bac6ce1304cf227a4aca223369a",
96
96
  "skills/audienti/references/profile-workflows.md": "80669b75eb5e8aeb614ce55ac046236d671af0de3ddd238853324579d5a3b251",
97
97
  "skills/audienti/references/workflow-receipt.md": "349f9a85472832b8bdc6a5d16c5b10b8e32d48d3e81523ad8d42f9a03b3655ff",
@@ -107,6 +107,6 @@
107
107
  "skills/getting-started/references/replies.md": "e543f13b9fae2ad3a7a07b6289ca03258c75e64df60cd3b36d4686ac9298d799",
108
108
  "skills/getting-started/references/reply-checks.md": "85fe692ad6b7bc73e204de313b27d225be3da1a0af71e8e7bb9945ae511a0dc3",
109
109
  "skills/playbook-design/SKILL.md": "56aa5692c1bbe3843f11819f0f9270988ddd0424b16f38db72a854864155407f",
110
- "skills/playbook-design/references/fields.md": "c7ba7a0bf8ac1325e70a5e4d98734716d752c311588d064bf2aba849925357aa"
110
+ "skills/playbook-design/references/fields.md": "e06231fb52a4b67f8c59d3ea03aed3968174e98c8cb79a6aac581837f7cec895"
111
111
  }
112
112
  }