@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-
|
|
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
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)
|
|
69227
|
-
|
|
69228
|
-
|
|
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
|
-
|
|
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.
|
|
114728
|
+
this.agentCache.clear();
|
|
114729
|
+
this.agentDefinitions = [];
|
|
114675
114730
|
});
|
|
114676
114731
|
this.toolbox = new Toolbox(this.interactor, services, this);
|
|
114677
114732
|
}
|
|
114678
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
114781
|
+
this.interactor.error(`Failed to initialize agent definitions: ${error}`);
|
|
114713
114782
|
throw error;
|
|
114714
114783
|
}
|
|
114715
|
-
const
|
|
114716
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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:
|
|
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
|
-
|
|
122149
|
+
const logData = {
|
|
122026
122150
|
project,
|
|
122027
122151
|
title: title || "Untitled",
|
|
122028
122152
|
username,
|
|
122029
122153
|
clientId,
|
|
122030
|
-
promptCount:
|
|
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(
|