eve 0.44.2 → 0.44.4
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/CHANGELOG.md +21 -0
- package/dist/src/channel/adapter.d.ts +2 -2
- package/dist/src/chunks/{use-eve-agent-DpUEwS23.js → use-eve-agent-BJwaMe5k.js} +6 -2
- package/dist/src/chunks/{use-eve-agent-BsLJiIbc.js → use-eve-agent-BtsOXv3s.js} +6 -2
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +2 -0
- package/dist/src/cli/dev/tui/setup-commands.js +1 -1
- package/dist/src/client/eve-agent-store.js +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +2 -2
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.d.ts +1 -0
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
- package/dist/src/execution/web-fetch/request.js +1 -1
- package/dist/src/execution/web-fetch/tool.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/harness/attachment-staging.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/public/channels/chat-sdk/authorization.d.ts +3 -0
- package/dist/src/public/channels/chat-sdk/authorization.js +3 -0
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +2 -0
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
- package/dist/src/public/channels/eve.js +3 -3
- package/dist/src/public/channels/linq/index.d.ts +1 -1
- package/dist/src/public/channels/linq/index.js +1 -1
- package/dist/src/public/channels/linq/linqChannel.d.ts +3 -1
- package/dist/src/public/channels/linq/linqChannel.js +1 -1
- package/dist/src/public/channels/photon/index.d.ts +1 -1
- package/dist/src/public/channels/photon/index.js +1 -1
- package/dist/src/public/channels/photon/photonIMessageChannel.d.ts +3 -1
- package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -1
- package/dist/src/public/channels/slack/api.d.ts +15 -5
- package/dist/src/public/channels/slack/api.js +1 -1
- package/dist/src/public/channels/slack/attachments.d.ts +2 -2
- package/dist/src/public/channels/slack/attachments.js +1 -1
- package/dist/src/public/channels/slack/hitl.d.ts +2 -0
- package/dist/src/public/channels/slack/inbound-content.d.ts +4 -0
- package/dist/src/public/channels/slack/inbound-content.js +4 -0
- package/dist/src/public/channels/slack/inbound.d.ts +8 -1
- package/dist/src/public/channels/slack/inbound.js +1 -1
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/input-response.d.ts +1 -0
- package/dist/src/public/channels/slack/input-response.js +1 -1
- package/dist/src/public/channels/slack/interaction-cards.d.ts +10 -0
- package/dist/src/public/channels/slack/interaction-cards.js +1 -0
- package/dist/src/public/channels/slack/interactions.d.ts +1 -0
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +6 -1
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/sandbox/just-bash-sandbox.d.ts +12 -1
- package/dist/src/setup/flows/registry.d.ts +7 -1
- package/dist/src/setup/flows/registry.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +39 -2
- package/dist/src/setup/scaffold/create/web-template.d.ts +4 -4
- package/dist/src/setup/scaffold/create/web-template.js +1 -1
- package/dist/src/shared/channel-definition.d.ts +5 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/channels/linq.mdx +1 -1
- package/docs/channels/photon.mdx +4 -4
- package/docs/channels/slack.mdx +18 -3
- package/docs/concepts/built-in-tools.md +1 -0
- package/docs/getting-started.mdx +1 -0
- package/docs/guides/dev-tui.md +26 -15
- package/docs/meta.json +1 -1
- package/docs/sandbox.mdx +20 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{defaultDeliverResult}from"#channel/adapter.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.rekey(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),s=e.onInputResponse??defaultOnInputResponse,c=e.events?.[`authorization.required`],l=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],u=e.events?.[`turn.started`]??defaultEvents[`turn.started`],m={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await l(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await u(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":c===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(c)},h=new Set;return defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{audience:`unknown`,channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{audience:e.audience??`unknown`,channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let i=e.state?.approvalResponderUsers;return typeof i==`object`&&i&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...i}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:i}=r,{from:a,resolveSession:o}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:a,resolveSession:o,waitUntil:i},{config:e,onInputResponse:s}):handleEventPost({body:c,from:a,resolveSession:o,waitUntil:i,config:e,uploadPolicy:t,handledEvents:h,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:m})}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=n.initialMessage;if(a&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let o=i;if(a){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,teamId:t.teamId}),n={card:a.card};a.fallbackText!==void 0&&(n.fallbackText=a.fallbackText),o=(await e.post(n)).id}let c=o||crypto.randomUUID();return t.from(slackContinuationToken(r,c)).send(e.message,{auth:e.auth,state:{channelId:r,threadTs:o||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null},title:e.title})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(t){let{config:n}=t,r,i;try{r=parseSlackWebhookBody(t.body,{headers:t.headers}),i=parseSlackEventEnvelope(t.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(r.kind===`url_verification`)return new Response(r.challenge,{status:200,headers:{"content-type":`text/plain`}});if(i===null)return new Response(`ok`);let a=typeof i.api_app_id==`string`?i.api_app_id:void 0,o=slackEventBotUserId(i),dispatch=null,s=null;if(r.kind===`app_mention`||r.kind===`direct_message`){let e=r.kind,i=slackMessageFromWebhookPayload(r);if(i!==null&&!isSelfAuthoredSlackMessage({appId:a,botUserId:o},i)){let dispatchMessageWith=r=>()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}),r=(e===`app_mention`?n.onAppMention:n.onDirectMessage)??n.onMessage;r===void 0?s=dispatchMessageWith(e===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy})}}if(dispatch===null&&n.onMessage!==void 0){let e=parseMessageEvent(i);e!==null&&!isSelfAuthoredSlackMessage({appId:a,botUserId:o},e)&&(o===void 0||!e.text.includes(`<@${o}`))&&(dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:n.onMessage,kind:`channel_message`,message:e,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let c=n.onEvent;if(dispatch===null&&c!==void 0&&(dispatch=()=>dispatchSlackEvent({from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,envelope:i,handler:c})),dispatch??=s,dispatch===null)return new Response(`ok`);let l=i.event_id;if(l){if(t.handledEvents.has(l))return log.warn(`received a duplicate event`,{event_id:l,event_time:i.event_time,retry_num:r.retry?.num??`(null)`,retry_reason:r.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(l,t.handledEvents)}return t.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:r,slack:i}=buildSlackBinding({appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,teamId:e.message.teamId}),a=e.message.author,o={audience:`unknown`,channelId:e.message.channelId,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringUserId:a?.userId??null},c=bindSlackSessionOperations({address:t,defaultAuth:a===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:a.fullName,isBot:a.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:a.userId,userName:a.userName}),from:e.from,resolveSession:e.resolveSession,state:o}),d,isDMOrPrivateChannel=()=>d??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:i.request}),f={...c,isBotMentioned:()=>e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),isDMOrPrivateChannel,isSubscribed:async()=>await c.resolveSession()!==void 0,slack:i,thread:r},p;try{p=await e.handler(f,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(p==null)return;let m=await isDMOrPrivateChannel();o.audience=e.kind===`direct_message`||m?`private`:`public`,await deliverSlackMessage({credentials:e.credentials,kind:e.kind,isPrivateConversation:m,message:e.message,result:p,sessionOperations:c,thread:r,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,r=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,i=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),a={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:n,target:i,title:a})=>receiveOnSlack({auth:n,message:t,target:i,title:a},{from:e.from,credentials:e.credentials,teamId:r,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,teamId:r}),waitUntil(e){i.push(e)}};try{await e.handler(a,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(i)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:r}=e;try{let n=formatSlackThreadContext(e.threadContext===void 0?[]:await loadThreadContextMessages(r,t,e.threadContext)),i=await collectInboundFileParts({mention:t,thread:r,policy:e.uploadPolicy}),a=formatSlackInboundMessage({channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},t),o=buildSlackTurnMessage(n===void 0?a:`${n}\n\n${a}`,i),s=e.result.context??[],c=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,l=s.length===0?{auth:e.result.auth,title:c}:{auth:e.result.auth,context:s,title:c};await e.sessionOperations.send(o,l)}catch(r){logError(log,`${e.kind} delivery failed`,r,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
|
|
1
|
+
import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{defaultDeliverResult}from"#channel/adapter.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.rekey(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),s=e.onInputResponse??defaultOnInputResponse,c=e.events?.[`authorization.required`],l=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],u=e.events?.[`turn.started`]??defaultEvents[`turn.started`],m={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await l(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await u(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":c===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(c)},h=new Set;return defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{audience:`unknown`,channelId:null,threadTs:null,teamId:null,installationTeamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{audience:e.audience??`unknown`,channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let i=e.state?.approvalResponderUsers;return typeof i==`object`&&i&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...i}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:i}=r,{from:a,resolveSession:o}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:a,resolveSession:o,waitUntil:i},{config:e,onInputResponse:s}):handleEventPost({body:c,from:a,resolveSession:o,waitUntil:i,config:e,uploadPolicy:t,handledEvents:h,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:m})}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,o=n.initialMessage;if(o&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=i;if(o){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:a,teamId:t.teamId}),n={card:o.card};o.fallbackText!==void 0&&(n.fallbackText=o.fallbackText),c=(await e.post(n)).id}let u=c||crypto.randomUUID();return t.from(slackContinuationToken(r,u)).send(e.message,{auth:e.auth,state:{channelId:r,installationTeamId:a??null,threadTs:c||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null},title:e.title})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(t){let{config:n}=t,r,i;try{r=parseSlackWebhookBody(t.body,{headers:t.headers}),i=parseSlackEventEnvelope(t.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(r.kind===`url_verification`)return new Response(r.challenge,{status:200,headers:{"content-type":`text/plain`}});if(i===null)return new Response(`ok`);let a=typeof i.api_app_id==`string`?i.api_app_id:void 0,o=slackEventBotUserId(i),s=slackEventInstallationTeamId(i),dispatch=null,c=null;if(r.kind===`app_mention`||r.kind===`direct_message`){let e=r.kind,i=slackMessageFromWebhookPayload(r);if(i!==null&&!isSelfAuthoredSlackMessage({appId:a,botUserId:o},i)){let dispatchMessageWith=r=>()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:s,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}),r=(e===`app_mention`?n.onAppMention:n.onDirectMessage)??n.onMessage;r===void 0?c=dispatchMessageWith(e===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:s,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy})}}if(dispatch===null&&n.onMessage!==void 0){let e=parseMessageEvent(i);e!==null&&!isSelfAuthoredSlackMessage({appId:a,botUserId:o},e)&&(o===void 0||!e.text.includes(`<@${o}`))&&(dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:n.onMessage,installationTeamId:s,kind:`channel_message`,message:e,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let l=n.onEvent;if(dispatch===null&&l!==void 0&&(dispatch=()=>dispatchSlackEvent({from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,envelope:i,handler:l,installationTeamId:s})),dispatch??=c,dispatch===null)return new Response(`ok`);let u=i.event_id;if(u){if(t.handledEvents.has(u))return log.warn(`received a duplicate event`,{event_id:u,event_time:i.event_time,retry_num:r.retry?.num??`(null)`,retry_reason:r.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(u,t.handledEvents)}return t.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:r,slack:i}=buildSlackBinding({appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,installationTeamId:e.installationTeamId,teamId:e.message.teamId}),a=e.message.author,o={audience:`unknown`,channelId:e.message.channelId,installationTeamId:e.installationTeamId??null,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringUserId:a?.userId??null},c=bindSlackSessionOperations({address:t,defaultAuth:a===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:a.fullName,isBot:a.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:a.userId,userName:a.userName}),from:e.from,resolveSession:e.resolveSession,state:o}),d,isDMOrPrivateChannel=()=>d??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:i.request}),f={...c,isBotMentioned:()=>e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),isDMOrPrivateChannel,isSubscribed:async()=>await c.resolveSession()!==void 0,slack:i,thread:r},p;try{p=await e.handler(f,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(p==null)return;let m=await isDMOrPrivateChannel();o.audience=e.kind===`direct_message`||m?`private`:`public`,await deliverSlackMessage({credentials:e.credentials,kind:e.kind,isPrivateConversation:m,message:e.message,result:p,sessionOperations:c,thread:r,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,r=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,i=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),a={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:n,target:i,title:a})=>receiveOnSlack({auth:n,message:t,target:i,title:a},{from:e.from,credentials:e.credentials,installationTeamId:e.installationTeamId,teamId:r,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:r}),waitUntil(e){i.push(e)}};try{await e.handler(a,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(i)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:r}=e;try{let n=formatSlackThreadContext(e.threadContext===void 0?[]:await loadThreadContextMessages(r,t,e.threadContext)),i=await collectInboundFileParts({mention:t,thread:r,policy:e.uploadPolicy}),a=formatSlackInboundMessage({channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},t),o=buildSlackTurnMessage(n===void 0?a:`${n}\n\n${a}`,i),s=e.result.context??[],c=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,l=s.length===0?{auth:e.result.auth,title:c}:{auth:e.result.auth,context:s,title:c};await e.sessionOperations.send(o,l)}catch(r){logError(log,`${e.kind} delivery failed`,r,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IFileSystem } from "just-bash";
|
|
1
|
+
import type { CustomCommand, IFileSystem } from "just-bash";
|
|
2
2
|
/**
|
|
3
3
|
* Context passed to a custom just-bash filesystem factory for each live handle.
|
|
4
4
|
*/
|
|
@@ -26,6 +26,17 @@ export interface JustBashSandboxCreateOptions {
|
|
|
26
26
|
* actionable install error instead. Defaults to `true`.
|
|
27
27
|
*/
|
|
28
28
|
readonly autoInstall?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Registers trusted host application code as commands in each live
|
|
31
|
+
* just-bash interpreter. Custom commands can participate in normal shell
|
|
32
|
+
* composition, including pipelines and redirections.
|
|
33
|
+
*
|
|
34
|
+
* Custom commands are not available during template prewarming. They run in
|
|
35
|
+
* eve's host process, outside the virtual filesystem security boundary, and
|
|
36
|
+
* are responsible for validating their own inputs and cleaning up host
|
|
37
|
+
* resources.
|
|
38
|
+
*/
|
|
39
|
+
readonly customCommands?: ReadonlyArray<CustomCommand>;
|
|
29
40
|
/**
|
|
30
41
|
* Composes the filesystem used by each live sandbox handle. The factory is
|
|
31
42
|
* called when a handle opens with eve's durable default filesystem; return a
|
|
@@ -27,10 +27,16 @@ export declare class RegistryFlowFailedError extends Error {
|
|
|
27
27
|
kind: "done";
|
|
28
28
|
}>);
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Runs the categorized interactive registry catalog used by the dev TUI's
|
|
32
|
+
* `/add`. `initialAddress` — `/add channel/slack` — skips the category and
|
|
33
|
+
* search screens and opens that item's confirmation directly; backing out of it
|
|
34
|
+
* lands on the same category hub the browser starts from.
|
|
35
|
+
*/
|
|
31
36
|
export declare function runRegistryFlow(input: {
|
|
32
37
|
appRoot: string;
|
|
33
38
|
prompter: Prompter;
|
|
34
39
|
signal?: AbortSignal;
|
|
40
|
+
initialAddress?: string;
|
|
35
41
|
deps?: Partial<RegistryFlowDeps>;
|
|
36
42
|
}): Promise<RegistryFlowResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRegistrySession}from"./registry-session.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{detectDeployment}from"#setup/project-resolution.js";import{withSpinner}from"#setup/with-spinner.js";const ADDRESS_PREFIX=`address:`,BACK=`action:back`,DONE=`action:done`,ALL=`category:all`,REGISTRY_CATEGORIES=[{value:`category:channel`,prefix:`channel/`,label:`Channels`,hint:`Where people talk to your agent — Web, Slack, Discord, Teams`,browseLabel:`Browse channels`},{value:`category:connection`,prefix:`connection/`,label:`MCP connections`,hint:`Connect services like Linear, Notion, GitHub, and Vercel`,browseLabel:`Browse MCP connections`},{value:`category:extension`,prefix:`extension/`,label:`Extensions`,hint:`Add browser automation, memory, and developer tools`,browseLabel:`Browse extensions`},{value:`category:instrumentation`,prefix:`instrumentation/`,label:`Observability`,hint:`Trace, evaluate, and monitor your agent`,browseLabel:`Browse observability integrations`}];var RegistryFlowFailedError=class extends Error{completed;constructor(e,t){super(e instanceof Error?e.message:String(e),{cause:e}),this.name=`RegistryFlowFailedError`,this.completed=t}};function itemLabel(e){return e.title===void 0?(e.name.split(`/`).at(-1)??e.name).split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `):e.title}function itemRows(e){return e.map((e,t)=>({value:`item:${t}`,label:itemLabel(e),hint:e.description??e.type}))}function categoryRows(){return[...REGISTRY_CATEGORIES.map(e=>({value:e.value,label:e.label,hint:e.hint})),{value:ALL,label:`Browse all`,hint:`Search every integration or enter an item address`},{value:DONE,label:`Return to chat`,trailingAction:!0}]}function categoryFor(e){return REGISTRY_CATEGORIES.find(t=>t.value===e)}function itemsForCategory(e,t){if(t===ALL)return e;let n=categoryFor(t);return n===void 0?e:e.filter(e=>e.address.startsWith(n.prefix)||e.name.startsWith(n.prefix))}function stringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function itemSource(e){if(e.startsWith(`@`))return e.split(`/`)[0]??e;if(/^https?:\/\//u.test(e))try{return new URL(e).host}catch{return e}return`Vercel`}function manifestRecord(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:{}}function summarizeDetails(e,t=3){let n=e.slice(0,t),r=e.length-n.length;return r>0?`${n.join(`, `)} … (+${r} more)`:n.join(`, `)}function itemDetails(e,t){let n=[{label:`Source`,value:e.source}],r=[...stringArray(t.dependencies),...stringArray(t.devDependencies),...stringArray(t.registryDependencies)];r.length>0&&n.push({label:`Packages`,value:summarizeDetails(r)});let i=t.envVars;if(typeof i==`object`&&i&&!Array.isArray(i)){let e=Object.keys(i);e.length>0&&n.push({label:`Environment`,value:summarizeDetails(e)})}let a=(Array.isArray(t.files)?t.files:[]).flatMap(e=>{if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.target;return typeof t==`string`?[t]:[]});return a.length>0&&n.push({label:`Files`,value:summarizeDetails(a)}),n}async function inspectItem(e,t,n,r,a,o){let s=a??manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r.address)));for(;;){let i={message:typeof s.title==`string`?s.title:r.name,metadata:itemDetails(r,s),options:[{value:`add`,label:`Add to project`},{value:`back`,label:`Back`}]},a=typeof s.description==`string`?s.description:r.description;if(a!==void 0&&(i.description=a),await e.select(i)===`back`)return{kind:`back`};let c=e.log.spinner?.(`Installing ${itemLabel(r)} and dependencies…`);try{c?.stop();let install=()=>t.installRegistryItem(n,r.address,{silent:!0,prompter:e,signal:o});return{kind:`added`,...await(e.withExclusiveTerminal?.(install)??install())}}finally{c?.stop()}}}async function resolveAddressItem(e,t,n,r){let a=manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r))),o={address:r,name:typeof a.name==`string`?a.name:r,source:itemSource(r)};return typeof a.type==`string`&&(o.type=a.type),typeof a.description==`string`&&(o.description=a.description),{item:o,manifest:a}}async function runRegistryFlow(c){let l;(c.deps?.browseRegistryCatalog===void 0||c.deps.getRegistryItemManifest===void 0||c.deps.installRegistryItem===void 0)&&(l=await import(`#cli/commands/registry.js`));let u={browseRegistryCatalog:c.deps?.browseRegistryCatalog??l.browseRegistryCatalog,detectDeployment:c.deps?.detectDeployment??detectDeployment,getRegistryItemManifest:c.deps?.getRegistryItemManifest??l.getRegistryItemManifest,installRegistryItem:c.deps?.installRegistryItem??l.installRegistryItem,runDeployFlow:c.deps?.runDeployFlow??runDeployFlow},d=[],f=createRegistrySession(u);try{for(;;){c.signal?.throwIfAborted();let e=await withSpinner(c.prompter,`Loading registry…`,()=>u.browseRegistryCatalog(c.appRoot));d=[...d,...e.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))];let t=await c.prompter.select({message:`Add an integration`,options:categoryRows(),hintLayout:`inline`,notices:d});if(d=[],t===DONE)return f.result();let n=itemsForCategory(e.items,t),r=itemRows(n);r.push({value:BACK,label:`Back`,trailingAction:!0});let l=await c.prompter.select({message:categoryFor(t)?.browseLabel??`Browse integrations`,options:r,search:!0,placeholder:`Search integrations or enter an item address`,searchAction:{label:e=>`Add “${e}”`,value:e=>`${ADDRESS_PREFIX}${e.trim()}`},hintLayout:`inline`});if(l===BACK)continue;let p=l.startsWith(ADDRESS_PREFIX)?await resolveAddressItem(c.prompter,u,c.appRoot,l.slice(8)):{item:n[Number(l.slice(5))]};if(p.item===void 0)throw Error(`The selected registry item is no longer available.`);let
|
|
1
|
+
import{createRegistrySession}from"./registry-session.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{detectDeployment}from"#setup/project-resolution.js";import{withSpinner}from"#setup/with-spinner.js";const ADDRESS_PREFIX=`address:`,BACK=`action:back`,DONE=`action:done`,ALL=`category:all`,REGISTRY_CATEGORIES=[{value:`category:channel`,prefix:`channel/`,label:`Channels`,hint:`Where people talk to your agent — Web, Slack, Discord, Teams`,browseLabel:`Browse channels`},{value:`category:connection`,prefix:`connection/`,label:`MCP connections`,hint:`Connect services like Linear, Notion, GitHub, and Vercel`,browseLabel:`Browse MCP connections`},{value:`category:extension`,prefix:`extension/`,label:`Extensions`,hint:`Add browser automation, memory, and developer tools`,browseLabel:`Browse extensions`},{value:`category:instrumentation`,prefix:`instrumentation/`,label:`Observability`,hint:`Trace, evaluate, and monitor your agent`,browseLabel:`Browse observability integrations`}];var RegistryFlowFailedError=class extends Error{completed;constructor(e,t){super(e instanceof Error?e.message:String(e),{cause:e}),this.name=`RegistryFlowFailedError`,this.completed=t}};function itemLabel(e){return e.title===void 0?(e.name.split(`/`).at(-1)??e.name).split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `):e.title}function itemRows(e){return e.map((e,t)=>({value:`item:${t}`,label:itemLabel(e),hint:e.description??e.type}))}function categoryRows(){return[...REGISTRY_CATEGORIES.map(e=>({value:e.value,label:e.label,hint:e.hint})),{value:ALL,label:`Browse all`,hint:`Search every integration or enter an item address`},{value:DONE,label:`Return to chat`,trailingAction:!0}]}function categoryFor(e){return REGISTRY_CATEGORIES.find(t=>t.value===e)}function itemsForCategory(e,t){if(t===ALL)return e;let n=categoryFor(t);return n===void 0?e:e.filter(e=>e.address.startsWith(n.prefix)||e.name.startsWith(n.prefix))}function stringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function itemSource(e){if(e.startsWith(`@`))return e.split(`/`)[0]??e;if(/^https?:\/\//u.test(e))try{return new URL(e).host}catch{return e}return`Vercel`}function manifestRecord(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:{}}function summarizeDetails(e,t=3){let n=e.slice(0,t),r=e.length-n.length;return r>0?`${n.join(`, `)} … (+${r} more)`:n.join(`, `)}function itemDetails(e,t){let n=[{label:`Source`,value:e.source}],r=[...stringArray(t.dependencies),...stringArray(t.devDependencies),...stringArray(t.registryDependencies)];r.length>0&&n.push({label:`Packages`,value:summarizeDetails(r)});let i=t.envVars;if(typeof i==`object`&&i&&!Array.isArray(i)){let e=Object.keys(i);e.length>0&&n.push({label:`Environment`,value:summarizeDetails(e)})}let a=(Array.isArray(t.files)?t.files:[]).flatMap(e=>{if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.target;return typeof t==`string`?[t]:[]});return a.length>0&&n.push({label:`Files`,value:summarizeDetails(a)}),n}async function inspectItem(e,t,n,r,a,o){let s=a??manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r.address)));for(;;){let i={message:typeof s.title==`string`?s.title:r.name,metadata:itemDetails(r,s),options:[{value:`add`,label:`Add to project`},{value:`back`,label:`Back`}]},a=typeof s.description==`string`?s.description:r.description;if(a!==void 0&&(i.description=a),await e.select(i)===`back`)return{kind:`back`};let c=e.log.spinner?.(`Installing ${itemLabel(r)} and dependencies…`);try{c?.stop();let install=()=>t.installRegistryItem(n,r.address,{silent:!0,prompter:e,signal:o});return{kind:`added`,...await(e.withExclusiveTerminal?.(install)??install())}}finally{c?.stop()}}}async function resolveAddressItem(e,t,n,r){let a=manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r))),o={address:r,name:typeof a.name==`string`?a.name:r,source:itemSource(r)};return typeof a.type==`string`&&(o.type=a.type),typeof a.description==`string`&&(o.description=a.description),{item:o,manifest:a}}async function offerItem(e,t,n,r,i){let a=await inspectItem(e.prompter,t,e.appRoot,r,i,e.signal);if(a.kind!==`added`)return;n.add(r.address,itemLabel(r),a.output,a.setup);let o=await n.continueAfterInstall({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal});return o===`add-more`?void 0:o}async function runRegistryFlow(c){let l;(c.deps?.browseRegistryCatalog===void 0||c.deps.getRegistryItemManifest===void 0||c.deps.installRegistryItem===void 0)&&(l=await import(`#cli/commands/registry.js`));let u={browseRegistryCatalog:c.deps?.browseRegistryCatalog??l.browseRegistryCatalog,detectDeployment:c.deps?.detectDeployment??detectDeployment,getRegistryItemManifest:c.deps?.getRegistryItemManifest??l.getRegistryItemManifest,installRegistryItem:c.deps?.installRegistryItem??l.installRegistryItem,runDeployFlow:c.deps?.runDeployFlow??runDeployFlow},d=[],f=createRegistrySession(u),p=c.initialAddress?.trim(),m=p===``?void 0:p;try{for(;;){if(c.signal?.throwIfAborted(),m!==void 0){let e=m;m=void 0;let t=await resolveAddressItem(c.prompter,u,c.appRoot,e),n=await offerItem(c,u,f,t.item,t.manifest);if(n!==void 0)return n;continue}let e=await withSpinner(c.prompter,`Loading registry…`,()=>u.browseRegistryCatalog(c.appRoot));d=[...d,...e.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))];let t=await c.prompter.select({message:`Add an integration`,options:categoryRows(),hintLayout:`inline`,notices:d});if(d=[],t===DONE)return f.result();let n=itemsForCategory(e.items,t),r=itemRows(n);r.push({value:BACK,label:`Back`,trailingAction:!0});let l=await c.prompter.select({message:categoryFor(t)?.browseLabel??`Browse integrations`,options:r,search:!0,placeholder:`Search integrations or enter an item address`,searchAction:{label:e=>`Add “${e}”`,value:e=>`${ADDRESS_PREFIX}${e.trim()}`},hintLayout:`inline`});if(l===BACK)continue;let p=l.startsWith(ADDRESS_PREFIX)?await resolveAddressItem(c.prompter,u,c.appRoot,l.slice(8)):{item:n[Number(l.slice(5))]};if(p.item===void 0)throw Error(`The selected registry item is no longer available.`);let h=await offerItem(c,u,f,p.item,`manifest`in p?p.manifest:void 0);if(h!==void 0)return h}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};let t=f.result();throw t.addedItems.length>0?new RegistryFlowFailedError(e,t):e}}export{RegistryFlowFailedError,runRegistryFlow};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.3`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.44.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.3`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.44.4`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e,t){return{"agent/agent.ts":renderAgentTemplate(e,t),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,n){let r=parseChatGptModelSelection(e);return r===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(r)}),\n${reasoningTemplateLine(n)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,n){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(n.model)!==void 0?renderAgentTemplate(n.model,n.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,n.appName).replaceAll(`__EVE_INIT_MODEL__`,n.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(n.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(n.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(n.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,n.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,n.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,n.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,n.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,n.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,n.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -13,7 +13,9 @@ __EVE_INIT_REASONING__});
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "eve build",
|
|
16
|
+
"deploy": "eve deploy",
|
|
16
17
|
"dev": "eve dev",
|
|
18
|
+
"eval": "eve eval",
|
|
17
19
|
"start": "eve start",
|
|
18
20
|
"typecheck": "tsc"
|
|
19
21
|
},
|
|
@@ -35,7 +37,42 @@ __EVE_INIT_REASONING__});
|
|
|
35
37
|
`,AGENT_INSTRUCTIONS_TEMPLATE=`# Identity
|
|
36
38
|
|
|
37
39
|
You are a helpful assistant.
|
|
38
|
-
`,SHARED_TEMPLATE_FILES={"
|
|
40
|
+
`,SHARED_TEMPLATE_FILES={"README.md":`# __EVE_INIT_APP_NAME__
|
|
41
|
+
|
|
42
|
+
This is an [eve](https://eve.dev) agent bootstrapped with [\`eve init\`](https://eve.dev/docs/reference/cli#eve-init).
|
|
43
|
+
|
|
44
|
+
## Getting started
|
|
45
|
+
|
|
46
|
+
First, run the development server:
|
|
47
|
+
|
|
48
|
+
\`\`\`bash
|
|
49
|
+
eve dev
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
The development TUI opens an interactive session where you can send messages to your agent.
|
|
53
|
+
|
|
54
|
+
Start by editing \`agent/instructions.md\` to define the agent's identity, purpose, tone, and response guidelines. Configure its model and runtime behavior in \`agent/agent.ts\`.
|
|
55
|
+
|
|
56
|
+
Add capabilities under \`agent/\`, including tools, connections, channels, skills, subagents, and schedules. eve reloads your changes as you work.
|
|
57
|
+
|
|
58
|
+
## Learn more
|
|
59
|
+
|
|
60
|
+
To learn more about eve, explore these resources:
|
|
61
|
+
|
|
62
|
+
- [eve documentation](https://eve.dev/docs) — learn about eve's features and authoring APIs.
|
|
63
|
+
- [Build an Agent tutorial](https://eve.dev/docs/tutorial/first-agent) — build and deploy an agent step by step.
|
|
64
|
+
- [eve on GitHub](https://github.com/vercel/eve) — view the source and contribute.
|
|
65
|
+
|
|
66
|
+
## Deploy on Vercel
|
|
67
|
+
|
|
68
|
+
Deploy your agent to [Vercel](https://vercel.com) from the project root:
|
|
69
|
+
|
|
70
|
+
\`\`\`bash
|
|
71
|
+
eve deploy
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
\`eve deploy\` links a Vercel project if needed and deploys the agent to production. See the [eve deployment documentation](https://eve.dev/docs/guides/deployment/vercel) for authentication, environment variables, and deployment options.
|
|
75
|
+
`,"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE,"tsconfig.json":`{
|
|
39
76
|
"compilerOptions": {
|
|
40
77
|
"target": "ES2022",
|
|
41
78
|
"module": "esnext",
|
|
@@ -63,9 +63,9 @@ export declare const WEB_APP_TEMPLATE_PACKAGE_JSON: {
|
|
|
63
63
|
};
|
|
64
64
|
readonly dependencies: {
|
|
65
65
|
readonly "@radix-ui/react-use-controllable-state": "1.2.2";
|
|
66
|
-
readonly "@shikijs/core": "
|
|
67
|
-
readonly "@shikijs/engine-javascript": "
|
|
68
|
-
readonly "@shikijs/engine-oniguruma": "
|
|
66
|
+
readonly "@shikijs/core": "3.23.0";
|
|
67
|
+
readonly "@shikijs/engine-javascript": "3.23.0";
|
|
68
|
+
readonly "@shikijs/engine-oniguruma": "3.23.0";
|
|
69
69
|
readonly "@streamdown/cjk": "1.0.3";
|
|
70
70
|
readonly "@streamdown/code": "1.1.1";
|
|
71
71
|
readonly "@streamdown/math": "1.0.2";
|
|
@@ -81,7 +81,7 @@ export declare const WEB_APP_TEMPLATE_PACKAGE_JSON: {
|
|
|
81
81
|
readonly "radix-ui": "1.4.3";
|
|
82
82
|
readonly react: "19.2.6";
|
|
83
83
|
readonly "react-dom": "19.2.6";
|
|
84
|
-
readonly shiki: "
|
|
84
|
+
readonly shiki: "3.23.0";
|
|
85
85
|
readonly streamdown: "2.5.0";
|
|
86
86
|
readonly "tailwind-merge": "3.6.0";
|
|
87
87
|
readonly tailwindcss: "4.3.0";
|
|
@@ -5830,4 +5830,4 @@ export const auth = betterAuth({
|
|
|
5830
5830
|
},
|
|
5831
5831
|
},
|
|
5832
5832
|
});
|
|
5833
|
-
`},WEB_APP_TEMPLATE_PACKAGE_JSON={scripts:{build:`next build`,"build:eve":`eve build`,dev:`next dev`,"dev:eve":`eve dev`,start:`next start`,"start:eve":`eve start`,typecheck:`tsc --noEmit -p tsconfig.json`},dependencies:{"@radix-ui/react-use-controllable-state":`1.2.2`,"@shikijs/core":`
|
|
5833
|
+
`},WEB_APP_TEMPLATE_PACKAGE_JSON={scripts:{build:`next build`,"build:eve":`eve build`,dev:`next dev`,"dev:eve":`eve dev`,start:`next start`,"start:eve":`eve start`,typecheck:`tsc --noEmit -p tsconfig.json`},dependencies:{"@radix-ui/react-use-controllable-state":`1.2.2`,"@shikijs/core":`3.23.0`,"@shikijs/engine-javascript":`3.23.0`,"@shikijs/engine-oniguruma":`3.23.0`,"@streamdown/cjk":`1.0.3`,"@streamdown/code":`1.1.1`,"@streamdown/math":`1.0.2`,"@streamdown/mermaid":`1.0.2`,"@tailwindcss/postcss":`4.3.0`,"class-variance-authority":`0.7.1`,clsx:`2.1.1`,cmdk:`1.1.1`,"lucide-react":`1.16.0`,motion:`12.40.0`,nanoid:`5.1.11`,next:`16.3.0-preview.6`,"radix-ui":`1.4.3`,react:`19.2.6`,"react-dom":`19.2.6`,shiki:`3.23.0`,streamdown:`2.5.0`,"tailwind-merge":`3.6.0`,tailwindcss:`4.3.0`,"use-stick-to-bottom":`1.1.4`,zod:`4.4.3`},devDependencies:{"@types/node":`26`,"@types/react":`19.2.15`,"@types/react-dom":`19.2.3`,typescript:`6.0.3`}};export{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON};
|
|
@@ -20,7 +20,11 @@ export interface FetchFileResult {
|
|
|
20
20
|
readonly mediaType?: string;
|
|
21
21
|
readonly filename?: string;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
/** Runtime context supplied while resolving a channel-owned file URL. */
|
|
24
|
+
export interface FetchFileContext {
|
|
25
|
+
readonly state: Readonly<Record<string, unknown>>;
|
|
26
|
+
}
|
|
27
|
+
export type FetchFileFunction = (url: string, context?: FetchFileContext) => Promise<Buffer | FetchFileResult | null>;
|
|
24
28
|
/**
|
|
25
29
|
* Input passed to a channel's `receive` callback when another channel or
|
|
26
30
|
* schedule proactively routes a message to it.
|
package/dist/src/svelte/index.js
CHANGED
package/dist/src/vue/index.js
CHANGED
package/docs/channels/linq.mdx
CHANGED
|
@@ -16,7 +16,7 @@ export default linqChannel({
|
|
|
16
16
|
});
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The default webhook route is `/eve/v1/linq`. The channel verifies forwarded webhooks with same-project Vercel OIDC by default, marks accepted messages as read, and continues the same eve session for every message in a Linq conversation. A new accepted message cooperatively cancels an active turn and steers its replacement turn.
|
|
19
|
+
The default webhook route is `/eve/v1/linq`. The channel verifies forwarded webhooks with same-project Vercel OIDC by default, derives user auth from each message author, marks accepted messages as read, and continues the same eve session for every message in a Linq conversation. A new accepted message cooperatively cancels an active turn and steers its replacement turn.
|
|
20
20
|
|
|
21
21
|
Set `turnPolicy: "queue"` when every response should finish before Linq starts the next message.
|
|
22
22
|
|
package/docs/channels/photon.mdx
CHANGED
|
@@ -22,10 +22,10 @@ export default photonIMessageChannel({
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
The default webhook route is `/eve/v1/photon`. The channel verifies forwarded
|
|
25
|
-
webhooks with same-project Vercel OIDC by default,
|
|
26
|
-
read, and continues the same eve
|
|
27
|
-
conversation. A new accepted message
|
|
28
|
-
steers its replacement turn.
|
|
25
|
+
webhooks with same-project Vercel OIDC by default, derives user auth from each
|
|
26
|
+
message author, marks accepted messages as read, and continues the same eve
|
|
27
|
+
session for every message in an iMessage conversation. A new accepted message
|
|
28
|
+
cooperatively cancels an active turn and steers its replacement turn.
|
|
29
29
|
|
|
30
30
|
Set `turnPolicy: "queue"` when every response should finish before Photon starts the next message.
|
|
31
31
|
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -33,6 +33,20 @@ export default slackChannel({
|
|
|
33
33
|
|
|
34
34
|
`connectSlackCredentials` returns `{ botToken, webhookVerifier }`, keeping token rotation, multi-workspace tenancy, and request verification inside Connect rather than your code.
|
|
35
35
|
|
|
36
|
+
Function-form bot tokens receive `{ teamId }` when Slack supplies the app installation workspace. Use it to select an installation in a self-managed multi-workspace app. The id can differ from the actor or content workspace for Slack Connect events:
|
|
37
|
+
|
|
38
|
+
```ts title="agent/channels/slack.ts"
|
|
39
|
+
import { slackChannel } from "eve/channels/slack";
|
|
40
|
+
|
|
41
|
+
export default slackChannel({
|
|
42
|
+
credentials: {
|
|
43
|
+
botToken: ({ teamId }) => loadInstallationToken(teamId),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Literal tokens and zero-argument token functions remain supported. A multi-workspace provider should reject a missing `teamId` instead of selecting a default installation.
|
|
49
|
+
|
|
36
50
|
### Manage Slack credentials yourself
|
|
37
51
|
|
|
38
52
|
Use this fallback when the agent does not run on Vercel or Vercel Connect is unavailable. In the setup command, **Use portable credentials** is the environment-variable option; eve adds `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` to `.env.example`.
|
|
@@ -357,6 +371,7 @@ import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
|
357
371
|
const { botToken } = connectSlackCredentials("slack/my-agent");
|
|
358
372
|
const response = await callSlackApi({
|
|
359
373
|
botToken,
|
|
374
|
+
context: { teamId: "T0123456789" },
|
|
360
375
|
operation: "reactions.get",
|
|
361
376
|
body: { channel: "C0123456789", timestamp: "1712345678.000100", full: true },
|
|
362
377
|
});
|
|
@@ -364,7 +379,7 @@ if (!response.ok) throw new Error(String(response.error));
|
|
|
364
379
|
```
|
|
365
380
|
|
|
366
381
|
`callSlackApi` resolves function-form tokens (secret managers, Connect
|
|
367
|
-
rotation) at call time and form-encodes the body — the only safe default,
|
|
382
|
+
rotation) with the supplied `context` at call time and form-encodes the body — the only safe default,
|
|
368
383
|
since Slack's JSON support is partial (`conversations.replies` rejects
|
|
369
384
|
JSON). `resolveSlackBotToken` materializes a `SlackBotToken` to a string
|
|
370
385
|
when you need the bearer token itself.
|
|
@@ -414,7 +429,7 @@ events: {
|
|
|
414
429
|
|
|
415
430
|
### Proactive sessions
|
|
416
431
|
|
|
417
|
-
Start a session without an inbound message through `ctx.to(slack, target).send(message, { auth })` from another channel, or `to(slack, target).send(message, { auth })` from a schedule `run` handler. The proactive target shape is `{ channelId, threadTs?, initialMessage? }`. This sends turn input to the agent; it is not a direct Slack post.
|
|
432
|
+
Start a session without an inbound message through `ctx.to(slack, target).send(message, { auth })` from another channel, or `to(slack, target).send(message, { auth })` from a schedule `run` handler. The proactive target shape is `{ channelId, installationTeamId?, threadTs?, initialMessage? }`. Set `installationTeamId` to the app installation workspace when a function-form bot token must select an installation. This sends turn input to the agent; it is not a direct Slack post.
|
|
418
433
|
|
|
419
434
|
To post without invoking the model, use `ctx.thread.post(...)` in an inbound Slack handler or `channel.thread.post(...)` in an `events` handler. Use `callSlackApi(...)` where no Slack channel context exists. For application-managed retries and deduplication, see [Durable cross-channel notifications](../patterns/durable-cross-channel-notifications).
|
|
420
435
|
|
|
@@ -422,7 +437,7 @@ To post without invoking the model, use `ctx.thread.post(...)` in an inbound Sla
|
|
|
422
437
|
|
|
423
438
|
Inbound files behind authenticated Slack URLs are staged with `fetchFile`. See [File uploads](./custom#file-uploads) for the `fetchFile` contract.
|
|
424
439
|
|
|
425
|
-
Private file downloads require the Slack bot token's `files:read` scope. Add the scope and reinstall the Slack app before sending attachments.
|
|
440
|
+
Private file downloads resolve credentials from the session's app installation workspace and require the Slack bot token's `files:read` scope. Add the scope and reinstall the Slack app before sending attachments.
|
|
426
441
|
|
|
427
442
|
Staged files live in the session sandbox under `/workspace/attachments`. Session history keeps a reference to that path, not a second copy of the Slack file. If the backend can no longer reattach the original sandbox, eve cannot restore the attachment bytes from history; later turns receive a missing-file result. Store files outside the sandbox when the agent must retain them independently of sandbox availability.
|
|
428
443
|
|
|
@@ -32,6 +32,7 @@ Notes:
|
|
|
32
32
|
- **`load_skill`** only pulls instructions into context. It adds no new execution surface, because behavior still comes from the tools the agent already has.
|
|
33
33
|
- **`connection_search`** surfaces a connection's tools by their qualified name (e.g. `linear__list_issues`), which the model can then call directly. It's registered only when the agent has connections.
|
|
34
34
|
- **`web_search`** has no local executor; the provider runs it. AI Gateway models use Exa by default. To use Parallel instead, export `webSearch({ provider: "parallel" })` from `agent/tools/web_search.ts`. Direct provider models continue to use their native search implementation. To supply your own implementation, override it with `defineTool()`.
|
|
35
|
+
- **`web_fetch`** follows up to ten redirects, rechecking every destination for SSRF safety. Non-success HTTP responses return a plain-text failure result with the response body when available instead of failing the tool call.
|
|
35
36
|
|
|
36
37
|
Review these default tools before production use. Disable, wrap, restrict, or require approval for any tool that can access the filesystem, network, shell, or sensitive data.
|
|
37
38
|
|
package/docs/getting-started.mdx
CHANGED
package/docs/guides/dev-tui.md
CHANGED
|
@@ -13,24 +13,35 @@ The transcript remains in your terminal scrollback after you exit. Run `/help` i
|
|
|
13
13
|
|
|
14
14
|
## Commands
|
|
15
15
|
|
|
16
|
-
| Command | Description
|
|
17
|
-
| ------------- |
|
|
18
|
-
| `/model` | Configure the model and its provider. Pass a model ID to set it directly: `/model provider/model-id`.
|
|
19
|
-
| `/add` | Browse and install channels, MCP connections, extensions, and observability integrations.
|
|
20
|
-
| `/deploy` | Deploy the agent to Vercel production. Links the directory first if needed.
|
|
21
|
-
| `/vc:install` | Install the Vercel CLI.
|
|
22
|
-
| `/vc:login` | Log in to Vercel or restore access to a remote deployment.
|
|
23
|
-
| `/loglevel` | Choose which server and agent logs appear in the transcript.
|
|
24
|
-
| `/traces` | Open the local trace viewer. Pass a trace ID prefix to open a specific trace.
|
|
25
|
-
| `/reset` | Start a fresh session.
|
|
26
|
-
| `/cancel` | Cancel the current turn without discarding settled context.
|
|
27
|
-
| `/clear` | Clear the session's model-message history. `/new` is an alias.
|
|
28
|
-
| `/compact` | Compact the current session's context.
|
|
29
|
-
| `/exit` | Quit the UI.
|
|
30
|
-
| `/help` | List available commands.
|
|
16
|
+
| Command | Description |
|
|
17
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `/model` | Configure the model and its provider. Pass a model ID to set it directly: `/model provider/model-id`. |
|
|
19
|
+
| `/add` | Browse and install channels, MCP connections, extensions, and observability integrations. Pass an item address to open it directly: `/add channel/slack`. |
|
|
20
|
+
| `/deploy` | Deploy the agent to Vercel production. Links the directory first if needed. |
|
|
21
|
+
| `/vc:install` | Install the Vercel CLI. |
|
|
22
|
+
| `/vc:login` | Log in to Vercel or restore access to a remote deployment. |
|
|
23
|
+
| `/loglevel` | Choose which server and agent logs appear in the transcript. |
|
|
24
|
+
| `/traces` | Open the local trace viewer. Pass a trace ID prefix to open a specific trace. |
|
|
25
|
+
| `/reset` | Start a fresh session. |
|
|
26
|
+
| `/cancel` | Cancel the current turn without discarding settled context. |
|
|
27
|
+
| `/clear` | Clear the session's model-message history. `/new` is an alias. |
|
|
28
|
+
| `/compact` | Compact the current session's context. |
|
|
29
|
+
| `/exit` | Quit the UI. |
|
|
30
|
+
| `/help` | List available commands. |
|
|
31
31
|
|
|
32
32
|
`/model`, `/add`, `/deploy`, and `/traces` are available when `eve dev` runs locally. They are unavailable when the UI connects to a server with `--url`.
|
|
33
33
|
|
|
34
|
+
## Add an integration
|
|
35
|
+
|
|
36
|
+
Bare `/add` opens the categorized registry browser. Pass an item address — `<category>/<name>`, where category is `channel`, `connection`, `extension`, or `instrumentation` — to skip the category and search screens and open that item directly:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
/add channel/slack
|
|
40
|
+
/add extension/agent-browser
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Either way the UI shows the item's details and asks to confirm before installing, then runs the same installation and setup prompts. Choosing **Back** on a directly addressed item returns to the registry browser.
|
|
44
|
+
|
|
34
45
|
## Work with the agent
|
|
35
46
|
|
|
36
47
|
Type a message and press `Enter` to send it. When the agent asks a question or requests tool approval, respond in the prompt shown by the UI. Connection authorization can open a browser; keep local `eve dev` running until the browser returns to it.
|
package/docs/meta.json
CHANGED
package/docs/sandbox.mdx
CHANGED
|
@@ -182,6 +182,26 @@ export default defineSandbox({
|
|
|
182
182
|
|
|
183
183
|
`justbash()` needs no daemon or VM, but commands run in a simulated bash with a virtual filesystem under `.eve/sandbox-cache/`, with no real binaries (`git`, `node`, package managers) and no network isolation. The `just-bash` package is an optional peer dependency, so `eve dev` installs it into your application automatically when missing (disable with `autoInstall: false`); production processes fail with an actionable install error instead.
|
|
184
184
|
|
|
185
|
+
Pass `customCommands` to expose a `just-bash` `CustomCommand` inside each live sandbox session. The command participates in shell pipelines, redirections, and exit-code handling like a built-in command:
|
|
186
|
+
|
|
187
|
+
```ts title="agent/sandbox.ts"
|
|
188
|
+
import { defineSandbox } from "eve/sandbox";
|
|
189
|
+
import { justbash } from "eve/sandbox/just-bash";
|
|
190
|
+
import { decodeBytesToUtf8, defineCommand } from "just-bash";
|
|
191
|
+
|
|
192
|
+
const uppercase = defineCommand("uppercase", async (_args, context) => ({
|
|
193
|
+
stdout: decodeBytesToUtf8(context.stdin).toUpperCase(),
|
|
194
|
+
stderr: "",
|
|
195
|
+
exitCode: 0,
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
export default defineSandbox({
|
|
199
|
+
backend: justbash({ customCommands: [uppercase] }),
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Custom commands are registered on live and reopened sessions, not during template prewarming. They run trusted host application code and create an explicit bridge from the sandbox to that code. Adapters can use this extension point to expose application APIs such as a Capable App's CLI; the outer eve `bash` tool does not automatically provide per-command Capable approvals.
|
|
204
|
+
|
|
185
205
|
You can also write your own backend. A `SandboxBackend` is an adapter object with a `name`, a `create`, and an optional `prewarm`. It can point at your own container runner, VM pool, internal sandbox service, or another isolation layer, as long as it returns the `SandboxSession` operations eve needs. Handles returned by `create` implement `shutdown()`, which stops the underlying compute at server shutdown. See the `SandboxBackend*` types on `eve/sandbox`.
|
|
186
206
|
|
|
187
207
|
## Lifecycle
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
|
|
6
6
|
"keywords": [
|
|
@@ -381,7 +381,7 @@
|
|
|
381
381
|
"@opentelemetry/api": "^1.0.0",
|
|
382
382
|
"ai": "^7.0.58",
|
|
383
383
|
"braintrust": "^3.0.0",
|
|
384
|
-
"just-bash": "^3.
|
|
384
|
+
"just-bash": "^3.1.0",
|
|
385
385
|
"microsandbox": "^0.5.0"
|
|
386
386
|
},
|
|
387
387
|
"peerDependenciesMeta": {
|