@whoz-oss/coday-server 0.108.0 → 0.109.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whoz-oss/coday-server",
3
- "version": "0.108.0",
3
+ "version": "0.109.0",
4
4
  "repository": "https://github.com/whoz-oss/coday",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
package/server.js CHANGED
@@ -1392,7 +1392,7 @@ ${m.map(y=>{let x=(y.assignees||[]).map(E=>E.name).join(", ")||"Unassigned",_=y.
1392
1392
 
1393
1393
  ${p}${h}`;return f>0&&(g+=`
1394
1394
 
1395
- _${f} comment(s) available \u2014 use getComments with recording ID ${a.id} to retrieve them._`),g}catch(t){return`Error: ${t.message}`}}var A9=class extends Vt{constructor(t,n,i,a,o){super(t,a,o);this.integrationService=n;this.userService=i}static TYPE="BASECAMP";oauth=null;async handleOAuthCallback(t){this.oauth&&await this.oauth.handleCallback(t)}async buildTools(t){let n=[];if(!this.integrationService.hasIntegration(this.name))return n;let i=this.integrationService.getIntegration(this.name);if(!i)return n;let a=i.username,o=i.apiKey,s=i.oauth2?.redirect_uri;if(!s)return this.interactor.displayText("Basecamp integration requires redirectUri"),n;if(!a||!o)return this.interactor.displayText("Basecamp integration requires clientId (username) and clientSecret (apiKey)"),n;let c=t.project.name;this.oauth=new sT(a,o,s,this.interactor,this.userService,c);let u={type:"function",function:{name:`${this.name}__listProjects`,description:"List all projects in the connected Basecamp account. Will prompt for OAuth authentication if not already connected. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each. Use the page parameter to navigate through results.",parameters:{type:"object",properties:{page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({page:x})=>g7e(this.oauth,x)}};n.push(u);let l={type:"function",function:{name:`${this.name}__getMessageBoard`,description:"Get the message board ID for a Basecamp project. You need this ID to retrieve messages from the project.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listBasecampProjects)"}}},parse:JSON.parse,function:async({projectId:x})=>v7e(this.oauth,x)}};n.push(l);let f={type:"function",function:{name:`${this.name}__getMessages`,description:"List all messages in a Basecamp message board. Returns a summary of each message with title, author, date, and preview. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each. Use the page parameter to navigate through results.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listBasecampProjects)"},messageBoardId:{type:"number",description:"The message board ID (from getBasecampMessageBoard)"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({projectId:x,messageBoardId:_,page:E})=>y7e(this.oauth,x,_,E)}};n.push(f);let p={type:"function",function:{name:`${this.name}__getMessage`,description:"Get the full content of a specific Basecamp message, including title, author, date, and complete text content.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listBasecampProjects)"},messageId:{type:"number",description:"The message ID (from getBasecampMessages)"}}},parse:JSON.parse,function:async({projectId:x,messageId:_})=>b7e(this.oauth,x,_)}};n.push(p);let d={type:"function",function:{name:`${this.name}__getComments`,description:"Get comments for any Basecamp recording (message, card, etc.). The recordingId is the ID of the message or card. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each.",parameters:{type:"object",properties:{recordingId:{type:"number",description:"The ID of the recording (message ID, card ID, etc.) to get comments for"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({recordingId:x,page:_})=>x7e(this.oauth,x,_)}};n.push(d);let m={type:"function",function:{name:`${this.name}__getForwards`,description:'List forwarded emails in a Basecamp inbox. The projectId comes from listProjects. The inboxId is the ID of the inbox tool (name: "inbox") from listProjects \u2014 it is listed even when disabled. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each.',parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listProjects)"},inboxId:{type:"number",description:"The inbox ID (from the project dock, tool name: inbox)"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({projectId:x,inboxId:_,page:E})=>w7e(this.oauth,x,_,E)}};n.push(m);let h={type:"function",function:{name:`${this.name}__getForward`,description:"Get the full content of a specific forwarded email, including subject, sender, date, and body.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listProjects)"},forwardId:{type:"number",description:"The forward ID (from getForwards)"}}},parse:JSON.parse,function:async({projectId:x,forwardId:_})=>_7e(this.oauth,x,_)}};n.push(h);let g={type:"function",function:{name:`${this.name}__getCardTable`,description:"Get a Basecamp card table (Kanban board) with its columns. The cardTableId is available in the project dock (tool name: kanban_board) from listProjects.",parameters:{type:"object",properties:{cardTableId:{type:"number",description:"The card table ID (from the project dock, tool name: kanban_board)"}}},parse:JSON.parse,function:async({cardTableId:x})=>S7e(this.oauth,x)}};n.push(g);let v={type:"function",function:{name:`${this.name}__getCards`,description:"List cards in a card table column. The columnId comes from getCardTable. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each.",parameters:{type:"object",properties:{columnId:{type:"number",description:"The column ID (from getCardTable)"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({columnId:x,page:_})=>E7e(this.oauth,x,_)}};n.push(v);let y={type:"function",function:{name:`${this.name}__getCard`,description:"Get the full details of a specific card, including content, assignees, due date, steps, and comment count.",parameters:{type:"object",properties:{cardId:{type:"number",description:"The card ID (from getCards)"}}},parse:JSON.parse,function:async({cardId:x})=>k7e(this.oauth,x)}};return n.push(y),n}};var cT=class{constructor(e,t,n,i,a){this.config=e;this.interactor=t;this.userService=n;this.integrationName=a;this.resolvedProjectName=n.resolveProjectName(i);let o=new URL(e.authorizationEndpoint);this.as={issuer:`${o.origin}${o.pathname.split("/").slice(0,-1).join("/")}`,authorization_endpoint:e.authorizationEndpoint,token_endpoint:e.tokenEndpoint},this.client={client_id:e.clientId},this.clientAuth=tT(e.clientSecret)}as;client;clientAuth;tokenData=null;pendingState=null;pendingCodeVerifier=null;pendingResolve=null;pendingReject=null;pendingAuthPromise=null;resolvedProjectName;hasToken(){return this.tokenData?!0:(this.loadTokensFromStorage(),!!this.tokenData)}hasRefreshToken(){return this.tokenData||this.loadTokensFromStorage(),!!this.tokenData?.refreshToken}isAuthenticated(){if(this.tokenData||(this.interactor.debug(`[OAuth:${this.integrationName}] no in-memory token, trying storage`),this.loadTokensFromStorage()),!this.tokenData)return this.interactor.debug(`[OAuth:${this.integrationName}] no token found in storage`),!1;let e=this.tokenData.expiresAt-Date.now(),t=e>300*1e3;return this.interactor.debug(`[OAuth:${this.integrationName}] token expiresAt=${new Date(this.tokenData.expiresAt).toISOString()}, remainingMs=${e}, valid=${t}`),t}async getAccessToken(){if(this.tokenData||this.loadTokensFromStorage(),!this.tokenData)throw new Error("Not authenticated. Call authenticate() first.");return this.isAuthenticated()||(this.tokenData.refreshToken?(this.interactor.debug(`[OAuth:${this.integrationName}] token expired, attempting refresh`),await this.refreshToken(),this.interactor.debug(`[OAuth:${this.integrationName}] token refreshed successfully`)):this.interactor.debug(`[OAuth:${this.integrationName}] token expired and no refresh_token, returning expired token`)),this.tokenData.accessToken}authenticate(){return this.isAuthenticated()?Promise.resolve(this.tokenData):this.pendingAuthPromise?(this.interactor.debug(`[OAuth:${this.integrationName}] OAuth flow already in progress, reusing pending promise`),this.pendingAuthPromise):(this.pendingAuthPromise=new Promise((e,t)=>{this.pendingResolve=e,this.pendingReject=t}),this.pendingAuthPromise.then(()=>{this.pendingAuthPromise=null},()=>{this.pendingAuthPromise=null}),this.startAuthFlow().catch(e=>this.rejectPending(e)),this.pendingAuthPromise)}async startAuthFlow(){this.tokenData=null,this.clearTokensFromStorage(),this.interactor.debug(`[OAuth:${this.integrationName}] starting fresh OAuth flow`);let e=YC(),t=KC(),n=await XC(t);this.pendingState=e,this.pendingCodeVerifier=t;let i=new URL(this.as.authorization_endpoint);if(i.searchParams.set("client_id",this.client.client_id),i.searchParams.set("redirect_uri",this.config.redirectUri),i.searchParams.set("response_type","code"),i.searchParams.set("state",e),i.searchParams.set("code_challenge",n),i.searchParams.set("code_challenge_method","S256"),i.searchParams.set("access_type","offline"),i.searchParams.set("prompt","consent"),this.config.scope){let a=Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope;i.searchParams.set("scope",a)}this.interactor.debug(`[OAuth:${this.integrationName}] emitting OAuthRequestEvent, authUrl=${i.toString()}`),this.interactor.sendEvent(new e2({authUrl:i.toString(),state:e,integrationName:this.integrationName})),this.interactor.debug(`[OAuth:${this.integrationName}] waiting for OAuth callback...`)}async handleCallback(e){if(e.integrationName===this.integrationName){if(e.state!==this.pendingState){let t=new Error("Invalid OAuth state");this.interactor.error(t.message),this.rejectPending(t);return}if(e.error){let t=e.error==="access_denied"?"OAuth authentication denied by user":`OAuth error: ${e.error}${e.errorDescription?" - "+e.errorDescription:""}`;this.interactor.warn(t),this.rejectPending(new Error(t));return}if(!e.code||!this.pendingCodeVerifier||!this.pendingState){let t=new Error("No pending OAuth flow or missing code");this.interactor.error(t.message),this.rejectPending(t);return}try{let t=new URL(this.config.redirectUri);t.searchParams.set("code",e.code),t.searchParams.set("state",e.state);let n=oT(this.as,this.client,t,this.pendingState),i=await iT(this.as,this.client,this.clientAuth,n,this.config.redirectUri,this.pendingCodeVerifier),a=await aT(this.as,this.client,i);this.tokenData={accessToken:a.access_token,refreshToken:a.refresh_token,expiresAt:Date.now()+(a.expires_in??3600)*1e3},this.saveTokensToStorage(),this.interactor.displayText(`OAuth authentication successful for ${this.integrationName}`),this.pendingResolve&&(this.pendingResolve(this.tokenData),this.pendingResolve=null)}catch(t){this.interactor.error(`OAuth token exchange failed: ${t.message}`),this.rejectPending(t)}finally{this.pendingState=null,this.pendingCodeVerifier=null}}}rejectPending(e){this.pendingReject&&(this.pendingReject(e),this.pendingReject=null),this.pendingResolve=null,this.pendingState=null,this.pendingCodeVerifier=null}async refreshToken(){if(!this.tokenData?.refreshToken)throw new Error("No refresh token available");try{let e=await rT(this.as,this.client,this.clientAuth,this.tokenData.refreshToken),t=await nT(this.as,this.client,e);this.tokenData={accessToken:t.access_token,refreshToken:t.refresh_token??this.tokenData.refreshToken,expiresAt:Date.now()+(t.expires_in??3600)*1e3},this.saveTokensToStorage()}catch(e){throw this.tokenData=null,this.clearTokensFromStorage(),new Error(`Token refresh failed: ${e.message}`)}}loadTokensFromStorage(){let e=this.userService.config.projects,t=e?.[this.resolvedProjectName],n=t?.integration?.[this.integrationName],i=n?.oauth2?.tokens;this.interactor.debug(`[OAuth:${this.integrationName}] loadTokensFromStorage: projectName=${this.resolvedProjectName}, hasProjects=${!!e}, hasProjectConfig=${!!t}, hasIntegrationConfig=${!!n}, hasTokens=${!!i}`),i&&(this.tokenData={accessToken:i.access_token,refreshToken:i.refresh_token,expiresAt:i.expires_at},this.interactor.debug(`[OAuth:${this.integrationName}] loaded tokens from storage, expiresAt=${new Date(i.expires_at).toISOString()}`))}saveTokensToStorage(){if(!this.tokenData)return;let e=this.userService.config;e.projects||(e.projects={}),e.projects[this.resolvedProjectName]||(e.projects[this.resolvedProjectName]={integration:{}}),e.projects[this.resolvedProjectName].integration||(e.projects[this.resolvedProjectName].integration={}),e.projects[this.resolvedProjectName].integration[this.integrationName]||(e.projects[this.resolvedProjectName].integration[this.integrationName]={}),e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2||(e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2={});let t=e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2;t.tokens={access_token:this.tokenData.accessToken,refresh_token:this.tokenData.refreshToken,expires_at:this.tokenData.expiresAt},this.userService.save(),this.interactor.debug(`[OAuth:${this.integrationName}] saved tokens to storage`)}clearTokensFromStorage(){let e=this.userService.config.projects?.[this.resolvedProjectName]?.integration?.[this.integrationName]?.oauth2;e&&(delete e.tokens,this.userService.save(),this.interactor.debug(`[OAuth:${this.integrationName}] cleared tokens from storage`))}};var A7e=["client_secret","tokens"],C7e=["apiKey","username"],C9=class r extends Vt{constructor(t,n){super(t,r.TYPE,{});this.integrationConfig=n}static TYPE="HTTP_CONFIG";async buildTools(t){return Em(t.username)?[this.buildLetUserFillAuth(),this.buildListHttpIntegrations(),this.buildGetHttpIntegration(),this.buildUpsertHttpIntegration(),this.buildDeleteHttpIntegration(),this.buildGetEndpoint(),this.buildUpsertEndpoint(),this.buildDeleteEndpoint()]:(this.interactor.debug(`[HTTP_CONFIG] user '${t.username}' is not CODAY_ADMIN, tools not available`),[])}getProjectHttpIntegrations(){let t=this.integrationConfig.getUnmaskedIntegrationsAtLevel("project");return Object.fromEntries(Object.entries(t).filter(([,n])=>n.type==="HTTP"))}sanitizeForAgent(t){let n={...t};for(let i of C7e)delete n[i];if(n.oauth2&&typeof n.oauth2=="object"){let i={...n.oauth2};for(let a of A7e)delete i[a];n.oauth2=i}return n}mergeIgnoringSensitive(t,n){let i={...t};for(let[a,o]of Object.entries(n))if(!C7e.includes(a))if(a==="oauth2"&&o&&typeof o=="object"){let s=t.oauth2??{},c=o,u={...s};for(let[l,f]of Object.entries(c))A7e.includes(l)||(u[l]=f);i.oauth2=u}else a!=="http"&&(i[a]=o);return i}buildLetUserFillAuth(){return{type:"function",function:{name:`${this.name}__letUserFillAuth`,description:"Interactively ask the user to fill in sensitive authentication credentials for an HTTP integration. The auth type is inferred from the integration config (e.g. oauth2 \u2192 prompts for client_id and client_secret). Values are saved at PROJECT level, shared across all project users (admin operation).",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the HTTP integration to configure credentials for"}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n})=>{let a=this.integrationConfig.getUnmaskedIntegrationsAtLevel("project")[n];if(!a)return{error:`Integration '${n}' not found`};if(!a.oauth2)return{error:`Integration '${n}' has no oauth2 configuration. Set up the integration first with upsert_http_integration.`};let o=a.oauth2??{},s=await this.interactor.promptSecretText(`OAuth2 client_id for '${n}' (leave as-is to keep current):`,o.client_id),c=await this.interactor.promptSecretText(`OAuth2 client_secret for '${n}' (leave as-is to keep current):`,o.client_secret);if(s===void 0&&c===void 0)return{success:!1,message:"No values provided, nothing was saved."};let u={...o,...s!==void 0&&{client_id:s},...c!==void 0&&{client_secret:c}},l={...a,oauth2:u};return await this.integrationConfig.saveIntegration(n,l,"project"),{success:!0,updated:[...s!==void 0?["client_id"]:[],...c!==void 0?["client_secret"]:[]]}}}}}buildListHttpIntegrations(){return{type:"function",function:{name:`${this.name}__list_http_integrations`,description:"List all HTTP integrations defined at project level. Returns non-sensitive config fields and the list of endpoint names for each integration.",parameters:{type:"object",properties:{}},parse:JSON.parse,function:async()=>{let n=this.getProjectHttpIntegrations();return Object.entries(n).map(([i,a])=>({name:i,type:a.type,baseUrl:a.http?.baseUrl,oauth2:a.oauth2?this.sanitizeForAgent({oauth2:a.oauth2}).oauth2:void 0,endpoints:(a.http?.endpoints??[]).map(o=>o.name)}))}}}}buildGetHttpIntegration(){return{type:"function",function:{name:`${this.name}__get_http_integration`,description:"Get the non-sensitive configuration of a specific HTTP integration (no secrets, endpoint names only \u2014 use get_endpoint for full endpoint details).",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration as defined in the config"}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n})=>{let a=this.getProjectHttpIntegrations()[n];return a?{name:n,...this.sanitizeForAgent(a),http:a.http?{baseUrl:a.http.baseUrl,endpointNames:(a.http.endpoints??[]).map(o=>o.name)}:void 0}:{error:`Integration '${n}' not found`}}}}}buildUpsertHttpIntegration(){return{type:"function",function:{name:`${this.name}__upsert_http_integration`,description:"Create or update an HTTP integration at project level. Only non-sensitive fields are accepted: baseUrl, oauth2 public fields (client_id, redirect_uri, authorization_endpoint, token_endpoint, scope). Sensitive fields (client_secret, tokens, apiKey) are silently ignored. Existing endpoints are always preserved.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration (uppercase recommended, e.g. MY_API)"},baseUrl:{type:"string",description:"Base URL for all HTTP requests of this integration"},oauth2:{type:"object",description:"OAuth2 public configuration. Omit sensitive fields (client_secret, tokens).",properties:{client_id:{type:"string"},redirect_uri:{type:"string"},authorization_endpoint:{type:"string"},token_endpoint:{type:"string"},scope:{type:"string",description:"Space-separated scopes or array of scope strings"}}}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n,baseUrl:i,oauth2:a})=>{let s=this.getProjectHttpIntegrations()[n]??{type:"HTTP"},c={};a&&(c.oauth2=a);let u=this.mergeIgnoringSensitive(s,c);return i!==void 0&&(u.http={...u.http??{},baseUrl:i}),u.type="HTTP",await this.integrationConfig.saveIntegration(n,u,"project"),{success:!0,integrationName:n}}}}}buildDeleteHttpIntegration(){return{type:"function",function:{name:`${this.name}__delete_http_integration`,description:"Delete an HTTP integration entirely from the project configuration.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration to delete"}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n})=>this.getProjectHttpIntegrations()[n]?(await this.integrationConfig.deleteIntegration(n,"project"),{success:!0,integrationName:n}):{error:`Integration '${n}' not found`}}}}buildGetEndpoint(){return{type:"function",function:{name:`${this.name}__get_endpoint`,description:"Get the full definition of a specific endpoint within an HTTP integration.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration"},endpointName:{type:"string",description:"Name of the endpoint"}},required:["integrationName","endpointName"]},parse:JSON.parse,function:async({integrationName:n,endpointName:i})=>{let o=this.getProjectHttpIntegrations()[n];if(!o)return{error:`Integration '${n}' not found`};let s=(o.http?.endpoints??[]).find(c=>c.name===i);return s||{error:`Endpoint '${i}' not found in '${n}'`}}}}}buildUpsertEndpoint(){return{type:"function",function:{name:`${this.name}__upsert_endpoint`,description:"Create or replace an endpoint within an HTTP integration, identified by endpoint.name. If an endpoint with that name already exists it is fully replaced. Other endpoints are preserved.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration"},endpoint:{type:"object",description:"Full endpoint definition",properties:{name:{type:"string",description:"Unique name within this integration (used as tool name suffix)"},method:{type:"string",description:"HTTP method: GET, POST, PUT or DELETE"},path:{type:"string",description:"Path relative to baseUrl, e.g. /users/{userId}"},description:{type:"string",description:"Description exposed to the AI as tool description"},params:{type:"array",description:"List of parameters",items:{type:"object",properties:{name:{type:"string"},type:{type:"string",description:"string | number | boolean"},description:{type:"string"},required:{type:"boolean"},location:{type:"string",description:"path | query | body (default: query)"}}}},keepPaths:{type:"array",items:{type:"string"},description:"Dot-notation paths to keep in response"},ignorePaths:{type:"array",items:{type:"string"},description:"Dot-notation paths to remove from response"},responseFormat:{type:"string",description:"json (default) or yaml"}}}},required:["integrationName","endpoint"]},parse:JSON.parse,function:async({integrationName:n,endpoint:i})=>{let o=this.getProjectHttpIntegrations()[n];if(!o)return{error:`Integration '${n}' not found`};let c=[...(o.http?.endpoints??[]).filter(l=>l.name!==i.name),i],u={...o,http:{...o.http??{baseUrl:""},endpoints:c}};return await this.integrationConfig.saveIntegration(n,u,"project"),{success:!0,integrationName:n,endpointName:i.name}}}}}buildDeleteEndpoint(){return{type:"function",function:{name:`${this.name}__delete_endpoint`,description:"Remove a specific endpoint from an HTTP integration. Other endpoints are preserved.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration"},endpointName:{type:"string",description:"Name of the endpoint to remove"}},required:["integrationName","endpointName"]},parse:JSON.parse,function:async({integrationName:n,endpointName:i})=>{let o=this.getProjectHttpIntegrations()[n];if(!o)return{error:`Integration '${n}' not found`};let s=o.http?.endpoints??[];if(!s.find(u=>u.name===i))return{error:`Endpoint '${i}' not found in '${n}'`};let c={...o,http:{...o.http??{baseUrl:""},endpoints:s.filter(u=>u.name!==i)}};return await this.integrationConfig.saveIntegration(n,c,"project"),{success:!0,integrationName:n,endpointName:i}}}}}};var T7e=ur(Pc(),1);var T9=class extends Vt{constructor(t,n,i,a){super(t,i,a);this.userService=n}static TYPE="HTTP";oauth=null;async handleOAuthCallback(t){this.oauth&&await this.oauth.handleCallback(t)}async buildTools(t,n){let i=this.config?.oauth2;if(!i?.client_id||!i?.client_secret||!i?.redirect_uri)return this.interactor.debug(`[HTTP:${this.name}] missing oauth2 fields: client_id=${!!i?.client_id}, client_secret=${!!i?.client_secret}, redirect_uri=${!!i?.redirect_uri}`),[];let a=this.config?.http?.baseUrl;if(!a)return this.interactor.debug(`[HTTP:${this.name}] missing http.baseUrl`),[];if(!i.authorization_endpoint||!i.token_endpoint)return this.interactor.debug(`[HTTP:${this.name}] missing oauth2 endpoints: authorization_endpoint=${i.authorization_endpoint}, token_endpoint=${i.token_endpoint}`),[];let o=this.config?.http?.endpoints;if(!o?.length)return this.interactor.debug(`[HTTP:${this.name}] no endpoints configured`),[];let s=o.map(c=>`${c.name}:${c.method} ${c.path} (${c.params?.length??0} params${c.keepPaths?", keepPaths["+c.keepPaths.length+"]":""}${c.ignorePaths?", ignorePaths["+c.ignorePaths.length+"]":""}${c.responseFormat?", "+c.responseFormat:""})`).join(" | ");return this.interactor.debug(`[HTTP:${this.name}] baseUrl=${a} | endpoints: ${s}`),this.oauth||(this.oauth=new cT({clientId:i.client_id,clientSecret:i.client_secret,redirectUri:i.redirect_uri,authorizationEndpoint:i.authorization_endpoint,tokenEndpoint:i.token_endpoint,scope:i.scope},this.interactor,this.userService,t.project.name,this.name)),o.map(c=>this.buildTool(c,a))}buildTool(t,n){let i=t.params??[],a={},o=[];for(let c of i)a[c.name]={type:c.type,description:c.description},c.required&&o.push(c.name);return{type:"function",function:{name:`${this.name}__${t.name}`,description:t.description,parameters:{type:"object",properties:a,...o.length?{required:o}:{}},parse:JSON.parse,function:async c=>this.executeEndpoint(t,n,c,i)}}}async executeEndpoint(t,n,i,a){this.interactor.debug(`[HTTP:${this.name}__${t.name}] args=${JSON.stringify(i)}`),this.interactor.debug(`[HTTP:${this.name}__${t.name}] oauth=${!!this.oauth}, isAuthenticated=${this.oauth?.isAuthenticated()}`);let o;try{this.oauth.isAuthenticated()||this.oauth.hasRefreshToken()?o=await this.oauth.getAccessToken():(this.interactor.debug(`[HTTP:${this.name}__${t.name}] no valid token or refresh_token, starting OAuth flow`),await this.oauth.authenticate(),o=await this.oauth.getAccessToken())}catch(v){throw new Error(`Authentication failed for ${this.name}: ${v.message}`)}this.interactor.debug(`[HTTP:${this.name}__${t.name}] got access token (length=${o.length})`);let s=t.path;for(let v of a.filter(y=>y.location==="path")){let y=i[v.name];y!==void 0&&(s=s.replace(`{${v.name}}`,encodeURIComponent(String(y))))}let c=new URL(`${n}${s}`);for(let v of a.filter(y=>!y.location||y.location==="query")){let y=i[v.name];y!==void 0&&c.searchParams.set(v.name,String(y))}let u=a.filter(v=>v.location==="body"),l=u.length>0?JSON.stringify(Object.fromEntries(u.filter(v=>i[v.name]!==void 0).map(v=>[v.name,i[v.name]]))):void 0;this.interactor.debug(`[HTTP:${this.name}__${t.name}] ${t.method} ${c.toString()}`);let f=await fetch(c.toString(),{method:t.method,headers:{Authorization:`Bearer ${o}`,...l?{"Content-Type":"application/json"}:{}},...l?{body:l}:{}});if(!f.ok){let v=await f.text();throw new Error(`HTTP ${f.status} from ${t.method} ${s}: ${v}`)}let p;if((f.headers.get("content-type")??"").includes("application/json"))p=await f.json();else{let v=await f.text();if(!v)return null;try{p=JSON.parse(v)}catch{p=v}}let m=JSON.stringify(p).length,h=Y9t(p,t.keepPaths,t.ignorePaths),g=JSON.stringify(h).length;return t.keepPaths?.length||t.ignorePaths?.length?this.interactor.debug(`[HTTP:${this.name}__${t.name}] response filtered ${m} \u2192 ${g} chars`):this.interactor.debug(`[HTTP:${this.name}__${t.name}] response ${m} chars (no filter)`),t.responseFormat==="yaml"?T7e.stringify(h):h}};function Y9t(r,e,t){return!e?.length&&!t?.length?r:e?.length?xG(r,e):wG(r,t)}function xG(r,e){if(Array.isArray(r))return r.map(t=>xG(t,e));if(r!==null&&typeof r=="object"){let t={};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let o=r,s=i==="*"?Object.keys(o):[i];for(let c of s){let u=o[c];if(u!==void 0)if(a.length===0)t[c]=u;else{let l=xG(u,[a.join(".")]);c in t&&t[c]!==null&&typeof t[c]=="object"&&!Array.isArray(t[c])?t[c]={...t[c],...l}:t[c]=l}}}return t}return r}function wG(r,e){if(Array.isArray(r))return r.map(t=>wG(t,e));if(r!==null&&typeof r=="object"){let t={...r};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let o=i==="*"?Object.keys(t):[i];for(let s of o)s in t&&(a.length===0?delete t[s]:t[s]=wG(t[s],[a.join(".")]))}return t}return r}var uT=class{constructor(e,t,n,i){this.interactor=e;this.services=t;this.mcpConfigs=t.mcp.getMergedConfiguration().servers,this.factoryConstructors=new Map,this.factoryConstructors.set(O8.TYPE,a=>new O8(e,a,{},this.services.options?.baseUrl)),this.factoryConstructors.set(eg.TYPE,a=>new eg(e,i,a,{})),this.factoryConstructors.set(tg.TYPE,a=>new tg(e,n,i,a,{},t.thread)),this.factoryConstructors.set(ng.TYPE,(a,o)=>new ng(e,a,o)),this.factoryConstructors.set(R8.TYPE,a=>new R8(e,a,{})),this.factoryConstructors.set(j8.TYPE,a=>new j8(e,t.memory,a,{})),this.factoryConstructors.set($8.TYPE,a=>new $8(e,t.thread,a,{})),this.factoryConstructors.set(F8.TYPE,(a,o)=>new F8(e,t.integration,a,o)),this.factoryConstructors.set(M8.TYPE,(a,o)=>new M8(e,t.integration,a,o,t.projectService)),this.factoryConstructors.set(e9.TYPE,(a,o)=>new e9(e,t.integration,a,o)),this.factoryConstructors.set(x9.TYPE,(a,o)=>new x9(e,t.integration,a,o)),this.factoryConstructors.set(w9.TYPE,(a,o)=>new w9(e,t.integration,a,o)),this.factoryConstructors.set(_9.TYPE,(a,o)=>new _9(e,t.integration,a,o)),this.factoryConstructors.set(E9.TYPE,(a,o)=>new E9(e,t.integration,a,o)),this.factoryConstructors.set(A9.TYPE,(a,o)=>new A9(e,t.integration,t.user,a,o)),this.factoryConstructors.set(T9.TYPE,(a,o)=>new T9(e,t.user,a,o)),this.factoryConstructors.set(C9.TYPE,()=>new C9(e,t.integrationConfig)),this.factoryConstructors.set(N8.TYPE,a=>new N8(e,a))}factoryConstructors;factoryInstances=new Map;mcpConfigs;tools=[];async kill(){console.log("[TOOLBOX] Closing tool factories"),await Promise.all(Array.from(this.factoryInstances.values()).map(e=>e.kill())),console.log("[TOOLBOX] Closed tool factories")}async getTools(e){let{context:t,integrations:n,agentName:i}=e,a=t.aiThread?.id;a||this.interactor.warn("No thread ID in context, MCP tools will not be available");let o=[];if(n)for(let[c]of n){let u=this.factoryInstances.get(c)??this.createFactory(c);u&&o.push(u)}else{let c=new Set(Object.values(this.services.integration.integrations).map(l=>l?.type).filter(Boolean)),u=new Set([...Array.from(this.factoryConstructors.keys()).filter(l=>!c.has(l)),...Object.keys(this.services.integration.integrations)]);for(let l of u){let f=this.factoryInstances.get(l)??this.createFactory(l);f&&o.push(f)}}let s=[];if(a){for(let c of this.mcpConfigs)if(!(n&&!n.has(c.name)))try{let u=await this.services.mcpPool.getOrCreateFactory(c,a,()=>new zA(c));s.push(u)}catch(u){this.interactor.debug(`Error creating MCP factory for ${c.name}: ${u}`)}}o.push(...s);try{let c=await Promise.all(o.map(async u=>{try{return await u.getTools(t,n?.get(u.name)??[],i??"default")}catch(l){return this.interactor.debug(`Error building tools from ${u.name} for agent ${i}: ${l}`),[]}}));return this.tools=c.flat(),this.tools}catch(c){return this.interactor.debug(`Unexpected error building tools for agent ${i}: ${c}`),[]}}createFactory(e){let t=this.services.integration.integrations,n=t[e]??{},i=t[e]?.type,a=i?this.factoryConstructors.get(i):void 0,o=this.factoryConstructors.get(e),s=a??o,c=a?i:e;if(!s){this.interactor.debug(`No factory constructor for integration '${e}' (type='${i??"none"}')`);return}try{let u=s(e,n);return this.factoryInstances.set(e,u),this.interactor.debug(`Created integration factory '${e}' of type '${c}'`),u}catch(u){this.interactor.debug(`Error creating factory for '${e}': ${u}`);return}}async handleOAuthCallback(e){let t=this.factoryInstances.get(e.integrationName)??this.createFactory(e.integrationName);if(!t){this.interactor.warn(`No integration found for OAuth callback: ${e.integrationName}`);return}if("handleOAuthCallback"in t&&typeof t.handleOAuthCallback=="function")try{await t.handleOAuthCallback(e)}catch(n){this.interactor.error(`Error handling OAuth callback for ${e.integrationName}: ${n}`)}else this.interactor.warn(`Integration ${e.integrationName} does not support OAuth callbacks`)}};var Z1=class{constructor(e,t,n,i,a=[]){this.interactor=e;this.aiClientProvider=t;this.services=n;this.projectPath=i;this.commandLineAgentFolders=a;this.services.project.selectedProject$.subscribe(()=>{this.agentCache.clear(),this.agentDefinitions=[]}),this.toolbox=new uT(this.interactor,n,this.findAgentByNameStart,this.listAgentSummaries)}agentCache=new Map;agentDefinitions=[];toolbox;listAgentSummaries=()=>this.agentDefinitions.map(e=>({name:e.definition.name,description:e.definition.description})).sort((e,t)=>e.name.toLowerCase().localeCompare(t.name.toLowerCase()));initialize=async e=>{if(this.agentDefinitions.length>0)return;let t=performance.now();this.interactor.debug("\u{1F680} Starting agent initialization...");try{let i=performance.now();if(e.project.agents?.length)for(let d of e.project.agents)this.addDefinition(d,this.projectPath);let a=performance.now()-i;this.interactor.debug(`\u{1F4CB} Loaded agent definitions from coday.yml: ${a.toFixed(2)}ms (${e.project.agents?.length??0} agents)`);let o=performance.now(),s=this.services.project.selectedProject;if(s?.config.agents?.length)for(let d of s.config.agents)this.addDefinition(d,this.projectPath);let c=performance.now()-o;this.interactor.debug(`\u2699\uFE0F Loaded agent definitions from project local config: ${c.toFixed(2)}ms (${s?.config.agents?.length??0} agents)`);let u=performance.now();await this.loadFromFiles(e);let l=performance.now()-u;this.interactor.debug(`\u{1F4C1} Loaded agent definitions from files: ${l.toFixed(2)}ms`),this.agentDefinitions.length===0&&(this.addDefinition(Ox,this.projectPath),this.interactor.debug("\u{1F504} No agent definitions found, using Coday as backup"));let f=performance.now();this.generateVirtualAgentsFromModels();let p=performance.now()-f;this.interactor.debug(`\u{1F916} Generated virtual agents from models: ${p.toFixed(2)}ms`)}catch(i){throw this.interactor.error(`Failed to initialize agent definitions: ${i}`),i}let n=performance.now()-t;this.interactor.debug(`\u{1F3AF} Total agent definition loading time: ${n.toFixed(2)}ms`)};async findByName(e,t){await this.initialize(t);let n=e.toLowerCase();if(this.agentCache.has(n))return this.agentCache.get(n);let i=this.agentDefinitions.find(a=>a.definition.name.toLowerCase()===n);if(i){let a=await this.tryAddAgent(i,t);if(a)return this.agentCache.set(n,a),a}}findAgentByNameStart=async(e,t)=>{if(!e)return;await this.initialize(t);let n=await this.findAgentsByNameStart(e?.toLowerCase()||"",t);if(n.length===0)return;if(n.length===1)return n[0];let i=n.map(a=>a.name);try{if(t.oneshot)return;let a=await this.interactor.chooseOption(i,`Multiple agents match '${e}', please select one:`);return n.find(o=>o.name===a)}catch{this.interactor.error("Selection cancelled");return}};async findAgentsByNameStart(e,t){await this.initialize(t);let n=e.toLowerCase(),i=this.agentDefinitions.filter(o=>o.definition.name.toLowerCase().startsWith(n)),a=[];for(let o of i){let s=o.definition.name.toLowerCase(),c=this.agentCache.get(s);c||(c=await this.tryAddAgent(o,t),c&&this.agentCache.set(s,c)),c&&a.push(c)}return a}getPreferredAgent(){let e=this.services.project.selectedProject?.name;return e?this.services.user.config.projects?.[this.services.user.resolveProjectName(e)]?.defaultAgent:void 0}async kill(){this.aiClientProvider.kill(),await this.toolbox.kill()}addDefinition(e,t=this.projectPath){this.agentDefinitions.find(n=>n.definition.name===e.name)||this.agentDefinitions.push({definition:{...Ox,...e},basePath:t})}generateVirtualAgentsFromModels(){let e=this.aiClientProvider.getAllModels();for(let t of e){let n={name:t.name,description:`Direct access to ${t.name} model (${t.providerName})`,instructions:"",aiProvider:t.providerName,modelName:t.name};this.addDefinition(n,this.projectPath)}this.interactor.debug(`\u{1F916} Generated ${e.length} virtual agents from available models: ${e.map(t=>t.name).join(", ")}`)}async loadFromFiles(e){let t=[],n=[],i=this.services.project.selectedProject;i&&t.push(Md.join(i.configPath,"agents"));let a=this.services.project.selectedProject?.config.path;if(a){let l=await f2({text:"coday.yaml",root:a});if(l.length>0){let f=Md.dirname(l[0]);t.push(Md.join(a,f,"agents"))}e.project.agentFolders?.length&&t.push(...e.project.agentFolders)}t.push(...this.commandLineAgentFolders);let o=performance.now();await Promise.all(t.map(async l=>{try{n.push(...(await lT.readdir(l)).filter(f=>f.endsWith(".yml")||f.endsWith(".yaml")).map(f=>Md.join(l,f)))}catch(f){f.code==="EPERM"?console.error(`Permission denied to access ${l}. This is common for protected directories.
1395
+ _${f} comment(s) available \u2014 use getComments with recording ID ${a.id} to retrieve them._`),g}catch(t){return`Error: ${t.message}`}}var A9=class extends Vt{constructor(t,n,i,a,o){super(t,a,o);this.integrationService=n;this.userService=i}static TYPE="BASECAMP";oauth=null;async handleOAuthCallback(t){this.oauth&&await this.oauth.handleCallback(t)}async buildTools(t){let n=[];if(!this.integrationService.hasIntegration(this.name))return n;let i=this.integrationService.getIntegration(this.name);if(!i)return n;let a=i.username,o=i.apiKey,s=i.oauth2?.redirect_uri;if(!s)return this.interactor.displayText("Basecamp integration requires redirectUri"),n;if(!a||!o)return this.interactor.displayText("Basecamp integration requires clientId (username) and clientSecret (apiKey)"),n;let c=t.project.name;this.oauth=new sT(a,o,s,this.interactor,this.userService,c);let u={type:"function",function:{name:`${this.name}__listProjects`,description:"List all projects in the connected Basecamp account. Will prompt for OAuth authentication if not already connected. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each. Use the page parameter to navigate through results.",parameters:{type:"object",properties:{page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({page:x})=>g7e(this.oauth,x)}};n.push(u);let l={type:"function",function:{name:`${this.name}__getMessageBoard`,description:"Get the message board ID for a Basecamp project. You need this ID to retrieve messages from the project.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listBasecampProjects)"}}},parse:JSON.parse,function:async({projectId:x})=>v7e(this.oauth,x)}};n.push(l);let f={type:"function",function:{name:`${this.name}__getMessages`,description:"List all messages in a Basecamp message board. Returns a summary of each message with title, author, date, and preview. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each. Use the page parameter to navigate through results.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listBasecampProjects)"},messageBoardId:{type:"number",description:"The message board ID (from getBasecampMessageBoard)"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({projectId:x,messageBoardId:_,page:E})=>y7e(this.oauth,x,_,E)}};n.push(f);let p={type:"function",function:{name:`${this.name}__getMessage`,description:"Get the full content of a specific Basecamp message, including title, author, date, and complete text content.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listBasecampProjects)"},messageId:{type:"number",description:"The message ID (from getBasecampMessages)"}}},parse:JSON.parse,function:async({projectId:x,messageId:_})=>b7e(this.oauth,x,_)}};n.push(p);let d={type:"function",function:{name:`${this.name}__getComments`,description:"Get comments for any Basecamp recording (message, card, etc.). The recordingId is the ID of the message or card. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each.",parameters:{type:"object",properties:{recordingId:{type:"number",description:"The ID of the recording (message ID, card ID, etc.) to get comments for"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({recordingId:x,page:_})=>x7e(this.oauth,x,_)}};n.push(d);let m={type:"function",function:{name:`${this.name}__getForwards`,description:'List forwarded emails in a Basecamp inbox. The projectId comes from listProjects. The inboxId is the ID of the inbox tool (name: "inbox") from listProjects \u2014 it is listed even when disabled. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each.',parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listProjects)"},inboxId:{type:"number",description:"The inbox ID (from the project dock, tool name: inbox)"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({projectId:x,inboxId:_,page:E})=>w7e(this.oauth,x,_,E)}};n.push(m);let h={type:"function",function:{name:`${this.name}__getForward`,description:"Get the full content of a specific forwarded email, including subject, sender, date, and body.",parameters:{type:"object",properties:{projectId:{type:"number",description:"The project ID (from listProjects)"},forwardId:{type:"number",description:"The forward ID (from getForwards)"}}},parse:JSON.parse,function:async({projectId:x,forwardId:_})=>_7e(this.oauth,x,_)}};n.push(h);let g={type:"function",function:{name:`${this.name}__getCardTable`,description:"Get a Basecamp card table (Kanban board) with its columns. The cardTableId is available in the project dock (tool name: kanban_board) from listProjects.",parameters:{type:"object",properties:{cardTableId:{type:"number",description:"The card table ID (from the project dock, tool name: kanban_board)"}}},parse:JSON.parse,function:async({cardTableId:x})=>S7e(this.oauth,x)}};n.push(g);let v={type:"function",function:{name:`${this.name}__getCards`,description:"List cards in a card table column. The columnId comes from getCardTable. Basecamp uses geared pagination: page 1 returns 15 results, page 2 returns 30, page 3 returns 50, and page 4+ return 100 results each.",parameters:{type:"object",properties:{columnId:{type:"number",description:"The column ID (from getCardTable)"},page:{type:"number",description:"Page number to retrieve (optional). If not provided, returns page 1."}}},parse:JSON.parse,function:async({columnId:x,page:_})=>E7e(this.oauth,x,_)}};n.push(v);let y={type:"function",function:{name:`${this.name}__getCard`,description:"Get the full details of a specific card, including content, assignees, due date, steps, and comment count.",parameters:{type:"object",properties:{cardId:{type:"number",description:"The card ID (from getCards)"}}},parse:JSON.parse,function:async({cardId:x})=>k7e(this.oauth,x)}};return n.push(y),n}};var cT=class{constructor(e,t,n,i,a){this.config=e;this.interactor=t;this.userService=n;this.integrationName=a;this.resolvedProjectName=n.resolveProjectName(i);let o=new URL(e.authorizationEndpoint);this.as={issuer:`${o.origin}${o.pathname.split("/").slice(0,-1).join("/")}`,authorization_endpoint:e.authorizationEndpoint,token_endpoint:e.tokenEndpoint},this.client={client_id:e.clientId},this.clientAuth=tT(e.clientSecret)}as;client;clientAuth;tokenData=null;pendingState=null;pendingCodeVerifier=null;pendingResolve=null;pendingReject=null;pendingAuthPromise=null;resolvedProjectName;hasToken(){return this.tokenData?!0:(this.loadTokensFromStorage(),!!this.tokenData)}hasRefreshToken(){return this.tokenData||this.loadTokensFromStorage(),!!this.tokenData?.refreshToken}isAuthenticated(){if(this.tokenData||(this.interactor.debug(`[OAuth:${this.integrationName}] no in-memory token, trying storage`),this.loadTokensFromStorage()),!this.tokenData)return this.interactor.debug(`[OAuth:${this.integrationName}] no token found in storage`),!1;let e=this.tokenData.expiresAt-Date.now(),t=e>300*1e3;return this.interactor.debug(`[OAuth:${this.integrationName}] token expiresAt=${new Date(this.tokenData.expiresAt).toISOString()}, remainingMs=${e}, valid=${t}`),t}async getAccessToken(){if(this.tokenData||this.loadTokensFromStorage(),!this.tokenData)throw new Error("Not authenticated. Call authenticate() first.");return this.isAuthenticated()||(this.tokenData.refreshToken?(this.interactor.debug(`[OAuth:${this.integrationName}] token expired, attempting refresh`),await this.refreshToken(),this.interactor.debug(`[OAuth:${this.integrationName}] token refreshed successfully`)):this.interactor.debug(`[OAuth:${this.integrationName}] token expired and no refresh_token, returning expired token`)),this.tokenData.accessToken}authenticate(){return this.isAuthenticated()?Promise.resolve(this.tokenData):this.pendingAuthPromise?(this.interactor.debug(`[OAuth:${this.integrationName}] OAuth flow already in progress, reusing pending promise`),this.pendingAuthPromise):(this.pendingAuthPromise=new Promise((e,t)=>{this.pendingResolve=e,this.pendingReject=t}),this.pendingAuthPromise.then(()=>{this.pendingAuthPromise=null},()=>{this.pendingAuthPromise=null}),this.startAuthFlow().catch(e=>this.rejectPending(e)),this.pendingAuthPromise)}async startAuthFlow(){this.tokenData=null,this.clearTokensFromStorage(),this.interactor.debug(`[OAuth:${this.integrationName}] starting fresh OAuth flow`);let e=YC(),t=KC(),n=await XC(t);this.pendingState=e,this.pendingCodeVerifier=t;let i=new URL(this.as.authorization_endpoint);if(i.searchParams.set("client_id",this.client.client_id),i.searchParams.set("redirect_uri",this.config.redirectUri),i.searchParams.set("response_type","code"),i.searchParams.set("state",e),i.searchParams.set("code_challenge",n),i.searchParams.set("code_challenge_method","S256"),i.searchParams.set("access_type","offline"),i.searchParams.set("prompt","consent"),this.config.scope){let a=Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope;i.searchParams.set("scope",a)}this.interactor.debug(`[OAuth:${this.integrationName}] emitting OAuthRequestEvent, authUrl=${i.toString()}`),this.interactor.sendEvent(new e2({authUrl:i.toString(),state:e,integrationName:this.integrationName})),this.interactor.debug(`[OAuth:${this.integrationName}] waiting for OAuth callback...`)}async handleCallback(e){if(e.integrationName===this.integrationName){if(e.state!==this.pendingState){let t=new Error("Invalid OAuth state");this.interactor.error(t.message),this.rejectPending(t);return}if(e.error){let t=e.error==="access_denied"?"OAuth authentication denied by user":`OAuth error: ${e.error}${e.errorDescription?" - "+e.errorDescription:""}`;this.interactor.warn(t),this.rejectPending(new Error(t));return}if(!e.code||!this.pendingCodeVerifier||!this.pendingState){let t=new Error("No pending OAuth flow or missing code");this.interactor.error(t.message),this.rejectPending(t);return}try{let t=new URL(this.config.redirectUri);t.searchParams.set("code",e.code),t.searchParams.set("state",e.state);let n=oT(this.as,this.client,t,this.pendingState),i=await iT(this.as,this.client,this.clientAuth,n,this.config.redirectUri,this.pendingCodeVerifier),a=await aT(this.as,this.client,i);this.tokenData={accessToken:a.access_token,refreshToken:a.refresh_token,expiresAt:Date.now()+(a.expires_in??3600)*1e3},this.saveTokensToStorage(),this.interactor.displayText(`OAuth authentication successful for ${this.integrationName}`),this.pendingResolve&&(this.pendingResolve(this.tokenData),this.pendingResolve=null)}catch(t){this.interactor.error(`OAuth token exchange failed: ${t.message}`),this.rejectPending(t)}finally{this.pendingState=null,this.pendingCodeVerifier=null}}}rejectPending(e){this.pendingReject&&(this.pendingReject(e),this.pendingReject=null),this.pendingResolve=null,this.pendingState=null,this.pendingCodeVerifier=null}async refreshToken(){if(!this.tokenData?.refreshToken)throw new Error("No refresh token available");try{let e=await rT(this.as,this.client,this.clientAuth,this.tokenData.refreshToken),t=await nT(this.as,this.client,e);this.tokenData={accessToken:t.access_token,refreshToken:t.refresh_token??this.tokenData.refreshToken,expiresAt:Date.now()+(t.expires_in??3600)*1e3},this.saveTokensToStorage()}catch(e){throw this.tokenData=null,this.clearTokensFromStorage(),new Error(`Token refresh failed: ${e.message}`)}}loadTokensFromStorage(){let e=this.userService.config.projects,t=e?.[this.resolvedProjectName],n=t?.integration?.[this.integrationName],i=n?.oauth2?.tokens;this.interactor.debug(`[OAuth:${this.integrationName}] loadTokensFromStorage: projectName=${this.resolvedProjectName}, hasProjects=${!!e}, hasProjectConfig=${!!t}, hasIntegrationConfig=${!!n}, hasTokens=${!!i}`),i&&(this.tokenData={accessToken:i.access_token,refreshToken:i.refresh_token,expiresAt:i.expires_at},this.interactor.debug(`[OAuth:${this.integrationName}] loaded tokens from storage, expiresAt=${new Date(i.expires_at).toISOString()}`))}saveTokensToStorage(){if(!this.tokenData)return;let e=this.userService.config;e.projects||(e.projects={}),e.projects[this.resolvedProjectName]||(e.projects[this.resolvedProjectName]={integration:{}}),e.projects[this.resolvedProjectName].integration||(e.projects[this.resolvedProjectName].integration={}),e.projects[this.resolvedProjectName].integration[this.integrationName]||(e.projects[this.resolvedProjectName].integration[this.integrationName]={}),e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2||(e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2={});let t=e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2;t.tokens={access_token:this.tokenData.accessToken,refresh_token:this.tokenData.refreshToken,expires_at:this.tokenData.expiresAt},this.userService.save(),this.interactor.debug(`[OAuth:${this.integrationName}] saved tokens to storage`)}invalidateTokens(){this.tokenData=null,this.clearTokensFromStorage(),this.interactor.debug(`[OAuth:${this.integrationName}] tokens invalidated`)}clearTokensFromStorage(){let e=this.userService.config.projects?.[this.resolvedProjectName]?.integration?.[this.integrationName]?.oauth2;e&&(delete e.tokens,this.userService.save(),this.interactor.debug(`[OAuth:${this.integrationName}] cleared tokens from storage`))}};var A7e=["client_secret","tokens"],C7e=["apiKey","username"],C9=class r extends Vt{constructor(t,n){super(t,r.TYPE,{});this.integrationConfig=n}static TYPE="HTTP_CONFIG";async buildTools(t){return Em(t.username)?[this.buildLetUserFillAuth(),this.buildListHttpIntegrations(),this.buildGetHttpIntegration(),this.buildUpsertHttpIntegration(),this.buildDeleteHttpIntegration(),this.buildGetEndpoint(),this.buildUpsertEndpoint(),this.buildDeleteEndpoint()]:(this.interactor.debug(`[HTTP_CONFIG] user '${t.username}' is not CODAY_ADMIN, tools not available`),[])}getProjectHttpIntegrations(){let t=this.integrationConfig.getUnmaskedIntegrationsAtLevel("project");return Object.fromEntries(Object.entries(t).filter(([,n])=>n.type==="HTTP"))}sanitizeForAgent(t){let n={...t};for(let i of C7e)delete n[i];if(n.oauth2&&typeof n.oauth2=="object"){let i={...n.oauth2};for(let a of A7e)delete i[a];n.oauth2=i}return n}mergeIgnoringSensitive(t,n){let i={...t};for(let[a,o]of Object.entries(n))if(!C7e.includes(a))if(a==="oauth2"&&o&&typeof o=="object"){let s=t.oauth2??{},c=o,u={...s};for(let[l,f]of Object.entries(c))A7e.includes(l)||(u[l]=f);i.oauth2=u}else a!=="http"&&(i[a]=o);return i}buildLetUserFillAuth(){return{type:"function",function:{name:`${this.name}__letUserFillAuth`,description:"Interactively ask the user to fill in sensitive authentication credentials for an HTTP integration. The auth type is inferred from the integration config (e.g. oauth2 \u2192 prompts for client_id and client_secret). Values are saved at PROJECT level, shared across all project users (admin operation).",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the HTTP integration to configure credentials for"}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n})=>{let a=this.integrationConfig.getUnmaskedIntegrationsAtLevel("project")[n];if(!a)return{error:`Integration '${n}' not found`};if(!a.oauth2)return{error:`Integration '${n}' has no oauth2 configuration. Set up the integration first with upsert_http_integration.`};let o=a.oauth2??{},s=await this.interactor.promptSecretText(`OAuth2 client_id for '${n}' (leave as-is to keep current):`,o.client_id),c=await this.interactor.promptSecretText(`OAuth2 client_secret for '${n}' (leave as-is to keep current):`,o.client_secret);if(s===void 0&&c===void 0)return{success:!1,message:"No values provided, nothing was saved."};let u={...o,...s!==void 0&&{client_id:s},...c!==void 0&&{client_secret:c}},l={...a,oauth2:u};return await this.integrationConfig.saveIntegration(n,l,"project"),{success:!0,updated:[...s!==void 0?["client_id"]:[],...c!==void 0?["client_secret"]:[]]}}}}}buildListHttpIntegrations(){return{type:"function",function:{name:`${this.name}__list_http_integrations`,description:"List all HTTP integrations defined at project level. Returns non-sensitive config fields and the list of endpoint names for each integration.",parameters:{type:"object",properties:{}},parse:JSON.parse,function:async()=>{let n=this.getProjectHttpIntegrations();return Object.entries(n).map(([i,a])=>({name:i,type:a.type,baseUrl:a.http?.baseUrl,oauth2:a.oauth2?this.sanitizeForAgent({oauth2:a.oauth2}).oauth2:void 0,endpoints:(a.http?.endpoints??[]).map(o=>o.name)}))}}}}buildGetHttpIntegration(){return{type:"function",function:{name:`${this.name}__get_http_integration`,description:"Get the non-sensitive configuration of a specific HTTP integration (no secrets, endpoint names only \u2014 use get_endpoint for full endpoint details).",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration as defined in the config"}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n})=>{let a=this.getProjectHttpIntegrations()[n];return a?{name:n,...this.sanitizeForAgent(a),http:a.http?{baseUrl:a.http.baseUrl,endpointNames:(a.http.endpoints??[]).map(o=>o.name)}:void 0}:{error:`Integration '${n}' not found`}}}}}buildUpsertHttpIntegration(){return{type:"function",function:{name:`${this.name}__upsert_http_integration`,description:"Create or update an HTTP integration at project level. Only non-sensitive fields are accepted: baseUrl, oauth2 public fields (client_id, redirect_uri, authorization_endpoint, token_endpoint, scope). Sensitive fields (client_secret, tokens, apiKey) are silently ignored. Existing endpoints are always preserved.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration (uppercase recommended, e.g. MY_API)"},baseUrl:{type:"string",description:"Base URL for all HTTP requests of this integration"},oauth2:{type:"object",description:"OAuth2 public configuration. Omit sensitive fields (client_secret, tokens).",properties:{client_id:{type:"string"},redirect_uri:{type:"string"},authorization_endpoint:{type:"string"},token_endpoint:{type:"string"},scope:{type:"string",description:"Space-separated scopes or array of scope strings"}}}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n,baseUrl:i,oauth2:a})=>{let s=this.getProjectHttpIntegrations()[n]??{type:"HTTP"},c={};a&&(c.oauth2=a);let u=this.mergeIgnoringSensitive(s,c);return i!==void 0&&(u.http={...u.http??{},baseUrl:i}),u.type="HTTP",await this.integrationConfig.saveIntegration(n,u,"project"),{success:!0,integrationName:n}}}}}buildDeleteHttpIntegration(){return{type:"function",function:{name:`${this.name}__delete_http_integration`,description:"Delete an HTTP integration entirely from the project configuration.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration to delete"}},required:["integrationName"]},parse:JSON.parse,function:async({integrationName:n})=>this.getProjectHttpIntegrations()[n]?(await this.integrationConfig.deleteIntegration(n,"project"),{success:!0,integrationName:n}):{error:`Integration '${n}' not found`}}}}buildGetEndpoint(){return{type:"function",function:{name:`${this.name}__get_endpoint`,description:"Get the full definition of a specific endpoint within an HTTP integration.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration"},endpointName:{type:"string",description:"Name of the endpoint"}},required:["integrationName","endpointName"]},parse:JSON.parse,function:async({integrationName:n,endpointName:i})=>{let o=this.getProjectHttpIntegrations()[n];if(!o)return{error:`Integration '${n}' not found`};let s=(o.http?.endpoints??[]).find(c=>c.name===i);return s||{error:`Endpoint '${i}' not found in '${n}'`}}}}}buildUpsertEndpoint(){return{type:"function",function:{name:`${this.name}__upsert_endpoint`,description:"Create or replace an endpoint within an HTTP integration, identified by endpoint.name. If an endpoint with that name already exists it is fully replaced. Other endpoints are preserved.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration"},endpoint:{type:"object",description:"Full endpoint definition",properties:{name:{type:"string",description:"Unique name within this integration (used as tool name suffix)"},method:{type:"string",description:"HTTP method: GET, POST, PUT or DELETE"},path:{type:"string",description:"Path relative to baseUrl, e.g. /users/{userId}"},description:{type:"string",description:"Description exposed to the AI as tool description"},params:{type:"array",description:"List of parameters",items:{type:"object",properties:{name:{type:"string"},type:{type:"string",description:"string | number | boolean"},description:{type:"string"},required:{type:"boolean"},location:{type:"string",description:"path | query | body (default: query)"}}}},keepPaths:{type:"array",items:{type:"string"},description:"Dot-notation paths to keep in response"},ignorePaths:{type:"array",items:{type:"string"},description:"Dot-notation paths to remove from response"},responseFormat:{type:"string",description:"json (default) or yaml"}}}},required:["integrationName","endpoint"]},parse:JSON.parse,function:async({integrationName:n,endpoint:i})=>{let o=this.getProjectHttpIntegrations()[n];if(!o)return{error:`Integration '${n}' not found`};let c=[...(o.http?.endpoints??[]).filter(l=>l.name!==i.name),i],u={...o,http:{...o.http??{baseUrl:""},endpoints:c}};return await this.integrationConfig.saveIntegration(n,u,"project"),{success:!0,integrationName:n,endpointName:i.name}}}}}buildDeleteEndpoint(){return{type:"function",function:{name:`${this.name}__delete_endpoint`,description:"Remove a specific endpoint from an HTTP integration. Other endpoints are preserved.",parameters:{type:"object",properties:{integrationName:{type:"string",description:"Name of the integration"},endpointName:{type:"string",description:"Name of the endpoint to remove"}},required:["integrationName","endpointName"]},parse:JSON.parse,function:async({integrationName:n,endpointName:i})=>{let o=this.getProjectHttpIntegrations()[n];if(!o)return{error:`Integration '${n}' not found`};let s=o.http?.endpoints??[];if(!s.find(u=>u.name===i))return{error:`Endpoint '${i}' not found in '${n}'`};let c={...o,http:{...o.http??{baseUrl:""},endpoints:s.filter(u=>u.name!==i)}};return await this.integrationConfig.saveIntegration(n,c,"project"),{success:!0,integrationName:n,endpointName:i}}}}}};var T7e=ur(Pc(),1);var T9=class extends Vt{constructor(t,n,i,a){super(t,i,a);this.userService=n}static TYPE="HTTP";oauth=null;async handleOAuthCallback(t){this.oauth&&await this.oauth.handleCallback(t)}async buildTools(t,n){let i=this.config?.oauth2;if(!i?.client_id||!i?.client_secret||!i?.redirect_uri)return this.interactor.debug(`[HTTP:${this.name}] missing oauth2 fields: client_id=${!!i?.client_id}, client_secret=${!!i?.client_secret}, redirect_uri=${!!i?.redirect_uri}`),[];let a=this.config?.http?.baseUrl;if(!a)return this.interactor.debug(`[HTTP:${this.name}] missing http.baseUrl`),[];if(!i.authorization_endpoint||!i.token_endpoint)return this.interactor.debug(`[HTTP:${this.name}] missing oauth2 endpoints: authorization_endpoint=${i.authorization_endpoint}, token_endpoint=${i.token_endpoint}`),[];let o=this.config?.http?.endpoints;if(!o?.length)return this.interactor.debug(`[HTTP:${this.name}] no endpoints configured`),[];let s=o.map(c=>`${c.name}:${c.method} ${c.path} (${c.params?.length??0} params${c.keepPaths?", keepPaths["+c.keepPaths.length+"]":""}${c.ignorePaths?", ignorePaths["+c.ignorePaths.length+"]":""}${c.responseFormat?", "+c.responseFormat:""})`).join(" | ");return this.interactor.debug(`[HTTP:${this.name}] baseUrl=${a} | endpoints: ${s}`),this.oauth||(this.oauth=new cT({clientId:i.client_id,clientSecret:i.client_secret,redirectUri:i.redirect_uri,authorizationEndpoint:i.authorization_endpoint,tokenEndpoint:i.token_endpoint,scope:i.scope},this.interactor,this.userService,t.project.name,this.name)),o.map(c=>this.buildTool(c,a))}buildTool(t,n){let i=t.params??[],a={},o=[];for(let c of i)a[c.name]={type:c.type,description:c.description},c.required&&o.push(c.name);return{type:"function",function:{name:`${this.name}__${t.name}`,description:t.description,parameters:{type:"object",properties:a,...o.length?{required:o}:{}},parse:JSON.parse,function:async c=>this.executeEndpoint(t,n,c,i)}}}async executeEndpoint(t,n,i,a){this.interactor.debug(`[HTTP:${this.name}__${t.name}] args=${JSON.stringify(i)}`),this.interactor.debug(`[HTTP:${this.name}__${t.name}] oauth=${!!this.oauth}, isAuthenticated=${this.oauth?.isAuthenticated()}`);let o;try{this.oauth.isAuthenticated()||this.oauth.hasRefreshToken()?o=await this.oauth.getAccessToken():(this.interactor.debug(`[HTTP:${this.name}__${t.name}] no valid token or refresh_token, starting OAuth flow`),await this.oauth.authenticate(),o=await this.oauth.getAccessToken())}catch(p){throw new Error(`Authentication failed for ${this.name}: ${p.message}`)}this.interactor.debug(`[HTTP:${this.name}__${t.name}] got access token (length=${o.length})`);let s=t.path;for(let p of a.filter(d=>d.location==="path")){let d=i[p.name];d!==void 0&&(s=s.replace(`{${p.name}}`,encodeURIComponent(String(d))))}let c=new URL(`${n}${s}`);for(let p of a.filter(d=>!d.location||d.location==="query")){let d=i[p.name];d!==void 0&&c.searchParams.set(p.name,String(d))}let u=a.filter(p=>p.location==="body"),l=u.length>0?JSON.stringify(Object.fromEntries(u.filter(p=>i[p.name]!==void 0).map(p=>[p.name,i[p.name]]))):void 0;this.interactor.debug(`[HTTP:${this.name}__${t.name}] ${t.method} ${c.toString()}`);let f=await fetch(c.toString(),{method:t.method,headers:{Authorization:`Bearer ${o}`,...l?{"Content-Type":"application/json"}:{}},...l?{body:l}:{}});if(!f.ok){if(f.status===401||f.status===403){this.interactor.debug(`[HTTP:${this.name}__${t.name}] got ${f.status}, re-authenticating`),await this.oauth.authenticate();let d=await this.oauth.getAccessToken(),m=await fetch(c.toString(),{method:t.method,headers:{Authorization:`Bearer ${d}`,...l?{"Content-Type":"application/json"}:{}},...l?{body:l}:{}});if(!m.ok){let h=await m.text();throw new Error(`HTTP ${m.status} from ${t.method} ${s} (after re-auth): ${h}`)}return this.parseResponse(m,t)}let p=await f.text();throw new Error(`HTTP ${f.status} from ${t.method} ${s}: ${p}`)}return this.parseResponse(f,t)}async parseResponse(t,n){let i;if((t.headers.get("content-type")??"").includes("application/json"))i=await t.json();else{let u=await t.text();if(!u)return null;try{i=JSON.parse(u)}catch{i=u}}let o=JSON.stringify(i).length,s=Y9t(i,n.keepPaths,n.ignorePaths),c=JSON.stringify(s).length;return n.keepPaths?.length||n.ignorePaths?.length?this.interactor.debug(`[HTTP:${this.name}__${n.name}] response filtered ${o} \u2192 ${c} chars`):this.interactor.debug(`[HTTP:${this.name}__${n.name}] response ${o} chars (no filter)`),n.responseFormat==="yaml"?T7e.stringify(s):s}};function Y9t(r,e,t){return!e?.length&&!t?.length?r:e?.length?xG(r,e):wG(r,t)}function xG(r,e){if(Array.isArray(r))return r.map(t=>xG(t,e));if(r!==null&&typeof r=="object"){let t={};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let o=r,s=i==="*"?Object.keys(o):[i];for(let c of s){let u=o[c];if(u!==void 0)if(a.length===0)t[c]=u;else{let l=xG(u,[a.join(".")]);c in t&&t[c]!==null&&typeof t[c]=="object"&&!Array.isArray(t[c])?t[c]={...t[c],...l}:t[c]=l}}}return t}return r}function wG(r,e){if(Array.isArray(r))return r.map(t=>wG(t,e));if(r!==null&&typeof r=="object"){let t={...r};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let o=i==="*"?Object.keys(t):[i];for(let s of o)s in t&&(a.length===0?delete t[s]:t[s]=wG(t[s],[a.join(".")]))}return t}return r}var uT=class{constructor(e,t,n,i){this.interactor=e;this.services=t;this.mcpConfigs=t.mcp.getMergedConfiguration().servers,this.factoryConstructors=new Map,this.factoryConstructors.set(O8.TYPE,a=>new O8(e,a,{},this.services.options?.baseUrl)),this.factoryConstructors.set(eg.TYPE,a=>new eg(e,i,a,{})),this.factoryConstructors.set(tg.TYPE,a=>new tg(e,n,i,a,{},t.thread)),this.factoryConstructors.set(ng.TYPE,(a,o)=>new ng(e,a,o)),this.factoryConstructors.set(R8.TYPE,a=>new R8(e,a,{})),this.factoryConstructors.set(j8.TYPE,a=>new j8(e,t.memory,a,{})),this.factoryConstructors.set($8.TYPE,a=>new $8(e,t.thread,a,{})),this.factoryConstructors.set(F8.TYPE,(a,o)=>new F8(e,t.integration,a,o)),this.factoryConstructors.set(M8.TYPE,(a,o)=>new M8(e,t.integration,a,o,t.projectService)),this.factoryConstructors.set(e9.TYPE,(a,o)=>new e9(e,t.integration,a,o)),this.factoryConstructors.set(x9.TYPE,(a,o)=>new x9(e,t.integration,a,o)),this.factoryConstructors.set(w9.TYPE,(a,o)=>new w9(e,t.integration,a,o)),this.factoryConstructors.set(_9.TYPE,(a,o)=>new _9(e,t.integration,a,o)),this.factoryConstructors.set(E9.TYPE,(a,o)=>new E9(e,t.integration,a,o)),this.factoryConstructors.set(A9.TYPE,(a,o)=>new A9(e,t.integration,t.user,a,o)),this.factoryConstructors.set(T9.TYPE,(a,o)=>new T9(e,t.user,a,o)),this.factoryConstructors.set(C9.TYPE,()=>new C9(e,t.integrationConfig)),this.factoryConstructors.set(N8.TYPE,a=>new N8(e,a))}factoryConstructors;factoryInstances=new Map;mcpConfigs;tools=[];async kill(){console.log("[TOOLBOX] Closing tool factories"),await Promise.all(Array.from(this.factoryInstances.values()).map(e=>e.kill())),console.log("[TOOLBOX] Closed tool factories")}async getTools(e){let{context:t,integrations:n,agentName:i}=e,a=t.aiThread?.id;a||this.interactor.warn("No thread ID in context, MCP tools will not be available");let o=[];if(n)for(let[c]of n){let u=this.factoryInstances.get(c)??this.createFactory(c);u&&o.push(u)}else{let c=new Set(Object.values(this.services.integration.integrations).map(l=>l?.type).filter(Boolean)),u=new Set([...Array.from(this.factoryConstructors.keys()).filter(l=>!c.has(l)),...Object.keys(this.services.integration.integrations)]);for(let l of u){let f=this.factoryInstances.get(l)??this.createFactory(l);f&&o.push(f)}}let s=[];if(a){for(let c of this.mcpConfigs)if(!(n&&!n.has(c.name)))try{let u=await this.services.mcpPool.getOrCreateFactory(c,a,()=>new zA(c));s.push(u)}catch(u){this.interactor.debug(`Error creating MCP factory for ${c.name}: ${u}`)}}o.push(...s);try{let c=await Promise.all(o.map(async u=>{try{return await u.getTools(t,n?.get(u.name)??[],i??"default")}catch(l){return this.interactor.debug(`Error building tools from ${u.name} for agent ${i}: ${l}`),[]}}));return this.tools=c.flat(),this.tools}catch(c){return this.interactor.debug(`Unexpected error building tools for agent ${i}: ${c}`),[]}}createFactory(e){let t=this.services.integration.integrations,n=t[e]??{},i=t[e]?.type,a=i?this.factoryConstructors.get(i):void 0,o=this.factoryConstructors.get(e),s=a??o,c=a?i:e;if(!s){this.interactor.debug(`No factory constructor for integration '${e}' (type='${i??"none"}')`);return}try{let u=s(e,n);return this.factoryInstances.set(e,u),this.interactor.debug(`Created integration factory '${e}' of type '${c}'`),u}catch(u){this.interactor.debug(`Error creating factory for '${e}': ${u}`);return}}async handleOAuthCallback(e){let t=this.factoryInstances.get(e.integrationName)??this.createFactory(e.integrationName);if(!t){this.interactor.warn(`No integration found for OAuth callback: ${e.integrationName}`);return}if("handleOAuthCallback"in t&&typeof t.handleOAuthCallback=="function")try{await t.handleOAuthCallback(e)}catch(n){this.interactor.error(`Error handling OAuth callback for ${e.integrationName}: ${n}`)}else this.interactor.warn(`Integration ${e.integrationName} does not support OAuth callbacks`)}};var Z1=class{constructor(e,t,n,i,a=[]){this.interactor=e;this.aiClientProvider=t;this.services=n;this.projectPath=i;this.commandLineAgentFolders=a;this.services.project.selectedProject$.subscribe(()=>{this.agentCache.clear(),this.agentDefinitions=[]}),this.toolbox=new uT(this.interactor,n,this.findAgentByNameStart,this.listAgentSummaries)}agentCache=new Map;agentDefinitions=[];toolbox;listAgentSummaries=()=>this.agentDefinitions.map(e=>({name:e.definition.name,description:e.definition.description})).sort((e,t)=>e.name.toLowerCase().localeCompare(t.name.toLowerCase()));initialize=async e=>{if(this.agentDefinitions.length>0)return;let t=performance.now();this.interactor.debug("\u{1F680} Starting agent initialization...");try{let i=performance.now();if(e.project.agents?.length)for(let d of e.project.agents)this.addDefinition(d,this.projectPath);let a=performance.now()-i;this.interactor.debug(`\u{1F4CB} Loaded agent definitions from coday.yml: ${a.toFixed(2)}ms (${e.project.agents?.length??0} agents)`);let o=performance.now(),s=this.services.project.selectedProject;if(s?.config.agents?.length)for(let d of s.config.agents)this.addDefinition(d,this.projectPath);let c=performance.now()-o;this.interactor.debug(`\u2699\uFE0F Loaded agent definitions from project local config: ${c.toFixed(2)}ms (${s?.config.agents?.length??0} agents)`);let u=performance.now();await this.loadFromFiles(e);let l=performance.now()-u;this.interactor.debug(`\u{1F4C1} Loaded agent definitions from files: ${l.toFixed(2)}ms`),this.agentDefinitions.length===0&&(this.addDefinition(Ox,this.projectPath),this.interactor.debug("\u{1F504} No agent definitions found, using Coday as backup"));let f=performance.now();this.generateVirtualAgentsFromModels();let p=performance.now()-f;this.interactor.debug(`\u{1F916} Generated virtual agents from models: ${p.toFixed(2)}ms`)}catch(i){throw this.interactor.error(`Failed to initialize agent definitions: ${i}`),i}let n=performance.now()-t;this.interactor.debug(`\u{1F3AF} Total agent definition loading time: ${n.toFixed(2)}ms`)};async findByName(e,t){await this.initialize(t);let n=e.toLowerCase();if(this.agentCache.has(n))return this.agentCache.get(n);let i=this.agentDefinitions.find(a=>a.definition.name.toLowerCase()===n);if(i){let a=await this.tryAddAgent(i,t);if(a)return this.agentCache.set(n,a),a}}findAgentByNameStart=async(e,t)=>{if(!e)return;await this.initialize(t);let n=await this.findAgentsByNameStart(e?.toLowerCase()||"",t);if(n.length===0)return;if(n.length===1)return n[0];let i=n.map(a=>a.name);try{if(t.oneshot)return;let a=await this.interactor.chooseOption(i,`Multiple agents match '${e}', please select one:`);return n.find(o=>o.name===a)}catch{this.interactor.error("Selection cancelled");return}};async findAgentsByNameStart(e,t){await this.initialize(t);let n=e.toLowerCase(),i=this.agentDefinitions.filter(o=>o.definition.name.toLowerCase().startsWith(n)),a=[];for(let o of i){let s=o.definition.name.toLowerCase(),c=this.agentCache.get(s);c||(c=await this.tryAddAgent(o,t),c&&this.agentCache.set(s,c)),c&&a.push(c)}return a}getPreferredAgent(){let e=this.services.project.selectedProject?.name;return e?this.services.user.config.projects?.[this.services.user.resolveProjectName(e)]?.defaultAgent:void 0}async kill(){this.aiClientProvider.kill(),await this.toolbox.kill()}addDefinition(e,t=this.projectPath){this.agentDefinitions.find(n=>n.definition.name===e.name)||this.agentDefinitions.push({definition:{...Ox,...e},basePath:t})}generateVirtualAgentsFromModels(){let e=this.aiClientProvider.getAllModels();for(let t of e){let n={name:t.name,description:`Direct access to ${t.name} model (${t.providerName})`,instructions:"",aiProvider:t.providerName,modelName:t.name};this.addDefinition(n,this.projectPath)}this.interactor.debug(`\u{1F916} Generated ${e.length} virtual agents from available models: ${e.map(t=>t.name).join(", ")}`)}async loadFromFiles(e){let t=[],n=[],i=this.services.project.selectedProject;i&&t.push(Md.join(i.configPath,"agents"));let a=this.services.project.selectedProject?.config.path;if(a){let l=await f2({text:"coday.yaml",root:a});if(l.length>0){let f=Md.dirname(l[0]);t.push(Md.join(a,f,"agents"))}e.project.agentFolders?.length&&t.push(...e.project.agentFolders)}t.push(...this.commandLineAgentFolders);let o=performance.now();await Promise.all(t.map(async l=>{try{n.push(...(await lT.readdir(l)).filter(f=>f.endsWith(".yml")||f.endsWith(".yaml")).map(f=>Md.join(l,f)))}catch(f){f.code==="EPERM"?console.error(`Permission denied to access ${l}. This is common for protected directories.
1396
1396
  Consider moving your agent files to a less restricted location.`):console.error(`Could not read directory ${l}: ${f.code}`)}}));let s=performance.now()-o;this.interactor.debug(` \u{1F4C2} Scanned agent directories: ${s.toFixed(2)}ms (found ${n.length} files)`);let c=performance.now();await Promise.all(n.map(async l=>{try{let f=await lT.readFile(l,"utf-8"),p=P7e.parse(f),d=Md.dirname(l),h=d.startsWith(this.projectPath)?this.projectPath:d;this.addDefinition(p,h)}catch(f){console.error(f)}}));let u=performance.now()-c;this.interactor.debug(` \u{1F4DD} Parsed agent files: ${u.toFixed(2)}ms`)}async tryAddAgent(e,t){let n={...Ox,...e.definition},i=performance.now();try{if(n.openaiAssistantId&&(n.aiProvider="openai"),!this.aiClientProvider||!this.toolbox){console.error(`Cannot create agent ${n.name}: dependencies not set. Call setDependencies first.`);return}this.interactor.debug(`\u{1F3D7}\uFE0F Creating agent '${n.name}' on-demand...`);let a=performance.now(),o=this.aiClientProvider.getClient(n.aiProvider,n.modelName);if(!o){this.interactor.error(`Cannot create agent ${n.name}: AI client creation failed`);return}let s=performance.now()-a,c=e.basePath,u=performance.now(),l=await e_(n,this.interactor,c,n.name),f=performance.now()-u;n.instructions=`${n.instructions}
1397
1397
 
1398
1398