chainlesschain 0.45.75 → 0.45.77
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/README.md +52 -15
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{Analytics-sBrYoc3A.js → Analytics-Dd2DjBH5.js} +2 -2
- package/src/assets/web-panel/assets/AppLayout-CP9fATUN.js +1 -0
- package/src/assets/web-panel/assets/AppLayout-cxfKLu-m.css +1 -0
- package/src/assets/web-panel/assets/Backup-D6Tc7sf3.js +1 -0
- package/src/assets/web-panel/assets/Chat-DDUJZJ9I.js +1 -0
- package/src/assets/web-panel/assets/Chat-DfR76jyX.css +1 -0
- package/src/assets/web-panel/assets/Cowork-CPqYhoMI.css +1 -0
- package/src/assets/web-panel/assets/Cowork-XRFqGfqJ.js +48 -0
- package/src/assets/web-panel/assets/{Cron-CNs03iHJ.js → Cron-BnWzy_ZB.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DanoHPSI.js → Dashboard-D2vCkoGu.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CCMVr3Y8.js → Git-DYlvK4sh.js} +2 -2
- package/src/assets/web-panel/assets/{Logs-BY6A0UNG.js → Logs-4VgUbfP0.js} +2 -2
- package/src/assets/web-panel/assets/{McpTools-CrBVYlg6.js → McpTools-ChaiHoWY.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-CWx3SpUt.js → Memory-PFtpuOwf.js} +2 -2
- package/src/assets/web-panel/assets/{Notes-1LcGD49x.js → Notes-wc_n6Rh1.js} +2 -2
- package/src/assets/web-panel/assets/{Organization-Dx2DhbkM.js → Organization-D1qUa8NQ.js} +4 -4
- package/src/assets/web-panel/assets/{P2P-B16fjqfJ.js → P2P-DIG2gnR8.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-BQbC9FzG.js → Permissions-CpE-Ar1e.js} +3 -3
- package/src/assets/web-panel/assets/{Projects-CjhZbNYm.js → Projects-GjuS-C6U.js} +2 -2
- package/src/assets/web-panel/assets/{Providers-ivOAQtHM.js → Providers-CCfGeqh_.js} +2 -2
- package/src/assets/web-panel/assets/{RssFeed-BrsErdrU.js → RssFeed-5TkrXK7Z.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DnEvJU5h.js → Security-CcfBWT1D.js} +3 -3
- package/src/assets/web-panel/assets/{Services-7jQywNbl.js → Services-Cnm5Zs5h.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CLlblJcG.js → Skills-BHapMb9h.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-CmJBC1cf.js → Tasks-DPb9OMck.js} +1 -1
- package/src/assets/web-panel/assets/Templates-Dij5t-rf.js +1 -0
- package/src/assets/web-panel/assets/{Wallet-3iYASEx_.js → Wallet-BJV5KmWA.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-s3Hzd9db.js → WebAuthn-DLkvYwSc.js} +5 -5
- package/src/assets/web-panel/assets/{antd-gZyc63Qr.js → antd-BQNxIyr-.js} +82 -82
- package/src/assets/web-panel/assets/github-dark-Dfs9RUU9.css +1 -0
- package/src/assets/web-panel/assets/index-CB5YlndO.js +2 -0
- package/src/assets/web-panel/assets/{markdown-Bv7nG63L.js → markdown-BeVIhIzs.js} +1 -1
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/learning.js +273 -0
- package/src/commands/lowcode.js +23 -8
- package/src/gateways/discord/discord-formatter.js +89 -0
- package/src/gateways/gateway-base.js +189 -0
- package/src/gateways/telegram/telegram-formatter.js +93 -0
- package/src/gateways/ws/action-protocol.js +54 -1
- package/src/gateways/ws/message-dispatcher.js +1 -0
- package/src/gateways/ws/ws-server.js +10 -1
- package/src/index.js +2 -0
- package/src/lib/app-builder.js +136 -8
- package/src/lib/autonomous-agent.js +8 -1
- package/src/lib/cli-context-engineering.js +15 -0
- package/src/lib/cowork-task-runner.js +101 -0
- package/src/lib/cowork-task-templates.js +493 -0
- package/src/lib/execution-backend.js +239 -0
- package/src/lib/hook-manager.js +2 -0
- package/src/lib/iteration-budget.js +175 -0
- package/src/lib/learning/learning-hooks.js +117 -0
- package/src/lib/learning/learning-tables.js +66 -0
- package/src/lib/learning/outcome-feedback.js +243 -0
- package/src/lib/learning/reflection-engine.js +323 -0
- package/src/lib/learning/skill-improver.js +536 -0
- package/src/lib/learning/skill-synthesizer.js +315 -0
- package/src/lib/learning/trajectory-store.js +409 -0
- package/src/lib/plugin-autodiscovery.js +224 -0
- package/src/lib/session-search.js +193 -0
- package/src/lib/sub-agent-context.js +7 -2
- package/src/lib/user-profile.js +172 -0
- package/src/lib/web-ui-server.js +1 -1
- package/src/repl/agent-repl.js +109 -0
- package/src/runtime/agent-core.js +75 -4
- package/src/runtime/coding-agent-contract-shared.cjs +35 -0
- package/src/runtime/coding-agent-policy.cjs +10 -0
- package/src/assets/web-panel/assets/AppLayout-2RCrdXxl.js +0 -1
- package/src/assets/web-panel/assets/AppLayout-D9pBLPC3.css +0 -1
- package/src/assets/web-panel/assets/Backup-D68fenbD.js +0 -1
- package/src/assets/web-panel/assets/Chat-B2nB8o_F.js +0 -1
- package/src/assets/web-panel/assets/Chat-DB46afPg.css +0 -1
- package/src/assets/web-panel/assets/Templates-RXT8-DNk.js +0 -1
- package/src/assets/web-panel/assets/index-CyGtHm63.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./AppLayout-CP9fATUN.js","./vendor-CN0Iv_qZ.js","./ws-CU7Gvoom.js","./_plugin-vue_export-helper-DlAUqK2U.js","./antd-BQNxIyr-.js","./AppLayout-cxfKLu-m.css","./Dashboard-D2vCkoGu.js","./chat-DWBA4-cl.js","./Dashboard-CKeMmCoT.css","./Chat-DDUJZJ9I.js","./markdown-BeVIhIzs.js","./Chat-DfR76jyX.css","./github-dark-Dfs9RUU9.css","./Cowork-XRFqGfqJ.js","./Cowork-CPqYhoMI.css","./Services-Cnm5Zs5h.js","./Services-C8Qs6KXv.css","./Logs-4VgUbfP0.js","./Logs-Gf_Mv9Nx.css","./Skills-BHapMb9h.js","./parsers-DftYMnlk.js","./Skills-BdjRyorN.css","./Providers-CCfGeqh_.js","./Providers-BEakqcO5.css","./McpTools-ChaiHoWY.js","./McpTools-CyhSLDwf.css","./Notes-wc_n6Rh1.js","./Notes-BG69sJKi.css","./Memory-PFtpuOwf.js","./Memory-DRghrGJr.css","./Cron-BnWzy_ZB.js","./Tasks-DPb9OMck.js","./Tasks-BJjN_YEm.css","./Security-CcfBWT1D.js","./Security-Dwxw7rfP.css","./Permissions-CpE-Ar1e.js","./Permissions-C9WlkGl-.css","./P2P-DIG2gnR8.js","./P2P-OEzOeMZX.css","./Git-DYlvK4sh.js","./Git-DGcuBXST.css","./Projects-GjuS-C6U.js","./Projects-DxKelI5h.css","./Wallet-BJV5KmWA.js","./Wallet-DnIumafl.css","./Organization-D1qUa8NQ.js","./Organization-DdOOM4ic.css","./Analytics-Dd2DjBH5.js","./Analytics-B4OM8S8X.css","./Templates-Dij5t-rf.js","./Templates-DOY_oZnm.css","./Backup-D6Tc7sf3.js","./Backup-fZqtfC1m.css","./RssFeed-5TkrXK7Z.js","./RssFeed-BlFC20eg.css","./WebAuthn-DLkvYwSc.js","./WebAuthn-CNPl2VQR.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{S as B,U as L,V as I,f as T,c as E,o as R,W as O,u as k,X as y,Y as V,Z as x,k as D,R as S,_ as w}from"./vendor-CN0Iv_qZ.js";import{a as g,A as M}from"./antd-BQNxIyr-.js";(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))m(t);new MutationObserver(t=>{for(const o of t)if(o.type==="childList")for(const n of o.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&m(n)}).observe(document,{childList:!0,subtree:!0});function c(t){const o={};return t.integrity&&(o.integrity=t.integrity),t.referrerPolicy&&(o.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?o.credentials="include":t.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function m(t){if(t.ep)return;t.ep=!0;const o=c(t);fetch(t.href,o)}})();const C="modulepreload",N=function(s,a){return new URL(s,a).href},P={},e=function(a,c,m){let t=Promise.resolve();if(c&&c.length>0){let b=function(i){return Promise.all(i.map(d=>Promise.resolve(d).then(p=>({status:"fulfilled",value:p}),p=>({status:"rejected",reason:p}))))};const n=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),u=r?.nonce||r?.getAttribute("nonce");t=b(c.map(i=>{if(i=N(i,m),i in P)return;P[i]=!0;const d=i.endsWith(".css"),p=d?'[rel="stylesheet"]':"";if(m)for(let f=n.length-1;f>=0;f--){const _=n[f];if(_.href===i&&(!d||_.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${p}`))return;const l=document.createElement("link");if(l.rel=d?"stylesheet":C,d||(l.as="script"),l.crossOrigin="",l.href=i,u&&l.setAttribute("nonce",u),document.head.appendChild(l),d)return new Promise((f,_)=>{l.addEventListener("load",f),l.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${i}`)))})}))}function o(n){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=n,window.dispatchEvent(r),!r.defaultPrevented)throw n}return t.then(n=>{for(const r of n||[])r.status==="rejected"&&o(r.reason);return a().catch(o)})},U=[{path:"/",component:()=>e(()=>import("./AppLayout-CP9fATUN.js"),__vite__mapDeps([0,1,2,3,4,5]),import.meta.url),children:[{path:"",redirect:"/dashboard"},{path:"dashboard",name:"Dashboard",component:()=>e(()=>import("./Dashboard-D2vCkoGu.js"),__vite__mapDeps([6,1,2,7,3,4,8]),import.meta.url)},{path:"chat",name:"Chat",component:()=>e(()=>import("./Chat-DDUJZJ9I.js"),__vite__mapDeps([9,1,10,4,7,2,3,11,12]),import.meta.url)},{path:"cowork",name:"Cowork",component:()=>e(()=>import("./Cowork-XRFqGfqJ.js"),__vite__mapDeps([13,1,10,4,2,7,3,14,12]),import.meta.url)},{path:"services",name:"Services",component:()=>e(()=>import("./Services-Cnm5Zs5h.js"),__vite__mapDeps([15,2,1,3,4,16]),import.meta.url)},{path:"logs",name:"Logs",component:()=>e(()=>import("./Logs-4VgUbfP0.js"),__vite__mapDeps([17,2,1,3,4,18]),import.meta.url)},{path:"skills",name:"Skills",component:()=>e(()=>import("./Skills-BHapMb9h.js"),__vite__mapDeps([19,1,2,20,7,3,4,21]),import.meta.url)},{path:"providers",name:"Providers",component:()=>e(()=>import("./Providers-CCfGeqh_.js"),__vite__mapDeps([22,1,2,20,3,4,23]),import.meta.url)},{path:"mcp",name:"McpTools",component:()=>e(()=>import("./McpTools-ChaiHoWY.js"),__vite__mapDeps([24,2,1,3,4,25]),import.meta.url)},{path:"notes",name:"Notes",component:()=>e(()=>import("./Notes-wc_n6Rh1.js"),__vite__mapDeps([26,2,1,3,4,27]),import.meta.url)},{path:"memory",name:"Memory",component:()=>e(()=>import("./Memory-PFtpuOwf.js"),__vite__mapDeps([28,2,1,3,4,29]),import.meta.url)},{path:"cron",name:"Cron",component:()=>e(()=>import("./Cron-BnWzy_ZB.js"),__vite__mapDeps([30,2,1,4]),import.meta.url)},{path:"tasks",name:"Tasks",component:()=>e(()=>import("./Tasks-DPb9OMck.js"),__vite__mapDeps([31,1,2,3,4,32]),import.meta.url)},{path:"security",name:"Security",component:()=>e(()=>import("./Security-CcfBWT1D.js"),__vite__mapDeps([33,2,1,3,4,34]),import.meta.url)},{path:"permissions",name:"Permissions",component:()=>e(()=>import("./Permissions-CpE-Ar1e.js"),__vite__mapDeps([35,2,1,3,4,36]),import.meta.url)},{path:"p2p",name:"P2P",component:()=>e(()=>import("./P2P-DIG2gnR8.js"),__vite__mapDeps([37,2,1,3,4,38]),import.meta.url)},{path:"git",name:"Git",component:()=>e(()=>import("./Git-DYlvK4sh.js"),__vite__mapDeps([39,2,1,3,4,40]),import.meta.url)},{path:"projects",name:"Projects",component:()=>e(()=>import("./Projects-GjuS-C6U.js"),__vite__mapDeps([41,2,1,3,4,42]),import.meta.url)},{path:"wallet",name:"Wallet",component:()=>e(()=>import("./Wallet-BJV5KmWA.js"),__vite__mapDeps([43,2,1,3,4,44]),import.meta.url)},{path:"organization",name:"Organization",component:()=>e(()=>import("./Organization-D1qUa8NQ.js"),__vite__mapDeps([45,1,2,3,4,46]),import.meta.url)},{path:"analytics",name:"Analytics",component:()=>e(()=>import("./Analytics-Dd2DjBH5.js"),__vite__mapDeps([47,2,1,3,4,48]),import.meta.url)},{path:"templates",name:"Templates",component:()=>e(()=>import("./Templates-Dij5t-rf.js"),__vite__mapDeps([49,1,2,3,4,50]),import.meta.url)},{path:"backup",name:"Backup",component:()=>e(()=>import("./Backup-D6Tc7sf3.js"),__vite__mapDeps([51,2,1,3,4,52]),import.meta.url)},{path:"rssfeed",name:"RssFeed",component:()=>e(()=>import("./RssFeed-5TkrXK7Z.js"),__vite__mapDeps([53,1,2,3,4,54]),import.meta.url)},{path:"webauthn",name:"WebAuthn",component:()=>e(()=>import("./WebAuthn-DLkvYwSc.js"),__vite__mapDeps([55,2,1,3,4,56]),import.meta.url)}]}],F=B({history:L(),routes:U}),A="cc_theme",v={dark:{label:"暗黑",icon:"🌑",antd:{algorithm:g.darkAlgorithm,token:{colorPrimary:"#1677ff",colorBgBase:"#141414",colorBgContainer:"#1f1f1f",colorBgElevated:"#2a2a2a",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#1c1c1c",headerBg:"#1c1c1c",bodyBg:"#141414"},Menu:{darkItemBg:"#1c1c1c",darkSubMenuItemBg:"#171717"}}},vars:{"--bg-base":"#141414","--bg-sidebar":"#1c1c1c","--bg-header":"#1c1c1c","--bg-card":"#1f1f1f","--bg-card-hover":"#262626","--border-color":"#252525","--border-subtle":"#1e1e1e","--text-primary":"#e0e0e0","--text-secondary":"#888","--text-muted":"#444","--logo-text":"#ffffff","--menu-mode":"dark","--shadow-card":"0 2px 8px rgba(0,0,0,.45)","--group-title":"#3a3a3a"}},light:{label:"亮白",icon:"☀️",antd:{algorithm:g.defaultAlgorithm,token:{colorPrimary:"#1677ff",colorBgBase:"#ffffff",colorBgContainer:"#ffffff",colorBgElevated:"#ffffff",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#ffffff",headerBg:"#ffffff",bodyBg:"#f4f6fb"},Menu:{itemBg:"#ffffff"}}},vars:{"--bg-base":"#f4f6fb","--bg-sidebar":"#ffffff","--bg-header":"#ffffff","--bg-card":"#ffffff","--bg-card-hover":"#f0f4ff","--border-color":"#e8edf5","--border-subtle":"#f0f0f0","--text-primary":"#1a1a2e","--text-secondary":"#5a6474","--text-muted":"#b0b8c8","--logo-text":"#1a1a2e","--menu-mode":"light","--shadow-card":"0 2px 12px rgba(0,0,0,.07)","--group-title":"#aab0bc"}},blue:{label:"深蓝",icon:"🌊",antd:{algorithm:g.darkAlgorithm,token:{colorPrimary:"#2f80ed",colorBgBase:"#0d1117",colorBgContainer:"#161b22",colorBgElevated:"#1c2230",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#0f1923",headerBg:"#0f1923",bodyBg:"#0d1117"},Menu:{darkItemBg:"#0f1923",darkSubMenuItemBg:"#0b1520"}}},vars:{"--bg-base":"#0d1117","--bg-sidebar":"#0f1923","--bg-header":"#0f1923","--bg-card":"#161b22","--bg-card-hover":"#1c2230","--border-color":"#21303f","--border-subtle":"#182030","--text-primary":"#c9d8ef","--text-secondary":"#6e8caa","--text-muted":"#2d4060","--logo-text":"#e0eeff","--menu-mode":"dark","--shadow-card":"0 2px 8px rgba(0,40,80,.5)","--group-title":"#2d4060"}},green:{label:"翠绿",icon:"🌿",antd:{algorithm:g.darkAlgorithm,token:{colorPrimary:"#29a270",colorBgBase:"#0a1a12",colorBgContainer:"#0f2318",colorBgElevated:"#152e20",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#0c1e14",headerBg:"#0c1e14",bodyBg:"#0a1a12"},Menu:{darkItemBg:"#0c1e14",darkSubMenuItemBg:"#091810"}}},vars:{"--bg-base":"#0a1a12","--bg-sidebar":"#0c1e14","--bg-header":"#0c1e14","--bg-card":"#0f2318","--bg-card-hover":"#152e20","--border-color":"#1a3828","--border-subtle":"#122a1c","--text-primary":"#c0e8c8","--text-secondary":"#5a9a6a","--text-muted":"#1e4028","--logo-text":"#d8f0e0","--menu-mode":"dark","--shadow-card":"0 2px 8px rgba(0,40,20,.5)","--group-title":"#1e4028"}}},j=I("theme",()=>{const s=T(localStorage.getItem(A)||"light"),a=E(()=>v[s.value]||v.dark),c=E(()=>a.value.antd),m=E(()=>s.value!=="light");function t(){const r=a.value.vars,u=document.documentElement;for(const[b,i]of Object.entries(r))u.style.setProperty(b,i);u.setAttribute("data-theme",s.value)}function o(r){v[r]&&(s.value=r,localStorage.setItem(A,r),t())}function n(){t()}return{current:s,config:a,antdTheme:c,isDark:m,setTheme:o,init:n}}),W={__name:"App",setup(s){const a=j();return R(()=>a.init()),(c,m)=>{const t=y("router-view"),o=y("a-config-provider");return V(),O(o,{theme:k(a).antdTheme},{default:x(()=>[D(t)]),_:1},8,["theme"])}}},h=S(W);h.use(w());h.use(F);h.use(M);h.mount("#app");export{v as T,j as u};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{g as Yc}from"./antd-
|
|
1
|
+
import{g as Yc}from"./antd-BQNxIyr-.js";function Ni(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}let He=Ni();function Mc(a){He=a}const Lc=/[&<>"']/,qc=new RegExp(Lc.source,"g"),xc=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,Hc=new RegExp(xc.source,"g"),Vc={"&":"&","<":"<",">":">",'"':""","'":"'"},$i=a=>Vc[a];function Ne(a,e){if(e){if(Lc.test(a))return a.replace(qc,$i)}else if(xc.test(a))return a.replace(Hc,$i);return a}const zc=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function Wc(a){return a.replace(zc,(e,t)=>(t=t.toLowerCase(),t==="colon"?":":t.charAt(0)==="#"?t.charAt(1)==="x"?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const $c=/(^|[^\[])\^/g;function Z(a,e){let t=typeof a=="string"?a:a.source;e=e||"";const n={replace:(r,i)=>{let o=typeof i=="string"?i:i.source;return o=o.replace($c,"$1"),t=t.replace(r,o),n},getRegex:()=>new RegExp(t,e)};return n}function Ki(a){try{a=encodeURI(a).replace(/%25/g,"%")}catch{return null}return a}const pt={exec:()=>null};function Qi(a,e){const t=a.replace(/\|/g,(i,o,s)=>{let l=!1,_=o;for(;--_>=0&&s[_]==="\\";)l=!l;return l?"|":" |"}),n=t.split(/ \|/);let r=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length<e;)n.push("");for(;r<n.length;r++)n[r]=n[r].trim().replace(/\\\|/g,"|");return n}function yt(a,e,t){const n=a.length;if(n===0)return"";let r=0;for(;r<n&&a.charAt(n-r-1)===e;)r++;return a.slice(0,n-r)}function Kc(a,e){if(a.indexOf(e[1])===-1)return-1;let t=0;for(let n=0;n<a.length;n++)if(a[n]==="\\")n++;else if(a[n]===e[0])t++;else if(a[n]===e[1]&&(t--,t<0))return n;return-1}function Xi(a,e,t,n){const r=e.href,i=e.title?Ne(e.title):null,o=a[1].replace(/\\([\[\]])/g,"$1");if(a[0].charAt(0)!=="!"){n.state.inLink=!0;const s={type:"link",raw:t,href:r,title:i,text:o,tokens:n.inlineTokens(o)};return n.state.inLink=!1,s}return{type:"image",raw:t,href:r,title:i,text:Ne(o)}}function Qc(a,e){const t=a.match(/^(\s+)(?:```)/);if(t===null)return e;const n=t[1];return e.split(`
|
|
2
2
|
`).map(r=>{const i=r.match(/^\s+/);if(i===null)return r;const[o]=i;return o.length>=n.length?r.slice(n.length):r}).join(`
|
|
3
3
|
`)}class Lt{options;rules;lexer;constructor(e){this.options=e||He}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const n=t[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:yt(n,`
|
|
4
4
|
`)}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const n=t[0],r=Qc(n,t[3]||"");return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:r}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(/#$/.test(n)){const r=yt(n,"#");(this.options.pedantic||!r||/ $/.test(r))&&(n=r.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let n=t[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,`
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
// Injected by web-ui-server.js at serve time
|
|
9
9
|
window.__CC_CONFIG__ = __CC_CONFIG_PLACEHOLDER__;
|
|
10
10
|
</script>
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-CB5YlndO.js"></script>
|
|
12
12
|
<link rel="modulepreload" crossorigin href="./assets/vendor-CN0Iv_qZ.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="./assets/antd-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/antd-BQNxIyr-.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="./assets/index-CyGyEIVX.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous Learning Loop commands
|
|
3
|
+
* chainlesschain learning stats|trajectories|reflect|synthesize|improve|cleanup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { logger } from "../lib/logger.js";
|
|
9
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
+
|
|
11
|
+
export function registerLearningCommand(program) {
|
|
12
|
+
const learning = program
|
|
13
|
+
.command("learning")
|
|
14
|
+
.description(
|
|
15
|
+
"Autonomous learning loop — trajectories, reflection, skill synthesis",
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// learning stats
|
|
19
|
+
learning
|
|
20
|
+
.command("stats")
|
|
21
|
+
.description("Show learning loop statistics")
|
|
22
|
+
.option("--json", "Output as JSON")
|
|
23
|
+
.action(async (options) => {
|
|
24
|
+
try {
|
|
25
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
26
|
+
if (!ctx.db) {
|
|
27
|
+
logger.error("Database not available");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const db = ctx.db.getDatabase();
|
|
31
|
+
const { TrajectoryStore } =
|
|
32
|
+
await import("../lib/learning/trajectory-store.js");
|
|
33
|
+
const store = new TrajectoryStore(db);
|
|
34
|
+
const stats = store.getStats();
|
|
35
|
+
|
|
36
|
+
if (options.json) {
|
|
37
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
38
|
+
} else {
|
|
39
|
+
logger.log(chalk.bold("Learning Loop Statistics"));
|
|
40
|
+
logger.log(` Total trajectories: ${chalk.cyan(stats.total)}`);
|
|
41
|
+
logger.log(` Complex (6+ tools): ${chalk.cyan(stats.complex)}`);
|
|
42
|
+
logger.log(` Scored: ${chalk.cyan(stats.scored)}`);
|
|
43
|
+
logger.log(
|
|
44
|
+
` Skills synthesized: ${chalk.cyan(stats.synthesized)}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await shutdown();
|
|
49
|
+
} catch (err) {
|
|
50
|
+
logger.error(`Failed: ${err.message}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// learning trajectories
|
|
56
|
+
learning
|
|
57
|
+
.command("trajectories")
|
|
58
|
+
.description("List recent trajectories")
|
|
59
|
+
.option("-n, --limit <n>", "Number of trajectories", "20")
|
|
60
|
+
.option("--session <id>", "Filter by session ID")
|
|
61
|
+
.option("--json", "Output as JSON")
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
try {
|
|
64
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
65
|
+
if (!ctx.db) {
|
|
66
|
+
logger.error("Database not available");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const db = ctx.db.getDatabase();
|
|
70
|
+
const { TrajectoryStore } =
|
|
71
|
+
await import("../lib/learning/trajectory-store.js");
|
|
72
|
+
const store = new TrajectoryStore(db);
|
|
73
|
+
|
|
74
|
+
const trajs = store.getRecent({
|
|
75
|
+
limit: parseInt(options.limit, 10),
|
|
76
|
+
sessionId: options.session,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (options.json) {
|
|
80
|
+
console.log(JSON.stringify(trajs, null, 2));
|
|
81
|
+
} else {
|
|
82
|
+
if (trajs.length === 0) {
|
|
83
|
+
logger.log(chalk.gray("No trajectories recorded yet."));
|
|
84
|
+
} else {
|
|
85
|
+
logger.log(chalk.bold(`Recent Trajectories (${trajs.length})`));
|
|
86
|
+
for (const t of trajs) {
|
|
87
|
+
const scoreStr =
|
|
88
|
+
t.outcomeScore != null
|
|
89
|
+
? chalk.cyan(t.outcomeScore.toFixed(2))
|
|
90
|
+
: chalk.gray("unscored");
|
|
91
|
+
const synthStr = t.synthesizedSkill
|
|
92
|
+
? chalk.green(` → ${t.synthesizedSkill}`)
|
|
93
|
+
: "";
|
|
94
|
+
logger.log(
|
|
95
|
+
` ${chalk.dim(t.id.slice(0, 8))} | ${t.complexityLevel.padEnd(8)} | ` +
|
|
96
|
+
`${t.toolCount} tools | score: ${scoreStr}${synthStr}`,
|
|
97
|
+
);
|
|
98
|
+
if (t.userIntent) {
|
|
99
|
+
logger.log(` ${chalk.dim(t.userIntent.slice(0, 80))}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
await shutdown();
|
|
106
|
+
} catch (err) {
|
|
107
|
+
logger.error(`Failed: ${err.message}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// learning reflect
|
|
113
|
+
learning
|
|
114
|
+
.command("reflect")
|
|
115
|
+
.description("Run a reflection cycle and generate report")
|
|
116
|
+
.option("--json", "Output as JSON")
|
|
117
|
+
.action(async (options) => {
|
|
118
|
+
try {
|
|
119
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
120
|
+
if (!ctx.db) {
|
|
121
|
+
logger.error("Database not available");
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
const db = ctx.db.getDatabase();
|
|
125
|
+
const { TrajectoryStore } =
|
|
126
|
+
await import("../lib/learning/trajectory-store.js");
|
|
127
|
+
const { ReflectionEngine } =
|
|
128
|
+
await import("../lib/learning/reflection-engine.js");
|
|
129
|
+
const store = new TrajectoryStore(db);
|
|
130
|
+
const engine = new ReflectionEngine(db, null, store);
|
|
131
|
+
|
|
132
|
+
const spinner = ora("Running reflection...").start();
|
|
133
|
+
const report = await engine.reflect();
|
|
134
|
+
spinner.succeed("Reflection complete");
|
|
135
|
+
|
|
136
|
+
if (options.json) {
|
|
137
|
+
console.log(JSON.stringify(report, null, 2));
|
|
138
|
+
} else {
|
|
139
|
+
logger.log(chalk.bold("Reflection Report"));
|
|
140
|
+
logger.log(` Period: ${chalk.dim(report.timestamp)}`);
|
|
141
|
+
logger.log(
|
|
142
|
+
` Trajectories: ${chalk.cyan(report.totalTrajectories)}`,
|
|
143
|
+
);
|
|
144
|
+
logger.log(
|
|
145
|
+
` Avg score: ${chalk.cyan(report.avgScore?.toFixed(2) || "N/A")}`,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const trendColor =
|
|
149
|
+
report.trend === "improving"
|
|
150
|
+
? chalk.green
|
|
151
|
+
: report.trend === "declining"
|
|
152
|
+
? chalk.red
|
|
153
|
+
: chalk.gray;
|
|
154
|
+
logger.log(` Trend: ${trendColor(report.trend)}`);
|
|
155
|
+
|
|
156
|
+
if (report.topTools && report.topTools.length > 0) {
|
|
157
|
+
logger.log(chalk.bold("\n Top Tools:"));
|
|
158
|
+
for (const t of report.topTools.slice(0, 5)) {
|
|
159
|
+
logger.log(
|
|
160
|
+
` ${t.tool}: ${t.count}x (${(t.errorRate * 100).toFixed(0)}% error)`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (report.errorProneTools && report.errorProneTools.length > 0) {
|
|
166
|
+
logger.log(chalk.bold("\n Error-prone Tools:"));
|
|
167
|
+
for (const t of report.errorProneTools) {
|
|
168
|
+
logger.log(
|
|
169
|
+
` ${chalk.red(t.tool)}: ${(t.errorRate * 100).toFixed(0)}% error rate`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (report.note) {
|
|
175
|
+
logger.log(chalk.gray(`\n Note: ${report.note}`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await shutdown();
|
|
180
|
+
} catch (err) {
|
|
181
|
+
logger.error(`Failed: ${err.message}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// learning synthesize
|
|
187
|
+
learning
|
|
188
|
+
.command("synthesize")
|
|
189
|
+
.description("Synthesize new skills from eligible trajectories")
|
|
190
|
+
.option("--json", "Output as JSON")
|
|
191
|
+
.action(async (options) => {
|
|
192
|
+
try {
|
|
193
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
194
|
+
if (!ctx.db) {
|
|
195
|
+
logger.error("Database not available");
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
const db = ctx.db.getDatabase();
|
|
199
|
+
const { TrajectoryStore } =
|
|
200
|
+
await import("../lib/learning/trajectory-store.js");
|
|
201
|
+
const { SkillSynthesizer } =
|
|
202
|
+
await import("../lib/learning/skill-synthesizer.js");
|
|
203
|
+
const store = new TrajectoryStore(db);
|
|
204
|
+
const synthesizer = new SkillSynthesizer(db, null, store);
|
|
205
|
+
|
|
206
|
+
const spinner = ora("Scanning for synthesizable patterns...").start();
|
|
207
|
+
const result = await synthesizer.synthesize();
|
|
208
|
+
spinner.succeed("Synthesis complete");
|
|
209
|
+
|
|
210
|
+
if (options.json) {
|
|
211
|
+
console.log(JSON.stringify(result, null, 2));
|
|
212
|
+
} else {
|
|
213
|
+
if (result.created.length > 0) {
|
|
214
|
+
logger.log(
|
|
215
|
+
chalk.green(`Created ${result.created.length} skill(s):`),
|
|
216
|
+
);
|
|
217
|
+
for (const name of result.created) {
|
|
218
|
+
logger.log(` ${chalk.cyan(name)}`);
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
logger.log(chalk.gray("No new skills synthesized."));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (result.skipped.length > 0) {
|
|
225
|
+
logger.log(
|
|
226
|
+
chalk.dim(`\nSkipped ${result.skipped.length} candidate(s)`),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
await shutdown();
|
|
232
|
+
} catch (err) {
|
|
233
|
+
logger.error(`Failed: ${err.message}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// learning cleanup
|
|
239
|
+
learning
|
|
240
|
+
.command("cleanup")
|
|
241
|
+
.description("Delete old trajectories beyond retention period")
|
|
242
|
+
.option("--days <n>", "Retention days", "90")
|
|
243
|
+
.option("--json", "Output as JSON")
|
|
244
|
+
.action(async (options) => {
|
|
245
|
+
try {
|
|
246
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
247
|
+
if (!ctx.db) {
|
|
248
|
+
logger.error("Database not available");
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
const db = ctx.db.getDatabase();
|
|
252
|
+
const { TrajectoryStore } =
|
|
253
|
+
await import("../lib/learning/trajectory-store.js");
|
|
254
|
+
const store = new TrajectoryStore(db);
|
|
255
|
+
|
|
256
|
+
const days = parseInt(options.days, 10);
|
|
257
|
+
const spinner = ora(
|
|
258
|
+
`Cleaning up trajectories older than ${days} days...`,
|
|
259
|
+
).start();
|
|
260
|
+
const deleted = store.cleanup(days);
|
|
261
|
+
spinner.succeed(`Cleanup complete: ${deleted} trajectories deleted`);
|
|
262
|
+
|
|
263
|
+
if (options.json) {
|
|
264
|
+
console.log(JSON.stringify({ deleted, retentionDays: days }));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
await shutdown();
|
|
268
|
+
} catch (err) {
|
|
269
|
+
logger.error(`Failed: ${err.message}`);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
package/src/commands/lowcode.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
rollbackApp,
|
|
20
20
|
exportApp,
|
|
21
21
|
listApps,
|
|
22
|
+
deployApp,
|
|
22
23
|
} from "../lib/app-builder.js";
|
|
23
24
|
|
|
24
25
|
export function registerLowcodeCommand(program) {
|
|
@@ -307,14 +308,28 @@ export function registerLowcodeCommand(program) {
|
|
|
307
308
|
// lowcode deploy <app-id>
|
|
308
309
|
lowcode
|
|
309
310
|
.command("deploy")
|
|
310
|
-
.description("Deploy application
|
|
311
|
+
.description("Deploy application as static HTML bundle")
|
|
311
312
|
.argument("<app-id>", "Application ID")
|
|
312
|
-
.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
)
|
|
318
|
-
|
|
313
|
+
.option("-o, --output <dir>", "Output directory for the deploy bundle")
|
|
314
|
+
.action(async (appId, options) => {
|
|
315
|
+
const spinner = ora("Deploying application...").start();
|
|
316
|
+
let ctx;
|
|
317
|
+
try {
|
|
318
|
+
ctx = await bootstrap();
|
|
319
|
+
ensureLowcodeTables(ctx.db);
|
|
320
|
+
const result = deployApp(ctx.db, appId, {
|
|
321
|
+
outputDir: options.output || undefined,
|
|
322
|
+
});
|
|
323
|
+
spinner.succeed(
|
|
324
|
+
chalk.green(`App ${chalk.cyan(appId)} deployed successfully`),
|
|
325
|
+
);
|
|
326
|
+
logger.log(` Output: ${chalk.cyan(result.outputDir)}`);
|
|
327
|
+
logger.log(` Files: ${result.files.join(", ")}`);
|
|
328
|
+
logger.log(` Deployed: ${result.deployedAt}`);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
spinner.fail(chalk.red(`Deploy failed: ${err.message}`));
|
|
331
|
+
} finally {
|
|
332
|
+
if (ctx) await shutdown(ctx);
|
|
333
|
+
}
|
|
319
334
|
});
|
|
320
335
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Formatter — converts agent responses to Discord-compatible markup.
|
|
3
|
+
*
|
|
4
|
+
* Discord supports a broad subset of Markdown. Main limit is 2000 chars per message.
|
|
5
|
+
*
|
|
6
|
+
* @module discord-formatter
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const DISCORD_MAX_LENGTH = 2000;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Format an agent response for Discord.
|
|
13
|
+
* Discord natively supports Markdown, so minimal conversion needed.
|
|
14
|
+
* @param {string} response
|
|
15
|
+
* @param {object} [options]
|
|
16
|
+
* @param {number} [options.maxLength=2000]
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export function formatForDiscord(response, options = {}) {
|
|
20
|
+
if (!response) return "";
|
|
21
|
+
const maxLength = options.maxLength || DISCORD_MAX_LENGTH;
|
|
22
|
+
|
|
23
|
+
let text = response;
|
|
24
|
+
|
|
25
|
+
if (text.length > maxLength) {
|
|
26
|
+
text = text.substring(0, maxLength - 3) + "...";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Split a response into Discord-sized chunks.
|
|
34
|
+
* Tries to split at code block or newline boundaries.
|
|
35
|
+
* @param {string} text
|
|
36
|
+
* @param {number} [maxLength=2000]
|
|
37
|
+
* @returns {string[]}
|
|
38
|
+
*/
|
|
39
|
+
export function splitForDiscord(text, maxLength = DISCORD_MAX_LENGTH) {
|
|
40
|
+
if (!text || text.length <= maxLength) return [text || ""];
|
|
41
|
+
|
|
42
|
+
const chunks = [];
|
|
43
|
+
let remaining = text;
|
|
44
|
+
|
|
45
|
+
while (remaining.length > 0) {
|
|
46
|
+
if (remaining.length <= maxLength) {
|
|
47
|
+
chunks.push(remaining);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Try to split at code block boundary
|
|
52
|
+
let splitIdx = remaining.lastIndexOf("\n```", maxLength);
|
|
53
|
+
if (splitIdx > maxLength * 0.3) {
|
|
54
|
+
splitIdx += 1; // Include the newline
|
|
55
|
+
} else {
|
|
56
|
+
// Try to split at newline
|
|
57
|
+
splitIdx = remaining.lastIndexOf("\n", maxLength);
|
|
58
|
+
if (splitIdx < maxLength * 0.3) {
|
|
59
|
+
splitIdx = maxLength;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
chunks.push(remaining.substring(0, splitIdx));
|
|
64
|
+
remaining = remaining.substring(splitIdx).trimStart();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return chunks;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Format a code block for Discord with syntax highlighting.
|
|
72
|
+
* @param {string} code
|
|
73
|
+
* @param {string} [language=""]
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
export function codeBlock(code, language = "") {
|
|
77
|
+
return `\`\`\`${language}\n${code}\n\`\`\``;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Format an embed-like block for Discord (using quote blocks).
|
|
82
|
+
* @param {string} title
|
|
83
|
+
* @param {string} content
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
export function quoteBlock(title, content) {
|
|
87
|
+
const lines = content.split("\n").map((l) => `> ${l}`);
|
|
88
|
+
return `**${title}**\n${lines.join("\n")}`;
|
|
89
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GatewayBase — shared foundation for messaging platform gateways.
|
|
3
|
+
*
|
|
4
|
+
* Each gateway (Telegram, Discord, etc.) extends this class.
|
|
5
|
+
* Provides: session-per-chat management, message mapping, rate limiting,
|
|
6
|
+
* and integration with the agent loop.
|
|
7
|
+
*
|
|
8
|
+
* @module gateway-base
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { EventEmitter } from "node:events";
|
|
12
|
+
|
|
13
|
+
// ─── Constants ──────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
const DEFAULT_MAX_RESPONSE_LENGTH = 4000;
|
|
16
|
+
const DEFAULT_RATE_LIMIT_WINDOW = 60000; // 1 minute
|
|
17
|
+
const DEFAULT_RATE_LIMIT_MAX = 20; // messages per window
|
|
18
|
+
|
|
19
|
+
// ─── GatewayBase ────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
export class GatewayBase extends EventEmitter {
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} options
|
|
24
|
+
* @param {string} options.platform - Platform name (e.g. "telegram", "discord")
|
|
25
|
+
* @param {number} [options.maxResponseLength] - Max chars per response
|
|
26
|
+
* @param {number} [options.rateLimitWindow] - Rate limit window in ms
|
|
27
|
+
* @param {number} [options.rateLimitMax] - Max messages per window
|
|
28
|
+
*/
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
super();
|
|
31
|
+
this.platform = options.platform || "unknown";
|
|
32
|
+
this.maxResponseLength =
|
|
33
|
+
options.maxResponseLength || DEFAULT_MAX_RESPONSE_LENGTH;
|
|
34
|
+
this.rateLimitWindow = options.rateLimitWindow || DEFAULT_RATE_LIMIT_WINDOW;
|
|
35
|
+
this.rateLimitMax = options.rateLimitMax || DEFAULT_RATE_LIMIT_MAX;
|
|
36
|
+
|
|
37
|
+
/** @type {Map<string, { messages: object[], lastActivity: number }>} */
|
|
38
|
+
this.sessions = new Map();
|
|
39
|
+
|
|
40
|
+
/** @type {Map<string, number[]>} */
|
|
41
|
+
this._rateLimitBuckets = new Map();
|
|
42
|
+
|
|
43
|
+
this._running = false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Lifecycle ───────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/** Start the gateway. Override in subclass. */
|
|
49
|
+
async start() {
|
|
50
|
+
this._running = true;
|
|
51
|
+
this.emit("started", { platform: this.platform });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Stop the gateway. Override in subclass. */
|
|
55
|
+
async stop() {
|
|
56
|
+
this._running = false;
|
|
57
|
+
this.sessions.clear();
|
|
58
|
+
this._rateLimitBuckets.clear();
|
|
59
|
+
this.emit("stopped", { platform: this.platform });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** @returns {boolean} */
|
|
63
|
+
isRunning() {
|
|
64
|
+
return this._running;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Session management ──────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get or create a session for a chat.
|
|
71
|
+
* @param {string} chatId - Platform-specific chat identifier
|
|
72
|
+
* @returns {{ messages: object[], lastActivity: number, isNew: boolean }}
|
|
73
|
+
*/
|
|
74
|
+
getOrCreateSession(chatId) {
|
|
75
|
+
if (this.sessions.has(chatId)) {
|
|
76
|
+
const session = this.sessions.get(chatId);
|
|
77
|
+
session.lastActivity = Date.now();
|
|
78
|
+
return { ...session, isNew: false };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const session = {
|
|
82
|
+
messages: [],
|
|
83
|
+
lastActivity: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
this.sessions.set(chatId, session);
|
|
86
|
+
return { ...session, isNew: true };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Add a message to a chat session.
|
|
91
|
+
* @param {string} chatId
|
|
92
|
+
* @param {string} role - "user" | "assistant"
|
|
93
|
+
* @param {string} content
|
|
94
|
+
*/
|
|
95
|
+
addMessage(chatId, role, content) {
|
|
96
|
+
const session = this.sessions.get(chatId);
|
|
97
|
+
if (session) {
|
|
98
|
+
session.messages.push({ role, content });
|
|
99
|
+
session.lastActivity = Date.now();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Clear a chat session.
|
|
105
|
+
* @param {string} chatId
|
|
106
|
+
*/
|
|
107
|
+
clearSession(chatId) {
|
|
108
|
+
this.sessions.delete(chatId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get active session count.
|
|
113
|
+
* @returns {number}
|
|
114
|
+
*/
|
|
115
|
+
getSessionCount() {
|
|
116
|
+
return this.sessions.size;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Rate limiting ───────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if a chat is rate-limited.
|
|
123
|
+
* @param {string} chatId
|
|
124
|
+
* @returns {boolean}
|
|
125
|
+
*/
|
|
126
|
+
isRateLimited(chatId) {
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
const bucket = this._rateLimitBuckets.get(chatId) || [];
|
|
129
|
+
// Clean old entries
|
|
130
|
+
const recent = bucket.filter((ts) => now - ts < this.rateLimitWindow);
|
|
131
|
+
this._rateLimitBuckets.set(chatId, recent);
|
|
132
|
+
return recent.length >= this.rateLimitMax;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Record a message for rate limiting.
|
|
137
|
+
* @param {string} chatId
|
|
138
|
+
*/
|
|
139
|
+
recordMessage(chatId) {
|
|
140
|
+
const bucket = this._rateLimitBuckets.get(chatId) || [];
|
|
141
|
+
bucket.push(Date.now());
|
|
142
|
+
this._rateLimitBuckets.set(chatId, bucket);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── Message formatting ──────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Split a long response into chunks.
|
|
149
|
+
* @param {string} text
|
|
150
|
+
* @param {number} [maxLength]
|
|
151
|
+
* @returns {string[]}
|
|
152
|
+
*/
|
|
153
|
+
splitResponse(text, maxLength) {
|
|
154
|
+
const limit = maxLength || this.maxResponseLength;
|
|
155
|
+
if (!text || text.length <= limit) return [text || ""];
|
|
156
|
+
|
|
157
|
+
const chunks = [];
|
|
158
|
+
let remaining = text;
|
|
159
|
+
while (remaining.length > 0) {
|
|
160
|
+
if (remaining.length <= limit) {
|
|
161
|
+
chunks.push(remaining);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
// Try to split at last newline within limit
|
|
165
|
+
let splitIdx = remaining.lastIndexOf("\n", limit);
|
|
166
|
+
if (splitIdx < limit * 0.5) {
|
|
167
|
+
// No good newline split point — split at limit
|
|
168
|
+
splitIdx = limit;
|
|
169
|
+
}
|
|
170
|
+
chunks.push(remaining.substring(0, splitIdx));
|
|
171
|
+
remaining = remaining.substring(splitIdx).trimStart();
|
|
172
|
+
}
|
|
173
|
+
return chunks;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ── Stats ───────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get gateway statistics.
|
|
180
|
+
* @returns {{ platform: string, running: boolean, sessions: number }}
|
|
181
|
+
*/
|
|
182
|
+
getStats() {
|
|
183
|
+
return {
|
|
184
|
+
platform: this.platform,
|
|
185
|
+
running: this._running,
|
|
186
|
+
sessions: this.sessions.size,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|