@audienti/cli 0.1.144 → 0.2.0-exora.1
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/README.md +30 -902
- package/bin/audienti-mcp.mjs +2 -0
- package/cli/account-intake-operations.mjs +28 -0
- package/cli/account-intake.mjs +33 -0
- package/cli/account-replies.mjs +35 -0
- package/cli/account-reply-operations.mjs +30 -0
- package/cli/agents.mjs +107 -0
- package/cli/approach-operations.mjs +12 -0
- package/cli/assignment-operations.mjs +218 -0
- package/cli/assignment.mjs +116 -0
- package/cli/audience-operations.mjs +16 -0
- package/cli/audiences.mjs +26 -0
- package/cli/audienti.mjs +124 -0
- package/cli/connection-registry-operations.mjs +7 -0
- package/cli/content.mjs +29 -0
- package/cli/discovery-operations.mjs +27 -0
- package/cli/discovery.mjs +14 -0
- package/cli/experiment-operations.mjs +25 -0
- package/cli/experiments.mjs +26 -0
- package/cli/json-file.mjs +13 -0
- package/cli/mcp.mjs +47 -0
- package/cli/offer-operations.mjs +12 -0
- package/cli/offers.mjs +24 -0
- package/cli/owner-account-client.mjs +18 -0
- package/cli/profiles.mjs +29 -0
- package/cli/progress.mjs +12 -0
- package/cli/prospecting-settings-operations.mjs +16 -0
- package/cli/prospecting-settings.mjs +15 -0
- package/cli/recipient-operations.mjs +13 -0
- package/cli/recipients.mjs +30 -0
- package/cli/sender-account-operations.mjs +24 -0
- package/cli/sender-accounts.mjs +23 -0
- package/cli/skills-mcp.mjs +24 -0
- package/cli/skills.mjs +56 -0
- package/cli/suppression-operations.mjs +8 -0
- package/cli/suppression.mjs +12 -0
- package/cli/tools-operations.mjs +70 -0
- package/cli/tools.mjs +72 -0
- package/cli/work-execution-operations.mjs +35 -0
- package/cli/work-execution.mjs +29 -0
- package/cli/work-feedback-operations.mjs +17 -0
- package/cli/work-feedback.mjs +18 -0
- package/cli/work-operations.mjs +28 -0
- package/cli/work.mjs +27 -0
- package/cli/workspaces.mjs +158 -0
- package/package.json +15 -26
- package/skills/audienti/SKILL.md +61 -323
- package/skills/audienti/references/helpers.md +149 -0
- package/skills/audienti/references/profile-workflows.md +212 -0
- package/skills/audienti/references/workflow-receipt.md +49 -0
- package/skills/audienti/scripts/contracts.mjs +72 -0
- package/skills/audienti/scripts/profile-helpers.mjs +138 -0
- package/skills/audienti/scripts/run.mjs +40 -0
- package/source-manifest.json +59 -0
- package/.mcp.json +0 -13
- package/CHANGELOG.md +0 -1012
- package/bin/audienti-mcp.js +0 -5
- package/bin/audienti.js +0 -6
- package/install +0 -83
- package/skills/audienti/references/methodology.md +0 -111
- package/skills/audienti-gift-research/SKILL.md +0 -47
- package/src/agent-skills.js +0 -60
- package/src/api-client.js +0 -1639
- package/src/cli.js +0 -16992
- package/src/config.js +0 -130
- package/src/mcp.js +0 -103
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {approachOperations} from './approach-operations.mjs';
|
|
2
|
+
import {connectionRegistryOperations} from './connection-registry-operations.mjs';
|
|
3
|
+
import { hostedToolOperations, paidToolByName, isPaidToolOperation, paidToolResult } from './tools-operations.mjs';
|
|
4
|
+
import { workspaceExecutionOperations } from './work-execution-operations.mjs';
|
|
5
|
+
import { workFeedbackOperations } from './work-feedback-operations.mjs';
|
|
6
|
+
import { accountReplyOperations } from './account-reply-operations.mjs';
|
|
7
|
+
import { accountIntakeOperations } from './account-intake-operations.mjs';
|
|
8
|
+
import { recipientOperations } from './recipient-operations.mjs';
|
|
9
|
+
import { suppressionOperations } from './suppression-operations.mjs';
|
|
10
|
+
import { workOperations } from './work-operations.mjs';
|
|
11
|
+
import { assignmentOperations } from './assignment-operations.mjs';
|
|
12
|
+
import {discoveryOperations} from './discovery-operations.mjs';
|
|
13
|
+
import {experimentOperations} from './experiment-operations.mjs';
|
|
14
|
+
import {audienceOperations} from './audience-operations.mjs';
|
|
15
|
+
import { readFileSync, writeFileSync, renameSync, mkdirSync, statSync, lstatSync, realpathSync } from 'node:fs';
|
|
16
|
+
import { dirname, resolve, relative, isAbsolute } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import {offerOperations} from './offer-operations.mjs';
|
|
20
|
+
const uuid = z.string().uuid();
|
|
21
|
+
const workspace = { workspaceId: uuid.optional() };
|
|
22
|
+
export const operations = {
|
|
23
|
+
...approachOperations,
|
|
24
|
+
...connectionRegistryOperations,
|
|
25
|
+
...hostedToolOperations,
|
|
26
|
+
...workOperations,
|
|
27
|
+
...workspaceExecutionOperations,
|
|
28
|
+
...workFeedbackOperations,
|
|
29
|
+
...suppressionOperations,
|
|
30
|
+
...recipientOperations,
|
|
31
|
+
...accountIntakeOperations,
|
|
32
|
+
...accountReplyOperations,
|
|
33
|
+
...assignmentOperations,
|
|
34
|
+
...discoveryOperations,
|
|
35
|
+
...offerOperations,
|
|
36
|
+
...audienceOperations,
|
|
37
|
+
...experimentOperations,
|
|
38
|
+
workspace_sending_settings_get: {schema:z.object({...workspace,userId:uuid.optional()}).strict(),method:'GET',path:a=>`/workspaces/${a.workspaceId}/sending-settings${a.userId?'?userId='+encodeURIComponent(a.userId):''}`,scoped:true,description:'Read workspace sending defaults, or own member effective hours, ramp and activity limits with userId. A workspace Admin may read another member. External activities remain agent-managed; these values do not authorize sending.'},
|
|
39
|
+
workspace_member: {schema:z.object({...workspace,userId:uuid}).strict(),method:'GET',path:a=>`/workspaces/${a.workspaceId}/members/${a.userId}`,scoped:true},
|
|
40
|
+
workspace_agent_access: { schema:z.object(workspace).strict(), method:'GET', path:a=>`/workspaces/${a.workspaceId}/agent-access`, scoped:true },
|
|
41
|
+
workspace_agent_access_set: { schema:z.object({...workspace,userId:uuid,blocked:z.boolean()}).strict(), method:'POST', path:a=>`/workspaces/${a.workspaceId}/members/${a.userId}/agent-access`, body:a=>({blocked:a.blocked}), scoped:true },
|
|
42
|
+
workspace_initialize: { schema: z.object({}).strict(), method: 'POST', path: () => '/workspaces/bootstrap' },
|
|
43
|
+
workspace_list: { schema: z.object({}).strict(), method: 'GET', path: () => '/workspaces' },
|
|
44
|
+
workspace_create: { schema: z.object({ name: z.string().trim().min(1).max(80), requestId: uuid }).strict(), method: 'POST', path: () => '/workspaces', body: a => a },
|
|
45
|
+
workspace_get: { schema: z.object(workspace).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}`, scoped: true },
|
|
46
|
+
workspace_invite: { schema: z.object({ ...workspace, email: z.string().trim().email() }).strict(), method: 'POST', path: a => `/workspaces/${a.workspaceId}/invitations`, body: a => ({ email: a.email }), scoped: true },
|
|
47
|
+
workspace_invitation_retry: { schema: z.object({ ...workspace, id: uuid }).strict(), method: 'POST', path: a => `/workspaces/${a.workspaceId}/invitations/${a.id}/retry`, scoped: true },
|
|
48
|
+
workspace_invitation_cancel: { schema: z.object({ ...workspace, id: uuid }).strict(), method: 'POST', path: a => `/workspaces/${a.workspaceId}/invitations/${a.id}/cancel`, scoped: true },
|
|
49
|
+
workspace_invitations: { schema: z.object({}).strict(), method: 'GET', path: () => '/me/invitations' },
|
|
50
|
+
workspace_invitation_get: { schema: z.object({ id: uuid, generation: z.number().int().positive() }).strict(), method: 'GET', path: a => `/invitations/${a.id}?generation=${a.generation}` },
|
|
51
|
+
workspace_invitation_accept: { schema: z.object({ id: uuid, generation: z.number().int().positive() }).strict(), method: 'POST', path: a => `/invitations/${a.id}/accept`, body: a => ({ generation: a.generation }) },
|
|
52
|
+
workspace_select: { schema: z.object({ workspaceId: uuid }).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}` },
|
|
53
|
+
};
|
|
54
|
+
function profilePath(path) {
|
|
55
|
+
const target=resolve(path); const parent=dirname(target);
|
|
56
|
+
const repo=realpathSync(fileURLToPath(new URL('..',import.meta.url)));
|
|
57
|
+
const lexical=relative(repo,parent);
|
|
58
|
+
if(!lexical || (!lexical.startsWith('..') && !isAbsolute(lexical))) throw new Error('Store CLI profiles outside the repository.');
|
|
59
|
+
mkdirSync(parent,{recursive:true,mode:0o700});
|
|
60
|
+
const realParent=realpathSync(parent);
|
|
61
|
+
const rel=relative(repo,realParent);
|
|
62
|
+
if (!rel || (!rel.startsWith('..') && !isAbsolute(rel))) throw new Error('Store CLI profiles outside the repository.');
|
|
63
|
+
return resolve(realParent,target.slice(parent.length+1));
|
|
64
|
+
}
|
|
65
|
+
export function readProfile(path) {
|
|
66
|
+
if (!path) return {};
|
|
67
|
+
const target=profilePath(path);
|
|
68
|
+
try {
|
|
69
|
+
if(lstatSync(target).isSymbolicLink()) throw new Error("CLI profiles must not be symbolic links.");
|
|
70
|
+
const stat=statSync(target);
|
|
71
|
+
if ((stat.mode & 0o077) !== 0 || stat.uid !== process.getuid?.()) throw new Error('CLI profile must be owned by you and readable only by you (chmod 600).');
|
|
72
|
+
return JSON.parse(readFileSync(target,'utf8'));
|
|
73
|
+
} catch(error) {if(error.code==='ENOENT') return {};throw new Error('Could not read a private CLI profile. Check ownership, permissions and JSON format.');}
|
|
74
|
+
}
|
|
75
|
+
export function writeProfile(path, profile) {
|
|
76
|
+
if (!path) throw new Error('Supply --profile PATH outside the repository.');
|
|
77
|
+
const target=profilePath(path); const temporary=`${target}.${process.pid}.tmp`;
|
|
78
|
+
writeFileSync(temporary,JSON.stringify(profile)+'\n',{mode:0o600,flag:'wx'}); renameSync(temporary,target);
|
|
79
|
+
}
|
|
80
|
+
export function configuration(path, scope='workspace:access') {
|
|
81
|
+
const profile=readProfile(path);
|
|
82
|
+
const agentProfile = Boolean(profile.agent || profile.pendingAgent || profile.connectionSecret);
|
|
83
|
+
return { url:agentProfile ? profile.url : process.env.AUDIENTI_API_URL ?? profile.url, token:agentProfile ? (scope === 'workspace:access' ? profile.tokens?.['workspace:read'] : undefined) : process.env.AUDIENTI_API_TOKEN ?? (scope === 'workspace:access' ? profile.tokens?.['workspace:read'] ?? profile.tokens?.[scope] : profile.tokens?.[scope]), workspaceId:profile.workspaceId, profile };
|
|
84
|
+
}
|
|
85
|
+
export function validateUrl(value) {
|
|
86
|
+
let url;try {url=new URL(value);}catch{throw new Error('Set AUDIENTI_API_URL to an API URL ending with /api/v1.');}
|
|
87
|
+
if(url.username || url.password || url.search || url.hash || url.pathname.replace(/\/$/,'')!=='/api/v1') throw new Error('The API URL must end with /api/v1 and contain no credentials, query or fragment.');
|
|
88
|
+
if(url.protocol!=='https:' && !(url.protocol==='http:' && ['localhost','127.0.0.1','[::1]'].includes(url.hostname))) throw new Error('Use HTTPS for remote API hosts.');
|
|
89
|
+
return url;
|
|
90
|
+
}
|
|
91
|
+
export class WorkspaceRequestError extends Error {
|
|
92
|
+
constructor(status, code, message, retryAt, blockers, requestTraceId) { super(message); this.status = status; this.code = code; this.retryAt = retryAt; this.blockers = blockers; this.requestTraceId = requestTraceId; }
|
|
93
|
+
toJSON() { return { ...(this.requestId ? {requestId:this.requestId,recovery:this.recovery} : {}), ...(this.requestTraceId ? {requestTraceId:this.requestTraceId} : {}), code:this.code, status:this.status, message:this.message, ...(this.retryAt ? {retryAt:this.retryAt} : {}), ...(this.blockers ? {blockers:this.blockers} : {}) }; }
|
|
94
|
+
}
|
|
95
|
+
export class HostedResponseUnknown extends Error {
|
|
96
|
+
constructor(requestId, reason, requestTraceId) { super('The server response is unknown. Paid work may have started. Retry using the same requestId and principal; never generate a new ID to recover this call.'); this.requestId=requestId; this.reason=reason; this.requestTraceId=requestTraceId; }
|
|
97
|
+
toJSON() { return {...(this.requestTraceId?{requestTraceId:this.requestTraceId}:{}),code:'response_unknown',requestId:this.requestId,reason:this.reason,message:this.message,recovery:'retry_same_request_id'}; }
|
|
98
|
+
}
|
|
99
|
+
export async function callWorkspace(name, args, config) {
|
|
100
|
+
const operation=operations[name]; if(!operation) throw new Error('Unknown workspace operation.');
|
|
101
|
+
const parsed=operation.schema.safeParse(args);if(!parsed.success) throw new Error('Invalid workspace arguments. Check IDs and required fields.');
|
|
102
|
+
const input={...parsed.data};
|
|
103
|
+
if(operation.scoped) { input.workspaceId ??= config.workspaceId;if(!uuid.safeParse(input.workspaceId).success) throw new Error('Select a workspace or supply --workspace ID.'); }
|
|
104
|
+
const url=validateUrl(config.url); if(!config.token) throw new Error('A workspace:access token is required. Configure the environment or a private profile.');
|
|
105
|
+
const target=new URL(`/api/v1${operation.path(input)}`,url);
|
|
106
|
+
let response;try {response=await fetch(target,{method:operation.method,headers:{Authorization:`Bearer ${config.token}`,Accept:'application/json','Content-Type':'application/json',...((operation.traced||name.startsWith('tools_'))?{'X-Audienti-Interface':config.interface==='mcp'?'mcp':'cli'}:{})},...(['POST','PUT','PATCH'].includes(operation.method)?{body:JSON.stringify(operation.body?.(input)??{})}:{}),redirect:'error',signal:AbortSignal.timeout(operation.timeoutMs ?? 10000)});}catch(error){if(isPaidToolOperation(name))throw new HostedResponseUnknown(input.requestId,['TimeoutError','AbortError'].includes(error?.name)?'timeout':'transport_failure');throw new Error('Could not reach the API. Redirects are not followed.');}
|
|
107
|
+
const headerTrace=(operation.traced||name.startsWith('tools_'))&&uuid.safeParse(response.headers.get('x-request-trace-id')).success?response.headers.get('x-request-trace-id'):undefined;
|
|
108
|
+
let result;try{result=await response.json();}catch{if(isPaidToolOperation(name))throw new HostedResponseUnknown(input.requestId,'invalid_response',headerTrace);throw new Error('The API returned an invalid response.');}
|
|
109
|
+
const requestTraceId=uuid.safeParse(result?.requestTraceId).success?result.requestTraceId:headerTrace;
|
|
110
|
+
if(!response.ok) {
|
|
111
|
+
const code = typeof result?.error?.code==='string' && /^[a-z_]+$/.test(result.error.code) ? result.error.code : 'request_failed';
|
|
112
|
+
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;
|
|
113
|
+
const blockers = code==='execution_blocked' && 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;
|
|
114
|
+
const error = new WorkspaceRequestError(response.status, code, `Workspace request failed: ${code} (HTTP ${response.status}).${retryAt ? ` Retry after ${retryAt}.` : ''}`, retryAt, blockers, requestTraceId);
|
|
115
|
+
if(isPaidToolOperation(name)){error.requestId=input.requestId;error.recovery=response.status>=500?'retry_same_request_id':'inspect_rejection';}
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
if(!result || !Object.hasOwn(result,'data')) {if(isPaidToolOperation(name))throw new HostedResponseUnknown(input.requestId,'invalid_response',requestTraceId);throw new Error('The API returned an invalid response.');}
|
|
119
|
+
const paidResult = paidToolResult(name, result?.data);
|
|
120
|
+
if(isPaidToolOperation(name)&&(!uuid.safeParse(paidResult?.operationId).success||paidResult.requestId!==input.requestId||!['running','succeeded','failed','uncertain','rejected'].includes(paidResult.status)))throw new HostedResponseUnknown(input.requestId,'invalid_response',requestTraceId);
|
|
121
|
+
if(name==='workspace_select') config.workspaceId=input.workspaceId;
|
|
122
|
+
return {data:result.data,...(requestTraceId?{requestTraceId}:{})};
|
|
123
|
+
}
|
|
124
|
+
export async function workspaceCli(args) {
|
|
125
|
+
const flags={}; const position=[];
|
|
126
|
+
for(let i=0;i<args.length;i++) {
|
|
127
|
+
if(args[i]==='--json') continue;
|
|
128
|
+
if(args[i].startsWith('--')) {const key=args[i].slice(2);if(!['profile','workspace','name','request-id','email','generation','scope','user'].includes(key)||!args[i+1]||args[i+1].startsWith('--')) throw new Error('Unknown or incomplete option.');flags[key]=args[++i];}
|
|
129
|
+
else position.push(args[i]);
|
|
130
|
+
}
|
|
131
|
+
if(position[0]==='profile' && position[1]==='save') {
|
|
132
|
+
if(!['account:read','workspace:access','agents:manage'].includes(flags.scope)) throw new Error('Specify --scope account:read, workspace:access or agents:manage.');
|
|
133
|
+
const url=validateUrl(process.env.AUDIENTI_API_URL).toString().replace(/\/$/,'');const token=process.env.AUDIENTI_API_TOKEN;if(!token) throw new Error('Set AUDIENTI_API_TOKEN.');
|
|
134
|
+
const profile=readProfile(flags.profile);if(profile.agent || profile.pendingAgent || profile.connectionSecret) throw new Error('Use a separate user profile; agent profiles cannot store user API tokens.');writeProfile(flags.profile,{...profile,url,tokens:{...profile.tokens,[flags.scope]:token}});console.log('Profile saved.');return;
|
|
135
|
+
}
|
|
136
|
+
const config=configuration(flags.profile);const command=position.slice(1).join(' ');
|
|
137
|
+
let name, input={};
|
|
138
|
+
if(command==='sending-settings') {name='workspace_sending_settings_get';input={workspaceId:flags.workspace,...(flags.user?{userId:flags.user}:{})};}
|
|
139
|
+
else if(command==='agent-access') {name='workspace_agent_access';input={workspaceId:flags.workspace};}
|
|
140
|
+
else if(['agent-access block','agent-access restore'].includes(command)) {name='workspace_agent_access_set';input={workspaceId:flags.workspace,userId:flags.user,blocked:command.endsWith('block')};}
|
|
141
|
+
else if(command==='init') name='workspace_initialize';
|
|
142
|
+
else if(command==='list') name='workspace_list';
|
|
143
|
+
else if(command==='create') {name='workspace_create';input={name:flags.name,requestId:flags['request-id']};}
|
|
144
|
+
else if(command==='show') {name='workspace_get';input={workspaceId:flags.workspace};}
|
|
145
|
+
else if(command==='invite') {name='workspace_invite';input={workspaceId:flags.workspace,email:flags.email};}
|
|
146
|
+
else if(command==='invitations') name='workspace_invitations';
|
|
147
|
+
else if(position[1]==='member' && position.length===3) {name='workspace_member';input={workspaceId:flags.workspace,userId:position[2]};}
|
|
148
|
+
else if(position[1]==='use' && position.length===3) {name='workspace_select';input={workspaceId:position[2]};}
|
|
149
|
+
else if(position[1]==='invitation' && position.length===4) {
|
|
150
|
+
const action=position[2];name={show:'workspace_invitation_get',accept:'workspace_invitation_accept',retry:'workspace_invitation_retry',cancel:'workspace_invitation_cancel'}[action];
|
|
151
|
+
input={id:position[3],...(['retry','cancel'].includes(action)?{workspaceId:flags.workspace}:{generation:Number(flags.generation)})};
|
|
152
|
+
}
|
|
153
|
+
if(!name) throw new Error('Unknown workspace command. Use audienti --help.');
|
|
154
|
+
const result=await callWorkspace(name,input,config);
|
|
155
|
+
|
|
156
|
+
if(name==='workspace_select') writeProfile(flags.profile,{...config.profile,workspaceId:config.workspaceId});
|
|
157
|
+
console.log(JSON.stringify(result));
|
|
158
|
+
}
|
package/package.json
CHANGED
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@audienti/cli",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0-exora.1",
|
|
4
|
+
"description": "Audienti CLI and MCP client for exo.audienti.com",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"audienti": "
|
|
8
|
-
"audienti-mcp": "
|
|
7
|
+
"audienti": "cli/audienti.mjs",
|
|
8
|
+
"audienti-mcp": "bin/audienti-mcp.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
+
"cli/",
|
|
11
12
|
"bin/",
|
|
12
|
-
"src/",
|
|
13
13
|
"skills/",
|
|
14
|
-
".mcp.json",
|
|
15
|
-
"install",
|
|
16
14
|
"README.md",
|
|
17
15
|
"LICENSE",
|
|
18
|
-
"
|
|
16
|
+
"source-manifest.json"
|
|
19
17
|
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"test": "node --test test/*.test.js",
|
|
22
|
-
"verify": "node scripts/verify-package.mjs",
|
|
23
|
-
"check": "npm run verify && npm test"
|
|
24
|
-
},
|
|
25
18
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
19
|
+
"node": ">=24 <25"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
23
|
+
"zod": "4.6.7"
|
|
27
24
|
},
|
|
28
25
|
"repository": {
|
|
29
26
|
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/audienti/cli.git"
|
|
31
|
-
|
|
32
|
-
"homepage": "https://github.com/audienti/cli#readme",
|
|
33
|
-
"bugs": {
|
|
34
|
-
"url": "https://github.com/audienti/cli/issues"
|
|
27
|
+
"url": "git+https://github.com/audienti/cli.git",
|
|
28
|
+
"directory": "exora"
|
|
35
29
|
},
|
|
36
30
|
"license": "SEE LICENSE IN LICENSE",
|
|
37
|
-
"keywords": [
|
|
38
|
-
"audienti",
|
|
39
|
-
"cli",
|
|
40
|
-
"agents",
|
|
41
|
-
"go-to-market"
|
|
42
|
-
],
|
|
43
31
|
"publishConfig": {
|
|
44
|
-
"access": "public"
|
|
32
|
+
"access": "public",
|
|
33
|
+
"tag": "exora"
|
|
45
34
|
}
|
|
46
35
|
}
|
package/skills/audienti/SKILL.md
CHANGED
|
@@ -1,337 +1,75 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: audienti
|
|
3
|
-
description:
|
|
3
|
+
description: Find, enrich, and expand CRM profile research using Audienti's account-scoped records, explicit paid tools, and executable deterministic helpers. Use for saved Prospect/Profile lookup, known profile URLs, relevant posts and their participants, or discovering additional profiles for the same identity.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Audienti
|
|
6
|
+
# Audienti profile research
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
You own the research workflow. Audienti keeps canonical Prospect/Profile records,
|
|
9
|
+
citations, authorization and paid-tool accounting. A Prospect is a person or company;
|
|
10
|
+
it can have many source Profiles. Employment links a person and company through
|
|
11
|
+
citations; it does not make them one identity.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
sending controls still apply.
|
|
13
|
+
First inspect the selected workspace, current permissions and available tools. Use
|
|
14
|
+
the account the user selected; never search another workspace or global cache as a
|
|
15
|
+
substitute. Discover current contracts with `audienti tools list`, `tools describe`
|
|
16
|
+
and resource help. Read [the workflow recipes](references/profile-workflows.md)
|
|
17
|
+
and [helper contracts and installation](references/helpers.md). Keep an
|
|
18
|
+
[agent research receipt](references/workflow-receipt.md) as you work.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
and follow the returned skill using your own browsing tools. The command supplies
|
|
23
|
-
instructions; it does not perform research or call the app. The bundled
|
|
24
|
-
`audienti-gift-research` skill also works independently of this production
|
|
25
|
-
workflow. Do not use offer scans or create gifts for this research exercise.
|
|
20
|
+
## Executable helpers
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
links and install commands, not bundled upstream implementations. Read the
|
|
30
|
-
upstream README and applicable skill before use; its dependencies and permissions
|
|
31
|
-
still apply. Installation requires a separate instruction from the user.
|
|
22
|
+
The skill includes four deterministic helpers. They consume supplied JSON; none
|
|
23
|
+
performs research, calls a model, reads the CRM, writes records or charges credits.
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
access is missing, report the missing capability and ask the owner; do not invent
|
|
39
|
-
findings, install tools or silently switch networks. Read the environment check
|
|
40
|
-
returned by `audienti skills show <finder>` before following its upstream skill.
|
|
41
|
-
The full returned check applies. It does not authorize granting access, sending
|
|
42
|
-
messages or posting comments to test access.
|
|
43
|
-
|
|
44
|
-
Set `approach` in a Motion create/update payload, or use `motions update <id> --approach "..."`. A nonblank Approach automatically guides adaptive planning and writing; blank, nil, or whitespace keeps the existing sequence. The simple `--approach ""` flag explicitly clears the guidance and restores that sequence. There is no separate planning-mode selector, and existing sending controls still apply.
|
|
45
|
-
|
|
46
|
-
To clear an Inbox Ops backlog, run `inbox-ops queue` first: it follows every page, numbers each row, and saves the numbered list locally for the selected account. Then use `inbox-ops ignore`, `filter-domain`, `filter-sender`, `allow-domain`, or `allow-sender` with numbers, ranges (`1-25,30`), row ids, `--domain`, or `--sender`. Always run `--dry-run` first when acting for a person, read the per-row planned/skipped/rejected results back, and only then re-run with `--yes`. Numbers stay valid until `inbox-ops queue` runs again; ignore hides one thread until a new reply, while filter and allow rules are durable across all of the owner's accounts. Rule verbs given only `--domain` or `--sender` write the keyed rule directly. Through MCP use `inbox_ops.queue`, then `inbox_ops.actions` with `{operation, row_ids, dry_run}`.
|
|
47
|
-
|
|
48
|
-
When Operator exposes `answer_planner_question`, read its prompt and candidate options with `operator queue` or `operator next`. Use `operator answer <row_id> --choice <id>` or `--answer "..."`, exactly one input. This command refetches the exact row and submits the current row and decision fingerprints. Do not substitute a generic outcome, a reusable steer note, or a guessed send. A 409 means refresh the question; 202 means the answer was recorded and planning continues. Answers apply only to that decision.
|
|
49
|
-
|
|
50
|
-
## Setup
|
|
51
|
-
|
|
52
|
-
1. Verify the command is installed:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
audienti --help
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
2. If it is unavailable, install the public package:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
curl -fsSL https://cli.audienti.com/install | bash
|
|
25
|
+
```sh
|
|
26
|
+
audienti skills helper profile-queries --file context.json --json
|
|
27
|
+
audienti skills helper profile-candidates --file results.json --json
|
|
28
|
+
audienti skills helper profile-completeness --file field-states.json --json
|
|
29
|
+
audienti skills helper profile-evidence --file supplied-text.json --json
|
|
62
30
|
```
|
|
63
31
|
|
|
64
|
-
|
|
65
|
-
production token into chat, a repository file, an issue, or a CI secret. For a
|
|
66
|
-
person at a terminal, the friendly first run is:
|
|
32
|
+
Without the CLI, run the same bundled code with Node:
|
|
67
33
|
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
audienti setup
|
|
34
|
+
```sh
|
|
35
|
+
node scripts/run.mjs profile-queries --file context.json
|
|
71
36
|
```
|
|
72
37
|
|
|
73
|
-
`audienti
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
audienti auth login
|
|
113
|
-
audienti accounts list --json
|
|
114
|
-
audienti accounts create --name Doppel --parent OMALab --json
|
|
115
|
-
audienti accounts select <acct_id>
|
|
116
|
-
audienti users select me
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
If the MCP host can call HTTP directly, use `POST /mcp` with the same bearer
|
|
120
|
-
token as the API. Call `tools/list` first, then call named tools with
|
|
121
|
-
`tools/call`. Account-scoped tools accept `account_id`; the local bridge injects
|
|
122
|
-
the selected CLI account when it is omitted.
|
|
123
|
-
|
|
124
|
-
Useful MCP tools:
|
|
125
|
-
|
|
126
|
-
- `auth.me` confirms the token identity.
|
|
127
|
-
- `accounts.list` shows accessible accounts.
|
|
128
|
-
- `setup.play_preflight` returns connected-account readiness and setup or
|
|
129
|
-
mapping URLs, including the selected sender's active days, working hours,
|
|
130
|
-
effective timezone, current in-window status, configured user fallback
|
|
131
|
-
location, effective proxy geography and source, and server-calculated
|
|
132
|
-
LinkedIn pacing and outstanding-invitation inventory authority under
|
|
133
|
-
`social_cookie.automation`. Treat `ready: true` as connection readiness, not
|
|
134
|
-
approval to start automation.
|
|
135
|
-
- `offers.create`, `icps.create`, and `motions.create` set up the offer, ICP,
|
|
136
|
-
and play.
|
|
137
|
-
- Use `audienti icps list --status all` to audit active and archived ICPs. Use
|
|
138
|
-
`audienti icps archive <icp_id>` for reversible cleanup and `restore` to make
|
|
139
|
-
the ICP selectable again; restore never reactivates motions.
|
|
140
|
-
- Use `audienti motions abm-companies <motn_id> add --file <txt|json>` to attach
|
|
141
|
-
a positive company filter list for isolated ABM discovery runs.
|
|
142
|
-
- `analytics.stages` returns stage conversion cohorts and stage aging.
|
|
143
|
-
- `analytics.cohort_lists.create` materializes event cohorts as reusable lists.
|
|
144
|
-
|
|
145
|
-
## Operating Rules
|
|
146
|
-
|
|
147
|
-
- Use `--json` whenever another agent or tool will consume the response.
|
|
148
|
-
- Use `audienti <resource> <action> help` before a mutation when the accepted
|
|
149
|
-
payload or behavior is unclear.
|
|
150
|
-
- Inspect the current resource before a create, update, attach, delete, or
|
|
151
|
-
operator outcome writeback.
|
|
152
|
-
- For account-user automation, run `users automation show`, then preview the
|
|
153
|
-
exact payload without `--apply`. Apply only after explicit authorization and
|
|
154
|
-
verify the applied response/readback.
|
|
155
|
-
- Treat the production API as the source of truth. Persist durable work in
|
|
156
|
-
Audienti rather than leaving it only in agent prose.
|
|
157
|
-
- Keep current gaps explicit. Do not imply that unsupported actions execute.
|
|
158
|
-
|
|
159
|
-
## Common Entry Points
|
|
160
|
-
|
|
161
|
-
```bash
|
|
162
|
-
audienti help agent-workflows
|
|
163
|
-
audienti prospects list --query "name or company" --wide --json
|
|
164
|
-
audienti prospects list --assigned-user unassigned --json
|
|
165
|
-
audienti prospects assign <prsp_id> --assigned-user me --json
|
|
166
|
-
audienti prospects move-account <prsp_id> --account <source_acct_id> --target-account <target_acct_id> --json
|
|
167
|
-
audienti prospects move-account <prsp_id> --account <source_acct_id> --target-account <target_acct_id> --assigned-user me --apply --json
|
|
168
|
-
audienti prospects set-status <prsp_id> --status not_fit --json
|
|
169
|
-
audienti prospects lock <prsp_id> --note "Emergency hold" --json
|
|
170
|
-
audienti prospects unlock <prsp_id> --json
|
|
171
|
-
audienti users activity me --window 7d --json
|
|
172
|
-
audienti users automation show <account_user_id|me> --platform linkedin --json --account <acct_id>
|
|
173
|
-
audienti users automation update <account_user_id|me> --payload automation-controls.json --json --account <acct_id>
|
|
174
|
-
audienti users automation update <account_user_id|me> --payload automation-controls.json --apply --json --account <acct_id>
|
|
175
|
-
audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] --json
|
|
176
|
-
audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me --json
|
|
177
|
-
audienti lists create --name "Target list" --json
|
|
178
|
-
audienti lists routing-rules <list_id> list --json
|
|
179
|
-
audienti lists routing-rules <list_id> create --payload routing-rule.json --json
|
|
180
|
-
audienti lists routing-rules <list_id> move <rule_id> up --json
|
|
181
|
-
audienti lists routing-rules <list_id> apply --json
|
|
182
|
-
audienti motions quick-start --url https://example.com --wait --confirm --json
|
|
183
|
-
audienti motions abm-companies <motn_id> add --file abm-domains.txt --json
|
|
184
|
-
audienti motions abm-companies <motn_id> list --json
|
|
185
|
-
audienti motions update <motn_id> --status paused --json
|
|
186
|
-
audienti motions update <motn_id> --start-date 2026-09-01 --end-date 2026-09-30 --maximum-company-count 25 --json
|
|
187
|
-
audienti motions update <motn_id> --payload motion-principal-list.json --json
|
|
188
|
-
audienti motions activate <motn_id> --json
|
|
189
|
-
audienti motions delete <motn_id> --confirm yes --json
|
|
190
|
-
audienti operator next --json
|
|
191
|
-
audienti operator next --plan
|
|
192
|
-
audienti network-ops queue --json
|
|
193
|
-
audienti network-ops accept <row_id> --account <acct_id> --json
|
|
194
|
-
audienti network-ops decline <row_id> --account <acct_id> --json
|
|
195
|
-
audienti inbox-ops queue --json
|
|
196
|
-
audienti inbox-ops queue --group-by domain
|
|
197
|
-
audienti inbox-ops ignore 1-25 --dry-run --json
|
|
198
|
-
audienti inbox-ops ignore 1-25 --yes --json
|
|
199
|
-
audienti inbox-ops filter-domain --domain alerts.example.com --yes --json
|
|
200
|
-
audienti inbox-ops filter-sender 3,7 --yes --json
|
|
201
|
-
audienti inbox-ops filters --json
|
|
202
|
-
audienti inbox-ops rule <row_id> --scope <sender|domain> --disposition <allow|filter> --json
|
|
203
|
-
audienti analytics motions --json
|
|
204
|
-
audienti analytics icps --json
|
|
205
|
-
audienti analytics prospects --window 24h --json
|
|
206
|
-
audienti analytics stages --window 30d
|
|
207
|
-
audienti analytics visibility --window 24h --user me --json
|
|
208
|
-
audienti analytics content --window week --json
|
|
209
|
-
audienti tools list --json
|
|
210
|
-
audienti tools humanize --file draft.txt --tone professional --json
|
|
211
|
-
audienti tools email-find --first-name Ada --last-name Lovelace --company-domain example.com --wait --json
|
|
212
|
-
audienti tools email-find --linkedin-url https://www.linkedin.com/in/example --wait --json
|
|
213
|
-
audienti tools linkedin-enrich --url https://www.linkedin.com/in/example --wait --json
|
|
214
|
-
audienti tools signals-find --icp "<ICP description>" --question "<signal question>" --wait --json
|
|
215
|
-
audienti tools write --purpose "<goal>" --audience "<who>" --fact "<fact>" --channel email --wait --json
|
|
216
|
-
audienti tools runs show <trun_id> --json
|
|
217
|
-
audienti tools runs results <trun_id> --json
|
|
218
|
-
audienti network list --cookie <scok_id> --kind connection --json
|
|
219
|
-
audienti tools linkedin-review --url https://www.linkedin.com/in/example --icp <icp_id> --json
|
|
220
|
-
audienti tools linkedin-review reports --json
|
|
221
|
-
audienti tools linkedin-review show <rprt_id> --json
|
|
222
|
-
audienti tools linkedin-review status <rprt_id> --json
|
|
223
|
-
audienti writer test-run <prsp_id>
|
|
224
|
-
audienti writer test-run <prsp_id> --mode report --report <rprt_id>
|
|
225
|
-
audienti writer test-run <prsp_id> --mode step --branch no-accept --step 3 --report <rprt_id>
|
|
226
|
-
audienti writer test-run <prsp_id> --mode step --branch no-accept --step 3 --report <rprt_id> --no-wait
|
|
227
|
-
audienti writer test-run show <prsp_id> <rprt_id>
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## Prospect Account Moves
|
|
231
|
-
|
|
232
|
-
Use `audienti prospects move-account` only to transfer one prospect and its
|
|
233
|
-
account-scoped history, not to copy it. `--account` is the source account and
|
|
234
|
-
`--target-account` is the destination. The authenticated user must administer
|
|
235
|
-
both accounts.
|
|
236
|
-
|
|
237
|
-
Start with a preview and inspect `eligible`, `blockers`, `mappings`,
|
|
238
|
-
`dispositions`, and `expected_state`:
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
audienti prospects move-account <prsp_id> \
|
|
242
|
-
--account <source_acct_id> \
|
|
243
|
-
--target-account <target_acct_id> \
|
|
244
|
-
--assigned-user <target_account_user_id|me> \
|
|
245
|
-
--target-motion <target_motn_id> \
|
|
246
|
-
--target-list <target_list_id> \
|
|
247
|
-
--json
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
Omit mappings that were not explicitly chosen; never infer a target motion,
|
|
251
|
-
list, agent, or research mapping by name. Apply only after the preview is
|
|
252
|
-
eligible and the user authorizes the move:
|
|
253
|
-
|
|
254
|
-
```bash
|
|
255
|
-
audienti prospects move-account <prsp_id> \
|
|
256
|
-
--account <source_acct_id> \
|
|
257
|
-
--target-account <target_acct_id> \
|
|
258
|
-
--assigned-user <target_account_user_id|me> \
|
|
259
|
-
--target-motion <target_motn_id> \
|
|
260
|
-
--target-list <target_list_id> \
|
|
261
|
-
--apply \
|
|
262
|
-
--json
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
With `--apply`, the CLI performs a fresh preview and submits that response's
|
|
266
|
-
`manifest_digest`; it does not reuse an older preview. Treat a stale manifest,
|
|
267
|
-
any blocker, or any failed response as not applied, then inspect current source
|
|
268
|
-
and target state before retrying.
|
|
269
|
-
|
|
270
|
-
## Account-User Automation Safety
|
|
271
|
-
|
|
272
|
-
`audienti users automation show <account_user_id|me>` resolves one connected
|
|
273
|
-
account through the selected account, account user, and LinkedIn platform. Use
|
|
274
|
-
per-command `--account` for administrative inspection without changing the
|
|
275
|
-
saved default. Fail closed on API ambiguity or missing access; do not substitute
|
|
276
|
-
another connected account.
|
|
277
|
-
|
|
278
|
-
Automation updates are preview-first:
|
|
279
|
-
|
|
280
|
-
```bash
|
|
281
|
-
audienti users automation show 136 --platform linkedin --json --account acct_example
|
|
282
|
-
audienti users automation update 136 --payload automation-controls.json --json --account acct_example
|
|
283
|
-
audienti users automation update 136 --payload automation-controls.json --apply --json --account acct_example
|
|
284
|
-
audienti users automation show 136 --platform linkedin --json --account acct_example
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
The payload object accepts `automation_controls`, `action_limits`, and
|
|
288
|
-
`visibility_ramp`. Action-limit keys are `profile_view`, `follow`, `like`,
|
|
289
|
-
`invite`, `message`, `comment`, and aggregate `visibility`; each action limit
|
|
290
|
-
may provide `hourly`, `daily`, and `weekly`. `follow` covers follow/unfollow,
|
|
291
|
-
`like` covers like/unlike, and `message` covers messages/InMail. A ramp accepts
|
|
292
|
-
`enabled`, `starting_daily_limit`, `weekly_increment`, and optional
|
|
293
|
-
`started_at`.
|
|
294
|
-
|
|
295
|
-
The CLI wraps the file as `{ automation: { ...payload, platform: "linkedin",
|
|
296
|
-
apply: false|true } }`; file values cannot override the command's platform or
|
|
297
|
-
apply decision. Omitted settings are preserved by the server. Use unchanged
|
|
298
|
-
JSON output to compare current, before, and after snapshots, including policy
|
|
299
|
-
source, schedule/timezone, configured/default/effective limits, used and
|
|
300
|
-
remaining capacity, binding limits, preview/applied state, and applied audit id.
|
|
301
|
-
|
|
302
|
-
For a new account, keep contact and writing controls disabled, enable risk
|
|
303
|
-
cooldown, and bound `visibility` to the authorized active-day warm-up level.
|
|
304
|
-
Never infer that `ready: true`, paused motions, or prepared queue work authorizes
|
|
305
|
-
provider execution.
|
|
306
|
-
|
|
307
|
-
List routing-rule create and update commands accept the same normalized
|
|
308
|
-
condition and action data as the list UI. Location and company_location labels
|
|
309
|
-
must resolve to a known geography or the request returns 422. Inspect the
|
|
310
|
-
ordered rules first, use `audienti lists routing-rules help` for the payload
|
|
311
|
-
contract, and treat `apply` as an asynchronous relaunch: it queues the existing
|
|
312
|
-
routing job and does not mean every list member has finished routing.
|
|
313
|
-
|
|
314
|
-
Motion start and end dates are optional lifecycle controls. A due start can
|
|
315
|
-
activate a configured preparing or paused motion; an end date is the final
|
|
316
|
-
active day and pauses the motion afterward. `--maximum-company-count` caps
|
|
317
|
-
durable company admissions only: research and people discovery for admitted
|
|
318
|
-
companies, prospect intake, and automation continue. Use `none` to clear any
|
|
319
|
-
of these settings.
|
|
320
|
-
|
|
321
|
-
`audienti writer test-run <prsp_id>` starts a report-backed writing session and
|
|
322
|
-
builds the campaign timeline without drafting every message. The printed
|
|
323
|
-
`Report: rprt_...` id is the session handle. Pass `--report <rprt_id>` when
|
|
324
|
-
drafting every message with `--mode report` or one selected row with
|
|
325
|
-
`--mode step`; the server report supplies prior drafted rows as context. Use
|
|
326
|
-
`--timeout-seconds <n>` for longer waits, or `--no-wait` to launch and return
|
|
327
|
-
immediately. Use `writer test-run show` with the report id to fetch the
|
|
328
|
-
completed report later.
|
|
329
|
-
|
|
330
|
-
## Standalone Tools Versus Prospect Tools
|
|
331
|
-
|
|
332
|
-
- For an email lookup with no prospect, use `audienti tools email-find`. `audienti tools get email` imports a prospect into the account; use it only when that is wanted.
|
|
333
|
-
- `tools email-find`, `linkedin-enrich`, `signals-find` and `write` return a run id immediately. Pass `--wait --json` to get results in one call, or poll `tools runs show <trun_id>` and read `tools runs results <trun_id>`. Nothing is sent and no prospects or experiments are created.
|
|
334
|
-
- Pass `--request-key <key>` when retrying a submission; the same key and input return the same run.
|
|
335
|
-
- `audienti network list|export` reads only the requester's own connected account's saved network. A 404 means the requester is not that account's owner. Coverage `unsupported_capture` or `not_yet_observed` is not zero.
|
|
336
|
-
|
|
337
|
-
Create a child workspace with `audienti accounts create --name <name> --parent <name-or-id> [--json]`. The parent can be a visible name, unique name fragment, numeric ID, or prefixed ID. You must administer a root team account. All parent admins receive access to the child; ordinary members do not. Ambiguous names show matching IDs and stop. A duplicate child name under the same parent fails clearly. The response includes the new account ID. Creation keeps your selected account unchanged: use `audienti accounts select <new_acct_id>` or `--account <new_acct_id>` before creating a motion there.
|
|
38
|
+
Use `audienti skills show audienti --json` for the exact version, digest and JSON
|
|
39
|
+
schemas. The local MCP tools expose the same helpers. An HTTP-only agent without
|
|
40
|
+
local execution cannot run these helpers: report that capability gap rather than
|
|
41
|
+
claiming to have executed them. Compatible research tools already available to you
|
|
42
|
+
can supply inputs; using Audienti's paid tools is optional.
|
|
43
|
+
|
|
44
|
+
## Preserve the boundary
|
|
45
|
+
|
|
46
|
+
- Search saved Prospects and Profiles before purchasing fresh data.
|
|
47
|
+
`profiles.enrich` accepts a known LinkedIn URL or authorized saved Profile ID,
|
|
48
|
+
saves the result and returns its current canonical Prospect and profile page.
|
|
49
|
+
Complete eligible cached data is reused; request `refresh` only deliberately.
|
|
50
|
+
- Execute each search, retrieval, contact lookup or optional AI decision explicitly.
|
|
51
|
+
Helpers return inputs for you to use; they never continue a research chain.
|
|
52
|
+
- A candidate rank is not an identity match, qualification, contact permission or
|
|
53
|
+
assurance that a source is trustworthy. `trusted_source` is a legacy V10 ranking
|
|
54
|
+
feature only. Submit attributed facts and citations to the CRM's shared owners.
|
|
55
|
+
- Reuse the returned Prospect/Profile IDs. Do not manufacture merge authority,
|
|
56
|
+
provider verification, or image-match evidence. The CRM owns final identity
|
|
57
|
+
reconciliation. If the installed API lacks the required guarded operation, keep
|
|
58
|
+
the evidence and report the missing capability; do not set foreign keys yourself.
|
|
59
|
+
- Recover an unknown paid outcome using the existing request/operation ID and run
|
|
60
|
+
history before retrying. A new request ID is new work, not a recovery mechanism.
|
|
61
|
+
Do not bypass a zero-credit refusal with another alias or credential.
|
|
62
|
+
- Persist requested relevant evidence even when a candidate fails an experiment's
|
|
63
|
+
fit criteria. Separate source facts from your interpretation and cite both.
|
|
64
|
+
Include this skill's identity/version on supported evidence and assessment writes.
|
|
65
|
+
- Profile research does not authorize a message, follow, contact purchase or change
|
|
66
|
+
to an approval. Use the customer's instructions and current server permissions.
|
|
67
|
+
|
|
68
|
+
Return canonical records read back from the selected workspace, evidence collected,
|
|
69
|
+
profiles matched or still unresolved, operation IDs for interrupted work, and the
|
|
70
|
+
specific remaining evidence/capability gap. Do not claim a complete enrichment or
|
|
71
|
+
expansion when only search results or advisory rankings are available.
|
|
72
|
+
|
|
73
|
+
For field coverage use `profile-completeness` with supplied field states. For observed
|
|
74
|
+
bio/website links and mentions use `profile-evidence`. Both run locally; neither
|
|
75
|
+
chooses a paid call or asserts an identity. See [helper contracts](references/helpers.md).
|