@whoz-oss/coday-web 0.18.1 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client/index.html CHANGED
@@ -9,5 +9,5 @@
9
9
  <style>:root{--color-bg:#f8f8f2;--color-bg-secondary:#f1f1eb;--color-text:#282a36;--color-text-secondary:#6272a4;--color-text-inverse:#ffffff;--color-primary:#7064fb;--color-primary-hover:#ff79c6;--color-link:#3498db;--color-border:#aeaeae;--color-input-bg:#ffffff;--color-success:#50fa7b;--color-error:#ff5555;--color-warning:#ffb86c;--color-info:#8be9fd;--color-message-user:#e2dee2;--color-message-ai:#e8ecf6;--color-code-bg:#f1f1f1;--color-code-text:#d63384;--color-bg-hover:#f1f3f4;--color-shadow:rgba(0, 0, 0, .1);--color-overlay:rgba(0, 0, 0, .3)}*{box-sizing:border-box}html,body{margin:0;padding:0;width:100%;height:100%}body{background-color:var(--color-bg, #f8f8f2);color:var(--color-text, #282a36);font-family:system-ui,sans-serif;transition:background-color .2s ease,color .2s ease}</style><link rel="stylesheet" href="styles-BJ6YBRRX.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-BJ6YBRRX.css"></noscript></head>
10
10
  <body>
11
11
  <app-root></app-root>
12
- <script src="polyfills-B6TNHZQ6.js" type="module"></script><script src="main-HYHKC5LJ.js" type="module"></script></body>
12
+ <script src="polyfills-B6TNHZQ6.js" type="module"></script><script src="main-EBP2SAFU.js" type="module"></script></body>
13
13
  </html>
@@ -70,7 +70,7 @@ Please report this to https://github.com/markedjs/marked.`,n){let r="<p>An error
70
70
  `;t.push(s)}return t.join("")})}onPlay(){let e=this.getTextContentForVoice();if(!e.trim()){console.warn("[CHAT-MESSAGE] No text content to play");return}if(this.isPlaying)console.log("[CHAT-MESSAGE] Stopping current playback"),this.voiceSynthesisService.stopSpeech(),this.isPlaying=!1;else{console.log("[CHAT-MESSAGE] Starting playback for message:",this.message.id);let t=()=>{console.log("[CHAT-MESSAGE] Playback ended for message:",this.message.id),this.isPlaying=!1};this.voiceSynthesisService.speak(e,t).then(r=>{r?this.isPlaying=!0:console.warn("[CHAT-MESSAGE] Failed to start speech synthesis")}).catch(r=>{console.error("[CHAT-MESSAGE] Error during speech synthesis:",r),this.isPlaying=!1})}this.playRequested.emit(this.message)}onCopy(){let e=this.getTextContentForVoice();e.trim()&&navigator.clipboard.writeText(e).then(()=>{console.log("[CHAT-MESSAGE] Message copied to clipboard")}).catch(t=>{console.error("[CHAT-MESSAGE] Failed to copy message:",t)}),this.copyRequested.emit(this.message)}onDelete(){console.log("[CHAT-MESSAGE] Delete requested for message:",this.message.id),this.deleteRequested.emit(this.message)}onMessageClick(){this.voiceSynthesisService.isSpeaking()&&this.voiceSynthesisService.stopSpeech()}getTextContentForVoice(){return this.extractTextContent()}extractTextContent(){return this.message.content.filter(e=>e.type==="text").map(e=>e.content).join(`
71
71
 
72
72
  `)}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=_e({type:n,selectors:[["app-chat-message"]],inputs:{message:"message",canDelete:"canDelete"},outputs:{playRequested:"playRequested",copyRequested:"copyRequested",deleteRequested:"deleteRequested"},decls:3,vars:4,consts:[["tabindex","0","role","button",1,"message",3,"click","keydown.enter","keydown.space","ngClass"],[4,"ngIf"],[1,"message-header"],["class","speaker",4,"ngIf"],["class","actions",4,"ngIf"],[1,"content","markdown-content",3,"innerHTML"],[1,"speaker"],[1,"actions"],[1,"action-btn","play-btn",3,"click","title"],["title","Copy message",1,"action-btn",3,"click"],["class","action-btn delete-btn","title","Delete this message and all following messages",3,"click",4,"ngIf"],["title","Delete this message and all following messages",1,"action-btn","delete-btn",3,"click"],[1,"simple-content","markdown-content"],[3,"innerHTML"],["target","_blank","class","event-link",3,"href","click",4,"ngIf"],["target","_blank",1,"event-link",3,"click","href"]],template:function(t,r){t&1&&(v(0,"div",0),M("click",function(){return r.onMessageClick()})("keydown.enter",function(){return r.onMessageClick()})("keydown.space",function(){return r.onMessageClick()}),re(1,cO,5,3,"ng-container",1)(2,dO,4,2,"ng-container",1),m()),t&2&&(A("ngClass",r.messageClasses),si("aria-label","Message from "+r.message.speaker+". Click to stop speech if playing."),E(),A("ngIf",!r.isSimplified),E(),A("ngIf",r.isSimplified))},dependencies:[Se,Hp,ke],styles:[".message[_ngcontent-%COMP%]{margin:.5rem 0;border-radius:8px;position:relative;max-width:75%;width:fit-content;min-width:100px;word-wrap:break-word;cursor:pointer}.message.user[_ngcontent-%COMP%]{padding:.75rem 1rem;background:var(--color-message-user, #e2dee2);margin-left:auto;margin-right:0;border-bottom-right-radius:4px}.message.assistant[_ngcontent-%COMP%]{padding:.75rem 1rem;background:var(--color-message-ai, #e8ecf6);margin-left:0;margin-right:auto;border-bottom-left-radius:4px}.message.system[_ngcontent-%COMP%], .message.technical[_ngcontent-%COMP%]{padding:.25rem 0;background:transparent;border:none;margin:.25rem 0;font-family:monospace;font-size:.85rem;color:var(--color-text-secondary, #6272a4);max-width:none;width:auto;min-width:auto;cursor:default}.message.error[_ngcontent-%COMP%]{padding:.75rem;background:#ff55551a;border-left:3px solid var(--color-error, #ff5555);color:var(--color-error, #ff5555)}.message.warning[_ngcontent-%COMP%]{padding:.75rem;background:#ffb86c1a;border-left:3px solid var(--color-warning, #ffb86c);color:var(--color-warning, #ffb86c)}.message-header[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem}.speaker[_ngcontent-%COMP%]{font-weight:600;font-size:.85rem;color:var(--color-text-secondary, #6272a4)}.content[_ngcontent-%COMP%]{line-height:1.5}.actions[_ngcontent-%COMP%]{display:flex;gap:.5rem;opacity:0;transition:opacity .2s}.message[_ngcontent-%COMP%]:hover .actions[_ngcontent-%COMP%]{opacity:1}.simple-content[_ngcontent-%COMP%]{display:flex}.simple-content[_ngcontent-%COMP%] .event-link[_ngcontent-%COMP%]{margin-left:5px;font-size:.9em;color:var(--color-link, #3498db);text-decoration:none}.simple-content[_ngcontent-%COMP%] .event-link[_ngcontent-%COMP%]:hover{text-decoration:underline}.action-btn[_ngcontent-%COMP%]{background:none;border:1px solid var(--color-border, #aeaeae);border-radius:3px;padding:.2rem .4rem;cursor:pointer;font-size:.7rem;transition:all .2s ease}.action-btn[_ngcontent-%COMP%]:hover{background:var(--color-bg-secondary, #f1f1eb);transform:scale(1.05)}.action-btn[_ngcontent-%COMP%]:active{transform:scale(.95)}.delete-btn[_ngcontent-%COMP%]{border-color:var(--color-error, #ff5555);color:var(--color-error, #ff5555)}.delete-btn[_ngcontent-%COMP%]:hover{background:#ff55551a;border-color:var(--color-error, #ff5555);transform:scale(1.05)}.delete-btn[_ngcontent-%COMP%]:active{background:#f553;transform:scale(.95)}.play-btn.playing[_ngcontent-%COMP%]{background:var(--color-primary, #7064fb);color:#fff;border-color:var(--color-primary, #7064fb)}.play-btn.playing[_ngcontent-%COMP%]:hover{background:var(--color-primary-hover, #5a4df5);border-color:var(--color-primary-hover, #5a4df5)}.text-part[_ngcontent-%COMP%]{margin:4px 0}.image-content[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;margin:1em 0}.message-image[_ngcontent-%COMP%]{display:block;max-width:100%;height:auto;margin:8px 0;border-radius:8px;cursor:pointer;box-shadow:0 2px 8px #0000001a;transition:transform .2s ease,box-shadow .2s ease}.message-image[_ngcontent-%COMP%]:hover{opacity:.9;transform:scale(1.02);box-shadow:0 4px 16px #0003}@media (max-width: 768px){.message-image[_ngcontent-%COMP%]{border-radius:4px;margin:6px 0}}@media (max-width: 480px){.message-image[_ngcontent-%COMP%]{margin:4px 0}}"]})};var Oo=class n{unreadCountSubject=new V(0);unreadCount$=this.unreadCountSubject.asObservable();constructor(){console.log("[UNREAD] Service initialized")}addUnread(){let t=this.unreadCountSubject.value+1;this.unreadCountSubject.next(t),console.log("[UNREAD] Added unread message, total:",t)}markAllAsRead(){this.unreadCountSubject.value>0&&(this.unreadCountSubject.next(0),console.log("[UNREAD] Marked all messages as read"))}getCurrentCount(){return this.unreadCountSubject.value}reset(){this.unreadCountSubject.next(0),console.log("[UNREAD] Counter reset")}static \u0275fac=function(t){return new(t||n)};static \u0275prov=C({token:n,factory:n.\u0275fac,providedIn:"root"})};function Rw(n,e=80){if(n.length<=e)return n;try{if(n.trim().startsWith("{")||n.trim().startsWith("[")){let t=JSON.parse(n),r=JSON.stringify(t);return r.length<=e?r:r.substring(0,e)+`...(${r.length} chars)`}}catch{}return n.substring(0,e)+"..."}var dt=class{constructor(e,t){this.type=t;if(e.timestamp)this.timestamp=e.timestamp;else{let r=Math.random().toString(36).substring(2,7);this.timestamp=`${new Date().toISOString()}-${r}`}this.parentKey=e.parentKey,this.length=0}timestamp;parentKey;static type;length},qu=class extends dt{invite;constructor(e,t){super(e,t),this.invite=e.invite}buildAnswer(e){return new Tr({answer:e,parentKey:this.timestamp,invite:this.invite})}},ko=class n extends dt{static type="heartbeat";constructor(e){super(e,n.type)}},Po=class n extends qu{defaultValue;static type="invite";constructor(e){super(e,n.type),this.defaultValue=e.defaultValue}},Tr=class n extends dt{answer;invite;static type="answer";constructor(e){super(e,n.type),this.answer=e.answer??"No answer",this.invite=e.invite}},Fo=class n extends dt{speaker;text;static type="text";constructor(e){super(e,n.type),this.speaker=e.speaker,this.text=e.text}},Lo=class n extends dt{warning;static type="warn";constructor(e){super(e,n.type),this.warning=e.warning}},Kn=class n extends dt{error;static type="error";constructor(e){super(e,n.type),this.error=e.error}},Vo=class n extends qu{options;optionalQuestion;static type="choice";constructor(e){super(e,n.type),this.options=e.options,this.optionalQuestion=e.optionalQuestion}},jo=class n extends dt{toolRequestId;name;args;static type="tool_request";constructor(e){super(e,n.type),this.toolRequestId=e.toolRequestId??this.timestamp,this.name=e.name,this.args=e.args,this.length=this.args.length+this.name.length+this.toolRequestId.length+20}buildResponse(e){return new Si({output:e,toolRequestId:this.toolRequestId,parentKey:this.timestamp})}toSingleLineString(e=50){let t=Rw(this.args,e);return`\u{1F527} ${this.name}(${t})`}},Si=class n extends dt{toolRequestId;output;static type="tool_response";constructor(e){if(super(e,n.type),this.toolRequestId=e.toolRequestId||this.timestamp||new Date().toISOString(),this.output=e.output,typeof this.output=="string")this.length=this.output.length+this.toolRequestId.length+20;else if(this.output.type==="text")this.length=this.output.content.length+this.toolRequestId.length+20;else if(this.output.type==="image"){let t=(this.output.width??0)*(this.output.height??0)/750;this.length=(t?t*3.5:this.output.content.length)+this.toolRequestId.length+20}else this.length=this.toolRequestId.length+20}getTextOutput(){return typeof this.output=="string"?this.output:this.output.type==="text"?this.output.content:this.output.type==="image"?`[Image: ${this.output.mimeType}]`:""}toSingleLineString(e=50){let t=this.getTextOutput(),r=Rw(t,e),i=typeof this.output!="string"&&this.output.type==="image"?" [image]":"";return`\u2B91 ${r}${i}`}},Ir=class n extends dt{projectName;static type="project_selected";constructor(e){super(e,n.type),this.projectName=e.projectName}},Mr=class n extends dt{threadId;threadName;static type="thread_selected";constructor(e){super(e,n.type),this.threadId=e.threadId,this.threadName=e.threadName}},Ti=class n extends dt{static type="thinking";static debounce=5e3;constructor(e){super(e,n.type)}},Bo=class n extends dt{role;name;content;static type="message";constructor(e){super(e,n.type),this.role=e.role,this.name=e.name,this.content=e.content,this.length=this.content.map(t=>{if(t.type==="text")return t.content.length;if(t.type==="image"){let r=(t.width||0)*(t.height||0)/750;return r?r*3.5:t.content.length}return 0}).reduce((t,r)=>t+r,0)}getTextContent(){return this.content.filter(e=>e.type==="text").map(e=>e.content).join(`
73
- `)}},fO={[Bo.type]:Bo,[Tr.type]:Tr,[Vo.type]:Vo,[Kn.type]:Kn,[ko.type]:ko,[Po.type]:Po,[Ir.type]:Ir,[Mr.type]:Mr,[jo.type]:jo,[Si.type]:Si,[Fo.type]:Fo,[Ti.type]:Ti,[Lo.type]:Lo};function Nw(n){let e=fO[n.type];return e?new e(n):void 0}var Cn=class n{clientId;http=p($c);constructor(){this.clientId=this.getOrCreateClientId()}getOrCreateClientId(){let t=new URLSearchParams(window.location.search).get("clientId");if(!t){t=Math.random().toString(36).substring(2,15);let r=new URL(window.location.href);r.searchParams.set("clientId",t),window.history.pushState({},"",r)}return t}getClientId(){return this.clientId}sendEvent(e){return this.http.post(`/api/message?clientId=${this.clientId}`,e,{observe:"response",responseType:"text"}).pipe(ge(t=>{t.status!==200&&console.warn("[API] Unexpected status:",t.status)}),j(t=>t.body))}stopExecution(){return this.http.post(`/api/stop?clientId=${this.clientId}`,{})}getEventDetails(e){return this.http.get(`/api/event/${e}?clientId=${this.clientId}`,{responseType:"text"})}getEventsUrl(){return`/events?clientId=${this.clientId}`}getSessionState(){return this.http.get(`/api/session/state?clientId=${this.clientId}`).pipe(ge(e=>console.log("[API] Session state received:",e)))}deleteMessage(e){let t=`/api/thread/message/${encodeURIComponent(e)}?clientId=${this.clientId}`;return this.http.delete(t).pipe(ge(r=>console.log("[API] Delete message response:",r)))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=C({token:n,factory:n.\u0275fac,providedIn:"root"})};var $o=class n{eventSource=null;eventsSubject=new H;connectionStatusSubject=new V({connected:!1,reconnectAttempts:0,maxAttempts:3});reconnectAttempts=0;MAX_RECONNECT_ATTEMPTS=3;RECONNECT_DELAY=2e3;events$=this.eventsSubject.asObservable();connectionStatus$=this.connectionStatusSubject.asObservable();codayApi=p(Cn);ngZone=p(me);connect(){console.log("[SSE] Setting up new EventSource"),this.eventSource&&(console.log("[SSE] Closing existing EventSource"),this.eventSource.close());let e=this.codayApi.getEventsUrl();this.eventSource=new EventSource(e),this.eventSource.onmessage=t=>{this.ngZone.run(()=>{console.log("[SSE] Message received:",t.data.substring(0,100)),this.reconnectAttempts=0,this.updateConnectionStatus(!0,0);try{let r=JSON.parse(t.data),i=Nw(r);i?(console.log("[SSE] Event:",i.type),this.eventsSubject.next(i)):console.warn("[SSE] Failed to build event:",r.type)}catch(r){console.error("[SSE] Parse error:",r.message)}})},this.eventSource.onopen=()=>{this.ngZone.run(()=>{console.log("[SSE] Connection established"),this.reconnectAttempts=0,this.updateConnectionStatus(!0,0)})},this.eventSource.onerror=t=>{this.ngZone.run(()=>{console.log("[SSE] EventSource error:",t),this.eventSource?.readyState===EventSource.CLOSED&&(console.log("[SSE] Connection closed"),this.updateConnectionStatus(!1,this.reconnectAttempts),this.reconnectAttempts<this.MAX_RECONNECT_ATTEMPTS?(console.log(`[SSE] Attempting reconnect ${this.reconnectAttempts+1}/${this.MAX_RECONNECT_ATTEMPTS}`),this.eventsSubject.next(new Kn({error:new Error(`Connection lost. Attempting to reconnect (${this.reconnectAttempts+1}/${this.MAX_RECONNECT_ATTEMPTS})...`)})),setTimeout(()=>{this.reconnectAttempts++,this.connect()},this.RECONNECT_DELAY)):(console.log("[SSE] Max reconnection attempts reached"),this.eventsSubject.next(new Kn({error:new Error("Connection lost permanently. Please refresh the page.")}))))})}}disconnect(){this.eventSource&&(console.log("[SSE] Disconnecting EventSource"),this.eventSource.close(),this.eventSource=null,this.updateConnectionStatus(!1,0))}isConnected(){return this.connectionStatusSubject.value.connected}updateConnectionStatus(e,t){this.connectionStatusSubject.next({connected:e,reconnectAttempts:t,maxAttempts:this.MAX_RECONNECT_ATTEMPTS})}ngOnDestroy(){this.disconnect(),this.eventsSubject.complete(),this.connectionStatusSubject.complete()}static \u0275fac=function(t){return new(t||n)};static \u0275prov=C({token:n,factory:n.\u0275fac,providedIn:"root"})};var ft=class n{destroy$=new H;messagesSubject=new V([]);isThinkingSubject=new V(!1);currentChoiceSubject=new V(null);projectTitleSubject=new V("Coday");currentInviteEventSubject=new V(null);messageToRestoreSubject=new V("");currentChoiceEvent=null;thinkingTimeout=null;messages$=this.messagesSubject.asObservable();isThinking$=this.isThinkingSubject.asObservable();currentChoice$=this.currentChoiceSubject.asObservable();projectTitle$=this.projectTitleSubject.asObservable();currentInviteEvent$=this.currentInviteEventSubject.asObservable();messageToRestore$=this.messageToRestoreSubject.asObservable();connectionStatus$;tabTitleService=null;codayApi=p(Cn);eventStream=p($o);constructor(){this.connectionStatus$=this.eventStream.connectionStatus$,this.initializeEventHandling()}setTabTitleService(e){this.tabTitleService=e}start(){this.eventStream.connect()}stop(){this.codayApi.stopExecution().subscribe({next:()=>console.log("[CODAY] Stop signal sent"),error:e=>console.error("[CODAY] Error stopping:",e)})}resetMessages(){console.log("[CODAY] Resetting messages for context change"),this.messagesSubject.next([]),this.currentChoiceSubject.next(null),this.currentInviteEventSubject.next(null),this.currentChoiceEvent=null,this.stopThinking()}sendMessage(e){let t=this.currentInviteEventSubject.value;if(t){let r=t.buildAnswer(e);this.currentInviteEventSubject.next(null),this.codayApi.sendEvent(r).subscribe({error:i=>console.error("[CODAY] Send error:",i)})}else{let r=new Tr({answer:e});this.codayApi.sendEvent(r).subscribe({error:i=>console.error("[CODAY] Send error:",i)})}}sendChoice(e){if(this.currentChoiceEvent){let t=this.currentChoiceEvent.buildAnswer(e);this.codayApi.sendEvent(t).subscribe({next:()=>{this.currentChoiceSubject.next(null)},error:r=>console.error("[CODAY] Choice error:",r)})}else console.error("[CODAY] No choice event available")}deleteMessage(e){console.log("[CODAY] Deleting message:",e);let t=this.messagesSubject.value.find(i=>i.id===e),r=this.extractTextContentFromMessage(t);return this.codayApi.deleteMessage(e).pipe(ge(i=>{i.success?(console.log("[CODAY] Message deleted successfully, updating local messages"),this.removeMessagesFromIndex(e),r.trim()&&(console.log("[CODAY] Restoring deleted message content to textarea"),this.messageToRestoreSubject.next(r))):console.warn("[CODAY] Failed to delete message:",i.error)}))}getCurrentMessages(){return this.messagesSubject.value}getCurrentProjectTitle(){return this.projectTitleSubject.value}getCurrentInviteEvent(){return this.currentInviteEventSubject.value}initializeEventHandling(){this.eventStream.events$.pipe(K(this.destroy$)).subscribe({next:e=>this.handleEvent(e),error:e=>console.error("[CODAY] Event stream error:",e),complete:()=>console.log("[CODAY] Event stream completed")})}handleEvent(e){e instanceof Bo?this.handleMessageEvent(e):e instanceof Fo?this.handleTextEvent(e):e instanceof Tr?this.handleAnswerEvent(e):e instanceof Kn?this.handleErrorEvent(e):e instanceof Lo?this.handleWarnEvent(e):e instanceof Ti?this.handleThinkingEvent(e):e instanceof jo?this.handleToolRequestEvent(e):e instanceof Si?this.handleToolResponseEvent(e):e instanceof Vo?this.handleChoiceEvent(e):e instanceof Ir?this.handleProjectSelectedEvent(e):e instanceof Mr?this.handleThreadSelectedEvent(e):e instanceof ko?this.handleHeartBeatEvent(e):e instanceof Po?this.handleInviteEvent(e):console.warn("[CODAY] Unhandled event type:",e.type)}handleMessageEvent(e){let t={id:e.timestamp,role:e.role,speaker:e.name,content:e.content,timestamp:new Date,type:"text"};this.addMessage(t)}handleTextEvent(e){let t={id:e.timestamp,role:e.speaker?"assistant":"system",speaker:e.speaker||"System",content:[{type:"text",content:e.text}],timestamp:new Date,type:e.speaker?"text":"technical"};this.addMessage(t)}handleAnswerEvent(e){let t={id:e.timestamp,role:"user",speaker:"User",content:[{type:"text",content:e.answer}],timestamp:new Date,type:"text"};this.addMessage(t)}handleErrorEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:`Error: ${JSON.stringify(e.error)}`}],timestamp:new Date,type:"error"};this.addMessage(t)}handleWarnEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:`Warning: ${JSON.stringify(e.warning)}`}],timestamp:new Date,type:"warning"};this.addMessage(t)}handleThinkingEvent(e){this.clearThinkingTimeout(),this.isThinkingSubject.next(!0),this.tabTitleService?.setSystemActive(),this.thinkingTimeout=setTimeout(()=>{this.isThinkingSubject.next(!1),this.thinkingTimeout=null,this.tabTitleService?.setSystemInactive()},Ti.debounce+1e3)}handleToolRequestEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:e.toSingleLineString()}],timestamp:new Date,type:"technical",eventId:e.timestamp};this.addMessage(t)}handleToolResponseEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:e.toSingleLineString()}],timestamp:new Date,type:"technical",eventId:e.timestamp};this.addMessage(t)}handleChoiceEvent(e){this.stopThinking(),this.currentChoiceEvent=e,this.tabTitleService?.setSystemInactive();let t=e.options.map(i=>({value:i,label:i})),r=e.optionalQuestion?`${e.optionalQuestion} ${e.invite}`:e.invite;this.currentChoiceSubject.next({options:t,label:r})}handleProjectSelectedEvent(e){console.log("[CODAY] Project selected:",e.projectName),this.resetMessages(),this.projectTitleSubject.next(e.projectName||"Coday")}handleThreadSelectedEvent(e){console.log("[CODAY] Thread selected:",e.threadId,e.threadName),this.resetMessages()}handleHeartBeatEvent(e){}handleInviteEvent(e){this.stopThinking(),this.currentInviteEventSubject.next(e),this.tabTitleService?.setSystemInactive()}addMessage(e){let r=[...this.messagesSubject.value,e];this.messagesSubject.next(r)}removeMessagesFromIndex(e){let t=this.messagesSubject.value,r=t.findIndex(o=>o.id===e);if(r===-1){console.warn("[CODAY] Message not found for local deletion:",e);return}if(r===0){console.warn("[CODAY] Cannot delete first message locally");return}let i=t.slice(0,r);console.log(`[CODAY] Locally removed ${t.length-i.length} messages from index ${r}`),this.messagesSubject.next(i),this.stopThinking()}extractTextContentFromMessage(e){return e?e.content.filter(t=>t.type==="text").map(t=>t.content).join(`
73
+ `)}},fO={[Bo.type]:Bo,[Tr.type]:Tr,[Vo.type]:Vo,[Kn.type]:Kn,[ko.type]:ko,[Po.type]:Po,[Ir.type]:Ir,[Mr.type]:Mr,[jo.type]:jo,[Si.type]:Si,[Fo.type]:Fo,[Ti.type]:Ti,[Lo.type]:Lo};function Nw(n){let e=fO[n.type];return e?new e(n):void 0}var Cn=class n{clientId;http=p($c);constructor(){this.clientId=this.getOrCreateClientId()}getOrCreateClientId(){let t=new URLSearchParams(window.location.search).get("clientId");if(!t){t=Math.random().toString(36).substring(2,15);let r=new URL(window.location.href);r.searchParams.set("clientId",t),window.history.pushState({},"",r)}return t}getClientId(){return this.clientId}sendEvent(e){return this.http.post(`/api/message?clientId=${this.clientId}`,e,{observe:"response",responseType:"text"}).pipe(ge(t=>{t.status!==200&&console.warn("[API] Unexpected status:",t.status)}),j(t=>t.body))}stopExecution(){return this.http.post(`/api/stop?clientId=${this.clientId}`,{})}getEventDetails(e){return this.http.get(`/api/event/${e}?clientId=${this.clientId}`,{responseType:"text"})}getEventsUrl(){return`/events?clientId=${this.clientId}`}getSessionState(){return this.http.get(`/api/session/state?clientId=${this.clientId}`).pipe(ge(e=>console.log("[API] Session state received:",e)))}deleteMessage(e){let t=`/api/thread/message/${encodeURIComponent(e)}?clientId=${this.clientId}`;return this.http.delete(t).pipe(ge(r=>console.log("[API] Delete message response:",r)))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=C({token:n,factory:n.\u0275fac,providedIn:"root"})};var $o=class n{eventSource=null;eventsSubject=new H;connectionStatusSubject=new V({connected:!1,reconnectAttempts:0,maxAttempts:3});reconnectAttempts=0;MAX_RECONNECT_ATTEMPTS=3;RECONNECT_DELAY=2e3;events$=this.eventsSubject.asObservable();connectionStatus$=this.connectionStatusSubject.asObservable();codayApi=p(Cn);ngZone=p(me);connect(){console.log("[SSE] Setting up new EventSource"),this.eventSource&&(console.log("[SSE] Closing existing EventSource"),this.eventSource.close());let e=this.codayApi.getEventsUrl();this.eventSource=new EventSource(e),this.eventSource.onmessage=t=>{this.ngZone.run(()=>{console.log("[SSE] Message received:",t.data.substring(0,100)),this.reconnectAttempts=0,this.updateConnectionStatus(!0,0);try{let r=JSON.parse(t.data),i=Nw(r);i?(console.log("[SSE] Event:",i.type),this.eventsSubject.next(i)):console.warn("[SSE] Failed to build event:",r.type)}catch(r){console.error("[SSE] Parse error:",r.message)}})},this.eventSource.onopen=()=>{this.ngZone.run(()=>{console.log("[SSE] Connection established"),this.reconnectAttempts=0,this.updateConnectionStatus(!0,0)})},this.eventSource.onerror=t=>{this.ngZone.run(()=>{console.log("[SSE] EventSource error:",t),this.eventSource?.readyState===EventSource.CLOSED&&(console.log("[SSE] Connection closed"),this.updateConnectionStatus(!1,this.reconnectAttempts),this.reconnectAttempts<this.MAX_RECONNECT_ATTEMPTS?(console.log(`[SSE] Attempting reconnect ${this.reconnectAttempts+1}/${this.MAX_RECONNECT_ATTEMPTS}`),this.eventsSubject.next(new Kn({error:new Error(`Connection lost. Attempting to reconnect (${this.reconnectAttempts+1}/${this.MAX_RECONNECT_ATTEMPTS})...`)})),setTimeout(()=>{this.reconnectAttempts++,this.connect()},this.RECONNECT_DELAY)):(console.log("[SSE] Max reconnection attempts reached"),this.eventsSubject.next(new Kn({error:new Error("Connection lost permanently. Please refresh the page.")}))))})}}disconnect(){this.eventSource&&(console.log("[SSE] Disconnecting EventSource"),this.eventSource.close(),this.eventSource=null,this.updateConnectionStatus(!1,0))}isConnected(){return this.connectionStatusSubject.value.connected}updateConnectionStatus(e,t){this.connectionStatusSubject.next({connected:e,reconnectAttempts:t,maxAttempts:this.MAX_RECONNECT_ATTEMPTS})}ngOnDestroy(){this.disconnect(),this.eventsSubject.complete(),this.connectionStatusSubject.complete()}static \u0275fac=function(t){return new(t||n)};static \u0275prov=C({token:n,factory:n.\u0275fac,providedIn:"root"})};var ft=class n{destroy$=new H;messagesSubject=new V([]);isThinkingSubject=new V(!1);currentChoiceSubject=new V(null);projectTitleSubject=new V("Coday");currentInviteEventSubject=new V(null);messageToRestoreSubject=new V("");currentChoiceEvent=null;thinkingTimeout=null;messages$=this.messagesSubject.asObservable();isThinking$=this.isThinkingSubject.asObservable();currentChoice$=this.currentChoiceSubject.asObservable();projectTitle$=this.projectTitleSubject.asObservable();currentInviteEvent$=this.currentInviteEventSubject.asObservable();messageToRestore$=this.messageToRestoreSubject.asObservable();connectionStatus$;tabTitleService=null;codayApi=p(Cn);eventStream=p($o);constructor(){this.connectionStatus$=this.eventStream.connectionStatus$,this.initializeEventHandling()}setTabTitleService(e){this.tabTitleService=e}start(){this.eventStream.connect()}stop(){this.codayApi.stopExecution().subscribe({next:()=>console.log("[CODAY] Stop signal sent"),error:e=>console.error("[CODAY] Error stopping:",e)})}resetMessages(){console.log("[CODAY] Resetting messages for context change"),this.messagesSubject.next([]),this.currentChoiceSubject.next(null),this.currentInviteEventSubject.next(null),this.currentChoiceEvent=null,this.stopThinking()}sendMessage(e){let t=this.currentInviteEventSubject.value;if(t){let r=t.buildAnswer(e);this.currentInviteEventSubject.next(null),this.codayApi.sendEvent(r).subscribe({error:i=>console.error("[CODAY] Send error:",i)})}else{let r=new Tr({answer:e});this.codayApi.sendEvent(r).subscribe({error:i=>console.error("[CODAY] Send error:",i)})}}sendChoice(e){if(this.currentChoiceEvent){let t=this.currentChoiceEvent.buildAnswer(e);console.log("[CODAY-CHOICE] Choice sent successfully, clearing UI"),this.currentChoiceSubject.next(null),this.currentChoiceEvent=null,this.codayApi.sendEvent(t).subscribe({next:()=>{},error:r=>{console.error("[CODAY-CHOICE] Choice error:",r)}})}else console.error("[CODAY-CHOICE] No choice event available for choice:",e)}deleteMessage(e){console.log("[CODAY] Deleting message:",e);let t=this.messagesSubject.value.find(i=>i.id===e),r=this.extractTextContentFromMessage(t);return this.codayApi.deleteMessage(e).pipe(ge(i=>{i.success?(console.log("[CODAY] Message deleted successfully, updating local messages"),this.removeMessagesFromIndex(e),r.trim()&&(console.log("[CODAY] Restoring deleted message content to textarea"),this.messageToRestoreSubject.next(r))):console.warn("[CODAY] Failed to delete message:",i.error)}))}getCurrentMessages(){return this.messagesSubject.value}getCurrentProjectTitle(){return this.projectTitleSubject.value}getCurrentInviteEvent(){return this.currentInviteEventSubject.value}initializeEventHandling(){this.eventStream.events$.pipe(K(this.destroy$)).subscribe({next:e=>this.handleEvent(e),error:e=>console.error("[CODAY] Event stream error:",e),complete:()=>console.log("[CODAY] Event stream completed")})}handleEvent(e){e instanceof Bo?this.handleMessageEvent(e):e instanceof Fo?this.handleTextEvent(e):e instanceof Tr?this.handleAnswerEvent(e):e instanceof Kn?this.handleErrorEvent(e):e instanceof Lo?this.handleWarnEvent(e):e instanceof Ti?this.handleThinkingEvent(e):e instanceof jo?this.handleToolRequestEvent(e):e instanceof Si?this.handleToolResponseEvent(e):e instanceof Vo?this.handleChoiceEvent(e):e instanceof Ir?this.handleProjectSelectedEvent(e):e instanceof Mr?this.handleThreadSelectedEvent(e):e instanceof ko?this.handleHeartBeatEvent(e):e instanceof Po?this.handleInviteEvent(e):console.warn("[CODAY] Unhandled event type:",e.type)}handleMessageEvent(e){let t={id:e.timestamp,role:e.role,speaker:e.name,content:e.content,timestamp:new Date,type:"text"};this.addMessage(t)}handleTextEvent(e){let t={id:e.timestamp,role:e.speaker?"assistant":"system",speaker:e.speaker||"System",content:[{type:"text",content:e.text}],timestamp:new Date,type:e.speaker?"text":"technical"};this.addMessage(t)}handleAnswerEvent(e){let t={id:e.timestamp,role:"user",speaker:"User",content:[{type:"text",content:e.answer}],timestamp:new Date,type:"text"};this.addMessage(t)}handleErrorEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:`Error: ${JSON.stringify(e.error)}`}],timestamp:new Date,type:"error"};this.addMessage(t)}handleWarnEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:`Warning: ${JSON.stringify(e.warning)}`}],timestamp:new Date,type:"warning"};this.addMessage(t)}handleThinkingEvent(e){this.clearThinkingTimeout(),this.isThinkingSubject.next(!0),this.tabTitleService?.setSystemActive(),this.thinkingTimeout=setTimeout(()=>{this.isThinkingSubject.next(!1),this.thinkingTimeout=null,this.tabTitleService?.setSystemInactive()},Ti.debounce+1e3)}handleToolRequestEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:e.toSingleLineString()}],timestamp:new Date,type:"technical",eventId:e.timestamp};this.addMessage(t)}handleToolResponseEvent(e){let t={id:e.timestamp,role:"system",speaker:"System",content:[{type:"text",content:e.toSingleLineString()}],timestamp:new Date,type:"technical",eventId:e.timestamp};this.addMessage(t)}handleChoiceEvent(e){this.stopThinking(),this.currentChoiceEvent=e,this.tabTitleService?.setSystemInactive();let t=e.options.map(i=>({value:i,label:i})),r=e.optionalQuestion?`${e.optionalQuestion} ${e.invite}`:e.invite;this.currentChoiceSubject.next({options:t,label:r})}handleProjectSelectedEvent(e){console.log("[CODAY] Project selected:",e.projectName),this.resetMessages(),this.projectTitleSubject.next(e.projectName||"Coday")}handleThreadSelectedEvent(e){console.log("[CODAY] Thread selected:",e.threadId,e.threadName),this.resetMessages()}handleHeartBeatEvent(e){}handleInviteEvent(e){this.stopThinking(),this.currentInviteEventSubject.next(e),this.tabTitleService?.setSystemInactive()}addMessage(e){let r=[...this.messagesSubject.value,e];this.messagesSubject.next(r)}removeMessagesFromIndex(e){let t=this.messagesSubject.value,r=t.findIndex(o=>o.id===e);if(r===-1){console.warn("[CODAY] Message not found for local deletion:",e);return}if(r===0){console.warn("[CODAY] Cannot delete first message locally");return}let i=t.slice(0,r);console.log(`[CODAY] Locally removed ${t.length-i.length} messages from index ${r}`),this.messagesSubject.next(i),this.stopThinking()}extractTextContentFromMessage(e){return e?e.content.filter(t=>t.type==="text").map(t=>t.content).join(`
74
74
 
75
75
  `):""}clearThinkingTimeout(){this.thinkingTimeout&&(clearTimeout(this.thinkingTimeout),this.thinkingTimeout=null)}stopThinking(){this.clearThinkingTimeout(),this.isThinkingSubject.next(!1),this.tabTitleService&&this.tabTitleService.setSystemInactive()}ngOnDestroy(){this.clearThinkingTimeout(),this.destroy$.next(),this.destroy$.complete(),this.currentInviteEventSubject.complete(),this.messageToRestoreSubject.complete(),this.eventStream.disconnect()}static \u0275fac=function(t){return new(t||n)};static \u0275prov=C({token:n,factory:n.\u0275fac,providedIn:"root"})};function hO(n,e){if(n&1){let t=de();v(0,"app-chat-message",4),M("playRequested",function(i){S(t);let o=I();return T(o.onPlayMessage(i))})("copyRequested",function(i){S(t);let o=I();return T(o.onCopyMessage(i))})("deleteRequested",function(i){S(t);let o=I();return T(o.onDeleteMessage(i))}),m()}if(n&2){let t=e.$implicit,r=e.index,i=I();A("message",t)("canDelete",i.canDeleteMessage(r))}}function pO(n,e){if(n&1){let t=de();v(0,"div",5)(1,"div",6),ue(2,"span")(3,"span")(4,"span"),m(),v(5,"button",7),M("click",function(){S(t);let i=I();return T(i.onStop())}),R(6," Stop "),m()()}}function gO(n,e){if(n&1){let t=de();v(0,"button",8),M("click",function(){S(t);let i=I();return T(i.goToBottom())}),R(1," \u2193 "),m()}}var Wu=class n{messages=[];isThinking=!1;playRequested=new ne;copyRequested=new ne;stopRequested=new ne;lastMessageCount=0;destroy$=new H;isTracking=!0;showGoToBottom=!1;scrollContainer=null;lastScrollTop=0;NEAR_BOTTOM_THRESHOLD=100;scrollCheckTimeout;MESSAGE_FRESHNESS_THRESHOLD=5*60*1e3;elementRef=p(He);unreadService=p(Oo);voiceSynthesisService=p(Sr);preferencesService=p(Pt);codayService=p(ft);ngOnInit(){this.findScrollContainer(),this.setupFocusListeners()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),this.scrollCheckTimeout&&clearTimeout(this.scrollCheckTimeout),window.removeEventListener("focus",this.handleWindowFocus),window.removeEventListener("blur",this.handleWindowBlur)}ngAfterViewChecked(){if(this.messages.length!==this.lastMessageCount){console.log("[CHAT-HISTORY] Message count changed:",this.lastMessageCount,"->",this.messages.length);let e=this.messages.length-this.lastMessageCount;e>0&&(this.handleNewMessages(e),this.handleVoiceAnnouncement(e)),this.lastMessageCount=this.messages.length,this.isTracking&&this.scrollToBottom()}this.scrollContainer||this.findScrollContainer()}scrollToBottom(){try{this.scrollContainer||this.findScrollContainer(),this.scrollContainer&&setTimeout(()=>{this.scrollContainer&&this.scrollContainer.scrollTo({top:this.scrollContainer.scrollHeight,behavior:"smooth"})},50)}catch(e){console.error("Error scrolling to bottom:",e)}}trackByMessageId(e,t){return t.id}onPlayMessage(e){this.playRequested.emit(e)}onCopyMessage(e){let t=e.content.filter(r=>r.type==="text").map(r=>r.content).join(`
76
76
 
package/package.json CHANGED
@@ -7,5 +7,5 @@
7
7
  "dependencies": {
8
8
  "sharp": "^0.33.5"
9
9
  },
10
- "version": "0.18.1"
10
+ "version": "0.18.3"
11
11
  }
package/server/server.js CHANGED
@@ -69190,6 +69190,9 @@ var AiThread = class _AiThread {
69190
69190
  parentThread;
69191
69191
  /** Internal storage of thread messages in chronological order */
69192
69192
  messages;
69193
+ get messagesLength() {
69194
+ return this.messages.length;
69195
+ }
69193
69196
  /**
69194
69197
  * Creates a new AiThread instance.
69195
69198
  * @param thread - Configuration object containing thread ID and optional message history
@@ -69223,21 +69226,16 @@ var AiThread = class _AiThread {
69223
69226
  if (!this.messages || !Array.isArray(this.messages)) {
69224
69227
  this.messages = [];
69225
69228
  }
69226
- if (!maxChars) return { messages: [...this.messages], compacted: false };
69227
- let { messages, overflow } = partition(this.messages.toReversed(), maxChars);
69228
- messages = messages.toReversed();
69229
- overflow = overflow.toReversed();
69230
- const messageIds = new Set(messages.map((m2) => m2.timestamp));
69231
- for (let i2 = 0; i2 < messages.length; i2++) {
69232
- const message = messages[i2];
69233
- if (message instanceof ToolResponseEvent && !messageIds.has(message.parentKey ?? "no_parent_key")) {
69234
- messages.splice(i2, 1);
69235
- overflow.push(message);
69236
- }
69229
+ if (!maxChars) {
69230
+ const cleanedMessages2 = this.cleanToolRequestResponseConsistency([...this.messages]);
69231
+ return { messages: cleanedMessages2, compacted: false };
69237
69232
  }
69233
+ let { messages, overflow } = partition(this.messages.toReversed(), maxChars);
69234
+ messages = this.cleanToolRequestResponseConsistency(messages.toReversed());
69235
+ overflow = this.cleanToolRequestResponseConsistency(overflow.toReversed());
69238
69236
  if (!compactor) {
69239
- this.messages = messages;
69240
- return { messages, compacted: true };
69237
+ this.messages = this.cleanToolRequestResponseConsistency(messages);
69238
+ return { messages: this.messages, compacted: true };
69241
69239
  }
69242
69240
  let summary;
69243
69241
  while (overflow.length) {
@@ -69245,12 +69243,54 @@ var AiThread = class _AiThread {
69245
69243
  summary = await compactor(overflowPartition.messages);
69246
69244
  overflow = overflowPartition.overflow.length ? [summary, ...overflowPartition.overflow] : [];
69247
69245
  }
69248
- this.messages = summary ? [summary, ...messages] : messages;
69246
+ const cleanedMessages = this.cleanToolRequestResponseConsistency(messages);
69247
+ this.messages = summary ? [summary, ...cleanedMessages] : cleanedMessages;
69249
69248
  return {
69250
- messages,
69249
+ messages: cleanedMessages,
69251
69250
  compacted: true
69252
69251
  };
69253
69252
  }
69253
+ /**
69254
+ * Cleans tool request-response consistency to prevent API errors.
69255
+ * Removes orphaned tool requests/responses that would cause Anthropic API 400 errors.
69256
+ *
69257
+ * @param messages Array of messages to clean
69258
+ * @returns Cleaned array of messages
69259
+ * @private
69260
+ */
69261
+ cleanToolRequestResponseConsistency(messages) {
69262
+ const cleanedMessages = [];
69263
+ const toolRequestIds = /* @__PURE__ */ new Set();
69264
+ const toolResponseIds = /* @__PURE__ */ new Set();
69265
+ for (const message of messages) {
69266
+ if (message instanceof ToolRequestEvent && message.toolRequestId) {
69267
+ toolRequestIds.add(message.toolRequestId);
69268
+ } else if (message instanceof ToolResponseEvent && message.toolRequestId) {
69269
+ toolResponseIds.add(message.toolRequestId);
69270
+ }
69271
+ }
69272
+ for (const message of messages) {
69273
+ let shouldKeep = true;
69274
+ if (message instanceof ToolRequestEvent) {
69275
+ if (message.toolRequestId && !toolResponseIds.has(message.toolRequestId)) {
69276
+ console.debug(`[AiThread] Removing orphaned tool request: ${message.name} (ID: ${message.toolRequestId})`);
69277
+ shouldKeep = false;
69278
+ }
69279
+ } else if (message instanceof ToolResponseEvent) {
69280
+ if (!message.toolRequestId) {
69281
+ console.warn(`[AiThread] Removing tool response without tool reference (timestamp: ${message.timestamp})`);
69282
+ shouldKeep = false;
69283
+ } else if (!toolRequestIds.has(message.toolRequestId)) {
69284
+ console.debug(`[AiThread] Removing orphaned tool response for missing request ID: ${message.toolRequestId}`);
69285
+ shouldKeep = false;
69286
+ }
69287
+ }
69288
+ if (shouldKeep) {
69289
+ cleanedMessages.push(message);
69290
+ }
69291
+ }
69292
+ return cleanedMessages;
69293
+ }
69254
69294
  /**
69255
69295
  * Resets the counters related to the run (all except price.thread)
69256
69296
  */
@@ -69625,7 +69665,8 @@ var AiThreadService = class {
69625
69665
  }
69626
69666
  async autoSave(newName) {
69627
69667
  const thread = this.activeThread$.value;
69628
- if (!thread) {
69668
+ if (!thread || thread.messagesLength == 0) {
69669
+ console.error(`Autosave of an empty or falsy thread aborted, threadId: ${thread?.id}`);
69629
69670
  return;
69630
69671
  }
69631
69672
  if (newName) {
@@ -97743,7 +97784,7 @@ var OPENAI_DEFAULT_MODELS = [
97743
97784
  }
97744
97785
  }
97745
97786
  ];
97746
- var OpenaiClient = class extends AiClient {
97787
+ var OpenaiClient = class _OpenaiClient extends AiClient {
97747
97788
  constructor(interactor, aiProviderConfig, logger2) {
97748
97789
  super(aiProviderConfig, logger2);
97749
97790
  this.interactor = interactor;
@@ -97754,6 +97795,19 @@ var OpenaiClient = class extends AiClient {
97754
97795
  this.name = aiProviderConfig.name;
97755
97796
  }
97756
97797
  name;
97798
+ static MAX_TOOLS = 128;
97799
+ /**
97800
+ * Truncates the tools list to OpenAI's maximum of 128 tools and warns the user if truncation occurs
97801
+ */
97802
+ truncateToolsIfNeeded(tools) {
97803
+ if (tools.length <= _OpenaiClient.MAX_TOOLS) {
97804
+ return tools;
97805
+ }
97806
+ this.interactor.warn(
97807
+ `\u26A0\uFE0F OpenAI limits tools to ${_OpenaiClient.MAX_TOOLS} maximum. Your agent has ${tools.length} tools. Truncating to first ${_OpenaiClient.MAX_TOOLS} tools. Consider reducing your integrations or using a shorter tool list for better performance.`
97808
+ );
97809
+ return tools.slice(0, _OpenaiClient.MAX_TOOLS);
97810
+ }
97757
97811
  async run(agent, thread) {
97758
97812
  thread.resetUsageForRun();
97759
97813
  const openai = this.isOpenaiReady();
@@ -97810,7 +97864,7 @@ var OpenaiClient = class extends AiClient {
97810
97864
  async processAssistantThread(client, agent, model, thread, subscriber) {
97811
97865
  const assistantStream = client.beta.threads.runs.stream(thread.data.openai.assistantThreadData.threadId, {
97812
97866
  assistant_id: agent.definition.openaiAssistantId,
97813
- tools: [...agent.tools.getTools(), { type: "file_search" }],
97867
+ tools: this.truncateToolsIfNeeded([...agent.tools.getTools(), { type: "file_search" }]),
97814
97868
  tool_choice: "auto",
97815
97869
  max_completion_tokens: 12e4,
97816
97870
  max_prompt_tokens: 12e4,
@@ -97826,7 +97880,7 @@ var OpenaiClient = class extends AiClient {
97826
97880
  const response = await client.chat.completions.create({
97827
97881
  model: model.name,
97828
97882
  messages: this.toOpenAiMessage(agent, data.messages),
97829
- tools: agent.tools.getTools(),
97883
+ tools: this.truncateToolsIfNeeded(agent.tools.getTools()),
97830
97884
  max_completion_tokens: void 0,
97831
97885
  temperature: agent.definition.temperature ?? 0.8
97832
97886
  });
@@ -114671,11 +114725,12 @@ var AgentService = class {
114671
114725
  this.projectPath = projectPath;
114672
114726
  this.commandLineAgentFolders = commandLineAgentFolders;
114673
114727
  this.services.project.selectedProject$.subscribe(() => {
114674
- this.agents.clear();
114728
+ this.agentCache.clear();
114729
+ this.agentDefinitions = [];
114675
114730
  });
114676
114731
  this.toolbox = new Toolbox(this.interactor, services, this);
114677
114732
  }
114678
- agents = /* @__PURE__ */ new Map();
114733
+ agentCache = /* @__PURE__ */ new Map();
114679
114734
  agentDefinitions = [];
114680
114735
  toolbox;
114681
114736
  listAgentSummaries() {
@@ -114691,29 +114746,43 @@ var AgentService = class {
114691
114746
  * - ~/.coday/[project]/agents/ folder
114692
114747
  */
114693
114748
  async initialize(context) {
114694
- if (this.agents.size > 0) return;
114749
+ if (this.agentDefinitions.length > 0) return;
114750
+ const startTime = performance.now();
114751
+ this.interactor.debug("\u{1F680} Starting agent initialization...");
114752
+ this.interactor.debug("\u{1F6E0}\uFE0F Pre-initializing tools in parallel...");
114753
+ this.toolbox.getTools({ context, integrations: void 0, agentName: "pre-init" }).then((_2) => this.interactor.debug("\u{1F6E0}\uFE0F ...completed pre-initializing tools in parallel")).catch((error) => this.interactor.debug(`Pre-initialization warning: ${error.message}`));
114695
114754
  try {
114755
+ const codayYmlStart = performance.now();
114696
114756
  if (context.project.agents?.length) {
114697
114757
  for (const def of context.project.agents) {
114698
114758
  this.addDefinition(def, this.projectPath);
114699
114759
  }
114700
114760
  }
114761
+ const codayYmlTime = performance.now() - codayYmlStart;
114762
+ this.interactor.debug(`\u{1F4CB} Loaded agent definitions from coday.yml: ${codayYmlTime.toFixed(2)}ms (${context.project.agents?.length || 0} agents)`);
114763
+ const projectConfigStart = performance.now();
114701
114764
  const selectedProject = this.services.project.selectedProject;
114702
114765
  if (selectedProject?.config.agents?.length) {
114703
114766
  for (const def of selectedProject.config.agents) {
114704
114767
  this.addDefinition(def, this.projectPath);
114705
114768
  }
114706
114769
  }
114770
+ const projectConfigTime = performance.now() - projectConfigStart;
114771
+ this.interactor.debug(`\u2699\uFE0F Loaded agent definitions from project local config: ${projectConfigTime.toFixed(2)}ms (${selectedProject?.config.agents?.length || 0} agents)`);
114772
+ const filesStart = performance.now();
114707
114773
  await this.loadFromFiles(context);
114708
- if (this.agents.size === 0) {
114774
+ const filesTime = performance.now() - filesStart;
114775
+ this.interactor.debug(`\u{1F4C1} Loaded agent definitions from files: ${filesTime.toFixed(2)}ms`);
114776
+ if (this.agentDefinitions.length === 0) {
114709
114777
  this.addDefinition(CodayAgentDefinition, this.projectPath);
114778
+ this.interactor.debug("\u{1F504} No agent definitions found, using Coday as backup");
114710
114779
  }
114711
114780
  } catch (error) {
114712
- this.interactor.error(`Failed to initialize agents: ${error}`);
114781
+ this.interactor.error(`Failed to initialize agent definitions: ${error}`);
114713
114782
  throw error;
114714
114783
  }
114715
- const allAgents = await Promise.all(this.agentDefinitions.map((entry) => this.tryAddAgent(entry, context)));
114716
- allAgents.filter((agent) => !!agent).forEach((agent) => this.agents.set(agent.name.toLowerCase(), agent));
114784
+ const totalTime = performance.now() - startTime;
114785
+ this.interactor.debug(`\u{1F3AF} Total agent definition loading time: ${totalTime.toFixed(2)}ms`);
114717
114786
  const agentNames = this.listAgentSummaries().map((a2) => ` - ${a2.name} : ${a2.description}`);
114718
114787
  if (agentNames.length > 1) {
114719
114788
  this.interactor.displayText(`Loaded agents (callable with '@[agent name]'):
@@ -114722,13 +114791,26 @@ ${agentNames.join("\n")}`);
114722
114791
  }
114723
114792
  /**
114724
114793
  * Find an agent by exact name match (case insensitive)
114794
+ * Uses lazy loading - creates agent on-demand if not in cache
114725
114795
  */
114726
114796
  async findByName(name, context) {
114727
114797
  await this.initialize(context);
114728
- return this.agents.get(name.toLowerCase());
114798
+ const lowerName = name.toLowerCase();
114799
+ if (this.agentCache.has(lowerName)) {
114800
+ return this.agentCache.get(lowerName);
114801
+ }
114802
+ const entry = this.agentDefinitions.find((e2) => e2.definition.name.toLowerCase() === lowerName);
114803
+ if (entry) {
114804
+ const agent = await this.tryAddAgent(entry, context);
114805
+ if (agent) {
114806
+ this.agentCache.set(lowerName, agent);
114807
+ return agent;
114808
+ }
114809
+ }
114810
+ return void 0;
114729
114811
  }
114730
114812
  async findAgentByNameStart(nameStart, context) {
114731
- if (!nameStart || context.oneshot) {
114813
+ if (!nameStart) {
114732
114814
  return;
114733
114815
  }
114734
114816
  await this.initialize(context);
@@ -114741,6 +114823,9 @@ ${agentNames.join("\n")}`);
114741
114823
  }
114742
114824
  const options = matchingAgents.map((agent) => agent.name);
114743
114825
  try {
114826
+ if (context.oneshot) {
114827
+ throw new Error("Agent ambiguous names not allowed in oneshot context");
114828
+ }
114744
114829
  const selection = await this.interactor.chooseOption(
114745
114830
  options,
114746
114831
  `Multiple agents match '${nameStart}', please select one:`
@@ -114755,11 +114840,29 @@ ${agentNames.join("\n")}`);
114755
114840
  * Find agents by the start of their name (case insensitive)
114756
114841
  * For example, "fid" will match "Fido_the_dog"
114757
114842
  * Returns all matching agents or empty array if none found
114843
+ * Uses lazy loading - creates agents on-demand if not in cache
114758
114844
  */
114759
114845
  async findAgentsByNameStart(nameStart, context) {
114760
114846
  await this.initialize(context);
114761
114847
  const lowerNameStart = nameStart.toLowerCase();
114762
- return Array.from(this.agents.keys()).filter((agentName) => agentName.toLowerCase().startsWith(lowerNameStart)).map((agentName) => this.agents.get(agentName)).filter((agent) => !!agent);
114848
+ const matchingEntries = this.agentDefinitions.filter(
114849
+ (entry) => entry.definition.name.toLowerCase().startsWith(lowerNameStart)
114850
+ );
114851
+ const agents = [];
114852
+ for (const entry of matchingEntries) {
114853
+ const lowerName = entry.definition.name.toLowerCase();
114854
+ let agent = this.agentCache.get(lowerName);
114855
+ if (!agent) {
114856
+ agent = await this.tryAddAgent(entry, context);
114857
+ if (agent) {
114858
+ this.agentCache.set(lowerName, agent);
114859
+ }
114860
+ }
114861
+ if (agent) {
114862
+ agents.push(agent);
114863
+ }
114864
+ }
114865
+ return agents;
114763
114866
  }
114764
114867
  /**
114765
114868
  * Get the user's preferred agent for the current project
@@ -114810,6 +114913,7 @@ ${agentNames.join("\n")}`);
114810
114913
  }
114811
114914
  }
114812
114915
  agentsPaths.push(...this.commandLineAgentFolders);
114916
+ const scanStart = performance.now();
114813
114917
  await Promise.all(
114814
114918
  agentsPaths.map(async (agentsPath) => {
114815
114919
  try {
@@ -114828,6 +114932,9 @@ Consider moving your agent files to a less restricted location.`
114828
114932
  }
114829
114933
  })
114830
114934
  );
114935
+ const scanTime = performance.now() - scanStart;
114936
+ this.interactor.debug(` \u{1F4C2} Scanned agent directories: ${scanTime.toFixed(2)}ms (found ${agentFiles.length} files)`);
114937
+ const parseStart = performance.now();
114831
114938
  await Promise.all(
114832
114939
  agentFiles.map(async (agentFilePath) => {
114833
114940
  try {
@@ -114842,27 +114949,34 @@ Consider moving your agent files to a less restricted location.`
114842
114949
  }
114843
114950
  })
114844
114951
  );
114952
+ const parseTime = performance.now() - parseStart;
114953
+ this.interactor.debug(` \u{1F4DD} Parsed agent files: ${parseTime.toFixed(2)}ms`);
114845
114954
  }
114846
114955
  /**
114847
- * Try to create and add an agent to the map
114956
+ * Try to create and add an agent (lazy loading)
114848
114957
  * Logs error if dependencies are missing
114849
114958
  */
114850
114959
  async tryAddAgent(entry, context) {
114851
114960
  const def = { ...CodayAgentDefinition, ...entry.definition };
114961
+ const agentStart = performance.now();
114852
114962
  try {
114853
114963
  if (def.openaiAssistantId) def.aiProvider = "openai";
114854
114964
  if (!this.aiClientProvider || !this.toolbox) {
114855
114965
  console.error(`Cannot create agent ${def.name}: dependencies not set. Call setDependencies first.`);
114856
114966
  return;
114857
114967
  }
114858
- console.log(`getting client for agent ${def.name}, ${def.aiProvider}, ${def.modelName}`);
114968
+ this.interactor.debug(`\u{1F3D7}\uFE0F Creating agent '${def.name}' on-demand...`);
114969
+ const clientStart = performance.now();
114859
114970
  const aiClient = this.aiClientProvider.getClient(def.aiProvider, def.modelName);
114860
114971
  if (!aiClient) {
114861
114972
  this.interactor.error(`Cannot create agent ${def.name}: AI client creation failed`);
114862
114973
  return;
114863
114974
  }
114975
+ const clientTime = performance.now() - clientStart;
114864
114976
  const basePath = entry.basePath;
114977
+ const docsStart = performance.now();
114865
114978
  const agentDocs = await getFormattedDocs(def, this.interactor, basePath, def.name);
114979
+ const docsTime = performance.now() - docsStart;
114866
114980
  def.instructions = `${def.instructions}
114867
114981
 
114868
114982
 
@@ -114882,9 +114996,14 @@ ${agentDocs}
114882
114996
  return [integration, toolNames];
114883
114997
  })
114884
114998
  ) : void 0;
114999
+ const toolsStart = performance.now();
114885
115000
  const syncTools = await this.toolbox.getTools({ context, integrations, agentName: def.name });
115001
+ const toolsTime = performance.now() - toolsStart;
114886
115002
  const toolset = new ToolSet([...syncTools]);
114887
- return new Agent(def, aiClient, toolset);
115003
+ const agent = new Agent(def, aiClient, toolset);
115004
+ const totalTime = performance.now() - agentStart;
115005
+ this.interactor.debug(`\u2728 Agent '${def.name}' created: ${totalTime.toFixed(2)}ms (client: ${clientTime.toFixed(2)}ms, docs: ${docsTime.toFixed(2)}ms, tools: ${toolsTime.toFixed(2)}ms)`);
115006
+ return agent;
114888
115007
  } catch (error) {
114889
115008
  const errorMessage = `Failed to create agent ${def.name}`;
114890
115009
  console.error(`${errorMessage}:`, error);
@@ -115204,7 +115323,9 @@ var Coday = class {
115204
115323
  continue;
115205
115324
  }
115206
115325
  const thread = this.context?.aiThread;
115207
- if (thread) thread.runStatus = "RUNNING" /* RUNNING */;
115326
+ if (thread) {
115327
+ thread.runStatus = "RUNNING" /* RUNNING */;
115328
+ }
115208
115329
  this.context?.addCommands(userCommand);
115209
115330
  try {
115210
115331
  this.context = await this.handlerLooper?.handle(this.context) ?? null;
@@ -121966,11 +122087,13 @@ app.post("/api/webhook/:uuid", async (req, res) => {
121966
122087
  try {
121967
122088
  const { uuid } = req.params;
121968
122089
  if (!uuid) {
122090
+ debugLog("WEBHOOK", "Missing UUID in request");
121969
122091
  res.status(400).send({ error: "Missing webhook UUID in URL" });
121970
122092
  return;
121971
122093
  }
121972
122094
  const webhook = await webhookService.get(uuid);
121973
122095
  if (!webhook) {
122096
+ debugLog("WEBHOOK", `Webhook not found for UUID: ${uuid}`);
121974
122097
  res.status(404).send({ error: `Webhook with UUID '${uuid}' not found` });
121975
122098
  return;
121976
122099
  }
@@ -122010,28 +122133,30 @@ app.post("/api/webhook/:uuid", async (req, res) => {
122010
122133
  return;
122011
122134
  }
122012
122135
  clientId = `webhook_${Math.random().toString(36).substring(2, 15)}`;
122136
+ const finalPrompts = title ? [`thread save ${title}`, ...prompts] : prompts;
122013
122137
  const oneShotOptions = {
122014
122138
  ...codayOptions,
122015
122139
  oneshot: true,
122016
122140
  // Creates isolated instance that terminates after processing
122017
122141
  project,
122018
122142
  // Target project for the AI agent interaction
122019
- prompts: title ? [`thread save ${title}`, ...prompts] : prompts
122143
+ prompts: finalPrompts
122020
122144
  // Auto-save thread with title + user prompts
122021
122145
  };
122022
122146
  const client = clientManager.getOrCreate(clientId, null, oneShotOptions, username);
122023
122147
  const interactor = client.getInteractor();
122024
122148
  client.startCoday();
122025
- logger.logWebhook({
122149
+ const logData = {
122026
122150
  project,
122027
122151
  title: title || "Untitled",
122028
122152
  username,
122029
122153
  clientId,
122030
- promptCount: prompts.length,
122154
+ promptCount: finalPrompts.length,
122031
122155
  awaitFinalAnswer: !!awaitFinalAnswer,
122032
122156
  webhookName: webhook.name,
122033
122157
  webhookUuid: webhook.uuid
122034
- });
122158
+ };
122159
+ logger.logWebhook(logData);
122035
122160
  const threadIdSource = client.getThreadId();
122036
122161
  if (awaitFinalAnswer) {
122037
122162
  const lastEventObservable = interactor.events.pipe(