UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

2 lines 8.63 kB
import{z}from"#compiled/zod/index.js";import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{toError}from"#shared/errors.js";import{defineTool}from"#tools/definition.js";import{stampDurableDynamicToolCallbacks}from"#tools/durable-callbacks.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection-key.js";import{resolveApprovalPolicy}from"#approval/definition.js";import{getAuthorizationResults,requestAuthorization}from"#harness/authorization.js";import{createAuthorizationExecution}from"#runtime/connections/scoped-authorization.js";import{isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#connections/errors.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";import{supportsInteractiveAuthorization}from"#shared/connection-types.js";const logger=createLogger(`framework.connection-search-dynamic`),CONNECTION_SEARCH_INPUT_SCHEMA=z.strictObject({connection:z.string().describe(`Optional: limit search to a specific connection name.`).optional(),keywords:z.string().describe(`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`),limit:z.number().describe(`Max results to return. Default 10.`).optional()}),connectionSchema=z.looseObject({}),CONNECTION_SEARCH_RESULT_ITEM_SCHEMA=z.strictObject({connection:z.string(),description:z.string(),error:z.string().optional(),inputSchema:connectionSchema.optional(),needsAuthorization:z.boolean().optional(),outputSchema:connectionSchema.optional(),qualifiedName:z.string().optional(),tool:z.string().optional()}),CONNECTION_SEARCH_OUTPUT_SCHEMA=z.array(CONNECTION_SEARCH_RESULT_ITEM_SCHEMA),ConnectionSearchResultsKey=new ContextKey(`eve.connectionSearchResults`);function qualifiedConnectionToolName(e,t){return`${e}__${t}`}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}async function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n===void 0)return;let r=await resolveConnectionAuthorization(n);if(r!==void 0&&supportsInteractiveAuthorization(r))return{scope:n.connectionName,instanceId:n.instanceId,connection:{url:n.url??``},authorization:r}}async function completePendingAuthorizations(e,t,n){assertPendingConnectionAuthorizationInstances(e);for(let r of t){if(!getAuthorizationResults().some(e=>e.name===r.connectionName))continue;let t=await resolveInteractiveAuth(e,r.connectionName);t!==void 0&&await n.complete(t)}}async function executeConnectionSearch(e){let t=loadContext(),n=t.get(ConnectionRegistryKey);if(n===void 0)return[];let r=e.limit??10,i=tokenize(e.keywords),a=[],o=[],s=e.connection!==void 0&&e.connection!==``?n.getConnections().filter(t=>t.connectionName===e.connection):n.getConnections();if(e.connection&&s.length===0)throw Error(`Connection "${e.connection}" is not registered. Available connections: ${n.getConnectionNames().join(`, `)}.`);let c=createAuthorizationExecution();await completePendingAuthorizations(n,s,c);let l=[];for(let e of s){let t;try{t=await n.getClient(e.connectionName).getToolMetadata()}catch(t){if(isConnectionAuthorizationRequiredError(t)){let r=await resolveInteractiveAuth(n,e.connectionName);if(r!==void 0)try{let e=await c.handleError(t,r);l.push(...e.challenges)}catch(t){let n=toError(t);logger.warn(`connection authorization failed`,{connection:e.connectionName,error:n}),o.push({connection:e.connectionName,description:e.description,error:isConnectionAuthorizationFailedError(n)?n.message:`Failed to start authorization for "${e.connectionName}": ${n.message}`});continue}o.push({connection:e.connectionName,description:e.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(t)){logger.warn(`connection authorization failed`,{connection:e.connectionName,reason:t.reason,retryable:t.retryable,error:t}),o.push({connection:e.connectionName,description:e.description,error:`Authorization failed for ${e.connectionName}: ${t.message}`});continue}let r=toError(t);logger.warn(`failed to load connection tools`,{connection:e.connectionName,error:r}),o.push({connection:e.connectionName,description:e.description,error:`Failed to load tools for "${e.connectionName}": ${r.message}`});continue}for(let n of t){let t=scoreMatch(i,n);t>0&&a.push({item:{connection:e.connectionName,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,qualifiedName:qualifiedConnectionToolName(e.connectionName,n.name),tool:n.name},score:t})}}if(l.length>0)return requestAuthorization(l);let u=o.filter(e=>e.error!==void 0);if(s.length>0&&u.length===s.length)throw Error(u.map(e=>e.error).join(` `));a.sort((e,t)=>t.score-e.score);let d=a.slice(0,r).map(e=>e.item);if(d.length>0){let e=[...d,...o],n=t.get(ConnectionSearchResultsKey)??[],r=new Map(n.map(e=>[e.qualifiedName,e]));for(let e of d)e.qualifiedName&&r.set(e.qualifiedName,e);return t.set(ConnectionSearchResultsKey,[...r.values()]),e}return s.map(e=>o.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}function readDiscoveredToolClosure(e){let t=e.connectionName,n=e.toolName;if(typeof t!=`string`||typeof n!=`string`)throw Error(`Discovered connection tool callback metadata is invalid.`);return{connectionName:t,toolName:n}}async function executeDiscoveredConnectionTool(e,t,n){let{connectionName:r,toolName:i}=readDiscoveredToolClosure(e),a=loadContext().get(ConnectionRegistryKey);if(a===void 0)throw Error(`Connection registry is unavailable while replaying a discovered tool.`);assertPendingConnectionAuthorizationInstances(a);let o=await resolveInteractiveAuth(a,r),s=createAuthorizationExecution();o!==void 0&&await s.complete(o);try{return await a.getClient(r).executeTool(i,t,{abortSignal:n.abortSignal,callId:n.callId})}catch(e){return await s.handleError(e,o)}}function assertPendingConnectionAuthorizationInstances(e){let t=new Map(e.getConnections().map(e=>[e.connectionName,e]));for(let e of getAuthorizationResults())if(e.instanceId!==void 0&&t.get(e.name)?.instanceId!==e.instanceId)throw Error(`Authorization for "${e.name}" cannot complete because its resolved connection changed while sign-in was pending. Start sign-in again.`)}async function requestDiscoveredConnectionToolApproval(e,t){let{connectionName:n}=readDiscoveredToolClosure(e),r=loadContext().get(ConnectionRegistryKey)?.getConnectionApproval(n);return r===void 0?`not-applicable`:await resolveApprovalPolicy(r)(t)}async function authorizeDiscoveredConnectionToolApproval(e,t){let{connectionName:n}=readDiscoveredToolClosure(e),r=loadContext().get(ConnectionRegistryKey)?.getConnectionApproval(n),i=r===void 0||typeof r==`function`?void 0:r.response;return i===void 0?{reason:`Approval response authorization is unavailable.`,status:`rejected`}:await i(t)}async function resolveConnectionSearchDynamicTools(){let e=loadContext().get(ConnectionRegistryKey);if(!e||e.getConnections().length===0)return null;let t=e.getConnections().map(e=>e.connectionName),n=new Set(t),r=(loadContext().get(ConnectionSearchResultsKey)??[]).filter(e=>n.has(e.connection)),i={},a=defineTool({description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. \`linear__list_issues\`) in your next response. Available connections: ${t.join(`, `)}.`,inputSchema:CONNECTION_SEARCH_INPUT_SCHEMA,async execute(e){return executeConnectionSearch(e)},outputSchema:CONNECTION_SEARCH_OUTPUT_SCHEMA});stampDurableDynamicToolCallbacks(a,{execute:{callback:(e,t)=>executeConnectionSearch(t),closure:{}}}),i.connection_search=a;for(let t of r){let n=t.connection,r=t.tool,a=e.getConnectionApproval(n),o={connectionName:n,toolName:r},s=defineTool({description:t.description,inputSchema:t.inputSchema??{type:`object`},approval:a,outputSchema:t.outputSchema,async execute(e,t){return await executeDiscoveredConnectionTool(o,e,t)}});stampDurableDynamicToolCallbacks(s,{execute:{callback:executeDiscoveredConnectionTool,closure:o},...a===void 0?{}:{approvalRequest:{callback:requestDiscoveredConnectionToolApproval,closure:o}},...a===void 0||typeof a==`function`||a.response===void 0?{}:{approvalResponse:{callback:authorizeDiscoveredConnectionToolApproval,closure:o}}}),i[qualifiedConnectionToolName(n,r)]=s}return i}export{resolveConnectionSearchDynamicTools};