@whoz-oss/coday-server 0.201.0 → 0.202.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.201.0",
3
+ "version": "0.202.0",
4
4
  "repository": "https://github.com/whoz-oss/coday",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
package/server.js CHANGED
@@ -1622,7 +1622,7 @@ ${m.map(b=>{let w=(b.assignees||[]).map(T=>T.name).join(", ")||"Unassigned",k=b.
1622
1622
 
1623
1623
  ${p}${g}`;return d>0&&(v+=`
1624
1624
 
1625
- _${d} comment(s) available \u2014 use getComments with recording ID ${a.id} to retrieve them._`),v}catch(r){return`Error: ${r.message}`}}var yE=class extends xr{constructor(r,n,i,a,s){super(r,a,s);this.integrationService=n;this.userService=i}static TYPE="BASECAMP";oauth=null;async handleOAuthCallback(r){this.oauth&&await this.oauth.handleCallback(r)}async buildTools(r){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,s=i.apiKey,o=i.oauth2?.redirect_uri;if(!o)return this.interactor.displayText("Basecamp integration requires redirectUri"),n;if(!a||!s)return this.interactor.displayText("Basecamp integration requires clientId (username) and clientSecret (apiKey)"),n;let c=r.project.name;this.oauth=new DL(a,s,o,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:w})=>dHe(this.oauth,w)}};n.push(u);let f={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:w})=>pHe(this.oauth,w)}};n.push(f);let d={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:w,messageBoardId:k,page:T})=>hHe(this.oauth,w,k,T)}};n.push(d);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:w,messageId:k})=>mHe(this.oauth,w,k)}};n.push(p);let h={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:w,page:k})=>gHe(this.oauth,w,k)}};n.push(h);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:w,inboxId:k,page:T})=>vHe(this.oauth,w,k,T)}};n.push(m);let g={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:w,forwardId:k})=>yHe(this.oauth,w,k)}};n.push(g);let v={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:w})=>bHe(this.oauth,w)}};n.push(v);let y={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:w,page:k})=>xHe(this.oauth,w,k)}};n.push(y);let b={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:w})=>wHe(this.oauth,w)}};return n.push(b),n}};var RL=class{constructor(e,r,n,i,a){this.config=e;this.interactor=r;this.userService=n;this.integrationName=a;if(this.resolvedProjectName=n.resolveProjectName(i),e.authorizationEndpoint&&e.tokenEndpoint){let s=new URL(e.authorizationEndpoint);this.as={issuer:`${s.origin}${s.pathname.split("/").slice(0,-1).join("/")}`,authorization_endpoint:e.authorizationEndpoint,token_endpoint:e.tokenEndpoint}}this.client={client_id:e.clientId},this.clientAuth=CL(e.clientSecret)}as=null;client;clientAuth;tokenData=null;pendingState=null;pendingCodeVerifier=null;pendingResolve=null;pendingReject=null;pendingAuthPromise=null;resolvedProjectName;validateDiscoveryUrl(e,r){let n;try{n=new URL(e)}catch{throw new Error(`[OAuth:${this.integrationName}] ${r} is not a valid URL: ${e}`)}if(n.protocol!=="https:")throw new Error(`[OAuth:${this.integrationName}] ${r} must use HTTPS (got "${n.protocol}"): ${e}`);let i=n.hostname;if([/^localhost$/i,/^127\./,/^10\./,/^172\.(1[6-9]|2[0-9]|3[01])\./,/^192\.168\./,/^169\.254\./,/^\[?::1\]?$/,/^\[?fc00:/i,/^\[?fd[0-9a-f]{2}:/i].some(s=>s.test(i)))throw new Error(`[OAuth:${this.integrationName}] ${r} resolves to a private/loopback address and is not allowed: ${e}`);return n}async resolveAuthorizationServer(){if(this.as)return this.as;let{issuer:e,discoveryUrl:r}=this.config;if(e){this.validateDiscoveryUrl(e,"issuer"),this.interactor.debug(`[OAuth:${this.integrationName}] resolving endpoints via discovery for issuer=${e}`);let n=new URL(e),i;try{i=await Tle(n,{algorithm:"oidc"})}catch{this.interactor.debug(`[OAuth:${this.integrationName}] OIDC discovery failed, falling back to RFC 8414 for issuer=${e}`),i=await Tle(n,{algorithm:"oauth2"})}let a=await eHe(n,i);return this.interactor.debug(`[OAuth:${this.integrationName}] discovery succeeded: authorization_endpoint=${a.authorization_endpoint}, token_endpoint=${a.token_endpoint}`),this.as=a,this.as}if(r){let n=this.validateDiscoveryUrl(r,"discoveryUrl");this.interactor.debug(`[OAuth:${this.integrationName}] fetching discovery document from explicit URL: ${r}`);let i=await fetch(r);if(!i.ok)throw new Error(`[OAuth:${this.integrationName}] failed to fetch discovery document from ${r}: HTTP ${i.status}`);let a=await i.json();if(!a.authorization_endpoint||!a.token_endpoint)throw new Error(`[OAuth:${this.integrationName}] discovery document at ${r} is missing authorization_endpoint or token_endpoint`);let s=n.origin;for(let[o,c]of[["authorization_endpoint",a.authorization_endpoint],["token_endpoint",a.token_endpoint]]){let u;try{u=new URL(c).origin}catch{throw new Error(`[OAuth:${this.integrationName}] discovery document field "${o}" is not a valid URL: ${c}`)}if(u!==s)throw new Error(`[OAuth:${this.integrationName}] discovery document field "${o}" (${c}) does not share the same origin as the discovery URL (${s}). This may indicate a tampered or malicious discovery document.`)}return this.as={...a,issuer:a.issuer??n.origin},this.as}throw new Error(`[OAuth:${this.integrationName}] OAuth configuration error: provide either authorizationEndpoint + tokenEndpoint, or issuer, or discoveryUrl`)}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(),r=e>300*1e3;return this.interactor.debug(`[OAuth:${this.integrationName}] token expiresAt=${new Date(this.tokenData.expiresAt).toISOString()}, remainingMs=${e}, valid=${r}`),r}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,r)=>{this.pendingResolve=e,this.pendingReject=r}),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=await this.resolveAuthorizationServer(),r=_L(),n=wL(),i=await SL(n);this.pendingState=r,this.pendingCodeVerifier=n;let a=new URL(e.authorization_endpoint);if(a.searchParams.set("client_id",this.client.client_id),a.searchParams.set("redirect_uri",this.config.redirectUri),a.searchParams.set("response_type","code"),a.searchParams.set("state",r),a.searchParams.set("code_challenge",i),a.searchParams.set("code_challenge_method","S256"),a.searchParams.set("access_type","offline"),a.searchParams.set("prompt","consent"),this.config.scope){let s=Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope;a.searchParams.set("scope",s)}this.interactor.debug(`[OAuth:${this.integrationName}] emitting OAuthRequestEvent, authUrl=${a.toString()}`),this.interactor.sendEvent(new mf({authUrl:a.toString(),state:r,integrationName:this.integrationName})),this.interactor.debug(`[OAuth:${this.integrationName}] waiting for OAuth callback...`)}async handleCallback(e){if(e.integrationName!==this.integrationName)return;if(e.state!==this.pendingState){let n=new Error("Invalid OAuth state");this.interactor.error(n.message),this.rejectPending(n);return}if(e.error){let n=e.error==="access_denied"?"OAuth authentication denied by user":`OAuth error: ${e.error}${e.errorDescription?" - "+e.errorDescription:""}`;this.interactor.warn(n),this.rejectPending(new Error(n));return}if(!e.code||!this.pendingCodeVerifier||!this.pendingState){let n=new Error("No pending OAuth flow or missing code");this.interactor.error(n.message),this.rejectPending(n);return}let r=this.as;try{let n=new URL(this.config.redirectUri);n.searchParams.set("code",e.code),n.searchParams.set("state",e.state);let i=IL(r,this.client,n,this.pendingState),a=await OL(r,this.client,this.clientAuth,i,this.config.redirectUri,this.pendingCodeVerifier),s=await PL(r,this.client,a);this.tokenData={accessToken:s.access_token,refreshToken:s.refresh_token,expiresAt:Date.now()+(s.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(n){this.interactor.error(`OAuth token exchange failed: ${n.message}`),this.rejectPending(n)}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");let e=await this.resolveAuthorizationServer();try{let r=await TL(e,this.client,this.clientAuth,this.tokenData.refreshToken),n=await AL(e,this.client,r);this.tokenData={accessToken:n.access_token,refreshToken:n.refresh_token??this.tokenData.refreshToken,expiresAt:Date.now()+(n.expires_in??3600)*1e3},this.saveTokensToStorage()}catch(r){throw this.tokenData=null,this.clearTokensFromStorage(),new Error(`Token refresh failed: ${r.message}`)}}loadTokensFromStorage(){let e=this.userService.config.projects,r=e?.[this.resolvedProjectName],n=r?.integration?.[this.integrationName],i=n?.oauth2?.tokens;this.interactor.debug(`[OAuth:${this.integrationName}] loadTokensFromStorage: projectName=${this.resolvedProjectName}, hasProjects=${!!e}, hasProjectConfig=${!!r}, 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 r=e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2;r.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 _He=["client_secret","tokens"],SHe=["apiKey","username"],bE=class t extends xr{constructor(r,n){super(r,t.TYPE,{});this.integrationConfig=n}static TYPE="HTTP_CONFIG";async buildTools(r){return Hv(r.username)?[this.buildLetUserFillAuth(),this.buildListHttpIntegrations(),this.buildGetHttpIntegration(),this.buildUpsertHttpIntegration(),this.buildDeleteHttpIntegration(),this.buildGetEndpoint(),this.buildUpsertEndpoint(),this.buildDeleteEndpoint()]:(this.interactor.debug(`[HTTP_CONFIG] user '${r.username}' is not CODAY_ADMIN, tools not available`),[])}getProjectHttpIntegrations(){let r=this.integrationConfig.getUnmaskedIntegrationsAtLevel("project");return Object.fromEntries(Object.entries(r).filter(([,n])=>n.type==="HTTP"))}sanitizeForAgent(r){let n={...r};for(let i of SHe)delete n[i];if(n.oauth2&&typeof n.oauth2=="object"){let i={...n.oauth2};for(let a of _He)delete i[a];n.oauth2=i}return n}mergeIgnoringSensitive(r,n){let i={...r};for(let[a,s]of Object.entries(n))if(!SHe.includes(a))if(a==="oauth2"&&s&&typeof s=="object"){let o=r.oauth2??{},c=s,u={...o};for(let[f,d]of Object.entries(c))_He.includes(f)||(u[f]=d);i.oauth2=u}else a!=="http"&&(i[a]=s);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 s=a.oauth2??{},o=await this.interactor.promptSecretText(`OAuth2 client_id for '${n}' (leave as-is to keep current):`,s.client_id),c=await this.interactor.promptSecretText(`OAuth2 client_secret for '${n}' (leave as-is to keep current):`,s.client_secret);if(o===void 0&&c===void 0)return{success:!1,message:"No values provided, nothing was saved."};let u={...s,...o!==void 0&&{client_id:o},...c!==void 0&&{client_secret:c}},f={...a,oauth2:u};return await this.integrationConfig.saveIntegration(n,f,"project"),{success:!0,updated:[...o!==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(s=>s.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(s=>s.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, issuer, discovery_url, 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"},issuer:{type:"string",description:"OAuth2/OIDC issuer URL for automatic endpoint discovery (RFC 8414 / OIDC). Alternative to providing authorization_endpoint + token_endpoint explicitly."},discovery_url:{type:"string",description:"Explicit discovery document URL when the discovery endpoint is non-standard (alternative to issuer)."},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 o=this.getProjectHttpIntegrations()[n]??{type:"HTTP"},c={};a&&(c.oauth2=a);let u=this.mergeIgnoringSensitive(o,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 s=this.getProjectHttpIntegrations()[n];if(!s)return{error:`Integration '${n}' not found`};let o=(s.http?.endpoints??[]).find(c=>c.name===i);return o||{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 s=this.getProjectHttpIntegrations()[n];if(!s)return{error:`Integration '${n}' not found`};let c=[...(s.http?.endpoints??[]).filter(f=>f.name!==i.name),i],u={...s,http:{...s.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 s=this.getProjectHttpIntegrations()[n];if(!s)return{error:`Integration '${n}' not found`};let o=s.http?.endpoints??[];if(!o.find(u=>u.name===i))return{error:`Endpoint '${i}' not found in '${n}'`};let c={...s,http:{...s.http??{baseUrl:""},endpoints:o.filter(u=>u.name!==i)}};return await this.integrationConfig.saveIntegration(n,c,"project"),{success:!0,integrationName:n,endpointName:i}}}}}};var EHe=Kr(Tf(),1);var xE=class extends xr{constructor(r,n,i,a){super(r,i,a);this.userService=n}static TYPE="HTTP";oauth=null;async handleOAuthCallback(r){this.oauth&&await this.oauth.handleCallback(r)}async buildTools(r,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`),[];let s=i.authorization_endpoint&&i.token_endpoint,o=i.issuer||i.discovery_url;if(!s&&!o)return this.interactor.debug(`[HTTP:${this.name}] missing oauth2 endpoint configuration: provide authorization_endpoint + token_endpoint, or issuer, or discovery_url`),[];let c=this.config?.http?.endpoints;if(!c?.length)return this.interactor.debug(`[HTTP:${this.name}] no endpoints configured`),[];let u=c.map(f=>`${f.name}:${f.method} ${f.path} (${f.params?.length??0} params${f.keepPaths?", keepPaths["+f.keepPaths.length+"]":""}${f.ignorePaths?", ignorePaths["+f.ignorePaths.length+"]":""}${f.responseFormat?", "+f.responseFormat:""})`).join(" | ");return this.interactor.debug(`[HTTP:${this.name}] baseUrl=${a} | endpoints: ${u}`),this.oauth||(this.oauth=new RL({clientId:i.client_id,clientSecret:i.client_secret,redirectUri:i.redirect_uri,authorizationEndpoint:i.authorization_endpoint,tokenEndpoint:i.token_endpoint,issuer:i.issuer,discoveryUrl:i.discovery_url,scope:i.scope},this.interactor,this.userService,r.project.name,this.name)),c.map(f=>this.buildTool(f,a))}buildTool(r,n){let i=r.params??[],a={},s=[];for(let c of i)a[c.name]={type:c.type,description:c.description},c.required&&s.push(c.name);return{type:"function",function:{name:`${this.name}__${r.name}`,description:r.description,parameters:{type:"object",properties:a,...s.length?{required:s}:{}},parse:JSON.parse,function:async c=>this.executeEndpoint(r,n,c,i)}}}async executeEndpoint(r,n,i,a){this.interactor.debug(`[HTTP:${this.name}__${r.name}] args=${JSON.stringify(i)}`),this.interactor.debug(`[HTTP:${this.name}__${r.name}] oauth=${!!this.oauth}, isAuthenticated=${this.oauth?.isAuthenticated()}`);let s;try{this.oauth.isAuthenticated()||this.oauth.hasRefreshToken()?s=await this.oauth.getAccessToken():(this.interactor.debug(`[HTTP:${this.name}__${r.name}] no valid token or refresh_token, starting OAuth flow`),await this.oauth.authenticate(),s=await this.oauth.getAccessToken())}catch(p){throw new Error(`Authentication failed for ${this.name}: ${p.message}`)}this.interactor.debug(`[HTTP:${this.name}__${r.name}] got access token (length=${s.length})`);let o=r.path;for(let p of a.filter(h=>h.location==="path")){let h=i[p.name];h!==void 0&&(o=o.replace(`{${p.name}}`,encodeURIComponent(String(h))))}let c=new URL(`${n}${o}`);for(let p of a.filter(h=>!h.location||h.location==="query")){let h=i[p.name];h!==void 0&&c.searchParams.set(p.name,String(h))}let u=a.filter(p=>p.location==="body"),f=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}__${r.name}] ${r.method} ${c.toString()}`);let d=await fetch(c.toString(),{method:r.method,headers:{Authorization:`Bearer ${s}`,...f?{"Content-Type":"application/json"}:{}},...f?{body:f}:{}});if(!d.ok){if(d.status===401||d.status===403){this.interactor.debug(`[HTTP:${this.name}__${r.name}] got ${d.status}, re-authenticating`),await this.oauth.authenticate();let h=await this.oauth.getAccessToken(),m=await fetch(c.toString(),{method:r.method,headers:{Authorization:`Bearer ${h}`,...f?{"Content-Type":"application/json"}:{}},...f?{body:f}:{}});if(!m.ok){let g=await m.text();throw new Error(`HTTP ${m.status} from ${r.method} ${o} (after re-auth): ${g}`)}return this.parseResponse(m,r)}let p=await d.text();throw new Error(`HTTP ${d.status} from ${r.method} ${o}: ${p}`)}return this.parseResponse(d,r)}async parseResponse(r,n){let i;if((r.headers.get("content-type")??"").includes("application/json"))i=await r.json();else{let u=await r.text();if(!u)return null;try{i=JSON.parse(u)}catch{i=u}}let s=JSON.stringify(i).length,o=Ifr(i,n.keepPaths,n.ignorePaths),c=JSON.stringify(o).length;return n.keepPaths?.length||n.ignorePaths?.length?this.interactor.debug(`[HTTP:${this.name}__${n.name}] response filtered ${s} \u2192 ${c} chars`):this.interactor.debug(`[HTTP:${this.name}__${n.name}] response ${s} chars (no filter)`),n.responseFormat==="yaml"?EHe.stringify(o):o}};function Ifr(t,e,r){return!e?.length&&!r?.length?t:e?.length?jle(t,e):Nle(t,r)}function jle(t,e){if(Array.isArray(t))return t.map(r=>jle(r,e));if(t!==null&&typeof t=="object"){let r={};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let s=t,o=i==="*"?Object.keys(s):[i];for(let c of o){let u=s[c];if(u!==void 0)if(a.length===0)r[c]=u;else{let f=jle(u,[a.join(".")]);c in r&&r[c]!==null&&typeof r[c]=="object"&&!Array.isArray(r[c])?r[c]={...r[c],...f}:r[c]=f}}}return r}return t}function Nle(t,e){if(Array.isArray(t))return t.map(r=>Nle(r,e));if(t!==null&&typeof t=="object"){let r={...t};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let s=i==="*"?Object.keys(r):[i];for(let o of s)o in r&&(a.length===0?delete r[o]:r[o]=Nle(r[o],[a.join(".")]))}return r}return t}import*as e4e from"path";import{existsSync as XPr}from"node:fs";var udt=Kr(cdt(),1),EC=class extends xr{static TYPE="EXCEL";constructor(e,r,n){super(e,r,n)}async buildTools(e,r){let n=[],i=u=>{if(u.startsWith("project://"))return e4e.resolve(e.project.root,u.replace("project://",""));if(u.startsWith("exchange://")){if(!e.threadFilesRoot)throw new Error("No exchange workspace available in this context");return e4e.resolve(e.threadFilesRoot,u.replace("exchange://",""))}throw new Error(`File path must start with "project://" or "exchange://". Got: "${u}"`)},a=async u=>{let f=i(u);if(!XPr(f))throw new Error(`File not found: ${u}`);let d=new udt.Workbook;return await d.xlsx.readFile(f),d},s={type:"function",function:{name:`${this.name}__listSheets`,description:'List all sheet names in an Excel file (.xlsx). Use this first to discover the structure of a workbook before reading data. File path must start with "project://" or "exchange://".',parameters:{type:"object",properties:{filePath:{type:"string",description:'Path to the Excel file (e.g. "exchange://report.xlsx" or "project://data/sales.xlsx")'}}},parse:JSON.parse,function:async({filePath:u})=>{try{let d=(await a(u)).worksheets.map(p=>p.name);return JSON.stringify({sheets:d},null,2)}catch(f){return`Error: ${f}`}}}};n.push(s);let o={type:"function",function:{name:`${this.name}__readSheet`,description:'Read the content of a sheet from an Excel file as JSON rows. If sheetName is omitted, the first sheet is used. Use maxRows to limit the number of rows returned (recommended for large files). Use startRow to paginate through large datasets (0-indexed, after the header row). Returns an array of objects where keys are column headers. File path must start with "project://" or "exchange://".',parameters:{type:"object",properties:{filePath:{type:"string",description:'Path to the Excel file (e.g. "exchange://report.xlsx")'},sheetName:{type:"string",description:"Name of the sheet to read. Defaults to the first sheet if omitted."},maxRows:{type:"number",description:"Maximum number of data rows to return (default: 100, max: 1000). Use to avoid overloading context."},startRow:{type:"number",description:"Zero-indexed row offset (after header) to start reading from. Use for pagination."}}},parse:JSON.parse,function:async({filePath:u,sheetName:f,maxRows:d=100,startRow:p=0})=>{try{let h=await a(u),m=f?h.getWorksheet(f):h.worksheets[0];if(!m){let R=h.worksheets.map(j=>j.name).join(", ");return f?`Error: sheet "${f}" not found. Available sheets: ${R}`:"Error: workbook has no sheets"}let g=m.getRow(1),v=[];g.eachCell({includeEmpty:!0},(R,j)=>{v[j-1]=R.text||`Column${j}`});let y=Math.min(d,1e3),b=2+p,w=b+y-1,k=m.rowCount-1,T=[];for(let R=b;R<=Math.min(w,m.rowCount);R++){let j=m.getRow(R);if(j.hasValues){let E={};j.eachCell({includeEmpty:!0},(A,S)=>{let C=v[S-1]??`Column${S}`;E[C]=A.text||null}),T.push(E)}}let P=b+y-1<m.rowCount;return JSON.stringify({sheet:m.name,totalRows:k,returnedRows:T.length,startRow:p,hasMore:P,...P?{nextStartRow:p+y}:{},rows:T},null,2)}catch(h){return`Error: ${h}`}}}};n.push(o);let c={type:"function",function:{name:`${this.name}__getSheetInfo`,description:'Get metadata about a sheet without loading all its data: number of rows, column headers, and a preview of the first 3 rows. Use this to understand the structure of a large sheet before deciding how to read it. File path must start with "project://" or "exchange://".',parameters:{type:"object",properties:{filePath:{type:"string",description:"Path to the Excel file"},sheetName:{type:"string",description:"Name of the sheet. Defaults to the first sheet if omitted."}}},parse:JSON.parse,function:async({filePath:u,sheetName:f})=>{try{let d=await a(u),p=f?d.getWorksheet(f):d.worksheets[0];if(!p){let v=d.worksheets.map(y=>y.name).join(", ");return f?`Error: sheet "${f}" not found. Available sheets: ${v}`:"Error: workbook has no sheets"}let h=p.getRow(1),m=[];h.eachCell({includeEmpty:!0},(v,y)=>{m[y-1]=v.text||`Column${y}`});let g=[];for(let v=2;v<=Math.min(4,p.rowCount);v++){let y=p.getRow(v);if(y.hasValues){let b={};y.eachCell({includeEmpty:!0},(w,k)=>{let T=m[k-1]??`Column${k}`;b[T]=w.text||null}),g.push(b)}}return JSON.stringify({sheet:p.name,totalRows:p.rowCount-1,columnCount:m.length,columns:m,preview:g},null,2)}catch(d){return`Error: ${d}`}}}};return n.push(c),n}async kill(){}};var UU=class t{constructor(e,r,n,i){this.interactor=e;this.services=r;this.mcpConfigs=r.mcp.getMergedConfiguration().servers,this.factoryConstructors=new Map,this.factoryConstructors.set(ES.TYPE,a=>new ES(e,a,{},this.services.options?.baseUrl)),this.factoryConstructors.set(Aw.TYPE,a=>new Aw(e,i,a,{})),this.factoryConstructors.set(Ow.TYPE,a=>new Ow(e,n,i,a,{},r.thread)),this.factoryConstructors.set(Dw.TYPE,(a,s)=>new Dw(e,a,s)),this.factoryConstructors.set(TS.TYPE,a=>new TS(e,a,{})),this.factoryConstructors.set(CS.TYPE,a=>new CS(e,r.memory,a,{})),this.factoryConstructors.set(kS.TYPE,a=>new kS(e,r.thread,a,{})),this.factoryConstructors.set(OS.TYPE,(a,s)=>new OS(e,r.integration,a,s)),this.factoryConstructors.set(PS.TYPE,(a,s)=>new PS(e,r.integration,a,s,r.projectService)),this.factoryConstructors.set(VS.TYPE,(a,s)=>new VS(e,r.integration,a,s)),this.factoryConstructors.set(dE.TYPE,(a,s)=>new dE(e,r.integration,a,s)),this.factoryConstructors.set(pE.TYPE,(a,s)=>new pE(e,r.integration,a,s)),this.factoryConstructors.set(hE.TYPE,(a,s)=>new hE(e,r.integration,a,s)),this.factoryConstructors.set(gE.TYPE,(a,s)=>new gE(e,r.integration,a,s)),this.factoryConstructors.set(yE.TYPE,(a,s)=>new yE(e,r.integration,r.user,a,s)),this.factoryConstructors.set(xE.TYPE,(a,s)=>new xE(e,r.user,a,s)),this.factoryConstructors.set(bE.TYPE,()=>new bE(e,r.integrationConfig)),this.factoryConstructors.set(AS.TYPE,a=>new AS(e,a)),this.factoryConstructors.set(EC.TYPE,(a,s)=>new EC(e,a,s))}factoryConstructors;factoryInstances=new Map;mcpConfigs;tools=[];currentThreadId;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:r,integrations:n,agentName:i}=e,a=r.aiThread?.id;a||this.interactor.warn("No thread ID in context, MCP tools will not be available");let s=new Set(this.mcpConfigs.map(u=>u.name)),o=[];if(n)for(let[u]of n){if(s.has(u))continue;let f=this.factoryInstances.get(u)??this.createFactory(u);f&&o.push(f)}else{let u=new Set(Object.values(this.services.integration.integrations).map(d=>d?.type).filter(Boolean)),f=new Set([...Array.from(this.factoryConstructors.keys()).filter(d=>!u.has(d)),...Object.keys(this.services.integration.integrations)]);for(let d of f){let p=this.factoryInstances.get(d)??this.createFactory(d);p&&o.push(p)}}let c=[];if(a){this.currentThreadId=a;for(let u of this.mcpConfigs)if(!(n&&!n.has(u.name)))try{let f=await this.services.mcpPool.getOrCreateFactory(u,a,()=>new dM(u,this.interactor,this.services.user,r.project.name,this.services.options?.baseUrl));c.push(f)}catch(f){this.interactor.debug(`Error creating MCP factory for ${u.name}: ${f}`)}}o.push(...c);try{let u=await Promise.all(o.map(async p=>{try{return await p.getTools(r,n?.get(p.name)??[],i??"default")}catch(h){return this.interactor.debug(`Error building tools from ${p.name} for agent ${i}: ${h}`),[]}})),{tools:f,duplicates:d}=t.deduplicateTools(u.flat());for(let p of d)this.interactor.warn(`Duplicate tool '${p.name}': kept source '${p.keptSource}', discarded source '${p.discardedSource}'`);return this.tools=f,this.tools}catch(u){return this.interactor.debug(`Unexpected error building tools for agent ${i}: ${u}`),[]}}createFactory(e){let r=this.services.integration.integrations,n=r[e]??{},i=r[e]?.type,a=i?this.factoryConstructors.get(i):void 0,s=this.factoryConstructors.get(e),o=a??s,c=a?i:e;if(!o){this.interactor.debug(`No factory constructor for integration '${e}' (type='${i??"none"}')`);return}try{let u=o(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}}static deduplicateTools(e){let r=new Map,n=[];for(let i of e){let a=i.function.name,s=r.get(a);s&&n.push({name:a,keptSource:t.extractSource(i),discardedSource:t.extractSource(s)}),r.set(a,i)}return{tools:Array.from(r.values()),duplicates:n}}static extractSource(e){let r=e.function.name,n=r.indexOf("__");return n>0?r.substring(0,n):r}async handleOAuthCallback(e){let r=this.factoryInstances.get(e.integrationName)??this.createFactory(e.integrationName);if(r){if("handleOAuthCallback"in r&&typeof r.handleOAuthCallback=="function")try{await r.handleOAuthCallback(e)}catch(i){this.interactor.error(`Error handling OAuth callback for ${e.integrationName}: ${i}`)}else this.interactor.warn(`Integration ${e.integrationName} does not support OAuth callbacks`);return}let n=this.mcpConfigs.find(i=>i.id===e.integrationName);if(n){let i=await this.services.mcpPool.getMcpFactory(n,this.currentThreadId);if(i&&"handleOAuthCallback"in i&&typeof i.handleOAuthCallback=="function"){try{await i.handleOAuthCallback(e)}catch(a){this.interactor.error(`Error handling OAuth callback for MCP ${e.integrationName}: ${a}`)}return}}this.interactor.warn(`No integration or MCP found for OAuth callback: ${e.integrationName}`)}};var uy=class{constructor(e,r,n,i,a=[]){this.interactor=e;this.aiClientProvider=r;this.services=n;this.projectPath=i;this.commandLineAgentFolders=a;this.services.project.selectedProject$.subscribe(()=>{this.agentCache.clear(),this.agentDefinitions=[]}),this.toolbox=new UU(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,r)=>e.name.toLowerCase().localeCompare(r.name.toLowerCase()));initialize=async e=>{if(this.agentDefinitions.length>0)return;let r=performance.now();this.interactor.debug("\u{1F680} Starting agent initialization...");try{let i=performance.now();if(e.project.agents?.length)for(let h of e.project.agents)this.addDefinition(h,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 s=performance.now(),o=this.services.project.selectedProject;if(o?.config.agents?.length)for(let h of o.config.agents)this.addDefinition(h,this.projectPath);let c=performance.now()-s;this.interactor.debug(`\u2699\uFE0F Loaded agent definitions from project local config: ${c.toFixed(2)}ms (${o?.config.agents?.length??0} agents)`);let u=performance.now();await this.loadFromFiles(e);let f=performance.now()-u;this.interactor.debug(`\u{1F4C1} Loaded agent definitions from files: ${f.toFixed(2)}ms`),this.agentDefinitions.length===0&&(this.addDefinition($I,this.projectPath),this.interactor.debug("\u{1F504} No agent definitions found, using Coday as backup"));let d=performance.now();this.generateVirtualAgentsFromModels();let p=performance.now()-d;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()-r;this.interactor.debug(`\u{1F3AF} Total agent definition loading time: ${n.toFixed(2)}ms`)};async findByName(e,r){await this.initialize(r);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,r);if(a)return this.agentCache.set(n,a),a}}findAgentByNameStart=async(e,r)=>{if(!e)return;await this.initialize(r);let n=await this.findAgentsByNameStart(e?.toLowerCase()||"",r);if(n.length===0)return;if(n.length===1)return n[0];let i=n.map(a=>a.name);try{if(r.oneshot)return;let a=await this.interactor.chooseOption(i,`Multiple agents match '${e}', please select one:`);return n.find(s=>s.name===a)}catch{this.interactor.error("Selection cancelled");return}};async findAgentsByNameStart(e,r){await this.initialize(r);let n=e.toLowerCase(),i=this.agentDefinitions.filter(s=>s.definition.name.toLowerCase().startsWith(n)),a=[];for(let s of i){let o=s.definition.name.toLowerCase(),c=this.agentCache.get(o);c||(c=await this.tryAddAgent(s,r),c&&this.agentCache.set(o,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,r=this.projectPath){this.agentDefinitions.find(n=>n.definition.name===e.name)||this.agentDefinitions.push({definition:{...$I,...e},basePath:r})}generateVirtualAgentsFromModels(){let e=this.aiClientProvider.getAllModels();for(let r of e){let n={name:r.name,description:`Direct access to ${r.name} model (${r.providerName})`,instructions:"",aiProvider:r.providerName,modelName:r.name};this.addDefinition(n,this.projectPath)}this.interactor.debug(`\u{1F916} Generated ${e.length} virtual agents from available models: ${e.map(r=>r.name).join(", ")}`)}async loadFromFiles(e){let r=[],n=[],i=this.services.project.selectedProject;i&&r.push(Kg.join(i.configPath,"agents"));let a=this.services.project.selectedProject?.config.path;if(a){let f=await u2({text:"coday.yaml",root:a});if(f.length>0){let d=Kg.dirname(f[0]);r.push(Kg.join(a,d,"agents"))}e.project.agentFolders?.length&&r.push(...e.project.agentFolders)}r.push(...this.commandLineAgentFolders);let s=performance.now();await Promise.all(r.map(async f=>{try{n.push(...(await HU.readdir(f)).filter(d=>d.endsWith(".yml")||d.endsWith(".yaml")).map(d=>Kg.join(f,d)))}catch(d){d.code==="EPERM"?console.error(`Permission denied to access ${f}. This is common for protected directories.
1625
+ _${d} comment(s) available \u2014 use getComments with recording ID ${a.id} to retrieve them._`),v}catch(r){return`Error: ${r.message}`}}var yE=class extends xr{constructor(r,n,i,a,s){super(r,a,s);this.integrationService=n;this.userService=i}static TYPE="BASECAMP";oauth=null;async handleOAuthCallback(r){this.oauth&&await this.oauth.handleCallback(r)}async buildTools(r){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,s=i.apiKey,o=i.oauth2?.redirect_uri;if(!o)return this.interactor.displayText("Basecamp integration requires redirectUri"),n;if(!a||!s)return this.interactor.displayText("Basecamp integration requires clientId (username) and clientSecret (apiKey)"),n;let c=r.project.name;this.oauth=new DL(a,s,o,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:w})=>dHe(this.oauth,w)}};n.push(u);let f={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:w})=>pHe(this.oauth,w)}};n.push(f);let d={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:w,messageBoardId:k,page:T})=>hHe(this.oauth,w,k,T)}};n.push(d);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:w,messageId:k})=>mHe(this.oauth,w,k)}};n.push(p);let h={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:w,page:k})=>gHe(this.oauth,w,k)}};n.push(h);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:w,inboxId:k,page:T})=>vHe(this.oauth,w,k,T)}};n.push(m);let g={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:w,forwardId:k})=>yHe(this.oauth,w,k)}};n.push(g);let v={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:w})=>bHe(this.oauth,w)}};n.push(v);let y={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:w,page:k})=>xHe(this.oauth,w,k)}};n.push(y);let b={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:w})=>wHe(this.oauth,w)}};return n.push(b),n}};var RL=class{constructor(e,r,n,i,a){this.config=e;this.interactor=r;this.userService=n;this.integrationName=a;if(this.resolvedProjectName=n.resolveProjectName(i),e.authorizationEndpoint&&e.tokenEndpoint){let s=new URL(e.authorizationEndpoint);this.as={issuer:`${s.origin}${s.pathname.split("/").slice(0,-1).join("/")}`,authorization_endpoint:e.authorizationEndpoint,token_endpoint:e.tokenEndpoint}}this.client={client_id:e.clientId},this.clientAuth=CL(e.clientSecret)}as=null;client;clientAuth;tokenData=null;pendingState=null;pendingCodeVerifier=null;pendingResolve=null;pendingReject=null;pendingAuthPromise=null;resolvedProjectName;validateDiscoveryUrl(e,r){let n;try{n=new URL(e)}catch{throw new Error(`[OAuth:${this.integrationName}] ${r} is not a valid URL: ${e}`)}if(n.protocol!=="https:")throw new Error(`[OAuth:${this.integrationName}] ${r} must use HTTPS (got "${n.protocol}"): ${e}`);let i=n.hostname;if([/^localhost$/i,/^127\./,/^10\./,/^172\.(1[6-9]|2[0-9]|3[01])\./,/^192\.168\./,/^169\.254\./,/^\[?::1\]?$/,/^\[?fc00:/i,/^\[?fd[0-9a-f]{2}:/i].some(s=>s.test(i)))throw new Error(`[OAuth:${this.integrationName}] ${r} resolves to a private/loopback address and is not allowed: ${e}`);return n}async resolveAuthorizationServer(){if(this.as)return this.as;let{issuer:e,discoveryUrl:r}=this.config;if(e){this.validateDiscoveryUrl(e,"issuer"),this.interactor.debug(`[OAuth:${this.integrationName}] resolving endpoints via discovery for issuer=${e}`);let n=new URL(e),i;try{i=await Tle(n,{algorithm:"oidc"})}catch{this.interactor.debug(`[OAuth:${this.integrationName}] OIDC discovery failed, falling back to RFC 8414 for issuer=${e}`),i=await Tle(n,{algorithm:"oauth2"})}let a=await eHe(n,i);return this.interactor.debug(`[OAuth:${this.integrationName}] discovery succeeded: authorization_endpoint=${a.authorization_endpoint}, token_endpoint=${a.token_endpoint}`),this.as=a,this.as}if(r){let n=this.validateDiscoveryUrl(r,"discoveryUrl");this.interactor.debug(`[OAuth:${this.integrationName}] fetching discovery document from explicit URL: ${r}`);let i=await fetch(r);if(!i.ok)throw new Error(`[OAuth:${this.integrationName}] failed to fetch discovery document from ${r}: HTTP ${i.status}`);let a=await i.json();if(!a.authorization_endpoint||!a.token_endpoint)throw new Error(`[OAuth:${this.integrationName}] discovery document at ${r} is missing authorization_endpoint or token_endpoint`);let s=n.origin;for(let[o,c]of[["authorization_endpoint",a.authorization_endpoint],["token_endpoint",a.token_endpoint]]){let u;try{u=new URL(c).origin}catch{throw new Error(`[OAuth:${this.integrationName}] discovery document field "${o}" is not a valid URL: ${c}`)}if(u!==s)throw new Error(`[OAuth:${this.integrationName}] discovery document field "${o}" (${c}) does not share the same origin as the discovery URL (${s}). This may indicate a tampered or malicious discovery document.`)}return this.as={...a,issuer:a.issuer??n.origin},this.as}throw new Error(`[OAuth:${this.integrationName}] OAuth configuration error: provide either authorizationEndpoint + tokenEndpoint, or issuer, or discoveryUrl`)}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(),r=e>300*1e3;return this.interactor.debug(`[OAuth:${this.integrationName}] token expiresAt=${new Date(this.tokenData.expiresAt).toISOString()}, remainingMs=${e}, valid=${r}`),r}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,r)=>{this.pendingResolve=e,this.pendingReject=r}),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=await this.resolveAuthorizationServer(),r=_L(),n=wL(),i=await SL(n);this.pendingState=r,this.pendingCodeVerifier=n;let a=new URL(e.authorization_endpoint);if(a.searchParams.set("client_id",this.client.client_id),a.searchParams.set("redirect_uri",this.config.redirectUri),a.searchParams.set("response_type","code"),a.searchParams.set("state",r),a.searchParams.set("code_challenge",i),a.searchParams.set("code_challenge_method","S256"),a.searchParams.set("access_type","offline"),a.searchParams.set("prompt","consent"),this.config.scope){let s=Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope;a.searchParams.set("scope",s)}this.interactor.debug(`[OAuth:${this.integrationName}] emitting OAuthRequestEvent, authUrl=${a.toString()}`),this.interactor.sendEvent(new mf({authUrl:a.toString(),state:r,integrationName:this.integrationName})),this.interactor.debug(`[OAuth:${this.integrationName}] waiting for OAuth callback...`)}async handleCallback(e){if(e.integrationName!==this.integrationName)return;if(e.state!==this.pendingState){let n=new Error("Invalid OAuth state");this.interactor.error(n.message),this.rejectPending(n);return}if(e.error){let n=e.error==="access_denied"?"OAuth authentication denied by user":`OAuth error: ${e.error}${e.errorDescription?" - "+e.errorDescription:""}`;this.interactor.warn(n),this.rejectPending(new Error(n));return}if(!e.code||!this.pendingCodeVerifier||!this.pendingState){let n=new Error("No pending OAuth flow or missing code");this.interactor.error(n.message),this.rejectPending(n);return}let r=this.as;try{let n=new URL(this.config.redirectUri);n.searchParams.set("code",e.code),n.searchParams.set("state",e.state);let i=IL(r,this.client,n,this.pendingState),a=await OL(r,this.client,this.clientAuth,i,this.config.redirectUri,this.pendingCodeVerifier),s=await PL(r,this.client,a);this.tokenData={accessToken:s.access_token,refreshToken:s.refresh_token,expiresAt:Date.now()+(s.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(n){this.interactor.error(`OAuth token exchange failed: ${n.message}`),this.rejectPending(n)}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");let e=await this.resolveAuthorizationServer();try{let r=await TL(e,this.client,this.clientAuth,this.tokenData.refreshToken),n=await AL(e,this.client,r);this.tokenData={accessToken:n.access_token,refreshToken:n.refresh_token??this.tokenData.refreshToken,expiresAt:Date.now()+(n.expires_in??3600)*1e3},this.saveTokensToStorage()}catch(r){throw this.tokenData=null,this.clearTokensFromStorage(),new Error(`Token refresh failed: ${r.message}`)}}loadTokensFromStorage(){let e=this.userService.config.projects,r=e?.[this.resolvedProjectName],n=r?.integration?.[this.integrationName],i=n?.oauth2?.tokens;this.interactor.debug(`[OAuth:${this.integrationName}] loadTokensFromStorage: projectName=${this.resolvedProjectName}, hasProjects=${!!e}, hasProjectConfig=${!!r}, 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 r=e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2;r.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 _He=["client_secret","tokens"],SHe=["apiKey","username"],bE=class t extends xr{constructor(r,n){super(r,t.TYPE,{});this.integrationConfig=n}static TYPE="HTTP_CONFIG";async buildTools(r){return Hv(r.username)?[this.buildLetUserFillAuth(),this.buildListHttpIntegrations(),this.buildGetHttpIntegration(),this.buildUpsertHttpIntegration(),this.buildDeleteHttpIntegration(),this.buildGetEndpoint(),this.buildUpsertEndpoint(),this.buildDeleteEndpoint()]:(this.interactor.debug(`[HTTP_CONFIG] user '${r.username}' is not CODAY_ADMIN, tools not available`),[])}getProjectHttpIntegrations(){let r=this.integrationConfig.getUnmaskedIntegrationsAtLevel("project");return Object.fromEntries(Object.entries(r).filter(([,n])=>n.type==="HTTP"))}sanitizeForAgent(r){let n={...r};for(let i of SHe)delete n[i];if(n.oauth2&&typeof n.oauth2=="object"){let i={...n.oauth2};for(let a of _He)delete i[a];n.oauth2=i}return n}mergeIgnoringSensitive(r,n){let i={...r};for(let[a,s]of Object.entries(n))if(!SHe.includes(a))if(a==="oauth2"&&s&&typeof s=="object"){let o=r.oauth2??{},c=s,u={...o};for(let[f,d]of Object.entries(c))_He.includes(f)||(u[f]=d);i.oauth2=u}else a!=="http"&&(i[a]=s);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 s=a.oauth2??{},o=await this.interactor.promptSecretText(`OAuth2 client_id for '${n}' (leave as-is to keep current):`,s.client_id),c=await this.interactor.promptSecretText(`OAuth2 client_secret for '${n}' (leave as-is to keep current):`,s.client_secret);if(o===void 0&&c===void 0)return{success:!1,message:"No values provided, nothing was saved."};let u={...s,...o!==void 0&&{client_id:o},...c!==void 0&&{client_secret:c}},f={...a,oauth2:u};return await this.integrationConfig.saveIntegration(n,f,"project"),{success:!0,updated:[...o!==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(s=>s.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(s=>s.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, issuer, discovery_url, 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"},issuer:{type:"string",description:"OAuth2/OIDC issuer URL for automatic endpoint discovery (RFC 8414 / OIDC). Alternative to providing authorization_endpoint + token_endpoint explicitly."},discovery_url:{type:"string",description:"Explicit discovery document URL when the discovery endpoint is non-standard (alternative to issuer)."},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 o=this.getProjectHttpIntegrations()[n]??{type:"HTTP"},c={};a&&(c.oauth2=a);let u=this.mergeIgnoringSensitive(o,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 s=this.getProjectHttpIntegrations()[n];if(!s)return{error:`Integration '${n}' not found`};let o=(s.http?.endpoints??[]).find(c=>c.name===i);return o||{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 s=this.getProjectHttpIntegrations()[n];if(!s)return{error:`Integration '${n}' not found`};let c=[...(s.http?.endpoints??[]).filter(f=>f.name!==i.name),i],u={...s,http:{...s.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 s=this.getProjectHttpIntegrations()[n];if(!s)return{error:`Integration '${n}' not found`};let o=s.http?.endpoints??[];if(!o.find(u=>u.name===i))return{error:`Endpoint '${i}' not found in '${n}'`};let c={...s,http:{...s.http??{baseUrl:""},endpoints:o.filter(u=>u.name!==i)}};return await this.integrationConfig.saveIntegration(n,c,"project"),{success:!0,integrationName:n,endpointName:i}}}}}};var EHe=Kr(Tf(),1);var xE=class extends xr{constructor(r,n,i,a){super(r,i,a);this.userService=n}static TYPE="HTTP";oauth=null;async handleOAuthCallback(r){this.oauth&&await this.oauth.handleCallback(r)}async buildTools(r,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`),[];let s=i.authorization_endpoint&&i.token_endpoint,o=i.issuer||i.discovery_url;if(!s&&!o)return this.interactor.debug(`[HTTP:${this.name}] missing oauth2 endpoint configuration: provide authorization_endpoint + token_endpoint, or issuer, or discovery_url`),[];let c=this.config?.http?.endpoints;if(!c?.length)return this.interactor.debug(`[HTTP:${this.name}] no endpoints configured`),[];let u=c.map(f=>`${f.name}:${f.method} ${f.path} (${f.params?.length??0} params${f.keepPaths?", keepPaths["+f.keepPaths.length+"]":""}${f.ignorePaths?", ignorePaths["+f.ignorePaths.length+"]":""}${f.responseFormat?", "+f.responseFormat:""})`).join(" | ");return this.interactor.debug(`[HTTP:${this.name}] baseUrl=${a} | endpoints: ${u}`),this.oauth||(this.oauth=new RL({clientId:i.client_id,clientSecret:i.client_secret,redirectUri:i.redirect_uri,authorizationEndpoint:i.authorization_endpoint,tokenEndpoint:i.token_endpoint,issuer:i.issuer,discoveryUrl:i.discovery_url,scope:i.scope},this.interactor,this.userService,r.project.name,this.name)),c.map(f=>this.buildTool(f,a))}buildTool(r,n){let i=r.params??[],a={},s=[];for(let c of i)a[c.name]={type:c.type,description:c.description},c.required&&s.push(c.name);return{type:"function",function:{name:`${this.name}__${r.name}`,description:r.description,parameters:{type:"object",properties:a,...s.length?{required:s}:{}},parse:JSON.parse,function:async c=>this.executeEndpoint(r,n,c,i)}}}async executeEndpoint(r,n,i,a){this.interactor.debug(`[HTTP:${this.name}__${r.name}] args=${JSON.stringify(i)}`),this.interactor.debug(`[HTTP:${this.name}__${r.name}] oauth=${!!this.oauth}, isAuthenticated=${this.oauth?.isAuthenticated()}`);let s;try{this.oauth.isAuthenticated()||this.oauth.hasRefreshToken()?s=await this.oauth.getAccessToken():(this.interactor.debug(`[HTTP:${this.name}__${r.name}] no valid token or refresh_token, starting OAuth flow`),await this.oauth.authenticate(),s=await this.oauth.getAccessToken())}catch(p){throw new Error(`Authentication failed for ${this.name}: ${p.message}`)}this.interactor.debug(`[HTTP:${this.name}__${r.name}] got access token (length=${s.length})`);let o=r.path;for(let p of a.filter(h=>h.location==="path")){let h=i[p.name];h!==void 0&&(o=o.replace(`{${p.name}}`,encodeURIComponent(String(h))))}let c=new URL(`${n}${o}`);for(let p of a.filter(h=>!h.location||h.location==="query")){let h=i[p.name];h!==void 0&&c.searchParams.set(p.name,String(h))}let u=a.filter(p=>p.location==="body"),f=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}__${r.name}] ${r.method} ${c.toString()}`);let d=await fetch(c.toString(),{method:r.method,headers:{Authorization:`Bearer ${s}`,...f?{"Content-Type":"application/json"}:{}},...f?{body:f}:{}});if(!d.ok){if(d.status===401||d.status===403){this.interactor.debug(`[HTTP:${this.name}__${r.name}] got ${d.status}, re-authenticating`),await this.oauth.authenticate();let h=await this.oauth.getAccessToken(),m=await fetch(c.toString(),{method:r.method,headers:{Authorization:`Bearer ${h}`,...f?{"Content-Type":"application/json"}:{}},...f?{body:f}:{}});if(!m.ok){let g=await m.text();throw new Error(`HTTP ${m.status} from ${r.method} ${o} (after re-auth): ${g}`)}return this.parseResponse(m,r)}let p=await d.text();throw new Error(`HTTP ${d.status} from ${r.method} ${o}: ${p}`)}return this.parseResponse(d,r)}async parseResponse(r,n){let i;if((r.headers.get("content-type")??"").includes("application/json"))i=await r.json();else{let u=await r.text();if(!u)return null;try{i=JSON.parse(u)}catch{i=u}}let s=JSON.stringify(i).length,o=Ifr(i,n.keepPaths,n.ignorePaths),c=JSON.stringify(o).length;return n.keepPaths?.length||n.ignorePaths?.length?this.interactor.debug(`[HTTP:${this.name}__${n.name}] response filtered ${s} \u2192 ${c} chars`):this.interactor.debug(`[HTTP:${this.name}__${n.name}] response ${s} chars (no filter)`),n.responseFormat==="yaml"?EHe.stringify(o):o}};function Ifr(t,e,r){return!e?.length&&!r?.length?t:e?.length?jle(t,e):Nle(t,r)}function jle(t,e){if(Array.isArray(t))return t.map(r=>jle(r,e));if(t!==null&&typeof t=="object"){let r={};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let s=t,o=i==="*"?Object.keys(s):[i];for(let c of o){let u=s[c];if(u!==void 0)if(a.length===0)r[c]=u;else{let f=jle(u,[a.join(".")]);c in r&&r[c]!==null&&typeof r[c]=="object"&&!Array.isArray(r[c])?r[c]={...r[c],...f}:r[c]=f}}}return r}return t}function Nle(t,e){if(Array.isArray(t))return t.map(r=>Nle(r,e));if(t!==null&&typeof t=="object"){let r={...t};for(let n of e){let[i,...a]=n.split(".");if(!i)continue;let s=i==="*"?Object.keys(r):[i];for(let o of s)o in r&&(a.length===0?delete r[o]:r[o]=Nle(r[o],[a.join(".")]))}return r}return t}import*as e4e from"path";import{existsSync as XPr}from"node:fs";var udt=Kr(cdt(),1),EC=class extends xr{static TYPE="EXCEL";constructor(e,r,n){super(e,r,n)}async buildTools(e,r){let n=[],i=u=>{if(u.startsWith("project://"))return e4e.resolve(e.project.root,u.replace("project://",""));if(u.startsWith("exchange://")){if(!e.threadFilesRoot)throw new Error("No exchange workspace available in this context");return e4e.resolve(e.threadFilesRoot,u.replace("exchange://",""))}throw new Error(`File path must start with "project://" or "exchange://". Got: "${u}"`)},a=async u=>{let f=i(u);if(!XPr(f))throw new Error(`File not found: ${u}`);let d=new udt.default.Workbook;return await d.xlsx.readFile(f),d},s={type:"function",function:{name:`${this.name}__listSheets`,description:'List all sheet names in an Excel file (.xlsx). Use this first to discover the structure of a workbook before reading data. File path must start with "project://" or "exchange://".',parameters:{type:"object",properties:{filePath:{type:"string",description:'Path to the Excel file (e.g. "exchange://report.xlsx" or "project://data/sales.xlsx")'}}},parse:JSON.parse,function:async({filePath:u})=>{try{let d=(await a(u)).worksheets.map(p=>p.name);return JSON.stringify({sheets:d},null,2)}catch(f){return`Error: ${f}`}}}};n.push(s);let o={type:"function",function:{name:`${this.name}__readSheet`,description:'Read the content of a sheet from an Excel file as JSON rows. If sheetName is omitted, the first sheet is used. Use maxRows to limit the number of rows returned (recommended for large files). Use startRow to paginate through large datasets (0-indexed, after the header row). Returns an array of objects where keys are column headers. File path must start with "project://" or "exchange://".',parameters:{type:"object",properties:{filePath:{type:"string",description:'Path to the Excel file (e.g. "exchange://report.xlsx")'},sheetName:{type:"string",description:"Name of the sheet to read. Defaults to the first sheet if omitted."},maxRows:{type:"number",description:"Maximum number of data rows to return (default: 100, max: 1000). Use to avoid overloading context."},startRow:{type:"number",description:"Zero-indexed row offset (after header) to start reading from. Use for pagination."}}},parse:JSON.parse,function:async({filePath:u,sheetName:f,maxRows:d=100,startRow:p=0})=>{try{let h=await a(u),m=f?h.getWorksheet(f):h.worksheets[0];if(!m){let R=h.worksheets.map(j=>j.name).join(", ");return f?`Error: sheet "${f}" not found. Available sheets: ${R}`:"Error: workbook has no sheets"}let g=m.getRow(1),v=[];g.eachCell({includeEmpty:!0},(R,j)=>{v[j-1]=R.text||`Column${j}`});let y=Math.min(d,1e3),b=2+p,w=b+y-1,k=m.rowCount-1,T=[];for(let R=b;R<=Math.min(w,m.rowCount);R++){let j=m.getRow(R);if(j.hasValues){let E={};j.eachCell({includeEmpty:!0},(A,S)=>{let C=v[S-1]??`Column${S}`;E[C]=A.text||null}),T.push(E)}}let P=b+y-1<m.rowCount;return JSON.stringify({sheet:m.name,totalRows:k,returnedRows:T.length,startRow:p,hasMore:P,...P?{nextStartRow:p+y}:{},rows:T},null,2)}catch(h){return`Error: ${h}`}}}};n.push(o);let c={type:"function",function:{name:`${this.name}__getSheetInfo`,description:'Get metadata about a sheet without loading all its data: number of rows, column headers, and a preview of the first 3 rows. Use this to understand the structure of a large sheet before deciding how to read it. File path must start with "project://" or "exchange://".',parameters:{type:"object",properties:{filePath:{type:"string",description:"Path to the Excel file"},sheetName:{type:"string",description:"Name of the sheet. Defaults to the first sheet if omitted."}}},parse:JSON.parse,function:async({filePath:u,sheetName:f})=>{try{let d=await a(u),p=f?d.getWorksheet(f):d.worksheets[0];if(!p){let v=d.worksheets.map(y=>y.name).join(", ");return f?`Error: sheet "${f}" not found. Available sheets: ${v}`:"Error: workbook has no sheets"}let h=p.getRow(1),m=[];h.eachCell({includeEmpty:!0},(v,y)=>{m[y-1]=v.text||`Column${y}`});let g=[];for(let v=2;v<=Math.min(4,p.rowCount);v++){let y=p.getRow(v);if(y.hasValues){let b={};y.eachCell({includeEmpty:!0},(w,k)=>{let T=m[k-1]??`Column${k}`;b[T]=w.text||null}),g.push(b)}}return JSON.stringify({sheet:p.name,totalRows:p.rowCount-1,columnCount:m.length,columns:m,preview:g},null,2)}catch(d){return`Error: ${d}`}}}};return n.push(c),n}async kill(){}};var UU=class t{constructor(e,r,n,i){this.interactor=e;this.services=r;this.mcpConfigs=r.mcp.getMergedConfiguration().servers,this.factoryConstructors=new Map,this.factoryConstructors.set(ES.TYPE,a=>new ES(e,a,{},this.services.options?.baseUrl)),this.factoryConstructors.set(Aw.TYPE,a=>new Aw(e,i,a,{})),this.factoryConstructors.set(Ow.TYPE,a=>new Ow(e,n,i,a,{},r.thread)),this.factoryConstructors.set(Dw.TYPE,(a,s)=>new Dw(e,a,s)),this.factoryConstructors.set(TS.TYPE,a=>new TS(e,a,{})),this.factoryConstructors.set(CS.TYPE,a=>new CS(e,r.memory,a,{})),this.factoryConstructors.set(kS.TYPE,a=>new kS(e,r.thread,a,{})),this.factoryConstructors.set(OS.TYPE,(a,s)=>new OS(e,r.integration,a,s)),this.factoryConstructors.set(PS.TYPE,(a,s)=>new PS(e,r.integration,a,s,r.projectService)),this.factoryConstructors.set(VS.TYPE,(a,s)=>new VS(e,r.integration,a,s)),this.factoryConstructors.set(dE.TYPE,(a,s)=>new dE(e,r.integration,a,s)),this.factoryConstructors.set(pE.TYPE,(a,s)=>new pE(e,r.integration,a,s)),this.factoryConstructors.set(hE.TYPE,(a,s)=>new hE(e,r.integration,a,s)),this.factoryConstructors.set(gE.TYPE,(a,s)=>new gE(e,r.integration,a,s)),this.factoryConstructors.set(yE.TYPE,(a,s)=>new yE(e,r.integration,r.user,a,s)),this.factoryConstructors.set(xE.TYPE,(a,s)=>new xE(e,r.user,a,s)),this.factoryConstructors.set(bE.TYPE,()=>new bE(e,r.integrationConfig)),this.factoryConstructors.set(AS.TYPE,a=>new AS(e,a)),this.factoryConstructors.set(EC.TYPE,(a,s)=>new EC(e,a,s))}factoryConstructors;factoryInstances=new Map;mcpConfigs;tools=[];currentThreadId;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:r,integrations:n,agentName:i}=e,a=r.aiThread?.id;a||this.interactor.warn("No thread ID in context, MCP tools will not be available");let s=new Set(this.mcpConfigs.map(u=>u.name)),o=[];if(n)for(let[u]of n){if(s.has(u))continue;let f=this.factoryInstances.get(u)??this.createFactory(u);f&&o.push(f)}else{let u=new Set(Object.values(this.services.integration.integrations).map(d=>d?.type).filter(Boolean)),f=new Set([...Array.from(this.factoryConstructors.keys()).filter(d=>!u.has(d)),...Object.keys(this.services.integration.integrations)]);for(let d of f){let p=this.factoryInstances.get(d)??this.createFactory(d);p&&o.push(p)}}let c=[];if(a){this.currentThreadId=a;for(let u of this.mcpConfigs)if(!(n&&!n.has(u.name)))try{let f=await this.services.mcpPool.getOrCreateFactory(u,a,()=>new dM(u,this.interactor,this.services.user,r.project.name,this.services.options?.baseUrl));c.push(f)}catch(f){this.interactor.debug(`Error creating MCP factory for ${u.name}: ${f}`)}}o.push(...c);try{let u=await Promise.all(o.map(async p=>{try{return await p.getTools(r,n?.get(p.name)??[],i??"default")}catch(h){return this.interactor.debug(`Error building tools from ${p.name} for agent ${i}: ${h}`),[]}})),{tools:f,duplicates:d}=t.deduplicateTools(u.flat());for(let p of d)this.interactor.warn(`Duplicate tool '${p.name}': kept source '${p.keptSource}', discarded source '${p.discardedSource}'`);return this.tools=f,this.tools}catch(u){return this.interactor.debug(`Unexpected error building tools for agent ${i}: ${u}`),[]}}createFactory(e){let r=this.services.integration.integrations,n=r[e]??{},i=r[e]?.type,a=i?this.factoryConstructors.get(i):void 0,s=this.factoryConstructors.get(e),o=a??s,c=a?i:e;if(!o){this.interactor.debug(`No factory constructor for integration '${e}' (type='${i??"none"}')`);return}try{let u=o(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}}static deduplicateTools(e){let r=new Map,n=[];for(let i of e){let a=i.function.name,s=r.get(a);s&&n.push({name:a,keptSource:t.extractSource(i),discardedSource:t.extractSource(s)}),r.set(a,i)}return{tools:Array.from(r.values()),duplicates:n}}static extractSource(e){let r=e.function.name,n=r.indexOf("__");return n>0?r.substring(0,n):r}async handleOAuthCallback(e){let r=this.factoryInstances.get(e.integrationName)??this.createFactory(e.integrationName);if(r){if("handleOAuthCallback"in r&&typeof r.handleOAuthCallback=="function")try{await r.handleOAuthCallback(e)}catch(i){this.interactor.error(`Error handling OAuth callback for ${e.integrationName}: ${i}`)}else this.interactor.warn(`Integration ${e.integrationName} does not support OAuth callbacks`);return}let n=this.mcpConfigs.find(i=>i.id===e.integrationName);if(n){let i=await this.services.mcpPool.getMcpFactory(n,this.currentThreadId);if(i&&"handleOAuthCallback"in i&&typeof i.handleOAuthCallback=="function"){try{await i.handleOAuthCallback(e)}catch(a){this.interactor.error(`Error handling OAuth callback for MCP ${e.integrationName}: ${a}`)}return}}this.interactor.warn(`No integration or MCP found for OAuth callback: ${e.integrationName}`)}};var uy=class{constructor(e,r,n,i,a=[]){this.interactor=e;this.aiClientProvider=r;this.services=n;this.projectPath=i;this.commandLineAgentFolders=a;this.services.project.selectedProject$.subscribe(()=>{this.agentCache.clear(),this.agentDefinitions=[]}),this.toolbox=new UU(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,r)=>e.name.toLowerCase().localeCompare(r.name.toLowerCase()));initialize=async e=>{if(this.agentDefinitions.length>0)return;let r=performance.now();this.interactor.debug("\u{1F680} Starting agent initialization...");try{let i=performance.now();if(e.project.agents?.length)for(let h of e.project.agents)this.addDefinition(h,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 s=performance.now(),o=this.services.project.selectedProject;if(o?.config.agents?.length)for(let h of o.config.agents)this.addDefinition(h,this.projectPath);let c=performance.now()-s;this.interactor.debug(`\u2699\uFE0F Loaded agent definitions from project local config: ${c.toFixed(2)}ms (${o?.config.agents?.length??0} agents)`);let u=performance.now();await this.loadFromFiles(e);let f=performance.now()-u;this.interactor.debug(`\u{1F4C1} Loaded agent definitions from files: ${f.toFixed(2)}ms`),this.agentDefinitions.length===0&&(this.addDefinition($I,this.projectPath),this.interactor.debug("\u{1F504} No agent definitions found, using Coday as backup"));let d=performance.now();this.generateVirtualAgentsFromModels();let p=performance.now()-d;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()-r;this.interactor.debug(`\u{1F3AF} Total agent definition loading time: ${n.toFixed(2)}ms`)};async findByName(e,r){await this.initialize(r);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,r);if(a)return this.agentCache.set(n,a),a}}findAgentByNameStart=async(e,r)=>{if(!e)return;await this.initialize(r);let n=await this.findAgentsByNameStart(e?.toLowerCase()||"",r);if(n.length===0)return;if(n.length===1)return n[0];let i=n.map(a=>a.name);try{if(r.oneshot)return;let a=await this.interactor.chooseOption(i,`Multiple agents match '${e}', please select one:`);return n.find(s=>s.name===a)}catch{this.interactor.error("Selection cancelled");return}};async findAgentsByNameStart(e,r){await this.initialize(r);let n=e.toLowerCase(),i=this.agentDefinitions.filter(s=>s.definition.name.toLowerCase().startsWith(n)),a=[];for(let s of i){let o=s.definition.name.toLowerCase(),c=this.agentCache.get(o);c||(c=await this.tryAddAgent(s,r),c&&this.agentCache.set(o,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,r=this.projectPath){this.agentDefinitions.find(n=>n.definition.name===e.name)||this.agentDefinitions.push({definition:{...$I,...e},basePath:r})}generateVirtualAgentsFromModels(){let e=this.aiClientProvider.getAllModels();for(let r of e){let n={name:r.name,description:`Direct access to ${r.name} model (${r.providerName})`,instructions:"",aiProvider:r.providerName,modelName:r.name};this.addDefinition(n,this.projectPath)}this.interactor.debug(`\u{1F916} Generated ${e.length} virtual agents from available models: ${e.map(r=>r.name).join(", ")}`)}async loadFromFiles(e){let r=[],n=[],i=this.services.project.selectedProject;i&&r.push(Kg.join(i.configPath,"agents"));let a=this.services.project.selectedProject?.config.path;if(a){let f=await u2({text:"coday.yaml",root:a});if(f.length>0){let d=Kg.dirname(f[0]);r.push(Kg.join(a,d,"agents"))}e.project.agentFolders?.length&&r.push(...e.project.agentFolders)}r.push(...this.commandLineAgentFolders);let s=performance.now();await Promise.all(r.map(async f=>{try{n.push(...(await HU.readdir(f)).filter(d=>d.endsWith(".yml")||d.endsWith(".yaml")).map(d=>Kg.join(f,d)))}catch(d){d.code==="EPERM"?console.error(`Permission denied to access ${f}. This is common for protected directories.
1626
1626
  Consider moving your agent files to a less restricted location.`):console.error(`Could not read directory ${f}: ${d.code}`)}}));let o=performance.now()-s;this.interactor.debug(` \u{1F4C2} Scanned agent directories: ${o.toFixed(2)}ms (found ${n.length} files)`);let c=performance.now();await Promise.all(n.map(async f=>{try{let d=await HU.readFile(f,"utf-8"),p=ldt.parse(d),h=Kg.dirname(f),g=h.startsWith(this.projectPath)?this.projectPath:h;this.addDefinition(p,g)}catch(d){console.error(d)}}));let u=performance.now()-c;this.interactor.debug(` \u{1F4DD} Parsed agent files: ${u.toFixed(2)}ms`)}async tryAddAgent(e,r){let n={...$I,...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(),s=this.aiClientProvider.getClient(n.aiProvider,n.modelName);if(!s){this.interactor.error(`Cannot create agent ${n.name}: AI client creation failed`);return}let o=performance.now()-a,c=e.basePath,u=performance.now(),f=await rR(n,this.interactor,c,n.name),d=performance.now()-u;n.instructions=`${n.instructions}
1627
1627
 
1628
1628