@audienti/cli 0.2.0-exora.5 → 0.2.0-exora.6
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 +6 -4
- package/cli/campaign-operations.mjs +3 -2
- package/cli/campaigns.mjs +15 -2
- package/cli/discovery-operations.mjs +1 -1
- package/cli/progress.mjs +1 -1
- package/cli/recipients.mjs +6 -2
- package/cli/secret-operations.mjs +1 -1
- package/cli/workspaces.mjs +1 -1
- package/package.json +1 -1
- package/skills/audienti/SKILL.md +10 -5
- package/skills/getting-started/SKILL.md +9 -1
- package/skills/getting-started/references/campaign-draft.md +4 -2
- package/skills/getting-started/references/capabilities.md +5 -0
- package/source-manifest.json +14 -14
package/cli/audienti.mjs
CHANGED
|
@@ -49,7 +49,7 @@ Audiences: audience options --kind industries|locations|sizes|types|seniorities|
|
|
|
49
49
|
audience summary [--audience ID]|people ID|companies ID
|
|
50
50
|
audience test ID --url LINKEDIN_URL --request-id UUID (spends hosted credits unless the profile is saved)
|
|
51
51
|
Files: file upload PATH --type MIME|list|prepare|register|download|delete ID (personal files, human token only)
|
|
52
|
-
Queue: audienti queue [list]|next|claim ID [--note TEXT]|done ID [--note TEXT]|skip ID --note TEXT|release ID [--workspace ID] (start here after you connect; without --workspace an agent reads every workspace its user approved it in)
|
|
52
|
+
Queue: audienti queue [list]|next|claim ID [--note TEXT]|done ID [--note TEXT]|skip ID --note TEXT|release ID [--workspace ID] (start here after you connect; without --workspace an agent reads every workspace its user approved it in; done or skip again with a new --note corrects the note of a finished task)
|
|
53
53
|
Outcome checks: audienti outcomes report TASK_ID --file RESULTS.json (results for the people an outcomes.check task lists)
|
|
54
54
|
Questions: audienti ask "QUESTION" [--context TEXT] [--option TEXT]... [--campaign ID] [--task ID] (questions for your user go to Operator, not chat)
|
|
55
55
|
audienti questions [list] [--state open|answered|withdrawn|all] | show ID | withdraw ID | answer ID --answer TEXT
|
|
@@ -61,7 +61,7 @@ Offer options: --file FIELDS.json --request-id UUID --expected-revision N --revi
|
|
|
61
61
|
Account intake: prospects intake --workspace ID --file INPUT.json
|
|
62
62
|
prospects conversation append|list PERSON --workspace ID
|
|
63
63
|
Discovery: prospects list|get ID|timeline ID [--limit N]|observation create|citation create|evidence create
|
|
64
|
-
prospects contacts list|record PERSON [--file INPUT.json] --workspace ID
|
|
64
|
+
prospects contacts list|record PERSON [--file INPUT.json] [--request-id UUID] --workspace ID
|
|
65
65
|
prospects contacts get|retire PERSON CONTACT --workspace ID
|
|
66
66
|
prospects suppression get|set ID --workspace ID
|
|
67
67
|
guardrails show|pause|resume --workspace ID
|
|
@@ -69,7 +69,7 @@ Discovery: prospects list|get ID|timeline ID [--limit N]|observation create|cita
|
|
|
69
69
|
dnc import --file LIST.csv --workspace ID (at most 5,000 rows; reports added, skipped and invalid rows)
|
|
70
70
|
dnc export [--output FILE.csv] --workspace ID
|
|
71
71
|
secret list [--workspace ID] (names only)
|
|
72
|
-
secret get NAME [--workspace ID] [--json] (prints the value;
|
|
72
|
+
secret get NAME [--workspace ID] [--json] (prints the value; the secret records who read it last and when)
|
|
73
73
|
budgets show --workspace ID
|
|
74
74
|
budgets alerts --workspace ID [--limit N]
|
|
75
75
|
usage breakdown [--workspace ID] [--since month|7d|30d|90d] [--by tool|provider|model|caller|day] [--format json|csv]
|
|
@@ -78,6 +78,8 @@ Discovery: prospects list|get ID|timeline ID [--limit N]|observation create|cita
|
|
|
78
78
|
Qualification: campaigns qualification submit --file INPUT.json
|
|
79
79
|
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)
|
|
80
80
|
campaign check CAMPAIGN_ID --url LINKEDIN_URL [--request-id UUID] (checks one person the same way; reuse the request ID after an interruption)
|
|
81
|
+
Target companies: campaign targets CAMPAIGN_ID [--limit N --after CURSOR] (reads the companies people must work at to join)
|
|
82
|
+
campaign targets CAMPAIGN_ID --add acme.com,linkedin.com/company/globex [--remove old.com] | --set FILE|- (one company per line; saves a new revision)
|
|
81
83
|
Playbooks: playbook list|show ID|save --file REQUEST.json|progress ID|record --file REQUEST.json
|
|
82
84
|
Analytics: analytics show --from ISO --to ISO [--campaign ID] [--playbook-version ID] [--format json|csv]
|
|
83
85
|
analytics outbound [--workspace ID] [--days N | --from ISO --to ISO] [--by sender|action|day] [--format json|csv]
|
|
@@ -103,7 +105,7 @@ Start local MCP stdio server: mcp
|
|
|
103
105
|
|
|
104
106
|
// Example inputs for the commands that save prospects, so `--help` shows a working shape.
|
|
105
107
|
const examples = {
|
|
106
|
-
'prospects observation create': { requestId: 'UUID', kind: 'person', source: { provider: '
|
|
108
|
+
'prospects observation create': { requestId: 'UUID', kind: 'person', source: { provider: 'linkedin', url: 'https://www.linkedin.com/in/jane-doe' }, fields: { name: 'Jane Doe', title: 'VP Sales', imageUrl: 'https://media.licdn.com/dms/image/PHOTO_ID' }, provenance: { url: 'https://example.com/team/jane' }, observedAt: '2026-01-01T00:00:00Z', skill: { id: 'signal-prospect-research', version: '1' } },
|
|
107
109
|
'prospects citation create': { requestId: 'UUID', source: { kind: 'prospect', id: 'PERSON_PROSPECT_ID' }, target: { kind: 'prospect', id: 'COMPANY_PROSPECT_ID' }, edgeType: 'works_at', roles: [{ roleId: 'role-1', title: 'VP Sales', current: true, observedAt: '2026-01-01T00:00:00Z' }] },
|
|
108
110
|
'prospects evidence create': { requestId: 'UUID', subject: { kind: 'person', prospectId: 'PROSPECT_ID' }, campaignId: 'CAMPAIGN_ID', campaignRevision: 1, kind: 'interpretation', data: { conclusion: 'Leads a 40-person sales team', reason: 'Team page lists her as VP Sales' }, sourceUrl: 'https://example.com/team', observedAt: '2026-01-01T00:00:00Z', skill: { id: 'signal-prospect-research', version: '1' } },
|
|
109
111
|
'prospects contacts record': { kind: 'email|linkedin|mobile|phone', value: 'jane@example.com, or +15551234567 for a mobile or a direct office phone', reason: 'Published on https://example.com/contact', requestId: 'UUID', provenance: { observationId: 'OBSERVATION_ID' } },
|
|
@@ -7,10 +7,9 @@ const signal=z.discriminatedUnion('kind',[
|
|
|
7
7
|
z.object({...signalBase,kind:z.literal('topic'),topic:z.string()}).strict(),
|
|
8
8
|
z.object({...signalBase,kind:z.literal('profile'),url:z.string(),track:z.enum(['audience','activity']).optional(),interactions:z.array(z.enum(['comments','reactions'])).optional()}).strict(),
|
|
9
9
|
]);
|
|
10
|
-
const fields=z.object({name:z.string(),audienceId:uuid,audienceRevision:z.number().int(),offerId:uuid,offerRevision:z.number().int(),premise:z.string().optional(),playbookPin:z.object({id:uuid,revision:z.number().int().positive()}).strict().optional(),notes:z.string().optional(),ownerUserId:uuid.nullable().optional(),tags:z.array(z.string()).optional(),signalMatch:z.enum(['any','all']).optional(),signals:z.array(signal).optional(),targets:z.discriminatedUnion('kind',[
|
|
10
|
+
const fields=z.object({name:z.string(),audienceId:uuid,audienceRevision:z.number().int(),offerId:uuid,offerRevision:z.number().int(),premise:z.string().optional(),playbookPin:z.object({id:uuid,revision:z.number().int().positive()}).strict().optional(),notes:z.string().optional(),ownerUserId:uuid.nullable().optional(),tags:z.array(z.string()).optional(),signalMatch:z.enum(['any','all']).optional(),signals:z.array(signal).optional(),method:z.enum(['signals','listen','track-audience','track-engagement','custom']).optional(),findingInstructions:z.string().optional(),targets:z.discriminatedUnion('kind',[
|
|
11
11
|
z.object({kind:z.literal('none'),values:z.array(z.never())}).strict(),
|
|
12
12
|
z.object({kind:z.literal('companies'),values:z.array(z.object({type:z.enum(['domain','linkedin-company']),value:z.string()}).strict())}).strict(),
|
|
13
|
-
z.object({kind:z.literal('people'),values:z.array(z.object({type:z.literal('linkedin-person'),value:z.string()}).strict())}).strict(),
|
|
14
13
|
]).optional(),channels:z.array(z.enum(['email','linkedin'])).optional(),approvalMode:z.literal('manual').optional(),contentOpsEnabled:z.boolean().optional(),schedule:z.object({startAt:z.string().nullable().optional(),endAt:z.string().nullable().optional(),maxCompanies:z.number().int().nullable().optional()}).strict().optional()}).strict();
|
|
15
14
|
const path=a=>`/workspaces/${a.workspaceId}/campaigns`,detail=a=>path(a)+`/${a.campaignId}`;
|
|
16
15
|
const qs=a=>new URLSearchParams(Object.fromEntries(Object.entries(a).filter(([k,v])=>!['workspaceId','campaignId'].includes(k)&&v!==undefined).map(([k,v])=>[k,String(v)])));
|
|
@@ -24,6 +23,8 @@ export const campaignOperations={
|
|
|
24
23
|
campaign_transition:{schema:z.object({...locator,expectedState:state,operation:z.enum(['prepare','activate','pause','resume','close','archive','restore']),reason:z.string().optional()}).strict(),method:'POST',path:a=>detail(a)+'/transitions',body:a=>({expectedState:a.expectedState,operation:a.operation,...(a.reason===undefined?{}:{reason:a.reason})}),scoped:true,description:'Move a Campaign through its lifecycle. prepare: Draft to Preparing (signal tracking and prospect finding run, no engagement). activate: Draft or Preparing to Active (finding and engagement run). pause: stop automation. resume: back to the paused state. close: Active to Closing (finding stops, engagement continues). archive: stop everything and hide it. restore: Archived to Paused, or Draft for a draft. Agents do not start or activate campaigns; the user does. A campaign that cannot run is refused with campaign_not_ready and its missing items in blockers.'},
|
|
25
24
|
campaign_pace_get:{schema:z.object({...locator}).strict(),method:'GET',path:a=>detail(a)+'/pace',scoped:true,description:'Read a Campaign pace: new people a day. Derived is 25 a day per confirmed LinkedIn account, each capped by its reported connection-request limit; an email-only Campaign uses its own number, 50 by default, within mailbox limits. The reply names the LinkedIn accounts, the weekly figure and the expected daily credits for hosted lookups.'},
|
|
26
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
|
+
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
|
+
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.'},
|
|
27
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
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},
|
|
29
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'].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'].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];
|
|
@@ -21,8 +21,21 @@ export async function campaignCli(args){
|
|
|
21
21
|
const {randomUUID}=await import('node:crypto');
|
|
22
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;
|
|
23
23
|
}
|
|
24
|
+
if(command==='targets'){
|
|
25
|
+
if(position.length!==3)throw new Error('Use campaign targets ID [--add a.com,b.com] [--remove a.com] [--set FILE|-] [--expected-revision N].');
|
|
26
|
+
const config=configuration(flags.profile),input={workspaceId:flags.workspace,campaignId:position[2]};
|
|
27
|
+
const change=['add','remove','set'].some(k=>flags[k]!==undefined);
|
|
28
|
+
if(!change){if(flags.limit!==undefined)input.limit=Number(flags.limit);if(flags.after)input.after=flags.after;console.log(JSON.stringify(await callWorkspace('campaign_targets_get',input,config)));return;}
|
|
29
|
+
if(flags.set!==undefined&&(flags.add!==undefined||flags.remove!==undefined))throw new Error('Use --set on its own, or --add and --remove.');
|
|
30
|
+
const split=v=>v.split(',').map(x=>x.trim()).filter(Boolean);
|
|
31
|
+
if(flags.add!==undefined)input.add=split(flags.add);
|
|
32
|
+
if(flags.remove!==undefined)input.remove=split(flags.remove);
|
|
33
|
+
if(flags.set!==undefined){const {readFileSync}=await import('node:fs');input.set=readFileSync(flags.set==='-'?0:flags.set,'utf8').split(/\r?\n/).map(x=>x.trim()).filter(x=>x&&!x.startsWith('#'));}
|
|
34
|
+
input.expectedRevision=flags['expected-revision']!==undefined?Number(flags['expected-revision']):(await callWorkspace('campaign_targets_get',{workspaceId:flags.workspace,campaignId:position[2],limit:1},config)).data.revision;
|
|
35
|
+
console.log(JSON.stringify(await callWorkspace('campaign_targets_change',input,config)));return;
|
|
36
|
+
}
|
|
24
37
|
const name={list:'campaign_list',show:'campaign_get',create:'campaign_create',update:'campaign_update',transition:'campaign_transition',successor:'campaign_successor',events:'campaign_events',pace:flags['per-day']===undefined&&flags['max-credits']===undefined?'campaign_pace_get':'campaign_pace_set'}[command];
|
|
25
|
-
if(!name||position.length!==(['list','create'].includes(command)?2:3))throw new Error('Use campaign list|show ID|create|update ID|transition ID|successor ID|events ID|pace ID [--per-day N|derived] [--max-credits C|none]|find ID --count N --max-credits C|check ID --url URL.');
|
|
38
|
+
if(!name||position.length!==(['list','create'].includes(command)?2:3))throw new Error('Use campaign list|show ID|create|update ID|transition ID|successor ID|events ID|pace ID [--per-day N|derived] [--max-credits C|none]|find ID --count N --max-credits C|check ID --url URL|targets ID [--add|--remove|--set].');
|
|
26
39
|
if(flags['per-day']!==undefined&&command!=='pace')throw new Error('Set the pace with campaign pace ID --per-day N|derived, after the campaign is saved.');
|
|
27
40
|
const input={workspaceId:flags.workspace};
|
|
28
41
|
if(position[2])input.campaignId=position[2];
|
|
@@ -5,7 +5,7 @@ const page=z.object({cursor:uuid.optional(),limit:z.number().int().min(1).max(10
|
|
|
5
5
|
const endpoint=z.object({kind:z.enum(['prospect','profile']),id:uuid}).strict();
|
|
6
6
|
const subject=z.object({kind:z.enum(['person','company']),prospectId:uuid}).strict();
|
|
7
7
|
const text=z.string().nullable().optional();
|
|
8
|
-
const fields=z.object({name:text,title:text,location:text,bio:text,domain:text,description:text,imageUrl:text}).strict();
|
|
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
10
|
const root=a=>`/workspaces/${a.workspaceId}/prospects`;
|
|
11
11
|
const body=a=>Object.fromEntries(Object.entries(a).filter(([k])=>k!=='workspaceId'));
|
package/cli/progress.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export async function progressCli(args){
|
|
|
14
14
|
console.log(JSON.stringify(result));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/**
|
|
17
|
+
/** Daily analytics rows plus a total row. Untracked outcome values are left blank. */
|
|
18
18
|
export function analyticsCsv(data){
|
|
19
19
|
if(!data||!Array.isArray(data.daily)||!data.total)throw new Error('Analytics response has no daily rows.');
|
|
20
20
|
const cell=value=>{const text=String(value);return /[",\n]/.test(text)?`"${text.replace(/"/g,'""')}"`:text;};
|
package/cli/recipients.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { callWorkspace, configuration } from './workspaces.mjs';
|
|
3
3
|
export async function recipientsCli(args) {
|
|
4
4
|
const flags = {}, positions = [];
|
|
5
|
-
const allowed = ['workspace','profile','status','cursor','limit','history-cursor','history-limit','file'];
|
|
5
|
+
const allowed = ['workspace','profile','status','cursor','limit','history-cursor','history-limit','file','request-id'];
|
|
6
6
|
for (let i=0;i<args.length;i++) {
|
|
7
7
|
const arg=args[i]; if(arg==='--json')continue;
|
|
8
8
|
if(!arg.startsWith('--')){positions.push(arg);continue;}
|
|
@@ -12,7 +12,7 @@ export async function recipientsCli(args) {
|
|
|
12
12
|
}
|
|
13
13
|
const command=positions[2];
|
|
14
14
|
if(positions[0]!=='prospects'||positions[1]!=='contacts'||!['list','get','record','retire'].includes(command)||positions.length!==(['get','retire'].includes(command)?5:4))throw Error('Use prospects contacts list|record PERSON or get|retire PERSON CONTACT --workspace ID.');
|
|
15
|
-
const valid=['workspace','profile',...(command==='list'?['status','cursor','limit']:command==='get'?['history-cursor','history-limit']:['file'])];
|
|
15
|
+
const valid=['workspace','profile',...(command==='list'?['status','cursor','limit']:command==='get'?['history-cursor','history-limit']:['file','request-id'])];
|
|
16
16
|
if(Object.keys(flags).some(k=>!valid.includes(k)))throw Error('Option does not apply to this contact command.');
|
|
17
17
|
let input={};
|
|
18
18
|
if(['record','retire'].includes(command)){
|
|
@@ -21,6 +21,10 @@ export async function recipientsCli(args) {
|
|
|
21
21
|
try{input=JSON.parse(raw.toString('utf8'));}catch{throw Error('validation_error');}
|
|
22
22
|
if(!input||typeof input!=='object'||Array.isArray(input)||['workspaceId','prospectId','contactId'].some(k=>Object.hasOwn(input,k)))throw Error('invalid_fields');
|
|
23
23
|
}
|
|
24
|
+
if(flags['request-id']!==undefined){
|
|
25
|
+
if(input.requestId!==undefined&&input.requestId!==flags['request-id'])throw Error('--request-id differs from requestId in the file.');
|
|
26
|
+
input.requestId=flags['request-id'];
|
|
27
|
+
}
|
|
24
28
|
Object.assign(input,{workspaceId:flags.workspace,prospectId:positions[3]});
|
|
25
29
|
if(['get','retire'].includes(command))input.contactId=positions[4];
|
|
26
30
|
if(command==='list')Object.assign(input,{...(flags.status===undefined?{}:{status:flags.status}),...(flags.cursor===undefined?{}:{cursor:flags.cursor}),...(flags.limit===undefined?{}:{limit:Number(flags.limit)})});
|
|
@@ -3,5 +3,5 @@ const workspace = { workspaceId: z.string().uuid().optional() };
|
|
|
3
3
|
export const secretName = z.string().regex(/^[A-Z][A-Z0-9_]{0,63}$/, 'A secret name uses capital letters, digits and underscores, for example HUBSPOT_TOKEN.');
|
|
4
4
|
export const secretOperations = {
|
|
5
5
|
workspace_secret_list: { schema: z.object(workspace).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}/secrets`, scoped: true, description: 'List the names of the secrets in the workspace Vault. Names only, never values. Use workspace_secret_get for one value.' },
|
|
6
|
-
workspace_secret_get: { schema: z.object({ ...workspace, name: secretName }).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}/secrets/${encodeURIComponent(a.name)}`, scoped: true, description: 'Get one secret by name from the workspace Vault (for example HUBSPOT_TOKEN). Every agent in the workspace can read every secret and reading is free. Each read
|
|
6
|
+
workspace_secret_get: { schema: z.object({ ...workspace, name: secretName }).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}/secrets/${encodeURIComponent(a.name)}`, scoped: true, description: 'Get one secret by name from the workspace Vault (for example HUBSPOT_TOKEN). Every agent in the workspace can read every secret and reading is free. Each read records who read it last and when. Use the value for the task only: never print it, paste it into a message or save it to a file.' },
|
|
7
7
|
};
|
package/cli/workspaces.mjs
CHANGED
|
@@ -151,7 +151,7 @@ export async function callWorkspace(name, args, config) {
|
|
|
151
151
|
if(!response.ok) {
|
|
152
152
|
const code = typeof result?.error?.code==='string' && /^[a-z_]+$/.test(result.error.code) ? result.error.code : 'request_failed';
|
|
153
153
|
const retryAt = typeof result?.error?.retryAt === 'string' && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(result.error.retryAt) ? result.error.retryAt : undefined;
|
|
154
|
-
const detail = ((response.status===400 && (code==='invalid_fields' || code==='validation_error' || code==='secret_rejected')) || (response.status===409 && (code==='identity_conflict' || code==='task_not_for_you' || code==='version_conflict' || code==='request_conflict' || code==='campaign_not_ready'))) && typeof result?.error?.message==='string' && result.error.message.length<=1000 && !/[\u0000-\u001f\u007f]/.test(result.error.message) && !/^Workspace request failed/.test(result.error.message) && !(config.token && result.error.message.includes(config.token)) ? result.error.message.trim() : '';
|
|
154
|
+
const detail = ((response.status===400 && (code==='invalid_fields' || code==='validation_error' || code==='secret_rejected')) || (response.status===409 && (code==='identity_conflict' || code==='task_not_for_you' || code==='version_conflict' || code==='request_conflict' || code==='campaign_not_ready' || code==='campaign_not_paced'))) && typeof result?.error?.message==='string' && result.error.message.length<=1000 && !/[\u0000-\u001f\u007f]/.test(result.error.message) && !/^Workspace request failed/.test(result.error.message) && !(config.token && result.error.message.includes(config.token)) ? result.error.message.trim() : '';
|
|
155
155
|
const blockers = (code==='execution_blocked' || code==='campaign_not_ready') && Array.isArray(result?.error?.blockers) && result.error.blockers.length<=28 && result.error.blockers.every(v=>typeof v==='string' && /^[a-z_]+$/.test(v)) ? result.error.blockers : undefined;
|
|
156
156
|
const error = new WorkspaceRequestError(response.status, code, `Workspace request failed: ${code} (HTTP ${response.status}).${detail ? ` ${detail}` : ''}${retryAt ? ` Retry after ${retryAt}.` : ''}`, retryAt, blockers, requestTraceId);
|
|
157
157
|
// What a failed paid check already spent (pico USD), so a caller counting its spend never loses it.
|
package/package.json
CHANGED
package/skills/audienti/SKILL.md
CHANGED
|
@@ -71,9 +71,12 @@ takes `--file INPUT.json`; the request fields are in the API schema at
|
|
|
71
71
|
`/api/v1/openapi.json`.
|
|
72
72
|
|
|
73
73
|
1. `audienti prospects observation create --file person.json` saves a person or company
|
|
74
|
-
profile with its source URL and returns the prospect ID.
|
|
74
|
+
profile with its source URL and returns the prospect ID. A person's source is their
|
|
75
|
+
own LinkedIn profile URL, email, phone or provider ID (or pass the existing person's
|
|
76
|
+
`prospectId`); a web page that names them goes in `provenance.url`, because a plain
|
|
77
|
+
`web` source never identifies a person. Add `fields.imageUrl` with
|
|
75
78
|
the LinkedIn profile photo or company logo URL when you have it; People and
|
|
76
|
-
Companies show it instead of initials.
|
|
79
|
+
Companies show it instead of initials. A company's source can be its own site.
|
|
77
80
|
2. `audienti prospects citation create --file employment.json` links a person to their
|
|
78
81
|
company (`edgeType` `works_at`, one current role). The first current employer
|
|
79
82
|
becomes the person's company in People.
|
|
@@ -101,9 +104,11 @@ value may follow in brackets, never on its own. The command:
|
|
|
101
104
|
- searches LinkedIn with the audience's titles, locations, industries and keyword, plus
|
|
102
105
|
the campaign's topic signals. With no person location, it searches the company
|
|
103
106
|
locations as where the person is;
|
|
104
|
-
- checks each new person against the campaign's audience rules,
|
|
105
|
-
|
|
106
|
-
company
|
|
107
|
+
- checks each new person against the campaign's audience rules, cheapest first: it reads
|
|
108
|
+
what the workspace already holds, fetches the person only if that is missing or old,
|
|
109
|
+
and fetches the company only when the audience has company rules or the campaign
|
|
110
|
+
targets companies. A person who fails a person rule is not a fit, and their company is
|
|
111
|
+
not paid for. The check also records where the person works as their primary employer;
|
|
107
112
|
- stops at N fits or before the next paid call would pass the cap.
|
|
108
113
|
|
|
109
114
|
The report lists the fits, the people held for your judgment, the not-fit count and the
|
|
@@ -101,6 +101,9 @@ capability report and ask your user to approve it in **Settings → Sending**; t
|
|
|
101
101
|
Audienti gives you a queue task to send one test email from the mailbox to itself, read it
|
|
102
102
|
back and report its code; the mailbox counts once you do
|
|
103
103
|
([the mailbox test email](references/mailbox-check.md)). Never ask your user for the code.
|
|
104
|
+
A campaign can run without a mailbox: its people with an email step wait at that step, and their
|
|
105
|
+
LinkedIn steps go on. Nothing is skipped. They go on once a mailbox is confirmed, so the fix is the
|
|
106
|
+
email connector.
|
|
104
107
|
|
|
105
108
|
## 2. Introduce yourself and propose a first campaign
|
|
106
109
|
|
|
@@ -209,7 +212,12 @@ Audienti hosted tool and the workspace has no credits left: tell the user once t
|
|
|
209
212
|
waits on credits and runs when they add credits in Billing, and do not ask again. When
|
|
210
213
|
credits are added or run out, or the user adds or removes one of their own provider keys
|
|
211
214
|
in the Vault, every agent gets a notice task: run `audienti agent capabilities` again,
|
|
212
|
-
then do what its steps say.
|
|
215
|
+
then do what its steps say. If you reported an `owner.notify` capability, Audienti puts one
|
|
216
|
+
**Tell your user about an alert** task in your queue for each alert it records. Its steps
|
|
217
|
+
give the message and a link: tell your user that message on whatever channel you use with
|
|
218
|
+
them (chat, push, Slack, SMS or email), include the link, and mark the task done once it
|
|
219
|
+
is delivered. Done means delivered: a done task is not created again for the same alert.
|
|
220
|
+
An agent that did not report `owner.notify` gets no such task. When the queue is empty, your timer runs it
|
|
213
221
|
again after `pollAfterSeconds`; when `ask` or `queue next` returns an `announce` line,
|
|
214
222
|
say it in your chat and point to Operator. Keep machine state in Audienti, not in your
|
|
215
223
|
own memory: `audienti kv set KEY VALUE` stores cursors, change tokens, hashes, ids and
|
|
@@ -23,8 +23,10 @@ Fill this before you ask the user anything. Mark each value with where it came f
|
|
|
23
23
|
## Signals: the through line
|
|
24
24
|
|
|
25
25
|
Prospect finding starts from signals: they find the companies that need the offer
|
|
26
|
-
today. A campaign with no signal
|
|
27
|
-
draft has at least one
|
|
26
|
+
today. A campaign with no signal, no target companies and no finding instructions cannot
|
|
27
|
+
start, so every signal campaign you draft has at least one signal. When the user already
|
|
28
|
+
has the people, or a source Audienti does not have, draft a custom campaign instead:
|
|
29
|
+
`method: "custom"`, finding instructions in the user's words and no signals. A signal is not "they might want to buy". It is an
|
|
28
30
|
observable event that shows a company has the problem the offer solves, now. Build the
|
|
29
31
|
line in this order:
|
|
30
32
|
|
|
@@ -183,6 +183,11 @@ user, but no task requires them yet.
|
|
|
183
183
|
| `webhook.receive` | none | read | Take a push instead of polling | later |
|
|
184
184
|
| `webhook.send` | none | send | Post events to the user's URL | later |
|
|
185
185
|
|
|
186
|
+
`owner.notify` means you can reach your user outside Audienti: chat, push, Slack, SMS or email. Report it
|
|
187
|
+
and Audienti puts a **Tell your user about an alert** task in your queue for each alert it records in
|
|
188
|
+
the workspace; the task carries the message and a link, and you mark it done once you delivered it. An
|
|
189
|
+
agent that has not reported `owner.notify` gets no such task.
|
|
190
|
+
|
|
186
191
|
### Tools you use for yourself
|
|
187
192
|
|
|
188
193
|
| Capability | Providers | Access | What it does | Launch |
|
package/source-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repository": "https://github.com/omalab/exora",
|
|
3
|
-
"sourceCommit": "
|
|
3
|
+
"sourceCommit": "213901acb79c89cc1b217fc723f843355358b7c6",
|
|
4
4
|
"files": {
|
|
5
5
|
"LICENSE": "220f297cfcd5c38eb64e6b4b4a77cab4ebc7f4686d4d99c6260a475d02ca11e5",
|
|
6
6
|
"README.md": "7ff32cc8ac89dd54b64d7b39c0b41b1f37e154b7ea10ad5a7bceefc3df77d87f",
|
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
"cli/assignment.mjs": "90cb85e0df8aadca2ff8391c99e778b41261183b5f38e8c6c32afee2f98f871a",
|
|
15
15
|
"cli/audience-operations.mjs": "133b72dc6b9e3a6b4f173ce28f1524163f9c7b049505b885115f83b9e4af7170",
|
|
16
16
|
"cli/audiences.mjs": "3c377346efc1a8bdde432a16c2d19a3f51f34f3333d344d3a2bfa94c188a017d",
|
|
17
|
-
"cli/audienti.mjs": "
|
|
17
|
+
"cli/audienti.mjs": "7a2ed9483bb971f09d156e43c8bf68e38d3698baf22eea171910b24672954ec6",
|
|
18
18
|
"cli/audit-operations.mjs": "184eb58bc479225314b77d6019a185670cd843895a10d9bf58a28de7f1355d91",
|
|
19
19
|
"cli/audit.mjs": "a7becaed67c58b063e668b5e66844b6dc15c11a63b428faa171b542ad922c001",
|
|
20
20
|
"cli/budget-operations.mjs": "d74916551ef393e29734c6e13cf9021251e0ed126eeaa529e18cf2332146e1e0",
|
|
21
21
|
"cli/budgets.mjs": "683e06cb4d410c851286fbd1cdd4fb9f1902726c60fd4fcddb2c13d164786525",
|
|
22
22
|
"cli/campaign-find.mjs": "9c0a44c69ab7ad3e24a0a2edc97b8f3a14f602e3b6caf7e1943b8e1b93609c4a",
|
|
23
|
-
"cli/campaign-operations.mjs": "
|
|
24
|
-
"cli/campaigns.mjs": "
|
|
23
|
+
"cli/campaign-operations.mjs": "c1ac42f53f8bf19957fac41cc2a80ab4e26a743a7c1cc0b42c8c0e85fcf6e61b",
|
|
24
|
+
"cli/campaigns.mjs": "63a65f3af9964ede1a62a5946c9d751110aa29b69cec0a67a451241e73ad8c1d",
|
|
25
25
|
"cli/cohort-operations.mjs": "91b1d1fc48e75581bb315f42d735ef94e21687dc216f352337d8a5d180bb691e",
|
|
26
26
|
"cli/cohorts.mjs": "c71940903430f3e6834608f113534910b75a98b3e410a5942f04051607488485",
|
|
27
27
|
"cli/connection-registry-operations.mjs": "888ea440d00ffc9ed68d4cc084ff73a5a00d0e60bd6293baba8bb4539a25ae62",
|
|
28
28
|
"cli/content.mjs": "db3fcbf7f96c314227a8172a442b70b46405126e0d61f8358cee1bfe6c627ebd",
|
|
29
|
-
"cli/discovery-operations.mjs": "
|
|
29
|
+
"cli/discovery-operations.mjs": "cc132abae230cc84439bcd24da813e2798ee1eb83a06a7d1141202ab9c68215a",
|
|
30
30
|
"cli/discovery.mjs": "34b3794ee6ca539dbbccef77ec2085fd5411023046bdfb9b83e2537414542a41",
|
|
31
31
|
"cli/file-operations.mjs": "564f334dd9d52e9c56f44d58b5c3b0bda59ff72f54126b755bc472144069349e",
|
|
32
32
|
"cli/files.mjs": "2221b067d6ade7ef5cc8965929aea2eb485781953d3f2b50600995b65fbdedae",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"cli/owner-account-client.mjs": "beb9736ac84a7ab57220ac1f1ea089b6401cf92fcf7c549417014d285f2340dc",
|
|
47
47
|
"cli/playbook-operations.mjs": "25c593a2276b06c7e35377e3f132e6eee15c0e34542df62f1cf37a339d4e4ad2",
|
|
48
48
|
"cli/profiles.mjs": "45929b224ffc916b6dbd7a94e06ac09a1ff7feec430633b10d160c1608fc2d32",
|
|
49
|
-
"cli/progress.mjs": "
|
|
49
|
+
"cli/progress.mjs": "5d0f6b6094f212fb80aef0716e0917cf177eb2349490421784acc6f7cb576c44",
|
|
50
50
|
"cli/prospecting-settings-operations.mjs": "37b881a867c19d9e5f2d02316038284ea0cf09de4f37b6dee2370a1faedf245e",
|
|
51
51
|
"cli/prospecting-settings.mjs": "b09f47c84416d9892d7d5b17dcf684cbb560e1cacb25513826ff04ef9307626e",
|
|
52
52
|
"cli/question-operations.mjs": "5d7a885f2d4e0d2ce2249aadce2b4478b95843d21761f25eabdfe2759582a3b6",
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
"cli/queue-operations.mjs": "bc5f5b8f79f056f579c2d3965d143b45ded4665629e2c4d427adfbaf7e9687fb",
|
|
55
55
|
"cli/queue.mjs": "046f4dba92ac9eacbb1f12215ab2b49623bae597342da6b415f7afb3c3a1344e",
|
|
56
56
|
"cli/recipient-operations.mjs": "7d41c17ef2d59dbed982893e0989c9c076317b91adcd3d752cbe1d2e0b8c41b2",
|
|
57
|
-
"cli/recipients.mjs": "
|
|
57
|
+
"cli/recipients.mjs": "dc93d4a23ca1df9c84a0c9734d09491af179644eed48d856d1f1a24d42276ea8",
|
|
58
58
|
"cli/rooms-operations.mjs": "09d36003183eccf2f6d2ee1ed6c4981519b8ae34fbffd0bb1a2234a8fc6cde68",
|
|
59
59
|
"cli/rooms.mjs": "7fd05b4cead1bab2e18bf724c56034e12b4304f55cc69e3fc7ea5c4536e5305c",
|
|
60
60
|
"cli/search-operations.mjs": "903f12fffb6824872160c4136925ab5638142bb4e39236b37f927cd2c6a43776",
|
|
61
|
-
"cli/secret-operations.mjs": "
|
|
61
|
+
"cli/secret-operations.mjs": "faefe9c9ece81087dc7aab32e88f331ed942d0757ce5a978b5f0722d514f377f",
|
|
62
62
|
"cli/secrets.mjs": "cab4ea932e724ce3c807c7825869810b44fb8d75cdacec32f8101353f0e7c5f5",
|
|
63
63
|
"cli/sender-account-operations.mjs": "3f5bc6b047916019a9266feb64041a7a338da94308910aa90eed6cb33e143ed0",
|
|
64
64
|
"cli/sender-accounts.mjs": "0c9be3e441aebf4bea2d2674ee54c4b17ed246c7a8476bcb4debbdad7cc1ec10",
|
|
@@ -76,21 +76,21 @@
|
|
|
76
76
|
"cli/work-feedback.mjs": "f3b77ecfd7ea1aba9c9f03c9d894137547e9c5575d9daa359ed94505ad025852",
|
|
77
77
|
"cli/work-operations.mjs": "addcfcd5049168e27d224ff879e9fcccd146228e876bcae25c9dd7cfeaa3c9a9",
|
|
78
78
|
"cli/work.mjs": "751107ecc3de5a6be9db4a068cddc41f3ad8e77c87e59686eaf522862caf9e9f",
|
|
79
|
-
"cli/workspaces.mjs": "
|
|
80
|
-
"package.json": "
|
|
79
|
+
"cli/workspaces.mjs": "09ea1fb6a416ef4810a72eab2b8bbe0fe7271e1dfe4ba29a214f31afd1af9707",
|
|
80
|
+
"package.json": "790a0840cdb4991f42dcc9883085932e4864b3001aa365b6644de1cce690be57",
|
|
81
81
|
"skills/agent-rooms/SKILL.md": "3da993292bf50f1220d2d724d74b5f46af8c8f5f054ce18bb7cf27ef29d0437d",
|
|
82
82
|
"skills/agent-rooms/references/commands.md": "7cff1ecefd74a838146344ad928b3936dafc8eb14f6747ec02c0215f207818e9",
|
|
83
|
-
"skills/audienti/SKILL.md": "
|
|
83
|
+
"skills/audienti/SKILL.md": "8f2124065bfc3a005b44e194e7dcce9bbf14062ab332e7db66d441eeccb518ed",
|
|
84
84
|
"skills/audienti/references/helpers.md": "e27aff0aa07b92f575d617612ed3def824765bac6ce1304cf227a4aca223369a",
|
|
85
85
|
"skills/audienti/references/profile-workflows.md": "80669b75eb5e8aeb614ce55ac046236d671af0de3ddd238853324579d5a3b251",
|
|
86
86
|
"skills/audienti/references/workflow-receipt.md": "349f9a85472832b8bdc6a5d16c5b10b8e32d48d3e81523ad8d42f9a03b3655ff",
|
|
87
87
|
"skills/audienti/scripts/contracts.mjs": "900428e067b7f8dae544012387bc3752f7b0d27777a80cd16ec0985848983f70",
|
|
88
88
|
"skills/audienti/scripts/profile-helpers.mjs": "7f62afdefd8e1b1bfec95f1ca6f48cfc4e204d1bff35f805261c45c6d5fcb46a",
|
|
89
89
|
"skills/audienti/scripts/run.mjs": "652094ff8b642d13f5e0bb30756bb49464c051511c37f202249fde917b319208",
|
|
90
|
-
"skills/getting-started/SKILL.md": "
|
|
90
|
+
"skills/getting-started/SKILL.md": "902fffe321a91acd2dc3583a372801df6952c7e974179198ce688b62ca8b8f21",
|
|
91
91
|
"skills/getting-started/references/audience.md": "6a2c061be5e299b3cbbd236578693ae5084d216425c500d3639a4d1fcb86ccb7",
|
|
92
|
-
"skills/getting-started/references/campaign-draft.md": "
|
|
93
|
-
"skills/getting-started/references/capabilities.md": "
|
|
92
|
+
"skills/getting-started/references/campaign-draft.md": "c2cbeb7de95734889698a5cd852268b9bed69e56f661153bb7ada237241a1a6b",
|
|
93
|
+
"skills/getting-started/references/capabilities.md": "67dc33dc6e3f5a73d77cfc14055e330b58c8f03c7a8268f7b5baa97f65e667bd",
|
|
94
94
|
"skills/getting-started/references/mailbox-check.md": "113574bb5a573125620dcde4e65ebf44164f7b0727cc936fb41234f5d57bba73",
|
|
95
95
|
"skills/getting-started/references/outcome-checks.md": "05b2431b5f89bf05026ee4e5bac3ca8df594be97640eaca6a5e38907eca6e3dd",
|
|
96
96
|
"skills/getting-started/references/replies.md": "019247b3eb260b21bf66689409748a9bb140d768fd0470a73992162fa8a5efd2",
|